diff --git a/23.5.26 b/23.5.26 deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/__pycache__/Gcode_generator.cpython-38.pyc b/plotter-app/__pycache__/Gcode_generator.cpython-38.pyc deleted file mode 100644 index 4415f2b..0000000 Binary files a/plotter-app/__pycache__/Gcode_generator.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/__pycache__/app.cpython-38.pyc b/plotter-app/__pycache__/app.cpython-38.pyc deleted file mode 100644 index 0a7e4b5..0000000 Binary files a/plotter-app/__pycache__/app.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/__pycache__/streamer.cpython-38.pyc b/plotter-app/__pycache__/streamer.cpython-38.pyc deleted file mode 100644 index 88a1b53..0000000 Binary files a/plotter-app/__pycache__/streamer.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/__pycache__/svgToGcode.cpython-38.pyc b/plotter-app/__pycache__/svgToGcode.cpython-38.pyc deleted file mode 100644 index 2cc66e1..0000000 Binary files a/plotter-app/__pycache__/svgToGcode.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/__pycache__/text_to_gcode.cpython-38.pyc b/plotter-app/__pycache__/text_to_gcode.cpython-38.pyc deleted file mode 100644 index 7ae0627..0000000 Binary files a/plotter-app/__pycache__/text_to_gcode.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/bin/Activate.ps1 b/plotter-app/venv/bin/Activate.ps1 deleted file mode 100644 index 2fb3852..0000000 --- a/plotter-app/venv/bin/Activate.ps1 +++ /dev/null @@ -1,241 +0,0 @@ -<# -.Synopsis -Activate a Python virtual environment for the current PowerShell session. - -.Description -Pushes the python executable for a virtual environment to the front of the -$Env:PATH environment variable and sets the prompt to signify that you are -in a Python virtual environment. Makes use of the command line switches as -well as the `pyvenv.cfg` file values present in the virtual environment. - -.Parameter VenvDir -Path to the directory that contains the virtual environment to activate. The -default value for this is the parent of the directory that the Activate.ps1 -script is located within. - -.Parameter Prompt -The prompt prefix to display when this virtual environment is activated. By -default, this prompt is the name of the virtual environment folder (VenvDir) -surrounded by parentheses and followed by a single space (ie. '(.venv) '). - -.Example -Activate.ps1 -Activates the Python virtual environment that contains the Activate.ps1 script. - -.Example -Activate.ps1 -Verbose -Activates the Python virtual environment that contains the Activate.ps1 script, -and shows extra information about the activation as it executes. - -.Example -Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv -Activates the Python virtual environment located in the specified location. - -.Example -Activate.ps1 -Prompt "MyPython" -Activates the Python virtual environment that contains the Activate.ps1 script, -and prefixes the current prompt with the specified string (surrounded in -parentheses) while the virtual environment is active. - -.Notes -On Windows, it may be required to enable this Activate.ps1 script by setting the -execution policy for the user. You can do this by issuing the following PowerShell -command: - -PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser - -For more information on Execution Policies: -https://go.microsoft.com/fwlink/?LinkID=135170 - -#> -Param( - [Parameter(Mandatory = $false)] - [String] - $VenvDir, - [Parameter(Mandatory = $false)] - [String] - $Prompt -) - -<# Function declarations --------------------------------------------------- #> - -<# -.Synopsis -Remove all shell session elements added by the Activate script, including the -addition of the virtual environment's Python executable from the beginning of -the PATH variable. - -.Parameter NonDestructive -If present, do not remove this function from the global namespace for the -session. - -#> -function global:deactivate ([switch]$NonDestructive) { - # Revert to original values - - # The prior prompt: - if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { - Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt - Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT - } - - # The prior PYTHONHOME: - if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { - Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME - Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME - } - - # The prior PATH: - if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { - Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH - Remove-Item -Path Env:_OLD_VIRTUAL_PATH - } - - # Just remove the VIRTUAL_ENV altogether: - if (Test-Path -Path Env:VIRTUAL_ENV) { - Remove-Item -Path env:VIRTUAL_ENV - } - - # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: - if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { - Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force - } - - # Leave deactivate function in the global namespace if requested: - if (-not $NonDestructive) { - Remove-Item -Path function:deactivate - } -} - -<# -.Description -Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the -given folder, and returns them in a map. - -For each line in the pyvenv.cfg file, if that line can be parsed into exactly -two strings separated by `=` (with any amount of whitespace surrounding the =) -then it is considered a `key = value` line. The left hand string is the key, -the right hand is the value. - -If the value starts with a `'` or a `"` then the first and last character is -stripped from the value before being captured. - -.Parameter ConfigDir -Path to the directory that contains the `pyvenv.cfg` file. -#> -function Get-PyVenvConfig( - [String] - $ConfigDir -) { - Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" - - # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). - $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue - - # An empty map will be returned if no config file is found. - $pyvenvConfig = @{ } - - if ($pyvenvConfigPath) { - - Write-Verbose "File exists, parse `key = value` lines" - $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath - - $pyvenvConfigContent | ForEach-Object { - $keyval = $PSItem -split "\s*=\s*", 2 - if ($keyval[0] -and $keyval[1]) { - $val = $keyval[1] - - # Remove extraneous quotations around a string value. - if ("'""".Contains($val.Substring(0, 1))) { - $val = $val.Substring(1, $val.Length - 2) - } - - $pyvenvConfig[$keyval[0]] = $val - Write-Verbose "Adding Key: '$($keyval[0])'='$val'" - } - } - } - return $pyvenvConfig -} - - -<# Begin Activate script --------------------------------------------------- #> - -# Determine the containing directory of this script -$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition -$VenvExecDir = Get-Item -Path $VenvExecPath - -Write-Verbose "Activation script is located in path: '$VenvExecPath'" -Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" -Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" - -# Set values required in priority: CmdLine, ConfigFile, Default -# First, get the location of the virtual environment, it might not be -# VenvExecDir if specified on the command line. -if ($VenvDir) { - Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" -} -else { - Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." - $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") - Write-Verbose "VenvDir=$VenvDir" -} - -# Next, read the `pyvenv.cfg` file to determine any required value such -# as `prompt`. -$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir - -# Next, set the prompt from the command line, or the config file, or -# just use the name of the virtual environment folder. -if ($Prompt) { - Write-Verbose "Prompt specified as argument, using '$Prompt'" -} -else { - Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" - if ($pyvenvCfg -and $pyvenvCfg['prompt']) { - Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" - $Prompt = $pyvenvCfg['prompt']; - } - else { - Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virutal environment)" - Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" - $Prompt = Split-Path -Path $venvDir -Leaf - } -} - -Write-Verbose "Prompt = '$Prompt'" -Write-Verbose "VenvDir='$VenvDir'" - -# Deactivate any currently active virtual environment, but leave the -# deactivate function in place. -deactivate -nondestructive - -# Now set the environment variable VIRTUAL_ENV, used by many tools to determine -# that there is an activated venv. -$env:VIRTUAL_ENV = $VenvDir - -if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { - - Write-Verbose "Setting prompt to '$Prompt'" - - # Set the prompt to include the env name - # Make sure _OLD_VIRTUAL_PROMPT is global - function global:_OLD_VIRTUAL_PROMPT { "" } - Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT - New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt - - function global:prompt { - Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " - _OLD_VIRTUAL_PROMPT - } -} - -# Clear PYTHONHOME -if (Test-Path -Path Env:PYTHONHOME) { - Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME - Remove-Item -Path Env:PYTHONHOME -} - -# Add the venv to the PATH -Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH -$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/plotter-app/venv/bin/HersheyFonts_demo b/plotter-app/venv/bin/HersheyFonts_demo deleted file mode 100755 index d63746c..0000000 --- a/plotter-app/venv/bin/HersheyFonts_demo +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/sowell/Documents/wall_plotter/wallter/plotter-app/venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from HersheyFonts.HersheyFonts import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/plotter-app/venv/bin/activate b/plotter-app/venv/bin/activate deleted file mode 100644 index cb445cb..0000000 --- a/plotter-app/venv/bin/activate +++ /dev/null @@ -1,76 +0,0 @@ -# This file must be used with "source bin/activate" *from bash* -# you cannot run it directly - -deactivate () { - # reset old environment variables - if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then - PATH="${_OLD_VIRTUAL_PATH:-}" - export PATH - unset _OLD_VIRTUAL_PATH - fi - if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then - PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" - export PYTHONHOME - unset _OLD_VIRTUAL_PYTHONHOME - fi - - # This should detect bash and zsh, which have a hash command that must - # be called to get it to forget past commands. Without forgetting - # past commands the $PATH changes we made may not be respected - if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then - hash -r - fi - - if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then - PS1="${_OLD_VIRTUAL_PS1:-}" - export PS1 - unset _OLD_VIRTUAL_PS1 - fi - - unset VIRTUAL_ENV - if [ ! "${1:-}" = "nondestructive" ] ; then - # Self destruct! - unset -f deactivate - fi -} - -# unset irrelevant variables -deactivate nondestructive - -VIRTUAL_ENV=/home/sowell/Documents/wall_plotter/wallter/plotter-app/venv -export VIRTUAL_ENV - -_OLD_VIRTUAL_PATH="$PATH" -PATH="$VIRTUAL_ENV/"bin":$PATH" -export PATH - -# unset PYTHONHOME if set -# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) -# could use `if (set -u; : $PYTHONHOME) ;` in bash -if [ -n "${PYTHONHOME:-}" ] ; then - _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" - unset PYTHONHOME -fi - -if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then - _OLD_VIRTUAL_PS1="${PS1:-}" - if [ "x'(venv) '" != x ] ; then - PS1='(venv) '"${PS1:-}" - else - if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then - # special case for Aspen magic directories - # see https://aspen.io/ - PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1" - else - PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1" - fi - fi - export PS1 -fi - -# This should detect bash and zsh, which have a hash command that must -# be called to get it to forget past commands. Without forgetting -# past commands the $PATH changes we made may not be respected -if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then - hash -r -fi diff --git a/plotter-app/venv/bin/activate.csh b/plotter-app/venv/bin/activate.csh deleted file mode 100644 index 1deda11..0000000 --- a/plotter-app/venv/bin/activate.csh +++ /dev/null @@ -1,37 +0,0 @@ -# This file must be used with "source bin/activate.csh" *from csh*. -# You cannot run it directly. -# Created by Davide Di Blasi . -# Ported to Python 3.3 venv by Andrew Svetlov - -alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate' - -# Unset irrelevant variables. -deactivate nondestructive - -setenv VIRTUAL_ENV /home/sowell/Documents/wall_plotter/wallter/plotter-app/venv - -set _OLD_VIRTUAL_PATH="$PATH" -setenv PATH "$VIRTUAL_ENV/"bin":$PATH" - - -set _OLD_VIRTUAL_PROMPT="$prompt" - -if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then - if ("venv" != "") then - set env_name = venv - else - if (`basename "VIRTUAL_ENV"` == "__") then - # special case for Aspen magic directories - # see https://aspen.io/ - set env_name = `basename \`dirname "$VIRTUAL_ENV"\`` - else - set env_name = `basename "$VIRTUAL_ENV"` - endif - endif - set prompt = "[$env_name] $prompt" - unset env_name -endif - -alias pydoc python -m pydoc - -rehash diff --git a/plotter-app/venv/bin/activate.fish b/plotter-app/venv/bin/activate.fish deleted file mode 100644 index df68c9e..0000000 --- a/plotter-app/venv/bin/activate.fish +++ /dev/null @@ -1,75 +0,0 @@ -# This file must be used with ". bin/activate.fish" *from fish* (http://fishshell.org) -# you cannot run it directly - -function deactivate -d "Exit virtualenv and return to normal shell environment" - # reset old environment variables - if test -n "$_OLD_VIRTUAL_PATH" - set -gx PATH $_OLD_VIRTUAL_PATH - set -e _OLD_VIRTUAL_PATH - end - if test -n "$_OLD_VIRTUAL_PYTHONHOME" - set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME - set -e _OLD_VIRTUAL_PYTHONHOME - end - - if test -n "$_OLD_FISH_PROMPT_OVERRIDE" - functions -e fish_prompt - set -e _OLD_FISH_PROMPT_OVERRIDE - functions -c _old_fish_prompt fish_prompt - functions -e _old_fish_prompt - end - - set -e VIRTUAL_ENV - if test "$argv[1]" != "nondestructive" - # Self destruct! - functions -e deactivate - end -end - -# unset irrelevant variables -deactivate nondestructive - -set -gx VIRTUAL_ENV /home/sowell/Documents/wall_plotter/wallter/plotter-app/venv - -set -gx _OLD_VIRTUAL_PATH $PATH -set -gx PATH "$VIRTUAL_ENV/"bin $PATH - -# unset PYTHONHOME if set -if set -q PYTHONHOME - set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME - set -e PYTHONHOME -end - -if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" - # fish uses a function instead of an env var to generate the prompt. - - # save the current fish_prompt function as the function _old_fish_prompt - functions -c fish_prompt _old_fish_prompt - - # with the original prompt function renamed, we can override with our own. - function fish_prompt - # Save the return status of the last command - set -l old_status $status - - # Prompt override? - if test -n "'(venv) '" - printf "%s%s" '(venv) ' (set_color normal) - else - # ...Otherwise, prepend env - set -l _checkbase (basename "$VIRTUAL_ENV") - if test $_checkbase = "__" - # special case for Aspen magic directories - # see https://aspen.io/ - printf "%s[%s]%s " (set_color -b blue white) (basename (dirname "$VIRTUAL_ENV")) (set_color normal) - else - printf "%s(%s)%s" (set_color -b blue white) (basename "$VIRTUAL_ENV") (set_color normal) - end - end - - # Restore the return status of the previous command. - echo "exit $old_status" | . - _old_fish_prompt - end - - set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" -end diff --git a/plotter-app/venv/bin/easy_install b/plotter-app/venv/bin/easy_install deleted file mode 100755 index ecc6e80..0000000 --- a/plotter-app/venv/bin/easy_install +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/sowell/Documents/wall_plotter/wallter/plotter-app/venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from setuptools.command.easy_install import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/plotter-app/venv/bin/easy_install-3.8 b/plotter-app/venv/bin/easy_install-3.8 deleted file mode 100755 index ecc6e80..0000000 --- a/plotter-app/venv/bin/easy_install-3.8 +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/sowell/Documents/wall_plotter/wallter/plotter-app/venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from setuptools.command.easy_install import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/plotter-app/venv/bin/f2py b/plotter-app/venv/bin/f2py deleted file mode 100755 index f075f27..0000000 --- a/plotter-app/venv/bin/f2py +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/sowell/Documents/wall_plotter/wallter/plotter-app/venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from numpy.f2py.f2py2e import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/plotter-app/venv/bin/f2py3 b/plotter-app/venv/bin/f2py3 deleted file mode 100755 index f075f27..0000000 --- a/plotter-app/venv/bin/f2py3 +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/sowell/Documents/wall_plotter/wallter/plotter-app/venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from numpy.f2py.f2py2e import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/plotter-app/venv/bin/f2py3.8 b/plotter-app/venv/bin/f2py3.8 deleted file mode 100755 index f075f27..0000000 --- a/plotter-app/venv/bin/f2py3.8 +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/sowell/Documents/wall_plotter/wallter/plotter-app/venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from numpy.f2py.f2py2e import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/plotter-app/venv/bin/flask b/plotter-app/venv/bin/flask deleted file mode 100755 index f8db987..0000000 --- a/plotter-app/venv/bin/flask +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/sowell/Documents/wall_plotter/wallter/plotter-app/venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from flask.cli import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/plotter-app/venv/bin/fonttools b/plotter-app/venv/bin/fonttools deleted file mode 100755 index 65e83d6..0000000 --- a/plotter-app/venv/bin/fonttools +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/sowell/Documents/wall_plotter/wallter/plotter-app/venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from fontTools.__main__ import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/plotter-app/venv/bin/markdown_py b/plotter-app/venv/bin/markdown_py deleted file mode 100755 index 77b4ba3..0000000 --- a/plotter-app/venv/bin/markdown_py +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/sowell/Documents/wall_plotter/wallter/plotter-app/venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from markdown.__main__ import run -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(run()) diff --git a/plotter-app/venv/bin/pip b/plotter-app/venv/bin/pip deleted file mode 100755 index 00d805a..0000000 --- a/plotter-app/venv/bin/pip +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/sowell/Documents/wall_plotter/wallter/plotter-app/venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from pip._internal.cli.main import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/plotter-app/venv/bin/pip-review b/plotter-app/venv/bin/pip-review deleted file mode 100755 index 4bd4247..0000000 --- a/plotter-app/venv/bin/pip-review +++ /dev/null @@ -1,12 +0,0 @@ -#!/home/sowell/Documents/wall_plotter/wallter/plotter-app/venv/bin/python3 -# EASY-INSTALL-ENTRY-SCRIPT: 'pip-review==1.3.0','console_scripts','pip-review' -__requires__ = 'pip-review==1.3.0' -import re -import sys -from pkg_resources import load_entry_point - -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) - sys.exit( - load_entry_point('pip-review==1.3.0', 'console_scripts', 'pip-review')() - ) diff --git a/plotter-app/venv/bin/pip3 b/plotter-app/venv/bin/pip3 deleted file mode 100755 index 00d805a..0000000 --- a/plotter-app/venv/bin/pip3 +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/sowell/Documents/wall_plotter/wallter/plotter-app/venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from pip._internal.cli.main import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/plotter-app/venv/bin/pip3.8 b/plotter-app/venv/bin/pip3.8 deleted file mode 100755 index 00d805a..0000000 --- a/plotter-app/venv/bin/pip3.8 +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/sowell/Documents/wall_plotter/wallter/plotter-app/venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from pip._internal.cli.main import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/plotter-app/venv/bin/pyftmerge b/plotter-app/venv/bin/pyftmerge deleted file mode 100755 index d842d52..0000000 --- a/plotter-app/venv/bin/pyftmerge +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/sowell/Documents/wall_plotter/wallter/plotter-app/venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from fontTools.merge import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/plotter-app/venv/bin/pyftsubset b/plotter-app/venv/bin/pyftsubset deleted file mode 100755 index 5da155b..0000000 --- a/plotter-app/venv/bin/pyftsubset +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/sowell/Documents/wall_plotter/wallter/plotter-app/venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from fontTools.subset import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/plotter-app/venv/bin/pyserial-miniterm b/plotter-app/venv/bin/pyserial-miniterm deleted file mode 100755 index 54c83f4..0000000 --- a/plotter-app/venv/bin/pyserial-miniterm +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/sowell/Documents/wall_plotter/wallter/plotter-app/venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from serial.tools.miniterm import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/plotter-app/venv/bin/pyserial-ports b/plotter-app/venv/bin/pyserial-ports deleted file mode 100755 index 3c1b0b6..0000000 --- a/plotter-app/venv/bin/pyserial-ports +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/sowell/Documents/wall_plotter/wallter/plotter-app/venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from serial.tools.list_ports import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/plotter-app/venv/bin/python b/plotter-app/venv/bin/python deleted file mode 120000 index b8a0adb..0000000 --- a/plotter-app/venv/bin/python +++ /dev/null @@ -1 +0,0 @@ -python3 \ No newline at end of file diff --git a/plotter-app/venv/bin/python3 b/plotter-app/venv/bin/python3 deleted file mode 120000 index ae65fda..0000000 --- a/plotter-app/venv/bin/python3 +++ /dev/null @@ -1 +0,0 @@ -/usr/bin/python3 \ No newline at end of file diff --git a/plotter-app/venv/bin/ttx b/plotter-app/venv/bin/ttx deleted file mode 100755 index d9f93ec..0000000 --- a/plotter-app/venv/bin/ttx +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/sowell/Documents/wall_plotter/wallter/plotter-app/venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from fontTools.ttx import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/plotter-app/venv/bin/wsdump b/plotter-app/venv/bin/wsdump deleted file mode 100755 index eb56659..0000000 --- a/plotter-app/venv/bin/wsdump +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/sowell/Documents/wall_plotter/wallter/plotter-app/venv/bin/python3 -# -*- coding: utf-8 -*- -import re -import sys -from websocket._wsdump import main -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(main()) diff --git a/plotter-app/venv/include/site/python3.8/greenlet/greenlet.h b/plotter-app/venv/include/site/python3.8/greenlet/greenlet.h deleted file mode 100644 index d02a16e..0000000 --- a/plotter-app/venv/include/site/python3.8/greenlet/greenlet.h +++ /dev/null @@ -1,164 +0,0 @@ -/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ - -/* Greenlet object interface */ - -#ifndef Py_GREENLETOBJECT_H -#define Py_GREENLETOBJECT_H - - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* This is deprecated and undocumented. It does not change. */ -#define GREENLET_VERSION "1.0.0" - -#ifndef GREENLET_MODULE -#define implementation_ptr_t void* -#endif - -typedef struct _greenlet { - PyObject_HEAD - PyObject* weakreflist; - PyObject* dict; - implementation_ptr_t pimpl; -} PyGreenlet; - -#define PyGreenlet_Check(op) (op && PyObject_TypeCheck(op, &PyGreenlet_Type)) - - -/* C API functions */ - -/* Total number of symbols that are exported */ -#define PyGreenlet_API_pointers 12 - -#define PyGreenlet_Type_NUM 0 -#define PyExc_GreenletError_NUM 1 -#define PyExc_GreenletExit_NUM 2 - -#define PyGreenlet_New_NUM 3 -#define PyGreenlet_GetCurrent_NUM 4 -#define PyGreenlet_Throw_NUM 5 -#define PyGreenlet_Switch_NUM 6 -#define PyGreenlet_SetParent_NUM 7 - -#define PyGreenlet_MAIN_NUM 8 -#define PyGreenlet_STARTED_NUM 9 -#define PyGreenlet_ACTIVE_NUM 10 -#define PyGreenlet_GET_PARENT_NUM 11 - -#ifndef GREENLET_MODULE -/* This section is used by modules that uses the greenlet C API */ -static void** _PyGreenlet_API = NULL; - -# define PyGreenlet_Type \ - (*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM]) - -# define PyExc_GreenletError \ - ((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM]) - -# define PyExc_GreenletExit \ - ((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM]) - -/* - * PyGreenlet_New(PyObject *args) - * - * greenlet.greenlet(run, parent=None) - */ -# define PyGreenlet_New \ - (*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \ - _PyGreenlet_API[PyGreenlet_New_NUM]) - -/* - * PyGreenlet_GetCurrent(void) - * - * greenlet.getcurrent() - */ -# define PyGreenlet_GetCurrent \ - (*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM]) - -/* - * PyGreenlet_Throw( - * PyGreenlet *greenlet, - * PyObject *typ, - * PyObject *val, - * PyObject *tb) - * - * g.throw(...) - */ -# define PyGreenlet_Throw \ - (*(PyObject * (*)(PyGreenlet * self, \ - PyObject * typ, \ - PyObject * val, \ - PyObject * tb)) \ - _PyGreenlet_API[PyGreenlet_Throw_NUM]) - -/* - * PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args) - * - * g.switch(*args, **kwargs) - */ -# define PyGreenlet_Switch \ - (*(PyObject * \ - (*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \ - _PyGreenlet_API[PyGreenlet_Switch_NUM]) - -/* - * PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent) - * - * g.parent = new_parent - */ -# define PyGreenlet_SetParent \ - (*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \ - _PyGreenlet_API[PyGreenlet_SetParent_NUM]) - -/* - * PyGreenlet_GetParent(PyObject* greenlet) - * - * return greenlet.parent; - * - * This could return NULL even if there is no exception active. - * If it does not return NULL, you are responsible for decrementing the - * reference count. - */ -# define PyGreenlet_GetParent \ - (*(PyGreenlet* (*)(PyGreenlet*)) \ - _PyGreenlet_API[PyGreenlet_GET_PARENT_NUM]) - -/* - * deprecated, undocumented alias. - */ -# define PyGreenlet_GET_PARENT PyGreenlet_GetParent - -# define PyGreenlet_MAIN \ - (*(int (*)(PyGreenlet*)) \ - _PyGreenlet_API[PyGreenlet_MAIN_NUM]) - -# define PyGreenlet_STARTED \ - (*(int (*)(PyGreenlet*)) \ - _PyGreenlet_API[PyGreenlet_STARTED_NUM]) - -# define PyGreenlet_ACTIVE \ - (*(int (*)(PyGreenlet*)) \ - _PyGreenlet_API[PyGreenlet_ACTIVE_NUM]) - - - - -/* Macro that imports greenlet and initializes C API */ -/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we - keep the older definition to be sure older code that might have a copy of - the header still works. */ -# define PyGreenlet_Import() \ - { \ - _PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \ - } - -#endif /* GREENLET_MODULE */ - -#ifdef __cplusplus -} -#endif -#endif /* !Py_GREENLETOBJECT_H */ diff --git a/plotter-app/venv/lib/python3.8/site-packages/Flask_FlatPages-0.8.2.dist-info/INSTALLER b/plotter-app/venv/lib/python3.8/site-packages/Flask_FlatPages-0.8.2.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/Flask_FlatPages-0.8.2.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/plotter-app/venv/lib/python3.8/site-packages/Flask_FlatPages-0.8.2.dist-info/LICENSE b/plotter-app/venv/lib/python3.8/site-packages/Flask_FlatPages-0.8.2.dist-info/LICENSE deleted file mode 100644 index cf9a2a2..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/Flask_FlatPages-0.8.2.dist-info/LICENSE +++ /dev/null @@ -1,31 +0,0 @@ -Copyright (c) 2010-2014 by Simon Sapin, 2013-2014 by Igor Davydenko. - -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. - - * The names of the contributors may not 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/plotter-app/venv/lib/python3.8/site-packages/Flask_FlatPages-0.8.2.dist-info/METADATA b/plotter-app/venv/lib/python3.8/site-packages/Flask_FlatPages-0.8.2.dist-info/METADATA deleted file mode 100644 index 66aec23..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/Flask_FlatPages-0.8.2.dist-info/METADATA +++ /dev/null @@ -1,62 +0,0 @@ -Metadata-Version: 2.1 -Name: Flask-FlatPages -Version: 0.8.2 -Summary: Provides flat static pages to a Flask application -Author: Simon Sapin, Igor Davydenko, Padraic Calpin -Maintainer-email: Padraic Calpin -Project-URL: Repository, https://github.com/Flask-FlatPages/Flask-FlatPages -Project-URL: Documentation, https://flask-flatpages.readthedocs.io/en/latest/ -Classifier: Environment :: Web Environment -Classifier: Framework :: Flask -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 :: Software Development :: Libraries :: Python Modules -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 -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 -Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7 -Description-Content-Type: text/x-rst -License-File: LICENSE -Requires-Dist: Flask >1.0 -Requires-Dist: Jinja2 >=2.10.2 -Requires-Dist: Markdown >=2.5 -Requires-Dist: PyYAML >5.3.1 -Requires-Dist: six -Requires-Dist: pytz ; python_version == "2.7" -Provides-Extra: codehilite -Requires-Dist: Pygmetns >=2.5.2 ; extra == 'codehilite' - -=============== -Flask-FlatPages -=============== - -.. image:: https://github.com/flask-FlatPages/flask-FlatPages/actions/workflows/test.yml/badge.svg?branch=master - :target: https://github.com/flask-FlatPages/flask-FlatPages/actions/workflows/test.yml/ - -.. image:: https://img.shields.io/pypi/v/Flask-FlatPages.svg - :target: https://pypi.python.org/pypi/Flask-FlatPages - -Provides flat static pages to a Flask_ application, based on text files -as opposed to a relational database. - -* Works on Python 2.7 and 3.6+ -* BSD licensed -* Latest documentation `on Read the Docs`_ -* Source, issues and pull requests `on Github`_ -* Releases `on PyPI`_ -* Install with ``pip install Flask-FlatPages`` - -.. _Flask: http://flask.pocoo.org/ -.. _on Read the Docs: http://flask-flatpages.readthedocs.org/ -.. _on Github: https://github.com/SimonSapin/Flask-FlatPages/ -.. _on PyPI: http://pypi.python.org/pypi/Flask-FlatPages diff --git a/plotter-app/venv/lib/python3.8/site-packages/Flask_FlatPages-0.8.2.dist-info/RECORD b/plotter-app/venv/lib/python3.8/site-packages/Flask_FlatPages-0.8.2.dist-info/RECORD deleted file mode 100644 index 2ed5b0f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/Flask_FlatPages-0.8.2.dist-info/RECORD +++ /dev/null @@ -1,16 +0,0 @@ -Flask_FlatPages-0.8.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -Flask_FlatPages-0.8.2.dist-info/LICENSE,sha256=RPamGyoEn-tZtkCtAE5C33ySOlvbvTOm9yDCHndScMY,1555 -Flask_FlatPages-0.8.2.dist-info/METADATA,sha256=hxkge75xYe8VpJvx49HVBmcd-XQgmamvrYMT35jOyhI,2572 -Flask_FlatPages-0.8.2.dist-info/RECORD,, -Flask_FlatPages-0.8.2.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92 -Flask_FlatPages-0.8.2.dist-info/top_level.txt,sha256=SZf1mE1dV8r0RfV8kgp2psYwzSRNpGIlWZeeSEqo5xs,16 -flask_flatpages/__init__.py,sha256=Fw_NE9xJwBAFjeYFVTbQzCupLsr5sdtaPuRH2TUKt48,527 -flask_flatpages/__pycache__/__init__.cpython-38.pyc,, -flask_flatpages/__pycache__/flatpages.cpython-38.pyc,, -flask_flatpages/__pycache__/imports.cpython-38.pyc,, -flask_flatpages/__pycache__/page.cpython-38.pyc,, -flask_flatpages/__pycache__/utils.cpython-38.pyc,, -flask_flatpages/flatpages.py,sha256=7XO62umai7QYmt-_zX8W-OMnCT86_6Xb_Jo0-xMq8fg,13997 -flask_flatpages/imports.py,sha256=NS8dO9caxC18bZlo6ww_LO3pyieT7J5ISOObAvrEPXY,161 -flask_flatpages/page.py,sha256=g-5m04JynJRIybtkhvKkVhLbUQpO17scx93t423zgk4,2482 -flask_flatpages/utils.py,sha256=KAFOaktnApkmsJ8HHmYaaTQkZOJTEOw2LeOyp2deJMw,3286 diff --git a/plotter-app/venv/lib/python3.8/site-packages/Flask_FlatPages-0.8.2.dist-info/WHEEL b/plotter-app/venv/lib/python3.8/site-packages/Flask_FlatPages-0.8.2.dist-info/WHEEL deleted file mode 100644 index ba48cbc..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/Flask_FlatPages-0.8.2.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.41.3) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/plotter-app/venv/lib/python3.8/site-packages/Flask_FlatPages-0.8.2.dist-info/top_level.txt b/plotter-app/venv/lib/python3.8/site-packages/Flask_FlatPages-0.8.2.dist-info/top_level.txt deleted file mode 100644 index 212f1a0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/Flask_FlatPages-0.8.2.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -flask_flatpages diff --git a/plotter-app/venv/lib/python3.8/site-packages/HersheyFonts/HersheyFonts.py b/plotter-app/venv/lib/python3.8/site-packages/HersheyFonts/HersheyFonts.py deleted file mode 100644 index 3c3a2b3..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/HersheyFonts/HersheyFonts.py +++ /dev/null @@ -1,583 +0,0 @@ -import base64 -import json -import tarfile -from io import BytesIO -from itertools import chain - -try: - from statistics import multimode as statistics_multimode - from statistics import median as statistics_median -except ImportError: - # Python 2.7 mockup for statistics - def statistics_multimode(data): - return data - - - def statistics_median(lst): - n = len(lst) - s = sorted(lst) - return (sum(s[n // 2 - 1:n // 2 + 1]) / 2.0, s[n // 2])[n % 2] if n else None - -try: - from itertools import zip_longest -except ImportError: - from itertools import izip_longest as zip_longest - -try: - from string import split -except ImportError: - split = str.split - - -class HersheyFonts(object): - '''The Hershey Fonts: - - are a set of more than 2000 glyph (symbol) descriptions in vector - ( point-to-point ) format - - can be grouped as almost 20 'occidental' (english, greek, - cyrillic) fonts, 3 or more 'oriental' (Kanji, Hiragana, - and Katakana) fonts, and a few hundred miscellaneous - symbols (mathematical, musical, cartographic, etc etc) - - are suitable for typographic quality output on a vector device - (such as a plotter) when used at an appropriate scale. - - were digitized by Dr. A. V. Hershey while working for the U.S. - Government National Bureau of Standards (NBS). - - are in the public domain, with a few caveats: - - They are available from NTIS (National Technical Info. - Service) in a computer-readable from which is *not* - in the public domain. This format is described in - a hardcopy publication "Tables of Coordinates for - Hershey's Repertory of Occidental Type Fonts and - Graphic Symbols" available from NTIS for less than - $20 US (phone number +1 703 487 4763). - - NTIS does not care about and doesn't want to know about - what happens to Hershey Font data that is not - distributed in their exact format. - - This distribution is not in the NTIS format, and thus is - only subject to the simple restriction described - at the top of this file. - -Hard Copy samples of the Hershey Fonts are best obtained by purchasing the -book described above from NTIS. It contains a sample of all of the Occidental -symbols (but none of the Oriental symbols). - -This distribution: - - contains - * a complete copy of the Font data using the original - glyph-numbering sequence - * a set of translation tables that could be used to generate - ASCII-sequence fonts in various typestyles - * a couple of sample programs in C and Fortran that are - capable of parsing the font data and displaying it - on a graphic device (we recommend that if you - wish to write programs using the fonts, you should - hack up one of these until it works on your system) - - - consists of the following files... - hershey.doc - details of the font data format, typestyles and - symbols included, etc. - hersh.oc[1-4] - The Occidental font data (these files can - be catenated into one large database) - hersh.or[1-4] - The Oriental font data (likewise here) - *.hmp - Occidental font map files. Each file is a translation - table from Hershey glyph numbers to ASCII - sequence for a particular typestyle. - hershey.f77 - A fortran program that reads and displays all - of the glyphs in a Hershey font file. - hershey.c - The same, in C, using GKS, for MS-DOS and the - PC-Color Graphics Adaptor. - -Additional Work To Be Done (volunteers welcome!): - - - Integrate this complete set of data with the hershey font typesetting - program recently distributed to mod.sources - - Come up with an integrated data structure and supporting routines - that make use of the ASCII translation tables - - Digitize additional characters for the few places where non-ideal - symbol substitutions were made in the ASCII translation tables. - - Make a version of the demo program (hershey.c or hershey.f77) that - uses the standard Un*x plot routines. - - Write a banner-style program using Hershey Fonts for input and - non-graphic terminals or printers for output. - - Anything else you'd like! - -This file provides a brief description of the contents of the Occidental -Hershey Font Files. For a complete listing of the fonts in hard copy, order -NBS Special Publication 424, "A contribution to computer typesetting -techniques: Tables of Coordinates for Hershey's Repertory of Occidental -Type Fonts and Graphic Symbols". You can get it from NTIS (phone number is -+1 703 487 4763) for less than twenty dollars US. - -Basic Glyph (symbol) data: - - hersh.oc1 - numbers 1 to 1199 - hersh.oc2 - numbers 1200 to 2499 - hersh.oc3 - numbers 2500 to 3199 - hersh.oc4 - numbers 3200 to 3999 - - These four files contain approximately 19 different fonts in -the A-Z alphabet plus greek and cyrillic, along with hundreds of special -symbols, described generically below. - - There are also four files of Oriental fonts (hersh.or[1-4]). These -files contain symbols from three Japanese alphabets (Kanji, Hiragana, and -Katakana). It is unknown what other symbols may be contained therein, nor -is it known what order the symbols are in (I don't know Japanese!). - - Back to the Occidental files: - -Fonts: - Roman: Plain, Simplex, Duplex, Complex Small, Complex, Triplex - Italic: Complex Small, Complex, Triplex - Script: Simplex, Complex - Gothic: German, English, Italian - Greek: Plain, Simplex, Complex Small, Complex - Cyrillic: Complex - -Symbols: - Mathematical (227-229,232,727-779,732,737-740,1227-1270,2227-2270, - 1294-1412,2294-2295,2401-2412) - Daggers (for footnotes, etc) (1276-1279, 2276-2279) - Astronomical (1281-1293,2281-2293) - Astrological (2301-2312) - Musical (2317-2382) - Typesetting (ffl,fl,fi sorts of things) (miscellaneous places) - Miscellaneous (mostly in 741-909, but also elsewhere): - - Playing card suits - - Meteorology - - Graphics (lines, curves) - - Electrical - - Geometric (shapes) - - Cartographic - - Naval - - Agricultural - - Highways - - Etc... - -ASCII sequence translation files: - - The Hershey glyphs, while in a particular order, are not in an - ASCII sequence. I have provided translation files that give the - sequence of glyph numbers that will most closely approximate the - ASCII printing sequence (from space through ~, with the degree - circle tacked on at the end) for each of the above fonts: - - File names are made up of fffffftt.hmp, - - where ffffff is the font style, one of: - roman Roman - greek Greek - italic Italic - script Script - cyril Cyrillic (some characters not placed in - the ASCII sequence) - gothgr Gothic German - gothgb Gothic English - gothit Gothic Italian - - and tt is the font type, one of: - p Plain (very small, no lower case) - s Simplex (plain, normal size, no serifs) - d Duplex (normal size, no serifs, doubled lines) - c Complex (normal size, serifs, doubled lines) - t Triplex (normal size, serifs, tripled lines) - cs Complex Small (Complex, smaller than normal size) - -The three sizes are coded with particular base line (bottom of a capital - letter) and cap line (top of a capital letter) values for 'y': - - Size Base Line Cap Line - - Very Small -5 +4 - Small -6 +7 - Normal -9 +12 - - (Note: some glyphs in the 'Very Small' fonts are actually 'Small') - -The top line and bottom line, which are normally used to define vertical - spacing, are not given. Maybe somebody can determine appropriate - values for these! - -The left line and right line, which are used to define horizontal spacing, - are provided with each character in the database. - -Format of Hershey glyphs: - -5 bytes - glyphnumber -3 bytes - length of data length in 16-bit words including left&right numbers -1 byte - x value of left margin -1 byte - x value of right margin -(length*2)-2 bytes - stroke data - -left&right margins and stroke data are biased by the value of the letter 'R' -Subtract the letter 'R' to get the data. - -e.g. if the data byte is 'R', the data is 0 - if the data byte is 'T', the data is +2 - if the data byte is 'J', the data is -8 - -and so on... - -The coordinate system is x-y, with the origin (0,0) in the center of the -glyph. X increases to the right and y increases *down*. - -The stroke data is pairs of bytes, one byte for x followed by one byte for y. - -A ' R' in the stroke data indicates a 'lift pen and move' instruction.''' - __compressed_fonts_base64 = B'''QlpoOTFBWSZTWVJsucUBqQ///djQiEJYD////////////+oBAgQMBABBhAAoAgAQCGEgW93utd4KUUAAAAAEb6qgaAAAAB99732vu3TJpsxNajWKgiU0MlUAkKEglQqhbNEpBpps1CWmvdgB3293u4BtvkAee93lcw09mjaktcupnIQO4sDu67QS0Nt7sne++W2s8b0tjrjWMwpo1VV9aAUpS3cHa20eQds+7zrffdwaNAFBXqXN9evUelABUgus1S7Ohb0956GjW+2kuQAbtk9FV9nd6fVUFdSoNVQnmqldjaiKB6D0nNjWAA3tmw1Y+3PtgAfQa00m300A0kFat1dhF9nfYB9A1oAoUGgD6HpkKNLMdO7d2UH33128z6A1Qe2aoB0GigPJ7YUHoHbZU6fZvk8E+qANO7A6V0iKPXoDoUBoPR98ybupPYBoFG2kPoGlPthkDyp1VW+8LjqeX2dB9O7b23YaUOm9ntg1Va0vrhdWmfWDTL6Lt1WmiKNYIoltkkaa5ZdtAVVVsDXplrNvbuX3c3fVLslX0zQrpQtiUXbPp0eig6fb65d91U9IV8s1RZqXJjVZADPg+d0r0tZl9sdDanTuWqlXR1pivtegdynoeEM1jSRKl2xVdgoOdDXmV329fKe2t0YczAoVPs0ut6ovIyl7dDtQNK+3vYyF9nXQN2B3d3XSjWnTp03bu2Qt2a+j1qMQGzszV7arLRWmztx0CuXWqWxe7vbXmsUeDilQ53VQNPOYdLWrWVM7dHJd25pp2w7rdvD13sAaNS0ZCkQWwjnvORShm1SC1tFmpR5lc6qcrO5jwse23Ozzz1Kbl2ita9evT77U6xDsZEWJAoEkQCaAECNCExEwhTyp6ZT9UeJMg0A0PSA0AAASQBEkElJ+JoT0Jim0QAA9QAAAAAAAACqJCAjQ0YjImMiNNDTTEKbMmpHmlNpqekG00JoyDRibUEmkgiUQjRMU9R+pGgNAAAAAAAAAAAARIhCaJoJkIanqanlU/xNTxTaaIjUaGjQB6gAAAAyAqRBCBBAgTIETRqmzaSk9IGgAPUANAMmQAZLPjKZiV8E0j+3ZPt+Hs2p9r6/p91f2fQmLHv+v7rV/S99uv4cAAeiekk94ASmRkGQCAM9hRB+/7v3f1fufg68zMRL3V3Tn7tMRETULwARYAMAQAHfHlRXPHNN03DDlKuB6JwAKQOt2e2KnzxPYrp7J+Yk9aYIlL6CQUShIfAAgmf1If4KH9X4XSUarVsv0+Wp/c/us8sFev8UfEjq9yJZSMJzHf3/uv9Nrj/3251yWZn2W4n07rP71h9XR+uhmCtDNXef3uf5c9/w/l8rr4SMJ06KH/HHaaz/5GCbHmZDghDoWHxTsf8pYoObLgc5Lo/wUlNdiZosiiRJI1ZLoyMdHA4Yt/zuVk+7F/8OhNjRyN6dByJYsZjA5YcsdAcyCo5EqdSRyOSmSfD/o69PFN0idIMzf6d+39mWSQeDYqOi7NstAyYYKJEzQdI9A0zJzWZZIiXOwplGanp0Z4sHPOcMo9oBOznl9IGm1i3rKOlXLPpifGWekC7PKVYYzSf08CoSEjsSg5k3rTzItJtIPyalo8NPrLzc7+KSfOR2KFSh1GO5I6miiaAxoZGhbCBcxPC0tIfeASYP9DB3gSFw98+EHv8aC5mFVQbwPCd4alw4ni3VpJi7dmSzYMcR9IP3GbtLGOgzy97dSWG76w36v18nphWT5uc2rvrl6HXOydijuNTHrGpBtNCLYVgSJaSlKRGenx3o7Zxjd7SNaHvGUuZNk+laZNPXUh2bJgrV5xyb7fB3comzHgYPm2+zw8Pm6fZ5GnJVTp1tDDVtIbMlZsGOoNlviYtrhxtXebpjaulY3N8JJK3DgDshJ0hhgPh8MqeTIDviu4TmzdgiwM4oFdMgDrhT4akkKQSUf8OtfRBiKpQWlVmoqN7C18w2Vy2jbMLhlggkguVPEaslUWKcKqir1UQiXPNl6LFE0wF3owx0yFBSxZkFfGvFp1oggLoxGCYDb4v6x829j1i3UoHc4WEAuVTrjqxmoS9DJsN5rJhiecF3TTVVGIssFXo5sbTgbDUZO7lT0ySmuikpAyzW5XCSo0x10lzESx92uhTP6DEV0mue3aAZjBuAMgzTKZ6DDmM3D/T/qt0XRpLLx/Mgtxoc4tDr7mTrTBLpr7GHroriYYZibchtgGG+E4jCsKScwZKTFhoWdQxgxM8NCUuyyyieFTRWWcNFrKpUxWagKis0ZCZQdOuNqQVPp67YaPhVU0IkdSkYL0m6lWnTo9ZlN8IDMixEhD2Q2LWd2I6Hs974FDA7WiWknlGCHZG7Eq27v2kjBkZWdqPMXV36srtWhvxt7c2wLNJoZq0Nl4bt7bYcjnVr5Q006mWmHhuNkKetg3saZimKHNVU1o2lyhDp9ux3ksZLTCcqzGT6g1+HXOY5oVMJjl/jjOBF2Cpoxa9yBXJU8qP+PTSD+QsG0MlDnp1pUi1Jvrlg7uZfB/Qn2JPh24h0bD04OkbGkngeeMYY1xaEmD4q6MRYbm/o/K7fHLW5zvfHKZzRydkYmJhLln7OZbE/RsyRidM0UnJTGMH8fSK+mdT6eL4WbalstvS5GjPmQIQqrkJbe8drz6wj6n0lgbOpFaF6m23ppmsKMUx1shYlzfyq1lfO9ZmbrTxEk09JctHQd3LuXLjhavrxHOtMKNgp2z13lRpqjB5LPwcbE2tghpq3T0hoaDlWOKqOVtZyBMazofPKTReLBU1Nci2vtRLJqpijV7dn1nIpcwlDxV5TkZHJciG6yG4axErxLTSMjV8IOWtC7YNaObVwkG7HLcRe9FxDyxJrMRY8Z9obMHNXCcCHp9M3mpDabdLZ4TNwfpr4lHpzoYD9idYWj62mFIwWMeAZ7g/n1mXjkHB4IXJy5p6z8vpDphdsPTjm1cGbjSRGzKETTHO3W8S9bCn3zVadpWo1nKONN1AyiJOYTPJ765taliBZZ2KUob1cplO0dh5KHZyUsNpklot4YEtPWN2Ogx9GqqOsxsrjofq+xr3h5YW5o9bDmDWhN7cmtaZVOkOIuudrYZaMs6TarLVlWNuY+TPpM70hnQy4lm0ozlKBODPu9d7bPeBKFoLLexh3pwZV/IP7QA+0APz/wUIAp+z+hKFP0CCQgGFLUoB/VsCkZP2ikUiulIofzBQlERpQSK/sf4EKUyRA1Kjibx/mQcMrWUZPjDTALBUIeSAGFAlkKlxUjEUOilLbWxt0snWrOZFSTm6HnKHH35Q4zVodZ8VvygvgMh05c5qOlJwrmpqjio/7gP/nB7AwjkqE8YiHrKzGfWQQ+MozQhkYYI71K3ZdGqPFXTEqIyqFyKDiQyB1nQZAUwSfXdDHTF4/VB1Q0moIaQ9nss8nhKoB0QPXjwYbZpnohh7fpIRh7BgG2Fa7ED1DGfGLPFErUr0Z4zTpA4ycGpDGs8ZsSfPlh1ICysxmMmPWQ6nxBVnjWu1d/dJAn2gTAxNJ4144kr4OPx+wU0QYQsnZ4FaHbelkes6Q7ZpkMTEDSTaFdvCEkGT3dm3aaQrWL66ggVNRn2buJ4wF2zb6qHWocZ9XbMX2wqSWQZJeUPU9ePBJgk9fWvidTj5b6rTpsw75a7fgQnyw+M9fWdfj8dvxnjxhYh7CESqrALIEEPiTF5ZxlStdPYzQyG34CGgQfl9Q76Qlh6/GVlT4r48dsh4+J4QjPmrFnEPjxK/HpCIQvaC0SdGHyD7Ge77ITJPGaZWVPGvggaVXjjXH52EnMk8ds3IMk4njt8aybQXTMYdeMOjPCEevUD1OuvG8IRJtOPU8akPjPE6QjJRBQXiQPnlDcJPlk4+cp4zHb1nxDrXr69VJ7JCCB7qldpjj412QidZ8V98lmk8fj5281fCEZ4nisArU8gyep61+yEgDA9Q0IevX6MyQQIL4MD1urJ6zUZtN6KadK4+M8eMhUkYzglejRh9Fde3xmZTbXx2y8tcfWtSdeudsPHideunE6zT6kd2vUX3KSuhDjxrIoV9SI+ieJOPght0r4m2dQFA0h6mozT16IadvghWTeiyHGHH5AAQ9Z8ej8fHb8ekGHjxgsmnRBJiepUr1JognPlA410QSHE27dEHw9pWddENSFJOp67TcjDacZ8TIz1ihK8fdU9bEPX2BE6mnj1ORjEnCCTTJSCexmPifHx69IJ6zwQR8dEEEg9deWtfibIduMyRxXxK8dSMx9dJ6nHTyAENU91RdBD5T18BmiAh6/Hx1A8uO2ePXbp0htnWaePYQGs9anr8W2HU8YVnqL1qeQgH1ADM+j8fjjKn0ZXN2bkQrpbmZqQRH9W823dTBuxm7UnwZ3fWnIYP0nTCxm0Ol/mGLP3DFrJj7zh4Sd/JEyy4f1VDD/T/iwOb/44L9PS8PYounv5W426u7OtENFhynj/DzOn6nAVHYhA+43bLPUpcHKmzMQEjlMJaCOh+RDKZgQNyZvAiYEzD9PEwb/xU4KnGX4Y/D2W8NMKk6Z/5NrZ+LOJrU7ZBtMqMbLMrN2NDghHChkWLkDubWMCr/axtDp0CgejMfbUttqyzdk/kfDs+pBwwfNg7nIUMPdGhA/MYeeiiYDx4zgOuZOjBs0K69eSLnJKoMwiFU6WhY9TAkcjZM0yU50+U/DLuv9H6yZTb7MvyHLz61ta18R6kElI4CUhJYHBAnFLg7Fx9N9UBnodtSx1yIIyJmBreLrou01i6lBg4J0OCQcicNVUCqdHoHEld5qVExywts2CnSY8ces0kDshFDow5DlpCmIcEPRj4e4yX1QYnEiWJkHHXUNYnUGM5BwGCqLqZhodTX9nMjoSLFLnVIhNjwMY9CvgizI5G/iMlIYxLctPk+rZp9335cO5Z7tfoGVtSOvwnYgQHOxgBqTI4+iCBukcJHeXiJgUCpE4LGAXklfQ4ViOhewJeDe5kwzBrMjUQ4uSwnkNEoxMnkXC/IbfcQhB6AM9x4OLFxUO0VITiUTTDRKU/Rh7PiOHB+Ryo7Q0wy8Kcuzl0mX5vh9571l9nb7R6TKdka1XgsqCbYr2ExkdAdEDA0NiEQia7CJm4wjsTOlt3FMieDk6SFtIT01zwExyGJmUL7J8Lnc2FBhGrmJ2NcQKDSFqBMTpMLCUnyJb7Q6fjcNnLh7sMts+m2Uwjc9qeVNtLulqlUuy5Pyf4fWfM0PT2cYBa8TBlwGYDwRPBgdoxJ5FNK+YwU+idS0y1yj6ZfJ4bZ+htOT6mk2lJgEwmEyyDHA9AcVWgayOe4timpMoMXNTLD8Oxv0mE9tZn4cjwph+FI6UdH5Kc/7XLE1IwOQgMc7BkaUORhhUMTQA7CqEhkdZyZMqHYgSBDHIxpU7FssAI5C7rFa4GYy7DB5CBQY3JnGO3Y6F7oFExOuRuakzQ4H3PtBhAj8TttJhPIN2BPa6k383wO44XkjkJzXqYn/Gep7/6/uvVY8KpqoJI1TMmOhubsoJ+ZUe9LUpyuGH3W5dS074fs5Spb7drbRKUVPw8MKSn2LUpNTT9N5fyVbTwuepuaT2R7polIt8vEfkwjJScTynXnACZeoExZmqK7+/PBY5JrVY2IhKKR6IYU5esI70n4+lo/dTlT07p3Yd1NMLfYiOaEA6ntbgwC/AYnYsDkDnZCqSJwOCglWpMgKZkZmhq7pmZmK1F1YqFg5EdUzAzDLAuGWfBI2yAPdixexCUCo1xdwckBuwsiZ/edjChMQyNRjAao7nKicGQnAolMOkBUMh5CJAmCy5GBw9z3H7JJaJ+q9lR5Mu6ZkntaGNE9j2cyMyqRPTS9qTDol/g22T5Z7P2tmpOHZw5OnY6YbfdP46Nk6fk2wwe87zbyfZNseVJ4Kfp3LzDMY4LHgzPcoYCVDEHJDEjkiKJYxmeT0JHn16YHYzMQyMw1yDc00OhMkbDmoYCGMMPzT5eHc4afyUYfDJ5H3Myv0v7vDj0TO5IJhEk2AonPd1CJ6G5ZRMzEngwdrm5AgTUTrga0cjSsAiRjkxqrHgMqhBgbWbiZGtGiwGHkEPgaDcydj3tIuB7nXeR3n9DqKgyYNRhS3D8ASgexBITr0W7hUzikvob6mZycTEFkdbxOhctNBJshlEGDzg7u6zBjFhy5j4JEkuBvcoGpmETksLYzKmAoFjvD03mYFCciZUUView+SpE1LDjsnPQNDMgndR2eLR8lH3dPDPh5fupwmlsu1W/Zw9ntNpOT3KelOnhNO3FuzuLcJf4qrdwx1C6gcnOKxUjIIm+R5JkVEIGKNmJjHYNB+8E58mtfcgmAqCGDgMD3gGpIJrg8MZq7+2GFF4Wus937rfvI3QVy+EGUTqsSLiCwnHyPEhDsEV3OoRFIw6GB6rA7Hf4w4Pq+r2U8PZtotlhLMtNvrG34UTNsyBsXDoeDjciZniwcmp3NQ4jyZFTINBbl9SRuehNSL9mclEcXQGJlDwWjybgcZi7mqyEQIHkr1I2KqJ5P75KC9BdByC1HILwLWDnBoJgitiRzUznRiRSBBTidCg4mHiQMUxAJG9ZUaZxRZmaaJcuHJADEGNBkVVR7HJOgqf2fX+kwKpGJ7+3RGYdFrADMdSEygKYbjIhbymnSi9i5SZqiQX4DjPCpkehUILBOiFnNPYC5QPJ+BgHwsReQc3Myu6041NwyMD1Ce5kaPHiv084t9H6+ylVcq3SYUy9s5V9lHw0qerW+Hu5dn7OzyYsynC3LU+DaJ8FAgtFvQqMXeygdkbIiwMtUmIRVCKaI5oq0O8CRMhuTwcDmXwIV8mwZkzuVgQGKmxgaHJjwblWMiBiUNiSmIGJIrZte04EIjkIsTsdKkCKRgJl6B6f6BvgzSBchNeu44v59p5XLPsnK5M7/NfSL0yW2WmUWm6hFTJGBEBHRSA0gepAIkT0JrmiSivN7mZL1c7MLO3aZlmLIgAwdnHM8RXGPQDcoGVDkoEhZ5JFBXGGEsBg1Uwvh5GmEz1QsjUkGYt+hWpka6nEJfRxQ0JlTixkZDi3dOfR5w7KdOD3K9G+lqdqPc+p4qjCbVb9FHlTw87v9KitLaZ/PziR+yyPjbJ65CBAc7H4HcPUMyfUqA57lTMzTdDnoUIHcVggaGY7Fch/VRUDQoNKhcpQiwSHHPBc8hJRZTwTkBjwMDsMLxkKBaOLBA17j/fZVW+5WazW2EFQlJS6JlcyMiZrAnbjtGhch4A6El/tZ4YUxJYPZigppt/esim6mbClm5wUktkwmTTvvGKu6ti/d+xPTam1tqn0YKWwVnwJ7UHA5Ts8x4fL9Hu0+LdzEWfhUlqZd2EQ2Fcc/3GDb65wTAdFXuj4Sc5gEBkZrNqk9SkVFHLMfZ7Sk9nA38rT7TB8dlkZx+bKgJoGQFm+R4z0qYsJwJh20iSDk9SWwbGdzggaTNiGxOFfBMgZBiFN/LgVGZ7kEEw9OjECez0gIoKYdJIc+DqdxcjB1Lmx3LrodQzJjB0MQxJBnAxxIizFYmSOx51O7LyptR91z2nT8m2Wi1NsPpRSnamiBAqXNhjPogVfOQOHJyZuKkWMxMXOwYfH8Hw+XUh5fd/Nh6Kfd7Lg2NrYVEHYYOWNzsDmYggk539A7uWOEymceT0NJchHu46qYdvV5ky5mBoKIG4WPVSDuJiCNssGIr0FnAKFAcNQU0eoysdR1QyjjrKi2NiBYgODC8CfseES2LmZlruWGF0JwLKIwSqJhSaHT6uy3Y2/VZ+jKU/NT8x9U6IPEI9wJBOSGPU8yGWl+khydTySclGnA462meIrNSizP0DcwJDEzQ0qzSJEQuB0IiKG/eDDC66OM2DOw0HEjE6+gQ/7D8j6eqAyPhT7fot3Yfy8rFzy/d8uIo/IUtP17r3bqHQF1JmexBfHsL2FM3PIuVIy0F0xa62ghJ/c2VTDqEeoGIrm6JliQaDDqC8kj3UjQgVJHCNVsTFiSc7EhiTBwbDnc2uZmxr06T6Nv4Und7vT2yU+Tt3U+jKaNtFNOGndsdjhj2aHu+rp1I+J5d3Zs4e7ThpMDBdC6c0Vdl3CyqOZYmJgNgNckXNQgujssr8nBumC3w8FcTDh0/fu/edmXYzUpS3lfw1liI/ShVSfd7S1PL5Yh9FHyo607J4kRkOUNsjMkTHF1S7nYLLxmSFvuWAJ44jOqETudjQcsalyZkFqPEFw5MWp6DnB1TmxgDnGo9TxYquFU205gP05WRwLlKhpJdSgWZzYoehAzIC2CsOWKC6EpmHQYgYjkBipIoSOTWsShYYliVLmRI2K4t8WOpDWhCQhwQs9DHI2PQ+FkaCeb9B4nBCG86ZKERc8JjYmqNiMMMOmKuoDGhQsGgdQmTiVNIiiXZbLMPc98V6kzqMsiA5mt++R0IyPT2olsqjmYJxNfmMk3M/kacp8unEdjuhyBYuCgQGDoPI5GCBmRJHoXPUv24NAlMgSNCxM5z7zP8vIlMYQuRb5s2CPU7U2enGyDscma79yr+q/ebGfNiyG5EKIFEog/qp+T7MNOlNantY/JThhe7crdlabaYeklNuI7KPpsW+cquX4moO+W0wp8YWpk+AEzT0GKkmMiBeC3STmhA5ELEkRMG3edPp04UnO2mHdDRd2pLVEypbAs4qJxjLzuSXoWT+jfBM9CYVoTEFxhObZTPAmXU8ub5rUgyRgls4XgorvIgXJrEzHDgUEoGOs6jCzWhztrBm8BAyGzi4u6w4MtPIcoFc9aWuhcaDpeBgXLJg8Dg6c1Pc9SJ3KGHBk+rJiQ9OvLBh5eXw0dHpfDHuuaW/Dutlp+FqfKe7ulGX6exUtPTo7pBQMTxIuuo4phiTHmmWRE62DkqOE14GWxgpkIO7R69g8SyH/Z7BQBartEoCNdAcLncXYggIJhC9cySc5GyQFlNQNpwUiSl0PTaFiZ6hwSLGQ5jwYAeJR607d1sdRbkOTxF4Lfq5/V5ZNPXzNviat6py5PT7mdvSATPAZHByRPk4oHlyITMEqrA5N+DweorC06VZ9h1fOYDK/fD8DVafRvtcT4Dqo6OpIuCIQw0+Z/k4+Bg95A0SRnmUPkT4EDNLcMj1F1PNAqYEl4kvIpryMBumRoFJsO00o5LsepsWYKYnXyWz9KLi+HOVOZSpY8G3Gb+GXqnNzYsLM0LFIIlsMZaaaI38nkiak13JBQ2ODEhc4HNxjIuGhUgZzGMg3JKvpF7oKRIjsEySDNC6Z+OkjLN0PY9J9kyYXcm6sikFKO4vSNu3Yy7m/HJ2slKXBxqEuxiXchj2O4ejq2uuSx74M37HygSzUhOExzlgVuq7KXY72KjViIgOStmZ7X6hfvzATlTQpPqNijEgOerBuGwOFiY6yN/WKwkGPq+WWljsOSCxqehPIGkGUxsyARlGpUyhNm1THSLmJkaGRQkGp0KUpSUvsBPH/QD/9Af/wDy5dOzsrtlTD6NtvLzumCI+RMWNW7vNQGyzpwXkRF5ER5nTMTIMwlthcPkYn7hzH5ZcblCZqAG+v9NdYgAKIfzBpRLo/WWzyh3HK/XhEZRTXFU8mO0kNNcKUcHG8s7upX/iJJzkCctKKsA+3gfUyVUnf4UEJJcDHrWf9YAT/rACADogzBmbE1yH/u0jBR0j6m9IWrYwSEzIZ2SgP0p72Zlulyq5Od4uMtYp2OHEkmYrnJttmuaaDxHGCQAYNYYpIgguLnaiolNXM5nOKRhmZhMZK0fhG8kwzAflZybgxqwjhT2coLjm80Am0TsELjzPGneVr8dEWzcMLht4ZYPznKGN3B347ZzOGBr8kGB2RTO06zvJDDRg1mElmlllaOVyDQl6gjjIlSrJgm1JJwkchMmBOBBAqShMQmIMWElEFlIsmHRIRNBIwyToG0xQjVrRAoZC7C/hD8z4dz4HwPoPgKv35W0z2zk+o7w1CQ6RAciRJDEWKOMpPBSHLnJmyCasuhxrTBb46RePk0PkTONL4mMxbU24oYGbKPGY5mfj+8vM75z3y9da1l2LS0KWqALGvLf0k8GSIcNedQzvHdp772umcsIqGT1DhH96IqHXiQg7TeHCPCFPfOzk+d5/nAB6Zf1BACAGT+zPT+wdGBAGzIdPTKPPaKq5evblm+eNAzMzMfaADuehBXo7JER3yFXqKr1631q8YeHIY8YZJAVvhfbGmG53MWR5Hm15hL+ChhCKtd55HKqafzzl8SF5HG7KSQbMIdnvnSutO13s2klvUgN6QzVHTmTPcVNne50SESF95WkdjvbvXdd5lCjokWQcWDu1TNu0DM8vDUmmJo0osoOHGGI6MToSrJHIIGNvjTO2DM6ZZppTOVzDCUoXG0qbSmYVGV2tc0Vtha0zGFSmFtEywYOioMMKELIQhSZIlkWSiIfp1P2PIxE9CB6LueDPC++sFhmkSHgajxjRIkMSEw7YVlwzl0WOwXOGXTBjvA1xbETDU3yYbZpDY4mtZjADezRu1mY/fv77nvmue+iij98paQpbPyILMMtL8pUUYEhctvCiHiHXmd2/PC+dXTG1h3cfyVARSFLawEtc+luioOoST35o8vS3581tb551rffc6dASiqJBOYBZdCLSlUMpSkAqlKCqUpTMEC7AlIrfLre0jYjyqZeeVl7unWNEAIA8HPAQvFfXlkyREc6N3nlX5okeFOkeAIV+LHeWUR3ztpEecq/N6g0YQwyWMhAV1U70yiM49JHO8vM71BTCNSGEJO9bzliIpIcQjAYRQExAAPmM5znOEIjIQ4BU1U2FghWYaOzZEJh4eHcT2rHJJKFMlDsSGCoYTlAldnSTEUh7CDEyMNmUwUwMrlnFMG2mJk0JVxE0I6ElGhMmGg1AwKUywsKM0mSQjRwoGGFCwFCwSU92ySbaYLpXg9NCZIyAovIGGt6m4fC1IzXV6HM6nGchwzt2a2F0ZuQdRdyttpbhrW9ewO8aEWDIwJNdNHc73vZKlYVhWS061DAhPNHed73pBQiyIMkRrma3tpEsFRGM6u2tag3JJQqpwQAgCoEBeNkyITMhImZmRCvzP7WZCca5vnnRKBUOLtAxUJhrWtaBKZiZBIKOjHWtjB3JKCgbRkkUqPRwcjdtbwVyRA+hNVCe6KLLoSdiThaW02OU2nE0YlaMVba2ChcitmSzEDTDE0q1SUy3lliQfRU/lnf3e130kOMMji8MeEiQcZzwcmWubnGGbDtm6u82Xvd850wNTnd7djXeZprqjXvMqbQdQhFRiRcRduwCLECtIq7ux2zVJVaEXRq9SGEXOc5zMghpEEMxETMRFTMXMVVBKguVUxnSmdF2hF3VSJJ4mYkELaqaoQtw0dmfBnqqw8Oi2lvZTZtqSThErTTeSTKdCyyCsZSjr0RZRbjqlI4SMSOIeE5Fza3ZiZaXSzay1RpTLEZYccaxuc3CrjhiC4oytcopRdpcfX7W++PX28+/Hfftz1rvkligmooJp2oFSoArcACoquYCYAiA4iikiBhIgRVCbQk2JFDrJIREJUCsCTHr1gSG2BIopANMNsA2xTisFMxBcEUqIaigZJICuIDiVBTEELIJogDiOotQApSCBiI3BTMUSoKMgmIDIISChJmKNsUJEyxkSoyKVEUqIuIgJUgVhKkmMimISoKTXOGufb3zDMy223zWta7mZbbbbbCGa7sTzQ71zsDMuJmZmZltzMy222222yQps3wda3bbbbbbbbbZCa2GymEImGGaTaBN7m6OGlVV7blqqhNmGzNKqqqqqsALmmluaXy1VVWQmjRsyTDDCEkaUY17MOzY0Jhmqb+agYwoMMR8n0nM3FZVvhTlqGErnJRhxKYZGBg21h0KMKjDe22bVail5WqJteCqytlhmLmEytlYxTE9vPdqnIPOJ5gyZG0HRx2mCCS6SHLaTzHceSax3e26MhpFL2a1HjtMig0pZCZtNmvpzy24ZmW3MuVtttttshL506dO853zMy22222226zMtttpJrJDp06d5vuZmZmZbbbmZmZmUkPOnerrrFUUIaNaXCTmtaaXWt3o+DV1aqqrCdADonTetioqvoAWqqpO9O9XW1VfyWqyqXBjJLSyiyxzN2IHYpu1a3kl8tY9GXTck2tck4dQmnNMLZY5EpixlmDlBiowgUkGDECwk4MSA2EjplRhSVNC7VhNsmDLS2WWcKkuGZQ0k2gjDaTYMUlRIkiVlBPYf3PySa2jfB6l+DDySTEynh+kSTKq3ZSXBOZNbpvdR3u4giov0Nmj4eTyjZS2dkkwzJAKWyjLSypS2UtlGWwHfBshw1NzKWWllpZaNlGVj9uGTLLLRsoTW+DTZodyls5hmSy2WWyy2WWxSNIcN8HUho4w8Sm6HLdju8iMqAKKHSxg1gdhVRRCB2EYMJMpGEzASXNRhynh6mG09jCTHHssZoEkWdBw0GpgiJ7HJs0UmyaME6GV0ywUmNHC5jDSxiIxNSNqphgJYGWWLLrQ+6c8Nah43V32Gneb35cLvQXd0OqGGs1NGGqYMVR1tV8kLVVVGg3LdWqqqqroC1VVVWgB7h4ZPN61Wj3zOm9bNlmbiJOjQMcGOIZQOVCdA8pggsjhdpFUDcmUwinLIyqKNLZphQ0htlk0Glpk0aIFEwSIwKUNkWBmUHOHAOIEPmPCJzzI+cIciWLJK4GjNjcHeGknx8Tsw9xWQypiSbFN1LJ3KCGkccGTJip9gAc6KiqiK0tURUREVpaIqqrZA4GgVUWeyGQBMDL22Zh8JdnacSq4OFFEBpIw8Ejr5NUMWUGiYUOOJcRSgcgIOEDioWkOHGHCaNU3U0wtwaSmTCzAyzy0zBejAlq5NF2JdURyYMFhhQoiXL3gjUZ+MoyRq5JmMNTY2UDhoyWLfD4eXi0rWbkaF9Mq1Pg/qChbGdGsVuT/D7nqdzz7dD/hN+uLHj86dT2PoH0MjuULAl6pC2O5/MkAeRkj9jqkdw+AXtsxMkIjHUFPE/e3IRDdVxDob5pIRZAeO5AXABzR27KQ9ysWtxU8AW7FbpCZeU87EFgAZmof5D7nyGPJ8H459zx4+p0O3HZjrIRSUEV8fCybW1bsZGNN1wt9wb7O3szQWu6+xD2HIlLpmA6jKwMeOsuOEheupEdIt3cDIHsrurFi66BB4yppB+GPgTdL+v38BQYLsfg0a+HnPfP3fuKwe2JWfj85lW8yZxKlTUjM7tf4dJh7uW/DZExxlbPVxprZybyeazNBFO3G42FcJcnl1V1wcuycjX3ajGbecqIumy+MYaO/Gl82WynnNMfhNcy7xmiYMP4ZWjDBBn8FywSe5gFivzyPJm2xrszh9T7ue4/0P6n8fVt/NRnJ+52/fy/0cGpEyUb/niE/mlvxgfC91WX1Y+afxnAZVKkGLEYNU/ed+8A9xhEjL2cmfM9zMiQPlgvoOfMj8jMPoRJjlyYwY3JqbU3JNonT2MuHK3PDa2Tld00eUhZw00GUJKSizDR1AnJKOBqVfpcgLVJoFgYn+Y8lonX1TJhj79jw7qIw5H1Y+5GB2I9mJOiVBMvatoRZUCKf2KYl6KTlDQ8ehncVKkBjGxAh+OBMWM2k2gNAkN7VjJnMvoDH4eL3Y8Hi7kYrE3aCML8ik/AIeXFOgPJlqG8TcM4iNgY/ADMqXKBpFwXC+bxqQLJlPXiijtH92UczMfMthh9O77D0mn9vST59Gh1PsycG3upZXl3OJ1MS3yaYNHLhiqVTdLZrKe7LLwPDmQqbO5smTZ7FbGdTxYIWEnBVJS+BZYtA8nSdlGjk6nK3Sp6MfXlbj1wcJUVKkOWuTIqqmmThUYzFgdsVW5yEiohUXKzKMMxMHVAcoczRMubVLxdi5UCRvsFcshzKx+4/M/E8Lj9wSgOEh5Kxyhw6muxBdwZLOW7kR3MCNRcpggHV9Q3f4Oh6HxYcgYU+Kp5CxO0LR6/iHUOHAYIdmgdMe5TJk8mJiGZlxNJ6mbjGBcWnhKZmHSZFo+yp5mS6ikikqST8FCjhUzJst+JTSfiShc/Xh+H4nqNwe6j3UUJ7J9lFKKlLMPhR1MSyMnBPOtokDZdhOqW2+T8DYt2IYSIoeb8SkPiZXCYHqeJU+Rkek9hRb/kv1JO/mWpJKpryWhgtJp7qNKLczMkyQeJOIokSZR1LI1O5QKdNCxrwTSVkyFBh+uzZhMVB647vB+fv5fRyoqJ8OksotErglne06SFBQlFEURiataV8MOUKGWKmdSYWVFltN8GvFRToEExZR7iI/AOTud2HhGJFJJGeB27u73fpTo78SbU3PcSkUoUSkZJSp4lkNkmwuRgGiBCxLGXotaDCe2XBuVPo+E5+i6FH0YMpanMg908RjRcQO0IwaOXpwrwJoOwOHRZUZSKioy1TSkXCgWltxmTMSWOwrRYmi5T7HBjbx9AoLaZBiOyvQaSfNSRx8YUYTEJRQ8kuNq5eChBh9ScDTCIJBCUUDIiLDQlRRpGWHlSUoYhu1mGZifV2d/PJbyHHHozDBO5w0HIZibmvAMJDgTuuSKeu+Xl07JMns7GEaZjfHUS1SRyVFb1Liy7qL1LT2a2wUXtSZZp5H2Pq7niexbsUcEekot7y094dOHTbMprVKoqVKz8sNsrtSaKE+q43GUaTRGBR6Kd9zu95UmHhnwVibHlT4p7rSk6ZU+py0TiVKO1OUU7qTLplKKLUl5wmHyxLmJpcTLcsaKtlnw/D4dnie8s2Ut9GJ9ZSHCuaB0+Wloh3Op7o+D13QLIR0j2DJsr4lFCfRGHK0phGFFG+eVLjsYUwxGeZWVGGFpb4Vu4SmU224aPavfh9KuYiWbGfOh1Do9T0RI9DsZd+zzuROAzcuNhTNspMqUMJcKRU9lszXq6nQwpFMPi8n2oeznvgZK6ZaKhqju8OcmyODqkN6BoLcBRTkGhwMVgwuElKLWFFqRShru3pUVcU7vebylTs8594x0cuG8tTEH1GurYYTvHws5ek8h8DD4DR4HLjD06Nb0CYGAwHA0JYFEElCiNDRTdBsnsL00hQ2bQGFRWNcyw3LJg4UkogyMybCibquJsJ4Lnu6WqJ6Tv7rd57cpuNVNTBZKzRwyaiKSpSalKhbsY1Rot7czy931UXGJYLUHhcXUqke9SMoy9jDkpUenlt8pOn2e7TfFKUnpuXPdNkvW/fKUjdlruDCmGFrTMKYYHA7EGxghszsMQTskMiW2HZYOEikvEiZtmjn3gRRRBVJaGI5kTFFRCAO6gnhsWmUgQfEoNhlscN6fEw2YSjBWzUtqWza4YPC5J5pKqFE6fV8NtdlpXMWKDoDhDnOZDbgTcNgajMbEB8hiKcKLwMgdq8ULQgBb0fMj1D3ncsYndoG4IBgLhjT2ZZ6A7JDMZfXY/NfZW4Y+6+kMDvVMsUeM9OR9Mz9ww9xhfmomwfmMfcc2KkHIKBGZ70KFDgL7CicRHuEMhDhyYn5kDAplA/NewknE49qzIELAkd061OfCRxLSq3RzSxiySDg5BfMNGCXpq1hhCso+wIKoSp0EaHVx7blR7JFEOipkYkcYGbhIiUSGJGYmUDc7HO48mD6GnwLX7f1/2H/iX+3a/0+UCErqTRNcfnD0wlKJY3InvgNEaRemJhDDD4EfMkfVfYpI/Eco0kHY+ZqKzGYRCmquAqC+0zEWIsFACGTr7BoLoLMWZsQqCslgOsIKRiGqqTlh50+Wp8eWTyo7PRWsZedu9Tl7OXzcca+kjQmRzVxhHoYzRqUKvqPChadPhWarosMzNULhlsqqQqGYsRSIEkGK4VzBamI4ZmRqECiY40ontepObiZSltvWpNBupWUlaNjK87Md1khtJO+lxk6dSxQq42zZYWFW20tx6rEff6FGGajGBZnDBhxJ9ixRBLhGIYLKIw9LcERcGN0pEJo0qGXLlSrEWDCKGSAQIFh2wIQKJ+qdIz6hUIIqW3Db9igqlRioVICc7Ih1i9RSELaEaFsGgaNB1fXOCohmbnAVLWp+TspOk1sp2S2k5oGzAQIxMwwsyD1mJJTDJyQ/ApZNDEmY+RUaSLpguneBSQnAZDFhx3CnDgolKfVT6Q2mMlo6oMDALIrYpSlI6U/ZmputMqbKTMrDCaJ1KlNI0yzgpLSg3oxTvvBO0Uk8zhRlG6QpMrkYMlOGy/L/VMYzp9z9C1Kk8VEUO6WU7Hg8sJ2PL93h11cWtsqFVRb1OMnmmjlbi+GqmEZ4s8GJRZbK3AkYxTQmP04GeBh8ji04mlFtYSknc7QnYpiSlImzOcpphTLTUajMUp2/icTRWYP4lQ+8o8sh0pN3HD2UZihRSSooSo3pKIFwyWSconULNdi7GzIaI26Ec5ybPIxeGOEtXC3Cm8an38zCM5i6psw5HdRSKUlFUVESkomjqFNxbO2HcvXCbirJeHbhqUotq2k8KZVmpJS01QxKW7Mk7eVEUqZpTTI27qLd2D2645TT08p06Tt3tOHK8q0m0y0eeNyOUYv5uRztHRxMp3haVKheVmTpTumGXDJ5lOHE54nGFMtKOyktymEsrWpsptVDSmmmDa8qVNk2TBLMXldFcQZUo0weinZnXywdeDfThGEGx6EUYFBRzdnJxomqII0SmpI1PBhpmaP4dSpT49lsFFRSV93dg6M+09maYLTtpMJRsUzBKKUh2Z0pmJMpJS77x53jCt6cFGXCvnpXct6fj5OXJy4dlpmUpTKWtJLGVwlu1IpjuyNr8tQzy/fprPA2G61XkWrgszdwsqKxppZRRCABecuz5cw1Pg7FYRzxa+lWp9Lt5lJJ3bGYGInnL8fzKGAZEaGjGBkXgMVVAkehJIMsnS3Y/yzOKKzVbl0lqVFvaYW5s/z/4n7ixEVCg47KkF7jBUP0FYGF+Yt7oi6YksxxndEVPPVFgw+eKUT74/oZrIxKUDA3GDI5f2+n0cMTLx2jbMMO7By08J8qGi+Uwi1rFp1SlMVAfUHBjF51zgxwYogWnDg4ZwodWmF0s13ognNKH1JYMuH04SMYmCoULDl3MJES5EHHmSSNNnxHM/vhwbKmNRRNnb71r00/dp7av2FpLkfKzcHFNqLuDe42quRZc7xI+kZshvw+GfCI/6H3bEp+7kgaGBcdxVPBofomUaY0x2HhwrgQ88D5mmxDPkfidjYb6ExPiTD9CHLMDJZkSwudTMogxNxhasK4Z6bOx4xOj0p587cPLw5nL0nTRjbydT1AWBGNFcoOSDJ50W8AyL6x9zyfv/VfP9vn1D9U1TbbCIt+xckmHmlx/zaf84zyvhG0Zb0X/JP5DR+62WKrZHYz0w377T4cSI4OaZdQpUV0Lo3UlQub/NTTMuWm0DpFEkGVUASwRlhP4xOFGyGVSoGN0eumocNtdSplzMqbaism8pmUT8FQvGm027bQw5Ufu1y0mZpjk+hb88JMDoZIiBFiLOnIPtOKSwVwkk4mFJtSLUlnKiyajKycad9MKjSWujNDplfL9WZMFJHHKbTkyXMe37t9ppoVKRG1J3jpudDcptvaZfbaGvEUeuW1GKdYd7MphZcMpOlmb/e2UZWyuMTo3aKadyX2RKa0p2lsYFFrkuO2iqMoWyynLQpSmtZLbfhp0mTvt9X2KUp0wadx3lpRiKIUpRkqSylfvenupMtOhSWxpc4FJkaSk0Z7pbScm+704Ww0jlxIlnJuIUopXUZiUmSlvls01pxUn774T4ZRZy4OK6UyaThpY4vWBDCwE5BYnFi6FCjbcMggUYjDhMOdbOCoSimUlJUlSxjjDJws6mRY6ywgqCkUWsCRaJ7sxkxQEhZpEkaWSA0/faRdkjAUhWLTR+Mjl5TS2JbKmE4kcS9HUcqXl1Sbbfh9/D2b1antzOXMMxSLw33W0waaMO7TuicG9lFZVtC2pef30k4jVLNX8+U7JOvh+ff2U7Mzs7qTTQqEppypOYWzSUmczc0+W/TCls1OPXOBuWWelOyTMCQLO8DVulmCd1jYgGYimcKOUNK6mC2aKBoYHTQ72pqwyQYNEKBOSSGjHRx6pi0khwhNwijLUpLoyfCsQU4UWqHCc8zIeqT7yTDKJ3T4ctq5asnTOZ4UzKxRS4SZMLKUF4DD+Y84MuSB6M+nPBYwa4LKNoMIYz4q7t2T9NHCYJbs4yw+xalNJUZtYES4GFtBycGAs0R/QNH4kHuQ9TPdfhR07Q9tSaYU721ifZRzu2lSotUeo7SH6fbD3bK3C1rkKUd6YThpoYMLiioeJgf0H8QhA/Mr+tMAQgj8CD+Yymuo6uP8iB676dBmKDAMg4IKxA/YTDmJ7npb5apH/Oez3Z2nT4pnSZjlpSdkpblwmy2XD5TunCaTLlKdJlwwmkoaMEFlrBScJKAYwopSwGmCk0sU9JRwwmXLQ26TjTGhplSW4k6YXLYjpRhSnKcsTKdC3LpblQ5UlOaaeHZiacuxOGFik7TsmxpiKUw/h0f5ebHednHg5dq1KVGJqUpjRUW8eChCg9JrUWUOvZacNckiSFww0pMMrHsRCw5YLH1NTu6MnIIyLk2Ik/uu2o5FQftnJzK287TAiz29fKn7eZVtz6n4I3u5wRTY2mBitcHDhs44YwDllhOJ+qsH7j8zMmED+C/QxoUUEfqMZxGwQfviJ6dddqk3P4CiTCOYolzRVF+wbDG+40LR9SQPc/M/sD+0/X+JlkIxX9SNEBH7yC3FT9pSysfCFswyuXKaj9tqHcrJw7YxxZhOFEpmfsknmmnNpY5K5NzNH3NX4nrpKALYc6fcBtDOZwgkWYa0WMVtT6yJZ+Bw5MJYdArfenWDStbRJh1JJZthiPyYftwyOD1wSM0Mfe+P2cLYagnaEwoxMvCYazYxi5H5qTdNvRdxyOLHKmSlqysf8OE4ZWa2KDgm5QmXCNImiyIuXfANlrCk0+r80mmmlUFpFy30VPTulUkdNl9195hVsphMpMIyoxNirP0nU8FZlFyReyWmLVvbKfsjTKvlZlNJSTXYYwRRRp2CKkQ4QKyRwhFyCxWZs9U2zNMJiMfSbk1TD1JLc8KhZeyw0OAsnD5G5gGARi50RpdEPk/A4DYiiTvZ+XR033AMMjvJ+DoB+jqJ8grLczSYldc5w+YV8eXlHMC2bk6LdOxwadzbYIrgYUvuN6JOXCQpaoKLscaSomWzCcMyZdOEpbDexbCnGzpkW4NNVJxpcrhS3LWGV5YK6YwoCDY0IndhCEhFIhBgPomgDY30ZHJMQXgdVNBSu+1U6FdrFaG6FK7KU7nRLZONzlLYOm2mZkz02yZSOcUDpIkirVCtFeOErAclDrhoaCimNV6y2vZqhMOhSOgiwH/AKg6cOHNKoqDhpb3U5ZfRJMfbDqpzxShru6hqpCeYZmGcm6spClz5t5TTTHKqHlwXJFJSRytM+ZteUtLntom2xp9ZiuLNLUnSbPrjJlpbpu5zQpSfXu7opgPZr8+/Lsh69QVS0VEqSZU8MFmH5U0dpabTZSThanwrKNyctRwviipfYucHGihyawwTcKFKH2SUmiojS5Ul+7rDpy6Wy7KikytXD013TRtStE0ajhJrSTSm2FGL5ZKVLmZllymWCW4ZWubjRGEaG56YGEWuR1Q5oKkcHCoxUoSK+UMWUOIUbBRZhs4IeaZKIm2W4fBF7PApODvUk4IgmbCInhTc0ZG6HFMJ2lH+tHZwtU2+tPnL9fKt9j7OoWw7ErQpst1WiJAoCiNljZ/EwUYMEUbbcusllB+R+po4m5od9v0P4ho0n6nzP4RLf7GRxR7u595f64yj4ZRM4WeVJFMHgXPtrE4ZXL2qv3csR/LThMTODjCgJxti2X8XkbHIdQ6kdlggcgdAocFSLutpllbyR5+Ds7U/w5dPtIt6KW8e7Lw4Px4OXL1yKYcuVPZTg64ibiovbMkYKgaYHOSm3fi2cunHdptpTaIMPZBo3hj8WZj0+NzYouxwj7dT25JJjqOVVVZez7fQw+Vj7raPu3pvEn3wpjjhwO4wQJFrr0MQgNA19CioVLWKBb+Jh4JDwD7i/zzYwwo/wYUf4fn+z/Khd2f1WfRhsqQ7UNJy8nLuUf32eV5HjXmCUyMDZnW4Nqrca2h3ijwSNwb9Sj/vHwJsZ4qZJ4/vlPScBZSIfoULAQerjkB1c1CEmCQsxOaiPB/q/koL5r6ATQv2DcsIh+ZFWARuwoezA5CG4UFI4Ka/5p1YpMWwuYez/pAw7De2zTLAbyzAphDQJ8E6FKVEiZ2YbNInNTLmb0Oc8FrHNraMn+HJlcxJIpWYzhhH52tNdkT+72dNHexyYLhEDHg+nSRhOGhgScO3IMEsMXslBxud44ZkOWp/pW2/2PWTzJRTSTqHlnI8/HY5Jw99M0LHLPZM7wIPGUcRIeCuIRKWpRwdzDdPzYnLg3aacONP1balAYxbkiWGuINOCk0YhvkME6EfrGDJ46NPbPiGeomHhkY1x+ePPYlOaXNDKWqxgav/dHdl9J6McKVqpJqUr96SWpT3eJHbKdlGnPd7ammlPS3qEe2U5OYWlqZW6coozrLrDliMGNtMxSUYkUpyuSbNC2cbbTlJTQt4HNk1ingfBCoQyWjFXZTJ/xOEjKUwT5in07rZ6nKU/fOA8vLEMdlNsQo8IYpPidTYyEFYHRB3IJNTcwrGwtmbSZn8u7TbJS221Ks5+mw2ybfCmDT9B9JejR7HaEFcKeXTrZijm20hRCAOPW5IxCzeDRn+8Lrp3mSzgi8FJgyYGqY09PZNODcJacwKccKat8qChxs8bOjRuFN33063MlpeLEoiB6CMSOUUpKd1P4520a9eODVTop4rhdTwmMtWNPDbDDyO5SSZMqOnjlS75F2XpKayMsOKGafRxuq024MNzBid2HMMKLcYLtu5EyWmMqr5hUODdNvxhc9hxg7qfhytRXNPTicTUjKnMtF6WwrIsLLcTDErzEOEkjl6URagxb8om5HERxK93ZDhqndScpRu89mRpCxx3tlKBg3CBizJMQ/0gUCb7TTq6i3OzmTWFtGuzy8tjZ4kacO+LOQ4MXR2lpYm0QMYPbvI5qChPWjBI03w2Uja3DeSmVSSP8PlGInF8w3PCslMV3d3dloqRQ2zgyMt+Hy7Onbz8TtJOz1ntl3p0xpRZpeEfLH/NzENTXOEYx4Nv3f7NTt78m4rNJFOnhj8ayy/WQTGpeqoIlytVtpUsQDFohOSoKQUEEp1OA4XMmCAZCL9FumJxx0IoUy/QpIpEIfzX8TQmYT7AJFBklgYnhfM+DYqgqamGtkKSqFCpQpKaEVZMmQ6d8QkLc9T2SfzeX2+i2uXR9Cdp/oybYt6ui0XD/D+R/MLhnc1BV2TlsfQY7jqIigUolQytsYo66nQvtEzTC47orxZacT1iRa826ZaoFYFietDIQvbQPg8C8MakpGCiC89yCQmMyLaU1IpSTbCvrkyYn1YYK/hX41bkqKuS1RTz5ZcRPseGG1UfY+xyMrSTkSID9hbwG5OSCIjBE6nbssSgZlnIjaZdPLsT6Pkw/Tp7zA/t+u8fhUW/gqN2ikb0/sbEKs/ajAYG0s6kMFmCBAwfyCREHRAifohhgZiJ4NCiuRqkDH815+uOUPurvqfQL7tAkexuN9QMHCa+oOD1/E4+pY+x9nNDNzUpKBA0HiWRQwA3JkwxMjBwwMPxt+OHDrzpix8rOmFz5X+RSfrZT/h+4oe8cjTDgMZA+C4ogJBpAp2SmmyDBwcxPyLNKE0w7uVszslrlJhS1hEdzc2LbdFFhBGMCLBH+DksLYEHqqwMJDKJzvRZZwWPUMFmSFjYlEPjkKIECK2BRIZ5ilFxKKTRhh2TDJXl89DbSqFqpbjC3UZBlMatc0tZFqJS2Bbl3WxboWb4rEJdOFyjWy4KVMUbDCLCyjAfHWiRBIQXRUmXGtaXMh1KAW4kidPTZGWU6OuFsEdaaNtJjZ+WRGJQwylUzXsUnsZuU9Bu+FnJUkaYMtDneXW5+TbSKWsjkobjvI6ZZWheat0rJhaxymGdYwfkNInc2cFMI0w430hkXy6KpZyjRRlMu2znhNvKynDtsqUjcVIaIcujhGTBBEmAL+WwocNSWTnBiGMxKZB+J3lFNzh325czWzhpNUSqZXTJrJPydm5uN7k6YkZH5KTF8JaYUiib0S205O49IUxUMmU4d3Np0uGCSnlDTblpwkopUJiIzG3bSzLRYmXbVvzpeXx7pYy60NXPQy8XwZ4ODhLDG9Fz3PAmSRQXZChk7pS1jnGFJdropKYFtU060uZUnNnM7nUJy2nQpacumeGOjCfRtOJkCokZERqJOTPgpEQDCUJiLsorzFjkpfMYQdYBrglajD0EmBQcIKBm7QRtcNa0YDRGHULG0fSJ2KODxdk0L1KOficLg6lGE6XRowxFsUsDfHfsUlZX6JLKOMOzSW7nHdgk7MYYLbJyzntorpz5FRC4crTIKGbpOA7AEIroh8GJGX3FObo6LES3COgP8SvC0WNgXUHhNVnawoY8HHXBwybFxeDJDGEkAeRIlGgON3mjc0GHF2WG6EYreDospARjLDFkgz9CZPuIoSgWDmLMf49c8TantTiqTPRHTa3X0m3ClKQUkpNLXFShaju3ywaTnOGGeZHV6hN+Xg17PJpRK2tyt9cp7jUtGmjsyYtLqRiU5+J4bj0nL17ZNOxO0UR2cNK7qcsvCpNmbnw7JtmWi5XKli3KmzhKVcdJv+GZpOelOn915hOTwSvMT4dNZhT9GEYpIYkaWW84WxmHDl2eWeX4oyfe3ZqpHDA4Yh6OzLTdrVClJg/0flaRtp5UpXBU/0aF55/zM4/utLdmHB9Pw09nCld2E/VQph9nuzr1plE/JRcprVKpcSWlk+ye/d3j8A9cmPPXjfQ32S1KO2G5naWpKTUl2VMx4f6UsPq2hwUJ6cMOVvmP85O08PlTjleAehlR/A9wG/Q66d+UHKET8HHo3OH2gIVwSeZmRriAyxRBByFdDE31b3Xk+RXsLfXU5k55ZqH1Rm1LdoYYaftbTOlSPsJUvAW0e82MFHyOz8T0MocbH0EbGSyPYlxjeosLOXkjXtlDJkyOQdA8GCiCmjxyUIkSfSImXoROG92vV4pkowGgKJA6FzEkf0qqRbL6piIcPq0/m/DRwpnShNqVGEZlvhLfuyfan341tTM9KZVE5UMKmcSqfClipdH4e7MO2mon3f1dk3PBVqzKJb7/yzOTluTbCMvSw+37/owvqRCDAdvlDoEnhCH1FB8PlPAWySP2Hdhw4wyyvDEX9+v9Gjg/znBli2VFmGXsZs+9NBybFh7zIYNwwl7z4fqn4H9qSj/f/0z5HHYPp+gy6w/Sa+xSJL4hH6IsyhUcpSzcj2MIirJdHPB+AopdDO5+isppsc/uhYkWj73MRSrnqIkGWRksC5JOZr97Pc0WxmLY2N1gUI/iQxTG6e8vUm2lWvFqYuRXE1KlMMKt+pa3HkqaryubgKrgUBT9kRvwGnBHsdQ4EoPsV1LoftSxaiYlybQ4WmGWImR/kqOksks7TKf2XMMxpbS1gtQkpSWtJKghFhYGhMdhoNib/To2c4kxDBSQJoVuGDFhotEIQ1h0Ogw/y2pwdBNpc+jkMp3MlPSmRkynKhaUQvkUErsS2EwqftnGXZt5ZRDCpHBSKylmFGH1ox4bUJns0nqdcS9NIxNarkoyXoWZcf5OG0KmXYo5Yb5iPKU2TpvbK07GBhLYZwKLu5XBFuSTZQYkcI5imjhq6Wp+j9/10aKNQlsHMM4VlLwmJvsmNRpcntJtpEzmfRhLKTmikzY4TxTT9537uSnJuqr9JhknEd2pGx2beCjiGs+0qJxNP22kZN4w6bFZVHWSxo+lm958C3Lnlpy0OHs915wqRTD4Kk2ibeFByoo7IeD7OUxicDzvlhKmgoKSC3JEEmFhArLokljMNDUWVLF7VOoy1h9qdKYTDssowns79D02eCswrg6WuMyUYjSTUSYbPfS1OGpwdlOJh2GhWGY4mksrTMS1W106Tj30UmFuE3amo3ZZsrhgb5M8MNSRccIdhmdnYfDs6aQvh0s9FWDkyQoh2OWiEOjoLL2djZxADItp2jP7+nG8nHVockMv3MGKTUuUdawBUGHdoC3ElSBX7UpYwpDkEHuTK56TRMDuqdYRbbo7nLjpw+yZNzM3yikyUj1NYw0Uo1OzTmSjHDEKhE+ZRkuXpsswIbmzbhXDn3Hre3TqTwOeVNKSj1/2eHgnLTHHCM32TTbClop2ZqFM48lXdvePb3nEbcOJSHbcYmGSM+sKXghaag9CNWRUa2MBYRCwPiUn177Oewha5ODnRp5LWMXsBI5MkFJLr7EtwBl6FhAqdW3xJIGsFRYmLPR06hNzhvh9lOGHKilJFUjh3hllVGFcxERme2SKCFQswsFjW4h5pTuBAz3M6fTDhh4U7cj1/rPMZmXkW7uUdqDcwezgfKkqotKId+eDlyYTLEzeGNB/XyWeK6Q0DfcOe9mLw6cLPDpTj9cZlsUM0rPw6LTRHm2p0tw57Ti5Mlz6ThMIpMKlihLfRTuzmbctj9XiplC2Xk6cFFRmay5d4XMGGTDMwsyynuaWYZcqCxTjl02037vrfdpt0ozGWrxpxMO514ZdPCKOyluNnhKUzJSWSmssJy8s8Ts72i6kFOzgpy7SnCHDWG9/7NLKdf9jHOS1PNSVSWtevL83xuejbuLVIoYtiU9j8id092h0AoFM0xYmiWwQY7/wXiyzGEWLBTETkcsX0wta2GofEpTy4gpFfN8MJwg5YuFMHKoTQxYWKqBSJp8ofrMniI9VE/idl/EWy7zUnF/EDgCAoisajUIKk+hss3WfYJGBAXuMQgcIGB3dDKsWY7mWjqn0MOEkmJe1NWoSn4f7bZUzGn8OPrpaNtLO7glIqUqUKRmXgqMLpqq70kx+79Xs5Ogcp0qSFFDD1bkZSp23TtMSrYM3aPh9HS2Pw6W7nlT+U8FDupbQ28HZP7Li8vJMKXiQUgnBiKXzGQE4oiSLDGgRY5DLx6rxcvd6Oj2pj0lswuU8TCWpRtaLUpS5CITwKgINIWlI/Zi9Z4iD7iFqTlDKRRP4aLGaE1q4FMyPT+FsMqZaWij5dflLAyEw9EO5ZKSdUUoE3uGC9RygXOAhQBjAYqcDEUtBixsdT3PUuXsI8SI8NkO3c8HBiSB3hUMFmCHUg8nyPcyiAeLHi6vIlsEwovoTpAgmR6ngtvFSPSH4ECH4hD9QssLPvLKNzkw0X+r/eQ/UNyaD6E7B+g2bFVJlbl0/wP9/9qFyf9j/tacib09fo8Ov39fWHzmU/OMIMEaFGh3r+MH+HqTcUIO7OwXJVMVeaeVfVvNlStB1eoY45Szvi2Ri7UzqZPFhnhRzEkUcddMfl6hvrufv43VQ4KJgczcgwDJzYGNndHAwaH5WmfqQ3goZDmRQxMDpiTGnkmVE78sU+rPFPZ4NTXVV4/59mGZFmfduZLe7D0p6blzDw+TjvZHce1w4lI+LPD3TLny8OnezlUoqR8Hlp6cJU5C5Y0RMpEuYwW+hUFEBWLXMIXB23KGJe4WLpUWqmDfZtVqxyzPK34NmuOAkvjMzKow2P++w1/JrCH3VtWC3528sw0UaOVsxO0Wjws7DaGaUkt3S07U0qHoJEdEyViAH5jHBBBikBjAcVJJRBBEOxLhqaMNGaICYUTCi0i1TUn4UwRTe4bm2q/kbY8f7sPCm9xJShbhRhwwmZkzidMOmWsMNKMqKYVMQ/Jrj/CMqRMJdl6JsHtQtFSaUiWngomztphHYst/NKcYjJ1mNKY4KZhQwkr86P5KbNrKN24ckpKJdKW5S1T64R5TCcJp5TRywsuJnFVha8J2Ys19WkcMvZbuw41L+SsIUTQ7NKUNImh9FYlJFLUrPfTBsSYYwBgUgwoIFD3QRkvwU4ozSKFHEUUkeXyfydkzSndVp/DxJ3pknZ2Mqd3iUteXM4Y0zqlpSz+Jhf7ZD4pt5lqVE5afu+0zE6k4lH0E8/Lpm2SW/N9zlEw4s+HQynOJNNJ8HuopJxgpPsVarHwuJl7ycenCNO6lJSkpZuLWhc6bMFNxYHDBJshKdEiIa0amTYjITs3c0MNaRRvbGEpPZFLLLfCjFT7lsQGQiRyYVfQ8FB50UX8mK6TC8Hdej4N7Cvq4k0mkcGylKdjkW9lrwbUNvhaZcp1KpQqQd6ufwtOiYUbSbFpta5KUdzUjb0nDiU2pUjiRUWqMMjYyuRNQxkwqUKWtkw/jl3Vl7vLaalRRmMsMlIzwjCMXUWpKNHy7LM92ultIXKcsp7vflpwUUcrVRS5lw1l0mh7PaSJpCkaVJLKe0p6ZRbCz04LUptaSYMSd2W1KbSi2mC0ptFqTdUaUnrBBZRwxFRg0l05RKFYwMpGDYRfjimZla0qmmbLGZUXrc2TCOSlooXITpTDK0thzJ6RxlOw/nHadnLs7JUiemSW9GGDt2dqkLYcKTFOKYRplItiXIZt2w3Hu4wnNIZUGHeDGYlQQL6UdGkVkDpvg2KRJQ46dwkpLSYY74wUUSkpF8dHSnstLaMJeBUUqUjyuIUvsw5nJRgp92tJ2UpPVMTax4KkcInLvbTTfLLjUtUJVwbbbWWwwo03UfVXO30KY9Lt7cOe1O622SowqpTmzLKdyU/CUoiCfXo8KcKE/umGwentKRPoIc6UwYXKW9mFvwt9k4yx1t4vWXbqs5lVcbwxhao5PT3PSkUz94qUqZSVCha33XCyink7z5dzC3l9pXZwtuw4d/h16UaU4b969bT3ZnDlRxOZdLBUKfZQ7KfV3k2nMlOlqtNeCx1JRUrEJe47OseXCJ1yctScsphgseCo08NdjlI+xy3KaMsSqwYOMsvkXIX7NO6o8LczTMWUq1ClST1OIwUUj0p6cO+8p82WlDldc5YXup0rbMPZ1QGVBLQuDreJEWL4mCxLxUMUWIjkpERT7VXt6dnKdFKQ+XY5o2MqUrvO7k5RaiiKE8M/DZacNuF8azFvKkztTba0fE92ehdN0rD18+3s1HZtLoOynTJ5MIetFx6e/stKurW5so4WWzp4MrwyMF8TFJyWWYHIZ6bmAopaFgnJSg20xSJRVnxKSyeFuE9591vo+yluncs7zs8KdnTbgqm3JXtHgcwAxMFYwLhEoKNmTNyTU2CAtlhuydHO2pAqYhM1zCWRSiroZDGGBIDgWpjdk5lUf8/9z/fB8vBZU9jeH2dvSvK3qWx7Op2U5OCo4lhajalUphZPTB8sppymG0yaMPc5/3U5KpUocPS1nM6dMKUwTazLon/R/s5Sck2otfZ/1f7lrBkUDgiVF/NnUlzNH8wqak11FUVVE1SNdxjzh7v1mmkP0YXI+5VHKl2x+p95o+0+78dmidJ+pwWwThhRjtKkYfyOz+SbbZZMpw4aebMNro2ezyeXqej9Fvf08nchIdDqOCHYwQ6nc8bDCwN6WQ03scsMsmWVyhlTselDDSmWSmiqkt5thUmWeEbUhlHPfbKFMmVRwkZEYBCEECAx4ErKe50fYfvB5Gw6rQ4MBg2GMDRMxQ9KHoWYgGQweQohQBkbpesqeFVcsJkweqiD0K9xkkeBjugZI59rUv+b4ZJklRS36mn7qF6an6VZ+uk8JSc0fZ4fjhyben5MMHMVU8G1xKOw8LbSkVdqLFChRRSj9cyU0JrzDO9DwSFMpgQOhk2PRGQt/GycDGTXBT9DL9z9pqcvuyd8xlv3urLwsp9SmSzx4WhTDTCocJueS02QcG+WyFmDtdARwiaj9hBVvtyUcnOzMuWcZBE9SpcuwzoXUgZCaBEZyBqpZVKKU9y3JveZT3yoaC0UoiEScEpMLmFDASxkZERERAkaMFHYoOxsvvkcmC4RhH5lkELFwtjDUYKqqlSnHwWn8TKdmPdyOzrI4bNYTLWGLwfVbA5RcuWpnC1LT3E2Oo0uHcIRpM7HiGcnEI9D6yHUSpK6if0NvmfEiz0pqR2lvR8zLhmlaUptmHTOYpTKy1TNlxbiJj1+fvhtv6csr/JltmLbd8JP7NLYaWphizFLZpO5bzPz+sw0py4c0ZZf5NGLX/n9sHhiz/Cn/FSm+XTg/Js/w5f9XSUn1ZYZYYcPvNKkWJU1FR5+Z5uUoOjy9LKZcuw0tCrglr1zXr5pGzcLGYnMDZojIu6/QP+UlL7P1GiR39wXrPPBQKnqnIMQF9xyAor62Wx9akDYiYmRgRwUSjZc36rO9Pk091zgkYMqOngUQX6LBxdpicvL/R3XNKdhS1HmmXD5m5ytlbBTyMRWIWmTIhITWTDjwLuYDGHsd8yJ+7zCka8FaBf832/t9gft9R9hbRsFMGBQUM+5KHsDEC+p/4l2e77zI2GFCGgIr/CxCiibUw/oOxon/FRJrCiTJpbC191LXalH7GCUm4N/63Ejw4cJMAMQiICAMfxMvCUokZD4FDJIVax12ZkYSiUpKVSMFspcaE6Tw8m9bTlSzbNsKSnCpKfotDL+q32btTdp3SpTllicOFsstElDZ34xJxo4YT3ocKSYUZpLTSezx2m2nEbk6ZJzKxhbaxN0S8lTOmk92GJezLTUmJpabm1SlSjIw0rX9U7hxk3Q4cKMlt5tioRwrcZ1a9mGUcmVxYxCy5dOTthMGA+ZwB+94chkhEWrY6s2xDA7RwuMg3111lNO3p3d1GQwQKJpaYSMAx2Nk6PA52bTAc1ZyUptf9WWlA6xLOKDTMmJDnP9n9WvbvbxCqPD3Yk74fd2e+mW3hbhdOlPMJfZXtM7am3cu2HSkp9pRPBTlLT2U0yKUnUacxzGMsyRKG0pSWwpbDVLmCTZjJ2amGG1tKUUj3pgMJIPrcTDjB00ILYdMYoJIDTbIqFY2IHQIHBBM5K0yESDBwU4X/E7hhk0hxZ6fM7uxz3YuGJDkdFJNFJh3tunXAabSpKTJeXCKWxMspMBPRBKLHZCiyiSZoYYhihl6JJw8p4cOCimXXjhmZlZNG2eWuUUN8GChEkkWDEiMRIpTicKPF+jQ+VgcOFHGnP9zSYUilO/SemXdUkqSLfdhc2OzhU2aZMSDEZNNMJhLmjLDVJJSnS2VyMzJvMbLwtSqGT3S3in3UZNMrN4Zw4wpmTMwxcOlpahSWha0UYUdMridxl6em3Bs04hL7SMrfko0k+Xu0/vTSeNvKkqKTLKlrXVO0LXNNPSbGVLSTTCmIaysmSl5SfDbJlQaatam2G45LhZuBqFPMM5CVESxhGE2ecEy2zKf3sspiTfLa3BgpP+6kWNOlGCaXGGeJTTDTT5SmMraYhMUx2GDMyuRwwaU6zamJmQpMObfQ9mpHDwnCc8/js7KIo8fC9PC3eE7LcFHSf9rDM0tJ3kaGGLYhbamNNRhrBpIE8Cr5gJI+Fiw/i05B+t0cD2c9wajTPd2a70mVCjUOhR9FD2VKikpUjA1JNjl8C2WnTeUlq6kUtzpiltxbbak7NxaTEJc/4OYcnKJt7zp9tOMpQtMroSMYJ0ewQejBiz0cO3oxJMv3qG1N0LkokaeDLhHuUmZSioO1SOsSMGEow8zDuvpWCx2Up1a4YeJToU5suLiU0NJZclZUp0WOVGWp3TDZzhho6s076mGVdOjfDodFGGooOXEoUUs4YXQpRRwpsopq2OZQtHFI5G0y4MuFQlLcxZUjROWSZ0sybYUsjBiMotjEsQPVi5MvZGrhBAr+9A8XJwExNbESNhxxaGrFTorDf14FSoMgbccsObbLWldHCvNyLZRlUkp7PUYMIopHT4YZMPhw/x2fn2OycuktZakopUpQUWVRYaD9nAfsA/3ME/uH+0ephDse9f0c4Og1PkbGhqWUrEBfDQiPiBOgtDY9iMpiJYfgowyVCxSqKpVUn5NufsfiaSlPTpyyjfQpRVDZKUMyUlPI27nZxHEm44SnLbCH8MmXetsJqPofJlpz/4I8rfZnFPFrTz/Ly+c+nBtRlcfrhMMROI0qIrwbHAcE1SrNbUsI6yFdSGNR4P1KeHk6PHdcWrddvDKzDsnrLLxSHmeaouc8GGk5SafyaMOUpswj4ZM+JzOUnWeIYUwc+SUmkk9mFDlqNuLtJso6W/h7nDlbbTmOX+RjLEjuiqNXPaWZYa8rYMUtlPCpNCbcMHA8ho0dA6aqnSmUhtgYaiIhuG5hSmHd9mstqPNNx1lKMNGWFMDKlu0LShS2hMQpbDtNMSabeEcnkwOTYraqUswuZMrTk6NqUyZUstS66k0UmnCiiWj5rU+kvnBf1d2H1O7p85JwpOaDweGZFs53O6syTa5LuNt7RmDLn4jhT2W4ft/wwOXSZssgNrZChgQX0MDQpZKSOCyfIYUKKKKH3H0zRsEm5iShRBFDClyLKkthYWvwpLjhKUoyciERGgYwjFWQY5BwS8miBRY4e4h4kgwLMW6aTLu0p5wNjR03LDHdTSWylmepo7yoy5bTtw2wYSiyOjHUcmBujqRbQtxXcpH7uyYD6sk9mPLDpzjbDpnp4kmhtl3swYcPdgmVIy1bDlW8KUS5lbChSnsvAn0/GjdjFTMlEYIERYSRdru6CKh9B9fyKlApJ0wf6tNK9mX/aqbaYYi1SumZwa4bfR+T/BybfRxaUZUclQt24f+Bw3r2tZUq/uUbKuIrX1IWkRHyNJ3V6Kk+VU8tnl83tutVrIzStSGsSJFNFpRjDt7+pFRXmVOMJrBj392J42hghQKl7j4xCNX7nt7HuYIDEan3L0AHSmJsy4oqe1Mns9E/opGE94t7uz1pWcPDzZvlS+hhIQLhTnu1EqekHRiCy/MXg5IxiFhRh4OMjUwtJXBeg70wBzBNpIEc5HZwNNOZwnL+bLydVK9yppTph4y9Pft3dvJ7opFIyl5HvQ2wpv3ZymPSyzsbGJKLvcOpIbHkFzvZ/Yfh+J+P9x0RZ+TQGyjP/Czw/ofQIH8CJ+D9J94RhPKEIcwhAj+Q6WkA2IIf4z8xpPmjgoyEU6WlJdhF/xBjGGNBFwREo3G0/tCykQ0RQyQSiKBTBhEhGEGH+OJ8+hGUpSYonCYUOGG8zTpRetIUqRe1KYUUuh/0KGmlJhJpGpp/D2SnTpsIk6nRDZEDWCEhhSkpKy48I0WwiaVEtVUmUYuSuWWhSYzghsPx9n4Qd9N1O94gaIYG0oMRkzvVp7KHGGZtkytTYxwyuT9ppk7pllviHzDk5/q00zx0t2IpeUwjH9bbhdmE8FJk2y4zKo6kcTtFGNqP6OeVFVpO+2XcqkSqmki2nZ+nG9UPikeKc+1unbxZinKJYpFsOswqiTUpqHhMpgMKaY5Z3Mz8mjkUpSnDhGGEMMIYthiVE9OD2Yt79uWMsOQxKXm3KlDsuE8OHDKbR44ZKOHzMSf0K8fDseYdHYbdg0tRTLzMygzFYpCBgyMiciwwU4TYgBZjSnGYzxcniNLYWrwk/qxbKdmkKjJOlGGYUq3Mt7GmZIlFEm5ruphsyQYUOk6L65Jh5YlYkmFRRAnNLGD9v7MkwRJEQ6eHTomIyIHA3wh1RcjSo20m5bLDnTLj+yYcsFZYcHE4py000M4wVhswsjSoaSMKKdl3DSsUwmLWyqQpNZ5lsJkNFODBa1Ts+GTsYcOHDtJ27PhR3TsVSa7cMsrmB0yYGUpRSlKWUUsvZak/NZl2bXNxedzsK202UlLhi6GjI4CFkH7SHVzkyGHaYkSzARwYkywMyJSnDGcpIq1L+Jks0pU4m1nC3EVNMtrLW2tLiky8Pq93fR0KIcDa+CtG4EMGHjcZQrm8QaMlmVylsJO9GcKMZdS0s3c3e1yOGQUkY4lwmmGJb7qC1VH0IDhgFiikr2mxli029lRpubnE4TVsqUpRSWThhMolsGWUx20mo0YSy1nNm2wplqFFwC8NFBgDWYSCoZJcNFZTgQYlwYdfBm9cYoWmhYxXEIkapYE0FUOUYkMJy5HFBHTQkgQoHBRT0zPpgmVDMth0nFK0aphhUx0OqnS1zci+RQWnptbs6dTrRNClTNCnTmLYix2yw2llOGHE4tLuKhNFhg66GiwNINIDU0GiwIFEGg4tKTDpbqVamlTLhTidMnELMtqNtJyjgW2q5TwxP+vzMtPFpTUMO0H0W7qylPC5Jeo0W4VWnniMHj2b9TtmboIqZFmKkTRnUgkXjTlyJkavhnRJYkSgyDs7nUMmXLMndStNszBOXQtRRzieVUkVeCFEEZCUR8A3/EybDl5G5E9Ka75pbV1KmFDdJp3stT5nHDns0cKcbZSGLh05lx7+CbYabluNO5T7pwbNpRUqUnApFTRMqlNrK1JWTt878Jy2w558rtlikipSln3KMGRvFaeXh326ccfF9NSaW0KlZXUuotHKnhtTT+z+7w0249luymXlSKoUKlKKKUSlOsR7qOShncWPG2EBIcaDZFZpIwvgY4QYswznEBaDozy45WwuafsWiY6dOnBy2ltzaN1KS2YyuMMvq/Nc7KNKUT8bfdl3T192Hgnil2nu+Cnd7NLHedr40lyU6cKUOFFqMIUw4Sn/XtpOEmnd7np0d3MYDGjBxOL0auhzw9WI0rolSIHDRzptn9aeVM54ZlbaJSYZmlNs0tMKSlKXZ853MG03ao8FHLwynepOH8lO7EpSdEdnsthh4ZMmR2dyzzIp05lUos00YOk00YeKjHgryn5Sm8Hns/Ny7v5S3DDwYlHopeXTMq8jLWGVSpeXuo2qTbSlvw7e/s/QOE27u/Y7GZNGtKpYp1ckbUhl/hZZh1MOAzU7SkSlsRESUlkCgyxhQUoJzFLNy5GcsswrSYT4XeKq3nC7ta6fWD2zNKTaxMnzLk/KOVe7wpyRqcvK2Cnch2T8TEyK9RpimNMGHVO4qQwp8/tnAZk92HMrLDr9IPDMT25ThQtUt0fDCa8LLJSHu9lySeVAQisc7lGhwgUM7CNlE4UaMWOSUyosv+zCYkqFGjF0y7YMb7dTjhs45fx07HDKn2W/7Om5Trkwwf8mynLfDT/d6eymUdNWWf36OCla74lee7Uy/4SJdUFGRUVxJaUYGCtFcREcumqhzIW/2fL0e/7zH+sf5T17Zt7dPPt5IHx9JQtUIWTW7fRSmpUthLD8rwWNLDFqDL8CIw/5FKkErr8if20xnHVkan1yL6n5E9FNJ16VHZp3NsqbTp/oLcR9HTp0dMsqalKmTE7Pl0zKYTRx4d3TT6NM8R2YrEqMFdLhIuxNQWQZhMgZl8FZYklAwFC4wOVHMDH5BIUSRmMjHBGhgWPg1tkpBDdqyv1L6UXV/SuppF2K1xFqq8W2krir+PT08nhjo+9iEN1H6kH+BiK2v8Bf0+dIlEVsA/Mhyon0f97SDKGXBSGw0a4kZSlNpA2KMEB2GB+gjfpbvaNC/o6PsIF9IxGcKaMpZ/37FyVCP7qZH7I3H7ssnKoKUUWLSZVBRTHSWmEwUjCGxhsbDBlMCwsRstYUOBi4IuJMGbMtlCvJRoftKMuQofkFCTAT7/TZ7EiSkNAd0HmhiCKmzCuGYsYaFSbXbeG5MKGVSyhT54cMzMr+OcjRFP5sBXOF5slNnCxsxo23g0tEtLttuLizJSlMt1NsRuMNzLLK2TLDLVajSjCGDEpTKjXH0zDTRatDmSWs/qXJbTS06jDMyroywvDK3U0/mxEl03hllNttzjF40NXgw4kbijg4tBQv+04MwnzKaUi+m3I/pO22ipl8xNywxecKLkYPQhTgoaehR2s58acKFTt4ntY9TntDsnWolFprwK7NTLLsbka7v6uR8L/q7zt3Kd/vHu0jTR6dmX926i1aUtb1wmFMRpvpw+nBcokc8wlqerJ1Kky1LKUo1CYnytplGedjWsLZFBV5MZYW6c4PU9eDs4TsklN9PUNN0w7NziEp3bbTglrMJwjVKUpNqzkyEUijCiBeTEIpWCFAuqOxbXu4XMC2ZS/Mtusjly2YkpTBZ0tK7mH056w3FV04tPA1huK54TqaFrHCnLh2WZlKKUppzizKncbN8JsHgTjwhM5wN4Ik2JwjMgaEpZHtbeEwNMCKUexuUPDgo3fgF7hfUo5UtktSnDUtLcrPGdTYo7qU5ulaFfQDyAhwQiZykjk0WX1CFXaXY3INgxVtwQSEbCEM0R7HOTWXk90xlP1adYzJWj0to1WmEdlNvwnnThttgpafyfLZ1k9uI4ndvTeld6YV2tywVhJOXKaWTKJkacyxck5LPCmIwWmXBKuYhytnBUvjEnutM0Tgam19uWlX1OcGBYHHKFZoo4jPwq0CtaUuSXKeHl3cjjU2tnETr006dk4duyLcjwvNcRmnZOsKlPFM6p0pLy00ob93gzJjR/HJbiRbSMCbtzLYYuW7G+WmVG5pnUqVHoVsqMGycZHClOXk/J6O6kpPMo4OnPL2TppidprKk8vDJ5YH+99joqU6fHRMp8/+G3auJStUmDEsVsbegQDFpCj0fiHmBTYGE5Ou+abilCinrZvty7bcNsOex75dJmSe8nwd3k0ny8I9FLT7Sp6d3ocFeB9DtB4FNB0dFaUmAMGCK7O/84GTHQqhQx5WPKjn3fD4ZOFSJtHMjqSlSDE5eHiMd+T6ONuVFU5OVTnw6yy/LlyciJwUggVCK2pThi5QdMTRAcJhSPcdFMxcbJuWG2LQcHcypZbhGiNnlRQruQHsp1TfEjcky6yKVTGGGh4Wy3MqMTbrp6k+nL05eyt06dm3cyMilRmVPxTR53auG5Gh/k+7ib0jphhwoHBRPZT4lBp+3L8/Bllp5y6To7TiZN9QmUcIAVcksAcndRstToSksWbhcjI7XHJGOI983TOMMYEB+WMw1UC8WbQLVSG1TJlm4TCVKThOdRah93A9OuqrqJ0oVCp5U7OhOwxRh4lrZZuUyuTxKbTdVxV1g/BZSz0dmW3S3GzCcdo0oyPHwxOE00w6i6VRzKeU5fHO2k7OmInRRNJdqaHXceudOThScFkwaknJptpIXVKVgpGymCkKiSgiETRKF1DEl2fs8JoOG+sRJ0SzUDRo1r85+IwOHhwMSKxVD1UP4qGEppoldlDKdo1CWVO+WIYmYed6blKRIdEr1BLQFgoNooLCtpPzX8y6yyZKMKWwpZTwtw220zb4fLnPuqff4cwYaVIojmYeWTspMV7V6M6blCRVSD86KgHjFNiyjbyIR9DoW9ZcJja2KcSMMvMTA1WrUWt1uSLGyCnJFAogWGIUqORwWWqwswMOKO6mjCj5/JbhstK5Wqk55F5hjQZX8Cwp6Ec70OCxoq4Q0fvKswFSJT/cczFE/ohsttbhbiHM0Y/xys+wMEMK7llBRzWShxubmE5UkJAVi/SQiU9KB6YUEQzwtPpg4X0/SccvNzq13z8/N6Hw/BtGdi0bubgyypiW96i7bjd2r63G5b0qaUC9scPmVX1V2uXICSzSkg+SC/2KmBA+YGKVCJ9D8TUY0f7Gwomw5oGQmFY4HxjIoRHBiKzgfcxOhgTJ6BYLYUSwJtqQEKKBWNDa5fGYmUyxzWQ1WexIsUAcxFrgX1LGxZUKmwovtWjDFatXcWzWXNO7pZ1OrW9z6KdfRMa/i5FfP6W36E8v9URzYGFKD+mBGB+qj9Yn8iFDQEIQBsJahHYYfW0FE+1IYIf1/yDKCZFhoQ+ZpGwYK/IWqDdiUP7jcsbCzWmXkJSF/VBsgMNFJyJBKQosSPPUcLYGAi0KFQ8FPuzaOEiRrTuAaEzgCGxwnSEoU3EdSFO6IZNQ1HAyIIk6dzVNpJgULswFyZULTw/b+p/m1DlRKOuGEp0jbTBkyuW6orKLNKH7qTFOcsJMTaPMLEEnQP25sMJPQadPBKdII9EoTCmFOql+ighBwwMDE00U+U44O4i5rtiQXp05jajhLUtNH/iaTgZRKOgwW5mE4NMRMmWpiW5aWxMLZT0cZLKQcLZsgwOcGRtdvsGxNtgo0WksWvx2mZKuKR5dHX/BkjpUplG1Oz+BZildLduPDllUvNySezszMI41yxxlZibrciic7lwWpiiG/P98NjZspO6pk7zzaTbLGZlHhwv4Uh8xSlQctPyVEwSZSi0VdxLdKUOKQztTNzMNGNTNIl/cWCxQcEqkjFy+rlrLaUUweJNsrpUjk1o5y4amm2ThKRNKyzalcKaUUWs96lsKeZ8dVKaTgYQJxg+1MggXnf28R6JHLR3vwr0OQJCEN9g5OE6JlSGKXaJEwQwWkzbjE3FMtRRktxGpnuZYOJysr6F8BkKh+DGEsYDqmHS7fgcLFDWzeEGx0T02epenpCxA3Skompd0wkaVmEwusmRjDGGbTKo7PWjbUtr+SgcWAsiKQ5IefUa0zUpei4gdyhggYGIJzQ0DE+93YcGkySiDDIQtLPwNjUJWjs1ao4d9R5aTqRMOGHFHrT5eNNTo79H17OinHhOSpbKuZy5eEVxpabdo5Z1GpTS7xLYVeVQy1cZUdPSdcTQ2+OEwo5owp1uXHAoUqnFtqS0odWkxPC+CKEacIkGTEisfX2hZka3wwMq3oyqTFypJpS/d2Ht3+XZy6QrpGJVMumCaUw7Oim8ylSX3swJhii9TOLUFQUk6QmPr7ZBJQ78z2Kmik7LTtt5TwbmnLYx8sLyVwnfao1kclQnecPD3PJznbTYtjl1uPWmp348MFqMuCltJJicLGXLU8HtPZMuJGmzqjDslRbNSdWmoppQ3nUaZt0PwPTUhE2Hhw9PoJPmMTsWtKGppM4JkplKTNza24tOxME3Wm1lz8+DGuWHaQo2pNLpk4vDJMPDU4abT4DhhVjDsDi1WYMUDIlL6fYZPmsMbCURlUq1iZIoOqKjmhEHM8SZUx+RA4xCwt0R4eDGEdyvOm30dmXmZacacB3Q7JG1kt7FQnCoyPHZbiPDt0Yy4Xa4Mwq6E6mA3dg3Ldbw7DPEOeXMMPYhQNgqMaFgcKK8c891U2eJkUM3zDINjc1eupI4Wp1wzI00Ty3eDMTs9jhmDCtVGMW+54W04YNmSysW/eZZYZlpUtJpxB37e+tsNcB1oWhhQjmolKUpzJnrEKO61JTPDJgczT7Ooy5Js+quVVUkmUopRShypcOzbwyZLIMIRMwaYaLb+OB4QO2XJCENIuGaRPtz3Zd30HZOR2dEtDtLgCKsC2tzOSEGOUQg5y2YDAULaWUotOplC2HlU6VGaqozBRdDaJe1YgZDDSe1YQ9S32fTQaZEwJ6exl9tvZbo8J2d25JFJWOzCp5ZKZX6aE6UU4kuDcdCIonoNGAl0MCoZ1QsOQDlgjmPoLa7LgzKGlKKFSWiHnPWH0GB0qxFDIUNAlZNIXZ4Pu89sE0qcTGKenjE0Z7qjsUsZllqduYTeGlfkpE2ZJTSU8MrlxP1ofqfwbEb+EWLA/mXgilBaj/H+YlgWp34dsrJqqjKqhWk9O9z1J6HPS1meNpdDoe/xuvr9ttlg3ZX7PndjPYt/dVNOBPrn75Ivyz9F/YWUmiDz0oPthTGk/pL6tCH0uUjopGWLphBDKh3/O5+IH5H6G4pP+gnGHKCsJTVcipM4WybXBYfmEjg5lgpwiQvHVgzBFk0imNhryrU3CvOJHIhcGHFEbBEFgZBrkV0ImZmXHIWAYg+RPm5mzlf7v6g/yFv+H85+tX+ZucgNmj+wRoRiDEGioPwUMRRH+x/v/NhJi3+6ikWUl3P+pQOD6hORdwuL9c2I6EzFCiEbgn8iMMS5P0HDLH5JbIueSWnke+/eU2r8cA6RRERzU9AoI/AvUWhcZFv4jLUoXC+I07RMtPfbCLUik2WmlT6Goe4wbOptpmTGELZ3ZeItL+FqUilJIq/W3sOfsJhsDAVCDkVsspLRhrKveD0Vv0Nw0GVcNjEKBsKCc8YMERGCfqIcBklEN6D4ZURjB3AZpIOxOFcuRyBlsYUpEHeDptw2Ae0o2ok13swxg9GMzb1Zzg0ncywZKtO8nS/KpppLUzJMtp5N5UmxRKcSYOHrK7udyoZtZSpNqhD+k7SXIDSkdlQi1JGjawyUh9JUXqmoSx3kttGFLNFzqXxm2yGyImEphwhMLCGKdEmthSIJKONJJtEowWW/fRk6meP5xMGmYaglFURy6GmIaVJSjCi0VEhIQAHDckfYLUEPeEXRE0QUaDNG7B2IrjmlNBACzGIJZwwhGPb+39WNKHM3JRS1yKKifU5MKZPYo9YvWArEOoIo9UQCgGSLKEnSMx0wUl0nxE5WRPVR5UJ8Im7iR4OVOy/DDJIhmpMK8JSOwojIqTFDKknDw25T6LiS2jiYTgxMMqYp6CVJSXwPB1hMwSIVmHhN/sael6ZHOow6fLpvO9A1NxE4NgGxpqCQOwQwypJhUQcUIwoknq39J6y1SHNSKrUFBtgWWNIe3GxYZAih4QA1g8Ifh+jScyjyUhdInSpN+mkp+KyDgerS8uCF+ingFmXcOjuUYYWbnR1FKnldNG1MNs6j63g1pxUcOOcFpR0hwBoGhlwgWkwlok8ZJJOCd6r6R74NkocwyijaEhwqQUqKdpYjwpJMUhKk2WcKtHuULRwRkBCEcD9pQBuOxo2NlcqQJISkr2JRHstaGKmqsowMyLg7e1o/v1wYNeGD2V8q/kowqG/SbHyyU8aGDDwnpgrKlPty0WuNG1rG2zSxTdwKUtwGVghbGFAwtCFmRohGzJSq6IqbhsUbGCzi9DA9Bofb0MBuAqHeIryQO0GouxByAd6Hd9FkPZpZLUOFJcKJOn5ezxl3UxgwTYHoWb+p2LUxkpYBRKIESEn2juqYQsXBSEIESzchBhCDxBaVsMBCGS6C0PYheJEo6EStU4Ew/EA9iCITSOhn2lhiFBgghIMBFIY0Twzp6nBpHqbGTtwdEVXqxkTlROlSRtRqkny7yzxtZjbbAxa0k9SiowXK/UmTcynNRp4Uta1rUlpWmFDhaUYYkWKRjIymJSWtZUnqRpVmGUqPRky7Naiptwxid34W2m6agUoTMMssJJ4qB7h08Dx4O517c1sGTJzDgnRVRoisIOiK7EVCoFHxaRC4o4YIYmXcpuGebLQInY4L1znwK4LMkViSyiPWxhCliu52pma9So3MIoXHThh4Uu6hzffQdiwexwuw5JAqPgmOmEocFkbbCFGoHSw9rDLGxhokKKVYEQ4QjSQXqmb2lOjXkWc7ogJ4pEXwinU4e2BxQMKMy5K17r9zwmxt4j3lzCe5w3UjIzVnZzMV5MtDl1yS1JxM21dXcUdEAwzVIcIMLOItBAIu6WeRERcgiWk0MNJoyyEMEAP62nPraO5SKR3W0zwukiVUEPmpAejBQS4kiyC+ZFBdiFwXkgoVESeVNTuQBM3mwKYlA/T9BaGGCH0ioX0qWnTlfLMuHliWipWJr6j3ZPY19WX0p2baYf2nnJqpDxQcRRt+LcvsUQA8Ksjsk2IUbYsYwSR8/GrdgpoYwnz5oX3arRDcnMHmHFhS5CJZJEgpUThlZipB5lB+0/ef1/7uvzn4X9mE7vmZdkkknhUQKlWoLUh6UtT2KYfDjZuDRVaT8PwfZpp0p89mkw0RYwQYh4SiemimifQ+hSE4WJdt0opKLs2wNdImZMKK+qZb2VoXSqSa/Z9mIjCoGVEHPFbeD4P49w/Vnq2MbIRiGPM6AAhzBB6MB6xI97uHt7y0YpGVzdMHXdVxTC3dL2QZp7e5dLqZwo9hdF7+B4QXhqceCAiEMMfB6GJUkCeVRP3R8Kzuq6NdRRI5EQR08OKZYTbDBxSJqUgfXGFdJXyp1WxQE3GCruQVKYdCIVAdYcBjFBFOo3KTR8KNEbTfI6OM5u7D6SqHk3a/9PhLR6+izqndwt6YPWFLWU0uXw+jAaYbC9IjZhOULxEQ3opZ9A5MMQokIvY3bGzB8nRYO0EX32rz7j30ZVINUm3VocJSe0dvaUZmYoobUGghadyiwMjK7/AqGIdAtP9qKPPQSHJsFDwZejpgcq26shTXTYsgxuCQowW9dQg97DjbVg5zH0MrmzJUdAxHETEWmIJZfu8yj2ygqHQggsGCwgJoijCB2mWK+jDyDxPvtNodWMKo6victsgh8SGQ0pQdBs6wsR1CqiGUxgrC9VM3aljgosgUQSCFQVpmwmW4+HtJPutdHb6Sqt9JoTuGTx3DmEg8qou5BV5CCJuwGgiryMlo8FOPprzhbKFOCrMUKPtPYXsOWMYh4WGtlehG/Z8z6DRu8QkhBw6fJ4dMu3bHp2S3illJSUmj3mB75XhXIEb4OS1NA7uW02SAfWbhdiGFS5w+XGKccR7ST2UnTu6UuNcNJYwovo0NhbgGkqJQQAOQeGOZIxkUljGP57KBgm0ogIgIhw+oeGi3EsllJwZZwOBonDy6kkIyqRMJUlyKjKmgZXC1rZPLlmM2SeQpRSilEjDo7n3yJy0465ZFaTOB5bkW1CdlKMm4wnlubSScp6fKe56ZctnDup0pPCltl9+HDe2dMzMUpClQmyhx+O7MPmU7J4ctsnQyUCQwEMvoR9jBgYHKqp1GALxIwtTBagxS4xhm2Dwy0lJmJlNYyeJA6VVTLg7EcTUdy6McQfEQwoJiaTCJjfZguUxSd0wWeFnA1Q8prSolOMMOpbhuItkzKLMIcSLeFskd4hojyIz2faS1OOHGKLhox9xoodDkPvIWyXtgmA2gSs+bA8VeCj1I9DpuUwMOVn0tiO09S0dnLKu6dHTBkpI2CKEOUQRO2xzvTJ8FQZMROBQLCGgjBVGHs7mA2yx9z6Qk5lIKKJ8eBhW3RPkWV6lGUihY5XAdnIH16PIhkJPiaLEfQ9UPUjAICQjGMYPOAcjp0IPBqbizLFiCFMEurIFsW1IAJ/qaGUqFFSUnLSMZllLmUFqeWZTEMOGXQpJiBglMDI+a+xeR894XiSBQJgYBh+ATJPidqNiUA9HokZRWBg66EVcCs+GQrCoYIMhDc3Vs9v79tZV+4ox9ewzxxCdDx91rn1SpY2m3wsd9P2aYP7+WzG3M096NT/0tesOIiUMLdMwlFKFUI2YP2gcqV+OC5AbugD4gantDyDZuIsXCWooW6rSw6JfPPkTwMKov7c1aMlJid9KbwZyf3U28UIMyl9SR9K3VDBMTUSZE99wY+pncxGB/N/dg8yn91NTieHk9OITb/OeumhzbbMwaZNzfu8PlxNVP6vLM8ns42pwuUXdfXaVvTi7rpwUnUX3ons63XV+53kd3wNAcMasWzU68A2BSD9yRBh+R9SVQBGDCE+ZFKiMYzZ6AmAwQZLIMpAmhALtCh3zaWp+YVE92Cyj99hNG4cGCDDhiBhhQESkZD9MJqw0ieljKNBYlkEsELCgjZQwIWOSxJFLtfzglqfsg0JpIpGMYGiUDI4CYCJQMIu5B4JMlokREmj26MbcTGn5Jo0UmWzDBScYkza0rbCeT8gw2BstGiqbSCOzIfJxixMfvNN8cuTYNxcruGFITuRscEoDCGVIfWSjMzh0KdMKy1TKhwx2UXnRbTMSSk1f9WZMlKJOFZikVRbk5Wpxy+ZNlHELb3IVgtb3Mh9sAIZNM1pyQlF4Upec27Jc5yf3mu3Y5ahwLNDgWBZYZPts2tcV15NThYq7G5Dg5mVsEikAwuQQhWOSEUdgb1RKsu8fIRJLIBB6mS8Y67lhkcFTkbOBmrEs0qP2Geph2AMwGIxC6ANESL2kiZWkUta23p/d3dNj723t5YTUSdupWG2RZ8Cs1uV4Pj8jCJ06UqeFQt5cW8SWqlCGCxiiUUBRIoHOkCHEn06mY0y1JDS2Zw4YaKJhgxJLLKFHrhjKWKnHl3YahU6YQxEOXUnflTLbgo69mFzJcZSkpKHMwpJSmWMMpZUZpZaWlwnBSZZhlJowQNZRFMO54PZC0g0zSjrujliFZNJglNhnTxMu7Yy5bO1paYVNzsMJwxKbIzgcGnptIPDZoMWLKNi1Em1R2ww6ZGlZMnTmOKT38LdnTDifdiMO8z4eDyKRKbWqO/E6YGYz7CnebhPB1xJhsnCUutEpSUyjEOGc5Zf3WngUw5WpTTCNp1gztUapdtrwspdLYmEz60h6nbQ455kk8s2dVLU9Jcd2Z2yopnsjUnLhxScI7qTtXLytejbwWy8uF9+NKUMuvo3JhQKZAlhrIGxIkrmBoRKJfs0bEjuJ0pTjnsnK9GrAWIZN7lGGhyl4UEnYrVtZqYllGM5zn4Ywom41hhKGlumDhSYtbKk1RJT0pt/4cuZnPDg0r3LtJnTuwzKVSuGl4vsw8UZKcNre2DFCFiZ4hNB7Ki6uHp0/AIfA9hfIQdMsYhiEjcJPX8DyDBHzYaTpZ7FEizoyJSqXGGlmWbcXrJlvbVsY4bfL3n1pcVYpMhQnOMcngUafNK/vKEL2PKPBpOAx7C8JOOyoUrFBJhbwe2HyTzDFAwaJhOlqs8laUMaGDMj6I30W5mLBhJQfTpsG3ewe7vw1nUu1WpyZYezjTEUw6OSgtIDWpG7ANkNxmQ7EEY9OpBKSYol1h+OVso+SMRVgnQwhUdNgmcMHMpTKWpMlTc5K8crfk9d3T8j1L7PZ5GImvPnMMo3VHfTimUM/KprHu8SWoRsYxNcGZcDkgGDdhqmJuOsWsYCqklNmpJZ0+E+C2sqcPYt6mahXVulJg7rXJw2yl4p9l008HC0c/LDh9WWpzNUB0aVjCHIe7QmCIMHzNzg97pdjc8vE7SlDwaU9nh2g5muvo66iq9SI5djpUTA6mllIqmGmdsL0NqwZs95FIwxviRl2aZmmjSiNSlxNMMuFAgaNTglMKeH3cgHkWAkYgxYhUribVtE/0dGtifnNE7SMyKULcPJ35bau4Ty4J3UkdnTRmDDBhwtpkpSMDHbArDTKmVFdvrOs/THWy0pu0zVnN8OPJ9x+IuhIJ6+CRmAejci349YaUO1z4wqndwezqTKYLVMphbDwvOVXTK5ieDhc5SjtTR3py5FoXaWiFKSV9Fjw4GYmKgpPA2paW92SmS0pKUlRSUJUiihUmUnuUaMH/NtcSRSoTMaG1qihpRBdihsxL2qC3GSMZrMKSoOZf3szEIDFnVU7E/DMSXqdxWihk+SzB+7+jTrMJv5m8iv+an/Qp/GYThr7LJcbqbmlhVBVcZFxQtrJhaeF4OVX6HodXU6urq3Nvuzv6NutE+zHp0de7U/DrSKayD4wFJlIraoVq32mGDYDBn6fbdPWcNCaZ0d8PHFhpR7i1Is/Uaj857dLBI1EYGovuG1JC1CJqQcwqVJWMAmUMP3JiZ7+HnJ0PEvr3eE5Ok9KSjzLzxMzGk64YJhSKUnhbTtSvd5eXeeezlSbd3nSAEaqRIzKklc2gGitgsGLFVoaGhpvofgVMCq8X5a1FTO5jo5+rs6I8/Y1C+hGRBJEePkiG1zQfkR/iH8oBYmXff5ZFWiCBkwsY+ytxttw4RmQ1RT+Rp/s/2cxttOCzMdwh/RBzgspHNUEYUNBAh7EG0UhSmIqWEoopMNrGP10yM0Vi7MMbDaIVYwPtP5GB/obFAQSGtNJZbCYcrLUXJRLT+KaqVrktLN0zvTXhs1Imi1PnhsznSnwmrMnKtEKQlyCBSCjuEgT+0wx4nFODBRWhTBTmjhNzGWaSl23l6aTJrbDibKKJ/mnKxl9Y5Z5jWHZl1jhq21uWjhJhDVf5k/UH6WKhwwztDi/OO4UTfCRO7oE3ifBiyiYGdQqAtJSFLTQFcDjEhiFZxVByyBGc4D2kzi2a5rHwYIWJSUUc4X+kkQrGrBhYKSYAVhMCqUlLU0XMWqk6uSyJ9lKS23Xk4bSOUc5juFAQQQrGpWvYkkkwohOMmFB1Chgk0ixEJsDJPaHTQcOWTUIhRVRDRTDiE2QSikmxCJrX20XQH6RMXarAOnTTAC0ksDBOZ+gssU1MqNOGmlKZRc0ThKGrRZMyhVGet6MuNsNqi1raK0tJnMtNC6lCknBNcKbJyRoTkKUzA3EJiYOuGcspSZYGnPE8Da+ey1l9DJnwvhwj6ZIlJTIJSgYO+6gYTNA6wbJtKzdSlNikpcbZTDKUlkqKpot23Ze25/n1LNRxOhbkdns2pTJ37ZYbOVqUXCaNsN4KdV11I5nY8PJhDBlKCSyjkkpRMY0kRI0GOQKBhxoVsSuMXBAxCuiQLlQZwwczcJaU2DBIyhiZOc3piwbBkcBg6JSdqaqhqyzWvKpWBp4hywuHOxeuLExcFSOMQFVxhQe1UEYEDAUmmdmrTTLMq5GlJ0tUGmpm33+Xj20bUdFW5LcqTpllhmSSi8TSkYs/ybhT8igWChkysoYY80mKDX9wdyStID2fE3A1nL93QvkwURdWThowbFkClXLMCtjZsZCwyYNmsK4oKxI0cBalmqE9dCzRyCVnleoSL8NCCyTh0QqK4+DE1Ix0mGgHJMFBDDpFHgxVSqTCGYVeWWmpvLu0tTtlVO2Zb9lomnq2HLqaZQdykzKXNqay7mO62ElPDWMXbXqxy76GE1LkyYbjM0qZ9578vMSiIw33vYV+RSFlnSPDAO5Rg9Dxvz4emjF+6dOzVRlSonK1qaZfVzOz2vc1PMhYtdJDHGhxhHcu8FUI+ez1e5ioAHZ4Y9+3dK5mm6pmJtl3T5eWU3bSlQmn2WSpkhGFnCgqqOjkJWTEUDpOJhMwd8SW2SyApYWBdAYYYhyAKOVQYXzwt8vpnJrntiqplE6dQ6JJpUXZllaM1MrSlFsS0tSVZazwsaUwUaooVC5MTSJy0u01DRVFFNKXgpijvpZpJNKKR7DMgxulU07PbQk6ky1FLLTdMZcqjUhTCW1Q4VM1l9yz2qPibrUHz6S91t7YdK5ynuqTNSCebt5nPiUzn0a06iZT4cTgYcuBhJJg/WjUy5GkRljCsMYUTCUqSFzLItUsfLzsk1U54YnD8mluVMGYSvl5bbTbnazg/NRphLROtnyPqEMlBvRCdLCw95uft+1JThMrA6sVooDkIHh/FkRIQ3OoLQ4M/tsT/Ke53/n/t/z/lr0XIvPYgMxBBbjqgeA/PacH8LzFXaSwcIv8sXIpMMivwecDEwHKVPoBIiQAsoH0PioTGJGCxdhlggxLmh+A9fpEdWMT56Wxx9vw0N1tobkCCx3NswWDLgkSFFzckA6+omE6idIFgiEUmqHIoBeCWxGMVMnc6MdFfXYOmuAr1V1oixRFGLEXVaiuExRcjzfUvPk6VHT4nLVN75nGRFCKVwHZ4cPii3yt5ytASxxeEoiR/krytqYUSmFkvSkXtUubUWylvwQPg27HYwYI/GIH2ox3IbEEJ/mKbApDCon+RUpUmFYRLLta4FyzCjwYuj91T+0ZM4N1FRe8q3VlFxRCwuGTmYO/6Es7Tgg6qtxoIMhRQQRgw4MwZSnKyTOH98GRnOWGFSSE0HWJAlzSKGJse0xINClhQnLHWTu/pthUOKFFppbC4tS2tySXGlUxPDPtlr7VOD6Ozsz28zbspo8CmzNRmHHLpEjKRyyYUjk/owyRrocQ5/CH9Snl7NHQd4aN1OlIUdcWIOYYUvrjCEySw7i7aDSlZidsiShxpMjEiQ/RSKDly0wVwzhhLGS1RM0tizhHlG50pxucThjJ2z8PK43KPBcSnhbC2K6hvRTkMmS0bQHNheRqwa5I2f6VWfBwDvA2HfRNxhBzA5vrDQQw7LcEIJU0vB/mplSkppVOLXhnblg0ktCIQwoYcOh2YU4JBkSQiPMOFTvh6uMnetgN+C3RCMYJGD94lO4aG8S9dlxoyaUm4pkzpJaihZS1OGtSTrVzgtbYZFJhKQ24m+DThTY4cMNsGoWxIwwbWZU3GpZclMDMi7LLBxYnHIKNWFUXa0Bhcaac8SmEjiWYN3dqGuFYZk6eHNqTLMbs4amWGGKUKk0XH7SozUMCnVMYTrH+blpNPXBuYdgssqPFPK7kTw6NKYhOrlWd6zCWdu6mjelRSgIkciUFBZGOypaluwHNJkyCwwXU2IkSjZJMY1UmkoTnvhgdwxMuhwDabpF2MEYZGkzxlwKYMoNJkuoGooZ4NUhASA1WMxfszEr4TCwxCTDAFWHow2zF1iINExp39soMDOmg/TwNjhNTSZnorrV1OiZIbLDQpQUOsJcMJCzo7sSuOYDuLEUmKb6Q5B8md6WbbJgmSEoHBwkQmSgZDBhJDDRa1afymWDLjW4Z9Jh4ZmY8zMt5ThWlhmngeVfLv4DbuaHg27ELz1Mmk7ZDTgkwdeo3QjmiympGJgAw4LuwKP0p6mGjbSU7bmpwqcMGmi38k/Mp3nLzwQtXAPg9ChHfDDDi4gVDCuRxekO8xLUxWnwqaqVDJvFrNum23WmGu/hmi9XDXQJ3OQBM86MMZ7gYZwsgfAgqWvKsSTMUcyeFNlOxb5w7tmjA2fxEC6L68s6ZShjgT6PEy8XqwkVB4QUehDMB4SWVYmDo1hm6tRoxd0lTJIUqc23ObceTpPSdlFns7ue7b57MSVEg7FWuEAUlXCAMR0JlkkvPdsS9zwVaid5VommVPZl2c7LdnIyoX3lNyGMPukNzJCGoSlp0+Uw6goU7fu9KuqqVVLqrvGkejlRs55YR9Putyr1wjo4ojy8u2on1rU3GnFYcU8SOeJDbLiTg4+np6nCklPB2UUpOk6UV3aYkx8d3lvE6nA44og2NCZcngYDsO+cm9F9hfV07KTqJalNrhiy7UWmPv2MHGVuHCjSIU9SUZD4EpT8W6ZHQhQspY1SSzS5JPl3nzSPu9Jp12d0UojCdkrMqVVPDb0wZhk2NqPKiI3FhbkmCiwojDAYaxYlvqVUR8yWMEsiFkdHBrTZoPK0IUaVjCSwiuCOYD437436GxZdFr5IGEfJTalIUw2TftlGHS09KJxUhmGJcYRhVr4jEg+42NKBpjk8Dd4sWZKeGMGGb5sEVzBLvMv05d3gxoSJsMMDDDESRBSM/oP9ns/wzmLkfFfzUG1kn/IeVMT3uTuf2lP4f2/w/zjHC79WMu+XHXXmLfRo+0Xs7WhBx23hci13Ja4JIR+WSNeT2nIHXXejdcffzR9oGCdZUIiaATvBphjBy8HytGUysx4aZyzvnjCha2kdKWzsVaz6M8mZy4M7M0qxNPYzPdfMwTXtP5zyphgrl4pGJkHzPj6R0LXNiNtYGSo+ivJgDM+QxdSMCzamJJUImBMIQYW0zSJq5DqvmXF+wcHO50OnGBAuY5Eqj9CbkjUboawKHJJY1wDlc6EwsUZIVR1AuswyWplqYKNDLE6DF4y0Ox/OS/hPy+1R4JGEX8Py+Bhj9PgwhPwJGUQpgRKhZcYfuqSZhgtcKSkXdq9lSrMHTMTu9y36NROWIj/3nIuMqqKSE/gQpWD8yQghAcDA+ntksjZQQGC0soUlpcf8pRJemm8TK8KbWupp8YYJW1oi1E/qppkZMCUUQTAvP0aPdzibOFGJscOBhienGUKwbxtNUmmNNFL1ouJnLypNZatYwooVHPNsuW6bDJmSjCsVLRM8NJgo1tZP3i2cssbyZqU/VlJnhUa0rDKX3UydKhacqScThrRUU0lJShYXdFlWf4IVjELr6SSoDHzbZjhdlgSQkNbjMQi4gMlVI3WqU12RSOSSAVkRIAxBCQODLHKZjIJuyAtBEuCgLGeSSBUYh6SkIQKixyxWJEF62bYTEzN6I0ptwU40m5TUZqNQYqN7NQGZc/owkaTLJgw6ptzy8u0Wly0uVUaat2tLyJR9HdpT227seOrxyWKfzMnhNnEdcuEyestK4k1jpcQMk/W2gsLAlNmxMENwwiyAwohVfA1Al3TSCIRViBoianOT7ndhpFLjhCLLiDCT2LWigVJUWoAg+pqWEYxYMQiIkPghs2Agge+MGHo5Ymx5aeHbTllT2cNu7rGFJy2vkUszGJKVKU5Si28jDZhtcwRmpJNi14VBti0tZbJ3VhqtKVrphAMVpRKlOOzFCb0KwmWE5SwLJjiQzMKaSkU00aMnM9f0y2VDSa4LmeTpSR1FU1M5RktpchakmzCEMYA7jupKMJgCzbCTKMDwjZLTie0OO/vB6E1mOr04bCkkcYOoPRelUVSbcfx/aaeSnZ28d5rSjScniR2emUlilKFKKeGmG0WpnszMMSrGGpNGGlLBpgiqjEy5TUVKtGDB4rCErD0QWQIuEZCY8NEZaVIJOB7/Jhi4amOHNY4QiUkwShlIMeA8h4lZYaWtSZcKtRpSVJnqcRD8yifratGIaTocXKoXh39o4YxRYscfV7N+ORn3+p4h03E/w7plkp20piUZxWFJbumTKYFajFnhlNPpwyy1SYnqioTbmGLLDYqBlPTNKbn7z1BqWjrqBYh0sQLpZrlDKtkdFQrJZ5m6YQ1AwQbCPLOWWanjN3SlUKj2Y0pntRpxpye7vIWe3gh4HMLOCeAYbpayl0QrQBfRuKSTBnyoMAqPaz0HCyCCw0hGmntp1CazC0krEzNKZKzeaUx6MIKNA4DnWWllNamEkrHaEwfZppVHbUraWpLFKP2OWnR8SbJuyaSHqqnBfBi4cIVWrW7HTO5HaXM3GBhq1LXLlM8np3VWUWyNXiwk9yuQWNaZuB7hxmbhh4vvQWCMw9QRNQMEkPiJ355rUOXR+Ipo8Z+UuvTuns8sr7PZl6UnBbwns1Hglqdyc1JSlFN2pVuDZ+GPs+eNOyOXt7B8KV5PlTufR7tkneD3dU8tphNqot5TDU3Tl4mMQq/nsm3lNmGxw09yU+E8JtynLlTC+VMDha5J2jF9kS7CgyaBO6sNkVfD08PyB+Xh8n0D58KJGWpKSi3ezTb2cOx3eJ2cacuyqqUpJSiHYymnln8tNqTzuW4Utmc1m8ZizpcdlTfpS30abYd2paGJRgoTmsJSlIoFKOlGtNNTNGatplZpl8Hd4mNInHLByuJk6aLNWamEW5QALFiJwMRbwBKuOCyYwFmCirIeWEqsNpdtDIldSsSXR68OtjA4R/kuhikqf4HCVWKCSjUxBh9Dh9fbWAyjF0WE2fMjmyI9jvY9w2DRoovyU32eTc2HdGKdCyqhT0LCxhpowlRpjGq8y3CjAjlf3GVwDiO/NHBsOu55FZRYblJy6eMhhwFkbIXWxDRaVO/5HV/ceXLp9H+fiUb4OWNmEpWGWsq/1Vypb/N7GzCBk/od+CGjJspIgRkIEWPvIP5flX/HT+//gyPbX3DubuOnGNXdes5C7Vp7q1IWhZqiq+rEGJEusHzNZLcjYoORgxcd539Pcz+JmY7Gse7+Pl0+I4cP3XOVJhy9P3ei2hTDWpypTCeVGiz81v2dv6MNpTwNMPotbxKKBQ+ZwTmVCUixoPf5SLYRKFy8aEBXFoWOeOeeXcweDD1MPhhS3dt3Ynnstt2eRic+TwKNMJIGD7DpRA90V9Zw30LT5+vNMWRnZEu+ens7dkxxcQ4hrpsjVEomtSJiCq0y+x6aZkqhVE0pSnsqcMv+WH2idQpRwQHYYBRS+ZZRohQMUjgi5FmIpTDUhlUFCqhKgUr8Mps2U2bIiJZgghMKFDCFY0iSQURhIlKQoIaEoiURuaNktLZZK00plT7JZo3JXjEaYKdr1OaTRkxPDp2TQTYJSJUibECwkOaJhqDP1zYUw9tPHR2GGyOa2SJTVQMHiF+EYYUlFyw4juMLfy6YYBgjiE5SlpakqUWSmP1ZOw03bcxJkoBgGFmBcLqZcE7mbC7NDDRDGT7jf7v4TtDxOmGU7mDUOZODsmcssp3UIPILsa3ZFsErmkFkhQQoDYIGrJkMYMkcWSFFFDYmTGrWzYaFPPfhByBiZEBwkOOg3R0pZmKIJsgVKVQMQDjdmTbwxlJqiSgccbaKpSiWotu4lQihGuSSFiMHsYY2wykuNcWMcHwp+9jqbUZYkaUhty50yqCacnByY02xPCv5WuOzLhtc7TWzf1wsUlPhY27OmJHRv3aZMp2SmCaYUxuZLZW1ojlS1zlwKqRpUaSmphhZDuRA+soRDfNhrEnEiJImBq1MTlNbJQ0gjGbmGsKJalNCfaSz0UB7mINIrTVwUEjJKlCCRpJKGALLBivyn6kCtadNSOJjojp0Zl8FCfELVsIpSUtclqcKSUKH8Hjc1sumDajDlwmlI2/ZZhlxhKW4cVwvS1FKNIYjbl3Rgn2w8RMMmkcYYTph2fzndJh57Op4djbCbUlDqXJYpTeFJPu0tKMDami2Hi2mFsxSkXPCm2vrLjanSRdScI2UbZamkygcQ4mJqoFILsJKxmKLc5jmA6oChlGiviMoy1mKlTY+iktOIpnavc5lOlLa6jTvOFrVfvbR2S0yYqqoeVJlTLKzClKfzWxDKs0qKSlkUenaZPB3porDaV4VpmmLWptpQ5YGllQ4rJNkJDTNGgdQwJlMjn284+HT0ctFKKOOYTDGMMUYW1kd3buNCsdyVlOeilQQPUEfmjAbQXAZIYGAJZL0MkZaLMGHaFG9JMKJSXVxmBgmiiSlUoYD4uXL3Yod3SKC+RiBiFEljObLklykja1jhQ6lGGiTCaYyDimElJeplipRtLbee9lRk5VNrWYoRHPDh0dOrJa232On1krw6EmBgH1Il23B1HFKuDLLg1qSWuLVo8S5lXmZUo4atra9kXSTww85jwldnGpC9u1hyiD8A2nyewmGTPBiSx8SNEx68HiQpTXIbnMLKzfMISmjUR0ZcNwon5i/cSYxMfJYnFDp1ocZe/Cu4FLcIAgPY10zM0t6aTpUmNmoS0K3bOo0nGGOgwgXYkooy/SEgSe3utweTkODy8so3FTtOXt3bctjx5nWFuqTKk8KfadnluwhO+2549Czgu3EGQ2OwbkaitmxMg1ppnwLi7dY0qtunlg2tSn1XhPdzLZcYcxulSTqOisrYdzeEUXHK25h9Z95mSe08uI4FrSkp6osU8Rnu7shG70JgkipQ+hZYcE2Nxx08utYdbFO2Ep1evNLEiwxcqSIDJQQMObAWBc6CgLUTzAcDY5RkS3Oi3A1AZBd0Ohbik+jYwYwCEgkGATv3KLFwUR4PPxPdvl9DdobPMGERhHkIAQeqngHAt+p6nuMBlo3d1IqO5BicPe2unOMhMeewYcFtrWo6djhRTeVIUiiynS1MrcT5y5N6pU7ZircKzo7zCNKG4aO4HWHBMjg2MEIkdEU5IkeBtPEp2nLbhSkrhRLcHL7PDzhv8VVO/RUnTDudi6aPpcWcqZYUtPLi0ZHZtejBktWoERhAgQijIEHsdDAYDSLGZCJYIiqioPrsPOE6CErbR2pOtIadLkJtammrvlQ1Q2JHR4jzg4MpxCMGys1zgKi6oqFSpRQOqPq0+n3WcUdSdIYlmC4opRKUmFycOYn0nDenwy1DwculHBwpTU4S0unCmLwGMyHwsamqaaUyw2x+m1tqaXb0m3iMzKnFK/4zmMzg+X6+Jp+ykwgUQMm5iBu5+30MlGjtZCXJ4EP5/if8Rg7PXDTU9ZezAzZSgUGGn036P70rZ2Z2axBukGj5G8M/LjIVXZ4d2NR40wbmO2W4MTcwd8XUdBIlOM4PpF7e+EWyke4+VxqvelGJuXbNtAgZmhQh3KBdZqhse5QJHk6IxHODUoSLZEzFZnQoUmK+KwWxjtuzDG5wYCVCgVCXQ1ke5JVImkjUqZmJqGRsXHIhc0NBxVA9yPYOCY+ZhYxGC9U5592appyFw8ERdFVYlAycTU5Oy3JnaaqVFyMKxw50Oq7HQIjlhhip1Uzs7OR0tJp4ZkRqUevldYTwcPBh+Xuc1zHA3KFSR4kCxVzYLA5wZEDAOhqWzmbQFQYVyIOLMx0jychoYDVzMTqQAkHc+bwtU28ini2H3lHfMJ0+zDbTTSdlqpVO6dbBEiU6GB2jCtaKItzbEsdI7yB7Ep+EscrGR23ezrjl4D5u48jOXtxREiYUROyppxh9opUod15pU/kyVrJT8nw1P31I0wLQZIWSd/IYWIIkTAsT9YZT0wsCFlR5KBzcYOSinJVFllWUUOB+l0y1m5iRSlJiaDJiMsMU5UlzDK5ei8MgywtKKjKrlC80zRllwotWWGkslqRSy8ZKYfyk0i2oufaUCmtdI5w7CYIn7ovToS9GJDaUiEjEgJgWEMJmKCGBTSEslWqHCfzU6KSdF2FJpqNsoyMOw0M8fzVS007diim2HPSWomlSKTSkjXUn8pyyybJTOphDDg0nCnKl0YZc42xN91LH9ENt4k4L8Okw1KlntPDQHIQjSQ4mphqhhh0QsaVGQTRjkWyRSsHPQbEKd/IWLoFrRjhzDFnDkwqE11JLaZXq7tbJNKUWLRmi2ic6yphm5xSrRgsW/rpgzjKU0TZG3CEEyb05YhTQujGBKkFnKKYHFus05OKZpkaW01L0HJ2Uw7BNwYCJOlN7Dhs5MERHqjhTApkrjMfZUlFJlRHpdnwp25nRh1DYw6MuFGChs5dnEmmXSZSjSTbMUacnDpZiUtxyjUXvNSqgpyWUcsMYLYJzJtyplSmzQpS2JMxwVD5cRpQ4OJTRkpVRFqiyocOFTT0lOynDBwvHLDnUqhhg+ZqrCVOA4LHEdw2An2PG8LNLsHALoswEadTb7DEEkhRKjgQDvJ4oGsSlqW7y21rUdMqph5WuGlDRRtOalhSlEcSaNZU2mMRJqGFxcEbqd9qcWcNzB2TIxMsjEi+Dk5YaomE9ks4OFpco787HDK10U0RipGa29mTDpQljbulCu+EmDBlhg6TbBakjJrLK6U1o5ThCgkhZSyAcQlYTQ8PmpoxUOxaJCBR3o3M9eEfahweTkpDwPJe5B6npNDJJkRBR74hWNhgoCxl6wcGwxih5bWzw1cjk4S00c9plG1NSGRpgSCYJEJObCiERJukE2/GUQtsFV1DPFwsTeFS6lLx9mTSkbj+i9WTuu5yoTg3MhgehbFoCCbY0roLrnjelVNtG21RRMlqNHFsJQUNFKKSS0XJKUaKlsMIYpaZ05I6TSrI7GxCGrGdzbLLnhvwacDg7cpzLULSKFv7T6TnueXjs8Ya06d3Knpuc7v05VMufIwyYR2D0K2eOE0bdRQ5LGyYMbTLvE7JYdNscTNhkkSCwRCzw8NhOHY+EjeB2dhZnLEdJkzRlbPppY1lt0xKnDpqTu8IMGtK89Xk0BaHQyBOCMFKYNEOiXMdii0qVRVFOVGVOhqRNEyqaTSlzdjid8hdOOrenh4TgrVtzp4NDg4GmIijvg4xw+xurBMMKl2wYTMSha5y7eG7O7nSKoNqZOHLLKpsUqKcJck4B0atVaXLS3PDJ8cbS/Z5Puie9J0nD54fNcg8VaKIz8xSBRUnmG6k4oYDLytZSlKYTMoMKaXEo55MJltNtsLki1RKUTYrWihzEdJtOxu4fiQ3ivFvFlYp3NSMZXSlT4m3mUMuJb44t4k7eJaVH2WLmWFmHd0TTKuqtpSd6OWWxwVDAoRmAmjAsCCIDEOkJ6QSIlJMSrHNahgkY+Pzn24iI4TRJlplYZqw2lJRLX6qvdrg7ak47roWqeF2hY574NkYpkjDkIb9RSGWcGiEPMwmCJjSbGwBFAH6TgggO4dmFJgzU2mFSFK+j6yzl7rillPOIpktn4egbiMFOxCniJkgjYWw5YBBCEFOTN2TrSmlO77yzOzycPiY76uyjgo5cITwsskUy62qxtKk5cT6vmPDpwnaVOVJbCldKMl0rhpMR9Ozv7Muns+e6FVFVO12Tw6hmepcuXPhNnlTp6dPoplg8S3KeZkyYWiLK1KqKTvzh1MCDlhIWGCwR6MJCGzQ+B7OTbDClLYB1LCCX8o5DU7s/GU+wxFiyIkMUFsujGW4x9DD5GNEwULF8EwtpctfQydAhZgh7PiW8GjJe5FCiWUUtkWrafZOc8YOTY5gYcmgM2AXJAdbwznIYblhhUGJhxNHEGywQxLUoxw+vhO/m4TEuy27aw24JiVbKOkHpAkIV2NlVCwJ8FSZIGREgM6wCSOixkbEVudcGidjgxY8tyEQ6kqMQYdK65NEEfd8KrBhshwQKbkfIKTzGBueJFcmoEHLQGQWSRKNYcKGDzsotzxmzvxzexv2eOsyNLLkpalqyW+3c/M922lU7SZGSz4tEIbn9RFs+8/rIfl+H4H6HA9j4Up8YSJ74uuBor3ejHXzrHxy9pT79XHky7RjAaC9FROoJgeQZkkLsO3SuI8qRM/Mu01lhR3mTv84e0iTfLKsxVgD5eRnUFVo1eXRpx95x0MYVV9pzsST6QjTh9mnHhUhNg0nUV2TMrrC6s7p+WpN+ela3rAublzgN1DE6LccGoSOgdCphiVMjr1DNPl923TKH2fYp07W7nD4zLlqTqcypUoy8Huez9/l5T3cSlTgmDoy7NPh5bcD32dGnwn1dno2eHcyy7lrNJ/Nt9cBzM6HJwYExYHPYqdUxA0DMKnUqYC0DkkQNyhAqDFRcqZPfk5DbI1hoLEzOuJgbFBl0OSZ+nzgSJiuQKhbm4SJGxQxCwtCO2BQzqchwYCyA2gJRDELLckanJ017lxVy1gZCzGFEdMFhoExZmHc0HNdFymKFyJMrAVhMsipU1OxUWX08GBsBcYNQ29TgurxKmSNCKLERhzAYpXu3AtzYlO8u6fuEOTqZn5H9PyAP8vn+kBP4H0fN+qHaP1W3YtH0BQNfMaBhR+5TMU6n7SpJSUi1Slii0P5NYn5UymJRswsUk/COcMUVQjtE2aJhNAmUoQtGIJED5KWaYxJfrQOQNZXZLg0MZIzEKH6+24ZhpCBUDKtClxhEdfv5oyaMlCMK0UGvlkzRTJami2WIYyxEm5NYNJtpSTOG3bZfDK0prMmRlKYWuYRaSY4BowE0RKGWGyfDcxH7pRis2GHz4DDK2UzH6MKKvOp7zs7DmahxOF6dzAEQElPC0jyDMzUyJNFOBspotFDjWSHuWThKYT4WVRpaQRswH3BixrPdlk7soNVJHDJ2TDAobZKRI4anTZmpqPApwfD9JsgH0BY7A4dC0RxqW4kzwsfukoOY6cyTsJYJ6IYlDJ6emF3JZTQ6gwoQxlTVyic0ummnFCXKYZMrbbMcvArDo2nUtOiiDqkJEaDEEUXoKmFaOkFBSEHQMJKNDEYdMok0gSZINxOatjK6NKcTLdMLWkbbixpsxThdETl7+G7y+g6tPBEpZyT9oPh3ZTFPdRsxPDDo60jC9LqU4VCmHbSyjZo34VDs8Tue0tDKGtp46W720V04UjAlnhW1qwy03Kap43K8NsMlCQ6DIJ04YzUGYFnBOCTZztNJRUxzeGmllP2zF7g+/FxibTsvlpkoDOusrBHwI8HNHLgLNEmDkljITLx08ziTMjbl1vkptY0t0TDvN8RZhFKGzafRKHTlaGiRTEpLVIRZHhSk5QwiCYdTQaLGOps/MYbfMskaB50SDGIF0xYJhI5gZnhpqkANHNwFRupiBlslmjGjhftQYmINiizZ2NmwcAWpwFCtkiLEIA1kmTRSxbENREm901IJ9/kCWGue9fDEo/fLrbROHZLcDy/dy5cOUVHFlqKCyiyaOFppNaJtZzgnbmuMG1FOYKdbaTUTlNrODTXDC3eDPSAy8OsSapB1VjmdCScCgYYtgRBKCkacGW1irRMkqCWGixMbRRTCUlFOzcRzoWrRrDcrIqZKu5GC/l137c7NO/LucDVXMOyLdy50MnUa5FsKNB3bQsfwh3HR4VOXJwpacTCSakamFrlzCPAmnQYDgwYbNqlqULWqbbbGDCqSsJShJOkp0/ArL+VJmHF0HCC+RO6KxVQb7k24Tyy6iUOjSQvahtKAqhgmDdYE3hC0eSKvBogZsjBzmW60BYGWYUvOYZT97TV0d1F8rZZROMJiXIcJ3yeTTKu6oiWlJ1J0QOCCYBR4fEKCDsSMlEWZNOaWYXBm624mY7i1suXZb1UhIBIskTg70HXg33I9kdHchhwcnBc6crKjTJPNLpuXEmVRNj0wPUkN4oZOCBsRWGiHVNYOCnYIdFcDh6lRvWWi0XWd+9TV2l3crNno9E8NnZyppJU6cnM1gndTVePPI4e9Fm+XBuNkImE4dFEAOxFWwSFpszNbEMwU19MlE5CaNpMSES80cP1PMAqWn0ZlnDC2vgx7tO1FV3OYxKbp2UmHK4NqMLptaYZqLZPypFqiSlQeVoy8I8sTp7ltKKShR29cKLRYiDJIsh0YIbDZ1roWG0n0k1HW0AHKXIAWMAqrmCRE4GmXMCG4uqhkMGSLCBooDcIjbbIyI9RsoJyZ9S21PKpbakLUhR3R6jp9cZU6qFRDJJlRC+sYs6TzjMIo1aBGGn1lqTXt7m7JGjtLcQxhwQvu4MGCLCA7ljfqdvJ3x2fGLNo8LS67dLYSzxeCU29KLLHg3KLO7QO8bI7Gw2F434iDaUQnkDv5FUVZo0HORYsWLJyfjDw0Og5RCiRREcJw/LNfOa++0rRmmCyWpDhSnDKJ04jlh654hiWU4jBQlC+ANKeianhXjaWrQT9ByBsh06IXkaKBx5enay7oSeu9zAUKZZkJjxurmq7uBfJi+xCGD1LEhDYLp5IWXTQcuC1yehk0VR2CZdEQPV0XacdzGPO6isFFbYdEPvNBuffvZljfr+TQmoSxi/AK2HbFldm0qJZ3Nyj2IHRjogaBKJUItjtuwxyd/TfdTIpBg0bkofcV2D6DRlcFHHMZoyORkDfY2fNV1MtEcJ4TJmMhj9g/EiOH7ggD/uPyP2Pfx1cNRhcMc+h0bxHsMQbz6+RlSKtjWCSS6MQZR8DkmBxgz9mvCeN6vFMfHprPKZYjtVYP519aGEcbk964whvSuF5NC89jI9zMxNZ8h8Eg6DBUKDAOtg+JmZeZZcnKo4bBdVLlSxgTSMTYDMzORjqsST3bdI90+6Hpnudk8+nE9reTonYp66Z3GU+q1u7mdMvCbeynph2fXS2z3acp8PDSx909vc4ekOXuZSeXZl7qeFuHwbETgYsJqnFjE4KkSmRhcwF1YwJTBXIFnsy2+GiNunmnmYeHBt8r6NGWOaVXX1W8NvM17uk6adm1+7D7+zt2kbU7LTam+AxXORIqBZVqBIYwNMw3OhkdDYqZByjT4WcThpqUrxt5qd2E4fV92HdO5ycDI02MyJgUMiRgQMAoGEXyMxVLDFi4rmxhYxHJnJU2GIw2OSJMgty0zVLdgqZBUsYpPyDlCAqMYmiJVfDAmBfAZYHuc/VH+f/Juot9f2+p+Yx95nwQYYYWYOnDk7noMBxqVEA49EDoJKIwZRIsJ6MMgcYSgH6tKAgO3QyXEYYaCyTSzQaE1TwjY3lsoIWXkyYKQy/NYQlZSEEqhiZFkxFZ3MlKYRleTVWkapptQwjPYgGT3Q0XuWaUshEkDkeXEhuulQ6w7IzM02ib8J6H8jjsnZLU8gFrwlLRGzSQKYEXki6b3YFnchpSkxI3TGVmXDDjEjlmLy/de8LzhY0qOWomlceC5Ndn7p0yybjHKYTO2GRSowUsoty3O/Hbu5clZJpUTTMzIlGFlqOothwo5tWHRKKlW/Mphgw7HEHJRU4ahbUjzPRNptNS6knJg4t0VQmWmVaY5ZYybPgMZTmPRMKmDg9ohTqTBmtwoeHMYRqSuFniYjlaiovuzbXY6MEzGnKbrSnF4dmlFy6lry4tkzAxBKCfVAkuljhw2WNBcDgYllqumCzoYJgYik/dqDUxAUoC5HJAbrjjKdgu8BgvI8m3JDmLoyQtYQ2ITkiQ5Asot6ZmDmyqNEfmE0MHBRnSxHB0jDv2jI/bpXfg4V4TeMKUeO94jCeOFmzPlNNrnlTmLN0WXGcTFVXFWUxDxJznRxws5pmTU6eGykLaNtqXKUm0pR1OUssXlhzIttgmXZSImilKljUllniB4CdKd4ciRJKcLacLDCMNNFMuF1wGHGGXPLqc20adGlMdJztlJaplS2xTjbanracLGlhh08n1ocPQ6HJSYogo8IGaDN5AmzqVqJehcwVopBogI0sA4M9EHghotOVonDR7PhJk1vCx22l5To6Mx2NuxTbqcC1xK6uQsUZUlPSJEqwCnaS+Yiw/ASXTEkyapYKQ7UlFLFrKihGKQJBMUKUhuYLt5KNAuoOXYoLd3a1qZUU7jusybTiUL7OzslHA6Id8BESlOk5FAfaeCVWempw0SloVo8SiTtNTRTRCGowuQ8lS8tk3G5XBa5alxaFLXAULIBQFADwoSkeHFMjPzz8KVNM8PHhJPSJUSlen8PDbyJic8k6gnmiaekqNHZzxUjiWjsv04Tk7BYy2pybllFlGEkQ0Xd0ROZohnLsYjahgIDIQMquEAM6UpBoBgkCMieiCm8tNjpshxxxwc8+GJvDLS38hYh5kT7MSONvKPDMjwJy0JgTd7UYUBgHDNnxFhC0pZB6jA+CEokCkZSS0WWMycSUpQqjiUt2kjIxmFYcw8sOmxx7PLsxOp279QwoUXTkLsayqlKaww6n5qNsJvalmI2EgEhpYZQ0RQDcVsKKwmV0MgQ5GixGzdyWQ+cCHEeg2BoyU9/HdlDMlzcwHDHLKMtI6di3GWxx2aMqnrgwwcplls0mTzi6jEuipJToiuLN0bYkQ3HoOBkCJCnGPQLZ2aXlNDTDzyQinIsaAks+r2DiRwXElkESAJ6cLJMDBEsElQ+HyJIMGDBj5aHpLMjnsGxu660ymyt9AQZ7NwbilSKOVE3plqE4MRnOsas3i00py8reXmab+HZs7KDtVFVFAMEgDBEifoDQURjIMETIRPtcrpTlwuevby4daqVThQ27VHTK0mY/CtqR4ZLU1b38OcdOHC/hywpZb2UoxQjg0aMvD0DArIISJUEhBVLqqPZhvlbhSN7FPhiYE8sny+eXh4+XTqcyq3h4bwzxNOHuyOWZSUejKmNmfUOONxSyeDPYck8HGFVtKiPj6agJGxxKhGxqkZkoKj1N7LQYDB476I9Nm7K9j2OWFuF4qr6MKKYV3mJcZ91tKp14mjA4R2pPD5ZmHwyt3Pus7qmHTqe0+ilNG9yrXdU7WWbtdIpwdi3hmfmmLpqhfNQy4kTGWWEu1saVl4M2UjChQac5DaZ1RNz9BJpHUUo+Zg/PTYraQZkDqSgYd3LkUQgqy3QpoCpqCAgidw/OybMGBqYUn6SyJZ2lLJpPDZgyVSXc2uJcm1SDUMhhaepZg5+L4aMbVVAU8R4LjNyhoWTKmEyyy2xqyak6wZmYn38ESfudD9zUKkhhz7H3HLBghdDHRs4uQ1kRiRZxg1nFm0cfbenoVqMeDsSVUjKLwwObzMe0fWc1NmrE9Ndl5KkSZmaEezaBgo7QdmKYXltTXSucKVrdmbs5e3DVu7akIVxhrvrIwKkiO+xLK+2F24YzxmYH2z5Xpr6+aw80R9j7TE7OfZRMs9w3PW3Tcj3b6R5jtTpfvz0gpqa2MG8u4zNs9yXFQOoMJoIH0fDu1lmnXtDe9nXYyOwW6LO/q2cMec0zTU6mOXqq0uq+abNelx9jLOvTivKYcKzpMmlBUUsS+O+NIimo3xVFQljjtSJCJixoX75BlunqdTJ7ayBx+MMb7Wz31fHJjvlhYzsYw2kc6jc495c6wabcZTOhGEsIMMcNqaaFQfooFI4W4nNYlY97j4dGxxGa0y5MLNA4y0q0aTjzEhebmJwZYmjRvwQImFjR86YE8y+U2hgqmWYQIkCN7xd2axjGeDZw0jfZZWH7GehpTPpDEre7wOcsCRZi+VIA0d+e9eZ70pDeVlLaRviYEDSeYx2DsbLUYN/Y3LFzFch7HsbQTh7nuDFBtSAZJwsi56ryeUOkdmEyycJ9TwbbGTcluSCMjc2CZqBSY68r3PV4+J4v0eaR9tMSMjBiEKN4dTkODRsWgucBgWpI9BiBMtgktLm091VyoUMRhjYu+A4wOMtET0iZjVHExoaQhHhxiNyZHqqjjBkZnQzPV5mGpsBwrpdjqrFzY0JaBwaTjllyZnp7TWtB7uzdVyou08NJLMPIpBwjIsqlgiWKirnA2FBLlOpqZITC6rqcxkjAv013l8Hp3cx6MaX2NtUqlWtfM+JlGnqNJ0oy44b8KOG3Z3eHT3gOKpYoYGXB0MDWQC3GV6l8xgyYBZDNDsd9jvL1hOo7FuzBXqQ2eDOjCPBuJfp4EDUoG64uYAxgXNCgl+DNoFCGxIGNxbqaHIJRMBalAoHXeZGfQ5EJ8SM1OcEuxMNCuoVGJiiEORsjg0JBcwDkfMhmOHXDEaEZE9Oz0J6mzS06dpw8MRweI5ufe5NvLlbib5O7wzhTs6d/RUtMtPKi06iMg7aXgy1VZjBmJGWtzIyKQLDkQiWpM4Rmd5kQ2NDY7kjEqSAzMUPVItDJ9luTqYnZYlDsRVpLIYs4giYjlhkwbl+UwX6BkLNsioXKBQyVksRGYxfoagYiYqYg5qUd31k8zuy6OG22mnl6N+DmMUfZcmUo55LjFcR9CgaD1HgKDhtGBybbDmLGYxMmmT8ifYeRLBCNtg2gc8ZmWV6ChRBMeqwGy+Xsvy+FP2ZZmXyu3MHh9XvBhmIenDDDamE8S/ZU6+SvLtt6+GzeXlpb7VPXnby+3sOqD09kuS4yNPsqPZPD8Msu7MMjE6hqyhS5xI6G3LDXrwYyXAkoqI6CXC0qNIzUInEtzMnroY9WMzEgbnBrVhtzcH2XPJnQtNHBmU4CxBTJli5jmcFTJGkZ4I5DNxFSUDEMTCpAYgbElguAmj0KLOMzdaoW7ZJjFaKUxhiZ4NVGHK3SXQ6KFMlgoLocYIoch6fWTxHTIwt1Bh6ZU8PjuwyO7nxOeobjy9ijoeryOEMtLHYCyyDyrGFDDCcgiSityu+JMLImZYAw9apdFigxgcGAllZjiJkQUirA1Ua3kb3ChPv15MGooKlDttiZ9ehkNW4p2lAiWJbRNVIbczDPnGm4bm5YuTLqYQoHIYHuS49hzgP4H+Y9z8z7dTgyEmSNz7WI0fWf0if1f1ZPa/sLMHyIkYgcmVpLIUC+86nIjs5iwgTJQNRpCIUO58iyTfmk1Fl6n1c2c0UYKKckNVMYgYNiZJQiTsj0csUGFQnMJQOEiYgaCXICLI/aFynzCeqyAGNQUJyAmki4LNynYFEy5HdvyzNmg7CyA5CeYaPovt+0lSpy5G9SHH9WMOls1e1WLRTxCYwXHEocJS6TEKSzTgpFnbqXmDGGMm5CgbenjwWjBHBKPrOSzg8T6TI5wQkRmjQhsP095mU1TD1PHQehg6PRVXLU94mmTPdNMyjrHn+zhmplpt5R8FOxiSWUKUVUVKOltI2cTLXqj64R8PMLKVI3N9C21FfLy8MJ7MVwW9oPJYTmJKeO3AdbLbTDeubq24KdiolSetlij7FvZTRR2d2FcnbE7593YX4iUpG1C5FKokVRVJOWIcEngbHQPAbEyd7O7Ps6DyPWbJuj2UjyPE86aaNGmE20XIqRbLEXNZPMJttw65k6cOtzbpZ6OZHCVKSobcvqm3E7JlT3no/ncd3Z3k5dKeb7UYtUVS1nbPhhuXSSVB5pkqJpPSrw0ranSdJoRhD0NpEDpsPO/Qpe2TEFUjBRSfQ6fkOmHwQRjGUTswB0fccI7SQIMYdobdhwm8MGSEInYMECOrmlJ3efJRuaUprpjp2TxiGnZw2rgCc2dh5HBkrlkIPVgR5QeQIQ6gLJSlCkopKSW8Kk8HSOpRMKdQhYWQMDRQ2uCRwOboCGVLuZ0KwOhhHLLMvsaoCZQxZKUooDaEiHB3hakp2SRwk5nEToKNSIo5S0UVKU6401lqXdmjFIRumaaCjmR0/n4YMPTpuJRyop38djMMypSh4eVmKST1FNvUOVSxicyqpNpUhzLSylxVmMo6WOJTETvo51W2itNRoo8SsIheTKBCKs4IY6jHkEik8IftRPH3UJPfAQQYMESFpKZFIIQggUpBoiBEIDwUnOOrWWISjk4enmUpK07K44cYYkzRYnRT0A0RyENCRGCIkBEpKWG3rDw4tNNKiFBSlShVUCmxqWtpTgtKKQyKiefSpT09J2OHQ6HYzbwpF5Skr6MDCkqVIppKCkQEYwRCRggyIDFBJ5h9h2OISHCPZSHo8CcQsoCiBK3BuucPXI2mDcWOgxw4PsJAkpIkGqCQ7c14FyNZtk9z4UdJ53IncYXRZThnw4dGgMJDQjAGEIhCIEGIkYigqKUKUkqVdPT1Ce8TpS+GE2y+ZfJlObKVvz7MGF92228qkf1VPLCPh3erRCG7ztz6mFwBA7djo6wOQIiSiqhlLB6TB9GUnsPEzglG30YeJ07Sn9pnNqmHwzS3TD4enLn3T4ShQ88TTqecJMuFQuKSqenniZUKbVpRHYcTnOhq8xgfbMiSCJA6EIjWNSsRbmaQtM5LMVRJZ1BxnHKHJGGATWoxQjQmbEQsxOnOo2RepiELxuNmw6PAtH09KcB0hQ7sQitjwWGMdMtPA6MtmCCeTRUGRqXVMXDcYioOihoOlGROFjejg+IZQQ+DpkhCPXxOxScw8CEIQYQ2ZwKcDgaNIifcSuhA0aEw1S/j+bMnIilJuUQLO4F9l1ADAYfMftYKfiGlz7q+WB39jmMUTpfeHhS6mXWVtKMyGAgKRQRBRENoHUzWpTA+5kuJ9qcPdw0yfnZaR+LhywaWfBmnvHiz5lEVtmA4ashwp8MCw/U4PpgwQ307UxKkUnotpilL/Z8/npo1KS7C/IZYPEmJ6eG++bNuZmLTBUuek1OskRaDDnjI0kSPq5En6hE+x9iwe+baDESZUoL7kMs9+htzj45jHGlCox7HU74tidsK5esvePpI8/BPR/B2cROXsVfvWNKLVDNM/Lw+h3fDsJ+ExjoGOdINWnwVv0+x9SIx/mH9Ehhj2Pg+rRD8Pw/RG22lohUUKSIyyMPqJPziGCH4HJIUYZElgow/WImHYFKITVXUG3BUdFhkKMA0twZfiHzNFkPXYs4cFL0lrtwtg0yT+XGtesLnLc5qDa2WcNHijfTjgytq7FN0m4aI2iSmhJIidDiYWh0tMmhOmtfqcd7D4antbcKeE9kRiCJJOCBBELBk/VkJvp2/DWOHhgjOEPJEgkEEo537wv4yV6UkEdQwSaE9hQkwMOLRQcAU2KVQK+HCiC0j70xhg/pcSwsnGYgDOlULqJEzivowOVYsTYwaJB6G/F6LBdLOaciAsaAeRCm0nlj6cEl1KsR+BzzIIJ6UwE2WJs9l4dPCRRgjLjlTfSTliFOuo7wlKpU6FzLTysxFKrByKcP5YdWvw+HYdzCuyWnj38P37NYqsQllHZEuSnNU6g9lPg5ZfymqYjERM6YdzCWAT99GQNcWmQ5eKxZOYjWUp8PNkPSlxlH5CgHgHqfLSUZPWp2dVTw5P3+yzu4NvD9+NyqlN1imFJSLlxi46PDGYmEsxMIHEtExirNs2JkanpNLOpv3gXvMypAtoDwSVkQX4WbqnBxMGJlkeQOVRh6wXCrWDJGGEChQyGASMFEYfiKYdNYIikPvSmSU+o/kBoF6XCKIjFAk6/BvoMfQTHuMDn861OzDsBzeMVFoxgeHRkdNzUagVORxrWqW+hqfMP7z2MhAfUnsUMJakIWRKGUQKPouRbL+LRbC6GIZ9DJYhifOzRrBqyjBg+mGI4VyqiktUjTlpuYy0oTKUWcP3226K+xThDlRKKgr3b044s1vpRKjhZ0kgOffxLF6Og56Ravw4HCR0pJRwTg6Ci+I/fn8QnD107FJWbvs7QjdD7B7vDtjjukmQ5qNEmTydo4cZAtCCxCkFOmg24c+8+77yvDp06AuiYXu56op4OIThZMNnFE48/nHdvs7FdhKS/Bw4DKo8LNgdxa34V1lw4dNosGE34cFfswxXTpoZ+ycgcOcQRS1exVjLpgGJNGK21Cjk22SQHBo171NvKzvGMcvnseQRTn1W4YgGJ1J4FDE+1NKfOBp8v1P1O3CQwenju3WJJh3QHViEe429a60fxMJHJAgWPRfB8Huj6UOqSfSQKbbIUHsXUPgRzPuwg4D9zpwQ/u24bvehspEXhhT+j6akTCny+5pYelhiFkUGE58yKoTHIFHbMqlExockzl3cOVJt+Silp06w39FyLhhJRt2Zs8zL7qbkcCh9jhEvBRzOzmVE2yTr705ZTktHbGxUJblw/I4MyOkjc+KEhhVTHNmZo2CcSQeD5DYBAIhHoysvx5adRqYdMtOp5lpJbM3K6cKaPd7ZWbKQV+RpJo84EhIRkMzbz8DPJ5nmbET5eXROB00p85Q5SzqfZ0+rSUtQ4Fg/b8z5H4rx0ANeG7RAiwEgchFyERMmY1a5/yWWUoPWX7KVWEjIjnS5ikSIIgwnagGfm6PBKYfmiGBuHeluxmacHuswcScKoqJwt+zbp9nD76lrUg55hoxgmGCgtdTA4hhoJK6dkZkZcTaK5+TTMGTL7vqcuVO5xCkgQ9eHHAYEytKRWmzfIb9BHdBvOGOTsw/BVKt07FtzJ2KNtMD9VZcLai1uFbPyluDEP2P2a/Z/xVbTylKcVlSKpEqbmgEdT5QfYCoGxGFrsTb0i0VFR14n1qusEVCFCFrKLNKEPQ7hYPUciQd2GzL9XTu6U005Wd2W2jlw5fq8P8P2Pqe5ux8xkDN6seoHHzaAvmOOSCB1NBgcspSWeofaph7pg4fLU+VMmfuT6q7qLUauScuZy140ZmVDmPXrB9XEffxhOgwu+CgxxFHYRvYK9iblj7wJq4SdYG1TJp/DDE/gqSd4Tc7YbcP5v5uSn9H+v8AoED6yKM1HMCuJ5OCR9T5mQAf7u8mXDL2IXPJL1WB8HuOaiF6nUp7kRC+PYmZGZ7HYiSCjlg7ONv99OzP+BuiJ+B9AuG34EwexsQgOw1WTJRT6ZsspR95Spc/BbT3YYdmJJhTT4ZcNmNPqmF1w+iMKcKKMJqMrTEi6CsrfJSaYj+bJM0vWn9CbzLOuET+op/WfowtheF22tZl+hSpP6K2hiVn1OHty4ge3gBVoB9tHruY8uxiybeXbo7EkyQz1i45/LqkSAqkeEjzOtr/Y9j8S0m80x/F06kAY5AvUZ4siwf4v5P+zREUWotTosLg3TV+iWdI13udG6W/YzqizJcZhrDHAdYtoGnNLQWoll/a/7qy55Y4f6PB/Z/m8vdyy07tsu4YKHZcp6Wy0qf951Pck7Pw6d3wYbe8hdTAkRSFNLTADR6D5DTODqZKJSQGJsMFEsShmTLAlJQLllInifcgSKnUiZGA61NS5pELn4nICsZFTYkGxqkcGh1Cy8DB+p/A/5Ufzfr/q7P9ZPJ5H6n0KjEi4fkUUqfWWor0t2ZeX0SyjKqqVlt+UfafeW1R04V2qlP1UaSjDH6lRy9nDZ+p+rRFep6l0kokUqHqFSoqHuZdGvmewtyXkfxgfmNAO3IPP3HH9dORvs7BSGpU2naS20tupLtYl5jzbjMzZFFOH05AcSWKOl+Op8/r4seznqzbNjDnt2NFFvuuJREi624vQSmMGVLwPUCNf+m5rgjPXXP8xmyPzerP8ECwAK4AZH+Jf2AAv7F+v5/p+lv1F+enPHp09oj+z+yTncCtkRqewEv2IHoTkOenvZ3t6ldbbc20CMf5ZO1iZMaWxDDrwSxKqNjU2EpZ97X7+OfZu/p3NPS9H6/r+H6XvpbOHY32wxdwZqss7U2q+ZOlHGuQrBGEEVMllrgX7yjdz7s3dFPPltthTX35mt4tnX5gRVUw3qA90f+3vx91yH5fVkbi/YBE7GKizWtU1nqyO0C7zorBZZHr2ruW9ly4ID4ayxg3da0CbSrVjE3lrNsfn+v56V+9axZ+fG12PX4nMsVbMbNq2/L46uIAZbaOOAEP/vbb3psf0Nxz8zY139/YAPjbyLY/r0fHr+OIF69fvAyMLCwtbW799o9HqB4nT0X2hmYFGt38OpqzNIuOd3HS6WngqUWOZYsY3kVrGXG+IPpgfdJEzhRCH/xdyRThQkFJsucUA==''' - - class _objdict(dict): - def __getattr__(self, name): - if name in self: - return self[name] - else: - raise AttributeError('No such attribute: ' + name) - - def __setattr__(self, name, value): - if name in self: - self[name] = value - else: - raise AttributeError('No such attribute: ' + name) - - class _rednderopts(_objdict): - @property - def cap_line(self): - return self['cap_line'] * self.scaley - - @property - def bottom_line(self): - return self['bottom_line'] * self.scaley - - @property - def base_line(self): - return self['base_line'] * self.scaley - - class _HersheyRenderIterator(object): - def __init__(self, glyphs, text=None): - self.__text = text or '' - if not isinstance(glyphs, dict): - raise TypeError('glyphs parameter has to be a dictionary') - self.__glyphs = glyphs - - def text_glyphs(self, text=None): - text = text or self.__text or '' - for current_char in text: - if current_char in self.__glyphs: - the_glyph = self.__glyphs[current_char] - if isinstance(the_glyph, _HersheyGlyph): - yield the_glyph - - def text_strokes(self, text=None, xofs=0, yofs=0, scalex=1, scaley=1, spacing=0, **kwargs): - for glyph in self.text_glyphs(text=text): - for stroke in glyph.strokes: - yield [(xofs + (x - glyph.left_offset) * scalex, yofs + y * scaley) for x, y in stroke] - xofs += spacing + scalex * glyph.char_width - - def __init__(self, load_from_data_iterator='', load_default_font=None): - self.__glyphs = {} - self.__default_font_names_list = None - self.__font_params = self._rednderopts({'xofs': 0, 'yofs': 0, 'scalex': 1, 'scaley': 1, 'spacing': 0, 'cap_line': -12, 'base_line': 9, 'bottom_line': 16}) - if load_default_font is not None: - self.load_default_font(load_default_font) - else: - self.read_from_string_lines(data_iterator=load_from_data_iterator) - - @property - def render_options(self): - '''xofs=0, yofs=0, -scalex=1, scaley=1, -spacing=0, -cap_line=-12, base_line= 9, bottom_line= 16''' - return self.__font_params - - @property - def all_glyphs(self): - '''Get all Glyphs stored for currently loaded font. ={} if no font loaded''' - return dict(self.__glyphs) - - @render_options.setter - def render_options(self, newdim): - '''xofs=0, yofs=0, -scalex=1, scaley=1, -spacing=0, -cap_line=-12, base_line= 9, bottom_line= 16''' - if newdim.issubset(self.render_options.keys()): - self.render_options.update(newdim) - else: - raise AttributeError('Unable to set unknown parameters') - - @property - def default_font_names(self): - '''Get the list of built-in fonts''' - if not self.__default_font_names_list: - with BytesIO(self.__get_compressed_font_bytes()) as compressed_file_stream: - with tarfile.open(fileobj=compressed_file_stream, mode='r', ) as ftar: - self.__default_font_names_list = list(map(lambda tar_member: tar_member.name, ftar.getmembers())) - del ftar - del compressed_file_stream - return list(self.__default_font_names_list) - - def __get_compressed_font_bytes(self): - for enc in ('64', '85', '32', '16'): - if hasattr(self, '_HersheyFonts__compressed_fonts_base' + enc): - if hasattr(base64, 'b' + enc + 'decode'): - decoded = getattr(base64, 'b' + enc + 'decode')(getattr(self, '_HersheyFonts__compressed_fonts_base' + enc)) - return bytes(decoded) - raise NotImplementedError('base' + enc + ' encoding not supported on this platform.') - - def normalize_rendering(self, factor=1.0): - '''Set rendering options to output text lines in upright direction, size set to "factor"''' - scale_factor = float(factor) / (self.render_options['bottom_line'] - self.render_options['cap_line']) - self.render_options.scaley = -scale_factor - self.render_options.scalex = scale_factor - self.render_options.yofs = self.render_options['bottom_line'] * scale_factor - self.render_options.xofs = 0 - - def load_default_font(self, default_font_name=''): - '''load built-in font by name. If default_font_name not specified, selects the predefined default font. The routine is returning the name of the loaded font.''' - if not default_font_name: - default_font_name = self.default_font_names[0] - if default_font_name in self.default_font_names: - with BytesIO(self.__get_compressed_font_bytes()) as compressed_file_stream: - with tarfile.open(fileobj=compressed_file_stream, mode='r', ) as ftar: - tarmember = ftar.extractfile(default_font_name) - self.read_from_string_lines(tarmember) - return default_font_name - raise ValueError('"{0}" font not found.'.format(default_font_name)) - - def load_font_file(self, file_name): - '''load font from external file''' - with open(file_name, 'r') as fin: - self.read_from_string_lines(fin) - - def read_from_string_lines(self, data_iterator=None, first_glyph_ascii_code=32, use_charcode=False, merge_existing=False): - '''Read font from iterable list of strings -Parameters: - - data_iterator : string list or empty to clear current font data - - use_charcode : if True use the font embedded charcode parameter for glyph storage - - first_glyph_ascii_code : if use_charcode is False, use this ASCII code for the first character in font line - - merge_existing : if True merge the glyphs from data_iterator to the current font - ''' - glyph_ascii_code = first_glyph_ascii_code - cap = [] - base = [] - bottom = [] - cap_line = None - base_line = None - bottom_line = None - aglyph = None - if not merge_existing: - self.__glyphs = {} - if data_iterator: - for line in data_iterator or '': - if isinstance(line, str) and hasattr(line, 'decode'): - line = line.decode() - elif isinstance(line, bytes) and hasattr(line, 'decode'): - line = line.decode('utf-8') - if line[0] == '#': - extraparams = json.loads(line[1:]) - if 'define_cap_line' in extraparams: - cap_line = extraparams['define_cap_line'] - if 'define_base_line' in extraparams: - base_line = extraparams['define_base_line'] - if 'define_bottom_line' in extraparams: - bottom_line = extraparams['define_bottom_line'] - if aglyph: - aglyph.parse_string_line(line) - else: - aglyph = _HersheyGlyph(data_line=line, default_base_line=base_line, default_bottom_line=bottom_line, default_cap_line=cap_line) - if line[0] != '#': - glyph_key = chr(aglyph.font_charcode if use_charcode else glyph_ascii_code) - self.__glyphs[glyph_key] = aglyph - cap.append(aglyph.cap_line) - base.append(aglyph.base_line) - bottom.append(aglyph.bottom_line) - aglyph = None - glyph_ascii_code += 1 - caps = statistics_multimode(cap) - bases = statistics_multimode(base) - bottoms = statistics_multimode(bottom) - self.render_options.cap_line = statistics_median(caps) if cap_line is None else cap_line - self.render_options.base_line = statistics_median(bases) if base_line is None else base_line - self.render_options.bottom_line = statistics_median(bottoms) if bottom_line is None else bottom_line - - def glyphs_for_text(self, text): - '''Return iterable list of glyphs for the given text''' - return self._HersheyRenderIterator(self.__glyphs).text_glyphs(text=text) - - def strokes_for_text(self, text): - '''Return iterable list of continuous strokes (polygons) for all characters with pre calculated offsets for the given text. -Strokes (polygons) are list of (x,y) coordinates. - ''' - return self._HersheyRenderIterator(self.__glyphs).text_strokes(text=text, **self.__font_params) - - def lines_for_text(self, text): - '''Return iterable list of individual lines for all characters with pre calculated offsets for the given text. -Lines are a list of ((x0,y0),(x1,y1)) coordinates. - ''' - return chain.from_iterable(zip(stroke[::], stroke[1::]) for stroke in self._HersheyRenderIterator(self.__glyphs).text_strokes(text=text, **self.__font_params)) - - -class _HersheyGlyph(object): - def __init__(self, data_line='', default_cap_line=None, default_base_line=None, default_bottom_line=None): - self.__capline = default_cap_line - self.__baseline = default_base_line - self.__bottomline = default_bottom_line - self.__charcode = -1 - self.__left_side = 0 - self.__right_side = 0 - self.__strokes = [] - self.__xmin = self.__xmax = self.__ymin = self.__ymax = 0 - self.parse_string_line(data_line=data_line) - - @property - def base_line(self): - '''Return the base line of the glyph. e.g. Horizontal leg of letter L. -The parameter might be in or outside of the bounding box for the glyph - ''' - return 9 if self.__baseline is None else self.__baseline - - @property - def cap_line(self): - '''Return the cap line of the glyph. e.g. Horizontal hat of letter T. -The parameter might be in or outside of the bounding box for the glyph - ''' - return -12 if self.__capline is None else self.__capline - - @property - def bottom_line(self): - '''Return the bottom line of the glyph. e.g. Lowest point of letter j. -The parameter might be in or outside of the bounding box for the glyph - ''' - return 16 if self.__bottomline is None else self.__bottomline - - @property - def font_charcode(self): - '''Get the Hershey charcode of this glyph.''' - return self.__charcode - - @property - def left_offset(self): - '''Get left side of the glyph. Can be different to bounding box.''' - return self.__left_side - - @property - def strokes(self): - '''Return iterable list of continuous strokes (polygons) for this glyph. -Strokes (polygons) are list of (x,y) coordinates. - ''' - return self.__strokes - - @property - def char_width(self): - '''Return the width of this glyph. May be different to bounding box.''' - return self.__right_side - self.__left_side - - @property - def draw_box(self): - '''Return the graphical bounding box for this Glyph in format ((xmin,ymin),(xmax,ymax))''' - return (self.__xmin, self.__ymin), (self.__xmax, self.__ymax) - - @property - def char_box(self): - '''Return the typographical bounding box for this Glyph in format ((xmin,ymin),(xmax,ymax)). -Can be different to bounding box. -See draw_box property for rendering bounding box - ''' - return (self.__left_side, self.__bottomline), (self.__right_side, self.__capline) - - def __char2val(self, c): # data is stored as signed bytes relative to ASCII R - return ord(c) - ord('R') - - @property - def lines(self): - '''Return iterable list of individual lines for this Glyph. -Lines are a list of ((x0,y0),(x1,y1)) coordinates. - ''' - return chain.from_iterable(zip(stroke[::], stroke[1::]) for stroke in self.__strokes) - - def parse_string_line(self, data_line): - """Interprets a line of Hershey font text """ - if data_line: - data_line = data_line.rstrip() - if data_line: - if data_line[0] == '#': - extraparams = json.loads(data_line[1:]) - if 'glyph_cap_line' in extraparams: - self.__capline = extraparams['glyph_cap_line'] - if 'glyph_base_line' in extraparams: - self.__baseline = extraparams['glyph_base_line'] - if 'glyph_bottom_line' in extraparams: - self.__bottomline = extraparams['glyph_bottom_line'] - elif len(data_line) > 9: - strokes = [] - xmin = xmax = ymin = ymax = None - # individual strokes are stored separated by +R - # starting at col 11 - for s in split(data_line[10:], ' R'): - if len(s): - stroke = list(zip(map(self.__char2val, s[::2]), map(self.__char2val, s[1::2]))) - xmin = min(stroke + ([xmin] if xmin else []), key=lambda t: t[0]) - ymin = min(stroke + ([ymin] if ymin else []), key=lambda t: t[1]) - xmax = max(stroke + ([xmax] if xmax else []), key=lambda t: t[0]) - ymax = max(stroke + ([ymax] if ymax else []), key=lambda t: t[1]) - strokes.append(stroke) - self.__charcode = int(data_line[0:5]) - self.__left_side = self.__char2val(data_line[8]) - self.__right_side = self.__char2val(data_line[9]) - self.__strokes = strokes - self.__xmin, self.__ymin, self.__xmax, self.__ymax = (xmin[0], ymin[1], xmax[0], ymax[1]) if strokes else (0, 0, 0, 0) - return True - return False - - -def main(): - thefont = HersheyFonts() - main_script(thefont) - main_gui(thefont) - - -def main_script(thefont=HersheyFonts()): - print('Built in fonts:') - default_font_names = sorted(thefont.default_font_names) - for fontname1, fontname2 in zip_longest(default_font_names[::2], default_font_names[1::2]): - fontname2 = '' if fontname2 is None else '- "' + fontname2 + '"' - fontname1 = '' if fontname1 is None else '"' + fontname1 + '"' - print(' - {0:<25} {1}'.format(fontname1, fontname2)) - print('Default font: "{0}"'.format(thefont.load_default_font())) - print('') - print('Rendering options:') - for optname, defval in thefont.render_options.items(): - print(' render_options.{0} = {1}'.format(optname, defval)) - - -def main_gui(thefont=HersheyFonts()): - import turtle - thefont.load_default_font() - thefont.normalize_rendering(30) - thefont.render_options.xofs = -367 - turtle.mode('logo') - turtle.tracer(2, delay=3) - for coord in range(4): - turtle.forward(200) - if coord < 2: - turtle.stamp() - turtle.back(200) - turtle.right(90) - turtle.color('blue') - lineslist = thefont.lines_for_text('Pack my box with five dozen liquor jugs.') - for pt1, pt2 in lineslist: - turtle.penup() - turtle.goto(pt1) - turtle.setheading(turtle.towards(pt2)) - turtle.pendown() - turtle.goto(pt2) - turtle.penup() - turtle.color('red') - turtle.goto(0, 100) - turtle.setheading(180) - turtle.update() - turtle.exitonclick() - - -if __name__ == '__main__': - main() diff --git a/plotter-app/venv/lib/python3.8/site-packages/HersheyFonts/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/HersheyFonts/__init__.py deleted file mode 100644 index 364c2b8..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/HersheyFonts/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .HersheyFonts import HersheyFonts - -__version__ = "2.1.0" \ No newline at end of file diff --git a/plotter-app/venv/lib/python3.8/site-packages/HersheyFonts/__main__.py b/plotter-app/venv/lib/python3.8/site-packages/HersheyFonts/__main__.py deleted file mode 100644 index 48539e2..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/HersheyFonts/__main__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .HersheyFonts import main - -if __name__ == "__main__": - print('Running demo') - main() diff --git a/plotter-app/venv/lib/python3.8/site-packages/HersheyFonts/__pycache__/HersheyFonts.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/HersheyFonts/__pycache__/HersheyFonts.cpython-38.pyc deleted file mode 100644 index b82bcd6..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/HersheyFonts/__pycache__/HersheyFonts.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/HersheyFonts/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/HersheyFonts/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index b02c170..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/HersheyFonts/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/HersheyFonts/__pycache__/__main__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/HersheyFonts/__pycache__/__main__.cpython-38.pyc deleted file mode 100644 index 4df65d9..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/HersheyFonts/__pycache__/__main__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/INSTALLER b/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/LICENSE b/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/LICENSE deleted file mode 100644 index 7605826..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020 apshu - -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/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/METADATA b/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/METADATA deleted file mode 100644 index bd63137..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/METADATA +++ /dev/null @@ -1,107 +0,0 @@ -Metadata-Version: 2.1 -Name: Hershey-Fonts -Version: 2.1.0 -Summary: Vector font package with built-in fonts and font rendering iterators -Home-page: https://github.com/apshu/HersheyFonts -Author: Attila -Author-email: attila.kolinger@gmail.com -License: MIT License -Platform: all -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Topic :: Text Processing :: Fonts -Classifier: Topic :: Multimedia :: Graphics -Classifier: Topic :: Scientific/Engineering :: Visualization -Classifier: Topic :: Scientific/Engineering -Classifier: Intended Audience :: Developers -Classifier: Development Status :: 5 - Production/Stable -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: OS Independent -Requires-Python: >=2.7 -Description-Content-Type: text/markdown -License-File: LICENSE - -# Hershey Font -
Hershey font is a python library with built in fonts and low level line and stroke iterators. - -## What is Hershey Font? -Hershey font is a vector font, designed many years ago. -The main use today if for generating text for CNC engraving. - -[Read more on Wikipedia](https://en.wikipedia.org/wiki/Hershey_fonts) - -[Font samples](http://soft9000.com/HersheyShowcase/) -## Overview -Hershey font consists of _glyphs_, each _glyph_ is assigned to an ASCII value from 32 (`'space'`) until 32+number of _glyphs_.
Each _glyph_ consits of array of **strokes**.
A **stroke** is an array of zero or more continous lines (points of an openend or closed ploygon). -## The Python module -The Hershey-Font package is providing the `HersheyFonts` class.
-Great care was taken to be compatible with defulat installation of python 2.7 and python 3.
-The `HersheyFonts` instance is handling only one font at a time. If you need to use multiple type faces as the same time, you can load a new typeface anytime (even during rendering) or create another `HersheyFonts` instance. -### Installing -Sources available on [GitHub](https://github.com/apshu/HersheyFonts) -Installation is available through pip and pip3 -```ShellSession -#python 3 -pip3 install Hershey-Fonts - -#python 2.7 -pip install Hershey-Fonts -``` -### Demo -After successfully installing the Hershey-Font package, you can run the module for a simple demonstration. -```ShellSession -#python 3 -python3 -m HersheyFonts - -#python 2.7 -python -m HersheyFonts -``` - -### Built-in fonts -The python module 1.0.0 has **32 fonts included in the source code** as a compressed base64 encoded variable. -The module can also load default fonts or from file and iterable string lines. -When you make your own font and want to include in the package, please contact me. -You can get the list of built-in fonts by looking at -```Python -from HersheyFonts import HersheyFonts - -print(HersheyFonts().default_font_names) -``` -The order and elements of the list may totally vary with any package release. -### Loading a font -To access one of the built-in fonts, use the `.load_default_font()` method. To read custome fonts you can call the `.load_font_file(file_name)` or `.read_from_string_lines(stringlines)` methods. The constructor also gives opportunity to read built-in or external font. -```Python -from HersheyFonts import HersheyFonts -thefont = HersheyFonts() -thefont.load_default_font('gothiceng') -thefont.load_default_font(thefont.default_font_names[0]) -thefont.load_default_font() #Returns the name of the loaded font -thefont.load_font_file('cyrillic.jhf') -thefont.read_from_string_lines(arrayofstrings) -``` -For more details and all options see doc comments in the sources. -### Renderig the loaded font -There are several options to convert a text to font data. The simplest way is to read endpoints of the lines returned by renderer method `.lines_for_text(sometext)`
-There are renderer methods returning list of glyps, list of strokes and list of line endpoints. -> The renderers in version 1.0.0 support only single line texts. -> Rendering is also affected by `.render_options` property.
-> There is a `.normalize_rendering()` method to automatically set the scaling and offsets for easy rendering. -```Python -# Minimalistic code for easy start -from HersheyFonts import HersheyFonts -def draw_line(x1, y1, x2, y2): -︙ -︙ - -thefont = HersheyFonts() -thefont.load_default_font() -thefont.normalize_rendering(100) -for (x1, y1), (x2, y2) in thefont.lines_for_text('Hello'): - draw_line(x1, y1 ,x2 ,y2) -``` -## The Hershey-Font API -The API is documented in the source code. -# Thank you -Big thanks to all people working on maintaining this old format for the modern age. - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/RECORD b/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/RECORD deleted file mode 100644 index 84f33df..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/RECORD +++ /dev/null @@ -1,19 +0,0 @@ -../../../bin/HersheyFonts_demo,sha256=3hzE7Jim3OoL7ydgxaEu-2TJ6g04DWSA34mbZwBhFJY,280 -../../../bin/HersheyFonts_demo,sha256=3hzE7Jim3OoL7ydgxaEu-2TJ6g04DWSA34mbZwBhFJY,280 -HersheyFonts/HersheyFonts.py,sha256=9LgE6vvinAMBI8jK9RBXnFQSKNaJrqccDaMlqKLDB3U,91220 -HersheyFonts/__init__.py,sha256=OLgzADyQPZNjqDXDZ-nt7eyPPo59KyNL7GH3thhfPag,63 -HersheyFonts/__main__.py,sha256=6HPExSkNYZrdUvuM5rtnxYObi_l4WerM0Lk0how55DA,101 -HersheyFonts/__pycache__/HersheyFonts.cpython-38.pyc,, -HersheyFonts/__pycache__/__init__.cpython-38.pyc,, -HersheyFonts/__pycache__/__main__.cpython-38.pyc,, -Hershey_Fonts-2.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -Hershey_Fonts-2.1.0.dist-info/LICENSE,sha256=RmGDhEj5lMwFXf30b9-xgsUuDQSbHXHK7iEP7MGwQaM,1062 -Hershey_Fonts-2.1.0.dist-info/METADATA,sha256=lfCFd2Mfw_gsd_86_dP4rhidduYOuk2CUxPStCsa2zc,4572 -Hershey_Fonts-2.1.0.dist-info/RECORD,, -Hershey_Fonts-2.1.0.dist-info/WHEEL,sha256=WzZ8cwjh8l0jtULNjYq1Hpr-WCqCRgPr--TX4P5I1Wo,110 -Hershey_Fonts-2.1.0.dist-info/entry_points.txt,sha256=szi9OSlDeM-R5Kwbnl3gGj0I8QOQfSVG98Mtt4yP3So,143 -Hershey_Fonts-2.1.0.dist-info/top_level.txt,sha256=V14Zt7CKG2QYI3kBLPtDnJo96eUx7-Xf2eJiOnc0Urc,19 -tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -tests/__pycache__/__init__.cpython-38.pyc,, -tests/__pycache__/test_HersheyFonts.cpython-38.pyc,, -tests/test_HersheyFonts.py,sha256=fwfTUsp2sPjm_7v_m73XR97biVrkW3ILjdODebbCpQw,4018 diff --git a/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/WHEEL b/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/WHEEL deleted file mode 100644 index b733a60..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.37.0) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/entry_points.txt b/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/entry_points.txt deleted file mode 100644 index cd3d137..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/entry_points.txt +++ /dev/null @@ -1,6 +0,0 @@ -[console_scripts] -HersheyFonts_demo = HersheyFonts.HersheyFonts:main_script - -[gui_scripts] -HersheyFonts_demo = HersheyFonts.HersheyFonts:main - diff --git a/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/top_level.txt b/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/top_level.txt deleted file mode 100644 index 2a81387..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/Hershey_Fonts-2.1.0.dist-info/top_level.txt +++ /dev/null @@ -1,2 +0,0 @@ -HersheyFonts -tests diff --git a/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/INSTALLER b/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/LICENSE.md b/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/LICENSE.md deleted file mode 100644 index 6249d60..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/LICENSE.md +++ /dev/null @@ -1,30 +0,0 @@ -BSD 3-Clause License - -Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later) -Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) -Copyright 2004 Manfred Stienstra (the original version) - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/METADATA b/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/METADATA deleted file mode 100644 index 233bc55..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/METADATA +++ /dev/null @@ -1,146 +0,0 @@ -Metadata-Version: 2.1 -Name: Markdown -Version: 3.7 -Summary: Python implementation of John Gruber's Markdown. -Author: Manfred Stienstra, Yuri Takhteyev -Author-email: Waylan limberg -Maintainer: Isaac Muse -Maintainer-email: Waylan Limberg -License: BSD 3-Clause License - - Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later) - Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) - Copyright 2004 Manfred Stienstra (the original version) - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -Project-URL: Homepage, https://Python-Markdown.github.io/ -Project-URL: Documentation, https://Python-Markdown.github.io/ -Project-URL: Repository, https://github.com/Python-Markdown/markdown -Project-URL: Issue Tracker, https://github.com/Python-Markdown/markdown/issues -Project-URL: Changelog, https://python-markdown.github.io/changelog/ -Keywords: markdown,markdown-parser,python-markdown,markdown-to-html -Classifier: Development Status :: 5 - Production/Stable -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.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 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Communications :: Email :: Filters -Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries -Classifier: Topic :: Internet :: WWW/HTTP :: Site Management -Classifier: Topic :: Software Development :: Documentation -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: Text Processing :: Filters -Classifier: Topic :: Text Processing :: Markup :: HTML -Classifier: Topic :: Text Processing :: Markup :: Markdown -Requires-Python: >=3.8 -Description-Content-Type: text/markdown -License-File: LICENSE.md -Requires-Dist: importlib-metadata >=4.4 ; python_version < "3.10" -Provides-Extra: docs -Requires-Dist: mkdocs >=1.5 ; extra == 'docs' -Requires-Dist: mkdocs-nature >=0.6 ; extra == 'docs' -Requires-Dist: mdx-gh-links >=0.2 ; extra == 'docs' -Requires-Dist: mkdocstrings[python] ; extra == 'docs' -Requires-Dist: mkdocs-gen-files ; extra == 'docs' -Requires-Dist: mkdocs-section-index ; extra == 'docs' -Requires-Dist: mkdocs-literate-nav ; extra == 'docs' -Provides-Extra: testing -Requires-Dist: coverage ; extra == 'testing' -Requires-Dist: pyyaml ; extra == 'testing' - -[Python-Markdown][] -=================== - -[![Build Status][build-button]][build] -[![Coverage Status][codecov-button]][codecov] -[![Latest Version][mdversion-button]][md-pypi] -[![Python Versions][pyversion-button]][md-pypi] -[![BSD License][bsdlicense-button]][bsdlicense] -[![Code of Conduct][codeofconduct-button]][Code of Conduct] - -[build-button]: https://github.com/Python-Markdown/markdown/workflows/CI/badge.svg?event=push -[build]: https://github.com/Python-Markdown/markdown/actions?query=workflow%3ACI+event%3Apush -[codecov-button]: https://codecov.io/gh/Python-Markdown/markdown/branch/master/graph/badge.svg -[codecov]: https://codecov.io/gh/Python-Markdown/markdown -[mdversion-button]: https://img.shields.io/pypi/v/Markdown.svg -[md-pypi]: https://pypi.org/project/Markdown/ -[pyversion-button]: https://img.shields.io/pypi/pyversions/Markdown.svg -[bsdlicense-button]: https://img.shields.io/badge/license-BSD-yellow.svg -[bsdlicense]: https://opensource.org/licenses/BSD-3-Clause -[codeofconduct-button]: https://img.shields.io/badge/code%20of%20conduct-contributor%20covenant-green.svg?style=flat-square -[Code of Conduct]: https://github.com/Python-Markdown/markdown/blob/master/CODE_OF_CONDUCT.md - -This is a Python implementation of John Gruber's [Markdown][]. -It is almost completely compliant with the reference implementation, -though there are a few known issues. See [Features][] for information -on what exactly is supported and what is not. Additional features are -supported by the [Available Extensions][]. - -[Python-Markdown]: https://Python-Markdown.github.io/ -[Markdown]: https://daringfireball.net/projects/markdown/ -[Features]: https://Python-Markdown.github.io#Features -[Available Extensions]: https://Python-Markdown.github.io/extensions - -Documentation -------------- - -```bash -pip install markdown -``` -```python -import markdown -html = markdown.markdown(your_text_string) -``` - -For more advanced [installation] and [usage] documentation, see the `docs/` directory -of the distribution or the project website at . - -[installation]: https://python-markdown.github.io/install/ -[usage]: https://python-markdown.github.io/reference/ - -See the change log at . - -Support -------- - -You may report bugs, ask for help, and discuss various other issues on the [bug tracker][]. - -[bug tracker]: https://github.com/Python-Markdown/markdown/issues - -Code of Conduct ---------------- - -Everyone interacting in the Python-Markdown project's code bases, issue trackers, -and mailing lists is expected to follow the [Code of Conduct]. diff --git a/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/RECORD b/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/RECORD deleted file mode 100644 index 5805db1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/RECORD +++ /dev/null @@ -1,74 +0,0 @@ -../../../bin/markdown_py,sha256=Bky1aNYXC4uWZmtihr2qWsEkZh-7xmjhQOBeFPe3Egs,270 -Markdown-3.7.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -Markdown-3.7.dist-info/LICENSE.md,sha256=e6TrbRCzKy0R3OE4ITQDUc27swuozMZ4Qdsv_Ybnmso,1650 -Markdown-3.7.dist-info/METADATA,sha256=nY8sewcY6R1akyROqkyO-Jk_eUDY8am_C4MkRP79sWA,7040 -Markdown-3.7.dist-info/RECORD,, -Markdown-3.7.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91 -Markdown-3.7.dist-info/entry_points.txt,sha256=lMEyiiA_ZZyfPCBlDviBl-SiU0cfoeuEKpwxw361sKQ,1102 -Markdown-3.7.dist-info/top_level.txt,sha256=IAxs8x618RXoH1uCqeLLxXsDefJvE_mIibr_M4sOlyk,9 -markdown/__init__.py,sha256=dfzwwdpG9L8QLEPBpLFPIHx_BN056aZXp9xZifTxYIU,1777 -markdown/__main__.py,sha256=innFBxRqwPBNxG1zhKktJji4bnRKtVyYYd30ID13Tcw,5859 -markdown/__meta__.py,sha256=RhwfJ30zyGvJaJXLHwQdNH5jw69-5fVKu2p-CVaJz0U,1712 -markdown/__pycache__/__init__.cpython-38.pyc,, -markdown/__pycache__/__main__.cpython-38.pyc,, -markdown/__pycache__/__meta__.cpython-38.pyc,, -markdown/__pycache__/blockparser.cpython-38.pyc,, -markdown/__pycache__/blockprocessors.cpython-38.pyc,, -markdown/__pycache__/core.cpython-38.pyc,, -markdown/__pycache__/htmlparser.cpython-38.pyc,, -markdown/__pycache__/inlinepatterns.cpython-38.pyc,, -markdown/__pycache__/postprocessors.cpython-38.pyc,, -markdown/__pycache__/preprocessors.cpython-38.pyc,, -markdown/__pycache__/serializers.cpython-38.pyc,, -markdown/__pycache__/test_tools.cpython-38.pyc,, -markdown/__pycache__/treeprocessors.cpython-38.pyc,, -markdown/__pycache__/util.cpython-38.pyc,, -markdown/blockparser.py,sha256=j4CQImVpiq7g9pz8wCxvzT61X_T2iSAjXupHJk8P3eA,5728 -markdown/blockprocessors.py,sha256=koY5rq8DixzBCHcquvZJp6x2JYyBGjrwxMWNZhd6D2U,27013 -markdown/core.py,sha256=DyyzDsmd-KcuEp8ZWUKJAeUCt7B7G3J3NeqZqp3LphI,21335 -markdown/extensions/__init__.py,sha256=9z1khsdKCVrmrJ_2GfxtPAdjD3FyMe5vhC7wmM4O9m0,4822 -markdown/extensions/__pycache__/__init__.cpython-38.pyc,, -markdown/extensions/__pycache__/abbr.cpython-38.pyc,, -markdown/extensions/__pycache__/admonition.cpython-38.pyc,, -markdown/extensions/__pycache__/attr_list.cpython-38.pyc,, -markdown/extensions/__pycache__/codehilite.cpython-38.pyc,, -markdown/extensions/__pycache__/def_list.cpython-38.pyc,, -markdown/extensions/__pycache__/extra.cpython-38.pyc,, -markdown/extensions/__pycache__/fenced_code.cpython-38.pyc,, -markdown/extensions/__pycache__/footnotes.cpython-38.pyc,, -markdown/extensions/__pycache__/legacy_attrs.cpython-38.pyc,, -markdown/extensions/__pycache__/legacy_em.cpython-38.pyc,, -markdown/extensions/__pycache__/md_in_html.cpython-38.pyc,, -markdown/extensions/__pycache__/meta.cpython-38.pyc,, -markdown/extensions/__pycache__/nl2br.cpython-38.pyc,, -markdown/extensions/__pycache__/sane_lists.cpython-38.pyc,, -markdown/extensions/__pycache__/smarty.cpython-38.pyc,, -markdown/extensions/__pycache__/tables.cpython-38.pyc,, -markdown/extensions/__pycache__/toc.cpython-38.pyc,, -markdown/extensions/__pycache__/wikilinks.cpython-38.pyc,, -markdown/extensions/abbr.py,sha256=Gqt9TUtLWez2cbsy3SQk5152RZekops2fUJj01bfkfw,6903 -markdown/extensions/admonition.py,sha256=Hqcn3I8JG0i-OPWdoqI189TmlQRgH6bs5PmpCANyLlg,6547 -markdown/extensions/attr_list.py,sha256=t3PrgAr5Ebldnq3nJNbteBt79bN0ccXS5RemmQfUZ9g,7820 -markdown/extensions/codehilite.py,sha256=ChlmpM6S--j-UK7t82859UpYjm8EftdiLqmgDnknyes,13503 -markdown/extensions/def_list.py,sha256=J3NVa6CllfZPsboJCEycPyRhtjBHnOn8ET6omEvVlDo,4029 -markdown/extensions/extra.py,sha256=1vleT284kued4HQBtF83IjSumJVo0q3ng6MjTkVNfNQ,2163 -markdown/extensions/fenced_code.py,sha256=-fYSmRZ9DTYQ8HO9b_78i47kVyVu6mcVJlqVTMdzvo4,8300 -markdown/extensions/footnotes.py,sha256=bRFlmIBOKDI5efG1jZfDkMoV2osfqWip1rN1j2P-mMg,16710 -markdown/extensions/legacy_attrs.py,sha256=oWcyNrfP0F6zsBoBOaD5NiwrJyy4kCpgQLl12HA7JGU,2788 -markdown/extensions/legacy_em.py,sha256=-Z_w4PEGSS-Xg-2-BtGAnXwwy5g5GDgv2tngASnPgxg,1693 -markdown/extensions/md_in_html.py,sha256=y4HEWEnkvfih22fojcaJeAmjx1AtF8N-a_jb6IDFfts,16546 -markdown/extensions/meta.py,sha256=v_4Uq7nbcQ76V1YAvqVPiNLbRLIQHJsnfsk-tN70RmY,2600 -markdown/extensions/nl2br.py,sha256=9KKcrPs62c3ENNnmOJZs0rrXXqUtTCfd43j1_OPpmgU,1090 -markdown/extensions/sane_lists.py,sha256=ogAKcm7gEpcXV7fSTf8JZH5YdKAssPCEOUzdGM3C9Tw,2150 -markdown/extensions/smarty.py,sha256=yqT0OiE2AqYrqqZtcUFFmp2eJsQHomiKzgyG2JFb9rI,11048 -markdown/extensions/tables.py,sha256=oTDvGD1qp9xjVWPGYNgDBWe9NqsX5gS6UU5wUsQ1bC8,8741 -markdown/extensions/toc.py,sha256=PGg-EqbBubm3n0b633r8Xa9kc6JIdbo20HGAOZ6GEl8,18322 -markdown/extensions/wikilinks.py,sha256=j7D2sozica6sqXOUa_GuAXqIzxp-7Hi60bfXymiuma8,3285 -markdown/htmlparser.py,sha256=dEr6IE7i9b6Tc1gdCLZGeWw6g6-E-jK1Z4KPj8yGk8Q,14332 -markdown/inlinepatterns.py,sha256=7_HF5nTOyQag_CyBgU4wwmuI6aMjtadvGadyS9IP21w,38256 -markdown/postprocessors.py,sha256=eYi6eW0mGudmWpmsW45hduLwX66Zr8Bf44WyU9vKp-I,4807 -markdown/preprocessors.py,sha256=pq5NnHKkOSVQeIo-ajC-Yt44kvyMV97D04FBOQXctJM,3224 -markdown/serializers.py,sha256=YtAFYQoOdp_TAmYGow6nBo0eB6I-Sl4PTLdLDfQJHwQ,7174 -markdown/test_tools.py,sha256=MtN4cf3ZPDtb83wXLTol-3q3aIGRIkJ2zWr6fd-RgVE,8662 -markdown/treeprocessors.py,sha256=o4dnoZZsIeVV8qR45Njr8XgwKleWYDS5pv8dKQhJvv8,17651 -markdown/util.py,sha256=vJ1E0xjMzDAlTqLUSJWgdEvxdQfLXDEYUssOQMw9kPQ,13929 diff --git a/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/WHEEL b/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/WHEEL deleted file mode 100644 index 71360e0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (72.2.0) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/entry_points.txt b/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/entry_points.txt deleted file mode 100644 index be3bd8f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/entry_points.txt +++ /dev/null @@ -1,22 +0,0 @@ -[console_scripts] -markdown_py = markdown.__main__:run - -[markdown.extensions] -abbr = markdown.extensions.abbr:AbbrExtension -admonition = markdown.extensions.admonition:AdmonitionExtension -attr_list = markdown.extensions.attr_list:AttrListExtension -codehilite = markdown.extensions.codehilite:CodeHiliteExtension -def_list = markdown.extensions.def_list:DefListExtension -extra = markdown.extensions.extra:ExtraExtension -fenced_code = markdown.extensions.fenced_code:FencedCodeExtension -footnotes = markdown.extensions.footnotes:FootnoteExtension -legacy_attrs = markdown.extensions.legacy_attrs:LegacyAttrExtension -legacy_em = markdown.extensions.legacy_em:LegacyEmExtension -md_in_html = markdown.extensions.md_in_html:MarkdownInHtmlExtension -meta = markdown.extensions.meta:MetaExtension -nl2br = markdown.extensions.nl2br:Nl2BrExtension -sane_lists = markdown.extensions.sane_lists:SaneListExtension -smarty = markdown.extensions.smarty:SmartyExtension -tables = markdown.extensions.tables:TableExtension -toc = markdown.extensions.toc:TocExtension -wikilinks = markdown.extensions.wikilinks:WikiLinkExtension diff --git a/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/top_level.txt b/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/top_level.txt deleted file mode 100644 index 0918c97..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/Markdown-3.7.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -markdown diff --git a/plotter-app/venv/lib/python3.8/site-packages/MarkupSafe-2.1.5.dist-info/INSTALLER b/plotter-app/venv/lib/python3.8/site-packages/MarkupSafe-2.1.5.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/MarkupSafe-2.1.5.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/plotter-app/venv/lib/python3.8/site-packages/MarkupSafe-2.1.5.dist-info/LICENSE.rst b/plotter-app/venv/lib/python3.8/site-packages/MarkupSafe-2.1.5.dist-info/LICENSE.rst deleted file mode 100644 index 9d227a0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/MarkupSafe-2.1.5.dist-info/LICENSE.rst +++ /dev/null @@ -1,28 +0,0 @@ -Copyright 2010 Pallets - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/plotter-app/venv/lib/python3.8/site-packages/MarkupSafe-2.1.5.dist-info/METADATA b/plotter-app/venv/lib/python3.8/site-packages/MarkupSafe-2.1.5.dist-info/METADATA deleted file mode 100644 index dfe37d5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/MarkupSafe-2.1.5.dist-info/METADATA +++ /dev/null @@ -1,93 +0,0 @@ -Metadata-Version: 2.1 -Name: MarkupSafe -Version: 2.1.5 -Summary: Safely add untrusted strings to HTML/XML markup. -Home-page: https://palletsprojects.com/p/markupsafe/ -Maintainer: Pallets -Maintainer-email: contact@palletsprojects.com -License: BSD-3-Clause -Project-URL: Donate, https://palletsprojects.com/donate -Project-URL: Documentation, https://markupsafe.palletsprojects.com/ -Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/ -Project-URL: Source Code, https://github.com/pallets/markupsafe/ -Project-URL: Issue Tracker, https://github.com/pallets/markupsafe/issues/ -Project-URL: Chat, https://discord.gg/pallets -Classifier: Development Status :: 5 - Production/Stable -Classifier: Environment :: Web Environment -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content -Classifier: Topic :: Text Processing :: Markup :: HTML -Requires-Python: >=3.7 -Description-Content-Type: text/x-rst -License-File: LICENSE.rst - -MarkupSafe -========== - -MarkupSafe implements a text object that escapes characters so it is -safe to use in HTML and XML. Characters that have special meanings are -replaced so that they display as the actual characters. This mitigates -injection attacks, meaning untrusted user input can safely be displayed -on a page. - - -Installing ----------- - -Install and update using `pip`_: - -.. code-block:: text - - pip install -U MarkupSafe - -.. _pip: https://pip.pypa.io/en/stable/getting-started/ - - -Examples --------- - -.. code-block:: pycon - - >>> from markupsafe import Markup, escape - - >>> # escape replaces special characters and wraps in Markup - >>> escape("") - Markup('<script>alert(document.cookie);</script>') - - >>> # wrap in Markup to mark text "safe" and prevent escaping - >>> Markup("Hello") - Markup('hello') - - >>> escape(Markup("Hello")) - Markup('hello') - - >>> # Markup is a str subclass - >>> # methods and operators escape their arguments - >>> template = Markup("Hello {name}") - >>> template.format(name='"World"') - Markup('Hello "World"') - - -Donate ------- - -The Pallets organization develops and supports MarkupSafe and other -popular packages. In order to grow the community of contributors and -users, and allow the maintainers to devote more time to the projects, -`please donate today`_. - -.. _please donate today: https://palletsprojects.com/donate - - -Links ------ - -- Documentation: https://markupsafe.palletsprojects.com/ -- Changes: https://markupsafe.palletsprojects.com/changes/ -- PyPI Releases: https://pypi.org/project/MarkupSafe/ -- Source Code: https://github.com/pallets/markupsafe/ -- Issue Tracker: https://github.com/pallets/markupsafe/issues/ -- Chat: https://discord.gg/pallets diff --git a/plotter-app/venv/lib/python3.8/site-packages/MarkupSafe-2.1.5.dist-info/RECORD b/plotter-app/venv/lib/python3.8/site-packages/MarkupSafe-2.1.5.dist-info/RECORD deleted file mode 100644 index 090301b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/MarkupSafe-2.1.5.dist-info/RECORD +++ /dev/null @@ -1,14 +0,0 @@ -MarkupSafe-2.1.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -MarkupSafe-2.1.5.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 -MarkupSafe-2.1.5.dist-info/METADATA,sha256=2dRDPam6OZLfpX0wg1JN5P3u9arqACxVSfdGmsJU7o8,3003 -MarkupSafe-2.1.5.dist-info/RECORD,, -MarkupSafe-2.1.5.dist-info/WHEEL,sha256=zTDqV7OR0em6fvysya0bwC-51Mb7EQ0x5PBJySRF2iQ,148 -MarkupSafe-2.1.5.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11 -markupsafe/__init__.py,sha256=r7VOTjUq7EMQ4v3p4R1LoVOGJg6ysfYRncLr34laRBs,10958 -markupsafe/__pycache__/__init__.cpython-38.pyc,, -markupsafe/__pycache__/_native.cpython-38.pyc,, -markupsafe/_native.py,sha256=GR86Qvo_GcgKmKreA1WmYN9ud17OFwkww8E-fiW-57s,1713 -markupsafe/_speedups.c,sha256=X2XvQVtIdcK4Usz70BvkzoOfjTCmQlDkkjYSn-swE0g,7083 -markupsafe/_speedups.cpython-38-x86_64-linux-gnu.so,sha256=dR4WFvrcpISjeacwjKYJrqkz-0j0ZXF4oAHtTK0cKpw,45024 -markupsafe/_speedups.pyi,sha256=vfMCsOgbAXRNLUXkyuyonG8uEWKYU4PDqNuMaDELAYw,229 -markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/plotter-app/venv/lib/python3.8/site-packages/MarkupSafe-2.1.5.dist-info/WHEEL b/plotter-app/venv/lib/python3.8/site-packages/MarkupSafe-2.1.5.dist-info/WHEEL deleted file mode 100644 index c825336..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/MarkupSafe-2.1.5.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.42.0) -Root-Is-Purelib: false -Tag: cp38-cp38-manylinux_2_17_x86_64 -Tag: cp38-cp38-manylinux2014_x86_64 - diff --git a/plotter-app/venv/lib/python3.8/site-packages/MarkupSafe-2.1.5.dist-info/top_level.txt b/plotter-app/venv/lib/python3.8/site-packages/MarkupSafe-2.1.5.dist-info/top_level.txt deleted file mode 100644 index 75bf729..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/MarkupSafe-2.1.5.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -markupsafe diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/BdfFontFile.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/BdfFontFile.py deleted file mode 100644 index bc1416c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/BdfFontFile.py +++ /dev/null @@ -1,133 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# bitmap distribution font (bdf) file parser -# -# history: -# 1996-05-16 fl created (as bdf2pil) -# 1997-08-25 fl converted to FontFile driver -# 2001-05-25 fl removed bogus __init__ call -# 2002-11-20 fl robustification (from Kevin Cazabon, Dmitry Vasiliev) -# 2003-04-22 fl more robustification (from Graham Dumpleton) -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1997-2003 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# - -""" -Parse X Bitmap Distribution Format (BDF) -""" -from __future__ import annotations - -from typing import BinaryIO - -from . import FontFile, Image - -bdf_slant = { - "R": "Roman", - "I": "Italic", - "O": "Oblique", - "RI": "Reverse Italic", - "RO": "Reverse Oblique", - "OT": "Other", -} - -bdf_spacing = {"P": "Proportional", "M": "Monospaced", "C": "Cell"} - - -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[:9] == b"STARTCHAR": - break - id = s[9:].strip().decode("ascii") - - # load symbol properties - props = {} - while True: - s = f.readline() - if not s or s[:6] == 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[:7] == 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 s[:13] != 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[:13] == 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/plotter-app/venv/lib/python3.8/site-packages/PIL/BlpImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/BlpImagePlugin.py deleted file mode 100644 index b9cefaf..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/BlpImagePlugin.py +++ /dev/null @@ -1,488 +0,0 @@ -""" -Blizzard Mipmap Format (.blp) -Jerome Leclanche - -The contents of this file are hereby released in the public domain (CC0) -Full text of the CC0 license: - https://creativecommons.org/publicdomain/zero/1.0/ - -BLP1 files, used mostly in Warcraft III, are not fully supported. -All types of BLP2 files used in World of Warcraft are supported. - -The BLP file structure consists of a header, up to 16 mipmaps of the -texture - -Texture sizes must be powers of two, though the two dimensions do -not have to be equal; 512x256 is valid, but 512x200 is not. -The first mipmap (mipmap #0) is the full size image; each subsequent -mipmap halves both dimensions. The final mipmap should be 1x1. - -BLP files come in many different flavours: -* JPEG-compressed (type == 0) - only supported for BLP1. -* RAW images (type == 1, encoding == 1). Each mipmap is stored as an - array of 8-bit values, one per pixel, left to right, top to bottom. - Each value is an index to the palette. -* DXT-compressed (type == 1, encoding == 2): -- DXT1 compression is used if alpha_encoding == 0. - - An additional alpha bit is used if alpha_depth == 1. - - DXT3 compression is used if alpha_encoding == 1. - - DXT5 compression is used if alpha_encoding == 7. -""" - -from __future__ import annotations - -import abc -import os -import struct -from enum import IntEnum -from io import BytesIO -from typing import IO - -from . import Image, ImageFile - - -class Format(IntEnum): - JPEG = 0 - - -class Encoding(IntEnum): - UNCOMPRESSED = 1 - DXT = 2 - UNCOMPRESSED_RAW_BGRA = 3 - - -class AlphaEncoding(IntEnum): - DXT1 = 0 - DXT3 = 1 - DXT5 = 7 - - -def unpack_565(i: int) -> tuple[int, int, int]: - return ((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3 - - -def decode_dxt1( - data: bytes, alpha: bool = False -) -> tuple[bytearray, bytearray, bytearray, bytearray]: - """ - input: one "row" of data (i.e. will produce 4*width pixels) - """ - - blocks = len(data) // 8 # number of blocks in row - ret = (bytearray(), bytearray(), bytearray(), bytearray()) - - for block_index in range(blocks): - # Decode next 8-byte block. - idx = block_index * 8 - color0, color1, bits = struct.unpack_from("> 2 - - a = 0xFF - if control == 0: - r, g, b = r0, g0, b0 - elif control == 1: - r, g, b = r1, g1, b1 - elif control == 2: - if color0 > color1: - r = (2 * r0 + r1) // 3 - g = (2 * g0 + g1) // 3 - b = (2 * b0 + b1) // 3 - else: - r = (r0 + r1) // 2 - g = (g0 + g1) // 2 - b = (b0 + b1) // 2 - elif control == 3: - if color0 > color1: - r = (2 * r1 + r0) // 3 - g = (2 * g1 + g0) // 3 - b = (2 * b1 + b0) // 3 - else: - r, g, b, a = 0, 0, 0, 0 - - if alpha: - ret[j].extend([r, g, b, a]) - else: - ret[j].extend([r, g, b]) - - return ret - - -def decode_dxt3(data: bytes) -> tuple[bytearray, bytearray, bytearray, bytearray]: - """ - input: one "row" of data (i.e. will produce 4*width pixels) - """ - - blocks = len(data) // 16 # number of blocks in row - ret = (bytearray(), bytearray(), bytearray(), bytearray()) - - for block_index in range(blocks): - idx = block_index * 16 - block = data[idx : idx + 16] - # Decode next 16-byte block. - bits = struct.unpack_from("<8B", block) - color0, color1 = struct.unpack_from(">= 4 - else: - high = True - a &= 0xF - a *= 17 # We get a value between 0 and 15 - - color_code = (code >> 2 * (4 * j + i)) & 0x03 - - if color_code == 0: - r, g, b = r0, g0, b0 - elif color_code == 1: - r, g, b = r1, g1, b1 - elif color_code == 2: - r = (2 * r0 + r1) // 3 - g = (2 * g0 + g1) // 3 - b = (2 * b0 + b1) // 3 - elif color_code == 3: - r = (2 * r1 + r0) // 3 - g = (2 * g1 + g0) // 3 - b = (2 * b1 + b0) // 3 - - ret[j].extend([r, g, b, a]) - - return ret - - -def decode_dxt5(data: bytes) -> tuple[bytearray, bytearray, bytearray, bytearray]: - """ - input: one "row" of data (i.e. will produce 4 * width pixels) - """ - - blocks = len(data) // 16 # number of blocks in row - ret = (bytearray(), bytearray(), bytearray(), bytearray()) - - for block_index in range(blocks): - idx = block_index * 16 - block = data[idx : idx + 16] - # Decode next 16-byte block. - a0, a1 = struct.unpack_from("> alphacode_index) & 0x07 - elif alphacode_index == 15: - alphacode = (alphacode2 >> 15) | ((alphacode1 << 1) & 0x06) - else: # alphacode_index >= 18 and alphacode_index <= 45 - alphacode = (alphacode1 >> (alphacode_index - 16)) & 0x07 - - if alphacode == 0: - a = a0 - elif alphacode == 1: - a = a1 - elif a0 > a1: - a = ((8 - alphacode) * a0 + (alphacode - 1) * a1) // 7 - elif alphacode == 6: - a = 0 - elif alphacode == 7: - a = 255 - else: - a = ((6 - alphacode) * a0 + (alphacode - 1) * a1) // 5 - - color_code = (code >> 2 * (4 * j + i)) & 0x03 - - if color_code == 0: - r, g, b = r0, g0, b0 - elif color_code == 1: - r, g, b = r1, g1, b1 - elif color_code == 2: - r = (2 * r0 + r1) // 3 - g = (2 * g0 + g1) // 3 - b = (2 * b0 + b1) // 3 - elif color_code == 3: - r = (2 * r1 + r0) // 3 - g = (2 * g1 + g0) // 3 - b = (2 * b1 + b0) // 3 - - ret[j].extend([r, g, b, a]) - - return ret - - -class BLPFormatError(NotImplementedError): - pass - - -def _accept(prefix: bytes) -> bool: - return prefix[:4] in (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) - - self.fp.seek(5, os.SEEK_CUR) - (self._blp_alpha_depth,) = struct.unpack(" tuple[int, int]: - try: - self._read_blp_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_blp_header(self) -> None: - assert self.fd is not None - self.fd.seek(4) - (self._blp_compression,) = struct.unpack(" bytes: - 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]]) -> bytearray: - data = bytearray() - _data = BytesIO(self._safe_read(self._blp_lengths[0])) - while True: - try: - (offset,) = struct.unpack(" None: - if self._blp_compression == Format.JPEG: - self._decode_jpeg_stream() - - elif self._blp_compression == 1: - if self._blp_encoding in (4, 5): - palette = self._read_palette() - data = self._read_bgra(palette) - self.set_as_raw(data) - else: - msg = f"Unsupported BLP encoding {repr(self._blp_encoding)}" - raise BLPFormatError(msg) - else: - msg = f"Unsupported BLP compression {repr(self._blp_encoding)}" - raise BLPFormatError(msg) - - def _decode_jpeg_stream(self) -> None: - from .JpegImagePlugin import JpegImageFile - - (jpeg_header_size,) = struct.unpack(" None: - palette = self._read_palette() - - assert self.fd is not None - self.fd.seek(self._blp_offsets[0]) - - if self._blp_compression == 1: - # Uncompressed or DirectX compression - - if self._blp_encoding == Encoding.UNCOMPRESSED: - data = self._read_bgra(palette) - - elif self._blp_encoding == Encoding.DXT: - data = bytearray() - if self._blp_alpha_encoding == AlphaEncoding.DXT1: - linesize = (self.size[0] + 3) // 4 * 8 - for yb in range((self.size[1] + 3) // 4): - for d in decode_dxt1( - self._safe_read(linesize), alpha=bool(self._blp_alpha_depth) - ): - data += d - - elif self._blp_alpha_encoding == AlphaEncoding.DXT3: - linesize = (self.size[0] + 3) // 4 * 16 - for yb in range((self.size[1] + 3) // 4): - for d in decode_dxt3(self._safe_read(linesize)): - data += d - - elif self._blp_alpha_encoding == AlphaEncoding.DXT5: - linesize = (self.size[0] + 3) // 4 * 16 - for yb in range((self.size[1] + 3) // 4): - for d in decode_dxt5(self._safe_read(linesize)): - data += d - else: - msg = f"Unsupported alpha encoding {repr(self._blp_alpha_encoding)}" - raise BLPFormatError(msg) - else: - msg = f"Unknown BLP encoding {repr(self._blp_encoding)}" - raise BLPFormatError(msg) - - else: - msg = f"Unknown BLP compression {repr(self._blp_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) - - 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"), -} - - -def _accept(prefix: bytes) -> bool: - return prefix[:2] == 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=0, offset=0): - """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 = {"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 - 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.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 - self.info["dpi"] = tuple(x / 39.3701 for x in file_info["pixels_per_meter"]) - if file_info["compression"] == self.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)) - 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 - self._size = file_info["width"], file_info["height"] - - # ------- If color count was not found in the header, compute from bits - file_info["colors"] = ( - file_info["colors"] - if file_info.get("colors", 0) - else (1 << file_info["bits"]) - ) - 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"], (None, None)) - if self.mode is None: - 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.BITFIELDS: - SUPPORTED = { - 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"]] - ): - 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"]] - ): - 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.RAW: - if file_info["bits"] == 32 and header == 22: # 32-bit .cur offset - raw_mode, self._mode = "BGRA", "RGBA" - elif file_info["compression"] in (self.RLE8, self.RLE4): - decoder_name = "bmp_rle" - else: - msg = f"Unsupported BMP compression ({file_info['compression']})" - raise OSError(msg) - - # --------------- Once the header is processed, process the palette/LUT - if self.mode == "P": # Paletted for 1, 4 and 8 bit images - # ---------------------------------------------------- 1-bit images - if not (0 < file_info["colors"] <= 65536): - msg = f"Unsupported BMP Palette size ({file_info['colors']})" - raise OSError(msg) - else: - padding = file_info["palette_padding"] - palette = read(padding * file_info["colors"]) - grayscale = True - indices = ( - (0, 255) - if file_info["colors"] == 2 - else list(range(file_info["colors"])) - ) - - # ----------------- Check if grayscale and ignore palette if so - for ind, val in enumerate(indices): - rgb = palette[ind * padding : ind * padding + 3] - if rgb != o8(val) * 3: - grayscale = False - - # ------- If all colors are gray, white or black, ditch palette - if grayscale: - self._mode = "1" if file_info["colors"] == 2 else "L" - raw_mode = self.mode - else: - self._mode = "P" - self.palette = ImagePalette.raw( - "BGRX" if padding == 4 else "BGR", palette - ) - - # ---------------------------- Finally set the tile data for the plugin - self.info["compression"] = file_info["compression"] - args = [raw_mode] - if decoder_name == "bmp_rle": - args.append(file_info["compression"] == self.RLE4) - else: - args.append(((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3)) - args.append(file_info["direction"]) - self.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) -> 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) * 4 for i in (0, 255)) - elif im.mode == "L": - palette = b"".join(o8(i) * 4 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, [("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/plotter-app/venv/lib/python3.8/site-packages/PIL/BufrStubImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/BufrStubImagePlugin.py deleted file mode 100644 index 0ee2f65..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/BufrStubImagePlugin.py +++ /dev/null @@ -1,76 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# BUFR stub adapter -# -# Copyright (c) 1996-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -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[:4] == b"BUFR" or prefix[:4] == b"ZCZC" - - -class BufrStubImageFile(ImageFile.StubImageFile): - format = "BUFR" - format_description = "BUFR" - - def _open(self) -> None: - offset = self.fp.tell() - - if not _accept(self.fp.read(4)): - msg = "Not a BUFR file" - raise SyntaxError(msg) - - self.fp.seek(offset) - - # 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/plotter-app/venv/lib/python3.8/site-packages/PIL/ContainerIO.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ContainerIO.py deleted file mode 100644 index 0035296..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ContainerIO.py +++ /dev/null @@ -1,121 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# a class to read from a container file -# -# History: -# 1995-06-18 fl Created -# 1995-09-07 fl Added readline(), readlines() -# -# Copyright (c) 1997-2001 by Secret Labs AB -# Copyright (c) 1995 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -from typing import IO, AnyStr, Generic, Literal - - -class ContainerIO(Generic[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 seek(self, offset: int, mode: Literal[0, 1, 2] = io.SEEK_SET) -> None: - """ - 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. - """ - 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) - - def tell(self) -> int: - """ - Get current file pointer. - - :returns: Offset from start of region, in bytes. - """ - return self.pos - - def read(self, n: int = 0) -> AnyStr: - """ - Read data. - - :param n: Number of bytes to read. If omitted or zero, - read until end of region. - :returns: An 8-bit string. - """ - if n: - n = min(n, self.length - self.pos) - else: - n = self.length - self.pos - if not n: # 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) -> AnyStr: - """ - Read a line of text. - - :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: - break - return s - - def readlines(self) -> list[AnyStr]: - """ - Read multiple lines of text. - - :returns: A list of 8-bit strings. - """ - lines = [] - while True: - s = self.readline() - if not s: - break - lines.append(s) - return lines diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/CurImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/CurImagePlugin.py deleted file mode 100644 index 85e2145..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/CurImagePlugin.py +++ /dev/null @@ -1,75 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# Windows Cursor support for PIL -# -# notes: -# uses BmpImagePlugin.py to read the bitmap data. -# -# history: -# 96-05-27 fl Created -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import BmpImagePlugin, Image -from ._binary import i16le as i16 -from ._binary import i32le as i32 - -# -# -------------------------------------------------------------------- - - -def _accept(prefix: bytes) -> bool: - return prefix[:4] == 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] = d, (0, 0) + self.size, o, a - - -# -# -------------------------------------------------------------------- - -Image.register_open(CurImageFile.format, CurImageFile, _accept) - -Image.register_extension(CurImageFile.format, ".cur") diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/DcxImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/DcxImagePlugin.py deleted file mode 100644 index f67f27d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/DcxImagePlugin.py +++ /dev/null @@ -1,80 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# DCX file handling -# -# DCX is a container file format defined by Intel, commonly used -# for fax applications. Each DCX file consists of a directory -# (a list of file offsets) followed by a set of (usually 1-bit) -# PCX files. -# -# History: -# 1995-09-09 fl Created -# 1996-03-20 fl Properly derived from PcxImageFile. -# 1998-07-15 fl Renamed offset attribute to avoid name clash -# 2002-07-30 fl Fixed file handling -# -# Copyright (c) 1997-98 by Secret Labs AB. -# Copyright (c) 1995-96 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image -from ._binary import i32le as i32 -from .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 - 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/plotter-app/venv/lib/python3.8/site-packages/PIL/DdsImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/DdsImagePlugin.py deleted file mode 100644 index a57e4ae..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/DdsImagePlugin.py +++ /dev/null @@ -1,575 +0,0 @@ -""" -A Pillow loader 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) -> 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) - - alpha = im.mode[-1] == "A" - if im.mode[0] == "L": - pixel_flags = DDPF.LUMINANCE - rawmode = im.mode - if alpha: - rgba_mask = [0x000000FF, 0x000000FF, 0x000000FF] - else: - rgba_mask = [0xFF000000, 0xFF000000, 0xFF000000] - else: - pixel_flags = DDPF.RGB - rawmode = 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) - - flags = DDSD.CAPS | DDSD.HEIGHT | DDSD.WIDTH | DDSD.PITCH | DDSD.PIXELFORMAT - bitcount = len(im.getbands()) * 8 - pitch = (im.width * bitcount + 7) // 8 - - 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, 0, bitcount) - + struct.pack("<4I", *rgba_mask) # dwRGBABitMask - + struct.pack("<5I", DDSCAPS.TEXTURE, 0, 0, 0, 0) - ) - ImageFile._save( - im, fp, [ImageFile._Tile("raw", (0, 0) + im.size, 0, (rawmode, 0, 1))] - ) - - -def _accept(prefix: bytes) -> bool: - return prefix[:4] == 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/plotter-app/venv/lib/python3.8/site-packages/PIL/EpsImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/EpsImagePlugin.py deleted file mode 100644 index f31b1c1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/EpsImagePlugin.py +++ /dev/null @@ -1,478 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# EPS file handling -# -# History: -# 1995-09-01 fl Created (0.1) -# 1996-05-18 fl Don't choke on "atend" fields, Ghostscript interface (0.2) -# 1996-08-22 fl Don't choke on floating point BoundingBox values -# 1996-08-23 fl Handle files from Macintosh (0.3) -# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4) -# 2003-09-07 fl Check gs.close status (from Federico Di Gregorio) (0.5) -# 2014-05-07 e Handling of EPS with binary preview and fixed resolution -# resizing -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1995-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -import os -import re -import subprocess -import sys -import tempfile -from typing import IO - -from . import Image, ImageFile -from ._binary import i32le as i32 -from ._deprecate import deprecate - -# -------------------------------------------------------------------- - - -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, size, fp, scale=1, transparency=False): - """Render an image using Ghostscript""" - global gs_binary - if not has_ghostscript(): - msg = "Unable to locate Ghostscript on paths" - raise OSError(msg) - - # Unpack decoder tile - decoder, tile, offset, data = tile[0] - length, bbox = data - - # 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) - - device = "pngalpha" if transparency else "ppmraw" - - # 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) - out_im = Image.open(outfile) - out_im.load() - finally: - try: - os.unlink(outfile) - if infile_temp: - os.unlink(infile_temp) - except OSError: - pass - - im = out_im.im.copy() - out_im.close() - return im - - -class PSFile: - """ - Wrapper for bytesio object that treats either CR or LF as end of line. - This class is no longer used internally, but kept for backwards compatibility. - """ - - def __init__(self, fp): - deprecate( - "PSFile", - 11, - action="If you need the functionality of this class " - "you will need to implement it yourself.", - ) - self.fp = fp - self.char = None - - def seek(self, offset, whence=io.SEEK_SET): - self.char = None - self.fp.seek(offset, whence) - - def readline(self) -> str: - s = [self.char or b""] - self.char = None - - c = self.fp.read(1) - while (c not in b"\r\n") and len(c): - s.append(c) - c = self.fp.read(1) - - self.char = self.fp.read(1) - # line endings can be 1 or 2 of \r \n, in either order - if self.char in b"\r\n": - self.char = None - - return b"".join(s).decode("latin-1") - - -def _accept(prefix: bytes) -> bool: - return prefix[:4] == 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" - self._size = 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 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 self._size 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. - box = [int(float(i)) for i in v.split()] - self._size = box[2] - box[0], box[3] - box[1] - self.tile = [("eps", (0, 0) + self.size, offset, (length, box))] - 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[:8] == "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 - - # 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 - - self._size = columns, rows - return - 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 - - if not self._size: - msg = "cannot determine EPS bounding box" - raise OSError(msg) - - def _find_offset(self, fp): - 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=1, transparency=False): - # 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, [("eps", (0, 0) + im.size, 0, None)]) - - 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/plotter-app/venv/lib/python3.8/site-packages/PIL/ExifTags.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ExifTags.py deleted file mode 100644 index 39b4aa5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ExifTags.py +++ /dev/null @@ -1,381 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# EXIF tags -# -# Copyright (c) 2003 by Secret Labs AB -# -# See the README file for information on usage and redistribution. -# - -""" -This module provides constants and clear-text names for various -well-known EXIF tags. -""" -from __future__ import annotations - -from enum import IntEnum - - -class Base(IntEnum): - # possibly incomplete - InteropIndex = 0x0001 - ProcessingSoftware = 0x000B - NewSubfileType = 0x00FE - SubfileType = 0x00FF - ImageWidth = 0x0100 - ImageLength = 0x0101 - BitsPerSample = 0x0102 - Compression = 0x0103 - PhotometricInterpretation = 0x0106 - Thresholding = 0x0107 - CellWidth = 0x0108 - CellLength = 0x0109 - FillOrder = 0x010A - DocumentName = 0x010D - ImageDescription = 0x010E - Make = 0x010F - Model = 0x0110 - StripOffsets = 0x0111 - Orientation = 0x0112 - SamplesPerPixel = 0x0115 - RowsPerStrip = 0x0116 - StripByteCounts = 0x0117 - MinSampleValue = 0x0118 - MaxSampleValue = 0x0119 - XResolution = 0x011A - YResolution = 0x011B - PlanarConfiguration = 0x011C - PageName = 0x011D - FreeOffsets = 0x0120 - FreeByteCounts = 0x0121 - GrayResponseUnit = 0x0122 - GrayResponseCurve = 0x0123 - T4Options = 0x0124 - T6Options = 0x0125 - ResolutionUnit = 0x0128 - PageNumber = 0x0129 - TransferFunction = 0x012D - Software = 0x0131 - DateTime = 0x0132 - Artist = 0x013B - HostComputer = 0x013C - Predictor = 0x013D - WhitePoint = 0x013E - PrimaryChromaticities = 0x013F - ColorMap = 0x0140 - HalftoneHints = 0x0141 - TileWidth = 0x0142 - TileLength = 0x0143 - TileOffsets = 0x0144 - TileByteCounts = 0x0145 - SubIFDs = 0x014A - InkSet = 0x014C - InkNames = 0x014D - NumberOfInks = 0x014E - DotRange = 0x0150 - TargetPrinter = 0x0151 - ExtraSamples = 0x0152 - SampleFormat = 0x0153 - SMinSampleValue = 0x0154 - SMaxSampleValue = 0x0155 - TransferRange = 0x0156 - ClipPath = 0x0157 - XClipPathUnits = 0x0158 - YClipPathUnits = 0x0159 - Indexed = 0x015A - JPEGTables = 0x015B - OPIProxy = 0x015F - JPEGProc = 0x0200 - JpegIFOffset = 0x0201 - JpegIFByteCount = 0x0202 - JpegRestartInterval = 0x0203 - JpegLosslessPredictors = 0x0205 - JpegPointTransforms = 0x0206 - JpegQTables = 0x0207 - JpegDCTables = 0x0208 - JpegACTables = 0x0209 - YCbCrCoefficients = 0x0211 - YCbCrSubSampling = 0x0212 - YCbCrPositioning = 0x0213 - ReferenceBlackWhite = 0x0214 - XMLPacket = 0x02BC - RelatedImageFileFormat = 0x1000 - RelatedImageWidth = 0x1001 - RelatedImageLength = 0x1002 - Rating = 0x4746 - RatingPercent = 0x4749 - ImageID = 0x800D - CFARepeatPatternDim = 0x828D - BatteryLevel = 0x828F - Copyright = 0x8298 - ExposureTime = 0x829A - FNumber = 0x829D - IPTCNAA = 0x83BB - ImageResources = 0x8649 - ExifOffset = 0x8769 - InterColorProfile = 0x8773 - ExposureProgram = 0x8822 - SpectralSensitivity = 0x8824 - GPSInfo = 0x8825 - ISOSpeedRatings = 0x8827 - OECF = 0x8828 - Interlace = 0x8829 - TimeZoneOffset = 0x882A - SelfTimerMode = 0x882B - SensitivityType = 0x8830 - StandardOutputSensitivity = 0x8831 - RecommendedExposureIndex = 0x8832 - ISOSpeed = 0x8833 - ISOSpeedLatitudeyyy = 0x8834 - ISOSpeedLatitudezzz = 0x8835 - ExifVersion = 0x9000 - DateTimeOriginal = 0x9003 - DateTimeDigitized = 0x9004 - OffsetTime = 0x9010 - OffsetTimeOriginal = 0x9011 - OffsetTimeDigitized = 0x9012 - ComponentsConfiguration = 0x9101 - CompressedBitsPerPixel = 0x9102 - ShutterSpeedValue = 0x9201 - ApertureValue = 0x9202 - BrightnessValue = 0x9203 - ExposureBiasValue = 0x9204 - MaxApertureValue = 0x9205 - SubjectDistance = 0x9206 - MeteringMode = 0x9207 - LightSource = 0x9208 - Flash = 0x9209 - FocalLength = 0x920A - Noise = 0x920D - ImageNumber = 0x9211 - SecurityClassification = 0x9212 - ImageHistory = 0x9213 - TIFFEPStandardID = 0x9216 - MakerNote = 0x927C - UserComment = 0x9286 - SubsecTime = 0x9290 - SubsecTimeOriginal = 0x9291 - SubsecTimeDigitized = 0x9292 - AmbientTemperature = 0x9400 - Humidity = 0x9401 - Pressure = 0x9402 - WaterDepth = 0x9403 - Acceleration = 0x9404 - CameraElevationAngle = 0x9405 - XPTitle = 0x9C9B - XPComment = 0x9C9C - XPAuthor = 0x9C9D - XPKeywords = 0x9C9E - XPSubject = 0x9C9F - FlashPixVersion = 0xA000 - ColorSpace = 0xA001 - ExifImageWidth = 0xA002 - ExifImageHeight = 0xA003 - RelatedSoundFile = 0xA004 - ExifInteroperabilityOffset = 0xA005 - FlashEnergy = 0xA20B - SpatialFrequencyResponse = 0xA20C - FocalPlaneXResolution = 0xA20E - FocalPlaneYResolution = 0xA20F - FocalPlaneResolutionUnit = 0xA210 - SubjectLocation = 0xA214 - ExposureIndex = 0xA215 - SensingMethod = 0xA217 - FileSource = 0xA300 - SceneType = 0xA301 - CFAPattern = 0xA302 - CustomRendered = 0xA401 - ExposureMode = 0xA402 - WhiteBalance = 0xA403 - DigitalZoomRatio = 0xA404 - FocalLengthIn35mmFilm = 0xA405 - SceneCaptureType = 0xA406 - GainControl = 0xA407 - Contrast = 0xA408 - Saturation = 0xA409 - Sharpness = 0xA40A - DeviceSettingDescription = 0xA40B - SubjectDistanceRange = 0xA40C - ImageUniqueID = 0xA420 - CameraOwnerName = 0xA430 - BodySerialNumber = 0xA431 - LensSpecification = 0xA432 - LensMake = 0xA433 - LensModel = 0xA434 - LensSerialNumber = 0xA435 - CompositeImage = 0xA460 - CompositeImageCount = 0xA461 - CompositeImageExposureTimes = 0xA462 - Gamma = 0xA500 - PrintImageMatching = 0xC4A5 - DNGVersion = 0xC612 - DNGBackwardVersion = 0xC613 - UniqueCameraModel = 0xC614 - LocalizedCameraModel = 0xC615 - CFAPlaneColor = 0xC616 - CFALayout = 0xC617 - LinearizationTable = 0xC618 - BlackLevelRepeatDim = 0xC619 - BlackLevel = 0xC61A - BlackLevelDeltaH = 0xC61B - BlackLevelDeltaV = 0xC61C - WhiteLevel = 0xC61D - DefaultScale = 0xC61E - DefaultCropOrigin = 0xC61F - DefaultCropSize = 0xC620 - ColorMatrix1 = 0xC621 - ColorMatrix2 = 0xC622 - CameraCalibration1 = 0xC623 - CameraCalibration2 = 0xC624 - ReductionMatrix1 = 0xC625 - ReductionMatrix2 = 0xC626 - AnalogBalance = 0xC627 - AsShotNeutral = 0xC628 - AsShotWhiteXY = 0xC629 - BaselineExposure = 0xC62A - BaselineNoise = 0xC62B - BaselineSharpness = 0xC62C - BayerGreenSplit = 0xC62D - LinearResponseLimit = 0xC62E - CameraSerialNumber = 0xC62F - LensInfo = 0xC630 - ChromaBlurRadius = 0xC631 - AntiAliasStrength = 0xC632 - ShadowScale = 0xC633 - DNGPrivateData = 0xC634 - MakerNoteSafety = 0xC635 - CalibrationIlluminant1 = 0xC65A - CalibrationIlluminant2 = 0xC65B - BestQualityScale = 0xC65C - RawDataUniqueID = 0xC65D - OriginalRawFileName = 0xC68B - OriginalRawFileData = 0xC68C - ActiveArea = 0xC68D - MaskedAreas = 0xC68E - AsShotICCProfile = 0xC68F - AsShotPreProfileMatrix = 0xC690 - CurrentICCProfile = 0xC691 - CurrentPreProfileMatrix = 0xC692 - ColorimetricReference = 0xC6BF - CameraCalibrationSignature = 0xC6F3 - ProfileCalibrationSignature = 0xC6F4 - AsShotProfileName = 0xC6F6 - NoiseReductionApplied = 0xC6F7 - ProfileName = 0xC6F8 - ProfileHueSatMapDims = 0xC6F9 - ProfileHueSatMapData1 = 0xC6FA - ProfileHueSatMapData2 = 0xC6FB - ProfileToneCurve = 0xC6FC - ProfileEmbedPolicy = 0xC6FD - ProfileCopyright = 0xC6FE - ForwardMatrix1 = 0xC714 - ForwardMatrix2 = 0xC715 - PreviewApplicationName = 0xC716 - PreviewApplicationVersion = 0xC717 - PreviewSettingsName = 0xC718 - PreviewSettingsDigest = 0xC719 - PreviewColorSpace = 0xC71A - PreviewDateTime = 0xC71B - RawImageDigest = 0xC71C - OriginalRawFileDigest = 0xC71D - SubTileBlockSize = 0xC71E - RowInterleaveFactor = 0xC71F - ProfileLookTableDims = 0xC725 - ProfileLookTableData = 0xC726 - OpcodeList1 = 0xC740 - OpcodeList2 = 0xC741 - OpcodeList3 = 0xC74E - NoiseProfile = 0xC761 - - -"""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 = 0 - GPSLatitudeRef = 1 - GPSLatitude = 2 - GPSLongitudeRef = 3 - GPSLongitude = 4 - GPSAltitudeRef = 5 - GPSAltitude = 6 - GPSTimeStamp = 7 - GPSSatellites = 8 - GPSStatus = 9 - GPSMeasureMode = 10 - GPSDOP = 11 - GPSSpeedRef = 12 - GPSSpeed = 13 - GPSTrackRef = 14 - GPSTrack = 15 - GPSImgDirectionRef = 16 - GPSImgDirection = 17 - GPSMapDatum = 18 - GPSDestLatitudeRef = 19 - GPSDestLatitude = 20 - GPSDestLongitudeRef = 21 - GPSDestLongitude = 22 - GPSDestBearingRef = 23 - GPSDestBearing = 24 - GPSDestDistanceRef = 25 - GPSDestDistance = 26 - GPSProcessingMethod = 27 - GPSAreaInformation = 28 - GPSDateStamp = 29 - GPSDifferential = 30 - GPSHPositioningError = 31 - - -"""Maps EXIF GPS tags to tag names.""" -GPSTAGS = {i.value: i.name for i in GPS} - - -class Interop(IntEnum): - InteropIndex = 1 - InteropVersion = 2 - RelatedImageFileFormat = 4096 - RelatedImageWidth = 4097 - RelatedImageHeight = 4098 - - -class IFD(IntEnum): - Exif = 34665 - GPSInfo = 34853 - Makernote = 37500 - Interop = 40965 - IFD1 = -1 - - -class LightSource(IntEnum): - Unknown = 0 - Daylight = 1 - Fluorescent = 2 - Tungsten = 3 - Flash = 4 - Fine = 9 - Cloudy = 10 - Shade = 11 - DaylightFluorescent = 12 - DayWhiteFluorescent = 13 - CoolWhiteFluorescent = 14 - WhiteFluorescent = 15 - StandardLightA = 17 - StandardLightB = 18 - StandardLightC = 19 - D55 = 20 - D65 = 21 - D75 = 22 - D50 = 23 - ISO = 24 - Other = 255 diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/FitsImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/FitsImagePlugin.py deleted file mode 100644 index 4846054..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/FitsImagePlugin.py +++ /dev/null @@ -1,152 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# FITS file handling -# -# Copyright (c) 1998-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import gzip -import math - -from . import Image, ImageFile - - -def _accept(prefix: bytes) -> bool: - return prefix[:6] == 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 = [(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) -> 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/plotter-app/venv/lib/python3.8/site-packages/PIL/FliImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/FliImagePlugin.py deleted file mode 100644 index dceb839..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/FliImagePlugin.py +++ /dev/null @@ -1,174 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# FLI/FLC file handling. -# -# History: -# 95-09-01 fl Created -# 97-01-03 fl Fixed parser, setup decoder tile -# 98-07-15 fl Renamed offset attribute to avoid name clash -# -# Copyright (c) Secret Labs AB 1997-98. -# Copyright (c) Fredrik Lundh 1995-97. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os - -from . import Image, ImageFile, ImagePalette -from ._binary import i16le as i16 -from ._binary import i32le as i32 -from ._binary import o8 - -# -# 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): - # 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 = 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 - - palette = [o8(r) + o8(g) + o8(b) for (r, g, b) in palette] - self.palette = ImagePalette.raw("RGB", b"".join(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, shift): - # 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 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 = [("fli", (0, 0) + self.size, self.__offset, None)] - - 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/plotter-app/venv/lib/python3.8/site-packages/PIL/FontFile.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/FontFile.py deleted file mode 100644 index 1e0c1c1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/FontFile.py +++ /dev/null @@ -1,134 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# base class for raster font file parsers -# -# history: -# 1997-06-05 fl created -# 1997-08-19 fl restrict image width -# -# Copyright (c) 1997-1998 by Secret Labs AB -# Copyright (c) 1997-1998 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os -from typing import BinaryIO - -from . import Image, _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/plotter-app/venv/lib/python3.8/site-packages/PIL/FpxImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/FpxImagePlugin.py deleted file mode 100644 index c1927bd..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/FpxImagePlugin.py +++ /dev/null @@ -1,255 +0,0 @@ -# -# THIS IS WORK IN PROGRESS -# -# The Python Imaging Library. -# $Id$ -# -# FlashPix support for PIL -# -# History: -# 97-01-25 fl Created (reads uncompressed RGB images only) -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1997. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import olefile - -from . import Image, ImageFile -from ._binary import i32le as i32 - -# we map from colour field tuples to (mode, rawmode) descriptors -MODES = { - # opacity - (0x00007FFE,): ("A", "L"), - # monochrome - (0x00010000,): ("L", "L"), - (0x00018000, 0x00017FFE): ("RGBA", "LA"), - # photo YCC - (0x00020000, 0x00020001, 0x00020002): ("RGB", "YCC;P"), - (0x00028000, 0x00028001, 0x00028002, 0x00027FFE): ("RGBA", "YCCA;P"), - # standard RGB (NIFRGB) - (0x00030000, 0x00030001, 0x00030002): ("RGB", "RGB"), - (0x00038000, 0x00038001, 0x00038002, 0x00037FFE): ("RGBA", "RGBA"), -} - - -# -# -------------------------------------------------------------------- - - -def _accept(prefix: bytes) -> bool: - return prefix[:8] == olefile.MAGIC - - -## -# Image plugin for the FlashPix images. - - -class FpxImageFile(ImageFile.ImageFile): - format = "FPX" - format_description = "FlashPix" - - def _open(self): - # - # 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 - - if self.ole.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) - - 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] - - bands = i32(s, 4) - if bands > 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( - ( - "raw", - (x, y, x1, y1), - i32(s, i) + 28, - (self.rawmode,), - ) - ) - - elif compression == 1: - # FIXME: the fill decoder is not implemented - self.tile.append( - ( - "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( - ( - "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): - 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/plotter-app/venv/lib/python3.8/site-packages/PIL/FtexImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/FtexImagePlugin.py deleted file mode 100644 index 5acbb49..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/FtexImagePlugin.py +++ /dev/null @@ -1,115 +0,0 @@ -""" -A Pillow loader for .ftc and .ftu files (FTEX) -Jerome Leclanche - -The contents of this file are hereby released in the public domain (CC0) -Full text of the CC0 license: - https://creativecommons.org/publicdomain/zero/1.0/ - -Independence War 2: Edge Of Chaos - Texture File Format - 16 October 2001 - -The textures used for 3D objects in Independence War 2: Edge Of Chaos are in a -packed custom format called FTEX. This file format uses file extensions FTC -and FTU. -* FTC files are compressed textures (using standard texture compression). -* FTU files are not compressed. -Texture File Format -The FTC and FTU texture files both use the same format. This -has the following structure: -{header} -{format_directory} -{data} -Where: -{header} = { - u32:magic, - u32:version, - u32:width, - u32:height, - u32:mipmap_count, - u32:format_count -} - -* The "magic" number is "FTEX". -* "width" and "height" are the dimensions of the texture. -* "mipmap_count" is the number of mipmaps in the texture. -* "format_count" is the number of texture formats (different versions of the -same texture) in this file. - -{format_directory} = format_count * { u32:format, u32:where } - -The format value is 0 for DXT1 compressed textures and 1 for 24-bit RGB -uncompressed textures. -The texture data for a format starts at the position "where" in the file. - -Each set of texture data in the file has the following structure: -{data} = format_count * { u32:mipmap_size, mipmap_size * { u8 } } -* "mipmap_size" is the number of bytes in that mip level. For compressed -textures this is the size of the texture data compressed with DXT1. For 24 bit -uncompressed textures, this is 3 * width * height. Following this are the image -bytes for that mipmap level. - -Note: All data is stored in little-Endian (Intel) byte order. -""" - -from __future__ import annotations - -import struct -from enum import IntEnum -from io import BytesIO - -from . import Image, ImageFile - -MAGIC = b"FTEX" - - -class Format(IntEnum): - DXT1 = 0 - UNCOMPRESSED = 1 - - -class FtexImageFile(ImageFile.ImageFile): - format = "FTEX" - format_description = "Texture File Format (IW2:EOC)" - - def _open(self) -> None: - 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[:4] == MAGIC - - -Image.register_open(FtexImageFile.format, FtexImageFile, _accept) -Image.register_extensions(FtexImageFile.format, [".ftc", ".ftu"]) diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/GbrImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/GbrImagePlugin.py deleted file mode 100644 index 93e89b1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/GbrImagePlugin.py +++ /dev/null @@ -1,103 +0,0 @@ -# -# The Python Imaging Library -# -# load a GIMP brush file -# -# History: -# 96-03-14 fl Created -# 16-01-08 es Version 2 -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996. -# Copyright (c) Eric Soroos 2016. -# -# See the README file for information on usage and redistribution. -# -# -# See https://github.com/GNOME/gimp/blob/mainline/devel-docs/gbr.txt for -# format documentation. -# -# This code Interprets version 1 and 2 .gbr files. -# Version 1 files are obsolete, and should not be used for new -# brushes. -# Version 2 files are saved by GIMP v2.8 (at least) -# Version 3 files have a format specifier of 18 for 16bit floats in -# the color depth field. This is currently unsupported by Pillow. -from __future__ import annotations - -from . import Image, ImageFile -from ._binary import i32be as i32 - - -def _accept(prefix: bytes) -> bool: - return len(prefix) >= 8 and i32(prefix, 0) >= 20 and i32(prefix, 4) in (1, 2) - - -## -# Image plugin for the GIMP brush format. - - -class GbrImageFile(ImageFile.ImageFile): - format = "GBR" - format_description = "GIMP brush file" - - def _open(self) -> None: - 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): - if not self.im: - 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/plotter-app/venv/lib/python3.8/site-packages/PIL/GdImageFile.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/GdImageFile.py deleted file mode 100644 index 88b87a2..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/GdImageFile.py +++ /dev/null @@ -1,102 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# GD file handling -# -# History: -# 1996-04-12 fl Created -# -# Copyright (c) 1997 by Secret Labs AB. -# Copyright (c) 1996 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# - - -""" -.. note:: - This format cannot be automatically recognized, so the - class is not registered for use with :py:func:`PIL.Image.open()`. To open a - gd file, use the :py:func:`PIL.GdImageFile.open()` function instead. - -.. warning:: - THE GD FORMAT IS NOT DESIGNED FOR DATA INTERCHANGE. This - implementation is provided for convenience and demonstrational - purposes only. -""" -from __future__ import annotations - -from typing import IO - -from . import ImageFile, ImagePalette, UnidentifiedImageError -from ._binary import i16be as i16 -from ._binary import i32be as i32 -from ._typing import StrOrBytesPath - - -class GdImageFile(ImageFile.ImageFile): - """ - Image plugin for the GD uncompressed format. Note that this format - is not supported by the standard :py:func:`PIL.Image.open()` function. To use - this plugin, you have to import the :py:mod:`PIL.GdImageFile` module and - use the :py:func:`PIL.GdImageFile.open()` function. - """ - - format = "GD" - format_description = "GD uncompressed images" - - def _open(self) -> None: - # Header - assert self.fp is not None - - s = self.fp.read(1037) - - if i16(s) not in [65534, 65535]: - msg = "Not a valid GD 2.x .gd file" - raise SyntaxError(msg) - - self._mode = "L" # FIXME: "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( - "XBGR", s[7 + true_color_offset + 4 : 7 + true_color_offset + 4 + 256 * 4] - ) - - self.tile = [ - ( - "raw", - (0, 0) + self.size, - 7 + true_color_offset + 4 + 256 * 4, - ("L", 0, 1), - ) - ] - - -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/plotter-app/venv/lib/python3.8/site-packages/PIL/GifImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/GifImagePlugin.py deleted file mode 100644 index 284128c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/GifImagePlugin.py +++ /dev/null @@ -1,1159 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# GIF file handling -# -# History: -# 1995-09-01 fl Created -# 1996-12-14 fl Added interlace support -# 1996-12-30 fl Added animation support -# 1997-01-05 fl Added write support, fixed local colour map bug -# 1997-02-23 fl Make sure to load raster data in getdata() -# 1997-07-05 fl Support external decoder (0.4) -# 1998-07-09 fl Handle all modes when saving (0.5) -# 1998-07-15 fl Renamed offset attribute to avoid name clash -# 2001-04-16 fl Added rewind support (seek to frame 0) (0.6) -# 2001-04-17 fl Added palette optimization (0.7) -# 2002-06-06 fl Added transparency support for save (0.8) -# 2004-02-24 fl Disable interlacing for small images -# -# Copyright (c) 1997-2004 by Secret Labs AB -# Copyright (c) 1995-2004 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import itertools -import math -import os -import subprocess -import sys -from enum import IntEnum -from functools import cached_property -from typing import IO, TYPE_CHECKING, Any, List, Literal, NamedTuple, Union - -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 - -if TYPE_CHECKING: - from . import _imaging - - -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[:6] in [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) - self.tile = [] - 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 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[:11] == 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 not palette and self.global_palette: - from copy import copy - - palette = copy(self.global_palette) - self.palette = palette - else: - if self.mode == "P": - if ( - LOADING_STRATEGY != LoadingStrategy.RGB_AFTER_DIFFERENT_PALETTE_ONLY - or palette - ): - self.pyaccess = None - 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 tuple(self._frame_palette.palette[color * 3 : color * 3 + 3]) - else: - return (color, color, color) - - self.dispose = None - self.dispose_extent = 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 = [ - ( - "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 - 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._frame_transparency is not None: - 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 == "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) - 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: - source_palette = im.im.getpalette("RGB")[:768] - 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) - - used_palette_colors: list[int] | None - if palette: - used_palette_colors = [] - assert source_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 - im = im.remap_palette(used_palette_colors) - else: - used_palette_colors = _get_optimize(im, info) - if used_palette_colors is not None: - im = im.remap_palette(used_palette_colors, source_palette) - if "transparency" in info: - try: - info["transparency"] = used_palette_colors.index( - info["transparency"] - ) - except ValueError: - del info["transparency"] - return im - - 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(): - 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, [("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]: - if _get_palette_bytes(im_frame) != _get_palette_bytes(base_im): - 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 - 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: - if background_im is None: - color = im.encoderinfo.get( - "transparency", im.info.get("transparency", (0, 0, 0)) - ) - background = _get_background(im_frame, color) - background_im = Image.new("P", im_frame.size, background) - background_im.putpalette(im_frames[0].im.palette) - bbox = _getbbox(background_im, im_frame)[1] - elif encoderinfo.get("optimize") and im_frame.mode != "1": - if "transparency" not in encoderinfo: - 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 - - 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 - - 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 - """ - return im.palette.palette if im.palette else b"" - - -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 - 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, [("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 = [] - - if sys.version_info >= (3, 12): - from collections.abc import Buffer - - def write(self, data: Buffer) -> int: - self.data.append(data) - return len(data) - - else: - - def write(self, data: Any) -> 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/plotter-app/venv/lib/python3.8/site-packages/PIL/GimpGradientFile.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/GimpGradientFile.py deleted file mode 100644 index 220eac5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/GimpGradientFile.py +++ /dev/null @@ -1,149 +0,0 @@ -# -# Python Imaging Library -# $Id$ -# -# stuff to read (and render) GIMP gradient files -# -# History: -# 97-08-23 fl Created -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1997. -# -# See the README file for information on usage and redistribution. -# - -""" -Stuff to translate curve segments to palette values (derived from -the corresponding code in GIMP, written by Federico Mena Quintero. -See the GIMP distribution for more information.) -""" -from __future__ import annotations - -from math import log, pi, sin, sqrt -from 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 fp.readline()[:13] != 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/plotter-app/venv/lib/python3.8/site-packages/PIL/GimpPaletteFile.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/GimpPaletteFile.py deleted file mode 100644 index 4cad0eb..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/GimpPaletteFile.py +++ /dev/null @@ -1,58 +0,0 @@ -# -# Python Imaging Library -# $Id$ -# -# stuff to read GIMP palette files -# -# History: -# 1997-08-23 fl Created -# 2004-09-07 fl Support GIMP 2.0 palette files. -# -# Copyright (c) Secret Labs AB 1997-2004. All rights reserved. -# Copyright (c) Fredrik Lundh 1997-2004. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import re -from typing import IO - -from ._binary import o8 - - -class GimpPaletteFile: - """File handler for GIMP's palette format.""" - - rawmode = "RGB" - - def __init__(self, fp: IO[bytes]) -> None: - palette = [o8(i) * 3 for i in range(256)] - - if fp.readline()[:12] != b"GIMP Palette": - msg = "not a GIMP palette file" - raise SyntaxError(msg) - - for i in range(256): - s = fp.readline() - if not s: - break - - # skip fields and comment lines - if re.match(rb"\w+:|#", s): - continue - if len(s) > 100: - msg = "bad palette file" - raise SyntaxError(msg) - - v = tuple(map(int, s.split()[:3])) - if len(v) != 3: - msg = "bad palette entry" - raise ValueError(msg) - - palette[i] = o8(v[0]) + o8(v[1]) + o8(v[2]) - - self.palette = b"".join(palette) - - def getpalette(self) -> tuple[bytes, str]: - return self.palette, self.rawmode diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/GribStubImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/GribStubImagePlugin.py deleted file mode 100644 index e9aa084..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/GribStubImagePlugin.py +++ /dev/null @@ -1,76 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# GRIB stub adapter -# -# Copyright (c) 1996-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -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[:4] == b"GRIB" and prefix[7] == 1 - - -class GribStubImageFile(ImageFile.StubImageFile): - format = "GRIB" - format_description = "GRIB" - - def _open(self) -> None: - offset = self.fp.tell() - - if not _accept(self.fp.read(8)): - msg = "Not a GRIB file" - raise SyntaxError(msg) - - self.fp.seek(offset) - - # 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/plotter-app/venv/lib/python3.8/site-packages/PIL/Hdf5StubImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/Hdf5StubImagePlugin.py deleted file mode 100644 index cc9e73d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/Hdf5StubImagePlugin.py +++ /dev/null @@ -1,76 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# HDF5 stub adapter -# -# Copyright (c) 2000-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -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[:8] == b"\x89HDF\r\n\x1a\n" - - -class HDF5StubImageFile(ImageFile.StubImageFile): - format = "HDF5" - format_description = "HDF5" - - def _open(self) -> None: - offset = self.fp.tell() - - if not _accept(self.fp.read(8)): - msg = "Not an HDF file" - raise SyntaxError(msg) - - self.fp.seek(offset) - - # 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/plotter-app/venv/lib/python3.8/site-packages/PIL/IcnsImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/IcnsImagePlugin.py deleted file mode 100644 index 2a89d49..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/IcnsImagePlugin.py +++ /dev/null @@ -1,399 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# macOS icns file decoder, based on icns.py by Bob Ippolito. -# -# history: -# 2004-10-09 fl Turned into a PIL plugin; removed 2.3 dependencies. -# 2020-04-04 Allow saving on all operating systems. -# -# Copyright (c) 2004 by Bob Ippolito. -# Copyright (c) 2004 by Secret Labs. -# Copyright (c) 2004 by Fredrik Lundh. -# Copyright (c) 2014 by Alastair Houghton. -# Copyright (c) 2020 by Pan Jing. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -import os -import struct -import sys -from typing import IO - -from . import Image, ImageFile, PngImagePlugin, features - -enable_jpeg2k = features.check_codec("jpg_2000") -if enable_jpeg2k: - from . import Jpeg2KImagePlugin - -MAGIC = b"icns" -HEADERSIZE = 8 - - -def nextheader(fobj): - return struct.unpack(">4sI", fobj.read(HEADERSIZE)) - - -def read_32t(fobj, start_length, size): - # 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, start_length, size): - """ - 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 = byte[0] - if byte & 0x80: - blocksize = byte - 125 - byte = fobj.read(1) - for i in range(blocksize): - data.append(byte) - else: - blocksize = byte + 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, start_length, size): - # 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, start_length, size): - (start, length) = start_length - fobj.seek(start) - sig = fobj.read(12) - if sig[:8] == b"\x89PNG\x0d\x0a\x1a\x0a": - fobj.seek(start) - im = PngImagePlugin.PngImageFile(fobj) - Image._decompression_bomb_check(im.size) - return {"RGBA": im} - elif ( - sig[:4] == b"\xff\x4f\xff\x51" - or sig[:4] == 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): - """ - fobj is a file-like object as an icns resource - """ - # signature : (start, length) - self.dct = 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 - dct[sig] = (i, blocksize) - fobj.seek(blocksize, io.SEEK_CUR) - i += blocksize - - def itersizes(self): - 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): - sizes = self.itersizes() - if not sizes: - msg = "No 32bit icon resources found" - raise SyntaxError(msg) - return max(sizes) - - def dataforsize(self, size): - """ - 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=None): - if size is None: - size = self.bestsize() - if len(size) == 2: - size = (size[0], size[1], 1) - channels = self.dataforsize(size) - - im = channels.get("RGBA", None) - if im: - return im - - im = channels.get("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 - def size(self): - return self._size - - @size.setter - def size(self, value): - info_size = value - if info_size not in self.info["sizes"] and len(info_size) == 2: - info_size = (info_size[0], info_size[1], 1) - if ( - info_size not in self.info["sizes"] - and len(info_size) == 3 - and info_size[2] == 1 - ): - simple_sizes = [ - (size[0] * size[2], size[1] * size[2]) for size in self.info["sizes"] - ] - if value in simple_sizes: - info_size = self.info["sizes"][simple_sizes.index(value)] - if info_size 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): - if len(self.size) == 3: - self.best_size = self.size - self.size = ( - self.best_size[0] * self.best_size[2], - self.best_size[1] * self.best_size[2], - ) - - 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[:4] == 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/plotter-app/venv/lib/python3.8/site-packages/PIL/IcoImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/IcoImagePlugin.py deleted file mode 100644 index 227fcf3..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/IcoImagePlugin.py +++ /dev/null @@ -1,360 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# Windows Icon support for PIL -# -# History: -# 96-05-27 fl Created -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# - -# This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis -# . -# https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki -# -# 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 - -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, [("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[:4] == _MAGIC - - -class IcoFile: - def __init__(self, buf): - """ - 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) - - icon_header = { - "width": s[0], - "height": s[1], - "nb_color": s[2], # No. of colors in image (0 if >=8bpp) - "reserved": s[3], - "planes": i16(s, 4), - "bpp": i16(s, 6), - "size": i32(s, 8), - "offset": i32(s, 12), - } - - # See Wikipedia - for j in ("width", "height"): - if not icon_header[j]: - icon_header[j] = 256 - - # See Wikipedia notes about color depth. - # We need this just to differ images with equal sizes - icon_header["color_depth"] = ( - icon_header["bpp"] - or ( - icon_header["nb_color"] != 0 - and ceil(log(icon_header["nb_color"], 2)) - ) - or 256 - ) - - icon_header["dim"] = (icon_header["width"], icon_header["height"]) - icon_header["square"] = icon_header["width"] * icon_header["height"] - - 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): - """ - Get a list of all available icon sizes and color depths. - """ - return {(h["width"], h["height"]) for h in self.entry} - - def getentryindex(self, size, bpp=False): - 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, bpp=False): - """ - 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] = d, (0, 0) + im.size, o, a - - # figure out where AND mask image starts - bpp = header["bpp"] - if 32 == bpp: - # 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 - mask = Image.frombuffer( - "L", # 8bpp - im.size, # (w, h) - alpha_bytes, # source chars - "raw", # raw decoder - ("L", 0, -1), # 8bpp inverted, unpadded, reversed - ) - 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 - 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 - ) - - # now we have two images, im is XOR image and mask is AND image - - # apply mask image as alpha channel - 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): - return self._size - - @size.setter - def size(self, value): - 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): - 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.pyaccess = None - 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 - - 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/plotter-app/venv/lib/python3.8/site-packages/PIL/ImImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImImagePlugin.py deleted file mode 100644 index 2fb7ecd..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImImagePlugin.py +++ /dev/null @@ -1,374 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# IFUNC IM file handling for PIL -# -# history: -# 1995-09-01 fl Created. -# 1997-01-03 fl Save palette images -# 1997-01-08 fl Added sequence support -# 1997-01-23 fl Added P and RGB save support -# 1997-05-31 fl Read floating point images -# 1997-06-22 fl Save floating point images -# 1997-08-27 fl Read and save 1-bit images -# 1998-06-25 fl Added support for RGB+LUT images -# 1998-07-02 fl Added support for YCC images -# 1998-07-15 fl Renamed offset attribute to avoid name clash -# 1998-12-29 fl Added I;16 support -# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7) -# 2003-09-26 fl Added LA/PA support -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1995-2001 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os -import re -from typing import IO, Any - -from . import Image, ImageFile, ImagePalette - -# -------------------------------------------------------------------- -# 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[-2:] == b"\r\n": - s = s[:-2] - elif s[-1:] == 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 s[:1] != 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[:2] == "F;": - # ifunc95 formats - try: - # use bit decoder (if necessary) - bits = int(self.rawmode[2:]) - if bits not in [8, 16, 32]: - self.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 = [ - ("raw", (0, 0) + self.size, offs, ("G", 0, -1)), - ("raw", (0, 0) + self.size, offs + size, ("R", 0, -1)), - ("raw", (0, 0) + self.size, offs + 2 * size, ("B", 0, -1)), - ] - else: - # LabEye/IFUNC files - self.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 - - 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 = [("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(("Image size (x*y): %d*%d\r\n" % im.size).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, [("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/plotter-app/venv/lib/python3.8/site-packages/PIL/Image.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/Image.py deleted file mode 100644 index d41c065..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/Image.py +++ /dev/null @@ -1,4147 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# the Image class wrapper -# -# partial release history: -# 1995-09-09 fl Created -# 1996-03-11 fl PIL release 0.0 (proof of concept) -# 1996-04-30 fl PIL release 0.1b1 -# 1999-07-28 fl PIL release 1.0 final -# 2000-06-07 fl PIL release 1.1 -# 2000-10-20 fl PIL release 1.1.1 -# 2001-05-07 fl PIL release 1.1.2 -# 2002-03-15 fl PIL release 1.1.3 -# 2003-05-10 fl PIL release 1.1.4 -# 2005-03-28 fl PIL release 1.1.5 -# 2006-12-02 fl PIL release 1.1.6 -# 2009-11-15 fl PIL release 1.1.7 -# -# Copyright (c) 1997-2009 by Secret Labs AB. All rights reserved. -# Copyright (c) 1995-2009 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# - -from __future__ import annotations - -import abc -import atexit -import builtins -import io -import logging -import math -import os -import re -import struct -import sys -import tempfile -import warnings -from collections.abc import Callable, MutableMapping -from enum import IntEnum -from types import ModuleType -from typing import ( - IO, - TYPE_CHECKING, - Any, - Literal, - Protocol, - Sequence, - Tuple, - 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 ._typing import StrOrBytesPath, TypeGuard -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 - - -USE_CFFI_ACCESS = False -cffi: ModuleType | None -try: - import cffi -except ImportError: - cffi = None - - -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 - """ - 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 - -if TYPE_CHECKING: - from . import ImageFile, PyAccess -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): - m = ImageMode.getmode(im.mode) - shape = (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 _E: - def __init__(self, scale, offset) -> None: - self.scale = scale - self.offset = offset - - def __neg__(self): - return _E(-self.scale, -self.offset) - - def __add__(self, other): - if isinstance(other, _E): - return _E(self.scale + other.scale, self.offset + other.offset) - return _E(self.scale, self.offset + other) - - __radd__ = __add__ - - def __sub__(self, other): - return self + -other - - def __rsub__(self, other): - return other + -self - - def __mul__(self, other): - if isinstance(other, _E): - return NotImplemented - return _E(self.scale * other, self.offset * other) - - __rmul__ = __mul__ - - def __truediv__(self, other): - if isinstance(other, _E): - return NotImplemented - return _E(self.scale / other, self.offset / other) - - -def _getscaleoffset(expr): - a = expr(_E(1, 0)) - return (a.scale, a.offset) if isinstance(a, _E) 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): - # FIXME: take "new" parameters / other image? - # FIXME: turn mode and size into delegating properties? - self.im = None - self._mode = "" - self._size = (0, 0) - self.palette = None - self.info = {} - self.readonly = 0 - self.pyaccess = None - self._exif = None - - @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 - - 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 _close_fp(self): - if getattr(self, "_fp", False): - if self._fp != self.fp: - self._fp.close() - self._fp = DeferredError(ValueError("Operation on closed image")) - if self.fp: - self.fp.close() - - def __exit__(self, *args): - if hasattr(self, "fp"): - if getattr(self, "_exclusive_fp", False): - self._close_fp() - self.fp = None - - def close(self) -> None: - """ - Closes the file pointer, if possible. - - This operation will destroy the image core and release its memory. - The image data will be unusable afterward. - - This function is required to close images that have multiple frames or - have not had their file read and closed by the - :py:meth:`~PIL.Image.Image.load` method. See :ref:`file-handling` for - more information. - """ - if hasattr(self, "fp"): - try: - self._close_fp() - self.fp = None - except Exception as msg: - logger.debug("Error closing: %s", msg) - - if getattr(self, "map", None): - self.map = 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.pyaccess = None - 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 "<%s.%s image mode=%s size=%dx%d at 0x%X>" % ( - self.__class__.__module__, - self.__class__.__name__, - self.mode, - self.size[0], - self.size[1], - id(self), - ) - - def _repr_pretty_(self, p, cycle) -> None: - """IPython plain text display support""" - - # Same as __repr__ but without unpredictable id(self), - # to keep Jupyter notebook `text/plain` output stable. - p.text( - "<%s.%s image mode=%s size=%dx%d>" - % ( - self.__class__.__module__, - self.__class__.__name__, - self.mode, - self.size[0], - 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): - # numpy array interface support - new = {"version": 3} - try: - 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() - except Exception as e: - if not isinstance(e, (MemoryError, RecursionError)): - try: - import numpy - from packaging.version import parse as parse_version - except ImportError: - pass - else: - if parse_version(numpy.__version__) < parse_version("1.23"): - warnings.warn(str(e)) - raise - new["shape"], new["typestr"] = _conv_type_shape(self) - return new - - def __getstate__(self): - im_data = self.tobytes() # load image first - return [self.info, self.mode, self.size, self.getpalette(), im_data] - - def __setstate__(self, state) -> None: - Image.__init__(self) - info, mode, size, palette, data = state - 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 the raw image data from the 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. - - 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) - - bufsize = max(65536, 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, 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 | PyAccess.PyAccess | 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` or :py:class:`.PyAccess` - """ - 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: - if cffi and USE_CFFI_ACCESS: - if self.pyaccess: - return self.pyaccess - from . import PyAccess - - self.pyaccess = PyAccess.new(self, self.readonly) - if self.pyaccess: - return self.pyaccess - 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(0, 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": - trns_im.putpalette(self.palette) - if isinstance(t, tuple): - err = "Couldn't allocate a palette color for transparency" - 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): - other_mode = mode if self.mode == "LAB" else self.mode - if other_mode in ("RGB", "RGBA", "RGBX"): - from . import ImageCms - - srgb = ImageCms.createProfile("sRGB") - lab = ImageCms.createProfile("LAB") - profiles = [lab, srgb] if self.mode == "LAB" else [srgb, lab] - transform = ImageCms.buildTransform( - profiles[0], profiles[1], self.mode, mode - ) - return transform.apply(self) - - # 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(trns, new_im) - 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=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) - 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 = im.im.getpalette(mode, mode)[: colors * len(mode)] - im.palette = ImagePalette.ImagePalette(mode, palette) - - 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)) - - if TYPE_CHECKING: - from . import ImageFilter - - 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): - """ - 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 = [(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): - """ - 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): - """ - 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): - value = {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") - if xmp_tags: - match = re.search(r'tiff:Orientation(="|>)([0-9])', 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]: - 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(513): - ifds.append((ifd1, exif._info.next)) - - offset = None - for ifd, ifd_offset in ifds: - current_offset = self.fp.tell() - if offset is None: - offset = current_offset - - fp = self.fp - thumbnail_offset = ifd.get(513) - if thumbnail_offset is not None: - thumbnail_offset += getattr(self, "_exif_offset", 0) - self.fp.seek(thumbnail_offset) - data = self.fp.read(ifd.get(514)) - fp = io.BytesIO(data) - - with 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: - self.fp.seek(offset) - return child_images - - def getim(self): - """ - 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. - """ - return ( - self.mode in ("LA", "La", "PA", "RGBA", "RGBa") - or (self.mode == "P" and self.palette.mode.endswith("A")) - or "transparency" in self.info - ) - - 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() - if self.pyaccess: - return self.pyaccess.getpixel(xy) - 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=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"): - if extrema is None: - extrema = self.getextrema() - return self.im.histogram(extrema) - return self.im.histogram() - - def entropy(self, mask=None, extrema=None): - """ - 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"): - if extrema is None: - extrema = self.getextrema() - return self.im.entropy(extrema) - 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 isImageType(box): - 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 - assert not isinstance(box, Image) - - if box is None: - box = (0, 0) - - if len(box) == 2: - # upper left corner given; get size from image or mask - if isImageType(im): - size = im.size - elif isImageType(mask): - 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]) - - if isinstance(im, str): - from . import ImageColor - - im = ImageColor.getcolor(im, self.mode) - - elif isImageType(im): - 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) - im = im.im - - self._ensure_mutable() - - if mask: - mask.load() - self.im.paste(im, box, mask.im) - else: - self.im.paste(im, 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] | Callable[[int], 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, data): - # 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) - 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 - 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.pyaccess = None - 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 isImageType(alpha): - # 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 - alpha = cast(int, alpha) # see python/typing#1013 - 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]], - 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, rawmode="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): - palette = ImagePalette.raw(data.rawmode, data.palette) - 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.pyaccess: - return self.pyaccess.putpixel(xy, value) - - 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] - palette_index = self.palette.getcolor(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, source_palette=None): - """ - 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)) - - 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, resample, box): - """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], - 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 2-tuple: - (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 specifies a number - of bits, such as "I;16", 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: - type_special = ";" in self.mode - resample = Resampling.NEAREST if type_special 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) - - self.load() - if box is None: - box = (0, 0) + self.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, 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, y, matrix): - (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)): - x, y = transform(x, y, matrix) - xx.append(x) - yy.append(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. - :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.path.realpath(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.path.realpath(os.fspath(fp.name)) - - # may mutate self! - self._ensure_mutable() - - save_all = params.pop("save_all", False) - self.encoderinfo = params - self.encoderconfig: tuple[Any, ...] = () - - 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 - - if format.upper() not in SAVE: - init() - if 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 - if open_fp: - fp.close() - - 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, key): - 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 - - box = None - final_size: tuple[int, int] - if reducing_gap is not None: - preserved_size = preserve_aspect_ratio() - if preserved_size is None: - return - final_size = preserved_size - - res = self.draft( - None, (int(size[0] * reducing_gap), int(size[1] * reducing_gap)) - ) - if res is not None: - box = res[1] - if box is None: - self.load() - - # load() may have changed the size of the image - preserved_size = preserve_aspect_ratio() - if preserved_size is None: - return - final_size = preserved_size - - 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 - self.pyaccess = None - - # 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, image, method, data, resample=Resampling.NEAREST, fill=1 - ): - 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): - msg = { - Resampling.BOX: "Image.Resampling.BOX", - Resampling.HAMMING: "Image.Resampling.HAMMING", - Resampling.LANCZOS: "Image.Resampling.LANCZOS", - }[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): - """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): - """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: - """ - 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: - """ - 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 - -# -# Debugging - - -def _wedge() -> Image: - """Create grayscale wedge (for debugging only)""" - - return Image()._new(core.wedge("L")) - - -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, - 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, 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() - - -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``. - - 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: - 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 fromqimage(im): - """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): - """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.path.realpath(os.fspath(fp)) - - if filename: - 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, *args): - """ - 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], - 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 = None - bigtiff = False - _loaded = False - - def __init__(self): - self._data = {} - self._hidden_data = {} - self._ifds = {} - self._info = None - self._loaded_exif = None - - def _fixup(self, value): - try: - if len(value) == 1 and isinstance(value, tuple): - return value[0] - except Exception: - pass - return value - - def _fixup_dict(self, src_dict): - # 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, group=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): - pass - else: - from . import TiffImagePlugin - - info = TiffImagePlugin.ImageFileDirectory_v2(self.head, group=group) - info.load(self.fp) - return self._fixup_dict(info) - - def _get_head(self): - 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): - # 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() - if data and data.startswith(b"Exif\x00\x00"): - data = data[6:] - if not data: - self._info = None - return - - self.fp = 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, offset=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): - 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, 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): - if tag not in self._ifds: - if tag == ExifTags.IFD.IFD1: - if self._info is not None and self._info.next != 0: - self._ifds[tag] = self._get_ifd_dict(self._info.next) - elif tag in [ExifTags.IFD.Exif, ExifTags.IFD.GPSInfo]: - offset = self._hidden_data.get(tag, self.get(tag)) - if offset is not None: - self._ifds[tag] = self._get_ifd_dict(offset, tag) - 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[:8] == b"FUJIFILM": - ifd_offset = i32le(tag_data, 8) - ifd_data = tag_data[ifd_offset:] - - makernote = {} - for i in range(0, 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 = {"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 - ) - - self.fp.read(4) - camerainfo["Category"] = self.fp.read(2) - - makernote = {0x1101: dict(self._fixup_dict(camerainfo))} - self._ifds[tag] = makernote - else: - # Interop - self._ifds[tag] = self._get_ifd_dict(tag_data, tag) - ifd = self._ifds.get(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): - 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) -> bool: - return tag in self._data or (self._info is not None and tag in self._info) - - def __setitem__(self, tag, value) -> 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): - keys = set(self._data) - if self._info is not None: - keys.update(self._info) - return iter(keys) diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageChops.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageChops.py deleted file mode 100644 index 29a5c99..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageChops.py +++ /dev/null @@ -1,311 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# standard channel operations -# -# History: -# 1996-03-24 fl Created -# 1996-08-13 fl Added logical operations (for "1" images) -# 2000-10-12 fl Added offset method (from Image.py) -# -# Copyright (c) 1997-2000 by Secret Labs AB -# Copyright (c) 1996-2000 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# - -from __future__ import annotations - -from . import Image - - -def constant(image: Image.Image, value: int) -> Image.Image: - """Fill a channel with a given gray level. - - :rtype: :py:class:`~PIL.Image.Image` - """ - - return Image.new("L", image.size, value) - - -def duplicate(image: Image.Image) -> Image.Image: - """Copy a channel. Alias for :py:meth:`PIL.Image.Image.copy`. - - :rtype: :py:class:`~PIL.Image.Image` - """ - - return image.copy() - - -def invert(image: Image.Image) -> Image.Image: - """ - Invert an image (channel). :: - - out = MAX - image - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image.load() - return image._new(image.im.chop_invert()) - - -def lighter(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Compares the two images, pixel by pixel, and returns a new image containing - the lighter values. :: - - out = max(image1, image2) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_lighter(image2.im)) - - -def darker(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Compares the two images, pixel by pixel, and returns a new image containing - the darker values. :: - - out = min(image1, image2) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_darker(image2.im)) - - -def difference(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Returns the absolute value of the pixel-by-pixel difference between the two - images. :: - - out = abs(image1 - image2) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_difference(image2.im)) - - -def multiply(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Superimposes two images on top of each other. - - If you multiply an image with a solid black image, the result is black. If - you multiply with a solid white image, the image is unaffected. :: - - out = image1 * image2 / MAX - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_multiply(image2.im)) - - -def screen(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Superimposes two inverted images on top of each other. :: - - out = MAX - ((MAX - image1) * (MAX - image2) / MAX) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_screen(image2.im)) - - -def soft_light(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Superimposes two images on top of each other using the Soft Light algorithm - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_soft_light(image2.im)) - - -def hard_light(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Superimposes two images on top of each other using the Hard Light algorithm - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_hard_light(image2.im)) - - -def overlay(image1: Image.Image, image2: Image.Image) -> Image.Image: - """ - Superimposes two images on top of each other using the Overlay algorithm - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_overlay(image2.im)) - - -def add( - image1: Image.Image, image2: Image.Image, scale: float = 1.0, offset: float = 0 -) -> Image.Image: - """ - Adds two images, dividing the result by scale and adding the - offset. If omitted, scale defaults to 1.0, and offset to 0.0. :: - - out = ((image1 + image2) / scale + offset) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_add(image2.im, scale, offset)) - - -def subtract( - image1: Image.Image, image2: Image.Image, scale: float = 1.0, offset: float = 0 -) -> Image.Image: - """ - Subtracts two images, dividing the result by scale and adding the offset. - If omitted, scale defaults to 1.0, and offset to 0.0. :: - - out = ((image1 - image2) / scale + offset) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_subtract(image2.im, scale, offset)) - - -def add_modulo(image1: Image.Image, image2: Image.Image) -> Image.Image: - """Add two images, without clipping the result. :: - - out = ((image1 + image2) % MAX) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_add_modulo(image2.im)) - - -def subtract_modulo(image1: Image.Image, image2: Image.Image) -> Image.Image: - """Subtract two images, without clipping the result. :: - - out = ((image1 - image2) % MAX) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_subtract_modulo(image2.im)) - - -def logical_and(image1: Image.Image, image2: Image.Image) -> Image.Image: - """Logical AND between two images. - - Both of the images must have mode "1". If you would like to perform a - logical AND on an image with a mode other than "1", try - :py:meth:`~PIL.ImageChops.multiply` instead, using a black-and-white mask - as the second image. :: - - out = ((image1 and image2) % MAX) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_and(image2.im)) - - -def logical_or(image1: Image.Image, image2: Image.Image) -> Image.Image: - """Logical OR between two images. - - Both of the images must have mode "1". :: - - out = ((image1 or image2) % MAX) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_or(image2.im)) - - -def logical_xor(image1: Image.Image, image2: Image.Image) -> Image.Image: - """Logical XOR between two images. - - Both of the images must have mode "1". :: - - out = ((bool(image1) != bool(image2)) % MAX) - - :rtype: :py:class:`~PIL.Image.Image` - """ - - image1.load() - image2.load() - return image1._new(image1.im.chop_xor(image2.im)) - - -def blend(image1: Image.Image, image2: Image.Image, alpha: float) -> Image.Image: - """Blend images using constant transparency weight. Alias for - :py:func:`PIL.Image.blend`. - - :rtype: :py:class:`~PIL.Image.Image` - """ - - return Image.blend(image1, image2, alpha) - - -def composite( - image1: Image.Image, image2: Image.Image, mask: Image.Image -) -> Image.Image: - """Create composite using transparency mask. Alias for - :py:func:`PIL.Image.composite`. - - :rtype: :py:class:`~PIL.Image.Image` - """ - - return Image.composite(image1, image2, mask) - - -def offset(image: Image.Image, xoffset: int, yoffset: int | None = None) -> Image.Image: - """Returns a copy of the image where data has been offset by the given - distances. Data wraps around the edges. If ``yoffset`` is omitted, it - is assumed to be equal to ``xoffset``. - - :param image: Input image. - :param xoffset: The horizontal distance. - :param yoffset: The vertical distance. If omitted, both - distances are set to the same value. - :rtype: :py:class:`~PIL.Image.Image` - """ - - if yoffset is None: - yoffset = xoffset - image.load() - return image._new(image.im.offset(xoffset, yoffset)) diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageCms.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageCms.py deleted file mode 100644 index ec10230..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageCms.py +++ /dev/null @@ -1,1127 +0,0 @@ -# The Python Imaging Library. -# $Id$ - -# Optional color management support, based on Kevin Cazabon's PyCMS -# library. - -# Originally released under LGPL. Graciously donated to PIL in -# March 2009, for distribution under the standard PIL license - -# History: - -# 2009-03-08 fl Added to PIL. - -# Copyright (C) 2002-2003 Kevin Cazabon -# Copyright (c) 2009 by Fredrik Lundh -# Copyright (c) 2013 by Eric Soroos - -# See the README file for information on usage and redistribution. See -# below for the original description. -from __future__ import annotations - -import operator -import sys -from enum import IntEnum, IntFlag -from functools import reduce -from typing import Any, Literal, SupportsFloat, SupportsInt, Union - -from . import Image, __version__ -from ._deprecate import deprecate -from ._typing import SupportsRead - -try: - from . import _imagingcms as core -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 - - """ - - 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._set(core.profile_frombytes(f.read())) - return - self._set(core.profile_open(profile), profile) - elif hasattr(profile, "read"): - self._set(core.profile_frombytes(profile.read())) - elif isinstance(profile, core.CmsProfile): - self._set(profile) - else: - msg = "Invalid type for Profile" # type: ignore[unreachable] - raise TypeError(msg) - - def _set(self, profile: core.CmsProfile, filename: str | None = None) -> None: - self.profile = profile - self.filename = filename - self.product_name = None # profile.product_name - self.product_info = None # profile.product_info - - 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: - im.load() - if imOut is None: - imOut = Image.new(self.output_mode, im.size, None) - self.transform.apply(im.im.id, imOut.im.id) - imOut.info["icc_profile"] = self.output_profile.tobytes() - return imOut - - def apply_in_place(self, im: Image.Image) -> Image.Image: - im.load() - if im.mode != self.output_mode: - msg = "mode mismatch" - raise ValueError(msg) # wrong output mode - self.transform.apply(im.im.id, im.im.id) - 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 -# --------------------------------------------------------------------. - -_CmsProfileCompatible = Union[ - str, SupportsRead[bytes], core.CmsProfile, ImageCmsProfile -] - - -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/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageColor.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageColor.py deleted file mode 100644 index 9a15a8e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageColor.py +++ /dev/null @@ -1,320 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# map CSS3-style colour description strings to RGB -# -# History: -# 2002-10-24 fl Added support for CSS-style color strings -# 2002-12-15 fl Added RGBA support -# 2004-03-27 fl Fixed remaining int() problems for Python 1.5.2 -# 2004-07-19 fl Fixed gray/grey spelling issues -# 2009-03-05 fl Fixed rounding error in grayscale calculation -# -# Copyright (c) 2002-2004 by Secret Labs AB -# Copyright (c) 2002-2004 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import re -from functools import lru_cache - -from . import Image - - -@lru_cache -def getrgb(color: str) -> tuple[int, int, int] | tuple[int, int, int, int]: - """ - Convert a color string to an RGB or RGBA tuple. If the string cannot be - parsed, this function raises a :py:exc:`ValueError` exception. - - .. versionadded:: 1.1.4 - - :param color: A color string - :return: ``(red, green, blue[, alpha])`` - """ - if len(color) > 100: - msg = "color specifier is too long" - raise ValueError(msg) - color = color.lower() - - rgb = colormap.get(color, None) - if rgb: - if isinstance(rgb, tuple): - return rgb - rgb_tuple = getrgb(rgb) - assert len(rgb_tuple) == 3 - colormap[color] = rgb_tuple - return rgb_tuple - - # check for known string formats - if re.match("#[a-f0-9]{3}$", color): - return int(color[1] * 2, 16), int(color[2] * 2, 16), int(color[3] * 2, 16) - - if re.match("#[a-f0-9]{4}$", color): - return ( - int(color[1] * 2, 16), - int(color[2] * 2, 16), - int(color[3] * 2, 16), - int(color[4] * 2, 16), - ) - - if re.match("#[a-f0-9]{6}$", color): - return int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16) - - if re.match("#[a-f0-9]{8}$", color): - return ( - int(color[1:3], 16), - int(color[3:5], 16), - int(color[5:7], 16), - int(color[7:9], 16), - ) - - m = re.match(r"rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) - if m: - return int(m.group(1)), int(m.group(2)), int(m.group(3)) - - m = re.match(r"rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color) - if m: - return ( - int((int(m.group(1)) * 255) / 100.0 + 0.5), - int((int(m.group(2)) * 255) / 100.0 + 0.5), - int((int(m.group(3)) * 255) / 100.0 + 0.5), - ) - - m = re.match( - r"hsl\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$", color - ) - if m: - from colorsys import hls_to_rgb - - rgb_floats = hls_to_rgb( - float(m.group(1)) / 360.0, - float(m.group(3)) / 100.0, - float(m.group(2)) / 100.0, - ) - return ( - int(rgb_floats[0] * 255 + 0.5), - int(rgb_floats[1] * 255 + 0.5), - int(rgb_floats[2] * 255 + 0.5), - ) - - m = re.match( - r"hs[bv]\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$", color - ) - if m: - from colorsys import hsv_to_rgb - - rgb_floats = hsv_to_rgb( - float(m.group(1)) / 360.0, - float(m.group(2)) / 100.0, - float(m.group(3)) / 100.0, - ) - return ( - int(rgb_floats[0] * 255 + 0.5), - int(rgb_floats[1] * 255 + 0.5), - int(rgb_floats[2] * 255 + 0.5), - ) - - m = re.match(r"rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color) - if m: - return int(m.group(1)), int(m.group(2)), int(m.group(3)), int(m.group(4)) - msg = f"unknown color specifier: {repr(color)}" - raise ValueError(msg) - - -@lru_cache -def getcolor(color: str, mode: str) -> int | tuple[int, ...]: - """ - Same as :py:func:`~PIL.ImageColor.getrgb` for most modes. However, if - ``mode`` is HSV, converts the RGB value to a HSV value, or if ``mode`` is - not color or a palette image, converts the RGB value to a grayscale value. - If the string cannot be parsed, this function raises a :py:exc:`ValueError` - exception. - - .. versionadded:: 1.1.4 - - :param color: A color string - :param mode: Convert result to this mode - :return: ``graylevel, (graylevel, alpha) or (red, green, blue[, alpha])`` - """ - # same as getrgb, but converts the result to the given mode - rgb, alpha = getrgb(color), 255 - if len(rgb) == 4: - alpha = rgb[3] - rgb = rgb[:3] - - if mode == "HSV": - from colorsys import rgb_to_hsv - - r, g, b = rgb - h, s, v = rgb_to_hsv(r / 255, g / 255, b / 255) - return int(h * 255), int(s * 255), int(v * 255) - elif Image.getmodebase(mode) == "L": - r, g, b = rgb - # ITU-R Recommendation 601-2 for nonlinear RGB - # scaled to 24 bits to match the convert's implementation. - graylevel = (r * 19595 + g * 38470 + b * 7471 + 0x8000) >> 16 - if mode[-1] == "A": - return graylevel, alpha - return graylevel - elif mode[-1] == "A": - return rgb + (alpha,) - return rgb - - -colormap: dict[str, str | tuple[int, int, int]] = { - # X11 colour table from https://drafts.csswg.org/css-color-4/, with - # gray/grey spelling issues fixed. This is a superset of HTML 4.0 - # colour names used in CSS 1. - "aliceblue": "#f0f8ff", - "antiquewhite": "#faebd7", - "aqua": "#00ffff", - "aquamarine": "#7fffd4", - "azure": "#f0ffff", - "beige": "#f5f5dc", - "bisque": "#ffe4c4", - "black": "#000000", - "blanchedalmond": "#ffebcd", - "blue": "#0000ff", - "blueviolet": "#8a2be2", - "brown": "#a52a2a", - "burlywood": "#deb887", - "cadetblue": "#5f9ea0", - "chartreuse": "#7fff00", - "chocolate": "#d2691e", - "coral": "#ff7f50", - "cornflowerblue": "#6495ed", - "cornsilk": "#fff8dc", - "crimson": "#dc143c", - "cyan": "#00ffff", - "darkblue": "#00008b", - "darkcyan": "#008b8b", - "darkgoldenrod": "#b8860b", - "darkgray": "#a9a9a9", - "darkgrey": "#a9a9a9", - "darkgreen": "#006400", - "darkkhaki": "#bdb76b", - "darkmagenta": "#8b008b", - "darkolivegreen": "#556b2f", - "darkorange": "#ff8c00", - "darkorchid": "#9932cc", - "darkred": "#8b0000", - "darksalmon": "#e9967a", - "darkseagreen": "#8fbc8f", - "darkslateblue": "#483d8b", - "darkslategray": "#2f4f4f", - "darkslategrey": "#2f4f4f", - "darkturquoise": "#00ced1", - "darkviolet": "#9400d3", - "deeppink": "#ff1493", - "deepskyblue": "#00bfff", - "dimgray": "#696969", - "dimgrey": "#696969", - "dodgerblue": "#1e90ff", - "firebrick": "#b22222", - "floralwhite": "#fffaf0", - "forestgreen": "#228b22", - "fuchsia": "#ff00ff", - "gainsboro": "#dcdcdc", - "ghostwhite": "#f8f8ff", - "gold": "#ffd700", - "goldenrod": "#daa520", - "gray": "#808080", - "grey": "#808080", - "green": "#008000", - "greenyellow": "#adff2f", - "honeydew": "#f0fff0", - "hotpink": "#ff69b4", - "indianred": "#cd5c5c", - "indigo": "#4b0082", - "ivory": "#fffff0", - "khaki": "#f0e68c", - "lavender": "#e6e6fa", - "lavenderblush": "#fff0f5", - "lawngreen": "#7cfc00", - "lemonchiffon": "#fffacd", - "lightblue": "#add8e6", - "lightcoral": "#f08080", - "lightcyan": "#e0ffff", - "lightgoldenrodyellow": "#fafad2", - "lightgreen": "#90ee90", - "lightgray": "#d3d3d3", - "lightgrey": "#d3d3d3", - "lightpink": "#ffb6c1", - "lightsalmon": "#ffa07a", - "lightseagreen": "#20b2aa", - "lightskyblue": "#87cefa", - "lightslategray": "#778899", - "lightslategrey": "#778899", - "lightsteelblue": "#b0c4de", - "lightyellow": "#ffffe0", - "lime": "#00ff00", - "limegreen": "#32cd32", - "linen": "#faf0e6", - "magenta": "#ff00ff", - "maroon": "#800000", - "mediumaquamarine": "#66cdaa", - "mediumblue": "#0000cd", - "mediumorchid": "#ba55d3", - "mediumpurple": "#9370db", - "mediumseagreen": "#3cb371", - "mediumslateblue": "#7b68ee", - "mediumspringgreen": "#00fa9a", - "mediumturquoise": "#48d1cc", - "mediumvioletred": "#c71585", - "midnightblue": "#191970", - "mintcream": "#f5fffa", - "mistyrose": "#ffe4e1", - "moccasin": "#ffe4b5", - "navajowhite": "#ffdead", - "navy": "#000080", - "oldlace": "#fdf5e6", - "olive": "#808000", - "olivedrab": "#6b8e23", - "orange": "#ffa500", - "orangered": "#ff4500", - "orchid": "#da70d6", - "palegoldenrod": "#eee8aa", - "palegreen": "#98fb98", - "paleturquoise": "#afeeee", - "palevioletred": "#db7093", - "papayawhip": "#ffefd5", - "peachpuff": "#ffdab9", - "peru": "#cd853f", - "pink": "#ffc0cb", - "plum": "#dda0dd", - "powderblue": "#b0e0e6", - "purple": "#800080", - "rebeccapurple": "#663399", - "red": "#ff0000", - "rosybrown": "#bc8f8f", - "royalblue": "#4169e1", - "saddlebrown": "#8b4513", - "salmon": "#fa8072", - "sandybrown": "#f4a460", - "seagreen": "#2e8b57", - "seashell": "#fff5ee", - "sienna": "#a0522d", - "silver": "#c0c0c0", - "skyblue": "#87ceeb", - "slateblue": "#6a5acd", - "slategray": "#708090", - "slategrey": "#708090", - "snow": "#fffafa", - "springgreen": "#00ff7f", - "steelblue": "#4682b4", - "tan": "#d2b48c", - "teal": "#008080", - "thistle": "#d8bfd8", - "tomato": "#ff6347", - "turquoise": "#40e0d0", - "violet": "#ee82ee", - "wheat": "#f5deb3", - "white": "#ffffff", - "whitesmoke": "#f5f5f5", - "yellow": "#ffff00", - "yellowgreen": "#9acd32", -} diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageDraw.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageDraw.py deleted file mode 100644 index 244d3d5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageDraw.py +++ /dev/null @@ -1,1206 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# drawing interface operations -# -# History: -# 1996-04-13 fl Created (experimental) -# 1996-08-07 fl Filled polygons, ellipses. -# 1996-08-13 fl Added text support -# 1998-06-28 fl Handle I and F images -# 1998-12-29 fl Added arc; use arc primitive to draw ellipses -# 1999-01-10 fl Added shape stuff (experimental) -# 1999-02-06 fl Added bitmap support -# 1999-02-11 fl Changed all primitives to take options -# 1999-02-20 fl Fixed backwards compatibility -# 2000-10-12 fl Copy on write, when necessary -# 2001-02-18 fl Use default ink for bitmap/text also in fill mode -# 2002-10-24 fl Added support for CSS-style color strings -# 2002-12-10 fl Added experimental support for RGBA-on-RGB drawing -# 2002-12-11 fl Refactored low-level drawing API (work in progress) -# 2004-08-26 fl Made Draw() a factory function, added getdraw() support -# 2004-09-04 fl Added width support to line primitive -# 2004-09-10 fl Added font mode handling -# 2006-06-19 fl Added font bearing support (getmask2) -# -# Copyright (c) 1997-2006 by Secret Labs AB -# Copyright (c) 1996-2006 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import math -import numbers -import struct -from types import ModuleType -from typing import TYPE_CHECKING, AnyStr, Callable, List, Sequence, Tuple, 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] | None -try: - Outline = Image.core.outline -except AttributeError: - Outline = None - -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 not isinstance(ink, numbers.Number): - 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 not isinstance(fill, numbers.Number): - 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] - - fill_im = mask.copy() - draw = Draw(fill_im) - draw.draw.draw_polygon(xy, mask_ink, 1) - - ink_im = mask.copy() - draw = Draw(ink_im) - width = width * 2 - 1 - draw.draw.draw_polygon(xy, mask_ink, 0, width) - - mask.paste(ink_im, mask=fill_im) - - im = Image.new(self.mode, self.im.size) - draw = Draw(im) - draw.draw.draw_polygon(xy, ink, 0, width) - self.im.paste(im.im, (0, 0) + im.size, 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) - else: - 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 _multiline_split(self, text: AnyStr) -> list[AnyStr]: - return text.split("\n" if isinstance(text, str) else b"\n") - - def _multiline_spacing(self, font, spacing, stroke_width): - return ( - self.textbbox((0, 0), "A", font, stroke_width=stroke_width)[3] - + stroke_width - + spacing - ) - - def text( - self, - xy: tuple[float, float], - text: str, - fill=None, - font: ( - ImageFont.ImageFont - | ImageFont.FreeTypeFont - | ImageFont.TransposedFont - | None - ) = None, - anchor=None, - spacing=4, - align="left", - direction=None, - features=None, - language=None, - stroke_width=0, - stroke_fill=None, - embedded_color=False, - *args, - **kwargs, - ) -> 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, stroke_width=0, stroke_offset=None) -> None: - mode = self.fontmode - if stroke_width == 0 and embedded_color: - mode = "RGBA" - coord = [] - start = [] - for i in range(2): - coord.append(int(xy[i])) - start.append(math.modf(xy[i])[0]) - try: - mask, offset = font.getmask2( # type: ignore[union-attr,misc] - text, - mode, - direction=direction, - features=features, - language=language, - stroke_width=stroke_width, - 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 stroke_offset: - coord = [coord[0] + stroke_offset[0], coord[1] + stroke_offset[1]] - 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 - draw_text(ink, 0) - else: - # Only draw normal text - draw_text(ink) - - def multiline_text( - self, - xy: tuple[float, float], - text: str, - fill=None, - font: ( - ImageFont.ImageFont - | ImageFont.FreeTypeFont - | ImageFont.TransposedFont - | None - ) = None, - anchor=None, - spacing=4, - align="left", - direction=None, - features=None, - language=None, - stroke_width=0, - stroke_fill=None, - embedded_color=False, - *, - font_size=None, - ) -> None: - if direction == "ttb": - msg = "ttb direction is unsupported for multiline text" - raise ValueError(msg) - - if anchor is None: - anchor = "la" - elif len(anchor) != 2: - msg = "anchor must be a 2 character string" - raise ValueError(msg) - elif anchor[1] in "tb": - msg = "anchor not supported for multiline text" - raise ValueError(msg) - - if font is None: - font = self._getfont(font_size) - - widths = [] - max_width: float = 0 - lines = self._multiline_split(text) - line_spacing = self._multiline_spacing(font, spacing, stroke_width) - for line in lines: - line_width = self.textlength( - line, font, direction=direction, features=features, language=language - ) - widths.append(line_width) - max_width = max(max_width, line_width) - - top = xy[1] - 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] - - # first align left by anchor - if anchor[0] == "m": - left -= width_difference / 2.0 - elif anchor[0] == "r": - left -= width_difference - - # then align by align parameter - if align == "left": - pass - elif align == "center": - left += width_difference / 2.0 - elif align == "right": - left += width_difference - else: - msg = 'align must be "left", "center" or "right"' - raise ValueError(msg) - - self.text( - (left, top), - line, - fill, - font, - anchor, - direction=direction, - features=features, - language=language, - stroke_width=stroke_width, - stroke_fill=stroke_fill, - embedded_color=embedded_color, - ) - top += line_spacing - - def textlength( - self, - text: str, - font: ( - ImageFont.ImageFont - | ImageFont.FreeTypeFont - | ImageFont.TransposedFont - | None - ) = None, - direction=None, - features=None, - language=None, - embedded_color=False, - *, - font_size=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, - text, - font=None, - anchor=None, - spacing=4, - align="left", - direction=None, - features=None, - language=None, - stroke_width=0, - embedded_color=False, - *, - font_size=None, - ) -> tuple[int, int, int, int]: - """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, - text, - font=None, - anchor=None, - spacing=4, - align="left", - direction=None, - features=None, - language=None, - stroke_width=0, - embedded_color=False, - *, - font_size=None, - ) -> tuple[int, int, int, int]: - if direction == "ttb": - msg = "ttb direction is unsupported for multiline text" - raise ValueError(msg) - - if anchor is None: - anchor = "la" - elif len(anchor) != 2: - msg = "anchor must be a 2 character string" - raise ValueError(msg) - elif anchor[1] in "tb": - msg = "anchor not supported for multiline text" - raise ValueError(msg) - - if font is None: - font = self._getfont(font_size) - - widths = [] - max_width: float = 0 - lines = self._multiline_split(text) - line_spacing = self._multiline_spacing(font, spacing, stroke_width) - 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) - - top = xy[1] - if anchor[1] == "m": - top -= (len(lines) - 1) * line_spacing / 2.0 - elif anchor[1] == "d": - top -= (len(lines) - 1) * line_spacing - - bbox: tuple[int, int, int, int] | None = None - - for idx, line in enumerate(lines): - left = xy[0] - width_difference = max_width - widths[idx] - - # first align left by anchor - if anchor[0] == "m": - left -= width_difference / 2.0 - elif anchor[0] == "r": - left -= width_difference - - # then align by align parameter - if align == "left": - pass - elif align == "center": - left += width_difference / 2.0 - elif align == "right": - left += width_difference - else: - msg = 'align must be "left", "center" or "right"' - raise ValueError(msg) - - bbox_line = self.textbbox( - (left, top), - 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]), - ) - - top += line_spacing - - 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(0, 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(0, len(second))) diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageDraw2.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageDraw2.py deleted file mode 100644 index e89a78b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageDraw2.py +++ /dev/null @@ -1,206 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# WCK-style drawing interface operations -# -# History: -# 2003-12-07 fl created -# 2005-05-15 fl updated; added to PIL as ImageDraw2 -# 2005-05-15 fl added text support -# 2005-05-20 fl added arc/chord/pieslice support -# -# Copyright (c) 2003-2005 by Secret Labs AB -# Copyright (c) 2003-2005 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# - - -""" -(Experimental) WCK-style drawing interface operations - -.. seealso:: :py:mod:`PIL.ImageDraw` -""" -from __future__ import annotations - -from typing import BinaryIO - -from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath -from ._typing import 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 = None - - def flush(self) -> Image.Image: - return self.image - - def render(self, op, xy, pen, brush=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: - xy = ImagePath.Path(xy) - xy.transform(self.transform) - # render the item - if op == "line": - self.draw.line(xy, fill=outline, width=width) - else: - getattr(self.draw, op)(xy, fill=fill, outline=outline) - - def settransform(self, offset): - """Sets a transformation offset.""" - (xoffset, yoffset) = offset - self.transform = (1, 0, xoffset, 0, 1, yoffset) - - def arc(self, xy, start, end, *options): - """ - 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, start, end, *options) - - def chord(self, xy, start, end, *options): - """ - 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, start, end, *options) - - def ellipse(self, xy, *options): - """ - Draws an ellipse inside the given bounding box. - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.ellipse` - """ - self.render("ellipse", xy, *options) - - def line(self, xy, *options): - """ - Draws a line between the coordinates in the ``xy`` list. - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.line` - """ - self.render("line", xy, *options) - - def pieslice(self, xy, start, end, *options): - """ - 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, start, end, *options) - - def polygon(self, xy, *options): - """ - 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, *options) - - def rectangle(self, xy, *options): - """ - Draws a rectangle. - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.rectangle` - """ - self.render("rectangle", xy, *options) - - def text(self, xy, text, font): - """ - Draws the string at the given position. - - .. seealso:: :py:meth:`PIL.ImageDraw.ImageDraw.text` - """ - if self.transform: - xy = ImagePath.Path(xy) - xy.transform(self.transform) - self.draw.text(xy, text, font=font.font, fill=font.color) - - def textbbox(self, xy, text, font): - """ - 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: - xy = ImagePath.Path(xy) - xy.transform(self.transform) - return self.draw.textbbox(xy, text, font=font.font) - - def textlength(self, text, font): - """ - 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/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageEnhance.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageEnhance.py deleted file mode 100644 index d7e99a9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageEnhance.py +++ /dev/null @@ -1,107 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# image enhancement classes -# -# For a background, see "Image Processing By Interpolation and -# Extrapolation", Paul Haeberli and Douglas Voorhies. Available -# at http://www.graficaobscura.com/interp/index.html -# -# History: -# 1996-03-23 fl Created -# 2009-06-16 fl Fixed mean calculation -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image, ImageFilter, ImageStat - - -class _Enhance: - image: Image.Image - degenerate: Image.Image - - def enhance(self, factor: float) -> Image.Image: - """ - Returns an enhanced image. - - :param factor: A floating point value controlling the enhancement. - Factor 1.0 always returns a copy of the original image, - lower factors mean less color (brightness, contrast, - etc), and higher values more. There are no restrictions - on this value. - :rtype: :py:class:`~PIL.Image.Image` - """ - return Image.blend(self.degenerate, self.image, factor) - - -class Color(_Enhance): - """Adjust image color balance. - - This class can be used to adjust the colour balance of an image, in - a manner similar to the controls on a colour TV set. An enhancement - factor of 0.0 gives a black and white image. A factor of 1.0 gives - the original image. - """ - - def __init__(self, image: Image.Image) -> None: - self.image = image - self.intermediate_mode = "L" - if "A" in image.getbands(): - self.intermediate_mode = "LA" - - self.degenerate = image.convert(self.intermediate_mode).convert(image.mode) - - -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 - mean = int(ImageStat.Stat(image.convert("L")).mean[0] + 0.5) - self.degenerate = Image.new("L", image.size, mean).convert(image.mode) - - if "A" in image.getbands(): - self.degenerate.putalpha(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/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageFile.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageFile.py deleted file mode 100644 index 69e7ee5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageFile.py +++ /dev/null @@ -1,810 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# base class for image file handlers -# -# history: -# 1995-09-09 fl Created -# 1996-03-11 fl Fixed load mechanism. -# 1996-04-15 fl Added pcx/xbm decoders. -# 1996-04-30 fl Added encoders. -# 1996-12-14 fl Added load helpers -# 1997-01-11 fl Use encode_to_file where possible -# 1997-08-27 fl Flush output in _save -# 1998-03-05 fl Use memory mapping for some modes -# 1999-02-04 fl Use memory mapping also for "I;16" and "I;16B" -# 1999-05-31 fl Added image parser -# 2000-10-12 fl Set readonly flag on memory-mapped images -# 2002-03-20 fl Use better messages for common decoder errors -# 2003-04-21 fl Fall back on mmap/map_buffer if map is not available -# 2003-10-30 fl Added StubImageFile class -# 2004-02-25 fl Made incremental parser more robust -# -# Copyright (c) 1997-2004 by Secret Labs AB -# Copyright (c) 1995-2004 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import abc -import io -import itertools -import struct -import sys -from typing import IO, Any, NamedTuple - -from . import Image -from ._deprecate import deprecate -from ._util import is_path - -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): - # sort on offset - return t[2] - - -class _Tile(NamedTuple): - codec_name: str - extents: tuple[int, int, int, int] - offset: int - args: tuple[Any, ...] | str | None - - -# -# -------------------------------------------------------------------- -# ImageFile base class - - -class ImageFile(Image.Image): - """Base class for image file format handlers.""" - - def __init__(self, fp=None, filename=None): - super().__init__() - - self._min_frame = 0 - - self.custom_mimetype = None - - self.tile = None - """ A list of tile descriptors, or ``None`` """ - - self.readonly = 1 # until we know better - - self.decoderconfig = () - self.decodermaxblock = MAXBLOCK - - if is_path(fp): - # filename - self.fp = open(fp, "rb") - self.filename = fp - self._exclusive_fp = True - else: - # stream - self.fp = fp - self.filename = filename - # can be overridden - self._exclusive_fp = None - - 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 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 __setstate__(self, state): - self.tile = [] - 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): - """Load image data based on tile list""" - - if self.tile is None: - msg = "cannot load this image" - raise OSError(msg) - - pixel = Image.Image.load(self) - if not self.tile: - return pixel - - self.map = None - use_mmap = self.filename and len(self.tile) == 1 - # As of pypy 2.1.0, memory mapping was failing here. - use_mmap = use_mmap and not hasattr(sys, "pypy_version_info") - - readonly = 0 - - # look for read/seek overrides - try: - read = self.load_read - # don't use mmap if there are custom read/seek functions - use_mmap = False - except AttributeError: - read = self.fp.read - - try: - seek = self.load_seek - use_mmap = False - except AttributeError: - 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 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) - - try: - # FIXME: This is a hack to handle TIFF's JpegTables tag. - prefix = self.tile_prefix - except AttributeError: - 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 decoder_name, extents, offset, args in 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: - try: - s = read(self.decodermaxblock) - 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 not self.im or self.im.mode != self.mode or self.im.size != self.size: - 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): - 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 >= self.n_frames + self._min_frame - ) - ): - msg = "attempt to seek outside sequence" - raise EOFError(msg) - - return self.tell() != frame - - -class StubHandler: - def open(self, im: StubImageFile) -> None: - pass - - @abc.abstractmethod - def load(self, im: StubImageFile) -> Image.Image: - pass - - -class StubImageFile(ImageFile): - """ - 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. - """ - - def _open(self) -> None: - msg = "StubImageFile subclass must implement _open" - raise NotImplementedError(msg) - - def load(self): - 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__ - self.__dict__ = image.__dict__ - return image.load() - - def _load(self) -> StubHandler | None: - """(Hook) Find actual image loader.""" - msg = "StubImageFile subclass must implement _load" - raise NotImplementedError(msg) - - -class Parser: - """ - Incremental image parser. This class implements the standard - feed/close consumer interface. - """ - - incremental = None - image: Image.Image | None = None - data = None - decoder = 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): - """ - (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): - return self - - def __exit__(self, *args: object) -> None: - self.close() - - def close(self): - """ - (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, fp, tile, bufsize=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, fp, tile: list[_Tile], bufsize, fh, exc=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 - 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, size): - """ - 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 - data = [] - remaining_size = size - while remaining_size > 0: - block = fp.read(min(remaining_size, SAFEBLOCK)) - if not block: - break - data.append(block) - remaining_size -= len(block) - if sum(len(d) for d in data) < size: - msg = "Truncated File Read" - raise OSError(msg) - return b"".join(data) - - -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, *args): - self.im = None - self.state = PyCodecState() - self.fd = None - self.mode = mode - self.init(args) - - def init(self, args): - """ - Override to perform codec specific initialization - - :param args: Array of args 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): - """ - 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, 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) -> 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=None) -> 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 - :returns: None - """ - - if not rawmode: - rawmode = self.mode - d = Image._getdecoder(self.mode, "raw", rawmode) - 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, bufsize): - """ - :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: - fh.write(buf[status:]) - return errcode diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageFilter.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageFilter.py deleted file mode 100644 index e18b4a4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageFilter.py +++ /dev/null @@ -1,604 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# standard filters -# -# History: -# 1995-11-27 fl Created -# 2002-06-08 fl Added rank and mode filters -# 2003-09-15 fl Fixed rank calculation in rank filter; added expand call -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1995-2002 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import abc -import functools -from types import ModuleType -from typing import TYPE_CHECKING, Any, Callable, Sequence, cast - -if TYPE_CHECKING: - from . import _imaging - from ._typing import NumpyArray - - -class Filter: - @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 = [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[0], - self.size[1], - self.size[2], - self.table, - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageFont.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageFont.py deleted file mode 100644 index d260eef..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageFont.py +++ /dev/null @@ -1,1290 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PIL raster font management -# -# History: -# 1996-08-07 fl created (experimental) -# 1997-08-25 fl minor adjustments to handle fonts from pilfont 0.3 -# 1999-02-06 fl rewrote most font management stuff in C -# 1999-03-17 fl take pth files into account in load_path (from Richard Jones) -# 2001-02-17 fl added freetype support -# 2001-05-09 fl added TransposedFont wrapper class -# 2002-03-04 fl make sure we have a "L" or "1" font -# 2002-12-04 fl skip non-directory entries in the system path -# 2003-04-29 fl add embedded default font -# 2003-09-27 fl added support for truetype charmap encodings -# -# Todo: -# Adapt to PILFONT2 format (16-bit fonts, compressed, single file) -# -# Copyright (c) 1997-2003 by Secret Labs AB -# Copyright (c) 1996-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# - -from __future__ import annotations - -import base64 -import os -import sys -import warnings -from enum import IntEnum -from io import BytesIO -from types import ModuleType -from typing import IO, TYPE_CHECKING, Any, BinaryIO - -from . import Image -from ._typing import StrOrBytesPath -from ._util import DeferredError, is_path - -if TYPE_CHECKING: - from . import ImageFile - from ._imaging import ImagingFont - from ._imagingft import Font - - -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 - for ext in (".png", ".gif", ".pbm"): - if image: - image.close() - try: - fullname = os.path.splitext(filename)[0] + ext - image = Image.open(fullname) - except Exception: - pass - else: - if image and image.mode in ("1", "L"): - break - else: - if image: - image.close() - msg = "cannot find glyph data file" - 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, mode="", *args, **kwargs): - """ - 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 | None = None, - 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 = "font size must be greater than 0" - raise ValueError(msg) - - self.path = font - self.size = size - self.index = index - self.encoding = encoding - - if layout_engine not in (Layout.BASIC, Layout.RAQM): - layout_engine = Layout.BASIC - if core.HAVE_RAQM: - layout_engine = Layout.RAQM - elif layout_engine == Layout.RAQM and not core.HAVE_RAQM: - warnings.warn( - "Raqm layout was requested, but Raqm is not available. " - "Falling back to basic layout." - ) - layout_engine = Layout.BASIC - - self.layout_engine = layout_engine - - def load_from_bytes(f): - self.font_bytes = f.read() - self.font = core.getfont( - "", size, index, encoding, self.font_bytes, layout_engine - ) - - if is_path(font): - font = os.path.realpath(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(font) - - def __getstate__(self): - return [self.path, self.size, self.index, self.encoding, self.layout_engine] - - def __setstate__(self, state): - path, size, index, encoding, layout_engine = state - self.__init__(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="", direction=None, features=None, language=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, - mode="", - direction=None, - features=None, - language=None, - stroke_width=0, - anchor=None, - ink=0, - start=None, - ): - """ - 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="", - direction=None, - features=None, - language=None, - stroke_width=0, - anchor=None, - ink=0, - start=None, - *args, - **kwargs, - ): - """ - 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, height): - 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, - anchor, - ink, - start[0], - start[1], - ) - - def font_variant( - self, font=None, size=None, index=None, encoding=None, layout_engine=None - ): - """ - 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): - """ - :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): - """ - :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, orientation=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, mode="", *args, **kwargs): - 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, *args, **kwargs): - # 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, **kwargs) -> 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. - - :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 | None = None, - 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. - - 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 | None) -> 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 = "cannot find font file" - 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/font/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/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageGrab.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageGrab.py deleted file mode 100644 index e27ca7e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageGrab.py +++ /dev/null @@ -1,194 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# screen grabber -# -# History: -# 2001-04-26 fl created -# 2001-09-17 fl use builtin driver, if present -# 2002-11-19 fl added grabclipboard support -# -# Copyright (c) 2001-2002 by Secret Labs AB -# Copyright (c) 2001-2002 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -import os -import shutil -import subprocess -import sys -import tempfile - -from . import Image - - -def grab( - bbox: tuple[int, int, int, int] | None = None, - include_layered_windows: bool = False, - all_screens: bool = False, - xdisplay: str | 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": - offset, size, data = Image.core.grabscreen_win32( - include_layered_windows, all_screens - ) - 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") - and shutil.which("gnome-screenshot") - ): - fh, filepath = tempfile.mkstemp(".png") - os.close(fh) - subprocess.call(["gnome-screenshot", "-f", 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": - fh, filepath = tempfile.mkstemp(".png") - os.close(fh) - commands = [ - 'set theFile to (open for access POSIX file "' - + filepath - + '" with write permission)', - "try", - " write (the clipboard as «class PNGf») to theFile", - "end try", - "close access theFile", - ] - script = ["osascript"] - for command in commands: - script += ["-e", command] - subprocess.call(script) - - im = None - if os.stat(filepath).st_size != 0: - im = Image.open(filepath) - im.load() - os.unlink(filepath) - return im - 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("utf-16le").split("\0") - else: - files = data[o:].decode("mbcs").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/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageMath.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageMath.py deleted file mode 100644 index 6664434..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageMath.py +++ /dev/null @@ -1,357 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# a simple math add-on for the Python Imaging Library -# -# History: -# 1999-02-15 fl Original PIL Plus release -# 2005-05-05 fl Simplified and cleaned up for PIL 1.1.6 -# 2005-09-12 fl Fixed int() and float() for Python 2.4.1 -# -# Copyright (c) 1999-2005 by Secret Labs AB -# Copyright (c) 2005 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import builtins -from 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) - im_1.load() - 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.im.id, im_1.im.id) - 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) - im_1.load() - im_2.load() - 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.im.id, im_1.im.id, im_2.im.id) - 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): - return self.apply("eq", self, other) - - def __ne__(self, other): - 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. You - can either use a dictionary, or one or more keyword - arguments. - :return: The expression result. This is usually an image object, but can - also be an integer, a floating point value, or a pixel tuple, - depending on the expression. - """ - - args: dict[str, Any] = ops.copy() - args.update(options) - args.update(kw) - for k, v in args.items(): - if hasattr(v, "im"): - 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. 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. - """ - - # build execution namespace - args: dict[str, Any] = ops.copy() - for k in list(options.keys()) + list(kw.keys()): - 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 hasattr(v, "im"): - 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/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageMode.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageMode.py deleted file mode 100644 index 92a08d2..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageMode.py +++ /dev/null @@ -1,92 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# standard mode descriptors -# -# History: -# 2006-03-20 fl Added -# -# Copyright (c) 2006 by Secret Labs AB. -# Copyright (c) 2006 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import sys -from functools import lru_cache -from typing import NamedTuple - -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.im.id, outimage.im.id) - 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.im.id) - - 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.im.id) - - 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/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageOps.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageOps.py deleted file mode 100644 index a84c083..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageOps.py +++ /dev/null @@ -1,728 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# standard image operations -# -# History: -# 2001-10-20 fl Created -# 2001-10-23 fl Added autocontrast operator -# 2001-12-18 fl Added Kevin's fit operator -# 2004-03-14 fl Fixed potential division by zero in equalize -# 2005-05-05 fl Fixed equalize for low number of values -# -# Copyright (c) 2001-2004 by Secret Labs AB -# Copyright (c) 2001-2004 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import functools -import operator -import re -from typing import Protocol, Sequence, cast - -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(0, 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(0, 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(0, midpoint - blackpoint) - range_map2 = range(0, 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(0, 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: - out.putpalette(resized.getpalette()) - 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) - - -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: - transposed_image = image.transpose(method) - if in_place: - image.im = transposed_image.im - image.pyaccess = None - image._size = transposed_image._size - 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] - exif_image.info[key] = ( - re.sub(pattern, "", value) - if isinstance(value, str) - else re.sub(pattern.encode(), b"", value) - ) - if not in_place: - return transposed_image - elif not in_place: - return image.copy() - return None diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImagePalette.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImagePalette.py deleted file mode 100644 index ed38285..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImagePalette.py +++ /dev/null @@ -1,284 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# image palette object -# -# History: -# 1996-03-11 fl Rewritten. -# 1997-01-03 fl Up and running. -# 1997-08-23 fl Added load hack -# 2001-04-16 fl Fixed randint shadow bug in random() -# -# Copyright (c) 1997-2001 by Secret Labs AB -# Copyright (c) 1996-1997 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import array -from typing import IO, TYPE_CHECKING, Sequence - -from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile - -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, 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/plotter-app/venv/lib/python3.8/site-packages/PIL/ImagePath.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImagePath.py deleted file mode 100644 index 77e8a60..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImagePath.py +++ /dev/null @@ -1,20 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# path interface -# -# History: -# 1996-11-04 fl Created -# 2002-04-14 fl Added documentation stub class -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image - -Path = Image.core.path diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageQt.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageQt.py deleted file mode 100644 index 35a3776..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageQt.py +++ /dev/null @@ -1,205 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# a simple Qt image interface. -# -# history: -# 2006-06-03 fl: created -# 2006-06-04 fl: inherit from QImage instead of wrapping it -# 2006-06-05 fl: removed toimage helper; move string support to ImageQt -# 2013-11-13 fl: add support for Qt5 (aurelien.ballier@cyclonit.com) -# -# Copyright (c) 2006 by Secret Labs AB -# Copyright (c) 2006 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import sys -from io import BytesIO -from typing import Callable - -from . import Image -from ._util import is_path - -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: - QBuffer: type - QIODevice: type - QImage: type - QPixmap: type - 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, g, b, a=255): - """(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): - """ - :param im: QImage or PIL ImageQt object - """ - buffer = QBuffer() - if qt_version == "6": - try: - qt_openmode = QIODevice.OpenModeFlag - except AttributeError: - qt_openmode = QIODevice.OpenMode - else: - qt_openmode = QIODevice - buffer.open(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): - return fromqimage(im) - - -def align8to32(bytes, width, mode): - """ - 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): - 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 - - qt_format = QImage.Format if qt_version == "6" else QImage - if im.mode == "1": - format = qt_format.Format_Mono - elif im.mode == "L": - format = qt_format.Format_Indexed8 - colortable = [rgb(i, i, i) for i in range(256)] - elif im.mode == "P": - format = qt_format.Format_Indexed8 - palette = im.getpalette() - 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 = qt_format.Format_RGB32 - elif im.mode == "RGBA": - data = im.tobytes("raw", "BGRA") - format = qt_format.Format_ARGB32 - elif im.mode == "I;16": - im = im.point(lambda i: i * 256) - - format = qt_format.Format_Grayscale16 - else: - if exclusive_fp: - im.close() - msg = f"unsupported image mode {repr(im.mode)}" - raise ValueError(msg) - - size = im.size - __data = data or align8to32(im.tobytes(), size[0], im.mode) - if exclusive_fp: - im.close() - return {"data": __data, "size": size, "format": format, "colortable": colortable} - - -if qt_is_installed: - - class ImageQt(QImage): - def __init__(self, im): - """ - 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) -> ImageQt: - return ImageQt(im) - - -def toqpixmap(im): - qimage = toqimage(im) - return QPixmap.fromImage(qimage) diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageSequence.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageSequence.py deleted file mode 100644 index 2c18502..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageSequence.py +++ /dev/null @@ -1,86 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# sequence support classes -# -# history: -# 1997-02-20 fl Created -# -# Copyright (c) 1997 by Secret Labs AB. -# Copyright (c) 1997 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# - -## -from __future__ import annotations - -from 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): - 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/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageShow.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageShow.py deleted file mode 100644 index 037d6f4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageShow.py +++ /dev/null @@ -1,363 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# im.show() drivers -# -# History: -# 2008-04-06 fl Created -# -# Copyright (c) Secret Labs AB 2008. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import abc -import os -import shutil -import subprocess -import sys -from shlex import quote -from typing import Any - -from . import Image - -_viewers = [] - - -def register(viewer, 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. - """ - try: - if issubclass(viewer, Viewer): - viewer = viewer() - except TypeError: - pass # raised if viewer wasn't a class - 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]) - executable = 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(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/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageStat.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageStat.py deleted file mode 100644 index 8bc5045..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageStat.py +++ /dev/null @@ -1,160 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# global image statistics -# -# History: -# 1996-04-05 fl Created -# 1997-05-21 fl Added mask; added rms, var, stddev attributes -# 1997-08-05 fl Added median -# 1998-07-05 hk Fixed integer overflow error -# -# Notes: -# This class shows how to implement delayed evaluation of attributes. -# To get a certain value, simply access the corresponding attribute. -# The __getattr__ dispatcher takes care of the rest. -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996-97. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import math -from functools import cached_property - -from . import Image - - -class Stat: - def __init__( - self, image_or_list: Image.Image | list[int], mask: Image.Image | None = None - ) -> None: - """ - Calculate statistics for the given image. If a mask is included, - only the regions covered by that mask are included in the - statistics. You can also pass in a previously calculated histogram. - - :param image: A PIL image, or a precalculated histogram. - - .. note:: - - For a PIL image, calculations rely on the - :py:meth:`~PIL.Image.Image.histogram` method. The pixel counts are - grouped into 256 bins, even if the image has more than 8 bits per - channel. So ``I`` and ``F`` mode images have a maximum ``mean``, - ``median`` and ``rms`` of 255, and cannot have an ``extrema`` maximum - of more than 255. - - :param mask: An optional mask. - """ - if isinstance(image_or_list, Image.Image): - self.h = image_or_list.histogram(mask) - elif isinstance(image_or_list, list): - self.h = image_or_list - else: - msg = "first argument must be image or list" # type: ignore[unreachable] - raise TypeError(msg) - self.bands = list(range(len(self.h) // 256)) - - @cached_property - def extrema(self) -> list[tuple[int, int]]: - """ - Min/max values for each band in the image. - - .. note:: - This relies on the :py:meth:`~PIL.Image.Image.histogram` method, and - simply returns the low and high bins used. This is correct for - images with 8 bits per channel, but fails for other modes such as - ``I`` or ``F``. Instead, use :py:meth:`~PIL.Image.Image.getextrema` to - return per-band extrema for the image. This is more correct and - efficient because, for non-8-bit modes, the histogram method uses - :py:meth:`~PIL.Image.Image.getextrema` to determine the bins used. - """ - - def minmax(histogram: list[int]) -> tuple[int, int]: - res_min, res_max = 255, 0 - for i in range(256): - if histogram[i]: - res_min = i - break - for i in range(255, -1, -1): - if histogram[i]: - res_max = i - break - return res_min, res_max - - return [minmax(self.h[i:]) for i in range(0, len(self.h), 256)] - - @cached_property - def count(self) -> list[int]: - """Total number of pixels for each band in the image.""" - return [sum(self.h[i : i + 256]) for i in range(0, len(self.h), 256)] - - @cached_property - def sum(self) -> list[float]: - """Sum of all pixels for each band in the image.""" - - v = [] - for i in range(0, len(self.h), 256): - layer_sum = 0.0 - for j in range(256): - layer_sum += j * self.h[i + j] - v.append(layer_sum) - return v - - @cached_property - def sum2(self) -> list[float]: - """Squared sum of all pixels for each band in the image.""" - - v = [] - for i in range(0, len(self.h), 256): - sum2 = 0.0 - for j in range(256): - sum2 += (j**2) * float(self.h[i + j]) - v.append(sum2) - return v - - @cached_property - def mean(self) -> list[float]: - """Average (arithmetic mean) pixel level for each band in the image.""" - return [self.sum[i] / self.count[i] 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/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageTk.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageTk.py deleted file mode 100644 index 90defdb..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageTk.py +++ /dev/null @@ -1,284 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# a Tk display interface -# -# History: -# 96-04-08 fl Created -# 96-09-06 fl Added getimage method -# 96-11-01 fl Rewritten, removed image attribute and crop method -# 97-05-09 fl Use PyImagingPaste method instead of image type -# 97-05-12 fl Minor tweaks to match the IFUNC95 interface -# 97-05-17 fl Support the "pilbitmap" booster patch -# 97-06-05 fl Added file= and data= argument to image constructors -# 98-03-09 fl Added width and height methods to Image classes -# 98-07-02 fl Use default mode for "P" images without palette attribute -# 98-07-02 fl Explicitly destroy Tkinter image objects -# 99-07-24 fl Support multiple Tk interpreters (from Greg Couch) -# 99-07-26 fl Automatically hook into Tkinter (if possible) -# 99-08-15 fl Hook uses _imagingtk instead of _imaging -# -# Copyright (c) 1997-1999 by Secret Labs AB -# Copyright (c) 1996-1997 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import tkinter -from io import BytesIO - -from . import Image - -# -------------------------------------------------------------------- -# Check for Tkinter interface hooks - -_pilbitmap_ok = None - - -def _pilbitmap_check() -> int: - global _pilbitmap_ok - if _pilbitmap_ok is None: - try: - im = Image.new("1", (1, 1)) - tkinter.BitmapImage(data=f"PIL:{im.im.id}") - _pilbitmap_ok = 1 - except tkinter.TclError: - _pilbitmap_ok = 0 - return _pilbitmap_ok - - -def _get_image_from_kw(kw): - source = None - if "file" in kw: - source = kw.pop("file") - elif "data" in kw: - source = BytesIO(kw.pop("data")) - if source: - return Image.open(source) - - -def _pyimagingtkcall(command, photo, id): - tk = photo.tk - try: - tk.call(command, photo, id) - 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, id) - - -# -------------------------------------------------------------------- -# 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=None, size=None, **kw): - # Tk compatibility: file or data - if image is None: - image = _get_image_from_kw(kw) - - if hasattr(image, "mode") and hasattr(image, "size"): - # got an image instead of a mode - mode = image.mode - if mode == "P": - # palette mapped data - image.apply_transparency() - image.load() - try: - mode = image.palette.mode - except AttributeError: - mode = "RGB" # default - size = image.size - kw["width"], kw["height"] = size - else: - mode = image - image = None - - 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: - name = self.__photo.name - 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 - im.load() - image = im.im - if image.isblock() and im.mode == self.__mode: - block = image - else: - block = image.new_block(self.__mode, im.size) - image.convert2(block, image) # convert directly between buffers - - _pyimagingtkcall("PyImagingPhoto", self.__photo, block.id) - - -# -------------------------------------------------------------------- -# 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=None, **kw): - # Tk compatibility: file or data - if image is None: - image = _get_image_from_kw(kw) - - self.__mode = image.mode - self.__size = image.size - - if _pilbitmap_check(): - # fast way (requires the pilbitmap booster patch) - image.load() - kw["data"] = f"PIL:{image.im.id}" - self.__im = image # must keep a reference - else: - # slow but safe way - kw["data"] = image.tobitmap() - self.__photo = tkinter.BitmapImage(**kw) - - def __del__(self) -> None: - name = self.__photo.name - 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())) - block = im.im - - _pyimagingtkcall("PyImagingPhotoGet", photo, block.id) - - return im - - -def _show(image, title): - """Helper for the Image.show method.""" - - class UI(tkinter.Label): - def __init__(self, master, im): - if im.mode == "1": - self.image = BitmapImage(im, foreground="white", master=master) - else: - self.image = PhotoImage(im, master=master) - super().__init__(master, image=self.image, bg="black", bd=0) - - if not tkinter._default_root: - msg = "tkinter not initialized" - raise OSError(msg) - top = tkinter.Toplevel() - if title: - top.title(title) - UI(top, image).pack() diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageTransform.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageTransform.py deleted file mode 100644 index ffd7916..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageTransform.py +++ /dev/null @@ -1,135 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# transform wrappers -# -# History: -# 2002-04-08 fl Created -# -# Copyright (c) 2002 by Secret Labs AB -# Copyright (c) 2002 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from typing import Any, Sequence - -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 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 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/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageWin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageWin.py deleted file mode 100644 index 978c5a9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImageWin.py +++ /dev/null @@ -1,238 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# a Windows DIB display interface -# -# History: -# 1996-05-20 fl Created -# 1996-09-20 fl Fixed subregion exposure -# 1997-09-21 fl Added draw primitive (for tzPrint) -# 2003-05-21 fl Added experimental Window/ImageWindow classes -# 2003-09-05 fl Added fromstring/tostring methods -# -# Copyright (c) Secret Labs AB 1997-2003. -# Copyright (c) Fredrik Lundh 1996-2003. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image - - -class HDC: - """ - Wraps an HDC integer. The resulting object can be passed to the - :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` - methods. - """ - - def __init__(self, dc: int) -> None: - self.dc = dc - - def __int__(self) -> int: - return self.dc - - -class HWND: - """ - Wraps an HWND integer. The resulting object can be passed to the - :py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose` - methods, instead of a DC. - """ - - def __init__(self, wnd: int) -> None: - self.wnd = wnd - - def __int__(self) -> int: - return self.wnd - - -class Dib: - """ - A Windows bitmap with the given mode and size. The mode can be one of "1", - "L", "P", or "RGB". - - If the display requires a palette, this constructor creates a suitable - palette and associates it with the image. For an "L" image, 128 graylevels - are allocated. For an "RGB" image, a 6x6x6 colour cube is used, together - with 20 graylevels. - - To make sure that palettes work properly under Windows, you must call the - ``palette`` method upon certain events from Windows. - - :param image: Either a PIL image, or a mode string. If a mode string is - used, a size must also be given. The mode can be one of "1", - "L", "P", or "RGB". - :param size: If the first argument is a mode string, this - defines the size of the image. - """ - - def __init__( - self, image: Image.Image | str, size: tuple[int, int] | list[int] | None = None - ) -> None: - if isinstance(image, str): - mode = image - image = "" - 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): - """ - 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. - """ - if isinstance(handle, HWND): - dc = self.image.getdc(handle) - try: - result = self.image.expose(dc) - finally: - self.image.releasedc(handle, dc) - else: - result = self.image.expose(handle) - return result - - def draw(self, handle, dst, src=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 not src: - src = (0, 0) + self.size - if isinstance(handle, HWND): - dc = self.image.getdc(handle) - try: - result = self.image.draw(dc, dst, src) - finally: - self.image.releasedc(handle, dc) - else: - result = self.image.draw(handle, dst, src) - return result - - def query_palette(self, handle): - """ - 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: A true value if one or more entries were changed (this - indicates that the image should be redrawn). - """ - if isinstance(handle, HWND): - handle = self.image.getdc(handle) - try: - result = self.image.query_palette(handle) - finally: - self.image.releasedc(handle, handle) - else: - result = self.image.query_palette(handle) - 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, *args): - return getattr(self, f"ui_handle_{action}")(*args) - - def ui_handle_clear(self, dc, x0, y0, x1, y1): - pass - - def ui_handle_damage(self, x0, y0, x1, y1): - pass - - def ui_handle_destroy(self) -> None: - pass - - def ui_handle_repair(self, dc, x0, y0, x1, y1): - pass - - def ui_handle_resize(self, width, height): - pass - - def mainloop(self) -> None: - Image.core.eventloop() - - -class ImageWindow(Window): - """Create an image window which displays the given image.""" - - def __init__(self, image, title="PIL"): - 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, x0, y0, x1, y1): - self.image.draw(dc, (x0, y0, x1, y1)) diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImtImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/ImtImagePlugin.py deleted file mode 100644 index abb3fb7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/ImtImagePlugin.py +++ /dev/null @@ -1,103 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# IM Tools support for PIL -# -# history: -# 1996-05-27 fl Created (read 8-bit images only) -# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.2) -# -# Copyright (c) Secret Labs AB 1997-2001. -# Copyright (c) Fredrik Lundh 1996-2001. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import re - -from . import Image, ImageFile - -# -# -------------------------------------------------------------------- - -field = re.compile(rb"([a-z]*) ([^ \r\n]*)") - - -## -# Image plugin for IM Tools images. - - -class ImtImageFile(ImageFile.ImageFile): - format = "IMT" - format_description = "IM Tools" - - def _open(self) -> None: - # Quick rejection: if there's not a LF among the first - # 100 bytes, this is (probably) not a text header. - - assert self.fp is not None - - buffer = self.fp.read(100) - if b"\n" not in buffer: - msg = "not an IM file" - raise SyntaxError(msg) - - xsize = ysize = 0 - - while True: - if buffer: - s = buffer[:1] - buffer = buffer[1:] - else: - s = self.fp.read(1) - if not s: - break - - if s == b"\x0C": - # image data begins - self.tile = [ - ( - "raw", - (0, 0) + self.size, - self.fp.tell() - len(buffer), - (self.mode, 0, 1), - ) - ] - - 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/plotter-app/venv/lib/python3.8/site-packages/PIL/IptcImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/IptcImagePlugin.py deleted file mode 100644 index 73df83b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/IptcImagePlugin.py +++ /dev/null @@ -1,235 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# IPTC/NAA file handling -# -# history: -# 1995-10-01 fl Created -# 1998-03-09 fl Cleaned up and added to PIL -# 2002-06-18 fl Added getiptcinfo helper -# -# Copyright (c) Secret Labs AB 1997-2002. -# Copyright (c) Fredrik Lundh 1995. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from io import BytesIO -from typing import Sequence - -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 = [("iptc", (0, 0) + self.size, offset, compression)] - - def load(self): - 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 - - -Image.register_open(IptcImageFile.format, IptcImageFile) - -Image.register_extension(IptcImageFile.format, ".iim") - - -def getiptcinfo(im): - """ - Get IPTC information from TIFF, JPEG, or IPTC file. - - :param im: An image containing IPTC data. - :returns: A dictionary containing IPTC information, or None if - no IPTC information block was found. - """ - from . import JpegImagePlugin, TiffImagePlugin - - data = None - - if isinstance(im, IptcImageFile): - # return info dictionary right away - return im.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.tagdata[TiffImagePlugin.IPTC_NAA_CHUNK] - except (AttributeError, KeyError): - pass - - if data is None: - return None # no properties - - # create an IptcImagePlugin object without initializing it - class FakeImage: - pass - - im = FakeImage() - im.__class__ = IptcImageFile - - # parse the IPTC information chunk - im.info = {} - im.fp = BytesIO(data) - - try: - im._open() - except (IndexError, KeyError): - pass # expected failure - - return im.info diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/Jpeg2KImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/Jpeg2KImagePlugin.py deleted file mode 100644 index e50cd77..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/Jpeg2KImagePlugin.py +++ /dev/null @@ -1,408 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# JPEG2000 file handling -# -# History: -# 2014-03-12 ajh Created -# 2021-06-30 rogermb Extract dpi information from the 'resc' header box -# -# Copyright (c) 2014 Coriolis Systems Limited -# Copyright (c) 2014 Alastair Houghton -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -import os -import struct -from typing import IO, Tuple, 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, length=-1): - 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) -> 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): - """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" - - 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") - 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") - bitdepths = header.read_fields(">" + ("B" * npc)) - if max(bitdepths) <= 8: - palette = ImagePalette.ImagePalette() - for i in range(ne): - palette.getcolor(header.read_fields(">" + ("B" * npc))) - 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") - 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) - 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"): - 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 = [ - ( - "jpeg2k", - (0, 0) + self.size, - 0, - (self.codec, self._reduce, self.layers, fd, length), - ) - ] - - def _parse_comment(self) -> None: - hdr = self.fp.read(2) - length = _binary.i16be(hdr) - self.fp.seek(length - 2, os.SEEK_CUR) - - 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 - def reduce(self): - # 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): - self._reduce = value - - def load(self): - 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] - t3 = (t[3][0], self._reduce, self.layers, t[3][3], t[3][4]) - self.tile = [(t[0], (0, 0) + self.size, t[2], t3)] - - return ImageFile.ImageFile.load(self) - - -def _accept(prefix: bytes) -> bool: - return ( - prefix[:4] == b"\xff\x4f\xff\x51" - or prefix[:12] == 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, [("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/plotter-app/venv/lib/python3.8/site-packages/PIL/JpegImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/JpegImagePlugin.py deleted file mode 100644 index b15bf06..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/JpegImagePlugin.py +++ /dev/null @@ -1,861 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# JPEG (JFIF) file handling -# -# See "Digital Compression and Coding of Continuous-Tone Still Images, -# Part 1, Requirements and Guidelines" (CCITT T.81 / ISO 10918-1) -# -# History: -# 1995-09-09 fl Created -# 1995-09-13 fl Added full parser -# 1996-03-25 fl Added hack to use the IJG command line utilities -# 1996-05-05 fl Workaround Photoshop 2.5 CMYK polarity bug -# 1996-05-28 fl Added draft support, JFIF version (0.1) -# 1996-12-30 fl Added encoder options, added progression property (0.2) -# 1997-08-27 fl Save mode 1 images as BW (0.3) -# 1998-07-12 fl Added YCbCr to draft and save methods (0.4) -# 1998-10-19 fl Don't hang on files using 16-bit DQT's (0.4.1) -# 2001-04-16 fl Extract DPI settings from JFIF files (0.4.2) -# 2002-07-01 fl Skip pad bytes before markers; identify Exif files (0.4.3) -# 2003-04-25 fl Added experimental EXIF decoder (0.5) -# 2003-06-06 fl Added experimental EXIF GPSinfo decoder -# 2003-09-13 fl Extract COM markers -# 2009-09-06 fl Added icc_profile support (from Florian Hoech) -# 2009-03-06 fl Changed CMYK handling; always use Adobe polarity (0.6) -# 2009-03-08 fl Added subsampling support (from Justin Huff). -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1995-1996 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import array -import io -import math -import os -import struct -import subprocess -import sys -import tempfile -import warnings -from 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 .JpegPresets import presets - -# -# Parser - - -def Skip(self: JpegImageFile, marker: int) -> None: - n = i16(self.fp.read(2)) - 2 - ImageFile._safe_read(self.fp, n) - - -def APP(self, marker): - # - # 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 = "APP%d" % (marker & 15) - - self.app[app] = s # compatibility - self.applist.append((app, s)) - - if marker == 0xFFE0 and s[:4] == 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 - self.info["jfif_unit"] = jfif_unit - self.info["jfif_density"] = jfif_density - elif marker == 0xFFE1 and s[:6] == 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[:29] == b"http://ns.adobe.com/xap/1.0/\x00": - self.info["xmp"] = s.split(b"\x00", 1)[1] - elif marker == 0xFFE2 and s[:5] == b"FPXR\0": - # extract FlashPix information (incomplete) - self.info["flashpix"] = s # FIXME: value will change - elif marker == 0xFFE2 and s[:12] == 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[:14] == 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 - data = { - "XResolution": i32(data, 0) / 65536, - "DisplayedUnitsX": i16(data, 4), - "YResolution": i32(data, 8) / 65536, - "DisplayedUnitsY": i16(data, 12), - } - photoshop[code] = data - offset += size - offset += offset & 1 # align - except struct.error: - break # insufficient data - - elif marker == 0xFFEE and s[:5] == 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[:4] == 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[:3] == 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): - 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 - - # JPEG specifics (internal) - self.layer = [] - self.huffman_dc = {} - self.huffman_ac = {} - self.quantization = {} - self.app = {} # compatibility - self.applist = [] - self.icclist = [] - - 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 = [("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 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 - - 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 - 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 = [(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[str, 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): - return _getmp(self) - - -def _getexif(self) -> dict[str, Any] | None: - if "exif" not in self.info: - return None - return self.getexif()._get_merged_dict() - - -def _getmp(self): - # 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[:4] == 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(0, 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): - # 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 hasattr(im, "layers") 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): - 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("H", table) - except TypeError as e: - msg = "Invalid quantization table" - raise ValueError(msg) from e - else: - qtables[idx] = list(table) - 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 - icc_profile = info.get("icc_profile") - if icc_profile: - ICC_OVERHEAD_LEN = 14 - MAX_DATA_BYTES_IN_MARKER = MAX_BYTES_IN_MARKER - ICC_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 + ICC_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[0], - dpi[1], - 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 - bufsize = 0 - 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(bufsize, len(exif) + 5, len(extra) + 1) - - ImageFile._save(im, fp, [("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=None, filename=None): - im = JpegImageFile(fp, filename) - try: - mpheader = im._getmp() - if 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/plotter-app/venv/lib/python3.8/site-packages/PIL/JpegPresets.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/JpegPresets.py deleted file mode 100644 index 3aefa07..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/JpegPresets.py +++ /dev/null @@ -1,242 +0,0 @@ -""" -JPEG quality settings equivalent to the Photoshop settings. -Can be used when saving JPEG files. - -The following presets are available by default: -``web_low``, ``web_medium``, ``web_high``, ``web_very_high``, ``web_maximum``, -``low``, ``medium``, ``high``, ``maximum``. -More presets can be added to the :py:data:`presets` dict if needed. - -To apply the preset, specify:: - - quality="preset_name" - -To apply only the quantization table:: - - qtables="preset_name" - -To apply only the subsampling setting:: - - subsampling="preset_name" - -Example:: - - im.save("image_name.jpg", quality="web_high") - -Subsampling ------------ - -Subsampling is the practice of encoding images by implementing less resolution -for chroma information than for luma information. -(ref.: https://en.wikipedia.org/wiki/Chroma_subsampling) - -Possible subsampling values are 0, 1 and 2 that correspond to 4:4:4, 4:2:2 and -4:2:0. - -You can get the subsampling of a JPEG with the -:func:`.JpegImagePlugin.get_sampling` function. - -In JPEG compressed data a JPEG marker is used instead of an EXIF tag. -(ref.: https://web.archive.org/web/20240227115053/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/plotter-app/venv/lib/python3.8/site-packages/PIL/McIdasImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/McIdasImagePlugin.py deleted file mode 100644 index 2797223..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/McIdasImagePlugin.py +++ /dev/null @@ -1,78 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# Basic McIdas support for PIL -# -# History: -# 1997-05-05 fl Created (8-bit images only) -# 2009-03-08 fl Added 16/32-bit support. -# -# Thanks to Richard Jones and Craig Swank for specs and samples. -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1997. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import struct - -from . import Image, ImageFile - - -def _accept(prefix: bytes) -> bool: - return prefix[:8] == 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] + list(struct.unpack("!64i", s)) - - # get mode - if w[11] == 1: - mode = rawmode = "L" - elif w[11] == 2: - # FIXME: add memory map support - mode = "I" - 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 = [("raw", (0, 0) + self.size, offset, (rawmode, stride, 1))] - - -# -------------------------------------------------------------------- -# registry - -Image.register_open(McIdasImageFile.format, McIdasImageFile, _accept) - -# no default extension diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/MicImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/MicImagePlugin.py deleted file mode 100644 index 0723988..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/MicImagePlugin.py +++ /dev/null @@ -1,107 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# Microsoft Image Composer support for PIL -# -# Notes: -# uses TiffImagePlugin.py to read the actual image streams -# -# History: -# 97-01-20 fl Created -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1997. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import olefile - -from . import Image, TiffImagePlugin - -# -# -------------------------------------------------------------------- - - -def _accept(prefix: bytes) -> bool: - return prefix[:8] == 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][-4:] == ".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): - if not self._seek_check(frame): - return - try: - filename = self.images[frame] - except IndexError as e: - msg = "no such frame" - raise EOFError(msg) from e - - 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/plotter-app/venv/lib/python3.8/site-packages/PIL/MpegImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/MpegImagePlugin.py deleted file mode 100644 index ad4d3e9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/MpegImagePlugin.py +++ /dev/null @@ -1,88 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# MPEG file handling -# -# History: -# 95-09-09 fl Created -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1995. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image, ImageFile -from ._binary import i8 -from ._typing import SupportsRead - -# -# Bitstream parser - - -class BitStream: - def __init__(self, fp: SupportsRead[bytes]) -> None: - self.fp = fp - self.bits = 0 - self.bitbuffer = 0 - - def next(self) -> int: - return i8(self.fp.read(1)) - - def peek(self, bits: int) -> int: - while self.bits < bits: - c = self.next() - if c < 0: - self.bits = 0 - continue - self.bitbuffer = (self.bitbuffer << 8) + c - 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[:4] == 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/plotter-app/venv/lib/python3.8/site-packages/PIL/MpoImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/MpoImagePlugin.py deleted file mode 100644 index f215706..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/MpoImagePlugin.py +++ /dev/null @@ -1,180 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# MPO file handling -# -# See "Multi-Picture Format" (CIPA DC-007-Translation 2009, Standard of the -# Camera & Imaging Products Association) -# -# The multi-picture object combines multiple JPEG images (with a modified EXIF -# data format) into a single file. While it can theoretically be used much like -# a GIF animation, it is commonly used to represent 3D photographs and is (as -# of this writing) the most commonly used format by 3D cameras. -# -# History: -# 2014-03-13 Feneric Created -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import itertools -import os -import struct -from typing import IO - -from . import ( - Image, - ImageSequence, - JpegImagePlugin, - TiffImagePlugin, -) -from ._binary import o32le - - -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] = [] - for imSequence in itertools.chain([im], append_images): - for im_frame in ImageSequence.Iterator(imSequence): - if not offsets: - # APP2 marker - im_frame.encoderinfo["extra"] = ( - b"\xFF\xE2" + struct.pack(">H", 6 + 82) + b"MPF\0" + b" " * 82 - ) - 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: - im_frame.save(fp, "JPEG") - 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=None): - self.mpinfo = mpheader if mpheader is not None else self._getmp() - 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: - self._fp.seek(pos) - - def seek(self, frame: int) -> None: - if not self._seek_check(frame): - return - 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 = [("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, mpheader=None): - """ - 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 - jpeg_instance._after_jpeg_open(mpheader) - return jpeg_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/plotter-app/venv/lib/python3.8/site-packages/PIL/MspImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/MspImagePlugin.py deleted file mode 100644 index 0a75c86..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/MspImagePlugin.py +++ /dev/null @@ -1,200 +0,0 @@ -# -# The Python Imaging Library. -# -# MSP file handling -# -# This is the format used by the Paint program in Windows 1 and 2. -# -# History: -# 95-09-05 fl Created -# 97-01-03 fl Read/write MSP images -# 17-02-21 es Fixed RLE interpretation -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1995-97. -# Copyright (c) Eric Soroos 2017. -# -# See the README file for information on usage and redistribution. -# -# More info on this format: https://archive.org/details/gg243631 -# Page 313: -# Figure 205. Windows Paint Version 1: "DanM" Format -# Figure 206. Windows Paint Version 2: "LinS" Format. Used in Windows V2.03 -# -# See also: https://www.fileformat.info/format/mspaint/egff.htm -from __future__ import annotations - -import io -import struct -from typing import IO - -from . import Image, ImageFile -from ._binary import i16le as i16 -from ._binary import o16le as o16 - -# -# read MSP files - - -def _accept(prefix: bytes) -> bool: - return prefix[:4] in [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[:4] == b"DanM": - self.tile = [("raw", (0, 0) + self.size, 32, ("1", 0, 1))] - else: - self.tile = [("MSP", (0, 0) + self.size, 32, None)] - - -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) -> 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", 0, 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, [("raw", (0, 0) + im.size, 32, ("1", 0, 1))]) - - -# -# registry - -Image.register_open(MspImageFile.format, MspImageFile, _accept) -Image.register_save(MspImageFile.format, _save) - -Image.register_extension(MspImageFile.format, ".msp") diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/PSDraw.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/PSDraw.py deleted file mode 100644 index 673eae1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/PSDraw.py +++ /dev/null @@ -1,237 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# Simple PostScript graphics interface -# -# History: -# 1996-04-20 fl Created -# 1999-01-10 fl Added gsave/grestore to image method -# 2005-05-04 fl Fixed floating point issue in image (from Eric Etheridge) -# -# Copyright (c) 1997-2005 by Secret Labs AB. All rights reserved. -# Copyright (c) 1996 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import sys -from typing import TYPE_CHECKING - -from . import EpsImagePlugin - -## -# Simple PostScript graphics interface. - - -class PSDraw: - """ - Sets up printing to the given file. If ``fp`` is omitted, - ``sys.stdout.buffer`` or ``sys.stdout`` is assumed. - """ - - def __init__(self, fp=None): - if not fp: - try: - fp = sys.stdout.buffer - except AttributeError: - fp = sys.stdout - 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/plotter-app/venv/lib/python3.8/site-packages/PIL/PaletteFile.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/PaletteFile.py deleted file mode 100644 index 81652e5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/PaletteFile.py +++ /dev/null @@ -1,54 +0,0 @@ -# -# Python Imaging Library -# $Id$ -# -# stuff to read simple, teragon-style palette files -# -# History: -# 97-08-23 fl Created -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1997. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from typing import IO - -from ._binary import o8 - - -class PaletteFile: - """File handler for Teragon-style palette files.""" - - rawmode = "RGB" - - def __init__(self, fp: IO[bytes]) -> None: - palette = [o8(i) * 3 for i in range(256)] - - while True: - s = fp.readline() - - if not s: - break - if s[:1] == 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/plotter-app/venv/lib/python3.8/site-packages/PIL/PalmImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/PalmImagePlugin.py deleted file mode 100644 index 1735070..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/PalmImagePlugin.py +++ /dev/null @@ -1,229 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# - -## -# Image plugin for Palm pixmap images (output only). -## -from __future__ import annotations - -from typing import IO - -from . import Image, ImageFile -from ._binary import o8 -from ._binary import o16be as o16b - -# fmt: off -_Palm8BitColormapValues = ( - (255, 255, 255), (255, 204, 255), (255, 153, 255), (255, 102, 255), - (255, 51, 255), (255, 0, 255), (255, 255, 204), (255, 204, 204), - (255, 153, 204), (255, 102, 204), (255, 51, 204), (255, 0, 204), - (255, 255, 153), (255, 204, 153), (255, 153, 153), (255, 102, 153), - (255, 51, 153), (255, 0, 153), (204, 255, 255), (204, 204, 255), - (204, 153, 255), (204, 102, 255), (204, 51, 255), (204, 0, 255), - (204, 255, 204), (204, 204, 204), (204, 153, 204), (204, 102, 204), - (204, 51, 204), (204, 0, 204), (204, 255, 153), (204, 204, 153), - (204, 153, 153), (204, 102, 153), (204, 51, 153), (204, 0, 153), - (153, 255, 255), (153, 204, 255), (153, 153, 255), (153, 102, 255), - (153, 51, 255), (153, 0, 255), (153, 255, 204), (153, 204, 204), - (153, 153, 204), (153, 102, 204), (153, 51, 204), (153, 0, 204), - (153, 255, 153), (153, 204, 153), (153, 153, 153), (153, 102, 153), - (153, 51, 153), (153, 0, 153), (102, 255, 255), (102, 204, 255), - (102, 153, 255), (102, 102, 255), (102, 51, 255), (102, 0, 255), - (102, 255, 204), (102, 204, 204), (102, 153, 204), (102, 102, 204), - (102, 51, 204), (102, 0, 204), (102, 255, 153), (102, 204, 153), - (102, 153, 153), (102, 102, 153), (102, 51, 153), (102, 0, 153), - (51, 255, 255), (51, 204, 255), (51, 153, 255), (51, 102, 255), - (51, 51, 255), (51, 0, 255), (51, 255, 204), (51, 204, 204), - (51, 153, 204), (51, 102, 204), (51, 51, 204), (51, 0, 204), - (51, 255, 153), (51, 204, 153), (51, 153, 153), (51, 102, 153), - (51, 51, 153), (51, 0, 153), (0, 255, 255), (0, 204, 255), - (0, 153, 255), (0, 102, 255), (0, 51, 255), (0, 0, 255), - (0, 255, 204), (0, 204, 204), (0, 153, 204), (0, 102, 204), - (0, 51, 204), (0, 0, 204), (0, 255, 153), (0, 204, 153), - (0, 153, 153), (0, 102, 153), (0, 51, 153), (0, 0, 153), - (255, 255, 102), (255, 204, 102), (255, 153, 102), (255, 102, 102), - (255, 51, 102), (255, 0, 102), (255, 255, 51), (255, 204, 51), - (255, 153, 51), (255, 102, 51), (255, 51, 51), (255, 0, 51), - (255, 255, 0), (255, 204, 0), (255, 153, 0), (255, 102, 0), - (255, 51, 0), (255, 0, 0), (204, 255, 102), (204, 204, 102), - (204, 153, 102), (204, 102, 102), (204, 51, 102), (204, 0, 102), - (204, 255, 51), (204, 204, 51), (204, 153, 51), (204, 102, 51), - (204, 51, 51), (204, 0, 51), (204, 255, 0), (204, 204, 0), - (204, 153, 0), (204, 102, 0), (204, 51, 0), (204, 0, 0), - (153, 255, 102), (153, 204, 102), (153, 153, 102), (153, 102, 102), - (153, 51, 102), (153, 0, 102), (153, 255, 51), (153, 204, 51), - (153, 153, 51), (153, 102, 51), (153, 51, 51), (153, 0, 51), - (153, 255, 0), (153, 204, 0), (153, 153, 0), (153, 102, 0), - (153, 51, 0), (153, 0, 0), (102, 255, 102), (102, 204, 102), - (102, 153, 102), (102, 102, 102), (102, 51, 102), (102, 0, 102), - (102, 255, 51), (102, 204, 51), (102, 153, 51), (102, 102, 51), - (102, 51, 51), (102, 0, 51), (102, 255, 0), (102, 204, 0), - (102, 153, 0), (102, 102, 0), (102, 51, 0), (102, 0, 0), - (51, 255, 102), (51, 204, 102), (51, 153, 102), (51, 102, 102), - (51, 51, 102), (51, 0, 102), (51, 255, 51), (51, 204, 51), - (51, 153, 51), (51, 102, 51), (51, 51, 51), (51, 0, 51), - (51, 255, 0), (51, 204, 0), (51, 153, 0), (51, 102, 0), - (51, 51, 0), (51, 0, 0), (0, 255, 102), (0, 204, 102), - (0, 153, 102), (0, 102, 102), (0, 51, 102), (0, 0, 102), - (0, 255, 51), (0, 204, 51), (0, 153, 51), (0, 102, 51), - (0, 51, 51), (0, 0, 51), (0, 255, 0), (0, 204, 0), - (0, 153, 0), (0, 102, 0), (0, 51, 0), (17, 17, 17), - (34, 34, 34), (68, 68, 68), (85, 85, 85), (119, 119, 119), - (136, 136, 136), (170, 170, 170), (187, 187, 187), (221, 221, 221), - (238, 238, 238), (192, 192, 192), (128, 0, 0), (128, 0, 128), - (0, 128, 0), (0, 128, 128), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0), - (0, 0, 0), (0, 0, 0), (0, 0, 0), (0, 0, 0)) -# fmt: on - - -# so build a prototype image to be used for palette resampling -def build_prototype_image() -> Image.Image: - image = Image.new("L", (1, len(_Palm8BitColormapValues))) - image.putdata(list(range(len(_Palm8BitColormapValues)))) - palettedata: tuple[int, ...] = () - for colormapValue in _Palm8BitColormapValues: - palettedata += colormapValue - palettedata += (0, 0, 0) * (256 - len(_Palm8BitColormapValues)) - image.putpalette(palettedata) - return image - - -Palm8BitColormapImage = build_prototype_image() - -# OK, we now have in Palm8BitColormapImage, -# a "P"-mode image with the right palette -# -# -------------------------------------------------------------------- - -_FLAGS = {"custom-colormap": 0x4000, "is-compressed": 0x8000, "has-transparent": 0x2000} - -_COMPRESSION_TYPES = {"none": 0xFF, "rle": 0x01, "scanline": 0x00} - - -# -# -------------------------------------------------------------------- - -## -# (Internal) Image save plugin for the Palm format. - - -def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - if im.mode == "P": - # we assume this is a color Palm image with the standard colormap, - # unless the "info" dict has a "custom-colormap" field - - 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" and "custom-colormap" in im.info: - flags = flags & _FLAGS["custom-colormap"] - colormapsize = 4 * 256 + 2 - colormapmode = im.palette.mode - colormap = im.getdata().getpalette() - 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 > 0: - fp.write(o16b(256)) - for i in range(256): - fp.write(o8(i)) - if colormapmode == "RGB": - fp.write( - o8(colormap[3 * i]) - + o8(colormap[3 * i + 1]) - + o8(colormap[3 * i + 2]) - ) - elif colormapmode == "RGBA": - fp.write( - o8(colormap[4 * i]) - + o8(colormap[4 * i + 1]) - + o8(colormap[4 * i + 2]) - ) - - # now convert data to raw form - ImageFile._save(im, fp, [("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/plotter-app/venv/lib/python3.8/site-packages/PIL/PcdImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/PcdImagePlugin.py deleted file mode 100644 index 1cd5c4a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/PcdImagePlugin.py +++ /dev/null @@ -1,66 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PCD file handling -# -# History: -# 96-05-10 fl Created -# 96-05-27 fl Added draft mode (128x192, 256x384) -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image, ImageFile - -## -# Image plugin for PhotoCD images. This plugin only reads the 768x512 -# image from the file; higher resolutions are encoded in a proprietary -# encoding. - - -class PcdImageFile(ImageFile.ImageFile): - format = "PCD" - format_description = "Kodak PhotoCD" - - def _open(self) -> None: - # rough - assert self.fp is not None - - self.fp.seek(2048) - s = self.fp.read(2048) - - if s[:4] != 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 = [("pcd", (0, 0) + self.size, 96 * 2048, None)] - - def load_end(self) -> None: - if self.tile_post_rotate: - # Handle rotated PCDs - assert self.im is not None - - 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/plotter-app/venv/lib/python3.8/site-packages/PIL/PcfFontFile.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/PcfFontFile.py deleted file mode 100644 index 0d1968b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/PcfFontFile.py +++ /dev/null @@ -1,254 +0,0 @@ -# -# THIS IS WORK IN PROGRESS -# -# The Python Imaging Library -# $Id$ -# -# portable compiled font file parser -# -# history: -# 1997-08-19 fl created -# 2003-09-13 fl fixed loading of unicode fonts -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1997-2003 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -from 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/plotter-app/venv/lib/python3.8/site-packages/PIL/PcxImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/PcxImagePlugin.py deleted file mode 100644 index dd42003..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/PcxImagePlugin.py +++ /dev/null @@ -1,227 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PCX file handling -# -# This format was originally used by ZSoft's popular PaintBrush -# program for the IBM PC. It is also supported by many MS-DOS and -# Windows applications, including the Windows PaintBrush program in -# Windows 3. -# -# history: -# 1995-09-01 fl Created -# 1996-05-20 fl Fixed RGB support -# 1997-01-03 fl Fixed 2-bit and 4-bit support -# 1999-02-03 fl Fixed 8-bit support (broken in 1.0b1) -# 1999-02-07 fl Added write support -# 2002-06-09 fl Made 2-bit and 4-bit support a bit more robust -# 2002-07-30 fl Seek from to current position, not beginning of file -# 2003-06-03 fl Extract DPI settings (info["dpi"]) -# -# Copyright (c) 1997-2003 by Secret Labs AB. -# Copyright (c) 1995-2003 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -import logging -from typing import IO - -from . import Image, ImageFile, ImagePalette -from ._binary import i16le as i16 -from ._binary import o8 -from ._binary import o16le as o16 - -logger = logging.getLogger(__name__) - - -def _accept(prefix: bytes) -> bool: - return 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(128) - 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) - - # 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 = "P;%dL" % planes - 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:]) - self.fp.seek(128) - - 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 = [("pcx", bbox, self.fp.tell(), (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, [("pcx", (0, 0) + im.size, 0, (rawmode, bits * planes))]) - - if im.mode == "P": - # colour palette - assert im.im is not None - - 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/plotter-app/venv/lib/python3.8/site-packages/PIL/PdfImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/PdfImagePlugin.py deleted file mode 100644 index f0da1e0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/PdfImagePlugin.py +++ /dev/null @@ -1,304 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PDF (Acrobat) file handling -# -# History: -# 1996-07-16 fl Created -# 1997-01-18 fl Fixed header -# 2004-02-21 fl Fixes for 1/L/CMYK images, etc. -# 2004-02-24 fl Fixes for 1 and P images. -# -# Copyright (c) 1997-2004 by Secret Labs AB. All rights reserved. -# Copyright (c) 1996-1997 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# - -## -# Image plugin for PDF images (output only). -## -from __future__ import annotations - -import io -import math -import os -import time -from typing import IO - -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, filename, existing_pdf, image_refs): - # 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 = {"BitsPerComponent": 8} - if im.mode == "1": - if features.check("libtiff"): - filter = "CCITTFaxDecode" - dict_obj["BitsPerComponent"] = 1 - params = PdfParser.PdfArray( - [ - PdfParser.PdfDict( - { - "K": -1, - "BlackIs1": True, - "Columns": width, - "Rows": height, - } - ) - ] - ) - else: - filter = "DCTDecode" - dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceGray") - procset = "ImageB" # grayscale - elif im.mode == "L": - filter = "DCTDecode" - # params = f"<< /Predictor 15 /Columns {width-2} >>" - dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceGray") - procset = "ImageB" # grayscale - elif im.mode == "LA": - filter = "JPXDecode" - # params = f"<< /Predictor 15 /Columns {width-2} >>" - procset = "ImageB" # grayscale - dict_obj["SMaskInData"] = 1 - elif im.mode == "P": - filter = "ASCIIHexDecode" - palette = im.getpalette() - 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": - filter = "DCTDecode" - dict_obj["ColorSpace"] = PdfParser.PdfName("DeviceRGB") - procset = "ImageC" # color images - elif im.mode == "RGBA": - filter = "JPXDecode" - procset = "ImageC" # color images - dict_obj["SMaskInData"] = 1 - elif im.mode == "CMYK": - 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 filter == "ASCIIHexDecode": - ImageFile._save(im, op, [("hex", (0, 0) + im.size, 0, im.mode)]) - elif filter == "CCITTFaxDecode": - im.save( - op, - "TIFF", - compression="group4", - # use a single strip - strip_size=math.ceil(width / 8) * height, - ) - elif filter == "DCTDecode": - Image.SAVE["JPEG"](im, op, filename) - elif filter == "JPXDecode": - del dict_obj["BitsPerComponent"] - Image.SAVE["JPEG2000"](im, op, filename) - else: - msg = f"unsupported PDF filter ({filter})" - raise ValueError(msg) - - stream = op.getvalue() - if filter == "CCITTFaxDecode": - stream = stream[8:] - filter = PdfParser.PdfArray([PdfParser.PdfName(filter)]) - else: - filter = PdfParser.PdfName(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, fp, filename, save_all=False): - is_appending = im.encoderinfo.get("append", False) - if is_appending: - existing_pdf = PdfParser.PdfParser(f=fp, filename=filename, mode="r+b") - else: - existing_pdf = PdfParser.PdfParser(f=fp, filename=filename, 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: - try: - im_number_of_pages = im.n_frames - except AttributeError: - # Image format does not have n_frames. - # It is a single frame image - pass - 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(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/plotter-app/venv/lib/python3.8/site-packages/PIL/PdfParser.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/PdfParser.py deleted file mode 100644 index 9e22313..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/PdfParser.py +++ /dev/null @@ -1,1003 +0,0 @@ -from __future__ import annotations - -import calendar -import codecs -import collections -import mmap -import os -import re -import time -import zlib -from typing import TYPE_CHECKING, Any, List, 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): - 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): - 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): - self.existing_entries = {} # object ID => (offset, generation) - self.new_entries = {} # object ID => (offset, generation) - self.deleted_entries = {0: 65536} # object ID => generation - self.reading_finished = False - - def __setitem__(self, key, value): - 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): - try: - return self.new_entries[key] - except KeyError: - return self.existing_entries[key] - - def __delitem__(self, key): - 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): - 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): - return ( - set(self.existing_entries.keys()) - set(self.deleted_entries.keys()) - ) | set(self.new_entries.keys()) - - def write(self, f): - 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 = 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 = None - 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: - def __init__(self, name): - 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): - 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): - 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" ]" - - -if TYPE_CHECKING: - _DictBase = collections.UserDict[Union[str, bytes], Any] -else: - _DictBase = collections.UserDict - - -class PdfDict(_DictBase): - def __setattr__(self, key, value): - if key == "data": - collections.UserDict.__setattr__(self, key, value) - else: - self[key.encode("us-ascii")] = value - - def __getattr__(self, key): - 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): - 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, buf): - self.dictionary = dictionary - self.buf = buf - - def decode(self): - try: - filter = self.dictionary.Filter - except AttributeError: - return self.buf - if filter == b"FlateDecode": - try: - expected_length = self.dictionary.DL - except AttributeError: - expected_length = self.dictionary.Length - return zlib.decompress(self.buf, bufsize=int(expected_length)) - else: - msg = f"stream filter {repr(self.dictionary.Filter)} unknown/unsupported" - raise NotImplementedError(msg) - - -def pdf_repr(x): - 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=None, f=None, buf=None, start_offset=0, mode="rb"): - 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 = 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 = 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 = {} - if 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 = {} - self.pages = [] - self.orig_pages = [] - self.pages_ref = None - self.last_xref_section_offset = None - self.trailer_dict = {} - 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: - try: - self.buf.close() - except AttributeError: - pass - 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: - self.f.seek(0, os.SEEK_END) - - def write_header(self) -> None: - self.f.write(b"%PDF-1.4\n") - - def write_comment(self, s): - self.f.write(f"% {s}\n".encode()) - - def write_catalog(self) -> IndirectReference: - 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=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 = {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, *objs, **dict_obj): - if isinstance(ref, int): - ref = self.pages[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(ref, *objs, **dict_obj) - - def write_obj(self, ref, *objs, **dict_obj): - 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): - 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: - self.file_size_total = len(self.buf) - self.file_size_this = self.file_size_total - self.start_offset - self.read_trailer() - self.root_ref = self.trailer_dict[b"Root"] - 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(b"Pages" in self.root, "/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"] - 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=None): - 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): - 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, "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 - 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): - 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, "previous trailer not found") - 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): - 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 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)) - value, offset = cls.get_value(trailer_data, m.end()) - trailer[key] = value - 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, as_text=False): - 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, offset, expect_indirect=None, max_nesting=-1): - 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, offset = cls.get_value(data, m.end(), max_nesting=max_nesting - 1) - if offset is None: - return object, None - m = cls.re_indirect_def_end.match(data, offset) - check_format_condition(m, "indirect object definition end not found") - 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 = {} - m = cls.re_dict_end.match(data, offset) - while not m: - key, offset = cls.get_value(data, offset, max_nesting=max_nesting - 1) - if offset is None: - return result, None - value, offset = cls.get_value(data, offset, max_nesting=max_nesting - 1) - result[key] = value - if offset is None: - return result, None - m = cls.re_dict_end.match(data, offset) - offset = m.end() - m = cls.re_stream_start.match(data, offset) - if m: - try: - stream_len_str = result.get(b"Length") - stream_len = int(stream_len_str) - except (TypeError, ValueError) as e: - msg = f"bad or missing Length in stream dict ({stream_len_str})" - raise PdfFormatError(msg) from e - stream_data = data[m.end() : m.end() + stream_len] - m = cls.re_stream_end.match(data, m.end() + stream_len) - check_format_condition(m, "stream end not found") - offset = m.end() - result = PdfStream(PdfDict(result), stream_data) - else: - result = PdfDict(result) - return result, offset - m = cls.re_array_start.match(data, offset) - if m: - offset = m.end() - result = [] - m = cls.re_array_end.match(data, offset) - while not m: - value, offset = cls.get_value(data, offset, max_nesting=max_nesting - 1) - result.append(value) - if offset is None: - return result, None - m = cls.re_array_end.match(data, offset) - return result, 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, offset): - 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): - subsection_found = False - m = self.re_xref_section_start.match( - self.buf, xref_section_offset + self.start_offset - ) - check_format_condition(m, "xref section start not found") - 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, "xref entry not found") - 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, max_nesting=-1): - 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}", - ) - 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=None): - if node is None: - node = self.page_tree_root - check_format_condition( - node[b"Type"] == b"Pages", "/Type of page tree node is not /Pages" - ) - pages = [] - for kid in 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/plotter-app/venv/lib/python3.8/site-packages/PIL/PixarImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/PixarImagePlugin.py deleted file mode 100644 index 887b656..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/PixarImagePlugin.py +++ /dev/null @@ -1,72 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PIXAR raster support for PIL -# -# history: -# 97-01-29 fl Created -# -# notes: -# This is incomplete; it is based on a few samples created with -# Photoshop 2.5 and 3.0, and a summary description provided by -# Greg Coats . Hopefully, "L" and -# "RGBA" support will be added in future versions. -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1997. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image, ImageFile -from ._binary import i16le as i16 - -# -# helpers - - -def _accept(prefix: bytes) -> bool: - return prefix[:4] == 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 = [("raw", (0, 0) + self.size, 1024, (self.mode, 0, 1))] - - -# -# -------------------------------------------------------------------- - -Image.register_open(PixarImageFile.format, PixarImageFile, _accept) - -Image.register_extension(PixarImageFile.format, ".pxr") diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/PngImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/PngImagePlugin.py deleted file mode 100644 index d283492..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/PngImagePlugin.py +++ /dev/null @@ -1,1489 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PNG support code -# -# See "PNG (Portable Network Graphics) Specification, version 1.0; -# W3C Recommendation", 1996-10-01, Thomas Boutell (ed.). -# -# history: -# 1996-05-06 fl Created (couldn't resist it) -# 1996-12-14 fl Upgraded, added read and verify support (0.2) -# 1996-12-15 fl Separate PNG stream parser -# 1996-12-29 fl Added write support, added getchunks -# 1996-12-30 fl Eliminated circular references in decoder (0.3) -# 1998-07-12 fl Read/write 16-bit images as mode I (0.4) -# 2001-02-08 fl Added transparency support (from Zircon) (0.5) -# 2001-04-16 fl Don't close data source in "open" method (0.6) -# 2004-02-24 fl Don't even pretend to support interlaced files (0.7) -# 2004-08-31 fl Do basic sanity check on chunk identifiers (0.8) -# 2004-09-20 fl Added PngInfo chunk container -# 2004-12-18 fl Added DPI read support (based on code by Niki Spahiev) -# 2008-08-13 fl Added tRNS support for RGB images -# 2009-03-06 fl Support for preserving ICC profiles (by Florian Hoech) -# 2009-03-08 fl Added zTXT support (from Lowell Alleman) -# 2009-03-29 fl Read interlaced PNG files (from Conrado Porto Lopes Gouvua) -# -# Copyright (c) 1997-2009 by Secret Labs AB -# Copyright (c) 1996 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import itertools -import logging -import re -import struct -import warnings -import zlib -from enum import IntEnum -from typing import IO, TYPE_CHECKING, Any, NoReturn - -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 - -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): - dobj = zlib.decompressobj() - plaintext = dobj.decompress(s, MAX_TEXT_CHUNK) - if dobj.unconsumed_tail: - msg = "Decompressed Data Too Large" - raise ValueError(msg) - return plaintext - - -def _crc32(data, seed=0): - 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, pos, length): - """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 = [] - - 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, lang=None, tkey=None): - """ - :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 PngStream(ChunkStream): - def __init__(self, fp): - super().__init__(fp) - - # local copies of Image attributes - self.im_info = {} - self.im_text = {} - self.im_size = (0, 0) - self.im_mode = None - self.im_tile = None - self.im_palette = None - self.im_custom_mimetype = None - self.im_n_frames = None - self._seq_num = None - self.rewind_state = 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 = { - "info": self.im_info.copy(), - "tile": self.im_tile, - "seq_num": 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 - 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 - 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 = [("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 = [("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 - 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 - 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 - 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 - - s = ImageFile._safe_read(self.fp, length) - raw_vals = struct.unpack(">%dI" % (len(s) // 4), 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 - - 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 - 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 - 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 = k.decode("latin-1", "strict") - v_str = v.decode("latin-1", "replace") - - self.im_info[k] = v if k == "exif" else v_str - self.im_text[k] = v_str - self.check_text_memory(len(v_str)) - - return s - - def chunk_zTXt(self, pos: int, length: int) -> bytes: - # compressed text - 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 = k.decode("latin-1", "strict") - v = v.decode("latin-1", "replace") - - self.im_info[k] = self.im_text[k] = v - self.check_text_memory(len(v)) - - return s - - def chunk_iTXt(self, pos: int, length: int) -> bytes: - # international text - 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 = k.decode("latin-1", "strict") - lang = lang.decode("utf-8", "strict") - tk = tk.decode("utf-8", "strict") - v = v.decode("utf-8", "strict") - except UnicodeError: - return s - - self.im_info[k] = self.im_text[k] = iTXt(v, lang, tk) - self.check_text_memory(len(v)) - - return s - - def chunk_eXIf(self, pos: int, length: int) -> bytes: - 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: - 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: - 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: - 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[:8] == _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 = 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): - # 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) - 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 - - self.dispose: _imaging.ImagingCore | None - if frame == 0: - if rewind: - self._fp.seek(self.__rewind) - self.png.rewind() - self.__prepare_idat = self.__rewind_idat - self.im = None - if self.pyaccess: - self.pyaccess = 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") - self.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") - self.dispose_extent = self.info.get("bbox") - - if not self.tile: - msg = "image not found in APNG frame" - raise EOFError(msg) - - # 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: - mask = updated.convert("RGBA") - self._prev_im.paste(updated, self.dispose_extent, mask) - self.im = self._prev_im - if self.pyaccess: - self.pyaccess = None - - def _getexif(self) -> dict[str, 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, cid, *data): - """Write a PNG chunk (including CRC field)""" - - data = b"".join(data) - - fp.write(o32(len(data)) + cid) - fp.write(data) - crc = _crc32(data, _crc32(cid)) - fp.write(o32(crc)) - - -class _idat: - # wrap output from the encoder in IDAT chunks - - def __init__(self, fp, chunk): - 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, chunk, seq_num): - self.fp = fp - self.chunk = chunk - self.seq_num = seq_num - - def write(self, data: bytes) -> None: - self.chunk(self.fp, b"fdAT", o32(self.seq_num), data) - self.seq_num += 1 - - -def _write_multiple_frames(im, fp, chunk, mode, rawmode, default_image, append_images): - 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 = [] - 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({"im": im_frame, "bbox": bbox, "encoderinfo": 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, _idat(fp, chunk), [("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, - _idat(fp, chunk), - [("zip", (0, 0) + im_frame.size, 0, rawmode)], - ) - else: - fdat_chunks = _fdat(fp, chunk, seq_num) - ImageFile._save( - im_frame, - fdat_chunks, - [("zip", (0, 0) + im_frame.size, 0, rawmode)], - ) - seq_num = fdat_chunks.seq_num - - -def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - _save(im, fp, filename, save_all=True) - - -def _save(im, fp, filename, chunk=putchunk, save_all=False): - # 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 - - # - # 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"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) - - if save_all: - im = _write_multiple_frames( - im, fp, chunk, mode, rawmode, default_image, append_images - ) - if im: - ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + 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, **params): - """Return a list of PNG chunks representing this image.""" - - class collector: - data = [] - - def write(self, data: bytes) -> None: - pass - - def append(self, chunk: bytes) -> None: - self.data.append(chunk) - - def append(fp, cid, *data): - data = b"".join(data) - crc = o32(_crc32(data, _crc32(cid))) - fp.append((cid, data, crc)) - - fp = collector() - - try: - im.encoderinfo = params - _save(im, fp, None, append) - finally: - del im.encoderinfo - - return fp.data - - -# -------------------------------------------------------------------- -# 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/plotter-app/venv/lib/python3.8/site-packages/PIL/PpmImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/PpmImagePlugin.py deleted file mode 100644 index 16c9ccb..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/PpmImagePlugin.py +++ /dev/null @@ -1,371 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# PPM support for PIL -# -# History: -# 96-03-24 fl Created -# 98-03-06 fl Write RGBA images (as RGB, that is) -# -# Copyright (c) Secret Labs AB 1997-98. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import math -from typing import IO - -from . import Image, ImageFile -from ._binary import i16be as i16 -from ._binary import o8 -from ._binary import o32le as o32 - -# -# -------------------------------------------------------------------- - -b_whitespace = b"\x20\x09\x0a\x0b\x0c\x0d" - -MODES = { - # standard - b"P1": "1", - b"P2": "L", - b"P3": "RGB", - b"P4": "1", - b"P5": "L", - b"P6": "RGB", - # extensions - b"P0CMYK": "CMYK", - b"Pf": "F", - # PIL extensions (for test purposes only) - b"PyP": "P", - b"PyRGBA": "RGBA", - b"PyCMYK": "CMYK", -} - - -def _accept(prefix: bytes) -> bool: - return prefix[0:1] == 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 = f"Token too long in file header: {token.decode()}" - raise ValueError(msg) - 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 = [(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) -> 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) -> 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 == "I": - 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, [("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/plotter-app/venv/lib/python3.8/site-packages/PIL/PsdImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/PsdImagePlugin.py deleted file mode 100644 index edf698b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/PsdImagePlugin.py +++ /dev/null @@ -1,326 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# Adobe PSD 2.5/3.0 file handling -# -# History: -# 1995-09-01 fl Created -# 1997-01-03 fl Read most PSD images -# 1997-01-18 fl Fixed P and CMYK support -# 2001-10-21 fl Added seek/tell support (for layers) -# -# Copyright (c) 1997-2001 by Secret Labs AB. -# Copyright (c) 1995-2001 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -from functools import cached_property - -from . 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 - -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[:4] == 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): - layers = [] - if self._layers_position is not None: - 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 - - # seek to given layer (1..max) - try: - _, mode, _, tile = self.layers[layer - 1] - self._mode = mode - self.tile = tile - self.frame = layer - self.fp = self._fp - except IndexError as e: - msg = "no such layer" - raise EOFError(msg) from e - - def tell(self) -> int: - # return layer number (0=image, 1..max=layers) - return self.frame - - -def _layerinfo(fp, ct_bytes): - # read layerinfo block - layers = [] - - def read(size): - 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 - mode = [] - 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: - m = "A" - else: - m = "RGBA"[type] - - mode.append(m) - read(4) # size - - # figure out the image mode - mode.sort() - if mode == ["R"]: - mode = "L" - elif mode == ["B", "G", "R"]: - mode = "RGB" - elif mode == ["A", "B", "G", "R"]: - mode = "RGBA" - else: - mode = None # 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 - for i, (name, mode, bbox) in enumerate(layers): - tile = [] - for m in mode: - t = _maketile(fp, m, bbox, 1) - if t: - tile.extend(t) - layers[i] = name, mode, bbox, tile - - return layers - - -def _maketile(file, mode, bbox, channels): - tile = None - 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 - tile = [] - for channel in range(channels): - layer = mode[channel] - if mode == "CMYK": - layer += ";I" - tile.append(("raw", bbox, offset, layer)) - offset = offset + xsize * ysize - - elif compression == 1: - # - # packbits compression - i = 0 - tile = [] - bytecount = read(channels * ysize * 2) - offset = file.tell() - for channel in range(channels): - layer = mode[channel] - if mode == "CMYK": - layer += ";I" - tile.append(("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 tile - - -# -------------------------------------------------------------------- -# 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/plotter-app/venv/lib/python3.8/site-packages/PIL/PyAccess.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/PyAccess.py deleted file mode 100644 index 3be1cca..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/PyAccess.py +++ /dev/null @@ -1,381 +0,0 @@ -# -# The Python Imaging Library -# Pillow fork -# -# Python implementation of the PixelAccess Object -# -# Copyright (c) 1997-2009 by Secret Labs AB. All rights reserved. -# Copyright (c) 1995-2009 by Fredrik Lundh. -# Copyright (c) 2013 Eric Soroos -# -# See the README file for information on usage and redistribution -# - -# Notes: -# -# * Implements the pixel access object following Access.c -# * Taking only the tuple form, which is used from python. -# * Fill.c uses the integer form, but it's still going to use the old -# Access.c implementation. -# -from __future__ import annotations - -import logging -import sys -from typing import TYPE_CHECKING - -from ._deprecate import deprecate - -FFI: type -try: - from cffi import FFI - - defs = """ - struct Pixel_RGBA { - unsigned char r,g,b,a; - }; - struct Pixel_I16 { - unsigned char l,r; - }; - """ - ffi = FFI() - ffi.cdef(defs) -except ImportError as ex: - # Allow error import for doc purposes, but error out when accessing - # anything in core. - from ._util import DeferredError - - FFI = ffi = DeferredError.new(ex) - -logger = logging.getLogger(__name__) - -if TYPE_CHECKING: - from . import Image - - -class PyAccess: - def __init__(self, img: Image.Image, readonly: bool = False) -> None: - deprecate("PyAccess", 11) - vals = dict(img.im.unsafe_ptrs) - self.readonly = readonly - self.image8 = ffi.cast("unsigned char **", vals["image8"]) - self.image32 = ffi.cast("int **", vals["image32"]) - self.image = ffi.cast("unsigned char **", vals["image"]) - self.xsize, self.ysize = img.im.size - self._img = img - - # Keep pointer to im object to prevent dereferencing. - self._im = img.im - if self._im.mode in ("P", "PA"): - self._palette = img.palette - - # Debugging is polluting test traces, only useful here - # when hacking on PyAccess - # logger.debug("%s", vals) - self._post_init() - - def _post_init(self) -> None: - pass - - def __setitem__( - self, - xy: tuple[int, int] | list[int], - color: float | tuple[int, ...] | list[int], - ) -> None: - """ - Modifies the pixel at x,y. 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. - - :param xy: The pixel coordinate, given as (x, y). See - :ref:`coordinate-system`. - :param color: The pixel value. - """ - if self.readonly: - msg = "Attempt to putpixel a read only image" - raise ValueError(msg) - (x, y) = xy - if x < 0: - x = self.xsize + x - if y < 0: - y = self.ysize + y - (x, y) = self.check_xy((x, y)) - - if ( - self._im.mode in ("P", "PA") - and isinstance(color, (list, tuple)) - and len(color) in [3, 4] - ): - # RGB or RGBA value for a P or PA image - if self._im.mode == "PA": - alpha = color[3] if len(color) == 4 else 255 - color = color[:3] - palette_index = self._palette.getcolor(color, self._img) - color = (palette_index, alpha) if self._im.mode == "PA" else palette_index - - return self.set_pixel(x, y, color) - - def __getitem__(self, xy: tuple[int, int] | list[int]) -> float | tuple[int, ...]: - """ - Returns the pixel at x,y. The pixel is returned as a single - value for single band images or a tuple for multiple band - images - - :param xy: The pixel coordinate, given as (x, y). See - :ref:`coordinate-system`. - :returns: a pixel value for single band images, a tuple of - pixel values for multiband images. - """ - (x, y) = xy - if x < 0: - x = self.xsize + x - if y < 0: - y = self.ysize + y - (x, y) = self.check_xy((x, y)) - return self.get_pixel(x, y) - - putpixel = __setitem__ - getpixel = __getitem__ - - def check_xy(self, xy: tuple[int, int]) -> tuple[int, int]: - (x, y) = xy - if not (0 <= x < self.xsize and 0 <= y < self.ysize): - msg = "pixel location out of range" - raise ValueError(msg) - return xy - - def get_pixel(self, x: int, y: int) -> float | tuple[int, ...]: - raise NotImplementedError() - - def set_pixel( - self, x: int, y: int, color: float | tuple[int, ...] | list[int] - ) -> None: - raise NotImplementedError() - - -class _PyAccess32_2(PyAccess): - """PA, LA, stored in first and last bytes of a 32 bit word""" - - def _post_init(self, *args, **kwargs): - self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) - - def get_pixel(self, x: int, y: int) -> tuple[int, int]: - pixel = self.pixels[y][x] - return pixel.r, pixel.a - - def set_pixel(self, x, y, color): - pixel = self.pixels[y][x] - # tuple - pixel.r = min(color[0], 255) - pixel.a = min(color[1], 255) - - -class _PyAccess32_3(PyAccess): - """RGB and friends, stored in the first three bytes of a 32 bit word""" - - def _post_init(self, *args, **kwargs): - self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) - - def get_pixel(self, x: int, y: int) -> tuple[int, int, int]: - pixel = self.pixels[y][x] - return pixel.r, pixel.g, pixel.b - - def set_pixel(self, x, y, color): - pixel = self.pixels[y][x] - # tuple - pixel.r = min(color[0], 255) - pixel.g = min(color[1], 255) - pixel.b = min(color[2], 255) - pixel.a = 255 - - -class _PyAccess32_4(PyAccess): - """RGBA etc, all 4 bytes of a 32 bit word""" - - def _post_init(self, *args, **kwargs): - self.pixels = ffi.cast("struct Pixel_RGBA **", self.image32) - - def get_pixel(self, x: int, y: int) -> tuple[int, int, int, int]: - pixel = self.pixels[y][x] - return pixel.r, pixel.g, pixel.b, pixel.a - - def set_pixel(self, x, y, color): - pixel = self.pixels[y][x] - # tuple - pixel.r = min(color[0], 255) - pixel.g = min(color[1], 255) - pixel.b = min(color[2], 255) - pixel.a = min(color[3], 255) - - -class _PyAccess8(PyAccess): - """1, L, P, 8 bit images stored as uint8""" - - def _post_init(self, *args, **kwargs): - self.pixels = self.image8 - - def get_pixel(self, x: int, y: int) -> int: - return self.pixels[y][x] - - def set_pixel(self, x, y, color): - try: - # integer - self.pixels[y][x] = min(color, 255) - except TypeError: - # tuple - self.pixels[y][x] = min(color[0], 255) - - -class _PyAccessI16_N(PyAccess): - """I;16 access, native bitendian without conversion""" - - def _post_init(self, *args, **kwargs): - self.pixels = ffi.cast("unsigned short **", self.image) - - def get_pixel(self, x: int, y: int) -> int: - return self.pixels[y][x] - - def set_pixel(self, x, y, color): - try: - # integer - self.pixels[y][x] = min(color, 65535) - except TypeError: - # tuple - self.pixels[y][x] = min(color[0], 65535) - - -class _PyAccessI16_L(PyAccess): - """I;16L access, with conversion""" - - def _post_init(self, *args, **kwargs): - self.pixels = ffi.cast("struct Pixel_I16 **", self.image) - - def get_pixel(self, x: int, y: int) -> int: - pixel = self.pixels[y][x] - return pixel.l + pixel.r * 256 - - def set_pixel(self, x, y, color): - pixel = self.pixels[y][x] - try: - color = min(color, 65535) - except TypeError: - color = min(color[0], 65535) - - pixel.l = color & 0xFF - pixel.r = color >> 8 - - -class _PyAccessI16_B(PyAccess): - """I;16B access, with conversion""" - - def _post_init(self, *args, **kwargs): - self.pixels = ffi.cast("struct Pixel_I16 **", self.image) - - def get_pixel(self, x: int, y: int) -> int: - pixel = self.pixels[y][x] - return pixel.l * 256 + pixel.r - - def set_pixel(self, x, y, color): - pixel = self.pixels[y][x] - try: - color = min(color, 65535) - except Exception: - color = min(color[0], 65535) - - pixel.l = color >> 8 - pixel.r = color & 0xFF - - -class _PyAccessI32_N(PyAccess): - """Signed Int32 access, native endian""" - - def _post_init(self, *args, **kwargs): - self.pixels = self.image32 - - def get_pixel(self, x: int, y: int) -> int: - return self.pixels[y][x] - - def set_pixel(self, x, y, color): - self.pixels[y][x] = color - - -class _PyAccessI32_Swap(PyAccess): - """I;32L/B access, with byteswapping conversion""" - - def _post_init(self, *args, **kwargs): - self.pixels = self.image32 - - def reverse(self, i): - orig = ffi.new("int *", i) - chars = ffi.cast("unsigned char *", orig) - chars[0], chars[1], chars[2], chars[3] = chars[3], chars[2], chars[1], chars[0] - return ffi.cast("int *", chars)[0] - - def get_pixel(self, x: int, y: int) -> int: - return self.reverse(self.pixels[y][x]) - - def set_pixel(self, x, y, color): - self.pixels[y][x] = self.reverse(color) - - -class _PyAccessF(PyAccess): - """32 bit float access""" - - def _post_init(self, *args, **kwargs): - self.pixels = ffi.cast("float **", self.image32) - - def get_pixel(self, x: int, y: int) -> float: - return self.pixels[y][x] - - def set_pixel(self, x, y, color): - try: - # not a tuple - self.pixels[y][x] = color - except TypeError: - # tuple - self.pixels[y][x] = color[0] - - -mode_map = { - "1": _PyAccess8, - "L": _PyAccess8, - "P": _PyAccess8, - "I;16N": _PyAccessI16_N, - "LA": _PyAccess32_2, - "La": _PyAccess32_2, - "PA": _PyAccess32_2, - "RGB": _PyAccess32_3, - "LAB": _PyAccess32_3, - "HSV": _PyAccess32_3, - "YCbCr": _PyAccess32_3, - "RGBA": _PyAccess32_4, - "RGBa": _PyAccess32_4, - "RGBX": _PyAccess32_4, - "CMYK": _PyAccess32_4, - "F": _PyAccessF, - "I": _PyAccessI32_N, -} - -if sys.byteorder == "little": - mode_map["I;16"] = _PyAccessI16_N - mode_map["I;16L"] = _PyAccessI16_N - mode_map["I;16B"] = _PyAccessI16_B - - mode_map["I;32L"] = _PyAccessI32_N - mode_map["I;32B"] = _PyAccessI32_Swap -else: - mode_map["I;16"] = _PyAccessI16_L - mode_map["I;16L"] = _PyAccessI16_L - mode_map["I;16B"] = _PyAccessI16_N - - mode_map["I;32L"] = _PyAccessI32_Swap - mode_map["I;32B"] = _PyAccessI32_N - - -def new(img: Image.Image, readonly: bool = False) -> PyAccess | None: - access_type = mode_map.get(img.mode, None) - if not access_type: - logger.debug("PyAccess Not Implemented: %s", img.mode) - return None - return access_type(img, readonly) diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/QoiImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/QoiImagePlugin.py deleted file mode 100644 index 202ef52..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/QoiImagePlugin.py +++ /dev/null @@ -1,115 +0,0 @@ -# -# The Python Imaging Library. -# -# QOI support for PIL -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os - -from . import Image, ImageFile -from ._binary import i32be as i32 - - -def _accept(prefix: bytes) -> bool: - return prefix[:4] == 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 = tuple(i32(self.fp.read(4)) for i in range(2)) - - channels = self.fp.read(1)[0] - self._mode = "RGB" if channels == 3 else "RGBA" - - self.fp.seek(1, os.SEEK_CUR) # colorspace - self.tile = [("qoi", (0, 0) + self._size, self.fp.tell(), None)] - - -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) -> tuple[int, int]: - assert self.fd is not None - - self._previously_seen_pixels = {} - self._add_to_previous_pixels(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 - - -Image.register_open(QoiImageFile.format, QoiImageFile, _accept) -Image.register_decoder("qoi", QoiDecoder) -Image.register_extension(QoiImageFile.format, ".qoi") diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/SgiImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/SgiImagePlugin.py deleted file mode 100644 index 50d9791..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/SgiImagePlugin.py +++ /dev/null @@ -1,238 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# SGI image file handling -# -# See "The SGI Image File Format (Draft version 0.97)", Paul Haeberli. -# -# -# -# History: -# 2017-22-07 mb Add RLE decompression -# 2016-16-10 mb Add save method without compression -# 1995-09-10 fl Created -# -# Copyright (c) 2016 by Mickael Bonfill. -# Copyright (c) 2008 by Karsten Hiddemann. -# Copyright (c) 1997 by Secret Labs AB. -# Copyright (c) 1995 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import os -import struct -from typing import IO - -from . import Image, ImageFile -from ._binary import i16be as i16 -from ._binary import o8 - - -def _accept(prefix: bytes) -> bool: - return len(prefix) >= 2 and i16(prefix) == 474 - - -MODES = { - (1, 1, 1): "L", - (1, 2, 1): "L", - (2, 1, 1): "L;16B", - (2, 2, 1): "L;16B", - (1, 3, 3): "RGB", - (2, 3, 3): "RGB;16B", - (1, 3, 4): "RGBA", - (2, 3, 4): "RGBA;16B", -} - - -## -# Image plugin for SGI images. -class SgiImageFile(ImageFile.ImageFile): - format = "SGI" - format_description = "SGI Image File Format" - - def _open(self) -> None: - # HEAD - assert self.fp is not None - - headlen = 512 - s = self.fp.read(headlen) - - if not _accept(s): - msg = "Not an SGI image file" - raise ValueError(msg) - - # compression : verbatim or RLE - compression = s[2] - - # bpc : 1 or 2 bytes (8bits or 16bits) - bpc = s[3] - - # dimension : 1, 2 or 3 (depending on xsize, ysize and zsize) - dimension = i16(s, 4) - - # xsize : width - xsize = i16(s, 6) - - # ysize : height - ysize = i16(s, 8) - - # zsize : channels count - zsize = i16(s, 10) - - # layout - layout = bpc, dimension, zsize - - # determine mode from bits/zsize - rawmode = "" - try: - rawmode = MODES[layout] - except KeyError: - pass - - if rawmode == "": - 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 = [ - ("SGI16", (0, 0) + self.size, headlen, (self.mode, 0, orientation)) - ] - else: - self.tile = [] - offset = headlen - for layer in self.mode: - self.tile.append( - ("raw", (0, 0) + self.size, offset, (layer, 0, orientation)) - ) - offset += pagesize - elif compression == 1: - self.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 - - # Number of dimensions (x,y,z) - dim = 3 - # X Dimension = width / Y Dimension = height - x, y = im.size - if im.mode == "L" and y == 1: - dim = 1 - elif im.mode == "L": - dim = 2 - # Z Dimension: Number of channels - z = len(im.mode) - - if dim in {1, 2}: - z = 1 - - # assert we've got the right number of bands. - if len(im.getbands()) != z: - msg = f"incorrect number of bands in SGI write: {z} vs {len(im.getbands())}" - raise ValueError(msg) - - # 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", dim)) - 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) -> 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/plotter-app/venv/lib/python3.8/site-packages/PIL/SpiderImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/SpiderImagePlugin.py deleted file mode 100644 index f5a09c3..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/SpiderImagePlugin.py +++ /dev/null @@ -1,325 +0,0 @@ -# -# The Python Imaging Library. -# -# SPIDER image file handling -# -# History: -# 2004-08-02 Created BB -# 2006-03-02 added save method -# 2006-03-13 added support for stack images -# -# Copyright (c) 2004 by Health Research Inc. (HRI) RENSSELAER, NY 12144. -# Copyright (c) 2004 by William Baxter. -# Copyright (c) 2004 by Secret Labs AB. -# Copyright (c) 2004 by Fredrik Lundh. -# - -## -# Image plugin for the Spider image format. This format is used -# by the SPIDER software, in processing image data from electron -# microscopy and tomography. -## - -# -# SpiderImagePlugin.py -# -# The Spider image format is used by SPIDER software, in processing -# image data from electron microscopy and tomography. -# -# Spider home page: -# https://spider.wadsworth.org/spider_doc/spider/docs/spider.html -# -# Details about the Spider image format: -# https://spider.wadsworth.org/spider_doc/spider/docs/image_doc.html -# -from __future__ import annotations - -import os -import struct -import sys -from typing import IO, TYPE_CHECKING, Any, Tuple, cast - -from . import Image, ImageFile - - -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 = [("raw", (0, 0) + self.size, offset, (self.rawmode, 0, 1))] - 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 - 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[SpiderImageFile] | 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 - - imglist = [] - for img in filelist: - if not os.path.exists(img): - print(f"unable to find {img}") - continue - try: - with Image.open(img) as im: - im = im.convert2byte() - except Exception: - if not isSpiderImage(img): - print(f"{img} is not a Spider image file") - continue - im.info["filename"] = img - imglist.append(im) - return imglist - - -# -------------------------------------------------------------------- -# 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[0] != "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, [("raw", (0, 0) + im.size, 0, (rawmode, 0, 1))]) - - -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/plotter-app/venv/lib/python3.8/site-packages/PIL/SunImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/SunImagePlugin.py deleted file mode 100644 index 4e09847..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/SunImagePlugin.py +++ /dev/null @@ -1,141 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# Sun image file handling -# -# History: -# 1995-09-10 fl Created -# 1996-05-28 fl Fixed 32-bit alignment -# 1998-12-29 fl Import ImagePalette module -# 2001-12-18 fl Fixed palette loading (from Jean-Claude Rimbault) -# -# Copyright (c) 1997-2001 by Secret Labs AB -# Copyright (c) 1995-1996 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -from . import Image, ImageFile, ImagePalette -from ._binary import i32be as i32 - - -def _accept(prefix: bytes) -> bool: - return len(prefix) >= 4 and i32(prefix) == 0x59A66A95 - - -## -# Image plugin for Sun raster files. - - -class SunImageFile(ImageFile.ImageFile): - format = "SUN" - format_description = "Sun Raster File" - - def _open(self) -> None: - # The Sun Raster file header is 32 bytes in length - # and has the following format: - - # typedef struct _SunRaster - # { - # DWORD MagicNumber; /* Magic (identification) number */ - # DWORD Width; /* Width of image in pixels */ - # DWORD Height; /* Height of image in pixels */ - # DWORD Depth; /* Number of bits per pixel */ - # DWORD Length; /* Size of image data in bytes */ - # DWORD Type; /* Type of raster file */ - # DWORD ColorMapType; /* Type of color map */ - # DWORD ColorMapLength; /* Size of the color map in bytes */ - # } SUNRASTER; - - assert self.fp is not None - - # HEAD - s = self.fp.read(32) - if not _accept(s): - msg = "not an SUN raster file" - raise SyntaxError(msg) - - offset = 32 - - self._size = i32(s, 4), i32(s, 8) - - depth = i32(s, 12) - # data_length = i32(s, 16) # unreliable, ignore. - file_type = i32(s, 20) - palette_type = i32(s, 24) # 0: None, 1: RGB, 2: Raw/arbitrary - palette_length = i32(s, 28) - - if depth == 1: - self._mode, rawmode = "1", "1;I" - elif depth == 4: - self._mode, rawmode = "L", "L;4" - elif depth == 8: - self._mode = rawmode = "L" - elif depth == 24: - if file_type == 3: - self._mode, rawmode = "RGB", "RGB" - else: - self._mode, rawmode = "RGB", "BGR" - elif depth == 32: - if file_type == 3: - self._mode, rawmode = "RGB", "RGBX" - else: - self._mode, rawmode = "RGB", "BGRX" - else: - msg = "Unsupported Mode/Bit Depth" - raise SyntaxError(msg) - - if palette_length: - if palette_length > 1024: - msg = "Unsupported Color Palette Length" - raise SyntaxError(msg) - - if palette_type != 1: - msg = "Unsupported Palette Type" - raise SyntaxError(msg) - - offset = offset + palette_length - self.palette = ImagePalette.raw("RGB;L", self.fp.read(palette_length)) - if self.mode == "L": - self._mode = "P" - rawmode = rawmode.replace("L", "P") - - # 16 bit boundaries on stride - stride = ((self.size[0] * depth + 15) // 16) * 2 - - # file type: Type is the version (or flavor) of the bitmap - # file. The following values are typically found in the Type - # field: - # 0000h Old - # 0001h Standard - # 0002h Byte-encoded - # 0003h RGB format - # 0004h TIFF format - # 0005h IFF format - # FFFFh Experimental - - # Old and standard are the same, except for the length tag. - # byte-encoded is run-length-encoded - # RGB looks similar to standard, but RGB byte order - # TIFF and IFF mean that they were converted from T/IFF - # Experimental means that it's something else. - # (https://www.fileformat.info/format/sunraster/egff.htm) - - if file_type in (0, 1, 3, 4, 5): - self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, stride))] - elif file_type == 2: - self.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/plotter-app/venv/lib/python3.8/site-packages/PIL/TarIO.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/TarIO.py deleted file mode 100644 index cba26d4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/TarIO.py +++ /dev/null @@ -1,67 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# read files from within a tar file -# -# History: -# 95-06-18 fl Created -# 96-05-28 fl Open files in binary mode -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1995-96. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io - -from . import ContainerIO - - -class TarIO(ContainerIO.ContainerIO[bytes]): - """A file object that provides read access to a given member of a TAR file.""" - - def __init__(self, tarfile: str, file: str) -> None: - """ - Create file object. - - :param tarfile: Name of TAR file. - :param file: Name of member file. - """ - self.fh = open(tarfile, "rb") - - while True: - s = self.fh.read(512) - if len(s) != 512: - msg = "unexpected end of tar file" - raise OSError(msg) - - name = s[:100].decode("utf-8") - i = name.find("\0") - if i == 0: - 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) - - # Context manager support - def __enter__(self) -> TarIO: - return self - - def __exit__(self, *args: object) -> None: - self.close() - - def close(self) -> None: - self.fh.close() diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/TgaImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/TgaImagePlugin.py deleted file mode 100644 index 39104ae..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/TgaImagePlugin.py +++ /dev/null @@ -1,262 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# TGA file handling -# -# History: -# 95-09-01 fl created (reads 24-bit files only) -# 97-01-04 fl support more TGA versions, including compressed images -# 98-07-04 fl fixed orientation and alpha layer bugs -# 98-09-11 fl fixed orientation for runlength decoder -# -# Copyright (c) Secret Labs AB 1997-98. -# Copyright (c) Fredrik Lundh 1995-97. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import 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 = [ - ( - "tga_rle", - (0, 0) + self.size, - self.fp.tell(), - (rawmode, orientation, depth), - ) - ] - else: - self.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: - assert self.im is not None - 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: - assert im.im is not None - 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, [("tga_rle", (0, 0) + im.size, 0, (rawmode, orientation))] - ) - else: - ImageFile._save( - im, fp, [("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/plotter-app/venv/lib/python3.8/site-packages/PIL/TiffImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/TiffImagePlugin.py deleted file mode 100644 index ac5b63c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/TiffImagePlugin.py +++ /dev/null @@ -1,2200 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# TIFF file handling -# -# TIFF is a flexible, if somewhat aged, image file format originally -# defined by Aldus. Although TIFF supports a wide variety of pixel -# layouts and compression methods, the name doesn't really stand for -# "thousands of incompatible file formats," it just feels that way. -# -# To read TIFF data from a stream, the stream must be seekable. For -# progressive decoding, make sure to use TIFF files where the tag -# directory is placed first in the file. -# -# History: -# 1995-09-01 fl Created -# 1996-05-04 fl Handle JPEGTABLES tag -# 1996-05-18 fl Fixed COLORMAP support -# 1997-01-05 fl Fixed PREDICTOR support -# 1997-08-27 fl Added support for rational tags (from Perry Stoll) -# 1998-01-10 fl Fixed seek/tell (from Jan Blom) -# 1998-07-15 fl Use private names for internal variables -# 1999-06-13 fl Rewritten for PIL 1.0 (1.0) -# 2000-10-11 fl Additional fixes for Python 2.0 (1.1) -# 2001-04-17 fl Fixed rewind support (seek to frame 0) (1.2) -# 2001-05-12 fl Added write support for more tags (from Greg Couch) (1.3) -# 2001-12-18 fl Added workaround for broken Matrox library -# 2002-01-18 fl Don't mess up if photometric tag is missing (D. Alan Stewart) -# 2003-05-19 fl Check FILLORDER tag -# 2003-09-26 fl Added RGBa support -# 2004-02-24 fl Added DPI support; fixed rational write support -# 2005-02-07 fl Added workaround for broken Corel Draw 10 files -# 2006-01-09 fl Added support for float/double tags (from Russell Nelson) -# -# Copyright (c) 1997-2006 by Secret Labs AB. All rights reserved. -# Copyright (c) 1995-1997 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import io -import itertools -import logging -import math -import os -import struct -import warnings -from collections.abc import MutableMapping -from fractions import Fraction -from numbers import Number, Rational -from typing import IO, TYPE_CHECKING, Any, Callable, NoReturn - -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 .TiffTags import TYPES - -logger = logging.getLogger(__name__) - -# Set these to true to force use of libtiff for reading or writing. -READ_LIBTIFF = False -WRITE_LIBTIFF = False -IFD_LEGACY_API = True -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"), - (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[:4] in PREFIXES - - -def _limit_rational(val, max_val): - 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, max_val, min_val): - frac = Fraction(val) - n_d = frac.numerator, frac.denominator - - if min(n_d) < min_val: - n_d = _limit_rational(val, abs(min_val)) - - if max(n_d) > max_val: - val = Fraction(*n_d) - n_d = _limit_rational(val, max_val) - - return n_d - - -## -# Wrapper for TIFF IFDs. - -_load_dispatch = {} -_write_dispatch = {} - - -def _delegate(op): - def delegate(self, *args): - 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, denominator=1): - """ - :param value: either an integer numerator, a - float/rational/other number, or an IFDRational - :param denominator: Optional integer denominator - """ - 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: - self._numerator = value - self._denominator = denominator - - if denominator == 0: - self._val = float("nan") - elif denominator == 1: - self._val = Fraction(value) - else: - self._val = Fraction(value, denominator) - - @property - def numerator(self): - return self._numerator - - @property - def denominator(self): - return self._denominator - - def limit_rational(self, max_denominator): - """ - - :param max_denominator: Integer, the maximum denominator value - :returns: Tuple of (numerator, denominator) - """ - - if self.denominator == 0: - return self.numerator, self.denominator - - 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: - 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): - return [self._val, self._numerator, self._denominator] - - def __setstate__(self, state): - IFDRational.__init__(self, 0) - _val, _numerator, _denominator = state - self._val = _val - self._numerator = _numerator - 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__") - - -def _register_loader(idx, size): - def decorator(func): - 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): - def decorator(func): - _write_dispatch[idx] = func # noqa: F821 - return func - - return decorator - - -def _register_basic(idx_fmt_name): - from .TiffTags import TYPES - - idx, fmt, name = idx_fmt_name - TYPES[idx] = name - size = struct.calcsize(f"={fmt}") - _load_dispatch[idx] = ( # noqa: F821 - size, - lambda self, data, legacy_api=True: ( - self._unpack(f"{len(data) // size}{fmt}", data) - ), - ) - _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, Callable[[ImageFileDirectory_v2, bytes, bool], Any]] = {} - _write_dispatch: dict[int, Callable[..., Any]] = {} - - def __init__( - self, - ifh: bytes = b"II\052\0\0\0\0\0", - 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:]) if self._bigtiff else self._unpack("L", ifh[4:]) - ) - 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 = None - - def __str__(self) -> str: - return str(dict(self)) - - def named(self): - """ - :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): - 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): - return tag in self._tags_v2 or tag in self._tagdata - - def __setitem__(self, tag, value): - self._setitem(tag, value, self.legacy_api) - - def _setitem(self, tag, value, legacy_api): - 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): - self.tagtype[tag] = ( - TiffTags.RATIONAL - if all(v >= 0 for v in values) - else TiffTags.SIGNED_RATIONAL - ) - elif all(isinstance(v, int) for v in values): - if all(0 <= v < 2**16 for v in values): - self.tagtype[tag] = TiffTags.SHORT - elif all(-(2**15) < v < 2**15 for v in values): - self.tagtype[tag] = TiffTags.SIGNED_SHORT - else: - self.tagtype[tag] = ( - TiffTags.LONG - if all(v >= 0 for v in values) - else 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) 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): - return iter(set(self._tagdata) | set(self._tags_v2)) - - def _unpack(self, fmt, data): - return struct.unpack(self._endian + fmt, data) - - def _pack(self, fmt, *values): - 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, legacy_api=True): - return data - - @_register_writer(1) # Basic type, except for the legacy API. - def write_byte(self, data): - 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, legacy_api=True): - if data.endswith(b"\0"): - data = data[:-1] - return data.decode("latin-1", "replace") - - @_register_writer(2) - def write_string(self, value): - # 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, legacy_api=True): - vals = self._unpack(f"{len(data) // 4}L", data) - - def combine(a, b): - 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): - 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, legacy_api=True): - return data - - @_register_writer(7) - def write_undefined(self, value): - 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, legacy_api=True): - vals = self._unpack(f"{len(data) // 4}l", data) - - def combine(a, b): - 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): - return b"".join( - self._pack("2l", *_limit_signed_rational(frac, 2**31 - 1, -(2**31))) - for frac in values - ) - - def _ensure_read(self, fp, size): - 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): - 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: " + ( - "" % size 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 tobytes(self, offset=0): - # FIXME What about tagdata? - result = self._pack("H", len(self._tags_v2)) - - entries = [] - offset = offset + len(result) + len(self._tags_v2) * 12 + 4 - 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.get(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: - if self._endian == "<": - ifh = b"II\x2A\x00\x08\x00\x00\x00" - else: - ifh = b"MM\x00\x2A\x00\x00\x00\x08" - ifd = ImageFileDirectory_v2(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})" - msg += " - value: " + ( - "" % len(data) 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) <= 4: - entries.append((tag, typ, count, data.ljust(4, b"\0"), b"")) - else: - entries.append((tag, typ, count, self._pack("L", 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: - msg = "multistrip support not yet implemented" - raise NotImplementedError(msg) - value = self._pack("L", self._unpack("L", 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("HHL4s", tag, typ, count, value) - - # -- overwrite here for multi-page -- - result += b"\0\0\0\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): - if fp.tell() == 0: # skip TIFF header on subsequent pages - # tiff header -- PIL always starts the first IFD at offset 8 - fp.write(self._prefix + self._pack("HL", 42, 8)) - - 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, **kwargs): - 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): - """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): - 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): - return iter(set(self._tagdata) | set(self._tags_v1)) - - def __setitem__(self, tag, value): - for legacy_api in (False, True): - self._setitem(tag, value, legacy_api) - - def __getitem__(self, tag): - 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 when IFD_LEGACY_API == False -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=None, filename=None): - self.tag_v2 = None - """ Image file directory (tag dictionary) """ - - self.tag = None - """ 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) - - # legacy IFD entries will be filled in later - self.ifd = None - - # 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): - if self._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) - 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) - # Create a new core image object on second and - # subsequent frames in the image. Image may be - # different size/mode. - Image._decompression_bomb_check(self.size) - self.im = Image.core.new(self.mode, self.size) - - def _seek(self, frame: int) -> None: - self.fp = self._fp - - # reset buffered io handle in case fp - # was passed to libtiff, invalidating the buffer - self.fp.tell() - - 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: - self.info["xmp"] = self.tag_v2[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): - """ - 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[:4] == 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): - if self.tile and self.use_load_libtiff: - return self._load_libtiff() - return super().load() - - 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 - - # reset buffered io handle in case fp - # was passed to libtiff, invalidating the buffer - self.fp.tell() - - # 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): - """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 = list(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: - args[2] = fp - - decoder = Image._getdecoder( - self.mode, "libtiff", tuple(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) - # 4 bytes, otherwise the trace might error out - n, err = decoder.decode(b"fpfp") - 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: - raise OSError(err) - - return Image.Image.load(self) - - def _setup(self): - """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.get(YCBCRSUBSAMPLING)) - - # size - xsize = int(self.tag_v2.get(IMAGEWIDTH)) - ysize = int(self.tag_v2.get(IMAGELENGTH)) - 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] - # 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. - if rawmode == "I;16": - rawmode = "I;16N" - if ";16B" in rawmode: - rawmode = rawmode.replace(";16B", ";16N") - if ";16L" in rawmode: - rawmode = rawmode.replace(";16L", ";16N") - - # 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" - - # 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(("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 = self.size[0] - else: - # tiled image - offsets = self.tag_v2[TILEOFFSETS] - w = self.tag_v2.get(TILEWIDTH) - h = self.tag_v2.get(TILELENGTH) - - 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 - - a = (tile_rawmode, int(stride), 1) - self.tile.append( - ( - self._compression, - (x, y, min(x + w, xsize), min(y + h, ysize)), - offset, - a, - ) - ) - x = x + w - if x >= self.size[0]: - x, y = 0, y + h - if y >= self.size[1]: - x = 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;16S": ("I;16S", II, 1, 2, (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;32BS": ("I;32BS", MM, 1, 2, (32,), None), - "I;16B": ("I;16B", MM, 1, 1, (16,), None), - "I;16BS": ("I;16BS", MM, 1, 2, (16,), None), - "F;32BF": ("F;32BF", MM, 1, 3, (32,), None), -} - - -def _save(im, fp, filename): - 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 - - ifd = ImageFileDirectory_v2(prefix=prefix) - - encoderinfo = im.encoderinfo - encoderconfig = im.encoderconfig - 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() - 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, value in { - YCBCRSUBSAMPLING: (1, 1), - REFERENCEBLACKWHITE: (0, 255, 128, 255, 128, 255), - }.items(): - ifd.setdefault(tag, 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 = os.dup(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 = {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 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;16B", "I;16"): - 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: - # undone, change to self.decodermaxblock: - errcode, data = encoder.encode(16 * 1024)[1:] - if not _fp: - fp.write(data) - if errcode: - break - if _fp: - try: - os.close(_fp) - except OSError: - pass - 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, [("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) - im._debug_multipage = ifd - - -class AppendingTiffWriter: - 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, new=False): - if hasattr(fn, "read"): - self.f = fn - self.close_fp = False - else: - 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") - self.beginning = self.f.tell() - self.setup() - - def setup(self) -> None: - # Reset everything. - self.f.seek(self.beginning, os.SEEK_SET) - - self.whereToWriteNewIFDOffset = None - self.offsetOfNewPage = 0 - - self.IIMM = iimm = self.f.read(4) - if not iimm: - # empty file - first page - self.isFirst = True - return - - self.isFirst = False - if iimm == b"II\x2a\x00": - self.setEndian("<") - elif iimm == b"MM\x00\x2a": - self.setEndian(">") - else: - msg = "Invalid TIFF file header" - raise RuntimeError(msg) - - 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) - - ifd_offset = self.readLong() - ifd_offset += self.offsetOfNewPage - self.f.seek(self.whereToWriteNewIFDOffset) - self.writeLong(ifd_offset) - 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, whence=io.SEEK_SET): - 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}HHL" - - def skipIFDs(self) -> None: - while True: - ifd_offset = self.readLong() - if ifd_offset == 0: - self.whereToWriteNewIFDOffset = self.f.tell() - 4 - break - - self.f.seek(ifd_offset) - num_tags = self.readShort() - self.f.seek(num_tags * 12, os.SEEK_CUR) - - def write(self, data: bytes) -> int | None: - return self.f.write(data) - - def readShort(self) -> int: - (value,) = struct.unpack(self.shortFmt, self.f.read(2)) - return value - - def readLong(self) -> int: - (value,) = struct.unpack(self.longFmt, self.f.read(4)) - return value - - def rewriteLastShortToLong(self, value: int) -> None: - self.f.seek(-2, os.SEEK_CUR) - bytes_written = self.f.write(struct.pack(self.longFmt, value)) - if bytes_written is not None and bytes_written != 4: - msg = f"wrote only {bytes_written} bytes but wanted 4" - raise RuntimeError(msg) - - def rewriteLastShort(self, value: int) -> None: - self.f.seek(-2, os.SEEK_CUR) - bytes_written = self.f.write(struct.pack(self.shortFmt, value)) - if bytes_written is not None and bytes_written != 2: - msg = f"wrote only {bytes_written} bytes but wanted 2" - raise RuntimeError(msg) - - def rewriteLastLong(self, value: int) -> None: - self.f.seek(-4, os.SEEK_CUR) - bytes_written = self.f.write(struct.pack(self.longFmt, value)) - if bytes_written is not None and bytes_written != 4: - msg = f"wrote only {bytes_written} bytes but wanted 4" - raise RuntimeError(msg) - - def writeShort(self, value: int) -> None: - bytes_written = self.f.write(struct.pack(self.shortFmt, value)) - if bytes_written is not None and bytes_written != 2: - msg = f"wrote only {bytes_written} bytes but wanted 2" - raise RuntimeError(msg) - - def writeLong(self, value: int) -> None: - bytes_written = self.f.write(struct.pack(self.longFmt, value)) - if bytes_written is not None and bytes_written != 4: - msg = f"wrote only {bytes_written} bytes but wanted 4" - raise RuntimeError(msg) - - def close(self) -> None: - self.finalize() - self.f.close() - - def fixIFD(self) -> None: - num_tags = self.readShort() - - for i in range(num_tags): - tag, field_type, count = struct.unpack(self.tagFormat, self.f.read(8)) - - field_size = self.fieldSizes[field_type] - total_size = field_size * count - is_local = total_size <= 4 - offset: int | None - if not is_local: - offset = self.readLong() + self.offsetOfNewPage - self.rewriteLastLong(offset) - - if tag in self.Tags: - cur_pos = self.f.tell() - - if is_local: - self.fixOffsets( - count, isShort=(field_size == 2), isLong=(field_size == 4) - ) - self.f.seek(cur_pos + 4) - else: - self.f.seek(offset) - self.fixOffsets( - count, isShort=(field_size == 2), isLong=(field_size == 4) - ) - self.f.seek(cur_pos) - - offset = cur_pos = None - - elif is_local: - # skip the locally stored value that is not an offset - self.f.seek(4, os.SEEK_CUR) - - def fixOffsets( - self, count: int, isShort: bool = False, isLong: bool = False - ) -> None: - if not isShort and not isLong: - msg = "offset is neither short nor long" - raise RuntimeError(msg) - - for i in range(count): - offset = self.readShort() if isShort else self.readLong() - offset += self.offsetOfNewPage - if isShort and offset >= 65536: - # offset is now too large - we must convert shorts to longs - 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.rewriteLastShortToLong(offset) - self.f.seek(-10, os.SEEK_CUR) - self.writeShort(TiffTags.LONG) # rewrite the type to LONG - self.f.seek(8, os.SEEK_CUR) - elif isShort: - self.rewriteLastShort(offset) - else: - self.rewriteLastLong(offset) - - -def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: - encoderinfo = im.encoderinfo.copy() - encoderconfig = im.encoderconfig - append_images = list(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: - ims.encoderinfo = encoderinfo - ims.encoderconfig = encoderconfig - if not hasattr(ims, "n_frames"): - nfr = 1 - else: - nfr = ims.n_frames - - for idx in range(nfr): - ims.seek(idx) - ims.load() - _save(ims, tf, filename) - tf.newFrame() - 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/plotter-app/venv/lib/python3.8/site-packages/PIL/TiffTags.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/TiffTags.py deleted file mode 100644 index e318c87..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/TiffTags.py +++ /dev/null @@ -1,555 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# TIFF tags -# -# This module provides clear-text names for various well-known -# TIFF tags. the TIFF codec works just fine without it. -# -# Copyright (c) Secret Labs AB 1999. -# -# See the README file for information on usage and redistribution. -# - -## -# This module provides constants and clear-text names for various -# well-known TIFF tags. -## -from __future__ import annotations - -from typing import NamedTuple - - -class _TagInfo(NamedTuple): - value: int | None - name: str - type: int | None - length: int | None - enum: dict[str, int] - - -class TagInfo(_TagInfo): - __slots__: list[str] = [] - - def __new__(cls, value=None, name="unknown", type=None, length=None, enum=None): - return super().__new__(cls, value, name, type, length, enum or {}) - - def cvt_enum(self, value): - # 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, group=None): - """ - :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 = { - 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 = { - 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] = {} - - -def _populate(): - 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 tags in TAGS_V2_GROUPS.values(): - for k, v in tags.items(): - tags[k] = TagInfo(k, *v) - - -_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/plotter-app/venv/lib/python3.8/site-packages/PIL/WalImageFile.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/WalImageFile.py deleted file mode 100644 index fbd7be6..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/WalImageFile.py +++ /dev/null @@ -1,124 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# WAL file handling -# -# History: -# 2003-04-23 fl created -# -# Copyright (c) 2003 by Fredrik Lundh. -# -# See the README file for information on usage and redistribution. -# - -""" -This reader is based on the specification available from: -https://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml -and has been tested with a few sample files found using google. - -.. note:: - This format cannot be automatically recognized, so the reader - is not registered for use with :py:func:`PIL.Image.open()`. - To open a WAL file, use the :py:func:`PIL.WalImageFile.open()` function instead. -""" -from __future__ import annotations - -from . import Image, ImageFile -from ._binary import i32le as i32 - - -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): - if not self.im: - 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): - """ - 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/plotter-app/venv/lib/python3.8/site-packages/PIL/WebPImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/WebPImagePlugin.py deleted file mode 100644 index 59be5bf..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/WebPImagePlugin.py +++ /dev/null @@ -1,363 +0,0 @@ -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 - - -_VALID_WEBP_MODES = {"RGBX": True, "RGBA": True, "RGB": True} - -_VALID_WEBP_LEGACY_MODES = {"RGB": True, "RGBA": True} - -_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[:4] == 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: - if not _webp.HAVE_WEBPANIM: - # Legacy mode - data, width, height, self._mode, icc_profile, exif = _webp.WebPDecode( - self.fp.read() - ) - if icc_profile: - self.info["icc_profile"] = icc_profile - if exif: - self.info["exif"] = exif - self._size = width, height - self.fp = BytesIO(data) - self.tile = [("raw", (0, 0) + self.size, 0, self.mode)] - self.n_frames = 1 - self.is_animated = False - return - - # 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 - width, height, loop_count, bgcolor, frame_count, mode = self._decoder.get_info() - self._size = width, height - 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 - self.tile = [] - - # 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[str, 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): - # 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): - if _webp.HAVE_WEBPANIM: - 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 = [("raw", (0, 0) + self.size, 0, self.rawmode)] - - return super().load() - - def load_seek(self, pos: int) -> None: - pass - - def tell(self) -> int: - if not _webp.HAVE_WEBPANIM: - return super().tell() - - return self.__logical_frame - - -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[0], - im.size[1], - 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 # of frames in this image - nfr = getattr(ims, "n_frames", 1) - - for idx in range(nfr): - ims.seek(idx) - ims.load() - - # Make sure image mode is supported - frame = ims - rawmode = ims.mode - if ims.mode not in _VALID_WEBP_MODES: - alpha = ( - "A" in ims.mode - or "a" in ims.mode - or (ims.mode == "P" and "A" in ims.im.getpalettemode()) - ) - rawmode = "RGBA" if alpha else "RGB" - frame = ims.convert(rawmode) - - if rawmode == "RGB": - # For faster conversion, use RGBX - rawmode = "RGBX" - - # Append the frame to the animation encoder - enc.add( - frame.tobytes("raw", rawmode), - round(timestamp), - frame.size[0], - frame.size[1], - rawmode, - 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), 0, 0, "", 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 - - if im.mode not in _VALID_WEBP_LEGACY_MODES: - im = im.convert("RGBA" if im.has_transparency_data else "RGB") - - data = _webp.WebPEncode( - im.tobytes(), - im.size[0], - im.size[1], - lossless, - float(quality), - float(alpha_quality), - im.mode, - 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) - if _webp.HAVE_WEBPANIM: - Image.register_save_all(WebPImageFile.format, _save_all) - Image.register_extension(WebPImageFile.format, ".webp") - Image.register_mime(WebPImageFile.format, "image/webp") diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/WmfImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/WmfImagePlugin.py deleted file mode 100644 index 3d5cddc..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/WmfImagePlugin.py +++ /dev/null @@ -1,181 +0,0 @@ -# -# The Python Imaging Library -# $Id$ -# -# WMF stub codec -# -# history: -# 1996-12-14 fl Created -# 2004-02-22 fl Turned into a stub driver -# 2004-02-23 fl Added EMF support -# -# Copyright (c) Secret Labs AB 1997-2004. All rights reserved. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# -# WMF/EMF reference documentation: -# https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-WMF/[MS-WMF].pdf -# http://wvware.sourceforge.net/caolan/index.html -# http://wvware.sourceforge.net/caolan/ora-wmf.html -from __future__ import annotations - -from typing import IO - -from . import Image, ImageFile -from ._binary import i16le as word -from ._binary import si16le as short -from ._binary import si32le as _long - -_handler = None - - -def register_handler(handler: ImageFile.StubHandler | None) -> None: - """ - Install application-specific WMF image handler. - - :param handler: Handler object. - """ - global _handler - _handler = handler - - -if hasattr(Image.core, "drawwmf"): - # install default handler (windows only) - - class WmfHandler(ImageFile.StubHandler): - def open(self, im: ImageFile.StubImageFile) -> None: - 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[:6] == b"\xd7\xcd\xc6\x9a\x00\x00" or prefix[:4] == b"\x01\x00\x00\x00" - ) - - -## -# Image plugin for Windows metafiles. - - -class WmfStubImageFile(ImageFile.StubImageFile): - format = "WMF" - format_description = "Windows Metafile" - - def _open(self) -> None: - self._inch = None - - # check placable header - s = self.fp.read(80) - - if s[:6] == b"\xd7\xcd\xc6\x9a\x00\x00": - # placeable windows metafile - - # get units per inch - self._inch = word(s, 14) - - # 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"] // self._inch, - (y1 - y0) * self.info["dpi"] // self._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[:4] == 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 - y0) / (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 - - 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=None): - if dpi is not None and self._inch is not None: - self.info["dpi"] = dpi - x0, y0, x1, y1 = self.info["wmf_bbox"] - self._size = ( - (x1 - x0) * self.info["dpi"] // self._inch, - (y1 - y0) * self.info["dpi"] // self._inch, - ) - 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/plotter-app/venv/lib/python3.8/site-packages/PIL/XVThumbImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/XVThumbImagePlugin.py deleted file mode 100644 index c84adac..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/XVThumbImagePlugin.py +++ /dev/null @@ -1,81 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# XV Thumbnail file handler by Charles E. "Gene" Cash -# (gcash@magicnet.net) -# -# see xvcolor.c and xvbrowse.c in the sources to John Bradley's XV, -# available from ftp://ftp.cis.upenn.edu/pub/xv/ -# -# history: -# 98-08-15 cec created (b/w only) -# 98-12-09 cec added color palette -# 98-12-28 fl added to PIL (with only a few very minor modifications) -# -# To do: -# FIXME: make save work (this requires quantization support) -# -from __future__ import annotations - -from . import Image, ImageFile, ImagePalette -from ._binary import o8 - -_MAGIC = b"P7 332" - -# standard color palette for thumbnails (RGB332) -PALETTE = b"" -for r in range(8): - for g in range(8): - for b in range(4): - PALETTE = PALETTE + ( - o8((r * 255) // 7) + o8((g * 255) // 7) + o8((b * 255) // 3) - ) - - -def _accept(prefix: bytes) -> bool: - return prefix[:6] == _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 = [("raw", (0, 0) + self.size, self.fp.tell(), (self.mode, 0, 1))] - - -# -------------------------------------------------------------------- - -Image.register_open(XVThumbImageFile.format, XVThumbImageFile, _accept) diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/XbmImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/XbmImagePlugin.py deleted file mode 100644 index 6d11bbf..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/XbmImagePlugin.py +++ /dev/null @@ -1,98 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# XBM File handling -# -# History: -# 1995-09-08 fl Created -# 1996-11-01 fl Added save support -# 1997-07-07 fl Made header parser more tolerant -# 1997-07-22 fl Fixed yet another parser bug -# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4) -# 2001-05-13 fl Added hotspot handling (based on code from Bernhard Herzog) -# 2004-02-24 fl Allow some whitespace before first #define -# -# Copyright (c) 1997-2004 by Secret Labs AB -# Copyright (c) 1996-1997 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import re -from typing import IO - -from . import Image, ImageFile - -# XBM header -xbm_head = re.compile( - rb"\s*#define[ \t]+.*_width[ \t]+(?P[0-9]+)[\r\n]+" - b"#define[ \t]+.*_height[ \t]+(?P[0-9]+)[\r\n]+" - b"(?P" - b"#define[ \t]+[^_]*_x_hot[ \t]+(?P[0-9]+)[\r\n]+" - b"#define[ \t]+[^_]*_y_hot[ \t]+(?P[0-9]+)[\r\n]+" - b")?" - rb"[\000-\377]*_bits\[]" -) - - -def _accept(prefix: bytes) -> bool: - return prefix.lstrip()[:7] == 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 = [("xbm", (0, 0) + self.size, m.end(), None)] - - -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, [("xbm", (0, 0) + im.size, 0, None)]) - - 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/plotter-app/venv/lib/python3.8/site-packages/PIL/XpmImagePlugin.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/XpmImagePlugin.py deleted file mode 100644 index 8d56331..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/XpmImagePlugin.py +++ /dev/null @@ -1,125 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# XPM File handling -# -# History: -# 1996-12-29 fl Created -# 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.7) -# -# Copyright (c) Secret Labs AB 1997-2001. -# Copyright (c) Fredrik Lundh 1996-2001. -# -# See the README file for information on usage and redistribution. -# -from __future__ import annotations - -import re - -from . import Image, ImageFile, ImagePalette -from ._binary import o8 - -# XPM header -xpm_head = re.compile(b'"([0-9]*) ([0-9]*) ([0-9]*) ([0-9]*)') - - -def _accept(prefix: bytes) -> bool: - return prefix[:9] == b"/* XPM */" - - -## -# Image plugin for X11 pixel maps. - - -class XpmImageFile(ImageFile.ImageFile): - format = "XPM" - format_description = "X11 Pixel Map" - - def _open(self) -> None: - if not _accept(self.fp.read(9)): - msg = "not an XPM file" - raise SyntaxError(msg) - - # skip forward to next string - while True: - s = self.fp.readline() - if not s: - msg = "broken XPM file" - raise SyntaxError(msg) - m = xpm_head.match(s) - if m: - break - - self._size = int(m.group(1)), int(m.group(2)) - - pal = int(m.group(3)) - bpp = int(m.group(4)) - - if pal > 256 or bpp != 1: - msg = "cannot read this XPM file" - raise ValueError(msg) - - # - # load palette description - - palette = [b"\0\0\0"] * 256 - - for _ in range(pal): - s = self.fp.readline() - if s[-2:] == b"\r\n": - s = s[:-2] - elif s[-1:] in b"\r\n": - s = s[:-1] - - c = s[1] - s = s[2:-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[:1] == b"#": - # FIXME: handle colour names (see ImagePalette.py) - rgb = int(rgb[1:], 16) - palette[c] = ( - o8((rgb >> 16) & 255) + o8((rgb >> 8) & 255) + o8(rgb & 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) - - self._mode = "P" - self.palette = ImagePalette.raw("RGB", b"".join(palette)) - - self.tile = [("raw", (0, 0) + self.size, self.fp.tell(), ("P", 0, 1))] - - def load_read(self, read_bytes: int) -> bytes: - # - # load all image data in one chunk - - xsize, ysize = self.size - - s = [self.fp.readline()[1 : xsize + 1].ljust(xsize) for i in range(ysize)] - - return b"".join(s) - - -# -# Registry - - -Image.register_open(XpmImageFile.format, XpmImageFile, _accept) - -Image.register_extension(XpmImageFile.format, ".xpm") - -Image.register_mime(XpmImageFile.format, "image/xpm") diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/__init__.py deleted file mode 100644 index 09546fe..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/__init__.py +++ /dev/null @@ -1,86 +0,0 @@ -"""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 = [ - "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/plotter-app/venv/lib/python3.8/site-packages/PIL/__main__.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/__main__.py deleted file mode 100644 index 043156e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/__main__.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import annotations - -import sys - -from .features import pilinfo - -pilinfo(supported_formats="--report" not in sys.argv) diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/BdfFontFile.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/BdfFontFile.cpython-38.pyc deleted file mode 100644 index c20f5f2..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/BdfFontFile.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/BlpImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/BlpImagePlugin.cpython-38.pyc deleted file mode 100644 index 3d392d0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/BlpImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/BmpImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/BmpImagePlugin.cpython-38.pyc deleted file mode 100644 index d41d64d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/BmpImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/BufrStubImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/BufrStubImagePlugin.cpython-38.pyc deleted file mode 100644 index 4aa8c01..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/BufrStubImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ContainerIO.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ContainerIO.cpython-38.pyc deleted file mode 100644 index f4f7766..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ContainerIO.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/CurImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/CurImagePlugin.cpython-38.pyc deleted file mode 100644 index 4adb799..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/CurImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/DcxImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/DcxImagePlugin.cpython-38.pyc deleted file mode 100644 index 9d9c644..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/DcxImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/DdsImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/DdsImagePlugin.cpython-38.pyc deleted file mode 100644 index 1c58f83..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/DdsImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/EpsImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/EpsImagePlugin.cpython-38.pyc deleted file mode 100644 index 6804130..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/EpsImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ExifTags.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ExifTags.cpython-38.pyc deleted file mode 100644 index 2b02dc5..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ExifTags.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/FitsImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/FitsImagePlugin.cpython-38.pyc deleted file mode 100644 index 00e4334..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/FitsImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/FliImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/FliImagePlugin.cpython-38.pyc deleted file mode 100644 index f66bcd6..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/FliImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/FontFile.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/FontFile.cpython-38.pyc deleted file mode 100644 index 6fa709a..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/FontFile.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/FpxImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/FpxImagePlugin.cpython-38.pyc deleted file mode 100644 index 9c16349..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/FpxImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/FtexImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/FtexImagePlugin.cpython-38.pyc deleted file mode 100644 index a56026b..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/FtexImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/GbrImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/GbrImagePlugin.cpython-38.pyc deleted file mode 100644 index ac358f9..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/GbrImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/GdImageFile.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/GdImageFile.cpython-38.pyc deleted file mode 100644 index 0add9e4..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/GdImageFile.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/GifImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/GifImagePlugin.cpython-38.pyc deleted file mode 100644 index 8f53946..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/GifImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/GimpGradientFile.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/GimpGradientFile.cpython-38.pyc deleted file mode 100644 index b7109d5..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/GimpGradientFile.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/GimpPaletteFile.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/GimpPaletteFile.cpython-38.pyc deleted file mode 100644 index 0464513..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/GimpPaletteFile.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/GribStubImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/GribStubImagePlugin.cpython-38.pyc deleted file mode 100644 index a9a13b8..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/GribStubImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/Hdf5StubImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/Hdf5StubImagePlugin.cpython-38.pyc deleted file mode 100644 index 5dc7988..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/Hdf5StubImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/IcnsImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/IcnsImagePlugin.cpython-38.pyc deleted file mode 100644 index bd6e03d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/IcnsImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/IcoImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/IcoImagePlugin.cpython-38.pyc deleted file mode 100644 index c0a6053..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/IcoImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImImagePlugin.cpython-38.pyc deleted file mode 100644 index ab60aec..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/Image.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/Image.cpython-38.pyc deleted file mode 100644 index 8ed9d96..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/Image.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageChops.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageChops.cpython-38.pyc deleted file mode 100644 index 5232092..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageChops.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageCms.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageCms.cpython-38.pyc deleted file mode 100644 index 4775b88..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageCms.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageColor.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageColor.cpython-38.pyc deleted file mode 100644 index 2f5d198..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageColor.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageDraw.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageDraw.cpython-38.pyc deleted file mode 100644 index b830122..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageDraw.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageDraw2.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageDraw2.cpython-38.pyc deleted file mode 100644 index fdffb98..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageDraw2.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageEnhance.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageEnhance.cpython-38.pyc deleted file mode 100644 index 5b67959..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageEnhance.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageFile.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageFile.cpython-38.pyc deleted file mode 100644 index 6cdbd24..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageFile.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageFilter.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageFilter.cpython-38.pyc deleted file mode 100644 index 3d22c0f..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageFilter.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageFont.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageFont.cpython-38.pyc deleted file mode 100644 index 2517a17..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageFont.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageGrab.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageGrab.cpython-38.pyc deleted file mode 100644 index 74c5012..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageGrab.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageMath.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageMath.cpython-38.pyc deleted file mode 100644 index 4588d3b..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageMath.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageMode.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageMode.cpython-38.pyc deleted file mode 100644 index 2a73daf..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageMode.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageMorph.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageMorph.cpython-38.pyc deleted file mode 100644 index c6405b7..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageMorph.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageOps.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageOps.cpython-38.pyc deleted file mode 100644 index 9203411..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageOps.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImagePalette.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImagePalette.cpython-38.pyc deleted file mode 100644 index d979726..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImagePalette.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImagePath.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImagePath.cpython-38.pyc deleted file mode 100644 index f23f61d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImagePath.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageQt.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageQt.cpython-38.pyc deleted file mode 100644 index 7b0f067..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageQt.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageSequence.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageSequence.cpython-38.pyc deleted file mode 100644 index a3678b9..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageSequence.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageShow.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageShow.cpython-38.pyc deleted file mode 100644 index 2c58c98..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageShow.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageStat.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageStat.cpython-38.pyc deleted file mode 100644 index 02f49c0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageStat.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageTk.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageTk.cpython-38.pyc deleted file mode 100644 index 3fac96d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageTk.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageTransform.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageTransform.cpython-38.pyc deleted file mode 100644 index 8b53ae0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageTransform.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageWin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageWin.cpython-38.pyc deleted file mode 100644 index 445bcf4..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImageWin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImtImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImtImagePlugin.cpython-38.pyc deleted file mode 100644 index a719acf..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/ImtImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/IptcImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/IptcImagePlugin.cpython-38.pyc deleted file mode 100644 index a773749..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/IptcImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/Jpeg2KImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/Jpeg2KImagePlugin.cpython-38.pyc deleted file mode 100644 index cafbdc2..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/Jpeg2KImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/JpegImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/JpegImagePlugin.cpython-38.pyc deleted file mode 100644 index e92535a..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/JpegImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/JpegPresets.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/JpegPresets.cpython-38.pyc deleted file mode 100644 index bbab487..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/JpegPresets.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/McIdasImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/McIdasImagePlugin.cpython-38.pyc deleted file mode 100644 index 7c4a4ac..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/McIdasImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/MicImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/MicImagePlugin.cpython-38.pyc deleted file mode 100644 index 660805c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/MicImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/MpegImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/MpegImagePlugin.cpython-38.pyc deleted file mode 100644 index a326f49..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/MpegImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/MpoImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/MpoImagePlugin.cpython-38.pyc deleted file mode 100644 index 8181d18..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/MpoImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/MspImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/MspImagePlugin.cpython-38.pyc deleted file mode 100644 index 27423db..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/MspImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PSDraw.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PSDraw.cpython-38.pyc deleted file mode 100644 index 0ef07e8..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PSDraw.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PaletteFile.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PaletteFile.cpython-38.pyc deleted file mode 100644 index 02ae35c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PaletteFile.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PalmImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PalmImagePlugin.cpython-38.pyc deleted file mode 100644 index c11e815..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PalmImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PcdImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PcdImagePlugin.cpython-38.pyc deleted file mode 100644 index 983913d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PcdImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PcfFontFile.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PcfFontFile.cpython-38.pyc deleted file mode 100644 index f3e3224..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PcfFontFile.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PcxImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PcxImagePlugin.cpython-38.pyc deleted file mode 100644 index b903e32..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PcxImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PdfImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PdfImagePlugin.cpython-38.pyc deleted file mode 100644 index e8b16da..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PdfImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PdfParser.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PdfParser.cpython-38.pyc deleted file mode 100644 index 09a09fb..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PdfParser.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PixarImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PixarImagePlugin.cpython-38.pyc deleted file mode 100644 index 7b4034e..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PixarImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PngImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PngImagePlugin.cpython-38.pyc deleted file mode 100644 index 57aa31b..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PngImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PpmImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PpmImagePlugin.cpython-38.pyc deleted file mode 100644 index 8d7689f..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PpmImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PsdImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PsdImagePlugin.cpython-38.pyc deleted file mode 100644 index 1aea729..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PsdImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PyAccess.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PyAccess.cpython-38.pyc deleted file mode 100644 index f6224f0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/PyAccess.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/QoiImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/QoiImagePlugin.cpython-38.pyc deleted file mode 100644 index 44b8556..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/QoiImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/SgiImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/SgiImagePlugin.cpython-38.pyc deleted file mode 100644 index fe1a5e0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/SgiImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/SpiderImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/SpiderImagePlugin.cpython-38.pyc deleted file mode 100644 index 7792db2..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/SpiderImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/SunImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/SunImagePlugin.cpython-38.pyc deleted file mode 100644 index 97572ef..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/SunImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/TarIO.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/TarIO.cpython-38.pyc deleted file mode 100644 index f66ea21..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/TarIO.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/TgaImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/TgaImagePlugin.cpython-38.pyc deleted file mode 100644 index 61f3250..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/TgaImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/TiffImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/TiffImagePlugin.cpython-38.pyc deleted file mode 100644 index 1971fbd..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/TiffImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/TiffTags.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/TiffTags.cpython-38.pyc deleted file mode 100644 index e2d5b61..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/TiffTags.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/WalImageFile.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/WalImageFile.cpython-38.pyc deleted file mode 100644 index 4981bf8..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/WalImageFile.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/WebPImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/WebPImagePlugin.cpython-38.pyc deleted file mode 100644 index 923cb21..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/WebPImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/WmfImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/WmfImagePlugin.cpython-38.pyc deleted file mode 100644 index cb6b7c9..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/WmfImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/XVThumbImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/XVThumbImagePlugin.cpython-38.pyc deleted file mode 100644 index 5d6a6f1..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/XVThumbImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/XbmImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/XbmImagePlugin.cpython-38.pyc deleted file mode 100644 index c325b3d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/XbmImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/XpmImagePlugin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/XpmImagePlugin.cpython-38.pyc deleted file mode 100644 index f6af069..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/XpmImagePlugin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index ec09d49..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/__main__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/__main__.cpython-38.pyc deleted file mode 100644 index b09624a..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/__main__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/_binary.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/_binary.cpython-38.pyc deleted file mode 100644 index 32eeda7..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/_binary.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/_deprecate.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/_deprecate.cpython-38.pyc deleted file mode 100644 index 93fc248..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/_deprecate.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/_tkinter_finder.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/_tkinter_finder.cpython-38.pyc deleted file mode 100644 index 201ff97..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/_tkinter_finder.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/_typing.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/_typing.cpython-38.pyc deleted file mode 100644 index 75a1de8..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/_typing.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/_util.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/_util.cpython-38.pyc deleted file mode 100644 index 945bd5d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/_util.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/_version.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/_version.cpython-38.pyc deleted file mode 100644 index 86ef13c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/_version.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/features.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/features.cpython-38.pyc deleted file mode 100644 index 01628f4..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/features.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/report.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/report.cpython-38.pyc deleted file mode 100644 index cf9da77..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/__pycache__/report.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/_binary.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/_binary.py deleted file mode 100644 index 4594ccc..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/_binary.py +++ /dev/null @@ -1,112 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# Binary input/output support routines. -# -# Copyright (c) 1997-2003 by Secret Labs AB -# Copyright (c) 1995-2003 by Fredrik Lundh -# Copyright (c) 2012 by Brian Crowell -# -# See the README file for information on usage and redistribution. -# - - -"""Binary input/output support routines.""" -from __future__ import annotations - -from struct import pack, unpack_from - - -def i8(c: bytes) -> int: - return c[0] - - -def o8(i: int) -> bytes: - return bytes((i & 255,)) - - -# Input, le = little endian, be = big endian -def i16le(c: bytes, o: int = 0) -> int: - """ - Converts a 2-bytes (16 bits) string to an unsigned integer. - - :param c: string containing bytes to convert - :param o: offset of bytes to convert in string - """ - return unpack_from(" int: - """ - Converts a 2-bytes (16 bits) string to a signed integer. - - :param c: string containing bytes to convert - :param o: offset of bytes to convert in string - """ - return unpack_from(" int: - """ - Converts a 2-bytes (16 bits) string to a signed integer, big endian. - - :param c: string containing bytes to convert - :param o: offset of bytes to convert in string - """ - return unpack_from(">h", c, o)[0] - - -def i32le(c: bytes, o: int = 0) -> int: - """ - Converts a 4-bytes (32 bits) string to an unsigned integer. - - :param c: string containing bytes to convert - :param o: offset of bytes to convert in string - """ - return unpack_from(" int: - """ - Converts a 4-bytes (32 bits) string to a signed integer. - - :param c: string containing bytes to convert - :param o: offset of bytes to convert in string - """ - return unpack_from(" int: - """ - Converts a 4-bytes (32 bits) string to a signed integer, big endian. - - :param c: string containing bytes to convert - :param o: offset of bytes to convert in string - """ - return unpack_from(">i", c, o)[0] - - -def i16be(c: bytes, o: int = 0) -> int: - return unpack_from(">H", c, o)[0] - - -def i32be(c: bytes, o: int = 0) -> int: - return unpack_from(">I", c, o)[0] - - -# Output, le = little endian, be = big endian -def o16le(i: int) -> bytes: - return pack(" bytes: - return pack(" bytes: - return pack(">H", i) - - -def o32be(i: int) -> bytes: - return pack(">I", i) diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/_deprecate.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/_deprecate.py deleted file mode 100644 index 33a0e07..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/_deprecate.py +++ /dev/null @@ -1,71 +0,0 @@ -from __future__ import annotations - -import warnings - -from . import __version__ - - -def deprecate( - deprecated: str, - when: int | None, - replacement: str | None = None, - *, - action: str | None = None, - plural: bool = False, -) -> 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 == 11: - removed = "Pillow 11 (2024-10-15)" - elif when == 12: - removed = "Pillow 12 (2025-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=3, - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/_imaging.cpython-38-x86_64-linux-gnu.so b/plotter-app/venv/lib/python3.8/site-packages/PIL/_imaging.cpython-38-x86_64-linux-gnu.so deleted file mode 100755 index c793234..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/_imaging.cpython-38-x86_64-linux-gnu.so and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/_imaging.pyi b/plotter-app/venv/lib/python3.8/site-packages/PIL/_imaging.pyi deleted file mode 100644 index 8cccd3a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/_imaging.pyi +++ /dev/null @@ -1,30 +0,0 @@ -from typing import Any - -class ImagingCore: - 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/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingcms.cpython-38-x86_64-linux-gnu.so b/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingcms.cpython-38-x86_64-linux-gnu.so deleted file mode 100755 index 876cba0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingcms.cpython-38-x86_64-linux-gnu.so and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingcms.pyi b/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingcms.pyi deleted file mode 100644 index 2abd6d0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingcms.pyi +++ /dev/null @@ -1,141 +0,0 @@ -import datetime -import sys -from typing import Literal, SupportsFloat, TypedDict - -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: int, id_out: int) -> 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/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingft.cpython-38-x86_64-linux-gnu.so b/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingft.cpython-38-x86_64-linux-gnu.so deleted file mode 100755 index 2a9f0f1..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingft.cpython-38-x86_64-linux-gnu.so and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingft.pyi b/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingft.pyi deleted file mode 100644 index 5e97b40..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingft.pyi +++ /dev/null @@ -1,69 +0,0 @@ -from typing import Any, TypedDict - -from . import _imaging - -class _Axis(TypedDict): - minimum: int | None - default: int | None - maximum: int | None - name: bytes | None - -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, - mode=..., - dir=..., - features=..., - lang=..., - stroke_width=..., - anchor=..., - foreground_ink_long=..., - x_start=..., - y_start=..., - /, - ) -> tuple[_imaging.ImagingCore, tuple[int, int]]: ... - def getsize( - self, - string: str | bytes | bytearray, - mode=..., - dir=..., - features=..., - lang=..., - anchor=..., - /, - ) -> tuple[tuple[int, int], tuple[int, int]]: ... - def getlength( - self, string: str | bytes, mode=..., dir=..., features=..., lang=..., / - ) -> float: ... - def getvarnames(self) -> list[bytes]: ... - def getvaraxes(self) -> list[_Axis] | None: ... - def setvarname(self, instance_index: int, /) -> None: ... - def setvaraxes(self, axes: list[float], /) -> None: ... - -def getfont( - filename: str | bytes, - size: float, - index=..., - encoding=..., - font_bytes=..., - layout_engine=..., -) -> Font: ... -def __getattr__(name: str) -> Any: ... diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingmath.cpython-38-x86_64-linux-gnu.so b/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingmath.cpython-38-x86_64-linux-gnu.so deleted file mode 100755 index c998f76..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingmath.cpython-38-x86_64-linux-gnu.so and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingmath.pyi b/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingmath.pyi deleted file mode 100644 index e27843e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingmath.pyi +++ /dev/null @@ -1,3 +0,0 @@ -from typing import Any - -def __getattr__(name: str) -> Any: ... diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingmorph.cpython-38-x86_64-linux-gnu.so b/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingmorph.cpython-38-x86_64-linux-gnu.so deleted file mode 100755 index e90c7e7..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingmorph.cpython-38-x86_64-linux-gnu.so and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingmorph.pyi b/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingmorph.pyi deleted file mode 100644 index e27843e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingmorph.pyi +++ /dev/null @@ -1,3 +0,0 @@ -from typing import Any - -def __getattr__(name: str) -> Any: ... diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingtk.cpython-38-x86_64-linux-gnu.so b/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingtk.cpython-38-x86_64-linux-gnu.so deleted file mode 100755 index 817e7c4..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/_imagingtk.cpython-38-x86_64-linux-gnu.so and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/_tkinter_finder.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/_tkinter_finder.py deleted file mode 100644 index beddfb0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/_tkinter_finder.py +++ /dev/null @@ -1,21 +0,0 @@ -""" Find compiled module linking to Tcl / Tk libraries -""" - -from __future__ import annotations - -import sys -import tkinter - -tk = getattr(tkinter, "_tkinter") - -try: - if hasattr(sys, "pypy_find_executable"): - TKINTER_LIB = tk.tklib_cffi.__file__ - else: - TKINTER_LIB = tk.__file__ -except AttributeError: - # _tkinter may be compiled directly into Python, in which case __file__ is - # not available. load_tkinter_funcs will check the binary first in any case. - TKINTER_LIB = None - -tk_version = str(tkinter.TkVersion) diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/_typing.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/_typing.py deleted file mode 100644 index 09ece18..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/_typing.py +++ /dev/null @@ -1,39 +0,0 @@ -from __future__ import annotations - -import os -import sys -from typing import Any, Protocol, Sequence, TypeVar, Union - -try: - import numpy.typing as npt - - NumpyArray = npt.NDArray[Any] -except ImportError: - pass - -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__ = ["TypeGuard", "StrOrBytesPath", "SupportsRead"] diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/_util.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/_util.py deleted file mode 100644 index 6bc7628..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/_util.py +++ /dev/null @@ -1,31 +0,0 @@ -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)) - - -def is_directory(f: Any) -> TypeGuard[StrOrBytesPath]: - """Checks if an object is a string, and that it points to a directory.""" - return is_path(f) and os.path.isdir(f) - - -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/plotter-app/venv/lib/python3.8/site-packages/PIL/_version.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/_version.py deleted file mode 100644 index cebfd86..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/_version.py +++ /dev/null @@ -1,4 +0,0 @@ -# Master version for Pillow -from __future__ import annotations - -__version__ = "10.4.0" diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/_webp.cpython-38-x86_64-linux-gnu.so b/plotter-app/venv/lib/python3.8/site-packages/PIL/_webp.cpython-38-x86_64-linux-gnu.so deleted file mode 100755 index 285b582..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/PIL/_webp.cpython-38-x86_64-linux-gnu.so and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/_webp.pyi b/plotter-app/venv/lib/python3.8/site-packages/PIL/_webp.pyi deleted file mode 100644 index e27843e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/_webp.pyi +++ /dev/null @@ -1,3 +0,0 @@ -from typing import Any - -def __getattr__(name: str) -> Any: ... diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/features.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/features.py deleted file mode 100644 index 13908c4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/features.py +++ /dev/null @@ -1,340 +0,0 @@ -from __future__ import annotations - -import collections -import os -import sys -import warnings -from typing import IO - -import PIL - -from . import Image - -modules = { - "pil": ("PIL._imaging", "PILLOW_VERSION"), - "tkinter": ("PIL._tkinter_finder", "tk_version"), - "freetype2": ("PIL._imagingft", "freetype2_version"), - "littlecms2": ("PIL._imagingcms", "littlecms_version"), - "webp": ("PIL._webp", "webpdecoder_version"), -} - - -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 = { - "webp_anim": ("PIL._webp", "HAVE_WEBPANIM", None), - "webp_mux": ("PIL._webp", "HAVE_WEBPMUX", None), - "transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY", 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"), - "libimagequant": ("PIL._imaging", "HAVE_LIBIMAGEQUANT", "imagequant_version"), - "xcb": ("PIL._imaging", "HAVE_XCB", None), -} - - -def check_feature(feature: str) -> bool | None: - """ - Checks if a feature is available. - - :param feature: The feature to check for. - :returns: ``True`` if available, ``False`` if unavailable, ``None`` if unknown. - :raises ValueError: If the feature is not defined in this version of Pillow. - """ - if feature not in features: - msg = f"Unknown feature {feature}" - raise ValueError(msg) - - module, flag, ver = features[feature] - - try: - imported_module = __import__(module, fromlist=["PIL"]) - return getattr(imported_module, flag) - except ModuleNotFoundError: - return None - except ImportError as ex: - warnings.warn(str(ex)) - return None - - -def version_feature(feature: str) -> str | None: - """ - :param feature: The feature to check for. - :returns: The version number as a string, or ``None`` if not available. - :raises ValueError: If the feature is not defined in this version of Pillow. - """ - if not check_feature(feature): - return None - - module, flag, ver = features[feature] - - if ver is None: - return None - - return getattr(__import__(module, fromlist=[ver]), ver) - - -def get_supported_features() -> list[str]: - """ - :returns: A list of all supported features. - """ - return [f for f in features if check_feature(f)] - - -def check(feature: str) -> bool | None: - """ - :param feature: A module, codec, or feature name. - :returns: - ``True`` if the module, codec, or feature is available, - ``False`` or ``None`` otherwise. - """ - - if feature in modules: - return check_module(feature) - if feature in codecs: - return check_codec(feature) - if feature in features: - return check_feature(feature) - warnings.warn(f"Unknown feature '{feature}'.", stacklevel=2) - return False - - -def version(feature: str) -> str | None: - """ - :param feature: - The module, codec, or feature to check for. - :returns: - The version number as a string, or ``None`` if unknown or not available. - """ - if feature in modules: - return version_module(feature) - if feature in codecs: - return version_codec(feature) - if feature in features: - return version_feature(feature) - return None - - -def get_supported() -> list[str]: - """ - :returns: A list of all supported modules, features, and codecs. - """ - - ret = get_supported_modules() - ret.extend(get_supported_features()) - ret.extend(get_supported_codecs()) - return ret - - -def pilinfo(out: IO[str] | None = None, supported_formats: bool = True) -> None: - """ - Prints information about this installation of Pillow. - This function can be called with ``python3 -m PIL``. - It can also be called with ``python3 -m PIL.report`` or ``python3 -m PIL --report`` - to have "supported_formats" set to ``False``, omitting the list of all supported - image file formats. - - :param out: - The output stream to print to. Defaults to ``sys.stdout`` if ``None``. - :param supported_formats: - If ``True``, a list of all supported image file formats will be printed. - """ - - if out is None: - out = sys.stdout - - Image.init() - - print("-" * 68, file=out) - print(f"Pillow {PIL.__version__}", file=out) - py_version_lines = sys.version.splitlines() - print(f"Python {py_version_lines[0].strip()}", file=out) - for py_version in py_version_lines[1:]: - print(f" {py_version.strip()}", file=out) - print("-" * 68, file=out) - print(f"Python executable is {sys.executable or 'unknown'}", file=out) - if sys.prefix != sys.base_prefix: - print(f"Environment Python files loaded from {sys.prefix}", file=out) - print(f"System Python files loaded from {sys.base_prefix}", file=out) - print("-" * 68, file=out) - print( - f"Python Pillow modules loaded from {os.path.dirname(Image.__file__)}", - file=out, - ) - print( - f"Binary Pillow modules loaded from {os.path.dirname(Image.core.__file__)}", - file=out, - ) - print("-" * 68, file=out) - - for name, feature in [ - ("pil", "PIL CORE"), - ("tkinter", "TKINTER"), - ("freetype2", "FREETYPE2"), - ("littlecms2", "LITTLECMS2"), - ("webp", "WEBP"), - ("transp_webp", "WEBP Transparency"), - ("webp_mux", "WEBPMUX"), - ("webp_anim", "WEBP Animation"), - ("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 = "libjpeg-turbo " + 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 == "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/plotter-app/venv/lib/python3.8/site-packages/PIL/py.typed b/plotter-app/venv/lib/python3.8/site-packages/PIL/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/PIL/report.py b/plotter-app/venv/lib/python3.8/site-packages/PIL/report.py deleted file mode 100644 index d2815e8..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PIL/report.py +++ /dev/null @@ -1,5 +0,0 @@ -from __future__ import annotations - -from .features import pilinfo - -pilinfo(supported_formats=False) diff --git a/plotter-app/venv/lib/python3.8/site-packages/PyYAML-6.0.2.dist-info/INSTALLER b/plotter-app/venv/lib/python3.8/site-packages/PyYAML-6.0.2.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PyYAML-6.0.2.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/plotter-app/venv/lib/python3.8/site-packages/PyYAML-6.0.2.dist-info/LICENSE b/plotter-app/venv/lib/python3.8/site-packages/PyYAML-6.0.2.dist-info/LICENSE deleted file mode 100644 index 2f1b8e1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PyYAML-6.0.2.dist-info/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2017-2021 Ingy döt Net -Copyright (c) 2006-2016 Kirill Simonov - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/plotter-app/venv/lib/python3.8/site-packages/PyYAML-6.0.2.dist-info/METADATA b/plotter-app/venv/lib/python3.8/site-packages/PyYAML-6.0.2.dist-info/METADATA deleted file mode 100644 index db029b7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PyYAML-6.0.2.dist-info/METADATA +++ /dev/null @@ -1,46 +0,0 @@ -Metadata-Version: 2.1 -Name: PyYAML -Version: 6.0.2 -Summary: YAML parser and emitter for Python -Home-page: https://pyyaml.org/ -Download-URL: https://pypi.org/project/PyYAML/ -Author: Kirill Simonov -Author-email: xi@resolvent.net -License: MIT -Project-URL: Bug Tracker, https://github.com/yaml/pyyaml/issues -Project-URL: CI, https://github.com/yaml/pyyaml/actions -Project-URL: Documentation, https://pyyaml.org/wiki/PyYAMLDocumentation -Project-URL: Mailing lists, http://lists.sourceforge.net/lists/listinfo/yaml-core -Project-URL: Source Code, https://github.com/yaml/pyyaml -Platform: Any -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Cython -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: Text Processing :: Markup -Requires-Python: >=3.8 -License-File: LICENSE - -YAML is a data serialization format designed for human readability -and interaction with scripting languages. PyYAML is a YAML parser -and emitter for Python. - -PyYAML features a complete YAML 1.1 parser, Unicode support, pickle -support, capable extension API, and sensible error messages. PyYAML -supports standard YAML tags and provides Python-specific tags that -allow to represent an arbitrary Python object. - -PyYAML is applicable for a broad range of tasks from complex -configuration files to object serialization and persistence. diff --git a/plotter-app/venv/lib/python3.8/site-packages/PyYAML-6.0.2.dist-info/RECORD b/plotter-app/venv/lib/python3.8/site-packages/PyYAML-6.0.2.dist-info/RECORD deleted file mode 100644 index 2830b3d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PyYAML-6.0.2.dist-info/RECORD +++ /dev/null @@ -1,43 +0,0 @@ -PyYAML-6.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -PyYAML-6.0.2.dist-info/LICENSE,sha256=jTko-dxEkP1jVwfLiOsmvXZBAqcoKVQwfT5RZ6V36KQ,1101 -PyYAML-6.0.2.dist-info/METADATA,sha256=9-odFB5seu4pGPcEv7E8iyxNF51_uKnaNGjLAhz2lto,2060 -PyYAML-6.0.2.dist-info/RECORD,, -PyYAML-6.0.2.dist-info/WHEEL,sha256=BNLU9SWje0ru8009ZyWPG7zS_e_-YS0KxJU6xSwvdrU,148 -PyYAML-6.0.2.dist-info/top_level.txt,sha256=rpj0IVMTisAjh_1vG3Ccf9v5jpCQwAz6cD1IVU5ZdhQ,11 -_yaml/__init__.py,sha256=04Ae_5osxahpJHa3XBZUAf4wi6XX32gR8D6X6p64GEA,1402 -_yaml/__pycache__/__init__.cpython-38.pyc,, -yaml/__init__.py,sha256=N35S01HMesFTe0aRRMWkPj0Pa8IEbHpE9FK7cr5Bdtw,12311 -yaml/__pycache__/__init__.cpython-38.pyc,, -yaml/__pycache__/composer.cpython-38.pyc,, -yaml/__pycache__/constructor.cpython-38.pyc,, -yaml/__pycache__/cyaml.cpython-38.pyc,, -yaml/__pycache__/dumper.cpython-38.pyc,, -yaml/__pycache__/emitter.cpython-38.pyc,, -yaml/__pycache__/error.cpython-38.pyc,, -yaml/__pycache__/events.cpython-38.pyc,, -yaml/__pycache__/loader.cpython-38.pyc,, -yaml/__pycache__/nodes.cpython-38.pyc,, -yaml/__pycache__/parser.cpython-38.pyc,, -yaml/__pycache__/reader.cpython-38.pyc,, -yaml/__pycache__/representer.cpython-38.pyc,, -yaml/__pycache__/resolver.cpython-38.pyc,, -yaml/__pycache__/scanner.cpython-38.pyc,, -yaml/__pycache__/serializer.cpython-38.pyc,, -yaml/__pycache__/tokens.cpython-38.pyc,, -yaml/_yaml.cpython-38-x86_64-linux-gnu.so,sha256=_3oKdUU-cDdJ8ZRrTSE72_cSPNW2wottYUd4Iw2VVmA,2425936 -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/plotter-app/venv/lib/python3.8/site-packages/PyYAML-6.0.2.dist-info/WHEEL b/plotter-app/venv/lib/python3.8/site-packages/PyYAML-6.0.2.dist-info/WHEEL deleted file mode 100644 index a96c739..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PyYAML-6.0.2.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.44.0) -Root-Is-Purelib: false -Tag: cp38-cp38-manylinux_2_17_x86_64 -Tag: cp38-cp38-manylinux2014_x86_64 - diff --git a/plotter-app/venv/lib/python3.8/site-packages/PyYAML-6.0.2.dist-info/top_level.txt b/plotter-app/venv/lib/python3.8/site-packages/PyYAML-6.0.2.dist-info/top_level.txt deleted file mode 100644 index e6475e9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/PyYAML-6.0.2.dist-info/top_level.txt +++ /dev/null @@ -1,2 +0,0 @@ -_yaml -yaml diff --git a/plotter-app/venv/lib/python3.8/site-packages/__pycache__/easy_install.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/__pycache__/easy_install.cpython-38.pyc deleted file mode 100644 index 713d8de..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/__pycache__/easy_install.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/__pycache__/pylab.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/__pycache__/pylab.cpython-38.pyc deleted file mode 100644 index 4d59d6c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/__pycache__/pylab.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/__pycache__/six.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/__pycache__/six.cpython-38.pyc deleted file mode 100644 index 2f765cf..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/__pycache__/six.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/_yaml/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/_yaml/__init__.py deleted file mode 100644 index 7baa8c4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/_yaml/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# This is a stub package designed to roughly emulate the _yaml -# extension module, which previously existed as a standalone module -# and has been moved into the `yaml` package namespace. -# It does not perfectly mimic its old counterpart, but should get -# close enough for anyone who's relying on it even when they shouldn't. -import yaml - -# in some circumstances, the yaml module we imoprted may be from a different version, so we need -# to tread carefully when poking at it here (it may not have the attributes we expect) -if not getattr(yaml, '__with_libyaml__', False): - from sys import version_info - - exc = ModuleNotFoundError if version_info >= (3, 6) else ImportError - raise exc("No module named '_yaml'") -else: - from yaml._yaml import * - import warnings - warnings.warn( - 'The _yaml extension module is now located at yaml._yaml' - ' and its location is subject to change. To use the' - ' LibYAML-based parser and emitter, import from `yaml`:' - ' `from yaml import CLoader as Loader, CDumper as Dumper`.', - DeprecationWarning - ) - del warnings - # Don't `del yaml` here because yaml is actually an existing - # namespace member of _yaml. - -__name__ = '_yaml' -# If the module is top-level (i.e. not a part of any specific package) -# then the attribute should be set to ''. -# https://docs.python.org/3.8/library/types.html -__package__ = '' diff --git a/plotter-app/venv/lib/python3.8/site-packages/_yaml/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/_yaml/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index cc9e4ab..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/_yaml/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/blinker-1.8.2.dist-info/INSTALLER b/plotter-app/venv/lib/python3.8/site-packages/blinker-1.8.2.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/blinker-1.8.2.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/plotter-app/venv/lib/python3.8/site-packages/blinker-1.8.2.dist-info/LICENSE.txt b/plotter-app/venv/lib/python3.8/site-packages/blinker-1.8.2.dist-info/LICENSE.txt deleted file mode 100644 index 79c9825..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/blinker-1.8.2.dist-info/LICENSE.txt +++ /dev/null @@ -1,20 +0,0 @@ -Copyright 2010 Jason Kirtland - -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/plotter-app/venv/lib/python3.8/site-packages/blinker-1.8.2.dist-info/METADATA b/plotter-app/venv/lib/python3.8/site-packages/blinker-1.8.2.dist-info/METADATA deleted file mode 100644 index efa45f5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/blinker-1.8.2.dist-info/METADATA +++ /dev/null @@ -1,60 +0,0 @@ -Metadata-Version: 2.1 -Name: blinker -Version: 1.8.2 -Summary: Fast, simple object-to-object and broadcast signaling -Author: Jason Kirtland -Maintainer-email: Pallets Ecosystem -Requires-Python: >=3.8 -Description-Content-Type: text/markdown -Classifier: Development Status :: 5 - Production/Stable -Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python -Classifier: Typing :: Typed -Project-URL: Chat, https://discord.gg/pallets -Project-URL: Documentation, https://blinker.readthedocs.io -Project-URL: Source, https://github.com/pallets-eco/blinker/ - -# Blinker - -Blinker provides a fast dispatching system that allows any number of -interested parties to subscribe to events, or "signals". - - -## Pallets Community Ecosystem - -> [!IMPORTANT]\ -> This project is part of the Pallets Community Ecosystem. Pallets is the open -> source organization that maintains Flask; Pallets-Eco enables community -> maintenance of related projects. If you are interested in helping maintain -> this project, please reach out on [the Pallets Discord server][discord]. -> -> [discord]: https://discord.gg/pallets - - -## Example - -Signal receivers can subscribe to specific senders or receive signals -sent by any sender. - -```pycon ->>> from blinker import signal ->>> started = signal('round-started') ->>> def each(round): -... print(f"Round {round}") -... ->>> started.connect(each) - ->>> def round_two(round): -... print("This is round two.") -... ->>> started.connect(round_two, sender=2) - ->>> for round in range(1, 4): -... started.send(round) -... -Round 1! -Round 2! -This is round two. -Round 3! -``` - diff --git a/plotter-app/venv/lib/python3.8/site-packages/blinker-1.8.2.dist-info/RECORD b/plotter-app/venv/lib/python3.8/site-packages/blinker-1.8.2.dist-info/RECORD deleted file mode 100644 index f020521..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/blinker-1.8.2.dist-info/RECORD +++ /dev/null @@ -1,12 +0,0 @@ -blinker-1.8.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -blinker-1.8.2.dist-info/LICENSE.txt,sha256=nrc6HzhZekqhcCXSrhvjg5Ykx5XphdTw6Xac4p-spGc,1054 -blinker-1.8.2.dist-info/METADATA,sha256=3tEx40hm9IEofyFqDPJsDPE9MAIEhtifapoSp7FqzuA,1633 -blinker-1.8.2.dist-info/RECORD,, -blinker-1.8.2.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 -blinker/__init__.py,sha256=ymyJY_PoTgBzaPgdr4dq-RRsGh7D-sYQIGMNp8Rx4qc,1577 -blinker/__pycache__/__init__.cpython-38.pyc,, -blinker/__pycache__/_utilities.cpython-38.pyc,, -blinker/__pycache__/base.cpython-38.pyc,, -blinker/_utilities.py,sha256=0J7eeXXTUx0Ivf8asfpx0ycVkp0Eqfqnj117x2mYX9E,1675 -blinker/base.py,sha256=nIZJEtXQ8LLZZJrwVp2wQcdfCzDixvAHR9VpSWiyVcQ,22574 -blinker/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/plotter-app/venv/lib/python3.8/site-packages/blinker-1.8.2.dist-info/WHEEL b/plotter-app/venv/lib/python3.8/site-packages/blinker-1.8.2.dist-info/WHEEL deleted file mode 100644 index 3b5e64b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/blinker-1.8.2.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: flit 3.9.0 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/plotter-app/venv/lib/python3.8/site-packages/blinker/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/blinker/__init__.py deleted file mode 100644 index c93527e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/blinker/__init__.py +++ /dev/null @@ -1,60 +0,0 @@ -from __future__ import annotations - -import typing as t - -from .base import ANY -from .base import default_namespace -from .base import NamedSignal -from .base import Namespace -from .base import Signal -from .base import signal - -__all__ = [ - "ANY", - "default_namespace", - "NamedSignal", - "Namespace", - "Signal", - "signal", -] - - -def __getattr__(name: str) -> t.Any: - import warnings - - if name == "__version__": - import importlib.metadata - - warnings.warn( - "The '__version__' attribute is deprecated and will be removed in" - " Blinker 1.9.0. Use feature detection or" - " 'importlib.metadata.version(\"blinker\")' instead.", - DeprecationWarning, - stacklevel=2, - ) - return importlib.metadata.version("blinker") - - if name == "receiver_connected": - from .base import _receiver_connected - - warnings.warn( - "The global 'receiver_connected' signal is deprecated and will be" - " removed in Blinker 1.9. Use 'Signal.receiver_connected' and" - " 'Signal.receiver_disconnected' instead.", - DeprecationWarning, - stacklevel=2, - ) - return _receiver_connected - - if name == "WeakNamespace": - from .base import _WeakNamespace - - warnings.warn( - "'WeakNamespace' is deprecated and will be removed in Blinker 1.9." - " Use 'Namespace' instead.", - DeprecationWarning, - stacklevel=2, - ) - return _WeakNamespace - - raise AttributeError(name) diff --git a/plotter-app/venv/lib/python3.8/site-packages/blinker/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/blinker/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 2dbe9e6..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/blinker/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/blinker/__pycache__/_utilities.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/blinker/__pycache__/_utilities.cpython-38.pyc deleted file mode 100644 index f4b0343..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/blinker/__pycache__/_utilities.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/blinker/__pycache__/base.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/blinker/__pycache__/base.cpython-38.pyc deleted file mode 100644 index fffb5b1..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/blinker/__pycache__/base.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/blinker/_utilities.py b/plotter-app/venv/lib/python3.8/site-packages/blinker/_utilities.py deleted file mode 100644 index 000c902..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/blinker/_utilities.py +++ /dev/null @@ -1,64 +0,0 @@ -from __future__ import annotations - -import collections.abc as c -import inspect -import typing as t -from weakref import ref -from weakref import WeakMethod - -T = t.TypeVar("T") - - -class Symbol: - """A constant symbol, nicer than ``object()``. Repeated calls return the - same instance. - - >>> Symbol('foo') is Symbol('foo') - True - >>> Symbol('foo') - foo - """ - - symbols: t.ClassVar[dict[str, Symbol]] = {} - - def __new__(cls, name: str) -> Symbol: - if name in cls.symbols: - return cls.symbols[name] - - obj = super().__new__(cls) - cls.symbols[name] = obj - return obj - - def __init__(self, name: str) -> None: - self.name = name - - def __repr__(self) -> str: - return self.name - - def __getnewargs__(self) -> tuple[t.Any, ...]: - return (self.name,) - - -def make_id(obj: object) -> c.Hashable: - """Get a stable identifier for a receiver or sender, to be used as a dict - key or in a set. - """ - if inspect.ismethod(obj): - # The id of a bound method is not stable, but the id of the unbound - # function and instance are. - return id(obj.__func__), id(obj.__self__) - - if isinstance(obj, (str, int)): - # Instances with the same value always compare equal and have the same - # hash, even if the id may change. - return obj - - # Assume other types are not hashable but will always be the same instance. - return id(obj) - - -def make_ref(obj: T, callback: c.Callable[[ref[T]], None] | None = None) -> ref[T]: - if inspect.ismethod(obj): - return WeakMethod(obj, callback) # type: ignore[arg-type, return-value] - - return ref(obj, callback) diff --git a/plotter-app/venv/lib/python3.8/site-packages/blinker/base.py b/plotter-app/venv/lib/python3.8/site-packages/blinker/base.py deleted file mode 100644 index ec494b1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/blinker/base.py +++ /dev/null @@ -1,621 +0,0 @@ -from __future__ import annotations - -import collections.abc as c -import typing as t -import warnings -import weakref -from collections import defaultdict -from contextlib import AbstractContextManager -from contextlib import contextmanager -from functools import cached_property -from inspect import iscoroutinefunction -from weakref import WeakValueDictionary - -from ._utilities import make_id -from ._utilities import make_ref -from ._utilities import Symbol - -if t.TYPE_CHECKING: - F = t.TypeVar("F", bound=c.Callable[..., t.Any]) - -ANY = Symbol("ANY") -"""Symbol for "any sender".""" - -ANY_ID = 0 - - -class Signal: - """A notification emitter. - - :param doc: The docstring for the signal. - """ - - ANY = ANY - """An alias for the :data:`~blinker.ANY` sender symbol.""" - - set_class: type[set[t.Any]] = set - """The set class to use for tracking connected receivers and senders. - Python's ``set`` is unordered. If receivers must be dispatched in the order - they were connected, an ordered set implementation can be used. - - .. versionadded:: 1.7 - """ - - @cached_property - def receiver_connected(self) -> Signal: - """Emitted at the end of each :meth:`connect` call. - - The signal sender is the signal instance, and the :meth:`connect` - arguments are passed through: ``receiver``, ``sender``, and ``weak``. - - .. versionadded:: 1.2 - """ - return Signal(doc="Emitted after a receiver connects.") - - @cached_property - def receiver_disconnected(self) -> Signal: - """Emitted at the end of each :meth:`disconnect` call. - - The sender is the signal instance, and the :meth:`disconnect` arguments - are passed through: ``receiver`` and ``sender``. - - This signal is emitted **only** when :meth:`disconnect` is called - explicitly. This signal cannot be emitted by an automatic disconnect - when a weakly referenced receiver or sender goes out of scope, as the - instance is no longer be available to be used as the sender for this - signal. - - An alternative approach is available by subscribing to - :attr:`receiver_connected` and setting up a custom weakref cleanup - callback on weak receivers and senders. - - .. versionadded:: 1.2 - """ - return Signal(doc="Emitted after a receiver disconnects.") - - def __init__(self, doc: str | None = None) -> None: - if doc: - self.__doc__ = doc - - self.receivers: dict[ - t.Any, weakref.ref[c.Callable[..., t.Any]] | c.Callable[..., t.Any] - ] = {} - """The map of connected receivers. Useful to quickly check if any - receivers are connected to the signal: ``if s.receivers:``. The - structure and data is not part of the public API, but checking its - boolean value is. - """ - - self.is_muted: bool = False - self._by_receiver: dict[t.Any, set[t.Any]] = defaultdict(self.set_class) - self._by_sender: dict[t.Any, set[t.Any]] = defaultdict(self.set_class) - self._weak_senders: dict[t.Any, weakref.ref[t.Any]] = {} - - def connect(self, receiver: F, sender: t.Any = ANY, weak: bool = True) -> F: - """Connect ``receiver`` to be called when the signal is sent by - ``sender``. - - :param receiver: The callable to call when :meth:`send` is called with - the given ``sender``, passing ``sender`` as a positional argument - along with any extra keyword arguments. - :param sender: Any object or :data:`ANY`. ``receiver`` will only be - called when :meth:`send` is called with this sender. If ``ANY``, the - receiver will be called for any sender. A receiver may be connected - to multiple senders by calling :meth:`connect` multiple times. - :param weak: Track the receiver with a :mod:`weakref`. The receiver will - be automatically disconnected when it is garbage collected. When - connecting a receiver defined within a function, set to ``False``, - otherwise it will be disconnected when the function scope ends. - """ - receiver_id = make_id(receiver) - sender_id = ANY_ID if sender is ANY else make_id(sender) - - if weak: - self.receivers[receiver_id] = make_ref( - receiver, self._make_cleanup_receiver(receiver_id) - ) - else: - self.receivers[receiver_id] = receiver - - self._by_sender[sender_id].add(receiver_id) - self._by_receiver[receiver_id].add(sender_id) - - if sender is not ANY and sender_id not in self._weak_senders: - # store a cleanup for weakref-able senders - try: - self._weak_senders[sender_id] = make_ref( - sender, self._make_cleanup_sender(sender_id) - ) - except TypeError: - pass - - if "receiver_connected" in self.__dict__ and self.receiver_connected.receivers: - try: - self.receiver_connected.send( - self, receiver=receiver, sender=sender, weak=weak - ) - except TypeError: - # TODO no explanation or test for this - self.disconnect(receiver, sender) - raise - - if _receiver_connected.receivers and self is not _receiver_connected: - try: - _receiver_connected.send( - self, receiver_arg=receiver, sender_arg=sender, weak_arg=weak - ) - except TypeError: - self.disconnect(receiver, sender) - raise - - return receiver - - def connect_via(self, sender: t.Any, weak: bool = False) -> c.Callable[[F], F]: - """Connect the decorated function to be called when the signal is sent - by ``sender``. - - The decorated function will be called when :meth:`send` is called with - the given ``sender``, passing ``sender`` as a positional argument along - with any extra keyword arguments. - - :param sender: Any object or :data:`ANY`. ``receiver`` will only be - called when :meth:`send` is called with this sender. If ``ANY``, the - receiver will be called for any sender. A receiver may be connected - to multiple senders by calling :meth:`connect` multiple times. - :param weak: Track the receiver with a :mod:`weakref`. The receiver will - be automatically disconnected when it is garbage collected. When - connecting a receiver defined within a function, set to ``False``, - otherwise it will be disconnected when the function scope ends.= - - .. versionadded:: 1.1 - """ - - def decorator(fn: F) -> F: - self.connect(fn, sender, weak) - return fn - - return decorator - - @contextmanager - def connected_to( - self, receiver: c.Callable[..., t.Any], sender: t.Any = ANY - ) -> c.Generator[None, None, None]: - """A context manager that temporarily connects ``receiver`` to the - signal while a ``with`` block executes. When the block exits, the - receiver is disconnected. Useful for tests. - - :param receiver: The callable to call when :meth:`send` is called with - the given ``sender``, passing ``sender`` as a positional argument - along with any extra keyword arguments. - :param sender: Any object or :data:`ANY`. ``receiver`` will only be - called when :meth:`send` is called with this sender. If ``ANY``, the - receiver will be called for any sender. - - .. versionadded:: 1.1 - """ - self.connect(receiver, sender=sender, weak=False) - - try: - yield None - finally: - self.disconnect(receiver) - - @contextmanager - def muted(self) -> c.Generator[None, None, None]: - """A context manager that temporarily disables the signal. No receivers - will be called if the signal is sent, until the ``with`` block exits. - Useful for tests. - """ - self.is_muted = True - - try: - yield None - finally: - self.is_muted = False - - def temporarily_connected_to( - self, receiver: c.Callable[..., t.Any], sender: t.Any = ANY - ) -> AbstractContextManager[None]: - """Deprecated alias for :meth:`connected_to`. - - .. deprecated:: 1.1 - Renamed to ``connected_to``. Will be removed in Blinker 1.9. - - .. versionadded:: 0.9 - """ - warnings.warn( - "'temporarily_connected_to' is renamed to 'connected_to'. The old name is" - " deprecated and will be removed in Blinker 1.9.", - DeprecationWarning, - stacklevel=2, - ) - return self.connected_to(receiver, sender) - - def send( - self, - sender: t.Any | None = None, - /, - *, - _async_wrapper: c.Callable[ - [c.Callable[..., c.Coroutine[t.Any, t.Any, t.Any]]], c.Callable[..., t.Any] - ] - | None = None, - **kwargs: t.Any, - ) -> list[tuple[c.Callable[..., t.Any], t.Any]]: - """Call all receivers that are connected to the given ``sender`` - or :data:`ANY`. Each receiver is called with ``sender`` as a positional - argument along with any extra keyword arguments. Return a list of - ``(receiver, return value)`` tuples. - - The order receivers are called is undefined, but can be influenced by - setting :attr:`set_class`. - - If a receiver raises an exception, that exception will propagate up. - This makes debugging straightforward, with an assumption that correctly - implemented receivers will not raise. - - :param sender: Call receivers connected to this sender, in addition to - those connected to :data:`ANY`. - :param _async_wrapper: Will be called on any receivers that are async - coroutines to turn them into sync callables. For example, could run - the receiver with an event loop. - :param kwargs: Extra keyword arguments to pass to each receiver. - - .. versionchanged:: 1.7 - Added the ``_async_wrapper`` argument. - """ - if self.is_muted: - return [] - - results = [] - - for receiver in self.receivers_for(sender): - if iscoroutinefunction(receiver): - if _async_wrapper is None: - raise RuntimeError("Cannot send to a coroutine function.") - - result = _async_wrapper(receiver)(sender, **kwargs) - else: - result = receiver(sender, **kwargs) - - results.append((receiver, result)) - - return results - - async def send_async( - self, - sender: t.Any | None = None, - /, - *, - _sync_wrapper: c.Callable[ - [c.Callable[..., t.Any]], c.Callable[..., c.Coroutine[t.Any, t.Any, t.Any]] - ] - | None = None, - **kwargs: t.Any, - ) -> list[tuple[c.Callable[..., t.Any], t.Any]]: - """Await all receivers that are connected to the given ``sender`` - or :data:`ANY`. Each receiver is called with ``sender`` as a positional - argument along with any extra keyword arguments. Return a list of - ``(receiver, return value)`` tuples. - - The order receivers are called is undefined, but can be influenced by - setting :attr:`set_class`. - - If a receiver raises an exception, that exception will propagate up. - This makes debugging straightforward, with an assumption that correctly - implemented receivers will not raise. - - :param sender: Call receivers connected to this sender, in addition to - those connected to :data:`ANY`. - :param _sync_wrapper: Will be called on any receivers that are sync - callables to turn them into async coroutines. For example, - could call the receiver in a thread. - :param kwargs: Extra keyword arguments to pass to each receiver. - - .. versionadded:: 1.7 - """ - if self.is_muted: - return [] - - results = [] - - for receiver in self.receivers_for(sender): - if not iscoroutinefunction(receiver): - if _sync_wrapper is None: - raise RuntimeError("Cannot send to a non-coroutine function.") - - result = await _sync_wrapper(receiver)(sender, **kwargs) - else: - result = await receiver(sender, **kwargs) - - results.append((receiver, result)) - - return results - - def has_receivers_for(self, sender: t.Any) -> bool: - """Check if there is at least one receiver that will be called with the - given ``sender``. A receiver connected to :data:`ANY` will always be - called, regardless of sender. Does not check if weakly referenced - receivers are still live. See :meth:`receivers_for` for a stronger - search. - - :param sender: Check for receivers connected to this sender, in addition - to those connected to :data:`ANY`. - """ - if not self.receivers: - return False - - if self._by_sender[ANY_ID]: - return True - - if sender is ANY: - return False - - return make_id(sender) in self._by_sender - - def receivers_for( - self, sender: t.Any - ) -> c.Generator[c.Callable[..., t.Any], None, None]: - """Yield each receiver to be called for ``sender``, in addition to those - to be called for :data:`ANY`. Weakly referenced receivers that are not - live will be disconnected and skipped. - - :param sender: Yield receivers connected to this sender, in addition - to those connected to :data:`ANY`. - """ - # TODO: test receivers_for(ANY) - if not self.receivers: - return - - sender_id = make_id(sender) - - if sender_id in self._by_sender: - ids = self._by_sender[ANY_ID] | self._by_sender[sender_id] - else: - ids = self._by_sender[ANY_ID].copy() - - for receiver_id in ids: - receiver = self.receivers.get(receiver_id) - - if receiver is None: - continue - - if isinstance(receiver, weakref.ref): - strong = receiver() - - if strong is None: - self._disconnect(receiver_id, ANY_ID) - continue - - yield strong - else: - yield receiver - - def disconnect(self, receiver: c.Callable[..., t.Any], sender: t.Any = ANY) -> None: - """Disconnect ``receiver`` from being called when the signal is sent by - ``sender``. - - :param receiver: A connected receiver callable. - :param sender: Disconnect from only this sender. By default, disconnect - from all senders. - """ - sender_id: c.Hashable - - if sender is ANY: - sender_id = ANY_ID - else: - sender_id = make_id(sender) - - receiver_id = make_id(receiver) - self._disconnect(receiver_id, sender_id) - - if ( - "receiver_disconnected" in self.__dict__ - and self.receiver_disconnected.receivers - ): - self.receiver_disconnected.send(self, receiver=receiver, sender=sender) - - def _disconnect(self, receiver_id: c.Hashable, sender_id: c.Hashable) -> None: - if sender_id == ANY_ID: - if self._by_receiver.pop(receiver_id, None) is not None: - for bucket in self._by_sender.values(): - bucket.discard(receiver_id) - - self.receivers.pop(receiver_id, None) - else: - self._by_sender[sender_id].discard(receiver_id) - self._by_receiver[receiver_id].discard(sender_id) - - def _make_cleanup_receiver( - self, receiver_id: c.Hashable - ) -> c.Callable[[weakref.ref[c.Callable[..., t.Any]]], None]: - """Create a callback function to disconnect a weakly referenced - receiver when it is garbage collected. - """ - - def cleanup(ref: weakref.ref[c.Callable[..., t.Any]]) -> None: - self._disconnect(receiver_id, ANY_ID) - - return cleanup - - def _make_cleanup_sender( - self, sender_id: c.Hashable - ) -> c.Callable[[weakref.ref[t.Any]], None]: - """Create a callback function to disconnect all receivers for a weakly - referenced sender when it is garbage collected. - """ - assert sender_id != ANY_ID - - def cleanup(ref: weakref.ref[t.Any]) -> None: - self._weak_senders.pop(sender_id, None) - - for receiver_id in self._by_sender.pop(sender_id, ()): - self._by_receiver[receiver_id].discard(sender_id) - - return cleanup - - def _cleanup_bookkeeping(self) -> None: - """Prune unused sender/receiver bookkeeping. Not threadsafe. - - Connecting & disconnecting leaves behind a small amount of bookkeeping - data. Typical workloads using Blinker, for example in most web apps, - Flask, CLI scripts, etc., are not adversely affected by this - bookkeeping. - - With a long-running process performing dynamic signal routing with high - volume, e.g. connecting to function closures, senders are all unique - object instances. Doing all of this over and over may cause memory usage - to grow due to extraneous bookkeeping. (An empty ``set`` for each stale - sender/receiver pair.) - - This method will prune that bookkeeping away, with the caveat that such - pruning is not threadsafe. The risk is that cleanup of a fully - disconnected receiver/sender pair occurs while another thread is - connecting that same pair. If you are in the highly dynamic, unique - receiver/sender situation that has lead you to this method, that failure - mode is perhaps not a big deal for you. - """ - for mapping in (self._by_sender, self._by_receiver): - for ident, bucket in list(mapping.items()): - if not bucket: - mapping.pop(ident, None) - - def _clear_state(self) -> None: - """Disconnect all receivers and senders. Useful for tests.""" - self._weak_senders.clear() - self.receivers.clear() - self._by_sender.clear() - self._by_receiver.clear() - - -_receiver_connected = Signal( - """\ -Sent by a :class:`Signal` after a receiver connects. - -:argument: the Signal that was connected to -:keyword receiver_arg: the connected receiver -:keyword sender_arg: the sender to connect to -:keyword weak_arg: true if the connection to receiver_arg is a weak reference - -.. deprecated:: 1.2 - Individual signals have their own :attr:`~Signal.receiver_connected` and - :attr:`~Signal.receiver_disconnected` signals with a slightly simplified - call signature. This global signal will be removed in Blinker 1.9. -""" -) - - -class NamedSignal(Signal): - """A named generic notification emitter. The name is not used by the signal - itself, but matches the key in the :class:`Namespace` that it belongs to. - - :param name: The name of the signal within the namespace. - :param doc: The docstring for the signal. - """ - - def __init__(self, name: str, doc: str | None = None) -> None: - super().__init__(doc) - - #: The name of this signal. - self.name: str = name - - def __repr__(self) -> str: - base = super().__repr__() - return f"{base[:-1]}; {self.name!r}>" # noqa: E702 - - -if t.TYPE_CHECKING: - - class PNamespaceSignal(t.Protocol): - def __call__(self, name: str, doc: str | None = None) -> NamedSignal: ... - - # Python < 3.9 - _NamespaceBase = dict[str, NamedSignal] # type: ignore[misc] -else: - _NamespaceBase = dict - - -class Namespace(_NamespaceBase): - """A dict mapping names to signals.""" - - def signal(self, name: str, doc: str | None = None) -> NamedSignal: - """Return the :class:`NamedSignal` for the given ``name``, creating it - if required. Repeated calls with the same name return the same signal. - - :param name: The name of the signal. - :param doc: The docstring of the signal. - """ - if name not in self: - self[name] = NamedSignal(name, doc) - - return self[name] - - -class _WeakNamespace(WeakValueDictionary): # type: ignore[type-arg] - """A weak mapping of names to signals. - - Automatically cleans up unused signals when the last reference goes out - of scope. This namespace implementation provides similar behavior to Blinker - <= 1.2. - - .. deprecated:: 1.3 - Will be removed in Blinker 1.9. - - .. versionadded:: 1.3 - """ - - def __init__(self) -> None: - warnings.warn( - "'WeakNamespace' is deprecated and will be removed in Blinker 1.9." - " Use 'Namespace' instead.", - DeprecationWarning, - stacklevel=2, - ) - super().__init__() - - def signal(self, name: str, doc: str | None = None) -> NamedSignal: - """Return the :class:`NamedSignal` for the given ``name``, creating it - if required. Repeated calls with the same name return the same signal. - - :param name: The name of the signal. - :param doc: The docstring of the signal. - """ - if name not in self: - self[name] = NamedSignal(name, doc) - - return self[name] # type: ignore[no-any-return] - - -default_namespace: Namespace = Namespace() -"""A default :class:`Namespace` for creating named signals. :func:`signal` -creates a :class:`NamedSignal` in this namespace. -""" - -signal: PNamespaceSignal = default_namespace.signal -"""Return a :class:`NamedSignal` in :data:`default_namespace` with the given -``name``, creating it if required. Repeated calls with the same name return the -same signal. -""" - - -def __getattr__(name: str) -> t.Any: - if name == "receiver_connected": - warnings.warn( - "The global 'receiver_connected' signal is deprecated and will be" - " removed in Blinker 1.9. Use 'Signal.receiver_connected' and" - " 'Signal.receiver_disconnected' instead.", - DeprecationWarning, - stacklevel=2, - ) - return _receiver_connected - - if name == "WeakNamespace": - warnings.warn( - "'WeakNamespace' is deprecated and will be removed in Blinker 1.9." - " Use 'Namespace' instead.", - DeprecationWarning, - stacklevel=2, - ) - return _WeakNamespace - - raise AttributeError(name) diff --git a/plotter-app/venv/lib/python3.8/site-packages/blinker/py.typed b/plotter-app/venv/lib/python3.8/site-packages/blinker/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/click-8.1.7.dist-info/INSTALLER b/plotter-app/venv/lib/python3.8/site-packages/click-8.1.7.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click-8.1.7.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/plotter-app/venv/lib/python3.8/site-packages/click-8.1.7.dist-info/LICENSE.rst b/plotter-app/venv/lib/python3.8/site-packages/click-8.1.7.dist-info/LICENSE.rst deleted file mode 100644 index d12a849..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click-8.1.7.dist-info/LICENSE.rst +++ /dev/null @@ -1,28 +0,0 @@ -Copyright 2014 Pallets - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/plotter-app/venv/lib/python3.8/site-packages/click-8.1.7.dist-info/METADATA b/plotter-app/venv/lib/python3.8/site-packages/click-8.1.7.dist-info/METADATA deleted file mode 100644 index 7a6bbb2..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click-8.1.7.dist-info/METADATA +++ /dev/null @@ -1,103 +0,0 @@ -Metadata-Version: 2.1 -Name: click -Version: 8.1.7 -Summary: Composable command line interface toolkit -Home-page: https://palletsprojects.com/p/click/ -Maintainer: Pallets -Maintainer-email: contact@palletsprojects.com -License: BSD-3-Clause -Project-URL: Donate, https://palletsprojects.com/donate -Project-URL: Documentation, https://click.palletsprojects.com/ -Project-URL: Changes, https://click.palletsprojects.com/changes/ -Project-URL: Source Code, https://github.com/pallets/click/ -Project-URL: Issue Tracker, https://github.com/pallets/click/issues/ -Project-URL: Chat, https://discord.gg/pallets -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Requires-Python: >=3.7 -Description-Content-Type: text/x-rst -License-File: LICENSE.rst -Requires-Dist: colorama ; platform_system == "Windows" -Requires-Dist: importlib-metadata ; python_version < "3.8" - -\$ click\_ -========== - -Click is a Python package for creating beautiful command line interfaces -in a composable way with as little code as necessary. It's the "Command -Line Interface Creation Kit". It's highly configurable but comes with -sensible defaults out of the box. - -It aims to make the process of writing command line tools quick and fun -while also preventing any frustration caused by the inability to -implement an intended CLI API. - -Click in three points: - -- Arbitrary nesting of commands -- Automatic help page generation -- Supports lazy loading of subcommands at runtime - - -Installing ----------- - -Install and update using `pip`_: - -.. code-block:: text - - $ pip install -U click - -.. _pip: https://pip.pypa.io/en/stable/getting-started/ - - -A Simple Example ----------------- - -.. code-block:: python - - import click - - @click.command() - @click.option("--count", default=1, help="Number of greetings.") - @click.option("--name", prompt="Your name", help="The person to greet.") - def hello(count, name): - """Simple program that greets NAME for a total of COUNT times.""" - for _ in range(count): - click.echo(f"Hello, {name}!") - - if __name__ == '__main__': - hello() - -.. code-block:: text - - $ python hello.py --count=3 - Your name: Click - Hello, Click! - Hello, Click! - Hello, Click! - - -Donate ------- - -The Pallets organization develops and supports Click and other popular -packages. In order to grow the community of contributors and users, and -allow the maintainers to devote more time to the projects, `please -donate today`_. - -.. _please donate today: https://palletsprojects.com/donate - - -Links ------ - -- Documentation: https://click.palletsprojects.com/ -- Changes: https://click.palletsprojects.com/changes/ -- PyPI Releases: https://pypi.org/project/click/ -- Source Code: https://github.com/pallets/click -- Issue Tracker: https://github.com/pallets/click/issues -- Chat: https://discord.gg/pallets diff --git a/plotter-app/venv/lib/python3.8/site-packages/click-8.1.7.dist-info/RECORD b/plotter-app/venv/lib/python3.8/site-packages/click-8.1.7.dist-info/RECORD deleted file mode 100644 index 8daf861..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click-8.1.7.dist-info/RECORD +++ /dev/null @@ -1,39 +0,0 @@ -click-8.1.7.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -click-8.1.7.dist-info/LICENSE.rst,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 -click-8.1.7.dist-info/METADATA,sha256=qIMevCxGA9yEmJOM_4WHuUJCwWpsIEVbCPOhs45YPN4,3014 -click-8.1.7.dist-info/RECORD,, -click-8.1.7.dist-info/WHEEL,sha256=5sUXSg9e4bi7lTLOHcm6QEYwO5TIF1TNbTSVFVjcJcc,92 -click-8.1.7.dist-info/top_level.txt,sha256=J1ZQogalYS4pphY_lPECoNMfw0HzTSrZglC4Yfwo4xA,6 -click/__init__.py,sha256=YDDbjm406dTOA0V8bTtdGnhN7zj5j-_dFRewZF_pLvw,3138 -click/__pycache__/__init__.cpython-38.pyc,, -click/__pycache__/_compat.cpython-38.pyc,, -click/__pycache__/_termui_impl.cpython-38.pyc,, -click/__pycache__/_textwrap.cpython-38.pyc,, -click/__pycache__/_winconsole.cpython-38.pyc,, -click/__pycache__/core.cpython-38.pyc,, -click/__pycache__/decorators.cpython-38.pyc,, -click/__pycache__/exceptions.cpython-38.pyc,, -click/__pycache__/formatting.cpython-38.pyc,, -click/__pycache__/globals.cpython-38.pyc,, -click/__pycache__/parser.cpython-38.pyc,, -click/__pycache__/shell_completion.cpython-38.pyc,, -click/__pycache__/termui.cpython-38.pyc,, -click/__pycache__/testing.cpython-38.pyc,, -click/__pycache__/types.cpython-38.pyc,, -click/__pycache__/utils.cpython-38.pyc,, -click/_compat.py,sha256=5318agQpbt4kroKsbqDOYpTSWzL_YCZVUQiTT04yXmc,18744 -click/_termui_impl.py,sha256=3dFYv4445Nw-rFvZOTBMBPYwB1bxnmNk9Du6Dm_oBSU,24069 -click/_textwrap.py,sha256=10fQ64OcBUMuK7mFvh8363_uoOxPlRItZBmKzRJDgoY,1353 -click/_winconsole.py,sha256=5ju3jQkcZD0W27WEMGqmEP4y_crUVzPCqsX_FYb7BO0,7860 -click/core.py,sha256=j6oEWtGgGna8JarD6WxhXmNnxLnfRjwXglbBc-8jr7U,114086 -click/decorators.py,sha256=-ZlbGYgV-oI8jr_oH4RpuL1PFS-5QmeuEAsLDAYgxtw,18719 -click/exceptions.py,sha256=fyROO-47HWFDjt2qupo7A3J32VlpM-ovJnfowu92K3s,9273 -click/formatting.py,sha256=Frf0-5W33-loyY_i9qrwXR8-STnW3m5gvyxLVUdyxyk,9706 -click/globals.py,sha256=TP-qM88STzc7f127h35TD_v920FgfOD2EwzqA0oE8XU,1961 -click/parser.py,sha256=LKyYQE9ZLj5KgIDXkrcTHQRXIggfoivX14_UVIn56YA,19067 -click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -click/shell_completion.py,sha256=Ty3VM_ts0sQhj6u7eFTiLwHPoTgcXTGEAUg2OpLqYKw,18460 -click/termui.py,sha256=H7Q8FpmPelhJ2ovOhfCRhjMtCpNyjFXryAMLZODqsdc,28324 -click/testing.py,sha256=1Qd4kS5bucn1hsNIRryd0WtTMuCpkA93grkWxT8POsU,16084 -click/types.py,sha256=TZvz3hKvBztf-Hpa2enOmP4eznSPLzijjig5b_0XMxE,36391 -click/utils.py,sha256=1476UduUNY6UePGU4m18uzVHLt1sKM2PP3yWsQhbItM,20298 diff --git a/plotter-app/venv/lib/python3.8/site-packages/click-8.1.7.dist-info/WHEEL b/plotter-app/venv/lib/python3.8/site-packages/click-8.1.7.dist-info/WHEEL deleted file mode 100644 index 2c08da0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click-8.1.7.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.41.1) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/plotter-app/venv/lib/python3.8/site-packages/click-8.1.7.dist-info/top_level.txt b/plotter-app/venv/lib/python3.8/site-packages/click-8.1.7.dist-info/top_level.txt deleted file mode 100644 index dca9a90..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click-8.1.7.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -click diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/click/__init__.py deleted file mode 100644 index 9a1dab0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click/__init__.py +++ /dev/null @@ -1,73 +0,0 @@ -""" -Click is a simple Python module inspired by the stdlib optparse to make -writing command line scripts fun. Unlike other modules, it's based -around a simple API that does not come with too much magic and is -composable. -""" -from .core import Argument as Argument -from .core import BaseCommand as BaseCommand -from .core import Command as Command -from .core import CommandCollection as CommandCollection -from .core import Context as Context -from .core import Group as Group -from .core import MultiCommand as MultiCommand -from .core import Option as Option -from .core import Parameter as Parameter -from .decorators import argument as argument -from .decorators import command as command -from .decorators import confirmation_option as confirmation_option -from .decorators import group as group -from .decorators import help_option as help_option -from .decorators import make_pass_decorator as make_pass_decorator -from .decorators import option as option -from .decorators import pass_context as pass_context -from .decorators import pass_obj as pass_obj -from .decorators import password_option as password_option -from .decorators import version_option as version_option -from .exceptions import Abort as Abort -from .exceptions import BadArgumentUsage as BadArgumentUsage -from .exceptions import BadOptionUsage as BadOptionUsage -from .exceptions import BadParameter as BadParameter -from .exceptions import ClickException as ClickException -from .exceptions import FileError as FileError -from .exceptions import MissingParameter as MissingParameter -from .exceptions import NoSuchOption as NoSuchOption -from .exceptions import UsageError as UsageError -from .formatting import HelpFormatter as HelpFormatter -from .formatting import wrap_text as wrap_text -from .globals import get_current_context as get_current_context -from .parser import OptionParser as OptionParser -from .termui import clear as clear -from .termui import confirm as confirm -from .termui import echo_via_pager as echo_via_pager -from .termui import edit as edit -from .termui import getchar as getchar -from .termui import launch as launch -from .termui import pause as pause -from .termui import progressbar as progressbar -from .termui import prompt as prompt -from .termui import secho as secho -from .termui import style as style -from .termui import unstyle as unstyle -from .types import BOOL as BOOL -from .types import Choice as Choice -from .types import DateTime as DateTime -from .types import File as File -from .types import FLOAT as FLOAT -from .types import FloatRange as FloatRange -from .types import INT as INT -from .types import IntRange as IntRange -from .types import ParamType as ParamType -from .types import Path as Path -from .types import STRING as STRING -from .types import Tuple as Tuple -from .types import UNPROCESSED as UNPROCESSED -from .types import UUID as UUID -from .utils import echo as echo -from .utils import format_filename as format_filename -from .utils import get_app_dir as get_app_dir -from .utils import get_binary_stream as get_binary_stream -from .utils import get_text_stream as get_text_stream -from .utils import open_file as open_file - -__version__ = "8.1.7" diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index d1f52ce..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/_compat.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/_compat.cpython-38.pyc deleted file mode 100644 index 9ec7ddb..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/_compat.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/_termui_impl.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/_termui_impl.cpython-38.pyc deleted file mode 100644 index dc88d63..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/_termui_impl.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/_textwrap.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/_textwrap.cpython-38.pyc deleted file mode 100644 index 608ca33..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/_textwrap.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/_winconsole.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/_winconsole.cpython-38.pyc deleted file mode 100644 index 4d6e0f2..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/_winconsole.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/core.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/core.cpython-38.pyc deleted file mode 100644 index 28fffe3..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/core.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/decorators.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/decorators.cpython-38.pyc deleted file mode 100644 index c074797..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/decorators.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/exceptions.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/exceptions.cpython-38.pyc deleted file mode 100644 index d1d7ebd..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/exceptions.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/formatting.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/formatting.cpython-38.pyc deleted file mode 100644 index 570ea95..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/formatting.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/globals.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/globals.cpython-38.pyc deleted file mode 100644 index 99d2ff2..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/globals.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/parser.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/parser.cpython-38.pyc deleted file mode 100644 index a1b0678..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/parser.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/shell_completion.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/shell_completion.cpython-38.pyc deleted file mode 100644 index 356888d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/shell_completion.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/termui.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/termui.cpython-38.pyc deleted file mode 100644 index e6a7a3a..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/termui.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/testing.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/testing.cpython-38.pyc deleted file mode 100644 index 35c521e..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/testing.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/types.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/types.cpython-38.pyc deleted file mode 100644 index 0b2ff26..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/types.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/utils.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/utils.cpython-38.pyc deleted file mode 100644 index 6c78d5e..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/click/__pycache__/utils.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/_compat.py b/plotter-app/venv/lib/python3.8/site-packages/click/_compat.py deleted file mode 100644 index 23f8866..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click/_compat.py +++ /dev/null @@ -1,623 +0,0 @@ -import codecs -import io -import os -import re -import sys -import typing as t -from weakref import WeakKeyDictionary - -CYGWIN = sys.platform.startswith("cygwin") -WIN = sys.platform.startswith("win") -auto_wrap_for_ansi: t.Optional[t.Callable[[t.TextIO], t.TextIO]] = None -_ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]") - - -def _make_text_stream( - stream: t.BinaryIO, - encoding: t.Optional[str], - errors: t.Optional[str], - force_readable: bool = False, - force_writable: bool = False, -) -> t.TextIO: - if encoding is None: - encoding = get_best_encoding(stream) - if errors is None: - errors = "replace" - return _NonClosingTextIOWrapper( - stream, - encoding, - errors, - line_buffering=True, - force_readable=force_readable, - force_writable=force_writable, - ) - - -def is_ascii_encoding(encoding: str) -> bool: - """Checks if a given encoding is ascii.""" - try: - return codecs.lookup(encoding).name == "ascii" - except LookupError: - return False - - -def get_best_encoding(stream: t.IO[t.Any]) -> str: - """Returns the default stream encoding if not found.""" - rv = getattr(stream, "encoding", None) or sys.getdefaultencoding() - if is_ascii_encoding(rv): - return "utf-8" - return rv - - -class _NonClosingTextIOWrapper(io.TextIOWrapper): - def __init__( - self, - stream: t.BinaryIO, - encoding: t.Optional[str], - errors: t.Optional[str], - force_readable: bool = False, - force_writable: bool = False, - **extra: t.Any, - ) -> None: - self._stream = stream = t.cast( - t.BinaryIO, _FixupStream(stream, force_readable, force_writable) - ) - super().__init__(stream, encoding, errors, **extra) - - def __del__(self) -> None: - try: - self.detach() - except Exception: - pass - - def isatty(self) -> bool: - # https://bitbucket.org/pypy/pypy/issue/1803 - return self._stream.isatty() - - -class _FixupStream: - """The new io interface needs more from streams than streams - traditionally implement. As such, this fix-up code is necessary in - some circumstances. - - The forcing of readable and writable flags are there because some tools - put badly patched objects on sys (one such offender are certain version - of jupyter notebook). - """ - - def __init__( - self, - stream: t.BinaryIO, - force_readable: bool = False, - force_writable: bool = False, - ): - self._stream = stream - self._force_readable = force_readable - self._force_writable = force_writable - - def __getattr__(self, name: str) -> t.Any: - return getattr(self._stream, name) - - def read1(self, size: int) -> bytes: - f = getattr(self._stream, "read1", None) - - if f is not None: - return t.cast(bytes, f(size)) - - return self._stream.read(size) - - def readable(self) -> bool: - if self._force_readable: - return True - x = getattr(self._stream, "readable", None) - if x is not None: - return t.cast(bool, x()) - try: - self._stream.read(0) - except Exception: - return False - return True - - def writable(self) -> bool: - if self._force_writable: - return True - x = getattr(self._stream, "writable", None) - if x is not None: - return t.cast(bool, x()) - try: - self._stream.write("") # type: ignore - except Exception: - try: - self._stream.write(b"") - except Exception: - return False - return True - - def seekable(self) -> bool: - x = getattr(self._stream, "seekable", None) - if x is not None: - return t.cast(bool, x()) - try: - self._stream.seek(self._stream.tell()) - except Exception: - return False - return True - - -def _is_binary_reader(stream: t.IO[t.Any], default: bool = False) -> bool: - try: - return isinstance(stream.read(0), bytes) - except Exception: - return default - # This happens in some cases where the stream was already - # closed. In this case, we assume the default. - - -def _is_binary_writer(stream: t.IO[t.Any], default: bool = False) -> bool: - try: - stream.write(b"") - except Exception: - try: - stream.write("") - return False - except Exception: - pass - return default - return True - - -def _find_binary_reader(stream: t.IO[t.Any]) -> t.Optional[t.BinaryIO]: - # We need to figure out if the given stream is already binary. - # This can happen because the official docs recommend detaching - # the streams to get binary streams. Some code might do this, so - # we need to deal with this case explicitly. - if _is_binary_reader(stream, False): - return t.cast(t.BinaryIO, stream) - - buf = getattr(stream, "buffer", None) - - # Same situation here; this time we assume that the buffer is - # actually binary in case it's closed. - if buf is not None and _is_binary_reader(buf, True): - return t.cast(t.BinaryIO, buf) - - return None - - -def _find_binary_writer(stream: t.IO[t.Any]) -> t.Optional[t.BinaryIO]: - # We need to figure out if the given stream is already binary. - # This can happen because the official docs recommend detaching - # the streams to get binary streams. Some code might do this, so - # we need to deal with this case explicitly. - if _is_binary_writer(stream, False): - return t.cast(t.BinaryIO, stream) - - buf = getattr(stream, "buffer", None) - - # Same situation here; this time we assume that the buffer is - # actually binary in case it's closed. - if buf is not None and _is_binary_writer(buf, True): - return t.cast(t.BinaryIO, buf) - - return None - - -def _stream_is_misconfigured(stream: t.TextIO) -> bool: - """A stream is misconfigured if its encoding is ASCII.""" - # If the stream does not have an encoding set, we assume it's set - # to ASCII. This appears to happen in certain unittest - # environments. It's not quite clear what the correct behavior is - # but this at least will force Click to recover somehow. - return is_ascii_encoding(getattr(stream, "encoding", None) or "ascii") - - -def _is_compat_stream_attr(stream: t.TextIO, attr: str, value: t.Optional[str]) -> bool: - """A stream attribute is compatible if it is equal to the - desired value or the desired value is unset and the attribute - has a value. - """ - stream_value = getattr(stream, attr, None) - return stream_value == value or (value is None and stream_value is not None) - - -def _is_compatible_text_stream( - stream: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] -) -> bool: - """Check if a stream's encoding and errors attributes are - compatible with the desired values. - """ - return _is_compat_stream_attr( - stream, "encoding", encoding - ) and _is_compat_stream_attr(stream, "errors", errors) - - -def _force_correct_text_stream( - text_stream: t.IO[t.Any], - encoding: t.Optional[str], - errors: t.Optional[str], - is_binary: t.Callable[[t.IO[t.Any], bool], bool], - find_binary: t.Callable[[t.IO[t.Any]], t.Optional[t.BinaryIO]], - force_readable: bool = False, - force_writable: bool = False, -) -> t.TextIO: - if is_binary(text_stream, False): - binary_reader = t.cast(t.BinaryIO, text_stream) - else: - text_stream = t.cast(t.TextIO, text_stream) - # If the stream looks compatible, and won't default to a - # misconfigured ascii encoding, return it as-is. - if _is_compatible_text_stream(text_stream, encoding, errors) and not ( - encoding is None and _stream_is_misconfigured(text_stream) - ): - return text_stream - - # Otherwise, get the underlying binary reader. - possible_binary_reader = find_binary(text_stream) - - # If that's not possible, silently use the original reader - # and get mojibake instead of exceptions. - if possible_binary_reader is None: - return text_stream - - binary_reader = possible_binary_reader - - # Default errors to replace instead of strict in order to get - # something that works. - if errors is None: - errors = "replace" - - # Wrap the binary stream in a text stream with the correct - # encoding parameters. - return _make_text_stream( - binary_reader, - encoding, - errors, - force_readable=force_readable, - force_writable=force_writable, - ) - - -def _force_correct_text_reader( - text_reader: t.IO[t.Any], - encoding: t.Optional[str], - errors: t.Optional[str], - force_readable: bool = False, -) -> t.TextIO: - return _force_correct_text_stream( - text_reader, - encoding, - errors, - _is_binary_reader, - _find_binary_reader, - force_readable=force_readable, - ) - - -def _force_correct_text_writer( - text_writer: t.IO[t.Any], - encoding: t.Optional[str], - errors: t.Optional[str], - force_writable: bool = False, -) -> t.TextIO: - return _force_correct_text_stream( - text_writer, - encoding, - errors, - _is_binary_writer, - _find_binary_writer, - force_writable=force_writable, - ) - - -def get_binary_stdin() -> t.BinaryIO: - reader = _find_binary_reader(sys.stdin) - if reader is None: - raise RuntimeError("Was not able to determine binary stream for sys.stdin.") - return reader - - -def get_binary_stdout() -> t.BinaryIO: - writer = _find_binary_writer(sys.stdout) - if writer is None: - raise RuntimeError("Was not able to determine binary stream for sys.stdout.") - return writer - - -def get_binary_stderr() -> t.BinaryIO: - writer = _find_binary_writer(sys.stderr) - if writer is None: - raise RuntimeError("Was not able to determine binary stream for sys.stderr.") - return writer - - -def get_text_stdin( - encoding: t.Optional[str] = None, errors: t.Optional[str] = None -) -> t.TextIO: - rv = _get_windows_console_stream(sys.stdin, encoding, errors) - if rv is not None: - return rv - return _force_correct_text_reader(sys.stdin, encoding, errors, force_readable=True) - - -def get_text_stdout( - encoding: t.Optional[str] = None, errors: t.Optional[str] = None -) -> t.TextIO: - rv = _get_windows_console_stream(sys.stdout, encoding, errors) - if rv is not None: - return rv - return _force_correct_text_writer(sys.stdout, encoding, errors, force_writable=True) - - -def get_text_stderr( - encoding: t.Optional[str] = None, errors: t.Optional[str] = None -) -> t.TextIO: - rv = _get_windows_console_stream(sys.stderr, encoding, errors) - if rv is not None: - return rv - return _force_correct_text_writer(sys.stderr, encoding, errors, force_writable=True) - - -def _wrap_io_open( - file: t.Union[str, "os.PathLike[str]", int], - mode: str, - encoding: t.Optional[str], - errors: t.Optional[str], -) -> t.IO[t.Any]: - """Handles not passing ``encoding`` and ``errors`` in binary mode.""" - if "b" in mode: - return open(file, mode) - - return open(file, mode, encoding=encoding, errors=errors) - - -def open_stream( - filename: "t.Union[str, os.PathLike[str]]", - mode: str = "r", - encoding: t.Optional[str] = None, - errors: t.Optional[str] = "strict", - atomic: bool = False, -) -> t.Tuple[t.IO[t.Any], bool]: - binary = "b" in mode - filename = os.fspath(filename) - - # Standard streams first. These are simple because they ignore the - # atomic flag. Use fsdecode to handle Path("-"). - if os.fsdecode(filename) == "-": - if any(m in mode for m in ["w", "a", "x"]): - if binary: - return get_binary_stdout(), False - return get_text_stdout(encoding=encoding, errors=errors), False - if binary: - return get_binary_stdin(), False - return get_text_stdin(encoding=encoding, errors=errors), False - - # Non-atomic writes directly go out through the regular open functions. - if not atomic: - return _wrap_io_open(filename, mode, encoding, errors), True - - # Some usability stuff for atomic writes - if "a" in mode: - raise ValueError( - "Appending to an existing file is not supported, because that" - " would involve an expensive `copy`-operation to a temporary" - " file. Open the file in normal `w`-mode and copy explicitly" - " if that's what you're after." - ) - if "x" in mode: - raise ValueError("Use the `overwrite`-parameter instead.") - if "w" not in mode: - raise ValueError("Atomic writes only make sense with `w`-mode.") - - # Atomic writes are more complicated. They work by opening a file - # as a proxy in the same folder and then using the fdopen - # functionality to wrap it in a Python file. Then we wrap it in an - # atomic file that moves the file over on close. - import errno - import random - - try: - perm: t.Optional[int] = os.stat(filename).st_mode - except OSError: - perm = None - - flags = os.O_RDWR | os.O_CREAT | os.O_EXCL - - if binary: - flags |= getattr(os, "O_BINARY", 0) - - while True: - tmp_filename = os.path.join( - os.path.dirname(filename), - f".__atomic-write{random.randrange(1 << 32):08x}", - ) - try: - fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm) - break - except OSError as e: - if e.errno == errno.EEXIST or ( - os.name == "nt" - and e.errno == errno.EACCES - and os.path.isdir(e.filename) - and os.access(e.filename, os.W_OK) - ): - continue - raise - - if perm is not None: - os.chmod(tmp_filename, perm) # in case perm includes bits in umask - - f = _wrap_io_open(fd, mode, encoding, errors) - af = _AtomicFile(f, tmp_filename, os.path.realpath(filename)) - return t.cast(t.IO[t.Any], af), True - - -class _AtomicFile: - def __init__(self, f: t.IO[t.Any], tmp_filename: str, real_filename: str) -> None: - self._f = f - self._tmp_filename = tmp_filename - self._real_filename = real_filename - self.closed = False - - @property - def name(self) -> str: - return self._real_filename - - def close(self, delete: bool = False) -> None: - if self.closed: - return - self._f.close() - os.replace(self._tmp_filename, self._real_filename) - self.closed = True - - def __getattr__(self, name: str) -> t.Any: - return getattr(self._f, name) - - def __enter__(self) -> "_AtomicFile": - return self - - def __exit__(self, exc_type: t.Optional[t.Type[BaseException]], *_: t.Any) -> None: - self.close(delete=exc_type is not None) - - def __repr__(self) -> str: - return repr(self._f) - - -def strip_ansi(value: str) -> str: - return _ansi_re.sub("", value) - - -def _is_jupyter_kernel_output(stream: t.IO[t.Any]) -> bool: - while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)): - stream = stream._stream - - return stream.__class__.__module__.startswith("ipykernel.") - - -def should_strip_ansi( - stream: t.Optional[t.IO[t.Any]] = None, color: t.Optional[bool] = None -) -> bool: - if color is None: - if stream is None: - stream = sys.stdin - return not isatty(stream) and not _is_jupyter_kernel_output(stream) - return not color - - -# On Windows, wrap the output streams with colorama to support ANSI -# color codes. -# NOTE: double check is needed so mypy does not analyze this on Linux -if sys.platform.startswith("win") and WIN: - from ._winconsole import _get_windows_console_stream - - def _get_argv_encoding() -> str: - import locale - - return locale.getpreferredencoding() - - _ansi_stream_wrappers: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() - - def auto_wrap_for_ansi( # noqa: F811 - stream: t.TextIO, color: t.Optional[bool] = None - ) -> t.TextIO: - """Support ANSI color and style codes on Windows by wrapping a - stream with colorama. - """ - try: - cached = _ansi_stream_wrappers.get(stream) - except Exception: - cached = None - - if cached is not None: - return cached - - import colorama - - strip = should_strip_ansi(stream, color) - ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip) - rv = t.cast(t.TextIO, ansi_wrapper.stream) - _write = rv.write - - def _safe_write(s): - try: - return _write(s) - except BaseException: - ansi_wrapper.reset_all() - raise - - rv.write = _safe_write - - try: - _ansi_stream_wrappers[stream] = rv - except Exception: - pass - - return rv - -else: - - def _get_argv_encoding() -> str: - return getattr(sys.stdin, "encoding", None) or sys.getfilesystemencoding() - - def _get_windows_console_stream( - f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] - ) -> t.Optional[t.TextIO]: - return None - - -def term_len(x: str) -> int: - return len(strip_ansi(x)) - - -def isatty(stream: t.IO[t.Any]) -> bool: - try: - return stream.isatty() - except Exception: - return False - - -def _make_cached_stream_func( - src_func: t.Callable[[], t.Optional[t.TextIO]], - wrapper_func: t.Callable[[], t.TextIO], -) -> t.Callable[[], t.Optional[t.TextIO]]: - cache: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() - - def func() -> t.Optional[t.TextIO]: - stream = src_func() - - if stream is None: - return None - - try: - rv = cache.get(stream) - except Exception: - rv = None - if rv is not None: - return rv - rv = wrapper_func() - try: - cache[stream] = rv - except Exception: - pass - return rv - - return func - - -_default_text_stdin = _make_cached_stream_func(lambda: sys.stdin, get_text_stdin) -_default_text_stdout = _make_cached_stream_func(lambda: sys.stdout, get_text_stdout) -_default_text_stderr = _make_cached_stream_func(lambda: sys.stderr, get_text_stderr) - - -binary_streams: t.Mapping[str, t.Callable[[], t.BinaryIO]] = { - "stdin": get_binary_stdin, - "stdout": get_binary_stdout, - "stderr": get_binary_stderr, -} - -text_streams: t.Mapping[ - str, t.Callable[[t.Optional[str], t.Optional[str]], t.TextIO] -] = { - "stdin": get_text_stdin, - "stdout": get_text_stdout, - "stderr": get_text_stderr, -} diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/_termui_impl.py b/plotter-app/venv/lib/python3.8/site-packages/click/_termui_impl.py deleted file mode 100644 index f744657..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click/_termui_impl.py +++ /dev/null @@ -1,739 +0,0 @@ -""" -This module contains implementations for the termui module. To keep the -import time of Click down, some infrequently used functionality is -placed in this module and only imported as needed. -""" -import contextlib -import math -import os -import sys -import time -import typing as t -from gettext import gettext as _ -from io import StringIO -from types import TracebackType - -from ._compat import _default_text_stdout -from ._compat import CYGWIN -from ._compat import get_best_encoding -from ._compat import isatty -from ._compat import open_stream -from ._compat import strip_ansi -from ._compat import term_len -from ._compat import WIN -from .exceptions import ClickException -from .utils import echo - -V = t.TypeVar("V") - -if os.name == "nt": - BEFORE_BAR = "\r" - AFTER_BAR = "\n" -else: - BEFORE_BAR = "\r\033[?25l" - AFTER_BAR = "\033[?25h\n" - - -class ProgressBar(t.Generic[V]): - def __init__( - self, - iterable: t.Optional[t.Iterable[V]], - length: t.Optional[int] = None, - fill_char: str = "#", - empty_char: str = " ", - bar_template: str = "%(bar)s", - info_sep: str = " ", - show_eta: bool = True, - show_percent: t.Optional[bool] = None, - show_pos: bool = False, - item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, - label: t.Optional[str] = None, - file: t.Optional[t.TextIO] = None, - color: t.Optional[bool] = None, - update_min_steps: int = 1, - width: int = 30, - ) -> None: - self.fill_char = fill_char - self.empty_char = empty_char - self.bar_template = bar_template - self.info_sep = info_sep - self.show_eta = show_eta - self.show_percent = show_percent - self.show_pos = show_pos - self.item_show_func = item_show_func - self.label: str = label or "" - - if file is None: - file = _default_text_stdout() - - # There are no standard streams attached to write to. For example, - # pythonw on Windows. - if file is None: - file = StringIO() - - self.file = file - self.color = color - self.update_min_steps = update_min_steps - self._completed_intervals = 0 - self.width: int = width - self.autowidth: bool = width == 0 - - if length is None: - from operator import length_hint - - length = length_hint(iterable, -1) - - if length == -1: - length = None - if iterable is None: - if length is None: - raise TypeError("iterable or length is required") - iterable = t.cast(t.Iterable[V], range(length)) - self.iter: t.Iterable[V] = iter(iterable) - self.length = length - self.pos = 0 - self.avg: t.List[float] = [] - self.last_eta: float - self.start: float - self.start = self.last_eta = time.time() - self.eta_known: bool = False - self.finished: bool = False - self.max_width: t.Optional[int] = None - self.entered: bool = False - self.current_item: t.Optional[V] = None - self.is_hidden: bool = not isatty(self.file) - self._last_line: t.Optional[str] = None - - def __enter__(self) -> "ProgressBar[V]": - self.entered = True - self.render_progress() - return self - - def __exit__( - self, - exc_type: t.Optional[t.Type[BaseException]], - exc_value: t.Optional[BaseException], - tb: t.Optional[TracebackType], - ) -> None: - self.render_finish() - - def __iter__(self) -> t.Iterator[V]: - if not self.entered: - raise RuntimeError("You need to use progress bars in a with block.") - self.render_progress() - return self.generator() - - def __next__(self) -> V: - # Iteration is defined in terms of a generator function, - # returned by iter(self); use that to define next(). This works - # because `self.iter` is an iterable consumed by that generator, - # so it is re-entry safe. Calling `next(self.generator())` - # twice works and does "what you want". - return next(iter(self)) - - def render_finish(self) -> None: - if self.is_hidden: - return - self.file.write(AFTER_BAR) - self.file.flush() - - @property - def pct(self) -> float: - if self.finished: - return 1.0 - return min(self.pos / (float(self.length or 1) or 1), 1.0) - - @property - def time_per_iteration(self) -> float: - if not self.avg: - return 0.0 - return sum(self.avg) / float(len(self.avg)) - - @property - def eta(self) -> float: - if self.length is not None and not self.finished: - return self.time_per_iteration * (self.length - self.pos) - return 0.0 - - def format_eta(self) -> str: - if self.eta_known: - t = int(self.eta) - seconds = t % 60 - t //= 60 - minutes = t % 60 - t //= 60 - hours = t % 24 - t //= 24 - if t > 0: - return f"{t}d {hours:02}:{minutes:02}:{seconds:02}" - else: - return f"{hours:02}:{minutes:02}:{seconds:02}" - return "" - - def format_pos(self) -> str: - pos = str(self.pos) - if self.length is not None: - pos += f"/{self.length}" - return pos - - def format_pct(self) -> str: - return f"{int(self.pct * 100): 4}%"[1:] - - def format_bar(self) -> str: - if self.length is not None: - bar_length = int(self.pct * self.width) - bar = self.fill_char * bar_length - bar += self.empty_char * (self.width - bar_length) - elif self.finished: - bar = self.fill_char * self.width - else: - chars = list(self.empty_char * (self.width or 1)) - if self.time_per_iteration != 0: - chars[ - int( - (math.cos(self.pos * self.time_per_iteration) / 2.0 + 0.5) - * self.width - ) - ] = self.fill_char - bar = "".join(chars) - return bar - - def format_progress_line(self) -> str: - show_percent = self.show_percent - - info_bits = [] - if self.length is not None and show_percent is None: - show_percent = not self.show_pos - - if self.show_pos: - info_bits.append(self.format_pos()) - if show_percent: - info_bits.append(self.format_pct()) - if self.show_eta and self.eta_known and not self.finished: - info_bits.append(self.format_eta()) - if self.item_show_func is not None: - item_info = self.item_show_func(self.current_item) - if item_info is not None: - info_bits.append(item_info) - - return ( - self.bar_template - % { - "label": self.label, - "bar": self.format_bar(), - "info": self.info_sep.join(info_bits), - } - ).rstrip() - - def render_progress(self) -> None: - import shutil - - if self.is_hidden: - # Only output the label as it changes if the output is not a - # TTY. Use file=stderr if you expect to be piping stdout. - if self._last_line != self.label: - self._last_line = self.label - echo(self.label, file=self.file, color=self.color) - - return - - buf = [] - # Update width in case the terminal has been resized - if self.autowidth: - old_width = self.width - self.width = 0 - clutter_length = term_len(self.format_progress_line()) - new_width = max(0, shutil.get_terminal_size().columns - clutter_length) - if new_width < old_width: - buf.append(BEFORE_BAR) - buf.append(" " * self.max_width) # type: ignore - self.max_width = new_width - self.width = new_width - - clear_width = self.width - if self.max_width is not None: - clear_width = self.max_width - - buf.append(BEFORE_BAR) - line = self.format_progress_line() - line_len = term_len(line) - if self.max_width is None or self.max_width < line_len: - self.max_width = line_len - - buf.append(line) - buf.append(" " * (clear_width - line_len)) - line = "".join(buf) - # Render the line only if it changed. - - if line != self._last_line: - self._last_line = line - echo(line, file=self.file, color=self.color, nl=False) - self.file.flush() - - def make_step(self, n_steps: int) -> None: - self.pos += n_steps - if self.length is not None and self.pos >= self.length: - self.finished = True - - if (time.time() - self.last_eta) < 1.0: - return - - self.last_eta = time.time() - - # self.avg is a rolling list of length <= 7 of steps where steps are - # defined as time elapsed divided by the total progress through - # self.length. - if self.pos: - step = (time.time() - self.start) / self.pos - else: - step = time.time() - self.start - - self.avg = self.avg[-6:] + [step] - - self.eta_known = self.length is not None - - def update(self, n_steps: int, current_item: t.Optional[V] = None) -> None: - """Update the progress bar by advancing a specified number of - steps, and optionally set the ``current_item`` for this new - position. - - :param n_steps: Number of steps to advance. - :param current_item: Optional item to set as ``current_item`` - for the updated position. - - .. versionchanged:: 8.0 - Added the ``current_item`` optional parameter. - - .. versionchanged:: 8.0 - Only render when the number of steps meets the - ``update_min_steps`` threshold. - """ - if current_item is not None: - self.current_item = current_item - - self._completed_intervals += n_steps - - if self._completed_intervals >= self.update_min_steps: - self.make_step(self._completed_intervals) - self.render_progress() - self._completed_intervals = 0 - - def finish(self) -> None: - self.eta_known = False - self.current_item = None - self.finished = True - - def generator(self) -> t.Iterator[V]: - """Return a generator which yields the items added to the bar - during construction, and updates the progress bar *after* the - yielded block returns. - """ - # WARNING: the iterator interface for `ProgressBar` relies on - # this and only works because this is a simple generator which - # doesn't create or manage additional state. If this function - # changes, the impact should be evaluated both against - # `iter(bar)` and `next(bar)`. `next()` in particular may call - # `self.generator()` repeatedly, and this must remain safe in - # order for that interface to work. - if not self.entered: - raise RuntimeError("You need to use progress bars in a with block.") - - if self.is_hidden: - yield from self.iter - else: - for rv in self.iter: - self.current_item = rv - - # This allows show_item_func to be updated before the - # item is processed. Only trigger at the beginning of - # the update interval. - if self._completed_intervals == 0: - self.render_progress() - - yield rv - self.update(1) - - self.finish() - self.render_progress() - - -def pager(generator: t.Iterable[str], color: t.Optional[bool] = None) -> None: - """Decide what method to use for paging through text.""" - stdout = _default_text_stdout() - - # There are no standard streams attached to write to. For example, - # pythonw on Windows. - if stdout is None: - stdout = StringIO() - - if not isatty(sys.stdin) or not isatty(stdout): - return _nullpager(stdout, generator, color) - pager_cmd = (os.environ.get("PAGER", None) or "").strip() - if pager_cmd: - if WIN: - return _tempfilepager(generator, pager_cmd, color) - return _pipepager(generator, pager_cmd, color) - if os.environ.get("TERM") in ("dumb", "emacs"): - return _nullpager(stdout, generator, color) - if WIN or sys.platform.startswith("os2"): - return _tempfilepager(generator, "more <", color) - if hasattr(os, "system") and os.system("(less) 2>/dev/null") == 0: - return _pipepager(generator, "less", color) - - import tempfile - - fd, filename = tempfile.mkstemp() - os.close(fd) - try: - if hasattr(os, "system") and os.system(f'more "{filename}"') == 0: - return _pipepager(generator, "more", color) - return _nullpager(stdout, generator, color) - finally: - os.unlink(filename) - - -def _pipepager(generator: t.Iterable[str], cmd: str, color: t.Optional[bool]) -> None: - """Page through text by feeding it to another program. Invoking a - pager through this might support colors. - """ - import subprocess - - env = dict(os.environ) - - # If we're piping to less we might support colors under the - # condition that - cmd_detail = cmd.rsplit("/", 1)[-1].split() - if color is None and cmd_detail[0] == "less": - less_flags = f"{os.environ.get('LESS', '')}{' '.join(cmd_detail[1:])}" - if not less_flags: - env["LESS"] = "-R" - color = True - elif "r" in less_flags or "R" in less_flags: - color = True - - c = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, env=env) - stdin = t.cast(t.BinaryIO, c.stdin) - encoding = get_best_encoding(stdin) - try: - for text in generator: - if not color: - text = strip_ansi(text) - - stdin.write(text.encode(encoding, "replace")) - except (OSError, KeyboardInterrupt): - pass - else: - stdin.close() - - # Less doesn't respect ^C, but catches it for its own UI purposes (aborting - # search or other commands inside less). - # - # That means when the user hits ^C, the parent process (click) terminates, - # but less is still alive, paging the output and messing up the terminal. - # - # If the user wants to make the pager exit on ^C, they should set - # `LESS='-K'`. It's not our decision to make. - while True: - try: - c.wait() - except KeyboardInterrupt: - pass - else: - break - - -def _tempfilepager( - generator: t.Iterable[str], cmd: str, color: t.Optional[bool] -) -> None: - """Page through text by invoking a program on a temporary file.""" - import tempfile - - fd, filename = tempfile.mkstemp() - # TODO: This never terminates if the passed generator never terminates. - text = "".join(generator) - if not color: - text = strip_ansi(text) - encoding = get_best_encoding(sys.stdout) - with open_stream(filename, "wb")[0] as f: - f.write(text.encode(encoding)) - try: - os.system(f'{cmd} "{filename}"') - finally: - os.close(fd) - os.unlink(filename) - - -def _nullpager( - stream: t.TextIO, generator: t.Iterable[str], color: t.Optional[bool] -) -> None: - """Simply print unformatted text. This is the ultimate fallback.""" - for text in generator: - if not color: - text = strip_ansi(text) - stream.write(text) - - -class Editor: - def __init__( - self, - editor: t.Optional[str] = None, - env: t.Optional[t.Mapping[str, str]] = None, - require_save: bool = True, - extension: str = ".txt", - ) -> None: - self.editor = editor - self.env = env - self.require_save = require_save - self.extension = extension - - def get_editor(self) -> str: - if self.editor is not None: - return self.editor - for key in "VISUAL", "EDITOR": - rv = os.environ.get(key) - if rv: - return rv - if WIN: - return "notepad" - for editor in "sensible-editor", "vim", "nano": - if os.system(f"which {editor} >/dev/null 2>&1") == 0: - return editor - return "vi" - - def edit_file(self, filename: str) -> None: - import subprocess - - editor = self.get_editor() - environ: t.Optional[t.Dict[str, str]] = None - - if self.env: - environ = os.environ.copy() - environ.update(self.env) - - try: - c = subprocess.Popen(f'{editor} "{filename}"', env=environ, shell=True) - exit_code = c.wait() - if exit_code != 0: - raise ClickException( - _("{editor}: Editing failed").format(editor=editor) - ) - except OSError as e: - raise ClickException( - _("{editor}: Editing failed: {e}").format(editor=editor, e=e) - ) from e - - def edit(self, text: t.Optional[t.AnyStr]) -> t.Optional[t.AnyStr]: - import tempfile - - if not text: - data = b"" - elif isinstance(text, (bytes, bytearray)): - data = text - else: - if text and not text.endswith("\n"): - text += "\n" - - if WIN: - data = text.replace("\n", "\r\n").encode("utf-8-sig") - else: - data = text.encode("utf-8") - - fd, name = tempfile.mkstemp(prefix="editor-", suffix=self.extension) - f: t.BinaryIO - - try: - with os.fdopen(fd, "wb") as f: - f.write(data) - - # If the filesystem resolution is 1 second, like Mac OS - # 10.12 Extended, or 2 seconds, like FAT32, and the editor - # closes very fast, require_save can fail. Set the modified - # time to be 2 seconds in the past to work around this. - os.utime(name, (os.path.getatime(name), os.path.getmtime(name) - 2)) - # Depending on the resolution, the exact value might not be - # recorded, so get the new recorded value. - timestamp = os.path.getmtime(name) - - self.edit_file(name) - - if self.require_save and os.path.getmtime(name) == timestamp: - return None - - with open(name, "rb") as f: - rv = f.read() - - if isinstance(text, (bytes, bytearray)): - return rv - - return rv.decode("utf-8-sig").replace("\r\n", "\n") # type: ignore - finally: - os.unlink(name) - - -def open_url(url: str, wait: bool = False, locate: bool = False) -> int: - import subprocess - - def _unquote_file(url: str) -> str: - from urllib.parse import unquote - - if url.startswith("file://"): - url = unquote(url[7:]) - - return url - - if sys.platform == "darwin": - args = ["open"] - if wait: - args.append("-W") - if locate: - args.append("-R") - args.append(_unquote_file(url)) - null = open("/dev/null", "w") - try: - return subprocess.Popen(args, stderr=null).wait() - finally: - null.close() - elif WIN: - if locate: - url = _unquote_file(url.replace('"', "")) - args = f'explorer /select,"{url}"' - else: - url = url.replace('"', "") - wait_str = "/WAIT" if wait else "" - args = f'start {wait_str} "" "{url}"' - return os.system(args) - elif CYGWIN: - if locate: - url = os.path.dirname(_unquote_file(url).replace('"', "")) - args = f'cygstart "{url}"' - else: - url = url.replace('"', "") - wait_str = "-w" if wait else "" - args = f'cygstart {wait_str} "{url}"' - return os.system(args) - - try: - if locate: - url = os.path.dirname(_unquote_file(url)) or "." - else: - url = _unquote_file(url) - c = subprocess.Popen(["xdg-open", url]) - if wait: - return c.wait() - return 0 - except OSError: - if url.startswith(("http://", "https://")) and not locate and not wait: - import webbrowser - - webbrowser.open(url) - return 0 - return 1 - - -def _translate_ch_to_exc(ch: str) -> t.Optional[BaseException]: - if ch == "\x03": - raise KeyboardInterrupt() - - if ch == "\x04" and not WIN: # Unix-like, Ctrl+D - raise EOFError() - - if ch == "\x1a" and WIN: # Windows, Ctrl+Z - raise EOFError() - - return None - - -if WIN: - import msvcrt - - @contextlib.contextmanager - def raw_terminal() -> t.Iterator[int]: - yield -1 - - def getchar(echo: bool) -> str: - # The function `getch` will return a bytes object corresponding to - # the pressed character. Since Windows 10 build 1803, it will also - # return \x00 when called a second time after pressing a regular key. - # - # `getwch` does not share this probably-bugged behavior. Moreover, it - # returns a Unicode object by default, which is what we want. - # - # Either of these functions will return \x00 or \xe0 to indicate - # a special key, and you need to call the same function again to get - # the "rest" of the code. The fun part is that \u00e0 is - # "latin small letter a with grave", so if you type that on a French - # keyboard, you _also_ get a \xe0. - # E.g., consider the Up arrow. This returns \xe0 and then \x48. The - # resulting Unicode string reads as "a with grave" + "capital H". - # This is indistinguishable from when the user actually types - # "a with grave" and then "capital H". - # - # When \xe0 is returned, we assume it's part of a special-key sequence - # and call `getwch` again, but that means that when the user types - # the \u00e0 character, `getchar` doesn't return until a second - # character is typed. - # The alternative is returning immediately, but that would mess up - # cross-platform handling of arrow keys and others that start with - # \xe0. Another option is using `getch`, but then we can't reliably - # read non-ASCII characters, because return values of `getch` are - # limited to the current 8-bit codepage. - # - # Anyway, Click doesn't claim to do this Right(tm), and using `getwch` - # is doing the right thing in more situations than with `getch`. - func: t.Callable[[], str] - - if echo: - func = msvcrt.getwche # type: ignore - else: - func = msvcrt.getwch # type: ignore - - rv = func() - - if rv in ("\x00", "\xe0"): - # \x00 and \xe0 are control characters that indicate special key, - # see above. - rv += func() - - _translate_ch_to_exc(rv) - return rv - -else: - import tty - import termios - - @contextlib.contextmanager - def raw_terminal() -> t.Iterator[int]: - f: t.Optional[t.TextIO] - fd: int - - if not isatty(sys.stdin): - f = open("/dev/tty") - fd = f.fileno() - else: - fd = sys.stdin.fileno() - f = None - - try: - old_settings = termios.tcgetattr(fd) - - try: - tty.setraw(fd) - yield fd - finally: - termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) - sys.stdout.flush() - - if f is not None: - f.close() - except termios.error: - pass - - def getchar(echo: bool) -> str: - with raw_terminal() as fd: - ch = os.read(fd, 32).decode(get_best_encoding(sys.stdin), "replace") - - if echo and isatty(sys.stdout): - sys.stdout.write(ch) - - _translate_ch_to_exc(ch) - return ch diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/_textwrap.py b/plotter-app/venv/lib/python3.8/site-packages/click/_textwrap.py deleted file mode 100644 index b47dcbd..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click/_textwrap.py +++ /dev/null @@ -1,49 +0,0 @@ -import textwrap -import typing as t -from contextlib import contextmanager - - -class TextWrapper(textwrap.TextWrapper): - def _handle_long_word( - self, - reversed_chunks: t.List[str], - cur_line: t.List[str], - cur_len: int, - width: int, - ) -> None: - space_left = max(width - cur_len, 1) - - if self.break_long_words: - last = reversed_chunks[-1] - cut = last[:space_left] - res = last[space_left:] - cur_line.append(cut) - reversed_chunks[-1] = res - elif not cur_line: - cur_line.append(reversed_chunks.pop()) - - @contextmanager - def extra_indent(self, indent: str) -> t.Iterator[None]: - old_initial_indent = self.initial_indent - old_subsequent_indent = self.subsequent_indent - self.initial_indent += indent - self.subsequent_indent += indent - - try: - yield - finally: - self.initial_indent = old_initial_indent - self.subsequent_indent = old_subsequent_indent - - def indent_only(self, text: str) -> str: - rv = [] - - for idx, line in enumerate(text.splitlines()): - indent = self.initial_indent - - if idx > 0: - indent = self.subsequent_indent - - rv.append(f"{indent}{line}") - - return "\n".join(rv) diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/_winconsole.py b/plotter-app/venv/lib/python3.8/site-packages/click/_winconsole.py deleted file mode 100644 index 6b20df3..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click/_winconsole.py +++ /dev/null @@ -1,279 +0,0 @@ -# This module is based on the excellent work by Adam Bartoš who -# provided a lot of what went into the implementation here in -# the discussion to issue1602 in the Python bug tracker. -# -# There are some general differences in regards to how this works -# compared to the original patches as we do not need to patch -# the entire interpreter but just work in our little world of -# echo and prompt. -import io -import sys -import time -import typing as t -from ctypes import byref -from ctypes import c_char -from ctypes import c_char_p -from ctypes import c_int -from ctypes import c_ssize_t -from ctypes import c_ulong -from ctypes import c_void_p -from ctypes import POINTER -from ctypes import py_object -from ctypes import Structure -from ctypes.wintypes import DWORD -from ctypes.wintypes import HANDLE -from ctypes.wintypes import LPCWSTR -from ctypes.wintypes import LPWSTR - -from ._compat import _NonClosingTextIOWrapper - -assert sys.platform == "win32" -import msvcrt # noqa: E402 -from ctypes import windll # noqa: E402 -from ctypes import WINFUNCTYPE # noqa: E402 - -c_ssize_p = POINTER(c_ssize_t) - -kernel32 = windll.kernel32 -GetStdHandle = kernel32.GetStdHandle -ReadConsoleW = kernel32.ReadConsoleW -WriteConsoleW = kernel32.WriteConsoleW -GetConsoleMode = kernel32.GetConsoleMode -GetLastError = kernel32.GetLastError -GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32)) -CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( - ("CommandLineToArgvW", windll.shell32) -) -LocalFree = WINFUNCTYPE(c_void_p, c_void_p)(("LocalFree", windll.kernel32)) - -STDIN_HANDLE = GetStdHandle(-10) -STDOUT_HANDLE = GetStdHandle(-11) -STDERR_HANDLE = GetStdHandle(-12) - -PyBUF_SIMPLE = 0 -PyBUF_WRITABLE = 1 - -ERROR_SUCCESS = 0 -ERROR_NOT_ENOUGH_MEMORY = 8 -ERROR_OPERATION_ABORTED = 995 - -STDIN_FILENO = 0 -STDOUT_FILENO = 1 -STDERR_FILENO = 2 - -EOF = b"\x1a" -MAX_BYTES_WRITTEN = 32767 - -try: - from ctypes import pythonapi -except ImportError: - # On PyPy we cannot get buffers so our ability to operate here is - # severely limited. - get_buffer = None -else: - - class Py_buffer(Structure): - _fields_ = [ - ("buf", c_void_p), - ("obj", py_object), - ("len", c_ssize_t), - ("itemsize", c_ssize_t), - ("readonly", c_int), - ("ndim", c_int), - ("format", c_char_p), - ("shape", c_ssize_p), - ("strides", c_ssize_p), - ("suboffsets", c_ssize_p), - ("internal", c_void_p), - ] - - PyObject_GetBuffer = pythonapi.PyObject_GetBuffer - PyBuffer_Release = pythonapi.PyBuffer_Release - - def get_buffer(obj, writable=False): - buf = Py_buffer() - flags = PyBUF_WRITABLE if writable else PyBUF_SIMPLE - PyObject_GetBuffer(py_object(obj), byref(buf), flags) - - try: - buffer_type = c_char * buf.len - return buffer_type.from_address(buf.buf) - finally: - PyBuffer_Release(byref(buf)) - - -class _WindowsConsoleRawIOBase(io.RawIOBase): - def __init__(self, handle): - self.handle = handle - - def isatty(self): - super().isatty() - return True - - -class _WindowsConsoleReader(_WindowsConsoleRawIOBase): - def readable(self): - return True - - def readinto(self, b): - bytes_to_be_read = len(b) - if not bytes_to_be_read: - return 0 - elif bytes_to_be_read % 2: - raise ValueError( - "cannot read odd number of bytes from UTF-16-LE encoded console" - ) - - buffer = get_buffer(b, writable=True) - code_units_to_be_read = bytes_to_be_read // 2 - code_units_read = c_ulong() - - rv = ReadConsoleW( - HANDLE(self.handle), - buffer, - code_units_to_be_read, - byref(code_units_read), - None, - ) - if GetLastError() == ERROR_OPERATION_ABORTED: - # wait for KeyboardInterrupt - time.sleep(0.1) - if not rv: - raise OSError(f"Windows error: {GetLastError()}") - - if buffer[0] == EOF: - return 0 - return 2 * code_units_read.value - - -class _WindowsConsoleWriter(_WindowsConsoleRawIOBase): - def writable(self): - return True - - @staticmethod - def _get_error_message(errno): - if errno == ERROR_SUCCESS: - return "ERROR_SUCCESS" - elif errno == ERROR_NOT_ENOUGH_MEMORY: - return "ERROR_NOT_ENOUGH_MEMORY" - return f"Windows error {errno}" - - def write(self, b): - bytes_to_be_written = len(b) - buf = get_buffer(b) - code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN) // 2 - code_units_written = c_ulong() - - WriteConsoleW( - HANDLE(self.handle), - buf, - code_units_to_be_written, - byref(code_units_written), - None, - ) - bytes_written = 2 * code_units_written.value - - if bytes_written == 0 and bytes_to_be_written > 0: - raise OSError(self._get_error_message(GetLastError())) - return bytes_written - - -class ConsoleStream: - def __init__(self, text_stream: t.TextIO, byte_stream: t.BinaryIO) -> None: - self._text_stream = text_stream - self.buffer = byte_stream - - @property - def name(self) -> str: - return self.buffer.name - - def write(self, x: t.AnyStr) -> int: - if isinstance(x, str): - return self._text_stream.write(x) - try: - self.flush() - except Exception: - pass - return self.buffer.write(x) - - def writelines(self, lines: t.Iterable[t.AnyStr]) -> None: - for line in lines: - self.write(line) - - def __getattr__(self, name: str) -> t.Any: - return getattr(self._text_stream, name) - - def isatty(self) -> bool: - return self.buffer.isatty() - - def __repr__(self): - return f"" - - -def _get_text_stdin(buffer_stream: t.BinaryIO) -> t.TextIO: - text_stream = _NonClosingTextIOWrapper( - io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), - "utf-16-le", - "strict", - line_buffering=True, - ) - return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) - - -def _get_text_stdout(buffer_stream: t.BinaryIO) -> t.TextIO: - text_stream = _NonClosingTextIOWrapper( - io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)), - "utf-16-le", - "strict", - line_buffering=True, - ) - return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) - - -def _get_text_stderr(buffer_stream: t.BinaryIO) -> t.TextIO: - text_stream = _NonClosingTextIOWrapper( - io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)), - "utf-16-le", - "strict", - line_buffering=True, - ) - return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) - - -_stream_factories: t.Mapping[int, t.Callable[[t.BinaryIO], t.TextIO]] = { - 0: _get_text_stdin, - 1: _get_text_stdout, - 2: _get_text_stderr, -} - - -def _is_console(f: t.TextIO) -> bool: - if not hasattr(f, "fileno"): - return False - - try: - fileno = f.fileno() - except (OSError, io.UnsupportedOperation): - return False - - handle = msvcrt.get_osfhandle(fileno) - return bool(GetConsoleMode(handle, byref(DWORD()))) - - -def _get_windows_console_stream( - f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] -) -> t.Optional[t.TextIO]: - if ( - get_buffer is not None - and encoding in {"utf-16-le", None} - and errors in {"strict", None} - and _is_console(f) - ): - func = _stream_factories.get(f.fileno()) - if func is not None: - b = getattr(f, "buffer", None) - - if b is None: - return None - - return func(b) diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/core.py b/plotter-app/venv/lib/python3.8/site-packages/click/core.py deleted file mode 100644 index cc65e89..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click/core.py +++ /dev/null @@ -1,3042 +0,0 @@ -import enum -import errno -import inspect -import os -import sys -import typing as t -from collections import abc -from contextlib import contextmanager -from contextlib import ExitStack -from functools import update_wrapper -from gettext import gettext as _ -from gettext import ngettext -from itertools import repeat -from types import TracebackType - -from . import types -from .exceptions import Abort -from .exceptions import BadParameter -from .exceptions import ClickException -from .exceptions import Exit -from .exceptions import MissingParameter -from .exceptions import UsageError -from .formatting import HelpFormatter -from .formatting import join_options -from .globals import pop_context -from .globals import push_context -from .parser import _flag_needs_value -from .parser import OptionParser -from .parser import split_opt -from .termui import confirm -from .termui import prompt -from .termui import style -from .utils import _detect_program_name -from .utils import _expand_args -from .utils import echo -from .utils import make_default_short_help -from .utils import make_str -from .utils import PacifyFlushWrapper - -if t.TYPE_CHECKING: - import typing_extensions as te - from .shell_completion import CompletionItem - -F = t.TypeVar("F", bound=t.Callable[..., t.Any]) -V = t.TypeVar("V") - - -def _complete_visible_commands( - ctx: "Context", incomplete: str -) -> t.Iterator[t.Tuple[str, "Command"]]: - """List all the subcommands of a group that start with the - incomplete value and aren't hidden. - - :param ctx: Invocation context for the group. - :param incomplete: Value being completed. May be empty. - """ - multi = t.cast(MultiCommand, ctx.command) - - for name in multi.list_commands(ctx): - if name.startswith(incomplete): - command = multi.get_command(ctx, name) - - if command is not None and not command.hidden: - yield name, command - - -def _check_multicommand( - base_command: "MultiCommand", cmd_name: str, cmd: "Command", register: bool = False -) -> None: - if not base_command.chain or not isinstance(cmd, MultiCommand): - return - if register: - hint = ( - "It is not possible to add multi commands as children to" - " another multi command that is in chain mode." - ) - else: - hint = ( - "Found a multi command as subcommand to a multi command" - " that is in chain mode. This is not supported." - ) - raise RuntimeError( - f"{hint}. Command {base_command.name!r} is set to chain and" - f" {cmd_name!r} was added as a subcommand but it in itself is a" - f" multi command. ({cmd_name!r} is a {type(cmd).__name__}" - f" within a chained {type(base_command).__name__} named" - f" {base_command.name!r})." - ) - - -def batch(iterable: t.Iterable[V], batch_size: int) -> t.List[t.Tuple[V, ...]]: - return list(zip(*repeat(iter(iterable), batch_size))) - - -@contextmanager -def augment_usage_errors( - ctx: "Context", param: t.Optional["Parameter"] = None -) -> t.Iterator[None]: - """Context manager that attaches extra information to exceptions.""" - try: - yield - except BadParameter as e: - if e.ctx is None: - e.ctx = ctx - if param is not None and e.param is None: - e.param = param - raise - except UsageError as e: - if e.ctx is None: - e.ctx = ctx - raise - - -def iter_params_for_processing( - invocation_order: t.Sequence["Parameter"], - declaration_order: t.Sequence["Parameter"], -) -> t.List["Parameter"]: - """Given a sequence of parameters in the order as should be considered - for processing and an iterable of parameters that exist, this returns - a list in the correct order as they should be processed. - """ - - def sort_key(item: "Parameter") -> t.Tuple[bool, float]: - try: - idx: float = invocation_order.index(item) - except ValueError: - idx = float("inf") - - return not item.is_eager, idx - - return sorted(declaration_order, key=sort_key) - - -class ParameterSource(enum.Enum): - """This is an :class:`~enum.Enum` that indicates the source of a - parameter's value. - - Use :meth:`click.Context.get_parameter_source` to get the - source for a parameter by name. - - .. versionchanged:: 8.0 - Use :class:`~enum.Enum` and drop the ``validate`` method. - - .. versionchanged:: 8.0 - Added the ``PROMPT`` value. - """ - - COMMANDLINE = enum.auto() - """The value was provided by the command line args.""" - ENVIRONMENT = enum.auto() - """The value was provided with an environment variable.""" - DEFAULT = enum.auto() - """Used the default specified by the parameter.""" - DEFAULT_MAP = enum.auto() - """Used a default provided by :attr:`Context.default_map`.""" - PROMPT = enum.auto() - """Used a prompt to confirm a default or provide a value.""" - - -class Context: - """The context is a special internal object that holds state relevant - for the script execution at every single level. It's normally invisible - to commands unless they opt-in to getting access to it. - - The context is useful as it can pass internal objects around and can - control special execution features such as reading data from - environment variables. - - A context can be used as context manager in which case it will call - :meth:`close` on teardown. - - :param command: the command class for this context. - :param parent: the parent context. - :param info_name: the info name for this invocation. Generally this - is the most descriptive name for the script or - command. For the toplevel script it is usually - the name of the script, for commands below it it's - the name of the script. - :param obj: an arbitrary object of user data. - :param auto_envvar_prefix: the prefix to use for automatic environment - variables. If this is `None` then reading - from environment variables is disabled. This - does not affect manually set environment - variables which are always read. - :param default_map: a dictionary (like object) with default values - for parameters. - :param terminal_width: the width of the terminal. The default is - inherit from parent context. If no context - defines the terminal width then auto - detection will be applied. - :param max_content_width: the maximum width for content rendered by - Click (this currently only affects help - pages). This defaults to 80 characters if - not overridden. In other words: even if the - terminal is larger than that, Click will not - format things wider than 80 characters by - default. In addition to that, formatters might - add some safety mapping on the right. - :param resilient_parsing: if this flag is enabled then Click will - parse without any interactivity or callback - invocation. Default values will also be - ignored. This is useful for implementing - things such as completion support. - :param allow_extra_args: if this is set to `True` then extra arguments - at the end will not raise an error and will be - kept on the context. The default is to inherit - from the command. - :param allow_interspersed_args: if this is set to `False` then options - and arguments cannot be mixed. The - default is to inherit from the command. - :param ignore_unknown_options: instructs click to ignore options it does - not know and keeps them for later - processing. - :param help_option_names: optionally a list of strings that define how - the default help parameter is named. The - default is ``['--help']``. - :param token_normalize_func: an optional function that is used to - normalize tokens (options, choices, - etc.). This for instance can be used to - implement case insensitive behavior. - :param color: controls if the terminal supports ANSI colors or not. The - default is autodetection. This is only needed if ANSI - codes are used in texts that Click prints which is by - default not the case. This for instance would affect - help output. - :param show_default: Show the default value for commands. If this - value is not set, it defaults to the value from the parent - context. ``Command.show_default`` overrides this default for the - specific command. - - .. versionchanged:: 8.1 - The ``show_default`` parameter is overridden by - ``Command.show_default``, instead of the other way around. - - .. versionchanged:: 8.0 - The ``show_default`` parameter defaults to the value from the - parent context. - - .. versionchanged:: 7.1 - Added the ``show_default`` parameter. - - .. versionchanged:: 4.0 - Added the ``color``, ``ignore_unknown_options``, and - ``max_content_width`` parameters. - - .. versionchanged:: 3.0 - Added the ``allow_extra_args`` and ``allow_interspersed_args`` - parameters. - - .. versionchanged:: 2.0 - Added the ``resilient_parsing``, ``help_option_names``, and - ``token_normalize_func`` parameters. - """ - - #: The formatter class to create with :meth:`make_formatter`. - #: - #: .. versionadded:: 8.0 - formatter_class: t.Type["HelpFormatter"] = HelpFormatter - - def __init__( - self, - command: "Command", - parent: t.Optional["Context"] = None, - info_name: t.Optional[str] = None, - obj: t.Optional[t.Any] = None, - auto_envvar_prefix: t.Optional[str] = None, - default_map: t.Optional[t.MutableMapping[str, t.Any]] = None, - terminal_width: t.Optional[int] = None, - max_content_width: t.Optional[int] = None, - resilient_parsing: bool = False, - allow_extra_args: t.Optional[bool] = None, - allow_interspersed_args: t.Optional[bool] = None, - ignore_unknown_options: t.Optional[bool] = None, - help_option_names: t.Optional[t.List[str]] = None, - token_normalize_func: t.Optional[t.Callable[[str], str]] = None, - color: t.Optional[bool] = None, - show_default: t.Optional[bool] = None, - ) -> None: - #: the parent context or `None` if none exists. - self.parent = parent - #: the :class:`Command` for this context. - self.command = command - #: the descriptive information name - self.info_name = info_name - #: Map of parameter names to their parsed values. Parameters - #: with ``expose_value=False`` are not stored. - self.params: t.Dict[str, t.Any] = {} - #: the leftover arguments. - self.args: t.List[str] = [] - #: protected arguments. These are arguments that are prepended - #: to `args` when certain parsing scenarios are encountered but - #: must be never propagated to another arguments. This is used - #: to implement nested parsing. - self.protected_args: t.List[str] = [] - #: the collected prefixes of the command's options. - self._opt_prefixes: t.Set[str] = set(parent._opt_prefixes) if parent else set() - - if obj is None and parent is not None: - obj = parent.obj - - #: the user object stored. - self.obj: t.Any = obj - self._meta: t.Dict[str, t.Any] = getattr(parent, "meta", {}) - - #: A dictionary (-like object) with defaults for parameters. - if ( - default_map is None - and info_name is not None - and parent is not None - and parent.default_map is not None - ): - default_map = parent.default_map.get(info_name) - - self.default_map: t.Optional[t.MutableMapping[str, t.Any]] = default_map - - #: This flag indicates if a subcommand is going to be executed. A - #: group callback can use this information to figure out if it's - #: being executed directly or because the execution flow passes - #: onwards to a subcommand. By default it's None, but it can be - #: the name of the subcommand to execute. - #: - #: If chaining is enabled this will be set to ``'*'`` in case - #: any commands are executed. It is however not possible to - #: figure out which ones. If you require this knowledge you - #: should use a :func:`result_callback`. - self.invoked_subcommand: t.Optional[str] = None - - if terminal_width is None and parent is not None: - terminal_width = parent.terminal_width - - #: The width of the terminal (None is autodetection). - self.terminal_width: t.Optional[int] = terminal_width - - if max_content_width is None and parent is not None: - max_content_width = parent.max_content_width - - #: The maximum width of formatted content (None implies a sensible - #: default which is 80 for most things). - self.max_content_width: t.Optional[int] = max_content_width - - if allow_extra_args is None: - allow_extra_args = command.allow_extra_args - - #: Indicates if the context allows extra args or if it should - #: fail on parsing. - #: - #: .. versionadded:: 3.0 - self.allow_extra_args = allow_extra_args - - if allow_interspersed_args is None: - allow_interspersed_args = command.allow_interspersed_args - - #: Indicates if the context allows mixing of arguments and - #: options or not. - #: - #: .. versionadded:: 3.0 - self.allow_interspersed_args: bool = allow_interspersed_args - - if ignore_unknown_options is None: - ignore_unknown_options = command.ignore_unknown_options - - #: Instructs click to ignore options that a command does not - #: understand and will store it on the context for later - #: processing. This is primarily useful for situations where you - #: want to call into external programs. Generally this pattern is - #: strongly discouraged because it's not possibly to losslessly - #: forward all arguments. - #: - #: .. versionadded:: 4.0 - self.ignore_unknown_options: bool = ignore_unknown_options - - if help_option_names is None: - if parent is not None: - help_option_names = parent.help_option_names - else: - help_option_names = ["--help"] - - #: The names for the help options. - self.help_option_names: t.List[str] = help_option_names - - if token_normalize_func is None and parent is not None: - token_normalize_func = parent.token_normalize_func - - #: An optional normalization function for tokens. This is - #: options, choices, commands etc. - self.token_normalize_func: t.Optional[ - t.Callable[[str], str] - ] = token_normalize_func - - #: Indicates if resilient parsing is enabled. In that case Click - #: will do its best to not cause any failures and default values - #: will be ignored. Useful for completion. - self.resilient_parsing: bool = resilient_parsing - - # If there is no envvar prefix yet, but the parent has one and - # the command on this level has a name, we can expand the envvar - # prefix automatically. - if auto_envvar_prefix is None: - if ( - parent is not None - and parent.auto_envvar_prefix is not None - and self.info_name is not None - ): - auto_envvar_prefix = ( - f"{parent.auto_envvar_prefix}_{self.info_name.upper()}" - ) - else: - auto_envvar_prefix = auto_envvar_prefix.upper() - - if auto_envvar_prefix is not None: - auto_envvar_prefix = auto_envvar_prefix.replace("-", "_") - - self.auto_envvar_prefix: t.Optional[str] = auto_envvar_prefix - - if color is None and parent is not None: - color = parent.color - - #: Controls if styling output is wanted or not. - self.color: t.Optional[bool] = color - - if show_default is None and parent is not None: - show_default = parent.show_default - - #: Show option default values when formatting help text. - self.show_default: t.Optional[bool] = show_default - - self._close_callbacks: t.List[t.Callable[[], t.Any]] = [] - self._depth = 0 - self._parameter_source: t.Dict[str, ParameterSource] = {} - self._exit_stack = ExitStack() - - def to_info_dict(self) -> t.Dict[str, t.Any]: - """Gather information that could be useful for a tool generating - user-facing documentation. This traverses the entire CLI - structure. - - .. code-block:: python - - with Context(cli) as ctx: - info = ctx.to_info_dict() - - .. versionadded:: 8.0 - """ - return { - "command": self.command.to_info_dict(self), - "info_name": self.info_name, - "allow_extra_args": self.allow_extra_args, - "allow_interspersed_args": self.allow_interspersed_args, - "ignore_unknown_options": self.ignore_unknown_options, - "auto_envvar_prefix": self.auto_envvar_prefix, - } - - def __enter__(self) -> "Context": - self._depth += 1 - push_context(self) - return self - - def __exit__( - self, - exc_type: t.Optional[t.Type[BaseException]], - exc_value: t.Optional[BaseException], - tb: t.Optional[TracebackType], - ) -> None: - self._depth -= 1 - if self._depth == 0: - self.close() - pop_context() - - @contextmanager - def scope(self, cleanup: bool = True) -> t.Iterator["Context"]: - """This helper method can be used with the context object to promote - it to the current thread local (see :func:`get_current_context`). - The default behavior of this is to invoke the cleanup functions which - can be disabled by setting `cleanup` to `False`. The cleanup - functions are typically used for things such as closing file handles. - - If the cleanup is intended the context object can also be directly - used as a context manager. - - Example usage:: - - with ctx.scope(): - assert get_current_context() is ctx - - This is equivalent:: - - with ctx: - assert get_current_context() is ctx - - .. versionadded:: 5.0 - - :param cleanup: controls if the cleanup functions should be run or - not. The default is to run these functions. In - some situations the context only wants to be - temporarily pushed in which case this can be disabled. - Nested pushes automatically defer the cleanup. - """ - if not cleanup: - self._depth += 1 - try: - with self as rv: - yield rv - finally: - if not cleanup: - self._depth -= 1 - - @property - def meta(self) -> t.Dict[str, t.Any]: - """This is a dictionary which is shared with all the contexts - that are nested. It exists so that click utilities can store some - state here if they need to. It is however the responsibility of - that code to manage this dictionary well. - - The keys are supposed to be unique dotted strings. For instance - module paths are a good choice for it. What is stored in there is - irrelevant for the operation of click. However what is important is - that code that places data here adheres to the general semantics of - the system. - - Example usage:: - - LANG_KEY = f'{__name__}.lang' - - def set_language(value): - ctx = get_current_context() - ctx.meta[LANG_KEY] = value - - def get_language(): - return get_current_context().meta.get(LANG_KEY, 'en_US') - - .. versionadded:: 5.0 - """ - return self._meta - - def make_formatter(self) -> HelpFormatter: - """Creates the :class:`~click.HelpFormatter` for the help and - usage output. - - To quickly customize the formatter class used without overriding - this method, set the :attr:`formatter_class` attribute. - - .. versionchanged:: 8.0 - Added the :attr:`formatter_class` attribute. - """ - return self.formatter_class( - width=self.terminal_width, max_width=self.max_content_width - ) - - def with_resource(self, context_manager: t.ContextManager[V]) -> V: - """Register a resource as if it were used in a ``with`` - statement. The resource will be cleaned up when the context is - popped. - - Uses :meth:`contextlib.ExitStack.enter_context`. It calls the - resource's ``__enter__()`` method and returns the result. When - the context is popped, it closes the stack, which calls the - resource's ``__exit__()`` method. - - To register a cleanup function for something that isn't a - context manager, use :meth:`call_on_close`. Or use something - from :mod:`contextlib` to turn it into a context manager first. - - .. code-block:: python - - @click.group() - @click.option("--name") - @click.pass_context - def cli(ctx): - ctx.obj = ctx.with_resource(connect_db(name)) - - :param context_manager: The context manager to enter. - :return: Whatever ``context_manager.__enter__()`` returns. - - .. versionadded:: 8.0 - """ - return self._exit_stack.enter_context(context_manager) - - def call_on_close(self, f: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: - """Register a function to be called when the context tears down. - - This can be used to close resources opened during the script - execution. Resources that support Python's context manager - protocol which would be used in a ``with`` statement should be - registered with :meth:`with_resource` instead. - - :param f: The function to execute on teardown. - """ - return self._exit_stack.callback(f) - - def close(self) -> None: - """Invoke all close callbacks registered with - :meth:`call_on_close`, and exit all context managers entered - with :meth:`with_resource`. - """ - self._exit_stack.close() - # In case the context is reused, create a new exit stack. - self._exit_stack = ExitStack() - - @property - def command_path(self) -> str: - """The computed command path. This is used for the ``usage`` - information on the help page. It's automatically created by - combining the info names of the chain of contexts to the root. - """ - rv = "" - if self.info_name is not None: - rv = self.info_name - if self.parent is not None: - parent_command_path = [self.parent.command_path] - - if isinstance(self.parent.command, Command): - for param in self.parent.command.get_params(self): - parent_command_path.extend(param.get_usage_pieces(self)) - - rv = f"{' '.join(parent_command_path)} {rv}" - return rv.lstrip() - - def find_root(self) -> "Context": - """Finds the outermost context.""" - node = self - while node.parent is not None: - node = node.parent - return node - - def find_object(self, object_type: t.Type[V]) -> t.Optional[V]: - """Finds the closest object of a given type.""" - node: t.Optional["Context"] = self - - while node is not None: - if isinstance(node.obj, object_type): - return node.obj - - node = node.parent - - return None - - def ensure_object(self, object_type: t.Type[V]) -> V: - """Like :meth:`find_object` but sets the innermost object to a - new instance of `object_type` if it does not exist. - """ - rv = self.find_object(object_type) - if rv is None: - self.obj = rv = object_type() - return rv - - @t.overload - def lookup_default( - self, name: str, call: "te.Literal[True]" = True - ) -> t.Optional[t.Any]: - ... - - @t.overload - def lookup_default( - self, name: str, call: "te.Literal[False]" = ... - ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: - ... - - def lookup_default(self, name: str, call: bool = True) -> t.Optional[t.Any]: - """Get the default for a parameter from :attr:`default_map`. - - :param name: Name of the parameter. - :param call: If the default is a callable, call it. Disable to - return the callable instead. - - .. versionchanged:: 8.0 - Added the ``call`` parameter. - """ - if self.default_map is not None: - value = self.default_map.get(name) - - if call and callable(value): - return value() - - return value - - return None - - def fail(self, message: str) -> "te.NoReturn": - """Aborts the execution of the program with a specific error - message. - - :param message: the error message to fail with. - """ - raise UsageError(message, self) - - def abort(self) -> "te.NoReturn": - """Aborts the script.""" - raise Abort() - - def exit(self, code: int = 0) -> "te.NoReturn": - """Exits the application with a given exit code.""" - raise Exit(code) - - def get_usage(self) -> str: - """Helper method to get formatted usage string for the current - context and command. - """ - return self.command.get_usage(self) - - def get_help(self) -> str: - """Helper method to get formatted help page for the current - context and command. - """ - return self.command.get_help(self) - - def _make_sub_context(self, command: "Command") -> "Context": - """Create a new context of the same type as this context, but - for a new command. - - :meta private: - """ - return type(self)(command, info_name=command.name, parent=self) - - @t.overload - def invoke( - __self, # noqa: B902 - __callback: "t.Callable[..., V]", - *args: t.Any, - **kwargs: t.Any, - ) -> V: - ... - - @t.overload - def invoke( - __self, # noqa: B902 - __callback: "Command", - *args: t.Any, - **kwargs: t.Any, - ) -> t.Any: - ... - - def invoke( - __self, # noqa: B902 - __callback: t.Union["Command", "t.Callable[..., V]"], - *args: t.Any, - **kwargs: t.Any, - ) -> t.Union[t.Any, V]: - """Invokes a command callback in exactly the way it expects. There - are two ways to invoke this method: - - 1. the first argument can be a callback and all other arguments and - keyword arguments are forwarded directly to the function. - 2. the first argument is a click command object. In that case all - arguments are forwarded as well but proper click parameters - (options and click arguments) must be keyword arguments and Click - will fill in defaults. - - Note that before Click 3.2 keyword arguments were not properly filled - in against the intention of this code and no context was created. For - more information about this change and why it was done in a bugfix - release see :ref:`upgrade-to-3.2`. - - .. versionchanged:: 8.0 - All ``kwargs`` are tracked in :attr:`params` so they will be - passed if :meth:`forward` is called at multiple levels. - """ - if isinstance(__callback, Command): - other_cmd = __callback - - if other_cmd.callback is None: - raise TypeError( - "The given command does not have a callback that can be invoked." - ) - else: - __callback = t.cast("t.Callable[..., V]", other_cmd.callback) - - ctx = __self._make_sub_context(other_cmd) - - for param in other_cmd.params: - if param.name not in kwargs and param.expose_value: - kwargs[param.name] = param.type_cast_value( # type: ignore - ctx, param.get_default(ctx) - ) - - # Track all kwargs as params, so that forward() will pass - # them on in subsequent calls. - ctx.params.update(kwargs) - else: - ctx = __self - - with augment_usage_errors(__self): - with ctx: - return __callback(*args, **kwargs) - - def forward( - __self, __cmd: "Command", *args: t.Any, **kwargs: t.Any # noqa: B902 - ) -> t.Any: - """Similar to :meth:`invoke` but fills in default keyword - arguments from the current context if the other command expects - it. This cannot invoke callbacks directly, only other commands. - - .. versionchanged:: 8.0 - All ``kwargs`` are tracked in :attr:`params` so they will be - passed if ``forward`` is called at multiple levels. - """ - # Can only forward to other commands, not direct callbacks. - if not isinstance(__cmd, Command): - raise TypeError("Callback is not a command.") - - for param in __self.params: - if param not in kwargs: - kwargs[param] = __self.params[param] - - return __self.invoke(__cmd, *args, **kwargs) - - def set_parameter_source(self, name: str, source: ParameterSource) -> None: - """Set the source of a parameter. This indicates the location - from which the value of the parameter was obtained. - - :param name: The name of the parameter. - :param source: A member of :class:`~click.core.ParameterSource`. - """ - self._parameter_source[name] = source - - def get_parameter_source(self, name: str) -> t.Optional[ParameterSource]: - """Get the source of a parameter. This indicates the location - from which the value of the parameter was obtained. - - This can be useful for determining when a user specified a value - on the command line that is the same as the default value. It - will be :attr:`~click.core.ParameterSource.DEFAULT` only if the - value was actually taken from the default. - - :param name: The name of the parameter. - :rtype: ParameterSource - - .. versionchanged:: 8.0 - Returns ``None`` if the parameter was not provided from any - source. - """ - return self._parameter_source.get(name) - - -class BaseCommand: - """The base command implements the minimal API contract of commands. - Most code will never use this as it does not implement a lot of useful - functionality but it can act as the direct subclass of alternative - parsing methods that do not depend on the Click parser. - - For instance, this can be used to bridge Click and other systems like - argparse or docopt. - - Because base commands do not implement a lot of the API that other - parts of Click take for granted, they are not supported for all - operations. For instance, they cannot be used with the decorators - usually and they have no built-in callback system. - - .. versionchanged:: 2.0 - Added the `context_settings` parameter. - - :param name: the name of the command to use unless a group overrides it. - :param context_settings: an optional dictionary with defaults that are - passed to the context object. - """ - - #: The context class to create with :meth:`make_context`. - #: - #: .. versionadded:: 8.0 - context_class: t.Type[Context] = Context - #: the default for the :attr:`Context.allow_extra_args` flag. - allow_extra_args = False - #: the default for the :attr:`Context.allow_interspersed_args` flag. - allow_interspersed_args = True - #: the default for the :attr:`Context.ignore_unknown_options` flag. - ignore_unknown_options = False - - def __init__( - self, - name: t.Optional[str], - context_settings: t.Optional[t.MutableMapping[str, t.Any]] = None, - ) -> None: - #: the name the command thinks it has. Upon registering a command - #: on a :class:`Group` the group will default the command name - #: with this information. You should instead use the - #: :class:`Context`\'s :attr:`~Context.info_name` attribute. - self.name = name - - if context_settings is None: - context_settings = {} - - #: an optional dictionary with defaults passed to the context. - self.context_settings: t.MutableMapping[str, t.Any] = context_settings - - def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: - """Gather information that could be useful for a tool generating - user-facing documentation. This traverses the entire structure - below this command. - - Use :meth:`click.Context.to_info_dict` to traverse the entire - CLI structure. - - :param ctx: A :class:`Context` representing this command. - - .. versionadded:: 8.0 - """ - return {"name": self.name} - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} {self.name}>" - - def get_usage(self, ctx: Context) -> str: - raise NotImplementedError("Base commands cannot get usage") - - def get_help(self, ctx: Context) -> str: - raise NotImplementedError("Base commands cannot get help") - - def make_context( - self, - info_name: t.Optional[str], - args: t.List[str], - parent: t.Optional[Context] = None, - **extra: t.Any, - ) -> Context: - """This function when given an info name and arguments will kick - off the parsing and create a new :class:`Context`. It does not - invoke the actual command callback though. - - To quickly customize the context class used without overriding - this method, set the :attr:`context_class` attribute. - - :param info_name: the info name for this invocation. Generally this - is the most descriptive name for the script or - command. For the toplevel script it's usually - the name of the script, for commands below it's - the name of the command. - :param args: the arguments to parse as list of strings. - :param parent: the parent context if available. - :param extra: extra keyword arguments forwarded to the context - constructor. - - .. versionchanged:: 8.0 - Added the :attr:`context_class` attribute. - """ - for key, value in self.context_settings.items(): - if key not in extra: - extra[key] = value - - ctx = self.context_class( - self, info_name=info_name, parent=parent, **extra # type: ignore - ) - - with ctx.scope(cleanup=False): - self.parse_args(ctx, args) - return ctx - - def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: - """Given a context and a list of arguments this creates the parser - and parses the arguments, then modifies the context as necessary. - This is automatically invoked by :meth:`make_context`. - """ - raise NotImplementedError("Base commands do not know how to parse arguments.") - - def invoke(self, ctx: Context) -> t.Any: - """Given a context, this invokes the command. The default - implementation is raising a not implemented error. - """ - raise NotImplementedError("Base commands are not invocable by default") - - def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: - """Return a list of completions for the incomplete value. Looks - at the names of chained multi-commands. - - Any command could be part of a chained multi-command, so sibling - commands are valid at any point during command completion. Other - command classes will return more completions. - - :param ctx: Invocation context for this command. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - results: t.List["CompletionItem"] = [] - - while ctx.parent is not None: - ctx = ctx.parent - - if isinstance(ctx.command, MultiCommand) and ctx.command.chain: - results.extend( - CompletionItem(name, help=command.get_short_help_str()) - for name, command in _complete_visible_commands(ctx, incomplete) - if name not in ctx.protected_args - ) - - return results - - @t.overload - def main( - self, - args: t.Optional[t.Sequence[str]] = None, - prog_name: t.Optional[str] = None, - complete_var: t.Optional[str] = None, - standalone_mode: "te.Literal[True]" = True, - **extra: t.Any, - ) -> "te.NoReturn": - ... - - @t.overload - def main( - self, - args: t.Optional[t.Sequence[str]] = None, - prog_name: t.Optional[str] = None, - complete_var: t.Optional[str] = None, - standalone_mode: bool = ..., - **extra: t.Any, - ) -> t.Any: - ... - - def main( - self, - args: t.Optional[t.Sequence[str]] = None, - prog_name: t.Optional[str] = None, - complete_var: t.Optional[str] = None, - standalone_mode: bool = True, - windows_expand_args: bool = True, - **extra: t.Any, - ) -> t.Any: - """This is the way to invoke a script with all the bells and - whistles as a command line application. This will always terminate - the application after a call. If this is not wanted, ``SystemExit`` - needs to be caught. - - This method is also available by directly calling the instance of - a :class:`Command`. - - :param args: the arguments that should be used for parsing. If not - provided, ``sys.argv[1:]`` is used. - :param prog_name: the program name that should be used. By default - the program name is constructed by taking the file - name from ``sys.argv[0]``. - :param complete_var: the environment variable that controls the - bash completion support. The default is - ``"__COMPLETE"`` with prog_name in - uppercase. - :param standalone_mode: the default behavior is to invoke the script - in standalone mode. Click will then - handle exceptions and convert them into - error messages and the function will never - return but shut down the interpreter. If - this is set to `False` they will be - propagated to the caller and the return - value of this function is the return value - of :meth:`invoke`. - :param windows_expand_args: Expand glob patterns, user dir, and - env vars in command line args on Windows. - :param extra: extra keyword arguments are forwarded to the context - constructor. See :class:`Context` for more information. - - .. versionchanged:: 8.0.1 - Added the ``windows_expand_args`` parameter to allow - disabling command line arg expansion on Windows. - - .. versionchanged:: 8.0 - When taking arguments from ``sys.argv`` on Windows, glob - patterns, user dir, and env vars are expanded. - - .. versionchanged:: 3.0 - Added the ``standalone_mode`` parameter. - """ - if args is None: - args = sys.argv[1:] - - if os.name == "nt" and windows_expand_args: - args = _expand_args(args) - else: - args = list(args) - - if prog_name is None: - prog_name = _detect_program_name() - - # Process shell completion requests and exit early. - self._main_shell_completion(extra, prog_name, complete_var) - - try: - try: - with self.make_context(prog_name, args, **extra) as ctx: - rv = self.invoke(ctx) - if not standalone_mode: - return rv - # it's not safe to `ctx.exit(rv)` here! - # note that `rv` may actually contain data like "1" which - # has obvious effects - # more subtle case: `rv=[None, None]` can come out of - # chained commands which all returned `None` -- so it's not - # even always obvious that `rv` indicates success/failure - # by its truthiness/falsiness - ctx.exit() - except (EOFError, KeyboardInterrupt) as e: - echo(file=sys.stderr) - raise Abort() from e - except ClickException as e: - if not standalone_mode: - raise - e.show() - sys.exit(e.exit_code) - except OSError as e: - if e.errno == errno.EPIPE: - sys.stdout = t.cast(t.TextIO, PacifyFlushWrapper(sys.stdout)) - sys.stderr = t.cast(t.TextIO, PacifyFlushWrapper(sys.stderr)) - sys.exit(1) - else: - raise - except Exit as e: - if standalone_mode: - sys.exit(e.exit_code) - else: - # in non-standalone mode, return the exit code - # note that this is only reached if `self.invoke` above raises - # an Exit explicitly -- thus bypassing the check there which - # would return its result - # the results of non-standalone execution may therefore be - # somewhat ambiguous: if there are codepaths which lead to - # `ctx.exit(1)` and to `return 1`, the caller won't be able to - # tell the difference between the two - return e.exit_code - except Abort: - if not standalone_mode: - raise - echo(_("Aborted!"), file=sys.stderr) - sys.exit(1) - - def _main_shell_completion( - self, - ctx_args: t.MutableMapping[str, t.Any], - prog_name: str, - complete_var: t.Optional[str] = None, - ) -> None: - """Check if the shell is asking for tab completion, process - that, then exit early. Called from :meth:`main` before the - program is invoked. - - :param prog_name: Name of the executable in the shell. - :param complete_var: Name of the environment variable that holds - the completion instruction. Defaults to - ``_{PROG_NAME}_COMPLETE``. - - .. versionchanged:: 8.2.0 - Dots (``.``) in ``prog_name`` are replaced with underscores (``_``). - """ - if complete_var is None: - complete_name = prog_name.replace("-", "_").replace(".", "_") - complete_var = f"_{complete_name}_COMPLETE".upper() - - instruction = os.environ.get(complete_var) - - if not instruction: - return - - from .shell_completion import shell_complete - - rv = shell_complete(self, ctx_args, prog_name, complete_var, instruction) - sys.exit(rv) - - def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any: - """Alias for :meth:`main`.""" - return self.main(*args, **kwargs) - - -class Command(BaseCommand): - """Commands are the basic building block of command line interfaces in - Click. A basic command handles command line parsing and might dispatch - more parsing to commands nested below it. - - :param name: the name of the command to use unless a group overrides it. - :param context_settings: an optional dictionary with defaults that are - passed to the context object. - :param callback: the callback to invoke. This is optional. - :param params: the parameters to register with this command. This can - be either :class:`Option` or :class:`Argument` objects. - :param help: the help string to use for this command. - :param epilog: like the help string but it's printed at the end of the - help page after everything else. - :param short_help: the short help to use for this command. This is - shown on the command listing of the parent command. - :param add_help_option: by default each command registers a ``--help`` - option. This can be disabled by this parameter. - :param no_args_is_help: this controls what happens if no arguments are - provided. This option is disabled by default. - If enabled this will add ``--help`` as argument - if no arguments are passed - :param hidden: hide this command from help outputs. - - :param deprecated: issues a message indicating that - the command is deprecated. - - .. versionchanged:: 8.1 - ``help``, ``epilog``, and ``short_help`` are stored unprocessed, - all formatting is done when outputting help text, not at init, - and is done even if not using the ``@command`` decorator. - - .. versionchanged:: 8.0 - Added a ``repr`` showing the command name. - - .. versionchanged:: 7.1 - Added the ``no_args_is_help`` parameter. - - .. versionchanged:: 2.0 - Added the ``context_settings`` parameter. - """ - - def __init__( - self, - name: t.Optional[str], - context_settings: t.Optional[t.MutableMapping[str, t.Any]] = None, - callback: t.Optional[t.Callable[..., t.Any]] = None, - params: t.Optional[t.List["Parameter"]] = None, - help: t.Optional[str] = None, - epilog: t.Optional[str] = None, - short_help: t.Optional[str] = None, - options_metavar: t.Optional[str] = "[OPTIONS]", - add_help_option: bool = True, - no_args_is_help: bool = False, - hidden: bool = False, - deprecated: bool = False, - ) -> None: - super().__init__(name, context_settings) - #: the callback to execute when the command fires. This might be - #: `None` in which case nothing happens. - self.callback = callback - #: the list of parameters for this command in the order they - #: should show up in the help page and execute. Eager parameters - #: will automatically be handled before non eager ones. - self.params: t.List["Parameter"] = params or [] - self.help = help - self.epilog = epilog - self.options_metavar = options_metavar - self.short_help = short_help - self.add_help_option = add_help_option - self.no_args_is_help = no_args_is_help - self.hidden = hidden - self.deprecated = deprecated - - def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict(ctx) - info_dict.update( - params=[param.to_info_dict() for param in self.get_params(ctx)], - help=self.help, - epilog=self.epilog, - short_help=self.short_help, - hidden=self.hidden, - deprecated=self.deprecated, - ) - return info_dict - - def get_usage(self, ctx: Context) -> str: - """Formats the usage line into a string and returns it. - - Calls :meth:`format_usage` internally. - """ - formatter = ctx.make_formatter() - self.format_usage(ctx, formatter) - return formatter.getvalue().rstrip("\n") - - def get_params(self, ctx: Context) -> t.List["Parameter"]: - rv = self.params - help_option = self.get_help_option(ctx) - - if help_option is not None: - rv = [*rv, help_option] - - return rv - - def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None: - """Writes the usage line into the formatter. - - This is a low-level method called by :meth:`get_usage`. - """ - pieces = self.collect_usage_pieces(ctx) - formatter.write_usage(ctx.command_path, " ".join(pieces)) - - def collect_usage_pieces(self, ctx: Context) -> t.List[str]: - """Returns all the pieces that go into the usage line and returns - it as a list of strings. - """ - rv = [self.options_metavar] if self.options_metavar else [] - - for param in self.get_params(ctx): - rv.extend(param.get_usage_pieces(ctx)) - - return rv - - def get_help_option_names(self, ctx: Context) -> t.List[str]: - """Returns the names for the help option.""" - all_names = set(ctx.help_option_names) - for param in self.params: - all_names.difference_update(param.opts) - all_names.difference_update(param.secondary_opts) - return list(all_names) - - def get_help_option(self, ctx: Context) -> t.Optional["Option"]: - """Returns the help option object.""" - help_options = self.get_help_option_names(ctx) - - if not help_options or not self.add_help_option: - return None - - def show_help(ctx: Context, param: "Parameter", value: str) -> None: - if value and not ctx.resilient_parsing: - echo(ctx.get_help(), color=ctx.color) - ctx.exit() - - return Option( - help_options, - is_flag=True, - is_eager=True, - expose_value=False, - callback=show_help, - help=_("Show this message and exit."), - ) - - def make_parser(self, ctx: Context) -> OptionParser: - """Creates the underlying option parser for this command.""" - parser = OptionParser(ctx) - for param in self.get_params(ctx): - param.add_to_parser(parser, ctx) - return parser - - def get_help(self, ctx: Context) -> str: - """Formats the help into a string and returns it. - - Calls :meth:`format_help` internally. - """ - formatter = ctx.make_formatter() - self.format_help(ctx, formatter) - return formatter.getvalue().rstrip("\n") - - def get_short_help_str(self, limit: int = 45) -> str: - """Gets short help for the command or makes it by shortening the - long help string. - """ - if self.short_help: - text = inspect.cleandoc(self.short_help) - elif self.help: - text = make_default_short_help(self.help, limit) - else: - text = "" - - if self.deprecated: - text = _("(Deprecated) {text}").format(text=text) - - return text.strip() - - def format_help(self, ctx: Context, formatter: HelpFormatter) -> None: - """Writes the help into the formatter if it exists. - - This is a low-level method called by :meth:`get_help`. - - This calls the following methods: - - - :meth:`format_usage` - - :meth:`format_help_text` - - :meth:`format_options` - - :meth:`format_epilog` - """ - self.format_usage(ctx, formatter) - self.format_help_text(ctx, formatter) - self.format_options(ctx, formatter) - self.format_epilog(ctx, formatter) - - def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None: - """Writes the help text to the formatter if it exists.""" - if self.help is not None: - # truncate the help text to the first form feed - text = inspect.cleandoc(self.help).partition("\f")[0] - else: - text = "" - - if self.deprecated: - text = _("(Deprecated) {text}").format(text=text) - - if text: - formatter.write_paragraph() - - with formatter.indentation(): - formatter.write_text(text) - - def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: - """Writes all the options into the formatter if they exist.""" - opts = [] - for param in self.get_params(ctx): - rv = param.get_help_record(ctx) - if rv is not None: - opts.append(rv) - - if opts: - with formatter.section(_("Options")): - formatter.write_dl(opts) - - def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None: - """Writes the epilog into the formatter if it exists.""" - if self.epilog: - epilog = inspect.cleandoc(self.epilog) - formatter.write_paragraph() - - with formatter.indentation(): - formatter.write_text(epilog) - - def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: - if not args and self.no_args_is_help and not ctx.resilient_parsing: - echo(ctx.get_help(), color=ctx.color) - ctx.exit() - - parser = self.make_parser(ctx) - opts, args, param_order = parser.parse_args(args=args) - - for param in iter_params_for_processing(param_order, self.get_params(ctx)): - value, args = param.handle_parse_result(ctx, opts, args) - - if args and not ctx.allow_extra_args and not ctx.resilient_parsing: - ctx.fail( - ngettext( - "Got unexpected extra argument ({args})", - "Got unexpected extra arguments ({args})", - len(args), - ).format(args=" ".join(map(str, args))) - ) - - ctx.args = args - ctx._opt_prefixes.update(parser._opt_prefixes) - return args - - def invoke(self, ctx: Context) -> t.Any: - """Given a context, this invokes the attached callback (if it exists) - in the right way. - """ - if self.deprecated: - message = _( - "DeprecationWarning: The command {name!r} is deprecated." - ).format(name=self.name) - echo(style(message, fg="red"), err=True) - - if self.callback is not None: - return ctx.invoke(self.callback, **ctx.params) - - def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: - """Return a list of completions for the incomplete value. Looks - at the names of options and chained multi-commands. - - :param ctx: Invocation context for this command. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - results: t.List["CompletionItem"] = [] - - if incomplete and not incomplete[0].isalnum(): - for param in self.get_params(ctx): - if ( - not isinstance(param, Option) - or param.hidden - or ( - not param.multiple - and ctx.get_parameter_source(param.name) # type: ignore - is ParameterSource.COMMANDLINE - ) - ): - continue - - results.extend( - CompletionItem(name, help=param.help) - for name in [*param.opts, *param.secondary_opts] - if name.startswith(incomplete) - ) - - results.extend(super().shell_complete(ctx, incomplete)) - return results - - -class MultiCommand(Command): - """A multi command is the basic implementation of a command that - dispatches to subcommands. The most common version is the - :class:`Group`. - - :param invoke_without_command: this controls how the multi command itself - is invoked. By default it's only invoked - if a subcommand is provided. - :param no_args_is_help: this controls what happens if no arguments are - provided. This option is enabled by default if - `invoke_without_command` is disabled or disabled - if it's enabled. If enabled this will add - ``--help`` as argument if no arguments are - passed. - :param subcommand_metavar: the string that is used in the documentation - to indicate the subcommand place. - :param chain: if this is set to `True` chaining of multiple subcommands - is enabled. This restricts the form of commands in that - they cannot have optional arguments but it allows - multiple commands to be chained together. - :param result_callback: The result callback to attach to this multi - command. This can be set or changed later with the - :meth:`result_callback` decorator. - :param attrs: Other command arguments described in :class:`Command`. - """ - - allow_extra_args = True - allow_interspersed_args = False - - def __init__( - self, - name: t.Optional[str] = None, - invoke_without_command: bool = False, - no_args_is_help: t.Optional[bool] = None, - subcommand_metavar: t.Optional[str] = None, - chain: bool = False, - result_callback: t.Optional[t.Callable[..., t.Any]] = None, - **attrs: t.Any, - ) -> None: - super().__init__(name, **attrs) - - if no_args_is_help is None: - no_args_is_help = not invoke_without_command - - self.no_args_is_help = no_args_is_help - self.invoke_without_command = invoke_without_command - - if subcommand_metavar is None: - if chain: - subcommand_metavar = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." - else: - subcommand_metavar = "COMMAND [ARGS]..." - - self.subcommand_metavar = subcommand_metavar - self.chain = chain - # The result callback that is stored. This can be set or - # overridden with the :func:`result_callback` decorator. - self._result_callback = result_callback - - if self.chain: - for param in self.params: - if isinstance(param, Argument) and not param.required: - raise RuntimeError( - "Multi commands in chain mode cannot have" - " optional arguments." - ) - - def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict(ctx) - commands = {} - - for name in self.list_commands(ctx): - command = self.get_command(ctx, name) - - if command is None: - continue - - sub_ctx = ctx._make_sub_context(command) - - with sub_ctx.scope(cleanup=False): - commands[name] = command.to_info_dict(sub_ctx) - - info_dict.update(commands=commands, chain=self.chain) - return info_dict - - def collect_usage_pieces(self, ctx: Context) -> t.List[str]: - rv = super().collect_usage_pieces(ctx) - rv.append(self.subcommand_metavar) - return rv - - def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: - super().format_options(ctx, formatter) - self.format_commands(ctx, formatter) - - def result_callback(self, replace: bool = False) -> t.Callable[[F], F]: - """Adds a result callback to the command. By default if a - result callback is already registered this will chain them but - this can be disabled with the `replace` parameter. The result - callback is invoked with the return value of the subcommand - (or the list of return values from all subcommands if chaining - is enabled) as well as the parameters as they would be passed - to the main callback. - - Example:: - - @click.group() - @click.option('-i', '--input', default=23) - def cli(input): - return 42 - - @cli.result_callback() - def process_result(result, input): - return result + input - - :param replace: if set to `True` an already existing result - callback will be removed. - - .. versionchanged:: 8.0 - Renamed from ``resultcallback``. - - .. versionadded:: 3.0 - """ - - def decorator(f: F) -> F: - old_callback = self._result_callback - - if old_callback is None or replace: - self._result_callback = f - return f - - def function(__value, *args, **kwargs): # type: ignore - inner = old_callback(__value, *args, **kwargs) - return f(inner, *args, **kwargs) - - self._result_callback = rv = update_wrapper(t.cast(F, function), f) - return rv - - return decorator - - def format_commands(self, ctx: Context, formatter: HelpFormatter) -> None: - """Extra format methods for multi methods that adds all the commands - after the options. - """ - commands = [] - for subcommand in self.list_commands(ctx): - cmd = self.get_command(ctx, subcommand) - # What is this, the tool lied about a command. Ignore it - if cmd is None: - continue - if cmd.hidden: - continue - - commands.append((subcommand, cmd)) - - # allow for 3 times the default spacing - if len(commands): - limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands) - - rows = [] - for subcommand, cmd in commands: - help = cmd.get_short_help_str(limit) - rows.append((subcommand, help)) - - if rows: - with formatter.section(_("Commands")): - formatter.write_dl(rows) - - def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: - if not args and self.no_args_is_help and not ctx.resilient_parsing: - echo(ctx.get_help(), color=ctx.color) - ctx.exit() - - rest = super().parse_args(ctx, args) - - if self.chain: - ctx.protected_args = rest - ctx.args = [] - elif rest: - ctx.protected_args, ctx.args = rest[:1], rest[1:] - - return ctx.args - - def invoke(self, ctx: Context) -> t.Any: - def _process_result(value: t.Any) -> t.Any: - if self._result_callback is not None: - value = ctx.invoke(self._result_callback, value, **ctx.params) - return value - - if not ctx.protected_args: - if self.invoke_without_command: - # No subcommand was invoked, so the result callback is - # invoked with the group return value for regular - # groups, or an empty list for chained groups. - with ctx: - rv = super().invoke(ctx) - return _process_result([] if self.chain else rv) - ctx.fail(_("Missing command.")) - - # Fetch args back out - args = [*ctx.protected_args, *ctx.args] - ctx.args = [] - ctx.protected_args = [] - - # If we're not in chain mode, we only allow the invocation of a - # single command but we also inform the current context about the - # name of the command to invoke. - if not self.chain: - # Make sure the context is entered so we do not clean up - # resources until the result processor has worked. - with ctx: - cmd_name, cmd, args = self.resolve_command(ctx, args) - assert cmd is not None - ctx.invoked_subcommand = cmd_name - super().invoke(ctx) - sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) - with sub_ctx: - return _process_result(sub_ctx.command.invoke(sub_ctx)) - - # In chain mode we create the contexts step by step, but after the - # base command has been invoked. Because at that point we do not - # know the subcommands yet, the invoked subcommand attribute is - # set to ``*`` to inform the command that subcommands are executed - # but nothing else. - with ctx: - ctx.invoked_subcommand = "*" if args else None - super().invoke(ctx) - - # Otherwise we make every single context and invoke them in a - # chain. In that case the return value to the result processor - # is the list of all invoked subcommand's results. - contexts = [] - while args: - cmd_name, cmd, args = self.resolve_command(ctx, args) - assert cmd is not None - sub_ctx = cmd.make_context( - cmd_name, - args, - parent=ctx, - allow_extra_args=True, - allow_interspersed_args=False, - ) - contexts.append(sub_ctx) - args, sub_ctx.args = sub_ctx.args, [] - - rv = [] - for sub_ctx in contexts: - with sub_ctx: - rv.append(sub_ctx.command.invoke(sub_ctx)) - return _process_result(rv) - - def resolve_command( - self, ctx: Context, args: t.List[str] - ) -> t.Tuple[t.Optional[str], t.Optional[Command], t.List[str]]: - cmd_name = make_str(args[0]) - original_cmd_name = cmd_name - - # Get the command - cmd = self.get_command(ctx, cmd_name) - - # If we can't find the command but there is a normalization - # function available, we try with that one. - if cmd is None and ctx.token_normalize_func is not None: - cmd_name = ctx.token_normalize_func(cmd_name) - cmd = self.get_command(ctx, cmd_name) - - # If we don't find the command we want to show an error message - # to the user that it was not provided. However, there is - # something else we should do: if the first argument looks like - # an option we want to kick off parsing again for arguments to - # resolve things like --help which now should go to the main - # place. - if cmd is None and not ctx.resilient_parsing: - if split_opt(cmd_name)[0]: - self.parse_args(ctx, ctx.args) - ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name)) - return cmd_name if cmd else None, cmd, args[1:] - - def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: - """Given a context and a command name, this returns a - :class:`Command` object if it exists or returns `None`. - """ - raise NotImplementedError - - def list_commands(self, ctx: Context) -> t.List[str]: - """Returns a list of subcommand names in the order they should - appear. - """ - return [] - - def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: - """Return a list of completions for the incomplete value. Looks - at the names of options, subcommands, and chained - multi-commands. - - :param ctx: Invocation context for this command. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - results = [ - CompletionItem(name, help=command.get_short_help_str()) - for name, command in _complete_visible_commands(ctx, incomplete) - ] - results.extend(super().shell_complete(ctx, incomplete)) - return results - - -class Group(MultiCommand): - """A group allows a command to have subcommands attached. This is - the most common way to implement nesting in Click. - - :param name: The name of the group command. - :param commands: A dict mapping names to :class:`Command` objects. - Can also be a list of :class:`Command`, which will use - :attr:`Command.name` to create the dict. - :param attrs: Other command arguments described in - :class:`MultiCommand`, :class:`Command`, and - :class:`BaseCommand`. - - .. versionchanged:: 8.0 - The ``commands`` argument can be a list of command objects. - """ - - #: If set, this is used by the group's :meth:`command` decorator - #: as the default :class:`Command` class. This is useful to make all - #: subcommands use a custom command class. - #: - #: .. versionadded:: 8.0 - command_class: t.Optional[t.Type[Command]] = None - - #: If set, this is used by the group's :meth:`group` decorator - #: as the default :class:`Group` class. This is useful to make all - #: subgroups use a custom group class. - #: - #: If set to the special value :class:`type` (literally - #: ``group_class = type``), this group's class will be used as the - #: default class. This makes a custom group class continue to make - #: custom groups. - #: - #: .. versionadded:: 8.0 - group_class: t.Optional[t.Union[t.Type["Group"], t.Type[type]]] = None - # Literal[type] isn't valid, so use Type[type] - - def __init__( - self, - name: t.Optional[str] = None, - commands: t.Optional[ - t.Union[t.MutableMapping[str, Command], t.Sequence[Command]] - ] = None, - **attrs: t.Any, - ) -> None: - super().__init__(name, **attrs) - - if commands is None: - commands = {} - elif isinstance(commands, abc.Sequence): - commands = {c.name: c for c in commands if c.name is not None} - - #: The registered subcommands by their exported names. - self.commands: t.MutableMapping[str, Command] = commands - - def add_command(self, cmd: Command, name: t.Optional[str] = None) -> None: - """Registers another :class:`Command` with this group. If the name - is not provided, the name of the command is used. - """ - name = name or cmd.name - if name is None: - raise TypeError("Command has no name.") - _check_multicommand(self, name, cmd, register=True) - self.commands[name] = cmd - - @t.overload - def command(self, __func: t.Callable[..., t.Any]) -> Command: - ... - - @t.overload - def command( - self, *args: t.Any, **kwargs: t.Any - ) -> t.Callable[[t.Callable[..., t.Any]], Command]: - ... - - def command( - self, *args: t.Any, **kwargs: t.Any - ) -> t.Union[t.Callable[[t.Callable[..., t.Any]], Command], Command]: - """A shortcut decorator for declaring and attaching a command to - the group. This takes the same arguments as :func:`command` and - immediately registers the created command with this group by - calling :meth:`add_command`. - - To customize the command class used, set the - :attr:`command_class` attribute. - - .. versionchanged:: 8.1 - This decorator can be applied without parentheses. - - .. versionchanged:: 8.0 - Added the :attr:`command_class` attribute. - """ - from .decorators import command - - func: t.Optional[t.Callable[..., t.Any]] = None - - if args and callable(args[0]): - assert ( - len(args) == 1 and not kwargs - ), "Use 'command(**kwargs)(callable)' to provide arguments." - (func,) = args - args = () - - if self.command_class and kwargs.get("cls") is None: - kwargs["cls"] = self.command_class - - def decorator(f: t.Callable[..., t.Any]) -> Command: - cmd: Command = command(*args, **kwargs)(f) - self.add_command(cmd) - return cmd - - if func is not None: - return decorator(func) - - return decorator - - @t.overload - def group(self, __func: t.Callable[..., t.Any]) -> "Group": - ... - - @t.overload - def group( - self, *args: t.Any, **kwargs: t.Any - ) -> t.Callable[[t.Callable[..., t.Any]], "Group"]: - ... - - def group( - self, *args: t.Any, **kwargs: t.Any - ) -> t.Union[t.Callable[[t.Callable[..., t.Any]], "Group"], "Group"]: - """A shortcut decorator for declaring and attaching a group to - the group. This takes the same arguments as :func:`group` and - immediately registers the created group with this group by - calling :meth:`add_command`. - - To customize the group class used, set the :attr:`group_class` - attribute. - - .. versionchanged:: 8.1 - This decorator can be applied without parentheses. - - .. versionchanged:: 8.0 - Added the :attr:`group_class` attribute. - """ - from .decorators import group - - func: t.Optional[t.Callable[..., t.Any]] = None - - if args and callable(args[0]): - assert ( - len(args) == 1 and not kwargs - ), "Use 'group(**kwargs)(callable)' to provide arguments." - (func,) = args - args = () - - if self.group_class is not None and kwargs.get("cls") is None: - if self.group_class is type: - kwargs["cls"] = type(self) - else: - kwargs["cls"] = self.group_class - - def decorator(f: t.Callable[..., t.Any]) -> "Group": - cmd: Group = group(*args, **kwargs)(f) - self.add_command(cmd) - return cmd - - if func is not None: - return decorator(func) - - return decorator - - def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: - return self.commands.get(cmd_name) - - def list_commands(self, ctx: Context) -> t.List[str]: - return sorted(self.commands) - - -class CommandCollection(MultiCommand): - """A command collection is a multi command that merges multiple multi - commands together into one. This is a straightforward implementation - that accepts a list of different multi commands as sources and - provides all the commands for each of them. - - See :class:`MultiCommand` and :class:`Command` for the description of - ``name`` and ``attrs``. - """ - - def __init__( - self, - name: t.Optional[str] = None, - sources: t.Optional[t.List[MultiCommand]] = None, - **attrs: t.Any, - ) -> None: - super().__init__(name, **attrs) - #: The list of registered multi commands. - self.sources: t.List[MultiCommand] = sources or [] - - def add_source(self, multi_cmd: MultiCommand) -> None: - """Adds a new multi command to the chain dispatcher.""" - self.sources.append(multi_cmd) - - def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: - for source in self.sources: - rv = source.get_command(ctx, cmd_name) - - if rv is not None: - if self.chain: - _check_multicommand(self, cmd_name, rv) - - return rv - - return None - - def list_commands(self, ctx: Context) -> t.List[str]: - rv: t.Set[str] = set() - - for source in self.sources: - rv.update(source.list_commands(ctx)) - - return sorted(rv) - - -def _check_iter(value: t.Any) -> t.Iterator[t.Any]: - """Check if the value is iterable but not a string. Raises a type - error, or return an iterator over the value. - """ - if isinstance(value, str): - raise TypeError - - return iter(value) - - -class Parameter: - r"""A parameter to a command comes in two versions: they are either - :class:`Option`\s or :class:`Argument`\s. Other subclasses are currently - not supported by design as some of the internals for parsing are - intentionally not finalized. - - Some settings are supported by both options and arguments. - - :param param_decls: the parameter declarations for this option or - argument. This is a list of flags or argument - names. - :param type: the type that should be used. Either a :class:`ParamType` - or a Python type. The latter is converted into the former - automatically if supported. - :param required: controls if this is optional or not. - :param default: the default value if omitted. This can also be a callable, - in which case it's invoked when the default is needed - without any arguments. - :param callback: A function to further process or validate the value - after type conversion. It is called as ``f(ctx, param, value)`` - and must return the value. It is called for all sources, - including prompts. - :param nargs: the number of arguments to match. If not ``1`` the return - value is a tuple instead of single value. The default for - nargs is ``1`` (except if the type is a tuple, then it's - the arity of the tuple). If ``nargs=-1``, all remaining - parameters are collected. - :param metavar: how the value is represented in the help page. - :param expose_value: if this is `True` then the value is passed onwards - to the command callback and stored on the context, - otherwise it's skipped. - :param is_eager: eager values are processed before non eager ones. This - should not be set for arguments or it will inverse the - order of processing. - :param envvar: a string or list of strings that are environment variables - that should be checked. - :param shell_complete: A function that returns custom shell - completions. Used instead of the param's type completion if - given. Takes ``ctx, param, incomplete`` and must return a list - of :class:`~click.shell_completion.CompletionItem` or a list of - strings. - - .. versionchanged:: 8.0 - ``process_value`` validates required parameters and bounded - ``nargs``, and invokes the parameter callback before returning - the value. This allows the callback to validate prompts. - ``full_process_value`` is removed. - - .. versionchanged:: 8.0 - ``autocompletion`` is renamed to ``shell_complete`` and has new - semantics described above. The old name is deprecated and will - be removed in 8.1, until then it will be wrapped to match the - new requirements. - - .. versionchanged:: 8.0 - For ``multiple=True, nargs>1``, the default must be a list of - tuples. - - .. versionchanged:: 8.0 - Setting a default is no longer required for ``nargs>1``, it will - default to ``None``. ``multiple=True`` or ``nargs=-1`` will - default to ``()``. - - .. versionchanged:: 7.1 - Empty environment variables are ignored rather than taking the - empty string value. This makes it possible for scripts to clear - variables if they can't unset them. - - .. versionchanged:: 2.0 - Changed signature for parameter callback to also be passed the - parameter. The old callback format will still work, but it will - raise a warning to give you a chance to migrate the code easier. - """ - - param_type_name = "parameter" - - def __init__( - self, - param_decls: t.Optional[t.Sequence[str]] = None, - type: t.Optional[t.Union[types.ParamType, t.Any]] = None, - required: bool = False, - default: t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]] = None, - callback: t.Optional[t.Callable[[Context, "Parameter", t.Any], t.Any]] = None, - nargs: t.Optional[int] = None, - multiple: bool = False, - metavar: t.Optional[str] = None, - expose_value: bool = True, - is_eager: bool = False, - envvar: t.Optional[t.Union[str, t.Sequence[str]]] = None, - shell_complete: t.Optional[ - t.Callable[ - [Context, "Parameter", str], - t.Union[t.List["CompletionItem"], t.List[str]], - ] - ] = None, - ) -> None: - self.name: t.Optional[str] - self.opts: t.List[str] - self.secondary_opts: t.List[str] - self.name, self.opts, self.secondary_opts = self._parse_decls( - param_decls or (), expose_value - ) - self.type: types.ParamType = types.convert_type(type, default) - - # Default nargs to what the type tells us if we have that - # information available. - if nargs is None: - if self.type.is_composite: - nargs = self.type.arity - else: - nargs = 1 - - self.required = required - self.callback = callback - self.nargs = nargs - self.multiple = multiple - self.expose_value = expose_value - self.default = default - self.is_eager = is_eager - self.metavar = metavar - self.envvar = envvar - self._custom_shell_complete = shell_complete - - if __debug__: - if self.type.is_composite and nargs != self.type.arity: - raise ValueError( - f"'nargs' must be {self.type.arity} (or None) for" - f" type {self.type!r}, but it was {nargs}." - ) - - # Skip no default or callable default. - check_default = default if not callable(default) else None - - if check_default is not None: - if multiple: - try: - # Only check the first value against nargs. - check_default = next(_check_iter(check_default), None) - except TypeError: - raise ValueError( - "'default' must be a list when 'multiple' is true." - ) from None - - # Can be None for multiple with empty default. - if nargs != 1 and check_default is not None: - try: - _check_iter(check_default) - except TypeError: - if multiple: - message = ( - "'default' must be a list of lists when 'multiple' is" - " true and 'nargs' != 1." - ) - else: - message = "'default' must be a list when 'nargs' != 1." - - raise ValueError(message) from None - - if nargs > 1 and len(check_default) != nargs: - subject = "item length" if multiple else "length" - raise ValueError( - f"'default' {subject} must match nargs={nargs}." - ) - - def to_info_dict(self) -> t.Dict[str, t.Any]: - """Gather information that could be useful for a tool generating - user-facing documentation. - - Use :meth:`click.Context.to_info_dict` to traverse the entire - CLI structure. - - .. versionadded:: 8.0 - """ - return { - "name": self.name, - "param_type_name": self.param_type_name, - "opts": self.opts, - "secondary_opts": self.secondary_opts, - "type": self.type.to_info_dict(), - "required": self.required, - "nargs": self.nargs, - "multiple": self.multiple, - "default": self.default, - "envvar": self.envvar, - } - - def __repr__(self) -> str: - return f"<{self.__class__.__name__} {self.name}>" - - def _parse_decls( - self, decls: t.Sequence[str], expose_value: bool - ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: - raise NotImplementedError() - - @property - def human_readable_name(self) -> str: - """Returns the human readable name of this parameter. This is the - same as the name for options, but the metavar for arguments. - """ - return self.name # type: ignore - - def make_metavar(self) -> str: - if self.metavar is not None: - return self.metavar - - metavar = self.type.get_metavar(self) - - if metavar is None: - metavar = self.type.name.upper() - - if self.nargs != 1: - metavar += "..." - - return metavar - - @t.overload - def get_default( - self, ctx: Context, call: "te.Literal[True]" = True - ) -> t.Optional[t.Any]: - ... - - @t.overload - def get_default( - self, ctx: Context, call: bool = ... - ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: - ... - - def get_default( - self, ctx: Context, call: bool = True - ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: - """Get the default for the parameter. Tries - :meth:`Context.lookup_default` first, then the local default. - - :param ctx: Current context. - :param call: If the default is a callable, call it. Disable to - return the callable instead. - - .. versionchanged:: 8.0.2 - Type casting is no longer performed when getting a default. - - .. versionchanged:: 8.0.1 - Type casting can fail in resilient parsing mode. Invalid - defaults will not prevent showing help text. - - .. versionchanged:: 8.0 - Looks at ``ctx.default_map`` first. - - .. versionchanged:: 8.0 - Added the ``call`` parameter. - """ - value = ctx.lookup_default(self.name, call=False) # type: ignore - - if value is None: - value = self.default - - if call and callable(value): - value = value() - - return value - - def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: - raise NotImplementedError() - - def consume_value( - self, ctx: Context, opts: t.Mapping[str, t.Any] - ) -> t.Tuple[t.Any, ParameterSource]: - value = opts.get(self.name) # type: ignore - source = ParameterSource.COMMANDLINE - - if value is None: - value = self.value_from_envvar(ctx) - source = ParameterSource.ENVIRONMENT - - if value is None: - value = ctx.lookup_default(self.name) # type: ignore - source = ParameterSource.DEFAULT_MAP - - if value is None: - value = self.get_default(ctx) - source = ParameterSource.DEFAULT - - return value, source - - def type_cast_value(self, ctx: Context, value: t.Any) -> t.Any: - """Convert and validate a value against the option's - :attr:`type`, :attr:`multiple`, and :attr:`nargs`. - """ - if value is None: - return () if self.multiple or self.nargs == -1 else None - - def check_iter(value: t.Any) -> t.Iterator[t.Any]: - try: - return _check_iter(value) - except TypeError: - # This should only happen when passing in args manually, - # the parser should construct an iterable when parsing - # the command line. - raise BadParameter( - _("Value must be an iterable."), ctx=ctx, param=self - ) from None - - if self.nargs == 1 or self.type.is_composite: - - def convert(value: t.Any) -> t.Any: - return self.type(value, param=self, ctx=ctx) - - elif self.nargs == -1: - - def convert(value: t.Any) -> t.Any: # t.Tuple[t.Any, ...] - return tuple(self.type(x, self, ctx) for x in check_iter(value)) - - else: # nargs > 1 - - def convert(value: t.Any) -> t.Any: # t.Tuple[t.Any, ...] - value = tuple(check_iter(value)) - - if len(value) != self.nargs: - raise BadParameter( - ngettext( - "Takes {nargs} values but 1 was given.", - "Takes {nargs} values but {len} were given.", - len(value), - ).format(nargs=self.nargs, len=len(value)), - ctx=ctx, - param=self, - ) - - return tuple(self.type(x, self, ctx) for x in value) - - if self.multiple: - return tuple(convert(x) for x in check_iter(value)) - - return convert(value) - - def value_is_missing(self, value: t.Any) -> bool: - if value is None: - return True - - if (self.nargs != 1 or self.multiple) and value == (): - return True - - return False - - def process_value(self, ctx: Context, value: t.Any) -> t.Any: - value = self.type_cast_value(ctx, value) - - if self.required and self.value_is_missing(value): - raise MissingParameter(ctx=ctx, param=self) - - if self.callback is not None: - value = self.callback(ctx, self, value) - - return value - - def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: - if self.envvar is None: - return None - - if isinstance(self.envvar, str): - rv = os.environ.get(self.envvar) - - if rv: - return rv - else: - for envvar in self.envvar: - rv = os.environ.get(envvar) - - if rv: - return rv - - return None - - def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: - rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) - - if rv is not None and self.nargs != 1: - rv = self.type.split_envvar_value(rv) - - return rv - - def handle_parse_result( - self, ctx: Context, opts: t.Mapping[str, t.Any], args: t.List[str] - ) -> t.Tuple[t.Any, t.List[str]]: - with augment_usage_errors(ctx, param=self): - value, source = self.consume_value(ctx, opts) - ctx.set_parameter_source(self.name, source) # type: ignore - - try: - value = self.process_value(ctx, value) - except Exception: - if not ctx.resilient_parsing: - raise - - value = None - - if self.expose_value: - ctx.params[self.name] = value # type: ignore - - return value, args - - def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: - pass - - def get_usage_pieces(self, ctx: Context) -> t.List[str]: - return [] - - def get_error_hint(self, ctx: Context) -> str: - """Get a stringified version of the param for use in error messages to - indicate which param caused the error. - """ - hint_list = self.opts or [self.human_readable_name] - return " / ".join(f"'{x}'" for x in hint_list) - - def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: - """Return a list of completions for the incomplete value. If a - ``shell_complete`` function was given during init, it is used. - Otherwise, the :attr:`type` - :meth:`~click.types.ParamType.shell_complete` function is used. - - :param ctx: Invocation context for this command. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - if self._custom_shell_complete is not None: - results = self._custom_shell_complete(ctx, self, incomplete) - - if results and isinstance(results[0], str): - from click.shell_completion import CompletionItem - - results = [CompletionItem(c) for c in results] - - return t.cast(t.List["CompletionItem"], results) - - return self.type.shell_complete(ctx, self, incomplete) - - -class Option(Parameter): - """Options are usually optional values on the command line and - have some extra features that arguments don't have. - - All other parameters are passed onwards to the parameter constructor. - - :param show_default: Show the default value for this option in its - help text. Values are not shown by default, unless - :attr:`Context.show_default` is ``True``. If this value is a - string, it shows that string in parentheses instead of the - actual value. This is particularly useful for dynamic options. - For single option boolean flags, the default remains hidden if - its value is ``False``. - :param show_envvar: Controls if an environment variable should be - shown on the help page. Normally, environment variables are not - shown. - :param prompt: If set to ``True`` or a non empty string then the - user will be prompted for input. If set to ``True`` the prompt - will be the option name capitalized. - :param confirmation_prompt: Prompt a second time to confirm the - value if it was prompted for. Can be set to a string instead of - ``True`` to customize the message. - :param prompt_required: If set to ``False``, the user will be - prompted for input only when the option was specified as a flag - without a value. - :param hide_input: If this is ``True`` then the input on the prompt - will be hidden from the user. This is useful for password input. - :param is_flag: forces this option to act as a flag. The default is - auto detection. - :param flag_value: which value should be used for this flag if it's - enabled. This is set to a boolean automatically if - the option string contains a slash to mark two options. - :param multiple: if this is set to `True` then the argument is accepted - multiple times and recorded. This is similar to ``nargs`` - in how it works but supports arbitrary number of - arguments. - :param count: this flag makes an option increment an integer. - :param allow_from_autoenv: if this is enabled then the value of this - parameter will be pulled from an environment - variable in case a prefix is defined on the - context. - :param help: the help string. - :param hidden: hide this option from help outputs. - :param attrs: Other command arguments described in :class:`Parameter`. - - .. versionchanged:: 8.1.0 - Help text indentation is cleaned here instead of only in the - ``@option`` decorator. - - .. versionchanged:: 8.1.0 - The ``show_default`` parameter overrides - ``Context.show_default``. - - .. versionchanged:: 8.1.0 - The default of a single option boolean flag is not shown if the - default value is ``False``. - - .. versionchanged:: 8.0.1 - ``type`` is detected from ``flag_value`` if given. - """ - - param_type_name = "option" - - def __init__( - self, - param_decls: t.Optional[t.Sequence[str]] = None, - show_default: t.Union[bool, str, None] = None, - prompt: t.Union[bool, str] = False, - confirmation_prompt: t.Union[bool, str] = False, - prompt_required: bool = True, - hide_input: bool = False, - is_flag: t.Optional[bool] = None, - flag_value: t.Optional[t.Any] = None, - multiple: bool = False, - count: bool = False, - allow_from_autoenv: bool = True, - type: t.Optional[t.Union[types.ParamType, t.Any]] = None, - help: t.Optional[str] = None, - hidden: bool = False, - show_choices: bool = True, - show_envvar: bool = False, - **attrs: t.Any, - ) -> None: - if help: - help = inspect.cleandoc(help) - - default_is_missing = "default" not in attrs - super().__init__(param_decls, type=type, multiple=multiple, **attrs) - - if prompt is True: - if self.name is None: - raise TypeError("'name' is required with 'prompt=True'.") - - prompt_text: t.Optional[str] = self.name.replace("_", " ").capitalize() - elif prompt is False: - prompt_text = None - else: - prompt_text = prompt - - self.prompt = prompt_text - self.confirmation_prompt = confirmation_prompt - self.prompt_required = prompt_required - self.hide_input = hide_input - self.hidden = hidden - - # If prompt is enabled but not required, then the option can be - # used as a flag to indicate using prompt or flag_value. - self._flag_needs_value = self.prompt is not None and not self.prompt_required - - if is_flag is None: - if flag_value is not None: - # Implicitly a flag because flag_value was set. - is_flag = True - elif self._flag_needs_value: - # Not a flag, but when used as a flag it shows a prompt. - is_flag = False - else: - # Implicitly a flag because flag options were given. - is_flag = bool(self.secondary_opts) - elif is_flag is False and not self._flag_needs_value: - # Not a flag, and prompt is not enabled, can be used as a - # flag if flag_value is set. - self._flag_needs_value = flag_value is not None - - self.default: t.Union[t.Any, t.Callable[[], t.Any]] - - if is_flag and default_is_missing and not self.required: - if multiple: - self.default = () - else: - self.default = False - - if flag_value is None: - flag_value = not self.default - - self.type: types.ParamType - if is_flag and type is None: - # Re-guess the type from the flag value instead of the - # default. - self.type = types.convert_type(None, flag_value) - - self.is_flag: bool = is_flag - self.is_bool_flag: bool = is_flag and isinstance(self.type, types.BoolParamType) - self.flag_value: t.Any = flag_value - - # Counting - self.count = count - if count: - if type is None: - self.type = types.IntRange(min=0) - if default_is_missing: - self.default = 0 - - self.allow_from_autoenv = allow_from_autoenv - self.help = help - self.show_default = show_default - self.show_choices = show_choices - self.show_envvar = show_envvar - - if __debug__: - if self.nargs == -1: - raise TypeError("nargs=-1 is not supported for options.") - - if self.prompt and self.is_flag and not self.is_bool_flag: - raise TypeError("'prompt' is not valid for non-boolean flag.") - - if not self.is_bool_flag and self.secondary_opts: - raise TypeError("Secondary flag is not valid for non-boolean flag.") - - if self.is_bool_flag and self.hide_input and self.prompt is not None: - raise TypeError( - "'prompt' with 'hide_input' is not valid for boolean flag." - ) - - if self.count: - if self.multiple: - raise TypeError("'count' is not valid with 'multiple'.") - - if self.is_flag: - raise TypeError("'count' is not valid with 'is_flag'.") - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict.update( - help=self.help, - prompt=self.prompt, - is_flag=self.is_flag, - flag_value=self.flag_value, - count=self.count, - hidden=self.hidden, - ) - return info_dict - - def _parse_decls( - self, decls: t.Sequence[str], expose_value: bool - ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: - opts = [] - secondary_opts = [] - name = None - possible_names = [] - - for decl in decls: - if decl.isidentifier(): - if name is not None: - raise TypeError(f"Name '{name}' defined twice") - name = decl - else: - split_char = ";" if decl[:1] == "/" else "/" - if split_char in decl: - first, second = decl.split(split_char, 1) - first = first.rstrip() - if first: - possible_names.append(split_opt(first)) - opts.append(first) - second = second.lstrip() - if second: - secondary_opts.append(second.lstrip()) - if first == second: - raise ValueError( - f"Boolean option {decl!r} cannot use the" - " same flag for true/false." - ) - else: - possible_names.append(split_opt(decl)) - opts.append(decl) - - if name is None and possible_names: - possible_names.sort(key=lambda x: -len(x[0])) # group long options first - name = possible_names[0][1].replace("-", "_").lower() - if not name.isidentifier(): - name = None - - if name is None: - if not expose_value: - return None, opts, secondary_opts - raise TypeError("Could not determine name for option") - - if not opts and not secondary_opts: - raise TypeError( - f"No options defined but a name was passed ({name})." - " Did you mean to declare an argument instead? Did" - f" you mean to pass '--{name}'?" - ) - - return name, opts, secondary_opts - - def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: - if self.multiple: - action = "append" - elif self.count: - action = "count" - else: - action = "store" - - if self.is_flag: - action = f"{action}_const" - - if self.is_bool_flag and self.secondary_opts: - parser.add_option( - obj=self, opts=self.opts, dest=self.name, action=action, const=True - ) - parser.add_option( - obj=self, - opts=self.secondary_opts, - dest=self.name, - action=action, - const=False, - ) - else: - parser.add_option( - obj=self, - opts=self.opts, - dest=self.name, - action=action, - const=self.flag_value, - ) - else: - parser.add_option( - obj=self, - opts=self.opts, - dest=self.name, - action=action, - nargs=self.nargs, - ) - - def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: - if self.hidden: - return None - - any_prefix_is_slash = False - - def _write_opts(opts: t.Sequence[str]) -> str: - nonlocal any_prefix_is_slash - - rv, any_slashes = join_options(opts) - - if any_slashes: - any_prefix_is_slash = True - - if not self.is_flag and not self.count: - rv += f" {self.make_metavar()}" - - return rv - - rv = [_write_opts(self.opts)] - - if self.secondary_opts: - rv.append(_write_opts(self.secondary_opts)) - - help = self.help or "" - extra = [] - - if self.show_envvar: - envvar = self.envvar - - if envvar is None: - if ( - self.allow_from_autoenv - and ctx.auto_envvar_prefix is not None - and self.name is not None - ): - envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" - - if envvar is not None: - var_str = ( - envvar - if isinstance(envvar, str) - else ", ".join(str(d) for d in envvar) - ) - extra.append(_("env var: {var}").format(var=var_str)) - - # Temporarily enable resilient parsing to avoid type casting - # failing for the default. Might be possible to extend this to - # help formatting in general. - resilient = ctx.resilient_parsing - ctx.resilient_parsing = True - - try: - default_value = self.get_default(ctx, call=False) - finally: - ctx.resilient_parsing = resilient - - show_default = False - show_default_is_str = False - - if self.show_default is not None: - if isinstance(self.show_default, str): - show_default_is_str = show_default = True - else: - show_default = self.show_default - elif ctx.show_default is not None: - show_default = ctx.show_default - - if show_default_is_str or (show_default and (default_value is not None)): - if show_default_is_str: - default_string = f"({self.show_default})" - elif isinstance(default_value, (list, tuple)): - default_string = ", ".join(str(d) for d in default_value) - elif inspect.isfunction(default_value): - default_string = _("(dynamic)") - elif self.is_bool_flag and self.secondary_opts: - # For boolean flags that have distinct True/False opts, - # use the opt without prefix instead of the value. - default_string = split_opt( - (self.opts if self.default else self.secondary_opts)[0] - )[1] - elif self.is_bool_flag and not self.secondary_opts and not default_value: - default_string = "" - else: - default_string = str(default_value) - - if default_string: - extra.append(_("default: {default}").format(default=default_string)) - - if ( - isinstance(self.type, types._NumberRangeBase) - # skip count with default range type - and not (self.count and self.type.min == 0 and self.type.max is None) - ): - range_str = self.type._describe_range() - - if range_str: - extra.append(range_str) - - if self.required: - extra.append(_("required")) - - if extra: - extra_str = "; ".join(extra) - help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" - - return ("; " if any_prefix_is_slash else " / ").join(rv), help - - @t.overload - def get_default( - self, ctx: Context, call: "te.Literal[True]" = True - ) -> t.Optional[t.Any]: - ... - - @t.overload - def get_default( - self, ctx: Context, call: bool = ... - ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: - ... - - def get_default( - self, ctx: Context, call: bool = True - ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: - # If we're a non boolean flag our default is more complex because - # we need to look at all flags in the same group to figure out - # if we're the default one in which case we return the flag - # value as default. - if self.is_flag and not self.is_bool_flag: - for param in ctx.command.params: - if param.name == self.name and param.default: - return t.cast(Option, param).flag_value - - return None - - return super().get_default(ctx, call=call) - - def prompt_for_value(self, ctx: Context) -> t.Any: - """This is an alternative flow that can be activated in the full - value processing if a value does not exist. It will prompt the - user until a valid value exists and then returns the processed - value as result. - """ - assert self.prompt is not None - - # Calculate the default before prompting anything to be stable. - default = self.get_default(ctx) - - # If this is a prompt for a flag we need to handle this - # differently. - if self.is_bool_flag: - return confirm(self.prompt, default) - - return prompt( - self.prompt, - default=default, - type=self.type, - hide_input=self.hide_input, - show_choices=self.show_choices, - confirmation_prompt=self.confirmation_prompt, - value_proc=lambda x: self.process_value(ctx, x), - ) - - def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: - rv = super().resolve_envvar_value(ctx) - - if rv is not None: - return rv - - if ( - self.allow_from_autoenv - and ctx.auto_envvar_prefix is not None - and self.name is not None - ): - envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" - rv = os.environ.get(envvar) - - if rv: - return rv - - return None - - def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: - rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) - - if rv is None: - return None - - value_depth = (self.nargs != 1) + bool(self.multiple) - - if value_depth > 0: - rv = self.type.split_envvar_value(rv) - - if self.multiple and self.nargs != 1: - rv = batch(rv, self.nargs) - - return rv - - def consume_value( - self, ctx: Context, opts: t.Mapping[str, "Parameter"] - ) -> t.Tuple[t.Any, ParameterSource]: - value, source = super().consume_value(ctx, opts) - - # The parser will emit a sentinel value if the option can be - # given as a flag without a value. This is different from None - # to distinguish from the flag not being given at all. - if value is _flag_needs_value: - if self.prompt is not None and not ctx.resilient_parsing: - value = self.prompt_for_value(ctx) - source = ParameterSource.PROMPT - else: - value = self.flag_value - source = ParameterSource.COMMANDLINE - - elif ( - self.multiple - and value is not None - and any(v is _flag_needs_value for v in value) - ): - value = [self.flag_value if v is _flag_needs_value else v for v in value] - source = ParameterSource.COMMANDLINE - - # The value wasn't set, or used the param's default, prompt if - # prompting is enabled. - elif ( - source in {None, ParameterSource.DEFAULT} - and self.prompt is not None - and (self.required or self.prompt_required) - and not ctx.resilient_parsing - ): - value = self.prompt_for_value(ctx) - source = ParameterSource.PROMPT - - return value, source - - -class Argument(Parameter): - """Arguments are positional parameters to a command. They generally - provide fewer features than options but can have infinite ``nargs`` - and are required by default. - - All parameters are passed onwards to the constructor of :class:`Parameter`. - """ - - param_type_name = "argument" - - def __init__( - self, - param_decls: t.Sequence[str], - required: t.Optional[bool] = None, - **attrs: t.Any, - ) -> None: - if required is None: - if attrs.get("default") is not None: - required = False - else: - required = attrs.get("nargs", 1) > 0 - - if "multiple" in attrs: - raise TypeError("__init__() got an unexpected keyword argument 'multiple'.") - - super().__init__(param_decls, required=required, **attrs) - - if __debug__: - if self.default is not None and self.nargs == -1: - raise TypeError("'default' is not supported for nargs=-1.") - - @property - def human_readable_name(self) -> str: - if self.metavar is not None: - return self.metavar - return self.name.upper() # type: ignore - - def make_metavar(self) -> str: - if self.metavar is not None: - return self.metavar - var = self.type.get_metavar(self) - if not var: - var = self.name.upper() # type: ignore - if not self.required: - var = f"[{var}]" - if self.nargs != 1: - var += "..." - return var - - def _parse_decls( - self, decls: t.Sequence[str], expose_value: bool - ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: - if not decls: - if not expose_value: - return None, [], [] - raise TypeError("Could not determine name for argument") - if len(decls) == 1: - name = arg = decls[0] - name = name.replace("-", "_").lower() - else: - raise TypeError( - "Arguments take exactly one parameter declaration, got" - f" {len(decls)}." - ) - return name, [arg], [] - - def get_usage_pieces(self, ctx: Context) -> t.List[str]: - return [self.make_metavar()] - - def get_error_hint(self, ctx: Context) -> str: - return f"'{self.make_metavar()}'" - - def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: - parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/decorators.py b/plotter-app/venv/lib/python3.8/site-packages/click/decorators.py deleted file mode 100644 index d9bba95..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click/decorators.py +++ /dev/null @@ -1,561 +0,0 @@ -import inspect -import types -import typing as t -from functools import update_wrapper -from gettext import gettext as _ - -from .core import Argument -from .core import Command -from .core import Context -from .core import Group -from .core import Option -from .core import Parameter -from .globals import get_current_context -from .utils import echo - -if t.TYPE_CHECKING: - import typing_extensions as te - - P = te.ParamSpec("P") - -R = t.TypeVar("R") -T = t.TypeVar("T") -_AnyCallable = t.Callable[..., t.Any] -FC = t.TypeVar("FC", bound=t.Union[_AnyCallable, Command]) - - -def pass_context(f: "t.Callable[te.Concatenate[Context, P], R]") -> "t.Callable[P, R]": - """Marks a callback as wanting to receive the current context - object as first argument. - """ - - def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": - return f(get_current_context(), *args, **kwargs) - - return update_wrapper(new_func, f) - - -def pass_obj(f: "t.Callable[te.Concatenate[t.Any, P], R]") -> "t.Callable[P, R]": - """Similar to :func:`pass_context`, but only pass the object on the - context onwards (:attr:`Context.obj`). This is useful if that object - represents the state of a nested system. - """ - - def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": - return f(get_current_context().obj, *args, **kwargs) - - return update_wrapper(new_func, f) - - -def make_pass_decorator( - object_type: t.Type[T], ensure: bool = False -) -> t.Callable[["t.Callable[te.Concatenate[T, P], R]"], "t.Callable[P, R]"]: - """Given an object type this creates a decorator that will work - similar to :func:`pass_obj` but instead of passing the object of the - current context, it will find the innermost context of type - :func:`object_type`. - - This generates a decorator that works roughly like this:: - - from functools import update_wrapper - - def decorator(f): - @pass_context - def new_func(ctx, *args, **kwargs): - obj = ctx.find_object(object_type) - return ctx.invoke(f, obj, *args, **kwargs) - return update_wrapper(new_func, f) - return decorator - - :param object_type: the type of the object to pass. - :param ensure: if set to `True`, a new object will be created and - remembered on the context if it's not there yet. - """ - - def decorator(f: "t.Callable[te.Concatenate[T, P], R]") -> "t.Callable[P, R]": - def new_func(*args: "P.args", **kwargs: "P.kwargs") -> "R": - ctx = get_current_context() - - obj: t.Optional[T] - if ensure: - obj = ctx.ensure_object(object_type) - else: - obj = ctx.find_object(object_type) - - if obj is None: - raise RuntimeError( - "Managed to invoke callback without a context" - f" object of type {object_type.__name__!r}" - " existing." - ) - - return ctx.invoke(f, obj, *args, **kwargs) - - return update_wrapper(new_func, f) - - return decorator # type: ignore[return-value] - - -def pass_meta_key( - key: str, *, doc_description: t.Optional[str] = None -) -> "t.Callable[[t.Callable[te.Concatenate[t.Any, P], R]], t.Callable[P, R]]": - """Create a decorator that passes a key from - :attr:`click.Context.meta` as the first argument to the decorated - function. - - :param key: Key in ``Context.meta`` to pass. - :param doc_description: Description of the object being passed, - inserted into the decorator's docstring. Defaults to "the 'key' - key from Context.meta". - - .. versionadded:: 8.0 - """ - - def decorator(f: "t.Callable[te.Concatenate[t.Any, P], R]") -> "t.Callable[P, R]": - def new_func(*args: "P.args", **kwargs: "P.kwargs") -> R: - ctx = get_current_context() - obj = ctx.meta[key] - return ctx.invoke(f, obj, *args, **kwargs) - - return update_wrapper(new_func, f) - - if doc_description is None: - doc_description = f"the {key!r} key from :attr:`click.Context.meta`" - - decorator.__doc__ = ( - f"Decorator that passes {doc_description} as the first argument" - " to the decorated function." - ) - return decorator # type: ignore[return-value] - - -CmdType = t.TypeVar("CmdType", bound=Command) - - -# variant: no call, directly as decorator for a function. -@t.overload -def command(name: _AnyCallable) -> Command: - ... - - -# variant: with positional name and with positional or keyword cls argument: -# @command(namearg, CommandCls, ...) or @command(namearg, cls=CommandCls, ...) -@t.overload -def command( - name: t.Optional[str], - cls: t.Type[CmdType], - **attrs: t.Any, -) -> t.Callable[[_AnyCallable], CmdType]: - ... - - -# variant: name omitted, cls _must_ be a keyword argument, @command(cls=CommandCls, ...) -@t.overload -def command( - name: None = None, - *, - cls: t.Type[CmdType], - **attrs: t.Any, -) -> t.Callable[[_AnyCallable], CmdType]: - ... - - -# variant: with optional string name, no cls argument provided. -@t.overload -def command( - name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any -) -> t.Callable[[_AnyCallable], Command]: - ... - - -def command( - name: t.Union[t.Optional[str], _AnyCallable] = None, - cls: t.Optional[t.Type[CmdType]] = None, - **attrs: t.Any, -) -> t.Union[Command, t.Callable[[_AnyCallable], t.Union[Command, CmdType]]]: - r"""Creates a new :class:`Command` and uses the decorated function as - callback. This will also automatically attach all decorated - :func:`option`\s and :func:`argument`\s as parameters to the command. - - The name of the command defaults to the name of the function with - underscores replaced by dashes. If you want to change that, you can - pass the intended name as the first argument. - - All keyword arguments are forwarded to the underlying command class. - For the ``params`` argument, any decorated params are appended to - the end of the list. - - Once decorated the function turns into a :class:`Command` instance - that can be invoked as a command line utility or be attached to a - command :class:`Group`. - - :param name: the name of the command. This defaults to the function - name with underscores replaced by dashes. - :param cls: the command class to instantiate. This defaults to - :class:`Command`. - - .. versionchanged:: 8.1 - This decorator can be applied without parentheses. - - .. versionchanged:: 8.1 - The ``params`` argument can be used. Decorated params are - appended to the end of the list. - """ - - func: t.Optional[t.Callable[[_AnyCallable], t.Any]] = None - - if callable(name): - func = name - name = None - assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class." - assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments." - - if cls is None: - cls = t.cast(t.Type[CmdType], Command) - - def decorator(f: _AnyCallable) -> CmdType: - if isinstance(f, Command): - raise TypeError("Attempted to convert a callback into a command twice.") - - attr_params = attrs.pop("params", None) - params = attr_params if attr_params is not None else [] - - try: - decorator_params = f.__click_params__ # type: ignore - except AttributeError: - pass - else: - del f.__click_params__ # type: ignore - params.extend(reversed(decorator_params)) - - if attrs.get("help") is None: - attrs["help"] = f.__doc__ - - if t.TYPE_CHECKING: - assert cls is not None - assert not callable(name) - - cmd = cls( - name=name or f.__name__.lower().replace("_", "-"), - callback=f, - params=params, - **attrs, - ) - cmd.__doc__ = f.__doc__ - return cmd - - if func is not None: - return decorator(func) - - return decorator - - -GrpType = t.TypeVar("GrpType", bound=Group) - - -# variant: no call, directly as decorator for a function. -@t.overload -def group(name: _AnyCallable) -> Group: - ... - - -# variant: with positional name and with positional or keyword cls argument: -# @group(namearg, GroupCls, ...) or @group(namearg, cls=GroupCls, ...) -@t.overload -def group( - name: t.Optional[str], - cls: t.Type[GrpType], - **attrs: t.Any, -) -> t.Callable[[_AnyCallable], GrpType]: - ... - - -# variant: name omitted, cls _must_ be a keyword argument, @group(cmd=GroupCls, ...) -@t.overload -def group( - name: None = None, - *, - cls: t.Type[GrpType], - **attrs: t.Any, -) -> t.Callable[[_AnyCallable], GrpType]: - ... - - -# variant: with optional string name, no cls argument provided. -@t.overload -def group( - name: t.Optional[str] = ..., cls: None = None, **attrs: t.Any -) -> t.Callable[[_AnyCallable], Group]: - ... - - -def group( - name: t.Union[str, _AnyCallable, None] = None, - cls: t.Optional[t.Type[GrpType]] = None, - **attrs: t.Any, -) -> t.Union[Group, t.Callable[[_AnyCallable], t.Union[Group, GrpType]]]: - """Creates a new :class:`Group` with a function as callback. This - works otherwise the same as :func:`command` just that the `cls` - parameter is set to :class:`Group`. - - .. versionchanged:: 8.1 - This decorator can be applied without parentheses. - """ - if cls is None: - cls = t.cast(t.Type[GrpType], Group) - - if callable(name): - return command(cls=cls, **attrs)(name) - - return command(name, cls, **attrs) - - -def _param_memo(f: t.Callable[..., t.Any], param: Parameter) -> None: - if isinstance(f, Command): - f.params.append(param) - else: - if not hasattr(f, "__click_params__"): - f.__click_params__ = [] # type: ignore - - f.__click_params__.append(param) # type: ignore - - -def argument( - *param_decls: str, cls: t.Optional[t.Type[Argument]] = None, **attrs: t.Any -) -> t.Callable[[FC], FC]: - """Attaches an argument to the command. All positional arguments are - passed as parameter declarations to :class:`Argument`; all keyword - arguments are forwarded unchanged (except ``cls``). - This is equivalent to creating an :class:`Argument` instance manually - and attaching it to the :attr:`Command.params` list. - - For the default argument class, refer to :class:`Argument` and - :class:`Parameter` for descriptions of parameters. - - :param cls: the argument class to instantiate. This defaults to - :class:`Argument`. - :param param_decls: Passed as positional arguments to the constructor of - ``cls``. - :param attrs: Passed as keyword arguments to the constructor of ``cls``. - """ - if cls is None: - cls = Argument - - def decorator(f: FC) -> FC: - _param_memo(f, cls(param_decls, **attrs)) - return f - - return decorator - - -def option( - *param_decls: str, cls: t.Optional[t.Type[Option]] = None, **attrs: t.Any -) -> t.Callable[[FC], FC]: - """Attaches an option to the command. All positional arguments are - passed as parameter declarations to :class:`Option`; all keyword - arguments are forwarded unchanged (except ``cls``). - This is equivalent to creating an :class:`Option` instance manually - and attaching it to the :attr:`Command.params` list. - - For the default option class, refer to :class:`Option` and - :class:`Parameter` for descriptions of parameters. - - :param cls: the option class to instantiate. This defaults to - :class:`Option`. - :param param_decls: Passed as positional arguments to the constructor of - ``cls``. - :param attrs: Passed as keyword arguments to the constructor of ``cls``. - """ - if cls is None: - cls = Option - - def decorator(f: FC) -> FC: - _param_memo(f, cls(param_decls, **attrs)) - return f - - return decorator - - -def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: - """Add a ``--yes`` option which shows a prompt before continuing if - not passed. If the prompt is declined, the program will exit. - - :param param_decls: One or more option names. Defaults to the single - value ``"--yes"``. - :param kwargs: Extra arguments are passed to :func:`option`. - """ - - def callback(ctx: Context, param: Parameter, value: bool) -> None: - if not value: - ctx.abort() - - if not param_decls: - param_decls = ("--yes",) - - kwargs.setdefault("is_flag", True) - kwargs.setdefault("callback", callback) - kwargs.setdefault("expose_value", False) - kwargs.setdefault("prompt", "Do you want to continue?") - kwargs.setdefault("help", "Confirm the action without prompting.") - return option(*param_decls, **kwargs) - - -def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: - """Add a ``--password`` option which prompts for a password, hiding - input and asking to enter the value again for confirmation. - - :param param_decls: One or more option names. Defaults to the single - value ``"--password"``. - :param kwargs: Extra arguments are passed to :func:`option`. - """ - if not param_decls: - param_decls = ("--password",) - - kwargs.setdefault("prompt", True) - kwargs.setdefault("confirmation_prompt", True) - kwargs.setdefault("hide_input", True) - return option(*param_decls, **kwargs) - - -def version_option( - version: t.Optional[str] = None, - *param_decls: str, - package_name: t.Optional[str] = None, - prog_name: t.Optional[str] = None, - message: t.Optional[str] = None, - **kwargs: t.Any, -) -> t.Callable[[FC], FC]: - """Add a ``--version`` option which immediately prints the version - number and exits the program. - - If ``version`` is not provided, Click will try to detect it using - :func:`importlib.metadata.version` to get the version for the - ``package_name``. On Python < 3.8, the ``importlib_metadata`` - backport must be installed. - - If ``package_name`` is not provided, Click will try to detect it by - inspecting the stack frames. This will be used to detect the - version, so it must match the name of the installed package. - - :param version: The version number to show. If not provided, Click - will try to detect it. - :param param_decls: One or more option names. Defaults to the single - value ``"--version"``. - :param package_name: The package name to detect the version from. If - not provided, Click will try to detect it. - :param prog_name: The name of the CLI to show in the message. If not - provided, it will be detected from the command. - :param message: The message to show. The values ``%(prog)s``, - ``%(package)s``, and ``%(version)s`` are available. Defaults to - ``"%(prog)s, version %(version)s"``. - :param kwargs: Extra arguments are passed to :func:`option`. - :raise RuntimeError: ``version`` could not be detected. - - .. versionchanged:: 8.0 - Add the ``package_name`` parameter, and the ``%(package)s`` - value for messages. - - .. versionchanged:: 8.0 - Use :mod:`importlib.metadata` instead of ``pkg_resources``. The - version is detected based on the package name, not the entry - point name. The Python package name must match the installed - package name, or be passed with ``package_name=``. - """ - if message is None: - message = _("%(prog)s, version %(version)s") - - if version is None and package_name is None: - frame = inspect.currentframe() - f_back = frame.f_back if frame is not None else None - f_globals = f_back.f_globals if f_back is not None else None - # break reference cycle - # https://docs.python.org/3/library/inspect.html#the-interpreter-stack - del frame - - if f_globals is not None: - package_name = f_globals.get("__name__") - - if package_name == "__main__": - package_name = f_globals.get("__package__") - - if package_name: - package_name = package_name.partition(".")[0] - - def callback(ctx: Context, param: Parameter, value: bool) -> None: - if not value or ctx.resilient_parsing: - return - - nonlocal prog_name - nonlocal version - - if prog_name is None: - prog_name = ctx.find_root().info_name - - if version is None and package_name is not None: - metadata: t.Optional[types.ModuleType] - - try: - from importlib import metadata # type: ignore - except ImportError: - # Python < 3.8 - import importlib_metadata as metadata # type: ignore - - try: - version = metadata.version(package_name) # type: ignore - except metadata.PackageNotFoundError: # type: ignore - raise RuntimeError( - f"{package_name!r} is not installed. Try passing" - " 'package_name' instead." - ) from None - - if version is None: - raise RuntimeError( - f"Could not determine the version for {package_name!r} automatically." - ) - - echo( - message % {"prog": prog_name, "package": package_name, "version": version}, - color=ctx.color, - ) - ctx.exit() - - if not param_decls: - param_decls = ("--version",) - - kwargs.setdefault("is_flag", True) - kwargs.setdefault("expose_value", False) - kwargs.setdefault("is_eager", True) - kwargs.setdefault("help", _("Show the version and exit.")) - kwargs["callback"] = callback - return option(*param_decls, **kwargs) - - -def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: - """Add a ``--help`` option which immediately prints the help page - and exits the program. - - This is usually unnecessary, as the ``--help`` option is added to - each command automatically unless ``add_help_option=False`` is - passed. - - :param param_decls: One or more option names. Defaults to the single - value ``"--help"``. - :param kwargs: Extra arguments are passed to :func:`option`. - """ - - def callback(ctx: Context, param: Parameter, value: bool) -> None: - if not value or ctx.resilient_parsing: - return - - echo(ctx.get_help(), color=ctx.color) - ctx.exit() - - if not param_decls: - param_decls = ("--help",) - - kwargs.setdefault("is_flag", True) - kwargs.setdefault("expose_value", False) - kwargs.setdefault("is_eager", True) - kwargs.setdefault("help", _("Show this message and exit.")) - kwargs["callback"] = callback - return option(*param_decls, **kwargs) diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/exceptions.py b/plotter-app/venv/lib/python3.8/site-packages/click/exceptions.py deleted file mode 100644 index fe68a36..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click/exceptions.py +++ /dev/null @@ -1,288 +0,0 @@ -import typing as t -from gettext import gettext as _ -from gettext import ngettext - -from ._compat import get_text_stderr -from .utils import echo -from .utils import format_filename - -if t.TYPE_CHECKING: - from .core import Command - from .core import Context - from .core import Parameter - - -def _join_param_hints( - param_hint: t.Optional[t.Union[t.Sequence[str], str]] -) -> t.Optional[str]: - if param_hint is not None and not isinstance(param_hint, str): - return " / ".join(repr(x) for x in param_hint) - - return param_hint - - -class ClickException(Exception): - """An exception that Click can handle and show to the user.""" - - #: The exit code for this exception. - exit_code = 1 - - def __init__(self, message: str) -> None: - super().__init__(message) - self.message = message - - def format_message(self) -> str: - return self.message - - def __str__(self) -> str: - return self.message - - def show(self, file: t.Optional[t.IO[t.Any]] = None) -> None: - if file is None: - file = get_text_stderr() - - echo(_("Error: {message}").format(message=self.format_message()), file=file) - - -class UsageError(ClickException): - """An internal exception that signals a usage error. This typically - aborts any further handling. - - :param message: the error message to display. - :param ctx: optionally the context that caused this error. Click will - fill in the context automatically in some situations. - """ - - exit_code = 2 - - def __init__(self, message: str, ctx: t.Optional["Context"] = None) -> None: - super().__init__(message) - self.ctx = ctx - self.cmd: t.Optional["Command"] = self.ctx.command if self.ctx else None - - def show(self, file: t.Optional[t.IO[t.Any]] = None) -> None: - if file is None: - file = get_text_stderr() - color = None - hint = "" - if ( - self.ctx is not None - and self.ctx.command.get_help_option(self.ctx) is not None - ): - hint = _("Try '{command} {option}' for help.").format( - command=self.ctx.command_path, option=self.ctx.help_option_names[0] - ) - hint = f"{hint}\n" - if self.ctx is not None: - color = self.ctx.color - echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color) - echo( - _("Error: {message}").format(message=self.format_message()), - file=file, - color=color, - ) - - -class BadParameter(UsageError): - """An exception that formats out a standardized error message for a - bad parameter. This is useful when thrown from a callback or type as - Click will attach contextual information to it (for instance, which - parameter it is). - - .. versionadded:: 2.0 - - :param param: the parameter object that caused this error. This can - be left out, and Click will attach this info itself - if possible. - :param param_hint: a string that shows up as parameter name. This - can be used as alternative to `param` in cases - where custom validation should happen. If it is - a string it's used as such, if it's a list then - each item is quoted and separated. - """ - - def __init__( - self, - message: str, - ctx: t.Optional["Context"] = None, - param: t.Optional["Parameter"] = None, - param_hint: t.Optional[str] = None, - ) -> None: - super().__init__(message, ctx) - self.param = param - self.param_hint = param_hint - - def format_message(self) -> str: - if self.param_hint is not None: - param_hint = self.param_hint - elif self.param is not None: - param_hint = self.param.get_error_hint(self.ctx) # type: ignore - else: - return _("Invalid value: {message}").format(message=self.message) - - return _("Invalid value for {param_hint}: {message}").format( - param_hint=_join_param_hints(param_hint), message=self.message - ) - - -class MissingParameter(BadParameter): - """Raised if click required an option or argument but it was not - provided when invoking the script. - - .. versionadded:: 4.0 - - :param param_type: a string that indicates the type of the parameter. - The default is to inherit the parameter type from - the given `param`. Valid values are ``'parameter'``, - ``'option'`` or ``'argument'``. - """ - - def __init__( - self, - message: t.Optional[str] = None, - ctx: t.Optional["Context"] = None, - param: t.Optional["Parameter"] = None, - param_hint: t.Optional[str] = None, - param_type: t.Optional[str] = None, - ) -> None: - super().__init__(message or "", ctx, param, param_hint) - self.param_type = param_type - - def format_message(self) -> str: - if self.param_hint is not None: - param_hint: t.Optional[str] = self.param_hint - elif self.param is not None: - param_hint = self.param.get_error_hint(self.ctx) # type: ignore - else: - param_hint = None - - param_hint = _join_param_hints(param_hint) - param_hint = f" {param_hint}" if param_hint else "" - - param_type = self.param_type - if param_type is None and self.param is not None: - param_type = self.param.param_type_name - - msg = self.message - if self.param is not None: - msg_extra = self.param.type.get_missing_message(self.param) - if msg_extra: - if msg: - msg += f". {msg_extra}" - else: - msg = msg_extra - - msg = f" {msg}" if msg else "" - - # Translate param_type for known types. - if param_type == "argument": - missing = _("Missing argument") - elif param_type == "option": - missing = _("Missing option") - elif param_type == "parameter": - missing = _("Missing parameter") - else: - missing = _("Missing {param_type}").format(param_type=param_type) - - return f"{missing}{param_hint}.{msg}" - - def __str__(self) -> str: - if not self.message: - param_name = self.param.name if self.param else None - return _("Missing parameter: {param_name}").format(param_name=param_name) - else: - return self.message - - -class NoSuchOption(UsageError): - """Raised if click attempted to handle an option that does not - exist. - - .. versionadded:: 4.0 - """ - - def __init__( - self, - option_name: str, - message: t.Optional[str] = None, - possibilities: t.Optional[t.Sequence[str]] = None, - ctx: t.Optional["Context"] = None, - ) -> None: - if message is None: - message = _("No such option: {name}").format(name=option_name) - - super().__init__(message, ctx) - self.option_name = option_name - self.possibilities = possibilities - - def format_message(self) -> str: - if not self.possibilities: - return self.message - - possibility_str = ", ".join(sorted(self.possibilities)) - suggest = ngettext( - "Did you mean {possibility}?", - "(Possible options: {possibilities})", - len(self.possibilities), - ).format(possibility=possibility_str, possibilities=possibility_str) - return f"{self.message} {suggest}" - - -class BadOptionUsage(UsageError): - """Raised if an option is generally supplied but the use of the option - was incorrect. This is for instance raised if the number of arguments - for an option is not correct. - - .. versionadded:: 4.0 - - :param option_name: the name of the option being used incorrectly. - """ - - def __init__( - self, option_name: str, message: str, ctx: t.Optional["Context"] = None - ) -> None: - super().__init__(message, ctx) - self.option_name = option_name - - -class BadArgumentUsage(UsageError): - """Raised if an argument is generally supplied but the use of the argument - was incorrect. This is for instance raised if the number of values - for an argument is not correct. - - .. versionadded:: 6.0 - """ - - -class FileError(ClickException): - """Raised if a file cannot be opened.""" - - def __init__(self, filename: str, hint: t.Optional[str] = None) -> None: - if hint is None: - hint = _("unknown error") - - super().__init__(hint) - self.ui_filename: str = format_filename(filename) - self.filename = filename - - def format_message(self) -> str: - return _("Could not open file {filename!r}: {message}").format( - filename=self.ui_filename, message=self.message - ) - - -class Abort(RuntimeError): - """An internal signalling exception that signals Click to abort.""" - - -class Exit(RuntimeError): - """An exception that indicates that the application should exit with some - status code. - - :param code: the status code to exit with. - """ - - __slots__ = ("exit_code",) - - def __init__(self, code: int = 0) -> None: - self.exit_code: int = code diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/formatting.py b/plotter-app/venv/lib/python3.8/site-packages/click/formatting.py deleted file mode 100644 index ddd2a2f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click/formatting.py +++ /dev/null @@ -1,301 +0,0 @@ -import typing as t -from contextlib import contextmanager -from gettext import gettext as _ - -from ._compat import term_len -from .parser import split_opt - -# Can force a width. This is used by the test system -FORCED_WIDTH: t.Optional[int] = None - - -def measure_table(rows: t.Iterable[t.Tuple[str, str]]) -> t.Tuple[int, ...]: - widths: t.Dict[int, int] = {} - - for row in rows: - for idx, col in enumerate(row): - widths[idx] = max(widths.get(idx, 0), term_len(col)) - - return tuple(y for x, y in sorted(widths.items())) - - -def iter_rows( - rows: t.Iterable[t.Tuple[str, str]], col_count: int -) -> t.Iterator[t.Tuple[str, ...]]: - for row in rows: - yield row + ("",) * (col_count - len(row)) - - -def wrap_text( - text: str, - width: int = 78, - initial_indent: str = "", - subsequent_indent: str = "", - preserve_paragraphs: bool = False, -) -> str: - """A helper function that intelligently wraps text. By default, it - assumes that it operates on a single paragraph of text but if the - `preserve_paragraphs` parameter is provided it will intelligently - handle paragraphs (defined by two empty lines). - - If paragraphs are handled, a paragraph can be prefixed with an empty - line containing the ``\\b`` character (``\\x08``) to indicate that - no rewrapping should happen in that block. - - :param text: the text that should be rewrapped. - :param width: the maximum width for the text. - :param initial_indent: the initial indent that should be placed on the - first line as a string. - :param subsequent_indent: the indent string that should be placed on - each consecutive line. - :param preserve_paragraphs: if this flag is set then the wrapping will - intelligently handle paragraphs. - """ - from ._textwrap import TextWrapper - - text = text.expandtabs() - wrapper = TextWrapper( - width, - initial_indent=initial_indent, - subsequent_indent=subsequent_indent, - replace_whitespace=False, - ) - if not preserve_paragraphs: - return wrapper.fill(text) - - p: t.List[t.Tuple[int, bool, str]] = [] - buf: t.List[str] = [] - indent = None - - def _flush_par() -> None: - if not buf: - return - if buf[0].strip() == "\b": - p.append((indent or 0, True, "\n".join(buf[1:]))) - else: - p.append((indent or 0, False, " ".join(buf))) - del buf[:] - - for line in text.splitlines(): - if not line: - _flush_par() - indent = None - else: - if indent is None: - orig_len = term_len(line) - line = line.lstrip() - indent = orig_len - term_len(line) - buf.append(line) - _flush_par() - - rv = [] - for indent, raw, text in p: - with wrapper.extra_indent(" " * indent): - if raw: - rv.append(wrapper.indent_only(text)) - else: - rv.append(wrapper.fill(text)) - - return "\n\n".join(rv) - - -class HelpFormatter: - """This class helps with formatting text-based help pages. It's - usually just needed for very special internal cases, but it's also - exposed so that developers can write their own fancy outputs. - - At present, it always writes into memory. - - :param indent_increment: the additional increment for each level. - :param width: the width for the text. This defaults to the terminal - width clamped to a maximum of 78. - """ - - def __init__( - self, - indent_increment: int = 2, - width: t.Optional[int] = None, - max_width: t.Optional[int] = None, - ) -> None: - import shutil - - self.indent_increment = indent_increment - if max_width is None: - max_width = 80 - if width is None: - width = FORCED_WIDTH - if width is None: - width = max(min(shutil.get_terminal_size().columns, max_width) - 2, 50) - self.width = width - self.current_indent = 0 - self.buffer: t.List[str] = [] - - def write(self, string: str) -> None: - """Writes a unicode string into the internal buffer.""" - self.buffer.append(string) - - def indent(self) -> None: - """Increases the indentation.""" - self.current_indent += self.indent_increment - - def dedent(self) -> None: - """Decreases the indentation.""" - self.current_indent -= self.indent_increment - - def write_usage( - self, prog: str, args: str = "", prefix: t.Optional[str] = None - ) -> None: - """Writes a usage line into the buffer. - - :param prog: the program name. - :param args: whitespace separated list of arguments. - :param prefix: The prefix for the first line. Defaults to - ``"Usage: "``. - """ - if prefix is None: - prefix = f"{_('Usage:')} " - - usage_prefix = f"{prefix:>{self.current_indent}}{prog} " - text_width = self.width - self.current_indent - - if text_width >= (term_len(usage_prefix) + 20): - # The arguments will fit to the right of the prefix. - indent = " " * term_len(usage_prefix) - self.write( - wrap_text( - args, - text_width, - initial_indent=usage_prefix, - subsequent_indent=indent, - ) - ) - else: - # The prefix is too long, put the arguments on the next line. - self.write(usage_prefix) - self.write("\n") - indent = " " * (max(self.current_indent, term_len(prefix)) + 4) - self.write( - wrap_text( - args, text_width, initial_indent=indent, subsequent_indent=indent - ) - ) - - self.write("\n") - - def write_heading(self, heading: str) -> None: - """Writes a heading into the buffer.""" - self.write(f"{'':>{self.current_indent}}{heading}:\n") - - def write_paragraph(self) -> None: - """Writes a paragraph into the buffer.""" - if self.buffer: - self.write("\n") - - def write_text(self, text: str) -> None: - """Writes re-indented text into the buffer. This rewraps and - preserves paragraphs. - """ - indent = " " * self.current_indent - self.write( - wrap_text( - text, - self.width, - initial_indent=indent, - subsequent_indent=indent, - preserve_paragraphs=True, - ) - ) - self.write("\n") - - def write_dl( - self, - rows: t.Sequence[t.Tuple[str, str]], - col_max: int = 30, - col_spacing: int = 2, - ) -> None: - """Writes a definition list into the buffer. This is how options - and commands are usually formatted. - - :param rows: a list of two item tuples for the terms and values. - :param col_max: the maximum width of the first column. - :param col_spacing: the number of spaces between the first and - second column. - """ - rows = list(rows) - widths = measure_table(rows) - if len(widths) != 2: - raise TypeError("Expected two columns for definition list") - - first_col = min(widths[0], col_max) + col_spacing - - for first, second in iter_rows(rows, len(widths)): - self.write(f"{'':>{self.current_indent}}{first}") - if not second: - self.write("\n") - continue - if term_len(first) <= first_col - col_spacing: - self.write(" " * (first_col - term_len(first))) - else: - self.write("\n") - self.write(" " * (first_col + self.current_indent)) - - text_width = max(self.width - first_col - 2, 10) - wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True) - lines = wrapped_text.splitlines() - - if lines: - self.write(f"{lines[0]}\n") - - for line in lines[1:]: - self.write(f"{'':>{first_col + self.current_indent}}{line}\n") - else: - self.write("\n") - - @contextmanager - def section(self, name: str) -> t.Iterator[None]: - """Helpful context manager that writes a paragraph, a heading, - and the indents. - - :param name: the section name that is written as heading. - """ - self.write_paragraph() - self.write_heading(name) - self.indent() - try: - yield - finally: - self.dedent() - - @contextmanager - def indentation(self) -> t.Iterator[None]: - """A context manager that increases the indentation.""" - self.indent() - try: - yield - finally: - self.dedent() - - def getvalue(self) -> str: - """Returns the buffer contents.""" - return "".join(self.buffer) - - -def join_options(options: t.Sequence[str]) -> t.Tuple[str, bool]: - """Given a list of option strings this joins them in the most appropriate - way and returns them in the form ``(formatted_string, - any_prefix_is_slash)`` where the second item in the tuple is a flag that - indicates if any of the option prefixes was a slash. - """ - rv = [] - any_prefix_is_slash = False - - for opt in options: - prefix = split_opt(opt)[0] - - if prefix == "/": - any_prefix_is_slash = True - - rv.append((len(prefix), opt)) - - rv.sort(key=lambda x: x[0]) - return ", ".join(x[1] for x in rv), any_prefix_is_slash diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/globals.py b/plotter-app/venv/lib/python3.8/site-packages/click/globals.py deleted file mode 100644 index 480058f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click/globals.py +++ /dev/null @@ -1,68 +0,0 @@ -import typing as t -from threading import local - -if t.TYPE_CHECKING: - import typing_extensions as te - from .core import Context - -_local = local() - - -@t.overload -def get_current_context(silent: "te.Literal[False]" = False) -> "Context": - ... - - -@t.overload -def get_current_context(silent: bool = ...) -> t.Optional["Context"]: - ... - - -def get_current_context(silent: bool = False) -> t.Optional["Context"]: - """Returns the current click context. This can be used as a way to - access the current context object from anywhere. This is a more implicit - alternative to the :func:`pass_context` decorator. This function is - primarily useful for helpers such as :func:`echo` which might be - interested in changing its behavior based on the current context. - - To push the current context, :meth:`Context.scope` can be used. - - .. versionadded:: 5.0 - - :param silent: if set to `True` the return value is `None` if no context - is available. The default behavior is to raise a - :exc:`RuntimeError`. - """ - try: - return t.cast("Context", _local.stack[-1]) - except (AttributeError, IndexError) as e: - if not silent: - raise RuntimeError("There is no active click context.") from e - - return None - - -def push_context(ctx: "Context") -> None: - """Pushes a new context to the current stack.""" - _local.__dict__.setdefault("stack", []).append(ctx) - - -def pop_context() -> None: - """Removes the top level from the stack.""" - _local.stack.pop() - - -def resolve_color_default(color: t.Optional[bool] = None) -> t.Optional[bool]: - """Internal helper to get the default value of the color flag. If a - value is passed it's returned unchanged, otherwise it's looked up from - the current context. - """ - if color is not None: - return color - - ctx = get_current_context(silent=True) - - if ctx is not None: - return ctx.color - - return None diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/parser.py b/plotter-app/venv/lib/python3.8/site-packages/click/parser.py deleted file mode 100644 index 5fa7adf..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click/parser.py +++ /dev/null @@ -1,529 +0,0 @@ -""" -This module started out as largely a copy paste from the stdlib's -optparse module with the features removed that we do not need from -optparse because we implement them in Click on a higher level (for -instance type handling, help formatting and a lot more). - -The plan is to remove more and more from here over time. - -The reason this is a different module and not optparse from the stdlib -is that there are differences in 2.x and 3.x about the error messages -generated and optparse in the stdlib uses gettext for no good reason -and might cause us issues. - -Click uses parts of optparse written by Gregory P. Ward and maintained -by the Python Software Foundation. This is limited to code in parser.py. - -Copyright 2001-2006 Gregory P. Ward. All rights reserved. -Copyright 2002-2006 Python Software Foundation. All rights reserved. -""" -# This code uses parts of optparse written by Gregory P. Ward and -# maintained by the Python Software Foundation. -# Copyright 2001-2006 Gregory P. Ward -# Copyright 2002-2006 Python Software Foundation -import typing as t -from collections import deque -from gettext import gettext as _ -from gettext import ngettext - -from .exceptions import BadArgumentUsage -from .exceptions import BadOptionUsage -from .exceptions import NoSuchOption -from .exceptions import UsageError - -if t.TYPE_CHECKING: - import typing_extensions as te - from .core import Argument as CoreArgument - from .core import Context - from .core import Option as CoreOption - from .core import Parameter as CoreParameter - -V = t.TypeVar("V") - -# Sentinel value that indicates an option was passed as a flag without a -# value but is not a flag option. Option.consume_value uses this to -# prompt or use the flag_value. -_flag_needs_value = object() - - -def _unpack_args( - args: t.Sequence[str], nargs_spec: t.Sequence[int] -) -> t.Tuple[t.Sequence[t.Union[str, t.Sequence[t.Optional[str]], None]], t.List[str]]: - """Given an iterable of arguments and an iterable of nargs specifications, - it returns a tuple with all the unpacked arguments at the first index - and all remaining arguments as the second. - - The nargs specification is the number of arguments that should be consumed - or `-1` to indicate that this position should eat up all the remainders. - - Missing items are filled with `None`. - """ - args = deque(args) - nargs_spec = deque(nargs_spec) - rv: t.List[t.Union[str, t.Tuple[t.Optional[str], ...], None]] = [] - spos: t.Optional[int] = None - - def _fetch(c: "te.Deque[V]") -> t.Optional[V]: - try: - if spos is None: - return c.popleft() - else: - return c.pop() - except IndexError: - return None - - while nargs_spec: - nargs = _fetch(nargs_spec) - - if nargs is None: - continue - - if nargs == 1: - rv.append(_fetch(args)) - elif nargs > 1: - x = [_fetch(args) for _ in range(nargs)] - - # If we're reversed, we're pulling in the arguments in reverse, - # so we need to turn them around. - if spos is not None: - x.reverse() - - rv.append(tuple(x)) - elif nargs < 0: - if spos is not None: - raise TypeError("Cannot have two nargs < 0") - - spos = len(rv) - rv.append(None) - - # spos is the position of the wildcard (star). If it's not `None`, - # we fill it with the remainder. - if spos is not None: - rv[spos] = tuple(args) - args = [] - rv[spos + 1 :] = reversed(rv[spos + 1 :]) - - return tuple(rv), list(args) - - -def split_opt(opt: str) -> t.Tuple[str, str]: - first = opt[:1] - if first.isalnum(): - return "", opt - if opt[1:2] == first: - return opt[:2], opt[2:] - return first, opt[1:] - - -def normalize_opt(opt: str, ctx: t.Optional["Context"]) -> str: - if ctx is None or ctx.token_normalize_func is None: - return opt - prefix, opt = split_opt(opt) - return f"{prefix}{ctx.token_normalize_func(opt)}" - - -def split_arg_string(string: str) -> t.List[str]: - """Split an argument string as with :func:`shlex.split`, but don't - fail if the string is incomplete. Ignores a missing closing quote or - incomplete escape sequence and uses the partial token as-is. - - .. code-block:: python - - split_arg_string("example 'my file") - ["example", "my file"] - - split_arg_string("example my\\") - ["example", "my"] - - :param string: String to split. - """ - import shlex - - lex = shlex.shlex(string, posix=True) - lex.whitespace_split = True - lex.commenters = "" - out = [] - - try: - for token in lex: - out.append(token) - except ValueError: - # Raised when end-of-string is reached in an invalid state. Use - # the partial token as-is. The quote or escape character is in - # lex.state, not lex.token. - out.append(lex.token) - - return out - - -class Option: - def __init__( - self, - obj: "CoreOption", - opts: t.Sequence[str], - dest: t.Optional[str], - action: t.Optional[str] = None, - nargs: int = 1, - const: t.Optional[t.Any] = None, - ): - self._short_opts = [] - self._long_opts = [] - self.prefixes: t.Set[str] = set() - - for opt in opts: - prefix, value = split_opt(opt) - if not prefix: - raise ValueError(f"Invalid start character for option ({opt})") - self.prefixes.add(prefix[0]) - if len(prefix) == 1 and len(value) == 1: - self._short_opts.append(opt) - else: - self._long_opts.append(opt) - self.prefixes.add(prefix) - - if action is None: - action = "store" - - self.dest = dest - self.action = action - self.nargs = nargs - self.const = const - self.obj = obj - - @property - def takes_value(self) -> bool: - return self.action in ("store", "append") - - def process(self, value: t.Any, state: "ParsingState") -> None: - if self.action == "store": - state.opts[self.dest] = value # type: ignore - elif self.action == "store_const": - state.opts[self.dest] = self.const # type: ignore - elif self.action == "append": - state.opts.setdefault(self.dest, []).append(value) # type: ignore - elif self.action == "append_const": - state.opts.setdefault(self.dest, []).append(self.const) # type: ignore - elif self.action == "count": - state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore - else: - raise ValueError(f"unknown action '{self.action}'") - state.order.append(self.obj) - - -class Argument: - def __init__(self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1): - self.dest = dest - self.nargs = nargs - self.obj = obj - - def process( - self, - value: t.Union[t.Optional[str], t.Sequence[t.Optional[str]]], - state: "ParsingState", - ) -> None: - if self.nargs > 1: - assert value is not None - holes = sum(1 for x in value if x is None) - if holes == len(value): - value = None - elif holes != 0: - raise BadArgumentUsage( - _("Argument {name!r} takes {nargs} values.").format( - name=self.dest, nargs=self.nargs - ) - ) - - if self.nargs == -1 and self.obj.envvar is not None and value == (): - # Replace empty tuple with None so that a value from the - # environment may be tried. - value = None - - state.opts[self.dest] = value # type: ignore - state.order.append(self.obj) - - -class ParsingState: - def __init__(self, rargs: t.List[str]) -> None: - self.opts: t.Dict[str, t.Any] = {} - self.largs: t.List[str] = [] - self.rargs = rargs - self.order: t.List["CoreParameter"] = [] - - -class OptionParser: - """The option parser is an internal class that is ultimately used to - parse options and arguments. It's modelled after optparse and brings - a similar but vastly simplified API. It should generally not be used - directly as the high level Click classes wrap it for you. - - It's not nearly as extensible as optparse or argparse as it does not - implement features that are implemented on a higher level (such as - types or defaults). - - :param ctx: optionally the :class:`~click.Context` where this parser - should go with. - """ - - def __init__(self, ctx: t.Optional["Context"] = None) -> None: - #: The :class:`~click.Context` for this parser. This might be - #: `None` for some advanced use cases. - self.ctx = ctx - #: This controls how the parser deals with interspersed arguments. - #: If this is set to `False`, the parser will stop on the first - #: non-option. Click uses this to implement nested subcommands - #: safely. - self.allow_interspersed_args: bool = True - #: This tells the parser how to deal with unknown options. By - #: default it will error out (which is sensible), but there is a - #: second mode where it will ignore it and continue processing - #: after shifting all the unknown options into the resulting args. - self.ignore_unknown_options: bool = False - - if ctx is not None: - self.allow_interspersed_args = ctx.allow_interspersed_args - self.ignore_unknown_options = ctx.ignore_unknown_options - - self._short_opt: t.Dict[str, Option] = {} - self._long_opt: t.Dict[str, Option] = {} - self._opt_prefixes = {"-", "--"} - self._args: t.List[Argument] = [] - - def add_option( - self, - obj: "CoreOption", - opts: t.Sequence[str], - dest: t.Optional[str], - action: t.Optional[str] = None, - nargs: int = 1, - const: t.Optional[t.Any] = None, - ) -> None: - """Adds a new option named `dest` to the parser. The destination - is not inferred (unlike with optparse) and needs to be explicitly - provided. Action can be any of ``store``, ``store_const``, - ``append``, ``append_const`` or ``count``. - - The `obj` can be used to identify the option in the order list - that is returned from the parser. - """ - opts = [normalize_opt(opt, self.ctx) for opt in opts] - option = Option(obj, opts, dest, action=action, nargs=nargs, const=const) - self._opt_prefixes.update(option.prefixes) - for opt in option._short_opts: - self._short_opt[opt] = option - for opt in option._long_opts: - self._long_opt[opt] = option - - def add_argument( - self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1 - ) -> None: - """Adds a positional argument named `dest` to the parser. - - The `obj` can be used to identify the option in the order list - that is returned from the parser. - """ - self._args.append(Argument(obj, dest=dest, nargs=nargs)) - - def parse_args( - self, args: t.List[str] - ) -> t.Tuple[t.Dict[str, t.Any], t.List[str], t.List["CoreParameter"]]: - """Parses positional arguments and returns ``(values, args, order)`` - for the parsed options and arguments as well as the leftover - arguments if there are any. The order is a list of objects as they - appear on the command line. If arguments appear multiple times they - will be memorized multiple times as well. - """ - state = ParsingState(args) - try: - self._process_args_for_options(state) - self._process_args_for_args(state) - except UsageError: - if self.ctx is None or not self.ctx.resilient_parsing: - raise - return state.opts, state.largs, state.order - - def _process_args_for_args(self, state: ParsingState) -> None: - pargs, args = _unpack_args( - state.largs + state.rargs, [x.nargs for x in self._args] - ) - - for idx, arg in enumerate(self._args): - arg.process(pargs[idx], state) - - state.largs = args - state.rargs = [] - - def _process_args_for_options(self, state: ParsingState) -> None: - while state.rargs: - arg = state.rargs.pop(0) - arglen = len(arg) - # Double dashes always handled explicitly regardless of what - # prefixes are valid. - if arg == "--": - return - elif arg[:1] in self._opt_prefixes and arglen > 1: - self._process_opts(arg, state) - elif self.allow_interspersed_args: - state.largs.append(arg) - else: - state.rargs.insert(0, arg) - return - - # Say this is the original argument list: - # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] - # ^ - # (we are about to process arg(i)). - # - # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of - # [arg0, ..., arg(i-1)] (any options and their arguments will have - # been removed from largs). - # - # The while loop will usually consume 1 or more arguments per pass. - # If it consumes 1 (eg. arg is an option that takes no arguments), - # then after _process_arg() is done the situation is: - # - # largs = subset of [arg0, ..., arg(i)] - # rargs = [arg(i+1), ..., arg(N-1)] - # - # If allow_interspersed_args is false, largs will always be - # *empty* -- still a subset of [arg0, ..., arg(i-1)], but - # not a very interesting subset! - - def _match_long_opt( - self, opt: str, explicit_value: t.Optional[str], state: ParsingState - ) -> None: - if opt not in self._long_opt: - from difflib import get_close_matches - - possibilities = get_close_matches(opt, self._long_opt) - raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx) - - option = self._long_opt[opt] - if option.takes_value: - # At this point it's safe to modify rargs by injecting the - # explicit value, because no exception is raised in this - # branch. This means that the inserted value will be fully - # consumed. - if explicit_value is not None: - state.rargs.insert(0, explicit_value) - - value = self._get_value_from_state(opt, option, state) - - elif explicit_value is not None: - raise BadOptionUsage( - opt, _("Option {name!r} does not take a value.").format(name=opt) - ) - - else: - value = None - - option.process(value, state) - - def _match_short_opt(self, arg: str, state: ParsingState) -> None: - stop = False - i = 1 - prefix = arg[0] - unknown_options = [] - - for ch in arg[1:]: - opt = normalize_opt(f"{prefix}{ch}", self.ctx) - option = self._short_opt.get(opt) - i += 1 - - if not option: - if self.ignore_unknown_options: - unknown_options.append(ch) - continue - raise NoSuchOption(opt, ctx=self.ctx) - if option.takes_value: - # Any characters left in arg? Pretend they're the - # next arg, and stop consuming characters of arg. - if i < len(arg): - state.rargs.insert(0, arg[i:]) - stop = True - - value = self._get_value_from_state(opt, option, state) - - else: - value = None - - option.process(value, state) - - if stop: - break - - # If we got any unknown options we recombine the string of the - # remaining options and re-attach the prefix, then report that - # to the state as new larg. This way there is basic combinatorics - # that can be achieved while still ignoring unknown arguments. - if self.ignore_unknown_options and unknown_options: - state.largs.append(f"{prefix}{''.join(unknown_options)}") - - def _get_value_from_state( - self, option_name: str, option: Option, state: ParsingState - ) -> t.Any: - nargs = option.nargs - - if len(state.rargs) < nargs: - if option.obj._flag_needs_value: - # Option allows omitting the value. - value = _flag_needs_value - else: - raise BadOptionUsage( - option_name, - ngettext( - "Option {name!r} requires an argument.", - "Option {name!r} requires {nargs} arguments.", - nargs, - ).format(name=option_name, nargs=nargs), - ) - elif nargs == 1: - next_rarg = state.rargs[0] - - if ( - option.obj._flag_needs_value - and isinstance(next_rarg, str) - and next_rarg[:1] in self._opt_prefixes - and len(next_rarg) > 1 - ): - # The next arg looks like the start of an option, don't - # use it as the value if omitting the value is allowed. - value = _flag_needs_value - else: - value = state.rargs.pop(0) - else: - value = tuple(state.rargs[:nargs]) - del state.rargs[:nargs] - - return value - - def _process_opts(self, arg: str, state: ParsingState) -> None: - explicit_value = None - # Long option handling happens in two parts. The first part is - # supporting explicitly attached values. In any case, we will try - # to long match the option first. - if "=" in arg: - long_opt, explicit_value = arg.split("=", 1) - else: - long_opt = arg - norm_long_opt = normalize_opt(long_opt, self.ctx) - - # At this point we will match the (assumed) long option through - # the long option matching code. Note that this allows options - # like "-foo" to be matched as long options. - try: - self._match_long_opt(norm_long_opt, explicit_value, state) - except NoSuchOption: - # At this point the long option matching failed, and we need - # to try with short options. However there is a special rule - # which says, that if we have a two character options prefix - # (applies to "--foo" for instance), we do not dispatch to the - # short option code and will instead raise the no option - # error. - if arg[:2] not in self._opt_prefixes: - self._match_short_opt(arg, state) - return - - if not self.ignore_unknown_options: - raise - - state.largs.append(arg) diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/py.typed b/plotter-app/venv/lib/python3.8/site-packages/click/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/shell_completion.py b/plotter-app/venv/lib/python3.8/site-packages/click/shell_completion.py deleted file mode 100644 index dc9e00b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click/shell_completion.py +++ /dev/null @@ -1,596 +0,0 @@ -import os -import re -import typing as t -from gettext import gettext as _ - -from .core import Argument -from .core import BaseCommand -from .core import Context -from .core import MultiCommand -from .core import Option -from .core import Parameter -from .core import ParameterSource -from .parser import split_arg_string -from .utils import echo - - -def shell_complete( - cli: BaseCommand, - ctx_args: t.MutableMapping[str, t.Any], - prog_name: str, - complete_var: str, - instruction: str, -) -> int: - """Perform shell completion for the given CLI program. - - :param cli: Command being called. - :param ctx_args: Extra arguments to pass to - ``cli.make_context``. - :param prog_name: Name of the executable in the shell. - :param complete_var: Name of the environment variable that holds - the completion instruction. - :param instruction: Value of ``complete_var`` with the completion - instruction and shell, in the form ``instruction_shell``. - :return: Status code to exit with. - """ - shell, _, instruction = instruction.partition("_") - comp_cls = get_completion_class(shell) - - if comp_cls is None: - return 1 - - comp = comp_cls(cli, ctx_args, prog_name, complete_var) - - if instruction == "source": - echo(comp.source()) - return 0 - - if instruction == "complete": - echo(comp.complete()) - return 0 - - return 1 - - -class CompletionItem: - """Represents a completion value and metadata about the value. The - default metadata is ``type`` to indicate special shell handling, - and ``help`` if a shell supports showing a help string next to the - value. - - Arbitrary parameters can be passed when creating the object, and - accessed using ``item.attr``. If an attribute wasn't passed, - accessing it returns ``None``. - - :param value: The completion suggestion. - :param type: Tells the shell script to provide special completion - support for the type. Click uses ``"dir"`` and ``"file"``. - :param help: String shown next to the value if supported. - :param kwargs: Arbitrary metadata. The built-in implementations - don't use this, but custom type completions paired with custom - shell support could use it. - """ - - __slots__ = ("value", "type", "help", "_info") - - def __init__( - self, - value: t.Any, - type: str = "plain", - help: t.Optional[str] = None, - **kwargs: t.Any, - ) -> None: - self.value: t.Any = value - self.type: str = type - self.help: t.Optional[str] = help - self._info = kwargs - - def __getattr__(self, name: str) -> t.Any: - return self._info.get(name) - - -# Only Bash >= 4.4 has the nosort option. -_SOURCE_BASH = """\ -%(complete_func)s() { - local IFS=$'\\n' - local response - - response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \ -%(complete_var)s=bash_complete $1) - - for completion in $response; do - IFS=',' read type value <<< "$completion" - - if [[ $type == 'dir' ]]; then - COMPREPLY=() - compopt -o dirnames - elif [[ $type == 'file' ]]; then - COMPREPLY=() - compopt -o default - elif [[ $type == 'plain' ]]; then - COMPREPLY+=($value) - fi - done - - return 0 -} - -%(complete_func)s_setup() { - complete -o nosort -F %(complete_func)s %(prog_name)s -} - -%(complete_func)s_setup; -""" - -_SOURCE_ZSH = """\ -#compdef %(prog_name)s - -%(complete_func)s() { - local -a completions - local -a completions_with_descriptions - local -a response - (( ! $+commands[%(prog_name)s] )) && return 1 - - response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) \ -%(complete_var)s=zsh_complete %(prog_name)s)}") - - for type key descr in ${response}; do - if [[ "$type" == "plain" ]]; then - if [[ "$descr" == "_" ]]; then - completions+=("$key") - else - completions_with_descriptions+=("$key":"$descr") - fi - elif [[ "$type" == "dir" ]]; then - _path_files -/ - elif [[ "$type" == "file" ]]; then - _path_files -f - fi - done - - if [ -n "$completions_with_descriptions" ]; then - _describe -V unsorted completions_with_descriptions -U - fi - - if [ -n "$completions" ]; then - compadd -U -V unsorted -a completions - fi -} - -if [[ $zsh_eval_context[-1] == loadautofunc ]]; then - # autoload from fpath, call function directly - %(complete_func)s "$@" -else - # eval/source/. command, register function for later - compdef %(complete_func)s %(prog_name)s -fi -""" - -_SOURCE_FISH = """\ -function %(complete_func)s; - set -l response (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \ -COMP_CWORD=(commandline -t) %(prog_name)s); - - for completion in $response; - set -l metadata (string split "," $completion); - - if test $metadata[1] = "dir"; - __fish_complete_directories $metadata[2]; - else if test $metadata[1] = "file"; - __fish_complete_path $metadata[2]; - else if test $metadata[1] = "plain"; - echo $metadata[2]; - end; - end; -end; - -complete --no-files --command %(prog_name)s --arguments \ -"(%(complete_func)s)"; -""" - - -class ShellComplete: - """Base class for providing shell completion support. A subclass for - a given shell will override attributes and methods to implement the - completion instructions (``source`` and ``complete``). - - :param cli: Command being called. - :param prog_name: Name of the executable in the shell. - :param complete_var: Name of the environment variable that holds - the completion instruction. - - .. versionadded:: 8.0 - """ - - name: t.ClassVar[str] - """Name to register the shell as with :func:`add_completion_class`. - This is used in completion instructions (``{name}_source`` and - ``{name}_complete``). - """ - - source_template: t.ClassVar[str] - """Completion script template formatted by :meth:`source`. This must - be provided by subclasses. - """ - - def __init__( - self, - cli: BaseCommand, - ctx_args: t.MutableMapping[str, t.Any], - prog_name: str, - complete_var: str, - ) -> None: - self.cli = cli - self.ctx_args = ctx_args - self.prog_name = prog_name - self.complete_var = complete_var - - @property - def func_name(self) -> str: - """The name of the shell function defined by the completion - script. - """ - safe_name = re.sub(r"\W*", "", self.prog_name.replace("-", "_"), flags=re.ASCII) - return f"_{safe_name}_completion" - - def source_vars(self) -> t.Dict[str, t.Any]: - """Vars for formatting :attr:`source_template`. - - By default this provides ``complete_func``, ``complete_var``, - and ``prog_name``. - """ - return { - "complete_func": self.func_name, - "complete_var": self.complete_var, - "prog_name": self.prog_name, - } - - def source(self) -> str: - """Produce the shell script that defines the completion - function. By default this ``%``-style formats - :attr:`source_template` with the dict returned by - :meth:`source_vars`. - """ - return self.source_template % self.source_vars() - - def get_completion_args(self) -> t.Tuple[t.List[str], str]: - """Use the env vars defined by the shell script to return a - tuple of ``args, incomplete``. This must be implemented by - subclasses. - """ - raise NotImplementedError - - def get_completions( - self, args: t.List[str], incomplete: str - ) -> t.List[CompletionItem]: - """Determine the context and last complete command or parameter - from the complete args. Call that object's ``shell_complete`` - method to get the completions for the incomplete value. - - :param args: List of complete args before the incomplete value. - :param incomplete: Value being completed. May be empty. - """ - ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args) - obj, incomplete = _resolve_incomplete(ctx, args, incomplete) - return obj.shell_complete(ctx, incomplete) - - def format_completion(self, item: CompletionItem) -> str: - """Format a completion item into the form recognized by the - shell script. This must be implemented by subclasses. - - :param item: Completion item to format. - """ - raise NotImplementedError - - def complete(self) -> str: - """Produce the completion data to send back to the shell. - - By default this calls :meth:`get_completion_args`, gets the - completions, then calls :meth:`format_completion` for each - completion. - """ - args, incomplete = self.get_completion_args() - completions = self.get_completions(args, incomplete) - out = [self.format_completion(item) for item in completions] - return "\n".join(out) - - -class BashComplete(ShellComplete): - """Shell completion for Bash.""" - - name = "bash" - source_template = _SOURCE_BASH - - @staticmethod - def _check_version() -> None: - import subprocess - - output = subprocess.run( - ["bash", "-c", 'echo "${BASH_VERSION}"'], stdout=subprocess.PIPE - ) - match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode()) - - if match is not None: - major, minor = match.groups() - - if major < "4" or major == "4" and minor < "4": - echo( - _( - "Shell completion is not supported for Bash" - " versions older than 4.4." - ), - err=True, - ) - else: - echo( - _("Couldn't detect Bash version, shell completion is not supported."), - err=True, - ) - - def source(self) -> str: - self._check_version() - return super().source() - - def get_completion_args(self) -> t.Tuple[t.List[str], str]: - cwords = split_arg_string(os.environ["COMP_WORDS"]) - cword = int(os.environ["COMP_CWORD"]) - args = cwords[1:cword] - - try: - incomplete = cwords[cword] - except IndexError: - incomplete = "" - - return args, incomplete - - def format_completion(self, item: CompletionItem) -> str: - return f"{item.type},{item.value}" - - -class ZshComplete(ShellComplete): - """Shell completion for Zsh.""" - - name = "zsh" - source_template = _SOURCE_ZSH - - def get_completion_args(self) -> t.Tuple[t.List[str], str]: - cwords = split_arg_string(os.environ["COMP_WORDS"]) - cword = int(os.environ["COMP_CWORD"]) - args = cwords[1:cword] - - try: - incomplete = cwords[cword] - except IndexError: - incomplete = "" - - return args, incomplete - - def format_completion(self, item: CompletionItem) -> str: - return f"{item.type}\n{item.value}\n{item.help if item.help else '_'}" - - -class FishComplete(ShellComplete): - """Shell completion for Fish.""" - - name = "fish" - source_template = _SOURCE_FISH - - def get_completion_args(self) -> t.Tuple[t.List[str], str]: - cwords = split_arg_string(os.environ["COMP_WORDS"]) - incomplete = os.environ["COMP_CWORD"] - args = cwords[1:] - - # Fish stores the partial word in both COMP_WORDS and - # COMP_CWORD, remove it from complete args. - if incomplete and args and args[-1] == incomplete: - args.pop() - - return args, incomplete - - def format_completion(self, item: CompletionItem) -> str: - if item.help: - return f"{item.type},{item.value}\t{item.help}" - - return f"{item.type},{item.value}" - - -ShellCompleteType = t.TypeVar("ShellCompleteType", bound=t.Type[ShellComplete]) - - -_available_shells: t.Dict[str, t.Type[ShellComplete]] = { - "bash": BashComplete, - "fish": FishComplete, - "zsh": ZshComplete, -} - - -def add_completion_class( - cls: ShellCompleteType, name: t.Optional[str] = None -) -> ShellCompleteType: - """Register a :class:`ShellComplete` subclass under the given name. - The name will be provided by the completion instruction environment - variable during completion. - - :param cls: The completion class that will handle completion for the - shell. - :param name: Name to register the class under. Defaults to the - class's ``name`` attribute. - """ - if name is None: - name = cls.name - - _available_shells[name] = cls - - return cls - - -def get_completion_class(shell: str) -> t.Optional[t.Type[ShellComplete]]: - """Look up a registered :class:`ShellComplete` subclass by the name - provided by the completion instruction environment variable. If the - name isn't registered, returns ``None``. - - :param shell: Name the class is registered under. - """ - return _available_shells.get(shell) - - -def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool: - """Determine if the given parameter is an argument that can still - accept values. - - :param ctx: Invocation context for the command represented by the - parsed complete args. - :param param: Argument object being checked. - """ - if not isinstance(param, Argument): - return False - - assert param.name is not None - # Will be None if expose_value is False. - value = ctx.params.get(param.name) - return ( - param.nargs == -1 - or ctx.get_parameter_source(param.name) is not ParameterSource.COMMANDLINE - or ( - param.nargs > 1 - and isinstance(value, (tuple, list)) - and len(value) < param.nargs - ) - ) - - -def _start_of_option(ctx: Context, value: str) -> bool: - """Check if the value looks like the start of an option.""" - if not value: - return False - - c = value[0] - return c in ctx._opt_prefixes - - -def _is_incomplete_option(ctx: Context, args: t.List[str], param: Parameter) -> bool: - """Determine if the given parameter is an option that needs a value. - - :param args: List of complete args before the incomplete value. - :param param: Option object being checked. - """ - if not isinstance(param, Option): - return False - - if param.is_flag or param.count: - return False - - last_option = None - - for index, arg in enumerate(reversed(args)): - if index + 1 > param.nargs: - break - - if _start_of_option(ctx, arg): - last_option = arg - - return last_option is not None and last_option in param.opts - - -def _resolve_context( - cli: BaseCommand, - ctx_args: t.MutableMapping[str, t.Any], - prog_name: str, - args: t.List[str], -) -> Context: - """Produce the context hierarchy starting with the command and - traversing the complete arguments. This only follows the commands, - it doesn't trigger input prompts or callbacks. - - :param cli: Command being called. - :param prog_name: Name of the executable in the shell. - :param args: List of complete args before the incomplete value. - """ - ctx_args["resilient_parsing"] = True - ctx = cli.make_context(prog_name, args.copy(), **ctx_args) - args = ctx.protected_args + ctx.args - - while args: - command = ctx.command - - if isinstance(command, MultiCommand): - if not command.chain: - name, cmd, args = command.resolve_command(ctx, args) - - if cmd is None: - return ctx - - ctx = cmd.make_context(name, args, parent=ctx, resilient_parsing=True) - args = ctx.protected_args + ctx.args - else: - sub_ctx = ctx - - while args: - name, cmd, args = command.resolve_command(ctx, args) - - if cmd is None: - return ctx - - sub_ctx = cmd.make_context( - name, - args, - parent=ctx, - allow_extra_args=True, - allow_interspersed_args=False, - resilient_parsing=True, - ) - args = sub_ctx.args - - ctx = sub_ctx - args = [*sub_ctx.protected_args, *sub_ctx.args] - else: - break - - return ctx - - -def _resolve_incomplete( - ctx: Context, args: t.List[str], incomplete: str -) -> t.Tuple[t.Union[BaseCommand, Parameter], str]: - """Find the Click object that will handle the completion of the - incomplete value. Return the object and the incomplete value. - - :param ctx: Invocation context for the command represented by - the parsed complete args. - :param args: List of complete args before the incomplete value. - :param incomplete: Value being completed. May be empty. - """ - # Different shells treat an "=" between a long option name and - # value differently. Might keep the value joined, return the "=" - # as a separate item, or return the split name and value. Always - # split and discard the "=" to make completion easier. - if incomplete == "=": - incomplete = "" - elif "=" in incomplete and _start_of_option(ctx, incomplete): - name, _, incomplete = incomplete.partition("=") - args.append(name) - - # The "--" marker tells Click to stop treating values as options - # even if they start with the option character. If it hasn't been - # given and the incomplete arg looks like an option, the current - # command will provide option name completions. - if "--" not in args and _start_of_option(ctx, incomplete): - return ctx.command, incomplete - - params = ctx.command.get_params(ctx) - - # If the last complete arg is an option name with an incomplete - # value, the option will provide value completions. - for param in params: - if _is_incomplete_option(ctx, args, param): - return param, incomplete - - # It's not an option name or value. The first argument without a - # parsed value will provide value completions. - for param in params: - if _is_incomplete_argument(ctx, param): - return param, incomplete - - # There were no unparsed arguments, the command may be a group that - # will provide command name completions. - return ctx.command, incomplete diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/termui.py b/plotter-app/venv/lib/python3.8/site-packages/click/termui.py deleted file mode 100644 index db7a4b2..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click/termui.py +++ /dev/null @@ -1,784 +0,0 @@ -import inspect -import io -import itertools -import sys -import typing as t -from gettext import gettext as _ - -from ._compat import isatty -from ._compat import strip_ansi -from .exceptions import Abort -from .exceptions import UsageError -from .globals import resolve_color_default -from .types import Choice -from .types import convert_type -from .types import ParamType -from .utils import echo -from .utils import LazyFile - -if t.TYPE_CHECKING: - from ._termui_impl import ProgressBar - -V = t.TypeVar("V") - -# The prompt functions to use. The doc tools currently override these -# functions to customize how they work. -visible_prompt_func: t.Callable[[str], str] = input - -_ansi_colors = { - "black": 30, - "red": 31, - "green": 32, - "yellow": 33, - "blue": 34, - "magenta": 35, - "cyan": 36, - "white": 37, - "reset": 39, - "bright_black": 90, - "bright_red": 91, - "bright_green": 92, - "bright_yellow": 93, - "bright_blue": 94, - "bright_magenta": 95, - "bright_cyan": 96, - "bright_white": 97, -} -_ansi_reset_all = "\033[0m" - - -def hidden_prompt_func(prompt: str) -> str: - import getpass - - return getpass.getpass(prompt) - - -def _build_prompt( - text: str, - suffix: str, - show_default: bool = False, - default: t.Optional[t.Any] = None, - show_choices: bool = True, - type: t.Optional[ParamType] = None, -) -> str: - prompt = text - if type is not None and show_choices and isinstance(type, Choice): - prompt += f" ({', '.join(map(str, type.choices))})" - if default is not None and show_default: - prompt = f"{prompt} [{_format_default(default)}]" - return f"{prompt}{suffix}" - - -def _format_default(default: t.Any) -> t.Any: - if isinstance(default, (io.IOBase, LazyFile)) and hasattr(default, "name"): - return default.name - - return default - - -def prompt( - text: str, - default: t.Optional[t.Any] = None, - hide_input: bool = False, - confirmation_prompt: t.Union[bool, str] = False, - type: t.Optional[t.Union[ParamType, t.Any]] = None, - value_proc: t.Optional[t.Callable[[str], t.Any]] = None, - prompt_suffix: str = ": ", - show_default: bool = True, - err: bool = False, - show_choices: bool = True, -) -> t.Any: - """Prompts a user for input. This is a convenience function that can - be used to prompt a user for input later. - - If the user aborts the input by sending an interrupt signal, this - function will catch it and raise a :exc:`Abort` exception. - - :param text: the text to show for the prompt. - :param default: the default value to use if no input happens. If this - is not given it will prompt until it's aborted. - :param hide_input: if this is set to true then the input value will - be hidden. - :param confirmation_prompt: Prompt a second time to confirm the - value. Can be set to a string instead of ``True`` to customize - the message. - :param type: the type to use to check the value against. - :param value_proc: if this parameter is provided it's a function that - is invoked instead of the type conversion to - convert a value. - :param prompt_suffix: a suffix that should be added to the prompt. - :param show_default: shows or hides the default value in the prompt. - :param err: if set to true the file defaults to ``stderr`` instead of - ``stdout``, the same as with echo. - :param show_choices: Show or hide choices if the passed type is a Choice. - For example if type is a Choice of either day or week, - show_choices is true and text is "Group by" then the - prompt will be "Group by (day, week): ". - - .. versionadded:: 8.0 - ``confirmation_prompt`` can be a custom string. - - .. versionadded:: 7.0 - Added the ``show_choices`` parameter. - - .. versionadded:: 6.0 - Added unicode support for cmd.exe on Windows. - - .. versionadded:: 4.0 - Added the `err` parameter. - - """ - - def prompt_func(text: str) -> str: - f = hidden_prompt_func if hide_input else visible_prompt_func - try: - # Write the prompt separately so that we get nice - # coloring through colorama on Windows - echo(text.rstrip(" "), nl=False, err=err) - # Echo a space to stdout to work around an issue where - # readline causes backspace to clear the whole line. - return f(" ") - except (KeyboardInterrupt, EOFError): - # getpass doesn't print a newline if the user aborts input with ^C. - # Allegedly this behavior is inherited from getpass(3). - # A doc bug has been filed at https://bugs.python.org/issue24711 - if hide_input: - echo(None, err=err) - raise Abort() from None - - if value_proc is None: - value_proc = convert_type(type, default) - - prompt = _build_prompt( - text, prompt_suffix, show_default, default, show_choices, type - ) - - if confirmation_prompt: - if confirmation_prompt is True: - confirmation_prompt = _("Repeat for confirmation") - - confirmation_prompt = _build_prompt(confirmation_prompt, prompt_suffix) - - while True: - while True: - value = prompt_func(prompt) - if value: - break - elif default is not None: - value = default - break - try: - result = value_proc(value) - except UsageError as e: - if hide_input: - echo(_("Error: The value you entered was invalid."), err=err) - else: - echo(_("Error: {e.message}").format(e=e), err=err) # noqa: B306 - continue - if not confirmation_prompt: - return result - while True: - value2 = prompt_func(confirmation_prompt) - is_empty = not value and not value2 - if value2 or is_empty: - break - if value == value2: - return result - echo(_("Error: The two entered values do not match."), err=err) - - -def confirm( - text: str, - default: t.Optional[bool] = False, - abort: bool = False, - prompt_suffix: str = ": ", - show_default: bool = True, - err: bool = False, -) -> bool: - """Prompts for confirmation (yes/no question). - - If the user aborts the input by sending a interrupt signal this - function will catch it and raise a :exc:`Abort` exception. - - :param text: the question to ask. - :param default: The default value to use when no input is given. If - ``None``, repeat until input is given. - :param abort: if this is set to `True` a negative answer aborts the - exception by raising :exc:`Abort`. - :param prompt_suffix: a suffix that should be added to the prompt. - :param show_default: shows or hides the default value in the prompt. - :param err: if set to true the file defaults to ``stderr`` instead of - ``stdout``, the same as with echo. - - .. versionchanged:: 8.0 - Repeat until input is given if ``default`` is ``None``. - - .. versionadded:: 4.0 - Added the ``err`` parameter. - """ - prompt = _build_prompt( - text, - prompt_suffix, - show_default, - "y/n" if default is None else ("Y/n" if default else "y/N"), - ) - - while True: - try: - # Write the prompt separately so that we get nice - # coloring through colorama on Windows - echo(prompt.rstrip(" "), nl=False, err=err) - # Echo a space to stdout to work around an issue where - # readline causes backspace to clear the whole line. - value = visible_prompt_func(" ").lower().strip() - except (KeyboardInterrupt, EOFError): - raise Abort() from None - if value in ("y", "yes"): - rv = True - elif value in ("n", "no"): - rv = False - elif default is not None and value == "": - rv = default - else: - echo(_("Error: invalid input"), err=err) - continue - break - if abort and not rv: - raise Abort() - return rv - - -def echo_via_pager( - text_or_generator: t.Union[t.Iterable[str], t.Callable[[], t.Iterable[str]], str], - color: t.Optional[bool] = None, -) -> None: - """This function takes a text and shows it via an environment specific - pager on stdout. - - .. versionchanged:: 3.0 - Added the `color` flag. - - :param text_or_generator: the text to page, or alternatively, a - generator emitting the text to page. - :param color: controls if the pager supports ANSI colors or not. The - default is autodetection. - """ - color = resolve_color_default(color) - - if inspect.isgeneratorfunction(text_or_generator): - i = t.cast(t.Callable[[], t.Iterable[str]], text_or_generator)() - elif isinstance(text_or_generator, str): - i = [text_or_generator] - else: - i = iter(t.cast(t.Iterable[str], text_or_generator)) - - # convert every element of i to a text type if necessary - text_generator = (el if isinstance(el, str) else str(el) for el in i) - - from ._termui_impl import pager - - return pager(itertools.chain(text_generator, "\n"), color) - - -def progressbar( - iterable: t.Optional[t.Iterable[V]] = None, - length: t.Optional[int] = None, - label: t.Optional[str] = None, - show_eta: bool = True, - show_percent: t.Optional[bool] = None, - show_pos: bool = False, - item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, - fill_char: str = "#", - empty_char: str = "-", - bar_template: str = "%(label)s [%(bar)s] %(info)s", - info_sep: str = " ", - width: int = 36, - file: t.Optional[t.TextIO] = None, - color: t.Optional[bool] = None, - update_min_steps: int = 1, -) -> "ProgressBar[V]": - """This function creates an iterable context manager that can be used - to iterate over something while showing a progress bar. It will - either iterate over the `iterable` or `length` items (that are counted - up). While iteration happens, this function will print a rendered - progress bar to the given `file` (defaults to stdout) and will attempt - to calculate remaining time and more. By default, this progress bar - will not be rendered if the file is not a terminal. - - The context manager creates the progress bar. When the context - manager is entered the progress bar is already created. With every - iteration over the progress bar, the iterable passed to the bar is - advanced and the bar is updated. When the context manager exits, - a newline is printed and the progress bar is finalized on screen. - - Note: The progress bar is currently designed for use cases where the - total progress can be expected to take at least several seconds. - Because of this, the ProgressBar class object won't display - progress that is considered too fast, and progress where the time - between steps is less than a second. - - No printing must happen or the progress bar will be unintentionally - destroyed. - - Example usage:: - - with progressbar(items) as bar: - for item in bar: - do_something_with(item) - - Alternatively, if no iterable is specified, one can manually update the - progress bar through the `update()` method instead of directly - iterating over the progress bar. The update method accepts the number - of steps to increment the bar with:: - - with progressbar(length=chunks.total_bytes) as bar: - for chunk in chunks: - process_chunk(chunk) - bar.update(chunks.bytes) - - The ``update()`` method also takes an optional value specifying the - ``current_item`` at the new position. This is useful when used - together with ``item_show_func`` to customize the output for each - manual step:: - - with click.progressbar( - length=total_size, - label='Unzipping archive', - item_show_func=lambda a: a.filename - ) as bar: - for archive in zip_file: - archive.extract() - bar.update(archive.size, archive) - - :param iterable: an iterable to iterate over. If not provided the length - is required. - :param length: the number of items to iterate over. By default the - progressbar will attempt to ask the iterator about its - length, which might or might not work. If an iterable is - also provided this parameter can be used to override the - length. If an iterable is not provided the progress bar - will iterate over a range of that length. - :param label: the label to show next to the progress bar. - :param show_eta: enables or disables the estimated time display. This is - automatically disabled if the length cannot be - determined. - :param show_percent: enables or disables the percentage display. The - default is `True` if the iterable has a length or - `False` if not. - :param show_pos: enables or disables the absolute position display. The - default is `False`. - :param item_show_func: A function called with the current item which - can return a string to show next to the progress bar. If the - function returns ``None`` nothing is shown. The current item can - be ``None``, such as when entering and exiting the bar. - :param fill_char: the character to use to show the filled part of the - progress bar. - :param empty_char: the character to use to show the non-filled part of - the progress bar. - :param bar_template: the format string to use as template for the bar. - The parameters in it are ``label`` for the label, - ``bar`` for the progress bar and ``info`` for the - info section. - :param info_sep: the separator between multiple info items (eta etc.) - :param width: the width of the progress bar in characters, 0 means full - terminal width - :param file: The file to write to. If this is not a terminal then - only the label is printed. - :param color: controls if the terminal supports ANSI colors or not. The - default is autodetection. This is only needed if ANSI - codes are included anywhere in the progress bar output - which is not the case by default. - :param update_min_steps: Render only when this many updates have - completed. This allows tuning for very fast iterators. - - .. versionchanged:: 8.0 - Output is shown even if execution time is less than 0.5 seconds. - - .. versionchanged:: 8.0 - ``item_show_func`` shows the current item, not the previous one. - - .. versionchanged:: 8.0 - Labels are echoed if the output is not a TTY. Reverts a change - in 7.0 that removed all output. - - .. versionadded:: 8.0 - Added the ``update_min_steps`` parameter. - - .. versionchanged:: 4.0 - Added the ``color`` parameter. Added the ``update`` method to - the object. - - .. versionadded:: 2.0 - """ - from ._termui_impl import ProgressBar - - color = resolve_color_default(color) - return ProgressBar( - iterable=iterable, - length=length, - show_eta=show_eta, - show_percent=show_percent, - show_pos=show_pos, - item_show_func=item_show_func, - fill_char=fill_char, - empty_char=empty_char, - bar_template=bar_template, - info_sep=info_sep, - file=file, - label=label, - width=width, - color=color, - update_min_steps=update_min_steps, - ) - - -def clear() -> None: - """Clears the terminal screen. This will have the effect of clearing - the whole visible space of the terminal and moving the cursor to the - top left. This does not do anything if not connected to a terminal. - - .. versionadded:: 2.0 - """ - if not isatty(sys.stdout): - return - - # ANSI escape \033[2J clears the screen, \033[1;1H moves the cursor - echo("\033[2J\033[1;1H", nl=False) - - -def _interpret_color( - color: t.Union[int, t.Tuple[int, int, int], str], offset: int = 0 -) -> str: - if isinstance(color, int): - return f"{38 + offset};5;{color:d}" - - if isinstance(color, (tuple, list)): - r, g, b = color - return f"{38 + offset};2;{r:d};{g:d};{b:d}" - - return str(_ansi_colors[color] + offset) - - -def style( - text: t.Any, - fg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, - bg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, - bold: t.Optional[bool] = None, - dim: t.Optional[bool] = None, - underline: t.Optional[bool] = None, - overline: t.Optional[bool] = None, - italic: t.Optional[bool] = None, - blink: t.Optional[bool] = None, - reverse: t.Optional[bool] = None, - strikethrough: t.Optional[bool] = None, - reset: bool = True, -) -> str: - """Styles a text with ANSI styles and returns the new string. By - default the styling is self contained which means that at the end - of the string a reset code is issued. This can be prevented by - passing ``reset=False``. - - Examples:: - - click.echo(click.style('Hello World!', fg='green')) - click.echo(click.style('ATTENTION!', blink=True)) - click.echo(click.style('Some things', reverse=True, fg='cyan')) - click.echo(click.style('More colors', fg=(255, 12, 128), bg=117)) - - Supported color names: - - * ``black`` (might be a gray) - * ``red`` - * ``green`` - * ``yellow`` (might be an orange) - * ``blue`` - * ``magenta`` - * ``cyan`` - * ``white`` (might be light gray) - * ``bright_black`` - * ``bright_red`` - * ``bright_green`` - * ``bright_yellow`` - * ``bright_blue`` - * ``bright_magenta`` - * ``bright_cyan`` - * ``bright_white`` - * ``reset`` (reset the color code only) - - If the terminal supports it, color may also be specified as: - - - An integer in the interval [0, 255]. The terminal must support - 8-bit/256-color mode. - - An RGB tuple of three integers in [0, 255]. The terminal must - support 24-bit/true-color mode. - - See https://en.wikipedia.org/wiki/ANSI_color and - https://gist.github.com/XVilka/8346728 for more information. - - :param text: the string to style with ansi codes. - :param fg: if provided this will become the foreground color. - :param bg: if provided this will become the background color. - :param bold: if provided this will enable or disable bold mode. - :param dim: if provided this will enable or disable dim mode. This is - badly supported. - :param underline: if provided this will enable or disable underline. - :param overline: if provided this will enable or disable overline. - :param italic: if provided this will enable or disable italic. - :param blink: if provided this will enable or disable blinking. - :param reverse: if provided this will enable or disable inverse - rendering (foreground becomes background and the - other way round). - :param strikethrough: if provided this will enable or disable - striking through text. - :param reset: by default a reset-all code is added at the end of the - string which means that styles do not carry over. This - can be disabled to compose styles. - - .. versionchanged:: 8.0 - A non-string ``message`` is converted to a string. - - .. versionchanged:: 8.0 - Added support for 256 and RGB color codes. - - .. versionchanged:: 8.0 - Added the ``strikethrough``, ``italic``, and ``overline`` - parameters. - - .. versionchanged:: 7.0 - Added support for bright colors. - - .. versionadded:: 2.0 - """ - if not isinstance(text, str): - text = str(text) - - bits = [] - - if fg: - try: - bits.append(f"\033[{_interpret_color(fg)}m") - except KeyError: - raise TypeError(f"Unknown color {fg!r}") from None - - if bg: - try: - bits.append(f"\033[{_interpret_color(bg, 10)}m") - except KeyError: - raise TypeError(f"Unknown color {bg!r}") from None - - if bold is not None: - bits.append(f"\033[{1 if bold else 22}m") - if dim is not None: - bits.append(f"\033[{2 if dim else 22}m") - if underline is not None: - bits.append(f"\033[{4 if underline else 24}m") - if overline is not None: - bits.append(f"\033[{53 if overline else 55}m") - if italic is not None: - bits.append(f"\033[{3 if italic else 23}m") - if blink is not None: - bits.append(f"\033[{5 if blink else 25}m") - if reverse is not None: - bits.append(f"\033[{7 if reverse else 27}m") - if strikethrough is not None: - bits.append(f"\033[{9 if strikethrough else 29}m") - bits.append(text) - if reset: - bits.append(_ansi_reset_all) - return "".join(bits) - - -def unstyle(text: str) -> str: - """Removes ANSI styling information from a string. Usually it's not - necessary to use this function as Click's echo function will - automatically remove styling if necessary. - - .. versionadded:: 2.0 - - :param text: the text to remove style information from. - """ - return strip_ansi(text) - - -def secho( - message: t.Optional[t.Any] = None, - file: t.Optional[t.IO[t.AnyStr]] = None, - nl: bool = True, - err: bool = False, - color: t.Optional[bool] = None, - **styles: t.Any, -) -> None: - """This function combines :func:`echo` and :func:`style` into one - call. As such the following two calls are the same:: - - click.secho('Hello World!', fg='green') - click.echo(click.style('Hello World!', fg='green')) - - All keyword arguments are forwarded to the underlying functions - depending on which one they go with. - - Non-string types will be converted to :class:`str`. However, - :class:`bytes` are passed directly to :meth:`echo` without applying - style. If you want to style bytes that represent text, call - :meth:`bytes.decode` first. - - .. versionchanged:: 8.0 - A non-string ``message`` is converted to a string. Bytes are - passed through without style applied. - - .. versionadded:: 2.0 - """ - if message is not None and not isinstance(message, (bytes, bytearray)): - message = style(message, **styles) - - return echo(message, file=file, nl=nl, err=err, color=color) - - -def edit( - text: t.Optional[t.AnyStr] = None, - editor: t.Optional[str] = None, - env: t.Optional[t.Mapping[str, str]] = None, - require_save: bool = True, - extension: str = ".txt", - filename: t.Optional[str] = None, -) -> t.Optional[t.AnyStr]: - r"""Edits the given text in the defined editor. If an editor is given - (should be the full path to the executable but the regular operating - system search path is used for finding the executable) it overrides - the detected editor. Optionally, some environment variables can be - used. If the editor is closed without changes, `None` is returned. In - case a file is edited directly the return value is always `None` and - `require_save` and `extension` are ignored. - - If the editor cannot be opened a :exc:`UsageError` is raised. - - Note for Windows: to simplify cross-platform usage, the newlines are - automatically converted from POSIX to Windows and vice versa. As such, - the message here will have ``\n`` as newline markers. - - :param text: the text to edit. - :param editor: optionally the editor to use. Defaults to automatic - detection. - :param env: environment variables to forward to the editor. - :param require_save: if this is true, then not saving in the editor - will make the return value become `None`. - :param extension: the extension to tell the editor about. This defaults - to `.txt` but changing this might change syntax - highlighting. - :param filename: if provided it will edit this file instead of the - provided text contents. It will not use a temporary - file as an indirection in that case. - """ - from ._termui_impl import Editor - - ed = Editor(editor=editor, env=env, require_save=require_save, extension=extension) - - if filename is None: - return ed.edit(text) - - ed.edit_file(filename) - return None - - -def launch(url: str, wait: bool = False, locate: bool = False) -> int: - """This function launches the given URL (or filename) in the default - viewer application for this file type. If this is an executable, it - might launch the executable in a new session. The return value is - the exit code of the launched application. Usually, ``0`` indicates - success. - - Examples:: - - click.launch('https://click.palletsprojects.com/') - click.launch('/my/downloaded/file', locate=True) - - .. versionadded:: 2.0 - - :param url: URL or filename of the thing to launch. - :param wait: Wait for the program to exit before returning. This - only works if the launched program blocks. In particular, - ``xdg-open`` on Linux does not block. - :param locate: if this is set to `True` then instead of launching the - application associated with the URL it will attempt to - launch a file manager with the file located. This - might have weird effects if the URL does not point to - the filesystem. - """ - from ._termui_impl import open_url - - return open_url(url, wait=wait, locate=locate) - - -# If this is provided, getchar() calls into this instead. This is used -# for unittesting purposes. -_getchar: t.Optional[t.Callable[[bool], str]] = None - - -def getchar(echo: bool = False) -> str: - """Fetches a single character from the terminal and returns it. This - will always return a unicode character and under certain rare - circumstances this might return more than one character. The - situations which more than one character is returned is when for - whatever reason multiple characters end up in the terminal buffer or - standard input was not actually a terminal. - - Note that this will always read from the terminal, even if something - is piped into the standard input. - - Note for Windows: in rare cases when typing non-ASCII characters, this - function might wait for a second character and then return both at once. - This is because certain Unicode characters look like special-key markers. - - .. versionadded:: 2.0 - - :param echo: if set to `True`, the character read will also show up on - the terminal. The default is to not show it. - """ - global _getchar - - if _getchar is None: - from ._termui_impl import getchar as f - - _getchar = f - - return _getchar(echo) - - -def raw_terminal() -> t.ContextManager[int]: - from ._termui_impl import raw_terminal as f - - return f() - - -def pause(info: t.Optional[str] = None, err: bool = False) -> None: - """This command stops execution and waits for the user to press any - key to continue. This is similar to the Windows batch "pause" - command. If the program is not run through a terminal, this command - will instead do nothing. - - .. versionadded:: 2.0 - - .. versionadded:: 4.0 - Added the `err` parameter. - - :param info: The message to print before pausing. Defaults to - ``"Press any key to continue..."``. - :param err: if set to message goes to ``stderr`` instead of - ``stdout``, the same as with echo. - """ - if not isatty(sys.stdin) or not isatty(sys.stdout): - return - - if info is None: - info = _("Press any key to continue...") - - try: - if info: - echo(info, nl=False, err=err) - try: - getchar() - except (KeyboardInterrupt, EOFError): - pass - finally: - if info: - echo(err=err) diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/testing.py b/plotter-app/venv/lib/python3.8/site-packages/click/testing.py deleted file mode 100644 index e0df0d2..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click/testing.py +++ /dev/null @@ -1,479 +0,0 @@ -import contextlib -import io -import os -import shlex -import shutil -import sys -import tempfile -import typing as t -from types import TracebackType - -from . import formatting -from . import termui -from . import utils -from ._compat import _find_binary_reader - -if t.TYPE_CHECKING: - from .core import BaseCommand - - -class EchoingStdin: - def __init__(self, input: t.BinaryIO, output: t.BinaryIO) -> None: - self._input = input - self._output = output - self._paused = False - - def __getattr__(self, x: str) -> t.Any: - return getattr(self._input, x) - - def _echo(self, rv: bytes) -> bytes: - if not self._paused: - self._output.write(rv) - - return rv - - def read(self, n: int = -1) -> bytes: - return self._echo(self._input.read(n)) - - def read1(self, n: int = -1) -> bytes: - return self._echo(self._input.read1(n)) # type: ignore - - def readline(self, n: int = -1) -> bytes: - return self._echo(self._input.readline(n)) - - def readlines(self) -> t.List[bytes]: - return [self._echo(x) for x in self._input.readlines()] - - def __iter__(self) -> t.Iterator[bytes]: - return iter(self._echo(x) for x in self._input) - - def __repr__(self) -> str: - return repr(self._input) - - -@contextlib.contextmanager -def _pause_echo(stream: t.Optional[EchoingStdin]) -> t.Iterator[None]: - if stream is None: - yield - else: - stream._paused = True - yield - stream._paused = False - - -class _NamedTextIOWrapper(io.TextIOWrapper): - def __init__( - self, buffer: t.BinaryIO, name: str, mode: str, **kwargs: t.Any - ) -> None: - super().__init__(buffer, **kwargs) - self._name = name - self._mode = mode - - @property - def name(self) -> str: - return self._name - - @property - def mode(self) -> str: - return self._mode - - -def make_input_stream( - input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]], charset: str -) -> t.BinaryIO: - # Is already an input stream. - if hasattr(input, "read"): - rv = _find_binary_reader(t.cast(t.IO[t.Any], input)) - - if rv is not None: - return rv - - raise TypeError("Could not find binary reader for input stream.") - - if input is None: - input = b"" - elif isinstance(input, str): - input = input.encode(charset) - - return io.BytesIO(input) - - -class Result: - """Holds the captured result of an invoked CLI script.""" - - def __init__( - self, - runner: "CliRunner", - stdout_bytes: bytes, - stderr_bytes: t.Optional[bytes], - return_value: t.Any, - exit_code: int, - exception: t.Optional[BaseException], - exc_info: t.Optional[ - t.Tuple[t.Type[BaseException], BaseException, TracebackType] - ] = None, - ): - #: The runner that created the result - self.runner = runner - #: The standard output as bytes. - self.stdout_bytes = stdout_bytes - #: The standard error as bytes, or None if not available - self.stderr_bytes = stderr_bytes - #: The value returned from the invoked command. - #: - #: .. versionadded:: 8.0 - self.return_value = return_value - #: The exit code as integer. - self.exit_code = exit_code - #: The exception that happened if one did. - self.exception = exception - #: The traceback - self.exc_info = exc_info - - @property - def output(self) -> str: - """The (standard) output as unicode string.""" - return self.stdout - - @property - def stdout(self) -> str: - """The standard output as unicode string.""" - return self.stdout_bytes.decode(self.runner.charset, "replace").replace( - "\r\n", "\n" - ) - - @property - def stderr(self) -> str: - """The standard error as unicode string.""" - if self.stderr_bytes is None: - raise ValueError("stderr not separately captured") - return self.stderr_bytes.decode(self.runner.charset, "replace").replace( - "\r\n", "\n" - ) - - def __repr__(self) -> str: - exc_str = repr(self.exception) if self.exception else "okay" - return f"<{type(self).__name__} {exc_str}>" - - -class CliRunner: - """The CLI runner provides functionality to invoke a Click command line - script for unittesting purposes in a isolated environment. This only - works in single-threaded systems without any concurrency as it changes the - global interpreter state. - - :param charset: the character set for the input and output data. - :param env: a dictionary with environment variables for overriding. - :param echo_stdin: if this is set to `True`, then reading from stdin writes - to stdout. This is useful for showing examples in - some circumstances. Note that regular prompts - will automatically echo the input. - :param mix_stderr: if this is set to `False`, then stdout and stderr are - preserved as independent streams. This is useful for - Unix-philosophy apps that have predictable stdout and - noisy stderr, such that each may be measured - independently - """ - - def __init__( - self, - charset: str = "utf-8", - env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, - echo_stdin: bool = False, - mix_stderr: bool = True, - ) -> None: - self.charset = charset - self.env: t.Mapping[str, t.Optional[str]] = env or {} - self.echo_stdin = echo_stdin - self.mix_stderr = mix_stderr - - def get_default_prog_name(self, cli: "BaseCommand") -> str: - """Given a command object it will return the default program name - for it. The default is the `name` attribute or ``"root"`` if not - set. - """ - return cli.name or "root" - - def make_env( - self, overrides: t.Optional[t.Mapping[str, t.Optional[str]]] = None - ) -> t.Mapping[str, t.Optional[str]]: - """Returns the environment overrides for invoking a script.""" - rv = dict(self.env) - if overrides: - rv.update(overrides) - return rv - - @contextlib.contextmanager - def isolation( - self, - input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]] = None, - env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, - color: bool = False, - ) -> t.Iterator[t.Tuple[io.BytesIO, t.Optional[io.BytesIO]]]: - """A context manager that sets up the isolation for invoking of a - command line tool. This sets up stdin with the given input data - and `os.environ` with the overrides from the given dictionary. - This also rebinds some internals in Click to be mocked (like the - prompt functionality). - - This is automatically done in the :meth:`invoke` method. - - :param input: the input stream to put into sys.stdin. - :param env: the environment overrides as dictionary. - :param color: whether the output should contain color codes. The - application can still override this explicitly. - - .. versionchanged:: 8.0 - ``stderr`` is opened with ``errors="backslashreplace"`` - instead of the default ``"strict"``. - - .. versionchanged:: 4.0 - Added the ``color`` parameter. - """ - bytes_input = make_input_stream(input, self.charset) - echo_input = None - - old_stdin = sys.stdin - old_stdout = sys.stdout - old_stderr = sys.stderr - old_forced_width = formatting.FORCED_WIDTH - formatting.FORCED_WIDTH = 80 - - env = self.make_env(env) - - bytes_output = io.BytesIO() - - if self.echo_stdin: - bytes_input = echo_input = t.cast( - t.BinaryIO, EchoingStdin(bytes_input, bytes_output) - ) - - sys.stdin = text_input = _NamedTextIOWrapper( - bytes_input, encoding=self.charset, name="", mode="r" - ) - - if self.echo_stdin: - # Force unbuffered reads, otherwise TextIOWrapper reads a - # large chunk which is echoed early. - text_input._CHUNK_SIZE = 1 # type: ignore - - sys.stdout = _NamedTextIOWrapper( - bytes_output, encoding=self.charset, name="", mode="w" - ) - - bytes_error = None - if self.mix_stderr: - sys.stderr = sys.stdout - else: - bytes_error = io.BytesIO() - sys.stderr = _NamedTextIOWrapper( - bytes_error, - encoding=self.charset, - name="", - mode="w", - errors="backslashreplace", - ) - - @_pause_echo(echo_input) # type: ignore - def visible_input(prompt: t.Optional[str] = None) -> str: - sys.stdout.write(prompt or "") - val = text_input.readline().rstrip("\r\n") - sys.stdout.write(f"{val}\n") - sys.stdout.flush() - return val - - @_pause_echo(echo_input) # type: ignore - def hidden_input(prompt: t.Optional[str] = None) -> str: - sys.stdout.write(f"{prompt or ''}\n") - sys.stdout.flush() - return text_input.readline().rstrip("\r\n") - - @_pause_echo(echo_input) # type: ignore - def _getchar(echo: bool) -> str: - char = sys.stdin.read(1) - - if echo: - sys.stdout.write(char) - - sys.stdout.flush() - return char - - default_color = color - - def should_strip_ansi( - stream: t.Optional[t.IO[t.Any]] = None, color: t.Optional[bool] = None - ) -> bool: - if color is None: - return not default_color - return not color - - old_visible_prompt_func = termui.visible_prompt_func - old_hidden_prompt_func = termui.hidden_prompt_func - old__getchar_func = termui._getchar - old_should_strip_ansi = utils.should_strip_ansi # type: ignore - termui.visible_prompt_func = visible_input - termui.hidden_prompt_func = hidden_input - termui._getchar = _getchar - utils.should_strip_ansi = should_strip_ansi # type: ignore - - old_env = {} - try: - for key, value in env.items(): - old_env[key] = os.environ.get(key) - if value is None: - try: - del os.environ[key] - except Exception: - pass - else: - os.environ[key] = value - yield (bytes_output, bytes_error) - finally: - for key, value in old_env.items(): - if value is None: - try: - del os.environ[key] - except Exception: - pass - else: - os.environ[key] = value - sys.stdout = old_stdout - sys.stderr = old_stderr - sys.stdin = old_stdin - termui.visible_prompt_func = old_visible_prompt_func - termui.hidden_prompt_func = old_hidden_prompt_func - termui._getchar = old__getchar_func - utils.should_strip_ansi = old_should_strip_ansi # type: ignore - formatting.FORCED_WIDTH = old_forced_width - - def invoke( - self, - cli: "BaseCommand", - args: t.Optional[t.Union[str, t.Sequence[str]]] = None, - input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]] = None, - env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, - catch_exceptions: bool = True, - color: bool = False, - **extra: t.Any, - ) -> Result: - """Invokes a command in an isolated environment. The arguments are - forwarded directly to the command line script, the `extra` keyword - arguments are passed to the :meth:`~clickpkg.Command.main` function of - the command. - - This returns a :class:`Result` object. - - :param cli: the command to invoke - :param args: the arguments to invoke. It may be given as an iterable - or a string. When given as string it will be interpreted - as a Unix shell command. More details at - :func:`shlex.split`. - :param input: the input data for `sys.stdin`. - :param env: the environment overrides. - :param catch_exceptions: Whether to catch any other exceptions than - ``SystemExit``. - :param extra: the keyword arguments to pass to :meth:`main`. - :param color: whether the output should contain color codes. The - application can still override this explicitly. - - .. versionchanged:: 8.0 - The result object has the ``return_value`` attribute with - the value returned from the invoked command. - - .. versionchanged:: 4.0 - Added the ``color`` parameter. - - .. versionchanged:: 3.0 - Added the ``catch_exceptions`` parameter. - - .. versionchanged:: 3.0 - The result object has the ``exc_info`` attribute with the - traceback if available. - """ - exc_info = None - with self.isolation(input=input, env=env, color=color) as outstreams: - return_value = None - exception: t.Optional[BaseException] = None - exit_code = 0 - - if isinstance(args, str): - args = shlex.split(args) - - try: - prog_name = extra.pop("prog_name") - except KeyError: - prog_name = self.get_default_prog_name(cli) - - try: - return_value = cli.main(args=args or (), prog_name=prog_name, **extra) - except SystemExit as e: - exc_info = sys.exc_info() - e_code = t.cast(t.Optional[t.Union[int, t.Any]], e.code) - - if e_code is None: - e_code = 0 - - if e_code != 0: - exception = e - - if not isinstance(e_code, int): - sys.stdout.write(str(e_code)) - sys.stdout.write("\n") - e_code = 1 - - exit_code = e_code - - except Exception as e: - if not catch_exceptions: - raise - exception = e - exit_code = 1 - exc_info = sys.exc_info() - finally: - sys.stdout.flush() - stdout = outstreams[0].getvalue() - if self.mix_stderr: - stderr = None - else: - stderr = outstreams[1].getvalue() # type: ignore - - return Result( - runner=self, - stdout_bytes=stdout, - stderr_bytes=stderr, - return_value=return_value, - exit_code=exit_code, - exception=exception, - exc_info=exc_info, # type: ignore - ) - - @contextlib.contextmanager - def isolated_filesystem( - self, temp_dir: t.Optional[t.Union[str, "os.PathLike[str]"]] = None - ) -> t.Iterator[str]: - """A context manager that creates a temporary directory and - changes the current working directory to it. This isolates tests - that affect the contents of the CWD to prevent them from - interfering with each other. - - :param temp_dir: Create the temporary directory under this - directory. If given, the created directory is not removed - when exiting. - - .. versionchanged:: 8.0 - Added the ``temp_dir`` parameter. - """ - cwd = os.getcwd() - dt = tempfile.mkdtemp(dir=temp_dir) - os.chdir(dt) - - try: - yield dt - finally: - os.chdir(cwd) - - if temp_dir is None: - try: - shutil.rmtree(dt) - except OSError: # noqa: B014 - pass diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/types.py b/plotter-app/venv/lib/python3.8/site-packages/click/types.py deleted file mode 100644 index 2b1d179..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click/types.py +++ /dev/null @@ -1,1089 +0,0 @@ -import os -import stat -import sys -import typing as t -from datetime import datetime -from gettext import gettext as _ -from gettext import ngettext - -from ._compat import _get_argv_encoding -from ._compat import open_stream -from .exceptions import BadParameter -from .utils import format_filename -from .utils import LazyFile -from .utils import safecall - -if t.TYPE_CHECKING: - import typing_extensions as te - from .core import Context - from .core import Parameter - from .shell_completion import CompletionItem - - -class ParamType: - """Represents the type of a parameter. Validates and converts values - from the command line or Python into the correct type. - - To implement a custom type, subclass and implement at least the - following: - - - The :attr:`name` class attribute must be set. - - Calling an instance of the type with ``None`` must return - ``None``. This is already implemented by default. - - :meth:`convert` must convert string values to the correct type. - - :meth:`convert` must accept values that are already the correct - type. - - It must be able to convert a value if the ``ctx`` and ``param`` - arguments are ``None``. This can occur when converting prompt - input. - """ - - is_composite: t.ClassVar[bool] = False - arity: t.ClassVar[int] = 1 - - #: the descriptive name of this type - name: str - - #: if a list of this type is expected and the value is pulled from a - #: string environment variable, this is what splits it up. `None` - #: means any whitespace. For all parameters the general rule is that - #: whitespace splits them up. The exception are paths and files which - #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on - #: Windows). - envvar_list_splitter: t.ClassVar[t.Optional[str]] = None - - def to_info_dict(self) -> t.Dict[str, t.Any]: - """Gather information that could be useful for a tool generating - user-facing documentation. - - Use :meth:`click.Context.to_info_dict` to traverse the entire - CLI structure. - - .. versionadded:: 8.0 - """ - # The class name without the "ParamType" suffix. - param_type = type(self).__name__.partition("ParamType")[0] - param_type = param_type.partition("ParameterType")[0] - - # Custom subclasses might not remember to set a name. - if hasattr(self, "name"): - name = self.name - else: - name = param_type - - return {"param_type": param_type, "name": name} - - def __call__( - self, - value: t.Any, - param: t.Optional["Parameter"] = None, - ctx: t.Optional["Context"] = None, - ) -> t.Any: - if value is not None: - return self.convert(value, param, ctx) - - def get_metavar(self, param: "Parameter") -> t.Optional[str]: - """Returns the metavar default for this param if it provides one.""" - - def get_missing_message(self, param: "Parameter") -> t.Optional[str]: - """Optionally might return extra information about a missing - parameter. - - .. versionadded:: 2.0 - """ - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - """Convert the value to the correct type. This is not called if - the value is ``None`` (the missing value). - - This must accept string values from the command line, as well as - values that are already the correct type. It may also convert - other compatible types. - - The ``param`` and ``ctx`` arguments may be ``None`` in certain - situations, such as when converting prompt input. - - If the value cannot be converted, call :meth:`fail` with a - descriptive message. - - :param value: The value to convert. - :param param: The parameter that is using this type to convert - its value. May be ``None``. - :param ctx: The current context that arrived at this value. May - be ``None``. - """ - return value - - def split_envvar_value(self, rv: str) -> t.Sequence[str]: - """Given a value from an environment variable this splits it up - into small chunks depending on the defined envvar list splitter. - - If the splitter is set to `None`, which means that whitespace splits, - then leading and trailing whitespace is ignored. Otherwise, leading - and trailing splitters usually lead to empty items being included. - """ - return (rv or "").split(self.envvar_list_splitter) - - def fail( - self, - message: str, - param: t.Optional["Parameter"] = None, - ctx: t.Optional["Context"] = None, - ) -> "t.NoReturn": - """Helper method to fail with an invalid value message.""" - raise BadParameter(message, ctx=ctx, param=param) - - def shell_complete( - self, ctx: "Context", param: "Parameter", incomplete: str - ) -> t.List["CompletionItem"]: - """Return a list of - :class:`~click.shell_completion.CompletionItem` objects for the - incomplete value. Most types do not provide completions, but - some do, and this allows custom types to provide custom - completions as well. - - :param ctx: Invocation context for this command. - :param param: The parameter that is requesting completion. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - return [] - - -class CompositeParamType(ParamType): - is_composite = True - - @property - def arity(self) -> int: # type: ignore - raise NotImplementedError() - - -class FuncParamType(ParamType): - def __init__(self, func: t.Callable[[t.Any], t.Any]) -> None: - self.name: str = func.__name__ - self.func = func - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict["func"] = self.func - return info_dict - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - try: - return self.func(value) - except ValueError: - try: - value = str(value) - except UnicodeError: - value = value.decode("utf-8", "replace") - - self.fail(value, param, ctx) - - -class UnprocessedParamType(ParamType): - name = "text" - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - return value - - def __repr__(self) -> str: - return "UNPROCESSED" - - -class StringParamType(ParamType): - name = "text" - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - if isinstance(value, bytes): - enc = _get_argv_encoding() - try: - value = value.decode(enc) - except UnicodeError: - fs_enc = sys.getfilesystemencoding() - if fs_enc != enc: - try: - value = value.decode(fs_enc) - except UnicodeError: - value = value.decode("utf-8", "replace") - else: - value = value.decode("utf-8", "replace") - return value - return str(value) - - def __repr__(self) -> str: - return "STRING" - - -class Choice(ParamType): - """The choice type allows a value to be checked against a fixed set - of supported values. All of these values have to be strings. - - You should only pass a list or tuple of choices. Other iterables - (like generators) may lead to surprising results. - - The resulting value will always be one of the originally passed choices - regardless of ``case_sensitive`` or any ``ctx.token_normalize_func`` - being specified. - - See :ref:`choice-opts` for an example. - - :param case_sensitive: Set to false to make choices case - insensitive. Defaults to true. - """ - - name = "choice" - - def __init__(self, choices: t.Sequence[str], case_sensitive: bool = True) -> None: - self.choices = choices - self.case_sensitive = case_sensitive - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict["choices"] = self.choices - info_dict["case_sensitive"] = self.case_sensitive - return info_dict - - def get_metavar(self, param: "Parameter") -> str: - choices_str = "|".join(self.choices) - - # Use curly braces to indicate a required argument. - if param.required and param.param_type_name == "argument": - return f"{{{choices_str}}}" - - # Use square braces to indicate an option or optional argument. - return f"[{choices_str}]" - - def get_missing_message(self, param: "Parameter") -> str: - return _("Choose from:\n\t{choices}").format(choices=",\n\t".join(self.choices)) - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - # Match through normalization and case sensitivity - # first do token_normalize_func, then lowercase - # preserve original `value` to produce an accurate message in - # `self.fail` - normed_value = value - normed_choices = {choice: choice for choice in self.choices} - - if ctx is not None and ctx.token_normalize_func is not None: - normed_value = ctx.token_normalize_func(value) - normed_choices = { - ctx.token_normalize_func(normed_choice): original - for normed_choice, original in normed_choices.items() - } - - if not self.case_sensitive: - normed_value = normed_value.casefold() - normed_choices = { - normed_choice.casefold(): original - for normed_choice, original in normed_choices.items() - } - - if normed_value in normed_choices: - return normed_choices[normed_value] - - choices_str = ", ".join(map(repr, self.choices)) - self.fail( - ngettext( - "{value!r} is not {choice}.", - "{value!r} is not one of {choices}.", - len(self.choices), - ).format(value=value, choice=choices_str, choices=choices_str), - param, - ctx, - ) - - def __repr__(self) -> str: - return f"Choice({list(self.choices)})" - - def shell_complete( - self, ctx: "Context", param: "Parameter", incomplete: str - ) -> t.List["CompletionItem"]: - """Complete choices that start with the incomplete value. - - :param ctx: Invocation context for this command. - :param param: The parameter that is requesting completion. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - str_choices = map(str, self.choices) - - if self.case_sensitive: - matched = (c for c in str_choices if c.startswith(incomplete)) - else: - incomplete = incomplete.lower() - matched = (c for c in str_choices if c.lower().startswith(incomplete)) - - return [CompletionItem(c) for c in matched] - - -class DateTime(ParamType): - """The DateTime type converts date strings into `datetime` objects. - - The format strings which are checked are configurable, but default to some - common (non-timezone aware) ISO 8601 formats. - - When specifying *DateTime* formats, you should only pass a list or a tuple. - Other iterables, like generators, may lead to surprising results. - - The format strings are processed using ``datetime.strptime``, and this - consequently defines the format strings which are allowed. - - Parsing is tried using each format, in order, and the first format which - parses successfully is used. - - :param formats: A list or tuple of date format strings, in the order in - which they should be tried. Defaults to - ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``, - ``'%Y-%m-%d %H:%M:%S'``. - """ - - name = "datetime" - - def __init__(self, formats: t.Optional[t.Sequence[str]] = None): - self.formats: t.Sequence[str] = formats or [ - "%Y-%m-%d", - "%Y-%m-%dT%H:%M:%S", - "%Y-%m-%d %H:%M:%S", - ] - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict["formats"] = self.formats - return info_dict - - def get_metavar(self, param: "Parameter") -> str: - return f"[{'|'.join(self.formats)}]" - - def _try_to_convert_date(self, value: t.Any, format: str) -> t.Optional[datetime]: - try: - return datetime.strptime(value, format) - except ValueError: - return None - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - if isinstance(value, datetime): - return value - - for format in self.formats: - converted = self._try_to_convert_date(value, format) - - if converted is not None: - return converted - - formats_str = ", ".join(map(repr, self.formats)) - self.fail( - ngettext( - "{value!r} does not match the format {format}.", - "{value!r} does not match the formats {formats}.", - len(self.formats), - ).format(value=value, format=formats_str, formats=formats_str), - param, - ctx, - ) - - def __repr__(self) -> str: - return "DateTime" - - -class _NumberParamTypeBase(ParamType): - _number_class: t.ClassVar[t.Type[t.Any]] - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - try: - return self._number_class(value) - except ValueError: - self.fail( - _("{value!r} is not a valid {number_type}.").format( - value=value, number_type=self.name - ), - param, - ctx, - ) - - -class _NumberRangeBase(_NumberParamTypeBase): - def __init__( - self, - min: t.Optional[float] = None, - max: t.Optional[float] = None, - min_open: bool = False, - max_open: bool = False, - clamp: bool = False, - ) -> None: - self.min = min - self.max = max - self.min_open = min_open - self.max_open = max_open - self.clamp = clamp - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict.update( - min=self.min, - max=self.max, - min_open=self.min_open, - max_open=self.max_open, - clamp=self.clamp, - ) - return info_dict - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - import operator - - rv = super().convert(value, param, ctx) - lt_min: bool = self.min is not None and ( - operator.le if self.min_open else operator.lt - )(rv, self.min) - gt_max: bool = self.max is not None and ( - operator.ge if self.max_open else operator.gt - )(rv, self.max) - - if self.clamp: - if lt_min: - return self._clamp(self.min, 1, self.min_open) # type: ignore - - if gt_max: - return self._clamp(self.max, -1, self.max_open) # type: ignore - - if lt_min or gt_max: - self.fail( - _("{value} is not in the range {range}.").format( - value=rv, range=self._describe_range() - ), - param, - ctx, - ) - - return rv - - def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: - """Find the valid value to clamp to bound in the given - direction. - - :param bound: The boundary value. - :param dir: 1 or -1 indicating the direction to move. - :param open: If true, the range does not include the bound. - """ - raise NotImplementedError - - def _describe_range(self) -> str: - """Describe the range for use in help text.""" - if self.min is None: - op = "<" if self.max_open else "<=" - return f"x{op}{self.max}" - - if self.max is None: - op = ">" if self.min_open else ">=" - return f"x{op}{self.min}" - - lop = "<" if self.min_open else "<=" - rop = "<" if self.max_open else "<=" - return f"{self.min}{lop}x{rop}{self.max}" - - def __repr__(self) -> str: - clamp = " clamped" if self.clamp else "" - return f"<{type(self).__name__} {self._describe_range()}{clamp}>" - - -class IntParamType(_NumberParamTypeBase): - name = "integer" - _number_class = int - - def __repr__(self) -> str: - return "INT" - - -class IntRange(_NumberRangeBase, IntParamType): - """Restrict an :data:`click.INT` value to a range of accepted - values. See :ref:`ranges`. - - If ``min`` or ``max`` are not passed, any value is accepted in that - direction. If ``min_open`` or ``max_open`` are enabled, the - corresponding boundary is not included in the range. - - If ``clamp`` is enabled, a value outside the range is clamped to the - boundary instead of failing. - - .. versionchanged:: 8.0 - Added the ``min_open`` and ``max_open`` parameters. - """ - - name = "integer range" - - def _clamp( # type: ignore - self, bound: int, dir: "te.Literal[1, -1]", open: bool - ) -> int: - if not open: - return bound - - return bound + dir - - -class FloatParamType(_NumberParamTypeBase): - name = "float" - _number_class = float - - def __repr__(self) -> str: - return "FLOAT" - - -class FloatRange(_NumberRangeBase, FloatParamType): - """Restrict a :data:`click.FLOAT` value to a range of accepted - values. See :ref:`ranges`. - - If ``min`` or ``max`` are not passed, any value is accepted in that - direction. If ``min_open`` or ``max_open`` are enabled, the - corresponding boundary is not included in the range. - - If ``clamp`` is enabled, a value outside the range is clamped to the - boundary instead of failing. This is not supported if either - boundary is marked ``open``. - - .. versionchanged:: 8.0 - Added the ``min_open`` and ``max_open`` parameters. - """ - - name = "float range" - - def __init__( - self, - min: t.Optional[float] = None, - max: t.Optional[float] = None, - min_open: bool = False, - max_open: bool = False, - clamp: bool = False, - ) -> None: - super().__init__( - min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp - ) - - if (min_open or max_open) and clamp: - raise TypeError("Clamping is not supported for open bounds.") - - def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: - if not open: - return bound - - # Could use Python 3.9's math.nextafter here, but clamping an - # open float range doesn't seem to be particularly useful. It's - # left up to the user to write a callback to do it if needed. - raise RuntimeError("Clamping is not supported for open bounds.") - - -class BoolParamType(ParamType): - name = "boolean" - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - if value in {False, True}: - return bool(value) - - norm = value.strip().lower() - - if norm in {"1", "true", "t", "yes", "y", "on"}: - return True - - if norm in {"0", "false", "f", "no", "n", "off"}: - return False - - self.fail( - _("{value!r} is not a valid boolean.").format(value=value), param, ctx - ) - - def __repr__(self) -> str: - return "BOOL" - - -class UUIDParameterType(ParamType): - name = "uuid" - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - import uuid - - if isinstance(value, uuid.UUID): - return value - - value = value.strip() - - try: - return uuid.UUID(value) - except ValueError: - self.fail( - _("{value!r} is not a valid UUID.").format(value=value), param, ctx - ) - - def __repr__(self) -> str: - return "UUID" - - -class File(ParamType): - """Declares a parameter to be a file for reading or writing. The file - is automatically closed once the context tears down (after the command - finished working). - - Files can be opened for reading or writing. The special value ``-`` - indicates stdin or stdout depending on the mode. - - By default, the file is opened for reading text data, but it can also be - opened in binary mode or for writing. The encoding parameter can be used - to force a specific encoding. - - The `lazy` flag controls if the file should be opened immediately or upon - first IO. The default is to be non-lazy for standard input and output - streams as well as files opened for reading, `lazy` otherwise. When opening a - file lazily for reading, it is still opened temporarily for validation, but - will not be held open until first IO. lazy is mainly useful when opening - for writing to avoid creating the file until it is needed. - - Starting with Click 2.0, files can also be opened atomically in which - case all writes go into a separate file in the same folder and upon - completion the file will be moved over to the original location. This - is useful if a file regularly read by other users is modified. - - See :ref:`file-args` for more information. - """ - - name = "filename" - envvar_list_splitter: t.ClassVar[str] = os.path.pathsep - - def __init__( - self, - mode: str = "r", - encoding: t.Optional[str] = None, - errors: t.Optional[str] = "strict", - lazy: t.Optional[bool] = None, - atomic: bool = False, - ) -> None: - self.mode = mode - self.encoding = encoding - self.errors = errors - self.lazy = lazy - self.atomic = atomic - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict.update(mode=self.mode, encoding=self.encoding) - return info_dict - - def resolve_lazy_flag(self, value: "t.Union[str, os.PathLike[str]]") -> bool: - if self.lazy is not None: - return self.lazy - if os.fspath(value) == "-": - return False - elif "w" in self.mode: - return True - return False - - def convert( - self, - value: t.Union[str, "os.PathLike[str]", t.IO[t.Any]], - param: t.Optional["Parameter"], - ctx: t.Optional["Context"], - ) -> t.IO[t.Any]: - if _is_file_like(value): - return value - - value = t.cast("t.Union[str, os.PathLike[str]]", value) - - try: - lazy = self.resolve_lazy_flag(value) - - if lazy: - lf = LazyFile( - value, self.mode, self.encoding, self.errors, atomic=self.atomic - ) - - if ctx is not None: - ctx.call_on_close(lf.close_intelligently) - - return t.cast(t.IO[t.Any], lf) - - f, should_close = open_stream( - value, self.mode, self.encoding, self.errors, atomic=self.atomic - ) - - # If a context is provided, we automatically close the file - # at the end of the context execution (or flush out). If a - # context does not exist, it's the caller's responsibility to - # properly close the file. This for instance happens when the - # type is used with prompts. - if ctx is not None: - if should_close: - ctx.call_on_close(safecall(f.close)) - else: - ctx.call_on_close(safecall(f.flush)) - - return f - except OSError as e: # noqa: B014 - self.fail(f"'{format_filename(value)}': {e.strerror}", param, ctx) - - def shell_complete( - self, ctx: "Context", param: "Parameter", incomplete: str - ) -> t.List["CompletionItem"]: - """Return a special completion marker that tells the completion - system to use the shell to provide file path completions. - - :param ctx: Invocation context for this command. - :param param: The parameter that is requesting completion. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - return [CompletionItem(incomplete, type="file")] - - -def _is_file_like(value: t.Any) -> "te.TypeGuard[t.IO[t.Any]]": - return hasattr(value, "read") or hasattr(value, "write") - - -class Path(ParamType): - """The ``Path`` type is similar to the :class:`File` type, but - returns the filename instead of an open file. Various checks can be - enabled to validate the type of file and permissions. - - :param exists: The file or directory needs to exist for the value to - be valid. If this is not set to ``True``, and the file does not - exist, then all further checks are silently skipped. - :param file_okay: Allow a file as a value. - :param dir_okay: Allow a directory as a value. - :param readable: if true, a readable check is performed. - :param writable: if true, a writable check is performed. - :param executable: if true, an executable check is performed. - :param resolve_path: Make the value absolute and resolve any - symlinks. A ``~`` is not expanded, as this is supposed to be - done by the shell only. - :param allow_dash: Allow a single dash as a value, which indicates - a standard stream (but does not open it). Use - :func:`~click.open_file` to handle opening this value. - :param path_type: Convert the incoming path value to this type. If - ``None``, keep Python's default, which is ``str``. Useful to - convert to :class:`pathlib.Path`. - - .. versionchanged:: 8.1 - Added the ``executable`` parameter. - - .. versionchanged:: 8.0 - Allow passing ``path_type=pathlib.Path``. - - .. versionchanged:: 6.0 - Added the ``allow_dash`` parameter. - """ - - envvar_list_splitter: t.ClassVar[str] = os.path.pathsep - - def __init__( - self, - exists: bool = False, - file_okay: bool = True, - dir_okay: bool = True, - writable: bool = False, - readable: bool = True, - resolve_path: bool = False, - allow_dash: bool = False, - path_type: t.Optional[t.Type[t.Any]] = None, - executable: bool = False, - ): - self.exists = exists - self.file_okay = file_okay - self.dir_okay = dir_okay - self.readable = readable - self.writable = writable - self.executable = executable - self.resolve_path = resolve_path - self.allow_dash = allow_dash - self.type = path_type - - if self.file_okay and not self.dir_okay: - self.name: str = _("file") - elif self.dir_okay and not self.file_okay: - self.name = _("directory") - else: - self.name = _("path") - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict.update( - exists=self.exists, - file_okay=self.file_okay, - dir_okay=self.dir_okay, - writable=self.writable, - readable=self.readable, - allow_dash=self.allow_dash, - ) - return info_dict - - def coerce_path_result( - self, value: "t.Union[str, os.PathLike[str]]" - ) -> "t.Union[str, bytes, os.PathLike[str]]": - if self.type is not None and not isinstance(value, self.type): - if self.type is str: - return os.fsdecode(value) - elif self.type is bytes: - return os.fsencode(value) - else: - return t.cast("os.PathLike[str]", self.type(value)) - - return value - - def convert( - self, - value: "t.Union[str, os.PathLike[str]]", - param: t.Optional["Parameter"], - ctx: t.Optional["Context"], - ) -> "t.Union[str, bytes, os.PathLike[str]]": - rv = value - - is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-") - - if not is_dash: - if self.resolve_path: - # os.path.realpath doesn't resolve symlinks on Windows - # until Python 3.8. Use pathlib for now. - import pathlib - - rv = os.fsdecode(pathlib.Path(rv).resolve()) - - try: - st = os.stat(rv) - except OSError: - if not self.exists: - return self.coerce_path_result(rv) - self.fail( - _("{name} {filename!r} does not exist.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - - if not self.file_okay and stat.S_ISREG(st.st_mode): - self.fail( - _("{name} {filename!r} is a file.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - if not self.dir_okay and stat.S_ISDIR(st.st_mode): - self.fail( - _("{name} '{filename}' is a directory.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - - if self.readable and not os.access(rv, os.R_OK): - self.fail( - _("{name} {filename!r} is not readable.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - - if self.writable and not os.access(rv, os.W_OK): - self.fail( - _("{name} {filename!r} is not writable.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - - if self.executable and not os.access(value, os.X_OK): - self.fail( - _("{name} {filename!r} is not executable.").format( - name=self.name.title(), filename=format_filename(value) - ), - param, - ctx, - ) - - return self.coerce_path_result(rv) - - def shell_complete( - self, ctx: "Context", param: "Parameter", incomplete: str - ) -> t.List["CompletionItem"]: - """Return a special completion marker that tells the completion - system to use the shell to provide path completions for only - directories or any paths. - - :param ctx: Invocation context for this command. - :param param: The parameter that is requesting completion. - :param incomplete: Value being completed. May be empty. - - .. versionadded:: 8.0 - """ - from click.shell_completion import CompletionItem - - type = "dir" if self.dir_okay and not self.file_okay else "file" - return [CompletionItem(incomplete, type=type)] - - -class Tuple(CompositeParamType): - """The default behavior of Click is to apply a type on a value directly. - This works well in most cases, except for when `nargs` is set to a fixed - count and different types should be used for different items. In this - case the :class:`Tuple` type can be used. This type can only be used - if `nargs` is set to a fixed number. - - For more information see :ref:`tuple-type`. - - This can be selected by using a Python tuple literal as a type. - - :param types: a list of types that should be used for the tuple items. - """ - - def __init__(self, types: t.Sequence[t.Union[t.Type[t.Any], ParamType]]) -> None: - self.types: t.Sequence[ParamType] = [convert_type(ty) for ty in types] - - def to_info_dict(self) -> t.Dict[str, t.Any]: - info_dict = super().to_info_dict() - info_dict["types"] = [t.to_info_dict() for t in self.types] - return info_dict - - @property - def name(self) -> str: # type: ignore - return f"<{' '.join(ty.name for ty in self.types)}>" - - @property - def arity(self) -> int: # type: ignore - return len(self.types) - - def convert( - self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] - ) -> t.Any: - len_type = len(self.types) - len_value = len(value) - - if len_value != len_type: - self.fail( - ngettext( - "{len_type} values are required, but {len_value} was given.", - "{len_type} values are required, but {len_value} were given.", - len_value, - ).format(len_type=len_type, len_value=len_value), - param=param, - ctx=ctx, - ) - - return tuple(ty(x, param, ctx) for ty, x in zip(self.types, value)) - - -def convert_type(ty: t.Optional[t.Any], default: t.Optional[t.Any] = None) -> ParamType: - """Find the most appropriate :class:`ParamType` for the given Python - type. If the type isn't provided, it can be inferred from a default - value. - """ - guessed_type = False - - if ty is None and default is not None: - if isinstance(default, (tuple, list)): - # If the default is empty, ty will remain None and will - # return STRING. - if default: - item = default[0] - - # A tuple of tuples needs to detect the inner types. - # Can't call convert recursively because that would - # incorrectly unwind the tuple to a single type. - if isinstance(item, (tuple, list)): - ty = tuple(map(type, item)) - else: - ty = type(item) - else: - ty = type(default) - - guessed_type = True - - if isinstance(ty, tuple): - return Tuple(ty) - - if isinstance(ty, ParamType): - return ty - - if ty is str or ty is None: - return STRING - - if ty is int: - return INT - - if ty is float: - return FLOAT - - if ty is bool: - return BOOL - - if guessed_type: - return STRING - - if __debug__: - try: - if issubclass(ty, ParamType): - raise AssertionError( - f"Attempted to use an uninstantiated parameter type ({ty})." - ) - except TypeError: - # ty is an instance (correct), so issubclass fails. - pass - - return FuncParamType(ty) - - -#: A dummy parameter type that just does nothing. From a user's -#: perspective this appears to just be the same as `STRING` but -#: internally no string conversion takes place if the input was bytes. -#: This is usually useful when working with file paths as they can -#: appear in bytes and unicode. -#: -#: For path related uses the :class:`Path` type is a better choice but -#: there are situations where an unprocessed type is useful which is why -#: it is is provided. -#: -#: .. versionadded:: 4.0 -UNPROCESSED = UnprocessedParamType() - -#: A unicode string parameter type which is the implicit default. This -#: can also be selected by using ``str`` as type. -STRING = StringParamType() - -#: An integer parameter. This can also be selected by using ``int`` as -#: type. -INT = IntParamType() - -#: A floating point value parameter. This can also be selected by using -#: ``float`` as type. -FLOAT = FloatParamType() - -#: A boolean parameter. This is the default for boolean flags. This can -#: also be selected by using ``bool`` as a type. -BOOL = BoolParamType() - -#: A UUID parameter. -UUID = UUIDParameterType() diff --git a/plotter-app/venv/lib/python3.8/site-packages/click/utils.py b/plotter-app/venv/lib/python3.8/site-packages/click/utils.py deleted file mode 100644 index d536434..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/click/utils.py +++ /dev/null @@ -1,624 +0,0 @@ -import os -import re -import sys -import typing as t -from functools import update_wrapper -from types import ModuleType -from types import TracebackType - -from ._compat import _default_text_stderr -from ._compat import _default_text_stdout -from ._compat import _find_binary_writer -from ._compat import auto_wrap_for_ansi -from ._compat import binary_streams -from ._compat import open_stream -from ._compat import should_strip_ansi -from ._compat import strip_ansi -from ._compat import text_streams -from ._compat import WIN -from .globals import resolve_color_default - -if t.TYPE_CHECKING: - import typing_extensions as te - - P = te.ParamSpec("P") - -R = t.TypeVar("R") - - -def _posixify(name: str) -> str: - return "-".join(name.split()).lower() - - -def safecall(func: "t.Callable[P, R]") -> "t.Callable[P, t.Optional[R]]": - """Wraps a function so that it swallows exceptions.""" - - def wrapper(*args: "P.args", **kwargs: "P.kwargs") -> t.Optional[R]: - try: - return func(*args, **kwargs) - except Exception: - pass - return None - - return update_wrapper(wrapper, func) - - -def make_str(value: t.Any) -> str: - """Converts a value into a valid string.""" - if isinstance(value, bytes): - try: - return value.decode(sys.getfilesystemencoding()) - except UnicodeError: - return value.decode("utf-8", "replace") - return str(value) - - -def make_default_short_help(help: str, max_length: int = 45) -> str: - """Returns a condensed version of help string.""" - # Consider only the first paragraph. - paragraph_end = help.find("\n\n") - - if paragraph_end != -1: - help = help[:paragraph_end] - - # Collapse newlines, tabs, and spaces. - words = help.split() - - if not words: - return "" - - # The first paragraph started with a "no rewrap" marker, ignore it. - if words[0] == "\b": - words = words[1:] - - total_length = 0 - last_index = len(words) - 1 - - for i, word in enumerate(words): - total_length += len(word) + (i > 0) - - if total_length > max_length: # too long, truncate - break - - if word[-1] == ".": # sentence end, truncate without "..." - return " ".join(words[: i + 1]) - - if total_length == max_length and i != last_index: - break # not at sentence end, truncate with "..." - else: - return " ".join(words) # no truncation needed - - # Account for the length of the suffix. - total_length += len("...") - - # remove words until the length is short enough - while i > 0: - total_length -= len(words[i]) + (i > 0) - - if total_length <= max_length: - break - - i -= 1 - - return " ".join(words[:i]) + "..." - - -class LazyFile: - """A lazy file works like a regular file but it does not fully open - the file but it does perform some basic checks early to see if the - filename parameter does make sense. This is useful for safely opening - files for writing. - """ - - def __init__( - self, - filename: t.Union[str, "os.PathLike[str]"], - mode: str = "r", - encoding: t.Optional[str] = None, - errors: t.Optional[str] = "strict", - atomic: bool = False, - ): - self.name: str = os.fspath(filename) - self.mode = mode - self.encoding = encoding - self.errors = errors - self.atomic = atomic - self._f: t.Optional[t.IO[t.Any]] - self.should_close: bool - - if self.name == "-": - self._f, self.should_close = open_stream(filename, mode, encoding, errors) - else: - if "r" in mode: - # Open and close the file in case we're opening it for - # reading so that we can catch at least some errors in - # some cases early. - open(filename, mode).close() - self._f = None - self.should_close = True - - def __getattr__(self, name: str) -> t.Any: - return getattr(self.open(), name) - - def __repr__(self) -> str: - if self._f is not None: - return repr(self._f) - return f"" - - def open(self) -> t.IO[t.Any]: - """Opens the file if it's not yet open. This call might fail with - a :exc:`FileError`. Not handling this error will produce an error - that Click shows. - """ - if self._f is not None: - return self._f - try: - rv, self.should_close = open_stream( - self.name, self.mode, self.encoding, self.errors, atomic=self.atomic - ) - except OSError as e: # noqa: E402 - from .exceptions import FileError - - raise FileError(self.name, hint=e.strerror) from e - self._f = rv - return rv - - def close(self) -> None: - """Closes the underlying file, no matter what.""" - if self._f is not None: - self._f.close() - - def close_intelligently(self) -> None: - """This function only closes the file if it was opened by the lazy - file wrapper. For instance this will never close stdin. - """ - if self.should_close: - self.close() - - def __enter__(self) -> "LazyFile": - return self - - def __exit__( - self, - exc_type: t.Optional[t.Type[BaseException]], - exc_value: t.Optional[BaseException], - tb: t.Optional[TracebackType], - ) -> None: - self.close_intelligently() - - def __iter__(self) -> t.Iterator[t.AnyStr]: - self.open() - return iter(self._f) # type: ignore - - -class KeepOpenFile: - def __init__(self, file: t.IO[t.Any]) -> None: - self._file: t.IO[t.Any] = file - - def __getattr__(self, name: str) -> t.Any: - return getattr(self._file, name) - - def __enter__(self) -> "KeepOpenFile": - return self - - def __exit__( - self, - exc_type: t.Optional[t.Type[BaseException]], - exc_value: t.Optional[BaseException], - tb: t.Optional[TracebackType], - ) -> None: - pass - - def __repr__(self) -> str: - return repr(self._file) - - def __iter__(self) -> t.Iterator[t.AnyStr]: - return iter(self._file) - - -def echo( - message: t.Optional[t.Any] = None, - file: t.Optional[t.IO[t.Any]] = None, - nl: bool = True, - err: bool = False, - color: t.Optional[bool] = None, -) -> None: - """Print a message and newline to stdout or a file. This should be - used instead of :func:`print` because it provides better support - for different data, files, and environments. - - Compared to :func:`print`, this does the following: - - - Ensures that the output encoding is not misconfigured on Linux. - - Supports Unicode in the Windows console. - - Supports writing to binary outputs, and supports writing bytes - to text outputs. - - Supports colors and styles on Windows. - - Removes ANSI color and style codes if the output does not look - like an interactive terminal. - - Always flushes the output. - - :param message: The string or bytes to output. Other objects are - converted to strings. - :param file: The file to write to. Defaults to ``stdout``. - :param err: Write to ``stderr`` instead of ``stdout``. - :param nl: Print a newline after the message. Enabled by default. - :param color: Force showing or hiding colors and other styles. By - default Click will remove color if the output does not look like - an interactive terminal. - - .. versionchanged:: 6.0 - Support Unicode output on the Windows console. Click does not - modify ``sys.stdout``, so ``sys.stdout.write()`` and ``print()`` - will still not support Unicode. - - .. versionchanged:: 4.0 - Added the ``color`` parameter. - - .. versionadded:: 3.0 - Added the ``err`` parameter. - - .. versionchanged:: 2.0 - Support colors on Windows if colorama is installed. - """ - if file is None: - if err: - file = _default_text_stderr() - else: - file = _default_text_stdout() - - # There are no standard streams attached to write to. For example, - # pythonw on Windows. - if file is None: - return - - # Convert non bytes/text into the native string type. - if message is not None and not isinstance(message, (str, bytes, bytearray)): - out: t.Optional[t.Union[str, bytes]] = str(message) - else: - out = message - - if nl: - out = out or "" - if isinstance(out, str): - out += "\n" - else: - out += b"\n" - - if not out: - file.flush() - return - - # If there is a message and the value looks like bytes, we manually - # need to find the binary stream and write the message in there. - # This is done separately so that most stream types will work as you - # would expect. Eg: you can write to StringIO for other cases. - if isinstance(out, (bytes, bytearray)): - binary_file = _find_binary_writer(file) - - if binary_file is not None: - file.flush() - binary_file.write(out) - binary_file.flush() - return - - # ANSI style code support. For no message or bytes, nothing happens. - # When outputting to a file instead of a terminal, strip codes. - else: - color = resolve_color_default(color) - - if should_strip_ansi(file, color): - out = strip_ansi(out) - elif WIN: - if auto_wrap_for_ansi is not None: - file = auto_wrap_for_ansi(file) # type: ignore - elif not color: - out = strip_ansi(out) - - file.write(out) # type: ignore - file.flush() - - -def get_binary_stream(name: "te.Literal['stdin', 'stdout', 'stderr']") -> t.BinaryIO: - """Returns a system stream for byte processing. - - :param name: the name of the stream to open. Valid names are ``'stdin'``, - ``'stdout'`` and ``'stderr'`` - """ - opener = binary_streams.get(name) - if opener is None: - raise TypeError(f"Unknown standard stream '{name}'") - return opener() - - -def get_text_stream( - name: "te.Literal['stdin', 'stdout', 'stderr']", - encoding: t.Optional[str] = None, - errors: t.Optional[str] = "strict", -) -> t.TextIO: - """Returns a system stream for text processing. This usually returns - a wrapped stream around a binary stream returned from - :func:`get_binary_stream` but it also can take shortcuts for already - correctly configured streams. - - :param name: the name of the stream to open. Valid names are ``'stdin'``, - ``'stdout'`` and ``'stderr'`` - :param encoding: overrides the detected default encoding. - :param errors: overrides the default error mode. - """ - opener = text_streams.get(name) - if opener is None: - raise TypeError(f"Unknown standard stream '{name}'") - return opener(encoding, errors) - - -def open_file( - filename: str, - mode: str = "r", - encoding: t.Optional[str] = None, - errors: t.Optional[str] = "strict", - lazy: bool = False, - atomic: bool = False, -) -> t.IO[t.Any]: - """Open a file, with extra behavior to handle ``'-'`` to indicate - a standard stream, lazy open on write, and atomic write. Similar to - the behavior of the :class:`~click.File` param type. - - If ``'-'`` is given to open ``stdout`` or ``stdin``, the stream is - wrapped so that using it in a context manager will not close it. - This makes it possible to use the function without accidentally - closing a standard stream: - - .. code-block:: python - - with open_file(filename) as f: - ... - - :param filename: The name of the file to open, or ``'-'`` for - ``stdin``/``stdout``. - :param mode: The mode in which to open the file. - :param encoding: The encoding to decode or encode a file opened in - text mode. - :param errors: The error handling mode. - :param lazy: Wait to open the file until it is accessed. For read - mode, the file is temporarily opened to raise access errors - early, then closed until it is read again. - :param atomic: Write to a temporary file and replace the given file - on close. - - .. versionadded:: 3.0 - """ - if lazy: - return t.cast( - t.IO[t.Any], LazyFile(filename, mode, encoding, errors, atomic=atomic) - ) - - f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic) - - if not should_close: - f = t.cast(t.IO[t.Any], KeepOpenFile(f)) - - return f - - -def format_filename( - filename: "t.Union[str, bytes, os.PathLike[str], os.PathLike[bytes]]", - shorten: bool = False, -) -> str: - """Format a filename as a string for display. Ensures the filename can be - displayed by replacing any invalid bytes or surrogate escapes in the name - with the replacement character ``�``. - - Invalid bytes or surrogate escapes will raise an error when written to a - stream with ``errors="strict". This will typically happen with ``stdout`` - when the locale is something like ``en_GB.UTF-8``. - - Many scenarios *are* safe to write surrogates though, due to PEP 538 and - PEP 540, including: - - - Writing to ``stderr``, which uses ``errors="backslashreplace"``. - - The system has ``LANG=C.UTF-8``, ``C``, or ``POSIX``. Python opens - stdout and stderr with ``errors="surrogateescape"``. - - None of ``LANG/LC_*`` are set. Python assumes ``LANG=C.UTF-8``. - - Python is started in UTF-8 mode with ``PYTHONUTF8=1`` or ``-X utf8``. - Python opens stdout and stderr with ``errors="surrogateescape"``. - - :param filename: formats a filename for UI display. This will also convert - the filename into unicode without failing. - :param shorten: this optionally shortens the filename to strip of the - path that leads up to it. - """ - if shorten: - filename = os.path.basename(filename) - else: - filename = os.fspath(filename) - - if isinstance(filename, bytes): - filename = filename.decode(sys.getfilesystemencoding(), "replace") - else: - filename = filename.encode("utf-8", "surrogateescape").decode( - "utf-8", "replace" - ) - - return filename - - -def get_app_dir(app_name: str, roaming: bool = True, force_posix: bool = False) -> str: - r"""Returns the config folder for the application. The default behavior - is to return whatever is most appropriate for the operating system. - - To give you an idea, for an app called ``"Foo Bar"``, something like - the following folders could be returned: - - Mac OS X: - ``~/Library/Application Support/Foo Bar`` - Mac OS X (POSIX): - ``~/.foo-bar`` - Unix: - ``~/.config/foo-bar`` - Unix (POSIX): - ``~/.foo-bar`` - Windows (roaming): - ``C:\Users\\AppData\Roaming\Foo Bar`` - Windows (not roaming): - ``C:\Users\\AppData\Local\Foo Bar`` - - .. versionadded:: 2.0 - - :param app_name: the application name. This should be properly capitalized - and can contain whitespace. - :param roaming: controls if the folder should be roaming or not on Windows. - Has no effect otherwise. - :param force_posix: if this is set to `True` then on any POSIX system the - folder will be stored in the home folder with a leading - dot instead of the XDG config home or darwin's - application support folder. - """ - if WIN: - key = "APPDATA" if roaming else "LOCALAPPDATA" - folder = os.environ.get(key) - if folder is None: - folder = os.path.expanduser("~") - return os.path.join(folder, app_name) - if force_posix: - return os.path.join(os.path.expanduser(f"~/.{_posixify(app_name)}")) - if sys.platform == "darwin": - return os.path.join( - os.path.expanduser("~/Library/Application Support"), app_name - ) - return os.path.join( - os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), - _posixify(app_name), - ) - - -class PacifyFlushWrapper: - """This wrapper is used to catch and suppress BrokenPipeErrors resulting - from ``.flush()`` being called on broken pipe during the shutdown/final-GC - of the Python interpreter. Notably ``.flush()`` is always called on - ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any - other cleanup code, and the case where the underlying file is not a broken - pipe, all calls and attributes are proxied. - """ - - def __init__(self, wrapped: t.IO[t.Any]) -> None: - self.wrapped = wrapped - - def flush(self) -> None: - try: - self.wrapped.flush() - except OSError as e: - import errno - - if e.errno != errno.EPIPE: - raise - - def __getattr__(self, attr: str) -> t.Any: - return getattr(self.wrapped, attr) - - -def _detect_program_name( - path: t.Optional[str] = None, _main: t.Optional[ModuleType] = None -) -> str: - """Determine the command used to run the program, for use in help - text. If a file or entry point was executed, the file name is - returned. If ``python -m`` was used to execute a module or package, - ``python -m name`` is returned. - - This doesn't try to be too precise, the goal is to give a concise - name for help text. Files are only shown as their name without the - path. ``python`` is only shown for modules, and the full path to - ``sys.executable`` is not shown. - - :param path: The Python file being executed. Python puts this in - ``sys.argv[0]``, which is used by default. - :param _main: The ``__main__`` module. This should only be passed - during internal testing. - - .. versionadded:: 8.0 - Based on command args detection in the Werkzeug reloader. - - :meta private: - """ - if _main is None: - _main = sys.modules["__main__"] - - if not path: - path = sys.argv[0] - - # The value of __package__ indicates how Python was called. It may - # not exist if a setuptools script is installed as an egg. It may be - # set incorrectly for entry points created with pip on Windows. - # It is set to "" inside a Shiv or PEX zipapp. - if getattr(_main, "__package__", None) in {None, ""} or ( - os.name == "nt" - and _main.__package__ == "" - and not os.path.exists(path) - and os.path.exists(f"{path}.exe") - ): - # Executed a file, like "python app.py". - return os.path.basename(path) - - # Executed a module, like "python -m example". - # Rewritten by Python from "-m script" to "/path/to/script.py". - # Need to look at main module to determine how it was executed. - py_module = t.cast(str, _main.__package__) - name = os.path.splitext(os.path.basename(path))[0] - - # A submodule like "example.cli". - if name != "__main__": - py_module = f"{py_module}.{name}" - - return f"python -m {py_module.lstrip('.')}" - - -def _expand_args( - args: t.Iterable[str], - *, - user: bool = True, - env: bool = True, - glob_recursive: bool = True, -) -> t.List[str]: - """Simulate Unix shell expansion with Python functions. - - See :func:`glob.glob`, :func:`os.path.expanduser`, and - :func:`os.path.expandvars`. - - This is intended for use on Windows, where the shell does not do any - expansion. It may not exactly match what a Unix shell would do. - - :param args: List of command line arguments to expand. - :param user: Expand user home directory. - :param env: Expand environment variables. - :param glob_recursive: ``**`` matches directories recursively. - - .. versionchanged:: 8.1 - Invalid glob patterns are treated as empty expansions rather - than raising an error. - - .. versionadded:: 8.0 - - :meta private: - """ - from glob import glob - - out = [] - - for arg in args: - if user: - arg = os.path.expanduser(arg) - - if env: - arg = os.path.expandvars(arg) - - try: - matches = glob(arg, recursive=glob_recursive) - except re.error: - matches = [] - - if not matches: - out.append(arg) - else: - out.extend(matches) - - return out diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy-1.1.1.dist-info/INSTALLER b/plotter-app/venv/lib/python3.8/site-packages/contourpy-1.1.1.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/contourpy-1.1.1.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy-1.1.1.dist-info/LICENSE b/plotter-app/venv/lib/python3.8/site-packages/contourpy-1.1.1.dist-info/LICENSE deleted file mode 100644 index 90f83c9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/contourpy-1.1.1.dist-info/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -BSD 3-Clause License - -Copyright (c) 2021-2023, ContourPy 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: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy-1.1.1.dist-info/METADATA b/plotter-app/venv/lib/python3.8/site-packages/contourpy-1.1.1.dist-info/METADATA deleted file mode 100644 index 62b336a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/contourpy-1.1.1.dist-info/METADATA +++ /dev/null @@ -1,92 +0,0 @@ -Metadata-Version: 2.1 -Name: contourpy -Version: 1.1.1 -Summary: Python library for calculating contours of 2D quadrilateral grids -Author-Email: Ian Thomas -License: BSD 3-Clause License - - Copyright (c) 2021-2023, ContourPy 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: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: Intended Audience :: Science/Research -Classifier: License :: OSI Approved :: BSD License -Classifier: Programming Language :: C++ -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: Topic :: Scientific/Engineering :: Information Analysis -Classifier: Topic :: Scientific/Engineering :: Mathematics -Classifier: Topic :: Scientific/Engineering :: Visualization -Project-URL: Homepage, https://github.com/contourpy/contourpy -Project-URL: Changelog, https://contourpy.readthedocs.io/en/latest/changelog.html -Project-URL: Documentation, https://contourpy.readthedocs.io -Project-URL: Repository, https://github.com/contourpy/contourpy -Requires-Python: >=3.8 -Requires-Dist: numpy<2.0,>=1.16; python_version <= "3.11" -Requires-Dist: numpy<2.0,>=1.26.0rc1; python_version >= "3.12" -Requires-Dist: furo; extra == "docs" -Requires-Dist: sphinx>=7.2; extra == "docs" -Requires-Dist: sphinx-copybutton; extra == "docs" -Requires-Dist: bokeh; extra == "bokeh" -Requires-Dist: selenium; extra == "bokeh" -Requires-Dist: contourpy[bokeh,docs]; extra == "mypy" -Requires-Dist: docutils-stubs; extra == "mypy" -Requires-Dist: mypy==1.4.1; extra == "mypy" -Requires-Dist: types-Pillow; extra == "mypy" -Requires-Dist: contourpy[test-no-images]; extra == "test" -Requires-Dist: matplotlib; extra == "test" -Requires-Dist: Pillow; extra == "test" -Requires-Dist: pytest; extra == "test-no-images" -Requires-Dist: pytest-cov; extra == "test-no-images" -Requires-Dist: wurlitzer; extra == "test-no-images" -Provides-Extra: docs -Provides-Extra: bokeh -Provides-Extra: mypy -Provides-Extra: test -Provides-Extra: test-no-images -Description-Content-Type: text/markdown - -ContourPy - -ContourPy is a Python library for calculating contours of 2D quadrilateral grids. It is written in C++11 and wrapped using pybind11. - -It contains the 2005 and 2014 algorithms used in Matplotlib as well as a newer algorithm that includes more features and is available in both serial and multithreaded versions. It provides an easy way for Python libraries to use contouring algorithms without having to include Matplotlib as a dependency. - - * **Documentation**: https://contourpy.readthedocs.io - * **Source code**: https://github.com/contourpy/contourpy - -| | | -| --- | --- | -| Latest release | [![PyPI version](https://img.shields.io/pypi/v/contourpy.svg?label=pypi&color=fdae61)](https://pypi.python.org/pypi/contourpy) [![conda-forge version](https://img.shields.io/conda/v/conda-forge/contourpy.svg?label=conda-forge&color=a6d96a)](https://anaconda.org/conda-forge/contourpy) [![anaconda version](https://img.shields.io/conda/v/anaconda/contourpy.svg?label=anaconda&color=1a9641)](https://anaconda.org/anaconda/contourpy) | -| Downloads | [![PyPi downloads](https://img.shields.io/pypi/dm/contourpy?label=pypi&style=flat&color=fdae61)](https://pepy.tech/project/contourpy) [![conda-forge downloads](https://raw.githubusercontent.com/contourpy/condabadges/main/cache/contourpy_conda-forge_monthly.svg)](https://anaconda.org/conda-forge/contourpy) [![anaconda downloads](https://raw.githubusercontent.com/contourpy/condabadges/main/cache/contourpy_anaconda_monthly.svg)](https://anaconda.org/anaconda/contourpy) | -| Python version | [![Platforms](https://img.shields.io/pypi/pyversions/contourpy?color=fdae61)](https://pypi.org/project/contourpy/) | -| Coverage | [![Codecov](https://img.shields.io/codecov/c/gh/contourpy/contourpy?color=fdae61&label=codecov)](https://app.codecov.io/gh/contourpy/contourpy) | diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy-1.1.1.dist-info/RECORD b/plotter-app/venv/lib/python3.8/site-packages/contourpy-1.1.1.dist-info/RECORD deleted file mode 100644 index 396a540..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/contourpy-1.1.1.dist-info/RECORD +++ /dev/null @@ -1,32 +0,0 @@ -contourpy-1.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -contourpy-1.1.1.dist-info/LICENSE,sha256=wlIhVrp9Tfu-wmbf6pi_dqabxB7bGMrTIxd_rWsZs18,1534 -contourpy-1.1.1.dist-info/METADATA,sha256=E0-Lfn-vSsIaFqysxo1y8yyn2LIxSDkwjpOmNLMprR0,5891 -contourpy-1.1.1.dist-info/RECORD,, -contourpy-1.1.1.dist-info/WHEEL,sha256=oyQbq5IjZYS8SfdUe0MQOtuDNmUI8m4b8mbrJyBGQvU,133 -contourpy/__init__.py,sha256=kvosJVnzf-Xsw1CENK2CpjM5cOva2uEi_uLYX7k-Jrg,10673 -contourpy/__pycache__/__init__.cpython-38.pyc,, -contourpy/__pycache__/_version.cpython-38.pyc,, -contourpy/__pycache__/chunk.cpython-38.pyc,, -contourpy/__pycache__/enum_util.cpython-38.pyc,, -contourpy/_contourpy.cpython-38-x86_64-linux-gnu.so,sha256=GEn-_sYxUMSMQi5UnySeohD-iIX3uKzWMLhN3RLVNZc,834056 -contourpy/_contourpy.pyi,sha256=aslf7ofKu8Pja89J1gfz-TkRoLoPXNXh24G4R6v8p_M,6870 -contourpy/_version.py,sha256=q8_5C0f-8mHWNb6mMw02zlYPnEGXBqvOmP3z0CEwZKM,22 -contourpy/chunk.py,sha256=8njDQqlpuD22RjaaCyA75FXQsSQDY5hZGJSrxFpvGGU,3279 -contourpy/enum_util.py,sha256=dAyS9RV5VeqcUXvs_iwkWuCJSumfVjgA7X_O-6hyCJE,1146 -contourpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -contourpy/util/__init__.py,sha256=eVhJ_crOHL7nkG4Kb0dOo7NL4WHMy_Px665aAN_3d-8,118 -contourpy/util/__pycache__/__init__.cpython-38.pyc,, -contourpy/util/__pycache__/_build_config.cpython-38.pyc,, -contourpy/util/__pycache__/bokeh_renderer.cpython-38.pyc,, -contourpy/util/__pycache__/bokeh_util.cpython-38.pyc,, -contourpy/util/__pycache__/data.cpython-38.pyc,, -contourpy/util/__pycache__/mpl_renderer.cpython-38.pyc,, -contourpy/util/__pycache__/mpl_util.cpython-38.pyc,, -contourpy/util/__pycache__/renderer.cpython-38.pyc,, -contourpy/util/_build_config.py,sha256=qved0H62K530frmD7VPx9CFZCvVfDs-UlvdiQKml0sE,1813 -contourpy/util/bokeh_renderer.py,sha256=WQHN89F9Uezew_FMVPmJT2kE1ItHJ9Q5g7Flap28Osg,13550 -contourpy/util/bokeh_util.py,sha256=Hk2tqHKX-8-nk8twI-DuThAFGoCg7T9pnMWjo1IkN4Y,3488 -contourpy/util/data.py,sha256=7w_jD9qZuQV2iJsYhWM50dfZu4Qcl0SQ8AcoHoJTlfA,2567 -contourpy/util/mpl_renderer.py,sha256=YfOgtNWbfqzDjMhROY4c5-5hZKJXjHvu59n3YwbtZno,23729 -contourpy/util/mpl_util.py,sha256=EIblVkQmiY33YXnBwXcMq4rYUcsgYjZEZaOFj7pwPaE,3323 -contourpy/util/renderer.py,sha256=0SIBY8F1Nz9wR3b9d81tfYXjz_q_cxaFoEnqISb8vvA,2355 diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy-1.1.1.dist-info/WHEEL b/plotter-app/venv/lib/python3.8/site-packages/contourpy-1.1.1.dist-info/WHEEL deleted file mode 100644 index b48ddf7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/contourpy-1.1.1.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: meson -Root-Is-Purelib: false -Tag: cp38-cp38-manylinux_2_17_x86_64 -Tag: cp38-cp38-manylinux2014_x86_64 - diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/contourpy/__init__.py deleted file mode 100644 index 006d5f5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/contourpy/__init__.py +++ /dev/null @@ -1,253 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING - -import numpy as np - -from contourpy._contourpy import ( - ContourGenerator, FillType, LineType, Mpl2005ContourGenerator, Mpl2014ContourGenerator, - SerialContourGenerator, ThreadedContourGenerator, ZInterp, max_threads, -) -from contourpy._version import __version__ -from contourpy.chunk import calc_chunk_sizes -from contourpy.enum_util import as_fill_type, as_line_type, as_z_interp - -if TYPE_CHECKING: - from typing import Any - - from numpy.typing import ArrayLike - - from ._contourpy import CoordinateArray, MaskArray - -__all__ = [ - "__version__", - "contour_generator", - "max_threads", - "FillType", - "LineType", - "ContourGenerator", - "Mpl2005ContourGenerator", - "Mpl2014ContourGenerator", - "SerialContourGenerator", - "ThreadedContourGenerator", - "ZInterp", -] - - -# Simple mapping of algorithm name to class name. -_class_lookup: dict[str, type[ContourGenerator]] = dict( - mpl2005=Mpl2005ContourGenerator, - mpl2014=Mpl2014ContourGenerator, - serial=SerialContourGenerator, - threaded=ThreadedContourGenerator, -) - - -def _remove_z_mask( - z: ArrayLike | np.ma.MaskedArray[Any, Any] | None, -) -> tuple[CoordinateArray, MaskArray | None]: - # Preserve mask if present. - z_array = np.ma.asarray(z, dtype=np.float64) # type: ignore[no-untyped-call] - z_masked = np.ma.masked_invalid(z_array, copy=False) # type: ignore[no-untyped-call] - - if np.ma.is_masked(z_masked): # type: ignore[no-untyped-call] - mask = np.ma.getmask(z_masked) # type: ignore[no-untyped-call] - else: - mask = None - - return np.ma.getdata(z_masked), mask # type: ignore[no-untyped-call] - - -def contour_generator( - x: ArrayLike | None = None, - y: ArrayLike | None = None, - z: ArrayLike | np.ma.MaskedArray[Any, Any] | None = None, - *, - name: str = "serial", - corner_mask: bool | None = None, - line_type: LineType | str | None = None, - fill_type: FillType | str | None = None, - chunk_size: int | tuple[int, int] | None = None, - chunk_count: int | tuple[int, int] | None = None, - total_chunk_count: int | None = None, - quad_as_tri: bool = False, - z_interp: ZInterp | str | None = ZInterp.Linear, - thread_count: int = 0, -) -> ContourGenerator: - """Create and return a contour generator object. - - The class and properties of the contour generator are determined by the function arguments, - with sensible defaults. - - Args: - x (array-like of shape (ny, nx) or (nx,), optional): The x-coordinates of the ``z`` values. - May be 2D with the same shape as ``z.shape``, or 1D with length ``nx = z.shape[1]``. - If not specified are assumed to be ``np.arange(nx)``. Must be ordered monotonically. - y (array-like of shape (ny, nx) or (ny,), optional): The y-coordinates of the ``z`` values. - May be 2D with the same shape as ``z.shape``, or 1D with length ``ny = z.shape[0]``. - If not specified are assumed to be ``np.arange(ny)``. Must be ordered monotonically. - z (array-like of shape (ny, nx), may be a masked array): The 2D gridded values to calculate - the contours of. May be a masked array, and any invalid values (``np.inf`` or - ``np.nan``) will also be masked out. - name (str): Algorithm name, one of ``"serial"``, ``"threaded"``, ``"mpl2005"`` or - ``"mpl2014"``, default ``"serial"``. - corner_mask (bool, optional): Enable/disable corner masking, which only has an effect if - ``z`` is a masked array. If ``False``, any quad touching a masked point is masked out. - If ``True``, only the triangular corners of quads nearest these points are always masked - out, other triangular corners comprising three unmasked points are contoured as usual. - If not specified, uses the default provided by the algorithm ``name``. - line_type (LineType, optional): The format of contour line data returned from calls to - :meth:`~contourpy.ContourGenerator.lines`. If not specified, uses the default provided - by the algorithm ``name``. - fill_type (FillType, optional): The format of filled contour data returned from calls to - :meth:`~contourpy.ContourGenerator.filled`. If not specified, uses the default provided - by the algorithm ``name``. - chunk_size (int or tuple(int, int), optional): Chunk size in (y, x) directions, or the same - size in both directions if only one value is specified. - chunk_count (int or tuple(int, int), optional): Chunk count in (y, x) directions, or the - same count in both directions if only one value is specified. - total_chunk_count (int, optional): Total number of chunks. - quad_as_tri (bool): Enable/disable treating quads as 4 triangles, default ``False``. - If ``False``, a contour line within a quad is a straight line between points on two of - its edges. If ``True``, each full quad is divided into 4 triangles using a virtual point - at the centre (mean x, y of the corner points) and a contour line is piecewise linear - within those triangles. Corner-masked triangles are not affected by this setting, only - full unmasked quads. - z_interp (ZInterp): How to interpolate ``z`` values when determining where contour lines - intersect the edges of quads and the ``z`` values of the central points of quads, - default ``ZInterp.Linear``. - thread_count (int): Number of threads to use for contour calculation, default 0. Threads can - only be used with an algorithm ``name`` that supports threads (currently only - ``name="threaded"``) and there must be at least the same number of chunks as threads. - If ``thread_count=0`` and ``name="threaded"`` then it uses the maximum number of threads - as determined by the C++11 call ``std::thread::hardware_concurrency()``. If ``name`` is - something other than ``"threaded"`` then the ``thread_count`` will be set to ``1``. - - Return: - :class:`~contourpy._contourpy.ContourGenerator`. - - Note: - A maximum of one of ``chunk_size``, ``chunk_count`` and ``total_chunk_count`` may be - specified. - - Warning: - The ``name="mpl2005"`` algorithm does not implement chunking for contour lines. - """ - x = np.asarray(x, dtype=np.float64) - y = np.asarray(y, dtype=np.float64) - z, mask = _remove_z_mask(z) - - # Check arguments: z. - if z.ndim != 2: - raise TypeError(f"Input z must be 2D, not {z.ndim}D") - - if z.shape[0] < 2 or z.shape[1] < 2: - raise TypeError(f"Input z must be at least a (2, 2) shaped array, but has shape {z.shape}") - - ny, nx = z.shape - - # Check arguments: x and y. - if x.ndim != y.ndim: - raise TypeError(f"Number of dimensions of x ({x.ndim}) and y ({y.ndim}) do not match") - - if x.ndim == 0: - x = np.arange(nx, dtype=np.float64) - y = np.arange(ny, dtype=np.float64) - x, y = np.meshgrid(x, y) - elif x.ndim == 1: - if len(x) != nx: - raise TypeError(f"Length of x ({len(x)}) must match number of columns in z ({nx})") - if len(y) != ny: - raise TypeError(f"Length of y ({len(y)}) must match number of rows in z ({ny})") - x, y = np.meshgrid(x, y) - elif x.ndim == 2: - if x.shape != z.shape: - raise TypeError(f"Shapes of x {x.shape} and z {z.shape} do not match") - if y.shape != z.shape: - raise TypeError(f"Shapes of y {y.shape} and z {z.shape} do not match") - else: - raise TypeError(f"Inputs x and y must be None, 1D or 2D, not {x.ndim}D") - - # Check mask shape just in case. - if mask is not None and mask.shape != z.shape: - raise ValueError("If mask is set it must be a 2D array with the same shape as z") - - # Check arguments: name. - if name not in _class_lookup: - raise ValueError(f"Unrecognised contour generator name: {name}") - - # Check arguments: chunk_size, chunk_count and total_chunk_count. - y_chunk_size, x_chunk_size = calc_chunk_sizes( - chunk_size, chunk_count, total_chunk_count, ny, nx) - - cls = _class_lookup[name] - - # Check arguments: corner_mask. - if corner_mask is None: - # Set it to default, which is True if the algorithm supports it. - corner_mask = cls.supports_corner_mask() - elif corner_mask and not cls.supports_corner_mask(): - raise ValueError(f"{name} contour generator does not support corner_mask=True") - - # Check arguments: line_type. - if line_type is None: - line_type = cls.default_line_type - else: - line_type = as_line_type(line_type) - - if not cls.supports_line_type(line_type): - raise ValueError(f"{name} contour generator does not support line_type {line_type}") - - # Check arguments: fill_type. - if fill_type is None: - fill_type = cls.default_fill_type - else: - fill_type = as_fill_type(fill_type) - - if not cls.supports_fill_type(fill_type): - raise ValueError(f"{name} contour generator does not support fill_type {fill_type}") - - # Check arguments: quad_as_tri. - if quad_as_tri and not cls.supports_quad_as_tri(): - raise ValueError(f"{name} contour generator does not support quad_as_tri=True") - - # Check arguments: z_interp. - if z_interp is None: - z_interp = ZInterp.Linear - else: - z_interp = as_z_interp(z_interp) - - if z_interp != ZInterp.Linear and not cls.supports_z_interp(): - raise ValueError(f"{name} contour generator does not support z_interp {z_interp}") - - # Check arguments: thread_count. - if thread_count not in (0, 1) and not cls.supports_threads(): - raise ValueError(f"{name} contour generator does not support thread_count {thread_count}") - - # Prepare args and kwargs for contour generator constructor. - args = [x, y, z, mask] - kwargs: dict[str, int | bool | LineType | FillType | ZInterp] = { - "x_chunk_size": x_chunk_size, - "y_chunk_size": y_chunk_size, - } - - if name not in ("mpl2005", "mpl2014"): - kwargs["line_type"] = line_type - kwargs["fill_type"] = fill_type - - if cls.supports_corner_mask(): - kwargs["corner_mask"] = corner_mask - - if cls.supports_quad_as_tri(): - kwargs["quad_as_tri"] = quad_as_tri - - if cls.supports_z_interp(): - kwargs["z_interp"] = z_interp - - if cls.supports_threads(): - kwargs["thread_count"] = thread_count - - # Create contour generator. - cont_gen = cls(*args, **kwargs) - - return cont_gen diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/contourpy/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 55e86b5..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/contourpy/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/__pycache__/_version.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/contourpy/__pycache__/_version.cpython-38.pyc deleted file mode 100644 index 50e15e7..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/contourpy/__pycache__/_version.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/__pycache__/chunk.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/contourpy/__pycache__/chunk.cpython-38.pyc deleted file mode 100644 index 8087b71..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/contourpy/__pycache__/chunk.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/__pycache__/enum_util.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/contourpy/__pycache__/enum_util.cpython-38.pyc deleted file mode 100644 index 193a2fb..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/contourpy/__pycache__/enum_util.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/_contourpy.cpython-38-x86_64-linux-gnu.so b/plotter-app/venv/lib/python3.8/site-packages/contourpy/_contourpy.cpython-38-x86_64-linux-gnu.so deleted file mode 100755 index 250faa0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/contourpy/_contourpy.cpython-38-x86_64-linux-gnu.so and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/_contourpy.pyi b/plotter-app/venv/lib/python3.8/site-packages/contourpy/_contourpy.pyi deleted file mode 100644 index 3bc569f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/contourpy/_contourpy.pyi +++ /dev/null @@ -1,198 +0,0 @@ -from __future__ import annotations - -from typing import ClassVar, NoReturn - -import numpy as np -import numpy.typing as npt -from typing_extensions import TypeAlias - -import contourpy._contourpy as cpy - -# Input numpy array types, the same as in common.h -CoordinateArray: TypeAlias = npt.NDArray[np.float64] -MaskArray: TypeAlias = npt.NDArray[np.bool_] - -# Output numpy array types, the same as in common.h -PointArray: TypeAlias = npt.NDArray[np.float64] -CodeArray: TypeAlias = npt.NDArray[np.uint8] -OffsetArray: TypeAlias = npt.NDArray[np.uint32] - -# Types returned from filled() -FillReturn_OuterCode: TypeAlias = tuple[list[PointArray], list[CodeArray]] -FillReturn_OuterOffset: TypeAlias = tuple[list[PointArray], list[OffsetArray]] -FillReturn_ChunkCombinedCode: TypeAlias = tuple[list[PointArray | None], list[CodeArray | None]] -FillReturn_ChunkCombinedOffset: TypeAlias = tuple[list[PointArray | None], list[OffsetArray | None]] -FillReturn_ChunkCombinedCodeOffset: TypeAlias = tuple[list[PointArray | None], list[CodeArray | None], list[OffsetArray | None]] -FillReturn_ChunkCombinedOffsetOffset: TypeAlias = tuple[list[PointArray | None], list[OffsetArray | None], list[OffsetArray | None]] -FillReturn: TypeAlias = FillReturn_OuterCode | FillReturn_OuterOffset | FillReturn_ChunkCombinedCode | FillReturn_ChunkCombinedOffset | FillReturn_ChunkCombinedCodeOffset | FillReturn_ChunkCombinedOffsetOffset - -# Types returned from lines() -LineReturn_Separate: TypeAlias = list[PointArray] -LineReturn_SeparateCode: TypeAlias = tuple[list[PointArray], list[CodeArray]] -LineReturn_ChunkCombinedCode: TypeAlias = tuple[list[PointArray | None], list[CodeArray | None]] -LineReturn_ChunkCombinedOffset: TypeAlias = tuple[list[PointArray | None], list[OffsetArray | None]] -LineReturn: TypeAlias = LineReturn_Separate | LineReturn_SeparateCode | LineReturn_ChunkCombinedCode | LineReturn_ChunkCombinedOffset - - -NDEBUG: int -__version__: str - -class FillType: - ChunkCombinedCode: ClassVar[cpy.FillType] - ChunkCombinedCodeOffset: ClassVar[cpy.FillType] - ChunkCombinedOffset: ClassVar[cpy.FillType] - ChunkCombinedOffsetOffset: ClassVar[cpy.FillType] - OuterCode: ClassVar[cpy.FillType] - OuterOffset: ClassVar[cpy.FillType] - __members__: ClassVar[dict[str, cpy.FillType]] - def __eq__(self, other: object) -> bool: ... - def __getstate__(self) -> int: ... - def __hash__(self) -> int: ... - def __index__(self) -> int: ... - def __init__(self, value: int) -> None: ... - def __int__(self) -> int: ... - def __ne__(self, other: object) -> bool: ... - def __repr__(self) -> str: ... - def __setstate__(self, state: int) -> NoReturn: ... - @property - def name(self) -> str: ... - @property - def value(self) -> int: ... - -class LineType: - ChunkCombinedCode: ClassVar[cpy.LineType] - ChunkCombinedOffset: ClassVar[cpy.LineType] - Separate: ClassVar[cpy.LineType] - SeparateCode: ClassVar[cpy.LineType] - __members__: ClassVar[dict[str, cpy.LineType]] - def __eq__(self, other: object) -> bool: ... - def __getstate__(self) -> int: ... - def __hash__(self) -> int: ... - def __index__(self) -> int: ... - def __init__(self, value: int) -> None: ... - def __int__(self) -> int: ... - def __ne__(self, other: object) -> bool: ... - def __repr__(self) -> str: ... - def __setstate__(self, state: int) -> NoReturn: ... - @property - def name(self) -> str: ... - @property - def value(self) -> int: ... - -class ZInterp: - Linear: ClassVar[cpy.ZInterp] - Log: ClassVar[cpy.ZInterp] - __members__: ClassVar[dict[str, cpy.ZInterp]] - def __eq__(self, other: object) -> bool: ... - def __getstate__(self) -> int: ... - def __hash__(self) -> int: ... - def __index__(self) -> int: ... - def __init__(self, value: int) -> None: ... - def __int__(self) -> int: ... - def __ne__(self, other: object) -> bool: ... - def __repr__(self) -> str: ... - def __setstate__(self, state: int) -> NoReturn: ... - @property - def name(self) -> str: ... - @property - def value(self) -> int: ... - -def max_threads() -> int: ... - -class ContourGenerator: - def create_contour(self, level: float) -> LineReturn: ... - def create_filled_contour(self, lower_level: float, upper_level: float) -> FillReturn: ... - def filled(self, lower_level: float, upper_level: float) -> FillReturn: ... - def lines(self, level: float) -> LineReturn: ... - @staticmethod - def supports_corner_mask() -> bool: ... - @staticmethod - def supports_fill_type(fill_type: FillType) -> bool: ... - @staticmethod - def supports_line_type(line_type: LineType) -> bool: ... - @staticmethod - def supports_quad_as_tri() -> bool: ... - @staticmethod - def supports_threads() -> bool: ... - @staticmethod - def supports_z_interp() -> bool: ... - @property - def chunk_count(self) -> tuple[int, int]: ... - @property - def chunk_size(self) -> tuple[int, int]: ... - @property - def corner_mask(self) -> bool: ... - @property - def fill_type(self) -> FillType: ... - @property - def line_type(self) -> LineType: ... - @property - def quad_as_tri(self) -> bool: ... - @property - def thread_count(self) -> int: ... - @property - def z_interp(self) -> ZInterp: ... - default_fill_type: cpy.FillType - default_line_type: cpy.LineType - -class Mpl2005ContourGenerator(ContourGenerator): - def __init__( - self, - x: CoordinateArray, - y: CoordinateArray, - z: CoordinateArray, - mask: MaskArray, - *, - x_chunk_size: int = 0, - y_chunk_size: int = 0, - ) -> None: ... - -class Mpl2014ContourGenerator(ContourGenerator): - def __init__( - self, - x: CoordinateArray, - y: CoordinateArray, - z: CoordinateArray, - mask: MaskArray, - *, - corner_mask: bool, - x_chunk_size: int = 0, - y_chunk_size: int = 0, - ) -> None: ... - -class SerialContourGenerator(ContourGenerator): - def __init__( - self, - x: CoordinateArray, - y: CoordinateArray, - z: CoordinateArray, - mask: MaskArray, - *, - corner_mask: bool, - line_type: LineType, - fill_type: FillType, - quad_as_tri: bool, - z_interp: ZInterp, - x_chunk_size: int = 0, - y_chunk_size: int = 0, - ) -> None: ... - def _write_cache(self) -> NoReturn: ... - -class ThreadedContourGenerator(ContourGenerator): - def __init__( - self, - x: CoordinateArray, - y: CoordinateArray, - z: CoordinateArray, - mask: MaskArray, - *, - corner_mask: bool, - line_type: LineType, - fill_type: FillType, - quad_as_tri: bool, - z_interp: ZInterp, - x_chunk_size: int = 0, - y_chunk_size: int = 0, - thread_count: int = 0, - ) -> None: ... - def _write_cache(self) -> None: ... diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/_version.py b/plotter-app/venv/lib/python3.8/site-packages/contourpy/_version.py deleted file mode 100644 index a82b376..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/contourpy/_version.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "1.1.1" diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/chunk.py b/plotter-app/venv/lib/python3.8/site-packages/contourpy/chunk.py deleted file mode 100644 index 94fded1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/contourpy/chunk.py +++ /dev/null @@ -1,95 +0,0 @@ -from __future__ import annotations - -import math - - -def calc_chunk_sizes( - chunk_size: int | tuple[int, int] | None, - chunk_count: int | tuple[int, int] | None, - total_chunk_count: int | None, - ny: int, - nx: int, -) -> tuple[int, int]: - """Calculate chunk sizes. - - Args: - chunk_size (int or tuple(int, int), optional): Chunk size in (y, x) directions, or the same - size in both directions if only one is specified. Cannot be negative. - chunk_count (int or tuple(int, int), optional): Chunk count in (y, x) directions, or the - same count in both directions if only one is specified. If less than 1, set to 1. - total_chunk_count (int, optional): Total number of chunks. If less than 1, set to 1. - ny (int): Number of grid points in y-direction. - nx (int): Number of grid points in x-direction. - - Return: - tuple(int, int): Chunk sizes (y_chunk_size, x_chunk_size). - - Note: - Zero or one of ``chunk_size``, ``chunk_count`` and ``total_chunk_count`` should be - specified. - """ - if sum([chunk_size is not None, chunk_count is not None, total_chunk_count is not None]) > 1: - raise ValueError("Only one of chunk_size, chunk_count and total_chunk_count should be set") - - if nx < 2 or ny < 2: - raise ValueError(f"(ny, nx) must be at least (2, 2), not ({ny}, {nx})") - - if total_chunk_count is not None: - max_chunk_count = (nx-1)*(ny-1) - total_chunk_count = min(max(total_chunk_count, 1), max_chunk_count) - if total_chunk_count == 1: - chunk_size = 0 - elif total_chunk_count == max_chunk_count: - chunk_size = (1, 1) - else: - factors = two_factors(total_chunk_count) - if ny > nx: - chunk_count = factors - else: - chunk_count = (factors[1], factors[0]) - - if chunk_count is not None: - if isinstance(chunk_count, tuple): - y_chunk_count, x_chunk_count = chunk_count - else: - y_chunk_count = x_chunk_count = chunk_count - x_chunk_count = min(max(x_chunk_count, 1), nx-1) - y_chunk_count = min(max(y_chunk_count, 1), ny-1) - chunk_size = (math.ceil((ny-1) / y_chunk_count), math.ceil((nx-1) / x_chunk_count)) - - if chunk_size is None: - y_chunk_size = x_chunk_size = 0 - elif isinstance(chunk_size, tuple): - y_chunk_size, x_chunk_size = chunk_size - else: - y_chunk_size = x_chunk_size = chunk_size - - if x_chunk_size < 0 or y_chunk_size < 0: - raise ValueError("chunk_size cannot be negative") - - return y_chunk_size, x_chunk_size - - -def two_factors(n: int) -> tuple[int, int]: - """Split an integer into two integer factors. - - The two factors will be as close as possible to the sqrt of n, and are returned in decreasing - order. Worst case returns (n, 1). - - Args: - n (int): The integer to factorize, must be positive. - - Return: - tuple(int, int): The two factors of n, in decreasing order. - """ - if n < 0: - raise ValueError(f"two_factors expects positive integer not {n}") - - i = math.ceil(math.sqrt(n)) - while n % i != 0: - i -= 1 - j = n // i - if i > j: - return i, j - else: - return j, i diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/enum_util.py b/plotter-app/venv/lib/python3.8/site-packages/contourpy/enum_util.py deleted file mode 100644 index 914d5d8..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/contourpy/enum_util.py +++ /dev/null @@ -1,48 +0,0 @@ -from __future__ import annotations - -from contourpy._contourpy import FillType, LineType, ZInterp - - -def as_fill_type(fill_type: FillType | str) -> FillType: - """Coerce a FillType or string value to a FillType. - - Args: - fill_type (FillType or str): Value to convert. - - Return: - FillType: Converted value. - """ - if isinstance(fill_type, str): - return FillType.__members__[fill_type] - else: - return fill_type - - -def as_line_type(line_type: LineType | str) -> LineType: - """Coerce a LineType or string value to a LineType. - - Args: - line_type (LineType or str): Value to convert. - - Return: - LineType: Converted value. - """ - if isinstance(line_type, str): - return LineType.__members__[line_type] - else: - return line_type - - -def as_z_interp(z_interp: ZInterp | str) -> ZInterp: - """Coerce a ZInterp or string value to a ZInterp. - - Args: - z_interp (ZInterp or str): Value to convert. - - Return: - ZInterp: Converted value. - """ - if isinstance(z_interp, str): - return ZInterp.__members__[z_interp] - else: - return z_interp diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/py.typed b/plotter-app/venv/lib/python3.8/site-packages/contourpy/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__init__.py deleted file mode 100644 index fe33fce..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from __future__ import annotations - -from contourpy.util._build_config import build_config - -__all__ = ["build_config"] diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index d81d787..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/_build_config.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/_build_config.cpython-38.pyc deleted file mode 100644 index 9e9550b..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/_build_config.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/bokeh_renderer.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/bokeh_renderer.cpython-38.pyc deleted file mode 100644 index ef10278..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/bokeh_renderer.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/bokeh_util.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/bokeh_util.cpython-38.pyc deleted file mode 100644 index cfb7e1c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/bokeh_util.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/data.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/data.cpython-38.pyc deleted file mode 100644 index b08fd65..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/data.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/mpl_renderer.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/mpl_renderer.cpython-38.pyc deleted file mode 100644 index 74ee63c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/mpl_renderer.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/mpl_util.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/mpl_util.cpython-38.pyc deleted file mode 100644 index 5f9b9e8..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/mpl_util.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/renderer.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/renderer.cpython-38.pyc deleted file mode 100644 index e4919a8..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/__pycache__/renderer.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/_build_config.py b/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/_build_config.py deleted file mode 100644 index de48586..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/_build_config.py +++ /dev/null @@ -1,58 +0,0 @@ -# _build_config.py.in is converted into _build_config.py during the meson build process. - -from __future__ import annotations - - -def build_config() -> dict[str, str]: - """ - Return a dictionary containing build configuration settings. - - All dictionary keys and values are strings, for example ``False`` is - returned as ``"False"``. - """ - return dict( - # Python settings - python_version="3.8", - python_install_dir=r"/usr/local/lib/python3.8/site-packages/", - python_path=r"/tmp/build-env-dhtgjwl5/bin/python", - - # Package versions - contourpy_version="1.1.1", - meson_version="1.2.1", - mesonpy_version="0.14.0", - pybind11_version="2.11.1", - - # Misc meson settings - meson_backend="ninja", - build_dir=r"/project/.mesonpy-otn0iaj2/lib/contourpy/util", - source_dir=r"/project/lib/contourpy/util", - cross_build="False", - - # Build options - build_options=r"-Dbuildtype=release -Db_ndebug=if-release -Db_vscrt=md -Dvsenv=True --native-file=/project/.mesonpy-otn0iaj2/meson-python-native-file.ini", - buildtype="release", - cpp_std="c++17", - debug="False", - optimization="3", - vsenv="True", - b_ndebug="if-release", - b_vscrt="from_buildtype", - - # C++ compiler - compiler_name="gcc", - compiler_version="10.2.1", - linker_id="ld.bfd", - compile_command="c++", - - # Host machine - host_cpu="x86_64", - host_cpu_family="x86_64", - host_cpu_endian="little", - host_cpu_system="linux", - - # Build machine, same as host machine if not a cross_build - build_cpu="x86_64", - build_cpu_family="x86_64", - build_cpu_endian="little", - build_cpu_system="linux", - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/bokeh_renderer.py b/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/bokeh_renderer.py deleted file mode 100644 index 46cea0b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/bokeh_renderer.py +++ /dev/null @@ -1,329 +0,0 @@ -from __future__ import annotations - -import io -from typing import TYPE_CHECKING, Any - -from bokeh.io import export_png, export_svg, show -from bokeh.io.export import get_screenshot_as_png -from bokeh.layouts import gridplot -from bokeh.models.annotations.labels import Label -from bokeh.palettes import Category10 -from bokeh.plotting import figure -import numpy as np - -from contourpy import FillType, LineType -from contourpy.util.bokeh_util import filled_to_bokeh, lines_to_bokeh -from contourpy.util.renderer import Renderer - -if TYPE_CHECKING: - from bokeh.models import GridPlot - from bokeh.palettes import Palette - from numpy.typing import ArrayLike - from selenium.webdriver.remote.webdriver import WebDriver - - from contourpy._contourpy import FillReturn, LineReturn - - -class BokehRenderer(Renderer): - _figures: list[figure] - _layout: GridPlot - _palette: Palette - _want_svg: bool - - """Utility renderer using Bokeh to render a grid of plots over the same (x, y) range. - - Args: - nrows (int, optional): Number of rows of plots, default ``1``. - ncols (int, optional): Number of columns of plots, default ``1``. - figsize (tuple(float, float), optional): Figure size in inches (assuming 100 dpi), default - ``(9, 9)``. - show_frame (bool, optional): Whether to show frame and axes ticks, default ``True``. - want_svg (bool, optional): Whether output is required in SVG format or not, default - ``False``. - - Warning: - :class:`~contourpy.util.bokeh_renderer.BokehRenderer`, unlike - :class:`~contourpy.util.mpl_renderer.MplRenderer`, needs to be told in advance if output to - SVG format will be required later, otherwise it will assume PNG output. - """ - def __init__( - self, - nrows: int = 1, - ncols: int = 1, - figsize: tuple[float, float] = (9, 9), - show_frame: bool = True, - want_svg: bool = False, - ) -> None: - self._want_svg = want_svg - self._palette = Category10[10] - - total_size = 100*np.asarray(figsize, dtype=int) # Assuming 100 dpi. - - nfigures = nrows*ncols - self._figures = [] - backend = "svg" if self._want_svg else "canvas" - for _ in range(nfigures): - fig = figure(output_backend=backend) - fig.xgrid.visible = False - fig.ygrid.visible = False - self._figures.append(fig) - if not show_frame: - fig.outline_line_color = None # type: ignore[assignment] - fig.axis.visible = False - - self._layout = gridplot( - self._figures, ncols=ncols, toolbar_location=None, # type: ignore[arg-type] - width=total_size[0] // ncols, height=total_size[1] // nrows) - - def _convert_color(self, color: str) -> str: - if isinstance(color, str) and color[0] == "C": - index = int(color[1:]) - color = self._palette[index] - return color - - def _get_figure(self, ax: figure | int) -> figure: - if isinstance(ax, int): - ax = self._figures[ax] - return ax - - def filled( - self, - filled: FillReturn, - fill_type: FillType, - ax: figure | int = 0, - color: str = "C0", - alpha: float = 0.7, - ) -> None: - """Plot filled contours on a single plot. - - Args: - filled (sequence of arrays): Filled contour data as returned by - :func:`~contourpy.ContourGenerator.filled`. - fill_type (FillType): Type of ``filled`` data, as returned by - :attr:`~contourpy.ContourGenerator.fill_type`. - ax (int or Bokeh Figure, optional): Which plot to use, default ``0``. - color (str, optional): Color to plot with. May be a string color or the letter ``"C"`` - followed by an integer in the range ``"C0"`` to ``"C9"`` to use a color from the - ``Category10`` palette. Default ``"C0"``. - alpha (float, optional): Opacity to plot with, default ``0.7``. - """ - fig = self._get_figure(ax) - color = self._convert_color(color) - xs, ys = filled_to_bokeh(filled, fill_type) - if len(xs) > 0: - fig.multi_polygons(xs=[xs], ys=[ys], color=color, fill_alpha=alpha, line_width=0) - - def grid( - self, - x: ArrayLike, - y: ArrayLike, - ax: figure | int = 0, - color: str = "black", - alpha: float = 0.1, - point_color: str | None = None, - quad_as_tri_alpha: float = 0, - ) -> None: - """Plot quad grid lines on a single plot. - - Args: - x (array-like of shape (ny, nx) or (nx,)): The x-coordinates of the grid points. - y (array-like of shape (ny, nx) or (ny,)): The y-coordinates of the grid points. - ax (int or Bokeh Figure, optional): Which plot to use, default ``0``. - color (str, optional): Color to plot grid lines, default ``"black"``. - alpha (float, optional): Opacity to plot lines with, default ``0.1``. - point_color (str, optional): Color to plot grid points or ``None`` if grid points - should not be plotted, default ``None``. - quad_as_tri_alpha (float, optional): Opacity to plot ``quad_as_tri`` grid, default - ``0``. - - Colors may be a string color or the letter ``"C"`` followed by an integer in the range - ``"C0"`` to ``"C9"`` to use a color from the ``Category10`` palette. - - Warning: - ``quad_as_tri_alpha > 0`` plots all quads as though they are unmasked. - """ - fig = self._get_figure(ax) - x, y = self._grid_as_2d(x, y) - xs = [row for row in x] + [row for row in x.T] - ys = [row for row in y] + [row for row in y.T] - kwargs = dict(line_color=color, alpha=alpha) - fig.multi_line(xs, ys, **kwargs) - if quad_as_tri_alpha > 0: - # Assumes no quad mask. - xmid = (0.25*(x[:-1, :-1] + x[1:, :-1] + x[:-1, 1:] + x[1:, 1:])).ravel() - ymid = (0.25*(y[:-1, :-1] + y[1:, :-1] + y[:-1, 1:] + y[1:, 1:])).ravel() - fig.multi_line( - [row for row in np.stack((x[:-1, :-1].ravel(), xmid, x[1:, 1:].ravel()), axis=1)], - [row for row in np.stack((y[:-1, :-1].ravel(), ymid, y[1:, 1:].ravel()), axis=1)], - **kwargs) - fig.multi_line( - [row for row in np.stack((x[:-1, 1:].ravel(), xmid, x[1:, :-1].ravel()), axis=1)], - [row for row in np.stack((y[:-1, 1:].ravel(), ymid, y[1:, :-1].ravel()), axis=1)], - **kwargs) - if point_color is not None: - fig.circle( - x=x.ravel(), y=y.ravel(), fill_color=color, line_color=None, alpha=alpha, size=8) - - def lines( - self, - lines: LineReturn, - line_type: LineType, - ax: figure | int = 0, - color: str = "C0", - alpha: float = 1.0, - linewidth: float = 1, - ) -> None: - """Plot contour lines on a single plot. - - Args: - lines (sequence of arrays): Contour line data as returned by - :func:`~contourpy.ContourGenerator.lines`. - line_type (LineType): Type of ``lines`` data, as returned by - :attr:`~contourpy.ContourGenerator.line_type`. - ax (int or Bokeh Figure, optional): Which plot to use, default ``0``. - color (str, optional): Color to plot lines. May be a string color or the letter ``"C"`` - followed by an integer in the range ``"C0"`` to ``"C9"`` to use a color from the - ``Category10`` palette. Default ``"C0"``. - alpha (float, optional): Opacity to plot lines with, default ``1.0``. - linewidth (float, optional): Width of lines, default ``1``. - - Note: - Assumes all lines are open line strips not closed line loops. - """ - fig = self._get_figure(ax) - color = self._convert_color(color) - xs, ys = lines_to_bokeh(lines, line_type) - if len(xs) > 0: - fig.multi_line(xs, ys, line_color=color, line_alpha=alpha, line_width=linewidth) - - def mask( - self, - x: ArrayLike, - y: ArrayLike, - z: ArrayLike | np.ma.MaskedArray[Any, Any], - ax: figure | int = 0, - color: str = "black", - ) -> None: - """Plot masked out grid points as circles on a single plot. - - Args: - x (array-like of shape (ny, nx) or (nx,)): The x-coordinates of the grid points. - y (array-like of shape (ny, nx) or (ny,)): The y-coordinates of the grid points. - z (masked array of shape (ny, nx): z-values. - ax (int or Bokeh Figure, optional): Which plot to use, default ``0``. - color (str, optional): Circle color, default ``"black"``. - """ - mask = np.ma.getmask(z) # type: ignore[no-untyped-call] - if mask is np.ma.nomask: - return - fig = self._get_figure(ax) - color = self._convert_color(color) - x, y = self._grid_as_2d(x, y) - fig.circle(x[mask], y[mask], fill_color=color, size=10) - - def save( - self, - filename: str, - transparent: bool = False, - *, - webdriver: WebDriver | None = None, - ) -> None: - """Save plots to SVG or PNG file. - - Args: - filename (str): Filename to save to. - transparent (bool, optional): Whether background should be transparent, default - ``False``. - webdriver (WebDriver, optional): Selenium WebDriver instance to use to create the image. - - Warning: - To output to SVG file, ``want_svg=True`` must have been passed to the constructor. - """ - if transparent: - for fig in self._figures: - fig.background_fill_color = None # type: ignore[assignment] - fig.border_fill_color = None # type: ignore[assignment] - - if self._want_svg: - export_svg(self._layout, filename=filename, webdriver=webdriver) - else: - export_png(self._layout, filename=filename, webdriver=webdriver) - - def save_to_buffer(self, *, webdriver: WebDriver | None = None) -> io.BytesIO: - """Save plots to an ``io.BytesIO`` buffer. - - Args: - webdriver (WebDriver, optional): Selenium WebDriver instance to use to create the image. - - Return: - BytesIO: PNG image buffer. - """ - image = get_screenshot_as_png(self._layout, driver=webdriver) - buffer = io.BytesIO() - image.save(buffer, "png") - return buffer - - def show(self) -> None: - """Show plots in web browser, in usual Bokeh manner. - """ - show(self._layout) - - def title(self, title: str, ax: figure | int = 0, color: str | None = None) -> None: - """Set the title of a single plot. - - Args: - title (str): Title text. - ax (int or Bokeh Figure, optional): Which plot to set the title of, default ``0``. - color (str, optional): Color to set title. May be a string color or the letter ``"C"`` - followed by an integer in the range ``"C0"`` to ``"C9"`` to use a color from the - ``Category10`` palette. Default ``None`` which is ``black``. - """ - fig = self._get_figure(ax) - fig.title = title # type: ignore[assignment] - fig.title.align = "center" # type: ignore[attr-defined] - if color is not None: - fig.title.text_color = self._convert_color(color) # type: ignore[attr-defined] - - def z_values( - self, - x: ArrayLike, - y: ArrayLike, - z: ArrayLike, - ax: figure | int = 0, - color: str = "green", - fmt: str = ".1f", - quad_as_tri: bool = False, - ) -> None: - """Show ``z`` values on a single plot. - - Args: - x (array-like of shape (ny, nx) or (nx,)): The x-coordinates of the grid points. - y (array-like of shape (ny, nx) or (ny,)): The y-coordinates of the grid points. - z (array-like of shape (ny, nx): z-values. - ax (int or Bokeh Figure, optional): Which plot to use, default ``0``. - color (str, optional): Color of added text. May be a string color or the letter ``"C"`` - followed by an integer in the range ``"C0"`` to ``"C9"`` to use a color from the - ``Category10`` palette. Default ``"green"``. - fmt (str, optional): Format to display z-values, default ``".1f"``. - quad_as_tri (bool, optional): Whether to show z-values at the ``quad_as_tri`` centres - of quads. - - Warning: - ``quad_as_tri=True`` shows z-values for all quads, even if masked. - """ - fig = self._get_figure(ax) - color = self._convert_color(color) - x, y = self._grid_as_2d(x, y) - z = np.asarray(z) - ny, nx = z.shape - kwargs = dict(text_color=color, text_align="center", text_baseline="middle") - for j in range(ny): - for i in range(nx): - fig.add_layout(Label(x=x[j, i], y=y[j, i], text=f"{z[j, i]:{fmt}}", **kwargs)) - if quad_as_tri: - for j in range(ny-1): - for i in range(nx-1): - xx = np.mean(x[j:j+2, i:i+2]) - yy = np.mean(y[j:j+2, i:i+2]) - zz = np.mean(z[j:j+2, i:i+2]) - fig.add_layout(Label(x=xx, y=yy, text=f"{zz:{fmt}}", **kwargs)) diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/bokeh_util.py b/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/bokeh_util.py deleted file mode 100644 index e75654d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/bokeh_util.py +++ /dev/null @@ -1,90 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, cast - -from contourpy import FillType, LineType -from contourpy.util.mpl_util import mpl_codes_to_offsets - -if TYPE_CHECKING: - from contourpy._contourpy import ( - CoordinateArray, FillReturn, LineReturn, LineReturn_Separate, LineReturn_SeparateCode, - ) - - -def filled_to_bokeh( - filled: FillReturn, - fill_type: FillType, -) -> tuple[list[list[CoordinateArray]], list[list[CoordinateArray]]]: - xs: list[list[CoordinateArray]] = [] - ys: list[list[CoordinateArray]] = [] - if fill_type in (FillType.OuterOffset, FillType.ChunkCombinedOffset, - FillType.OuterCode, FillType.ChunkCombinedCode): - have_codes = fill_type in (FillType.OuterCode, FillType.ChunkCombinedCode) - - for points, offsets in zip(*filled): - if points is None: - continue - if have_codes: - offsets = mpl_codes_to_offsets(offsets) - xs.append([]) # New outer with zero or more holes. - ys.append([]) - for i in range(len(offsets)-1): - xys = points[offsets[i]:offsets[i+1]] - xs[-1].append(xys[:, 0]) - ys[-1].append(xys[:, 1]) - elif fill_type in (FillType.ChunkCombinedCodeOffset, FillType.ChunkCombinedOffsetOffset): - for points, codes_or_offsets, outer_offsets in zip(*filled): - if points is None: - continue - for j in range(len(outer_offsets)-1): - if fill_type == FillType.ChunkCombinedCodeOffset: - codes = codes_or_offsets[outer_offsets[j]:outer_offsets[j+1]] - offsets = mpl_codes_to_offsets(codes) + outer_offsets[j] - else: - offsets = codes_or_offsets[outer_offsets[j]:outer_offsets[j+1]+1] - xs.append([]) # New outer with zero or more holes. - ys.append([]) - for k in range(len(offsets)-1): - xys = points[offsets[k]:offsets[k+1]] - xs[-1].append(xys[:, 0]) - ys[-1].append(xys[:, 1]) - else: - raise RuntimeError(f"Conversion of FillType {fill_type} to Bokeh is not implemented") - - return xs, ys - - -def lines_to_bokeh( - lines: LineReturn, - line_type: LineType, -) -> tuple[list[CoordinateArray], list[CoordinateArray]]: - xs: list[CoordinateArray] = [] - ys: list[CoordinateArray] = [] - - if line_type == LineType.Separate: - if TYPE_CHECKING: - lines = cast(LineReturn_Separate, lines) - for line in lines: - xs.append(line[:, 0]) - ys.append(line[:, 1]) - elif line_type == LineType.SeparateCode: - if TYPE_CHECKING: - lines = cast(LineReturn_SeparateCode, lines) - for line in lines[0]: - xs.append(line[:, 0]) - ys.append(line[:, 1]) - elif line_type in (LineType.ChunkCombinedCode, LineType.ChunkCombinedOffset): - for points, offsets in zip(*lines): - if points is None: - continue - if line_type == LineType.ChunkCombinedCode: - offsets = mpl_codes_to_offsets(offsets) - - for i in range(len(offsets)-1): - line = points[offsets[i]:offsets[i+1]] - xs.append(line[:, 0]) - ys.append(line[:, 1]) - else: - raise RuntimeError(f"Conversion of LineType {line_type} to Bokeh is not implemented") - - return xs, ys diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/data.py b/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/data.py deleted file mode 100644 index e6ba9a9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/data.py +++ /dev/null @@ -1,78 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any - -import numpy as np - -if TYPE_CHECKING: - from contourpy._contourpy import CoordinateArray - - -def simple( - shape: tuple[int, int], want_mask: bool = False, -) -> tuple[CoordinateArray, CoordinateArray, CoordinateArray | np.ma.MaskedArray[Any, Any]]: - """Return simple test data consisting of the sum of two gaussians. - - Args: - shape (tuple(int, int)): 2D shape of data to return. - want_mask (bool, optional): Whether test data should be masked or not, default ``False``. - - Return: - Tuple of 3 arrays: ``x``, ``y``, ``z`` test data, ``z`` will be masked if - ``want_mask=True``. - """ - ny, nx = shape - x = np.arange(nx, dtype=np.float64) - y = np.arange(ny, dtype=np.float64) - x, y = np.meshgrid(x, y) - - xscale = nx - 1.0 - yscale = ny - 1.0 - - # z is sum of 2D gaussians. - amp = np.asarray([1.0, -1.0, 0.8, -0.9, 0.7]) - mid = np.asarray([[0.4, 0.2], [0.3, 0.8], [0.9, 0.75], [0.7, 0.3], [0.05, 0.7]]) - width = np.asarray([0.4, 0.2, 0.2, 0.2, 0.1]) - - z = np.zeros_like(x) - for i in range(len(amp)): - z += amp[i]*np.exp(-((x/xscale - mid[i, 0])**2 + (y/yscale - mid[i, 1])**2) / width[i]**2) - - if want_mask: - mask = np.logical_or( - ((x/xscale - 1.0)**2 / 0.2 + (y/yscale - 0.0)**2 / 0.1) < 1.0, - ((x/xscale - 0.2)**2 / 0.02 + (y/yscale - 0.45)**2 / 0.08) < 1.0, - ) - z = np.ma.array(z, mask=mask) # type: ignore[no-untyped-call] - - return x, y, z - - -def random( - shape: tuple[int, int], seed: int = 2187, mask_fraction: float = 0.0, -) -> tuple[CoordinateArray, CoordinateArray, CoordinateArray | np.ma.MaskedArray[Any, Any]]: - """Return random test data.. - - Args: - shape (tuple(int, int)): 2D shape of data to return. - seed (int, optional): Seed for random number generator, default 2187. - mask_fraction (float, optional): Fraction of elements to mask, default 0. - - Return: - Tuple of 3 arrays: ``x``, ``y``, ``z`` test data, ``z`` will be masked if - ``mask_fraction`` is greater than zero. - """ - ny, nx = shape - x = np.arange(nx, dtype=np.float64) - y = np.arange(ny, dtype=np.float64) - x, y = np.meshgrid(x, y) - - rng = np.random.default_rng(seed) - z = rng.uniform(size=shape) - - if mask_fraction > 0.0: - mask_fraction = min(mask_fraction, 0.99) - mask = rng.uniform(size=shape) < mask_fraction - z = np.ma.array(z, mask=mask) # type: ignore[no-untyped-call] - - return x, y, z diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/mpl_renderer.py b/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/mpl_renderer.py deleted file mode 100644 index dbcb5ca..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/mpl_renderer.py +++ /dev/null @@ -1,613 +0,0 @@ -from __future__ import annotations - -import io -from typing import TYPE_CHECKING, Any, cast - -import matplotlib.collections as mcollections -import matplotlib.pyplot as plt -import numpy as np - -from contourpy import FillType, LineType -from contourpy.util.mpl_util import filled_to_mpl_paths, lines_to_mpl_paths, mpl_codes_to_offsets -from contourpy.util.renderer import Renderer - -if TYPE_CHECKING: - from matplotlib.axes import Axes - from matplotlib.figure import Figure - from numpy.typing import ArrayLike - - import contourpy._contourpy as cpy - - -class MplRenderer(Renderer): - _axes: Axes - _fig: Figure - _want_tight: bool - - """Utility renderer using Matplotlib to render a grid of plots over the same (x, y) range. - - Args: - nrows (int, optional): Number of rows of plots, default ``1``. - ncols (int, optional): Number of columns of plots, default ``1``. - figsize (tuple(float, float), optional): Figure size in inches, default ``(9, 9)``. - show_frame (bool, optional): Whether to show frame and axes ticks, default ``True``. - backend (str, optional): Matplotlib backend to use or ``None`` for default backend. - Default ``None``. - gridspec_kw (dict, optional): Gridspec keyword arguments to pass to ``plt.subplots``, - default None. - """ - def __init__( - self, - nrows: int = 1, - ncols: int = 1, - figsize: tuple[float, float] = (9, 9), - show_frame: bool = True, - backend: str | None = None, - gridspec_kw: dict[str, Any] | None = None, - ) -> None: - if backend is not None: - import matplotlib - matplotlib.use(backend) - - kwargs = dict(figsize=figsize, squeeze=False, sharex=True, sharey=True) - if gridspec_kw is not None: - kwargs["gridspec_kw"] = gridspec_kw - else: - kwargs["subplot_kw"] = dict(aspect="equal") - - self._fig, axes = plt.subplots(nrows, ncols, **kwargs) - self._axes = axes.flatten() - if not show_frame: - for ax in self._axes: - ax.axis("off") - - self._want_tight = True - - def __del__(self) -> None: - if hasattr(self, "_fig"): - plt.close(self._fig) - - def _autoscale(self) -> None: - # Using axes._need_autoscale attribute if need to autoscale before rendering after adding - # lines/filled. Only want to autoscale once per axes regardless of how many lines/filled - # added. - for ax in self._axes: - if getattr(ax, "_need_autoscale", False): - ax.autoscale_view(tight=True) - ax._need_autoscale = False - if self._want_tight and len(self._axes) > 1: - self._fig.tight_layout() - - def _get_ax(self, ax: Axes | int) -> Axes: - if isinstance(ax, int): - ax = self._axes[ax] - return ax - - def filled( - self, - filled: cpy.FillReturn, - fill_type: FillType, - ax: Axes | int = 0, - color: str = "C0", - alpha: float = 0.7, - ) -> None: - """Plot filled contours on a single Axes. - - Args: - filled (sequence of arrays): Filled contour data as returned by - :func:`~contourpy.ContourGenerator.filled`. - fill_type (FillType): Type of ``filled`` data, as returned by - :attr:`~contourpy.ContourGenerator.fill_type`. - ax (int or Maplotlib Axes, optional): Which axes to plot on, default ``0``. - color (str, optional): Color to plot with. May be a string color or the letter ``"C"`` - followed by an integer in the range ``"C0"`` to ``"C9"`` to use a color from the - ``tab10`` colormap. Default ``"C0"``. - alpha (float, optional): Opacity to plot with, default ``0.7``. - """ - ax = self._get_ax(ax) - paths = filled_to_mpl_paths(filled, fill_type) - collection = mcollections.PathCollection( - paths, facecolors=color, edgecolors="none", lw=0, alpha=alpha) - ax.add_collection(collection) - ax._need_autoscale = True - - def grid( - self, - x: ArrayLike, - y: ArrayLike, - ax: Axes | int = 0, - color: str = "black", - alpha: float = 0.1, - point_color: str | None = None, - quad_as_tri_alpha: float = 0, - ) -> None: - """Plot quad grid lines on a single Axes. - - Args: - x (array-like of shape (ny, nx) or (nx,)): The x-coordinates of the grid points. - y (array-like of shape (ny, nx) or (ny,)): The y-coordinates of the grid points. - ax (int or Matplotlib Axes, optional): Which Axes to plot on, default ``0``. - color (str, optional): Color to plot grid lines, default ``"black"``. - alpha (float, optional): Opacity to plot lines with, default ``0.1``. - point_color (str, optional): Color to plot grid points or ``None`` if grid points - should not be plotted, default ``None``. - quad_as_tri_alpha (float, optional): Opacity to plot ``quad_as_tri`` grid, default 0. - - Colors may be a string color or the letter ``"C"`` followed by an integer in the range - ``"C0"`` to ``"C9"`` to use a color from the ``tab10`` colormap. - - Warning: - ``quad_as_tri_alpha > 0`` plots all quads as though they are unmasked. - """ - ax = self._get_ax(ax) - x, y = self._grid_as_2d(x, y) - kwargs = dict(color=color, alpha=alpha) - ax.plot(x, y, x.T, y.T, **kwargs) - if quad_as_tri_alpha > 0: - # Assumes no quad mask. - xmid = 0.25*(x[:-1, :-1] + x[1:, :-1] + x[:-1, 1:] + x[1:, 1:]) - ymid = 0.25*(y[:-1, :-1] + y[1:, :-1] + y[:-1, 1:] + y[1:, 1:]) - kwargs["alpha"] = quad_as_tri_alpha - ax.plot( - np.stack((x[:-1, :-1], xmid, x[1:, 1:])).reshape((3, -1)), - np.stack((y[:-1, :-1], ymid, y[1:, 1:])).reshape((3, -1)), - np.stack((x[1:, :-1], xmid, x[:-1, 1:])).reshape((3, -1)), - np.stack((y[1:, :-1], ymid, y[:-1, 1:])).reshape((3, -1)), - **kwargs) - if point_color is not None: - ax.plot(x, y, color=point_color, alpha=alpha, marker="o", lw=0) - ax._need_autoscale = True - - def lines( - self, - lines: cpy.LineReturn, - line_type: LineType, - ax: Axes | int = 0, - color: str = "C0", - alpha: float = 1.0, - linewidth: float = 1, - ) -> None: - """Plot contour lines on a single Axes. - - Args: - lines (sequence of arrays): Contour line data as returned by - :func:`~contourpy.ContourGenerator.lines`. - line_type (LineType): Type of ``lines`` data, as returned by - :attr:`~contourpy.ContourGenerator.line_type`. - ax (int or Matplotlib Axes, optional): Which Axes to plot on, default ``0``. - color (str, optional): Color to plot lines. May be a string color or the letter ``"C"`` - followed by an integer in the range ``"C0"`` to ``"C9"`` to use a color from the - ``tab10`` colormap. Default ``"C0"``. - alpha (float, optional): Opacity to plot lines with, default ``1.0``. - linewidth (float, optional): Width of lines, default ``1``. - """ - ax = self._get_ax(ax) - paths = lines_to_mpl_paths(lines, line_type) - collection = mcollections.PathCollection( - paths, facecolors="none", edgecolors=color, lw=linewidth, alpha=alpha) - ax.add_collection(collection) - ax._need_autoscale = True - - def mask( - self, - x: ArrayLike, - y: ArrayLike, - z: ArrayLike | np.ma.MaskedArray[Any, Any], - ax: Axes | int = 0, - color: str = "black", - ) -> None: - """Plot masked out grid points as circles on a single Axes. - - Args: - x (array-like of shape (ny, nx) or (nx,)): The x-coordinates of the grid points. - y (array-like of shape (ny, nx) or (ny,)): The y-coordinates of the grid points. - z (masked array of shape (ny, nx): z-values. - ax (int or Matplotlib Axes, optional): Which Axes to plot on, default ``0``. - color (str, optional): Circle color, default ``"black"``. - """ - mask = np.ma.getmask(z) # type: ignore[no-untyped-call] - if mask is np.ma.nomask: - return - ax = self._get_ax(ax) - x, y = self._grid_as_2d(x, y) - ax.plot(x[mask], y[mask], "o", c=color) - - def save(self, filename: str, transparent: bool = False) -> None: - """Save plots to SVG or PNG file. - - Args: - filename (str): Filename to save to. - transparent (bool, optional): Whether background should be transparent, default - ``False``. - """ - self._autoscale() - self._fig.savefig(filename, transparent=transparent) - - def save_to_buffer(self) -> io.BytesIO: - """Save plots to an ``io.BytesIO`` buffer. - - Return: - BytesIO: PNG image buffer. - """ - self._autoscale() - buf = io.BytesIO() - self._fig.savefig(buf, format="png") - buf.seek(0) - return buf - - def show(self) -> None: - """Show plots in an interactive window, in the usual Matplotlib manner. - """ - self._autoscale() - plt.show() - - def title(self, title: str, ax: Axes | int = 0, color: str | None = None) -> None: - """Set the title of a single Axes. - - Args: - title (str): Title text. - ax (int or Matplotlib Axes, optional): Which Axes to set the title of, default ``0``. - color (str, optional): Color to set title. May be a string color or the letter ``"C"`` - followed by an integer in the range ``"C0"`` to ``"C9"`` to use a color from the - ``tab10`` colormap. Default is ``None`` which uses Matplotlib's default title color - that depends on the stylesheet in use. - """ - if color: - self._get_ax(ax).set_title(title, color=color) - else: - self._get_ax(ax).set_title(title) - - def z_values( - self, - x: ArrayLike, - y: ArrayLike, - z: ArrayLike, - ax: Axes | int = 0, - color: str = "green", - fmt: str = ".1f", - quad_as_tri: bool = False, - ) -> None: - """Show ``z`` values on a single Axes. - - Args: - x (array-like of shape (ny, nx) or (nx,)): The x-coordinates of the grid points. - y (array-like of shape (ny, nx) or (ny,)): The y-coordinates of the grid points. - z (array-like of shape (ny, nx): z-values. - ax (int or Matplotlib Axes, optional): Which Axes to plot on, default ``0``. - color (str, optional): Color of added text. May be a string color or the letter ``"C"`` - followed by an integer in the range ``"C0"`` to ``"C9"`` to use a color from the - ``tab10`` colormap. Default ``"green"``. - fmt (str, optional): Format to display z-values, default ``".1f"``. - quad_as_tri (bool, optional): Whether to show z-values at the ``quad_as_tri`` centers - of quads. - - Warning: - ``quad_as_tri=True`` shows z-values for all quads, even if masked. - """ - ax = self._get_ax(ax) - x, y = self._grid_as_2d(x, y) - z = np.asarray(z) - ny, nx = z.shape - for j in range(ny): - for i in range(nx): - ax.text(x[j, i], y[j, i], f"{z[j, i]:{fmt}}", ha="center", va="center", - color=color, clip_on=True) - if quad_as_tri: - for j in range(ny-1): - for i in range(nx-1): - xx = np.mean(x[j:j+2, i:i+2]) - yy = np.mean(y[j:j+2, i:i+2]) - zz = np.mean(z[j:j+2, i:i+2]) - ax.text(xx, yy, f"{zz:{fmt}}", ha="center", va="center", color=color, - clip_on=True) - - -class MplTestRenderer(MplRenderer): - """Test renderer implemented using Matplotlib. - - No whitespace around plots and no spines/ticks displayed. - Uses Agg backend, so can only save to file/buffer, cannot call ``show()``. - """ - def __init__( - self, - nrows: int = 1, - ncols: int = 1, - figsize: tuple[float, float] = (9, 9), - ) -> None: - gridspec = { - "left": 0.01, - "right": 0.99, - "top": 0.99, - "bottom": 0.01, - "wspace": 0.01, - "hspace": 0.01, - } - super().__init__( - nrows, ncols, figsize, show_frame=True, backend="Agg", gridspec_kw=gridspec, - ) - - for ax in self._axes: - ax.set_xmargin(0.0) - ax.set_ymargin(0.0) - ax.set_xticks([]) - ax.set_yticks([]) - - self._want_tight = False - - -class MplDebugRenderer(MplRenderer): - """Debug renderer implemented using Matplotlib. - - Extends ``MplRenderer`` to add extra information to help in debugging such as markers, arrows, - text, etc. - """ - def __init__( - self, - nrows: int = 1, - ncols: int = 1, - figsize: tuple[float, float] = (9, 9), - show_frame: bool = True, - ) -> None: - super().__init__(nrows, ncols, figsize, show_frame) - - def _arrow( - self, - ax: Axes, - line_start: cpy.CoordinateArray, - line_end: cpy.CoordinateArray, - color: str, - alpha: float, - arrow_size: float, - ) -> None: - mid = 0.5*(line_start + line_end) - along = line_end - line_start - along /= np.sqrt(np.dot(along, along)) # Unit vector. - right = np.asarray((along[1], -along[0])) - arrow = np.stack(( - mid - (along*0.5 - right)*arrow_size, - mid + along*0.5*arrow_size, - mid - (along*0.5 + right)*arrow_size, - )) - ax.plot(arrow[:, 0], arrow[:, 1], "-", c=color, alpha=alpha) - - def _filled_to_lists_of_points_and_offsets( - self, - filled: cpy.FillReturn, - fill_type: FillType, - ) -> tuple[list[cpy.PointArray], list[cpy.OffsetArray]]: - if fill_type == FillType.OuterCode: - if TYPE_CHECKING: - filled = cast(cpy.FillReturn_OuterCode, filled) - all_points = filled[0] - all_offsets = [mpl_codes_to_offsets(codes) for codes in filled[1]] - elif fill_type == FillType.ChunkCombinedCode: - if TYPE_CHECKING: - filled = cast(cpy.FillReturn_ChunkCombinedCode, filled) - all_points = [points for points in filled[0] if points is not None] - all_offsets = [mpl_codes_to_offsets(codes) for codes in filled[1] if codes is not None] - elif fill_type == FillType.OuterOffset: - if TYPE_CHECKING: - filled = cast(cpy.FillReturn_OuterOffset, filled) - all_points = filled[0] - all_offsets = filled[1] - elif fill_type == FillType.ChunkCombinedOffset: - if TYPE_CHECKING: - filled = cast(cpy.FillReturn_ChunkCombinedOffset, filled) - all_points = [points for points in filled[0] if points is not None] - all_offsets = [offsets for offsets in filled[1] if offsets is not None] - elif fill_type == FillType.ChunkCombinedCodeOffset: - if TYPE_CHECKING: - filled = cast(cpy.FillReturn_ChunkCombinedCodeOffset, filled) - all_points = [] - all_offsets = [] - for points, codes, outer_offsets in zip(*filled): - if points is None: - continue - if TYPE_CHECKING: - assert codes is not None and outer_offsets is not None - all_points += np.split(points, outer_offsets[1:-1]) - all_codes = np.split(codes, outer_offsets[1:-1]) - all_offsets += [mpl_codes_to_offsets(codes) for codes in all_codes] - elif fill_type == FillType.ChunkCombinedOffsetOffset: - if TYPE_CHECKING: - filled = cast(cpy.FillReturn_ChunkCombinedOffsetOffset, filled) - all_points = [] - all_offsets = [] - for points, offsets, outer_offsets in zip(*filled): - if points is None: - continue - if TYPE_CHECKING: - assert offsets is not None and outer_offsets is not None - for i in range(len(outer_offsets)-1): - offs = offsets[outer_offsets[i]:outer_offsets[i+1]+1] - all_points.append(points[offs[0]:offs[-1]]) - all_offsets.append(offs - offs[0]) - else: - raise RuntimeError(f"Rendering FillType {fill_type} not implemented") - - return all_points, all_offsets - - def _lines_to_list_of_points( - self, lines: cpy.LineReturn, line_type: LineType, - ) -> list[cpy.PointArray]: - if line_type == LineType.Separate: - if TYPE_CHECKING: - lines = cast(cpy.LineReturn_Separate, lines) - all_lines = lines - elif line_type == LineType.SeparateCode: - if TYPE_CHECKING: - lines = cast(cpy.LineReturn_SeparateCode, lines) - all_lines = lines[0] - elif line_type == LineType.ChunkCombinedCode: - if TYPE_CHECKING: - lines = cast(cpy.LineReturn_ChunkCombinedCode, lines) - all_lines = [] - for points, codes in zip(*lines): - if points is not None: - if TYPE_CHECKING: - assert codes is not None - offsets = mpl_codes_to_offsets(codes) - for i in range(len(offsets)-1): - all_lines.append(points[offsets[i]:offsets[i+1]]) - elif line_type == LineType.ChunkCombinedOffset: - if TYPE_CHECKING: - lines = cast(cpy.LineReturn_ChunkCombinedOffset, lines) - all_lines = [] - for points, all_offsets in zip(*lines): - if points is not None: - if TYPE_CHECKING: - assert all_offsets is not None - for i in range(len(all_offsets)-1): - all_lines.append(points[all_offsets[i]:all_offsets[i+1]]) - else: - raise RuntimeError(f"Rendering LineType {line_type} not implemented") - - return all_lines - - def filled( - self, - filled: cpy.FillReturn, - fill_type: FillType, - ax: Axes | int = 0, - color: str = "C1", - alpha: float = 0.7, - line_color: str = "C0", - line_alpha: float = 0.7, - point_color: str = "C0", - start_point_color: str = "red", - arrow_size: float = 0.1, - ) -> None: - super().filled(filled, fill_type, ax, color, alpha) - - if line_color is None and point_color is None: - return - - ax = self._get_ax(ax) - all_points, all_offsets = self._filled_to_lists_of_points_and_offsets(filled, fill_type) - - # Lines. - if line_color is not None: - for points, offsets in zip(all_points, all_offsets): - for start, end in zip(offsets[:-1], offsets[1:]): - xys = points[start:end] - ax.plot(xys[:, 0], xys[:, 1], c=line_color, alpha=line_alpha) - - if arrow_size > 0.0: - n = len(xys) - for i in range(n-1): - self._arrow(ax, xys[i], xys[i+1], line_color, line_alpha, arrow_size) - - # Points. - if point_color is not None: - for points, offsets in zip(all_points, all_offsets): - mask = np.ones(offsets[-1], dtype=bool) - mask[offsets[1:]-1] = False # Exclude end points. - if start_point_color is not None: - start_indices = offsets[:-1] - mask[start_indices] = False # Exclude start points. - ax.plot( - points[:, 0][mask], points[:, 1][mask], "o", c=point_color, alpha=line_alpha) - - if start_point_color is not None: - ax.plot(points[:, 0][start_indices], points[:, 1][start_indices], "o", - c=start_point_color, alpha=line_alpha) - - def lines( - self, - lines: cpy.LineReturn, - line_type: LineType, - ax: Axes | int = 0, - color: str = "C0", - alpha: float = 1.0, - linewidth: float = 1, - point_color: str = "C0", - start_point_color: str = "red", - arrow_size: float = 0.1, - ) -> None: - super().lines(lines, line_type, ax, color, alpha, linewidth) - - if arrow_size == 0.0 and point_color is None: - return - - ax = self._get_ax(ax) - all_lines = self._lines_to_list_of_points(lines, line_type) - - if arrow_size > 0.0: - for line in all_lines: - for i in range(len(line)-1): - self._arrow(ax, line[i], line[i+1], color, alpha, arrow_size) - - if point_color is not None: - for line in all_lines: - start_index = 0 - end_index = len(line) - if start_point_color is not None: - ax.plot(line[0, 0], line[0, 1], "o", c=start_point_color, alpha=alpha) - start_index = 1 - if line[0][0] == line[-1][0] and line[0][1] == line[-1][1]: - end_index -= 1 - ax.plot(line[start_index:end_index, 0], line[start_index:end_index, 1], "o", - c=color, alpha=alpha) - - def point_numbers( - self, - x: ArrayLike, - y: ArrayLike, - z: ArrayLike, - ax: Axes | int = 0, - color: str = "red", - ) -> None: - ax = self._get_ax(ax) - x, y = self._grid_as_2d(x, y) - z = np.asarray(z) - ny, nx = z.shape - for j in range(ny): - for i in range(nx): - quad = i + j*nx - ax.text(x[j, i], y[j, i], str(quad), ha="right", va="top", color=color, - clip_on=True) - - def quad_numbers( - self, - x: ArrayLike, - y: ArrayLike, - z: ArrayLike, - ax: Axes | int = 0, - color: str = "blue", - ) -> None: - ax = self._get_ax(ax) - x, y = self._grid_as_2d(x, y) - z = np.asarray(z) - ny, nx = z.shape - for j in range(1, ny): - for i in range(1, nx): - quad = i + j*nx - xmid = x[j-1:j+1, i-1:i+1].mean() - ymid = y[j-1:j+1, i-1:i+1].mean() - ax.text(xmid, ymid, str(quad), ha="center", va="center", color=color, clip_on=True) - - def z_levels( - self, - x: ArrayLike, - y: ArrayLike, - z: ArrayLike, - lower_level: float, - upper_level: float | None = None, - ax: Axes | int = 0, - color: str = "green", - ) -> None: - ax = self._get_ax(ax) - x, y = self._grid_as_2d(x, y) - z = np.asarray(z) - ny, nx = z.shape - for j in range(ny): - for i in range(nx): - zz = z[j, i] - if upper_level is not None and zz > upper_level: - z_level = 2 - elif zz > lower_level: - z_level = 1 - else: - z_level = 0 - ax.text(x[j, i], y[j, i], z_level, ha="left", va="bottom", color=color, - clip_on=True) diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/mpl_util.py b/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/mpl_util.py deleted file mode 100644 index 0c97088..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/mpl_util.py +++ /dev/null @@ -1,79 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, cast - -import matplotlib.path as mpath -import numpy as np - -from contourpy import FillType, LineType - -if TYPE_CHECKING: - from contourpy._contourpy import ( - CodeArray, FillReturn, LineReturn, LineReturn_Separate, OffsetArray, - ) - - -def filled_to_mpl_paths(filled: FillReturn, fill_type: FillType) -> list[mpath.Path]: - if fill_type in (FillType.OuterCode, FillType.ChunkCombinedCode): - paths = [mpath.Path(points, codes) for points, codes in zip(*filled) if points is not None] - elif fill_type in (FillType.OuterOffset, FillType.ChunkCombinedOffset): - paths = [mpath.Path(points, offsets_to_mpl_codes(offsets)) - for points, offsets in zip(*filled) if points is not None] - elif fill_type == FillType.ChunkCombinedCodeOffset: - paths = [] - for points, codes, outer_offsets in zip(*filled): - if points is None: - continue - points = np.split(points, outer_offsets[1:-1]) - codes = np.split(codes, outer_offsets[1:-1]) - paths += [mpath.Path(p, c) for p, c in zip(points, codes)] - elif fill_type == FillType.ChunkCombinedOffsetOffset: - paths = [] - for points, offsets, outer_offsets in zip(*filled): - if points is None: - continue - for i in range(len(outer_offsets)-1): - offs = offsets[outer_offsets[i]:outer_offsets[i+1]+1] - pts = points[offs[0]:offs[-1]] - paths += [mpath.Path(pts, offsets_to_mpl_codes(offs - offs[0]))] - else: - raise RuntimeError(f"Conversion of FillType {fill_type} to MPL Paths is not implemented") - return paths - - -def lines_to_mpl_paths(lines: LineReturn, line_type: LineType) -> list[mpath.Path]: - if line_type == LineType.Separate: - if TYPE_CHECKING: - lines = cast(LineReturn_Separate, lines) - paths = [] - for line in lines: - # Drawing as Paths so that they can be closed correctly. - closed = line[0, 0] == line[-1, 0] and line[0, 1] == line[-1, 1] - paths.append(mpath.Path(line, closed=closed)) - elif line_type in (LineType.SeparateCode, LineType.ChunkCombinedCode): - paths = [mpath.Path(points, codes) for points, codes in zip(*lines) if points is not None] - elif line_type == LineType.ChunkCombinedOffset: - paths = [] - for points, offsets in zip(*lines): - if points is None: - continue - for i in range(len(offsets)-1): - line = points[offsets[i]:offsets[i+1]] - closed = line[0, 0] == line[-1, 0] and line[0, 1] == line[-1, 1] - paths.append(mpath.Path(line, closed=closed)) - else: - raise RuntimeError(f"Conversion of LineType {line_type} to MPL Paths is not implemented") - return paths - - -def mpl_codes_to_offsets(codes: CodeArray) -> OffsetArray: - offsets = np.nonzero(codes == 1)[0].astype(np.uint32) - offsets = np.append(offsets, len(codes)) - return offsets - - -def offsets_to_mpl_codes(offsets: OffsetArray) -> CodeArray: - codes = np.full(offsets[-1]-offsets[0], 2, dtype=np.uint8) # LINETO = 2 - codes[offsets[:-1]] = 1 # MOVETO = 1 - codes[offsets[1:]-1] = 79 # CLOSEPOLY 79 - return codes diff --git a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/renderer.py b/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/renderer.py deleted file mode 100644 index ef1d065..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/contourpy/util/renderer.py +++ /dev/null @@ -1,106 +0,0 @@ -from __future__ import annotations - -from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, Any - -import numpy as np - -if TYPE_CHECKING: - import io - - from numpy.typing import ArrayLike - - from contourpy._contourpy import CoordinateArray, FillReturn, FillType, LineReturn, LineType - - -class Renderer(ABC): - """Abstract base class for renderers, defining the interface that they must implement.""" - - def _grid_as_2d(self, x: ArrayLike, y: ArrayLike) -> tuple[CoordinateArray, CoordinateArray]: - x = np.asarray(x) - y = np.asarray(y) - if x.ndim == 1: - x, y = np.meshgrid(x, y) - return x, y - - x = np.asarray(x) - y = np.asarray(y) - if x.ndim == 1: - x, y = np.meshgrid(x, y) - return x, y - - @abstractmethod - def filled( - self, - filled: FillReturn, - fill_type: FillType, - ax: Any = 0, - color: str = "C0", - alpha: float = 0.7, - ) -> None: - pass - - @abstractmethod - def grid( - self, - x: ArrayLike, - y: ArrayLike, - ax: Any = 0, - color: str = "black", - alpha: float = 0.1, - point_color: str | None = None, - quad_as_tri_alpha: float = 0, - ) -> None: - pass - - @abstractmethod - def lines( - self, - lines: LineReturn, - line_type: LineType, - ax: Any = 0, - color: str = "C0", - alpha: float = 1.0, - linewidth: float = 1, - ) -> None: - pass - - @abstractmethod - def mask( - self, - x: ArrayLike, - y: ArrayLike, - z: ArrayLike | np.ma.MaskedArray[Any, Any], - ax: Any = 0, - color: str = "black", - ) -> None: - pass - - @abstractmethod - def save(self, filename: str, transparent: bool = False) -> None: - pass - - @abstractmethod - def save_to_buffer(self) -> io.BytesIO: - pass - - @abstractmethod - def show(self) -> None: - pass - - @abstractmethod - def title(self, title: str, ax: Any = 0, color: str | None = None) -> None: - pass - - @abstractmethod - def z_values( - self, - x: ArrayLike, - y: ArrayLike, - z: ArrayLike, - ax: Any = 0, - color: str = "green", - fmt: str = ".1f", - quad_as_tri: bool = False, - ) -> None: - pass diff --git a/plotter-app/venv/lib/python3.8/site-packages/cycler-0.12.1.dist-info/INSTALLER b/plotter-app/venv/lib/python3.8/site-packages/cycler-0.12.1.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/cycler-0.12.1.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/plotter-app/venv/lib/python3.8/site-packages/cycler-0.12.1.dist-info/LICENSE b/plotter-app/venv/lib/python3.8/site-packages/cycler-0.12.1.dist-info/LICENSE deleted file mode 100644 index d41d808..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/cycler-0.12.1.dist-info/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2015, matplotlib 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: - -* 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 matplotlib 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 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. \ No newline at end of file diff --git a/plotter-app/venv/lib/python3.8/site-packages/cycler-0.12.1.dist-info/METADATA b/plotter-app/venv/lib/python3.8/site-packages/cycler-0.12.1.dist-info/METADATA deleted file mode 100644 index e81ab4f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/cycler-0.12.1.dist-info/METADATA +++ /dev/null @@ -1,78 +0,0 @@ -Metadata-Version: 2.1 -Name: cycler -Version: 0.12.1 -Summary: Composable style cycles -Author-email: Thomas A Caswell -License: Copyright (c) 2015, matplotlib 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: - - * 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 matplotlib 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 HOLDER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -Project-URL: homepage, https://matplotlib.org/cycler/ -Project-URL: repository, https://github.com/matplotlib/cycler -Keywords: cycle kwargs -Classifier: License :: OSI Approved :: BSD License -Classifier: Development Status :: 4 - Beta -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 :: Only -Requires-Python: >=3.8 -Description-Content-Type: text/x-rst -License-File: LICENSE -Provides-Extra: docs -Requires-Dist: ipython ; extra == 'docs' -Requires-Dist: matplotlib ; extra == 'docs' -Requires-Dist: numpydoc ; extra == 'docs' -Requires-Dist: sphinx ; extra == 'docs' -Provides-Extra: tests -Requires-Dist: pytest ; extra == 'tests' -Requires-Dist: pytest-cov ; extra == 'tests' -Requires-Dist: pytest-xdist ; extra == 'tests' - -|PyPi|_ |Conda|_ |Supported Python versions|_ |GitHub Actions|_ |Codecov|_ - -.. |PyPi| image:: https://img.shields.io/pypi/v/cycler.svg?style=flat -.. _PyPi: https://pypi.python.org/pypi/cycler - -.. |Conda| image:: https://img.shields.io/conda/v/conda-forge/cycler -.. _Conda: https://anaconda.org/conda-forge/cycler - -.. |Supported Python versions| image:: https://img.shields.io/pypi/pyversions/cycler.svg -.. _Supported Python versions: https://pypi.python.org/pypi/cycler - -.. |GitHub Actions| image:: https://github.com/matplotlib/cycler/actions/workflows/tests.yml/badge.svg -.. _GitHub Actions: https://github.com/matplotlib/cycler/actions - -.. |Codecov| image:: https://codecov.io/github/matplotlib/cycler/badge.svg?branch=main&service=github -.. _Codecov: https://codecov.io/github/matplotlib/cycler?branch=main - -cycler: composable cycles -========================= - -Docs: https://matplotlib.org/cycler/ diff --git a/plotter-app/venv/lib/python3.8/site-packages/cycler-0.12.1.dist-info/RECORD b/plotter-app/venv/lib/python3.8/site-packages/cycler-0.12.1.dist-info/RECORD deleted file mode 100644 index 8effaf3..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/cycler-0.12.1.dist-info/RECORD +++ /dev/null @@ -1,9 +0,0 @@ -cycler-0.12.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -cycler-0.12.1.dist-info/LICENSE,sha256=8SGBQ9dm2j_qZvEzlrfxXfRqgzA_Kb-Wum6Y601C9Ag,1497 -cycler-0.12.1.dist-info/METADATA,sha256=IyieGbdvHgE5Qidpbmryts0c556JcxIJv5GVFIsY7TY,3779 -cycler-0.12.1.dist-info/RECORD,, -cycler-0.12.1.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92 -cycler-0.12.1.dist-info/top_level.txt,sha256=D8BVVDdAAelLb2FOEz7lDpc6-AL21ylKPrMhtG6yzyE,7 -cycler/__init__.py,sha256=1JdRgv5Zzxo-W1ev7B_LWquysWP6LZH6CHk_COtIaXE,16709 -cycler/__pycache__/__init__.cpython-38.pyc,, -cycler/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/plotter-app/venv/lib/python3.8/site-packages/cycler-0.12.1.dist-info/WHEEL b/plotter-app/venv/lib/python3.8/site-packages/cycler-0.12.1.dist-info/WHEEL deleted file mode 100644 index 7e68873..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/cycler-0.12.1.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.41.2) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/plotter-app/venv/lib/python3.8/site-packages/cycler-0.12.1.dist-info/top_level.txt b/plotter-app/venv/lib/python3.8/site-packages/cycler-0.12.1.dist-info/top_level.txt deleted file mode 100644 index 2254644..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/cycler-0.12.1.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -cycler diff --git a/plotter-app/venv/lib/python3.8/site-packages/cycler/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/cycler/__init__.py deleted file mode 100644 index 9794954..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/cycler/__init__.py +++ /dev/null @@ -1,573 +0,0 @@ -""" -Cycler -====== - -Cycling through combinations of values, producing dictionaries. - -You can add cyclers:: - - from cycler import cycler - cc = (cycler(color=list('rgb')) + - cycler(linestyle=['-', '--', '-.'])) - for d in cc: - print(d) - -Results in:: - - {'color': 'r', 'linestyle': '-'} - {'color': 'g', 'linestyle': '--'} - {'color': 'b', 'linestyle': '-.'} - - -You can multiply cyclers:: - - from cycler import cycler - cc = (cycler(color=list('rgb')) * - cycler(linestyle=['-', '--', '-.'])) - for d in cc: - print(d) - -Results in:: - - {'color': 'r', 'linestyle': '-'} - {'color': 'r', 'linestyle': '--'} - {'color': 'r', 'linestyle': '-.'} - {'color': 'g', 'linestyle': '-'} - {'color': 'g', 'linestyle': '--'} - {'color': 'g', 'linestyle': '-.'} - {'color': 'b', 'linestyle': '-'} - {'color': 'b', 'linestyle': '--'} - {'color': 'b', 'linestyle': '-.'} -""" - - -from __future__ import annotations - -from collections.abc import Hashable, Iterable, Generator -import copy -from functools import reduce -from itertools import product, cycle -from operator import mul, add -# Dict, List, Union required for runtime cast calls -from typing import TypeVar, Generic, Callable, Union, Dict, List, Any, overload, cast - -__version__ = "0.12.1" - -K = TypeVar("K", bound=Hashable) -L = TypeVar("L", bound=Hashable) -V = TypeVar("V") -U = TypeVar("U") - - -def _process_keys( - left: Cycler[K, V] | Iterable[dict[K, V]], - right: Cycler[K, V] | Iterable[dict[K, V]] | None, -) -> set[K]: - """ - Helper function to compose cycler keys. - - Parameters - ---------- - left, right : iterable of dictionaries or None - The cyclers to be composed. - - Returns - ------- - keys : set - The keys in the composition of the two cyclers. - """ - l_peek: dict[K, V] = next(iter(left)) if left != [] else {} - r_peek: dict[K, V] = next(iter(right)) if right is not None else {} - l_key: set[K] = set(l_peek.keys()) - r_key: set[K] = set(r_peek.keys()) - if l_key & r_key: - raise ValueError("Can not compose overlapping cycles") - return l_key | r_key - - -def concat(left: Cycler[K, V], right: Cycler[K, U]) -> Cycler[K, V | U]: - r""" - Concatenate `Cycler`\s, as if chained using `itertools.chain`. - - The keys must match exactly. - - Examples - -------- - >>> num = cycler('a', range(3)) - >>> let = cycler('a', 'abc') - >>> num.concat(let) - cycler('a', [0, 1, 2, 'a', 'b', 'c']) - - Returns - ------- - `Cycler` - The concatenated cycler. - """ - if left.keys != right.keys: - raise ValueError( - "Keys do not match:\n" - "\tIntersection: {both!r}\n" - "\tDisjoint: {just_one!r}".format( - both=left.keys & right.keys, just_one=left.keys ^ right.keys - ) - ) - _l = cast(Dict[K, List[Union[V, U]]], left.by_key()) - _r = cast(Dict[K, List[Union[V, U]]], right.by_key()) - return reduce(add, (_cycler(k, _l[k] + _r[k]) for k in left.keys)) - - -class Cycler(Generic[K, V]): - """ - Composable cycles. - - This class has compositions methods: - - ``+`` - for 'inner' products (zip) - - ``+=`` - in-place ``+`` - - ``*`` - for outer products (`itertools.product`) and integer multiplication - - ``*=`` - in-place ``*`` - - and supports basic slicing via ``[]``. - - Parameters - ---------- - left, right : Cycler or None - The 'left' and 'right' cyclers. - op : func or None - Function which composes the 'left' and 'right' cyclers. - """ - - def __call__(self): - return cycle(self) - - def __init__( - self, - left: Cycler[K, V] | Iterable[dict[K, V]] | None, - right: Cycler[K, V] | None = None, - op: Any = None, - ): - """ - Semi-private init. - - Do not use this directly, use `cycler` function instead. - """ - if isinstance(left, Cycler): - self._left: Cycler[K, V] | list[dict[K, V]] = Cycler( - left._left, left._right, left._op - ) - elif left is not None: - # Need to copy the dictionary or else that will be a residual - # mutable that could lead to strange errors - self._left = [copy.copy(v) for v in left] - else: - self._left = [] - - if isinstance(right, Cycler): - self._right: Cycler[K, V] | None = Cycler( - right._left, right._right, right._op - ) - else: - self._right = None - - self._keys: set[K] = _process_keys(self._left, self._right) - self._op: Any = op - - def __contains__(self, k): - return k in self._keys - - @property - def keys(self) -> set[K]: - """The keys this Cycler knows about.""" - return set(self._keys) - - def change_key(self, old: K, new: K) -> None: - """ - Change a key in this cycler to a new name. - Modification is performed in-place. - - Does nothing if the old key is the same as the new key. - Raises a ValueError if the new key is already a key. - Raises a KeyError if the old key isn't a key. - """ - if old == new: - return - if new in self._keys: - raise ValueError( - f"Can't replace {old} with {new}, {new} is already a key" - ) - if old not in self._keys: - raise KeyError( - f"Can't replace {old} with {new}, {old} is not a key" - ) - - self._keys.remove(old) - self._keys.add(new) - - if self._right is not None and old in self._right.keys: - self._right.change_key(old, new) - - # self._left should always be non-None - # if self._keys is non-empty. - elif isinstance(self._left, Cycler): - self._left.change_key(old, new) - else: - # It should be completely safe at this point to - # assume that the old key can be found in each - # iteration. - self._left = [{new: entry[old]} for entry in self._left] - - @classmethod - def _from_iter(cls, label: K, itr: Iterable[V]) -> Cycler[K, V]: - """ - Class method to create 'base' Cycler objects - that do not have a 'right' or 'op' and for which - the 'left' object is not another Cycler. - - Parameters - ---------- - label : hashable - The property key. - - itr : iterable - Finite length iterable of the property values. - - Returns - ------- - `Cycler` - New 'base' cycler. - """ - ret: Cycler[K, V] = cls(None) - ret._left = list({label: v} for v in itr) - ret._keys = {label} - return ret - - def __getitem__(self, key: slice) -> Cycler[K, V]: - # TODO : maybe add numpy style fancy slicing - if isinstance(key, slice): - trans = self.by_key() - return reduce(add, (_cycler(k, v[key]) for k, v in trans.items())) - else: - raise ValueError("Can only use slices with Cycler.__getitem__") - - def __iter__(self) -> Generator[dict[K, V], None, None]: - if self._right is None: - for left in self._left: - yield dict(left) - else: - if self._op is None: - raise TypeError( - "Operation cannot be None when both left and right are defined" - ) - for a, b in self._op(self._left, self._right): - out = {} - out.update(a) - out.update(b) - yield out - - def __add__(self, other: Cycler[L, U]) -> Cycler[K | L, V | U]: - """ - Pair-wise combine two equal length cyclers (zip). - - Parameters - ---------- - other : Cycler - """ - if len(self) != len(other): - raise ValueError( - f"Can only add equal length cycles, not {len(self)} and {len(other)}" - ) - return Cycler( - cast(Cycler[Union[K, L], Union[V, U]], self), - cast(Cycler[Union[K, L], Union[V, U]], other), - zip - ) - - @overload - def __mul__(self, other: Cycler[L, U]) -> Cycler[K | L, V | U]: - ... - - @overload - def __mul__(self, other: int) -> Cycler[K, V]: - ... - - def __mul__(self, other): - """ - Outer product of two cyclers (`itertools.product`) or integer - multiplication. - - Parameters - ---------- - other : Cycler or int - """ - if isinstance(other, Cycler): - return Cycler( - cast(Cycler[Union[K, L], Union[V, U]], self), - cast(Cycler[Union[K, L], Union[V, U]], other), - product - ) - elif isinstance(other, int): - trans = self.by_key() - return reduce( - add, (_cycler(k, v * other) for k, v in trans.items()) - ) - else: - return NotImplemented - - @overload - def __rmul__(self, other: Cycler[L, U]) -> Cycler[K | L, V | U]: - ... - - @overload - def __rmul__(self, other: int) -> Cycler[K, V]: - ... - - def __rmul__(self, other): - return self * other - - def __len__(self) -> int: - op_dict: dict[Callable, Callable[[int, int], int]] = {zip: min, product: mul} - if self._right is None: - return len(self._left) - l_len = len(self._left) - r_len = len(self._right) - return op_dict[self._op](l_len, r_len) - - # iadd and imul do not exapand the the type as the returns must be consistent with - # self, thus they flag as inconsistent with add/mul - def __iadd__(self, other: Cycler[K, V]) -> Cycler[K, V]: # type: ignore[misc] - """ - In-place pair-wise combine two equal length cyclers (zip). - - Parameters - ---------- - other : Cycler - """ - if not isinstance(other, Cycler): - raise TypeError("Cannot += with a non-Cycler object") - # True shallow copy of self is fine since this is in-place - old_self = copy.copy(self) - self._keys = _process_keys(old_self, other) - self._left = old_self - self._op = zip - self._right = Cycler(other._left, other._right, other._op) - return self - - def __imul__(self, other: Cycler[K, V] | int) -> Cycler[K, V]: # type: ignore[misc] - """ - In-place outer product of two cyclers (`itertools.product`). - - Parameters - ---------- - other : Cycler - """ - if not isinstance(other, Cycler): - raise TypeError("Cannot *= with a non-Cycler object") - # True shallow copy of self is fine since this is in-place - old_self = copy.copy(self) - self._keys = _process_keys(old_self, other) - self._left = old_self - self._op = product - self._right = Cycler(other._left, other._right, other._op) - return self - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Cycler): - return False - if len(self) != len(other): - return False - if self.keys ^ other.keys: - return False - return all(a == b for a, b in zip(self, other)) - - __hash__ = None # type: ignore - - def __repr__(self) -> str: - op_map = {zip: "+", product: "*"} - if self._right is None: - lab = self.keys.pop() - itr = list(v[lab] for v in self) - return f"cycler({lab!r}, {itr!r})" - else: - op = op_map.get(self._op, "?") - msg = "({left!r} {op} {right!r})" - return msg.format(left=self._left, op=op, right=self._right) - - def _repr_html_(self) -> str: - # an table showing the value of each key through a full cycle - output = "" - sorted_keys = sorted(self.keys, key=repr) - for key in sorted_keys: - output += f"" - for d in iter(self): - output += "" - for k in sorted_keys: - output += f"" - output += "" - output += "
{key!r}
{d[k]!r}
" - return output - - def by_key(self) -> dict[K, list[V]]: - """ - Values by key. - - This returns the transposed values of the cycler. Iterating - over a `Cycler` yields dicts with a single value for each key, - this method returns a `dict` of `list` which are the values - for the given key. - - The returned value can be used to create an equivalent `Cycler` - using only `+`. - - Returns - ------- - transpose : dict - dict of lists of the values for each key. - """ - - # TODO : sort out if this is a bottle neck, if there is a better way - # and if we care. - - keys = self.keys - out: dict[K, list[V]] = {k: list() for k in keys} - - for d in self: - for k in keys: - out[k].append(d[k]) - return out - - # for back compatibility - _transpose = by_key - - def simplify(self) -> Cycler[K, V]: - """ - Simplify the cycler into a sum (but no products) of cyclers. - - Returns - ------- - simple : Cycler - """ - # TODO: sort out if it is worth the effort to make sure this is - # balanced. Currently it is is - # (((a + b) + c) + d) vs - # ((a + b) + (c + d)) - # I would believe that there is some performance implications - trans = self.by_key() - return reduce(add, (_cycler(k, v) for k, v in trans.items())) - - concat = concat - - -@overload -def cycler(arg: Cycler[K, V]) -> Cycler[K, V]: - ... - - -@overload -def cycler(**kwargs: Iterable[V]) -> Cycler[str, V]: - ... - - -@overload -def cycler(label: K, itr: Iterable[V]) -> Cycler[K, V]: - ... - - -def cycler(*args, **kwargs): - """ - Create a new `Cycler` object from a single positional argument, - a pair of positional arguments, or the combination of keyword arguments. - - cycler(arg) - cycler(label1=itr1[, label2=iter2[, ...]]) - cycler(label, itr) - - Form 1 simply copies a given `Cycler` object. - - Form 2 composes a `Cycler` as an inner product of the - pairs of keyword arguments. In other words, all of the - iterables are cycled simultaneously, as if through zip(). - - Form 3 creates a `Cycler` from a label and an iterable. - This is useful for when the label cannot be a keyword argument - (e.g., an integer or a name that has a space in it). - - Parameters - ---------- - arg : Cycler - Copy constructor for Cycler (does a shallow copy of iterables). - label : name - The property key. In the 2-arg form of the function, - the label can be any hashable object. In the keyword argument - form of the function, it must be a valid python identifier. - itr : iterable - Finite length iterable of the property values. - Can be a single-property `Cycler` that would - be like a key change, but as a shallow copy. - - Returns - ------- - cycler : Cycler - New `Cycler` for the given property - - """ - if args and kwargs: - raise TypeError( - "cycler() can only accept positional OR keyword arguments -- not both." - ) - - if len(args) == 1: - if not isinstance(args[0], Cycler): - raise TypeError( - "If only one positional argument given, it must " - "be a Cycler instance." - ) - return Cycler(args[0]) - elif len(args) == 2: - return _cycler(*args) - elif len(args) > 2: - raise TypeError( - "Only a single Cycler can be accepted as the lone " - "positional argument. Use keyword arguments instead." - ) - - if kwargs: - return reduce(add, (_cycler(k, v) for k, v in kwargs.items())) - - raise TypeError("Must have at least a positional OR keyword arguments") - - -def _cycler(label: K, itr: Iterable[V]) -> Cycler[K, V]: - """ - Create a new `Cycler` object from a property name and iterable of values. - - Parameters - ---------- - label : hashable - The property key. - itr : iterable - Finite length iterable of the property values. - - Returns - ------- - cycler : Cycler - New `Cycler` for the given property - """ - if isinstance(itr, Cycler): - keys = itr.keys - if len(keys) != 1: - msg = "Can not create Cycler from a multi-property Cycler" - raise ValueError(msg) - - lab = keys.pop() - # Doesn't need to be a new list because - # _from_iter() will be creating that new list anyway. - itr = (v[lab] for v in itr) - - return Cycler._from_iter(label, itr) diff --git a/plotter-app/venv/lib/python3.8/site-packages/cycler/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/cycler/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index d0718da..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/cycler/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/cycler/py.typed b/plotter-app/venv/lib/python3.8/site-packages/cycler/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/dateutil/__init__.py deleted file mode 100644 index a2c19c0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/dateutil/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -import sys - -try: - from ._version import version as __version__ -except ImportError: - __version__ = 'unknown' - -__all__ = ['easter', 'parser', 'relativedelta', 'rrule', 'tz', - 'utils', 'zoneinfo'] - -def __getattr__(name): - import importlib - - if name in __all__: - return importlib.import_module("." + name, __name__) - raise AttributeError( - "module {!r} has not attribute {!r}".format(__name__, name) - ) - - -def __dir__(): - # __dir__ should include all the lazy-importable modules as well. - return [x for x in globals() if x not in sys.modules] + __all__ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index c4d27e0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/_common.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/_common.cpython-38.pyc deleted file mode 100644 index d134bd2..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/_common.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/_version.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/_version.cpython-38.pyc deleted file mode 100644 index 8d29a65..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/_version.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/easter.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/easter.cpython-38.pyc deleted file mode 100644 index 694555b..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/easter.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/relativedelta.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/relativedelta.cpython-38.pyc deleted file mode 100644 index d4f7759..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/relativedelta.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/rrule.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/rrule.cpython-38.pyc deleted file mode 100644 index 842931d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/rrule.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/tzwin.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/tzwin.cpython-38.pyc deleted file mode 100644 index cf5a9fb..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/tzwin.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/utils.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/utils.cpython-38.pyc deleted file mode 100644 index 9d04e07..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/dateutil/__pycache__/utils.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/_common.py b/plotter-app/venv/lib/python3.8/site-packages/dateutil/_common.py deleted file mode 100644 index 4eb2659..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/dateutil/_common.py +++ /dev/null @@ -1,43 +0,0 @@ -""" -Common code used in multiple modules. -""" - - -class weekday(object): - __slots__ = ["weekday", "n"] - - def __init__(self, weekday, n=None): - self.weekday = weekday - self.n = n - - def __call__(self, n): - if n == self.n: - return self - else: - return self.__class__(self.weekday, n) - - def __eq__(self, other): - try: - if self.weekday != other.weekday or self.n != other.n: - return False - except AttributeError: - return False - return True - - def __hash__(self): - return hash(( - self.weekday, - self.n, - )) - - def __ne__(self, other): - return not (self == other) - - def __repr__(self): - s = ("MO", "TU", "WE", "TH", "FR", "SA", "SU")[self.weekday] - if not self.n: - return s - else: - return "%s(%+d)" % (s, self.n) - -# vim:ts=4:sw=4:et diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/_version.py b/plotter-app/venv/lib/python3.8/site-packages/dateutil/_version.py deleted file mode 100644 index ddda980..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/dateutil/_version.py +++ /dev/null @@ -1,4 +0,0 @@ -# file generated by setuptools_scm -# don't change, don't track in version control -__version__ = version = '2.9.0.post0' -__version_tuple__ = version_tuple = (2, 9, 0) diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/easter.py b/plotter-app/venv/lib/python3.8/site-packages/dateutil/easter.py deleted file mode 100644 index f74d1f7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/dateutil/easter.py +++ /dev/null @@ -1,89 +0,0 @@ -# -*- coding: utf-8 -*- -""" -This module offers a generic Easter computing method for any given year, using -Western, Orthodox or Julian algorithms. -""" - -import datetime - -__all__ = ["easter", "EASTER_JULIAN", "EASTER_ORTHODOX", "EASTER_WESTERN"] - -EASTER_JULIAN = 1 -EASTER_ORTHODOX = 2 -EASTER_WESTERN = 3 - - -def easter(year, method=EASTER_WESTERN): - """ - This method was ported from the work done by GM Arts, - on top of the algorithm by Claus Tondering, which was - based in part on the algorithm of Ouding (1940), as - quoted in "Explanatory Supplement to the Astronomical - Almanac", P. Kenneth Seidelmann, editor. - - This algorithm implements three different Easter - calculation methods: - - 1. Original calculation in Julian calendar, valid in - dates after 326 AD - 2. Original method, with date converted to Gregorian - calendar, valid in years 1583 to 4099 - 3. Revised method, in Gregorian calendar, valid in - years 1583 to 4099 as well - - These methods are represented by the constants: - - * ``EASTER_JULIAN = 1`` - * ``EASTER_ORTHODOX = 2`` - * ``EASTER_WESTERN = 3`` - - The default method is method 3. - - More about the algorithm may be found at: - - `GM Arts: Easter Algorithms `_ - - and - - `The Calendar FAQ: Easter `_ - - """ - - if not (1 <= method <= 3): - raise ValueError("invalid method") - - # g - Golden year - 1 - # c - Century - # h - (23 - Epact) mod 30 - # i - Number of days from March 21 to Paschal Full Moon - # j - Weekday for PFM (0=Sunday, etc) - # p - Number of days from March 21 to Sunday on or before PFM - # (-6 to 28 methods 1 & 3, to 56 for method 2) - # e - Extra days to add for method 2 (converting Julian - # date to Gregorian date) - - y = year - g = y % 19 - e = 0 - if method < 3: - # Old method - i = (19*g + 15) % 30 - j = (y + y//4 + i) % 7 - if method == 2: - # Extra dates to convert Julian to Gregorian date - e = 10 - if y > 1600: - e = e + y//100 - 16 - (y//100 - 16)//4 - else: - # New method - c = y//100 - h = (c - c//4 - (8*c + 13)//25 + 19*g + 15) % 30 - i = h - (h//28)*(1 - (h//28)*(29//(h + 1))*((21 - g)//11)) - j = (y + y//4 + i + 2 - c + c//4) % 7 - - # p can be from -6 to 56 corresponding to dates 22 March to 23 May - # (later dates apply to method 2, although 23 May never actually occurs) - p = i - j + e - d = 1 + (p + 27 + (p + 6)//40) % 31 - m = 3 + (p + 26)//30 - return datetime.date(int(y), int(m), int(d)) diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/parser/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/dateutil/parser/__init__.py deleted file mode 100644 index d174b0e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/dateutil/parser/__init__.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- -from ._parser import parse, parser, parserinfo, ParserError -from ._parser import DEFAULTPARSER, DEFAULTTZPARSER -from ._parser import UnknownTimezoneWarning - -from ._parser import __doc__ - -from .isoparser import isoparser, isoparse - -__all__ = ['parse', 'parser', 'parserinfo', - 'isoparse', 'isoparser', - 'ParserError', - 'UnknownTimezoneWarning'] - - -### -# Deprecate portions of the private interface so that downstream code that -# is improperly relying on it is given *some* notice. - - -def __deprecated_private_func(f): - from functools import wraps - import warnings - - msg = ('{name} is a private function and may break without warning, ' - 'it will be moved and or renamed in future versions.') - msg = msg.format(name=f.__name__) - - @wraps(f) - def deprecated_func(*args, **kwargs): - warnings.warn(msg, DeprecationWarning) - return f(*args, **kwargs) - - return deprecated_func - -def __deprecate_private_class(c): - import warnings - - msg = ('{name} is a private class and may break without warning, ' - 'it will be moved and or renamed in future versions.') - msg = msg.format(name=c.__name__) - - class private_class(c): - __doc__ = c.__doc__ - - def __init__(self, *args, **kwargs): - warnings.warn(msg, DeprecationWarning) - super(private_class, self).__init__(*args, **kwargs) - - private_class.__name__ = c.__name__ - - return private_class - - -from ._parser import _timelex, _resultbase -from ._parser import _tzparser, _parsetz - -_timelex = __deprecate_private_class(_timelex) -_tzparser = __deprecate_private_class(_tzparser) -_resultbase = __deprecate_private_class(_resultbase) -_parsetz = __deprecated_private_func(_parsetz) diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/parser/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/dateutil/parser/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 007c6bc..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/dateutil/parser/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/parser/__pycache__/_parser.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/dateutil/parser/__pycache__/_parser.cpython-38.pyc deleted file mode 100644 index 3534271..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/dateutil/parser/__pycache__/_parser.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/parser/__pycache__/isoparser.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/dateutil/parser/__pycache__/isoparser.cpython-38.pyc deleted file mode 100644 index 967c724..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/dateutil/parser/__pycache__/isoparser.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/parser/_parser.py b/plotter-app/venv/lib/python3.8/site-packages/dateutil/parser/_parser.py deleted file mode 100644 index 37d1663..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/dateutil/parser/_parser.py +++ /dev/null @@ -1,1613 +0,0 @@ -# -*- coding: utf-8 -*- -""" -This module offers a generic date/time string parser which is able to parse -most known formats to represent a date and/or time. - -This module attempts to be forgiving with regards to unlikely input formats, -returning a datetime object even for dates which are ambiguous. If an element -of a date/time stamp is omitted, the following rules are applied: - -- If AM or PM is left unspecified, a 24-hour clock is assumed, however, an hour - on a 12-hour clock (``0 <= hour <= 12``) *must* be specified if AM or PM is - specified. -- If a time zone is omitted, a timezone-naive datetime is returned. - -If any other elements are missing, they are taken from the -:class:`datetime.datetime` object passed to the parameter ``default``. If this -results in a day number exceeding the valid number of days per month, the -value falls back to the end of the month. - -Additional resources about date/time string formats can be found below: - -- `A summary of the international standard date and time notation - `_ -- `W3C Date and Time Formats `_ -- `Time Formats (Planetary Rings Node) `_ -- `CPAN ParseDate module - `_ -- `Java SimpleDateFormat Class - `_ -""" -from __future__ import unicode_literals - -import datetime -import re -import string -import time -import warnings - -from calendar import monthrange -from io import StringIO - -import six -from six import integer_types, text_type - -from decimal import Decimal - -from warnings import warn - -from .. import relativedelta -from .. import tz - -__all__ = ["parse", "parserinfo", "ParserError"] - - -# TODO: pandas.core.tools.datetimes imports this explicitly. Might be worth -# making public and/or figuring out if there is something we can -# take off their plate. -class _timelex(object): - # Fractional seconds are sometimes split by a comma - _split_decimal = re.compile("([.,])") - - def __init__(self, instream): - if isinstance(instream, (bytes, bytearray)): - instream = instream.decode() - - if isinstance(instream, text_type): - instream = StringIO(instream) - elif getattr(instream, 'read', None) is None: - raise TypeError('Parser must be a string or character stream, not ' - '{itype}'.format(itype=instream.__class__.__name__)) - - self.instream = instream - self.charstack = [] - self.tokenstack = [] - self.eof = False - - def get_token(self): - """ - This function breaks the time string into lexical units (tokens), which - can be parsed by the parser. Lexical units are demarcated by changes in - the character set, so any continuous string of letters is considered - one unit, any continuous string of numbers is considered one unit. - - The main complication arises from the fact that dots ('.') can be used - both as separators (e.g. "Sep.20.2009") or decimal points (e.g. - "4:30:21.447"). As such, it is necessary to read the full context of - any dot-separated strings before breaking it into tokens; as such, this - function maintains a "token stack", for when the ambiguous context - demands that multiple tokens be parsed at once. - """ - if self.tokenstack: - return self.tokenstack.pop(0) - - seenletters = False - token = None - state = None - - while not self.eof: - # We only realize that we've reached the end of a token when we - # find a character that's not part of the current token - since - # that character may be part of the next token, it's stored in the - # charstack. - if self.charstack: - nextchar = self.charstack.pop(0) - else: - nextchar = self.instream.read(1) - while nextchar == '\x00': - nextchar = self.instream.read(1) - - if not nextchar: - self.eof = True - break - elif not state: - # First character of the token - determines if we're starting - # to parse a word, a number or something else. - token = nextchar - if self.isword(nextchar): - state = 'a' - elif self.isnum(nextchar): - state = '0' - elif self.isspace(nextchar): - token = ' ' - break # emit token - else: - break # emit token - elif state == 'a': - # If we've already started reading a word, we keep reading - # letters until we find something that's not part of a word. - seenletters = True - if self.isword(nextchar): - token += nextchar - elif nextchar == '.': - token += nextchar - state = 'a.' - else: - self.charstack.append(nextchar) - break # emit token - elif state == '0': - # If we've already started reading a number, we keep reading - # numbers until we find something that doesn't fit. - if self.isnum(nextchar): - token += nextchar - elif nextchar == '.' or (nextchar == ',' and len(token) >= 2): - token += nextchar - state = '0.' - else: - self.charstack.append(nextchar) - break # emit token - elif state == 'a.': - # If we've seen some letters and a dot separator, continue - # parsing, and the tokens will be broken up later. - seenletters = True - if nextchar == '.' or self.isword(nextchar): - token += nextchar - elif self.isnum(nextchar) and token[-1] == '.': - token += nextchar - state = '0.' - else: - self.charstack.append(nextchar) - break # emit token - elif state == '0.': - # If we've seen at least one dot separator, keep going, we'll - # break up the tokens later. - if nextchar == '.' or self.isnum(nextchar): - token += nextchar - elif self.isword(nextchar) and token[-1] == '.': - token += nextchar - state = 'a.' - else: - self.charstack.append(nextchar) - break # emit token - - if (state in ('a.', '0.') and (seenletters or token.count('.') > 1 or - token[-1] in '.,')): - l = self._split_decimal.split(token) - token = l[0] - for tok in l[1:]: - if tok: - self.tokenstack.append(tok) - - if state == '0.' and token.count('.') == 0: - token = token.replace(',', '.') - - return token - - def __iter__(self): - return self - - def __next__(self): - token = self.get_token() - if token is None: - raise StopIteration - - return token - - def next(self): - return self.__next__() # Python 2.x support - - @classmethod - def split(cls, s): - return list(cls(s)) - - @classmethod - def isword(cls, nextchar): - """ Whether or not the next character is part of a word """ - return nextchar.isalpha() - - @classmethod - def isnum(cls, nextchar): - """ Whether the next character is part of a number """ - return nextchar.isdigit() - - @classmethod - def isspace(cls, nextchar): - """ Whether the next character is whitespace """ - return nextchar.isspace() - - -class _resultbase(object): - - def __init__(self): - for attr in self.__slots__: - setattr(self, attr, None) - - def _repr(self, classname): - l = [] - for attr in self.__slots__: - value = getattr(self, attr) - if value is not None: - l.append("%s=%s" % (attr, repr(value))) - return "%s(%s)" % (classname, ", ".join(l)) - - def __len__(self): - return (sum(getattr(self, attr) is not None - for attr in self.__slots__)) - - def __repr__(self): - return self._repr(self.__class__.__name__) - - -class parserinfo(object): - """ - Class which handles what inputs are accepted. Subclass this to customize - the language and acceptable values for each parameter. - - :param dayfirst: - Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the day (``True``) or month (``False``). If - ``yearfirst`` is set to ``True``, this distinguishes between YDM - and YMD. Default is ``False``. - - :param yearfirst: - Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the year. If ``True``, the first number is taken - to be the year, otherwise the last number is taken to be the year. - Default is ``False``. - """ - - # m from a.m/p.m, t from ISO T separator - JUMP = [" ", ".", ",", ";", "-", "/", "'", - "at", "on", "and", "ad", "m", "t", "of", - "st", "nd", "rd", "th"] - - WEEKDAYS = [("Mon", "Monday"), - ("Tue", "Tuesday"), # TODO: "Tues" - ("Wed", "Wednesday"), - ("Thu", "Thursday"), # TODO: "Thurs" - ("Fri", "Friday"), - ("Sat", "Saturday"), - ("Sun", "Sunday")] - MONTHS = [("Jan", "January"), - ("Feb", "February"), # TODO: "Febr" - ("Mar", "March"), - ("Apr", "April"), - ("May", "May"), - ("Jun", "June"), - ("Jul", "July"), - ("Aug", "August"), - ("Sep", "Sept", "September"), - ("Oct", "October"), - ("Nov", "November"), - ("Dec", "December")] - HMS = [("h", "hour", "hours"), - ("m", "minute", "minutes"), - ("s", "second", "seconds")] - AMPM = [("am", "a"), - ("pm", "p")] - UTCZONE = ["UTC", "GMT", "Z", "z"] - PERTAIN = ["of"] - TZOFFSET = {} - # TODO: ERA = ["AD", "BC", "CE", "BCE", "Stardate", - # "Anno Domini", "Year of Our Lord"] - - def __init__(self, dayfirst=False, yearfirst=False): - self._jump = self._convert(self.JUMP) - self._weekdays = self._convert(self.WEEKDAYS) - self._months = self._convert(self.MONTHS) - self._hms = self._convert(self.HMS) - self._ampm = self._convert(self.AMPM) - self._utczone = self._convert(self.UTCZONE) - self._pertain = self._convert(self.PERTAIN) - - self.dayfirst = dayfirst - self.yearfirst = yearfirst - - self._year = time.localtime().tm_year - self._century = self._year // 100 * 100 - - def _convert(self, lst): - dct = {} - for i, v in enumerate(lst): - if isinstance(v, tuple): - for v in v: - dct[v.lower()] = i - else: - dct[v.lower()] = i - return dct - - def jump(self, name): - return name.lower() in self._jump - - def weekday(self, name): - try: - return self._weekdays[name.lower()] - except KeyError: - pass - return None - - def month(self, name): - try: - return self._months[name.lower()] + 1 - except KeyError: - pass - return None - - def hms(self, name): - try: - return self._hms[name.lower()] - except KeyError: - return None - - def ampm(self, name): - try: - return self._ampm[name.lower()] - except KeyError: - return None - - def pertain(self, name): - return name.lower() in self._pertain - - def utczone(self, name): - return name.lower() in self._utczone - - def tzoffset(self, name): - if name in self._utczone: - return 0 - - return self.TZOFFSET.get(name) - - def convertyear(self, year, century_specified=False): - """ - Converts two-digit years to year within [-50, 49] - range of self._year (current local time) - """ - - # Function contract is that the year is always positive - assert year >= 0 - - if year < 100 and not century_specified: - # assume current century to start - year += self._century - - if year >= self._year + 50: # if too far in future - year -= 100 - elif year < self._year - 50: # if too far in past - year += 100 - - return year - - def validate(self, res): - # move to info - if res.year is not None: - res.year = self.convertyear(res.year, res.century_specified) - - if ((res.tzoffset == 0 and not res.tzname) or - (res.tzname == 'Z' or res.tzname == 'z')): - res.tzname = "UTC" - res.tzoffset = 0 - elif res.tzoffset != 0 and res.tzname and self.utczone(res.tzname): - res.tzoffset = 0 - return True - - -class _ymd(list): - def __init__(self, *args, **kwargs): - super(self.__class__, self).__init__(*args, **kwargs) - self.century_specified = False - self.dstridx = None - self.mstridx = None - self.ystridx = None - - @property - def has_year(self): - return self.ystridx is not None - - @property - def has_month(self): - return self.mstridx is not None - - @property - def has_day(self): - return self.dstridx is not None - - def could_be_day(self, value): - if self.has_day: - return False - elif not self.has_month: - return 1 <= value <= 31 - elif not self.has_year: - # Be permissive, assume leap year - month = self[self.mstridx] - return 1 <= value <= monthrange(2000, month)[1] - else: - month = self[self.mstridx] - year = self[self.ystridx] - return 1 <= value <= monthrange(year, month)[1] - - def append(self, val, label=None): - if hasattr(val, '__len__'): - if val.isdigit() and len(val) > 2: - self.century_specified = True - if label not in [None, 'Y']: # pragma: no cover - raise ValueError(label) - label = 'Y' - elif val > 100: - self.century_specified = True - if label not in [None, 'Y']: # pragma: no cover - raise ValueError(label) - label = 'Y' - - super(self.__class__, self).append(int(val)) - - if label == 'M': - if self.has_month: - raise ValueError('Month is already set') - self.mstridx = len(self) - 1 - elif label == 'D': - if self.has_day: - raise ValueError('Day is already set') - self.dstridx = len(self) - 1 - elif label == 'Y': - if self.has_year: - raise ValueError('Year is already set') - self.ystridx = len(self) - 1 - - def _resolve_from_stridxs(self, strids): - """ - Try to resolve the identities of year/month/day elements using - ystridx, mstridx, and dstridx, if enough of these are specified. - """ - if len(self) == 3 and len(strids) == 2: - # we can back out the remaining stridx value - missing = [x for x in range(3) if x not in strids.values()] - key = [x for x in ['y', 'm', 'd'] if x not in strids] - assert len(missing) == len(key) == 1 - key = key[0] - val = missing[0] - strids[key] = val - - assert len(self) == len(strids) # otherwise this should not be called - out = {key: self[strids[key]] for key in strids} - return (out.get('y'), out.get('m'), out.get('d')) - - def resolve_ymd(self, yearfirst, dayfirst): - len_ymd = len(self) - year, month, day = (None, None, None) - - strids = (('y', self.ystridx), - ('m', self.mstridx), - ('d', self.dstridx)) - - strids = {key: val for key, val in strids if val is not None} - if (len(self) == len(strids) > 0 or - (len(self) == 3 and len(strids) == 2)): - return self._resolve_from_stridxs(strids) - - mstridx = self.mstridx - - if len_ymd > 3: - raise ValueError("More than three YMD values") - elif len_ymd == 1 or (mstridx is not None and len_ymd == 2): - # One member, or two members with a month string - if mstridx is not None: - month = self[mstridx] - # since mstridx is 0 or 1, self[mstridx-1] always - # looks up the other element - other = self[mstridx - 1] - else: - other = self[0] - - if len_ymd > 1 or mstridx is None: - if other > 31: - year = other - else: - day = other - - elif len_ymd == 2: - # Two members with numbers - if self[0] > 31: - # 99-01 - year, month = self - elif self[1] > 31: - # 01-99 - month, year = self - elif dayfirst and self[1] <= 12: - # 13-01 - day, month = self - else: - # 01-13 - month, day = self - - elif len_ymd == 3: - # Three members - if mstridx == 0: - if self[1] > 31: - # Apr-2003-25 - month, year, day = self - else: - month, day, year = self - elif mstridx == 1: - if self[0] > 31 or (yearfirst and self[2] <= 31): - # 99-Jan-01 - year, month, day = self - else: - # 01-Jan-01 - # Give precedence to day-first, since - # two-digit years is usually hand-written. - day, month, year = self - - elif mstridx == 2: - # WTF!? - if self[1] > 31: - # 01-99-Jan - day, year, month = self - else: - # 99-01-Jan - year, day, month = self - - else: - if (self[0] > 31 or - self.ystridx == 0 or - (yearfirst and self[1] <= 12 and self[2] <= 31)): - # 99-01-01 - if dayfirst and self[2] <= 12: - year, day, month = self - else: - year, month, day = self - elif self[0] > 12 or (dayfirst and self[1] <= 12): - # 13-01-01 - day, month, year = self - else: - # 01-13-01 - month, day, year = self - - return year, month, day - - -class parser(object): - def __init__(self, info=None): - self.info = info or parserinfo() - - def parse(self, timestr, default=None, - ignoretz=False, tzinfos=None, **kwargs): - """ - Parse the date/time string into a :class:`datetime.datetime` object. - - :param timestr: - Any date/time string using the supported formats. - - :param default: - The default datetime object, if this is a datetime object and not - ``None``, elements specified in ``timestr`` replace elements in the - default object. - - :param ignoretz: - If set ``True``, time zones in parsed strings are ignored and a - naive :class:`datetime.datetime` object is returned. - - :param tzinfos: - Additional time zone names / aliases which may be present in the - string. This argument maps time zone names (and optionally offsets - from those time zones) to time zones. This parameter can be a - dictionary with timezone aliases mapping time zone names to time - zones or a function taking two parameters (``tzname`` and - ``tzoffset``) and returning a time zone. - - The timezones to which the names are mapped can be an integer - offset from UTC in seconds or a :class:`tzinfo` object. - - .. doctest:: - :options: +NORMALIZE_WHITESPACE - - >>> from dateutil.parser import parse - >>> from dateutil.tz import gettz - >>> tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")} - >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos) - datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200)) - >>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos) - datetime.datetime(2012, 1, 19, 17, 21, - tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago')) - - This parameter is ignored if ``ignoretz`` is set. - - :param \\*\\*kwargs: - Keyword arguments as passed to ``_parse()``. - - :return: - Returns a :class:`datetime.datetime` object or, if the - ``fuzzy_with_tokens`` option is ``True``, returns a tuple, the - first element being a :class:`datetime.datetime` object, the second - a tuple containing the fuzzy tokens. - - :raises ParserError: - Raised for invalid or unknown string format, if the provided - :class:`tzinfo` is not in a valid format, or if an invalid date - would be created. - - :raises TypeError: - Raised for non-string or character stream input. - - :raises OverflowError: - Raised if the parsed date exceeds the largest valid C integer on - your system. - """ - - if default is None: - default = datetime.datetime.now().replace(hour=0, minute=0, - second=0, microsecond=0) - - res, skipped_tokens = self._parse(timestr, **kwargs) - - if res is None: - raise ParserError("Unknown string format: %s", timestr) - - if len(res) == 0: - raise ParserError("String does not contain a date: %s", timestr) - - try: - ret = self._build_naive(res, default) - except ValueError as e: - six.raise_from(ParserError(str(e) + ": %s", timestr), e) - - if not ignoretz: - ret = self._build_tzaware(ret, res, tzinfos) - - if kwargs.get('fuzzy_with_tokens', False): - return ret, skipped_tokens - else: - return ret - - class _result(_resultbase): - __slots__ = ["year", "month", "day", "weekday", - "hour", "minute", "second", "microsecond", - "tzname", "tzoffset", "ampm","any_unused_tokens"] - - def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False, - fuzzy_with_tokens=False): - """ - Private method which performs the heavy lifting of parsing, called from - ``parse()``, which passes on its ``kwargs`` to this function. - - :param timestr: - The string to parse. - - :param dayfirst: - Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the day (``True``) or month (``False``). If - ``yearfirst`` is set to ``True``, this distinguishes between YDM - and YMD. If set to ``None``, this value is retrieved from the - current :class:`parserinfo` object (which itself defaults to - ``False``). - - :param yearfirst: - Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the year. If ``True``, the first number is taken - to be the year, otherwise the last number is taken to be the year. - If this is set to ``None``, the value is retrieved from the current - :class:`parserinfo` object (which itself defaults to ``False``). - - :param fuzzy: - Whether to allow fuzzy parsing, allowing for string like "Today is - January 1, 2047 at 8:21:00AM". - - :param fuzzy_with_tokens: - If ``True``, ``fuzzy`` is automatically set to True, and the parser - will return a tuple where the first element is the parsed - :class:`datetime.datetime` datetimestamp and the second element is - a tuple containing the portions of the string which were ignored: - - .. doctest:: - - >>> from dateutil.parser import parse - >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True) - (datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at ')) - - """ - if fuzzy_with_tokens: - fuzzy = True - - info = self.info - - if dayfirst is None: - dayfirst = info.dayfirst - - if yearfirst is None: - yearfirst = info.yearfirst - - res = self._result() - l = _timelex.split(timestr) # Splits the timestr into tokens - - skipped_idxs = [] - - # year/month/day list - ymd = _ymd() - - len_l = len(l) - i = 0 - try: - while i < len_l: - - # Check if it's a number - value_repr = l[i] - try: - value = float(value_repr) - except ValueError: - value = None - - if value is not None: - # Numeric token - i = self._parse_numeric_token(l, i, info, ymd, res, fuzzy) - - # Check weekday - elif info.weekday(l[i]) is not None: - value = info.weekday(l[i]) - res.weekday = value - - # Check month name - elif info.month(l[i]) is not None: - value = info.month(l[i]) - ymd.append(value, 'M') - - if i + 1 < len_l: - if l[i + 1] in ('-', '/'): - # Jan-01[-99] - sep = l[i + 1] - ymd.append(l[i + 2]) - - if i + 3 < len_l and l[i + 3] == sep: - # Jan-01-99 - ymd.append(l[i + 4]) - i += 2 - - i += 2 - - elif (i + 4 < len_l and l[i + 1] == l[i + 3] == ' ' and - info.pertain(l[i + 2])): - # Jan of 01 - # In this case, 01 is clearly year - if l[i + 4].isdigit(): - # Convert it here to become unambiguous - value = int(l[i + 4]) - year = str(info.convertyear(value)) - ymd.append(year, 'Y') - else: - # Wrong guess - pass - # TODO: not hit in tests - i += 4 - - # Check am/pm - elif info.ampm(l[i]) is not None: - value = info.ampm(l[i]) - val_is_ampm = self._ampm_valid(res.hour, res.ampm, fuzzy) - - if val_is_ampm: - res.hour = self._adjust_ampm(res.hour, value) - res.ampm = value - - elif fuzzy: - skipped_idxs.append(i) - - # Check for a timezone name - elif self._could_be_tzname(res.hour, res.tzname, res.tzoffset, l[i]): - res.tzname = l[i] - res.tzoffset = info.tzoffset(res.tzname) - - # Check for something like GMT+3, or BRST+3. Notice - # that it doesn't mean "I am 3 hours after GMT", but - # "my time +3 is GMT". If found, we reverse the - # logic so that timezone parsing code will get it - # right. - if i + 1 < len_l and l[i + 1] in ('+', '-'): - l[i + 1] = ('+', '-')[l[i + 1] == '+'] - res.tzoffset = None - if info.utczone(res.tzname): - # With something like GMT+3, the timezone - # is *not* GMT. - res.tzname = None - - # Check for a numbered timezone - elif res.hour is not None and l[i] in ('+', '-'): - signal = (-1, 1)[l[i] == '+'] - len_li = len(l[i + 1]) - - # TODO: check that l[i + 1] is integer? - if len_li == 4: - # -0300 - hour_offset = int(l[i + 1][:2]) - min_offset = int(l[i + 1][2:]) - elif i + 2 < len_l and l[i + 2] == ':': - # -03:00 - hour_offset = int(l[i + 1]) - min_offset = int(l[i + 3]) # TODO: Check that l[i+3] is minute-like? - i += 2 - elif len_li <= 2: - # -[0]3 - hour_offset = int(l[i + 1][:2]) - min_offset = 0 - else: - raise ValueError(timestr) - - res.tzoffset = signal * (hour_offset * 3600 + min_offset * 60) - - # Look for a timezone name between parenthesis - if (i + 5 < len_l and - info.jump(l[i + 2]) and l[i + 3] == '(' and - l[i + 5] == ')' and - 3 <= len(l[i + 4]) and - self._could_be_tzname(res.hour, res.tzname, - None, l[i + 4])): - # -0300 (BRST) - res.tzname = l[i + 4] - i += 4 - - i += 1 - - # Check jumps - elif not (info.jump(l[i]) or fuzzy): - raise ValueError(timestr) - - else: - skipped_idxs.append(i) - i += 1 - - # Process year/month/day - year, month, day = ymd.resolve_ymd(yearfirst, dayfirst) - - res.century_specified = ymd.century_specified - res.year = year - res.month = month - res.day = day - - except (IndexError, ValueError): - return None, None - - if not info.validate(res): - return None, None - - if fuzzy_with_tokens: - skipped_tokens = self._recombine_skipped(l, skipped_idxs) - return res, tuple(skipped_tokens) - else: - return res, None - - def _parse_numeric_token(self, tokens, idx, info, ymd, res, fuzzy): - # Token is a number - value_repr = tokens[idx] - try: - value = self._to_decimal(value_repr) - except Exception as e: - six.raise_from(ValueError('Unknown numeric token'), e) - - len_li = len(value_repr) - - len_l = len(tokens) - - if (len(ymd) == 3 and len_li in (2, 4) and - res.hour is None and - (idx + 1 >= len_l or - (tokens[idx + 1] != ':' and - info.hms(tokens[idx + 1]) is None))): - # 19990101T23[59] - s = tokens[idx] - res.hour = int(s[:2]) - - if len_li == 4: - res.minute = int(s[2:]) - - elif len_li == 6 or (len_li > 6 and tokens[idx].find('.') == 6): - # YYMMDD or HHMMSS[.ss] - s = tokens[idx] - - if not ymd and '.' not in tokens[idx]: - ymd.append(s[:2]) - ymd.append(s[2:4]) - ymd.append(s[4:]) - else: - # 19990101T235959[.59] - - # TODO: Check if res attributes already set. - res.hour = int(s[:2]) - res.minute = int(s[2:4]) - res.second, res.microsecond = self._parsems(s[4:]) - - elif len_li in (8, 12, 14): - # YYYYMMDD - s = tokens[idx] - ymd.append(s[:4], 'Y') - ymd.append(s[4:6]) - ymd.append(s[6:8]) - - if len_li > 8: - res.hour = int(s[8:10]) - res.minute = int(s[10:12]) - - if len_li > 12: - res.second = int(s[12:]) - - elif self._find_hms_idx(idx, tokens, info, allow_jump=True) is not None: - # HH[ ]h or MM[ ]m or SS[.ss][ ]s - hms_idx = self._find_hms_idx(idx, tokens, info, allow_jump=True) - (idx, hms) = self._parse_hms(idx, tokens, info, hms_idx) - if hms is not None: - # TODO: checking that hour/minute/second are not - # already set? - self._assign_hms(res, value_repr, hms) - - elif idx + 2 < len_l and tokens[idx + 1] == ':': - # HH:MM[:SS[.ss]] - res.hour = int(value) - value = self._to_decimal(tokens[idx + 2]) # TODO: try/except for this? - (res.minute, res.second) = self._parse_min_sec(value) - - if idx + 4 < len_l and tokens[idx + 3] == ':': - res.second, res.microsecond = self._parsems(tokens[idx + 4]) - - idx += 2 - - idx += 2 - - elif idx + 1 < len_l and tokens[idx + 1] in ('-', '/', '.'): - sep = tokens[idx + 1] - ymd.append(value_repr) - - if idx + 2 < len_l and not info.jump(tokens[idx + 2]): - if tokens[idx + 2].isdigit(): - # 01-01[-01] - ymd.append(tokens[idx + 2]) - else: - # 01-Jan[-01] - value = info.month(tokens[idx + 2]) - - if value is not None: - ymd.append(value, 'M') - else: - raise ValueError() - - if idx + 3 < len_l and tokens[idx + 3] == sep: - # We have three members - value = info.month(tokens[idx + 4]) - - if value is not None: - ymd.append(value, 'M') - else: - ymd.append(tokens[idx + 4]) - idx += 2 - - idx += 1 - idx += 1 - - elif idx + 1 >= len_l or info.jump(tokens[idx + 1]): - if idx + 2 < len_l and info.ampm(tokens[idx + 2]) is not None: - # 12 am - hour = int(value) - res.hour = self._adjust_ampm(hour, info.ampm(tokens[idx + 2])) - idx += 1 - else: - # Year, month or day - ymd.append(value) - idx += 1 - - elif info.ampm(tokens[idx + 1]) is not None and (0 <= value < 24): - # 12am - hour = int(value) - res.hour = self._adjust_ampm(hour, info.ampm(tokens[idx + 1])) - idx += 1 - - elif ymd.could_be_day(value): - ymd.append(value) - - elif not fuzzy: - raise ValueError() - - return idx - - def _find_hms_idx(self, idx, tokens, info, allow_jump): - len_l = len(tokens) - - if idx+1 < len_l and info.hms(tokens[idx+1]) is not None: - # There is an "h", "m", or "s" label following this token. We take - # assign the upcoming label to the current token. - # e.g. the "12" in 12h" - hms_idx = idx + 1 - - elif (allow_jump and idx+2 < len_l and tokens[idx+1] == ' ' and - info.hms(tokens[idx+2]) is not None): - # There is a space and then an "h", "m", or "s" label. - # e.g. the "12" in "12 h" - hms_idx = idx + 2 - - elif idx > 0 and info.hms(tokens[idx-1]) is not None: - # There is a "h", "m", or "s" preceding this token. Since neither - # of the previous cases was hit, there is no label following this - # token, so we use the previous label. - # e.g. the "04" in "12h04" - hms_idx = idx-1 - - elif (1 < idx == len_l-1 and tokens[idx-1] == ' ' and - info.hms(tokens[idx-2]) is not None): - # If we are looking at the final token, we allow for a - # backward-looking check to skip over a space. - # TODO: Are we sure this is the right condition here? - hms_idx = idx - 2 - - else: - hms_idx = None - - return hms_idx - - def _assign_hms(self, res, value_repr, hms): - # See GH issue #427, fixing float rounding - value = self._to_decimal(value_repr) - - if hms == 0: - # Hour - res.hour = int(value) - if value % 1: - res.minute = int(60*(value % 1)) - - elif hms == 1: - (res.minute, res.second) = self._parse_min_sec(value) - - elif hms == 2: - (res.second, res.microsecond) = self._parsems(value_repr) - - def _could_be_tzname(self, hour, tzname, tzoffset, token): - return (hour is not None and - tzname is None and - tzoffset is None and - len(token) <= 5 and - (all(x in string.ascii_uppercase for x in token) - or token in self.info.UTCZONE)) - - def _ampm_valid(self, hour, ampm, fuzzy): - """ - For fuzzy parsing, 'a' or 'am' (both valid English words) - may erroneously trigger the AM/PM flag. Deal with that - here. - """ - val_is_ampm = True - - # If there's already an AM/PM flag, this one isn't one. - if fuzzy and ampm is not None: - val_is_ampm = False - - # If AM/PM is found and hour is not, raise a ValueError - if hour is None: - if fuzzy: - val_is_ampm = False - else: - raise ValueError('No hour specified with AM or PM flag.') - elif not 0 <= hour <= 12: - # If AM/PM is found, it's a 12 hour clock, so raise - # an error for invalid range - if fuzzy: - val_is_ampm = False - else: - raise ValueError('Invalid hour specified for 12-hour clock.') - - return val_is_ampm - - def _adjust_ampm(self, hour, ampm): - if hour < 12 and ampm == 1: - hour += 12 - elif hour == 12 and ampm == 0: - hour = 0 - return hour - - def _parse_min_sec(self, value): - # TODO: Every usage of this function sets res.second to the return - # value. Are there any cases where second will be returned as None and - # we *don't* want to set res.second = None? - minute = int(value) - second = None - - sec_remainder = value % 1 - if sec_remainder: - second = int(60 * sec_remainder) - return (minute, second) - - def _parse_hms(self, idx, tokens, info, hms_idx): - # TODO: Is this going to admit a lot of false-positives for when we - # just happen to have digits and "h", "m" or "s" characters in non-date - # text? I guess hex hashes won't have that problem, but there's plenty - # of random junk out there. - if hms_idx is None: - hms = None - new_idx = idx - elif hms_idx > idx: - hms = info.hms(tokens[hms_idx]) - new_idx = hms_idx - else: - # Looking backwards, increment one. - hms = info.hms(tokens[hms_idx]) + 1 - new_idx = idx - - return (new_idx, hms) - - # ------------------------------------------------------------------ - # Handling for individual tokens. These are kept as methods instead - # of functions for the sake of customizability via subclassing. - - def _parsems(self, value): - """Parse a I[.F] seconds value into (seconds, microseconds).""" - if "." not in value: - return int(value), 0 - else: - i, f = value.split(".") - return int(i), int(f.ljust(6, "0")[:6]) - - def _to_decimal(self, val): - try: - decimal_value = Decimal(val) - # See GH 662, edge case, infinite value should not be converted - # via `_to_decimal` - if not decimal_value.is_finite(): - raise ValueError("Converted decimal value is infinite or NaN") - except Exception as e: - msg = "Could not convert %s to decimal" % val - six.raise_from(ValueError(msg), e) - else: - return decimal_value - - # ------------------------------------------------------------------ - # Post-Parsing construction of datetime output. These are kept as - # methods instead of functions for the sake of customizability via - # subclassing. - - def _build_tzinfo(self, tzinfos, tzname, tzoffset): - if callable(tzinfos): - tzdata = tzinfos(tzname, tzoffset) - else: - tzdata = tzinfos.get(tzname) - # handle case where tzinfo is paased an options that returns None - # eg tzinfos = {'BRST' : None} - if isinstance(tzdata, datetime.tzinfo) or tzdata is None: - tzinfo = tzdata - elif isinstance(tzdata, text_type): - tzinfo = tz.tzstr(tzdata) - elif isinstance(tzdata, integer_types): - tzinfo = tz.tzoffset(tzname, tzdata) - else: - raise TypeError("Offset must be tzinfo subclass, tz string, " - "or int offset.") - return tzinfo - - def _build_tzaware(self, naive, res, tzinfos): - if (callable(tzinfos) or (tzinfos and res.tzname in tzinfos)): - tzinfo = self._build_tzinfo(tzinfos, res.tzname, res.tzoffset) - aware = naive.replace(tzinfo=tzinfo) - aware = self._assign_tzname(aware, res.tzname) - - elif res.tzname and res.tzname in time.tzname: - aware = naive.replace(tzinfo=tz.tzlocal()) - - # Handle ambiguous local datetime - aware = self._assign_tzname(aware, res.tzname) - - # This is mostly relevant for winter GMT zones parsed in the UK - if (aware.tzname() != res.tzname and - res.tzname in self.info.UTCZONE): - aware = aware.replace(tzinfo=tz.UTC) - - elif res.tzoffset == 0: - aware = naive.replace(tzinfo=tz.UTC) - - elif res.tzoffset: - aware = naive.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset)) - - elif not res.tzname and not res.tzoffset: - # i.e. no timezone information was found. - aware = naive - - elif res.tzname: - # tz-like string was parsed but we don't know what to do - # with it - warnings.warn("tzname {tzname} identified but not understood. " - "Pass `tzinfos` argument in order to correctly " - "return a timezone-aware datetime. In a future " - "version, this will raise an " - "exception.".format(tzname=res.tzname), - category=UnknownTimezoneWarning) - aware = naive - - return aware - - def _build_naive(self, res, default): - repl = {} - for attr in ("year", "month", "day", "hour", - "minute", "second", "microsecond"): - value = getattr(res, attr) - if value is not None: - repl[attr] = value - - if 'day' not in repl: - # If the default day exceeds the last day of the month, fall back - # to the end of the month. - cyear = default.year if res.year is None else res.year - cmonth = default.month if res.month is None else res.month - cday = default.day if res.day is None else res.day - - if cday > monthrange(cyear, cmonth)[1]: - repl['day'] = monthrange(cyear, cmonth)[1] - - naive = default.replace(**repl) - - if res.weekday is not None and not res.day: - naive = naive + relativedelta.relativedelta(weekday=res.weekday) - - return naive - - def _assign_tzname(self, dt, tzname): - if dt.tzname() != tzname: - new_dt = tz.enfold(dt, fold=1) - if new_dt.tzname() == tzname: - return new_dt - - return dt - - def _recombine_skipped(self, tokens, skipped_idxs): - """ - >>> tokens = ["foo", " ", "bar", " ", "19June2000", "baz"] - >>> skipped_idxs = [0, 1, 2, 5] - >>> _recombine_skipped(tokens, skipped_idxs) - ["foo bar", "baz"] - """ - skipped_tokens = [] - for i, idx in enumerate(sorted(skipped_idxs)): - if i > 0 and idx - 1 == skipped_idxs[i - 1]: - skipped_tokens[-1] = skipped_tokens[-1] + tokens[idx] - else: - skipped_tokens.append(tokens[idx]) - - return skipped_tokens - - -DEFAULTPARSER = parser() - - -def parse(timestr, parserinfo=None, **kwargs): - """ - - Parse a string in one of the supported formats, using the - ``parserinfo`` parameters. - - :param timestr: - A string containing a date/time stamp. - - :param parserinfo: - A :class:`parserinfo` object containing parameters for the parser. - If ``None``, the default arguments to the :class:`parserinfo` - constructor are used. - - The ``**kwargs`` parameter takes the following keyword arguments: - - :param default: - The default datetime object, if this is a datetime object and not - ``None``, elements specified in ``timestr`` replace elements in the - default object. - - :param ignoretz: - If set ``True``, time zones in parsed strings are ignored and a naive - :class:`datetime` object is returned. - - :param tzinfos: - Additional time zone names / aliases which may be present in the - string. This argument maps time zone names (and optionally offsets - from those time zones) to time zones. This parameter can be a - dictionary with timezone aliases mapping time zone names to time - zones or a function taking two parameters (``tzname`` and - ``tzoffset``) and returning a time zone. - - The timezones to which the names are mapped can be an integer - offset from UTC in seconds or a :class:`tzinfo` object. - - .. doctest:: - :options: +NORMALIZE_WHITESPACE - - >>> from dateutil.parser import parse - >>> from dateutil.tz import gettz - >>> tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")} - >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos) - datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200)) - >>> parse("2012-01-19 17:21:00 CST", tzinfos=tzinfos) - datetime.datetime(2012, 1, 19, 17, 21, - tzinfo=tzfile('/usr/share/zoneinfo/America/Chicago')) - - This parameter is ignored if ``ignoretz`` is set. - - :param dayfirst: - Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the day (``True``) or month (``False``). If - ``yearfirst`` is set to ``True``, this distinguishes between YDM and - YMD. If set to ``None``, this value is retrieved from the current - :class:`parserinfo` object (which itself defaults to ``False``). - - :param yearfirst: - Whether to interpret the first value in an ambiguous 3-integer date - (e.g. 01/05/09) as the year. If ``True``, the first number is taken to - be the year, otherwise the last number is taken to be the year. If - this is set to ``None``, the value is retrieved from the current - :class:`parserinfo` object (which itself defaults to ``False``). - - :param fuzzy: - Whether to allow fuzzy parsing, allowing for string like "Today is - January 1, 2047 at 8:21:00AM". - - :param fuzzy_with_tokens: - If ``True``, ``fuzzy`` is automatically set to True, and the parser - will return a tuple where the first element is the parsed - :class:`datetime.datetime` datetimestamp and the second element is - a tuple containing the portions of the string which were ignored: - - .. doctest:: - - >>> from dateutil.parser import parse - >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True) - (datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at ')) - - :return: - Returns a :class:`datetime.datetime` object or, if the - ``fuzzy_with_tokens`` option is ``True``, returns a tuple, the - first element being a :class:`datetime.datetime` object, the second - a tuple containing the fuzzy tokens. - - :raises ParserError: - Raised for invalid or unknown string formats, if the provided - :class:`tzinfo` is not in a valid format, or if an invalid date would - be created. - - :raises OverflowError: - Raised if the parsed date exceeds the largest valid C integer on - your system. - """ - if parserinfo: - return parser(parserinfo).parse(timestr, **kwargs) - else: - return DEFAULTPARSER.parse(timestr, **kwargs) - - -class _tzparser(object): - - class _result(_resultbase): - - __slots__ = ["stdabbr", "stdoffset", "dstabbr", "dstoffset", - "start", "end"] - - class _attr(_resultbase): - __slots__ = ["month", "week", "weekday", - "yday", "jyday", "day", "time"] - - def __repr__(self): - return self._repr("") - - def __init__(self): - _resultbase.__init__(self) - self.start = self._attr() - self.end = self._attr() - - def parse(self, tzstr): - res = self._result() - l = [x for x in re.split(r'([,:.]|[a-zA-Z]+|[0-9]+)',tzstr) if x] - used_idxs = list() - try: - - len_l = len(l) - - i = 0 - while i < len_l: - # BRST+3[BRDT[+2]] - j = i - while j < len_l and not [x for x in l[j] - if x in "0123456789:,-+"]: - j += 1 - if j != i: - if not res.stdabbr: - offattr = "stdoffset" - res.stdabbr = "".join(l[i:j]) - else: - offattr = "dstoffset" - res.dstabbr = "".join(l[i:j]) - - for ii in range(j): - used_idxs.append(ii) - i = j - if (i < len_l and (l[i] in ('+', '-') or l[i][0] in - "0123456789")): - if l[i] in ('+', '-'): - # Yes, that's right. See the TZ variable - # documentation. - signal = (1, -1)[l[i] == '+'] - used_idxs.append(i) - i += 1 - else: - signal = -1 - len_li = len(l[i]) - if len_li == 4: - # -0300 - setattr(res, offattr, (int(l[i][:2]) * 3600 + - int(l[i][2:]) * 60) * signal) - elif i + 1 < len_l and l[i + 1] == ':': - # -03:00 - setattr(res, offattr, - (int(l[i]) * 3600 + - int(l[i + 2]) * 60) * signal) - used_idxs.append(i) - i += 2 - elif len_li <= 2: - # -[0]3 - setattr(res, offattr, - int(l[i][:2]) * 3600 * signal) - else: - return None - used_idxs.append(i) - i += 1 - if res.dstabbr: - break - else: - break - - - if i < len_l: - for j in range(i, len_l): - if l[j] == ';': - l[j] = ',' - - assert l[i] == ',' - - i += 1 - - if i >= len_l: - pass - elif (8 <= l.count(',') <= 9 and - not [y for x in l[i:] if x != ',' - for y in x if y not in "0123456789+-"]): - # GMT0BST,3,0,30,3600,10,0,26,7200[,3600] - for x in (res.start, res.end): - x.month = int(l[i]) - used_idxs.append(i) - i += 2 - if l[i] == '-': - value = int(l[i + 1]) * -1 - used_idxs.append(i) - i += 1 - else: - value = int(l[i]) - used_idxs.append(i) - i += 2 - if value: - x.week = value - x.weekday = (int(l[i]) - 1) % 7 - else: - x.day = int(l[i]) - used_idxs.append(i) - i += 2 - x.time = int(l[i]) - used_idxs.append(i) - i += 2 - if i < len_l: - if l[i] in ('-', '+'): - signal = (-1, 1)[l[i] == "+"] - used_idxs.append(i) - i += 1 - else: - signal = 1 - used_idxs.append(i) - res.dstoffset = (res.stdoffset + int(l[i]) * signal) - - # This was a made-up format that is not in normal use - warn(('Parsed time zone "%s"' % tzstr) + - 'is in a non-standard dateutil-specific format, which ' + - 'is now deprecated; support for parsing this format ' + - 'will be removed in future versions. It is recommended ' + - 'that you switch to a standard format like the GNU ' + - 'TZ variable format.', tz.DeprecatedTzFormatWarning) - elif (l.count(',') == 2 and l[i:].count('/') <= 2 and - not [y for x in l[i:] if x not in (',', '/', 'J', 'M', - '.', '-', ':') - for y in x if y not in "0123456789"]): - for x in (res.start, res.end): - if l[i] == 'J': - # non-leap year day (1 based) - used_idxs.append(i) - i += 1 - x.jyday = int(l[i]) - elif l[i] == 'M': - # month[-.]week[-.]weekday - used_idxs.append(i) - i += 1 - x.month = int(l[i]) - used_idxs.append(i) - i += 1 - assert l[i] in ('-', '.') - used_idxs.append(i) - i += 1 - x.week = int(l[i]) - if x.week == 5: - x.week = -1 - used_idxs.append(i) - i += 1 - assert l[i] in ('-', '.') - used_idxs.append(i) - i += 1 - x.weekday = (int(l[i]) - 1) % 7 - else: - # year day (zero based) - x.yday = int(l[i]) + 1 - - used_idxs.append(i) - i += 1 - - if i < len_l and l[i] == '/': - used_idxs.append(i) - i += 1 - # start time - len_li = len(l[i]) - if len_li == 4: - # -0300 - x.time = (int(l[i][:2]) * 3600 + - int(l[i][2:]) * 60) - elif i + 1 < len_l and l[i + 1] == ':': - # -03:00 - x.time = int(l[i]) * 3600 + int(l[i + 2]) * 60 - used_idxs.append(i) - i += 2 - if i + 1 < len_l and l[i + 1] == ':': - used_idxs.append(i) - i += 2 - x.time += int(l[i]) - elif len_li <= 2: - # -[0]3 - x.time = (int(l[i][:2]) * 3600) - else: - return None - used_idxs.append(i) - i += 1 - - assert i == len_l or l[i] == ',' - - i += 1 - - assert i >= len_l - - except (IndexError, ValueError, AssertionError): - return None - - unused_idxs = set(range(len_l)).difference(used_idxs) - res.any_unused_tokens = not {l[n] for n in unused_idxs}.issubset({",",":"}) - return res - - -DEFAULTTZPARSER = _tzparser() - - -def _parsetz(tzstr): - return DEFAULTTZPARSER.parse(tzstr) - - -class ParserError(ValueError): - """Exception subclass used for any failure to parse a datetime string. - - This is a subclass of :py:exc:`ValueError`, and should be raised any time - earlier versions of ``dateutil`` would have raised ``ValueError``. - - .. versionadded:: 2.8.1 - """ - def __str__(self): - try: - return self.args[0] % self.args[1:] - except (TypeError, IndexError): - return super(ParserError, self).__str__() - - def __repr__(self): - args = ", ".join("'%s'" % arg for arg in self.args) - return "%s(%s)" % (self.__class__.__name__, args) - - -class UnknownTimezoneWarning(RuntimeWarning): - """Raised when the parser finds a timezone it cannot parse into a tzinfo. - - .. versionadded:: 2.7.0 - """ -# vim:ts=4:sw=4:et diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/parser/isoparser.py b/plotter-app/venv/lib/python3.8/site-packages/dateutil/parser/isoparser.py deleted file mode 100644 index 7060087..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/dateutil/parser/isoparser.py +++ /dev/null @@ -1,416 +0,0 @@ -# -*- coding: utf-8 -*- -""" -This module offers a parser for ISO-8601 strings - -It is intended to support all valid date, time and datetime formats per the -ISO-8601 specification. - -..versionadded:: 2.7.0 -""" -from datetime import datetime, timedelta, time, date -import calendar -from dateutil import tz - -from functools import wraps - -import re -import six - -__all__ = ["isoparse", "isoparser"] - - -def _takes_ascii(f): - @wraps(f) - def func(self, str_in, *args, **kwargs): - # If it's a stream, read the whole thing - str_in = getattr(str_in, 'read', lambda: str_in)() - - # If it's unicode, turn it into bytes, since ISO-8601 only covers ASCII - if isinstance(str_in, six.text_type): - # ASCII is the same in UTF-8 - try: - str_in = str_in.encode('ascii') - except UnicodeEncodeError as e: - msg = 'ISO-8601 strings should contain only ASCII characters' - six.raise_from(ValueError(msg), e) - - return f(self, str_in, *args, **kwargs) - - return func - - -class isoparser(object): - def __init__(self, sep=None): - """ - :param sep: - A single character that separates date and time portions. If - ``None``, the parser will accept any single character. - For strict ISO-8601 adherence, pass ``'T'``. - """ - if sep is not None: - if (len(sep) != 1 or ord(sep) >= 128 or sep in '0123456789'): - raise ValueError('Separator must be a single, non-numeric ' + - 'ASCII character') - - sep = sep.encode('ascii') - - self._sep = sep - - @_takes_ascii - def isoparse(self, dt_str): - """ - Parse an ISO-8601 datetime string into a :class:`datetime.datetime`. - - An ISO-8601 datetime string consists of a date portion, followed - optionally by a time portion - the date and time portions are separated - by a single character separator, which is ``T`` in the official - standard. Incomplete date formats (such as ``YYYY-MM``) may *not* be - combined with a time portion. - - Supported date formats are: - - Common: - - - ``YYYY`` - - ``YYYY-MM`` - - ``YYYY-MM-DD`` or ``YYYYMMDD`` - - Uncommon: - - - ``YYYY-Www`` or ``YYYYWww`` - ISO week (day defaults to 0) - - ``YYYY-Www-D`` or ``YYYYWwwD`` - ISO week and day - - The ISO week and day numbering follows the same logic as - :func:`datetime.date.isocalendar`. - - Supported time formats are: - - - ``hh`` - - ``hh:mm`` or ``hhmm`` - - ``hh:mm:ss`` or ``hhmmss`` - - ``hh:mm:ss.ssssss`` (Up to 6 sub-second digits) - - Midnight is a special case for `hh`, as the standard supports both - 00:00 and 24:00 as a representation. The decimal separator can be - either a dot or a comma. - - - .. caution:: - - Support for fractional components other than seconds is part of the - ISO-8601 standard, but is not currently implemented in this parser. - - Supported time zone offset formats are: - - - `Z` (UTC) - - `±HH:MM` - - `±HHMM` - - `±HH` - - Offsets will be represented as :class:`dateutil.tz.tzoffset` objects, - with the exception of UTC, which will be represented as - :class:`dateutil.tz.tzutc`. Time zone offsets equivalent to UTC (such - as `+00:00`) will also be represented as :class:`dateutil.tz.tzutc`. - - :param dt_str: - A string or stream containing only an ISO-8601 datetime string - - :return: - Returns a :class:`datetime.datetime` representing the string. - Unspecified components default to their lowest value. - - .. warning:: - - As of version 2.7.0, the strictness of the parser should not be - considered a stable part of the contract. Any valid ISO-8601 string - that parses correctly with the default settings will continue to - parse correctly in future versions, but invalid strings that - currently fail (e.g. ``2017-01-01T00:00+00:00:00``) are not - guaranteed to continue failing in future versions if they encode - a valid date. - - .. versionadded:: 2.7.0 - """ - components, pos = self._parse_isodate(dt_str) - - if len(dt_str) > pos: - if self._sep is None or dt_str[pos:pos + 1] == self._sep: - components += self._parse_isotime(dt_str[pos + 1:]) - else: - raise ValueError('String contains unknown ISO components') - - if len(components) > 3 and components[3] == 24: - components[3] = 0 - return datetime(*components) + timedelta(days=1) - - return datetime(*components) - - @_takes_ascii - def parse_isodate(self, datestr): - """ - Parse the date portion of an ISO string. - - :param datestr: - The string portion of an ISO string, without a separator - - :return: - Returns a :class:`datetime.date` object - """ - components, pos = self._parse_isodate(datestr) - if pos < len(datestr): - raise ValueError('String contains unknown ISO ' + - 'components: {!r}'.format(datestr.decode('ascii'))) - return date(*components) - - @_takes_ascii - def parse_isotime(self, timestr): - """ - Parse the time portion of an ISO string. - - :param timestr: - The time portion of an ISO string, without a separator - - :return: - Returns a :class:`datetime.time` object - """ - components = self._parse_isotime(timestr) - if components[0] == 24: - components[0] = 0 - return time(*components) - - @_takes_ascii - def parse_tzstr(self, tzstr, zero_as_utc=True): - """ - Parse a valid ISO time zone string. - - See :func:`isoparser.isoparse` for details on supported formats. - - :param tzstr: - A string representing an ISO time zone offset - - :param zero_as_utc: - Whether to return :class:`dateutil.tz.tzutc` for zero-offset zones - - :return: - Returns :class:`dateutil.tz.tzoffset` for offsets and - :class:`dateutil.tz.tzutc` for ``Z`` and (if ``zero_as_utc`` is - specified) offsets equivalent to UTC. - """ - return self._parse_tzstr(tzstr, zero_as_utc=zero_as_utc) - - # Constants - _DATE_SEP = b'-' - _TIME_SEP = b':' - _FRACTION_REGEX = re.compile(b'[\\.,]([0-9]+)') - - def _parse_isodate(self, dt_str): - try: - return self._parse_isodate_common(dt_str) - except ValueError: - return self._parse_isodate_uncommon(dt_str) - - def _parse_isodate_common(self, dt_str): - len_str = len(dt_str) - components = [1, 1, 1] - - if len_str < 4: - raise ValueError('ISO string too short') - - # Year - components[0] = int(dt_str[0:4]) - pos = 4 - if pos >= len_str: - return components, pos - - has_sep = dt_str[pos:pos + 1] == self._DATE_SEP - if has_sep: - pos += 1 - - # Month - if len_str - pos < 2: - raise ValueError('Invalid common month') - - components[1] = int(dt_str[pos:pos + 2]) - pos += 2 - - if pos >= len_str: - if has_sep: - return components, pos - else: - raise ValueError('Invalid ISO format') - - if has_sep: - if dt_str[pos:pos + 1] != self._DATE_SEP: - raise ValueError('Invalid separator in ISO string') - pos += 1 - - # Day - if len_str - pos < 2: - raise ValueError('Invalid common day') - components[2] = int(dt_str[pos:pos + 2]) - return components, pos + 2 - - def _parse_isodate_uncommon(self, dt_str): - if len(dt_str) < 4: - raise ValueError('ISO string too short') - - # All ISO formats start with the year - year = int(dt_str[0:4]) - - has_sep = dt_str[4:5] == self._DATE_SEP - - pos = 4 + has_sep # Skip '-' if it's there - if dt_str[pos:pos + 1] == b'W': - # YYYY-?Www-?D? - pos += 1 - weekno = int(dt_str[pos:pos + 2]) - pos += 2 - - dayno = 1 - if len(dt_str) > pos: - if (dt_str[pos:pos + 1] == self._DATE_SEP) != has_sep: - raise ValueError('Inconsistent use of dash separator') - - pos += has_sep - - dayno = int(dt_str[pos:pos + 1]) - pos += 1 - - base_date = self._calculate_weekdate(year, weekno, dayno) - else: - # YYYYDDD or YYYY-DDD - if len(dt_str) - pos < 3: - raise ValueError('Invalid ordinal day') - - ordinal_day = int(dt_str[pos:pos + 3]) - pos += 3 - - if ordinal_day < 1 or ordinal_day > (365 + calendar.isleap(year)): - raise ValueError('Invalid ordinal day' + - ' {} for year {}'.format(ordinal_day, year)) - - base_date = date(year, 1, 1) + timedelta(days=ordinal_day - 1) - - components = [base_date.year, base_date.month, base_date.day] - return components, pos - - def _calculate_weekdate(self, year, week, day): - """ - Calculate the day of corresponding to the ISO year-week-day calendar. - - This function is effectively the inverse of - :func:`datetime.date.isocalendar`. - - :param year: - The year in the ISO calendar - - :param week: - The week in the ISO calendar - range is [1, 53] - - :param day: - The day in the ISO calendar - range is [1 (MON), 7 (SUN)] - - :return: - Returns a :class:`datetime.date` - """ - if not 0 < week < 54: - raise ValueError('Invalid week: {}'.format(week)) - - if not 0 < day < 8: # Range is 1-7 - raise ValueError('Invalid weekday: {}'.format(day)) - - # Get week 1 for the specific year: - jan_4 = date(year, 1, 4) # Week 1 always has January 4th in it - week_1 = jan_4 - timedelta(days=jan_4.isocalendar()[2] - 1) - - # Now add the specific number of weeks and days to get what we want - week_offset = (week - 1) * 7 + (day - 1) - return week_1 + timedelta(days=week_offset) - - def _parse_isotime(self, timestr): - len_str = len(timestr) - components = [0, 0, 0, 0, None] - pos = 0 - comp = -1 - - if len_str < 2: - raise ValueError('ISO time too short') - - has_sep = False - - while pos < len_str and comp < 5: - comp += 1 - - if timestr[pos:pos + 1] in b'-+Zz': - # Detect time zone boundary - components[-1] = self._parse_tzstr(timestr[pos:]) - pos = len_str - break - - if comp == 1 and timestr[pos:pos+1] == self._TIME_SEP: - has_sep = True - pos += 1 - elif comp == 2 and has_sep: - if timestr[pos:pos+1] != self._TIME_SEP: - raise ValueError('Inconsistent use of colon separator') - pos += 1 - - if comp < 3: - # Hour, minute, second - components[comp] = int(timestr[pos:pos + 2]) - pos += 2 - - if comp == 3: - # Fraction of a second - frac = self._FRACTION_REGEX.match(timestr[pos:]) - if not frac: - continue - - us_str = frac.group(1)[:6] # Truncate to microseconds - components[comp] = int(us_str) * 10**(6 - len(us_str)) - pos += len(frac.group()) - - if pos < len_str: - raise ValueError('Unused components in ISO string') - - if components[0] == 24: - # Standard supports 00:00 and 24:00 as representations of midnight - if any(component != 0 for component in components[1:4]): - raise ValueError('Hour may only be 24 at 24:00:00.000') - - return components - - def _parse_tzstr(self, tzstr, zero_as_utc=True): - if tzstr == b'Z' or tzstr == b'z': - return tz.UTC - - if len(tzstr) not in {3, 5, 6}: - raise ValueError('Time zone offset must be 1, 3, 5 or 6 characters') - - if tzstr[0:1] == b'-': - mult = -1 - elif tzstr[0:1] == b'+': - mult = 1 - else: - raise ValueError('Time zone offset requires sign') - - hours = int(tzstr[1:3]) - if len(tzstr) == 3: - minutes = 0 - else: - minutes = int(tzstr[(4 if tzstr[3:4] == self._TIME_SEP else 3):]) - - if zero_as_utc and hours == 0 and minutes == 0: - return tz.UTC - else: - if minutes > 59: - raise ValueError('Invalid minutes in time zone offset') - - if hours > 23: - raise ValueError('Invalid hours in time zone offset') - - return tz.tzoffset(None, mult * (hours * 60 + minutes) * 60) - - -DEFAULT_ISOPARSER = isoparser() -isoparse = DEFAULT_ISOPARSER.isoparse diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/relativedelta.py b/plotter-app/venv/lib/python3.8/site-packages/dateutil/relativedelta.py deleted file mode 100644 index cd323a5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/dateutil/relativedelta.py +++ /dev/null @@ -1,599 +0,0 @@ -# -*- coding: utf-8 -*- -import datetime -import calendar - -import operator -from math import copysign - -from six import integer_types -from warnings import warn - -from ._common import weekday - -MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7)) - -__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"] - - -class relativedelta(object): - """ - The relativedelta type is designed to be applied to an existing datetime and - can replace specific components of that datetime, or represents an interval - of time. - - It is based on the specification of the excellent work done by M.-A. Lemburg - in his - `mx.DateTime `_ extension. - However, notice that this type does *NOT* implement the same algorithm as - his work. Do *NOT* expect it to behave like mx.DateTime's counterpart. - - There are two different ways to build a relativedelta instance. The - first one is passing it two date/datetime classes:: - - relativedelta(datetime1, datetime2) - - The second one is passing it any number of the following keyword arguments:: - - relativedelta(arg1=x,arg2=y,arg3=z...) - - year, month, day, hour, minute, second, microsecond: - Absolute information (argument is singular); adding or subtracting a - relativedelta with absolute information does not perform an arithmetic - operation, but rather REPLACES the corresponding value in the - original datetime with the value(s) in relativedelta. - - years, months, weeks, days, hours, minutes, seconds, microseconds: - Relative information, may be negative (argument is plural); adding - or subtracting a relativedelta with relative information performs - the corresponding arithmetic operation on the original datetime value - with the information in the relativedelta. - - weekday: - One of the weekday instances (MO, TU, etc) available in the - relativedelta module. These instances may receive a parameter N, - specifying the Nth weekday, which could be positive or negative - (like MO(+1) or MO(-2)). Not specifying it is the same as specifying - +1. You can also use an integer, where 0=MO. This argument is always - relative e.g. if the calculated date is already Monday, using MO(1) - or MO(-1) won't change the day. To effectively make it absolute, use - it in combination with the day argument (e.g. day=1, MO(1) for first - Monday of the month). - - leapdays: - Will add given days to the date found, if year is a leap - year, and the date found is post 28 of february. - - yearday, nlyearday: - Set the yearday or the non-leap year day (jump leap days). - These are converted to day/month/leapdays information. - - There are relative and absolute forms of the keyword - arguments. The plural is relative, and the singular is - absolute. For each argument in the order below, the absolute form - is applied first (by setting each attribute to that value) and - then the relative form (by adding the value to the attribute). - - The order of attributes considered when this relativedelta is - added to a datetime is: - - 1. Year - 2. Month - 3. Day - 4. Hours - 5. Minutes - 6. Seconds - 7. Microseconds - - Finally, weekday is applied, using the rule described above. - - For example - - >>> from datetime import datetime - >>> from dateutil.relativedelta import relativedelta, MO - >>> dt = datetime(2018, 4, 9, 13, 37, 0) - >>> delta = relativedelta(hours=25, day=1, weekday=MO(1)) - >>> dt + delta - datetime.datetime(2018, 4, 2, 14, 37) - - First, the day is set to 1 (the first of the month), then 25 hours - are added, to get to the 2nd day and 14th hour, finally the - weekday is applied, but since the 2nd is already a Monday there is - no effect. - - """ - - def __init__(self, dt1=None, dt2=None, - years=0, months=0, days=0, leapdays=0, weeks=0, - hours=0, minutes=0, seconds=0, microseconds=0, - year=None, month=None, day=None, weekday=None, - yearday=None, nlyearday=None, - hour=None, minute=None, second=None, microsecond=None): - - if dt1 and dt2: - # datetime is a subclass of date. So both must be date - if not (isinstance(dt1, datetime.date) and - isinstance(dt2, datetime.date)): - raise TypeError("relativedelta only diffs datetime/date") - - # We allow two dates, or two datetimes, so we coerce them to be - # of the same type - if (isinstance(dt1, datetime.datetime) != - isinstance(dt2, datetime.datetime)): - if not isinstance(dt1, datetime.datetime): - dt1 = datetime.datetime.fromordinal(dt1.toordinal()) - elif not isinstance(dt2, datetime.datetime): - dt2 = datetime.datetime.fromordinal(dt2.toordinal()) - - self.years = 0 - self.months = 0 - self.days = 0 - self.leapdays = 0 - self.hours = 0 - self.minutes = 0 - self.seconds = 0 - self.microseconds = 0 - self.year = None - self.month = None - self.day = None - self.weekday = None - self.hour = None - self.minute = None - self.second = None - self.microsecond = None - self._has_time = 0 - - # Get year / month delta between the two - months = (dt1.year - dt2.year) * 12 + (dt1.month - dt2.month) - self._set_months(months) - - # Remove the year/month delta so the timedelta is just well-defined - # time units (seconds, days and microseconds) - dtm = self.__radd__(dt2) - - # If we've overshot our target, make an adjustment - if dt1 < dt2: - compare = operator.gt - increment = 1 - else: - compare = operator.lt - increment = -1 - - while compare(dt1, dtm): - months += increment - self._set_months(months) - dtm = self.__radd__(dt2) - - # Get the timedelta between the "months-adjusted" date and dt1 - delta = dt1 - dtm - self.seconds = delta.seconds + delta.days * 86400 - self.microseconds = delta.microseconds - else: - # Check for non-integer values in integer-only quantities - if any(x is not None and x != int(x) for x in (years, months)): - raise ValueError("Non-integer years and months are " - "ambiguous and not currently supported.") - - # Relative information - self.years = int(years) - self.months = int(months) - self.days = days + weeks * 7 - self.leapdays = leapdays - self.hours = hours - self.minutes = minutes - self.seconds = seconds - self.microseconds = microseconds - - # Absolute information - self.year = year - self.month = month - self.day = day - self.hour = hour - self.minute = minute - self.second = second - self.microsecond = microsecond - - if any(x is not None and int(x) != x - for x in (year, month, day, hour, - minute, second, microsecond)): - # For now we'll deprecate floats - later it'll be an error. - warn("Non-integer value passed as absolute information. " + - "This is not a well-defined condition and will raise " + - "errors in future versions.", DeprecationWarning) - - if isinstance(weekday, integer_types): - self.weekday = weekdays[weekday] - else: - self.weekday = weekday - - yday = 0 - if nlyearday: - yday = nlyearday - elif yearday: - yday = yearday - if yearday > 59: - self.leapdays = -1 - if yday: - ydayidx = [31, 59, 90, 120, 151, 181, 212, - 243, 273, 304, 334, 366] - for idx, ydays in enumerate(ydayidx): - if yday <= ydays: - self.month = idx+1 - if idx == 0: - self.day = yday - else: - self.day = yday-ydayidx[idx-1] - break - else: - raise ValueError("invalid year day (%d)" % yday) - - self._fix() - - def _fix(self): - if abs(self.microseconds) > 999999: - s = _sign(self.microseconds) - div, mod = divmod(self.microseconds * s, 1000000) - self.microseconds = mod * s - self.seconds += div * s - if abs(self.seconds) > 59: - s = _sign(self.seconds) - div, mod = divmod(self.seconds * s, 60) - self.seconds = mod * s - self.minutes += div * s - if abs(self.minutes) > 59: - s = _sign(self.minutes) - div, mod = divmod(self.minutes * s, 60) - self.minutes = mod * s - self.hours += div * s - if abs(self.hours) > 23: - s = _sign(self.hours) - div, mod = divmod(self.hours * s, 24) - self.hours = mod * s - self.days += div * s - if abs(self.months) > 11: - s = _sign(self.months) - div, mod = divmod(self.months * s, 12) - self.months = mod * s - self.years += div * s - if (self.hours or self.minutes or self.seconds or self.microseconds - or self.hour is not None or self.minute is not None or - self.second is not None or self.microsecond is not None): - self._has_time = 1 - else: - self._has_time = 0 - - @property - def weeks(self): - return int(self.days / 7.0) - - @weeks.setter - def weeks(self, value): - self.days = self.days - (self.weeks * 7) + value * 7 - - def _set_months(self, months): - self.months = months - if abs(self.months) > 11: - s = _sign(self.months) - div, mod = divmod(self.months * s, 12) - self.months = mod * s - self.years = div * s - else: - self.years = 0 - - def normalized(self): - """ - Return a version of this object represented entirely using integer - values for the relative attributes. - - >>> relativedelta(days=1.5, hours=2).normalized() - relativedelta(days=+1, hours=+14) - - :return: - Returns a :class:`dateutil.relativedelta.relativedelta` object. - """ - # Cascade remainders down (rounding each to roughly nearest microsecond) - days = int(self.days) - - hours_f = round(self.hours + 24 * (self.days - days), 11) - hours = int(hours_f) - - minutes_f = round(self.minutes + 60 * (hours_f - hours), 10) - minutes = int(minutes_f) - - seconds_f = round(self.seconds + 60 * (minutes_f - minutes), 8) - seconds = int(seconds_f) - - microseconds = round(self.microseconds + 1e6 * (seconds_f - seconds)) - - # Constructor carries overflow back up with call to _fix() - return self.__class__(years=self.years, months=self.months, - days=days, hours=hours, minutes=minutes, - seconds=seconds, microseconds=microseconds, - leapdays=self.leapdays, year=self.year, - month=self.month, day=self.day, - weekday=self.weekday, hour=self.hour, - minute=self.minute, second=self.second, - microsecond=self.microsecond) - - def __add__(self, other): - if isinstance(other, relativedelta): - return self.__class__(years=other.years + self.years, - months=other.months + self.months, - days=other.days + self.days, - hours=other.hours + self.hours, - minutes=other.minutes + self.minutes, - seconds=other.seconds + self.seconds, - microseconds=(other.microseconds + - self.microseconds), - leapdays=other.leapdays or self.leapdays, - year=(other.year if other.year is not None - else self.year), - month=(other.month if other.month is not None - else self.month), - day=(other.day if other.day is not None - else self.day), - weekday=(other.weekday if other.weekday is not None - else self.weekday), - hour=(other.hour if other.hour is not None - else self.hour), - minute=(other.minute if other.minute is not None - else self.minute), - second=(other.second if other.second is not None - else self.second), - microsecond=(other.microsecond if other.microsecond - is not None else - self.microsecond)) - if isinstance(other, datetime.timedelta): - return self.__class__(years=self.years, - months=self.months, - days=self.days + other.days, - hours=self.hours, - minutes=self.minutes, - seconds=self.seconds + other.seconds, - microseconds=self.microseconds + other.microseconds, - leapdays=self.leapdays, - year=self.year, - month=self.month, - day=self.day, - weekday=self.weekday, - hour=self.hour, - minute=self.minute, - second=self.second, - microsecond=self.microsecond) - if not isinstance(other, datetime.date): - return NotImplemented - elif self._has_time and not isinstance(other, datetime.datetime): - other = datetime.datetime.fromordinal(other.toordinal()) - year = (self.year or other.year)+self.years - month = self.month or other.month - if self.months: - assert 1 <= abs(self.months) <= 12 - month += self.months - if month > 12: - year += 1 - month -= 12 - elif month < 1: - year -= 1 - month += 12 - day = min(calendar.monthrange(year, month)[1], - self.day or other.day) - repl = {"year": year, "month": month, "day": day} - for attr in ["hour", "minute", "second", "microsecond"]: - value = getattr(self, attr) - if value is not None: - repl[attr] = value - days = self.days - if self.leapdays and month > 2 and calendar.isleap(year): - days += self.leapdays - ret = (other.replace(**repl) - + datetime.timedelta(days=days, - hours=self.hours, - minutes=self.minutes, - seconds=self.seconds, - microseconds=self.microseconds)) - if self.weekday: - weekday, nth = self.weekday.weekday, self.weekday.n or 1 - jumpdays = (abs(nth) - 1) * 7 - if nth > 0: - jumpdays += (7 - ret.weekday() + weekday) % 7 - else: - jumpdays += (ret.weekday() - weekday) % 7 - jumpdays *= -1 - ret += datetime.timedelta(days=jumpdays) - return ret - - def __radd__(self, other): - return self.__add__(other) - - def __rsub__(self, other): - return self.__neg__().__radd__(other) - - def __sub__(self, other): - if not isinstance(other, relativedelta): - return NotImplemented # In case the other object defines __rsub__ - return self.__class__(years=self.years - other.years, - months=self.months - other.months, - days=self.days - other.days, - hours=self.hours - other.hours, - minutes=self.minutes - other.minutes, - seconds=self.seconds - other.seconds, - microseconds=self.microseconds - other.microseconds, - leapdays=self.leapdays or other.leapdays, - year=(self.year if self.year is not None - else other.year), - month=(self.month if self.month is not None else - other.month), - day=(self.day if self.day is not None else - other.day), - weekday=(self.weekday if self.weekday is not None else - other.weekday), - hour=(self.hour if self.hour is not None else - other.hour), - minute=(self.minute if self.minute is not None else - other.minute), - second=(self.second if self.second is not None else - other.second), - microsecond=(self.microsecond if self.microsecond - is not None else - other.microsecond)) - - def __abs__(self): - return self.__class__(years=abs(self.years), - months=abs(self.months), - days=abs(self.days), - hours=abs(self.hours), - minutes=abs(self.minutes), - seconds=abs(self.seconds), - microseconds=abs(self.microseconds), - leapdays=self.leapdays, - year=self.year, - month=self.month, - day=self.day, - weekday=self.weekday, - hour=self.hour, - minute=self.minute, - second=self.second, - microsecond=self.microsecond) - - def __neg__(self): - return self.__class__(years=-self.years, - months=-self.months, - days=-self.days, - hours=-self.hours, - minutes=-self.minutes, - seconds=-self.seconds, - microseconds=-self.microseconds, - leapdays=self.leapdays, - year=self.year, - month=self.month, - day=self.day, - weekday=self.weekday, - hour=self.hour, - minute=self.minute, - second=self.second, - microsecond=self.microsecond) - - def __bool__(self): - return not (not self.years and - not self.months and - not self.days and - not self.hours and - not self.minutes and - not self.seconds and - not self.microseconds and - not self.leapdays and - self.year is None and - self.month is None and - self.day is None and - self.weekday is None and - self.hour is None and - self.minute is None and - self.second is None and - self.microsecond is None) - # Compatibility with Python 2.x - __nonzero__ = __bool__ - - def __mul__(self, other): - try: - f = float(other) - except TypeError: - return NotImplemented - - return self.__class__(years=int(self.years * f), - months=int(self.months * f), - days=int(self.days * f), - hours=int(self.hours * f), - minutes=int(self.minutes * f), - seconds=int(self.seconds * f), - microseconds=int(self.microseconds * f), - leapdays=self.leapdays, - year=self.year, - month=self.month, - day=self.day, - weekday=self.weekday, - hour=self.hour, - minute=self.minute, - second=self.second, - microsecond=self.microsecond) - - __rmul__ = __mul__ - - def __eq__(self, other): - if not isinstance(other, relativedelta): - return NotImplemented - if self.weekday or other.weekday: - if not self.weekday or not other.weekday: - return False - if self.weekday.weekday != other.weekday.weekday: - return False - n1, n2 = self.weekday.n, other.weekday.n - if n1 != n2 and not ((not n1 or n1 == 1) and (not n2 or n2 == 1)): - return False - return (self.years == other.years and - self.months == other.months and - self.days == other.days and - self.hours == other.hours and - self.minutes == other.minutes and - self.seconds == other.seconds and - self.microseconds == other.microseconds and - self.leapdays == other.leapdays and - self.year == other.year and - self.month == other.month and - self.day == other.day and - self.hour == other.hour and - self.minute == other.minute and - self.second == other.second and - self.microsecond == other.microsecond) - - def __hash__(self): - return hash(( - self.weekday, - self.years, - self.months, - self.days, - self.hours, - self.minutes, - self.seconds, - self.microseconds, - self.leapdays, - self.year, - self.month, - self.day, - self.hour, - self.minute, - self.second, - self.microsecond, - )) - - def __ne__(self, other): - return not self.__eq__(other) - - def __div__(self, other): - try: - reciprocal = 1 / float(other) - except TypeError: - return NotImplemented - - return self.__mul__(reciprocal) - - __truediv__ = __div__ - - def __repr__(self): - l = [] - for attr in ["years", "months", "days", "leapdays", - "hours", "minutes", "seconds", "microseconds"]: - value = getattr(self, attr) - if value: - l.append("{attr}={value:+g}".format(attr=attr, value=value)) - for attr in ["year", "month", "day", "weekday", - "hour", "minute", "second", "microsecond"]: - value = getattr(self, attr) - if value is not None: - l.append("{attr}={value}".format(attr=attr, value=repr(value))) - return "{classname}({attrs})".format(classname=self.__class__.__name__, - attrs=", ".join(l)) - - -def _sign(x): - return int(copysign(1, x)) - -# vim:ts=4:sw=4:et diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/rrule.py b/plotter-app/venv/lib/python3.8/site-packages/dateutil/rrule.py deleted file mode 100644 index 571a0d2..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/dateutil/rrule.py +++ /dev/null @@ -1,1737 +0,0 @@ -# -*- coding: utf-8 -*- -""" -The rrule module offers a small, complete, and very fast, implementation of -the recurrence rules documented in the -`iCalendar RFC `_, -including support for caching of results. -""" -import calendar -import datetime -import heapq -import itertools -import re -import sys -from functools import wraps -# For warning about deprecation of until and count -from warnings import warn - -from six import advance_iterator, integer_types - -from six.moves import _thread, range - -from ._common import weekday as weekdaybase - -try: - from math import gcd -except ImportError: - from fractions import gcd - -__all__ = ["rrule", "rruleset", "rrulestr", - "YEARLY", "MONTHLY", "WEEKLY", "DAILY", - "HOURLY", "MINUTELY", "SECONDLY", - "MO", "TU", "WE", "TH", "FR", "SA", "SU"] - -# Every mask is 7 days longer to handle cross-year weekly periods. -M366MASK = tuple([1]*31+[2]*29+[3]*31+[4]*30+[5]*31+[6]*30 + - [7]*31+[8]*31+[9]*30+[10]*31+[11]*30+[12]*31+[1]*7) -M365MASK = list(M366MASK) -M29, M30, M31 = list(range(1, 30)), list(range(1, 31)), list(range(1, 32)) -MDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7]) -MDAY365MASK = list(MDAY366MASK) -M29, M30, M31 = list(range(-29, 0)), list(range(-30, 0)), list(range(-31, 0)) -NMDAY366MASK = tuple(M31+M29+M31+M30+M31+M30+M31+M31+M30+M31+M30+M31+M31[:7]) -NMDAY365MASK = list(NMDAY366MASK) -M366RANGE = (0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366) -M365RANGE = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365) -WDAYMASK = [0, 1, 2, 3, 4, 5, 6]*55 -del M29, M30, M31, M365MASK[59], MDAY365MASK[59], NMDAY365MASK[31] -MDAY365MASK = tuple(MDAY365MASK) -M365MASK = tuple(M365MASK) - -FREQNAMES = ['YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY', 'HOURLY', 'MINUTELY', 'SECONDLY'] - -(YEARLY, - MONTHLY, - WEEKLY, - DAILY, - HOURLY, - MINUTELY, - SECONDLY) = list(range(7)) - -# Imported on demand. -easter = None -parser = None - - -class weekday(weekdaybase): - """ - This version of weekday does not allow n = 0. - """ - def __init__(self, wkday, n=None): - if n == 0: - raise ValueError("Can't create weekday with n==0") - - super(weekday, self).__init__(wkday, n) - - -MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(weekday(x) for x in range(7)) - - -def _invalidates_cache(f): - """ - Decorator for rruleset methods which may invalidate the - cached length. - """ - @wraps(f) - def inner_func(self, *args, **kwargs): - rv = f(self, *args, **kwargs) - self._invalidate_cache() - return rv - - return inner_func - - -class rrulebase(object): - def __init__(self, cache=False): - if cache: - self._cache = [] - self._cache_lock = _thread.allocate_lock() - self._invalidate_cache() - else: - self._cache = None - self._cache_complete = False - self._len = None - - def __iter__(self): - if self._cache_complete: - return iter(self._cache) - elif self._cache is None: - return self._iter() - else: - return self._iter_cached() - - def _invalidate_cache(self): - if self._cache is not None: - self._cache = [] - self._cache_complete = False - self._cache_gen = self._iter() - - if self._cache_lock.locked(): - self._cache_lock.release() - - self._len = None - - def _iter_cached(self): - i = 0 - gen = self._cache_gen - cache = self._cache - acquire = self._cache_lock.acquire - release = self._cache_lock.release - while gen: - if i == len(cache): - acquire() - if self._cache_complete: - break - try: - for j in range(10): - cache.append(advance_iterator(gen)) - except StopIteration: - self._cache_gen = gen = None - self._cache_complete = True - break - release() - yield cache[i] - i += 1 - while i < self._len: - yield cache[i] - i += 1 - - def __getitem__(self, item): - if self._cache_complete: - return self._cache[item] - elif isinstance(item, slice): - if item.step and item.step < 0: - return list(iter(self))[item] - else: - return list(itertools.islice(self, - item.start or 0, - item.stop or sys.maxsize, - item.step or 1)) - elif item >= 0: - gen = iter(self) - try: - for i in range(item+1): - res = advance_iterator(gen) - except StopIteration: - raise IndexError - return res - else: - return list(iter(self))[item] - - def __contains__(self, item): - if self._cache_complete: - return item in self._cache - else: - for i in self: - if i == item: - return True - elif i > item: - return False - return False - - # __len__() introduces a large performance penalty. - def count(self): - """ Returns the number of recurrences in this set. It will have go - through the whole recurrence, if this hasn't been done before. """ - if self._len is None: - for x in self: - pass - return self._len - - def before(self, dt, inc=False): - """ Returns the last recurrence before the given datetime instance. The - inc keyword defines what happens if dt is an occurrence. With - inc=True, if dt itself is an occurrence, it will be returned. """ - if self._cache_complete: - gen = self._cache - else: - gen = self - last = None - if inc: - for i in gen: - if i > dt: - break - last = i - else: - for i in gen: - if i >= dt: - break - last = i - return last - - def after(self, dt, inc=False): - """ Returns the first recurrence after the given datetime instance. The - inc keyword defines what happens if dt is an occurrence. With - inc=True, if dt itself is an occurrence, it will be returned. """ - if self._cache_complete: - gen = self._cache - else: - gen = self - if inc: - for i in gen: - if i >= dt: - return i - else: - for i in gen: - if i > dt: - return i - return None - - def xafter(self, dt, count=None, inc=False): - """ - Generator which yields up to `count` recurrences after the given - datetime instance, equivalent to `after`. - - :param dt: - The datetime at which to start generating recurrences. - - :param count: - The maximum number of recurrences to generate. If `None` (default), - dates are generated until the recurrence rule is exhausted. - - :param inc: - If `dt` is an instance of the rule and `inc` is `True`, it is - included in the output. - - :yields: Yields a sequence of `datetime` objects. - """ - - if self._cache_complete: - gen = self._cache - else: - gen = self - - # Select the comparison function - if inc: - comp = lambda dc, dtc: dc >= dtc - else: - comp = lambda dc, dtc: dc > dtc - - # Generate dates - n = 0 - for d in gen: - if comp(d, dt): - if count is not None: - n += 1 - if n > count: - break - - yield d - - def between(self, after, before, inc=False, count=1): - """ Returns all the occurrences of the rrule between after and before. - The inc keyword defines what happens if after and/or before are - themselves occurrences. With inc=True, they will be included in the - list, if they are found in the recurrence set. """ - if self._cache_complete: - gen = self._cache - else: - gen = self - started = False - l = [] - if inc: - for i in gen: - if i > before: - break - elif not started: - if i >= after: - started = True - l.append(i) - else: - l.append(i) - else: - for i in gen: - if i >= before: - break - elif not started: - if i > after: - started = True - l.append(i) - else: - l.append(i) - return l - - -class rrule(rrulebase): - """ - That's the base of the rrule operation. It accepts all the keywords - defined in the RFC as its constructor parameters (except byday, - which was renamed to byweekday) and more. The constructor prototype is:: - - rrule(freq) - - Where freq must be one of YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, - or SECONDLY. - - .. note:: - Per RFC section 3.3.10, recurrence instances falling on invalid dates - and times are ignored rather than coerced: - - Recurrence rules may generate recurrence instances with an invalid - date (e.g., February 30) or nonexistent local time (e.g., 1:30 AM - on a day where the local time is moved forward by an hour at 1:00 - AM). Such recurrence instances MUST be ignored and MUST NOT be - counted as part of the recurrence set. - - This can lead to possibly surprising behavior when, for example, the - start date occurs at the end of the month: - - >>> from dateutil.rrule import rrule, MONTHLY - >>> from datetime import datetime - >>> start_date = datetime(2014, 12, 31) - >>> list(rrule(freq=MONTHLY, count=4, dtstart=start_date)) - ... # doctest: +NORMALIZE_WHITESPACE - [datetime.datetime(2014, 12, 31, 0, 0), - datetime.datetime(2015, 1, 31, 0, 0), - datetime.datetime(2015, 3, 31, 0, 0), - datetime.datetime(2015, 5, 31, 0, 0)] - - Additionally, it supports the following keyword arguments: - - :param dtstart: - The recurrence start. Besides being the base for the recurrence, - missing parameters in the final recurrence instances will also be - extracted from this date. If not given, datetime.now() will be used - instead. - :param interval: - The interval between each freq iteration. For example, when using - YEARLY, an interval of 2 means once every two years, but with HOURLY, - it means once every two hours. The default interval is 1. - :param wkst: - The week start day. Must be one of the MO, TU, WE constants, or an - integer, specifying the first day of the week. This will affect - recurrences based on weekly periods. The default week start is got - from calendar.firstweekday(), and may be modified by - calendar.setfirstweekday(). - :param count: - If given, this determines how many occurrences will be generated. - - .. note:: - As of version 2.5.0, the use of the keyword ``until`` in conjunction - with ``count`` is deprecated, to make sure ``dateutil`` is fully - compliant with `RFC-5545 Sec. 3.3.10 `_. Therefore, ``until`` and ``count`` - **must not** occur in the same call to ``rrule``. - :param until: - If given, this must be a datetime instance specifying the upper-bound - limit of the recurrence. The last recurrence in the rule is the greatest - datetime that is less than or equal to the value specified in the - ``until`` parameter. - - .. note:: - As of version 2.5.0, the use of the keyword ``until`` in conjunction - with ``count`` is deprecated, to make sure ``dateutil`` is fully - compliant with `RFC-5545 Sec. 3.3.10 `_. Therefore, ``until`` and ``count`` - **must not** occur in the same call to ``rrule``. - :param bysetpos: - If given, it must be either an integer, or a sequence of integers, - positive or negative. Each given integer will specify an occurrence - number, corresponding to the nth occurrence of the rule inside the - frequency period. For example, a bysetpos of -1 if combined with a - MONTHLY frequency, and a byweekday of (MO, TU, WE, TH, FR), will - result in the last work day of every month. - :param bymonth: - If given, it must be either an integer, or a sequence of integers, - meaning the months to apply the recurrence to. - :param bymonthday: - If given, it must be either an integer, or a sequence of integers, - meaning the month days to apply the recurrence to. - :param byyearday: - If given, it must be either an integer, or a sequence of integers, - meaning the year days to apply the recurrence to. - :param byeaster: - If given, it must be either an integer, or a sequence of integers, - positive or negative. Each integer will define an offset from the - Easter Sunday. Passing the offset 0 to byeaster will yield the Easter - Sunday itself. This is an extension to the RFC specification. - :param byweekno: - If given, it must be either an integer, or a sequence of integers, - meaning the week numbers to apply the recurrence to. Week numbers - have the meaning described in ISO8601, that is, the first week of - the year is that containing at least four days of the new year. - :param byweekday: - If given, it must be either an integer (0 == MO), a sequence of - integers, one of the weekday constants (MO, TU, etc), or a sequence - of these constants. When given, these variables will define the - weekdays where the recurrence will be applied. It's also possible to - use an argument n for the weekday instances, which will mean the nth - occurrence of this weekday in the period. For example, with MONTHLY, - or with YEARLY and BYMONTH, using FR(+1) in byweekday will specify the - first friday of the month where the recurrence happens. Notice that in - the RFC documentation, this is specified as BYDAY, but was renamed to - avoid the ambiguity of that keyword. - :param byhour: - If given, it must be either an integer, or a sequence of integers, - meaning the hours to apply the recurrence to. - :param byminute: - If given, it must be either an integer, or a sequence of integers, - meaning the minutes to apply the recurrence to. - :param bysecond: - If given, it must be either an integer, or a sequence of integers, - meaning the seconds to apply the recurrence to. - :param cache: - If given, it must be a boolean value specifying to enable or disable - caching of results. If you will use the same rrule instance multiple - times, enabling caching will improve the performance considerably. - """ - def __init__(self, freq, dtstart=None, - interval=1, wkst=None, count=None, until=None, bysetpos=None, - bymonth=None, bymonthday=None, byyearday=None, byeaster=None, - byweekno=None, byweekday=None, - byhour=None, byminute=None, bysecond=None, - cache=False): - super(rrule, self).__init__(cache) - global easter - if not dtstart: - if until and until.tzinfo: - dtstart = datetime.datetime.now(tz=until.tzinfo).replace(microsecond=0) - else: - dtstart = datetime.datetime.now().replace(microsecond=0) - elif not isinstance(dtstart, datetime.datetime): - dtstart = datetime.datetime.fromordinal(dtstart.toordinal()) - else: - dtstart = dtstart.replace(microsecond=0) - self._dtstart = dtstart - self._tzinfo = dtstart.tzinfo - self._freq = freq - self._interval = interval - self._count = count - - # Cache the original byxxx rules, if they are provided, as the _byxxx - # attributes do not necessarily map to the inputs, and this can be - # a problem in generating the strings. Only store things if they've - # been supplied (the string retrieval will just use .get()) - self._original_rule = {} - - if until and not isinstance(until, datetime.datetime): - until = datetime.datetime.fromordinal(until.toordinal()) - self._until = until - - if self._dtstart and self._until: - if (self._dtstart.tzinfo is not None) != (self._until.tzinfo is not None): - # According to RFC5545 Section 3.3.10: - # https://tools.ietf.org/html/rfc5545#section-3.3.10 - # - # > If the "DTSTART" property is specified as a date with UTC - # > time or a date with local time and time zone reference, - # > then the UNTIL rule part MUST be specified as a date with - # > UTC time. - raise ValueError( - 'RRULE UNTIL values must be specified in UTC when DTSTART ' - 'is timezone-aware' - ) - - if count is not None and until: - warn("Using both 'count' and 'until' is inconsistent with RFC 5545" - " and has been deprecated in dateutil. Future versions will " - "raise an error.", DeprecationWarning) - - if wkst is None: - self._wkst = calendar.firstweekday() - elif isinstance(wkst, integer_types): - self._wkst = wkst - else: - self._wkst = wkst.weekday - - if bysetpos is None: - self._bysetpos = None - elif isinstance(bysetpos, integer_types): - if bysetpos == 0 or not (-366 <= bysetpos <= 366): - raise ValueError("bysetpos must be between 1 and 366, " - "or between -366 and -1") - self._bysetpos = (bysetpos,) - else: - self._bysetpos = tuple(bysetpos) - for pos in self._bysetpos: - if pos == 0 or not (-366 <= pos <= 366): - raise ValueError("bysetpos must be between 1 and 366, " - "or between -366 and -1") - - if self._bysetpos: - self._original_rule['bysetpos'] = self._bysetpos - - if (byweekno is None and byyearday is None and bymonthday is None and - byweekday is None and byeaster is None): - if freq == YEARLY: - if bymonth is None: - bymonth = dtstart.month - self._original_rule['bymonth'] = None - bymonthday = dtstart.day - self._original_rule['bymonthday'] = None - elif freq == MONTHLY: - bymonthday = dtstart.day - self._original_rule['bymonthday'] = None - elif freq == WEEKLY: - byweekday = dtstart.weekday() - self._original_rule['byweekday'] = None - - # bymonth - if bymonth is None: - self._bymonth = None - else: - if isinstance(bymonth, integer_types): - bymonth = (bymonth,) - - self._bymonth = tuple(sorted(set(bymonth))) - - if 'bymonth' not in self._original_rule: - self._original_rule['bymonth'] = self._bymonth - - # byyearday - if byyearday is None: - self._byyearday = None - else: - if isinstance(byyearday, integer_types): - byyearday = (byyearday,) - - self._byyearday = tuple(sorted(set(byyearday))) - self._original_rule['byyearday'] = self._byyearday - - # byeaster - if byeaster is not None: - if not easter: - from dateutil import easter - if isinstance(byeaster, integer_types): - self._byeaster = (byeaster,) - else: - self._byeaster = tuple(sorted(byeaster)) - - self._original_rule['byeaster'] = self._byeaster - else: - self._byeaster = None - - # bymonthday - if bymonthday is None: - self._bymonthday = () - self._bynmonthday = () - else: - if isinstance(bymonthday, integer_types): - bymonthday = (bymonthday,) - - bymonthday = set(bymonthday) # Ensure it's unique - - self._bymonthday = tuple(sorted(x for x in bymonthday if x > 0)) - self._bynmonthday = tuple(sorted(x for x in bymonthday if x < 0)) - - # Storing positive numbers first, then negative numbers - if 'bymonthday' not in self._original_rule: - self._original_rule['bymonthday'] = tuple( - itertools.chain(self._bymonthday, self._bynmonthday)) - - # byweekno - if byweekno is None: - self._byweekno = None - else: - if isinstance(byweekno, integer_types): - byweekno = (byweekno,) - - self._byweekno = tuple(sorted(set(byweekno))) - - self._original_rule['byweekno'] = self._byweekno - - # byweekday / bynweekday - if byweekday is None: - self._byweekday = None - self._bynweekday = None - else: - # If it's one of the valid non-sequence types, convert to a - # single-element sequence before the iterator that builds the - # byweekday set. - if isinstance(byweekday, integer_types) or hasattr(byweekday, "n"): - byweekday = (byweekday,) - - self._byweekday = set() - self._bynweekday = set() - for wday in byweekday: - if isinstance(wday, integer_types): - self._byweekday.add(wday) - elif not wday.n or freq > MONTHLY: - self._byweekday.add(wday.weekday) - else: - self._bynweekday.add((wday.weekday, wday.n)) - - if not self._byweekday: - self._byweekday = None - elif not self._bynweekday: - self._bynweekday = None - - if self._byweekday is not None: - self._byweekday = tuple(sorted(self._byweekday)) - orig_byweekday = [weekday(x) for x in self._byweekday] - else: - orig_byweekday = () - - if self._bynweekday is not None: - self._bynweekday = tuple(sorted(self._bynweekday)) - orig_bynweekday = [weekday(*x) for x in self._bynweekday] - else: - orig_bynweekday = () - - if 'byweekday' not in self._original_rule: - self._original_rule['byweekday'] = tuple(itertools.chain( - orig_byweekday, orig_bynweekday)) - - # byhour - if byhour is None: - if freq < HOURLY: - self._byhour = {dtstart.hour} - else: - self._byhour = None - else: - if isinstance(byhour, integer_types): - byhour = (byhour,) - - if freq == HOURLY: - self._byhour = self.__construct_byset(start=dtstart.hour, - byxxx=byhour, - base=24) - else: - self._byhour = set(byhour) - - self._byhour = tuple(sorted(self._byhour)) - self._original_rule['byhour'] = self._byhour - - # byminute - if byminute is None: - if freq < MINUTELY: - self._byminute = {dtstart.minute} - else: - self._byminute = None - else: - if isinstance(byminute, integer_types): - byminute = (byminute,) - - if freq == MINUTELY: - self._byminute = self.__construct_byset(start=dtstart.minute, - byxxx=byminute, - base=60) - else: - self._byminute = set(byminute) - - self._byminute = tuple(sorted(self._byminute)) - self._original_rule['byminute'] = self._byminute - - # bysecond - if bysecond is None: - if freq < SECONDLY: - self._bysecond = ((dtstart.second,)) - else: - self._bysecond = None - else: - if isinstance(bysecond, integer_types): - bysecond = (bysecond,) - - self._bysecond = set(bysecond) - - if freq == SECONDLY: - self._bysecond = self.__construct_byset(start=dtstart.second, - byxxx=bysecond, - base=60) - else: - self._bysecond = set(bysecond) - - self._bysecond = tuple(sorted(self._bysecond)) - self._original_rule['bysecond'] = self._bysecond - - if self._freq >= HOURLY: - self._timeset = None - else: - self._timeset = [] - for hour in self._byhour: - for minute in self._byminute: - for second in self._bysecond: - self._timeset.append( - datetime.time(hour, minute, second, - tzinfo=self._tzinfo)) - self._timeset.sort() - self._timeset = tuple(self._timeset) - - def __str__(self): - """ - Output a string that would generate this RRULE if passed to rrulestr. - This is mostly compatible with RFC5545, except for the - dateutil-specific extension BYEASTER. - """ - - output = [] - h, m, s = [None] * 3 - if self._dtstart: - output.append(self._dtstart.strftime('DTSTART:%Y%m%dT%H%M%S')) - h, m, s = self._dtstart.timetuple()[3:6] - - parts = ['FREQ=' + FREQNAMES[self._freq]] - if self._interval != 1: - parts.append('INTERVAL=' + str(self._interval)) - - if self._wkst: - parts.append('WKST=' + repr(weekday(self._wkst))[0:2]) - - if self._count is not None: - parts.append('COUNT=' + str(self._count)) - - if self._until: - parts.append(self._until.strftime('UNTIL=%Y%m%dT%H%M%S')) - - if self._original_rule.get('byweekday') is not None: - # The str() method on weekday objects doesn't generate - # RFC5545-compliant strings, so we should modify that. - original_rule = dict(self._original_rule) - wday_strings = [] - for wday in original_rule['byweekday']: - if wday.n: - wday_strings.append('{n:+d}{wday}'.format( - n=wday.n, - wday=repr(wday)[0:2])) - else: - wday_strings.append(repr(wday)) - - original_rule['byweekday'] = wday_strings - else: - original_rule = self._original_rule - - partfmt = '{name}={vals}' - for name, key in [('BYSETPOS', 'bysetpos'), - ('BYMONTH', 'bymonth'), - ('BYMONTHDAY', 'bymonthday'), - ('BYYEARDAY', 'byyearday'), - ('BYWEEKNO', 'byweekno'), - ('BYDAY', 'byweekday'), - ('BYHOUR', 'byhour'), - ('BYMINUTE', 'byminute'), - ('BYSECOND', 'bysecond'), - ('BYEASTER', 'byeaster')]: - value = original_rule.get(key) - if value: - parts.append(partfmt.format(name=name, vals=(','.join(str(v) - for v in value)))) - - output.append('RRULE:' + ';'.join(parts)) - return '\n'.join(output) - - def replace(self, **kwargs): - """Return new rrule with same attributes except for those attributes given new - values by whichever keyword arguments are specified.""" - new_kwargs = {"interval": self._interval, - "count": self._count, - "dtstart": self._dtstart, - "freq": self._freq, - "until": self._until, - "wkst": self._wkst, - "cache": False if self._cache is None else True } - new_kwargs.update(self._original_rule) - new_kwargs.update(kwargs) - return rrule(**new_kwargs) - - def _iter(self): - year, month, day, hour, minute, second, weekday, yearday, _ = \ - self._dtstart.timetuple() - - # Some local variables to speed things up a bit - freq = self._freq - interval = self._interval - wkst = self._wkst - until = self._until - bymonth = self._bymonth - byweekno = self._byweekno - byyearday = self._byyearday - byweekday = self._byweekday - byeaster = self._byeaster - bymonthday = self._bymonthday - bynmonthday = self._bynmonthday - bysetpos = self._bysetpos - byhour = self._byhour - byminute = self._byminute - bysecond = self._bysecond - - ii = _iterinfo(self) - ii.rebuild(year, month) - - getdayset = {YEARLY: ii.ydayset, - MONTHLY: ii.mdayset, - WEEKLY: ii.wdayset, - DAILY: ii.ddayset, - HOURLY: ii.ddayset, - MINUTELY: ii.ddayset, - SECONDLY: ii.ddayset}[freq] - - if freq < HOURLY: - timeset = self._timeset - else: - gettimeset = {HOURLY: ii.htimeset, - MINUTELY: ii.mtimeset, - SECONDLY: ii.stimeset}[freq] - if ((freq >= HOURLY and - self._byhour and hour not in self._byhour) or - (freq >= MINUTELY and - self._byminute and minute not in self._byminute) or - (freq >= SECONDLY and - self._bysecond and second not in self._bysecond)): - timeset = () - else: - timeset = gettimeset(hour, minute, second) - - total = 0 - count = self._count - while True: - # Get dayset with the right frequency - dayset, start, end = getdayset(year, month, day) - - # Do the "hard" work ;-) - filtered = False - for i in dayset[start:end]: - if ((bymonth and ii.mmask[i] not in bymonth) or - (byweekno and not ii.wnomask[i]) or - (byweekday and ii.wdaymask[i] not in byweekday) or - (ii.nwdaymask and not ii.nwdaymask[i]) or - (byeaster and not ii.eastermask[i]) or - ((bymonthday or bynmonthday) and - ii.mdaymask[i] not in bymonthday and - ii.nmdaymask[i] not in bynmonthday) or - (byyearday and - ((i < ii.yearlen and i+1 not in byyearday and - -ii.yearlen+i not in byyearday) or - (i >= ii.yearlen and i+1-ii.yearlen not in byyearday and - -ii.nextyearlen+i-ii.yearlen not in byyearday)))): - dayset[i] = None - filtered = True - - # Output results - if bysetpos and timeset: - poslist = [] - for pos in bysetpos: - if pos < 0: - daypos, timepos = divmod(pos, len(timeset)) - else: - daypos, timepos = divmod(pos-1, len(timeset)) - try: - i = [x for x in dayset[start:end] - if x is not None][daypos] - time = timeset[timepos] - except IndexError: - pass - else: - date = datetime.date.fromordinal(ii.yearordinal+i) - res = datetime.datetime.combine(date, time) - if res not in poslist: - poslist.append(res) - poslist.sort() - for res in poslist: - if until and res > until: - self._len = total - return - elif res >= self._dtstart: - if count is not None: - count -= 1 - if count < 0: - self._len = total - return - total += 1 - yield res - else: - for i in dayset[start:end]: - if i is not None: - date = datetime.date.fromordinal(ii.yearordinal + i) - for time in timeset: - res = datetime.datetime.combine(date, time) - if until and res > until: - self._len = total - return - elif res >= self._dtstart: - if count is not None: - count -= 1 - if count < 0: - self._len = total - return - - total += 1 - yield res - - # Handle frequency and interval - fixday = False - if freq == YEARLY: - year += interval - if year > datetime.MAXYEAR: - self._len = total - return - ii.rebuild(year, month) - elif freq == MONTHLY: - month += interval - if month > 12: - div, mod = divmod(month, 12) - month = mod - year += div - if month == 0: - month = 12 - year -= 1 - if year > datetime.MAXYEAR: - self._len = total - return - ii.rebuild(year, month) - elif freq == WEEKLY: - if wkst > weekday: - day += -(weekday+1+(6-wkst))+self._interval*7 - else: - day += -(weekday-wkst)+self._interval*7 - weekday = wkst - fixday = True - elif freq == DAILY: - day += interval - fixday = True - elif freq == HOURLY: - if filtered: - # Jump to one iteration before next day - hour += ((23-hour)//interval)*interval - - if byhour: - ndays, hour = self.__mod_distance(value=hour, - byxxx=self._byhour, - base=24) - else: - ndays, hour = divmod(hour+interval, 24) - - if ndays: - day += ndays - fixday = True - - timeset = gettimeset(hour, minute, second) - elif freq == MINUTELY: - if filtered: - # Jump to one iteration before next day - minute += ((1439-(hour*60+minute))//interval)*interval - - valid = False - rep_rate = (24*60) - for j in range(rep_rate // gcd(interval, rep_rate)): - if byminute: - nhours, minute = \ - self.__mod_distance(value=minute, - byxxx=self._byminute, - base=60) - else: - nhours, minute = divmod(minute+interval, 60) - - div, hour = divmod(hour+nhours, 24) - if div: - day += div - fixday = True - filtered = False - - if not byhour or hour in byhour: - valid = True - break - - if not valid: - raise ValueError('Invalid combination of interval and ' + - 'byhour resulting in empty rule.') - - timeset = gettimeset(hour, minute, second) - elif freq == SECONDLY: - if filtered: - # Jump to one iteration before next day - second += (((86399 - (hour * 3600 + minute * 60 + second)) - // interval) * interval) - - rep_rate = (24 * 3600) - valid = False - for j in range(0, rep_rate // gcd(interval, rep_rate)): - if bysecond: - nminutes, second = \ - self.__mod_distance(value=second, - byxxx=self._bysecond, - base=60) - else: - nminutes, second = divmod(second+interval, 60) - - div, minute = divmod(minute+nminutes, 60) - if div: - hour += div - div, hour = divmod(hour, 24) - if div: - day += div - fixday = True - - if ((not byhour or hour in byhour) and - (not byminute or minute in byminute) and - (not bysecond or second in bysecond)): - valid = True - break - - if not valid: - raise ValueError('Invalid combination of interval, ' + - 'byhour and byminute resulting in empty' + - ' rule.') - - timeset = gettimeset(hour, minute, second) - - if fixday and day > 28: - daysinmonth = calendar.monthrange(year, month)[1] - if day > daysinmonth: - while day > daysinmonth: - day -= daysinmonth - month += 1 - if month == 13: - month = 1 - year += 1 - if year > datetime.MAXYEAR: - self._len = total - return - daysinmonth = calendar.monthrange(year, month)[1] - ii.rebuild(year, month) - - def __construct_byset(self, start, byxxx, base): - """ - If a `BYXXX` sequence is passed to the constructor at the same level as - `FREQ` (e.g. `FREQ=HOURLY,BYHOUR={2,4,7},INTERVAL=3`), there are some - specifications which cannot be reached given some starting conditions. - - This occurs whenever the interval is not coprime with the base of a - given unit and the difference between the starting position and the - ending position is not coprime with the greatest common denominator - between the interval and the base. For example, with a FREQ of hourly - starting at 17:00 and an interval of 4, the only valid values for - BYHOUR would be {21, 1, 5, 9, 13, 17}, because 4 and 24 are not - coprime. - - :param start: - Specifies the starting position. - :param byxxx: - An iterable containing the list of allowed values. - :param base: - The largest allowable value for the specified frequency (e.g. - 24 hours, 60 minutes). - - This does not preserve the type of the iterable, returning a set, since - the values should be unique and the order is irrelevant, this will - speed up later lookups. - - In the event of an empty set, raises a :exception:`ValueError`, as this - results in an empty rrule. - """ - - cset = set() - - # Support a single byxxx value. - if isinstance(byxxx, integer_types): - byxxx = (byxxx, ) - - for num in byxxx: - i_gcd = gcd(self._interval, base) - # Use divmod rather than % because we need to wrap negative nums. - if i_gcd == 1 or divmod(num - start, i_gcd)[1] == 0: - cset.add(num) - - if len(cset) == 0: - raise ValueError("Invalid rrule byxxx generates an empty set.") - - return cset - - def __mod_distance(self, value, byxxx, base): - """ - Calculates the next value in a sequence where the `FREQ` parameter is - specified along with a `BYXXX` parameter at the same "level" - (e.g. `HOURLY` specified with `BYHOUR`). - - :param value: - The old value of the component. - :param byxxx: - The `BYXXX` set, which should have been generated by - `rrule._construct_byset`, or something else which checks that a - valid rule is present. - :param base: - The largest allowable value for the specified frequency (e.g. - 24 hours, 60 minutes). - - If a valid value is not found after `base` iterations (the maximum - number before the sequence would start to repeat), this raises a - :exception:`ValueError`, as no valid values were found. - - This returns a tuple of `divmod(n*interval, base)`, where `n` is the - smallest number of `interval` repetitions until the next specified - value in `byxxx` is found. - """ - accumulator = 0 - for ii in range(1, base + 1): - # Using divmod() over % to account for negative intervals - div, value = divmod(value + self._interval, base) - accumulator += div - if value in byxxx: - return (accumulator, value) - - -class _iterinfo(object): - __slots__ = ["rrule", "lastyear", "lastmonth", - "yearlen", "nextyearlen", "yearordinal", "yearweekday", - "mmask", "mrange", "mdaymask", "nmdaymask", - "wdaymask", "wnomask", "nwdaymask", "eastermask"] - - def __init__(self, rrule): - for attr in self.__slots__: - setattr(self, attr, None) - self.rrule = rrule - - def rebuild(self, year, month): - # Every mask is 7 days longer to handle cross-year weekly periods. - rr = self.rrule - if year != self.lastyear: - self.yearlen = 365 + calendar.isleap(year) - self.nextyearlen = 365 + calendar.isleap(year + 1) - firstyday = datetime.date(year, 1, 1) - self.yearordinal = firstyday.toordinal() - self.yearweekday = firstyday.weekday() - - wday = datetime.date(year, 1, 1).weekday() - if self.yearlen == 365: - self.mmask = M365MASK - self.mdaymask = MDAY365MASK - self.nmdaymask = NMDAY365MASK - self.wdaymask = WDAYMASK[wday:] - self.mrange = M365RANGE - else: - self.mmask = M366MASK - self.mdaymask = MDAY366MASK - self.nmdaymask = NMDAY366MASK - self.wdaymask = WDAYMASK[wday:] - self.mrange = M366RANGE - - if not rr._byweekno: - self.wnomask = None - else: - self.wnomask = [0]*(self.yearlen+7) - # no1wkst = firstwkst = self.wdaymask.index(rr._wkst) - no1wkst = firstwkst = (7-self.yearweekday+rr._wkst) % 7 - if no1wkst >= 4: - no1wkst = 0 - # Number of days in the year, plus the days we got - # from last year. - wyearlen = self.yearlen+(self.yearweekday-rr._wkst) % 7 - else: - # Number of days in the year, minus the days we - # left in last year. - wyearlen = self.yearlen-no1wkst - div, mod = divmod(wyearlen, 7) - numweeks = div+mod//4 - for n in rr._byweekno: - if n < 0: - n += numweeks+1 - if not (0 < n <= numweeks): - continue - if n > 1: - i = no1wkst+(n-1)*7 - if no1wkst != firstwkst: - i -= 7-firstwkst - else: - i = no1wkst - for j in range(7): - self.wnomask[i] = 1 - i += 1 - if self.wdaymask[i] == rr._wkst: - break - if 1 in rr._byweekno: - # Check week number 1 of next year as well - # TODO: Check -numweeks for next year. - i = no1wkst+numweeks*7 - if no1wkst != firstwkst: - i -= 7-firstwkst - if i < self.yearlen: - # If week starts in next year, we - # don't care about it. - for j in range(7): - self.wnomask[i] = 1 - i += 1 - if self.wdaymask[i] == rr._wkst: - break - if no1wkst: - # Check last week number of last year as - # well. If no1wkst is 0, either the year - # started on week start, or week number 1 - # got days from last year, so there are no - # days from last year's last week number in - # this year. - if -1 not in rr._byweekno: - lyearweekday = datetime.date(year-1, 1, 1).weekday() - lno1wkst = (7-lyearweekday+rr._wkst) % 7 - lyearlen = 365+calendar.isleap(year-1) - if lno1wkst >= 4: - lno1wkst = 0 - lnumweeks = 52+(lyearlen + - (lyearweekday-rr._wkst) % 7) % 7//4 - else: - lnumweeks = 52+(self.yearlen-no1wkst) % 7//4 - else: - lnumweeks = -1 - if lnumweeks in rr._byweekno: - for i in range(no1wkst): - self.wnomask[i] = 1 - - if (rr._bynweekday and (month != self.lastmonth or - year != self.lastyear)): - ranges = [] - if rr._freq == YEARLY: - if rr._bymonth: - for month in rr._bymonth: - ranges.append(self.mrange[month-1:month+1]) - else: - ranges = [(0, self.yearlen)] - elif rr._freq == MONTHLY: - ranges = [self.mrange[month-1:month+1]] - if ranges: - # Weekly frequency won't get here, so we may not - # care about cross-year weekly periods. - self.nwdaymask = [0]*self.yearlen - for first, last in ranges: - last -= 1 - for wday, n in rr._bynweekday: - if n < 0: - i = last+(n+1)*7 - i -= (self.wdaymask[i]-wday) % 7 - else: - i = first+(n-1)*7 - i += (7-self.wdaymask[i]+wday) % 7 - if first <= i <= last: - self.nwdaymask[i] = 1 - - if rr._byeaster: - self.eastermask = [0]*(self.yearlen+7) - eyday = easter.easter(year).toordinal()-self.yearordinal - for offset in rr._byeaster: - self.eastermask[eyday+offset] = 1 - - self.lastyear = year - self.lastmonth = month - - def ydayset(self, year, month, day): - return list(range(self.yearlen)), 0, self.yearlen - - def mdayset(self, year, month, day): - dset = [None]*self.yearlen - start, end = self.mrange[month-1:month+1] - for i in range(start, end): - dset[i] = i - return dset, start, end - - def wdayset(self, year, month, day): - # We need to handle cross-year weeks here. - dset = [None]*(self.yearlen+7) - i = datetime.date(year, month, day).toordinal()-self.yearordinal - start = i - for j in range(7): - dset[i] = i - i += 1 - # if (not (0 <= i < self.yearlen) or - # self.wdaymask[i] == self.rrule._wkst): - # This will cross the year boundary, if necessary. - if self.wdaymask[i] == self.rrule._wkst: - break - return dset, start, i - - def ddayset(self, year, month, day): - dset = [None] * self.yearlen - i = datetime.date(year, month, day).toordinal() - self.yearordinal - dset[i] = i - return dset, i, i + 1 - - def htimeset(self, hour, minute, second): - tset = [] - rr = self.rrule - for minute in rr._byminute: - for second in rr._bysecond: - tset.append(datetime.time(hour, minute, second, - tzinfo=rr._tzinfo)) - tset.sort() - return tset - - def mtimeset(self, hour, minute, second): - tset = [] - rr = self.rrule - for second in rr._bysecond: - tset.append(datetime.time(hour, minute, second, tzinfo=rr._tzinfo)) - tset.sort() - return tset - - def stimeset(self, hour, minute, second): - return (datetime.time(hour, minute, second, - tzinfo=self.rrule._tzinfo),) - - -class rruleset(rrulebase): - """ The rruleset type allows more complex recurrence setups, mixing - multiple rules, dates, exclusion rules, and exclusion dates. The type - constructor takes the following keyword arguments: - - :param cache: If True, caching of results will be enabled, improving - performance of multiple queries considerably. """ - - class _genitem(object): - def __init__(self, genlist, gen): - try: - self.dt = advance_iterator(gen) - genlist.append(self) - except StopIteration: - pass - self.genlist = genlist - self.gen = gen - - def __next__(self): - try: - self.dt = advance_iterator(self.gen) - except StopIteration: - if self.genlist[0] is self: - heapq.heappop(self.genlist) - else: - self.genlist.remove(self) - heapq.heapify(self.genlist) - - next = __next__ - - def __lt__(self, other): - return self.dt < other.dt - - def __gt__(self, other): - return self.dt > other.dt - - def __eq__(self, other): - return self.dt == other.dt - - def __ne__(self, other): - return self.dt != other.dt - - def __init__(self, cache=False): - super(rruleset, self).__init__(cache) - self._rrule = [] - self._rdate = [] - self._exrule = [] - self._exdate = [] - - @_invalidates_cache - def rrule(self, rrule): - """ Include the given :py:class:`rrule` instance in the recurrence set - generation. """ - self._rrule.append(rrule) - - @_invalidates_cache - def rdate(self, rdate): - """ Include the given :py:class:`datetime` instance in the recurrence - set generation. """ - self._rdate.append(rdate) - - @_invalidates_cache - def exrule(self, exrule): - """ Include the given rrule instance in the recurrence set exclusion - list. Dates which are part of the given recurrence rules will not - be generated, even if some inclusive rrule or rdate matches them. - """ - self._exrule.append(exrule) - - @_invalidates_cache - def exdate(self, exdate): - """ Include the given datetime instance in the recurrence set - exclusion list. Dates included that way will not be generated, - even if some inclusive rrule or rdate matches them. """ - self._exdate.append(exdate) - - def _iter(self): - rlist = [] - self._rdate.sort() - self._genitem(rlist, iter(self._rdate)) - for gen in [iter(x) for x in self._rrule]: - self._genitem(rlist, gen) - exlist = [] - self._exdate.sort() - self._genitem(exlist, iter(self._exdate)) - for gen in [iter(x) for x in self._exrule]: - self._genitem(exlist, gen) - lastdt = None - total = 0 - heapq.heapify(rlist) - heapq.heapify(exlist) - while rlist: - ritem = rlist[0] - if not lastdt or lastdt != ritem.dt: - while exlist and exlist[0] < ritem: - exitem = exlist[0] - advance_iterator(exitem) - if exlist and exlist[0] is exitem: - heapq.heapreplace(exlist, exitem) - if not exlist or ritem != exlist[0]: - total += 1 - yield ritem.dt - lastdt = ritem.dt - advance_iterator(ritem) - if rlist and rlist[0] is ritem: - heapq.heapreplace(rlist, ritem) - self._len = total - - - - -class _rrulestr(object): - """ Parses a string representation of a recurrence rule or set of - recurrence rules. - - :param s: - Required, a string defining one or more recurrence rules. - - :param dtstart: - If given, used as the default recurrence start if not specified in the - rule string. - - :param cache: - If set ``True`` caching of results will be enabled, improving - performance of multiple queries considerably. - - :param unfold: - If set ``True`` indicates that a rule string is split over more - than one line and should be joined before processing. - - :param forceset: - If set ``True`` forces a :class:`dateutil.rrule.rruleset` to - be returned. - - :param compatible: - If set ``True`` forces ``unfold`` and ``forceset`` to be ``True``. - - :param ignoretz: - If set ``True``, time zones in parsed strings are ignored and a naive - :class:`datetime.datetime` object is returned. - - :param tzids: - If given, a callable or mapping used to retrieve a - :class:`datetime.tzinfo` from a string representation. - Defaults to :func:`dateutil.tz.gettz`. - - :param tzinfos: - Additional time zone names / aliases which may be present in a string - representation. See :func:`dateutil.parser.parse` for more - information. - - :return: - Returns a :class:`dateutil.rrule.rruleset` or - :class:`dateutil.rrule.rrule` - """ - - _freq_map = {"YEARLY": YEARLY, - "MONTHLY": MONTHLY, - "WEEKLY": WEEKLY, - "DAILY": DAILY, - "HOURLY": HOURLY, - "MINUTELY": MINUTELY, - "SECONDLY": SECONDLY} - - _weekday_map = {"MO": 0, "TU": 1, "WE": 2, "TH": 3, - "FR": 4, "SA": 5, "SU": 6} - - def _handle_int(self, rrkwargs, name, value, **kwargs): - rrkwargs[name.lower()] = int(value) - - def _handle_int_list(self, rrkwargs, name, value, **kwargs): - rrkwargs[name.lower()] = [int(x) for x in value.split(',')] - - _handle_INTERVAL = _handle_int - _handle_COUNT = _handle_int - _handle_BYSETPOS = _handle_int_list - _handle_BYMONTH = _handle_int_list - _handle_BYMONTHDAY = _handle_int_list - _handle_BYYEARDAY = _handle_int_list - _handle_BYEASTER = _handle_int_list - _handle_BYWEEKNO = _handle_int_list - _handle_BYHOUR = _handle_int_list - _handle_BYMINUTE = _handle_int_list - _handle_BYSECOND = _handle_int_list - - def _handle_FREQ(self, rrkwargs, name, value, **kwargs): - rrkwargs["freq"] = self._freq_map[value] - - def _handle_UNTIL(self, rrkwargs, name, value, **kwargs): - global parser - if not parser: - from dateutil import parser - try: - rrkwargs["until"] = parser.parse(value, - ignoretz=kwargs.get("ignoretz"), - tzinfos=kwargs.get("tzinfos")) - except ValueError: - raise ValueError("invalid until date") - - def _handle_WKST(self, rrkwargs, name, value, **kwargs): - rrkwargs["wkst"] = self._weekday_map[value] - - def _handle_BYWEEKDAY(self, rrkwargs, name, value, **kwargs): - """ - Two ways to specify this: +1MO or MO(+1) - """ - l = [] - for wday in value.split(','): - if '(' in wday: - # If it's of the form TH(+1), etc. - splt = wday.split('(') - w = splt[0] - n = int(splt[1][:-1]) - elif len(wday): - # If it's of the form +1MO - for i in range(len(wday)): - if wday[i] not in '+-0123456789': - break - n = wday[:i] or None - w = wday[i:] - if n: - n = int(n) - else: - raise ValueError("Invalid (empty) BYDAY specification.") - - l.append(weekdays[self._weekday_map[w]](n)) - rrkwargs["byweekday"] = l - - _handle_BYDAY = _handle_BYWEEKDAY - - def _parse_rfc_rrule(self, line, - dtstart=None, - cache=False, - ignoretz=False, - tzinfos=None): - if line.find(':') != -1: - name, value = line.split(':') - if name != "RRULE": - raise ValueError("unknown parameter name") - else: - value = line - rrkwargs = {} - for pair in value.split(';'): - name, value = pair.split('=') - name = name.upper() - value = value.upper() - try: - getattr(self, "_handle_"+name)(rrkwargs, name, value, - ignoretz=ignoretz, - tzinfos=tzinfos) - except AttributeError: - raise ValueError("unknown parameter '%s'" % name) - except (KeyError, ValueError): - raise ValueError("invalid '%s': %s" % (name, value)) - return rrule(dtstart=dtstart, cache=cache, **rrkwargs) - - def _parse_date_value(self, date_value, parms, rule_tzids, - ignoretz, tzids, tzinfos): - global parser - if not parser: - from dateutil import parser - - datevals = [] - value_found = False - TZID = None - - for parm in parms: - if parm.startswith("TZID="): - try: - tzkey = rule_tzids[parm.split('TZID=')[-1]] - except KeyError: - continue - if tzids is None: - from . import tz - tzlookup = tz.gettz - elif callable(tzids): - tzlookup = tzids - else: - tzlookup = getattr(tzids, 'get', None) - if tzlookup is None: - msg = ('tzids must be a callable, mapping, or None, ' - 'not %s' % tzids) - raise ValueError(msg) - - TZID = tzlookup(tzkey) - continue - - # RFC 5445 3.8.2.4: The VALUE parameter is optional, but may be found - # only once. - if parm not in {"VALUE=DATE-TIME", "VALUE=DATE"}: - raise ValueError("unsupported parm: " + parm) - else: - if value_found: - msg = ("Duplicate value parameter found in: " + parm) - raise ValueError(msg) - value_found = True - - for datestr in date_value.split(','): - date = parser.parse(datestr, ignoretz=ignoretz, tzinfos=tzinfos) - if TZID is not None: - if date.tzinfo is None: - date = date.replace(tzinfo=TZID) - else: - raise ValueError('DTSTART/EXDATE specifies multiple timezone') - datevals.append(date) - - return datevals - - def _parse_rfc(self, s, - dtstart=None, - cache=False, - unfold=False, - forceset=False, - compatible=False, - ignoretz=False, - tzids=None, - tzinfos=None): - global parser - if compatible: - forceset = True - unfold = True - - TZID_NAMES = dict(map( - lambda x: (x.upper(), x), - re.findall('TZID=(?P[^:]+):', s) - )) - s = s.upper() - if not s.strip(): - raise ValueError("empty string") - if unfold: - lines = s.splitlines() - i = 0 - while i < len(lines): - line = lines[i].rstrip() - if not line: - del lines[i] - elif i > 0 and line[0] == " ": - lines[i-1] += line[1:] - del lines[i] - else: - i += 1 - else: - lines = s.split() - if (not forceset and len(lines) == 1 and (s.find(':') == -1 or - s.startswith('RRULE:'))): - return self._parse_rfc_rrule(lines[0], cache=cache, - dtstart=dtstart, ignoretz=ignoretz, - tzinfos=tzinfos) - else: - rrulevals = [] - rdatevals = [] - exrulevals = [] - exdatevals = [] - for line in lines: - if not line: - continue - if line.find(':') == -1: - name = "RRULE" - value = line - else: - name, value = line.split(':', 1) - parms = name.split(';') - if not parms: - raise ValueError("empty property name") - name = parms[0] - parms = parms[1:] - if name == "RRULE": - for parm in parms: - raise ValueError("unsupported RRULE parm: "+parm) - rrulevals.append(value) - elif name == "RDATE": - for parm in parms: - if parm != "VALUE=DATE-TIME": - raise ValueError("unsupported RDATE parm: "+parm) - rdatevals.append(value) - elif name == "EXRULE": - for parm in parms: - raise ValueError("unsupported EXRULE parm: "+parm) - exrulevals.append(value) - elif name == "EXDATE": - exdatevals.extend( - self._parse_date_value(value, parms, - TZID_NAMES, ignoretz, - tzids, tzinfos) - ) - elif name == "DTSTART": - dtvals = self._parse_date_value(value, parms, TZID_NAMES, - ignoretz, tzids, tzinfos) - if len(dtvals) != 1: - raise ValueError("Multiple DTSTART values specified:" + - value) - dtstart = dtvals[0] - else: - raise ValueError("unsupported property: "+name) - if (forceset or len(rrulevals) > 1 or rdatevals - or exrulevals or exdatevals): - if not parser and (rdatevals or exdatevals): - from dateutil import parser - rset = rruleset(cache=cache) - for value in rrulevals: - rset.rrule(self._parse_rfc_rrule(value, dtstart=dtstart, - ignoretz=ignoretz, - tzinfos=tzinfos)) - for value in rdatevals: - for datestr in value.split(','): - rset.rdate(parser.parse(datestr, - ignoretz=ignoretz, - tzinfos=tzinfos)) - for value in exrulevals: - rset.exrule(self._parse_rfc_rrule(value, dtstart=dtstart, - ignoretz=ignoretz, - tzinfos=tzinfos)) - for value in exdatevals: - rset.exdate(value) - if compatible and dtstart: - rset.rdate(dtstart) - return rset - else: - return self._parse_rfc_rrule(rrulevals[0], - dtstart=dtstart, - cache=cache, - ignoretz=ignoretz, - tzinfos=tzinfos) - - def __call__(self, s, **kwargs): - return self._parse_rfc(s, **kwargs) - - -rrulestr = _rrulestr() - -# vim:ts=4:sw=4:et diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/__init__.py deleted file mode 100644 index af1352c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -from .tz import * -from .tz import __doc__ - -__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange", - "tzstr", "tzical", "tzwin", "tzwinlocal", "gettz", - "enfold", "datetime_ambiguous", "datetime_exists", - "resolve_imaginary", "UTC", "DeprecatedTzFormatWarning"] - - -class DeprecatedTzFormatWarning(Warning): - """Warning raised when time zones are parsed from deprecated formats.""" diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index aa1b9c6..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/__pycache__/_common.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/__pycache__/_common.cpython-38.pyc deleted file mode 100644 index 5eb72ef..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/__pycache__/_common.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/__pycache__/_factories.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/__pycache__/_factories.cpython-38.pyc deleted file mode 100644 index 9b05e13..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/__pycache__/_factories.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/__pycache__/tz.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/__pycache__/tz.cpython-38.pyc deleted file mode 100644 index 95a582c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/__pycache__/tz.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/__pycache__/win.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/__pycache__/win.cpython-38.pyc deleted file mode 100644 index dbad89e..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/__pycache__/win.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/_common.py b/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/_common.py deleted file mode 100644 index e6ac118..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/_common.py +++ /dev/null @@ -1,419 +0,0 @@ -from six import PY2 - -from functools import wraps - -from datetime import datetime, timedelta, tzinfo - - -ZERO = timedelta(0) - -__all__ = ['tzname_in_python2', 'enfold'] - - -def tzname_in_python2(namefunc): - """Change unicode output into bytestrings in Python 2 - - tzname() API changed in Python 3. It used to return bytes, but was changed - to unicode strings - """ - if PY2: - @wraps(namefunc) - def adjust_encoding(*args, **kwargs): - name = namefunc(*args, **kwargs) - if name is not None: - name = name.encode() - - return name - - return adjust_encoding - else: - return namefunc - - -# The following is adapted from Alexander Belopolsky's tz library -# https://github.com/abalkin/tz -if hasattr(datetime, 'fold'): - # This is the pre-python 3.6 fold situation - def enfold(dt, fold=1): - """ - Provides a unified interface for assigning the ``fold`` attribute to - datetimes both before and after the implementation of PEP-495. - - :param fold: - The value for the ``fold`` attribute in the returned datetime. This - should be either 0 or 1. - - :return: - Returns an object for which ``getattr(dt, 'fold', 0)`` returns - ``fold`` for all versions of Python. In versions prior to - Python 3.6, this is a ``_DatetimeWithFold`` object, which is a - subclass of :py:class:`datetime.datetime` with the ``fold`` - attribute added, if ``fold`` is 1. - - .. versionadded:: 2.6.0 - """ - return dt.replace(fold=fold) - -else: - class _DatetimeWithFold(datetime): - """ - This is a class designed to provide a PEP 495-compliant interface for - Python versions before 3.6. It is used only for dates in a fold, so - the ``fold`` attribute is fixed at ``1``. - - .. versionadded:: 2.6.0 - """ - __slots__ = () - - def replace(self, *args, **kwargs): - """ - Return a datetime with the same attributes, except for those - attributes given new values by whichever keyword arguments are - specified. Note that tzinfo=None can be specified to create a naive - datetime from an aware datetime with no conversion of date and time - data. - - This is reimplemented in ``_DatetimeWithFold`` because pypy3 will - return a ``datetime.datetime`` even if ``fold`` is unchanged. - """ - argnames = ( - 'year', 'month', 'day', 'hour', 'minute', 'second', - 'microsecond', 'tzinfo' - ) - - for arg, argname in zip(args, argnames): - if argname in kwargs: - raise TypeError('Duplicate argument: {}'.format(argname)) - - kwargs[argname] = arg - - for argname in argnames: - if argname not in kwargs: - kwargs[argname] = getattr(self, argname) - - dt_class = self.__class__ if kwargs.get('fold', 1) else datetime - - return dt_class(**kwargs) - - @property - def fold(self): - return 1 - - def enfold(dt, fold=1): - """ - Provides a unified interface for assigning the ``fold`` attribute to - datetimes both before and after the implementation of PEP-495. - - :param fold: - The value for the ``fold`` attribute in the returned datetime. This - should be either 0 or 1. - - :return: - Returns an object for which ``getattr(dt, 'fold', 0)`` returns - ``fold`` for all versions of Python. In versions prior to - Python 3.6, this is a ``_DatetimeWithFold`` object, which is a - subclass of :py:class:`datetime.datetime` with the ``fold`` - attribute added, if ``fold`` is 1. - - .. versionadded:: 2.6.0 - """ - if getattr(dt, 'fold', 0) == fold: - return dt - - args = dt.timetuple()[:6] - args += (dt.microsecond, dt.tzinfo) - - if fold: - return _DatetimeWithFold(*args) - else: - return datetime(*args) - - -def _validate_fromutc_inputs(f): - """ - The CPython version of ``fromutc`` checks that the input is a ``datetime`` - object and that ``self`` is attached as its ``tzinfo``. - """ - @wraps(f) - def fromutc(self, dt): - if not isinstance(dt, datetime): - raise TypeError("fromutc() requires a datetime argument") - if dt.tzinfo is not self: - raise ValueError("dt.tzinfo is not self") - - return f(self, dt) - - return fromutc - - -class _tzinfo(tzinfo): - """ - Base class for all ``dateutil`` ``tzinfo`` objects. - """ - - def is_ambiguous(self, dt): - """ - Whether or not the "wall time" of a given datetime is ambiguous in this - zone. - - :param dt: - A :py:class:`datetime.datetime`, naive or time zone aware. - - - :return: - Returns ``True`` if ambiguous, ``False`` otherwise. - - .. versionadded:: 2.6.0 - """ - - dt = dt.replace(tzinfo=self) - - wall_0 = enfold(dt, fold=0) - wall_1 = enfold(dt, fold=1) - - same_offset = wall_0.utcoffset() == wall_1.utcoffset() - same_dt = wall_0.replace(tzinfo=None) == wall_1.replace(tzinfo=None) - - return same_dt and not same_offset - - def _fold_status(self, dt_utc, dt_wall): - """ - Determine the fold status of a "wall" datetime, given a representation - of the same datetime as a (naive) UTC datetime. This is calculated based - on the assumption that ``dt.utcoffset() - dt.dst()`` is constant for all - datetimes, and that this offset is the actual number of hours separating - ``dt_utc`` and ``dt_wall``. - - :param dt_utc: - Representation of the datetime as UTC - - :param dt_wall: - Representation of the datetime as "wall time". This parameter must - either have a `fold` attribute or have a fold-naive - :class:`datetime.tzinfo` attached, otherwise the calculation may - fail. - """ - if self.is_ambiguous(dt_wall): - delta_wall = dt_wall - dt_utc - _fold = int(delta_wall == (dt_utc.utcoffset() - dt_utc.dst())) - else: - _fold = 0 - - return _fold - - def _fold(self, dt): - return getattr(dt, 'fold', 0) - - def _fromutc(self, dt): - """ - Given a timezone-aware datetime in a given timezone, calculates a - timezone-aware datetime in a new timezone. - - Since this is the one time that we *know* we have an unambiguous - datetime object, we take this opportunity to determine whether the - datetime is ambiguous and in a "fold" state (e.g. if it's the first - occurrence, chronologically, of the ambiguous datetime). - - :param dt: - A timezone-aware :class:`datetime.datetime` object. - """ - - # Re-implement the algorithm from Python's datetime.py - dtoff = dt.utcoffset() - if dtoff is None: - raise ValueError("fromutc() requires a non-None utcoffset() " - "result") - - # The original datetime.py code assumes that `dst()` defaults to - # zero during ambiguous times. PEP 495 inverts this presumption, so - # for pre-PEP 495 versions of python, we need to tweak the algorithm. - dtdst = dt.dst() - if dtdst is None: - raise ValueError("fromutc() requires a non-None dst() result") - delta = dtoff - dtdst - - dt += delta - # Set fold=1 so we can default to being in the fold for - # ambiguous dates. - dtdst = enfold(dt, fold=1).dst() - if dtdst is None: - raise ValueError("fromutc(): dt.dst gave inconsistent " - "results; cannot convert") - return dt + dtdst - - @_validate_fromutc_inputs - def fromutc(self, dt): - """ - Given a timezone-aware datetime in a given timezone, calculates a - timezone-aware datetime in a new timezone. - - Since this is the one time that we *know* we have an unambiguous - datetime object, we take this opportunity to determine whether the - datetime is ambiguous and in a "fold" state (e.g. if it's the first - occurrence, chronologically, of the ambiguous datetime). - - :param dt: - A timezone-aware :class:`datetime.datetime` object. - """ - dt_wall = self._fromutc(dt) - - # Calculate the fold status given the two datetimes. - _fold = self._fold_status(dt, dt_wall) - - # Set the default fold value for ambiguous dates - return enfold(dt_wall, fold=_fold) - - -class tzrangebase(_tzinfo): - """ - This is an abstract base class for time zones represented by an annual - transition into and out of DST. Child classes should implement the following - methods: - - * ``__init__(self, *args, **kwargs)`` - * ``transitions(self, year)`` - this is expected to return a tuple of - datetimes representing the DST on and off transitions in standard - time. - - A fully initialized ``tzrangebase`` subclass should also provide the - following attributes: - * ``hasdst``: Boolean whether or not the zone uses DST. - * ``_dst_offset`` / ``_std_offset``: :class:`datetime.timedelta` objects - representing the respective UTC offsets. - * ``_dst_abbr`` / ``_std_abbr``: Strings representing the timezone short - abbreviations in DST and STD, respectively. - * ``_hasdst``: Whether or not the zone has DST. - - .. versionadded:: 2.6.0 - """ - def __init__(self): - raise NotImplementedError('tzrangebase is an abstract base class') - - def utcoffset(self, dt): - isdst = self._isdst(dt) - - if isdst is None: - return None - elif isdst: - return self._dst_offset - else: - return self._std_offset - - def dst(self, dt): - isdst = self._isdst(dt) - - if isdst is None: - return None - elif isdst: - return self._dst_base_offset - else: - return ZERO - - @tzname_in_python2 - def tzname(self, dt): - if self._isdst(dt): - return self._dst_abbr - else: - return self._std_abbr - - def fromutc(self, dt): - """ Given a datetime in UTC, return local time """ - if not isinstance(dt, datetime): - raise TypeError("fromutc() requires a datetime argument") - - if dt.tzinfo is not self: - raise ValueError("dt.tzinfo is not self") - - # Get transitions - if there are none, fixed offset - transitions = self.transitions(dt.year) - if transitions is None: - return dt + self.utcoffset(dt) - - # Get the transition times in UTC - dston, dstoff = transitions - - dston -= self._std_offset - dstoff -= self._std_offset - - utc_transitions = (dston, dstoff) - dt_utc = dt.replace(tzinfo=None) - - isdst = self._naive_isdst(dt_utc, utc_transitions) - - if isdst: - dt_wall = dt + self._dst_offset - else: - dt_wall = dt + self._std_offset - - _fold = int(not isdst and self.is_ambiguous(dt_wall)) - - return enfold(dt_wall, fold=_fold) - - def is_ambiguous(self, dt): - """ - Whether or not the "wall time" of a given datetime is ambiguous in this - zone. - - :param dt: - A :py:class:`datetime.datetime`, naive or time zone aware. - - - :return: - Returns ``True`` if ambiguous, ``False`` otherwise. - - .. versionadded:: 2.6.0 - """ - if not self.hasdst: - return False - - start, end = self.transitions(dt.year) - - dt = dt.replace(tzinfo=None) - return (end <= dt < end + self._dst_base_offset) - - def _isdst(self, dt): - if not self.hasdst: - return False - elif dt is None: - return None - - transitions = self.transitions(dt.year) - - if transitions is None: - return False - - dt = dt.replace(tzinfo=None) - - isdst = self._naive_isdst(dt, transitions) - - # Handle ambiguous dates - if not isdst and self.is_ambiguous(dt): - return not self._fold(dt) - else: - return isdst - - def _naive_isdst(self, dt, transitions): - dston, dstoff = transitions - - dt = dt.replace(tzinfo=None) - - if dston < dstoff: - isdst = dston <= dt < dstoff - else: - isdst = not dstoff <= dt < dston - - return isdst - - @property - def _dst_base_offset(self): - return self._dst_offset - self._std_offset - - __hash__ = None - - def __ne__(self, other): - return not (self == other) - - def __repr__(self): - return "%s(...)" % self.__class__.__name__ - - __reduce__ = object.__reduce__ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/_factories.py b/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/_factories.py deleted file mode 100644 index f8a6589..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/_factories.py +++ /dev/null @@ -1,80 +0,0 @@ -from datetime import timedelta -import weakref -from collections import OrderedDict - -from six.moves import _thread - - -class _TzSingleton(type): - def __init__(cls, *args, **kwargs): - cls.__instance = None - super(_TzSingleton, cls).__init__(*args, **kwargs) - - def __call__(cls): - if cls.__instance is None: - cls.__instance = super(_TzSingleton, cls).__call__() - return cls.__instance - - -class _TzFactory(type): - def instance(cls, *args, **kwargs): - """Alternate constructor that returns a fresh instance""" - return type.__call__(cls, *args, **kwargs) - - -class _TzOffsetFactory(_TzFactory): - def __init__(cls, *args, **kwargs): - cls.__instances = weakref.WeakValueDictionary() - cls.__strong_cache = OrderedDict() - cls.__strong_cache_size = 8 - - cls._cache_lock = _thread.allocate_lock() - - def __call__(cls, name, offset): - if isinstance(offset, timedelta): - key = (name, offset.total_seconds()) - else: - key = (name, offset) - - instance = cls.__instances.get(key, None) - if instance is None: - instance = cls.__instances.setdefault(key, - cls.instance(name, offset)) - - # This lock may not be necessary in Python 3. See GH issue #901 - with cls._cache_lock: - cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance) - - # Remove an item if the strong cache is overpopulated - if len(cls.__strong_cache) > cls.__strong_cache_size: - cls.__strong_cache.popitem(last=False) - - return instance - - -class _TzStrFactory(_TzFactory): - def __init__(cls, *args, **kwargs): - cls.__instances = weakref.WeakValueDictionary() - cls.__strong_cache = OrderedDict() - cls.__strong_cache_size = 8 - - cls.__cache_lock = _thread.allocate_lock() - - def __call__(cls, s, posix_offset=False): - key = (s, posix_offset) - instance = cls.__instances.get(key, None) - - if instance is None: - instance = cls.__instances.setdefault(key, - cls.instance(s, posix_offset)) - - # This lock may not be necessary in Python 3. See GH issue #901 - with cls.__cache_lock: - cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance) - - # Remove an item if the strong cache is overpopulated - if len(cls.__strong_cache) > cls.__strong_cache_size: - cls.__strong_cache.popitem(last=False) - - return instance - diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/tz.py b/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/tz.py deleted file mode 100644 index 6175914..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/tz.py +++ /dev/null @@ -1,1849 +0,0 @@ -# -*- coding: utf-8 -*- -""" -This module offers timezone implementations subclassing the abstract -:py:class:`datetime.tzinfo` type. There are classes to handle tzfile format -files (usually are in :file:`/etc/localtime`, :file:`/usr/share/zoneinfo`, -etc), TZ environment string (in all known formats), given ranges (with help -from relative deltas), local machine timezone, fixed offset timezone, and UTC -timezone. -""" -import datetime -import struct -import time -import sys -import os -import bisect -import weakref -from collections import OrderedDict - -import six -from six import string_types -from six.moves import _thread -from ._common import tzname_in_python2, _tzinfo -from ._common import tzrangebase, enfold -from ._common import _validate_fromutc_inputs - -from ._factories import _TzSingleton, _TzOffsetFactory -from ._factories import _TzStrFactory -try: - from .win import tzwin, tzwinlocal -except ImportError: - tzwin = tzwinlocal = None - -# For warning about rounding tzinfo -from warnings import warn - -ZERO = datetime.timedelta(0) -EPOCH = datetime.datetime(1970, 1, 1, 0, 0) -EPOCHORDINAL = EPOCH.toordinal() - - -@six.add_metaclass(_TzSingleton) -class tzutc(datetime.tzinfo): - """ - This is a tzinfo object that represents the UTC time zone. - - **Examples:** - - .. doctest:: - - >>> from datetime import * - >>> from dateutil.tz import * - - >>> datetime.now() - datetime.datetime(2003, 9, 27, 9, 40, 1, 521290) - - >>> datetime.now(tzutc()) - datetime.datetime(2003, 9, 27, 12, 40, 12, 156379, tzinfo=tzutc()) - - >>> datetime.now(tzutc()).tzname() - 'UTC' - - .. versionchanged:: 2.7.0 - ``tzutc()`` is now a singleton, so the result of ``tzutc()`` will - always return the same object. - - .. doctest:: - - >>> from dateutil.tz import tzutc, UTC - >>> tzutc() is tzutc() - True - >>> tzutc() is UTC - True - """ - def utcoffset(self, dt): - return ZERO - - def dst(self, dt): - return ZERO - - @tzname_in_python2 - def tzname(self, dt): - return "UTC" - - def is_ambiguous(self, dt): - """ - Whether or not the "wall time" of a given datetime is ambiguous in this - zone. - - :param dt: - A :py:class:`datetime.datetime`, naive or time zone aware. - - - :return: - Returns ``True`` if ambiguous, ``False`` otherwise. - - .. versionadded:: 2.6.0 - """ - return False - - @_validate_fromutc_inputs - def fromutc(self, dt): - """ - Fast track version of fromutc() returns the original ``dt`` object for - any valid :py:class:`datetime.datetime` object. - """ - return dt - - def __eq__(self, other): - if not isinstance(other, (tzutc, tzoffset)): - return NotImplemented - - return (isinstance(other, tzutc) or - (isinstance(other, tzoffset) and other._offset == ZERO)) - - __hash__ = None - - def __ne__(self, other): - return not (self == other) - - def __repr__(self): - return "%s()" % self.__class__.__name__ - - __reduce__ = object.__reduce__ - - -#: Convenience constant providing a :class:`tzutc()` instance -#: -#: .. versionadded:: 2.7.0 -UTC = tzutc() - - -@six.add_metaclass(_TzOffsetFactory) -class tzoffset(datetime.tzinfo): - """ - A simple class for representing a fixed offset from UTC. - - :param name: - The timezone name, to be returned when ``tzname()`` is called. - :param offset: - The time zone offset in seconds, or (since version 2.6.0, represented - as a :py:class:`datetime.timedelta` object). - """ - def __init__(self, name, offset): - self._name = name - - try: - # Allow a timedelta - offset = offset.total_seconds() - except (TypeError, AttributeError): - pass - - self._offset = datetime.timedelta(seconds=_get_supported_offset(offset)) - - def utcoffset(self, dt): - return self._offset - - def dst(self, dt): - return ZERO - - @tzname_in_python2 - def tzname(self, dt): - return self._name - - @_validate_fromutc_inputs - def fromutc(self, dt): - return dt + self._offset - - def is_ambiguous(self, dt): - """ - Whether or not the "wall time" of a given datetime is ambiguous in this - zone. - - :param dt: - A :py:class:`datetime.datetime`, naive or time zone aware. - :return: - Returns ``True`` if ambiguous, ``False`` otherwise. - - .. versionadded:: 2.6.0 - """ - return False - - def __eq__(self, other): - if not isinstance(other, tzoffset): - return NotImplemented - - return self._offset == other._offset - - __hash__ = None - - def __ne__(self, other): - return not (self == other) - - def __repr__(self): - return "%s(%s, %s)" % (self.__class__.__name__, - repr(self._name), - int(self._offset.total_seconds())) - - __reduce__ = object.__reduce__ - - -class tzlocal(_tzinfo): - """ - A :class:`tzinfo` subclass built around the ``time`` timezone functions. - """ - def __init__(self): - super(tzlocal, self).__init__() - - self._std_offset = datetime.timedelta(seconds=-time.timezone) - if time.daylight: - self._dst_offset = datetime.timedelta(seconds=-time.altzone) - else: - self._dst_offset = self._std_offset - - self._dst_saved = self._dst_offset - self._std_offset - self._hasdst = bool(self._dst_saved) - self._tznames = tuple(time.tzname) - - def utcoffset(self, dt): - if dt is None and self._hasdst: - return None - - if self._isdst(dt): - return self._dst_offset - else: - return self._std_offset - - def dst(self, dt): - if dt is None and self._hasdst: - return None - - if self._isdst(dt): - return self._dst_offset - self._std_offset - else: - return ZERO - - @tzname_in_python2 - def tzname(self, dt): - return self._tznames[self._isdst(dt)] - - def is_ambiguous(self, dt): - """ - Whether or not the "wall time" of a given datetime is ambiguous in this - zone. - - :param dt: - A :py:class:`datetime.datetime`, naive or time zone aware. - - - :return: - Returns ``True`` if ambiguous, ``False`` otherwise. - - .. versionadded:: 2.6.0 - """ - naive_dst = self._naive_is_dst(dt) - return (not naive_dst and - (naive_dst != self._naive_is_dst(dt - self._dst_saved))) - - def _naive_is_dst(self, dt): - timestamp = _datetime_to_timestamp(dt) - return time.localtime(timestamp + time.timezone).tm_isdst - - def _isdst(self, dt, fold_naive=True): - # We can't use mktime here. It is unstable when deciding if - # the hour near to a change is DST or not. - # - # timestamp = time.mktime((dt.year, dt.month, dt.day, dt.hour, - # dt.minute, dt.second, dt.weekday(), 0, -1)) - # return time.localtime(timestamp).tm_isdst - # - # The code above yields the following result: - # - # >>> import tz, datetime - # >>> t = tz.tzlocal() - # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() - # 'BRDT' - # >>> datetime.datetime(2003,2,16,0,tzinfo=t).tzname() - # 'BRST' - # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() - # 'BRST' - # >>> datetime.datetime(2003,2,15,22,tzinfo=t).tzname() - # 'BRDT' - # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() - # 'BRDT' - # - # Here is a more stable implementation: - # - if not self._hasdst: - return False - - # Check for ambiguous times: - dstval = self._naive_is_dst(dt) - fold = getattr(dt, 'fold', None) - - if self.is_ambiguous(dt): - if fold is not None: - return not self._fold(dt) - else: - return True - - return dstval - - def __eq__(self, other): - if isinstance(other, tzlocal): - return (self._std_offset == other._std_offset and - self._dst_offset == other._dst_offset) - elif isinstance(other, tzutc): - return (not self._hasdst and - self._tznames[0] in {'UTC', 'GMT'} and - self._std_offset == ZERO) - elif isinstance(other, tzoffset): - return (not self._hasdst and - self._tznames[0] == other._name and - self._std_offset == other._offset) - else: - return NotImplemented - - __hash__ = None - - def __ne__(self, other): - return not (self == other) - - def __repr__(self): - return "%s()" % self.__class__.__name__ - - __reduce__ = object.__reduce__ - - -class _ttinfo(object): - __slots__ = ["offset", "delta", "isdst", "abbr", - "isstd", "isgmt", "dstoffset"] - - def __init__(self): - for attr in self.__slots__: - setattr(self, attr, None) - - def __repr__(self): - l = [] - for attr in self.__slots__: - value = getattr(self, attr) - if value is not None: - l.append("%s=%s" % (attr, repr(value))) - return "%s(%s)" % (self.__class__.__name__, ", ".join(l)) - - def __eq__(self, other): - if not isinstance(other, _ttinfo): - return NotImplemented - - return (self.offset == other.offset and - self.delta == other.delta and - self.isdst == other.isdst and - self.abbr == other.abbr and - self.isstd == other.isstd and - self.isgmt == other.isgmt and - self.dstoffset == other.dstoffset) - - __hash__ = None - - def __ne__(self, other): - return not (self == other) - - def __getstate__(self): - state = {} - for name in self.__slots__: - state[name] = getattr(self, name, None) - return state - - def __setstate__(self, state): - for name in self.__slots__: - if name in state: - setattr(self, name, state[name]) - - -class _tzfile(object): - """ - Lightweight class for holding the relevant transition and time zone - information read from binary tzfiles. - """ - attrs = ['trans_list', 'trans_list_utc', 'trans_idx', 'ttinfo_list', - 'ttinfo_std', 'ttinfo_dst', 'ttinfo_before', 'ttinfo_first'] - - def __init__(self, **kwargs): - for attr in self.attrs: - setattr(self, attr, kwargs.get(attr, None)) - - -class tzfile(_tzinfo): - """ - This is a ``tzinfo`` subclass that allows one to use the ``tzfile(5)`` - format timezone files to extract current and historical zone information. - - :param fileobj: - This can be an opened file stream or a file name that the time zone - information can be read from. - - :param filename: - This is an optional parameter specifying the source of the time zone - information in the event that ``fileobj`` is a file object. If omitted - and ``fileobj`` is a file stream, this parameter will be set either to - ``fileobj``'s ``name`` attribute or to ``repr(fileobj)``. - - See `Sources for Time Zone and Daylight Saving Time Data - `_ for more information. - Time zone files can be compiled from the `IANA Time Zone database files - `_ with the `zic time zone compiler - `_ - - .. note:: - - Only construct a ``tzfile`` directly if you have a specific timezone - file on disk that you want to read into a Python ``tzinfo`` object. - If you want to get a ``tzfile`` representing a specific IANA zone, - (e.g. ``'America/New_York'``), you should call - :func:`dateutil.tz.gettz` with the zone identifier. - - - **Examples:** - - Using the US Eastern time zone as an example, we can see that a ``tzfile`` - provides time zone information for the standard Daylight Saving offsets: - - .. testsetup:: tzfile - - from dateutil.tz import gettz - from datetime import datetime - - .. doctest:: tzfile - - >>> NYC = gettz('America/New_York') - >>> NYC - tzfile('/usr/share/zoneinfo/America/New_York') - - >>> print(datetime(2016, 1, 3, tzinfo=NYC)) # EST - 2016-01-03 00:00:00-05:00 - - >>> print(datetime(2016, 7, 7, tzinfo=NYC)) # EDT - 2016-07-07 00:00:00-04:00 - - - The ``tzfile`` structure contains a fully history of the time zone, - so historical dates will also have the right offsets. For example, before - the adoption of the UTC standards, New York used local solar mean time: - - .. doctest:: tzfile - - >>> print(datetime(1901, 4, 12, tzinfo=NYC)) # LMT - 1901-04-12 00:00:00-04:56 - - And during World War II, New York was on "Eastern War Time", which was a - state of permanent daylight saving time: - - .. doctest:: tzfile - - >>> print(datetime(1944, 2, 7, tzinfo=NYC)) # EWT - 1944-02-07 00:00:00-04:00 - - """ - - def __init__(self, fileobj, filename=None): - super(tzfile, self).__init__() - - file_opened_here = False - if isinstance(fileobj, string_types): - self._filename = fileobj - fileobj = open(fileobj, 'rb') - file_opened_here = True - elif filename is not None: - self._filename = filename - elif hasattr(fileobj, "name"): - self._filename = fileobj.name - else: - self._filename = repr(fileobj) - - if fileobj is not None: - if not file_opened_here: - fileobj = _nullcontext(fileobj) - - with fileobj as file_stream: - tzobj = self._read_tzfile(file_stream) - - self._set_tzdata(tzobj) - - def _set_tzdata(self, tzobj): - """ Set the time zone data of this object from a _tzfile object """ - # Copy the relevant attributes over as private attributes - for attr in _tzfile.attrs: - setattr(self, '_' + attr, getattr(tzobj, attr)) - - def _read_tzfile(self, fileobj): - out = _tzfile() - - # From tzfile(5): - # - # The time zone information files used by tzset(3) - # begin with the magic characters "TZif" to identify - # them as time zone information files, followed by - # sixteen bytes reserved for future use, followed by - # six four-byte values of type long, written in a - # ``standard'' byte order (the high-order byte - # of the value is written first). - if fileobj.read(4).decode() != "TZif": - raise ValueError("magic not found") - - fileobj.read(16) - - ( - # The number of UTC/local indicators stored in the file. - ttisgmtcnt, - - # The number of standard/wall indicators stored in the file. - ttisstdcnt, - - # The number of leap seconds for which data is - # stored in the file. - leapcnt, - - # The number of "transition times" for which data - # is stored in the file. - timecnt, - - # The number of "local time types" for which data - # is stored in the file (must not be zero). - typecnt, - - # The number of characters of "time zone - # abbreviation strings" stored in the file. - charcnt, - - ) = struct.unpack(">6l", fileobj.read(24)) - - # The above header is followed by tzh_timecnt four-byte - # values of type long, sorted in ascending order. - # These values are written in ``standard'' byte order. - # Each is used as a transition time (as returned by - # time(2)) at which the rules for computing local time - # change. - - if timecnt: - out.trans_list_utc = list(struct.unpack(">%dl" % timecnt, - fileobj.read(timecnt*4))) - else: - out.trans_list_utc = [] - - # Next come tzh_timecnt one-byte values of type unsigned - # char; each one tells which of the different types of - # ``local time'' types described in the file is associated - # with the same-indexed transition time. These values - # serve as indices into an array of ttinfo structures that - # appears next in the file. - - if timecnt: - out.trans_idx = struct.unpack(">%dB" % timecnt, - fileobj.read(timecnt)) - else: - out.trans_idx = [] - - # Each ttinfo structure is written as a four-byte value - # for tt_gmtoff of type long, in a standard byte - # order, followed by a one-byte value for tt_isdst - # and a one-byte value for tt_abbrind. In each - # structure, tt_gmtoff gives the number of - # seconds to be added to UTC, tt_isdst tells whether - # tm_isdst should be set by localtime(3), and - # tt_abbrind serves as an index into the array of - # time zone abbreviation characters that follow the - # ttinfo structure(s) in the file. - - ttinfo = [] - - for i in range(typecnt): - ttinfo.append(struct.unpack(">lbb", fileobj.read(6))) - - abbr = fileobj.read(charcnt).decode() - - # Then there are tzh_leapcnt pairs of four-byte - # values, written in standard byte order; the - # first value of each pair gives the time (as - # returned by time(2)) at which a leap second - # occurs; the second gives the total number of - # leap seconds to be applied after the given time. - # The pairs of values are sorted in ascending order - # by time. - - # Not used, for now (but seek for correct file position) - if leapcnt: - fileobj.seek(leapcnt * 8, os.SEEK_CUR) - - # Then there are tzh_ttisstdcnt standard/wall - # indicators, each stored as a one-byte value; - # they tell whether the transition times associated - # with local time types were specified as standard - # time or wall clock time, and are used when - # a time zone file is used in handling POSIX-style - # time zone environment variables. - - if ttisstdcnt: - isstd = struct.unpack(">%db" % ttisstdcnt, - fileobj.read(ttisstdcnt)) - - # Finally, there are tzh_ttisgmtcnt UTC/local - # indicators, each stored as a one-byte value; - # they tell whether the transition times associated - # with local time types were specified as UTC or - # local time, and are used when a time zone file - # is used in handling POSIX-style time zone envi- - # ronment variables. - - if ttisgmtcnt: - isgmt = struct.unpack(">%db" % ttisgmtcnt, - fileobj.read(ttisgmtcnt)) - - # Build ttinfo list - out.ttinfo_list = [] - for i in range(typecnt): - gmtoff, isdst, abbrind = ttinfo[i] - gmtoff = _get_supported_offset(gmtoff) - tti = _ttinfo() - tti.offset = gmtoff - tti.dstoffset = datetime.timedelta(0) - tti.delta = datetime.timedelta(seconds=gmtoff) - tti.isdst = isdst - tti.abbr = abbr[abbrind:abbr.find('\x00', abbrind)] - tti.isstd = (ttisstdcnt > i and isstd[i] != 0) - tti.isgmt = (ttisgmtcnt > i and isgmt[i] != 0) - out.ttinfo_list.append(tti) - - # Replace ttinfo indexes for ttinfo objects. - out.trans_idx = [out.ttinfo_list[idx] for idx in out.trans_idx] - - # Set standard, dst, and before ttinfos. before will be - # used when a given time is before any transitions, - # and will be set to the first non-dst ttinfo, or to - # the first dst, if all of them are dst. - out.ttinfo_std = None - out.ttinfo_dst = None - out.ttinfo_before = None - if out.ttinfo_list: - if not out.trans_list_utc: - out.ttinfo_std = out.ttinfo_first = out.ttinfo_list[0] - else: - for i in range(timecnt-1, -1, -1): - tti = out.trans_idx[i] - if not out.ttinfo_std and not tti.isdst: - out.ttinfo_std = tti - elif not out.ttinfo_dst and tti.isdst: - out.ttinfo_dst = tti - - if out.ttinfo_std and out.ttinfo_dst: - break - else: - if out.ttinfo_dst and not out.ttinfo_std: - out.ttinfo_std = out.ttinfo_dst - - for tti in out.ttinfo_list: - if not tti.isdst: - out.ttinfo_before = tti - break - else: - out.ttinfo_before = out.ttinfo_list[0] - - # Now fix transition times to become relative to wall time. - # - # I'm not sure about this. In my tests, the tz source file - # is setup to wall time, and in the binary file isstd and - # isgmt are off, so it should be in wall time. OTOH, it's - # always in gmt time. Let me know if you have comments - # about this. - lastdst = None - lastoffset = None - lastdstoffset = None - lastbaseoffset = None - out.trans_list = [] - - for i, tti in enumerate(out.trans_idx): - offset = tti.offset - dstoffset = 0 - - if lastdst is not None: - if tti.isdst: - if not lastdst: - dstoffset = offset - lastoffset - - if not dstoffset and lastdstoffset: - dstoffset = lastdstoffset - - tti.dstoffset = datetime.timedelta(seconds=dstoffset) - lastdstoffset = dstoffset - - # If a time zone changes its base offset during a DST transition, - # then you need to adjust by the previous base offset to get the - # transition time in local time. Otherwise you use the current - # base offset. Ideally, I would have some mathematical proof of - # why this is true, but I haven't really thought about it enough. - baseoffset = offset - dstoffset - adjustment = baseoffset - if (lastbaseoffset is not None and baseoffset != lastbaseoffset - and tti.isdst != lastdst): - # The base DST has changed - adjustment = lastbaseoffset - - lastdst = tti.isdst - lastoffset = offset - lastbaseoffset = baseoffset - - out.trans_list.append(out.trans_list_utc[i] + adjustment) - - out.trans_idx = tuple(out.trans_idx) - out.trans_list = tuple(out.trans_list) - out.trans_list_utc = tuple(out.trans_list_utc) - - return out - - def _find_last_transition(self, dt, in_utc=False): - # If there's no list, there are no transitions to find - if not self._trans_list: - return None - - timestamp = _datetime_to_timestamp(dt) - - # Find where the timestamp fits in the transition list - if the - # timestamp is a transition time, it's part of the "after" period. - trans_list = self._trans_list_utc if in_utc else self._trans_list - idx = bisect.bisect_right(trans_list, timestamp) - - # We want to know when the previous transition was, so subtract off 1 - return idx - 1 - - def _get_ttinfo(self, idx): - # For no list or after the last transition, default to _ttinfo_std - if idx is None or (idx + 1) >= len(self._trans_list): - return self._ttinfo_std - - # If there is a list and the time is before it, return _ttinfo_before - if idx < 0: - return self._ttinfo_before - - return self._trans_idx[idx] - - def _find_ttinfo(self, dt): - idx = self._resolve_ambiguous_time(dt) - - return self._get_ttinfo(idx) - - def fromutc(self, dt): - """ - The ``tzfile`` implementation of :py:func:`datetime.tzinfo.fromutc`. - - :param dt: - A :py:class:`datetime.datetime` object. - - :raises TypeError: - Raised if ``dt`` is not a :py:class:`datetime.datetime` object. - - :raises ValueError: - Raised if this is called with a ``dt`` which does not have this - ``tzinfo`` attached. - - :return: - Returns a :py:class:`datetime.datetime` object representing the - wall time in ``self``'s time zone. - """ - # These isinstance checks are in datetime.tzinfo, so we'll preserve - # them, even if we don't care about duck typing. - if not isinstance(dt, datetime.datetime): - raise TypeError("fromutc() requires a datetime argument") - - if dt.tzinfo is not self: - raise ValueError("dt.tzinfo is not self") - - # First treat UTC as wall time and get the transition we're in. - idx = self._find_last_transition(dt, in_utc=True) - tti = self._get_ttinfo(idx) - - dt_out = dt + datetime.timedelta(seconds=tti.offset) - - fold = self.is_ambiguous(dt_out, idx=idx) - - return enfold(dt_out, fold=int(fold)) - - def is_ambiguous(self, dt, idx=None): - """ - Whether or not the "wall time" of a given datetime is ambiguous in this - zone. - - :param dt: - A :py:class:`datetime.datetime`, naive or time zone aware. - - - :return: - Returns ``True`` if ambiguous, ``False`` otherwise. - - .. versionadded:: 2.6.0 - """ - if idx is None: - idx = self._find_last_transition(dt) - - # Calculate the difference in offsets from current to previous - timestamp = _datetime_to_timestamp(dt) - tti = self._get_ttinfo(idx) - - if idx is None or idx <= 0: - return False - - od = self._get_ttinfo(idx - 1).offset - tti.offset - tt = self._trans_list[idx] # Transition time - - return timestamp < tt + od - - def _resolve_ambiguous_time(self, dt): - idx = self._find_last_transition(dt) - - # If we have no transitions, return the index - _fold = self._fold(dt) - if idx is None or idx == 0: - return idx - - # If it's ambiguous and we're in a fold, shift to a different index. - idx_offset = int(not _fold and self.is_ambiguous(dt, idx)) - - return idx - idx_offset - - def utcoffset(self, dt): - if dt is None: - return None - - if not self._ttinfo_std: - return ZERO - - return self._find_ttinfo(dt).delta - - def dst(self, dt): - if dt is None: - return None - - if not self._ttinfo_dst: - return ZERO - - tti = self._find_ttinfo(dt) - - if not tti.isdst: - return ZERO - - # The documentation says that utcoffset()-dst() must - # be constant for every dt. - return tti.dstoffset - - @tzname_in_python2 - def tzname(self, dt): - if not self._ttinfo_std or dt is None: - return None - return self._find_ttinfo(dt).abbr - - def __eq__(self, other): - if not isinstance(other, tzfile): - return NotImplemented - return (self._trans_list == other._trans_list and - self._trans_idx == other._trans_idx and - self._ttinfo_list == other._ttinfo_list) - - __hash__ = None - - def __ne__(self, other): - return not (self == other) - - def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, repr(self._filename)) - - def __reduce__(self): - return self.__reduce_ex__(None) - - def __reduce_ex__(self, protocol): - return (self.__class__, (None, self._filename), self.__dict__) - - -class tzrange(tzrangebase): - """ - The ``tzrange`` object is a time zone specified by a set of offsets and - abbreviations, equivalent to the way the ``TZ`` variable can be specified - in POSIX-like systems, but using Python delta objects to specify DST - start, end and offsets. - - :param stdabbr: - The abbreviation for standard time (e.g. ``'EST'``). - - :param stdoffset: - An integer or :class:`datetime.timedelta` object or equivalent - specifying the base offset from UTC. - - If unspecified, +00:00 is used. - - :param dstabbr: - The abbreviation for DST / "Summer" time (e.g. ``'EDT'``). - - If specified, with no other DST information, DST is assumed to occur - and the default behavior or ``dstoffset``, ``start`` and ``end`` is - used. If unspecified and no other DST information is specified, it - is assumed that this zone has no DST. - - If this is unspecified and other DST information is *is* specified, - DST occurs in the zone but the time zone abbreviation is left - unchanged. - - :param dstoffset: - A an integer or :class:`datetime.timedelta` object or equivalent - specifying the UTC offset during DST. If unspecified and any other DST - information is specified, it is assumed to be the STD offset +1 hour. - - :param start: - A :class:`relativedelta.relativedelta` object or equivalent specifying - the time and time of year that daylight savings time starts. To - specify, for example, that DST starts at 2AM on the 2nd Sunday in - March, pass: - - ``relativedelta(hours=2, month=3, day=1, weekday=SU(+2))`` - - If unspecified and any other DST information is specified, the default - value is 2 AM on the first Sunday in April. - - :param end: - A :class:`relativedelta.relativedelta` object or equivalent - representing the time and time of year that daylight savings time - ends, with the same specification method as in ``start``. One note is - that this should point to the first time in the *standard* zone, so if - a transition occurs at 2AM in the DST zone and the clocks are set back - 1 hour to 1AM, set the ``hours`` parameter to +1. - - - **Examples:** - - .. testsetup:: tzrange - - from dateutil.tz import tzrange, tzstr - - .. doctest:: tzrange - - >>> tzstr('EST5EDT') == tzrange("EST", -18000, "EDT") - True - - >>> from dateutil.relativedelta import * - >>> range1 = tzrange("EST", -18000, "EDT") - >>> range2 = tzrange("EST", -18000, "EDT", -14400, - ... relativedelta(hours=+2, month=4, day=1, - ... weekday=SU(+1)), - ... relativedelta(hours=+1, month=10, day=31, - ... weekday=SU(-1))) - >>> tzstr('EST5EDT') == range1 == range2 - True - - """ - def __init__(self, stdabbr, stdoffset=None, - dstabbr=None, dstoffset=None, - start=None, end=None): - - global relativedelta - from dateutil import relativedelta - - self._std_abbr = stdabbr - self._dst_abbr = dstabbr - - try: - stdoffset = stdoffset.total_seconds() - except (TypeError, AttributeError): - pass - - try: - dstoffset = dstoffset.total_seconds() - except (TypeError, AttributeError): - pass - - if stdoffset is not None: - self._std_offset = datetime.timedelta(seconds=stdoffset) - else: - self._std_offset = ZERO - - if dstoffset is not None: - self._dst_offset = datetime.timedelta(seconds=dstoffset) - elif dstabbr and stdoffset is not None: - self._dst_offset = self._std_offset + datetime.timedelta(hours=+1) - else: - self._dst_offset = ZERO - - if dstabbr and start is None: - self._start_delta = relativedelta.relativedelta( - hours=+2, month=4, day=1, weekday=relativedelta.SU(+1)) - else: - self._start_delta = start - - if dstabbr and end is None: - self._end_delta = relativedelta.relativedelta( - hours=+1, month=10, day=31, weekday=relativedelta.SU(-1)) - else: - self._end_delta = end - - self._dst_base_offset_ = self._dst_offset - self._std_offset - self.hasdst = bool(self._start_delta) - - def transitions(self, year): - """ - For a given year, get the DST on and off transition times, expressed - always on the standard time side. For zones with no transitions, this - function returns ``None``. - - :param year: - The year whose transitions you would like to query. - - :return: - Returns a :class:`tuple` of :class:`datetime.datetime` objects, - ``(dston, dstoff)`` for zones with an annual DST transition, or - ``None`` for fixed offset zones. - """ - if not self.hasdst: - return None - - base_year = datetime.datetime(year, 1, 1) - - start = base_year + self._start_delta - end = base_year + self._end_delta - - return (start, end) - - def __eq__(self, other): - if not isinstance(other, tzrange): - return NotImplemented - - return (self._std_abbr == other._std_abbr and - self._dst_abbr == other._dst_abbr and - self._std_offset == other._std_offset and - self._dst_offset == other._dst_offset and - self._start_delta == other._start_delta and - self._end_delta == other._end_delta) - - @property - def _dst_base_offset(self): - return self._dst_base_offset_ - - -@six.add_metaclass(_TzStrFactory) -class tzstr(tzrange): - """ - ``tzstr`` objects are time zone objects specified by a time-zone string as - it would be passed to a ``TZ`` variable on POSIX-style systems (see - the `GNU C Library: TZ Variable`_ for more details). - - There is one notable exception, which is that POSIX-style time zones use an - inverted offset format, so normally ``GMT+3`` would be parsed as an offset - 3 hours *behind* GMT. The ``tzstr`` time zone object will parse this as an - offset 3 hours *ahead* of GMT. If you would like to maintain the POSIX - behavior, pass a ``True`` value to ``posix_offset``. - - The :class:`tzrange` object provides the same functionality, but is - specified using :class:`relativedelta.relativedelta` objects. rather than - strings. - - :param s: - A time zone string in ``TZ`` variable format. This can be a - :class:`bytes` (2.x: :class:`str`), :class:`str` (2.x: - :class:`unicode`) or a stream emitting unicode characters - (e.g. :class:`StringIO`). - - :param posix_offset: - Optional. If set to ``True``, interpret strings such as ``GMT+3`` or - ``UTC+3`` as being 3 hours *behind* UTC rather than ahead, per the - POSIX standard. - - .. caution:: - - Prior to version 2.7.0, this function also supported time zones - in the format: - - * ``EST5EDT,4,0,6,7200,10,0,26,7200,3600`` - * ``EST5EDT,4,1,0,7200,10,-1,0,7200,3600`` - - This format is non-standard and has been deprecated; this function - will raise a :class:`DeprecatedTZFormatWarning` until - support is removed in a future version. - - .. _`GNU C Library: TZ Variable`: - https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html - """ - def __init__(self, s, posix_offset=False): - global parser - from dateutil.parser import _parser as parser - - self._s = s - - res = parser._parsetz(s) - if res is None or res.any_unused_tokens: - raise ValueError("unknown string format") - - # Here we break the compatibility with the TZ variable handling. - # GMT-3 actually *means* the timezone -3. - if res.stdabbr in ("GMT", "UTC") and not posix_offset: - res.stdoffset *= -1 - - # We must initialize it first, since _delta() needs - # _std_offset and _dst_offset set. Use False in start/end - # to avoid building it two times. - tzrange.__init__(self, res.stdabbr, res.stdoffset, - res.dstabbr, res.dstoffset, - start=False, end=False) - - if not res.dstabbr: - self._start_delta = None - self._end_delta = None - else: - self._start_delta = self._delta(res.start) - if self._start_delta: - self._end_delta = self._delta(res.end, isend=1) - - self.hasdst = bool(self._start_delta) - - def _delta(self, x, isend=0): - from dateutil import relativedelta - kwargs = {} - if x.month is not None: - kwargs["month"] = x.month - if x.weekday is not None: - kwargs["weekday"] = relativedelta.weekday(x.weekday, x.week) - if x.week > 0: - kwargs["day"] = 1 - else: - kwargs["day"] = 31 - elif x.day: - kwargs["day"] = x.day - elif x.yday is not None: - kwargs["yearday"] = x.yday - elif x.jyday is not None: - kwargs["nlyearday"] = x.jyday - if not kwargs: - # Default is to start on first sunday of april, and end - # on last sunday of october. - if not isend: - kwargs["month"] = 4 - kwargs["day"] = 1 - kwargs["weekday"] = relativedelta.SU(+1) - else: - kwargs["month"] = 10 - kwargs["day"] = 31 - kwargs["weekday"] = relativedelta.SU(-1) - if x.time is not None: - kwargs["seconds"] = x.time - else: - # Default is 2AM. - kwargs["seconds"] = 7200 - if isend: - # Convert to standard time, to follow the documented way - # of working with the extra hour. See the documentation - # of the tzinfo class. - delta = self._dst_offset - self._std_offset - kwargs["seconds"] -= delta.seconds + delta.days * 86400 - return relativedelta.relativedelta(**kwargs) - - def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, repr(self._s)) - - -class _tzicalvtzcomp(object): - def __init__(self, tzoffsetfrom, tzoffsetto, isdst, - tzname=None, rrule=None): - self.tzoffsetfrom = datetime.timedelta(seconds=tzoffsetfrom) - self.tzoffsetto = datetime.timedelta(seconds=tzoffsetto) - self.tzoffsetdiff = self.tzoffsetto - self.tzoffsetfrom - self.isdst = isdst - self.tzname = tzname - self.rrule = rrule - - -class _tzicalvtz(_tzinfo): - def __init__(self, tzid, comps=[]): - super(_tzicalvtz, self).__init__() - - self._tzid = tzid - self._comps = comps - self._cachedate = [] - self._cachecomp = [] - self._cache_lock = _thread.allocate_lock() - - def _find_comp(self, dt): - if len(self._comps) == 1: - return self._comps[0] - - dt = dt.replace(tzinfo=None) - - try: - with self._cache_lock: - return self._cachecomp[self._cachedate.index( - (dt, self._fold(dt)))] - except ValueError: - pass - - lastcompdt = None - lastcomp = None - - for comp in self._comps: - compdt = self._find_compdt(comp, dt) - - if compdt and (not lastcompdt or lastcompdt < compdt): - lastcompdt = compdt - lastcomp = comp - - if not lastcomp: - # RFC says nothing about what to do when a given - # time is before the first onset date. We'll look for the - # first standard component, or the first component, if - # none is found. - for comp in self._comps: - if not comp.isdst: - lastcomp = comp - break - else: - lastcomp = comp[0] - - with self._cache_lock: - self._cachedate.insert(0, (dt, self._fold(dt))) - self._cachecomp.insert(0, lastcomp) - - if len(self._cachedate) > 10: - self._cachedate.pop() - self._cachecomp.pop() - - return lastcomp - - def _find_compdt(self, comp, dt): - if comp.tzoffsetdiff < ZERO and self._fold(dt): - dt -= comp.tzoffsetdiff - - compdt = comp.rrule.before(dt, inc=True) - - return compdt - - def utcoffset(self, dt): - if dt is None: - return None - - return self._find_comp(dt).tzoffsetto - - def dst(self, dt): - comp = self._find_comp(dt) - if comp.isdst: - return comp.tzoffsetdiff - else: - return ZERO - - @tzname_in_python2 - def tzname(self, dt): - return self._find_comp(dt).tzname - - def __repr__(self): - return "" % repr(self._tzid) - - __reduce__ = object.__reduce__ - - -class tzical(object): - """ - This object is designed to parse an iCalendar-style ``VTIMEZONE`` structure - as set out in `RFC 5545`_ Section 4.6.5 into one or more `tzinfo` objects. - - :param `fileobj`: - A file or stream in iCalendar format, which should be UTF-8 encoded - with CRLF endings. - - .. _`RFC 5545`: https://tools.ietf.org/html/rfc5545 - """ - def __init__(self, fileobj): - global rrule - from dateutil import rrule - - if isinstance(fileobj, string_types): - self._s = fileobj - # ical should be encoded in UTF-8 with CRLF - fileobj = open(fileobj, 'r') - else: - self._s = getattr(fileobj, 'name', repr(fileobj)) - fileobj = _nullcontext(fileobj) - - self._vtz = {} - - with fileobj as fobj: - self._parse_rfc(fobj.read()) - - def keys(self): - """ - Retrieves the available time zones as a list. - """ - return list(self._vtz.keys()) - - def get(self, tzid=None): - """ - Retrieve a :py:class:`datetime.tzinfo` object by its ``tzid``. - - :param tzid: - If there is exactly one time zone available, omitting ``tzid`` - or passing :py:const:`None` value returns it. Otherwise a valid - key (which can be retrieved from :func:`keys`) is required. - - :raises ValueError: - Raised if ``tzid`` is not specified but there are either more - or fewer than 1 zone defined. - - :returns: - Returns either a :py:class:`datetime.tzinfo` object representing - the relevant time zone or :py:const:`None` if the ``tzid`` was - not found. - """ - if tzid is None: - if len(self._vtz) == 0: - raise ValueError("no timezones defined") - elif len(self._vtz) > 1: - raise ValueError("more than one timezone available") - tzid = next(iter(self._vtz)) - - return self._vtz.get(tzid) - - def _parse_offset(self, s): - s = s.strip() - if not s: - raise ValueError("empty offset") - if s[0] in ('+', '-'): - signal = (-1, +1)[s[0] == '+'] - s = s[1:] - else: - signal = +1 - if len(s) == 4: - return (int(s[:2]) * 3600 + int(s[2:]) * 60) * signal - elif len(s) == 6: - return (int(s[:2]) * 3600 + int(s[2:4]) * 60 + int(s[4:])) * signal - else: - raise ValueError("invalid offset: " + s) - - def _parse_rfc(self, s): - lines = s.splitlines() - if not lines: - raise ValueError("empty string") - - # Unfold - i = 0 - while i < len(lines): - line = lines[i].rstrip() - if not line: - del lines[i] - elif i > 0 and line[0] == " ": - lines[i-1] += line[1:] - del lines[i] - else: - i += 1 - - tzid = None - comps = [] - invtz = False - comptype = None - for line in lines: - if not line: - continue - name, value = line.split(':', 1) - parms = name.split(';') - if not parms: - raise ValueError("empty property name") - name = parms[0].upper() - parms = parms[1:] - if invtz: - if name == "BEGIN": - if value in ("STANDARD", "DAYLIGHT"): - # Process component - pass - else: - raise ValueError("unknown component: "+value) - comptype = value - founddtstart = False - tzoffsetfrom = None - tzoffsetto = None - rrulelines = [] - tzname = None - elif name == "END": - if value == "VTIMEZONE": - if comptype: - raise ValueError("component not closed: "+comptype) - if not tzid: - raise ValueError("mandatory TZID not found") - if not comps: - raise ValueError( - "at least one component is needed") - # Process vtimezone - self._vtz[tzid] = _tzicalvtz(tzid, comps) - invtz = False - elif value == comptype: - if not founddtstart: - raise ValueError("mandatory DTSTART not found") - if tzoffsetfrom is None: - raise ValueError( - "mandatory TZOFFSETFROM not found") - if tzoffsetto is None: - raise ValueError( - "mandatory TZOFFSETFROM not found") - # Process component - rr = None - if rrulelines: - rr = rrule.rrulestr("\n".join(rrulelines), - compatible=True, - ignoretz=True, - cache=True) - comp = _tzicalvtzcomp(tzoffsetfrom, tzoffsetto, - (comptype == "DAYLIGHT"), - tzname, rr) - comps.append(comp) - comptype = None - else: - raise ValueError("invalid component end: "+value) - elif comptype: - if name == "DTSTART": - # DTSTART in VTIMEZONE takes a subset of valid RRULE - # values under RFC 5545. - for parm in parms: - if parm != 'VALUE=DATE-TIME': - msg = ('Unsupported DTSTART param in ' + - 'VTIMEZONE: ' + parm) - raise ValueError(msg) - rrulelines.append(line) - founddtstart = True - elif name in ("RRULE", "RDATE", "EXRULE", "EXDATE"): - rrulelines.append(line) - elif name == "TZOFFSETFROM": - if parms: - raise ValueError( - "unsupported %s parm: %s " % (name, parms[0])) - tzoffsetfrom = self._parse_offset(value) - elif name == "TZOFFSETTO": - if parms: - raise ValueError( - "unsupported TZOFFSETTO parm: "+parms[0]) - tzoffsetto = self._parse_offset(value) - elif name == "TZNAME": - if parms: - raise ValueError( - "unsupported TZNAME parm: "+parms[0]) - tzname = value - elif name == "COMMENT": - pass - else: - raise ValueError("unsupported property: "+name) - else: - if name == "TZID": - if parms: - raise ValueError( - "unsupported TZID parm: "+parms[0]) - tzid = value - elif name in ("TZURL", "LAST-MODIFIED", "COMMENT"): - pass - else: - raise ValueError("unsupported property: "+name) - elif name == "BEGIN" and value == "VTIMEZONE": - tzid = None - comps = [] - invtz = True - - def __repr__(self): - return "%s(%s)" % (self.__class__.__name__, repr(self._s)) - - -if sys.platform != "win32": - TZFILES = ["/etc/localtime", "localtime"] - TZPATHS = ["/usr/share/zoneinfo", - "/usr/lib/zoneinfo", - "/usr/share/lib/zoneinfo", - "/etc/zoneinfo"] -else: - TZFILES = [] - TZPATHS = [] - - -def __get_gettz(): - tzlocal_classes = (tzlocal,) - if tzwinlocal is not None: - tzlocal_classes += (tzwinlocal,) - - class GettzFunc(object): - """ - Retrieve a time zone object from a string representation - - This function is intended to retrieve the :py:class:`tzinfo` subclass - that best represents the time zone that would be used if a POSIX - `TZ variable`_ were set to the same value. - - If no argument or an empty string is passed to ``gettz``, local time - is returned: - - .. code-block:: python3 - - >>> gettz() - tzfile('/etc/localtime') - - This function is also the preferred way to map IANA tz database keys - to :class:`tzfile` objects: - - .. code-block:: python3 - - >>> gettz('Pacific/Kiritimati') - tzfile('/usr/share/zoneinfo/Pacific/Kiritimati') - - On Windows, the standard is extended to include the Windows-specific - zone names provided by the operating system: - - .. code-block:: python3 - - >>> gettz('Egypt Standard Time') - tzwin('Egypt Standard Time') - - Passing a GNU ``TZ`` style string time zone specification returns a - :class:`tzstr` object: - - .. code-block:: python3 - - >>> gettz('AEST-10AEDT-11,M10.1.0/2,M4.1.0/3') - tzstr('AEST-10AEDT-11,M10.1.0/2,M4.1.0/3') - - :param name: - A time zone name (IANA, or, on Windows, Windows keys), location of - a ``tzfile(5)`` zoneinfo file or ``TZ`` variable style time zone - specifier. An empty string, no argument or ``None`` is interpreted - as local time. - - :return: - Returns an instance of one of ``dateutil``'s :py:class:`tzinfo` - subclasses. - - .. versionchanged:: 2.7.0 - - After version 2.7.0, any two calls to ``gettz`` using the same - input strings will return the same object: - - .. code-block:: python3 - - >>> tz.gettz('America/Chicago') is tz.gettz('America/Chicago') - True - - In addition to improving performance, this ensures that - `"same zone" semantics`_ are used for datetimes in the same zone. - - - .. _`TZ variable`: - https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html - - .. _`"same zone" semantics`: - https://blog.ganssle.io/articles/2018/02/aware-datetime-arithmetic.html - """ - def __init__(self): - - self.__instances = weakref.WeakValueDictionary() - self.__strong_cache_size = 8 - self.__strong_cache = OrderedDict() - self._cache_lock = _thread.allocate_lock() - - def __call__(self, name=None): - with self._cache_lock: - rv = self.__instances.get(name, None) - - if rv is None: - rv = self.nocache(name=name) - if not (name is None - or isinstance(rv, tzlocal_classes) - or rv is None): - # tzlocal is slightly more complicated than the other - # time zone providers because it depends on environment - # at construction time, so don't cache that. - # - # We also cannot store weak references to None, so we - # will also not store that. - self.__instances[name] = rv - else: - # No need for strong caching, return immediately - return rv - - self.__strong_cache[name] = self.__strong_cache.pop(name, rv) - - if len(self.__strong_cache) > self.__strong_cache_size: - self.__strong_cache.popitem(last=False) - - return rv - - def set_cache_size(self, size): - with self._cache_lock: - self.__strong_cache_size = size - while len(self.__strong_cache) > size: - self.__strong_cache.popitem(last=False) - - def cache_clear(self): - with self._cache_lock: - self.__instances = weakref.WeakValueDictionary() - self.__strong_cache.clear() - - @staticmethod - def nocache(name=None): - """A non-cached version of gettz""" - tz = None - if not name: - try: - name = os.environ["TZ"] - except KeyError: - pass - if name is None or name in ("", ":"): - for filepath in TZFILES: - if not os.path.isabs(filepath): - filename = filepath - for path in TZPATHS: - filepath = os.path.join(path, filename) - if os.path.isfile(filepath): - break - else: - continue - if os.path.isfile(filepath): - try: - tz = tzfile(filepath) - break - except (IOError, OSError, ValueError): - pass - else: - tz = tzlocal() - else: - try: - if name.startswith(":"): - name = name[1:] - except TypeError as e: - if isinstance(name, bytes): - new_msg = "gettz argument should be str, not bytes" - six.raise_from(TypeError(new_msg), e) - else: - raise - if os.path.isabs(name): - if os.path.isfile(name): - tz = tzfile(name) - else: - tz = None - else: - for path in TZPATHS: - filepath = os.path.join(path, name) - if not os.path.isfile(filepath): - filepath = filepath.replace(' ', '_') - if not os.path.isfile(filepath): - continue - try: - tz = tzfile(filepath) - break - except (IOError, OSError, ValueError): - pass - else: - tz = None - if tzwin is not None: - try: - tz = tzwin(name) - except (WindowsError, UnicodeEncodeError): - # UnicodeEncodeError is for Python 2.7 compat - tz = None - - if not tz: - from dateutil.zoneinfo import get_zonefile_instance - tz = get_zonefile_instance().get(name) - - if not tz: - for c in name: - # name is not a tzstr unless it has at least - # one offset. For short values of "name", an - # explicit for loop seems to be the fastest way - # To determine if a string contains a digit - if c in "0123456789": - try: - tz = tzstr(name) - except ValueError: - pass - break - else: - if name in ("GMT", "UTC"): - tz = UTC - elif name in time.tzname: - tz = tzlocal() - return tz - - return GettzFunc() - - -gettz = __get_gettz() -del __get_gettz - - -def datetime_exists(dt, tz=None): - """ - Given a datetime and a time zone, determine whether or not a given datetime - would fall in a gap. - - :param dt: - A :class:`datetime.datetime` (whose time zone will be ignored if ``tz`` - is provided.) - - :param tz: - A :class:`datetime.tzinfo` with support for the ``fold`` attribute. If - ``None`` or not provided, the datetime's own time zone will be used. - - :return: - Returns a boolean value whether or not the "wall time" exists in - ``tz``. - - .. versionadded:: 2.7.0 - """ - if tz is None: - if dt.tzinfo is None: - raise ValueError('Datetime is naive and no time zone provided.') - tz = dt.tzinfo - - dt = dt.replace(tzinfo=None) - - # This is essentially a test of whether or not the datetime can survive - # a round trip to UTC. - dt_rt = dt.replace(tzinfo=tz).astimezone(UTC).astimezone(tz) - dt_rt = dt_rt.replace(tzinfo=None) - - return dt == dt_rt - - -def datetime_ambiguous(dt, tz=None): - """ - Given a datetime and a time zone, determine whether or not a given datetime - is ambiguous (i.e if there are two times differentiated only by their DST - status). - - :param dt: - A :class:`datetime.datetime` (whose time zone will be ignored if ``tz`` - is provided.) - - :param tz: - A :class:`datetime.tzinfo` with support for the ``fold`` attribute. If - ``None`` or not provided, the datetime's own time zone will be used. - - :return: - Returns a boolean value whether or not the "wall time" is ambiguous in - ``tz``. - - .. versionadded:: 2.6.0 - """ - if tz is None: - if dt.tzinfo is None: - raise ValueError('Datetime is naive and no time zone provided.') - - tz = dt.tzinfo - - # If a time zone defines its own "is_ambiguous" function, we'll use that. - is_ambiguous_fn = getattr(tz, 'is_ambiguous', None) - if is_ambiguous_fn is not None: - try: - return tz.is_ambiguous(dt) - except Exception: - pass - - # If it doesn't come out and tell us it's ambiguous, we'll just check if - # the fold attribute has any effect on this particular date and time. - dt = dt.replace(tzinfo=tz) - wall_0 = enfold(dt, fold=0) - wall_1 = enfold(dt, fold=1) - - same_offset = wall_0.utcoffset() == wall_1.utcoffset() - same_dst = wall_0.dst() == wall_1.dst() - - return not (same_offset and same_dst) - - -def resolve_imaginary(dt): - """ - Given a datetime that may be imaginary, return an existing datetime. - - This function assumes that an imaginary datetime represents what the - wall time would be in a zone had the offset transition not occurred, so - it will always fall forward by the transition's change in offset. - - .. doctest:: - - >>> from dateutil import tz - >>> from datetime import datetime - >>> NYC = tz.gettz('America/New_York') - >>> print(tz.resolve_imaginary(datetime(2017, 3, 12, 2, 30, tzinfo=NYC))) - 2017-03-12 03:30:00-04:00 - - >>> KIR = tz.gettz('Pacific/Kiritimati') - >>> print(tz.resolve_imaginary(datetime(1995, 1, 1, 12, 30, tzinfo=KIR))) - 1995-01-02 12:30:00+14:00 - - As a note, :func:`datetime.astimezone` is guaranteed to produce a valid, - existing datetime, so a round-trip to and from UTC is sufficient to get - an extant datetime, however, this generally "falls back" to an earlier time - rather than falling forward to the STD side (though no guarantees are made - about this behavior). - - :param dt: - A :class:`datetime.datetime` which may or may not exist. - - :return: - Returns an existing :class:`datetime.datetime`. If ``dt`` was not - imaginary, the datetime returned is guaranteed to be the same object - passed to the function. - - .. versionadded:: 2.7.0 - """ - if dt.tzinfo is not None and not datetime_exists(dt): - - curr_offset = (dt + datetime.timedelta(hours=24)).utcoffset() - old_offset = (dt - datetime.timedelta(hours=24)).utcoffset() - - dt += curr_offset - old_offset - - return dt - - -def _datetime_to_timestamp(dt): - """ - Convert a :class:`datetime.datetime` object to an epoch timestamp in - seconds since January 1, 1970, ignoring the time zone. - """ - return (dt.replace(tzinfo=None) - EPOCH).total_seconds() - - -if sys.version_info >= (3, 6): - def _get_supported_offset(second_offset): - return second_offset -else: - def _get_supported_offset(second_offset): - # For python pre-3.6, round to full-minutes if that's not the case. - # Python's datetime doesn't accept sub-minute timezones. Check - # http://python.org/sf/1447945 or https://bugs.python.org/issue5288 - # for some information. - old_offset = second_offset - calculated_offset = 60 * ((second_offset + 30) // 60) - return calculated_offset - - -try: - # Python 3.7 feature - from contextlib import nullcontext as _nullcontext -except ImportError: - class _nullcontext(object): - """ - Class for wrapping contexts so that they are passed through in a - with statement. - """ - def __init__(self, context): - self.context = context - - def __enter__(self): - return self.context - - def __exit__(*args, **kwargs): - pass - -# vim:ts=4:sw=4:et diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/win.py b/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/win.py deleted file mode 100644 index cde07ba..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tz/win.py +++ /dev/null @@ -1,370 +0,0 @@ -# -*- coding: utf-8 -*- -""" -This module provides an interface to the native time zone data on Windows, -including :py:class:`datetime.tzinfo` implementations. - -Attempting to import this module on a non-Windows platform will raise an -:py:obj:`ImportError`. -""" -# This code was originally contributed by Jeffrey Harris. -import datetime -import struct - -from six.moves import winreg -from six import text_type - -try: - import ctypes - from ctypes import wintypes -except ValueError: - # ValueError is raised on non-Windows systems for some horrible reason. - raise ImportError("Running tzwin on non-Windows system") - -from ._common import tzrangebase - -__all__ = ["tzwin", "tzwinlocal", "tzres"] - -ONEWEEK = datetime.timedelta(7) - -TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones" -TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones" -TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation" - - -def _settzkeyname(): - handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) - try: - winreg.OpenKey(handle, TZKEYNAMENT).Close() - TZKEYNAME = TZKEYNAMENT - except WindowsError: - TZKEYNAME = TZKEYNAME9X - handle.Close() - return TZKEYNAME - - -TZKEYNAME = _settzkeyname() - - -class tzres(object): - """ - Class for accessing ``tzres.dll``, which contains timezone name related - resources. - - .. versionadded:: 2.5.0 - """ - p_wchar = ctypes.POINTER(wintypes.WCHAR) # Pointer to a wide char - - def __init__(self, tzres_loc='tzres.dll'): - # Load the user32 DLL so we can load strings from tzres - user32 = ctypes.WinDLL('user32') - - # Specify the LoadStringW function - user32.LoadStringW.argtypes = (wintypes.HINSTANCE, - wintypes.UINT, - wintypes.LPWSTR, - ctypes.c_int) - - self.LoadStringW = user32.LoadStringW - self._tzres = ctypes.WinDLL(tzres_loc) - self.tzres_loc = tzres_loc - - def load_name(self, offset): - """ - Load a timezone name from a DLL offset (integer). - - >>> from dateutil.tzwin import tzres - >>> tzr = tzres() - >>> print(tzr.load_name(112)) - 'Eastern Standard Time' - - :param offset: - A positive integer value referring to a string from the tzres dll. - - .. note:: - - Offsets found in the registry are generally of the form - ``@tzres.dll,-114``. The offset in this case is 114, not -114. - - """ - resource = self.p_wchar() - lpBuffer = ctypes.cast(ctypes.byref(resource), wintypes.LPWSTR) - nchar = self.LoadStringW(self._tzres._handle, offset, lpBuffer, 0) - return resource[:nchar] - - def name_from_string(self, tzname_str): - """ - Parse strings as returned from the Windows registry into the time zone - name as defined in the registry. - - >>> from dateutil.tzwin import tzres - >>> tzr = tzres() - >>> print(tzr.name_from_string('@tzres.dll,-251')) - 'Dateline Daylight Time' - >>> print(tzr.name_from_string('Eastern Standard Time')) - 'Eastern Standard Time' - - :param tzname_str: - A timezone name string as returned from a Windows registry key. - - :return: - Returns the localized timezone string from tzres.dll if the string - is of the form `@tzres.dll,-offset`, else returns the input string. - """ - if not tzname_str.startswith('@'): - return tzname_str - - name_splt = tzname_str.split(',-') - try: - offset = int(name_splt[1]) - except: - raise ValueError("Malformed timezone string.") - - return self.load_name(offset) - - -class tzwinbase(tzrangebase): - """tzinfo class based on win32's timezones available in the registry.""" - def __init__(self): - raise NotImplementedError('tzwinbase is an abstract base class') - - def __eq__(self, other): - # Compare on all relevant dimensions, including name. - if not isinstance(other, tzwinbase): - return NotImplemented - - return (self._std_offset == other._std_offset and - self._dst_offset == other._dst_offset and - self._stddayofweek == other._stddayofweek and - self._dstdayofweek == other._dstdayofweek and - self._stdweeknumber == other._stdweeknumber and - self._dstweeknumber == other._dstweeknumber and - self._stdhour == other._stdhour and - self._dsthour == other._dsthour and - self._stdminute == other._stdminute and - self._dstminute == other._dstminute and - self._std_abbr == other._std_abbr and - self._dst_abbr == other._dst_abbr) - - @staticmethod - def list(): - """Return a list of all time zones known to the system.""" - with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: - with winreg.OpenKey(handle, TZKEYNAME) as tzkey: - result = [winreg.EnumKey(tzkey, i) - for i in range(winreg.QueryInfoKey(tzkey)[0])] - return result - - def display(self): - """ - Return the display name of the time zone. - """ - return self._display - - def transitions(self, year): - """ - For a given year, get the DST on and off transition times, expressed - always on the standard time side. For zones with no transitions, this - function returns ``None``. - - :param year: - The year whose transitions you would like to query. - - :return: - Returns a :class:`tuple` of :class:`datetime.datetime` objects, - ``(dston, dstoff)`` for zones with an annual DST transition, or - ``None`` for fixed offset zones. - """ - - if not self.hasdst: - return None - - dston = picknthweekday(year, self._dstmonth, self._dstdayofweek, - self._dsthour, self._dstminute, - self._dstweeknumber) - - dstoff = picknthweekday(year, self._stdmonth, self._stddayofweek, - self._stdhour, self._stdminute, - self._stdweeknumber) - - # Ambiguous dates default to the STD side - dstoff -= self._dst_base_offset - - return dston, dstoff - - def _get_hasdst(self): - return self._dstmonth != 0 - - @property - def _dst_base_offset(self): - return self._dst_base_offset_ - - -class tzwin(tzwinbase): - """ - Time zone object created from the zone info in the Windows registry - - These are similar to :py:class:`dateutil.tz.tzrange` objects in that - the time zone data is provided in the format of a single offset rule - for either 0 or 2 time zone transitions per year. - - :param: name - The name of a Windows time zone key, e.g. "Eastern Standard Time". - The full list of keys can be retrieved with :func:`tzwin.list`. - """ - - def __init__(self, name): - self._name = name - - with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: - tzkeyname = text_type("{kn}\\{name}").format(kn=TZKEYNAME, name=name) - with winreg.OpenKey(handle, tzkeyname) as tzkey: - keydict = valuestodict(tzkey) - - self._std_abbr = keydict["Std"] - self._dst_abbr = keydict["Dlt"] - - self._display = keydict["Display"] - - # See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm - tup = struct.unpack("=3l16h", keydict["TZI"]) - stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1 - dstoffset = stdoffset-tup[2] # + DaylightBias * -1 - self._std_offset = datetime.timedelta(minutes=stdoffset) - self._dst_offset = datetime.timedelta(minutes=dstoffset) - - # for the meaning see the win32 TIME_ZONE_INFORMATION structure docs - # http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx - (self._stdmonth, - self._stddayofweek, # Sunday = 0 - self._stdweeknumber, # Last = 5 - self._stdhour, - self._stdminute) = tup[4:9] - - (self._dstmonth, - self._dstdayofweek, # Sunday = 0 - self._dstweeknumber, # Last = 5 - self._dsthour, - self._dstminute) = tup[12:17] - - self._dst_base_offset_ = self._dst_offset - self._std_offset - self.hasdst = self._get_hasdst() - - def __repr__(self): - return "tzwin(%s)" % repr(self._name) - - def __reduce__(self): - return (self.__class__, (self._name,)) - - -class tzwinlocal(tzwinbase): - """ - Class representing the local time zone information in the Windows registry - - While :class:`dateutil.tz.tzlocal` makes system calls (via the :mod:`time` - module) to retrieve time zone information, ``tzwinlocal`` retrieves the - rules directly from the Windows registry and creates an object like - :class:`dateutil.tz.tzwin`. - - Because Windows does not have an equivalent of :func:`time.tzset`, on - Windows, :class:`dateutil.tz.tzlocal` instances will always reflect the - time zone settings *at the time that the process was started*, meaning - changes to the machine's time zone settings during the run of a program - on Windows will **not** be reflected by :class:`dateutil.tz.tzlocal`. - Because ``tzwinlocal`` reads the registry directly, it is unaffected by - this issue. - """ - def __init__(self): - with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle: - with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey: - keydict = valuestodict(tzlocalkey) - - self._std_abbr = keydict["StandardName"] - self._dst_abbr = keydict["DaylightName"] - - try: - tzkeyname = text_type('{kn}\\{sn}').format(kn=TZKEYNAME, - sn=self._std_abbr) - with winreg.OpenKey(handle, tzkeyname) as tzkey: - _keydict = valuestodict(tzkey) - self._display = _keydict["Display"] - except OSError: - self._display = None - - stdoffset = -keydict["Bias"]-keydict["StandardBias"] - dstoffset = stdoffset-keydict["DaylightBias"] - - self._std_offset = datetime.timedelta(minutes=stdoffset) - self._dst_offset = datetime.timedelta(minutes=dstoffset) - - # For reasons unclear, in this particular key, the day of week has been - # moved to the END of the SYSTEMTIME structure. - tup = struct.unpack("=8h", keydict["StandardStart"]) - - (self._stdmonth, - self._stdweeknumber, # Last = 5 - self._stdhour, - self._stdminute) = tup[1:5] - - self._stddayofweek = tup[7] - - tup = struct.unpack("=8h", keydict["DaylightStart"]) - - (self._dstmonth, - self._dstweeknumber, # Last = 5 - self._dsthour, - self._dstminute) = tup[1:5] - - self._dstdayofweek = tup[7] - - self._dst_base_offset_ = self._dst_offset - self._std_offset - self.hasdst = self._get_hasdst() - - def __repr__(self): - return "tzwinlocal()" - - def __str__(self): - # str will return the standard name, not the daylight name. - return "tzwinlocal(%s)" % repr(self._std_abbr) - - def __reduce__(self): - return (self.__class__, ()) - - -def picknthweekday(year, month, dayofweek, hour, minute, whichweek): - """ dayofweek == 0 means Sunday, whichweek 5 means last instance """ - first = datetime.datetime(year, month, 1, hour, minute) - - # This will work if dayofweek is ISO weekday (1-7) or Microsoft-style (0-6), - # Because 7 % 7 = 0 - weekdayone = first.replace(day=((dayofweek - first.isoweekday()) % 7) + 1) - wd = weekdayone + ((whichweek - 1) * ONEWEEK) - if (wd.month != month): - wd -= ONEWEEK - - return wd - - -def valuestodict(key): - """Convert a registry key's values to a dictionary.""" - dout = {} - size = winreg.QueryInfoKey(key)[1] - tz_res = None - - for i in range(size): - key_name, value, dtype = winreg.EnumValue(key, i) - if dtype == winreg.REG_DWORD or dtype == winreg.REG_DWORD_LITTLE_ENDIAN: - # If it's a DWORD (32-bit integer), it's stored as unsigned - convert - # that to a proper signed integer - if value & (1 << 31): - value = value - (1 << 32) - elif dtype == winreg.REG_SZ: - # If it's a reference to the tzres DLL, load the actual string - if value.startswith('@tzres'): - tz_res = tz_res or tzres() - value = tz_res.name_from_string(value) - - value = value.rstrip('\x00') # Remove trailing nulls - - dout[key_name] = value - - return dout diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tzwin.py b/plotter-app/venv/lib/python3.8/site-packages/dateutil/tzwin.py deleted file mode 100644 index cebc673..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/dateutil/tzwin.py +++ /dev/null @@ -1,2 +0,0 @@ -# tzwin has moved to dateutil.tz.win -from .tz.win import * diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/utils.py b/plotter-app/venv/lib/python3.8/site-packages/dateutil/utils.py deleted file mode 100644 index dd2d245..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/dateutil/utils.py +++ /dev/null @@ -1,71 +0,0 @@ -# -*- coding: utf-8 -*- -""" -This module offers general convenience and utility functions for dealing with -datetimes. - -.. versionadded:: 2.7.0 -""" -from __future__ import unicode_literals - -from datetime import datetime, time - - -def today(tzinfo=None): - """ - Returns a :py:class:`datetime` representing the current day at midnight - - :param tzinfo: - The time zone to attach (also used to determine the current day). - - :return: - A :py:class:`datetime.datetime` object representing the current day - at midnight. - """ - - dt = datetime.now(tzinfo) - return datetime.combine(dt.date(), time(0, tzinfo=tzinfo)) - - -def default_tzinfo(dt, tzinfo): - """ - Sets the ``tzinfo`` parameter on naive datetimes only - - This is useful for example when you are provided a datetime that may have - either an implicit or explicit time zone, such as when parsing a time zone - string. - - .. doctest:: - - >>> from dateutil.tz import tzoffset - >>> from dateutil.parser import parse - >>> from dateutil.utils import default_tzinfo - >>> dflt_tz = tzoffset("EST", -18000) - >>> print(default_tzinfo(parse('2014-01-01 12:30 UTC'), dflt_tz)) - 2014-01-01 12:30:00+00:00 - >>> print(default_tzinfo(parse('2014-01-01 12:30'), dflt_tz)) - 2014-01-01 12:30:00-05:00 - - :param dt: - The datetime on which to replace the time zone - - :param tzinfo: - The :py:class:`datetime.tzinfo` subclass instance to assign to - ``dt`` if (and only if) it is naive. - - :return: - Returns an aware :py:class:`datetime.datetime`. - """ - if dt.tzinfo is not None: - return dt - else: - return dt.replace(tzinfo=tzinfo) - - -def within_delta(dt1, dt2, delta): - """ - Useful for comparing two datetimes that may have a negligible difference - to be considered equal. - """ - delta = abs(delta) - difference = dt1 - dt2 - return -delta <= difference <= delta diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/zoneinfo/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/dateutil/zoneinfo/__init__.py deleted file mode 100644 index 34f11ad..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/dateutil/zoneinfo/__init__.py +++ /dev/null @@ -1,167 +0,0 @@ -# -*- coding: utf-8 -*- -import warnings -import json - -from tarfile import TarFile -from pkgutil import get_data -from io import BytesIO - -from dateutil.tz import tzfile as _tzfile - -__all__ = ["get_zonefile_instance", "gettz", "gettz_db_metadata"] - -ZONEFILENAME = "dateutil-zoneinfo.tar.gz" -METADATA_FN = 'METADATA' - - -class tzfile(_tzfile): - def __reduce__(self): - return (gettz, (self._filename,)) - - -def getzoneinfofile_stream(): - try: - return BytesIO(get_data(__name__, ZONEFILENAME)) - except IOError as e: # TODO switch to FileNotFoundError? - warnings.warn("I/O error({0}): {1}".format(e.errno, e.strerror)) - return None - - -class ZoneInfoFile(object): - def __init__(self, zonefile_stream=None): - if zonefile_stream is not None: - with TarFile.open(fileobj=zonefile_stream) as tf: - self.zones = {zf.name: tzfile(tf.extractfile(zf), filename=zf.name) - for zf in tf.getmembers() - if zf.isfile() and zf.name != METADATA_FN} - # deal with links: They'll point to their parent object. Less - # waste of memory - links = {zl.name: self.zones[zl.linkname] - for zl in tf.getmembers() if - zl.islnk() or zl.issym()} - self.zones.update(links) - try: - metadata_json = tf.extractfile(tf.getmember(METADATA_FN)) - metadata_str = metadata_json.read().decode('UTF-8') - self.metadata = json.loads(metadata_str) - except KeyError: - # no metadata in tar file - self.metadata = None - else: - self.zones = {} - self.metadata = None - - def get(self, name, default=None): - """ - Wrapper for :func:`ZoneInfoFile.zones.get`. This is a convenience method - for retrieving zones from the zone dictionary. - - :param name: - The name of the zone to retrieve. (Generally IANA zone names) - - :param default: - The value to return in the event of a missing key. - - .. versionadded:: 2.6.0 - - """ - return self.zones.get(name, default) - - -# The current API has gettz as a module function, although in fact it taps into -# a stateful class. So as a workaround for now, without changing the API, we -# will create a new "global" class instance the first time a user requests a -# timezone. Ugly, but adheres to the api. -# -# TODO: Remove after deprecation period. -_CLASS_ZONE_INSTANCE = [] - - -def get_zonefile_instance(new_instance=False): - """ - This is a convenience function which provides a :class:`ZoneInfoFile` - instance using the data provided by the ``dateutil`` package. By default, it - caches a single instance of the ZoneInfoFile object and returns that. - - :param new_instance: - If ``True``, a new instance of :class:`ZoneInfoFile` is instantiated and - used as the cached instance for the next call. Otherwise, new instances - are created only as necessary. - - :return: - Returns a :class:`ZoneInfoFile` object. - - .. versionadded:: 2.6 - """ - if new_instance: - zif = None - else: - zif = getattr(get_zonefile_instance, '_cached_instance', None) - - if zif is None: - zif = ZoneInfoFile(getzoneinfofile_stream()) - - get_zonefile_instance._cached_instance = zif - - return zif - - -def gettz(name): - """ - This retrieves a time zone from the local zoneinfo tarball that is packaged - with dateutil. - - :param name: - An IANA-style time zone name, as found in the zoneinfo file. - - :return: - Returns a :class:`dateutil.tz.tzfile` time zone object. - - .. warning:: - It is generally inadvisable to use this function, and it is only - provided for API compatibility with earlier versions. This is *not* - equivalent to ``dateutil.tz.gettz()``, which selects an appropriate - time zone based on the inputs, favoring system zoneinfo. This is ONLY - for accessing the dateutil-specific zoneinfo (which may be out of - date compared to the system zoneinfo). - - .. deprecated:: 2.6 - If you need to use a specific zoneinfofile over the system zoneinfo, - instantiate a :class:`dateutil.zoneinfo.ZoneInfoFile` object and call - :func:`dateutil.zoneinfo.ZoneInfoFile.get(name)` instead. - - Use :func:`get_zonefile_instance` to retrieve an instance of the - dateutil-provided zoneinfo. - """ - warnings.warn("zoneinfo.gettz() will be removed in future versions, " - "to use the dateutil-provided zoneinfo files, instantiate a " - "ZoneInfoFile object and use ZoneInfoFile.zones.get() " - "instead. See the documentation for details.", - DeprecationWarning) - - if len(_CLASS_ZONE_INSTANCE) == 0: - _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream())) - return _CLASS_ZONE_INSTANCE[0].zones.get(name) - - -def gettz_db_metadata(): - """ Get the zonefile metadata - - See `zonefile_metadata`_ - - :returns: - A dictionary with the database metadata - - .. deprecated:: 2.6 - See deprecation warning in :func:`zoneinfo.gettz`. To get metadata, - query the attribute ``zoneinfo.ZoneInfoFile.metadata``. - """ - warnings.warn("zoneinfo.gettz_db_metadata() will be removed in future " - "versions, to use the dateutil-provided zoneinfo files, " - "ZoneInfoFile object and query the 'metadata' attribute " - "instead. See the documentation for details.", - DeprecationWarning) - - if len(_CLASS_ZONE_INSTANCE) == 0: - _CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream())) - return _CLASS_ZONE_INSTANCE[0].metadata diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/zoneinfo/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/dateutil/zoneinfo/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index ed778e5..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/dateutil/zoneinfo/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/zoneinfo/__pycache__/rebuild.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/dateutil/zoneinfo/__pycache__/rebuild.cpython-38.pyc deleted file mode 100644 index 3c13af4..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/dateutil/zoneinfo/__pycache__/rebuild.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz b/plotter-app/venv/lib/python3.8/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz deleted file mode 100644 index 1461f8c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/dateutil/zoneinfo/rebuild.py b/plotter-app/venv/lib/python3.8/site-packages/dateutil/zoneinfo/rebuild.py deleted file mode 100644 index 684c658..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/dateutil/zoneinfo/rebuild.py +++ /dev/null @@ -1,75 +0,0 @@ -import logging -import os -import tempfile -import shutil -import json -from subprocess import check_call, check_output -from tarfile import TarFile - -from dateutil.zoneinfo import METADATA_FN, ZONEFILENAME - - -def rebuild(filename, tag=None, format="gz", zonegroups=[], metadata=None): - """Rebuild the internal timezone info in dateutil/zoneinfo/zoneinfo*tar* - - filename is the timezone tarball from ``ftp.iana.org/tz``. - - """ - tmpdir = tempfile.mkdtemp() - zonedir = os.path.join(tmpdir, "zoneinfo") - moduledir = os.path.dirname(__file__) - try: - with TarFile.open(filename) as tf: - for name in zonegroups: - tf.extract(name, tmpdir) - filepaths = [os.path.join(tmpdir, n) for n in zonegroups] - - _run_zic(zonedir, filepaths) - - # write metadata file - with open(os.path.join(zonedir, METADATA_FN), 'w') as f: - json.dump(metadata, f, indent=4, sort_keys=True) - target = os.path.join(moduledir, ZONEFILENAME) - with TarFile.open(target, "w:%s" % format) as tf: - for entry in os.listdir(zonedir): - entrypath = os.path.join(zonedir, entry) - tf.add(entrypath, entry) - finally: - shutil.rmtree(tmpdir) - - -def _run_zic(zonedir, filepaths): - """Calls the ``zic`` compiler in a compatible way to get a "fat" binary. - - Recent versions of ``zic`` default to ``-b slim``, while older versions - don't even have the ``-b`` option (but default to "fat" binaries). The - current version of dateutil does not support Version 2+ TZif files, which - causes problems when used in conjunction with "slim" binaries, so this - function is used to ensure that we always get a "fat" binary. - """ - - try: - help_text = check_output(["zic", "--help"]) - except OSError as e: - _print_on_nosuchfile(e) - raise - - if b"-b " in help_text: - bloat_args = ["-b", "fat"] - else: - bloat_args = [] - - check_call(["zic"] + bloat_args + ["-d", zonedir] + filepaths) - - -def _print_on_nosuchfile(e): - """Print helpful troubleshooting message - - e is an exception raised by subprocess.check_call() - - """ - if e.errno == 2: - logging.error( - "Could not find zic. Perhaps you need to install " - "libc-bin or some other package that provides it, " - "or it's not in your PATH?") diff --git a/plotter-app/venv/lib/python3.8/site-packages/easy_install.py b/plotter-app/venv/lib/python3.8/site-packages/easy_install.py deleted file mode 100644 index d87e984..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/easy_install.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Run the EasyInstall command""" - -if __name__ == '__main__': - from setuptools.command.easy_install import main - main() diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask-3.0.3.dist-info/INSTALLER b/plotter-app/venv/lib/python3.8/site-packages/flask-3.0.3.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask-3.0.3.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask-3.0.3.dist-info/LICENSE.txt b/plotter-app/venv/lib/python3.8/site-packages/flask-3.0.3.dist-info/LICENSE.txt deleted file mode 100644 index 9d227a0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask-3.0.3.dist-info/LICENSE.txt +++ /dev/null @@ -1,28 +0,0 @@ -Copyright 2010 Pallets - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask-3.0.3.dist-info/METADATA b/plotter-app/venv/lib/python3.8/site-packages/flask-3.0.3.dist-info/METADATA deleted file mode 100644 index 5a02107..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask-3.0.3.dist-info/METADATA +++ /dev/null @@ -1,101 +0,0 @@ -Metadata-Version: 2.1 -Name: Flask -Version: 3.0.3 -Summary: A simple framework for building complex web applications. -Maintainer-email: Pallets -Requires-Python: >=3.8 -Description-Content-Type: text/markdown -Classifier: Development Status :: 5 - Production/Stable -Classifier: Environment :: Web Environment -Classifier: Framework :: Flask -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 :: Internet :: WWW/HTTP :: WSGI -Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application -Classifier: Topic :: Software Development :: Libraries :: Application Frameworks -Classifier: Typing :: Typed -Requires-Dist: Werkzeug>=3.0.0 -Requires-Dist: Jinja2>=3.1.2 -Requires-Dist: itsdangerous>=2.1.2 -Requires-Dist: click>=8.1.3 -Requires-Dist: blinker>=1.6.2 -Requires-Dist: importlib-metadata>=3.6.0; python_version < '3.10' -Requires-Dist: asgiref>=3.2 ; extra == "async" -Requires-Dist: python-dotenv ; extra == "dotenv" -Project-URL: Changes, https://flask.palletsprojects.com/changes/ -Project-URL: Chat, https://discord.gg/pallets -Project-URL: Documentation, https://flask.palletsprojects.com/ -Project-URL: Donate, https://palletsprojects.com/donate -Project-URL: Source, https://github.com/pallets/flask/ -Provides-Extra: async -Provides-Extra: dotenv - -# Flask - -Flask is a lightweight [WSGI][] web application framework. It is designed -to make getting started quick and easy, with the ability to scale up to -complex applications. It began as a simple wrapper around [Werkzeug][] -and [Jinja][], and has become one of the most popular Python web -application frameworks. - -Flask offers suggestions, but doesn't enforce any dependencies or -project layout. It is up to the developer to choose the tools and -libraries they want to use. There are many extensions provided by the -community that make adding new functionality easy. - -[WSGI]: https://wsgi.readthedocs.io/ -[Werkzeug]: https://werkzeug.palletsprojects.com/ -[Jinja]: https://jinja.palletsprojects.com/ - - -## Installing - -Install and update from [PyPI][] using an installer such as [pip][]: - -``` -$ pip install -U Flask -``` - -[PyPI]: https://pypi.org/project/Flask/ -[pip]: https://pip.pypa.io/en/stable/getting-started/ - - -## A Simple Example - -```python -# save this as app.py -from flask import Flask - -app = Flask(__name__) - -@app.route("/") -def hello(): - return "Hello, World!" -``` - -``` -$ flask run - * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) -``` - - -## Contributing - -For guidance on setting up a development environment and how to make a -contribution to Flask, see the [contributing guidelines][]. - -[contributing guidelines]: https://github.com/pallets/flask/blob/main/CONTRIBUTING.rst - - -## Donate - -The Pallets organization develops and supports Flask and the libraries -it uses. In order to grow the community of contributors and users, and -allow the maintainers to devote more time to the projects, [please -donate today][]. - -[please donate today]: https://palletsprojects.com/donate - diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask-3.0.3.dist-info/RECORD b/plotter-app/venv/lib/python3.8/site-packages/flask-3.0.3.dist-info/RECORD deleted file mode 100644 index 18b6d47..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask-3.0.3.dist-info/RECORD +++ /dev/null @@ -1,57 +0,0 @@ -../../../bin/flask,sha256=nE1jHHpqk8m2ZB07IHfBHZ51PCAoXcEwEKaOGgQGNGs,264 -flask-3.0.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -flask-3.0.3.dist-info/LICENSE.txt,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 -flask-3.0.3.dist-info/METADATA,sha256=exPahy4aahjV-mYqd9qb5HNP8haB_IxTuaotoSvCtag,3177 -flask-3.0.3.dist-info/RECORD,, -flask-3.0.3.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 -flask-3.0.3.dist-info/entry_points.txt,sha256=bBP7hTOS5fz9zLtC7sPofBZAlMkEvBxu7KqS6l5lvc4,40 -flask/__init__.py,sha256=6xMqdVA0FIQ2U1KVaGX3lzNCdXPzoHPaa0hvQCNcfSk,2625 -flask/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30 -flask/__pycache__/__init__.cpython-38.pyc,, -flask/__pycache__/__main__.cpython-38.pyc,, -flask/__pycache__/app.cpython-38.pyc,, -flask/__pycache__/blueprints.cpython-38.pyc,, -flask/__pycache__/cli.cpython-38.pyc,, -flask/__pycache__/config.cpython-38.pyc,, -flask/__pycache__/ctx.cpython-38.pyc,, -flask/__pycache__/debughelpers.cpython-38.pyc,, -flask/__pycache__/globals.cpython-38.pyc,, -flask/__pycache__/helpers.cpython-38.pyc,, -flask/__pycache__/logging.cpython-38.pyc,, -flask/__pycache__/sessions.cpython-38.pyc,, -flask/__pycache__/signals.cpython-38.pyc,, -flask/__pycache__/templating.cpython-38.pyc,, -flask/__pycache__/testing.cpython-38.pyc,, -flask/__pycache__/typing.cpython-38.pyc,, -flask/__pycache__/views.cpython-38.pyc,, -flask/__pycache__/wrappers.cpython-38.pyc,, -flask/app.py,sha256=7-lh6cIj27riTE1Q18Ok1p5nOZ8qYiMux4Btc6o6mNc,60143 -flask/blueprints.py,sha256=7INXPwTkUxfOQXOOv1yu52NpHPmPGI5fMTMFZ-BG9yY,4430 -flask/cli.py,sha256=OOaf_Efqih1i2in58j-5ZZZmQnPpaSfiUFbEjlL9bzw,35825 -flask/config.py,sha256=bLzLVAj-cq-Xotu9erqOFte0xSFaVXyfz0AkP4GbwmY,13312 -flask/ctx.py,sha256=4atDhJJ_cpV1VMq4qsfU4E_61M1oN93jlS2H9gjrl58,15120 -flask/debughelpers.py,sha256=PGIDhStW_efRjpaa3zHIpo-htStJOR41Ip3OJWPYBwo,6080 -flask/globals.py,sha256=XdQZmStBmPIs8t93tjx6pO7Bm3gobAaONWkFcUHaGas,1713 -flask/helpers.py,sha256=tYrcQ_73GuSZVEgwFr-eMmV69UriFQDBmt8wZJIAqvg,23084 -flask/json/__init__.py,sha256=hLNR898paqoefdeAhraa5wyJy-bmRB2k2dV4EgVy2Z8,5602 -flask/json/__pycache__/__init__.cpython-38.pyc,, -flask/json/__pycache__/provider.cpython-38.pyc,, -flask/json/__pycache__/tag.cpython-38.pyc,, -flask/json/provider.py,sha256=q6iB83lSiopy80DZPrU-9mGcWwrD0mvLjiv9fHrRZgc,7646 -flask/json/tag.py,sha256=DhaNwuIOhdt2R74oOC9Y4Z8ZprxFYiRb5dUP5byyINw,9281 -flask/logging.py,sha256=8sM3WMTubi1cBb2c_lPkWpN0J8dMAqrgKRYLLi1dCVI,2377 -flask/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -flask/sansio/README.md,sha256=-0X1tECnilmz1cogx-YhNw5d7guK7GKrq_DEV2OzlU0,228 -flask/sansio/__pycache__/app.cpython-38.pyc,, -flask/sansio/__pycache__/blueprints.cpython-38.pyc,, -flask/sansio/__pycache__/scaffold.cpython-38.pyc,, -flask/sansio/app.py,sha256=YG5Gf7JVf1c0yccWDZ86q5VSfJUidOVp27HFxFNxC7U,38053 -flask/sansio/blueprints.py,sha256=Tqe-7EkZ-tbWchm8iDoCfD848f0_3nLv6NNjeIPvHwM,24637 -flask/sansio/scaffold.py,sha256=WLV9TRQMMhGlXz-1OKtQ3lv6mtIBQZxdW2HezYrGxoI,30633 -flask/sessions.py,sha256=RU4lzm9MQW9CtH8rVLRTDm8USMJyT4LbvYe7sxM2__k,14807 -flask/signals.py,sha256=V7lMUww7CqgJ2ThUBn1PiatZtQanOyt7OZpu2GZI-34,750 -flask/templating.py,sha256=2TcXLT85Asflm2W9WOSFxKCmYn5e49w_Jkg9-NaaJWo,7537 -flask/testing.py,sha256=3BFXb3bP7R5r-XLBuobhczbxDu8-1LWRzYuhbr-lwaE,10163 -flask/typing.py,sha256=ZavK-wV28Yv8CQB7u73qZp_jLalpbWdrXS37QR1ftN0,3190 -flask/views.py,sha256=B66bTvYBBcHMYk4dA1ScZD0oTRTBl0I5smp1lRm9riI,6939 -flask/wrappers.py,sha256=m1j5tIJxIu8_sPPgTAB_G4TTh52Q-HoDuw_qHV5J59g,5831 diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask-3.0.3.dist-info/WHEEL b/plotter-app/venv/lib/python3.8/site-packages/flask-3.0.3.dist-info/WHEEL deleted file mode 100644 index 3b5e64b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask-3.0.3.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: flit 3.9.0 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask-3.0.3.dist-info/entry_points.txt b/plotter-app/venv/lib/python3.8/site-packages/flask-3.0.3.dist-info/entry_points.txt deleted file mode 100644 index eec6733..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask-3.0.3.dist-info/entry_points.txt +++ /dev/null @@ -1,3 +0,0 @@ -[console_scripts] -flask=flask.cli:main - diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/flask/__init__.py deleted file mode 100644 index e86eb43..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/__init__.py +++ /dev/null @@ -1,60 +0,0 @@ -from __future__ import annotations - -import typing as t - -from . import json as json -from .app import Flask as Flask -from .blueprints import Blueprint as Blueprint -from .config import Config as Config -from .ctx import after_this_request as after_this_request -from .ctx import copy_current_request_context as copy_current_request_context -from .ctx import has_app_context as has_app_context -from .ctx import has_request_context as has_request_context -from .globals import current_app as current_app -from .globals import g as g -from .globals import request as request -from .globals import session as session -from .helpers import abort as abort -from .helpers import flash as flash -from .helpers import get_flashed_messages as get_flashed_messages -from .helpers import get_template_attribute as get_template_attribute -from .helpers import make_response as make_response -from .helpers import redirect as redirect -from .helpers import send_file as send_file -from .helpers import send_from_directory as send_from_directory -from .helpers import stream_with_context as stream_with_context -from .helpers import url_for as url_for -from .json import jsonify as jsonify -from .signals import appcontext_popped as appcontext_popped -from .signals import appcontext_pushed as appcontext_pushed -from .signals import appcontext_tearing_down as appcontext_tearing_down -from .signals import before_render_template as before_render_template -from .signals import got_request_exception as got_request_exception -from .signals import message_flashed as message_flashed -from .signals import request_finished as request_finished -from .signals import request_started as request_started -from .signals import request_tearing_down as request_tearing_down -from .signals import template_rendered as template_rendered -from .templating import render_template as render_template -from .templating import render_template_string as render_template_string -from .templating import stream_template as stream_template -from .templating import stream_template_string as stream_template_string -from .wrappers import Request as Request -from .wrappers import Response as Response - - -def __getattr__(name: str) -> t.Any: - if name == "__version__": - import importlib.metadata - import warnings - - warnings.warn( - "The '__version__' attribute is deprecated and will be removed in" - " Flask 3.1. Use feature detection or" - " 'importlib.metadata.version(\"flask\")' instead.", - DeprecationWarning, - stacklevel=2, - ) - return importlib.metadata.version("flask") - - raise AttributeError(name) diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/__main__.py b/plotter-app/venv/lib/python3.8/site-packages/flask/__main__.py deleted file mode 100644 index 4e28416..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/__main__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .cli import main - -main() diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index fcc0518..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/__main__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/__main__.cpython-38.pyc deleted file mode 100644 index 35c5e27..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/__main__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/app.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/app.cpython-38.pyc deleted file mode 100644 index bbdeed4..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/app.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/blueprints.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/blueprints.cpython-38.pyc deleted file mode 100644 index 3644062..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/blueprints.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/cli.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/cli.cpython-38.pyc deleted file mode 100644 index 24ab2ed..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/cli.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/config.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/config.cpython-38.pyc deleted file mode 100644 index e4f5e22..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/config.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/ctx.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/ctx.cpython-38.pyc deleted file mode 100644 index f8d193c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/ctx.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/debughelpers.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/debughelpers.cpython-38.pyc deleted file mode 100644 index f1c86be..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/debughelpers.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/globals.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/globals.cpython-38.pyc deleted file mode 100644 index 8c82ef5..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/globals.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/helpers.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/helpers.cpython-38.pyc deleted file mode 100644 index 0a27539..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/helpers.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/logging.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/logging.cpython-38.pyc deleted file mode 100644 index d6efdaa..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/logging.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/sessions.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/sessions.cpython-38.pyc deleted file mode 100644 index e268e26..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/sessions.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/signals.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/signals.cpython-38.pyc deleted file mode 100644 index 09b05e1..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/signals.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/templating.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/templating.cpython-38.pyc deleted file mode 100644 index 835c967..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/templating.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/testing.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/testing.cpython-38.pyc deleted file mode 100644 index a77778e..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/testing.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/typing.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/typing.cpython-38.pyc deleted file mode 100644 index f335234..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/typing.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/views.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/views.cpython-38.pyc deleted file mode 100644 index fe64b71..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/views.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/wrappers.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/wrappers.cpython-38.pyc deleted file mode 100644 index eab5720..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/__pycache__/wrappers.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/app.py b/plotter-app/venv/lib/python3.8/site-packages/flask/app.py deleted file mode 100644 index 7622b5e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/app.py +++ /dev/null @@ -1,1498 +0,0 @@ -from __future__ import annotations - -import collections.abc as cabc -import os -import sys -import typing as t -import weakref -from datetime import timedelta -from inspect import iscoroutinefunction -from itertools import chain -from types import TracebackType -from urllib.parse import quote as _url_quote - -import click -from werkzeug.datastructures import Headers -from werkzeug.datastructures import ImmutableDict -from werkzeug.exceptions import BadRequestKeyError -from werkzeug.exceptions import HTTPException -from werkzeug.exceptions import InternalServerError -from werkzeug.routing import BuildError -from werkzeug.routing import MapAdapter -from werkzeug.routing import RequestRedirect -from werkzeug.routing import RoutingException -from werkzeug.routing import Rule -from werkzeug.serving import is_running_from_reloader -from werkzeug.wrappers import Response as BaseResponse - -from . import cli -from . import typing as ft -from .ctx import AppContext -from .ctx import RequestContext -from .globals import _cv_app -from .globals import _cv_request -from .globals import current_app -from .globals import g -from .globals import request -from .globals import request_ctx -from .globals import session -from .helpers import get_debug_flag -from .helpers import get_flashed_messages -from .helpers import get_load_dotenv -from .helpers import send_from_directory -from .sansio.app import App -from .sansio.scaffold import _sentinel -from .sessions import SecureCookieSessionInterface -from .sessions import SessionInterface -from .signals import appcontext_tearing_down -from .signals import got_request_exception -from .signals import request_finished -from .signals import request_started -from .signals import request_tearing_down -from .templating import Environment -from .wrappers import Request -from .wrappers import Response - -if t.TYPE_CHECKING: # pragma: no cover - from _typeshed.wsgi import StartResponse - from _typeshed.wsgi import WSGIEnvironment - - from .testing import FlaskClient - from .testing import FlaskCliRunner - -T_shell_context_processor = t.TypeVar( - "T_shell_context_processor", bound=ft.ShellContextProcessorCallable -) -T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable) -T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallable) -T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable) -T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable) - - -def _make_timedelta(value: timedelta | int | None) -> timedelta | None: - if value is None or isinstance(value, timedelta): - return value - - return timedelta(seconds=value) - - -class Flask(App): - """The flask object implements a WSGI application and acts as the central - object. It is passed the name of the module or package of the - application. Once it is created it will act as a central registry for - the view functions, the URL rules, template configuration and much more. - - The name of the package is used to resolve resources from inside the - package or the folder the module is contained in depending on if the - package parameter resolves to an actual python package (a folder with - an :file:`__init__.py` file inside) or a standard module (just a ``.py`` file). - - For more information about resource loading, see :func:`open_resource`. - - Usually you create a :class:`Flask` instance in your main module or - in the :file:`__init__.py` file of your package like this:: - - from flask import Flask - app = Flask(__name__) - - .. admonition:: About the First Parameter - - The idea of the first parameter is to give Flask an idea of what - belongs to your application. This name is used to find resources - on the filesystem, can be used by extensions to improve debugging - information and a lot more. - - So it's important what you provide there. If you are using a single - module, `__name__` is always the correct value. If you however are - using a package, it's usually recommended to hardcode the name of - your package there. - - For example if your application is defined in :file:`yourapplication/app.py` - you should create it with one of the two versions below:: - - app = Flask('yourapplication') - app = Flask(__name__.split('.')[0]) - - Why is that? The application will work even with `__name__`, thanks - to how resources are looked up. However it will make debugging more - painful. Certain extensions can make assumptions based on the - import name of your application. For example the Flask-SQLAlchemy - extension will look for the code in your application that triggered - an SQL query in debug mode. If the import name is not properly set - up, that debugging information is lost. (For example it would only - pick up SQL queries in `yourapplication.app` and not - `yourapplication.views.frontend`) - - .. versionadded:: 0.7 - The `static_url_path`, `static_folder`, and `template_folder` - parameters were added. - - .. versionadded:: 0.8 - The `instance_path` and `instance_relative_config` parameters were - added. - - .. versionadded:: 0.11 - The `root_path` parameter was added. - - .. versionadded:: 1.0 - The ``host_matching`` and ``static_host`` parameters were added. - - .. versionadded:: 1.0 - The ``subdomain_matching`` parameter was added. Subdomain - matching needs to be enabled manually now. Setting - :data:`SERVER_NAME` does not implicitly enable it. - - :param import_name: the name of the application package - :param static_url_path: can be used to specify a different path for the - static files on the web. Defaults to the name - of the `static_folder` folder. - :param static_folder: The folder with static files that is served at - ``static_url_path``. Relative to the application ``root_path`` - or an absolute path. Defaults to ``'static'``. - :param static_host: the host to use when adding the static route. - Defaults to None. Required when using ``host_matching=True`` - with a ``static_folder`` configured. - :param host_matching: set ``url_map.host_matching`` attribute. - Defaults to False. - :param subdomain_matching: consider the subdomain relative to - :data:`SERVER_NAME` when matching routes. Defaults to False. - :param template_folder: the folder that contains the templates that should - be used by the application. Defaults to - ``'templates'`` folder in the root path of the - application. - :param instance_path: An alternative instance path for the application. - By default the folder ``'instance'`` next to the - package or module is assumed to be the instance - path. - :param instance_relative_config: if set to ``True`` relative filenames - for loading the config are assumed to - be relative to the instance path instead - of the application root. - :param root_path: The path to the root of the application files. - This should only be set manually when it can't be detected - automatically, such as for namespace packages. - """ - - default_config = ImmutableDict( - { - "DEBUG": None, - "TESTING": False, - "PROPAGATE_EXCEPTIONS": None, - "SECRET_KEY": None, - "PERMANENT_SESSION_LIFETIME": timedelta(days=31), - "USE_X_SENDFILE": False, - "SERVER_NAME": None, - "APPLICATION_ROOT": "/", - "SESSION_COOKIE_NAME": "session", - "SESSION_COOKIE_DOMAIN": None, - "SESSION_COOKIE_PATH": None, - "SESSION_COOKIE_HTTPONLY": True, - "SESSION_COOKIE_SECURE": False, - "SESSION_COOKIE_SAMESITE": None, - "SESSION_REFRESH_EACH_REQUEST": True, - "MAX_CONTENT_LENGTH": None, - "SEND_FILE_MAX_AGE_DEFAULT": None, - "TRAP_BAD_REQUEST_ERRORS": None, - "TRAP_HTTP_EXCEPTIONS": False, - "EXPLAIN_TEMPLATE_LOADING": False, - "PREFERRED_URL_SCHEME": "http", - "TEMPLATES_AUTO_RELOAD": None, - "MAX_COOKIE_SIZE": 4093, - } - ) - - #: The class that is used for request objects. See :class:`~flask.Request` - #: for more information. - request_class: type[Request] = Request - - #: The class that is used for response objects. See - #: :class:`~flask.Response` for more information. - response_class: type[Response] = Response - - #: the session interface to use. By default an instance of - #: :class:`~flask.sessions.SecureCookieSessionInterface` is used here. - #: - #: .. versionadded:: 0.8 - session_interface: SessionInterface = SecureCookieSessionInterface() - - def __init__( - self, - import_name: str, - static_url_path: str | None = None, - static_folder: str | os.PathLike[str] | None = "static", - static_host: str | None = None, - host_matching: bool = False, - subdomain_matching: bool = False, - template_folder: str | os.PathLike[str] | None = "templates", - instance_path: str | None = None, - instance_relative_config: bool = False, - root_path: str | None = None, - ): - super().__init__( - import_name=import_name, - static_url_path=static_url_path, - static_folder=static_folder, - static_host=static_host, - host_matching=host_matching, - subdomain_matching=subdomain_matching, - template_folder=template_folder, - instance_path=instance_path, - instance_relative_config=instance_relative_config, - root_path=root_path, - ) - - #: The Click command group for registering CLI commands for this - #: object. The commands are available from the ``flask`` command - #: once the application has been discovered and blueprints have - #: been registered. - self.cli = cli.AppGroup() - - # Set the name of the Click group in case someone wants to add - # the app's commands to another CLI tool. - self.cli.name = self.name - - # Add a static route using the provided static_url_path, static_host, - # and static_folder if there is a configured static_folder. - # Note we do this without checking if static_folder exists. - # For one, it might be created while the server is running (e.g. during - # development). Also, Google App Engine stores static files somewhere - if self.has_static_folder: - assert ( - bool(static_host) == host_matching - ), "Invalid static_host/host_matching combination" - # Use a weakref to avoid creating a reference cycle between the app - # and the view function (see #3761). - self_ref = weakref.ref(self) - self.add_url_rule( - f"{self.static_url_path}/", - endpoint="static", - host=static_host, - view_func=lambda **kw: self_ref().send_static_file(**kw), # type: ignore # noqa: B950 - ) - - def get_send_file_max_age(self, filename: str | None) -> int | None: - """Used by :func:`send_file` to determine the ``max_age`` cache - value for a given file path if it wasn't passed. - - By default, this returns :data:`SEND_FILE_MAX_AGE_DEFAULT` from - the configuration of :data:`~flask.current_app`. This defaults - to ``None``, which tells the browser to use conditional requests - instead of a timed cache, which is usually preferable. - - Note this is a duplicate of the same method in the Flask - class. - - .. versionchanged:: 2.0 - The default configuration is ``None`` instead of 12 hours. - - .. versionadded:: 0.9 - """ - value = current_app.config["SEND_FILE_MAX_AGE_DEFAULT"] - - if value is None: - return None - - if isinstance(value, timedelta): - return int(value.total_seconds()) - - return value # type: ignore[no-any-return] - - def send_static_file(self, filename: str) -> Response: - """The view function used to serve files from - :attr:`static_folder`. A route is automatically registered for - this view at :attr:`static_url_path` if :attr:`static_folder` is - set. - - Note this is a duplicate of the same method in the Flask - class. - - .. versionadded:: 0.5 - - """ - if not self.has_static_folder: - raise RuntimeError("'static_folder' must be set to serve static_files.") - - # send_file only knows to call get_send_file_max_age on the app, - # call it here so it works for blueprints too. - max_age = self.get_send_file_max_age(filename) - return send_from_directory( - t.cast(str, self.static_folder), filename, max_age=max_age - ) - - def open_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]: - """Open a resource file relative to :attr:`root_path` for - reading. - - For example, if the file ``schema.sql`` is next to the file - ``app.py`` where the ``Flask`` app is defined, it can be opened - with: - - .. code-block:: python - - with app.open_resource("schema.sql") as f: - conn.executescript(f.read()) - - :param resource: Path to the resource relative to - :attr:`root_path`. - :param mode: Open the file in this mode. Only reading is - supported, valid values are "r" (or "rt") and "rb". - - Note this is a duplicate of the same method in the Flask - class. - - """ - if mode not in {"r", "rt", "rb"}: - raise ValueError("Resources can only be opened for reading.") - - return open(os.path.join(self.root_path, resource), mode) - - def open_instance_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]: - """Opens a resource from the application's instance folder - (:attr:`instance_path`). Otherwise works like - :meth:`open_resource`. Instance resources can also be opened for - writing. - - :param resource: the name of the resource. To access resources within - subfolders use forward slashes as separator. - :param mode: resource file opening mode, default is 'rb'. - """ - return open(os.path.join(self.instance_path, resource), mode) - - def create_jinja_environment(self) -> Environment: - """Create the Jinja environment based on :attr:`jinja_options` - and the various Jinja-related methods of the app. Changing - :attr:`jinja_options` after this will have no effect. Also adds - Flask-related globals and filters to the environment. - - .. versionchanged:: 0.11 - ``Environment.auto_reload`` set in accordance with - ``TEMPLATES_AUTO_RELOAD`` configuration option. - - .. versionadded:: 0.5 - """ - options = dict(self.jinja_options) - - if "autoescape" not in options: - options["autoescape"] = self.select_jinja_autoescape - - if "auto_reload" not in options: - auto_reload = self.config["TEMPLATES_AUTO_RELOAD"] - - if auto_reload is None: - auto_reload = self.debug - - options["auto_reload"] = auto_reload - - rv = self.jinja_environment(self, **options) - rv.globals.update( - url_for=self.url_for, - get_flashed_messages=get_flashed_messages, - config=self.config, - # request, session and g are normally added with the - # context processor for efficiency reasons but for imported - # templates we also want the proxies in there. - request=request, - session=session, - g=g, - ) - rv.policies["json.dumps_function"] = self.json.dumps - return rv - - def create_url_adapter(self, request: Request | None) -> MapAdapter | None: - """Creates a URL adapter for the given request. The URL adapter - is created at a point where the request context is not yet set - up so the request is passed explicitly. - - .. versionadded:: 0.6 - - .. versionchanged:: 0.9 - This can now also be called without a request object when the - URL adapter is created for the application context. - - .. versionchanged:: 1.0 - :data:`SERVER_NAME` no longer implicitly enables subdomain - matching. Use :attr:`subdomain_matching` instead. - """ - if request is not None: - # If subdomain matching is disabled (the default), use the - # default subdomain in all cases. This should be the default - # in Werkzeug but it currently does not have that feature. - if not self.subdomain_matching: - subdomain = self.url_map.default_subdomain or None - else: - subdomain = None - - return self.url_map.bind_to_environ( - request.environ, - server_name=self.config["SERVER_NAME"], - subdomain=subdomain, - ) - # We need at the very least the server name to be set for this - # to work. - if self.config["SERVER_NAME"] is not None: - return self.url_map.bind( - self.config["SERVER_NAME"], - script_name=self.config["APPLICATION_ROOT"], - url_scheme=self.config["PREFERRED_URL_SCHEME"], - ) - - return None - - def raise_routing_exception(self, request: Request) -> t.NoReturn: - """Intercept routing exceptions and possibly do something else. - - In debug mode, intercept a routing redirect and replace it with - an error if the body will be discarded. - - With modern Werkzeug this shouldn't occur, since it now uses a - 308 status which tells the browser to resend the method and - body. - - .. versionchanged:: 2.1 - Don't intercept 307 and 308 redirects. - - :meta private: - :internal: - """ - if ( - not self.debug - or not isinstance(request.routing_exception, RequestRedirect) - or request.routing_exception.code in {307, 308} - or request.method in {"GET", "HEAD", "OPTIONS"} - ): - raise request.routing_exception # type: ignore[misc] - - from .debughelpers import FormDataRoutingRedirect - - raise FormDataRoutingRedirect(request) - - def update_template_context(self, context: dict[str, t.Any]) -> None: - """Update the template context with some commonly used variables. - This injects request, session, config and g into the template - context as well as everything template context processors want - to inject. Note that the as of Flask 0.6, the original values - in the context will not be overridden if a context processor - decides to return a value with the same key. - - :param context: the context as a dictionary that is updated in place - to add extra variables. - """ - names: t.Iterable[str | None] = (None,) - - # A template may be rendered outside a request context. - if request: - names = chain(names, reversed(request.blueprints)) - - # The values passed to render_template take precedence. Keep a - # copy to re-apply after all context functions. - orig_ctx = context.copy() - - for name in names: - if name in self.template_context_processors: - for func in self.template_context_processors[name]: - context.update(self.ensure_sync(func)()) - - context.update(orig_ctx) - - def make_shell_context(self) -> dict[str, t.Any]: - """Returns the shell context for an interactive shell for this - application. This runs all the registered shell context - processors. - - .. versionadded:: 0.11 - """ - rv = {"app": self, "g": g} - for processor in self.shell_context_processors: - rv.update(processor()) - return rv - - def run( - self, - host: str | None = None, - port: int | None = None, - debug: bool | None = None, - load_dotenv: bool = True, - **options: t.Any, - ) -> None: - """Runs the application on a local development server. - - Do not use ``run()`` in a production setting. It is not intended to - meet security and performance requirements for a production server. - Instead, see :doc:`/deploying/index` for WSGI server recommendations. - - If the :attr:`debug` flag is set the server will automatically reload - for code changes and show a debugger in case an exception happened. - - If you want to run the application in debug mode, but disable the - code execution on the interactive debugger, you can pass - ``use_evalex=False`` as parameter. This will keep the debugger's - traceback screen active, but disable code execution. - - It is not recommended to use this function for development with - automatic reloading as this is badly supported. Instead you should - be using the :command:`flask` command line script's ``run`` support. - - .. admonition:: Keep in Mind - - Flask will suppress any server error with a generic error page - unless it is in debug mode. As such to enable just the - interactive debugger without the code reloading, you have to - invoke :meth:`run` with ``debug=True`` and ``use_reloader=False``. - Setting ``use_debugger`` to ``True`` without being in debug mode - won't catch any exceptions because there won't be any to - catch. - - :param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to - have the server available externally as well. Defaults to - ``'127.0.0.1'`` or the host in the ``SERVER_NAME`` config variable - if present. - :param port: the port of the webserver. Defaults to ``5000`` or the - port defined in the ``SERVER_NAME`` config variable if present. - :param debug: if given, enable or disable debug mode. See - :attr:`debug`. - :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv` - files to set environment variables. Will also change the working - directory to the directory containing the first file found. - :param options: the options to be forwarded to the underlying Werkzeug - server. See :func:`werkzeug.serving.run_simple` for more - information. - - .. versionchanged:: 1.0 - If installed, python-dotenv will be used to load environment - variables from :file:`.env` and :file:`.flaskenv` files. - - The :envvar:`FLASK_DEBUG` environment variable will override :attr:`debug`. - - Threaded mode is enabled by default. - - .. versionchanged:: 0.10 - The default port is now picked from the ``SERVER_NAME`` - variable. - """ - # Ignore this call so that it doesn't start another server if - # the 'flask run' command is used. - if os.environ.get("FLASK_RUN_FROM_CLI") == "true": - if not is_running_from_reloader(): - click.secho( - " * Ignoring a call to 'app.run()' that would block" - " the current 'flask' CLI command.\n" - " Only call 'app.run()' in an 'if __name__ ==" - ' "__main__"\' guard.', - fg="red", - ) - - return - - if get_load_dotenv(load_dotenv): - cli.load_dotenv() - - # if set, env var overrides existing value - if "FLASK_DEBUG" in os.environ: - self.debug = get_debug_flag() - - # debug passed to method overrides all other sources - if debug is not None: - self.debug = bool(debug) - - server_name = self.config.get("SERVER_NAME") - sn_host = sn_port = None - - if server_name: - sn_host, _, sn_port = server_name.partition(":") - - if not host: - if sn_host: - host = sn_host - else: - host = "127.0.0.1" - - if port or port == 0: - port = int(port) - elif sn_port: - port = int(sn_port) - else: - port = 5000 - - options.setdefault("use_reloader", self.debug) - options.setdefault("use_debugger", self.debug) - options.setdefault("threaded", True) - - cli.show_server_banner(self.debug, self.name) - - from werkzeug.serving import run_simple - - try: - run_simple(t.cast(str, host), port, self, **options) - finally: - # reset the first request information if the development server - # reset normally. This makes it possible to restart the server - # without reloader and that stuff from an interactive shell. - self._got_first_request = False - - def test_client(self, use_cookies: bool = True, **kwargs: t.Any) -> FlaskClient: - """Creates a test client for this application. For information - about unit testing head over to :doc:`/testing`. - - Note that if you are testing for assertions or exceptions in your - application code, you must set ``app.testing = True`` in order for the - exceptions to propagate to the test client. Otherwise, the exception - will be handled by the application (not visible to the test client) and - the only indication of an AssertionError or other exception will be a - 500 status code response to the test client. See the :attr:`testing` - attribute. For example:: - - app.testing = True - client = app.test_client() - - The test client can be used in a ``with`` block to defer the closing down - of the context until the end of the ``with`` block. This is useful if - you want to access the context locals for testing:: - - with app.test_client() as c: - rv = c.get('/?vodka=42') - assert request.args['vodka'] == '42' - - Additionally, you may pass optional keyword arguments that will then - be passed to the application's :attr:`test_client_class` constructor. - For example:: - - from flask.testing import FlaskClient - - class CustomClient(FlaskClient): - def __init__(self, *args, **kwargs): - self._authentication = kwargs.pop("authentication") - super(CustomClient,self).__init__( *args, **kwargs) - - app.test_client_class = CustomClient - client = app.test_client(authentication='Basic ....') - - See :class:`~flask.testing.FlaskClient` for more information. - - .. versionchanged:: 0.4 - added support for ``with`` block usage for the client. - - .. versionadded:: 0.7 - The `use_cookies` parameter was added as well as the ability - to override the client to be used by setting the - :attr:`test_client_class` attribute. - - .. versionchanged:: 0.11 - Added `**kwargs` to support passing additional keyword arguments to - the constructor of :attr:`test_client_class`. - """ - cls = self.test_client_class - if cls is None: - from .testing import FlaskClient as cls - return cls( # type: ignore - self, self.response_class, use_cookies=use_cookies, **kwargs - ) - - def test_cli_runner(self, **kwargs: t.Any) -> FlaskCliRunner: - """Create a CLI runner for testing CLI commands. - See :ref:`testing-cli`. - - Returns an instance of :attr:`test_cli_runner_class`, by default - :class:`~flask.testing.FlaskCliRunner`. The Flask app object is - passed as the first argument. - - .. versionadded:: 1.0 - """ - cls = self.test_cli_runner_class - - if cls is None: - from .testing import FlaskCliRunner as cls - - return cls(self, **kwargs) # type: ignore - - def handle_http_exception( - self, e: HTTPException - ) -> HTTPException | ft.ResponseReturnValue: - """Handles an HTTP exception. By default this will invoke the - registered error handlers and fall back to returning the - exception as response. - - .. versionchanged:: 1.0.3 - ``RoutingException``, used internally for actions such as - slash redirects during routing, is not passed to error - handlers. - - .. versionchanged:: 1.0 - Exceptions are looked up by code *and* by MRO, so - ``HTTPException`` subclasses can be handled with a catch-all - handler for the base ``HTTPException``. - - .. versionadded:: 0.3 - """ - # Proxy exceptions don't have error codes. We want to always return - # those unchanged as errors - if e.code is None: - return e - - # RoutingExceptions are used internally to trigger routing - # actions, such as slash redirects raising RequestRedirect. They - # are not raised or handled in user code. - if isinstance(e, RoutingException): - return e - - handler = self._find_error_handler(e, request.blueprints) - if handler is None: - return e - return self.ensure_sync(handler)(e) # type: ignore[no-any-return] - - def handle_user_exception( - self, e: Exception - ) -> HTTPException | ft.ResponseReturnValue: - """This method is called whenever an exception occurs that - should be handled. A special case is :class:`~werkzeug - .exceptions.HTTPException` which is forwarded to the - :meth:`handle_http_exception` method. This function will either - return a response value or reraise the exception with the same - traceback. - - .. versionchanged:: 1.0 - Key errors raised from request data like ``form`` show the - bad key in debug mode rather than a generic bad request - message. - - .. versionadded:: 0.7 - """ - if isinstance(e, BadRequestKeyError) and ( - self.debug or self.config["TRAP_BAD_REQUEST_ERRORS"] - ): - e.show_exception = True - - if isinstance(e, HTTPException) and not self.trap_http_exception(e): - return self.handle_http_exception(e) - - handler = self._find_error_handler(e, request.blueprints) - - if handler is None: - raise - - return self.ensure_sync(handler)(e) # type: ignore[no-any-return] - - def handle_exception(self, e: Exception) -> Response: - """Handle an exception that did not have an error handler - associated with it, or that was raised from an error handler. - This always causes a 500 ``InternalServerError``. - - Always sends the :data:`got_request_exception` signal. - - If :data:`PROPAGATE_EXCEPTIONS` is ``True``, such as in debug - mode, the error will be re-raised so that the debugger can - display it. Otherwise, the original exception is logged, and - an :exc:`~werkzeug.exceptions.InternalServerError` is returned. - - If an error handler is registered for ``InternalServerError`` or - ``500``, it will be used. For consistency, the handler will - always receive the ``InternalServerError``. The original - unhandled exception is available as ``e.original_exception``. - - .. versionchanged:: 1.1.0 - Always passes the ``InternalServerError`` instance to the - handler, setting ``original_exception`` to the unhandled - error. - - .. versionchanged:: 1.1.0 - ``after_request`` functions and other finalization is done - even for the default 500 response when there is no handler. - - .. versionadded:: 0.3 - """ - exc_info = sys.exc_info() - got_request_exception.send(self, _async_wrapper=self.ensure_sync, exception=e) - propagate = self.config["PROPAGATE_EXCEPTIONS"] - - if propagate is None: - propagate = self.testing or self.debug - - if propagate: - # Re-raise if called with an active exception, otherwise - # raise the passed in exception. - if exc_info[1] is e: - raise - - raise e - - self.log_exception(exc_info) - server_error: InternalServerError | ft.ResponseReturnValue - server_error = InternalServerError(original_exception=e) - handler = self._find_error_handler(server_error, request.blueprints) - - if handler is not None: - server_error = self.ensure_sync(handler)(server_error) - - return self.finalize_request(server_error, from_error_handler=True) - - def log_exception( - self, - exc_info: (tuple[type, BaseException, TracebackType] | tuple[None, None, None]), - ) -> None: - """Logs an exception. This is called by :meth:`handle_exception` - if debugging is disabled and right before the handler is called. - The default implementation logs the exception as error on the - :attr:`logger`. - - .. versionadded:: 0.8 - """ - self.logger.error( - f"Exception on {request.path} [{request.method}]", exc_info=exc_info - ) - - def dispatch_request(self) -> ft.ResponseReturnValue: - """Does the request dispatching. Matches the URL and returns the - return value of the view or error handler. This does not have to - be a response object. In order to convert the return value to a - proper response object, call :func:`make_response`. - - .. versionchanged:: 0.7 - This no longer does the exception handling, this code was - moved to the new :meth:`full_dispatch_request`. - """ - req = request_ctx.request - if req.routing_exception is not None: - self.raise_routing_exception(req) - rule: Rule = req.url_rule # type: ignore[assignment] - # if we provide automatic options for this URL and the - # request came with the OPTIONS method, reply automatically - if ( - getattr(rule, "provide_automatic_options", False) - and req.method == "OPTIONS" - ): - return self.make_default_options_response() - # otherwise dispatch to the handler for that endpoint - view_args: dict[str, t.Any] = req.view_args # type: ignore[assignment] - return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) # type: ignore[no-any-return] - - def full_dispatch_request(self) -> Response: - """Dispatches the request and on top of that performs request - pre and postprocessing as well as HTTP exception catching and - error handling. - - .. versionadded:: 0.7 - """ - self._got_first_request = True - - try: - request_started.send(self, _async_wrapper=self.ensure_sync) - rv = self.preprocess_request() - if rv is None: - rv = self.dispatch_request() - except Exception as e: - rv = self.handle_user_exception(e) - return self.finalize_request(rv) - - def finalize_request( - self, - rv: ft.ResponseReturnValue | HTTPException, - from_error_handler: bool = False, - ) -> Response: - """Given the return value from a view function this finalizes - the request by converting it into a response and invoking the - postprocessing functions. This is invoked for both normal - request dispatching as well as error handlers. - - Because this means that it might be called as a result of a - failure a special safe mode is available which can be enabled - with the `from_error_handler` flag. If enabled, failures in - response processing will be logged and otherwise ignored. - - :internal: - """ - response = self.make_response(rv) - try: - response = self.process_response(response) - request_finished.send( - self, _async_wrapper=self.ensure_sync, response=response - ) - except Exception: - if not from_error_handler: - raise - self.logger.exception( - "Request finalizing failed with an error while handling an error" - ) - return response - - def make_default_options_response(self) -> Response: - """This method is called to create the default ``OPTIONS`` response. - This can be changed through subclassing to change the default - behavior of ``OPTIONS`` responses. - - .. versionadded:: 0.7 - """ - adapter = request_ctx.url_adapter - methods = adapter.allowed_methods() # type: ignore[union-attr] - rv = self.response_class() - rv.allow.update(methods) - return rv - - def ensure_sync(self, func: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: - """Ensure that the function is synchronous for WSGI workers. - Plain ``def`` functions are returned as-is. ``async def`` - functions are wrapped to run and wait for the response. - - Override this method to change how the app runs async views. - - .. versionadded:: 2.0 - """ - if iscoroutinefunction(func): - return self.async_to_sync(func) - - return func - - def async_to_sync( - self, func: t.Callable[..., t.Coroutine[t.Any, t.Any, t.Any]] - ) -> t.Callable[..., t.Any]: - """Return a sync function that will run the coroutine function. - - .. code-block:: python - - result = app.async_to_sync(func)(*args, **kwargs) - - Override this method to change how the app converts async code - to be synchronously callable. - - .. versionadded:: 2.0 - """ - try: - from asgiref.sync import async_to_sync as asgiref_async_to_sync - except ImportError: - raise RuntimeError( - "Install Flask with the 'async' extra in order to use async views." - ) from None - - return asgiref_async_to_sync(func) - - def url_for( - self, - /, - endpoint: str, - *, - _anchor: str | None = None, - _method: str | None = None, - _scheme: str | None = None, - _external: bool | None = None, - **values: t.Any, - ) -> str: - """Generate a URL to the given endpoint with the given values. - - This is called by :func:`flask.url_for`, and can be called - directly as well. - - An *endpoint* is the name of a URL rule, usually added with - :meth:`@app.route() `, and usually the same name as the - view function. A route defined in a :class:`~flask.Blueprint` - will prepend the blueprint's name separated by a ``.`` to the - endpoint. - - In some cases, such as email messages, you want URLs to include - the scheme and domain, like ``https://example.com/hello``. When - not in an active request, URLs will be external by default, but - this requires setting :data:`SERVER_NAME` so Flask knows what - domain to use. :data:`APPLICATION_ROOT` and - :data:`PREFERRED_URL_SCHEME` should also be configured as - needed. This config is only used when not in an active request. - - Functions can be decorated with :meth:`url_defaults` to modify - keyword arguments before the URL is built. - - If building fails for some reason, such as an unknown endpoint - or incorrect values, the app's :meth:`handle_url_build_error` - method is called. If that returns a string, that is returned, - otherwise a :exc:`~werkzeug.routing.BuildError` is raised. - - :param endpoint: The endpoint name associated with the URL to - generate. If this starts with a ``.``, the current blueprint - name (if any) will be used. - :param _anchor: If given, append this as ``#anchor`` to the URL. - :param _method: If given, generate the URL associated with this - method for the endpoint. - :param _scheme: If given, the URL will have this scheme if it - is external. - :param _external: If given, prefer the URL to be internal - (False) or require it to be external (True). External URLs - include the scheme and domain. When not in an active - request, URLs are external by default. - :param values: Values to use for the variable parts of the URL - rule. Unknown keys are appended as query string arguments, - like ``?a=b&c=d``. - - .. versionadded:: 2.2 - Moved from ``flask.url_for``, which calls this method. - """ - req_ctx = _cv_request.get(None) - - if req_ctx is not None: - url_adapter = req_ctx.url_adapter - blueprint_name = req_ctx.request.blueprint - - # If the endpoint starts with "." and the request matches a - # blueprint, the endpoint is relative to the blueprint. - if endpoint[:1] == ".": - if blueprint_name is not None: - endpoint = f"{blueprint_name}{endpoint}" - else: - endpoint = endpoint[1:] - - # When in a request, generate a URL without scheme and - # domain by default, unless a scheme is given. - if _external is None: - _external = _scheme is not None - else: - app_ctx = _cv_app.get(None) - - # If called by helpers.url_for, an app context is active, - # use its url_adapter. Otherwise, app.url_for was called - # directly, build an adapter. - if app_ctx is not None: - url_adapter = app_ctx.url_adapter - else: - url_adapter = self.create_url_adapter(None) - - if url_adapter is None: - raise RuntimeError( - "Unable to build URLs outside an active request" - " without 'SERVER_NAME' configured. Also configure" - " 'APPLICATION_ROOT' and 'PREFERRED_URL_SCHEME' as" - " needed." - ) - - # When outside a request, generate a URL with scheme and - # domain by default. - if _external is None: - _external = True - - # It is an error to set _scheme when _external=False, in order - # to avoid accidental insecure URLs. - if _scheme is not None and not _external: - raise ValueError("When specifying '_scheme', '_external' must be True.") - - self.inject_url_defaults(endpoint, values) - - try: - rv = url_adapter.build( # type: ignore[union-attr] - endpoint, - values, - method=_method, - url_scheme=_scheme, - force_external=_external, - ) - except BuildError as error: - values.update( - _anchor=_anchor, _method=_method, _scheme=_scheme, _external=_external - ) - return self.handle_url_build_error(error, endpoint, values) - - if _anchor is not None: - _anchor = _url_quote(_anchor, safe="%!#$&'()*+,/:;=?@") - rv = f"{rv}#{_anchor}" - - return rv - - def make_response(self, rv: ft.ResponseReturnValue) -> Response: - """Convert the return value from a view function to an instance of - :attr:`response_class`. - - :param rv: the return value from the view function. The view function - must return a response. Returning ``None``, or the view ending - without returning, is not allowed. The following types are allowed - for ``view_rv``: - - ``str`` - A response object is created with the string encoded to UTF-8 - as the body. - - ``bytes`` - A response object is created with the bytes as the body. - - ``dict`` - A dictionary that will be jsonify'd before being returned. - - ``list`` - A list that will be jsonify'd before being returned. - - ``generator`` or ``iterator`` - A generator that returns ``str`` or ``bytes`` to be - streamed as the response. - - ``tuple`` - Either ``(body, status, headers)``, ``(body, status)``, or - ``(body, headers)``, where ``body`` is any of the other types - allowed here, ``status`` is a string or an integer, and - ``headers`` is a dictionary or a list of ``(key, value)`` - tuples. If ``body`` is a :attr:`response_class` instance, - ``status`` overwrites the exiting value and ``headers`` are - extended. - - :attr:`response_class` - The object is returned unchanged. - - other :class:`~werkzeug.wrappers.Response` class - The object is coerced to :attr:`response_class`. - - :func:`callable` - The function is called as a WSGI application. The result is - used to create a response object. - - .. versionchanged:: 2.2 - A generator will be converted to a streaming response. - A list will be converted to a JSON response. - - .. versionchanged:: 1.1 - A dict will be converted to a JSON response. - - .. versionchanged:: 0.9 - Previously a tuple was interpreted as the arguments for the - response object. - """ - - status = headers = None - - # unpack tuple returns - if isinstance(rv, tuple): - len_rv = len(rv) - - # a 3-tuple is unpacked directly - if len_rv == 3: - rv, status, headers = rv # type: ignore[misc] - # decide if a 2-tuple has status or headers - elif len_rv == 2: - if isinstance(rv[1], (Headers, dict, tuple, list)): - rv, headers = rv - else: - rv, status = rv # type: ignore[assignment,misc] - # other sized tuples are not allowed - else: - raise TypeError( - "The view function did not return a valid response tuple." - " The tuple must have the form (body, status, headers)," - " (body, status), or (body, headers)." - ) - - # the body must not be None - if rv is None: - raise TypeError( - f"The view function for {request.endpoint!r} did not" - " return a valid response. The function either returned" - " None or ended without a return statement." - ) - - # make sure the body is an instance of the response class - if not isinstance(rv, self.response_class): - if isinstance(rv, (str, bytes, bytearray)) or isinstance(rv, cabc.Iterator): - # let the response class set the status and headers instead of - # waiting to do it manually, so that the class can handle any - # special logic - rv = self.response_class( - rv, - status=status, - headers=headers, # type: ignore[arg-type] - ) - status = headers = None - elif isinstance(rv, (dict, list)): - rv = self.json.response(rv) - elif isinstance(rv, BaseResponse) or callable(rv): - # evaluate a WSGI callable, or coerce a different response - # class to the correct type - try: - rv = self.response_class.force_type( - rv, # type: ignore[arg-type] - request.environ, - ) - except TypeError as e: - raise TypeError( - f"{e}\nThe view function did not return a valid" - " response. The return type must be a string," - " dict, list, tuple with headers or status," - " Response instance, or WSGI callable, but it" - f" was a {type(rv).__name__}." - ).with_traceback(sys.exc_info()[2]) from None - else: - raise TypeError( - "The view function did not return a valid" - " response. The return type must be a string," - " dict, list, tuple with headers or status," - " Response instance, or WSGI callable, but it was a" - f" {type(rv).__name__}." - ) - - rv = t.cast(Response, rv) - # prefer the status if it was provided - if status is not None: - if isinstance(status, (str, bytes, bytearray)): - rv.status = status - else: - rv.status_code = status - - # extend existing headers with provided headers - if headers: - rv.headers.update(headers) # type: ignore[arg-type] - - return rv - - def preprocess_request(self) -> ft.ResponseReturnValue | None: - """Called before the request is dispatched. Calls - :attr:`url_value_preprocessors` registered with the app and the - current blueprint (if any). Then calls :attr:`before_request_funcs` - registered with the app and the blueprint. - - If any :meth:`before_request` handler returns a non-None value, the - value is handled as if it was the return value from the view, and - further request handling is stopped. - """ - names = (None, *reversed(request.blueprints)) - - for name in names: - if name in self.url_value_preprocessors: - for url_func in self.url_value_preprocessors[name]: - url_func(request.endpoint, request.view_args) - - for name in names: - if name in self.before_request_funcs: - for before_func in self.before_request_funcs[name]: - rv = self.ensure_sync(before_func)() - - if rv is not None: - return rv # type: ignore[no-any-return] - - return None - - def process_response(self, response: Response) -> Response: - """Can be overridden in order to modify the response object - before it's sent to the WSGI server. By default this will - call all the :meth:`after_request` decorated functions. - - .. versionchanged:: 0.5 - As of Flask 0.5 the functions registered for after request - execution are called in reverse order of registration. - - :param response: a :attr:`response_class` object. - :return: a new response object or the same, has to be an - instance of :attr:`response_class`. - """ - ctx = request_ctx._get_current_object() # type: ignore[attr-defined] - - for func in ctx._after_request_functions: - response = self.ensure_sync(func)(response) - - for name in chain(request.blueprints, (None,)): - if name in self.after_request_funcs: - for func in reversed(self.after_request_funcs[name]): - response = self.ensure_sync(func)(response) - - if not self.session_interface.is_null_session(ctx.session): - self.session_interface.save_session(self, ctx.session, response) - - return response - - def do_teardown_request( - self, - exc: BaseException | None = _sentinel, # type: ignore[assignment] - ) -> None: - """Called after the request is dispatched and the response is - returned, right before the request context is popped. - - This calls all functions decorated with - :meth:`teardown_request`, and :meth:`Blueprint.teardown_request` - if a blueprint handled the request. Finally, the - :data:`request_tearing_down` signal is sent. - - This is called by - :meth:`RequestContext.pop() `, - which may be delayed during testing to maintain access to - resources. - - :param exc: An unhandled exception raised while dispatching the - request. Detected from the current exception information if - not passed. Passed to each teardown function. - - .. versionchanged:: 0.9 - Added the ``exc`` argument. - """ - if exc is _sentinel: - exc = sys.exc_info()[1] - - for name in chain(request.blueprints, (None,)): - if name in self.teardown_request_funcs: - for func in reversed(self.teardown_request_funcs[name]): - self.ensure_sync(func)(exc) - - request_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc) - - def do_teardown_appcontext( - self, - exc: BaseException | None = _sentinel, # type: ignore[assignment] - ) -> None: - """Called right before the application context is popped. - - When handling a request, the application context is popped - after the request context. See :meth:`do_teardown_request`. - - This calls all functions decorated with - :meth:`teardown_appcontext`. Then the - :data:`appcontext_tearing_down` signal is sent. - - This is called by - :meth:`AppContext.pop() `. - - .. versionadded:: 0.9 - """ - if exc is _sentinel: - exc = sys.exc_info()[1] - - for func in reversed(self.teardown_appcontext_funcs): - self.ensure_sync(func)(exc) - - appcontext_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc) - - def app_context(self) -> AppContext: - """Create an :class:`~flask.ctx.AppContext`. Use as a ``with`` - block to push the context, which will make :data:`current_app` - point at this application. - - An application context is automatically pushed by - :meth:`RequestContext.push() ` - when handling a request, and when running a CLI command. Use - this to manually create a context outside of these situations. - - :: - - with app.app_context(): - init_db() - - See :doc:`/appcontext`. - - .. versionadded:: 0.9 - """ - return AppContext(self) - - def request_context(self, environ: WSGIEnvironment) -> RequestContext: - """Create a :class:`~flask.ctx.RequestContext` representing a - WSGI environment. Use a ``with`` block to push the context, - which will make :data:`request` point at this request. - - See :doc:`/reqcontext`. - - Typically you should not call this from your own code. A request - context is automatically pushed by the :meth:`wsgi_app` when - handling a request. Use :meth:`test_request_context` to create - an environment and context instead of this method. - - :param environ: a WSGI environment - """ - return RequestContext(self, environ) - - def test_request_context(self, *args: t.Any, **kwargs: t.Any) -> RequestContext: - """Create a :class:`~flask.ctx.RequestContext` for a WSGI - environment created from the given values. This is mostly useful - during testing, where you may want to run a function that uses - request data without dispatching a full request. - - See :doc:`/reqcontext`. - - Use a ``with`` block to push the context, which will make - :data:`request` point at the request for the created - environment. :: - - with app.test_request_context(...): - generate_report() - - When using the shell, it may be easier to push and pop the - context manually to avoid indentation. :: - - ctx = app.test_request_context(...) - ctx.push() - ... - ctx.pop() - - Takes the same arguments as Werkzeug's - :class:`~werkzeug.test.EnvironBuilder`, with some defaults from - the application. See the linked Werkzeug docs for most of the - available arguments. Flask-specific behavior is listed here. - - :param path: URL path being requested. - :param base_url: Base URL where the app is being served, which - ``path`` is relative to. If not given, built from - :data:`PREFERRED_URL_SCHEME`, ``subdomain``, - :data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`. - :param subdomain: Subdomain name to append to - :data:`SERVER_NAME`. - :param url_scheme: Scheme to use instead of - :data:`PREFERRED_URL_SCHEME`. - :param data: The request body, either as a string or a dict of - form keys and values. - :param json: If given, this is serialized as JSON and passed as - ``data``. Also defaults ``content_type`` to - ``application/json``. - :param args: other positional arguments passed to - :class:`~werkzeug.test.EnvironBuilder`. - :param kwargs: other keyword arguments passed to - :class:`~werkzeug.test.EnvironBuilder`. - """ - from .testing import EnvironBuilder - - builder = EnvironBuilder(self, *args, **kwargs) - - try: - return self.request_context(builder.get_environ()) - finally: - builder.close() - - def wsgi_app( - self, environ: WSGIEnvironment, start_response: StartResponse - ) -> cabc.Iterable[bytes]: - """The actual WSGI application. This is not implemented in - :meth:`__call__` so that middlewares can be applied without - losing a reference to the app object. Instead of doing this:: - - app = MyMiddleware(app) - - It's a better idea to do this instead:: - - app.wsgi_app = MyMiddleware(app.wsgi_app) - - Then you still have the original application object around and - can continue to call methods on it. - - .. versionchanged:: 0.7 - Teardown events for the request and app contexts are called - even if an unhandled error occurs. Other events may not be - called depending on when an error occurs during dispatch. - See :ref:`callbacks-and-errors`. - - :param environ: A WSGI environment. - :param start_response: A callable accepting a status code, - a list of headers, and an optional exception context to - start the response. - """ - ctx = self.request_context(environ) - error: BaseException | None = None - try: - try: - ctx.push() - response = self.full_dispatch_request() - except Exception as e: - error = e - response = self.handle_exception(e) - except: # noqa: B001 - error = sys.exc_info()[1] - raise - return response(environ, start_response) - finally: - if "werkzeug.debug.preserve_context" in environ: - environ["werkzeug.debug.preserve_context"](_cv_app.get()) - environ["werkzeug.debug.preserve_context"](_cv_request.get()) - - if error is not None and self.should_ignore_error(error): - error = None - - ctx.pop(error) - - def __call__( - self, environ: WSGIEnvironment, start_response: StartResponse - ) -> cabc.Iterable[bytes]: - """The WSGI server calls the Flask application object as the - WSGI application. This calls :meth:`wsgi_app`, which can be - wrapped to apply middleware. - """ - return self.wsgi_app(environ, start_response) diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/blueprints.py b/plotter-app/venv/lib/python3.8/site-packages/flask/blueprints.py deleted file mode 100644 index aa9eacf..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/blueprints.py +++ /dev/null @@ -1,129 +0,0 @@ -from __future__ import annotations - -import os -import typing as t -from datetime import timedelta - -from .cli import AppGroup -from .globals import current_app -from .helpers import send_from_directory -from .sansio.blueprints import Blueprint as SansioBlueprint -from .sansio.blueprints import BlueprintSetupState as BlueprintSetupState # noqa -from .sansio.scaffold import _sentinel - -if t.TYPE_CHECKING: # pragma: no cover - from .wrappers import Response - - -class Blueprint(SansioBlueprint): - def __init__( - self, - name: str, - import_name: str, - static_folder: str | os.PathLike[str] | None = None, - static_url_path: str | None = None, - template_folder: str | os.PathLike[str] | None = None, - url_prefix: str | None = None, - subdomain: str | None = None, - url_defaults: dict[str, t.Any] | None = None, - root_path: str | None = None, - cli_group: str | None = _sentinel, # type: ignore - ) -> None: - super().__init__( - name, - import_name, - static_folder, - static_url_path, - template_folder, - url_prefix, - subdomain, - url_defaults, - root_path, - cli_group, - ) - - #: The Click command group for registering CLI commands for this - #: object. The commands are available from the ``flask`` command - #: once the application has been discovered and blueprints have - #: been registered. - self.cli = AppGroup() - - # Set the name of the Click group in case someone wants to add - # the app's commands to another CLI tool. - self.cli.name = self.name - - def get_send_file_max_age(self, filename: str | None) -> int | None: - """Used by :func:`send_file` to determine the ``max_age`` cache - value for a given file path if it wasn't passed. - - By default, this returns :data:`SEND_FILE_MAX_AGE_DEFAULT` from - the configuration of :data:`~flask.current_app`. This defaults - to ``None``, which tells the browser to use conditional requests - instead of a timed cache, which is usually preferable. - - Note this is a duplicate of the same method in the Flask - class. - - .. versionchanged:: 2.0 - The default configuration is ``None`` instead of 12 hours. - - .. versionadded:: 0.9 - """ - value = current_app.config["SEND_FILE_MAX_AGE_DEFAULT"] - - if value is None: - return None - - if isinstance(value, timedelta): - return int(value.total_seconds()) - - return value # type: ignore[no-any-return] - - def send_static_file(self, filename: str) -> Response: - """The view function used to serve files from - :attr:`static_folder`. A route is automatically registered for - this view at :attr:`static_url_path` if :attr:`static_folder` is - set. - - Note this is a duplicate of the same method in the Flask - class. - - .. versionadded:: 0.5 - - """ - if not self.has_static_folder: - raise RuntimeError("'static_folder' must be set to serve static_files.") - - # send_file only knows to call get_send_file_max_age on the app, - # call it here so it works for blueprints too. - max_age = self.get_send_file_max_age(filename) - return send_from_directory( - t.cast(str, self.static_folder), filename, max_age=max_age - ) - - def open_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]: - """Open a resource file relative to :attr:`root_path` for - reading. - - For example, if the file ``schema.sql`` is next to the file - ``app.py`` where the ``Flask`` app is defined, it can be opened - with: - - .. code-block:: python - - with app.open_resource("schema.sql") as f: - conn.executescript(f.read()) - - :param resource: Path to the resource relative to - :attr:`root_path`. - :param mode: Open the file in this mode. Only reading is - supported, valid values are "r" (or "rt") and "rb". - - Note this is a duplicate of the same method in the Flask - class. - - """ - if mode not in {"r", "rt", "rb"}: - raise ValueError("Resources can only be opened for reading.") - - return open(os.path.join(self.root_path, resource), mode) diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/cli.py b/plotter-app/venv/lib/python3.8/site-packages/flask/cli.py deleted file mode 100644 index ecb292a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/cli.py +++ /dev/null @@ -1,1109 +0,0 @@ -from __future__ import annotations - -import ast -import collections.abc as cabc -import importlib.metadata -import inspect -import os -import platform -import re -import sys -import traceback -import typing as t -from functools import update_wrapper -from operator import itemgetter -from types import ModuleType - -import click -from click.core import ParameterSource -from werkzeug import run_simple -from werkzeug.serving import is_running_from_reloader -from werkzeug.utils import import_string - -from .globals import current_app -from .helpers import get_debug_flag -from .helpers import get_load_dotenv - -if t.TYPE_CHECKING: - import ssl - - from _typeshed.wsgi import StartResponse - from _typeshed.wsgi import WSGIApplication - from _typeshed.wsgi import WSGIEnvironment - - from .app import Flask - - -class NoAppException(click.UsageError): - """Raised if an application cannot be found or loaded.""" - - -def find_best_app(module: ModuleType) -> Flask: - """Given a module instance this tries to find the best possible - application in the module or raises an exception. - """ - from . import Flask - - # Search for the most common names first. - for attr_name in ("app", "application"): - app = getattr(module, attr_name, None) - - if isinstance(app, Flask): - return app - - # Otherwise find the only object that is a Flask instance. - matches = [v for v in module.__dict__.values() if isinstance(v, Flask)] - - if len(matches) == 1: - return matches[0] - elif len(matches) > 1: - raise NoAppException( - "Detected multiple Flask applications in module" - f" '{module.__name__}'. Use '{module.__name__}:name'" - " to specify the correct one." - ) - - # Search for app factory functions. - for attr_name in ("create_app", "make_app"): - app_factory = getattr(module, attr_name, None) - - if inspect.isfunction(app_factory): - try: - app = app_factory() - - if isinstance(app, Flask): - return app - except TypeError as e: - if not _called_with_wrong_args(app_factory): - raise - - raise NoAppException( - f"Detected factory '{attr_name}' in module '{module.__name__}'," - " but could not call it without arguments. Use" - f" '{module.__name__}:{attr_name}(args)'" - " to specify arguments." - ) from e - - raise NoAppException( - "Failed to find Flask application or factory in module" - f" '{module.__name__}'. Use '{module.__name__}:name'" - " to specify one." - ) - - -def _called_with_wrong_args(f: t.Callable[..., Flask]) -> bool: - """Check whether calling a function raised a ``TypeError`` because - the call failed or because something in the factory raised the - error. - - :param f: The function that was called. - :return: ``True`` if the call failed. - """ - tb = sys.exc_info()[2] - - try: - while tb is not None: - if tb.tb_frame.f_code is f.__code__: - # In the function, it was called successfully. - return False - - tb = tb.tb_next - - # Didn't reach the function. - return True - finally: - # Delete tb to break a circular reference. - # https://docs.python.org/2/library/sys.html#sys.exc_info - del tb - - -def find_app_by_string(module: ModuleType, app_name: str) -> Flask: - """Check if the given string is a variable name or a function. Call - a function to get the app instance, or return the variable directly. - """ - from . import Flask - - # Parse app_name as a single expression to determine if it's a valid - # attribute name or function call. - try: - expr = ast.parse(app_name.strip(), mode="eval").body - except SyntaxError: - raise NoAppException( - f"Failed to parse {app_name!r} as an attribute name or function call." - ) from None - - if isinstance(expr, ast.Name): - name = expr.id - args = [] - kwargs = {} - elif isinstance(expr, ast.Call): - # Ensure the function name is an attribute name only. - if not isinstance(expr.func, ast.Name): - raise NoAppException( - f"Function reference must be a simple name: {app_name!r}." - ) - - name = expr.func.id - - # Parse the positional and keyword arguments as literals. - try: - args = [ast.literal_eval(arg) for arg in expr.args] - kwargs = { - kw.arg: ast.literal_eval(kw.value) - for kw in expr.keywords - if kw.arg is not None - } - except ValueError: - # literal_eval gives cryptic error messages, show a generic - # message with the full expression instead. - raise NoAppException( - f"Failed to parse arguments as literal values: {app_name!r}." - ) from None - else: - raise NoAppException( - f"Failed to parse {app_name!r} as an attribute name or function call." - ) - - try: - attr = getattr(module, name) - except AttributeError as e: - raise NoAppException( - f"Failed to find attribute {name!r} in {module.__name__!r}." - ) from e - - # If the attribute is a function, call it with any args and kwargs - # to get the real application. - if inspect.isfunction(attr): - try: - app = attr(*args, **kwargs) - except TypeError as e: - if not _called_with_wrong_args(attr): - raise - - raise NoAppException( - f"The factory {app_name!r} in module" - f" {module.__name__!r} could not be called with the" - " specified arguments." - ) from e - else: - app = attr - - if isinstance(app, Flask): - return app - - raise NoAppException( - "A valid Flask application was not obtained from" - f" '{module.__name__}:{app_name}'." - ) - - -def prepare_import(path: str) -> str: - """Given a filename this will try to calculate the python path, add it - to the search path and return the actual module name that is expected. - """ - path = os.path.realpath(path) - - fname, ext = os.path.splitext(path) - if ext == ".py": - path = fname - - if os.path.basename(path) == "__init__": - path = os.path.dirname(path) - - module_name = [] - - # move up until outside package structure (no __init__.py) - while True: - path, name = os.path.split(path) - module_name.append(name) - - if not os.path.exists(os.path.join(path, "__init__.py")): - break - - if sys.path[0] != path: - sys.path.insert(0, path) - - return ".".join(module_name[::-1]) - - -@t.overload -def locate_app( - module_name: str, app_name: str | None, raise_if_not_found: t.Literal[True] = True -) -> Flask: ... - - -@t.overload -def locate_app( - module_name: str, app_name: str | None, raise_if_not_found: t.Literal[False] = ... -) -> Flask | None: ... - - -def locate_app( - module_name: str, app_name: str | None, raise_if_not_found: bool = True -) -> Flask | None: - try: - __import__(module_name) - except ImportError: - # Reraise the ImportError if it occurred within the imported module. - # Determine this by checking whether the trace has a depth > 1. - if sys.exc_info()[2].tb_next: # type: ignore[union-attr] - raise NoAppException( - f"While importing {module_name!r}, an ImportError was" - f" raised:\n\n{traceback.format_exc()}" - ) from None - elif raise_if_not_found: - raise NoAppException(f"Could not import {module_name!r}.") from None - else: - return None - - module = sys.modules[module_name] - - if app_name is None: - return find_best_app(module) - else: - return find_app_by_string(module, app_name) - - -def get_version(ctx: click.Context, param: click.Parameter, value: t.Any) -> None: - if not value or ctx.resilient_parsing: - return - - flask_version = importlib.metadata.version("flask") - werkzeug_version = importlib.metadata.version("werkzeug") - - click.echo( - f"Python {platform.python_version()}\n" - f"Flask {flask_version}\n" - f"Werkzeug {werkzeug_version}", - color=ctx.color, - ) - ctx.exit() - - -version_option = click.Option( - ["--version"], - help="Show the Flask version.", - expose_value=False, - callback=get_version, - is_flag=True, - is_eager=True, -) - - -class ScriptInfo: - """Helper object to deal with Flask applications. This is usually not - necessary to interface with as it's used internally in the dispatching - to click. In future versions of Flask this object will most likely play - a bigger role. Typically it's created automatically by the - :class:`FlaskGroup` but you can also manually create it and pass it - onwards as click object. - """ - - def __init__( - self, - app_import_path: str | None = None, - create_app: t.Callable[..., Flask] | None = None, - set_debug_flag: bool = True, - ) -> None: - #: Optionally the import path for the Flask application. - self.app_import_path = app_import_path - #: Optionally a function that is passed the script info to create - #: the instance of the application. - self.create_app = create_app - #: A dictionary with arbitrary data that can be associated with - #: this script info. - self.data: dict[t.Any, t.Any] = {} - self.set_debug_flag = set_debug_flag - self._loaded_app: Flask | None = None - - def load_app(self) -> Flask: - """Loads the Flask app (if not yet loaded) and returns it. Calling - this multiple times will just result in the already loaded app to - be returned. - """ - if self._loaded_app is not None: - return self._loaded_app - - if self.create_app is not None: - app: Flask | None = self.create_app() - else: - if self.app_import_path: - path, name = ( - re.split(r":(?![\\/])", self.app_import_path, maxsplit=1) + [None] - )[:2] - import_name = prepare_import(path) - app = locate_app(import_name, name) - else: - for path in ("wsgi.py", "app.py"): - import_name = prepare_import(path) - app = locate_app(import_name, None, raise_if_not_found=False) - - if app is not None: - break - - if app is None: - raise NoAppException( - "Could not locate a Flask application. Use the" - " 'flask --app' option, 'FLASK_APP' environment" - " variable, or a 'wsgi.py' or 'app.py' file in the" - " current directory." - ) - - if self.set_debug_flag: - # Update the app's debug flag through the descriptor so that - # other values repopulate as well. - app.debug = get_debug_flag() - - self._loaded_app = app - return app - - -pass_script_info = click.make_pass_decorator(ScriptInfo, ensure=True) - -F = t.TypeVar("F", bound=t.Callable[..., t.Any]) - - -def with_appcontext(f: F) -> F: - """Wraps a callback so that it's guaranteed to be executed with the - script's application context. - - Custom commands (and their options) registered under ``app.cli`` or - ``blueprint.cli`` will always have an app context available, this - decorator is not required in that case. - - .. versionchanged:: 2.2 - The app context is active for subcommands as well as the - decorated callback. The app context is always available to - ``app.cli`` command and parameter callbacks. - """ - - @click.pass_context - def decorator(ctx: click.Context, /, *args: t.Any, **kwargs: t.Any) -> t.Any: - if not current_app: - app = ctx.ensure_object(ScriptInfo).load_app() - ctx.with_resource(app.app_context()) - - return ctx.invoke(f, *args, **kwargs) - - return update_wrapper(decorator, f) # type: ignore[return-value] - - -class AppGroup(click.Group): - """This works similar to a regular click :class:`~click.Group` but it - changes the behavior of the :meth:`command` decorator so that it - automatically wraps the functions in :func:`with_appcontext`. - - Not to be confused with :class:`FlaskGroup`. - """ - - def command( # type: ignore[override] - self, *args: t.Any, **kwargs: t.Any - ) -> t.Callable[[t.Callable[..., t.Any]], click.Command]: - """This works exactly like the method of the same name on a regular - :class:`click.Group` but it wraps callbacks in :func:`with_appcontext` - unless it's disabled by passing ``with_appcontext=False``. - """ - wrap_for_ctx = kwargs.pop("with_appcontext", True) - - def decorator(f: t.Callable[..., t.Any]) -> click.Command: - if wrap_for_ctx: - f = with_appcontext(f) - return super(AppGroup, self).command(*args, **kwargs)(f) # type: ignore[no-any-return] - - return decorator - - def group( # type: ignore[override] - self, *args: t.Any, **kwargs: t.Any - ) -> t.Callable[[t.Callable[..., t.Any]], click.Group]: - """This works exactly like the method of the same name on a regular - :class:`click.Group` but it defaults the group class to - :class:`AppGroup`. - """ - kwargs.setdefault("cls", AppGroup) - return super().group(*args, **kwargs) # type: ignore[no-any-return] - - -def _set_app(ctx: click.Context, param: click.Option, value: str | None) -> str | None: - if value is None: - return None - - info = ctx.ensure_object(ScriptInfo) - info.app_import_path = value - return value - - -# This option is eager so the app will be available if --help is given. -# --help is also eager, so --app must be before it in the param list. -# no_args_is_help bypasses eager processing, so this option must be -# processed manually in that case to ensure FLASK_APP gets picked up. -_app_option = click.Option( - ["-A", "--app"], - metavar="IMPORT", - help=( - "The Flask application or factory function to load, in the form 'module:name'." - " Module can be a dotted import or file path. Name is not required if it is" - " 'app', 'application', 'create_app', or 'make_app', and can be 'name(args)' to" - " pass arguments." - ), - is_eager=True, - expose_value=False, - callback=_set_app, -) - - -def _set_debug(ctx: click.Context, param: click.Option, value: bool) -> bool | None: - # If the flag isn't provided, it will default to False. Don't use - # that, let debug be set by env in that case. - source = ctx.get_parameter_source(param.name) # type: ignore[arg-type] - - if source is not None and source in ( - ParameterSource.DEFAULT, - ParameterSource.DEFAULT_MAP, - ): - return None - - # Set with env var instead of ScriptInfo.load so that it can be - # accessed early during a factory function. - os.environ["FLASK_DEBUG"] = "1" if value else "0" - return value - - -_debug_option = click.Option( - ["--debug/--no-debug"], - help="Set debug mode.", - expose_value=False, - callback=_set_debug, -) - - -def _env_file_callback( - ctx: click.Context, param: click.Option, value: str | None -) -> str | None: - if value is None: - return None - - import importlib - - try: - importlib.import_module("dotenv") - except ImportError: - raise click.BadParameter( - "python-dotenv must be installed to load an env file.", - ctx=ctx, - param=param, - ) from None - - # Don't check FLASK_SKIP_DOTENV, that only disables automatically - # loading .env and .flaskenv files. - load_dotenv(value) - return value - - -# This option is eager so env vars are loaded as early as possible to be -# used by other options. -_env_file_option = click.Option( - ["-e", "--env-file"], - type=click.Path(exists=True, dir_okay=False), - help="Load environment variables from this file. python-dotenv must be installed.", - is_eager=True, - expose_value=False, - callback=_env_file_callback, -) - - -class FlaskGroup(AppGroup): - """Special subclass of the :class:`AppGroup` group that supports - loading more commands from the configured Flask app. Normally a - developer does not have to interface with this class but there are - some very advanced use cases for which it makes sense to create an - instance of this. see :ref:`custom-scripts`. - - :param add_default_commands: if this is True then the default run and - shell commands will be added. - :param add_version_option: adds the ``--version`` option. - :param create_app: an optional callback that is passed the script info and - returns the loaded app. - :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv` - files to set environment variables. Will also change the working - directory to the directory containing the first file found. - :param set_debug_flag: Set the app's debug flag. - - .. versionchanged:: 2.2 - Added the ``-A/--app``, ``--debug/--no-debug``, ``-e/--env-file`` options. - - .. versionchanged:: 2.2 - An app context is pushed when running ``app.cli`` commands, so - ``@with_appcontext`` is no longer required for those commands. - - .. versionchanged:: 1.0 - If installed, python-dotenv will be used to load environment variables - from :file:`.env` and :file:`.flaskenv` files. - """ - - def __init__( - self, - add_default_commands: bool = True, - create_app: t.Callable[..., Flask] | None = None, - add_version_option: bool = True, - load_dotenv: bool = True, - set_debug_flag: bool = True, - **extra: t.Any, - ) -> None: - params = list(extra.pop("params", None) or ()) - # Processing is done with option callbacks instead of a group - # callback. This allows users to make a custom group callback - # without losing the behavior. --env-file must come first so - # that it is eagerly evaluated before --app. - params.extend((_env_file_option, _app_option, _debug_option)) - - if add_version_option: - params.append(version_option) - - if "context_settings" not in extra: - extra["context_settings"] = {} - - extra["context_settings"].setdefault("auto_envvar_prefix", "FLASK") - - super().__init__(params=params, **extra) - - self.create_app = create_app - self.load_dotenv = load_dotenv - self.set_debug_flag = set_debug_flag - - if add_default_commands: - self.add_command(run_command) - self.add_command(shell_command) - self.add_command(routes_command) - - self._loaded_plugin_commands = False - - def _load_plugin_commands(self) -> None: - if self._loaded_plugin_commands: - return - - if sys.version_info >= (3, 10): - from importlib import metadata - else: - # Use a backport on Python < 3.10. We technically have - # importlib.metadata on 3.8+, but the API changed in 3.10, - # so use the backport for consistency. - import importlib_metadata as metadata - - for ep in metadata.entry_points(group="flask.commands"): - self.add_command(ep.load(), ep.name) - - self._loaded_plugin_commands = True - - def get_command(self, ctx: click.Context, name: str) -> click.Command | None: - self._load_plugin_commands() - # Look up built-in and plugin commands, which should be - # available even if the app fails to load. - rv = super().get_command(ctx, name) - - if rv is not None: - return rv - - info = ctx.ensure_object(ScriptInfo) - - # Look up commands provided by the app, showing an error and - # continuing if the app couldn't be loaded. - try: - app = info.load_app() - except NoAppException as e: - click.secho(f"Error: {e.format_message()}\n", err=True, fg="red") - return None - - # Push an app context for the loaded app unless it is already - # active somehow. This makes the context available to parameter - # and command callbacks without needing @with_appcontext. - if not current_app or current_app._get_current_object() is not app: # type: ignore[attr-defined] - ctx.with_resource(app.app_context()) - - return app.cli.get_command(ctx, name) - - def list_commands(self, ctx: click.Context) -> list[str]: - self._load_plugin_commands() - # Start with the built-in and plugin commands. - rv = set(super().list_commands(ctx)) - info = ctx.ensure_object(ScriptInfo) - - # Add commands provided by the app, showing an error and - # continuing if the app couldn't be loaded. - try: - rv.update(info.load_app().cli.list_commands(ctx)) - except NoAppException as e: - # When an app couldn't be loaded, show the error message - # without the traceback. - click.secho(f"Error: {e.format_message()}\n", err=True, fg="red") - except Exception: - # When any other errors occurred during loading, show the - # full traceback. - click.secho(f"{traceback.format_exc()}\n", err=True, fg="red") - - return sorted(rv) - - def make_context( - self, - info_name: str | None, - args: list[str], - parent: click.Context | None = None, - **extra: t.Any, - ) -> click.Context: - # Set a flag to tell app.run to become a no-op. If app.run was - # not in a __name__ == __main__ guard, it would start the server - # when importing, blocking whatever command is being called. - os.environ["FLASK_RUN_FROM_CLI"] = "true" - - # Attempt to load .env and .flask env files. The --env-file - # option can cause another file to be loaded. - if get_load_dotenv(self.load_dotenv): - load_dotenv() - - if "obj" not in extra and "obj" not in self.context_settings: - extra["obj"] = ScriptInfo( - create_app=self.create_app, set_debug_flag=self.set_debug_flag - ) - - return super().make_context(info_name, args, parent=parent, **extra) - - def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]: - if not args and self.no_args_is_help: - # Attempt to load --env-file and --app early in case they - # were given as env vars. Otherwise no_args_is_help will not - # see commands from app.cli. - _env_file_option.handle_parse_result(ctx, {}, []) - _app_option.handle_parse_result(ctx, {}, []) - - return super().parse_args(ctx, args) - - -def _path_is_ancestor(path: str, other: str) -> bool: - """Take ``other`` and remove the length of ``path`` from it. Then join it - to ``path``. If it is the original value, ``path`` is an ancestor of - ``other``.""" - return os.path.join(path, other[len(path) :].lstrip(os.sep)) == other - - -def load_dotenv(path: str | os.PathLike[str] | None = None) -> bool: - """Load "dotenv" files in order of precedence to set environment variables. - - If an env var is already set it is not overwritten, so earlier files in the - list are preferred over later files. - - This is a no-op if `python-dotenv`_ is not installed. - - .. _python-dotenv: https://github.com/theskumar/python-dotenv#readme - - :param path: Load the file at this location instead of searching. - :return: ``True`` if a file was loaded. - - .. versionchanged:: 2.0 - The current directory is not changed to the location of the - loaded file. - - .. versionchanged:: 2.0 - When loading the env files, set the default encoding to UTF-8. - - .. versionchanged:: 1.1.0 - Returns ``False`` when python-dotenv is not installed, or when - the given path isn't a file. - - .. versionadded:: 1.0 - """ - try: - import dotenv - except ImportError: - if path or os.path.isfile(".env") or os.path.isfile(".flaskenv"): - click.secho( - " * Tip: There are .env or .flaskenv files present." - ' Do "pip install python-dotenv" to use them.', - fg="yellow", - err=True, - ) - - return False - - # Always return after attempting to load a given path, don't load - # the default files. - if path is not None: - if os.path.isfile(path): - return dotenv.load_dotenv(path, encoding="utf-8") - - return False - - loaded = False - - for name in (".env", ".flaskenv"): - path = dotenv.find_dotenv(name, usecwd=True) - - if not path: - continue - - dotenv.load_dotenv(path, encoding="utf-8") - loaded = True - - return loaded # True if at least one file was located and loaded. - - -def show_server_banner(debug: bool, app_import_path: str | None) -> None: - """Show extra startup messages the first time the server is run, - ignoring the reloader. - """ - if is_running_from_reloader(): - return - - if app_import_path is not None: - click.echo(f" * Serving Flask app '{app_import_path}'") - - if debug is not None: - click.echo(f" * Debug mode: {'on' if debug else 'off'}") - - -class CertParamType(click.ParamType): - """Click option type for the ``--cert`` option. Allows either an - existing file, the string ``'adhoc'``, or an import for a - :class:`~ssl.SSLContext` object. - """ - - name = "path" - - def __init__(self) -> None: - self.path_type = click.Path(exists=True, dir_okay=False, resolve_path=True) - - def convert( - self, value: t.Any, param: click.Parameter | None, ctx: click.Context | None - ) -> t.Any: - try: - import ssl - except ImportError: - raise click.BadParameter( - 'Using "--cert" requires Python to be compiled with SSL support.', - ctx, - param, - ) from None - - try: - return self.path_type(value, param, ctx) - except click.BadParameter: - value = click.STRING(value, param, ctx).lower() - - if value == "adhoc": - try: - import cryptography # noqa: F401 - except ImportError: - raise click.BadParameter( - "Using ad-hoc certificates requires the cryptography library.", - ctx, - param, - ) from None - - return value - - obj = import_string(value, silent=True) - - if isinstance(obj, ssl.SSLContext): - return obj - - raise - - -def _validate_key(ctx: click.Context, param: click.Parameter, value: t.Any) -> t.Any: - """The ``--key`` option must be specified when ``--cert`` is a file. - Modifies the ``cert`` param to be a ``(cert, key)`` pair if needed. - """ - cert = ctx.params.get("cert") - is_adhoc = cert == "adhoc" - - try: - import ssl - except ImportError: - is_context = False - else: - is_context = isinstance(cert, ssl.SSLContext) - - if value is not None: - if is_adhoc: - raise click.BadParameter( - 'When "--cert" is "adhoc", "--key" is not used.', ctx, param - ) - - if is_context: - raise click.BadParameter( - 'When "--cert" is an SSLContext object, "--key" is not used.', - ctx, - param, - ) - - if not cert: - raise click.BadParameter('"--cert" must also be specified.', ctx, param) - - ctx.params["cert"] = cert, value - - else: - if cert and not (is_adhoc or is_context): - raise click.BadParameter('Required when using "--cert".', ctx, param) - - return value - - -class SeparatedPathType(click.Path): - """Click option type that accepts a list of values separated by the - OS's path separator (``:``, ``;`` on Windows). Each value is - validated as a :class:`click.Path` type. - """ - - def convert( - self, value: t.Any, param: click.Parameter | None, ctx: click.Context | None - ) -> t.Any: - items = self.split_envvar_value(value) - # can't call no-arg super() inside list comprehension until Python 3.12 - super_convert = super().convert - return [super_convert(item, param, ctx) for item in items] - - -@click.command("run", short_help="Run a development server.") -@click.option("--host", "-h", default="127.0.0.1", help="The interface to bind to.") -@click.option("--port", "-p", default=5000, help="The port to bind to.") -@click.option( - "--cert", - type=CertParamType(), - help="Specify a certificate file to use HTTPS.", - is_eager=True, -) -@click.option( - "--key", - type=click.Path(exists=True, dir_okay=False, resolve_path=True), - callback=_validate_key, - expose_value=False, - help="The key file to use when specifying a certificate.", -) -@click.option( - "--reload/--no-reload", - default=None, - help="Enable or disable the reloader. By default the reloader " - "is active if debug is enabled.", -) -@click.option( - "--debugger/--no-debugger", - default=None, - help="Enable or disable the debugger. By default the debugger " - "is active if debug is enabled.", -) -@click.option( - "--with-threads/--without-threads", - default=True, - help="Enable or disable multithreading.", -) -@click.option( - "--extra-files", - default=None, - type=SeparatedPathType(), - help=( - "Extra files that trigger a reload on change. Multiple paths" - f" are separated by {os.path.pathsep!r}." - ), -) -@click.option( - "--exclude-patterns", - default=None, - type=SeparatedPathType(), - help=( - "Files matching these fnmatch patterns will not trigger a reload" - " on change. Multiple patterns are separated by" - f" {os.path.pathsep!r}." - ), -) -@pass_script_info -def run_command( - info: ScriptInfo, - host: str, - port: int, - reload: bool, - debugger: bool, - with_threads: bool, - cert: ssl.SSLContext | tuple[str, str | None] | t.Literal["adhoc"] | None, - extra_files: list[str] | None, - exclude_patterns: list[str] | None, -) -> None: - """Run a local development server. - - This server is for development purposes only. It does not provide - the stability, security, or performance of production WSGI servers. - - The reloader and debugger are enabled by default with the '--debug' - option. - """ - try: - app: WSGIApplication = info.load_app() - except Exception as e: - if is_running_from_reloader(): - # When reloading, print out the error immediately, but raise - # it later so the debugger or server can handle it. - traceback.print_exc() - err = e - - def app( - environ: WSGIEnvironment, start_response: StartResponse - ) -> cabc.Iterable[bytes]: - raise err from None - - else: - # When not reloading, raise the error immediately so the - # command fails. - raise e from None - - debug = get_debug_flag() - - if reload is None: - reload = debug - - if debugger is None: - debugger = debug - - show_server_banner(debug, info.app_import_path) - - run_simple( - host, - port, - app, - use_reloader=reload, - use_debugger=debugger, - threaded=with_threads, - ssl_context=cert, - extra_files=extra_files, - exclude_patterns=exclude_patterns, - ) - - -run_command.params.insert(0, _debug_option) - - -@click.command("shell", short_help="Run a shell in the app context.") -@with_appcontext -def shell_command() -> None: - """Run an interactive Python shell in the context of a given - Flask application. The application will populate the default - namespace of this shell according to its configuration. - - This is useful for executing small snippets of management code - without having to manually configure the application. - """ - import code - - banner = ( - f"Python {sys.version} on {sys.platform}\n" - f"App: {current_app.import_name}\n" - f"Instance: {current_app.instance_path}" - ) - ctx: dict[str, t.Any] = {} - - # Support the regular Python interpreter startup script if someone - # is using it. - startup = os.environ.get("PYTHONSTARTUP") - if startup and os.path.isfile(startup): - with open(startup) as f: - eval(compile(f.read(), startup, "exec"), ctx) - - ctx.update(current_app.make_shell_context()) - - # Site, customize, or startup script can set a hook to call when - # entering interactive mode. The default one sets up readline with - # tab and history completion. - interactive_hook = getattr(sys, "__interactivehook__", None) - - if interactive_hook is not None: - try: - import readline - from rlcompleter import Completer - except ImportError: - pass - else: - # rlcompleter uses __main__.__dict__ by default, which is - # flask.__main__. Use the shell context instead. - readline.set_completer(Completer(ctx).complete) - - interactive_hook() - - code.interact(banner=banner, local=ctx) - - -@click.command("routes", short_help="Show the routes for the app.") -@click.option( - "--sort", - "-s", - type=click.Choice(("endpoint", "methods", "domain", "rule", "match")), - default="endpoint", - help=( - "Method to sort routes by. 'match' is the order that Flask will match routes" - " when dispatching a request." - ), -) -@click.option("--all-methods", is_flag=True, help="Show HEAD and OPTIONS methods.") -@with_appcontext -def routes_command(sort: str, all_methods: bool) -> None: - """Show all registered routes with endpoints and methods.""" - rules = list(current_app.url_map.iter_rules()) - - if not rules: - click.echo("No routes were registered.") - return - - ignored_methods = set() if all_methods else {"HEAD", "OPTIONS"} - host_matching = current_app.url_map.host_matching - has_domain = any(rule.host if host_matching else rule.subdomain for rule in rules) - rows = [] - - for rule in rules: - row = [ - rule.endpoint, - ", ".join(sorted((rule.methods or set()) - ignored_methods)), - ] - - if has_domain: - row.append((rule.host if host_matching else rule.subdomain) or "") - - row.append(rule.rule) - rows.append(row) - - headers = ["Endpoint", "Methods"] - sorts = ["endpoint", "methods"] - - if has_domain: - headers.append("Host" if host_matching else "Subdomain") - sorts.append("domain") - - headers.append("Rule") - sorts.append("rule") - - try: - rows.sort(key=itemgetter(sorts.index(sort))) - except ValueError: - pass - - rows.insert(0, headers) - widths = [max(len(row[i]) for row in rows) for i in range(len(headers))] - rows.insert(1, ["-" * w for w in widths]) - template = " ".join(f"{{{i}:<{w}}}" for i, w in enumerate(widths)) - - for row in rows: - click.echo(template.format(*row)) - - -cli = FlaskGroup( - name="flask", - help="""\ -A general utility script for Flask applications. - -An application to load must be given with the '--app' option, -'FLASK_APP' environment variable, or with a 'wsgi.py' or 'app.py' file -in the current directory. -""", -) - - -def main() -> None: - cli.main() - - -if __name__ == "__main__": - main() diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/config.py b/plotter-app/venv/lib/python3.8/site-packages/flask/config.py deleted file mode 100644 index 7e3ba17..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/config.py +++ /dev/null @@ -1,370 +0,0 @@ -from __future__ import annotations - -import errno -import json -import os -import types -import typing as t - -from werkzeug.utils import import_string - -if t.TYPE_CHECKING: - import typing_extensions as te - - from .sansio.app import App - - -T = t.TypeVar("T") - - -class ConfigAttribute(t.Generic[T]): - """Makes an attribute forward to the config""" - - def __init__( - self, name: str, get_converter: t.Callable[[t.Any], T] | None = None - ) -> None: - self.__name__ = name - self.get_converter = get_converter - - @t.overload - def __get__(self, obj: None, owner: None) -> te.Self: ... - - @t.overload - def __get__(self, obj: App, owner: type[App]) -> T: ... - - def __get__(self, obj: App | None, owner: type[App] | None = None) -> T | te.Self: - if obj is None: - return self - - rv = obj.config[self.__name__] - - if self.get_converter is not None: - rv = self.get_converter(rv) - - return rv # type: ignore[no-any-return] - - def __set__(self, obj: App, value: t.Any) -> None: - obj.config[self.__name__] = value - - -class Config(dict): # type: ignore[type-arg] - """Works exactly like a dict but provides ways to fill it from files - or special dictionaries. There are two common patterns to populate the - config. - - Either you can fill the config from a config file:: - - app.config.from_pyfile('yourconfig.cfg') - - Or alternatively you can define the configuration options in the - module that calls :meth:`from_object` or provide an import path to - a module that should be loaded. It is also possible to tell it to - use the same module and with that provide the configuration values - just before the call:: - - DEBUG = True - SECRET_KEY = 'development key' - app.config.from_object(__name__) - - In both cases (loading from any Python file or loading from modules), - only uppercase keys are added to the config. This makes it possible to use - lowercase values in the config file for temporary values that are not added - to the config or to define the config keys in the same file that implements - the application. - - Probably the most interesting way to load configurations is from an - environment variable pointing to a file:: - - app.config.from_envvar('YOURAPPLICATION_SETTINGS') - - In this case before launching the application you have to set this - environment variable to the file you want to use. On Linux and OS X - use the export statement:: - - export YOURAPPLICATION_SETTINGS='/path/to/config/file' - - On windows use `set` instead. - - :param root_path: path to which files are read relative from. When the - config object is created by the application, this is - the application's :attr:`~flask.Flask.root_path`. - :param defaults: an optional dictionary of default values - """ - - def __init__( - self, - root_path: str | os.PathLike[str], - defaults: dict[str, t.Any] | None = None, - ) -> None: - super().__init__(defaults or {}) - self.root_path = root_path - - def from_envvar(self, variable_name: str, silent: bool = False) -> bool: - """Loads a configuration from an environment variable pointing to - a configuration file. This is basically just a shortcut with nicer - error messages for this line of code:: - - app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS']) - - :param variable_name: name of the environment variable - :param silent: set to ``True`` if you want silent failure for missing - files. - :return: ``True`` if the file was loaded successfully. - """ - rv = os.environ.get(variable_name) - if not rv: - if silent: - return False - raise RuntimeError( - f"The environment variable {variable_name!r} is not set" - " and as such configuration could not be loaded. Set" - " this variable and make it point to a configuration" - " file" - ) - return self.from_pyfile(rv, silent=silent) - - def from_prefixed_env( - self, prefix: str = "FLASK", *, loads: t.Callable[[str], t.Any] = json.loads - ) -> bool: - """Load any environment variables that start with ``FLASK_``, - dropping the prefix from the env key for the config key. Values - are passed through a loading function to attempt to convert them - to more specific types than strings. - - Keys are loaded in :func:`sorted` order. - - The default loading function attempts to parse values as any - valid JSON type, including dicts and lists. - - Specific items in nested dicts can be set by separating the - keys with double underscores (``__``). If an intermediate key - doesn't exist, it will be initialized to an empty dict. - - :param prefix: Load env vars that start with this prefix, - separated with an underscore (``_``). - :param loads: Pass each string value to this function and use - the returned value as the config value. If any error is - raised it is ignored and the value remains a string. The - default is :func:`json.loads`. - - .. versionadded:: 2.1 - """ - prefix = f"{prefix}_" - len_prefix = len(prefix) - - for key in sorted(os.environ): - if not key.startswith(prefix): - continue - - value = os.environ[key] - - try: - value = loads(value) - except Exception: - # Keep the value as a string if loading failed. - pass - - # Change to key.removeprefix(prefix) on Python >= 3.9. - key = key[len_prefix:] - - if "__" not in key: - # A non-nested key, set directly. - self[key] = value - continue - - # Traverse nested dictionaries with keys separated by "__". - current = self - *parts, tail = key.split("__") - - for part in parts: - # If an intermediate dict does not exist, create it. - if part not in current: - current[part] = {} - - current = current[part] - - current[tail] = value - - return True - - def from_pyfile( - self, filename: str | os.PathLike[str], silent: bool = False - ) -> bool: - """Updates the values in the config from a Python file. This function - behaves as if the file was imported as module with the - :meth:`from_object` function. - - :param filename: the filename of the config. This can either be an - absolute filename or a filename relative to the - root path. - :param silent: set to ``True`` if you want silent failure for missing - files. - :return: ``True`` if the file was loaded successfully. - - .. versionadded:: 0.7 - `silent` parameter. - """ - filename = os.path.join(self.root_path, filename) - d = types.ModuleType("config") - d.__file__ = filename - try: - with open(filename, mode="rb") as config_file: - exec(compile(config_file.read(), filename, "exec"), d.__dict__) - except OSError as e: - if silent and e.errno in (errno.ENOENT, errno.EISDIR, errno.ENOTDIR): - return False - e.strerror = f"Unable to load configuration file ({e.strerror})" - raise - self.from_object(d) - return True - - def from_object(self, obj: object | str) -> None: - """Updates the values from the given object. An object can be of one - of the following two types: - - - a string: in this case the object with that name will be imported - - an actual object reference: that object is used directly - - Objects are usually either modules or classes. :meth:`from_object` - loads only the uppercase attributes of the module/class. A ``dict`` - object will not work with :meth:`from_object` because the keys of a - ``dict`` are not attributes of the ``dict`` class. - - Example of module-based configuration:: - - app.config.from_object('yourapplication.default_config') - from yourapplication import default_config - app.config.from_object(default_config) - - Nothing is done to the object before loading. If the object is a - class and has ``@property`` attributes, it needs to be - instantiated before being passed to this method. - - You should not use this function to load the actual configuration but - rather configuration defaults. The actual config should be loaded - with :meth:`from_pyfile` and ideally from a location not within the - package because the package might be installed system wide. - - See :ref:`config-dev-prod` for an example of class-based configuration - using :meth:`from_object`. - - :param obj: an import name or object - """ - if isinstance(obj, str): - obj = import_string(obj) - for key in dir(obj): - if key.isupper(): - self[key] = getattr(obj, key) - - def from_file( - self, - filename: str | os.PathLike[str], - load: t.Callable[[t.IO[t.Any]], t.Mapping[str, t.Any]], - silent: bool = False, - text: bool = True, - ) -> bool: - """Update the values in the config from a file that is loaded - using the ``load`` parameter. The loaded data is passed to the - :meth:`from_mapping` method. - - .. code-block:: python - - import json - app.config.from_file("config.json", load=json.load) - - import tomllib - app.config.from_file("config.toml", load=tomllib.load, text=False) - - :param filename: The path to the data file. This can be an - absolute path or relative to the config root path. - :param load: A callable that takes a file handle and returns a - mapping of loaded data from the file. - :type load: ``Callable[[Reader], Mapping]`` where ``Reader`` - implements a ``read`` method. - :param silent: Ignore the file if it doesn't exist. - :param text: Open the file in text or binary mode. - :return: ``True`` if the file was loaded successfully. - - .. versionchanged:: 2.3 - The ``text`` parameter was added. - - .. versionadded:: 2.0 - """ - filename = os.path.join(self.root_path, filename) - - try: - with open(filename, "r" if text else "rb") as f: - obj = load(f) - except OSError as e: - if silent and e.errno in (errno.ENOENT, errno.EISDIR): - return False - - e.strerror = f"Unable to load configuration file ({e.strerror})" - raise - - return self.from_mapping(obj) - - def from_mapping( - self, mapping: t.Mapping[str, t.Any] | None = None, **kwargs: t.Any - ) -> bool: - """Updates the config like :meth:`update` ignoring items with - non-upper keys. - - :return: Always returns ``True``. - - .. versionadded:: 0.11 - """ - mappings: dict[str, t.Any] = {} - if mapping is not None: - mappings.update(mapping) - mappings.update(kwargs) - for key, value in mappings.items(): - if key.isupper(): - self[key] = value - return True - - def get_namespace( - self, namespace: str, lowercase: bool = True, trim_namespace: bool = True - ) -> dict[str, t.Any]: - """Returns a dictionary containing a subset of configuration options - that match the specified namespace/prefix. Example usage:: - - app.config['IMAGE_STORE_TYPE'] = 'fs' - app.config['IMAGE_STORE_PATH'] = '/var/app/images' - app.config['IMAGE_STORE_BASE_URL'] = 'http://img.website.com' - image_store_config = app.config.get_namespace('IMAGE_STORE_') - - The resulting dictionary `image_store_config` would look like:: - - { - 'type': 'fs', - 'path': '/var/app/images', - 'base_url': 'http://img.website.com' - } - - This is often useful when configuration options map directly to - keyword arguments in functions or class constructors. - - :param namespace: a configuration namespace - :param lowercase: a flag indicating if the keys of the resulting - dictionary should be lowercase - :param trim_namespace: a flag indicating if the keys of the resulting - dictionary should not include the namespace - - .. versionadded:: 0.11 - """ - rv = {} - for k, v in self.items(): - if not k.startswith(namespace): - continue - if trim_namespace: - key = k[len(namespace) :] - else: - key = k - if lowercase: - key = key.lower() - rv[key] = v - return rv - - def __repr__(self) -> str: - return f"<{type(self).__name__} {dict.__repr__(self)}>" diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/ctx.py b/plotter-app/venv/lib/python3.8/site-packages/flask/ctx.py deleted file mode 100644 index 9b164d3..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/ctx.py +++ /dev/null @@ -1,449 +0,0 @@ -from __future__ import annotations - -import contextvars -import sys -import typing as t -from functools import update_wrapper -from types import TracebackType - -from werkzeug.exceptions import HTTPException - -from . import typing as ft -from .globals import _cv_app -from .globals import _cv_request -from .signals import appcontext_popped -from .signals import appcontext_pushed - -if t.TYPE_CHECKING: # pragma: no cover - from _typeshed.wsgi import WSGIEnvironment - - from .app import Flask - from .sessions import SessionMixin - from .wrappers import Request - - -# a singleton sentinel value for parameter defaults -_sentinel = object() - - -class _AppCtxGlobals: - """A plain object. Used as a namespace for storing data during an - application context. - - Creating an app context automatically creates this object, which is - made available as the :data:`g` proxy. - - .. describe:: 'key' in g - - Check whether an attribute is present. - - .. versionadded:: 0.10 - - .. describe:: iter(g) - - Return an iterator over the attribute names. - - .. versionadded:: 0.10 - """ - - # Define attr methods to let mypy know this is a namespace object - # that has arbitrary attributes. - - def __getattr__(self, name: str) -> t.Any: - try: - return self.__dict__[name] - except KeyError: - raise AttributeError(name) from None - - def __setattr__(self, name: str, value: t.Any) -> None: - self.__dict__[name] = value - - def __delattr__(self, name: str) -> None: - try: - del self.__dict__[name] - except KeyError: - raise AttributeError(name) from None - - def get(self, name: str, default: t.Any | None = None) -> t.Any: - """Get an attribute by name, or a default value. Like - :meth:`dict.get`. - - :param name: Name of attribute to get. - :param default: Value to return if the attribute is not present. - - .. versionadded:: 0.10 - """ - return self.__dict__.get(name, default) - - def pop(self, name: str, default: t.Any = _sentinel) -> t.Any: - """Get and remove an attribute by name. Like :meth:`dict.pop`. - - :param name: Name of attribute to pop. - :param default: Value to return if the attribute is not present, - instead of raising a ``KeyError``. - - .. versionadded:: 0.11 - """ - if default is _sentinel: - return self.__dict__.pop(name) - else: - return self.__dict__.pop(name, default) - - def setdefault(self, name: str, default: t.Any = None) -> t.Any: - """Get the value of an attribute if it is present, otherwise - set and return a default value. Like :meth:`dict.setdefault`. - - :param name: Name of attribute to get. - :param default: Value to set and return if the attribute is not - present. - - .. versionadded:: 0.11 - """ - return self.__dict__.setdefault(name, default) - - def __contains__(self, item: str) -> bool: - return item in self.__dict__ - - def __iter__(self) -> t.Iterator[str]: - return iter(self.__dict__) - - def __repr__(self) -> str: - ctx = _cv_app.get(None) - if ctx is not None: - return f"" - return object.__repr__(self) - - -def after_this_request( - f: ft.AfterRequestCallable[t.Any], -) -> ft.AfterRequestCallable[t.Any]: - """Executes a function after this request. This is useful to modify - response objects. The function is passed the response object and has - to return the same or a new one. - - Example:: - - @app.route('/') - def index(): - @after_this_request - def add_header(response): - response.headers['X-Foo'] = 'Parachute' - return response - return 'Hello World!' - - This is more useful if a function other than the view function wants to - modify a response. For instance think of a decorator that wants to add - some headers without converting the return value into a response object. - - .. versionadded:: 0.9 - """ - ctx = _cv_request.get(None) - - if ctx is None: - raise RuntimeError( - "'after_this_request' can only be used when a request" - " context is active, such as in a view function." - ) - - ctx._after_request_functions.append(f) - return f - - -F = t.TypeVar("F", bound=t.Callable[..., t.Any]) - - -def copy_current_request_context(f: F) -> F: - """A helper function that decorates a function to retain the current - request context. This is useful when working with greenlets. The moment - the function is decorated a copy of the request context is created and - then pushed when the function is called. The current session is also - included in the copied request context. - - Example:: - - import gevent - from flask import copy_current_request_context - - @app.route('/') - def index(): - @copy_current_request_context - def do_some_work(): - # do some work here, it can access flask.request or - # flask.session like you would otherwise in the view function. - ... - gevent.spawn(do_some_work) - return 'Regular response' - - .. versionadded:: 0.10 - """ - ctx = _cv_request.get(None) - - if ctx is None: - raise RuntimeError( - "'copy_current_request_context' can only be used when a" - " request context is active, such as in a view function." - ) - - ctx = ctx.copy() - - def wrapper(*args: t.Any, **kwargs: t.Any) -> t.Any: - with ctx: # type: ignore[union-attr] - return ctx.app.ensure_sync(f)(*args, **kwargs) # type: ignore[union-attr] - - return update_wrapper(wrapper, f) # type: ignore[return-value] - - -def has_request_context() -> bool: - """If you have code that wants to test if a request context is there or - not this function can be used. For instance, you may want to take advantage - of request information if the request object is available, but fail - silently if it is unavailable. - - :: - - class User(db.Model): - - def __init__(self, username, remote_addr=None): - self.username = username - if remote_addr is None and has_request_context(): - remote_addr = request.remote_addr - self.remote_addr = remote_addr - - Alternatively you can also just test any of the context bound objects - (such as :class:`request` or :class:`g`) for truthness:: - - class User(db.Model): - - def __init__(self, username, remote_addr=None): - self.username = username - if remote_addr is None and request: - remote_addr = request.remote_addr - self.remote_addr = remote_addr - - .. versionadded:: 0.7 - """ - return _cv_request.get(None) is not None - - -def has_app_context() -> bool: - """Works like :func:`has_request_context` but for the application - context. You can also just do a boolean check on the - :data:`current_app` object instead. - - .. versionadded:: 0.9 - """ - return _cv_app.get(None) is not None - - -class AppContext: - """The app context contains application-specific information. An app - context is created and pushed at the beginning of each request if - one is not already active. An app context is also pushed when - running CLI commands. - """ - - def __init__(self, app: Flask) -> None: - self.app = app - self.url_adapter = app.create_url_adapter(None) - self.g: _AppCtxGlobals = app.app_ctx_globals_class() - self._cv_tokens: list[contextvars.Token[AppContext]] = [] - - def push(self) -> None: - """Binds the app context to the current context.""" - self._cv_tokens.append(_cv_app.set(self)) - appcontext_pushed.send(self.app, _async_wrapper=self.app.ensure_sync) - - def pop(self, exc: BaseException | None = _sentinel) -> None: # type: ignore - """Pops the app context.""" - try: - if len(self._cv_tokens) == 1: - if exc is _sentinel: - exc = sys.exc_info()[1] - self.app.do_teardown_appcontext(exc) - finally: - ctx = _cv_app.get() - _cv_app.reset(self._cv_tokens.pop()) - - if ctx is not self: - raise AssertionError( - f"Popped wrong app context. ({ctx!r} instead of {self!r})" - ) - - appcontext_popped.send(self.app, _async_wrapper=self.app.ensure_sync) - - def __enter__(self) -> AppContext: - self.push() - return self - - def __exit__( - self, - exc_type: type | None, - exc_value: BaseException | None, - tb: TracebackType | None, - ) -> None: - self.pop(exc_value) - - -class RequestContext: - """The request context contains per-request information. The Flask - app creates and pushes it at the beginning of the request, then pops - it at the end of the request. It will create the URL adapter and - request object for the WSGI environment provided. - - Do not attempt to use this class directly, instead use - :meth:`~flask.Flask.test_request_context` and - :meth:`~flask.Flask.request_context` to create this object. - - When the request context is popped, it will evaluate all the - functions registered on the application for teardown execution - (:meth:`~flask.Flask.teardown_request`). - - The request context is automatically popped at the end of the - request. When using the interactive debugger, the context will be - restored so ``request`` is still accessible. Similarly, the test - client can preserve the context after the request ends. However, - teardown functions may already have closed some resources such as - database connections. - """ - - def __init__( - self, - app: Flask, - environ: WSGIEnvironment, - request: Request | None = None, - session: SessionMixin | None = None, - ) -> None: - self.app = app - if request is None: - request = app.request_class(environ) - request.json_module = app.json - self.request: Request = request - self.url_adapter = None - try: - self.url_adapter = app.create_url_adapter(self.request) - except HTTPException as e: - self.request.routing_exception = e - self.flashes: list[tuple[str, str]] | None = None - self.session: SessionMixin | None = session - # Functions that should be executed after the request on the response - # object. These will be called before the regular "after_request" - # functions. - self._after_request_functions: list[ft.AfterRequestCallable[t.Any]] = [] - - self._cv_tokens: list[ - tuple[contextvars.Token[RequestContext], AppContext | None] - ] = [] - - def copy(self) -> RequestContext: - """Creates a copy of this request context with the same request object. - This can be used to move a request context to a different greenlet. - Because the actual request object is the same this cannot be used to - move a request context to a different thread unless access to the - request object is locked. - - .. versionadded:: 0.10 - - .. versionchanged:: 1.1 - The current session object is used instead of reloading the original - data. This prevents `flask.session` pointing to an out-of-date object. - """ - return self.__class__( - self.app, - environ=self.request.environ, - request=self.request, - session=self.session, - ) - - def match_request(self) -> None: - """Can be overridden by a subclass to hook into the matching - of the request. - """ - try: - result = self.url_adapter.match(return_rule=True) # type: ignore - self.request.url_rule, self.request.view_args = result # type: ignore - except HTTPException as e: - self.request.routing_exception = e - - def push(self) -> None: - # Before we push the request context we have to ensure that there - # is an application context. - app_ctx = _cv_app.get(None) - - if app_ctx is None or app_ctx.app is not self.app: - app_ctx = self.app.app_context() - app_ctx.push() - else: - app_ctx = None - - self._cv_tokens.append((_cv_request.set(self), app_ctx)) - - # Open the session at the moment that the request context is available. - # This allows a custom open_session method to use the request context. - # Only open a new session if this is the first time the request was - # pushed, otherwise stream_with_context loses the session. - if self.session is None: - session_interface = self.app.session_interface - self.session = session_interface.open_session(self.app, self.request) - - if self.session is None: - self.session = session_interface.make_null_session(self.app) - - # Match the request URL after loading the session, so that the - # session is available in custom URL converters. - if self.url_adapter is not None: - self.match_request() - - def pop(self, exc: BaseException | None = _sentinel) -> None: # type: ignore - """Pops the request context and unbinds it by doing that. This will - also trigger the execution of functions registered by the - :meth:`~flask.Flask.teardown_request` decorator. - - .. versionchanged:: 0.9 - Added the `exc` argument. - """ - clear_request = len(self._cv_tokens) == 1 - - try: - if clear_request: - if exc is _sentinel: - exc = sys.exc_info()[1] - self.app.do_teardown_request(exc) - - request_close = getattr(self.request, "close", None) - if request_close is not None: - request_close() - finally: - ctx = _cv_request.get() - token, app_ctx = self._cv_tokens.pop() - _cv_request.reset(token) - - # get rid of circular dependencies at the end of the request - # so that we don't require the GC to be active. - if clear_request: - ctx.request.environ["werkzeug.request"] = None - - if app_ctx is not None: - app_ctx.pop(exc) - - if ctx is not self: - raise AssertionError( - f"Popped wrong request context. ({ctx!r} instead of {self!r})" - ) - - def __enter__(self) -> RequestContext: - self.push() - return self - - def __exit__( - self, - exc_type: type | None, - exc_value: BaseException | None, - tb: TracebackType | None, - ) -> None: - self.pop(exc_value) - - def __repr__(self) -> str: - return ( - f"<{type(self).__name__} {self.request.url!r}" - f" [{self.request.method}] of {self.app.name}>" - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/debughelpers.py b/plotter-app/venv/lib/python3.8/site-packages/flask/debughelpers.py deleted file mode 100644 index 2c8c4c4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/debughelpers.py +++ /dev/null @@ -1,178 +0,0 @@ -from __future__ import annotations - -import typing as t - -from jinja2.loaders import BaseLoader -from werkzeug.routing import RequestRedirect - -from .blueprints import Blueprint -from .globals import request_ctx -from .sansio.app import App - -if t.TYPE_CHECKING: - from .sansio.scaffold import Scaffold - from .wrappers import Request - - -class UnexpectedUnicodeError(AssertionError, UnicodeError): - """Raised in places where we want some better error reporting for - unexpected unicode or binary data. - """ - - -class DebugFilesKeyError(KeyError, AssertionError): - """Raised from request.files during debugging. The idea is that it can - provide a better error message than just a generic KeyError/BadRequest. - """ - - def __init__(self, request: Request, key: str) -> None: - form_matches = request.form.getlist(key) - buf = [ - f"You tried to access the file {key!r} in the request.files" - " dictionary but it does not exist. The mimetype for the" - f" request is {request.mimetype!r} instead of" - " 'multipart/form-data' which means that no file contents" - " were transmitted. To fix this error you should provide" - ' enctype="multipart/form-data" in your form.' - ] - if form_matches: - names = ", ".join(repr(x) for x in form_matches) - buf.append( - "\n\nThe browser instead transmitted some file names. " - f"This was submitted: {names}" - ) - self.msg = "".join(buf) - - def __str__(self) -> str: - return self.msg - - -class FormDataRoutingRedirect(AssertionError): - """This exception is raised in debug mode if a routing redirect - would cause the browser to drop the method or body. This happens - when method is not GET, HEAD or OPTIONS and the status code is not - 307 or 308. - """ - - def __init__(self, request: Request) -> None: - exc = request.routing_exception - assert isinstance(exc, RequestRedirect) - buf = [ - f"A request was sent to '{request.url}', but routing issued" - f" a redirect to the canonical URL '{exc.new_url}'." - ] - - if f"{request.base_url}/" == exc.new_url.partition("?")[0]: - buf.append( - " The URL was defined with a trailing slash. Flask" - " will redirect to the URL with a trailing slash if it" - " was accessed without one." - ) - - buf.append( - " Send requests to the canonical URL, or use 307 or 308 for" - " routing redirects. Otherwise, browsers will drop form" - " data.\n\n" - "This exception is only raised in debug mode." - ) - super().__init__("".join(buf)) - - -def attach_enctype_error_multidict(request: Request) -> None: - """Patch ``request.files.__getitem__`` to raise a descriptive error - about ``enctype=multipart/form-data``. - - :param request: The request to patch. - :meta private: - """ - oldcls = request.files.__class__ - - class newcls(oldcls): # type: ignore[valid-type, misc] - def __getitem__(self, key: str) -> t.Any: - try: - return super().__getitem__(key) - except KeyError as e: - if key not in request.form: - raise - - raise DebugFilesKeyError(request, key).with_traceback( - e.__traceback__ - ) from None - - newcls.__name__ = oldcls.__name__ - newcls.__module__ = oldcls.__module__ - request.files.__class__ = newcls - - -def _dump_loader_info(loader: BaseLoader) -> t.Iterator[str]: - yield f"class: {type(loader).__module__}.{type(loader).__name__}" - for key, value in sorted(loader.__dict__.items()): - if key.startswith("_"): - continue - if isinstance(value, (tuple, list)): - if not all(isinstance(x, str) for x in value): - continue - yield f"{key}:" - for item in value: - yield f" - {item}" - continue - elif not isinstance(value, (str, int, float, bool)): - continue - yield f"{key}: {value!r}" - - -def explain_template_loading_attempts( - app: App, - template: str, - attempts: list[ - tuple[ - BaseLoader, - Scaffold, - tuple[str, str | None, t.Callable[[], bool] | None] | None, - ] - ], -) -> None: - """This should help developers understand what failed""" - info = [f"Locating template {template!r}:"] - total_found = 0 - blueprint = None - if request_ctx and request_ctx.request.blueprint is not None: - blueprint = request_ctx.request.blueprint - - for idx, (loader, srcobj, triple) in enumerate(attempts): - if isinstance(srcobj, App): - src_info = f"application {srcobj.import_name!r}" - elif isinstance(srcobj, Blueprint): - src_info = f"blueprint {srcobj.name!r} ({srcobj.import_name})" - else: - src_info = repr(srcobj) - - info.append(f"{idx + 1:5}: trying loader of {src_info}") - - for line in _dump_loader_info(loader): - info.append(f" {line}") - - if triple is None: - detail = "no match" - else: - detail = f"found ({triple[1] or ''!r})" - total_found += 1 - info.append(f" -> {detail}") - - seems_fishy = False - if total_found == 0: - info.append("Error: the template could not be found.") - seems_fishy = True - elif total_found > 1: - info.append("Warning: multiple loaders returned a match for the template.") - seems_fishy = True - - if blueprint is not None and seems_fishy: - info.append( - " The template was looked up from an endpoint that belongs" - f" to the blueprint {blueprint!r}." - ) - info.append(" Maybe you did not place a template in the right folder?") - info.append(" See https://flask.palletsprojects.com/blueprints/#templates") - - app.logger.info("\n".join(info)) diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/globals.py b/plotter-app/venv/lib/python3.8/site-packages/flask/globals.py deleted file mode 100644 index e2c410c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/globals.py +++ /dev/null @@ -1,51 +0,0 @@ -from __future__ import annotations - -import typing as t -from contextvars import ContextVar - -from werkzeug.local import LocalProxy - -if t.TYPE_CHECKING: # pragma: no cover - from .app import Flask - from .ctx import _AppCtxGlobals - from .ctx import AppContext - from .ctx import RequestContext - from .sessions import SessionMixin - from .wrappers import Request - - -_no_app_msg = """\ -Working outside of application context. - -This typically means that you attempted to use functionality that needed -the current application. To solve this, set up an application context -with app.app_context(). See the documentation for more information.\ -""" -_cv_app: ContextVar[AppContext] = ContextVar("flask.app_ctx") -app_ctx: AppContext = LocalProxy( # type: ignore[assignment] - _cv_app, unbound_message=_no_app_msg -) -current_app: Flask = LocalProxy( # type: ignore[assignment] - _cv_app, "app", unbound_message=_no_app_msg -) -g: _AppCtxGlobals = LocalProxy( # type: ignore[assignment] - _cv_app, "g", unbound_message=_no_app_msg -) - -_no_req_msg = """\ -Working outside of request context. - -This typically means that you attempted to use functionality that needed -an active HTTP request. Consult the documentation on testing for -information about how to avoid this problem.\ -""" -_cv_request: ContextVar[RequestContext] = ContextVar("flask.request_ctx") -request_ctx: RequestContext = LocalProxy( # type: ignore[assignment] - _cv_request, unbound_message=_no_req_msg -) -request: Request = LocalProxy( # type: ignore[assignment] - _cv_request, "request", unbound_message=_no_req_msg -) -session: SessionMixin = LocalProxy( # type: ignore[assignment] - _cv_request, "session", unbound_message=_no_req_msg -) diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/helpers.py b/plotter-app/venv/lib/python3.8/site-packages/flask/helpers.py deleted file mode 100644 index 359a842..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/helpers.py +++ /dev/null @@ -1,621 +0,0 @@ -from __future__ import annotations - -import importlib.util -import os -import sys -import typing as t -from datetime import datetime -from functools import lru_cache -from functools import update_wrapper - -import werkzeug.utils -from werkzeug.exceptions import abort as _wz_abort -from werkzeug.utils import redirect as _wz_redirect -from werkzeug.wrappers import Response as BaseResponse - -from .globals import _cv_request -from .globals import current_app -from .globals import request -from .globals import request_ctx -from .globals import session -from .signals import message_flashed - -if t.TYPE_CHECKING: # pragma: no cover - from .wrappers import Response - - -def get_debug_flag() -> bool: - """Get whether debug mode should be enabled for the app, indicated by the - :envvar:`FLASK_DEBUG` environment variable. The default is ``False``. - """ - val = os.environ.get("FLASK_DEBUG") - return bool(val and val.lower() not in {"0", "false", "no"}) - - -def get_load_dotenv(default: bool = True) -> bool: - """Get whether the user has disabled loading default dotenv files by - setting :envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load - the files. - - :param default: What to return if the env var isn't set. - """ - val = os.environ.get("FLASK_SKIP_DOTENV") - - if not val: - return default - - return val.lower() in ("0", "false", "no") - - -def stream_with_context( - generator_or_function: t.Iterator[t.AnyStr] | t.Callable[..., t.Iterator[t.AnyStr]], -) -> t.Iterator[t.AnyStr]: - """Request contexts disappear when the response is started on the server. - This is done for efficiency reasons and to make it less likely to encounter - memory leaks with badly written WSGI middlewares. The downside is that if - you are using streamed responses, the generator cannot access request bound - information any more. - - This function however can help you keep the context around for longer:: - - from flask import stream_with_context, request, Response - - @app.route('/stream') - def streamed_response(): - @stream_with_context - def generate(): - yield 'Hello ' - yield request.args['name'] - yield '!' - return Response(generate()) - - Alternatively it can also be used around a specific generator:: - - from flask import stream_with_context, request, Response - - @app.route('/stream') - def streamed_response(): - def generate(): - yield 'Hello ' - yield request.args['name'] - yield '!' - return Response(stream_with_context(generate())) - - .. versionadded:: 0.9 - """ - try: - gen = iter(generator_or_function) # type: ignore[arg-type] - except TypeError: - - def decorator(*args: t.Any, **kwargs: t.Any) -> t.Any: - gen = generator_or_function(*args, **kwargs) # type: ignore[operator] - return stream_with_context(gen) - - return update_wrapper(decorator, generator_or_function) # type: ignore[arg-type] - - def generator() -> t.Iterator[t.AnyStr | None]: - ctx = _cv_request.get(None) - if ctx is None: - raise RuntimeError( - "'stream_with_context' can only be used when a request" - " context is active, such as in a view function." - ) - with ctx: - # Dummy sentinel. Has to be inside the context block or we're - # not actually keeping the context around. - yield None - - # The try/finally is here so that if someone passes a WSGI level - # iterator in we're still running the cleanup logic. Generators - # don't need that because they are closed on their destruction - # automatically. - try: - yield from gen - finally: - if hasattr(gen, "close"): - gen.close() - - # The trick is to start the generator. Then the code execution runs until - # the first dummy None is yielded at which point the context was already - # pushed. This item is discarded. Then when the iteration continues the - # real generator is executed. - wrapped_g = generator() - next(wrapped_g) - return wrapped_g # type: ignore[return-value] - - -def make_response(*args: t.Any) -> Response: - """Sometimes it is necessary to set additional headers in a view. Because - views do not have to return response objects but can return a value that - is converted into a response object by Flask itself, it becomes tricky to - add headers to it. This function can be called instead of using a return - and you will get a response object which you can use to attach headers. - - If view looked like this and you want to add a new header:: - - def index(): - return render_template('index.html', foo=42) - - You can now do something like this:: - - def index(): - response = make_response(render_template('index.html', foo=42)) - response.headers['X-Parachutes'] = 'parachutes are cool' - return response - - This function accepts the very same arguments you can return from a - view function. This for example creates a response with a 404 error - code:: - - response = make_response(render_template('not_found.html'), 404) - - The other use case of this function is to force the return value of a - view function into a response which is helpful with view - decorators:: - - response = make_response(view_function()) - response.headers['X-Parachutes'] = 'parachutes are cool' - - Internally this function does the following things: - - - if no arguments are passed, it creates a new response argument - - if one argument is passed, :meth:`flask.Flask.make_response` - is invoked with it. - - if more than one argument is passed, the arguments are passed - to the :meth:`flask.Flask.make_response` function as tuple. - - .. versionadded:: 0.6 - """ - if not args: - return current_app.response_class() - if len(args) == 1: - args = args[0] - return current_app.make_response(args) - - -def url_for( - endpoint: str, - *, - _anchor: str | None = None, - _method: str | None = None, - _scheme: str | None = None, - _external: bool | None = None, - **values: t.Any, -) -> str: - """Generate a URL to the given endpoint with the given values. - - This requires an active request or application context, and calls - :meth:`current_app.url_for() `. See that method - for full documentation. - - :param endpoint: The endpoint name associated with the URL to - generate. If this starts with a ``.``, the current blueprint - name (if any) will be used. - :param _anchor: If given, append this as ``#anchor`` to the URL. - :param _method: If given, generate the URL associated with this - method for the endpoint. - :param _scheme: If given, the URL will have this scheme if it is - external. - :param _external: If given, prefer the URL to be internal (False) or - require it to be external (True). External URLs include the - scheme and domain. When not in an active request, URLs are - external by default. - :param values: Values to use for the variable parts of the URL rule. - Unknown keys are appended as query string arguments, like - ``?a=b&c=d``. - - .. versionchanged:: 2.2 - Calls ``current_app.url_for``, allowing an app to override the - behavior. - - .. versionchanged:: 0.10 - The ``_scheme`` parameter was added. - - .. versionchanged:: 0.9 - The ``_anchor`` and ``_method`` parameters were added. - - .. versionchanged:: 0.9 - Calls ``app.handle_url_build_error`` on build errors. - """ - return current_app.url_for( - endpoint, - _anchor=_anchor, - _method=_method, - _scheme=_scheme, - _external=_external, - **values, - ) - - -def redirect( - location: str, code: int = 302, Response: type[BaseResponse] | None = None -) -> BaseResponse: - """Create a redirect response object. - - If :data:`~flask.current_app` is available, it will use its - :meth:`~flask.Flask.redirect` method, otherwise it will use - :func:`werkzeug.utils.redirect`. - - :param location: The URL to redirect to. - :param code: The status code for the redirect. - :param Response: The response class to use. Not used when - ``current_app`` is active, which uses ``app.response_class``. - - .. versionadded:: 2.2 - Calls ``current_app.redirect`` if available instead of always - using Werkzeug's default ``redirect``. - """ - if current_app: - return current_app.redirect(location, code=code) - - return _wz_redirect(location, code=code, Response=Response) - - -def abort(code: int | BaseResponse, *args: t.Any, **kwargs: t.Any) -> t.NoReturn: - """Raise an :exc:`~werkzeug.exceptions.HTTPException` for the given - status code. - - If :data:`~flask.current_app` is available, it will call its - :attr:`~flask.Flask.aborter` object, otherwise it will use - :func:`werkzeug.exceptions.abort`. - - :param code: The status code for the exception, which must be - registered in ``app.aborter``. - :param args: Passed to the exception. - :param kwargs: Passed to the exception. - - .. versionadded:: 2.2 - Calls ``current_app.aborter`` if available instead of always - using Werkzeug's default ``abort``. - """ - if current_app: - current_app.aborter(code, *args, **kwargs) - - _wz_abort(code, *args, **kwargs) - - -def get_template_attribute(template_name: str, attribute: str) -> t.Any: - """Loads a macro (or variable) a template exports. This can be used to - invoke a macro from within Python code. If you for example have a - template named :file:`_cider.html` with the following contents: - - .. sourcecode:: html+jinja - - {% macro hello(name) %}Hello {{ name }}!{% endmacro %} - - You can access this from Python code like this:: - - hello = get_template_attribute('_cider.html', 'hello') - return hello('World') - - .. versionadded:: 0.2 - - :param template_name: the name of the template - :param attribute: the name of the variable of macro to access - """ - return getattr(current_app.jinja_env.get_template(template_name).module, attribute) - - -def flash(message: str, category: str = "message") -> None: - """Flashes a message to the next request. In order to remove the - flashed message from the session and to display it to the user, - the template has to call :func:`get_flashed_messages`. - - .. versionchanged:: 0.3 - `category` parameter added. - - :param message: the message to be flashed. - :param category: the category for the message. The following values - are recommended: ``'message'`` for any kind of message, - ``'error'`` for errors, ``'info'`` for information - messages and ``'warning'`` for warnings. However any - kind of string can be used as category. - """ - # Original implementation: - # - # session.setdefault('_flashes', []).append((category, message)) - # - # This assumed that changes made to mutable structures in the session are - # always in sync with the session object, which is not true for session - # implementations that use external storage for keeping their keys/values. - flashes = session.get("_flashes", []) - flashes.append((category, message)) - session["_flashes"] = flashes - app = current_app._get_current_object() # type: ignore - message_flashed.send( - app, - _async_wrapper=app.ensure_sync, - message=message, - category=category, - ) - - -def get_flashed_messages( - with_categories: bool = False, category_filter: t.Iterable[str] = () -) -> list[str] | list[tuple[str, str]]: - """Pulls all flashed messages from the session and returns them. - Further calls in the same request to the function will return - the same messages. By default just the messages are returned, - but when `with_categories` is set to ``True``, the return value will - be a list of tuples in the form ``(category, message)`` instead. - - Filter the flashed messages to one or more categories by providing those - categories in `category_filter`. This allows rendering categories in - separate html blocks. The `with_categories` and `category_filter` - arguments are distinct: - - * `with_categories` controls whether categories are returned with message - text (``True`` gives a tuple, where ``False`` gives just the message text). - * `category_filter` filters the messages down to only those matching the - provided categories. - - See :doc:`/patterns/flashing` for examples. - - .. versionchanged:: 0.3 - `with_categories` parameter added. - - .. versionchanged:: 0.9 - `category_filter` parameter added. - - :param with_categories: set to ``True`` to also receive categories. - :param category_filter: filter of categories to limit return values. Only - categories in the list will be returned. - """ - flashes = request_ctx.flashes - if flashes is None: - flashes = session.pop("_flashes") if "_flashes" in session else [] - request_ctx.flashes = flashes - if category_filter: - flashes = list(filter(lambda f: f[0] in category_filter, flashes)) - if not with_categories: - return [x[1] for x in flashes] - return flashes - - -def _prepare_send_file_kwargs(**kwargs: t.Any) -> dict[str, t.Any]: - if kwargs.get("max_age") is None: - kwargs["max_age"] = current_app.get_send_file_max_age - - kwargs.update( - environ=request.environ, - use_x_sendfile=current_app.config["USE_X_SENDFILE"], - response_class=current_app.response_class, - _root_path=current_app.root_path, # type: ignore - ) - return kwargs - - -def send_file( - path_or_file: os.PathLike[t.AnyStr] | str | t.BinaryIO, - mimetype: str | None = None, - as_attachment: bool = False, - download_name: str | None = None, - conditional: bool = True, - etag: bool | str = True, - last_modified: datetime | int | float | None = None, - max_age: None | (int | t.Callable[[str | None], int | None]) = None, -) -> Response: - """Send the contents of a file to the client. - - The first argument can be a file path or a file-like object. Paths - are preferred in most cases because Werkzeug can manage the file and - get extra information from the path. Passing a file-like object - requires that the file is opened in binary mode, and is mostly - useful when building a file in memory with :class:`io.BytesIO`. - - Never pass file paths provided by a user. The path is assumed to be - trusted, so a user could craft a path to access a file you didn't - intend. Use :func:`send_from_directory` to safely serve - user-requested paths from within a directory. - - If the WSGI server sets a ``file_wrapper`` in ``environ``, it is - used, otherwise Werkzeug's built-in wrapper is used. Alternatively, - if the HTTP server supports ``X-Sendfile``, configuring Flask with - ``USE_X_SENDFILE = True`` will tell the server to send the given - path, which is much more efficient than reading it in Python. - - :param path_or_file: The path to the file to send, relative to the - current working directory if a relative path is given. - Alternatively, a file-like object opened in binary mode. Make - sure the file pointer is seeked to the start of the data. - :param mimetype: The MIME type to send for the file. If not - provided, it will try to detect it from the file name. - :param as_attachment: Indicate to a browser that it should offer to - save the file instead of displaying it. - :param download_name: The default name browsers will use when saving - the file. Defaults to the passed file name. - :param conditional: Enable conditional and range responses based on - request headers. Requires passing a file path and ``environ``. - :param etag: Calculate an ETag for the file, which requires passing - a file path. Can also be a string to use instead. - :param last_modified: The last modified time to send for the file, - in seconds. If not provided, it will try to detect it from the - file path. - :param max_age: How long the client should cache the file, in - seconds. If set, ``Cache-Control`` will be ``public``, otherwise - it will be ``no-cache`` to prefer conditional caching. - - .. versionchanged:: 2.0 - ``download_name`` replaces the ``attachment_filename`` - parameter. If ``as_attachment=False``, it is passed with - ``Content-Disposition: inline`` instead. - - .. versionchanged:: 2.0 - ``max_age`` replaces the ``cache_timeout`` parameter. - ``conditional`` is enabled and ``max_age`` is not set by - default. - - .. versionchanged:: 2.0 - ``etag`` replaces the ``add_etags`` parameter. It can be a - string to use instead of generating one. - - .. versionchanged:: 2.0 - Passing a file-like object that inherits from - :class:`~io.TextIOBase` will raise a :exc:`ValueError` rather - than sending an empty file. - - .. versionadded:: 2.0 - Moved the implementation to Werkzeug. This is now a wrapper to - pass some Flask-specific arguments. - - .. versionchanged:: 1.1 - ``filename`` may be a :class:`~os.PathLike` object. - - .. versionchanged:: 1.1 - Passing a :class:`~io.BytesIO` object supports range requests. - - .. versionchanged:: 1.0.3 - Filenames are encoded with ASCII instead of Latin-1 for broader - compatibility with WSGI servers. - - .. versionchanged:: 1.0 - UTF-8 filenames as specified in :rfc:`2231` are supported. - - .. versionchanged:: 0.12 - The filename is no longer automatically inferred from file - objects. If you want to use automatic MIME and etag support, - pass a filename via ``filename_or_fp`` or - ``attachment_filename``. - - .. versionchanged:: 0.12 - ``attachment_filename`` is preferred over ``filename`` for MIME - detection. - - .. versionchanged:: 0.9 - ``cache_timeout`` defaults to - :meth:`Flask.get_send_file_max_age`. - - .. versionchanged:: 0.7 - MIME guessing and etag support for file-like objects was - removed because it was unreliable. Pass a filename if you are - able to, otherwise attach an etag yourself. - - .. versionchanged:: 0.5 - The ``add_etags``, ``cache_timeout`` and ``conditional`` - parameters were added. The default behavior is to add etags. - - .. versionadded:: 0.2 - """ - return werkzeug.utils.send_file( # type: ignore[return-value] - **_prepare_send_file_kwargs( - path_or_file=path_or_file, - environ=request.environ, - mimetype=mimetype, - as_attachment=as_attachment, - download_name=download_name, - conditional=conditional, - etag=etag, - last_modified=last_modified, - max_age=max_age, - ) - ) - - -def send_from_directory( - directory: os.PathLike[str] | str, - path: os.PathLike[str] | str, - **kwargs: t.Any, -) -> Response: - """Send a file from within a directory using :func:`send_file`. - - .. code-block:: python - - @app.route("/uploads/") - def download_file(name): - return send_from_directory( - app.config['UPLOAD_FOLDER'], name, as_attachment=True - ) - - This is a secure way to serve files from a folder, such as static - files or uploads. Uses :func:`~werkzeug.security.safe_join` to - ensure the path coming from the client is not maliciously crafted to - point outside the specified directory. - - If the final path does not point to an existing regular file, - raises a 404 :exc:`~werkzeug.exceptions.NotFound` error. - - :param directory: The directory that ``path`` must be located under, - relative to the current application's root path. - :param path: The path to the file to send, relative to - ``directory``. - :param kwargs: Arguments to pass to :func:`send_file`. - - .. versionchanged:: 2.0 - ``path`` replaces the ``filename`` parameter. - - .. versionadded:: 2.0 - Moved the implementation to Werkzeug. This is now a wrapper to - pass some Flask-specific arguments. - - .. versionadded:: 0.5 - """ - return werkzeug.utils.send_from_directory( # type: ignore[return-value] - directory, path, **_prepare_send_file_kwargs(**kwargs) - ) - - -def get_root_path(import_name: str) -> str: - """Find the root path of a package, or the path that contains a - module. If it cannot be found, returns the current working - directory. - - Not to be confused with the value returned by :func:`find_package`. - - :meta private: - """ - # Module already imported and has a file attribute. Use that first. - mod = sys.modules.get(import_name) - - if mod is not None and hasattr(mod, "__file__") and mod.__file__ is not None: - return os.path.dirname(os.path.abspath(mod.__file__)) - - # Next attempt: check the loader. - try: - spec = importlib.util.find_spec(import_name) - - if spec is None: - raise ValueError - except (ImportError, ValueError): - loader = None - else: - loader = spec.loader - - # Loader does not exist or we're referring to an unloaded main - # module or a main module without path (interactive sessions), go - # with the current working directory. - if loader is None: - return os.getcwd() - - if hasattr(loader, "get_filename"): - filepath = loader.get_filename(import_name) - else: - # Fall back to imports. - __import__(import_name) - mod = sys.modules[import_name] - filepath = getattr(mod, "__file__", None) - - # If we don't have a file path it might be because it is a - # namespace package. In this case pick the root path from the - # first module that is contained in the package. - if filepath is None: - raise RuntimeError( - "No root path can be found for the provided module" - f" {import_name!r}. This can happen because the module" - " came from an import hook that does not provide file" - " name information or because it's a namespace package." - " In this case the root path needs to be explicitly" - " provided." - ) - - # filepath is import_name.py for a module, or __init__.py for a package. - return os.path.dirname(os.path.abspath(filepath)) # type: ignore[no-any-return] - - -@lru_cache(maxsize=None) -def _split_blueprint_path(name: str) -> list[str]: - out: list[str] = [name] - - if "." in name: - out.extend(_split_blueprint_path(name.rpartition(".")[0])) - - return out diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/json/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/flask/json/__init__.py deleted file mode 100644 index c0941d0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/json/__init__.py +++ /dev/null @@ -1,170 +0,0 @@ -from __future__ import annotations - -import json as _json -import typing as t - -from ..globals import current_app -from .provider import _default - -if t.TYPE_CHECKING: # pragma: no cover - from ..wrappers import Response - - -def dumps(obj: t.Any, **kwargs: t.Any) -> str: - """Serialize data as JSON. - - If :data:`~flask.current_app` is available, it will use its - :meth:`app.json.dumps() ` - method, otherwise it will use :func:`json.dumps`. - - :param obj: The data to serialize. - :param kwargs: Arguments passed to the ``dumps`` implementation. - - .. versionchanged:: 2.3 - The ``app`` parameter was removed. - - .. versionchanged:: 2.2 - Calls ``current_app.json.dumps``, allowing an app to override - the behavior. - - .. versionchanged:: 2.0.2 - :class:`decimal.Decimal` is supported by converting to a string. - - .. versionchanged:: 2.0 - ``encoding`` will be removed in Flask 2.1. - - .. versionchanged:: 1.0.3 - ``app`` can be passed directly, rather than requiring an app - context for configuration. - """ - if current_app: - return current_app.json.dumps(obj, **kwargs) - - kwargs.setdefault("default", _default) - return _json.dumps(obj, **kwargs) - - -def dump(obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None: - """Serialize data as JSON and write to a file. - - If :data:`~flask.current_app` is available, it will use its - :meth:`app.json.dump() ` - method, otherwise it will use :func:`json.dump`. - - :param obj: The data to serialize. - :param fp: A file opened for writing text. Should use the UTF-8 - encoding to be valid JSON. - :param kwargs: Arguments passed to the ``dump`` implementation. - - .. versionchanged:: 2.3 - The ``app`` parameter was removed. - - .. versionchanged:: 2.2 - Calls ``current_app.json.dump``, allowing an app to override - the behavior. - - .. versionchanged:: 2.0 - Writing to a binary file, and the ``encoding`` argument, will be - removed in Flask 2.1. - """ - if current_app: - current_app.json.dump(obj, fp, **kwargs) - else: - kwargs.setdefault("default", _default) - _json.dump(obj, fp, **kwargs) - - -def loads(s: str | bytes, **kwargs: t.Any) -> t.Any: - """Deserialize data as JSON. - - If :data:`~flask.current_app` is available, it will use its - :meth:`app.json.loads() ` - method, otherwise it will use :func:`json.loads`. - - :param s: Text or UTF-8 bytes. - :param kwargs: Arguments passed to the ``loads`` implementation. - - .. versionchanged:: 2.3 - The ``app`` parameter was removed. - - .. versionchanged:: 2.2 - Calls ``current_app.json.loads``, allowing an app to override - the behavior. - - .. versionchanged:: 2.0 - ``encoding`` will be removed in Flask 2.1. The data must be a - string or UTF-8 bytes. - - .. versionchanged:: 1.0.3 - ``app`` can be passed directly, rather than requiring an app - context for configuration. - """ - if current_app: - return current_app.json.loads(s, **kwargs) - - return _json.loads(s, **kwargs) - - -def load(fp: t.IO[t.AnyStr], **kwargs: t.Any) -> t.Any: - """Deserialize data as JSON read from a file. - - If :data:`~flask.current_app` is available, it will use its - :meth:`app.json.load() ` - method, otherwise it will use :func:`json.load`. - - :param fp: A file opened for reading text or UTF-8 bytes. - :param kwargs: Arguments passed to the ``load`` implementation. - - .. versionchanged:: 2.3 - The ``app`` parameter was removed. - - .. versionchanged:: 2.2 - Calls ``current_app.json.load``, allowing an app to override - the behavior. - - .. versionchanged:: 2.2 - The ``app`` parameter will be removed in Flask 2.3. - - .. versionchanged:: 2.0 - ``encoding`` will be removed in Flask 2.1. The file must be text - mode, or binary mode with UTF-8 bytes. - """ - if current_app: - return current_app.json.load(fp, **kwargs) - - return _json.load(fp, **kwargs) - - -def jsonify(*args: t.Any, **kwargs: t.Any) -> Response: - """Serialize the given arguments as JSON, and return a - :class:`~flask.Response` object with the ``application/json`` - mimetype. A dict or list returned from a view will be converted to a - JSON response automatically without needing to call this. - - This requires an active request or application context, and calls - :meth:`app.json.response() `. - - In debug mode, the output is formatted with indentation to make it - easier to read. This may also be controlled by the provider. - - Either positional or keyword arguments can be given, not both. - If no arguments are given, ``None`` is serialized. - - :param args: A single value to serialize, or multiple values to - treat as a list to serialize. - :param kwargs: Treat as a dict to serialize. - - .. versionchanged:: 2.2 - Calls ``current_app.json.response``, allowing an app to override - the behavior. - - .. versionchanged:: 2.0.2 - :class:`decimal.Decimal` is supported by converting to a string. - - .. versionchanged:: 0.11 - Added support for serializing top-level arrays. This was a - security risk in ancient browsers. See :ref:`security-json`. - - .. versionadded:: 0.2 - """ - return current_app.json.response(*args, **kwargs) # type: ignore[return-value] diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/json/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/json/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 817eab7..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/json/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/json/__pycache__/provider.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/json/__pycache__/provider.cpython-38.pyc deleted file mode 100644 index 96f0dc8..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/json/__pycache__/provider.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/json/__pycache__/tag.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/json/__pycache__/tag.cpython-38.pyc deleted file mode 100644 index bc511ad..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/json/__pycache__/tag.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/json/provider.py b/plotter-app/venv/lib/python3.8/site-packages/flask/json/provider.py deleted file mode 100644 index f9b2e8f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/json/provider.py +++ /dev/null @@ -1,215 +0,0 @@ -from __future__ import annotations - -import dataclasses -import decimal -import json -import typing as t -import uuid -import weakref -from datetime import date - -from werkzeug.http import http_date - -if t.TYPE_CHECKING: # pragma: no cover - from werkzeug.sansio.response import Response - - from ..sansio.app import App - - -class JSONProvider: - """A standard set of JSON operations for an application. Subclasses - of this can be used to customize JSON behavior or use different - JSON libraries. - - To implement a provider for a specific library, subclass this base - class and implement at least :meth:`dumps` and :meth:`loads`. All - other methods have default implementations. - - To use a different provider, either subclass ``Flask`` and set - :attr:`~flask.Flask.json_provider_class` to a provider class, or set - :attr:`app.json ` to an instance of the class. - - :param app: An application instance. This will be stored as a - :class:`weakref.proxy` on the :attr:`_app` attribute. - - .. versionadded:: 2.2 - """ - - def __init__(self, app: App) -> None: - self._app: App = weakref.proxy(app) - - def dumps(self, obj: t.Any, **kwargs: t.Any) -> str: - """Serialize data as JSON. - - :param obj: The data to serialize. - :param kwargs: May be passed to the underlying JSON library. - """ - raise NotImplementedError - - def dump(self, obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None: - """Serialize data as JSON and write to a file. - - :param obj: The data to serialize. - :param fp: A file opened for writing text. Should use the UTF-8 - encoding to be valid JSON. - :param kwargs: May be passed to the underlying JSON library. - """ - fp.write(self.dumps(obj, **kwargs)) - - def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any: - """Deserialize data as JSON. - - :param s: Text or UTF-8 bytes. - :param kwargs: May be passed to the underlying JSON library. - """ - raise NotImplementedError - - def load(self, fp: t.IO[t.AnyStr], **kwargs: t.Any) -> t.Any: - """Deserialize data as JSON read from a file. - - :param fp: A file opened for reading text or UTF-8 bytes. - :param kwargs: May be passed to the underlying JSON library. - """ - return self.loads(fp.read(), **kwargs) - - def _prepare_response_obj( - self, args: tuple[t.Any, ...], kwargs: dict[str, t.Any] - ) -> t.Any: - if args and kwargs: - raise TypeError("app.json.response() takes either args or kwargs, not both") - - if not args and not kwargs: - return None - - if len(args) == 1: - return args[0] - - return args or kwargs - - def response(self, *args: t.Any, **kwargs: t.Any) -> Response: - """Serialize the given arguments as JSON, and return a - :class:`~flask.Response` object with the ``application/json`` - mimetype. - - The :func:`~flask.json.jsonify` function calls this method for - the current application. - - Either positional or keyword arguments can be given, not both. - If no arguments are given, ``None`` is serialized. - - :param args: A single value to serialize, or multiple values to - treat as a list to serialize. - :param kwargs: Treat as a dict to serialize. - """ - obj = self._prepare_response_obj(args, kwargs) - return self._app.response_class(self.dumps(obj), mimetype="application/json") - - -def _default(o: t.Any) -> t.Any: - if isinstance(o, date): - return http_date(o) - - if isinstance(o, (decimal.Decimal, uuid.UUID)): - return str(o) - - if dataclasses and dataclasses.is_dataclass(o): - return dataclasses.asdict(o) - - if hasattr(o, "__html__"): - return str(o.__html__()) - - raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable") - - -class DefaultJSONProvider(JSONProvider): - """Provide JSON operations using Python's built-in :mod:`json` - library. Serializes the following additional data types: - - - :class:`datetime.datetime` and :class:`datetime.date` are - serialized to :rfc:`822` strings. This is the same as the HTTP - date format. - - :class:`uuid.UUID` is serialized to a string. - - :class:`dataclasses.dataclass` is passed to - :func:`dataclasses.asdict`. - - :class:`~markupsafe.Markup` (or any object with a ``__html__`` - method) will call the ``__html__`` method to get a string. - """ - - default: t.Callable[[t.Any], t.Any] = staticmethod(_default) # type: ignore[assignment] - """Apply this function to any object that :meth:`json.dumps` does - not know how to serialize. It should return a valid JSON type or - raise a ``TypeError``. - """ - - ensure_ascii = True - """Replace non-ASCII characters with escape sequences. This may be - more compatible with some clients, but can be disabled for better - performance and size. - """ - - sort_keys = True - """Sort the keys in any serialized dicts. This may be useful for - some caching situations, but can be disabled for better performance. - When enabled, keys must all be strings, they are not converted - before sorting. - """ - - compact: bool | None = None - """If ``True``, or ``None`` out of debug mode, the :meth:`response` - output will not add indentation, newlines, or spaces. If ``False``, - or ``None`` in debug mode, it will use a non-compact representation. - """ - - mimetype = "application/json" - """The mimetype set in :meth:`response`.""" - - def dumps(self, obj: t.Any, **kwargs: t.Any) -> str: - """Serialize data as JSON to a string. - - Keyword arguments are passed to :func:`json.dumps`. Sets some - parameter defaults from the :attr:`default`, - :attr:`ensure_ascii`, and :attr:`sort_keys` attributes. - - :param obj: The data to serialize. - :param kwargs: Passed to :func:`json.dumps`. - """ - kwargs.setdefault("default", self.default) - kwargs.setdefault("ensure_ascii", self.ensure_ascii) - kwargs.setdefault("sort_keys", self.sort_keys) - return json.dumps(obj, **kwargs) - - def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any: - """Deserialize data as JSON from a string or bytes. - - :param s: Text or UTF-8 bytes. - :param kwargs: Passed to :func:`json.loads`. - """ - return json.loads(s, **kwargs) - - def response(self, *args: t.Any, **kwargs: t.Any) -> Response: - """Serialize the given arguments as JSON, and return a - :class:`~flask.Response` object with it. The response mimetype - will be "application/json" and can be changed with - :attr:`mimetype`. - - If :attr:`compact` is ``False`` or debug mode is enabled, the - output will be formatted to be easier to read. - - Either positional or keyword arguments can be given, not both. - If no arguments are given, ``None`` is serialized. - - :param args: A single value to serialize, or multiple values to - treat as a list to serialize. - :param kwargs: Treat as a dict to serialize. - """ - obj = self._prepare_response_obj(args, kwargs) - dump_args: dict[str, t.Any] = {} - - if (self.compact is None and self._app.debug) or self.compact is False: - dump_args.setdefault("indent", 2) - else: - dump_args.setdefault("separators", (",", ":")) - - return self._app.response_class( - f"{self.dumps(obj, **dump_args)}\n", mimetype=self.mimetype - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/json/tag.py b/plotter-app/venv/lib/python3.8/site-packages/flask/json/tag.py deleted file mode 100644 index 8dc3629..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/json/tag.py +++ /dev/null @@ -1,327 +0,0 @@ -""" -Tagged JSON -~~~~~~~~~~~ - -A compact representation for lossless serialization of non-standard JSON -types. :class:`~flask.sessions.SecureCookieSessionInterface` uses this -to serialize the session data, but it may be useful in other places. It -can be extended to support other types. - -.. autoclass:: TaggedJSONSerializer - :members: - -.. autoclass:: JSONTag - :members: - -Let's see an example that adds support for -:class:`~collections.OrderedDict`. Dicts don't have an order in JSON, so -to handle this we will dump the items as a list of ``[key, value]`` -pairs. Subclass :class:`JSONTag` and give it the new key ``' od'`` to -identify the type. The session serializer processes dicts first, so -insert the new tag at the front of the order since ``OrderedDict`` must -be processed before ``dict``. - -.. code-block:: python - - from flask.json.tag import JSONTag - - class TagOrderedDict(JSONTag): - __slots__ = ('serializer',) - key = ' od' - - def check(self, value): - return isinstance(value, OrderedDict) - - def to_json(self, value): - return [[k, self.serializer.tag(v)] for k, v in iteritems(value)] - - def to_python(self, value): - return OrderedDict(value) - - app.session_interface.serializer.register(TagOrderedDict, index=0) -""" - -from __future__ import annotations - -import typing as t -from base64 import b64decode -from base64 import b64encode -from datetime import datetime -from uuid import UUID - -from markupsafe import Markup -from werkzeug.http import http_date -from werkzeug.http import parse_date - -from ..json import dumps -from ..json import loads - - -class JSONTag: - """Base class for defining type tags for :class:`TaggedJSONSerializer`.""" - - __slots__ = ("serializer",) - - #: The tag to mark the serialized object with. If empty, this tag is - #: only used as an intermediate step during tagging. - key: str = "" - - def __init__(self, serializer: TaggedJSONSerializer) -> None: - """Create a tagger for the given serializer.""" - self.serializer = serializer - - def check(self, value: t.Any) -> bool: - """Check if the given value should be tagged by this tag.""" - raise NotImplementedError - - def to_json(self, value: t.Any) -> t.Any: - """Convert the Python object to an object that is a valid JSON type. - The tag will be added later.""" - raise NotImplementedError - - def to_python(self, value: t.Any) -> t.Any: - """Convert the JSON representation back to the correct type. The tag - will already be removed.""" - raise NotImplementedError - - def tag(self, value: t.Any) -> dict[str, t.Any]: - """Convert the value to a valid JSON type and add the tag structure - around it.""" - return {self.key: self.to_json(value)} - - -class TagDict(JSONTag): - """Tag for 1-item dicts whose only key matches a registered tag. - - Internally, the dict key is suffixed with `__`, and the suffix is removed - when deserializing. - """ - - __slots__ = () - key = " di" - - def check(self, value: t.Any) -> bool: - return ( - isinstance(value, dict) - and len(value) == 1 - and next(iter(value)) in self.serializer.tags - ) - - def to_json(self, value: t.Any) -> t.Any: - key = next(iter(value)) - return {f"{key}__": self.serializer.tag(value[key])} - - def to_python(self, value: t.Any) -> t.Any: - key = next(iter(value)) - return {key[:-2]: value[key]} - - -class PassDict(JSONTag): - __slots__ = () - - def check(self, value: t.Any) -> bool: - return isinstance(value, dict) - - def to_json(self, value: t.Any) -> t.Any: - # JSON objects may only have string keys, so don't bother tagging the - # key here. - return {k: self.serializer.tag(v) for k, v in value.items()} - - tag = to_json - - -class TagTuple(JSONTag): - __slots__ = () - key = " t" - - def check(self, value: t.Any) -> bool: - return isinstance(value, tuple) - - def to_json(self, value: t.Any) -> t.Any: - return [self.serializer.tag(item) for item in value] - - def to_python(self, value: t.Any) -> t.Any: - return tuple(value) - - -class PassList(JSONTag): - __slots__ = () - - def check(self, value: t.Any) -> bool: - return isinstance(value, list) - - def to_json(self, value: t.Any) -> t.Any: - return [self.serializer.tag(item) for item in value] - - tag = to_json - - -class TagBytes(JSONTag): - __slots__ = () - key = " b" - - def check(self, value: t.Any) -> bool: - return isinstance(value, bytes) - - def to_json(self, value: t.Any) -> t.Any: - return b64encode(value).decode("ascii") - - def to_python(self, value: t.Any) -> t.Any: - return b64decode(value) - - -class TagMarkup(JSONTag): - """Serialize anything matching the :class:`~markupsafe.Markup` API by - having a ``__html__`` method to the result of that method. Always - deserializes to an instance of :class:`~markupsafe.Markup`.""" - - __slots__ = () - key = " m" - - def check(self, value: t.Any) -> bool: - return callable(getattr(value, "__html__", None)) - - def to_json(self, value: t.Any) -> t.Any: - return str(value.__html__()) - - def to_python(self, value: t.Any) -> t.Any: - return Markup(value) - - -class TagUUID(JSONTag): - __slots__ = () - key = " u" - - def check(self, value: t.Any) -> bool: - return isinstance(value, UUID) - - def to_json(self, value: t.Any) -> t.Any: - return value.hex - - def to_python(self, value: t.Any) -> t.Any: - return UUID(value) - - -class TagDateTime(JSONTag): - __slots__ = () - key = " d" - - def check(self, value: t.Any) -> bool: - return isinstance(value, datetime) - - def to_json(self, value: t.Any) -> t.Any: - return http_date(value) - - def to_python(self, value: t.Any) -> t.Any: - return parse_date(value) - - -class TaggedJSONSerializer: - """Serializer that uses a tag system to compactly represent objects that - are not JSON types. Passed as the intermediate serializer to - :class:`itsdangerous.Serializer`. - - The following extra types are supported: - - * :class:`dict` - * :class:`tuple` - * :class:`bytes` - * :class:`~markupsafe.Markup` - * :class:`~uuid.UUID` - * :class:`~datetime.datetime` - """ - - __slots__ = ("tags", "order") - - #: Tag classes to bind when creating the serializer. Other tags can be - #: added later using :meth:`~register`. - default_tags = [ - TagDict, - PassDict, - TagTuple, - PassList, - TagBytes, - TagMarkup, - TagUUID, - TagDateTime, - ] - - def __init__(self) -> None: - self.tags: dict[str, JSONTag] = {} - self.order: list[JSONTag] = [] - - for cls in self.default_tags: - self.register(cls) - - def register( - self, - tag_class: type[JSONTag], - force: bool = False, - index: int | None = None, - ) -> None: - """Register a new tag with this serializer. - - :param tag_class: tag class to register. Will be instantiated with this - serializer instance. - :param force: overwrite an existing tag. If false (default), a - :exc:`KeyError` is raised. - :param index: index to insert the new tag in the tag order. Useful when - the new tag is a special case of an existing tag. If ``None`` - (default), the tag is appended to the end of the order. - - :raise KeyError: if the tag key is already registered and ``force`` is - not true. - """ - tag = tag_class(self) - key = tag.key - - if key: - if not force and key in self.tags: - raise KeyError(f"Tag '{key}' is already registered.") - - self.tags[key] = tag - - if index is None: - self.order.append(tag) - else: - self.order.insert(index, tag) - - def tag(self, value: t.Any) -> t.Any: - """Convert a value to a tagged representation if necessary.""" - for tag in self.order: - if tag.check(value): - return tag.tag(value) - - return value - - def untag(self, value: dict[str, t.Any]) -> t.Any: - """Convert a tagged representation back to the original type.""" - if len(value) != 1: - return value - - key = next(iter(value)) - - if key not in self.tags: - return value - - return self.tags[key].to_python(value[key]) - - def _untag_scan(self, value: t.Any) -> t.Any: - if isinstance(value, dict): - # untag each item recursively - value = {k: self._untag_scan(v) for k, v in value.items()} - # untag the dict itself - value = self.untag(value) - elif isinstance(value, list): - # untag each item recursively - value = [self._untag_scan(item) for item in value] - - return value - - def dumps(self, value: t.Any) -> str: - """Tag the value and dump it to a compact JSON string.""" - return dumps(self.tag(value), separators=(",", ":")) - - def loads(self, value: str) -> t.Any: - """Load data from a JSON string and deserialized any tagged objects.""" - return self._untag_scan(loads(value)) diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/logging.py b/plotter-app/venv/lib/python3.8/site-packages/flask/logging.py deleted file mode 100644 index 0cb8f43..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/logging.py +++ /dev/null @@ -1,79 +0,0 @@ -from __future__ import annotations - -import logging -import sys -import typing as t - -from werkzeug.local import LocalProxy - -from .globals import request - -if t.TYPE_CHECKING: # pragma: no cover - from .sansio.app import App - - -@LocalProxy -def wsgi_errors_stream() -> t.TextIO: - """Find the most appropriate error stream for the application. If a request - is active, log to ``wsgi.errors``, otherwise use ``sys.stderr``. - - If you configure your own :class:`logging.StreamHandler`, you may want to - use this for the stream. If you are using file or dict configuration and - can't import this directly, you can refer to it as - ``ext://flask.logging.wsgi_errors_stream``. - """ - if request: - return request.environ["wsgi.errors"] # type: ignore[no-any-return] - - return sys.stderr - - -def has_level_handler(logger: logging.Logger) -> bool: - """Check if there is a handler in the logging chain that will handle the - given logger's :meth:`effective level <~logging.Logger.getEffectiveLevel>`. - """ - level = logger.getEffectiveLevel() - current = logger - - while current: - if any(handler.level <= level for handler in current.handlers): - return True - - if not current.propagate: - break - - current = current.parent # type: ignore - - return False - - -#: Log messages to :func:`~flask.logging.wsgi_errors_stream` with the format -#: ``[%(asctime)s] %(levelname)s in %(module)s: %(message)s``. -default_handler = logging.StreamHandler(wsgi_errors_stream) # type: ignore -default_handler.setFormatter( - logging.Formatter("[%(asctime)s] %(levelname)s in %(module)s: %(message)s") -) - - -def create_logger(app: App) -> logging.Logger: - """Get the Flask app's logger and configure it if needed. - - The logger name will be the same as - :attr:`app.import_name `. - - When :attr:`~flask.Flask.debug` is enabled, set the logger level to - :data:`logging.DEBUG` if it is not set. - - If there is no handler for the logger's effective level, add a - :class:`~logging.StreamHandler` for - :func:`~flask.logging.wsgi_errors_stream` with a basic format. - """ - logger = logging.getLogger(app.name) - - if app.debug and not logger.level: - logger.setLevel(logging.DEBUG) - - if not has_level_handler(logger): - logger.addHandler(default_handler) - - return logger diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/py.typed b/plotter-app/venv/lib/python3.8/site-packages/flask/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/README.md b/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/README.md deleted file mode 100644 index 623ac19..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Sansio - -This folder contains code that can be used by alternative Flask -implementations, for example Quart. The code therefore cannot do any -IO, nor be part of a likely IO path. Finally this code cannot use the -Flask globals. diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/__pycache__/app.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/__pycache__/app.cpython-38.pyc deleted file mode 100644 index 3df94d9..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/__pycache__/app.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/__pycache__/blueprints.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/__pycache__/blueprints.cpython-38.pyc deleted file mode 100644 index 929eab3..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/__pycache__/blueprints.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/__pycache__/scaffold.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/__pycache__/scaffold.cpython-38.pyc deleted file mode 100644 index 2b82f6c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/__pycache__/scaffold.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/app.py b/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/app.py deleted file mode 100644 index 01fd5db..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/app.py +++ /dev/null @@ -1,964 +0,0 @@ -from __future__ import annotations - -import logging -import os -import sys -import typing as t -from datetime import timedelta -from itertools import chain - -from werkzeug.exceptions import Aborter -from werkzeug.exceptions import BadRequest -from werkzeug.exceptions import BadRequestKeyError -from werkzeug.routing import BuildError -from werkzeug.routing import Map -from werkzeug.routing import Rule -from werkzeug.sansio.response import Response -from werkzeug.utils import cached_property -from werkzeug.utils import redirect as _wz_redirect - -from .. import typing as ft -from ..config import Config -from ..config import ConfigAttribute -from ..ctx import _AppCtxGlobals -from ..helpers import _split_blueprint_path -from ..helpers import get_debug_flag -from ..json.provider import DefaultJSONProvider -from ..json.provider import JSONProvider -from ..logging import create_logger -from ..templating import DispatchingJinjaLoader -from ..templating import Environment -from .scaffold import _endpoint_from_view_func -from .scaffold import find_package -from .scaffold import Scaffold -from .scaffold import setupmethod - -if t.TYPE_CHECKING: # pragma: no cover - from werkzeug.wrappers import Response as BaseResponse - - from ..testing import FlaskClient - from ..testing import FlaskCliRunner - from .blueprints import Blueprint - -T_shell_context_processor = t.TypeVar( - "T_shell_context_processor", bound=ft.ShellContextProcessorCallable -) -T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable) -T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallable) -T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable) -T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable) - - -def _make_timedelta(value: timedelta | int | None) -> timedelta | None: - if value is None or isinstance(value, timedelta): - return value - - return timedelta(seconds=value) - - -class App(Scaffold): - """The flask object implements a WSGI application and acts as the central - object. It is passed the name of the module or package of the - application. Once it is created it will act as a central registry for - the view functions, the URL rules, template configuration and much more. - - The name of the package is used to resolve resources from inside the - package or the folder the module is contained in depending on if the - package parameter resolves to an actual python package (a folder with - an :file:`__init__.py` file inside) or a standard module (just a ``.py`` file). - - For more information about resource loading, see :func:`open_resource`. - - Usually you create a :class:`Flask` instance in your main module or - in the :file:`__init__.py` file of your package like this:: - - from flask import Flask - app = Flask(__name__) - - .. admonition:: About the First Parameter - - The idea of the first parameter is to give Flask an idea of what - belongs to your application. This name is used to find resources - on the filesystem, can be used by extensions to improve debugging - information and a lot more. - - So it's important what you provide there. If you are using a single - module, `__name__` is always the correct value. If you however are - using a package, it's usually recommended to hardcode the name of - your package there. - - For example if your application is defined in :file:`yourapplication/app.py` - you should create it with one of the two versions below:: - - app = Flask('yourapplication') - app = Flask(__name__.split('.')[0]) - - Why is that? The application will work even with `__name__`, thanks - to how resources are looked up. However it will make debugging more - painful. Certain extensions can make assumptions based on the - import name of your application. For example the Flask-SQLAlchemy - extension will look for the code in your application that triggered - an SQL query in debug mode. If the import name is not properly set - up, that debugging information is lost. (For example it would only - pick up SQL queries in `yourapplication.app` and not - `yourapplication.views.frontend`) - - .. versionadded:: 0.7 - The `static_url_path`, `static_folder`, and `template_folder` - parameters were added. - - .. versionadded:: 0.8 - The `instance_path` and `instance_relative_config` parameters were - added. - - .. versionadded:: 0.11 - The `root_path` parameter was added. - - .. versionadded:: 1.0 - The ``host_matching`` and ``static_host`` parameters were added. - - .. versionadded:: 1.0 - The ``subdomain_matching`` parameter was added. Subdomain - matching needs to be enabled manually now. Setting - :data:`SERVER_NAME` does not implicitly enable it. - - :param import_name: the name of the application package - :param static_url_path: can be used to specify a different path for the - static files on the web. Defaults to the name - of the `static_folder` folder. - :param static_folder: The folder with static files that is served at - ``static_url_path``. Relative to the application ``root_path`` - or an absolute path. Defaults to ``'static'``. - :param static_host: the host to use when adding the static route. - Defaults to None. Required when using ``host_matching=True`` - with a ``static_folder`` configured. - :param host_matching: set ``url_map.host_matching`` attribute. - Defaults to False. - :param subdomain_matching: consider the subdomain relative to - :data:`SERVER_NAME` when matching routes. Defaults to False. - :param template_folder: the folder that contains the templates that should - be used by the application. Defaults to - ``'templates'`` folder in the root path of the - application. - :param instance_path: An alternative instance path for the application. - By default the folder ``'instance'`` next to the - package or module is assumed to be the instance - path. - :param instance_relative_config: if set to ``True`` relative filenames - for loading the config are assumed to - be relative to the instance path instead - of the application root. - :param root_path: The path to the root of the application files. - This should only be set manually when it can't be detected - automatically, such as for namespace packages. - """ - - #: The class of the object assigned to :attr:`aborter`, created by - #: :meth:`create_aborter`. That object is called by - #: :func:`flask.abort` to raise HTTP errors, and can be - #: called directly as well. - #: - #: Defaults to :class:`werkzeug.exceptions.Aborter`. - #: - #: .. versionadded:: 2.2 - aborter_class = Aborter - - #: The class that is used for the Jinja environment. - #: - #: .. versionadded:: 0.11 - jinja_environment = Environment - - #: The class that is used for the :data:`~flask.g` instance. - #: - #: Example use cases for a custom class: - #: - #: 1. Store arbitrary attributes on flask.g. - #: 2. Add a property for lazy per-request database connectors. - #: 3. Return None instead of AttributeError on unexpected attributes. - #: 4. Raise exception if an unexpected attr is set, a "controlled" flask.g. - #: - #: In Flask 0.9 this property was called `request_globals_class` but it - #: was changed in 0.10 to :attr:`app_ctx_globals_class` because the - #: flask.g object is now application context scoped. - #: - #: .. versionadded:: 0.10 - app_ctx_globals_class = _AppCtxGlobals - - #: The class that is used for the ``config`` attribute of this app. - #: Defaults to :class:`~flask.Config`. - #: - #: Example use cases for a custom class: - #: - #: 1. Default values for certain config options. - #: 2. Access to config values through attributes in addition to keys. - #: - #: .. versionadded:: 0.11 - config_class = Config - - #: The testing flag. Set this to ``True`` to enable the test mode of - #: Flask extensions (and in the future probably also Flask itself). - #: For example this might activate test helpers that have an - #: additional runtime cost which should not be enabled by default. - #: - #: If this is enabled and PROPAGATE_EXCEPTIONS is not changed from the - #: default it's implicitly enabled. - #: - #: This attribute can also be configured from the config with the - #: ``TESTING`` configuration key. Defaults to ``False``. - testing = ConfigAttribute[bool]("TESTING") - - #: If a secret key is set, cryptographic components can use this to - #: sign cookies and other things. Set this to a complex random value - #: when you want to use the secure cookie for instance. - #: - #: This attribute can also be configured from the config with the - #: :data:`SECRET_KEY` configuration key. Defaults to ``None``. - secret_key = ConfigAttribute[t.Union[str, bytes, None]]("SECRET_KEY") - - #: A :class:`~datetime.timedelta` which is used to set the expiration - #: date of a permanent session. The default is 31 days which makes a - #: permanent session survive for roughly one month. - #: - #: This attribute can also be configured from the config with the - #: ``PERMANENT_SESSION_LIFETIME`` configuration key. Defaults to - #: ``timedelta(days=31)`` - permanent_session_lifetime = ConfigAttribute[timedelta]( - "PERMANENT_SESSION_LIFETIME", - get_converter=_make_timedelta, # type: ignore[arg-type] - ) - - json_provider_class: type[JSONProvider] = DefaultJSONProvider - """A subclass of :class:`~flask.json.provider.JSONProvider`. An - instance is created and assigned to :attr:`app.json` when creating - the app. - - The default, :class:`~flask.json.provider.DefaultJSONProvider`, uses - Python's built-in :mod:`json` library. A different provider can use - a different JSON library. - - .. versionadded:: 2.2 - """ - - #: Options that are passed to the Jinja environment in - #: :meth:`create_jinja_environment`. Changing these options after - #: the environment is created (accessing :attr:`jinja_env`) will - #: have no effect. - #: - #: .. versionchanged:: 1.1.0 - #: This is a ``dict`` instead of an ``ImmutableDict`` to allow - #: easier configuration. - #: - jinja_options: dict[str, t.Any] = {} - - #: The rule object to use for URL rules created. This is used by - #: :meth:`add_url_rule`. Defaults to :class:`werkzeug.routing.Rule`. - #: - #: .. versionadded:: 0.7 - url_rule_class = Rule - - #: The map object to use for storing the URL rules and routing - #: configuration parameters. Defaults to :class:`werkzeug.routing.Map`. - #: - #: .. versionadded:: 1.1.0 - url_map_class = Map - - #: The :meth:`test_client` method creates an instance of this test - #: client class. Defaults to :class:`~flask.testing.FlaskClient`. - #: - #: .. versionadded:: 0.7 - test_client_class: type[FlaskClient] | None = None - - #: The :class:`~click.testing.CliRunner` subclass, by default - #: :class:`~flask.testing.FlaskCliRunner` that is used by - #: :meth:`test_cli_runner`. Its ``__init__`` method should take a - #: Flask app object as the first argument. - #: - #: .. versionadded:: 1.0 - test_cli_runner_class: type[FlaskCliRunner] | None = None - - default_config: dict[str, t.Any] - response_class: type[Response] - - def __init__( - self, - import_name: str, - static_url_path: str | None = None, - static_folder: str | os.PathLike[str] | None = "static", - static_host: str | None = None, - host_matching: bool = False, - subdomain_matching: bool = False, - template_folder: str | os.PathLike[str] | None = "templates", - instance_path: str | None = None, - instance_relative_config: bool = False, - root_path: str | None = None, - ): - super().__init__( - import_name=import_name, - static_folder=static_folder, - static_url_path=static_url_path, - template_folder=template_folder, - root_path=root_path, - ) - - if instance_path is None: - instance_path = self.auto_find_instance_path() - elif not os.path.isabs(instance_path): - raise ValueError( - "If an instance path is provided it must be absolute." - " A relative path was given instead." - ) - - #: Holds the path to the instance folder. - #: - #: .. versionadded:: 0.8 - self.instance_path = instance_path - - #: The configuration dictionary as :class:`Config`. This behaves - #: exactly like a regular dictionary but supports additional methods - #: to load a config from files. - self.config = self.make_config(instance_relative_config) - - #: An instance of :attr:`aborter_class` created by - #: :meth:`make_aborter`. This is called by :func:`flask.abort` - #: to raise HTTP errors, and can be called directly as well. - #: - #: .. versionadded:: 2.2 - #: Moved from ``flask.abort``, which calls this object. - self.aborter = self.make_aborter() - - self.json: JSONProvider = self.json_provider_class(self) - """Provides access to JSON methods. Functions in ``flask.json`` - will call methods on this provider when the application context - is active. Used for handling JSON requests and responses. - - An instance of :attr:`json_provider_class`. Can be customized by - changing that attribute on a subclass, or by assigning to this - attribute afterwards. - - The default, :class:`~flask.json.provider.DefaultJSONProvider`, - uses Python's built-in :mod:`json` library. A different provider - can use a different JSON library. - - .. versionadded:: 2.2 - """ - - #: A list of functions that are called by - #: :meth:`handle_url_build_error` when :meth:`.url_for` raises a - #: :exc:`~werkzeug.routing.BuildError`. Each function is called - #: with ``error``, ``endpoint`` and ``values``. If a function - #: returns ``None`` or raises a ``BuildError``, it is skipped. - #: Otherwise, its return value is returned by ``url_for``. - #: - #: .. versionadded:: 0.9 - self.url_build_error_handlers: list[ - t.Callable[[Exception, str, dict[str, t.Any]], str] - ] = [] - - #: A list of functions that are called when the application context - #: is destroyed. Since the application context is also torn down - #: if the request ends this is the place to store code that disconnects - #: from databases. - #: - #: .. versionadded:: 0.9 - self.teardown_appcontext_funcs: list[ft.TeardownCallable] = [] - - #: A list of shell context processor functions that should be run - #: when a shell context is created. - #: - #: .. versionadded:: 0.11 - self.shell_context_processors: list[ft.ShellContextProcessorCallable] = [] - - #: Maps registered blueprint names to blueprint objects. The - #: dict retains the order the blueprints were registered in. - #: Blueprints can be registered multiple times, this dict does - #: not track how often they were attached. - #: - #: .. versionadded:: 0.7 - self.blueprints: dict[str, Blueprint] = {} - - #: a place where extensions can store application specific state. For - #: example this is where an extension could store database engines and - #: similar things. - #: - #: The key must match the name of the extension module. For example in - #: case of a "Flask-Foo" extension in `flask_foo`, the key would be - #: ``'foo'``. - #: - #: .. versionadded:: 0.7 - self.extensions: dict[str, t.Any] = {} - - #: The :class:`~werkzeug.routing.Map` for this instance. You can use - #: this to change the routing converters after the class was created - #: but before any routes are connected. Example:: - #: - #: from werkzeug.routing import BaseConverter - #: - #: class ListConverter(BaseConverter): - #: def to_python(self, value): - #: return value.split(',') - #: def to_url(self, values): - #: return ','.join(super(ListConverter, self).to_url(value) - #: for value in values) - #: - #: app = Flask(__name__) - #: app.url_map.converters['list'] = ListConverter - self.url_map = self.url_map_class(host_matching=host_matching) - - self.subdomain_matching = subdomain_matching - - # tracks internally if the application already handled at least one - # request. - self._got_first_request = False - - def _check_setup_finished(self, f_name: str) -> None: - if self._got_first_request: - raise AssertionError( - f"The setup method '{f_name}' can no longer be called" - " on the application. It has already handled its first" - " request, any changes will not be applied" - " consistently.\n" - "Make sure all imports, decorators, functions, etc." - " needed to set up the application are done before" - " running it." - ) - - @cached_property - def name(self) -> str: # type: ignore - """The name of the application. This is usually the import name - with the difference that it's guessed from the run file if the - import name is main. This name is used as a display name when - Flask needs the name of the application. It can be set and overridden - to change the value. - - .. versionadded:: 0.8 - """ - if self.import_name == "__main__": - fn: str | None = getattr(sys.modules["__main__"], "__file__", None) - if fn is None: - return "__main__" - return os.path.splitext(os.path.basename(fn))[0] - return self.import_name - - @cached_property - def logger(self) -> logging.Logger: - """A standard Python :class:`~logging.Logger` for the app, with - the same name as :attr:`name`. - - In debug mode, the logger's :attr:`~logging.Logger.level` will - be set to :data:`~logging.DEBUG`. - - If there are no handlers configured, a default handler will be - added. See :doc:`/logging` for more information. - - .. versionchanged:: 1.1.0 - The logger takes the same name as :attr:`name` rather than - hard-coding ``"flask.app"``. - - .. versionchanged:: 1.0.0 - Behavior was simplified. The logger is always named - ``"flask.app"``. The level is only set during configuration, - it doesn't check ``app.debug`` each time. Only one format is - used, not different ones depending on ``app.debug``. No - handlers are removed, and a handler is only added if no - handlers are already configured. - - .. versionadded:: 0.3 - """ - return create_logger(self) - - @cached_property - def jinja_env(self) -> Environment: - """The Jinja environment used to load templates. - - The environment is created the first time this property is - accessed. Changing :attr:`jinja_options` after that will have no - effect. - """ - return self.create_jinja_environment() - - def create_jinja_environment(self) -> Environment: - raise NotImplementedError() - - def make_config(self, instance_relative: bool = False) -> Config: - """Used to create the config attribute by the Flask constructor. - The `instance_relative` parameter is passed in from the constructor - of Flask (there named `instance_relative_config`) and indicates if - the config should be relative to the instance path or the root path - of the application. - - .. versionadded:: 0.8 - """ - root_path = self.root_path - if instance_relative: - root_path = self.instance_path - defaults = dict(self.default_config) - defaults["DEBUG"] = get_debug_flag() - return self.config_class(root_path, defaults) - - def make_aborter(self) -> Aborter: - """Create the object to assign to :attr:`aborter`. That object - is called by :func:`flask.abort` to raise HTTP errors, and can - be called directly as well. - - By default, this creates an instance of :attr:`aborter_class`, - which defaults to :class:`werkzeug.exceptions.Aborter`. - - .. versionadded:: 2.2 - """ - return self.aborter_class() - - def auto_find_instance_path(self) -> str: - """Tries to locate the instance path if it was not provided to the - constructor of the application class. It will basically calculate - the path to a folder named ``instance`` next to your main file or - the package. - - .. versionadded:: 0.8 - """ - prefix, package_path = find_package(self.import_name) - if prefix is None: - return os.path.join(package_path, "instance") - return os.path.join(prefix, "var", f"{self.name}-instance") - - def create_global_jinja_loader(self) -> DispatchingJinjaLoader: - """Creates the loader for the Jinja2 environment. Can be used to - override just the loader and keeping the rest unchanged. It's - discouraged to override this function. Instead one should override - the :meth:`jinja_loader` function instead. - - The global loader dispatches between the loaders of the application - and the individual blueprints. - - .. versionadded:: 0.7 - """ - return DispatchingJinjaLoader(self) - - def select_jinja_autoescape(self, filename: str) -> bool: - """Returns ``True`` if autoescaping should be active for the given - template name. If no template name is given, returns `True`. - - .. versionchanged:: 2.2 - Autoescaping is now enabled by default for ``.svg`` files. - - .. versionadded:: 0.5 - """ - if filename is None: - return True - return filename.endswith((".html", ".htm", ".xml", ".xhtml", ".svg")) - - @property - def debug(self) -> bool: - """Whether debug mode is enabled. When using ``flask run`` to start the - development server, an interactive debugger will be shown for unhandled - exceptions, and the server will be reloaded when code changes. This maps to the - :data:`DEBUG` config key. It may not behave as expected if set late. - - **Do not enable debug mode when deploying in production.** - - Default: ``False`` - """ - return self.config["DEBUG"] # type: ignore[no-any-return] - - @debug.setter - def debug(self, value: bool) -> None: - self.config["DEBUG"] = value - - if self.config["TEMPLATES_AUTO_RELOAD"] is None: - self.jinja_env.auto_reload = value - - @setupmethod - def register_blueprint(self, blueprint: Blueprint, **options: t.Any) -> None: - """Register a :class:`~flask.Blueprint` on the application. Keyword - arguments passed to this method will override the defaults set on the - blueprint. - - Calls the blueprint's :meth:`~flask.Blueprint.register` method after - recording the blueprint in the application's :attr:`blueprints`. - - :param blueprint: The blueprint to register. - :param url_prefix: Blueprint routes will be prefixed with this. - :param subdomain: Blueprint routes will match on this subdomain. - :param url_defaults: Blueprint routes will use these default values for - view arguments. - :param options: Additional keyword arguments are passed to - :class:`~flask.blueprints.BlueprintSetupState`. They can be - accessed in :meth:`~flask.Blueprint.record` callbacks. - - .. versionchanged:: 2.0.1 - The ``name`` option can be used to change the (pre-dotted) - name the blueprint is registered with. This allows the same - blueprint to be registered multiple times with unique names - for ``url_for``. - - .. versionadded:: 0.7 - """ - blueprint.register(self, options) - - def iter_blueprints(self) -> t.ValuesView[Blueprint]: - """Iterates over all blueprints by the order they were registered. - - .. versionadded:: 0.11 - """ - return self.blueprints.values() - - @setupmethod - def add_url_rule( - self, - rule: str, - endpoint: str | None = None, - view_func: ft.RouteCallable | None = None, - provide_automatic_options: bool | None = None, - **options: t.Any, - ) -> None: - if endpoint is None: - endpoint = _endpoint_from_view_func(view_func) # type: ignore - options["endpoint"] = endpoint - methods = options.pop("methods", None) - - # if the methods are not given and the view_func object knows its - # methods we can use that instead. If neither exists, we go with - # a tuple of only ``GET`` as default. - if methods is None: - methods = getattr(view_func, "methods", None) or ("GET",) - if isinstance(methods, str): - raise TypeError( - "Allowed methods must be a list of strings, for" - ' example: @app.route(..., methods=["POST"])' - ) - methods = {item.upper() for item in methods} - - # Methods that should always be added - required_methods = set(getattr(view_func, "required_methods", ())) - - # starting with Flask 0.8 the view_func object can disable and - # force-enable the automatic options handling. - if provide_automatic_options is None: - provide_automatic_options = getattr( - view_func, "provide_automatic_options", None - ) - - if provide_automatic_options is None: - if "OPTIONS" not in methods: - provide_automatic_options = True - required_methods.add("OPTIONS") - else: - provide_automatic_options = False - - # Add the required methods now. - methods |= required_methods - - rule_obj = self.url_rule_class(rule, methods=methods, **options) - rule_obj.provide_automatic_options = provide_automatic_options # type: ignore[attr-defined] - - self.url_map.add(rule_obj) - if view_func is not None: - old_func = self.view_functions.get(endpoint) - if old_func is not None and old_func != view_func: - raise AssertionError( - "View function mapping is overwriting an existing" - f" endpoint function: {endpoint}" - ) - self.view_functions[endpoint] = view_func - - @setupmethod - def template_filter( - self, name: str | None = None - ) -> t.Callable[[T_template_filter], T_template_filter]: - """A decorator that is used to register custom template filter. - You can specify a name for the filter, otherwise the function - name will be used. Example:: - - @app.template_filter() - def reverse(s): - return s[::-1] - - :param name: the optional name of the filter, otherwise the - function name will be used. - """ - - def decorator(f: T_template_filter) -> T_template_filter: - self.add_template_filter(f, name=name) - return f - - return decorator - - @setupmethod - def add_template_filter( - self, f: ft.TemplateFilterCallable, name: str | None = None - ) -> None: - """Register a custom template filter. Works exactly like the - :meth:`template_filter` decorator. - - :param name: the optional name of the filter, otherwise the - function name will be used. - """ - self.jinja_env.filters[name or f.__name__] = f - - @setupmethod - def template_test( - self, name: str | None = None - ) -> t.Callable[[T_template_test], T_template_test]: - """A decorator that is used to register custom template test. - You can specify a name for the test, otherwise the function - name will be used. Example:: - - @app.template_test() - def is_prime(n): - if n == 2: - return True - for i in range(2, int(math.ceil(math.sqrt(n))) + 1): - if n % i == 0: - return False - return True - - .. versionadded:: 0.10 - - :param name: the optional name of the test, otherwise the - function name will be used. - """ - - def decorator(f: T_template_test) -> T_template_test: - self.add_template_test(f, name=name) - return f - - return decorator - - @setupmethod - def add_template_test( - self, f: ft.TemplateTestCallable, name: str | None = None - ) -> None: - """Register a custom template test. Works exactly like the - :meth:`template_test` decorator. - - .. versionadded:: 0.10 - - :param name: the optional name of the test, otherwise the - function name will be used. - """ - self.jinja_env.tests[name or f.__name__] = f - - @setupmethod - def template_global( - self, name: str | None = None - ) -> t.Callable[[T_template_global], T_template_global]: - """A decorator that is used to register a custom template global function. - You can specify a name for the global function, otherwise the function - name will be used. Example:: - - @app.template_global() - def double(n): - return 2 * n - - .. versionadded:: 0.10 - - :param name: the optional name of the global function, otherwise the - function name will be used. - """ - - def decorator(f: T_template_global) -> T_template_global: - self.add_template_global(f, name=name) - return f - - return decorator - - @setupmethod - def add_template_global( - self, f: ft.TemplateGlobalCallable, name: str | None = None - ) -> None: - """Register a custom template global function. Works exactly like the - :meth:`template_global` decorator. - - .. versionadded:: 0.10 - - :param name: the optional name of the global function, otherwise the - function name will be used. - """ - self.jinja_env.globals[name or f.__name__] = f - - @setupmethod - def teardown_appcontext(self, f: T_teardown) -> T_teardown: - """Registers a function to be called when the application - context is popped. The application context is typically popped - after the request context for each request, at the end of CLI - commands, or after a manually pushed context ends. - - .. code-block:: python - - with app.app_context(): - ... - - When the ``with`` block exits (or ``ctx.pop()`` is called), the - teardown functions are called just before the app context is - made inactive. Since a request context typically also manages an - application context it would also be called when you pop a - request context. - - When a teardown function was called because of an unhandled - exception it will be passed an error object. If an - :meth:`errorhandler` is registered, it will handle the exception - and the teardown will not receive it. - - Teardown functions must avoid raising exceptions. If they - execute code that might fail they must surround that code with a - ``try``/``except`` block and log any errors. - - The return values of teardown functions are ignored. - - .. versionadded:: 0.9 - """ - self.teardown_appcontext_funcs.append(f) - return f - - @setupmethod - def shell_context_processor( - self, f: T_shell_context_processor - ) -> T_shell_context_processor: - """Registers a shell context processor function. - - .. versionadded:: 0.11 - """ - self.shell_context_processors.append(f) - return f - - def _find_error_handler( - self, e: Exception, blueprints: list[str] - ) -> ft.ErrorHandlerCallable | None: - """Return a registered error handler for an exception in this order: - blueprint handler for a specific code, app handler for a specific code, - blueprint handler for an exception class, app handler for an exception - class, or ``None`` if a suitable handler is not found. - """ - exc_class, code = self._get_exc_class_and_code(type(e)) - names = (*blueprints, None) - - for c in (code, None) if code is not None else (None,): - for name in names: - handler_map = self.error_handler_spec[name][c] - - if not handler_map: - continue - - for cls in exc_class.__mro__: - handler = handler_map.get(cls) - - if handler is not None: - return handler - return None - - def trap_http_exception(self, e: Exception) -> bool: - """Checks if an HTTP exception should be trapped or not. By default - this will return ``False`` for all exceptions except for a bad request - key error if ``TRAP_BAD_REQUEST_ERRORS`` is set to ``True``. It - also returns ``True`` if ``TRAP_HTTP_EXCEPTIONS`` is set to ``True``. - - This is called for all HTTP exceptions raised by a view function. - If it returns ``True`` for any exception the error handler for this - exception is not called and it shows up as regular exception in the - traceback. This is helpful for debugging implicitly raised HTTP - exceptions. - - .. versionchanged:: 1.0 - Bad request errors are not trapped by default in debug mode. - - .. versionadded:: 0.8 - """ - if self.config["TRAP_HTTP_EXCEPTIONS"]: - return True - - trap_bad_request = self.config["TRAP_BAD_REQUEST_ERRORS"] - - # if unset, trap key errors in debug mode - if ( - trap_bad_request is None - and self.debug - and isinstance(e, BadRequestKeyError) - ): - return True - - if trap_bad_request: - return isinstance(e, BadRequest) - - return False - - def should_ignore_error(self, error: BaseException | None) -> bool: - """This is called to figure out if an error should be ignored - or not as far as the teardown system is concerned. If this - function returns ``True`` then the teardown handlers will not be - passed the error. - - .. versionadded:: 0.10 - """ - return False - - def redirect(self, location: str, code: int = 302) -> BaseResponse: - """Create a redirect response object. - - This is called by :func:`flask.redirect`, and can be called - directly as well. - - :param location: The URL to redirect to. - :param code: The status code for the redirect. - - .. versionadded:: 2.2 - Moved from ``flask.redirect``, which calls this method. - """ - return _wz_redirect( - location, - code=code, - Response=self.response_class, # type: ignore[arg-type] - ) - - def inject_url_defaults(self, endpoint: str, values: dict[str, t.Any]) -> None: - """Injects the URL defaults for the given endpoint directly into - the values dictionary passed. This is used internally and - automatically called on URL building. - - .. versionadded:: 0.7 - """ - names: t.Iterable[str | None] = (None,) - - # url_for may be called outside a request context, parse the - # passed endpoint instead of using request.blueprints. - if "." in endpoint: - names = chain( - names, reversed(_split_blueprint_path(endpoint.rpartition(".")[0])) - ) - - for name in names: - if name in self.url_default_functions: - for func in self.url_default_functions[name]: - func(endpoint, values) - - def handle_url_build_error( - self, error: BuildError, endpoint: str, values: dict[str, t.Any] - ) -> str: - """Called by :meth:`.url_for` if a - :exc:`~werkzeug.routing.BuildError` was raised. If this returns - a value, it will be returned by ``url_for``, otherwise the error - will be re-raised. - - Each function in :attr:`url_build_error_handlers` is called with - ``error``, ``endpoint`` and ``values``. If a function returns - ``None`` or raises a ``BuildError``, it is skipped. Otherwise, - its return value is returned by ``url_for``. - - :param error: The active ``BuildError`` being handled. - :param endpoint: The endpoint being built. - :param values: The keyword arguments passed to ``url_for``. - """ - for handler in self.url_build_error_handlers: - try: - rv = handler(error, endpoint, values) - except BuildError as e: - # make error available outside except block - error = e - else: - if rv is not None: - return rv - - # Re-raise if called with an active exception, otherwise raise - # the passed in exception. - if error is sys.exc_info()[1]: - raise - - raise error diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/blueprints.py b/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/blueprints.py deleted file mode 100644 index 4f912cc..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/blueprints.py +++ /dev/null @@ -1,632 +0,0 @@ -from __future__ import annotations - -import os -import typing as t -from collections import defaultdict -from functools import update_wrapper - -from .. import typing as ft -from .scaffold import _endpoint_from_view_func -from .scaffold import _sentinel -from .scaffold import Scaffold -from .scaffold import setupmethod - -if t.TYPE_CHECKING: # pragma: no cover - from .app import App - -DeferredSetupFunction = t.Callable[["BlueprintSetupState"], None] -T_after_request = t.TypeVar("T_after_request", bound=ft.AfterRequestCallable[t.Any]) -T_before_request = t.TypeVar("T_before_request", bound=ft.BeforeRequestCallable) -T_error_handler = t.TypeVar("T_error_handler", bound=ft.ErrorHandlerCallable) -T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable) -T_template_context_processor = t.TypeVar( - "T_template_context_processor", bound=ft.TemplateContextProcessorCallable -) -T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallable) -T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable) -T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable) -T_url_defaults = t.TypeVar("T_url_defaults", bound=ft.URLDefaultCallable) -T_url_value_preprocessor = t.TypeVar( - "T_url_value_preprocessor", bound=ft.URLValuePreprocessorCallable -) - - -class BlueprintSetupState: - """Temporary holder object for registering a blueprint with the - application. An instance of this class is created by the - :meth:`~flask.Blueprint.make_setup_state` method and later passed - to all register callback functions. - """ - - def __init__( - self, - blueprint: Blueprint, - app: App, - options: t.Any, - first_registration: bool, - ) -> None: - #: a reference to the current application - self.app = app - - #: a reference to the blueprint that created this setup state. - self.blueprint = blueprint - - #: a dictionary with all options that were passed to the - #: :meth:`~flask.Flask.register_blueprint` method. - self.options = options - - #: as blueprints can be registered multiple times with the - #: application and not everything wants to be registered - #: multiple times on it, this attribute can be used to figure - #: out if the blueprint was registered in the past already. - self.first_registration = first_registration - - subdomain = self.options.get("subdomain") - if subdomain is None: - subdomain = self.blueprint.subdomain - - #: The subdomain that the blueprint should be active for, ``None`` - #: otherwise. - self.subdomain = subdomain - - url_prefix = self.options.get("url_prefix") - if url_prefix is None: - url_prefix = self.blueprint.url_prefix - #: The prefix that should be used for all URLs defined on the - #: blueprint. - self.url_prefix = url_prefix - - self.name = self.options.get("name", blueprint.name) - self.name_prefix = self.options.get("name_prefix", "") - - #: A dictionary with URL defaults that is added to each and every - #: URL that was defined with the blueprint. - self.url_defaults = dict(self.blueprint.url_values_defaults) - self.url_defaults.update(self.options.get("url_defaults", ())) - - def add_url_rule( - self, - rule: str, - endpoint: str | None = None, - view_func: ft.RouteCallable | None = None, - **options: t.Any, - ) -> None: - """A helper method to register a rule (and optionally a view function) - to the application. The endpoint is automatically prefixed with the - blueprint's name. - """ - if self.url_prefix is not None: - if rule: - rule = "/".join((self.url_prefix.rstrip("/"), rule.lstrip("/"))) - else: - rule = self.url_prefix - options.setdefault("subdomain", self.subdomain) - if endpoint is None: - endpoint = _endpoint_from_view_func(view_func) # type: ignore - defaults = self.url_defaults - if "defaults" in options: - defaults = dict(defaults, **options.pop("defaults")) - - self.app.add_url_rule( - rule, - f"{self.name_prefix}.{self.name}.{endpoint}".lstrip("."), - view_func, - defaults=defaults, - **options, - ) - - -class Blueprint(Scaffold): - """Represents a blueprint, a collection of routes and other - app-related functions that can be registered on a real application - later. - - A blueprint is an object that allows defining application functions - without requiring an application object ahead of time. It uses the - same decorators as :class:`~flask.Flask`, but defers the need for an - application by recording them for later registration. - - Decorating a function with a blueprint creates a deferred function - that is called with :class:`~flask.blueprints.BlueprintSetupState` - when the blueprint is registered on an application. - - See :doc:`/blueprints` for more information. - - :param name: The name of the blueprint. Will be prepended to each - endpoint name. - :param import_name: The name of the blueprint package, usually - ``__name__``. This helps locate the ``root_path`` for the - blueprint. - :param static_folder: A folder with static files that should be - served by the blueprint's static route. The path is relative to - the blueprint's root path. Blueprint static files are disabled - by default. - :param static_url_path: The url to serve static files from. - Defaults to ``static_folder``. If the blueprint does not have - a ``url_prefix``, the app's static route will take precedence, - and the blueprint's static files won't be accessible. - :param template_folder: A folder with templates that should be added - to the app's template search path. The path is relative to the - blueprint's root path. Blueprint templates are disabled by - default. Blueprint templates have a lower precedence than those - in the app's templates folder. - :param url_prefix: A path to prepend to all of the blueprint's URLs, - to make them distinct from the rest of the app's routes. - :param subdomain: A subdomain that blueprint routes will match on by - default. - :param url_defaults: A dict of default values that blueprint routes - will receive by default. - :param root_path: By default, the blueprint will automatically set - this based on ``import_name``. In certain situations this - automatic detection can fail, so the path can be specified - manually instead. - - .. versionchanged:: 1.1.0 - Blueprints have a ``cli`` group to register nested CLI commands. - The ``cli_group`` parameter controls the name of the group under - the ``flask`` command. - - .. versionadded:: 0.7 - """ - - _got_registered_once = False - - def __init__( - self, - name: str, - import_name: str, - static_folder: str | os.PathLike[str] | None = None, - static_url_path: str | None = None, - template_folder: str | os.PathLike[str] | None = None, - url_prefix: str | None = None, - subdomain: str | None = None, - url_defaults: dict[str, t.Any] | None = None, - root_path: str | None = None, - cli_group: str | None = _sentinel, # type: ignore[assignment] - ): - super().__init__( - import_name=import_name, - static_folder=static_folder, - static_url_path=static_url_path, - template_folder=template_folder, - root_path=root_path, - ) - - if not name: - raise ValueError("'name' may not be empty.") - - if "." in name: - raise ValueError("'name' may not contain a dot '.' character.") - - self.name = name - self.url_prefix = url_prefix - self.subdomain = subdomain - self.deferred_functions: list[DeferredSetupFunction] = [] - - if url_defaults is None: - url_defaults = {} - - self.url_values_defaults = url_defaults - self.cli_group = cli_group - self._blueprints: list[tuple[Blueprint, dict[str, t.Any]]] = [] - - def _check_setup_finished(self, f_name: str) -> None: - if self._got_registered_once: - raise AssertionError( - f"The setup method '{f_name}' can no longer be called on the blueprint" - f" '{self.name}'. It has already been registered at least once, any" - " changes will not be applied consistently.\n" - "Make sure all imports, decorators, functions, etc. needed to set up" - " the blueprint are done before registering it." - ) - - @setupmethod - def record(self, func: DeferredSetupFunction) -> None: - """Registers a function that is called when the blueprint is - registered on the application. This function is called with the - state as argument as returned by the :meth:`make_setup_state` - method. - """ - self.deferred_functions.append(func) - - @setupmethod - def record_once(self, func: DeferredSetupFunction) -> None: - """Works like :meth:`record` but wraps the function in another - function that will ensure the function is only called once. If the - blueprint is registered a second time on the application, the - function passed is not called. - """ - - def wrapper(state: BlueprintSetupState) -> None: - if state.first_registration: - func(state) - - self.record(update_wrapper(wrapper, func)) - - def make_setup_state( - self, app: App, options: dict[str, t.Any], first_registration: bool = False - ) -> BlueprintSetupState: - """Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState` - object that is later passed to the register callback functions. - Subclasses can override this to return a subclass of the setup state. - """ - return BlueprintSetupState(self, app, options, first_registration) - - @setupmethod - def register_blueprint(self, blueprint: Blueprint, **options: t.Any) -> None: - """Register a :class:`~flask.Blueprint` on this blueprint. Keyword - arguments passed to this method will override the defaults set - on the blueprint. - - .. versionchanged:: 2.0.1 - The ``name`` option can be used to change the (pre-dotted) - name the blueprint is registered with. This allows the same - blueprint to be registered multiple times with unique names - for ``url_for``. - - .. versionadded:: 2.0 - """ - if blueprint is self: - raise ValueError("Cannot register a blueprint on itself") - self._blueprints.append((blueprint, options)) - - def register(self, app: App, options: dict[str, t.Any]) -> None: - """Called by :meth:`Flask.register_blueprint` to register all - views and callbacks registered on the blueprint with the - application. Creates a :class:`.BlueprintSetupState` and calls - each :meth:`record` callback with it. - - :param app: The application this blueprint is being registered - with. - :param options: Keyword arguments forwarded from - :meth:`~Flask.register_blueprint`. - - .. versionchanged:: 2.3 - Nested blueprints now correctly apply subdomains. - - .. versionchanged:: 2.1 - Registering the same blueprint with the same name multiple - times is an error. - - .. versionchanged:: 2.0.1 - Nested blueprints are registered with their dotted name. - This allows different blueprints with the same name to be - nested at different locations. - - .. versionchanged:: 2.0.1 - The ``name`` option can be used to change the (pre-dotted) - name the blueprint is registered with. This allows the same - blueprint to be registered multiple times with unique names - for ``url_for``. - """ - name_prefix = options.get("name_prefix", "") - self_name = options.get("name", self.name) - name = f"{name_prefix}.{self_name}".lstrip(".") - - if name in app.blueprints: - bp_desc = "this" if app.blueprints[name] is self else "a different" - existing_at = f" '{name}'" if self_name != name else "" - - raise ValueError( - f"The name '{self_name}' is already registered for" - f" {bp_desc} blueprint{existing_at}. Use 'name=' to" - f" provide a unique name." - ) - - first_bp_registration = not any(bp is self for bp in app.blueprints.values()) - first_name_registration = name not in app.blueprints - - app.blueprints[name] = self - self._got_registered_once = True - state = self.make_setup_state(app, options, first_bp_registration) - - if self.has_static_folder: - state.add_url_rule( - f"{self.static_url_path}/", - view_func=self.send_static_file, # type: ignore[attr-defined] - endpoint="static", - ) - - # Merge blueprint data into parent. - if first_bp_registration or first_name_registration: - self._merge_blueprint_funcs(app, name) - - for deferred in self.deferred_functions: - deferred(state) - - cli_resolved_group = options.get("cli_group", self.cli_group) - - if self.cli.commands: - if cli_resolved_group is None: - app.cli.commands.update(self.cli.commands) - elif cli_resolved_group is _sentinel: - self.cli.name = name - app.cli.add_command(self.cli) - else: - self.cli.name = cli_resolved_group - app.cli.add_command(self.cli) - - for blueprint, bp_options in self._blueprints: - bp_options = bp_options.copy() - bp_url_prefix = bp_options.get("url_prefix") - bp_subdomain = bp_options.get("subdomain") - - if bp_subdomain is None: - bp_subdomain = blueprint.subdomain - - if state.subdomain is not None and bp_subdomain is not None: - bp_options["subdomain"] = bp_subdomain + "." + state.subdomain - elif bp_subdomain is not None: - bp_options["subdomain"] = bp_subdomain - elif state.subdomain is not None: - bp_options["subdomain"] = state.subdomain - - if bp_url_prefix is None: - bp_url_prefix = blueprint.url_prefix - - if state.url_prefix is not None and bp_url_prefix is not None: - bp_options["url_prefix"] = ( - state.url_prefix.rstrip("/") + "/" + bp_url_prefix.lstrip("/") - ) - elif bp_url_prefix is not None: - bp_options["url_prefix"] = bp_url_prefix - elif state.url_prefix is not None: - bp_options["url_prefix"] = state.url_prefix - - bp_options["name_prefix"] = name - blueprint.register(app, bp_options) - - def _merge_blueprint_funcs(self, app: App, name: str) -> None: - def extend( - bp_dict: dict[ft.AppOrBlueprintKey, list[t.Any]], - parent_dict: dict[ft.AppOrBlueprintKey, list[t.Any]], - ) -> None: - for key, values in bp_dict.items(): - key = name if key is None else f"{name}.{key}" - parent_dict[key].extend(values) - - for key, value in self.error_handler_spec.items(): - key = name if key is None else f"{name}.{key}" - value = defaultdict( - dict, - { - code: {exc_class: func for exc_class, func in code_values.items()} - for code, code_values in value.items() - }, - ) - app.error_handler_spec[key] = value - - for endpoint, func in self.view_functions.items(): - app.view_functions[endpoint] = func - - extend(self.before_request_funcs, app.before_request_funcs) - extend(self.after_request_funcs, app.after_request_funcs) - extend( - self.teardown_request_funcs, - app.teardown_request_funcs, - ) - extend(self.url_default_functions, app.url_default_functions) - extend(self.url_value_preprocessors, app.url_value_preprocessors) - extend(self.template_context_processors, app.template_context_processors) - - @setupmethod - def add_url_rule( - self, - rule: str, - endpoint: str | None = None, - view_func: ft.RouteCallable | None = None, - provide_automatic_options: bool | None = None, - **options: t.Any, - ) -> None: - """Register a URL rule with the blueprint. See :meth:`.Flask.add_url_rule` for - full documentation. - - The URL rule is prefixed with the blueprint's URL prefix. The endpoint name, - used with :func:`url_for`, is prefixed with the blueprint's name. - """ - if endpoint and "." in endpoint: - raise ValueError("'endpoint' may not contain a dot '.' character.") - - if view_func and hasattr(view_func, "__name__") and "." in view_func.__name__: - raise ValueError("'view_func' name may not contain a dot '.' character.") - - self.record( - lambda s: s.add_url_rule( - rule, - endpoint, - view_func, - provide_automatic_options=provide_automatic_options, - **options, - ) - ) - - @setupmethod - def app_template_filter( - self, name: str | None = None - ) -> t.Callable[[T_template_filter], T_template_filter]: - """Register a template filter, available in any template rendered by the - application. Equivalent to :meth:`.Flask.template_filter`. - - :param name: the optional name of the filter, otherwise the - function name will be used. - """ - - def decorator(f: T_template_filter) -> T_template_filter: - self.add_app_template_filter(f, name=name) - return f - - return decorator - - @setupmethod - def add_app_template_filter( - self, f: ft.TemplateFilterCallable, name: str | None = None - ) -> None: - """Register a template filter, available in any template rendered by the - application. Works like the :meth:`app_template_filter` decorator. Equivalent to - :meth:`.Flask.add_template_filter`. - - :param name: the optional name of the filter, otherwise the - function name will be used. - """ - - def register_template(state: BlueprintSetupState) -> None: - state.app.jinja_env.filters[name or f.__name__] = f - - self.record_once(register_template) - - @setupmethod - def app_template_test( - self, name: str | None = None - ) -> t.Callable[[T_template_test], T_template_test]: - """Register a template test, available in any template rendered by the - application. Equivalent to :meth:`.Flask.template_test`. - - .. versionadded:: 0.10 - - :param name: the optional name of the test, otherwise the - function name will be used. - """ - - def decorator(f: T_template_test) -> T_template_test: - self.add_app_template_test(f, name=name) - return f - - return decorator - - @setupmethod - def add_app_template_test( - self, f: ft.TemplateTestCallable, name: str | None = None - ) -> None: - """Register a template test, available in any template rendered by the - application. Works like the :meth:`app_template_test` decorator. Equivalent to - :meth:`.Flask.add_template_test`. - - .. versionadded:: 0.10 - - :param name: the optional name of the test, otherwise the - function name will be used. - """ - - def register_template(state: BlueprintSetupState) -> None: - state.app.jinja_env.tests[name or f.__name__] = f - - self.record_once(register_template) - - @setupmethod - def app_template_global( - self, name: str | None = None - ) -> t.Callable[[T_template_global], T_template_global]: - """Register a template global, available in any template rendered by the - application. Equivalent to :meth:`.Flask.template_global`. - - .. versionadded:: 0.10 - - :param name: the optional name of the global, otherwise the - function name will be used. - """ - - def decorator(f: T_template_global) -> T_template_global: - self.add_app_template_global(f, name=name) - return f - - return decorator - - @setupmethod - def add_app_template_global( - self, f: ft.TemplateGlobalCallable, name: str | None = None - ) -> None: - """Register a template global, available in any template rendered by the - application. Works like the :meth:`app_template_global` decorator. Equivalent to - :meth:`.Flask.add_template_global`. - - .. versionadded:: 0.10 - - :param name: the optional name of the global, otherwise the - function name will be used. - """ - - def register_template(state: BlueprintSetupState) -> None: - state.app.jinja_env.globals[name or f.__name__] = f - - self.record_once(register_template) - - @setupmethod - def before_app_request(self, f: T_before_request) -> T_before_request: - """Like :meth:`before_request`, but before every request, not only those handled - by the blueprint. Equivalent to :meth:`.Flask.before_request`. - """ - self.record_once( - lambda s: s.app.before_request_funcs.setdefault(None, []).append(f) - ) - return f - - @setupmethod - def after_app_request(self, f: T_after_request) -> T_after_request: - """Like :meth:`after_request`, but after every request, not only those handled - by the blueprint. Equivalent to :meth:`.Flask.after_request`. - """ - self.record_once( - lambda s: s.app.after_request_funcs.setdefault(None, []).append(f) - ) - return f - - @setupmethod - def teardown_app_request(self, f: T_teardown) -> T_teardown: - """Like :meth:`teardown_request`, but after every request, not only those - handled by the blueprint. Equivalent to :meth:`.Flask.teardown_request`. - """ - self.record_once( - lambda s: s.app.teardown_request_funcs.setdefault(None, []).append(f) - ) - return f - - @setupmethod - def app_context_processor( - self, f: T_template_context_processor - ) -> T_template_context_processor: - """Like :meth:`context_processor`, but for templates rendered by every view, not - only by the blueprint. Equivalent to :meth:`.Flask.context_processor`. - """ - self.record_once( - lambda s: s.app.template_context_processors.setdefault(None, []).append(f) - ) - return f - - @setupmethod - def app_errorhandler( - self, code: type[Exception] | int - ) -> t.Callable[[T_error_handler], T_error_handler]: - """Like :meth:`errorhandler`, but for every request, not only those handled by - the blueprint. Equivalent to :meth:`.Flask.errorhandler`. - """ - - def decorator(f: T_error_handler) -> T_error_handler: - def from_blueprint(state: BlueprintSetupState) -> None: - state.app.errorhandler(code)(f) - - self.record_once(from_blueprint) - return f - - return decorator - - @setupmethod - def app_url_value_preprocessor( - self, f: T_url_value_preprocessor - ) -> T_url_value_preprocessor: - """Like :meth:`url_value_preprocessor`, but for every request, not only those - handled by the blueprint. Equivalent to :meth:`.Flask.url_value_preprocessor`. - """ - self.record_once( - lambda s: s.app.url_value_preprocessors.setdefault(None, []).append(f) - ) - return f - - @setupmethod - def app_url_defaults(self, f: T_url_defaults) -> T_url_defaults: - """Like :meth:`url_defaults`, but for every request, not only those handled by - the blueprint. Equivalent to :meth:`.Flask.url_defaults`. - """ - self.record_once( - lambda s: s.app.url_default_functions.setdefault(None, []).append(f) - ) - return f diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/scaffold.py b/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/scaffold.py deleted file mode 100644 index 69e33a0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/sansio/scaffold.py +++ /dev/null @@ -1,801 +0,0 @@ -from __future__ import annotations - -import importlib.util -import os -import pathlib -import sys -import typing as t -from collections import defaultdict -from functools import update_wrapper - -from jinja2 import BaseLoader -from jinja2 import FileSystemLoader -from werkzeug.exceptions import default_exceptions -from werkzeug.exceptions import HTTPException -from werkzeug.utils import cached_property - -from .. import typing as ft -from ..helpers import get_root_path -from ..templating import _default_template_ctx_processor - -if t.TYPE_CHECKING: # pragma: no cover - from click import Group - -# a singleton sentinel value for parameter defaults -_sentinel = object() - -F = t.TypeVar("F", bound=t.Callable[..., t.Any]) -T_after_request = t.TypeVar("T_after_request", bound=ft.AfterRequestCallable[t.Any]) -T_before_request = t.TypeVar("T_before_request", bound=ft.BeforeRequestCallable) -T_error_handler = t.TypeVar("T_error_handler", bound=ft.ErrorHandlerCallable) -T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable) -T_template_context_processor = t.TypeVar( - "T_template_context_processor", bound=ft.TemplateContextProcessorCallable -) -T_url_defaults = t.TypeVar("T_url_defaults", bound=ft.URLDefaultCallable) -T_url_value_preprocessor = t.TypeVar( - "T_url_value_preprocessor", bound=ft.URLValuePreprocessorCallable -) -T_route = t.TypeVar("T_route", bound=ft.RouteCallable) - - -def setupmethod(f: F) -> F: - f_name = f.__name__ - - def wrapper_func(self: Scaffold, *args: t.Any, **kwargs: t.Any) -> t.Any: - self._check_setup_finished(f_name) - return f(self, *args, **kwargs) - - return t.cast(F, update_wrapper(wrapper_func, f)) - - -class Scaffold: - """Common behavior shared between :class:`~flask.Flask` and - :class:`~flask.blueprints.Blueprint`. - - :param import_name: The import name of the module where this object - is defined. Usually :attr:`__name__` should be used. - :param static_folder: Path to a folder of static files to serve. - If this is set, a static route will be added. - :param static_url_path: URL prefix for the static route. - :param template_folder: Path to a folder containing template files. - for rendering. If this is set, a Jinja loader will be added. - :param root_path: The path that static, template, and resource files - are relative to. Typically not set, it is discovered based on - the ``import_name``. - - .. versionadded:: 2.0 - """ - - cli: Group - name: str - _static_folder: str | None = None - _static_url_path: str | None = None - - def __init__( - self, - import_name: str, - static_folder: str | os.PathLike[str] | None = None, - static_url_path: str | None = None, - template_folder: str | os.PathLike[str] | None = None, - root_path: str | None = None, - ): - #: The name of the package or module that this object belongs - #: to. Do not change this once it is set by the constructor. - self.import_name = import_name - - self.static_folder = static_folder # type: ignore - self.static_url_path = static_url_path - - #: The path to the templates folder, relative to - #: :attr:`root_path`, to add to the template loader. ``None`` if - #: templates should not be added. - self.template_folder = template_folder - - if root_path is None: - root_path = get_root_path(self.import_name) - - #: Absolute path to the package on the filesystem. Used to look - #: up resources contained in the package. - self.root_path = root_path - - #: A dictionary mapping endpoint names to view functions. - #: - #: To register a view function, use the :meth:`route` decorator. - #: - #: This data structure is internal. It should not be modified - #: directly and its format may change at any time. - self.view_functions: dict[str, ft.RouteCallable] = {} - - #: A data structure of registered error handlers, in the format - #: ``{scope: {code: {class: handler}}}``. The ``scope`` key is - #: the name of a blueprint the handlers are active for, or - #: ``None`` for all requests. The ``code`` key is the HTTP - #: status code for ``HTTPException``, or ``None`` for - #: other exceptions. The innermost dictionary maps exception - #: classes to handler functions. - #: - #: To register an error handler, use the :meth:`errorhandler` - #: decorator. - #: - #: This data structure is internal. It should not be modified - #: directly and its format may change at any time. - self.error_handler_spec: dict[ - ft.AppOrBlueprintKey, - dict[int | None, dict[type[Exception], ft.ErrorHandlerCallable]], - ] = defaultdict(lambda: defaultdict(dict)) - - #: A data structure of functions to call at the beginning of - #: each request, in the format ``{scope: [functions]}``. The - #: ``scope`` key is the name of a blueprint the functions are - #: active for, or ``None`` for all requests. - #: - #: To register a function, use the :meth:`before_request` - #: decorator. - #: - #: This data structure is internal. It should not be modified - #: directly and its format may change at any time. - self.before_request_funcs: dict[ - ft.AppOrBlueprintKey, list[ft.BeforeRequestCallable] - ] = defaultdict(list) - - #: A data structure of functions to call at the end of each - #: request, in the format ``{scope: [functions]}``. The - #: ``scope`` key is the name of a blueprint the functions are - #: active for, or ``None`` for all requests. - #: - #: To register a function, use the :meth:`after_request` - #: decorator. - #: - #: This data structure is internal. It should not be modified - #: directly and its format may change at any time. - self.after_request_funcs: dict[ - ft.AppOrBlueprintKey, list[ft.AfterRequestCallable[t.Any]] - ] = defaultdict(list) - - #: A data structure of functions to call at the end of each - #: request even if an exception is raised, in the format - #: ``{scope: [functions]}``. The ``scope`` key is the name of a - #: blueprint the functions are active for, or ``None`` for all - #: requests. - #: - #: To register a function, use the :meth:`teardown_request` - #: decorator. - #: - #: This data structure is internal. It should not be modified - #: directly and its format may change at any time. - self.teardown_request_funcs: dict[ - ft.AppOrBlueprintKey, list[ft.TeardownCallable] - ] = defaultdict(list) - - #: A data structure of functions to call to pass extra context - #: values when rendering templates, in the format - #: ``{scope: [functions]}``. The ``scope`` key is the name of a - #: blueprint the functions are active for, or ``None`` for all - #: requests. - #: - #: To register a function, use the :meth:`context_processor` - #: decorator. - #: - #: This data structure is internal. It should not be modified - #: directly and its format may change at any time. - self.template_context_processors: dict[ - ft.AppOrBlueprintKey, list[ft.TemplateContextProcessorCallable] - ] = defaultdict(list, {None: [_default_template_ctx_processor]}) - - #: A data structure of functions to call to modify the keyword - #: arguments passed to the view function, in the format - #: ``{scope: [functions]}``. The ``scope`` key is the name of a - #: blueprint the functions are active for, or ``None`` for all - #: requests. - #: - #: To register a function, use the - #: :meth:`url_value_preprocessor` decorator. - #: - #: This data structure is internal. It should not be modified - #: directly and its format may change at any time. - self.url_value_preprocessors: dict[ - ft.AppOrBlueprintKey, - list[ft.URLValuePreprocessorCallable], - ] = defaultdict(list) - - #: A data structure of functions to call to modify the keyword - #: arguments when generating URLs, in the format - #: ``{scope: [functions]}``. The ``scope`` key is the name of a - #: blueprint the functions are active for, or ``None`` for all - #: requests. - #: - #: To register a function, use the :meth:`url_defaults` - #: decorator. - #: - #: This data structure is internal. It should not be modified - #: directly and its format may change at any time. - self.url_default_functions: dict[ - ft.AppOrBlueprintKey, list[ft.URLDefaultCallable] - ] = defaultdict(list) - - def __repr__(self) -> str: - return f"<{type(self).__name__} {self.name!r}>" - - def _check_setup_finished(self, f_name: str) -> None: - raise NotImplementedError - - @property - def static_folder(self) -> str | None: - """The absolute path to the configured static folder. ``None`` - if no static folder is set. - """ - if self._static_folder is not None: - return os.path.join(self.root_path, self._static_folder) - else: - return None - - @static_folder.setter - def static_folder(self, value: str | os.PathLike[str] | None) -> None: - if value is not None: - value = os.fspath(value).rstrip(r"\/") - - self._static_folder = value - - @property - def has_static_folder(self) -> bool: - """``True`` if :attr:`static_folder` is set. - - .. versionadded:: 0.5 - """ - return self.static_folder is not None - - @property - def static_url_path(self) -> str | None: - """The URL prefix that the static route will be accessible from. - - If it was not configured during init, it is derived from - :attr:`static_folder`. - """ - if self._static_url_path is not None: - return self._static_url_path - - if self.static_folder is not None: - basename = os.path.basename(self.static_folder) - return f"/{basename}".rstrip("/") - - return None - - @static_url_path.setter - def static_url_path(self, value: str | None) -> None: - if value is not None: - value = value.rstrip("/") - - self._static_url_path = value - - @cached_property - def jinja_loader(self) -> BaseLoader | None: - """The Jinja loader for this object's templates. By default this - is a class :class:`jinja2.loaders.FileSystemLoader` to - :attr:`template_folder` if it is set. - - .. versionadded:: 0.5 - """ - if self.template_folder is not None: - return FileSystemLoader(os.path.join(self.root_path, self.template_folder)) - else: - return None - - def _method_route( - self, - method: str, - rule: str, - options: dict[str, t.Any], - ) -> t.Callable[[T_route], T_route]: - if "methods" in options: - raise TypeError("Use the 'route' decorator to use the 'methods' argument.") - - return self.route(rule, methods=[method], **options) - - @setupmethod - def get(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: - """Shortcut for :meth:`route` with ``methods=["GET"]``. - - .. versionadded:: 2.0 - """ - return self._method_route("GET", rule, options) - - @setupmethod - def post(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: - """Shortcut for :meth:`route` with ``methods=["POST"]``. - - .. versionadded:: 2.0 - """ - return self._method_route("POST", rule, options) - - @setupmethod - def put(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: - """Shortcut for :meth:`route` with ``methods=["PUT"]``. - - .. versionadded:: 2.0 - """ - return self._method_route("PUT", rule, options) - - @setupmethod - def delete(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: - """Shortcut for :meth:`route` with ``methods=["DELETE"]``. - - .. versionadded:: 2.0 - """ - return self._method_route("DELETE", rule, options) - - @setupmethod - def patch(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: - """Shortcut for :meth:`route` with ``methods=["PATCH"]``. - - .. versionadded:: 2.0 - """ - return self._method_route("PATCH", rule, options) - - @setupmethod - def route(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: - """Decorate a view function to register it with the given URL - rule and options. Calls :meth:`add_url_rule`, which has more - details about the implementation. - - .. code-block:: python - - @app.route("/") - def index(): - return "Hello, World!" - - See :ref:`url-route-registrations`. - - The endpoint name for the route defaults to the name of the view - function if the ``endpoint`` parameter isn't passed. - - The ``methods`` parameter defaults to ``["GET"]``. ``HEAD`` and - ``OPTIONS`` are added automatically. - - :param rule: The URL rule string. - :param options: Extra options passed to the - :class:`~werkzeug.routing.Rule` object. - """ - - def decorator(f: T_route) -> T_route: - endpoint = options.pop("endpoint", None) - self.add_url_rule(rule, endpoint, f, **options) - return f - - return decorator - - @setupmethod - def add_url_rule( - self, - rule: str, - endpoint: str | None = None, - view_func: ft.RouteCallable | None = None, - provide_automatic_options: bool | None = None, - **options: t.Any, - ) -> None: - """Register a rule for routing incoming requests and building - URLs. The :meth:`route` decorator is a shortcut to call this - with the ``view_func`` argument. These are equivalent: - - .. code-block:: python - - @app.route("/") - def index(): - ... - - .. code-block:: python - - def index(): - ... - - app.add_url_rule("/", view_func=index) - - See :ref:`url-route-registrations`. - - The endpoint name for the route defaults to the name of the view - function if the ``endpoint`` parameter isn't passed. An error - will be raised if a function has already been registered for the - endpoint. - - The ``methods`` parameter defaults to ``["GET"]``. ``HEAD`` is - always added automatically, and ``OPTIONS`` is added - automatically by default. - - ``view_func`` does not necessarily need to be passed, but if the - rule should participate in routing an endpoint name must be - associated with a view function at some point with the - :meth:`endpoint` decorator. - - .. code-block:: python - - app.add_url_rule("/", endpoint="index") - - @app.endpoint("index") - def index(): - ... - - If ``view_func`` has a ``required_methods`` attribute, those - methods are added to the passed and automatic methods. If it - has a ``provide_automatic_methods`` attribute, it is used as the - default if the parameter is not passed. - - :param rule: The URL rule string. - :param endpoint: The endpoint name to associate with the rule - and view function. Used when routing and building URLs. - Defaults to ``view_func.__name__``. - :param view_func: The view function to associate with the - endpoint name. - :param provide_automatic_options: Add the ``OPTIONS`` method and - respond to ``OPTIONS`` requests automatically. - :param options: Extra options passed to the - :class:`~werkzeug.routing.Rule` object. - """ - raise NotImplementedError - - @setupmethod - def endpoint(self, endpoint: str) -> t.Callable[[F], F]: - """Decorate a view function to register it for the given - endpoint. Used if a rule is added without a ``view_func`` with - :meth:`add_url_rule`. - - .. code-block:: python - - app.add_url_rule("/ex", endpoint="example") - - @app.endpoint("example") - def example(): - ... - - :param endpoint: The endpoint name to associate with the view - function. - """ - - def decorator(f: F) -> F: - self.view_functions[endpoint] = f - return f - - return decorator - - @setupmethod - def before_request(self, f: T_before_request) -> T_before_request: - """Register a function to run before each request. - - For example, this can be used to open a database connection, or - to load the logged in user from the session. - - .. code-block:: python - - @app.before_request - def load_user(): - if "user_id" in session: - g.user = db.session.get(session["user_id"]) - - The function will be called without any arguments. If it returns - a non-``None`` value, the value is handled as if it was the - return value from the view, and further request handling is - stopped. - - This is available on both app and blueprint objects. When used on an app, this - executes before every request. When used on a blueprint, this executes before - every request that the blueprint handles. To register with a blueprint and - execute before every request, use :meth:`.Blueprint.before_app_request`. - """ - self.before_request_funcs.setdefault(None, []).append(f) - return f - - @setupmethod - def after_request(self, f: T_after_request) -> T_after_request: - """Register a function to run after each request to this object. - - The function is called with the response object, and must return - a response object. This allows the functions to modify or - replace the response before it is sent. - - If a function raises an exception, any remaining - ``after_request`` functions will not be called. Therefore, this - should not be used for actions that must execute, such as to - close resources. Use :meth:`teardown_request` for that. - - This is available on both app and blueprint objects. When used on an app, this - executes after every request. When used on a blueprint, this executes after - every request that the blueprint handles. To register with a blueprint and - execute after every request, use :meth:`.Blueprint.after_app_request`. - """ - self.after_request_funcs.setdefault(None, []).append(f) - return f - - @setupmethod - def teardown_request(self, f: T_teardown) -> T_teardown: - """Register a function to be called when the request context is - popped. Typically this happens at the end of each request, but - contexts may be pushed manually as well during testing. - - .. code-block:: python - - with app.test_request_context(): - ... - - When the ``with`` block exits (or ``ctx.pop()`` is called), the - teardown functions are called just before the request context is - made inactive. - - When a teardown function was called because of an unhandled - exception it will be passed an error object. If an - :meth:`errorhandler` is registered, it will handle the exception - and the teardown will not receive it. - - Teardown functions must avoid raising exceptions. If they - execute code that might fail they must surround that code with a - ``try``/``except`` block and log any errors. - - The return values of teardown functions are ignored. - - This is available on both app and blueprint objects. When used on an app, this - executes after every request. When used on a blueprint, this executes after - every request that the blueprint handles. To register with a blueprint and - execute after every request, use :meth:`.Blueprint.teardown_app_request`. - """ - self.teardown_request_funcs.setdefault(None, []).append(f) - return f - - @setupmethod - def context_processor( - self, - f: T_template_context_processor, - ) -> T_template_context_processor: - """Registers a template context processor function. These functions run before - rendering a template. The keys of the returned dict are added as variables - available in the template. - - This is available on both app and blueprint objects. When used on an app, this - is called for every rendered template. When used on a blueprint, this is called - for templates rendered from the blueprint's views. To register with a blueprint - and affect every template, use :meth:`.Blueprint.app_context_processor`. - """ - self.template_context_processors[None].append(f) - return f - - @setupmethod - def url_value_preprocessor( - self, - f: T_url_value_preprocessor, - ) -> T_url_value_preprocessor: - """Register a URL value preprocessor function for all view - functions in the application. These functions will be called before the - :meth:`before_request` functions. - - The function can modify the values captured from the matched url before - they are passed to the view. For example, this can be used to pop a - common language code value and place it in ``g`` rather than pass it to - every view. - - The function is passed the endpoint name and values dict. The return - value is ignored. - - This is available on both app and blueprint objects. When used on an app, this - is called for every request. When used on a blueprint, this is called for - requests that the blueprint handles. To register with a blueprint and affect - every request, use :meth:`.Blueprint.app_url_value_preprocessor`. - """ - self.url_value_preprocessors[None].append(f) - return f - - @setupmethod - def url_defaults(self, f: T_url_defaults) -> T_url_defaults: - """Callback function for URL defaults for all view functions of the - application. It's called with the endpoint and values and should - update the values passed in place. - - This is available on both app and blueprint objects. When used on an app, this - is called for every request. When used on a blueprint, this is called for - requests that the blueprint handles. To register with a blueprint and affect - every request, use :meth:`.Blueprint.app_url_defaults`. - """ - self.url_default_functions[None].append(f) - return f - - @setupmethod - def errorhandler( - self, code_or_exception: type[Exception] | int - ) -> t.Callable[[T_error_handler], T_error_handler]: - """Register a function to handle errors by code or exception class. - - A decorator that is used to register a function given an - error code. Example:: - - @app.errorhandler(404) - def page_not_found(error): - return 'This page does not exist', 404 - - You can also register handlers for arbitrary exceptions:: - - @app.errorhandler(DatabaseError) - def special_exception_handler(error): - return 'Database connection failed', 500 - - This is available on both app and blueprint objects. When used on an app, this - can handle errors from every request. When used on a blueprint, this can handle - errors from requests that the blueprint handles. To register with a blueprint - and affect every request, use :meth:`.Blueprint.app_errorhandler`. - - .. versionadded:: 0.7 - Use :meth:`register_error_handler` instead of modifying - :attr:`error_handler_spec` directly, for application wide error - handlers. - - .. versionadded:: 0.7 - One can now additionally also register custom exception types - that do not necessarily have to be a subclass of the - :class:`~werkzeug.exceptions.HTTPException` class. - - :param code_or_exception: the code as integer for the handler, or - an arbitrary exception - """ - - def decorator(f: T_error_handler) -> T_error_handler: - self.register_error_handler(code_or_exception, f) - return f - - return decorator - - @setupmethod - def register_error_handler( - self, - code_or_exception: type[Exception] | int, - f: ft.ErrorHandlerCallable, - ) -> None: - """Alternative error attach function to the :meth:`errorhandler` - decorator that is more straightforward to use for non decorator - usage. - - .. versionadded:: 0.7 - """ - exc_class, code = self._get_exc_class_and_code(code_or_exception) - self.error_handler_spec[None][code][exc_class] = f - - @staticmethod - def _get_exc_class_and_code( - exc_class_or_code: type[Exception] | int, - ) -> tuple[type[Exception], int | None]: - """Get the exception class being handled. For HTTP status codes - or ``HTTPException`` subclasses, return both the exception and - status code. - - :param exc_class_or_code: Any exception class, or an HTTP status - code as an integer. - """ - exc_class: type[Exception] - - if isinstance(exc_class_or_code, int): - try: - exc_class = default_exceptions[exc_class_or_code] - except KeyError: - raise ValueError( - f"'{exc_class_or_code}' is not a recognized HTTP" - " error code. Use a subclass of HTTPException with" - " that code instead." - ) from None - else: - exc_class = exc_class_or_code - - if isinstance(exc_class, Exception): - raise TypeError( - f"{exc_class!r} is an instance, not a class. Handlers" - " can only be registered for Exception classes or HTTP" - " error codes." - ) - - if not issubclass(exc_class, Exception): - raise ValueError( - f"'{exc_class.__name__}' is not a subclass of Exception." - " Handlers can only be registered for Exception classes" - " or HTTP error codes." - ) - - if issubclass(exc_class, HTTPException): - return exc_class, exc_class.code - else: - return exc_class, None - - -def _endpoint_from_view_func(view_func: ft.RouteCallable) -> str: - """Internal helper that returns the default endpoint for a given - function. This always is the function name. - """ - assert view_func is not None, "expected view func if endpoint is not provided." - return view_func.__name__ - - -def _path_is_relative_to(path: pathlib.PurePath, base: str) -> bool: - # Path.is_relative_to doesn't exist until Python 3.9 - try: - path.relative_to(base) - return True - except ValueError: - return False - - -def _find_package_path(import_name: str) -> str: - """Find the path that contains the package or module.""" - root_mod_name, _, _ = import_name.partition(".") - - try: - root_spec = importlib.util.find_spec(root_mod_name) - - if root_spec is None: - raise ValueError("not found") - except (ImportError, ValueError): - # ImportError: the machinery told us it does not exist - # ValueError: - # - the module name was invalid - # - the module name is __main__ - # - we raised `ValueError` due to `root_spec` being `None` - return os.getcwd() - - if root_spec.submodule_search_locations: - if root_spec.origin is None or root_spec.origin == "namespace": - # namespace package - package_spec = importlib.util.find_spec(import_name) - - if package_spec is not None and package_spec.submodule_search_locations: - # Pick the path in the namespace that contains the submodule. - package_path = pathlib.Path( - os.path.commonpath(package_spec.submodule_search_locations) - ) - search_location = next( - location - for location in root_spec.submodule_search_locations - if _path_is_relative_to(package_path, location) - ) - else: - # Pick the first path. - search_location = root_spec.submodule_search_locations[0] - - return os.path.dirname(search_location) - else: - # package with __init__.py - return os.path.dirname(os.path.dirname(root_spec.origin)) - else: - # module - return os.path.dirname(root_spec.origin) # type: ignore[type-var, return-value] - - -def find_package(import_name: str) -> tuple[str | None, str]: - """Find the prefix that a package is installed under, and the path - that it would be imported from. - - The prefix is the directory containing the standard directory - hierarchy (lib, bin, etc.). If the package is not installed to the - system (:attr:`sys.prefix`) or a virtualenv (``site-packages``), - ``None`` is returned. - - The path is the entry in :attr:`sys.path` that contains the package - for import. If the package is not installed, it's assumed that the - package was imported from the current working directory. - """ - package_path = _find_package_path(import_name) - py_prefix = os.path.abspath(sys.prefix) - - # installed to the system - if _path_is_relative_to(pathlib.PurePath(package_path), py_prefix): - return py_prefix, package_path - - site_parent, site_folder = os.path.split(package_path) - - # installed to a virtualenv - if site_folder.lower() == "site-packages": - parent, folder = os.path.split(site_parent) - - # Windows (prefix/lib/site-packages) - if folder.lower() == "lib": - return parent, package_path - - # Unix (prefix/lib/pythonX.Y/site-packages) - if os.path.basename(parent).lower() == "lib": - return os.path.dirname(parent), package_path - - # something else (prefix/site-packages) - return site_parent, package_path - - # not installed - return None, package_path diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/sessions.py b/plotter-app/venv/lib/python3.8/site-packages/flask/sessions.py deleted file mode 100644 index ee19ad6..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/sessions.py +++ /dev/null @@ -1,379 +0,0 @@ -from __future__ import annotations - -import hashlib -import typing as t -from collections.abc import MutableMapping -from datetime import datetime -from datetime import timezone - -from itsdangerous import BadSignature -from itsdangerous import URLSafeTimedSerializer -from werkzeug.datastructures import CallbackDict - -from .json.tag import TaggedJSONSerializer - -if t.TYPE_CHECKING: # pragma: no cover - import typing_extensions as te - - from .app import Flask - from .wrappers import Request - from .wrappers import Response - - -# TODO generic when Python > 3.8 -class SessionMixin(MutableMapping): # type: ignore[type-arg] - """Expands a basic dictionary with session attributes.""" - - @property - def permanent(self) -> bool: - """This reflects the ``'_permanent'`` key in the dict.""" - return self.get("_permanent", False) - - @permanent.setter - def permanent(self, value: bool) -> None: - self["_permanent"] = bool(value) - - #: Some implementations can detect whether a session is newly - #: created, but that is not guaranteed. Use with caution. The mixin - # default is hard-coded ``False``. - new = False - - #: Some implementations can detect changes to the session and set - #: this when that happens. The mixin default is hard coded to - #: ``True``. - modified = True - - #: Some implementations can detect when session data is read or - #: written and set this when that happens. The mixin default is hard - #: coded to ``True``. - accessed = True - - -# TODO generic when Python > 3.8 -class SecureCookieSession(CallbackDict, SessionMixin): # type: ignore[type-arg] - """Base class for sessions based on signed cookies. - - This session backend will set the :attr:`modified` and - :attr:`accessed` attributes. It cannot reliably track whether a - session is new (vs. empty), so :attr:`new` remains hard coded to - ``False``. - """ - - #: When data is changed, this is set to ``True``. Only the session - #: dictionary itself is tracked; if the session contains mutable - #: data (for example a nested dict) then this must be set to - #: ``True`` manually when modifying that data. The session cookie - #: will only be written to the response if this is ``True``. - modified = False - - #: When data is read or written, this is set to ``True``. Used by - # :class:`.SecureCookieSessionInterface` to add a ``Vary: Cookie`` - #: header, which allows caching proxies to cache different pages for - #: different users. - accessed = False - - def __init__(self, initial: t.Any = None) -> None: - def on_update(self: te.Self) -> None: - self.modified = True - self.accessed = True - - super().__init__(initial, on_update) - - def __getitem__(self, key: str) -> t.Any: - self.accessed = True - return super().__getitem__(key) - - def get(self, key: str, default: t.Any = None) -> t.Any: - self.accessed = True - return super().get(key, default) - - def setdefault(self, key: str, default: t.Any = None) -> t.Any: - self.accessed = True - return super().setdefault(key, default) - - -class NullSession(SecureCookieSession): - """Class used to generate nicer error messages if sessions are not - available. Will still allow read-only access to the empty session - but fail on setting. - """ - - def _fail(self, *args: t.Any, **kwargs: t.Any) -> t.NoReturn: - raise RuntimeError( - "The session is unavailable because no secret " - "key was set. Set the secret_key on the " - "application to something unique and secret." - ) - - __setitem__ = __delitem__ = clear = pop = popitem = update = setdefault = _fail # type: ignore # noqa: B950 - del _fail - - -class SessionInterface: - """The basic interface you have to implement in order to replace the - default session interface which uses werkzeug's securecookie - implementation. The only methods you have to implement are - :meth:`open_session` and :meth:`save_session`, the others have - useful defaults which you don't need to change. - - The session object returned by the :meth:`open_session` method has to - provide a dictionary like interface plus the properties and methods - from the :class:`SessionMixin`. We recommend just subclassing a dict - and adding that mixin:: - - class Session(dict, SessionMixin): - pass - - If :meth:`open_session` returns ``None`` Flask will call into - :meth:`make_null_session` to create a session that acts as replacement - if the session support cannot work because some requirement is not - fulfilled. The default :class:`NullSession` class that is created - will complain that the secret key was not set. - - To replace the session interface on an application all you have to do - is to assign :attr:`flask.Flask.session_interface`:: - - app = Flask(__name__) - app.session_interface = MySessionInterface() - - Multiple requests with the same session may be sent and handled - concurrently. When implementing a new session interface, consider - whether reads or writes to the backing store must be synchronized. - There is no guarantee on the order in which the session for each - request is opened or saved, it will occur in the order that requests - begin and end processing. - - .. versionadded:: 0.8 - """ - - #: :meth:`make_null_session` will look here for the class that should - #: be created when a null session is requested. Likewise the - #: :meth:`is_null_session` method will perform a typecheck against - #: this type. - null_session_class = NullSession - - #: A flag that indicates if the session interface is pickle based. - #: This can be used by Flask extensions to make a decision in regards - #: to how to deal with the session object. - #: - #: .. versionadded:: 0.10 - pickle_based = False - - def make_null_session(self, app: Flask) -> NullSession: - """Creates a null session which acts as a replacement object if the - real session support could not be loaded due to a configuration - error. This mainly aids the user experience because the job of the - null session is to still support lookup without complaining but - modifications are answered with a helpful error message of what - failed. - - This creates an instance of :attr:`null_session_class` by default. - """ - return self.null_session_class() - - def is_null_session(self, obj: object) -> bool: - """Checks if a given object is a null session. Null sessions are - not asked to be saved. - - This checks if the object is an instance of :attr:`null_session_class` - by default. - """ - return isinstance(obj, self.null_session_class) - - def get_cookie_name(self, app: Flask) -> str: - """The name of the session cookie. Uses``app.config["SESSION_COOKIE_NAME"]``.""" - return app.config["SESSION_COOKIE_NAME"] # type: ignore[no-any-return] - - def get_cookie_domain(self, app: Flask) -> str | None: - """The value of the ``Domain`` parameter on the session cookie. If not set, - browsers will only send the cookie to the exact domain it was set from. - Otherwise, they will send it to any subdomain of the given value as well. - - Uses the :data:`SESSION_COOKIE_DOMAIN` config. - - .. versionchanged:: 2.3 - Not set by default, does not fall back to ``SERVER_NAME``. - """ - return app.config["SESSION_COOKIE_DOMAIN"] # type: ignore[no-any-return] - - def get_cookie_path(self, app: Flask) -> str: - """Returns the path for which the cookie should be valid. The - default implementation uses the value from the ``SESSION_COOKIE_PATH`` - config var if it's set, and falls back to ``APPLICATION_ROOT`` or - uses ``/`` if it's ``None``. - """ - return app.config["SESSION_COOKIE_PATH"] or app.config["APPLICATION_ROOT"] # type: ignore[no-any-return] - - def get_cookie_httponly(self, app: Flask) -> bool: - """Returns True if the session cookie should be httponly. This - currently just returns the value of the ``SESSION_COOKIE_HTTPONLY`` - config var. - """ - return app.config["SESSION_COOKIE_HTTPONLY"] # type: ignore[no-any-return] - - def get_cookie_secure(self, app: Flask) -> bool: - """Returns True if the cookie should be secure. This currently - just returns the value of the ``SESSION_COOKIE_SECURE`` setting. - """ - return app.config["SESSION_COOKIE_SECURE"] # type: ignore[no-any-return] - - def get_cookie_samesite(self, app: Flask) -> str | None: - """Return ``'Strict'`` or ``'Lax'`` if the cookie should use the - ``SameSite`` attribute. This currently just returns the value of - the :data:`SESSION_COOKIE_SAMESITE` setting. - """ - return app.config["SESSION_COOKIE_SAMESITE"] # type: ignore[no-any-return] - - def get_expiration_time(self, app: Flask, session: SessionMixin) -> datetime | None: - """A helper method that returns an expiration date for the session - or ``None`` if the session is linked to the browser session. The - default implementation returns now + the permanent session - lifetime configured on the application. - """ - if session.permanent: - return datetime.now(timezone.utc) + app.permanent_session_lifetime - return None - - def should_set_cookie(self, app: Flask, session: SessionMixin) -> bool: - """Used by session backends to determine if a ``Set-Cookie`` header - should be set for this session cookie for this response. If the session - has been modified, the cookie is set. If the session is permanent and - the ``SESSION_REFRESH_EACH_REQUEST`` config is true, the cookie is - always set. - - This check is usually skipped if the session was deleted. - - .. versionadded:: 0.11 - """ - - return session.modified or ( - session.permanent and app.config["SESSION_REFRESH_EACH_REQUEST"] - ) - - def open_session(self, app: Flask, request: Request) -> SessionMixin | None: - """This is called at the beginning of each request, after - pushing the request context, before matching the URL. - - This must return an object which implements a dictionary-like - interface as well as the :class:`SessionMixin` interface. - - This will return ``None`` to indicate that loading failed in - some way that is not immediately an error. The request - context will fall back to using :meth:`make_null_session` - in this case. - """ - raise NotImplementedError() - - def save_session( - self, app: Flask, session: SessionMixin, response: Response - ) -> None: - """This is called at the end of each request, after generating - a response, before removing the request context. It is skipped - if :meth:`is_null_session` returns ``True``. - """ - raise NotImplementedError() - - -session_json_serializer = TaggedJSONSerializer() - - -def _lazy_sha1(string: bytes = b"") -> t.Any: - """Don't access ``hashlib.sha1`` until runtime. FIPS builds may not include - SHA-1, in which case the import and use as a default would fail before the - developer can configure something else. - """ - return hashlib.sha1(string) - - -class SecureCookieSessionInterface(SessionInterface): - """The default session interface that stores sessions in signed cookies - through the :mod:`itsdangerous` module. - """ - - #: the salt that should be applied on top of the secret key for the - #: signing of cookie based sessions. - salt = "cookie-session" - #: the hash function to use for the signature. The default is sha1 - digest_method = staticmethod(_lazy_sha1) - #: the name of the itsdangerous supported key derivation. The default - #: is hmac. - key_derivation = "hmac" - #: A python serializer for the payload. The default is a compact - #: JSON derived serializer with support for some extra Python types - #: such as datetime objects or tuples. - serializer = session_json_serializer - session_class = SecureCookieSession - - def get_signing_serializer(self, app: Flask) -> URLSafeTimedSerializer | None: - if not app.secret_key: - return None - signer_kwargs = dict( - key_derivation=self.key_derivation, digest_method=self.digest_method - ) - return URLSafeTimedSerializer( - app.secret_key, - salt=self.salt, - serializer=self.serializer, - signer_kwargs=signer_kwargs, - ) - - def open_session(self, app: Flask, request: Request) -> SecureCookieSession | None: - s = self.get_signing_serializer(app) - if s is None: - return None - val = request.cookies.get(self.get_cookie_name(app)) - if not val: - return self.session_class() - max_age = int(app.permanent_session_lifetime.total_seconds()) - try: - data = s.loads(val, max_age=max_age) - return self.session_class(data) - except BadSignature: - return self.session_class() - - def save_session( - self, app: Flask, session: SessionMixin, response: Response - ) -> None: - name = self.get_cookie_name(app) - domain = self.get_cookie_domain(app) - path = self.get_cookie_path(app) - secure = self.get_cookie_secure(app) - samesite = self.get_cookie_samesite(app) - httponly = self.get_cookie_httponly(app) - - # Add a "Vary: Cookie" header if the session was accessed at all. - if session.accessed: - response.vary.add("Cookie") - - # If the session is modified to be empty, remove the cookie. - # If the session is empty, return without setting the cookie. - if not session: - if session.modified: - response.delete_cookie( - name, - domain=domain, - path=path, - secure=secure, - samesite=samesite, - httponly=httponly, - ) - response.vary.add("Cookie") - - return - - if not self.should_set_cookie(app, session): - return - - expires = self.get_expiration_time(app, session) - val = self.get_signing_serializer(app).dumps(dict(session)) # type: ignore - response.set_cookie( - name, - val, # type: ignore - expires=expires, - httponly=httponly, - domain=domain, - path=path, - secure=secure, - samesite=samesite, - ) - response.vary.add("Cookie") diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/signals.py b/plotter-app/venv/lib/python3.8/site-packages/flask/signals.py deleted file mode 100644 index 444fda9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/signals.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import annotations - -from blinker import Namespace - -# This namespace is only for signals provided by Flask itself. -_signals = Namespace() - -template_rendered = _signals.signal("template-rendered") -before_render_template = _signals.signal("before-render-template") -request_started = _signals.signal("request-started") -request_finished = _signals.signal("request-finished") -request_tearing_down = _signals.signal("request-tearing-down") -got_request_exception = _signals.signal("got-request-exception") -appcontext_tearing_down = _signals.signal("appcontext-tearing-down") -appcontext_pushed = _signals.signal("appcontext-pushed") -appcontext_popped = _signals.signal("appcontext-popped") -message_flashed = _signals.signal("message-flashed") diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/templating.py b/plotter-app/venv/lib/python3.8/site-packages/flask/templating.py deleted file mode 100644 index 618a3b3..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/templating.py +++ /dev/null @@ -1,219 +0,0 @@ -from __future__ import annotations - -import typing as t - -from jinja2 import BaseLoader -from jinja2 import Environment as BaseEnvironment -from jinja2 import Template -from jinja2 import TemplateNotFound - -from .globals import _cv_app -from .globals import _cv_request -from .globals import current_app -from .globals import request -from .helpers import stream_with_context -from .signals import before_render_template -from .signals import template_rendered - -if t.TYPE_CHECKING: # pragma: no cover - from .app import Flask - from .sansio.app import App - from .sansio.scaffold import Scaffold - - -def _default_template_ctx_processor() -> dict[str, t.Any]: - """Default template context processor. Injects `request`, - `session` and `g`. - """ - appctx = _cv_app.get(None) - reqctx = _cv_request.get(None) - rv: dict[str, t.Any] = {} - if appctx is not None: - rv["g"] = appctx.g - if reqctx is not None: - rv["request"] = reqctx.request - rv["session"] = reqctx.session - return rv - - -class Environment(BaseEnvironment): - """Works like a regular Jinja2 environment but has some additional - knowledge of how Flask's blueprint works so that it can prepend the - name of the blueprint to referenced templates if necessary. - """ - - def __init__(self, app: App, **options: t.Any) -> None: - if "loader" not in options: - options["loader"] = app.create_global_jinja_loader() - BaseEnvironment.__init__(self, **options) - self.app = app - - -class DispatchingJinjaLoader(BaseLoader): - """A loader that looks for templates in the application and all - the blueprint folders. - """ - - def __init__(self, app: App) -> None: - self.app = app - - def get_source( - self, environment: BaseEnvironment, template: str - ) -> tuple[str, str | None, t.Callable[[], bool] | None]: - if self.app.config["EXPLAIN_TEMPLATE_LOADING"]: - return self._get_source_explained(environment, template) - return self._get_source_fast(environment, template) - - def _get_source_explained( - self, environment: BaseEnvironment, template: str - ) -> tuple[str, str | None, t.Callable[[], bool] | None]: - attempts = [] - rv: tuple[str, str | None, t.Callable[[], bool] | None] | None - trv: None | (tuple[str, str | None, t.Callable[[], bool] | None]) = None - - for srcobj, loader in self._iter_loaders(template): - try: - rv = loader.get_source(environment, template) - if trv is None: - trv = rv - except TemplateNotFound: - rv = None - attempts.append((loader, srcobj, rv)) - - from .debughelpers import explain_template_loading_attempts - - explain_template_loading_attempts(self.app, template, attempts) - - if trv is not None: - return trv - raise TemplateNotFound(template) - - def _get_source_fast( - self, environment: BaseEnvironment, template: str - ) -> tuple[str, str | None, t.Callable[[], bool] | None]: - for _srcobj, loader in self._iter_loaders(template): - try: - return loader.get_source(environment, template) - except TemplateNotFound: - continue - raise TemplateNotFound(template) - - def _iter_loaders(self, template: str) -> t.Iterator[tuple[Scaffold, BaseLoader]]: - loader = self.app.jinja_loader - if loader is not None: - yield self.app, loader - - for blueprint in self.app.iter_blueprints(): - loader = blueprint.jinja_loader - if loader is not None: - yield blueprint, loader - - def list_templates(self) -> list[str]: - result = set() - loader = self.app.jinja_loader - if loader is not None: - result.update(loader.list_templates()) - - for blueprint in self.app.iter_blueprints(): - loader = blueprint.jinja_loader - if loader is not None: - for template in loader.list_templates(): - result.add(template) - - return list(result) - - -def _render(app: Flask, template: Template, context: dict[str, t.Any]) -> str: - app.update_template_context(context) - before_render_template.send( - app, _async_wrapper=app.ensure_sync, template=template, context=context - ) - rv = template.render(context) - template_rendered.send( - app, _async_wrapper=app.ensure_sync, template=template, context=context - ) - return rv - - -def render_template( - template_name_or_list: str | Template | list[str | Template], - **context: t.Any, -) -> str: - """Render a template by name with the given context. - - :param template_name_or_list: The name of the template to render. If - a list is given, the first name to exist will be rendered. - :param context: The variables to make available in the template. - """ - app = current_app._get_current_object() # type: ignore[attr-defined] - template = app.jinja_env.get_or_select_template(template_name_or_list) - return _render(app, template, context) - - -def render_template_string(source: str, **context: t.Any) -> str: - """Render a template from the given source string with the given - context. - - :param source: The source code of the template to render. - :param context: The variables to make available in the template. - """ - app = current_app._get_current_object() # type: ignore[attr-defined] - template = app.jinja_env.from_string(source) - return _render(app, template, context) - - -def _stream( - app: Flask, template: Template, context: dict[str, t.Any] -) -> t.Iterator[str]: - app.update_template_context(context) - before_render_template.send( - app, _async_wrapper=app.ensure_sync, template=template, context=context - ) - - def generate() -> t.Iterator[str]: - yield from template.generate(context) - template_rendered.send( - app, _async_wrapper=app.ensure_sync, template=template, context=context - ) - - rv = generate() - - # If a request context is active, keep it while generating. - if request: - rv = stream_with_context(rv) - - return rv - - -def stream_template( - template_name_or_list: str | Template | list[str | Template], - **context: t.Any, -) -> t.Iterator[str]: - """Render a template by name with the given context as a stream. - This returns an iterator of strings, which can be used as a - streaming response from a view. - - :param template_name_or_list: The name of the template to render. If - a list is given, the first name to exist will be rendered. - :param context: The variables to make available in the template. - - .. versionadded:: 2.2 - """ - app = current_app._get_current_object() # type: ignore[attr-defined] - template = app.jinja_env.get_or_select_template(template_name_or_list) - return _stream(app, template, context) - - -def stream_template_string(source: str, **context: t.Any) -> t.Iterator[str]: - """Render a template from the given source string with the given - context as a stream. This returns an iterator of strings, which can - be used as a streaming response from a view. - - :param source: The source code of the template to render. - :param context: The variables to make available in the template. - - .. versionadded:: 2.2 - """ - app = current_app._get_current_object() # type: ignore[attr-defined] - template = app.jinja_env.from_string(source) - return _stream(app, template, context) diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/testing.py b/plotter-app/venv/lib/python3.8/site-packages/flask/testing.py deleted file mode 100644 index a27b7c8..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/testing.py +++ /dev/null @@ -1,298 +0,0 @@ -from __future__ import annotations - -import importlib.metadata -import typing as t -from contextlib import contextmanager -from contextlib import ExitStack -from copy import copy -from types import TracebackType -from urllib.parse import urlsplit - -import werkzeug.test -from click.testing import CliRunner -from werkzeug.test import Client -from werkzeug.wrappers import Request as BaseRequest - -from .cli import ScriptInfo -from .sessions import SessionMixin - -if t.TYPE_CHECKING: # pragma: no cover - from _typeshed.wsgi import WSGIEnvironment - from werkzeug.test import TestResponse - - from .app import Flask - - -class EnvironBuilder(werkzeug.test.EnvironBuilder): - """An :class:`~werkzeug.test.EnvironBuilder`, that takes defaults from the - application. - - :param app: The Flask application to configure the environment from. - :param path: URL path being requested. - :param base_url: Base URL where the app is being served, which - ``path`` is relative to. If not given, built from - :data:`PREFERRED_URL_SCHEME`, ``subdomain``, - :data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`. - :param subdomain: Subdomain name to append to :data:`SERVER_NAME`. - :param url_scheme: Scheme to use instead of - :data:`PREFERRED_URL_SCHEME`. - :param json: If given, this is serialized as JSON and passed as - ``data``. Also defaults ``content_type`` to - ``application/json``. - :param args: other positional arguments passed to - :class:`~werkzeug.test.EnvironBuilder`. - :param kwargs: other keyword arguments passed to - :class:`~werkzeug.test.EnvironBuilder`. - """ - - def __init__( - self, - app: Flask, - path: str = "/", - base_url: str | None = None, - subdomain: str | None = None, - url_scheme: str | None = None, - *args: t.Any, - **kwargs: t.Any, - ) -> None: - assert not (base_url or subdomain or url_scheme) or ( - base_url is not None - ) != bool( - subdomain or url_scheme - ), 'Cannot pass "subdomain" or "url_scheme" with "base_url".' - - if base_url is None: - http_host = app.config.get("SERVER_NAME") or "localhost" - app_root = app.config["APPLICATION_ROOT"] - - if subdomain: - http_host = f"{subdomain}.{http_host}" - - if url_scheme is None: - url_scheme = app.config["PREFERRED_URL_SCHEME"] - - url = urlsplit(path) - base_url = ( - f"{url.scheme or url_scheme}://{url.netloc or http_host}" - f"/{app_root.lstrip('/')}" - ) - path = url.path - - if url.query: - sep = b"?" if isinstance(url.query, bytes) else "?" - path += sep + url.query - - self.app = app - super().__init__(path, base_url, *args, **kwargs) - - def json_dumps(self, obj: t.Any, **kwargs: t.Any) -> str: # type: ignore - """Serialize ``obj`` to a JSON-formatted string. - - The serialization will be configured according to the config associated - with this EnvironBuilder's ``app``. - """ - return self.app.json.dumps(obj, **kwargs) - - -_werkzeug_version = "" - - -def _get_werkzeug_version() -> str: - global _werkzeug_version - - if not _werkzeug_version: - _werkzeug_version = importlib.metadata.version("werkzeug") - - return _werkzeug_version - - -class FlaskClient(Client): - """Works like a regular Werkzeug test client but has knowledge about - Flask's contexts to defer the cleanup of the request context until - the end of a ``with`` block. For general information about how to - use this class refer to :class:`werkzeug.test.Client`. - - .. versionchanged:: 0.12 - `app.test_client()` includes preset default environment, which can be - set after instantiation of the `app.test_client()` object in - `client.environ_base`. - - Basic usage is outlined in the :doc:`/testing` chapter. - """ - - application: Flask - - def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: - super().__init__(*args, **kwargs) - self.preserve_context = False - self._new_contexts: list[t.ContextManager[t.Any]] = [] - self._context_stack = ExitStack() - self.environ_base = { - "REMOTE_ADDR": "127.0.0.1", - "HTTP_USER_AGENT": f"Werkzeug/{_get_werkzeug_version()}", - } - - @contextmanager - def session_transaction( - self, *args: t.Any, **kwargs: t.Any - ) -> t.Iterator[SessionMixin]: - """When used in combination with a ``with`` statement this opens a - session transaction. This can be used to modify the session that - the test client uses. Once the ``with`` block is left the session is - stored back. - - :: - - with client.session_transaction() as session: - session['value'] = 42 - - Internally this is implemented by going through a temporary test - request context and since session handling could depend on - request variables this function accepts the same arguments as - :meth:`~flask.Flask.test_request_context` which are directly - passed through. - """ - if self._cookies is None: - raise TypeError( - "Cookies are disabled. Create a client with 'use_cookies=True'." - ) - - app = self.application - ctx = app.test_request_context(*args, **kwargs) - self._add_cookies_to_wsgi(ctx.request.environ) - - with ctx: - sess = app.session_interface.open_session(app, ctx.request) - - if sess is None: - raise RuntimeError("Session backend did not open a session.") - - yield sess - resp = app.response_class() - - if app.session_interface.is_null_session(sess): - return - - with ctx: - app.session_interface.save_session(app, sess, resp) - - self._update_cookies_from_response( - ctx.request.host.partition(":")[0], - ctx.request.path, - resp.headers.getlist("Set-Cookie"), - ) - - def _copy_environ(self, other: WSGIEnvironment) -> WSGIEnvironment: - out = {**self.environ_base, **other} - - if self.preserve_context: - out["werkzeug.debug.preserve_context"] = self._new_contexts.append - - return out - - def _request_from_builder_args( - self, args: tuple[t.Any, ...], kwargs: dict[str, t.Any] - ) -> BaseRequest: - kwargs["environ_base"] = self._copy_environ(kwargs.get("environ_base", {})) - builder = EnvironBuilder(self.application, *args, **kwargs) - - try: - return builder.get_request() - finally: - builder.close() - - def open( - self, - *args: t.Any, - buffered: bool = False, - follow_redirects: bool = False, - **kwargs: t.Any, - ) -> TestResponse: - if args and isinstance( - args[0], (werkzeug.test.EnvironBuilder, dict, BaseRequest) - ): - if isinstance(args[0], werkzeug.test.EnvironBuilder): - builder = copy(args[0]) - builder.environ_base = self._copy_environ(builder.environ_base or {}) # type: ignore[arg-type] - request = builder.get_request() - elif isinstance(args[0], dict): - request = EnvironBuilder.from_environ( - args[0], app=self.application, environ_base=self._copy_environ({}) - ).get_request() - else: - # isinstance(args[0], BaseRequest) - request = copy(args[0]) - request.environ = self._copy_environ(request.environ) - else: - # request is None - request = self._request_from_builder_args(args, kwargs) - - # Pop any previously preserved contexts. This prevents contexts - # from being preserved across redirects or multiple requests - # within a single block. - self._context_stack.close() - - response = super().open( - request, - buffered=buffered, - follow_redirects=follow_redirects, - ) - response.json_module = self.application.json # type: ignore[assignment] - - # Re-push contexts that were preserved during the request. - while self._new_contexts: - cm = self._new_contexts.pop() - self._context_stack.enter_context(cm) - - return response - - def __enter__(self) -> FlaskClient: - if self.preserve_context: - raise RuntimeError("Cannot nest client invocations") - self.preserve_context = True - return self - - def __exit__( - self, - exc_type: type | None, - exc_value: BaseException | None, - tb: TracebackType | None, - ) -> None: - self.preserve_context = False - self._context_stack.close() - - -class FlaskCliRunner(CliRunner): - """A :class:`~click.testing.CliRunner` for testing a Flask app's - CLI commands. Typically created using - :meth:`~flask.Flask.test_cli_runner`. See :ref:`testing-cli`. - """ - - def __init__(self, app: Flask, **kwargs: t.Any) -> None: - self.app = app - super().__init__(**kwargs) - - def invoke( # type: ignore - self, cli: t.Any = None, args: t.Any = None, **kwargs: t.Any - ) -> t.Any: - """Invokes a CLI command in an isolated environment. See - :meth:`CliRunner.invoke ` for - full method documentation. See :ref:`testing-cli` for examples. - - If the ``obj`` argument is not given, passes an instance of - :class:`~flask.cli.ScriptInfo` that knows how to load the Flask - app being tested. - - :param cli: Command object to invoke. Default is the app's - :attr:`~flask.app.Flask.cli` group. - :param args: List of strings to invoke the command with. - - :return: a :class:`~click.testing.Result` object. - """ - if cli is None: - cli = self.app.cli - - if "obj" not in kwargs: - kwargs["obj"] = ScriptInfo(create_app=lambda: self.app) - - return super().invoke(cli, args, **kwargs) diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/typing.py b/plotter-app/venv/lib/python3.8/site-packages/flask/typing.py deleted file mode 100644 index cf6d4ae..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/typing.py +++ /dev/null @@ -1,90 +0,0 @@ -from __future__ import annotations - -import typing as t - -if t.TYPE_CHECKING: # pragma: no cover - from _typeshed.wsgi import WSGIApplication # noqa: F401 - from werkzeug.datastructures import Headers # noqa: F401 - from werkzeug.sansio.response import Response # noqa: F401 - -# The possible types that are directly convertible or are a Response object. -ResponseValue = t.Union[ - "Response", - str, - bytes, - t.List[t.Any], - # Only dict is actually accepted, but Mapping allows for TypedDict. - t.Mapping[str, t.Any], - t.Iterator[str], - t.Iterator[bytes], -] - -# the possible types for an individual HTTP header -# This should be a Union, but mypy doesn't pass unless it's a TypeVar. -HeaderValue = t.Union[str, t.List[str], t.Tuple[str, ...]] - -# the possible types for HTTP headers -HeadersValue = t.Union[ - "Headers", - t.Mapping[str, HeaderValue], - t.Sequence[t.Tuple[str, HeaderValue]], -] - -# The possible types returned by a route function. -ResponseReturnValue = t.Union[ - ResponseValue, - t.Tuple[ResponseValue, HeadersValue], - t.Tuple[ResponseValue, int], - t.Tuple[ResponseValue, int, HeadersValue], - "WSGIApplication", -] - -# Allow any subclass of werkzeug.Response, such as the one from Flask, -# as a callback argument. Using werkzeug.Response directly makes a -# callback annotated with flask.Response fail type checking. -ResponseClass = t.TypeVar("ResponseClass", bound="Response") - -AppOrBlueprintKey = t.Optional[str] # The App key is None, whereas blueprints are named -AfterRequestCallable = t.Union[ - t.Callable[[ResponseClass], ResponseClass], - t.Callable[[ResponseClass], t.Awaitable[ResponseClass]], -] -BeforeFirstRequestCallable = t.Union[ - t.Callable[[], None], t.Callable[[], t.Awaitable[None]] -] -BeforeRequestCallable = t.Union[ - t.Callable[[], t.Optional[ResponseReturnValue]], - t.Callable[[], t.Awaitable[t.Optional[ResponseReturnValue]]], -] -ShellContextProcessorCallable = t.Callable[[], t.Dict[str, t.Any]] -TeardownCallable = t.Union[ - t.Callable[[t.Optional[BaseException]], None], - t.Callable[[t.Optional[BaseException]], t.Awaitable[None]], -] -TemplateContextProcessorCallable = t.Union[ - t.Callable[[], t.Dict[str, t.Any]], - t.Callable[[], t.Awaitable[t.Dict[str, t.Any]]], -] -TemplateFilterCallable = t.Callable[..., t.Any] -TemplateGlobalCallable = t.Callable[..., t.Any] -TemplateTestCallable = t.Callable[..., bool] -URLDefaultCallable = t.Callable[[str, t.Dict[str, t.Any]], None] -URLValuePreprocessorCallable = t.Callable[ - [t.Optional[str], t.Optional[t.Dict[str, t.Any]]], None -] - -# This should take Exception, but that either breaks typing the argument -# with a specific exception, or decorating multiple times with different -# exceptions (and using a union type on the argument). -# https://github.com/pallets/flask/issues/4095 -# https://github.com/pallets/flask/issues/4295 -# https://github.com/pallets/flask/issues/4297 -ErrorHandlerCallable = t.Union[ - t.Callable[[t.Any], ResponseReturnValue], - t.Callable[[t.Any], t.Awaitable[ResponseReturnValue]], -] - -RouteCallable = t.Union[ - t.Callable[..., ResponseReturnValue], - t.Callable[..., t.Awaitable[ResponseReturnValue]], -] diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/views.py b/plotter-app/venv/lib/python3.8/site-packages/flask/views.py deleted file mode 100644 index 794fdc0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/views.py +++ /dev/null @@ -1,191 +0,0 @@ -from __future__ import annotations - -import typing as t - -from . import typing as ft -from .globals import current_app -from .globals import request - -F = t.TypeVar("F", bound=t.Callable[..., t.Any]) - -http_method_funcs = frozenset( - ["get", "post", "head", "options", "delete", "put", "trace", "patch"] -) - - -class View: - """Subclass this class and override :meth:`dispatch_request` to - create a generic class-based view. Call :meth:`as_view` to create a - view function that creates an instance of the class with the given - arguments and calls its ``dispatch_request`` method with any URL - variables. - - See :doc:`views` for a detailed guide. - - .. code-block:: python - - class Hello(View): - init_every_request = False - - def dispatch_request(self, name): - return f"Hello, {name}!" - - app.add_url_rule( - "/hello/", view_func=Hello.as_view("hello") - ) - - Set :attr:`methods` on the class to change what methods the view - accepts. - - Set :attr:`decorators` on the class to apply a list of decorators to - the generated view function. Decorators applied to the class itself - will not be applied to the generated view function! - - Set :attr:`init_every_request` to ``False`` for efficiency, unless - you need to store request-global data on ``self``. - """ - - #: The methods this view is registered for. Uses the same default - #: (``["GET", "HEAD", "OPTIONS"]``) as ``route`` and - #: ``add_url_rule`` by default. - methods: t.ClassVar[t.Collection[str] | None] = None - - #: Control whether the ``OPTIONS`` method is handled automatically. - #: Uses the same default (``True``) as ``route`` and - #: ``add_url_rule`` by default. - provide_automatic_options: t.ClassVar[bool | None] = None - - #: A list of decorators to apply, in order, to the generated view - #: function. Remember that ``@decorator`` syntax is applied bottom - #: to top, so the first decorator in the list would be the bottom - #: decorator. - #: - #: .. versionadded:: 0.8 - decorators: t.ClassVar[list[t.Callable[[F], F]]] = [] - - #: Create a new instance of this view class for every request by - #: default. If a view subclass sets this to ``False``, the same - #: instance is used for every request. - #: - #: A single instance is more efficient, especially if complex setup - #: is done during init. However, storing data on ``self`` is no - #: longer safe across requests, and :data:`~flask.g` should be used - #: instead. - #: - #: .. versionadded:: 2.2 - init_every_request: t.ClassVar[bool] = True - - def dispatch_request(self) -> ft.ResponseReturnValue: - """The actual view function behavior. Subclasses must override - this and return a valid response. Any variables from the URL - rule are passed as keyword arguments. - """ - raise NotImplementedError() - - @classmethod - def as_view( - cls, name: str, *class_args: t.Any, **class_kwargs: t.Any - ) -> ft.RouteCallable: - """Convert the class into a view function that can be registered - for a route. - - By default, the generated view will create a new instance of the - view class for every request and call its - :meth:`dispatch_request` method. If the view class sets - :attr:`init_every_request` to ``False``, the same instance will - be used for every request. - - Except for ``name``, all other arguments passed to this method - are forwarded to the view class ``__init__`` method. - - .. versionchanged:: 2.2 - Added the ``init_every_request`` class attribute. - """ - if cls.init_every_request: - - def view(**kwargs: t.Any) -> ft.ResponseReturnValue: - self = view.view_class( # type: ignore[attr-defined] - *class_args, **class_kwargs - ) - return current_app.ensure_sync(self.dispatch_request)(**kwargs) # type: ignore[no-any-return] - - else: - self = cls(*class_args, **class_kwargs) - - def view(**kwargs: t.Any) -> ft.ResponseReturnValue: - return current_app.ensure_sync(self.dispatch_request)(**kwargs) # type: ignore[no-any-return] - - if cls.decorators: - view.__name__ = name - view.__module__ = cls.__module__ - for decorator in cls.decorators: - view = decorator(view) - - # We attach the view class to the view function for two reasons: - # first of all it allows us to easily figure out what class-based - # view this thing came from, secondly it's also used for instantiating - # the view class so you can actually replace it with something else - # for testing purposes and debugging. - view.view_class = cls # type: ignore - view.__name__ = name - view.__doc__ = cls.__doc__ - view.__module__ = cls.__module__ - view.methods = cls.methods # type: ignore - view.provide_automatic_options = cls.provide_automatic_options # type: ignore - return view - - -class MethodView(View): - """Dispatches request methods to the corresponding instance methods. - For example, if you implement a ``get`` method, it will be used to - handle ``GET`` requests. - - This can be useful for defining a REST API. - - :attr:`methods` is automatically set based on the methods defined on - the class. - - See :doc:`views` for a detailed guide. - - .. code-block:: python - - class CounterAPI(MethodView): - def get(self): - return str(session.get("counter", 0)) - - def post(self): - session["counter"] = session.get("counter", 0) + 1 - return redirect(url_for("counter")) - - app.add_url_rule( - "/counter", view_func=CounterAPI.as_view("counter") - ) - """ - - def __init_subclass__(cls, **kwargs: t.Any) -> None: - super().__init_subclass__(**kwargs) - - if "methods" not in cls.__dict__: - methods = set() - - for base in cls.__bases__: - if getattr(base, "methods", None): - methods.update(base.methods) # type: ignore[attr-defined] - - for key in http_method_funcs: - if hasattr(cls, key): - methods.add(key.upper()) - - if methods: - cls.methods = methods - - def dispatch_request(self, **kwargs: t.Any) -> ft.ResponseReturnValue: - meth = getattr(self, request.method.lower(), None) - - # If the request method is HEAD and we don't have a handler for it - # retry with GET. - if meth is None and request.method == "HEAD": - meth = getattr(self, "get", None) - - assert meth is not None, f"Unimplemented method {request.method!r}" - return current_app.ensure_sync(meth)(**kwargs) # type: ignore[no-any-return] diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask/wrappers.py b/plotter-app/venv/lib/python3.8/site-packages/flask/wrappers.py deleted file mode 100644 index c1eca80..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask/wrappers.py +++ /dev/null @@ -1,174 +0,0 @@ -from __future__ import annotations - -import typing as t - -from werkzeug.exceptions import BadRequest -from werkzeug.exceptions import HTTPException -from werkzeug.wrappers import Request as RequestBase -from werkzeug.wrappers import Response as ResponseBase - -from . import json -from .globals import current_app -from .helpers import _split_blueprint_path - -if t.TYPE_CHECKING: # pragma: no cover - from werkzeug.routing import Rule - - -class Request(RequestBase): - """The request object used by default in Flask. Remembers the - matched endpoint and view arguments. - - It is what ends up as :class:`~flask.request`. If you want to replace - the request object used you can subclass this and set - :attr:`~flask.Flask.request_class` to your subclass. - - The request object is a :class:`~werkzeug.wrappers.Request` subclass and - provides all of the attributes Werkzeug defines plus a few Flask - specific ones. - """ - - json_module: t.Any = json - - #: The internal URL rule that matched the request. This can be - #: useful to inspect which methods are allowed for the URL from - #: a before/after handler (``request.url_rule.methods``) etc. - #: Though if the request's method was invalid for the URL rule, - #: the valid list is available in ``routing_exception.valid_methods`` - #: instead (an attribute of the Werkzeug exception - #: :exc:`~werkzeug.exceptions.MethodNotAllowed`) - #: because the request was never internally bound. - #: - #: .. versionadded:: 0.6 - url_rule: Rule | None = None - - #: A dict of view arguments that matched the request. If an exception - #: happened when matching, this will be ``None``. - view_args: dict[str, t.Any] | None = None - - #: If matching the URL failed, this is the exception that will be - #: raised / was raised as part of the request handling. This is - #: usually a :exc:`~werkzeug.exceptions.NotFound` exception or - #: something similar. - routing_exception: HTTPException | None = None - - @property - def max_content_length(self) -> int | None: # type: ignore[override] - """Read-only view of the ``MAX_CONTENT_LENGTH`` config key.""" - if current_app: - return current_app.config["MAX_CONTENT_LENGTH"] # type: ignore[no-any-return] - else: - return None - - @property - def endpoint(self) -> str | None: - """The endpoint that matched the request URL. - - This will be ``None`` if matching failed or has not been - performed yet. - - This in combination with :attr:`view_args` can be used to - reconstruct the same URL or a modified URL. - """ - if self.url_rule is not None: - return self.url_rule.endpoint - - return None - - @property - def blueprint(self) -> str | None: - """The registered name of the current blueprint. - - This will be ``None`` if the endpoint is not part of a - blueprint, or if URL matching failed or has not been performed - yet. - - This does not necessarily match the name the blueprint was - created with. It may have been nested, or registered with a - different name. - """ - endpoint = self.endpoint - - if endpoint is not None and "." in endpoint: - return endpoint.rpartition(".")[0] - - return None - - @property - def blueprints(self) -> list[str]: - """The registered names of the current blueprint upwards through - parent blueprints. - - This will be an empty list if there is no current blueprint, or - if URL matching failed. - - .. versionadded:: 2.0.1 - """ - name = self.blueprint - - if name is None: - return [] - - return _split_blueprint_path(name) - - def _load_form_data(self) -> None: - super()._load_form_data() - - # In debug mode we're replacing the files multidict with an ad-hoc - # subclass that raises a different error for key errors. - if ( - current_app - and current_app.debug - and self.mimetype != "multipart/form-data" - and not self.files - ): - from .debughelpers import attach_enctype_error_multidict - - attach_enctype_error_multidict(self) - - def on_json_loading_failed(self, e: ValueError | None) -> t.Any: - try: - return super().on_json_loading_failed(e) - except BadRequest as e: - if current_app and current_app.debug: - raise - - raise BadRequest() from e - - -class Response(ResponseBase): - """The response object that is used by default in Flask. Works like the - response object from Werkzeug but is set to have an HTML mimetype by - default. Quite often you don't have to create this object yourself because - :meth:`~flask.Flask.make_response` will take care of that for you. - - If you want to replace the response object used you can subclass this and - set :attr:`~flask.Flask.response_class` to your subclass. - - .. versionchanged:: 1.0 - JSON support is added to the response, like the request. This is useful - when testing to get the test client response data as JSON. - - .. versionchanged:: 1.0 - - Added :attr:`max_cookie_size`. - """ - - default_mimetype: str | None = "text/html" - - json_module = json - - autocorrect_location_header = False - - @property - def max_cookie_size(self) -> int: # type: ignore - """Read-only view of the :data:`MAX_COOKIE_SIZE` config key. - - See :attr:`~werkzeug.wrappers.Response.max_cookie_size` in - Werkzeug's docs. - """ - if current_app: - return current_app.config["MAX_COOKIE_SIZE"] # type: ignore[no-any-return] - - # return Werkzeug's default when not in an app context - return super().max_cookie_size diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/__init__.py deleted file mode 100644 index 8fd5f9e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -""" - -Flask-FlatPages provides a collection of pages to your Flask application. - -Pages are built from "flat" text files as opposed to a relational database. - -:copyright: (c) 2010-2015 by Simon Sapin, 2013-2015 by Igor Davydenko. -:license: BSD, see LICENSE for more details. -""" - -from .flatpages import FlatPages # noqa -from .page import Page # noqa -from .utils import pygmented_markdown, pygments_style_defs # noqa - -__author__ = "Simon Sapin, Igor Davydenko, Padraic Calpin" -__license__ = "BSD License" -__version__ = "0.8.2" diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 49c6bb4..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/__pycache__/flatpages.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/__pycache__/flatpages.cpython-38.pyc deleted file mode 100644 index ba0ba94..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/__pycache__/flatpages.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/__pycache__/imports.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/__pycache__/imports.cpython-38.pyc deleted file mode 100644 index 1e008d8..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/__pycache__/imports.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/__pycache__/page.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/__pycache__/page.cpython-38.pyc deleted file mode 100644 index 921c376..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/__pycache__/page.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/__pycache__/utils.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/__pycache__/utils.cpython-38.pyc deleted file mode 100644 index 0e82eaf..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/__pycache__/utils.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/flatpages.py b/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/flatpages.py deleted file mode 100644 index 690329f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/flatpages.py +++ /dev/null @@ -1,420 +0,0 @@ -"""Flatpages extension.""" -import operator -import os -from itertools import takewhile - - -import six -from flask import abort -from werkzeug.utils import cached_property, import_string -from yaml import ( - BlockMappingStartToken, - BlockSequenceStartToken, - DocumentEndToken, - DocumentStartToken, - FlowMappingStartToken, - FlowSequenceStartToken, - KeyToken, - SafeLoader, - ScalarToken, -) - - -from .page import Page -from .utils import force_unicode, NamedStringIO, pygmented_markdown - - -if six.PY3: - from inspect import getfullargspec -else: - from inspect import getargspec as getfullargspec - - -START_TOKENS = ( - BlockMappingStartToken, - BlockSequenceStartToken, - DocumentStartToken, - FlowMappingStartToken, - FlowSequenceStartToken, - KeyToken, -) - - -def _check_newline_token(token): - return ( - isinstance(token, ScalarToken) - and token.style is None - and "\n" in token.value - ) - - -def _check_continue_parsing_tokens(token): - return not ( - isinstance(token, (DocumentStartToken, DocumentEndToken)) - or token is None - ) - - -class FlatPages(object): - """A collection of :class:`Page` objects.""" - - #: Default configuration for FlatPages extension - default_config = ( - ("root", "pages"), - ("extension", ".html"), - ("encoding", "utf-8"), - ("html_renderer", pygmented_markdown), - ("markdown_extensions", ["codehilite"]), - ("extension_configs", {}), - ("auto_reload", "if debug"), - ("case_insensitive", False), - ("instance_relative", False), - ("legacy_meta_parser", False), - ) - - def __init__(self, app=None, name=None): - """Initialize FlatPages extension. - - :param app: Your application. Can be omitted if you call - :meth:`init_app` later. - :type app: A :class:`~flask.Flask` instance - :param name: The name for this FlatPages instance. Used for looking - up config values using - 'FLATPAGES_%s_%s' % (name.upper(), key) - By default, no name is used, so configuration is - done by specifying config values using - 'FLATPAGES_%s' % (key) - Typically, you only need to set this parameter if you - want to use multiple :class:`FlatPages instances within the - same Flask application. - :type name: string - - .. versionchanged:: 0.6 - - New parameter `name` to support multiple FlatPages instances. - """ - self.name = name - - if name is None: - self.config_prefix = "FLATPAGES" - else: - self.config_prefix = "_".join(("FLATPAGES", name.upper())) - - #: dict of filename: (page object, mtime when loaded) - self._file_cache = {} - - if app: - self.init_app(app) - - def __iter__(self): - """Iterate on all :class:`Page` objects.""" - return six.itervalues(self._pages) - - def config(self, key): - """Read actual configuration from Flask application config. - - :param key: Lowercase config key from :attr:`default_config` tuple - """ - return self.app.config["_".join((self.config_prefix, key.upper()))] - - def get(self, path, default=None): - """ - Return the :class:`Page` object at ``path``. - - Returns ``default`` if there is no such page. - """ - # This may trigger the property. Do it outside of the try block. - pages = self._pages - try: - return pages[path] - except KeyError: - return default - - def get_or_404(self, path): - """ - Return the :class:`Page` object at ``path``. - - Raise Flask's 404 error if there is no such page. - """ - page = self.get(path) - if not page: - abort(404) - return page - - def init_app(self, app): - """ - Use to initialize an application. - - Ueful for passing an app later and app factory patterns. - - :param app: your application - :type app: a :class:`~flask.Flask` instance - """ - # Store default config to application - for key, value in self.default_config: - config_key = "_".join((self.config_prefix, key.upper())) - app.config.setdefault(config_key, value) - - # Register function to forget all pages if necessary - app.before_request(self._conditional_auto_reset) - - # And finally store application to current instance and current - # instance to application - if "flatpages" not in app.extensions: - app.extensions["flatpages"] = {} - app.extensions["flatpages"][self.name] = self - self.app = app - - def reload(self): - """Forget all pages. - - All pages will be reloaded next time they're accessed. - """ - try: - # This will "unshadow" the cached_property. - # The property will be re-executed on next access. - del self.__dict__["_pages"] - except KeyError: - pass - - @property - def root(self): - """Full path to the directory where pages are looked for. - - This corresponds to the `FLATPAGES_%(name)s_ROOT` config value, - interpreted as relative to the app's root directory, or as relative - to the app's instance folder if `FLATPAGES_%(name)s_INSTANCE_RELATIVE` - is set to `True`. - - """ - if self.config("instance_relative"): - root_dir = os.path.join( - self.app.instance_path, self.config("root") - ) - else: - root_dir = os.path.join(self.app.root_path, self.config("root")) - return force_unicode(root_dir) - - def _conditional_auto_reset(self): - """Reset if configured to do so on new requests.""" - auto = self.config("auto_reload") - if auto == "if debug": - auto = self.app.debug - if auto: - self.reload() - - def _load_file(self, path, filename, rel_path): - """ - Load file from file system and cache it. - - We store the result as a tuple of :class:`Path` and the file `mtime`. - """ - mtime = os.path.getmtime(filename) - cached = self._file_cache.get(filename) - - if cached and cached[1] == mtime: - page = cached[0] - else: - encoding = self.config("encoding") - - if six.PY3: - with open(filename, encoding=encoding) as handler: - content = handler.read() - else: - with open(filename) as handler: - content = handler.read().decode(encoding) - - page = self._parse(content, path, rel_path) - self._file_cache[filename] = (page, mtime) - - return page - - @cached_property - def _pages(self): - """ - Walk the page root directory and return a dict of pages. - - Returns a dictionary of pages keyed by their path. - """ - - def _walker(): - """ - Walk over directory and find all possible flatpages. - - Returns files which end with the string or sequence given by - ``FLATPAGES_%(name)s_EXTENSION``. - """ - for cur_path, _, filenames in os.walk(self.root): - rel_path = cur_path.replace(self.root, "").lstrip(os.sep) - path_prefix = tuple(rel_path.split(os.sep)) if rel_path else () - - for name in filenames: - if not name.endswith(extension): - continue - - full_name = os.path.join(cur_path, name) - name_without_extension = [ - name[: -len(item)] - for item in extension - if name.endswith(item) - ][0] - path = "/".join(path_prefix + (name_without_extension,)) - if self.config("case_insensitive"): - path = path.lower() - yield (path, full_name, rel_path) - - # Read extension from config - extension = self.config("extension") - - # Support for multiple extensions - if isinstance(extension, six.string_types): - if "," in extension: - extension = tuple(extension.split(",")) - else: - extension = (extension,) - elif isinstance(extension, (list, set)): - extension = tuple(extension) - - # FlatPage extension should be a string or a sequence - if not isinstance(extension, tuple): - raise ValueError( - "Invalid value for FlatPages extension. Should be a string or " - "a sequence, got {0} instead: {1}".format( - type(extension).__name__, extension - ) - ) - pages = {} - for path, full_name, rel_path in _walker(): - if path in pages: - raise ValueError( - "Multiple pages found which correspond to the same path. " - "This error can arise when using multiple extensions." - ) - pages[path] = self._load_file(path, full_name, rel_path) - return pages - - def _libyaml_parser(self, content, path): - if not six.PY3: - content = force_unicode(content) - yaml_loader = SafeLoader(NamedStringIO(content, path)) - yaml_loader.get_token() # Get stream start token - token = yaml_loader.get_token() - if not isinstance(token, START_TOKENS): - meta = "" - content = content.lstrip("\n") - else: - lines = content.split("\n") - if isinstance(token, DocumentStartToken): - token = yaml_loader.get_token() - newline_token = None - while _check_continue_parsing_tokens(token): - try: - token = yaml_loader.get_token() - if _check_newline_token(token) and newline_token is None: - newline_token = token - except Exception: - break - if token is None and newline_token is None: - meta = content - content = "" - else: - if token is not None: - meta_end_line = token.end_mark.line + 1 - else: - meta_end_line = newline_token.start_mark.line - meta_end_line += lines[meta_end_line:].index("") - meta = "\n".join(lines[:meta_end_line]) - content = "\n".join(lines[meta_end_line:]).lstrip("\n") - if not six.PY3: - return force_unicode(meta), force_unicode(content) - return meta, content - - def _legacy_parser(self, content): - lines = iter(content.split("\n")) - - # Read lines until an empty line is encountered. - meta = "\n".join(takewhile(operator.methodcaller("strip"), lines)) - # The rest is the content. `lines` is an iterator so it continues - # where `itertools.takewhile` left it. - content = "\n".join(lines) - return meta, content - - def _parse(self, content, path, rel_path): - """Parse a flatpage file, i.e. read and parse its meta data and body. - - :return: initialized :class:`Page` instance. - """ - if self.config("legacy_meta_parser"): - meta, content = self._legacy_parser(content) - else: - meta, content = self._libyaml_parser(content, path) - - # Now we ready to get HTML renderer function - html_renderer = self.config("html_renderer") - - # If function is not callable yet, import it - if not callable(html_renderer): - html_renderer = import_string(html_renderer) - - # Make able to pass custom arguments to renderer function - html_renderer = self._smart_html_renderer(html_renderer) - - # Assign the relative path (to root) for use in the page object - folder = rel_path - - # Initialize and return Page instance - return Page(path, meta, content, html_renderer, folder) - - def _smart_html_renderer(self, html_renderer): - """ - Wrappper to enable rendering functions with differing signatures. - - We stay backwards compatible by using reflection, i.e. we inspect the - given rendering function's signature in order to find out how many - arguments the function takes. - - .. versionchanged:: 0.6 - - Support for HTML renderer functions with signature - ``f(body, flatpages, page)``, where ``page`` is an instance of - :class:`Page`. - - .. versionchanged:: 0.5 - - Support for HTML renderer functions with signature - ``f(body, flatpages)``, where ``flatpages`` is an instance of - :class:`FlatPages`. - - """ - - def wrapper(page): - """Wrap HTML renderer function. - - Pass arguments to the renderer based on the number of arguments. - - * 1 argument -> page body - * 2 arguments -> page body, flatpages instance - * 3 arguments -> page body, flatpages instance, page instance - """ - body = page.body - - try: - args_length = len(getfullargspec(html_renderer).args) - except TypeError: - return html_renderer(body) - - if args_length == 1: - return html_renderer(body) - elif args_length == 2: - return html_renderer(body, self) - elif args_length == 3: - return html_renderer(body, self, page) - - raise ValueError( - "HTML renderer function {0!r} not supported by " - "Flask-FlatPages, wrong number of arguments: {1}.".format( - html_renderer, args_length - ) - ) - - return wrapper diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/imports.py b/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/imports.py deleted file mode 100644 index b2b628c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/imports.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Conditional imports.""" - -try: - from pygments.formatters import HtmlFormatter as PygmentsHtmlFormatter -except ImportError: - PygmentsHtmlFormatter = None diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/page.py b/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/page.py deleted file mode 100644 index 682b882..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/page.py +++ /dev/null @@ -1,80 +0,0 @@ -"""Define flatpage instance.""" -from io import StringIO - - -import yaml -from werkzeug.utils import cached_property - - -class Page(object): - """Simple class to store all necessary information about a flatpage. - - Main purpose is to render the page's content with a ``html_renderer`` - function. - """ - - def __init__(self, path, meta, body, html_renderer, folder): - """Initialize Page instance. - - :param path: Page path. - :param meta: Page meta data in YAML format. - :param body: Page body. - :param html_renderer: HTML renderer function. - :param folder: The folder the page is contained in. - """ - #: Path this page was obtained from, as in ``pages.get(path)`` - self.path = path - #: Content of the page - self._meta = meta - self.body = body - #: Renderer function - self.html_renderer = html_renderer - #: The name of the folder the page is contained in. - self.folder = folder - - def __getitem__(self, name): - """Shortcut for accessing metadata. - - ``page['title']`` or, in a template, ``{{ page.title }}`` are - equivalent to ``page.meta['title']``. - """ - return self.meta[name] - - def __html__(self): - """ - Return HTML for use in Jinja templates. - - In a template, ``{{ page }}`` is equivalent to - ``{{ page.html|safe }}``. - """ - return self.html - - def __repr__(self): - """Machine representation of :class:`Page` instance.""" - return "" % self.path - - @cached_property - def html(self): - """Content of the page, rendered as HTML by the configured renderer.""" - return self.html_renderer(self) - - @cached_property - def meta(self): - """Store a dict of metadata parsed from the YAML header of the file.""" - # meta = yaml.safe_load(self._meta) - meta = {} - for doc in yaml.safe_load_all(StringIO(self._meta)): - if doc is not None: - meta.update(doc) - # YAML documents can be any type but we want a dict - # eg. yaml.safe_load('') -> None - # yaml.safe_load('- 1\n- a') -> [1, 'a'] - if not meta: - return {} - if not isinstance(meta, dict): - raise ValueError( - "Expected a dict in metadata for '{0}', got {1}".format( - self.path, type(meta).__name__ - ) - ) - return meta diff --git a/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/utils.py b/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/utils.py deleted file mode 100644 index c954d22..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/flask_flatpages/utils.py +++ /dev/null @@ -1,101 +0,0 @@ -"""Utility functions to render Markdown text to HTML.""" - -import markdown -import six -from markdown.extensions import codehilite - -from .imports import PygmentsHtmlFormatter - -if six.PY3: - from io import StringIO -else: - from StringIO import StringIO - - -class NamedStringIO(StringIO, object): - """Subclass adding a Name to :class:`StringIO` objects.""" - - def __init__(self, content, name): - """ - Initialise the NamedStringIO. - - :param content: The string to be treated as a stream - :param name: The name to attach to the stream. Will - be consumed in e.g. ReaderErrors raised by pyyaml. - """ - if not six.PY3: - super(NamedStringIO, self).__init__(content) - else: - super().__init__(content) - self.name = name - - -def force_unicode(value, encoding="utf-8", errors="strict"): - """Convert bytes or any other Python instance to string.""" - if isinstance(value, six.text_type): - return value - return value.decode(encoding, errors) - - -def pygmented_markdown(text, flatpages=None): - """Render Markdown text to HTML. - - Uses the `CodeHilite`_ extension only if `Pygments`_ is available. If - `Pygments`_ is not available, "codehilite" is removed from list of - extensions. - - If you need other extensions, set them up using the - ``FLATPAGES_MARKDOWN_EXTENSIONS`` setting, which should be a sequence - of strings or Markdown Extension objects. - Extensions specified with entrypoint strings should be configured using - ``FLATPAGES_EXTENSION_CONFIGS``. - - .. _CodeHilite: - http://www.freewisdom.org/projects/python-markdown/CodeHilite - .. _Pygments: http://pygments.org/ - """ - if flatpages: - extensions = flatpages.config("markdown_extensions") - extension_configs = flatpages.config("extension_configs") - else: - extensions = [] - extension_configs = {} - if PygmentsHtmlFormatter is None: - original_extensions = extensions - original_config = extension_configs - extensions = [] - extension_configs = {} - - for extension in original_extensions: - if ( - isinstance(extension, six.string_types) - and "codehilite" in extension - ): - continue - elif isinstance(extension, codehilite.CodeHiliteExtension): - continue - extensions.append(extension) - if isinstance(extension, six.string_types): - if extension in original_config: - extension_configs[extension] = original_config[extension] - elif not extensions: - extensions = ["codehilite"] - return markdown.markdown( - text, extensions=extensions, extension_configs=extension_configs - ) - - -def pygments_style_defs(style="default"): - """:return: the CSS definitions for the `CodeHilite`_ Markdown plugin. - - :param style: The Pygments `style`_ to use. - - Only available if `Pygments`_ is. - - .. _CodeHilite: - http://www.freewisdom.org/projects/python-markdown/CodeHilite - .. _Pygments: http://pygments.org/ - .. _style: http://pygments.org/docs/styles/ - """ - formatter = PygmentsHtmlFormatter(style=style) - return formatter.get_style_defs(".codehilite") diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/__init__.py deleted file mode 100644 index 502ca5c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -import logging -from fontTools.misc.loggingTools import configLogger - -log = logging.getLogger(__name__) - -version = __version__ = "4.55.0" - -__all__ = ["version", "log", "configLogger"] diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__main__.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/__main__.py deleted file mode 100644 index 7c74ad3..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__main__.py +++ /dev/null @@ -1,35 +0,0 @@ -import sys - - -def main(args=None): - if args is None: - args = sys.argv[1:] - - # TODO Handle library-wide options. Eg.: - # --unicodedata - # --verbose / other logging stuff - - # TODO Allow a way to run arbitrary modules? Useful for setting - # library-wide options and calling another library. Eg.: - # - # $ fonttools --unicodedata=... fontmake ... - # - # This allows for a git-like command where thirdparty commands - # can be added. Should we just try importing the fonttools - # module first and try without if it fails? - - if len(sys.argv) < 2: - sys.argv.append("help") - if sys.argv[1] == "-h" or sys.argv[1] == "--help": - sys.argv[1] = "help" - mod = "fontTools." + sys.argv[1] - sys.argv[1] = sys.argv[0] + " " + sys.argv[1] - del sys.argv[0] - - import runpy - - runpy.run_module(mod, run_name="__main__") - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 8ea1042..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/__main__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/__main__.cpython-38.pyc deleted file mode 100644 index 3e24321..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/__main__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/afmLib.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/afmLib.cpython-38.pyc deleted file mode 100644 index d6c310e..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/afmLib.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/agl.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/agl.cpython-38.pyc deleted file mode 100644 index df22867..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/agl.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/fontBuilder.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/fontBuilder.cpython-38.pyc deleted file mode 100644 index 8297dcf..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/fontBuilder.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/help.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/help.cpython-38.pyc deleted file mode 100644 index 34f1e1f..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/help.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/tfmLib.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/tfmLib.cpython-38.pyc deleted file mode 100644 index d0c92ee..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/tfmLib.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/ttx.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/ttx.cpython-38.pyc deleted file mode 100644 index 3199020..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/ttx.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/unicode.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/unicode.cpython-38.pyc deleted file mode 100644 index 69dfadc..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/__pycache__/unicode.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/afmLib.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/afmLib.py deleted file mode 100644 index 0aabf7f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/afmLib.py +++ /dev/null @@ -1,439 +0,0 @@ -"""Module for reading and writing AFM (Adobe Font Metrics) files. - -Note that this has been designed to read in AFM files generated by Fontographer -and has not been tested on many other files. In particular, it does not -implement the whole Adobe AFM specification [#f1]_ but, it should read most -"common" AFM files. - -Here is an example of using `afmLib` to read, modify and write an AFM file: - - >>> from fontTools.afmLib import AFM - >>> f = AFM("Tests/afmLib/data/TestAFM.afm") - >>> - >>> # Accessing a pair gets you the kern value - >>> f[("V","A")] - -60 - >>> - >>> # Accessing a glyph name gets you metrics - >>> f["A"] - (65, 668, (8, -25, 660, 666)) - >>> # (charnum, width, bounding box) - >>> - >>> # Accessing an attribute gets you metadata - >>> f.FontName - 'TestFont-Regular' - >>> f.FamilyName - 'TestFont' - >>> f.Weight - 'Regular' - >>> f.XHeight - 500 - >>> f.Ascender - 750 - >>> - >>> # Attributes and items can also be set - >>> f[("A","V")] = -150 # Tighten kerning - >>> f.FontName = "TestFont Squished" - >>> - >>> # And the font written out again (remove the # in front) - >>> #f.write("testfont-squished.afm") - -.. rubric:: Footnotes - -.. [#f1] `Adobe Technote 5004 `_, - Adobe Font Metrics File Format Specification. - -""" - -import re - -# every single line starts with a "word" -identifierRE = re.compile(r"^([A-Za-z]+).*") - -# regular expression to parse char lines -charRE = re.compile( - r"(-?\d+)" # charnum - r"\s*;\s*WX\s+" # ; WX - r"(-?\d+)" # width - r"\s*;\s*N\s+" # ; N - r"([.A-Za-z0-9_]+)" # charname - r"\s*;\s*B\s+" # ; B - r"(-?\d+)" # left - r"\s+" - r"(-?\d+)" # bottom - r"\s+" - r"(-?\d+)" # right - r"\s+" - r"(-?\d+)" # top - r"\s*;\s*" # ; -) - -# regular expression to parse kerning lines -kernRE = re.compile( - r"([.A-Za-z0-9_]+)" # leftchar - r"\s+" - r"([.A-Za-z0-9_]+)" # rightchar - r"\s+" - r"(-?\d+)" # value - r"\s*" -) - -# regular expressions to parse composite info lines of the form: -# Aacute 2 ; PCC A 0 0 ; PCC acute 182 211 ; -compositeRE = re.compile( - r"([.A-Za-z0-9_]+)" # char name - r"\s+" - r"(\d+)" # number of parts - r"\s*;\s*" -) -componentRE = re.compile( - r"PCC\s+" # PPC - r"([.A-Za-z0-9_]+)" # base char name - r"\s+" - r"(-?\d+)" # x offset - r"\s+" - r"(-?\d+)" # y offset - r"\s*;\s*" -) - -preferredAttributeOrder = [ - "FontName", - "FullName", - "FamilyName", - "Weight", - "ItalicAngle", - "IsFixedPitch", - "FontBBox", - "UnderlinePosition", - "UnderlineThickness", - "Version", - "Notice", - "EncodingScheme", - "CapHeight", - "XHeight", - "Ascender", - "Descender", -] - - -class error(Exception): - pass - - -class AFM(object): - _attrs = None - - _keywords = [ - "StartFontMetrics", - "EndFontMetrics", - "StartCharMetrics", - "EndCharMetrics", - "StartKernData", - "StartKernPairs", - "EndKernPairs", - "EndKernData", - "StartComposites", - "EndComposites", - ] - - def __init__(self, path=None): - """AFM file reader. - - Instantiating an object with a path name will cause the file to be opened, - read, and parsed. Alternatively the path can be left unspecified, and a - file can be parsed later with the :meth:`read` method.""" - self._attrs = {} - self._chars = {} - self._kerning = {} - self._index = {} - self._comments = [] - self._composites = {} - if path is not None: - self.read(path) - - def read(self, path): - """Opens, reads and parses a file.""" - lines = readlines(path) - for line in lines: - if not line.strip(): - continue - m = identifierRE.match(line) - if m is None: - raise error("syntax error in AFM file: " + repr(line)) - - pos = m.regs[1][1] - word = line[:pos] - rest = line[pos:].strip() - if word in self._keywords: - continue - if word == "C": - self.parsechar(rest) - elif word == "KPX": - self.parsekernpair(rest) - elif word == "CC": - self.parsecomposite(rest) - else: - self.parseattr(word, rest) - - def parsechar(self, rest): - m = charRE.match(rest) - if m is None: - raise error("syntax error in AFM file: " + repr(rest)) - things = [] - for fr, to in m.regs[1:]: - things.append(rest[fr:to]) - charname = things[2] - del things[2] - charnum, width, l, b, r, t = (int(thing) for thing in things) - self._chars[charname] = charnum, width, (l, b, r, t) - - def parsekernpair(self, rest): - m = kernRE.match(rest) - if m is None: - raise error("syntax error in AFM file: " + repr(rest)) - things = [] - for fr, to in m.regs[1:]: - things.append(rest[fr:to]) - leftchar, rightchar, value = things - value = int(value) - self._kerning[(leftchar, rightchar)] = value - - def parseattr(self, word, rest): - if word == "FontBBox": - l, b, r, t = [int(thing) for thing in rest.split()] - self._attrs[word] = l, b, r, t - elif word == "Comment": - self._comments.append(rest) - else: - try: - value = int(rest) - except (ValueError, OverflowError): - self._attrs[word] = rest - else: - self._attrs[word] = value - - def parsecomposite(self, rest): - m = compositeRE.match(rest) - if m is None: - raise error("syntax error in AFM file: " + repr(rest)) - charname = m.group(1) - ncomponents = int(m.group(2)) - rest = rest[m.regs[0][1] :] - components = [] - while True: - m = componentRE.match(rest) - if m is None: - raise error("syntax error in AFM file: " + repr(rest)) - basechar = m.group(1) - xoffset = int(m.group(2)) - yoffset = int(m.group(3)) - components.append((basechar, xoffset, yoffset)) - rest = rest[m.regs[0][1] :] - if not rest: - break - assert len(components) == ncomponents - self._composites[charname] = components - - def write(self, path, sep="\r"): - """Writes out an AFM font to the given path.""" - import time - - lines = [ - "StartFontMetrics 2.0", - "Comment Generated by afmLib; at %s" - % (time.strftime("%m/%d/%Y %H:%M:%S", time.localtime(time.time()))), - ] - - # write comments, assuming (possibly wrongly!) they should - # all appear at the top - for comment in self._comments: - lines.append("Comment " + comment) - - # write attributes, first the ones we know about, in - # a preferred order - attrs = self._attrs - for attr in preferredAttributeOrder: - if attr in attrs: - value = attrs[attr] - if attr == "FontBBox": - value = "%s %s %s %s" % value - lines.append(attr + " " + str(value)) - # then write the attributes we don't know about, - # in alphabetical order - items = sorted(attrs.items()) - for attr, value in items: - if attr in preferredAttributeOrder: - continue - lines.append(attr + " " + str(value)) - - # write char metrics - lines.append("StartCharMetrics " + repr(len(self._chars))) - items = [ - (charnum, (charname, width, box)) - for charname, (charnum, width, box) in self._chars.items() - ] - - def myKey(a): - """Custom key function to make sure unencoded chars (-1) - end up at the end of the list after sorting.""" - if a[0] == -1: - a = (0xFFFF,) + a[1:] # 0xffff is an arbitrary large number - return a - - items.sort(key=myKey) - - for charnum, (charname, width, (l, b, r, t)) in items: - lines.append( - "C %d ; WX %d ; N %s ; B %d %d %d %d ;" - % (charnum, width, charname, l, b, r, t) - ) - lines.append("EndCharMetrics") - - # write kerning info - lines.append("StartKernData") - lines.append("StartKernPairs " + repr(len(self._kerning))) - items = sorted(self._kerning.items()) - for (leftchar, rightchar), value in items: - lines.append("KPX %s %s %d" % (leftchar, rightchar, value)) - lines.append("EndKernPairs") - lines.append("EndKernData") - - if self._composites: - composites = sorted(self._composites.items()) - lines.append("StartComposites %s" % len(self._composites)) - for charname, components in composites: - line = "CC %s %s ;" % (charname, len(components)) - for basechar, xoffset, yoffset in components: - line = line + " PCC %s %s %s ;" % (basechar, xoffset, yoffset) - lines.append(line) - lines.append("EndComposites") - - lines.append("EndFontMetrics") - - writelines(path, lines, sep) - - def has_kernpair(self, pair): - """Returns `True` if the given glyph pair (specified as a tuple) exists - in the kerning dictionary.""" - return pair in self._kerning - - def kernpairs(self): - """Returns a list of all kern pairs in the kerning dictionary.""" - return list(self._kerning.keys()) - - def has_char(self, char): - """Returns `True` if the given glyph exists in the font.""" - return char in self._chars - - def chars(self): - """Returns a list of all glyph names in the font.""" - return list(self._chars.keys()) - - def comments(self): - """Returns all comments from the file.""" - return self._comments - - def addComment(self, comment): - """Adds a new comment to the file.""" - self._comments.append(comment) - - def addComposite(self, glyphName, components): - """Specifies that the glyph `glyphName` is made up of the given components. - The components list should be of the following form:: - - [ - (glyphname, xOffset, yOffset), - ... - ] - - """ - self._composites[glyphName] = components - - def __getattr__(self, attr): - if attr in self._attrs: - return self._attrs[attr] - else: - raise AttributeError(attr) - - def __setattr__(self, attr, value): - # all attrs *not* starting with "_" are consider to be AFM keywords - if attr[:1] == "_": - self.__dict__[attr] = value - else: - self._attrs[attr] = value - - def __delattr__(self, attr): - # all attrs *not* starting with "_" are consider to be AFM keywords - if attr[:1] == "_": - try: - del self.__dict__[attr] - except KeyError: - raise AttributeError(attr) - else: - try: - del self._attrs[attr] - except KeyError: - raise AttributeError(attr) - - def __getitem__(self, key): - if isinstance(key, tuple): - # key is a tuple, return the kernpair - return self._kerning[key] - else: - # return the metrics instead - return self._chars[key] - - def __setitem__(self, key, value): - if isinstance(key, tuple): - # key is a tuple, set kernpair - self._kerning[key] = value - else: - # set char metrics - self._chars[key] = value - - def __delitem__(self, key): - if isinstance(key, tuple): - # key is a tuple, del kernpair - del self._kerning[key] - else: - # del char metrics - del self._chars[key] - - def __repr__(self): - if hasattr(self, "FullName"): - return "" % self.FullName - else: - return "" % id(self) - - -def readlines(path): - with open(path, "r", encoding="ascii") as f: - data = f.read() - return data.splitlines() - - -def writelines(path, lines, sep="\r"): - with open(path, "w", encoding="ascii", newline=sep) as f: - f.write("\n".join(lines) + "\n") - - -if __name__ == "__main__": - import EasyDialogs - - path = EasyDialogs.AskFileForOpen() - if path: - afm = AFM(path) - char = "A" - if afm.has_char(char): - print(afm[char]) # print charnum, width and boundingbox - pair = ("A", "V") - if afm.has_kernpair(pair): - print(afm[pair]) # print kerning value for pair - print(afm.Version) # various other afm entries have become attributes - print(afm.Weight) - # afm.comments() returns a list of all Comment lines found in the AFM - print(afm.comments()) - # print afm.chars() - # print afm.kernpairs() - print(afm) - afm.write(path + ".muck") diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/agl.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/agl.py deleted file mode 100644 index d699462..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/agl.py +++ /dev/null @@ -1,5233 +0,0 @@ -# -*- coding: utf-8 -*- -# The tables below are taken from -# https://github.com/adobe-type-tools/agl-aglfn/raw/4036a9ca80a62f64f9de4f7321a9a045ad0ecfd6/glyphlist.txt -# and -# https://github.com/adobe-type-tools/agl-aglfn/raw/4036a9ca80a62f64f9de4f7321a9a045ad0ecfd6/aglfn.txt -""" -Interface to the Adobe Glyph List - -This module exists to convert glyph names from the Adobe Glyph List -to their Unicode equivalents. Example usage: - - >>> from fontTools.agl import toUnicode - >>> toUnicode("nahiragana") - 'ãª' - -It also contains two dictionaries, ``UV2AGL`` and ``AGL2UV``, which map from -Unicode codepoints to AGL names and vice versa: - - >>> import fontTools - >>> fontTools.agl.UV2AGL[ord("?")] - 'question' - >>> fontTools.agl.AGL2UV["wcircumflex"] - 373 - -This is used by fontTools when it has to construct glyph names for a font which -doesn't include any (e.g. format 3.0 post tables). -""" - -from fontTools.misc.textTools import tostr -import re - - -_aglText = """\ -# ----------------------------------------------------------- -# Copyright 2002-2019 Adobe (http://www.adobe.com/). -# -# 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 Adobe nor the names of its contributors -# may be used to endorse or promote products derived from this -# software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND -# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# ----------------------------------------------------------- -# Name: Adobe Glyph List -# Table version: 2.0 -# Date: September 20, 2002 -# URL: https://github.com/adobe-type-tools/agl-aglfn -# -# Format: two semicolon-delimited fields: -# (1) glyph name--upper/lowercase letters and digits -# (2) Unicode scalar value--four uppercase hexadecimal digits -# -A;0041 -AE;00C6 -AEacute;01FC -AEmacron;01E2 -AEsmall;F7E6 -Aacute;00C1 -Aacutesmall;F7E1 -Abreve;0102 -Abreveacute;1EAE -Abrevecyrillic;04D0 -Abrevedotbelow;1EB6 -Abrevegrave;1EB0 -Abrevehookabove;1EB2 -Abrevetilde;1EB4 -Acaron;01CD -Acircle;24B6 -Acircumflex;00C2 -Acircumflexacute;1EA4 -Acircumflexdotbelow;1EAC -Acircumflexgrave;1EA6 -Acircumflexhookabove;1EA8 -Acircumflexsmall;F7E2 -Acircumflextilde;1EAA -Acute;F6C9 -Acutesmall;F7B4 -Acyrillic;0410 -Adblgrave;0200 -Adieresis;00C4 -Adieresiscyrillic;04D2 -Adieresismacron;01DE -Adieresissmall;F7E4 -Adotbelow;1EA0 -Adotmacron;01E0 -Agrave;00C0 -Agravesmall;F7E0 -Ahookabove;1EA2 -Aiecyrillic;04D4 -Ainvertedbreve;0202 -Alpha;0391 -Alphatonos;0386 -Amacron;0100 -Amonospace;FF21 -Aogonek;0104 -Aring;00C5 -Aringacute;01FA -Aringbelow;1E00 -Aringsmall;F7E5 -Asmall;F761 -Atilde;00C3 -Atildesmall;F7E3 -Aybarmenian;0531 -B;0042 -Bcircle;24B7 -Bdotaccent;1E02 -Bdotbelow;1E04 -Becyrillic;0411 -Benarmenian;0532 -Beta;0392 -Bhook;0181 -Blinebelow;1E06 -Bmonospace;FF22 -Brevesmall;F6F4 -Bsmall;F762 -Btopbar;0182 -C;0043 -Caarmenian;053E -Cacute;0106 -Caron;F6CA -Caronsmall;F6F5 -Ccaron;010C -Ccedilla;00C7 -Ccedillaacute;1E08 -Ccedillasmall;F7E7 -Ccircle;24B8 -Ccircumflex;0108 -Cdot;010A -Cdotaccent;010A -Cedillasmall;F7B8 -Chaarmenian;0549 -Cheabkhasiancyrillic;04BC -Checyrillic;0427 -Chedescenderabkhasiancyrillic;04BE -Chedescendercyrillic;04B6 -Chedieresiscyrillic;04F4 -Cheharmenian;0543 -Chekhakassiancyrillic;04CB -Cheverticalstrokecyrillic;04B8 -Chi;03A7 -Chook;0187 -Circumflexsmall;F6F6 -Cmonospace;FF23 -Coarmenian;0551 -Csmall;F763 -D;0044 -DZ;01F1 -DZcaron;01C4 -Daarmenian;0534 -Dafrican;0189 -Dcaron;010E -Dcedilla;1E10 -Dcircle;24B9 -Dcircumflexbelow;1E12 -Dcroat;0110 -Ddotaccent;1E0A -Ddotbelow;1E0C -Decyrillic;0414 -Deicoptic;03EE -Delta;2206 -Deltagreek;0394 -Dhook;018A -Dieresis;F6CB -DieresisAcute;F6CC -DieresisGrave;F6CD -Dieresissmall;F7A8 -Digammagreek;03DC -Djecyrillic;0402 -Dlinebelow;1E0E -Dmonospace;FF24 -Dotaccentsmall;F6F7 -Dslash;0110 -Dsmall;F764 -Dtopbar;018B -Dz;01F2 -Dzcaron;01C5 -Dzeabkhasiancyrillic;04E0 -Dzecyrillic;0405 -Dzhecyrillic;040F -E;0045 -Eacute;00C9 -Eacutesmall;F7E9 -Ebreve;0114 -Ecaron;011A -Ecedillabreve;1E1C -Echarmenian;0535 -Ecircle;24BA -Ecircumflex;00CA -Ecircumflexacute;1EBE -Ecircumflexbelow;1E18 -Ecircumflexdotbelow;1EC6 -Ecircumflexgrave;1EC0 -Ecircumflexhookabove;1EC2 -Ecircumflexsmall;F7EA -Ecircumflextilde;1EC4 -Ecyrillic;0404 -Edblgrave;0204 -Edieresis;00CB -Edieresissmall;F7EB -Edot;0116 -Edotaccent;0116 -Edotbelow;1EB8 -Efcyrillic;0424 -Egrave;00C8 -Egravesmall;F7E8 -Eharmenian;0537 -Ehookabove;1EBA -Eightroman;2167 -Einvertedbreve;0206 -Eiotifiedcyrillic;0464 -Elcyrillic;041B -Elevenroman;216A -Emacron;0112 -Emacronacute;1E16 -Emacrongrave;1E14 -Emcyrillic;041C -Emonospace;FF25 -Encyrillic;041D -Endescendercyrillic;04A2 -Eng;014A -Enghecyrillic;04A4 -Enhookcyrillic;04C7 -Eogonek;0118 -Eopen;0190 -Epsilon;0395 -Epsilontonos;0388 -Ercyrillic;0420 -Ereversed;018E -Ereversedcyrillic;042D -Escyrillic;0421 -Esdescendercyrillic;04AA -Esh;01A9 -Esmall;F765 -Eta;0397 -Etarmenian;0538 -Etatonos;0389 -Eth;00D0 -Ethsmall;F7F0 -Etilde;1EBC -Etildebelow;1E1A -Euro;20AC -Ezh;01B7 -Ezhcaron;01EE -Ezhreversed;01B8 -F;0046 -Fcircle;24BB -Fdotaccent;1E1E -Feharmenian;0556 -Feicoptic;03E4 -Fhook;0191 -Fitacyrillic;0472 -Fiveroman;2164 -Fmonospace;FF26 -Fourroman;2163 -Fsmall;F766 -G;0047 -GBsquare;3387 -Gacute;01F4 -Gamma;0393 -Gammaafrican;0194 -Gangiacoptic;03EA -Gbreve;011E -Gcaron;01E6 -Gcedilla;0122 -Gcircle;24BC -Gcircumflex;011C -Gcommaaccent;0122 -Gdot;0120 -Gdotaccent;0120 -Gecyrillic;0413 -Ghadarmenian;0542 -Ghemiddlehookcyrillic;0494 -Ghestrokecyrillic;0492 -Gheupturncyrillic;0490 -Ghook;0193 -Gimarmenian;0533 -Gjecyrillic;0403 -Gmacron;1E20 -Gmonospace;FF27 -Grave;F6CE -Gravesmall;F760 -Gsmall;F767 -Gsmallhook;029B -Gstroke;01E4 -H;0048 -H18533;25CF -H18543;25AA -H18551;25AB -H22073;25A1 -HPsquare;33CB -Haabkhasiancyrillic;04A8 -Hadescendercyrillic;04B2 -Hardsigncyrillic;042A -Hbar;0126 -Hbrevebelow;1E2A -Hcedilla;1E28 -Hcircle;24BD -Hcircumflex;0124 -Hdieresis;1E26 -Hdotaccent;1E22 -Hdotbelow;1E24 -Hmonospace;FF28 -Hoarmenian;0540 -Horicoptic;03E8 -Hsmall;F768 -Hungarumlaut;F6CF -Hungarumlautsmall;F6F8 -Hzsquare;3390 -I;0049 -IAcyrillic;042F -IJ;0132 -IUcyrillic;042E -Iacute;00CD -Iacutesmall;F7ED -Ibreve;012C -Icaron;01CF -Icircle;24BE -Icircumflex;00CE -Icircumflexsmall;F7EE -Icyrillic;0406 -Idblgrave;0208 -Idieresis;00CF -Idieresisacute;1E2E -Idieresiscyrillic;04E4 -Idieresissmall;F7EF -Idot;0130 -Idotaccent;0130 -Idotbelow;1ECA -Iebrevecyrillic;04D6 -Iecyrillic;0415 -Ifraktur;2111 -Igrave;00CC -Igravesmall;F7EC -Ihookabove;1EC8 -Iicyrillic;0418 -Iinvertedbreve;020A -Iishortcyrillic;0419 -Imacron;012A -Imacroncyrillic;04E2 -Imonospace;FF29 -Iniarmenian;053B -Iocyrillic;0401 -Iogonek;012E -Iota;0399 -Iotaafrican;0196 -Iotadieresis;03AA -Iotatonos;038A -Ismall;F769 -Istroke;0197 -Itilde;0128 -Itildebelow;1E2C -Izhitsacyrillic;0474 -Izhitsadblgravecyrillic;0476 -J;004A -Jaarmenian;0541 -Jcircle;24BF -Jcircumflex;0134 -Jecyrillic;0408 -Jheharmenian;054B -Jmonospace;FF2A -Jsmall;F76A -K;004B -KBsquare;3385 -KKsquare;33CD -Kabashkircyrillic;04A0 -Kacute;1E30 -Kacyrillic;041A -Kadescendercyrillic;049A -Kahookcyrillic;04C3 -Kappa;039A -Kastrokecyrillic;049E -Kaverticalstrokecyrillic;049C -Kcaron;01E8 -Kcedilla;0136 -Kcircle;24C0 -Kcommaaccent;0136 -Kdotbelow;1E32 -Keharmenian;0554 -Kenarmenian;053F -Khacyrillic;0425 -Kheicoptic;03E6 -Khook;0198 -Kjecyrillic;040C -Klinebelow;1E34 -Kmonospace;FF2B -Koppacyrillic;0480 -Koppagreek;03DE -Ksicyrillic;046E -Ksmall;F76B -L;004C -LJ;01C7 -LL;F6BF -Lacute;0139 -Lambda;039B -Lcaron;013D -Lcedilla;013B -Lcircle;24C1 -Lcircumflexbelow;1E3C -Lcommaaccent;013B -Ldot;013F -Ldotaccent;013F -Ldotbelow;1E36 -Ldotbelowmacron;1E38 -Liwnarmenian;053C -Lj;01C8 -Ljecyrillic;0409 -Llinebelow;1E3A -Lmonospace;FF2C -Lslash;0141 -Lslashsmall;F6F9 -Lsmall;F76C -M;004D -MBsquare;3386 -Macron;F6D0 -Macronsmall;F7AF -Macute;1E3E -Mcircle;24C2 -Mdotaccent;1E40 -Mdotbelow;1E42 -Menarmenian;0544 -Mmonospace;FF2D -Msmall;F76D -Mturned;019C -Mu;039C -N;004E -NJ;01CA -Nacute;0143 -Ncaron;0147 -Ncedilla;0145 -Ncircle;24C3 -Ncircumflexbelow;1E4A -Ncommaaccent;0145 -Ndotaccent;1E44 -Ndotbelow;1E46 -Nhookleft;019D -Nineroman;2168 -Nj;01CB -Njecyrillic;040A -Nlinebelow;1E48 -Nmonospace;FF2E -Nowarmenian;0546 -Nsmall;F76E -Ntilde;00D1 -Ntildesmall;F7F1 -Nu;039D -O;004F -OE;0152 -OEsmall;F6FA -Oacute;00D3 -Oacutesmall;F7F3 -Obarredcyrillic;04E8 -Obarreddieresiscyrillic;04EA -Obreve;014E -Ocaron;01D1 -Ocenteredtilde;019F -Ocircle;24C4 -Ocircumflex;00D4 -Ocircumflexacute;1ED0 -Ocircumflexdotbelow;1ED8 -Ocircumflexgrave;1ED2 -Ocircumflexhookabove;1ED4 -Ocircumflexsmall;F7F4 -Ocircumflextilde;1ED6 -Ocyrillic;041E -Odblacute;0150 -Odblgrave;020C -Odieresis;00D6 -Odieresiscyrillic;04E6 -Odieresissmall;F7F6 -Odotbelow;1ECC -Ogoneksmall;F6FB -Ograve;00D2 -Ogravesmall;F7F2 -Oharmenian;0555 -Ohm;2126 -Ohookabove;1ECE -Ohorn;01A0 -Ohornacute;1EDA -Ohorndotbelow;1EE2 -Ohorngrave;1EDC -Ohornhookabove;1EDE -Ohorntilde;1EE0 -Ohungarumlaut;0150 -Oi;01A2 -Oinvertedbreve;020E -Omacron;014C -Omacronacute;1E52 -Omacrongrave;1E50 -Omega;2126 -Omegacyrillic;0460 -Omegagreek;03A9 -Omegaroundcyrillic;047A -Omegatitlocyrillic;047C -Omegatonos;038F -Omicron;039F -Omicrontonos;038C -Omonospace;FF2F -Oneroman;2160 -Oogonek;01EA -Oogonekmacron;01EC -Oopen;0186 -Oslash;00D8 -Oslashacute;01FE -Oslashsmall;F7F8 -Osmall;F76F -Ostrokeacute;01FE -Otcyrillic;047E -Otilde;00D5 -Otildeacute;1E4C -Otildedieresis;1E4E -Otildesmall;F7F5 -P;0050 -Pacute;1E54 -Pcircle;24C5 -Pdotaccent;1E56 -Pecyrillic;041F -Peharmenian;054A -Pemiddlehookcyrillic;04A6 -Phi;03A6 -Phook;01A4 -Pi;03A0 -Piwrarmenian;0553 -Pmonospace;FF30 -Psi;03A8 -Psicyrillic;0470 -Psmall;F770 -Q;0051 -Qcircle;24C6 -Qmonospace;FF31 -Qsmall;F771 -R;0052 -Raarmenian;054C -Racute;0154 -Rcaron;0158 -Rcedilla;0156 -Rcircle;24C7 -Rcommaaccent;0156 -Rdblgrave;0210 -Rdotaccent;1E58 -Rdotbelow;1E5A -Rdotbelowmacron;1E5C -Reharmenian;0550 -Rfraktur;211C -Rho;03A1 -Ringsmall;F6FC -Rinvertedbreve;0212 -Rlinebelow;1E5E -Rmonospace;FF32 -Rsmall;F772 -Rsmallinverted;0281 -Rsmallinvertedsuperior;02B6 -S;0053 -SF010000;250C -SF020000;2514 -SF030000;2510 -SF040000;2518 -SF050000;253C -SF060000;252C -SF070000;2534 -SF080000;251C -SF090000;2524 -SF100000;2500 -SF110000;2502 -SF190000;2561 -SF200000;2562 -SF210000;2556 -SF220000;2555 -SF230000;2563 -SF240000;2551 -SF250000;2557 -SF260000;255D -SF270000;255C -SF280000;255B -SF360000;255E -SF370000;255F -SF380000;255A -SF390000;2554 -SF400000;2569 -SF410000;2566 -SF420000;2560 -SF430000;2550 -SF440000;256C -SF450000;2567 -SF460000;2568 -SF470000;2564 -SF480000;2565 -SF490000;2559 -SF500000;2558 -SF510000;2552 -SF520000;2553 -SF530000;256B -SF540000;256A -Sacute;015A -Sacutedotaccent;1E64 -Sampigreek;03E0 -Scaron;0160 -Scarondotaccent;1E66 -Scaronsmall;F6FD -Scedilla;015E -Schwa;018F -Schwacyrillic;04D8 -Schwadieresiscyrillic;04DA -Scircle;24C8 -Scircumflex;015C -Scommaaccent;0218 -Sdotaccent;1E60 -Sdotbelow;1E62 -Sdotbelowdotaccent;1E68 -Seharmenian;054D -Sevenroman;2166 -Shaarmenian;0547 -Shacyrillic;0428 -Shchacyrillic;0429 -Sheicoptic;03E2 -Shhacyrillic;04BA -Shimacoptic;03EC -Sigma;03A3 -Sixroman;2165 -Smonospace;FF33 -Softsigncyrillic;042C -Ssmall;F773 -Stigmagreek;03DA -T;0054 -Tau;03A4 -Tbar;0166 -Tcaron;0164 -Tcedilla;0162 -Tcircle;24C9 -Tcircumflexbelow;1E70 -Tcommaaccent;0162 -Tdotaccent;1E6A -Tdotbelow;1E6C -Tecyrillic;0422 -Tedescendercyrillic;04AC -Tenroman;2169 -Tetsecyrillic;04B4 -Theta;0398 -Thook;01AC -Thorn;00DE -Thornsmall;F7FE -Threeroman;2162 -Tildesmall;F6FE -Tiwnarmenian;054F -Tlinebelow;1E6E -Tmonospace;FF34 -Toarmenian;0539 -Tonefive;01BC -Tonesix;0184 -Tonetwo;01A7 -Tretroflexhook;01AE -Tsecyrillic;0426 -Tshecyrillic;040B -Tsmall;F774 -Twelveroman;216B -Tworoman;2161 -U;0055 -Uacute;00DA -Uacutesmall;F7FA -Ubreve;016C -Ucaron;01D3 -Ucircle;24CA -Ucircumflex;00DB -Ucircumflexbelow;1E76 -Ucircumflexsmall;F7FB -Ucyrillic;0423 -Udblacute;0170 -Udblgrave;0214 -Udieresis;00DC -Udieresisacute;01D7 -Udieresisbelow;1E72 -Udieresiscaron;01D9 -Udieresiscyrillic;04F0 -Udieresisgrave;01DB -Udieresismacron;01D5 -Udieresissmall;F7FC -Udotbelow;1EE4 -Ugrave;00D9 -Ugravesmall;F7F9 -Uhookabove;1EE6 -Uhorn;01AF -Uhornacute;1EE8 -Uhorndotbelow;1EF0 -Uhorngrave;1EEA -Uhornhookabove;1EEC -Uhorntilde;1EEE -Uhungarumlaut;0170 -Uhungarumlautcyrillic;04F2 -Uinvertedbreve;0216 -Ukcyrillic;0478 -Umacron;016A -Umacroncyrillic;04EE -Umacrondieresis;1E7A -Umonospace;FF35 -Uogonek;0172 -Upsilon;03A5 -Upsilon1;03D2 -Upsilonacutehooksymbolgreek;03D3 -Upsilonafrican;01B1 -Upsilondieresis;03AB -Upsilondieresishooksymbolgreek;03D4 -Upsilonhooksymbol;03D2 -Upsilontonos;038E -Uring;016E -Ushortcyrillic;040E -Usmall;F775 -Ustraightcyrillic;04AE -Ustraightstrokecyrillic;04B0 -Utilde;0168 -Utildeacute;1E78 -Utildebelow;1E74 -V;0056 -Vcircle;24CB -Vdotbelow;1E7E -Vecyrillic;0412 -Vewarmenian;054E -Vhook;01B2 -Vmonospace;FF36 -Voarmenian;0548 -Vsmall;F776 -Vtilde;1E7C -W;0057 -Wacute;1E82 -Wcircle;24CC -Wcircumflex;0174 -Wdieresis;1E84 -Wdotaccent;1E86 -Wdotbelow;1E88 -Wgrave;1E80 -Wmonospace;FF37 -Wsmall;F777 -X;0058 -Xcircle;24CD -Xdieresis;1E8C -Xdotaccent;1E8A -Xeharmenian;053D -Xi;039E -Xmonospace;FF38 -Xsmall;F778 -Y;0059 -Yacute;00DD -Yacutesmall;F7FD -Yatcyrillic;0462 -Ycircle;24CE -Ycircumflex;0176 -Ydieresis;0178 -Ydieresissmall;F7FF -Ydotaccent;1E8E -Ydotbelow;1EF4 -Yericyrillic;042B -Yerudieresiscyrillic;04F8 -Ygrave;1EF2 -Yhook;01B3 -Yhookabove;1EF6 -Yiarmenian;0545 -Yicyrillic;0407 -Yiwnarmenian;0552 -Ymonospace;FF39 -Ysmall;F779 -Ytilde;1EF8 -Yusbigcyrillic;046A -Yusbigiotifiedcyrillic;046C -Yuslittlecyrillic;0466 -Yuslittleiotifiedcyrillic;0468 -Z;005A -Zaarmenian;0536 -Zacute;0179 -Zcaron;017D -Zcaronsmall;F6FF -Zcircle;24CF -Zcircumflex;1E90 -Zdot;017B -Zdotaccent;017B -Zdotbelow;1E92 -Zecyrillic;0417 -Zedescendercyrillic;0498 -Zedieresiscyrillic;04DE -Zeta;0396 -Zhearmenian;053A -Zhebrevecyrillic;04C1 -Zhecyrillic;0416 -Zhedescendercyrillic;0496 -Zhedieresiscyrillic;04DC -Zlinebelow;1E94 -Zmonospace;FF3A -Zsmall;F77A -Zstroke;01B5 -a;0061 -aabengali;0986 -aacute;00E1 -aadeva;0906 -aagujarati;0A86 -aagurmukhi;0A06 -aamatragurmukhi;0A3E -aarusquare;3303 -aavowelsignbengali;09BE -aavowelsigndeva;093E -aavowelsigngujarati;0ABE -abbreviationmarkarmenian;055F -abbreviationsigndeva;0970 -abengali;0985 -abopomofo;311A -abreve;0103 -abreveacute;1EAF -abrevecyrillic;04D1 -abrevedotbelow;1EB7 -abrevegrave;1EB1 -abrevehookabove;1EB3 -abrevetilde;1EB5 -acaron;01CE -acircle;24D0 -acircumflex;00E2 -acircumflexacute;1EA5 -acircumflexdotbelow;1EAD -acircumflexgrave;1EA7 -acircumflexhookabove;1EA9 -acircumflextilde;1EAB -acute;00B4 -acutebelowcmb;0317 -acutecmb;0301 -acutecomb;0301 -acutedeva;0954 -acutelowmod;02CF -acutetonecmb;0341 -acyrillic;0430 -adblgrave;0201 -addakgurmukhi;0A71 -adeva;0905 -adieresis;00E4 -adieresiscyrillic;04D3 -adieresismacron;01DF -adotbelow;1EA1 -adotmacron;01E1 -ae;00E6 -aeacute;01FD -aekorean;3150 -aemacron;01E3 -afii00208;2015 -afii08941;20A4 -afii10017;0410 -afii10018;0411 -afii10019;0412 -afii10020;0413 -afii10021;0414 -afii10022;0415 -afii10023;0401 -afii10024;0416 -afii10025;0417 -afii10026;0418 -afii10027;0419 -afii10028;041A -afii10029;041B -afii10030;041C -afii10031;041D -afii10032;041E -afii10033;041F -afii10034;0420 -afii10035;0421 -afii10036;0422 -afii10037;0423 -afii10038;0424 -afii10039;0425 -afii10040;0426 -afii10041;0427 -afii10042;0428 -afii10043;0429 -afii10044;042A -afii10045;042B -afii10046;042C -afii10047;042D -afii10048;042E -afii10049;042F -afii10050;0490 -afii10051;0402 -afii10052;0403 -afii10053;0404 -afii10054;0405 -afii10055;0406 -afii10056;0407 -afii10057;0408 -afii10058;0409 -afii10059;040A -afii10060;040B -afii10061;040C -afii10062;040E -afii10063;F6C4 -afii10064;F6C5 -afii10065;0430 -afii10066;0431 -afii10067;0432 -afii10068;0433 -afii10069;0434 -afii10070;0435 -afii10071;0451 -afii10072;0436 -afii10073;0437 -afii10074;0438 -afii10075;0439 -afii10076;043A -afii10077;043B -afii10078;043C -afii10079;043D -afii10080;043E -afii10081;043F -afii10082;0440 -afii10083;0441 -afii10084;0442 -afii10085;0443 -afii10086;0444 -afii10087;0445 -afii10088;0446 -afii10089;0447 -afii10090;0448 -afii10091;0449 -afii10092;044A -afii10093;044B -afii10094;044C -afii10095;044D -afii10096;044E -afii10097;044F -afii10098;0491 -afii10099;0452 -afii10100;0453 -afii10101;0454 -afii10102;0455 -afii10103;0456 -afii10104;0457 -afii10105;0458 -afii10106;0459 -afii10107;045A -afii10108;045B -afii10109;045C -afii10110;045E -afii10145;040F -afii10146;0462 -afii10147;0472 -afii10148;0474 -afii10192;F6C6 -afii10193;045F -afii10194;0463 -afii10195;0473 -afii10196;0475 -afii10831;F6C7 -afii10832;F6C8 -afii10846;04D9 -afii299;200E -afii300;200F -afii301;200D -afii57381;066A -afii57388;060C -afii57392;0660 -afii57393;0661 -afii57394;0662 -afii57395;0663 -afii57396;0664 -afii57397;0665 -afii57398;0666 -afii57399;0667 -afii57400;0668 -afii57401;0669 -afii57403;061B -afii57407;061F -afii57409;0621 -afii57410;0622 -afii57411;0623 -afii57412;0624 -afii57413;0625 -afii57414;0626 -afii57415;0627 -afii57416;0628 -afii57417;0629 -afii57418;062A -afii57419;062B -afii57420;062C -afii57421;062D -afii57422;062E -afii57423;062F -afii57424;0630 -afii57425;0631 -afii57426;0632 -afii57427;0633 -afii57428;0634 -afii57429;0635 -afii57430;0636 -afii57431;0637 -afii57432;0638 -afii57433;0639 -afii57434;063A -afii57440;0640 -afii57441;0641 -afii57442;0642 -afii57443;0643 -afii57444;0644 -afii57445;0645 -afii57446;0646 -afii57448;0648 -afii57449;0649 -afii57450;064A -afii57451;064B -afii57452;064C -afii57453;064D -afii57454;064E -afii57455;064F -afii57456;0650 -afii57457;0651 -afii57458;0652 -afii57470;0647 -afii57505;06A4 -afii57506;067E -afii57507;0686 -afii57508;0698 -afii57509;06AF -afii57511;0679 -afii57512;0688 -afii57513;0691 -afii57514;06BA -afii57519;06D2 -afii57534;06D5 -afii57636;20AA -afii57645;05BE -afii57658;05C3 -afii57664;05D0 -afii57665;05D1 -afii57666;05D2 -afii57667;05D3 -afii57668;05D4 -afii57669;05D5 -afii57670;05D6 -afii57671;05D7 -afii57672;05D8 -afii57673;05D9 -afii57674;05DA -afii57675;05DB -afii57676;05DC -afii57677;05DD -afii57678;05DE -afii57679;05DF -afii57680;05E0 -afii57681;05E1 -afii57682;05E2 -afii57683;05E3 -afii57684;05E4 -afii57685;05E5 -afii57686;05E6 -afii57687;05E7 -afii57688;05E8 -afii57689;05E9 -afii57690;05EA -afii57694;FB2A -afii57695;FB2B -afii57700;FB4B -afii57705;FB1F -afii57716;05F0 -afii57717;05F1 -afii57718;05F2 -afii57723;FB35 -afii57793;05B4 -afii57794;05B5 -afii57795;05B6 -afii57796;05BB -afii57797;05B8 -afii57798;05B7 -afii57799;05B0 -afii57800;05B2 -afii57801;05B1 -afii57802;05B3 -afii57803;05C2 -afii57804;05C1 -afii57806;05B9 -afii57807;05BC -afii57839;05BD -afii57841;05BF -afii57842;05C0 -afii57929;02BC -afii61248;2105 -afii61289;2113 -afii61352;2116 -afii61573;202C -afii61574;202D -afii61575;202E -afii61664;200C -afii63167;066D -afii64937;02BD -agrave;00E0 -agujarati;0A85 -agurmukhi;0A05 -ahiragana;3042 -ahookabove;1EA3 -aibengali;0990 -aibopomofo;311E -aideva;0910 -aiecyrillic;04D5 -aigujarati;0A90 -aigurmukhi;0A10 -aimatragurmukhi;0A48 -ainarabic;0639 -ainfinalarabic;FECA -aininitialarabic;FECB -ainmedialarabic;FECC -ainvertedbreve;0203 -aivowelsignbengali;09C8 -aivowelsigndeva;0948 -aivowelsigngujarati;0AC8 -akatakana;30A2 -akatakanahalfwidth;FF71 -akorean;314F -alef;05D0 -alefarabic;0627 -alefdageshhebrew;FB30 -aleffinalarabic;FE8E -alefhamzaabovearabic;0623 -alefhamzaabovefinalarabic;FE84 -alefhamzabelowarabic;0625 -alefhamzabelowfinalarabic;FE88 -alefhebrew;05D0 -aleflamedhebrew;FB4F -alefmaddaabovearabic;0622 -alefmaddaabovefinalarabic;FE82 -alefmaksuraarabic;0649 -alefmaksurafinalarabic;FEF0 -alefmaksurainitialarabic;FEF3 -alefmaksuramedialarabic;FEF4 -alefpatahhebrew;FB2E -alefqamatshebrew;FB2F -aleph;2135 -allequal;224C -alpha;03B1 -alphatonos;03AC -amacron;0101 -amonospace;FF41 -ampersand;0026 -ampersandmonospace;FF06 -ampersandsmall;F726 -amsquare;33C2 -anbopomofo;3122 -angbopomofo;3124 -angkhankhuthai;0E5A -angle;2220 -anglebracketleft;3008 -anglebracketleftvertical;FE3F -anglebracketright;3009 -anglebracketrightvertical;FE40 -angleleft;2329 -angleright;232A -angstrom;212B -anoteleia;0387 -anudattadeva;0952 -anusvarabengali;0982 -anusvaradeva;0902 -anusvaragujarati;0A82 -aogonek;0105 -apaatosquare;3300 -aparen;249C -apostrophearmenian;055A -apostrophemod;02BC -apple;F8FF -approaches;2250 -approxequal;2248 -approxequalorimage;2252 -approximatelyequal;2245 -araeaekorean;318E -araeakorean;318D -arc;2312 -arighthalfring;1E9A -aring;00E5 -aringacute;01FB -aringbelow;1E01 -arrowboth;2194 -arrowdashdown;21E3 -arrowdashleft;21E0 -arrowdashright;21E2 -arrowdashup;21E1 -arrowdblboth;21D4 -arrowdbldown;21D3 -arrowdblleft;21D0 -arrowdblright;21D2 -arrowdblup;21D1 -arrowdown;2193 -arrowdownleft;2199 -arrowdownright;2198 -arrowdownwhite;21E9 -arrowheaddownmod;02C5 -arrowheadleftmod;02C2 -arrowheadrightmod;02C3 -arrowheadupmod;02C4 -arrowhorizex;F8E7 -arrowleft;2190 -arrowleftdbl;21D0 -arrowleftdblstroke;21CD -arrowleftoverright;21C6 -arrowleftwhite;21E6 -arrowright;2192 -arrowrightdblstroke;21CF -arrowrightheavy;279E -arrowrightoverleft;21C4 -arrowrightwhite;21E8 -arrowtableft;21E4 -arrowtabright;21E5 -arrowup;2191 -arrowupdn;2195 -arrowupdnbse;21A8 -arrowupdownbase;21A8 -arrowupleft;2196 -arrowupleftofdown;21C5 -arrowupright;2197 -arrowupwhite;21E7 -arrowvertex;F8E6 -asciicircum;005E -asciicircummonospace;FF3E -asciitilde;007E -asciitildemonospace;FF5E -ascript;0251 -ascriptturned;0252 -asmallhiragana;3041 -asmallkatakana;30A1 -asmallkatakanahalfwidth;FF67 -asterisk;002A -asteriskaltonearabic;066D -asteriskarabic;066D -asteriskmath;2217 -asteriskmonospace;FF0A -asterisksmall;FE61 -asterism;2042 -asuperior;F6E9 -asymptoticallyequal;2243 -at;0040 -atilde;00E3 -atmonospace;FF20 -atsmall;FE6B -aturned;0250 -aubengali;0994 -aubopomofo;3120 -audeva;0914 -augujarati;0A94 -augurmukhi;0A14 -aulengthmarkbengali;09D7 -aumatragurmukhi;0A4C -auvowelsignbengali;09CC -auvowelsigndeva;094C -auvowelsigngujarati;0ACC -avagrahadeva;093D -aybarmenian;0561 -ayin;05E2 -ayinaltonehebrew;FB20 -ayinhebrew;05E2 -b;0062 -babengali;09AC -backslash;005C -backslashmonospace;FF3C -badeva;092C -bagujarati;0AAC -bagurmukhi;0A2C -bahiragana;3070 -bahtthai;0E3F -bakatakana;30D0 -bar;007C -barmonospace;FF5C -bbopomofo;3105 -bcircle;24D1 -bdotaccent;1E03 -bdotbelow;1E05 -beamedsixteenthnotes;266C -because;2235 -becyrillic;0431 -beharabic;0628 -behfinalarabic;FE90 -behinitialarabic;FE91 -behiragana;3079 -behmedialarabic;FE92 -behmeeminitialarabic;FC9F -behmeemisolatedarabic;FC08 -behnoonfinalarabic;FC6D -bekatakana;30D9 -benarmenian;0562 -bet;05D1 -beta;03B2 -betasymbolgreek;03D0 -betdagesh;FB31 -betdageshhebrew;FB31 -bethebrew;05D1 -betrafehebrew;FB4C -bhabengali;09AD -bhadeva;092D -bhagujarati;0AAD -bhagurmukhi;0A2D -bhook;0253 -bihiragana;3073 -bikatakana;30D3 -bilabialclick;0298 -bindigurmukhi;0A02 -birusquare;3331 -blackcircle;25CF -blackdiamond;25C6 -blackdownpointingtriangle;25BC -blackleftpointingpointer;25C4 -blackleftpointingtriangle;25C0 -blacklenticularbracketleft;3010 -blacklenticularbracketleftvertical;FE3B -blacklenticularbracketright;3011 -blacklenticularbracketrightvertical;FE3C -blacklowerlefttriangle;25E3 -blacklowerrighttriangle;25E2 -blackrectangle;25AC -blackrightpointingpointer;25BA -blackrightpointingtriangle;25B6 -blacksmallsquare;25AA -blacksmilingface;263B -blacksquare;25A0 -blackstar;2605 -blackupperlefttriangle;25E4 -blackupperrighttriangle;25E5 -blackuppointingsmalltriangle;25B4 -blackuppointingtriangle;25B2 -blank;2423 -blinebelow;1E07 -block;2588 -bmonospace;FF42 -bobaimaithai;0E1A -bohiragana;307C -bokatakana;30DC -bparen;249D -bqsquare;33C3 -braceex;F8F4 -braceleft;007B -braceleftbt;F8F3 -braceleftmid;F8F2 -braceleftmonospace;FF5B -braceleftsmall;FE5B -bracelefttp;F8F1 -braceleftvertical;FE37 -braceright;007D -bracerightbt;F8FE -bracerightmid;F8FD -bracerightmonospace;FF5D -bracerightsmall;FE5C -bracerighttp;F8FC -bracerightvertical;FE38 -bracketleft;005B -bracketleftbt;F8F0 -bracketleftex;F8EF -bracketleftmonospace;FF3B -bracketlefttp;F8EE -bracketright;005D -bracketrightbt;F8FB -bracketrightex;F8FA -bracketrightmonospace;FF3D -bracketrighttp;F8F9 -breve;02D8 -brevebelowcmb;032E -brevecmb;0306 -breveinvertedbelowcmb;032F -breveinvertedcmb;0311 -breveinverteddoublecmb;0361 -bridgebelowcmb;032A -bridgeinvertedbelowcmb;033A -brokenbar;00A6 -bstroke;0180 -bsuperior;F6EA -btopbar;0183 -buhiragana;3076 -bukatakana;30D6 -bullet;2022 -bulletinverse;25D8 -bulletoperator;2219 -bullseye;25CE -c;0063 -caarmenian;056E -cabengali;099A -cacute;0107 -cadeva;091A -cagujarati;0A9A -cagurmukhi;0A1A -calsquare;3388 -candrabindubengali;0981 -candrabinducmb;0310 -candrabindudeva;0901 -candrabindugujarati;0A81 -capslock;21EA -careof;2105 -caron;02C7 -caronbelowcmb;032C -caroncmb;030C -carriagereturn;21B5 -cbopomofo;3118 -ccaron;010D -ccedilla;00E7 -ccedillaacute;1E09 -ccircle;24D2 -ccircumflex;0109 -ccurl;0255 -cdot;010B -cdotaccent;010B -cdsquare;33C5 -cedilla;00B8 -cedillacmb;0327 -cent;00A2 -centigrade;2103 -centinferior;F6DF -centmonospace;FFE0 -centoldstyle;F7A2 -centsuperior;F6E0 -chaarmenian;0579 -chabengali;099B -chadeva;091B -chagujarati;0A9B -chagurmukhi;0A1B -chbopomofo;3114 -cheabkhasiancyrillic;04BD -checkmark;2713 -checyrillic;0447 -chedescenderabkhasiancyrillic;04BF -chedescendercyrillic;04B7 -chedieresiscyrillic;04F5 -cheharmenian;0573 -chekhakassiancyrillic;04CC -cheverticalstrokecyrillic;04B9 -chi;03C7 -chieuchacirclekorean;3277 -chieuchaparenkorean;3217 -chieuchcirclekorean;3269 -chieuchkorean;314A -chieuchparenkorean;3209 -chochangthai;0E0A -chochanthai;0E08 -chochingthai;0E09 -chochoethai;0E0C -chook;0188 -cieucacirclekorean;3276 -cieucaparenkorean;3216 -cieuccirclekorean;3268 -cieuckorean;3148 -cieucparenkorean;3208 -cieucuparenkorean;321C -circle;25CB -circlemultiply;2297 -circleot;2299 -circleplus;2295 -circlepostalmark;3036 -circlewithlefthalfblack;25D0 -circlewithrighthalfblack;25D1 -circumflex;02C6 -circumflexbelowcmb;032D -circumflexcmb;0302 -clear;2327 -clickalveolar;01C2 -clickdental;01C0 -clicklateral;01C1 -clickretroflex;01C3 -club;2663 -clubsuitblack;2663 -clubsuitwhite;2667 -cmcubedsquare;33A4 -cmonospace;FF43 -cmsquaredsquare;33A0 -coarmenian;0581 -colon;003A -colonmonetary;20A1 -colonmonospace;FF1A -colonsign;20A1 -colonsmall;FE55 -colontriangularhalfmod;02D1 -colontriangularmod;02D0 -comma;002C -commaabovecmb;0313 -commaaboverightcmb;0315 -commaaccent;F6C3 -commaarabic;060C -commaarmenian;055D -commainferior;F6E1 -commamonospace;FF0C -commareversedabovecmb;0314 -commareversedmod;02BD -commasmall;FE50 -commasuperior;F6E2 -commaturnedabovecmb;0312 -commaturnedmod;02BB -compass;263C -congruent;2245 -contourintegral;222E -control;2303 -controlACK;0006 -controlBEL;0007 -controlBS;0008 -controlCAN;0018 -controlCR;000D -controlDC1;0011 -controlDC2;0012 -controlDC3;0013 -controlDC4;0014 -controlDEL;007F -controlDLE;0010 -controlEM;0019 -controlENQ;0005 -controlEOT;0004 -controlESC;001B -controlETB;0017 -controlETX;0003 -controlFF;000C -controlFS;001C -controlGS;001D -controlHT;0009 -controlLF;000A -controlNAK;0015 -controlRS;001E -controlSI;000F -controlSO;000E -controlSOT;0002 -controlSTX;0001 -controlSUB;001A -controlSYN;0016 -controlUS;001F -controlVT;000B -copyright;00A9 -copyrightsans;F8E9 -copyrightserif;F6D9 -cornerbracketleft;300C -cornerbracketlefthalfwidth;FF62 -cornerbracketleftvertical;FE41 -cornerbracketright;300D -cornerbracketrighthalfwidth;FF63 -cornerbracketrightvertical;FE42 -corporationsquare;337F -cosquare;33C7 -coverkgsquare;33C6 -cparen;249E -cruzeiro;20A2 -cstretched;0297 -curlyand;22CF -curlyor;22CE -currency;00A4 -cyrBreve;F6D1 -cyrFlex;F6D2 -cyrbreve;F6D4 -cyrflex;F6D5 -d;0064 -daarmenian;0564 -dabengali;09A6 -dadarabic;0636 -dadeva;0926 -dadfinalarabic;FEBE -dadinitialarabic;FEBF -dadmedialarabic;FEC0 -dagesh;05BC -dageshhebrew;05BC -dagger;2020 -daggerdbl;2021 -dagujarati;0AA6 -dagurmukhi;0A26 -dahiragana;3060 -dakatakana;30C0 -dalarabic;062F -dalet;05D3 -daletdagesh;FB33 -daletdageshhebrew;FB33 -dalethatafpatah;05D3 05B2 -dalethatafpatahhebrew;05D3 05B2 -dalethatafsegol;05D3 05B1 -dalethatafsegolhebrew;05D3 05B1 -dalethebrew;05D3 -dalethiriq;05D3 05B4 -dalethiriqhebrew;05D3 05B4 -daletholam;05D3 05B9 -daletholamhebrew;05D3 05B9 -daletpatah;05D3 05B7 -daletpatahhebrew;05D3 05B7 -daletqamats;05D3 05B8 -daletqamatshebrew;05D3 05B8 -daletqubuts;05D3 05BB -daletqubutshebrew;05D3 05BB -daletsegol;05D3 05B6 -daletsegolhebrew;05D3 05B6 -daletsheva;05D3 05B0 -daletshevahebrew;05D3 05B0 -dalettsere;05D3 05B5 -dalettserehebrew;05D3 05B5 -dalfinalarabic;FEAA -dammaarabic;064F -dammalowarabic;064F -dammatanaltonearabic;064C -dammatanarabic;064C -danda;0964 -dargahebrew;05A7 -dargalefthebrew;05A7 -dasiapneumatacyrilliccmb;0485 -dblGrave;F6D3 -dblanglebracketleft;300A -dblanglebracketleftvertical;FE3D -dblanglebracketright;300B -dblanglebracketrightvertical;FE3E -dblarchinvertedbelowcmb;032B -dblarrowleft;21D4 -dblarrowright;21D2 -dbldanda;0965 -dblgrave;F6D6 -dblgravecmb;030F -dblintegral;222C -dbllowline;2017 -dbllowlinecmb;0333 -dbloverlinecmb;033F -dblprimemod;02BA -dblverticalbar;2016 -dblverticallineabovecmb;030E -dbopomofo;3109 -dbsquare;33C8 -dcaron;010F -dcedilla;1E11 -dcircle;24D3 -dcircumflexbelow;1E13 -dcroat;0111 -ddabengali;09A1 -ddadeva;0921 -ddagujarati;0AA1 -ddagurmukhi;0A21 -ddalarabic;0688 -ddalfinalarabic;FB89 -dddhadeva;095C -ddhabengali;09A2 -ddhadeva;0922 -ddhagujarati;0AA2 -ddhagurmukhi;0A22 -ddotaccent;1E0B -ddotbelow;1E0D -decimalseparatorarabic;066B -decimalseparatorpersian;066B -decyrillic;0434 -degree;00B0 -dehihebrew;05AD -dehiragana;3067 -deicoptic;03EF -dekatakana;30C7 -deleteleft;232B -deleteright;2326 -delta;03B4 -deltaturned;018D -denominatorminusonenumeratorbengali;09F8 -dezh;02A4 -dhabengali;09A7 -dhadeva;0927 -dhagujarati;0AA7 -dhagurmukhi;0A27 -dhook;0257 -dialytikatonos;0385 -dialytikatonoscmb;0344 -diamond;2666 -diamondsuitwhite;2662 -dieresis;00A8 -dieresisacute;F6D7 -dieresisbelowcmb;0324 -dieresiscmb;0308 -dieresisgrave;F6D8 -dieresistonos;0385 -dihiragana;3062 -dikatakana;30C2 -dittomark;3003 -divide;00F7 -divides;2223 -divisionslash;2215 -djecyrillic;0452 -dkshade;2593 -dlinebelow;1E0F -dlsquare;3397 -dmacron;0111 -dmonospace;FF44 -dnblock;2584 -dochadathai;0E0E -dodekthai;0E14 -dohiragana;3069 -dokatakana;30C9 -dollar;0024 -dollarinferior;F6E3 -dollarmonospace;FF04 -dollaroldstyle;F724 -dollarsmall;FE69 -dollarsuperior;F6E4 -dong;20AB -dorusquare;3326 -dotaccent;02D9 -dotaccentcmb;0307 -dotbelowcmb;0323 -dotbelowcomb;0323 -dotkatakana;30FB -dotlessi;0131 -dotlessj;F6BE -dotlessjstrokehook;0284 -dotmath;22C5 -dottedcircle;25CC -doubleyodpatah;FB1F -doubleyodpatahhebrew;FB1F -downtackbelowcmb;031E -downtackmod;02D5 -dparen;249F -dsuperior;F6EB -dtail;0256 -dtopbar;018C -duhiragana;3065 -dukatakana;30C5 -dz;01F3 -dzaltone;02A3 -dzcaron;01C6 -dzcurl;02A5 -dzeabkhasiancyrillic;04E1 -dzecyrillic;0455 -dzhecyrillic;045F -e;0065 -eacute;00E9 -earth;2641 -ebengali;098F -ebopomofo;311C -ebreve;0115 -ecandradeva;090D -ecandragujarati;0A8D -ecandravowelsigndeva;0945 -ecandravowelsigngujarati;0AC5 -ecaron;011B -ecedillabreve;1E1D -echarmenian;0565 -echyiwnarmenian;0587 -ecircle;24D4 -ecircumflex;00EA -ecircumflexacute;1EBF -ecircumflexbelow;1E19 -ecircumflexdotbelow;1EC7 -ecircumflexgrave;1EC1 -ecircumflexhookabove;1EC3 -ecircumflextilde;1EC5 -ecyrillic;0454 -edblgrave;0205 -edeva;090F -edieresis;00EB -edot;0117 -edotaccent;0117 -edotbelow;1EB9 -eegurmukhi;0A0F -eematragurmukhi;0A47 -efcyrillic;0444 -egrave;00E8 -egujarati;0A8F -eharmenian;0567 -ehbopomofo;311D -ehiragana;3048 -ehookabove;1EBB -eibopomofo;311F -eight;0038 -eightarabic;0668 -eightbengali;09EE -eightcircle;2467 -eightcircleinversesansserif;2791 -eightdeva;096E -eighteencircle;2471 -eighteenparen;2485 -eighteenperiod;2499 -eightgujarati;0AEE -eightgurmukhi;0A6E -eighthackarabic;0668 -eighthangzhou;3028 -eighthnotebeamed;266B -eightideographicparen;3227 -eightinferior;2088 -eightmonospace;FF18 -eightoldstyle;F738 -eightparen;247B -eightperiod;248F -eightpersian;06F8 -eightroman;2177 -eightsuperior;2078 -eightthai;0E58 -einvertedbreve;0207 -eiotifiedcyrillic;0465 -ekatakana;30A8 -ekatakanahalfwidth;FF74 -ekonkargurmukhi;0A74 -ekorean;3154 -elcyrillic;043B -element;2208 -elevencircle;246A -elevenparen;247E -elevenperiod;2492 -elevenroman;217A -ellipsis;2026 -ellipsisvertical;22EE -emacron;0113 -emacronacute;1E17 -emacrongrave;1E15 -emcyrillic;043C -emdash;2014 -emdashvertical;FE31 -emonospace;FF45 -emphasismarkarmenian;055B -emptyset;2205 -enbopomofo;3123 -encyrillic;043D -endash;2013 -endashvertical;FE32 -endescendercyrillic;04A3 -eng;014B -engbopomofo;3125 -enghecyrillic;04A5 -enhookcyrillic;04C8 -enspace;2002 -eogonek;0119 -eokorean;3153 -eopen;025B -eopenclosed;029A -eopenreversed;025C -eopenreversedclosed;025E -eopenreversedhook;025D -eparen;24A0 -epsilon;03B5 -epsilontonos;03AD -equal;003D -equalmonospace;FF1D -equalsmall;FE66 -equalsuperior;207C -equivalence;2261 -erbopomofo;3126 -ercyrillic;0440 -ereversed;0258 -ereversedcyrillic;044D -escyrillic;0441 -esdescendercyrillic;04AB -esh;0283 -eshcurl;0286 -eshortdeva;090E -eshortvowelsigndeva;0946 -eshreversedloop;01AA -eshsquatreversed;0285 -esmallhiragana;3047 -esmallkatakana;30A7 -esmallkatakanahalfwidth;FF6A -estimated;212E -esuperior;F6EC -eta;03B7 -etarmenian;0568 -etatonos;03AE -eth;00F0 -etilde;1EBD -etildebelow;1E1B -etnahtafoukhhebrew;0591 -etnahtafoukhlefthebrew;0591 -etnahtahebrew;0591 -etnahtalefthebrew;0591 -eturned;01DD -eukorean;3161 -euro;20AC -evowelsignbengali;09C7 -evowelsigndeva;0947 -evowelsigngujarati;0AC7 -exclam;0021 -exclamarmenian;055C -exclamdbl;203C -exclamdown;00A1 -exclamdownsmall;F7A1 -exclammonospace;FF01 -exclamsmall;F721 -existential;2203 -ezh;0292 -ezhcaron;01EF -ezhcurl;0293 -ezhreversed;01B9 -ezhtail;01BA -f;0066 -fadeva;095E -fagurmukhi;0A5E -fahrenheit;2109 -fathaarabic;064E -fathalowarabic;064E -fathatanarabic;064B -fbopomofo;3108 -fcircle;24D5 -fdotaccent;1E1F -feharabic;0641 -feharmenian;0586 -fehfinalarabic;FED2 -fehinitialarabic;FED3 -fehmedialarabic;FED4 -feicoptic;03E5 -female;2640 -ff;FB00 -ffi;FB03 -ffl;FB04 -fi;FB01 -fifteencircle;246E -fifteenparen;2482 -fifteenperiod;2496 -figuredash;2012 -filledbox;25A0 -filledrect;25AC -finalkaf;05DA -finalkafdagesh;FB3A -finalkafdageshhebrew;FB3A -finalkafhebrew;05DA -finalkafqamats;05DA 05B8 -finalkafqamatshebrew;05DA 05B8 -finalkafsheva;05DA 05B0 -finalkafshevahebrew;05DA 05B0 -finalmem;05DD -finalmemhebrew;05DD -finalnun;05DF -finalnunhebrew;05DF -finalpe;05E3 -finalpehebrew;05E3 -finaltsadi;05E5 -finaltsadihebrew;05E5 -firsttonechinese;02C9 -fisheye;25C9 -fitacyrillic;0473 -five;0035 -fivearabic;0665 -fivebengali;09EB -fivecircle;2464 -fivecircleinversesansserif;278E -fivedeva;096B -fiveeighths;215D -fivegujarati;0AEB -fivegurmukhi;0A6B -fivehackarabic;0665 -fivehangzhou;3025 -fiveideographicparen;3224 -fiveinferior;2085 -fivemonospace;FF15 -fiveoldstyle;F735 -fiveparen;2478 -fiveperiod;248C -fivepersian;06F5 -fiveroman;2174 -fivesuperior;2075 -fivethai;0E55 -fl;FB02 -florin;0192 -fmonospace;FF46 -fmsquare;3399 -fofanthai;0E1F -fofathai;0E1D -fongmanthai;0E4F -forall;2200 -four;0034 -fourarabic;0664 -fourbengali;09EA -fourcircle;2463 -fourcircleinversesansserif;278D -fourdeva;096A -fourgujarati;0AEA -fourgurmukhi;0A6A -fourhackarabic;0664 -fourhangzhou;3024 -fourideographicparen;3223 -fourinferior;2084 -fourmonospace;FF14 -fournumeratorbengali;09F7 -fouroldstyle;F734 -fourparen;2477 -fourperiod;248B -fourpersian;06F4 -fourroman;2173 -foursuperior;2074 -fourteencircle;246D -fourteenparen;2481 -fourteenperiod;2495 -fourthai;0E54 -fourthtonechinese;02CB -fparen;24A1 -fraction;2044 -franc;20A3 -g;0067 -gabengali;0997 -gacute;01F5 -gadeva;0917 -gafarabic;06AF -gaffinalarabic;FB93 -gafinitialarabic;FB94 -gafmedialarabic;FB95 -gagujarati;0A97 -gagurmukhi;0A17 -gahiragana;304C -gakatakana;30AC -gamma;03B3 -gammalatinsmall;0263 -gammasuperior;02E0 -gangiacoptic;03EB -gbopomofo;310D -gbreve;011F -gcaron;01E7 -gcedilla;0123 -gcircle;24D6 -gcircumflex;011D -gcommaaccent;0123 -gdot;0121 -gdotaccent;0121 -gecyrillic;0433 -gehiragana;3052 -gekatakana;30B2 -geometricallyequal;2251 -gereshaccenthebrew;059C -gereshhebrew;05F3 -gereshmuqdamhebrew;059D -germandbls;00DF -gershayimaccenthebrew;059E -gershayimhebrew;05F4 -getamark;3013 -ghabengali;0998 -ghadarmenian;0572 -ghadeva;0918 -ghagujarati;0A98 -ghagurmukhi;0A18 -ghainarabic;063A -ghainfinalarabic;FECE -ghaininitialarabic;FECF -ghainmedialarabic;FED0 -ghemiddlehookcyrillic;0495 -ghestrokecyrillic;0493 -gheupturncyrillic;0491 -ghhadeva;095A -ghhagurmukhi;0A5A -ghook;0260 -ghzsquare;3393 -gihiragana;304E -gikatakana;30AE -gimarmenian;0563 -gimel;05D2 -gimeldagesh;FB32 -gimeldageshhebrew;FB32 -gimelhebrew;05D2 -gjecyrillic;0453 -glottalinvertedstroke;01BE -glottalstop;0294 -glottalstopinverted;0296 -glottalstopmod;02C0 -glottalstopreversed;0295 -glottalstopreversedmod;02C1 -glottalstopreversedsuperior;02E4 -glottalstopstroke;02A1 -glottalstopstrokereversed;02A2 -gmacron;1E21 -gmonospace;FF47 -gohiragana;3054 -gokatakana;30B4 -gparen;24A2 -gpasquare;33AC -gradient;2207 -grave;0060 -gravebelowcmb;0316 -gravecmb;0300 -gravecomb;0300 -gravedeva;0953 -gravelowmod;02CE -gravemonospace;FF40 -gravetonecmb;0340 -greater;003E -greaterequal;2265 -greaterequalorless;22DB -greatermonospace;FF1E -greaterorequivalent;2273 -greaterorless;2277 -greateroverequal;2267 -greatersmall;FE65 -gscript;0261 -gstroke;01E5 -guhiragana;3050 -guillemotleft;00AB -guillemotright;00BB -guilsinglleft;2039 -guilsinglright;203A -gukatakana;30B0 -guramusquare;3318 -gysquare;33C9 -h;0068 -haabkhasiancyrillic;04A9 -haaltonearabic;06C1 -habengali;09B9 -hadescendercyrillic;04B3 -hadeva;0939 -hagujarati;0AB9 -hagurmukhi;0A39 -haharabic;062D -hahfinalarabic;FEA2 -hahinitialarabic;FEA3 -hahiragana;306F -hahmedialarabic;FEA4 -haitusquare;332A -hakatakana;30CF -hakatakanahalfwidth;FF8A -halantgurmukhi;0A4D -hamzaarabic;0621 -hamzadammaarabic;0621 064F -hamzadammatanarabic;0621 064C -hamzafathaarabic;0621 064E -hamzafathatanarabic;0621 064B -hamzalowarabic;0621 -hamzalowkasraarabic;0621 0650 -hamzalowkasratanarabic;0621 064D -hamzasukunarabic;0621 0652 -hangulfiller;3164 -hardsigncyrillic;044A -harpoonleftbarbup;21BC -harpoonrightbarbup;21C0 -hasquare;33CA -hatafpatah;05B2 -hatafpatah16;05B2 -hatafpatah23;05B2 -hatafpatah2f;05B2 -hatafpatahhebrew;05B2 -hatafpatahnarrowhebrew;05B2 -hatafpatahquarterhebrew;05B2 -hatafpatahwidehebrew;05B2 -hatafqamats;05B3 -hatafqamats1b;05B3 -hatafqamats28;05B3 -hatafqamats34;05B3 -hatafqamatshebrew;05B3 -hatafqamatsnarrowhebrew;05B3 -hatafqamatsquarterhebrew;05B3 -hatafqamatswidehebrew;05B3 -hatafsegol;05B1 -hatafsegol17;05B1 -hatafsegol24;05B1 -hatafsegol30;05B1 -hatafsegolhebrew;05B1 -hatafsegolnarrowhebrew;05B1 -hatafsegolquarterhebrew;05B1 -hatafsegolwidehebrew;05B1 -hbar;0127 -hbopomofo;310F -hbrevebelow;1E2B -hcedilla;1E29 -hcircle;24D7 -hcircumflex;0125 -hdieresis;1E27 -hdotaccent;1E23 -hdotbelow;1E25 -he;05D4 -heart;2665 -heartsuitblack;2665 -heartsuitwhite;2661 -hedagesh;FB34 -hedageshhebrew;FB34 -hehaltonearabic;06C1 -heharabic;0647 -hehebrew;05D4 -hehfinalaltonearabic;FBA7 -hehfinalalttwoarabic;FEEA -hehfinalarabic;FEEA -hehhamzaabovefinalarabic;FBA5 -hehhamzaaboveisolatedarabic;FBA4 -hehinitialaltonearabic;FBA8 -hehinitialarabic;FEEB -hehiragana;3078 -hehmedialaltonearabic;FBA9 -hehmedialarabic;FEEC -heiseierasquare;337B -hekatakana;30D8 -hekatakanahalfwidth;FF8D -hekutaarusquare;3336 -henghook;0267 -herutusquare;3339 -het;05D7 -hethebrew;05D7 -hhook;0266 -hhooksuperior;02B1 -hieuhacirclekorean;327B -hieuhaparenkorean;321B -hieuhcirclekorean;326D -hieuhkorean;314E -hieuhparenkorean;320D -hihiragana;3072 -hikatakana;30D2 -hikatakanahalfwidth;FF8B -hiriq;05B4 -hiriq14;05B4 -hiriq21;05B4 -hiriq2d;05B4 -hiriqhebrew;05B4 -hiriqnarrowhebrew;05B4 -hiriqquarterhebrew;05B4 -hiriqwidehebrew;05B4 -hlinebelow;1E96 -hmonospace;FF48 -hoarmenian;0570 -hohipthai;0E2B -hohiragana;307B -hokatakana;30DB -hokatakanahalfwidth;FF8E -holam;05B9 -holam19;05B9 -holam26;05B9 -holam32;05B9 -holamhebrew;05B9 -holamnarrowhebrew;05B9 -holamquarterhebrew;05B9 -holamwidehebrew;05B9 -honokhukthai;0E2E -hookabovecomb;0309 -hookcmb;0309 -hookpalatalizedbelowcmb;0321 -hookretroflexbelowcmb;0322 -hoonsquare;3342 -horicoptic;03E9 -horizontalbar;2015 -horncmb;031B -hotsprings;2668 -house;2302 -hparen;24A3 -hsuperior;02B0 -hturned;0265 -huhiragana;3075 -huiitosquare;3333 -hukatakana;30D5 -hukatakanahalfwidth;FF8C -hungarumlaut;02DD -hungarumlautcmb;030B -hv;0195 -hyphen;002D -hypheninferior;F6E5 -hyphenmonospace;FF0D -hyphensmall;FE63 -hyphensuperior;F6E6 -hyphentwo;2010 -i;0069 -iacute;00ED -iacyrillic;044F -ibengali;0987 -ibopomofo;3127 -ibreve;012D -icaron;01D0 -icircle;24D8 -icircumflex;00EE -icyrillic;0456 -idblgrave;0209 -ideographearthcircle;328F -ideographfirecircle;328B -ideographicallianceparen;323F -ideographiccallparen;323A -ideographiccentrecircle;32A5 -ideographicclose;3006 -ideographiccomma;3001 -ideographiccommaleft;FF64 -ideographiccongratulationparen;3237 -ideographiccorrectcircle;32A3 -ideographicearthparen;322F -ideographicenterpriseparen;323D -ideographicexcellentcircle;329D -ideographicfestivalparen;3240 -ideographicfinancialcircle;3296 -ideographicfinancialparen;3236 -ideographicfireparen;322B -ideographichaveparen;3232 -ideographichighcircle;32A4 -ideographiciterationmark;3005 -ideographiclaborcircle;3298 -ideographiclaborparen;3238 -ideographicleftcircle;32A7 -ideographiclowcircle;32A6 -ideographicmedicinecircle;32A9 -ideographicmetalparen;322E -ideographicmoonparen;322A -ideographicnameparen;3234 -ideographicperiod;3002 -ideographicprintcircle;329E -ideographicreachparen;3243 -ideographicrepresentparen;3239 -ideographicresourceparen;323E -ideographicrightcircle;32A8 -ideographicsecretcircle;3299 -ideographicselfparen;3242 -ideographicsocietyparen;3233 -ideographicspace;3000 -ideographicspecialparen;3235 -ideographicstockparen;3231 -ideographicstudyparen;323B -ideographicsunparen;3230 -ideographicsuperviseparen;323C -ideographicwaterparen;322C -ideographicwoodparen;322D -ideographiczero;3007 -ideographmetalcircle;328E -ideographmooncircle;328A -ideographnamecircle;3294 -ideographsuncircle;3290 -ideographwatercircle;328C -ideographwoodcircle;328D -ideva;0907 -idieresis;00EF -idieresisacute;1E2F -idieresiscyrillic;04E5 -idotbelow;1ECB -iebrevecyrillic;04D7 -iecyrillic;0435 -ieungacirclekorean;3275 -ieungaparenkorean;3215 -ieungcirclekorean;3267 -ieungkorean;3147 -ieungparenkorean;3207 -igrave;00EC -igujarati;0A87 -igurmukhi;0A07 -ihiragana;3044 -ihookabove;1EC9 -iibengali;0988 -iicyrillic;0438 -iideva;0908 -iigujarati;0A88 -iigurmukhi;0A08 -iimatragurmukhi;0A40 -iinvertedbreve;020B -iishortcyrillic;0439 -iivowelsignbengali;09C0 -iivowelsigndeva;0940 -iivowelsigngujarati;0AC0 -ij;0133 -ikatakana;30A4 -ikatakanahalfwidth;FF72 -ikorean;3163 -ilde;02DC -iluyhebrew;05AC -imacron;012B -imacroncyrillic;04E3 -imageorapproximatelyequal;2253 -imatragurmukhi;0A3F -imonospace;FF49 -increment;2206 -infinity;221E -iniarmenian;056B -integral;222B -integralbottom;2321 -integralbt;2321 -integralex;F8F5 -integraltop;2320 -integraltp;2320 -intersection;2229 -intisquare;3305 -invbullet;25D8 -invcircle;25D9 -invsmileface;263B -iocyrillic;0451 -iogonek;012F -iota;03B9 -iotadieresis;03CA -iotadieresistonos;0390 -iotalatin;0269 -iotatonos;03AF -iparen;24A4 -irigurmukhi;0A72 -ismallhiragana;3043 -ismallkatakana;30A3 -ismallkatakanahalfwidth;FF68 -issharbengali;09FA -istroke;0268 -isuperior;F6ED -iterationhiragana;309D -iterationkatakana;30FD -itilde;0129 -itildebelow;1E2D -iubopomofo;3129 -iucyrillic;044E -ivowelsignbengali;09BF -ivowelsigndeva;093F -ivowelsigngujarati;0ABF -izhitsacyrillic;0475 -izhitsadblgravecyrillic;0477 -j;006A -jaarmenian;0571 -jabengali;099C -jadeva;091C -jagujarati;0A9C -jagurmukhi;0A1C -jbopomofo;3110 -jcaron;01F0 -jcircle;24D9 -jcircumflex;0135 -jcrossedtail;029D -jdotlessstroke;025F -jecyrillic;0458 -jeemarabic;062C -jeemfinalarabic;FE9E -jeeminitialarabic;FE9F -jeemmedialarabic;FEA0 -jeharabic;0698 -jehfinalarabic;FB8B -jhabengali;099D -jhadeva;091D -jhagujarati;0A9D -jhagurmukhi;0A1D -jheharmenian;057B -jis;3004 -jmonospace;FF4A -jparen;24A5 -jsuperior;02B2 -k;006B -kabashkircyrillic;04A1 -kabengali;0995 -kacute;1E31 -kacyrillic;043A -kadescendercyrillic;049B -kadeva;0915 -kaf;05DB -kafarabic;0643 -kafdagesh;FB3B -kafdageshhebrew;FB3B -kaffinalarabic;FEDA -kafhebrew;05DB -kafinitialarabic;FEDB -kafmedialarabic;FEDC -kafrafehebrew;FB4D -kagujarati;0A95 -kagurmukhi;0A15 -kahiragana;304B -kahookcyrillic;04C4 -kakatakana;30AB -kakatakanahalfwidth;FF76 -kappa;03BA -kappasymbolgreek;03F0 -kapyeounmieumkorean;3171 -kapyeounphieuphkorean;3184 -kapyeounpieupkorean;3178 -kapyeounssangpieupkorean;3179 -karoriisquare;330D -kashidaautoarabic;0640 -kashidaautonosidebearingarabic;0640 -kasmallkatakana;30F5 -kasquare;3384 -kasraarabic;0650 -kasratanarabic;064D -kastrokecyrillic;049F -katahiraprolongmarkhalfwidth;FF70 -kaverticalstrokecyrillic;049D -kbopomofo;310E -kcalsquare;3389 -kcaron;01E9 -kcedilla;0137 -kcircle;24DA -kcommaaccent;0137 -kdotbelow;1E33 -keharmenian;0584 -kehiragana;3051 -kekatakana;30B1 -kekatakanahalfwidth;FF79 -kenarmenian;056F -kesmallkatakana;30F6 -kgreenlandic;0138 -khabengali;0996 -khacyrillic;0445 -khadeva;0916 -khagujarati;0A96 -khagurmukhi;0A16 -khaharabic;062E -khahfinalarabic;FEA6 -khahinitialarabic;FEA7 -khahmedialarabic;FEA8 -kheicoptic;03E7 -khhadeva;0959 -khhagurmukhi;0A59 -khieukhacirclekorean;3278 -khieukhaparenkorean;3218 -khieukhcirclekorean;326A -khieukhkorean;314B -khieukhparenkorean;320A -khokhaithai;0E02 -khokhonthai;0E05 -khokhuatthai;0E03 -khokhwaithai;0E04 -khomutthai;0E5B -khook;0199 -khorakhangthai;0E06 -khzsquare;3391 -kihiragana;304D -kikatakana;30AD -kikatakanahalfwidth;FF77 -kiroguramusquare;3315 -kiromeetorusquare;3316 -kirosquare;3314 -kiyeokacirclekorean;326E -kiyeokaparenkorean;320E -kiyeokcirclekorean;3260 -kiyeokkorean;3131 -kiyeokparenkorean;3200 -kiyeoksioskorean;3133 -kjecyrillic;045C -klinebelow;1E35 -klsquare;3398 -kmcubedsquare;33A6 -kmonospace;FF4B -kmsquaredsquare;33A2 -kohiragana;3053 -kohmsquare;33C0 -kokaithai;0E01 -kokatakana;30B3 -kokatakanahalfwidth;FF7A -kooposquare;331E -koppacyrillic;0481 -koreanstandardsymbol;327F -koroniscmb;0343 -kparen;24A6 -kpasquare;33AA -ksicyrillic;046F -ktsquare;33CF -kturned;029E -kuhiragana;304F -kukatakana;30AF -kukatakanahalfwidth;FF78 -kvsquare;33B8 -kwsquare;33BE -l;006C -labengali;09B2 -lacute;013A -ladeva;0932 -lagujarati;0AB2 -lagurmukhi;0A32 -lakkhangyaothai;0E45 -lamaleffinalarabic;FEFC -lamalefhamzaabovefinalarabic;FEF8 -lamalefhamzaaboveisolatedarabic;FEF7 -lamalefhamzabelowfinalarabic;FEFA -lamalefhamzabelowisolatedarabic;FEF9 -lamalefisolatedarabic;FEFB -lamalefmaddaabovefinalarabic;FEF6 -lamalefmaddaaboveisolatedarabic;FEF5 -lamarabic;0644 -lambda;03BB -lambdastroke;019B -lamed;05DC -lameddagesh;FB3C -lameddageshhebrew;FB3C -lamedhebrew;05DC -lamedholam;05DC 05B9 -lamedholamdagesh;05DC 05B9 05BC -lamedholamdageshhebrew;05DC 05B9 05BC -lamedholamhebrew;05DC 05B9 -lamfinalarabic;FEDE -lamhahinitialarabic;FCCA -laminitialarabic;FEDF -lamjeeminitialarabic;FCC9 -lamkhahinitialarabic;FCCB -lamlamhehisolatedarabic;FDF2 -lammedialarabic;FEE0 -lammeemhahinitialarabic;FD88 -lammeeminitialarabic;FCCC -lammeemjeeminitialarabic;FEDF FEE4 FEA0 -lammeemkhahinitialarabic;FEDF FEE4 FEA8 -largecircle;25EF -lbar;019A -lbelt;026C -lbopomofo;310C -lcaron;013E -lcedilla;013C -lcircle;24DB -lcircumflexbelow;1E3D -lcommaaccent;013C -ldot;0140 -ldotaccent;0140 -ldotbelow;1E37 -ldotbelowmacron;1E39 -leftangleabovecmb;031A -lefttackbelowcmb;0318 -less;003C -lessequal;2264 -lessequalorgreater;22DA -lessmonospace;FF1C -lessorequivalent;2272 -lessorgreater;2276 -lessoverequal;2266 -lesssmall;FE64 -lezh;026E -lfblock;258C -lhookretroflex;026D -lira;20A4 -liwnarmenian;056C -lj;01C9 -ljecyrillic;0459 -ll;F6C0 -lladeva;0933 -llagujarati;0AB3 -llinebelow;1E3B -llladeva;0934 -llvocalicbengali;09E1 -llvocalicdeva;0961 -llvocalicvowelsignbengali;09E3 -llvocalicvowelsigndeva;0963 -lmiddletilde;026B -lmonospace;FF4C -lmsquare;33D0 -lochulathai;0E2C -logicaland;2227 -logicalnot;00AC -logicalnotreversed;2310 -logicalor;2228 -lolingthai;0E25 -longs;017F -lowlinecenterline;FE4E -lowlinecmb;0332 -lowlinedashed;FE4D -lozenge;25CA -lparen;24A7 -lslash;0142 -lsquare;2113 -lsuperior;F6EE -ltshade;2591 -luthai;0E26 -lvocalicbengali;098C -lvocalicdeva;090C -lvocalicvowelsignbengali;09E2 -lvocalicvowelsigndeva;0962 -lxsquare;33D3 -m;006D -mabengali;09AE -macron;00AF -macronbelowcmb;0331 -macroncmb;0304 -macronlowmod;02CD -macronmonospace;FFE3 -macute;1E3F -madeva;092E -magujarati;0AAE -magurmukhi;0A2E -mahapakhhebrew;05A4 -mahapakhlefthebrew;05A4 -mahiragana;307E -maichattawalowleftthai;F895 -maichattawalowrightthai;F894 -maichattawathai;0E4B -maichattawaupperleftthai;F893 -maieklowleftthai;F88C -maieklowrightthai;F88B -maiekthai;0E48 -maiekupperleftthai;F88A -maihanakatleftthai;F884 -maihanakatthai;0E31 -maitaikhuleftthai;F889 -maitaikhuthai;0E47 -maitholowleftthai;F88F -maitholowrightthai;F88E -maithothai;0E49 -maithoupperleftthai;F88D -maitrilowleftthai;F892 -maitrilowrightthai;F891 -maitrithai;0E4A -maitriupperleftthai;F890 -maiyamokthai;0E46 -makatakana;30DE -makatakanahalfwidth;FF8F -male;2642 -mansyonsquare;3347 -maqafhebrew;05BE -mars;2642 -masoracirclehebrew;05AF -masquare;3383 -mbopomofo;3107 -mbsquare;33D4 -mcircle;24DC -mcubedsquare;33A5 -mdotaccent;1E41 -mdotbelow;1E43 -meemarabic;0645 -meemfinalarabic;FEE2 -meeminitialarabic;FEE3 -meemmedialarabic;FEE4 -meemmeeminitialarabic;FCD1 -meemmeemisolatedarabic;FC48 -meetorusquare;334D -mehiragana;3081 -meizierasquare;337E -mekatakana;30E1 -mekatakanahalfwidth;FF92 -mem;05DE -memdagesh;FB3E -memdageshhebrew;FB3E -memhebrew;05DE -menarmenian;0574 -merkhahebrew;05A5 -merkhakefulahebrew;05A6 -merkhakefulalefthebrew;05A6 -merkhalefthebrew;05A5 -mhook;0271 -mhzsquare;3392 -middledotkatakanahalfwidth;FF65 -middot;00B7 -mieumacirclekorean;3272 -mieumaparenkorean;3212 -mieumcirclekorean;3264 -mieumkorean;3141 -mieumpansioskorean;3170 -mieumparenkorean;3204 -mieumpieupkorean;316E -mieumsioskorean;316F -mihiragana;307F -mikatakana;30DF -mikatakanahalfwidth;FF90 -minus;2212 -minusbelowcmb;0320 -minuscircle;2296 -minusmod;02D7 -minusplus;2213 -minute;2032 -miribaarusquare;334A -mirisquare;3349 -mlonglegturned;0270 -mlsquare;3396 -mmcubedsquare;33A3 -mmonospace;FF4D -mmsquaredsquare;339F -mohiragana;3082 -mohmsquare;33C1 -mokatakana;30E2 -mokatakanahalfwidth;FF93 -molsquare;33D6 -momathai;0E21 -moverssquare;33A7 -moverssquaredsquare;33A8 -mparen;24A8 -mpasquare;33AB -mssquare;33B3 -msuperior;F6EF -mturned;026F -mu;00B5 -mu1;00B5 -muasquare;3382 -muchgreater;226B -muchless;226A -mufsquare;338C -mugreek;03BC -mugsquare;338D -muhiragana;3080 -mukatakana;30E0 -mukatakanahalfwidth;FF91 -mulsquare;3395 -multiply;00D7 -mumsquare;339B -munahhebrew;05A3 -munahlefthebrew;05A3 -musicalnote;266A -musicalnotedbl;266B -musicflatsign;266D -musicsharpsign;266F -mussquare;33B2 -muvsquare;33B6 -muwsquare;33BC -mvmegasquare;33B9 -mvsquare;33B7 -mwmegasquare;33BF -mwsquare;33BD -n;006E -nabengali;09A8 -nabla;2207 -nacute;0144 -nadeva;0928 -nagujarati;0AA8 -nagurmukhi;0A28 -nahiragana;306A -nakatakana;30CA -nakatakanahalfwidth;FF85 -napostrophe;0149 -nasquare;3381 -nbopomofo;310B -nbspace;00A0 -ncaron;0148 -ncedilla;0146 -ncircle;24DD -ncircumflexbelow;1E4B -ncommaaccent;0146 -ndotaccent;1E45 -ndotbelow;1E47 -nehiragana;306D -nekatakana;30CD -nekatakanahalfwidth;FF88 -newsheqelsign;20AA -nfsquare;338B -ngabengali;0999 -ngadeva;0919 -ngagujarati;0A99 -ngagurmukhi;0A19 -ngonguthai;0E07 -nhiragana;3093 -nhookleft;0272 -nhookretroflex;0273 -nieunacirclekorean;326F -nieunaparenkorean;320F -nieuncieuckorean;3135 -nieuncirclekorean;3261 -nieunhieuhkorean;3136 -nieunkorean;3134 -nieunpansioskorean;3168 -nieunparenkorean;3201 -nieunsioskorean;3167 -nieuntikeutkorean;3166 -nihiragana;306B -nikatakana;30CB -nikatakanahalfwidth;FF86 -nikhahitleftthai;F899 -nikhahitthai;0E4D -nine;0039 -ninearabic;0669 -ninebengali;09EF -ninecircle;2468 -ninecircleinversesansserif;2792 -ninedeva;096F -ninegujarati;0AEF -ninegurmukhi;0A6F -ninehackarabic;0669 -ninehangzhou;3029 -nineideographicparen;3228 -nineinferior;2089 -ninemonospace;FF19 -nineoldstyle;F739 -nineparen;247C -nineperiod;2490 -ninepersian;06F9 -nineroman;2178 -ninesuperior;2079 -nineteencircle;2472 -nineteenparen;2486 -nineteenperiod;249A -ninethai;0E59 -nj;01CC -njecyrillic;045A -nkatakana;30F3 -nkatakanahalfwidth;FF9D -nlegrightlong;019E -nlinebelow;1E49 -nmonospace;FF4E -nmsquare;339A -nnabengali;09A3 -nnadeva;0923 -nnagujarati;0AA3 -nnagurmukhi;0A23 -nnnadeva;0929 -nohiragana;306E -nokatakana;30CE -nokatakanahalfwidth;FF89 -nonbreakingspace;00A0 -nonenthai;0E13 -nonuthai;0E19 -noonarabic;0646 -noonfinalarabic;FEE6 -noonghunnaarabic;06BA -noonghunnafinalarabic;FB9F -noonhehinitialarabic;FEE7 FEEC -nooninitialarabic;FEE7 -noonjeeminitialarabic;FCD2 -noonjeemisolatedarabic;FC4B -noonmedialarabic;FEE8 -noonmeeminitialarabic;FCD5 -noonmeemisolatedarabic;FC4E -noonnoonfinalarabic;FC8D -notcontains;220C -notelement;2209 -notelementof;2209 -notequal;2260 -notgreater;226F -notgreaternorequal;2271 -notgreaternorless;2279 -notidentical;2262 -notless;226E -notlessnorequal;2270 -notparallel;2226 -notprecedes;2280 -notsubset;2284 -notsucceeds;2281 -notsuperset;2285 -nowarmenian;0576 -nparen;24A9 -nssquare;33B1 -nsuperior;207F -ntilde;00F1 -nu;03BD -nuhiragana;306C -nukatakana;30CC -nukatakanahalfwidth;FF87 -nuktabengali;09BC -nuktadeva;093C -nuktagujarati;0ABC -nuktagurmukhi;0A3C -numbersign;0023 -numbersignmonospace;FF03 -numbersignsmall;FE5F -numeralsigngreek;0374 -numeralsignlowergreek;0375 -numero;2116 -nun;05E0 -nundagesh;FB40 -nundageshhebrew;FB40 -nunhebrew;05E0 -nvsquare;33B5 -nwsquare;33BB -nyabengali;099E -nyadeva;091E -nyagujarati;0A9E -nyagurmukhi;0A1E -o;006F -oacute;00F3 -oangthai;0E2D -obarred;0275 -obarredcyrillic;04E9 -obarreddieresiscyrillic;04EB -obengali;0993 -obopomofo;311B -obreve;014F -ocandradeva;0911 -ocandragujarati;0A91 -ocandravowelsigndeva;0949 -ocandravowelsigngujarati;0AC9 -ocaron;01D2 -ocircle;24DE -ocircumflex;00F4 -ocircumflexacute;1ED1 -ocircumflexdotbelow;1ED9 -ocircumflexgrave;1ED3 -ocircumflexhookabove;1ED5 -ocircumflextilde;1ED7 -ocyrillic;043E -odblacute;0151 -odblgrave;020D -odeva;0913 -odieresis;00F6 -odieresiscyrillic;04E7 -odotbelow;1ECD -oe;0153 -oekorean;315A -ogonek;02DB -ogonekcmb;0328 -ograve;00F2 -ogujarati;0A93 -oharmenian;0585 -ohiragana;304A -ohookabove;1ECF -ohorn;01A1 -ohornacute;1EDB -ohorndotbelow;1EE3 -ohorngrave;1EDD -ohornhookabove;1EDF -ohorntilde;1EE1 -ohungarumlaut;0151 -oi;01A3 -oinvertedbreve;020F -okatakana;30AA -okatakanahalfwidth;FF75 -okorean;3157 -olehebrew;05AB -omacron;014D -omacronacute;1E53 -omacrongrave;1E51 -omdeva;0950 -omega;03C9 -omega1;03D6 -omegacyrillic;0461 -omegalatinclosed;0277 -omegaroundcyrillic;047B -omegatitlocyrillic;047D -omegatonos;03CE -omgujarati;0AD0 -omicron;03BF -omicrontonos;03CC -omonospace;FF4F -one;0031 -onearabic;0661 -onebengali;09E7 -onecircle;2460 -onecircleinversesansserif;278A -onedeva;0967 -onedotenleader;2024 -oneeighth;215B -onefitted;F6DC -onegujarati;0AE7 -onegurmukhi;0A67 -onehackarabic;0661 -onehalf;00BD -onehangzhou;3021 -oneideographicparen;3220 -oneinferior;2081 -onemonospace;FF11 -onenumeratorbengali;09F4 -oneoldstyle;F731 -oneparen;2474 -oneperiod;2488 -onepersian;06F1 -onequarter;00BC -oneroman;2170 -onesuperior;00B9 -onethai;0E51 -onethird;2153 -oogonek;01EB -oogonekmacron;01ED -oogurmukhi;0A13 -oomatragurmukhi;0A4B -oopen;0254 -oparen;24AA -openbullet;25E6 -option;2325 -ordfeminine;00AA -ordmasculine;00BA -orthogonal;221F -oshortdeva;0912 -oshortvowelsigndeva;094A -oslash;00F8 -oslashacute;01FF -osmallhiragana;3049 -osmallkatakana;30A9 -osmallkatakanahalfwidth;FF6B -ostrokeacute;01FF -osuperior;F6F0 -otcyrillic;047F -otilde;00F5 -otildeacute;1E4D -otildedieresis;1E4F -oubopomofo;3121 -overline;203E -overlinecenterline;FE4A -overlinecmb;0305 -overlinedashed;FE49 -overlinedblwavy;FE4C -overlinewavy;FE4B -overscore;00AF -ovowelsignbengali;09CB -ovowelsigndeva;094B -ovowelsigngujarati;0ACB -p;0070 -paampssquare;3380 -paasentosquare;332B -pabengali;09AA -pacute;1E55 -padeva;092A -pagedown;21DF -pageup;21DE -pagujarati;0AAA -pagurmukhi;0A2A -pahiragana;3071 -paiyannoithai;0E2F -pakatakana;30D1 -palatalizationcyrilliccmb;0484 -palochkacyrillic;04C0 -pansioskorean;317F -paragraph;00B6 -parallel;2225 -parenleft;0028 -parenleftaltonearabic;FD3E -parenleftbt;F8ED -parenleftex;F8EC -parenleftinferior;208D -parenleftmonospace;FF08 -parenleftsmall;FE59 -parenleftsuperior;207D -parenlefttp;F8EB -parenleftvertical;FE35 -parenright;0029 -parenrightaltonearabic;FD3F -parenrightbt;F8F8 -parenrightex;F8F7 -parenrightinferior;208E -parenrightmonospace;FF09 -parenrightsmall;FE5A -parenrightsuperior;207E -parenrighttp;F8F6 -parenrightvertical;FE36 -partialdiff;2202 -paseqhebrew;05C0 -pashtahebrew;0599 -pasquare;33A9 -patah;05B7 -patah11;05B7 -patah1d;05B7 -patah2a;05B7 -patahhebrew;05B7 -patahnarrowhebrew;05B7 -patahquarterhebrew;05B7 -patahwidehebrew;05B7 -pazerhebrew;05A1 -pbopomofo;3106 -pcircle;24DF -pdotaccent;1E57 -pe;05E4 -pecyrillic;043F -pedagesh;FB44 -pedageshhebrew;FB44 -peezisquare;333B -pefinaldageshhebrew;FB43 -peharabic;067E -peharmenian;057A -pehebrew;05E4 -pehfinalarabic;FB57 -pehinitialarabic;FB58 -pehiragana;307A -pehmedialarabic;FB59 -pekatakana;30DA -pemiddlehookcyrillic;04A7 -perafehebrew;FB4E -percent;0025 -percentarabic;066A -percentmonospace;FF05 -percentsmall;FE6A -period;002E -periodarmenian;0589 -periodcentered;00B7 -periodhalfwidth;FF61 -periodinferior;F6E7 -periodmonospace;FF0E -periodsmall;FE52 -periodsuperior;F6E8 -perispomenigreekcmb;0342 -perpendicular;22A5 -perthousand;2030 -peseta;20A7 -pfsquare;338A -phabengali;09AB -phadeva;092B -phagujarati;0AAB -phagurmukhi;0A2B -phi;03C6 -phi1;03D5 -phieuphacirclekorean;327A -phieuphaparenkorean;321A -phieuphcirclekorean;326C -phieuphkorean;314D -phieuphparenkorean;320C -philatin;0278 -phinthuthai;0E3A -phisymbolgreek;03D5 -phook;01A5 -phophanthai;0E1E -phophungthai;0E1C -phosamphaothai;0E20 -pi;03C0 -pieupacirclekorean;3273 -pieupaparenkorean;3213 -pieupcieuckorean;3176 -pieupcirclekorean;3265 -pieupkiyeokkorean;3172 -pieupkorean;3142 -pieupparenkorean;3205 -pieupsioskiyeokkorean;3174 -pieupsioskorean;3144 -pieupsiostikeutkorean;3175 -pieupthieuthkorean;3177 -pieuptikeutkorean;3173 -pihiragana;3074 -pikatakana;30D4 -pisymbolgreek;03D6 -piwrarmenian;0583 -plus;002B -plusbelowcmb;031F -pluscircle;2295 -plusminus;00B1 -plusmod;02D6 -plusmonospace;FF0B -plussmall;FE62 -plussuperior;207A -pmonospace;FF50 -pmsquare;33D8 -pohiragana;307D -pointingindexdownwhite;261F -pointingindexleftwhite;261C -pointingindexrightwhite;261E -pointingindexupwhite;261D -pokatakana;30DD -poplathai;0E1B -postalmark;3012 -postalmarkface;3020 -pparen;24AB -precedes;227A -prescription;211E -primemod;02B9 -primereversed;2035 -product;220F -projective;2305 -prolongedkana;30FC -propellor;2318 -propersubset;2282 -propersuperset;2283 -proportion;2237 -proportional;221D -psi;03C8 -psicyrillic;0471 -psilipneumatacyrilliccmb;0486 -pssquare;33B0 -puhiragana;3077 -pukatakana;30D7 -pvsquare;33B4 -pwsquare;33BA -q;0071 -qadeva;0958 -qadmahebrew;05A8 -qafarabic;0642 -qaffinalarabic;FED6 -qafinitialarabic;FED7 -qafmedialarabic;FED8 -qamats;05B8 -qamats10;05B8 -qamats1a;05B8 -qamats1c;05B8 -qamats27;05B8 -qamats29;05B8 -qamats33;05B8 -qamatsde;05B8 -qamatshebrew;05B8 -qamatsnarrowhebrew;05B8 -qamatsqatanhebrew;05B8 -qamatsqatannarrowhebrew;05B8 -qamatsqatanquarterhebrew;05B8 -qamatsqatanwidehebrew;05B8 -qamatsquarterhebrew;05B8 -qamatswidehebrew;05B8 -qarneyparahebrew;059F -qbopomofo;3111 -qcircle;24E0 -qhook;02A0 -qmonospace;FF51 -qof;05E7 -qofdagesh;FB47 -qofdageshhebrew;FB47 -qofhatafpatah;05E7 05B2 -qofhatafpatahhebrew;05E7 05B2 -qofhatafsegol;05E7 05B1 -qofhatafsegolhebrew;05E7 05B1 -qofhebrew;05E7 -qofhiriq;05E7 05B4 -qofhiriqhebrew;05E7 05B4 -qofholam;05E7 05B9 -qofholamhebrew;05E7 05B9 -qofpatah;05E7 05B7 -qofpatahhebrew;05E7 05B7 -qofqamats;05E7 05B8 -qofqamatshebrew;05E7 05B8 -qofqubuts;05E7 05BB -qofqubutshebrew;05E7 05BB -qofsegol;05E7 05B6 -qofsegolhebrew;05E7 05B6 -qofsheva;05E7 05B0 -qofshevahebrew;05E7 05B0 -qoftsere;05E7 05B5 -qoftserehebrew;05E7 05B5 -qparen;24AC -quarternote;2669 -qubuts;05BB -qubuts18;05BB -qubuts25;05BB -qubuts31;05BB -qubutshebrew;05BB -qubutsnarrowhebrew;05BB -qubutsquarterhebrew;05BB -qubutswidehebrew;05BB -question;003F -questionarabic;061F -questionarmenian;055E -questiondown;00BF -questiondownsmall;F7BF -questiongreek;037E -questionmonospace;FF1F -questionsmall;F73F -quotedbl;0022 -quotedblbase;201E -quotedblleft;201C -quotedblmonospace;FF02 -quotedblprime;301E -quotedblprimereversed;301D -quotedblright;201D -quoteleft;2018 -quoteleftreversed;201B -quotereversed;201B -quoteright;2019 -quoterightn;0149 -quotesinglbase;201A -quotesingle;0027 -quotesinglemonospace;FF07 -r;0072 -raarmenian;057C -rabengali;09B0 -racute;0155 -radeva;0930 -radical;221A -radicalex;F8E5 -radoverssquare;33AE -radoverssquaredsquare;33AF -radsquare;33AD -rafe;05BF -rafehebrew;05BF -ragujarati;0AB0 -ragurmukhi;0A30 -rahiragana;3089 -rakatakana;30E9 -rakatakanahalfwidth;FF97 -ralowerdiagonalbengali;09F1 -ramiddlediagonalbengali;09F0 -ramshorn;0264 -ratio;2236 -rbopomofo;3116 -rcaron;0159 -rcedilla;0157 -rcircle;24E1 -rcommaaccent;0157 -rdblgrave;0211 -rdotaccent;1E59 -rdotbelow;1E5B -rdotbelowmacron;1E5D -referencemark;203B -reflexsubset;2286 -reflexsuperset;2287 -registered;00AE -registersans;F8E8 -registerserif;F6DA -reharabic;0631 -reharmenian;0580 -rehfinalarabic;FEAE -rehiragana;308C -rehyehaleflamarabic;0631 FEF3 FE8E 0644 -rekatakana;30EC -rekatakanahalfwidth;FF9A -resh;05E8 -reshdageshhebrew;FB48 -reshhatafpatah;05E8 05B2 -reshhatafpatahhebrew;05E8 05B2 -reshhatafsegol;05E8 05B1 -reshhatafsegolhebrew;05E8 05B1 -reshhebrew;05E8 -reshhiriq;05E8 05B4 -reshhiriqhebrew;05E8 05B4 -reshholam;05E8 05B9 -reshholamhebrew;05E8 05B9 -reshpatah;05E8 05B7 -reshpatahhebrew;05E8 05B7 -reshqamats;05E8 05B8 -reshqamatshebrew;05E8 05B8 -reshqubuts;05E8 05BB -reshqubutshebrew;05E8 05BB -reshsegol;05E8 05B6 -reshsegolhebrew;05E8 05B6 -reshsheva;05E8 05B0 -reshshevahebrew;05E8 05B0 -reshtsere;05E8 05B5 -reshtserehebrew;05E8 05B5 -reversedtilde;223D -reviahebrew;0597 -reviamugrashhebrew;0597 -revlogicalnot;2310 -rfishhook;027E -rfishhookreversed;027F -rhabengali;09DD -rhadeva;095D -rho;03C1 -rhook;027D -rhookturned;027B -rhookturnedsuperior;02B5 -rhosymbolgreek;03F1 -rhotichookmod;02DE -rieulacirclekorean;3271 -rieulaparenkorean;3211 -rieulcirclekorean;3263 -rieulhieuhkorean;3140 -rieulkiyeokkorean;313A -rieulkiyeoksioskorean;3169 -rieulkorean;3139 -rieulmieumkorean;313B -rieulpansioskorean;316C -rieulparenkorean;3203 -rieulphieuphkorean;313F -rieulpieupkorean;313C -rieulpieupsioskorean;316B -rieulsioskorean;313D -rieulthieuthkorean;313E -rieultikeutkorean;316A -rieulyeorinhieuhkorean;316D -rightangle;221F -righttackbelowcmb;0319 -righttriangle;22BF -rihiragana;308A -rikatakana;30EA -rikatakanahalfwidth;FF98 -ring;02DA -ringbelowcmb;0325 -ringcmb;030A -ringhalfleft;02BF -ringhalfleftarmenian;0559 -ringhalfleftbelowcmb;031C -ringhalfleftcentered;02D3 -ringhalfright;02BE -ringhalfrightbelowcmb;0339 -ringhalfrightcentered;02D2 -rinvertedbreve;0213 -rittorusquare;3351 -rlinebelow;1E5F -rlongleg;027C -rlonglegturned;027A -rmonospace;FF52 -rohiragana;308D -rokatakana;30ED -rokatakanahalfwidth;FF9B -roruathai;0E23 -rparen;24AD -rrabengali;09DC -rradeva;0931 -rragurmukhi;0A5C -rreharabic;0691 -rrehfinalarabic;FB8D -rrvocalicbengali;09E0 -rrvocalicdeva;0960 -rrvocalicgujarati;0AE0 -rrvocalicvowelsignbengali;09C4 -rrvocalicvowelsigndeva;0944 -rrvocalicvowelsigngujarati;0AC4 -rsuperior;F6F1 -rtblock;2590 -rturned;0279 -rturnedsuperior;02B4 -ruhiragana;308B -rukatakana;30EB -rukatakanahalfwidth;FF99 -rupeemarkbengali;09F2 -rupeesignbengali;09F3 -rupiah;F6DD -ruthai;0E24 -rvocalicbengali;098B -rvocalicdeva;090B -rvocalicgujarati;0A8B -rvocalicvowelsignbengali;09C3 -rvocalicvowelsigndeva;0943 -rvocalicvowelsigngujarati;0AC3 -s;0073 -sabengali;09B8 -sacute;015B -sacutedotaccent;1E65 -sadarabic;0635 -sadeva;0938 -sadfinalarabic;FEBA -sadinitialarabic;FEBB -sadmedialarabic;FEBC -sagujarati;0AB8 -sagurmukhi;0A38 -sahiragana;3055 -sakatakana;30B5 -sakatakanahalfwidth;FF7B -sallallahoualayhewasallamarabic;FDFA -samekh;05E1 -samekhdagesh;FB41 -samekhdageshhebrew;FB41 -samekhhebrew;05E1 -saraaathai;0E32 -saraaethai;0E41 -saraaimaimalaithai;0E44 -saraaimaimuanthai;0E43 -saraamthai;0E33 -saraathai;0E30 -saraethai;0E40 -saraiileftthai;F886 -saraiithai;0E35 -saraileftthai;F885 -saraithai;0E34 -saraothai;0E42 -saraueeleftthai;F888 -saraueethai;0E37 -saraueleftthai;F887 -sarauethai;0E36 -sarauthai;0E38 -sarauuthai;0E39 -sbopomofo;3119 -scaron;0161 -scarondotaccent;1E67 -scedilla;015F -schwa;0259 -schwacyrillic;04D9 -schwadieresiscyrillic;04DB -schwahook;025A -scircle;24E2 -scircumflex;015D -scommaaccent;0219 -sdotaccent;1E61 -sdotbelow;1E63 -sdotbelowdotaccent;1E69 -seagullbelowcmb;033C -second;2033 -secondtonechinese;02CA -section;00A7 -seenarabic;0633 -seenfinalarabic;FEB2 -seeninitialarabic;FEB3 -seenmedialarabic;FEB4 -segol;05B6 -segol13;05B6 -segol1f;05B6 -segol2c;05B6 -segolhebrew;05B6 -segolnarrowhebrew;05B6 -segolquarterhebrew;05B6 -segoltahebrew;0592 -segolwidehebrew;05B6 -seharmenian;057D -sehiragana;305B -sekatakana;30BB -sekatakanahalfwidth;FF7E -semicolon;003B -semicolonarabic;061B -semicolonmonospace;FF1B -semicolonsmall;FE54 -semivoicedmarkkana;309C -semivoicedmarkkanahalfwidth;FF9F -sentisquare;3322 -sentosquare;3323 -seven;0037 -sevenarabic;0667 -sevenbengali;09ED -sevencircle;2466 -sevencircleinversesansserif;2790 -sevendeva;096D -seveneighths;215E -sevengujarati;0AED -sevengurmukhi;0A6D -sevenhackarabic;0667 -sevenhangzhou;3027 -sevenideographicparen;3226 -seveninferior;2087 -sevenmonospace;FF17 -sevenoldstyle;F737 -sevenparen;247A -sevenperiod;248E -sevenpersian;06F7 -sevenroman;2176 -sevensuperior;2077 -seventeencircle;2470 -seventeenparen;2484 -seventeenperiod;2498 -seventhai;0E57 -sfthyphen;00AD -shaarmenian;0577 -shabengali;09B6 -shacyrillic;0448 -shaddaarabic;0651 -shaddadammaarabic;FC61 -shaddadammatanarabic;FC5E -shaddafathaarabic;FC60 -shaddafathatanarabic;0651 064B -shaddakasraarabic;FC62 -shaddakasratanarabic;FC5F -shade;2592 -shadedark;2593 -shadelight;2591 -shademedium;2592 -shadeva;0936 -shagujarati;0AB6 -shagurmukhi;0A36 -shalshelethebrew;0593 -shbopomofo;3115 -shchacyrillic;0449 -sheenarabic;0634 -sheenfinalarabic;FEB6 -sheeninitialarabic;FEB7 -sheenmedialarabic;FEB8 -sheicoptic;03E3 -sheqel;20AA -sheqelhebrew;20AA -sheva;05B0 -sheva115;05B0 -sheva15;05B0 -sheva22;05B0 -sheva2e;05B0 -shevahebrew;05B0 -shevanarrowhebrew;05B0 -shevaquarterhebrew;05B0 -shevawidehebrew;05B0 -shhacyrillic;04BB -shimacoptic;03ED -shin;05E9 -shindagesh;FB49 -shindageshhebrew;FB49 -shindageshshindot;FB2C -shindageshshindothebrew;FB2C -shindageshsindot;FB2D -shindageshsindothebrew;FB2D -shindothebrew;05C1 -shinhebrew;05E9 -shinshindot;FB2A -shinshindothebrew;FB2A -shinsindot;FB2B -shinsindothebrew;FB2B -shook;0282 -sigma;03C3 -sigma1;03C2 -sigmafinal;03C2 -sigmalunatesymbolgreek;03F2 -sihiragana;3057 -sikatakana;30B7 -sikatakanahalfwidth;FF7C -siluqhebrew;05BD -siluqlefthebrew;05BD -similar;223C -sindothebrew;05C2 -siosacirclekorean;3274 -siosaparenkorean;3214 -sioscieuckorean;317E -sioscirclekorean;3266 -sioskiyeokkorean;317A -sioskorean;3145 -siosnieunkorean;317B -siosparenkorean;3206 -siospieupkorean;317D -siostikeutkorean;317C -six;0036 -sixarabic;0666 -sixbengali;09EC -sixcircle;2465 -sixcircleinversesansserif;278F -sixdeva;096C -sixgujarati;0AEC -sixgurmukhi;0A6C -sixhackarabic;0666 -sixhangzhou;3026 -sixideographicparen;3225 -sixinferior;2086 -sixmonospace;FF16 -sixoldstyle;F736 -sixparen;2479 -sixperiod;248D -sixpersian;06F6 -sixroman;2175 -sixsuperior;2076 -sixteencircle;246F -sixteencurrencydenominatorbengali;09F9 -sixteenparen;2483 -sixteenperiod;2497 -sixthai;0E56 -slash;002F -slashmonospace;FF0F -slong;017F -slongdotaccent;1E9B -smileface;263A -smonospace;FF53 -sofpasuqhebrew;05C3 -softhyphen;00AD -softsigncyrillic;044C -sohiragana;305D -sokatakana;30BD -sokatakanahalfwidth;FF7F -soliduslongoverlaycmb;0338 -solidusshortoverlaycmb;0337 -sorusithai;0E29 -sosalathai;0E28 -sosothai;0E0B -sosuathai;0E2A -space;0020 -spacehackarabic;0020 -spade;2660 -spadesuitblack;2660 -spadesuitwhite;2664 -sparen;24AE -squarebelowcmb;033B -squarecc;33C4 -squarecm;339D -squarediagonalcrosshatchfill;25A9 -squarehorizontalfill;25A4 -squarekg;338F -squarekm;339E -squarekmcapital;33CE -squareln;33D1 -squarelog;33D2 -squaremg;338E -squaremil;33D5 -squaremm;339C -squaremsquared;33A1 -squareorthogonalcrosshatchfill;25A6 -squareupperlefttolowerrightfill;25A7 -squareupperrighttolowerleftfill;25A8 -squareverticalfill;25A5 -squarewhitewithsmallblack;25A3 -srsquare;33DB -ssabengali;09B7 -ssadeva;0937 -ssagujarati;0AB7 -ssangcieuckorean;3149 -ssanghieuhkorean;3185 -ssangieungkorean;3180 -ssangkiyeokkorean;3132 -ssangnieunkorean;3165 -ssangpieupkorean;3143 -ssangsioskorean;3146 -ssangtikeutkorean;3138 -ssuperior;F6F2 -sterling;00A3 -sterlingmonospace;FFE1 -strokelongoverlaycmb;0336 -strokeshortoverlaycmb;0335 -subset;2282 -subsetnotequal;228A -subsetorequal;2286 -succeeds;227B -suchthat;220B -suhiragana;3059 -sukatakana;30B9 -sukatakanahalfwidth;FF7D -sukunarabic;0652 -summation;2211 -sun;263C -superset;2283 -supersetnotequal;228B -supersetorequal;2287 -svsquare;33DC -syouwaerasquare;337C -t;0074 -tabengali;09A4 -tackdown;22A4 -tackleft;22A3 -tadeva;0924 -tagujarati;0AA4 -tagurmukhi;0A24 -taharabic;0637 -tahfinalarabic;FEC2 -tahinitialarabic;FEC3 -tahiragana;305F -tahmedialarabic;FEC4 -taisyouerasquare;337D -takatakana;30BF -takatakanahalfwidth;FF80 -tatweelarabic;0640 -tau;03C4 -tav;05EA -tavdages;FB4A -tavdagesh;FB4A -tavdageshhebrew;FB4A -tavhebrew;05EA -tbar;0167 -tbopomofo;310A -tcaron;0165 -tccurl;02A8 -tcedilla;0163 -tcheharabic;0686 -tchehfinalarabic;FB7B -tchehinitialarabic;FB7C -tchehmedialarabic;FB7D -tchehmeeminitialarabic;FB7C FEE4 -tcircle;24E3 -tcircumflexbelow;1E71 -tcommaaccent;0163 -tdieresis;1E97 -tdotaccent;1E6B -tdotbelow;1E6D -tecyrillic;0442 -tedescendercyrillic;04AD -teharabic;062A -tehfinalarabic;FE96 -tehhahinitialarabic;FCA2 -tehhahisolatedarabic;FC0C -tehinitialarabic;FE97 -tehiragana;3066 -tehjeeminitialarabic;FCA1 -tehjeemisolatedarabic;FC0B -tehmarbutaarabic;0629 -tehmarbutafinalarabic;FE94 -tehmedialarabic;FE98 -tehmeeminitialarabic;FCA4 -tehmeemisolatedarabic;FC0E -tehnoonfinalarabic;FC73 -tekatakana;30C6 -tekatakanahalfwidth;FF83 -telephone;2121 -telephoneblack;260E -telishagedolahebrew;05A0 -telishaqetanahebrew;05A9 -tencircle;2469 -tenideographicparen;3229 -tenparen;247D -tenperiod;2491 -tenroman;2179 -tesh;02A7 -tet;05D8 -tetdagesh;FB38 -tetdageshhebrew;FB38 -tethebrew;05D8 -tetsecyrillic;04B5 -tevirhebrew;059B -tevirlefthebrew;059B -thabengali;09A5 -thadeva;0925 -thagujarati;0AA5 -thagurmukhi;0A25 -thalarabic;0630 -thalfinalarabic;FEAC -thanthakhatlowleftthai;F898 -thanthakhatlowrightthai;F897 -thanthakhatthai;0E4C -thanthakhatupperleftthai;F896 -theharabic;062B -thehfinalarabic;FE9A -thehinitialarabic;FE9B -thehmedialarabic;FE9C -thereexists;2203 -therefore;2234 -theta;03B8 -theta1;03D1 -thetasymbolgreek;03D1 -thieuthacirclekorean;3279 -thieuthaparenkorean;3219 -thieuthcirclekorean;326B -thieuthkorean;314C -thieuthparenkorean;320B -thirteencircle;246C -thirteenparen;2480 -thirteenperiod;2494 -thonangmonthothai;0E11 -thook;01AD -thophuthaothai;0E12 -thorn;00FE -thothahanthai;0E17 -thothanthai;0E10 -thothongthai;0E18 -thothungthai;0E16 -thousandcyrillic;0482 -thousandsseparatorarabic;066C -thousandsseparatorpersian;066C -three;0033 -threearabic;0663 -threebengali;09E9 -threecircle;2462 -threecircleinversesansserif;278C -threedeva;0969 -threeeighths;215C -threegujarati;0AE9 -threegurmukhi;0A69 -threehackarabic;0663 -threehangzhou;3023 -threeideographicparen;3222 -threeinferior;2083 -threemonospace;FF13 -threenumeratorbengali;09F6 -threeoldstyle;F733 -threeparen;2476 -threeperiod;248A -threepersian;06F3 -threequarters;00BE -threequartersemdash;F6DE -threeroman;2172 -threesuperior;00B3 -threethai;0E53 -thzsquare;3394 -tihiragana;3061 -tikatakana;30C1 -tikatakanahalfwidth;FF81 -tikeutacirclekorean;3270 -tikeutaparenkorean;3210 -tikeutcirclekorean;3262 -tikeutkorean;3137 -tikeutparenkorean;3202 -tilde;02DC -tildebelowcmb;0330 -tildecmb;0303 -tildecomb;0303 -tildedoublecmb;0360 -tildeoperator;223C -tildeoverlaycmb;0334 -tildeverticalcmb;033E -timescircle;2297 -tipehahebrew;0596 -tipehalefthebrew;0596 -tippigurmukhi;0A70 -titlocyrilliccmb;0483 -tiwnarmenian;057F -tlinebelow;1E6F -tmonospace;FF54 -toarmenian;0569 -tohiragana;3068 -tokatakana;30C8 -tokatakanahalfwidth;FF84 -tonebarextrahighmod;02E5 -tonebarextralowmod;02E9 -tonebarhighmod;02E6 -tonebarlowmod;02E8 -tonebarmidmod;02E7 -tonefive;01BD -tonesix;0185 -tonetwo;01A8 -tonos;0384 -tonsquare;3327 -topatakthai;0E0F -tortoiseshellbracketleft;3014 -tortoiseshellbracketleftsmall;FE5D -tortoiseshellbracketleftvertical;FE39 -tortoiseshellbracketright;3015 -tortoiseshellbracketrightsmall;FE5E -tortoiseshellbracketrightvertical;FE3A -totaothai;0E15 -tpalatalhook;01AB -tparen;24AF -trademark;2122 -trademarksans;F8EA -trademarkserif;F6DB -tretroflexhook;0288 -triagdn;25BC -triaglf;25C4 -triagrt;25BA -triagup;25B2 -ts;02A6 -tsadi;05E6 -tsadidagesh;FB46 -tsadidageshhebrew;FB46 -tsadihebrew;05E6 -tsecyrillic;0446 -tsere;05B5 -tsere12;05B5 -tsere1e;05B5 -tsere2b;05B5 -tserehebrew;05B5 -tserenarrowhebrew;05B5 -tserequarterhebrew;05B5 -tserewidehebrew;05B5 -tshecyrillic;045B -tsuperior;F6F3 -ttabengali;099F -ttadeva;091F -ttagujarati;0A9F -ttagurmukhi;0A1F -tteharabic;0679 -ttehfinalarabic;FB67 -ttehinitialarabic;FB68 -ttehmedialarabic;FB69 -tthabengali;09A0 -tthadeva;0920 -tthagujarati;0AA0 -tthagurmukhi;0A20 -tturned;0287 -tuhiragana;3064 -tukatakana;30C4 -tukatakanahalfwidth;FF82 -tusmallhiragana;3063 -tusmallkatakana;30C3 -tusmallkatakanahalfwidth;FF6F -twelvecircle;246B -twelveparen;247F -twelveperiod;2493 -twelveroman;217B -twentycircle;2473 -twentyhangzhou;5344 -twentyparen;2487 -twentyperiod;249B -two;0032 -twoarabic;0662 -twobengali;09E8 -twocircle;2461 -twocircleinversesansserif;278B -twodeva;0968 -twodotenleader;2025 -twodotleader;2025 -twodotleadervertical;FE30 -twogujarati;0AE8 -twogurmukhi;0A68 -twohackarabic;0662 -twohangzhou;3022 -twoideographicparen;3221 -twoinferior;2082 -twomonospace;FF12 -twonumeratorbengali;09F5 -twooldstyle;F732 -twoparen;2475 -twoperiod;2489 -twopersian;06F2 -tworoman;2171 -twostroke;01BB -twosuperior;00B2 -twothai;0E52 -twothirds;2154 -u;0075 -uacute;00FA -ubar;0289 -ubengali;0989 -ubopomofo;3128 -ubreve;016D -ucaron;01D4 -ucircle;24E4 -ucircumflex;00FB -ucircumflexbelow;1E77 -ucyrillic;0443 -udattadeva;0951 -udblacute;0171 -udblgrave;0215 -udeva;0909 -udieresis;00FC -udieresisacute;01D8 -udieresisbelow;1E73 -udieresiscaron;01DA -udieresiscyrillic;04F1 -udieresisgrave;01DC -udieresismacron;01D6 -udotbelow;1EE5 -ugrave;00F9 -ugujarati;0A89 -ugurmukhi;0A09 -uhiragana;3046 -uhookabove;1EE7 -uhorn;01B0 -uhornacute;1EE9 -uhorndotbelow;1EF1 -uhorngrave;1EEB -uhornhookabove;1EED -uhorntilde;1EEF -uhungarumlaut;0171 -uhungarumlautcyrillic;04F3 -uinvertedbreve;0217 -ukatakana;30A6 -ukatakanahalfwidth;FF73 -ukcyrillic;0479 -ukorean;315C -umacron;016B -umacroncyrillic;04EF -umacrondieresis;1E7B -umatragurmukhi;0A41 -umonospace;FF55 -underscore;005F -underscoredbl;2017 -underscoremonospace;FF3F -underscorevertical;FE33 -underscorewavy;FE4F -union;222A -universal;2200 -uogonek;0173 -uparen;24B0 -upblock;2580 -upperdothebrew;05C4 -upsilon;03C5 -upsilondieresis;03CB -upsilondieresistonos;03B0 -upsilonlatin;028A -upsilontonos;03CD -uptackbelowcmb;031D -uptackmod;02D4 -uragurmukhi;0A73 -uring;016F -ushortcyrillic;045E -usmallhiragana;3045 -usmallkatakana;30A5 -usmallkatakanahalfwidth;FF69 -ustraightcyrillic;04AF -ustraightstrokecyrillic;04B1 -utilde;0169 -utildeacute;1E79 -utildebelow;1E75 -uubengali;098A -uudeva;090A -uugujarati;0A8A -uugurmukhi;0A0A -uumatragurmukhi;0A42 -uuvowelsignbengali;09C2 -uuvowelsigndeva;0942 -uuvowelsigngujarati;0AC2 -uvowelsignbengali;09C1 -uvowelsigndeva;0941 -uvowelsigngujarati;0AC1 -v;0076 -vadeva;0935 -vagujarati;0AB5 -vagurmukhi;0A35 -vakatakana;30F7 -vav;05D5 -vavdagesh;FB35 -vavdagesh65;FB35 -vavdageshhebrew;FB35 -vavhebrew;05D5 -vavholam;FB4B -vavholamhebrew;FB4B -vavvavhebrew;05F0 -vavyodhebrew;05F1 -vcircle;24E5 -vdotbelow;1E7F -vecyrillic;0432 -veharabic;06A4 -vehfinalarabic;FB6B -vehinitialarabic;FB6C -vehmedialarabic;FB6D -vekatakana;30F9 -venus;2640 -verticalbar;007C -verticallineabovecmb;030D -verticallinebelowcmb;0329 -verticallinelowmod;02CC -verticallinemod;02C8 -vewarmenian;057E -vhook;028B -vikatakana;30F8 -viramabengali;09CD -viramadeva;094D -viramagujarati;0ACD -visargabengali;0983 -visargadeva;0903 -visargagujarati;0A83 -vmonospace;FF56 -voarmenian;0578 -voicediterationhiragana;309E -voicediterationkatakana;30FE -voicedmarkkana;309B -voicedmarkkanahalfwidth;FF9E -vokatakana;30FA -vparen;24B1 -vtilde;1E7D -vturned;028C -vuhiragana;3094 -vukatakana;30F4 -w;0077 -wacute;1E83 -waekorean;3159 -wahiragana;308F -wakatakana;30EF -wakatakanahalfwidth;FF9C -wakorean;3158 -wasmallhiragana;308E -wasmallkatakana;30EE -wattosquare;3357 -wavedash;301C -wavyunderscorevertical;FE34 -wawarabic;0648 -wawfinalarabic;FEEE -wawhamzaabovearabic;0624 -wawhamzaabovefinalarabic;FE86 -wbsquare;33DD -wcircle;24E6 -wcircumflex;0175 -wdieresis;1E85 -wdotaccent;1E87 -wdotbelow;1E89 -wehiragana;3091 -weierstrass;2118 -wekatakana;30F1 -wekorean;315E -weokorean;315D -wgrave;1E81 -whitebullet;25E6 -whitecircle;25CB -whitecircleinverse;25D9 -whitecornerbracketleft;300E -whitecornerbracketleftvertical;FE43 -whitecornerbracketright;300F -whitecornerbracketrightvertical;FE44 -whitediamond;25C7 -whitediamondcontainingblacksmalldiamond;25C8 -whitedownpointingsmalltriangle;25BF -whitedownpointingtriangle;25BD -whiteleftpointingsmalltriangle;25C3 -whiteleftpointingtriangle;25C1 -whitelenticularbracketleft;3016 -whitelenticularbracketright;3017 -whiterightpointingsmalltriangle;25B9 -whiterightpointingtriangle;25B7 -whitesmallsquare;25AB -whitesmilingface;263A -whitesquare;25A1 -whitestar;2606 -whitetelephone;260F -whitetortoiseshellbracketleft;3018 -whitetortoiseshellbracketright;3019 -whiteuppointingsmalltriangle;25B5 -whiteuppointingtriangle;25B3 -wihiragana;3090 -wikatakana;30F0 -wikorean;315F -wmonospace;FF57 -wohiragana;3092 -wokatakana;30F2 -wokatakanahalfwidth;FF66 -won;20A9 -wonmonospace;FFE6 -wowaenthai;0E27 -wparen;24B2 -wring;1E98 -wsuperior;02B7 -wturned;028D -wynn;01BF -x;0078 -xabovecmb;033D -xbopomofo;3112 -xcircle;24E7 -xdieresis;1E8D -xdotaccent;1E8B -xeharmenian;056D -xi;03BE -xmonospace;FF58 -xparen;24B3 -xsuperior;02E3 -y;0079 -yaadosquare;334E -yabengali;09AF -yacute;00FD -yadeva;092F -yaekorean;3152 -yagujarati;0AAF -yagurmukhi;0A2F -yahiragana;3084 -yakatakana;30E4 -yakatakanahalfwidth;FF94 -yakorean;3151 -yamakkanthai;0E4E -yasmallhiragana;3083 -yasmallkatakana;30E3 -yasmallkatakanahalfwidth;FF6C -yatcyrillic;0463 -ycircle;24E8 -ycircumflex;0177 -ydieresis;00FF -ydotaccent;1E8F -ydotbelow;1EF5 -yeharabic;064A -yehbarreearabic;06D2 -yehbarreefinalarabic;FBAF -yehfinalarabic;FEF2 -yehhamzaabovearabic;0626 -yehhamzaabovefinalarabic;FE8A -yehhamzaaboveinitialarabic;FE8B -yehhamzaabovemedialarabic;FE8C -yehinitialarabic;FEF3 -yehmedialarabic;FEF4 -yehmeeminitialarabic;FCDD -yehmeemisolatedarabic;FC58 -yehnoonfinalarabic;FC94 -yehthreedotsbelowarabic;06D1 -yekorean;3156 -yen;00A5 -yenmonospace;FFE5 -yeokorean;3155 -yeorinhieuhkorean;3186 -yerahbenyomohebrew;05AA -yerahbenyomolefthebrew;05AA -yericyrillic;044B -yerudieresiscyrillic;04F9 -yesieungkorean;3181 -yesieungpansioskorean;3183 -yesieungsioskorean;3182 -yetivhebrew;059A -ygrave;1EF3 -yhook;01B4 -yhookabove;1EF7 -yiarmenian;0575 -yicyrillic;0457 -yikorean;3162 -yinyang;262F -yiwnarmenian;0582 -ymonospace;FF59 -yod;05D9 -yoddagesh;FB39 -yoddageshhebrew;FB39 -yodhebrew;05D9 -yodyodhebrew;05F2 -yodyodpatahhebrew;FB1F -yohiragana;3088 -yoikorean;3189 -yokatakana;30E8 -yokatakanahalfwidth;FF96 -yokorean;315B -yosmallhiragana;3087 -yosmallkatakana;30E7 -yosmallkatakanahalfwidth;FF6E -yotgreek;03F3 -yoyaekorean;3188 -yoyakorean;3187 -yoyakthai;0E22 -yoyingthai;0E0D -yparen;24B4 -ypogegrammeni;037A -ypogegrammenigreekcmb;0345 -yr;01A6 -yring;1E99 -ysuperior;02B8 -ytilde;1EF9 -yturned;028E -yuhiragana;3086 -yuikorean;318C -yukatakana;30E6 -yukatakanahalfwidth;FF95 -yukorean;3160 -yusbigcyrillic;046B -yusbigiotifiedcyrillic;046D -yuslittlecyrillic;0467 -yuslittleiotifiedcyrillic;0469 -yusmallhiragana;3085 -yusmallkatakana;30E5 -yusmallkatakanahalfwidth;FF6D -yuyekorean;318B -yuyeokorean;318A -yyabengali;09DF -yyadeva;095F -z;007A -zaarmenian;0566 -zacute;017A -zadeva;095B -zagurmukhi;0A5B -zaharabic;0638 -zahfinalarabic;FEC6 -zahinitialarabic;FEC7 -zahiragana;3056 -zahmedialarabic;FEC8 -zainarabic;0632 -zainfinalarabic;FEB0 -zakatakana;30B6 -zaqefgadolhebrew;0595 -zaqefqatanhebrew;0594 -zarqahebrew;0598 -zayin;05D6 -zayindagesh;FB36 -zayindageshhebrew;FB36 -zayinhebrew;05D6 -zbopomofo;3117 -zcaron;017E -zcircle;24E9 -zcircumflex;1E91 -zcurl;0291 -zdot;017C -zdotaccent;017C -zdotbelow;1E93 -zecyrillic;0437 -zedescendercyrillic;0499 -zedieresiscyrillic;04DF -zehiragana;305C -zekatakana;30BC -zero;0030 -zeroarabic;0660 -zerobengali;09E6 -zerodeva;0966 -zerogujarati;0AE6 -zerogurmukhi;0A66 -zerohackarabic;0660 -zeroinferior;2080 -zeromonospace;FF10 -zerooldstyle;F730 -zeropersian;06F0 -zerosuperior;2070 -zerothai;0E50 -zerowidthjoiner;FEFF -zerowidthnonjoiner;200C -zerowidthspace;200B -zeta;03B6 -zhbopomofo;3113 -zhearmenian;056A -zhebrevecyrillic;04C2 -zhecyrillic;0436 -zhedescendercyrillic;0497 -zhedieresiscyrillic;04DD -zihiragana;3058 -zikatakana;30B8 -zinorhebrew;05AE -zlinebelow;1E95 -zmonospace;FF5A -zohiragana;305E -zokatakana;30BE -zparen;24B5 -zretroflexhook;0290 -zstroke;01B6 -zuhiragana;305A -zukatakana;30BA -# END -""" - - -_aglfnText = """\ -# ----------------------------------------------------------- -# Copyright 2002-2019 Adobe (http://www.adobe.com/). -# -# 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 Adobe nor the names of its contributors -# may be used to endorse or promote products derived from this -# software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND -# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# ----------------------------------------------------------- -# Name: Adobe Glyph List For New Fonts -# Table version: 1.7 -# Date: November 6, 2008 -# URL: https://github.com/adobe-type-tools/agl-aglfn -# -# Description: -# -# AGLFN (Adobe Glyph List For New Fonts) provides a list of base glyph -# names that are recommended for new fonts, which are compatible with -# the AGL (Adobe Glyph List) Specification, and which should be used -# as described in Section 6 of that document. AGLFN comprises the set -# of glyph names from AGL that map via the AGL Specification rules to -# the semantically correct UV (Unicode Value). For example, "Asmall" -# is omitted because AGL maps this glyph name to the PUA (Private Use -# Area) value U+F761, rather than to the UV that maps from the glyph -# name "A." Also omitted is "ffi," because AGL maps this to the -# Alphabetic Presentation Forms value U+FB03, rather than decomposing -# it into the following sequence of three UVs: U+0066, U+0066, and -# U+0069. The name "arrowvertex" has been omitted because this glyph -# now has a real UV, and AGL is now incorrect in mapping it to the PUA -# value U+F8E6. If you do not find an appropriate name for your glyph -# in this list, then please refer to Section 6 of the AGL -# Specification. -# -# Format: three semicolon-delimited fields: -# (1) Standard UV or CUS UV--four uppercase hexadecimal digits -# (2) Glyph name--upper/lowercase letters and digits -# (3) Character names: Unicode character names for standard UVs, and -# descriptive names for CUS UVs--uppercase letters, hyphen, and -# space -# -# The records are sorted by glyph name in increasing ASCII order, -# entries with the same glyph name are sorted in decreasing priority -# order, the UVs and Unicode character names are provided for -# convenience, lines starting with "#" are comments, and blank lines -# should be ignored. -# -# Revision History: -# -# 1.7 [6 November 2008] -# - Reverted to the original 1.4 and earlier mappings for Delta, -# Omega, and mu. -# - Removed mappings for "afii" names. These should now be assigned -# "uni" names. -# - Removed mappings for "commaaccent" names. These should now be -# assigned "uni" names. -# -# 1.6 [30 January 2006] -# - Completed work intended in 1.5. -# -# 1.5 [23 November 2005] -# - Removed duplicated block at end of file. -# - Changed mappings: -# 2206;Delta;INCREMENT changed to 0394;Delta;GREEK CAPITAL LETTER DELTA -# 2126;Omega;OHM SIGN changed to 03A9;Omega;GREEK CAPITAL LETTER OMEGA -# 03BC;mu;MICRO SIGN changed to 03BC;mu;GREEK SMALL LETTER MU -# - Corrected statement above about why "ffi" is omitted. -# -# 1.4 [24 September 2003] -# - Changed version to 1.4, to avoid confusion with the AGL 1.3. -# - Fixed spelling errors in the header. -# - Fully removed "arrowvertex," as it is mapped only to a PUA Unicode -# value in some fonts. -# -# 1.1 [17 April 2003] -# - Renamed [Tt]cedilla back to [Tt]commaaccent. -# -# 1.0 [31 January 2003] -# - Original version. -# - Derived from the AGLv1.2 by: -# removing the PUA area codes; -# removing duplicate Unicode mappings; and -# renaming "tcommaaccent" to "tcedilla" and "Tcommaaccent" to "Tcedilla" -# -0041;A;LATIN CAPITAL LETTER A -00C6;AE;LATIN CAPITAL LETTER AE -01FC;AEacute;LATIN CAPITAL LETTER AE WITH ACUTE -00C1;Aacute;LATIN CAPITAL LETTER A WITH ACUTE -0102;Abreve;LATIN CAPITAL LETTER A WITH BREVE -00C2;Acircumflex;LATIN CAPITAL LETTER A WITH CIRCUMFLEX -00C4;Adieresis;LATIN CAPITAL LETTER A WITH DIAERESIS -00C0;Agrave;LATIN CAPITAL LETTER A WITH GRAVE -0391;Alpha;GREEK CAPITAL LETTER ALPHA -0386;Alphatonos;GREEK CAPITAL LETTER ALPHA WITH TONOS -0100;Amacron;LATIN CAPITAL LETTER A WITH MACRON -0104;Aogonek;LATIN CAPITAL LETTER A WITH OGONEK -00C5;Aring;LATIN CAPITAL LETTER A WITH RING ABOVE -01FA;Aringacute;LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE -00C3;Atilde;LATIN CAPITAL LETTER A WITH TILDE -0042;B;LATIN CAPITAL LETTER B -0392;Beta;GREEK CAPITAL LETTER BETA -0043;C;LATIN CAPITAL LETTER C -0106;Cacute;LATIN CAPITAL LETTER C WITH ACUTE -010C;Ccaron;LATIN CAPITAL LETTER C WITH CARON -00C7;Ccedilla;LATIN CAPITAL LETTER C WITH CEDILLA -0108;Ccircumflex;LATIN CAPITAL LETTER C WITH CIRCUMFLEX -010A;Cdotaccent;LATIN CAPITAL LETTER C WITH DOT ABOVE -03A7;Chi;GREEK CAPITAL LETTER CHI -0044;D;LATIN CAPITAL LETTER D -010E;Dcaron;LATIN CAPITAL LETTER D WITH CARON -0110;Dcroat;LATIN CAPITAL LETTER D WITH STROKE -2206;Delta;INCREMENT -0045;E;LATIN CAPITAL LETTER E -00C9;Eacute;LATIN CAPITAL LETTER E WITH ACUTE -0114;Ebreve;LATIN CAPITAL LETTER E WITH BREVE -011A;Ecaron;LATIN CAPITAL LETTER E WITH CARON -00CA;Ecircumflex;LATIN CAPITAL LETTER E WITH CIRCUMFLEX -00CB;Edieresis;LATIN CAPITAL LETTER E WITH DIAERESIS -0116;Edotaccent;LATIN CAPITAL LETTER E WITH DOT ABOVE -00C8;Egrave;LATIN CAPITAL LETTER E WITH GRAVE -0112;Emacron;LATIN CAPITAL LETTER E WITH MACRON -014A;Eng;LATIN CAPITAL LETTER ENG -0118;Eogonek;LATIN CAPITAL LETTER E WITH OGONEK -0395;Epsilon;GREEK CAPITAL LETTER EPSILON -0388;Epsilontonos;GREEK CAPITAL LETTER EPSILON WITH TONOS -0397;Eta;GREEK CAPITAL LETTER ETA -0389;Etatonos;GREEK CAPITAL LETTER ETA WITH TONOS -00D0;Eth;LATIN CAPITAL LETTER ETH -20AC;Euro;EURO SIGN -0046;F;LATIN CAPITAL LETTER F -0047;G;LATIN CAPITAL LETTER G -0393;Gamma;GREEK CAPITAL LETTER GAMMA -011E;Gbreve;LATIN CAPITAL LETTER G WITH BREVE -01E6;Gcaron;LATIN CAPITAL LETTER G WITH CARON -011C;Gcircumflex;LATIN CAPITAL LETTER G WITH CIRCUMFLEX -0120;Gdotaccent;LATIN CAPITAL LETTER G WITH DOT ABOVE -0048;H;LATIN CAPITAL LETTER H -25CF;H18533;BLACK CIRCLE -25AA;H18543;BLACK SMALL SQUARE -25AB;H18551;WHITE SMALL SQUARE -25A1;H22073;WHITE SQUARE -0126;Hbar;LATIN CAPITAL LETTER H WITH STROKE -0124;Hcircumflex;LATIN CAPITAL LETTER H WITH CIRCUMFLEX -0049;I;LATIN CAPITAL LETTER I -0132;IJ;LATIN CAPITAL LIGATURE IJ -00CD;Iacute;LATIN CAPITAL LETTER I WITH ACUTE -012C;Ibreve;LATIN CAPITAL LETTER I WITH BREVE -00CE;Icircumflex;LATIN CAPITAL LETTER I WITH CIRCUMFLEX -00CF;Idieresis;LATIN CAPITAL LETTER I WITH DIAERESIS -0130;Idotaccent;LATIN CAPITAL LETTER I WITH DOT ABOVE -2111;Ifraktur;BLACK-LETTER CAPITAL I -00CC;Igrave;LATIN CAPITAL LETTER I WITH GRAVE -012A;Imacron;LATIN CAPITAL LETTER I WITH MACRON -012E;Iogonek;LATIN CAPITAL LETTER I WITH OGONEK -0399;Iota;GREEK CAPITAL LETTER IOTA -03AA;Iotadieresis;GREEK CAPITAL LETTER IOTA WITH DIALYTIKA -038A;Iotatonos;GREEK CAPITAL LETTER IOTA WITH TONOS -0128;Itilde;LATIN CAPITAL LETTER I WITH TILDE -004A;J;LATIN CAPITAL LETTER J -0134;Jcircumflex;LATIN CAPITAL LETTER J WITH CIRCUMFLEX -004B;K;LATIN CAPITAL LETTER K -039A;Kappa;GREEK CAPITAL LETTER KAPPA -004C;L;LATIN CAPITAL LETTER L -0139;Lacute;LATIN CAPITAL LETTER L WITH ACUTE -039B;Lambda;GREEK CAPITAL LETTER LAMDA -013D;Lcaron;LATIN CAPITAL LETTER L WITH CARON -013F;Ldot;LATIN CAPITAL LETTER L WITH MIDDLE DOT -0141;Lslash;LATIN CAPITAL LETTER L WITH STROKE -004D;M;LATIN CAPITAL LETTER M -039C;Mu;GREEK CAPITAL LETTER MU -004E;N;LATIN CAPITAL LETTER N -0143;Nacute;LATIN CAPITAL LETTER N WITH ACUTE -0147;Ncaron;LATIN CAPITAL LETTER N WITH CARON -00D1;Ntilde;LATIN CAPITAL LETTER N WITH TILDE -039D;Nu;GREEK CAPITAL LETTER NU -004F;O;LATIN CAPITAL LETTER O -0152;OE;LATIN CAPITAL LIGATURE OE -00D3;Oacute;LATIN CAPITAL LETTER O WITH ACUTE -014E;Obreve;LATIN CAPITAL LETTER O WITH BREVE -00D4;Ocircumflex;LATIN CAPITAL LETTER O WITH CIRCUMFLEX -00D6;Odieresis;LATIN CAPITAL LETTER O WITH DIAERESIS -00D2;Ograve;LATIN CAPITAL LETTER O WITH GRAVE -01A0;Ohorn;LATIN CAPITAL LETTER O WITH HORN -0150;Ohungarumlaut;LATIN CAPITAL LETTER O WITH DOUBLE ACUTE -014C;Omacron;LATIN CAPITAL LETTER O WITH MACRON -2126;Omega;OHM SIGN -038F;Omegatonos;GREEK CAPITAL LETTER OMEGA WITH TONOS -039F;Omicron;GREEK CAPITAL LETTER OMICRON -038C;Omicrontonos;GREEK CAPITAL LETTER OMICRON WITH TONOS -00D8;Oslash;LATIN CAPITAL LETTER O WITH STROKE -01FE;Oslashacute;LATIN CAPITAL LETTER O WITH STROKE AND ACUTE -00D5;Otilde;LATIN CAPITAL LETTER O WITH TILDE -0050;P;LATIN CAPITAL LETTER P -03A6;Phi;GREEK CAPITAL LETTER PHI -03A0;Pi;GREEK CAPITAL LETTER PI -03A8;Psi;GREEK CAPITAL LETTER PSI -0051;Q;LATIN CAPITAL LETTER Q -0052;R;LATIN CAPITAL LETTER R -0154;Racute;LATIN CAPITAL LETTER R WITH ACUTE -0158;Rcaron;LATIN CAPITAL LETTER R WITH CARON -211C;Rfraktur;BLACK-LETTER CAPITAL R -03A1;Rho;GREEK CAPITAL LETTER RHO -0053;S;LATIN CAPITAL LETTER S -250C;SF010000;BOX DRAWINGS LIGHT DOWN AND RIGHT -2514;SF020000;BOX DRAWINGS LIGHT UP AND RIGHT -2510;SF030000;BOX DRAWINGS LIGHT DOWN AND LEFT -2518;SF040000;BOX DRAWINGS LIGHT UP AND LEFT -253C;SF050000;BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL -252C;SF060000;BOX DRAWINGS LIGHT DOWN AND HORIZONTAL -2534;SF070000;BOX DRAWINGS LIGHT UP AND HORIZONTAL -251C;SF080000;BOX DRAWINGS LIGHT VERTICAL AND RIGHT -2524;SF090000;BOX DRAWINGS LIGHT VERTICAL AND LEFT -2500;SF100000;BOX DRAWINGS LIGHT HORIZONTAL -2502;SF110000;BOX DRAWINGS LIGHT VERTICAL -2561;SF190000;BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE -2562;SF200000;BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE -2556;SF210000;BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE -2555;SF220000;BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE -2563;SF230000;BOX DRAWINGS DOUBLE VERTICAL AND LEFT -2551;SF240000;BOX DRAWINGS DOUBLE VERTICAL -2557;SF250000;BOX DRAWINGS DOUBLE DOWN AND LEFT -255D;SF260000;BOX DRAWINGS DOUBLE UP AND LEFT -255C;SF270000;BOX DRAWINGS UP DOUBLE AND LEFT SINGLE -255B;SF280000;BOX DRAWINGS UP SINGLE AND LEFT DOUBLE -255E;SF360000;BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE -255F;SF370000;BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE -255A;SF380000;BOX DRAWINGS DOUBLE UP AND RIGHT -2554;SF390000;BOX DRAWINGS DOUBLE DOWN AND RIGHT -2569;SF400000;BOX DRAWINGS DOUBLE UP AND HORIZONTAL -2566;SF410000;BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL -2560;SF420000;BOX DRAWINGS DOUBLE VERTICAL AND RIGHT -2550;SF430000;BOX DRAWINGS DOUBLE HORIZONTAL -256C;SF440000;BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL -2567;SF450000;BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE -2568;SF460000;BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE -2564;SF470000;BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE -2565;SF480000;BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE -2559;SF490000;BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE -2558;SF500000;BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE -2552;SF510000;BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE -2553;SF520000;BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE -256B;SF530000;BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE -256A;SF540000;BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE -015A;Sacute;LATIN CAPITAL LETTER S WITH ACUTE -0160;Scaron;LATIN CAPITAL LETTER S WITH CARON -015E;Scedilla;LATIN CAPITAL LETTER S WITH CEDILLA -015C;Scircumflex;LATIN CAPITAL LETTER S WITH CIRCUMFLEX -03A3;Sigma;GREEK CAPITAL LETTER SIGMA -0054;T;LATIN CAPITAL LETTER T -03A4;Tau;GREEK CAPITAL LETTER TAU -0166;Tbar;LATIN CAPITAL LETTER T WITH STROKE -0164;Tcaron;LATIN CAPITAL LETTER T WITH CARON -0398;Theta;GREEK CAPITAL LETTER THETA -00DE;Thorn;LATIN CAPITAL LETTER THORN -0055;U;LATIN CAPITAL LETTER U -00DA;Uacute;LATIN CAPITAL LETTER U WITH ACUTE -016C;Ubreve;LATIN CAPITAL LETTER U WITH BREVE -00DB;Ucircumflex;LATIN CAPITAL LETTER U WITH CIRCUMFLEX -00DC;Udieresis;LATIN CAPITAL LETTER U WITH DIAERESIS -00D9;Ugrave;LATIN CAPITAL LETTER U WITH GRAVE -01AF;Uhorn;LATIN CAPITAL LETTER U WITH HORN -0170;Uhungarumlaut;LATIN CAPITAL LETTER U WITH DOUBLE ACUTE -016A;Umacron;LATIN CAPITAL LETTER U WITH MACRON -0172;Uogonek;LATIN CAPITAL LETTER U WITH OGONEK -03A5;Upsilon;GREEK CAPITAL LETTER UPSILON -03D2;Upsilon1;GREEK UPSILON WITH HOOK SYMBOL -03AB;Upsilondieresis;GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA -038E;Upsilontonos;GREEK CAPITAL LETTER UPSILON WITH TONOS -016E;Uring;LATIN CAPITAL LETTER U WITH RING ABOVE -0168;Utilde;LATIN CAPITAL LETTER U WITH TILDE -0056;V;LATIN CAPITAL LETTER V -0057;W;LATIN CAPITAL LETTER W -1E82;Wacute;LATIN CAPITAL LETTER W WITH ACUTE -0174;Wcircumflex;LATIN CAPITAL LETTER W WITH CIRCUMFLEX -1E84;Wdieresis;LATIN CAPITAL LETTER W WITH DIAERESIS -1E80;Wgrave;LATIN CAPITAL LETTER W WITH GRAVE -0058;X;LATIN CAPITAL LETTER X -039E;Xi;GREEK CAPITAL LETTER XI -0059;Y;LATIN CAPITAL LETTER Y -00DD;Yacute;LATIN CAPITAL LETTER Y WITH ACUTE -0176;Ycircumflex;LATIN CAPITAL LETTER Y WITH CIRCUMFLEX -0178;Ydieresis;LATIN CAPITAL LETTER Y WITH DIAERESIS -1EF2;Ygrave;LATIN CAPITAL LETTER Y WITH GRAVE -005A;Z;LATIN CAPITAL LETTER Z -0179;Zacute;LATIN CAPITAL LETTER Z WITH ACUTE -017D;Zcaron;LATIN CAPITAL LETTER Z WITH CARON -017B;Zdotaccent;LATIN CAPITAL LETTER Z WITH DOT ABOVE -0396;Zeta;GREEK CAPITAL LETTER ZETA -0061;a;LATIN SMALL LETTER A -00E1;aacute;LATIN SMALL LETTER A WITH ACUTE -0103;abreve;LATIN SMALL LETTER A WITH BREVE -00E2;acircumflex;LATIN SMALL LETTER A WITH CIRCUMFLEX -00B4;acute;ACUTE ACCENT -0301;acutecomb;COMBINING ACUTE ACCENT -00E4;adieresis;LATIN SMALL LETTER A WITH DIAERESIS -00E6;ae;LATIN SMALL LETTER AE -01FD;aeacute;LATIN SMALL LETTER AE WITH ACUTE -00E0;agrave;LATIN SMALL LETTER A WITH GRAVE -2135;aleph;ALEF SYMBOL -03B1;alpha;GREEK SMALL LETTER ALPHA -03AC;alphatonos;GREEK SMALL LETTER ALPHA WITH TONOS -0101;amacron;LATIN SMALL LETTER A WITH MACRON -0026;ampersand;AMPERSAND -2220;angle;ANGLE -2329;angleleft;LEFT-POINTING ANGLE BRACKET -232A;angleright;RIGHT-POINTING ANGLE BRACKET -0387;anoteleia;GREEK ANO TELEIA -0105;aogonek;LATIN SMALL LETTER A WITH OGONEK -2248;approxequal;ALMOST EQUAL TO -00E5;aring;LATIN SMALL LETTER A WITH RING ABOVE -01FB;aringacute;LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE -2194;arrowboth;LEFT RIGHT ARROW -21D4;arrowdblboth;LEFT RIGHT DOUBLE ARROW -21D3;arrowdbldown;DOWNWARDS DOUBLE ARROW -21D0;arrowdblleft;LEFTWARDS DOUBLE ARROW -21D2;arrowdblright;RIGHTWARDS DOUBLE ARROW -21D1;arrowdblup;UPWARDS DOUBLE ARROW -2193;arrowdown;DOWNWARDS ARROW -2190;arrowleft;LEFTWARDS ARROW -2192;arrowright;RIGHTWARDS ARROW -2191;arrowup;UPWARDS ARROW -2195;arrowupdn;UP DOWN ARROW -21A8;arrowupdnbse;UP DOWN ARROW WITH BASE -005E;asciicircum;CIRCUMFLEX ACCENT -007E;asciitilde;TILDE -002A;asterisk;ASTERISK -2217;asteriskmath;ASTERISK OPERATOR -0040;at;COMMERCIAL AT -00E3;atilde;LATIN SMALL LETTER A WITH TILDE -0062;b;LATIN SMALL LETTER B -005C;backslash;REVERSE SOLIDUS -007C;bar;VERTICAL LINE -03B2;beta;GREEK SMALL LETTER BETA -2588;block;FULL BLOCK -007B;braceleft;LEFT CURLY BRACKET -007D;braceright;RIGHT CURLY BRACKET -005B;bracketleft;LEFT SQUARE BRACKET -005D;bracketright;RIGHT SQUARE BRACKET -02D8;breve;BREVE -00A6;brokenbar;BROKEN BAR -2022;bullet;BULLET -0063;c;LATIN SMALL LETTER C -0107;cacute;LATIN SMALL LETTER C WITH ACUTE -02C7;caron;CARON -21B5;carriagereturn;DOWNWARDS ARROW WITH CORNER LEFTWARDS -010D;ccaron;LATIN SMALL LETTER C WITH CARON -00E7;ccedilla;LATIN SMALL LETTER C WITH CEDILLA -0109;ccircumflex;LATIN SMALL LETTER C WITH CIRCUMFLEX -010B;cdotaccent;LATIN SMALL LETTER C WITH DOT ABOVE -00B8;cedilla;CEDILLA -00A2;cent;CENT SIGN -03C7;chi;GREEK SMALL LETTER CHI -25CB;circle;WHITE CIRCLE -2297;circlemultiply;CIRCLED TIMES -2295;circleplus;CIRCLED PLUS -02C6;circumflex;MODIFIER LETTER CIRCUMFLEX ACCENT -2663;club;BLACK CLUB SUIT -003A;colon;COLON -20A1;colonmonetary;COLON SIGN -002C;comma;COMMA -2245;congruent;APPROXIMATELY EQUAL TO -00A9;copyright;COPYRIGHT SIGN -00A4;currency;CURRENCY SIGN -0064;d;LATIN SMALL LETTER D -2020;dagger;DAGGER -2021;daggerdbl;DOUBLE DAGGER -010F;dcaron;LATIN SMALL LETTER D WITH CARON -0111;dcroat;LATIN SMALL LETTER D WITH STROKE -00B0;degree;DEGREE SIGN -03B4;delta;GREEK SMALL LETTER DELTA -2666;diamond;BLACK DIAMOND SUIT -00A8;dieresis;DIAERESIS -0385;dieresistonos;GREEK DIALYTIKA TONOS -00F7;divide;DIVISION SIGN -2593;dkshade;DARK SHADE -2584;dnblock;LOWER HALF BLOCK -0024;dollar;DOLLAR SIGN -20AB;dong;DONG SIGN -02D9;dotaccent;DOT ABOVE -0323;dotbelowcomb;COMBINING DOT BELOW -0131;dotlessi;LATIN SMALL LETTER DOTLESS I -22C5;dotmath;DOT OPERATOR -0065;e;LATIN SMALL LETTER E -00E9;eacute;LATIN SMALL LETTER E WITH ACUTE -0115;ebreve;LATIN SMALL LETTER E WITH BREVE -011B;ecaron;LATIN SMALL LETTER E WITH CARON -00EA;ecircumflex;LATIN SMALL LETTER E WITH CIRCUMFLEX -00EB;edieresis;LATIN SMALL LETTER E WITH DIAERESIS -0117;edotaccent;LATIN SMALL LETTER E WITH DOT ABOVE -00E8;egrave;LATIN SMALL LETTER E WITH GRAVE -0038;eight;DIGIT EIGHT -2208;element;ELEMENT OF -2026;ellipsis;HORIZONTAL ELLIPSIS -0113;emacron;LATIN SMALL LETTER E WITH MACRON -2014;emdash;EM DASH -2205;emptyset;EMPTY SET -2013;endash;EN DASH -014B;eng;LATIN SMALL LETTER ENG -0119;eogonek;LATIN SMALL LETTER E WITH OGONEK -03B5;epsilon;GREEK SMALL LETTER EPSILON -03AD;epsilontonos;GREEK SMALL LETTER EPSILON WITH TONOS -003D;equal;EQUALS SIGN -2261;equivalence;IDENTICAL TO -212E;estimated;ESTIMATED SYMBOL -03B7;eta;GREEK SMALL LETTER ETA -03AE;etatonos;GREEK SMALL LETTER ETA WITH TONOS -00F0;eth;LATIN SMALL LETTER ETH -0021;exclam;EXCLAMATION MARK -203C;exclamdbl;DOUBLE EXCLAMATION MARK -00A1;exclamdown;INVERTED EXCLAMATION MARK -2203;existential;THERE EXISTS -0066;f;LATIN SMALL LETTER F -2640;female;FEMALE SIGN -2012;figuredash;FIGURE DASH -25A0;filledbox;BLACK SQUARE -25AC;filledrect;BLACK RECTANGLE -0035;five;DIGIT FIVE -215D;fiveeighths;VULGAR FRACTION FIVE EIGHTHS -0192;florin;LATIN SMALL LETTER F WITH HOOK -0034;four;DIGIT FOUR -2044;fraction;FRACTION SLASH -20A3;franc;FRENCH FRANC SIGN -0067;g;LATIN SMALL LETTER G -03B3;gamma;GREEK SMALL LETTER GAMMA -011F;gbreve;LATIN SMALL LETTER G WITH BREVE -01E7;gcaron;LATIN SMALL LETTER G WITH CARON -011D;gcircumflex;LATIN SMALL LETTER G WITH CIRCUMFLEX -0121;gdotaccent;LATIN SMALL LETTER G WITH DOT ABOVE -00DF;germandbls;LATIN SMALL LETTER SHARP S -2207;gradient;NABLA -0060;grave;GRAVE ACCENT -0300;gravecomb;COMBINING GRAVE ACCENT -003E;greater;GREATER-THAN SIGN -2265;greaterequal;GREATER-THAN OR EQUAL TO -00AB;guillemotleft;LEFT-POINTING DOUBLE ANGLE QUOTATION MARK -00BB;guillemotright;RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK -2039;guilsinglleft;SINGLE LEFT-POINTING ANGLE QUOTATION MARK -203A;guilsinglright;SINGLE RIGHT-POINTING ANGLE QUOTATION MARK -0068;h;LATIN SMALL LETTER H -0127;hbar;LATIN SMALL LETTER H WITH STROKE -0125;hcircumflex;LATIN SMALL LETTER H WITH CIRCUMFLEX -2665;heart;BLACK HEART SUIT -0309;hookabovecomb;COMBINING HOOK ABOVE -2302;house;HOUSE -02DD;hungarumlaut;DOUBLE ACUTE ACCENT -002D;hyphen;HYPHEN-MINUS -0069;i;LATIN SMALL LETTER I -00ED;iacute;LATIN SMALL LETTER I WITH ACUTE -012D;ibreve;LATIN SMALL LETTER I WITH BREVE -00EE;icircumflex;LATIN SMALL LETTER I WITH CIRCUMFLEX -00EF;idieresis;LATIN SMALL LETTER I WITH DIAERESIS -00EC;igrave;LATIN SMALL LETTER I WITH GRAVE -0133;ij;LATIN SMALL LIGATURE IJ -012B;imacron;LATIN SMALL LETTER I WITH MACRON -221E;infinity;INFINITY -222B;integral;INTEGRAL -2321;integralbt;BOTTOM HALF INTEGRAL -2320;integraltp;TOP HALF INTEGRAL -2229;intersection;INTERSECTION -25D8;invbullet;INVERSE BULLET -25D9;invcircle;INVERSE WHITE CIRCLE -263B;invsmileface;BLACK SMILING FACE -012F;iogonek;LATIN SMALL LETTER I WITH OGONEK -03B9;iota;GREEK SMALL LETTER IOTA -03CA;iotadieresis;GREEK SMALL LETTER IOTA WITH DIALYTIKA -0390;iotadieresistonos;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS -03AF;iotatonos;GREEK SMALL LETTER IOTA WITH TONOS -0129;itilde;LATIN SMALL LETTER I WITH TILDE -006A;j;LATIN SMALL LETTER J -0135;jcircumflex;LATIN SMALL LETTER J WITH CIRCUMFLEX -006B;k;LATIN SMALL LETTER K -03BA;kappa;GREEK SMALL LETTER KAPPA -0138;kgreenlandic;LATIN SMALL LETTER KRA -006C;l;LATIN SMALL LETTER L -013A;lacute;LATIN SMALL LETTER L WITH ACUTE -03BB;lambda;GREEK SMALL LETTER LAMDA -013E;lcaron;LATIN SMALL LETTER L WITH CARON -0140;ldot;LATIN SMALL LETTER L WITH MIDDLE DOT -003C;less;LESS-THAN SIGN -2264;lessequal;LESS-THAN OR EQUAL TO -258C;lfblock;LEFT HALF BLOCK -20A4;lira;LIRA SIGN -2227;logicaland;LOGICAL AND -00AC;logicalnot;NOT SIGN -2228;logicalor;LOGICAL OR -017F;longs;LATIN SMALL LETTER LONG S -25CA;lozenge;LOZENGE -0142;lslash;LATIN SMALL LETTER L WITH STROKE -2591;ltshade;LIGHT SHADE -006D;m;LATIN SMALL LETTER M -00AF;macron;MACRON -2642;male;MALE SIGN -2212;minus;MINUS SIGN -2032;minute;PRIME -00B5;mu;MICRO SIGN -00D7;multiply;MULTIPLICATION SIGN -266A;musicalnote;EIGHTH NOTE -266B;musicalnotedbl;BEAMED EIGHTH NOTES -006E;n;LATIN SMALL LETTER N -0144;nacute;LATIN SMALL LETTER N WITH ACUTE -0149;napostrophe;LATIN SMALL LETTER N PRECEDED BY APOSTROPHE -0148;ncaron;LATIN SMALL LETTER N WITH CARON -0039;nine;DIGIT NINE -2209;notelement;NOT AN ELEMENT OF -2260;notequal;NOT EQUAL TO -2284;notsubset;NOT A SUBSET OF -00F1;ntilde;LATIN SMALL LETTER N WITH TILDE -03BD;nu;GREEK SMALL LETTER NU -0023;numbersign;NUMBER SIGN -006F;o;LATIN SMALL LETTER O -00F3;oacute;LATIN SMALL LETTER O WITH ACUTE -014F;obreve;LATIN SMALL LETTER O WITH BREVE -00F4;ocircumflex;LATIN SMALL LETTER O WITH CIRCUMFLEX -00F6;odieresis;LATIN SMALL LETTER O WITH DIAERESIS -0153;oe;LATIN SMALL LIGATURE OE -02DB;ogonek;OGONEK -00F2;ograve;LATIN SMALL LETTER O WITH GRAVE -01A1;ohorn;LATIN SMALL LETTER O WITH HORN -0151;ohungarumlaut;LATIN SMALL LETTER O WITH DOUBLE ACUTE -014D;omacron;LATIN SMALL LETTER O WITH MACRON -03C9;omega;GREEK SMALL LETTER OMEGA -03D6;omega1;GREEK PI SYMBOL -03CE;omegatonos;GREEK SMALL LETTER OMEGA WITH TONOS -03BF;omicron;GREEK SMALL LETTER OMICRON -03CC;omicrontonos;GREEK SMALL LETTER OMICRON WITH TONOS -0031;one;DIGIT ONE -2024;onedotenleader;ONE DOT LEADER -215B;oneeighth;VULGAR FRACTION ONE EIGHTH -00BD;onehalf;VULGAR FRACTION ONE HALF -00BC;onequarter;VULGAR FRACTION ONE QUARTER -2153;onethird;VULGAR FRACTION ONE THIRD -25E6;openbullet;WHITE BULLET -00AA;ordfeminine;FEMININE ORDINAL INDICATOR -00BA;ordmasculine;MASCULINE ORDINAL INDICATOR -221F;orthogonal;RIGHT ANGLE -00F8;oslash;LATIN SMALL LETTER O WITH STROKE -01FF;oslashacute;LATIN SMALL LETTER O WITH STROKE AND ACUTE -00F5;otilde;LATIN SMALL LETTER O WITH TILDE -0070;p;LATIN SMALL LETTER P -00B6;paragraph;PILCROW SIGN -0028;parenleft;LEFT PARENTHESIS -0029;parenright;RIGHT PARENTHESIS -2202;partialdiff;PARTIAL DIFFERENTIAL -0025;percent;PERCENT SIGN -002E;period;FULL STOP -00B7;periodcentered;MIDDLE DOT -22A5;perpendicular;UP TACK -2030;perthousand;PER MILLE SIGN -20A7;peseta;PESETA SIGN -03C6;phi;GREEK SMALL LETTER PHI -03D5;phi1;GREEK PHI SYMBOL -03C0;pi;GREEK SMALL LETTER PI -002B;plus;PLUS SIGN -00B1;plusminus;PLUS-MINUS SIGN -211E;prescription;PRESCRIPTION TAKE -220F;product;N-ARY PRODUCT -2282;propersubset;SUBSET OF -2283;propersuperset;SUPERSET OF -221D;proportional;PROPORTIONAL TO -03C8;psi;GREEK SMALL LETTER PSI -0071;q;LATIN SMALL LETTER Q -003F;question;QUESTION MARK -00BF;questiondown;INVERTED QUESTION MARK -0022;quotedbl;QUOTATION MARK -201E;quotedblbase;DOUBLE LOW-9 QUOTATION MARK -201C;quotedblleft;LEFT DOUBLE QUOTATION MARK -201D;quotedblright;RIGHT DOUBLE QUOTATION MARK -2018;quoteleft;LEFT SINGLE QUOTATION MARK -201B;quotereversed;SINGLE HIGH-REVERSED-9 QUOTATION MARK -2019;quoteright;RIGHT SINGLE QUOTATION MARK -201A;quotesinglbase;SINGLE LOW-9 QUOTATION MARK -0027;quotesingle;APOSTROPHE -0072;r;LATIN SMALL LETTER R -0155;racute;LATIN SMALL LETTER R WITH ACUTE -221A;radical;SQUARE ROOT -0159;rcaron;LATIN SMALL LETTER R WITH CARON -2286;reflexsubset;SUBSET OF OR EQUAL TO -2287;reflexsuperset;SUPERSET OF OR EQUAL TO -00AE;registered;REGISTERED SIGN -2310;revlogicalnot;REVERSED NOT SIGN -03C1;rho;GREEK SMALL LETTER RHO -02DA;ring;RING ABOVE -2590;rtblock;RIGHT HALF BLOCK -0073;s;LATIN SMALL LETTER S -015B;sacute;LATIN SMALL LETTER S WITH ACUTE -0161;scaron;LATIN SMALL LETTER S WITH CARON -015F;scedilla;LATIN SMALL LETTER S WITH CEDILLA -015D;scircumflex;LATIN SMALL LETTER S WITH CIRCUMFLEX -2033;second;DOUBLE PRIME -00A7;section;SECTION SIGN -003B;semicolon;SEMICOLON -0037;seven;DIGIT SEVEN -215E;seveneighths;VULGAR FRACTION SEVEN EIGHTHS -2592;shade;MEDIUM SHADE -03C3;sigma;GREEK SMALL LETTER SIGMA -03C2;sigma1;GREEK SMALL LETTER FINAL SIGMA -223C;similar;TILDE OPERATOR -0036;six;DIGIT SIX -002F;slash;SOLIDUS -263A;smileface;WHITE SMILING FACE -0020;space;SPACE -2660;spade;BLACK SPADE SUIT -00A3;sterling;POUND SIGN -220B;suchthat;CONTAINS AS MEMBER -2211;summation;N-ARY SUMMATION -263C;sun;WHITE SUN WITH RAYS -0074;t;LATIN SMALL LETTER T -03C4;tau;GREEK SMALL LETTER TAU -0167;tbar;LATIN SMALL LETTER T WITH STROKE -0165;tcaron;LATIN SMALL LETTER T WITH CARON -2234;therefore;THEREFORE -03B8;theta;GREEK SMALL LETTER THETA -03D1;theta1;GREEK THETA SYMBOL -00FE;thorn;LATIN SMALL LETTER THORN -0033;three;DIGIT THREE -215C;threeeighths;VULGAR FRACTION THREE EIGHTHS -00BE;threequarters;VULGAR FRACTION THREE QUARTERS -02DC;tilde;SMALL TILDE -0303;tildecomb;COMBINING TILDE -0384;tonos;GREEK TONOS -2122;trademark;TRADE MARK SIGN -25BC;triagdn;BLACK DOWN-POINTING TRIANGLE -25C4;triaglf;BLACK LEFT-POINTING POINTER -25BA;triagrt;BLACK RIGHT-POINTING POINTER -25B2;triagup;BLACK UP-POINTING TRIANGLE -0032;two;DIGIT TWO -2025;twodotenleader;TWO DOT LEADER -2154;twothirds;VULGAR FRACTION TWO THIRDS -0075;u;LATIN SMALL LETTER U -00FA;uacute;LATIN SMALL LETTER U WITH ACUTE -016D;ubreve;LATIN SMALL LETTER U WITH BREVE -00FB;ucircumflex;LATIN SMALL LETTER U WITH CIRCUMFLEX -00FC;udieresis;LATIN SMALL LETTER U WITH DIAERESIS -00F9;ugrave;LATIN SMALL LETTER U WITH GRAVE -01B0;uhorn;LATIN SMALL LETTER U WITH HORN -0171;uhungarumlaut;LATIN SMALL LETTER U WITH DOUBLE ACUTE -016B;umacron;LATIN SMALL LETTER U WITH MACRON -005F;underscore;LOW LINE -2017;underscoredbl;DOUBLE LOW LINE -222A;union;UNION -2200;universal;FOR ALL -0173;uogonek;LATIN SMALL LETTER U WITH OGONEK -2580;upblock;UPPER HALF BLOCK -03C5;upsilon;GREEK SMALL LETTER UPSILON -03CB;upsilondieresis;GREEK SMALL LETTER UPSILON WITH DIALYTIKA -03B0;upsilondieresistonos;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS -03CD;upsilontonos;GREEK SMALL LETTER UPSILON WITH TONOS -016F;uring;LATIN SMALL LETTER U WITH RING ABOVE -0169;utilde;LATIN SMALL LETTER U WITH TILDE -0076;v;LATIN SMALL LETTER V -0077;w;LATIN SMALL LETTER W -1E83;wacute;LATIN SMALL LETTER W WITH ACUTE -0175;wcircumflex;LATIN SMALL LETTER W WITH CIRCUMFLEX -1E85;wdieresis;LATIN SMALL LETTER W WITH DIAERESIS -2118;weierstrass;SCRIPT CAPITAL P -1E81;wgrave;LATIN SMALL LETTER W WITH GRAVE -0078;x;LATIN SMALL LETTER X -03BE;xi;GREEK SMALL LETTER XI -0079;y;LATIN SMALL LETTER Y -00FD;yacute;LATIN SMALL LETTER Y WITH ACUTE -0177;ycircumflex;LATIN SMALL LETTER Y WITH CIRCUMFLEX -00FF;ydieresis;LATIN SMALL LETTER Y WITH DIAERESIS -00A5;yen;YEN SIGN -1EF3;ygrave;LATIN SMALL LETTER Y WITH GRAVE -007A;z;LATIN SMALL LETTER Z -017A;zacute;LATIN SMALL LETTER Z WITH ACUTE -017E;zcaron;LATIN SMALL LETTER Z WITH CARON -017C;zdotaccent;LATIN SMALL LETTER Z WITH DOT ABOVE -0030;zero;DIGIT ZERO -03B6;zeta;GREEK SMALL LETTER ZETA -# END -""" - - -class AGLError(Exception): - pass - - -LEGACY_AGL2UV = {} -AGL2UV = {} -UV2AGL = {} - - -def _builddicts(): - import re - - lines = _aglText.splitlines() - - parseAGL_RE = re.compile("([A-Za-z0-9]+);((?:[0-9A-F]{4})(?: (?:[0-9A-F]{4}))*)$") - - for line in lines: - if not line or line[:1] == "#": - continue - m = parseAGL_RE.match(line) - if not m: - raise AGLError("syntax error in glyphlist.txt: %s" % repr(line[:20])) - unicodes = m.group(2) - assert len(unicodes) % 5 == 4 - unicodes = [int(unicode, 16) for unicode in unicodes.split()] - glyphName = tostr(m.group(1)) - LEGACY_AGL2UV[glyphName] = unicodes - - lines = _aglfnText.splitlines() - - parseAGLFN_RE = re.compile("([0-9A-F]{4});([A-Za-z0-9]+);.*?$") - - for line in lines: - if not line or line[:1] == "#": - continue - m = parseAGLFN_RE.match(line) - if not m: - raise AGLError("syntax error in aglfn.txt: %s" % repr(line[:20])) - unicode = m.group(1) - assert len(unicode) == 4 - unicode = int(unicode, 16) - glyphName = tostr(m.group(2)) - AGL2UV[glyphName] = unicode - UV2AGL[unicode] = glyphName - - -_builddicts() - - -def toUnicode(glyph, isZapfDingbats=False): - """Convert glyph names to Unicode, such as ``'longs_t.oldstyle'`` --> ``u'Å¿t'`` - - If ``isZapfDingbats`` is ``True``, the implementation recognizes additional - glyph names (as required by the AGL specification). - """ - # https://github.com/adobe-type-tools/agl-specification#2-the-mapping - # - # 1. Drop all the characters from the glyph name starting with - # the first occurrence of a period (U+002E; FULL STOP), if any. - glyph = glyph.split(".", 1)[0] - - # 2. Split the remaining string into a sequence of components, - # using underscore (U+005F; LOW LINE) as the delimiter. - components = glyph.split("_") - - # 3. Map each component to a character string according to the - # procedure below, and concatenate those strings; the result - # is the character string to which the glyph name is mapped. - result = [_glyphComponentToUnicode(c, isZapfDingbats) for c in components] - return "".join(result) - - -def _glyphComponentToUnicode(component, isZapfDingbats): - # If the font is Zapf Dingbats (PostScript FontName: ZapfDingbats), - # and the component is in the ITC Zapf Dingbats Glyph List, then - # map it to the corresponding character in that list. - dingbat = _zapfDingbatsToUnicode(component) if isZapfDingbats else None - if dingbat: - return dingbat - - # Otherwise, if the component is in AGL, then map it - # to the corresponding character in that list. - uchars = LEGACY_AGL2UV.get(component) - if uchars: - return "".join(map(chr, uchars)) - - # Otherwise, if the component is of the form "uni" (U+0075, - # U+006E, and U+0069) followed by a sequence of uppercase - # hexadecimal digits (0–9 and A–F, meaning U+0030 through - # U+0039 and U+0041 through U+0046), if the length of that - # sequence is a multiple of four, and if each group of four - # digits represents a value in the ranges 0000 through D7FF - # or E000 through FFFF, then interpret each as a Unicode scalar - # value and map the component to the string made of those - # scalar values. Note that the range and digit-length - # restrictions mean that the "uni" glyph name prefix can be - # used only with UVs in the Basic Multilingual Plane (BMP). - uni = _uniToUnicode(component) - if uni: - return uni - - # Otherwise, if the component is of the form "u" (U+0075) - # followed by a sequence of four to six uppercase hexadecimal - # digits (0–9 and A–F, meaning U+0030 through U+0039 and - # U+0041 through U+0046), and those digits represents a value - # in the ranges 0000 through D7FF or E000 through 10FFFF, then - # interpret it as a Unicode scalar value and map the component - # to the string made of this scalar value. - uni = _uToUnicode(component) - if uni: - return uni - - # Otherwise, map the component to an empty string. - return "" - - -# https://github.com/adobe-type-tools/agl-aglfn/blob/master/zapfdingbats.txt -_AGL_ZAPF_DINGBATS = ( - " âœâœ‚✄☎✆âœâœžâœŸâœ âœ¡â˜›â˜žâœŒâœâœŽâœâœ‘✒✓✔✕✖✗✘✙✚✛✜✢✣✤✥✦✧★✩✪✫✬✭✮✯✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿â€" - "ââ‚âƒâ„â…â†â‡âˆâ‰âŠâ‹â—ââ– ââ‘▲▼◆■◗â˜â™âšâ¯â±â²â³â¨â©â¬â­âªâ«â´âµâ›âœââžâ¡â¢â£â¤âœâ¥â¦â§â™ â™¥â™¦â™£ ✉✈✇" - "①②③④⑤⑥⑦⑧⑨⑩â¶â·â¸â¹âºâ»â¼â½â¾â¿âž€âžâž‚➃➄➅➆➇➈➉➊➋➌âžâžŽâžâžâž‘➒➓➔→➣↔" - "↕➙➛➜âžâžžâžŸâž âž¡âž¢âž¤âž¥âž¦âž§âž¨âž©âž«âž­âž¯âž²âž³âžµâž¸âžºâž»âž¼âž½âž¾âžšâžªâž¶âž¹âž˜âž´âž·âž¬âž®âž±âœƒââ’â®â°" -) - - -def _zapfDingbatsToUnicode(glyph): - """Helper for toUnicode().""" - if len(glyph) < 2 or glyph[0] != "a": - return None - try: - gid = int(glyph[1:]) - except ValueError: - return None - if gid < 0 or gid >= len(_AGL_ZAPF_DINGBATS): - return None - uchar = _AGL_ZAPF_DINGBATS[gid] - return uchar if uchar != " " else None - - -_re_uni = re.compile("^uni([0-9A-F]+)$") - - -def _uniToUnicode(component): - """Helper for toUnicode() to handle "uniABCD" components.""" - match = _re_uni.match(component) - if match is None: - return None - digits = match.group(1) - if len(digits) % 4 != 0: - return None - chars = [int(digits[i : i + 4], 16) for i in range(0, len(digits), 4)] - if any(c >= 0xD800 and c <= 0xDFFF for c in chars): - # The AGL specification explicitly excluded surrogate pairs. - return None - return "".join([chr(c) for c in chars]) - - -_re_u = re.compile("^u([0-9A-F]{4,6})$") - - -def _uToUnicode(component): - """Helper for toUnicode() to handle "u1ABCD" components.""" - match = _re_u.match(component) - if match is None: - return None - digits = match.group(1) - try: - value = int(digits, 16) - except ValueError: - return None - if (value >= 0x0000 and value <= 0xD7FF) or (value >= 0xE000 and value <= 0x10FFFF): - return chr(value) - return None diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/CFF2ToCFF.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/CFF2ToCFF.py deleted file mode 100644 index f929cc9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/CFF2ToCFF.py +++ /dev/null @@ -1,203 +0,0 @@ -"""CFF2 to CFF converter.""" - -from fontTools.ttLib import TTFont, newTable -from fontTools.misc.cliTools import makeOutputFileName -from fontTools.cffLib import ( - TopDictIndex, - buildOrder, - buildDefaults, - topDictOperators, - privateDictOperators, -) -from .width import optimizeWidths -from collections import defaultdict -import logging - - -__all__ = ["convertCFF2ToCFF", "main"] - - -log = logging.getLogger("fontTools.cffLib") - - -def _convertCFF2ToCFF(cff, otFont): - """Converts this object from CFF2 format to CFF format. This conversion - is done 'in-place'. The conversion cannot be reversed. - - The CFF2 font cannot be variable. (TODO Accept those and convert to the - default instance?) - - This assumes a decompiled CFF table. (i.e. that the object has been - filled via :meth:`decompile` and e.g. not loaded from XML.)""" - - cff.major = 1 - - topDictData = TopDictIndex(None) - for item in cff.topDictIndex: - # Iterate over, such that all are decompiled - item.cff2GetGlyphOrder = None - topDictData.append(item) - cff.topDictIndex = topDictData - topDict = topDictData[0] - - if hasattr(topDict, "VarStore"): - raise ValueError("Variable CFF2 font cannot be converted to CFF format.") - - opOrder = buildOrder(topDictOperators) - topDict.order = opOrder - for key in topDict.rawDict.keys(): - if key not in opOrder: - del topDict.rawDict[key] - if hasattr(topDict, key): - delattr(topDict, key) - - fdArray = topDict.FDArray - charStrings = topDict.CharStrings - - defaults = buildDefaults(privateDictOperators) - order = buildOrder(privateDictOperators) - for fd in fdArray: - fd.setCFF2(False) - privateDict = fd.Private - privateDict.order = order - for key in order: - if key not in privateDict.rawDict and key in defaults: - privateDict.rawDict[key] = defaults[key] - for key in privateDict.rawDict.keys(): - if key not in order: - del privateDict.rawDict[key] - if hasattr(privateDict, key): - delattr(privateDict, key) - - for cs in charStrings.values(): - cs.decompile() - cs.program.append("endchar") - for subrSets in [cff.GlobalSubrs] + [ - getattr(fd.Private, "Subrs", []) for fd in fdArray - ]: - for cs in subrSets: - cs.program.append("return") - - # Add (optimal) width to CharStrings that need it. - widths = defaultdict(list) - metrics = otFont["hmtx"].metrics - for glyphName in charStrings.keys(): - cs, fdIndex = charStrings.getItemAndSelector(glyphName) - if fdIndex == None: - fdIndex = 0 - widths[fdIndex].append(metrics[glyphName][0]) - for fdIndex, widthList in widths.items(): - bestDefault, bestNominal = optimizeWidths(widthList) - private = fdArray[fdIndex].Private - private.defaultWidthX = bestDefault - private.nominalWidthX = bestNominal - for glyphName in charStrings.keys(): - cs, fdIndex = charStrings.getItemAndSelector(glyphName) - if fdIndex == None: - fdIndex = 0 - private = fdArray[fdIndex].Private - width = metrics[glyphName][0] - if width != private.defaultWidthX: - cs.program.insert(0, width - private.nominalWidthX) - - mapping = { - name: ("cid" + str(n) if n else ".notdef") - for n, name in enumerate(topDict.charset) - } - topDict.charset = [ - "cid" + str(n) if n else ".notdef" for n in range(len(topDict.charset)) - ] - charStrings.charStrings = { - mapping[name]: v for name, v in charStrings.charStrings.items() - } - - # I'm not sure why the following is *not* necessary. And it breaks - # the output if I add it. - # topDict.ROS = ("Adobe", "Identity", 0) - - -def convertCFF2ToCFF(font, *, updatePostTable=True): - cff = font["CFF2"].cff - _convertCFF2ToCFF(cff, font) - del font["CFF2"] - table = font["CFF "] = newTable("CFF ") - table.cff = cff - - if updatePostTable and "post" in font: - # Only version supported for fonts with CFF table is 0x00030000 not 0x20000 - post = font["post"] - if post.formatType == 2.0: - post.formatType = 3.0 - - -def main(args=None): - """Convert CFF OTF font to CFF2 OTF font""" - if args is None: - import sys - - args = sys.argv[1:] - - import argparse - - parser = argparse.ArgumentParser( - "fonttools cffLib.CFFToCFF2", - description="Upgrade a CFF font to CFF2.", - ) - parser.add_argument( - "input", metavar="INPUT.ttf", help="Input OTF file with CFF table." - ) - parser.add_argument( - "-o", - "--output", - metavar="OUTPUT.ttf", - default=None, - help="Output instance OTF file (default: INPUT-CFF2.ttf).", - ) - parser.add_argument( - "--no-recalc-timestamp", - dest="recalc_timestamp", - action="store_false", - help="Don't set the output font's timestamp to the current time.", - ) - loggingGroup = parser.add_mutually_exclusive_group(required=False) - loggingGroup.add_argument( - "-v", "--verbose", action="store_true", help="Run more verbosely." - ) - loggingGroup.add_argument( - "-q", "--quiet", action="store_true", help="Turn verbosity off." - ) - options = parser.parse_args(args) - - from fontTools import configLogger - - configLogger( - level=("DEBUG" if options.verbose else "ERROR" if options.quiet else "INFO") - ) - - import os - - infile = options.input - if not os.path.isfile(infile): - parser.error("No such file '{}'".format(infile)) - - outfile = ( - makeOutputFileName(infile, overWrite=True, suffix="-CFF") - if not options.output - else options.output - ) - - font = TTFont(infile, recalcTimestamp=options.recalc_timestamp, recalcBBoxes=False) - - convertCFF2ToCFF(font) - - log.info( - "Saving %s", - outfile, - ) - font.save(outfile) - - -if __name__ == "__main__": - import sys - - sys.exit(main(sys.argv[1:])) diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/CFFToCFF2.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/CFFToCFF2.py deleted file mode 100644 index 2555f0b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/CFFToCFF2.py +++ /dev/null @@ -1,305 +0,0 @@ -"""CFF to CFF2 converter.""" - -from fontTools.ttLib import TTFont, newTable -from fontTools.misc.cliTools import makeOutputFileName -from fontTools.misc.psCharStrings import T2WidthExtractor -from fontTools.cffLib import ( - TopDictIndex, - FDArrayIndex, - FontDict, - buildOrder, - topDictOperators, - privateDictOperators, - topDictOperators2, - privateDictOperators2, -) -from io import BytesIO -import logging - -__all__ = ["convertCFFToCFF2", "main"] - - -log = logging.getLogger("fontTools.cffLib") - - -class _NominalWidthUsedError(Exception): - def __add__(self, other): - raise self - - def __radd__(self, other): - raise self - - -def _convertCFFToCFF2(cff, otFont): - """Converts this object from CFF format to CFF2 format. This conversion - is done 'in-place'. The conversion cannot be reversed. - - This assumes a decompiled CFF table. (i.e. that the object has been - filled via :meth:`decompile` and e.g. not loaded from XML.)""" - - # Clean up T2CharStrings - - topDict = cff.topDictIndex[0] - fdArray = topDict.FDArray if hasattr(topDict, "FDArray") else None - charStrings = topDict.CharStrings - globalSubrs = cff.GlobalSubrs - localSubrs = ( - [getattr(fd.Private, "Subrs", []) for fd in fdArray] - if fdArray - else ( - [topDict.Private.Subrs] - if hasattr(topDict, "Private") and hasattr(topDict.Private, "Subrs") - else [] - ) - ) - - for glyphName in charStrings.keys(): - cs, fdIndex = charStrings.getItemAndSelector(glyphName) - cs.decompile() - - # Clean up subroutines first - for subrs in [globalSubrs] + localSubrs: - for subr in subrs: - program = subr.program - i = j = len(program) - try: - i = program.index("return") - except ValueError: - pass - try: - j = program.index("endchar") - except ValueError: - pass - program[min(i, j) :] = [] - - # Clean up glyph charstrings - removeUnusedSubrs = False - nominalWidthXError = _NominalWidthUsedError() - for glyphName in charStrings.keys(): - cs, fdIndex = charStrings.getItemAndSelector(glyphName) - program = cs.program - - thisLocalSubrs = ( - localSubrs[fdIndex] - if fdIndex is not None - else ( - getattr(topDict.Private, "Subrs", []) - if hasattr(topDict, "Private") - else [] - ) - ) - - # Intentionally use custom type for nominalWidthX, such that any - # CharString that has an explicit width encoded will throw back to us. - extractor = T2WidthExtractor( - thisLocalSubrs, - globalSubrs, - nominalWidthXError, - 0, - ) - try: - extractor.execute(cs) - except _NominalWidthUsedError: - # Program has explicit width. We want to drop it, but can't - # just pop the first number since it may be a subroutine call. - # Instead, when seeing that, we embed the subroutine and recurse. - # If this ever happened, we later prune unused subroutines. - while len(program) >= 2 and program[1] in ["callsubr", "callgsubr"]: - removeUnusedSubrs = True - subrNumber = program.pop(0) - assert isinstance(subrNumber, int), subrNumber - op = program.pop(0) - bias = extractor.localBias if op == "callsubr" else extractor.globalBias - subrNumber += bias - subrSet = thisLocalSubrs if op == "callsubr" else globalSubrs - subrProgram = subrSet[subrNumber].program - program[:0] = subrProgram - # Now pop the actual width - assert len(program) >= 1, program - program.pop(0) - - if program and program[-1] == "endchar": - program.pop() - - if removeUnusedSubrs: - cff.remove_unused_subroutines() - - # Upconvert TopDict - - cff.major = 2 - cff2GetGlyphOrder = cff.otFont.getGlyphOrder - topDictData = TopDictIndex(None, cff2GetGlyphOrder) - for item in cff.topDictIndex: - # Iterate over, such that all are decompiled - topDictData.append(item) - cff.topDictIndex = topDictData - topDict = topDictData[0] - if hasattr(topDict, "Private"): - privateDict = topDict.Private - else: - privateDict = None - opOrder = buildOrder(topDictOperators2) - topDict.order = opOrder - topDict.cff2GetGlyphOrder = cff2GetGlyphOrder - - if not hasattr(topDict, "FDArray"): - fdArray = topDict.FDArray = FDArrayIndex() - fdArray.strings = None - fdArray.GlobalSubrs = topDict.GlobalSubrs - topDict.GlobalSubrs.fdArray = fdArray - charStrings = topDict.CharStrings - if charStrings.charStringsAreIndexed: - charStrings.charStringsIndex.fdArray = fdArray - else: - charStrings.fdArray = fdArray - fontDict = FontDict() - fontDict.setCFF2(True) - fdArray.append(fontDict) - fontDict.Private = privateDict - privateOpOrder = buildOrder(privateDictOperators2) - if privateDict is not None: - for entry in privateDictOperators: - key = entry[1] - if key not in privateOpOrder: - if key in privateDict.rawDict: - # print "Removing private dict", key - del privateDict.rawDict[key] - if hasattr(privateDict, key): - delattr(privateDict, key) - # print "Removing privateDict attr", key - else: - # clean up the PrivateDicts in the fdArray - fdArray = topDict.FDArray - privateOpOrder = buildOrder(privateDictOperators2) - for fontDict in fdArray: - fontDict.setCFF2(True) - for key in list(fontDict.rawDict.keys()): - if key not in fontDict.order: - del fontDict.rawDict[key] - if hasattr(fontDict, key): - delattr(fontDict, key) - - privateDict = fontDict.Private - for entry in privateDictOperators: - key = entry[1] - if key not in privateOpOrder: - if key in list(privateDict.rawDict.keys()): - # print "Removing private dict", key - del privateDict.rawDict[key] - if hasattr(privateDict, key): - delattr(privateDict, key) - # print "Removing privateDict attr", key - - # Now delete up the deprecated topDict operators from CFF 1.0 - for entry in topDictOperators: - key = entry[1] - # We seem to need to keep the charset operator for now, - # or we fail to compile with some fonts, like AdditionFont.otf. - # I don't know which kind of CFF font those are. But keeping - # charset seems to work. It will be removed when we save and - # read the font again. - # - # AdditionFont.otf has . - if key == "charset": - continue - if key not in opOrder: - if key in topDict.rawDict: - del topDict.rawDict[key] - if hasattr(topDict, key): - delattr(topDict, key) - - # TODO(behdad): What does the following comment even mean? Both CFF and CFF2 - # use the same T2Charstring class. I *think* what it means is that the CharStrings - # were loaded for CFF1, and we need to reload them for CFF2 to set varstore, etc - # on them. At least that's what I understand. It's probably safe to remove this - # and just set vstore where needed. - # - # See comment above about charset as well. - - # At this point, the Subrs and Charstrings are all still T2Charstring class - # easiest to fix this by compiling, then decompiling again - file = BytesIO() - cff.compile(file, otFont, isCFF2=True) - file.seek(0) - cff.decompile(file, otFont, isCFF2=True) - - -def convertCFFToCFF2(font): - cff = font["CFF "].cff - del font["CFF "] - _convertCFFToCFF2(cff, font) - table = font["CFF2"] = newTable("CFF2") - table.cff = cff - - -def main(args=None): - """Convert CFF OTF font to CFF2 OTF font""" - if args is None: - import sys - - args = sys.argv[1:] - - import argparse - - parser = argparse.ArgumentParser( - "fonttools cffLib.CFFToCFF2", - description="Upgrade a CFF font to CFF2.", - ) - parser.add_argument( - "input", metavar="INPUT.ttf", help="Input OTF file with CFF table." - ) - parser.add_argument( - "-o", - "--output", - metavar="OUTPUT.ttf", - default=None, - help="Output instance OTF file (default: INPUT-CFF2.ttf).", - ) - parser.add_argument( - "--no-recalc-timestamp", - dest="recalc_timestamp", - action="store_false", - help="Don't set the output font's timestamp to the current time.", - ) - loggingGroup = parser.add_mutually_exclusive_group(required=False) - loggingGroup.add_argument( - "-v", "--verbose", action="store_true", help="Run more verbosely." - ) - loggingGroup.add_argument( - "-q", "--quiet", action="store_true", help="Turn verbosity off." - ) - options = parser.parse_args(args) - - from fontTools import configLogger - - configLogger( - level=("DEBUG" if options.verbose else "ERROR" if options.quiet else "INFO") - ) - - import os - - infile = options.input - if not os.path.isfile(infile): - parser.error("No such file '{}'".format(infile)) - - outfile = ( - makeOutputFileName(infile, overWrite=True, suffix="-CFF2") - if not options.output - else options.output - ) - - font = TTFont(infile, recalcTimestamp=options.recalc_timestamp, recalcBBoxes=False) - - convertCFFToCFF2(font) - - log.info( - "Saving %s", - outfile, - ) - font.save(outfile) - - -if __name__ == "__main__": - import sys - - sys.exit(main(sys.argv[1:])) diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__init__.py deleted file mode 100644 index d75e23b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__init__.py +++ /dev/null @@ -1,3659 +0,0 @@ -"""cffLib: read/write Adobe CFF fonts - -OpenType fonts with PostScript outlines embed a completely independent -font file in Adobe's *Compact Font Format*. So dealing with OpenType fonts -requires also dealing with CFF. This module allows you to read and write -fonts written in the CFF format. - -In 2016, OpenType 1.8 introduced the `CFF2 `_ -format which, along with other changes, extended the CFF format to deal with -the demands of variable fonts. This module parses both original CFF and CFF2. - -""" - -from fontTools.misc import sstruct -from fontTools.misc import psCharStrings -from fontTools.misc.arrayTools import unionRect, intRect -from fontTools.misc.textTools import ( - bytechr, - byteord, - bytesjoin, - tobytes, - tostr, - safeEval, -) -from fontTools.ttLib import TTFont -from fontTools.ttLib.tables.otBase import OTTableWriter -from fontTools.ttLib.tables.otBase import OTTableReader -from fontTools.ttLib.tables import otTables as ot -from io import BytesIO -import struct -import logging -import re - -# mute cffLib debug messages when running ttx in verbose mode -DEBUG = logging.DEBUG - 1 -log = logging.getLogger(__name__) - -cffHeaderFormat = """ - major: B - minor: B - hdrSize: B -""" - -maxStackLimit = 513 -# maxstack operator has been deprecated. max stack is now always 513. - - -class CFFFontSet(object): - """A CFF font "file" can contain more than one font, although this is - extremely rare (and not allowed within OpenType fonts). - - This class is the entry point for parsing a CFF table. To actually - manipulate the data inside the CFF font, you will want to access the - ``CFFFontSet``'s :class:`TopDict` object. To do this, a ``CFFFontSet`` - object can either be treated as a dictionary (with appropriate - ``keys()`` and ``values()`` methods) mapping font names to :class:`TopDict` - objects, or as a list. - - .. code:: python - - from fontTools import ttLib - tt = ttLib.TTFont("Tests/cffLib/data/LinLibertine_RBI.otf") - tt["CFF "].cff - # - tt["CFF "].cff[0] # Here's your actual font data - # - - """ - - def decompile(self, file, otFont, isCFF2=None): - """Parse a binary CFF file into an internal representation. ``file`` - should be a file handle object. ``otFont`` is the top-level - :py:class:`fontTools.ttLib.ttFont.TTFont` object containing this CFF file. - - If ``isCFF2`` is passed and set to ``True`` or ``False``, then the - library makes an assertion that the CFF header is of the appropriate - version. - """ - - self.otFont = otFont - sstruct.unpack(cffHeaderFormat, file.read(3), self) - if isCFF2 is not None: - # called from ttLib: assert 'major' as read from file matches the - # expected version - expected_major = 2 if isCFF2 else 1 - if self.major != expected_major: - raise ValueError( - "Invalid CFF 'major' version: expected %d, found %d" - % (expected_major, self.major) - ) - else: - # use 'major' version from file to determine if isCFF2 - assert self.major in (1, 2), "Unknown CFF format" - isCFF2 = self.major == 2 - if not isCFF2: - self.offSize = struct.unpack("B", file.read(1))[0] - file.seek(self.hdrSize) - self.fontNames = list(tostr(s) for s in Index(file, isCFF2=isCFF2)) - self.topDictIndex = TopDictIndex(file, isCFF2=isCFF2) - self.strings = IndexedStrings(file) - else: # isCFF2 - self.topDictSize = struct.unpack(">H", file.read(2))[0] - file.seek(self.hdrSize) - self.fontNames = ["CFF2Font"] - cff2GetGlyphOrder = otFont.getGlyphOrder - # in CFF2, offsetSize is the size of the TopDict data. - self.topDictIndex = TopDictIndex( - file, cff2GetGlyphOrder, self.topDictSize, isCFF2=isCFF2 - ) - self.strings = None - self.GlobalSubrs = GlobalSubrsIndex(file, isCFF2=isCFF2) - self.topDictIndex.strings = self.strings - self.topDictIndex.GlobalSubrs = self.GlobalSubrs - - def __len__(self): - return len(self.fontNames) - - def keys(self): - return list(self.fontNames) - - def values(self): - return self.topDictIndex - - def __getitem__(self, nameOrIndex): - """Return TopDict instance identified by name (str) or index (int - or any object that implements `__index__`). - """ - if hasattr(nameOrIndex, "__index__"): - index = nameOrIndex.__index__() - elif isinstance(nameOrIndex, str): - name = nameOrIndex - try: - index = self.fontNames.index(name) - except ValueError: - raise KeyError(nameOrIndex) - else: - raise TypeError(nameOrIndex) - return self.topDictIndex[index] - - def compile(self, file, otFont, isCFF2=None): - """Write the object back into binary representation onto the given file. - ``file`` should be a file handle object. ``otFont`` is the top-level - :py:class:`fontTools.ttLib.ttFont.TTFont` object containing this CFF file. - - If ``isCFF2`` is passed and set to ``True`` or ``False``, then the - library makes an assertion that the CFF header is of the appropriate - version. - """ - self.otFont = otFont - if isCFF2 is not None: - # called from ttLib: assert 'major' value matches expected version - expected_major = 2 if isCFF2 else 1 - if self.major != expected_major: - raise ValueError( - "Invalid CFF 'major' version: expected %d, found %d" - % (expected_major, self.major) - ) - else: - # use current 'major' value to determine output format - assert self.major in (1, 2), "Unknown CFF format" - isCFF2 = self.major == 2 - - if otFont.recalcBBoxes and not isCFF2: - for topDict in self.topDictIndex: - topDict.recalcFontBBox() - - if not isCFF2: - strings = IndexedStrings() - else: - strings = None - writer = CFFWriter(isCFF2) - topCompiler = self.topDictIndex.getCompiler(strings, self, isCFF2=isCFF2) - if isCFF2: - self.hdrSize = 5 - writer.add(sstruct.pack(cffHeaderFormat, self)) - # Note: topDictSize will most likely change in CFFWriter.toFile(). - self.topDictSize = topCompiler.getDataLength() - writer.add(struct.pack(">H", self.topDictSize)) - else: - self.hdrSize = 4 - self.offSize = 4 # will most likely change in CFFWriter.toFile(). - writer.add(sstruct.pack(cffHeaderFormat, self)) - writer.add(struct.pack("B", self.offSize)) - if not isCFF2: - fontNames = Index() - for name in self.fontNames: - fontNames.append(name) - writer.add(fontNames.getCompiler(strings, self, isCFF2=isCFF2)) - writer.add(topCompiler) - if not isCFF2: - writer.add(strings.getCompiler()) - writer.add(self.GlobalSubrs.getCompiler(strings, self, isCFF2=isCFF2)) - - for topDict in self.topDictIndex: - if not hasattr(topDict, "charset") or topDict.charset is None: - charset = otFont.getGlyphOrder() - topDict.charset = charset - children = topCompiler.getChildren(strings) - for child in children: - writer.add(child) - - writer.toFile(file) - - def toXML(self, xmlWriter): - """Write the object into XML representation onto the given - :class:`fontTools.misc.xmlWriter.XMLWriter`. - - .. code:: python - - writer = xmlWriter.XMLWriter(sys.stdout) - tt["CFF "].cff.toXML(writer) - - """ - - xmlWriter.simpletag("major", value=self.major) - xmlWriter.newline() - xmlWriter.simpletag("minor", value=self.minor) - xmlWriter.newline() - for fontName in self.fontNames: - xmlWriter.begintag("CFFFont", name=tostr(fontName)) - xmlWriter.newline() - font = self[fontName] - font.toXML(xmlWriter) - xmlWriter.endtag("CFFFont") - xmlWriter.newline() - xmlWriter.newline() - xmlWriter.begintag("GlobalSubrs") - xmlWriter.newline() - self.GlobalSubrs.toXML(xmlWriter) - xmlWriter.endtag("GlobalSubrs") - xmlWriter.newline() - - def fromXML(self, name, attrs, content, otFont=None): - """Reads data from the XML element into the ``CFFFontSet`` object.""" - self.otFont = otFont - - # set defaults. These will be replaced if there are entries for them - # in the XML file. - if not hasattr(self, "major"): - self.major = 1 - if not hasattr(self, "minor"): - self.minor = 0 - - if name == "CFFFont": - if self.major == 1: - if not hasattr(self, "offSize"): - # this will be recalculated when the cff is compiled. - self.offSize = 4 - if not hasattr(self, "hdrSize"): - self.hdrSize = 4 - if not hasattr(self, "GlobalSubrs"): - self.GlobalSubrs = GlobalSubrsIndex() - if not hasattr(self, "fontNames"): - self.fontNames = [] - self.topDictIndex = TopDictIndex() - fontName = attrs["name"] - self.fontNames.append(fontName) - topDict = TopDict(GlobalSubrs=self.GlobalSubrs) - topDict.charset = None # gets filled in later - elif self.major == 2: - if not hasattr(self, "hdrSize"): - self.hdrSize = 5 - if not hasattr(self, "GlobalSubrs"): - self.GlobalSubrs = GlobalSubrsIndex() - if not hasattr(self, "fontNames"): - self.fontNames = ["CFF2Font"] - cff2GetGlyphOrder = self.otFont.getGlyphOrder - topDict = TopDict( - GlobalSubrs=self.GlobalSubrs, cff2GetGlyphOrder=cff2GetGlyphOrder - ) - self.topDictIndex = TopDictIndex(None, cff2GetGlyphOrder) - self.topDictIndex.append(topDict) - for element in content: - if isinstance(element, str): - continue - name, attrs, content = element - topDict.fromXML(name, attrs, content) - - if hasattr(topDict, "VarStore") and topDict.FDArray[0].vstore is None: - fdArray = topDict.FDArray - for fontDict in fdArray: - if hasattr(fontDict, "Private"): - fontDict.Private.vstore = topDict.VarStore - - elif name == "GlobalSubrs": - subrCharStringClass = psCharStrings.T2CharString - if not hasattr(self, "GlobalSubrs"): - self.GlobalSubrs = GlobalSubrsIndex() - for element in content: - if isinstance(element, str): - continue - name, attrs, content = element - subr = subrCharStringClass() - subr.fromXML(name, attrs, content) - self.GlobalSubrs.append(subr) - elif name == "major": - self.major = int(attrs["value"]) - elif name == "minor": - self.minor = int(attrs["value"]) - - def convertCFFToCFF2(self, otFont): - from .CFFToCFF2 import _convertCFFToCFF2 - - _convertCFFToCFF2(self, otFont) - - def convertCFF2ToCFF(self, otFont): - from .CFF2ToCFF import _convertCFF2ToCFF - - _convertCFF2ToCFF(self, otFont) - - def desubroutinize(self): - from .transforms import desubroutinize - - desubroutinize(self) - - def remove_hints(self): - from .transforms import remove_hints - - remove_hints(self) - - def remove_unused_subroutines(self): - from .transforms import remove_unused_subroutines - - remove_unused_subroutines(self) - - -class CFFWriter(object): - """Helper class for serializing CFF data to binary. Used by - :meth:`CFFFontSet.compile`.""" - - def __init__(self, isCFF2): - self.data = [] - self.isCFF2 = isCFF2 - - def add(self, table): - self.data.append(table) - - def toFile(self, file): - lastPosList = None - count = 1 - while True: - log.log(DEBUG, "CFFWriter.toFile() iteration: %d", count) - count = count + 1 - pos = 0 - posList = [pos] - for item in self.data: - if hasattr(item, "getDataLength"): - endPos = pos + item.getDataLength() - if isinstance(item, TopDictIndexCompiler) and item.isCFF2: - self.topDictSize = item.getDataLength() - else: - endPos = pos + len(item) - if hasattr(item, "setPos"): - item.setPos(pos, endPos) - pos = endPos - posList.append(pos) - if posList == lastPosList: - break - lastPosList = posList - log.log(DEBUG, "CFFWriter.toFile() writing to file.") - begin = file.tell() - if self.isCFF2: - self.data[1] = struct.pack(">H", self.topDictSize) - else: - self.offSize = calcOffSize(lastPosList[-1]) - self.data[1] = struct.pack("B", self.offSize) - posList = [0] - for item in self.data: - if hasattr(item, "toFile"): - item.toFile(file) - else: - file.write(item) - posList.append(file.tell() - begin) - assert posList == lastPosList - - -def calcOffSize(largestOffset): - if largestOffset < 0x100: - offSize = 1 - elif largestOffset < 0x10000: - offSize = 2 - elif largestOffset < 0x1000000: - offSize = 3 - else: - offSize = 4 - return offSize - - -class IndexCompiler(object): - """Base class for writing CFF `INDEX data `_ - to binary.""" - - def __init__(self, items, strings, parent, isCFF2=None): - if isCFF2 is None and hasattr(parent, "isCFF2"): - isCFF2 = parent.isCFF2 - assert isCFF2 is not None - self.isCFF2 = isCFF2 - self.items = self.getItems(items, strings) - self.parent = parent - - def getItems(self, items, strings): - return items - - def getOffsets(self): - # An empty INDEX contains only the count field. - if self.items: - pos = 1 - offsets = [pos] - for item in self.items: - if hasattr(item, "getDataLength"): - pos = pos + item.getDataLength() - else: - pos = pos + len(item) - offsets.append(pos) - else: - offsets = [] - return offsets - - def getDataLength(self): - if self.isCFF2: - countSize = 4 - else: - countSize = 2 - - if self.items: - lastOffset = self.getOffsets()[-1] - offSize = calcOffSize(lastOffset) - dataLength = ( - countSize - + 1 # count - + (len(self.items) + 1) * offSize # offSize - + lastOffset # the offsets - - 1 # size of object data - ) - else: - # count. For empty INDEX tables, this is the only entry. - dataLength = countSize - - return dataLength - - def toFile(self, file): - offsets = self.getOffsets() - if self.isCFF2: - writeCard32(file, len(self.items)) - else: - writeCard16(file, len(self.items)) - # An empty INDEX contains only the count field. - if self.items: - offSize = calcOffSize(offsets[-1]) - writeCard8(file, offSize) - offSize = -offSize - pack = struct.pack - for offset in offsets: - binOffset = pack(">l", offset)[offSize:] - assert len(binOffset) == -offSize - file.write(binOffset) - for item in self.items: - if hasattr(item, "toFile"): - item.toFile(file) - else: - data = tobytes(item, encoding="latin1") - file.write(data) - - -class IndexedStringsCompiler(IndexCompiler): - def getItems(self, items, strings): - return items.strings - - -class TopDictIndexCompiler(IndexCompiler): - """Helper class for writing the TopDict to binary.""" - - def getItems(self, items, strings): - out = [] - for item in items: - out.append(item.getCompiler(strings, self)) - return out - - def getChildren(self, strings): - children = [] - for topDict in self.items: - children.extend(topDict.getChildren(strings)) - return children - - def getOffsets(self): - if self.isCFF2: - offsets = [0, self.items[0].getDataLength()] - return offsets - else: - return super(TopDictIndexCompiler, self).getOffsets() - - def getDataLength(self): - if self.isCFF2: - dataLength = self.items[0].getDataLength() - return dataLength - else: - return super(TopDictIndexCompiler, self).getDataLength() - - def toFile(self, file): - if self.isCFF2: - self.items[0].toFile(file) - else: - super(TopDictIndexCompiler, self).toFile(file) - - -class FDArrayIndexCompiler(IndexCompiler): - """Helper class for writing the - `Font DICT INDEX `_ - to binary.""" - - def getItems(self, items, strings): - out = [] - for item in items: - out.append(item.getCompiler(strings, self)) - return out - - def getChildren(self, strings): - children = [] - for fontDict in self.items: - children.extend(fontDict.getChildren(strings)) - return children - - def toFile(self, file): - offsets = self.getOffsets() - if self.isCFF2: - writeCard32(file, len(self.items)) - else: - writeCard16(file, len(self.items)) - offSize = calcOffSize(offsets[-1]) - writeCard8(file, offSize) - offSize = -offSize - pack = struct.pack - for offset in offsets: - binOffset = pack(">l", offset)[offSize:] - assert len(binOffset) == -offSize - file.write(binOffset) - for item in self.items: - if hasattr(item, "toFile"): - item.toFile(file) - else: - file.write(item) - - def setPos(self, pos, endPos): - self.parent.rawDict["FDArray"] = pos - - -class GlobalSubrsCompiler(IndexCompiler): - """Helper class for writing the `global subroutine INDEX `_ - to binary.""" - - def getItems(self, items, strings): - out = [] - for cs in items: - cs.compile(self.isCFF2) - out.append(cs.bytecode) - return out - - -class SubrsCompiler(GlobalSubrsCompiler): - """Helper class for writing the `local subroutine INDEX `_ - to binary.""" - - def setPos(self, pos, endPos): - offset = pos - self.parent.pos - self.parent.rawDict["Subrs"] = offset - - -class CharStringsCompiler(GlobalSubrsCompiler): - """Helper class for writing the `CharStrings INDEX `_ - to binary.""" - - def getItems(self, items, strings): - out = [] - for cs in items: - cs.compile(self.isCFF2) - out.append(cs.bytecode) - return out - - def setPos(self, pos, endPos): - self.parent.rawDict["CharStrings"] = pos - - -class Index(object): - """This class represents what the CFF spec calls an INDEX (an array of - variable-sized objects). `Index` items can be addressed and set using - Python list indexing.""" - - compilerClass = IndexCompiler - - def __init__(self, file=None, isCFF2=None): - self.items = [] - self.offsets = offsets = [] - name = self.__class__.__name__ - if file is None: - return - self._isCFF2 = isCFF2 - log.log(DEBUG, "loading %s at %s", name, file.tell()) - self.file = file - if isCFF2: - count = readCard32(file) - else: - count = readCard16(file) - if count == 0: - return - self.items = [None] * count - offSize = readCard8(file) - log.log(DEBUG, " index count: %s offSize: %s", count, offSize) - assert offSize <= 4, "offSize too large: %s" % offSize - pad = b"\0" * (4 - offSize) - for index in range(count + 1): - chunk = file.read(offSize) - chunk = pad + chunk - (offset,) = struct.unpack(">L", chunk) - offsets.append(int(offset)) - self.offsetBase = file.tell() - 1 - file.seek(self.offsetBase + offsets[-1]) # pretend we've read the whole lot - log.log(DEBUG, " end of %s at %s", name, file.tell()) - - def __len__(self): - return len(self.items) - - def __getitem__(self, index): - item = self.items[index] - if item is not None: - return item - offset = self.offsets[index] + self.offsetBase - size = self.offsets[index + 1] - self.offsets[index] - file = self.file - file.seek(offset) - data = file.read(size) - assert len(data) == size - item = self.produceItem(index, data, file, offset) - self.items[index] = item - return item - - def __setitem__(self, index, item): - self.items[index] = item - - def produceItem(self, index, data, file, offset): - return data - - def append(self, item): - """Add an item to an INDEX.""" - self.items.append(item) - - def getCompiler(self, strings, parent, isCFF2=None): - return self.compilerClass(self, strings, parent, isCFF2=isCFF2) - - def clear(self): - """Empty the INDEX.""" - del self.items[:] - - -class GlobalSubrsIndex(Index): - """This index contains all the global subroutines in the font. A global - subroutine is a set of ``CharString`` data which is accessible to any - glyph in the font, and are used to store repeated instructions - for - example, components may be encoded as global subroutines, but so could - hinting instructions. - - Remember that when interpreting a ``callgsubr`` instruction (or indeed - a ``callsubr`` instruction) that you will need to add the "subroutine - number bias" to number given: - - .. code:: python - - tt = ttLib.TTFont("Almendra-Bold.otf") - u = tt["CFF "].cff[0].CharStrings["udieresis"] - u.decompile() - - u.toXML(XMLWriter(sys.stdout)) - # - # -64 callgsubr <-- Subroutine which implements the dieresis mark - # - - tt["CFF "].cff[0].GlobalSubrs[-64] # <-- WRONG - # - - tt["CFF "].cff[0].GlobalSubrs[-64 + 107] # <-- RIGHT - # - - ("The bias applied depends on the number of subrs (gsubrs). If the number of - subrs (gsubrs) is less than 1240, the bias is 107. Otherwise if it is less - than 33900, it is 1131; otherwise it is 32768.", - `Subroutine Operators `) - """ - - compilerClass = GlobalSubrsCompiler - subrClass = psCharStrings.T2CharString - charStringClass = psCharStrings.T2CharString - - def __init__( - self, - file=None, - globalSubrs=None, - private=None, - fdSelect=None, - fdArray=None, - isCFF2=None, - ): - super(GlobalSubrsIndex, self).__init__(file, isCFF2=isCFF2) - self.globalSubrs = globalSubrs - self.private = private - if fdSelect: - self.fdSelect = fdSelect - if fdArray: - self.fdArray = fdArray - - def produceItem(self, index, data, file, offset): - if self.private is not None: - private = self.private - elif hasattr(self, "fdArray") and self.fdArray is not None: - if hasattr(self, "fdSelect") and self.fdSelect is not None: - fdIndex = self.fdSelect[index] - else: - fdIndex = 0 - private = self.fdArray[fdIndex].Private - else: - private = None - return self.subrClass(data, private=private, globalSubrs=self.globalSubrs) - - def toXML(self, xmlWriter): - """Write the subroutines index into XML representation onto the given - :class:`fontTools.misc.xmlWriter.XMLWriter`. - - .. code:: python - - writer = xmlWriter.XMLWriter(sys.stdout) - tt["CFF "].cff[0].GlobalSubrs.toXML(writer) - - """ - xmlWriter.comment( - "The 'index' attribute is only for humans; " "it is ignored when parsed." - ) - xmlWriter.newline() - for i in range(len(self)): - subr = self[i] - if subr.needsDecompilation(): - xmlWriter.begintag("CharString", index=i, raw=1) - else: - xmlWriter.begintag("CharString", index=i) - xmlWriter.newline() - subr.toXML(xmlWriter) - xmlWriter.endtag("CharString") - xmlWriter.newline() - - def fromXML(self, name, attrs, content): - if name != "CharString": - return - subr = self.subrClass() - subr.fromXML(name, attrs, content) - self.append(subr) - - def getItemAndSelector(self, index): - sel = None - if hasattr(self, "fdSelect"): - sel = self.fdSelect[index] - return self[index], sel - - -class SubrsIndex(GlobalSubrsIndex): - """This index contains a glyph's local subroutines. A local subroutine is a - private set of ``CharString`` data which is accessible only to the glyph to - which the index is attached.""" - - compilerClass = SubrsCompiler - - -class TopDictIndex(Index): - """This index represents the array of ``TopDict`` structures in the font - (again, usually only one entry is present). Hence the following calls are - equivalent: - - .. code:: python - - tt["CFF "].cff[0] - # - tt["CFF "].cff.topDictIndex[0] - # - - """ - - compilerClass = TopDictIndexCompiler - - def __init__(self, file=None, cff2GetGlyphOrder=None, topSize=0, isCFF2=None): - self.cff2GetGlyphOrder = cff2GetGlyphOrder - if file is not None and isCFF2: - self._isCFF2 = isCFF2 - self.items = [] - name = self.__class__.__name__ - log.log(DEBUG, "loading %s at %s", name, file.tell()) - self.file = file - count = 1 - self.items = [None] * count - self.offsets = [0, topSize] - self.offsetBase = file.tell() - # pretend we've read the whole lot - file.seek(self.offsetBase + topSize) - log.log(DEBUG, " end of %s at %s", name, file.tell()) - else: - super(TopDictIndex, self).__init__(file, isCFF2=isCFF2) - - def produceItem(self, index, data, file, offset): - top = TopDict( - self.strings, - file, - offset, - self.GlobalSubrs, - self.cff2GetGlyphOrder, - isCFF2=self._isCFF2, - ) - top.decompile(data) - return top - - def toXML(self, xmlWriter): - for i in range(len(self)): - xmlWriter.begintag("FontDict", index=i) - xmlWriter.newline() - self[i].toXML(xmlWriter) - xmlWriter.endtag("FontDict") - xmlWriter.newline() - - -class FDArrayIndex(Index): - compilerClass = FDArrayIndexCompiler - - def toXML(self, xmlWriter): - for i in range(len(self)): - xmlWriter.begintag("FontDict", index=i) - xmlWriter.newline() - self[i].toXML(xmlWriter) - xmlWriter.endtag("FontDict") - xmlWriter.newline() - - def produceItem(self, index, data, file, offset): - fontDict = FontDict( - self.strings, - file, - offset, - self.GlobalSubrs, - isCFF2=self._isCFF2, - vstore=self.vstore, - ) - fontDict.decompile(data) - return fontDict - - def fromXML(self, name, attrs, content): - if name != "FontDict": - return - fontDict = FontDict() - for element in content: - if isinstance(element, str): - continue - name, attrs, content = element - fontDict.fromXML(name, attrs, content) - self.append(fontDict) - - -class VarStoreData(object): - def __init__(self, file=None, otVarStore=None): - self.file = file - self.data = None - self.otVarStore = otVarStore - self.font = TTFont() # dummy font for the decompile function. - - def decompile(self): - if self.file: - # read data in from file. Assume position is correct. - length = readCard16(self.file) - # https://github.com/fonttools/fonttools/issues/3673 - if length == 65535: - self.data = self.file.read() - else: - self.data = self.file.read(length) - globalState = {} - reader = OTTableReader(self.data, globalState) - self.otVarStore = ot.VarStore() - self.otVarStore.decompile(reader, self.font) - self.data = None - return self - - def compile(self): - writer = OTTableWriter() - self.otVarStore.compile(writer, self.font) - # Note that this omits the initial Card16 length from the CFF2 - # VarStore data block - self.data = writer.getAllData() - - def writeXML(self, xmlWriter, name): - self.otVarStore.toXML(xmlWriter, self.font) - - def xmlRead(self, name, attrs, content, parent): - self.otVarStore = ot.VarStore() - for element in content: - if isinstance(element, tuple): - name, attrs, content = element - self.otVarStore.fromXML(name, attrs, content, self.font) - else: - pass - return None - - def __len__(self): - return len(self.data) - - def getNumRegions(self, vsIndex): - if vsIndex is None: - vsIndex = 0 - varData = self.otVarStore.VarData[vsIndex] - numRegions = varData.VarRegionCount - return numRegions - - -class FDSelect(object): - def __init__(self, file=None, numGlyphs=None, format=None): - if file: - # read data in from file - self.format = readCard8(file) - if self.format == 0: - from array import array - - self.gidArray = array("B", file.read(numGlyphs)).tolist() - elif self.format == 3: - gidArray = [None] * numGlyphs - nRanges = readCard16(file) - fd = None - prev = None - for i in range(nRanges): - first = readCard16(file) - if prev is not None: - for glyphID in range(prev, first): - gidArray[glyphID] = fd - prev = first - fd = readCard8(file) - if prev is not None: - first = readCard16(file) - for glyphID in range(prev, first): - gidArray[glyphID] = fd - self.gidArray = gidArray - elif self.format == 4: - gidArray = [None] * numGlyphs - nRanges = readCard32(file) - fd = None - prev = None - for i in range(nRanges): - first = readCard32(file) - if prev is not None: - for glyphID in range(prev, first): - gidArray[glyphID] = fd - prev = first - fd = readCard16(file) - if prev is not None: - first = readCard32(file) - for glyphID in range(prev, first): - gidArray[glyphID] = fd - self.gidArray = gidArray - else: - assert False, "unsupported FDSelect format: %s" % format - else: - # reading from XML. Make empty gidArray, and leave format as passed in. - # format is None will result in the smallest representation being used. - self.format = format - self.gidArray = [] - - def __len__(self): - return len(self.gidArray) - - def __getitem__(self, index): - return self.gidArray[index] - - def __setitem__(self, index, fdSelectValue): - self.gidArray[index] = fdSelectValue - - def append(self, fdSelectValue): - self.gidArray.append(fdSelectValue) - - -class CharStrings(object): - """The ``CharStrings`` in the font represent the instructions for drawing - each glyph. This object presents a dictionary interface to the font's - CharStrings, indexed by glyph name: - - .. code:: python - - tt["CFF "].cff[0].CharStrings["a"] - # - - See :class:`fontTools.misc.psCharStrings.T1CharString` and - :class:`fontTools.misc.psCharStrings.T2CharString` for how to decompile, - compile and interpret the glyph drawing instructions in the returned objects. - - """ - - def __init__( - self, - file, - charset, - globalSubrs, - private, - fdSelect, - fdArray, - isCFF2=None, - varStore=None, - ): - self.globalSubrs = globalSubrs - self.varStore = varStore - if file is not None: - self.charStringsIndex = SubrsIndex( - file, globalSubrs, private, fdSelect, fdArray, isCFF2=isCFF2 - ) - self.charStrings = charStrings = {} - for i in range(len(charset)): - charStrings[charset[i]] = i - # read from OTF file: charStrings.values() are indices into - # charStringsIndex. - self.charStringsAreIndexed = 1 - else: - self.charStrings = {} - # read from ttx file: charStrings.values() are actual charstrings - self.charStringsAreIndexed = 0 - self.private = private - if fdSelect is not None: - self.fdSelect = fdSelect - if fdArray is not None: - self.fdArray = fdArray - - def keys(self): - return list(self.charStrings.keys()) - - def values(self): - if self.charStringsAreIndexed: - return self.charStringsIndex - else: - return list(self.charStrings.values()) - - def has_key(self, name): - return name in self.charStrings - - __contains__ = has_key - - def __len__(self): - return len(self.charStrings) - - def __getitem__(self, name): - charString = self.charStrings[name] - if self.charStringsAreIndexed: - charString = self.charStringsIndex[charString] - return charString - - def __setitem__(self, name, charString): - if self.charStringsAreIndexed: - index = self.charStrings[name] - self.charStringsIndex[index] = charString - else: - self.charStrings[name] = charString - - def getItemAndSelector(self, name): - if self.charStringsAreIndexed: - index = self.charStrings[name] - return self.charStringsIndex.getItemAndSelector(index) - else: - if hasattr(self, "fdArray"): - if hasattr(self, "fdSelect"): - sel = self.charStrings[name].fdSelectIndex - else: - sel = 0 - else: - sel = None - return self.charStrings[name], sel - - def toXML(self, xmlWriter): - names = sorted(self.keys()) - for name in names: - charStr, fdSelectIndex = self.getItemAndSelector(name) - if charStr.needsDecompilation(): - raw = [("raw", 1)] - else: - raw = [] - if fdSelectIndex is None: - xmlWriter.begintag("CharString", [("name", name)] + raw) - else: - xmlWriter.begintag( - "CharString", - [("name", name), ("fdSelectIndex", fdSelectIndex)] + raw, - ) - xmlWriter.newline() - charStr.toXML(xmlWriter) - xmlWriter.endtag("CharString") - xmlWriter.newline() - - def fromXML(self, name, attrs, content): - for element in content: - if isinstance(element, str): - continue - name, attrs, content = element - if name != "CharString": - continue - fdID = -1 - if hasattr(self, "fdArray"): - try: - fdID = safeEval(attrs["fdSelectIndex"]) - except KeyError: - fdID = 0 - private = self.fdArray[fdID].Private - else: - private = self.private - - glyphName = attrs["name"] - charStringClass = psCharStrings.T2CharString - charString = charStringClass(private=private, globalSubrs=self.globalSubrs) - charString.fromXML(name, attrs, content) - if fdID >= 0: - charString.fdSelectIndex = fdID - self[glyphName] = charString - - -def readCard8(file): - return byteord(file.read(1)) - - -def readCard16(file): - (value,) = struct.unpack(">H", file.read(2)) - return value - - -def readCard32(file): - (value,) = struct.unpack(">L", file.read(4)) - return value - - -def writeCard8(file, value): - file.write(bytechr(value)) - - -def writeCard16(file, value): - file.write(struct.pack(">H", value)) - - -def writeCard32(file, value): - file.write(struct.pack(">L", value)) - - -def packCard8(value): - return bytechr(value) - - -def packCard16(value): - return struct.pack(">H", value) - - -def packCard32(value): - return struct.pack(">L", value) - - -def buildOperatorDict(table): - d = {} - for op, name, arg, default, conv in table: - d[op] = (name, arg) - return d - - -def buildOpcodeDict(table): - d = {} - for op, name, arg, default, conv in table: - if isinstance(op, tuple): - op = bytechr(op[0]) + bytechr(op[1]) - else: - op = bytechr(op) - d[name] = (op, arg) - return d - - -def buildOrder(table): - l = [] - for op, name, arg, default, conv in table: - l.append(name) - return l - - -def buildDefaults(table): - d = {} - for op, name, arg, default, conv in table: - if default is not None: - d[name] = default - return d - - -def buildConverters(table): - d = {} - for op, name, arg, default, conv in table: - d[name] = conv - return d - - -class SimpleConverter(object): - def read(self, parent, value): - if not hasattr(parent, "file"): - return self._read(parent, value) - file = parent.file - pos = file.tell() - try: - return self._read(parent, value) - finally: - file.seek(pos) - - def _read(self, parent, value): - return value - - def write(self, parent, value): - return value - - def xmlWrite(self, xmlWriter, name, value): - xmlWriter.simpletag(name, value=value) - xmlWriter.newline() - - def xmlRead(self, name, attrs, content, parent): - return attrs["value"] - - -class ASCIIConverter(SimpleConverter): - def _read(self, parent, value): - return tostr(value, encoding="ascii") - - def write(self, parent, value): - return tobytes(value, encoding="ascii") - - def xmlWrite(self, xmlWriter, name, value): - xmlWriter.simpletag(name, value=tostr(value, encoding="ascii")) - xmlWriter.newline() - - def xmlRead(self, name, attrs, content, parent): - return tobytes(attrs["value"], encoding=("ascii")) - - -class Latin1Converter(SimpleConverter): - def _read(self, parent, value): - return tostr(value, encoding="latin1") - - def write(self, parent, value): - return tobytes(value, encoding="latin1") - - def xmlWrite(self, xmlWriter, name, value): - value = tostr(value, encoding="latin1") - if name in ["Notice", "Copyright"]: - value = re.sub(r"[\r\n]\s+", " ", value) - xmlWriter.simpletag(name, value=value) - xmlWriter.newline() - - def xmlRead(self, name, attrs, content, parent): - return tobytes(attrs["value"], encoding=("latin1")) - - -def parseNum(s): - try: - value = int(s) - except: - value = float(s) - return value - - -def parseBlendList(s): - valueList = [] - for element in s: - if isinstance(element, str): - continue - name, attrs, content = element - blendList = attrs["value"].split() - blendList = [eval(val) for val in blendList] - valueList.append(blendList) - if len(valueList) == 1: - valueList = valueList[0] - return valueList - - -class NumberConverter(SimpleConverter): - def xmlWrite(self, xmlWriter, name, value): - if isinstance(value, list): - xmlWriter.begintag(name) - xmlWriter.newline() - xmlWriter.indent() - blendValue = " ".join([str(val) for val in value]) - xmlWriter.simpletag(kBlendDictOpName, value=blendValue) - xmlWriter.newline() - xmlWriter.dedent() - xmlWriter.endtag(name) - xmlWriter.newline() - else: - xmlWriter.simpletag(name, value=value) - xmlWriter.newline() - - def xmlRead(self, name, attrs, content, parent): - valueString = attrs.get("value", None) - if valueString is None: - value = parseBlendList(content) - else: - value = parseNum(attrs["value"]) - return value - - -class ArrayConverter(SimpleConverter): - def xmlWrite(self, xmlWriter, name, value): - if value and isinstance(value[0], list): - xmlWriter.begintag(name) - xmlWriter.newline() - xmlWriter.indent() - for valueList in value: - blendValue = " ".join([str(val) for val in valueList]) - xmlWriter.simpletag(kBlendDictOpName, value=blendValue) - xmlWriter.newline() - xmlWriter.dedent() - xmlWriter.endtag(name) - xmlWriter.newline() - else: - value = " ".join([str(val) for val in value]) - xmlWriter.simpletag(name, value=value) - xmlWriter.newline() - - def xmlRead(self, name, attrs, content, parent): - valueString = attrs.get("value", None) - if valueString is None: - valueList = parseBlendList(content) - else: - values = valueString.split() - valueList = [parseNum(value) for value in values] - return valueList - - -class TableConverter(SimpleConverter): - def xmlWrite(self, xmlWriter, name, value): - xmlWriter.begintag(name) - xmlWriter.newline() - value.toXML(xmlWriter) - xmlWriter.endtag(name) - xmlWriter.newline() - - def xmlRead(self, name, attrs, content, parent): - ob = self.getClass()() - for element in content: - if isinstance(element, str): - continue - name, attrs, content = element - ob.fromXML(name, attrs, content) - return ob - - -class PrivateDictConverter(TableConverter): - def getClass(self): - return PrivateDict - - def _read(self, parent, value): - size, offset = value - file = parent.file - isCFF2 = parent._isCFF2 - try: - vstore = parent.vstore - except AttributeError: - vstore = None - priv = PrivateDict(parent.strings, file, offset, isCFF2=isCFF2, vstore=vstore) - file.seek(offset) - data = file.read(size) - assert len(data) == size - priv.decompile(data) - return priv - - def write(self, parent, value): - return (0, 0) # dummy value - - -class SubrsConverter(TableConverter): - def getClass(self): - return SubrsIndex - - def _read(self, parent, value): - file = parent.file - isCFF2 = parent._isCFF2 - file.seek(parent.offset + value) # Offset(self) - return SubrsIndex(file, isCFF2=isCFF2) - - def write(self, parent, value): - return 0 # dummy value - - -class CharStringsConverter(TableConverter): - def _read(self, parent, value): - file = parent.file - isCFF2 = parent._isCFF2 - charset = parent.charset - varStore = getattr(parent, "VarStore", None) - globalSubrs = parent.GlobalSubrs - if hasattr(parent, "FDArray"): - fdArray = parent.FDArray - if hasattr(parent, "FDSelect"): - fdSelect = parent.FDSelect - else: - fdSelect = None - private = None - else: - fdSelect, fdArray = None, None - private = parent.Private - file.seek(value) # Offset(0) - charStrings = CharStrings( - file, - charset, - globalSubrs, - private, - fdSelect, - fdArray, - isCFF2=isCFF2, - varStore=varStore, - ) - return charStrings - - def write(self, parent, value): - return 0 # dummy value - - def xmlRead(self, name, attrs, content, parent): - if hasattr(parent, "FDArray"): - # if it is a CID-keyed font, then the private Dict is extracted from the - # parent.FDArray - fdArray = parent.FDArray - if hasattr(parent, "FDSelect"): - fdSelect = parent.FDSelect - else: - fdSelect = None - private = None - else: - # if it is a name-keyed font, then the private dict is in the top dict, - # and - # there is no fdArray. - private, fdSelect, fdArray = parent.Private, None, None - charStrings = CharStrings( - None, - None, - parent.GlobalSubrs, - private, - fdSelect, - fdArray, - varStore=getattr(parent, "VarStore", None), - ) - charStrings.fromXML(name, attrs, content) - return charStrings - - -class CharsetConverter(SimpleConverter): - def _read(self, parent, value): - isCID = hasattr(parent, "ROS") - if value > 2: - numGlyphs = parent.numGlyphs - file = parent.file - file.seek(value) - log.log(DEBUG, "loading charset at %s", value) - format = readCard8(file) - if format == 0: - charset = parseCharset0(numGlyphs, file, parent.strings, isCID) - elif format == 1 or format == 2: - charset = parseCharset(numGlyphs, file, parent.strings, isCID, format) - else: - raise NotImplementedError - assert len(charset) == numGlyphs - log.log(DEBUG, " charset end at %s", file.tell()) - # make sure glyph names are unique - allNames = {} - newCharset = [] - for glyphName in charset: - if glyphName in allNames: - # make up a new glyphName that's unique - n = allNames[glyphName] - while (glyphName + "#" + str(n)) in allNames: - n += 1 - allNames[glyphName] = n + 1 - glyphName = glyphName + "#" + str(n) - allNames[glyphName] = 1 - newCharset.append(glyphName) - charset = newCharset - else: # offset == 0 -> no charset data. - if isCID or "CharStrings" not in parent.rawDict: - # We get here only when processing fontDicts from the FDArray of - # CFF-CID fonts. Only the real topDict references the charset. - assert value == 0 - charset = None - elif value == 0: - charset = cffISOAdobeStrings - elif value == 1: - charset = cffIExpertStrings - elif value == 2: - charset = cffExpertSubsetStrings - if charset and (len(charset) != parent.numGlyphs): - charset = charset[: parent.numGlyphs] - return charset - - def write(self, parent, value): - return 0 # dummy value - - def xmlWrite(self, xmlWriter, name, value): - # XXX only write charset when not in OT/TTX context, where we - # dump charset as a separate "GlyphOrder" table. - # # xmlWriter.simpletag("charset") - xmlWriter.comment("charset is dumped separately as the 'GlyphOrder' element") - xmlWriter.newline() - - def xmlRead(self, name, attrs, content, parent): - pass - - -class CharsetCompiler(object): - def __init__(self, strings, charset, parent): - assert charset[0] == ".notdef" - isCID = hasattr(parent.dictObj, "ROS") - data0 = packCharset0(charset, isCID, strings) - data = packCharset(charset, isCID, strings) - if len(data) < len(data0): - self.data = data - else: - self.data = data0 - self.parent = parent - - def setPos(self, pos, endPos): - self.parent.rawDict["charset"] = pos - - def getDataLength(self): - return len(self.data) - - def toFile(self, file): - file.write(self.data) - - -def getStdCharSet(charset): - # check to see if we can use a predefined charset value. - predefinedCharSetVal = None - predefinedCharSets = [ - (cffISOAdobeStringCount, cffISOAdobeStrings, 0), - (cffExpertStringCount, cffIExpertStrings, 1), - (cffExpertSubsetStringCount, cffExpertSubsetStrings, 2), - ] - lcs = len(charset) - for cnt, pcs, csv in predefinedCharSets: - if predefinedCharSetVal is not None: - break - if lcs > cnt: - continue - predefinedCharSetVal = csv - for i in range(lcs): - if charset[i] != pcs[i]: - predefinedCharSetVal = None - break - return predefinedCharSetVal - - -def getCIDfromName(name, strings): - return int(name[3:]) - - -def getSIDfromName(name, strings): - return strings.getSID(name) - - -def packCharset0(charset, isCID, strings): - fmt = 0 - data = [packCard8(fmt)] - if isCID: - getNameID = getCIDfromName - else: - getNameID = getSIDfromName - - for name in charset[1:]: - data.append(packCard16(getNameID(name, strings))) - return bytesjoin(data) - - -def packCharset(charset, isCID, strings): - fmt = 1 - ranges = [] - first = None - end = 0 - if isCID: - getNameID = getCIDfromName - else: - getNameID = getSIDfromName - - for name in charset[1:]: - SID = getNameID(name, strings) - if first is None: - first = SID - elif end + 1 != SID: - nLeft = end - first - if nLeft > 255: - fmt = 2 - ranges.append((first, nLeft)) - first = SID - end = SID - if end: - nLeft = end - first - if nLeft > 255: - fmt = 2 - ranges.append((first, nLeft)) - - data = [packCard8(fmt)] - if fmt == 1: - nLeftFunc = packCard8 - else: - nLeftFunc = packCard16 - for first, nLeft in ranges: - data.append(packCard16(first) + nLeftFunc(nLeft)) - return bytesjoin(data) - - -def parseCharset0(numGlyphs, file, strings, isCID): - charset = [".notdef"] - if isCID: - for i in range(numGlyphs - 1): - CID = readCard16(file) - charset.append("cid" + str(CID).zfill(5)) - else: - for i in range(numGlyphs - 1): - SID = readCard16(file) - charset.append(strings[SID]) - return charset - - -def parseCharset(numGlyphs, file, strings, isCID, fmt): - charset = [".notdef"] - count = 1 - if fmt == 1: - nLeftFunc = readCard8 - else: - nLeftFunc = readCard16 - while count < numGlyphs: - first = readCard16(file) - nLeft = nLeftFunc(file) - if isCID: - for CID in range(first, first + nLeft + 1): - charset.append("cid" + str(CID).zfill(5)) - else: - for SID in range(first, first + nLeft + 1): - charset.append(strings[SID]) - count = count + nLeft + 1 - return charset - - -class EncodingCompiler(object): - def __init__(self, strings, encoding, parent): - assert not isinstance(encoding, str) - data0 = packEncoding0(parent.dictObj.charset, encoding, parent.strings) - data1 = packEncoding1(parent.dictObj.charset, encoding, parent.strings) - if len(data0) < len(data1): - self.data = data0 - else: - self.data = data1 - self.parent = parent - - def setPos(self, pos, endPos): - self.parent.rawDict["Encoding"] = pos - - def getDataLength(self): - return len(self.data) - - def toFile(self, file): - file.write(self.data) - - -class EncodingConverter(SimpleConverter): - def _read(self, parent, value): - if value == 0: - return "StandardEncoding" - elif value == 1: - return "ExpertEncoding" - else: - assert value > 1 - file = parent.file - file.seek(value) - log.log(DEBUG, "loading Encoding at %s", value) - fmt = readCard8(file) - haveSupplement = fmt & 0x80 - if haveSupplement: - raise NotImplementedError("Encoding supplements are not yet supported") - fmt = fmt & 0x7F - if fmt == 0: - encoding = parseEncoding0( - parent.charset, file, haveSupplement, parent.strings - ) - elif fmt == 1: - encoding = parseEncoding1( - parent.charset, file, haveSupplement, parent.strings - ) - return encoding - - def write(self, parent, value): - if value == "StandardEncoding": - return 0 - elif value == "ExpertEncoding": - return 1 - return 0 # dummy value - - def xmlWrite(self, xmlWriter, name, value): - if value in ("StandardEncoding", "ExpertEncoding"): - xmlWriter.simpletag(name, name=value) - xmlWriter.newline() - return - xmlWriter.begintag(name) - xmlWriter.newline() - for code in range(len(value)): - glyphName = value[code] - if glyphName != ".notdef": - xmlWriter.simpletag("map", code=hex(code), name=glyphName) - xmlWriter.newline() - xmlWriter.endtag(name) - xmlWriter.newline() - - def xmlRead(self, name, attrs, content, parent): - if "name" in attrs: - return attrs["name"] - encoding = [".notdef"] * 256 - for element in content: - if isinstance(element, str): - continue - name, attrs, content = element - code = safeEval(attrs["code"]) - glyphName = attrs["name"] - encoding[code] = glyphName - return encoding - - -def parseEncoding0(charset, file, haveSupplement, strings): - nCodes = readCard8(file) - encoding = [".notdef"] * 256 - for glyphID in range(1, nCodes + 1): - code = readCard8(file) - if code != 0: - encoding[code] = charset[glyphID] - return encoding - - -def parseEncoding1(charset, file, haveSupplement, strings): - nRanges = readCard8(file) - encoding = [".notdef"] * 256 - glyphID = 1 - for i in range(nRanges): - code = readCard8(file) - nLeft = readCard8(file) - for glyphID in range(glyphID, glyphID + nLeft + 1): - encoding[code] = charset[glyphID] - code = code + 1 - glyphID = glyphID + 1 - return encoding - - -def packEncoding0(charset, encoding, strings): - fmt = 0 - m = {} - for code in range(len(encoding)): - name = encoding[code] - if name != ".notdef": - m[name] = code - codes = [] - for name in charset[1:]: - code = m.get(name) - codes.append(code) - - while codes and codes[-1] is None: - codes.pop() - - data = [packCard8(fmt), packCard8(len(codes))] - for code in codes: - if code is None: - code = 0 - data.append(packCard8(code)) - return bytesjoin(data) - - -def packEncoding1(charset, encoding, strings): - fmt = 1 - m = {} - for code in range(len(encoding)): - name = encoding[code] - if name != ".notdef": - m[name] = code - ranges = [] - first = None - end = 0 - for name in charset[1:]: - code = m.get(name, -1) - if first is None: - first = code - elif end + 1 != code: - nLeft = end - first - ranges.append((first, nLeft)) - first = code - end = code - nLeft = end - first - ranges.append((first, nLeft)) - - # remove unencoded glyphs at the end. - while ranges and ranges[-1][0] == -1: - ranges.pop() - - data = [packCard8(fmt), packCard8(len(ranges))] - for first, nLeft in ranges: - if first == -1: # unencoded - first = 0 - data.append(packCard8(first) + packCard8(nLeft)) - return bytesjoin(data) - - -class FDArrayConverter(TableConverter): - def _read(self, parent, value): - try: - vstore = parent.VarStore - except AttributeError: - vstore = None - file = parent.file - isCFF2 = parent._isCFF2 - file.seek(value) - fdArray = FDArrayIndex(file, isCFF2=isCFF2) - fdArray.vstore = vstore - fdArray.strings = parent.strings - fdArray.GlobalSubrs = parent.GlobalSubrs - return fdArray - - def write(self, parent, value): - return 0 # dummy value - - def xmlRead(self, name, attrs, content, parent): - fdArray = FDArrayIndex() - for element in content: - if isinstance(element, str): - continue - name, attrs, content = element - fdArray.fromXML(name, attrs, content) - return fdArray - - -class FDSelectConverter(SimpleConverter): - def _read(self, parent, value): - file = parent.file - file.seek(value) - fdSelect = FDSelect(file, parent.numGlyphs) - return fdSelect - - def write(self, parent, value): - return 0 # dummy value - - # The FDSelect glyph data is written out to XML in the charstring keys, - # so we write out only the format selector - def xmlWrite(self, xmlWriter, name, value): - xmlWriter.simpletag(name, [("format", value.format)]) - xmlWriter.newline() - - def xmlRead(self, name, attrs, content, parent): - fmt = safeEval(attrs["format"]) - file = None - numGlyphs = None - fdSelect = FDSelect(file, numGlyphs, fmt) - return fdSelect - - -class VarStoreConverter(SimpleConverter): - def _read(self, parent, value): - file = parent.file - file.seek(value) - varStore = VarStoreData(file) - varStore.decompile() - return varStore - - def write(self, parent, value): - return 0 # dummy value - - def xmlWrite(self, xmlWriter, name, value): - value.writeXML(xmlWriter, name) - - def xmlRead(self, name, attrs, content, parent): - varStore = VarStoreData() - varStore.xmlRead(name, attrs, content, parent) - return varStore - - -def packFDSelect0(fdSelectArray): - fmt = 0 - data = [packCard8(fmt)] - for index in fdSelectArray: - data.append(packCard8(index)) - return bytesjoin(data) - - -def packFDSelect3(fdSelectArray): - fmt = 3 - fdRanges = [] - lenArray = len(fdSelectArray) - lastFDIndex = -1 - for i in range(lenArray): - fdIndex = fdSelectArray[i] - if lastFDIndex != fdIndex: - fdRanges.append([i, fdIndex]) - lastFDIndex = fdIndex - sentinelGID = i + 1 - - data = [packCard8(fmt)] - data.append(packCard16(len(fdRanges))) - for fdRange in fdRanges: - data.append(packCard16(fdRange[0])) - data.append(packCard8(fdRange[1])) - data.append(packCard16(sentinelGID)) - return bytesjoin(data) - - -def packFDSelect4(fdSelectArray): - fmt = 4 - fdRanges = [] - lenArray = len(fdSelectArray) - lastFDIndex = -1 - for i in range(lenArray): - fdIndex = fdSelectArray[i] - if lastFDIndex != fdIndex: - fdRanges.append([i, fdIndex]) - lastFDIndex = fdIndex - sentinelGID = i + 1 - - data = [packCard8(fmt)] - data.append(packCard32(len(fdRanges))) - for fdRange in fdRanges: - data.append(packCard32(fdRange[0])) - data.append(packCard16(fdRange[1])) - data.append(packCard32(sentinelGID)) - return bytesjoin(data) - - -class FDSelectCompiler(object): - def __init__(self, fdSelect, parent): - fmt = fdSelect.format - fdSelectArray = fdSelect.gidArray - if fmt == 0: - self.data = packFDSelect0(fdSelectArray) - elif fmt == 3: - self.data = packFDSelect3(fdSelectArray) - elif fmt == 4: - self.data = packFDSelect4(fdSelectArray) - else: - # choose smaller of the two formats - data0 = packFDSelect0(fdSelectArray) - data3 = packFDSelect3(fdSelectArray) - if len(data0) < len(data3): - self.data = data0 - fdSelect.format = 0 - else: - self.data = data3 - fdSelect.format = 3 - - self.parent = parent - - def setPos(self, pos, endPos): - self.parent.rawDict["FDSelect"] = pos - - def getDataLength(self): - return len(self.data) - - def toFile(self, file): - file.write(self.data) - - -class VarStoreCompiler(object): - def __init__(self, varStoreData, parent): - self.parent = parent - if not varStoreData.data: - varStoreData.compile() - varStoreDataLen = min(0xFFFF, len(varStoreData.data)) - data = [packCard16(varStoreDataLen), varStoreData.data] - self.data = bytesjoin(data) - - def setPos(self, pos, endPos): - self.parent.rawDict["VarStore"] = pos - - def getDataLength(self): - return len(self.data) - - def toFile(self, file): - file.write(self.data) - - -class ROSConverter(SimpleConverter): - def xmlWrite(self, xmlWriter, name, value): - registry, order, supplement = value - xmlWriter.simpletag( - name, - [ - ("Registry", tostr(registry)), - ("Order", tostr(order)), - ("Supplement", supplement), - ], - ) - xmlWriter.newline() - - def xmlRead(self, name, attrs, content, parent): - return (attrs["Registry"], attrs["Order"], safeEval(attrs["Supplement"])) - - -topDictOperators = [ - # opcode name argument type default converter - (25, "maxstack", "number", None, None), - ((12, 30), "ROS", ("SID", "SID", "number"), None, ROSConverter()), - ((12, 20), "SyntheticBase", "number", None, None), - (0, "version", "SID", None, None), - (1, "Notice", "SID", None, Latin1Converter()), - ((12, 0), "Copyright", "SID", None, Latin1Converter()), - (2, "FullName", "SID", None, Latin1Converter()), - ((12, 38), "FontName", "SID", None, Latin1Converter()), - (3, "FamilyName", "SID", None, Latin1Converter()), - (4, "Weight", "SID", None, None), - ((12, 1), "isFixedPitch", "number", 0, None), - ((12, 2), "ItalicAngle", "number", 0, None), - ((12, 3), "UnderlinePosition", "number", -100, None), - ((12, 4), "UnderlineThickness", "number", 50, None), - ((12, 5), "PaintType", "number", 0, None), - ((12, 6), "CharstringType", "number", 2, None), - ((12, 7), "FontMatrix", "array", [0.001, 0, 0, 0.001, 0, 0], None), - (13, "UniqueID", "number", None, None), - (5, "FontBBox", "array", [0, 0, 0, 0], None), - ((12, 8), "StrokeWidth", "number", 0, None), - (14, "XUID", "array", None, None), - ((12, 21), "PostScript", "SID", None, None), - ((12, 22), "BaseFontName", "SID", None, None), - ((12, 23), "BaseFontBlend", "delta", None, None), - ((12, 31), "CIDFontVersion", "number", 0, None), - ((12, 32), "CIDFontRevision", "number", 0, None), - ((12, 33), "CIDFontType", "number", 0, None), - ((12, 34), "CIDCount", "number", 8720, None), - (15, "charset", "number", None, CharsetConverter()), - ((12, 35), "UIDBase", "number", None, None), - (16, "Encoding", "number", 0, EncodingConverter()), - (18, "Private", ("number", "number"), None, PrivateDictConverter()), - ((12, 37), "FDSelect", "number", None, FDSelectConverter()), - ((12, 36), "FDArray", "number", None, FDArrayConverter()), - (17, "CharStrings", "number", None, CharStringsConverter()), - (24, "VarStore", "number", None, VarStoreConverter()), -] - -topDictOperators2 = [ - # opcode name argument type default converter - (25, "maxstack", "number", None, None), - ((12, 7), "FontMatrix", "array", [0.001, 0, 0, 0.001, 0, 0], None), - ((12, 37), "FDSelect", "number", None, FDSelectConverter()), - ((12, 36), "FDArray", "number", None, FDArrayConverter()), - (17, "CharStrings", "number", None, CharStringsConverter()), - (24, "VarStore", "number", None, VarStoreConverter()), -] - -# Note! FDSelect and FDArray must both preceed CharStrings in the output XML build order, -# in order for the font to compile back from xml. - -kBlendDictOpName = "blend" -blendOp = 23 - -privateDictOperators = [ - # opcode name argument type default converter - (22, "vsindex", "number", None, None), - ( - blendOp, - kBlendDictOpName, - "blendList", - None, - None, - ), # This is for reading to/from XML: it not written to CFF. - (6, "BlueValues", "delta", None, None), - (7, "OtherBlues", "delta", None, None), - (8, "FamilyBlues", "delta", None, None), - (9, "FamilyOtherBlues", "delta", None, None), - ((12, 9), "BlueScale", "number", 0.039625, None), - ((12, 10), "BlueShift", "number", 7, None), - ((12, 11), "BlueFuzz", "number", 1, None), - (10, "StdHW", "number", None, None), - (11, "StdVW", "number", None, None), - ((12, 12), "StemSnapH", "delta", None, None), - ((12, 13), "StemSnapV", "delta", None, None), - ((12, 14), "ForceBold", "number", 0, None), - ((12, 15), "ForceBoldThreshold", "number", None, None), # deprecated - ((12, 16), "lenIV", "number", None, None), # deprecated - ((12, 17), "LanguageGroup", "number", 0, None), - ((12, 18), "ExpansionFactor", "number", 0.06, None), - ((12, 19), "initialRandomSeed", "number", 0, None), - (20, "defaultWidthX", "number", 0, None), - (21, "nominalWidthX", "number", 0, None), - (19, "Subrs", "number", None, SubrsConverter()), -] - -privateDictOperators2 = [ - # opcode name argument type default converter - (22, "vsindex", "number", None, None), - ( - blendOp, - kBlendDictOpName, - "blendList", - None, - None, - ), # This is for reading to/from XML: it not written to CFF. - (6, "BlueValues", "delta", None, None), - (7, "OtherBlues", "delta", None, None), - (8, "FamilyBlues", "delta", None, None), - (9, "FamilyOtherBlues", "delta", None, None), - ((12, 9), "BlueScale", "number", 0.039625, None), - ((12, 10), "BlueShift", "number", 7, None), - ((12, 11), "BlueFuzz", "number", 1, None), - (10, "StdHW", "number", None, None), - (11, "StdVW", "number", None, None), - ((12, 12), "StemSnapH", "delta", None, None), - ((12, 13), "StemSnapV", "delta", None, None), - ((12, 17), "LanguageGroup", "number", 0, None), - ((12, 18), "ExpansionFactor", "number", 0.06, None), - (19, "Subrs", "number", None, SubrsConverter()), -] - - -def addConverters(table): - for i in range(len(table)): - op, name, arg, default, conv = table[i] - if conv is not None: - continue - if arg in ("delta", "array"): - conv = ArrayConverter() - elif arg == "number": - conv = NumberConverter() - elif arg == "SID": - conv = ASCIIConverter() - elif arg == "blendList": - conv = None - else: - assert False - table[i] = op, name, arg, default, conv - - -addConverters(privateDictOperators) -addConverters(topDictOperators) - - -class TopDictDecompiler(psCharStrings.DictDecompiler): - operators = buildOperatorDict(topDictOperators) - - -class PrivateDictDecompiler(psCharStrings.DictDecompiler): - operators = buildOperatorDict(privateDictOperators) - - -class DictCompiler(object): - maxBlendStack = 0 - - def __init__(self, dictObj, strings, parent, isCFF2=None): - if strings: - assert isinstance(strings, IndexedStrings) - if isCFF2 is None and hasattr(parent, "isCFF2"): - isCFF2 = parent.isCFF2 - assert isCFF2 is not None - self.isCFF2 = isCFF2 - self.dictObj = dictObj - self.strings = strings - self.parent = parent - rawDict = {} - for name in dictObj.order: - value = getattr(dictObj, name, None) - if value is None: - continue - conv = dictObj.converters[name] - value = conv.write(dictObj, value) - if value == dictObj.defaults.get(name): - continue - rawDict[name] = value - self.rawDict = rawDict - - def setPos(self, pos, endPos): - pass - - def getDataLength(self): - return len(self.compile("getDataLength")) - - def compile(self, reason): - log.log(DEBUG, "-- compiling %s for %s", self.__class__.__name__, reason) - rawDict = self.rawDict - data = [] - for name in self.dictObj.order: - value = rawDict.get(name) - if value is None: - continue - op, argType = self.opcodes[name] - if isinstance(argType, tuple): - l = len(argType) - assert len(value) == l, "value doesn't match arg type" - for i in range(l): - arg = argType[i] - v = value[i] - arghandler = getattr(self, "arg_" + arg) - data.append(arghandler(v)) - else: - arghandler = getattr(self, "arg_" + argType) - data.append(arghandler(value)) - data.append(op) - data = bytesjoin(data) - return data - - def toFile(self, file): - data = self.compile("toFile") - file.write(data) - - def arg_number(self, num): - if isinstance(num, list): - data = [encodeNumber(val) for val in num] - data.append(encodeNumber(1)) - data.append(bytechr(blendOp)) - datum = bytesjoin(data) - else: - datum = encodeNumber(num) - return datum - - def arg_SID(self, s): - return psCharStrings.encodeIntCFF(self.strings.getSID(s)) - - def arg_array(self, value): - data = [] - for num in value: - data.append(self.arg_number(num)) - return bytesjoin(data) - - def arg_delta(self, value): - if not value: - return b"" - val0 = value[0] - if isinstance(val0, list): - data = self.arg_delta_blend(value) - else: - out = [] - last = 0 - for v in value: - out.append(v - last) - last = v - data = [] - for num in out: - data.append(encodeNumber(num)) - return bytesjoin(data) - - def arg_delta_blend(self, value): - """A delta list with blend lists has to be *all* blend lists. - - The value is a list is arranged as follows:: - - [ - [V0, d0..dn] - [V1, d0..dn] - ... - [Vm, d0..dn] - ] - - ``V`` is the absolute coordinate value from the default font, and ``d0-dn`` - are the delta values from the *n* regions. Each ``V`` is an absolute - coordinate from the default font. - - We want to return a list:: - - [ - [v0, v1..vm] - [d0..dn] - ... - [d0..dn] - numBlends - blendOp - ] - - where each ``v`` is relative to the previous default font value. - """ - numMasters = len(value[0]) - numBlends = len(value) - numStack = (numBlends * numMasters) + 1 - if numStack > self.maxBlendStack: - # Figure out the max number of value we can blend - # and divide this list up into chunks of that size. - - numBlendValues = int((self.maxBlendStack - 1) / numMasters) - out = [] - while True: - numVal = min(len(value), numBlendValues) - if numVal == 0: - break - valList = value[0:numVal] - out1 = self.arg_delta_blend(valList) - out.extend(out1) - value = value[numVal:] - else: - firstList = [0] * numBlends - deltaList = [None] * numBlends - i = 0 - prevVal = 0 - while i < numBlends: - # For PrivateDict BlueValues, the default font - # values are absolute, not relative. - # Must convert these back to relative coordinates - # before writing to CFF2. - defaultValue = value[i][0] - firstList[i] = defaultValue - prevVal - prevVal = defaultValue - deltaList[i] = value[i][1:] - i += 1 - - relValueList = firstList - for blendList in deltaList: - relValueList.extend(blendList) - out = [encodeNumber(val) for val in relValueList] - out.append(encodeNumber(numBlends)) - out.append(bytechr(blendOp)) - return out - - -def encodeNumber(num): - if isinstance(num, float): - return psCharStrings.encodeFloat(num) - else: - return psCharStrings.encodeIntCFF(num) - - -class TopDictCompiler(DictCompiler): - opcodes = buildOpcodeDict(topDictOperators) - - def getChildren(self, strings): - isCFF2 = self.isCFF2 - children = [] - if self.dictObj.cff2GetGlyphOrder is None: - if hasattr(self.dictObj, "charset") and self.dictObj.charset: - if hasattr(self.dictObj, "ROS"): # aka isCID - charsetCode = None - else: - charsetCode = getStdCharSet(self.dictObj.charset) - if charsetCode is None: - children.append( - CharsetCompiler(strings, self.dictObj.charset, self) - ) - else: - self.rawDict["charset"] = charsetCode - if hasattr(self.dictObj, "Encoding") and self.dictObj.Encoding: - encoding = self.dictObj.Encoding - if not isinstance(encoding, str): - children.append(EncodingCompiler(strings, encoding, self)) - else: - if hasattr(self.dictObj, "VarStore"): - varStoreData = self.dictObj.VarStore - varStoreComp = VarStoreCompiler(varStoreData, self) - children.append(varStoreComp) - if hasattr(self.dictObj, "FDSelect"): - # I have not yet supported merging a ttx CFF-CID font, as there are - # interesting issues about merging the FDArrays. Here I assume that - # either the font was read from XML, and the FDSelect indices are all - # in the charstring data, or the FDSelect array is already fully defined. - fdSelect = self.dictObj.FDSelect - # probably read in from XML; assume fdIndex in CharString data - if len(fdSelect) == 0: - charStrings = self.dictObj.CharStrings - for name in self.dictObj.charset: - fdSelect.append(charStrings[name].fdSelectIndex) - fdSelectComp = FDSelectCompiler(fdSelect, self) - children.append(fdSelectComp) - if hasattr(self.dictObj, "CharStrings"): - items = [] - charStrings = self.dictObj.CharStrings - for name in self.dictObj.charset: - items.append(charStrings[name]) - charStringsComp = CharStringsCompiler(items, strings, self, isCFF2=isCFF2) - children.append(charStringsComp) - if hasattr(self.dictObj, "FDArray"): - # I have not yet supported merging a ttx CFF-CID font, as there are - # interesting issues about merging the FDArrays. Here I assume that the - # FDArray info is correct and complete. - fdArrayIndexComp = self.dictObj.FDArray.getCompiler(strings, self) - children.append(fdArrayIndexComp) - children.extend(fdArrayIndexComp.getChildren(strings)) - if hasattr(self.dictObj, "Private"): - privComp = self.dictObj.Private.getCompiler(strings, self) - children.append(privComp) - children.extend(privComp.getChildren(strings)) - return children - - -class FontDictCompiler(DictCompiler): - opcodes = buildOpcodeDict(topDictOperators) - - def __init__(self, dictObj, strings, parent, isCFF2=None): - super(FontDictCompiler, self).__init__(dictObj, strings, parent, isCFF2=isCFF2) - # - # We now take some effort to detect if there were any key/value pairs - # supplied that were ignored in the FontDict context, and issue a warning - # for those cases. - # - ignoredNames = [] - dictObj = self.dictObj - for name in sorted(set(dictObj.converters) - set(dictObj.order)): - if name in dictObj.rawDict: - # The font was directly read from binary. In this - # case, we want to report *all* "useless" key/value - # pairs that are in the font, not just the ones that - # are different from the default. - ignoredNames.append(name) - else: - # The font was probably read from a TTX file. We only - # warn about keys whos value is not the default. The - # ones that have the default value will not be written - # to binary anyway. - default = dictObj.defaults.get(name) - if default is not None: - conv = dictObj.converters[name] - default = conv.read(dictObj, default) - if getattr(dictObj, name, None) != default: - ignoredNames.append(name) - if ignoredNames: - log.warning( - "Some CFF FDArray/FontDict keys were ignored upon compile: " - + " ".join(sorted(ignoredNames)) - ) - - def getChildren(self, strings): - children = [] - if hasattr(self.dictObj, "Private"): - privComp = self.dictObj.Private.getCompiler(strings, self) - children.append(privComp) - children.extend(privComp.getChildren(strings)) - return children - - -class PrivateDictCompiler(DictCompiler): - maxBlendStack = maxStackLimit - opcodes = buildOpcodeDict(privateDictOperators) - - def setPos(self, pos, endPos): - size = endPos - pos - self.parent.rawDict["Private"] = size, pos - self.pos = pos - - def getChildren(self, strings): - children = [] - if hasattr(self.dictObj, "Subrs"): - children.append(self.dictObj.Subrs.getCompiler(strings, self)) - return children - - -class BaseDict(object): - def __init__(self, strings=None, file=None, offset=None, isCFF2=None): - assert (isCFF2 is None) == (file is None) - self.rawDict = {} - self.skipNames = [] - self.strings = strings - if file is None: - return - self._isCFF2 = isCFF2 - self.file = file - if offset is not None: - log.log(DEBUG, "loading %s at %s", self.__class__.__name__, offset) - self.offset = offset - - def decompile(self, data): - log.log(DEBUG, " length %s is %d", self.__class__.__name__, len(data)) - dec = self.decompilerClass(self.strings, self) - dec.decompile(data) - self.rawDict = dec.getDict() - self.postDecompile() - - def postDecompile(self): - pass - - def getCompiler(self, strings, parent, isCFF2=None): - return self.compilerClass(self, strings, parent, isCFF2=isCFF2) - - def __getattr__(self, name): - if name[:2] == name[-2:] == "__": - # to make deepcopy() and pickle.load() work, we need to signal with - # AttributeError that dunder methods like '__deepcopy__' or '__getstate__' - # aren't implemented. For more details, see: - # https://github.com/fonttools/fonttools/pull/1488 - raise AttributeError(name) - value = self.rawDict.get(name, None) - if value is None: - value = self.defaults.get(name) - if value is None: - raise AttributeError(name) - conv = self.converters[name] - value = conv.read(self, value) - setattr(self, name, value) - return value - - def toXML(self, xmlWriter): - for name in self.order: - if name in self.skipNames: - continue - value = getattr(self, name, None) - # XXX For "charset" we never skip calling xmlWrite even if the - # value is None, so we always write the following XML comment: - # - # - # - # Charset is None when 'CFF ' table is imported from XML into an - # empty TTFont(). By writing this comment all the time, we obtain - # the same XML output whether roundtripping XML-to-XML or - # dumping binary-to-XML - if value is None and name != "charset": - continue - conv = self.converters[name] - conv.xmlWrite(xmlWriter, name, value) - ignoredNames = set(self.rawDict) - set(self.order) - if ignoredNames: - xmlWriter.comment( - "some keys were ignored: %s" % " ".join(sorted(ignoredNames)) - ) - xmlWriter.newline() - - def fromXML(self, name, attrs, content): - conv = self.converters[name] - value = conv.xmlRead(name, attrs, content, self) - setattr(self, name, value) - - -class TopDict(BaseDict): - """The ``TopDict`` represents the top-level dictionary holding font - information. CFF2 tables contain a restricted set of top-level entries - as described `here `_, - but CFF tables may contain a wider range of information. This information - can be accessed through attributes or through the dictionary returned - through the ``rawDict`` property: - - .. code:: python - - font = tt["CFF "].cff[0] - font.FamilyName - # 'Linux Libertine O' - font.rawDict["FamilyName"] - # 'Linux Libertine O' - - More information is available in the CFF file's private dictionary, accessed - via the ``Private`` property: - - .. code:: python - - tt["CFF "].cff[0].Private.BlueValues - # [-15, 0, 515, 515, 666, 666] - - """ - - defaults = buildDefaults(topDictOperators) - converters = buildConverters(topDictOperators) - compilerClass = TopDictCompiler - order = buildOrder(topDictOperators) - decompilerClass = TopDictDecompiler - - def __init__( - self, - strings=None, - file=None, - offset=None, - GlobalSubrs=None, - cff2GetGlyphOrder=None, - isCFF2=None, - ): - super(TopDict, self).__init__(strings, file, offset, isCFF2=isCFF2) - self.cff2GetGlyphOrder = cff2GetGlyphOrder - self.GlobalSubrs = GlobalSubrs - if isCFF2: - self.defaults = buildDefaults(topDictOperators2) - self.charset = cff2GetGlyphOrder() - self.order = buildOrder(topDictOperators2) - else: - self.defaults = buildDefaults(topDictOperators) - self.order = buildOrder(topDictOperators) - - def getGlyphOrder(self): - """Returns a list of glyph names in the CFF font.""" - return self.charset - - def postDecompile(self): - offset = self.rawDict.get("CharStrings") - if offset is None: - return - # get the number of glyphs beforehand. - self.file.seek(offset) - if self._isCFF2: - self.numGlyphs = readCard32(self.file) - else: - self.numGlyphs = readCard16(self.file) - - def toXML(self, xmlWriter): - if hasattr(self, "CharStrings"): - self.decompileAllCharStrings() - if hasattr(self, "ROS"): - self.skipNames = ["Encoding"] - if not hasattr(self, "ROS") or not hasattr(self, "CharStrings"): - # these values have default values, but I only want them to show up - # in CID fonts. - self.skipNames = [ - "CIDFontVersion", - "CIDFontRevision", - "CIDFontType", - "CIDCount", - ] - BaseDict.toXML(self, xmlWriter) - - def decompileAllCharStrings(self): - # Make sure that all the Private Dicts have been instantiated. - for i, charString in enumerate(self.CharStrings.values()): - try: - charString.decompile() - except: - log.error("Error in charstring %s", i) - raise - - def recalcFontBBox(self): - fontBBox = None - for charString in self.CharStrings.values(): - bounds = charString.calcBounds(self.CharStrings) - if bounds is not None: - if fontBBox is not None: - fontBBox = unionRect(fontBBox, bounds) - else: - fontBBox = bounds - - if fontBBox is None: - self.FontBBox = self.defaults["FontBBox"][:] - else: - self.FontBBox = list(intRect(fontBBox)) - - -class FontDict(BaseDict): - # - # Since fonttools used to pass a lot of fields that are not relevant in the FDArray - # FontDict, there are 'ttx' files in the wild that contain all these. These got in - # the ttx files because fonttools writes explicit values for all the TopDict default - # values. These are not actually illegal in the context of an FDArray FontDict - you - # can legally, per spec, put any arbitrary key/value pair in a FontDict - but are - # useless since current major company CFF interpreters ignore anything but the set - # listed in this file. So, we just silently skip them. An exception is Weight: this - # is not used by any interpreter, but some foundries have asked that this be - # supported in FDArray FontDicts just to preserve information about the design when - # the font is being inspected. - # - # On top of that, there are fonts out there that contain such useless FontDict values. - # - # By subclassing TopDict, we *allow* all key/values from TopDict, both when reading - # from binary or when reading from XML, but by overriding `order` with a limited - # list of names, we ensure that only the useful names ever get exported to XML and - # ever get compiled into the binary font. - # - # We override compilerClass so we can warn about "useless" key/value pairs, either - # from the original binary font or from TTX input. - # - # See: - # - https://github.com/fonttools/fonttools/issues/740 - # - https://github.com/fonttools/fonttools/issues/601 - # - https://github.com/adobe-type-tools/afdko/issues/137 - # - defaults = {} - converters = buildConverters(topDictOperators) - compilerClass = FontDictCompiler - orderCFF = ["FontName", "FontMatrix", "Weight", "Private"] - orderCFF2 = ["Private"] - decompilerClass = TopDictDecompiler - - def __init__( - self, - strings=None, - file=None, - offset=None, - GlobalSubrs=None, - isCFF2=None, - vstore=None, - ): - super(FontDict, self).__init__(strings, file, offset, isCFF2=isCFF2) - self.vstore = vstore - self.setCFF2(isCFF2) - - def setCFF2(self, isCFF2): - # isCFF2 may be None. - if isCFF2: - self.order = self.orderCFF2 - self._isCFF2 = True - else: - self.order = self.orderCFF - self._isCFF2 = False - - -class PrivateDict(BaseDict): - defaults = buildDefaults(privateDictOperators) - converters = buildConverters(privateDictOperators) - order = buildOrder(privateDictOperators) - decompilerClass = PrivateDictDecompiler - compilerClass = PrivateDictCompiler - - def __init__(self, strings=None, file=None, offset=None, isCFF2=None, vstore=None): - super(PrivateDict, self).__init__(strings, file, offset, isCFF2=isCFF2) - self.vstore = vstore - if isCFF2: - self.defaults = buildDefaults(privateDictOperators2) - self.order = buildOrder(privateDictOperators2) - # Provide dummy values. This avoids needing to provide - # an isCFF2 state in a lot of places. - self.nominalWidthX = self.defaultWidthX = None - self._isCFF2 = True - else: - self.defaults = buildDefaults(privateDictOperators) - self.order = buildOrder(privateDictOperators) - self._isCFF2 = False - - @property - def in_cff2(self): - return self._isCFF2 - - def getNumRegions(self, vi=None): # called from misc/psCharStrings.py - # if getNumRegions is being called, we can assume that VarStore exists. - if vi is None: - if hasattr(self, "vsindex"): - vi = self.vsindex - else: - vi = 0 - numRegions = self.vstore.getNumRegions(vi) - return numRegions - - -class IndexedStrings(object): - """SID -> string mapping.""" - - def __init__(self, file=None): - if file is None: - strings = [] - else: - strings = [tostr(s, encoding="latin1") for s in Index(file, isCFF2=False)] - self.strings = strings - - def getCompiler(self): - return IndexedStringsCompiler(self, None, self, isCFF2=False) - - def __len__(self): - return len(self.strings) - - def __getitem__(self, SID): - if SID < cffStandardStringCount: - return cffStandardStrings[SID] - else: - return self.strings[SID - cffStandardStringCount] - - def getSID(self, s): - if not hasattr(self, "stringMapping"): - self.buildStringMapping() - s = tostr(s, encoding="latin1") - if s in cffStandardStringMapping: - SID = cffStandardStringMapping[s] - elif s in self.stringMapping: - SID = self.stringMapping[s] - else: - SID = len(self.strings) + cffStandardStringCount - self.strings.append(s) - self.stringMapping[s] = SID - return SID - - def getStrings(self): - return self.strings - - def buildStringMapping(self): - self.stringMapping = {} - for index in range(len(self.strings)): - self.stringMapping[self.strings[index]] = index + cffStandardStringCount - - -# The 391 Standard Strings as used in the CFF format. -# from Adobe Technical None #5176, version 1.0, 18 March 1998 - -cffStandardStrings = [ - ".notdef", - "space", - "exclam", - "quotedbl", - "numbersign", - "dollar", - "percent", - "ampersand", - "quoteright", - "parenleft", - "parenright", - "asterisk", - "plus", - "comma", - "hyphen", - "period", - "slash", - "zero", - "one", - "two", - "three", - "four", - "five", - "six", - "seven", - "eight", - "nine", - "colon", - "semicolon", - "less", - "equal", - "greater", - "question", - "at", - "A", - "B", - "C", - "D", - "E", - "F", - "G", - "H", - "I", - "J", - "K", - "L", - "M", - "N", - "O", - "P", - "Q", - "R", - "S", - "T", - "U", - "V", - "W", - "X", - "Y", - "Z", - "bracketleft", - "backslash", - "bracketright", - "asciicircum", - "underscore", - "quoteleft", - "a", - "b", - "c", - "d", - "e", - "f", - "g", - "h", - "i", - "j", - "k", - "l", - "m", - "n", - "o", - "p", - "q", - "r", - "s", - "t", - "u", - "v", - "w", - "x", - "y", - "z", - "braceleft", - "bar", - "braceright", - "asciitilde", - "exclamdown", - "cent", - "sterling", - "fraction", - "yen", - "florin", - "section", - "currency", - "quotesingle", - "quotedblleft", - "guillemotleft", - "guilsinglleft", - "guilsinglright", - "fi", - "fl", - "endash", - "dagger", - "daggerdbl", - "periodcentered", - "paragraph", - "bullet", - "quotesinglbase", - "quotedblbase", - "quotedblright", - "guillemotright", - "ellipsis", - "perthousand", - "questiondown", - "grave", - "acute", - "circumflex", - "tilde", - "macron", - "breve", - "dotaccent", - "dieresis", - "ring", - "cedilla", - "hungarumlaut", - "ogonek", - "caron", - "emdash", - "AE", - "ordfeminine", - "Lslash", - "Oslash", - "OE", - "ordmasculine", - "ae", - "dotlessi", - "lslash", - "oslash", - "oe", - "germandbls", - "onesuperior", - "logicalnot", - "mu", - "trademark", - "Eth", - "onehalf", - "plusminus", - "Thorn", - "onequarter", - "divide", - "brokenbar", - "degree", - "thorn", - "threequarters", - "twosuperior", - "registered", - "minus", - "eth", - "multiply", - "threesuperior", - "copyright", - "Aacute", - "Acircumflex", - "Adieresis", - "Agrave", - "Aring", - "Atilde", - "Ccedilla", - "Eacute", - "Ecircumflex", - "Edieresis", - "Egrave", - "Iacute", - "Icircumflex", - "Idieresis", - "Igrave", - "Ntilde", - "Oacute", - "Ocircumflex", - "Odieresis", - "Ograve", - "Otilde", - "Scaron", - "Uacute", - "Ucircumflex", - "Udieresis", - "Ugrave", - "Yacute", - "Ydieresis", - "Zcaron", - "aacute", - "acircumflex", - "adieresis", - "agrave", - "aring", - "atilde", - "ccedilla", - "eacute", - "ecircumflex", - "edieresis", - "egrave", - "iacute", - "icircumflex", - "idieresis", - "igrave", - "ntilde", - "oacute", - "ocircumflex", - "odieresis", - "ograve", - "otilde", - "scaron", - "uacute", - "ucircumflex", - "udieresis", - "ugrave", - "yacute", - "ydieresis", - "zcaron", - "exclamsmall", - "Hungarumlautsmall", - "dollaroldstyle", - "dollarsuperior", - "ampersandsmall", - "Acutesmall", - "parenleftsuperior", - "parenrightsuperior", - "twodotenleader", - "onedotenleader", - "zerooldstyle", - "oneoldstyle", - "twooldstyle", - "threeoldstyle", - "fouroldstyle", - "fiveoldstyle", - "sixoldstyle", - "sevenoldstyle", - "eightoldstyle", - "nineoldstyle", - "commasuperior", - "threequartersemdash", - "periodsuperior", - "questionsmall", - "asuperior", - "bsuperior", - "centsuperior", - "dsuperior", - "esuperior", - "isuperior", - "lsuperior", - "msuperior", - "nsuperior", - "osuperior", - "rsuperior", - "ssuperior", - "tsuperior", - "ff", - "ffi", - "ffl", - "parenleftinferior", - "parenrightinferior", - "Circumflexsmall", - "hyphensuperior", - "Gravesmall", - "Asmall", - "Bsmall", - "Csmall", - "Dsmall", - "Esmall", - "Fsmall", - "Gsmall", - "Hsmall", - "Ismall", - "Jsmall", - "Ksmall", - "Lsmall", - "Msmall", - "Nsmall", - "Osmall", - "Psmall", - "Qsmall", - "Rsmall", - "Ssmall", - "Tsmall", - "Usmall", - "Vsmall", - "Wsmall", - "Xsmall", - "Ysmall", - "Zsmall", - "colonmonetary", - "onefitted", - "rupiah", - "Tildesmall", - "exclamdownsmall", - "centoldstyle", - "Lslashsmall", - "Scaronsmall", - "Zcaronsmall", - "Dieresissmall", - "Brevesmall", - "Caronsmall", - "Dotaccentsmall", - "Macronsmall", - "figuredash", - "hypheninferior", - "Ogoneksmall", - "Ringsmall", - "Cedillasmall", - "questiondownsmall", - "oneeighth", - "threeeighths", - "fiveeighths", - "seveneighths", - "onethird", - "twothirds", - "zerosuperior", - "foursuperior", - "fivesuperior", - "sixsuperior", - "sevensuperior", - "eightsuperior", - "ninesuperior", - "zeroinferior", - "oneinferior", - "twoinferior", - "threeinferior", - "fourinferior", - "fiveinferior", - "sixinferior", - "seveninferior", - "eightinferior", - "nineinferior", - "centinferior", - "dollarinferior", - "periodinferior", - "commainferior", - "Agravesmall", - "Aacutesmall", - "Acircumflexsmall", - "Atildesmall", - "Adieresissmall", - "Aringsmall", - "AEsmall", - "Ccedillasmall", - "Egravesmall", - "Eacutesmall", - "Ecircumflexsmall", - "Edieresissmall", - "Igravesmall", - "Iacutesmall", - "Icircumflexsmall", - "Idieresissmall", - "Ethsmall", - "Ntildesmall", - "Ogravesmall", - "Oacutesmall", - "Ocircumflexsmall", - "Otildesmall", - "Odieresissmall", - "OEsmall", - "Oslashsmall", - "Ugravesmall", - "Uacutesmall", - "Ucircumflexsmall", - "Udieresissmall", - "Yacutesmall", - "Thornsmall", - "Ydieresissmall", - "001.000", - "001.001", - "001.002", - "001.003", - "Black", - "Bold", - "Book", - "Light", - "Medium", - "Regular", - "Roman", - "Semibold", -] - -cffStandardStringCount = 391 -assert len(cffStandardStrings) == cffStandardStringCount -# build reverse mapping -cffStandardStringMapping = {} -for _i in range(cffStandardStringCount): - cffStandardStringMapping[cffStandardStrings[_i]] = _i - -cffISOAdobeStrings = [ - ".notdef", - "space", - "exclam", - "quotedbl", - "numbersign", - "dollar", - "percent", - "ampersand", - "quoteright", - "parenleft", - "parenright", - "asterisk", - "plus", - "comma", - "hyphen", - "period", - "slash", - "zero", - "one", - "two", - "three", - "four", - "five", - "six", - "seven", - "eight", - "nine", - "colon", - "semicolon", - "less", - "equal", - "greater", - "question", - "at", - "A", - "B", - "C", - "D", - "E", - "F", - "G", - "H", - "I", - "J", - "K", - "L", - "M", - "N", - "O", - "P", - "Q", - "R", - "S", - "T", - "U", - "V", - "W", - "X", - "Y", - "Z", - "bracketleft", - "backslash", - "bracketright", - "asciicircum", - "underscore", - "quoteleft", - "a", - "b", - "c", - "d", - "e", - "f", - "g", - "h", - "i", - "j", - "k", - "l", - "m", - "n", - "o", - "p", - "q", - "r", - "s", - "t", - "u", - "v", - "w", - "x", - "y", - "z", - "braceleft", - "bar", - "braceright", - "asciitilde", - "exclamdown", - "cent", - "sterling", - "fraction", - "yen", - "florin", - "section", - "currency", - "quotesingle", - "quotedblleft", - "guillemotleft", - "guilsinglleft", - "guilsinglright", - "fi", - "fl", - "endash", - "dagger", - "daggerdbl", - "periodcentered", - "paragraph", - "bullet", - "quotesinglbase", - "quotedblbase", - "quotedblright", - "guillemotright", - "ellipsis", - "perthousand", - "questiondown", - "grave", - "acute", - "circumflex", - "tilde", - "macron", - "breve", - "dotaccent", - "dieresis", - "ring", - "cedilla", - "hungarumlaut", - "ogonek", - "caron", - "emdash", - "AE", - "ordfeminine", - "Lslash", - "Oslash", - "OE", - "ordmasculine", - "ae", - "dotlessi", - "lslash", - "oslash", - "oe", - "germandbls", - "onesuperior", - "logicalnot", - "mu", - "trademark", - "Eth", - "onehalf", - "plusminus", - "Thorn", - "onequarter", - "divide", - "brokenbar", - "degree", - "thorn", - "threequarters", - "twosuperior", - "registered", - "minus", - "eth", - "multiply", - "threesuperior", - "copyright", - "Aacute", - "Acircumflex", - "Adieresis", - "Agrave", - "Aring", - "Atilde", - "Ccedilla", - "Eacute", - "Ecircumflex", - "Edieresis", - "Egrave", - "Iacute", - "Icircumflex", - "Idieresis", - "Igrave", - "Ntilde", - "Oacute", - "Ocircumflex", - "Odieresis", - "Ograve", - "Otilde", - "Scaron", - "Uacute", - "Ucircumflex", - "Udieresis", - "Ugrave", - "Yacute", - "Ydieresis", - "Zcaron", - "aacute", - "acircumflex", - "adieresis", - "agrave", - "aring", - "atilde", - "ccedilla", - "eacute", - "ecircumflex", - "edieresis", - "egrave", - "iacute", - "icircumflex", - "idieresis", - "igrave", - "ntilde", - "oacute", - "ocircumflex", - "odieresis", - "ograve", - "otilde", - "scaron", - "uacute", - "ucircumflex", - "udieresis", - "ugrave", - "yacute", - "ydieresis", - "zcaron", -] - -cffISOAdobeStringCount = 229 -assert len(cffISOAdobeStrings) == cffISOAdobeStringCount - -cffIExpertStrings = [ - ".notdef", - "space", - "exclamsmall", - "Hungarumlautsmall", - "dollaroldstyle", - "dollarsuperior", - "ampersandsmall", - "Acutesmall", - "parenleftsuperior", - "parenrightsuperior", - "twodotenleader", - "onedotenleader", - "comma", - "hyphen", - "period", - "fraction", - "zerooldstyle", - "oneoldstyle", - "twooldstyle", - "threeoldstyle", - "fouroldstyle", - "fiveoldstyle", - "sixoldstyle", - "sevenoldstyle", - "eightoldstyle", - "nineoldstyle", - "colon", - "semicolon", - "commasuperior", - "threequartersemdash", - "periodsuperior", - "questionsmall", - "asuperior", - "bsuperior", - "centsuperior", - "dsuperior", - "esuperior", - "isuperior", - "lsuperior", - "msuperior", - "nsuperior", - "osuperior", - "rsuperior", - "ssuperior", - "tsuperior", - "ff", - "fi", - "fl", - "ffi", - "ffl", - "parenleftinferior", - "parenrightinferior", - "Circumflexsmall", - "hyphensuperior", - "Gravesmall", - "Asmall", - "Bsmall", - "Csmall", - "Dsmall", - "Esmall", - "Fsmall", - "Gsmall", - "Hsmall", - "Ismall", - "Jsmall", - "Ksmall", - "Lsmall", - "Msmall", - "Nsmall", - "Osmall", - "Psmall", - "Qsmall", - "Rsmall", - "Ssmall", - "Tsmall", - "Usmall", - "Vsmall", - "Wsmall", - "Xsmall", - "Ysmall", - "Zsmall", - "colonmonetary", - "onefitted", - "rupiah", - "Tildesmall", - "exclamdownsmall", - "centoldstyle", - "Lslashsmall", - "Scaronsmall", - "Zcaronsmall", - "Dieresissmall", - "Brevesmall", - "Caronsmall", - "Dotaccentsmall", - "Macronsmall", - "figuredash", - "hypheninferior", - "Ogoneksmall", - "Ringsmall", - "Cedillasmall", - "onequarter", - "onehalf", - "threequarters", - "questiondownsmall", - "oneeighth", - "threeeighths", - "fiveeighths", - "seveneighths", - "onethird", - "twothirds", - "zerosuperior", - "onesuperior", - "twosuperior", - "threesuperior", - "foursuperior", - "fivesuperior", - "sixsuperior", - "sevensuperior", - "eightsuperior", - "ninesuperior", - "zeroinferior", - "oneinferior", - "twoinferior", - "threeinferior", - "fourinferior", - "fiveinferior", - "sixinferior", - "seveninferior", - "eightinferior", - "nineinferior", - "centinferior", - "dollarinferior", - "periodinferior", - "commainferior", - "Agravesmall", - "Aacutesmall", - "Acircumflexsmall", - "Atildesmall", - "Adieresissmall", - "Aringsmall", - "AEsmall", - "Ccedillasmall", - "Egravesmall", - "Eacutesmall", - "Ecircumflexsmall", - "Edieresissmall", - "Igravesmall", - "Iacutesmall", - "Icircumflexsmall", - "Idieresissmall", - "Ethsmall", - "Ntildesmall", - "Ogravesmall", - "Oacutesmall", - "Ocircumflexsmall", - "Otildesmall", - "Odieresissmall", - "OEsmall", - "Oslashsmall", - "Ugravesmall", - "Uacutesmall", - "Ucircumflexsmall", - "Udieresissmall", - "Yacutesmall", - "Thornsmall", - "Ydieresissmall", -] - -cffExpertStringCount = 166 -assert len(cffIExpertStrings) == cffExpertStringCount - -cffExpertSubsetStrings = [ - ".notdef", - "space", - "dollaroldstyle", - "dollarsuperior", - "parenleftsuperior", - "parenrightsuperior", - "twodotenleader", - "onedotenleader", - "comma", - "hyphen", - "period", - "fraction", - "zerooldstyle", - "oneoldstyle", - "twooldstyle", - "threeoldstyle", - "fouroldstyle", - "fiveoldstyle", - "sixoldstyle", - "sevenoldstyle", - "eightoldstyle", - "nineoldstyle", - "colon", - "semicolon", - "commasuperior", - "threequartersemdash", - "periodsuperior", - "asuperior", - "bsuperior", - "centsuperior", - "dsuperior", - "esuperior", - "isuperior", - "lsuperior", - "msuperior", - "nsuperior", - "osuperior", - "rsuperior", - "ssuperior", - "tsuperior", - "ff", - "fi", - "fl", - "ffi", - "ffl", - "parenleftinferior", - "parenrightinferior", - "hyphensuperior", - "colonmonetary", - "onefitted", - "rupiah", - "centoldstyle", - "figuredash", - "hypheninferior", - "onequarter", - "onehalf", - "threequarters", - "oneeighth", - "threeeighths", - "fiveeighths", - "seveneighths", - "onethird", - "twothirds", - "zerosuperior", - "onesuperior", - "twosuperior", - "threesuperior", - "foursuperior", - "fivesuperior", - "sixsuperior", - "sevensuperior", - "eightsuperior", - "ninesuperior", - "zeroinferior", - "oneinferior", - "twoinferior", - "threeinferior", - "fourinferior", - "fiveinferior", - "sixinferior", - "seveninferior", - "eightinferior", - "nineinferior", - "centinferior", - "dollarinferior", - "periodinferior", - "commainferior", -] - -cffExpertSubsetStringCount = 87 -assert len(cffExpertSubsetStrings) == cffExpertSubsetStringCount diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__pycache__/CFF2ToCFF.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__pycache__/CFF2ToCFF.cpython-38.pyc deleted file mode 100644 index f20f534..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__pycache__/CFF2ToCFF.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__pycache__/CFFToCFF2.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__pycache__/CFFToCFF2.cpython-38.pyc deleted file mode 100644 index 473c546..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__pycache__/CFFToCFF2.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 4095804..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__pycache__/specializer.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__pycache__/specializer.cpython-38.pyc deleted file mode 100644 index 37e73a0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__pycache__/specializer.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__pycache__/transforms.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__pycache__/transforms.cpython-38.pyc deleted file mode 100644 index 5143d96..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__pycache__/transforms.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__pycache__/width.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__pycache__/width.cpython-38.pyc deleted file mode 100644 index 6f5871e..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/__pycache__/width.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/specializer.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/specializer.py deleted file mode 100644 index 5fddcb6..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/specializer.py +++ /dev/null @@ -1,924 +0,0 @@ -# -*- coding: utf-8 -*- - -"""T2CharString operator specializer and generalizer. - -PostScript glyph drawing operations can be expressed in multiple different -ways. For example, as well as the ``lineto`` operator, there is also a -``hlineto`` operator which draws a horizontal line, removing the need to -specify a ``dx`` coordinate, and a ``vlineto`` operator which draws a -vertical line, removing the need to specify a ``dy`` coordinate. As well -as decompiling :class:`fontTools.misc.psCharStrings.T2CharString` objects -into lists of operations, this module allows for conversion between general -and specific forms of the operation. - -""" - -from fontTools.cffLib import maxStackLimit - - -def stringToProgram(string): - if isinstance(string, str): - string = string.split() - program = [] - for token in string: - try: - token = int(token) - except ValueError: - try: - token = float(token) - except ValueError: - pass - program.append(token) - return program - - -def programToString(program): - return " ".join(str(x) for x in program) - - -def programToCommands(program, getNumRegions=None): - """Takes a T2CharString program list and returns list of commands. - Each command is a two-tuple of commandname,arg-list. The commandname might - be empty string if no commandname shall be emitted (used for glyph width, - hintmask/cntrmask argument, as well as stray arguments at the end of the - program (🤷). - 'getNumRegions' may be None, or a callable object. It must return the - number of regions. 'getNumRegions' takes a single argument, vsindex. It - returns the numRegions for the vsindex. - The Charstring may or may not start with a width value. If the first - non-blend operator has an odd number of arguments, then the first argument is - a width, and is popped off. This is complicated with blend operators, as - there may be more than one before the first hint or moveto operator, and each - one reduces several arguments to just one list argument. We have to sum the - number of arguments that are not part of the blend arguments, and all the - 'numBlends' values. We could instead have said that by definition, if there - is a blend operator, there is no width value, since CFF2 Charstrings don't - have width values. I discussed this with Behdad, and we are allowing for an - initial width value in this case because developers may assemble a CFF2 - charstring from CFF Charstrings, which could have width values. - """ - - seenWidthOp = False - vsIndex = 0 - lenBlendStack = 0 - lastBlendIndex = 0 - commands = [] - stack = [] - it = iter(program) - - for token in it: - if not isinstance(token, str): - stack.append(token) - continue - - if token == "blend": - assert getNumRegions is not None - numSourceFonts = 1 + getNumRegions(vsIndex) - # replace the blend op args on the stack with a single list - # containing all the blend op args. - numBlends = stack[-1] - numBlendArgs = numBlends * numSourceFonts + 1 - # replace first blend op by a list of the blend ops. - stack[-numBlendArgs:] = [stack[-numBlendArgs:]] - lenStack = len(stack) - lenBlendStack += numBlends + lenStack - 1 - lastBlendIndex = lenStack - # if a blend op exists, this is or will be a CFF2 charstring. - continue - - elif token == "vsindex": - vsIndex = stack[-1] - assert type(vsIndex) is int - - elif (not seenWidthOp) and token in { - "hstem", - "hstemhm", - "vstem", - "vstemhm", - "cntrmask", - "hintmask", - "hmoveto", - "vmoveto", - "rmoveto", - "endchar", - }: - seenWidthOp = True - parity = token in {"hmoveto", "vmoveto"} - if lenBlendStack: - # lenBlendStack has the number of args represented by the last blend - # arg and all the preceding args. We need to now add the number of - # args following the last blend arg. - numArgs = lenBlendStack + len(stack[lastBlendIndex:]) - else: - numArgs = len(stack) - if numArgs and (numArgs % 2) ^ parity: - width = stack.pop(0) - commands.append(("", [width])) - - if token in {"hintmask", "cntrmask"}: - if stack: - commands.append(("", stack)) - commands.append((token, [])) - commands.append(("", [next(it)])) - else: - commands.append((token, stack)) - stack = [] - if stack: - commands.append(("", stack)) - return commands - - -def _flattenBlendArgs(args): - token_list = [] - for arg in args: - if isinstance(arg, list): - token_list.extend(arg) - token_list.append("blend") - else: - token_list.append(arg) - return token_list - - -def commandsToProgram(commands): - """Takes a commands list as returned by programToCommands() and converts - it back to a T2CharString program list.""" - program = [] - for op, args in commands: - if any(isinstance(arg, list) for arg in args): - args = _flattenBlendArgs(args) - program.extend(args) - if op: - program.append(op) - return program - - -def _everyN(el, n): - """Group the list el into groups of size n""" - l = len(el) - if l % n != 0: - raise ValueError(el) - for i in range(0, l, n): - yield el[i : i + n] - - -class _GeneralizerDecombinerCommandsMap(object): - @staticmethod - def rmoveto(args): - if len(args) != 2: - raise ValueError(args) - yield ("rmoveto", args) - - @staticmethod - def hmoveto(args): - if len(args) != 1: - raise ValueError(args) - yield ("rmoveto", [args[0], 0]) - - @staticmethod - def vmoveto(args): - if len(args) != 1: - raise ValueError(args) - yield ("rmoveto", [0, args[0]]) - - @staticmethod - def rlineto(args): - if not args: - raise ValueError(args) - for args in _everyN(args, 2): - yield ("rlineto", args) - - @staticmethod - def hlineto(args): - if not args: - raise ValueError(args) - it = iter(args) - try: - while True: - yield ("rlineto", [next(it), 0]) - yield ("rlineto", [0, next(it)]) - except StopIteration: - pass - - @staticmethod - def vlineto(args): - if not args: - raise ValueError(args) - it = iter(args) - try: - while True: - yield ("rlineto", [0, next(it)]) - yield ("rlineto", [next(it), 0]) - except StopIteration: - pass - - @staticmethod - def rrcurveto(args): - if not args: - raise ValueError(args) - for args in _everyN(args, 6): - yield ("rrcurveto", args) - - @staticmethod - def hhcurveto(args): - l = len(args) - if l < 4 or l % 4 > 1: - raise ValueError(args) - if l % 2 == 1: - yield ("rrcurveto", [args[1], args[0], args[2], args[3], args[4], 0]) - args = args[5:] - for args in _everyN(args, 4): - yield ("rrcurveto", [args[0], 0, args[1], args[2], args[3], 0]) - - @staticmethod - def vvcurveto(args): - l = len(args) - if l < 4 or l % 4 > 1: - raise ValueError(args) - if l % 2 == 1: - yield ("rrcurveto", [args[0], args[1], args[2], args[3], 0, args[4]]) - args = args[5:] - for args in _everyN(args, 4): - yield ("rrcurveto", [0, args[0], args[1], args[2], 0, args[3]]) - - @staticmethod - def hvcurveto(args): - l = len(args) - if l < 4 or l % 8 not in {0, 1, 4, 5}: - raise ValueError(args) - last_args = None - if l % 2 == 1: - lastStraight = l % 8 == 5 - args, last_args = args[:-5], args[-5:] - it = _everyN(args, 4) - try: - while True: - args = next(it) - yield ("rrcurveto", [args[0], 0, args[1], args[2], 0, args[3]]) - args = next(it) - yield ("rrcurveto", [0, args[0], args[1], args[2], args[3], 0]) - except StopIteration: - pass - if last_args: - args = last_args - if lastStraight: - yield ("rrcurveto", [args[0], 0, args[1], args[2], args[4], args[3]]) - else: - yield ("rrcurveto", [0, args[0], args[1], args[2], args[3], args[4]]) - - @staticmethod - def vhcurveto(args): - l = len(args) - if l < 4 or l % 8 not in {0, 1, 4, 5}: - raise ValueError(args) - last_args = None - if l % 2 == 1: - lastStraight = l % 8 == 5 - args, last_args = args[:-5], args[-5:] - it = _everyN(args, 4) - try: - while True: - args = next(it) - yield ("rrcurveto", [0, args[0], args[1], args[2], args[3], 0]) - args = next(it) - yield ("rrcurveto", [args[0], 0, args[1], args[2], 0, args[3]]) - except StopIteration: - pass - if last_args: - args = last_args - if lastStraight: - yield ("rrcurveto", [0, args[0], args[1], args[2], args[3], args[4]]) - else: - yield ("rrcurveto", [args[0], 0, args[1], args[2], args[4], args[3]]) - - @staticmethod - def rcurveline(args): - l = len(args) - if l < 8 or l % 6 != 2: - raise ValueError(args) - args, last_args = args[:-2], args[-2:] - for args in _everyN(args, 6): - yield ("rrcurveto", args) - yield ("rlineto", last_args) - - @staticmethod - def rlinecurve(args): - l = len(args) - if l < 8 or l % 2 != 0: - raise ValueError(args) - args, last_args = args[:-6], args[-6:] - for args in _everyN(args, 2): - yield ("rlineto", args) - yield ("rrcurveto", last_args) - - -def _convertBlendOpToArgs(blendList): - # args is list of blend op args. Since we are supporting - # recursive blend op calls, some of these args may also - # be a list of blend op args, and need to be converted before - # we convert the current list. - if any([isinstance(arg, list) for arg in blendList]): - args = [ - i - for e in blendList - for i in (_convertBlendOpToArgs(e) if isinstance(e, list) else [e]) - ] - else: - args = blendList - - # We now know that blendList contains a blend op argument list, even if - # some of the args are lists that each contain a blend op argument list. - # Convert from: - # [default font arg sequence x0,...,xn] + [delta tuple for x0] + ... + [delta tuple for xn] - # to: - # [ [x0] + [delta tuple for x0], - # ..., - # [xn] + [delta tuple for xn] ] - numBlends = args[-1] - # Can't use args.pop() when the args are being used in a nested list - # comprehension. See calling context - args = args[:-1] - - l = len(args) - numRegions = l // numBlends - 1 - if not (numBlends * (numRegions + 1) == l): - raise ValueError(blendList) - - defaultArgs = [[arg] for arg in args[:numBlends]] - deltaArgs = args[numBlends:] - numDeltaValues = len(deltaArgs) - deltaList = [ - deltaArgs[i : i + numRegions] for i in range(0, numDeltaValues, numRegions) - ] - blend_args = [a + b + [1] for a, b in zip(defaultArgs, deltaList)] - return blend_args - - -def generalizeCommands(commands, ignoreErrors=False): - result = [] - mapping = _GeneralizerDecombinerCommandsMap - for op, args in commands: - # First, generalize any blend args in the arg list. - if any([isinstance(arg, list) for arg in args]): - try: - args = [ - n - for arg in args - for n in ( - _convertBlendOpToArgs(arg) if isinstance(arg, list) else [arg] - ) - ] - except ValueError: - if ignoreErrors: - # Store op as data, such that consumers of commands do not have to - # deal with incorrect number of arguments. - result.append(("", args)) - result.append(("", [op])) - else: - raise - - func = getattr(mapping, op, None) - if func is None: - result.append((op, args)) - continue - try: - for command in func(args): - result.append(command) - except ValueError: - if ignoreErrors: - # Store op as data, such that consumers of commands do not have to - # deal with incorrect number of arguments. - result.append(("", args)) - result.append(("", [op])) - else: - raise - return result - - -def generalizeProgram(program, getNumRegions=None, **kwargs): - return commandsToProgram( - generalizeCommands(programToCommands(program, getNumRegions), **kwargs) - ) - - -def _categorizeVector(v): - """ - Takes X,Y vector v and returns one of r, h, v, or 0 depending on which - of X and/or Y are zero, plus tuple of nonzero ones. If both are zero, - it returns a single zero still. - - >>> _categorizeVector((0,0)) - ('0', (0,)) - >>> _categorizeVector((1,0)) - ('h', (1,)) - >>> _categorizeVector((0,2)) - ('v', (2,)) - >>> _categorizeVector((1,2)) - ('r', (1, 2)) - """ - if not v[0]: - if not v[1]: - return "0", v[:1] - else: - return "v", v[1:] - else: - if not v[1]: - return "h", v[:1] - else: - return "r", v - - -def _mergeCategories(a, b): - if a == "0": - return b - if b == "0": - return a - if a == b: - return a - return None - - -def _negateCategory(a): - if a == "h": - return "v" - if a == "v": - return "h" - assert a in "0r" - return a - - -def _convertToBlendCmds(args): - # return a list of blend commands, and - # the remaining non-blended args, if any. - num_args = len(args) - stack_use = 0 - new_args = [] - i = 0 - while i < num_args: - arg = args[i] - i += 1 - if not isinstance(arg, list): - new_args.append(arg) - stack_use += 1 - else: - prev_stack_use = stack_use - # The arg is a tuple of blend values. - # These are each (master 0,delta 1..delta n, 1) - # Combine as many successive tuples as we can, - # up to the max stack limit. - num_sources = len(arg) - 1 - blendlist = [arg] - stack_use += 1 + num_sources # 1 for the num_blends arg - - # if we are here, max stack is the CFF2 max stack. - # I use the CFF2 max stack limit here rather than - # the 'maxstack' chosen by the client, as the default - # maxstack may have been used unintentionally. For all - # the other operators, this just produces a little less - # optimization, but here it puts a hard (and low) limit - # on the number of source fonts that can be used. - # - # Make sure the stack depth does not exceed (maxstack - 1), so - # that subroutinizer can insert subroutine calls at any point. - while ( - (i < num_args) - and isinstance(args[i], list) - and stack_use + num_sources < maxStackLimit - ): - blendlist.append(args[i]) - i += 1 - stack_use += num_sources - # blendList now contains as many single blend tuples as can be - # combined without exceeding the CFF2 stack limit. - num_blends = len(blendlist) - # append the 'num_blends' default font values - blend_args = [] - for arg in blendlist: - blend_args.append(arg[0]) - for arg in blendlist: - assert arg[-1] == 1 - blend_args.extend(arg[1:-1]) - blend_args.append(num_blends) - new_args.append(blend_args) - stack_use = prev_stack_use + num_blends - - return new_args - - -def _addArgs(a, b): - if isinstance(b, list): - if isinstance(a, list): - if len(a) != len(b) or a[-1] != b[-1]: - raise ValueError() - return [_addArgs(va, vb) for va, vb in zip(a[:-1], b[:-1])] + [a[-1]] - else: - a, b = b, a - if isinstance(a, list): - assert a[-1] == 1 - return [_addArgs(a[0], b)] + a[1:] - return a + b - - -def _argsStackUse(args): - stackLen = 0 - maxLen = 0 - for arg in args: - if type(arg) is list: - # Blended arg - maxLen = max(maxLen, stackLen + _argsStackUse(arg)) - stackLen += arg[-1] - else: - stackLen += 1 - return max(stackLen, maxLen) - - -def specializeCommands( - commands, - ignoreErrors=False, - generalizeFirst=True, - preserveTopology=False, - maxstack=48, -): - # We perform several rounds of optimizations. They are carefully ordered and are: - # - # 0. Generalize commands. - # This ensures that they are in our expected simple form, with each line/curve only - # having arguments for one segment, and using the generic form (rlineto/rrcurveto). - # If caller is sure the input is in this form, they can turn off generalization to - # save time. - # - # 1. Combine successive rmoveto operations. - # - # 2. Specialize rmoveto/rlineto/rrcurveto operators into horizontal/vertical variants. - # We specialize into some, made-up, variants as well, which simplifies following - # passes. - # - # 3. Merge or delete redundant operations, to the extent requested. - # OpenType spec declares point numbers in CFF undefined. As such, we happily - # change topology. If client relies on point numbers (in GPOS anchors, or for - # hinting purposes(what?)) they can turn this off. - # - # 4. Peephole optimization to revert back some of the h/v variants back into their - # original "relative" operator (rline/rrcurveto) if that saves a byte. - # - # 5. Combine adjacent operators when possible, minding not to go over max stack size. - # - # 6. Resolve any remaining made-up operators into real operators. - # - # I have convinced myself that this produces optimal bytecode (except for, possibly - # one byte each time maxstack size prohibits combining.) YMMV, but you'd be wrong. :-) - # A dynamic-programming approach can do the same but would be significantly slower. - # - # 7. For any args which are blend lists, convert them to a blend command. - - # 0. Generalize commands. - if generalizeFirst: - commands = generalizeCommands(commands, ignoreErrors=ignoreErrors) - else: - commands = list(commands) # Make copy since we modify in-place later. - - # 1. Combine successive rmoveto operations. - for i in range(len(commands) - 1, 0, -1): - if "rmoveto" == commands[i][0] == commands[i - 1][0]: - v1, v2 = commands[i - 1][1], commands[i][1] - commands[i - 1] = ("rmoveto", [v1[0] + v2[0], v1[1] + v2[1]]) - del commands[i] - - # 2. Specialize rmoveto/rlineto/rrcurveto operators into horizontal/vertical variants. - # - # We, in fact, specialize into more, made-up, variants that special-case when both - # X and Y components are zero. This simplifies the following optimization passes. - # This case is rare, but OCD does not let me skip it. - # - # After this round, we will have four variants that use the following mnemonics: - # - # - 'r' for relative, ie. non-zero X and non-zero Y, - # - 'h' for horizontal, ie. zero X and non-zero Y, - # - 'v' for vertical, ie. non-zero X and zero Y, - # - '0' for zeros, ie. zero X and zero Y. - # - # The '0' pseudo-operators are not part of the spec, but help simplify the following - # optimization rounds. We resolve them at the end. So, after this, we will have four - # moveto and four lineto variants: - # - # - 0moveto, 0lineto - # - hmoveto, hlineto - # - vmoveto, vlineto - # - rmoveto, rlineto - # - # and sixteen curveto variants. For example, a '0hcurveto' operator means a curve - # dx0,dy0,dx1,dy1,dx2,dy2,dx3,dy3 where dx0, dx1, and dy3 are zero but not dx3. - # An 'rvcurveto' means dx3 is zero but not dx0,dy0,dy3. - # - # There are nine different variants of curves without the '0'. Those nine map exactly - # to the existing curve variants in the spec: rrcurveto, and the four variants hhcurveto, - # vvcurveto, hvcurveto, and vhcurveto each cover two cases, one with an odd number of - # arguments and one without. Eg. an hhcurveto with an extra argument (odd number of - # arguments) is in fact an rhcurveto. The operators in the spec are designed such that - # all four of rhcurveto, rvcurveto, hrcurveto, and vrcurveto are encodable for one curve. - # - # Of the curve types with '0', the 00curveto is equivalent to a lineto variant. The rest - # of the curve types with a 0 need to be encoded as a h or v variant. Ie. a '0' can be - # thought of a "don't care" and can be used as either an 'h' or a 'v'. As such, we always - # encode a number 0 as argument when we use a '0' variant. Later on, we can just substitute - # the '0' with either 'h' or 'v' and it works. - # - # When we get to curve splines however, things become more complicated... XXX finish this. - # There's one more complexity with splines. If one side of the spline is not horizontal or - # vertical (or zero), ie. if it's 'r', then it limits which spline types we can encode. - # Only hhcurveto and vvcurveto operators can encode a spline starting with 'r', and - # only hvcurveto and vhcurveto operators can encode a spline ending with 'r'. - # This limits our merge opportunities later. - # - for i in range(len(commands)): - op, args = commands[i] - - if op in {"rmoveto", "rlineto"}: - c, args = _categorizeVector(args) - commands[i] = c + op[1:], args - continue - - if op == "rrcurveto": - c1, args1 = _categorizeVector(args[:2]) - c2, args2 = _categorizeVector(args[-2:]) - commands[i] = c1 + c2 + "curveto", args1 + args[2:4] + args2 - continue - - # 3. Merge or delete redundant operations, to the extent requested. - # - # TODO - # A 0moveto that comes before all other path operations can be removed. - # though I find conflicting evidence for this. - # - # TODO - # "If hstem and vstem hints are both declared at the beginning of a - # CharString, and this sequence is followed directly by the hintmask or - # cntrmask operators, then the vstem hint operator (or, if applicable, - # the vstemhm operator) need not be included." - # - # "The sequence and form of a CFF2 CharString program may be represented as: - # {hs* vs* cm* hm* mt subpath}? {mt subpath}*" - # - # https://www.microsoft.com/typography/otspec/cff2charstr.htm#section3.1 - # - # For Type2 CharStrings the sequence is: - # w? {hs* vs* cm* hm* mt subpath}? {mt subpath}* endchar" - - # Some other redundancies change topology (point numbers). - if not preserveTopology: - for i in range(len(commands) - 1, -1, -1): - op, args = commands[i] - - # A 00curveto is demoted to a (specialized) lineto. - if op == "00curveto": - assert len(args) == 4 - c, args = _categorizeVector(args[1:3]) - op = c + "lineto" - commands[i] = op, args - # and then... - - # A 0lineto can be deleted. - if op == "0lineto": - del commands[i] - continue - - # Merge adjacent hlineto's and vlineto's. - # In CFF2 charstrings from variable fonts, each - # arg item may be a list of blendable values, one from - # each source font. - if i and op in {"hlineto", "vlineto"} and (op == commands[i - 1][0]): - _, other_args = commands[i - 1] - assert len(args) == 1 and len(other_args) == 1 - try: - new_args = [_addArgs(args[0], other_args[0])] - except ValueError: - continue - commands[i - 1] = (op, new_args) - del commands[i] - continue - - # 4. Peephole optimization to revert back some of the h/v variants back into their - # original "relative" operator (rline/rrcurveto) if that saves a byte. - for i in range(1, len(commands) - 1): - op, args = commands[i] - prv, nxt = commands[i - 1][0], commands[i + 1][0] - - if op in {"0lineto", "hlineto", "vlineto"} and prv == nxt == "rlineto": - assert len(args) == 1 - args = [0, args[0]] if op[0] == "v" else [args[0], 0] - commands[i] = ("rlineto", args) - continue - - if op[2:] == "curveto" and len(args) == 5 and prv == nxt == "rrcurveto": - assert (op[0] == "r") ^ (op[1] == "r") - if op[0] == "v": - pos = 0 - elif op[0] != "r": - pos = 1 - elif op[1] == "v": - pos = 4 - else: - pos = 5 - # Insert, while maintaining the type of args (can be tuple or list). - args = args[:pos] + type(args)((0,)) + args[pos:] - commands[i] = ("rrcurveto", args) - continue - - # 5. Combine adjacent operators when possible, minding not to go over max stack size. - stackUse = _argsStackUse(commands[-1][1]) if commands else 0 - for i in range(len(commands) - 1, 0, -1): - op1, args1 = commands[i - 1] - op2, args2 = commands[i] - new_op = None - - # Merge logic... - if {op1, op2} <= {"rlineto", "rrcurveto"}: - if op1 == op2: - new_op = op1 - else: - l = len(args2) - if op2 == "rrcurveto" and l == 6: - new_op = "rlinecurve" - elif l == 2: - new_op = "rcurveline" - - elif (op1, op2) in {("rlineto", "rlinecurve"), ("rrcurveto", "rcurveline")}: - new_op = op2 - - elif {op1, op2} == {"vlineto", "hlineto"}: - new_op = op1 - - elif "curveto" == op1[2:] == op2[2:]: - d0, d1 = op1[:2] - d2, d3 = op2[:2] - - if d1 == "r" or d2 == "r" or d0 == d3 == "r": - continue - - d = _mergeCategories(d1, d2) - if d is None: - continue - if d0 == "r": - d = _mergeCategories(d, d3) - if d is None: - continue - new_op = "r" + d + "curveto" - elif d3 == "r": - d0 = _mergeCategories(d0, _negateCategory(d)) - if d0 is None: - continue - new_op = d0 + "r" + "curveto" - else: - d0 = _mergeCategories(d0, d3) - if d0 is None: - continue - new_op = d0 + d + "curveto" - - # Make sure the stack depth does not exceed (maxstack - 1), so - # that subroutinizer can insert subroutine calls at any point. - args1StackUse = _argsStackUse(args1) - combinedStackUse = max(args1StackUse, len(args1) + stackUse) - if new_op and combinedStackUse < maxstack: - commands[i - 1] = (new_op, args1 + args2) - del commands[i] - stackUse = combinedStackUse - else: - stackUse = args1StackUse - - # 6. Resolve any remaining made-up operators into real operators. - for i in range(len(commands)): - op, args = commands[i] - - if op in {"0moveto", "0lineto"}: - commands[i] = "h" + op[1:], args - continue - - if op[2:] == "curveto" and op[:2] not in {"rr", "hh", "vv", "vh", "hv"}: - l = len(args) - - op0, op1 = op[:2] - if (op0 == "r") ^ (op1 == "r"): - assert l % 2 == 1 - if op0 == "0": - op0 = "h" - if op1 == "0": - op1 = "h" - if op0 == "r": - op0 = op1 - if op1 == "r": - op1 = _negateCategory(op0) - assert {op0, op1} <= {"h", "v"}, (op0, op1) - - if l % 2: - if op0 != op1: # vhcurveto / hvcurveto - if (op0 == "h") ^ (l % 8 == 1): - # Swap last two args order - args = args[:-2] + args[-1:] + args[-2:-1] - else: # hhcurveto / vvcurveto - if op0 == "h": # hhcurveto - # Swap first two args order - args = args[1:2] + args[:1] + args[2:] - - commands[i] = op0 + op1 + "curveto", args - continue - - # 7. For any series of args which are blend lists, convert the series to a single blend arg. - for i in range(len(commands)): - op, args = commands[i] - if any(isinstance(arg, list) for arg in args): - commands[i] = op, _convertToBlendCmds(args) - - return commands - - -def specializeProgram(program, getNumRegions=None, **kwargs): - return commandsToProgram( - specializeCommands(programToCommands(program, getNumRegions), **kwargs) - ) - - -if __name__ == "__main__": - import sys - - if len(sys.argv) == 1: - import doctest - - sys.exit(doctest.testmod().failed) - - import argparse - - parser = argparse.ArgumentParser( - "fonttools cffLib.specializer", - description="CFF CharString generalizer/specializer", - ) - parser.add_argument("program", metavar="command", nargs="*", help="Commands.") - parser.add_argument( - "--num-regions", - metavar="NumRegions", - nargs="*", - default=None, - help="Number of variable-font regions for blend opertaions.", - ) - parser.add_argument( - "--font", - metavar="FONTFILE", - default=None, - help="CFF2 font to specialize.", - ) - parser.add_argument( - "-o", - "--output-file", - type=str, - help="Output font file name.", - ) - - options = parser.parse_args(sys.argv[1:]) - - if options.program: - getNumRegions = ( - None - if options.num_regions is None - else lambda vsIndex: int( - options.num_regions[0 if vsIndex is None else vsIndex] - ) - ) - - program = stringToProgram(options.program) - print("Program:") - print(programToString(program)) - commands = programToCommands(program, getNumRegions) - print("Commands:") - print(commands) - program2 = commandsToProgram(commands) - print("Program from commands:") - print(programToString(program2)) - assert program == program2 - print("Generalized program:") - print(programToString(generalizeProgram(program, getNumRegions))) - print("Specialized program:") - print(programToString(specializeProgram(program, getNumRegions))) - - if options.font: - from fontTools.ttLib import TTFont - - font = TTFont(options.font) - cff2 = font["CFF2"].cff.topDictIndex[0] - charstrings = cff2.CharStrings - for glyphName in charstrings.keys(): - charstring = charstrings[glyphName] - charstring.decompile() - getNumRegions = charstring.private.getNumRegions - charstring.program = specializeProgram( - charstring.program, getNumRegions, maxstack=maxStackLimit - ) - - if options.output_file is None: - from fontTools.misc.cliTools import makeOutputFileName - - outfile = makeOutputFileName( - options.font, overWrite=True, suffix=".specialized" - ) - else: - outfile = options.output_file - if outfile: - print("Saving", outfile) - font.save(outfile) diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/transforms.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/transforms.py deleted file mode 100644 index 5b474a7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/transforms.py +++ /dev/null @@ -1,485 +0,0 @@ -from fontTools.misc.psCharStrings import ( - SimpleT2Decompiler, - T2WidthExtractor, - calcSubrBias, -) - - -def _uniq_sort(l): - return sorted(set(l)) - - -class StopHintCountEvent(Exception): - pass - - -class _DesubroutinizingT2Decompiler(SimpleT2Decompiler): - stop_hintcount_ops = ( - "op_hintmask", - "op_cntrmask", - "op_rmoveto", - "op_hmoveto", - "op_vmoveto", - ) - - def __init__(self, localSubrs, globalSubrs, private=None): - SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs, private) - - def execute(self, charString): - self.need_hintcount = True # until proven otherwise - for op_name in self.stop_hintcount_ops: - setattr(self, op_name, self.stop_hint_count) - - if hasattr(charString, "_desubroutinized"): - # If a charstring has already been desubroutinized, we will still - # need to execute it if we need to count hints in order to - # compute the byte length for mask arguments, and haven't finished - # counting hints pairs. - if self.need_hintcount and self.callingStack: - try: - SimpleT2Decompiler.execute(self, charString) - except StopHintCountEvent: - del self.callingStack[-1] - return - - charString._patches = [] - SimpleT2Decompiler.execute(self, charString) - desubroutinized = charString.program[:] - for idx, expansion in reversed(charString._patches): - assert idx >= 2 - assert desubroutinized[idx - 1] in [ - "callsubr", - "callgsubr", - ], desubroutinized[idx - 1] - assert type(desubroutinized[idx - 2]) == int - if expansion[-1] == "return": - expansion = expansion[:-1] - desubroutinized[idx - 2 : idx] = expansion - if not self.private.in_cff2: - if "endchar" in desubroutinized: - # Cut off after first endchar - desubroutinized = desubroutinized[ - : desubroutinized.index("endchar") + 1 - ] - - charString._desubroutinized = desubroutinized - del charString._patches - - def op_callsubr(self, index): - subr = self.localSubrs[self.operandStack[-1] + self.localBias] - SimpleT2Decompiler.op_callsubr(self, index) - self.processSubr(index, subr) - - def op_callgsubr(self, index): - subr = self.globalSubrs[self.operandStack[-1] + self.globalBias] - SimpleT2Decompiler.op_callgsubr(self, index) - self.processSubr(index, subr) - - def stop_hint_count(self, *args): - self.need_hintcount = False - for op_name in self.stop_hintcount_ops: - setattr(self, op_name, None) - cs = self.callingStack[-1] - if hasattr(cs, "_desubroutinized"): - raise StopHintCountEvent() - - def op_hintmask(self, index): - SimpleT2Decompiler.op_hintmask(self, index) - if self.need_hintcount: - self.stop_hint_count() - - def processSubr(self, index, subr): - cs = self.callingStack[-1] - if not hasattr(cs, "_desubroutinized"): - cs._patches.append((index, subr._desubroutinized)) - - -def desubroutinize(cff): - for fontName in cff.fontNames: - font = cff[fontName] - cs = font.CharStrings - for c in cs.values(): - c.decompile() - subrs = getattr(c.private, "Subrs", []) - decompiler = _DesubroutinizingT2Decompiler(subrs, c.globalSubrs, c.private) - decompiler.execute(c) - c.program = c._desubroutinized - del c._desubroutinized - # Delete all the local subrs - if hasattr(font, "FDArray"): - for fd in font.FDArray: - pd = fd.Private - if hasattr(pd, "Subrs"): - del pd.Subrs - if "Subrs" in pd.rawDict: - del pd.rawDict["Subrs"] - else: - pd = font.Private - if hasattr(pd, "Subrs"): - del pd.Subrs - if "Subrs" in pd.rawDict: - del pd.rawDict["Subrs"] - # as well as the global subrs - cff.GlobalSubrs.clear() - - -class _MarkingT2Decompiler(SimpleT2Decompiler): - def __init__(self, localSubrs, globalSubrs, private): - SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs, private) - for subrs in [localSubrs, globalSubrs]: - if subrs and not hasattr(subrs, "_used"): - subrs._used = set() - - def op_callsubr(self, index): - self.localSubrs._used.add(self.operandStack[-1] + self.localBias) - SimpleT2Decompiler.op_callsubr(self, index) - - def op_callgsubr(self, index): - self.globalSubrs._used.add(self.operandStack[-1] + self.globalBias) - SimpleT2Decompiler.op_callgsubr(self, index) - - -class _DehintingT2Decompiler(T2WidthExtractor): - class Hints(object): - def __init__(self): - # Whether calling this charstring produces any hint stems - # Note that if a charstring starts with hintmask, it will - # have has_hint set to True, because it *might* produce an - # implicit vstem if called under certain conditions. - self.has_hint = False - # Index to start at to drop all hints - self.last_hint = 0 - # Index up to which we know more hints are possible. - # Only relevant if status is 0 or 1. - self.last_checked = 0 - # The status means: - # 0: after dropping hints, this charstring is empty - # 1: after dropping hints, there may be more hints - # continuing after this, or there might be - # other things. Not clear yet. - # 2: no more hints possible after this charstring - self.status = 0 - # Has hintmask instructions; not recursive - self.has_hintmask = False - # List of indices of calls to empty subroutines to remove. - self.deletions = [] - - pass - - def __init__( - self, css, localSubrs, globalSubrs, nominalWidthX, defaultWidthX, private=None - ): - self._css = css - T2WidthExtractor.__init__( - self, localSubrs, globalSubrs, nominalWidthX, defaultWidthX - ) - self.private = private - - def execute(self, charString): - old_hints = charString._hints if hasattr(charString, "_hints") else None - charString._hints = self.Hints() - - T2WidthExtractor.execute(self, charString) - - hints = charString._hints - - if hints.has_hint or hints.has_hintmask: - self._css.add(charString) - - if hints.status != 2: - # Check from last_check, make sure we didn't have any operators. - for i in range(hints.last_checked, len(charString.program) - 1): - if isinstance(charString.program[i], str): - hints.status = 2 - break - else: - hints.status = 1 # There's *something* here - hints.last_checked = len(charString.program) - - if old_hints: - assert hints.__dict__ == old_hints.__dict__ - - def op_callsubr(self, index): - subr = self.localSubrs[self.operandStack[-1] + self.localBias] - T2WidthExtractor.op_callsubr(self, index) - self.processSubr(index, subr) - - def op_callgsubr(self, index): - subr = self.globalSubrs[self.operandStack[-1] + self.globalBias] - T2WidthExtractor.op_callgsubr(self, index) - self.processSubr(index, subr) - - def op_hstem(self, index): - T2WidthExtractor.op_hstem(self, index) - self.processHint(index) - - def op_vstem(self, index): - T2WidthExtractor.op_vstem(self, index) - self.processHint(index) - - def op_hstemhm(self, index): - T2WidthExtractor.op_hstemhm(self, index) - self.processHint(index) - - def op_vstemhm(self, index): - T2WidthExtractor.op_vstemhm(self, index) - self.processHint(index) - - def op_hintmask(self, index): - rv = T2WidthExtractor.op_hintmask(self, index) - self.processHintmask(index) - return rv - - def op_cntrmask(self, index): - rv = T2WidthExtractor.op_cntrmask(self, index) - self.processHintmask(index) - return rv - - def processHintmask(self, index): - cs = self.callingStack[-1] - hints = cs._hints - hints.has_hintmask = True - if hints.status != 2: - # Check from last_check, see if we may be an implicit vstem - for i in range(hints.last_checked, index - 1): - if isinstance(cs.program[i], str): - hints.status = 2 - break - else: - # We are an implicit vstem - hints.has_hint = True - hints.last_hint = index + 1 - hints.status = 0 - hints.last_checked = index + 1 - - def processHint(self, index): - cs = self.callingStack[-1] - hints = cs._hints - hints.has_hint = True - hints.last_hint = index - hints.last_checked = index - - def processSubr(self, index, subr): - cs = self.callingStack[-1] - hints = cs._hints - subr_hints = subr._hints - - # Check from last_check, make sure we didn't have - # any operators. - if hints.status != 2: - for i in range(hints.last_checked, index - 1): - if isinstance(cs.program[i], str): - hints.status = 2 - break - hints.last_checked = index - - if hints.status != 2: - if subr_hints.has_hint: - hints.has_hint = True - - # Decide where to chop off from - if subr_hints.status == 0: - hints.last_hint = index - else: - hints.last_hint = index - 2 # Leave the subr call in - - elif subr_hints.status == 0: - hints.deletions.append(index) - - hints.status = max(hints.status, subr_hints.status) - - -def _cs_subset_subroutines(charstring, subrs, gsubrs): - p = charstring.program - for i in range(1, len(p)): - if p[i] == "callsubr": - assert isinstance(p[i - 1], int) - p[i - 1] = subrs._used.index(p[i - 1] + subrs._old_bias) - subrs._new_bias - elif p[i] == "callgsubr": - assert isinstance(p[i - 1], int) - p[i - 1] = ( - gsubrs._used.index(p[i - 1] + gsubrs._old_bias) - gsubrs._new_bias - ) - - -def _cs_drop_hints(charstring): - hints = charstring._hints - - if hints.deletions: - p = charstring.program - for idx in reversed(hints.deletions): - del p[idx - 2 : idx] - - if hints.has_hint: - assert not hints.deletions or hints.last_hint <= hints.deletions[0] - charstring.program = charstring.program[hints.last_hint :] - if not charstring.program: - # TODO CFF2 no need for endchar. - charstring.program.append("endchar") - if hasattr(charstring, "width"): - # Insert width back if needed - if charstring.width != charstring.private.defaultWidthX: - # For CFF2 charstrings, this should never happen - assert ( - charstring.private.defaultWidthX is not None - ), "CFF2 CharStrings must not have an initial width value" - charstring.program.insert( - 0, charstring.width - charstring.private.nominalWidthX - ) - - if hints.has_hintmask: - i = 0 - p = charstring.program - while i < len(p): - if p[i] in ["hintmask", "cntrmask"]: - assert i + 1 <= len(p) - del p[i : i + 2] - continue - i += 1 - - assert len(charstring.program) - - del charstring._hints - - -def remove_hints(cff, *, removeUnusedSubrs: bool = True): - for fontname in cff.keys(): - font = cff[fontname] - cs = font.CharStrings - # This can be tricky, but doesn't have to. What we do is: - # - # - Run all used glyph charstrings and recurse into subroutines, - # - For each charstring (including subroutines), if it has any - # of the hint stem operators, we mark it as such. - # Upon returning, for each charstring we note all the - # subroutine calls it makes that (recursively) contain a stem, - # - Dropping hinting then consists of the following two ops: - # * Drop the piece of the program in each charstring before the - # last call to a stem op or a stem-calling subroutine, - # * Drop all hintmask operations. - # - It's trickier... A hintmask right after hints and a few numbers - # will act as an implicit vstemhm. As such, we track whether - # we have seen any non-hint operators so far and do the right - # thing, recursively... Good luck understanding that :( - css = set() - for c in cs.values(): - c.decompile() - subrs = getattr(c.private, "Subrs", []) - decompiler = _DehintingT2Decompiler( - css, - subrs, - c.globalSubrs, - c.private.nominalWidthX, - c.private.defaultWidthX, - c.private, - ) - decompiler.execute(c) - c.width = decompiler.width - for charstring in css: - _cs_drop_hints(charstring) - del css - - # Drop font-wide hinting values - all_privs = [] - if hasattr(font, "FDArray"): - all_privs.extend(fd.Private for fd in font.FDArray) - else: - all_privs.append(font.Private) - for priv in all_privs: - for k in [ - "BlueValues", - "OtherBlues", - "FamilyBlues", - "FamilyOtherBlues", - "BlueScale", - "BlueShift", - "BlueFuzz", - "StemSnapH", - "StemSnapV", - "StdHW", - "StdVW", - "ForceBold", - "LanguageGroup", - "ExpansionFactor", - ]: - if hasattr(priv, k): - setattr(priv, k, None) - if removeUnusedSubrs: - remove_unused_subroutines(cff) - - -def _pd_delete_empty_subrs(private_dict): - if hasattr(private_dict, "Subrs") and not private_dict.Subrs: - if "Subrs" in private_dict.rawDict: - del private_dict.rawDict["Subrs"] - del private_dict.Subrs - - -def remove_unused_subroutines(cff): - for fontname in cff.keys(): - font = cff[fontname] - cs = font.CharStrings - # Renumber subroutines to remove unused ones - - # Mark all used subroutines - for c in cs.values(): - subrs = getattr(c.private, "Subrs", []) - decompiler = _MarkingT2Decompiler(subrs, c.globalSubrs, c.private) - decompiler.execute(c) - - all_subrs = [font.GlobalSubrs] - if hasattr(font, "FDArray"): - all_subrs.extend( - fd.Private.Subrs - for fd in font.FDArray - if hasattr(fd.Private, "Subrs") and fd.Private.Subrs - ) - elif hasattr(font.Private, "Subrs") and font.Private.Subrs: - all_subrs.append(font.Private.Subrs) - - subrs = set(subrs) # Remove duplicates - - # Prepare - for subrs in all_subrs: - if not hasattr(subrs, "_used"): - subrs._used = set() - subrs._used = _uniq_sort(subrs._used) - subrs._old_bias = calcSubrBias(subrs) - subrs._new_bias = calcSubrBias(subrs._used) - - # Renumber glyph charstrings - for c in cs.values(): - subrs = getattr(c.private, "Subrs", None) - _cs_subset_subroutines(c, subrs, font.GlobalSubrs) - - # Renumber subroutines themselves - for subrs in all_subrs: - if subrs == font.GlobalSubrs: - if not hasattr(font, "FDArray") and hasattr(font.Private, "Subrs"): - local_subrs = font.Private.Subrs - elif hasattr(font, "FDArray") and len(font.FDArray) == 1: - local_subrs = font.FDArray[0].Private.Subrs - else: - local_subrs = None - else: - local_subrs = subrs - - subrs.items = [subrs.items[i] for i in subrs._used] - if hasattr(subrs, "file"): - del subrs.file - if hasattr(subrs, "offsets"): - del subrs.offsets - - for subr in subrs.items: - _cs_subset_subroutines(subr, local_subrs, font.GlobalSubrs) - - # Delete local SubrsIndex if empty - if hasattr(font, "FDArray"): - for fd in font.FDArray: - _pd_delete_empty_subrs(fd.Private) - else: - _pd_delete_empty_subrs(font.Private) - - # Cleanup - for subrs in all_subrs: - del subrs._used, subrs._old_bias, subrs._new_bias diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/width.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/width.py deleted file mode 100644 index 78ff27e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cffLib/width.py +++ /dev/null @@ -1,210 +0,0 @@ -# -*- coding: utf-8 -*- - -"""T2CharString glyph width optimizer. - -CFF glyphs whose width equals the CFF Private dictionary's ``defaultWidthX`` -value do not need to specify their width in their charstring, saving bytes. -This module determines the optimum ``defaultWidthX`` and ``nominalWidthX`` -values for a font, when provided with a list of glyph widths.""" - -from fontTools.ttLib import TTFont -from collections import defaultdict -from operator import add -from functools import reduce - - -__all__ = ["optimizeWidths", "main"] - - -class missingdict(dict): - def __init__(self, missing_func): - self.missing_func = missing_func - - def __missing__(self, v): - return self.missing_func(v) - - -def cumSum(f, op=add, start=0, decreasing=False): - keys = sorted(f.keys()) - minx, maxx = keys[0], keys[-1] - - total = reduce(op, f.values(), start) - - if decreasing: - missing = lambda x: start if x > maxx else total - domain = range(maxx, minx - 1, -1) - else: - missing = lambda x: start if x < minx else total - domain = range(minx, maxx + 1) - - out = missingdict(missing) - - v = start - for x in domain: - v = op(v, f[x]) - out[x] = v - - return out - - -def byteCost(widths, default, nominal): - if not hasattr(widths, "items"): - d = defaultdict(int) - for w in widths: - d[w] += 1 - widths = d - - cost = 0 - for w, freq in widths.items(): - if w == default: - continue - diff = abs(w - nominal) - if diff <= 107: - cost += freq - elif diff <= 1131: - cost += freq * 2 - else: - cost += freq * 5 - return cost - - -def optimizeWidthsBruteforce(widths): - """Bruteforce version. Veeeeeeeeeeeeeeeeery slow. Only works for smallests of fonts.""" - - d = defaultdict(int) - for w in widths: - d[w] += 1 - - # Maximum number of bytes using default can possibly save - maxDefaultAdvantage = 5 * max(d.values()) - - minw, maxw = min(widths), max(widths) - domain = list(range(minw, maxw + 1)) - - bestCostWithoutDefault = min(byteCost(widths, None, nominal) for nominal in domain) - - bestCost = len(widths) * 5 + 1 - for nominal in domain: - if byteCost(widths, None, nominal) > bestCost + maxDefaultAdvantage: - continue - for default in domain: - cost = byteCost(widths, default, nominal) - if cost < bestCost: - bestCost = cost - bestDefault = default - bestNominal = nominal - - return bestDefault, bestNominal - - -def optimizeWidths(widths): - """Given a list of glyph widths, or dictionary mapping glyph width to number of - glyphs having that, returns a tuple of best CFF default and nominal glyph widths. - - This algorithm is linear in UPEM+numGlyphs.""" - - if not hasattr(widths, "items"): - d = defaultdict(int) - for w in widths: - d[w] += 1 - widths = d - - keys = sorted(widths.keys()) - minw, maxw = keys[0], keys[-1] - domain = list(range(minw, maxw + 1)) - - # Cumulative sum/max forward/backward. - cumFrqU = cumSum(widths, op=add) - cumMaxU = cumSum(widths, op=max) - cumFrqD = cumSum(widths, op=add, decreasing=True) - cumMaxD = cumSum(widths, op=max, decreasing=True) - - # Cost per nominal choice, without default consideration. - nomnCostU = missingdict( - lambda x: cumFrqU[x] + cumFrqU[x - 108] + cumFrqU[x - 1132] * 3 - ) - nomnCostD = missingdict( - lambda x: cumFrqD[x] + cumFrqD[x + 108] + cumFrqD[x + 1132] * 3 - ) - nomnCost = missingdict(lambda x: nomnCostU[x] + nomnCostD[x] - widths[x]) - - # Cost-saving per nominal choice, by best default choice. - dfltCostU = missingdict( - lambda x: max(cumMaxU[x], cumMaxU[x - 108] * 2, cumMaxU[x - 1132] * 5) - ) - dfltCostD = missingdict( - lambda x: max(cumMaxD[x], cumMaxD[x + 108] * 2, cumMaxD[x + 1132] * 5) - ) - dfltCost = missingdict(lambda x: max(dfltCostU[x], dfltCostD[x])) - - # Combined cost per nominal choice. - bestCost = missingdict(lambda x: nomnCost[x] - dfltCost[x]) - - # Best nominal. - nominal = min(domain, key=lambda x: bestCost[x]) - - # Work back the best default. - bestC = bestCost[nominal] - dfltC = nomnCost[nominal] - bestCost[nominal] - ends = [] - if dfltC == dfltCostU[nominal]: - starts = [nominal, nominal - 108, nominal - 1132] - for start in starts: - while cumMaxU[start] and cumMaxU[start] == cumMaxU[start - 1]: - start -= 1 - ends.append(start) - else: - starts = [nominal, nominal + 108, nominal + 1132] - for start in starts: - while cumMaxD[start] and cumMaxD[start] == cumMaxD[start + 1]: - start += 1 - ends.append(start) - default = min(ends, key=lambda default: byteCost(widths, default, nominal)) - - return default, nominal - - -def main(args=None): - """Calculate optimum defaultWidthX/nominalWidthX values""" - - import argparse - - parser = argparse.ArgumentParser( - "fonttools cffLib.width", - description=main.__doc__, - ) - parser.add_argument( - "inputs", metavar="FILE", type=str, nargs="+", help="Input TTF files" - ) - parser.add_argument( - "-b", - "--brute-force", - dest="brute", - action="store_true", - help="Use brute-force approach (VERY slow)", - ) - - args = parser.parse_args(args) - - for fontfile in args.inputs: - font = TTFont(fontfile) - hmtx = font["hmtx"] - widths = [m[0] for m in hmtx.metrics.values()] - if args.brute: - default, nominal = optimizeWidthsBruteforce(widths) - else: - default, nominal = optimizeWidths(widths) - print( - "glyphs=%d default=%d nominal=%d byteCost=%d" - % (len(widths), default, nominal, byteCost(widths, default, nominal)) - ) - - -if __name__ == "__main__": - import sys - - if len(sys.argv) == 1: - import doctest - - sys.exit(doctest.testmod().failed) - main() diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index aff5e09..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/__pycache__/builder.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/__pycache__/builder.cpython-38.pyc deleted file mode 100644 index f68bc58..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/__pycache__/builder.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/__pycache__/errors.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/__pycache__/errors.cpython-38.pyc deleted file mode 100644 index 7bcfd3d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/__pycache__/errors.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/__pycache__/geometry.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/__pycache__/geometry.cpython-38.pyc deleted file mode 100644 index c5b8177..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/__pycache__/geometry.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/__pycache__/table_builder.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/__pycache__/table_builder.cpython-38.pyc deleted file mode 100644 index c51866a..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/__pycache__/table_builder.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/__pycache__/unbuilder.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/__pycache__/unbuilder.cpython-38.pyc deleted file mode 100644 index d2e08fd..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/__pycache__/unbuilder.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/builder.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/builder.py deleted file mode 100644 index 6e45e7a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/builder.py +++ /dev/null @@ -1,664 +0,0 @@ -""" -colorLib.builder: Build COLR/CPAL tables from scratch - -""" - -import collections -import copy -import enum -from functools import partial -from math import ceil, log -from typing import ( - Any, - Dict, - Generator, - Iterable, - List, - Mapping, - Optional, - Sequence, - Tuple, - Type, - TypeVar, - Union, -) -from fontTools.misc.arrayTools import intRect -from fontTools.misc.fixedTools import fixedToFloat -from fontTools.misc.treeTools import build_n_ary_tree -from fontTools.ttLib.tables import C_O_L_R_ -from fontTools.ttLib.tables import C_P_A_L_ -from fontTools.ttLib.tables import _n_a_m_e -from fontTools.ttLib.tables import otTables as ot -from fontTools.ttLib.tables.otTables import ExtendMode, CompositeMode -from .errors import ColorLibError -from .geometry import round_start_circle_stable_containment -from .table_builder import BuildCallback, TableBuilder - - -# TODO move type aliases to colorLib.types? -T = TypeVar("T") -_Kwargs = Mapping[str, Any] -_PaintInput = Union[int, _Kwargs, ot.Paint, Tuple[str, "_PaintInput"]] -_PaintInputList = Sequence[_PaintInput] -_ColorGlyphsDict = Dict[str, Union[_PaintInputList, _PaintInput]] -_ColorGlyphsV0Dict = Dict[str, Sequence[Tuple[str, int]]] -_ClipBoxInput = Union[ - Tuple[int, int, int, int, int], # format 1, variable - Tuple[int, int, int, int], # format 0, non-variable - ot.ClipBox, -] - - -MAX_PAINT_COLR_LAYER_COUNT = 255 -_DEFAULT_ALPHA = 1.0 -_MAX_REUSE_LEN = 32 - - -def _beforeBuildPaintRadialGradient(paint, source): - x0 = source["x0"] - y0 = source["y0"] - r0 = source["r0"] - x1 = source["x1"] - y1 = source["y1"] - r1 = source["r1"] - - # TODO apparently no builder_test confirms this works (?) - - # avoid abrupt change after rounding when c0 is near c1's perimeter - c = round_start_circle_stable_containment((x0, y0), r0, (x1, y1), r1) - x0, y0 = c.centre - r0 = c.radius - - # update source to ensure paint is built with corrected values - source["x0"] = x0 - source["y0"] = y0 - source["r0"] = r0 - source["x1"] = x1 - source["y1"] = y1 - source["r1"] = r1 - - return paint, source - - -def _defaultColorStop(): - colorStop = ot.ColorStop() - colorStop.Alpha = _DEFAULT_ALPHA - return colorStop - - -def _defaultVarColorStop(): - colorStop = ot.VarColorStop() - colorStop.Alpha = _DEFAULT_ALPHA - return colorStop - - -def _defaultColorLine(): - colorLine = ot.ColorLine() - colorLine.Extend = ExtendMode.PAD - return colorLine - - -def _defaultVarColorLine(): - colorLine = ot.VarColorLine() - colorLine.Extend = ExtendMode.PAD - return colorLine - - -def _defaultPaintSolid(): - paint = ot.Paint() - paint.Alpha = _DEFAULT_ALPHA - return paint - - -def _buildPaintCallbacks(): - return { - ( - BuildCallback.BEFORE_BUILD, - ot.Paint, - ot.PaintFormat.PaintRadialGradient, - ): _beforeBuildPaintRadialGradient, - ( - BuildCallback.BEFORE_BUILD, - ot.Paint, - ot.PaintFormat.PaintVarRadialGradient, - ): _beforeBuildPaintRadialGradient, - (BuildCallback.CREATE_DEFAULT, ot.ColorStop): _defaultColorStop, - (BuildCallback.CREATE_DEFAULT, ot.VarColorStop): _defaultVarColorStop, - (BuildCallback.CREATE_DEFAULT, ot.ColorLine): _defaultColorLine, - (BuildCallback.CREATE_DEFAULT, ot.VarColorLine): _defaultVarColorLine, - ( - BuildCallback.CREATE_DEFAULT, - ot.Paint, - ot.PaintFormat.PaintSolid, - ): _defaultPaintSolid, - ( - BuildCallback.CREATE_DEFAULT, - ot.Paint, - ot.PaintFormat.PaintVarSolid, - ): _defaultPaintSolid, - } - - -def populateCOLRv0( - table: ot.COLR, - colorGlyphsV0: _ColorGlyphsV0Dict, - glyphMap: Optional[Mapping[str, int]] = None, -): - """Build v0 color layers and add to existing COLR table. - - Args: - table: a raw ``otTables.COLR()`` object (not ttLib's ``table_C_O_L_R_``). - colorGlyphsV0: map of base glyph names to lists of (layer glyph names, - color palette index) tuples. Can be empty. - glyphMap: a map from glyph names to glyph indices, as returned from - ``TTFont.getReverseGlyphMap()``, to optionally sort base records by GID. - """ - if glyphMap is not None: - colorGlyphItems = sorted( - colorGlyphsV0.items(), key=lambda item: glyphMap[item[0]] - ) - else: - colorGlyphItems = colorGlyphsV0.items() - baseGlyphRecords = [] - layerRecords = [] - for baseGlyph, layers in colorGlyphItems: - baseRec = ot.BaseGlyphRecord() - baseRec.BaseGlyph = baseGlyph - baseRec.FirstLayerIndex = len(layerRecords) - baseRec.NumLayers = len(layers) - baseGlyphRecords.append(baseRec) - - for layerGlyph, paletteIndex in layers: - layerRec = ot.LayerRecord() - layerRec.LayerGlyph = layerGlyph - layerRec.PaletteIndex = paletteIndex - layerRecords.append(layerRec) - - table.BaseGlyphRecordArray = table.LayerRecordArray = None - if baseGlyphRecords: - table.BaseGlyphRecordArray = ot.BaseGlyphRecordArray() - table.BaseGlyphRecordArray.BaseGlyphRecord = baseGlyphRecords - if layerRecords: - table.LayerRecordArray = ot.LayerRecordArray() - table.LayerRecordArray.LayerRecord = layerRecords - table.BaseGlyphRecordCount = len(baseGlyphRecords) - table.LayerRecordCount = len(layerRecords) - - -def buildCOLR( - colorGlyphs: _ColorGlyphsDict, - version: Optional[int] = None, - *, - glyphMap: Optional[Mapping[str, int]] = None, - varStore: Optional[ot.VarStore] = None, - varIndexMap: Optional[ot.DeltaSetIndexMap] = None, - clipBoxes: Optional[Dict[str, _ClipBoxInput]] = None, - allowLayerReuse: bool = True, -) -> C_O_L_R_.table_C_O_L_R_: - """Build COLR table from color layers mapping. - - Args: - - colorGlyphs: map of base glyph name to, either list of (layer glyph name, - color palette index) tuples for COLRv0; or a single ``Paint`` (dict) or - list of ``Paint`` for COLRv1. - version: the version of COLR table. If None, the version is determined - by the presence of COLRv1 paints or variation data (varStore), which - require version 1; otherwise, if all base glyphs use only simple color - layers, version 0 is used. - glyphMap: a map from glyph names to glyph indices, as returned from - TTFont.getReverseGlyphMap(), to optionally sort base records by GID. - varStore: Optional ItemVarationStore for deltas associated with v1 layer. - varIndexMap: Optional DeltaSetIndexMap for deltas associated with v1 layer. - clipBoxes: Optional map of base glyph name to clip box 4- or 5-tuples: - (xMin, yMin, xMax, yMax) or (xMin, yMin, xMax, yMax, varIndexBase). - - Returns: - A new COLR table. - """ - self = C_O_L_R_.table_C_O_L_R_() - - if varStore is not None and version == 0: - raise ValueError("Can't add VarStore to COLRv0") - - if version in (None, 0) and not varStore: - # split color glyphs into v0 and v1 and encode separately - colorGlyphsV0, colorGlyphsV1 = _split_color_glyphs_by_version(colorGlyphs) - if version == 0 and colorGlyphsV1: - raise ValueError("Can't encode COLRv1 glyphs in COLRv0") - else: - # unless explicitly requested for v1 or have variations, in which case - # we encode all color glyph as v1 - colorGlyphsV0, colorGlyphsV1 = {}, colorGlyphs - - colr = ot.COLR() - - populateCOLRv0(colr, colorGlyphsV0, glyphMap) - - colr.LayerList, colr.BaseGlyphList = buildColrV1( - colorGlyphsV1, - glyphMap, - allowLayerReuse=allowLayerReuse, - ) - - if version is None: - version = 1 if (varStore or colorGlyphsV1) else 0 - elif version not in (0, 1): - raise NotImplementedError(version) - self.version = colr.Version = version - - if version == 0: - self.ColorLayers = self._decompileColorLayersV0(colr) - else: - colr.ClipList = buildClipList(clipBoxes) if clipBoxes else None - colr.VarIndexMap = varIndexMap - colr.VarStore = varStore - self.table = colr - - return self - - -def buildClipList(clipBoxes: Dict[str, _ClipBoxInput]) -> ot.ClipList: - clipList = ot.ClipList() - clipList.Format = 1 - clipList.clips = {name: buildClipBox(box) for name, box in clipBoxes.items()} - return clipList - - -def buildClipBox(clipBox: _ClipBoxInput) -> ot.ClipBox: - if isinstance(clipBox, ot.ClipBox): - return clipBox - n = len(clipBox) - clip = ot.ClipBox() - if n not in (4, 5): - raise ValueError(f"Invalid ClipBox: expected 4 or 5 values, found {n}") - clip.xMin, clip.yMin, clip.xMax, clip.yMax = intRect(clipBox[:4]) - clip.Format = int(n == 5) + 1 - if n == 5: - clip.VarIndexBase = int(clipBox[4]) - return clip - - -class ColorPaletteType(enum.IntFlag): - USABLE_WITH_LIGHT_BACKGROUND = 0x0001 - USABLE_WITH_DARK_BACKGROUND = 0x0002 - - @classmethod - def _missing_(cls, value): - # enforce reserved bits - if isinstance(value, int) and (value < 0 or value & 0xFFFC != 0): - raise ValueError(f"{value} is not a valid {cls.__name__}") - return super()._missing_(value) - - -# None, 'abc' or {'en': 'abc', 'de': 'xyz'} -_OptionalLocalizedString = Union[None, str, Dict[str, str]] - - -def buildPaletteLabels( - labels: Iterable[_OptionalLocalizedString], nameTable: _n_a_m_e.table__n_a_m_e -) -> List[Optional[int]]: - return [ - ( - nameTable.addMultilingualName(l, mac=False) - if isinstance(l, dict) - else ( - C_P_A_L_.table_C_P_A_L_.NO_NAME_ID - if l is None - else nameTable.addMultilingualName({"en": l}, mac=False) - ) - ) - for l in labels - ] - - -def buildCPAL( - palettes: Sequence[Sequence[Tuple[float, float, float, float]]], - paletteTypes: Optional[Sequence[ColorPaletteType]] = None, - paletteLabels: Optional[Sequence[_OptionalLocalizedString]] = None, - paletteEntryLabels: Optional[Sequence[_OptionalLocalizedString]] = None, - nameTable: Optional[_n_a_m_e.table__n_a_m_e] = None, -) -> C_P_A_L_.table_C_P_A_L_: - """Build CPAL table from list of color palettes. - - Args: - palettes: list of lists of colors encoded as tuples of (R, G, B, A) floats - in the range [0..1]. - paletteTypes: optional list of ColorPaletteType, one for each palette. - paletteLabels: optional list of palette labels. Each lable can be either: - None (no label), a string (for for default English labels), or a - localized string (as a dict keyed with BCP47 language codes). - paletteEntryLabels: optional list of palette entry labels, one for each - palette entry (see paletteLabels). - nameTable: optional name table where to store palette and palette entry - labels. Required if either paletteLabels or paletteEntryLabels is set. - - Return: - A new CPAL v0 or v1 table, if custom palette types or labels are specified. - """ - if len({len(p) for p in palettes}) != 1: - raise ColorLibError("color palettes have different lengths") - - if (paletteLabels or paletteEntryLabels) and not nameTable: - raise TypeError( - "nameTable is required if palette or palette entries have labels" - ) - - cpal = C_P_A_L_.table_C_P_A_L_() - cpal.numPaletteEntries = len(palettes[0]) - - cpal.palettes = [] - for i, palette in enumerate(palettes): - colors = [] - for j, color in enumerate(palette): - if not isinstance(color, tuple) or len(color) != 4: - raise ColorLibError( - f"In palette[{i}][{j}]: expected (R, G, B, A) tuple, got {color!r}" - ) - if any(v > 1 or v < 0 for v in color): - raise ColorLibError( - f"palette[{i}][{j}] has invalid out-of-range [0..1] color: {color!r}" - ) - # input colors are RGBA, CPAL encodes them as BGRA - red, green, blue, alpha = color - colors.append( - C_P_A_L_.Color(*(round(v * 255) for v in (blue, green, red, alpha))) - ) - cpal.palettes.append(colors) - - if any(v is not None for v in (paletteTypes, paletteLabels, paletteEntryLabels)): - cpal.version = 1 - - if paletteTypes is not None: - if len(paletteTypes) != len(palettes): - raise ColorLibError( - f"Expected {len(palettes)} paletteTypes, got {len(paletteTypes)}" - ) - cpal.paletteTypes = [ColorPaletteType(t).value for t in paletteTypes] - else: - cpal.paletteTypes = [C_P_A_L_.table_C_P_A_L_.DEFAULT_PALETTE_TYPE] * len( - palettes - ) - - if paletteLabels is not None: - if len(paletteLabels) != len(palettes): - raise ColorLibError( - f"Expected {len(palettes)} paletteLabels, got {len(paletteLabels)}" - ) - cpal.paletteLabels = buildPaletteLabels(paletteLabels, nameTable) - else: - cpal.paletteLabels = [C_P_A_L_.table_C_P_A_L_.NO_NAME_ID] * len(palettes) - - if paletteEntryLabels is not None: - if len(paletteEntryLabels) != cpal.numPaletteEntries: - raise ColorLibError( - f"Expected {cpal.numPaletteEntries} paletteEntryLabels, " - f"got {len(paletteEntryLabels)}" - ) - cpal.paletteEntryLabels = buildPaletteLabels(paletteEntryLabels, nameTable) - else: - cpal.paletteEntryLabels = [ - C_P_A_L_.table_C_P_A_L_.NO_NAME_ID - ] * cpal.numPaletteEntries - else: - cpal.version = 0 - - return cpal - - -# COLR v1 tables -# See draft proposal at: https://github.com/googlefonts/colr-gradients-spec - - -def _is_colrv0_layer(layer: Any) -> bool: - # Consider as COLRv0 layer any sequence of length 2 (be it tuple or list) in which - # the first element is a str (the layerGlyph) and the second element is an int - # (CPAL paletteIndex). - # https://github.com/googlefonts/ufo2ft/issues/426 - try: - layerGlyph, paletteIndex = layer - except (TypeError, ValueError): - return False - else: - return isinstance(layerGlyph, str) and isinstance(paletteIndex, int) - - -def _split_color_glyphs_by_version( - colorGlyphs: _ColorGlyphsDict, -) -> Tuple[_ColorGlyphsV0Dict, _ColorGlyphsDict]: - colorGlyphsV0 = {} - colorGlyphsV1 = {} - for baseGlyph, layers in colorGlyphs.items(): - if all(_is_colrv0_layer(l) for l in layers): - colorGlyphsV0[baseGlyph] = layers - else: - colorGlyphsV1[baseGlyph] = layers - - # sanity check - assert set(colorGlyphs) == (set(colorGlyphsV0) | set(colorGlyphsV1)) - - return colorGlyphsV0, colorGlyphsV1 - - -def _reuse_ranges(num_layers: int) -> Generator[Tuple[int, int], None, None]: - # TODO feels like something itertools might have already - for lbound in range(num_layers): - # Reuse of very large #s of layers is relatively unlikely - # +2: we want sequences of at least 2 - # otData handles single-record duplication - for ubound in range( - lbound + 2, min(num_layers + 1, lbound + 2 + _MAX_REUSE_LEN) - ): - yield (lbound, ubound) - - -class LayerReuseCache: - reusePool: Mapping[Tuple[Any, ...], int] - tuples: Mapping[int, Tuple[Any, ...]] - keepAlive: List[ot.Paint] # we need id to remain valid - - def __init__(self): - self.reusePool = {} - self.tuples = {} - self.keepAlive = [] - - def _paint_tuple(self, paint: ot.Paint): - # start simple, who even cares about cyclic graphs or interesting field types - def _tuple_safe(value): - if isinstance(value, enum.Enum): - return value - elif hasattr(value, "__dict__"): - return tuple( - (k, _tuple_safe(v)) for k, v in sorted(value.__dict__.items()) - ) - elif isinstance(value, collections.abc.MutableSequence): - return tuple(_tuple_safe(e) for e in value) - return value - - # Cache the tuples for individual Paint instead of the whole sequence - # because the seq could be a transient slice - result = self.tuples.get(id(paint), None) - if result is None: - result = _tuple_safe(paint) - self.tuples[id(paint)] = result - self.keepAlive.append(paint) - return result - - def _as_tuple(self, paints: Sequence[ot.Paint]) -> Tuple[Any, ...]: - return tuple(self._paint_tuple(p) for p in paints) - - def try_reuse(self, layers: List[ot.Paint]) -> List[ot.Paint]: - found_reuse = True - while found_reuse: - found_reuse = False - - ranges = sorted( - _reuse_ranges(len(layers)), - key=lambda t: (t[1] - t[0], t[1], t[0]), - reverse=True, - ) - for lbound, ubound in ranges: - reuse_lbound = self.reusePool.get( - self._as_tuple(layers[lbound:ubound]), -1 - ) - if reuse_lbound == -1: - continue - new_slice = ot.Paint() - new_slice.Format = int(ot.PaintFormat.PaintColrLayers) - new_slice.NumLayers = ubound - lbound - new_slice.FirstLayerIndex = reuse_lbound - layers = layers[:lbound] + [new_slice] + layers[ubound:] - found_reuse = True - break - return layers - - def add(self, layers: List[ot.Paint], first_layer_index: int): - for lbound, ubound in _reuse_ranges(len(layers)): - self.reusePool[self._as_tuple(layers[lbound:ubound])] = ( - lbound + first_layer_index - ) - - -class LayerListBuilder: - layers: List[ot.Paint] - cache: LayerReuseCache - allowLayerReuse: bool - - def __init__(self, *, allowLayerReuse=True): - self.layers = [] - if allowLayerReuse: - self.cache = LayerReuseCache() - else: - self.cache = None - - # We need to intercept construction of PaintColrLayers - callbacks = _buildPaintCallbacks() - callbacks[ - ( - BuildCallback.BEFORE_BUILD, - ot.Paint, - ot.PaintFormat.PaintColrLayers, - ) - ] = self._beforeBuildPaintColrLayers - self.tableBuilder = TableBuilder(callbacks) - - # COLR layers is unusual in that it modifies shared state - # so we need a callback into an object - def _beforeBuildPaintColrLayers(self, dest, source): - # Sketchy gymnastics: a sequence input will have dropped it's layers - # into NumLayers; get it back - if isinstance(source.get("NumLayers", None), collections.abc.Sequence): - layers = source["NumLayers"] - else: - layers = source["Layers"] - - # Convert maps seqs or whatever into typed objects - layers = [self.buildPaint(l) for l in layers] - - # No reason to have a colr layers with just one entry - if len(layers) == 1: - return layers[0], {} - - if self.cache is not None: - # Look for reuse, with preference to longer sequences - # This may make the layer list smaller - layers = self.cache.try_reuse(layers) - - # The layer list is now final; if it's too big we need to tree it - is_tree = len(layers) > MAX_PAINT_COLR_LAYER_COUNT - layers = build_n_ary_tree(layers, n=MAX_PAINT_COLR_LAYER_COUNT) - - # We now have a tree of sequences with Paint leaves. - # Convert the sequences into PaintColrLayers. - def listToColrLayers(layer): - if isinstance(layer, collections.abc.Sequence): - return self.buildPaint( - { - "Format": ot.PaintFormat.PaintColrLayers, - "Layers": [listToColrLayers(l) for l in layer], - } - ) - return layer - - layers = [listToColrLayers(l) for l in layers] - - # No reason to have a colr layers with just one entry - if len(layers) == 1: - return layers[0], {} - - paint = ot.Paint() - paint.Format = int(ot.PaintFormat.PaintColrLayers) - paint.NumLayers = len(layers) - paint.FirstLayerIndex = len(self.layers) - self.layers.extend(layers) - - # Register our parts for reuse provided we aren't a tree - # If we are a tree the leaves registered for reuse and that will suffice - if self.cache is not None and not is_tree: - self.cache.add(layers, paint.FirstLayerIndex) - - # we've fully built dest; empty source prevents generalized build from kicking in - return paint, {} - - def buildPaint(self, paint: _PaintInput) -> ot.Paint: - return self.tableBuilder.build(ot.Paint, paint) - - def build(self) -> Optional[ot.LayerList]: - if not self.layers: - return None - layers = ot.LayerList() - layers.LayerCount = len(self.layers) - layers.Paint = self.layers - return layers - - -def buildBaseGlyphPaintRecord( - baseGlyph: str, layerBuilder: LayerListBuilder, paint: _PaintInput -) -> ot.BaseGlyphList: - self = ot.BaseGlyphPaintRecord() - self.BaseGlyph = baseGlyph - self.Paint = layerBuilder.buildPaint(paint) - return self - - -def _format_glyph_errors(errors: Mapping[str, Exception]) -> str: - lines = [] - for baseGlyph, error in sorted(errors.items()): - lines.append(f" {baseGlyph} => {type(error).__name__}: {error}") - return "\n".join(lines) - - -def buildColrV1( - colorGlyphs: _ColorGlyphsDict, - glyphMap: Optional[Mapping[str, int]] = None, - *, - allowLayerReuse: bool = True, -) -> Tuple[Optional[ot.LayerList], ot.BaseGlyphList]: - if glyphMap is not None: - colorGlyphItems = sorted( - colorGlyphs.items(), key=lambda item: glyphMap[item[0]] - ) - else: - colorGlyphItems = colorGlyphs.items() - - errors = {} - baseGlyphs = [] - layerBuilder = LayerListBuilder(allowLayerReuse=allowLayerReuse) - for baseGlyph, paint in colorGlyphItems: - try: - baseGlyphs.append(buildBaseGlyphPaintRecord(baseGlyph, layerBuilder, paint)) - - except (ColorLibError, OverflowError, ValueError, TypeError) as e: - errors[baseGlyph] = e - - if errors: - failed_glyphs = _format_glyph_errors(errors) - exc = ColorLibError(f"Failed to build BaseGlyphList:\n{failed_glyphs}") - exc.errors = errors - raise exc from next(iter(errors.values())) - - layers = layerBuilder.build() - glyphs = ot.BaseGlyphList() - glyphs.BaseGlyphCount = len(baseGlyphs) - glyphs.BaseGlyphPaintRecord = baseGlyphs - return (layers, glyphs) diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/errors.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/errors.py deleted file mode 100644 index 18cbebb..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/errors.py +++ /dev/null @@ -1,2 +0,0 @@ -class ColorLibError(Exception): - pass diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/geometry.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/geometry.py deleted file mode 100644 index 1ce161b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/geometry.py +++ /dev/null @@ -1,143 +0,0 @@ -"""Helpers for manipulating 2D points and vectors in COLR table.""" - -from math import copysign, cos, hypot, isclose, pi -from fontTools.misc.roundTools import otRound - - -def _vector_between(origin, target): - return (target[0] - origin[0], target[1] - origin[1]) - - -def _round_point(pt): - return (otRound(pt[0]), otRound(pt[1])) - - -def _unit_vector(vec): - length = hypot(*vec) - if length == 0: - return None - return (vec[0] / length, vec[1] / length) - - -_CIRCLE_INSIDE_TOLERANCE = 1e-4 - - -# The unit vector's X and Y components are respectively -# U = (cos(α), sin(α)) -# where α is the angle between the unit vector and the positive x axis. -_UNIT_VECTOR_THRESHOLD = cos(3 / 8 * pi) # == sin(1/8 * pi) == 0.38268343236508984 - - -def _rounding_offset(direction): - # Return 2-tuple of -/+ 1.0 or 0.0 approximately based on the direction vector. - # We divide the unit circle in 8 equal slices oriented towards the cardinal - # (N, E, S, W) and intermediate (NE, SE, SW, NW) directions. To each slice we - # map one of the possible cases: -1, 0, +1 for either X and Y coordinate. - # E.g. Return (+1.0, -1.0) if unit vector is oriented towards SE, or - # (-1.0, 0.0) if it's pointing West, etc. - uv = _unit_vector(direction) - if not uv: - return (0, 0) - - result = [] - for uv_component in uv: - if -_UNIT_VECTOR_THRESHOLD <= uv_component < _UNIT_VECTOR_THRESHOLD: - # unit vector component near 0: direction almost orthogonal to the - # direction of the current axis, thus keep coordinate unchanged - result.append(0) - else: - # nudge coord by +/- 1.0 in direction of unit vector - result.append(copysign(1.0, uv_component)) - return tuple(result) - - -class Circle: - def __init__(self, centre, radius): - self.centre = centre - self.radius = radius - - def __repr__(self): - return f"Circle(centre={self.centre}, radius={self.radius})" - - def round(self): - return Circle(_round_point(self.centre), otRound(self.radius)) - - def inside(self, outer_circle, tolerance=_CIRCLE_INSIDE_TOLERANCE): - dist = self.radius + hypot(*_vector_between(self.centre, outer_circle.centre)) - return ( - isclose(outer_circle.radius, dist, rel_tol=_CIRCLE_INSIDE_TOLERANCE) - or outer_circle.radius > dist - ) - - def concentric(self, other): - return self.centre == other.centre - - def move(self, dx, dy): - self.centre = (self.centre[0] + dx, self.centre[1] + dy) - - -def round_start_circle_stable_containment(c0, r0, c1, r1): - """Round start circle so that it stays inside/outside end circle after rounding. - - The rounding of circle coordinates to integers may cause an abrupt change - if the start circle c0 is so close to the end circle c1's perimiter that - it ends up falling outside (or inside) as a result of the rounding. - To keep the gradient unchanged, we nudge it in the right direction. - - See: - https://github.com/googlefonts/colr-gradients-spec/issues/204 - https://github.com/googlefonts/picosvg/issues/158 - """ - start, end = Circle(c0, r0), Circle(c1, r1) - - inside_before_round = start.inside(end) - - round_start = start.round() - round_end = end.round() - inside_after_round = round_start.inside(round_end) - - if inside_before_round == inside_after_round: - return round_start - elif inside_after_round: - # start was outside before rounding: we need to push start away from end - direction = _vector_between(round_end.centre, round_start.centre) - radius_delta = +1.0 - else: - # start was inside before rounding: we need to push start towards end - direction = _vector_between(round_start.centre, round_end.centre) - radius_delta = -1.0 - dx, dy = _rounding_offset(direction) - - # At most 2 iterations ought to be enough to converge. Before the loop, we - # know the start circle didn't keep containment after normal rounding; thus - # we continue adjusting by -/+ 1.0 until containment is restored. - # Normal rounding can at most move each coordinates -/+0.5; in the worst case - # both the start and end circle's centres and radii will be rounded in opposite - # directions, e.g. when they move along a 45 degree diagonal: - # c0 = (1.5, 1.5) ===> (2.0, 2.0) - # r0 = 0.5 ===> 1.0 - # c1 = (0.499, 0.499) ===> (0.0, 0.0) - # r1 = 2.499 ===> 2.0 - # In this example, the relative distance between the circles, calculated - # as r1 - (r0 + distance(c0, c1)) is initially 0.57437 (c0 is inside c1), and - # -1.82842 after rounding (c0 is now outside c1). Nudging c0 by -1.0 on both - # x and y axes moves it towards c1 by hypot(-1.0, -1.0) = 1.41421. Two of these - # moves cover twice that distance, which is enough to restore containment. - max_attempts = 2 - for _ in range(max_attempts): - if round_start.concentric(round_end): - # can't move c0 towards c1 (they are the same), so we change the radius - round_start.radius += radius_delta - assert round_start.radius >= 0 - else: - round_start.move(dx, dy) - if inside_before_round == round_start.inside(round_end): - break - else: # likely a bug - raise AssertionError( - f"Rounding circle {start} " - f"{'inside' if inside_before_round else 'outside'} " - f"{end} failed after {max_attempts} attempts!" - ) - - return round_start diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/table_builder.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/table_builder.py deleted file mode 100644 index f1e182c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/table_builder.py +++ /dev/null @@ -1,223 +0,0 @@ -""" -colorLib.table_builder: Generic helper for filling in BaseTable derivatives from tuples and maps and such. - -""" - -import collections -import enum -from fontTools.ttLib.tables.otBase import ( - BaseTable, - FormatSwitchingBaseTable, - UInt8FormatSwitchingBaseTable, -) -from fontTools.ttLib.tables.otConverters import ( - ComputedInt, - SimpleValue, - Struct, - Short, - UInt8, - UShort, - IntValue, - FloatValue, - OptionalValue, -) -from fontTools.misc.roundTools import otRound - - -class BuildCallback(enum.Enum): - """Keyed on (BEFORE_BUILD, class[, Format if available]). - Receives (dest, source). - Should return (dest, source), which can be new objects. - """ - - BEFORE_BUILD = enum.auto() - - """Keyed on (AFTER_BUILD, class[, Format if available]). - Receives (dest). - Should return dest, which can be a new object. - """ - AFTER_BUILD = enum.auto() - - """Keyed on (CREATE_DEFAULT, class[, Format if available]). - Receives no arguments. - Should return a new instance of class. - """ - CREATE_DEFAULT = enum.auto() - - -def _assignable(convertersByName): - return {k: v for k, v in convertersByName.items() if not isinstance(v, ComputedInt)} - - -def _isNonStrSequence(value): - return isinstance(value, collections.abc.Sequence) and not isinstance(value, str) - - -def _split_format(cls, source): - if _isNonStrSequence(source): - assert len(source) > 0, f"{cls} needs at least format from {source}" - fmt, remainder = source[0], source[1:] - elif isinstance(source, collections.abc.Mapping): - assert "Format" in source, f"{cls} needs at least Format from {source}" - remainder = source.copy() - fmt = remainder.pop("Format") - else: - raise ValueError(f"Not sure how to populate {cls} from {source}") - - assert isinstance( - fmt, collections.abc.Hashable - ), f"{cls} Format is not hashable: {fmt!r}" - assert fmt in cls.convertersByName, f"{cls} invalid Format: {fmt!r}" - - return fmt, remainder - - -class TableBuilder: - """ - Helps to populate things derived from BaseTable from maps, tuples, etc. - - A table of lifecycle callbacks may be provided to add logic beyond what is possible - based on otData info for the target class. See BuildCallbacks. - """ - - def __init__(self, callbackTable=None): - if callbackTable is None: - callbackTable = {} - self._callbackTable = callbackTable - - def _convert(self, dest, field, converter, value): - enumClass = getattr(converter, "enumClass", None) - - if enumClass: - if isinstance(value, enumClass): - pass - elif isinstance(value, str): - try: - value = getattr(enumClass, value.upper()) - except AttributeError: - raise ValueError(f"{value} is not a valid {enumClass}") - else: - value = enumClass(value) - - elif isinstance(converter, IntValue): - value = otRound(value) - elif isinstance(converter, FloatValue): - value = float(value) - - elif isinstance(converter, Struct): - if converter.repeat: - if _isNonStrSequence(value): - value = [self.build(converter.tableClass, v) for v in value] - else: - value = [self.build(converter.tableClass, value)] - setattr(dest, converter.repeat, len(value)) - else: - value = self.build(converter.tableClass, value) - elif callable(converter): - value = converter(value) - - setattr(dest, field, value) - - def build(self, cls, source): - assert issubclass(cls, BaseTable) - - if isinstance(source, cls): - return source - - callbackKey = (cls,) - fmt = None - if issubclass(cls, FormatSwitchingBaseTable): - fmt, source = _split_format(cls, source) - callbackKey = (cls, fmt) - - dest = self._callbackTable.get( - (BuildCallback.CREATE_DEFAULT,) + callbackKey, lambda: cls() - )() - assert isinstance(dest, cls) - - convByName = _assignable(cls.convertersByName) - skippedFields = set() - - # For format switchers we need to resolve converters based on format - if issubclass(cls, FormatSwitchingBaseTable): - dest.Format = fmt - convByName = _assignable(convByName[dest.Format]) - skippedFields.add("Format") - - # Convert sequence => mapping so before thunk only has to handle one format - if _isNonStrSequence(source): - # Sequence (typically list or tuple) assumed to match fields in declaration order - assert len(source) <= len( - convByName - ), f"Sequence of {len(source)} too long for {cls}; expected <= {len(convByName)} values" - source = dict(zip(convByName.keys(), source)) - - dest, source = self._callbackTable.get( - (BuildCallback.BEFORE_BUILD,) + callbackKey, lambda d, s: (d, s) - )(dest, source) - - if isinstance(source, collections.abc.Mapping): - for field, value in source.items(): - if field in skippedFields: - continue - converter = convByName.get(field, None) - if not converter: - raise ValueError( - f"Unrecognized field {field} for {cls}; expected one of {sorted(convByName.keys())}" - ) - self._convert(dest, field, converter, value) - else: - # let's try as a 1-tuple - dest = self.build(cls, (source,)) - - for field, conv in convByName.items(): - if not hasattr(dest, field) and isinstance(conv, OptionalValue): - setattr(dest, field, conv.DEFAULT) - - dest = self._callbackTable.get( - (BuildCallback.AFTER_BUILD,) + callbackKey, lambda d: d - )(dest) - - return dest - - -class TableUnbuilder: - def __init__(self, callbackTable=None): - if callbackTable is None: - callbackTable = {} - self._callbackTable = callbackTable - - def unbuild(self, table): - assert isinstance(table, BaseTable) - - source = {} - - callbackKey = (type(table),) - if isinstance(table, FormatSwitchingBaseTable): - source["Format"] = int(table.Format) - callbackKey += (table.Format,) - - for converter in table.getConverters(): - if isinstance(converter, ComputedInt): - continue - value = getattr(table, converter.name) - - enumClass = getattr(converter, "enumClass", None) - if enumClass: - source[converter.name] = value.name.lower() - elif isinstance(converter, Struct): - if converter.repeat: - source[converter.name] = [self.unbuild(v) for v in value] - else: - source[converter.name] = self.unbuild(value) - elif isinstance(converter, SimpleValue): - # "simple" values (e.g. int, float, str) need no further un-building - source[converter.name] = value - else: - raise NotImplementedError( - "Don't know how unbuild {value!r} with {converter!r}" - ) - - source = self._callbackTable.get(callbackKey, lambda s: s)(source) - - return source diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/unbuilder.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/unbuilder.py deleted file mode 100644 index ac24355..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/colorLib/unbuilder.py +++ /dev/null @@ -1,81 +0,0 @@ -from fontTools.ttLib.tables import otTables as ot -from .table_builder import TableUnbuilder - - -def unbuildColrV1(layerList, baseGlyphList): - layers = [] - if layerList: - layers = layerList.Paint - unbuilder = LayerListUnbuilder(layers) - return { - rec.BaseGlyph: unbuilder.unbuildPaint(rec.Paint) - for rec in baseGlyphList.BaseGlyphPaintRecord - } - - -def _flatten_layers(lst): - for paint in lst: - if paint["Format"] == ot.PaintFormat.PaintColrLayers: - yield from _flatten_layers(paint["Layers"]) - else: - yield paint - - -class LayerListUnbuilder: - def __init__(self, layers): - self.layers = layers - - callbacks = { - ( - ot.Paint, - ot.PaintFormat.PaintColrLayers, - ): self._unbuildPaintColrLayers, - } - self.tableUnbuilder = TableUnbuilder(callbacks) - - def unbuildPaint(self, paint): - assert isinstance(paint, ot.Paint) - return self.tableUnbuilder.unbuild(paint) - - def _unbuildPaintColrLayers(self, source): - assert source["Format"] == ot.PaintFormat.PaintColrLayers - - layers = list( - _flatten_layers( - [ - self.unbuildPaint(childPaint) - for childPaint in self.layers[ - source["FirstLayerIndex"] : source["FirstLayerIndex"] - + source["NumLayers"] - ] - ] - ) - ) - - if len(layers) == 1: - return layers[0] - - return {"Format": source["Format"], "Layers": layers} - - -if __name__ == "__main__": - from pprint import pprint - import sys - from fontTools.ttLib import TTFont - - try: - fontfile = sys.argv[1] - except IndexError: - sys.exit("usage: fonttools colorLib.unbuilder FONTFILE") - - font = TTFont(fontfile) - colr = font["COLR"] - if colr.version < 1: - sys.exit(f"error: No COLR table version=1 found in {fontfile}") - - colorGlyphs = unbuildColrV1( - colr.table.LayerList, - colr.table.BaseGlyphList, - ) - - pprint(colorGlyphs) diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/config/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/config/__init__.py deleted file mode 100644 index 41ab8f7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/config/__init__.py +++ /dev/null @@ -1,75 +0,0 @@ -""" -Define all configuration options that can affect the working of fontTools -modules. E.g. optimization levels of varLib IUP, otlLib GPOS compression level, -etc. If this file gets too big, split it into smaller files per-module. - -An instance of the Config class can be attached to a TTFont object, so that -the various modules can access their configuration options from it. -""" - -from textwrap import dedent - -from fontTools.misc.configTools import * - - -class Config(AbstractConfig): - options = Options() - - -OPTIONS = Config.options - - -Config.register_option( - name="fontTools.otlLib.optimize.gpos:COMPRESSION_LEVEL", - help=dedent( - """\ - GPOS Lookup type 2 (PairPos) compression level: - 0 = do not attempt to compact PairPos lookups; - 1 to 8 = create at most 1 to 8 new subtables for each existing - subtable, provided that it would yield a 50%% file size saving; - 9 = create as many new subtables as needed to yield a file size saving. - Default: 0. - - This compaction aims to save file size, by splitting large class - kerning subtables (Format 2) that contain many zero values into - smaller and denser subtables. It's a trade-off between the overhead - of several subtables versus the sparseness of one big subtable. - - See the pull request: https://github.com/fonttools/fonttools/pull/2326 - """ - ), - default=0, - parse=int, - validate=lambda v: v in range(10), -) - -Config.register_option( - name="fontTools.ttLib.tables.otBase:USE_HARFBUZZ_REPACKER", - help=dedent( - """\ - FontTools tries to use the HarfBuzz Repacker to serialize GPOS/GSUB tables - if the uharfbuzz python bindings are importable, otherwise falls back to its - slower, less efficient serializer. Set to False to always use the latter. - Set to True to explicitly request the HarfBuzz Repacker (will raise an - error if uharfbuzz cannot be imported). - """ - ), - default=None, - parse=Option.parse_optional_bool, - validate=Option.validate_optional_bool, -) - -Config.register_option( - name="fontTools.otlLib.builder:WRITE_GPOS7", - help=dedent( - """\ - macOS before 13.2 didn’t support GPOS LookupType 7 (non-chaining - ContextPos lookups), so FontTools.otlLib.builder disables a file size - optimisation that would use LookupType 7 instead of 8 when there is no - chaining (no prefix or suffix). Set to True to enable the optimization. - """ - ), - default=False, - parse=Option.parse_optional_bool, - validate=Option.validate_optional_bool, -) diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/config/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/config/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 237b6a7..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/config/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__init__.py deleted file mode 100644 index 4ae6356..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2016 Google Inc. 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. - -from .cu2qu import * diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__main__.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__main__.py deleted file mode 100644 index 5205ffe..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__main__.py +++ /dev/null @@ -1,6 +0,0 @@ -import sys -from .cli import _main as main - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 685ffa6..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/__main__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/__main__.cpython-38.pyc deleted file mode 100644 index 9f75e40..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/__main__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/benchmark.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/benchmark.cpython-38.pyc deleted file mode 100644 index 7db5111..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/benchmark.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/cli.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/cli.cpython-38.pyc deleted file mode 100644 index 47880d0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/cli.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/cu2qu.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/cu2qu.cpython-38.pyc deleted file mode 100644 index aa6b0f7..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/cu2qu.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/errors.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/errors.cpython-38.pyc deleted file mode 100644 index ef99fd6..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/errors.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/ufo.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/ufo.cpython-38.pyc deleted file mode 100644 index ba01645..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/__pycache__/ufo.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/benchmark.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/benchmark.py deleted file mode 100644 index 007f75d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/benchmark.py +++ /dev/null @@ -1,54 +0,0 @@ -"""Benchmark the cu2qu algorithm performance.""" - -from .cu2qu import * -import random -import timeit - -MAX_ERR = 0.05 - - -def generate_curve(): - return [ - tuple(float(random.randint(0, 2048)) for coord in range(2)) - for point in range(4) - ] - - -def setup_curve_to_quadratic(): - return generate_curve(), MAX_ERR - - -def setup_curves_to_quadratic(): - num_curves = 3 - return ([generate_curve() for curve in range(num_curves)], [MAX_ERR] * num_curves) - - -def run_benchmark(module, function, setup_suffix="", repeat=5, number=1000): - setup_func = "setup_" + function - if setup_suffix: - print("%s with %s:" % (function, setup_suffix), end="") - setup_func += "_" + setup_suffix - else: - print("%s:" % function, end="") - - def wrapper(function, setup_func): - function = globals()[function] - setup_func = globals()[setup_func] - - def wrapped(): - return function(*setup_func()) - - return wrapped - - results = timeit.repeat(wrapper(function, setup_func), repeat=repeat, number=number) - print("\t%5.1fus" % (min(results) * 1000000.0 / number)) - - -def main(): - run_benchmark("cu2qu", "curve_to_quadratic") - run_benchmark("cu2qu", "curves_to_quadratic") - - -if __name__ == "__main__": - random.seed(1) - main() diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/cli.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/cli.py deleted file mode 100644 index ddc6450..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/cli.py +++ /dev/null @@ -1,198 +0,0 @@ -import os -import argparse -import logging -import shutil -import multiprocessing as mp -from contextlib import closing -from functools import partial - -import fontTools -from .ufo import font_to_quadratic, fonts_to_quadratic - -ufo_module = None -try: - import ufoLib2 as ufo_module -except ImportError: - try: - import defcon as ufo_module - except ImportError as e: - pass - - -logger = logging.getLogger("fontTools.cu2qu") - - -def _cpu_count(): - try: - return mp.cpu_count() - except NotImplementedError: # pragma: no cover - return 1 - - -def open_ufo(path): - if hasattr(ufo_module.Font, "open"): # ufoLib2 - return ufo_module.Font.open(path) - return ufo_module.Font(path) # defcon - - -def _font_to_quadratic(input_path, output_path=None, **kwargs): - ufo = open_ufo(input_path) - logger.info("Converting curves for %s", input_path) - if font_to_quadratic(ufo, **kwargs): - logger.info("Saving %s", output_path) - if output_path: - ufo.save(output_path) - else: - ufo.save() # save in-place - elif output_path: - _copytree(input_path, output_path) - - -def _samepath(path1, path2): - # TODO on python3+, there's os.path.samefile - path1 = os.path.normcase(os.path.abspath(os.path.realpath(path1))) - path2 = os.path.normcase(os.path.abspath(os.path.realpath(path2))) - return path1 == path2 - - -def _copytree(input_path, output_path): - if _samepath(input_path, output_path): - logger.debug("input and output paths are the same file; skipped copy") - return - if os.path.exists(output_path): - shutil.rmtree(output_path) - shutil.copytree(input_path, output_path) - - -def _main(args=None): - """Convert a UFO font from cubic to quadratic curves""" - parser = argparse.ArgumentParser(prog="cu2qu") - parser.add_argument("--version", action="version", version=fontTools.__version__) - parser.add_argument( - "infiles", - nargs="+", - metavar="INPUT", - help="one or more input UFO source file(s).", - ) - parser.add_argument("-v", "--verbose", action="count", default=0) - parser.add_argument( - "-e", - "--conversion-error", - type=float, - metavar="ERROR", - default=None, - help="maxiumum approximation error measured in EM (default: 0.001)", - ) - parser.add_argument( - "-m", - "--mixed", - default=False, - action="store_true", - help="whether to used mixed quadratic and cubic curves", - ) - parser.add_argument( - "--keep-direction", - dest="reverse_direction", - action="store_false", - help="do not reverse the contour direction", - ) - - mode_parser = parser.add_mutually_exclusive_group() - mode_parser.add_argument( - "-i", - "--interpolatable", - action="store_true", - help="whether curve conversion should keep interpolation compatibility", - ) - mode_parser.add_argument( - "-j", - "--jobs", - type=int, - nargs="?", - default=1, - const=_cpu_count(), - metavar="N", - help="Convert using N multiple processes (default: %(default)s)", - ) - - output_parser = parser.add_mutually_exclusive_group() - output_parser.add_argument( - "-o", - "--output-file", - default=None, - metavar="OUTPUT", - help=( - "output filename for the converted UFO. By default fonts are " - "modified in place. This only works with a single input." - ), - ) - output_parser.add_argument( - "-d", - "--output-dir", - default=None, - metavar="DIRECTORY", - help="output directory where to save converted UFOs", - ) - - options = parser.parse_args(args) - - if ufo_module is None: - parser.error("Either ufoLib2 or defcon are required to run this script.") - - if not options.verbose: - level = "WARNING" - elif options.verbose == 1: - level = "INFO" - else: - level = "DEBUG" - logging.basicConfig(level=level) - - if len(options.infiles) > 1 and options.output_file: - parser.error("-o/--output-file can't be used with multile inputs") - - if options.output_dir: - output_dir = options.output_dir - if not os.path.exists(output_dir): - os.mkdir(output_dir) - elif not os.path.isdir(output_dir): - parser.error("'%s' is not a directory" % output_dir) - output_paths = [ - os.path.join(output_dir, os.path.basename(p)) for p in options.infiles - ] - elif options.output_file: - output_paths = [options.output_file] - else: - # save in-place - output_paths = [None] * len(options.infiles) - - kwargs = dict( - dump_stats=options.verbose > 0, - max_err_em=options.conversion_error, - reverse_direction=options.reverse_direction, - all_quadratic=False if options.mixed else True, - ) - - if options.interpolatable: - logger.info("Converting curves compatibly") - ufos = [open_ufo(infile) for infile in options.infiles] - if fonts_to_quadratic(ufos, **kwargs): - for ufo, output_path in zip(ufos, output_paths): - logger.info("Saving %s", output_path) - if output_path: - ufo.save(output_path) - else: - ufo.save() - else: - for input_path, output_path in zip(options.infiles, output_paths): - if output_path: - _copytree(input_path, output_path) - else: - jobs = min(len(options.infiles), options.jobs) if options.jobs > 1 else 1 - if jobs > 1: - func = partial(_font_to_quadratic, **kwargs) - logger.info("Running %d parallel processes", jobs) - with closing(mp.Pool(jobs)) as pool: - pool.starmap(func, zip(options.infiles, output_paths)) - else: - for input_path, output_path in zip(options.infiles, output_paths): - _font_to_quadratic(input_path, output_path, **kwargs) diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/cu2qu.c b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/cu2qu.c deleted file mode 100644 index d742bb1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/cu2qu.c +++ /dev/null @@ -1,14929 +0,0 @@ -/* Generated by Cython 3.0.11 */ - -/* BEGIN: Cython Metadata -{ - "distutils": { - "define_macros": [ - [ - "CYTHON_TRACE_NOGIL", - "1" - ] - ], - "name": "fontTools.cu2qu.cu2qu", - "sources": [ - "Lib/fontTools/cu2qu/cu2qu.py" - ] - }, - "module_name": "fontTools.cu2qu.cu2qu" -} -END: Cython Metadata */ - -#ifndef PY_SSIZE_T_CLEAN -#define PY_SSIZE_T_CLEAN -#endif /* PY_SSIZE_T_CLEAN */ -#if defined(CYTHON_LIMITED_API) && 0 - #ifndef Py_LIMITED_API - #if CYTHON_LIMITED_API+0 > 0x03030000 - #define Py_LIMITED_API CYTHON_LIMITED_API - #else - #define Py_LIMITED_API 0x03030000 - #endif - #endif -#endif - -#include "Python.h" -#ifndef Py_PYTHON_H - #error Python headers needed to compile C extensions, please install development version of Python. -#elif PY_VERSION_HEX < 0x02070000 || (0x03000000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x03030000) - #error Cython requires Python 2.7+ or Python 3.3+. -#else -#if defined(CYTHON_LIMITED_API) && CYTHON_LIMITED_API -#define __PYX_EXTRA_ABI_MODULE_NAME "limited" -#else -#define __PYX_EXTRA_ABI_MODULE_NAME "" -#endif -#define CYTHON_ABI "3_0_11" __PYX_EXTRA_ABI_MODULE_NAME -#define __PYX_ABI_MODULE_NAME "_cython_" CYTHON_ABI -#define __PYX_TYPE_MODULE_PREFIX __PYX_ABI_MODULE_NAME "." -#define CYTHON_HEX_VERSION 0x03000BF0 -#define CYTHON_FUTURE_DIVISION 1 -#include -#ifndef offsetof - #define offsetof(type, member) ( (size_t) & ((type*)0) -> member ) -#endif -#if !defined(_WIN32) && !defined(WIN32) && !defined(MS_WINDOWS) - #ifndef __stdcall - #define __stdcall - #endif - #ifndef __cdecl - #define __cdecl - #endif - #ifndef __fastcall - #define __fastcall - #endif -#endif -#ifndef DL_IMPORT - #define DL_IMPORT(t) t -#endif -#ifndef DL_EXPORT - #define DL_EXPORT(t) t -#endif -#define __PYX_COMMA , -#ifndef HAVE_LONG_LONG - #define HAVE_LONG_LONG -#endif -#ifndef PY_LONG_LONG - #define PY_LONG_LONG LONG_LONG -#endif -#ifndef Py_HUGE_VAL - #define Py_HUGE_VAL HUGE_VAL -#endif -#define __PYX_LIMITED_VERSION_HEX PY_VERSION_HEX -#if defined(GRAALVM_PYTHON) - /* For very preliminary testing purposes. Most variables are set the same as PyPy. - The existence of this section does not imply that anything works or is even tested */ - #define CYTHON_COMPILING_IN_PYPY 0 - #define CYTHON_COMPILING_IN_CPYTHON 0 - #define CYTHON_COMPILING_IN_LIMITED_API 0 - #define CYTHON_COMPILING_IN_GRAAL 1 - #define CYTHON_COMPILING_IN_NOGIL 0 - #undef CYTHON_USE_TYPE_SLOTS - #define CYTHON_USE_TYPE_SLOTS 0 - #undef CYTHON_USE_TYPE_SPECS - #define CYTHON_USE_TYPE_SPECS 0 - #undef CYTHON_USE_PYTYPE_LOOKUP - #define CYTHON_USE_PYTYPE_LOOKUP 0 - #if PY_VERSION_HEX < 0x03050000 - #undef CYTHON_USE_ASYNC_SLOTS - #define CYTHON_USE_ASYNC_SLOTS 0 - #elif !defined(CYTHON_USE_ASYNC_SLOTS) - #define CYTHON_USE_ASYNC_SLOTS 1 - #endif - #undef CYTHON_USE_PYLIST_INTERNALS - #define CYTHON_USE_PYLIST_INTERNALS 0 - #undef CYTHON_USE_UNICODE_INTERNALS - #define CYTHON_USE_UNICODE_INTERNALS 0 - #undef CYTHON_USE_UNICODE_WRITER - #define CYTHON_USE_UNICODE_WRITER 0 - #undef CYTHON_USE_PYLONG_INTERNALS - #define CYTHON_USE_PYLONG_INTERNALS 0 - #undef CYTHON_AVOID_BORROWED_REFS - #define CYTHON_AVOID_BORROWED_REFS 1 - #undef CYTHON_ASSUME_SAFE_MACROS - #define CYTHON_ASSUME_SAFE_MACROS 0 - #undef CYTHON_UNPACK_METHODS - #define CYTHON_UNPACK_METHODS 0 - #undef CYTHON_FAST_THREAD_STATE - #define CYTHON_FAST_THREAD_STATE 0 - #undef CYTHON_FAST_GIL - #define CYTHON_FAST_GIL 0 - #undef CYTHON_METH_FASTCALL - #define CYTHON_METH_FASTCALL 0 - #undef CYTHON_FAST_PYCALL - #define CYTHON_FAST_PYCALL 0 - #ifndef CYTHON_PEP487_INIT_SUBCLASS - #define CYTHON_PEP487_INIT_SUBCLASS (PY_MAJOR_VERSION >= 3) - #endif - #undef CYTHON_PEP489_MULTI_PHASE_INIT - #define CYTHON_PEP489_MULTI_PHASE_INIT 1 - #undef CYTHON_USE_MODULE_STATE - #define CYTHON_USE_MODULE_STATE 0 - #undef CYTHON_USE_TP_FINALIZE - #define CYTHON_USE_TP_FINALIZE 0 - #undef CYTHON_USE_DICT_VERSIONS - #define CYTHON_USE_DICT_VERSIONS 0 - #undef CYTHON_USE_EXC_INFO_STACK - #define CYTHON_USE_EXC_INFO_STACK 0 - #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC - #define CYTHON_UPDATE_DESCRIPTOR_DOC 0 - #endif - #undef CYTHON_USE_FREELISTS - #define CYTHON_USE_FREELISTS 0 -#elif defined(PYPY_VERSION) - #define CYTHON_COMPILING_IN_PYPY 1 - #define CYTHON_COMPILING_IN_CPYTHON 0 - #define CYTHON_COMPILING_IN_LIMITED_API 0 - #define CYTHON_COMPILING_IN_GRAAL 0 - #define CYTHON_COMPILING_IN_NOGIL 0 - #undef CYTHON_USE_TYPE_SLOTS - #define CYTHON_USE_TYPE_SLOTS 0 - #ifndef CYTHON_USE_TYPE_SPECS - #define CYTHON_USE_TYPE_SPECS 0 - #endif - #undef CYTHON_USE_PYTYPE_LOOKUP - #define CYTHON_USE_PYTYPE_LOOKUP 0 - #if PY_VERSION_HEX < 0x03050000 - #undef CYTHON_USE_ASYNC_SLOTS - #define CYTHON_USE_ASYNC_SLOTS 0 - #elif !defined(CYTHON_USE_ASYNC_SLOTS) - #define CYTHON_USE_ASYNC_SLOTS 1 - #endif - #undef CYTHON_USE_PYLIST_INTERNALS - #define CYTHON_USE_PYLIST_INTERNALS 0 - #undef CYTHON_USE_UNICODE_INTERNALS - #define CYTHON_USE_UNICODE_INTERNALS 0 - #undef CYTHON_USE_UNICODE_WRITER - #define CYTHON_USE_UNICODE_WRITER 0 - #undef CYTHON_USE_PYLONG_INTERNALS - #define CYTHON_USE_PYLONG_INTERNALS 0 - #undef CYTHON_AVOID_BORROWED_REFS - #define CYTHON_AVOID_BORROWED_REFS 1 - #undef CYTHON_ASSUME_SAFE_MACROS - #define CYTHON_ASSUME_SAFE_MACROS 0 - #undef CYTHON_UNPACK_METHODS - #define CYTHON_UNPACK_METHODS 0 - #undef CYTHON_FAST_THREAD_STATE - #define CYTHON_FAST_THREAD_STATE 0 - #undef CYTHON_FAST_GIL - #define CYTHON_FAST_GIL 0 - #undef CYTHON_METH_FASTCALL - #define CYTHON_METH_FASTCALL 0 - #undef CYTHON_FAST_PYCALL - #define CYTHON_FAST_PYCALL 0 - #ifndef CYTHON_PEP487_INIT_SUBCLASS - #define CYTHON_PEP487_INIT_SUBCLASS (PY_MAJOR_VERSION >= 3) - #endif - #if PY_VERSION_HEX < 0x03090000 - #undef CYTHON_PEP489_MULTI_PHASE_INIT - #define CYTHON_PEP489_MULTI_PHASE_INIT 0 - #elif !defined(CYTHON_PEP489_MULTI_PHASE_INIT) - #define CYTHON_PEP489_MULTI_PHASE_INIT 1 - #endif - #undef CYTHON_USE_MODULE_STATE - #define CYTHON_USE_MODULE_STATE 0 - #undef CYTHON_USE_TP_FINALIZE - #define CYTHON_USE_TP_FINALIZE (PY_VERSION_HEX >= 0x030400a1 && PYPY_VERSION_NUM >= 0x07030C00) - #undef CYTHON_USE_DICT_VERSIONS - #define CYTHON_USE_DICT_VERSIONS 0 - #undef CYTHON_USE_EXC_INFO_STACK - #define CYTHON_USE_EXC_INFO_STACK 0 - #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC - #define CYTHON_UPDATE_DESCRIPTOR_DOC 0 - #endif - #undef CYTHON_USE_FREELISTS - #define CYTHON_USE_FREELISTS 0 -#elif defined(CYTHON_LIMITED_API) - #ifdef Py_LIMITED_API - #undef __PYX_LIMITED_VERSION_HEX - #define __PYX_LIMITED_VERSION_HEX Py_LIMITED_API - #endif - #define CYTHON_COMPILING_IN_PYPY 0 - #define CYTHON_COMPILING_IN_CPYTHON 0 - #define CYTHON_COMPILING_IN_LIMITED_API 1 - #define CYTHON_COMPILING_IN_GRAAL 0 - #define CYTHON_COMPILING_IN_NOGIL 0 - #undef CYTHON_CLINE_IN_TRACEBACK - #define CYTHON_CLINE_IN_TRACEBACK 0 - #undef CYTHON_USE_TYPE_SLOTS - #define CYTHON_USE_TYPE_SLOTS 0 - #undef CYTHON_USE_TYPE_SPECS - #define CYTHON_USE_TYPE_SPECS 1 - #undef CYTHON_USE_PYTYPE_LOOKUP - #define CYTHON_USE_PYTYPE_LOOKUP 0 - #undef CYTHON_USE_ASYNC_SLOTS - #define CYTHON_USE_ASYNC_SLOTS 0 - #undef CYTHON_USE_PYLIST_INTERNALS - #define CYTHON_USE_PYLIST_INTERNALS 0 - #undef CYTHON_USE_UNICODE_INTERNALS - #define CYTHON_USE_UNICODE_INTERNALS 0 - #ifndef CYTHON_USE_UNICODE_WRITER - #define CYTHON_USE_UNICODE_WRITER 0 - #endif - #undef CYTHON_USE_PYLONG_INTERNALS - #define CYTHON_USE_PYLONG_INTERNALS 0 - #ifndef CYTHON_AVOID_BORROWED_REFS - #define CYTHON_AVOID_BORROWED_REFS 0 - #endif - #undef CYTHON_ASSUME_SAFE_MACROS - #define CYTHON_ASSUME_SAFE_MACROS 0 - #undef CYTHON_UNPACK_METHODS - #define CYTHON_UNPACK_METHODS 0 - #undef CYTHON_FAST_THREAD_STATE - #define CYTHON_FAST_THREAD_STATE 0 - #undef CYTHON_FAST_GIL - #define CYTHON_FAST_GIL 0 - #undef CYTHON_METH_FASTCALL - #define CYTHON_METH_FASTCALL 0 - #undef CYTHON_FAST_PYCALL - #define CYTHON_FAST_PYCALL 0 - #ifndef CYTHON_PEP487_INIT_SUBCLASS - #define CYTHON_PEP487_INIT_SUBCLASS 1 - #endif - #undef CYTHON_PEP489_MULTI_PHASE_INIT - #define CYTHON_PEP489_MULTI_PHASE_INIT 0 - #undef CYTHON_USE_MODULE_STATE - #define CYTHON_USE_MODULE_STATE 1 - #ifndef CYTHON_USE_TP_FINALIZE - #define CYTHON_USE_TP_FINALIZE 0 - #endif - #undef CYTHON_USE_DICT_VERSIONS - #define CYTHON_USE_DICT_VERSIONS 0 - #undef CYTHON_USE_EXC_INFO_STACK - #define CYTHON_USE_EXC_INFO_STACK 0 - #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC - #define CYTHON_UPDATE_DESCRIPTOR_DOC 0 - #endif - #undef CYTHON_USE_FREELISTS - #define CYTHON_USE_FREELISTS 0 -#elif defined(Py_GIL_DISABLED) || defined(Py_NOGIL) - #define CYTHON_COMPILING_IN_PYPY 0 - #define CYTHON_COMPILING_IN_CPYTHON 0 - #define CYTHON_COMPILING_IN_LIMITED_API 0 - #define CYTHON_COMPILING_IN_GRAAL 0 - #define CYTHON_COMPILING_IN_NOGIL 1 - #ifndef CYTHON_USE_TYPE_SLOTS - #define CYTHON_USE_TYPE_SLOTS 1 - #endif - #ifndef CYTHON_USE_TYPE_SPECS - #define CYTHON_USE_TYPE_SPECS 0 - #endif - #undef CYTHON_USE_PYTYPE_LOOKUP - #define CYTHON_USE_PYTYPE_LOOKUP 0 - #ifndef CYTHON_USE_ASYNC_SLOTS - #define CYTHON_USE_ASYNC_SLOTS 1 - #endif - #ifndef CYTHON_USE_PYLONG_INTERNALS - #define CYTHON_USE_PYLONG_INTERNALS 0 - #endif - #undef CYTHON_USE_PYLIST_INTERNALS - #define CYTHON_USE_PYLIST_INTERNALS 0 - #ifndef CYTHON_USE_UNICODE_INTERNALS - #define CYTHON_USE_UNICODE_INTERNALS 1 - #endif - #undef CYTHON_USE_UNICODE_WRITER - #define CYTHON_USE_UNICODE_WRITER 0 - #ifndef CYTHON_AVOID_BORROWED_REFS - #define CYTHON_AVOID_BORROWED_REFS 0 - #endif - #ifndef CYTHON_ASSUME_SAFE_MACROS - #define CYTHON_ASSUME_SAFE_MACROS 1 - #endif - #ifndef CYTHON_UNPACK_METHODS - #define CYTHON_UNPACK_METHODS 1 - #endif - #undef CYTHON_FAST_THREAD_STATE - #define CYTHON_FAST_THREAD_STATE 0 - #undef CYTHON_FAST_GIL - #define CYTHON_FAST_GIL 0 - #ifndef CYTHON_METH_FASTCALL - #define CYTHON_METH_FASTCALL 1 - #endif - #undef CYTHON_FAST_PYCALL - #define CYTHON_FAST_PYCALL 0 - #ifndef CYTHON_PEP487_INIT_SUBCLASS - #define CYTHON_PEP487_INIT_SUBCLASS 1 - #endif - #ifndef CYTHON_PEP489_MULTI_PHASE_INIT - #define CYTHON_PEP489_MULTI_PHASE_INIT 1 - #endif - #ifndef CYTHON_USE_MODULE_STATE - #define CYTHON_USE_MODULE_STATE 0 - #endif - #ifndef CYTHON_USE_TP_FINALIZE - #define CYTHON_USE_TP_FINALIZE 1 - #endif - #undef CYTHON_USE_DICT_VERSIONS - #define CYTHON_USE_DICT_VERSIONS 0 - #undef CYTHON_USE_EXC_INFO_STACK - #define CYTHON_USE_EXC_INFO_STACK 0 - #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC - #define CYTHON_UPDATE_DESCRIPTOR_DOC 1 - #endif - #ifndef CYTHON_USE_FREELISTS - #define CYTHON_USE_FREELISTS 0 - #endif -#else - #define CYTHON_COMPILING_IN_PYPY 0 - #define CYTHON_COMPILING_IN_CPYTHON 1 - #define CYTHON_COMPILING_IN_LIMITED_API 0 - #define CYTHON_COMPILING_IN_GRAAL 0 - #define CYTHON_COMPILING_IN_NOGIL 0 - #ifndef CYTHON_USE_TYPE_SLOTS - #define CYTHON_USE_TYPE_SLOTS 1 - #endif - #ifndef CYTHON_USE_TYPE_SPECS - #define CYTHON_USE_TYPE_SPECS 0 - #endif - #ifndef CYTHON_USE_PYTYPE_LOOKUP - #define CYTHON_USE_PYTYPE_LOOKUP 1 - #endif - #if PY_MAJOR_VERSION < 3 - #undef CYTHON_USE_ASYNC_SLOTS - #define CYTHON_USE_ASYNC_SLOTS 0 - #elif !defined(CYTHON_USE_ASYNC_SLOTS) - #define CYTHON_USE_ASYNC_SLOTS 1 - #endif - #ifndef CYTHON_USE_PYLONG_INTERNALS - #define CYTHON_USE_PYLONG_INTERNALS 1 - #endif - #ifndef CYTHON_USE_PYLIST_INTERNALS - #define CYTHON_USE_PYLIST_INTERNALS 1 - #endif - #ifndef CYTHON_USE_UNICODE_INTERNALS - #define CYTHON_USE_UNICODE_INTERNALS 1 - #endif - #if PY_VERSION_HEX < 0x030300F0 || PY_VERSION_HEX >= 0x030B00A2 - #undef CYTHON_USE_UNICODE_WRITER - #define CYTHON_USE_UNICODE_WRITER 0 - #elif !defined(CYTHON_USE_UNICODE_WRITER) - #define CYTHON_USE_UNICODE_WRITER 1 - #endif - #ifndef CYTHON_AVOID_BORROWED_REFS - #define CYTHON_AVOID_BORROWED_REFS 0 - #endif - #ifndef CYTHON_ASSUME_SAFE_MACROS - #define CYTHON_ASSUME_SAFE_MACROS 1 - #endif - #ifndef CYTHON_UNPACK_METHODS - #define CYTHON_UNPACK_METHODS 1 - #endif - #ifndef CYTHON_FAST_THREAD_STATE - #define CYTHON_FAST_THREAD_STATE 1 - #endif - #ifndef CYTHON_FAST_GIL - #define CYTHON_FAST_GIL (PY_MAJOR_VERSION < 3 || PY_VERSION_HEX >= 0x03060000 && PY_VERSION_HEX < 0x030C00A6) - #endif - #ifndef CYTHON_METH_FASTCALL - #define CYTHON_METH_FASTCALL (PY_VERSION_HEX >= 0x030700A1) - #endif - #ifndef CYTHON_FAST_PYCALL - #define CYTHON_FAST_PYCALL 1 - #endif - #ifndef CYTHON_PEP487_INIT_SUBCLASS - #define CYTHON_PEP487_INIT_SUBCLASS 1 - #endif - #if PY_VERSION_HEX < 0x03050000 - #undef CYTHON_PEP489_MULTI_PHASE_INIT - #define CYTHON_PEP489_MULTI_PHASE_INIT 0 - #elif !defined(CYTHON_PEP489_MULTI_PHASE_INIT) - #define CYTHON_PEP489_MULTI_PHASE_INIT 1 - #endif - #ifndef CYTHON_USE_MODULE_STATE - #define CYTHON_USE_MODULE_STATE 0 - #endif - #if PY_VERSION_HEX < 0x030400a1 - #undef CYTHON_USE_TP_FINALIZE - #define CYTHON_USE_TP_FINALIZE 0 - #elif !defined(CYTHON_USE_TP_FINALIZE) - #define CYTHON_USE_TP_FINALIZE 1 - #endif - #if PY_VERSION_HEX < 0x030600B1 - #undef CYTHON_USE_DICT_VERSIONS - #define CYTHON_USE_DICT_VERSIONS 0 - #elif !defined(CYTHON_USE_DICT_VERSIONS) - #define CYTHON_USE_DICT_VERSIONS (PY_VERSION_HEX < 0x030C00A5) - #endif - #if PY_VERSION_HEX < 0x030700A3 - #undef CYTHON_USE_EXC_INFO_STACK - #define CYTHON_USE_EXC_INFO_STACK 0 - #elif !defined(CYTHON_USE_EXC_INFO_STACK) - #define CYTHON_USE_EXC_INFO_STACK 1 - #endif - #ifndef CYTHON_UPDATE_DESCRIPTOR_DOC - #define CYTHON_UPDATE_DESCRIPTOR_DOC 1 - #endif - #ifndef CYTHON_USE_FREELISTS - #define CYTHON_USE_FREELISTS 1 - #endif -#endif -#if !defined(CYTHON_FAST_PYCCALL) -#define CYTHON_FAST_PYCCALL (CYTHON_FAST_PYCALL && PY_VERSION_HEX >= 0x030600B1) -#endif -#if !defined(CYTHON_VECTORCALL) -#define CYTHON_VECTORCALL (CYTHON_FAST_PYCCALL && PY_VERSION_HEX >= 0x030800B1) -#endif -#define CYTHON_BACKPORT_VECTORCALL (CYTHON_METH_FASTCALL && PY_VERSION_HEX < 0x030800B1) -#if CYTHON_USE_PYLONG_INTERNALS - #if PY_MAJOR_VERSION < 3 - #include "longintrepr.h" - #endif - #undef SHIFT - #undef BASE - #undef MASK - #ifdef SIZEOF_VOID_P - enum { __pyx_check_sizeof_voidp = 1 / (int)(SIZEOF_VOID_P == sizeof(void*)) }; - #endif -#endif -#ifndef __has_attribute - #define __has_attribute(x) 0 -#endif -#ifndef __has_cpp_attribute - #define __has_cpp_attribute(x) 0 -#endif -#ifndef CYTHON_RESTRICT - #if defined(__GNUC__) - #define CYTHON_RESTRICT __restrict__ - #elif defined(_MSC_VER) && _MSC_VER >= 1400 - #define CYTHON_RESTRICT __restrict - #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L - #define CYTHON_RESTRICT restrict - #else - #define CYTHON_RESTRICT - #endif -#endif -#ifndef CYTHON_UNUSED - #if defined(__cplusplus) - /* for clang __has_cpp_attribute(maybe_unused) is true even before C++17 - * but leads to warnings with -pedantic, since it is a C++17 feature */ - #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L) - #if __has_cpp_attribute(maybe_unused) - #define CYTHON_UNUSED [[maybe_unused]] - #endif - #endif - #endif -#endif -#ifndef CYTHON_UNUSED -# if defined(__GNUC__) -# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) -# define CYTHON_UNUSED __attribute__ ((__unused__)) -# else -# define CYTHON_UNUSED -# endif -# elif defined(__ICC) || (defined(__INTEL_COMPILER) && !defined(_MSC_VER)) -# define CYTHON_UNUSED __attribute__ ((__unused__)) -# else -# define CYTHON_UNUSED -# endif -#endif -#ifndef CYTHON_UNUSED_VAR -# if defined(__cplusplus) - template void CYTHON_UNUSED_VAR( const T& ) { } -# else -# define CYTHON_UNUSED_VAR(x) (void)(x) -# endif -#endif -#ifndef CYTHON_MAYBE_UNUSED_VAR - #define CYTHON_MAYBE_UNUSED_VAR(x) CYTHON_UNUSED_VAR(x) -#endif -#ifndef CYTHON_NCP_UNUSED -# if CYTHON_COMPILING_IN_CPYTHON -# define CYTHON_NCP_UNUSED -# else -# define CYTHON_NCP_UNUSED CYTHON_UNUSED -# endif -#endif -#ifndef CYTHON_USE_CPP_STD_MOVE - #if defined(__cplusplus) && (\ - __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1600)) - #define CYTHON_USE_CPP_STD_MOVE 1 - #else - #define CYTHON_USE_CPP_STD_MOVE 0 - #endif -#endif -#define __Pyx_void_to_None(void_result) ((void)(void_result), Py_INCREF(Py_None), Py_None) -#ifdef _MSC_VER - #ifndef _MSC_STDINT_H_ - #if _MSC_VER < 1300 - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; - typedef unsigned int uint32_t; - #else - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; - #endif - #endif - #if _MSC_VER < 1300 - #ifdef _WIN64 - typedef unsigned long long __pyx_uintptr_t; - #else - typedef unsigned int __pyx_uintptr_t; - #endif - #else - #ifdef _WIN64 - typedef unsigned __int64 __pyx_uintptr_t; - #else - typedef unsigned __int32 __pyx_uintptr_t; - #endif - #endif -#else - #include - typedef uintptr_t __pyx_uintptr_t; -#endif -#ifndef CYTHON_FALLTHROUGH - #if defined(__cplusplus) - /* for clang __has_cpp_attribute(fallthrough) is true even before C++17 - * but leads to warnings with -pedantic, since it is a C++17 feature */ - #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L) - #if __has_cpp_attribute(fallthrough) - #define CYTHON_FALLTHROUGH [[fallthrough]] - #endif - #endif - #ifndef CYTHON_FALLTHROUGH - #if __has_cpp_attribute(clang::fallthrough) - #define CYTHON_FALLTHROUGH [[clang::fallthrough]] - #elif __has_cpp_attribute(gnu::fallthrough) - #define CYTHON_FALLTHROUGH [[gnu::fallthrough]] - #endif - #endif - #endif - #ifndef CYTHON_FALLTHROUGH - #if __has_attribute(fallthrough) - #define CYTHON_FALLTHROUGH __attribute__((fallthrough)) - #else - #define CYTHON_FALLTHROUGH - #endif - #endif - #if defined(__clang__) && defined(__apple_build_version__) - #if __apple_build_version__ < 7000000 - #undef CYTHON_FALLTHROUGH - #define CYTHON_FALLTHROUGH - #endif - #endif -#endif -#ifdef __cplusplus - template - struct __PYX_IS_UNSIGNED_IMPL {static const bool value = T(0) < T(-1);}; - #define __PYX_IS_UNSIGNED(type) (__PYX_IS_UNSIGNED_IMPL::value) -#else - #define __PYX_IS_UNSIGNED(type) (((type)-1) > 0) -#endif -#if CYTHON_COMPILING_IN_PYPY == 1 - #define __PYX_NEED_TP_PRINT_SLOT (PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x030A0000) -#else - #define __PYX_NEED_TP_PRINT_SLOT (PY_VERSION_HEX >= 0x030800b4 && PY_VERSION_HEX < 0x03090000) -#endif -#define __PYX_REINTERPRET_FUNCION(func_pointer, other_pointer) ((func_pointer)(void(*)(void))(other_pointer)) - -#ifndef CYTHON_INLINE - #if defined(__clang__) - #define CYTHON_INLINE __inline__ __attribute__ ((__unused__)) - #elif defined(__GNUC__) - #define CYTHON_INLINE __inline__ - #elif defined(_MSC_VER) - #define CYTHON_INLINE __inline - #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L - #define CYTHON_INLINE inline - #else - #define CYTHON_INLINE - #endif -#endif - -#define __PYX_BUILD_PY_SSIZE_T "n" -#define CYTHON_FORMAT_SSIZE_T "z" -#if PY_MAJOR_VERSION < 3 - #define __Pyx_BUILTIN_MODULE_NAME "__builtin__" - #define __Pyx_DefaultClassType PyClass_Type - #define __Pyx_PyCode_New(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)\ - PyCode_New(a+k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) -#else - #define __Pyx_BUILTIN_MODULE_NAME "builtins" - #define __Pyx_DefaultClassType PyType_Type -#if CYTHON_COMPILING_IN_LIMITED_API - static CYTHON_INLINE PyObject* __Pyx_PyCode_New(int a, int p, int k, int l, int s, int f, - PyObject *code, PyObject *c, PyObject* n, PyObject *v, - PyObject *fv, PyObject *cell, PyObject* fn, - PyObject *name, int fline, PyObject *lnos) { - PyObject *exception_table = NULL; - PyObject *types_module=NULL, *code_type=NULL, *result=NULL; - #if __PYX_LIMITED_VERSION_HEX < 0x030B0000 - PyObject *version_info; - PyObject *py_minor_version = NULL; - #endif - long minor_version = 0; - PyObject *type, *value, *traceback; - PyErr_Fetch(&type, &value, &traceback); - #if __PYX_LIMITED_VERSION_HEX >= 0x030B0000 - minor_version = 11; - #else - if (!(version_info = PySys_GetObject("version_info"))) goto end; - if (!(py_minor_version = PySequence_GetItem(version_info, 1))) goto end; - minor_version = PyLong_AsLong(py_minor_version); - Py_DECREF(py_minor_version); - if (minor_version == -1 && PyErr_Occurred()) goto end; - #endif - if (!(types_module = PyImport_ImportModule("types"))) goto end; - if (!(code_type = PyObject_GetAttrString(types_module, "CodeType"))) goto end; - if (minor_version <= 7) { - (void)p; - result = PyObject_CallFunction(code_type, "iiiiiOOOOOOiOO", a, k, l, s, f, code, - c, n, v, fn, name, fline, lnos, fv, cell); - } else if (minor_version <= 10) { - result = PyObject_CallFunction(code_type, "iiiiiiOOOOOOiOO", a,p, k, l, s, f, code, - c, n, v, fn, name, fline, lnos, fv, cell); - } else { - if (!(exception_table = PyBytes_FromStringAndSize(NULL, 0))) goto end; - result = PyObject_CallFunction(code_type, "iiiiiiOOOOOOOiOO", a,p, k, l, s, f, code, - c, n, v, fn, name, name, fline, lnos, exception_table, fv, cell); - } - end: - Py_XDECREF(code_type); - Py_XDECREF(exception_table); - Py_XDECREF(types_module); - if (type) { - PyErr_Restore(type, value, traceback); - } - return result; - } - #ifndef CO_OPTIMIZED - #define CO_OPTIMIZED 0x0001 - #endif - #ifndef CO_NEWLOCALS - #define CO_NEWLOCALS 0x0002 - #endif - #ifndef CO_VARARGS - #define CO_VARARGS 0x0004 - #endif - #ifndef CO_VARKEYWORDS - #define CO_VARKEYWORDS 0x0008 - #endif - #ifndef CO_ASYNC_GENERATOR - #define CO_ASYNC_GENERATOR 0x0200 - #endif - #ifndef CO_GENERATOR - #define CO_GENERATOR 0x0020 - #endif - #ifndef CO_COROUTINE - #define CO_COROUTINE 0x0080 - #endif -#elif PY_VERSION_HEX >= 0x030B0000 - static CYTHON_INLINE PyCodeObject* __Pyx_PyCode_New(int a, int p, int k, int l, int s, int f, - PyObject *code, PyObject *c, PyObject* n, PyObject *v, - PyObject *fv, PyObject *cell, PyObject* fn, - PyObject *name, int fline, PyObject *lnos) { - PyCodeObject *result; - PyObject *empty_bytes = PyBytes_FromStringAndSize("", 0); - if (!empty_bytes) return NULL; - result = - #if PY_VERSION_HEX >= 0x030C0000 - PyUnstable_Code_NewWithPosOnlyArgs - #else - PyCode_NewWithPosOnlyArgs - #endif - (a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, name, fline, lnos, empty_bytes); - Py_DECREF(empty_bytes); - return result; - } -#elif PY_VERSION_HEX >= 0x030800B2 && !CYTHON_COMPILING_IN_PYPY - #define __Pyx_PyCode_New(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)\ - PyCode_NewWithPosOnlyArgs(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) -#else - #define __Pyx_PyCode_New(a, p, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)\ - PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos) -#endif -#endif -#if PY_VERSION_HEX >= 0x030900A4 || defined(Py_IS_TYPE) - #define __Pyx_IS_TYPE(ob, type) Py_IS_TYPE(ob, type) -#else - #define __Pyx_IS_TYPE(ob, type) (((const PyObject*)ob)->ob_type == (type)) -#endif -#if PY_VERSION_HEX >= 0x030A00B1 || defined(Py_Is) - #define __Pyx_Py_Is(x, y) Py_Is(x, y) -#else - #define __Pyx_Py_Is(x, y) ((x) == (y)) -#endif -#if PY_VERSION_HEX >= 0x030A00B1 || defined(Py_IsNone) - #define __Pyx_Py_IsNone(ob) Py_IsNone(ob) -#else - #define __Pyx_Py_IsNone(ob) __Pyx_Py_Is((ob), Py_None) -#endif -#if PY_VERSION_HEX >= 0x030A00B1 || defined(Py_IsTrue) - #define __Pyx_Py_IsTrue(ob) Py_IsTrue(ob) -#else - #define __Pyx_Py_IsTrue(ob) __Pyx_Py_Is((ob), Py_True) -#endif -#if PY_VERSION_HEX >= 0x030A00B1 || defined(Py_IsFalse) - #define __Pyx_Py_IsFalse(ob) Py_IsFalse(ob) -#else - #define __Pyx_Py_IsFalse(ob) __Pyx_Py_Is((ob), Py_False) -#endif -#define __Pyx_NoneAsNull(obj) (__Pyx_Py_IsNone(obj) ? NULL : (obj)) -#if PY_VERSION_HEX >= 0x030900F0 && !CYTHON_COMPILING_IN_PYPY - #define __Pyx_PyObject_GC_IsFinalized(o) PyObject_GC_IsFinalized(o) -#else - #define __Pyx_PyObject_GC_IsFinalized(o) _PyGC_FINALIZED(o) -#endif -#ifndef CO_COROUTINE - #define CO_COROUTINE 0x80 -#endif -#ifndef CO_ASYNC_GENERATOR - #define CO_ASYNC_GENERATOR 0x200 -#endif -#ifndef Py_TPFLAGS_CHECKTYPES - #define Py_TPFLAGS_CHECKTYPES 0 -#endif -#ifndef Py_TPFLAGS_HAVE_INDEX - #define Py_TPFLAGS_HAVE_INDEX 0 -#endif -#ifndef Py_TPFLAGS_HAVE_NEWBUFFER - #define Py_TPFLAGS_HAVE_NEWBUFFER 0 -#endif -#ifndef Py_TPFLAGS_HAVE_FINALIZE - #define Py_TPFLAGS_HAVE_FINALIZE 0 -#endif -#ifndef Py_TPFLAGS_SEQUENCE - #define Py_TPFLAGS_SEQUENCE 0 -#endif -#ifndef Py_TPFLAGS_MAPPING - #define Py_TPFLAGS_MAPPING 0 -#endif -#ifndef METH_STACKLESS - #define METH_STACKLESS 0 -#endif -#if PY_VERSION_HEX <= 0x030700A3 || !defined(METH_FASTCALL) - #ifndef METH_FASTCALL - #define METH_FASTCALL 0x80 - #endif - typedef PyObject *(*__Pyx_PyCFunctionFast) (PyObject *self, PyObject *const *args, Py_ssize_t nargs); - typedef PyObject *(*__Pyx_PyCFunctionFastWithKeywords) (PyObject *self, PyObject *const *args, - Py_ssize_t nargs, PyObject *kwnames); -#else - #if PY_VERSION_HEX >= 0x030d00A4 - # define __Pyx_PyCFunctionFast PyCFunctionFast - # define __Pyx_PyCFunctionFastWithKeywords PyCFunctionFastWithKeywords - #else - # define __Pyx_PyCFunctionFast _PyCFunctionFast - # define __Pyx_PyCFunctionFastWithKeywords _PyCFunctionFastWithKeywords - #endif -#endif -#if CYTHON_METH_FASTCALL - #define __Pyx_METH_FASTCALL METH_FASTCALL - #define __Pyx_PyCFunction_FastCall __Pyx_PyCFunctionFast - #define __Pyx_PyCFunction_FastCallWithKeywords __Pyx_PyCFunctionFastWithKeywords -#else - #define __Pyx_METH_FASTCALL METH_VARARGS - #define __Pyx_PyCFunction_FastCall PyCFunction - #define __Pyx_PyCFunction_FastCallWithKeywords PyCFunctionWithKeywords -#endif -#if CYTHON_VECTORCALL - #define __pyx_vectorcallfunc vectorcallfunc - #define __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET PY_VECTORCALL_ARGUMENTS_OFFSET - #define __Pyx_PyVectorcall_NARGS(n) PyVectorcall_NARGS((size_t)(n)) -#elif CYTHON_BACKPORT_VECTORCALL - typedef PyObject *(*__pyx_vectorcallfunc)(PyObject *callable, PyObject *const *args, - size_t nargsf, PyObject *kwnames); - #define __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET ((size_t)1 << (8 * sizeof(size_t) - 1)) - #define __Pyx_PyVectorcall_NARGS(n) ((Py_ssize_t)(((size_t)(n)) & ~__Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET)) -#else - #define __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET 0 - #define __Pyx_PyVectorcall_NARGS(n) ((Py_ssize_t)(n)) -#endif -#if PY_MAJOR_VERSION >= 0x030900B1 -#define __Pyx_PyCFunction_CheckExact(func) PyCFunction_CheckExact(func) -#else -#define __Pyx_PyCFunction_CheckExact(func) PyCFunction_Check(func) -#endif -#define __Pyx_CyOrPyCFunction_Check(func) PyCFunction_Check(func) -#if CYTHON_COMPILING_IN_CPYTHON -#define __Pyx_CyOrPyCFunction_GET_FUNCTION(func) (((PyCFunctionObject*)(func))->m_ml->ml_meth) -#elif !CYTHON_COMPILING_IN_LIMITED_API -#define __Pyx_CyOrPyCFunction_GET_FUNCTION(func) PyCFunction_GET_FUNCTION(func) -#endif -#if CYTHON_COMPILING_IN_CPYTHON -#define __Pyx_CyOrPyCFunction_GET_FLAGS(func) (((PyCFunctionObject*)(func))->m_ml->ml_flags) -static CYTHON_INLINE PyObject* __Pyx_CyOrPyCFunction_GET_SELF(PyObject *func) { - return (__Pyx_CyOrPyCFunction_GET_FLAGS(func) & METH_STATIC) ? NULL : ((PyCFunctionObject*)func)->m_self; -} -#endif -static CYTHON_INLINE int __Pyx__IsSameCFunction(PyObject *func, void *cfunc) { -#if CYTHON_COMPILING_IN_LIMITED_API - return PyCFunction_Check(func) && PyCFunction_GetFunction(func) == (PyCFunction) cfunc; -#else - return PyCFunction_Check(func) && PyCFunction_GET_FUNCTION(func) == (PyCFunction) cfunc; -#endif -} -#define __Pyx_IsSameCFunction(func, cfunc) __Pyx__IsSameCFunction(func, cfunc) -#if __PYX_LIMITED_VERSION_HEX < 0x030900B1 - #define __Pyx_PyType_FromModuleAndSpec(m, s, b) ((void)m, PyType_FromSpecWithBases(s, b)) - typedef PyObject *(*__Pyx_PyCMethod)(PyObject *, PyTypeObject *, PyObject *const *, size_t, PyObject *); -#else - #define __Pyx_PyType_FromModuleAndSpec(m, s, b) PyType_FromModuleAndSpec(m, s, b) - #define __Pyx_PyCMethod PyCMethod -#endif -#ifndef METH_METHOD - #define METH_METHOD 0x200 -#endif -#if CYTHON_COMPILING_IN_PYPY && !defined(PyObject_Malloc) - #define PyObject_Malloc(s) PyMem_Malloc(s) - #define PyObject_Free(p) PyMem_Free(p) - #define PyObject_Realloc(p) PyMem_Realloc(p) -#endif -#if CYTHON_COMPILING_IN_LIMITED_API - #define __Pyx_PyCode_HasFreeVars(co) (PyCode_GetNumFree(co) > 0) - #define __Pyx_PyFrame_SetLineNumber(frame, lineno) -#else - #define __Pyx_PyCode_HasFreeVars(co) (PyCode_GetNumFree(co) > 0) - #define __Pyx_PyFrame_SetLineNumber(frame, lineno) (frame)->f_lineno = (lineno) -#endif -#if CYTHON_COMPILING_IN_LIMITED_API - #define __Pyx_PyThreadState_Current PyThreadState_Get() -#elif !CYTHON_FAST_THREAD_STATE - #define __Pyx_PyThreadState_Current PyThreadState_GET() -#elif PY_VERSION_HEX >= 0x030d00A1 - #define __Pyx_PyThreadState_Current PyThreadState_GetUnchecked() -#elif PY_VERSION_HEX >= 0x03060000 - #define __Pyx_PyThreadState_Current _PyThreadState_UncheckedGet() -#elif PY_VERSION_HEX >= 0x03000000 - #define __Pyx_PyThreadState_Current PyThreadState_GET() -#else - #define __Pyx_PyThreadState_Current _PyThreadState_Current -#endif -#if CYTHON_COMPILING_IN_LIMITED_API -static CYTHON_INLINE void *__Pyx_PyModule_GetState(PyObject *op) -{ - void *result; - result = PyModule_GetState(op); - if (!result) - Py_FatalError("Couldn't find the module state"); - return result; -} -#endif -#define __Pyx_PyObject_GetSlot(obj, name, func_ctype) __Pyx_PyType_GetSlot(Py_TYPE(obj), name, func_ctype) -#if CYTHON_COMPILING_IN_LIMITED_API - #define __Pyx_PyType_GetSlot(type, name, func_ctype) ((func_ctype) PyType_GetSlot((type), Py_##name)) -#else - #define __Pyx_PyType_GetSlot(type, name, func_ctype) ((type)->name) -#endif -#if PY_VERSION_HEX < 0x030700A2 && !defined(PyThread_tss_create) && !defined(Py_tss_NEEDS_INIT) -#include "pythread.h" -#define Py_tss_NEEDS_INIT 0 -typedef int Py_tss_t; -static CYTHON_INLINE int PyThread_tss_create(Py_tss_t *key) { - *key = PyThread_create_key(); - return 0; -} -static CYTHON_INLINE Py_tss_t * PyThread_tss_alloc(void) { - Py_tss_t *key = (Py_tss_t *)PyObject_Malloc(sizeof(Py_tss_t)); - *key = Py_tss_NEEDS_INIT; - return key; -} -static CYTHON_INLINE void PyThread_tss_free(Py_tss_t *key) { - PyObject_Free(key); -} -static CYTHON_INLINE int PyThread_tss_is_created(Py_tss_t *key) { - return *key != Py_tss_NEEDS_INIT; -} -static CYTHON_INLINE void PyThread_tss_delete(Py_tss_t *key) { - PyThread_delete_key(*key); - *key = Py_tss_NEEDS_INIT; -} -static CYTHON_INLINE int PyThread_tss_set(Py_tss_t *key, void *value) { - return PyThread_set_key_value(*key, value); -} -static CYTHON_INLINE void * PyThread_tss_get(Py_tss_t *key) { - return PyThread_get_key_value(*key); -} -#endif -#if PY_MAJOR_VERSION < 3 - #if CYTHON_COMPILING_IN_PYPY - #if PYPY_VERSION_NUM < 0x07030600 - #if defined(__cplusplus) && __cplusplus >= 201402L - [[deprecated("`with nogil:` inside a nogil function will not release the GIL in PyPy2 < 7.3.6")]] - #elif defined(__GNUC__) || defined(__clang__) - __attribute__ ((__deprecated__("`with nogil:` inside a nogil function will not release the GIL in PyPy2 < 7.3.6"))) - #elif defined(_MSC_VER) - __declspec(deprecated("`with nogil:` inside a nogil function will not release the GIL in PyPy2 < 7.3.6")) - #endif - static CYTHON_INLINE int PyGILState_Check(void) { - return 0; - } - #else // PYPY_VERSION_NUM < 0x07030600 - #endif // PYPY_VERSION_NUM < 0x07030600 - #else - static CYTHON_INLINE int PyGILState_Check(void) { - PyThreadState * tstate = _PyThreadState_Current; - return tstate && (tstate == PyGILState_GetThisThreadState()); - } - #endif -#endif -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030d0000 || defined(_PyDict_NewPresized) -#define __Pyx_PyDict_NewPresized(n) ((n <= 8) ? PyDict_New() : _PyDict_NewPresized(n)) -#else -#define __Pyx_PyDict_NewPresized(n) PyDict_New() -#endif -#if PY_MAJOR_VERSION >= 3 || CYTHON_FUTURE_DIVISION - #define __Pyx_PyNumber_Divide(x,y) PyNumber_TrueDivide(x,y) - #define __Pyx_PyNumber_InPlaceDivide(x,y) PyNumber_InPlaceTrueDivide(x,y) -#else - #define __Pyx_PyNumber_Divide(x,y) PyNumber_Divide(x,y) - #define __Pyx_PyNumber_InPlaceDivide(x,y) PyNumber_InPlaceDivide(x,y) -#endif -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX > 0x030600B4 && PY_VERSION_HEX < 0x030d0000 && CYTHON_USE_UNICODE_INTERNALS -#define __Pyx_PyDict_GetItemStrWithError(dict, name) _PyDict_GetItem_KnownHash(dict, name, ((PyASCIIObject *) name)->hash) -static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStr(PyObject *dict, PyObject *name) { - PyObject *res = __Pyx_PyDict_GetItemStrWithError(dict, name); - if (res == NULL) PyErr_Clear(); - return res; -} -#elif PY_MAJOR_VERSION >= 3 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07020000) -#define __Pyx_PyDict_GetItemStrWithError PyDict_GetItemWithError -#define __Pyx_PyDict_GetItemStr PyDict_GetItem -#else -static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict, PyObject *name) { -#if CYTHON_COMPILING_IN_PYPY - return PyDict_GetItem(dict, name); -#else - PyDictEntry *ep; - PyDictObject *mp = (PyDictObject*) dict; - long hash = ((PyStringObject *) name)->ob_shash; - assert(hash != -1); - ep = (mp->ma_lookup)(mp, name, hash); - if (ep == NULL) { - return NULL; - } - return ep->me_value; -#endif -} -#define __Pyx_PyDict_GetItemStr PyDict_GetItem -#endif -#if CYTHON_USE_TYPE_SLOTS - #define __Pyx_PyType_GetFlags(tp) (((PyTypeObject *)tp)->tp_flags) - #define __Pyx_PyType_HasFeature(type, feature) ((__Pyx_PyType_GetFlags(type) & (feature)) != 0) - #define __Pyx_PyObject_GetIterNextFunc(obj) (Py_TYPE(obj)->tp_iternext) -#else - #define __Pyx_PyType_GetFlags(tp) (PyType_GetFlags((PyTypeObject *)tp)) - #define __Pyx_PyType_HasFeature(type, feature) PyType_HasFeature(type, feature) - #define __Pyx_PyObject_GetIterNextFunc(obj) PyIter_Next -#endif -#if CYTHON_COMPILING_IN_LIMITED_API - #define __Pyx_SetItemOnTypeDict(tp, k, v) PyObject_GenericSetAttr((PyObject*)tp, k, v) -#else - #define __Pyx_SetItemOnTypeDict(tp, k, v) PyDict_SetItem(tp->tp_dict, k, v) -#endif -#if CYTHON_USE_TYPE_SPECS && PY_VERSION_HEX >= 0x03080000 -#define __Pyx_PyHeapTypeObject_GC_Del(obj) {\ - PyTypeObject *type = Py_TYPE((PyObject*)obj);\ - assert(__Pyx_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE));\ - PyObject_GC_Del(obj);\ - Py_DECREF(type);\ -} -#else -#define __Pyx_PyHeapTypeObject_GC_Del(obj) PyObject_GC_Del(obj) -#endif -#if CYTHON_COMPILING_IN_LIMITED_API - #define CYTHON_PEP393_ENABLED 1 - #define __Pyx_PyUnicode_READY(op) (0) - #define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GetLength(u) - #define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_ReadChar(u, i) - #define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) ((void)u, 1114111U) - #define __Pyx_PyUnicode_KIND(u) ((void)u, (0)) - #define __Pyx_PyUnicode_DATA(u) ((void*)u) - #define __Pyx_PyUnicode_READ(k, d, i) ((void)k, PyUnicode_ReadChar((PyObject*)(d), i)) - #define __Pyx_PyUnicode_IS_TRUE(u) (0 != PyUnicode_GetLength(u)) -#elif PY_VERSION_HEX > 0x03030000 && defined(PyUnicode_KIND) - #define CYTHON_PEP393_ENABLED 1 - #if PY_VERSION_HEX >= 0x030C0000 - #define __Pyx_PyUnicode_READY(op) (0) - #else - #define __Pyx_PyUnicode_READY(op) (likely(PyUnicode_IS_READY(op)) ?\ - 0 : _PyUnicode_Ready((PyObject *)(op))) - #endif - #define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GET_LENGTH(u) - #define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_READ_CHAR(u, i) - #define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) PyUnicode_MAX_CHAR_VALUE(u) - #define __Pyx_PyUnicode_KIND(u) ((int)PyUnicode_KIND(u)) - #define __Pyx_PyUnicode_DATA(u) PyUnicode_DATA(u) - #define __Pyx_PyUnicode_READ(k, d, i) PyUnicode_READ(k, d, i) - #define __Pyx_PyUnicode_WRITE(k, d, i, ch) PyUnicode_WRITE(k, d, i, (Py_UCS4) ch) - #if PY_VERSION_HEX >= 0x030C0000 - #define __Pyx_PyUnicode_IS_TRUE(u) (0 != PyUnicode_GET_LENGTH(u)) - #else - #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03090000 - #define __Pyx_PyUnicode_IS_TRUE(u) (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : ((PyCompactUnicodeObject *)(u))->wstr_length)) - #else - #define __Pyx_PyUnicode_IS_TRUE(u) (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : PyUnicode_GET_SIZE(u))) - #endif - #endif -#else - #define CYTHON_PEP393_ENABLED 0 - #define PyUnicode_1BYTE_KIND 1 - #define PyUnicode_2BYTE_KIND 2 - #define PyUnicode_4BYTE_KIND 4 - #define __Pyx_PyUnicode_READY(op) (0) - #define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GET_SIZE(u) - #define __Pyx_PyUnicode_READ_CHAR(u, i) ((Py_UCS4)(PyUnicode_AS_UNICODE(u)[i])) - #define __Pyx_PyUnicode_MAX_CHAR_VALUE(u) ((sizeof(Py_UNICODE) == 2) ? 65535U : 1114111U) - #define __Pyx_PyUnicode_KIND(u) ((int)sizeof(Py_UNICODE)) - #define __Pyx_PyUnicode_DATA(u) ((void*)PyUnicode_AS_UNICODE(u)) - #define __Pyx_PyUnicode_READ(k, d, i) ((void)(k), (Py_UCS4)(((Py_UNICODE*)d)[i])) - #define __Pyx_PyUnicode_WRITE(k, d, i, ch) (((void)(k)), ((Py_UNICODE*)d)[i] = (Py_UNICODE) ch) - #define __Pyx_PyUnicode_IS_TRUE(u) (0 != PyUnicode_GET_SIZE(u)) -#endif -#if CYTHON_COMPILING_IN_PYPY - #define __Pyx_PyUnicode_Concat(a, b) PyNumber_Add(a, b) - #define __Pyx_PyUnicode_ConcatSafe(a, b) PyNumber_Add(a, b) -#else - #define __Pyx_PyUnicode_Concat(a, b) PyUnicode_Concat(a, b) - #define __Pyx_PyUnicode_ConcatSafe(a, b) ((unlikely((a) == Py_None) || unlikely((b) == Py_None)) ?\ - PyNumber_Add(a, b) : __Pyx_PyUnicode_Concat(a, b)) -#endif -#if CYTHON_COMPILING_IN_PYPY - #if !defined(PyUnicode_DecodeUnicodeEscape) - #define PyUnicode_DecodeUnicodeEscape(s, size, errors) PyUnicode_Decode(s, size, "unicode_escape", errors) - #endif - #if !defined(PyUnicode_Contains) || (PY_MAJOR_VERSION == 2 && PYPY_VERSION_NUM < 0x07030500) - #undef PyUnicode_Contains - #define PyUnicode_Contains(u, s) PySequence_Contains(u, s) - #endif - #if !defined(PyByteArray_Check) - #define PyByteArray_Check(obj) PyObject_TypeCheck(obj, &PyByteArray_Type) - #endif - #if !defined(PyObject_Format) - #define PyObject_Format(obj, fmt) PyObject_CallMethod(obj, "__format__", "O", fmt) - #endif -#endif -#define __Pyx_PyString_FormatSafe(a, b) ((unlikely((a) == Py_None || (PyString_Check(b) && !PyString_CheckExact(b)))) ? PyNumber_Remainder(a, b) : __Pyx_PyString_Format(a, b)) -#define __Pyx_PyUnicode_FormatSafe(a, b) ((unlikely((a) == Py_None || (PyUnicode_Check(b) && !PyUnicode_CheckExact(b)))) ? PyNumber_Remainder(a, b) : PyUnicode_Format(a, b)) -#if PY_MAJOR_VERSION >= 3 - #define __Pyx_PyString_Format(a, b) PyUnicode_Format(a, b) -#else - #define __Pyx_PyString_Format(a, b) PyString_Format(a, b) -#endif -#if PY_MAJOR_VERSION < 3 && !defined(PyObject_ASCII) - #define PyObject_ASCII(o) PyObject_Repr(o) -#endif -#if PY_MAJOR_VERSION >= 3 - #define PyBaseString_Type PyUnicode_Type - #define PyStringObject PyUnicodeObject - #define PyString_Type PyUnicode_Type - #define PyString_Check PyUnicode_Check - #define PyString_CheckExact PyUnicode_CheckExact -#ifndef PyObject_Unicode - #define PyObject_Unicode PyObject_Str -#endif -#endif -#if PY_MAJOR_VERSION >= 3 - #define __Pyx_PyBaseString_Check(obj) PyUnicode_Check(obj) - #define __Pyx_PyBaseString_CheckExact(obj) PyUnicode_CheckExact(obj) -#else - #define __Pyx_PyBaseString_Check(obj) (PyString_Check(obj) || PyUnicode_Check(obj)) - #define __Pyx_PyBaseString_CheckExact(obj) (PyString_CheckExact(obj) || PyUnicode_CheckExact(obj)) -#endif -#if CYTHON_COMPILING_IN_CPYTHON - #define __Pyx_PySequence_ListKeepNew(obj)\ - (likely(PyList_CheckExact(obj) && Py_REFCNT(obj) == 1) ? __Pyx_NewRef(obj) : PySequence_List(obj)) -#else - #define __Pyx_PySequence_ListKeepNew(obj) PySequence_List(obj) -#endif -#ifndef PySet_CheckExact - #define PySet_CheckExact(obj) __Pyx_IS_TYPE(obj, &PySet_Type) -#endif -#if PY_VERSION_HEX >= 0x030900A4 - #define __Pyx_SET_REFCNT(obj, refcnt) Py_SET_REFCNT(obj, refcnt) - #define __Pyx_SET_SIZE(obj, size) Py_SET_SIZE(obj, size) -#else - #define __Pyx_SET_REFCNT(obj, refcnt) Py_REFCNT(obj) = (refcnt) - #define __Pyx_SET_SIZE(obj, size) Py_SIZE(obj) = (size) -#endif -#if CYTHON_ASSUME_SAFE_MACROS - #define __Pyx_PySequence_ITEM(o, i) PySequence_ITEM(o, i) - #define __Pyx_PySequence_SIZE(seq) Py_SIZE(seq) - #define __Pyx_PyTuple_SET_ITEM(o, i, v) (PyTuple_SET_ITEM(o, i, v), (0)) - #define __Pyx_PyList_SET_ITEM(o, i, v) (PyList_SET_ITEM(o, i, v), (0)) - #define __Pyx_PyTuple_GET_SIZE(o) PyTuple_GET_SIZE(o) - #define __Pyx_PyList_GET_SIZE(o) PyList_GET_SIZE(o) - #define __Pyx_PySet_GET_SIZE(o) PySet_GET_SIZE(o) - #define __Pyx_PyBytes_GET_SIZE(o) PyBytes_GET_SIZE(o) - #define __Pyx_PyByteArray_GET_SIZE(o) PyByteArray_GET_SIZE(o) -#else - #define __Pyx_PySequence_ITEM(o, i) PySequence_GetItem(o, i) - #define __Pyx_PySequence_SIZE(seq) PySequence_Size(seq) - #define __Pyx_PyTuple_SET_ITEM(o, i, v) PyTuple_SetItem(o, i, v) - #define __Pyx_PyList_SET_ITEM(o, i, v) PyList_SetItem(o, i, v) - #define __Pyx_PyTuple_GET_SIZE(o) PyTuple_Size(o) - #define __Pyx_PyList_GET_SIZE(o) PyList_Size(o) - #define __Pyx_PySet_GET_SIZE(o) PySet_Size(o) - #define __Pyx_PyBytes_GET_SIZE(o) PyBytes_Size(o) - #define __Pyx_PyByteArray_GET_SIZE(o) PyByteArray_Size(o) -#endif -#if __PYX_LIMITED_VERSION_HEX >= 0x030d00A1 - #define __Pyx_PyImport_AddModuleRef(name) PyImport_AddModuleRef(name) -#else - static CYTHON_INLINE PyObject *__Pyx_PyImport_AddModuleRef(const char *name) { - PyObject *module = PyImport_AddModule(name); - Py_XINCREF(module); - return module; - } -#endif -#if PY_MAJOR_VERSION >= 3 - #define PyIntObject PyLongObject - #define PyInt_Type PyLong_Type - #define PyInt_Check(op) PyLong_Check(op) - #define PyInt_CheckExact(op) PyLong_CheckExact(op) - #define __Pyx_Py3Int_Check(op) PyLong_Check(op) - #define __Pyx_Py3Int_CheckExact(op) PyLong_CheckExact(op) - #define PyInt_FromString PyLong_FromString - #define PyInt_FromUnicode PyLong_FromUnicode - #define PyInt_FromLong PyLong_FromLong - #define PyInt_FromSize_t PyLong_FromSize_t - #define PyInt_FromSsize_t PyLong_FromSsize_t - #define PyInt_AsLong PyLong_AsLong - #define PyInt_AS_LONG PyLong_AS_LONG - #define PyInt_AsSsize_t PyLong_AsSsize_t - #define PyInt_AsUnsignedLongMask PyLong_AsUnsignedLongMask - #define PyInt_AsUnsignedLongLongMask PyLong_AsUnsignedLongLongMask - #define PyNumber_Int PyNumber_Long -#else - #define __Pyx_Py3Int_Check(op) (PyLong_Check(op) || PyInt_Check(op)) - #define __Pyx_Py3Int_CheckExact(op) (PyLong_CheckExact(op) || PyInt_CheckExact(op)) -#endif -#if PY_MAJOR_VERSION >= 3 - #define PyBoolObject PyLongObject -#endif -#if PY_MAJOR_VERSION >= 3 && CYTHON_COMPILING_IN_PYPY - #ifndef PyUnicode_InternFromString - #define PyUnicode_InternFromString(s) PyUnicode_FromString(s) - #endif -#endif -#if PY_VERSION_HEX < 0x030200A4 - typedef long Py_hash_t; - #define __Pyx_PyInt_FromHash_t PyInt_FromLong - #define __Pyx_PyInt_AsHash_t __Pyx_PyIndex_AsHash_t -#else - #define __Pyx_PyInt_FromHash_t PyInt_FromSsize_t - #define __Pyx_PyInt_AsHash_t __Pyx_PyIndex_AsSsize_t -#endif -#if CYTHON_USE_ASYNC_SLOTS - #if PY_VERSION_HEX >= 0x030500B1 - #define __Pyx_PyAsyncMethodsStruct PyAsyncMethods - #define __Pyx_PyType_AsAsync(obj) (Py_TYPE(obj)->tp_as_async) - #else - #define __Pyx_PyType_AsAsync(obj) ((__Pyx_PyAsyncMethodsStruct*) (Py_TYPE(obj)->tp_reserved)) - #endif -#else - #define __Pyx_PyType_AsAsync(obj) NULL -#endif -#ifndef __Pyx_PyAsyncMethodsStruct - typedef struct { - unaryfunc am_await; - unaryfunc am_aiter; - unaryfunc am_anext; - } __Pyx_PyAsyncMethodsStruct; -#endif - -#if defined(_WIN32) || defined(WIN32) || defined(MS_WINDOWS) - #if !defined(_USE_MATH_DEFINES) - #define _USE_MATH_DEFINES - #endif -#endif -#include -#ifdef NAN -#define __PYX_NAN() ((float) NAN) -#else -static CYTHON_INLINE float __PYX_NAN() { - float value; - memset(&value, 0xFF, sizeof(value)); - return value; -} -#endif -#if defined(__CYGWIN__) && defined(_LDBL_EQ_DBL) -#define __Pyx_truncl trunc -#else -#define __Pyx_truncl truncl -#endif - -#define __PYX_MARK_ERR_POS(f_index, lineno) \ - { __pyx_filename = __pyx_f[f_index]; (void)__pyx_filename; __pyx_lineno = lineno; (void)__pyx_lineno; __pyx_clineno = __LINE__; (void)__pyx_clineno; } -#define __PYX_ERR(f_index, lineno, Ln_error) \ - { __PYX_MARK_ERR_POS(f_index, lineno) goto Ln_error; } - -#ifdef CYTHON_EXTERN_C - #undef __PYX_EXTERN_C - #define __PYX_EXTERN_C CYTHON_EXTERN_C -#elif defined(__PYX_EXTERN_C) - #ifdef _MSC_VER - #pragma message ("Please do not define the '__PYX_EXTERN_C' macro externally. Use 'CYTHON_EXTERN_C' instead.") - #else - #warning Please do not define the '__PYX_EXTERN_C' macro externally. Use 'CYTHON_EXTERN_C' instead. - #endif -#else - #ifdef __cplusplus - #define __PYX_EXTERN_C extern "C" - #else - #define __PYX_EXTERN_C extern - #endif -#endif - -#define __PYX_HAVE__fontTools__cu2qu__cu2qu -#define __PYX_HAVE_API__fontTools__cu2qu__cu2qu -/* Early includes */ -#ifdef _OPENMP -#include -#endif /* _OPENMP */ - -#if defined(PYREX_WITHOUT_ASSERTIONS) && !defined(CYTHON_WITHOUT_ASSERTIONS) -#define CYTHON_WITHOUT_ASSERTIONS -#endif - -typedef struct {PyObject **p; const char *s; const Py_ssize_t n; const char* encoding; - const char is_unicode; const char is_str; const char intern; } __Pyx_StringTabEntry; - -#define __PYX_DEFAULT_STRING_ENCODING_IS_ASCII 0 -#define __PYX_DEFAULT_STRING_ENCODING_IS_UTF8 0 -#define __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT (PY_MAJOR_VERSION >= 3 && __PYX_DEFAULT_STRING_ENCODING_IS_UTF8) -#define __PYX_DEFAULT_STRING_ENCODING "" -#define __Pyx_PyObject_FromString __Pyx_PyBytes_FromString -#define __Pyx_PyObject_FromStringAndSize __Pyx_PyBytes_FromStringAndSize -#define __Pyx_uchar_cast(c) ((unsigned char)c) -#define __Pyx_long_cast(x) ((long)x) -#define __Pyx_fits_Py_ssize_t(v, type, is_signed) (\ - (sizeof(type) < sizeof(Py_ssize_t)) ||\ - (sizeof(type) > sizeof(Py_ssize_t) &&\ - likely(v < (type)PY_SSIZE_T_MAX ||\ - v == (type)PY_SSIZE_T_MAX) &&\ - (!is_signed || likely(v > (type)PY_SSIZE_T_MIN ||\ - v == (type)PY_SSIZE_T_MIN))) ||\ - (sizeof(type) == sizeof(Py_ssize_t) &&\ - (is_signed || likely(v < (type)PY_SSIZE_T_MAX ||\ - v == (type)PY_SSIZE_T_MAX))) ) -static CYTHON_INLINE int __Pyx_is_valid_index(Py_ssize_t i, Py_ssize_t limit) { - return (size_t) i < (size_t) limit; -} -#if defined (__cplusplus) && __cplusplus >= 201103L - #include - #define __Pyx_sst_abs(value) std::abs(value) -#elif SIZEOF_INT >= SIZEOF_SIZE_T - #define __Pyx_sst_abs(value) abs(value) -#elif SIZEOF_LONG >= SIZEOF_SIZE_T - #define __Pyx_sst_abs(value) labs(value) -#elif defined (_MSC_VER) - #define __Pyx_sst_abs(value) ((Py_ssize_t)_abs64(value)) -#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L - #define __Pyx_sst_abs(value) llabs(value) -#elif defined (__GNUC__) - #define __Pyx_sst_abs(value) __builtin_llabs(value) -#else - #define __Pyx_sst_abs(value) ((value<0) ? -value : value) -#endif -static CYTHON_INLINE Py_ssize_t __Pyx_ssize_strlen(const char *s); -static CYTHON_INLINE const char* __Pyx_PyObject_AsString(PyObject*); -static CYTHON_INLINE const char* __Pyx_PyObject_AsStringAndSize(PyObject*, Py_ssize_t* length); -static CYTHON_INLINE PyObject* __Pyx_PyByteArray_FromString(const char*); -#define __Pyx_PyByteArray_FromStringAndSize(s, l) PyByteArray_FromStringAndSize((const char*)s, l) -#define __Pyx_PyBytes_FromString PyBytes_FromString -#define __Pyx_PyBytes_FromStringAndSize PyBytes_FromStringAndSize -static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(const char*); -#if PY_MAJOR_VERSION < 3 - #define __Pyx_PyStr_FromString __Pyx_PyBytes_FromString - #define __Pyx_PyStr_FromStringAndSize __Pyx_PyBytes_FromStringAndSize -#else - #define __Pyx_PyStr_FromString __Pyx_PyUnicode_FromString - #define __Pyx_PyStr_FromStringAndSize __Pyx_PyUnicode_FromStringAndSize -#endif -#define __Pyx_PyBytes_AsWritableString(s) ((char*) PyBytes_AS_STRING(s)) -#define __Pyx_PyBytes_AsWritableSString(s) ((signed char*) PyBytes_AS_STRING(s)) -#define __Pyx_PyBytes_AsWritableUString(s) ((unsigned char*) PyBytes_AS_STRING(s)) -#define __Pyx_PyBytes_AsString(s) ((const char*) PyBytes_AS_STRING(s)) -#define __Pyx_PyBytes_AsSString(s) ((const signed char*) PyBytes_AS_STRING(s)) -#define __Pyx_PyBytes_AsUString(s) ((const unsigned char*) PyBytes_AS_STRING(s)) -#define __Pyx_PyObject_AsWritableString(s) ((char*)(__pyx_uintptr_t) __Pyx_PyObject_AsString(s)) -#define __Pyx_PyObject_AsWritableSString(s) ((signed char*)(__pyx_uintptr_t) __Pyx_PyObject_AsString(s)) -#define __Pyx_PyObject_AsWritableUString(s) ((unsigned char*)(__pyx_uintptr_t) __Pyx_PyObject_AsString(s)) -#define __Pyx_PyObject_AsSString(s) ((const signed char*) __Pyx_PyObject_AsString(s)) -#define __Pyx_PyObject_AsUString(s) ((const unsigned char*) __Pyx_PyObject_AsString(s)) -#define __Pyx_PyObject_FromCString(s) __Pyx_PyObject_FromString((const char*)s) -#define __Pyx_PyBytes_FromCString(s) __Pyx_PyBytes_FromString((const char*)s) -#define __Pyx_PyByteArray_FromCString(s) __Pyx_PyByteArray_FromString((const char*)s) -#define __Pyx_PyStr_FromCString(s) __Pyx_PyStr_FromString((const char*)s) -#define __Pyx_PyUnicode_FromCString(s) __Pyx_PyUnicode_FromString((const char*)s) -#define __Pyx_PyUnicode_FromOrdinal(o) PyUnicode_FromOrdinal((int)o) -#define __Pyx_PyUnicode_AsUnicode PyUnicode_AsUnicode -#define __Pyx_NewRef(obj) (Py_INCREF(obj), obj) -#define __Pyx_Owned_Py_None(b) __Pyx_NewRef(Py_None) -static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b); -static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*); -static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject*); -static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(PyObject* x); -#define __Pyx_PySequence_Tuple(obj)\ - (likely(PyTuple_CheckExact(obj)) ? __Pyx_NewRef(obj) : PySequence_Tuple(obj)) -static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject*); -static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t); -static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject*); -#if CYTHON_ASSUME_SAFE_MACROS -#define __pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x)) -#else -#define __pyx_PyFloat_AsDouble(x) PyFloat_AsDouble(x) -#endif -#define __pyx_PyFloat_AsFloat(x) ((float) __pyx_PyFloat_AsDouble(x)) -#if PY_MAJOR_VERSION >= 3 -#define __Pyx_PyNumber_Int(x) (PyLong_CheckExact(x) ? __Pyx_NewRef(x) : PyNumber_Long(x)) -#else -#define __Pyx_PyNumber_Int(x) (PyInt_CheckExact(x) ? __Pyx_NewRef(x) : PyNumber_Int(x)) -#endif -#if CYTHON_USE_PYLONG_INTERNALS - #if PY_VERSION_HEX >= 0x030C00A7 - #ifndef _PyLong_SIGN_MASK - #define _PyLong_SIGN_MASK 3 - #endif - #ifndef _PyLong_NON_SIZE_BITS - #define _PyLong_NON_SIZE_BITS 3 - #endif - #define __Pyx_PyLong_Sign(x) (((PyLongObject*)x)->long_value.lv_tag & _PyLong_SIGN_MASK) - #define __Pyx_PyLong_IsNeg(x) ((__Pyx_PyLong_Sign(x) & 2) != 0) - #define __Pyx_PyLong_IsNonNeg(x) (!__Pyx_PyLong_IsNeg(x)) - #define __Pyx_PyLong_IsZero(x) (__Pyx_PyLong_Sign(x) & 1) - #define __Pyx_PyLong_IsPos(x) (__Pyx_PyLong_Sign(x) == 0) - #define __Pyx_PyLong_CompactValueUnsigned(x) (__Pyx_PyLong_Digits(x)[0]) - #define __Pyx_PyLong_DigitCount(x) ((Py_ssize_t) (((PyLongObject*)x)->long_value.lv_tag >> _PyLong_NON_SIZE_BITS)) - #define __Pyx_PyLong_SignedDigitCount(x)\ - ((1 - (Py_ssize_t) __Pyx_PyLong_Sign(x)) * __Pyx_PyLong_DigitCount(x)) - #if defined(PyUnstable_Long_IsCompact) && defined(PyUnstable_Long_CompactValue) - #define __Pyx_PyLong_IsCompact(x) PyUnstable_Long_IsCompact((PyLongObject*) x) - #define __Pyx_PyLong_CompactValue(x) PyUnstable_Long_CompactValue((PyLongObject*) x) - #else - #define __Pyx_PyLong_IsCompact(x) (((PyLongObject*)x)->long_value.lv_tag < (2 << _PyLong_NON_SIZE_BITS)) - #define __Pyx_PyLong_CompactValue(x) ((1 - (Py_ssize_t) __Pyx_PyLong_Sign(x)) * (Py_ssize_t) __Pyx_PyLong_Digits(x)[0]) - #endif - typedef Py_ssize_t __Pyx_compact_pylong; - typedef size_t __Pyx_compact_upylong; - #else - #define __Pyx_PyLong_IsNeg(x) (Py_SIZE(x) < 0) - #define __Pyx_PyLong_IsNonNeg(x) (Py_SIZE(x) >= 0) - #define __Pyx_PyLong_IsZero(x) (Py_SIZE(x) == 0) - #define __Pyx_PyLong_IsPos(x) (Py_SIZE(x) > 0) - #define __Pyx_PyLong_CompactValueUnsigned(x) ((Py_SIZE(x) == 0) ? 0 : __Pyx_PyLong_Digits(x)[0]) - #define __Pyx_PyLong_DigitCount(x) __Pyx_sst_abs(Py_SIZE(x)) - #define __Pyx_PyLong_SignedDigitCount(x) Py_SIZE(x) - #define __Pyx_PyLong_IsCompact(x) (Py_SIZE(x) == 0 || Py_SIZE(x) == 1 || Py_SIZE(x) == -1) - #define __Pyx_PyLong_CompactValue(x)\ - ((Py_SIZE(x) == 0) ? (sdigit) 0 : ((Py_SIZE(x) < 0) ? -(sdigit)__Pyx_PyLong_Digits(x)[0] : (sdigit)__Pyx_PyLong_Digits(x)[0])) - typedef sdigit __Pyx_compact_pylong; - typedef digit __Pyx_compact_upylong; - #endif - #if PY_VERSION_HEX >= 0x030C00A5 - #define __Pyx_PyLong_Digits(x) (((PyLongObject*)x)->long_value.ob_digit) - #else - #define __Pyx_PyLong_Digits(x) (((PyLongObject*)x)->ob_digit) - #endif -#endif -#if PY_MAJOR_VERSION < 3 && __PYX_DEFAULT_STRING_ENCODING_IS_ASCII -#include -static int __Pyx_sys_getdefaultencoding_not_ascii; -static int __Pyx_init_sys_getdefaultencoding_params(void) { - PyObject* sys; - PyObject* default_encoding = NULL; - PyObject* ascii_chars_u = NULL; - PyObject* ascii_chars_b = NULL; - const char* default_encoding_c; - sys = PyImport_ImportModule("sys"); - if (!sys) goto bad; - default_encoding = PyObject_CallMethod(sys, (char*) "getdefaultencoding", NULL); - Py_DECREF(sys); - if (!default_encoding) goto bad; - default_encoding_c = PyBytes_AsString(default_encoding); - if (!default_encoding_c) goto bad; - if (strcmp(default_encoding_c, "ascii") == 0) { - __Pyx_sys_getdefaultencoding_not_ascii = 0; - } else { - char ascii_chars[128]; - int c; - for (c = 0; c < 128; c++) { - ascii_chars[c] = (char) c; - } - __Pyx_sys_getdefaultencoding_not_ascii = 1; - ascii_chars_u = PyUnicode_DecodeASCII(ascii_chars, 128, NULL); - if (!ascii_chars_u) goto bad; - ascii_chars_b = PyUnicode_AsEncodedString(ascii_chars_u, default_encoding_c, NULL); - if (!ascii_chars_b || !PyBytes_Check(ascii_chars_b) || memcmp(ascii_chars, PyBytes_AS_STRING(ascii_chars_b), 128) != 0) { - PyErr_Format( - PyExc_ValueError, - "This module compiled with c_string_encoding=ascii, but default encoding '%.200s' is not a superset of ascii.", - default_encoding_c); - goto bad; - } - Py_DECREF(ascii_chars_u); - Py_DECREF(ascii_chars_b); - } - Py_DECREF(default_encoding); - return 0; -bad: - Py_XDECREF(default_encoding); - Py_XDECREF(ascii_chars_u); - Py_XDECREF(ascii_chars_b); - return -1; -} -#endif -#if __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT && PY_MAJOR_VERSION >= 3 -#define __Pyx_PyUnicode_FromStringAndSize(c_str, size) PyUnicode_DecodeUTF8(c_str, size, NULL) -#else -#define __Pyx_PyUnicode_FromStringAndSize(c_str, size) PyUnicode_Decode(c_str, size, __PYX_DEFAULT_STRING_ENCODING, NULL) -#if __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT -#include -static char* __PYX_DEFAULT_STRING_ENCODING; -static int __Pyx_init_sys_getdefaultencoding_params(void) { - PyObject* sys; - PyObject* default_encoding = NULL; - char* default_encoding_c; - sys = PyImport_ImportModule("sys"); - if (!sys) goto bad; - default_encoding = PyObject_CallMethod(sys, (char*) (const char*) "getdefaultencoding", NULL); - Py_DECREF(sys); - if (!default_encoding) goto bad; - default_encoding_c = PyBytes_AsString(default_encoding); - if (!default_encoding_c) goto bad; - __PYX_DEFAULT_STRING_ENCODING = (char*) malloc(strlen(default_encoding_c) + 1); - if (!__PYX_DEFAULT_STRING_ENCODING) goto bad; - strcpy(__PYX_DEFAULT_STRING_ENCODING, default_encoding_c); - Py_DECREF(default_encoding); - return 0; -bad: - Py_XDECREF(default_encoding); - return -1; -} -#endif -#endif - - -/* Test for GCC > 2.95 */ -#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95))) - #define likely(x) __builtin_expect(!!(x), 1) - #define unlikely(x) __builtin_expect(!!(x), 0) -#else /* !__GNUC__ or GCC < 2.95 */ - #define likely(x) (x) - #define unlikely(x) (x) -#endif /* __GNUC__ */ -static CYTHON_INLINE void __Pyx_pretend_to_initialize(void* ptr) { (void)ptr; } - -#if !CYTHON_USE_MODULE_STATE -static PyObject *__pyx_m = NULL; -#endif -static int __pyx_lineno; -static int __pyx_clineno = 0; -static const char * __pyx_cfilenm = __FILE__; -static const char *__pyx_filename; - -/* Header.proto */ -#if !defined(CYTHON_CCOMPLEX) - #if defined(__cplusplus) - #define CYTHON_CCOMPLEX 1 - #elif (defined(_Complex_I) && !defined(_MSC_VER)) || ((defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) && !defined(__STDC_NO_COMPLEX__) && !defined(_MSC_VER)) - #define CYTHON_CCOMPLEX 1 - #else - #define CYTHON_CCOMPLEX 0 - #endif -#endif -#if CYTHON_CCOMPLEX - #ifdef __cplusplus - #include - #else - #include - #endif -#endif -#if CYTHON_CCOMPLEX && !defined(__cplusplus) && defined(__sun__) && defined(__GNUC__) - #undef _Complex_I - #define _Complex_I 1.0fj -#endif - -/* #### Code section: filename_table ### */ - -static const char *__pyx_f[] = { - "Lib/fontTools/cu2qu/cu2qu.py", -}; -/* #### Code section: utility_code_proto_before_types ### */ -/* ForceInitThreads.proto */ -#ifndef __PYX_FORCE_INIT_THREADS - #define __PYX_FORCE_INIT_THREADS 0 -#endif - -/* #### Code section: numeric_typedefs ### */ -/* #### Code section: complex_type_declarations ### */ -/* Declarations.proto */ -#if CYTHON_CCOMPLEX && (1) && (!0 || __cplusplus) - #ifdef __cplusplus - typedef ::std::complex< double > __pyx_t_double_complex; - #else - typedef double _Complex __pyx_t_double_complex; - #endif -#else - typedef struct { double real, imag; } __pyx_t_double_complex; -#endif -static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(double, double); - -/* #### Code section: type_declarations ### */ - -/*--- Type declarations ---*/ -struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen; - -/* "fontTools/cu2qu/cu2qu.py":127 - * - * - * @cython.locals( # <<<<<<<<<<<<<< - * p0=cython.complex, - * p1=cython.complex, - */ -struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen { - PyObject_HEAD - __pyx_t_double_complex __pyx_v_a; - __pyx_t_double_complex __pyx_v_a1; - __pyx_t_double_complex __pyx_v_b; - __pyx_t_double_complex __pyx_v_b1; - __pyx_t_double_complex __pyx_v_c; - __pyx_t_double_complex __pyx_v_c1; - __pyx_t_double_complex __pyx_v_d; - __pyx_t_double_complex __pyx_v_d1; - double __pyx_v_delta_2; - double __pyx_v_delta_3; - double __pyx_v_dt; - int __pyx_v_i; - int __pyx_v_n; - __pyx_t_double_complex __pyx_v_p0; - __pyx_t_double_complex __pyx_v_p1; - __pyx_t_double_complex __pyx_v_p2; - __pyx_t_double_complex __pyx_v_p3; - double __pyx_v_t1; - double __pyx_v_t1_2; - int __pyx_t_0; - int __pyx_t_1; - int __pyx_t_2; -}; - -/* #### Code section: utility_code_proto ### */ - -/* --- Runtime support code (head) --- */ -/* Refnanny.proto */ -#ifndef CYTHON_REFNANNY - #define CYTHON_REFNANNY 0 -#endif -#if CYTHON_REFNANNY - typedef struct { - void (*INCREF)(void*, PyObject*, Py_ssize_t); - void (*DECREF)(void*, PyObject*, Py_ssize_t); - void (*GOTREF)(void*, PyObject*, Py_ssize_t); - void (*GIVEREF)(void*, PyObject*, Py_ssize_t); - void* (*SetupContext)(const char*, Py_ssize_t, const char*); - void (*FinishContext)(void**); - } __Pyx_RefNannyAPIStruct; - static __Pyx_RefNannyAPIStruct *__Pyx_RefNanny = NULL; - static __Pyx_RefNannyAPIStruct *__Pyx_RefNannyImportAPI(const char *modname); - #define __Pyx_RefNannyDeclarations void *__pyx_refnanny = NULL; -#ifdef WITH_THREAD - #define __Pyx_RefNannySetupContext(name, acquire_gil)\ - if (acquire_gil) {\ - PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();\ - __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), (__LINE__), (__FILE__));\ - PyGILState_Release(__pyx_gilstate_save);\ - } else {\ - __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), (__LINE__), (__FILE__));\ - } - #define __Pyx_RefNannyFinishContextNogil() {\ - PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();\ - __Pyx_RefNannyFinishContext();\ - PyGILState_Release(__pyx_gilstate_save);\ - } -#else - #define __Pyx_RefNannySetupContext(name, acquire_gil)\ - __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), (__LINE__), (__FILE__)) - #define __Pyx_RefNannyFinishContextNogil() __Pyx_RefNannyFinishContext() -#endif - #define __Pyx_RefNannyFinishContextNogil() {\ - PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();\ - __Pyx_RefNannyFinishContext();\ - PyGILState_Release(__pyx_gilstate_save);\ - } - #define __Pyx_RefNannyFinishContext()\ - __Pyx_RefNanny->FinishContext(&__pyx_refnanny) - #define __Pyx_INCREF(r) __Pyx_RefNanny->INCREF(__pyx_refnanny, (PyObject *)(r), (__LINE__)) - #define __Pyx_DECREF(r) __Pyx_RefNanny->DECREF(__pyx_refnanny, (PyObject *)(r), (__LINE__)) - #define __Pyx_GOTREF(r) __Pyx_RefNanny->GOTREF(__pyx_refnanny, (PyObject *)(r), (__LINE__)) - #define __Pyx_GIVEREF(r) __Pyx_RefNanny->GIVEREF(__pyx_refnanny, (PyObject *)(r), (__LINE__)) - #define __Pyx_XINCREF(r) do { if((r) == NULL); else {__Pyx_INCREF(r); }} while(0) - #define __Pyx_XDECREF(r) do { if((r) == NULL); else {__Pyx_DECREF(r); }} while(0) - #define __Pyx_XGOTREF(r) do { if((r) == NULL); else {__Pyx_GOTREF(r); }} while(0) - #define __Pyx_XGIVEREF(r) do { if((r) == NULL); else {__Pyx_GIVEREF(r);}} while(0) -#else - #define __Pyx_RefNannyDeclarations - #define __Pyx_RefNannySetupContext(name, acquire_gil) - #define __Pyx_RefNannyFinishContextNogil() - #define __Pyx_RefNannyFinishContext() - #define __Pyx_INCREF(r) Py_INCREF(r) - #define __Pyx_DECREF(r) Py_DECREF(r) - #define __Pyx_GOTREF(r) - #define __Pyx_GIVEREF(r) - #define __Pyx_XINCREF(r) Py_XINCREF(r) - #define __Pyx_XDECREF(r) Py_XDECREF(r) - #define __Pyx_XGOTREF(r) - #define __Pyx_XGIVEREF(r) -#endif -#define __Pyx_Py_XDECREF_SET(r, v) do {\ - PyObject *tmp = (PyObject *) r;\ - r = v; Py_XDECREF(tmp);\ - } while (0) -#define __Pyx_XDECREF_SET(r, v) do {\ - PyObject *tmp = (PyObject *) r;\ - r = v; __Pyx_XDECREF(tmp);\ - } while (0) -#define __Pyx_DECREF_SET(r, v) do {\ - PyObject *tmp = (PyObject *) r;\ - r = v; __Pyx_DECREF(tmp);\ - } while (0) -#define __Pyx_CLEAR(r) do { PyObject* tmp = ((PyObject*)(r)); r = NULL; __Pyx_DECREF(tmp);} while(0) -#define __Pyx_XCLEAR(r) do { if((r) != NULL) {PyObject* tmp = ((PyObject*)(r)); r = NULL; __Pyx_DECREF(tmp);}} while(0) - -/* PyErrExceptionMatches.proto */ -#if CYTHON_FAST_THREAD_STATE -#define __Pyx_PyErr_ExceptionMatches(err) __Pyx_PyErr_ExceptionMatchesInState(__pyx_tstate, err) -static CYTHON_INLINE int __Pyx_PyErr_ExceptionMatchesInState(PyThreadState* tstate, PyObject* err); -#else -#define __Pyx_PyErr_ExceptionMatches(err) PyErr_ExceptionMatches(err) -#endif - -/* PyThreadStateGet.proto */ -#if CYTHON_FAST_THREAD_STATE -#define __Pyx_PyThreadState_declare PyThreadState *__pyx_tstate; -#define __Pyx_PyThreadState_assign __pyx_tstate = __Pyx_PyThreadState_Current; -#if PY_VERSION_HEX >= 0x030C00A6 -#define __Pyx_PyErr_Occurred() (__pyx_tstate->current_exception != NULL) -#define __Pyx_PyErr_CurrentExceptionType() (__pyx_tstate->current_exception ? (PyObject*) Py_TYPE(__pyx_tstate->current_exception) : (PyObject*) NULL) -#else -#define __Pyx_PyErr_Occurred() (__pyx_tstate->curexc_type != NULL) -#define __Pyx_PyErr_CurrentExceptionType() (__pyx_tstate->curexc_type) -#endif -#else -#define __Pyx_PyThreadState_declare -#define __Pyx_PyThreadState_assign -#define __Pyx_PyErr_Occurred() (PyErr_Occurred() != NULL) -#define __Pyx_PyErr_CurrentExceptionType() PyErr_Occurred() -#endif - -/* PyErrFetchRestore.proto */ -#if CYTHON_FAST_THREAD_STATE -#define __Pyx_PyErr_Clear() __Pyx_ErrRestore(NULL, NULL, NULL) -#define __Pyx_ErrRestoreWithState(type, value, tb) __Pyx_ErrRestoreInState(PyThreadState_GET(), type, value, tb) -#define __Pyx_ErrFetchWithState(type, value, tb) __Pyx_ErrFetchInState(PyThreadState_GET(), type, value, tb) -#define __Pyx_ErrRestore(type, value, tb) __Pyx_ErrRestoreInState(__pyx_tstate, type, value, tb) -#define __Pyx_ErrFetch(type, value, tb) __Pyx_ErrFetchInState(__pyx_tstate, type, value, tb) -static CYTHON_INLINE void __Pyx_ErrRestoreInState(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb); -static CYTHON_INLINE void __Pyx_ErrFetchInState(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb); -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030C00A6 -#define __Pyx_PyErr_SetNone(exc) (Py_INCREF(exc), __Pyx_ErrRestore((exc), NULL, NULL)) -#else -#define __Pyx_PyErr_SetNone(exc) PyErr_SetNone(exc) -#endif -#else -#define __Pyx_PyErr_Clear() PyErr_Clear() -#define __Pyx_PyErr_SetNone(exc) PyErr_SetNone(exc) -#define __Pyx_ErrRestoreWithState(type, value, tb) PyErr_Restore(type, value, tb) -#define __Pyx_ErrFetchWithState(type, value, tb) PyErr_Fetch(type, value, tb) -#define __Pyx_ErrRestoreInState(tstate, type, value, tb) PyErr_Restore(type, value, tb) -#define __Pyx_ErrFetchInState(tstate, type, value, tb) PyErr_Fetch(type, value, tb) -#define __Pyx_ErrRestore(type, value, tb) PyErr_Restore(type, value, tb) -#define __Pyx_ErrFetch(type, value, tb) PyErr_Fetch(type, value, tb) -#endif - -/* PyObjectGetAttrStr.proto */ -#if CYTHON_USE_TYPE_SLOTS -static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStr(PyObject* obj, PyObject* attr_name); -#else -#define __Pyx_PyObject_GetAttrStr(o,n) PyObject_GetAttr(o,n) -#endif - -/* PyObjectGetAttrStrNoError.proto */ -static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStrNoError(PyObject* obj, PyObject* attr_name); - -/* GetBuiltinName.proto */ -static PyObject *__Pyx_GetBuiltinName(PyObject *name); - -/* PyIntCompare.proto */ -static CYTHON_INLINE int __Pyx_PyInt_BoolEqObjC(PyObject *op1, PyObject *op2, long intval, long inplace); - -/* RaiseTooManyValuesToUnpack.proto */ -static CYTHON_INLINE void __Pyx_RaiseTooManyValuesError(Py_ssize_t expected); - -/* RaiseNeedMoreValuesToUnpack.proto */ -static CYTHON_INLINE void __Pyx_RaiseNeedMoreValuesError(Py_ssize_t index); - -/* IterFinish.proto */ -static CYTHON_INLINE int __Pyx_IterFinish(void); - -/* UnpackItemEndCheck.proto */ -static int __Pyx_IternextUnpackEndCheck(PyObject *retval, Py_ssize_t expected); - -/* GetItemInt.proto */ -#define __Pyx_GetItemInt(o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck)\ - (__Pyx_fits_Py_ssize_t(i, type, is_signed) ?\ - __Pyx_GetItemInt_Fast(o, (Py_ssize_t)i, is_list, wraparound, boundscheck) :\ - (is_list ? (PyErr_SetString(PyExc_IndexError, "list index out of range"), (PyObject*)NULL) :\ - __Pyx_GetItemInt_Generic(o, to_py_func(i)))) -#define __Pyx_GetItemInt_List(o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck)\ - (__Pyx_fits_Py_ssize_t(i, type, is_signed) ?\ - __Pyx_GetItemInt_List_Fast(o, (Py_ssize_t)i, wraparound, boundscheck) :\ - (PyErr_SetString(PyExc_IndexError, "list index out of range"), (PyObject*)NULL)) -static CYTHON_INLINE PyObject *__Pyx_GetItemInt_List_Fast(PyObject *o, Py_ssize_t i, - int wraparound, int boundscheck); -#define __Pyx_GetItemInt_Tuple(o, i, type, is_signed, to_py_func, is_list, wraparound, boundscheck)\ - (__Pyx_fits_Py_ssize_t(i, type, is_signed) ?\ - __Pyx_GetItemInt_Tuple_Fast(o, (Py_ssize_t)i, wraparound, boundscheck) :\ - (PyErr_SetString(PyExc_IndexError, "tuple index out of range"), (PyObject*)NULL)) -static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Tuple_Fast(PyObject *o, Py_ssize_t i, - int wraparound, int boundscheck); -static PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j); -static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i, - int is_list, int wraparound, int boundscheck); - -/* PyDictVersioning.proto */ -#if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_TYPE_SLOTS -#define __PYX_DICT_VERSION_INIT ((PY_UINT64_T) -1) -#define __PYX_GET_DICT_VERSION(dict) (((PyDictObject*)(dict))->ma_version_tag) -#define __PYX_UPDATE_DICT_CACHE(dict, value, cache_var, version_var)\ - (version_var) = __PYX_GET_DICT_VERSION(dict);\ - (cache_var) = (value); -#define __PYX_PY_DICT_LOOKUP_IF_MODIFIED(VAR, DICT, LOOKUP) {\ - static PY_UINT64_T __pyx_dict_version = 0;\ - static PyObject *__pyx_dict_cached_value = NULL;\ - if (likely(__PYX_GET_DICT_VERSION(DICT) == __pyx_dict_version)) {\ - (VAR) = __pyx_dict_cached_value;\ - } else {\ - (VAR) = __pyx_dict_cached_value = (LOOKUP);\ - __pyx_dict_version = __PYX_GET_DICT_VERSION(DICT);\ - }\ -} -static CYTHON_INLINE PY_UINT64_T __Pyx_get_tp_dict_version(PyObject *obj); -static CYTHON_INLINE PY_UINT64_T __Pyx_get_object_dict_version(PyObject *obj); -static CYTHON_INLINE int __Pyx_object_dict_version_matches(PyObject* obj, PY_UINT64_T tp_dict_version, PY_UINT64_T obj_dict_version); -#else -#define __PYX_GET_DICT_VERSION(dict) (0) -#define __PYX_UPDATE_DICT_CACHE(dict, value, cache_var, version_var) -#define __PYX_PY_DICT_LOOKUP_IF_MODIFIED(VAR, DICT, LOOKUP) (VAR) = (LOOKUP); -#endif - -/* GetModuleGlobalName.proto */ -#if CYTHON_USE_DICT_VERSIONS -#define __Pyx_GetModuleGlobalName(var, name) do {\ - static PY_UINT64_T __pyx_dict_version = 0;\ - static PyObject *__pyx_dict_cached_value = NULL;\ - (var) = (likely(__pyx_dict_version == __PYX_GET_DICT_VERSION(__pyx_d))) ?\ - (likely(__pyx_dict_cached_value) ? __Pyx_NewRef(__pyx_dict_cached_value) : __Pyx_GetBuiltinName(name)) :\ - __Pyx__GetModuleGlobalName(name, &__pyx_dict_version, &__pyx_dict_cached_value);\ -} while(0) -#define __Pyx_GetModuleGlobalNameUncached(var, name) do {\ - PY_UINT64_T __pyx_dict_version;\ - PyObject *__pyx_dict_cached_value;\ - (var) = __Pyx__GetModuleGlobalName(name, &__pyx_dict_version, &__pyx_dict_cached_value);\ -} while(0) -static PyObject *__Pyx__GetModuleGlobalName(PyObject *name, PY_UINT64_T *dict_version, PyObject **dict_cached_value); -#else -#define __Pyx_GetModuleGlobalName(var, name) (var) = __Pyx__GetModuleGlobalName(name) -#define __Pyx_GetModuleGlobalNameUncached(var, name) (var) = __Pyx__GetModuleGlobalName(name) -static CYTHON_INLINE PyObject *__Pyx__GetModuleGlobalName(PyObject *name); -#endif - -/* PyFunctionFastCall.proto */ -#if CYTHON_FAST_PYCALL -#if !CYTHON_VECTORCALL -#define __Pyx_PyFunction_FastCall(func, args, nargs)\ - __Pyx_PyFunction_FastCallDict((func), (args), (nargs), NULL) -static PyObject *__Pyx_PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, PyObject *kwargs); -#endif -#define __Pyx_BUILD_ASSERT_EXPR(cond)\ - (sizeof(char [1 - 2*!(cond)]) - 1) -#ifndef Py_MEMBER_SIZE -#define Py_MEMBER_SIZE(type, member) sizeof(((type *)0)->member) -#endif -#if !CYTHON_VECTORCALL -#if PY_VERSION_HEX >= 0x03080000 - #include "frameobject.h" -#if PY_VERSION_HEX >= 0x030b00a6 && !CYTHON_COMPILING_IN_LIMITED_API - #ifndef Py_BUILD_CORE - #define Py_BUILD_CORE 1 - #endif - #include "internal/pycore_frame.h" -#endif - #define __Pxy_PyFrame_Initialize_Offsets() - #define __Pyx_PyFrame_GetLocalsplus(frame) ((frame)->f_localsplus) -#else - static size_t __pyx_pyframe_localsplus_offset = 0; - #include "frameobject.h" - #define __Pxy_PyFrame_Initialize_Offsets()\ - ((void)__Pyx_BUILD_ASSERT_EXPR(sizeof(PyFrameObject) == offsetof(PyFrameObject, f_localsplus) + Py_MEMBER_SIZE(PyFrameObject, f_localsplus)),\ - (void)(__pyx_pyframe_localsplus_offset = ((size_t)PyFrame_Type.tp_basicsize) - Py_MEMBER_SIZE(PyFrameObject, f_localsplus))) - #define __Pyx_PyFrame_GetLocalsplus(frame)\ - (assert(__pyx_pyframe_localsplus_offset), (PyObject **)(((char *)(frame)) + __pyx_pyframe_localsplus_offset)) -#endif -#endif -#endif - -/* PyObjectCall.proto */ -#if CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw); -#else -#define __Pyx_PyObject_Call(func, arg, kw) PyObject_Call(func, arg, kw) -#endif - -/* PyObjectCallMethO.proto */ -#if CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE PyObject* __Pyx_PyObject_CallMethO(PyObject *func, PyObject *arg); -#endif - -/* PyObjectFastCall.proto */ -#define __Pyx_PyObject_FastCall(func, args, nargs) __Pyx_PyObject_FastCallDict(func, args, (size_t)(nargs), NULL) -static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObject **args, size_t nargs, PyObject *kwargs); - -/* TupleAndListFromArray.proto */ -#if CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE PyObject* __Pyx_PyList_FromArray(PyObject *const *src, Py_ssize_t n); -static CYTHON_INLINE PyObject* __Pyx_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n); -#endif - -/* IncludeStringH.proto */ -#include - -/* BytesEquals.proto */ -static CYTHON_INLINE int __Pyx_PyBytes_Equals(PyObject* s1, PyObject* s2, int equals); - -/* UnicodeEquals.proto */ -static CYTHON_INLINE int __Pyx_PyUnicode_Equals(PyObject* s1, PyObject* s2, int equals); - -/* fastcall.proto */ -#if CYTHON_AVOID_BORROWED_REFS - #define __Pyx_Arg_VARARGS(args, i) PySequence_GetItem(args, i) -#elif CYTHON_ASSUME_SAFE_MACROS - #define __Pyx_Arg_VARARGS(args, i) PyTuple_GET_ITEM(args, i) -#else - #define __Pyx_Arg_VARARGS(args, i) PyTuple_GetItem(args, i) -#endif -#if CYTHON_AVOID_BORROWED_REFS - #define __Pyx_Arg_NewRef_VARARGS(arg) __Pyx_NewRef(arg) - #define __Pyx_Arg_XDECREF_VARARGS(arg) Py_XDECREF(arg) -#else - #define __Pyx_Arg_NewRef_VARARGS(arg) arg - #define __Pyx_Arg_XDECREF_VARARGS(arg) -#endif -#define __Pyx_NumKwargs_VARARGS(kwds) PyDict_Size(kwds) -#define __Pyx_KwValues_VARARGS(args, nargs) NULL -#define __Pyx_GetKwValue_VARARGS(kw, kwvalues, s) __Pyx_PyDict_GetItemStrWithError(kw, s) -#define __Pyx_KwargsAsDict_VARARGS(kw, kwvalues) PyDict_Copy(kw) -#if CYTHON_METH_FASTCALL - #define __Pyx_Arg_FASTCALL(args, i) args[i] - #define __Pyx_NumKwargs_FASTCALL(kwds) PyTuple_GET_SIZE(kwds) - #define __Pyx_KwValues_FASTCALL(args, nargs) ((args) + (nargs)) - static CYTHON_INLINE PyObject * __Pyx_GetKwValue_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues, PyObject *s); -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000 - CYTHON_UNUSED static PyObject *__Pyx_KwargsAsDict_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues); - #else - #define __Pyx_KwargsAsDict_FASTCALL(kw, kwvalues) _PyStack_AsDict(kwvalues, kw) - #endif - #define __Pyx_Arg_NewRef_FASTCALL(arg) arg /* no-op, __Pyx_Arg_FASTCALL is direct and this needs - to have the same reference counting */ - #define __Pyx_Arg_XDECREF_FASTCALL(arg) -#else - #define __Pyx_Arg_FASTCALL __Pyx_Arg_VARARGS - #define __Pyx_NumKwargs_FASTCALL __Pyx_NumKwargs_VARARGS - #define __Pyx_KwValues_FASTCALL __Pyx_KwValues_VARARGS - #define __Pyx_GetKwValue_FASTCALL __Pyx_GetKwValue_VARARGS - #define __Pyx_KwargsAsDict_FASTCALL __Pyx_KwargsAsDict_VARARGS - #define __Pyx_Arg_NewRef_FASTCALL(arg) __Pyx_Arg_NewRef_VARARGS(arg) - #define __Pyx_Arg_XDECREF_FASTCALL(arg) __Pyx_Arg_XDECREF_VARARGS(arg) -#endif -#if CYTHON_COMPILING_IN_CPYTHON && CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS -#define __Pyx_ArgsSlice_VARARGS(args, start, stop) __Pyx_PyTuple_FromArray(&__Pyx_Arg_VARARGS(args, start), stop - start) -#define __Pyx_ArgsSlice_FASTCALL(args, start, stop) __Pyx_PyTuple_FromArray(&__Pyx_Arg_FASTCALL(args, start), stop - start) -#else -#define __Pyx_ArgsSlice_VARARGS(args, start, stop) PyTuple_GetSlice(args, start, stop) -#define __Pyx_ArgsSlice_FASTCALL(args, start, stop) PyTuple_GetSlice(args, start, stop) -#endif - -/* RaiseArgTupleInvalid.proto */ -static void __Pyx_RaiseArgtupleInvalid(const char* func_name, int exact, - Py_ssize_t num_min, Py_ssize_t num_max, Py_ssize_t num_found); - -/* RaiseDoubleKeywords.proto */ -static void __Pyx_RaiseDoubleKeywordsError(const char* func_name, PyObject* kw_name); - -/* ParseKeywords.proto */ -static int __Pyx_ParseOptionalKeywords(PyObject *kwds, PyObject *const *kwvalues, - PyObject **argnames[], - PyObject *kwds2, PyObject *values[], Py_ssize_t num_pos_args, - const char* function_name); - -/* GetException.proto */ -#if CYTHON_FAST_THREAD_STATE -#define __Pyx_GetException(type, value, tb) __Pyx__GetException(__pyx_tstate, type, value, tb) -static int __Pyx__GetException(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb); -#else -static int __Pyx_GetException(PyObject **type, PyObject **value, PyObject **tb); -#endif - -/* pep479.proto */ -static void __Pyx_Generator_Replace_StopIteration(int in_async_gen); - -/* GetTopmostException.proto */ -#if CYTHON_USE_EXC_INFO_STACK && CYTHON_FAST_THREAD_STATE -static _PyErr_StackItem * __Pyx_PyErr_GetTopmostException(PyThreadState *tstate); -#endif - -/* SaveResetException.proto */ -#if CYTHON_FAST_THREAD_STATE -#define __Pyx_ExceptionSave(type, value, tb) __Pyx__ExceptionSave(__pyx_tstate, type, value, tb) -static CYTHON_INLINE void __Pyx__ExceptionSave(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb); -#define __Pyx_ExceptionReset(type, value, tb) __Pyx__ExceptionReset(__pyx_tstate, type, value, tb) -static CYTHON_INLINE void __Pyx__ExceptionReset(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb); -#else -#define __Pyx_ExceptionSave(type, value, tb) PyErr_GetExcInfo(type, value, tb) -#define __Pyx_ExceptionReset(type, value, tb) PyErr_SetExcInfo(type, value, tb) -#endif - -/* IterNext.proto */ -#define __Pyx_PyIter_Next(obj) __Pyx_PyIter_Next2(obj, NULL) -static CYTHON_INLINE PyObject *__Pyx_PyIter_Next2(PyObject *, PyObject *); - -/* ListAppend.proto */ -#if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS -static CYTHON_INLINE int __Pyx_PyList_Append(PyObject* list, PyObject* x) { - PyListObject* L = (PyListObject*) list; - Py_ssize_t len = Py_SIZE(list); - if (likely(L->allocated > len) & likely(len > (L->allocated >> 1))) { - Py_INCREF(x); - #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000 - L->ob_item[len] = x; - #else - PyList_SET_ITEM(list, len, x); - #endif - __Pyx_SET_SIZE(list, len + 1); - return 0; - } - return PyList_Append(list, x); -} -#else -#define __Pyx_PyList_Append(L,x) PyList_Append(L,x) -#endif - -/* ListCompAppend.proto */ -#if CYTHON_USE_PYLIST_INTERNALS && CYTHON_ASSUME_SAFE_MACROS -static CYTHON_INLINE int __Pyx_ListComp_Append(PyObject* list, PyObject* x) { - PyListObject* L = (PyListObject*) list; - Py_ssize_t len = Py_SIZE(list); - if (likely(L->allocated > len)) { - Py_INCREF(x); - #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000 - L->ob_item[len] = x; - #else - PyList_SET_ITEM(list, len, x); - #endif - __Pyx_SET_SIZE(list, len + 1); - return 0; - } - return PyList_Append(list, x); -} -#else -#define __Pyx_ListComp_Append(L,x) PyList_Append(L,x) -#endif - -/* PyIntBinop.proto */ -#if !CYTHON_COMPILING_IN_PYPY -static PyObject* __Pyx_PyInt_AddObjC(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check); -#else -#define __Pyx_PyInt_AddObjC(op1, op2, intval, inplace, zerodivision_check)\ - (inplace ? PyNumber_InPlaceAdd(op1, op2) : PyNumber_Add(op1, op2)) -#endif - -/* RaiseException.proto */ -static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, PyObject *cause); - -/* AssertionsEnabled.proto */ -#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX < 0x02070600 && !defined(Py_OptimizeFlag) - #define __Pyx_init_assertions_enabled() (0) - #define __pyx_assertions_enabled() (1) -#elif CYTHON_COMPILING_IN_LIMITED_API || (CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030C0000) - static int __pyx_assertions_enabled_flag; - #define __pyx_assertions_enabled() (__pyx_assertions_enabled_flag) - static int __Pyx_init_assertions_enabled(void) { - PyObject *builtins, *debug, *debug_str; - int flag; - builtins = PyEval_GetBuiltins(); - if (!builtins) goto bad; - debug_str = PyUnicode_FromStringAndSize("__debug__", 9); - if (!debug_str) goto bad; - debug = PyObject_GetItem(builtins, debug_str); - Py_DECREF(debug_str); - if (!debug) goto bad; - flag = PyObject_IsTrue(debug); - Py_DECREF(debug); - if (flag == -1) goto bad; - __pyx_assertions_enabled_flag = flag; - return 0; - bad: - __pyx_assertions_enabled_flag = 1; - return -1; - } -#else - #define __Pyx_init_assertions_enabled() (0) - #define __pyx_assertions_enabled() (!Py_OptimizeFlag) -#endif - -/* SetItemInt.proto */ -#define __Pyx_SetItemInt(o, i, v, type, is_signed, to_py_func, is_list, wraparound, boundscheck)\ - (__Pyx_fits_Py_ssize_t(i, type, is_signed) ?\ - __Pyx_SetItemInt_Fast(o, (Py_ssize_t)i, v, is_list, wraparound, boundscheck) :\ - (is_list ? (PyErr_SetString(PyExc_IndexError, "list assignment index out of range"), -1) :\ - __Pyx_SetItemInt_Generic(o, to_py_func(i), v))) -static int __Pyx_SetItemInt_Generic(PyObject *o, PyObject *j, PyObject *v); -static CYTHON_INLINE int __Pyx_SetItemInt_Fast(PyObject *o, Py_ssize_t i, PyObject *v, - int is_list, int wraparound, int boundscheck); - -/* ModInt[long].proto */ -static CYTHON_INLINE long __Pyx_mod_long(long, long); - -/* IncludeStructmemberH.proto */ -#include - -/* FixUpExtensionType.proto */ -#if CYTHON_USE_TYPE_SPECS -static int __Pyx_fix_up_extension_type_from_spec(PyType_Spec *spec, PyTypeObject *type); -#endif - -/* PyObjectCallNoArg.proto */ -static CYTHON_INLINE PyObject* __Pyx_PyObject_CallNoArg(PyObject *func); - -/* PyObjectCallOneArg.proto */ -static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg); - -/* PyObjectGetMethod.proto */ -static int __Pyx_PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method); - -/* PyObjectCallMethod0.proto */ -static PyObject* __Pyx_PyObject_CallMethod0(PyObject* obj, PyObject* method_name); - -/* ValidateBasesTuple.proto */ -#if CYTHON_COMPILING_IN_CPYTHON || CYTHON_COMPILING_IN_LIMITED_API || CYTHON_USE_TYPE_SPECS -static int __Pyx_validate_bases_tuple(const char *type_name, Py_ssize_t dictoffset, PyObject *bases); -#endif - -/* PyType_Ready.proto */ -CYTHON_UNUSED static int __Pyx_PyType_Ready(PyTypeObject *t); - -/* PyObject_GenericGetAttrNoDict.proto */ -#if CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP && PY_VERSION_HEX < 0x03070000 -static CYTHON_INLINE PyObject* __Pyx_PyObject_GenericGetAttrNoDict(PyObject* obj, PyObject* attr_name); -#else -#define __Pyx_PyObject_GenericGetAttrNoDict PyObject_GenericGetAttr -#endif - -/* FastTypeChecks.proto */ -#if CYTHON_COMPILING_IN_CPYTHON -#define __Pyx_TypeCheck(obj, type) __Pyx_IsSubtype(Py_TYPE(obj), (PyTypeObject *)type) -#define __Pyx_TypeCheck2(obj, type1, type2) __Pyx_IsAnySubtype2(Py_TYPE(obj), (PyTypeObject *)type1, (PyTypeObject *)type2) -static CYTHON_INLINE int __Pyx_IsSubtype(PyTypeObject *a, PyTypeObject *b); -static CYTHON_INLINE int __Pyx_IsAnySubtype2(PyTypeObject *cls, PyTypeObject *a, PyTypeObject *b); -static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches(PyObject *err, PyObject *type); -static CYTHON_INLINE int __Pyx_PyErr_GivenExceptionMatches2(PyObject *err, PyObject *type1, PyObject *type2); -#else -#define __Pyx_TypeCheck(obj, type) PyObject_TypeCheck(obj, (PyTypeObject *)type) -#define __Pyx_TypeCheck2(obj, type1, type2) (PyObject_TypeCheck(obj, (PyTypeObject *)type1) || PyObject_TypeCheck(obj, (PyTypeObject *)type2)) -#define __Pyx_PyErr_GivenExceptionMatches(err, type) PyErr_GivenExceptionMatches(err, type) -#define __Pyx_PyErr_GivenExceptionMatches2(err, type1, type2) (PyErr_GivenExceptionMatches(err, type1) || PyErr_GivenExceptionMatches(err, type2)) -#endif -#define __Pyx_PyErr_ExceptionMatches2(err1, err2) __Pyx_PyErr_GivenExceptionMatches2(__Pyx_PyErr_CurrentExceptionType(), err1, err2) -#define __Pyx_PyException_Check(obj) __Pyx_TypeCheck(obj, PyExc_Exception) - -/* Import.proto */ -static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, int level); - -/* ImportFrom.proto */ -static PyObject* __Pyx_ImportFrom(PyObject* module, PyObject* name); - -/* ImportDottedModule.proto */ -static PyObject *__Pyx_ImportDottedModule(PyObject *name, PyObject *parts_tuple); -#if PY_MAJOR_VERSION >= 3 -static PyObject *__Pyx_ImportDottedModule_WalkParts(PyObject *module, PyObject *name, PyObject *parts_tuple); -#endif - -/* pybytes_as_double.proto */ -static double __Pyx_SlowPyString_AsDouble(PyObject *obj); -static double __Pyx__PyBytes_AsDouble(PyObject *obj, const char* start, Py_ssize_t length); -static CYTHON_INLINE double __Pyx_PyBytes_AsDouble(PyObject *obj) { - char* as_c_string; - Py_ssize_t size; -#if CYTHON_ASSUME_SAFE_MACROS - as_c_string = PyBytes_AS_STRING(obj); - size = PyBytes_GET_SIZE(obj); -#else - if (PyBytes_AsStringAndSize(obj, &as_c_string, &size) < 0) { - return (double)-1; - } -#endif - return __Pyx__PyBytes_AsDouble(obj, as_c_string, size); -} -static CYTHON_INLINE double __Pyx_PyByteArray_AsDouble(PyObject *obj) { - char* as_c_string; - Py_ssize_t size; -#if CYTHON_ASSUME_SAFE_MACROS - as_c_string = PyByteArray_AS_STRING(obj); - size = PyByteArray_GET_SIZE(obj); -#else - as_c_string = PyByteArray_AsString(obj); - if (as_c_string == NULL) { - return (double)-1; - } - size = PyByteArray_Size(obj); -#endif - return __Pyx__PyBytes_AsDouble(obj, as_c_string, size); -} - -/* pyunicode_as_double.proto */ -#if PY_MAJOR_VERSION >= 3 && !CYTHON_COMPILING_IN_PYPY && CYTHON_ASSUME_SAFE_MACROS -static const char* __Pyx__PyUnicode_AsDouble_Copy(const void* data, const int kind, char* buffer, Py_ssize_t start, Py_ssize_t end) { - int last_was_punctuation; - Py_ssize_t i; - last_was_punctuation = 1; - for (i=start; i <= end; i++) { - Py_UCS4 chr = PyUnicode_READ(kind, data, i); - int is_punctuation = (chr == '_') | (chr == '.'); - *buffer = (char)chr; - buffer += (chr != '_'); - if (unlikely(chr > 127)) goto parse_failure; - if (unlikely(last_was_punctuation & is_punctuation)) goto parse_failure; - last_was_punctuation = is_punctuation; - } - if (unlikely(last_was_punctuation)) goto parse_failure; - *buffer = '\0'; - return buffer; -parse_failure: - return NULL; -} -static double __Pyx__PyUnicode_AsDouble_inf_nan(const void* data, int kind, Py_ssize_t start, Py_ssize_t length) { - int matches = 1; - Py_UCS4 chr; - Py_UCS4 sign = PyUnicode_READ(kind, data, start); - int is_signed = (sign == '-') | (sign == '+'); - start += is_signed; - length -= is_signed; - switch (PyUnicode_READ(kind, data, start)) { - #ifdef Py_NAN - case 'n': - case 'N': - if (unlikely(length != 3)) goto parse_failure; - chr = PyUnicode_READ(kind, data, start+1); - matches &= (chr == 'a') | (chr == 'A'); - chr = PyUnicode_READ(kind, data, start+2); - matches &= (chr == 'n') | (chr == 'N'); - if (unlikely(!matches)) goto parse_failure; - return (sign == '-') ? -Py_NAN : Py_NAN; - #endif - case 'i': - case 'I': - if (unlikely(length < 3)) goto parse_failure; - chr = PyUnicode_READ(kind, data, start+1); - matches &= (chr == 'n') | (chr == 'N'); - chr = PyUnicode_READ(kind, data, start+2); - matches &= (chr == 'f') | (chr == 'F'); - if (likely(length == 3 && matches)) - return (sign == '-') ? -Py_HUGE_VAL : Py_HUGE_VAL; - if (unlikely(length != 8)) goto parse_failure; - chr = PyUnicode_READ(kind, data, start+3); - matches &= (chr == 'i') | (chr == 'I'); - chr = PyUnicode_READ(kind, data, start+4); - matches &= (chr == 'n') | (chr == 'N'); - chr = PyUnicode_READ(kind, data, start+5); - matches &= (chr == 'i') | (chr == 'I'); - chr = PyUnicode_READ(kind, data, start+6); - matches &= (chr == 't') | (chr == 'T'); - chr = PyUnicode_READ(kind, data, start+7); - matches &= (chr == 'y') | (chr == 'Y'); - if (unlikely(!matches)) goto parse_failure; - return (sign == '-') ? -Py_HUGE_VAL : Py_HUGE_VAL; - case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - break; - default: - goto parse_failure; - } - return 0.0; -parse_failure: - return -1.0; -} -static double __Pyx_PyUnicode_AsDouble_WithSpaces(PyObject *obj) { - double value; - const char *last; - char *end; - Py_ssize_t start, length = PyUnicode_GET_LENGTH(obj); - const int kind = PyUnicode_KIND(obj); - const void* data = PyUnicode_DATA(obj); - start = 0; - while (Py_UNICODE_ISSPACE(PyUnicode_READ(kind, data, start))) - start++; - while (start < length - 1 && Py_UNICODE_ISSPACE(PyUnicode_READ(kind, data, length - 1))) - length--; - length -= start; - if (unlikely(length <= 0)) goto fallback; - value = __Pyx__PyUnicode_AsDouble_inf_nan(data, kind, start, length); - if (unlikely(value == -1.0)) goto fallback; - if (value != 0.0) return value; - if (length < 40) { - char number[40]; - last = __Pyx__PyUnicode_AsDouble_Copy(data, kind, number, start, start + length); - if (unlikely(!last)) goto fallback; - value = PyOS_string_to_double(number, &end, NULL); - } else { - char *number = (char*) PyMem_Malloc((length + 1) * sizeof(char)); - if (unlikely(!number)) goto fallback; - last = __Pyx__PyUnicode_AsDouble_Copy(data, kind, number, start, start + length); - if (unlikely(!last)) { - PyMem_Free(number); - goto fallback; - } - value = PyOS_string_to_double(number, &end, NULL); - PyMem_Free(number); - } - if (likely(end == last) || (value == (double)-1 && PyErr_Occurred())) { - return value; - } -fallback: - return __Pyx_SlowPyString_AsDouble(obj); -} -#endif -static CYTHON_INLINE double __Pyx_PyUnicode_AsDouble(PyObject *obj) { -#if PY_MAJOR_VERSION >= 3 && !CYTHON_COMPILING_IN_PYPY && CYTHON_ASSUME_SAFE_MACROS - if (unlikely(__Pyx_PyUnicode_READY(obj) == -1)) - return (double)-1; - if (likely(PyUnicode_IS_ASCII(obj))) { - const char *s; - Py_ssize_t length; - s = PyUnicode_AsUTF8AndSize(obj, &length); - return __Pyx__PyBytes_AsDouble(obj, s, length); - } - return __Pyx_PyUnicode_AsDouble_WithSpaces(obj); -#else - return __Pyx_SlowPyString_AsDouble(obj); -#endif -} - -/* FetchSharedCythonModule.proto */ -static PyObject *__Pyx_FetchSharedCythonABIModule(void); - -/* FetchCommonType.proto */ -#if !CYTHON_USE_TYPE_SPECS -static PyTypeObject* __Pyx_FetchCommonType(PyTypeObject* type); -#else -static PyTypeObject* __Pyx_FetchCommonTypeFromSpec(PyObject *module, PyType_Spec *spec, PyObject *bases); -#endif - -/* PyMethodNew.proto */ -#if CYTHON_COMPILING_IN_LIMITED_API -static PyObject *__Pyx_PyMethod_New(PyObject *func, PyObject *self, PyObject *typ) { - PyObject *typesModule=NULL, *methodType=NULL, *result=NULL; - CYTHON_UNUSED_VAR(typ); - if (!self) - return __Pyx_NewRef(func); - typesModule = PyImport_ImportModule("types"); - if (!typesModule) return NULL; - methodType = PyObject_GetAttrString(typesModule, "MethodType"); - Py_DECREF(typesModule); - if (!methodType) return NULL; - result = PyObject_CallFunctionObjArgs(methodType, func, self, NULL); - Py_DECREF(methodType); - return result; -} -#elif PY_MAJOR_VERSION >= 3 -static PyObject *__Pyx_PyMethod_New(PyObject *func, PyObject *self, PyObject *typ) { - CYTHON_UNUSED_VAR(typ); - if (!self) - return __Pyx_NewRef(func); - return PyMethod_New(func, self); -} -#else - #define __Pyx_PyMethod_New PyMethod_New -#endif - -/* PyVectorcallFastCallDict.proto */ -#if CYTHON_METH_FASTCALL -static CYTHON_INLINE PyObject *__Pyx_PyVectorcall_FastCallDict(PyObject *func, __pyx_vectorcallfunc vc, PyObject *const *args, size_t nargs, PyObject *kw); -#endif - -/* CythonFunctionShared.proto */ -#define __Pyx_CyFunction_USED -#define __Pyx_CYFUNCTION_STATICMETHOD 0x01 -#define __Pyx_CYFUNCTION_CLASSMETHOD 0x02 -#define __Pyx_CYFUNCTION_CCLASS 0x04 -#define __Pyx_CYFUNCTION_COROUTINE 0x08 -#define __Pyx_CyFunction_GetClosure(f)\ - (((__pyx_CyFunctionObject *) (f))->func_closure) -#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API - #define __Pyx_CyFunction_GetClassObj(f)\ - (((__pyx_CyFunctionObject *) (f))->func_classobj) -#else - #define __Pyx_CyFunction_GetClassObj(f)\ - ((PyObject*) ((PyCMethodObject *) (f))->mm_class) -#endif -#define __Pyx_CyFunction_SetClassObj(f, classobj)\ - __Pyx__CyFunction_SetClassObj((__pyx_CyFunctionObject *) (f), (classobj)) -#define __Pyx_CyFunction_Defaults(type, f)\ - ((type *)(((__pyx_CyFunctionObject *) (f))->defaults)) -#define __Pyx_CyFunction_SetDefaultsGetter(f, g)\ - ((__pyx_CyFunctionObject *) (f))->defaults_getter = (g) -typedef struct { -#if CYTHON_COMPILING_IN_LIMITED_API - PyObject_HEAD - PyObject *func; -#elif PY_VERSION_HEX < 0x030900B1 - PyCFunctionObject func; -#else - PyCMethodObject func; -#endif -#if CYTHON_BACKPORT_VECTORCALL - __pyx_vectorcallfunc func_vectorcall; -#endif -#if PY_VERSION_HEX < 0x030500A0 || CYTHON_COMPILING_IN_LIMITED_API - PyObject *func_weakreflist; -#endif - PyObject *func_dict; - PyObject *func_name; - PyObject *func_qualname; - PyObject *func_doc; - PyObject *func_globals; - PyObject *func_code; - PyObject *func_closure; -#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API - PyObject *func_classobj; -#endif - void *defaults; - int defaults_pyobjects; - size_t defaults_size; - int flags; - PyObject *defaults_tuple; - PyObject *defaults_kwdict; - PyObject *(*defaults_getter)(PyObject *); - PyObject *func_annotations; - PyObject *func_is_coroutine; -} __pyx_CyFunctionObject; -#undef __Pyx_CyOrPyCFunction_Check -#define __Pyx_CyFunction_Check(obj) __Pyx_TypeCheck(obj, __pyx_CyFunctionType) -#define __Pyx_CyOrPyCFunction_Check(obj) __Pyx_TypeCheck2(obj, __pyx_CyFunctionType, &PyCFunction_Type) -#define __Pyx_CyFunction_CheckExact(obj) __Pyx_IS_TYPE(obj, __pyx_CyFunctionType) -static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void *cfunc); -#undef __Pyx_IsSameCFunction -#define __Pyx_IsSameCFunction(func, cfunc) __Pyx__IsSameCyOrCFunction(func, cfunc) -static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject* op, PyMethodDef *ml, - int flags, PyObject* qualname, - PyObject *closure, - PyObject *module, PyObject *globals, - PyObject* code); -static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* f, PyObject* classobj); -static CYTHON_INLINE void *__Pyx_CyFunction_InitDefaults(PyObject *m, - size_t size, - int pyobjects); -static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsTuple(PyObject *m, - PyObject *tuple); -static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsKwDict(PyObject *m, - PyObject *dict); -static CYTHON_INLINE void __Pyx_CyFunction_SetAnnotationsDict(PyObject *m, - PyObject *dict); -static int __pyx_CyFunction_init(PyObject *module); -#if CYTHON_METH_FASTCALL -static PyObject * __Pyx_CyFunction_Vectorcall_NOARGS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); -static PyObject * __Pyx_CyFunction_Vectorcall_O(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); -static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); -static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); -#if CYTHON_BACKPORT_VECTORCALL -#define __Pyx_CyFunction_func_vectorcall(f) (((__pyx_CyFunctionObject*)f)->func_vectorcall) -#else -#define __Pyx_CyFunction_func_vectorcall(f) (((PyCFunctionObject*)f)->vectorcall) -#endif -#endif - -/* CythonFunction.proto */ -static PyObject *__Pyx_CyFunction_New(PyMethodDef *ml, - int flags, PyObject* qualname, - PyObject *closure, - PyObject *module, PyObject *globals, - PyObject* code); - -/* CLineInTraceback.proto */ -#ifdef CYTHON_CLINE_IN_TRACEBACK -#define __Pyx_CLineForTraceback(tstate, c_line) (((CYTHON_CLINE_IN_TRACEBACK)) ? c_line : 0) -#else -static int __Pyx_CLineForTraceback(PyThreadState *tstate, int c_line); -#endif - -/* CodeObjectCache.proto */ -#if !CYTHON_COMPILING_IN_LIMITED_API -typedef struct { - PyCodeObject* code_object; - int code_line; -} __Pyx_CodeObjectCacheEntry; -struct __Pyx_CodeObjectCache { - int count; - int max_count; - __Pyx_CodeObjectCacheEntry* entries; -}; -static struct __Pyx_CodeObjectCache __pyx_code_cache = {0,0,NULL}; -static int __pyx_bisect_code_objects(__Pyx_CodeObjectCacheEntry* entries, int count, int code_line); -static PyCodeObject *__pyx_find_code_object(int code_line); -static void __pyx_insert_code_object(int code_line, PyCodeObject* code_object); -#endif - -/* AddTraceback.proto */ -static void __Pyx_AddTraceback(const char *funcname, int c_line, - int py_line, const char *filename); - -/* RealImag.proto */ -#if CYTHON_CCOMPLEX - #ifdef __cplusplus - #define __Pyx_CREAL(z) ((z).real()) - #define __Pyx_CIMAG(z) ((z).imag()) - #else - #define __Pyx_CREAL(z) (__real__(z)) - #define __Pyx_CIMAG(z) (__imag__(z)) - #endif -#else - #define __Pyx_CREAL(z) ((z).real) - #define __Pyx_CIMAG(z) ((z).imag) -#endif -#if defined(__cplusplus) && CYTHON_CCOMPLEX\ - && (defined(_WIN32) || defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 5 || __GNUC__ == 4 && __GNUC_MINOR__ >= 4 )) || __cplusplus >= 201103) - #define __Pyx_SET_CREAL(z,x) ((z).real(x)) - #define __Pyx_SET_CIMAG(z,y) ((z).imag(y)) -#else - #define __Pyx_SET_CREAL(z,x) __Pyx_CREAL(z) = (x) - #define __Pyx_SET_CIMAG(z,y) __Pyx_CIMAG(z) = (y) -#endif - -/* Arithmetic.proto */ -#if CYTHON_CCOMPLEX && (1) && (!0 || __cplusplus) - #define __Pyx_c_eq_double(a, b) ((a)==(b)) - #define __Pyx_c_sum_double(a, b) ((a)+(b)) - #define __Pyx_c_diff_double(a, b) ((a)-(b)) - #define __Pyx_c_prod_double(a, b) ((a)*(b)) - #define __Pyx_c_quot_double(a, b) ((a)/(b)) - #define __Pyx_c_neg_double(a) (-(a)) - #ifdef __cplusplus - #define __Pyx_c_is_zero_double(z) ((z)==(double)0) - #define __Pyx_c_conj_double(z) (::std::conj(z)) - #if 1 - #define __Pyx_c_abs_double(z) (::std::abs(z)) - #define __Pyx_c_pow_double(a, b) (::std::pow(a, b)) - #endif - #else - #define __Pyx_c_is_zero_double(z) ((z)==0) - #define __Pyx_c_conj_double(z) (conj(z)) - #if 1 - #define __Pyx_c_abs_double(z) (cabs(z)) - #define __Pyx_c_pow_double(a, b) (cpow(a, b)) - #endif - #endif -#else - static CYTHON_INLINE int __Pyx_c_eq_double(__pyx_t_double_complex, __pyx_t_double_complex); - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_sum_double(__pyx_t_double_complex, __pyx_t_double_complex); - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_diff_double(__pyx_t_double_complex, __pyx_t_double_complex); - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_prod_double(__pyx_t_double_complex, __pyx_t_double_complex); - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_quot_double(__pyx_t_double_complex, __pyx_t_double_complex); - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_neg_double(__pyx_t_double_complex); - static CYTHON_INLINE int __Pyx_c_is_zero_double(__pyx_t_double_complex); - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_conj_double(__pyx_t_double_complex); - #if 1 - static CYTHON_INLINE double __Pyx_c_abs_double(__pyx_t_double_complex); - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_pow_double(__pyx_t_double_complex, __pyx_t_double_complex); - #endif -#endif - -/* FromPy.proto */ -static __pyx_t_double_complex __Pyx_PyComplex_As___pyx_t_double_complex(PyObject*); - -/* GCCDiagnostics.proto */ -#if !defined(__INTEL_COMPILER) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) -#define __Pyx_HAS_GCC_DIAGNOSTIC -#endif - -/* ToPy.proto */ -#define __pyx_PyComplex_FromComplex(z)\ - PyComplex_FromDoubles((double)__Pyx_CREAL(z),\ - (double)__Pyx_CIMAG(z)) - -/* CIntFromPy.proto */ -static CYTHON_INLINE int __Pyx_PyInt_As_int(PyObject *); - -/* CIntToPy.proto */ -static CYTHON_INLINE PyObject* __Pyx_PyInt_From_long(long value); - -/* CIntToPy.proto */ -static CYTHON_INLINE PyObject* __Pyx_PyInt_From_int(int value); - -/* CIntFromPy.proto */ -static CYTHON_INLINE long __Pyx_PyInt_As_long(PyObject *); - -/* FormatTypeName.proto */ -#if CYTHON_COMPILING_IN_LIMITED_API -typedef PyObject *__Pyx_TypeName; -#define __Pyx_FMT_TYPENAME "%U" -static __Pyx_TypeName __Pyx_PyType_GetName(PyTypeObject* tp); -#define __Pyx_DECREF_TypeName(obj) Py_XDECREF(obj) -#else -typedef const char *__Pyx_TypeName; -#define __Pyx_FMT_TYPENAME "%.200s" -#define __Pyx_PyType_GetName(tp) ((tp)->tp_name) -#define __Pyx_DECREF_TypeName(obj) -#endif - -/* SwapException.proto */ -#if CYTHON_FAST_THREAD_STATE -#define __Pyx_ExceptionSwap(type, value, tb) __Pyx__ExceptionSwap(__pyx_tstate, type, value, tb) -static CYTHON_INLINE void __Pyx__ExceptionSwap(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb); -#else -static CYTHON_INLINE void __Pyx_ExceptionSwap(PyObject **type, PyObject **value, PyObject **tb); -#endif - -/* PyObjectCall2Args.proto */ -static CYTHON_INLINE PyObject* __Pyx_PyObject_Call2Args(PyObject* function, PyObject* arg1, PyObject* arg2); - -/* PyObjectCallMethod1.proto */ -static PyObject* __Pyx_PyObject_CallMethod1(PyObject* obj, PyObject* method_name, PyObject* arg); - -/* CoroutineBase.proto */ -struct __pyx_CoroutineObject; -typedef PyObject *(*__pyx_coroutine_body_t)(struct __pyx_CoroutineObject *, PyThreadState *, PyObject *); -#if CYTHON_USE_EXC_INFO_STACK -#define __Pyx_ExcInfoStruct _PyErr_StackItem -#else -typedef struct { - PyObject *exc_type; - PyObject *exc_value; - PyObject *exc_traceback; -} __Pyx_ExcInfoStruct; -#endif -typedef struct __pyx_CoroutineObject { - PyObject_HEAD - __pyx_coroutine_body_t body; - PyObject *closure; - __Pyx_ExcInfoStruct gi_exc_state; - PyObject *gi_weakreflist; - PyObject *classobj; - PyObject *yieldfrom; - PyObject *gi_name; - PyObject *gi_qualname; - PyObject *gi_modulename; - PyObject *gi_code; - PyObject *gi_frame; - int resume_label; - char is_running; -} __pyx_CoroutineObject; -static __pyx_CoroutineObject *__Pyx__Coroutine_New( - PyTypeObject *type, __pyx_coroutine_body_t body, PyObject *code, PyObject *closure, - PyObject *name, PyObject *qualname, PyObject *module_name); -static __pyx_CoroutineObject *__Pyx__Coroutine_NewInit( - __pyx_CoroutineObject *gen, __pyx_coroutine_body_t body, PyObject *code, PyObject *closure, - PyObject *name, PyObject *qualname, PyObject *module_name); -static CYTHON_INLINE void __Pyx_Coroutine_ExceptionClear(__Pyx_ExcInfoStruct *self); -static int __Pyx_Coroutine_clear(PyObject *self); -static PyObject *__Pyx_Coroutine_Send(PyObject *self, PyObject *value); -static PyObject *__Pyx_Coroutine_Close(PyObject *self); -static PyObject *__Pyx_Coroutine_Throw(PyObject *gen, PyObject *args); -#if CYTHON_USE_EXC_INFO_STACK -#define __Pyx_Coroutine_SwapException(self) -#define __Pyx_Coroutine_ResetAndClearException(self) __Pyx_Coroutine_ExceptionClear(&(self)->gi_exc_state) -#else -#define __Pyx_Coroutine_SwapException(self) {\ - __Pyx_ExceptionSwap(&(self)->gi_exc_state.exc_type, &(self)->gi_exc_state.exc_value, &(self)->gi_exc_state.exc_traceback);\ - __Pyx_Coroutine_ResetFrameBackpointer(&(self)->gi_exc_state);\ - } -#define __Pyx_Coroutine_ResetAndClearException(self) {\ - __Pyx_ExceptionReset((self)->gi_exc_state.exc_type, (self)->gi_exc_state.exc_value, (self)->gi_exc_state.exc_traceback);\ - (self)->gi_exc_state.exc_type = (self)->gi_exc_state.exc_value = (self)->gi_exc_state.exc_traceback = NULL;\ - } -#endif -#if CYTHON_FAST_THREAD_STATE -#define __Pyx_PyGen_FetchStopIterationValue(pvalue)\ - __Pyx_PyGen__FetchStopIterationValue(__pyx_tstate, pvalue) -#else -#define __Pyx_PyGen_FetchStopIterationValue(pvalue)\ - __Pyx_PyGen__FetchStopIterationValue(__Pyx_PyThreadState_Current, pvalue) -#endif -static int __Pyx_PyGen__FetchStopIterationValue(PyThreadState *tstate, PyObject **pvalue); -static CYTHON_INLINE void __Pyx_Coroutine_ResetFrameBackpointer(__Pyx_ExcInfoStruct *exc_state); - -/* PatchModuleWithCoroutine.proto */ -static PyObject* __Pyx_Coroutine_patch_module(PyObject* module, const char* py_code); - -/* PatchGeneratorABC.proto */ -static int __Pyx_patch_abc(void); - -/* Generator.proto */ -#define __Pyx_Generator_USED -#define __Pyx_Generator_CheckExact(obj) __Pyx_IS_TYPE(obj, __pyx_GeneratorType) -#define __Pyx_Generator_New(body, code, closure, name, qualname, module_name)\ - __Pyx__Coroutine_New(__pyx_GeneratorType, body, code, closure, name, qualname, module_name) -static PyObject *__Pyx_Generator_Next(PyObject *self); -static int __pyx_Generator_init(PyObject *module); - -/* CheckBinaryVersion.proto */ -static unsigned long __Pyx_get_runtime_version(void); -static int __Pyx_check_binary_version(unsigned long ct_version, unsigned long rt_version, int allow_newer); - -/* InitStrings.proto */ -static int __Pyx_InitStrings(__Pyx_StringTabEntry *t); - -/* #### Code section: module_declarations ### */ - -/* Module declarations from "cython" */ - -/* Module declarations from "fontTools.cu2qu.cu2qu" */ -static CYTHON_INLINE double __pyx_f_9fontTools_5cu2qu_5cu2qu_dot(__pyx_t_double_complex, __pyx_t_double_complex); /*proto*/ -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_calc_cubic_points(__pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex); /*proto*/ -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_calc_cubic_parameters(__pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex); /*proto*/ -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_n_iter(__pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, PyObject *); /*proto*/ -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_two(__pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex); /*proto*/ -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_three(__pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex); /*proto*/ -static CYTHON_INLINE __pyx_t_double_complex __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_control(double, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex); /*proto*/ -static CYTHON_INLINE __pyx_t_double_complex __pyx_f_9fontTools_5cu2qu_5cu2qu_calc_intersect(__pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex); /*proto*/ -static int __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_farthest_fit_inside(__pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, __pyx_t_double_complex, double); /*proto*/ -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_quadratic(PyObject *, double); /*proto*/ -static PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_spline(PyObject *, int, double, int); /*proto*/ -/* #### Code section: typeinfo ### */ -/* #### Code section: before_global_var ### */ -#define __Pyx_MODULE_NAME "fontTools.cu2qu.cu2qu" -extern int __pyx_module_is_main_fontTools__cu2qu__cu2qu; -int __pyx_module_is_main_fontTools__cu2qu__cu2qu = 0; - -/* Implementation of "fontTools.cu2qu.cu2qu" */ -/* #### Code section: global_var ### */ -static PyObject *__pyx_builtin_AttributeError; -static PyObject *__pyx_builtin_ImportError; -static PyObject *__pyx_builtin_range; -static PyObject *__pyx_builtin_ZeroDivisionError; -static PyObject *__pyx_builtin_AssertionError; -/* #### Code section: string_decls ### */ -static const char __pyx_k_a[] = "a"; -static const char __pyx_k_b[] = "b"; -static const char __pyx_k_c[] = "c"; -static const char __pyx_k_d[] = "d"; -static const char __pyx_k_i[] = "i"; -static const char __pyx_k_l[] = "l"; -static const char __pyx_k_n[] = "n"; -static const char __pyx_k_p[] = "p"; -static const char __pyx_k_s[] = "s"; -static const char __pyx_k__2[] = "."; -static const char __pyx_k__3[] = "*"; -static const char __pyx_k__9[] = "?"; -static const char __pyx_k_a1[] = "a1"; -static const char __pyx_k_b1[] = "b1"; -static const char __pyx_k_c1[] = "c1"; -static const char __pyx_k_d1[] = "d1"; -static const char __pyx_k_dt[] = "dt"; -static const char __pyx_k_gc[] = "gc"; -static const char __pyx_k_p0[] = "p0"; -static const char __pyx_k_p1[] = "p1"; -static const char __pyx_k_p2[] = "p2"; -static const char __pyx_k_p3[] = "p3"; -static const char __pyx_k_t1[] = "t1"; -static const char __pyx_k_NAN[] = "NAN"; -static const char __pyx_k_NaN[] = "NaN"; -static const char __pyx_k_all[] = "__all__"; -static const char __pyx_k_args[] = "args"; -static const char __pyx_k_imag[] = "imag"; -static const char __pyx_k_main[] = "__main__"; -static const char __pyx_k_math[] = "math"; -static const char __pyx_k_name[] = "__name__"; -static const char __pyx_k_real[] = "real"; -static const char __pyx_k_send[] = "send"; -static const char __pyx_k_spec[] = "__spec__"; -static const char __pyx_k_t1_2[] = "t1_2"; -static const char __pyx_k_test[] = "__test__"; -static const char __pyx_k_Error[] = "Error"; -static const char __pyx_k_MAX_N[] = "MAX_N"; -static const char __pyx_k_close[] = "close"; -static const char __pyx_k_curve[] = "curve"; -static const char __pyx_k_isnan[] = "isnan"; -static const char __pyx_k_range[] = "range"; -static const char __pyx_k_throw[] = "throw"; -static const char __pyx_k_curves[] = "curves"; -static const char __pyx_k_cython[] = "cython"; -static const char __pyx_k_enable[] = "enable"; -static const char __pyx_k_errors[] = "errors"; -static const char __pyx_k_import[] = "__import__"; -static const char __pyx_k_last_i[] = "last_i"; -static const char __pyx_k_spline[] = "spline"; -static const char __pyx_k_delta_2[] = "delta_2"; -static const char __pyx_k_delta_3[] = "delta_3"; -static const char __pyx_k_disable[] = "disable"; -static const char __pyx_k_max_err[] = "max_err"; -static const char __pyx_k_splines[] = "splines"; -static const char __pyx_k_COMPILED[] = "COMPILED"; -static const char __pyx_k_isenabled[] = "isenabled"; -static const char __pyx_k_Cu2QuError[] = "Cu2QuError"; -static const char __pyx_k_max_errors[] = "max_errors"; -static const char __pyx_k_ImportError[] = "ImportError"; -static const char __pyx_k_initializing[] = "_initializing"; -static const char __pyx_k_is_coroutine[] = "_is_coroutine"; -static const char __pyx_k_all_quadratic[] = "all_quadratic"; -static const char __pyx_k_AssertionError[] = "AssertionError"; -static const char __pyx_k_AttributeError[] = "AttributeError"; -static const char __pyx_k_fontTools_misc[] = "fontTools.misc"; -static const char __pyx_k_ZeroDivisionError[] = "ZeroDivisionError"; -static const char __pyx_k_asyncio_coroutines[] = "asyncio.coroutines"; -static const char __pyx_k_cline_in_traceback[] = "cline_in_traceback"; -static const char __pyx_k_curve_to_quadratic[] = "curve_to_quadratic"; -static const char __pyx_k_ApproxNotFoundError[] = "ApproxNotFoundError"; -static const char __pyx_k_curves_to_quadratic[] = "curves_to_quadratic"; -static const char __pyx_k_fontTools_cu2qu_cu2qu[] = "fontTools.cu2qu.cu2qu"; -static const char __pyx_k_split_cubic_into_n_gen[] = "_split_cubic_into_n_gen"; -static const char __pyx_k_Lib_fontTools_cu2qu_cu2qu_py[] = "Lib/fontTools/cu2qu/cu2qu.py"; -static const char __pyx_k_curves_to_quadratic_line_474[] = "curves_to_quadratic (line 474)"; -static const char __pyx_k_Return_quadratic_Bezier_splines[] = "Return quadratic Bezier splines approximating the input cubic Beziers.\n\n Args:\n curves: A sequence of *n* curves, each curve being a sequence of four\n 2D tuples.\n max_errors: A sequence of *n* floats representing the maximum permissible\n deviation from each of the cubic Bezier curves.\n all_quadratic (bool): If True (default) returned values are a\n quadratic spline. If False, they are either a single quadratic\n curve or a single cubic curve.\n\n Example::\n\n >>> curves_to_quadratic( [\n ... [ (50,50), (100,100), (150,100), (200,50) ],\n ... [ (75,50), (120,100), (150,75), (200,60) ]\n ... ], [1,1] )\n [[(50.0, 50.0), (75.0, 75.0), (125.0, 91.66666666666666), (175.0, 75.0), (200.0, 50.0)], [(75.0, 50.0), (97.5, 75.0), (135.41666666666666, 82.08333333333333), (175.0, 67.5), (200.0, 60.0)]]\n\n The returned splines have \"implied oncurve points\" suitable for use in\n TrueType ``glif`` outlines - i.e. in the first spline returned above,\n the first quadratic segment runs from (50,50) to\n ( (75 + 125)/2 , (120 + 91.666..)/2 ) = (100, 83.333...).\n\n Returns:\n If all_quadratic is True, a list of splines, each spline being a list\n of 2D tuples.\n\n If all_quadratic is False, a list of curves, each curve being a quadratic\n (length 3), or cubic (length 4).\n\n Raises:\n fontTools.cu2qu.Errors.ApproxNotFoundError: if no suitable approximation\n can be found for all curves with the given parameters.\n "; -/* #### Code section: decls ### */ -static PyObject *__pyx_pf_9fontTools_5cu2qu_5cu2qu__split_cubic_into_n_gen(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2, __pyx_t_double_complex __pyx_v_p3, int __pyx_v_n); /* proto */ -static PyObject *__pyx_pf_9fontTools_5cu2qu_5cu2qu_3curve_to_quadratic(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_curve, double __pyx_v_max_err, int __pyx_v_all_quadratic); /* proto */ -static PyObject *__pyx_pf_9fontTools_5cu2qu_5cu2qu_5curves_to_quadratic(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_curves, PyObject *__pyx_v_max_errors, int __pyx_v_all_quadratic); /* proto */ -static PyObject *__pyx_tp_new_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/ -/* #### Code section: late_includes ### */ -/* #### Code section: module_state ### */ -typedef struct { - PyObject *__pyx_d; - PyObject *__pyx_b; - PyObject *__pyx_cython_runtime; - PyObject *__pyx_empty_tuple; - PyObject *__pyx_empty_bytes; - PyObject *__pyx_empty_unicode; - #ifdef __Pyx_CyFunction_USED - PyTypeObject *__pyx_CyFunctionType; - #endif - #ifdef __Pyx_FusedFunction_USED - PyTypeObject *__pyx_FusedFunctionType; - #endif - #ifdef __Pyx_Generator_USED - PyTypeObject *__pyx_GeneratorType; - #endif - #ifdef __Pyx_IterableCoroutine_USED - PyTypeObject *__pyx_IterableCoroutineType; - #endif - #ifdef __Pyx_Coroutine_USED - PyTypeObject *__pyx_CoroutineAwaitType; - #endif - #ifdef __Pyx_Coroutine_USED - PyTypeObject *__pyx_CoroutineType; - #endif - #if CYTHON_USE_MODULE_STATE - #endif - #if CYTHON_USE_MODULE_STATE - PyObject *__pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen; - #endif - PyTypeObject *__pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen; - PyObject *__pyx_n_s_ApproxNotFoundError; - PyObject *__pyx_n_s_AssertionError; - PyObject *__pyx_n_s_AttributeError; - PyObject *__pyx_n_s_COMPILED; - PyObject *__pyx_n_s_Cu2QuError; - PyObject *__pyx_n_s_Error; - PyObject *__pyx_n_s_ImportError; - PyObject *__pyx_kp_s_Lib_fontTools_cu2qu_cu2qu_py; - PyObject *__pyx_n_s_MAX_N; - PyObject *__pyx_n_s_NAN; - PyObject *__pyx_n_u_NaN; - PyObject *__pyx_kp_u_Return_quadratic_Bezier_splines; - PyObject *__pyx_n_s_ZeroDivisionError; - PyObject *__pyx_kp_u__2; - PyObject *__pyx_n_s__3; - PyObject *__pyx_n_s__9; - PyObject *__pyx_n_s_a; - PyObject *__pyx_n_s_a1; - PyObject *__pyx_n_s_all; - PyObject *__pyx_n_s_all_quadratic; - PyObject *__pyx_n_s_args; - PyObject *__pyx_n_s_asyncio_coroutines; - PyObject *__pyx_n_s_b; - PyObject *__pyx_n_s_b1; - PyObject *__pyx_n_s_c; - PyObject *__pyx_n_s_c1; - PyObject *__pyx_n_s_cline_in_traceback; - PyObject *__pyx_n_s_close; - PyObject *__pyx_n_s_curve; - PyObject *__pyx_n_s_curve_to_quadratic; - PyObject *__pyx_n_u_curve_to_quadratic; - PyObject *__pyx_n_s_curves; - PyObject *__pyx_n_s_curves_to_quadratic; - PyObject *__pyx_n_u_curves_to_quadratic; - PyObject *__pyx_kp_u_curves_to_quadratic_line_474; - PyObject *__pyx_n_s_cython; - PyObject *__pyx_n_s_d; - PyObject *__pyx_n_s_d1; - PyObject *__pyx_n_s_delta_2; - PyObject *__pyx_n_s_delta_3; - PyObject *__pyx_kp_u_disable; - PyObject *__pyx_n_s_dt; - PyObject *__pyx_kp_u_enable; - PyObject *__pyx_n_s_errors; - PyObject *__pyx_n_s_fontTools_cu2qu_cu2qu; - PyObject *__pyx_n_s_fontTools_misc; - PyObject *__pyx_kp_u_gc; - PyObject *__pyx_n_s_i; - PyObject *__pyx_n_s_imag; - PyObject *__pyx_n_s_import; - PyObject *__pyx_n_s_initializing; - PyObject *__pyx_n_s_is_coroutine; - PyObject *__pyx_kp_u_isenabled; - PyObject *__pyx_n_s_isnan; - PyObject *__pyx_n_s_l; - PyObject *__pyx_n_s_last_i; - PyObject *__pyx_n_s_main; - PyObject *__pyx_n_s_math; - PyObject *__pyx_n_s_max_err; - PyObject *__pyx_n_s_max_errors; - PyObject *__pyx_n_s_n; - PyObject *__pyx_n_s_name; - PyObject *__pyx_n_s_p; - PyObject *__pyx_n_s_p0; - PyObject *__pyx_n_s_p1; - PyObject *__pyx_n_s_p2; - PyObject *__pyx_n_s_p3; - PyObject *__pyx_n_s_range; - PyObject *__pyx_n_s_real; - PyObject *__pyx_n_s_s; - PyObject *__pyx_n_s_send; - PyObject *__pyx_n_s_spec; - PyObject *__pyx_n_s_spline; - PyObject *__pyx_n_s_splines; - PyObject *__pyx_n_s_split_cubic_into_n_gen; - PyObject *__pyx_n_s_t1; - PyObject *__pyx_n_s_t1_2; - PyObject *__pyx_n_s_test; - PyObject *__pyx_n_s_throw; - PyObject *__pyx_int_1; - PyObject *__pyx_int_2; - PyObject *__pyx_int_3; - PyObject *__pyx_int_4; - PyObject *__pyx_int_6; - PyObject *__pyx_int_100; - PyObject *__pyx_codeobj_; - PyObject *__pyx_tuple__4; - PyObject *__pyx_tuple__5; - PyObject *__pyx_tuple__7; - PyObject *__pyx_codeobj__6; - PyObject *__pyx_codeobj__8; -} __pyx_mstate; - -#if CYTHON_USE_MODULE_STATE -#ifdef __cplusplus -namespace { - extern struct PyModuleDef __pyx_moduledef; -} /* anonymous namespace */ -#else -static struct PyModuleDef __pyx_moduledef; -#endif - -#define __pyx_mstate(o) ((__pyx_mstate *)__Pyx_PyModule_GetState(o)) - -#define __pyx_mstate_global (__pyx_mstate(PyState_FindModule(&__pyx_moduledef))) - -#define __pyx_m (PyState_FindModule(&__pyx_moduledef)) -#else -static __pyx_mstate __pyx_mstate_global_static = -#ifdef __cplusplus - {}; -#else - {0}; -#endif -static __pyx_mstate *__pyx_mstate_global = &__pyx_mstate_global_static; -#endif -/* #### Code section: module_state_clear ### */ -#if CYTHON_USE_MODULE_STATE -static int __pyx_m_clear(PyObject *m) { - __pyx_mstate *clear_module_state = __pyx_mstate(m); - if (!clear_module_state) return 0; - Py_CLEAR(clear_module_state->__pyx_d); - Py_CLEAR(clear_module_state->__pyx_b); - Py_CLEAR(clear_module_state->__pyx_cython_runtime); - Py_CLEAR(clear_module_state->__pyx_empty_tuple); - Py_CLEAR(clear_module_state->__pyx_empty_bytes); - Py_CLEAR(clear_module_state->__pyx_empty_unicode); - #ifdef __Pyx_CyFunction_USED - Py_CLEAR(clear_module_state->__pyx_CyFunctionType); - #endif - #ifdef __Pyx_FusedFunction_USED - Py_CLEAR(clear_module_state->__pyx_FusedFunctionType); - #endif - Py_CLEAR(clear_module_state->__pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen); - Py_CLEAR(clear_module_state->__pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen); - Py_CLEAR(clear_module_state->__pyx_n_s_ApproxNotFoundError); - Py_CLEAR(clear_module_state->__pyx_n_s_AssertionError); - Py_CLEAR(clear_module_state->__pyx_n_s_AttributeError); - Py_CLEAR(clear_module_state->__pyx_n_s_COMPILED); - Py_CLEAR(clear_module_state->__pyx_n_s_Cu2QuError); - Py_CLEAR(clear_module_state->__pyx_n_s_Error); - Py_CLEAR(clear_module_state->__pyx_n_s_ImportError); - Py_CLEAR(clear_module_state->__pyx_kp_s_Lib_fontTools_cu2qu_cu2qu_py); - Py_CLEAR(clear_module_state->__pyx_n_s_MAX_N); - Py_CLEAR(clear_module_state->__pyx_n_s_NAN); - Py_CLEAR(clear_module_state->__pyx_n_u_NaN); - Py_CLEAR(clear_module_state->__pyx_kp_u_Return_quadratic_Bezier_splines); - Py_CLEAR(clear_module_state->__pyx_n_s_ZeroDivisionError); - Py_CLEAR(clear_module_state->__pyx_kp_u__2); - Py_CLEAR(clear_module_state->__pyx_n_s__3); - Py_CLEAR(clear_module_state->__pyx_n_s__9); - Py_CLEAR(clear_module_state->__pyx_n_s_a); - Py_CLEAR(clear_module_state->__pyx_n_s_a1); - Py_CLEAR(clear_module_state->__pyx_n_s_all); - Py_CLEAR(clear_module_state->__pyx_n_s_all_quadratic); - Py_CLEAR(clear_module_state->__pyx_n_s_args); - Py_CLEAR(clear_module_state->__pyx_n_s_asyncio_coroutines); - Py_CLEAR(clear_module_state->__pyx_n_s_b); - Py_CLEAR(clear_module_state->__pyx_n_s_b1); - Py_CLEAR(clear_module_state->__pyx_n_s_c); - Py_CLEAR(clear_module_state->__pyx_n_s_c1); - Py_CLEAR(clear_module_state->__pyx_n_s_cline_in_traceback); - Py_CLEAR(clear_module_state->__pyx_n_s_close); - Py_CLEAR(clear_module_state->__pyx_n_s_curve); - Py_CLEAR(clear_module_state->__pyx_n_s_curve_to_quadratic); - Py_CLEAR(clear_module_state->__pyx_n_u_curve_to_quadratic); - Py_CLEAR(clear_module_state->__pyx_n_s_curves); - Py_CLEAR(clear_module_state->__pyx_n_s_curves_to_quadratic); - Py_CLEAR(clear_module_state->__pyx_n_u_curves_to_quadratic); - Py_CLEAR(clear_module_state->__pyx_kp_u_curves_to_quadratic_line_474); - Py_CLEAR(clear_module_state->__pyx_n_s_cython); - Py_CLEAR(clear_module_state->__pyx_n_s_d); - Py_CLEAR(clear_module_state->__pyx_n_s_d1); - Py_CLEAR(clear_module_state->__pyx_n_s_delta_2); - Py_CLEAR(clear_module_state->__pyx_n_s_delta_3); - Py_CLEAR(clear_module_state->__pyx_kp_u_disable); - Py_CLEAR(clear_module_state->__pyx_n_s_dt); - Py_CLEAR(clear_module_state->__pyx_kp_u_enable); - Py_CLEAR(clear_module_state->__pyx_n_s_errors); - Py_CLEAR(clear_module_state->__pyx_n_s_fontTools_cu2qu_cu2qu); - Py_CLEAR(clear_module_state->__pyx_n_s_fontTools_misc); - Py_CLEAR(clear_module_state->__pyx_kp_u_gc); - Py_CLEAR(clear_module_state->__pyx_n_s_i); - Py_CLEAR(clear_module_state->__pyx_n_s_imag); - Py_CLEAR(clear_module_state->__pyx_n_s_import); - Py_CLEAR(clear_module_state->__pyx_n_s_initializing); - Py_CLEAR(clear_module_state->__pyx_n_s_is_coroutine); - Py_CLEAR(clear_module_state->__pyx_kp_u_isenabled); - Py_CLEAR(clear_module_state->__pyx_n_s_isnan); - Py_CLEAR(clear_module_state->__pyx_n_s_l); - Py_CLEAR(clear_module_state->__pyx_n_s_last_i); - Py_CLEAR(clear_module_state->__pyx_n_s_main); - Py_CLEAR(clear_module_state->__pyx_n_s_math); - Py_CLEAR(clear_module_state->__pyx_n_s_max_err); - Py_CLEAR(clear_module_state->__pyx_n_s_max_errors); - Py_CLEAR(clear_module_state->__pyx_n_s_n); - Py_CLEAR(clear_module_state->__pyx_n_s_name); - Py_CLEAR(clear_module_state->__pyx_n_s_p); - Py_CLEAR(clear_module_state->__pyx_n_s_p0); - Py_CLEAR(clear_module_state->__pyx_n_s_p1); - Py_CLEAR(clear_module_state->__pyx_n_s_p2); - Py_CLEAR(clear_module_state->__pyx_n_s_p3); - Py_CLEAR(clear_module_state->__pyx_n_s_range); - Py_CLEAR(clear_module_state->__pyx_n_s_real); - Py_CLEAR(clear_module_state->__pyx_n_s_s); - Py_CLEAR(clear_module_state->__pyx_n_s_send); - Py_CLEAR(clear_module_state->__pyx_n_s_spec); - Py_CLEAR(clear_module_state->__pyx_n_s_spline); - Py_CLEAR(clear_module_state->__pyx_n_s_splines); - Py_CLEAR(clear_module_state->__pyx_n_s_split_cubic_into_n_gen); - Py_CLEAR(clear_module_state->__pyx_n_s_t1); - Py_CLEAR(clear_module_state->__pyx_n_s_t1_2); - Py_CLEAR(clear_module_state->__pyx_n_s_test); - Py_CLEAR(clear_module_state->__pyx_n_s_throw); - Py_CLEAR(clear_module_state->__pyx_int_1); - Py_CLEAR(clear_module_state->__pyx_int_2); - Py_CLEAR(clear_module_state->__pyx_int_3); - Py_CLEAR(clear_module_state->__pyx_int_4); - Py_CLEAR(clear_module_state->__pyx_int_6); - Py_CLEAR(clear_module_state->__pyx_int_100); - Py_CLEAR(clear_module_state->__pyx_codeobj_); - Py_CLEAR(clear_module_state->__pyx_tuple__4); - Py_CLEAR(clear_module_state->__pyx_tuple__5); - Py_CLEAR(clear_module_state->__pyx_tuple__7); - Py_CLEAR(clear_module_state->__pyx_codeobj__6); - Py_CLEAR(clear_module_state->__pyx_codeobj__8); - return 0; -} -#endif -/* #### Code section: module_state_traverse ### */ -#if CYTHON_USE_MODULE_STATE -static int __pyx_m_traverse(PyObject *m, visitproc visit, void *arg) { - __pyx_mstate *traverse_module_state = __pyx_mstate(m); - if (!traverse_module_state) return 0; - Py_VISIT(traverse_module_state->__pyx_d); - Py_VISIT(traverse_module_state->__pyx_b); - Py_VISIT(traverse_module_state->__pyx_cython_runtime); - Py_VISIT(traverse_module_state->__pyx_empty_tuple); - Py_VISIT(traverse_module_state->__pyx_empty_bytes); - Py_VISIT(traverse_module_state->__pyx_empty_unicode); - #ifdef __Pyx_CyFunction_USED - Py_VISIT(traverse_module_state->__pyx_CyFunctionType); - #endif - #ifdef __Pyx_FusedFunction_USED - Py_VISIT(traverse_module_state->__pyx_FusedFunctionType); - #endif - Py_VISIT(traverse_module_state->__pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen); - Py_VISIT(traverse_module_state->__pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen); - Py_VISIT(traverse_module_state->__pyx_n_s_ApproxNotFoundError); - Py_VISIT(traverse_module_state->__pyx_n_s_AssertionError); - Py_VISIT(traverse_module_state->__pyx_n_s_AttributeError); - Py_VISIT(traverse_module_state->__pyx_n_s_COMPILED); - Py_VISIT(traverse_module_state->__pyx_n_s_Cu2QuError); - Py_VISIT(traverse_module_state->__pyx_n_s_Error); - Py_VISIT(traverse_module_state->__pyx_n_s_ImportError); - Py_VISIT(traverse_module_state->__pyx_kp_s_Lib_fontTools_cu2qu_cu2qu_py); - Py_VISIT(traverse_module_state->__pyx_n_s_MAX_N); - Py_VISIT(traverse_module_state->__pyx_n_s_NAN); - Py_VISIT(traverse_module_state->__pyx_n_u_NaN); - Py_VISIT(traverse_module_state->__pyx_kp_u_Return_quadratic_Bezier_splines); - Py_VISIT(traverse_module_state->__pyx_n_s_ZeroDivisionError); - Py_VISIT(traverse_module_state->__pyx_kp_u__2); - Py_VISIT(traverse_module_state->__pyx_n_s__3); - Py_VISIT(traverse_module_state->__pyx_n_s__9); - Py_VISIT(traverse_module_state->__pyx_n_s_a); - Py_VISIT(traverse_module_state->__pyx_n_s_a1); - Py_VISIT(traverse_module_state->__pyx_n_s_all); - Py_VISIT(traverse_module_state->__pyx_n_s_all_quadratic); - Py_VISIT(traverse_module_state->__pyx_n_s_args); - Py_VISIT(traverse_module_state->__pyx_n_s_asyncio_coroutines); - Py_VISIT(traverse_module_state->__pyx_n_s_b); - Py_VISIT(traverse_module_state->__pyx_n_s_b1); - Py_VISIT(traverse_module_state->__pyx_n_s_c); - Py_VISIT(traverse_module_state->__pyx_n_s_c1); - Py_VISIT(traverse_module_state->__pyx_n_s_cline_in_traceback); - Py_VISIT(traverse_module_state->__pyx_n_s_close); - Py_VISIT(traverse_module_state->__pyx_n_s_curve); - Py_VISIT(traverse_module_state->__pyx_n_s_curve_to_quadratic); - Py_VISIT(traverse_module_state->__pyx_n_u_curve_to_quadratic); - Py_VISIT(traverse_module_state->__pyx_n_s_curves); - Py_VISIT(traverse_module_state->__pyx_n_s_curves_to_quadratic); - Py_VISIT(traverse_module_state->__pyx_n_u_curves_to_quadratic); - Py_VISIT(traverse_module_state->__pyx_kp_u_curves_to_quadratic_line_474); - Py_VISIT(traverse_module_state->__pyx_n_s_cython); - Py_VISIT(traverse_module_state->__pyx_n_s_d); - Py_VISIT(traverse_module_state->__pyx_n_s_d1); - Py_VISIT(traverse_module_state->__pyx_n_s_delta_2); - Py_VISIT(traverse_module_state->__pyx_n_s_delta_3); - Py_VISIT(traverse_module_state->__pyx_kp_u_disable); - Py_VISIT(traverse_module_state->__pyx_n_s_dt); - Py_VISIT(traverse_module_state->__pyx_kp_u_enable); - Py_VISIT(traverse_module_state->__pyx_n_s_errors); - Py_VISIT(traverse_module_state->__pyx_n_s_fontTools_cu2qu_cu2qu); - Py_VISIT(traverse_module_state->__pyx_n_s_fontTools_misc); - Py_VISIT(traverse_module_state->__pyx_kp_u_gc); - Py_VISIT(traverse_module_state->__pyx_n_s_i); - Py_VISIT(traverse_module_state->__pyx_n_s_imag); - Py_VISIT(traverse_module_state->__pyx_n_s_import); - Py_VISIT(traverse_module_state->__pyx_n_s_initializing); - Py_VISIT(traverse_module_state->__pyx_n_s_is_coroutine); - Py_VISIT(traverse_module_state->__pyx_kp_u_isenabled); - Py_VISIT(traverse_module_state->__pyx_n_s_isnan); - Py_VISIT(traverse_module_state->__pyx_n_s_l); - Py_VISIT(traverse_module_state->__pyx_n_s_last_i); - Py_VISIT(traverse_module_state->__pyx_n_s_main); - Py_VISIT(traverse_module_state->__pyx_n_s_math); - Py_VISIT(traverse_module_state->__pyx_n_s_max_err); - Py_VISIT(traverse_module_state->__pyx_n_s_max_errors); - Py_VISIT(traverse_module_state->__pyx_n_s_n); - Py_VISIT(traverse_module_state->__pyx_n_s_name); - Py_VISIT(traverse_module_state->__pyx_n_s_p); - Py_VISIT(traverse_module_state->__pyx_n_s_p0); - Py_VISIT(traverse_module_state->__pyx_n_s_p1); - Py_VISIT(traverse_module_state->__pyx_n_s_p2); - Py_VISIT(traverse_module_state->__pyx_n_s_p3); - Py_VISIT(traverse_module_state->__pyx_n_s_range); - Py_VISIT(traverse_module_state->__pyx_n_s_real); - Py_VISIT(traverse_module_state->__pyx_n_s_s); - Py_VISIT(traverse_module_state->__pyx_n_s_send); - Py_VISIT(traverse_module_state->__pyx_n_s_spec); - Py_VISIT(traverse_module_state->__pyx_n_s_spline); - Py_VISIT(traverse_module_state->__pyx_n_s_splines); - Py_VISIT(traverse_module_state->__pyx_n_s_split_cubic_into_n_gen); - Py_VISIT(traverse_module_state->__pyx_n_s_t1); - Py_VISIT(traverse_module_state->__pyx_n_s_t1_2); - Py_VISIT(traverse_module_state->__pyx_n_s_test); - Py_VISIT(traverse_module_state->__pyx_n_s_throw); - Py_VISIT(traverse_module_state->__pyx_int_1); - Py_VISIT(traverse_module_state->__pyx_int_2); - Py_VISIT(traverse_module_state->__pyx_int_3); - Py_VISIT(traverse_module_state->__pyx_int_4); - Py_VISIT(traverse_module_state->__pyx_int_6); - Py_VISIT(traverse_module_state->__pyx_int_100); - Py_VISIT(traverse_module_state->__pyx_codeobj_); - Py_VISIT(traverse_module_state->__pyx_tuple__4); - Py_VISIT(traverse_module_state->__pyx_tuple__5); - Py_VISIT(traverse_module_state->__pyx_tuple__7); - Py_VISIT(traverse_module_state->__pyx_codeobj__6); - Py_VISIT(traverse_module_state->__pyx_codeobj__8); - return 0; -} -#endif -/* #### Code section: module_state_defines ### */ -#define __pyx_d __pyx_mstate_global->__pyx_d -#define __pyx_b __pyx_mstate_global->__pyx_b -#define __pyx_cython_runtime __pyx_mstate_global->__pyx_cython_runtime -#define __pyx_empty_tuple __pyx_mstate_global->__pyx_empty_tuple -#define __pyx_empty_bytes __pyx_mstate_global->__pyx_empty_bytes -#define __pyx_empty_unicode __pyx_mstate_global->__pyx_empty_unicode -#ifdef __Pyx_CyFunction_USED -#define __pyx_CyFunctionType __pyx_mstate_global->__pyx_CyFunctionType -#endif -#ifdef __Pyx_FusedFunction_USED -#define __pyx_FusedFunctionType __pyx_mstate_global->__pyx_FusedFunctionType -#endif -#ifdef __Pyx_Generator_USED -#define __pyx_GeneratorType __pyx_mstate_global->__pyx_GeneratorType -#endif -#ifdef __Pyx_IterableCoroutine_USED -#define __pyx_IterableCoroutineType __pyx_mstate_global->__pyx_IterableCoroutineType -#endif -#ifdef __Pyx_Coroutine_USED -#define __pyx_CoroutineAwaitType __pyx_mstate_global->__pyx_CoroutineAwaitType -#endif -#ifdef __Pyx_Coroutine_USED -#define __pyx_CoroutineType __pyx_mstate_global->__pyx_CoroutineType -#endif -#if CYTHON_USE_MODULE_STATE -#endif -#if CYTHON_USE_MODULE_STATE -#define __pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen __pyx_mstate_global->__pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen -#endif -#define __pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen __pyx_mstate_global->__pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen -#define __pyx_n_s_ApproxNotFoundError __pyx_mstate_global->__pyx_n_s_ApproxNotFoundError -#define __pyx_n_s_AssertionError __pyx_mstate_global->__pyx_n_s_AssertionError -#define __pyx_n_s_AttributeError __pyx_mstate_global->__pyx_n_s_AttributeError -#define __pyx_n_s_COMPILED __pyx_mstate_global->__pyx_n_s_COMPILED -#define __pyx_n_s_Cu2QuError __pyx_mstate_global->__pyx_n_s_Cu2QuError -#define __pyx_n_s_Error __pyx_mstate_global->__pyx_n_s_Error -#define __pyx_n_s_ImportError __pyx_mstate_global->__pyx_n_s_ImportError -#define __pyx_kp_s_Lib_fontTools_cu2qu_cu2qu_py __pyx_mstate_global->__pyx_kp_s_Lib_fontTools_cu2qu_cu2qu_py -#define __pyx_n_s_MAX_N __pyx_mstate_global->__pyx_n_s_MAX_N -#define __pyx_n_s_NAN __pyx_mstate_global->__pyx_n_s_NAN -#define __pyx_n_u_NaN __pyx_mstate_global->__pyx_n_u_NaN -#define __pyx_kp_u_Return_quadratic_Bezier_splines __pyx_mstate_global->__pyx_kp_u_Return_quadratic_Bezier_splines -#define __pyx_n_s_ZeroDivisionError __pyx_mstate_global->__pyx_n_s_ZeroDivisionError -#define __pyx_kp_u__2 __pyx_mstate_global->__pyx_kp_u__2 -#define __pyx_n_s__3 __pyx_mstate_global->__pyx_n_s__3 -#define __pyx_n_s__9 __pyx_mstate_global->__pyx_n_s__9 -#define __pyx_n_s_a __pyx_mstate_global->__pyx_n_s_a -#define __pyx_n_s_a1 __pyx_mstate_global->__pyx_n_s_a1 -#define __pyx_n_s_all __pyx_mstate_global->__pyx_n_s_all -#define __pyx_n_s_all_quadratic __pyx_mstate_global->__pyx_n_s_all_quadratic -#define __pyx_n_s_args __pyx_mstate_global->__pyx_n_s_args -#define __pyx_n_s_asyncio_coroutines __pyx_mstate_global->__pyx_n_s_asyncio_coroutines -#define __pyx_n_s_b __pyx_mstate_global->__pyx_n_s_b -#define __pyx_n_s_b1 __pyx_mstate_global->__pyx_n_s_b1 -#define __pyx_n_s_c __pyx_mstate_global->__pyx_n_s_c -#define __pyx_n_s_c1 __pyx_mstate_global->__pyx_n_s_c1 -#define __pyx_n_s_cline_in_traceback __pyx_mstate_global->__pyx_n_s_cline_in_traceback -#define __pyx_n_s_close __pyx_mstate_global->__pyx_n_s_close -#define __pyx_n_s_curve __pyx_mstate_global->__pyx_n_s_curve -#define __pyx_n_s_curve_to_quadratic __pyx_mstate_global->__pyx_n_s_curve_to_quadratic -#define __pyx_n_u_curve_to_quadratic __pyx_mstate_global->__pyx_n_u_curve_to_quadratic -#define __pyx_n_s_curves __pyx_mstate_global->__pyx_n_s_curves -#define __pyx_n_s_curves_to_quadratic __pyx_mstate_global->__pyx_n_s_curves_to_quadratic -#define __pyx_n_u_curves_to_quadratic __pyx_mstate_global->__pyx_n_u_curves_to_quadratic -#define __pyx_kp_u_curves_to_quadratic_line_474 __pyx_mstate_global->__pyx_kp_u_curves_to_quadratic_line_474 -#define __pyx_n_s_cython __pyx_mstate_global->__pyx_n_s_cython -#define __pyx_n_s_d __pyx_mstate_global->__pyx_n_s_d -#define __pyx_n_s_d1 __pyx_mstate_global->__pyx_n_s_d1 -#define __pyx_n_s_delta_2 __pyx_mstate_global->__pyx_n_s_delta_2 -#define __pyx_n_s_delta_3 __pyx_mstate_global->__pyx_n_s_delta_3 -#define __pyx_kp_u_disable __pyx_mstate_global->__pyx_kp_u_disable -#define __pyx_n_s_dt __pyx_mstate_global->__pyx_n_s_dt -#define __pyx_kp_u_enable __pyx_mstate_global->__pyx_kp_u_enable -#define __pyx_n_s_errors __pyx_mstate_global->__pyx_n_s_errors -#define __pyx_n_s_fontTools_cu2qu_cu2qu __pyx_mstate_global->__pyx_n_s_fontTools_cu2qu_cu2qu -#define __pyx_n_s_fontTools_misc __pyx_mstate_global->__pyx_n_s_fontTools_misc -#define __pyx_kp_u_gc __pyx_mstate_global->__pyx_kp_u_gc -#define __pyx_n_s_i __pyx_mstate_global->__pyx_n_s_i -#define __pyx_n_s_imag __pyx_mstate_global->__pyx_n_s_imag -#define __pyx_n_s_import __pyx_mstate_global->__pyx_n_s_import -#define __pyx_n_s_initializing __pyx_mstate_global->__pyx_n_s_initializing -#define __pyx_n_s_is_coroutine __pyx_mstate_global->__pyx_n_s_is_coroutine -#define __pyx_kp_u_isenabled __pyx_mstate_global->__pyx_kp_u_isenabled -#define __pyx_n_s_isnan __pyx_mstate_global->__pyx_n_s_isnan -#define __pyx_n_s_l __pyx_mstate_global->__pyx_n_s_l -#define __pyx_n_s_last_i __pyx_mstate_global->__pyx_n_s_last_i -#define __pyx_n_s_main __pyx_mstate_global->__pyx_n_s_main -#define __pyx_n_s_math __pyx_mstate_global->__pyx_n_s_math -#define __pyx_n_s_max_err __pyx_mstate_global->__pyx_n_s_max_err -#define __pyx_n_s_max_errors __pyx_mstate_global->__pyx_n_s_max_errors -#define __pyx_n_s_n __pyx_mstate_global->__pyx_n_s_n -#define __pyx_n_s_name __pyx_mstate_global->__pyx_n_s_name -#define __pyx_n_s_p __pyx_mstate_global->__pyx_n_s_p -#define __pyx_n_s_p0 __pyx_mstate_global->__pyx_n_s_p0 -#define __pyx_n_s_p1 __pyx_mstate_global->__pyx_n_s_p1 -#define __pyx_n_s_p2 __pyx_mstate_global->__pyx_n_s_p2 -#define __pyx_n_s_p3 __pyx_mstate_global->__pyx_n_s_p3 -#define __pyx_n_s_range __pyx_mstate_global->__pyx_n_s_range -#define __pyx_n_s_real __pyx_mstate_global->__pyx_n_s_real -#define __pyx_n_s_s __pyx_mstate_global->__pyx_n_s_s -#define __pyx_n_s_send __pyx_mstate_global->__pyx_n_s_send -#define __pyx_n_s_spec __pyx_mstate_global->__pyx_n_s_spec -#define __pyx_n_s_spline __pyx_mstate_global->__pyx_n_s_spline -#define __pyx_n_s_splines __pyx_mstate_global->__pyx_n_s_splines -#define __pyx_n_s_split_cubic_into_n_gen __pyx_mstate_global->__pyx_n_s_split_cubic_into_n_gen -#define __pyx_n_s_t1 __pyx_mstate_global->__pyx_n_s_t1 -#define __pyx_n_s_t1_2 __pyx_mstate_global->__pyx_n_s_t1_2 -#define __pyx_n_s_test __pyx_mstate_global->__pyx_n_s_test -#define __pyx_n_s_throw __pyx_mstate_global->__pyx_n_s_throw -#define __pyx_int_1 __pyx_mstate_global->__pyx_int_1 -#define __pyx_int_2 __pyx_mstate_global->__pyx_int_2 -#define __pyx_int_3 __pyx_mstate_global->__pyx_int_3 -#define __pyx_int_4 __pyx_mstate_global->__pyx_int_4 -#define __pyx_int_6 __pyx_mstate_global->__pyx_int_6 -#define __pyx_int_100 __pyx_mstate_global->__pyx_int_100 -#define __pyx_codeobj_ __pyx_mstate_global->__pyx_codeobj_ -#define __pyx_tuple__4 __pyx_mstate_global->__pyx_tuple__4 -#define __pyx_tuple__5 __pyx_mstate_global->__pyx_tuple__5 -#define __pyx_tuple__7 __pyx_mstate_global->__pyx_tuple__7 -#define __pyx_codeobj__6 __pyx_mstate_global->__pyx_codeobj__6 -#define __pyx_codeobj__8 __pyx_mstate_global->__pyx_codeobj__8 -/* #### Code section: module_code ### */ - -/* "fontTools/cu2qu/cu2qu.py":40 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.returns(cython.double) - */ - -static CYTHON_INLINE double __pyx_f_9fontTools_5cu2qu_5cu2qu_dot(__pyx_t_double_complex __pyx_v_v1, __pyx_t_double_complex __pyx_v_v2) { - double __pyx_r; - - /* "fontTools/cu2qu/cu2qu.py":54 - * double: Dot product. - * """ - * return (v1 * v2.conjugate()).real # <<<<<<<<<<<<<< - * - * - */ - __pyx_r = __Pyx_CREAL(__Pyx_c_prod_double(__pyx_v_v1, __Pyx_c_conj_double(__pyx_v_v2))); - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":40 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.returns(cython.double) - */ - - /* function exit code */ - __pyx_L0:; - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":57 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex) - */ - -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_calc_cubic_points(__pyx_t_double_complex __pyx_v_a, __pyx_t_double_complex __pyx_v_b, __pyx_t_double_complex __pyx_v_c, __pyx_t_double_complex __pyx_v_d) { - __pyx_t_double_complex __pyx_v__1; - __pyx_t_double_complex __pyx_v__2; - __pyx_t_double_complex __pyx_v__3; - __pyx_t_double_complex __pyx_v__4; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - __pyx_t_double_complex __pyx_t_1; - __pyx_t_double_complex __pyx_t_2; - PyObject *__pyx_t_3 = NULL; - PyObject *__pyx_t_4 = NULL; - PyObject *__pyx_t_5 = NULL; - PyObject *__pyx_t_6 = NULL; - PyObject *__pyx_t_7 = NULL; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("calc_cubic_points", 1); - - /* "fontTools/cu2qu/cu2qu.py":64 - * ) - * def calc_cubic_points(a, b, c, d): - * _1 = d # <<<<<<<<<<<<<< - * _2 = (c / 3.0) + d - * _3 = (b + c) / 3.0 + _2 - */ - __pyx_v__1 = __pyx_v_d; - - /* "fontTools/cu2qu/cu2qu.py":65 - * def calc_cubic_points(a, b, c, d): - * _1 = d - * _2 = (c / 3.0) + d # <<<<<<<<<<<<<< - * _3 = (b + c) / 3.0 + _2 - * _4 = a + d + c + b - */ - __pyx_t_1 = __pyx_t_double_complex_from_parts(3.0, 0); - if (unlikely(__Pyx_c_is_zero_double(__pyx_t_1))) { - PyErr_SetString(PyExc_ZeroDivisionError, "float division"); - __PYX_ERR(0, 65, __pyx_L1_error) - } - __pyx_v__2 = __Pyx_c_sum_double(__Pyx_c_quot_double(__pyx_v_c, __pyx_t_1), __pyx_v_d); - - /* "fontTools/cu2qu/cu2qu.py":66 - * _1 = d - * _2 = (c / 3.0) + d - * _3 = (b + c) / 3.0 + _2 # <<<<<<<<<<<<<< - * _4 = a + d + c + b - * return _1, _2, _3, _4 - */ - __pyx_t_1 = __Pyx_c_sum_double(__pyx_v_b, __pyx_v_c); - __pyx_t_2 = __pyx_t_double_complex_from_parts(3.0, 0); - if (unlikely(__Pyx_c_is_zero_double(__pyx_t_2))) { - PyErr_SetString(PyExc_ZeroDivisionError, "float division"); - __PYX_ERR(0, 66, __pyx_L1_error) - } - __pyx_v__3 = __Pyx_c_sum_double(__Pyx_c_quot_double(__pyx_t_1, __pyx_t_2), __pyx_v__2); - - /* "fontTools/cu2qu/cu2qu.py":67 - * _2 = (c / 3.0) + d - * _3 = (b + c) / 3.0 + _2 - * _4 = a + d + c + b # <<<<<<<<<<<<<< - * return _1, _2, _3, _4 - * - */ - __pyx_v__4 = __Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__pyx_v_a, __pyx_v_d), __pyx_v_c), __pyx_v_b); - - /* "fontTools/cu2qu/cu2qu.py":68 - * _3 = (b + c) / 3.0 + _2 - * _4 = a + d + c + b - * return _1, _2, _3, _4 # <<<<<<<<<<<<<< - * - * - */ - __Pyx_XDECREF(__pyx_r); - __pyx_t_3 = __pyx_PyComplex_FromComplex(__pyx_v__1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 68, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_4 = __pyx_PyComplex_FromComplex(__pyx_v__2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 68, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_5 = __pyx_PyComplex_FromComplex(__pyx_v__3); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 68, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __pyx_t_6 = __pyx_PyComplex_FromComplex(__pyx_v__4); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 68, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - __pyx_t_7 = PyTuple_New(4); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 68, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - __Pyx_GIVEREF(__pyx_t_3); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 0, __pyx_t_3)) __PYX_ERR(0, 68, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_4); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 1, __pyx_t_4)) __PYX_ERR(0, 68, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_5); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 2, __pyx_t_5)) __PYX_ERR(0, 68, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_6); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 3, __pyx_t_6)) __PYX_ERR(0, 68, __pyx_L1_error); - __pyx_t_3 = 0; - __pyx_t_4 = 0; - __pyx_t_5 = 0; - __pyx_t_6 = 0; - __pyx_r = __pyx_t_7; - __pyx_t_7 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":57 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex) - */ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_3); - __Pyx_XDECREF(__pyx_t_4); - __Pyx_XDECREF(__pyx_t_5); - __Pyx_XDECREF(__pyx_t_6); - __Pyx_XDECREF(__pyx_t_7); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.calc_cubic_points", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = 0; - __pyx_L0:; - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":71 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals( - */ - -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_calc_cubic_parameters(__pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2, __pyx_t_double_complex __pyx_v_p3) { - __pyx_t_double_complex __pyx_v_a; - __pyx_t_double_complex __pyx_v_b; - __pyx_t_double_complex __pyx_v_c; - __pyx_t_double_complex __pyx_v_d; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - PyObject *__pyx_t_1 = NULL; - PyObject *__pyx_t_2 = NULL; - PyObject *__pyx_t_3 = NULL; - PyObject *__pyx_t_4 = NULL; - PyObject *__pyx_t_5 = NULL; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("calc_cubic_parameters", 1); - - /* "fontTools/cu2qu/cu2qu.py":78 - * @cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex) - * def calc_cubic_parameters(p0, p1, p2, p3): - * c = (p1 - p0) * 3.0 # <<<<<<<<<<<<<< - * b = (p2 - p1) * 3.0 - c - * d = p0 - */ - __pyx_v_c = __Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_p1, __pyx_v_p0), __pyx_t_double_complex_from_parts(3.0, 0)); - - /* "fontTools/cu2qu/cu2qu.py":79 - * def calc_cubic_parameters(p0, p1, p2, p3): - * c = (p1 - p0) * 3.0 - * b = (p2 - p1) * 3.0 - c # <<<<<<<<<<<<<< - * d = p0 - * a = p3 - d - c - b - */ - __pyx_v_b = __Pyx_c_diff_double(__Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_p2, __pyx_v_p1), __pyx_t_double_complex_from_parts(3.0, 0)), __pyx_v_c); - - /* "fontTools/cu2qu/cu2qu.py":80 - * c = (p1 - p0) * 3.0 - * b = (p2 - p1) * 3.0 - c - * d = p0 # <<<<<<<<<<<<<< - * a = p3 - d - c - b - * return a, b, c, d - */ - __pyx_v_d = __pyx_v_p0; - - /* "fontTools/cu2qu/cu2qu.py":81 - * b = (p2 - p1) * 3.0 - c - * d = p0 - * a = p3 - d - c - b # <<<<<<<<<<<<<< - * return a, b, c, d - * - */ - __pyx_v_a = __Pyx_c_diff_double(__Pyx_c_diff_double(__Pyx_c_diff_double(__pyx_v_p3, __pyx_v_d), __pyx_v_c), __pyx_v_b); - - /* "fontTools/cu2qu/cu2qu.py":82 - * d = p0 - * a = p3 - d - c - b - * return a, b, c, d # <<<<<<<<<<<<<< - * - * - */ - __Pyx_XDECREF(__pyx_r); - __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_a); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 82, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_2 = __pyx_PyComplex_FromComplex(__pyx_v_b); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 82, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_3 = __pyx_PyComplex_FromComplex(__pyx_v_c); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 82, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_4 = __pyx_PyComplex_FromComplex(__pyx_v_d); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 82, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_5 = PyTuple_New(4); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 82, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __Pyx_GIVEREF(__pyx_t_1); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_t_1)) __PYX_ERR(0, 82, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_2); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_t_2)) __PYX_ERR(0, 82, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_3); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 2, __pyx_t_3)) __PYX_ERR(0, 82, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_4); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_5, 3, __pyx_t_4)) __PYX_ERR(0, 82, __pyx_L1_error); - __pyx_t_1 = 0; - __pyx_t_2 = 0; - __pyx_t_3 = 0; - __pyx_t_4 = 0; - __pyx_r = __pyx_t_5; - __pyx_t_5 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":71 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals( - */ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_1); - __Pyx_XDECREF(__pyx_t_2); - __Pyx_XDECREF(__pyx_t_3); - __Pyx_XDECREF(__pyx_t_4); - __Pyx_XDECREF(__pyx_t_5); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.calc_cubic_parameters", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = 0; - __pyx_L0:; - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":85 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals( - */ - -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_n_iter(__pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2, __pyx_t_double_complex __pyx_v_p3, PyObject *__pyx_v_n) { - PyObject *__pyx_v_a = NULL; - PyObject *__pyx_v_b = NULL; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - int __pyx_t_1; - PyObject *__pyx_t_2 = NULL; - PyObject *__pyx_t_3 = NULL; - PyObject *__pyx_t_4 = NULL; - PyObject *__pyx_t_5 = NULL; - PyObject *(*__pyx_t_6)(PyObject *); - __pyx_t_double_complex __pyx_t_7; - __pyx_t_double_complex __pyx_t_8; - __pyx_t_double_complex __pyx_t_9; - __pyx_t_double_complex __pyx_t_10; - PyObject *__pyx_t_11 = NULL; - PyObject *__pyx_t_12 = NULL; - PyObject *__pyx_t_13 = NULL; - unsigned int __pyx_t_14; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("split_cubic_into_n_iter", 1); - - /* "fontTools/cu2qu/cu2qu.py":107 - * """ - * # Hand-coded special-cases - * if n == 2: # <<<<<<<<<<<<<< - * return iter(split_cubic_into_two(p0, p1, p2, p3)) - * if n == 3: - */ - __pyx_t_1 = (__Pyx_PyInt_BoolEqObjC(__pyx_v_n, __pyx_int_2, 2, 0)); if (unlikely((__pyx_t_1 < 0))) __PYX_ERR(0, 107, __pyx_L1_error) - if (__pyx_t_1) { - - /* "fontTools/cu2qu/cu2qu.py":108 - * # Hand-coded special-cases - * if n == 2: - * return iter(split_cubic_into_two(p0, p1, p2, p3)) # <<<<<<<<<<<<<< - * if n == 3: - * return iter(split_cubic_into_three(p0, p1, p2, p3)) - */ - __Pyx_XDECREF(__pyx_r); - __pyx_t_2 = __pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_two(__pyx_v_p0, __pyx_v_p1, __pyx_v_p2, __pyx_v_p3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 108, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_3 = PyObject_GetIter(__pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 108, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_r = __pyx_t_3; - __pyx_t_3 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":107 - * """ - * # Hand-coded special-cases - * if n == 2: # <<<<<<<<<<<<<< - * return iter(split_cubic_into_two(p0, p1, p2, p3)) - * if n == 3: - */ - } - - /* "fontTools/cu2qu/cu2qu.py":109 - * if n == 2: - * return iter(split_cubic_into_two(p0, p1, p2, p3)) - * if n == 3: # <<<<<<<<<<<<<< - * return iter(split_cubic_into_three(p0, p1, p2, p3)) - * if n == 4: - */ - __pyx_t_1 = (__Pyx_PyInt_BoolEqObjC(__pyx_v_n, __pyx_int_3, 3, 0)); if (unlikely((__pyx_t_1 < 0))) __PYX_ERR(0, 109, __pyx_L1_error) - if (__pyx_t_1) { - - /* "fontTools/cu2qu/cu2qu.py":110 - * return iter(split_cubic_into_two(p0, p1, p2, p3)) - * if n == 3: - * return iter(split_cubic_into_three(p0, p1, p2, p3)) # <<<<<<<<<<<<<< - * if n == 4: - * a, b = split_cubic_into_two(p0, p1, p2, p3) - */ - __Pyx_XDECREF(__pyx_r); - __pyx_t_3 = __pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_three(__pyx_v_p0, __pyx_v_p1, __pyx_v_p2, __pyx_v_p3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 110, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_2 = PyObject_GetIter(__pyx_t_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 110, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - __pyx_r = __pyx_t_2; - __pyx_t_2 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":109 - * if n == 2: - * return iter(split_cubic_into_two(p0, p1, p2, p3)) - * if n == 3: # <<<<<<<<<<<<<< - * return iter(split_cubic_into_three(p0, p1, p2, p3)) - * if n == 4: - */ - } - - /* "fontTools/cu2qu/cu2qu.py":111 - * if n == 3: - * return iter(split_cubic_into_three(p0, p1, p2, p3)) - * if n == 4: # <<<<<<<<<<<<<< - * a, b = split_cubic_into_two(p0, p1, p2, p3) - * return iter( - */ - __pyx_t_1 = (__Pyx_PyInt_BoolEqObjC(__pyx_v_n, __pyx_int_4, 4, 0)); if (unlikely((__pyx_t_1 < 0))) __PYX_ERR(0, 111, __pyx_L1_error) - if (__pyx_t_1) { - - /* "fontTools/cu2qu/cu2qu.py":112 - * return iter(split_cubic_into_three(p0, p1, p2, p3)) - * if n == 4: - * a, b = split_cubic_into_two(p0, p1, p2, p3) # <<<<<<<<<<<<<< - * return iter( - * split_cubic_into_two(a[0], a[1], a[2], a[3]) - */ - __pyx_t_2 = __pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_two(__pyx_v_p0, __pyx_v_p1, __pyx_v_p2, __pyx_v_p3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 112, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - if ((likely(PyTuple_CheckExact(__pyx_t_2))) || (PyList_CheckExact(__pyx_t_2))) { - PyObject* sequence = __pyx_t_2; - Py_ssize_t size = __Pyx_PySequence_SIZE(sequence); - if (unlikely(size != 2)) { - if (size > 2) __Pyx_RaiseTooManyValuesError(2); - else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size); - __PYX_ERR(0, 112, __pyx_L1_error) - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - if (likely(PyTuple_CheckExact(sequence))) { - __pyx_t_3 = PyTuple_GET_ITEM(sequence, 0); - __pyx_t_4 = PyTuple_GET_ITEM(sequence, 1); - } else { - __pyx_t_3 = PyList_GET_ITEM(sequence, 0); - __pyx_t_4 = PyList_GET_ITEM(sequence, 1); - } - __Pyx_INCREF(__pyx_t_3); - __Pyx_INCREF(__pyx_t_4); - #else - __pyx_t_3 = PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 112, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_4 = PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 112, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - #endif - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - } else { - Py_ssize_t index = -1; - __pyx_t_5 = PyObject_GetIter(__pyx_t_2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 112, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_6 = __Pyx_PyObject_GetIterNextFunc(__pyx_t_5); - index = 0; __pyx_t_3 = __pyx_t_6(__pyx_t_5); if (unlikely(!__pyx_t_3)) goto __pyx_L6_unpacking_failed; - __Pyx_GOTREF(__pyx_t_3); - index = 1; __pyx_t_4 = __pyx_t_6(__pyx_t_5); if (unlikely(!__pyx_t_4)) goto __pyx_L6_unpacking_failed; - __Pyx_GOTREF(__pyx_t_4); - if (__Pyx_IternextUnpackEndCheck(__pyx_t_6(__pyx_t_5), 2) < 0) __PYX_ERR(0, 112, __pyx_L1_error) - __pyx_t_6 = NULL; - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - goto __pyx_L7_unpacking_done; - __pyx_L6_unpacking_failed:; - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - __pyx_t_6 = NULL; - if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index); - __PYX_ERR(0, 112, __pyx_L1_error) - __pyx_L7_unpacking_done:; - } - __pyx_v_a = __pyx_t_3; - __pyx_t_3 = 0; - __pyx_v_b = __pyx_t_4; - __pyx_t_4 = 0; - - /* "fontTools/cu2qu/cu2qu.py":113 - * if n == 4: - * a, b = split_cubic_into_two(p0, p1, p2, p3) - * return iter( # <<<<<<<<<<<<<< - * split_cubic_into_two(a[0], a[1], a[2], a[3]) - * + split_cubic_into_two(b[0], b[1], b[2], b[3]) - */ - __Pyx_XDECREF(__pyx_r); - - /* "fontTools/cu2qu/cu2qu.py":114 - * a, b = split_cubic_into_two(p0, p1, p2, p3) - * return iter( - * split_cubic_into_two(a[0], a[1], a[2], a[3]) # <<<<<<<<<<<<<< - * + split_cubic_into_two(b[0], b[1], b[2], b[3]) - * ) - */ - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_a, 0, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 114, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_7 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 114, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_a, 1, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 114, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_8 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 114, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_a, 2, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 114, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_9 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 114, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_a, 3, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 114, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_10 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 114, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_two(__pyx_t_7, __pyx_t_8, __pyx_t_9, __pyx_t_10); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 114, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - - /* "fontTools/cu2qu/cu2qu.py":115 - * return iter( - * split_cubic_into_two(a[0], a[1], a[2], a[3]) - * + split_cubic_into_two(b[0], b[1], b[2], b[3]) # <<<<<<<<<<<<<< - * ) - * if n == 6: - */ - __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_b, 0, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 115, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_10 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 115, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_b, 1, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 115, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_9 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 115, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_b, 2, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 115, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_8 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 115, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_b, 3, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 115, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_7 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 115, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_4 = __pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_two(__pyx_t_10, __pyx_t_9, __pyx_t_8, __pyx_t_7); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 115, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_3 = PyNumber_Add(__pyx_t_2, __pyx_t_4); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 115, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - - /* "fontTools/cu2qu/cu2qu.py":113 - * if n == 4: - * a, b = split_cubic_into_two(p0, p1, p2, p3) - * return iter( # <<<<<<<<<<<<<< - * split_cubic_into_two(a[0], a[1], a[2], a[3]) - * + split_cubic_into_two(b[0], b[1], b[2], b[3]) - */ - __pyx_t_4 = PyObject_GetIter(__pyx_t_3); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 113, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - __pyx_r = __pyx_t_4; - __pyx_t_4 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":111 - * if n == 3: - * return iter(split_cubic_into_three(p0, p1, p2, p3)) - * if n == 4: # <<<<<<<<<<<<<< - * a, b = split_cubic_into_two(p0, p1, p2, p3) - * return iter( - */ - } - - /* "fontTools/cu2qu/cu2qu.py":117 - * + split_cubic_into_two(b[0], b[1], b[2], b[3]) - * ) - * if n == 6: # <<<<<<<<<<<<<< - * a, b = split_cubic_into_two(p0, p1, p2, p3) - * return iter( - */ - __pyx_t_1 = (__Pyx_PyInt_BoolEqObjC(__pyx_v_n, __pyx_int_6, 6, 0)); if (unlikely((__pyx_t_1 < 0))) __PYX_ERR(0, 117, __pyx_L1_error) - if (__pyx_t_1) { - - /* "fontTools/cu2qu/cu2qu.py":118 - * ) - * if n == 6: - * a, b = split_cubic_into_two(p0, p1, p2, p3) # <<<<<<<<<<<<<< - * return iter( - * split_cubic_into_three(a[0], a[1], a[2], a[3]) - */ - __pyx_t_4 = __pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_two(__pyx_v_p0, __pyx_v_p1, __pyx_v_p2, __pyx_v_p3); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 118, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - if ((likely(PyTuple_CheckExact(__pyx_t_4))) || (PyList_CheckExact(__pyx_t_4))) { - PyObject* sequence = __pyx_t_4; - Py_ssize_t size = __Pyx_PySequence_SIZE(sequence); - if (unlikely(size != 2)) { - if (size > 2) __Pyx_RaiseTooManyValuesError(2); - else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size); - __PYX_ERR(0, 118, __pyx_L1_error) - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - if (likely(PyTuple_CheckExact(sequence))) { - __pyx_t_3 = PyTuple_GET_ITEM(sequence, 0); - __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1); - } else { - __pyx_t_3 = PyList_GET_ITEM(sequence, 0); - __pyx_t_2 = PyList_GET_ITEM(sequence, 1); - } - __Pyx_INCREF(__pyx_t_3); - __Pyx_INCREF(__pyx_t_2); - #else - __pyx_t_3 = PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 118, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_2 = PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 118, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - #endif - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - } else { - Py_ssize_t index = -1; - __pyx_t_5 = PyObject_GetIter(__pyx_t_4); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 118, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_6 = __Pyx_PyObject_GetIterNextFunc(__pyx_t_5); - index = 0; __pyx_t_3 = __pyx_t_6(__pyx_t_5); if (unlikely(!__pyx_t_3)) goto __pyx_L9_unpacking_failed; - __Pyx_GOTREF(__pyx_t_3); - index = 1; __pyx_t_2 = __pyx_t_6(__pyx_t_5); if (unlikely(!__pyx_t_2)) goto __pyx_L9_unpacking_failed; - __Pyx_GOTREF(__pyx_t_2); - if (__Pyx_IternextUnpackEndCheck(__pyx_t_6(__pyx_t_5), 2) < 0) __PYX_ERR(0, 118, __pyx_L1_error) - __pyx_t_6 = NULL; - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - goto __pyx_L10_unpacking_done; - __pyx_L9_unpacking_failed:; - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - __pyx_t_6 = NULL; - if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index); - __PYX_ERR(0, 118, __pyx_L1_error) - __pyx_L10_unpacking_done:; - } - __pyx_v_a = __pyx_t_3; - __pyx_t_3 = 0; - __pyx_v_b = __pyx_t_2; - __pyx_t_2 = 0; - - /* "fontTools/cu2qu/cu2qu.py":119 - * if n == 6: - * a, b = split_cubic_into_two(p0, p1, p2, p3) - * return iter( # <<<<<<<<<<<<<< - * split_cubic_into_three(a[0], a[1], a[2], a[3]) - * + split_cubic_into_three(b[0], b[1], b[2], b[3]) - */ - __Pyx_XDECREF(__pyx_r); - - /* "fontTools/cu2qu/cu2qu.py":120 - * a, b = split_cubic_into_two(p0, p1, p2, p3) - * return iter( - * split_cubic_into_three(a[0], a[1], a[2], a[3]) # <<<<<<<<<<<<<< - * + split_cubic_into_three(b[0], b[1], b[2], b[3]) - * ) - */ - __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_a, 0, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 120, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_7 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 120, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_a, 1, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 120, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_8 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 120, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_a, 2, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 120, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_9 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 120, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_4 = __Pyx_GetItemInt(__pyx_v_a, 3, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 120, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_10 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 120, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_4 = __pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_three(__pyx_t_7, __pyx_t_8, __pyx_t_9, __pyx_t_10); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 120, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - - /* "fontTools/cu2qu/cu2qu.py":121 - * return iter( - * split_cubic_into_three(a[0], a[1], a[2], a[3]) - * + split_cubic_into_three(b[0], b[1], b[2], b[3]) # <<<<<<<<<<<<<< - * ) - * - */ - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_b, 0, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 121, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_10 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 121, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_b, 1, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 121, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_9 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 121, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_b, 2, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 121, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_8 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 121, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_b, 3, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 121, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_7 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 121, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_three(__pyx_t_10, __pyx_t_9, __pyx_t_8, __pyx_t_7); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 121, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_3 = PyNumber_Add(__pyx_t_4, __pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 121, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - - /* "fontTools/cu2qu/cu2qu.py":119 - * if n == 6: - * a, b = split_cubic_into_two(p0, p1, p2, p3) - * return iter( # <<<<<<<<<<<<<< - * split_cubic_into_three(a[0], a[1], a[2], a[3]) - * + split_cubic_into_three(b[0], b[1], b[2], b[3]) - */ - __pyx_t_2 = PyObject_GetIter(__pyx_t_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 119, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - __pyx_r = __pyx_t_2; - __pyx_t_2 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":117 - * + split_cubic_into_two(b[0], b[1], b[2], b[3]) - * ) - * if n == 6: # <<<<<<<<<<<<<< - * a, b = split_cubic_into_two(p0, p1, p2, p3) - * return iter( - */ - } - - /* "fontTools/cu2qu/cu2qu.py":124 - * ) - * - * return _split_cubic_into_n_gen(p0, p1, p2, p3, n) # <<<<<<<<<<<<<< - * - * - */ - __Pyx_XDECREF(__pyx_r); - __Pyx_GetModuleGlobalName(__pyx_t_3, __pyx_n_s_split_cubic_into_n_gen); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 124, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_4 = __pyx_PyComplex_FromComplex(__pyx_v_p0); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 124, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_5 = __pyx_PyComplex_FromComplex(__pyx_v_p1); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 124, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __pyx_t_11 = __pyx_PyComplex_FromComplex(__pyx_v_p2); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 124, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_11); - __pyx_t_12 = __pyx_PyComplex_FromComplex(__pyx_v_p3); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 124, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_12); - __pyx_t_13 = NULL; - __pyx_t_14 = 0; - #if CYTHON_UNPACK_METHODS - if (unlikely(PyMethod_Check(__pyx_t_3))) { - __pyx_t_13 = PyMethod_GET_SELF(__pyx_t_3); - if (likely(__pyx_t_13)) { - PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_3); - __Pyx_INCREF(__pyx_t_13); - __Pyx_INCREF(function); - __Pyx_DECREF_SET(__pyx_t_3, function); - __pyx_t_14 = 1; - } - } - #endif - { - PyObject *__pyx_callargs[6] = {__pyx_t_13, __pyx_t_4, __pyx_t_5, __pyx_t_11, __pyx_t_12, __pyx_v_n}; - __pyx_t_2 = __Pyx_PyObject_FastCall(__pyx_t_3, __pyx_callargs+1-__pyx_t_14, 5+__pyx_t_14); - __Pyx_XDECREF(__pyx_t_13); __pyx_t_13 = 0; - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0; - __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0; - if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 124, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - } - __pyx_r = __pyx_t_2; - __pyx_t_2 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":85 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals( - */ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_2); - __Pyx_XDECREF(__pyx_t_3); - __Pyx_XDECREF(__pyx_t_4); - __Pyx_XDECREF(__pyx_t_5); - __Pyx_XDECREF(__pyx_t_11); - __Pyx_XDECREF(__pyx_t_12); - __Pyx_XDECREF(__pyx_t_13); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.split_cubic_into_n_iter", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = 0; - __pyx_L0:; - __Pyx_XDECREF(__pyx_v_a); - __Pyx_XDECREF(__pyx_v_b); - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} -static PyObject *__pyx_gb_9fontTools_5cu2qu_5cu2qu_2generator(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value); /* proto */ - -/* "fontTools/cu2qu/cu2qu.py":127 - * - * - * @cython.locals( # <<<<<<<<<<<<<< - * p0=cython.complex, - * p1=cython.complex, - */ - -/* Python wrapper */ -static PyObject *__pyx_pw_9fontTools_5cu2qu_5cu2qu_1_split_cubic_into_n_gen(PyObject *__pyx_self, -#if CYTHON_METH_FASTCALL -PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds -#else -PyObject *__pyx_args, PyObject *__pyx_kwds -#endif -); /*proto*/ -PyDoc_STRVAR(__pyx_doc_9fontTools_5cu2qu_5cu2qu__split_cubic_into_n_gen, "_split_cubic_into_n_gen(double complex p0, double complex p1, double complex p2, double complex p3, int n)"); -static PyMethodDef __pyx_mdef_9fontTools_5cu2qu_5cu2qu_1_split_cubic_into_n_gen = {"_split_cubic_into_n_gen", (PyCFunction)(void*)(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_5cu2qu_5cu2qu_1_split_cubic_into_n_gen, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_5cu2qu_5cu2qu__split_cubic_into_n_gen}; -static PyObject *__pyx_pw_9fontTools_5cu2qu_5cu2qu_1_split_cubic_into_n_gen(PyObject *__pyx_self, -#if CYTHON_METH_FASTCALL -PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds -#else -PyObject *__pyx_args, PyObject *__pyx_kwds -#endif -) { - __pyx_t_double_complex __pyx_v_p0; - __pyx_t_double_complex __pyx_v_p1; - __pyx_t_double_complex __pyx_v_p2; - __pyx_t_double_complex __pyx_v_p3; - int __pyx_v_n; - #if !CYTHON_METH_FASTCALL - CYTHON_UNUSED Py_ssize_t __pyx_nargs; - #endif - CYTHON_UNUSED PyObject *const *__pyx_kwvalues; - PyObject* values[5] = {0,0,0,0,0}; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - PyObject *__pyx_r = 0; - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("_split_cubic_into_n_gen (wrapper)", 0); - #if !CYTHON_METH_FASTCALL - #if CYTHON_ASSUME_SAFE_MACROS - __pyx_nargs = PyTuple_GET_SIZE(__pyx_args); - #else - __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL; - #endif - #endif - __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs); - { - PyObject **__pyx_pyargnames[] = {&__pyx_n_s_p0,&__pyx_n_s_p1,&__pyx_n_s_p2,&__pyx_n_s_p3,&__pyx_n_s_n,0}; - if (__pyx_kwds) { - Py_ssize_t kw_args; - switch (__pyx_nargs) { - case 5: values[4] = __Pyx_Arg_FASTCALL(__pyx_args, 4); - CYTHON_FALLTHROUGH; - case 4: values[3] = __Pyx_Arg_FASTCALL(__pyx_args, 3); - CYTHON_FALLTHROUGH; - case 3: values[2] = __Pyx_Arg_FASTCALL(__pyx_args, 2); - CYTHON_FALLTHROUGH; - case 2: values[1] = __Pyx_Arg_FASTCALL(__pyx_args, 1); - CYTHON_FALLTHROUGH; - case 1: values[0] = __Pyx_Arg_FASTCALL(__pyx_args, 0); - CYTHON_FALLTHROUGH; - case 0: break; - default: goto __pyx_L5_argtuple_error; - } - kw_args = __Pyx_NumKwargs_FASTCALL(__pyx_kwds); - switch (__pyx_nargs) { - case 0: - if (likely((values[0] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_p0)) != 0)) { - (void)__Pyx_Arg_NewRef_FASTCALL(values[0]); - kw_args--; - } - else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 127, __pyx_L3_error) - else goto __pyx_L5_argtuple_error; - CYTHON_FALLTHROUGH; - case 1: - if (likely((values[1] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_p1)) != 0)) { - (void)__Pyx_Arg_NewRef_FASTCALL(values[1]); - kw_args--; - } - else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 127, __pyx_L3_error) - else { - __Pyx_RaiseArgtupleInvalid("_split_cubic_into_n_gen", 1, 5, 5, 1); __PYX_ERR(0, 127, __pyx_L3_error) - } - CYTHON_FALLTHROUGH; - case 2: - if (likely((values[2] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_p2)) != 0)) { - (void)__Pyx_Arg_NewRef_FASTCALL(values[2]); - kw_args--; - } - else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 127, __pyx_L3_error) - else { - __Pyx_RaiseArgtupleInvalid("_split_cubic_into_n_gen", 1, 5, 5, 2); __PYX_ERR(0, 127, __pyx_L3_error) - } - CYTHON_FALLTHROUGH; - case 3: - if (likely((values[3] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_p3)) != 0)) { - (void)__Pyx_Arg_NewRef_FASTCALL(values[3]); - kw_args--; - } - else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 127, __pyx_L3_error) - else { - __Pyx_RaiseArgtupleInvalid("_split_cubic_into_n_gen", 1, 5, 5, 3); __PYX_ERR(0, 127, __pyx_L3_error) - } - CYTHON_FALLTHROUGH; - case 4: - if (likely((values[4] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_n)) != 0)) { - (void)__Pyx_Arg_NewRef_FASTCALL(values[4]); - kw_args--; - } - else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 127, __pyx_L3_error) - else { - __Pyx_RaiseArgtupleInvalid("_split_cubic_into_n_gen", 1, 5, 5, 4); __PYX_ERR(0, 127, __pyx_L3_error) - } - } - if (unlikely(kw_args > 0)) { - const Py_ssize_t kwd_pos_args = __pyx_nargs; - if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values + 0, kwd_pos_args, "_split_cubic_into_n_gen") < 0)) __PYX_ERR(0, 127, __pyx_L3_error) - } - } else if (unlikely(__pyx_nargs != 5)) { - goto __pyx_L5_argtuple_error; - } else { - values[0] = __Pyx_Arg_FASTCALL(__pyx_args, 0); - values[1] = __Pyx_Arg_FASTCALL(__pyx_args, 1); - values[2] = __Pyx_Arg_FASTCALL(__pyx_args, 2); - values[3] = __Pyx_Arg_FASTCALL(__pyx_args, 3); - values[4] = __Pyx_Arg_FASTCALL(__pyx_args, 4); - } - __pyx_v_p0 = __Pyx_PyComplex_As___pyx_t_double_complex(values[0]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 141, __pyx_L3_error) - __pyx_v_p1 = __Pyx_PyComplex_As___pyx_t_double_complex(values[1]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 141, __pyx_L3_error) - __pyx_v_p2 = __Pyx_PyComplex_As___pyx_t_double_complex(values[2]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 141, __pyx_L3_error) - __pyx_v_p3 = __Pyx_PyComplex_As___pyx_t_double_complex(values[3]); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 141, __pyx_L3_error) - __pyx_v_n = __Pyx_PyInt_As_int(values[4]); if (unlikely((__pyx_v_n == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 141, __pyx_L3_error) - } - goto __pyx_L6_skip; - __pyx_L5_argtuple_error:; - __Pyx_RaiseArgtupleInvalid("_split_cubic_into_n_gen", 1, 5, 5, __pyx_nargs); __PYX_ERR(0, 127, __pyx_L3_error) - __pyx_L6_skip:; - goto __pyx_L4_argument_unpacking_done; - __pyx_L3_error:; - { - Py_ssize_t __pyx_temp; - for (__pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { - __Pyx_Arg_XDECREF_FASTCALL(values[__pyx_temp]); - } - } - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu._split_cubic_into_n_gen", __pyx_clineno, __pyx_lineno, __pyx_filename); - __Pyx_RefNannyFinishContext(); - return NULL; - __pyx_L4_argument_unpacking_done:; - __pyx_r = __pyx_pf_9fontTools_5cu2qu_5cu2qu__split_cubic_into_n_gen(__pyx_self, __pyx_v_p0, __pyx_v_p1, __pyx_v_p2, __pyx_v_p3, __pyx_v_n); - - /* function exit code */ - { - Py_ssize_t __pyx_temp; - for (__pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { - __Pyx_Arg_XDECREF_FASTCALL(values[__pyx_temp]); - } - } - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -static PyObject *__pyx_pf_9fontTools_5cu2qu_5cu2qu__split_cubic_into_n_gen(CYTHON_UNUSED PyObject *__pyx_self, __pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2, __pyx_t_double_complex __pyx_v_p3, int __pyx_v_n) { - struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen *__pyx_cur_scope; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("_split_cubic_into_n_gen", 0); - __pyx_cur_scope = (struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen *)__pyx_tp_new_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen(__pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen, __pyx_empty_tuple, NULL); - if (unlikely(!__pyx_cur_scope)) { - __pyx_cur_scope = ((struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen *)Py_None); - __Pyx_INCREF(Py_None); - __PYX_ERR(0, 127, __pyx_L1_error) - } else { - __Pyx_GOTREF((PyObject *)__pyx_cur_scope); - } - __pyx_cur_scope->__pyx_v_p0 = __pyx_v_p0; - __pyx_cur_scope->__pyx_v_p1 = __pyx_v_p1; - __pyx_cur_scope->__pyx_v_p2 = __pyx_v_p2; - __pyx_cur_scope->__pyx_v_p3 = __pyx_v_p3; - __pyx_cur_scope->__pyx_v_n = __pyx_v_n; - { - __pyx_CoroutineObject *gen = __Pyx_Generator_New((__pyx_coroutine_body_t) __pyx_gb_9fontTools_5cu2qu_5cu2qu_2generator, __pyx_codeobj_, (PyObject *) __pyx_cur_scope, __pyx_n_s_split_cubic_into_n_gen, __pyx_n_s_split_cubic_into_n_gen, __pyx_n_s_fontTools_cu2qu_cu2qu); if (unlikely(!gen)) __PYX_ERR(0, 127, __pyx_L1_error) - __Pyx_DECREF(__pyx_cur_scope); - __Pyx_RefNannyFinishContext(); - return (PyObject *) gen; - } - - /* function exit code */ - __pyx_L1_error:; - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu._split_cubic_into_n_gen", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = NULL; - __Pyx_DECREF((PyObject *)__pyx_cur_scope); - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -static PyObject *__pyx_gb_9fontTools_5cu2qu_5cu2qu_2generator(__pyx_CoroutineObject *__pyx_generator, CYTHON_UNUSED PyThreadState *__pyx_tstate, PyObject *__pyx_sent_value) /* generator body */ -{ - struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen *__pyx_cur_scope = ((struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen *)__pyx_generator->closure); - PyObject *__pyx_r = NULL; - PyObject *__pyx_t_1 = NULL; - PyObject *__pyx_t_2 = NULL; - PyObject *__pyx_t_3 = NULL; - PyObject *__pyx_t_4 = NULL; - PyObject *__pyx_t_5 = NULL; - PyObject *__pyx_t_6 = NULL; - PyObject *(*__pyx_t_7)(PyObject *); - __pyx_t_double_complex __pyx_t_8; - __pyx_t_double_complex __pyx_t_9; - __pyx_t_double_complex __pyx_t_10; - __pyx_t_double_complex __pyx_t_11; - int __pyx_t_12; - int __pyx_t_13; - int __pyx_t_14; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("_split_cubic_into_n_gen", 0); - switch (__pyx_generator->resume_label) { - case 0: goto __pyx_L3_first_run; - case 1: goto __pyx_L8_resume_from_yield; - default: /* CPython raises the right error here */ - __Pyx_RefNannyFinishContext(); - return NULL; - } - __pyx_L3_first_run:; - if (unlikely(!__pyx_sent_value)) __PYX_ERR(0, 127, __pyx_L1_error) - - /* "fontTools/cu2qu/cu2qu.py":142 - * ) - * def _split_cubic_into_n_gen(p0, p1, p2, p3, n): - * a, b, c, d = calc_cubic_parameters(p0, p1, p2, p3) # <<<<<<<<<<<<<< - * dt = 1 / n - * delta_2 = dt * dt - */ - __pyx_t_1 = __pyx_f_9fontTools_5cu2qu_5cu2qu_calc_cubic_parameters(__pyx_cur_scope->__pyx_v_p0, __pyx_cur_scope->__pyx_v_p1, __pyx_cur_scope->__pyx_v_p2, __pyx_cur_scope->__pyx_v_p3); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 142, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - if ((likely(PyTuple_CheckExact(__pyx_t_1))) || (PyList_CheckExact(__pyx_t_1))) { - PyObject* sequence = __pyx_t_1; - Py_ssize_t size = __Pyx_PySequence_SIZE(sequence); - if (unlikely(size != 4)) { - if (size > 4) __Pyx_RaiseTooManyValuesError(4); - else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size); - __PYX_ERR(0, 142, __pyx_L1_error) - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - if (likely(PyTuple_CheckExact(sequence))) { - __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0); - __pyx_t_3 = PyTuple_GET_ITEM(sequence, 1); - __pyx_t_4 = PyTuple_GET_ITEM(sequence, 2); - __pyx_t_5 = PyTuple_GET_ITEM(sequence, 3); - } else { - __pyx_t_2 = PyList_GET_ITEM(sequence, 0); - __pyx_t_3 = PyList_GET_ITEM(sequence, 1); - __pyx_t_4 = PyList_GET_ITEM(sequence, 2); - __pyx_t_5 = PyList_GET_ITEM(sequence, 3); - } - __Pyx_INCREF(__pyx_t_2); - __Pyx_INCREF(__pyx_t_3); - __Pyx_INCREF(__pyx_t_4); - __Pyx_INCREF(__pyx_t_5); - #else - { - Py_ssize_t i; - PyObject** temps[4] = {&__pyx_t_2,&__pyx_t_3,&__pyx_t_4,&__pyx_t_5}; - for (i=0; i < 4; i++) { - PyObject* item = PySequence_ITEM(sequence, i); if (unlikely(!item)) __PYX_ERR(0, 142, __pyx_L1_error) - __Pyx_GOTREF(item); - *(temps[i]) = item; - } - } - #endif - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - } else { - Py_ssize_t index = -1; - PyObject** temps[4] = {&__pyx_t_2,&__pyx_t_3,&__pyx_t_4,&__pyx_t_5}; - __pyx_t_6 = PyObject_GetIter(__pyx_t_1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 142, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_t_7 = __Pyx_PyObject_GetIterNextFunc(__pyx_t_6); - for (index=0; index < 4; index++) { - PyObject* item = __pyx_t_7(__pyx_t_6); if (unlikely(!item)) goto __pyx_L4_unpacking_failed; - __Pyx_GOTREF(item); - *(temps[index]) = item; - } - if (__Pyx_IternextUnpackEndCheck(__pyx_t_7(__pyx_t_6), 4) < 0) __PYX_ERR(0, 142, __pyx_L1_error) - __pyx_t_7 = NULL; - __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; - goto __pyx_L5_unpacking_done; - __pyx_L4_unpacking_failed:; - __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; - __pyx_t_7 = NULL; - if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index); - __PYX_ERR(0, 142, __pyx_L1_error) - __pyx_L5_unpacking_done:; - } - __pyx_t_8 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 142, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_9 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_3); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 142, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0; - __pyx_t_10 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 142, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0; - __pyx_t_11 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_5); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 142, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - __pyx_cur_scope->__pyx_v_a = __pyx_t_8; - __pyx_cur_scope->__pyx_v_b = __pyx_t_9; - __pyx_cur_scope->__pyx_v_c = __pyx_t_10; - __pyx_cur_scope->__pyx_v_d = __pyx_t_11; - - /* "fontTools/cu2qu/cu2qu.py":143 - * def _split_cubic_into_n_gen(p0, p1, p2, p3, n): - * a, b, c, d = calc_cubic_parameters(p0, p1, p2, p3) - * dt = 1 / n # <<<<<<<<<<<<<< - * delta_2 = dt * dt - * delta_3 = dt * delta_2 - */ - if (unlikely(__pyx_cur_scope->__pyx_v_n == 0)) { - PyErr_SetString(PyExc_ZeroDivisionError, "float division"); - __PYX_ERR(0, 143, __pyx_L1_error) - } - __pyx_cur_scope->__pyx_v_dt = (1.0 / ((double)__pyx_cur_scope->__pyx_v_n)); - - /* "fontTools/cu2qu/cu2qu.py":144 - * a, b, c, d = calc_cubic_parameters(p0, p1, p2, p3) - * dt = 1 / n - * delta_2 = dt * dt # <<<<<<<<<<<<<< - * delta_3 = dt * delta_2 - * for i in range(n): - */ - __pyx_cur_scope->__pyx_v_delta_2 = (__pyx_cur_scope->__pyx_v_dt * __pyx_cur_scope->__pyx_v_dt); - - /* "fontTools/cu2qu/cu2qu.py":145 - * dt = 1 / n - * delta_2 = dt * dt - * delta_3 = dt * delta_2 # <<<<<<<<<<<<<< - * for i in range(n): - * t1 = i * dt - */ - __pyx_cur_scope->__pyx_v_delta_3 = (__pyx_cur_scope->__pyx_v_dt * __pyx_cur_scope->__pyx_v_delta_2); - - /* "fontTools/cu2qu/cu2qu.py":146 - * delta_2 = dt * dt - * delta_3 = dt * delta_2 - * for i in range(n): # <<<<<<<<<<<<<< - * t1 = i * dt - * t1_2 = t1 * t1 - */ - __pyx_t_12 = __pyx_cur_scope->__pyx_v_n; - __pyx_t_13 = __pyx_t_12; - for (__pyx_t_14 = 0; __pyx_t_14 < __pyx_t_13; __pyx_t_14+=1) { - __pyx_cur_scope->__pyx_v_i = __pyx_t_14; - - /* "fontTools/cu2qu/cu2qu.py":147 - * delta_3 = dt * delta_2 - * for i in range(n): - * t1 = i * dt # <<<<<<<<<<<<<< - * t1_2 = t1 * t1 - * # calc new a, b, c and d - */ - __pyx_cur_scope->__pyx_v_t1 = (__pyx_cur_scope->__pyx_v_i * __pyx_cur_scope->__pyx_v_dt); - - /* "fontTools/cu2qu/cu2qu.py":148 - * for i in range(n): - * t1 = i * dt - * t1_2 = t1 * t1 # <<<<<<<<<<<<<< - * # calc new a, b, c and d - * a1 = a * delta_3 - */ - __pyx_cur_scope->__pyx_v_t1_2 = (__pyx_cur_scope->__pyx_v_t1 * __pyx_cur_scope->__pyx_v_t1); - - /* "fontTools/cu2qu/cu2qu.py":150 - * t1_2 = t1 * t1 - * # calc new a, b, c and d - * a1 = a * delta_3 # <<<<<<<<<<<<<< - * b1 = (3 * a * t1 + b) * delta_2 - * c1 = (2 * b * t1 + c + 3 * a * t1_2) * dt - */ - __pyx_cur_scope->__pyx_v_a1 = __Pyx_c_prod_double(__pyx_cur_scope->__pyx_v_a, __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_delta_3, 0)); - - /* "fontTools/cu2qu/cu2qu.py":151 - * # calc new a, b, c and d - * a1 = a * delta_3 - * b1 = (3 * a * t1 + b) * delta_2 # <<<<<<<<<<<<<< - * c1 = (2 * b * t1 + c + 3 * a * t1_2) * dt - * d1 = a * t1 * t1_2 + b * t1_2 + c * t1 + d - */ - __pyx_cur_scope->__pyx_v_b1 = __Pyx_c_prod_double(__Pyx_c_sum_double(__Pyx_c_prod_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(3, 0), __pyx_cur_scope->__pyx_v_a), __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_t1, 0)), __pyx_cur_scope->__pyx_v_b), __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_delta_2, 0)); - - /* "fontTools/cu2qu/cu2qu.py":152 - * a1 = a * delta_3 - * b1 = (3 * a * t1 + b) * delta_2 - * c1 = (2 * b * t1 + c + 3 * a * t1_2) * dt # <<<<<<<<<<<<<< - * d1 = a * t1 * t1_2 + b * t1_2 + c * t1 + d - * yield calc_cubic_points(a1, b1, c1, d1) - */ - __pyx_cur_scope->__pyx_v_c1 = __Pyx_c_prod_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_prod_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(2, 0), __pyx_cur_scope->__pyx_v_b), __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_t1, 0)), __pyx_cur_scope->__pyx_v_c), __Pyx_c_prod_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(3, 0), __pyx_cur_scope->__pyx_v_a), __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_t1_2, 0))), __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_dt, 0)); - - /* "fontTools/cu2qu/cu2qu.py":153 - * b1 = (3 * a * t1 + b) * delta_2 - * c1 = (2 * b * t1 + c + 3 * a * t1_2) * dt - * d1 = a * t1 * t1_2 + b * t1_2 + c * t1 + d # <<<<<<<<<<<<<< - * yield calc_cubic_points(a1, b1, c1, d1) - * - */ - __pyx_cur_scope->__pyx_v_d1 = __Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_prod_double(__Pyx_c_prod_double(__pyx_cur_scope->__pyx_v_a, __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_t1, 0)), __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_t1_2, 0)), __Pyx_c_prod_double(__pyx_cur_scope->__pyx_v_b, __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_t1_2, 0))), __Pyx_c_prod_double(__pyx_cur_scope->__pyx_v_c, __pyx_t_double_complex_from_parts(__pyx_cur_scope->__pyx_v_t1, 0))), __pyx_cur_scope->__pyx_v_d); - - /* "fontTools/cu2qu/cu2qu.py":154 - * c1 = (2 * b * t1 + c + 3 * a * t1_2) * dt - * d1 = a * t1 * t1_2 + b * t1_2 + c * t1 + d - * yield calc_cubic_points(a1, b1, c1, d1) # <<<<<<<<<<<<<< - * - * - */ - __pyx_t_1 = __pyx_f_9fontTools_5cu2qu_5cu2qu_calc_cubic_points(__pyx_cur_scope->__pyx_v_a1, __pyx_cur_scope->__pyx_v_b1, __pyx_cur_scope->__pyx_v_c1, __pyx_cur_scope->__pyx_v_d1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 154, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_r = __pyx_t_1; - __pyx_t_1 = 0; - __pyx_cur_scope->__pyx_t_0 = __pyx_t_12; - __pyx_cur_scope->__pyx_t_1 = __pyx_t_13; - __pyx_cur_scope->__pyx_t_2 = __pyx_t_14; - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - __Pyx_Coroutine_ResetAndClearException(__pyx_generator); - /* return from generator, yielding value */ - __pyx_generator->resume_label = 1; - return __pyx_r; - __pyx_L8_resume_from_yield:; - __pyx_t_12 = __pyx_cur_scope->__pyx_t_0; - __pyx_t_13 = __pyx_cur_scope->__pyx_t_1; - __pyx_t_14 = __pyx_cur_scope->__pyx_t_2; - if (unlikely(!__pyx_sent_value)) __PYX_ERR(0, 154, __pyx_L1_error) - } - CYTHON_MAYBE_UNUSED_VAR(__pyx_cur_scope); - - /* "fontTools/cu2qu/cu2qu.py":127 - * - * - * @cython.locals( # <<<<<<<<<<<<<< - * p0=cython.complex, - * p1=cython.complex, - */ - - /* function exit code */ - PyErr_SetNone(PyExc_StopIteration); - goto __pyx_L0; - __pyx_L1_error:; - __Pyx_Generator_Replace_StopIteration(0); - __Pyx_XDECREF(__pyx_t_1); - __Pyx_XDECREF(__pyx_t_2); - __Pyx_XDECREF(__pyx_t_3); - __Pyx_XDECREF(__pyx_t_4); - __Pyx_XDECREF(__pyx_t_5); - __Pyx_XDECREF(__pyx_t_6); - __Pyx_AddTraceback("_split_cubic_into_n_gen", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_L0:; - __Pyx_XDECREF(__pyx_r); __pyx_r = 0; - #if !CYTHON_USE_EXC_INFO_STACK - __Pyx_Coroutine_ResetAndClearException(__pyx_generator); - #endif - __pyx_generator->resume_label = -1; - __Pyx_Coroutine_clear((PyObject*)__pyx_generator); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":157 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals( - */ - -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_two(__pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2, __pyx_t_double_complex __pyx_v_p3) { - __pyx_t_double_complex __pyx_v_mid; - __pyx_t_double_complex __pyx_v_deriv3; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - PyObject *__pyx_t_1 = NULL; - __pyx_t_double_complex __pyx_t_2; - PyObject *__pyx_t_3 = NULL; - PyObject *__pyx_t_4 = NULL; - PyObject *__pyx_t_5 = NULL; - PyObject *__pyx_t_6 = NULL; - PyObject *__pyx_t_7 = NULL; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("split_cubic_into_two", 1); - - /* "fontTools/cu2qu/cu2qu.py":178 - * values). - * """ - * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 # <<<<<<<<<<<<<< - * deriv3 = (p3 + p2 - p1 - p0) * 0.125 - * return ( - */ - __pyx_v_mid = __Pyx_c_prod_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__pyx_v_p0, __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(3, 0), __Pyx_c_sum_double(__pyx_v_p1, __pyx_v_p2))), __pyx_v_p3), __pyx_t_double_complex_from_parts(0.125, 0)); - - /* "fontTools/cu2qu/cu2qu.py":179 - * """ - * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 - * deriv3 = (p3 + p2 - p1 - p0) * 0.125 # <<<<<<<<<<<<<< - * return ( - * (p0, (p0 + p1) * 0.5, mid - deriv3, mid), - */ - __pyx_v_deriv3 = __Pyx_c_prod_double(__Pyx_c_diff_double(__Pyx_c_diff_double(__Pyx_c_sum_double(__pyx_v_p3, __pyx_v_p2), __pyx_v_p1), __pyx_v_p0), __pyx_t_double_complex_from_parts(0.125, 0)); - - /* "fontTools/cu2qu/cu2qu.py":180 - * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 - * deriv3 = (p3 + p2 - p1 - p0) * 0.125 - * return ( # <<<<<<<<<<<<<< - * (p0, (p0 + p1) * 0.5, mid - deriv3, mid), - * (mid, mid + deriv3, (p2 + p3) * 0.5, p3), - */ - __Pyx_XDECREF(__pyx_r); - - /* "fontTools/cu2qu/cu2qu.py":181 - * deriv3 = (p3 + p2 - p1 - p0) * 0.125 - * return ( - * (p0, (p0 + p1) * 0.5, mid - deriv3, mid), # <<<<<<<<<<<<<< - * (mid, mid + deriv3, (p2 + p3) * 0.5, p3), - * ) - */ - __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_p0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 181, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_2 = __Pyx_c_prod_double(__Pyx_c_sum_double(__pyx_v_p0, __pyx_v_p1), __pyx_t_double_complex_from_parts(0.5, 0)); - __pyx_t_3 = __pyx_PyComplex_FromComplex(__pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 181, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_2 = __Pyx_c_diff_double(__pyx_v_mid, __pyx_v_deriv3); - __pyx_t_4 = __pyx_PyComplex_FromComplex(__pyx_t_2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 181, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_5 = __pyx_PyComplex_FromComplex(__pyx_v_mid); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 181, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __pyx_t_6 = PyTuple_New(4); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 181, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - __Pyx_GIVEREF(__pyx_t_1); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 0, __pyx_t_1)) __PYX_ERR(0, 181, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_3); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 1, __pyx_t_3)) __PYX_ERR(0, 181, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_4); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 2, __pyx_t_4)) __PYX_ERR(0, 181, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_5); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_6, 3, __pyx_t_5)) __PYX_ERR(0, 181, __pyx_L1_error); - __pyx_t_1 = 0; - __pyx_t_3 = 0; - __pyx_t_4 = 0; - __pyx_t_5 = 0; - - /* "fontTools/cu2qu/cu2qu.py":182 - * return ( - * (p0, (p0 + p1) * 0.5, mid - deriv3, mid), - * (mid, mid + deriv3, (p2 + p3) * 0.5, p3), # <<<<<<<<<<<<<< - * ) - * - */ - __pyx_t_5 = __pyx_PyComplex_FromComplex(__pyx_v_mid); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 182, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __pyx_t_2 = __Pyx_c_sum_double(__pyx_v_mid, __pyx_v_deriv3); - __pyx_t_4 = __pyx_PyComplex_FromComplex(__pyx_t_2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 182, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_4); - __pyx_t_2 = __Pyx_c_prod_double(__Pyx_c_sum_double(__pyx_v_p2, __pyx_v_p3), __pyx_t_double_complex_from_parts(0.5, 0)); - __pyx_t_3 = __pyx_PyComplex_FromComplex(__pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 182, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_3); - __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_p3); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 182, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_7 = PyTuple_New(4); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 182, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - __Pyx_GIVEREF(__pyx_t_5); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 0, __pyx_t_5)) __PYX_ERR(0, 182, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_4); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 1, __pyx_t_4)) __PYX_ERR(0, 182, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_3); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 2, __pyx_t_3)) __PYX_ERR(0, 182, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_1); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 3, __pyx_t_1)) __PYX_ERR(0, 182, __pyx_L1_error); - __pyx_t_5 = 0; - __pyx_t_4 = 0; - __pyx_t_3 = 0; - __pyx_t_1 = 0; - - /* "fontTools/cu2qu/cu2qu.py":181 - * deriv3 = (p3 + p2 - p1 - p0) * 0.125 - * return ( - * (p0, (p0 + p1) * 0.5, mid - deriv3, mid), # <<<<<<<<<<<<<< - * (mid, mid + deriv3, (p2 + p3) * 0.5, p3), - * ) - */ - __pyx_t_1 = PyTuple_New(2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 181, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __Pyx_GIVEREF(__pyx_t_6); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_6)) __PYX_ERR(0, 181, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_7); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_t_7)) __PYX_ERR(0, 181, __pyx_L1_error); - __pyx_t_6 = 0; - __pyx_t_7 = 0; - __pyx_r = __pyx_t_1; - __pyx_t_1 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":157 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals( - */ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_1); - __Pyx_XDECREF(__pyx_t_3); - __Pyx_XDECREF(__pyx_t_4); - __Pyx_XDECREF(__pyx_t_5); - __Pyx_XDECREF(__pyx_t_6); - __Pyx_XDECREF(__pyx_t_7); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.split_cubic_into_two", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = 0; - __pyx_L0:; - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":186 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals( - */ - -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_three(__pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2, __pyx_t_double_complex __pyx_v_p3) { - __pyx_t_double_complex __pyx_v_mid1; - __pyx_t_double_complex __pyx_v_deriv1; - __pyx_t_double_complex __pyx_v_mid2; - __pyx_t_double_complex __pyx_v_deriv2; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - PyObject *__pyx_t_1 = NULL; - __pyx_t_double_complex __pyx_t_2; - __pyx_t_double_complex __pyx_t_3; - __pyx_t_double_complex __pyx_t_4; - PyObject *__pyx_t_5 = NULL; - PyObject *__pyx_t_6 = NULL; - PyObject *__pyx_t_7 = NULL; - PyObject *__pyx_t_8 = NULL; - PyObject *__pyx_t_9 = NULL; - PyObject *__pyx_t_10 = NULL; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("split_cubic_into_three", 1); - - /* "fontTools/cu2qu/cu2qu.py":215 - * values). - * """ - * mid1 = (8 * p0 + 12 * p1 + 6 * p2 + p3) * (1 / 27) # <<<<<<<<<<<<<< - * deriv1 = (p3 + 3 * p2 - 4 * p0) * (1 / 27) - * mid2 = (p0 + 6 * p1 + 12 * p2 + 8 * p3) * (1 / 27) - */ - __pyx_v_mid1 = __Pyx_c_prod_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(8, 0), __pyx_v_p0), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(12, 0), __pyx_v_p1)), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(6, 0), __pyx_v_p2)), __pyx_v_p3), __pyx_t_double_complex_from_parts((1.0 / 27.0), 0)); - - /* "fontTools/cu2qu/cu2qu.py":216 - * """ - * mid1 = (8 * p0 + 12 * p1 + 6 * p2 + p3) * (1 / 27) - * deriv1 = (p3 + 3 * p2 - 4 * p0) * (1 / 27) # <<<<<<<<<<<<<< - * mid2 = (p0 + 6 * p1 + 12 * p2 + 8 * p3) * (1 / 27) - * deriv2 = (4 * p3 - 3 * p1 - p0) * (1 / 27) - */ - __pyx_v_deriv1 = __Pyx_c_prod_double(__Pyx_c_diff_double(__Pyx_c_sum_double(__pyx_v_p3, __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(3, 0), __pyx_v_p2)), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(4, 0), __pyx_v_p0)), __pyx_t_double_complex_from_parts((1.0 / 27.0), 0)); - - /* "fontTools/cu2qu/cu2qu.py":217 - * mid1 = (8 * p0 + 12 * p1 + 6 * p2 + p3) * (1 / 27) - * deriv1 = (p3 + 3 * p2 - 4 * p0) * (1 / 27) - * mid2 = (p0 + 6 * p1 + 12 * p2 + 8 * p3) * (1 / 27) # <<<<<<<<<<<<<< - * deriv2 = (4 * p3 - 3 * p1 - p0) * (1 / 27) - * return ( - */ - __pyx_v_mid2 = __Pyx_c_prod_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__pyx_v_p0, __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(6, 0), __pyx_v_p1)), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(12, 0), __pyx_v_p2)), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(8, 0), __pyx_v_p3)), __pyx_t_double_complex_from_parts((1.0 / 27.0), 0)); - - /* "fontTools/cu2qu/cu2qu.py":218 - * deriv1 = (p3 + 3 * p2 - 4 * p0) * (1 / 27) - * mid2 = (p0 + 6 * p1 + 12 * p2 + 8 * p3) * (1 / 27) - * deriv2 = (4 * p3 - 3 * p1 - p0) * (1 / 27) # <<<<<<<<<<<<<< - * return ( - * (p0, (2 * p0 + p1) / 3.0, mid1 - deriv1, mid1), - */ - __pyx_v_deriv2 = __Pyx_c_prod_double(__Pyx_c_diff_double(__Pyx_c_diff_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(4, 0), __pyx_v_p3), __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(3, 0), __pyx_v_p1)), __pyx_v_p0), __pyx_t_double_complex_from_parts((1.0 / 27.0), 0)); - - /* "fontTools/cu2qu/cu2qu.py":219 - * mid2 = (p0 + 6 * p1 + 12 * p2 + 8 * p3) * (1 / 27) - * deriv2 = (4 * p3 - 3 * p1 - p0) * (1 / 27) - * return ( # <<<<<<<<<<<<<< - * (p0, (2 * p0 + p1) / 3.0, mid1 - deriv1, mid1), - * (mid1, mid1 + deriv1, mid2 - deriv2, mid2), - */ - __Pyx_XDECREF(__pyx_r); - - /* "fontTools/cu2qu/cu2qu.py":220 - * deriv2 = (4 * p3 - 3 * p1 - p0) * (1 / 27) - * return ( - * (p0, (2 * p0 + p1) / 3.0, mid1 - deriv1, mid1), # <<<<<<<<<<<<<< - * (mid1, mid1 + deriv1, mid2 - deriv2, mid2), - * (mid2, mid2 + deriv2, (p2 + 2 * p3) / 3.0, p3), - */ - __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_p0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 220, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_2 = __Pyx_c_sum_double(__Pyx_c_prod_double(__pyx_t_double_complex_from_parts(2, 0), __pyx_v_p0), __pyx_v_p1); - __pyx_t_3 = __pyx_t_double_complex_from_parts(3.0, 0); - if (unlikely(__Pyx_c_is_zero_double(__pyx_t_3))) { - PyErr_SetString(PyExc_ZeroDivisionError, "float division"); - __PYX_ERR(0, 220, __pyx_L1_error) - } - __pyx_t_4 = __Pyx_c_quot_double(__pyx_t_2, __pyx_t_3); - __pyx_t_5 = __pyx_PyComplex_FromComplex(__pyx_t_4); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 220, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __pyx_t_4 = __Pyx_c_diff_double(__pyx_v_mid1, __pyx_v_deriv1); - __pyx_t_6 = __pyx_PyComplex_FromComplex(__pyx_t_4); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 220, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - __pyx_t_7 = __pyx_PyComplex_FromComplex(__pyx_v_mid1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 220, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - __pyx_t_8 = PyTuple_New(4); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 220, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __Pyx_GIVEREF(__pyx_t_1); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_8, 0, __pyx_t_1)) __PYX_ERR(0, 220, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_5); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_8, 1, __pyx_t_5)) __PYX_ERR(0, 220, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_6); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_8, 2, __pyx_t_6)) __PYX_ERR(0, 220, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_7); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_8, 3, __pyx_t_7)) __PYX_ERR(0, 220, __pyx_L1_error); - __pyx_t_1 = 0; - __pyx_t_5 = 0; - __pyx_t_6 = 0; - __pyx_t_7 = 0; - - /* "fontTools/cu2qu/cu2qu.py":221 - * return ( - * (p0, (2 * p0 + p1) / 3.0, mid1 - deriv1, mid1), - * (mid1, mid1 + deriv1, mid2 - deriv2, mid2), # <<<<<<<<<<<<<< - * (mid2, mid2 + deriv2, (p2 + 2 * p3) / 3.0, p3), - * ) - */ - __pyx_t_7 = __pyx_PyComplex_FromComplex(__pyx_v_mid1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 221, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - __pyx_t_4 = __Pyx_c_sum_double(__pyx_v_mid1, __pyx_v_deriv1); - __pyx_t_6 = __pyx_PyComplex_FromComplex(__pyx_t_4); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 221, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - __pyx_t_4 = __Pyx_c_diff_double(__pyx_v_mid2, __pyx_v_deriv2); - __pyx_t_5 = __pyx_PyComplex_FromComplex(__pyx_t_4); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 221, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_mid2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 221, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_9 = PyTuple_New(4); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 221, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_9); - __Pyx_GIVEREF(__pyx_t_7); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_9, 0, __pyx_t_7)) __PYX_ERR(0, 221, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_6); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_9, 1, __pyx_t_6)) __PYX_ERR(0, 221, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_5); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_9, 2, __pyx_t_5)) __PYX_ERR(0, 221, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_1); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_9, 3, __pyx_t_1)) __PYX_ERR(0, 221, __pyx_L1_error); - __pyx_t_7 = 0; - __pyx_t_6 = 0; - __pyx_t_5 = 0; - __pyx_t_1 = 0; - - /* "fontTools/cu2qu/cu2qu.py":222 - * (p0, (2 * p0 + p1) / 3.0, mid1 - deriv1, mid1), - * (mid1, mid1 + deriv1, mid2 - deriv2, mid2), - * (mid2, mid2 + deriv2, (p2 + 2 * p3) / 3.0, p3), # <<<<<<<<<<<<<< - * ) - * - */ - __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_mid2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 222, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_4 = __Pyx_c_sum_double(__pyx_v_mid2, __pyx_v_deriv2); - __pyx_t_5 = __pyx_PyComplex_FromComplex(__pyx_t_4); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 222, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_5); - __pyx_t_4 = __Pyx_c_sum_double(__pyx_v_p2, __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(2, 0), __pyx_v_p3)); - __pyx_t_3 = __pyx_t_double_complex_from_parts(3.0, 0); - if (unlikely(__Pyx_c_is_zero_double(__pyx_t_3))) { - PyErr_SetString(PyExc_ZeroDivisionError, "float division"); - __PYX_ERR(0, 222, __pyx_L1_error) - } - __pyx_t_2 = __Pyx_c_quot_double(__pyx_t_4, __pyx_t_3); - __pyx_t_6 = __pyx_PyComplex_FromComplex(__pyx_t_2); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 222, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - __pyx_t_7 = __pyx_PyComplex_FromComplex(__pyx_v_p3); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 222, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - __pyx_t_10 = PyTuple_New(4); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 222, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_10); - __Pyx_GIVEREF(__pyx_t_1); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_10, 0, __pyx_t_1)) __PYX_ERR(0, 222, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_5); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_10, 1, __pyx_t_5)) __PYX_ERR(0, 222, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_6); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_10, 2, __pyx_t_6)) __PYX_ERR(0, 222, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_7); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_10, 3, __pyx_t_7)) __PYX_ERR(0, 222, __pyx_L1_error); - __pyx_t_1 = 0; - __pyx_t_5 = 0; - __pyx_t_6 = 0; - __pyx_t_7 = 0; - - /* "fontTools/cu2qu/cu2qu.py":220 - * deriv2 = (4 * p3 - 3 * p1 - p0) * (1 / 27) - * return ( - * (p0, (2 * p0 + p1) / 3.0, mid1 - deriv1, mid1), # <<<<<<<<<<<<<< - * (mid1, mid1 + deriv1, mid2 - deriv2, mid2), - * (mid2, mid2 + deriv2, (p2 + 2 * p3) / 3.0, p3), - */ - __pyx_t_7 = PyTuple_New(3); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 220, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - __Pyx_GIVEREF(__pyx_t_8); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 0, __pyx_t_8)) __PYX_ERR(0, 220, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_9); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 1, __pyx_t_9)) __PYX_ERR(0, 220, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_10); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 2, __pyx_t_10)) __PYX_ERR(0, 220, __pyx_L1_error); - __pyx_t_8 = 0; - __pyx_t_9 = 0; - __pyx_t_10 = 0; - __pyx_r = __pyx_t_7; - __pyx_t_7 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":186 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals( - */ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_1); - __Pyx_XDECREF(__pyx_t_5); - __Pyx_XDECREF(__pyx_t_6); - __Pyx_XDECREF(__pyx_t_7); - __Pyx_XDECREF(__pyx_t_8); - __Pyx_XDECREF(__pyx_t_9); - __Pyx_XDECREF(__pyx_t_10); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.split_cubic_into_three", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = 0; - __pyx_L0:; - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":226 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.returns(cython.complex) - */ - -static CYTHON_INLINE __pyx_t_double_complex __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_control(double __pyx_v_t, __pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2, __pyx_t_double_complex __pyx_v_p3) { - __pyx_t_double_complex __pyx_v__p1; - __pyx_t_double_complex __pyx_v__p2; - __pyx_t_double_complex __pyx_r; - - /* "fontTools/cu2qu/cu2qu.py":250 - * complex: Location of candidate control point on quadratic curve. - * """ - * _p1 = p0 + (p1 - p0) * 1.5 # <<<<<<<<<<<<<< - * _p2 = p3 + (p2 - p3) * 1.5 - * return _p1 + (_p2 - _p1) * t - */ - __pyx_v__p1 = __Pyx_c_sum_double(__pyx_v_p0, __Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_p1, __pyx_v_p0), __pyx_t_double_complex_from_parts(1.5, 0))); - - /* "fontTools/cu2qu/cu2qu.py":251 - * """ - * _p1 = p0 + (p1 - p0) * 1.5 - * _p2 = p3 + (p2 - p3) * 1.5 # <<<<<<<<<<<<<< - * return _p1 + (_p2 - _p1) * t - * - */ - __pyx_v__p2 = __Pyx_c_sum_double(__pyx_v_p3, __Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_p2, __pyx_v_p3), __pyx_t_double_complex_from_parts(1.5, 0))); - - /* "fontTools/cu2qu/cu2qu.py":252 - * _p1 = p0 + (p1 - p0) * 1.5 - * _p2 = p3 + (p2 - p3) * 1.5 - * return _p1 + (_p2 - _p1) * t # <<<<<<<<<<<<<< - * - * - */ - __pyx_r = __Pyx_c_sum_double(__pyx_v__p1, __Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v__p2, __pyx_v__p1), __pyx_t_double_complex_from_parts(__pyx_v_t, 0))); - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":226 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.returns(cython.complex) - */ - - /* function exit code */ - __pyx_L0:; - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":255 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.returns(cython.complex) - */ - -static CYTHON_INLINE __pyx_t_double_complex __pyx_f_9fontTools_5cu2qu_5cu2qu_calc_intersect(__pyx_t_double_complex __pyx_v_a, __pyx_t_double_complex __pyx_v_b, __pyx_t_double_complex __pyx_v_c, __pyx_t_double_complex __pyx_v_d) { - __pyx_t_double_complex __pyx_v_ab; - __pyx_t_double_complex __pyx_v_cd; - __pyx_t_double_complex __pyx_v_p; - double __pyx_v_h; - __pyx_t_double_complex __pyx_r; - __Pyx_RefNannyDeclarations - PyObject *__pyx_t_1 = NULL; - PyObject *__pyx_t_2 = NULL; - PyObject *__pyx_t_3 = NULL; - double __pyx_t_4; - double __pyx_t_5; - int __pyx_t_6; - PyObject *__pyx_t_7 = NULL; - PyObject *__pyx_t_8 = NULL; - PyObject *__pyx_t_9 = NULL; - PyObject *__pyx_t_10 = NULL; - PyObject *__pyx_t_11 = NULL; - PyObject *__pyx_t_12 = NULL; - __pyx_t_double_complex __pyx_t_13; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("calc_intersect", 1); - - /* "fontTools/cu2qu/cu2qu.py":273 - * if no intersection was found. - * """ - * ab = b - a # <<<<<<<<<<<<<< - * cd = d - c - * p = ab * 1j - */ - __pyx_v_ab = __Pyx_c_diff_double(__pyx_v_b, __pyx_v_a); - - /* "fontTools/cu2qu/cu2qu.py":274 - * """ - * ab = b - a - * cd = d - c # <<<<<<<<<<<<<< - * p = ab * 1j - * try: - */ - __pyx_v_cd = __Pyx_c_diff_double(__pyx_v_d, __pyx_v_c); - - /* "fontTools/cu2qu/cu2qu.py":275 - * ab = b - a - * cd = d - c - * p = ab * 1j # <<<<<<<<<<<<<< - * try: - * h = dot(p, a - c) / dot(p, cd) - */ - __pyx_v_p = __Pyx_c_prod_double(__pyx_v_ab, __pyx_t_double_complex_from_parts(0, 1.0)); - - /* "fontTools/cu2qu/cu2qu.py":276 - * cd = d - c - * p = ab * 1j - * try: # <<<<<<<<<<<<<< - * h = dot(p, a - c) / dot(p, cd) - * except ZeroDivisionError: - */ - { - __Pyx_PyThreadState_declare - __Pyx_PyThreadState_assign - __Pyx_ExceptionSave(&__pyx_t_1, &__pyx_t_2, &__pyx_t_3); - __Pyx_XGOTREF(__pyx_t_1); - __Pyx_XGOTREF(__pyx_t_2); - __Pyx_XGOTREF(__pyx_t_3); - /*try:*/ { - - /* "fontTools/cu2qu/cu2qu.py":277 - * p = ab * 1j - * try: - * h = dot(p, a - c) / dot(p, cd) # <<<<<<<<<<<<<< - * except ZeroDivisionError: - * return complex(NAN, NAN) - */ - __pyx_t_4 = __pyx_f_9fontTools_5cu2qu_5cu2qu_dot(__pyx_v_p, __Pyx_c_diff_double(__pyx_v_a, __pyx_v_c)); if (unlikely(__pyx_t_4 == ((double)-1) && PyErr_Occurred())) __PYX_ERR(0, 277, __pyx_L3_error) - __pyx_t_5 = __pyx_f_9fontTools_5cu2qu_5cu2qu_dot(__pyx_v_p, __pyx_v_cd); if (unlikely(__pyx_t_5 == ((double)-1) && PyErr_Occurred())) __PYX_ERR(0, 277, __pyx_L3_error) - if (unlikely(__pyx_t_5 == 0)) { - PyErr_SetString(PyExc_ZeroDivisionError, "float division"); - __PYX_ERR(0, 277, __pyx_L3_error) - } - __pyx_v_h = (__pyx_t_4 / __pyx_t_5); - - /* "fontTools/cu2qu/cu2qu.py":276 - * cd = d - c - * p = ab * 1j - * try: # <<<<<<<<<<<<<< - * h = dot(p, a - c) / dot(p, cd) - * except ZeroDivisionError: - */ - } - __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0; - __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0; - __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0; - goto __pyx_L8_try_end; - __pyx_L3_error:; - - /* "fontTools/cu2qu/cu2qu.py":278 - * try: - * h = dot(p, a - c) / dot(p, cd) - * except ZeroDivisionError: # <<<<<<<<<<<<<< - * return complex(NAN, NAN) - * return c + cd * h - */ - __pyx_t_6 = __Pyx_PyErr_ExceptionMatches(__pyx_builtin_ZeroDivisionError); - if (__pyx_t_6) { - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.calc_intersect", __pyx_clineno, __pyx_lineno, __pyx_filename); - if (__Pyx_GetException(&__pyx_t_7, &__pyx_t_8, &__pyx_t_9) < 0) __PYX_ERR(0, 278, __pyx_L5_except_error) - __Pyx_XGOTREF(__pyx_t_7); - __Pyx_XGOTREF(__pyx_t_8); - __Pyx_XGOTREF(__pyx_t_9); - - /* "fontTools/cu2qu/cu2qu.py":279 - * h = dot(p, a - c) / dot(p, cd) - * except ZeroDivisionError: - * return complex(NAN, NAN) # <<<<<<<<<<<<<< - * return c + cd * h - * - */ - __Pyx_GetModuleGlobalName(__pyx_t_10, __pyx_n_s_NAN); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 279, __pyx_L5_except_error) - __Pyx_GOTREF(__pyx_t_10); - __Pyx_GetModuleGlobalName(__pyx_t_11, __pyx_n_s_NAN); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 279, __pyx_L5_except_error) - __Pyx_GOTREF(__pyx_t_11); - __pyx_t_12 = PyTuple_New(2); if (unlikely(!__pyx_t_12)) __PYX_ERR(0, 279, __pyx_L5_except_error) - __Pyx_GOTREF(__pyx_t_12); - __Pyx_GIVEREF(__pyx_t_10); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_12, 0, __pyx_t_10)) __PYX_ERR(0, 279, __pyx_L5_except_error); - __Pyx_GIVEREF(__pyx_t_11); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_12, 1, __pyx_t_11)) __PYX_ERR(0, 279, __pyx_L5_except_error); - __pyx_t_10 = 0; - __pyx_t_11 = 0; - __pyx_t_11 = __Pyx_PyObject_Call(((PyObject *)(&PyComplex_Type)), __pyx_t_12, NULL); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 279, __pyx_L5_except_error) - __Pyx_GOTREF(__pyx_t_11); - __Pyx_DECREF(__pyx_t_12); __pyx_t_12 = 0; - __pyx_t_13 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_11); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 279, __pyx_L5_except_error) - __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0; - __pyx_r = __pyx_t_13; - __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0; - goto __pyx_L6_except_return; - } - goto __pyx_L5_except_error; - - /* "fontTools/cu2qu/cu2qu.py":276 - * cd = d - c - * p = ab * 1j - * try: # <<<<<<<<<<<<<< - * h = dot(p, a - c) / dot(p, cd) - * except ZeroDivisionError: - */ - __pyx_L5_except_error:; - __Pyx_XGIVEREF(__pyx_t_1); - __Pyx_XGIVEREF(__pyx_t_2); - __Pyx_XGIVEREF(__pyx_t_3); - __Pyx_ExceptionReset(__pyx_t_1, __pyx_t_2, __pyx_t_3); - goto __pyx_L1_error; - __pyx_L6_except_return:; - __Pyx_XGIVEREF(__pyx_t_1); - __Pyx_XGIVEREF(__pyx_t_2); - __Pyx_XGIVEREF(__pyx_t_3); - __Pyx_ExceptionReset(__pyx_t_1, __pyx_t_2, __pyx_t_3); - goto __pyx_L0; - __pyx_L8_try_end:; - } - - /* "fontTools/cu2qu/cu2qu.py":280 - * except ZeroDivisionError: - * return complex(NAN, NAN) - * return c + cd * h # <<<<<<<<<<<<<< - * - * - */ - __pyx_r = __Pyx_c_sum_double(__pyx_v_c, __Pyx_c_prod_double(__pyx_v_cd, __pyx_t_double_complex_from_parts(__pyx_v_h, 0))); - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":255 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.returns(cython.complex) - */ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_7); - __Pyx_XDECREF(__pyx_t_8); - __Pyx_XDECREF(__pyx_t_9); - __Pyx_XDECREF(__pyx_t_10); - __Pyx_XDECREF(__pyx_t_11); - __Pyx_XDECREF(__pyx_t_12); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.calc_intersect", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = __pyx_t_double_complex_from_parts(0, 0); - __pyx_L0:; - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":283 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.returns(cython.int) - * @cython.locals( - */ - -static int __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_farthest_fit_inside(__pyx_t_double_complex __pyx_v_p0, __pyx_t_double_complex __pyx_v_p1, __pyx_t_double_complex __pyx_v_p2, __pyx_t_double_complex __pyx_v_p3, double __pyx_v_tolerance) { - __pyx_t_double_complex __pyx_v_mid; - __pyx_t_double_complex __pyx_v_deriv3; - int __pyx_r; - int __pyx_t_1; - int __pyx_t_2; - int __pyx_t_3; - int __pyx_t_4; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - - /* "fontTools/cu2qu/cu2qu.py":312 - * """ - * # First check p2 then p1, as p2 has higher error early on. - * if abs(p2) <= tolerance and abs(p1) <= tolerance: # <<<<<<<<<<<<<< - * return True - * - */ - __pyx_t_2 = (__Pyx_c_abs_double(__pyx_v_p2) <= __pyx_v_tolerance); - if (__pyx_t_2) { - } else { - __pyx_t_1 = __pyx_t_2; - goto __pyx_L4_bool_binop_done; - } - __pyx_t_2 = (__Pyx_c_abs_double(__pyx_v_p1) <= __pyx_v_tolerance); - __pyx_t_1 = __pyx_t_2; - __pyx_L4_bool_binop_done:; - if (__pyx_t_1) { - - /* "fontTools/cu2qu/cu2qu.py":313 - * # First check p2 then p1, as p2 has higher error early on. - * if abs(p2) <= tolerance and abs(p1) <= tolerance: - * return True # <<<<<<<<<<<<<< - * - * # Split. - */ - __pyx_r = 1; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":312 - * """ - * # First check p2 then p1, as p2 has higher error early on. - * if abs(p2) <= tolerance and abs(p1) <= tolerance: # <<<<<<<<<<<<<< - * return True - * - */ - } - - /* "fontTools/cu2qu/cu2qu.py":316 - * - * # Split. - * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 # <<<<<<<<<<<<<< - * if abs(mid) > tolerance: - * return False - */ - __pyx_v_mid = __Pyx_c_prod_double(__Pyx_c_sum_double(__Pyx_c_sum_double(__pyx_v_p0, __Pyx_c_prod_double(__pyx_t_double_complex_from_parts(3, 0), __Pyx_c_sum_double(__pyx_v_p1, __pyx_v_p2))), __pyx_v_p3), __pyx_t_double_complex_from_parts(0.125, 0)); - - /* "fontTools/cu2qu/cu2qu.py":317 - * # Split. - * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 - * if abs(mid) > tolerance: # <<<<<<<<<<<<<< - * return False - * deriv3 = (p3 + p2 - p1 - p0) * 0.125 - */ - __pyx_t_1 = (__Pyx_c_abs_double(__pyx_v_mid) > __pyx_v_tolerance); - if (__pyx_t_1) { - - /* "fontTools/cu2qu/cu2qu.py":318 - * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 - * if abs(mid) > tolerance: - * return False # <<<<<<<<<<<<<< - * deriv3 = (p3 + p2 - p1 - p0) * 0.125 - * return cubic_farthest_fit_inside( - */ - __pyx_r = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":317 - * # Split. - * mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 - * if abs(mid) > tolerance: # <<<<<<<<<<<<<< - * return False - * deriv3 = (p3 + p2 - p1 - p0) * 0.125 - */ - } - - /* "fontTools/cu2qu/cu2qu.py":319 - * if abs(mid) > tolerance: - * return False - * deriv3 = (p3 + p2 - p1 - p0) * 0.125 # <<<<<<<<<<<<<< - * return cubic_farthest_fit_inside( - * p0, (p0 + p1) * 0.5, mid - deriv3, mid, tolerance - */ - __pyx_v_deriv3 = __Pyx_c_prod_double(__Pyx_c_diff_double(__Pyx_c_diff_double(__Pyx_c_sum_double(__pyx_v_p3, __pyx_v_p2), __pyx_v_p1), __pyx_v_p0), __pyx_t_double_complex_from_parts(0.125, 0)); - - /* "fontTools/cu2qu/cu2qu.py":320 - * return False - * deriv3 = (p3 + p2 - p1 - p0) * 0.125 - * return cubic_farthest_fit_inside( # <<<<<<<<<<<<<< - * p0, (p0 + p1) * 0.5, mid - deriv3, mid, tolerance - * ) and cubic_farthest_fit_inside(mid, mid + deriv3, (p2 + p3) * 0.5, p3, tolerance) - */ - __pyx_t_4 = __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_farthest_fit_inside(__pyx_v_p0, __Pyx_c_prod_double(__Pyx_c_sum_double(__pyx_v_p0, __pyx_v_p1), __pyx_t_double_complex_from_parts(0.5, 0)), __Pyx_c_diff_double(__pyx_v_mid, __pyx_v_deriv3), __pyx_v_mid, __pyx_v_tolerance); if (unlikely(__pyx_t_4 == ((int)-1) && PyErr_Occurred())) __PYX_ERR(0, 320, __pyx_L1_error) - if (__pyx_t_4) { - } else { - __pyx_t_3 = __pyx_t_4; - goto __pyx_L7_bool_binop_done; - } - - /* "fontTools/cu2qu/cu2qu.py":322 - * return cubic_farthest_fit_inside( - * p0, (p0 + p1) * 0.5, mid - deriv3, mid, tolerance - * ) and cubic_farthest_fit_inside(mid, mid + deriv3, (p2 + p3) * 0.5, p3, tolerance) # <<<<<<<<<<<<<< - * - * - */ - __pyx_t_4 = __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_farthest_fit_inside(__pyx_v_mid, __Pyx_c_sum_double(__pyx_v_mid, __pyx_v_deriv3), __Pyx_c_prod_double(__Pyx_c_sum_double(__pyx_v_p2, __pyx_v_p3), __pyx_t_double_complex_from_parts(0.5, 0)), __pyx_v_p3, __pyx_v_tolerance); if (unlikely(__pyx_t_4 == ((int)-1) && PyErr_Occurred())) __PYX_ERR(0, 322, __pyx_L1_error) - __pyx_t_3 = __pyx_t_4; - __pyx_L7_bool_binop_done:; - __pyx_r = __pyx_t_3; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":283 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.returns(cython.int) - * @cython.locals( - */ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.cubic_farthest_fit_inside", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = -1; - __pyx_L0:; - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":325 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals(tolerance=cython.double) - */ - -static CYTHON_INLINE PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_quadratic(PyObject *__pyx_v_cubic, double __pyx_v_tolerance) { - __pyx_t_double_complex __pyx_v_q1; - __pyx_t_double_complex __pyx_v_c0; - __pyx_t_double_complex __pyx_v_c1; - __pyx_t_double_complex __pyx_v_c2; - __pyx_t_double_complex __pyx_v_c3; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - PyObject *__pyx_t_1 = NULL; - __pyx_t_double_complex __pyx_t_2; - __pyx_t_double_complex __pyx_t_3; - __pyx_t_double_complex __pyx_t_4; - __pyx_t_double_complex __pyx_t_5; - __pyx_t_double_complex __pyx_t_6; - PyObject *__pyx_t_7 = NULL; - PyObject *__pyx_t_8 = NULL; - PyObject *__pyx_t_9 = NULL; - unsigned int __pyx_t_10; - int __pyx_t_11; - int __pyx_t_12; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("cubic_approx_quadratic", 1); - - /* "fontTools/cu2qu/cu2qu.py":349 - * """ - * - * q1 = calc_intersect(cubic[0], cubic[1], cubic[2], cubic[3]) # <<<<<<<<<<<<<< - * if math.isnan(q1.imag): - * return None - */ - __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_cubic, 0, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 349, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_2 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_1); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 349, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_cubic, 1, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 349, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_3 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_1); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 349, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_cubic, 2, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 349, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_4 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_1); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 349, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_cubic, 3, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 349, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_5 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_1); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 349, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_t_6 = __pyx_f_9fontTools_5cu2qu_5cu2qu_calc_intersect(__pyx_t_2, __pyx_t_3, __pyx_t_4, __pyx_t_5); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 349, __pyx_L1_error) - __pyx_v_q1 = __pyx_t_6; - - /* "fontTools/cu2qu/cu2qu.py":350 - * - * q1 = calc_intersect(cubic[0], cubic[1], cubic[2], cubic[3]) - * if math.isnan(q1.imag): # <<<<<<<<<<<<<< - * return None - * c0 = cubic[0] - */ - __Pyx_GetModuleGlobalName(__pyx_t_7, __pyx_n_s_math); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 350, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - __pyx_t_8 = __Pyx_PyObject_GetAttrStr(__pyx_t_7, __pyx_n_s_isnan); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 350, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; - __pyx_t_7 = PyFloat_FromDouble(__Pyx_CIMAG(__pyx_v_q1)); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 350, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - __pyx_t_9 = NULL; - __pyx_t_10 = 0; - #if CYTHON_UNPACK_METHODS - if (unlikely(PyMethod_Check(__pyx_t_8))) { - __pyx_t_9 = PyMethod_GET_SELF(__pyx_t_8); - if (likely(__pyx_t_9)) { - PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_8); - __Pyx_INCREF(__pyx_t_9); - __Pyx_INCREF(function); - __Pyx_DECREF_SET(__pyx_t_8, function); - __pyx_t_10 = 1; - } - } - #endif - { - PyObject *__pyx_callargs[2] = {__pyx_t_9, __pyx_t_7}; - __pyx_t_1 = __Pyx_PyObject_FastCall(__pyx_t_8, __pyx_callargs+1-__pyx_t_10, 1+__pyx_t_10); - __Pyx_XDECREF(__pyx_t_9); __pyx_t_9 = 0; - __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; - if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 350, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - } - __pyx_t_11 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_11 < 0))) __PYX_ERR(0, 350, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - if (__pyx_t_11) { - - /* "fontTools/cu2qu/cu2qu.py":351 - * q1 = calc_intersect(cubic[0], cubic[1], cubic[2], cubic[3]) - * if math.isnan(q1.imag): - * return None # <<<<<<<<<<<<<< - * c0 = cubic[0] - * c3 = cubic[3] - */ - __Pyx_XDECREF(__pyx_r); - __pyx_r = Py_None; __Pyx_INCREF(Py_None); - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":350 - * - * q1 = calc_intersect(cubic[0], cubic[1], cubic[2], cubic[3]) - * if math.isnan(q1.imag): # <<<<<<<<<<<<<< - * return None - * c0 = cubic[0] - */ - } - - /* "fontTools/cu2qu/cu2qu.py":352 - * if math.isnan(q1.imag): - * return None - * c0 = cubic[0] # <<<<<<<<<<<<<< - * c3 = cubic[3] - * c1 = c0 + (q1 - c0) * (2 / 3) - */ - __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_cubic, 0, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 352, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_6 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_1); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 352, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_v_c0 = __pyx_t_6; - - /* "fontTools/cu2qu/cu2qu.py":353 - * return None - * c0 = cubic[0] - * c3 = cubic[3] # <<<<<<<<<<<<<< - * c1 = c0 + (q1 - c0) * (2 / 3) - * c2 = c3 + (q1 - c3) * (2 / 3) - */ - __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_cubic, 3, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 353, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_6 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_1); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 353, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_v_c3 = __pyx_t_6; - - /* "fontTools/cu2qu/cu2qu.py":354 - * c0 = cubic[0] - * c3 = cubic[3] - * c1 = c0 + (q1 - c0) * (2 / 3) # <<<<<<<<<<<<<< - * c2 = c3 + (q1 - c3) * (2 / 3) - * if not cubic_farthest_fit_inside(0, c1 - cubic[1], c2 - cubic[2], 0, tolerance): - */ - __pyx_v_c1 = __Pyx_c_sum_double(__pyx_v_c0, __Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_q1, __pyx_v_c0), __pyx_t_double_complex_from_parts((2.0 / 3.0), 0))); - - /* "fontTools/cu2qu/cu2qu.py":355 - * c3 = cubic[3] - * c1 = c0 + (q1 - c0) * (2 / 3) - * c2 = c3 + (q1 - c3) * (2 / 3) # <<<<<<<<<<<<<< - * if not cubic_farthest_fit_inside(0, c1 - cubic[1], c2 - cubic[2], 0, tolerance): - * return None - */ - __pyx_v_c2 = __Pyx_c_sum_double(__pyx_v_c3, __Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_q1, __pyx_v_c3), __pyx_t_double_complex_from_parts((2.0 / 3.0), 0))); - - /* "fontTools/cu2qu/cu2qu.py":356 - * c1 = c0 + (q1 - c0) * (2 / 3) - * c2 = c3 + (q1 - c3) * (2 / 3) - * if not cubic_farthest_fit_inside(0, c1 - cubic[1], c2 - cubic[2], 0, tolerance): # <<<<<<<<<<<<<< - * return None - * return c0, q1, c3 - */ - __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_c1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 356, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_8 = __Pyx_GetItemInt(__pyx_v_cubic, 1, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 356, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_t_7 = PyNumber_Subtract(__pyx_t_1, __pyx_t_8); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 356, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __pyx_t_6 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_7); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 356, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; - __pyx_t_7 = __pyx_PyComplex_FromComplex(__pyx_v_c2); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 356, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - __pyx_t_8 = __Pyx_GetItemInt(__pyx_v_cubic, 2, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 356, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_t_1 = PyNumber_Subtract(__pyx_t_7, __pyx_t_8); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 356, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __pyx_t_5 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_1); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 356, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_t_12 = __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_farthest_fit_inside(__pyx_t_double_complex_from_parts(0, 0), __pyx_t_6, __pyx_t_5, __pyx_t_double_complex_from_parts(0, 0), __pyx_v_tolerance); if (unlikely(__pyx_t_12 == ((int)-1) && PyErr_Occurred())) __PYX_ERR(0, 356, __pyx_L1_error) - __pyx_t_11 = (!(__pyx_t_12 != 0)); - if (__pyx_t_11) { - - /* "fontTools/cu2qu/cu2qu.py":357 - * c2 = c3 + (q1 - c3) * (2 / 3) - * if not cubic_farthest_fit_inside(0, c1 - cubic[1], c2 - cubic[2], 0, tolerance): - * return None # <<<<<<<<<<<<<< - * return c0, q1, c3 - * - */ - __Pyx_XDECREF(__pyx_r); - __pyx_r = Py_None; __Pyx_INCREF(Py_None); - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":356 - * c1 = c0 + (q1 - c0) * (2 / 3) - * c2 = c3 + (q1 - c3) * (2 / 3) - * if not cubic_farthest_fit_inside(0, c1 - cubic[1], c2 - cubic[2], 0, tolerance): # <<<<<<<<<<<<<< - * return None - * return c0, q1, c3 - */ - } - - /* "fontTools/cu2qu/cu2qu.py":358 - * if not cubic_farthest_fit_inside(0, c1 - cubic[1], c2 - cubic[2], 0, tolerance): - * return None - * return c0, q1, c3 # <<<<<<<<<<<<<< - * - * - */ - __Pyx_XDECREF(__pyx_r); - __pyx_t_1 = __pyx_PyComplex_FromComplex(__pyx_v_c0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 358, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_8 = __pyx_PyComplex_FromComplex(__pyx_v_q1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 358, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_t_7 = __pyx_PyComplex_FromComplex(__pyx_v_c3); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 358, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - __pyx_t_9 = PyTuple_New(3); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 358, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_9); - __Pyx_GIVEREF(__pyx_t_1); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_9, 0, __pyx_t_1)) __PYX_ERR(0, 358, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_8); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_9, 1, __pyx_t_8)) __PYX_ERR(0, 358, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_7); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_9, 2, __pyx_t_7)) __PYX_ERR(0, 358, __pyx_L1_error); - __pyx_t_1 = 0; - __pyx_t_8 = 0; - __pyx_t_7 = 0; - __pyx_r = __pyx_t_9; - __pyx_t_9 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":325 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.inline - * @cython.locals(tolerance=cython.double) - */ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_1); - __Pyx_XDECREF(__pyx_t_7); - __Pyx_XDECREF(__pyx_t_8); - __Pyx_XDECREF(__pyx_t_9); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.cubic_approx_quadratic", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = 0; - __pyx_L0:; - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":361 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.locals(n=cython.int, tolerance=cython.double) - * @cython.locals(i=cython.int) - */ - -static PyObject *__pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_spline(PyObject *__pyx_v_cubic, int __pyx_v_n, double __pyx_v_tolerance, int __pyx_v_all_quadratic) { - __pyx_t_double_complex __pyx_v_q0; - __pyx_t_double_complex __pyx_v_q1; - __pyx_t_double_complex __pyx_v_next_q1; - __pyx_t_double_complex __pyx_v_q2; - __pyx_t_double_complex __pyx_v_d1; - CYTHON_UNUSED __pyx_t_double_complex __pyx_v_c0; - __pyx_t_double_complex __pyx_v_c1; - __pyx_t_double_complex __pyx_v_c2; - __pyx_t_double_complex __pyx_v_c3; - int __pyx_v_i; - PyObject *__pyx_v_cubics = NULL; - PyObject *__pyx_v_next_cubic = NULL; - PyObject *__pyx_v_spline = NULL; - __pyx_t_double_complex __pyx_v_d0; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - int __pyx_t_1; - PyObject *__pyx_t_2 = NULL; - int __pyx_t_3; - __pyx_t_double_complex __pyx_t_4; - __pyx_t_double_complex __pyx_t_5; - __pyx_t_double_complex __pyx_t_6; - __pyx_t_double_complex __pyx_t_7; - PyObject *__pyx_t_8 = NULL; - __pyx_t_double_complex __pyx_t_9; - PyObject *__pyx_t_10 = NULL; - long __pyx_t_11; - long __pyx_t_12; - int __pyx_t_13; - PyObject *__pyx_t_14 = NULL; - PyObject *__pyx_t_15 = NULL; - PyObject *(*__pyx_t_16)(PyObject *); - long __pyx_t_17; - int __pyx_t_18; - int __pyx_t_19; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("cubic_approx_spline", 1); - - /* "fontTools/cu2qu/cu2qu.py":390 - * """ - * - * if n == 1: # <<<<<<<<<<<<<< - * return cubic_approx_quadratic(cubic, tolerance) - * if n == 2 and all_quadratic == False: - */ - __pyx_t_1 = (__pyx_v_n == 1); - if (__pyx_t_1) { - - /* "fontTools/cu2qu/cu2qu.py":391 - * - * if n == 1: - * return cubic_approx_quadratic(cubic, tolerance) # <<<<<<<<<<<<<< - * if n == 2 and all_quadratic == False: - * return cubic - */ - __Pyx_XDECREF(__pyx_r); - __pyx_t_2 = __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_quadratic(__pyx_v_cubic, __pyx_v_tolerance); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 391, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_r = __pyx_t_2; - __pyx_t_2 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":390 - * """ - * - * if n == 1: # <<<<<<<<<<<<<< - * return cubic_approx_quadratic(cubic, tolerance) - * if n == 2 and all_quadratic == False: - */ - } - - /* "fontTools/cu2qu/cu2qu.py":392 - * if n == 1: - * return cubic_approx_quadratic(cubic, tolerance) - * if n == 2 and all_quadratic == False: # <<<<<<<<<<<<<< - * return cubic - * - */ - __pyx_t_3 = (__pyx_v_n == 2); - if (__pyx_t_3) { - } else { - __pyx_t_1 = __pyx_t_3; - goto __pyx_L5_bool_binop_done; - } - __pyx_t_3 = (__pyx_v_all_quadratic == 0); - __pyx_t_1 = __pyx_t_3; - __pyx_L5_bool_binop_done:; - if (__pyx_t_1) { - - /* "fontTools/cu2qu/cu2qu.py":393 - * return cubic_approx_quadratic(cubic, tolerance) - * if n == 2 and all_quadratic == False: - * return cubic # <<<<<<<<<<<<<< - * - * cubics = split_cubic_into_n_iter(cubic[0], cubic[1], cubic[2], cubic[3], n) - */ - __Pyx_XDECREF(__pyx_r); - __Pyx_INCREF(__pyx_v_cubic); - __pyx_r = __pyx_v_cubic; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":392 - * if n == 1: - * return cubic_approx_quadratic(cubic, tolerance) - * if n == 2 and all_quadratic == False: # <<<<<<<<<<<<<< - * return cubic - * - */ - } - - /* "fontTools/cu2qu/cu2qu.py":395 - * return cubic - * - * cubics = split_cubic_into_n_iter(cubic[0], cubic[1], cubic[2], cubic[3], n) # <<<<<<<<<<<<<< - * - * # calculate the spline of quadratics and check errors at the same time. - */ - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_cubic, 0, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 395, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_4 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 395, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_cubic, 1, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 395, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_5 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 395, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_cubic, 2, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 395, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_6 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 395, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_cubic, 3, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 395, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_7 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 395, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __Pyx_PyInt_From_int(__pyx_v_n); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 395, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_8 = __pyx_f_9fontTools_5cu2qu_5cu2qu_split_cubic_into_n_iter(__pyx_t_4, __pyx_t_5, __pyx_t_6, __pyx_t_7, __pyx_t_2); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 395, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_v_cubics = __pyx_t_8; - __pyx_t_8 = 0; - - /* "fontTools/cu2qu/cu2qu.py":398 - * - * # calculate the spline of quadratics and check errors at the same time. - * next_cubic = next(cubics) # <<<<<<<<<<<<<< - * next_q1 = cubic_approx_control( - * 0, next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] - */ - __pyx_t_8 = __Pyx_PyIter_Next(__pyx_v_cubics); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 398, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_v_next_cubic = __pyx_t_8; - __pyx_t_8 = 0; - - /* "fontTools/cu2qu/cu2qu.py":400 - * next_cubic = next(cubics) - * next_q1 = cubic_approx_control( - * 0, next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] # <<<<<<<<<<<<<< - * ) - * q2 = cubic[0] - */ - __pyx_t_8 = __Pyx_GetItemInt(__pyx_v_next_cubic, 0, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 400, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_t_7 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_8); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 400, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __pyx_t_8 = __Pyx_GetItemInt(__pyx_v_next_cubic, 1, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 400, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_t_6 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_8); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 400, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __pyx_t_8 = __Pyx_GetItemInt(__pyx_v_next_cubic, 2, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 400, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_t_5 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_8); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 400, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __pyx_t_8 = __Pyx_GetItemInt(__pyx_v_next_cubic, 3, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 400, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_t_4 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_8); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 400, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - - /* "fontTools/cu2qu/cu2qu.py":399 - * # calculate the spline of quadratics and check errors at the same time. - * next_cubic = next(cubics) - * next_q1 = cubic_approx_control( # <<<<<<<<<<<<<< - * 0, next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] - * ) - */ - __pyx_t_9 = __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_control(0.0, __pyx_t_7, __pyx_t_6, __pyx_t_5, __pyx_t_4); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 399, __pyx_L1_error) - __pyx_v_next_q1 = __pyx_t_9; - - /* "fontTools/cu2qu/cu2qu.py":402 - * 0, next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] - * ) - * q2 = cubic[0] # <<<<<<<<<<<<<< - * d1 = 0j - * spline = [cubic[0], next_q1] - */ - __pyx_t_8 = __Pyx_GetItemInt(__pyx_v_cubic, 0, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 402, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_t_9 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_8); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 402, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __pyx_v_q2 = __pyx_t_9; - - /* "fontTools/cu2qu/cu2qu.py":403 - * ) - * q2 = cubic[0] - * d1 = 0j # <<<<<<<<<<<<<< - * spline = [cubic[0], next_q1] - * for i in range(1, n + 1): - */ - __pyx_v_d1 = __pyx_t_double_complex_from_parts(0, 0.0); - - /* "fontTools/cu2qu/cu2qu.py":404 - * q2 = cubic[0] - * d1 = 0j - * spline = [cubic[0], next_q1] # <<<<<<<<<<<<<< - * for i in range(1, n + 1): - * # Current cubic to convert - */ - __pyx_t_8 = __Pyx_GetItemInt(__pyx_v_cubic, 0, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 404, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_8); - __pyx_t_2 = __pyx_PyComplex_FromComplex(__pyx_v_next_q1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 404, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_10 = PyList_New(2); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 404, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_10); - __Pyx_GIVEREF(__pyx_t_8); - if (__Pyx_PyList_SET_ITEM(__pyx_t_10, 0, __pyx_t_8)) __PYX_ERR(0, 404, __pyx_L1_error); - __Pyx_GIVEREF(__pyx_t_2); - if (__Pyx_PyList_SET_ITEM(__pyx_t_10, 1, __pyx_t_2)) __PYX_ERR(0, 404, __pyx_L1_error); - __pyx_t_8 = 0; - __pyx_t_2 = 0; - __pyx_v_spline = ((PyObject*)__pyx_t_10); - __pyx_t_10 = 0; - - /* "fontTools/cu2qu/cu2qu.py":405 - * d1 = 0j - * spline = [cubic[0], next_q1] - * for i in range(1, n + 1): # <<<<<<<<<<<<<< - * # Current cubic to convert - * c0, c1, c2, c3 = next_cubic - */ - __pyx_t_11 = (__pyx_v_n + 1); - __pyx_t_12 = __pyx_t_11; - for (__pyx_t_13 = 1; __pyx_t_13 < __pyx_t_12; __pyx_t_13+=1) { - __pyx_v_i = __pyx_t_13; - - /* "fontTools/cu2qu/cu2qu.py":407 - * for i in range(1, n + 1): - * # Current cubic to convert - * c0, c1, c2, c3 = next_cubic # <<<<<<<<<<<<<< - * - * # Current quadratic approximation of current cubic - */ - if ((likely(PyTuple_CheckExact(__pyx_v_next_cubic))) || (PyList_CheckExact(__pyx_v_next_cubic))) { - PyObject* sequence = __pyx_v_next_cubic; - Py_ssize_t size = __Pyx_PySequence_SIZE(sequence); - if (unlikely(size != 4)) { - if (size > 4) __Pyx_RaiseTooManyValuesError(4); - else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size); - __PYX_ERR(0, 407, __pyx_L1_error) - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - if (likely(PyTuple_CheckExact(sequence))) { - __pyx_t_10 = PyTuple_GET_ITEM(sequence, 0); - __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1); - __pyx_t_8 = PyTuple_GET_ITEM(sequence, 2); - __pyx_t_14 = PyTuple_GET_ITEM(sequence, 3); - } else { - __pyx_t_10 = PyList_GET_ITEM(sequence, 0); - __pyx_t_2 = PyList_GET_ITEM(sequence, 1); - __pyx_t_8 = PyList_GET_ITEM(sequence, 2); - __pyx_t_14 = PyList_GET_ITEM(sequence, 3); - } - __Pyx_INCREF(__pyx_t_10); - __Pyx_INCREF(__pyx_t_2); - __Pyx_INCREF(__pyx_t_8); - __Pyx_INCREF(__pyx_t_14); - #else - { - Py_ssize_t i; - PyObject** temps[4] = {&__pyx_t_10,&__pyx_t_2,&__pyx_t_8,&__pyx_t_14}; - for (i=0; i < 4; i++) { - PyObject* item = PySequence_ITEM(sequence, i); if (unlikely(!item)) __PYX_ERR(0, 407, __pyx_L1_error) - __Pyx_GOTREF(item); - *(temps[i]) = item; - } - } - #endif - } else { - Py_ssize_t index = -1; - PyObject** temps[4] = {&__pyx_t_10,&__pyx_t_2,&__pyx_t_8,&__pyx_t_14}; - __pyx_t_15 = PyObject_GetIter(__pyx_v_next_cubic); if (unlikely(!__pyx_t_15)) __PYX_ERR(0, 407, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_15); - __pyx_t_16 = __Pyx_PyObject_GetIterNextFunc(__pyx_t_15); - for (index=0; index < 4; index++) { - PyObject* item = __pyx_t_16(__pyx_t_15); if (unlikely(!item)) goto __pyx_L9_unpacking_failed; - __Pyx_GOTREF(item); - *(temps[index]) = item; - } - if (__Pyx_IternextUnpackEndCheck(__pyx_t_16(__pyx_t_15), 4) < 0) __PYX_ERR(0, 407, __pyx_L1_error) - __pyx_t_16 = NULL; - __Pyx_DECREF(__pyx_t_15); __pyx_t_15 = 0; - goto __pyx_L10_unpacking_done; - __pyx_L9_unpacking_failed:; - __Pyx_DECREF(__pyx_t_15); __pyx_t_15 = 0; - __pyx_t_16 = NULL; - if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index); - __PYX_ERR(0, 407, __pyx_L1_error) - __pyx_L10_unpacking_done:; - } - __pyx_t_9 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_10); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 407, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0; - __pyx_t_4 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_2); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 407, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_5 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_8); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 407, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __pyx_t_6 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_14); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 407, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_14); __pyx_t_14 = 0; - __pyx_v_c0 = __pyx_t_9; - __pyx_v_c1 = __pyx_t_4; - __pyx_v_c2 = __pyx_t_5; - __pyx_v_c3 = __pyx_t_6; - - /* "fontTools/cu2qu/cu2qu.py":410 - * - * # Current quadratic approximation of current cubic - * q0 = q2 # <<<<<<<<<<<<<< - * q1 = next_q1 - * if i < n: - */ - __pyx_v_q0 = __pyx_v_q2; - - /* "fontTools/cu2qu/cu2qu.py":411 - * # Current quadratic approximation of current cubic - * q0 = q2 - * q1 = next_q1 # <<<<<<<<<<<<<< - * if i < n: - * next_cubic = next(cubics) - */ - __pyx_v_q1 = __pyx_v_next_q1; - - /* "fontTools/cu2qu/cu2qu.py":412 - * q0 = q2 - * q1 = next_q1 - * if i < n: # <<<<<<<<<<<<<< - * next_cubic = next(cubics) - * next_q1 = cubic_approx_control( - */ - __pyx_t_1 = (__pyx_v_i < __pyx_v_n); - if (__pyx_t_1) { - - /* "fontTools/cu2qu/cu2qu.py":413 - * q1 = next_q1 - * if i < n: - * next_cubic = next(cubics) # <<<<<<<<<<<<<< - * next_q1 = cubic_approx_control( - * i / (n - 1), next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] - */ - __pyx_t_14 = __Pyx_PyIter_Next(__pyx_v_cubics); if (unlikely(!__pyx_t_14)) __PYX_ERR(0, 413, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_14); - __Pyx_DECREF_SET(__pyx_v_next_cubic, __pyx_t_14); - __pyx_t_14 = 0; - - /* "fontTools/cu2qu/cu2qu.py":415 - * next_cubic = next(cubics) - * next_q1 = cubic_approx_control( - * i / (n - 1), next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] # <<<<<<<<<<<<<< - * ) - * spline.append(next_q1) - */ - __pyx_t_17 = (__pyx_v_n - 1); - if (unlikely(__pyx_t_17 == 0)) { - PyErr_SetString(PyExc_ZeroDivisionError, "float division"); - __PYX_ERR(0, 415, __pyx_L1_error) - } - __pyx_t_14 = __Pyx_GetItemInt(__pyx_v_next_cubic, 0, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_14)) __PYX_ERR(0, 415, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_14); - __pyx_t_6 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_14); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 415, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_14); __pyx_t_14 = 0; - __pyx_t_14 = __Pyx_GetItemInt(__pyx_v_next_cubic, 1, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_14)) __PYX_ERR(0, 415, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_14); - __pyx_t_5 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_14); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 415, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_14); __pyx_t_14 = 0; - __pyx_t_14 = __Pyx_GetItemInt(__pyx_v_next_cubic, 2, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_14)) __PYX_ERR(0, 415, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_14); - __pyx_t_4 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_14); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 415, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_14); __pyx_t_14 = 0; - __pyx_t_14 = __Pyx_GetItemInt(__pyx_v_next_cubic, 3, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_14)) __PYX_ERR(0, 415, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_14); - __pyx_t_9 = __Pyx_PyComplex_As___pyx_t_double_complex(__pyx_t_14); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 415, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_14); __pyx_t_14 = 0; - - /* "fontTools/cu2qu/cu2qu.py":414 - * if i < n: - * next_cubic = next(cubics) - * next_q1 = cubic_approx_control( # <<<<<<<<<<<<<< - * i / (n - 1), next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] - * ) - */ - __pyx_t_7 = __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_control((((double)__pyx_v_i) / ((double)__pyx_t_17)), __pyx_t_6, __pyx_t_5, __pyx_t_4, __pyx_t_9); if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 414, __pyx_L1_error) - __pyx_v_next_q1 = __pyx_t_7; - - /* "fontTools/cu2qu/cu2qu.py":417 - * i / (n - 1), next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] - * ) - * spline.append(next_q1) # <<<<<<<<<<<<<< - * q2 = (q1 + next_q1) * 0.5 - * else: - */ - __pyx_t_14 = __pyx_PyComplex_FromComplex(__pyx_v_next_q1); if (unlikely(!__pyx_t_14)) __PYX_ERR(0, 417, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_14); - __pyx_t_18 = __Pyx_PyList_Append(__pyx_v_spline, __pyx_t_14); if (unlikely(__pyx_t_18 == ((int)-1))) __PYX_ERR(0, 417, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_14); __pyx_t_14 = 0; - - /* "fontTools/cu2qu/cu2qu.py":418 - * ) - * spline.append(next_q1) - * q2 = (q1 + next_q1) * 0.5 # <<<<<<<<<<<<<< - * else: - * q2 = c3 - */ - __pyx_v_q2 = __Pyx_c_prod_double(__Pyx_c_sum_double(__pyx_v_q1, __pyx_v_next_q1), __pyx_t_double_complex_from_parts(0.5, 0)); - - /* "fontTools/cu2qu/cu2qu.py":412 - * q0 = q2 - * q1 = next_q1 - * if i < n: # <<<<<<<<<<<<<< - * next_cubic = next(cubics) - * next_q1 = cubic_approx_control( - */ - goto __pyx_L11; - } - - /* "fontTools/cu2qu/cu2qu.py":420 - * q2 = (q1 + next_q1) * 0.5 - * else: - * q2 = c3 # <<<<<<<<<<<<<< - * - * # End-point deltas - */ - /*else*/ { - __pyx_v_q2 = __pyx_v_c3; - } - __pyx_L11:; - - /* "fontTools/cu2qu/cu2qu.py":423 - * - * # End-point deltas - * d0 = d1 # <<<<<<<<<<<<<< - * d1 = q2 - c3 - * - */ - __pyx_v_d0 = __pyx_v_d1; - - /* "fontTools/cu2qu/cu2qu.py":424 - * # End-point deltas - * d0 = d1 - * d1 = q2 - c3 # <<<<<<<<<<<<<< - * - * if abs(d1) > tolerance or not cubic_farthest_fit_inside( - */ - __pyx_v_d1 = __Pyx_c_diff_double(__pyx_v_q2, __pyx_v_c3); - - /* "fontTools/cu2qu/cu2qu.py":426 - * d1 = q2 - c3 - * - * if abs(d1) > tolerance or not cubic_farthest_fit_inside( # <<<<<<<<<<<<<< - * d0, - * q0 + (q1 - q0) * (2 / 3) - c1, - */ - __pyx_t_3 = (__Pyx_c_abs_double(__pyx_v_d1) > __pyx_v_tolerance); - if (!__pyx_t_3) { - } else { - __pyx_t_1 = __pyx_t_3; - goto __pyx_L13_bool_binop_done; - } - - /* "fontTools/cu2qu/cu2qu.py":431 - * q2 + (q1 - q2) * (2 / 3) - c2, - * d1, - * tolerance, # <<<<<<<<<<<<<< - * ): - * return None - */ - __pyx_t_19 = __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_farthest_fit_inside(__pyx_v_d0, __Pyx_c_diff_double(__Pyx_c_sum_double(__pyx_v_q0, __Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_q1, __pyx_v_q0), __pyx_t_double_complex_from_parts((2.0 / 3.0), 0))), __pyx_v_c1), __Pyx_c_diff_double(__Pyx_c_sum_double(__pyx_v_q2, __Pyx_c_prod_double(__Pyx_c_diff_double(__pyx_v_q1, __pyx_v_q2), __pyx_t_double_complex_from_parts((2.0 / 3.0), 0))), __pyx_v_c2), __pyx_v_d1, __pyx_v_tolerance); if (unlikely(__pyx_t_19 == ((int)-1) && PyErr_Occurred())) __PYX_ERR(0, 426, __pyx_L1_error) - - /* "fontTools/cu2qu/cu2qu.py":426 - * d1 = q2 - c3 - * - * if abs(d1) > tolerance or not cubic_farthest_fit_inside( # <<<<<<<<<<<<<< - * d0, - * q0 + (q1 - q0) * (2 / 3) - c1, - */ - __pyx_t_3 = (!(__pyx_t_19 != 0)); - __pyx_t_1 = __pyx_t_3; - __pyx_L13_bool_binop_done:; - if (__pyx_t_1) { - - /* "fontTools/cu2qu/cu2qu.py":433 - * tolerance, - * ): - * return None # <<<<<<<<<<<<<< - * spline.append(cubic[3]) - * - */ - __Pyx_XDECREF(__pyx_r); - __pyx_r = Py_None; __Pyx_INCREF(Py_None); - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":426 - * d1 = q2 - c3 - * - * if abs(d1) > tolerance or not cubic_farthest_fit_inside( # <<<<<<<<<<<<<< - * d0, - * q0 + (q1 - q0) * (2 / 3) - c1, - */ - } - } - - /* "fontTools/cu2qu/cu2qu.py":434 - * ): - * return None - * spline.append(cubic[3]) # <<<<<<<<<<<<<< - * - * return spline - */ - __pyx_t_14 = __Pyx_GetItemInt(__pyx_v_cubic, 3, long, 1, __Pyx_PyInt_From_long, 0, 0, 1); if (unlikely(!__pyx_t_14)) __PYX_ERR(0, 434, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_14); - __pyx_t_18 = __Pyx_PyList_Append(__pyx_v_spline, __pyx_t_14); if (unlikely(__pyx_t_18 == ((int)-1))) __PYX_ERR(0, 434, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_14); __pyx_t_14 = 0; - - /* "fontTools/cu2qu/cu2qu.py":436 - * spline.append(cubic[3]) - * - * return spline # <<<<<<<<<<<<<< - * - * - */ - __Pyx_XDECREF(__pyx_r); - __Pyx_INCREF(__pyx_v_spline); - __pyx_r = __pyx_v_spline; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":361 - * - * - * @cython.cfunc # <<<<<<<<<<<<<< - * @cython.locals(n=cython.int, tolerance=cython.double) - * @cython.locals(i=cython.int) - */ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_2); - __Pyx_XDECREF(__pyx_t_8); - __Pyx_XDECREF(__pyx_t_10); - __Pyx_XDECREF(__pyx_t_14); - __Pyx_XDECREF(__pyx_t_15); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.cubic_approx_spline", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = 0; - __pyx_L0:; - __Pyx_XDECREF(__pyx_v_cubics); - __Pyx_XDECREF(__pyx_v_next_cubic); - __Pyx_XDECREF(__pyx_v_spline); - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":439 - * - * - * @cython.locals(max_err=cython.double) # <<<<<<<<<<<<<< - * @cython.locals(n=cython.int) - * @cython.locals(all_quadratic=cython.int) - */ - -/* Python wrapper */ -static PyObject *__pyx_pw_9fontTools_5cu2qu_5cu2qu_4curve_to_quadratic(PyObject *__pyx_self, -#if CYTHON_METH_FASTCALL -PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds -#else -PyObject *__pyx_args, PyObject *__pyx_kwds -#endif -); /*proto*/ -PyDoc_STRVAR(__pyx_doc_9fontTools_5cu2qu_5cu2qu_3curve_to_quadratic, "curve_to_quadratic(curve, double max_err, int all_quadratic=True)\nApproximate a cubic Bezier curve with a spline of n quadratics.\n\n Args:\n cubic (sequence): Four 2D tuples representing control points of\n the cubic Bezier curve.\n max_err (double): Permitted deviation from the original curve.\n all_quadratic (bool): If True (default) returned value is a\n quadratic spline. If False, it's either a single quadratic\n curve or a single cubic curve.\n\n Returns:\n If all_quadratic is True: A list of 2D tuples, representing\n control points of the quadratic spline if it fits within the\n given tolerance, or ``None`` if no suitable spline could be\n calculated.\n\n If all_quadratic is False: Either a quadratic curve (if length\n of output is 3), or a cubic curve (if length of output is 4).\n "); -static PyMethodDef __pyx_mdef_9fontTools_5cu2qu_5cu2qu_4curve_to_quadratic = {"curve_to_quadratic", (PyCFunction)(void*)(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_5cu2qu_5cu2qu_4curve_to_quadratic, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_5cu2qu_5cu2qu_3curve_to_quadratic}; -static PyObject *__pyx_pw_9fontTools_5cu2qu_5cu2qu_4curve_to_quadratic(PyObject *__pyx_self, -#if CYTHON_METH_FASTCALL -PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds -#else -PyObject *__pyx_args, PyObject *__pyx_kwds -#endif -) { - PyObject *__pyx_v_curve = 0; - double __pyx_v_max_err; - int __pyx_v_all_quadratic; - #if !CYTHON_METH_FASTCALL - CYTHON_UNUSED Py_ssize_t __pyx_nargs; - #endif - CYTHON_UNUSED PyObject *const *__pyx_kwvalues; - PyObject* values[3] = {0,0,0}; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - PyObject *__pyx_r = 0; - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("curve_to_quadratic (wrapper)", 0); - #if !CYTHON_METH_FASTCALL - #if CYTHON_ASSUME_SAFE_MACROS - __pyx_nargs = PyTuple_GET_SIZE(__pyx_args); - #else - __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL; - #endif - #endif - __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs); - { - PyObject **__pyx_pyargnames[] = {&__pyx_n_s_curve,&__pyx_n_s_max_err,&__pyx_n_s_all_quadratic,0}; - if (__pyx_kwds) { - Py_ssize_t kw_args; - switch (__pyx_nargs) { - case 3: values[2] = __Pyx_Arg_FASTCALL(__pyx_args, 2); - CYTHON_FALLTHROUGH; - case 2: values[1] = __Pyx_Arg_FASTCALL(__pyx_args, 1); - CYTHON_FALLTHROUGH; - case 1: values[0] = __Pyx_Arg_FASTCALL(__pyx_args, 0); - CYTHON_FALLTHROUGH; - case 0: break; - default: goto __pyx_L5_argtuple_error; - } - kw_args = __Pyx_NumKwargs_FASTCALL(__pyx_kwds); - switch (__pyx_nargs) { - case 0: - if (likely((values[0] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_curve)) != 0)) { - (void)__Pyx_Arg_NewRef_FASTCALL(values[0]); - kw_args--; - } - else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 439, __pyx_L3_error) - else goto __pyx_L5_argtuple_error; - CYTHON_FALLTHROUGH; - case 1: - if (likely((values[1] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_max_err)) != 0)) { - (void)__Pyx_Arg_NewRef_FASTCALL(values[1]); - kw_args--; - } - else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 439, __pyx_L3_error) - else { - __Pyx_RaiseArgtupleInvalid("curve_to_quadratic", 0, 2, 3, 1); __PYX_ERR(0, 439, __pyx_L3_error) - } - CYTHON_FALLTHROUGH; - case 2: - if (kw_args > 0) { - PyObject* value = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_all_quadratic); - if (value) { values[2] = __Pyx_Arg_NewRef_FASTCALL(value); kw_args--; } - else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 439, __pyx_L3_error) - } - } - if (unlikely(kw_args > 0)) { - const Py_ssize_t kwd_pos_args = __pyx_nargs; - if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values + 0, kwd_pos_args, "curve_to_quadratic") < 0)) __PYX_ERR(0, 439, __pyx_L3_error) - } - } else { - switch (__pyx_nargs) { - case 3: values[2] = __Pyx_Arg_FASTCALL(__pyx_args, 2); - CYTHON_FALLTHROUGH; - case 2: values[1] = __Pyx_Arg_FASTCALL(__pyx_args, 1); - values[0] = __Pyx_Arg_FASTCALL(__pyx_args, 0); - break; - default: goto __pyx_L5_argtuple_error; - } - } - __pyx_v_curve = values[0]; - __pyx_v_max_err = __pyx_PyFloat_AsDouble(values[1]); if (unlikely((__pyx_v_max_err == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 442, __pyx_L3_error) - if (values[2]) { - __pyx_v_all_quadratic = __Pyx_PyInt_As_int(values[2]); if (unlikely((__pyx_v_all_quadratic == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 442, __pyx_L3_error) - } else { - - /* "fontTools/cu2qu/cu2qu.py":442 - * @cython.locals(n=cython.int) - * @cython.locals(all_quadratic=cython.int) - * def curve_to_quadratic(curve, max_err, all_quadratic=True): # <<<<<<<<<<<<<< - * """Approximate a cubic Bezier curve with a spline of n quadratics. - * - */ - __pyx_v_all_quadratic = ((int)((int)1)); - } - } - goto __pyx_L6_skip; - __pyx_L5_argtuple_error:; - __Pyx_RaiseArgtupleInvalid("curve_to_quadratic", 0, 2, 3, __pyx_nargs); __PYX_ERR(0, 439, __pyx_L3_error) - __pyx_L6_skip:; - goto __pyx_L4_argument_unpacking_done; - __pyx_L3_error:; - { - Py_ssize_t __pyx_temp; - for (__pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { - __Pyx_Arg_XDECREF_FASTCALL(values[__pyx_temp]); - } - } - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.curve_to_quadratic", __pyx_clineno, __pyx_lineno, __pyx_filename); - __Pyx_RefNannyFinishContext(); - return NULL; - __pyx_L4_argument_unpacking_done:; - __pyx_r = __pyx_pf_9fontTools_5cu2qu_5cu2qu_3curve_to_quadratic(__pyx_self, __pyx_v_curve, __pyx_v_max_err, __pyx_v_all_quadratic); - - /* "fontTools/cu2qu/cu2qu.py":439 - * - * - * @cython.locals(max_err=cython.double) # <<<<<<<<<<<<<< - * @cython.locals(n=cython.int) - * @cython.locals(all_quadratic=cython.int) - */ - - /* function exit code */ - { - Py_ssize_t __pyx_temp; - for (__pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { - __Pyx_Arg_XDECREF_FASTCALL(values[__pyx_temp]); - } - } - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -static PyObject *__pyx_pf_9fontTools_5cu2qu_5cu2qu_3curve_to_quadratic(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_curve, double __pyx_v_max_err, int __pyx_v_all_quadratic) { - int __pyx_v_n; - PyObject *__pyx_v_spline = NULL; - PyObject *__pyx_7genexpr__pyx_v_p = NULL; - PyObject *__pyx_8genexpr1__pyx_v_s = NULL; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - PyObject *__pyx_t_1 = NULL; - PyObject *__pyx_t_2 = NULL; - Py_ssize_t __pyx_t_3; - PyObject *(*__pyx_t_4)(PyObject *); - PyObject *__pyx_t_5 = NULL; - PyObject *__pyx_t_6 = NULL; - long __pyx_t_7; - long __pyx_t_8; - int __pyx_t_9; - int __pyx_t_10; - PyObject *__pyx_t_11 = NULL; - unsigned int __pyx_t_12; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("curve_to_quadratic", 0); - __Pyx_INCREF(__pyx_v_curve); - - /* "fontTools/cu2qu/cu2qu.py":463 - * """ - * - * curve = [complex(*p) for p in curve] # <<<<<<<<<<<<<< - * - * for n in range(1, MAX_N + 1): - */ - { /* enter inner scope */ - __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 463, __pyx_L5_error) - __Pyx_GOTREF(__pyx_t_1); - if (likely(PyList_CheckExact(__pyx_v_curve)) || PyTuple_CheckExact(__pyx_v_curve)) { - __pyx_t_2 = __pyx_v_curve; __Pyx_INCREF(__pyx_t_2); - __pyx_t_3 = 0; - __pyx_t_4 = NULL; - } else { - __pyx_t_3 = -1; __pyx_t_2 = PyObject_GetIter(__pyx_v_curve); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 463, __pyx_L5_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_4 = __Pyx_PyObject_GetIterNextFunc(__pyx_t_2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 463, __pyx_L5_error) - } - for (;;) { - if (likely(!__pyx_t_4)) { - if (likely(PyList_CheckExact(__pyx_t_2))) { - { - Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_2); - #if !CYTHON_ASSUME_SAFE_MACROS - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 463, __pyx_L5_error) - #endif - if (__pyx_t_3 >= __pyx_temp) break; - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - __pyx_t_5 = PyList_GET_ITEM(__pyx_t_2, __pyx_t_3); __Pyx_INCREF(__pyx_t_5); __pyx_t_3++; if (unlikely((0 < 0))) __PYX_ERR(0, 463, __pyx_L5_error) - #else - __pyx_t_5 = __Pyx_PySequence_ITEM(__pyx_t_2, __pyx_t_3); __pyx_t_3++; if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 463, __pyx_L5_error) - __Pyx_GOTREF(__pyx_t_5); - #endif - } else { - { - Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_2); - #if !CYTHON_ASSUME_SAFE_MACROS - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 463, __pyx_L5_error) - #endif - if (__pyx_t_3 >= __pyx_temp) break; - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - __pyx_t_5 = PyTuple_GET_ITEM(__pyx_t_2, __pyx_t_3); __Pyx_INCREF(__pyx_t_5); __pyx_t_3++; if (unlikely((0 < 0))) __PYX_ERR(0, 463, __pyx_L5_error) - #else - __pyx_t_5 = __Pyx_PySequence_ITEM(__pyx_t_2, __pyx_t_3); __pyx_t_3++; if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 463, __pyx_L5_error) - __Pyx_GOTREF(__pyx_t_5); - #endif - } - } else { - __pyx_t_5 = __pyx_t_4(__pyx_t_2); - if (unlikely(!__pyx_t_5)) { - PyObject* exc_type = PyErr_Occurred(); - if (exc_type) { - if (likely(__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) PyErr_Clear(); - else __PYX_ERR(0, 463, __pyx_L5_error) - } - break; - } - __Pyx_GOTREF(__pyx_t_5); - } - __Pyx_XDECREF_SET(__pyx_7genexpr__pyx_v_p, __pyx_t_5); - __pyx_t_5 = 0; - __pyx_t_5 = __Pyx_PySequence_Tuple(__pyx_7genexpr__pyx_v_p); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 463, __pyx_L5_error) - __Pyx_GOTREF(__pyx_t_5); - __pyx_t_6 = __Pyx_PyObject_Call(((PyObject *)(&PyComplex_Type)), __pyx_t_5, NULL); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 463, __pyx_L5_error) - __Pyx_GOTREF(__pyx_t_6); - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - if (unlikely(__Pyx_ListComp_Append(__pyx_t_1, (PyObject*)__pyx_t_6))) __PYX_ERR(0, 463, __pyx_L5_error) - __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; - } - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __Pyx_XDECREF(__pyx_7genexpr__pyx_v_p); __pyx_7genexpr__pyx_v_p = 0; - goto __pyx_L9_exit_scope; - __pyx_L5_error:; - __Pyx_XDECREF(__pyx_7genexpr__pyx_v_p); __pyx_7genexpr__pyx_v_p = 0; - goto __pyx_L1_error; - __pyx_L9_exit_scope:; - } /* exit inner scope */ - __Pyx_DECREF_SET(__pyx_v_curve, __pyx_t_1); - __pyx_t_1 = 0; - - /* "fontTools/cu2qu/cu2qu.py":465 - * curve = [complex(*p) for p in curve] - * - * for n in range(1, MAX_N + 1): # <<<<<<<<<<<<<< - * spline = cubic_approx_spline(curve, n, max_err, all_quadratic) - * if spline is not None: - */ - __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_n_s_MAX_N); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 465, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_2 = __Pyx_PyInt_AddObjC(__pyx_t_1, __pyx_int_1, 1, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 465, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __pyx_t_7 = __Pyx_PyInt_As_long(__pyx_t_2); if (unlikely((__pyx_t_7 == (long)-1) && PyErr_Occurred())) __PYX_ERR(0, 465, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_8 = __pyx_t_7; - for (__pyx_t_9 = 1; __pyx_t_9 < __pyx_t_8; __pyx_t_9+=1) { - __pyx_v_n = __pyx_t_9; - - /* "fontTools/cu2qu/cu2qu.py":466 - * - * for n in range(1, MAX_N + 1): - * spline = cubic_approx_spline(curve, n, max_err, all_quadratic) # <<<<<<<<<<<<<< - * if spline is not None: - * # done. go home - */ - __pyx_t_2 = __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_spline(__pyx_v_curve, __pyx_v_n, __pyx_v_max_err, __pyx_v_all_quadratic); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 466, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __Pyx_XDECREF_SET(__pyx_v_spline, __pyx_t_2); - __pyx_t_2 = 0; - - /* "fontTools/cu2qu/cu2qu.py":467 - * for n in range(1, MAX_N + 1): - * spline = cubic_approx_spline(curve, n, max_err, all_quadratic) - * if spline is not None: # <<<<<<<<<<<<<< - * # done. go home - * return [(s.real, s.imag) for s in spline] - */ - __pyx_t_10 = (__pyx_v_spline != Py_None); - if (__pyx_t_10) { - - /* "fontTools/cu2qu/cu2qu.py":469 - * if spline is not None: - * # done. go home - * return [(s.real, s.imag) for s in spline] # <<<<<<<<<<<<<< - * - * raise ApproxNotFoundError(curve) - */ - __Pyx_XDECREF(__pyx_r); - { /* enter inner scope */ - __pyx_t_2 = PyList_New(0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 469, __pyx_L15_error) - __Pyx_GOTREF(__pyx_t_2); - if (likely(PyList_CheckExact(__pyx_v_spline)) || PyTuple_CheckExact(__pyx_v_spline)) { - __pyx_t_1 = __pyx_v_spline; __Pyx_INCREF(__pyx_t_1); - __pyx_t_3 = 0; - __pyx_t_4 = NULL; - } else { - __pyx_t_3 = -1; __pyx_t_1 = PyObject_GetIter(__pyx_v_spline); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 469, __pyx_L15_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_4 = __Pyx_PyObject_GetIterNextFunc(__pyx_t_1); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 469, __pyx_L15_error) - } - for (;;) { - if (likely(!__pyx_t_4)) { - if (likely(PyList_CheckExact(__pyx_t_1))) { - { - Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_1); - #if !CYTHON_ASSUME_SAFE_MACROS - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 469, __pyx_L15_error) - #endif - if (__pyx_t_3 >= __pyx_temp) break; - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - __pyx_t_6 = PyList_GET_ITEM(__pyx_t_1, __pyx_t_3); __Pyx_INCREF(__pyx_t_6); __pyx_t_3++; if (unlikely((0 < 0))) __PYX_ERR(0, 469, __pyx_L15_error) - #else - __pyx_t_6 = __Pyx_PySequence_ITEM(__pyx_t_1, __pyx_t_3); __pyx_t_3++; if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 469, __pyx_L15_error) - __Pyx_GOTREF(__pyx_t_6); - #endif - } else { - { - Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_1); - #if !CYTHON_ASSUME_SAFE_MACROS - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 469, __pyx_L15_error) - #endif - if (__pyx_t_3 >= __pyx_temp) break; - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - __pyx_t_6 = PyTuple_GET_ITEM(__pyx_t_1, __pyx_t_3); __Pyx_INCREF(__pyx_t_6); __pyx_t_3++; if (unlikely((0 < 0))) __PYX_ERR(0, 469, __pyx_L15_error) - #else - __pyx_t_6 = __Pyx_PySequence_ITEM(__pyx_t_1, __pyx_t_3); __pyx_t_3++; if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 469, __pyx_L15_error) - __Pyx_GOTREF(__pyx_t_6); - #endif - } - } else { - __pyx_t_6 = __pyx_t_4(__pyx_t_1); - if (unlikely(!__pyx_t_6)) { - PyObject* exc_type = PyErr_Occurred(); - if (exc_type) { - if (likely(__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) PyErr_Clear(); - else __PYX_ERR(0, 469, __pyx_L15_error) - } - break; - } - __Pyx_GOTREF(__pyx_t_6); - } - __Pyx_XDECREF_SET(__pyx_8genexpr1__pyx_v_s, __pyx_t_6); - __pyx_t_6 = 0; - __pyx_t_6 = __Pyx_PyObject_GetAttrStr(__pyx_8genexpr1__pyx_v_s, __pyx_n_s_real); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 469, __pyx_L15_error) - __Pyx_GOTREF(__pyx_t_6); - __pyx_t_5 = __Pyx_PyObject_GetAttrStr(__pyx_8genexpr1__pyx_v_s, __pyx_n_s_imag); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 469, __pyx_L15_error) - __Pyx_GOTREF(__pyx_t_5); - __pyx_t_11 = PyTuple_New(2); if (unlikely(!__pyx_t_11)) __PYX_ERR(0, 469, __pyx_L15_error) - __Pyx_GOTREF(__pyx_t_11); - __Pyx_GIVEREF(__pyx_t_6); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_11, 0, __pyx_t_6)) __PYX_ERR(0, 469, __pyx_L15_error); - __Pyx_GIVEREF(__pyx_t_5); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_11, 1, __pyx_t_5)) __PYX_ERR(0, 469, __pyx_L15_error); - __pyx_t_6 = 0; - __pyx_t_5 = 0; - if (unlikely(__Pyx_ListComp_Append(__pyx_t_2, (PyObject*)__pyx_t_11))) __PYX_ERR(0, 469, __pyx_L15_error) - __Pyx_DECREF(__pyx_t_11); __pyx_t_11 = 0; - } - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __Pyx_XDECREF(__pyx_8genexpr1__pyx_v_s); __pyx_8genexpr1__pyx_v_s = 0; - goto __pyx_L19_exit_scope; - __pyx_L15_error:; - __Pyx_XDECREF(__pyx_8genexpr1__pyx_v_s); __pyx_8genexpr1__pyx_v_s = 0; - goto __pyx_L1_error; - __pyx_L19_exit_scope:; - } /* exit inner scope */ - __pyx_r = __pyx_t_2; - __pyx_t_2 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":467 - * for n in range(1, MAX_N + 1): - * spline = cubic_approx_spline(curve, n, max_err, all_quadratic) - * if spline is not None: # <<<<<<<<<<<<<< - * # done. go home - * return [(s.real, s.imag) for s in spline] - */ - } - } - - /* "fontTools/cu2qu/cu2qu.py":471 - * return [(s.real, s.imag) for s in spline] - * - * raise ApproxNotFoundError(curve) # <<<<<<<<<<<<<< - * - * - */ - __Pyx_GetModuleGlobalName(__pyx_t_1, __pyx_n_s_ApproxNotFoundError); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 471, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_11 = NULL; - __pyx_t_12 = 0; - #if CYTHON_UNPACK_METHODS - if (unlikely(PyMethod_Check(__pyx_t_1))) { - __pyx_t_11 = PyMethod_GET_SELF(__pyx_t_1); - if (likely(__pyx_t_11)) { - PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_1); - __Pyx_INCREF(__pyx_t_11); - __Pyx_INCREF(function); - __Pyx_DECREF_SET(__pyx_t_1, function); - __pyx_t_12 = 1; - } - } - #endif - { - PyObject *__pyx_callargs[2] = {__pyx_t_11, __pyx_v_curve}; - __pyx_t_2 = __Pyx_PyObject_FastCall(__pyx_t_1, __pyx_callargs+1-__pyx_t_12, 1+__pyx_t_12); - __Pyx_XDECREF(__pyx_t_11); __pyx_t_11 = 0; - if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 471, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - } - __Pyx_Raise(__pyx_t_2, 0, 0, 0); - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __PYX_ERR(0, 471, __pyx_L1_error) - - /* "fontTools/cu2qu/cu2qu.py":439 - * - * - * @cython.locals(max_err=cython.double) # <<<<<<<<<<<<<< - * @cython.locals(n=cython.int) - * @cython.locals(all_quadratic=cython.int) - */ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_1); - __Pyx_XDECREF(__pyx_t_2); - __Pyx_XDECREF(__pyx_t_5); - __Pyx_XDECREF(__pyx_t_6); - __Pyx_XDECREF(__pyx_t_11); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.curve_to_quadratic", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = NULL; - __pyx_L0:; - __Pyx_XDECREF(__pyx_v_spline); - __Pyx_XDECREF(__pyx_7genexpr__pyx_v_p); - __Pyx_XDECREF(__pyx_8genexpr1__pyx_v_s); - __Pyx_XDECREF(__pyx_v_curve); - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -/* "fontTools/cu2qu/cu2qu.py":474 - * - * - * @cython.locals(l=cython.int, last_i=cython.int, i=cython.int) # <<<<<<<<<<<<<< - * @cython.locals(all_quadratic=cython.int) - * def curves_to_quadratic(curves, max_errors, all_quadratic=True): - */ - -/* Python wrapper */ -static PyObject *__pyx_pw_9fontTools_5cu2qu_5cu2qu_6curves_to_quadratic(PyObject *__pyx_self, -#if CYTHON_METH_FASTCALL -PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds -#else -PyObject *__pyx_args, PyObject *__pyx_kwds -#endif -); /*proto*/ -PyDoc_STRVAR(__pyx_doc_9fontTools_5cu2qu_5cu2qu_5curves_to_quadratic, "curves_to_quadratic(curves, max_errors, int all_quadratic=True)\nReturn quadratic Bezier splines approximating the input cubic Beziers.\n\n Args:\n curves: A sequence of *n* curves, each curve being a sequence of four\n 2D tuples.\n max_errors: A sequence of *n* floats representing the maximum permissible\n deviation from each of the cubic Bezier curves.\n all_quadratic (bool): If True (default) returned values are a\n quadratic spline. If False, they are either a single quadratic\n curve or a single cubic curve.\n\n Example::\n\n >>> curves_to_quadratic( [\n ... [ (50,50), (100,100), (150,100), (200,50) ],\n ... [ (75,50), (120,100), (150,75), (200,60) ]\n ... ], [1,1] )\n [[(50.0, 50.0), (75.0, 75.0), (125.0, 91.66666666666666), (175.0, 75.0), (200.0, 50.0)], [(75.0, 50.0), (97.5, 75.0), (135.41666666666666, 82.08333333333333), (175.0, 67.5), (200.0, 60.0)]]\n\n The returned splines have \"implied oncurve points\" suitable for use in\n TrueType ``glif`` outlines - i.e. in the first spline returned above,\n the first quadratic segment runs from (50,50) to\n ( (75 + 125)/2 , (120 + 91.666..)/2 ) = (100, 83.333...).\n\n Returns:\n If all_quadratic is True, a list of splines, each spline being a list\n of 2D tuples.\n\n If all_quadratic is False, a list of curves, each curve being a quadratic\n (length 3), or cubic (length 4).\n\n Raises:\n fontTools.cu2qu.Errors.ApproxNotFoundError: if no suitable approximation\n can be found for all curves with the given parameters.\n "); -static PyMethodDef __pyx_mdef_9fontTools_5cu2qu_5cu2qu_6curves_to_quadratic = {"curves_to_quadratic", (PyCFunction)(void*)(__Pyx_PyCFunction_FastCallWithKeywords)__pyx_pw_9fontTools_5cu2qu_5cu2qu_6curves_to_quadratic, __Pyx_METH_FASTCALL|METH_KEYWORDS, __pyx_doc_9fontTools_5cu2qu_5cu2qu_5curves_to_quadratic}; -static PyObject *__pyx_pw_9fontTools_5cu2qu_5cu2qu_6curves_to_quadratic(PyObject *__pyx_self, -#if CYTHON_METH_FASTCALL -PyObject *const *__pyx_args, Py_ssize_t __pyx_nargs, PyObject *__pyx_kwds -#else -PyObject *__pyx_args, PyObject *__pyx_kwds -#endif -) { - PyObject *__pyx_v_curves = 0; - PyObject *__pyx_v_max_errors = 0; - int __pyx_v_all_quadratic; - #if !CYTHON_METH_FASTCALL - CYTHON_UNUSED Py_ssize_t __pyx_nargs; - #endif - CYTHON_UNUSED PyObject *const *__pyx_kwvalues; - PyObject* values[3] = {0,0,0}; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - PyObject *__pyx_r = 0; - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("curves_to_quadratic (wrapper)", 0); - #if !CYTHON_METH_FASTCALL - #if CYTHON_ASSUME_SAFE_MACROS - __pyx_nargs = PyTuple_GET_SIZE(__pyx_args); - #else - __pyx_nargs = PyTuple_Size(__pyx_args); if (unlikely(__pyx_nargs < 0)) return NULL; - #endif - #endif - __pyx_kwvalues = __Pyx_KwValues_FASTCALL(__pyx_args, __pyx_nargs); - { - PyObject **__pyx_pyargnames[] = {&__pyx_n_s_curves,&__pyx_n_s_max_errors,&__pyx_n_s_all_quadratic,0}; - if (__pyx_kwds) { - Py_ssize_t kw_args; - switch (__pyx_nargs) { - case 3: values[2] = __Pyx_Arg_FASTCALL(__pyx_args, 2); - CYTHON_FALLTHROUGH; - case 2: values[1] = __Pyx_Arg_FASTCALL(__pyx_args, 1); - CYTHON_FALLTHROUGH; - case 1: values[0] = __Pyx_Arg_FASTCALL(__pyx_args, 0); - CYTHON_FALLTHROUGH; - case 0: break; - default: goto __pyx_L5_argtuple_error; - } - kw_args = __Pyx_NumKwargs_FASTCALL(__pyx_kwds); - switch (__pyx_nargs) { - case 0: - if (likely((values[0] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_curves)) != 0)) { - (void)__Pyx_Arg_NewRef_FASTCALL(values[0]); - kw_args--; - } - else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 474, __pyx_L3_error) - else goto __pyx_L5_argtuple_error; - CYTHON_FALLTHROUGH; - case 1: - if (likely((values[1] = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_max_errors)) != 0)) { - (void)__Pyx_Arg_NewRef_FASTCALL(values[1]); - kw_args--; - } - else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 474, __pyx_L3_error) - else { - __Pyx_RaiseArgtupleInvalid("curves_to_quadratic", 0, 2, 3, 1); __PYX_ERR(0, 474, __pyx_L3_error) - } - CYTHON_FALLTHROUGH; - case 2: - if (kw_args > 0) { - PyObject* value = __Pyx_GetKwValue_FASTCALL(__pyx_kwds, __pyx_kwvalues, __pyx_n_s_all_quadratic); - if (value) { values[2] = __Pyx_Arg_NewRef_FASTCALL(value); kw_args--; } - else if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 474, __pyx_L3_error) - } - } - if (unlikely(kw_args > 0)) { - const Py_ssize_t kwd_pos_args = __pyx_nargs; - if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_kwvalues, __pyx_pyargnames, 0, values + 0, kwd_pos_args, "curves_to_quadratic") < 0)) __PYX_ERR(0, 474, __pyx_L3_error) - } - } else { - switch (__pyx_nargs) { - case 3: values[2] = __Pyx_Arg_FASTCALL(__pyx_args, 2); - CYTHON_FALLTHROUGH; - case 2: values[1] = __Pyx_Arg_FASTCALL(__pyx_args, 1); - values[0] = __Pyx_Arg_FASTCALL(__pyx_args, 0); - break; - default: goto __pyx_L5_argtuple_error; - } - } - __pyx_v_curves = values[0]; - __pyx_v_max_errors = values[1]; - if (values[2]) { - __pyx_v_all_quadratic = __Pyx_PyInt_As_int(values[2]); if (unlikely((__pyx_v_all_quadratic == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 476, __pyx_L3_error) - } else { - - /* "fontTools/cu2qu/cu2qu.py":476 - * @cython.locals(l=cython.int, last_i=cython.int, i=cython.int) - * @cython.locals(all_quadratic=cython.int) - * def curves_to_quadratic(curves, max_errors, all_quadratic=True): # <<<<<<<<<<<<<< - * """Return quadratic Bezier splines approximating the input cubic Beziers. - * - */ - __pyx_v_all_quadratic = ((int)((int)1)); - } - } - goto __pyx_L6_skip; - __pyx_L5_argtuple_error:; - __Pyx_RaiseArgtupleInvalid("curves_to_quadratic", 0, 2, 3, __pyx_nargs); __PYX_ERR(0, 474, __pyx_L3_error) - __pyx_L6_skip:; - goto __pyx_L4_argument_unpacking_done; - __pyx_L3_error:; - { - Py_ssize_t __pyx_temp; - for (__pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { - __Pyx_Arg_XDECREF_FASTCALL(values[__pyx_temp]); - } - } - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.curves_to_quadratic", __pyx_clineno, __pyx_lineno, __pyx_filename); - __Pyx_RefNannyFinishContext(); - return NULL; - __pyx_L4_argument_unpacking_done:; - __pyx_r = __pyx_pf_9fontTools_5cu2qu_5cu2qu_5curves_to_quadratic(__pyx_self, __pyx_v_curves, __pyx_v_max_errors, __pyx_v_all_quadratic); - - /* "fontTools/cu2qu/cu2qu.py":474 - * - * - * @cython.locals(l=cython.int, last_i=cython.int, i=cython.int) # <<<<<<<<<<<<<< - * @cython.locals(all_quadratic=cython.int) - * def curves_to_quadratic(curves, max_errors, all_quadratic=True): - */ - - /* function exit code */ - { - Py_ssize_t __pyx_temp; - for (__pyx_temp=0; __pyx_temp < (Py_ssize_t)(sizeof(values)/sizeof(values[0])); ++__pyx_temp) { - __Pyx_Arg_XDECREF_FASTCALL(values[__pyx_temp]); - } - } - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -static PyObject *__pyx_pf_9fontTools_5cu2qu_5cu2qu_5curves_to_quadratic(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_curves, PyObject *__pyx_v_max_errors, int __pyx_v_all_quadratic) { - int __pyx_v_l; - int __pyx_v_last_i; - int __pyx_v_i; - PyObject *__pyx_v_splines = NULL; - PyObject *__pyx_v_n = NULL; - PyObject *__pyx_v_spline = NULL; - PyObject *__pyx_8genexpr2__pyx_v_curve = NULL; - PyObject *__pyx_8genexpr3__pyx_v_p = NULL; - PyObject *__pyx_8genexpr4__pyx_v_spline = NULL; - PyObject *__pyx_8genexpr5__pyx_v_s = NULL; - PyObject *__pyx_r = NULL; - __Pyx_RefNannyDeclarations - PyObject *__pyx_t_1 = NULL; - PyObject *__pyx_t_2 = NULL; - Py_ssize_t __pyx_t_3; - PyObject *(*__pyx_t_4)(PyObject *); - PyObject *__pyx_t_5 = NULL; - PyObject *__pyx_t_6 = NULL; - Py_ssize_t __pyx_t_7; - PyObject *(*__pyx_t_8)(PyObject *); - PyObject *__pyx_t_9 = NULL; - PyObject *__pyx_t_10 = NULL; - int __pyx_t_11; - int __pyx_t_12; - double __pyx_t_13; - long __pyx_t_14; - PyObject *__pyx_t_15 = NULL; - unsigned int __pyx_t_16; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("curves_to_quadratic", 0); - __Pyx_INCREF(__pyx_v_curves); - - /* "fontTools/cu2qu/cu2qu.py":513 - * """ - * - * curves = [[complex(*p) for p in curve] for curve in curves] # <<<<<<<<<<<<<< - * assert len(max_errors) == len(curves) - * - */ - { /* enter inner scope */ - __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 513, __pyx_L5_error) - __Pyx_GOTREF(__pyx_t_1); - if (likely(PyList_CheckExact(__pyx_v_curves)) || PyTuple_CheckExact(__pyx_v_curves)) { - __pyx_t_2 = __pyx_v_curves; __Pyx_INCREF(__pyx_t_2); - __pyx_t_3 = 0; - __pyx_t_4 = NULL; - } else { - __pyx_t_3 = -1; __pyx_t_2 = PyObject_GetIter(__pyx_v_curves); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 513, __pyx_L5_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_4 = __Pyx_PyObject_GetIterNextFunc(__pyx_t_2); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 513, __pyx_L5_error) - } - for (;;) { - if (likely(!__pyx_t_4)) { - if (likely(PyList_CheckExact(__pyx_t_2))) { - { - Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_2); - #if !CYTHON_ASSUME_SAFE_MACROS - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 513, __pyx_L5_error) - #endif - if (__pyx_t_3 >= __pyx_temp) break; - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - __pyx_t_5 = PyList_GET_ITEM(__pyx_t_2, __pyx_t_3); __Pyx_INCREF(__pyx_t_5); __pyx_t_3++; if (unlikely((0 < 0))) __PYX_ERR(0, 513, __pyx_L5_error) - #else - __pyx_t_5 = __Pyx_PySequence_ITEM(__pyx_t_2, __pyx_t_3); __pyx_t_3++; if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 513, __pyx_L5_error) - __Pyx_GOTREF(__pyx_t_5); - #endif - } else { - { - Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_2); - #if !CYTHON_ASSUME_SAFE_MACROS - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 513, __pyx_L5_error) - #endif - if (__pyx_t_3 >= __pyx_temp) break; - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - __pyx_t_5 = PyTuple_GET_ITEM(__pyx_t_2, __pyx_t_3); __Pyx_INCREF(__pyx_t_5); __pyx_t_3++; if (unlikely((0 < 0))) __PYX_ERR(0, 513, __pyx_L5_error) - #else - __pyx_t_5 = __Pyx_PySequence_ITEM(__pyx_t_2, __pyx_t_3); __pyx_t_3++; if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 513, __pyx_L5_error) - __Pyx_GOTREF(__pyx_t_5); - #endif - } - } else { - __pyx_t_5 = __pyx_t_4(__pyx_t_2); - if (unlikely(!__pyx_t_5)) { - PyObject* exc_type = PyErr_Occurred(); - if (exc_type) { - if (likely(__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) PyErr_Clear(); - else __PYX_ERR(0, 513, __pyx_L5_error) - } - break; - } - __Pyx_GOTREF(__pyx_t_5); - } - __Pyx_XDECREF_SET(__pyx_8genexpr2__pyx_v_curve, __pyx_t_5); - __pyx_t_5 = 0; - { /* enter inner scope */ - __pyx_t_5 = PyList_New(0); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 513, __pyx_L10_error) - __Pyx_GOTREF(__pyx_t_5); - if (likely(PyList_CheckExact(__pyx_8genexpr2__pyx_v_curve)) || PyTuple_CheckExact(__pyx_8genexpr2__pyx_v_curve)) { - __pyx_t_6 = __pyx_8genexpr2__pyx_v_curve; __Pyx_INCREF(__pyx_t_6); - __pyx_t_7 = 0; - __pyx_t_8 = NULL; - } else { - __pyx_t_7 = -1; __pyx_t_6 = PyObject_GetIter(__pyx_8genexpr2__pyx_v_curve); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 513, __pyx_L10_error) - __Pyx_GOTREF(__pyx_t_6); - __pyx_t_8 = __Pyx_PyObject_GetIterNextFunc(__pyx_t_6); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 513, __pyx_L10_error) - } - for (;;) { - if (likely(!__pyx_t_8)) { - if (likely(PyList_CheckExact(__pyx_t_6))) { - { - Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_6); - #if !CYTHON_ASSUME_SAFE_MACROS - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 513, __pyx_L10_error) - #endif - if (__pyx_t_7 >= __pyx_temp) break; - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - __pyx_t_9 = PyList_GET_ITEM(__pyx_t_6, __pyx_t_7); __Pyx_INCREF(__pyx_t_9); __pyx_t_7++; if (unlikely((0 < 0))) __PYX_ERR(0, 513, __pyx_L10_error) - #else - __pyx_t_9 = __Pyx_PySequence_ITEM(__pyx_t_6, __pyx_t_7); __pyx_t_7++; if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 513, __pyx_L10_error) - __Pyx_GOTREF(__pyx_t_9); - #endif - } else { - { - Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_6); - #if !CYTHON_ASSUME_SAFE_MACROS - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 513, __pyx_L10_error) - #endif - if (__pyx_t_7 >= __pyx_temp) break; - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - __pyx_t_9 = PyTuple_GET_ITEM(__pyx_t_6, __pyx_t_7); __Pyx_INCREF(__pyx_t_9); __pyx_t_7++; if (unlikely((0 < 0))) __PYX_ERR(0, 513, __pyx_L10_error) - #else - __pyx_t_9 = __Pyx_PySequence_ITEM(__pyx_t_6, __pyx_t_7); __pyx_t_7++; if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 513, __pyx_L10_error) - __Pyx_GOTREF(__pyx_t_9); - #endif - } - } else { - __pyx_t_9 = __pyx_t_8(__pyx_t_6); - if (unlikely(!__pyx_t_9)) { - PyObject* exc_type = PyErr_Occurred(); - if (exc_type) { - if (likely(__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) PyErr_Clear(); - else __PYX_ERR(0, 513, __pyx_L10_error) - } - break; - } - __Pyx_GOTREF(__pyx_t_9); - } - __Pyx_XDECREF_SET(__pyx_8genexpr3__pyx_v_p, __pyx_t_9); - __pyx_t_9 = 0; - __pyx_t_9 = __Pyx_PySequence_Tuple(__pyx_8genexpr3__pyx_v_p); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 513, __pyx_L10_error) - __Pyx_GOTREF(__pyx_t_9); - __pyx_t_10 = __Pyx_PyObject_Call(((PyObject *)(&PyComplex_Type)), __pyx_t_9, NULL); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 513, __pyx_L10_error) - __Pyx_GOTREF(__pyx_t_10); - __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0; - if (unlikely(__Pyx_ListComp_Append(__pyx_t_5, (PyObject*)__pyx_t_10))) __PYX_ERR(0, 513, __pyx_L10_error) - __Pyx_DECREF(__pyx_t_10); __pyx_t_10 = 0; - } - __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; - __Pyx_XDECREF(__pyx_8genexpr3__pyx_v_p); __pyx_8genexpr3__pyx_v_p = 0; - goto __pyx_L14_exit_scope; - __pyx_L10_error:; - __Pyx_XDECREF(__pyx_8genexpr3__pyx_v_p); __pyx_8genexpr3__pyx_v_p = 0; - goto __pyx_L5_error; - __pyx_L14_exit_scope:; - } /* exit inner scope */ - if (unlikely(__Pyx_ListComp_Append(__pyx_t_1, (PyObject*)__pyx_t_5))) __PYX_ERR(0, 513, __pyx_L5_error) - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - } - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __Pyx_XDECREF(__pyx_8genexpr2__pyx_v_curve); __pyx_8genexpr2__pyx_v_curve = 0; - goto __pyx_L16_exit_scope; - __pyx_L5_error:; - __Pyx_XDECREF(__pyx_8genexpr2__pyx_v_curve); __pyx_8genexpr2__pyx_v_curve = 0; - goto __pyx_L1_error; - __pyx_L16_exit_scope:; - } /* exit inner scope */ - __Pyx_DECREF_SET(__pyx_v_curves, __pyx_t_1); - __pyx_t_1 = 0; - - /* "fontTools/cu2qu/cu2qu.py":514 - * - * curves = [[complex(*p) for p in curve] for curve in curves] - * assert len(max_errors) == len(curves) # <<<<<<<<<<<<<< - * - * l = len(curves) - */ - #ifndef CYTHON_WITHOUT_ASSERTIONS - if (unlikely(__pyx_assertions_enabled())) { - __pyx_t_3 = PyObject_Length(__pyx_v_max_errors); if (unlikely(__pyx_t_3 == ((Py_ssize_t)-1))) __PYX_ERR(0, 514, __pyx_L1_error) - __pyx_t_7 = PyObject_Length(__pyx_v_curves); if (unlikely(__pyx_t_7 == ((Py_ssize_t)-1))) __PYX_ERR(0, 514, __pyx_L1_error) - __pyx_t_11 = (__pyx_t_3 == __pyx_t_7); - if (unlikely(!__pyx_t_11)) { - __Pyx_Raise(__pyx_builtin_AssertionError, 0, 0, 0); - __PYX_ERR(0, 514, __pyx_L1_error) - } - } - #else - if ((1)); else __PYX_ERR(0, 514, __pyx_L1_error) - #endif - - /* "fontTools/cu2qu/cu2qu.py":516 - * assert len(max_errors) == len(curves) - * - * l = len(curves) # <<<<<<<<<<<<<< - * splines = [None] * l - * last_i = i = 0 - */ - __pyx_t_7 = PyObject_Length(__pyx_v_curves); if (unlikely(__pyx_t_7 == ((Py_ssize_t)-1))) __PYX_ERR(0, 516, __pyx_L1_error) - __pyx_v_l = __pyx_t_7; - - /* "fontTools/cu2qu/cu2qu.py":517 - * - * l = len(curves) - * splines = [None] * l # <<<<<<<<<<<<<< - * last_i = i = 0 - * n = 1 - */ - __pyx_t_1 = PyList_New(1 * ((__pyx_v_l<0) ? 0:__pyx_v_l)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 517, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - { Py_ssize_t __pyx_temp; - for (__pyx_temp=0; __pyx_temp < __pyx_v_l; __pyx_temp++) { - __Pyx_INCREF(Py_None); - __Pyx_GIVEREF(Py_None); - if (__Pyx_PyList_SET_ITEM(__pyx_t_1, __pyx_temp, Py_None)) __PYX_ERR(0, 517, __pyx_L1_error); - } - } - __pyx_v_splines = ((PyObject*)__pyx_t_1); - __pyx_t_1 = 0; - - /* "fontTools/cu2qu/cu2qu.py":518 - * l = len(curves) - * splines = [None] * l - * last_i = i = 0 # <<<<<<<<<<<<<< - * n = 1 - * while True: - */ - __pyx_v_last_i = 0; - __pyx_v_i = 0; - - /* "fontTools/cu2qu/cu2qu.py":519 - * splines = [None] * l - * last_i = i = 0 - * n = 1 # <<<<<<<<<<<<<< - * while True: - * spline = cubic_approx_spline(curves[i], n, max_errors[i], all_quadratic) - */ - __Pyx_INCREF(__pyx_int_1); - __pyx_v_n = __pyx_int_1; - - /* "fontTools/cu2qu/cu2qu.py":520 - * last_i = i = 0 - * n = 1 - * while True: # <<<<<<<<<<<<<< - * spline = cubic_approx_spline(curves[i], n, max_errors[i], all_quadratic) - * if spline is None: - */ - while (1) { - - /* "fontTools/cu2qu/cu2qu.py":521 - * n = 1 - * while True: - * spline = cubic_approx_spline(curves[i], n, max_errors[i], all_quadratic) # <<<<<<<<<<<<<< - * if spline is None: - * if n == MAX_N: - */ - __pyx_t_1 = __Pyx_GetItemInt(__pyx_v_curves, __pyx_v_i, int, 1, __Pyx_PyInt_From_int, 0, 1, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 521, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_12 = __Pyx_PyInt_As_int(__pyx_v_n); if (unlikely((__pyx_t_12 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 521, __pyx_L1_error) - __pyx_t_2 = __Pyx_GetItemInt(__pyx_v_max_errors, __pyx_v_i, int, 1, __Pyx_PyInt_From_int, 0, 1, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 521, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_13 = __pyx_PyFloat_AsDouble(__pyx_t_2); if (unlikely((__pyx_t_13 == (double)-1) && PyErr_Occurred())) __PYX_ERR(0, 521, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_2 = __pyx_f_9fontTools_5cu2qu_5cu2qu_cubic_approx_spline(__pyx_t_1, __pyx_t_12, __pyx_t_13, __pyx_v_all_quadratic); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 521, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __Pyx_XDECREF_SET(__pyx_v_spline, __pyx_t_2); - __pyx_t_2 = 0; - - /* "fontTools/cu2qu/cu2qu.py":522 - * while True: - * spline = cubic_approx_spline(curves[i], n, max_errors[i], all_quadratic) - * if spline is None: # <<<<<<<<<<<<<< - * if n == MAX_N: - * break - */ - __pyx_t_11 = (__pyx_v_spline == Py_None); - if (__pyx_t_11) { - - /* "fontTools/cu2qu/cu2qu.py":523 - * spline = cubic_approx_spline(curves[i], n, max_errors[i], all_quadratic) - * if spline is None: - * if n == MAX_N: # <<<<<<<<<<<<<< - * break - * n += 1 - */ - __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_MAX_N); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 523, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_1 = PyObject_RichCompare(__pyx_v_n, __pyx_t_2, Py_EQ); __Pyx_XGOTREF(__pyx_t_1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 523, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __pyx_t_11 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely((__pyx_t_11 < 0))) __PYX_ERR(0, 523, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - if (__pyx_t_11) { - - /* "fontTools/cu2qu/cu2qu.py":524 - * if spline is None: - * if n == MAX_N: - * break # <<<<<<<<<<<<<< - * n += 1 - * last_i = i - */ - goto __pyx_L18_break; - - /* "fontTools/cu2qu/cu2qu.py":523 - * spline = cubic_approx_spline(curves[i], n, max_errors[i], all_quadratic) - * if spline is None: - * if n == MAX_N: # <<<<<<<<<<<<<< - * break - * n += 1 - */ - } - - /* "fontTools/cu2qu/cu2qu.py":525 - * if n == MAX_N: - * break - * n += 1 # <<<<<<<<<<<<<< - * last_i = i - * continue - */ - __pyx_t_1 = __Pyx_PyInt_AddObjC(__pyx_v_n, __pyx_int_1, 1, 1, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 525, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __Pyx_DECREF_SET(__pyx_v_n, __pyx_t_1); - __pyx_t_1 = 0; - - /* "fontTools/cu2qu/cu2qu.py":526 - * break - * n += 1 - * last_i = i # <<<<<<<<<<<<<< - * continue - * splines[i] = spline - */ - __pyx_v_last_i = __pyx_v_i; - - /* "fontTools/cu2qu/cu2qu.py":527 - * n += 1 - * last_i = i - * continue # <<<<<<<<<<<<<< - * splines[i] = spline - * i = (i + 1) % l - */ - goto __pyx_L17_continue; - - /* "fontTools/cu2qu/cu2qu.py":522 - * while True: - * spline = cubic_approx_spline(curves[i], n, max_errors[i], all_quadratic) - * if spline is None: # <<<<<<<<<<<<<< - * if n == MAX_N: - * break - */ - } - - /* "fontTools/cu2qu/cu2qu.py":528 - * last_i = i - * continue - * splines[i] = spline # <<<<<<<<<<<<<< - * i = (i + 1) % l - * if i == last_i: - */ - if (unlikely((__Pyx_SetItemInt(__pyx_v_splines, __pyx_v_i, __pyx_v_spline, int, 1, __Pyx_PyInt_From_int, 1, 1, 1) < 0))) __PYX_ERR(0, 528, __pyx_L1_error) - - /* "fontTools/cu2qu/cu2qu.py":529 - * continue - * splines[i] = spline - * i = (i + 1) % l # <<<<<<<<<<<<<< - * if i == last_i: - * # done. go home - */ - __pyx_t_14 = (__pyx_v_i + 1); - if (unlikely(__pyx_v_l == 0)) { - PyErr_SetString(PyExc_ZeroDivisionError, "integer division or modulo by zero"); - __PYX_ERR(0, 529, __pyx_L1_error) - } - __pyx_v_i = __Pyx_mod_long(__pyx_t_14, __pyx_v_l); - - /* "fontTools/cu2qu/cu2qu.py":530 - * splines[i] = spline - * i = (i + 1) % l - * if i == last_i: # <<<<<<<<<<<<<< - * # done. go home - * return [[(s.real, s.imag) for s in spline] for spline in splines] - */ - __pyx_t_11 = (__pyx_v_i == __pyx_v_last_i); - if (__pyx_t_11) { - - /* "fontTools/cu2qu/cu2qu.py":532 - * if i == last_i: - * # done. go home - * return [[(s.real, s.imag) for s in spline] for spline in splines] # <<<<<<<<<<<<<< - * - * raise ApproxNotFoundError(curves) - */ - __Pyx_XDECREF(__pyx_r); - { /* enter inner scope */ - __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 532, __pyx_L24_error) - __Pyx_GOTREF(__pyx_t_1); - __pyx_t_2 = __pyx_v_splines; __Pyx_INCREF(__pyx_t_2); - __pyx_t_7 = 0; - for (;;) { - { - Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_2); - #if !CYTHON_ASSUME_SAFE_MACROS - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 532, __pyx_L24_error) - #endif - if (__pyx_t_7 >= __pyx_temp) break; - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - __pyx_t_5 = PyList_GET_ITEM(__pyx_t_2, __pyx_t_7); __Pyx_INCREF(__pyx_t_5); __pyx_t_7++; if (unlikely((0 < 0))) __PYX_ERR(0, 532, __pyx_L24_error) - #else - __pyx_t_5 = __Pyx_PySequence_ITEM(__pyx_t_2, __pyx_t_7); __pyx_t_7++; if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 532, __pyx_L24_error) - __Pyx_GOTREF(__pyx_t_5); - #endif - __Pyx_XDECREF_SET(__pyx_8genexpr4__pyx_v_spline, __pyx_t_5); - __pyx_t_5 = 0; - { /* enter inner scope */ - __pyx_t_5 = PyList_New(0); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 532, __pyx_L29_error) - __Pyx_GOTREF(__pyx_t_5); - if (likely(PyList_CheckExact(__pyx_8genexpr4__pyx_v_spline)) || PyTuple_CheckExact(__pyx_8genexpr4__pyx_v_spline)) { - __pyx_t_6 = __pyx_8genexpr4__pyx_v_spline; __Pyx_INCREF(__pyx_t_6); - __pyx_t_3 = 0; - __pyx_t_4 = NULL; - } else { - __pyx_t_3 = -1; __pyx_t_6 = PyObject_GetIter(__pyx_8genexpr4__pyx_v_spline); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 532, __pyx_L29_error) - __Pyx_GOTREF(__pyx_t_6); - __pyx_t_4 = __Pyx_PyObject_GetIterNextFunc(__pyx_t_6); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 532, __pyx_L29_error) - } - for (;;) { - if (likely(!__pyx_t_4)) { - if (likely(PyList_CheckExact(__pyx_t_6))) { - { - Py_ssize_t __pyx_temp = __Pyx_PyList_GET_SIZE(__pyx_t_6); - #if !CYTHON_ASSUME_SAFE_MACROS - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 532, __pyx_L29_error) - #endif - if (__pyx_t_3 >= __pyx_temp) break; - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - __pyx_t_10 = PyList_GET_ITEM(__pyx_t_6, __pyx_t_3); __Pyx_INCREF(__pyx_t_10); __pyx_t_3++; if (unlikely((0 < 0))) __PYX_ERR(0, 532, __pyx_L29_error) - #else - __pyx_t_10 = __Pyx_PySequence_ITEM(__pyx_t_6, __pyx_t_3); __pyx_t_3++; if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 532, __pyx_L29_error) - __Pyx_GOTREF(__pyx_t_10); - #endif - } else { - { - Py_ssize_t __pyx_temp = __Pyx_PyTuple_GET_SIZE(__pyx_t_6); - #if !CYTHON_ASSUME_SAFE_MACROS - if (unlikely((__pyx_temp < 0))) __PYX_ERR(0, 532, __pyx_L29_error) - #endif - if (__pyx_t_3 >= __pyx_temp) break; - } - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - __pyx_t_10 = PyTuple_GET_ITEM(__pyx_t_6, __pyx_t_3); __Pyx_INCREF(__pyx_t_10); __pyx_t_3++; if (unlikely((0 < 0))) __PYX_ERR(0, 532, __pyx_L29_error) - #else - __pyx_t_10 = __Pyx_PySequence_ITEM(__pyx_t_6, __pyx_t_3); __pyx_t_3++; if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 532, __pyx_L29_error) - __Pyx_GOTREF(__pyx_t_10); - #endif - } - } else { - __pyx_t_10 = __pyx_t_4(__pyx_t_6); - if (unlikely(!__pyx_t_10)) { - PyObject* exc_type = PyErr_Occurred(); - if (exc_type) { - if (likely(__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) PyErr_Clear(); - else __PYX_ERR(0, 532, __pyx_L29_error) - } - break; - } - __Pyx_GOTREF(__pyx_t_10); - } - __Pyx_XDECREF_SET(__pyx_8genexpr5__pyx_v_s, __pyx_t_10); - __pyx_t_10 = 0; - __pyx_t_10 = __Pyx_PyObject_GetAttrStr(__pyx_8genexpr5__pyx_v_s, __pyx_n_s_real); if (unlikely(!__pyx_t_10)) __PYX_ERR(0, 532, __pyx_L29_error) - __Pyx_GOTREF(__pyx_t_10); - __pyx_t_9 = __Pyx_PyObject_GetAttrStr(__pyx_8genexpr5__pyx_v_s, __pyx_n_s_imag); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 532, __pyx_L29_error) - __Pyx_GOTREF(__pyx_t_9); - __pyx_t_15 = PyTuple_New(2); if (unlikely(!__pyx_t_15)) __PYX_ERR(0, 532, __pyx_L29_error) - __Pyx_GOTREF(__pyx_t_15); - __Pyx_GIVEREF(__pyx_t_10); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_15, 0, __pyx_t_10)) __PYX_ERR(0, 532, __pyx_L29_error); - __Pyx_GIVEREF(__pyx_t_9); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_15, 1, __pyx_t_9)) __PYX_ERR(0, 532, __pyx_L29_error); - __pyx_t_10 = 0; - __pyx_t_9 = 0; - if (unlikely(__Pyx_ListComp_Append(__pyx_t_5, (PyObject*)__pyx_t_15))) __PYX_ERR(0, 532, __pyx_L29_error) - __Pyx_DECREF(__pyx_t_15); __pyx_t_15 = 0; - } - __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; - __Pyx_XDECREF(__pyx_8genexpr5__pyx_v_s); __pyx_8genexpr5__pyx_v_s = 0; - goto __pyx_L33_exit_scope; - __pyx_L29_error:; - __Pyx_XDECREF(__pyx_8genexpr5__pyx_v_s); __pyx_8genexpr5__pyx_v_s = 0; - goto __pyx_L24_error; - __pyx_L33_exit_scope:; - } /* exit inner scope */ - if (unlikely(__Pyx_ListComp_Append(__pyx_t_1, (PyObject*)__pyx_t_5))) __PYX_ERR(0, 532, __pyx_L24_error) - __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0; - } - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - __Pyx_XDECREF(__pyx_8genexpr4__pyx_v_spline); __pyx_8genexpr4__pyx_v_spline = 0; - goto __pyx_L35_exit_scope; - __pyx_L24_error:; - __Pyx_XDECREF(__pyx_8genexpr4__pyx_v_spline); __pyx_8genexpr4__pyx_v_spline = 0; - goto __pyx_L1_error; - __pyx_L35_exit_scope:; - } /* exit inner scope */ - __pyx_r = __pyx_t_1; - __pyx_t_1 = 0; - goto __pyx_L0; - - /* "fontTools/cu2qu/cu2qu.py":530 - * splines[i] = spline - * i = (i + 1) % l - * if i == last_i: # <<<<<<<<<<<<<< - * # done. go home - * return [[(s.real, s.imag) for s in spline] for spline in splines] - */ - } - __pyx_L17_continue:; - } - __pyx_L18_break:; - - /* "fontTools/cu2qu/cu2qu.py":534 - * return [[(s.real, s.imag) for s in spline] for spline in splines] - * - * raise ApproxNotFoundError(curves) # <<<<<<<<<<<<<< - */ - __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_ApproxNotFoundError); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 534, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_2); - __pyx_t_5 = NULL; - __pyx_t_16 = 0; - #if CYTHON_UNPACK_METHODS - if (unlikely(PyMethod_Check(__pyx_t_2))) { - __pyx_t_5 = PyMethod_GET_SELF(__pyx_t_2); - if (likely(__pyx_t_5)) { - PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_2); - __Pyx_INCREF(__pyx_t_5); - __Pyx_INCREF(function); - __Pyx_DECREF_SET(__pyx_t_2, function); - __pyx_t_16 = 1; - } - } - #endif - { - PyObject *__pyx_callargs[2] = {__pyx_t_5, __pyx_v_curves}; - __pyx_t_1 = __Pyx_PyObject_FastCall(__pyx_t_2, __pyx_callargs+1-__pyx_t_16, 1+__pyx_t_16); - __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0; - if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 534, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_1); - __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0; - } - __Pyx_Raise(__pyx_t_1, 0, 0, 0); - __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0; - __PYX_ERR(0, 534, __pyx_L1_error) - - /* "fontTools/cu2qu/cu2qu.py":474 - * - * - * @cython.locals(l=cython.int, last_i=cython.int, i=cython.int) # <<<<<<<<<<<<<< - * @cython.locals(all_quadratic=cython.int) - * def curves_to_quadratic(curves, max_errors, all_quadratic=True): - */ - - /* function exit code */ - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_1); - __Pyx_XDECREF(__pyx_t_2); - __Pyx_XDECREF(__pyx_t_5); - __Pyx_XDECREF(__pyx_t_6); - __Pyx_XDECREF(__pyx_t_9); - __Pyx_XDECREF(__pyx_t_10); - __Pyx_XDECREF(__pyx_t_15); - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu.curves_to_quadratic", __pyx_clineno, __pyx_lineno, __pyx_filename); - __pyx_r = NULL; - __pyx_L0:; - __Pyx_XDECREF(__pyx_v_splines); - __Pyx_XDECREF(__pyx_v_n); - __Pyx_XDECREF(__pyx_v_spline); - __Pyx_XDECREF(__pyx_8genexpr2__pyx_v_curve); - __Pyx_XDECREF(__pyx_8genexpr3__pyx_v_p); - __Pyx_XDECREF(__pyx_8genexpr4__pyx_v_spline); - __Pyx_XDECREF(__pyx_8genexpr5__pyx_v_s); - __Pyx_XDECREF(__pyx_v_curves); - __Pyx_XGIVEREF(__pyx_r); - __Pyx_RefNannyFinishContext(); - return __pyx_r; -} - -#if CYTHON_USE_FREELISTS -static struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen *__pyx_freelist_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen[8]; -static int __pyx_freecount_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen = 0; -#endif - -static PyObject *__pyx_tp_new_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen(PyTypeObject *t, CYTHON_UNUSED PyObject *a, CYTHON_UNUSED PyObject *k) { - PyObject *o; - #if CYTHON_COMPILING_IN_LIMITED_API - allocfunc alloc_func = (allocfunc)PyType_GetSlot(t, Py_tp_alloc); - o = alloc_func(t, 0); - #else - #if CYTHON_USE_FREELISTS - if (likely((int)(__pyx_freecount_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen > 0) & (int)(t->tp_basicsize == sizeof(struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen)))) { - o = (PyObject*)__pyx_freelist_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen[--__pyx_freecount_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen]; - memset(o, 0, sizeof(struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen)); - (void) PyObject_INIT(o, t); - } else - #endif - { - o = (*t->tp_alloc)(t, 0); - if (unlikely(!o)) return 0; - } - #endif - return o; -} - -static void __pyx_tp_dealloc_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen(PyObject *o) { - #if CYTHON_USE_TP_FINALIZE - if (unlikely((PY_VERSION_HEX >= 0x03080000 || __Pyx_PyType_HasFeature(Py_TYPE(o), Py_TPFLAGS_HAVE_FINALIZE)) && __Pyx_PyObject_GetSlot(o, tp_finalize, destructor)) && (!PyType_IS_GC(Py_TYPE(o)) || !__Pyx_PyObject_GC_IsFinalized(o))) { - if (__Pyx_PyObject_GetSlot(o, tp_dealloc, destructor) == __pyx_tp_dealloc_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen) { - if (PyObject_CallFinalizerFromDealloc(o)) return; - } - } - #endif - #if CYTHON_USE_FREELISTS - if (((int)(__pyx_freecount_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen < 8) & (int)(Py_TYPE(o)->tp_basicsize == sizeof(struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen)))) { - __pyx_freelist_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen[__pyx_freecount_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen++] = ((struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen *)o); - } else - #endif - { - #if CYTHON_USE_TYPE_SLOTS || CYTHON_COMPILING_IN_PYPY - (*Py_TYPE(o)->tp_free)(o); - #else - { - freefunc tp_free = (freefunc)PyType_GetSlot(Py_TYPE(o), Py_tp_free); - if (tp_free) tp_free(o); - } - #endif - } -} -#if CYTHON_USE_TYPE_SPECS -static PyType_Slot __pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen_slots[] = { - {Py_tp_dealloc, (void *)__pyx_tp_dealloc_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen}, - {Py_tp_new, (void *)__pyx_tp_new_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen}, - {0, 0}, -}; -static PyType_Spec __pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen_spec = { - "fontTools.cu2qu.cu2qu.__pyx_scope_struct___split_cubic_into_n_gen", - sizeof(struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen), - 0, - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_FINALIZE, - __pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen_slots, -}; -#else - -static PyTypeObject __pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen = { - PyVarObject_HEAD_INIT(0, 0) - "fontTools.cu2qu.cu2qu.""__pyx_scope_struct___split_cubic_into_n_gen", /*tp_name*/ - sizeof(struct __pyx_obj_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - __pyx_tp_dealloc_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen, /*tp_dealloc*/ - #if PY_VERSION_HEX < 0x030800b4 - 0, /*tp_print*/ - #endif - #if PY_VERSION_HEX >= 0x030800b4 - 0, /*tp_vectorcall_offset*/ - #endif - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - #if PY_MAJOR_VERSION < 3 - 0, /*tp_compare*/ - #endif - #if PY_MAJOR_VERSION >= 3 - 0, /*tp_as_async*/ - #endif - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_HAVE_FINALIZE, /*tp_flags*/ - 0, /*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*/ - #if !CYTHON_USE_TYPE_SPECS - 0, /*tp_dictoffset*/ - #endif - 0, /*tp_init*/ - 0, /*tp_alloc*/ - __pyx_tp_new_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen, /*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 >= 0x030400a1 - #if CYTHON_USE_TP_FINALIZE - 0, /*tp_finalize*/ - #else - NULL, /*tp_finalize*/ - #endif - #endif - #if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800) - 0, /*tp_vectorcall*/ - #endif - #if __PYX_NEED_TP_PRINT_SLOT == 1 - 0, /*tp_print*/ - #endif - #if PY_VERSION_HEX >= 0x030C0000 - 0, /*tp_watched*/ - #endif - #if PY_VERSION_HEX >= 0x030d00A4 - 0, /*tp_versions_used*/ - #endif - #if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000 && PY_VERSION_HEX < 0x030a0000 - 0, /*tp_pypy_flags*/ - #endif -}; -#endif - -static PyMethodDef __pyx_methods[] = { - {0, 0, 0, 0} -}; -#ifndef CYTHON_SMALL_CODE -#if defined(__clang__) - #define CYTHON_SMALL_CODE -#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define CYTHON_SMALL_CODE __attribute__((cold)) -#else - #define CYTHON_SMALL_CODE -#endif -#endif -/* #### Code section: pystring_table ### */ - -static int __Pyx_CreateStringTabAndInitStrings(void) { - __Pyx_StringTabEntry __pyx_string_tab[] = { - {&__pyx_n_s_ApproxNotFoundError, __pyx_k_ApproxNotFoundError, sizeof(__pyx_k_ApproxNotFoundError), 0, 0, 1, 1}, - {&__pyx_n_s_AssertionError, __pyx_k_AssertionError, sizeof(__pyx_k_AssertionError), 0, 0, 1, 1}, - {&__pyx_n_s_AttributeError, __pyx_k_AttributeError, sizeof(__pyx_k_AttributeError), 0, 0, 1, 1}, - {&__pyx_n_s_COMPILED, __pyx_k_COMPILED, sizeof(__pyx_k_COMPILED), 0, 0, 1, 1}, - {&__pyx_n_s_Cu2QuError, __pyx_k_Cu2QuError, sizeof(__pyx_k_Cu2QuError), 0, 0, 1, 1}, - {&__pyx_n_s_Error, __pyx_k_Error, sizeof(__pyx_k_Error), 0, 0, 1, 1}, - {&__pyx_n_s_ImportError, __pyx_k_ImportError, sizeof(__pyx_k_ImportError), 0, 0, 1, 1}, - {&__pyx_kp_s_Lib_fontTools_cu2qu_cu2qu_py, __pyx_k_Lib_fontTools_cu2qu_cu2qu_py, sizeof(__pyx_k_Lib_fontTools_cu2qu_cu2qu_py), 0, 0, 1, 0}, - {&__pyx_n_s_MAX_N, __pyx_k_MAX_N, sizeof(__pyx_k_MAX_N), 0, 0, 1, 1}, - {&__pyx_n_s_NAN, __pyx_k_NAN, sizeof(__pyx_k_NAN), 0, 0, 1, 1}, - {&__pyx_n_u_NaN, __pyx_k_NaN, sizeof(__pyx_k_NaN), 0, 1, 0, 1}, - {&__pyx_kp_u_Return_quadratic_Bezier_splines, __pyx_k_Return_quadratic_Bezier_splines, sizeof(__pyx_k_Return_quadratic_Bezier_splines), 0, 1, 0, 0}, - {&__pyx_n_s_ZeroDivisionError, __pyx_k_ZeroDivisionError, sizeof(__pyx_k_ZeroDivisionError), 0, 0, 1, 1}, - {&__pyx_kp_u__2, __pyx_k__2, sizeof(__pyx_k__2), 0, 1, 0, 0}, - {&__pyx_n_s__3, __pyx_k__3, sizeof(__pyx_k__3), 0, 0, 1, 1}, - {&__pyx_n_s__9, __pyx_k__9, sizeof(__pyx_k__9), 0, 0, 1, 1}, - {&__pyx_n_s_a, __pyx_k_a, sizeof(__pyx_k_a), 0, 0, 1, 1}, - {&__pyx_n_s_a1, __pyx_k_a1, sizeof(__pyx_k_a1), 0, 0, 1, 1}, - {&__pyx_n_s_all, __pyx_k_all, sizeof(__pyx_k_all), 0, 0, 1, 1}, - {&__pyx_n_s_all_quadratic, __pyx_k_all_quadratic, sizeof(__pyx_k_all_quadratic), 0, 0, 1, 1}, - {&__pyx_n_s_args, __pyx_k_args, sizeof(__pyx_k_args), 0, 0, 1, 1}, - {&__pyx_n_s_asyncio_coroutines, __pyx_k_asyncio_coroutines, sizeof(__pyx_k_asyncio_coroutines), 0, 0, 1, 1}, - {&__pyx_n_s_b, __pyx_k_b, sizeof(__pyx_k_b), 0, 0, 1, 1}, - {&__pyx_n_s_b1, __pyx_k_b1, sizeof(__pyx_k_b1), 0, 0, 1, 1}, - {&__pyx_n_s_c, __pyx_k_c, sizeof(__pyx_k_c), 0, 0, 1, 1}, - {&__pyx_n_s_c1, __pyx_k_c1, sizeof(__pyx_k_c1), 0, 0, 1, 1}, - {&__pyx_n_s_cline_in_traceback, __pyx_k_cline_in_traceback, sizeof(__pyx_k_cline_in_traceback), 0, 0, 1, 1}, - {&__pyx_n_s_close, __pyx_k_close, sizeof(__pyx_k_close), 0, 0, 1, 1}, - {&__pyx_n_s_curve, __pyx_k_curve, sizeof(__pyx_k_curve), 0, 0, 1, 1}, - {&__pyx_n_s_curve_to_quadratic, __pyx_k_curve_to_quadratic, sizeof(__pyx_k_curve_to_quadratic), 0, 0, 1, 1}, - {&__pyx_n_u_curve_to_quadratic, __pyx_k_curve_to_quadratic, sizeof(__pyx_k_curve_to_quadratic), 0, 1, 0, 1}, - {&__pyx_n_s_curves, __pyx_k_curves, sizeof(__pyx_k_curves), 0, 0, 1, 1}, - {&__pyx_n_s_curves_to_quadratic, __pyx_k_curves_to_quadratic, sizeof(__pyx_k_curves_to_quadratic), 0, 0, 1, 1}, - {&__pyx_n_u_curves_to_quadratic, __pyx_k_curves_to_quadratic, sizeof(__pyx_k_curves_to_quadratic), 0, 1, 0, 1}, - {&__pyx_kp_u_curves_to_quadratic_line_474, __pyx_k_curves_to_quadratic_line_474, sizeof(__pyx_k_curves_to_quadratic_line_474), 0, 1, 0, 0}, - {&__pyx_n_s_cython, __pyx_k_cython, sizeof(__pyx_k_cython), 0, 0, 1, 1}, - {&__pyx_n_s_d, __pyx_k_d, sizeof(__pyx_k_d), 0, 0, 1, 1}, - {&__pyx_n_s_d1, __pyx_k_d1, sizeof(__pyx_k_d1), 0, 0, 1, 1}, - {&__pyx_n_s_delta_2, __pyx_k_delta_2, sizeof(__pyx_k_delta_2), 0, 0, 1, 1}, - {&__pyx_n_s_delta_3, __pyx_k_delta_3, sizeof(__pyx_k_delta_3), 0, 0, 1, 1}, - {&__pyx_kp_u_disable, __pyx_k_disable, sizeof(__pyx_k_disable), 0, 1, 0, 0}, - {&__pyx_n_s_dt, __pyx_k_dt, sizeof(__pyx_k_dt), 0, 0, 1, 1}, - {&__pyx_kp_u_enable, __pyx_k_enable, sizeof(__pyx_k_enable), 0, 1, 0, 0}, - {&__pyx_n_s_errors, __pyx_k_errors, sizeof(__pyx_k_errors), 0, 0, 1, 1}, - {&__pyx_n_s_fontTools_cu2qu_cu2qu, __pyx_k_fontTools_cu2qu_cu2qu, sizeof(__pyx_k_fontTools_cu2qu_cu2qu), 0, 0, 1, 1}, - {&__pyx_n_s_fontTools_misc, __pyx_k_fontTools_misc, sizeof(__pyx_k_fontTools_misc), 0, 0, 1, 1}, - {&__pyx_kp_u_gc, __pyx_k_gc, sizeof(__pyx_k_gc), 0, 1, 0, 0}, - {&__pyx_n_s_i, __pyx_k_i, sizeof(__pyx_k_i), 0, 0, 1, 1}, - {&__pyx_n_s_imag, __pyx_k_imag, sizeof(__pyx_k_imag), 0, 0, 1, 1}, - {&__pyx_n_s_import, __pyx_k_import, sizeof(__pyx_k_import), 0, 0, 1, 1}, - {&__pyx_n_s_initializing, __pyx_k_initializing, sizeof(__pyx_k_initializing), 0, 0, 1, 1}, - {&__pyx_n_s_is_coroutine, __pyx_k_is_coroutine, sizeof(__pyx_k_is_coroutine), 0, 0, 1, 1}, - {&__pyx_kp_u_isenabled, __pyx_k_isenabled, sizeof(__pyx_k_isenabled), 0, 1, 0, 0}, - {&__pyx_n_s_isnan, __pyx_k_isnan, sizeof(__pyx_k_isnan), 0, 0, 1, 1}, - {&__pyx_n_s_l, __pyx_k_l, sizeof(__pyx_k_l), 0, 0, 1, 1}, - {&__pyx_n_s_last_i, __pyx_k_last_i, sizeof(__pyx_k_last_i), 0, 0, 1, 1}, - {&__pyx_n_s_main, __pyx_k_main, sizeof(__pyx_k_main), 0, 0, 1, 1}, - {&__pyx_n_s_math, __pyx_k_math, sizeof(__pyx_k_math), 0, 0, 1, 1}, - {&__pyx_n_s_max_err, __pyx_k_max_err, sizeof(__pyx_k_max_err), 0, 0, 1, 1}, - {&__pyx_n_s_max_errors, __pyx_k_max_errors, sizeof(__pyx_k_max_errors), 0, 0, 1, 1}, - {&__pyx_n_s_n, __pyx_k_n, sizeof(__pyx_k_n), 0, 0, 1, 1}, - {&__pyx_n_s_name, __pyx_k_name, sizeof(__pyx_k_name), 0, 0, 1, 1}, - {&__pyx_n_s_p, __pyx_k_p, sizeof(__pyx_k_p), 0, 0, 1, 1}, - {&__pyx_n_s_p0, __pyx_k_p0, sizeof(__pyx_k_p0), 0, 0, 1, 1}, - {&__pyx_n_s_p1, __pyx_k_p1, sizeof(__pyx_k_p1), 0, 0, 1, 1}, - {&__pyx_n_s_p2, __pyx_k_p2, sizeof(__pyx_k_p2), 0, 0, 1, 1}, - {&__pyx_n_s_p3, __pyx_k_p3, sizeof(__pyx_k_p3), 0, 0, 1, 1}, - {&__pyx_n_s_range, __pyx_k_range, sizeof(__pyx_k_range), 0, 0, 1, 1}, - {&__pyx_n_s_real, __pyx_k_real, sizeof(__pyx_k_real), 0, 0, 1, 1}, - {&__pyx_n_s_s, __pyx_k_s, sizeof(__pyx_k_s), 0, 0, 1, 1}, - {&__pyx_n_s_send, __pyx_k_send, sizeof(__pyx_k_send), 0, 0, 1, 1}, - {&__pyx_n_s_spec, __pyx_k_spec, sizeof(__pyx_k_spec), 0, 0, 1, 1}, - {&__pyx_n_s_spline, __pyx_k_spline, sizeof(__pyx_k_spline), 0, 0, 1, 1}, - {&__pyx_n_s_splines, __pyx_k_splines, sizeof(__pyx_k_splines), 0, 0, 1, 1}, - {&__pyx_n_s_split_cubic_into_n_gen, __pyx_k_split_cubic_into_n_gen, sizeof(__pyx_k_split_cubic_into_n_gen), 0, 0, 1, 1}, - {&__pyx_n_s_t1, __pyx_k_t1, sizeof(__pyx_k_t1), 0, 0, 1, 1}, - {&__pyx_n_s_t1_2, __pyx_k_t1_2, sizeof(__pyx_k_t1_2), 0, 0, 1, 1}, - {&__pyx_n_s_test, __pyx_k_test, sizeof(__pyx_k_test), 0, 0, 1, 1}, - {&__pyx_n_s_throw, __pyx_k_throw, sizeof(__pyx_k_throw), 0, 0, 1, 1}, - {0, 0, 0, 0, 0, 0, 0} - }; - return __Pyx_InitStrings(__pyx_string_tab); -} -/* #### Code section: cached_builtins ### */ -static CYTHON_SMALL_CODE int __Pyx_InitCachedBuiltins(void) { - __pyx_builtin_AttributeError = __Pyx_GetBuiltinName(__pyx_n_s_AttributeError); if (!__pyx_builtin_AttributeError) __PYX_ERR(0, 22, __pyx_L1_error) - __pyx_builtin_ImportError = __Pyx_GetBuiltinName(__pyx_n_s_ImportError); if (!__pyx_builtin_ImportError) __PYX_ERR(0, 22, __pyx_L1_error) - __pyx_builtin_range = __Pyx_GetBuiltinName(__pyx_n_s_range); if (!__pyx_builtin_range) __PYX_ERR(0, 146, __pyx_L1_error) - __pyx_builtin_ZeroDivisionError = __Pyx_GetBuiltinName(__pyx_n_s_ZeroDivisionError); if (!__pyx_builtin_ZeroDivisionError) __PYX_ERR(0, 278, __pyx_L1_error) - __pyx_builtin_AssertionError = __Pyx_GetBuiltinName(__pyx_n_s_AssertionError); if (!__pyx_builtin_AssertionError) __PYX_ERR(0, 514, __pyx_L1_error) - return 0; - __pyx_L1_error:; - return -1; -} -/* #### Code section: cached_constants ### */ - -static CYTHON_SMALL_CODE int __Pyx_InitCachedConstants(void) { - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("__Pyx_InitCachedConstants", 0); - - /* "fontTools/cu2qu/cu2qu.py":127 - * - * - * @cython.locals( # <<<<<<<<<<<<<< - * p0=cython.complex, - * p1=cython.complex, - */ - __pyx_tuple__4 = PyTuple_Pack(19, __pyx_n_s_p0, __pyx_n_s_p1, __pyx_n_s_p2, __pyx_n_s_p3, __pyx_n_s_n, __pyx_n_s_a1, __pyx_n_s_b1, __pyx_n_s_c1, __pyx_n_s_d1, __pyx_n_s_dt, __pyx_n_s_delta_2, __pyx_n_s_delta_3, __pyx_n_s_i, __pyx_n_s_a, __pyx_n_s_b, __pyx_n_s_c, __pyx_n_s_d, __pyx_n_s_t1, __pyx_n_s_t1_2); if (unlikely(!__pyx_tuple__4)) __PYX_ERR(0, 127, __pyx_L1_error) - __Pyx_GOTREF(__pyx_tuple__4); - __Pyx_GIVEREF(__pyx_tuple__4); - __pyx_codeobj_ = (PyObject*)__Pyx_PyCode_New(5, 0, 0, 19, 0, CO_OPTIMIZED|CO_NEWLOCALS|CO_GENERATOR, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__4, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_Lib_fontTools_cu2qu_cu2qu_py, __pyx_n_s_split_cubic_into_n_gen, 127, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj_)) __PYX_ERR(0, 127, __pyx_L1_error) - - /* "fontTools/cu2qu/cu2qu.py":439 - * - * - * @cython.locals(max_err=cython.double) # <<<<<<<<<<<<<< - * @cython.locals(n=cython.int) - * @cython.locals(all_quadratic=cython.int) - */ - __pyx_tuple__5 = PyTuple_Pack(7, __pyx_n_s_curve, __pyx_n_s_max_err, __pyx_n_s_all_quadratic, __pyx_n_s_n, __pyx_n_s_spline, __pyx_n_s_p, __pyx_n_s_s); if (unlikely(!__pyx_tuple__5)) __PYX_ERR(0, 439, __pyx_L1_error) - __Pyx_GOTREF(__pyx_tuple__5); - __Pyx_GIVEREF(__pyx_tuple__5); - __pyx_codeobj__6 = (PyObject*)__Pyx_PyCode_New(3, 0, 0, 7, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__5, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_Lib_fontTools_cu2qu_cu2qu_py, __pyx_n_s_curve_to_quadratic, 439, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__6)) __PYX_ERR(0, 439, __pyx_L1_error) - - /* "fontTools/cu2qu/cu2qu.py":474 - * - * - * @cython.locals(l=cython.int, last_i=cython.int, i=cython.int) # <<<<<<<<<<<<<< - * @cython.locals(all_quadratic=cython.int) - * def curves_to_quadratic(curves, max_errors, all_quadratic=True): - */ - __pyx_tuple__7 = PyTuple_Pack(13, __pyx_n_s_curves, __pyx_n_s_max_errors, __pyx_n_s_all_quadratic, __pyx_n_s_l, __pyx_n_s_last_i, __pyx_n_s_i, __pyx_n_s_splines, __pyx_n_s_n, __pyx_n_s_spline, __pyx_n_s_curve, __pyx_n_s_p, __pyx_n_s_spline, __pyx_n_s_s); if (unlikely(!__pyx_tuple__7)) __PYX_ERR(0, 474, __pyx_L1_error) - __Pyx_GOTREF(__pyx_tuple__7); - __Pyx_GIVEREF(__pyx_tuple__7); - __pyx_codeobj__8 = (PyObject*)__Pyx_PyCode_New(3, 0, 0, 13, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__7, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_Lib_fontTools_cu2qu_cu2qu_py, __pyx_n_s_curves_to_quadratic, 474, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__8)) __PYX_ERR(0, 474, __pyx_L1_error) - __Pyx_RefNannyFinishContext(); - return 0; - __pyx_L1_error:; - __Pyx_RefNannyFinishContext(); - return -1; -} -/* #### Code section: init_constants ### */ - -static CYTHON_SMALL_CODE int __Pyx_InitConstants(void) { - if (__Pyx_CreateStringTabAndInitStrings() < 0) __PYX_ERR(0, 1, __pyx_L1_error); - __pyx_int_1 = PyInt_FromLong(1); if (unlikely(!__pyx_int_1)) __PYX_ERR(0, 1, __pyx_L1_error) - __pyx_int_2 = PyInt_FromLong(2); if (unlikely(!__pyx_int_2)) __PYX_ERR(0, 1, __pyx_L1_error) - __pyx_int_3 = PyInt_FromLong(3); if (unlikely(!__pyx_int_3)) __PYX_ERR(0, 1, __pyx_L1_error) - __pyx_int_4 = PyInt_FromLong(4); if (unlikely(!__pyx_int_4)) __PYX_ERR(0, 1, __pyx_L1_error) - __pyx_int_6 = PyInt_FromLong(6); if (unlikely(!__pyx_int_6)) __PYX_ERR(0, 1, __pyx_L1_error) - __pyx_int_100 = PyInt_FromLong(100); if (unlikely(!__pyx_int_100)) __PYX_ERR(0, 1, __pyx_L1_error) - return 0; - __pyx_L1_error:; - return -1; -} -/* #### Code section: init_globals ### */ - -static CYTHON_SMALL_CODE int __Pyx_InitGlobals(void) { - /* AssertionsEnabled.init */ - if (likely(__Pyx_init_assertions_enabled() == 0)); else - -if (unlikely(PyErr_Occurred())) __PYX_ERR(0, 1, __pyx_L1_error) - - return 0; - __pyx_L1_error:; - return -1; -} -/* #### Code section: init_module ### */ - -static CYTHON_SMALL_CODE int __Pyx_modinit_global_init_code(void); /*proto*/ -static CYTHON_SMALL_CODE int __Pyx_modinit_variable_export_code(void); /*proto*/ -static CYTHON_SMALL_CODE int __Pyx_modinit_function_export_code(void); /*proto*/ -static CYTHON_SMALL_CODE int __Pyx_modinit_type_init_code(void); /*proto*/ -static CYTHON_SMALL_CODE int __Pyx_modinit_type_import_code(void); /*proto*/ -static CYTHON_SMALL_CODE int __Pyx_modinit_variable_import_code(void); /*proto*/ -static CYTHON_SMALL_CODE int __Pyx_modinit_function_import_code(void); /*proto*/ - -static int __Pyx_modinit_global_init_code(void) { - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("__Pyx_modinit_global_init_code", 0); - /*--- Global init code ---*/ - __Pyx_RefNannyFinishContext(); - return 0; -} - -static int __Pyx_modinit_variable_export_code(void) { - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("__Pyx_modinit_variable_export_code", 0); - /*--- Variable export code ---*/ - __Pyx_RefNannyFinishContext(); - return 0; -} - -static int __Pyx_modinit_function_export_code(void) { - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("__Pyx_modinit_function_export_code", 0); - /*--- Function export code ---*/ - __Pyx_RefNannyFinishContext(); - return 0; -} - -static int __Pyx_modinit_type_init_code(void) { - __Pyx_RefNannyDeclarations - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannySetupContext("__Pyx_modinit_type_init_code", 0); - /*--- Type init code ---*/ - #if CYTHON_USE_TYPE_SPECS - __pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen = (PyTypeObject *) __Pyx_PyType_FromModuleAndSpec(__pyx_m, &__pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen_spec, NULL); if (unlikely(!__pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen)) __PYX_ERR(0, 127, __pyx_L1_error) - if (__Pyx_fix_up_extension_type_from_spec(&__pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen_spec, __pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen) < 0) __PYX_ERR(0, 127, __pyx_L1_error) - #else - __pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen = &__pyx_type_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen; - #endif - #if !CYTHON_COMPILING_IN_LIMITED_API - #endif - #if !CYTHON_USE_TYPE_SPECS - if (__Pyx_PyType_Ready(__pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen) < 0) __PYX_ERR(0, 127, __pyx_L1_error) - #endif - #if PY_MAJOR_VERSION < 3 - __pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen->tp_print = 0; - #endif - #if !CYTHON_COMPILING_IN_LIMITED_API - if ((CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP) && likely(!__pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen->tp_dictoffset && __pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen->tp_getattro == PyObject_GenericGetAttr)) { - __pyx_ptype_9fontTools_5cu2qu_5cu2qu___pyx_scope_struct___split_cubic_into_n_gen->tp_getattro = __Pyx_PyObject_GenericGetAttrNoDict; - } - #endif - __Pyx_RefNannyFinishContext(); - return 0; - __pyx_L1_error:; - __Pyx_RefNannyFinishContext(); - return -1; -} - -static int __Pyx_modinit_type_import_code(void) { - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("__Pyx_modinit_type_import_code", 0); - /*--- Type import code ---*/ - __Pyx_RefNannyFinishContext(); - return 0; -} - -static int __Pyx_modinit_variable_import_code(void) { - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("__Pyx_modinit_variable_import_code", 0); - /*--- Variable import code ---*/ - __Pyx_RefNannyFinishContext(); - return 0; -} - -static int __Pyx_modinit_function_import_code(void) { - __Pyx_RefNannyDeclarations - __Pyx_RefNannySetupContext("__Pyx_modinit_function_import_code", 0); - /*--- Function import code ---*/ - __Pyx_RefNannyFinishContext(); - return 0; -} - - -#if PY_MAJOR_VERSION >= 3 -#if CYTHON_PEP489_MULTI_PHASE_INIT -static PyObject* __pyx_pymod_create(PyObject *spec, PyModuleDef *def); /*proto*/ -static int __pyx_pymod_exec_cu2qu(PyObject* module); /*proto*/ -static PyModuleDef_Slot __pyx_moduledef_slots[] = { - {Py_mod_create, (void*)__pyx_pymod_create}, - {Py_mod_exec, (void*)__pyx_pymod_exec_cu2qu}, - {0, NULL} -}; -#endif - -#ifdef __cplusplus -namespace { - struct PyModuleDef __pyx_moduledef = - #else - static struct PyModuleDef __pyx_moduledef = - #endif - { - PyModuleDef_HEAD_INIT, - "cu2qu", - 0, /* m_doc */ - #if CYTHON_PEP489_MULTI_PHASE_INIT - 0, /* m_size */ - #elif CYTHON_USE_MODULE_STATE - sizeof(__pyx_mstate), /* m_size */ - #else - -1, /* m_size */ - #endif - __pyx_methods /* m_methods */, - #if CYTHON_PEP489_MULTI_PHASE_INIT - __pyx_moduledef_slots, /* m_slots */ - #else - NULL, /* m_reload */ - #endif - #if CYTHON_USE_MODULE_STATE - __pyx_m_traverse, /* m_traverse */ - __pyx_m_clear, /* m_clear */ - NULL /* m_free */ - #else - NULL, /* m_traverse */ - NULL, /* m_clear */ - NULL /* m_free */ - #endif - }; - #ifdef __cplusplus -} /* anonymous namespace */ -#endif -#endif - -#ifndef CYTHON_NO_PYINIT_EXPORT -#define __Pyx_PyMODINIT_FUNC PyMODINIT_FUNC -#elif PY_MAJOR_VERSION < 3 -#ifdef __cplusplus -#define __Pyx_PyMODINIT_FUNC extern "C" void -#else -#define __Pyx_PyMODINIT_FUNC void -#endif -#else -#ifdef __cplusplus -#define __Pyx_PyMODINIT_FUNC extern "C" PyObject * -#else -#define __Pyx_PyMODINIT_FUNC PyObject * -#endif -#endif - - -#if PY_MAJOR_VERSION < 3 -__Pyx_PyMODINIT_FUNC initcu2qu(void) CYTHON_SMALL_CODE; /*proto*/ -__Pyx_PyMODINIT_FUNC initcu2qu(void) -#else -__Pyx_PyMODINIT_FUNC PyInit_cu2qu(void) CYTHON_SMALL_CODE; /*proto*/ -__Pyx_PyMODINIT_FUNC PyInit_cu2qu(void) -#if CYTHON_PEP489_MULTI_PHASE_INIT -{ - return PyModuleDef_Init(&__pyx_moduledef); -} -static CYTHON_SMALL_CODE int __Pyx_check_single_interpreter(void) { - #if PY_VERSION_HEX >= 0x030700A1 - static PY_INT64_T main_interpreter_id = -1; - PY_INT64_T current_id = PyInterpreterState_GetID(PyThreadState_Get()->interp); - if (main_interpreter_id == -1) { - main_interpreter_id = current_id; - return (unlikely(current_id == -1)) ? -1 : 0; - } else if (unlikely(main_interpreter_id != current_id)) - #else - static PyInterpreterState *main_interpreter = NULL; - PyInterpreterState *current_interpreter = PyThreadState_Get()->interp; - if (!main_interpreter) { - main_interpreter = current_interpreter; - } else if (unlikely(main_interpreter != current_interpreter)) - #endif - { - PyErr_SetString( - PyExc_ImportError, - "Interpreter change detected - this module can only be loaded into one interpreter per process."); - return -1; - } - return 0; -} -#if CYTHON_COMPILING_IN_LIMITED_API -static CYTHON_SMALL_CODE int __Pyx_copy_spec_to_module(PyObject *spec, PyObject *module, const char* from_name, const char* to_name, int allow_none) -#else -static CYTHON_SMALL_CODE int __Pyx_copy_spec_to_module(PyObject *spec, PyObject *moddict, const char* from_name, const char* to_name, int allow_none) -#endif -{ - PyObject *value = PyObject_GetAttrString(spec, from_name); - int result = 0; - if (likely(value)) { - if (allow_none || value != Py_None) { -#if CYTHON_COMPILING_IN_LIMITED_API - result = PyModule_AddObject(module, to_name, value); -#else - result = PyDict_SetItemString(moddict, to_name, value); -#endif - } - Py_DECREF(value); - } else if (PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Clear(); - } else { - result = -1; - } - return result; -} -static CYTHON_SMALL_CODE PyObject* __pyx_pymod_create(PyObject *spec, PyModuleDef *def) { - PyObject *module = NULL, *moddict, *modname; - CYTHON_UNUSED_VAR(def); - if (__Pyx_check_single_interpreter()) - return NULL; - if (__pyx_m) - return __Pyx_NewRef(__pyx_m); - modname = PyObject_GetAttrString(spec, "name"); - if (unlikely(!modname)) goto bad; - module = PyModule_NewObject(modname); - Py_DECREF(modname); - if (unlikely(!module)) goto bad; -#if CYTHON_COMPILING_IN_LIMITED_API - moddict = module; -#else - moddict = PyModule_GetDict(module); - if (unlikely(!moddict)) goto bad; -#endif - if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "loader", "__loader__", 1) < 0)) goto bad; - if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "origin", "__file__", 1) < 0)) goto bad; - if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "parent", "__package__", 1) < 0)) goto bad; - if (unlikely(__Pyx_copy_spec_to_module(spec, moddict, "submodule_search_locations", "__path__", 0) < 0)) goto bad; - return module; -bad: - Py_XDECREF(module); - return NULL; -} - - -static CYTHON_SMALL_CODE int __pyx_pymod_exec_cu2qu(PyObject *__pyx_pyinit_module) -#endif -#endif -{ - int stringtab_initialized = 0; - #if CYTHON_USE_MODULE_STATE - int pystate_addmodule_run = 0; - #endif - PyObject *__pyx_t_1 = NULL; - PyObject *__pyx_t_2 = NULL; - PyObject *__pyx_t_3 = NULL; - int __pyx_t_4; - PyObject *__pyx_t_5 = NULL; - PyObject *__pyx_t_6 = NULL; - PyObject *__pyx_t_7 = NULL; - PyObject *__pyx_t_8 = NULL; - PyObject *__pyx_t_9 = NULL; - double __pyx_t_10; - int __pyx_lineno = 0; - const char *__pyx_filename = NULL; - int __pyx_clineno = 0; - __Pyx_RefNannyDeclarations - #if CYTHON_PEP489_MULTI_PHASE_INIT - if (__pyx_m) { - if (__pyx_m == __pyx_pyinit_module) return 0; - PyErr_SetString(PyExc_RuntimeError, "Module 'cu2qu' has already been imported. Re-initialisation is not supported."); - return -1; - } - #elif PY_MAJOR_VERSION >= 3 - if (__pyx_m) return __Pyx_NewRef(__pyx_m); - #endif - /*--- Module creation code ---*/ - #if CYTHON_PEP489_MULTI_PHASE_INIT - __pyx_m = __pyx_pyinit_module; - Py_INCREF(__pyx_m); - #else - #if PY_MAJOR_VERSION < 3 - __pyx_m = Py_InitModule4("cu2qu", __pyx_methods, 0, 0, PYTHON_API_VERSION); Py_XINCREF(__pyx_m); - if (unlikely(!__pyx_m)) __PYX_ERR(0, 1, __pyx_L1_error) - #elif CYTHON_USE_MODULE_STATE - __pyx_t_1 = PyModule_Create(&__pyx_moduledef); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1, __pyx_L1_error) - { - int add_module_result = PyState_AddModule(__pyx_t_1, &__pyx_moduledef); - __pyx_t_1 = 0; /* transfer ownership from __pyx_t_1 to "cu2qu" pseudovariable */ - if (unlikely((add_module_result < 0))) __PYX_ERR(0, 1, __pyx_L1_error) - pystate_addmodule_run = 1; - } - #else - __pyx_m = PyModule_Create(&__pyx_moduledef); - if (unlikely(!__pyx_m)) __PYX_ERR(0, 1, __pyx_L1_error) - #endif - #endif - CYTHON_UNUSED_VAR(__pyx_t_1); - __pyx_d = PyModule_GetDict(__pyx_m); if (unlikely(!__pyx_d)) __PYX_ERR(0, 1, __pyx_L1_error) - Py_INCREF(__pyx_d); - __pyx_b = __Pyx_PyImport_AddModuleRef(__Pyx_BUILTIN_MODULE_NAME); if (unlikely(!__pyx_b)) __PYX_ERR(0, 1, __pyx_L1_error) - __pyx_cython_runtime = __Pyx_PyImport_AddModuleRef((const char *) "cython_runtime"); if (unlikely(!__pyx_cython_runtime)) __PYX_ERR(0, 1, __pyx_L1_error) - if (PyObject_SetAttrString(__pyx_m, "__builtins__", __pyx_b) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - #if CYTHON_REFNANNY -__Pyx_RefNanny = __Pyx_RefNannyImportAPI("refnanny"); -if (!__Pyx_RefNanny) { - PyErr_Clear(); - __Pyx_RefNanny = __Pyx_RefNannyImportAPI("Cython.Runtime.refnanny"); - if (!__Pyx_RefNanny) - Py_FatalError("failed to import 'refnanny' module"); -} -#endif - __Pyx_RefNannySetupContext("__Pyx_PyMODINIT_FUNC PyInit_cu2qu(void)", 0); - if (__Pyx_check_binary_version(__PYX_LIMITED_VERSION_HEX, __Pyx_get_runtime_version(), CYTHON_COMPILING_IN_LIMITED_API) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - #ifdef __Pxy_PyFrame_Initialize_Offsets - __Pxy_PyFrame_Initialize_Offsets(); - #endif - __pyx_empty_tuple = PyTuple_New(0); if (unlikely(!__pyx_empty_tuple)) __PYX_ERR(0, 1, __pyx_L1_error) - __pyx_empty_bytes = PyBytes_FromStringAndSize("", 0); if (unlikely(!__pyx_empty_bytes)) __PYX_ERR(0, 1, __pyx_L1_error) - __pyx_empty_unicode = PyUnicode_FromStringAndSize("", 0); if (unlikely(!__pyx_empty_unicode)) __PYX_ERR(0, 1, __pyx_L1_error) - #ifdef __Pyx_CyFunction_USED - if (__pyx_CyFunction_init(__pyx_m) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - #endif - #ifdef __Pyx_FusedFunction_USED - if (__pyx_FusedFunction_init(__pyx_m) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - #endif - #ifdef __Pyx_Coroutine_USED - if (__pyx_Coroutine_init(__pyx_m) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - #endif - #ifdef __Pyx_Generator_USED - if (__pyx_Generator_init(__pyx_m) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - #endif - #ifdef __Pyx_AsyncGen_USED - if (__pyx_AsyncGen_init(__pyx_m) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - #endif - #ifdef __Pyx_StopAsyncIteration_USED - if (__pyx_StopAsyncIteration_init(__pyx_m) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - #endif - /*--- Library function declarations ---*/ - /*--- Threads initialization code ---*/ - #if defined(WITH_THREAD) && PY_VERSION_HEX < 0x030700F0 && defined(__PYX_FORCE_INIT_THREADS) && __PYX_FORCE_INIT_THREADS - PyEval_InitThreads(); - #endif - /*--- Initialize various global constants etc. ---*/ - if (__Pyx_InitConstants() < 0) __PYX_ERR(0, 1, __pyx_L1_error) - stringtab_initialized = 1; - if (__Pyx_InitGlobals() < 0) __PYX_ERR(0, 1, __pyx_L1_error) - #if PY_MAJOR_VERSION < 3 && (__PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT) - if (__Pyx_init_sys_getdefaultencoding_params() < 0) __PYX_ERR(0, 1, __pyx_L1_error) - #endif - if (__pyx_module_is_main_fontTools__cu2qu__cu2qu) { - if (PyObject_SetAttr(__pyx_m, __pyx_n_s_name, __pyx_n_s_main) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - } - #if PY_MAJOR_VERSION >= 3 - { - PyObject *modules = PyImport_GetModuleDict(); if (unlikely(!modules)) __PYX_ERR(0, 1, __pyx_L1_error) - if (!PyDict_GetItemString(modules, "fontTools.cu2qu.cu2qu")) { - if (unlikely((PyDict_SetItemString(modules, "fontTools.cu2qu.cu2qu", __pyx_m) < 0))) __PYX_ERR(0, 1, __pyx_L1_error) - } - } - #endif - /*--- Builtin init code ---*/ - if (__Pyx_InitCachedBuiltins() < 0) __PYX_ERR(0, 1, __pyx_L1_error) - /*--- Constants init code ---*/ - if (__Pyx_InitCachedConstants() < 0) __PYX_ERR(0, 1, __pyx_L1_error) - /*--- Global type/function init code ---*/ - (void)__Pyx_modinit_global_init_code(); - (void)__Pyx_modinit_variable_export_code(); - (void)__Pyx_modinit_function_export_code(); - if (unlikely((__Pyx_modinit_type_init_code() < 0))) __PYX_ERR(0, 1, __pyx_L1_error) - (void)__Pyx_modinit_type_import_code(); - (void)__Pyx_modinit_variable_import_code(); - (void)__Pyx_modinit_function_import_code(); - /*--- Execution code ---*/ - #if defined(__Pyx_Generator_USED) || defined(__Pyx_Coroutine_USED) - if (__Pyx_patch_abc() < 0) __PYX_ERR(0, 1, __pyx_L1_error) - #endif - - /* "fontTools/cu2qu/cu2qu.py":18 - * # limitations under the License. - * - * try: # <<<<<<<<<<<<<< - * import cython - * - */ - { - __Pyx_PyThreadState_declare - __Pyx_PyThreadState_assign - __Pyx_ExceptionSave(&__pyx_t_1, &__pyx_t_2, &__pyx_t_3); - __Pyx_XGOTREF(__pyx_t_1); - __Pyx_XGOTREF(__pyx_t_2); - __Pyx_XGOTREF(__pyx_t_3); - /*try:*/ { - - /* "fontTools/cu2qu/cu2qu.py":21 - * import cython - * - * COMPILED = cython.compiled # <<<<<<<<<<<<<< - * except (AttributeError, ImportError): - * # if cython not installed, use mock module with no-op decorators and types - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s_COMPILED, Py_True) < 0) __PYX_ERR(0, 21, __pyx_L2_error) - - /* "fontTools/cu2qu/cu2qu.py":18 - * # limitations under the License. - * - * try: # <<<<<<<<<<<<<< - * import cython - * - */ - } - __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0; - __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0; - __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0; - goto __pyx_L7_try_end; - __pyx_L2_error:; - - /* "fontTools/cu2qu/cu2qu.py":22 - * - * COMPILED = cython.compiled - * except (AttributeError, ImportError): # <<<<<<<<<<<<<< - * # if cython not installed, use mock module with no-op decorators and types - * from fontTools.misc import cython - */ - __pyx_t_4 = __Pyx_PyErr_ExceptionMatches2(__pyx_builtin_AttributeError, __pyx_builtin_ImportError); - if (__pyx_t_4) { - __Pyx_AddTraceback("fontTools.cu2qu.cu2qu", __pyx_clineno, __pyx_lineno, __pyx_filename); - if (__Pyx_GetException(&__pyx_t_5, &__pyx_t_6, &__pyx_t_7) < 0) __PYX_ERR(0, 22, __pyx_L4_except_error) - __Pyx_XGOTREF(__pyx_t_5); - __Pyx_XGOTREF(__pyx_t_6); - __Pyx_XGOTREF(__pyx_t_7); - - /* "fontTools/cu2qu/cu2qu.py":24 - * except (AttributeError, ImportError): - * # if cython not installed, use mock module with no-op decorators and types - * from fontTools.misc import cython # <<<<<<<<<<<<<< - * - * COMPILED = False - */ - __pyx_t_8 = PyList_New(1); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 24, __pyx_L4_except_error) - __Pyx_GOTREF(__pyx_t_8); - __Pyx_INCREF(__pyx_n_s_cython); - __Pyx_GIVEREF(__pyx_n_s_cython); - if (__Pyx_PyList_SET_ITEM(__pyx_t_8, 0, __pyx_n_s_cython)) __PYX_ERR(0, 24, __pyx_L4_except_error); - __pyx_t_9 = __Pyx_Import(__pyx_n_s_fontTools_misc, __pyx_t_8, 0); if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 24, __pyx_L4_except_error) - __Pyx_GOTREF(__pyx_t_9); - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __pyx_t_8 = __Pyx_ImportFrom(__pyx_t_9, __pyx_n_s_cython); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 24, __pyx_L4_except_error) - __Pyx_GOTREF(__pyx_t_8); - if (PyDict_SetItem(__pyx_d, __pyx_n_s_cython, __pyx_t_8) < 0) __PYX_ERR(0, 24, __pyx_L4_except_error) - __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0; - __Pyx_DECREF(__pyx_t_9); __pyx_t_9 = 0; - - /* "fontTools/cu2qu/cu2qu.py":26 - * from fontTools.misc import cython - * - * COMPILED = False # <<<<<<<<<<<<<< - * - * import math - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s_COMPILED, Py_False) < 0) __PYX_ERR(0, 26, __pyx_L4_except_error) - __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0; - __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0; - __Pyx_XDECREF(__pyx_t_7); __pyx_t_7 = 0; - goto __pyx_L3_exception_handled; - } - goto __pyx_L4_except_error; - - /* "fontTools/cu2qu/cu2qu.py":18 - * # limitations under the License. - * - * try: # <<<<<<<<<<<<<< - * import cython - * - */ - __pyx_L4_except_error:; - __Pyx_XGIVEREF(__pyx_t_1); - __Pyx_XGIVEREF(__pyx_t_2); - __Pyx_XGIVEREF(__pyx_t_3); - __Pyx_ExceptionReset(__pyx_t_1, __pyx_t_2, __pyx_t_3); - goto __pyx_L1_error; - __pyx_L3_exception_handled:; - __Pyx_XGIVEREF(__pyx_t_1); - __Pyx_XGIVEREF(__pyx_t_2); - __Pyx_XGIVEREF(__pyx_t_3); - __Pyx_ExceptionReset(__pyx_t_1, __pyx_t_2, __pyx_t_3); - __pyx_L7_try_end:; - } - - /* "fontTools/cu2qu/cu2qu.py":28 - * COMPILED = False - * - * import math # <<<<<<<<<<<<<< - * - * from .errors import Error as Cu2QuError, ApproxNotFoundError - */ - __pyx_t_7 = __Pyx_ImportDottedModule(__pyx_n_s_math, NULL); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 28, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - if (PyDict_SetItem(__pyx_d, __pyx_n_s_math, __pyx_t_7) < 0) __PYX_ERR(0, 28, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; - - /* "fontTools/cu2qu/cu2qu.py":30 - * import math - * - * from .errors import Error as Cu2QuError, ApproxNotFoundError # <<<<<<<<<<<<<< - * - * - */ - __pyx_t_7 = PyList_New(2); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 30, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - __Pyx_INCREF(__pyx_n_s_Error); - __Pyx_GIVEREF(__pyx_n_s_Error); - if (__Pyx_PyList_SET_ITEM(__pyx_t_7, 0, __pyx_n_s_Error)) __PYX_ERR(0, 30, __pyx_L1_error); - __Pyx_INCREF(__pyx_n_s_ApproxNotFoundError); - __Pyx_GIVEREF(__pyx_n_s_ApproxNotFoundError); - if (__Pyx_PyList_SET_ITEM(__pyx_t_7, 1, __pyx_n_s_ApproxNotFoundError)) __PYX_ERR(0, 30, __pyx_L1_error); - __pyx_t_6 = __Pyx_Import(__pyx_n_s_errors, __pyx_t_7, 1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 30, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; - __pyx_t_7 = __Pyx_ImportFrom(__pyx_t_6, __pyx_n_s_Error); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 30, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - if (PyDict_SetItem(__pyx_d, __pyx_n_s_Cu2QuError, __pyx_t_7) < 0) __PYX_ERR(0, 30, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; - __pyx_t_7 = __Pyx_ImportFrom(__pyx_t_6, __pyx_n_s_ApproxNotFoundError); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 30, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - if (PyDict_SetItem(__pyx_d, __pyx_n_s_ApproxNotFoundError, __pyx_t_7) < 0) __PYX_ERR(0, 30, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; - __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; - - /* "fontTools/cu2qu/cu2qu.py":33 - * - * - * __all__ = ["curve_to_quadratic", "curves_to_quadratic"] # <<<<<<<<<<<<<< - * - * MAX_N = 100 - */ - __pyx_t_6 = PyList_New(2); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 33, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - __Pyx_INCREF(__pyx_n_u_curve_to_quadratic); - __Pyx_GIVEREF(__pyx_n_u_curve_to_quadratic); - if (__Pyx_PyList_SET_ITEM(__pyx_t_6, 0, __pyx_n_u_curve_to_quadratic)) __PYX_ERR(0, 33, __pyx_L1_error); - __Pyx_INCREF(__pyx_n_u_curves_to_quadratic); - __Pyx_GIVEREF(__pyx_n_u_curves_to_quadratic); - if (__Pyx_PyList_SET_ITEM(__pyx_t_6, 1, __pyx_n_u_curves_to_quadratic)) __PYX_ERR(0, 33, __pyx_L1_error); - if (PyDict_SetItem(__pyx_d, __pyx_n_s_all, __pyx_t_6) < 0) __PYX_ERR(0, 33, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; - - /* "fontTools/cu2qu/cu2qu.py":35 - * __all__ = ["curve_to_quadratic", "curves_to_quadratic"] - * - * MAX_N = 100 # <<<<<<<<<<<<<< - * - * NAN = float("NaN") - */ - if (PyDict_SetItem(__pyx_d, __pyx_n_s_MAX_N, __pyx_int_100) < 0) __PYX_ERR(0, 35, __pyx_L1_error) - - /* "fontTools/cu2qu/cu2qu.py":37 - * MAX_N = 100 - * - * NAN = float("NaN") # <<<<<<<<<<<<<< - * - * - */ - __pyx_t_10 = __Pyx_PyUnicode_AsDouble(__pyx_n_u_NaN); if (unlikely(__pyx_t_10 == ((double)((double)-1)) && PyErr_Occurred())) __PYX_ERR(0, 37, __pyx_L1_error) - __pyx_t_6 = PyFloat_FromDouble(__pyx_t_10); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 37, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - if (PyDict_SetItem(__pyx_d, __pyx_n_s_NAN, __pyx_t_6) < 0) __PYX_ERR(0, 37, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; - - /* "fontTools/cu2qu/cu2qu.py":127 - * - * - * @cython.locals( # <<<<<<<<<<<<<< - * p0=cython.complex, - * p1=cython.complex, - */ - __pyx_t_6 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_5cu2qu_5cu2qu_1_split_cubic_into_n_gen, 0, __pyx_n_s_split_cubic_into_n_gen, NULL, __pyx_n_s_fontTools_cu2qu_cu2qu, __pyx_d, ((PyObject *)__pyx_codeobj_)); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 127, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - if (PyDict_SetItem(__pyx_d, __pyx_n_s_split_cubic_into_n_gen, __pyx_t_6) < 0) __PYX_ERR(0, 127, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; - - /* "fontTools/cu2qu/cu2qu.py":442 - * @cython.locals(n=cython.int) - * @cython.locals(all_quadratic=cython.int) - * def curve_to_quadratic(curve, max_err, all_quadratic=True): # <<<<<<<<<<<<<< - * """Approximate a cubic Bezier curve with a spline of n quadratics. - * - */ - __pyx_t_6 = __Pyx_PyBool_FromLong(((int)1)); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 442, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - - /* "fontTools/cu2qu/cu2qu.py":439 - * - * - * @cython.locals(max_err=cython.double) # <<<<<<<<<<<<<< - * @cython.locals(n=cython.int) - * @cython.locals(all_quadratic=cython.int) - */ - __pyx_t_7 = PyTuple_New(1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 439, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - __Pyx_GIVEREF(__pyx_t_6); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 0, __pyx_t_6)) __PYX_ERR(0, 439, __pyx_L1_error); - __pyx_t_6 = 0; - __pyx_t_6 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_5cu2qu_5cu2qu_4curve_to_quadratic, 0, __pyx_n_s_curve_to_quadratic, NULL, __pyx_n_s_fontTools_cu2qu_cu2qu, __pyx_d, ((PyObject *)__pyx_codeobj__6)); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 439, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - __Pyx_CyFunction_SetDefaultsTuple(__pyx_t_6, __pyx_t_7); - __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; - if (PyDict_SetItem(__pyx_d, __pyx_n_s_curve_to_quadratic, __pyx_t_6) < 0) __PYX_ERR(0, 439, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; - - /* "fontTools/cu2qu/cu2qu.py":476 - * @cython.locals(l=cython.int, last_i=cython.int, i=cython.int) - * @cython.locals(all_quadratic=cython.int) - * def curves_to_quadratic(curves, max_errors, all_quadratic=True): # <<<<<<<<<<<<<< - * """Return quadratic Bezier splines approximating the input cubic Beziers. - * - */ - __pyx_t_6 = __Pyx_PyBool_FromLong(((int)1)); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 476, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - - /* "fontTools/cu2qu/cu2qu.py":474 - * - * - * @cython.locals(l=cython.int, last_i=cython.int, i=cython.int) # <<<<<<<<<<<<<< - * @cython.locals(all_quadratic=cython.int) - * def curves_to_quadratic(curves, max_errors, all_quadratic=True): - */ - __pyx_t_7 = PyTuple_New(1); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 474, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_7); - __Pyx_GIVEREF(__pyx_t_6); - if (__Pyx_PyTuple_SET_ITEM(__pyx_t_7, 0, __pyx_t_6)) __PYX_ERR(0, 474, __pyx_L1_error); - __pyx_t_6 = 0; - __pyx_t_6 = __Pyx_CyFunction_New(&__pyx_mdef_9fontTools_5cu2qu_5cu2qu_6curves_to_quadratic, 0, __pyx_n_s_curves_to_quadratic, NULL, __pyx_n_s_fontTools_cu2qu_cu2qu, __pyx_d, ((PyObject *)__pyx_codeobj__8)); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 474, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - __Pyx_CyFunction_SetDefaultsTuple(__pyx_t_6, __pyx_t_7); - __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0; - if (PyDict_SetItem(__pyx_d, __pyx_n_s_curves_to_quadratic, __pyx_t_6) < 0) __PYX_ERR(0, 474, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; - - /* "fontTools/cu2qu/cu2qu.py":1 - * # cython: language_level=3 # <<<<<<<<<<<<<< - * # distutils: define_macros=CYTHON_TRACE_NOGIL=1 - * - */ - __pyx_t_6 = __Pyx_PyDict_NewPresized(1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 1, __pyx_L1_error) - __Pyx_GOTREF(__pyx_t_6); - if (PyDict_SetItem(__pyx_t_6, __pyx_kp_u_curves_to_quadratic_line_474, __pyx_kp_u_Return_quadratic_Bezier_splines) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_6) < 0) __PYX_ERR(0, 1, __pyx_L1_error) - __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0; - - /*--- Wrapped vars code ---*/ - - goto __pyx_L0; - __pyx_L1_error:; - __Pyx_XDECREF(__pyx_t_5); - __Pyx_XDECREF(__pyx_t_6); - __Pyx_XDECREF(__pyx_t_7); - __Pyx_XDECREF(__pyx_t_8); - __Pyx_XDECREF(__pyx_t_9); - if (__pyx_m) { - if (__pyx_d && stringtab_initialized) { - __Pyx_AddTraceback("init fontTools.cu2qu.cu2qu", __pyx_clineno, __pyx_lineno, __pyx_filename); - } - #if !CYTHON_USE_MODULE_STATE - Py_CLEAR(__pyx_m); - #else - Py_DECREF(__pyx_m); - if (pystate_addmodule_run) { - PyObject *tp, *value, *tb; - PyErr_Fetch(&tp, &value, &tb); - PyState_RemoveModule(&__pyx_moduledef); - PyErr_Restore(tp, value, tb); - } - #endif - } else if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_ImportError, "init fontTools.cu2qu.cu2qu"); - } - __pyx_L0:; - __Pyx_RefNannyFinishContext(); - #if CYTHON_PEP489_MULTI_PHASE_INIT - return (__pyx_m != NULL) ? 0 : -1; - #elif PY_MAJOR_VERSION >= 3 - return __pyx_m; - #else - return; - #endif -} -/* #### Code section: cleanup_globals ### */ -/* #### Code section: cleanup_module ### */ -/* #### Code section: main_method ### */ -/* #### Code section: utility_code_pragmas ### */ -#ifdef _MSC_VER -#pragma warning( push ) -/* Warning 4127: conditional expression is constant - * Cython uses constant conditional expressions to allow in inline functions to be optimized at - * compile-time, so this warning is not useful - */ -#pragma warning( disable : 4127 ) -#endif - - - -/* #### Code section: utility_code_def ### */ - -/* --- Runtime support code --- */ -/* Refnanny */ -#if CYTHON_REFNANNY -static __Pyx_RefNannyAPIStruct *__Pyx_RefNannyImportAPI(const char *modname) { - PyObject *m = NULL, *p = NULL; - void *r = NULL; - m = PyImport_ImportModule(modname); - if (!m) goto end; - p = PyObject_GetAttrString(m, "RefNannyAPI"); - if (!p) goto end; - r = PyLong_AsVoidPtr(p); -end: - Py_XDECREF(p); - Py_XDECREF(m); - return (__Pyx_RefNannyAPIStruct *)r; -} -#endif - -/* PyErrExceptionMatches */ -#if CYTHON_FAST_THREAD_STATE -static int __Pyx_PyErr_ExceptionMatchesTuple(PyObject *exc_type, PyObject *tuple) { - Py_ssize_t i, n; - n = PyTuple_GET_SIZE(tuple); -#if PY_MAJOR_VERSION >= 3 - for (i=0; i= 0x030C00A6 - PyObject *current_exception = tstate->current_exception; - if (unlikely(!current_exception)) return 0; - exc_type = (PyObject*) Py_TYPE(current_exception); - if (exc_type == err) return 1; -#else - exc_type = tstate->curexc_type; - if (exc_type == err) return 1; - if (unlikely(!exc_type)) return 0; -#endif - #if CYTHON_AVOID_BORROWED_REFS - Py_INCREF(exc_type); - #endif - if (unlikely(PyTuple_Check(err))) { - result = __Pyx_PyErr_ExceptionMatchesTuple(exc_type, err); - } else { - result = __Pyx_PyErr_GivenExceptionMatches(exc_type, err); - } - #if CYTHON_AVOID_BORROWED_REFS - Py_DECREF(exc_type); - #endif - return result; -} -#endif - -/* PyErrFetchRestore */ -#if CYTHON_FAST_THREAD_STATE -static CYTHON_INLINE void __Pyx_ErrRestoreInState(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb) { -#if PY_VERSION_HEX >= 0x030C00A6 - PyObject *tmp_value; - assert(type == NULL || (value != NULL && type == (PyObject*) Py_TYPE(value))); - if (value) { - #if CYTHON_COMPILING_IN_CPYTHON - if (unlikely(((PyBaseExceptionObject*) value)->traceback != tb)) - #endif - PyException_SetTraceback(value, tb); - } - tmp_value = tstate->current_exception; - tstate->current_exception = value; - Py_XDECREF(tmp_value); - Py_XDECREF(type); - Py_XDECREF(tb); -#else - PyObject *tmp_type, *tmp_value, *tmp_tb; - tmp_type = tstate->curexc_type; - tmp_value = tstate->curexc_value; - tmp_tb = tstate->curexc_traceback; - tstate->curexc_type = type; - tstate->curexc_value = value; - tstate->curexc_traceback = tb; - Py_XDECREF(tmp_type); - Py_XDECREF(tmp_value); - Py_XDECREF(tmp_tb); -#endif -} -static CYTHON_INLINE void __Pyx_ErrFetchInState(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) { -#if PY_VERSION_HEX >= 0x030C00A6 - PyObject* exc_value; - exc_value = tstate->current_exception; - tstate->current_exception = 0; - *value = exc_value; - *type = NULL; - *tb = NULL; - if (exc_value) { - *type = (PyObject*) Py_TYPE(exc_value); - Py_INCREF(*type); - #if CYTHON_COMPILING_IN_CPYTHON - *tb = ((PyBaseExceptionObject*) exc_value)->traceback; - Py_XINCREF(*tb); - #else - *tb = PyException_GetTraceback(exc_value); - #endif - } -#else - *type = tstate->curexc_type; - *value = tstate->curexc_value; - *tb = tstate->curexc_traceback; - tstate->curexc_type = 0; - tstate->curexc_value = 0; - tstate->curexc_traceback = 0; -#endif -} -#endif - -/* PyObjectGetAttrStr */ -#if CYTHON_USE_TYPE_SLOTS -static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStr(PyObject* obj, PyObject* attr_name) { - PyTypeObject* tp = Py_TYPE(obj); - if (likely(tp->tp_getattro)) - return tp->tp_getattro(obj, attr_name); -#if PY_MAJOR_VERSION < 3 - if (likely(tp->tp_getattr)) - return tp->tp_getattr(obj, PyString_AS_STRING(attr_name)); -#endif - return PyObject_GetAttr(obj, attr_name); -} -#endif - -/* PyObjectGetAttrStrNoError */ -#if __PYX_LIMITED_VERSION_HEX < 0x030d00A1 -static void __Pyx_PyObject_GetAttrStr_ClearAttributeError(void) { - __Pyx_PyThreadState_declare - __Pyx_PyThreadState_assign - if (likely(__Pyx_PyErr_ExceptionMatches(PyExc_AttributeError))) - __Pyx_PyErr_Clear(); -} -#endif -static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStrNoError(PyObject* obj, PyObject* attr_name) { - PyObject *result; -#if __PYX_LIMITED_VERSION_HEX >= 0x030d00A1 - (void) PyObject_GetOptionalAttr(obj, attr_name, &result); - return result; -#else -#if CYTHON_COMPILING_IN_CPYTHON && CYTHON_USE_TYPE_SLOTS && PY_VERSION_HEX >= 0x030700B1 - PyTypeObject* tp = Py_TYPE(obj); - if (likely(tp->tp_getattro == PyObject_GenericGetAttr)) { - return _PyObject_GenericGetAttrWithDict(obj, attr_name, NULL, 1); - } -#endif - result = __Pyx_PyObject_GetAttrStr(obj, attr_name); - if (unlikely(!result)) { - __Pyx_PyObject_GetAttrStr_ClearAttributeError(); - } - return result; -#endif -} - -/* GetBuiltinName */ -static PyObject *__Pyx_GetBuiltinName(PyObject *name) { - PyObject* result = __Pyx_PyObject_GetAttrStrNoError(__pyx_b, name); - if (unlikely(!result) && !PyErr_Occurred()) { - PyErr_Format(PyExc_NameError, -#if PY_MAJOR_VERSION >= 3 - "name '%U' is not defined", name); -#else - "name '%.200s' is not defined", PyString_AS_STRING(name)); -#endif - } - return result; -} - -/* PyIntCompare */ -static CYTHON_INLINE int __Pyx_PyInt_BoolEqObjC(PyObject *op1, PyObject *op2, long intval, long inplace) { - CYTHON_MAYBE_UNUSED_VAR(intval); - CYTHON_UNUSED_VAR(inplace); - if (op1 == op2) { - return 1; - } - #if PY_MAJOR_VERSION < 3 - if (likely(PyInt_CheckExact(op1))) { - const long b = intval; - long a = PyInt_AS_LONG(op1); - return (a == b); - } - #endif - #if CYTHON_USE_PYLONG_INTERNALS - if (likely(PyLong_CheckExact(op1))) { - int unequal; - unsigned long uintval; - Py_ssize_t size = __Pyx_PyLong_DigitCount(op1); - const digit* digits = __Pyx_PyLong_Digits(op1); - if (intval == 0) { - return (__Pyx_PyLong_IsZero(op1) == 1); - } else if (intval < 0) { - if (__Pyx_PyLong_IsNonNeg(op1)) - return 0; - intval = -intval; - } else { - if (__Pyx_PyLong_IsNeg(op1)) - return 0; - } - uintval = (unsigned long) intval; -#if PyLong_SHIFT * 4 < SIZEOF_LONG*8 - if (uintval >> (PyLong_SHIFT * 4)) { - unequal = (size != 5) || (digits[0] != (uintval & (unsigned long) PyLong_MASK)) - | (digits[1] != ((uintval >> (1 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[2] != ((uintval >> (2 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[3] != ((uintval >> (3 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[4] != ((uintval >> (4 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)); - } else -#endif -#if PyLong_SHIFT * 3 < SIZEOF_LONG*8 - if (uintval >> (PyLong_SHIFT * 3)) { - unequal = (size != 4) || (digits[0] != (uintval & (unsigned long) PyLong_MASK)) - | (digits[1] != ((uintval >> (1 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[2] != ((uintval >> (2 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[3] != ((uintval >> (3 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)); - } else -#endif -#if PyLong_SHIFT * 2 < SIZEOF_LONG*8 - if (uintval >> (PyLong_SHIFT * 2)) { - unequal = (size != 3) || (digits[0] != (uintval & (unsigned long) PyLong_MASK)) - | (digits[1] != ((uintval >> (1 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)) | (digits[2] != ((uintval >> (2 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)); - } else -#endif -#if PyLong_SHIFT * 1 < SIZEOF_LONG*8 - if (uintval >> (PyLong_SHIFT * 1)) { - unequal = (size != 2) || (digits[0] != (uintval & (unsigned long) PyLong_MASK)) - | (digits[1] != ((uintval >> (1 * PyLong_SHIFT)) & (unsigned long) PyLong_MASK)); - } else -#endif - unequal = (size != 1) || (((unsigned long) digits[0]) != (uintval & (unsigned long) PyLong_MASK)); - return (unequal == 0); - } - #endif - if (PyFloat_CheckExact(op1)) { - const long b = intval; -#if CYTHON_COMPILING_IN_LIMITED_API - double a = __pyx_PyFloat_AsDouble(op1); -#else - double a = PyFloat_AS_DOUBLE(op1); -#endif - return ((double)a == (double)b); - } - return __Pyx_PyObject_IsTrueAndDecref( - PyObject_RichCompare(op1, op2, Py_EQ)); -} - -/* RaiseTooManyValuesToUnpack */ -static CYTHON_INLINE void __Pyx_RaiseTooManyValuesError(Py_ssize_t expected) { - PyErr_Format(PyExc_ValueError, - "too many values to unpack (expected %" CYTHON_FORMAT_SSIZE_T "d)", expected); -} - -/* RaiseNeedMoreValuesToUnpack */ -static CYTHON_INLINE void __Pyx_RaiseNeedMoreValuesError(Py_ssize_t index) { - PyErr_Format(PyExc_ValueError, - "need more than %" CYTHON_FORMAT_SSIZE_T "d value%.1s to unpack", - index, (index == 1) ? "" : "s"); -} - -/* IterFinish */ -static CYTHON_INLINE int __Pyx_IterFinish(void) { - PyObject* exc_type; - __Pyx_PyThreadState_declare - __Pyx_PyThreadState_assign - exc_type = __Pyx_PyErr_CurrentExceptionType(); - if (unlikely(exc_type)) { - if (unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) - return -1; - __Pyx_PyErr_Clear(); - return 0; - } - return 0; -} - -/* UnpackItemEndCheck */ -static int __Pyx_IternextUnpackEndCheck(PyObject *retval, Py_ssize_t expected) { - if (unlikely(retval)) { - Py_DECREF(retval); - __Pyx_RaiseTooManyValuesError(expected); - return -1; - } - return __Pyx_IterFinish(); -} - -/* GetItemInt */ -static PyObject *__Pyx_GetItemInt_Generic(PyObject *o, PyObject* j) { - PyObject *r; - if (unlikely(!j)) return NULL; - r = PyObject_GetItem(o, j); - Py_DECREF(j); - return r; -} -static CYTHON_INLINE PyObject *__Pyx_GetItemInt_List_Fast(PyObject *o, Py_ssize_t i, - CYTHON_NCP_UNUSED int wraparound, - CYTHON_NCP_UNUSED int boundscheck) { -#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - Py_ssize_t wrapped_i = i; - if (wraparound & unlikely(i < 0)) { - wrapped_i += PyList_GET_SIZE(o); - } - if ((!boundscheck) || likely(__Pyx_is_valid_index(wrapped_i, PyList_GET_SIZE(o)))) { - PyObject *r = PyList_GET_ITEM(o, wrapped_i); - Py_INCREF(r); - return r; - } - return __Pyx_GetItemInt_Generic(o, PyInt_FromSsize_t(i)); -#else - return PySequence_GetItem(o, i); -#endif -} -static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Tuple_Fast(PyObject *o, Py_ssize_t i, - CYTHON_NCP_UNUSED int wraparound, - CYTHON_NCP_UNUSED int boundscheck) { -#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - Py_ssize_t wrapped_i = i; - if (wraparound & unlikely(i < 0)) { - wrapped_i += PyTuple_GET_SIZE(o); - } - if ((!boundscheck) || likely(__Pyx_is_valid_index(wrapped_i, PyTuple_GET_SIZE(o)))) { - PyObject *r = PyTuple_GET_ITEM(o, wrapped_i); - Py_INCREF(r); - return r; - } - return __Pyx_GetItemInt_Generic(o, PyInt_FromSsize_t(i)); -#else - return PySequence_GetItem(o, i); -#endif -} -static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i, int is_list, - CYTHON_NCP_UNUSED int wraparound, - CYTHON_NCP_UNUSED int boundscheck) { -#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS && CYTHON_USE_TYPE_SLOTS - if (is_list || PyList_CheckExact(o)) { - Py_ssize_t n = ((!wraparound) | likely(i >= 0)) ? i : i + PyList_GET_SIZE(o); - if ((!boundscheck) || (likely(__Pyx_is_valid_index(n, PyList_GET_SIZE(o))))) { - PyObject *r = PyList_GET_ITEM(o, n); - Py_INCREF(r); - return r; - } - } - else if (PyTuple_CheckExact(o)) { - Py_ssize_t n = ((!wraparound) | likely(i >= 0)) ? i : i + PyTuple_GET_SIZE(o); - if ((!boundscheck) || likely(__Pyx_is_valid_index(n, PyTuple_GET_SIZE(o)))) { - PyObject *r = PyTuple_GET_ITEM(o, n); - Py_INCREF(r); - return r; - } - } else { - PyMappingMethods *mm = Py_TYPE(o)->tp_as_mapping; - PySequenceMethods *sm = Py_TYPE(o)->tp_as_sequence; - if (mm && mm->mp_subscript) { - PyObject *r, *key = PyInt_FromSsize_t(i); - if (unlikely(!key)) return NULL; - r = mm->mp_subscript(o, key); - Py_DECREF(key); - return r; - } - if (likely(sm && sm->sq_item)) { - if (wraparound && unlikely(i < 0) && likely(sm->sq_length)) { - Py_ssize_t l = sm->sq_length(o); - if (likely(l >= 0)) { - i += l; - } else { - if (!PyErr_ExceptionMatches(PyExc_OverflowError)) - return NULL; - PyErr_Clear(); - } - } - return sm->sq_item(o, i); - } - } -#else - if (is_list || !PyMapping_Check(o)) { - return PySequence_GetItem(o, i); - } -#endif - return __Pyx_GetItemInt_Generic(o, PyInt_FromSsize_t(i)); -} - -/* PyDictVersioning */ -#if CYTHON_USE_DICT_VERSIONS && CYTHON_USE_TYPE_SLOTS -static CYTHON_INLINE PY_UINT64_T __Pyx_get_tp_dict_version(PyObject *obj) { - PyObject *dict = Py_TYPE(obj)->tp_dict; - return likely(dict) ? __PYX_GET_DICT_VERSION(dict) : 0; -} -static CYTHON_INLINE PY_UINT64_T __Pyx_get_object_dict_version(PyObject *obj) { - PyObject **dictptr = NULL; - Py_ssize_t offset = Py_TYPE(obj)->tp_dictoffset; - if (offset) { -#if CYTHON_COMPILING_IN_CPYTHON - dictptr = (likely(offset > 0)) ? (PyObject **) ((char *)obj + offset) : _PyObject_GetDictPtr(obj); -#else - dictptr = _PyObject_GetDictPtr(obj); -#endif - } - return (dictptr && *dictptr) ? __PYX_GET_DICT_VERSION(*dictptr) : 0; -} -static CYTHON_INLINE int __Pyx_object_dict_version_matches(PyObject* obj, PY_UINT64_T tp_dict_version, PY_UINT64_T obj_dict_version) { - PyObject *dict = Py_TYPE(obj)->tp_dict; - if (unlikely(!dict) || unlikely(tp_dict_version != __PYX_GET_DICT_VERSION(dict))) - return 0; - return obj_dict_version == __Pyx_get_object_dict_version(obj); -} -#endif - -/* GetModuleGlobalName */ -#if CYTHON_USE_DICT_VERSIONS -static PyObject *__Pyx__GetModuleGlobalName(PyObject *name, PY_UINT64_T *dict_version, PyObject **dict_cached_value) -#else -static CYTHON_INLINE PyObject *__Pyx__GetModuleGlobalName(PyObject *name) -#endif -{ - PyObject *result; -#if !CYTHON_AVOID_BORROWED_REFS -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030500A1 && PY_VERSION_HEX < 0x030d0000 - result = _PyDict_GetItem_KnownHash(__pyx_d, name, ((PyASCIIObject *) name)->hash); - __PYX_UPDATE_DICT_CACHE(__pyx_d, result, *dict_cached_value, *dict_version) - if (likely(result)) { - return __Pyx_NewRef(result); - } else if (unlikely(PyErr_Occurred())) { - return NULL; - } -#elif CYTHON_COMPILING_IN_LIMITED_API - if (unlikely(!__pyx_m)) { - return NULL; - } - result = PyObject_GetAttr(__pyx_m, name); - if (likely(result)) { - return result; - } -#else - result = PyDict_GetItem(__pyx_d, name); - __PYX_UPDATE_DICT_CACHE(__pyx_d, result, *dict_cached_value, *dict_version) - if (likely(result)) { - return __Pyx_NewRef(result); - } -#endif -#else - result = PyObject_GetItem(__pyx_d, name); - __PYX_UPDATE_DICT_CACHE(__pyx_d, result, *dict_cached_value, *dict_version) - if (likely(result)) { - return __Pyx_NewRef(result); - } - PyErr_Clear(); -#endif - return __Pyx_GetBuiltinName(name); -} - -/* PyFunctionFastCall */ -#if CYTHON_FAST_PYCALL && !CYTHON_VECTORCALL -static PyObject* __Pyx_PyFunction_FastCallNoKw(PyCodeObject *co, PyObject **args, Py_ssize_t na, - PyObject *globals) { - PyFrameObject *f; - PyThreadState *tstate = __Pyx_PyThreadState_Current; - PyObject **fastlocals; - Py_ssize_t i; - PyObject *result; - assert(globals != NULL); - /* XXX Perhaps we should create a specialized - PyFrame_New() that doesn't take locals, but does - take builtins without sanity checking them. - */ - assert(tstate != NULL); - f = PyFrame_New(tstate, co, globals, NULL); - if (f == NULL) { - return NULL; - } - fastlocals = __Pyx_PyFrame_GetLocalsplus(f); - for (i = 0; i < na; i++) { - Py_INCREF(*args); - fastlocals[i] = *args++; - } - result = PyEval_EvalFrameEx(f,0); - ++tstate->recursion_depth; - Py_DECREF(f); - --tstate->recursion_depth; - return result; -} -static PyObject *__Pyx_PyFunction_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, PyObject *kwargs) { - PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func); - PyObject *globals = PyFunction_GET_GLOBALS(func); - PyObject *argdefs = PyFunction_GET_DEFAULTS(func); - PyObject *closure; -#if PY_MAJOR_VERSION >= 3 - PyObject *kwdefs; -#endif - PyObject *kwtuple, **k; - PyObject **d; - Py_ssize_t nd; - Py_ssize_t nk; - PyObject *result; - assert(kwargs == NULL || PyDict_Check(kwargs)); - nk = kwargs ? PyDict_Size(kwargs) : 0; - #if PY_MAJOR_VERSION < 3 - if (unlikely(Py_EnterRecursiveCall((char*)" while calling a Python object"))) { - return NULL; - } - #else - if (unlikely(Py_EnterRecursiveCall(" while calling a Python object"))) { - return NULL; - } - #endif - if ( -#if PY_MAJOR_VERSION >= 3 - co->co_kwonlyargcount == 0 && -#endif - likely(kwargs == NULL || nk == 0) && - co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) { - if (argdefs == NULL && co->co_argcount == nargs) { - result = __Pyx_PyFunction_FastCallNoKw(co, args, nargs, globals); - goto done; - } - else if (nargs == 0 && argdefs != NULL - && co->co_argcount == Py_SIZE(argdefs)) { - /* function called with no arguments, but all parameters have - a default value: use default values as arguments .*/ - args = &PyTuple_GET_ITEM(argdefs, 0); - result =__Pyx_PyFunction_FastCallNoKw(co, args, Py_SIZE(argdefs), globals); - goto done; - } - } - if (kwargs != NULL) { - Py_ssize_t pos, i; - kwtuple = PyTuple_New(2 * nk); - if (kwtuple == NULL) { - result = NULL; - goto done; - } - k = &PyTuple_GET_ITEM(kwtuple, 0); - pos = i = 0; - while (PyDict_Next(kwargs, &pos, &k[i], &k[i+1])) { - Py_INCREF(k[i]); - Py_INCREF(k[i+1]); - i += 2; - } - nk = i / 2; - } - else { - kwtuple = NULL; - k = NULL; - } - closure = PyFunction_GET_CLOSURE(func); -#if PY_MAJOR_VERSION >= 3 - kwdefs = PyFunction_GET_KW_DEFAULTS(func); -#endif - if (argdefs != NULL) { - d = &PyTuple_GET_ITEM(argdefs, 0); - nd = Py_SIZE(argdefs); - } - else { - d = NULL; - nd = 0; - } -#if PY_MAJOR_VERSION >= 3 - result = PyEval_EvalCodeEx((PyObject*)co, globals, (PyObject *)NULL, - args, (int)nargs, - k, (int)nk, - d, (int)nd, kwdefs, closure); -#else - result = PyEval_EvalCodeEx(co, globals, (PyObject *)NULL, - args, (int)nargs, - k, (int)nk, - d, (int)nd, closure); -#endif - Py_XDECREF(kwtuple); -done: - Py_LeaveRecursiveCall(); - return result; -} -#endif - -/* PyObjectCall */ -#if CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw) { - PyObject *result; - ternaryfunc call = Py_TYPE(func)->tp_call; - if (unlikely(!call)) - return PyObject_Call(func, arg, kw); - #if PY_MAJOR_VERSION < 3 - if (unlikely(Py_EnterRecursiveCall((char*)" while calling a Python object"))) - return NULL; - #else - if (unlikely(Py_EnterRecursiveCall(" while calling a Python object"))) - return NULL; - #endif - result = (*call)(func, arg, kw); - Py_LeaveRecursiveCall(); - if (unlikely(!result) && unlikely(!PyErr_Occurred())) { - PyErr_SetString( - PyExc_SystemError, - "NULL result without error in PyObject_Call"); - } - return result; -} -#endif - -/* PyObjectCallMethO */ -#if CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE PyObject* __Pyx_PyObject_CallMethO(PyObject *func, PyObject *arg) { - PyObject *self, *result; - PyCFunction cfunc; - cfunc = __Pyx_CyOrPyCFunction_GET_FUNCTION(func); - self = __Pyx_CyOrPyCFunction_GET_SELF(func); - #if PY_MAJOR_VERSION < 3 - if (unlikely(Py_EnterRecursiveCall((char*)" while calling a Python object"))) - return NULL; - #else - if (unlikely(Py_EnterRecursiveCall(" while calling a Python object"))) - return NULL; - #endif - result = cfunc(self, arg); - Py_LeaveRecursiveCall(); - if (unlikely(!result) && unlikely(!PyErr_Occurred())) { - PyErr_SetString( - PyExc_SystemError, - "NULL result without error in PyObject_Call"); - } - return result; -} -#endif - -/* PyObjectFastCall */ -#if PY_VERSION_HEX < 0x03090000 || CYTHON_COMPILING_IN_LIMITED_API -static PyObject* __Pyx_PyObject_FastCall_fallback(PyObject *func, PyObject **args, size_t nargs, PyObject *kwargs) { - PyObject *argstuple; - PyObject *result = 0; - size_t i; - argstuple = PyTuple_New((Py_ssize_t)nargs); - if (unlikely(!argstuple)) return NULL; - for (i = 0; i < nargs; i++) { - Py_INCREF(args[i]); - if (__Pyx_PyTuple_SET_ITEM(argstuple, (Py_ssize_t)i, args[i]) < 0) goto bad; - } - result = __Pyx_PyObject_Call(func, argstuple, kwargs); - bad: - Py_DECREF(argstuple); - return result; -} -#endif -static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObject **args, size_t _nargs, PyObject *kwargs) { - Py_ssize_t nargs = __Pyx_PyVectorcall_NARGS(_nargs); -#if CYTHON_COMPILING_IN_CPYTHON - if (nargs == 0 && kwargs == NULL) { - if (__Pyx_CyOrPyCFunction_Check(func) && likely( __Pyx_CyOrPyCFunction_GET_FLAGS(func) & METH_NOARGS)) - return __Pyx_PyObject_CallMethO(func, NULL); - } - else if (nargs == 1 && kwargs == NULL) { - if (__Pyx_CyOrPyCFunction_Check(func) && likely( __Pyx_CyOrPyCFunction_GET_FLAGS(func) & METH_O)) - return __Pyx_PyObject_CallMethO(func, args[0]); - } -#endif - #if PY_VERSION_HEX < 0x030800B1 - #if CYTHON_FAST_PYCCALL - if (PyCFunction_Check(func)) { - if (kwargs) { - return _PyCFunction_FastCallDict(func, args, nargs, kwargs); - } else { - return _PyCFunction_FastCallKeywords(func, args, nargs, NULL); - } - } - #if PY_VERSION_HEX >= 0x030700A1 - if (!kwargs && __Pyx_IS_TYPE(func, &PyMethodDescr_Type)) { - return _PyMethodDescr_FastCallKeywords(func, args, nargs, NULL); - } - #endif - #endif - #if CYTHON_FAST_PYCALL - if (PyFunction_Check(func)) { - return __Pyx_PyFunction_FastCallDict(func, args, nargs, kwargs); - } - #endif - #endif - if (kwargs == NULL) { - #if CYTHON_VECTORCALL - #if PY_VERSION_HEX < 0x03090000 - vectorcallfunc f = _PyVectorcall_Function(func); - #else - vectorcallfunc f = PyVectorcall_Function(func); - #endif - if (f) { - return f(func, args, (size_t)nargs, NULL); - } - #elif defined(__Pyx_CyFunction_USED) && CYTHON_BACKPORT_VECTORCALL - if (__Pyx_CyFunction_CheckExact(func)) { - __pyx_vectorcallfunc f = __Pyx_CyFunction_func_vectorcall(func); - if (f) return f(func, args, (size_t)nargs, NULL); - } - #endif - } - if (nargs == 0) { - return __Pyx_PyObject_Call(func, __pyx_empty_tuple, kwargs); - } - #if PY_VERSION_HEX >= 0x03090000 && !CYTHON_COMPILING_IN_LIMITED_API - return PyObject_VectorcallDict(func, args, (size_t)nargs, kwargs); - #else - return __Pyx_PyObject_FastCall_fallback(func, args, (size_t)nargs, kwargs); - #endif -} - -/* TupleAndListFromArray */ -#if CYTHON_COMPILING_IN_CPYTHON -static CYTHON_INLINE void __Pyx_copy_object_array(PyObject *const *CYTHON_RESTRICT src, PyObject** CYTHON_RESTRICT dest, Py_ssize_t length) { - PyObject *v; - Py_ssize_t i; - for (i = 0; i < length; i++) { - v = dest[i] = src[i]; - Py_INCREF(v); - } -} -static CYTHON_INLINE PyObject * -__Pyx_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n) -{ - PyObject *res; - if (n <= 0) { - Py_INCREF(__pyx_empty_tuple); - return __pyx_empty_tuple; - } - res = PyTuple_New(n); - if (unlikely(res == NULL)) return NULL; - __Pyx_copy_object_array(src, ((PyTupleObject*)res)->ob_item, n); - return res; -} -static CYTHON_INLINE PyObject * -__Pyx_PyList_FromArray(PyObject *const *src, Py_ssize_t n) -{ - PyObject *res; - if (n <= 0) { - return PyList_New(0); - } - res = PyList_New(n); - if (unlikely(res == NULL)) return NULL; - __Pyx_copy_object_array(src, ((PyListObject*)res)->ob_item, n); - return res; -} -#endif - -/* BytesEquals */ -static CYTHON_INLINE int __Pyx_PyBytes_Equals(PyObject* s1, PyObject* s2, int equals) { -#if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API - return PyObject_RichCompareBool(s1, s2, equals); -#else - if (s1 == s2) { - return (equals == Py_EQ); - } else if (PyBytes_CheckExact(s1) & PyBytes_CheckExact(s2)) { - const char *ps1, *ps2; - Py_ssize_t length = PyBytes_GET_SIZE(s1); - if (length != PyBytes_GET_SIZE(s2)) - return (equals == Py_NE); - ps1 = PyBytes_AS_STRING(s1); - ps2 = PyBytes_AS_STRING(s2); - if (ps1[0] != ps2[0]) { - return (equals == Py_NE); - } else if (length == 1) { - return (equals == Py_EQ); - } else { - int result; -#if CYTHON_USE_UNICODE_INTERNALS && (PY_VERSION_HEX < 0x030B0000) - Py_hash_t hash1, hash2; - hash1 = ((PyBytesObject*)s1)->ob_shash; - hash2 = ((PyBytesObject*)s2)->ob_shash; - if (hash1 != hash2 && hash1 != -1 && hash2 != -1) { - return (equals == Py_NE); - } -#endif - result = memcmp(ps1, ps2, (size_t)length); - return (equals == Py_EQ) ? (result == 0) : (result != 0); - } - } else if ((s1 == Py_None) & PyBytes_CheckExact(s2)) { - return (equals == Py_NE); - } else if ((s2 == Py_None) & PyBytes_CheckExact(s1)) { - return (equals == Py_NE); - } else { - int result; - PyObject* py_result = PyObject_RichCompare(s1, s2, equals); - if (!py_result) - return -1; - result = __Pyx_PyObject_IsTrue(py_result); - Py_DECREF(py_result); - return result; - } -#endif -} - -/* UnicodeEquals */ -static CYTHON_INLINE int __Pyx_PyUnicode_Equals(PyObject* s1, PyObject* s2, int equals) { -#if CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API - return PyObject_RichCompareBool(s1, s2, equals); -#else -#if PY_MAJOR_VERSION < 3 - PyObject* owned_ref = NULL; -#endif - int s1_is_unicode, s2_is_unicode; - if (s1 == s2) { - goto return_eq; - } - s1_is_unicode = PyUnicode_CheckExact(s1); - s2_is_unicode = PyUnicode_CheckExact(s2); -#if PY_MAJOR_VERSION < 3 - if ((s1_is_unicode & (!s2_is_unicode)) && PyString_CheckExact(s2)) { - owned_ref = PyUnicode_FromObject(s2); - if (unlikely(!owned_ref)) - return -1; - s2 = owned_ref; - s2_is_unicode = 1; - } else if ((s2_is_unicode & (!s1_is_unicode)) && PyString_CheckExact(s1)) { - owned_ref = PyUnicode_FromObject(s1); - if (unlikely(!owned_ref)) - return -1; - s1 = owned_ref; - s1_is_unicode = 1; - } else if (((!s2_is_unicode) & (!s1_is_unicode))) { - return __Pyx_PyBytes_Equals(s1, s2, equals); - } -#endif - if (s1_is_unicode & s2_is_unicode) { - Py_ssize_t length; - int kind; - void *data1, *data2; - if (unlikely(__Pyx_PyUnicode_READY(s1) < 0) || unlikely(__Pyx_PyUnicode_READY(s2) < 0)) - return -1; - length = __Pyx_PyUnicode_GET_LENGTH(s1); - if (length != __Pyx_PyUnicode_GET_LENGTH(s2)) { - goto return_ne; - } -#if CYTHON_USE_UNICODE_INTERNALS - { - Py_hash_t hash1, hash2; - #if CYTHON_PEP393_ENABLED - hash1 = ((PyASCIIObject*)s1)->hash; - hash2 = ((PyASCIIObject*)s2)->hash; - #else - hash1 = ((PyUnicodeObject*)s1)->hash; - hash2 = ((PyUnicodeObject*)s2)->hash; - #endif - if (hash1 != hash2 && hash1 != -1 && hash2 != -1) { - goto return_ne; - } - } -#endif - kind = __Pyx_PyUnicode_KIND(s1); - if (kind != __Pyx_PyUnicode_KIND(s2)) { - goto return_ne; - } - data1 = __Pyx_PyUnicode_DATA(s1); - data2 = __Pyx_PyUnicode_DATA(s2); - if (__Pyx_PyUnicode_READ(kind, data1, 0) != __Pyx_PyUnicode_READ(kind, data2, 0)) { - goto return_ne; - } else if (length == 1) { - goto return_eq; - } else { - int result = memcmp(data1, data2, (size_t)(length * kind)); - #if PY_MAJOR_VERSION < 3 - Py_XDECREF(owned_ref); - #endif - return (equals == Py_EQ) ? (result == 0) : (result != 0); - } - } else if ((s1 == Py_None) & s2_is_unicode) { - goto return_ne; - } else if ((s2 == Py_None) & s1_is_unicode) { - goto return_ne; - } else { - int result; - PyObject* py_result = PyObject_RichCompare(s1, s2, equals); - #if PY_MAJOR_VERSION < 3 - Py_XDECREF(owned_ref); - #endif - if (!py_result) - return -1; - result = __Pyx_PyObject_IsTrue(py_result); - Py_DECREF(py_result); - return result; - } -return_eq: - #if PY_MAJOR_VERSION < 3 - Py_XDECREF(owned_ref); - #endif - return (equals == Py_EQ); -return_ne: - #if PY_MAJOR_VERSION < 3 - Py_XDECREF(owned_ref); - #endif - return (equals == Py_NE); -#endif -} - -/* fastcall */ -#if CYTHON_METH_FASTCALL -static CYTHON_INLINE PyObject * __Pyx_GetKwValue_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues, PyObject *s) -{ - Py_ssize_t i, n = PyTuple_GET_SIZE(kwnames); - for (i = 0; i < n; i++) - { - if (s == PyTuple_GET_ITEM(kwnames, i)) return kwvalues[i]; - } - for (i = 0; i < n; i++) - { - int eq = __Pyx_PyUnicode_Equals(s, PyTuple_GET_ITEM(kwnames, i), Py_EQ); - if (unlikely(eq != 0)) { - if (unlikely(eq < 0)) return NULL; - return kwvalues[i]; - } - } - return NULL; -} -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030d0000 -CYTHON_UNUSED static PyObject *__Pyx_KwargsAsDict_FASTCALL(PyObject *kwnames, PyObject *const *kwvalues) { - Py_ssize_t i, nkwargs = PyTuple_GET_SIZE(kwnames); - PyObject *dict; - dict = PyDict_New(); - if (unlikely(!dict)) - return NULL; - for (i=0; i= 3 - "%s() got multiple values for keyword argument '%U'", func_name, kw_name); - #else - "%s() got multiple values for keyword argument '%s'", func_name, - PyString_AsString(kw_name)); - #endif -} - -/* ParseKeywords */ -static int __Pyx_ParseOptionalKeywords( - PyObject *kwds, - PyObject *const *kwvalues, - PyObject **argnames[], - PyObject *kwds2, - PyObject *values[], - Py_ssize_t num_pos_args, - const char* function_name) -{ - PyObject *key = 0, *value = 0; - Py_ssize_t pos = 0; - PyObject*** name; - PyObject*** first_kw_arg = argnames + num_pos_args; - int kwds_is_tuple = CYTHON_METH_FASTCALL && likely(PyTuple_Check(kwds)); - while (1) { - Py_XDECREF(key); key = NULL; - Py_XDECREF(value); value = NULL; - if (kwds_is_tuple) { - Py_ssize_t size; -#if CYTHON_ASSUME_SAFE_MACROS - size = PyTuple_GET_SIZE(kwds); -#else - size = PyTuple_Size(kwds); - if (size < 0) goto bad; -#endif - if (pos >= size) break; -#if CYTHON_AVOID_BORROWED_REFS - key = __Pyx_PySequence_ITEM(kwds, pos); - if (!key) goto bad; -#elif CYTHON_ASSUME_SAFE_MACROS - key = PyTuple_GET_ITEM(kwds, pos); -#else - key = PyTuple_GetItem(kwds, pos); - if (!key) goto bad; -#endif - value = kwvalues[pos]; - pos++; - } - else - { - if (!PyDict_Next(kwds, &pos, &key, &value)) break; -#if CYTHON_AVOID_BORROWED_REFS - Py_INCREF(key); -#endif - } - name = first_kw_arg; - while (*name && (**name != key)) name++; - if (*name) { - values[name-argnames] = value; -#if CYTHON_AVOID_BORROWED_REFS - Py_INCREF(value); - Py_DECREF(key); -#endif - key = NULL; - value = NULL; - continue; - } -#if !CYTHON_AVOID_BORROWED_REFS - Py_INCREF(key); -#endif - Py_INCREF(value); - name = first_kw_arg; - #if PY_MAJOR_VERSION < 3 - if (likely(PyString_Check(key))) { - while (*name) { - if ((CYTHON_COMPILING_IN_PYPY || PyString_GET_SIZE(**name) == PyString_GET_SIZE(key)) - && _PyString_Eq(**name, key)) { - values[name-argnames] = value; -#if CYTHON_AVOID_BORROWED_REFS - value = NULL; -#endif - break; - } - name++; - } - if (*name) continue; - else { - PyObject*** argname = argnames; - while (argname != first_kw_arg) { - if ((**argname == key) || ( - (CYTHON_COMPILING_IN_PYPY || PyString_GET_SIZE(**argname) == PyString_GET_SIZE(key)) - && _PyString_Eq(**argname, key))) { - goto arg_passed_twice; - } - argname++; - } - } - } else - #endif - if (likely(PyUnicode_Check(key))) { - while (*name) { - int cmp = ( - #if !CYTHON_COMPILING_IN_PYPY && PY_MAJOR_VERSION >= 3 - (__Pyx_PyUnicode_GET_LENGTH(**name) != __Pyx_PyUnicode_GET_LENGTH(key)) ? 1 : - #endif - PyUnicode_Compare(**name, key) - ); - if (cmp < 0 && unlikely(PyErr_Occurred())) goto bad; - if (cmp == 0) { - values[name-argnames] = value; -#if CYTHON_AVOID_BORROWED_REFS - value = NULL; -#endif - break; - } - name++; - } - if (*name) continue; - else { - PyObject*** argname = argnames; - while (argname != first_kw_arg) { - int cmp = (**argname == key) ? 0 : - #if !CYTHON_COMPILING_IN_PYPY && PY_MAJOR_VERSION >= 3 - (__Pyx_PyUnicode_GET_LENGTH(**argname) != __Pyx_PyUnicode_GET_LENGTH(key)) ? 1 : - #endif - PyUnicode_Compare(**argname, key); - if (cmp < 0 && unlikely(PyErr_Occurred())) goto bad; - if (cmp == 0) goto arg_passed_twice; - argname++; - } - } - } else - goto invalid_keyword_type; - if (kwds2) { - if (unlikely(PyDict_SetItem(kwds2, key, value))) goto bad; - } else { - goto invalid_keyword; - } - } - Py_XDECREF(key); - Py_XDECREF(value); - return 0; -arg_passed_twice: - __Pyx_RaiseDoubleKeywordsError(function_name, key); - goto bad; -invalid_keyword_type: - PyErr_Format(PyExc_TypeError, - "%.200s() keywords must be strings", function_name); - goto bad; -invalid_keyword: - #if PY_MAJOR_VERSION < 3 - PyErr_Format(PyExc_TypeError, - "%.200s() got an unexpected keyword argument '%.200s'", - function_name, PyString_AsString(key)); - #else - PyErr_Format(PyExc_TypeError, - "%s() got an unexpected keyword argument '%U'", - function_name, key); - #endif -bad: - Py_XDECREF(key); - Py_XDECREF(value); - return -1; -} - -/* GetException */ -#if CYTHON_FAST_THREAD_STATE -static int __Pyx__GetException(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) -#else -static int __Pyx_GetException(PyObject **type, PyObject **value, PyObject **tb) -#endif -{ - PyObject *local_type = NULL, *local_value, *local_tb = NULL; -#if CYTHON_FAST_THREAD_STATE - PyObject *tmp_type, *tmp_value, *tmp_tb; - #if PY_VERSION_HEX >= 0x030C00A6 - local_value = tstate->current_exception; - tstate->current_exception = 0; - if (likely(local_value)) { - local_type = (PyObject*) Py_TYPE(local_value); - Py_INCREF(local_type); - local_tb = PyException_GetTraceback(local_value); - } - #else - local_type = tstate->curexc_type; - local_value = tstate->curexc_value; - local_tb = tstate->curexc_traceback; - tstate->curexc_type = 0; - tstate->curexc_value = 0; - tstate->curexc_traceback = 0; - #endif -#else - PyErr_Fetch(&local_type, &local_value, &local_tb); -#endif - PyErr_NormalizeException(&local_type, &local_value, &local_tb); -#if CYTHON_FAST_THREAD_STATE && PY_VERSION_HEX >= 0x030C00A6 - if (unlikely(tstate->current_exception)) -#elif CYTHON_FAST_THREAD_STATE - if (unlikely(tstate->curexc_type)) -#else - if (unlikely(PyErr_Occurred())) -#endif - goto bad; - #if PY_MAJOR_VERSION >= 3 - if (local_tb) { - if (unlikely(PyException_SetTraceback(local_value, local_tb) < 0)) - goto bad; - } - #endif - Py_XINCREF(local_tb); - Py_XINCREF(local_type); - Py_XINCREF(local_value); - *type = local_type; - *value = local_value; - *tb = local_tb; -#if CYTHON_FAST_THREAD_STATE - #if CYTHON_USE_EXC_INFO_STACK - { - _PyErr_StackItem *exc_info = tstate->exc_info; - #if PY_VERSION_HEX >= 0x030B00a4 - tmp_value = exc_info->exc_value; - exc_info->exc_value = local_value; - tmp_type = NULL; - tmp_tb = NULL; - Py_XDECREF(local_type); - Py_XDECREF(local_tb); - #else - tmp_type = exc_info->exc_type; - tmp_value = exc_info->exc_value; - tmp_tb = exc_info->exc_traceback; - exc_info->exc_type = local_type; - exc_info->exc_value = local_value; - exc_info->exc_traceback = local_tb; - #endif - } - #else - tmp_type = tstate->exc_type; - tmp_value = tstate->exc_value; - tmp_tb = tstate->exc_traceback; - tstate->exc_type = local_type; - tstate->exc_value = local_value; - tstate->exc_traceback = local_tb; - #endif - Py_XDECREF(tmp_type); - Py_XDECREF(tmp_value); - Py_XDECREF(tmp_tb); -#else - PyErr_SetExcInfo(local_type, local_value, local_tb); -#endif - return 0; -bad: - *type = 0; - *value = 0; - *tb = 0; - Py_XDECREF(local_type); - Py_XDECREF(local_value); - Py_XDECREF(local_tb); - return -1; -} - -/* pep479 */ -static void __Pyx_Generator_Replace_StopIteration(int in_async_gen) { - PyObject *exc, *val, *tb, *cur_exc; - __Pyx_PyThreadState_declare - #ifdef __Pyx_StopAsyncIteration_USED - int is_async_stopiteration = 0; - #endif - CYTHON_MAYBE_UNUSED_VAR(in_async_gen); - cur_exc = PyErr_Occurred(); - if (likely(!__Pyx_PyErr_GivenExceptionMatches(cur_exc, PyExc_StopIteration))) { - #ifdef __Pyx_StopAsyncIteration_USED - if (in_async_gen && unlikely(__Pyx_PyErr_GivenExceptionMatches(cur_exc, __Pyx_PyExc_StopAsyncIteration))) { - is_async_stopiteration = 1; - } else - #endif - return; - } - __Pyx_PyThreadState_assign - __Pyx_GetException(&exc, &val, &tb); - Py_XDECREF(exc); - Py_XDECREF(val); - Py_XDECREF(tb); - PyErr_SetString(PyExc_RuntimeError, - #ifdef __Pyx_StopAsyncIteration_USED - is_async_stopiteration ? "async generator raised StopAsyncIteration" : - in_async_gen ? "async generator raised StopIteration" : - #endif - "generator raised StopIteration"); -} - -/* GetTopmostException */ -#if CYTHON_USE_EXC_INFO_STACK && CYTHON_FAST_THREAD_STATE -static _PyErr_StackItem * -__Pyx_PyErr_GetTopmostException(PyThreadState *tstate) -{ - _PyErr_StackItem *exc_info = tstate->exc_info; - while ((exc_info->exc_value == NULL || exc_info->exc_value == Py_None) && - exc_info->previous_item != NULL) - { - exc_info = exc_info->previous_item; - } - return exc_info; -} -#endif - -/* SaveResetException */ -#if CYTHON_FAST_THREAD_STATE -static CYTHON_INLINE void __Pyx__ExceptionSave(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) { - #if CYTHON_USE_EXC_INFO_STACK && PY_VERSION_HEX >= 0x030B00a4 - _PyErr_StackItem *exc_info = __Pyx_PyErr_GetTopmostException(tstate); - PyObject *exc_value = exc_info->exc_value; - if (exc_value == NULL || exc_value == Py_None) { - *value = NULL; - *type = NULL; - *tb = NULL; - } else { - *value = exc_value; - Py_INCREF(*value); - *type = (PyObject*) Py_TYPE(exc_value); - Py_INCREF(*type); - *tb = PyException_GetTraceback(exc_value); - } - #elif CYTHON_USE_EXC_INFO_STACK - _PyErr_StackItem *exc_info = __Pyx_PyErr_GetTopmostException(tstate); - *type = exc_info->exc_type; - *value = exc_info->exc_value; - *tb = exc_info->exc_traceback; - Py_XINCREF(*type); - Py_XINCREF(*value); - Py_XINCREF(*tb); - #else - *type = tstate->exc_type; - *value = tstate->exc_value; - *tb = tstate->exc_traceback; - Py_XINCREF(*type); - Py_XINCREF(*value); - Py_XINCREF(*tb); - #endif -} -static CYTHON_INLINE void __Pyx__ExceptionReset(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb) { - #if CYTHON_USE_EXC_INFO_STACK && PY_VERSION_HEX >= 0x030B00a4 - _PyErr_StackItem *exc_info = tstate->exc_info; - PyObject *tmp_value = exc_info->exc_value; - exc_info->exc_value = value; - Py_XDECREF(tmp_value); - Py_XDECREF(type); - Py_XDECREF(tb); - #else - PyObject *tmp_type, *tmp_value, *tmp_tb; - #if CYTHON_USE_EXC_INFO_STACK - _PyErr_StackItem *exc_info = tstate->exc_info; - tmp_type = exc_info->exc_type; - tmp_value = exc_info->exc_value; - tmp_tb = exc_info->exc_traceback; - exc_info->exc_type = type; - exc_info->exc_value = value; - exc_info->exc_traceback = tb; - #else - tmp_type = tstate->exc_type; - tmp_value = tstate->exc_value; - tmp_tb = tstate->exc_traceback; - tstate->exc_type = type; - tstate->exc_value = value; - tstate->exc_traceback = tb; - #endif - Py_XDECREF(tmp_type); - Py_XDECREF(tmp_value); - Py_XDECREF(tmp_tb); - #endif -} -#endif - -/* IterNext */ -static PyObject *__Pyx_PyIter_Next2Default(PyObject* defval) { - PyObject* exc_type; - __Pyx_PyThreadState_declare - __Pyx_PyThreadState_assign - exc_type = __Pyx_PyErr_CurrentExceptionType(); - if (unlikely(exc_type)) { - if (!defval || unlikely(!__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) - return NULL; - __Pyx_PyErr_Clear(); - Py_INCREF(defval); - return defval; - } - if (defval) { - Py_INCREF(defval); - return defval; - } - __Pyx_PyErr_SetNone(PyExc_StopIteration); - return NULL; -} -static void __Pyx_PyIter_Next_ErrorNoIterator(PyObject *iterator) { - __Pyx_TypeName iterator_type_name = __Pyx_PyType_GetName(Py_TYPE(iterator)); - PyErr_Format(PyExc_TypeError, - __Pyx_FMT_TYPENAME " object is not an iterator", iterator_type_name); - __Pyx_DECREF_TypeName(iterator_type_name); -} -static CYTHON_INLINE PyObject *__Pyx_PyIter_Next2(PyObject* iterator, PyObject* defval) { - PyObject* next; - iternextfunc iternext = Py_TYPE(iterator)->tp_iternext; - if (likely(iternext)) { -#if CYTHON_USE_TYPE_SLOTS || CYTHON_COMPILING_IN_PYPY - next = iternext(iterator); - if (likely(next)) - return next; -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030d0000 - if (unlikely(iternext == &_PyObject_NextNotImplemented)) - return NULL; -#endif -#else - next = PyIter_Next(iterator); - if (likely(next)) - return next; -#endif - } else if (CYTHON_USE_TYPE_SLOTS || unlikely(!PyIter_Check(iterator))) { - __Pyx_PyIter_Next_ErrorNoIterator(iterator); - return NULL; - } -#if !CYTHON_USE_TYPE_SLOTS - else { - next = PyIter_Next(iterator); - if (likely(next)) - return next; - } -#endif - return __Pyx_PyIter_Next2Default(defval); -} - -/* PyIntBinop */ -#if !CYTHON_COMPILING_IN_PYPY -static PyObject* __Pyx_PyInt_AddObjC(PyObject *op1, PyObject *op2, long intval, int inplace, int zerodivision_check) { - CYTHON_MAYBE_UNUSED_VAR(intval); - CYTHON_MAYBE_UNUSED_VAR(inplace); - CYTHON_UNUSED_VAR(zerodivision_check); - #if PY_MAJOR_VERSION < 3 - if (likely(PyInt_CheckExact(op1))) { - const long b = intval; - long x; - long a = PyInt_AS_LONG(op1); - - x = (long)((unsigned long)a + (unsigned long)b); - if (likely((x^a) >= 0 || (x^b) >= 0)) - return PyInt_FromLong(x); - return PyLong_Type.tp_as_number->nb_add(op1, op2); - } - #endif - #if CYTHON_USE_PYLONG_INTERNALS - if (likely(PyLong_CheckExact(op1))) { - const long b = intval; - long a, x; -#ifdef HAVE_LONG_LONG - const PY_LONG_LONG llb = intval; - PY_LONG_LONG lla, llx; -#endif - if (unlikely(__Pyx_PyLong_IsZero(op1))) { - return __Pyx_NewRef(op2); - } - if (likely(__Pyx_PyLong_IsCompact(op1))) { - a = __Pyx_PyLong_CompactValue(op1); - } else { - const digit* digits = __Pyx_PyLong_Digits(op1); - const Py_ssize_t size = __Pyx_PyLong_SignedDigitCount(op1); - switch (size) { - case -2: - if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) { - a = -(long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])); - break; - #ifdef HAVE_LONG_LONG - } else if (8 * sizeof(PY_LONG_LONG) - 1 > 2 * PyLong_SHIFT) { - lla = -(PY_LONG_LONG) (((((unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0])); - goto long_long; - #endif - } - CYTHON_FALLTHROUGH; - case 2: - if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) { - a = (long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])); - break; - #ifdef HAVE_LONG_LONG - } else if (8 * sizeof(PY_LONG_LONG) - 1 > 2 * PyLong_SHIFT) { - lla = (PY_LONG_LONG) (((((unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0])); - goto long_long; - #endif - } - CYTHON_FALLTHROUGH; - case -3: - if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT) { - a = -(long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])); - break; - #ifdef HAVE_LONG_LONG - } else if (8 * sizeof(PY_LONG_LONG) - 1 > 3 * PyLong_SHIFT) { - lla = -(PY_LONG_LONG) (((((((unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0])); - goto long_long; - #endif - } - CYTHON_FALLTHROUGH; - case 3: - if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT) { - a = (long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])); - break; - #ifdef HAVE_LONG_LONG - } else if (8 * sizeof(PY_LONG_LONG) - 1 > 3 * PyLong_SHIFT) { - lla = (PY_LONG_LONG) (((((((unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0])); - goto long_long; - #endif - } - CYTHON_FALLTHROUGH; - case -4: - if (8 * sizeof(long) - 1 > 4 * PyLong_SHIFT) { - a = -(long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])); - break; - #ifdef HAVE_LONG_LONG - } else if (8 * sizeof(PY_LONG_LONG) - 1 > 4 * PyLong_SHIFT) { - lla = -(PY_LONG_LONG) (((((((((unsigned PY_LONG_LONG)digits[3]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0])); - goto long_long; - #endif - } - CYTHON_FALLTHROUGH; - case 4: - if (8 * sizeof(long) - 1 > 4 * PyLong_SHIFT) { - a = (long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])); - break; - #ifdef HAVE_LONG_LONG - } else if (8 * sizeof(PY_LONG_LONG) - 1 > 4 * PyLong_SHIFT) { - lla = (PY_LONG_LONG) (((((((((unsigned PY_LONG_LONG)digits[3]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0])); - goto long_long; - #endif - } - CYTHON_FALLTHROUGH; - default: return PyLong_Type.tp_as_number->nb_add(op1, op2); - } - } - x = a + b; - return PyLong_FromLong(x); -#ifdef HAVE_LONG_LONG - long_long: - llx = lla + llb; - return PyLong_FromLongLong(llx); -#endif - - - } - #endif - if (PyFloat_CheckExact(op1)) { - const long b = intval; -#if CYTHON_COMPILING_IN_LIMITED_API - double a = __pyx_PyFloat_AsDouble(op1); -#else - double a = PyFloat_AS_DOUBLE(op1); -#endif - double result; - - PyFPE_START_PROTECT("add", return NULL) - result = ((double)a) + (double)b; - PyFPE_END_PROTECT(result) - return PyFloat_FromDouble(result); - } - return (inplace ? PyNumber_InPlaceAdd : PyNumber_Add)(op1, op2); -} -#endif - -/* RaiseException */ -#if PY_MAJOR_VERSION < 3 -static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, PyObject *cause) { - __Pyx_PyThreadState_declare - CYTHON_UNUSED_VAR(cause); - Py_XINCREF(type); - if (!value || value == Py_None) - value = NULL; - else - Py_INCREF(value); - if (!tb || tb == Py_None) - tb = NULL; - else { - Py_INCREF(tb); - if (!PyTraceBack_Check(tb)) { - PyErr_SetString(PyExc_TypeError, - "raise: arg 3 must be a traceback or None"); - goto raise_error; - } - } - if (PyType_Check(type)) { -#if CYTHON_COMPILING_IN_PYPY - if (!value) { - Py_INCREF(Py_None); - value = Py_None; - } -#endif - PyErr_NormalizeException(&type, &value, &tb); - } else { - if (value) { - PyErr_SetString(PyExc_TypeError, - "instance exception may not have a separate value"); - goto raise_error; - } - value = type; - type = (PyObject*) Py_TYPE(type); - Py_INCREF(type); - if (!PyType_IsSubtype((PyTypeObject *)type, (PyTypeObject *)PyExc_BaseException)) { - PyErr_SetString(PyExc_TypeError, - "raise: exception class must be a subclass of BaseException"); - goto raise_error; - } - } - __Pyx_PyThreadState_assign - __Pyx_ErrRestore(type, value, tb); - return; -raise_error: - Py_XDECREF(value); - Py_XDECREF(type); - Py_XDECREF(tb); - return; -} -#else -static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, PyObject *cause) { - PyObject* owned_instance = NULL; - if (tb == Py_None) { - tb = 0; - } else if (tb && !PyTraceBack_Check(tb)) { - PyErr_SetString(PyExc_TypeError, - "raise: arg 3 must be a traceback or None"); - goto bad; - } - if (value == Py_None) - value = 0; - if (PyExceptionInstance_Check(type)) { - if (value) { - PyErr_SetString(PyExc_TypeError, - "instance exception may not have a separate value"); - goto bad; - } - value = type; - type = (PyObject*) Py_TYPE(value); - } else if (PyExceptionClass_Check(type)) { - PyObject *instance_class = NULL; - if (value && PyExceptionInstance_Check(value)) { - instance_class = (PyObject*) Py_TYPE(value); - if (instance_class != type) { - int is_subclass = PyObject_IsSubclass(instance_class, type); - if (!is_subclass) { - instance_class = NULL; - } else if (unlikely(is_subclass == -1)) { - goto bad; - } else { - type = instance_class; - } - } - } - if (!instance_class) { - PyObject *args; - if (!value) - args = PyTuple_New(0); - else if (PyTuple_Check(value)) { - Py_INCREF(value); - args = value; - } else - args = PyTuple_Pack(1, value); - if (!args) - goto bad; - owned_instance = PyObject_Call(type, args, NULL); - Py_DECREF(args); - if (!owned_instance) - goto bad; - value = owned_instance; - if (!PyExceptionInstance_Check(value)) { - PyErr_Format(PyExc_TypeError, - "calling %R should have returned an instance of " - "BaseException, not %R", - type, Py_TYPE(value)); - goto bad; - } - } - } else { - PyErr_SetString(PyExc_TypeError, - "raise: exception class must be a subclass of BaseException"); - goto bad; - } - if (cause) { - PyObject *fixed_cause; - if (cause == Py_None) { - fixed_cause = NULL; - } else if (PyExceptionClass_Check(cause)) { - fixed_cause = PyObject_CallObject(cause, NULL); - if (fixed_cause == NULL) - goto bad; - } else if (PyExceptionInstance_Check(cause)) { - fixed_cause = cause; - Py_INCREF(fixed_cause); - } else { - PyErr_SetString(PyExc_TypeError, - "exception causes must derive from " - "BaseException"); - goto bad; - } - PyException_SetCause(value, fixed_cause); - } - PyErr_SetObject(type, value); - if (tb) { - #if PY_VERSION_HEX >= 0x030C00A6 - PyException_SetTraceback(value, tb); - #elif CYTHON_FAST_THREAD_STATE - PyThreadState *tstate = __Pyx_PyThreadState_Current; - PyObject* tmp_tb = tstate->curexc_traceback; - if (tb != tmp_tb) { - Py_INCREF(tb); - tstate->curexc_traceback = tb; - Py_XDECREF(tmp_tb); - } -#else - PyObject *tmp_type, *tmp_value, *tmp_tb; - PyErr_Fetch(&tmp_type, &tmp_value, &tmp_tb); - Py_INCREF(tb); - PyErr_Restore(tmp_type, tmp_value, tb); - Py_XDECREF(tmp_tb); -#endif - } -bad: - Py_XDECREF(owned_instance); - return; -} -#endif - -/* SetItemInt */ -static int __Pyx_SetItemInt_Generic(PyObject *o, PyObject *j, PyObject *v) { - int r; - if (unlikely(!j)) return -1; - r = PyObject_SetItem(o, j, v); - Py_DECREF(j); - return r; -} -static CYTHON_INLINE int __Pyx_SetItemInt_Fast(PyObject *o, Py_ssize_t i, PyObject *v, int is_list, - CYTHON_NCP_UNUSED int wraparound, CYTHON_NCP_UNUSED int boundscheck) { -#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS && CYTHON_USE_TYPE_SLOTS - if (is_list || PyList_CheckExact(o)) { - Py_ssize_t n = (!wraparound) ? i : ((likely(i >= 0)) ? i : i + PyList_GET_SIZE(o)); - if ((!boundscheck) || likely(__Pyx_is_valid_index(n, PyList_GET_SIZE(o)))) { - PyObject* old = PyList_GET_ITEM(o, n); - Py_INCREF(v); - PyList_SET_ITEM(o, n, v); - Py_DECREF(old); - return 1; - } - } else { - PyMappingMethods *mm = Py_TYPE(o)->tp_as_mapping; - PySequenceMethods *sm = Py_TYPE(o)->tp_as_sequence; - if (mm && mm->mp_ass_subscript) { - int r; - PyObject *key = PyInt_FromSsize_t(i); - if (unlikely(!key)) return -1; - r = mm->mp_ass_subscript(o, key, v); - Py_DECREF(key); - return r; - } - if (likely(sm && sm->sq_ass_item)) { - if (wraparound && unlikely(i < 0) && likely(sm->sq_length)) { - Py_ssize_t l = sm->sq_length(o); - if (likely(l >= 0)) { - i += l; - } else { - if (!PyErr_ExceptionMatches(PyExc_OverflowError)) - return -1; - PyErr_Clear(); - } - } - return sm->sq_ass_item(o, i, v); - } - } -#else - if (is_list || !PyMapping_Check(o)) - { - return PySequence_SetItem(o, i, v); - } -#endif - return __Pyx_SetItemInt_Generic(o, PyInt_FromSsize_t(i), v); -} - -/* ModInt[long] */ -static CYTHON_INLINE long __Pyx_mod_long(long a, long b) { - long r = a % b; - r += ((r != 0) & ((r ^ b) < 0)) * b; - return r; -} - -/* FixUpExtensionType */ -#if CYTHON_USE_TYPE_SPECS -static int __Pyx_fix_up_extension_type_from_spec(PyType_Spec *spec, PyTypeObject *type) { -#if PY_VERSION_HEX > 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API - CYTHON_UNUSED_VAR(spec); - CYTHON_UNUSED_VAR(type); -#else - const PyType_Slot *slot = spec->slots; - while (slot && slot->slot && slot->slot != Py_tp_members) - slot++; - if (slot && slot->slot == Py_tp_members) { - int changed = 0; -#if !(PY_VERSION_HEX <= 0x030900b1 && CYTHON_COMPILING_IN_CPYTHON) - const -#endif - PyMemberDef *memb = (PyMemberDef*) slot->pfunc; - while (memb && memb->name) { - if (memb->name[0] == '_' && memb->name[1] == '_') { -#if PY_VERSION_HEX < 0x030900b1 - if (strcmp(memb->name, "__weaklistoffset__") == 0) { - assert(memb->type == T_PYSSIZET); - assert(memb->flags == READONLY); - type->tp_weaklistoffset = memb->offset; - changed = 1; - } - else if (strcmp(memb->name, "__dictoffset__") == 0) { - assert(memb->type == T_PYSSIZET); - assert(memb->flags == READONLY); - type->tp_dictoffset = memb->offset; - changed = 1; - } -#if CYTHON_METH_FASTCALL - else if (strcmp(memb->name, "__vectorcalloffset__") == 0) { - assert(memb->type == T_PYSSIZET); - assert(memb->flags == READONLY); -#if PY_VERSION_HEX >= 0x030800b4 - type->tp_vectorcall_offset = memb->offset; -#else - type->tp_print = (printfunc) memb->offset; -#endif - changed = 1; - } -#endif -#else - if ((0)); -#endif -#if PY_VERSION_HEX <= 0x030900b1 && CYTHON_COMPILING_IN_CPYTHON - else if (strcmp(memb->name, "__module__") == 0) { - PyObject *descr; - assert(memb->type == T_OBJECT); - assert(memb->flags == 0 || memb->flags == READONLY); - descr = PyDescr_NewMember(type, memb); - if (unlikely(!descr)) - return -1; - if (unlikely(PyDict_SetItem(type->tp_dict, PyDescr_NAME(descr), descr) < 0)) { - Py_DECREF(descr); - return -1; - } - Py_DECREF(descr); - changed = 1; - } -#endif - } - memb++; - } - if (changed) - PyType_Modified(type); - } -#endif - return 0; -} -#endif - -/* PyObjectCallNoArg */ -static CYTHON_INLINE PyObject* __Pyx_PyObject_CallNoArg(PyObject *func) { - PyObject *arg[2] = {NULL, NULL}; - return __Pyx_PyObject_FastCall(func, arg + 1, 0 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET); -} - -/* PyObjectCallOneArg */ -static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg) { - PyObject *args[2] = {NULL, arg}; - return __Pyx_PyObject_FastCall(func, args+1, 1 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET); -} - -/* PyObjectGetMethod */ -static int __Pyx_PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) { - PyObject *attr; -#if CYTHON_UNPACK_METHODS && CYTHON_COMPILING_IN_CPYTHON && CYTHON_USE_PYTYPE_LOOKUP - __Pyx_TypeName type_name; - PyTypeObject *tp = Py_TYPE(obj); - PyObject *descr; - descrgetfunc f = NULL; - PyObject **dictptr, *dict; - int meth_found = 0; - assert (*method == NULL); - if (unlikely(tp->tp_getattro != PyObject_GenericGetAttr)) { - attr = __Pyx_PyObject_GetAttrStr(obj, name); - goto try_unpack; - } - if (unlikely(tp->tp_dict == NULL) && unlikely(PyType_Ready(tp) < 0)) { - return 0; - } - descr = _PyType_Lookup(tp, name); - if (likely(descr != NULL)) { - Py_INCREF(descr); -#if defined(Py_TPFLAGS_METHOD_DESCRIPTOR) && Py_TPFLAGS_METHOD_DESCRIPTOR - if (__Pyx_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)) -#elif PY_MAJOR_VERSION >= 3 - #ifdef __Pyx_CyFunction_USED - if (likely(PyFunction_Check(descr) || __Pyx_IS_TYPE(descr, &PyMethodDescr_Type) || __Pyx_CyFunction_Check(descr))) - #else - if (likely(PyFunction_Check(descr) || __Pyx_IS_TYPE(descr, &PyMethodDescr_Type))) - #endif -#else - #ifdef __Pyx_CyFunction_USED - if (likely(PyFunction_Check(descr) || __Pyx_CyFunction_Check(descr))) - #else - if (likely(PyFunction_Check(descr))) - #endif -#endif - { - meth_found = 1; - } else { - f = Py_TYPE(descr)->tp_descr_get; - if (f != NULL && PyDescr_IsData(descr)) { - attr = f(descr, obj, (PyObject *)Py_TYPE(obj)); - Py_DECREF(descr); - goto try_unpack; - } - } - } - dictptr = _PyObject_GetDictPtr(obj); - if (dictptr != NULL && (dict = *dictptr) != NULL) { - Py_INCREF(dict); - attr = __Pyx_PyDict_GetItemStr(dict, name); - if (attr != NULL) { - Py_INCREF(attr); - Py_DECREF(dict); - Py_XDECREF(descr); - goto try_unpack; - } - Py_DECREF(dict); - } - if (meth_found) { - *method = descr; - return 1; - } - if (f != NULL) { - attr = f(descr, obj, (PyObject *)Py_TYPE(obj)); - Py_DECREF(descr); - goto try_unpack; - } - if (likely(descr != NULL)) { - *method = descr; - return 0; - } - type_name = __Pyx_PyType_GetName(tp); - PyErr_Format(PyExc_AttributeError, -#if PY_MAJOR_VERSION >= 3 - "'" __Pyx_FMT_TYPENAME "' object has no attribute '%U'", - type_name, name); -#else - "'" __Pyx_FMT_TYPENAME "' object has no attribute '%.400s'", - type_name, PyString_AS_STRING(name)); -#endif - __Pyx_DECREF_TypeName(type_name); - return 0; -#else - attr = __Pyx_PyObject_GetAttrStr(obj, name); - goto try_unpack; -#endif -try_unpack: -#if CYTHON_UNPACK_METHODS - if (likely(attr) && PyMethod_Check(attr) && likely(PyMethod_GET_SELF(attr) == obj)) { - PyObject *function = PyMethod_GET_FUNCTION(attr); - Py_INCREF(function); - Py_DECREF(attr); - *method = function; - return 1; - } -#endif - *method = attr; - return 0; -} - -/* PyObjectCallMethod0 */ -static PyObject* __Pyx_PyObject_CallMethod0(PyObject* obj, PyObject* method_name) { - PyObject *method = NULL, *result = NULL; - int is_method = __Pyx_PyObject_GetMethod(obj, method_name, &method); - if (likely(is_method)) { - result = __Pyx_PyObject_CallOneArg(method, obj); - Py_DECREF(method); - return result; - } - if (unlikely(!method)) goto bad; - result = __Pyx_PyObject_CallNoArg(method); - Py_DECREF(method); -bad: - return result; -} - -/* ValidateBasesTuple */ -#if CYTHON_COMPILING_IN_CPYTHON || CYTHON_COMPILING_IN_LIMITED_API || CYTHON_USE_TYPE_SPECS -static int __Pyx_validate_bases_tuple(const char *type_name, Py_ssize_t dictoffset, PyObject *bases) { - Py_ssize_t i, n; -#if CYTHON_ASSUME_SAFE_MACROS - n = PyTuple_GET_SIZE(bases); -#else - n = PyTuple_Size(bases); - if (n < 0) return -1; -#endif - for (i = 1; i < n; i++) - { -#if CYTHON_AVOID_BORROWED_REFS - PyObject *b0 = PySequence_GetItem(bases, i); - if (!b0) return -1; -#elif CYTHON_ASSUME_SAFE_MACROS - PyObject *b0 = PyTuple_GET_ITEM(bases, i); -#else - PyObject *b0 = PyTuple_GetItem(bases, i); - if (!b0) return -1; -#endif - PyTypeObject *b; -#if PY_MAJOR_VERSION < 3 - if (PyClass_Check(b0)) - { - PyErr_Format(PyExc_TypeError, "base class '%.200s' is an old-style class", - PyString_AS_STRING(((PyClassObject*)b0)->cl_name)); -#if CYTHON_AVOID_BORROWED_REFS - Py_DECREF(b0); -#endif - return -1; - } -#endif - b = (PyTypeObject*) b0; - if (!__Pyx_PyType_HasFeature(b, Py_TPFLAGS_HEAPTYPE)) - { - __Pyx_TypeName b_name = __Pyx_PyType_GetName(b); - PyErr_Format(PyExc_TypeError, - "base class '" __Pyx_FMT_TYPENAME "' is not a heap type", b_name); - __Pyx_DECREF_TypeName(b_name); -#if CYTHON_AVOID_BORROWED_REFS - Py_DECREF(b0); -#endif - return -1; - } - if (dictoffset == 0) - { - Py_ssize_t b_dictoffset = 0; -#if CYTHON_USE_TYPE_SLOTS || CYTHON_COMPILING_IN_PYPY - b_dictoffset = b->tp_dictoffset; -#else - PyObject *py_b_dictoffset = PyObject_GetAttrString((PyObject*)b, "__dictoffset__"); - if (!py_b_dictoffset) goto dictoffset_return; - b_dictoffset = PyLong_AsSsize_t(py_b_dictoffset); - Py_DECREF(py_b_dictoffset); - if (b_dictoffset == -1 && PyErr_Occurred()) goto dictoffset_return; -#endif - if (b_dictoffset) { - { - __Pyx_TypeName b_name = __Pyx_PyType_GetName(b); - PyErr_Format(PyExc_TypeError, - "extension type '%.200s' has no __dict__ slot, " - "but base type '" __Pyx_FMT_TYPENAME "' has: " - "either add 'cdef dict __dict__' to the extension type " - "or add '__slots__ = [...]' to the base type", - type_name, b_name); - __Pyx_DECREF_TypeName(b_name); - } -#if !(CYTHON_USE_TYPE_SLOTS || CYTHON_COMPILING_IN_PYPY) - dictoffset_return: -#endif -#if CYTHON_AVOID_BORROWED_REFS - Py_DECREF(b0); -#endif - return -1; - } - } -#if CYTHON_AVOID_BORROWED_REFS - Py_DECREF(b0); -#endif - } - return 0; -} -#endif - -/* PyType_Ready */ -static int __Pyx_PyType_Ready(PyTypeObject *t) { -#if CYTHON_USE_TYPE_SPECS || !(CYTHON_COMPILING_IN_CPYTHON || CYTHON_COMPILING_IN_LIMITED_API) || defined(PYSTON_MAJOR_VERSION) - (void)__Pyx_PyObject_CallMethod0; -#if CYTHON_USE_TYPE_SPECS - (void)__Pyx_validate_bases_tuple; -#endif - return PyType_Ready(t); -#else - int r; - PyObject *bases = __Pyx_PyType_GetSlot(t, tp_bases, PyObject*); - if (bases && unlikely(__Pyx_validate_bases_tuple(t->tp_name, t->tp_dictoffset, bases) == -1)) - return -1; -#if PY_VERSION_HEX >= 0x03050000 && !defined(PYSTON_MAJOR_VERSION) - { - int gc_was_enabled; - #if PY_VERSION_HEX >= 0x030A00b1 - gc_was_enabled = PyGC_Disable(); - (void)__Pyx_PyObject_CallMethod0; - #else - PyObject *ret, *py_status; - PyObject *gc = NULL; - #if PY_VERSION_HEX >= 0x030700a1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM+0 >= 0x07030400) - gc = PyImport_GetModule(__pyx_kp_u_gc); - #endif - if (unlikely(!gc)) gc = PyImport_Import(__pyx_kp_u_gc); - if (unlikely(!gc)) return -1; - py_status = __Pyx_PyObject_CallMethod0(gc, __pyx_kp_u_isenabled); - if (unlikely(!py_status)) { - Py_DECREF(gc); - return -1; - } - gc_was_enabled = __Pyx_PyObject_IsTrue(py_status); - Py_DECREF(py_status); - if (gc_was_enabled > 0) { - ret = __Pyx_PyObject_CallMethod0(gc, __pyx_kp_u_disable); - if (unlikely(!ret)) { - Py_DECREF(gc); - return -1; - } - Py_DECREF(ret); - } else if (unlikely(gc_was_enabled == -1)) { - Py_DECREF(gc); - return -1; - } - #endif - t->tp_flags |= Py_TPFLAGS_HEAPTYPE; -#if PY_VERSION_HEX >= 0x030A0000 - t->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE; -#endif -#else - (void)__Pyx_PyObject_CallMethod0; -#endif - r = PyType_Ready(t); -#if PY_VERSION_HEX >= 0x03050000 && !defined(PYSTON_MAJOR_VERSION) - t->tp_flags &= ~Py_TPFLAGS_HEAPTYPE; - #if PY_VERSION_HEX >= 0x030A00b1 - if (gc_was_enabled) - PyGC_Enable(); - #else - if (gc_was_enabled) { - PyObject *tp, *v, *tb; - PyErr_Fetch(&tp, &v, &tb); - ret = __Pyx_PyObject_CallMethod0(gc, __pyx_kp_u_enable); - if (likely(ret || r == -1)) { - Py_XDECREF(ret); - PyErr_Restore(tp, v, tb); - } else { - Py_XDECREF(tp); - Py_XDECREF(v); - Py_XDECREF(tb); - r = -1; - } - } - Py_DECREF(gc); - #endif - } -#endif - return r; -#endif -} - -/* PyObject_GenericGetAttrNoDict */ -#if CYTHON_USE_TYPE_SLOTS && CYTHON_USE_PYTYPE_LOOKUP && PY_VERSION_HEX < 0x03070000 -static PyObject *__Pyx_RaiseGenericGetAttributeError(PyTypeObject *tp, PyObject *attr_name) { - __Pyx_TypeName type_name = __Pyx_PyType_GetName(tp); - PyErr_Format(PyExc_AttributeError, -#if PY_MAJOR_VERSION >= 3 - "'" __Pyx_FMT_TYPENAME "' object has no attribute '%U'", - type_name, attr_name); -#else - "'" __Pyx_FMT_TYPENAME "' object has no attribute '%.400s'", - type_name, PyString_AS_STRING(attr_name)); -#endif - __Pyx_DECREF_TypeName(type_name); - return NULL; -} -static CYTHON_INLINE PyObject* __Pyx_PyObject_GenericGetAttrNoDict(PyObject* obj, PyObject* attr_name) { - PyObject *descr; - PyTypeObject *tp = Py_TYPE(obj); - if (unlikely(!PyString_Check(attr_name))) { - return PyObject_GenericGetAttr(obj, attr_name); - } - assert(!tp->tp_dictoffset); - descr = _PyType_Lookup(tp, attr_name); - if (unlikely(!descr)) { - return __Pyx_RaiseGenericGetAttributeError(tp, attr_name); - } - Py_INCREF(descr); - #if PY_MAJOR_VERSION < 3 - if (likely(PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_HAVE_CLASS))) - #endif - { - descrgetfunc f = Py_TYPE(descr)->tp_descr_get; - if (unlikely(f)) { - PyObject *res = f(descr, obj, (PyObject *)tp); - Py_DECREF(descr); - return res; - } - } - return descr; -} -#endif - -/* FastTypeChecks */ -#if CYTHON_COMPILING_IN_CPYTHON -static int __Pyx_InBases(PyTypeObject *a, PyTypeObject *b) { - while (a) { - a = __Pyx_PyType_GetSlot(a, tp_base, PyTypeObject*); - if (a == b) - return 1; - } - return b == &PyBaseObject_Type; -} -static CYTHON_INLINE int __Pyx_IsSubtype(PyTypeObject *a, PyTypeObject *b) { - PyObject *mro; - if (a == b) return 1; - mro = a->tp_mro; - if (likely(mro)) { - Py_ssize_t i, n; - n = PyTuple_GET_SIZE(mro); - for (i = 0; i < n; i++) { - if (PyTuple_GET_ITEM(mro, i) == (PyObject *)b) - return 1; - } - return 0; - } - return __Pyx_InBases(a, b); -} -static CYTHON_INLINE int __Pyx_IsAnySubtype2(PyTypeObject *cls, PyTypeObject *a, PyTypeObject *b) { - PyObject *mro; - if (cls == a || cls == b) return 1; - mro = cls->tp_mro; - if (likely(mro)) { - Py_ssize_t i, n; - n = PyTuple_GET_SIZE(mro); - for (i = 0; i < n; i++) { - PyObject *base = PyTuple_GET_ITEM(mro, i); - if (base == (PyObject *)a || base == (PyObject *)b) - return 1; - } - return 0; - } - return __Pyx_InBases(cls, a) || __Pyx_InBases(cls, b); -} -#if PY_MAJOR_VERSION == 2 -static int __Pyx_inner_PyErr_GivenExceptionMatches2(PyObject *err, PyObject* exc_type1, PyObject* exc_type2) { - PyObject *exception, *value, *tb; - int res; - __Pyx_PyThreadState_declare - __Pyx_PyThreadState_assign - __Pyx_ErrFetch(&exception, &value, &tb); - res = exc_type1 ? PyObject_IsSubclass(err, exc_type1) : 0; - if (unlikely(res == -1)) { - PyErr_WriteUnraisable(err); - res = 0; - } - if (!res) { - res = PyObject_IsSubclass(err, exc_type2); - if (unlikely(res == -1)) { - PyErr_WriteUnraisable(err); - res = 0; - } - } - __Pyx_ErrRestore(exception, value, tb); - return res; -} -#else -static CYTHON_INLINE int __Pyx_inner_PyErr_GivenExceptionMatches2(PyObject *err, PyObject* exc_type1, PyObject *exc_type2) { - if (exc_type1) { - return __Pyx_IsAnySubtype2((PyTypeObject*)err, (PyTypeObject*)exc_type1, (PyTypeObject*)exc_type2); - } else { - return __Pyx_IsSubtype((PyTypeObject*)err, (PyTypeObject*)exc_type2); - } -} -#endif -static int __Pyx_PyErr_GivenExceptionMatchesTuple(PyObject *exc_type, PyObject *tuple) { - Py_ssize_t i, n; - assert(PyExceptionClass_Check(exc_type)); - n = PyTuple_GET_SIZE(tuple); -#if PY_MAJOR_VERSION >= 3 - for (i=0; i= 3 - if (level == -1) { - if (strchr(__Pyx_MODULE_NAME, '.') != NULL) { - module = PyImport_ImportModuleLevelObject( - name, __pyx_d, empty_dict, from_list, 1); - if (unlikely(!module)) { - if (unlikely(!PyErr_ExceptionMatches(PyExc_ImportError))) - goto bad; - PyErr_Clear(); - } - } - level = 0; - } - #endif - if (!module) { - #if PY_MAJOR_VERSION < 3 - PyObject *py_level = PyInt_FromLong(level); - if (unlikely(!py_level)) - goto bad; - module = PyObject_CallFunctionObjArgs(py_import, - name, __pyx_d, empty_dict, from_list, py_level, (PyObject *)NULL); - Py_DECREF(py_level); - #else - module = PyImport_ImportModuleLevelObject( - name, __pyx_d, empty_dict, from_list, level); - #endif - } - } -bad: - Py_XDECREF(empty_dict); - Py_XDECREF(empty_list); - #if PY_MAJOR_VERSION < 3 - Py_XDECREF(py_import); - #endif - return module; -} - -/* ImportFrom */ -static PyObject* __Pyx_ImportFrom(PyObject* module, PyObject* name) { - PyObject* value = __Pyx_PyObject_GetAttrStr(module, name); - if (unlikely(!value) && PyErr_ExceptionMatches(PyExc_AttributeError)) { - const char* module_name_str = 0; - PyObject* module_name = 0; - PyObject* module_dot = 0; - PyObject* full_name = 0; - PyErr_Clear(); - module_name_str = PyModule_GetName(module); - if (unlikely(!module_name_str)) { goto modbad; } - module_name = PyUnicode_FromString(module_name_str); - if (unlikely(!module_name)) { goto modbad; } - module_dot = PyUnicode_Concat(module_name, __pyx_kp_u__2); - if (unlikely(!module_dot)) { goto modbad; } - full_name = PyUnicode_Concat(module_dot, name); - if (unlikely(!full_name)) { goto modbad; } - #if PY_VERSION_HEX < 0x030700A1 || (CYTHON_COMPILING_IN_PYPY && PYPY_VERSION_NUM < 0x07030400) - { - PyObject *modules = PyImport_GetModuleDict(); - if (unlikely(!modules)) - goto modbad; - value = PyObject_GetItem(modules, full_name); - } - #else - value = PyImport_GetModule(full_name); - #endif - modbad: - Py_XDECREF(full_name); - Py_XDECREF(module_dot); - Py_XDECREF(module_name); - } - if (unlikely(!value)) { - PyErr_Format(PyExc_ImportError, - #if PY_MAJOR_VERSION < 3 - "cannot import name %.230s", PyString_AS_STRING(name)); - #else - "cannot import name %S", name); - #endif - } - return value; -} - -/* ImportDottedModule */ -#if PY_MAJOR_VERSION >= 3 -static PyObject *__Pyx__ImportDottedModule_Error(PyObject *name, PyObject *parts_tuple, Py_ssize_t count) { - PyObject *partial_name = NULL, *slice = NULL, *sep = NULL; - if (unlikely(PyErr_Occurred())) { - PyErr_Clear(); - } - if (likely(PyTuple_GET_SIZE(parts_tuple) == count)) { - partial_name = name; - } else { - slice = PySequence_GetSlice(parts_tuple, 0, count); - if (unlikely(!slice)) - goto bad; - sep = PyUnicode_FromStringAndSize(".", 1); - if (unlikely(!sep)) - goto bad; - partial_name = PyUnicode_Join(sep, slice); - } - PyErr_Format( -#if PY_MAJOR_VERSION < 3 - PyExc_ImportError, - "No module named '%s'", PyString_AS_STRING(partial_name)); -#else -#if PY_VERSION_HEX >= 0x030600B1 - PyExc_ModuleNotFoundError, -#else - PyExc_ImportError, -#endif - "No module named '%U'", partial_name); -#endif -bad: - Py_XDECREF(sep); - Py_XDECREF(slice); - Py_XDECREF(partial_name); - return NULL; -} -#endif -#if PY_MAJOR_VERSION >= 3 -static PyObject *__Pyx__ImportDottedModule_Lookup(PyObject *name) { - PyObject *imported_module; -#if PY_VERSION_HEX < 0x030700A1 || (CYTHON_COMPILING_IN_PYPY && PYPY_VERSION_NUM < 0x07030400) - PyObject *modules = PyImport_GetModuleDict(); - if (unlikely(!modules)) - return NULL; - imported_module = __Pyx_PyDict_GetItemStr(modules, name); - Py_XINCREF(imported_module); -#else - imported_module = PyImport_GetModule(name); -#endif - return imported_module; -} -#endif -#if PY_MAJOR_VERSION >= 3 -static PyObject *__Pyx_ImportDottedModule_WalkParts(PyObject *module, PyObject *name, PyObject *parts_tuple) { - Py_ssize_t i, nparts; - nparts = PyTuple_GET_SIZE(parts_tuple); - for (i=1; i < nparts && module; i++) { - PyObject *part, *submodule; -#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - part = PyTuple_GET_ITEM(parts_tuple, i); -#else - part = PySequence_ITEM(parts_tuple, i); -#endif - submodule = __Pyx_PyObject_GetAttrStrNoError(module, part); -#if !(CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS) - Py_DECREF(part); -#endif - Py_DECREF(module); - module = submodule; - } - if (unlikely(!module)) { - return __Pyx__ImportDottedModule_Error(name, parts_tuple, i); - } - return module; -} -#endif -static PyObject *__Pyx__ImportDottedModule(PyObject *name, PyObject *parts_tuple) { -#if PY_MAJOR_VERSION < 3 - PyObject *module, *from_list, *star = __pyx_n_s__3; - CYTHON_UNUSED_VAR(parts_tuple); - from_list = PyList_New(1); - if (unlikely(!from_list)) - return NULL; - Py_INCREF(star); - PyList_SET_ITEM(from_list, 0, star); - module = __Pyx_Import(name, from_list, 0); - Py_DECREF(from_list); - return module; -#else - PyObject *imported_module; - PyObject *module = __Pyx_Import(name, NULL, 0); - if (!parts_tuple || unlikely(!module)) - return module; - imported_module = __Pyx__ImportDottedModule_Lookup(name); - if (likely(imported_module)) { - Py_DECREF(module); - return imported_module; - } - PyErr_Clear(); - return __Pyx_ImportDottedModule_WalkParts(module, name, parts_tuple); -#endif -} -static PyObject *__Pyx_ImportDottedModule(PyObject *name, PyObject *parts_tuple) { -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x030400B1 - PyObject *module = __Pyx__ImportDottedModule_Lookup(name); - if (likely(module)) { - PyObject *spec = __Pyx_PyObject_GetAttrStrNoError(module, __pyx_n_s_spec); - if (likely(spec)) { - PyObject *unsafe = __Pyx_PyObject_GetAttrStrNoError(spec, __pyx_n_s_initializing); - if (likely(!unsafe || !__Pyx_PyObject_IsTrue(unsafe))) { - Py_DECREF(spec); - spec = NULL; - } - Py_XDECREF(unsafe); - } - if (likely(!spec)) { - PyErr_Clear(); - return module; - } - Py_DECREF(spec); - Py_DECREF(module); - } else if (PyErr_Occurred()) { - PyErr_Clear(); - } -#endif - return __Pyx__ImportDottedModule(name, parts_tuple); -} - -/* pybytes_as_double */ -static double __Pyx_SlowPyString_AsDouble(PyObject *obj) { - PyObject *float_value; -#if PY_MAJOR_VERSION >= 3 - float_value = PyFloat_FromString(obj); -#else - float_value = PyFloat_FromString(obj, 0); -#endif - if (likely(float_value)) { -#if CYTHON_ASSUME_SAFE_MACROS - double value = PyFloat_AS_DOUBLE(float_value); -#else - double value = PyFloat_AsDouble(float_value); -#endif - Py_DECREF(float_value); - return value; - } - return (double)-1; -} -static const char* __Pyx__PyBytes_AsDouble_Copy(const char* start, char* buffer, Py_ssize_t length) { - int last_was_punctuation = 1; - Py_ssize_t i; - for (i=0; i < length; i++) { - char chr = start[i]; - int is_punctuation = (chr == '_') | (chr == '.') | (chr == 'e') | (chr == 'E'); - *buffer = chr; - buffer += (chr != '_'); - if (unlikely(last_was_punctuation & is_punctuation)) goto parse_failure; - last_was_punctuation = is_punctuation; - } - if (unlikely(last_was_punctuation)) goto parse_failure; - *buffer = '\0'; - return buffer; -parse_failure: - return NULL; -} -static double __Pyx__PyBytes_AsDouble_inf_nan(const char* start, Py_ssize_t length) { - int matches = 1; - char sign = start[0]; - int is_signed = (sign == '+') | (sign == '-'); - start += is_signed; - length -= is_signed; - switch (start[0]) { - #ifdef Py_NAN - case 'n': - case 'N': - if (unlikely(length != 3)) goto parse_failure; - matches &= (start[1] == 'a' || start[1] == 'A'); - matches &= (start[2] == 'n' || start[2] == 'N'); - if (unlikely(!matches)) goto parse_failure; - return (sign == '-') ? -Py_NAN : Py_NAN; - #endif - case 'i': - case 'I': - if (unlikely(length < 3)) goto parse_failure; - matches &= (start[1] == 'n' || start[1] == 'N'); - matches &= (start[2] == 'f' || start[2] == 'F'); - if (likely(length == 3 && matches)) - return (sign == '-') ? -Py_HUGE_VAL : Py_HUGE_VAL; - if (unlikely(length != 8)) goto parse_failure; - matches &= (start[3] == 'i' || start[3] == 'I'); - matches &= (start[4] == 'n' || start[4] == 'N'); - matches &= (start[5] == 'i' || start[5] == 'I'); - matches &= (start[6] == 't' || start[6] == 'T'); - matches &= (start[7] == 'y' || start[7] == 'Y'); - if (unlikely(!matches)) goto parse_failure; - return (sign == '-') ? -Py_HUGE_VAL : Py_HUGE_VAL; - case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - break; - default: - goto parse_failure; - } - return 0.0; -parse_failure: - return -1.0; -} -static CYTHON_INLINE int __Pyx__PyBytes_AsDouble_IsSpace(char ch) { - return (ch == 0x20) | !((ch < 0x9) | (ch > 0xd)); -} -CYTHON_UNUSED static double __Pyx__PyBytes_AsDouble(PyObject *obj, const char* start, Py_ssize_t length) { - double value; - Py_ssize_t i, digits; - const char *last = start + length; - char *end; - while (__Pyx__PyBytes_AsDouble_IsSpace(*start)) - start++; - while (start < last - 1 && __Pyx__PyBytes_AsDouble_IsSpace(last[-1])) - last--; - length = last - start; - if (unlikely(length <= 0)) goto fallback; - value = __Pyx__PyBytes_AsDouble_inf_nan(start, length); - if (unlikely(value == -1.0)) goto fallback; - if (value != 0.0) return value; - digits = 0; - for (i=0; i < length; digits += start[i++] != '_'); - if (likely(digits == length)) { - value = PyOS_string_to_double(start, &end, NULL); - } else if (digits < 40) { - char number[40]; - last = __Pyx__PyBytes_AsDouble_Copy(start, number, length); - if (unlikely(!last)) goto fallback; - value = PyOS_string_to_double(number, &end, NULL); - } else { - char *number = (char*) PyMem_Malloc((digits + 1) * sizeof(char)); - if (unlikely(!number)) goto fallback; - last = __Pyx__PyBytes_AsDouble_Copy(start, number, length); - if (unlikely(!last)) { - PyMem_Free(number); - goto fallback; - } - value = PyOS_string_to_double(number, &end, NULL); - PyMem_Free(number); - } - if (likely(end == last) || (value == (double)-1 && PyErr_Occurred())) { - return value; - } -fallback: - return __Pyx_SlowPyString_AsDouble(obj); -} - -/* FetchSharedCythonModule */ -static PyObject *__Pyx_FetchSharedCythonABIModule(void) { - return __Pyx_PyImport_AddModuleRef((char*) __PYX_ABI_MODULE_NAME); -} - -/* FetchCommonType */ -static int __Pyx_VerifyCachedType(PyObject *cached_type, - const char *name, - Py_ssize_t basicsize, - Py_ssize_t expected_basicsize) { - if (!PyType_Check(cached_type)) { - PyErr_Format(PyExc_TypeError, - "Shared Cython type %.200s is not a type object", name); - return -1; - } - if (basicsize != expected_basicsize) { - PyErr_Format(PyExc_TypeError, - "Shared Cython type %.200s has the wrong size, try recompiling", - name); - return -1; - } - return 0; -} -#if !CYTHON_USE_TYPE_SPECS -static PyTypeObject* __Pyx_FetchCommonType(PyTypeObject* type) { - PyObject* abi_module; - const char* object_name; - PyTypeObject *cached_type = NULL; - abi_module = __Pyx_FetchSharedCythonABIModule(); - if (!abi_module) return NULL; - object_name = strrchr(type->tp_name, '.'); - object_name = object_name ? object_name+1 : type->tp_name; - cached_type = (PyTypeObject*) PyObject_GetAttrString(abi_module, object_name); - if (cached_type) { - if (__Pyx_VerifyCachedType( - (PyObject *)cached_type, - object_name, - cached_type->tp_basicsize, - type->tp_basicsize) < 0) { - goto bad; - } - goto done; - } - if (!PyErr_ExceptionMatches(PyExc_AttributeError)) goto bad; - PyErr_Clear(); - if (PyType_Ready(type) < 0) goto bad; - if (PyObject_SetAttrString(abi_module, object_name, (PyObject *)type) < 0) - goto bad; - Py_INCREF(type); - cached_type = type; -done: - Py_DECREF(abi_module); - return cached_type; -bad: - Py_XDECREF(cached_type); - cached_type = NULL; - goto done; -} -#else -static PyTypeObject *__Pyx_FetchCommonTypeFromSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) { - PyObject *abi_module, *cached_type = NULL; - const char* object_name = strrchr(spec->name, '.'); - object_name = object_name ? object_name+1 : spec->name; - abi_module = __Pyx_FetchSharedCythonABIModule(); - if (!abi_module) return NULL; - cached_type = PyObject_GetAttrString(abi_module, object_name); - if (cached_type) { - Py_ssize_t basicsize; -#if CYTHON_COMPILING_IN_LIMITED_API - PyObject *py_basicsize; - py_basicsize = PyObject_GetAttrString(cached_type, "__basicsize__"); - if (unlikely(!py_basicsize)) goto bad; - basicsize = PyLong_AsSsize_t(py_basicsize); - Py_DECREF(py_basicsize); - py_basicsize = 0; - if (unlikely(basicsize == (Py_ssize_t)-1) && PyErr_Occurred()) goto bad; -#else - basicsize = likely(PyType_Check(cached_type)) ? ((PyTypeObject*) cached_type)->tp_basicsize : -1; -#endif - if (__Pyx_VerifyCachedType( - cached_type, - object_name, - basicsize, - spec->basicsize) < 0) { - goto bad; - } - goto done; - } - if (!PyErr_ExceptionMatches(PyExc_AttributeError)) goto bad; - PyErr_Clear(); - CYTHON_UNUSED_VAR(module); - cached_type = __Pyx_PyType_FromModuleAndSpec(abi_module, spec, bases); - if (unlikely(!cached_type)) goto bad; - if (unlikely(__Pyx_fix_up_extension_type_from_spec(spec, (PyTypeObject *) cached_type) < 0)) goto bad; - if (PyObject_SetAttrString(abi_module, object_name, cached_type) < 0) goto bad; -done: - Py_DECREF(abi_module); - assert(cached_type == NULL || PyType_Check(cached_type)); - return (PyTypeObject *) cached_type; -bad: - Py_XDECREF(cached_type); - cached_type = NULL; - goto done; -} -#endif - -/* PyVectorcallFastCallDict */ -#if CYTHON_METH_FASTCALL -static PyObject *__Pyx_PyVectorcall_FastCallDict_kw(PyObject *func, __pyx_vectorcallfunc vc, PyObject *const *args, size_t nargs, PyObject *kw) -{ - PyObject *res = NULL; - PyObject *kwnames; - PyObject **newargs; - PyObject **kwvalues; - Py_ssize_t i, pos; - size_t j; - PyObject *key, *value; - unsigned long keys_are_strings; - Py_ssize_t nkw = PyDict_GET_SIZE(kw); - newargs = (PyObject **)PyMem_Malloc((nargs + (size_t)nkw) * sizeof(args[0])); - if (unlikely(newargs == NULL)) { - PyErr_NoMemory(); - return NULL; - } - for (j = 0; j < nargs; j++) newargs[j] = args[j]; - kwnames = PyTuple_New(nkw); - if (unlikely(kwnames == NULL)) { - PyMem_Free(newargs); - return NULL; - } - kwvalues = newargs + nargs; - pos = i = 0; - keys_are_strings = Py_TPFLAGS_UNICODE_SUBCLASS; - while (PyDict_Next(kw, &pos, &key, &value)) { - keys_are_strings &= Py_TYPE(key)->tp_flags; - Py_INCREF(key); - Py_INCREF(value); - PyTuple_SET_ITEM(kwnames, i, key); - kwvalues[i] = value; - i++; - } - if (unlikely(!keys_are_strings)) { - PyErr_SetString(PyExc_TypeError, "keywords must be strings"); - goto cleanup; - } - res = vc(func, newargs, nargs, kwnames); -cleanup: - Py_DECREF(kwnames); - for (i = 0; i < nkw; i++) - Py_DECREF(kwvalues[i]); - PyMem_Free(newargs); - return res; -} -static CYTHON_INLINE PyObject *__Pyx_PyVectorcall_FastCallDict(PyObject *func, __pyx_vectorcallfunc vc, PyObject *const *args, size_t nargs, PyObject *kw) -{ - if (likely(kw == NULL) || PyDict_GET_SIZE(kw) == 0) { - return vc(func, args, nargs, NULL); - } - return __Pyx_PyVectorcall_FastCallDict_kw(func, vc, args, nargs, kw); -} -#endif - -/* CythonFunctionShared */ -#if CYTHON_COMPILING_IN_LIMITED_API -static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void *cfunc) { - if (__Pyx_CyFunction_Check(func)) { - return PyCFunction_GetFunction(((__pyx_CyFunctionObject*)func)->func) == (PyCFunction) cfunc; - } else if (PyCFunction_Check(func)) { - return PyCFunction_GetFunction(func) == (PyCFunction) cfunc; - } - return 0; -} -#else -static CYTHON_INLINE int __Pyx__IsSameCyOrCFunction(PyObject *func, void *cfunc) { - return __Pyx_CyOrPyCFunction_Check(func) && __Pyx_CyOrPyCFunction_GET_FUNCTION(func) == (PyCFunction) cfunc; -} -#endif -static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* f, PyObject* classobj) { -#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API - __Pyx_Py_XDECREF_SET( - __Pyx_CyFunction_GetClassObj(f), - ((classobj) ? __Pyx_NewRef(classobj) : NULL)); -#else - __Pyx_Py_XDECREF_SET( - ((PyCMethodObject *) (f))->mm_class, - (PyTypeObject*)((classobj) ? __Pyx_NewRef(classobj) : NULL)); -#endif -} -static PyObject * -__Pyx_CyFunction_get_doc(__pyx_CyFunctionObject *op, void *closure) -{ - CYTHON_UNUSED_VAR(closure); - if (unlikely(op->func_doc == NULL)) { -#if CYTHON_COMPILING_IN_LIMITED_API - op->func_doc = PyObject_GetAttrString(op->func, "__doc__"); - if (unlikely(!op->func_doc)) return NULL; -#else - if (((PyCFunctionObject*)op)->m_ml->ml_doc) { -#if PY_MAJOR_VERSION >= 3 - op->func_doc = PyUnicode_FromString(((PyCFunctionObject*)op)->m_ml->ml_doc); -#else - op->func_doc = PyString_FromString(((PyCFunctionObject*)op)->m_ml->ml_doc); -#endif - if (unlikely(op->func_doc == NULL)) - return NULL; - } else { - Py_INCREF(Py_None); - return Py_None; - } -#endif - } - Py_INCREF(op->func_doc); - return op->func_doc; -} -static int -__Pyx_CyFunction_set_doc(__pyx_CyFunctionObject *op, PyObject *value, void *context) -{ - CYTHON_UNUSED_VAR(context); - if (value == NULL) { - value = Py_None; - } - Py_INCREF(value); - __Pyx_Py_XDECREF_SET(op->func_doc, value); - return 0; -} -static PyObject * -__Pyx_CyFunction_get_name(__pyx_CyFunctionObject *op, void *context) -{ - CYTHON_UNUSED_VAR(context); - if (unlikely(op->func_name == NULL)) { -#if CYTHON_COMPILING_IN_LIMITED_API - op->func_name = PyObject_GetAttrString(op->func, "__name__"); -#elif PY_MAJOR_VERSION >= 3 - op->func_name = PyUnicode_InternFromString(((PyCFunctionObject*)op)->m_ml->ml_name); -#else - op->func_name = PyString_InternFromString(((PyCFunctionObject*)op)->m_ml->ml_name); -#endif - if (unlikely(op->func_name == NULL)) - return NULL; - } - Py_INCREF(op->func_name); - return op->func_name; -} -static int -__Pyx_CyFunction_set_name(__pyx_CyFunctionObject *op, PyObject *value, void *context) -{ - CYTHON_UNUSED_VAR(context); -#if PY_MAJOR_VERSION >= 3 - if (unlikely(value == NULL || !PyUnicode_Check(value))) -#else - if (unlikely(value == NULL || !PyString_Check(value))) -#endif - { - PyErr_SetString(PyExc_TypeError, - "__name__ must be set to a string object"); - return -1; - } - Py_INCREF(value); - __Pyx_Py_XDECREF_SET(op->func_name, value); - return 0; -} -static PyObject * -__Pyx_CyFunction_get_qualname(__pyx_CyFunctionObject *op, void *context) -{ - CYTHON_UNUSED_VAR(context); - Py_INCREF(op->func_qualname); - return op->func_qualname; -} -static int -__Pyx_CyFunction_set_qualname(__pyx_CyFunctionObject *op, PyObject *value, void *context) -{ - CYTHON_UNUSED_VAR(context); -#if PY_MAJOR_VERSION >= 3 - if (unlikely(value == NULL || !PyUnicode_Check(value))) -#else - if (unlikely(value == NULL || !PyString_Check(value))) -#endif - { - PyErr_SetString(PyExc_TypeError, - "__qualname__ must be set to a string object"); - return -1; - } - Py_INCREF(value); - __Pyx_Py_XDECREF_SET(op->func_qualname, value); - return 0; -} -static PyObject * -__Pyx_CyFunction_get_dict(__pyx_CyFunctionObject *op, void *context) -{ - CYTHON_UNUSED_VAR(context); - if (unlikely(op->func_dict == NULL)) { - op->func_dict = PyDict_New(); - if (unlikely(op->func_dict == NULL)) - return NULL; - } - Py_INCREF(op->func_dict); - return op->func_dict; -} -static int -__Pyx_CyFunction_set_dict(__pyx_CyFunctionObject *op, PyObject *value, void *context) -{ - CYTHON_UNUSED_VAR(context); - if (unlikely(value == NULL)) { - PyErr_SetString(PyExc_TypeError, - "function's dictionary may not be deleted"); - return -1; - } - if (unlikely(!PyDict_Check(value))) { - PyErr_SetString(PyExc_TypeError, - "setting function's dictionary to a non-dict"); - return -1; - } - Py_INCREF(value); - __Pyx_Py_XDECREF_SET(op->func_dict, value); - return 0; -} -static PyObject * -__Pyx_CyFunction_get_globals(__pyx_CyFunctionObject *op, void *context) -{ - CYTHON_UNUSED_VAR(context); - Py_INCREF(op->func_globals); - return op->func_globals; -} -static PyObject * -__Pyx_CyFunction_get_closure(__pyx_CyFunctionObject *op, void *context) -{ - CYTHON_UNUSED_VAR(op); - CYTHON_UNUSED_VAR(context); - Py_INCREF(Py_None); - return Py_None; -} -static PyObject * -__Pyx_CyFunction_get_code(__pyx_CyFunctionObject *op, void *context) -{ - PyObject* result = (op->func_code) ? op->func_code : Py_None; - CYTHON_UNUSED_VAR(context); - Py_INCREF(result); - return result; -} -static int -__Pyx_CyFunction_init_defaults(__pyx_CyFunctionObject *op) { - int result = 0; - PyObject *res = op->defaults_getter((PyObject *) op); - if (unlikely(!res)) - return -1; - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - op->defaults_tuple = PyTuple_GET_ITEM(res, 0); - Py_INCREF(op->defaults_tuple); - op->defaults_kwdict = PyTuple_GET_ITEM(res, 1); - Py_INCREF(op->defaults_kwdict); - #else - op->defaults_tuple = __Pyx_PySequence_ITEM(res, 0); - if (unlikely(!op->defaults_tuple)) result = -1; - else { - op->defaults_kwdict = __Pyx_PySequence_ITEM(res, 1); - if (unlikely(!op->defaults_kwdict)) result = -1; - } - #endif - Py_DECREF(res); - return result; -} -static int -__Pyx_CyFunction_set_defaults(__pyx_CyFunctionObject *op, PyObject* value, void *context) { - CYTHON_UNUSED_VAR(context); - if (!value) { - value = Py_None; - } else if (unlikely(value != Py_None && !PyTuple_Check(value))) { - PyErr_SetString(PyExc_TypeError, - "__defaults__ must be set to a tuple object"); - return -1; - } - PyErr_WarnEx(PyExc_RuntimeWarning, "changes to cyfunction.__defaults__ will not " - "currently affect the values used in function calls", 1); - Py_INCREF(value); - __Pyx_Py_XDECREF_SET(op->defaults_tuple, value); - return 0; -} -static PyObject * -__Pyx_CyFunction_get_defaults(__pyx_CyFunctionObject *op, void *context) { - PyObject* result = op->defaults_tuple; - CYTHON_UNUSED_VAR(context); - if (unlikely(!result)) { - if (op->defaults_getter) { - if (unlikely(__Pyx_CyFunction_init_defaults(op) < 0)) return NULL; - result = op->defaults_tuple; - } else { - result = Py_None; - } - } - Py_INCREF(result); - return result; -} -static int -__Pyx_CyFunction_set_kwdefaults(__pyx_CyFunctionObject *op, PyObject* value, void *context) { - CYTHON_UNUSED_VAR(context); - if (!value) { - value = Py_None; - } else if (unlikely(value != Py_None && !PyDict_Check(value))) { - PyErr_SetString(PyExc_TypeError, - "__kwdefaults__ must be set to a dict object"); - return -1; - } - PyErr_WarnEx(PyExc_RuntimeWarning, "changes to cyfunction.__kwdefaults__ will not " - "currently affect the values used in function calls", 1); - Py_INCREF(value); - __Pyx_Py_XDECREF_SET(op->defaults_kwdict, value); - return 0; -} -static PyObject * -__Pyx_CyFunction_get_kwdefaults(__pyx_CyFunctionObject *op, void *context) { - PyObject* result = op->defaults_kwdict; - CYTHON_UNUSED_VAR(context); - if (unlikely(!result)) { - if (op->defaults_getter) { - if (unlikely(__Pyx_CyFunction_init_defaults(op) < 0)) return NULL; - result = op->defaults_kwdict; - } else { - result = Py_None; - } - } - Py_INCREF(result); - return result; -} -static int -__Pyx_CyFunction_set_annotations(__pyx_CyFunctionObject *op, PyObject* value, void *context) { - CYTHON_UNUSED_VAR(context); - if (!value || value == Py_None) { - value = NULL; - } else if (unlikely(!PyDict_Check(value))) { - PyErr_SetString(PyExc_TypeError, - "__annotations__ must be set to a dict object"); - return -1; - } - Py_XINCREF(value); - __Pyx_Py_XDECREF_SET(op->func_annotations, value); - return 0; -} -static PyObject * -__Pyx_CyFunction_get_annotations(__pyx_CyFunctionObject *op, void *context) { - PyObject* result = op->func_annotations; - CYTHON_UNUSED_VAR(context); - if (unlikely(!result)) { - result = PyDict_New(); - if (unlikely(!result)) return NULL; - op->func_annotations = result; - } - Py_INCREF(result); - return result; -} -static PyObject * -__Pyx_CyFunction_get_is_coroutine(__pyx_CyFunctionObject *op, void *context) { - int is_coroutine; - CYTHON_UNUSED_VAR(context); - if (op->func_is_coroutine) { - return __Pyx_NewRef(op->func_is_coroutine); - } - is_coroutine = op->flags & __Pyx_CYFUNCTION_COROUTINE; -#if PY_VERSION_HEX >= 0x03050000 - if (is_coroutine) { - PyObject *module, *fromlist, *marker = __pyx_n_s_is_coroutine; - fromlist = PyList_New(1); - if (unlikely(!fromlist)) return NULL; - Py_INCREF(marker); -#if CYTHON_ASSUME_SAFE_MACROS - PyList_SET_ITEM(fromlist, 0, marker); -#else - if (unlikely(PyList_SetItem(fromlist, 0, marker) < 0)) { - Py_DECREF(marker); - Py_DECREF(fromlist); - return NULL; - } -#endif - module = PyImport_ImportModuleLevelObject(__pyx_n_s_asyncio_coroutines, NULL, NULL, fromlist, 0); - Py_DECREF(fromlist); - if (unlikely(!module)) goto ignore; - op->func_is_coroutine = __Pyx_PyObject_GetAttrStr(module, marker); - Py_DECREF(module); - if (likely(op->func_is_coroutine)) { - return __Pyx_NewRef(op->func_is_coroutine); - } -ignore: - PyErr_Clear(); - } -#endif - op->func_is_coroutine = __Pyx_PyBool_FromLong(is_coroutine); - return __Pyx_NewRef(op->func_is_coroutine); -} -#if CYTHON_COMPILING_IN_LIMITED_API -static PyObject * -__Pyx_CyFunction_get_module(__pyx_CyFunctionObject *op, void *context) { - CYTHON_UNUSED_VAR(context); - return PyObject_GetAttrString(op->func, "__module__"); -} -static int -__Pyx_CyFunction_set_module(__pyx_CyFunctionObject *op, PyObject* value, void *context) { - CYTHON_UNUSED_VAR(context); - return PyObject_SetAttrString(op->func, "__module__", value); -} -#endif -static PyGetSetDef __pyx_CyFunction_getsets[] = { - {(char *) "func_doc", (getter)__Pyx_CyFunction_get_doc, (setter)__Pyx_CyFunction_set_doc, 0, 0}, - {(char *) "__doc__", (getter)__Pyx_CyFunction_get_doc, (setter)__Pyx_CyFunction_set_doc, 0, 0}, - {(char *) "func_name", (getter)__Pyx_CyFunction_get_name, (setter)__Pyx_CyFunction_set_name, 0, 0}, - {(char *) "__name__", (getter)__Pyx_CyFunction_get_name, (setter)__Pyx_CyFunction_set_name, 0, 0}, - {(char *) "__qualname__", (getter)__Pyx_CyFunction_get_qualname, (setter)__Pyx_CyFunction_set_qualname, 0, 0}, - {(char *) "func_dict", (getter)__Pyx_CyFunction_get_dict, (setter)__Pyx_CyFunction_set_dict, 0, 0}, - {(char *) "__dict__", (getter)__Pyx_CyFunction_get_dict, (setter)__Pyx_CyFunction_set_dict, 0, 0}, - {(char *) "func_globals", (getter)__Pyx_CyFunction_get_globals, 0, 0, 0}, - {(char *) "__globals__", (getter)__Pyx_CyFunction_get_globals, 0, 0, 0}, - {(char *) "func_closure", (getter)__Pyx_CyFunction_get_closure, 0, 0, 0}, - {(char *) "__closure__", (getter)__Pyx_CyFunction_get_closure, 0, 0, 0}, - {(char *) "func_code", (getter)__Pyx_CyFunction_get_code, 0, 0, 0}, - {(char *) "__code__", (getter)__Pyx_CyFunction_get_code, 0, 0, 0}, - {(char *) "func_defaults", (getter)__Pyx_CyFunction_get_defaults, (setter)__Pyx_CyFunction_set_defaults, 0, 0}, - {(char *) "__defaults__", (getter)__Pyx_CyFunction_get_defaults, (setter)__Pyx_CyFunction_set_defaults, 0, 0}, - {(char *) "__kwdefaults__", (getter)__Pyx_CyFunction_get_kwdefaults, (setter)__Pyx_CyFunction_set_kwdefaults, 0, 0}, - {(char *) "__annotations__", (getter)__Pyx_CyFunction_get_annotations, (setter)__Pyx_CyFunction_set_annotations, 0, 0}, - {(char *) "_is_coroutine", (getter)__Pyx_CyFunction_get_is_coroutine, 0, 0, 0}, -#if CYTHON_COMPILING_IN_LIMITED_API - {"__module__", (getter)__Pyx_CyFunction_get_module, (setter)__Pyx_CyFunction_set_module, 0, 0}, -#endif - {0, 0, 0, 0, 0} -}; -static PyMemberDef __pyx_CyFunction_members[] = { -#if !CYTHON_COMPILING_IN_LIMITED_API - {(char *) "__module__", T_OBJECT, offsetof(PyCFunctionObject, m_module), 0, 0}, -#endif -#if CYTHON_USE_TYPE_SPECS - {(char *) "__dictoffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_dict), READONLY, 0}, -#if CYTHON_METH_FASTCALL -#if CYTHON_BACKPORT_VECTORCALL - {(char *) "__vectorcalloffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_vectorcall), READONLY, 0}, -#else -#if !CYTHON_COMPILING_IN_LIMITED_API - {(char *) "__vectorcalloffset__", T_PYSSIZET, offsetof(PyCFunctionObject, vectorcall), READONLY, 0}, -#endif -#endif -#endif -#if PY_VERSION_HEX < 0x030500A0 || CYTHON_COMPILING_IN_LIMITED_API - {(char *) "__weaklistoffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_weakreflist), READONLY, 0}, -#else - {(char *) "__weaklistoffset__", T_PYSSIZET, offsetof(PyCFunctionObject, m_weakreflist), READONLY, 0}, -#endif -#endif - {0, 0, 0, 0, 0} -}; -static PyObject * -__Pyx_CyFunction_reduce(__pyx_CyFunctionObject *m, PyObject *args) -{ - CYTHON_UNUSED_VAR(args); -#if PY_MAJOR_VERSION >= 3 - Py_INCREF(m->func_qualname); - return m->func_qualname; -#else - return PyString_FromString(((PyCFunctionObject*)m)->m_ml->ml_name); -#endif -} -static PyMethodDef __pyx_CyFunction_methods[] = { - {"__reduce__", (PyCFunction)__Pyx_CyFunction_reduce, METH_VARARGS, 0}, - {0, 0, 0, 0} -}; -#if PY_VERSION_HEX < 0x030500A0 || CYTHON_COMPILING_IN_LIMITED_API -#define __Pyx_CyFunction_weakreflist(cyfunc) ((cyfunc)->func_weakreflist) -#else -#define __Pyx_CyFunction_weakreflist(cyfunc) (((PyCFunctionObject*)cyfunc)->m_weakreflist) -#endif -static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject *op, PyMethodDef *ml, int flags, PyObject* qualname, - PyObject *closure, PyObject *module, PyObject* globals, PyObject* code) { -#if !CYTHON_COMPILING_IN_LIMITED_API - PyCFunctionObject *cf = (PyCFunctionObject*) op; -#endif - if (unlikely(op == NULL)) - return NULL; -#if CYTHON_COMPILING_IN_LIMITED_API - op->func = PyCFunction_NewEx(ml, (PyObject*)op, module); - if (unlikely(!op->func)) return NULL; -#endif - op->flags = flags; - __Pyx_CyFunction_weakreflist(op) = NULL; -#if !CYTHON_COMPILING_IN_LIMITED_API - cf->m_ml = ml; - cf->m_self = (PyObject *) op; -#endif - Py_XINCREF(closure); - op->func_closure = closure; -#if !CYTHON_COMPILING_IN_LIMITED_API - Py_XINCREF(module); - cf->m_module = module; -#endif - op->func_dict = NULL; - op->func_name = NULL; - Py_INCREF(qualname); - op->func_qualname = qualname; - op->func_doc = NULL; -#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API - op->func_classobj = NULL; -#else - ((PyCMethodObject*)op)->mm_class = NULL; -#endif - op->func_globals = globals; - Py_INCREF(op->func_globals); - Py_XINCREF(code); - op->func_code = code; - op->defaults_pyobjects = 0; - op->defaults_size = 0; - op->defaults = NULL; - op->defaults_tuple = NULL; - op->defaults_kwdict = NULL; - op->defaults_getter = NULL; - op->func_annotations = NULL; - op->func_is_coroutine = NULL; -#if CYTHON_METH_FASTCALL - switch (ml->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O | METH_KEYWORDS | METH_METHOD)) { - case METH_NOARGS: - __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_NOARGS; - break; - case METH_O: - __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_O; - break; - case METH_METHOD | METH_FASTCALL | METH_KEYWORDS: - __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD; - break; - case METH_FASTCALL | METH_KEYWORDS: - __Pyx_CyFunction_func_vectorcall(op) = __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS; - break; - case METH_VARARGS | METH_KEYWORDS: - __Pyx_CyFunction_func_vectorcall(op) = NULL; - break; - default: - PyErr_SetString(PyExc_SystemError, "Bad call flags for CyFunction"); - Py_DECREF(op); - return NULL; - } -#endif - return (PyObject *) op; -} -static int -__Pyx_CyFunction_clear(__pyx_CyFunctionObject *m) -{ - Py_CLEAR(m->func_closure); -#if CYTHON_COMPILING_IN_LIMITED_API - Py_CLEAR(m->func); -#else - Py_CLEAR(((PyCFunctionObject*)m)->m_module); -#endif - Py_CLEAR(m->func_dict); - Py_CLEAR(m->func_name); - Py_CLEAR(m->func_qualname); - Py_CLEAR(m->func_doc); - Py_CLEAR(m->func_globals); - Py_CLEAR(m->func_code); -#if !CYTHON_COMPILING_IN_LIMITED_API -#if PY_VERSION_HEX < 0x030900B1 - Py_CLEAR(__Pyx_CyFunction_GetClassObj(m)); -#else - { - PyObject *cls = (PyObject*) ((PyCMethodObject *) (m))->mm_class; - ((PyCMethodObject *) (m))->mm_class = NULL; - Py_XDECREF(cls); - } -#endif -#endif - Py_CLEAR(m->defaults_tuple); - Py_CLEAR(m->defaults_kwdict); - Py_CLEAR(m->func_annotations); - Py_CLEAR(m->func_is_coroutine); - if (m->defaults) { - PyObject **pydefaults = __Pyx_CyFunction_Defaults(PyObject *, m); - int i; - for (i = 0; i < m->defaults_pyobjects; i++) - Py_XDECREF(pydefaults[i]); - PyObject_Free(m->defaults); - m->defaults = NULL; - } - return 0; -} -static void __Pyx__CyFunction_dealloc(__pyx_CyFunctionObject *m) -{ - if (__Pyx_CyFunction_weakreflist(m) != NULL) - PyObject_ClearWeakRefs((PyObject *) m); - __Pyx_CyFunction_clear(m); - __Pyx_PyHeapTypeObject_GC_Del(m); -} -static void __Pyx_CyFunction_dealloc(__pyx_CyFunctionObject *m) -{ - PyObject_GC_UnTrack(m); - __Pyx__CyFunction_dealloc(m); -} -static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit, void *arg) -{ - Py_VISIT(m->func_closure); -#if CYTHON_COMPILING_IN_LIMITED_API - Py_VISIT(m->func); -#else - Py_VISIT(((PyCFunctionObject*)m)->m_module); -#endif - Py_VISIT(m->func_dict); - Py_VISIT(m->func_name); - Py_VISIT(m->func_qualname); - Py_VISIT(m->func_doc); - Py_VISIT(m->func_globals); - Py_VISIT(m->func_code); -#if !CYTHON_COMPILING_IN_LIMITED_API - Py_VISIT(__Pyx_CyFunction_GetClassObj(m)); -#endif - Py_VISIT(m->defaults_tuple); - Py_VISIT(m->defaults_kwdict); - Py_VISIT(m->func_is_coroutine); - if (m->defaults) { - PyObject **pydefaults = __Pyx_CyFunction_Defaults(PyObject *, m); - int i; - for (i = 0; i < m->defaults_pyobjects; i++) - Py_VISIT(pydefaults[i]); - } - return 0; -} -static PyObject* -__Pyx_CyFunction_repr(__pyx_CyFunctionObject *op) -{ -#if PY_MAJOR_VERSION >= 3 - return PyUnicode_FromFormat("", - op->func_qualname, (void *)op); -#else - return PyString_FromFormat("", - PyString_AsString(op->func_qualname), (void *)op); -#endif -} -static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, PyObject *arg, PyObject *kw) { -#if CYTHON_COMPILING_IN_LIMITED_API - PyObject *f = ((__pyx_CyFunctionObject*)func)->func; - PyObject *py_name = NULL; - PyCFunction meth; - int flags; - meth = PyCFunction_GetFunction(f); - if (unlikely(!meth)) return NULL; - flags = PyCFunction_GetFlags(f); - if (unlikely(flags < 0)) return NULL; -#else - PyCFunctionObject* f = (PyCFunctionObject*)func; - PyCFunction meth = f->m_ml->ml_meth; - int flags = f->m_ml->ml_flags; -#endif - Py_ssize_t size; - switch (flags & (METH_VARARGS | METH_KEYWORDS | METH_NOARGS | METH_O)) { - case METH_VARARGS: - if (likely(kw == NULL || PyDict_Size(kw) == 0)) - return (*meth)(self, arg); - break; - case METH_VARARGS | METH_KEYWORDS: - return (*(PyCFunctionWithKeywords)(void*)meth)(self, arg, kw); - case METH_NOARGS: - if (likely(kw == NULL || PyDict_Size(kw) == 0)) { -#if CYTHON_ASSUME_SAFE_MACROS - size = PyTuple_GET_SIZE(arg); -#else - size = PyTuple_Size(arg); - if (unlikely(size < 0)) return NULL; -#endif - if (likely(size == 0)) - return (*meth)(self, NULL); -#if CYTHON_COMPILING_IN_LIMITED_API - py_name = __Pyx_CyFunction_get_name((__pyx_CyFunctionObject*)func, NULL); - if (!py_name) return NULL; - PyErr_Format(PyExc_TypeError, - "%.200S() takes no arguments (%" CYTHON_FORMAT_SSIZE_T "d given)", - py_name, size); - Py_DECREF(py_name); -#else - PyErr_Format(PyExc_TypeError, - "%.200s() takes no arguments (%" CYTHON_FORMAT_SSIZE_T "d given)", - f->m_ml->ml_name, size); -#endif - return NULL; - } - break; - case METH_O: - if (likely(kw == NULL || PyDict_Size(kw) == 0)) { -#if CYTHON_ASSUME_SAFE_MACROS - size = PyTuple_GET_SIZE(arg); -#else - size = PyTuple_Size(arg); - if (unlikely(size < 0)) return NULL; -#endif - if (likely(size == 1)) { - PyObject *result, *arg0; - #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - arg0 = PyTuple_GET_ITEM(arg, 0); - #else - arg0 = __Pyx_PySequence_ITEM(arg, 0); if (unlikely(!arg0)) return NULL; - #endif - result = (*meth)(self, arg0); - #if !(CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS) - Py_DECREF(arg0); - #endif - return result; - } -#if CYTHON_COMPILING_IN_LIMITED_API - py_name = __Pyx_CyFunction_get_name((__pyx_CyFunctionObject*)func, NULL); - if (!py_name) return NULL; - PyErr_Format(PyExc_TypeError, - "%.200S() takes exactly one argument (%" CYTHON_FORMAT_SSIZE_T "d given)", - py_name, size); - Py_DECREF(py_name); -#else - PyErr_Format(PyExc_TypeError, - "%.200s() takes exactly one argument (%" CYTHON_FORMAT_SSIZE_T "d given)", - f->m_ml->ml_name, size); -#endif - return NULL; - } - break; - default: - PyErr_SetString(PyExc_SystemError, "Bad call flags for CyFunction"); - return NULL; - } -#if CYTHON_COMPILING_IN_LIMITED_API - py_name = __Pyx_CyFunction_get_name((__pyx_CyFunctionObject*)func, NULL); - if (!py_name) return NULL; - PyErr_Format(PyExc_TypeError, "%.200S() takes no keyword arguments", - py_name); - Py_DECREF(py_name); -#else - PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", - f->m_ml->ml_name); -#endif - return NULL; -} -static CYTHON_INLINE PyObject *__Pyx_CyFunction_Call(PyObject *func, PyObject *arg, PyObject *kw) { - PyObject *self, *result; -#if CYTHON_COMPILING_IN_LIMITED_API - self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)func)->func); - if (unlikely(!self) && PyErr_Occurred()) return NULL; -#else - self = ((PyCFunctionObject*)func)->m_self; -#endif - result = __Pyx_CyFunction_CallMethod(func, self, arg, kw); - return result; -} -static PyObject *__Pyx_CyFunction_CallAsMethod(PyObject *func, PyObject *args, PyObject *kw) { - PyObject *result; - __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *) func; -#if CYTHON_METH_FASTCALL - __pyx_vectorcallfunc vc = __Pyx_CyFunction_func_vectorcall(cyfunc); - if (vc) { -#if CYTHON_ASSUME_SAFE_MACROS - return __Pyx_PyVectorcall_FastCallDict(func, vc, &PyTuple_GET_ITEM(args, 0), (size_t)PyTuple_GET_SIZE(args), kw); -#else - (void) &__Pyx_PyVectorcall_FastCallDict; - return PyVectorcall_Call(func, args, kw); -#endif - } -#endif - if ((cyfunc->flags & __Pyx_CYFUNCTION_CCLASS) && !(cyfunc->flags & __Pyx_CYFUNCTION_STATICMETHOD)) { - Py_ssize_t argc; - PyObject *new_args; - PyObject *self; -#if CYTHON_ASSUME_SAFE_MACROS - argc = PyTuple_GET_SIZE(args); -#else - argc = PyTuple_Size(args); - if (unlikely(!argc) < 0) return NULL; -#endif - new_args = PyTuple_GetSlice(args, 1, argc); - if (unlikely(!new_args)) - return NULL; - self = PyTuple_GetItem(args, 0); - if (unlikely(!self)) { - Py_DECREF(new_args); -#if PY_MAJOR_VERSION > 2 - PyErr_Format(PyExc_TypeError, - "unbound method %.200S() needs an argument", - cyfunc->func_qualname); -#else - PyErr_SetString(PyExc_TypeError, - "unbound method needs an argument"); -#endif - return NULL; - } - result = __Pyx_CyFunction_CallMethod(func, self, new_args, kw); - Py_DECREF(new_args); - } else { - result = __Pyx_CyFunction_Call(func, args, kw); - } - return result; -} -#if CYTHON_METH_FASTCALL -static CYTHON_INLINE int __Pyx_CyFunction_Vectorcall_CheckArgs(__pyx_CyFunctionObject *cyfunc, Py_ssize_t nargs, PyObject *kwnames) -{ - int ret = 0; - if ((cyfunc->flags & __Pyx_CYFUNCTION_CCLASS) && !(cyfunc->flags & __Pyx_CYFUNCTION_STATICMETHOD)) { - if (unlikely(nargs < 1)) { - PyErr_Format(PyExc_TypeError, "%.200s() needs an argument", - ((PyCFunctionObject*)cyfunc)->m_ml->ml_name); - return -1; - } - ret = 1; - } - if (unlikely(kwnames) && unlikely(PyTuple_GET_SIZE(kwnames))) { - PyErr_Format(PyExc_TypeError, - "%.200s() takes no keyword arguments", ((PyCFunctionObject*)cyfunc)->m_ml->ml_name); - return -1; - } - return ret; -} -static PyObject * __Pyx_CyFunction_Vectorcall_NOARGS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) -{ - __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func; - PyMethodDef* def = ((PyCFunctionObject*)cyfunc)->m_ml; -#if CYTHON_BACKPORT_VECTORCALL - Py_ssize_t nargs = (Py_ssize_t)nargsf; -#else - Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); -#endif - PyObject *self; - switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, kwnames)) { - case 1: - self = args[0]; - args += 1; - nargs -= 1; - break; - case 0: - self = ((PyCFunctionObject*)cyfunc)->m_self; - break; - default: - return NULL; - } - if (unlikely(nargs != 0)) { - PyErr_Format(PyExc_TypeError, - "%.200s() takes no arguments (%" CYTHON_FORMAT_SSIZE_T "d given)", - def->ml_name, nargs); - return NULL; - } - return def->ml_meth(self, NULL); -} -static PyObject * __Pyx_CyFunction_Vectorcall_O(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) -{ - __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func; - PyMethodDef* def = ((PyCFunctionObject*)cyfunc)->m_ml; -#if CYTHON_BACKPORT_VECTORCALL - Py_ssize_t nargs = (Py_ssize_t)nargsf; -#else - Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); -#endif - PyObject *self; - switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, kwnames)) { - case 1: - self = args[0]; - args += 1; - nargs -= 1; - break; - case 0: - self = ((PyCFunctionObject*)cyfunc)->m_self; - break; - default: - return NULL; - } - if (unlikely(nargs != 1)) { - PyErr_Format(PyExc_TypeError, - "%.200s() takes exactly one argument (%" CYTHON_FORMAT_SSIZE_T "d given)", - def->ml_name, nargs); - return NULL; - } - return def->ml_meth(self, args[0]); -} -static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) -{ - __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func; - PyMethodDef* def = ((PyCFunctionObject*)cyfunc)->m_ml; -#if CYTHON_BACKPORT_VECTORCALL - Py_ssize_t nargs = (Py_ssize_t)nargsf; -#else - Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); -#endif - PyObject *self; - switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, NULL)) { - case 1: - self = args[0]; - args += 1; - nargs -= 1; - break; - case 0: - self = ((PyCFunctionObject*)cyfunc)->m_self; - break; - default: - return NULL; - } - return ((__Pyx_PyCFunctionFastWithKeywords)(void(*)(void))def->ml_meth)(self, args, nargs, kwnames); -} -static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) -{ - __pyx_CyFunctionObject *cyfunc = (__pyx_CyFunctionObject *)func; - PyMethodDef* def = ((PyCFunctionObject*)cyfunc)->m_ml; - PyTypeObject *cls = (PyTypeObject *) __Pyx_CyFunction_GetClassObj(cyfunc); -#if CYTHON_BACKPORT_VECTORCALL - Py_ssize_t nargs = (Py_ssize_t)nargsf; -#else - Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); -#endif - PyObject *self; - switch (__Pyx_CyFunction_Vectorcall_CheckArgs(cyfunc, nargs, NULL)) { - case 1: - self = args[0]; - args += 1; - nargs -= 1; - break; - case 0: - self = ((PyCFunctionObject*)cyfunc)->m_self; - break; - default: - return NULL; - } - return ((__Pyx_PyCMethod)(void(*)(void))def->ml_meth)(self, cls, args, (size_t)nargs, kwnames); -} -#endif -#if CYTHON_USE_TYPE_SPECS -static PyType_Slot __pyx_CyFunctionType_slots[] = { - {Py_tp_dealloc, (void *)__Pyx_CyFunction_dealloc}, - {Py_tp_repr, (void *)__Pyx_CyFunction_repr}, - {Py_tp_call, (void *)__Pyx_CyFunction_CallAsMethod}, - {Py_tp_traverse, (void *)__Pyx_CyFunction_traverse}, - {Py_tp_clear, (void *)__Pyx_CyFunction_clear}, - {Py_tp_methods, (void *)__pyx_CyFunction_methods}, - {Py_tp_members, (void *)__pyx_CyFunction_members}, - {Py_tp_getset, (void *)__pyx_CyFunction_getsets}, - {Py_tp_descr_get, (void *)__Pyx_PyMethod_New}, - {0, 0}, -}; -static PyType_Spec __pyx_CyFunctionType_spec = { - __PYX_TYPE_MODULE_PREFIX "cython_function_or_method", - sizeof(__pyx_CyFunctionObject), - 0, -#ifdef Py_TPFLAGS_METHOD_DESCRIPTOR - Py_TPFLAGS_METHOD_DESCRIPTOR | -#endif -#if (defined(_Py_TPFLAGS_HAVE_VECTORCALL) && CYTHON_METH_FASTCALL) - _Py_TPFLAGS_HAVE_VECTORCALL | -#endif - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, - __pyx_CyFunctionType_slots -}; -#else -static PyTypeObject __pyx_CyFunctionType_type = { - PyVarObject_HEAD_INIT(0, 0) - __PYX_TYPE_MODULE_PREFIX "cython_function_or_method", - sizeof(__pyx_CyFunctionObject), - 0, - (destructor) __Pyx_CyFunction_dealloc, -#if !CYTHON_METH_FASTCALL - 0, -#elif CYTHON_BACKPORT_VECTORCALL - (printfunc)offsetof(__pyx_CyFunctionObject, func_vectorcall), -#else - offsetof(PyCFunctionObject, vectorcall), -#endif - 0, - 0, -#if PY_MAJOR_VERSION < 3 - 0, -#else - 0, -#endif - (reprfunc) __Pyx_CyFunction_repr, - 0, - 0, - 0, - 0, - __Pyx_CyFunction_CallAsMethod, - 0, - 0, - 0, - 0, -#ifdef Py_TPFLAGS_METHOD_DESCRIPTOR - Py_TPFLAGS_METHOD_DESCRIPTOR | -#endif -#if defined(_Py_TPFLAGS_HAVE_VECTORCALL) && CYTHON_METH_FASTCALL - _Py_TPFLAGS_HAVE_VECTORCALL | -#endif - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, - 0, - (traverseproc) __Pyx_CyFunction_traverse, - (inquiry) __Pyx_CyFunction_clear, - 0, -#if PY_VERSION_HEX < 0x030500A0 - offsetof(__pyx_CyFunctionObject, func_weakreflist), -#else - offsetof(PyCFunctionObject, m_weakreflist), -#endif - 0, - 0, - __pyx_CyFunction_methods, - __pyx_CyFunction_members, - __pyx_CyFunction_getsets, - 0, - 0, - __Pyx_PyMethod_New, - 0, - offsetof(__pyx_CyFunctionObject, func_dict), - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, -#if PY_VERSION_HEX >= 0x030400a1 - 0, -#endif -#if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800) - 0, -#endif -#if __PYX_NEED_TP_PRINT_SLOT - 0, -#endif -#if PY_VERSION_HEX >= 0x030C0000 - 0, -#endif -#if PY_VERSION_HEX >= 0x030d00A4 - 0, -#endif -#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000 && PY_VERSION_HEX < 0x030a0000 - 0, -#endif -}; -#endif -static int __pyx_CyFunction_init(PyObject *module) { -#if CYTHON_USE_TYPE_SPECS - __pyx_CyFunctionType = __Pyx_FetchCommonTypeFromSpec(module, &__pyx_CyFunctionType_spec, NULL); -#else - CYTHON_UNUSED_VAR(module); - __pyx_CyFunctionType = __Pyx_FetchCommonType(&__pyx_CyFunctionType_type); -#endif - if (unlikely(__pyx_CyFunctionType == NULL)) { - return -1; - } - return 0; -} -static CYTHON_INLINE void *__Pyx_CyFunction_InitDefaults(PyObject *func, size_t size, int pyobjects) { - __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func; - m->defaults = PyObject_Malloc(size); - if (unlikely(!m->defaults)) - return PyErr_NoMemory(); - memset(m->defaults, 0, size); - m->defaults_pyobjects = pyobjects; - m->defaults_size = size; - return m->defaults; -} -static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsTuple(PyObject *func, PyObject *tuple) { - __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func; - m->defaults_tuple = tuple; - Py_INCREF(tuple); -} -static CYTHON_INLINE void __Pyx_CyFunction_SetDefaultsKwDict(PyObject *func, PyObject *dict) { - __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func; - m->defaults_kwdict = dict; - Py_INCREF(dict); -} -static CYTHON_INLINE void __Pyx_CyFunction_SetAnnotationsDict(PyObject *func, PyObject *dict) { - __pyx_CyFunctionObject *m = (__pyx_CyFunctionObject *) func; - m->func_annotations = dict; - Py_INCREF(dict); -} - -/* CythonFunction */ -static PyObject *__Pyx_CyFunction_New(PyMethodDef *ml, int flags, PyObject* qualname, - PyObject *closure, PyObject *module, PyObject* globals, PyObject* code) { - PyObject *op = __Pyx_CyFunction_Init( - PyObject_GC_New(__pyx_CyFunctionObject, __pyx_CyFunctionType), - ml, flags, qualname, closure, module, globals, code - ); - if (likely(op)) { - PyObject_GC_Track(op); - } - return op; -} - -/* CLineInTraceback */ -#ifndef CYTHON_CLINE_IN_TRACEBACK -static int __Pyx_CLineForTraceback(PyThreadState *tstate, int c_line) { - PyObject *use_cline; - PyObject *ptype, *pvalue, *ptraceback; -#if CYTHON_COMPILING_IN_CPYTHON - PyObject **cython_runtime_dict; -#endif - CYTHON_MAYBE_UNUSED_VAR(tstate); - if (unlikely(!__pyx_cython_runtime)) { - return c_line; - } - __Pyx_ErrFetchInState(tstate, &ptype, &pvalue, &ptraceback); -#if CYTHON_COMPILING_IN_CPYTHON - cython_runtime_dict = _PyObject_GetDictPtr(__pyx_cython_runtime); - if (likely(cython_runtime_dict)) { - __PYX_PY_DICT_LOOKUP_IF_MODIFIED( - use_cline, *cython_runtime_dict, - __Pyx_PyDict_GetItemStr(*cython_runtime_dict, __pyx_n_s_cline_in_traceback)) - } else -#endif - { - PyObject *use_cline_obj = __Pyx_PyObject_GetAttrStrNoError(__pyx_cython_runtime, __pyx_n_s_cline_in_traceback); - if (use_cline_obj) { - use_cline = PyObject_Not(use_cline_obj) ? Py_False : Py_True; - Py_DECREF(use_cline_obj); - } else { - PyErr_Clear(); - use_cline = NULL; - } - } - if (!use_cline) { - c_line = 0; - (void) PyObject_SetAttr(__pyx_cython_runtime, __pyx_n_s_cline_in_traceback, Py_False); - } - else if (use_cline == Py_False || (use_cline != Py_True && PyObject_Not(use_cline) != 0)) { - c_line = 0; - } - __Pyx_ErrRestoreInState(tstate, ptype, pvalue, ptraceback); - return c_line; -} -#endif - -/* CodeObjectCache */ -#if !CYTHON_COMPILING_IN_LIMITED_API -static int __pyx_bisect_code_objects(__Pyx_CodeObjectCacheEntry* entries, int count, int code_line) { - int start = 0, mid = 0, end = count - 1; - if (end >= 0 && code_line > entries[end].code_line) { - return count; - } - while (start < end) { - mid = start + (end - start) / 2; - if (code_line < entries[mid].code_line) { - end = mid; - } else if (code_line > entries[mid].code_line) { - start = mid + 1; - } else { - return mid; - } - } - if (code_line <= entries[mid].code_line) { - return mid; - } else { - return mid + 1; - } -} -static PyCodeObject *__pyx_find_code_object(int code_line) { - PyCodeObject* code_object; - int pos; - if (unlikely(!code_line) || unlikely(!__pyx_code_cache.entries)) { - return NULL; - } - pos = __pyx_bisect_code_objects(__pyx_code_cache.entries, __pyx_code_cache.count, code_line); - if (unlikely(pos >= __pyx_code_cache.count) || unlikely(__pyx_code_cache.entries[pos].code_line != code_line)) { - return NULL; - } - code_object = __pyx_code_cache.entries[pos].code_object; - Py_INCREF(code_object); - return code_object; -} -static void __pyx_insert_code_object(int code_line, PyCodeObject* code_object) { - int pos, i; - __Pyx_CodeObjectCacheEntry* entries = __pyx_code_cache.entries; - if (unlikely(!code_line)) { - return; - } - if (unlikely(!entries)) { - entries = (__Pyx_CodeObjectCacheEntry*)PyMem_Malloc(64*sizeof(__Pyx_CodeObjectCacheEntry)); - if (likely(entries)) { - __pyx_code_cache.entries = entries; - __pyx_code_cache.max_count = 64; - __pyx_code_cache.count = 1; - entries[0].code_line = code_line; - entries[0].code_object = code_object; - Py_INCREF(code_object); - } - return; - } - pos = __pyx_bisect_code_objects(__pyx_code_cache.entries, __pyx_code_cache.count, code_line); - if ((pos < __pyx_code_cache.count) && unlikely(__pyx_code_cache.entries[pos].code_line == code_line)) { - PyCodeObject* tmp = entries[pos].code_object; - entries[pos].code_object = code_object; - Py_DECREF(tmp); - return; - } - if (__pyx_code_cache.count == __pyx_code_cache.max_count) { - int new_max = __pyx_code_cache.max_count + 64; - entries = (__Pyx_CodeObjectCacheEntry*)PyMem_Realloc( - __pyx_code_cache.entries, ((size_t)new_max) * sizeof(__Pyx_CodeObjectCacheEntry)); - if (unlikely(!entries)) { - return; - } - __pyx_code_cache.entries = entries; - __pyx_code_cache.max_count = new_max; - } - for (i=__pyx_code_cache.count; i>pos; i--) { - entries[i] = entries[i-1]; - } - entries[pos].code_line = code_line; - entries[pos].code_object = code_object; - __pyx_code_cache.count++; - Py_INCREF(code_object); -} -#endif - -/* AddTraceback */ -#include "compile.h" -#include "frameobject.h" -#include "traceback.h" -#if PY_VERSION_HEX >= 0x030b00a6 && !CYTHON_COMPILING_IN_LIMITED_API - #ifndef Py_BUILD_CORE - #define Py_BUILD_CORE 1 - #endif - #include "internal/pycore_frame.h" -#endif -#if CYTHON_COMPILING_IN_LIMITED_API -static PyObject *__Pyx_PyCode_Replace_For_AddTraceback(PyObject *code, PyObject *scratch_dict, - PyObject *firstlineno, PyObject *name) { - PyObject *replace = NULL; - if (unlikely(PyDict_SetItemString(scratch_dict, "co_firstlineno", firstlineno))) return NULL; - if (unlikely(PyDict_SetItemString(scratch_dict, "co_name", name))) return NULL; - replace = PyObject_GetAttrString(code, "replace"); - if (likely(replace)) { - PyObject *result; - result = PyObject_Call(replace, __pyx_empty_tuple, scratch_dict); - Py_DECREF(replace); - return result; - } - PyErr_Clear(); - #if __PYX_LIMITED_VERSION_HEX < 0x030780000 - { - PyObject *compiled = NULL, *result = NULL; - if (unlikely(PyDict_SetItemString(scratch_dict, "code", code))) return NULL; - if (unlikely(PyDict_SetItemString(scratch_dict, "type", (PyObject*)(&PyType_Type)))) return NULL; - compiled = Py_CompileString( - "out = type(code)(\n" - " code.co_argcount, code.co_kwonlyargcount, code.co_nlocals, code.co_stacksize,\n" - " code.co_flags, code.co_code, code.co_consts, code.co_names,\n" - " code.co_varnames, code.co_filename, co_name, co_firstlineno,\n" - " code.co_lnotab)\n", "", Py_file_input); - if (!compiled) return NULL; - result = PyEval_EvalCode(compiled, scratch_dict, scratch_dict); - Py_DECREF(compiled); - if (!result) PyErr_Print(); - Py_DECREF(result); - result = PyDict_GetItemString(scratch_dict, "out"); - if (result) Py_INCREF(result); - return result; - } - #else - return NULL; - #endif -} -static void __Pyx_AddTraceback(const char *funcname, int c_line, - int py_line, const char *filename) { - PyObject *code_object = NULL, *py_py_line = NULL, *py_funcname = NULL, *dict = NULL; - PyObject *replace = NULL, *getframe = NULL, *frame = NULL; - PyObject *exc_type, *exc_value, *exc_traceback; - int success = 0; - if (c_line) { - (void) __pyx_cfilenm; - (void) __Pyx_CLineForTraceback(__Pyx_PyThreadState_Current, c_line); - } - PyErr_Fetch(&exc_type, &exc_value, &exc_traceback); - code_object = Py_CompileString("_getframe()", filename, Py_eval_input); - if (unlikely(!code_object)) goto bad; - py_py_line = PyLong_FromLong(py_line); - if (unlikely(!py_py_line)) goto bad; - py_funcname = PyUnicode_FromString(funcname); - if (unlikely(!py_funcname)) goto bad; - dict = PyDict_New(); - if (unlikely(!dict)) goto bad; - { - PyObject *old_code_object = code_object; - code_object = __Pyx_PyCode_Replace_For_AddTraceback(code_object, dict, py_py_line, py_funcname); - Py_DECREF(old_code_object); - } - if (unlikely(!code_object)) goto bad; - getframe = PySys_GetObject("_getframe"); - if (unlikely(!getframe)) goto bad; - if (unlikely(PyDict_SetItemString(dict, "_getframe", getframe))) goto bad; - frame = PyEval_EvalCode(code_object, dict, dict); - if (unlikely(!frame) || frame == Py_None) goto bad; - success = 1; - bad: - PyErr_Restore(exc_type, exc_value, exc_traceback); - Py_XDECREF(code_object); - Py_XDECREF(py_py_line); - Py_XDECREF(py_funcname); - Py_XDECREF(dict); - Py_XDECREF(replace); - if (success) { - PyTraceBack_Here( - (struct _frame*)frame); - } - Py_XDECREF(frame); -} -#else -static PyCodeObject* __Pyx_CreateCodeObjectForTraceback( - const char *funcname, int c_line, - int py_line, const char *filename) { - PyCodeObject *py_code = NULL; - PyObject *py_funcname = NULL; - #if PY_MAJOR_VERSION < 3 - PyObject *py_srcfile = NULL; - py_srcfile = PyString_FromString(filename); - if (!py_srcfile) goto bad; - #endif - if (c_line) { - #if PY_MAJOR_VERSION < 3 - py_funcname = PyString_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line); - if (!py_funcname) goto bad; - #else - py_funcname = PyUnicode_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line); - if (!py_funcname) goto bad; - funcname = PyUnicode_AsUTF8(py_funcname); - if (!funcname) goto bad; - #endif - } - else { - #if PY_MAJOR_VERSION < 3 - py_funcname = PyString_FromString(funcname); - if (!py_funcname) goto bad; - #endif - } - #if PY_MAJOR_VERSION < 3 - py_code = __Pyx_PyCode_New( - 0, - 0, - 0, - 0, - 0, - 0, - __pyx_empty_bytes, /*PyObject *code,*/ - __pyx_empty_tuple, /*PyObject *consts,*/ - __pyx_empty_tuple, /*PyObject *names,*/ - __pyx_empty_tuple, /*PyObject *varnames,*/ - __pyx_empty_tuple, /*PyObject *freevars,*/ - __pyx_empty_tuple, /*PyObject *cellvars,*/ - py_srcfile, /*PyObject *filename,*/ - py_funcname, /*PyObject *name,*/ - py_line, - __pyx_empty_bytes /*PyObject *lnotab*/ - ); - Py_DECREF(py_srcfile); - #else - py_code = PyCode_NewEmpty(filename, funcname, py_line); - #endif - Py_XDECREF(py_funcname); - return py_code; -bad: - Py_XDECREF(py_funcname); - #if PY_MAJOR_VERSION < 3 - Py_XDECREF(py_srcfile); - #endif - return NULL; -} -static void __Pyx_AddTraceback(const char *funcname, int c_line, - int py_line, const char *filename) { - PyCodeObject *py_code = 0; - PyFrameObject *py_frame = 0; - PyThreadState *tstate = __Pyx_PyThreadState_Current; - PyObject *ptype, *pvalue, *ptraceback; - if (c_line) { - c_line = __Pyx_CLineForTraceback(tstate, c_line); - } - py_code = __pyx_find_code_object(c_line ? -c_line : py_line); - if (!py_code) { - __Pyx_ErrFetchInState(tstate, &ptype, &pvalue, &ptraceback); - py_code = __Pyx_CreateCodeObjectForTraceback( - funcname, c_line, py_line, filename); - if (!py_code) { - /* If the code object creation fails, then we should clear the - fetched exception references and propagate the new exception */ - Py_XDECREF(ptype); - Py_XDECREF(pvalue); - Py_XDECREF(ptraceback); - goto bad; - } - __Pyx_ErrRestoreInState(tstate, ptype, pvalue, ptraceback); - __pyx_insert_code_object(c_line ? -c_line : py_line, py_code); - } - py_frame = PyFrame_New( - tstate, /*PyThreadState *tstate,*/ - py_code, /*PyCodeObject *code,*/ - __pyx_d, /*PyObject *globals,*/ - 0 /*PyObject *locals*/ - ); - if (!py_frame) goto bad; - __Pyx_PyFrame_SetLineNumber(py_frame, py_line); - PyTraceBack_Here(py_frame); -bad: - Py_XDECREF(py_code); - Py_XDECREF(py_frame); -} -#endif - -/* Declarations */ -#if CYTHON_CCOMPLEX && (1) && (!0 || __cplusplus) - #ifdef __cplusplus - static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(double x, double y) { - return ::std::complex< double >(x, y); - } - #else - static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(double x, double y) { - return x + y*(__pyx_t_double_complex)_Complex_I; - } - #endif -#else - static CYTHON_INLINE __pyx_t_double_complex __pyx_t_double_complex_from_parts(double x, double y) { - __pyx_t_double_complex z; - z.real = x; - z.imag = y; - return z; - } -#endif - -/* Arithmetic */ -#if CYTHON_CCOMPLEX && (1) && (!0 || __cplusplus) -#else - static CYTHON_INLINE int __Pyx_c_eq_double(__pyx_t_double_complex a, __pyx_t_double_complex b) { - return (a.real == b.real) && (a.imag == b.imag); - } - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_sum_double(__pyx_t_double_complex a, __pyx_t_double_complex b) { - __pyx_t_double_complex z; - z.real = a.real + b.real; - z.imag = a.imag + b.imag; - return z; - } - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_diff_double(__pyx_t_double_complex a, __pyx_t_double_complex b) { - __pyx_t_double_complex z; - z.real = a.real - b.real; - z.imag = a.imag - b.imag; - return z; - } - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_prod_double(__pyx_t_double_complex a, __pyx_t_double_complex b) { - __pyx_t_double_complex z; - z.real = a.real * b.real - a.imag * b.imag; - z.imag = a.real * b.imag + a.imag * b.real; - return z; - } - #if 1 - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_quot_double(__pyx_t_double_complex a, __pyx_t_double_complex b) { - if (b.imag == 0) { - return __pyx_t_double_complex_from_parts(a.real / b.real, a.imag / b.real); - } else if (fabs(b.real) >= fabs(b.imag)) { - if (b.real == 0 && b.imag == 0) { - return __pyx_t_double_complex_from_parts(a.real / b.real, a.imag / b.imag); - } else { - double r = b.imag / b.real; - double s = (double)(1.0) / (b.real + b.imag * r); - return __pyx_t_double_complex_from_parts( - (a.real + a.imag * r) * s, (a.imag - a.real * r) * s); - } - } else { - double r = b.real / b.imag; - double s = (double)(1.0) / (b.imag + b.real * r); - return __pyx_t_double_complex_from_parts( - (a.real * r + a.imag) * s, (a.imag * r - a.real) * s); - } - } - #else - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_quot_double(__pyx_t_double_complex a, __pyx_t_double_complex b) { - if (b.imag == 0) { - return __pyx_t_double_complex_from_parts(a.real / b.real, a.imag / b.real); - } else { - double denom = b.real * b.real + b.imag * b.imag; - return __pyx_t_double_complex_from_parts( - (a.real * b.real + a.imag * b.imag) / denom, - (a.imag * b.real - a.real * b.imag) / denom); - } - } - #endif - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_neg_double(__pyx_t_double_complex a) { - __pyx_t_double_complex z; - z.real = -a.real; - z.imag = -a.imag; - return z; - } - static CYTHON_INLINE int __Pyx_c_is_zero_double(__pyx_t_double_complex a) { - return (a.real == 0) && (a.imag == 0); - } - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_conj_double(__pyx_t_double_complex a) { - __pyx_t_double_complex z; - z.real = a.real; - z.imag = -a.imag; - return z; - } - #if 1 - static CYTHON_INLINE double __Pyx_c_abs_double(__pyx_t_double_complex z) { - #if !defined(HAVE_HYPOT) || defined(_MSC_VER) - return sqrt(z.real*z.real + z.imag*z.imag); - #else - return hypot(z.real, z.imag); - #endif - } - static CYTHON_INLINE __pyx_t_double_complex __Pyx_c_pow_double(__pyx_t_double_complex a, __pyx_t_double_complex b) { - __pyx_t_double_complex z; - double r, lnr, theta, z_r, z_theta; - if (b.imag == 0 && b.real == (int)b.real) { - if (b.real < 0) { - double denom = a.real * a.real + a.imag * a.imag; - a.real = a.real / denom; - a.imag = -a.imag / denom; - b.real = -b.real; - } - switch ((int)b.real) { - case 0: - z.real = 1; - z.imag = 0; - return z; - case 1: - return a; - case 2: - return __Pyx_c_prod_double(a, a); - case 3: - z = __Pyx_c_prod_double(a, a); - return __Pyx_c_prod_double(z, a); - case 4: - z = __Pyx_c_prod_double(a, a); - return __Pyx_c_prod_double(z, z); - } - } - if (a.imag == 0) { - if (a.real == 0) { - return a; - } else if ((b.imag == 0) && (a.real >= 0)) { - z.real = pow(a.real, b.real); - z.imag = 0; - return z; - } else if (a.real > 0) { - r = a.real; - theta = 0; - } else { - r = -a.real; - theta = atan2(0.0, -1.0); - } - } else { - r = __Pyx_c_abs_double(a); - theta = atan2(a.imag, a.real); - } - lnr = log(r); - z_r = exp(lnr * b.real - theta * b.imag); - z_theta = theta * b.real + lnr * b.imag; - z.real = z_r * cos(z_theta); - z.imag = z_r * sin(z_theta); - return z; - } - #endif -#endif - -/* FromPy */ -static __pyx_t_double_complex __Pyx_PyComplex_As___pyx_t_double_complex(PyObject* o) { - Py_complex cval; -#if !CYTHON_COMPILING_IN_PYPY - if (PyComplex_CheckExact(o)) - cval = ((PyComplexObject *)o)->cval; - else -#endif - cval = PyComplex_AsCComplex(o); - return __pyx_t_double_complex_from_parts( - (double)cval.real, - (double)cval.imag); -} - -/* CIntFromPyVerify */ -#define __PYX_VERIFY_RETURN_INT(target_type, func_type, func_value)\ - __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, 0) -#define __PYX_VERIFY_RETURN_INT_EXC(target_type, func_type, func_value)\ - __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, 1) -#define __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, exc)\ - {\ - func_type value = func_value;\ - if (sizeof(target_type) < sizeof(func_type)) {\ - if (unlikely(value != (func_type) (target_type) value)) {\ - func_type zero = 0;\ - if (exc && unlikely(value == (func_type)-1 && PyErr_Occurred()))\ - return (target_type) -1;\ - if (is_unsigned && unlikely(value < zero))\ - goto raise_neg_overflow;\ - else\ - goto raise_overflow;\ - }\ - }\ - return (target_type) value;\ - } - -/* CIntFromPy */ -static CYTHON_INLINE int __Pyx_PyInt_As_int(PyObject *x) { -#ifdef __Pyx_HAS_GCC_DIAGNOSTIC -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconversion" -#endif - const int neg_one = (int) -1, const_zero = (int) 0; -#ifdef __Pyx_HAS_GCC_DIAGNOSTIC -#pragma GCC diagnostic pop -#endif - const int is_unsigned = neg_one > const_zero; -#if PY_MAJOR_VERSION < 3 - if (likely(PyInt_Check(x))) { - if ((sizeof(int) < sizeof(long))) { - __PYX_VERIFY_RETURN_INT(int, long, PyInt_AS_LONG(x)) - } else { - long val = PyInt_AS_LONG(x); - if (is_unsigned && unlikely(val < 0)) { - goto raise_neg_overflow; - } - return (int) val; - } - } -#endif - if (unlikely(!PyLong_Check(x))) { - int val; - PyObject *tmp = __Pyx_PyNumber_IntOrLong(x); - if (!tmp) return (int) -1; - val = __Pyx_PyInt_As_int(tmp); - Py_DECREF(tmp); - return val; - } - if (is_unsigned) { -#if CYTHON_USE_PYLONG_INTERNALS - if (unlikely(__Pyx_PyLong_IsNeg(x))) { - goto raise_neg_overflow; - } else if (__Pyx_PyLong_IsCompact(x)) { - __PYX_VERIFY_RETURN_INT(int, __Pyx_compact_upylong, __Pyx_PyLong_CompactValueUnsigned(x)) - } else { - const digit* digits = __Pyx_PyLong_Digits(x); - assert(__Pyx_PyLong_DigitCount(x) > 1); - switch (__Pyx_PyLong_DigitCount(x)) { - case 2: - if ((8 * sizeof(int) > 1 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(int) >= 2 * PyLong_SHIFT)) { - return (int) (((((int)digits[1]) << PyLong_SHIFT) | (int)digits[0])); - } - } - break; - case 3: - if ((8 * sizeof(int) > 2 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(int) >= 3 * PyLong_SHIFT)) { - return (int) (((((((int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0])); - } - } - break; - case 4: - if ((8 * sizeof(int) > 3 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(int) >= 4 * PyLong_SHIFT)) { - return (int) (((((((((int)digits[3]) << PyLong_SHIFT) | (int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0])); - } - } - break; - } - } -#endif -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030C00A7 - if (unlikely(Py_SIZE(x) < 0)) { - goto raise_neg_overflow; - } -#else - { - int result = PyObject_RichCompareBool(x, Py_False, Py_LT); - if (unlikely(result < 0)) - return (int) -1; - if (unlikely(result == 1)) - goto raise_neg_overflow; - } -#endif - if ((sizeof(int) <= sizeof(unsigned long))) { - __PYX_VERIFY_RETURN_INT_EXC(int, unsigned long, PyLong_AsUnsignedLong(x)) -#ifdef HAVE_LONG_LONG - } else if ((sizeof(int) <= sizeof(unsigned PY_LONG_LONG))) { - __PYX_VERIFY_RETURN_INT_EXC(int, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong(x)) -#endif - } - } else { -#if CYTHON_USE_PYLONG_INTERNALS - if (__Pyx_PyLong_IsCompact(x)) { - __PYX_VERIFY_RETURN_INT(int, __Pyx_compact_pylong, __Pyx_PyLong_CompactValue(x)) - } else { - const digit* digits = __Pyx_PyLong_Digits(x); - assert(__Pyx_PyLong_DigitCount(x) > 1); - switch (__Pyx_PyLong_SignedDigitCount(x)) { - case -2: - if ((8 * sizeof(int) - 1 > 1 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(int, long, -(long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(int) - 1 > 2 * PyLong_SHIFT)) { - return (int) (((int)-1)*(((((int)digits[1]) << PyLong_SHIFT) | (int)digits[0]))); - } - } - break; - case 2: - if ((8 * sizeof(int) > 1 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(int) - 1 > 2 * PyLong_SHIFT)) { - return (int) ((((((int)digits[1]) << PyLong_SHIFT) | (int)digits[0]))); - } - } - break; - case -3: - if ((8 * sizeof(int) - 1 > 2 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(int, long, -(long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(int) - 1 > 3 * PyLong_SHIFT)) { - return (int) (((int)-1)*(((((((int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0]))); - } - } - break; - case 3: - if ((8 * sizeof(int) > 2 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(int) - 1 > 3 * PyLong_SHIFT)) { - return (int) ((((((((int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0]))); - } - } - break; - case -4: - if ((8 * sizeof(int) - 1 > 3 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(int, long, -(long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(int) - 1 > 4 * PyLong_SHIFT)) { - return (int) (((int)-1)*(((((((((int)digits[3]) << PyLong_SHIFT) | (int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0]))); - } - } - break; - case 4: - if ((8 * sizeof(int) > 3 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(int) - 1 > 4 * PyLong_SHIFT)) { - return (int) ((((((((((int)digits[3]) << PyLong_SHIFT) | (int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0]))); - } - } - break; - } - } -#endif - if ((sizeof(int) <= sizeof(long))) { - __PYX_VERIFY_RETURN_INT_EXC(int, long, PyLong_AsLong(x)) -#ifdef HAVE_LONG_LONG - } else if ((sizeof(int) <= sizeof(PY_LONG_LONG))) { - __PYX_VERIFY_RETURN_INT_EXC(int, PY_LONG_LONG, PyLong_AsLongLong(x)) -#endif - } - } - { - int val; - int ret = -1; -#if PY_VERSION_HEX >= 0x030d00A6 && !CYTHON_COMPILING_IN_LIMITED_API - Py_ssize_t bytes_copied = PyLong_AsNativeBytes( - x, &val, sizeof(val), Py_ASNATIVEBYTES_NATIVE_ENDIAN | (is_unsigned ? Py_ASNATIVEBYTES_UNSIGNED_BUFFER | Py_ASNATIVEBYTES_REJECT_NEGATIVE : 0)); - if (unlikely(bytes_copied == -1)) { - } else if (unlikely(bytes_copied > (Py_ssize_t) sizeof(val))) { - goto raise_overflow; - } else { - ret = 0; - } -#elif PY_VERSION_HEX < 0x030d0000 && !(CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API) || defined(_PyLong_AsByteArray) - int one = 1; int is_little = (int)*(unsigned char *)&one; - unsigned char *bytes = (unsigned char *)&val; - ret = _PyLong_AsByteArray((PyLongObject *)x, - bytes, sizeof(val), - is_little, !is_unsigned); -#else - PyObject *v; - PyObject *stepval = NULL, *mask = NULL, *shift = NULL; - int bits, remaining_bits, is_negative = 0; - int chunk_size = (sizeof(long) < 8) ? 30 : 62; - if (likely(PyLong_CheckExact(x))) { - v = __Pyx_NewRef(x); - } else { - v = PyNumber_Long(x); - if (unlikely(!v)) return (int) -1; - assert(PyLong_CheckExact(v)); - } - { - int result = PyObject_RichCompareBool(v, Py_False, Py_LT); - if (unlikely(result < 0)) { - Py_DECREF(v); - return (int) -1; - } - is_negative = result == 1; - } - if (is_unsigned && unlikely(is_negative)) { - Py_DECREF(v); - goto raise_neg_overflow; - } else if (is_negative) { - stepval = PyNumber_Invert(v); - Py_DECREF(v); - if (unlikely(!stepval)) - return (int) -1; - } else { - stepval = v; - } - v = NULL; - val = (int) 0; - mask = PyLong_FromLong((1L << chunk_size) - 1); if (unlikely(!mask)) goto done; - shift = PyLong_FromLong(chunk_size); if (unlikely(!shift)) goto done; - for (bits = 0; bits < (int) sizeof(int) * 8 - chunk_size; bits += chunk_size) { - PyObject *tmp, *digit; - long idigit; - digit = PyNumber_And(stepval, mask); - if (unlikely(!digit)) goto done; - idigit = PyLong_AsLong(digit); - Py_DECREF(digit); - if (unlikely(idigit < 0)) goto done; - val |= ((int) idigit) << bits; - tmp = PyNumber_Rshift(stepval, shift); - if (unlikely(!tmp)) goto done; - Py_DECREF(stepval); stepval = tmp; - } - Py_DECREF(shift); shift = NULL; - Py_DECREF(mask); mask = NULL; - { - long idigit = PyLong_AsLong(stepval); - if (unlikely(idigit < 0)) goto done; - remaining_bits = ((int) sizeof(int) * 8) - bits - (is_unsigned ? 0 : 1); - if (unlikely(idigit >= (1L << remaining_bits))) - goto raise_overflow; - val |= ((int) idigit) << bits; - } - if (!is_unsigned) { - if (unlikely(val & (((int) 1) << (sizeof(int) * 8 - 1)))) - goto raise_overflow; - if (is_negative) - val = ~val; - } - ret = 0; - done: - Py_XDECREF(shift); - Py_XDECREF(mask); - Py_XDECREF(stepval); -#endif - if (unlikely(ret)) - return (int) -1; - return val; - } -raise_overflow: - PyErr_SetString(PyExc_OverflowError, - "value too large to convert to int"); - return (int) -1; -raise_neg_overflow: - PyErr_SetString(PyExc_OverflowError, - "can't convert negative value to int"); - return (int) -1; -} - -/* CIntToPy */ -static CYTHON_INLINE PyObject* __Pyx_PyInt_From_long(long value) { -#ifdef __Pyx_HAS_GCC_DIAGNOSTIC -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconversion" -#endif - const long neg_one = (long) -1, const_zero = (long) 0; -#ifdef __Pyx_HAS_GCC_DIAGNOSTIC -#pragma GCC diagnostic pop -#endif - const int is_unsigned = neg_one > const_zero; - if (is_unsigned) { - if (sizeof(long) < sizeof(long)) { - return PyInt_FromLong((long) value); - } else if (sizeof(long) <= sizeof(unsigned long)) { - return PyLong_FromUnsignedLong((unsigned long) value); -#ifdef HAVE_LONG_LONG - } else if (sizeof(long) <= sizeof(unsigned PY_LONG_LONG)) { - return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG) value); -#endif - } - } else { - if (sizeof(long) <= sizeof(long)) { - return PyInt_FromLong((long) value); -#ifdef HAVE_LONG_LONG - } else if (sizeof(long) <= sizeof(PY_LONG_LONG)) { - return PyLong_FromLongLong((PY_LONG_LONG) value); -#endif - } - } - { - unsigned char *bytes = (unsigned char *)&value; -#if !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x030d00A4 - if (is_unsigned) { - return PyLong_FromUnsignedNativeBytes(bytes, sizeof(value), -1); - } else { - return PyLong_FromNativeBytes(bytes, sizeof(value), -1); - } -#elif !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030d0000 - int one = 1; int little = (int)*(unsigned char *)&one; - return _PyLong_FromByteArray(bytes, sizeof(long), - little, !is_unsigned); -#else - int one = 1; int little = (int)*(unsigned char *)&one; - PyObject *from_bytes, *result = NULL; - PyObject *py_bytes = NULL, *arg_tuple = NULL, *kwds = NULL, *order_str = NULL; - from_bytes = PyObject_GetAttrString((PyObject*)&PyLong_Type, "from_bytes"); - if (!from_bytes) return NULL; - py_bytes = PyBytes_FromStringAndSize((char*)bytes, sizeof(long)); - if (!py_bytes) goto limited_bad; - order_str = PyUnicode_FromString(little ? "little" : "big"); - if (!order_str) goto limited_bad; - arg_tuple = PyTuple_Pack(2, py_bytes, order_str); - if (!arg_tuple) goto limited_bad; - if (!is_unsigned) { - kwds = PyDict_New(); - if (!kwds) goto limited_bad; - if (PyDict_SetItemString(kwds, "signed", __Pyx_NewRef(Py_True))) goto limited_bad; - } - result = PyObject_Call(from_bytes, arg_tuple, kwds); - limited_bad: - Py_XDECREF(kwds); - Py_XDECREF(arg_tuple); - Py_XDECREF(order_str); - Py_XDECREF(py_bytes); - Py_XDECREF(from_bytes); - return result; -#endif - } -} - -/* CIntToPy */ -static CYTHON_INLINE PyObject* __Pyx_PyInt_From_int(int value) { -#ifdef __Pyx_HAS_GCC_DIAGNOSTIC -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconversion" -#endif - const int neg_one = (int) -1, const_zero = (int) 0; -#ifdef __Pyx_HAS_GCC_DIAGNOSTIC -#pragma GCC diagnostic pop -#endif - const int is_unsigned = neg_one > const_zero; - if (is_unsigned) { - if (sizeof(int) < sizeof(long)) { - return PyInt_FromLong((long) value); - } else if (sizeof(int) <= sizeof(unsigned long)) { - return PyLong_FromUnsignedLong((unsigned long) value); -#ifdef HAVE_LONG_LONG - } else if (sizeof(int) <= sizeof(unsigned PY_LONG_LONG)) { - return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG) value); -#endif - } - } else { - if (sizeof(int) <= sizeof(long)) { - return PyInt_FromLong((long) value); -#ifdef HAVE_LONG_LONG - } else if (sizeof(int) <= sizeof(PY_LONG_LONG)) { - return PyLong_FromLongLong((PY_LONG_LONG) value); -#endif - } - } - { - unsigned char *bytes = (unsigned char *)&value; -#if !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x030d00A4 - if (is_unsigned) { - return PyLong_FromUnsignedNativeBytes(bytes, sizeof(value), -1); - } else { - return PyLong_FromNativeBytes(bytes, sizeof(value), -1); - } -#elif !CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030d0000 - int one = 1; int little = (int)*(unsigned char *)&one; - return _PyLong_FromByteArray(bytes, sizeof(int), - little, !is_unsigned); -#else - int one = 1; int little = (int)*(unsigned char *)&one; - PyObject *from_bytes, *result = NULL; - PyObject *py_bytes = NULL, *arg_tuple = NULL, *kwds = NULL, *order_str = NULL; - from_bytes = PyObject_GetAttrString((PyObject*)&PyLong_Type, "from_bytes"); - if (!from_bytes) return NULL; - py_bytes = PyBytes_FromStringAndSize((char*)bytes, sizeof(int)); - if (!py_bytes) goto limited_bad; - order_str = PyUnicode_FromString(little ? "little" : "big"); - if (!order_str) goto limited_bad; - arg_tuple = PyTuple_Pack(2, py_bytes, order_str); - if (!arg_tuple) goto limited_bad; - if (!is_unsigned) { - kwds = PyDict_New(); - if (!kwds) goto limited_bad; - if (PyDict_SetItemString(kwds, "signed", __Pyx_NewRef(Py_True))) goto limited_bad; - } - result = PyObject_Call(from_bytes, arg_tuple, kwds); - limited_bad: - Py_XDECREF(kwds); - Py_XDECREF(arg_tuple); - Py_XDECREF(order_str); - Py_XDECREF(py_bytes); - Py_XDECREF(from_bytes); - return result; -#endif - } -} - -/* CIntFromPy */ -static CYTHON_INLINE long __Pyx_PyInt_As_long(PyObject *x) { -#ifdef __Pyx_HAS_GCC_DIAGNOSTIC -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconversion" -#endif - const long neg_one = (long) -1, const_zero = (long) 0; -#ifdef __Pyx_HAS_GCC_DIAGNOSTIC -#pragma GCC diagnostic pop -#endif - const int is_unsigned = neg_one > const_zero; -#if PY_MAJOR_VERSION < 3 - if (likely(PyInt_Check(x))) { - if ((sizeof(long) < sizeof(long))) { - __PYX_VERIFY_RETURN_INT(long, long, PyInt_AS_LONG(x)) - } else { - long val = PyInt_AS_LONG(x); - if (is_unsigned && unlikely(val < 0)) { - goto raise_neg_overflow; - } - return (long) val; - } - } -#endif - if (unlikely(!PyLong_Check(x))) { - long val; - PyObject *tmp = __Pyx_PyNumber_IntOrLong(x); - if (!tmp) return (long) -1; - val = __Pyx_PyInt_As_long(tmp); - Py_DECREF(tmp); - return val; - } - if (is_unsigned) { -#if CYTHON_USE_PYLONG_INTERNALS - if (unlikely(__Pyx_PyLong_IsNeg(x))) { - goto raise_neg_overflow; - } else if (__Pyx_PyLong_IsCompact(x)) { - __PYX_VERIFY_RETURN_INT(long, __Pyx_compact_upylong, __Pyx_PyLong_CompactValueUnsigned(x)) - } else { - const digit* digits = __Pyx_PyLong_Digits(x); - assert(__Pyx_PyLong_DigitCount(x) > 1); - switch (__Pyx_PyLong_DigitCount(x)) { - case 2: - if ((8 * sizeof(long) > 1 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(long) >= 2 * PyLong_SHIFT)) { - return (long) (((((long)digits[1]) << PyLong_SHIFT) | (long)digits[0])); - } - } - break; - case 3: - if ((8 * sizeof(long) > 2 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(long) >= 3 * PyLong_SHIFT)) { - return (long) (((((((long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0])); - } - } - break; - case 4: - if ((8 * sizeof(long) > 3 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(long) >= 4 * PyLong_SHIFT)) { - return (long) (((((((((long)digits[3]) << PyLong_SHIFT) | (long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0])); - } - } - break; - } - } -#endif -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030C00A7 - if (unlikely(Py_SIZE(x) < 0)) { - goto raise_neg_overflow; - } -#else - { - int result = PyObject_RichCompareBool(x, Py_False, Py_LT); - if (unlikely(result < 0)) - return (long) -1; - if (unlikely(result == 1)) - goto raise_neg_overflow; - } -#endif - if ((sizeof(long) <= sizeof(unsigned long))) { - __PYX_VERIFY_RETURN_INT_EXC(long, unsigned long, PyLong_AsUnsignedLong(x)) -#ifdef HAVE_LONG_LONG - } else if ((sizeof(long) <= sizeof(unsigned PY_LONG_LONG))) { - __PYX_VERIFY_RETURN_INT_EXC(long, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong(x)) -#endif - } - } else { -#if CYTHON_USE_PYLONG_INTERNALS - if (__Pyx_PyLong_IsCompact(x)) { - __PYX_VERIFY_RETURN_INT(long, __Pyx_compact_pylong, __Pyx_PyLong_CompactValue(x)) - } else { - const digit* digits = __Pyx_PyLong_Digits(x); - assert(__Pyx_PyLong_DigitCount(x) > 1); - switch (__Pyx_PyLong_SignedDigitCount(x)) { - case -2: - if ((8 * sizeof(long) - 1 > 1 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(long, long, -(long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(long) - 1 > 2 * PyLong_SHIFT)) { - return (long) (((long)-1)*(((((long)digits[1]) << PyLong_SHIFT) | (long)digits[0]))); - } - } - break; - case 2: - if ((8 * sizeof(long) > 1 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(long) - 1 > 2 * PyLong_SHIFT)) { - return (long) ((((((long)digits[1]) << PyLong_SHIFT) | (long)digits[0]))); - } - } - break; - case -3: - if ((8 * sizeof(long) - 1 > 2 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(long, long, -(long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(long) - 1 > 3 * PyLong_SHIFT)) { - return (long) (((long)-1)*(((((((long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0]))); - } - } - break; - case 3: - if ((8 * sizeof(long) > 2 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 3 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(long) - 1 > 3 * PyLong_SHIFT)) { - return (long) ((((((((long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0]))); - } - } - break; - case -4: - if ((8 * sizeof(long) - 1 > 3 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(long, long, -(long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(long) - 1 > 4 * PyLong_SHIFT)) { - return (long) (((long)-1)*(((((((((long)digits[3]) << PyLong_SHIFT) | (long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0]))); - } - } - break; - case 4: - if ((8 * sizeof(long) > 3 * PyLong_SHIFT)) { - if ((8 * sizeof(unsigned long) > 4 * PyLong_SHIFT)) { - __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]))) - } else if ((8 * sizeof(long) - 1 > 4 * PyLong_SHIFT)) { - return (long) ((((((((((long)digits[3]) << PyLong_SHIFT) | (long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0]))); - } - } - break; - } - } -#endif - if ((sizeof(long) <= sizeof(long))) { - __PYX_VERIFY_RETURN_INT_EXC(long, long, PyLong_AsLong(x)) -#ifdef HAVE_LONG_LONG - } else if ((sizeof(long) <= sizeof(PY_LONG_LONG))) { - __PYX_VERIFY_RETURN_INT_EXC(long, PY_LONG_LONG, PyLong_AsLongLong(x)) -#endif - } - } - { - long val; - int ret = -1; -#if PY_VERSION_HEX >= 0x030d00A6 && !CYTHON_COMPILING_IN_LIMITED_API - Py_ssize_t bytes_copied = PyLong_AsNativeBytes( - x, &val, sizeof(val), Py_ASNATIVEBYTES_NATIVE_ENDIAN | (is_unsigned ? Py_ASNATIVEBYTES_UNSIGNED_BUFFER | Py_ASNATIVEBYTES_REJECT_NEGATIVE : 0)); - if (unlikely(bytes_copied == -1)) { - } else if (unlikely(bytes_copied > (Py_ssize_t) sizeof(val))) { - goto raise_overflow; - } else { - ret = 0; - } -#elif PY_VERSION_HEX < 0x030d0000 && !(CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API) || defined(_PyLong_AsByteArray) - int one = 1; int is_little = (int)*(unsigned char *)&one; - unsigned char *bytes = (unsigned char *)&val; - ret = _PyLong_AsByteArray((PyLongObject *)x, - bytes, sizeof(val), - is_little, !is_unsigned); -#else - PyObject *v; - PyObject *stepval = NULL, *mask = NULL, *shift = NULL; - int bits, remaining_bits, is_negative = 0; - int chunk_size = (sizeof(long) < 8) ? 30 : 62; - if (likely(PyLong_CheckExact(x))) { - v = __Pyx_NewRef(x); - } else { - v = PyNumber_Long(x); - if (unlikely(!v)) return (long) -1; - assert(PyLong_CheckExact(v)); - } - { - int result = PyObject_RichCompareBool(v, Py_False, Py_LT); - if (unlikely(result < 0)) { - Py_DECREF(v); - return (long) -1; - } - is_negative = result == 1; - } - if (is_unsigned && unlikely(is_negative)) { - Py_DECREF(v); - goto raise_neg_overflow; - } else if (is_negative) { - stepval = PyNumber_Invert(v); - Py_DECREF(v); - if (unlikely(!stepval)) - return (long) -1; - } else { - stepval = v; - } - v = NULL; - val = (long) 0; - mask = PyLong_FromLong((1L << chunk_size) - 1); if (unlikely(!mask)) goto done; - shift = PyLong_FromLong(chunk_size); if (unlikely(!shift)) goto done; - for (bits = 0; bits < (int) sizeof(long) * 8 - chunk_size; bits += chunk_size) { - PyObject *tmp, *digit; - long idigit; - digit = PyNumber_And(stepval, mask); - if (unlikely(!digit)) goto done; - idigit = PyLong_AsLong(digit); - Py_DECREF(digit); - if (unlikely(idigit < 0)) goto done; - val |= ((long) idigit) << bits; - tmp = PyNumber_Rshift(stepval, shift); - if (unlikely(!tmp)) goto done; - Py_DECREF(stepval); stepval = tmp; - } - Py_DECREF(shift); shift = NULL; - Py_DECREF(mask); mask = NULL; - { - long idigit = PyLong_AsLong(stepval); - if (unlikely(idigit < 0)) goto done; - remaining_bits = ((int) sizeof(long) * 8) - bits - (is_unsigned ? 0 : 1); - if (unlikely(idigit >= (1L << remaining_bits))) - goto raise_overflow; - val |= ((long) idigit) << bits; - } - if (!is_unsigned) { - if (unlikely(val & (((long) 1) << (sizeof(long) * 8 - 1)))) - goto raise_overflow; - if (is_negative) - val = ~val; - } - ret = 0; - done: - Py_XDECREF(shift); - Py_XDECREF(mask); - Py_XDECREF(stepval); -#endif - if (unlikely(ret)) - return (long) -1; - return val; - } -raise_overflow: - PyErr_SetString(PyExc_OverflowError, - "value too large to convert to long"); - return (long) -1; -raise_neg_overflow: - PyErr_SetString(PyExc_OverflowError, - "can't convert negative value to long"); - return (long) -1; -} - -/* FormatTypeName */ -#if CYTHON_COMPILING_IN_LIMITED_API -static __Pyx_TypeName -__Pyx_PyType_GetName(PyTypeObject* tp) -{ - PyObject *name = __Pyx_PyObject_GetAttrStr((PyObject *)tp, - __pyx_n_s_name); - if (unlikely(name == NULL) || unlikely(!PyUnicode_Check(name))) { - PyErr_Clear(); - Py_XDECREF(name); - name = __Pyx_NewRef(__pyx_n_s__9); - } - return name; -} -#endif - -/* SwapException */ -#if CYTHON_FAST_THREAD_STATE -static CYTHON_INLINE void __Pyx__ExceptionSwap(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) { - PyObject *tmp_type, *tmp_value, *tmp_tb; - #if CYTHON_USE_EXC_INFO_STACK && PY_VERSION_HEX >= 0x030B00a4 - _PyErr_StackItem *exc_info = tstate->exc_info; - tmp_value = exc_info->exc_value; - exc_info->exc_value = *value; - if (tmp_value == NULL || tmp_value == Py_None) { - Py_XDECREF(tmp_value); - tmp_value = NULL; - tmp_type = NULL; - tmp_tb = NULL; - } else { - tmp_type = (PyObject*) Py_TYPE(tmp_value); - Py_INCREF(tmp_type); - #if CYTHON_COMPILING_IN_CPYTHON - tmp_tb = ((PyBaseExceptionObject*) tmp_value)->traceback; - Py_XINCREF(tmp_tb); - #else - tmp_tb = PyException_GetTraceback(tmp_value); - #endif - } - #elif CYTHON_USE_EXC_INFO_STACK - _PyErr_StackItem *exc_info = tstate->exc_info; - tmp_type = exc_info->exc_type; - tmp_value = exc_info->exc_value; - tmp_tb = exc_info->exc_traceback; - exc_info->exc_type = *type; - exc_info->exc_value = *value; - exc_info->exc_traceback = *tb; - #else - tmp_type = tstate->exc_type; - tmp_value = tstate->exc_value; - tmp_tb = tstate->exc_traceback; - tstate->exc_type = *type; - tstate->exc_value = *value; - tstate->exc_traceback = *tb; - #endif - *type = tmp_type; - *value = tmp_value; - *tb = tmp_tb; -} -#else -static CYTHON_INLINE void __Pyx_ExceptionSwap(PyObject **type, PyObject **value, PyObject **tb) { - PyObject *tmp_type, *tmp_value, *tmp_tb; - PyErr_GetExcInfo(&tmp_type, &tmp_value, &tmp_tb); - PyErr_SetExcInfo(*type, *value, *tb); - *type = tmp_type; - *value = tmp_value; - *tb = tmp_tb; -} -#endif - -/* PyObjectCall2Args */ -static CYTHON_INLINE PyObject* __Pyx_PyObject_Call2Args(PyObject* function, PyObject* arg1, PyObject* arg2) { - PyObject *args[3] = {NULL, arg1, arg2}; - return __Pyx_PyObject_FastCall(function, args+1, 2 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET); -} - -/* PyObjectCallMethod1 */ -#if !(CYTHON_VECTORCALL && __PYX_LIMITED_VERSION_HEX >= 0x030C00A2) -static PyObject* __Pyx__PyObject_CallMethod1(PyObject* method, PyObject* arg) { - PyObject *result = __Pyx_PyObject_CallOneArg(method, arg); - Py_DECREF(method); - return result; -} -#endif -static PyObject* __Pyx_PyObject_CallMethod1(PyObject* obj, PyObject* method_name, PyObject* arg) { -#if CYTHON_VECTORCALL && __PYX_LIMITED_VERSION_HEX >= 0x030C00A2 - PyObject *args[2] = {obj, arg}; - (void) __Pyx_PyObject_GetMethod; - (void) __Pyx_PyObject_CallOneArg; - (void) __Pyx_PyObject_Call2Args; - return PyObject_VectorcallMethod(method_name, args, 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); -#else - PyObject *method = NULL, *result; - int is_method = __Pyx_PyObject_GetMethod(obj, method_name, &method); - if (likely(is_method)) { - result = __Pyx_PyObject_Call2Args(method, obj, arg); - Py_DECREF(method); - return result; - } - if (unlikely(!method)) return NULL; - return __Pyx__PyObject_CallMethod1(method, arg); -#endif -} - -/* CoroutineBase */ -#include -#if PY_VERSION_HEX >= 0x030b00a6 - #ifndef Py_BUILD_CORE - #define Py_BUILD_CORE 1 - #endif - #include "internal/pycore_frame.h" -#endif -#define __Pyx_Coroutine_Undelegate(gen) Py_CLEAR((gen)->yieldfrom) -static int __Pyx_PyGen__FetchStopIterationValue(PyThreadState *__pyx_tstate, PyObject **pvalue) { - PyObject *et, *ev, *tb; - PyObject *value = NULL; - CYTHON_UNUSED_VAR(__pyx_tstate); - __Pyx_ErrFetch(&et, &ev, &tb); - if (!et) { - Py_XDECREF(tb); - Py_XDECREF(ev); - Py_INCREF(Py_None); - *pvalue = Py_None; - return 0; - } - if (likely(et == PyExc_StopIteration)) { - if (!ev) { - Py_INCREF(Py_None); - value = Py_None; - } -#if PY_VERSION_HEX >= 0x030300A0 - else if (likely(__Pyx_IS_TYPE(ev, (PyTypeObject*)PyExc_StopIteration))) { - value = ((PyStopIterationObject *)ev)->value; - Py_INCREF(value); - Py_DECREF(ev); - } -#endif - else if (unlikely(PyTuple_Check(ev))) { - if (PyTuple_GET_SIZE(ev) >= 1) { -#if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS - value = PyTuple_GET_ITEM(ev, 0); - Py_INCREF(value); -#else - value = PySequence_ITEM(ev, 0); -#endif - } else { - Py_INCREF(Py_None); - value = Py_None; - } - Py_DECREF(ev); - } - else if (!__Pyx_TypeCheck(ev, (PyTypeObject*)PyExc_StopIteration)) { - value = ev; - } - if (likely(value)) { - Py_XDECREF(tb); - Py_DECREF(et); - *pvalue = value; - return 0; - } - } else if (!__Pyx_PyErr_GivenExceptionMatches(et, PyExc_StopIteration)) { - __Pyx_ErrRestore(et, ev, tb); - return -1; - } - PyErr_NormalizeException(&et, &ev, &tb); - if (unlikely(!PyObject_TypeCheck(ev, (PyTypeObject*)PyExc_StopIteration))) { - __Pyx_ErrRestore(et, ev, tb); - return -1; - } - Py_XDECREF(tb); - Py_DECREF(et); -#if PY_VERSION_HEX >= 0x030300A0 - value = ((PyStopIterationObject *)ev)->value; - Py_INCREF(value); - Py_DECREF(ev); -#else - { - PyObject* args = __Pyx_PyObject_GetAttrStr(ev, __pyx_n_s_args); - Py_DECREF(ev); - if (likely(args)) { - value = PySequence_GetItem(args, 0); - Py_DECREF(args); - } - if (unlikely(!value)) { - __Pyx_ErrRestore(NULL, NULL, NULL); - Py_INCREF(Py_None); - value = Py_None; - } - } -#endif - *pvalue = value; - return 0; -} -static CYTHON_INLINE -void __Pyx_Coroutine_ExceptionClear(__Pyx_ExcInfoStruct *exc_state) { -#if PY_VERSION_HEX >= 0x030B00a4 - Py_CLEAR(exc_state->exc_value); -#else - PyObject *t, *v, *tb; - t = exc_state->exc_type; - v = exc_state->exc_value; - tb = exc_state->exc_traceback; - exc_state->exc_type = NULL; - exc_state->exc_value = NULL; - exc_state->exc_traceback = NULL; - Py_XDECREF(t); - Py_XDECREF(v); - Py_XDECREF(tb); -#endif -} -#define __Pyx_Coroutine_AlreadyRunningError(gen) (__Pyx__Coroutine_AlreadyRunningError(gen), (PyObject*)NULL) -static void __Pyx__Coroutine_AlreadyRunningError(__pyx_CoroutineObject *gen) { - const char *msg; - CYTHON_MAYBE_UNUSED_VAR(gen); - if ((0)) { - #ifdef __Pyx_Coroutine_USED - } else if (__Pyx_Coroutine_Check((PyObject*)gen)) { - msg = "coroutine already executing"; - #endif - #ifdef __Pyx_AsyncGen_USED - } else if (__Pyx_AsyncGen_CheckExact((PyObject*)gen)) { - msg = "async generator already executing"; - #endif - } else { - msg = "generator already executing"; - } - PyErr_SetString(PyExc_ValueError, msg); -} -#define __Pyx_Coroutine_NotStartedError(gen) (__Pyx__Coroutine_NotStartedError(gen), (PyObject*)NULL) -static void __Pyx__Coroutine_NotStartedError(PyObject *gen) { - const char *msg; - CYTHON_MAYBE_UNUSED_VAR(gen); - if ((0)) { - #ifdef __Pyx_Coroutine_USED - } else if (__Pyx_Coroutine_Check(gen)) { - msg = "can't send non-None value to a just-started coroutine"; - #endif - #ifdef __Pyx_AsyncGen_USED - } else if (__Pyx_AsyncGen_CheckExact(gen)) { - msg = "can't send non-None value to a just-started async generator"; - #endif - } else { - msg = "can't send non-None value to a just-started generator"; - } - PyErr_SetString(PyExc_TypeError, msg); -} -#define __Pyx_Coroutine_AlreadyTerminatedError(gen, value, closing) (__Pyx__Coroutine_AlreadyTerminatedError(gen, value, closing), (PyObject*)NULL) -static void __Pyx__Coroutine_AlreadyTerminatedError(PyObject *gen, PyObject *value, int closing) { - CYTHON_MAYBE_UNUSED_VAR(gen); - CYTHON_MAYBE_UNUSED_VAR(closing); - #ifdef __Pyx_Coroutine_USED - if (!closing && __Pyx_Coroutine_Check(gen)) { - PyErr_SetString(PyExc_RuntimeError, "cannot reuse already awaited coroutine"); - } else - #endif - if (value) { - #ifdef __Pyx_AsyncGen_USED - if (__Pyx_AsyncGen_CheckExact(gen)) - PyErr_SetNone(__Pyx_PyExc_StopAsyncIteration); - else - #endif - PyErr_SetNone(PyExc_StopIteration); - } -} -static -PyObject *__Pyx_Coroutine_SendEx(__pyx_CoroutineObject *self, PyObject *value, int closing) { - __Pyx_PyThreadState_declare - PyThreadState *tstate; - __Pyx_ExcInfoStruct *exc_state; - PyObject *retval; - assert(!self->is_running); - if (unlikely(self->resume_label == 0)) { - if (unlikely(value && value != Py_None)) { - return __Pyx_Coroutine_NotStartedError((PyObject*)self); - } - } - if (unlikely(self->resume_label == -1)) { - return __Pyx_Coroutine_AlreadyTerminatedError((PyObject*)self, value, closing); - } -#if CYTHON_FAST_THREAD_STATE - __Pyx_PyThreadState_assign - tstate = __pyx_tstate; -#else - tstate = __Pyx_PyThreadState_Current; -#endif - exc_state = &self->gi_exc_state; - if (exc_state->exc_value) { - #if CYTHON_COMPILING_IN_PYPY - #else - PyObject *exc_tb; - #if PY_VERSION_HEX >= 0x030B00a4 && !CYTHON_COMPILING_IN_CPYTHON - exc_tb = PyException_GetTraceback(exc_state->exc_value); - #elif PY_VERSION_HEX >= 0x030B00a4 - exc_tb = ((PyBaseExceptionObject*) exc_state->exc_value)->traceback; - #else - exc_tb = exc_state->exc_traceback; - #endif - if (exc_tb) { - PyTracebackObject *tb = (PyTracebackObject *) exc_tb; - PyFrameObject *f = tb->tb_frame; - assert(f->f_back == NULL); - #if PY_VERSION_HEX >= 0x030B00A1 - f->f_back = PyThreadState_GetFrame(tstate); - #else - Py_XINCREF(tstate->frame); - f->f_back = tstate->frame; - #endif - #if PY_VERSION_HEX >= 0x030B00a4 && !CYTHON_COMPILING_IN_CPYTHON - Py_DECREF(exc_tb); - #endif - } - #endif - } -#if CYTHON_USE_EXC_INFO_STACK - exc_state->previous_item = tstate->exc_info; - tstate->exc_info = exc_state; -#else - if (exc_state->exc_type) { - __Pyx_ExceptionSwap(&exc_state->exc_type, &exc_state->exc_value, &exc_state->exc_traceback); - } else { - __Pyx_Coroutine_ExceptionClear(exc_state); - __Pyx_ExceptionSave(&exc_state->exc_type, &exc_state->exc_value, &exc_state->exc_traceback); - } -#endif - self->is_running = 1; - retval = self->body(self, tstate, value); - self->is_running = 0; -#if CYTHON_USE_EXC_INFO_STACK - exc_state = &self->gi_exc_state; - tstate->exc_info = exc_state->previous_item; - exc_state->previous_item = NULL; - __Pyx_Coroutine_ResetFrameBackpointer(exc_state); -#endif - return retval; -} -static CYTHON_INLINE void __Pyx_Coroutine_ResetFrameBackpointer(__Pyx_ExcInfoStruct *exc_state) { -#if CYTHON_COMPILING_IN_PYPY - CYTHON_UNUSED_VAR(exc_state); -#else - PyObject *exc_tb; - #if PY_VERSION_HEX >= 0x030B00a4 - if (!exc_state->exc_value) return; - exc_tb = PyException_GetTraceback(exc_state->exc_value); - #else - exc_tb = exc_state->exc_traceback; - #endif - if (likely(exc_tb)) { - PyTracebackObject *tb = (PyTracebackObject *) exc_tb; - PyFrameObject *f = tb->tb_frame; - Py_CLEAR(f->f_back); - #if PY_VERSION_HEX >= 0x030B00a4 - Py_DECREF(exc_tb); - #endif - } -#endif -} -static CYTHON_INLINE -PyObject *__Pyx_Coroutine_MethodReturn(PyObject* gen, PyObject *retval) { - CYTHON_MAYBE_UNUSED_VAR(gen); - if (unlikely(!retval)) { - __Pyx_PyThreadState_declare - __Pyx_PyThreadState_assign - if (!__Pyx_PyErr_Occurred()) { - PyObject *exc = PyExc_StopIteration; - #ifdef __Pyx_AsyncGen_USED - if (__Pyx_AsyncGen_CheckExact(gen)) - exc = __Pyx_PyExc_StopAsyncIteration; - #endif - __Pyx_PyErr_SetNone(exc); - } - } - return retval; -} -#if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03030000 && (defined(__linux__) || PY_VERSION_HEX >= 0x030600B3) -static CYTHON_INLINE -PyObject *__Pyx_PyGen_Send(PyGenObject *gen, PyObject *arg) { -#if PY_VERSION_HEX <= 0x030A00A1 - return _PyGen_Send(gen, arg); -#else - PyObject *result; - if (PyIter_Send((PyObject*)gen, arg ? arg : Py_None, &result) == PYGEN_RETURN) { - if (PyAsyncGen_CheckExact(gen)) { - assert(result == Py_None); - PyErr_SetNone(PyExc_StopAsyncIteration); - } - else if (result == Py_None) { - PyErr_SetNone(PyExc_StopIteration); - } - else { -#if PY_VERSION_HEX < 0x030d00A1 - _PyGen_SetStopIterationValue(result); -#else - if (!PyTuple_Check(result) && !PyExceptionInstance_Check(result)) { - PyErr_SetObject(PyExc_StopIteration, result); - } else { - PyObject *exc = __Pyx_PyObject_CallOneArg(PyExc_StopIteration, result); - if (likely(exc != NULL)) { - PyErr_SetObject(PyExc_StopIteration, exc); - Py_DECREF(exc); - } - } -#endif - } - Py_DECREF(result); - result = NULL; - } - return result; -#endif -} -#endif -static CYTHON_INLINE -PyObject *__Pyx_Coroutine_FinishDelegation(__pyx_CoroutineObject *gen) { - PyObject *ret; - PyObject *val = NULL; - __Pyx_Coroutine_Undelegate(gen); - __Pyx_PyGen__FetchStopIterationValue(__Pyx_PyThreadState_Current, &val); - ret = __Pyx_Coroutine_SendEx(gen, val, 0); - Py_XDECREF(val); - return ret; -} -static PyObject *__Pyx_Coroutine_Send(PyObject *self, PyObject *value) { - PyObject *retval; - __pyx_CoroutineObject *gen = (__pyx_CoroutineObject*) self; - PyObject *yf = gen->yieldfrom; - if (unlikely(gen->is_running)) - return __Pyx_Coroutine_AlreadyRunningError(gen); - if (yf) { - PyObject *ret; - gen->is_running = 1; - #ifdef __Pyx_Generator_USED - if (__Pyx_Generator_CheckExact(yf)) { - ret = __Pyx_Coroutine_Send(yf, value); - } else - #endif - #ifdef __Pyx_Coroutine_USED - if (__Pyx_Coroutine_Check(yf)) { - ret = __Pyx_Coroutine_Send(yf, value); - } else - #endif - #ifdef __Pyx_AsyncGen_USED - if (__pyx_PyAsyncGenASend_CheckExact(yf)) { - ret = __Pyx_async_gen_asend_send(yf, value); - } else - #endif - #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03030000 && (defined(__linux__) || PY_VERSION_HEX >= 0x030600B3) - if (PyGen_CheckExact(yf)) { - ret = __Pyx_PyGen_Send((PyGenObject*)yf, value == Py_None ? NULL : value); - } else - #endif - #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03050000 && defined(PyCoro_CheckExact) && (defined(__linux__) || PY_VERSION_HEX >= 0x030600B3) - if (PyCoro_CheckExact(yf)) { - ret = __Pyx_PyGen_Send((PyGenObject*)yf, value == Py_None ? NULL : value); - } else - #endif - { - if (value == Py_None) - ret = __Pyx_PyObject_GetIterNextFunc(yf)(yf); - else - ret = __Pyx_PyObject_CallMethod1(yf, __pyx_n_s_send, value); - } - gen->is_running = 0; - if (likely(ret)) { - return ret; - } - retval = __Pyx_Coroutine_FinishDelegation(gen); - } else { - retval = __Pyx_Coroutine_SendEx(gen, value, 0); - } - return __Pyx_Coroutine_MethodReturn(self, retval); -} -static int __Pyx_Coroutine_CloseIter(__pyx_CoroutineObject *gen, PyObject *yf) { - PyObject *retval = NULL; - int err = 0; - #ifdef __Pyx_Generator_USED - if (__Pyx_Generator_CheckExact(yf)) { - retval = __Pyx_Coroutine_Close(yf); - if (!retval) - return -1; - } else - #endif - #ifdef __Pyx_Coroutine_USED - if (__Pyx_Coroutine_Check(yf)) { - retval = __Pyx_Coroutine_Close(yf); - if (!retval) - return -1; - } else - if (__Pyx_CoroutineAwait_CheckExact(yf)) { - retval = __Pyx_CoroutineAwait_Close((__pyx_CoroutineAwaitObject*)yf, NULL); - if (!retval) - return -1; - } else - #endif - #ifdef __Pyx_AsyncGen_USED - if (__pyx_PyAsyncGenASend_CheckExact(yf)) { - retval = __Pyx_async_gen_asend_close(yf, NULL); - } else - if (__pyx_PyAsyncGenAThrow_CheckExact(yf)) { - retval = __Pyx_async_gen_athrow_close(yf, NULL); - } else - #endif - { - PyObject *meth; - gen->is_running = 1; - meth = __Pyx_PyObject_GetAttrStrNoError(yf, __pyx_n_s_close); - if (unlikely(!meth)) { - if (unlikely(PyErr_Occurred())) { - PyErr_WriteUnraisable(yf); - } - } else { - retval = __Pyx_PyObject_CallNoArg(meth); - Py_DECREF(meth); - if (unlikely(!retval)) - err = -1; - } - gen->is_running = 0; - } - Py_XDECREF(retval); - return err; -} -static PyObject *__Pyx_Generator_Next(PyObject *self) { - __pyx_CoroutineObject *gen = (__pyx_CoroutineObject*) self; - PyObject *yf = gen->yieldfrom; - if (unlikely(gen->is_running)) - return __Pyx_Coroutine_AlreadyRunningError(gen); - if (yf) { - PyObject *ret; - gen->is_running = 1; - #ifdef __Pyx_Generator_USED - if (__Pyx_Generator_CheckExact(yf)) { - ret = __Pyx_Generator_Next(yf); - } else - #endif - #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x03030000 && (defined(__linux__) || PY_VERSION_HEX >= 0x030600B3) - if (PyGen_CheckExact(yf)) { - ret = __Pyx_PyGen_Send((PyGenObject*)yf, NULL); - } else - #endif - #ifdef __Pyx_Coroutine_USED - if (__Pyx_Coroutine_Check(yf)) { - ret = __Pyx_Coroutine_Send(yf, Py_None); - } else - #endif - ret = __Pyx_PyObject_GetIterNextFunc(yf)(yf); - gen->is_running = 0; - if (likely(ret)) { - return ret; - } - return __Pyx_Coroutine_FinishDelegation(gen); - } - return __Pyx_Coroutine_SendEx(gen, Py_None, 0); -} -static PyObject *__Pyx_Coroutine_Close_Method(PyObject *self, PyObject *arg) { - CYTHON_UNUSED_VAR(arg); - return __Pyx_Coroutine_Close(self); -} -static PyObject *__Pyx_Coroutine_Close(PyObject *self) { - __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self; - PyObject *retval, *raised_exception; - PyObject *yf = gen->yieldfrom; - int err = 0; - if (unlikely(gen->is_running)) - return __Pyx_Coroutine_AlreadyRunningError(gen); - if (yf) { - Py_INCREF(yf); - err = __Pyx_Coroutine_CloseIter(gen, yf); - __Pyx_Coroutine_Undelegate(gen); - Py_DECREF(yf); - } - if (err == 0) - PyErr_SetNone(PyExc_GeneratorExit); - retval = __Pyx_Coroutine_SendEx(gen, NULL, 1); - if (unlikely(retval)) { - const char *msg; - Py_DECREF(retval); - if ((0)) { - #ifdef __Pyx_Coroutine_USED - } else if (__Pyx_Coroutine_Check(self)) { - msg = "coroutine ignored GeneratorExit"; - #endif - #ifdef __Pyx_AsyncGen_USED - } else if (__Pyx_AsyncGen_CheckExact(self)) { -#if PY_VERSION_HEX < 0x03060000 - msg = "async generator ignored GeneratorExit - might require Python 3.6+ finalisation (PEP 525)"; -#else - msg = "async generator ignored GeneratorExit"; -#endif - #endif - } else { - msg = "generator ignored GeneratorExit"; - } - PyErr_SetString(PyExc_RuntimeError, msg); - return NULL; - } - raised_exception = PyErr_Occurred(); - if (likely(!raised_exception || __Pyx_PyErr_GivenExceptionMatches2(raised_exception, PyExc_GeneratorExit, PyExc_StopIteration))) { - if (raised_exception) PyErr_Clear(); - Py_INCREF(Py_None); - return Py_None; - } - return NULL; -} -static PyObject *__Pyx__Coroutine_Throw(PyObject *self, PyObject *typ, PyObject *val, PyObject *tb, - PyObject *args, int close_on_genexit) { - __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self; - PyObject *yf = gen->yieldfrom; - if (unlikely(gen->is_running)) - return __Pyx_Coroutine_AlreadyRunningError(gen); - if (yf) { - PyObject *ret; - Py_INCREF(yf); - if (__Pyx_PyErr_GivenExceptionMatches(typ, PyExc_GeneratorExit) && close_on_genexit) { - int err = __Pyx_Coroutine_CloseIter(gen, yf); - Py_DECREF(yf); - __Pyx_Coroutine_Undelegate(gen); - if (err < 0) - return __Pyx_Coroutine_MethodReturn(self, __Pyx_Coroutine_SendEx(gen, NULL, 0)); - goto throw_here; - } - gen->is_running = 1; - if (0 - #ifdef __Pyx_Generator_USED - || __Pyx_Generator_CheckExact(yf) - #endif - #ifdef __Pyx_Coroutine_USED - || __Pyx_Coroutine_Check(yf) - #endif - ) { - ret = __Pyx__Coroutine_Throw(yf, typ, val, tb, args, close_on_genexit); - #ifdef __Pyx_Coroutine_USED - } else if (__Pyx_CoroutineAwait_CheckExact(yf)) { - ret = __Pyx__Coroutine_Throw(((__pyx_CoroutineAwaitObject*)yf)->coroutine, typ, val, tb, args, close_on_genexit); - #endif - } else { - PyObject *meth = __Pyx_PyObject_GetAttrStrNoError(yf, __pyx_n_s_throw); - if (unlikely(!meth)) { - Py_DECREF(yf); - if (unlikely(PyErr_Occurred())) { - gen->is_running = 0; - return NULL; - } - __Pyx_Coroutine_Undelegate(gen); - gen->is_running = 0; - goto throw_here; - } - if (likely(args)) { - ret = __Pyx_PyObject_Call(meth, args, NULL); - } else { - PyObject *cargs[4] = {NULL, typ, val, tb}; - ret = __Pyx_PyObject_FastCall(meth, cargs+1, 3 | __Pyx_PY_VECTORCALL_ARGUMENTS_OFFSET); - } - Py_DECREF(meth); - } - gen->is_running = 0; - Py_DECREF(yf); - if (!ret) { - ret = __Pyx_Coroutine_FinishDelegation(gen); - } - return __Pyx_Coroutine_MethodReturn(self, ret); - } -throw_here: - __Pyx_Raise(typ, val, tb, NULL); - return __Pyx_Coroutine_MethodReturn(self, __Pyx_Coroutine_SendEx(gen, NULL, 0)); -} -static PyObject *__Pyx_Coroutine_Throw(PyObject *self, PyObject *args) { - PyObject *typ; - PyObject *val = NULL; - PyObject *tb = NULL; - if (unlikely(!PyArg_UnpackTuple(args, (char *)"throw", 1, 3, &typ, &val, &tb))) - return NULL; - return __Pyx__Coroutine_Throw(self, typ, val, tb, args, 1); -} -static CYTHON_INLINE int __Pyx_Coroutine_traverse_excstate(__Pyx_ExcInfoStruct *exc_state, visitproc visit, void *arg) { -#if PY_VERSION_HEX >= 0x030B00a4 - Py_VISIT(exc_state->exc_value); -#else - Py_VISIT(exc_state->exc_type); - Py_VISIT(exc_state->exc_value); - Py_VISIT(exc_state->exc_traceback); -#endif - return 0; -} -static int __Pyx_Coroutine_traverse(__pyx_CoroutineObject *gen, visitproc visit, void *arg) { - Py_VISIT(gen->closure); - Py_VISIT(gen->classobj); - Py_VISIT(gen->yieldfrom); - return __Pyx_Coroutine_traverse_excstate(&gen->gi_exc_state, visit, arg); -} -static int __Pyx_Coroutine_clear(PyObject *self) { - __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self; - Py_CLEAR(gen->closure); - Py_CLEAR(gen->classobj); - Py_CLEAR(gen->yieldfrom); - __Pyx_Coroutine_ExceptionClear(&gen->gi_exc_state); -#ifdef __Pyx_AsyncGen_USED - if (__Pyx_AsyncGen_CheckExact(self)) { - Py_CLEAR(((__pyx_PyAsyncGenObject*)gen)->ag_finalizer); - } -#endif - Py_CLEAR(gen->gi_code); - Py_CLEAR(gen->gi_frame); - Py_CLEAR(gen->gi_name); - Py_CLEAR(gen->gi_qualname); - Py_CLEAR(gen->gi_modulename); - return 0; -} -static void __Pyx_Coroutine_dealloc(PyObject *self) { - __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self; - PyObject_GC_UnTrack(gen); - if (gen->gi_weakreflist != NULL) - PyObject_ClearWeakRefs(self); - if (gen->resume_label >= 0) { - PyObject_GC_Track(self); -#if PY_VERSION_HEX >= 0x030400a1 && CYTHON_USE_TP_FINALIZE - if (unlikely(PyObject_CallFinalizerFromDealloc(self))) -#else - Py_TYPE(gen)->tp_del(self); - if (unlikely(Py_REFCNT(self) > 0)) -#endif - { - return; - } - PyObject_GC_UnTrack(self); - } -#ifdef __Pyx_AsyncGen_USED - if (__Pyx_AsyncGen_CheckExact(self)) { - /* We have to handle this case for asynchronous generators - right here, because this code has to be between UNTRACK - and GC_Del. */ - Py_CLEAR(((__pyx_PyAsyncGenObject*)self)->ag_finalizer); - } -#endif - __Pyx_Coroutine_clear(self); - __Pyx_PyHeapTypeObject_GC_Del(gen); -} -static void __Pyx_Coroutine_del(PyObject *self) { - PyObject *error_type, *error_value, *error_traceback; - __pyx_CoroutineObject *gen = (__pyx_CoroutineObject *) self; - __Pyx_PyThreadState_declare - if (gen->resume_label < 0) { - return; - } -#if !CYTHON_USE_TP_FINALIZE - assert(self->ob_refcnt == 0); - __Pyx_SET_REFCNT(self, 1); -#endif - __Pyx_PyThreadState_assign - __Pyx_ErrFetch(&error_type, &error_value, &error_traceback); -#ifdef __Pyx_AsyncGen_USED - if (__Pyx_AsyncGen_CheckExact(self)) { - __pyx_PyAsyncGenObject *agen = (__pyx_PyAsyncGenObject*)self; - PyObject *finalizer = agen->ag_finalizer; - if (finalizer && !agen->ag_closed) { - PyObject *res = __Pyx_PyObject_CallOneArg(finalizer, self); - if (unlikely(!res)) { - PyErr_WriteUnraisable(self); - } else { - Py_DECREF(res); - } - __Pyx_ErrRestore(error_type, error_value, error_traceback); - return; - } - } -#endif - if (unlikely(gen->resume_label == 0 && !error_value)) { -#ifdef __Pyx_Coroutine_USED -#ifdef __Pyx_Generator_USED - if (!__Pyx_Generator_CheckExact(self)) -#endif - { - PyObject_GC_UnTrack(self); -#if PY_MAJOR_VERSION >= 3 || defined(PyErr_WarnFormat) - if (unlikely(PyErr_WarnFormat(PyExc_RuntimeWarning, 1, "coroutine '%.50S' was never awaited", gen->gi_qualname) < 0)) - PyErr_WriteUnraisable(self); -#else - {PyObject *msg; - char *cmsg; - #if CYTHON_COMPILING_IN_PYPY - msg = NULL; - cmsg = (char*) "coroutine was never awaited"; - #else - char *cname; - PyObject *qualname; - qualname = gen->gi_qualname; - cname = PyString_AS_STRING(qualname); - msg = PyString_FromFormat("coroutine '%.50s' was never awaited", cname); - if (unlikely(!msg)) { - PyErr_Clear(); - cmsg = (char*) "coroutine was never awaited"; - } else { - cmsg = PyString_AS_STRING(msg); - } - #endif - if (unlikely(PyErr_WarnEx(PyExc_RuntimeWarning, cmsg, 1) < 0)) - PyErr_WriteUnraisable(self); - Py_XDECREF(msg);} -#endif - PyObject_GC_Track(self); - } -#endif - } else { - PyObject *res = __Pyx_Coroutine_Close(self); - if (unlikely(!res)) { - if (PyErr_Occurred()) - PyErr_WriteUnraisable(self); - } else { - Py_DECREF(res); - } - } - __Pyx_ErrRestore(error_type, error_value, error_traceback); -#if !CYTHON_USE_TP_FINALIZE - assert(Py_REFCNT(self) > 0); - if (likely(--self->ob_refcnt == 0)) { - return; - } - { - Py_ssize_t refcnt = Py_REFCNT(self); - _Py_NewReference(self); - __Pyx_SET_REFCNT(self, refcnt); - } -#if CYTHON_COMPILING_IN_CPYTHON - assert(PyType_IS_GC(Py_TYPE(self)) && - _Py_AS_GC(self)->gc.gc_refs != _PyGC_REFS_UNTRACKED); - _Py_DEC_REFTOTAL; -#endif -#ifdef COUNT_ALLOCS - --Py_TYPE(self)->tp_frees; - --Py_TYPE(self)->tp_allocs; -#endif -#endif -} -static PyObject * -__Pyx_Coroutine_get_name(__pyx_CoroutineObject *self, void *context) -{ - PyObject *name = self->gi_name; - CYTHON_UNUSED_VAR(context); - if (unlikely(!name)) name = Py_None; - Py_INCREF(name); - return name; -} -static int -__Pyx_Coroutine_set_name(__pyx_CoroutineObject *self, PyObject *value, void *context) -{ - CYTHON_UNUSED_VAR(context); -#if PY_MAJOR_VERSION >= 3 - if (unlikely(value == NULL || !PyUnicode_Check(value))) -#else - if (unlikely(value == NULL || !PyString_Check(value))) -#endif - { - PyErr_SetString(PyExc_TypeError, - "__name__ must be set to a string object"); - return -1; - } - Py_INCREF(value); - __Pyx_Py_XDECREF_SET(self->gi_name, value); - return 0; -} -static PyObject * -__Pyx_Coroutine_get_qualname(__pyx_CoroutineObject *self, void *context) -{ - PyObject *name = self->gi_qualname; - CYTHON_UNUSED_VAR(context); - if (unlikely(!name)) name = Py_None; - Py_INCREF(name); - return name; -} -static int -__Pyx_Coroutine_set_qualname(__pyx_CoroutineObject *self, PyObject *value, void *context) -{ - CYTHON_UNUSED_VAR(context); -#if PY_MAJOR_VERSION >= 3 - if (unlikely(value == NULL || !PyUnicode_Check(value))) -#else - if (unlikely(value == NULL || !PyString_Check(value))) -#endif - { - PyErr_SetString(PyExc_TypeError, - "__qualname__ must be set to a string object"); - return -1; - } - Py_INCREF(value); - __Pyx_Py_XDECREF_SET(self->gi_qualname, value); - return 0; -} -static PyObject * -__Pyx_Coroutine_get_frame(__pyx_CoroutineObject *self, void *context) -{ - PyObject *frame = self->gi_frame; - CYTHON_UNUSED_VAR(context); - if (!frame) { - if (unlikely(!self->gi_code)) { - Py_RETURN_NONE; - } - frame = (PyObject *) PyFrame_New( - PyThreadState_Get(), /*PyThreadState *tstate,*/ - (PyCodeObject*) self->gi_code, /*PyCodeObject *code,*/ - __pyx_d, /*PyObject *globals,*/ - 0 /*PyObject *locals*/ - ); - if (unlikely(!frame)) - return NULL; - self->gi_frame = frame; - } - Py_INCREF(frame); - return frame; -} -static __pyx_CoroutineObject *__Pyx__Coroutine_New( - PyTypeObject* type, __pyx_coroutine_body_t body, PyObject *code, PyObject *closure, - PyObject *name, PyObject *qualname, PyObject *module_name) { - __pyx_CoroutineObject *gen = PyObject_GC_New(__pyx_CoroutineObject, type); - if (unlikely(!gen)) - return NULL; - return __Pyx__Coroutine_NewInit(gen, body, code, closure, name, qualname, module_name); -} -static __pyx_CoroutineObject *__Pyx__Coroutine_NewInit( - __pyx_CoroutineObject *gen, __pyx_coroutine_body_t body, PyObject *code, PyObject *closure, - PyObject *name, PyObject *qualname, PyObject *module_name) { - gen->body = body; - gen->closure = closure; - Py_XINCREF(closure); - gen->is_running = 0; - gen->resume_label = 0; - gen->classobj = NULL; - gen->yieldfrom = NULL; - #if PY_VERSION_HEX >= 0x030B00a4 - gen->gi_exc_state.exc_value = NULL; - #else - gen->gi_exc_state.exc_type = NULL; - gen->gi_exc_state.exc_value = NULL; - gen->gi_exc_state.exc_traceback = NULL; - #endif -#if CYTHON_USE_EXC_INFO_STACK - gen->gi_exc_state.previous_item = NULL; -#endif - gen->gi_weakreflist = NULL; - Py_XINCREF(qualname); - gen->gi_qualname = qualname; - Py_XINCREF(name); - gen->gi_name = name; - Py_XINCREF(module_name); - gen->gi_modulename = module_name; - Py_XINCREF(code); - gen->gi_code = code; - gen->gi_frame = NULL; - PyObject_GC_Track(gen); - return gen; -} - -/* PatchModuleWithCoroutine */ -static PyObject* __Pyx_Coroutine_patch_module(PyObject* module, const char* py_code) { -#if defined(__Pyx_Generator_USED) || defined(__Pyx_Coroutine_USED) - int result; - PyObject *globals, *result_obj; - globals = PyDict_New(); if (unlikely(!globals)) goto ignore; - result = PyDict_SetItemString(globals, "_cython_coroutine_type", - #ifdef __Pyx_Coroutine_USED - (PyObject*)__pyx_CoroutineType); - #else - Py_None); - #endif - if (unlikely(result < 0)) goto ignore; - result = PyDict_SetItemString(globals, "_cython_generator_type", - #ifdef __Pyx_Generator_USED - (PyObject*)__pyx_GeneratorType); - #else - Py_None); - #endif - if (unlikely(result < 0)) goto ignore; - if (unlikely(PyDict_SetItemString(globals, "_module", module) < 0)) goto ignore; - if (unlikely(PyDict_SetItemString(globals, "__builtins__", __pyx_b) < 0)) goto ignore; - result_obj = PyRun_String(py_code, Py_file_input, globals, globals); - if (unlikely(!result_obj)) goto ignore; - Py_DECREF(result_obj); - Py_DECREF(globals); - return module; -ignore: - Py_XDECREF(globals); - PyErr_WriteUnraisable(module); - if (unlikely(PyErr_WarnEx(PyExc_RuntimeWarning, "Cython module failed to patch module with custom type", 1) < 0)) { - Py_DECREF(module); - module = NULL; - } -#else - py_code++; -#endif - return module; -} - -/* PatchGeneratorABC */ -#ifndef CYTHON_REGISTER_ABCS -#define CYTHON_REGISTER_ABCS 1 -#endif -#if defined(__Pyx_Generator_USED) || defined(__Pyx_Coroutine_USED) -static PyObject* __Pyx_patch_abc_module(PyObject *module); -static PyObject* __Pyx_patch_abc_module(PyObject *module) { - module = __Pyx_Coroutine_patch_module( - module, "" -"if _cython_generator_type is not None:\n" -" try: Generator = _module.Generator\n" -" except AttributeError: pass\n" -" else: Generator.register(_cython_generator_type)\n" -"if _cython_coroutine_type is not None:\n" -" try: Coroutine = _module.Coroutine\n" -" except AttributeError: pass\n" -" else: Coroutine.register(_cython_coroutine_type)\n" - ); - return module; -} -#endif -static int __Pyx_patch_abc(void) { -#if defined(__Pyx_Generator_USED) || defined(__Pyx_Coroutine_USED) - static int abc_patched = 0; - if (CYTHON_REGISTER_ABCS && !abc_patched) { - PyObject *module; - module = PyImport_ImportModule((PY_MAJOR_VERSION >= 3) ? "collections.abc" : "collections"); - if (unlikely(!module)) { - PyErr_WriteUnraisable(NULL); - if (unlikely(PyErr_WarnEx(PyExc_RuntimeWarning, - ((PY_MAJOR_VERSION >= 3) ? - "Cython module failed to register with collections.abc module" : - "Cython module failed to register with collections module"), 1) < 0)) { - return -1; - } - } else { - module = __Pyx_patch_abc_module(module); - abc_patched = 1; - if (unlikely(!module)) - return -1; - Py_DECREF(module); - } - module = PyImport_ImportModule("backports_abc"); - if (module) { - module = __Pyx_patch_abc_module(module); - Py_XDECREF(module); - } - if (!module) { - PyErr_Clear(); - } - } -#else - if ((0)) __Pyx_Coroutine_patch_module(NULL, NULL); -#endif - return 0; -} - -/* Generator */ -static PyMethodDef __pyx_Generator_methods[] = { - {"send", (PyCFunction) __Pyx_Coroutine_Send, METH_O, - (char*) PyDoc_STR("send(arg) -> send 'arg' into generator,\nreturn next yielded value or raise StopIteration.")}, - {"throw", (PyCFunction) __Pyx_Coroutine_Throw, METH_VARARGS, - (char*) PyDoc_STR("throw(typ[,val[,tb]]) -> raise exception in generator,\nreturn next yielded value or raise StopIteration.")}, - {"close", (PyCFunction) __Pyx_Coroutine_Close_Method, METH_NOARGS, - (char*) PyDoc_STR("close() -> raise GeneratorExit inside generator.")}, - {0, 0, 0, 0} -}; -static PyMemberDef __pyx_Generator_memberlist[] = { - {(char *) "gi_running", T_BOOL, offsetof(__pyx_CoroutineObject, is_running), READONLY, NULL}, - {(char*) "gi_yieldfrom", T_OBJECT, offsetof(__pyx_CoroutineObject, yieldfrom), READONLY, - (char*) PyDoc_STR("object being iterated by 'yield from', or None")}, - {(char*) "gi_code", T_OBJECT, offsetof(__pyx_CoroutineObject, gi_code), READONLY, NULL}, - {(char *) "__module__", T_OBJECT, offsetof(__pyx_CoroutineObject, gi_modulename), 0, 0}, -#if CYTHON_USE_TYPE_SPECS - {(char *) "__weaklistoffset__", T_PYSSIZET, offsetof(__pyx_CoroutineObject, gi_weakreflist), READONLY, 0}, -#endif - {0, 0, 0, 0, 0} -}; -static PyGetSetDef __pyx_Generator_getsets[] = { - {(char *) "__name__", (getter)__Pyx_Coroutine_get_name, (setter)__Pyx_Coroutine_set_name, - (char*) PyDoc_STR("name of the generator"), 0}, - {(char *) "__qualname__", (getter)__Pyx_Coroutine_get_qualname, (setter)__Pyx_Coroutine_set_qualname, - (char*) PyDoc_STR("qualified name of the generator"), 0}, - {(char *) "gi_frame", (getter)__Pyx_Coroutine_get_frame, NULL, - (char*) PyDoc_STR("Frame of the generator"), 0}, - {0, 0, 0, 0, 0} -}; -#if CYTHON_USE_TYPE_SPECS -static PyType_Slot __pyx_GeneratorType_slots[] = { - {Py_tp_dealloc, (void *)__Pyx_Coroutine_dealloc}, - {Py_tp_traverse, (void *)__Pyx_Coroutine_traverse}, - {Py_tp_iter, (void *)PyObject_SelfIter}, - {Py_tp_iternext, (void *)__Pyx_Generator_Next}, - {Py_tp_methods, (void *)__pyx_Generator_methods}, - {Py_tp_members, (void *)__pyx_Generator_memberlist}, - {Py_tp_getset, (void *)__pyx_Generator_getsets}, - {Py_tp_getattro, (void *) __Pyx_PyObject_GenericGetAttrNoDict}, -#if CYTHON_USE_TP_FINALIZE - {Py_tp_finalize, (void *)__Pyx_Coroutine_del}, -#endif - {0, 0}, -}; -static PyType_Spec __pyx_GeneratorType_spec = { - __PYX_TYPE_MODULE_PREFIX "generator", - sizeof(__pyx_CoroutineObject), - 0, - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, - __pyx_GeneratorType_slots -}; -#else -static PyTypeObject __pyx_GeneratorType_type = { - PyVarObject_HEAD_INIT(0, 0) - __PYX_TYPE_MODULE_PREFIX "generator", - sizeof(__pyx_CoroutineObject), - 0, - (destructor) __Pyx_Coroutine_dealloc, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, - 0, - (traverseproc) __Pyx_Coroutine_traverse, - 0, - 0, - offsetof(__pyx_CoroutineObject, gi_weakreflist), - 0, - (iternextfunc) __Pyx_Generator_Next, - __pyx_Generator_methods, - __pyx_Generator_memberlist, - __pyx_Generator_getsets, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, -#if CYTHON_USE_TP_FINALIZE - 0, -#else - __Pyx_Coroutine_del, -#endif - 0, -#if CYTHON_USE_TP_FINALIZE - __Pyx_Coroutine_del, -#elif PY_VERSION_HEX >= 0x030400a1 - 0, -#endif -#if PY_VERSION_HEX >= 0x030800b1 && (!CYTHON_COMPILING_IN_PYPY || PYPY_VERSION_NUM >= 0x07030800) - 0, -#endif -#if __PYX_NEED_TP_PRINT_SLOT - 0, -#endif -#if PY_VERSION_HEX >= 0x030C0000 - 0, -#endif -#if PY_VERSION_HEX >= 0x030d00A4 - 0, -#endif -#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX >= 0x03090000 && PY_VERSION_HEX < 0x030a0000 - 0, -#endif -}; -#endif -static int __pyx_Generator_init(PyObject *module) { -#if CYTHON_USE_TYPE_SPECS - __pyx_GeneratorType = __Pyx_FetchCommonTypeFromSpec(module, &__pyx_GeneratorType_spec, NULL); -#else - CYTHON_UNUSED_VAR(module); - __pyx_GeneratorType_type.tp_getattro = __Pyx_PyObject_GenericGetAttrNoDict; - __pyx_GeneratorType_type.tp_iter = PyObject_SelfIter; - __pyx_GeneratorType = __Pyx_FetchCommonType(&__pyx_GeneratorType_type); -#endif - if (unlikely(!__pyx_GeneratorType)) { - return -1; - } - return 0; -} - -/* CheckBinaryVersion */ -static unsigned long __Pyx_get_runtime_version(void) { -#if __PYX_LIMITED_VERSION_HEX >= 0x030B00A4 - return Py_Version & ~0xFFUL; -#else - const char* rt_version = Py_GetVersion(); - unsigned long version = 0; - unsigned long factor = 0x01000000UL; - unsigned int digit = 0; - int i = 0; - while (factor) { - while ('0' <= rt_version[i] && rt_version[i] <= '9') { - digit = digit * 10 + (unsigned int) (rt_version[i] - '0'); - ++i; - } - version += factor * digit; - if (rt_version[i] != '.') - break; - digit = 0; - factor >>= 8; - ++i; - } - return version; -#endif -} -static int __Pyx_check_binary_version(unsigned long ct_version, unsigned long rt_version, int allow_newer) { - const unsigned long MAJOR_MINOR = 0xFFFF0000UL; - if ((rt_version & MAJOR_MINOR) == (ct_version & MAJOR_MINOR)) - return 0; - if (likely(allow_newer && (rt_version & MAJOR_MINOR) > (ct_version & MAJOR_MINOR))) - return 1; - { - char message[200]; - PyOS_snprintf(message, sizeof(message), - "compile time Python version %d.%d " - "of module '%.100s' " - "%s " - "runtime version %d.%d", - (int) (ct_version >> 24), (int) ((ct_version >> 16) & 0xFF), - __Pyx_MODULE_NAME, - (allow_newer) ? "was newer than" : "does not match", - (int) (rt_version >> 24), (int) ((rt_version >> 16) & 0xFF) - ); - return PyErr_WarnEx(NULL, message, 1); - } -} - -/* InitStrings */ -#if PY_MAJOR_VERSION >= 3 -static int __Pyx_InitString(__Pyx_StringTabEntry t, PyObject **str) { - if (t.is_unicode | t.is_str) { - if (t.intern) { - *str = PyUnicode_InternFromString(t.s); - } else if (t.encoding) { - *str = PyUnicode_Decode(t.s, t.n - 1, t.encoding, NULL); - } else { - *str = PyUnicode_FromStringAndSize(t.s, t.n - 1); - } - } else { - *str = PyBytes_FromStringAndSize(t.s, t.n - 1); - } - if (!*str) - return -1; - if (PyObject_Hash(*str) == -1) - return -1; - return 0; -} -#endif -static int __Pyx_InitStrings(__Pyx_StringTabEntry *t) { - while (t->p) { - #if PY_MAJOR_VERSION >= 3 - __Pyx_InitString(*t, t->p); - #else - if (t->is_unicode) { - *t->p = PyUnicode_DecodeUTF8(t->s, t->n - 1, NULL); - } else if (t->intern) { - *t->p = PyString_InternFromString(t->s); - } else { - *t->p = PyString_FromStringAndSize(t->s, t->n - 1); - } - if (!*t->p) - return -1; - if (PyObject_Hash(*t->p) == -1) - return -1; - #endif - ++t; - } - return 0; -} - -#include -static CYTHON_INLINE Py_ssize_t __Pyx_ssize_strlen(const char *s) { - size_t len = strlen(s); - if (unlikely(len > (size_t) PY_SSIZE_T_MAX)) { - PyErr_SetString(PyExc_OverflowError, "byte string is too long"); - return -1; - } - return (Py_ssize_t) len; -} -static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(const char* c_str) { - Py_ssize_t len = __Pyx_ssize_strlen(c_str); - if (unlikely(len < 0)) return NULL; - return __Pyx_PyUnicode_FromStringAndSize(c_str, len); -} -static CYTHON_INLINE PyObject* __Pyx_PyByteArray_FromString(const char* c_str) { - Py_ssize_t len = __Pyx_ssize_strlen(c_str); - if (unlikely(len < 0)) return NULL; - return PyByteArray_FromStringAndSize(c_str, len); -} -static CYTHON_INLINE const char* __Pyx_PyObject_AsString(PyObject* o) { - Py_ssize_t ignore; - return __Pyx_PyObject_AsStringAndSize(o, &ignore); -} -#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT -#if !CYTHON_PEP393_ENABLED -static const char* __Pyx_PyUnicode_AsStringAndSize(PyObject* o, Py_ssize_t *length) { - char* defenc_c; - PyObject* defenc = _PyUnicode_AsDefaultEncodedString(o, NULL); - if (!defenc) return NULL; - defenc_c = PyBytes_AS_STRING(defenc); -#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII - { - char* end = defenc_c + PyBytes_GET_SIZE(defenc); - char* c; - for (c = defenc_c; c < end; c++) { - if ((unsigned char) (*c) >= 128) { - PyUnicode_AsASCIIString(o); - return NULL; - } - } - } -#endif - *length = PyBytes_GET_SIZE(defenc); - return defenc_c; -} -#else -static CYTHON_INLINE const char* __Pyx_PyUnicode_AsStringAndSize(PyObject* o, Py_ssize_t *length) { - if (unlikely(__Pyx_PyUnicode_READY(o) == -1)) return NULL; -#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII - if (likely(PyUnicode_IS_ASCII(o))) { - *length = PyUnicode_GET_LENGTH(o); - return PyUnicode_AsUTF8(o); - } else { - PyUnicode_AsASCIIString(o); - return NULL; - } -#else - return PyUnicode_AsUTF8AndSize(o, length); -#endif -} -#endif -#endif -static CYTHON_INLINE const char* __Pyx_PyObject_AsStringAndSize(PyObject* o, Py_ssize_t *length) { -#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT - if ( -#if PY_MAJOR_VERSION < 3 && __PYX_DEFAULT_STRING_ENCODING_IS_ASCII - __Pyx_sys_getdefaultencoding_not_ascii && -#endif - PyUnicode_Check(o)) { - return __Pyx_PyUnicode_AsStringAndSize(o, length); - } else -#endif -#if (!CYTHON_COMPILING_IN_PYPY && !CYTHON_COMPILING_IN_LIMITED_API) || (defined(PyByteArray_AS_STRING) && defined(PyByteArray_GET_SIZE)) - if (PyByteArray_Check(o)) { - *length = PyByteArray_GET_SIZE(o); - return PyByteArray_AS_STRING(o); - } else -#endif - { - char* result; - int r = PyBytes_AsStringAndSize(o, &result, length); - if (unlikely(r < 0)) { - return NULL; - } else { - return result; - } - } -} -static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject* x) { - int is_true = x == Py_True; - if (is_true | (x == Py_False) | (x == Py_None)) return is_true; - else return PyObject_IsTrue(x); -} -static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject* x) { - int retval; - if (unlikely(!x)) return -1; - retval = __Pyx_PyObject_IsTrue(x); - Py_DECREF(x); - return retval; -} -static PyObject* __Pyx_PyNumber_IntOrLongWrongResultType(PyObject* result, const char* type_name) { - __Pyx_TypeName result_type_name = __Pyx_PyType_GetName(Py_TYPE(result)); -#if PY_MAJOR_VERSION >= 3 - if (PyLong_Check(result)) { - if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1, - "__int__ returned non-int (type " __Pyx_FMT_TYPENAME "). " - "The ability to return an instance of a strict subclass of int is deprecated, " - "and may be removed in a future version of Python.", - result_type_name)) { - __Pyx_DECREF_TypeName(result_type_name); - Py_DECREF(result); - return NULL; - } - __Pyx_DECREF_TypeName(result_type_name); - return result; - } -#endif - PyErr_Format(PyExc_TypeError, - "__%.4s__ returned non-%.4s (type " __Pyx_FMT_TYPENAME ")", - type_name, type_name, result_type_name); - __Pyx_DECREF_TypeName(result_type_name); - Py_DECREF(result); - return NULL; -} -static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(PyObject* x) { -#if CYTHON_USE_TYPE_SLOTS - PyNumberMethods *m; -#endif - const char *name = NULL; - PyObject *res = NULL; -#if PY_MAJOR_VERSION < 3 - if (likely(PyInt_Check(x) || PyLong_Check(x))) -#else - if (likely(PyLong_Check(x))) -#endif - return __Pyx_NewRef(x); -#if CYTHON_USE_TYPE_SLOTS - m = Py_TYPE(x)->tp_as_number; - #if PY_MAJOR_VERSION < 3 - if (m && m->nb_int) { - name = "int"; - res = m->nb_int(x); - } - else if (m && m->nb_long) { - name = "long"; - res = m->nb_long(x); - } - #else - if (likely(m && m->nb_int)) { - name = "int"; - res = m->nb_int(x); - } - #endif -#else - if (!PyBytes_CheckExact(x) && !PyUnicode_CheckExact(x)) { - res = PyNumber_Int(x); - } -#endif - if (likely(res)) { -#if PY_MAJOR_VERSION < 3 - if (unlikely(!PyInt_Check(res) && !PyLong_Check(res))) { -#else - if (unlikely(!PyLong_CheckExact(res))) { -#endif - return __Pyx_PyNumber_IntOrLongWrongResultType(res, name); - } - } - else if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_TypeError, - "an integer is required"); - } - return res; -} -static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) { - Py_ssize_t ival; - PyObject *x; -#if PY_MAJOR_VERSION < 3 - if (likely(PyInt_CheckExact(b))) { - if (sizeof(Py_ssize_t) >= sizeof(long)) - return PyInt_AS_LONG(b); - else - return PyInt_AsSsize_t(b); - } -#endif - if (likely(PyLong_CheckExact(b))) { - #if CYTHON_USE_PYLONG_INTERNALS - if (likely(__Pyx_PyLong_IsCompact(b))) { - return __Pyx_PyLong_CompactValue(b); - } else { - const digit* digits = __Pyx_PyLong_Digits(b); - const Py_ssize_t size = __Pyx_PyLong_SignedDigitCount(b); - switch (size) { - case 2: - if (8 * sizeof(Py_ssize_t) > 2 * PyLong_SHIFT) { - return (Py_ssize_t) (((((size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0])); - } - break; - case -2: - if (8 * sizeof(Py_ssize_t) > 2 * PyLong_SHIFT) { - return -(Py_ssize_t) (((((size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0])); - } - break; - case 3: - if (8 * sizeof(Py_ssize_t) > 3 * PyLong_SHIFT) { - return (Py_ssize_t) (((((((size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0])); - } - break; - case -3: - if (8 * sizeof(Py_ssize_t) > 3 * PyLong_SHIFT) { - return -(Py_ssize_t) (((((((size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0])); - } - break; - case 4: - if (8 * sizeof(Py_ssize_t) > 4 * PyLong_SHIFT) { - return (Py_ssize_t) (((((((((size_t)digits[3]) << PyLong_SHIFT) | (size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0])); - } - break; - case -4: - if (8 * sizeof(Py_ssize_t) > 4 * PyLong_SHIFT) { - return -(Py_ssize_t) (((((((((size_t)digits[3]) << PyLong_SHIFT) | (size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0])); - } - break; - } - } - #endif - return PyLong_AsSsize_t(b); - } - x = PyNumber_Index(b); - if (!x) return -1; - ival = PyInt_AsSsize_t(x); - Py_DECREF(x); - return ival; -} -static CYTHON_INLINE Py_hash_t __Pyx_PyIndex_AsHash_t(PyObject* o) { - if (sizeof(Py_hash_t) == sizeof(Py_ssize_t)) { - return (Py_hash_t) __Pyx_PyIndex_AsSsize_t(o); -#if PY_MAJOR_VERSION < 3 - } else if (likely(PyInt_CheckExact(o))) { - return PyInt_AS_LONG(o); -#endif - } else { - Py_ssize_t ival; - PyObject *x; - x = PyNumber_Index(o); - if (!x) return -1; - ival = PyInt_AsLong(x); - Py_DECREF(x); - return ival; - } -} -static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b) { - return b ? __Pyx_NewRef(Py_True) : __Pyx_NewRef(Py_False); -} -static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t ival) { - return PyInt_FromSize_t(ival); -} - - -/* #### Code section: utility_code_pragmas_end ### */ -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - - - -/* #### Code section: end ### */ -#endif /* Py_PYTHON_H */ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/cu2qu.cpython-38-x86_64-linux-gnu.so b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/cu2qu.cpython-38-x86_64-linux-gnu.so deleted file mode 100755 index 8fe33f1..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/cu2qu.cpython-38-x86_64-linux-gnu.so and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/cu2qu.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/cu2qu.py deleted file mode 100644 index e620b48..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/cu2qu.py +++ /dev/null @@ -1,534 +0,0 @@ -# cython: language_level=3 -# distutils: define_macros=CYTHON_TRACE_NOGIL=1 - -# Copyright 2015 Google Inc. 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. - -try: - import cython - - COMPILED = cython.compiled -except (AttributeError, ImportError): - # if cython not installed, use mock module with no-op decorators and types - from fontTools.misc import cython - - COMPILED = False - -import math - -from .errors import Error as Cu2QuError, ApproxNotFoundError - - -__all__ = ["curve_to_quadratic", "curves_to_quadratic"] - -MAX_N = 100 - -NAN = float("NaN") - - -@cython.cfunc -@cython.inline -@cython.returns(cython.double) -@cython.locals(v1=cython.complex, v2=cython.complex) -def dot(v1, v2): - """Return the dot product of two vectors. - - Args: - v1 (complex): First vector. - v2 (complex): Second vector. - - Returns: - double: Dot product. - """ - return (v1 * v2.conjugate()).real - - -@cython.cfunc -@cython.inline -@cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex) -@cython.locals( - _1=cython.complex, _2=cython.complex, _3=cython.complex, _4=cython.complex -) -def calc_cubic_points(a, b, c, d): - _1 = d - _2 = (c / 3.0) + d - _3 = (b + c) / 3.0 + _2 - _4 = a + d + c + b - return _1, _2, _3, _4 - - -@cython.cfunc -@cython.inline -@cython.locals( - p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex -) -@cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex) -def calc_cubic_parameters(p0, p1, p2, p3): - c = (p1 - p0) * 3.0 - b = (p2 - p1) * 3.0 - c - d = p0 - a = p3 - d - c - b - return a, b, c, d - - -@cython.cfunc -@cython.inline -@cython.locals( - p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex -) -def split_cubic_into_n_iter(p0, p1, p2, p3, n): - """Split a cubic Bezier into n equal parts. - - Splits the curve into `n` equal parts by curve time. - (t=0..1/n, t=1/n..2/n, ...) - - Args: - p0 (complex): Start point of curve. - p1 (complex): First handle of curve. - p2 (complex): Second handle of curve. - p3 (complex): End point of curve. - - Returns: - An iterator yielding the control points (four complex values) of the - subcurves. - """ - # Hand-coded special-cases - if n == 2: - return iter(split_cubic_into_two(p0, p1, p2, p3)) - if n == 3: - return iter(split_cubic_into_three(p0, p1, p2, p3)) - if n == 4: - a, b = split_cubic_into_two(p0, p1, p2, p3) - return iter( - split_cubic_into_two(a[0], a[1], a[2], a[3]) - + split_cubic_into_two(b[0], b[1], b[2], b[3]) - ) - if n == 6: - a, b = split_cubic_into_two(p0, p1, p2, p3) - return iter( - split_cubic_into_three(a[0], a[1], a[2], a[3]) - + split_cubic_into_three(b[0], b[1], b[2], b[3]) - ) - - return _split_cubic_into_n_gen(p0, p1, p2, p3, n) - - -@cython.locals( - p0=cython.complex, - p1=cython.complex, - p2=cython.complex, - p3=cython.complex, - n=cython.int, -) -@cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex) -@cython.locals( - dt=cython.double, delta_2=cython.double, delta_3=cython.double, i=cython.int -) -@cython.locals( - a1=cython.complex, b1=cython.complex, c1=cython.complex, d1=cython.complex -) -def _split_cubic_into_n_gen(p0, p1, p2, p3, n): - a, b, c, d = calc_cubic_parameters(p0, p1, p2, p3) - dt = 1 / n - delta_2 = dt * dt - delta_3 = dt * delta_2 - for i in range(n): - t1 = i * dt - t1_2 = t1 * t1 - # calc new a, b, c and d - a1 = a * delta_3 - b1 = (3 * a * t1 + b) * delta_2 - c1 = (2 * b * t1 + c + 3 * a * t1_2) * dt - d1 = a * t1 * t1_2 + b * t1_2 + c * t1 + d - yield calc_cubic_points(a1, b1, c1, d1) - - -@cython.cfunc -@cython.inline -@cython.locals( - p0=cython.complex, p1=cython.complex, p2=cython.complex, p3=cython.complex -) -@cython.locals(mid=cython.complex, deriv3=cython.complex) -def split_cubic_into_two(p0, p1, p2, p3): - """Split a cubic Bezier into two equal parts. - - Splits the curve into two equal parts at t = 0.5 - - Args: - p0 (complex): Start point of curve. - p1 (complex): First handle of curve. - p2 (complex): Second handle of curve. - p3 (complex): End point of curve. - - Returns: - tuple: Two cubic Beziers (each expressed as a tuple of four complex - values). - """ - mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 - deriv3 = (p3 + p2 - p1 - p0) * 0.125 - return ( - (p0, (p0 + p1) * 0.5, mid - deriv3, mid), - (mid, mid + deriv3, (p2 + p3) * 0.5, p3), - ) - - -@cython.cfunc -@cython.inline -@cython.locals( - p0=cython.complex, - p1=cython.complex, - p2=cython.complex, - p3=cython.complex, -) -@cython.locals( - mid1=cython.complex, - deriv1=cython.complex, - mid2=cython.complex, - deriv2=cython.complex, -) -def split_cubic_into_three(p0, p1, p2, p3): - """Split a cubic Bezier into three equal parts. - - Splits the curve into three equal parts at t = 1/3 and t = 2/3 - - Args: - p0 (complex): Start point of curve. - p1 (complex): First handle of curve. - p2 (complex): Second handle of curve. - p3 (complex): End point of curve. - - Returns: - tuple: Three cubic Beziers (each expressed as a tuple of four complex - values). - """ - mid1 = (8 * p0 + 12 * p1 + 6 * p2 + p3) * (1 / 27) - deriv1 = (p3 + 3 * p2 - 4 * p0) * (1 / 27) - mid2 = (p0 + 6 * p1 + 12 * p2 + 8 * p3) * (1 / 27) - deriv2 = (4 * p3 - 3 * p1 - p0) * (1 / 27) - return ( - (p0, (2 * p0 + p1) / 3.0, mid1 - deriv1, mid1), - (mid1, mid1 + deriv1, mid2 - deriv2, mid2), - (mid2, mid2 + deriv2, (p2 + 2 * p3) / 3.0, p3), - ) - - -@cython.cfunc -@cython.inline -@cython.returns(cython.complex) -@cython.locals( - t=cython.double, - p0=cython.complex, - p1=cython.complex, - p2=cython.complex, - p3=cython.complex, -) -@cython.locals(_p1=cython.complex, _p2=cython.complex) -def cubic_approx_control(t, p0, p1, p2, p3): - """Approximate a cubic Bezier using a quadratic one. - - Args: - t (double): Position of control point. - p0 (complex): Start point of curve. - p1 (complex): First handle of curve. - p2 (complex): Second handle of curve. - p3 (complex): End point of curve. - - Returns: - complex: Location of candidate control point on quadratic curve. - """ - _p1 = p0 + (p1 - p0) * 1.5 - _p2 = p3 + (p2 - p3) * 1.5 - return _p1 + (_p2 - _p1) * t - - -@cython.cfunc -@cython.inline -@cython.returns(cython.complex) -@cython.locals(a=cython.complex, b=cython.complex, c=cython.complex, d=cython.complex) -@cython.locals(ab=cython.complex, cd=cython.complex, p=cython.complex, h=cython.double) -def calc_intersect(a, b, c, d): - """Calculate the intersection of two lines. - - Args: - a (complex): Start point of first line. - b (complex): End point of first line. - c (complex): Start point of second line. - d (complex): End point of second line. - - Returns: - complex: Location of intersection if one present, ``complex(NaN,NaN)`` - if no intersection was found. - """ - ab = b - a - cd = d - c - p = ab * 1j - try: - h = dot(p, a - c) / dot(p, cd) - except ZeroDivisionError: - return complex(NAN, NAN) - return c + cd * h - - -@cython.cfunc -@cython.returns(cython.int) -@cython.locals( - tolerance=cython.double, - p0=cython.complex, - p1=cython.complex, - p2=cython.complex, - p3=cython.complex, -) -@cython.locals(mid=cython.complex, deriv3=cython.complex) -def cubic_farthest_fit_inside(p0, p1, p2, p3, tolerance): - """Check if a cubic Bezier lies within a given distance of the origin. - - "Origin" means *the* origin (0,0), not the start of the curve. Note that no - checks are made on the start and end positions of the curve; this function - only checks the inside of the curve. - - Args: - p0 (complex): Start point of curve. - p1 (complex): First handle of curve. - p2 (complex): Second handle of curve. - p3 (complex): End point of curve. - tolerance (double): Distance from origin. - - Returns: - bool: True if the cubic Bezier ``p`` entirely lies within a distance - ``tolerance`` of the origin, False otherwise. - """ - # First check p2 then p1, as p2 has higher error early on. - if abs(p2) <= tolerance and abs(p1) <= tolerance: - return True - - # Split. - mid = (p0 + 3 * (p1 + p2) + p3) * 0.125 - if abs(mid) > tolerance: - return False - deriv3 = (p3 + p2 - p1 - p0) * 0.125 - return cubic_farthest_fit_inside( - p0, (p0 + p1) * 0.5, mid - deriv3, mid, tolerance - ) and cubic_farthest_fit_inside(mid, mid + deriv3, (p2 + p3) * 0.5, p3, tolerance) - - -@cython.cfunc -@cython.inline -@cython.locals(tolerance=cython.double) -@cython.locals( - q1=cython.complex, - c0=cython.complex, - c1=cython.complex, - c2=cython.complex, - c3=cython.complex, -) -def cubic_approx_quadratic(cubic, tolerance): - """Approximate a cubic Bezier with a single quadratic within a given tolerance. - - Args: - cubic (sequence): Four complex numbers representing control points of - the cubic Bezier curve. - tolerance (double): Permitted deviation from the original curve. - - Returns: - Three complex numbers representing control points of the quadratic - curve if it fits within the given tolerance, or ``None`` if no suitable - curve could be calculated. - """ - - q1 = calc_intersect(cubic[0], cubic[1], cubic[2], cubic[3]) - if math.isnan(q1.imag): - return None - c0 = cubic[0] - c3 = cubic[3] - c1 = c0 + (q1 - c0) * (2 / 3) - c2 = c3 + (q1 - c3) * (2 / 3) - if not cubic_farthest_fit_inside(0, c1 - cubic[1], c2 - cubic[2], 0, tolerance): - return None - return c0, q1, c3 - - -@cython.cfunc -@cython.locals(n=cython.int, tolerance=cython.double) -@cython.locals(i=cython.int) -@cython.locals(all_quadratic=cython.int) -@cython.locals( - c0=cython.complex, c1=cython.complex, c2=cython.complex, c3=cython.complex -) -@cython.locals( - q0=cython.complex, - q1=cython.complex, - next_q1=cython.complex, - q2=cython.complex, - d1=cython.complex, -) -def cubic_approx_spline(cubic, n, tolerance, all_quadratic): - """Approximate a cubic Bezier curve with a spline of n quadratics. - - Args: - cubic (sequence): Four complex numbers representing control points of - the cubic Bezier curve. - n (int): Number of quadratic Bezier curves in the spline. - tolerance (double): Permitted deviation from the original curve. - - Returns: - A list of ``n+2`` complex numbers, representing control points of the - quadratic spline if it fits within the given tolerance, or ``None`` if - no suitable spline could be calculated. - """ - - if n == 1: - return cubic_approx_quadratic(cubic, tolerance) - if n == 2 and all_quadratic == False: - return cubic - - cubics = split_cubic_into_n_iter(cubic[0], cubic[1], cubic[2], cubic[3], n) - - # calculate the spline of quadratics and check errors at the same time. - next_cubic = next(cubics) - next_q1 = cubic_approx_control( - 0, next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] - ) - q2 = cubic[0] - d1 = 0j - spline = [cubic[0], next_q1] - for i in range(1, n + 1): - # Current cubic to convert - c0, c1, c2, c3 = next_cubic - - # Current quadratic approximation of current cubic - q0 = q2 - q1 = next_q1 - if i < n: - next_cubic = next(cubics) - next_q1 = cubic_approx_control( - i / (n - 1), next_cubic[0], next_cubic[1], next_cubic[2], next_cubic[3] - ) - spline.append(next_q1) - q2 = (q1 + next_q1) * 0.5 - else: - q2 = c3 - - # End-point deltas - d0 = d1 - d1 = q2 - c3 - - if abs(d1) > tolerance or not cubic_farthest_fit_inside( - d0, - q0 + (q1 - q0) * (2 / 3) - c1, - q2 + (q1 - q2) * (2 / 3) - c2, - d1, - tolerance, - ): - return None - spline.append(cubic[3]) - - return spline - - -@cython.locals(max_err=cython.double) -@cython.locals(n=cython.int) -@cython.locals(all_quadratic=cython.int) -def curve_to_quadratic(curve, max_err, all_quadratic=True): - """Approximate a cubic Bezier curve with a spline of n quadratics. - - Args: - cubic (sequence): Four 2D tuples representing control points of - the cubic Bezier curve. - max_err (double): Permitted deviation from the original curve. - all_quadratic (bool): If True (default) returned value is a - quadratic spline. If False, it's either a single quadratic - curve or a single cubic curve. - - Returns: - If all_quadratic is True: A list of 2D tuples, representing - control points of the quadratic spline if it fits within the - given tolerance, or ``None`` if no suitable spline could be - calculated. - - If all_quadratic is False: Either a quadratic curve (if length - of output is 3), or a cubic curve (if length of output is 4). - """ - - curve = [complex(*p) for p in curve] - - for n in range(1, MAX_N + 1): - spline = cubic_approx_spline(curve, n, max_err, all_quadratic) - if spline is not None: - # done. go home - return [(s.real, s.imag) for s in spline] - - raise ApproxNotFoundError(curve) - - -@cython.locals(l=cython.int, last_i=cython.int, i=cython.int) -@cython.locals(all_quadratic=cython.int) -def curves_to_quadratic(curves, max_errors, all_quadratic=True): - """Return quadratic Bezier splines approximating the input cubic Beziers. - - Args: - curves: A sequence of *n* curves, each curve being a sequence of four - 2D tuples. - max_errors: A sequence of *n* floats representing the maximum permissible - deviation from each of the cubic Bezier curves. - all_quadratic (bool): If True (default) returned values are a - quadratic spline. If False, they are either a single quadratic - curve or a single cubic curve. - - Example:: - - >>> curves_to_quadratic( [ - ... [ (50,50), (100,100), (150,100), (200,50) ], - ... [ (75,50), (120,100), (150,75), (200,60) ] - ... ], [1,1] ) - [[(50.0, 50.0), (75.0, 75.0), (125.0, 91.66666666666666), (175.0, 75.0), (200.0, 50.0)], [(75.0, 50.0), (97.5, 75.0), (135.41666666666666, 82.08333333333333), (175.0, 67.5), (200.0, 60.0)]] - - The returned splines have "implied oncurve points" suitable for use in - TrueType ``glif`` outlines - i.e. in the first spline returned above, - the first quadratic segment runs from (50,50) to - ( (75 + 125)/2 , (120 + 91.666..)/2 ) = (100, 83.333...). - - Returns: - If all_quadratic is True, a list of splines, each spline being a list - of 2D tuples. - - If all_quadratic is False, a list of curves, each curve being a quadratic - (length 3), or cubic (length 4). - - Raises: - fontTools.cu2qu.Errors.ApproxNotFoundError: if no suitable approximation - can be found for all curves with the given parameters. - """ - - curves = [[complex(*p) for p in curve] for curve in curves] - assert len(max_errors) == len(curves) - - l = len(curves) - splines = [None] * l - last_i = i = 0 - n = 1 - while True: - spline = cubic_approx_spline(curves[i], n, max_errors[i], all_quadratic) - if spline is None: - if n == MAX_N: - break - n += 1 - last_i = i - continue - splines[i] = spline - i = (i + 1) % l - if i == last_i: - # done. go home - return [[(s.real, s.imag) for s in spline] for spline in splines] - - raise ApproxNotFoundError(curves) diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/errors.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/errors.py deleted file mode 100644 index fa3dc42..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/errors.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright 2016 Google Inc. 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. - - -class Error(Exception): - """Base Cu2Qu exception class for all other errors.""" - - -class ApproxNotFoundError(Error): - def __init__(self, curve): - message = "no approximation found: %s" % curve - super().__init__(message) - self.curve = curve - - -class UnequalZipLengthsError(Error): - pass - - -class IncompatibleGlyphsError(Error): - def __init__(self, glyphs): - assert len(glyphs) > 1 - self.glyphs = glyphs - names = set(repr(g.name) for g in glyphs) - if len(names) > 1: - self.combined_name = "{%s}" % ", ".join(sorted(names)) - else: - self.combined_name = names.pop() - - def __repr__(self): - return "<%s %s>" % (type(self).__name__, self.combined_name) - - -class IncompatibleSegmentNumberError(IncompatibleGlyphsError): - def __str__(self): - return "Glyphs named %s have different number of segments" % ( - self.combined_name - ) - - -class IncompatibleSegmentTypesError(IncompatibleGlyphsError): - def __init__(self, glyphs, segments): - IncompatibleGlyphsError.__init__(self, glyphs) - self.segments = segments - - def __str__(self): - lines = [] - ndigits = len(str(max(self.segments))) - for i, tags in sorted(self.segments.items()): - lines.append( - "%s: (%s)" % (str(i).rjust(ndigits), ", ".join(repr(t) for t in tags)) - ) - return "Glyphs named %s have incompatible segment types:\n %s" % ( - self.combined_name, - "\n ".join(lines), - ) - - -class IncompatibleFontsError(Error): - def __init__(self, glyph_errors): - self.glyph_errors = glyph_errors - - def __str__(self): - return "fonts contains incompatible glyphs: %s" % ( - ", ".join(repr(g) for g in sorted(self.glyph_errors.keys())) - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/ufo.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/ufo.py deleted file mode 100644 index 7a6dbc6..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/cu2qu/ufo.py +++ /dev/null @@ -1,349 +0,0 @@ -# Copyright 2015 Google Inc. 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. - - -"""Converts cubic bezier curves to quadratic splines. - -Conversion is performed such that the quadratic splines keep the same end-curve -tangents as the original cubics. The approach is iterative, increasing the -number of segments for a spline until the error gets below a bound. - -Respective curves from multiple fonts will be converted at once to ensure that -the resulting splines are interpolation-compatible. -""" - -import logging -from fontTools.pens.basePen import AbstractPen -from fontTools.pens.pointPen import PointToSegmentPen -from fontTools.pens.reverseContourPen import ReverseContourPen - -from . import curves_to_quadratic -from .errors import ( - UnequalZipLengthsError, - IncompatibleSegmentNumberError, - IncompatibleSegmentTypesError, - IncompatibleGlyphsError, - IncompatibleFontsError, -) - - -__all__ = ["fonts_to_quadratic", "font_to_quadratic"] - -# The default approximation error below is a relative value (1/1000 of the EM square). -# Later on, we convert it to absolute font units by multiplying it by a font's UPEM -# (see fonts_to_quadratic). -DEFAULT_MAX_ERR = 0.001 -CURVE_TYPE_LIB_KEY = "com.github.googlei18n.cu2qu.curve_type" - -logger = logging.getLogger(__name__) - - -_zip = zip - - -def zip(*args): - """Ensure each argument to zip has the same length. Also make sure a list is - returned for python 2/3 compatibility. - """ - - if len(set(len(a) for a in args)) != 1: - raise UnequalZipLengthsError(*args) - return list(_zip(*args)) - - -class GetSegmentsPen(AbstractPen): - """Pen to collect segments into lists of points for conversion. - - Curves always include their initial on-curve point, so some points are - duplicated between segments. - """ - - def __init__(self): - self._last_pt = None - self.segments = [] - - def _add_segment(self, tag, *args): - if tag in ["move", "line", "qcurve", "curve"]: - self._last_pt = args[-1] - self.segments.append((tag, args)) - - def moveTo(self, pt): - self._add_segment("move", pt) - - def lineTo(self, pt): - self._add_segment("line", pt) - - def qCurveTo(self, *points): - self._add_segment("qcurve", self._last_pt, *points) - - def curveTo(self, *points): - self._add_segment("curve", self._last_pt, *points) - - def closePath(self): - self._add_segment("close") - - def endPath(self): - self._add_segment("end") - - def addComponent(self, glyphName, transformation): - pass - - -def _get_segments(glyph): - """Get a glyph's segments as extracted by GetSegmentsPen.""" - - pen = GetSegmentsPen() - # glyph.draw(pen) - # We can't simply draw the glyph with the pen, but we must initialize the - # PointToSegmentPen explicitly with outputImpliedClosingLine=True. - # By default PointToSegmentPen does not outputImpliedClosingLine -- unless - # last and first point on closed contour are duplicated. Because we are - # converting multiple glyphs at the same time, we want to make sure - # this function returns the same number of segments, whether or not - # the last and first point overlap. - # https://github.com/googlefonts/fontmake/issues/572 - # https://github.com/fonttools/fonttools/pull/1720 - pointPen = PointToSegmentPen(pen, outputImpliedClosingLine=True) - glyph.drawPoints(pointPen) - return pen.segments - - -def _set_segments(glyph, segments, reverse_direction): - """Draw segments as extracted by GetSegmentsPen back to a glyph.""" - - glyph.clearContours() - pen = glyph.getPen() - if reverse_direction: - pen = ReverseContourPen(pen) - for tag, args in segments: - if tag == "move": - pen.moveTo(*args) - elif tag == "line": - pen.lineTo(*args) - elif tag == "curve": - pen.curveTo(*args[1:]) - elif tag == "qcurve": - pen.qCurveTo(*args[1:]) - elif tag == "close": - pen.closePath() - elif tag == "end": - pen.endPath() - else: - raise AssertionError('Unhandled segment type "%s"' % tag) - - -def _segments_to_quadratic(segments, max_err, stats, all_quadratic=True): - """Return quadratic approximations of cubic segments.""" - - assert all(s[0] == "curve" for s in segments), "Non-cubic given to convert" - - new_points = curves_to_quadratic([s[1] for s in segments], max_err, all_quadratic) - n = len(new_points[0]) - assert all(len(s) == n for s in new_points[1:]), "Converted incompatibly" - - spline_length = str(n - 2) - stats[spline_length] = stats.get(spline_length, 0) + 1 - - if all_quadratic or n == 3: - return [("qcurve", p) for p in new_points] - else: - return [("curve", p) for p in new_points] - - -def _glyphs_to_quadratic(glyphs, max_err, reverse_direction, stats, all_quadratic=True): - """Do the actual conversion of a set of compatible glyphs, after arguments - have been set up. - - Return True if the glyphs were modified, else return False. - """ - - try: - segments_by_location = zip(*[_get_segments(g) for g in glyphs]) - except UnequalZipLengthsError: - raise IncompatibleSegmentNumberError(glyphs) - if not any(segments_by_location): - return False - - # always modify input glyphs if reverse_direction is True - glyphs_modified = reverse_direction - - new_segments_by_location = [] - incompatible = {} - for i, segments in enumerate(segments_by_location): - tag = segments[0][0] - if not all(s[0] == tag for s in segments[1:]): - incompatible[i] = [s[0] for s in segments] - elif tag == "curve": - new_segments = _segments_to_quadratic( - segments, max_err, stats, all_quadratic - ) - if all_quadratic or new_segments != segments: - glyphs_modified = True - segments = new_segments - new_segments_by_location.append(segments) - - if glyphs_modified: - new_segments_by_glyph = zip(*new_segments_by_location) - for glyph, new_segments in zip(glyphs, new_segments_by_glyph): - _set_segments(glyph, new_segments, reverse_direction) - - if incompatible: - raise IncompatibleSegmentTypesError(glyphs, segments=incompatible) - return glyphs_modified - - -def glyphs_to_quadratic( - glyphs, max_err=None, reverse_direction=False, stats=None, all_quadratic=True -): - """Convert the curves of a set of compatible of glyphs to quadratic. - - All curves will be converted to quadratic at once, ensuring interpolation - compatibility. If this is not required, calling glyphs_to_quadratic with one - glyph at a time may yield slightly more optimized results. - - Return True if glyphs were modified, else return False. - - Raises IncompatibleGlyphsError if glyphs have non-interpolatable outlines. - """ - if stats is None: - stats = {} - - if not max_err: - # assume 1000 is the default UPEM - max_err = DEFAULT_MAX_ERR * 1000 - - if isinstance(max_err, (list, tuple)): - max_errors = max_err - else: - max_errors = [max_err] * len(glyphs) - assert len(max_errors) == len(glyphs) - - return _glyphs_to_quadratic( - glyphs, max_errors, reverse_direction, stats, all_quadratic - ) - - -def fonts_to_quadratic( - fonts, - max_err_em=None, - max_err=None, - reverse_direction=False, - stats=None, - dump_stats=False, - remember_curve_type=True, - all_quadratic=True, -): - """Convert the curves of a collection of fonts to quadratic. - - All curves will be converted to quadratic at once, ensuring interpolation - compatibility. If this is not required, calling fonts_to_quadratic with one - font at a time may yield slightly more optimized results. - - Return the set of modified glyph names if any, else return an empty set. - - By default, cu2qu stores the curve type in the fonts' lib, under a private - key "com.github.googlei18n.cu2qu.curve_type", and will not try to convert - them again if the curve type is already set to "quadratic". - Setting 'remember_curve_type' to False disables this optimization. - - Raises IncompatibleFontsError if same-named glyphs from different fonts - have non-interpolatable outlines. - """ - - if remember_curve_type: - curve_types = {f.lib.get(CURVE_TYPE_LIB_KEY, "cubic") for f in fonts} - if len(curve_types) == 1: - curve_type = next(iter(curve_types)) - if curve_type in ("quadratic", "mixed"): - logger.info("Curves already converted to quadratic") - return False - elif curve_type == "cubic": - pass # keep converting - else: - raise NotImplementedError(curve_type) - elif len(curve_types) > 1: - # going to crash later if they do differ - logger.warning("fonts may contain different curve types") - - if stats is None: - stats = {} - - if max_err_em and max_err: - raise TypeError("Only one of max_err and max_err_em can be specified.") - if not (max_err_em or max_err): - max_err_em = DEFAULT_MAX_ERR - - if isinstance(max_err, (list, tuple)): - assert len(max_err) == len(fonts) - max_errors = max_err - elif max_err: - max_errors = [max_err] * len(fonts) - - if isinstance(max_err_em, (list, tuple)): - assert len(fonts) == len(max_err_em) - max_errors = [f.info.unitsPerEm * e for f, e in zip(fonts, max_err_em)] - elif max_err_em: - max_errors = [f.info.unitsPerEm * max_err_em for f in fonts] - - modified = set() - glyph_errors = {} - for name in set().union(*(f.keys() for f in fonts)): - glyphs = [] - cur_max_errors = [] - for font, error in zip(fonts, max_errors): - if name in font: - glyphs.append(font[name]) - cur_max_errors.append(error) - try: - if _glyphs_to_quadratic( - glyphs, cur_max_errors, reverse_direction, stats, all_quadratic - ): - modified.add(name) - except IncompatibleGlyphsError as exc: - logger.error(exc) - glyph_errors[name] = exc - - if glyph_errors: - raise IncompatibleFontsError(glyph_errors) - - if modified and dump_stats: - spline_lengths = sorted(stats.keys()) - logger.info( - "New spline lengths: %s" - % (", ".join("%s: %d" % (l, stats[l]) for l in spline_lengths)) - ) - - if remember_curve_type: - for font in fonts: - curve_type = font.lib.get(CURVE_TYPE_LIB_KEY, "cubic") - new_curve_type = "quadratic" if all_quadratic else "mixed" - if curve_type != new_curve_type: - font.lib[CURVE_TYPE_LIB_KEY] = new_curve_type - return modified - - -def glyph_to_quadratic(glyph, **kwargs): - """Convenience wrapper around glyphs_to_quadratic, for just one glyph. - Return True if the glyph was modified, else return False. - """ - - return glyphs_to_quadratic([glyph], **kwargs) - - -def font_to_quadratic(font, **kwargs): - """Convenience wrapper around fonts_to_quadratic, for just one font. - Return the set of modified glyph names if any, else return empty set. - """ - - return fonts_to_quadratic([font], **kwargs) diff --git a/plotter-app/venv/lib/python3.8/site-packages/fontTools/designspaceLib/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/fontTools/designspaceLib/__init__.py deleted file mode 100644 index 0a1e782..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/fontTools/designspaceLib/__init__.py +++ /dev/null @@ -1,3338 +0,0 @@ -""" - designSpaceDocument - - - Read and write designspace files -""" - -from __future__ import annotations - -import collections -import copy -import itertools -import math -import os -import posixpath -from io import BytesIO, StringIO -from textwrap import indent -from typing import Any, Dict, List, MutableMapping, Optional, Tuple, Union, cast - -from fontTools.misc import etree as ET -from fontTools.misc import plistlib -from fontTools.misc.loggingTools import LogMixin -from fontTools.misc.textTools import tobytes, tostr - - -__all__ = [ - "AxisDescriptor", - "AxisLabelDescriptor", - "AxisMappingDescriptor", - "BaseDocReader", - "BaseDocWriter", - "DesignSpaceDocument", - "DesignSpaceDocumentError", - "DiscreteAxisDescriptor", - "InstanceDescriptor", - "LocationLabelDescriptor", - "RangeAxisSubsetDescriptor", - "RuleDescriptor", - "SourceDescriptor", - "ValueAxisSubsetDescriptor", - "VariableFontDescriptor", -] - -# ElementTree allows to find namespace-prefixed elements, but not attributes -# so we have to do it ourselves for 'xml:lang' -XML_NS = "{http://www.w3.org/XML/1998/namespace}" -XML_LANG = XML_NS + "lang" - - -def posix(path): - """Normalize paths using forward slash to work also on Windows.""" - new_path = posixpath.join(*path.split(os.path.sep)) - if path.startswith("/"): - # The above transformation loses absolute paths - new_path = "/" + new_path - elif path.startswith(r"\\"): - # The above transformation loses leading slashes of UNC path mounts - new_path = "//" + new_path - return new_path - - -def posixpath_property(private_name): - """Generate a propery that holds a path always using forward slashes.""" - - def getter(self): - # Normal getter - return getattr(self, private_name) - - def setter(self, value): - # The setter rewrites paths using forward slashes - if value is not None: - value = posix(value) - setattr(self, private_name, value) - - return property(getter, setter) - - -class DesignSpaceDocumentError(Exception): - def __init__(self, msg, obj=None): - self.msg = msg - self.obj = obj - - def __str__(self): - return str(self.msg) + (": %r" % self.obj if self.obj is not None else "") - - -class AsDictMixin(object): - def asdict(self): - d = {} - for attr, value in self.__dict__.items(): - if attr.startswith("_"): - continue - if hasattr(value, "asdict"): - value = value.asdict() - elif isinstance(value, list): - value = [v.asdict() if hasattr(v, "asdict") else v for v in value] - d[attr] = value - return d - - -class SimpleDescriptor(AsDictMixin): - """Containers for a bunch of attributes""" - - # XXX this is ugly. The 'print' is inappropriate here, and instead of - # assert, it should simply return True/False - def compare(self, other): - # test if this object contains the same data as the other - for attr in self._attrs: - try: - assert getattr(self, attr) == getattr(other, attr) - except AssertionError: - print( - "failed attribute", - attr, - getattr(self, attr), - "!=", - getattr(other, attr), - ) - - def __repr__(self): - attrs = [f"{a}={repr(getattr(self, a))}," for a in self._attrs] - attrs = indent("\n".join(attrs), " ") - return f"{self.__class__.__name__}(\n{attrs}\n)" - - -class SourceDescriptor(SimpleDescriptor): - """Simple container for data related to the source - - .. code:: python - - doc = DesignSpaceDocument() - s1 = SourceDescriptor() - s1.path = masterPath1 - s1.name = "master.ufo1" - s1.font = defcon.Font("master.ufo1") - s1.location = dict(weight=0) - s1.familyName = "MasterFamilyName" - s1.styleName = "MasterStyleNameOne" - s1.localisedFamilyName = dict(fr="Caractère") - s1.mutedGlyphNames.append("A") - s1.mutedGlyphNames.append("Z") - doc.addSource(s1) - - """ - - flavor = "source" - _attrs = [ - "filename", - "path", - "name", - "layerName", - "location", - "copyLib", - "copyGroups", - "copyFeatures", - "muteKerning", - "muteInfo", - "mutedGlyphNames", - "familyName", - "styleName", - "localisedFamilyName", - ] - - filename = posixpath_property("_filename") - path = posixpath_property("_path") - - def __init__( - self, - *, - filename=None, - path=None, - font=None, - name=None, - location=None, - designLocation=None, - layerName=None, - familyName=None, - styleName=None, - localisedFamilyName=None, - copyLib=False, - copyInfo=False, - copyGroups=False, - copyFeatures=False, - muteKerning=False, - muteInfo=False, - mutedGlyphNames=None, - ): - self.filename = filename - """string. A relative path to the source file, **as it is in the document**. - - MutatorMath + VarLib. - """ - self.path = path - """The absolute path, calculated from filename.""" - - self.font = font - """Any Python object. Optional. Points to a representation of this - source font that is loaded in memory, as a Python object (e.g. a - ``defcon.Font`` or a ``fontTools.ttFont.TTFont``). - - The default document reader will not fill-in this attribute, and the - default writer will not use this attribute. It is up to the user of - ``designspaceLib`` to either load the resource identified by - ``filename`` and store it in this field, or write the contents of - this field to the disk and make ```filename`` point to that. - """ - - self.name = name - """string. Optional. Unique identifier name for this source. - - MutatorMath + varLib. - """ - - self.designLocation = ( - designLocation if designLocation is not None else location or {} - ) - """dict. Axis values for this source, in design space coordinates. - - MutatorMath + varLib. - - This may be only part of the full design location. - See :meth:`getFullDesignLocation()` - - .. versionadded:: 5.0 - """ - - self.layerName = layerName - """string. The name of the layer in the source to look for - outline data. Default ``None`` which means ``foreground``. - """ - self.familyName = familyName - """string. Family name of this source. Though this data - can be extracted from the font, it can be efficient to have it right - here. - - varLib. - """ - self.styleName = styleName - """string. Style name of this source. Though this data - can be extracted from the font, it can be efficient to have it right - here. - - varLib. - """ - self.localisedFamilyName = localisedFamilyName or {} - """dict. A dictionary of localised family name strings, keyed by - language code. - - If present, will be used to build localized names for all instances. - - .. versionadded:: 5.0 - """ - - self.copyLib = copyLib - """bool. Indicates if the contents of the font.lib need to - be copied to the instances. - - MutatorMath. - - .. deprecated:: 5.0 - """ - self.copyInfo = copyInfo - """bool. Indicates if the non-interpolating font.info needs - to be copied to the instances. - - MutatorMath. - - .. deprecated:: 5.0 - """ - self.copyGroups = copyGroups - """bool. Indicates if the groups need to be copied to the - instances. - - MutatorMath. - - .. deprecated:: 5.0 - """ - self.copyFeatures = copyFeatures - """bool. Indicates if the feature text needs to be - copied to the instances. - - MutatorMath. - - .. deprecated:: 5.0 - """ - self.muteKerning = muteKerning - """bool. Indicates if the kerning data from this source - needs to be muted (i.e. not be part of the calculations). - - MutatorMath only. - """ - self.muteInfo = muteInfo - """bool. Indicated if the interpolating font.info data for - this source needs to be muted. - - MutatorMath only. - """ - self.mutedGlyphNames = mutedGlyphNames or [] - """list. Glyphnames that need to be muted in the - instances. - - MutatorMath only. - """ - - @property - def location(self): - """dict. Axis values for this source, in design space coordinates. - - MutatorMath + varLib. - - .. deprecated:: 5.0 - Use the more explicit alias for this property :attr:`designLocation`. - """ - return self.designLocation - - @location.setter - def location(self, location: Optional[SimpleLocationDict]): - self.designLocation = location or {} - - def setFamilyName(self, familyName, languageCode="en"): - """Setter for :attr:`localisedFamilyName` - - .. versionadded:: 5.0 - """ - self.localisedFamilyName[languageCode] = tostr(familyName) - - def getFamilyName(self, languageCode="en"): - """Getter for :attr:`localisedFamilyName` - - .. versionadded:: 5.0 - """ - return self.localisedFamilyName.get(languageCode) - - def getFullDesignLocation(self, doc: "DesignSpaceDocument") -> SimpleLocationDict: - """Get the complete design location of this source, from its - :attr:`designLocation` and the document's axis defaults. - - .. versionadded:: 5.0 - """ - result: SimpleLocationDict = {} - for axis in doc.axes: - if axis.name in self.designLocation: - result[axis.name] = self.designLocation[axis.name] - else: - result[axis.name] = axis.map_forward(axis.default) - return result - - -class RuleDescriptor(SimpleDescriptor): - """Represents the rule descriptor element: a set of glyph substitutions to - trigger conditionally in some parts of the designspace. - - .. code:: python - - r1 = RuleDescriptor() - r1.name = "unique.rule.name" - r1.conditionSets.append([dict(name="weight", minimum=-10, maximum=10), dict(...)]) - r1.conditionSets.append([dict(...), dict(...)]) - r1.subs.append(("a", "a.alt")) - - .. code:: xml - - - - - - - - - - - - - - """ - - _attrs = ["name", "conditionSets", "subs"] # what do we need here - - def __init__(self, *, name=None, conditionSets=None, subs=None): - self.name = name - """string. Unique name for this rule. Can be used to reference this rule data.""" - # list of lists of dict(name='aaaa', minimum=0, maximum=1000) - self.conditionSets = conditionSets or [] - """a list of conditionsets. - - - Each conditionset is a list of conditions. - - Each condition is a dict with ``name``, ``minimum`` and ``maximum`` keys. - """ - # list of substitutions stored as tuples of glyphnames ("a", "a.alt") - self.subs = subs or [] - """list of substitutions. - - - Each substitution is stored as tuples of glyphnames, e.g. ("a", "a.alt"). - - Note: By default, rules are applied first, before other text - shaping/OpenType layout, as they are part of the - `Required Variation Alternates OpenType feature `_. - See ref:`rules-element` § Attributes. - """ - - -def evaluateRule(rule, location): - """Return True if any of the rule's conditionsets matches the given location.""" - return any(evaluateConditions(c, location) for c in rule.conditionSets) - - -def evaluateConditions(conditions, location): - """Return True if all the conditions matches the given location. - - - If a condition has no minimum, check for < maximum. - - If a condition has no maximum, check for > minimum. - """ - for cd in conditions: - value = location[cd["name"]] - if cd.get("minimum") is None: - if value > cd["maximum"]: - return False - elif cd.get("maximum") is None: - if cd["minimum"] > value: - return False - elif not cd["minimum"] <= value <= cd["maximum"]: - return False - return True - - -def processRules(rules, location, glyphNames): - """Apply these rules at this location to these glyphnames. - - Return a new list of glyphNames with substitutions applied. - - - rule order matters - """ - newNames = [] - for rule in rules: - if evaluateRule(rule, location): - for name in glyphNames: - swap = False - for a, b in rule.subs: - if name == a: - swap = True - break - if swap: - newNames.append(b) - else: - newNames.append(name) - glyphNames = newNames - newNames = [] - return glyphNames - - -AnisotropicLocationDict = Dict[str, Union[float, Tuple[float, float]]] -SimpleLocationDict = Dict[str, float] - - -class AxisMappingDescriptor(SimpleDescriptor): - """Represents the axis mapping element: mapping an input location - to an output location in the designspace. - - .. code:: python - - m1 = AxisMappingDescriptor() - m1.inputLocation = {"weight": 900, "width": 150} - m1.outputLocation = {"weight": 870} - - .. code:: xml - - - - - - - - - - - - - """ - - _attrs = ["inputLocation", "outputLocation"] - - def __init__( - self, - *, - inputLocation=None, - outputLocation=None, - description=None, - groupDescription=None, - ): - self.inputLocation: SimpleLocationDict = inputLocation or {} - """dict. Axis values for the input of the mapping, in design space coordinates. - - varLib. - - .. versionadded:: 5.1 - """ - self.outputLocation: SimpleLocationDict = outputLocation or {} - """dict. Axis values for the output of the mapping, in design space coordinates. - - varLib. - - .. versionadded:: 5.1 - """ - self.description = description - """string. A description of the mapping. - - varLib. - - .. versionadded:: 5.2 - """ - self.groupDescription = groupDescription - """string. A description of the group of mappings. - - varLib. - - .. versionadded:: 5.2 - """ - - -class InstanceDescriptor(SimpleDescriptor): - """Simple container for data related to the instance - - - .. code:: python - - i2 = InstanceDescriptor() - i2.path = instancePath2 - i2.familyName = "InstanceFamilyName" - i2.styleName = "InstanceStyleName" - i2.name = "instance.ufo2" - # anisotropic location - i2.designLocation = dict(weight=500, width=(400,300)) - i2.postScriptFontName = "InstancePostscriptName" - i2.styleMapFamilyName = "InstanceStyleMapFamilyName" - i2.styleMapStyleName = "InstanceStyleMapStyleName" - i2.lib['com.coolDesignspaceApp.specimenText'] = 'Hamburgerwhatever' - doc.addInstance(i2) - """ - - flavor = "instance" - _defaultLanguageCode = "en" - _attrs = [ - "filename", - "path", - "name", - "locationLabel", - "designLocation", - "userLocation", - "familyName", - "styleName", - "postScriptFontName", - "styleMapFamilyName", - "styleMapStyleName", - "localisedFamilyName", - "localisedStyleName", - "localisedStyleMapFamilyName", - "localisedStyleMapStyleName", - "glyphs", - "kerning", - "info", - "lib", - ] - - filename = posixpath_property("_filename") - path = posixpath_property("_path") - - def __init__( - self, - *, - filename=None, - path=None, - font=None, - name=None, - location=None, - locationLabel=None, - designLocation=None, - userLocation=None, - familyName=None, - styleName=None, - postScriptFontName=None, - styleMapFamilyName=None, - styleMapStyleName=None, - localisedFamilyName=None, - localisedStyleName=None, - localisedStyleMapFamilyName=None, - localisedStyleMapStyleName=None, - glyphs=None, - kerning=True, - info=True, - lib=None, - ): - self.filename = filename - """string. Relative path to the instance file, **as it is - in the document**. The file may or may not exist. - - MutatorMath + VarLib. - """ - self.path = path - """string. Absolute path to the instance file, calculated from - the document path and the string in the filename attr. The file may - or may not exist. - - MutatorMath. - """ - self.font = font - """Same as :attr:`SourceDescriptor.font` - - .. seealso:: :attr:`SourceDescriptor.font` - """ - self.name = name - """string. Unique identifier name of the instance, used to - identify it if it needs to be referenced from elsewhere in the - document. - """ - self.locationLabel = locationLabel - """Name of a :class:`LocationLabelDescriptor`. If - provided, the instance should have the same location as the - LocationLabel. - - .. seealso:: - :meth:`getFullDesignLocation` - :meth:`getFullUserLocation` - - .. versionadded:: 5.0 - """ - self.designLocation: AnisotropicLocationDict = ( - designLocation if designLocation is not None else (location or {}) - ) - """dict. Axis values for this instance, in design space coordinates. - - MutatorMath + varLib. - - .. seealso:: This may be only part of the full location. See: - :meth:`getFullDesignLocation` - :meth:`getFullUserLocation` - - .. versionadded:: 5.0 - """ - self.userLocation: SimpleLocationDict = userLocation or {} - """dict. Axis values for this instance, in user space coordinates. - - MutatorMath + varLib. - - .. seealso:: This may be only part of the full location. See: - :meth:`getFullDesignLocation` - :meth:`getFullUserLocation` - - .. versionadded:: 5.0 - """ - self.familyName = familyName - """string. Family name of this instance. - - MutatorMath + varLib. - """ - self.styleName = styleName - """string. Style name of this instance. - - MutatorMath + varLib. - """ - self.postScriptFontName = postScriptFontName - """string. Postscript fontname for this instance. - - MutatorMath + varLib. - """ - self.styleMapFamilyName = styleMapFamilyName - """string. StyleMap familyname for this instance. - - MutatorMath + varLib. - """ - self.styleMapStyleName = styleMapStyleName - """string. StyleMap stylename for this instance. - - MutatorMath + varLib. - """ - self.localisedFamilyName = localisedFamilyName or {} - """dict. A dictionary of localised family name - strings, keyed by language code. - """ - self.localisedStyleName = localisedStyleName or {} - """dict. A dictionary of localised stylename - strings, keyed by language code. - """ - self.localisedStyleMapFamilyName = localisedStyleMapFamilyName or {} - """A dictionary of localised style map - familyname strings, keyed by language code. - """ - self.localisedStyleMapStyleName = localisedStyleMapStyleName or {} - """A dictionary of localised style map - stylename strings, keyed by language code. - """ - self.glyphs = glyphs or {} - """dict for special master definitions for glyphs. If glyphs - need special masters (to record the results of executed rules for - example). - - MutatorMath. - - .. deprecated:: 5.0 - Use rules or sparse sources instead. - """ - self.kerning = kerning - """ bool. Indicates if this instance needs its kerning - calculated. - - MutatorMath. - - .. deprecated:: 5.0 - """ - self.info = info - """bool. Indicated if this instance needs the interpolating - font.info calculated. - - .. deprecated:: 5.0 - """ - - self.lib = lib or {} - """Custom data associated with this instance.""" - - @property - def location(self): - """dict. Axis values for this instance. - - MutatorMath + varLib. - - .. deprecated:: 5.0 - Use the more explicit alias for this property :attr:`designLocation`. - """ - return self.designLocation - - @location.setter - def location(self, location: Optional[AnisotropicLocationDict]): - self.designLocation = location or {} - - def setStyleName(self, styleName, languageCode="en"): - """These methods give easier access to the localised names.""" - self.localisedStyleName[languageCode] = tostr(styleName) - - def getStyleName(self, languageCode="en"): - return self.localisedStyleName.get(languageCode) - - def setFamilyName(self, familyName, languageCode="en"): - self.localisedFamilyName[languageCode] = tostr(familyName) - - def getFamilyName(self, languageCode="en"): - return self.localisedFamilyName.get(languageCode) - - def setStyleMapStyleName(self, styleMapStyleName, languageCode="en"): - self.localisedStyleMapStyleName[languageCode] = tostr(styleMapStyleName) - - def getStyleMapStyleName(self, languageCode="en"): - return self.localisedStyleMapStyleName.get(languageCode) - - def setStyleMapFamilyName(self, styleMapFamilyName, languageCode="en"): - self.localisedStyleMapFamilyName[languageCode] = tostr(styleMapFamilyName) - - def getStyleMapFamilyName(self, languageCode="en"): - return self.localisedStyleMapFamilyName.get(languageCode) - - def clearLocation(self, axisName: Optional[str] = None): - """Clear all location-related fields. Ensures that - :attr:``designLocation`` and :attr:``userLocation`` are dictionaries - (possibly empty if clearing everything). - - In order to update the location of this instance wholesale, a user - should first clear all the fields, then change the field(s) for which - they have data. - - .. code:: python - - instance.clearLocation() - instance.designLocation = {'Weight': (34, 36.5), 'Width': 100} - instance.userLocation = {'Opsz': 16} - - In order to update a single axis location, the user should only clear - that axis, then edit the values: - - .. code:: python - - instance.clearLocation('Weight') - instance.designLocation['Weight'] = (34, 36.5) - - Args: - axisName: if provided, only clear the location for that axis. - - .. versionadded:: 5.0 - """ - self.locationLabel = None - if axisName is None: - self.designLocation = {} - self.userLocation = {} - else: - if self.designLocation is None: - self.designLocation = {} - if axisName in self.designLocation: - del self.designLocation[axisName] - if self.userLocation is None: - self.userLocation = {} - if axisName in self.userLocation: - del self.userLocation[axisName] - - def getLocationLabelDescriptor( - self, doc: "DesignSpaceDocument" - ) -> Optional[LocationLabelDescriptor]: - """Get the :class:`LocationLabelDescriptor` instance that matches - this instances's :attr:`locationLabel`. - - Raises if the named label can't be found. - - .. versionadded:: 5.0 - """ - if self.locationLabel is None: - return None - label = doc.getLocationLabel(self.locationLabel) - if label is None: - raise DesignSpaceDocumentError( - "InstanceDescriptor.getLocationLabelDescriptor(): " - f"unknown location label `{self.locationLabel}` in instance `{self.name}`." - ) - return label - - def getFullDesignLocation( - self, doc: "DesignSpaceDocument" - ) -> AnisotropicLocationDict: - """Get the complete design location of this instance, by combining data - from the various location fields, default axis values and mappings, and - top-level location labels. - - The source of truth for this instance's location is determined for each - axis independently by taking the first not-None field in this list: - - - ``locationLabel``: the location along this axis is the same as the - matching STAT format 4 label. No anisotropy. - - ``designLocation[axisName]``: the explicit design location along this - axis, possibly anisotropic. - - ``userLocation[axisName]``: the explicit user location along this - axis. No anisotropy. - - ``axis.default``: default axis value. No anisotropy. - - .. versionadded:: 5.0 - """ - label = self.getLocationLabelDescriptor(doc) - if label is not None: - return doc.map_forward(label.userLocation) # type: ignore - result: AnisotropicLocationDict = {} - for axis in doc.axes: - if axis.name in self.designLocation: - result[axis.name] = self.designLocation[axis.name] - elif axis.name in self.userLocation: - result[axis.name] = axis.map_forward(self.userLocation[axis.name]) - else: - result[axis.name] = axis.map_forward(axis.default) - return result - - def getFullUserLocation(self, doc: "DesignSpaceDocument") -> SimpleLocationDict: - """Get the complete user location for this instance. - - .. seealso:: :meth:`getFullDesignLocation` - - .. versionadded:: 5.0 - """ - return doc.map_backward(self.getFullDesignLocation(doc)) - - -def tagForAxisName(name): - # try to find or make a tag name for this axis name - names = { - "weight": ("wght", dict(en="Weight")), - "width": ("wdth", dict(en="Width")), - "optical": ("opsz", dict(en="Optical Size")), - "slant": ("slnt", dict(en="Slant")), - "italic": ("ital", dict(en="Italic")), - } - if name.lower() in names: - return names[name.lower()] - if len(name) < 4: - tag = name + "*" * (4 - len(name)) - else: - tag = name[:4] - return tag, dict(en=name) - - -class AbstractAxisDescriptor(SimpleDescriptor): - flavor = "axis" - - def __init__( - self, - *, - tag=None, - name=None, - labelNames=None, - hidden=False, - map=None, - axisOrdering=None, - axisLabels=None, - ): - # opentype tag for this axis - self.tag = tag - """string. Four letter tag for this axis. Some might be - registered at the `OpenType - specification `__. - Privately-defined axis tags must begin with an uppercase letter and - use only uppercase letters or digits. - """ - # name of the axis used in locations - self.name = name - """string. Name of the axis as it is used in the location dicts. - - MutatorMath + varLib. - """ - # names for UI purposes, if this is not a standard axis, - self.labelNames = labelNames or {} - """dict. When defining a non-registered axis, it will be - necessary to define user-facing readable names for the axis. Keyed by - xml:lang code. Values are required to be ``unicode`` strings, even if - they only contain ASCII characters. - """ - self.hidden = hidden - """bool. Whether this axis should be hidden in user interfaces. - """ - self.map = map or [] - """list of input / output values that can describe a warp of user space - to design space coordinates. If no map values are present, it is assumed - user space is the same as design space, as in [(minimum, minimum), - (maximum, maximum)]. - - varLib. - """ - self.axisOrdering = axisOrdering - """STAT table field ``axisOrdering``. - - See: `OTSpec STAT Axis Record `_ - - .. versionadded:: 5.0 - """ - self.axisLabels: List[AxisLabelDescriptor] = axisLabels or [] - """STAT table entries for Axis Value Tables format 1, 2, 3. - - See: `OTSpec STAT Axis Value Tables `_ - - .. versionadded:: 5.0 - """ - - -class AxisDescriptor(AbstractAxisDescriptor): - """Simple container for the axis data. - - Add more localisations? - - .. code:: python - - a1 = AxisDescriptor() - a1.minimum = 1 - a1.maximum = 1000 - a1.default = 400 - a1.name = "weight" - a1.tag = "wght" - a1.labelNames['fa-IR'] = "قطر" - a1.labelNames['en'] = "Wéíght" - a1.map = [(1.0, 10.0), (400.0, 66.0), (1000.0, 990.0)] - a1.axisOrdering = 1 - a1.axisLabels = [ - AxisLabelDescriptor(name="Regular", userValue=400, elidable=True) - ] - doc.addAxis(a1) - """ - - _attrs = [ - "tag", - "name", - "maximum", - "minimum", - "default", - "map", - "axisOrdering", - "axisLabels", - ] - - def __init__( - self, - *, - tag=None, - name=None, - labelNames=None, - minimum=None, - default=None, - maximum=None, - hidden=False, - map=None, - axisOrdering=None, - axisLabels=None, - ): - super().__init__( - tag=tag, - name=name, - labelNames=labelNames, - hidden=hidden, - map=map, - axisOrdering=axisOrdering, - axisLabels=axisLabels, - ) - self.minimum = minimum - """number. The minimum value for this axis in user space. - - MutatorMath + varLib. - """ - self.maximum = maximum - """number. The maximum value for this axis in user space. - - MutatorMath + varLib. - """ - self.default = default - """number. The default value for this axis, i.e. when a new location is - created, this is the value this axis will get in user space. - - MutatorMath + varLib. - """ - - def serialize(self): - # output to a dict, used in testing - return dict( - tag=self.tag, - name=self.name, - labelNames=self.labelNames, - maximum=self.maximum, - minimum=self.minimum, - default=self.default, - hidden=self.hidden, - map=self.map, - axisOrdering=self.axisOrdering, - axisLabels=self.axisLabels, - ) - - def map_forward(self, v): - """Maps value from axis mapping's input (user) to output (design).""" - from fontTools.varLib.models import piecewiseLinearMap - - if not self.map: - return v - return piecewiseLinearMap(v, {k: v for k, v in self.map}) - - def map_backward(self, v): - """Maps value from axis mapping's output (design) to input (user).""" - from fontTools.varLib.models import piecewiseLinearMap - - if isinstance(v, tuple): - v = v[0] - if not self.map: - return v - return piecewiseLinearMap(v, {v: k for k, v in self.map}) - - -class DiscreteAxisDescriptor(AbstractAxisDescriptor): - """Container for discrete axis data. - - Use this for axes that do not interpolate. The main difference from a - continuous axis is that a continuous axis has a ``minimum`` and ``maximum``, - while a discrete axis has a list of ``values``. - - Example: an Italic axis with 2 stops, Roman and Italic, that are not - compatible. The axis still allows to bind together the full font family, - which is useful for the STAT table, however it can't become a variation - axis in a VF. - - .. code:: python - - a2 = DiscreteAxisDescriptor() - a2.values = [0, 1] - a2.default = 0 - a2.name = "Italic" - a2.tag = "ITAL" - a2.labelNames['fr'] = "Italique" - a2.map = [(0, 0), (1, -11)] - a2.axisOrdering = 2 - a2.axisLabels = [ - AxisLabelDescriptor(name="Roman", userValue=0, elidable=True) - ] - doc.addAxis(a2) - - .. versionadded:: 5.0 - """ - - flavor = "axis" - _attrs = ("tag", "name", "values", "default", "map", "axisOrdering", "axisLabels") - - def __init__( - self, - *, - tag=None, - name=None, - labelNames=None, - values=None, - default=None, - hidden=False, - map=None, - axisOrdering=None, - axisLabels=None, - ): - super().__init__( - tag=tag, - name=name, - labelNames=labelNames, - hidden=hidden, - map=map, - axisOrdering=axisOrdering, - axisLabels=axisLabels, - ) - self.default: float = default - """The default value for this axis, i.e. when a new location is - created, this is the value this axis will get in user space. - - However, this default value is less important than in continuous axes: - - - it doesn't define the "neutral" version of outlines from which - deltas would apply, as this axis does not interpolate. - - it doesn't provide the reference glyph set for the designspace, as - fonts at each value can have different glyph sets. - """ - self.values: List[float] = values or [] - """List of possible values for this axis. Contrary to continuous axes, - only the values in this list can be taken by the axis, nothing in-between. - """ - - def map_forward(self, value): - """Maps value from axis mapping's input to output. - - Returns value unchanged if no mapping entry is found. - - Note: for discrete axes, each value must have its mapping entry, if - you intend that value to be mapped. - """ - return next((v for k, v in self.map if k == value), value) - - def map_backward(self, value): - """Maps value from axis mapping's output to input. - - Returns value unchanged if no mapping entry is found. - - Note: for discrete axes, each value must have its mapping entry, if - you intend that value to be mapped. - """ - if isinstance(value, tuple): - value = value[0] - return next((k for k, v in self.map if v == value), value) - - -class AxisLabelDescriptor(SimpleDescriptor): - """Container for axis label data. - - Analogue of OpenType's STAT data for a single axis (formats 1, 2 and 3). - All values are user values. - See: `OTSpec STAT Axis value table, format 1, 2, 3 `_ - - The STAT format of the Axis value depends on which field are filled-in, - see :meth:`getFormat` - - .. versionadded:: 5.0 - """ - - flavor = "label" - _attrs = ( - "userMinimum", - "userValue", - "userMaximum", - "name", - "elidable", - "olderSibling", - "linkedUserValue", - "labelNames", - ) - - def __init__( - self, - *, - name, - userValue, - userMinimum=None, - userMaximum=None, - elidable=False, - olderSibling=False, - linkedUserValue=None, - labelNames=None, - ): - self.userMinimum: Optional[float] = userMinimum - """STAT field ``rangeMinValue`` (format 2).""" - self.userValue: float = userValue - """STAT field ``value`` (format 1, 3) or ``nominalValue`` (format 2).""" - self.userMaximum: Optional[float] = userMaximum - """STAT field ``rangeMaxValue`` (format 2).""" - self.name: str = name - """Label for this axis location, STAT field ``valueNameID``.""" - self.elidable: bool = elidable - """STAT flag ``ELIDABLE_AXIS_VALUE_NAME``. - - See: `OTSpec STAT Flags `_ - """ - self.olderSibling: bool = olderSibling - """STAT flag ``OLDER_SIBLING_FONT_ATTRIBUTE``. - - See: `OTSpec STAT Flags `_ - """ - self.linkedUserValue: Optional[float] = linkedUserValue - """STAT field ``linkedValue`` (format 3).""" - self.labelNames: MutableMapping[str, str] = labelNames or {} - """User-facing translations of this location's label. Keyed by - ``xml:lang`` code. - """ - - def getFormat(self) -> int: - """Determine which format of STAT Axis value to use to encode this label. - - =========== ========= =========== =========== =============== - STAT Format userValue userMinimum userMaximum linkedUserValue - =========== ========= =========== =========== =============== - 1 ✅ ⌠⌠⌠- 2 ✅ ✅ ✅ ⌠- 3 ✅ ⌠⌠✅ - =========== ========= =========== =========== =============== - """ - if self.linkedUserValue is not None: - return 3 - if self.userMinimum is not None or self.userMaximum is not None: - return 2 - return 1 - - @property - def defaultName(self) -> str: - """Return the English name from :attr:`labelNames` or the :attr:`name`.""" - return self.labelNames.get("en") or self.name - - -class LocationLabelDescriptor(SimpleDescriptor): - """Container for location label data. - - Analogue of OpenType's STAT data for a free-floating location (format 4). - All values are user values. - - See: `OTSpec STAT Axis value table, format 4 `_ - - .. versionadded:: 5.0 - """ - - flavor = "label" - _attrs = ("name", "elidable", "olderSibling", "userLocation", "labelNames") - - def __init__( - self, - *, - name, - userLocation, - elidable=False, - olderSibling=False, - labelNames=None, - ): - self.name: str = name - """Label for this named location, STAT field ``valueNameID``.""" - self.userLocation: SimpleLocationDict = userLocation or {} - """Location in user coordinates along each axis. - - If an axis is not mentioned, it is assumed to be at its default location. - - .. seealso:: This may be only part of the full location. See: - :meth:`getFullUserLocation` - """ - self.elidable: bool = elidable - """STAT flag ``ELIDABLE_AXIS_VALUE_NAME``. - - See: `OTSpec STAT Flags `_ - """ - self.olderSibling: bool = olderSibling - """STAT flag ``OLDER_SIBLING_FONT_ATTRIBUTE``. - - See: `OTSpec STAT Flags `_ - """ - self.labelNames: Dict[str, str] = labelNames or {} - """User-facing translations of this location's label. Keyed by - xml:lang code. - """ - - @property - def defaultName(self) -> str: - """Return the English name from :attr:`labelNames` or the :attr:`name`.""" - return self.labelNames.get("en") or self.name - - def getFullUserLocation(self, doc: "DesignSpaceDocument") -> SimpleLocationDict: - """Get the complete user location of this label, by combining data - from the explicit user location and default axis values. - - .. versionadded:: 5.0 - """ - return { - axis.name: self.userLocation.get(axis.name, axis.default) - for axis in doc.axes - } - - -class VariableFontDescriptor(SimpleDescriptor): - """Container for variable fonts, sub-spaces of the Designspace. - - Use-cases: - - - From a single DesignSpace with discrete axes, define 1 variable font - per value on the discrete axes. Before version 5, you would have needed - 1 DesignSpace per such variable font, and a lot of data duplication. - - From a big variable font with many axes, define subsets of that variable - font that only include some axes and freeze other axes at a given location. - - .. versionadded:: 5.0 - """ - - flavor = "variable-font" - _attrs = ("filename", "axisSubsets", "lib") - - filename = posixpath_property("_filename") - - def __init__(self, *, name, filename=None, axisSubsets=None, lib=None): - self.name: str = name - """string, required. Name of this variable to identify it during the - build process and from other parts of the document, and also as a - filename in case the filename property is empty. - - VarLib. - """ - self.filename: str = filename - """string, optional. Relative path to the variable font file, **as it is - in the document**. The file may or may not exist. - - If not specified, the :attr:`name` will be used as a basename for the file. - """ - self.axisSubsets: List[ - Union[RangeAxisSubsetDescriptor, ValueAxisSubsetDescriptor] - ] = (axisSubsets or []) - """Axis subsets to include in this variable font. - - If an axis is not mentioned, assume that we only want the default - location of that axis (same as a :class:`ValueAxisSubsetDescriptor`). - """ - self.lib: MutableMapping[str, Any] = lib or {} - """Custom data associated with this variable font.""" - - -class RangeAxisSubsetDescriptor(SimpleDescriptor): - """Subset of a continuous axis to include in a variable font. - - .. versionadded:: 5.0 - """ - - flavor = "axis-subset" - _attrs = ("name", "userMinimum", "userDefault", "userMaximum") - - def __init__( - self, *, name, userMinimum=-math.inf, userDefault=None, userMaximum=math.inf - ): - self.name: str = name - """Name of the :class:`AxisDescriptor` to subset.""" - self.userMinimum: float = userMinimum - """New minimum value of the axis in the target variable font. - If not specified, assume the same minimum value as the full axis. - (default = ``-math.inf``) - """ - self.userDefault: Optional[float] = userDefault - """New default value of the axis in the target variable font. - If not specified, assume the same default value as the full axis. - (default = ``None``) - """ - self.userMaximum: float = userMaximum - """New maximum value of the axis in the target variable font. - If not specified, assume the same maximum value as the full axis. - (default = ``math.inf``) - """ - - -class ValueAxisSubsetDescriptor(SimpleDescriptor): - """Single value of a discrete or continuous axis to use in a variable font. - - .. versionadded:: 5.0 - """ - - flavor = "axis-subset" - _attrs = ("name", "userValue") - - def __init__(self, *, name, userValue): - self.name: str = name - """Name of the :class:`AxisDescriptor` or :class:`DiscreteAxisDescriptor` - to "snapshot" or "freeze". - """ - self.userValue: float = userValue - """Value in user coordinates at which to freeze the given axis.""" - - -class BaseDocWriter(object): - _whiteSpace = " " - axisDescriptorClass = AxisDescriptor - discreteAxisDescriptorClass = DiscreteAxisDescriptor - axisLabelDescriptorClass = AxisLabelDescriptor - axisMappingDescriptorClass = AxisMappingDescriptor - locationLabelDescriptorClass = LocationLabelDescriptor - ruleDescriptorClass = RuleDescriptor - sourceDescriptorClass = SourceDescriptor - variableFontDescriptorClass = VariableFontDescriptor - valueAxisSubsetDescriptorClass = ValueAxisSubsetDescriptor - rangeAxisSubsetDescriptorClass = RangeAxisSubsetDescriptor - instanceDescriptorClass = InstanceDescriptor - - @classmethod - def getAxisDecriptor(cls): - return cls.axisDescriptorClass() - - @classmethod - def getAxisMappingDescriptor(cls): - return cls.axisMappingDescriptorClass() - - @classmethod - def getSourceDescriptor(cls): - return cls.sourceDescriptorClass() - - @classmethod - def getInstanceDescriptor(cls): - return cls.instanceDescriptorClass() - - @classmethod - def getRuleDescriptor(cls): - return cls.ruleDescriptorClass() - - def __init__(self, documentPath, documentObject: DesignSpaceDocument): - self.path = documentPath - self.documentObject = documentObject - self.effectiveFormatTuple = self._getEffectiveFormatTuple() - self.root = ET.Element("designspace") - - def write(self, pretty=True, encoding="UTF-8", xml_declaration=True): - self.root.attrib["format"] = ".".join(str(i) for i in self.effectiveFormatTuple) - - if ( - self.documentObject.axes - or self.documentObject.axisMappings - or self.documentObject.elidedFallbackName is not None - ): - axesElement = ET.Element("axes") - if self.documentObject.elidedFallbackName is not None: - axesElement.attrib["elidedfallbackname"] = ( - self.documentObject.elidedFallbackName - ) - self.root.append(axesElement) - for axisObject in self.documentObject.axes: - self._addAxis(axisObject) - - if self.documentObject.axisMappings: - mappingsElement = None - lastGroup = object() - for mappingObject in self.documentObject.axisMappings: - if getattr(mappingObject, "groupDescription", None) != lastGroup: - if mappingsElement is not None: - self.root.findall(".axes")[0].append(mappingsElement) - lastGroup = getattr(mappingObject, "groupDescription", None) - mappingsElement = ET.Element("mappings") - if lastGroup is not None: - mappingsElement.attrib["description"] = lastGroup - self._addAxisMapping(mappingsElement, mappingObject) - if mappingsElement is not None: - self.root.findall(".axes")[0].append(mappingsElement) - - if self.documentObject.locationLabels: - labelsElement = ET.Element("labels") - for labelObject in self.documentObject.locationLabels: - self._addLocationLabel(labelsElement, labelObject) - self.root.append(labelsElement) - - if self.documentObject.rules: - if getattr(self.documentObject, "rulesProcessingLast", False): - attributes = {"processing": "last"} - else: - attributes = {} - self.root.append(ET.Element("rules", attributes)) - for ruleObject in self.documentObject.rules: - self._addRule(ruleObject) - - if self.documentObject.sources: - self.root.append(ET.Element("sources")) - for sourceObject in self.documentObject.sources: - self._addSource(sourceObject) - - if self.documentObject.variableFonts: - variableFontsElement = ET.Element("variable-fonts") - for variableFont in self.documentObject.variableFonts: - self._addVariableFont(variableFontsElement, variableFont) - self.root.append(variableFontsElement) - - if self.documentObject.instances: - self.root.append(ET.Element("instances")) - for instanceObject in self.documentObject.instances: - self._addInstance(instanceObject) - - if self.documentObject.lib: - self._addLib(self.root, self.documentObject.lib, 2) - - tree = ET.ElementTree(self.root) - tree.write( - self.path, - encoding=encoding, - method="xml", - xml_declaration=xml_declaration, - pretty_print=pretty, - ) - - def _getEffectiveFormatTuple(self): - """Try to use the version specified in the document, or a sufficiently - recent version to be able to encode what the document contains. - """ - minVersion = self.documentObject.formatTuple - if ( - any( - hasattr(axis, "values") - or axis.axisOrdering is not None - or axis.axisLabels - for axis in self.documentObject.axes - ) - or self.documentObject.locationLabels - or any(source.localisedFamilyName for source in self.documentObject.sources) - or self.documentObject.variableFonts - or any( - instance.locationLabel or instance.userLocation - for instance in self.documentObject.instances - ) - ): - if minVersion < (5, 0): - minVersion = (5, 0) - if self.documentObject.axisMappings: - if minVersion < (5, 1): - minVersion = (5, 1) - return minVersion - - def _makeLocationElement(self, locationObject, name=None): - """Convert Location dict to a locationElement.""" - locElement = ET.Element("location") - if name is not None: - locElement.attrib["name"] = name - validatedLocation = self.documentObject.newDefaultLocation() - for axisName, axisValue in locationObject.items(): - if axisName in validatedLocation: - # only accept values we know - validatedLocation[axisName] = axisValue - for dimensionName, dimensionValue in validatedLocation.items(): - dimElement = ET.Element("dimension") - dimElement.attrib["name"] = dimensionName - if type(dimensionValue) == tuple: - dimElement.attrib["xvalue"] = self.intOrFloat(dimensionValue[0]) - dimElement.attrib["yvalue"] = self.intOrFloat(dimensionValue[1]) - else: - dimElement.attrib["xvalue"] = self.intOrFloat(dimensionValue) - locElement.append(dimElement) - return locElement, validatedLocation - - def intOrFloat(self, num): - if int(num) == num: - return "%d" % num - return ("%f" % num).rstrip("0").rstrip(".") - - def _addRule(self, ruleObject): - # if none of the conditions have minimum or maximum values, do not add the rule. - ruleElement = ET.Element("rule") - if ruleObject.name is not None: - ruleElement.attrib["name"] = ruleObject.name - for conditions in ruleObject.conditionSets: - conditionsetElement = ET.Element("conditionset") - for cond in conditions: - if cond.get("minimum") is None and cond.get("maximum") is None: - # neither is defined, don't add this condition - continue - conditionElement = ET.Element("condition") - conditionElement.attrib["name"] = cond.get("name") - if cond.get("minimum") is not None: - conditionElement.attrib["minimum"] = self.intOrFloat( - cond.get("minimum") - ) - if cond.get("maximum") is not None: - conditionElement.attrib["maximum"] = self.intOrFloat( - cond.get("maximum") - ) - conditionsetElement.append(conditionElement) - if len(conditionsetElement): - ruleElement.append(conditionsetElement) - for sub in ruleObject.subs: - subElement = ET.Element("sub") - subElement.attrib["name"] = sub[0] - subElement.attrib["with"] = sub[1] - ruleElement.append(subElement) - if len(ruleElement): - self.root.findall(".rules")[0].append(ruleElement) - - def _addAxis(self, axisObject): - axisElement = ET.Element("axis") - axisElement.attrib["tag"] = axisObject.tag - axisElement.attrib["name"] = axisObject.name - self._addLabelNames(axisElement, axisObject.labelNames) - if axisObject.map: - for inputValue, outputValue in axisObject.map: - mapElement = ET.Element("map") - mapElement.attrib["input"] = self.intOrFloat(inputValue) - mapElement.attrib["output"] = self.intOrFloat(outputValue) - axisElement.append(mapElement) - if axisObject.axisOrdering or axisObject.axisLabels: - labelsElement = ET.Element("labels") - if axisObject.axisOrdering is not None: - labelsElement.attrib["ordering"] = str(axisObject.axisOrdering) - for label in axisObject.axisLabels: - self._addAxisLabel(labelsElement, label) - axisElement.append(labelsElement) - if hasattr(axisObject, "minimum"): - axisElement.attrib["minimum"] = self.intOrFloat(axisObject.minimum) - axisElement.attrib["maximum"] = self.intOrFloat(axisObject.maximum) - elif hasattr(axisObject, "values"): - axisElement.attrib["values"] = " ".join( - self.intOrFloat(v) for v in axisObject.values - ) - axisElement.attrib["default"] = self.intOrFloat(axisObject.default) - if axisObject.hidden: - axisElement.attrib["hidden"] = "1" - self.root.findall(".axes")[0].append(axisElement) - - def _addAxisMapping(self, mappingsElement, mappingObject): - mappingElement = ET.Element("mapping") - if getattr(mappingObject, "description", None) is not None: - mappingElement.attrib["description"] = mappingObject.description - for what in ("inputLocation", "outputLocation"): - whatObject = getattr(mappingObject, what, None) - if whatObject is None: - continue - whatElement = ET.Element(what[:-8]) - mappingElement.append(whatElement) - - for name, value in whatObject.items(): - dimensionElement = ET.Element("dimension") - dimensionElement.attrib["name"] = name - dimensionElement.attrib["xvalue"] = self.intOrFloat(value) - whatElement.append(dimensionElement) - - mappingsElement.append(mappingElement) - - def _addAxisLabel( - self, axisElement: ET.Element, label: AxisLabelDescriptor - ) -> None: - labelElement = ET.Element("label") - labelElement.attrib["uservalue"] = self.intOrFloat(label.userValue) - if label.userMinimum is not None: - labelElement.attrib["userminimum"] = self.intOrFloat(label.userMinimum) - if label.userMaximum is not None: - labelElement.attrib["usermaximum"] = self.intOrFloat(label.userMaximum) - labelElement.attrib["name"] = label.name - if label.elidable: - labelElement.attrib["elidable"] = "true" - if label.olderSibling: - labelElement.attrib["oldersibling"] = "true" - if label.linkedUserValue is not None: - labelElement.attrib["linkeduservalue"] = self.intOrFloat( - label.linkedUserValue - ) - self._addLabelNames(labelElement, label.labelNames) - axisElement.append(labelElement) - - def _addLabelNames(self, parentElement, labelNames): - for languageCode, labelName in sorted(labelNames.items()): - languageElement = ET.Element("labelname") - languageElement.attrib[XML_LANG] = languageCode - languageElement.text = labelName - parentElement.append(languageElement) - - def _addLocationLabel( - self, parentElement: ET.Element, label: LocationLabelDescriptor - ) -> None: - labelElement = ET.Element("label") - labelElement.attrib["name"] = label.name - if label.elidable: - labelElement.attrib["elidable"] = "true" - if label.olderSibling: - labelElement.attrib["oldersibling"] = "true" - self._addLabelNames(labelElement, label.labelNames) - self._addLocationElement(labelElement, userLocation=label.userLocation) - parentElement.append(labelElement) - - def _addLocationElement( - self, - parentElement, - *, - designLocation: AnisotropicLocationDict = None, - userLocation: SimpleLocationDict = None, - ): - locElement = ET.Element("location") - for axis in self.documentObject.axes: - if designLocation is not None and axis.name in designLocation: - dimElement = ET.Element("dimension") - dimElement.attrib["name"] = axis.name - value = designLocation[axis.name] - if isinstance(value, tuple): - dimElement.attrib["xvalue"] = self.intOrFloat(value[0]) - dimElement.attrib["yvalue"] = self.intOrFloat(value[1]) - else: - dimElement.attrib["xvalue"] = self.intOrFloat(value) - locElement.append(dimElement) - elif userLocation is not None and axis.name in userLocation: - dimElement = ET.Element("dimension") - dimElement.attrib["name"] = axis.name - value = userLocation[axis.name] - dimElement.attrib["uservalue"] = self.intOrFloat(value) - locElement.append(dimElement) - if len(locElement) > 0: - parentElement.append(locElement) - - def _addInstance(self, instanceObject): - instanceElement = ET.Element("instance") - if instanceObject.name is not None: - instanceElement.attrib["name"] = instanceObject.name - if instanceObject.locationLabel is not None: - instanceElement.attrib["location"] = instanceObject.locationLabel - if instanceObject.familyName is not None: - instanceElement.attrib["familyname"] = instanceObject.familyName - if instanceObject.styleName is not None: - instanceElement.attrib["stylename"] = instanceObject.styleName - # add localisations - if instanceObject.localisedStyleName: - languageCodes = list(instanceObject.localisedStyleName.keys()) - languageCodes.sort() - for code in languageCodes: - if code == "en": - continue # already stored in the element attribute - localisedStyleNameElement = ET.Element("stylename") - localisedStyleNameElement.attrib[XML_LANG] = code - localisedStyleNameElement.text = instanceObject.getStyleName(code) - instanceElement.append(localisedStyleNameElement) - if instanceObject.localisedFamilyName: - languageCodes = list(instanceObject.localisedFamilyName.keys()) - languageCodes.sort() - for code in languageCodes: - if code == "en": - continue # already stored in the element attribute - localisedFamilyNameElement = ET.Element("familyname") - localisedFamilyNameElement.attrib[XML_LANG] = code - localisedFamilyNameElement.text = instanceObject.getFamilyName(code) - instanceElement.append(localisedFamilyNameElement) - if instanceObject.localisedStyleMapStyleName: - languageCodes = list(instanceObject.localisedStyleMapStyleName.keys()) - languageCodes.sort() - for code in languageCodes: - if code == "en": - continue - localisedStyleMapStyleNameElement = ET.Element("stylemapstylename") - localisedStyleMapStyleNameElement.attrib[XML_LANG] = code - localisedStyleMapStyleNameElement.text = ( - instanceObject.getStyleMapStyleName(code) - ) - instanceElement.append(localisedStyleMapStyleNameElement) - if instanceObject.localisedStyleMapFamilyName: - languageCodes = list(instanceObject.localisedStyleMapFamilyName.keys()) - languageCodes.sort() - for code in languageCodes: - if code == "en": - continue - localisedStyleMapFamilyNameElement = ET.Element("stylemapfamilyname") - localisedStyleMapFamilyNameElement.attrib[XML_LANG] = code - localisedStyleMapFamilyNameElement.text = ( - instanceObject.getStyleMapFamilyName(code) - ) - instanceElement.append(localisedStyleMapFamilyNameElement) - - if self.effectiveFormatTuple >= (5, 0): - if instanceObject.locationLabel is None: - self._addLocationElement( - instanceElement, - designLocation=instanceObject.designLocation, - userLocation=instanceObject.userLocation, - ) - else: - # Pre-version 5.0 code was validating and filling in the location - # dict while writing it out, as preserved below. - if instanceObject.location is not None: - locationElement, instanceObject.location = self._makeLocationElement( - instanceObject.location - ) - instanceElement.append(locationElement) - if instanceObject.filename is not None: - instanceElement.attrib["filename"] = instanceObject.filename - if instanceObject.postScriptFontName is not None: - instanceElement.attrib["postscriptfontname"] = ( - instanceObject.postScriptFontName - ) - if instanceObject.styleMapFamilyName is not None: - instanceElement.attrib["stylemapfamilyname"] = ( - instanceObject.styleMapFamilyName - ) - if instanceObject.styleMapStyleName is not None: - instanceElement.attrib["stylemapstylename"] = ( - instanceObject.styleMapStyleName - ) - if self.effectiveFormatTuple < (5, 0): - # Deprecated members as of version 5.0 - if instanceObject.glyphs: - if instanceElement.findall(".glyphs") == []: - glyphsElement = ET.Element("glyphs") - instanceElement.append(glyphsElement) - glyphsElement = instanceElement.findall(".glyphs")[0] - for glyphName, data in sorted(instanceObject.glyphs.items()): - glyphElement = self._writeGlyphElement( - instanceElement, instanceObject, glyphName, data - ) - glyphsElement.append(glyphElement) - if instanceObject.kerning: - kerningElement = ET.Element("kerning") - instanceElement.append(kerningElement) - if instanceObject.info: - infoElement = ET.Element("info") - instanceElement.append(infoElement) - self._addLib(instanceElement, instanceObject.lib, 4) - self.root.findall(".instances")[0].append(instanceElement) - - def _addSource(self, sourceObject): - sourceElement = ET.Element("source") - if sourceObject.filename is not None: - sourceElement.attrib["filename"] = sourceObject.filename - if sourceObject.name is not None: - if sourceObject.name.find("temp_master") != 0: - # do not save temporary source names - sourceElement.attrib["name"] = sourceObject.name - if sourceObject.familyName is not None: - sourceElement.attrib["familyname"] = sourceObject.familyName - if sourceObject.styleName is not None: - sourceElement.attrib["stylename"] = sourceObject.styleName - if sourceObject.layerName is not None: - sourceElement.attrib["layer"] = sourceObject.layerName - if sourceObject.localisedFamilyName: - languageCodes = list(sourceObject.localisedFamilyName.keys()) - languageCodes.sort() - for code in languageCodes: - if code == "en": - continue # already stored in the element attribute - localisedFamilyNameElement = ET.Element("familyname") - localisedFamilyNameElement.attrib[XML_LANG] = code - localisedFamilyNameElement.text = sourceObject.getFamilyName(code) - sourceElement.append(localisedFamilyNameElement) - if sourceObject.copyLib: - libElement = ET.Element("lib") - libElement.attrib["copy"] = "1" - sourceElement.append(libElement) - if sourceObject.copyGroups: - groupsElement = ET.Element("groups") - groupsElement.attrib["copy"] = "1" - sourceElement.append(groupsElement) - if sourceObject.copyFeatures: - featuresElement = ET.Element("features") - featuresElement.attrib["copy"] = "1" - sourceElement.append(featuresElement) - if sourceObject.copyInfo or sourceObject.muteInfo: - infoElement = ET.Element("info") - if sourceObject.copyInfo: - infoElement.attrib["copy"] = "1" - if sourceObject.muteInfo: - infoElement.attrib["mute"] = "1" - sourceElement.append(infoElement) - if sourceObject.muteKerning: - kerningElement = ET.Element("kerning") - kerningElement.attrib["mute"] = "1" - sourceElement.append(kerningElement) - if sourceObject.mutedGlyphNames: - for name in sourceObject.mutedGlyphNames: - glyphElement = ET.Element("glyph") - glyphElement.attrib["name"] = name - glyphElement.attrib["mute"] = "1" - sourceElement.append(glyphElement) - if self.effectiveFormatTuple >= (5, 0): - self._addLocationElement( - sourceElement, designLocation=sourceObject.location - ) - else: - # Pre-version 5.0 code was validating and filling in the location - # dict while writing it out, as preserved below. - locationElement, sourceObject.location = self._makeLocationElement( - sourceObject.location - ) - sourceElement.append(locationElement) - self.root.findall(".sources")[0].append(sourceElement) - - def _addVariableFont( - self, parentElement: ET.Element, vf: VariableFontDescriptor - ) -> None: - vfElement = ET.Element("variable-font") - vfElement.attrib["name"] = vf.name - if vf.filename is not None: - vfElement.attrib["filename"] = vf.filename - if vf.axisSubsets: - subsetsElement = ET.Element("axis-subsets") - for subset in vf.axisSubsets: - subsetElement = ET.Element("axis-subset") - subsetElement.attrib["name"] = subset.name - # Mypy doesn't support narrowing union types via hasattr() - # https://mypy.readthedocs.io/en/stable/type_narrowing.html - # TODO(Python 3.10): use TypeGuard - if hasattr(subset, "userMinimum"): - subset = cast(RangeAxisSubsetDescriptor, subset) - if subset.userMinimum != -math.inf: - subsetElement.attrib["userminimum"] = self.intOrFloat( - subset.userMinimum - ) - if subset.userMaximum != math.inf: - subsetElement.attrib["usermaximum"] = self.intOrFloat( - subset.userMaximum - ) - if subset.userDefault is not None: - subsetElement.attrib["userdefault"] = self.intOrFloat( - subset.userDefault - ) - elif hasattr(subset, "userValue"): - subset = cast(ValueAxisSubsetDescriptor, subset) - subsetElement.attrib["uservalue"] = self.intOrFloat( - subset.userValue - ) - subsetsElement.append(subsetElement) - vfElement.append(subsetsElement) - self._addLib(vfElement, vf.lib, 4) - parentElement.append(vfElement) - - def _addLib(self, parentElement: ET.Element, data: Any, indent_level: int) -> None: - if not data: - return - libElement = ET.Element("lib") - libElement.append(plistlib.totree(data, indent_level=indent_level)) - parentElement.append(libElement) - - def _writeGlyphElement(self, instanceElement, instanceObject, glyphName, data): - glyphElement = ET.Element("glyph") - if data.get("mute"): - glyphElement.attrib["mute"] = "1" - if data.get("unicodes") is not None: - glyphElement.attrib["unicode"] = " ".join( - [hex(u) for u in data.get("unicodes")] - ) - if data.get("instanceLocation") is not None: - locationElement, data["instanceLocation"] = self._makeLocationElement( - data.get("instanceLocation") - ) - glyphElement.append(locationElement) - if glyphName is not None: - glyphElement.attrib["name"] = glyphName - if data.get("note") is not None: - noteElement = ET.Element("note") - noteElement.text = data.get("note") - glyphElement.append(noteElement) - if data.get("masters") is not None: - mastersElement = ET.Element("masters") - for m in data.get("masters"): - masterElement = ET.Element("master") - if m.get("glyphName") is not None: - masterElement.attrib["glyphname"] = m.get("glyphName") - if m.get("font") is not None: - masterElement.attrib["source"] = m.get("font") - if m.get("location") is not None: - locationElement, m["location"] = self._makeLocationElement( - m.get("location") - ) - masterElement.append(locationElement) - mastersElement.append(masterElement) - glyphElement.append(mastersElement) - return glyphElement - - -class BaseDocReader(LogMixin): - axisDescriptorClass = AxisDescriptor - discreteAxisDescriptorClass = DiscreteAxisDescriptor - axisLabelDescriptorClass = AxisLabelDescriptor - axisMappingDescriptorClass = AxisMappingDescriptor - locationLabelDescriptorClass = LocationLabelDescriptor - ruleDescriptorClass = RuleDescriptor - sourceDescriptorClass = SourceDescriptor - variableFontsDescriptorClass = VariableFontDescriptor - valueAxisSubsetDescriptorClass = ValueAxisSubsetDescriptor - rangeAxisSubsetDescriptorClass = RangeAxisSubsetDescriptor - instanceDescriptorClass = InstanceDescriptor - - def __init__(self, documentPath, documentObject): - self.path = documentPath - self.documentObject = documentObject - tree = ET.parse(self.path) - self.root = tree.getroot() - self.documentObject.formatVersion = self.root.attrib.get("format", "3.0") - self._axes = [] - self.rules = [] - self.sources = [] - self.instances = [] - self.axisDefaults = {} - self._strictAxisNames = True - - @classmethod - def fromstring(cls, string, documentObject): - f = BytesIO(tobytes(string, encoding="utf-8")) - self = cls(f, documentObject) - self.path = None - return self - - def read(self): - self.readAxes() - self.readLabels() - self.readRules() - self.readVariableFonts() - self.readSources() - self.readInstances() - self.readLib() - - def readRules(self): - # we also need to read any conditions that are outside of a condition set. - rules = [] - rulesElement = self.root.find(".rules") - if rulesElement is not None: - processingValue = rulesElement.attrib.get("processing", "first") - if processingValue not in {"first", "last"}: - raise DesignSpaceDocumentError( - " processing attribute value is not valid: %r, " - "expected 'first' or 'last'" % processingValue - ) - self.documentObject.rulesProcessingLast = processingValue == "last" - for ruleElement in self.root.findall(".rules/rule"): - ruleObject = self.ruleDescriptorClass() - ruleName = ruleObject.name = ruleElement.attrib.get("name") - # read any stray conditions outside a condition set - externalConditions = self._readConditionElements( - ruleElement, - ruleName, - ) - if externalConditions: - ruleObject.conditionSets.append(externalConditions) - self.log.info( - "Found stray rule conditions outside a conditionset. " - "Wrapped them in a new conditionset." - ) - # read the conditionsets - for conditionSetElement in ruleElement.findall(".conditionset"): - conditionSet = self._readConditionElements( - conditionSetElement, - ruleName, - ) - if conditionSet is not None: - ruleObject.conditionSets.append(conditionSet) - for subElement in ruleElement.findall(".sub"): - a = subElement.attrib["name"] - b = subElement.attrib["with"] - ruleObject.subs.append((a, b)) - rules.append(ruleObject) - self.documentObject.rules = rules - - def _readConditionElements(self, parentElement, ruleName=None): - cds = [] - for conditionElement in parentElement.findall(".condition"): - cd = {} - cdMin = conditionElement.attrib.get("minimum") - if cdMin is not None: - cd["minimum"] = float(cdMin) - else: - # will allow these to be None, assume axis.minimum - cd["minimum"] = None - cdMax = conditionElement.attrib.get("maximum") - if cdMax is not None: - cd["maximum"] = float(cdMax) - else: - # will allow these to be None, assume axis.maximum - cd["maximum"] = None - cd["name"] = conditionElement.attrib.get("name") - # # test for things - if cd.get("minimum") is None and cd.get("maximum") is None: - raise DesignSpaceDocumentError( - "condition missing required minimum or maximum in rule" - + (" '%s'" % ruleName if ruleName is not None else "") - ) - cds.append(cd) - return cds - - def readAxes(self): - # read the axes elements, including the warp map. - axesElement = self.root.find(".axes") - if axesElement is not None and "elidedfallbackname" in axesElement.attrib: - self.documentObject.elidedFallbackName = axesElement.attrib[ - "elidedfallbackname" - ] - axisElements = self.root.findall(".axes/axis") - if not axisElements: - return - for axisElement in axisElements: - if ( - self.documentObject.formatTuple >= (5, 0) - and "values" in axisElement.attrib - ): - axisObject = self.discreteAxisDescriptorClass() - axisObject.values = [ - float(s) for s in axisElement.attrib["values"].split(" ") - ] - else: - axisObject = self.axisDescriptorClass() - axisObject.minimum = float(axisElement.attrib.get("minimum")) - axisObject.maximum = float(axisElement.attrib.get("maximum")) - axisObject.default = float(axisElement.attrib.get("default")) - axisObject.name = axisElement.attrib.get("name") - if axisElement.attrib.get("hidden", False): - axisObject.hidden = True - axisObject.tag = axisElement.attrib.get("tag") - for mapElement in axisElement.findall("map"): - a = float(mapElement.attrib["input"]) - b = float(mapElement.attrib["output"]) - axisObject.map.append((a, b)) - for labelNameElement in axisElement.findall("labelname"): - # Note: elementtree reads the "xml:lang" attribute name as - # '{http://www.w3.org/XML/1998/namespace}lang' - for key, lang in labelNameElement.items(): - if key == XML_LANG: - axisObject.labelNames[lang] = tostr(labelNameElement.text) - labelElement = axisElement.find(".labels") - if labelElement is not None: - if "ordering" in labelElement.attrib: - axisObject.axisOrdering = int(labelElement.attrib["ordering"]) - for label in labelElement.findall(".label"): - axisObject.axisLabels.append(self.readAxisLabel(label)) - self.documentObject.axes.append(axisObject) - self.axisDefaults[axisObject.name] = axisObject.default - - self.documentObject.axisMappings = [] - for mappingsElement in self.root.findall(".axes/mappings"): - groupDescription = mappingsElement.attrib.get("description") - for mappingElement in mappingsElement.findall("mapping"): - description = mappingElement.attrib.get("description") - inputElement = mappingElement.find("input") - outputElement = mappingElement.find("output") - inputLoc = {} - outputLoc = {} - for dimElement in inputElement.findall(".dimension"): - name = dimElement.attrib["name"] - value = float(dimElement.attrib["xvalue"]) - inputLoc[name] = value - for dimElement in outputElement.findall(".dimension"): - name = dimElement.attrib["name"] - value = float(dimElement.attrib["xvalue"]) - outputLoc[name] = value - axisMappingObject = self.axisMappingDescriptorClass( - inputLocation=inputLoc, - outputLocation=outputLoc, - description=description, - groupDescription=groupDescription, - ) - self.documentObject.axisMappings.append(axisMappingObject) - - def readAxisLabel(self, element: ET.Element): - xml_attrs = { - "userminimum", - "uservalue", - "usermaximum", - "name", - "elidable", - "oldersibling", - "linkeduservalue", - } - unknown_attrs = set(element.attrib) - xml_attrs - if unknown_attrs: - raise DesignSpaceDocumentError( - f"label element contains unknown attributes: {', '.join(unknown_attrs)}" - ) - - name = element.get("name") - if name is None: - raise DesignSpaceDocumentError("label element must have a name attribute.") - valueStr = element.get("uservalue") - if valueStr is None: - raise DesignSpaceDocumentError( - "label element must have a uservalue attribute." - ) - value = float(valueStr) - minimumStr = element.get("userminimum") - minimum = float(minimumStr) if minimumStr is not None else None - maximumStr = element.get("usermaximum") - maximum = float(maximumStr) if maximumStr is not None else None - linkedValueStr = element.get("linkeduservalue") - linkedValue = float(linkedValueStr) if linkedValueStr is not None else None - elidable = True if element.get("elidable") == "true" else False - olderSibling = True if element.get("oldersibling") == "true" else False - labelNames = { - lang: label_name.text or "" - for label_name in element.findall("labelname") - for attr, lang in label_name.items() - if attr == XML_LANG - # Note: elementtree reads the "xml:lang" attribute name as - # '{http://www.w3.org/XML/1998/namespace}lang' - } - return self.axisLabelDescriptorClass( - name=name, - userValue=value, - userMinimum=minimum, - userMaximum=maximum, - elidable=elidable, - olderSibling=olderSibling, - linkedUserValue=linkedValue, - labelNames=labelNames, - ) - - def readLabels(self): - if self.documentObject.formatTuple < (5, 0): - return - - xml_attrs = {"name", "elidable", "oldersibling"} - for labelElement in self.root.findall(".labels/label"): - unknown_attrs = set(labelElement.attrib) - xml_attrs - if unknown_attrs: - raise DesignSpaceDocumentError( - f"Label element contains unknown attributes: {', '.join(unknown_attrs)}" - ) - - name = labelElement.get("name") - if name is None: - raise DesignSpaceDocumentError( - "label element must have a name attribute." - ) - designLocation, userLocation = self.locationFromElement(labelElement) - if designLocation: - raise DesignSpaceDocumentError( - f'

" % self.uuid)) - try: - self.comm = Comm('matplotlib', data={'id': self.uuid}) - except AttributeError as err: - raise RuntimeError('Unable to create an IPython notebook Comm ' - 'instance. Are you in the IPython ' - 'notebook?') from err - self.comm.on_msg(self.on_message) - - manager = self.manager - self._ext_close = False - - def _on_close(close_message): - self._ext_close = True - manager.remove_comm(close_message['content']['comm_id']) - manager.clearup_closed() - - self.comm.on_close(_on_close) - - def is_open(self): - return not (self._ext_close or self.comm._closed) - - def on_close(self): - # When the socket is closed, deregister the websocket with - # the FigureManager. - if self.is_open(): - try: - self.comm.close() - except KeyError: - # apparently already cleaned it up? - pass - - def send_json(self, content): - self.comm.send({'data': json.dumps(content)}) - - def send_binary(self, blob): - if self.supports_binary: - self.comm.send({'blob': 'image/png'}, buffers=[blob]) - else: - # The comm is ASCII, so we send the image in base64 encoded data - # URL form. - data = b64encode(blob).decode('ascii') - data_uri = "data:image/png;base64,{0}".format(data) - self.comm.send({'data': data_uri}) - - def on_message(self, message): - # The 'supports_binary' message is relevant to the - # websocket itself. The other messages get passed along - # to matplotlib as-is. - - # Every message has a "type" and a "figure_id". - message = json.loads(message['content']['data']) - if message['type'] == 'closing': - self.on_close() - self.manager.clearup_closed() - elif message['type'] == 'supports_binary': - self.supports_binary = message['value'] - else: - self.manager.handle_json(message) - - -@_Backend.export -class _BackendNbAgg(_Backend): - FigureCanvas = FigureCanvasNbAgg - FigureManager = FigureManagerNbAgg diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_pdf.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_pdf.py deleted file mode 100644 index 08a034f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_pdf.py +++ /dev/null @@ -1,2840 +0,0 @@ -""" -A PDF Matplotlib backend. - -Author: Jouni K Seppänen and others. -""" - -import codecs -from datetime import timezone -from datetime import datetime -from enum import Enum -from functools import total_ordering -from io import BytesIO -import itertools -import logging -import math -import os -import string -import struct -import sys -import time -import types -import warnings -import zlib - -import numpy as np -from PIL import Image - -import matplotlib as mpl -from matplotlib import _api, _text_helpers, _type1font, cbook, dviread -from matplotlib._pylab_helpers import Gcf -from matplotlib.backend_bases import ( - _Backend, FigureCanvasBase, FigureManagerBase, GraphicsContextBase, - RendererBase) -from matplotlib.backends.backend_mixed import MixedModeRenderer -from matplotlib.figure import Figure -from matplotlib.font_manager import get_font, fontManager as _fontManager -from matplotlib._afm import AFM -from matplotlib.ft2font import (FIXED_WIDTH, ITALIC, LOAD_NO_SCALE, - LOAD_NO_HINTING, KERNING_UNFITTED, FT2Font) -from matplotlib.transforms import Affine2D, BboxBase -from matplotlib.path import Path -from matplotlib.dates import UTC -from matplotlib import _path -from . import _backend_pdf_ps - -_log = logging.getLogger(__name__) - -# Overview -# -# The low-level knowledge about pdf syntax lies mainly in the pdfRepr -# function and the classes Reference, Name, Operator, and Stream. The -# PdfFile class knows about the overall structure of pdf documents. -# It provides a "write" method for writing arbitrary strings in the -# file, and an "output" method that passes objects through the pdfRepr -# function before writing them in the file. The output method is -# called by the RendererPdf class, which contains the various draw_foo -# methods. RendererPdf contains a GraphicsContextPdf instance, and -# each draw_foo calls self.check_gc before outputting commands. This -# method checks whether the pdf graphics state needs to be modified -# and outputs the necessary commands. GraphicsContextPdf represents -# the graphics state, and its "delta" method returns the commands that -# modify the state. - -# Add "pdf.use14corefonts: True" in your configuration file to use only -# the 14 PDF core fonts. These fonts do not need to be embedded; every -# PDF viewing application is required to have them. This results in very -# light PDF files you can use directly in LaTeX or ConTeXt documents -# generated with pdfTeX, without any conversion. - -# These fonts are: Helvetica, Helvetica-Bold, Helvetica-Oblique, -# Helvetica-BoldOblique, Courier, Courier-Bold, Courier-Oblique, -# Courier-BoldOblique, Times-Roman, Times-Bold, Times-Italic, -# Times-BoldItalic, Symbol, ZapfDingbats. -# -# Some tricky points: -# -# 1. The clip path can only be widened by popping from the state -# stack. Thus the state must be pushed onto the stack before narrowing -# the clip path. This is taken care of by GraphicsContextPdf. -# -# 2. Sometimes it is necessary to refer to something (e.g., font, -# image, or extended graphics state, which contains the alpha value) -# in the page stream by a name that needs to be defined outside the -# stream. PdfFile provides the methods fontName, imageObject, and -# alphaState for this purpose. The implementations of these methods -# should perhaps be generalized. - -# TODOs: -# -# * encoding of fonts, including mathtext fonts and Unicode support -# * TTF support has lots of small TODOs, e.g., how do you know if a font -# is serif/sans-serif, or symbolic/non-symbolic? -# * draw_quad_mesh - - -@_api.deprecated("3.6", alternative="a vendored copy of _fill") -def fill(strings, linelen=75): - return _fill(strings, linelen=linelen) - - -def _fill(strings, linelen=75): - """ - Make one string from sequence of strings, with whitespace in between. - - The whitespace is chosen to form lines of at most *linelen* characters, - if possible. - """ - currpos = 0 - lasti = 0 - result = [] - for i, s in enumerate(strings): - length = len(s) - if currpos + length < linelen: - currpos += length + 1 - else: - result.append(b' '.join(strings[lasti:i])) - lasti = i - currpos = length - result.append(b' '.join(strings[lasti:])) - return b'\n'.join(result) - - -def _create_pdf_info_dict(backend, metadata): - """ - Create a PDF infoDict based on user-supplied metadata. - - A default ``Creator``, ``Producer``, and ``CreationDate`` are added, though - the user metadata may override it. The date may be the current time, or a - time set by the ``SOURCE_DATE_EPOCH`` environment variable. - - Metadata is verified to have the correct keys and their expected types. Any - unknown keys/types will raise a warning. - - Parameters - ---------- - backend : str - The name of the backend to use in the Producer value. - - metadata : dict[str, Union[str, datetime, Name]] - A dictionary of metadata supplied by the user with information - following the PDF specification, also defined in - `~.backend_pdf.PdfPages` below. - - If any value is *None*, then the key will be removed. This can be used - to remove any pre-defined values. - - Returns - ------- - dict[str, Union[str, datetime, Name]] - A validated dictionary of metadata. - """ - - # get source date from SOURCE_DATE_EPOCH, if set - # See https://reproducible-builds.org/specs/source-date-epoch/ - source_date_epoch = os.getenv("SOURCE_DATE_EPOCH") - if source_date_epoch: - source_date = datetime.fromtimestamp(int(source_date_epoch), timezone.utc) - source_date = source_date.replace(tzinfo=UTC) - else: - source_date = datetime.today() - - info = { - 'Creator': f'Matplotlib v{mpl.__version__}, https://matplotlib.org', - 'Producer': f'Matplotlib {backend} backend v{mpl.__version__}', - 'CreationDate': source_date, - **metadata - } - info = {k: v for (k, v) in info.items() if v is not None} - - def is_string_like(x): - return isinstance(x, str) - is_string_like.text_for_warning = "an instance of str" - - def is_date(x): - return isinstance(x, datetime) - is_date.text_for_warning = "an instance of datetime.datetime" - - def check_trapped(x): - if isinstance(x, Name): - return x.name in (b'True', b'False', b'Unknown') - else: - return x in ('True', 'False', 'Unknown') - check_trapped.text_for_warning = 'one of {"True", "False", "Unknown"}' - - keywords = { - 'Title': is_string_like, - 'Author': is_string_like, - 'Subject': is_string_like, - 'Keywords': is_string_like, - 'Creator': is_string_like, - 'Producer': is_string_like, - 'CreationDate': is_date, - 'ModDate': is_date, - 'Trapped': check_trapped, - } - for k in info: - if k not in keywords: - _api.warn_external(f'Unknown infodict keyword: {k!r}. ' - f'Must be one of {set(keywords)!r}.') - elif not keywords[k](info[k]): - _api.warn_external(f'Bad value for infodict keyword {k}. ' - f'Got {info[k]!r} which is not ' - f'{keywords[k].text_for_warning}.') - if 'Trapped' in info: - info['Trapped'] = Name(info['Trapped']) - - return info - - -def _datetime_to_pdf(d): - """ - Convert a datetime to a PDF string representing it. - - Used for PDF and PGF. - """ - r = d.strftime('D:%Y%m%d%H%M%S') - z = d.utcoffset() - if z is not None: - z = z.seconds - else: - if time.daylight: - z = time.altzone - else: - z = time.timezone - if z == 0: - r += 'Z' - elif z < 0: - r += "+%02d'%02d'" % ((-z) // 3600, (-z) % 3600) - else: - r += "-%02d'%02d'" % (z // 3600, z % 3600) - return r - - -def _calculate_quad_point_coordinates(x, y, width, height, angle=0): - """ - Calculate the coordinates of rectangle when rotated by angle around x, y - """ - - angle = math.radians(-angle) - sin_angle = math.sin(angle) - cos_angle = math.cos(angle) - a = x + height * sin_angle - b = y + height * cos_angle - c = x + width * cos_angle + height * sin_angle - d = y - width * sin_angle + height * cos_angle - e = x + width * cos_angle - f = y - width * sin_angle - return ((x, y), (e, f), (c, d), (a, b)) - - -def _get_coordinates_of_block(x, y, width, height, angle=0): - """ - Get the coordinates of rotated rectangle and rectangle that covers the - rotated rectangle. - """ - - vertices = _calculate_quad_point_coordinates(x, y, width, - height, angle) - - # Find min and max values for rectangle - # adjust so that QuadPoints is inside Rect - # PDF docs says that QuadPoints should be ignored if any point lies - # outside Rect, but for Acrobat it is enough that QuadPoints is on the - # border of Rect. - - pad = 0.00001 if angle % 90 else 0 - min_x = min(v[0] for v in vertices) - pad - min_y = min(v[1] for v in vertices) - pad - max_x = max(v[0] for v in vertices) + pad - max_y = max(v[1] for v in vertices) + pad - return (tuple(itertools.chain.from_iterable(vertices)), - (min_x, min_y, max_x, max_y)) - - -def _get_link_annotation(gc, x, y, width, height, angle=0): - """ - Create a link annotation object for embedding URLs. - """ - quadpoints, rect = _get_coordinates_of_block(x, y, width, height, angle) - link_annotation = { - 'Type': Name('Annot'), - 'Subtype': Name('Link'), - 'Rect': rect, - 'Border': [0, 0, 0], - 'A': { - 'S': Name('URI'), - 'URI': gc.get_url(), - }, - } - if angle % 90: - # Add QuadPoints - link_annotation['QuadPoints'] = quadpoints - return link_annotation - - -# PDF strings are supposed to be able to include any eight-bit data, except -# that unbalanced parens and backslashes must be escaped by a backslash. -# However, sf bug #2708559 shows that the carriage return character may get -# read as a newline; these characters correspond to \gamma and \Omega in TeX's -# math font encoding. Escaping them fixes the bug. -_str_escapes = str.maketrans({ - '\\': '\\\\', '(': '\\(', ')': '\\)', '\n': '\\n', '\r': '\\r'}) - - -def pdfRepr(obj): - """Map Python objects to PDF syntax.""" - - # Some objects defined later have their own pdfRepr method. - if hasattr(obj, 'pdfRepr'): - return obj.pdfRepr() - - # Floats. PDF does not have exponential notation (1.0e-10) so we - # need to use %f with some precision. Perhaps the precision - # should adapt to the magnitude of the number? - elif isinstance(obj, (float, np.floating)): - if not np.isfinite(obj): - raise ValueError("Can only output finite numbers in PDF") - r = b"%.10f" % obj - return r.rstrip(b'0').rstrip(b'.') - - # Booleans. Needs to be tested before integers since - # isinstance(True, int) is true. - elif isinstance(obj, bool): - return [b'false', b'true'][obj] - - # Integers are written as such. - elif isinstance(obj, (int, np.integer)): - return b"%d" % obj - - # Non-ASCII Unicode strings are encoded in UTF-16BE with byte-order mark. - elif isinstance(obj, str): - return pdfRepr(obj.encode('ascii') if obj.isascii() - else codecs.BOM_UTF16_BE + obj.encode('UTF-16BE')) - - # Strings are written in parentheses, with backslashes and parens - # escaped. Actually balanced parens are allowed, but it is - # simpler to escape them all. TODO: cut long strings into lines; - # I believe there is some maximum line length in PDF. - # Despite the extra decode/encode, translate is faster than regex. - elif isinstance(obj, bytes): - return ( - b'(' + - obj.decode('latin-1').translate(_str_escapes).encode('latin-1') - + b')') - - # Dictionaries. The keys must be PDF names, so if we find strings - # there, we make Name objects from them. The values may be - # anything, so the caller must ensure that PDF names are - # represented as Name objects. - elif isinstance(obj, dict): - return _fill([ - b"<<", - *[Name(k).pdfRepr() + b" " + pdfRepr(v) for k, v in obj.items()], - b">>", - ]) - - # Lists. - elif isinstance(obj, (list, tuple)): - return _fill([b"[", *[pdfRepr(val) for val in obj], b"]"]) - - # The null keyword. - elif obj is None: - return b'null' - - # A date. - elif isinstance(obj, datetime): - return pdfRepr(_datetime_to_pdf(obj)) - - # A bounding box - elif isinstance(obj, BboxBase): - return _fill([pdfRepr(val) for val in obj.bounds]) - - else: - raise TypeError("Don't know a PDF representation for {} objects" - .format(type(obj))) - - -def _font_supports_glyph(fonttype, glyph): - """ - Returns True if the font is able to provide codepoint *glyph* in a PDF. - - For a Type 3 font, this method returns True only for single-byte - characters. For Type 42 fonts this method return True if the character is - from the Basic Multilingual Plane. - """ - if fonttype == 3: - return glyph <= 255 - if fonttype == 42: - return glyph <= 65535 - raise NotImplementedError() - - -class Reference: - """ - PDF reference object. - - Use PdfFile.reserveObject() to create References. - """ - - def __init__(self, id): - self.id = id - - def __repr__(self): - return "" % self.id - - def pdfRepr(self): - return b"%d 0 R" % self.id - - def write(self, contents, file): - write = file.write - write(b"%d 0 obj\n" % self.id) - write(pdfRepr(contents)) - write(b"\nendobj\n") - - -@total_ordering -class Name: - """PDF name object.""" - __slots__ = ('name',) - _hexify = {c: '#%02x' % c - for c in {*range(256)} - {*range(ord('!'), ord('~') + 1)}} - - def __init__(self, name): - if isinstance(name, Name): - self.name = name.name - else: - if isinstance(name, bytes): - name = name.decode('ascii') - self.name = name.translate(self._hexify).encode('ascii') - - def __repr__(self): - return "" % self.name - - def __str__(self): - return '/' + self.name.decode('ascii') - - def __eq__(self, other): - return isinstance(other, Name) and self.name == other.name - - def __lt__(self, other): - return isinstance(other, Name) and self.name < other.name - - def __hash__(self): - return hash(self.name) - - @staticmethod - @_api.deprecated("3.6") - def hexify(match): - return '#%02x' % ord(match.group()) - - def pdfRepr(self): - return b'/' + self.name - - -@_api.deprecated("3.6") -class Operator: - __slots__ = ('op',) - - def __init__(self, op): - self.op = op - - def __repr__(self): - return '' % self.op - - def pdfRepr(self): - return self.op - - -class Verbatim: - """Store verbatim PDF command content for later inclusion in the stream.""" - def __init__(self, x): - self._x = x - - def pdfRepr(self): - return self._x - - -class Op(Enum): - """PDF operators (not an exhaustive list).""" - - close_fill_stroke = b'b' - fill_stroke = b'B' - fill = b'f' - closepath = b'h' - close_stroke = b's' - stroke = b'S' - endpath = b'n' - begin_text = b'BT' - end_text = b'ET' - curveto = b'c' - rectangle = b're' - lineto = b'l' - moveto = b'm' - concat_matrix = b'cm' - use_xobject = b'Do' - setgray_stroke = b'G' - setgray_nonstroke = b'g' - setrgb_stroke = b'RG' - setrgb_nonstroke = b'rg' - setcolorspace_stroke = b'CS' - setcolorspace_nonstroke = b'cs' - setcolor_stroke = b'SCN' - setcolor_nonstroke = b'scn' - setdash = b'd' - setlinejoin = b'j' - setlinecap = b'J' - setgstate = b'gs' - gsave = b'q' - grestore = b'Q' - textpos = b'Td' - selectfont = b'Tf' - textmatrix = b'Tm' - show = b'Tj' - showkern = b'TJ' - setlinewidth = b'w' - clip = b'W' - shading = b'sh' - - op = _api.deprecated('3.6')(property(lambda self: self.value)) - - def pdfRepr(self): - return self.value - - @classmethod - def paint_path(cls, fill, stroke): - """ - Return the PDF operator to paint a path. - - Parameters - ---------- - fill : bool - Fill the path with the fill color. - stroke : bool - Stroke the outline of the path with the line color. - """ - if stroke: - if fill: - return cls.fill_stroke - else: - return cls.stroke - else: - if fill: - return cls.fill - else: - return cls.endpath - - -class Stream: - """ - PDF stream object. - - This has no pdfRepr method. Instead, call begin(), then output the - contents of the stream by calling write(), and finally call end(). - """ - __slots__ = ('id', 'len', 'pdfFile', 'file', 'compressobj', 'extra', 'pos') - - def __init__(self, id, len, file, extra=None, png=None): - """ - Parameters - ---------- - id : int - Object id of the stream. - len : Reference or None - An unused Reference object for the length of the stream; - None means to use a memory buffer so the length can be inlined. - file : PdfFile - The underlying object to write the stream to. - extra : dict from Name to anything, or None - Extra key-value pairs to include in the stream header. - png : dict or None - If the data is already png encoded, the decode parameters. - """ - self.id = id # object id - self.len = len # id of length object - self.pdfFile = file - self.file = file.fh # file to which the stream is written - self.compressobj = None # compression object - if extra is None: - self.extra = dict() - else: - self.extra = extra.copy() - if png is not None: - self.extra.update({'Filter': Name('FlateDecode'), - 'DecodeParms': png}) - - self.pdfFile.recordXref(self.id) - if mpl.rcParams['pdf.compression'] and not png: - self.compressobj = zlib.compressobj( - mpl.rcParams['pdf.compression']) - if self.len is None: - self.file = BytesIO() - else: - self._writeHeader() - self.pos = self.file.tell() - - def _writeHeader(self): - write = self.file.write - write(b"%d 0 obj\n" % self.id) - dict = self.extra - dict['Length'] = self.len - if mpl.rcParams['pdf.compression']: - dict['Filter'] = Name('FlateDecode') - - write(pdfRepr(dict)) - write(b"\nstream\n") - - def end(self): - """Finalize stream.""" - - self._flush() - if self.len is None: - contents = self.file.getvalue() - self.len = len(contents) - self.file = self.pdfFile.fh - self._writeHeader() - self.file.write(contents) - self.file.write(b"\nendstream\nendobj\n") - else: - length = self.file.tell() - self.pos - self.file.write(b"\nendstream\nendobj\n") - self.pdfFile.writeObject(self.len, length) - - def write(self, data): - """Write some data on the stream.""" - - if self.compressobj is None: - self.file.write(data) - else: - compressed = self.compressobj.compress(data) - self.file.write(compressed) - - def _flush(self): - """Flush the compression object.""" - - if self.compressobj is not None: - compressed = self.compressobj.flush() - self.file.write(compressed) - self.compressobj = None - - -def _get_pdf_charprocs(font_path, glyph_ids): - font = get_font(font_path, hinting_factor=1) - conv = 1000 / font.units_per_EM # Conversion to PS units (1/1000's). - procs = {} - for glyph_id in glyph_ids: - g = font.load_glyph(glyph_id, LOAD_NO_SCALE) - # NOTE: We should be using round(), but instead use - # "(x+.5).astype(int)" to keep backcompat with the old ttconv code - # (this is different for negative x's). - d1 = (np.array([g.horiAdvance, 0, *g.bbox]) * conv + .5).astype(int) - v, c = font.get_path() - v = (v * 64).astype(int) # Back to TrueType's internal units (1/64's). - # Backcompat with old ttconv code: control points between two quads are - # omitted if they are exactly at the midpoint between the control of - # the quad before and the quad after, but ttconv used to interpolate - # *after* conversion to PS units, causing floating point errors. Here - # we reproduce ttconv's logic, detecting these "implicit" points and - # re-interpolating them. Note that occasionally (e.g. with DejaVu Sans - # glyph "0") a point detected as "implicit" is actually explicit, and - # will thus be shifted by 1. - quads, = np.nonzero(c == 3) - quads_on = quads[1::2] - quads_mid_on = np.array( - sorted({*quads_on} & {*(quads - 1)} & {*(quads + 1)}), int) - implicit = quads_mid_on[ - (v[quads_mid_on] # As above, use astype(int), not // division - == ((v[quads_mid_on - 1] + v[quads_mid_on + 1]) / 2).astype(int)) - .all(axis=1)] - if (font.postscript_name, glyph_id) in [ - ("DejaVuSerif-Italic", 77), # j - ("DejaVuSerif-Italic", 135), # \AA - ]: - v[:, 0] -= 1 # Hard-coded backcompat (FreeType shifts glyph by 1). - v = (v * conv + .5).astype(int) # As above re: truncation vs rounding. - v[implicit] = (( # Fix implicit points; again, truncate. - (v[implicit - 1] + v[implicit + 1]) / 2).astype(int)) - procs[font.get_glyph_name(glyph_id)] = ( - " ".join(map(str, d1)).encode("ascii") + b" d1\n" - + _path.convert_to_string( - Path(v, c), None, None, False, None, -1, - # no code for quad Beziers triggers auto-conversion to cubics. - [b"m", b"l", b"", b"c", b"h"], True) - + b"f") - return procs - - -class PdfFile: - """PDF file object.""" - - def __init__(self, filename, metadata=None): - """ - Parameters - ---------- - filename : str or path-like or file-like - Output target; if a string, a file will be opened for writing. - - metadata : dict from strings to strings and dates - Information dictionary object (see PDF reference section 10.2.1 - 'Document Information Dictionary'), e.g.: - ``{'Creator': 'My software', 'Author': 'Me', 'Title': 'Awesome'}``. - - The standard keys are 'Title', 'Author', 'Subject', 'Keywords', - 'Creator', 'Producer', 'CreationDate', 'ModDate', and - 'Trapped'. Values have been predefined for 'Creator', 'Producer' - and 'CreationDate'. They can be removed by setting them to `None`. - """ - super().__init__() - - self._object_seq = itertools.count(1) # consumed by reserveObject - self.xrefTable = [[0, 65535, 'the zero object']] - self.passed_in_file_object = False - self.original_file_like = None - self.tell_base = 0 - fh, opened = cbook.to_filehandle(filename, "wb", return_opened=True) - if not opened: - try: - self.tell_base = filename.tell() - except IOError: - fh = BytesIO() - self.original_file_like = filename - else: - fh = filename - self.passed_in_file_object = True - - self.fh = fh - self.currentstream = None # stream object to write to, if any - fh.write(b"%PDF-1.4\n") # 1.4 is the first version to have alpha - # Output some eight-bit chars as a comment so various utilities - # recognize the file as binary by looking at the first few - # lines (see note in section 3.4.1 of the PDF reference). - fh.write(b"%\254\334 \253\272\n") - - self.rootObject = self.reserveObject('root') - self.pagesObject = self.reserveObject('pages') - self.pageList = [] - self.fontObject = self.reserveObject('fonts') - self._extGStateObject = self.reserveObject('extended graphics states') - self.hatchObject = self.reserveObject('tiling patterns') - self.gouraudObject = self.reserveObject('Gouraud triangles') - self.XObjectObject = self.reserveObject('external objects') - self.resourceObject = self.reserveObject('resources') - - root = {'Type': Name('Catalog'), - 'Pages': self.pagesObject} - self.writeObject(self.rootObject, root) - - self.infoDict = _create_pdf_info_dict('pdf', metadata or {}) - - self.fontNames = {} # maps filenames to internal font names - self._internal_font_seq = (Name(f'F{i}') for i in itertools.count(1)) - self.dviFontInfo = {} # maps dvi font names to embedding information - # differently encoded Type-1 fonts may share the same descriptor - self.type1Descriptors = {} - self._character_tracker = _backend_pdf_ps.CharacterTracker() - - self.alphaStates = {} # maps alpha values to graphics state objects - self._alpha_state_seq = (Name(f'A{i}') for i in itertools.count(1)) - self._soft_mask_states = {} - self._soft_mask_seq = (Name(f'SM{i}') for i in itertools.count(1)) - self._soft_mask_groups = [] - self.hatchPatterns = {} - self._hatch_pattern_seq = (Name(f'H{i}') for i in itertools.count(1)) - self.gouraudTriangles = [] - - self._images = {} - self._image_seq = (Name(f'I{i}') for i in itertools.count(1)) - - self.markers = {} - self.multi_byte_charprocs = {} - - self.paths = [] - - # A list of annotations for each page. Each entry is a tuple of the - # overall Annots object reference that's inserted into the page object, - # followed by a list of the actual annotations. - self._annotations = [] - # For annotations added before a page is created; mostly for the - # purpose of newTextnote. - self.pageAnnotations = [] - - # The PDF spec recommends to include every procset - procsets = [Name(x) for x in "PDF Text ImageB ImageC ImageI".split()] - - # Write resource dictionary. - # Possibly TODO: more general ExtGState (graphics state dictionaries) - # ColorSpace Pattern Shading Properties - resources = {'Font': self.fontObject, - 'XObject': self.XObjectObject, - 'ExtGState': self._extGStateObject, - 'Pattern': self.hatchObject, - 'Shading': self.gouraudObject, - 'ProcSet': procsets} - self.writeObject(self.resourceObject, resources) - - def newPage(self, width, height): - self.endStream() - - self.width, self.height = width, height - contentObject = self.reserveObject('page contents') - annotsObject = self.reserveObject('annotations') - thePage = {'Type': Name('Page'), - 'Parent': self.pagesObject, - 'Resources': self.resourceObject, - 'MediaBox': [0, 0, 72 * width, 72 * height], - 'Contents': contentObject, - 'Annots': annotsObject, - } - pageObject = self.reserveObject('page') - self.writeObject(pageObject, thePage) - self.pageList.append(pageObject) - self._annotations.append((annotsObject, self.pageAnnotations)) - - self.beginStream(contentObject.id, - self.reserveObject('length of content stream')) - # Initialize the pdf graphics state to match the default Matplotlib - # graphics context (colorspace and joinstyle). - self.output(Name('DeviceRGB'), Op.setcolorspace_stroke) - self.output(Name('DeviceRGB'), Op.setcolorspace_nonstroke) - self.output(GraphicsContextPdf.joinstyles['round'], Op.setlinejoin) - - # Clear the list of annotations for the next page - self.pageAnnotations = [] - - def newTextnote(self, text, positionRect=[-100, -100, 0, 0]): - # Create a new annotation of type text - theNote = {'Type': Name('Annot'), - 'Subtype': Name('Text'), - 'Contents': text, - 'Rect': positionRect, - } - self.pageAnnotations.append(theNote) - - def _get_subsetted_psname(self, ps_name, charmap): - def toStr(n, base): - if n < base: - return string.ascii_uppercase[n] - else: - return ( - toStr(n // base, base) + string.ascii_uppercase[n % base] - ) - - # encode to string using base 26 - hashed = hash(frozenset(charmap.keys())) % ((sys.maxsize + 1) * 2) - prefix = toStr(hashed, 26) - - # get first 6 characters from prefix - return prefix[:6] + "+" + ps_name - - def finalize(self): - """Write out the various deferred objects and the pdf end matter.""" - - self.endStream() - self._write_annotations() - self.writeFonts() - self.writeExtGSTates() - self._write_soft_mask_groups() - self.writeHatches() - self.writeGouraudTriangles() - xobjects = { - name: ob for image, name, ob in self._images.values()} - for tup in self.markers.values(): - xobjects[tup[0]] = tup[1] - for name, value in self.multi_byte_charprocs.items(): - xobjects[name] = value - for name, path, trans, ob, join, cap, padding, filled, stroked \ - in self.paths: - xobjects[name] = ob - self.writeObject(self.XObjectObject, xobjects) - self.writeImages() - self.writeMarkers() - self.writePathCollectionTemplates() - self.writeObject(self.pagesObject, - {'Type': Name('Pages'), - 'Kids': self.pageList, - 'Count': len(self.pageList)}) - self.writeInfoDict() - - # Finalize the file - self.writeXref() - self.writeTrailer() - - def close(self): - """Flush all buffers and free all resources.""" - - self.endStream() - if self.passed_in_file_object: - self.fh.flush() - else: - if self.original_file_like is not None: - self.original_file_like.write(self.fh.getvalue()) - self.fh.close() - - def write(self, data): - if self.currentstream is None: - self.fh.write(data) - else: - self.currentstream.write(data) - - def output(self, *data): - self.write(_fill([pdfRepr(x) for x in data])) - self.write(b'\n') - - def beginStream(self, id, len, extra=None, png=None): - assert self.currentstream is None - self.currentstream = Stream(id, len, self, extra, png) - - def endStream(self): - if self.currentstream is not None: - self.currentstream.end() - self.currentstream = None - - def outputStream(self, ref, data, *, extra=None): - self.beginStream(ref.id, None, extra) - self.currentstream.write(data) - self.endStream() - - def _write_annotations(self): - for annotsObject, annotations in self._annotations: - self.writeObject(annotsObject, annotations) - - def fontName(self, fontprop): - """ - Select a font based on fontprop and return a name suitable for - Op.selectfont. If fontprop is a string, it will be interpreted - as the filename of the font. - """ - - if isinstance(fontprop, str): - filenames = [fontprop] - elif mpl.rcParams['pdf.use14corefonts']: - filenames = _fontManager._find_fonts_by_props( - fontprop, fontext='afm', directory=RendererPdf._afm_font_dir - ) - else: - filenames = _fontManager._find_fonts_by_props(fontprop) - first_Fx = None - for fname in filenames: - Fx = self.fontNames.get(fname) - if not first_Fx: - first_Fx = Fx - if Fx is None: - Fx = next(self._internal_font_seq) - self.fontNames[fname] = Fx - _log.debug('Assigning font %s = %r', Fx, fname) - if not first_Fx: - first_Fx = Fx - - # find_fontsprop's first value always adheres to - # findfont's value, so technically no behaviour change - return first_Fx - - def dviFontName(self, dvifont): - """ - Given a dvi font object, return a name suitable for Op.selectfont. - This registers the font information in ``self.dviFontInfo`` if not yet - registered. - """ - - dvi_info = self.dviFontInfo.get(dvifont.texname) - if dvi_info is not None: - return dvi_info.pdfname - - tex_font_map = dviread.PsfontsMap(dviread._find_tex_file('pdftex.map')) - psfont = tex_font_map[dvifont.texname] - if psfont.filename is None: - raise ValueError( - "No usable font file found for {} (TeX: {}); " - "the font may lack a Type-1 version" - .format(psfont.psname, dvifont.texname)) - - pdfname = next(self._internal_font_seq) - _log.debug('Assigning font %s = %s (dvi)', pdfname, dvifont.texname) - self.dviFontInfo[dvifont.texname] = types.SimpleNamespace( - dvifont=dvifont, - pdfname=pdfname, - fontfile=psfont.filename, - basefont=psfont.psname, - encodingfile=psfont.encoding, - effects=psfont.effects) - return pdfname - - def writeFonts(self): - fonts = {} - for dviname, info in sorted(self.dviFontInfo.items()): - Fx = info.pdfname - _log.debug('Embedding Type-1 font %s from dvi.', dviname) - fonts[Fx] = self._embedTeXFont(info) - for filename in sorted(self.fontNames): - Fx = self.fontNames[filename] - _log.debug('Embedding font %s.', filename) - if filename.endswith('.afm'): - # from pdf.use14corefonts - _log.debug('Writing AFM font.') - fonts[Fx] = self._write_afm_font(filename) - else: - # a normal TrueType font - _log.debug('Writing TrueType font.') - chars = self._character_tracker.used.get(filename) - if chars: - fonts[Fx] = self.embedTTF(filename, chars) - self.writeObject(self.fontObject, fonts) - - def _write_afm_font(self, filename): - with open(filename, 'rb') as fh: - font = AFM(fh) - fontname = font.get_fontname() - fontdict = {'Type': Name('Font'), - 'Subtype': Name('Type1'), - 'BaseFont': Name(fontname), - 'Encoding': Name('WinAnsiEncoding')} - fontdictObject = self.reserveObject('font dictionary') - self.writeObject(fontdictObject, fontdict) - return fontdictObject - - def _embedTeXFont(self, fontinfo): - _log.debug('Embedding TeX font %s - fontinfo=%s', - fontinfo.dvifont.texname, fontinfo.__dict__) - - # Widths - widthsObject = self.reserveObject('font widths') - self.writeObject(widthsObject, fontinfo.dvifont.widths) - - # Font dictionary - fontdictObject = self.reserveObject('font dictionary') - fontdict = { - 'Type': Name('Font'), - 'Subtype': Name('Type1'), - 'FirstChar': 0, - 'LastChar': len(fontinfo.dvifont.widths) - 1, - 'Widths': widthsObject, - } - - # Encoding (if needed) - if fontinfo.encodingfile is not None: - fontdict['Encoding'] = { - 'Type': Name('Encoding'), - 'Differences': [ - 0, *map(Name, dviread._parse_enc(fontinfo.encodingfile))], - } - - # If no file is specified, stop short - if fontinfo.fontfile is None: - _log.warning( - "Because of TeX configuration (pdftex.map, see updmap option " - "pdftexDownloadBase14) the font %s is not embedded. This is " - "deprecated as of PDF 1.5 and it may cause the consumer " - "application to show something that was not intended.", - fontinfo.basefont) - fontdict['BaseFont'] = Name(fontinfo.basefont) - self.writeObject(fontdictObject, fontdict) - return fontdictObject - - # We have a font file to embed - read it in and apply any effects - t1font = _type1font.Type1Font(fontinfo.fontfile) - if fontinfo.effects: - t1font = t1font.transform(fontinfo.effects) - fontdict['BaseFont'] = Name(t1font.prop['FontName']) - - # Font descriptors may be shared between differently encoded - # Type-1 fonts, so only create a new descriptor if there is no - # existing descriptor for this font. - effects = (fontinfo.effects.get('slant', 0.0), - fontinfo.effects.get('extend', 1.0)) - fontdesc = self.type1Descriptors.get((fontinfo.fontfile, effects)) - if fontdesc is None: - fontdesc = self.createType1Descriptor(t1font, fontinfo.fontfile) - self.type1Descriptors[(fontinfo.fontfile, effects)] = fontdesc - fontdict['FontDescriptor'] = fontdesc - - self.writeObject(fontdictObject, fontdict) - return fontdictObject - - def createType1Descriptor(self, t1font, fontfile): - # Create and write the font descriptor and the font file - # of a Type-1 font - fontdescObject = self.reserveObject('font descriptor') - fontfileObject = self.reserveObject('font file') - - italic_angle = t1font.prop['ItalicAngle'] - fixed_pitch = t1font.prop['isFixedPitch'] - - flags = 0 - # fixed width - if fixed_pitch: - flags |= 1 << 0 - # TODO: serif - if 0: - flags |= 1 << 1 - # TODO: symbolic (most TeX fonts are) - if 1: - flags |= 1 << 2 - # non-symbolic - else: - flags |= 1 << 5 - # italic - if italic_angle: - flags |= 1 << 6 - # TODO: all caps - if 0: - flags |= 1 << 16 - # TODO: small caps - if 0: - flags |= 1 << 17 - # TODO: force bold - if 0: - flags |= 1 << 18 - - ft2font = get_font(fontfile) - - descriptor = { - 'Type': Name('FontDescriptor'), - 'FontName': Name(t1font.prop['FontName']), - 'Flags': flags, - 'FontBBox': ft2font.bbox, - 'ItalicAngle': italic_angle, - 'Ascent': ft2font.ascender, - 'Descent': ft2font.descender, - 'CapHeight': 1000, # TODO: find this out - 'XHeight': 500, # TODO: this one too - 'FontFile': fontfileObject, - 'FontFamily': t1font.prop['FamilyName'], - 'StemV': 50, # TODO - # (see also revision 3874; but not all TeX distros have AFM files!) - # 'FontWeight': a number where 400 = Regular, 700 = Bold - } - - self.writeObject(fontdescObject, descriptor) - - self.outputStream(fontfileObject, b"".join(t1font.parts[:2]), - extra={'Length1': len(t1font.parts[0]), - 'Length2': len(t1font.parts[1]), - 'Length3': 0}) - - return fontdescObject - - def _get_xobject_glyph_name(self, filename, glyph_name): - Fx = self.fontName(filename) - return "-".join([ - Fx.name.decode(), - os.path.splitext(os.path.basename(filename))[0], - glyph_name]) - - _identityToUnicodeCMap = b"""/CIDInit /ProcSet findresource begin -12 dict begin -begincmap -/CIDSystemInfo -<< /Registry (Adobe) - /Ordering (UCS) - /Supplement 0 ->> def -/CMapName /Adobe-Identity-UCS def -/CMapType 2 def -1 begincodespacerange -<0000> -endcodespacerange -%d beginbfrange -%s -endbfrange -endcmap -CMapName currentdict /CMap defineresource pop -end -end""" - - def embedTTF(self, filename, characters): - """Embed the TTF font from the named file into the document.""" - - font = get_font(filename) - fonttype = mpl.rcParams['pdf.fonttype'] - - def cvt(length, upe=font.units_per_EM, nearest=True): - """Convert font coordinates to PDF glyph coordinates.""" - value = length / upe * 1000 - if nearest: - return round(value) - # Best(?) to round away from zero for bounding boxes and the like. - if value < 0: - return math.floor(value) - else: - return math.ceil(value) - - def embedTTFType3(font, characters, descriptor): - """The Type 3-specific part of embedding a Truetype font""" - widthsObject = self.reserveObject('font widths') - fontdescObject = self.reserveObject('font descriptor') - fontdictObject = self.reserveObject('font dictionary') - charprocsObject = self.reserveObject('character procs') - differencesArray = [] - firstchar, lastchar = 0, 255 - bbox = [cvt(x, nearest=False) for x in font.bbox] - - fontdict = { - 'Type': Name('Font'), - 'BaseFont': ps_name, - 'FirstChar': firstchar, - 'LastChar': lastchar, - 'FontDescriptor': fontdescObject, - 'Subtype': Name('Type3'), - 'Name': descriptor['FontName'], - 'FontBBox': bbox, - 'FontMatrix': [.001, 0, 0, .001, 0, 0], - 'CharProcs': charprocsObject, - 'Encoding': { - 'Type': Name('Encoding'), - 'Differences': differencesArray}, - 'Widths': widthsObject - } - - from encodings import cp1252 - - # Make the "Widths" array - def get_char_width(charcode): - s = ord(cp1252.decoding_table[charcode]) - width = font.load_char( - s, flags=LOAD_NO_SCALE | LOAD_NO_HINTING).horiAdvance - return cvt(width) - with warnings.catch_warnings(): - # Ignore 'Required glyph missing from current font' warning - # from ft2font: here we're just building the widths table, but - # the missing glyphs may not even be used in the actual string. - warnings.filterwarnings("ignore") - widths = [get_char_width(charcode) - for charcode in range(firstchar, lastchar+1)] - descriptor['MaxWidth'] = max(widths) - - # Make the "Differences" array, sort the ccodes < 255 from - # the multi-byte ccodes, and build the whole set of glyph ids - # that we need from this font. - glyph_ids = [] - differences = [] - multi_byte_chars = set() - for c in characters: - ccode = c - gind = font.get_char_index(ccode) - glyph_ids.append(gind) - glyph_name = font.get_glyph_name(gind) - if ccode <= 255: - differences.append((ccode, glyph_name)) - else: - multi_byte_chars.add(glyph_name) - differences.sort() - - last_c = -2 - for c, name in differences: - if c != last_c + 1: - differencesArray.append(c) - differencesArray.append(Name(name)) - last_c = c - - # Make the charprocs array. - rawcharprocs = _get_pdf_charprocs(filename, glyph_ids) - charprocs = {} - for charname in sorted(rawcharprocs): - stream = rawcharprocs[charname] - charprocDict = {} - # The 2-byte characters are used as XObjects, so they - # need extra info in their dictionary - if charname in multi_byte_chars: - charprocDict = {'Type': Name('XObject'), - 'Subtype': Name('Form'), - 'BBox': bbox} - # Each glyph includes bounding box information, - # but xpdf and ghostscript can't handle it in a - # Form XObject (they segfault!!!), so we remove it - # from the stream here. It's not needed anyway, - # since the Form XObject includes it in its BBox - # value. - stream = stream[stream.find(b"d1") + 2:] - charprocObject = self.reserveObject('charProc') - self.outputStream(charprocObject, stream, extra=charprocDict) - - # Send the glyphs with ccode > 255 to the XObject dictionary, - # and the others to the font itself - if charname in multi_byte_chars: - name = self._get_xobject_glyph_name(filename, charname) - self.multi_byte_charprocs[name] = charprocObject - else: - charprocs[charname] = charprocObject - - # Write everything out - self.writeObject(fontdictObject, fontdict) - self.writeObject(fontdescObject, descriptor) - self.writeObject(widthsObject, widths) - self.writeObject(charprocsObject, charprocs) - - return fontdictObject - - def embedTTFType42(font, characters, descriptor): - """The Type 42-specific part of embedding a Truetype font""" - fontdescObject = self.reserveObject('font descriptor') - cidFontDictObject = self.reserveObject('CID font dictionary') - type0FontDictObject = self.reserveObject('Type 0 font dictionary') - cidToGidMapObject = self.reserveObject('CIDToGIDMap stream') - fontfileObject = self.reserveObject('font file stream') - wObject = self.reserveObject('Type 0 widths') - toUnicodeMapObject = self.reserveObject('ToUnicode map') - - subset_str = "".join(chr(c) for c in characters) - _log.debug("SUBSET %s characters: %s", filename, subset_str) - fontdata = _backend_pdf_ps.get_glyphs_subset(filename, subset_str) - _log.debug( - "SUBSET %s %d -> %d", filename, - os.stat(filename).st_size, fontdata.getbuffer().nbytes - ) - - # We need this ref for XObjects - full_font = font - - # reload the font object from the subset - # (all the necessary data could probably be obtained directly - # using fontLib.ttLib) - font = FT2Font(fontdata) - - cidFontDict = { - 'Type': Name('Font'), - 'Subtype': Name('CIDFontType2'), - 'BaseFont': ps_name, - 'CIDSystemInfo': { - 'Registry': 'Adobe', - 'Ordering': 'Identity', - 'Supplement': 0}, - 'FontDescriptor': fontdescObject, - 'W': wObject, - 'CIDToGIDMap': cidToGidMapObject - } - - type0FontDict = { - 'Type': Name('Font'), - 'Subtype': Name('Type0'), - 'BaseFont': ps_name, - 'Encoding': Name('Identity-H'), - 'DescendantFonts': [cidFontDictObject], - 'ToUnicode': toUnicodeMapObject - } - - # Make fontfile stream - descriptor['FontFile2'] = fontfileObject - self.outputStream( - fontfileObject, fontdata.getvalue(), - extra={'Length1': fontdata.getbuffer().nbytes}) - - # Make the 'W' (Widths) array, CidToGidMap and ToUnicode CMap - # at the same time - cid_to_gid_map = ['\0'] * 65536 - widths = [] - max_ccode = 0 - for c in characters: - ccode = c - gind = font.get_char_index(ccode) - glyph = font.load_char(ccode, - flags=LOAD_NO_SCALE | LOAD_NO_HINTING) - widths.append((ccode, cvt(glyph.horiAdvance))) - if ccode < 65536: - cid_to_gid_map[ccode] = chr(gind) - max_ccode = max(ccode, max_ccode) - widths.sort() - cid_to_gid_map = cid_to_gid_map[:max_ccode + 1] - - last_ccode = -2 - w = [] - max_width = 0 - unicode_groups = [] - for ccode, width in widths: - if ccode != last_ccode + 1: - w.append(ccode) - w.append([width]) - unicode_groups.append([ccode, ccode]) - else: - w[-1].append(width) - unicode_groups[-1][1] = ccode - max_width = max(max_width, width) - last_ccode = ccode - - unicode_bfrange = [] - for start, end in unicode_groups: - # Ensure the CID map contains only chars from BMP - if start > 65535: - continue - end = min(65535, end) - - unicode_bfrange.append( - b"<%04x> <%04x> [%s]" % - (start, end, - b" ".join(b"<%04x>" % x for x in range(start, end+1)))) - unicode_cmap = (self._identityToUnicodeCMap % - (len(unicode_groups), b"\n".join(unicode_bfrange))) - - # Add XObjects for unsupported chars - glyph_ids = [] - for ccode in characters: - if not _font_supports_glyph(fonttype, ccode): - gind = full_font.get_char_index(ccode) - glyph_ids.append(gind) - - bbox = [cvt(x, nearest=False) for x in full_font.bbox] - rawcharprocs = _get_pdf_charprocs(filename, glyph_ids) - for charname in sorted(rawcharprocs): - stream = rawcharprocs[charname] - charprocDict = {'Type': Name('XObject'), - 'Subtype': Name('Form'), - 'BBox': bbox} - # Each glyph includes bounding box information, - # but xpdf and ghostscript can't handle it in a - # Form XObject (they segfault!!!), so we remove it - # from the stream here. It's not needed anyway, - # since the Form XObject includes it in its BBox - # value. - stream = stream[stream.find(b"d1") + 2:] - charprocObject = self.reserveObject('charProc') - self.outputStream(charprocObject, stream, extra=charprocDict) - - name = self._get_xobject_glyph_name(filename, charname) - self.multi_byte_charprocs[name] = charprocObject - - # CIDToGIDMap stream - cid_to_gid_map = "".join(cid_to_gid_map).encode("utf-16be") - self.outputStream(cidToGidMapObject, cid_to_gid_map) - - # ToUnicode CMap - self.outputStream(toUnicodeMapObject, unicode_cmap) - - descriptor['MaxWidth'] = max_width - - # Write everything out - self.writeObject(cidFontDictObject, cidFontDict) - self.writeObject(type0FontDictObject, type0FontDict) - self.writeObject(fontdescObject, descriptor) - self.writeObject(wObject, w) - - return type0FontDictObject - - # Beginning of main embedTTF function... - - ps_name = self._get_subsetted_psname( - font.postscript_name, - font.get_charmap() - ) - ps_name = ps_name.encode('ascii', 'replace') - ps_name = Name(ps_name) - pclt = font.get_sfnt_table('pclt') or {'capHeight': 0, 'xHeight': 0} - post = font.get_sfnt_table('post') or {'italicAngle': (0, 0)} - ff = font.face_flags - sf = font.style_flags - - flags = 0 - symbolic = False # ps_name.name in ('Cmsy10', 'Cmmi10', 'Cmex10') - if ff & FIXED_WIDTH: - flags |= 1 << 0 - if 0: # TODO: serif - flags |= 1 << 1 - if symbolic: - flags |= 1 << 2 - else: - flags |= 1 << 5 - if sf & ITALIC: - flags |= 1 << 6 - if 0: # TODO: all caps - flags |= 1 << 16 - if 0: # TODO: small caps - flags |= 1 << 17 - if 0: # TODO: force bold - flags |= 1 << 18 - - descriptor = { - 'Type': Name('FontDescriptor'), - 'FontName': ps_name, - 'Flags': flags, - 'FontBBox': [cvt(x, nearest=False) for x in font.bbox], - 'Ascent': cvt(font.ascender, nearest=False), - 'Descent': cvt(font.descender, nearest=False), - 'CapHeight': cvt(pclt['capHeight'], nearest=False), - 'XHeight': cvt(pclt['xHeight']), - 'ItalicAngle': post['italicAngle'][1], # ??? - 'StemV': 0 # ??? - } - - if fonttype == 3: - return embedTTFType3(font, characters, descriptor) - elif fonttype == 42: - return embedTTFType42(font, characters, descriptor) - - def alphaState(self, alpha): - """Return name of an ExtGState that sets alpha to the given value.""" - - state = self.alphaStates.get(alpha, None) - if state is not None: - return state[0] - - name = next(self._alpha_state_seq) - self.alphaStates[alpha] = \ - (name, {'Type': Name('ExtGState'), - 'CA': alpha[0], 'ca': alpha[1]}) - return name - - def _soft_mask_state(self, smask): - """ - Return an ExtGState that sets the soft mask to the given shading. - - Parameters - ---------- - smask : Reference - Reference to a shading in DeviceGray color space, whose luminosity - is to be used as the alpha channel. - - Returns - ------- - Name - """ - - state = self._soft_mask_states.get(smask, None) - if state is not None: - return state[0] - - name = next(self._soft_mask_seq) - groupOb = self.reserveObject('transparency group for soft mask') - self._soft_mask_states[smask] = ( - name, - { - 'Type': Name('ExtGState'), - 'AIS': False, - 'SMask': { - 'Type': Name('Mask'), - 'S': Name('Luminosity'), - 'BC': [1], - 'G': groupOb - } - } - ) - self._soft_mask_groups.append(( - groupOb, - { - 'Type': Name('XObject'), - 'Subtype': Name('Form'), - 'FormType': 1, - 'Group': { - 'S': Name('Transparency'), - 'CS': Name('DeviceGray') - }, - 'Matrix': [1, 0, 0, 1, 0, 0], - 'Resources': {'Shading': {'S': smask}}, - 'BBox': [0, 0, 1, 1] - }, - [Name('S'), Op.shading] - )) - return name - - def writeExtGSTates(self): - self.writeObject( - self._extGStateObject, - dict([ - *self.alphaStates.values(), - *self._soft_mask_states.values() - ]) - ) - - def _write_soft_mask_groups(self): - for ob, attributes, content in self._soft_mask_groups: - self.beginStream(ob.id, None, attributes) - self.output(*content) - self.endStream() - - def hatchPattern(self, hatch_style): - # The colors may come in as numpy arrays, which aren't hashable - if hatch_style is not None: - edge, face, hatch = hatch_style - if edge is not None: - edge = tuple(edge) - if face is not None: - face = tuple(face) - hatch_style = (edge, face, hatch) - - pattern = self.hatchPatterns.get(hatch_style, None) - if pattern is not None: - return pattern - - name = next(self._hatch_pattern_seq) - self.hatchPatterns[hatch_style] = name - return name - - def writeHatches(self): - hatchDict = dict() - sidelen = 72.0 - for hatch_style, name in self.hatchPatterns.items(): - ob = self.reserveObject('hatch pattern') - hatchDict[name] = ob - res = {'Procsets': - [Name(x) for x in "PDF Text ImageB ImageC ImageI".split()]} - self.beginStream( - ob.id, None, - {'Type': Name('Pattern'), - 'PatternType': 1, 'PaintType': 1, 'TilingType': 1, - 'BBox': [0, 0, sidelen, sidelen], - 'XStep': sidelen, 'YStep': sidelen, - 'Resources': res, - # Change origin to match Agg at top-left. - 'Matrix': [1, 0, 0, 1, 0, self.height * 72]}) - - stroke_rgb, fill_rgb, hatch = hatch_style - self.output(stroke_rgb[0], stroke_rgb[1], stroke_rgb[2], - Op.setrgb_stroke) - if fill_rgb is not None: - self.output(fill_rgb[0], fill_rgb[1], fill_rgb[2], - Op.setrgb_nonstroke, - 0, 0, sidelen, sidelen, Op.rectangle, - Op.fill) - - self.output(mpl.rcParams['hatch.linewidth'], Op.setlinewidth) - - self.output(*self.pathOperations( - Path.hatch(hatch), - Affine2D().scale(sidelen), - simplify=False)) - self.output(Op.fill_stroke) - - self.endStream() - self.writeObject(self.hatchObject, hatchDict) - - def addGouraudTriangles(self, points, colors): - """ - Add a Gouraud triangle shading. - - Parameters - ---------- - points : np.ndarray - Triangle vertices, shape (n, 3, 2) - where n = number of triangles, 3 = vertices, 2 = x, y. - colors : np.ndarray - Vertex colors, shape (n, 3, 1) or (n, 3, 4) - as with points, but last dimension is either (gray,) - or (r, g, b, alpha). - - Returns - ------- - Name, Reference - """ - name = Name('GT%d' % len(self.gouraudTriangles)) - ob = self.reserveObject(f'Gouraud triangle {name}') - self.gouraudTriangles.append((name, ob, points, colors)) - return name, ob - - def writeGouraudTriangles(self): - gouraudDict = dict() - for name, ob, points, colors in self.gouraudTriangles: - gouraudDict[name] = ob - shape = points.shape - flat_points = points.reshape((shape[0] * shape[1], 2)) - colordim = colors.shape[2] - assert colordim in (1, 4) - flat_colors = colors.reshape((shape[0] * shape[1], colordim)) - if colordim == 4: - # strip the alpha channel - colordim = 3 - points_min = np.min(flat_points, axis=0) - (1 << 8) - points_max = np.max(flat_points, axis=0) + (1 << 8) - factor = 0xffffffff / (points_max - points_min) - - self.beginStream( - ob.id, None, - {'ShadingType': 4, - 'BitsPerCoordinate': 32, - 'BitsPerComponent': 8, - 'BitsPerFlag': 8, - 'ColorSpace': Name( - 'DeviceRGB' if colordim == 3 else 'DeviceGray' - ), - 'AntiAlias': False, - 'Decode': ([points_min[0], points_max[0], - points_min[1], points_max[1]] - + [0, 1] * colordim), - }) - - streamarr = np.empty( - (shape[0] * shape[1],), - dtype=[('flags', 'u1'), - ('points', '>u4', (2,)), - ('colors', 'u1', (colordim,))]) - streamarr['flags'] = 0 - streamarr['points'] = (flat_points - points_min) * factor - streamarr['colors'] = flat_colors[:, :colordim] * 255.0 - - self.write(streamarr.tobytes()) - self.endStream() - self.writeObject(self.gouraudObject, gouraudDict) - - def imageObject(self, image): - """Return name of an image XObject representing the given image.""" - - entry = self._images.get(id(image), None) - if entry is not None: - return entry[1] - - name = next(self._image_seq) - ob = self.reserveObject(f'image {name}') - self._images[id(image)] = (image, name, ob) - return name - - def _unpack(self, im): - """ - Unpack image array *im* into ``(data, alpha)``, which have shape - ``(height, width, 3)`` (RGB) or ``(height, width, 1)`` (grayscale or - alpha), except that alpha is None if the image is fully opaque. - """ - im = im[::-1] - if im.ndim == 2: - return im, None - else: - rgb = im[:, :, :3] - rgb = np.array(rgb, order='C') - # PDF needs a separate alpha image - if im.shape[2] == 4: - alpha = im[:, :, 3][..., None] - if np.all(alpha == 255): - alpha = None - else: - alpha = np.array(alpha, order='C') - else: - alpha = None - return rgb, alpha - - def _writePng(self, img): - """ - Write the image *img* into the pdf file using png - predictors with Flate compression. - """ - buffer = BytesIO() - img.save(buffer, format="png") - buffer.seek(8) - png_data = b'' - bit_depth = palette = None - while True: - length, type = struct.unpack(b'!L4s', buffer.read(8)) - if type in [b'IHDR', b'PLTE', b'IDAT']: - data = buffer.read(length) - if len(data) != length: - raise RuntimeError("truncated data") - if type == b'IHDR': - bit_depth = int(data[8]) - elif type == b'PLTE': - palette = data - elif type == b'IDAT': - png_data += data - elif type == b'IEND': - break - else: - buffer.seek(length, 1) - buffer.seek(4, 1) # skip CRC - return png_data, bit_depth, palette - - def _writeImg(self, data, id, smask=None): - """ - Write the image *data*, of shape ``(height, width, 1)`` (grayscale) or - ``(height, width, 3)`` (RGB), as pdf object *id* and with the soft mask - (alpha channel) *smask*, which should be either None or a ``(height, - width, 1)`` array. - """ - height, width, color_channels = data.shape - obj = {'Type': Name('XObject'), - 'Subtype': Name('Image'), - 'Width': width, - 'Height': height, - 'ColorSpace': Name({1: 'DeviceGray', 3: 'DeviceRGB'}[color_channels]), - 'BitsPerComponent': 8} - if smask: - obj['SMask'] = smask - if mpl.rcParams['pdf.compression']: - if data.shape[-1] == 1: - data = data.squeeze(axis=-1) - png = {'Predictor': 10, 'Colors': color_channels, 'Columns': width} - img = Image.fromarray(data) - img_colors = img.getcolors(maxcolors=256) - if color_channels == 3 and img_colors is not None: - # Convert to indexed color if there are 256 colors or fewer. This can - # significantly reduce the file size. - num_colors = len(img_colors) - palette = np.array([comp for _, color in img_colors for comp in color], - dtype=np.uint8) - palette24 = ((palette[0::3].astype(np.uint32) << 16) | - (palette[1::3].astype(np.uint32) << 8) | - palette[2::3]) - rgb24 = ((data[:, :, 0].astype(np.uint32) << 16) | - (data[:, :, 1].astype(np.uint32) << 8) | - data[:, :, 2]) - indices = np.argsort(palette24).astype(np.uint8) - rgb8 = indices[np.searchsorted(palette24, rgb24, sorter=indices)] - img = Image.fromarray(rgb8, mode='P') - img.putpalette(palette) - png_data, bit_depth, palette = self._writePng(img) - if bit_depth is None or palette is None: - raise RuntimeError("invalid PNG header") - palette = palette[:num_colors * 3] # Trim padding; remove for Pillow>=9 - obj['ColorSpace'] = [Name('Indexed'), Name('DeviceRGB'), - num_colors - 1, palette] - obj['BitsPerComponent'] = bit_depth - png['Colors'] = 1 - png['BitsPerComponent'] = bit_depth - else: - png_data, _, _ = self._writePng(img) - else: - png = None - self.beginStream( - id, - self.reserveObject('length of image stream'), - obj, - png=png - ) - if png: - self.currentstream.write(png_data) - else: - self.currentstream.write(data.tobytes()) - self.endStream() - - def writeImages(self): - for img, name, ob in self._images.values(): - data, adata = self._unpack(img) - if adata is not None: - smaskObject = self.reserveObject("smask") - self._writeImg(adata, smaskObject.id) - else: - smaskObject = None - self._writeImg(data, ob.id, smaskObject) - - def markerObject(self, path, trans, fill, stroke, lw, joinstyle, - capstyle): - """Return name of a marker XObject representing the given path.""" - # self.markers used by markerObject, writeMarkers, close: - # mapping from (path operations, fill?, stroke?) to - # [name, object reference, bounding box, linewidth] - # This enables different draw_markers calls to share the XObject - # if the gc is sufficiently similar: colors etc can vary, but - # the choices of whether to fill and whether to stroke cannot. - # We need a bounding box enclosing all of the XObject path, - # but since line width may vary, we store the maximum of all - # occurring line widths in self.markers. - # close() is somewhat tightly coupled in that it expects the - # first two components of each value in self.markers to be the - # name and object reference. - pathops = self.pathOperations(path, trans, simplify=False) - key = (tuple(pathops), bool(fill), bool(stroke), joinstyle, capstyle) - result = self.markers.get(key) - if result is None: - name = Name('M%d' % len(self.markers)) - ob = self.reserveObject('marker %d' % len(self.markers)) - bbox = path.get_extents(trans) - self.markers[key] = [name, ob, bbox, lw] - else: - if result[-1] < lw: - result[-1] = lw - name = result[0] - return name - - def writeMarkers(self): - for ((pathops, fill, stroke, joinstyle, capstyle), - (name, ob, bbox, lw)) in self.markers.items(): - # bbox wraps the exact limits of the control points, so half a line - # will appear outside it. If the join style is miter and the line - # is not parallel to the edge, then the line will extend even - # further. From the PDF specification, Section 8.4.3.5, the miter - # limit is miterLength / lineWidth and from Table 52, the default - # is 10. With half the miter length outside, that works out to the - # following padding: - bbox = bbox.padded(lw * 5) - self.beginStream( - ob.id, None, - {'Type': Name('XObject'), 'Subtype': Name('Form'), - 'BBox': list(bbox.extents)}) - self.output(GraphicsContextPdf.joinstyles[joinstyle], - Op.setlinejoin) - self.output(GraphicsContextPdf.capstyles[capstyle], Op.setlinecap) - self.output(*pathops) - self.output(Op.paint_path(fill, stroke)) - self.endStream() - - def pathCollectionObject(self, gc, path, trans, padding, filled, stroked): - name = Name('P%d' % len(self.paths)) - ob = self.reserveObject('path %d' % len(self.paths)) - self.paths.append( - (name, path, trans, ob, gc.get_joinstyle(), gc.get_capstyle(), - padding, filled, stroked)) - return name - - def writePathCollectionTemplates(self): - for (name, path, trans, ob, joinstyle, capstyle, padding, filled, - stroked) in self.paths: - pathops = self.pathOperations(path, trans, simplify=False) - bbox = path.get_extents(trans) - if not np.all(np.isfinite(bbox.extents)): - extents = [0, 0, 0, 0] - else: - bbox = bbox.padded(padding) - extents = list(bbox.extents) - self.beginStream( - ob.id, None, - {'Type': Name('XObject'), 'Subtype': Name('Form'), - 'BBox': extents}) - self.output(GraphicsContextPdf.joinstyles[joinstyle], - Op.setlinejoin) - self.output(GraphicsContextPdf.capstyles[capstyle], Op.setlinecap) - self.output(*pathops) - self.output(Op.paint_path(filled, stroked)) - self.endStream() - - @staticmethod - def pathOperations(path, transform, clip=None, simplify=None, sketch=None): - return [Verbatim(_path.convert_to_string( - path, transform, clip, simplify, sketch, - 6, - [Op.moveto.value, Op.lineto.value, b'', Op.curveto.value, - Op.closepath.value], - True))] - - def writePath(self, path, transform, clip=False, sketch=None): - if clip: - clip = (0.0, 0.0, self.width * 72, self.height * 72) - simplify = path.should_simplify - else: - clip = None - simplify = False - cmds = self.pathOperations(path, transform, clip, simplify=simplify, - sketch=sketch) - self.output(*cmds) - - def reserveObject(self, name=''): - """ - Reserve an ID for an indirect object. - - The name is used for debugging in case we forget to print out - the object with writeObject. - """ - id = next(self._object_seq) - self.xrefTable.append([None, 0, name]) - return Reference(id) - - def recordXref(self, id): - self.xrefTable[id][0] = self.fh.tell() - self.tell_base - - def writeObject(self, object, contents): - self.recordXref(object.id) - object.write(contents, self) - - def writeXref(self): - """Write out the xref table.""" - self.startxref = self.fh.tell() - self.tell_base - self.write(b"xref\n0 %d\n" % len(self.xrefTable)) - for i, (offset, generation, name) in enumerate(self.xrefTable): - if offset is None: - raise AssertionError( - 'No offset for object %d (%s)' % (i, name)) - else: - key = b"f" if name == 'the zero object' else b"n" - text = b"%010d %05d %b \n" % (offset, generation, key) - self.write(text) - - def writeInfoDict(self): - """Write out the info dictionary, checking it for good form""" - - self.infoObject = self.reserveObject('info') - self.writeObject(self.infoObject, self.infoDict) - - def writeTrailer(self): - """Write out the PDF trailer.""" - - self.write(b"trailer\n") - self.write(pdfRepr( - {'Size': len(self.xrefTable), - 'Root': self.rootObject, - 'Info': self.infoObject})) - # Could add 'ID' - self.write(b"\nstartxref\n%d\n%%%%EOF\n" % self.startxref) - - -class RendererPdf(_backend_pdf_ps.RendererPDFPSBase): - - _afm_font_dir = cbook._get_data_path("fonts/pdfcorefonts") - _use_afm_rc_name = "pdf.use14corefonts" - - def __init__(self, file, image_dpi, height, width): - super().__init__(width, height) - self.file = file - self.gc = self.new_gc() - self.image_dpi = image_dpi - - def finalize(self): - self.file.output(*self.gc.finalize()) - - def check_gc(self, gc, fillcolor=None): - orig_fill = getattr(gc, '_fillcolor', (0., 0., 0.)) - gc._fillcolor = fillcolor - - orig_alphas = getattr(gc, '_effective_alphas', (1.0, 1.0)) - - if gc.get_rgb() is None: - # It should not matter what color here since linewidth should be - # 0 unless affected by global settings in rcParams, hence setting - # zero alpha just in case. - gc.set_foreground((0, 0, 0, 0), isRGBA=True) - - if gc._forced_alpha: - gc._effective_alphas = (gc._alpha, gc._alpha) - elif fillcolor is None or len(fillcolor) < 4: - gc._effective_alphas = (gc._rgb[3], 1.0) - else: - gc._effective_alphas = (gc._rgb[3], fillcolor[3]) - - delta = self.gc.delta(gc) - if delta: - self.file.output(*delta) - - # Restore gc to avoid unwanted side effects - gc._fillcolor = orig_fill - gc._effective_alphas = orig_alphas - - def get_image_magnification(self): - return self.image_dpi/72.0 - - def draw_image(self, gc, x, y, im, transform=None): - # docstring inherited - - h, w = im.shape[:2] - if w == 0 or h == 0: - return - - if transform is None: - # If there's no transform, alpha has already been applied - gc.set_alpha(1.0) - - self.check_gc(gc) - - w = 72.0 * w / self.image_dpi - h = 72.0 * h / self.image_dpi - - imob = self.file.imageObject(im) - - if transform is None: - self.file.output(Op.gsave, - w, 0, 0, h, x, y, Op.concat_matrix, - imob, Op.use_xobject, Op.grestore) - else: - tr1, tr2, tr3, tr4, tr5, tr6 = transform.frozen().to_values() - - self.file.output(Op.gsave, - 1, 0, 0, 1, x, y, Op.concat_matrix, - tr1, tr2, tr3, tr4, tr5, tr6, Op.concat_matrix, - imob, Op.use_xobject, Op.grestore) - - def draw_path(self, gc, path, transform, rgbFace=None): - # docstring inherited - self.check_gc(gc, rgbFace) - self.file.writePath( - path, transform, - rgbFace is None and gc.get_hatch_path() is None, - gc.get_sketch_params()) - self.file.output(self.gc.paint()) - - def draw_path_collection(self, gc, master_transform, paths, all_transforms, - offsets, offset_trans, facecolors, edgecolors, - linewidths, linestyles, antialiaseds, urls, - offset_position): - # We can only reuse the objects if the presence of fill and - # stroke (and the amount of alpha for each) is the same for - # all of them - can_do_optimization = True - facecolors = np.asarray(facecolors) - edgecolors = np.asarray(edgecolors) - - if not len(facecolors): - filled = False - can_do_optimization = not gc.get_hatch() - else: - if np.all(facecolors[:, 3] == facecolors[0, 3]): - filled = facecolors[0, 3] != 0.0 - else: - can_do_optimization = False - - if not len(edgecolors): - stroked = False - else: - if np.all(np.asarray(linewidths) == 0.0): - stroked = False - elif np.all(edgecolors[:, 3] == edgecolors[0, 3]): - stroked = edgecolors[0, 3] != 0.0 - else: - can_do_optimization = False - - # Is the optimization worth it? Rough calculation: - # cost of emitting a path in-line is len_path * uses_per_path - # cost of XObject is len_path + 5 for the definition, - # uses_per_path for the uses - len_path = len(paths[0].vertices) if len(paths) > 0 else 0 - uses_per_path = self._iter_collection_uses_per_path( - paths, all_transforms, offsets, facecolors, edgecolors) - should_do_optimization = \ - len_path + uses_per_path + 5 < len_path * uses_per_path - - if (not can_do_optimization) or (not should_do_optimization): - return RendererBase.draw_path_collection( - self, gc, master_transform, paths, all_transforms, - offsets, offset_trans, facecolors, edgecolors, - linewidths, linestyles, antialiaseds, urls, - offset_position) - - padding = np.max(linewidths) - path_codes = [] - for i, (path, transform) in enumerate(self._iter_collection_raw_paths( - master_transform, paths, all_transforms)): - name = self.file.pathCollectionObject( - gc, path, transform, padding, filled, stroked) - path_codes.append(name) - - output = self.file.output - output(*self.gc.push()) - lastx, lasty = 0, 0 - for xo, yo, path_id, gc0, rgbFace in self._iter_collection( - gc, path_codes, offsets, offset_trans, - facecolors, edgecolors, linewidths, linestyles, - antialiaseds, urls, offset_position): - - self.check_gc(gc0, rgbFace) - dx, dy = xo - lastx, yo - lasty - output(1, 0, 0, 1, dx, dy, Op.concat_matrix, path_id, - Op.use_xobject) - lastx, lasty = xo, yo - output(*self.gc.pop()) - - def draw_markers(self, gc, marker_path, marker_trans, path, trans, - rgbFace=None): - # docstring inherited - - # Same logic as in draw_path_collection - len_marker_path = len(marker_path) - uses = len(path) - if len_marker_path * uses < len_marker_path + uses + 5: - RendererBase.draw_markers(self, gc, marker_path, marker_trans, - path, trans, rgbFace) - return - - self.check_gc(gc, rgbFace) - fill = gc.fill(rgbFace) - stroke = gc.stroke() - - output = self.file.output - marker = self.file.markerObject( - marker_path, marker_trans, fill, stroke, self.gc._linewidth, - gc.get_joinstyle(), gc.get_capstyle()) - - output(Op.gsave) - lastx, lasty = 0, 0 - for vertices, code in path.iter_segments( - trans, - clip=(0, 0, self.file.width*72, self.file.height*72), - simplify=False): - if len(vertices): - x, y = vertices[-2:] - if not (0 <= x <= self.file.width * 72 - and 0 <= y <= self.file.height * 72): - continue - dx, dy = x - lastx, y - lasty - output(1, 0, 0, 1, dx, dy, Op.concat_matrix, - marker, Op.use_xobject) - lastx, lasty = x, y - output(Op.grestore) - - def draw_gouraud_triangle(self, gc, points, colors, trans): - self.draw_gouraud_triangles(gc, points.reshape((1, 3, 2)), - colors.reshape((1, 3, 4)), trans) - - def draw_gouraud_triangles(self, gc, points, colors, trans): - assert len(points) == len(colors) - if len(points) == 0: - return - assert points.ndim == 3 - assert points.shape[1] == 3 - assert points.shape[2] == 2 - assert colors.ndim == 3 - assert colors.shape[1] == 3 - assert colors.shape[2] in (1, 4) - - shape = points.shape - points = points.reshape((shape[0] * shape[1], 2)) - tpoints = trans.transform(points) - tpoints = tpoints.reshape(shape) - name, _ = self.file.addGouraudTriangles(tpoints, colors) - output = self.file.output - - if colors.shape[2] == 1: - # grayscale - gc.set_alpha(1.0) - self.check_gc(gc) - output(name, Op.shading) - return - - alpha = colors[0, 0, 3] - if np.allclose(alpha, colors[:, :, 3]): - # single alpha value - gc.set_alpha(alpha) - self.check_gc(gc) - output(name, Op.shading) - else: - # varying alpha: use a soft mask - alpha = colors[:, :, 3][:, :, None] - _, smask_ob = self.file.addGouraudTriangles(tpoints, alpha) - gstate = self.file._soft_mask_state(smask_ob) - output(Op.gsave, gstate, Op.setgstate, - name, Op.shading, - Op.grestore) - - def _setup_textpos(self, x, y, angle, oldx=0, oldy=0, oldangle=0): - if angle == oldangle == 0: - self.file.output(x - oldx, y - oldy, Op.textpos) - else: - angle = math.radians(angle) - self.file.output(math.cos(angle), math.sin(angle), - -math.sin(angle), math.cos(angle), - x, y, Op.textmatrix) - self.file.output(0, 0, Op.textpos) - - def draw_mathtext(self, gc, x, y, s, prop, angle): - # TODO: fix positioning and encoding - width, height, descent, glyphs, rects = \ - self._text2path.mathtext_parser.parse(s, 72, prop) - - if gc.get_url() is not None: - self.file._annotations[-1][1].append(_get_link_annotation( - gc, x, y, width, height, angle)) - - fonttype = mpl.rcParams['pdf.fonttype'] - - # Set up a global transformation matrix for the whole math expression - a = math.radians(angle) - self.file.output(Op.gsave) - self.file.output(math.cos(a), math.sin(a), - -math.sin(a), math.cos(a), - x, y, Op.concat_matrix) - - self.check_gc(gc, gc._rgb) - prev_font = None, None - oldx, oldy = 0, 0 - unsupported_chars = [] - - self.file.output(Op.begin_text) - for font, fontsize, num, ox, oy in glyphs: - self.file._character_tracker.track_glyph(font, num) - fontname = font.fname - if not _font_supports_glyph(fonttype, num): - # Unsupported chars (i.e. multibyte in Type 3 or beyond BMP in - # Type 42) must be emitted separately (below). - unsupported_chars.append((font, fontsize, ox, oy, num)) - else: - self._setup_textpos(ox, oy, 0, oldx, oldy) - oldx, oldy = ox, oy - if (fontname, fontsize) != prev_font: - self.file.output(self.file.fontName(fontname), fontsize, - Op.selectfont) - prev_font = fontname, fontsize - self.file.output(self.encode_string(chr(num), fonttype), - Op.show) - self.file.output(Op.end_text) - - for font, fontsize, ox, oy, num in unsupported_chars: - self._draw_xobject_glyph( - font, fontsize, font.get_char_index(num), ox, oy) - - # Draw any horizontal lines in the math layout - for ox, oy, width, height in rects: - self.file.output(Op.gsave, ox, oy, width, height, - Op.rectangle, Op.fill, Op.grestore) - - # Pop off the global transformation - self.file.output(Op.grestore) - - def draw_tex(self, gc, x, y, s, prop, angle, *, mtext=None): - # docstring inherited - texmanager = self.get_texmanager() - fontsize = prop.get_size_in_points() - dvifile = texmanager.make_dvi(s, fontsize) - with dviread.Dvi(dvifile, 72) as dvi: - page, = dvi - - if gc.get_url() is not None: - self.file._annotations[-1][1].append(_get_link_annotation( - gc, x, y, page.width, page.height, angle)) - - # Gather font information and do some setup for combining - # characters into strings. The variable seq will contain a - # sequence of font and text entries. A font entry is a list - # ['font', name, size] where name is a Name object for the - # font. A text entry is ['text', x, y, glyphs, x+w] where x - # and y are the starting coordinates, w is the width, and - # glyphs is a list; in this phase it will always contain just - # one one-character string, but later it may have longer - # strings interspersed with kern amounts. - oldfont, seq = None, [] - for x1, y1, dvifont, glyph, width in page.text: - if dvifont != oldfont: - pdfname = self.file.dviFontName(dvifont) - seq += [['font', pdfname, dvifont.size]] - oldfont = dvifont - seq += [['text', x1, y1, [bytes([glyph])], x1+width]] - - # Find consecutive text strings with constant y coordinate and - # combine into a sequence of strings and kerns, or just one - # string (if any kerns would be less than 0.1 points). - i, curx, fontsize = 0, 0, None - while i < len(seq)-1: - elt, nxt = seq[i:i+2] - if elt[0] == 'font': - fontsize = elt[2] - elif elt[0] == nxt[0] == 'text' and elt[2] == nxt[2]: - offset = elt[4] - nxt[1] - if abs(offset) < 0.1: - elt[3][-1] += nxt[3][0] - elt[4] += nxt[4]-nxt[1] - else: - elt[3] += [offset*1000.0/fontsize, nxt[3][0]] - elt[4] = nxt[4] - del seq[i+1] - continue - i += 1 - - # Create a transform to map the dvi contents to the canvas. - mytrans = Affine2D().rotate_deg(angle).translate(x, y) - - # Output the text. - self.check_gc(gc, gc._rgb) - self.file.output(Op.begin_text) - curx, cury, oldx, oldy = 0, 0, 0, 0 - for elt in seq: - if elt[0] == 'font': - self.file.output(elt[1], elt[2], Op.selectfont) - elif elt[0] == 'text': - curx, cury = mytrans.transform((elt[1], elt[2])) - self._setup_textpos(curx, cury, angle, oldx, oldy) - oldx, oldy = curx, cury - if len(elt[3]) == 1: - self.file.output(elt[3][0], Op.show) - else: - self.file.output(elt[3], Op.showkern) - else: - assert False - self.file.output(Op.end_text) - - # Then output the boxes (e.g., variable-length lines of square - # roots). - boxgc = self.new_gc() - boxgc.copy_properties(gc) - boxgc.set_linewidth(0) - pathops = [Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, - Path.CLOSEPOLY] - for x1, y1, h, w in page.boxes: - path = Path([[x1, y1], [x1+w, y1], [x1+w, y1+h], [x1, y1+h], - [0, 0]], pathops) - self.draw_path(boxgc, path, mytrans, gc._rgb) - - def encode_string(self, s, fonttype): - if fonttype in (1, 3): - return s.encode('cp1252', 'replace') - return s.encode('utf-16be', 'replace') - - def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): - # docstring inherited - - # TODO: combine consecutive texts into one BT/ET delimited section - - self.check_gc(gc, gc._rgb) - if ismath: - return self.draw_mathtext(gc, x, y, s, prop, angle) - - fontsize = prop.get_size_in_points() - - if mpl.rcParams['pdf.use14corefonts']: - font = self._get_font_afm(prop) - fonttype = 1 - else: - font = self._get_font_ttf(prop) - self.file._character_tracker.track(font, s) - fonttype = mpl.rcParams['pdf.fonttype'] - - if gc.get_url() is not None: - font.set_text(s) - width, height = font.get_width_height() - self.file._annotations[-1][1].append(_get_link_annotation( - gc, x, y, width / 64, height / 64, angle)) - - # If fonttype is neither 3 nor 42, emit the whole string at once - # without manual kerning. - if fonttype not in [3, 42]: - self.file.output(Op.begin_text, - self.file.fontName(prop), fontsize, Op.selectfont) - self._setup_textpos(x, y, angle) - self.file.output(self.encode_string(s, fonttype), - Op.show, Op.end_text) - - # A sequence of characters is broken into multiple chunks. The chunking - # serves two purposes: - # - For Type 3 fonts, there is no way to access multibyte characters, - # as they cannot have a CIDMap. Therefore, in this case we break - # the string into chunks, where each chunk contains either a string - # of consecutive 1-byte characters or a single multibyte character. - # - A sequence of 1-byte characters is split into chunks to allow for - # kerning adjustments between consecutive chunks. - # - # Each chunk is emitted with a separate command: 1-byte characters use - # the regular text show command (TJ) with appropriate kerning between - # chunks, whereas multibyte characters use the XObject command (Do). - else: - # List of (ft_object, start_x, [prev_kern, char, char, ...]), - # w/o zero kerns. - singlebyte_chunks = [] - # List of (ft_object, start_x, glyph_index). - multibyte_glyphs = [] - prev_was_multibyte = True - prev_font = font - for item in _text_helpers.layout( - s, font, kern_mode=KERNING_UNFITTED): - if _font_supports_glyph(fonttype, ord(item.char)): - if prev_was_multibyte or item.ft_object != prev_font: - singlebyte_chunks.append((item.ft_object, item.x, [])) - prev_font = item.ft_object - if item.prev_kern: - singlebyte_chunks[-1][2].append(item.prev_kern) - singlebyte_chunks[-1][2].append(item.char) - prev_was_multibyte = False - else: - multibyte_glyphs.append( - (item.ft_object, item.x, item.glyph_idx) - ) - prev_was_multibyte = True - # Do the rotation and global translation as a single matrix - # concatenation up front - self.file.output(Op.gsave) - a = math.radians(angle) - self.file.output(math.cos(a), math.sin(a), - -math.sin(a), math.cos(a), - x, y, Op.concat_matrix) - # Emit all the 1-byte characters in a BT/ET group. - - self.file.output(Op.begin_text) - prev_start_x = 0 - for ft_object, start_x, kerns_or_chars in singlebyte_chunks: - ft_name = self.file.fontName(ft_object.fname) - self.file.output(ft_name, fontsize, Op.selectfont) - self._setup_textpos(start_x, 0, 0, prev_start_x, 0, 0) - self.file.output( - # See pdf spec "Text space details" for the 1000/fontsize - # (aka. 1000/T_fs) factor. - [-1000 * next(group) / fontsize if tp == float # a kern - else self.encode_string("".join(group), fonttype) - for tp, group in itertools.groupby(kerns_or_chars, type)], - Op.showkern) - prev_start_x = start_x - self.file.output(Op.end_text) - # Then emit all the multibyte characters, one at a time. - for ft_object, start_x, glyph_idx in multibyte_glyphs: - self._draw_xobject_glyph( - ft_object, fontsize, glyph_idx, start_x, 0 - ) - self.file.output(Op.grestore) - - def _draw_xobject_glyph(self, font, fontsize, glyph_idx, x, y): - """Draw a multibyte character from a Type 3 font as an XObject.""" - glyph_name = font.get_glyph_name(glyph_idx) - name = self.file._get_xobject_glyph_name(font.fname, glyph_name) - self.file.output( - Op.gsave, - 0.001 * fontsize, 0, 0, 0.001 * fontsize, x, y, Op.concat_matrix, - Name(name), Op.use_xobject, - Op.grestore, - ) - - def new_gc(self): - # docstring inherited - return GraphicsContextPdf(self.file) - - -class GraphicsContextPdf(GraphicsContextBase): - - def __init__(self, file): - super().__init__() - self._fillcolor = (0.0, 0.0, 0.0) - self._effective_alphas = (1.0, 1.0) - self.file = file - self.parent = None - - def __repr__(self): - d = dict(self.__dict__) - del d['file'] - del d['parent'] - return repr(d) - - def stroke(self): - """ - Predicate: does the path need to be stroked (its outline drawn)? - This tests for the various conditions that disable stroking - the path, in which case it would presumably be filled. - """ - # _linewidth > 0: in pdf a line of width 0 is drawn at minimum - # possible device width, but e.g., agg doesn't draw at all - return (self._linewidth > 0 and self._alpha > 0 and - (len(self._rgb) <= 3 or self._rgb[3] != 0.0)) - - def fill(self, *args): - """ - Predicate: does the path need to be filled? - - An optional argument can be used to specify an alternative - _fillcolor, as needed by RendererPdf.draw_markers. - """ - if len(args): - _fillcolor = args[0] - else: - _fillcolor = self._fillcolor - return (self._hatch or - (_fillcolor is not None and - (len(_fillcolor) <= 3 or _fillcolor[3] != 0.0))) - - def paint(self): - """ - Return the appropriate pdf operator to cause the path to be - stroked, filled, or both. - """ - return Op.paint_path(self.fill(), self.stroke()) - - capstyles = {'butt': 0, 'round': 1, 'projecting': 2} - joinstyles = {'miter': 0, 'round': 1, 'bevel': 2} - - def capstyle_cmd(self, style): - return [self.capstyles[style], Op.setlinecap] - - def joinstyle_cmd(self, style): - return [self.joinstyles[style], Op.setlinejoin] - - def linewidth_cmd(self, width): - return [width, Op.setlinewidth] - - def dash_cmd(self, dashes): - offset, dash = dashes - if dash is None: - dash = [] - offset = 0 - return [list(dash), offset, Op.setdash] - - def alpha_cmd(self, alpha, forced, effective_alphas): - name = self.file.alphaState(effective_alphas) - return [name, Op.setgstate] - - def hatch_cmd(self, hatch, hatch_color): - if not hatch: - if self._fillcolor is not None: - return self.fillcolor_cmd(self._fillcolor) - else: - return [Name('DeviceRGB'), Op.setcolorspace_nonstroke] - else: - hatch_style = (hatch_color, self._fillcolor, hatch) - name = self.file.hatchPattern(hatch_style) - return [Name('Pattern'), Op.setcolorspace_nonstroke, - name, Op.setcolor_nonstroke] - - def rgb_cmd(self, rgb): - if mpl.rcParams['pdf.inheritcolor']: - return [] - if rgb[0] == rgb[1] == rgb[2]: - return [rgb[0], Op.setgray_stroke] - else: - return [*rgb[:3], Op.setrgb_stroke] - - def fillcolor_cmd(self, rgb): - if rgb is None or mpl.rcParams['pdf.inheritcolor']: - return [] - elif rgb[0] == rgb[1] == rgb[2]: - return [rgb[0], Op.setgray_nonstroke] - else: - return [*rgb[:3], Op.setrgb_nonstroke] - - def push(self): - parent = GraphicsContextPdf(self.file) - parent.copy_properties(self) - parent.parent = self.parent - self.parent = parent - return [Op.gsave] - - def pop(self): - assert self.parent is not None - self.copy_properties(self.parent) - self.parent = self.parent.parent - return [Op.grestore] - - def clip_cmd(self, cliprect, clippath): - """Set clip rectangle. Calls `.pop()` and `.push()`.""" - cmds = [] - # Pop graphics state until we hit the right one or the stack is empty - while ((self._cliprect, self._clippath) != (cliprect, clippath) - and self.parent is not None): - cmds.extend(self.pop()) - # Unless we hit the right one, set the clip polygon - if ((self._cliprect, self._clippath) != (cliprect, clippath) or - self.parent is None): - cmds.extend(self.push()) - if self._cliprect != cliprect: - cmds.extend([cliprect, Op.rectangle, Op.clip, Op.endpath]) - if self._clippath != clippath: - path, affine = clippath.get_transformed_path_and_affine() - cmds.extend( - PdfFile.pathOperations(path, affine, simplify=False) + - [Op.clip, Op.endpath]) - return cmds - - commands = ( - # must come first since may pop - (('_cliprect', '_clippath'), clip_cmd), - (('_alpha', '_forced_alpha', '_effective_alphas'), alpha_cmd), - (('_capstyle',), capstyle_cmd), - (('_fillcolor',), fillcolor_cmd), - (('_joinstyle',), joinstyle_cmd), - (('_linewidth',), linewidth_cmd), - (('_dashes',), dash_cmd), - (('_rgb',), rgb_cmd), - # must come after fillcolor and rgb - (('_hatch', '_hatch_color'), hatch_cmd), - ) - - def delta(self, other): - """ - Copy properties of other into self and return PDF commands - needed to transform *self* into *other*. - """ - cmds = [] - fill_performed = False - for params, cmd in self.commands: - different = False - for p in params: - ours = getattr(self, p) - theirs = getattr(other, p) - try: - if ours is None or theirs is None: - different = ours is not theirs - else: - different = bool(ours != theirs) - except ValueError: - ours = np.asarray(ours) - theirs = np.asarray(theirs) - different = (ours.shape != theirs.shape or - np.any(ours != theirs)) - if different: - break - - # Need to update hatching if we also updated fillcolor - if params == ('_hatch', '_hatch_color') and fill_performed: - different = True - - if different: - if params == ('_fillcolor',): - fill_performed = True - theirs = [getattr(other, p) for p in params] - cmds.extend(cmd(self, *theirs)) - for p in params: - setattr(self, p, getattr(other, p)) - return cmds - - def copy_properties(self, other): - """ - Copy properties of other into self. - """ - super().copy_properties(other) - fillcolor = getattr(other, '_fillcolor', self._fillcolor) - effective_alphas = getattr(other, '_effective_alphas', - self._effective_alphas) - self._fillcolor = fillcolor - self._effective_alphas = effective_alphas - - def finalize(self): - """ - Make sure every pushed graphics state is popped. - """ - cmds = [] - while self.parent is not None: - cmds.extend(self.pop()) - return cmds - - -class PdfPages: - """ - A multi-page PDF file. - - Examples - -------- - >>> import matplotlib.pyplot as plt - >>> # Initialize: - >>> with PdfPages('foo.pdf') as pdf: - ... # As many times as you like, create a figure fig and save it: - ... fig = plt.figure() - ... pdf.savefig(fig) - ... # When no figure is specified the current figure is saved - ... pdf.savefig() - - Notes - ----- - In reality `PdfPages` is a thin wrapper around `PdfFile`, in order to avoid - confusion when using `~.pyplot.savefig` and forgetting the format argument. - """ - __slots__ = ('_file', 'keep_empty') - - def __init__(self, filename, keep_empty=True, metadata=None): - """ - Create a new PdfPages object. - - Parameters - ---------- - filename : str or path-like or file-like - Plots using `PdfPages.savefig` will be written to a file at this - location. The file is opened at once and any older file with the - same name is overwritten. - - keep_empty : bool, optional - If set to False, then empty pdf files will be deleted automatically - when closed. - - metadata : dict, optional - Information dictionary object (see PDF reference section 10.2.1 - 'Document Information Dictionary'), e.g.: - ``{'Creator': 'My software', 'Author': 'Me', 'Title': 'Awesome'}``. - - The standard keys are 'Title', 'Author', 'Subject', 'Keywords', - 'Creator', 'Producer', 'CreationDate', 'ModDate', and - 'Trapped'. Values have been predefined for 'Creator', 'Producer' - and 'CreationDate'. They can be removed by setting them to `None`. - """ - self._file = PdfFile(filename, metadata=metadata) - self.keep_empty = keep_empty - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.close() - - def close(self): - """ - Finalize this object, making the underlying file a complete - PDF file. - """ - self._file.finalize() - self._file.close() - if (self.get_pagecount() == 0 and not self.keep_empty and - not self._file.passed_in_file_object): - os.remove(self._file.fh.name) - self._file = None - - def infodict(self): - """ - Return a modifiable information dictionary object - (see PDF reference section 10.2.1 'Document Information - Dictionary'). - """ - return self._file.infoDict - - def savefig(self, figure=None, **kwargs): - """ - Save a `.Figure` to this file as a new page. - - Any other keyword arguments are passed to `~.Figure.savefig`. - - Parameters - ---------- - figure : `.Figure` or int, default: the active figure - The figure, or index of the figure, that is saved to the file. - """ - if not isinstance(figure, Figure): - if figure is None: - manager = Gcf.get_active() - else: - manager = Gcf.get_fig_manager(figure) - if manager is None: - raise ValueError("No figure {}".format(figure)) - figure = manager.canvas.figure - # Force use of pdf backend, as PdfPages is tightly coupled with it. - try: - orig_canvas = figure.canvas - figure.canvas = FigureCanvasPdf(figure) - figure.savefig(self, format="pdf", **kwargs) - finally: - figure.canvas = orig_canvas - - def get_pagecount(self): - """Return the current number of pages in the multipage pdf file.""" - return len(self._file.pageList) - - def attach_note(self, text, positionRect=[-100, -100, 0, 0]): - """ - Add a new text note to the page to be saved next. The optional - positionRect specifies the position of the new note on the - page. It is outside the page per default to make sure it is - invisible on printouts. - """ - self._file.newTextnote(text, positionRect) - - -class FigureCanvasPdf(FigureCanvasBase): - # docstring inherited - - fixed_dpi = 72 - filetypes = {'pdf': 'Portable Document Format'} - - def get_default_filetype(self): - return 'pdf' - - def print_pdf(self, filename, *, - bbox_inches_restore=None, metadata=None): - - dpi = self.figure.dpi - self.figure.dpi = 72 # there are 72 pdf points to an inch - width, height = self.figure.get_size_inches() - if isinstance(filename, PdfPages): - file = filename._file - else: - file = PdfFile(filename, metadata=metadata) - try: - file.newPage(width, height) - renderer = MixedModeRenderer( - self.figure, width, height, dpi, - RendererPdf(file, dpi, height, width), - bbox_inches_restore=bbox_inches_restore) - self.figure.draw(renderer) - renderer.finalize() - if not isinstance(filename, PdfPages): - file.finalize() - finally: - if isinstance(filename, PdfPages): # finish off this page - file.endStream() - else: # we opened the file above; now finish it off - file.close() - - def draw(self): - self.figure.draw_without_rendering() - return super().draw() - - -FigureManagerPdf = FigureManagerBase - - -@_Backend.export -class _BackendPdf(_Backend): - FigureCanvas = FigureCanvasPdf diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_pgf.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_pgf.py deleted file mode 100644 index 96d6040..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_pgf.py +++ /dev/null @@ -1,1059 +0,0 @@ -import codecs -import datetime -import functools -from io import BytesIO -import logging -import math -import os -import pathlib -import re -import shutil -import subprocess -from tempfile import TemporaryDirectory -import weakref - -from PIL import Image - -import matplotlib as mpl -from matplotlib import _api, cbook, font_manager as fm -from matplotlib.backend_bases import ( - _Backend, FigureCanvasBase, FigureManagerBase, RendererBase -) -from matplotlib.backends.backend_mixed import MixedModeRenderer -from matplotlib.backends.backend_pdf import ( - _create_pdf_info_dict, _datetime_to_pdf) -from matplotlib.path import Path -from matplotlib.figure import Figure -from matplotlib._pylab_helpers import Gcf - -_log = logging.getLogger(__name__) - - -# Note: When formatting floating point values, it is important to use the -# %f/{:f} format rather than %s/{} to avoid triggering scientific notation, -# which is not recognized by TeX. - - -@_api.caching_module_getattr -class __getattr__: - NO_ESCAPE = _api.deprecated("3.6", obj_type="")( - property(lambda self: _NO_ESCAPE)) - re_mathsep = _api.deprecated("3.6", obj_type="")( - property(lambda self: _split_math.__self__)) - - -@_api.deprecated("3.6") -def get_fontspec(): - """Build fontspec preamble from rc.""" - with mpl.rc_context({"pgf.preamble": ""}): - return _get_preamble() - - -@_api.deprecated("3.6") -def get_preamble(): - """Get LaTeX preamble from rc.""" - return mpl.rcParams["pgf.preamble"] - - -def _get_preamble(): - """Prepare a LaTeX preamble based on the rcParams configuration.""" - preamble = [mpl.rcParams["pgf.preamble"]] - if mpl.rcParams["pgf.texsystem"] != "pdflatex": - preamble.append("\\usepackage{fontspec}") - if mpl.rcParams["pgf.rcfonts"]: - families = ["serif", "sans\\-serif", "monospace"] - commands = ["setmainfont", "setsansfont", "setmonofont"] - for family, command in zip(families, commands): - # 1) Forward slashes also work on Windows, so don't mess with - # backslashes. 2) The dirname needs to include a separator. - path = pathlib.Path(fm.findfont(family)) - preamble.append(r"\%s{%s}[Path=\detokenize{%s/}]" % ( - command, path.name, path.parent.as_posix())) - preamble.append(mpl.texmanager._usepackage_if_not_loaded( - "underscore", option="strings")) # Documented as "must come last". - return "\n".join(preamble) - - -# It's better to use only one unit for all coordinates, since the -# arithmetic in latex seems to produce inaccurate conversions. -latex_pt_to_in = 1. / 72.27 -latex_in_to_pt = 1. / latex_pt_to_in -mpl_pt_to_in = 1. / 72. -mpl_in_to_pt = 1. / mpl_pt_to_in - - -_NO_ESCAPE = r"(? 3 else 1.0 - - if has_fill: - _writeln(self.fh, - r"\definecolor{currentfill}{rgb}{%f,%f,%f}" - % tuple(rgbFace[:3])) - _writeln(self.fh, r"\pgfsetfillcolor{currentfill}") - if has_fill and fillopacity != 1.0: - _writeln(self.fh, r"\pgfsetfillopacity{%f}" % fillopacity) - - # linewidth and color - lw = gc.get_linewidth() * mpl_pt_to_in * latex_in_to_pt - stroke_rgba = gc.get_rgb() - _writeln(self.fh, r"\pgfsetlinewidth{%fpt}" % lw) - _writeln(self.fh, - r"\definecolor{currentstroke}{rgb}{%f,%f,%f}" - % stroke_rgba[:3]) - _writeln(self.fh, r"\pgfsetstrokecolor{currentstroke}") - if strokeopacity != 1.0: - _writeln(self.fh, r"\pgfsetstrokeopacity{%f}" % strokeopacity) - - # line style - dash_offset, dash_list = gc.get_dashes() - if dash_list is None: - _writeln(self.fh, r"\pgfsetdash{}{0pt}") - else: - _writeln(self.fh, - r"\pgfsetdash{%s}{%fpt}" - % ("".join(r"{%fpt}" % dash for dash in dash_list), - dash_offset)) - - def _print_pgf_path(self, gc, path, transform, rgbFace=None): - f = 1. / self.dpi - # check for clip box / ignore clip for filled paths - bbox = gc.get_clip_rectangle() if gc else None - maxcoord = 16383 / 72.27 * self.dpi # Max dimensions in LaTeX. - if bbox and (rgbFace is None): - p1, p2 = bbox.get_points() - clip = (max(p1[0], -maxcoord), max(p1[1], -maxcoord), - min(p2[0], maxcoord), min(p2[1], maxcoord)) - else: - clip = (-maxcoord, -maxcoord, maxcoord, maxcoord) - # build path - for points, code in path.iter_segments(transform, clip=clip): - if code == Path.MOVETO: - x, y = tuple(points) - _writeln(self.fh, - r"\pgfpathmoveto{\pgfqpoint{%fin}{%fin}}" % - (f * x, f * y)) - elif code == Path.CLOSEPOLY: - _writeln(self.fh, r"\pgfpathclose") - elif code == Path.LINETO: - x, y = tuple(points) - _writeln(self.fh, - r"\pgfpathlineto{\pgfqpoint{%fin}{%fin}}" % - (f * x, f * y)) - elif code == Path.CURVE3: - cx, cy, px, py = tuple(points) - coords = cx * f, cy * f, px * f, py * f - _writeln(self.fh, - r"\pgfpathquadraticcurveto" - r"{\pgfqpoint{%fin}{%fin}}{\pgfqpoint{%fin}{%fin}}" - % coords) - elif code == Path.CURVE4: - c1x, c1y, c2x, c2y, px, py = tuple(points) - coords = c1x * f, c1y * f, c2x * f, c2y * f, px * f, py * f - _writeln(self.fh, - r"\pgfpathcurveto" - r"{\pgfqpoint{%fin}{%fin}}" - r"{\pgfqpoint{%fin}{%fin}}" - r"{\pgfqpoint{%fin}{%fin}}" - % coords) - - # apply pgf decorators - sketch_params = gc.get_sketch_params() if gc else None - if sketch_params is not None: - # Only "length" directly maps to "segment length" in PGF's API. - # PGF uses "amplitude" to pass the combined deviation in both x- - # and y-direction, while matplotlib only varies the length of the - # wiggle along the line ("randomness" and "length" parameters) - # and has a separate "scale" argument for the amplitude. - # -> Use "randomness" as PRNG seed to allow the user to force the - # same shape on multiple sketched lines - scale, length, randomness = sketch_params - if scale is not None: - # make matplotlib and PGF rendering visually similar - length *= 0.5 - scale *= 2 - # PGF guarantees that repeated loading is a no-op - _writeln(self.fh, r"\usepgfmodule{decorations}") - _writeln(self.fh, r"\usepgflibrary{decorations.pathmorphing}") - _writeln(self.fh, r"\pgfkeys{/pgf/decoration/.cd, " - f"segment length = {(length * f):f}in, " - f"amplitude = {(scale * f):f}in}}") - _writeln(self.fh, f"\\pgfmathsetseed{{{int(randomness)}}}") - _writeln(self.fh, r"\pgfdecoratecurrentpath{random steps}") - - def _pgf_path_draw(self, stroke=True, fill=False): - actions = [] - if stroke: - actions.append("stroke") - if fill: - actions.append("fill") - _writeln(self.fh, r"\pgfusepath{%s}" % ",".join(actions)) - - def option_scale_image(self): - # docstring inherited - return True - - def option_image_nocomposite(self): - # docstring inherited - return not mpl.rcParams['image.composite_image'] - - def draw_image(self, gc, x, y, im, transform=None): - # docstring inherited - - h, w = im.shape[:2] - if w == 0 or h == 0: - return - - if not os.path.exists(getattr(self.fh, "name", "")): - raise ValueError( - "streamed pgf-code does not support raster graphics, consider " - "using the pgf-to-pdf option") - - # save the images to png files - path = pathlib.Path(self.fh.name) - fname_img = "%s-img%d.png" % (path.stem, self.image_counter) - Image.fromarray(im[::-1]).save(path.parent / fname_img) - self.image_counter += 1 - - # reference the image in the pgf picture - _writeln(self.fh, r"\begin{pgfscope}") - self._print_pgf_clip(gc) - f = 1. / self.dpi # from display coords to inch - if transform is None: - _writeln(self.fh, - r"\pgfsys@transformshift{%fin}{%fin}" % (x * f, y * f)) - w, h = w * f, h * f - else: - tr1, tr2, tr3, tr4, tr5, tr6 = transform.frozen().to_values() - _writeln(self.fh, - r"\pgfsys@transformcm{%f}{%f}{%f}{%f}{%fin}{%fin}" % - (tr1 * f, tr2 * f, tr3 * f, tr4 * f, - (tr5 + x) * f, (tr6 + y) * f)) - w = h = 1 # scale is already included in the transform - interp = str(transform is None).lower() # interpolation in PDF reader - _writeln(self.fh, - r"\pgftext[left,bottom]" - r"{%s[interpolate=%s,width=%fin,height=%fin]{%s}}" % - (_get_image_inclusion_command(), - interp, w, h, fname_img)) - _writeln(self.fh, r"\end{pgfscope}") - - def draw_tex(self, gc, x, y, s, prop, angle, *, mtext=None): - # docstring inherited - self.draw_text(gc, x, y, s, prop, angle, ismath="TeX", mtext=mtext) - - def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): - # docstring inherited - - # prepare string for tex - s = _escape_and_apply_props(s, prop) - - _writeln(self.fh, r"\begin{pgfscope}") - - alpha = gc.get_alpha() - if alpha != 1.0: - _writeln(self.fh, r"\pgfsetfillopacity{%f}" % alpha) - _writeln(self.fh, r"\pgfsetstrokeopacity{%f}" % alpha) - rgb = tuple(gc.get_rgb())[:3] - _writeln(self.fh, r"\definecolor{textcolor}{rgb}{%f,%f,%f}" % rgb) - _writeln(self.fh, r"\pgfsetstrokecolor{textcolor}") - _writeln(self.fh, r"\pgfsetfillcolor{textcolor}") - s = r"\color{textcolor}" + s - - dpi = self.figure.dpi - text_args = [] - if mtext and ( - (angle == 0 or - mtext.get_rotation_mode() == "anchor") and - mtext.get_verticalalignment() != "center_baseline"): - # if text anchoring can be supported, get the original coordinates - # and add alignment information - pos = mtext.get_unitless_position() - x, y = mtext.get_transform().transform(pos) - halign = {"left": "left", "right": "right", "center": ""} - valign = {"top": "top", "bottom": "bottom", - "baseline": "base", "center": ""} - text_args.extend([ - f"x={x/dpi:f}in", - f"y={y/dpi:f}in", - halign[mtext.get_horizontalalignment()], - valign[mtext.get_verticalalignment()], - ]) - else: - # if not, use the text layout provided by Matplotlib. - text_args.append(f"x={x/dpi:f}in, y={y/dpi:f}in, left, base") - - if angle != 0: - text_args.append("rotate=%f" % angle) - - _writeln(self.fh, r"\pgftext[%s]{%s}" % (",".join(text_args), s)) - _writeln(self.fh, r"\end{pgfscope}") - - def get_text_width_height_descent(self, s, prop, ismath): - # docstring inherited - # get text metrics in units of latex pt, convert to display units - w, h, d = (LatexManager._get_cached_or_new() - .get_width_height_descent(s, prop)) - # TODO: this should be latex_pt_to_in instead of mpl_pt_to_in - # but having a little bit more space around the text looks better, - # plus the bounding box reported by LaTeX is VERY narrow - f = mpl_pt_to_in * self.dpi - return w * f, h * f, d * f - - def flipy(self): - # docstring inherited - return False - - def get_canvas_width_height(self): - # docstring inherited - return (self.figure.get_figwidth() * self.dpi, - self.figure.get_figheight() * self.dpi) - - def points_to_pixels(self, points): - # docstring inherited - return points * mpl_pt_to_in * self.dpi - - -class FigureCanvasPgf(FigureCanvasBase): - filetypes = {"pgf": "LaTeX PGF picture", - "pdf": "LaTeX compiled PGF picture", - "png": "Portable Network Graphics", } - - def get_default_filetype(self): - return 'pdf' - - def _print_pgf_to_fh(self, fh, *, bbox_inches_restore=None): - - header_text = """%% Creator: Matplotlib, PGF backend -%% -%% To include the figure in your LaTeX document, write -%% \\input{.pgf} -%% -%% Make sure the required packages are loaded in your preamble -%% \\usepackage{pgf} -%% -%% Also ensure that all the required font packages are loaded; for instance, -%% the lmodern package is sometimes necessary when using math font. -%% \\usepackage{lmodern} -%% -%% Figures using additional raster images can only be included by \\input if -%% they are in the same directory as the main LaTeX file. For loading figures -%% from other directories you can use the `import` package -%% \\usepackage{import} -%% -%% and then include the figures with -%% \\import{}{.pgf} -%% -""" - - # append the preamble used by the backend as a comment for debugging - header_info_preamble = ["%% Matplotlib used the following preamble"] - for line in _get_preamble().splitlines(): - header_info_preamble.append("%% " + line) - header_info_preamble.append("%%") - header_info_preamble = "\n".join(header_info_preamble) - - # get figure size in inch - w, h = self.figure.get_figwidth(), self.figure.get_figheight() - dpi = self.figure.dpi - - # create pgfpicture environment and write the pgf code - fh.write(header_text) - fh.write(header_info_preamble) - fh.write("\n") - _writeln(fh, r"\begingroup") - _writeln(fh, r"\makeatletter") - _writeln(fh, r"\begin{pgfpicture}") - _writeln(fh, - r"\pgfpathrectangle{\pgfpointorigin}{\pgfqpoint{%fin}{%fin}}" - % (w, h)) - _writeln(fh, r"\pgfusepath{use as bounding box, clip}") - renderer = MixedModeRenderer(self.figure, w, h, dpi, - RendererPgf(self.figure, fh), - bbox_inches_restore=bbox_inches_restore) - self.figure.draw(renderer) - - # end the pgfpicture environment - _writeln(fh, r"\end{pgfpicture}") - _writeln(fh, r"\makeatother") - _writeln(fh, r"\endgroup") - - def print_pgf(self, fname_or_fh, **kwargs): - """ - Output pgf macros for drawing the figure so it can be included and - rendered in latex documents. - """ - with cbook.open_file_cm(fname_or_fh, "w", encoding="utf-8") as file: - if not cbook.file_requires_unicode(file): - file = codecs.getwriter("utf-8")(file) - self._print_pgf_to_fh(file, **kwargs) - - def print_pdf(self, fname_or_fh, *, metadata=None, **kwargs): - """Use LaTeX to compile a pgf generated figure to pdf.""" - w, h = self.figure.get_size_inches() - - info_dict = _create_pdf_info_dict('pgf', metadata or {}) - pdfinfo = ','.join( - _metadata_to_str(k, v) for k, v in info_dict.items()) - - # print figure to pgf and compile it with latex - with TemporaryDirectory() as tmpdir: - tmppath = pathlib.Path(tmpdir) - self.print_pgf(tmppath / "figure.pgf", **kwargs) - (tmppath / "figure.tex").write_text( - "\n".join([ - r"\documentclass[12pt]{article}", - r"\usepackage[pdfinfo={%s}]{hyperref}" % pdfinfo, - r"\usepackage[papersize={%fin,%fin}, margin=0in]{geometry}" - % (w, h), - r"\usepackage{pgf}", - _get_preamble(), - r"\begin{document}", - r"\centering", - r"\input{figure.pgf}", - r"\end{document}", - ]), encoding="utf-8") - texcommand = mpl.rcParams["pgf.texsystem"] - cbook._check_and_log_subprocess( - [texcommand, "-interaction=nonstopmode", "-halt-on-error", - "figure.tex"], _log, cwd=tmpdir) - with (tmppath / "figure.pdf").open("rb") as orig, \ - cbook.open_file_cm(fname_or_fh, "wb") as dest: - shutil.copyfileobj(orig, dest) # copy file contents to target - - def print_png(self, fname_or_fh, **kwargs): - """Use LaTeX to compile a pgf figure to pdf and convert it to png.""" - converter = make_pdf_to_png_converter() - with TemporaryDirectory() as tmpdir: - tmppath = pathlib.Path(tmpdir) - pdf_path = tmppath / "figure.pdf" - png_path = tmppath / "figure.png" - self.print_pdf(pdf_path, **kwargs) - converter(pdf_path, png_path, dpi=self.figure.dpi) - with png_path.open("rb") as orig, \ - cbook.open_file_cm(fname_or_fh, "wb") as dest: - shutil.copyfileobj(orig, dest) # copy file contents to target - - def get_renderer(self): - return RendererPgf(self.figure, None) - - def draw(self): - self.figure.draw_without_rendering() - return super().draw() - - -FigureManagerPgf = FigureManagerBase - - -@_Backend.export -class _BackendPgf(_Backend): - FigureCanvas = FigureCanvasPgf - - -class PdfPages: - """ - A multi-page PDF file using the pgf backend - - Examples - -------- - >>> import matplotlib.pyplot as plt - >>> # Initialize: - >>> with PdfPages('foo.pdf') as pdf: - ... # As many times as you like, create a figure fig and save it: - ... fig = plt.figure() - ... pdf.savefig(fig) - ... # When no figure is specified the current figure is saved - ... pdf.savefig() - """ - __slots__ = ( - '_output_name', - 'keep_empty', - '_n_figures', - '_file', - '_info_dict', - '_metadata', - ) - - def __init__(self, filename, *, keep_empty=True, metadata=None): - """ - Create a new PdfPages object. - - Parameters - ---------- - filename : str or path-like - Plots using `PdfPages.savefig` will be written to a file at this - location. Any older file with the same name is overwritten. - - keep_empty : bool, default: True - If set to False, then empty pdf files will be deleted automatically - when closed. - - metadata : dict, optional - Information dictionary object (see PDF reference section 10.2.1 - 'Document Information Dictionary'), e.g.: - ``{'Creator': 'My software', 'Author': 'Me', 'Title': 'Awesome'}``. - - The standard keys are 'Title', 'Author', 'Subject', 'Keywords', - 'Creator', 'Producer', 'CreationDate', 'ModDate', and - 'Trapped'. Values have been predefined for 'Creator', 'Producer' - and 'CreationDate'. They can be removed by setting them to `None`. - - Note that some versions of LaTeX engines may ignore the 'Producer' - key and set it to themselves. - """ - self._output_name = filename - self._n_figures = 0 - self.keep_empty = keep_empty - self._metadata = (metadata or {}).copy() - self._info_dict = _create_pdf_info_dict('pgf', self._metadata) - self._file = BytesIO() - - def _write_header(self, width_inches, height_inches): - pdfinfo = ','.join( - _metadata_to_str(k, v) for k, v in self._info_dict.items()) - latex_header = "\n".join([ - r"\documentclass[12pt]{article}", - r"\usepackage[pdfinfo={%s}]{hyperref}" % pdfinfo, - r"\usepackage[papersize={%fin,%fin}, margin=0in]{geometry}" - % (width_inches, height_inches), - r"\usepackage{pgf}", - _get_preamble(), - r"\setlength{\parindent}{0pt}", - r"\begin{document}%", - ]) - self._file.write(latex_header.encode('utf-8')) - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.close() - - def close(self): - """ - Finalize this object, running LaTeX in a temporary directory - and moving the final pdf file to *filename*. - """ - self._file.write(rb'\end{document}\n') - if self._n_figures > 0: - self._run_latex() - elif self.keep_empty: - open(self._output_name, 'wb').close() - self._file.close() - - def _run_latex(self): - texcommand = mpl.rcParams["pgf.texsystem"] - with TemporaryDirectory() as tmpdir: - tex_source = pathlib.Path(tmpdir, "pdf_pages.tex") - tex_source.write_bytes(self._file.getvalue()) - cbook._check_and_log_subprocess( - [texcommand, "-interaction=nonstopmode", "-halt-on-error", - tex_source], - _log, cwd=tmpdir) - shutil.move(tex_source.with_suffix(".pdf"), self._output_name) - - def savefig(self, figure=None, **kwargs): - """ - Save a `.Figure` to this file as a new page. - - Any other keyword arguments are passed to `~.Figure.savefig`. - - Parameters - ---------- - figure : `.Figure` or int, default: the active figure - The figure, or index of the figure, that is saved to the file. - """ - if not isinstance(figure, Figure): - if figure is None: - manager = Gcf.get_active() - else: - manager = Gcf.get_fig_manager(figure) - if manager is None: - raise ValueError("No figure {}".format(figure)) - figure = manager.canvas.figure - - try: - orig_canvas = figure.canvas - figure.canvas = FigureCanvasPgf(figure) - - width, height = figure.get_size_inches() - if self._n_figures == 0: - self._write_header(width, height) - else: - # \pdfpagewidth and \pdfpageheight exist on pdftex, xetex, and - # luatex<0.85; they were renamed to \pagewidth and \pageheight - # on luatex>=0.85. - self._file.write( - br'\newpage' - br'\ifdefined\pdfpagewidth\pdfpagewidth' - br'\else\pagewidth\fi=%ain' - br'\ifdefined\pdfpageheight\pdfpageheight' - br'\else\pageheight\fi=%ain' - b'%%\n' % (width, height) - ) - - figure.savefig(self._file, format="pgf", **kwargs) - self._n_figures += 1 - finally: - figure.canvas = orig_canvas - - def get_pagecount(self): - """Return the current number of pages in the multipage pdf file.""" - return self._n_figures diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_ps.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_ps.py deleted file mode 100644 index 75ed4ff..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_ps.py +++ /dev/null @@ -1,1362 +0,0 @@ -""" -A PostScript backend, which can produce both PostScript .ps and .eps. -""" - -import codecs -import datetime -from enum import Enum -import functools -from io import StringIO -import itertools -import logging -import os -import pathlib -import re -import shutil -from tempfile import TemporaryDirectory -import time - -import numpy as np - -import matplotlib as mpl -from matplotlib import _api, cbook, _path, _text_helpers -from matplotlib._afm import AFM -from matplotlib.backend_bases import ( - _Backend, FigureCanvasBase, FigureManagerBase, RendererBase) -from matplotlib.cbook import is_writable_file_like, file_requires_unicode -from matplotlib.font_manager import get_font -from matplotlib.ft2font import LOAD_NO_SCALE, FT2Font -from matplotlib._ttconv import convert_ttf_to_ps -from matplotlib._mathtext_data import uni2type1 -from matplotlib.path import Path -from matplotlib.texmanager import TexManager -from matplotlib.transforms import Affine2D -from matplotlib.backends.backend_mixed import MixedModeRenderer -from . import _backend_pdf_ps - - -_log = logging.getLogger(__name__) -debugPS = False - - -@_api.deprecated("3.7") -class PsBackendHelper: - def __init__(self): - self._cached = {} - - -@_api.caching_module_getattr -class __getattr__: - # module-level deprecations - ps_backend_helper = _api.deprecated("3.7", obj_type="")( - property(lambda self: PsBackendHelper())) - - -papersize = {'letter': (8.5, 11), - 'legal': (8.5, 14), - 'ledger': (11, 17), - 'a0': (33.11, 46.81), - 'a1': (23.39, 33.11), - 'a2': (16.54, 23.39), - 'a3': (11.69, 16.54), - 'a4': (8.27, 11.69), - 'a5': (5.83, 8.27), - 'a6': (4.13, 5.83), - 'a7': (2.91, 4.13), - 'a8': (2.05, 2.91), - 'a9': (1.46, 2.05), - 'a10': (1.02, 1.46), - 'b0': (40.55, 57.32), - 'b1': (28.66, 40.55), - 'b2': (20.27, 28.66), - 'b3': (14.33, 20.27), - 'b4': (10.11, 14.33), - 'b5': (7.16, 10.11), - 'b6': (5.04, 7.16), - 'b7': (3.58, 5.04), - 'b8': (2.51, 3.58), - 'b9': (1.76, 2.51), - 'b10': (1.26, 1.76)} - - -def _get_papertype(w, h): - for key, (pw, ph) in sorted(papersize.items(), reverse=True): - if key.startswith('l'): - continue - if w < pw and h < ph: - return key - return 'a0' - - -def _nums_to_str(*args): - return " ".join(f"{arg:1.3f}".rstrip("0").rstrip(".") for arg in args) - - -@_api.deprecated("3.6", alternative="a vendored copy of this function") -def quote_ps_string(s): - """ - Quote dangerous characters of S for use in a PostScript string constant. - """ - s = s.replace(b"\\", b"\\\\") - s = s.replace(b"(", b"\\(") - s = s.replace(b")", b"\\)") - s = s.replace(b"'", b"\\251") - s = s.replace(b"`", b"\\301") - s = re.sub(br"[^ -~\n]", lambda x: br"\%03o" % ord(x.group()), s) - return s.decode('ascii') - - -def _move_path_to_path_or_stream(src, dst): - """ - Move the contents of file at *src* to path-or-filelike *dst*. - - If *dst* is a path, the metadata of *src* are *not* copied. - """ - if is_writable_file_like(dst): - fh = (open(src, 'r', encoding='latin-1') - if file_requires_unicode(dst) - else open(src, 'rb')) - with fh: - shutil.copyfileobj(fh, dst) - else: - shutil.move(src, dst, copy_function=shutil.copyfile) - - -def _font_to_ps_type3(font_path, chars): - """ - Subset *chars* from the font at *font_path* into a Type 3 font. - - Parameters - ---------- - font_path : path-like - Path to the font to be subsetted. - chars : str - The characters to include in the subsetted font. - - Returns - ------- - str - The string representation of a Type 3 font, which can be included - verbatim into a PostScript file. - """ - font = get_font(font_path, hinting_factor=1) - glyph_ids = [font.get_char_index(c) for c in chars] - - preamble = """\ -%!PS-Adobe-3.0 Resource-Font -%%Creator: Converted from TrueType to Type 3 by Matplotlib. -10 dict begin -/FontName /{font_name} def -/PaintType 0 def -/FontMatrix [{inv_units_per_em} 0 0 {inv_units_per_em} 0 0] def -/FontBBox [{bbox}] def -/FontType 3 def -/Encoding [{encoding}] def -/CharStrings {num_glyphs} dict dup begin -/.notdef 0 def -""".format(font_name=font.postscript_name, - inv_units_per_em=1 / font.units_per_EM, - bbox=" ".join(map(str, font.bbox)), - encoding=" ".join("/{}".format(font.get_glyph_name(glyph_id)) - for glyph_id in glyph_ids), - num_glyphs=len(glyph_ids) + 1) - postamble = """ -end readonly def - -/BuildGlyph { - exch begin - CharStrings exch - 2 copy known not {pop /.notdef} if - true 3 1 roll get exec - end -} _d - -/BuildChar { - 1 index /Encoding get exch get - 1 index /BuildGlyph get exec -} _d - -FontName currentdict end definefont pop -""" - - entries = [] - for glyph_id in glyph_ids: - g = font.load_glyph(glyph_id, LOAD_NO_SCALE) - v, c = font.get_path() - entries.append( - "/%(name)s{%(bbox)s sc\n" % { - "name": font.get_glyph_name(glyph_id), - "bbox": " ".join(map(str, [g.horiAdvance, 0, *g.bbox])), - } - + _path.convert_to_string( - # Convert back to TrueType's internal units (1/64's). - # (Other dimensions are already in these units.) - Path(v * 64, c), None, None, False, None, 0, - # No code for quad Beziers triggers auto-conversion to cubics. - # Drop intermediate closepolys (relying on the outline - # decomposer always explicitly moving to the closing point - # first). - [b"m", b"l", b"", b"c", b""], True).decode("ascii") - + "ce} _d" - ) - - return preamble + "\n".join(entries) + postamble - - -def _font_to_ps_type42(font_path, chars, fh): - """ - Subset *chars* from the font at *font_path* into a Type 42 font at *fh*. - - Parameters - ---------- - font_path : path-like - Path to the font to be subsetted. - chars : str - The characters to include in the subsetted font. - fh : file-like - Where to write the font. - """ - subset_str = ''.join(chr(c) for c in chars) - _log.debug("SUBSET %s characters: %s", font_path, subset_str) - try: - fontdata = _backend_pdf_ps.get_glyphs_subset(font_path, subset_str) - _log.debug("SUBSET %s %d -> %d", font_path, os.stat(font_path).st_size, - fontdata.getbuffer().nbytes) - - # Give ttconv a subsetted font along with updated glyph_ids. - font = FT2Font(fontdata) - glyph_ids = [font.get_char_index(c) for c in chars] - with TemporaryDirectory() as tmpdir: - tmpfile = os.path.join(tmpdir, "tmp.ttf") - - with open(tmpfile, 'wb') as tmp: - tmp.write(fontdata.getvalue()) - - # TODO: allow convert_ttf_to_ps to input file objects (BytesIO) - convert_ttf_to_ps(os.fsencode(tmpfile), fh, 42, glyph_ids) - except RuntimeError: - _log.warning( - "The PostScript backend does not currently " - "support the selected font.") - raise - - -def _log_if_debug_on(meth): - """ - Wrap `RendererPS` method *meth* to emit a PS comment with the method name, - if the global flag `debugPS` is set. - """ - @functools.wraps(meth) - def wrapper(self, *args, **kwargs): - if debugPS: - self._pswriter.write(f"% {meth.__name__}\n") - return meth(self, *args, **kwargs) - - return wrapper - - -class RendererPS(_backend_pdf_ps.RendererPDFPSBase): - """ - The renderer handles all the drawing primitives using a graphics - context instance that controls the colors/styles. - """ - - _afm_font_dir = cbook._get_data_path("fonts/afm") - _use_afm_rc_name = "ps.useafm" - - def __init__(self, width, height, pswriter, imagedpi=72): - # Although postscript itself is dpi independent, we need to inform the - # image code about a requested dpi to generate high resolution images - # and them scale them before embedding them. - super().__init__(width, height) - self._pswriter = pswriter - if mpl.rcParams['text.usetex']: - self.textcnt = 0 - self.psfrag = [] - self.imagedpi = imagedpi - - # current renderer state (None=uninitialised) - self.color = None - self.linewidth = None - self.linejoin = None - self.linecap = None - self.linedash = None - self.fontname = None - self.fontsize = None - self._hatches = {} - self.image_magnification = imagedpi / 72 - self._clip_paths = {} - self._path_collection_id = 0 - - self._character_tracker = _backend_pdf_ps.CharacterTracker() - self._logwarn_once = functools.lru_cache(None)(_log.warning) - - def _is_transparent(self, rgb_or_rgba): - if rgb_or_rgba is None: - return True # Consistent with rgbFace semantics. - elif len(rgb_or_rgba) == 4: - if rgb_or_rgba[3] == 0: - return True - if rgb_or_rgba[3] != 1: - self._logwarn_once( - "The PostScript backend does not support transparency; " - "partially transparent artists will be rendered opaque.") - return False - else: # len() == 3. - return False - - def set_color(self, r, g, b, store=True): - if (r, g, b) != self.color: - self._pswriter.write(f"{r:1.3f} setgray\n" - if r == g == b else - f"{r:1.3f} {g:1.3f} {b:1.3f} setrgbcolor\n") - if store: - self.color = (r, g, b) - - def set_linewidth(self, linewidth, store=True): - linewidth = float(linewidth) - if linewidth != self.linewidth: - self._pswriter.write("%1.3f setlinewidth\n" % linewidth) - if store: - self.linewidth = linewidth - - @staticmethod - def _linejoin_cmd(linejoin): - # Support for directly passing integer values is for backcompat. - linejoin = {'miter': 0, 'round': 1, 'bevel': 2, 0: 0, 1: 1, 2: 2}[ - linejoin] - return f"{linejoin:d} setlinejoin\n" - - def set_linejoin(self, linejoin, store=True): - if linejoin != self.linejoin: - self._pswriter.write(self._linejoin_cmd(linejoin)) - if store: - self.linejoin = linejoin - - @staticmethod - def _linecap_cmd(linecap): - # Support for directly passing integer values is for backcompat. - linecap = {'butt': 0, 'round': 1, 'projecting': 2, 0: 0, 1: 1, 2: 2}[ - linecap] - return f"{linecap:d} setlinecap\n" - - def set_linecap(self, linecap, store=True): - if linecap != self.linecap: - self._pswriter.write(self._linecap_cmd(linecap)) - if store: - self.linecap = linecap - - def set_linedash(self, offset, seq, store=True): - if self.linedash is not None: - oldo, oldseq = self.linedash - if np.array_equal(seq, oldseq) and oldo == offset: - return - - self._pswriter.write(f"[{_nums_to_str(*seq)}]" - f" {_nums_to_str(offset)} setdash\n" - if seq is not None and len(seq) else - "[] 0 setdash\n") - if store: - self.linedash = (offset, seq) - - def set_font(self, fontname, fontsize, store=True): - if (fontname, fontsize) != (self.fontname, self.fontsize): - self._pswriter.write(f"/{fontname} {fontsize:1.3f} selectfont\n") - if store: - self.fontname = fontname - self.fontsize = fontsize - - def create_hatch(self, hatch): - sidelen = 72 - if hatch in self._hatches: - return self._hatches[hatch] - name = 'H%d' % len(self._hatches) - linewidth = mpl.rcParams['hatch.linewidth'] - pageheight = self.height * 72 - self._pswriter.write(f"""\ - << /PatternType 1 - /PaintType 2 - /TilingType 2 - /BBox[0 0 {sidelen:d} {sidelen:d}] - /XStep {sidelen:d} - /YStep {sidelen:d} - - /PaintProc {{ - pop - {linewidth:g} setlinewidth -{self._convert_path( - Path.hatch(hatch), Affine2D().scale(sidelen), simplify=False)} - gsave - fill - grestore - stroke - }} bind - >> - matrix - 0 {pageheight:g} translate - makepattern - /{name} exch def -""") - self._hatches[hatch] = name - return name - - def get_image_magnification(self): - """ - Get the factor by which to magnify images passed to draw_image. - Allows a backend to have images at a different resolution to other - artists. - """ - return self.image_magnification - - def _convert_path(self, path, transform, clip=False, simplify=None): - if clip: - clip = (0.0, 0.0, self.width * 72.0, self.height * 72.0) - else: - clip = None - return _path.convert_to_string( - path, transform, clip, simplify, None, - 6, [b"m", b"l", b"", b"c", b"cl"], True).decode("ascii") - - def _get_clip_cmd(self, gc): - clip = [] - rect = gc.get_clip_rectangle() - if rect is not None: - clip.append("%s clipbox\n" % _nums_to_str(*rect.size, *rect.p0)) - path, trf = gc.get_clip_path() - if path is not None: - key = (path, id(trf)) - custom_clip_cmd = self._clip_paths.get(key) - if custom_clip_cmd is None: - custom_clip_cmd = "c%d" % len(self._clip_paths) - self._pswriter.write(f"""\ -/{custom_clip_cmd} {{ -{self._convert_path(path, trf, simplify=False)} -clip -newpath -}} bind def -""") - self._clip_paths[key] = custom_clip_cmd - clip.append(f"{custom_clip_cmd}\n") - return "".join(clip) - - @_log_if_debug_on - def draw_image(self, gc, x, y, im, transform=None): - # docstring inherited - - h, w = im.shape[:2] - imagecmd = "false 3 colorimage" - data = im[::-1, :, :3] # Vertically flipped rgb values. - hexdata = data.tobytes().hex("\n", -64) # Linewrap to 128 chars. - - if transform is None: - matrix = "1 0 0 1 0 0" - xscale = w / self.image_magnification - yscale = h / self.image_magnification - else: - matrix = " ".join(map(str, transform.frozen().to_values())) - xscale = 1.0 - yscale = 1.0 - - self._pswriter.write(f"""\ -gsave -{self._get_clip_cmd(gc)} -{x:g} {y:g} translate -[{matrix}] concat -{xscale:g} {yscale:g} scale -/DataString {w:d} string def -{w:d} {h:d} 8 [ {w:d} 0 0 -{h:d} 0 {h:d} ] -{{ -currentfile DataString readhexstring pop -}} bind {imagecmd} -{hexdata} -grestore -""") - - @_log_if_debug_on - def draw_path(self, gc, path, transform, rgbFace=None): - # docstring inherited - clip = rgbFace is None and gc.get_hatch_path() is None - simplify = path.should_simplify and clip - ps = self._convert_path(path, transform, clip=clip, simplify=simplify) - self._draw_ps(ps, gc, rgbFace) - - @_log_if_debug_on - def draw_markers( - self, gc, marker_path, marker_trans, path, trans, rgbFace=None): - # docstring inherited - - ps_color = ( - None - if self._is_transparent(rgbFace) - else '%1.3f setgray' % rgbFace[0] - if rgbFace[0] == rgbFace[1] == rgbFace[2] - else '%1.3f %1.3f %1.3f setrgbcolor' % rgbFace[:3]) - - # construct the generic marker command: - - # don't want the translate to be global - ps_cmd = ['/o {', 'gsave', 'newpath', 'translate'] - - lw = gc.get_linewidth() - alpha = (gc.get_alpha() - if gc.get_forced_alpha() or len(gc.get_rgb()) == 3 - else gc.get_rgb()[3]) - stroke = lw > 0 and alpha > 0 - if stroke: - ps_cmd.append('%.1f setlinewidth' % lw) - ps_cmd.append(self._linejoin_cmd(gc.get_joinstyle())) - ps_cmd.append(self._linecap_cmd(gc.get_capstyle())) - - ps_cmd.append(self._convert_path(marker_path, marker_trans, - simplify=False)) - - if rgbFace: - if stroke: - ps_cmd.append('gsave') - if ps_color: - ps_cmd.extend([ps_color, 'fill']) - if stroke: - ps_cmd.append('grestore') - - if stroke: - ps_cmd.append('stroke') - ps_cmd.extend(['grestore', '} bind def']) - - for vertices, code in path.iter_segments( - trans, - clip=(0, 0, self.width*72, self.height*72), - simplify=False): - if len(vertices): - x, y = vertices[-2:] - ps_cmd.append("%g %g o" % (x, y)) - - ps = '\n'.join(ps_cmd) - self._draw_ps(ps, gc, rgbFace, fill=False, stroke=False) - - @_log_if_debug_on - def draw_path_collection(self, gc, master_transform, paths, all_transforms, - offsets, offset_trans, facecolors, edgecolors, - linewidths, linestyles, antialiaseds, urls, - offset_position): - # Is the optimization worth it? Rough calculation: - # cost of emitting a path in-line is - # (len_path + 2) * uses_per_path - # cost of definition+use is - # (len_path + 3) + 3 * uses_per_path - len_path = len(paths[0].vertices) if len(paths) > 0 else 0 - uses_per_path = self._iter_collection_uses_per_path( - paths, all_transforms, offsets, facecolors, edgecolors) - should_do_optimization = \ - len_path + 3 * uses_per_path + 3 < (len_path + 2) * uses_per_path - if not should_do_optimization: - return RendererBase.draw_path_collection( - self, gc, master_transform, paths, all_transforms, - offsets, offset_trans, facecolors, edgecolors, - linewidths, linestyles, antialiaseds, urls, - offset_position) - - path_codes = [] - for i, (path, transform) in enumerate(self._iter_collection_raw_paths( - master_transform, paths, all_transforms)): - name = 'p%d_%d' % (self._path_collection_id, i) - path_bytes = self._convert_path(path, transform, simplify=False) - self._pswriter.write(f"""\ -/{name} {{ -newpath -translate -{path_bytes} -}} bind def -""") - path_codes.append(name) - - for xo, yo, path_id, gc0, rgbFace in self._iter_collection( - gc, path_codes, offsets, offset_trans, - facecolors, edgecolors, linewidths, linestyles, - antialiaseds, urls, offset_position): - ps = "%g %g %s" % (xo, yo, path_id) - self._draw_ps(ps, gc0, rgbFace) - - self._path_collection_id += 1 - - @_log_if_debug_on - def draw_tex(self, gc, x, y, s, prop, angle, *, mtext=None): - # docstring inherited - if self._is_transparent(gc.get_rgb()): - return # Special handling for fully transparent. - - if not hasattr(self, "psfrag"): - self._logwarn_once( - "The PS backend determines usetex status solely based on " - "rcParams['text.usetex'] and does not support having " - "usetex=True only for some elements; this element will thus " - "be rendered as if usetex=False.") - self.draw_text(gc, x, y, s, prop, angle, False, mtext) - return - - w, h, bl = self.get_text_width_height_descent(s, prop, ismath="TeX") - fontsize = prop.get_size_in_points() - thetext = 'psmarker%d' % self.textcnt - color = '%1.3f,%1.3f,%1.3f' % gc.get_rgb()[:3] - fontcmd = {'sans-serif': r'{\sffamily %s}', - 'monospace': r'{\ttfamily %s}'}.get( - mpl.rcParams['font.family'][0], r'{\rmfamily %s}') - s = fontcmd % s - tex = r'\color[rgb]{%s} %s' % (color, s) - - # Stick to the bottom alignment. - pos = _nums_to_str(x, y-bl) - self.psfrag.append( - r'\psfrag{%s}[bl][bl][1][%f]{\fontsize{%f}{%f}%s}' % ( - thetext, angle, fontsize, fontsize*1.25, tex)) - - self._pswriter.write(f"""\ -gsave -{pos} moveto -({thetext}) -show -grestore -""") - self.textcnt += 1 - - @_log_if_debug_on - def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): - # docstring inherited - - if self._is_transparent(gc.get_rgb()): - return # Special handling for fully transparent. - - if ismath == 'TeX': - return self.draw_tex(gc, x, y, s, prop, angle) - - if ismath: - return self.draw_mathtext(gc, x, y, s, prop, angle) - - stream = [] # list of (ps_name, x, char_name) - - if mpl.rcParams['ps.useafm']: - font = self._get_font_afm(prop) - ps_name = (font.postscript_name.encode("ascii", "replace") - .decode("ascii")) - scale = 0.001 * prop.get_size_in_points() - thisx = 0 - last_name = None # kerns returns 0 for None. - for c in s: - name = uni2type1.get(ord(c), f"uni{ord(c):04X}") - try: - width = font.get_width_from_char_name(name) - except KeyError: - name = 'question' - width = font.get_width_char('?') - kern = font.get_kern_dist_from_name(last_name, name) - last_name = name - thisx += kern * scale - stream.append((ps_name, thisx, name)) - thisx += width * scale - - else: - font = self._get_font_ttf(prop) - self._character_tracker.track(font, s) - for item in _text_helpers.layout(s, font): - ps_name = (item.ft_object.postscript_name - .encode("ascii", "replace").decode("ascii")) - glyph_name = item.ft_object.get_glyph_name(item.glyph_idx) - stream.append((ps_name, item.x, glyph_name)) - self.set_color(*gc.get_rgb()) - - for ps_name, group in itertools. \ - groupby(stream, lambda entry: entry[0]): - self.set_font(ps_name, prop.get_size_in_points(), False) - thetext = "\n".join(f"{x:g} 0 m /{name:s} glyphshow" - for _, x, name in group) - self._pswriter.write(f"""\ -gsave -{self._get_clip_cmd(gc)} -{x:g} {y:g} translate -{angle:g} rotate -{thetext} -grestore -""") - - @_log_if_debug_on - def draw_mathtext(self, gc, x, y, s, prop, angle): - """Draw the math text using matplotlib.mathtext.""" - width, height, descent, glyphs, rects = \ - self._text2path.mathtext_parser.parse(s, 72, prop) - self.set_color(*gc.get_rgb()) - self._pswriter.write( - f"gsave\n" - f"{x:g} {y:g} translate\n" - f"{angle:g} rotate\n") - lastfont = None - for font, fontsize, num, ox, oy in glyphs: - self._character_tracker.track_glyph(font, num) - if (font.postscript_name, fontsize) != lastfont: - lastfont = font.postscript_name, fontsize - self._pswriter.write( - f"/{font.postscript_name} {fontsize} selectfont\n") - glyph_name = ( - font.get_name_char(chr(num)) if isinstance(font, AFM) else - font.get_glyph_name(font.get_char_index(num))) - self._pswriter.write( - f"{ox:g} {oy:g} moveto\n" - f"/{glyph_name} glyphshow\n") - for ox, oy, w, h in rects: - self._pswriter.write(f"{ox} {oy} {w} {h} rectfill\n") - self._pswriter.write("grestore\n") - - @_log_if_debug_on - def draw_gouraud_triangle(self, gc, points, colors, trans): - self.draw_gouraud_triangles(gc, points.reshape((1, 3, 2)), - colors.reshape((1, 3, 4)), trans) - - @_log_if_debug_on - def draw_gouraud_triangles(self, gc, points, colors, trans): - assert len(points) == len(colors) - assert points.ndim == 3 - assert points.shape[1] == 3 - assert points.shape[2] == 2 - assert colors.ndim == 3 - assert colors.shape[1] == 3 - assert colors.shape[2] == 4 - - shape = points.shape - flat_points = points.reshape((shape[0] * shape[1], 2)) - flat_points = trans.transform(flat_points) - flat_colors = colors.reshape((shape[0] * shape[1], 4)) - points_min = np.min(flat_points, axis=0) - (1 << 12) - points_max = np.max(flat_points, axis=0) + (1 << 12) - factor = np.ceil((2 ** 32 - 1) / (points_max - points_min)) - - xmin, ymin = points_min - xmax, ymax = points_max - - data = np.empty( - shape[0] * shape[1], - dtype=[('flags', 'u1'), ('points', '2>u4'), ('colors', '3u1')]) - data['flags'] = 0 - data['points'] = (flat_points - points_min) * factor - data['colors'] = flat_colors[:, :3] * 255.0 - hexdata = data.tobytes().hex("\n", -64) # Linewrap to 128 chars. - - self._pswriter.write(f"""\ -gsave -<< /ShadingType 4 - /ColorSpace [/DeviceRGB] - /BitsPerCoordinate 32 - /BitsPerComponent 8 - /BitsPerFlag 8 - /AntiAlias true - /Decode [ {xmin:g} {xmax:g} {ymin:g} {ymax:g} 0 1 0 1 0 1 ] - /DataSource < -{hexdata} -> ->> -shfill -grestore -""") - - def _draw_ps(self, ps, gc, rgbFace, *, fill=True, stroke=True): - """ - Emit the PostScript snippet *ps* with all the attributes from *gc* - applied. *ps* must consist of PostScript commands to construct a path. - - The *fill* and/or *stroke* kwargs can be set to False if the *ps* - string already includes filling and/or stroking, in which case - `_draw_ps` is just supplying properties and clipping. - """ - write = self._pswriter.write - mightstroke = (gc.get_linewidth() > 0 - and not self._is_transparent(gc.get_rgb())) - if not mightstroke: - stroke = False - if self._is_transparent(rgbFace): - fill = False - hatch = gc.get_hatch() - - if mightstroke: - self.set_linewidth(gc.get_linewidth()) - self.set_linejoin(gc.get_joinstyle()) - self.set_linecap(gc.get_capstyle()) - self.set_linedash(*gc.get_dashes()) - if mightstroke or hatch: - self.set_color(*gc.get_rgb()[:3]) - write('gsave\n') - - write(self._get_clip_cmd(gc)) - - write(ps.strip()) - write("\n") - - if fill: - if stroke or hatch: - write("gsave\n") - self.set_color(*rgbFace[:3], store=False) - write("fill\n") - if stroke or hatch: - write("grestore\n") - - if hatch: - hatch_name = self.create_hatch(hatch) - write("gsave\n") - write("%f %f %f " % gc.get_hatch_color()[:3]) - write("%s setpattern fill grestore\n" % hatch_name) - - if stroke: - write("stroke\n") - - write("grestore\n") - - -class _Orientation(Enum): - portrait, landscape = range(2) - - def swap_if_landscape(self, shape): - return shape[::-1] if self.name == "landscape" else shape - - -class FigureCanvasPS(FigureCanvasBase): - fixed_dpi = 72 - filetypes = {'ps': 'Postscript', - 'eps': 'Encapsulated Postscript'} - - def get_default_filetype(self): - return 'ps' - - def _print_ps( - self, fmt, outfile, *, - metadata=None, papertype=None, orientation='portrait', - bbox_inches_restore=None, **kwargs): - - dpi = self.figure.dpi - self.figure.dpi = 72 # Override the dpi kwarg - - dsc_comments = {} - if isinstance(outfile, (str, os.PathLike)): - filename = pathlib.Path(outfile).name - dsc_comments["Title"] = \ - filename.encode("ascii", "replace").decode("ascii") - dsc_comments["Creator"] = (metadata or {}).get( - "Creator", - f"Matplotlib v{mpl.__version__}, https://matplotlib.org/") - # See https://reproducible-builds.org/specs/source-date-epoch/ - source_date_epoch = os.getenv("SOURCE_DATE_EPOCH") - dsc_comments["CreationDate"] = ( - datetime.datetime.fromtimestamp( - int(source_date_epoch), - datetime.timezone.utc).strftime("%a %b %d %H:%M:%S %Y") - if source_date_epoch - else time.ctime()) - dsc_comments = "\n".join( - f"%%{k}: {v}" for k, v in dsc_comments.items()) - - if papertype is None: - papertype = mpl.rcParams['ps.papersize'] - papertype = papertype.lower() - _api.check_in_list(['auto', *papersize], papertype=papertype) - - orientation = _api.check_getitem( - _Orientation, orientation=orientation.lower()) - - printer = (self._print_figure_tex - if mpl.rcParams['text.usetex'] else - self._print_figure) - printer(fmt, outfile, dpi=dpi, dsc_comments=dsc_comments, - orientation=orientation, papertype=papertype, - bbox_inches_restore=bbox_inches_restore, **kwargs) - - def _print_figure( - self, fmt, outfile, *, - dpi, dsc_comments, orientation, papertype, - bbox_inches_restore=None): - """ - Render the figure to a filesystem path or a file-like object. - - Parameters are as for `.print_figure`, except that *dsc_comments* is a - string containing Document Structuring Convention comments, - generated from the *metadata* parameter to `.print_figure`. - """ - is_eps = fmt == 'eps' - if not (isinstance(outfile, (str, os.PathLike)) - or is_writable_file_like(outfile)): - raise ValueError("outfile must be a path or a file-like object") - - # find the appropriate papertype - width, height = self.figure.get_size_inches() - if papertype == 'auto': - papertype = _get_papertype( - *orientation.swap_if_landscape((width, height))) - paper_width, paper_height = orientation.swap_if_landscape( - papersize[papertype]) - - if mpl.rcParams['ps.usedistiller']: - # distillers improperly clip eps files if pagesize is too small - if width > paper_width or height > paper_height: - papertype = _get_papertype( - *orientation.swap_if_landscape((width, height))) - paper_width, paper_height = orientation.swap_if_landscape( - papersize[papertype]) - - # center the figure on the paper - xo = 72 * 0.5 * (paper_width - width) - yo = 72 * 0.5 * (paper_height - height) - - llx = xo - lly = yo - urx = llx + self.figure.bbox.width - ury = lly + self.figure.bbox.height - rotation = 0 - if orientation is _Orientation.landscape: - llx, lly, urx, ury = lly, llx, ury, urx - xo, yo = 72 * paper_height - yo, xo - rotation = 90 - bbox = (llx, lly, urx, ury) - - self._pswriter = StringIO() - - # mixed mode rendering - ps_renderer = RendererPS(width, height, self._pswriter, imagedpi=dpi) - renderer = MixedModeRenderer( - self.figure, width, height, dpi, ps_renderer, - bbox_inches_restore=bbox_inches_restore) - - self.figure.draw(renderer) - - def print_figure_impl(fh): - # write the PostScript headers - if is_eps: - print("%!PS-Adobe-3.0 EPSF-3.0", file=fh) - else: - print(f"%!PS-Adobe-3.0\n" - f"%%DocumentPaperSizes: {papertype}\n" - f"%%Pages: 1\n", - end="", file=fh) - print(f"{dsc_comments}\n" - f"%%Orientation: {orientation.name}\n" - f"{get_bbox_header(bbox)[0]}\n" - f"%%EndComments\n", - end="", file=fh) - - Ndict = len(psDefs) - print("%%BeginProlog", file=fh) - if not mpl.rcParams['ps.useafm']: - Ndict += len(ps_renderer._character_tracker.used) - print("/mpldict %d dict def" % Ndict, file=fh) - print("mpldict begin", file=fh) - print("\n".join(psDefs), file=fh) - if not mpl.rcParams['ps.useafm']: - for font_path, chars \ - in ps_renderer._character_tracker.used.items(): - if not chars: - continue - fonttype = mpl.rcParams['ps.fonttype'] - # Can't use more than 255 chars from a single Type 3 font. - if len(chars) > 255: - fonttype = 42 - fh.flush() - if fonttype == 3: - fh.write(_font_to_ps_type3(font_path, chars)) - else: # Type 42 only. - _font_to_ps_type42(font_path, chars, fh) - print("end", file=fh) - print("%%EndProlog", file=fh) - - if not is_eps: - print("%%Page: 1 1", file=fh) - print("mpldict begin", file=fh) - - print("%s translate" % _nums_to_str(xo, yo), file=fh) - if rotation: - print("%d rotate" % rotation, file=fh) - print("%s clipbox" % _nums_to_str(width*72, height*72, 0, 0), - file=fh) - - # write the figure - print(self._pswriter.getvalue(), file=fh) - - # write the trailer - print("end", file=fh) - print("showpage", file=fh) - if not is_eps: - print("%%EOF", file=fh) - fh.flush() - - if mpl.rcParams['ps.usedistiller']: - # We are going to use an external program to process the output. - # Write to a temporary file. - with TemporaryDirectory() as tmpdir: - tmpfile = os.path.join(tmpdir, "tmp.ps") - with open(tmpfile, 'w', encoding='latin-1') as fh: - print_figure_impl(fh) - if mpl.rcParams['ps.usedistiller'] == 'ghostscript': - _try_distill(gs_distill, - tmpfile, is_eps, ptype=papertype, bbox=bbox) - elif mpl.rcParams['ps.usedistiller'] == 'xpdf': - _try_distill(xpdf_distill, - tmpfile, is_eps, ptype=papertype, bbox=bbox) - _move_path_to_path_or_stream(tmpfile, outfile) - - else: # Write directly to outfile. - with cbook.open_file_cm(outfile, "w", encoding="latin-1") as file: - if not file_requires_unicode(file): - file = codecs.getwriter("latin-1")(file) - print_figure_impl(file) - - def _print_figure_tex( - self, fmt, outfile, *, - dpi, dsc_comments, orientation, papertype, - bbox_inches_restore=None): - """ - If :rc:`text.usetex` is True, a temporary pair of tex/eps files - are created to allow tex to manage the text layout via the PSFrags - package. These files are processed to yield the final ps or eps file. - - The rest of the behavior is as for `._print_figure`. - """ - is_eps = fmt == 'eps' - - width, height = self.figure.get_size_inches() - xo = 0 - yo = 0 - - llx = xo - lly = yo - urx = llx + self.figure.bbox.width - ury = lly + self.figure.bbox.height - bbox = (llx, lly, urx, ury) - - self._pswriter = StringIO() - - # mixed mode rendering - ps_renderer = RendererPS(width, height, self._pswriter, imagedpi=dpi) - renderer = MixedModeRenderer(self.figure, - width, height, dpi, ps_renderer, - bbox_inches_restore=bbox_inches_restore) - - self.figure.draw(renderer) - - # write to a temp file, we'll move it to outfile when done - with TemporaryDirectory() as tmpdir: - tmppath = pathlib.Path(tmpdir, "tmp.ps") - tmppath.write_text( - f"""\ -%!PS-Adobe-3.0 EPSF-3.0 -{dsc_comments} -{get_bbox_header(bbox)[0]} -%%EndComments -%%BeginProlog -/mpldict {len(psDefs)} dict def -mpldict begin -{"".join(psDefs)} -end -%%EndProlog -mpldict begin -{_nums_to_str(xo, yo)} translate -{_nums_to_str(width*72, height*72)} 0 0 clipbox -{self._pswriter.getvalue()} -end -showpage -""", - encoding="latin-1") - - if orientation is _Orientation.landscape: # now, ready to rotate - width, height = height, width - bbox = (lly, llx, ury, urx) - - # set the paper size to the figure size if is_eps. The - # resulting ps file has the given size with correct bounding - # box so that there is no need to call 'pstoeps' - if is_eps: - paper_width, paper_height = orientation.swap_if_landscape( - self.figure.get_size_inches()) - else: - if papertype == 'auto': - papertype = _get_papertype(width, height) - paper_width, paper_height = papersize[papertype] - - psfrag_rotated = _convert_psfrags( - tmppath, ps_renderer.psfrag, paper_width, paper_height, - orientation.name) - - if (mpl.rcParams['ps.usedistiller'] == 'ghostscript' - or mpl.rcParams['text.usetex']): - _try_distill(gs_distill, - tmppath, is_eps, ptype=papertype, bbox=bbox, - rotated=psfrag_rotated) - elif mpl.rcParams['ps.usedistiller'] == 'xpdf': - _try_distill(xpdf_distill, - tmppath, is_eps, ptype=papertype, bbox=bbox, - rotated=psfrag_rotated) - - _move_path_to_path_or_stream(tmppath, outfile) - - print_ps = functools.partialmethod(_print_ps, "ps") - print_eps = functools.partialmethod(_print_ps, "eps") - - def draw(self): - self.figure.draw_without_rendering() - return super().draw() - - -@_api.deprecated("3.6") -def convert_psfrags(tmpfile, psfrags, font_preamble, custom_preamble, - paper_width, paper_height, orientation): - return _convert_psfrags( - pathlib.Path(tmpfile), psfrags, paper_width, paper_height, orientation) - - -def _convert_psfrags(tmppath, psfrags, paper_width, paper_height, orientation): - """ - When we want to use the LaTeX backend with postscript, we write PSFrag tags - to a temporary postscript file, each one marking a position for LaTeX to - render some text. convert_psfrags generates a LaTeX document containing the - commands to convert those tags to text. LaTeX/dvips produces the postscript - file that includes the actual text. - """ - with mpl.rc_context({ - "text.latex.preamble": - mpl.rcParams["text.latex.preamble"] + - mpl.texmanager._usepackage_if_not_loaded("color") + - mpl.texmanager._usepackage_if_not_loaded("graphicx") + - mpl.texmanager._usepackage_if_not_loaded("psfrag") + - r"\geometry{papersize={%(width)sin,%(height)sin},margin=0in}" - % {"width": paper_width, "height": paper_height} - }): - dvifile = TexManager().make_dvi( - "\n" - r"\begin{figure}""\n" - r" \centering\leavevmode""\n" - r" %(psfrags)s""\n" - r" \includegraphics*[angle=%(angle)s]{%(epsfile)s}""\n" - r"\end{figure}" - % { - "psfrags": "\n".join(psfrags), - "angle": 90 if orientation == 'landscape' else 0, - "epsfile": tmppath.resolve().as_posix(), - }, - fontsize=10) # tex's default fontsize. - - with TemporaryDirectory() as tmpdir: - psfile = os.path.join(tmpdir, "tmp.ps") - cbook._check_and_log_subprocess( - ['dvips', '-q', '-R0', '-o', psfile, dvifile], _log) - shutil.move(psfile, tmppath) - - # check if the dvips created a ps in landscape paper. Somehow, - # above latex+dvips results in a ps file in a landscape mode for a - # certain figure sizes (e.g., 8.3in, 5.8in which is a5). And the - # bounding box of the final output got messed up. We check see if - # the generated ps file is in landscape and return this - # information. The return value is used in pstoeps step to recover - # the correct bounding box. 2010-06-05 JJL - with open(tmppath) as fh: - psfrag_rotated = "Landscape" in fh.read(1000) - return psfrag_rotated - - -def _try_distill(func, tmppath, *args, **kwargs): - try: - func(str(tmppath), *args, **kwargs) - except mpl.ExecutableNotFoundError as exc: - _log.warning("%s. Distillation step skipped.", exc) - - -def gs_distill(tmpfile, eps=False, ptype='letter', bbox=None, rotated=False): - """ - Use ghostscript's pswrite or epswrite device to distill a file. - This yields smaller files without illegal encapsulated postscript - operators. The output is low-level, converting text to outlines. - """ - - if eps: - paper_option = "-dEPSCrop" - else: - paper_option = "-sPAPERSIZE=%s" % ptype - - psfile = tmpfile + '.ps' - dpi = mpl.rcParams['ps.distiller.res'] - - cbook._check_and_log_subprocess( - [mpl._get_executable_info("gs").executable, - "-dBATCH", "-dNOPAUSE", "-r%d" % dpi, "-sDEVICE=ps2write", - paper_option, "-sOutputFile=%s" % psfile, tmpfile], - _log) - - os.remove(tmpfile) - shutil.move(psfile, tmpfile) - - # While it is best if above steps preserve the original bounding - # box, there seem to be cases when it is not. For those cases, - # the original bbox can be restored during the pstoeps step. - - if eps: - # For some versions of gs, above steps result in a ps file where the - # original bbox is no more correct. Do not adjust bbox for now. - pstoeps(tmpfile, bbox, rotated=rotated) - - -def xpdf_distill(tmpfile, eps=False, ptype='letter', bbox=None, rotated=False): - """ - Use ghostscript's ps2pdf and xpdf's/poppler's pdftops to distill a file. - This yields smaller files without illegal encapsulated postscript - operators. This distiller is preferred, generating high-level postscript - output that treats text as text. - """ - mpl._get_executable_info("gs") # Effectively checks for ps2pdf. - mpl._get_executable_info("pdftops") - - with TemporaryDirectory() as tmpdir: - tmppdf = pathlib.Path(tmpdir, "tmp.pdf") - tmpps = pathlib.Path(tmpdir, "tmp.ps") - # Pass options as `-foo#bar` instead of `-foo=bar` to keep Windows - # happy (https://ghostscript.com/doc/9.56.1/Use.htm#MS_Windows). - cbook._check_and_log_subprocess( - ["ps2pdf", - "-dAutoFilterColorImages#false", - "-dAutoFilterGrayImages#false", - "-sAutoRotatePages#None", - "-sGrayImageFilter#FlateEncode", - "-sColorImageFilter#FlateEncode", - "-dEPSCrop" if eps else "-sPAPERSIZE#%s" % ptype, - tmpfile, tmppdf], _log) - cbook._check_and_log_subprocess( - ["pdftops", "-paper", "match", "-level2", tmppdf, tmpps], _log) - shutil.move(tmpps, tmpfile) - if eps: - pstoeps(tmpfile) - - -def get_bbox_header(lbrt, rotated=False): - """ - Return a postscript header string for the given bbox lbrt=(l, b, r, t). - Optionally, return rotate command. - """ - - l, b, r, t = lbrt - if rotated: - rotate = "%.2f %.2f translate\n90 rotate" % (l+r, 0) - else: - rotate = "" - bbox_info = '%%%%BoundingBox: %d %d %d %d' % (l, b, np.ceil(r), np.ceil(t)) - hires_bbox_info = '%%%%HiResBoundingBox: %.6f %.6f %.6f %.6f' % ( - l, b, r, t) - - return '\n'.join([bbox_info, hires_bbox_info]), rotate - - -def pstoeps(tmpfile, bbox=None, rotated=False): - """ - Convert the postscript to encapsulated postscript. The bbox of - the eps file will be replaced with the given *bbox* argument. If - None, original bbox will be used. - """ - - # if rotated==True, the output eps file need to be rotated - if bbox: - bbox_info, rotate = get_bbox_header(bbox, rotated=rotated) - else: - bbox_info, rotate = None, None - - epsfile = tmpfile + '.eps' - with open(epsfile, 'wb') as epsh, open(tmpfile, 'rb') as tmph: - write = epsh.write - # Modify the header: - for line in tmph: - if line.startswith(b'%!PS'): - write(b"%!PS-Adobe-3.0 EPSF-3.0\n") - if bbox: - write(bbox_info.encode('ascii') + b'\n') - elif line.startswith(b'%%EndComments'): - write(line) - write(b'%%BeginProlog\n' - b'save\n' - b'countdictstack\n' - b'mark\n' - b'newpath\n' - b'/showpage {} def\n' - b'/setpagedevice {pop} def\n' - b'%%EndProlog\n' - b'%%Page 1 1\n') - if rotate: - write(rotate.encode('ascii') + b'\n') - break - elif bbox and line.startswith((b'%%Bound', b'%%HiResBound', - b'%%DocumentMedia', b'%%Pages')): - pass - else: - write(line) - # Now rewrite the rest of the file, and modify the trailer. - # This is done in a second loop such that the header of the embedded - # eps file is not modified. - for line in tmph: - if line.startswith(b'%%EOF'): - write(b'cleartomark\n' - b'countdictstack\n' - b'exch sub { end } repeat\n' - b'restore\n' - b'showpage\n' - b'%%EOF\n') - elif line.startswith(b'%%PageBoundingBox'): - pass - else: - write(line) - - os.remove(tmpfile) - shutil.move(epsfile, tmpfile) - - -FigureManagerPS = FigureManagerBase - - -# The following Python dictionary psDefs contains the entries for the -# PostScript dictionary mpldict. This dictionary implements most of -# the matplotlib primitives and some abbreviations. -# -# References: -# https://www.adobe.com/content/dam/acom/en/devnet/actionscript/articles/PLRM.pdf -# http://preserve.mactech.com/articles/mactech/Vol.09/09.04/PostscriptTutorial -# http://www.math.ubc.ca/people/faculty/cass/graphics/text/www/ -# - -# The usage comments use the notation of the operator summary -# in the PostScript Language reference manual. -psDefs = [ - # name proc *_d* - - # Note that this cannot be bound to /d, because when embedding a Type3 font - # we may want to define a "d" glyph using "/d{...} d" which would locally - # overwrite the definition. - "/_d { bind def } bind def", - # x y *m* - - "/m { moveto } _d", - # x y *l* - - "/l { lineto } _d", - # x y *r* - - "/r { rlineto } _d", - # x1 y1 x2 y2 x y *c* - - "/c { curveto } _d", - # *cl* - - "/cl { closepath } _d", - # *ce* - - "/ce { closepath eofill } _d", - # w h x y *box* - - """/box { - m - 1 index 0 r - 0 exch r - neg 0 r - cl - } _d""", - # w h x y *clipbox* - - """/clipbox { - box - clip - newpath - } _d""", - # wx wy llx lly urx ury *setcachedevice* - - "/sc { setcachedevice } _d", -] - - -@_Backend.export -class _BackendPS(_Backend): - backend_version = 'Level II' - FigureCanvas = FigureCanvasPS diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_qt.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_qt.py deleted file mode 100644 index 138fd30..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_qt.py +++ /dev/null @@ -1,1030 +0,0 @@ -import functools -import os -import sys -import traceback - -import matplotlib as mpl -from matplotlib import _api, backend_tools, cbook -from matplotlib._pylab_helpers import Gcf -from matplotlib.backend_bases import ( - _Backend, FigureCanvasBase, FigureManagerBase, NavigationToolbar2, - TimerBase, cursors, ToolContainerBase, MouseButton, - CloseEvent, KeyEvent, LocationEvent, MouseEvent, ResizeEvent) -import matplotlib.backends.qt_editor.figureoptions as figureoptions -from . import qt_compat -from .qt_compat import ( - QtCore, QtGui, QtWidgets, __version__, QT_API, - _enum, _to_int, _isdeleted, _maybe_allow_interrupt -) - - -# SPECIAL_KEYS are Qt::Key that do *not* return their Unicode name -# instead they have manually specified names. -SPECIAL_KEYS = { - _to_int(getattr(_enum("QtCore.Qt.Key"), k)): v for k, v in [ - ("Key_Escape", "escape"), - ("Key_Tab", "tab"), - ("Key_Backspace", "backspace"), - ("Key_Return", "enter"), - ("Key_Enter", "enter"), - ("Key_Insert", "insert"), - ("Key_Delete", "delete"), - ("Key_Pause", "pause"), - ("Key_SysReq", "sysreq"), - ("Key_Clear", "clear"), - ("Key_Home", "home"), - ("Key_End", "end"), - ("Key_Left", "left"), - ("Key_Up", "up"), - ("Key_Right", "right"), - ("Key_Down", "down"), - ("Key_PageUp", "pageup"), - ("Key_PageDown", "pagedown"), - ("Key_Shift", "shift"), - # In OSX, the control and super (aka cmd/apple) keys are switched. - ("Key_Control", "control" if sys.platform != "darwin" else "cmd"), - ("Key_Meta", "meta" if sys.platform != "darwin" else "control"), - ("Key_Alt", "alt"), - ("Key_CapsLock", "caps_lock"), - ("Key_F1", "f1"), - ("Key_F2", "f2"), - ("Key_F3", "f3"), - ("Key_F4", "f4"), - ("Key_F5", "f5"), - ("Key_F6", "f6"), - ("Key_F7", "f7"), - ("Key_F8", "f8"), - ("Key_F9", "f9"), - ("Key_F10", "f10"), - ("Key_F10", "f11"), - ("Key_F12", "f12"), - ("Key_Super_L", "super"), - ("Key_Super_R", "super"), - ] -} -# Define which modifier keys are collected on keyboard events. -# Elements are (Qt::KeyboardModifiers, Qt::Key) tuples. -# Order determines the modifier order (ctrl+alt+...) reported by Matplotlib. -_MODIFIER_KEYS = [ - (_to_int(getattr(_enum("QtCore.Qt.KeyboardModifier"), mod)), - _to_int(getattr(_enum("QtCore.Qt.Key"), key))) - for mod, key in [ - ("ControlModifier", "Key_Control"), - ("AltModifier", "Key_Alt"), - ("ShiftModifier", "Key_Shift"), - ("MetaModifier", "Key_Meta"), - ] -] -cursord = { - k: getattr(_enum("QtCore.Qt.CursorShape"), v) for k, v in [ - (cursors.MOVE, "SizeAllCursor"), - (cursors.HAND, "PointingHandCursor"), - (cursors.POINTER, "ArrowCursor"), - (cursors.SELECT_REGION, "CrossCursor"), - (cursors.WAIT, "WaitCursor"), - (cursors.RESIZE_HORIZONTAL, "SizeHorCursor"), - (cursors.RESIZE_VERTICAL, "SizeVerCursor"), - ] -} - - -@_api.caching_module_getattr -class __getattr__: - qApp = _api.deprecated( - "3.6", alternative="QtWidgets.QApplication.instance()")( - property(lambda self: QtWidgets.QApplication.instance())) - - -# lru_cache keeps a reference to the QApplication instance, keeping it from -# being GC'd. -@functools.lru_cache(1) -def _create_qApp(): - app = QtWidgets.QApplication.instance() - - # Create a new QApplication and configure it if none exists yet, as only - # one QApplication can exist at a time. - if app is None: - # display_is_valid returns False only if on Linux and neither X11 - # nor Wayland display can be opened. - if not mpl._c_internal_utils.display_is_valid(): - raise RuntimeError('Invalid DISPLAY variable') - - # Check to make sure a QApplication from a different major version - # of Qt is not instantiated in the process - if QT_API in {'PyQt6', 'PySide6'}: - other_bindings = ('PyQt5', 'PySide2') - qt_version = 6 - elif QT_API in {'PyQt5', 'PySide2'}: - other_bindings = ('PyQt6', 'PySide6') - qt_version = 5 - else: - raise RuntimeError("Should never be here") - - for binding in other_bindings: - mod = sys.modules.get(f'{binding}.QtWidgets') - if mod is not None and mod.QApplication.instance() is not None: - other_core = sys.modules.get(f'{binding}.QtCore') - _api.warn_external( - f'Matplotlib is using {QT_API} which wraps ' - f'{QtCore.qVersion()} however an instantiated ' - f'QApplication from {binding} which wraps ' - f'{other_core.qVersion()} exists. Mixing Qt major ' - 'versions may not work as expected.' - ) - break - if qt_version == 5: - try: - QtWidgets.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling) - except AttributeError: # Only for Qt>=5.6, <6. - pass - try: - QtWidgets.QApplication.setHighDpiScaleFactorRoundingPolicy( - QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough) - except AttributeError: # Only for Qt>=5.14. - pass - app = QtWidgets.QApplication(["matplotlib"]) - if sys.platform == "darwin": - image = str(cbook._get_data_path('images/matplotlib.svg')) - icon = QtGui.QIcon(image) - app.setWindowIcon(icon) - app.lastWindowClosed.connect(app.quit) - cbook._setup_new_guiapp() - - if qt_version == 5: - app.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps) - - return app - - -class TimerQT(TimerBase): - """Subclass of `.TimerBase` using QTimer events.""" - - def __init__(self, *args, **kwargs): - # Create a new timer and connect the timeout() signal to the - # _on_timer method. - self._timer = QtCore.QTimer() - self._timer.timeout.connect(self._on_timer) - super().__init__(*args, **kwargs) - - def __del__(self): - # The check for deletedness is needed to avoid an error at animation - # shutdown with PySide2. - if not _isdeleted(self._timer): - self._timer_stop() - - def _timer_set_single_shot(self): - self._timer.setSingleShot(self._single) - - def _timer_set_interval(self): - self._timer.setInterval(self._interval) - - def _timer_start(self): - self._timer.start() - - def _timer_stop(self): - self._timer.stop() - - -class FigureCanvasQT(FigureCanvasBase, QtWidgets.QWidget): - required_interactive_framework = "qt" - _timer_cls = TimerQT - manager_class = _api.classproperty(lambda cls: FigureManagerQT) - - buttond = { - getattr(_enum("QtCore.Qt.MouseButton"), k): v for k, v in [ - ("LeftButton", MouseButton.LEFT), - ("RightButton", MouseButton.RIGHT), - ("MiddleButton", MouseButton.MIDDLE), - ("XButton1", MouseButton.BACK), - ("XButton2", MouseButton.FORWARD), - ] - } - - def __init__(self, figure=None): - _create_qApp() - super().__init__(figure=figure) - - self._draw_pending = False - self._is_drawing = False - self._draw_rect_callback = lambda painter: None - self._in_resize_event = False - - self.setAttribute( - _enum("QtCore.Qt.WidgetAttribute").WA_OpaquePaintEvent) - self.setMouseTracking(True) - self.resize(*self.get_width_height()) - - palette = QtGui.QPalette(QtGui.QColor("white")) - self.setPalette(palette) - - def _update_pixel_ratio(self): - if self._set_device_pixel_ratio( - self.devicePixelRatioF() or 1): # rarely, devicePixelRatioF=0 - # The easiest way to resize the canvas is to emit a resizeEvent - # since we implement all the logic for resizing the canvas for - # that event. - event = QtGui.QResizeEvent(self.size(), self.size()) - self.resizeEvent(event) - - def _update_screen(self, screen): - # Handler for changes to a window's attached screen. - self._update_pixel_ratio() - if screen is not None: - screen.physicalDotsPerInchChanged.connect(self._update_pixel_ratio) - screen.logicalDotsPerInchChanged.connect(self._update_pixel_ratio) - - def showEvent(self, event): - # Set up correct pixel ratio, and connect to any signal changes for it, - # once the window is shown (and thus has these attributes). - window = self.window().windowHandle() - window.screenChanged.connect(self._update_screen) - self._update_screen(window.screen()) - - def set_cursor(self, cursor): - # docstring inherited - self.setCursor(_api.check_getitem(cursord, cursor=cursor)) - - def mouseEventCoords(self, pos=None): - """ - Calculate mouse coordinates in physical pixels. - - Qt uses logical pixels, but the figure is scaled to physical - pixels for rendering. Transform to physical pixels so that - all of the down-stream transforms work as expected. - - Also, the origin is different and needs to be corrected. - """ - if pos is None: - pos = self.mapFromGlobal(QtGui.QCursor.pos()) - elif hasattr(pos, "position"): # qt6 QtGui.QEvent - pos = pos.position() - elif hasattr(pos, "pos"): # qt5 QtCore.QEvent - pos = pos.pos() - # (otherwise, it's already a QPoint) - x = pos.x() - # flip y so y=0 is bottom of canvas - y = self.figure.bbox.height / self.device_pixel_ratio - pos.y() - return x * self.device_pixel_ratio, y * self.device_pixel_ratio - - def enterEvent(self, event): - # Force querying of the modifiers, as the cached modifier state can - # have been invalidated while the window was out of focus. - mods = QtWidgets.QApplication.instance().queryKeyboardModifiers() - LocationEvent("figure_enter_event", self, - *self.mouseEventCoords(event), - modifiers=self._mpl_modifiers(mods), - guiEvent=event)._process() - - def leaveEvent(self, event): - QtWidgets.QApplication.restoreOverrideCursor() - LocationEvent("figure_leave_event", self, - *self.mouseEventCoords(), - modifiers=self._mpl_modifiers(), - guiEvent=event)._process() - - def mousePressEvent(self, event): - button = self.buttond.get(event.button()) - if button is not None: - MouseEvent("button_press_event", self, - *self.mouseEventCoords(event), button, - modifiers=self._mpl_modifiers(), - guiEvent=event)._process() - - def mouseDoubleClickEvent(self, event): - button = self.buttond.get(event.button()) - if button is not None: - MouseEvent("button_press_event", self, - *self.mouseEventCoords(event), button, dblclick=True, - modifiers=self._mpl_modifiers(), - guiEvent=event)._process() - - def mouseMoveEvent(self, event): - MouseEvent("motion_notify_event", self, - *self.mouseEventCoords(event), - modifiers=self._mpl_modifiers(), - guiEvent=event)._process() - - def mouseReleaseEvent(self, event): - button = self.buttond.get(event.button()) - if button is not None: - MouseEvent("button_release_event", self, - *self.mouseEventCoords(event), button, - modifiers=self._mpl_modifiers(), - guiEvent=event)._process() - - def wheelEvent(self, event): - # from QWheelEvent::pixelDelta doc: pixelDelta is sometimes not - # provided (`isNull()`) and is unreliable on X11 ("xcb"). - if (event.pixelDelta().isNull() - or QtWidgets.QApplication.instance().platformName() == "xcb"): - steps = event.angleDelta().y() / 120 - else: - steps = event.pixelDelta().y() - if steps: - MouseEvent("scroll_event", self, - *self.mouseEventCoords(event), step=steps, - modifiers=self._mpl_modifiers(), - guiEvent=event)._process() - - def keyPressEvent(self, event): - key = self._get_key(event) - if key is not None: - KeyEvent("key_press_event", self, - key, *self.mouseEventCoords(), - guiEvent=event)._process() - - def keyReleaseEvent(self, event): - key = self._get_key(event) - if key is not None: - KeyEvent("key_release_event", self, - key, *self.mouseEventCoords(), - guiEvent=event)._process() - - def resizeEvent(self, event): - if self._in_resize_event: # Prevent PyQt6 recursion - return - self._in_resize_event = True - try: - w = event.size().width() * self.device_pixel_ratio - h = event.size().height() * self.device_pixel_ratio - dpival = self.figure.dpi - winch = w / dpival - hinch = h / dpival - self.figure.set_size_inches(winch, hinch, forward=False) - # pass back into Qt to let it finish - QtWidgets.QWidget.resizeEvent(self, event) - # emit our resize events - ResizeEvent("resize_event", self)._process() - self.draw_idle() - finally: - self._in_resize_event = False - - def sizeHint(self): - w, h = self.get_width_height() - return QtCore.QSize(w, h) - - def minumumSizeHint(self): - return QtCore.QSize(10, 10) - - @staticmethod - def _mpl_modifiers(modifiers=None, *, exclude=None): - if modifiers is None: - modifiers = QtWidgets.QApplication.instance().keyboardModifiers() - modifiers = _to_int(modifiers) - # get names of the pressed modifier keys - # 'control' is named 'control' when a standalone key, but 'ctrl' when a - # modifier - # bit twiddling to pick out modifier keys from modifiers bitmask, - # if exclude is a MODIFIER, it should not be duplicated in mods - return [SPECIAL_KEYS[key].replace('control', 'ctrl') - for mask, key in _MODIFIER_KEYS - if exclude != key and modifiers & mask] - - def _get_key(self, event): - event_key = event.key() - mods = self._mpl_modifiers(exclude=event_key) - try: - # for certain keys (enter, left, backspace, etc) use a word for the - # key, rather than Unicode - key = SPECIAL_KEYS[event_key] - except KeyError: - # Unicode defines code points up to 0x10ffff (sys.maxunicode) - # QT will use Key_Codes larger than that for keyboard keys that are - # not Unicode characters (like multimedia keys) - # skip these - # if you really want them, you should add them to SPECIAL_KEYS - if event_key > sys.maxunicode: - return None - - key = chr(event_key) - # qt delivers capitalized letters. fix capitalization - # note that capslock is ignored - if 'shift' in mods: - mods.remove('shift') - else: - key = key.lower() - - return '+'.join(mods + [key]) - - def flush_events(self): - # docstring inherited - QtWidgets.QApplication.instance().processEvents() - - def start_event_loop(self, timeout=0): - # docstring inherited - if hasattr(self, "_event_loop") and self._event_loop.isRunning(): - raise RuntimeError("Event loop already running") - self._event_loop = event_loop = QtCore.QEventLoop() - if timeout > 0: - _ = QtCore.QTimer.singleShot(int(timeout * 1000), event_loop.quit) - - with _maybe_allow_interrupt(event_loop): - qt_compat._exec(event_loop) - - def stop_event_loop(self, event=None): - # docstring inherited - if hasattr(self, "_event_loop"): - self._event_loop.quit() - - def draw(self): - """Render the figure, and queue a request for a Qt draw.""" - # The renderer draw is done here; delaying causes problems with code - # that uses the result of the draw() to update plot elements. - if self._is_drawing: - return - with cbook._setattr_cm(self, _is_drawing=True): - super().draw() - self.update() - - def draw_idle(self): - """Queue redraw of the Agg buffer and request Qt paintEvent.""" - # The Agg draw needs to be handled by the same thread Matplotlib - # modifies the scene graph from. Post Agg draw request to the - # current event loop in order to ensure thread affinity and to - # accumulate multiple draw requests from event handling. - # TODO: queued signal connection might be safer than singleShot - if not (getattr(self, '_draw_pending', False) or - getattr(self, '_is_drawing', False)): - self._draw_pending = True - QtCore.QTimer.singleShot(0, self._draw_idle) - - def blit(self, bbox=None): - # docstring inherited - if bbox is None and self.figure: - bbox = self.figure.bbox # Blit the entire canvas if bbox is None. - # repaint uses logical pixels, not physical pixels like the renderer. - l, b, w, h = [int(pt / self.device_pixel_ratio) for pt in bbox.bounds] - t = b + h - self.repaint(l, self.rect().height() - t, w, h) - - def _draw_idle(self): - with self._idle_draw_cntx(): - if not self._draw_pending: - return - self._draw_pending = False - if self.height() < 0 or self.width() < 0: - return - try: - self.draw() - except Exception: - # Uncaught exceptions are fatal for PyQt5, so catch them. - traceback.print_exc() - - def drawRectangle(self, rect): - # Draw the zoom rectangle to the QPainter. _draw_rect_callback needs - # to be called at the end of paintEvent. - if rect is not None: - x0, y0, w, h = [int(pt / self.device_pixel_ratio) for pt in rect] - x1 = x0 + w - y1 = y0 + h - def _draw_rect_callback(painter): - pen = QtGui.QPen( - QtGui.QColor("black"), - 1 / self.device_pixel_ratio - ) - - pen.setDashPattern([3, 3]) - for color, offset in [ - (QtGui.QColor("black"), 0), - (QtGui.QColor("white"), 3), - ]: - pen.setDashOffset(offset) - pen.setColor(color) - painter.setPen(pen) - # Draw the lines from x0, y0 towards x1, y1 so that the - # dashes don't "jump" when moving the zoom box. - painter.drawLine(x0, y0, x0, y1) - painter.drawLine(x0, y0, x1, y0) - painter.drawLine(x0, y1, x1, y1) - painter.drawLine(x1, y0, x1, y1) - else: - def _draw_rect_callback(painter): - return - self._draw_rect_callback = _draw_rect_callback - self.update() - - -class MainWindow(QtWidgets.QMainWindow): - closing = QtCore.Signal() - - def closeEvent(self, event): - self.closing.emit() - super().closeEvent(event) - - -class FigureManagerQT(FigureManagerBase): - """ - Attributes - ---------- - canvas : `FigureCanvas` - The FigureCanvas instance - num : int or str - The Figure number - toolbar : qt.QToolBar - The qt.QToolBar - window : qt.QMainWindow - The qt.QMainWindow - """ - - def __init__(self, canvas, num): - self.window = MainWindow() - super().__init__(canvas, num) - self.window.closing.connect(self._widgetclosed) - - if sys.platform != "darwin": - image = str(cbook._get_data_path('images/matplotlib.svg')) - icon = QtGui.QIcon(image) - self.window.setWindowIcon(icon) - - self.window._destroying = False - - if self.toolbar: - self.window.addToolBar(self.toolbar) - tbs_height = self.toolbar.sizeHint().height() - else: - tbs_height = 0 - - # resize the main window so it will display the canvas with the - # requested size: - cs = canvas.sizeHint() - cs_height = cs.height() - height = cs_height + tbs_height - self.window.resize(cs.width(), height) - - self.window.setCentralWidget(self.canvas) - - if mpl.is_interactive(): - self.window.show() - self.canvas.draw_idle() - - # Give the keyboard focus to the figure instead of the manager: - # StrongFocus accepts both tab and click to focus and will enable the - # canvas to process event without clicking. - # https://doc.qt.io/qt-5/qt.html#FocusPolicy-enum - self.canvas.setFocusPolicy(_enum("QtCore.Qt.FocusPolicy").StrongFocus) - self.canvas.setFocus() - - self.window.raise_() - - def full_screen_toggle(self): - if self.window.isFullScreen(): - self.window.showNormal() - else: - self.window.showFullScreen() - - def _widgetclosed(self): - CloseEvent("close_event", self.canvas)._process() - if self.window._destroying: - return - self.window._destroying = True - try: - Gcf.destroy(self) - except AttributeError: - pass - # It seems that when the python session is killed, - # Gcf can get destroyed before the Gcf.destroy - # line is run, leading to a useless AttributeError. - - def resize(self, width, height): - # The Qt methods return sizes in 'virtual' pixels so we do need to - # rescale from physical to logical pixels. - width = int(width / self.canvas.device_pixel_ratio) - height = int(height / self.canvas.device_pixel_ratio) - extra_width = self.window.width() - self.canvas.width() - extra_height = self.window.height() - self.canvas.height() - self.canvas.resize(width, height) - self.window.resize(width + extra_width, height + extra_height) - - @classmethod - def start_main_loop(cls): - qapp = QtWidgets.QApplication.instance() - if qapp: - with _maybe_allow_interrupt(qapp): - qt_compat._exec(qapp) - - def show(self): - self.window.show() - if mpl.rcParams['figure.raise_window']: - self.window.activateWindow() - self.window.raise_() - - def destroy(self, *args): - # check for qApp first, as PySide deletes it in its atexit handler - if QtWidgets.QApplication.instance() is None: - return - if self.window._destroying: - return - self.window._destroying = True - if self.toolbar: - self.toolbar.destroy() - self.window.close() - - def get_window_title(self): - return self.window.windowTitle() - - def set_window_title(self, title): - self.window.setWindowTitle(title) - - -class NavigationToolbar2QT(NavigationToolbar2, QtWidgets.QToolBar): - message = QtCore.Signal(str) - - toolitems = [*NavigationToolbar2.toolitems] - toolitems.insert( - # Add 'customize' action after 'subplots' - [name for name, *_ in toolitems].index("Subplots") + 1, - ("Customize", "Edit axis, curve and image parameters", - "qt4_editor_options", "edit_parameters")) - - def __init__(self, canvas, parent=None, coordinates=True): - """coordinates: should we show the coordinates on the right?""" - QtWidgets.QToolBar.__init__(self, parent) - self.setAllowedAreas(QtCore.Qt.ToolBarArea( - _to_int(_enum("QtCore.Qt.ToolBarArea").TopToolBarArea) | - _to_int(_enum("QtCore.Qt.ToolBarArea").BottomToolBarArea))) - - self.coordinates = coordinates - self._actions = {} # mapping of toolitem method names to QActions. - self._subplot_dialog = None - - for text, tooltip_text, image_file, callback in self.toolitems: - if text is None: - self.addSeparator() - else: - a = self.addAction(self._icon(image_file + '.png'), - text, getattr(self, callback)) - self._actions[callback] = a - if callback in ['zoom', 'pan']: - a.setCheckable(True) - if tooltip_text is not None: - a.setToolTip(tooltip_text) - - # Add the (x, y) location widget at the right side of the toolbar - # The stretch factor is 1 which means any resizing of the toolbar - # will resize this label instead of the buttons. - if self.coordinates: - self.locLabel = QtWidgets.QLabel("", self) - self.locLabel.setAlignment(QtCore.Qt.AlignmentFlag( - _to_int(_enum("QtCore.Qt.AlignmentFlag").AlignRight) | - _to_int(_enum("QtCore.Qt.AlignmentFlag").AlignVCenter))) - self.locLabel.setSizePolicy(QtWidgets.QSizePolicy( - _enum("QtWidgets.QSizePolicy.Policy").Expanding, - _enum("QtWidgets.QSizePolicy.Policy").Ignored, - )) - labelAction = self.addWidget(self.locLabel) - labelAction.setVisible(True) - - NavigationToolbar2.__init__(self, canvas) - - def _icon(self, name): - """ - Construct a `.QIcon` from an image file *name*, including the extension - and relative to Matplotlib's "images" data directory. - """ - # use a high-resolution icon with suffix '_large' if available - # note: user-provided icons may not have '_large' versions - path_regular = cbook._get_data_path('images', name) - path_large = path_regular.with_name( - path_regular.name.replace('.png', '_large.png')) - filename = str(path_large if path_large.exists() else path_regular) - - pm = QtGui.QPixmap(filename) - pm.setDevicePixelRatio( - self.devicePixelRatioF() or 1) # rarely, devicePixelRatioF=0 - if self.palette().color(self.backgroundRole()).value() < 128: - icon_color = self.palette().color(self.foregroundRole()) - mask = pm.createMaskFromColor( - QtGui.QColor('black'), - _enum("QtCore.Qt.MaskMode").MaskOutColor) - pm.fill(icon_color) - pm.setMask(mask) - return QtGui.QIcon(pm) - - def edit_parameters(self): - axes = self.canvas.figure.get_axes() - if not axes: - QtWidgets.QMessageBox.warning( - self.canvas.parent(), "Error", "There are no axes to edit.") - return - elif len(axes) == 1: - ax, = axes - else: - titles = [ - ax.get_label() or - ax.get_title() or - ax.get_title("left") or - ax.get_title("right") or - " - ".join(filter(None, [ax.get_xlabel(), ax.get_ylabel()])) or - f"" - for ax in axes] - duplicate_titles = [ - title for title in titles if titles.count(title) > 1] - for i, ax in enumerate(axes): - if titles[i] in duplicate_titles: - titles[i] += f" (id: {id(ax):#x})" # Deduplicate titles. - item, ok = QtWidgets.QInputDialog.getItem( - self.canvas.parent(), - 'Customize', 'Select axes:', titles, 0, False) - if not ok: - return - ax = axes[titles.index(item)] - figureoptions.figure_edit(ax, self) - - def _update_buttons_checked(self): - # sync button checkstates to match active mode - if 'pan' in self._actions: - self._actions['pan'].setChecked(self.mode.name == 'PAN') - if 'zoom' in self._actions: - self._actions['zoom'].setChecked(self.mode.name == 'ZOOM') - - def pan(self, *args): - super().pan(*args) - self._update_buttons_checked() - - def zoom(self, *args): - super().zoom(*args) - self._update_buttons_checked() - - def set_message(self, s): - self.message.emit(s) - if self.coordinates: - self.locLabel.setText(s) - - def draw_rubberband(self, event, x0, y0, x1, y1): - height = self.canvas.figure.bbox.height - y1 = height - y1 - y0 = height - y0 - rect = [int(val) for val in (x0, y0, x1 - x0, y1 - y0)] - self.canvas.drawRectangle(rect) - - def remove_rubberband(self): - self.canvas.drawRectangle(None) - - def configure_subplots(self): - if self._subplot_dialog is None: - self._subplot_dialog = SubplotToolQt( - self.canvas.figure, self.canvas.parent()) - self.canvas.mpl_connect( - "close_event", lambda e: self._subplot_dialog.reject()) - self._subplot_dialog.update_from_current_subplotpars() - self._subplot_dialog.show() - return self._subplot_dialog - - def save_figure(self, *args): - filetypes = self.canvas.get_supported_filetypes_grouped() - sorted_filetypes = sorted(filetypes.items()) - default_filetype = self.canvas.get_default_filetype() - - startpath = os.path.expanduser(mpl.rcParams['savefig.directory']) - start = os.path.join(startpath, self.canvas.get_default_filename()) - filters = [] - selectedFilter = None - for name, exts in sorted_filetypes: - exts_list = " ".join(['*.%s' % ext for ext in exts]) - filter = '%s (%s)' % (name, exts_list) - if default_filetype in exts: - selectedFilter = filter - filters.append(filter) - filters = ';;'.join(filters) - - fname, filter = qt_compat._getSaveFileName( - self.canvas.parent(), "Choose a filename to save to", start, - filters, selectedFilter) - if fname: - # Save dir for next time, unless empty str (i.e., use cwd). - if startpath != "": - mpl.rcParams['savefig.directory'] = os.path.dirname(fname) - try: - self.canvas.figure.savefig(fname) - except Exception as e: - QtWidgets.QMessageBox.critical( - self, "Error saving file", str(e), - _enum("QtWidgets.QMessageBox.StandardButton").Ok, - _enum("QtWidgets.QMessageBox.StandardButton").NoButton) - - def set_history_buttons(self): - can_backward = self._nav_stack._pos > 0 - can_forward = self._nav_stack._pos < len(self._nav_stack._elements) - 1 - if 'back' in self._actions: - self._actions['back'].setEnabled(can_backward) - if 'forward' in self._actions: - self._actions['forward'].setEnabled(can_forward) - - -class SubplotToolQt(QtWidgets.QDialog): - def __init__(self, targetfig, parent): - super().__init__() - self.setWindowIcon(QtGui.QIcon( - str(cbook._get_data_path("images/matplotlib.png")))) - self.setObjectName("SubplotTool") - self._spinboxes = {} - main_layout = QtWidgets.QHBoxLayout() - self.setLayout(main_layout) - for group, spinboxes, buttons in [ - ("Borders", - ["top", "bottom", "left", "right"], - [("Export values", self._export_values)]), - ("Spacings", - ["hspace", "wspace"], - [("Tight layout", self._tight_layout), - ("Reset", self._reset), - ("Close", self.close)])]: - layout = QtWidgets.QVBoxLayout() - main_layout.addLayout(layout) - box = QtWidgets.QGroupBox(group) - layout.addWidget(box) - inner = QtWidgets.QFormLayout(box) - for name in spinboxes: - self._spinboxes[name] = spinbox = QtWidgets.QDoubleSpinBox() - spinbox.setRange(0, 1) - spinbox.setDecimals(3) - spinbox.setSingleStep(0.005) - spinbox.setKeyboardTracking(False) - spinbox.valueChanged.connect(self._on_value_changed) - inner.addRow(name, spinbox) - layout.addStretch(1) - for name, method in buttons: - button = QtWidgets.QPushButton(name) - # Don't trigger on , which is used to input values. - button.setAutoDefault(False) - button.clicked.connect(method) - layout.addWidget(button) - if name == "Close": - button.setFocus() - self._figure = targetfig - self._defaults = {} - self._export_values_dialog = None - self.update_from_current_subplotpars() - - def update_from_current_subplotpars(self): - self._defaults = {spinbox: getattr(self._figure.subplotpars, name) - for name, spinbox in self._spinboxes.items()} - self._reset() # Set spinbox current values without triggering signals. - - def _export_values(self): - # Explicitly round to 3 decimals (which is also the spinbox precision) - # to avoid numbers of the form 0.100...001. - self._export_values_dialog = QtWidgets.QDialog() - layout = QtWidgets.QVBoxLayout() - self._export_values_dialog.setLayout(layout) - text = QtWidgets.QPlainTextEdit() - text.setReadOnly(True) - layout.addWidget(text) - text.setPlainText( - ",\n".join(f"{attr}={spinbox.value():.3}" - for attr, spinbox in self._spinboxes.items())) - # Adjust the height of the text widget to fit the whole text, plus - # some padding. - size = text.maximumSize() - size.setHeight( - QtGui.QFontMetrics(text.document().defaultFont()) - .size(0, text.toPlainText()).height() + 20) - text.setMaximumSize(size) - self._export_values_dialog.show() - - def _on_value_changed(self): - spinboxes = self._spinboxes - # Set all mins and maxes, so that this can also be used in _reset(). - for lower, higher in [("bottom", "top"), ("left", "right")]: - spinboxes[higher].setMinimum(spinboxes[lower].value() + .001) - spinboxes[lower].setMaximum(spinboxes[higher].value() - .001) - self._figure.subplots_adjust( - **{attr: spinbox.value() for attr, spinbox in spinboxes.items()}) - self._figure.canvas.draw_idle() - - def _tight_layout(self): - self._figure.tight_layout() - for attr, spinbox in self._spinboxes.items(): - spinbox.blockSignals(True) - spinbox.setValue(getattr(self._figure.subplotpars, attr)) - spinbox.blockSignals(False) - self._figure.canvas.draw_idle() - - def _reset(self): - for spinbox, value in self._defaults.items(): - spinbox.setRange(0, 1) - spinbox.blockSignals(True) - spinbox.setValue(value) - spinbox.blockSignals(False) - self._on_value_changed() - - -class ToolbarQt(ToolContainerBase, QtWidgets.QToolBar): - def __init__(self, toolmanager, parent=None): - ToolContainerBase.__init__(self, toolmanager) - QtWidgets.QToolBar.__init__(self, parent) - self.setAllowedAreas(QtCore.Qt.ToolBarArea( - _to_int(_enum("QtCore.Qt.ToolBarArea").TopToolBarArea) | - _to_int(_enum("QtCore.Qt.ToolBarArea").BottomToolBarArea))) - message_label = QtWidgets.QLabel("") - message_label.setAlignment(QtCore.Qt.AlignmentFlag( - _to_int(_enum("QtCore.Qt.AlignmentFlag").AlignRight) | - _to_int(_enum("QtCore.Qt.AlignmentFlag").AlignVCenter))) - message_label.setSizePolicy(QtWidgets.QSizePolicy( - _enum("QtWidgets.QSizePolicy.Policy").Expanding, - _enum("QtWidgets.QSizePolicy.Policy").Ignored, - )) - self._message_action = self.addWidget(message_label) - self._toolitems = {} - self._groups = {} - - def add_toolitem( - self, name, group, position, image_file, description, toggle): - - button = QtWidgets.QToolButton(self) - if image_file: - button.setIcon(NavigationToolbar2QT._icon(self, image_file)) - button.setText(name) - if description: - button.setToolTip(description) - - def handler(): - self.trigger_tool(name) - if toggle: - button.setCheckable(True) - button.toggled.connect(handler) - else: - button.clicked.connect(handler) - - self._toolitems.setdefault(name, []) - self._add_to_group(group, name, button, position) - self._toolitems[name].append((button, handler)) - - def _add_to_group(self, group, name, button, position): - gr = self._groups.get(group, []) - if not gr: - sep = self.insertSeparator(self._message_action) - gr.append(sep) - before = gr[position] - widget = self.insertWidget(before, button) - gr.insert(position, widget) - self._groups[group] = gr - - def toggle_toolitem(self, name, toggled): - if name not in self._toolitems: - return - for button, handler in self._toolitems[name]: - button.toggled.disconnect(handler) - button.setChecked(toggled) - button.toggled.connect(handler) - - def remove_toolitem(self, name): - for button, handler in self._toolitems[name]: - button.setParent(None) - del self._toolitems[name] - - def set_message(self, s): - self.widgetForAction(self._message_action).setText(s) - - -@backend_tools._register_tool_class(FigureCanvasQT) -class ConfigureSubplotsQt(backend_tools.ConfigureSubplotsBase): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._subplot_dialog = None - - def trigger(self, *args): - NavigationToolbar2QT.configure_subplots(self) - - -@backend_tools._register_tool_class(FigureCanvasQT) -class SaveFigureQt(backend_tools.SaveFigureBase): - def trigger(self, *args): - NavigationToolbar2QT.save_figure( - self._make_classic_style_pseudo_toolbar()) - - -@backend_tools._register_tool_class(FigureCanvasQT) -class RubberbandQt(backend_tools.RubberbandBase): - def draw_rubberband(self, x0, y0, x1, y1): - NavigationToolbar2QT.draw_rubberband( - self._make_classic_style_pseudo_toolbar(), None, x0, y0, x1, y1) - - def remove_rubberband(self): - NavigationToolbar2QT.remove_rubberband( - self._make_classic_style_pseudo_toolbar()) - - -@backend_tools._register_tool_class(FigureCanvasQT) -class HelpQt(backend_tools.ToolHelpBase): - def trigger(self, *args): - QtWidgets.QMessageBox.information(None, "Help", self._get_help_html()) - - -@backend_tools._register_tool_class(FigureCanvasQT) -class ToolCopyToClipboardQT(backend_tools.ToolCopyToClipboardBase): - def trigger(self, *args, **kwargs): - pixmap = self.canvas.grab() - QtWidgets.QApplication.instance().clipboard().setPixmap(pixmap) - - -FigureManagerQT._toolbar2_class = NavigationToolbar2QT -FigureManagerQT._toolmanager_toolbar_class = ToolbarQt - - -@_Backend.export -class _BackendQT(_Backend): - backend_version = __version__ - FigureCanvas = FigureCanvasQT - FigureManager = FigureManagerQT - mainloop = FigureManagerQT.start_main_loop diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_qt5.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_qt5.py deleted file mode 100644 index d94062b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_qt5.py +++ /dev/null @@ -1,28 +0,0 @@ -from .. import backends - -backends._QT_FORCE_QT5_BINDING = True - - -from .backend_qt import ( # noqa - SPECIAL_KEYS, - # Public API - cursord, _create_qApp, _BackendQT, TimerQT, MainWindow, FigureCanvasQT, - FigureManagerQT, ToolbarQt, NavigationToolbar2QT, SubplotToolQt, - SaveFigureQt, ConfigureSubplotsQt, RubberbandQt, - HelpQt, ToolCopyToClipboardQT, - # internal re-exports - FigureCanvasBase, FigureManagerBase, MouseButton, NavigationToolbar2, - TimerBase, ToolContainerBase, figureoptions, Gcf -) -from . import backend_qt as _backend_qt # noqa - - -@_BackendQT.export -class _BackendQT5(_BackendQT): - pass - - -def __getattr__(name): - if name == 'qApp': - return _backend_qt.qApp - raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_qt5agg.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_qt5agg.py deleted file mode 100644 index 8a92fd5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_qt5agg.py +++ /dev/null @@ -1,14 +0,0 @@ -""" -Render to qt from agg -""" -from .. import backends - -backends._QT_FORCE_QT5_BINDING = True -from .backend_qtagg import ( # noqa: F401, E402 # pylint: disable=W0611 - _BackendQTAgg, FigureCanvasQTAgg, FigureManagerQT, NavigationToolbar2QT, - FigureCanvasAgg, FigureCanvasQT) - - -@_BackendQTAgg.export -class _BackendQT5Agg(_BackendQTAgg): - pass diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_qt5cairo.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_qt5cairo.py deleted file mode 100644 index a4263f5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_qt5cairo.py +++ /dev/null @@ -1,11 +0,0 @@ -from .. import backends - -backends._QT_FORCE_QT5_BINDING = True -from .backend_qtcairo import ( # noqa: F401, E402 # pylint: disable=W0611 - _BackendQTCairo, FigureCanvasQTCairo, FigureCanvasCairo, FigureCanvasQT -) - - -@_BackendQTCairo.export -class _BackendQT5Cairo(_BackendQTCairo): - pass diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_qtagg.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_qtagg.py deleted file mode 100644 index f64264d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_qtagg.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -Render to qt from agg. -""" - -import ctypes - -from matplotlib.transforms import Bbox - -from .qt_compat import QT_API, _enum -from .backend_agg import FigureCanvasAgg -from .backend_qt import QtCore, QtGui, _BackendQT, FigureCanvasQT -from .backend_qt import ( # noqa: F401 # pylint: disable=W0611 - FigureManagerQT, NavigationToolbar2QT) - - -class FigureCanvasQTAgg(FigureCanvasAgg, FigureCanvasQT): - - def paintEvent(self, event): - """ - Copy the image from the Agg canvas to the qt.drawable. - - In Qt, all drawing should be done inside of here when a widget is - shown onscreen. - """ - self._draw_idle() # Only does something if a draw is pending. - - # If the canvas does not have a renderer, then give up and wait for - # FigureCanvasAgg.draw(self) to be called. - if not hasattr(self, 'renderer'): - return - - painter = QtGui.QPainter(self) - try: - # See documentation of QRect: bottom() and right() are off - # by 1, so use left() + width() and top() + height(). - rect = event.rect() - # scale rect dimensions using the screen dpi ratio to get - # correct values for the Figure coordinates (rather than - # QT5's coords) - width = rect.width() * self.device_pixel_ratio - height = rect.height() * self.device_pixel_ratio - left, top = self.mouseEventCoords(rect.topLeft()) - # shift the "top" by the height of the image to get the - # correct corner for our coordinate system - bottom = top - height - # same with the right side of the image - right = left + width - # create a buffer using the image bounding box - bbox = Bbox([[left, bottom], [right, top]]) - buf = memoryview(self.copy_from_bbox(bbox)) - - if QT_API == "PyQt6": - from PyQt6 import sip - ptr = int(sip.voidptr(buf)) - else: - ptr = buf - - painter.eraseRect(rect) # clear the widget canvas - qimage = QtGui.QImage(ptr, buf.shape[1], buf.shape[0], - _enum("QtGui.QImage.Format").Format_RGBA8888) - qimage.setDevicePixelRatio(self.device_pixel_ratio) - # set origin using original QT coordinates - origin = QtCore.QPoint(rect.left(), rect.top()) - painter.drawImage(origin, qimage) - # Adjust the buf reference count to work around a memory - # leak bug in QImage under PySide. - if QT_API == "PySide2" and QtCore.__version_info__ < (5, 12): - ctypes.c_long.from_address(id(buf)).value = 1 - - self._draw_rect_callback(painter) - finally: - painter.end() - - def print_figure(self, *args, **kwargs): - super().print_figure(*args, **kwargs) - self.draw() - - -@_BackendQT.export -class _BackendQTAgg(_BackendQT): - FigureCanvas = FigureCanvasQTAgg diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_qtcairo.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_qtcairo.py deleted file mode 100644 index cca1be0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_qtcairo.py +++ /dev/null @@ -1,46 +0,0 @@ -import ctypes - -from .backend_cairo import cairo, FigureCanvasCairo -from .backend_qt import QtCore, QtGui, _BackendQT, FigureCanvasQT -from .qt_compat import QT_API, _enum - - -class FigureCanvasQTCairo(FigureCanvasCairo, FigureCanvasQT): - def draw(self): - if hasattr(self._renderer.gc, "ctx"): - self._renderer.dpi = self.figure.dpi - self.figure.draw(self._renderer) - super().draw() - - def paintEvent(self, event): - width = int(self.device_pixel_ratio * self.width()) - height = int(self.device_pixel_ratio * self.height()) - if (width, height) != self._renderer.get_canvas_width_height(): - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) - self._renderer.set_context(cairo.Context(surface)) - self._renderer.dpi = self.figure.dpi - self.figure.draw(self._renderer) - buf = self._renderer.gc.ctx.get_target().get_data() - if QT_API == "PyQt6": - from PyQt6 import sip - ptr = int(sip.voidptr(buf)) - else: - ptr = buf - qimage = QtGui.QImage( - ptr, width, height, - _enum("QtGui.QImage.Format").Format_ARGB32_Premultiplied) - # Adjust the buf reference count to work around a memory leak bug in - # QImage under PySide. - if QT_API == "PySide2" and QtCore.__version_info__ < (5, 12): - ctypes.c_long.from_address(id(buf)).value = 1 - qimage.setDevicePixelRatio(self.device_pixel_ratio) - painter = QtGui.QPainter(self) - painter.eraseRect(event.rect()) - painter.drawImage(0, 0, qimage) - self._draw_rect_callback(painter) - painter.end() - - -@_BackendQT.export -class _BackendQTCairo(_BackendQT): - FigureCanvas = FigureCanvasQTCairo diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_svg.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_svg.py deleted file mode 100644 index 4a6c1bb..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_svg.py +++ /dev/null @@ -1,1398 +0,0 @@ -import base64 -import codecs -import datetime -import gzip -import hashlib -from io import BytesIO -import itertools -import logging -import os -import re -import uuid - -import numpy as np -from PIL import Image - -import matplotlib as mpl -from matplotlib import _api, cbook, font_manager as fm -from matplotlib.backend_bases import ( - _Backend, FigureCanvasBase, FigureManagerBase, RendererBase) -from matplotlib.backends.backend_mixed import MixedModeRenderer -from matplotlib.colors import rgb2hex -from matplotlib.dates import UTC -from matplotlib.path import Path -from matplotlib import _path -from matplotlib.transforms import Affine2D, Affine2DBase - - -_log = logging.getLogger(__name__) - - -# ---------------------------------------------------------------------- -# SimpleXMLWriter class -# -# Based on an original by Fredrik Lundh, but modified here to: -# 1. Support modern Python idioms -# 2. Remove encoding support (it's handled by the file writer instead) -# 3. Support proper indentation -# 4. Minify things a little bit - -# -------------------------------------------------------------------- -# The SimpleXMLWriter module is -# -# Copyright (c) 2001-2004 by Fredrik Lundh -# -# By obtaining, using, and/or copying this software and/or its -# associated documentation, you agree that you have read, understood, -# and will comply with the following terms and conditions: -# -# Permission to use, copy, modify, and distribute this software and -# its associated documentation for any purpose and without fee is -# hereby granted, provided that the above copyright notice appears in -# all copies, and that both that copyright notice and this permission -# notice appear in supporting documentation, and that the name of -# Secret Labs AB or the author not be used in advertising or publicity -# pertaining to distribution of the software without specific, written -# prior permission. -# -# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD -# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- -# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR -# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY -# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, -# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS -# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE -# OF THIS SOFTWARE. -# -------------------------------------------------------------------- - - -@_api.deprecated("3.6", alternative="a vendored copy of _escape_cdata") -def escape_cdata(s): - return _escape_cdata(s) - - -def _escape_cdata(s): - s = s.replace("&", "&") - s = s.replace("<", "<") - s = s.replace(">", ">") - return s - - -_escape_xml_comment = re.compile(r'-(?=-)') - - -@_api.deprecated("3.6", alternative="a vendored copy of _escape_comment") -def escape_comment(s): - return _escape_comment.sub(s) - - -def _escape_comment(s): - s = _escape_cdata(s) - return _escape_xml_comment.sub('- ', s) - - -@_api.deprecated("3.6", alternative="a vendored copy of _escape_attrib") -def escape_attrib(s): - return _escape_attrib(s) - - -def _escape_attrib(s): - s = s.replace("&", "&") - s = s.replace("'", "'") - s = s.replace('"', """) - s = s.replace("<", "<") - s = s.replace(">", ">") - return s - - -def _quote_escape_attrib(s): - return ('"' + _escape_cdata(s) + '"' if '"' not in s else - "'" + _escape_cdata(s) + "'" if "'" not in s else - '"' + _escape_attrib(s) + '"') - - -@_api.deprecated("3.6", alternative="a vendored copy of _short_float_fmt") -def short_float_fmt(x): - return _short_float_fmt(x) - - -def _short_float_fmt(x): - """ - Create a short string representation of a float, which is %f - formatting with trailing zeros and the decimal point removed. - """ - return '{0:f}'.format(x).rstrip('0').rstrip('.') - - -class XMLWriter: - """ - Parameters - ---------- - file : writable text file-like object - """ - - def __init__(self, file): - self.__write = file.write - if hasattr(file, "flush"): - self.flush = file.flush - self.__open = 0 # true if start tag is open - self.__tags = [] - self.__data = [] - self.__indentation = " " * 64 - - def __flush(self, indent=True): - # flush internal buffers - if self.__open: - if indent: - self.__write(">\n") - else: - self.__write(">") - self.__open = 0 - if self.__data: - data = ''.join(self.__data) - self.__write(_escape_cdata(data)) - self.__data = [] - - def start(self, tag, attrib={}, **extra): - """ - Open a new element. Attributes can be given as keyword - arguments, or as a string/string dictionary. The method returns - an opaque identifier that can be passed to the :meth:`close` - method, to close all open elements up to and including this one. - - Parameters - ---------- - tag - Element tag. - attrib - Attribute dictionary. Alternatively, attributes can be given as - keyword arguments. - - Returns - ------- - An element identifier. - """ - self.__flush() - tag = _escape_cdata(tag) - self.__data = [] - self.__tags.append(tag) - self.__write(self.__indentation[:len(self.__tags) - 1]) - self.__write("<%s" % tag) - for k, v in {**attrib, **extra}.items(): - if v: - k = _escape_cdata(k) - v = _quote_escape_attrib(v) - self.__write(' %s=%s' % (k, v)) - self.__open = 1 - return len(self.__tags) - 1 - - def comment(self, comment): - """ - Add a comment to the output stream. - - Parameters - ---------- - comment : str - Comment text. - """ - self.__flush() - self.__write(self.__indentation[:len(self.__tags)]) - self.__write("\n" % _escape_comment(comment)) - - def data(self, text): - """ - Add character data to the output stream. - - Parameters - ---------- - text : str - Character data. - """ - self.__data.append(text) - - def end(self, tag=None, indent=True): - """ - Close the current element (opened by the most recent call to - :meth:`start`). - - Parameters - ---------- - tag - Element tag. If given, the tag must match the start tag. If - omitted, the current element is closed. - """ - if tag: - assert self.__tags, "unbalanced end(%s)" % tag - assert _escape_cdata(tag) == self.__tags[-1], \ - "expected end(%s), got %s" % (self.__tags[-1], tag) - else: - assert self.__tags, "unbalanced end()" - tag = self.__tags.pop() - if self.__data: - self.__flush(indent) - elif self.__open: - self.__open = 0 - self.__write("/>\n") - return - if indent: - self.__write(self.__indentation[:len(self.__tags)]) - self.__write("\n" % tag) - - def close(self, id): - """ - Close open elements, up to (and including) the element identified - by the given identifier. - - Parameters - ---------- - id - Element identifier, as returned by the :meth:`start` method. - """ - while len(self.__tags) > id: - self.end() - - def element(self, tag, text=None, attrib={}, **extra): - """ - Add an entire element. This is the same as calling :meth:`start`, - :meth:`data`, and :meth:`end` in sequence. The *text* argument can be - omitted. - """ - self.start(tag, attrib, **extra) - if text: - self.data(text) - self.end(indent=False) - - def flush(self): - """Flush the output stream.""" - pass # replaced by the constructor - - -def _generate_transform(transform_list): - parts = [] - for type, value in transform_list: - if (type == 'scale' and (value == (1,) or value == (1, 1)) - or type == 'translate' and value == (0, 0) - or type == 'rotate' and value == (0,)): - continue - if type == 'matrix' and isinstance(value, Affine2DBase): - value = value.to_values() - parts.append('%s(%s)' % ( - type, ' '.join(_short_float_fmt(x) for x in value))) - return ' '.join(parts) - - -@_api.deprecated("3.6") -def generate_transform(transform_list=None): - return _generate_transform(transform_list or []) - - -def _generate_css(attrib): - return "; ".join(f"{k}: {v}" for k, v in attrib.items()) - - -@_api.deprecated("3.6") -def generate_css(attrib=None): - return _generate_css(attrib or {}) - - -_capstyle_d = {'projecting': 'square', 'butt': 'butt', 'round': 'round'} - - -def _check_is_str(info, key): - if not isinstance(info, str): - raise TypeError(f'Invalid type for {key} metadata. Expected str, not ' - f'{type(info)}.') - - -def _check_is_iterable_of_str(infos, key): - if np.iterable(infos): - for info in infos: - if not isinstance(info, str): - raise TypeError(f'Invalid type for {key} metadata. Expected ' - f'iterable of str, not {type(info)}.') - else: - raise TypeError(f'Invalid type for {key} metadata. Expected str or ' - f'iterable of str, not {type(infos)}.') - - -class RendererSVG(RendererBase): - def __init__(self, width, height, svgwriter, basename=None, image_dpi=72, - *, metadata=None): - self.width = width - self.height = height - self.writer = XMLWriter(svgwriter) - self.image_dpi = image_dpi # actual dpi at which we rasterize stuff - - if basename is None: - basename = getattr(svgwriter, "name", "") - if not isinstance(basename, str): - basename = "" - self.basename = basename - - self._groupd = {} - self._image_counter = itertools.count() - self._clipd = {} - self._markers = {} - self._path_collection_id = 0 - self._hatchd = {} - self._has_gouraud = False - self._n_gradients = 0 - - super().__init__() - self._glyph_map = dict() - str_height = _short_float_fmt(height) - str_width = _short_float_fmt(width) - svgwriter.write(svgProlog) - self._start_id = self.writer.start( - 'svg', - width='%spt' % str_width, - height='%spt' % str_height, - viewBox='0 0 %s %s' % (str_width, str_height), - xmlns="http://www.w3.org/2000/svg", - version="1.1", - attrib={'xmlns:xlink': "http://www.w3.org/1999/xlink"}) - self._write_metadata(metadata) - self._write_default_style() - - def finalize(self): - self._write_clips() - self._write_hatches() - self.writer.close(self._start_id) - self.writer.flush() - - def _write_metadata(self, metadata): - # Add metadata following the Dublin Core Metadata Initiative, and the - # Creative Commons Rights Expression Language. This is mainly for - # compatibility with Inkscape. - if metadata is None: - metadata = {} - metadata = { - 'Format': 'image/svg+xml', - 'Type': 'http://purl.org/dc/dcmitype/StillImage', - 'Creator': - f'Matplotlib v{mpl.__version__}, https://matplotlib.org/', - **metadata - } - writer = self.writer - - if 'Title' in metadata: - title = metadata['Title'] - _check_is_str(title, 'Title') - writer.element('title', text=title) - - # Special handling. - date = metadata.get('Date', None) - if date is not None: - if isinstance(date, str): - dates = [date] - elif isinstance(date, (datetime.datetime, datetime.date)): - dates = [date.isoformat()] - elif np.iterable(date): - dates = [] - for d in date: - if isinstance(d, str): - dates.append(d) - elif isinstance(d, (datetime.datetime, datetime.date)): - dates.append(d.isoformat()) - else: - raise TypeError( - f'Invalid type for Date metadata. ' - f'Expected iterable of str, date, or datetime, ' - f'not {type(d)}.') - else: - raise TypeError(f'Invalid type for Date metadata. ' - f'Expected str, date, datetime, or iterable ' - f'of the same, not {type(date)}.') - metadata['Date'] = '/'.join(dates) - elif 'Date' not in metadata: - # Do not add `Date` if the user explicitly set `Date` to `None` - # Get source date from SOURCE_DATE_EPOCH, if set. - # See https://reproducible-builds.org/specs/source-date-epoch/ - date = os.getenv("SOURCE_DATE_EPOCH") - if date: - date = datetime.datetime.fromtimestamp(int(date), datetime.timezone.utc) - metadata['Date'] = date.replace(tzinfo=UTC).isoformat() - else: - metadata['Date'] = datetime.datetime.today().isoformat() - - mid = None - def ensure_metadata(mid): - if mid is not None: - return mid - mid = writer.start('metadata') - writer.start('rdf:RDF', attrib={ - 'xmlns:dc': "http://purl.org/dc/elements/1.1/", - 'xmlns:cc': "http://creativecommons.org/ns#", - 'xmlns:rdf': "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - }) - writer.start('cc:Work') - return mid - - uri = metadata.pop('Type', None) - if uri is not None: - mid = ensure_metadata(mid) - writer.element('dc:type', attrib={'rdf:resource': uri}) - - # Single value only. - for key in ['Title', 'Coverage', 'Date', 'Description', 'Format', - 'Identifier', 'Language', 'Relation', 'Source']: - info = metadata.pop(key, None) - if info is not None: - mid = ensure_metadata(mid) - _check_is_str(info, key) - writer.element(f'dc:{key.lower()}', text=info) - - # Multiple Agent values. - for key in ['Creator', 'Contributor', 'Publisher', 'Rights']: - agents = metadata.pop(key, None) - if agents is None: - continue - - if isinstance(agents, str): - agents = [agents] - - _check_is_iterable_of_str(agents, key) - # Now we know that we have an iterable of str - mid = ensure_metadata(mid) - writer.start(f'dc:{key.lower()}') - for agent in agents: - writer.start('cc:Agent') - writer.element('dc:title', text=agent) - writer.end('cc:Agent') - writer.end(f'dc:{key.lower()}') - - # Multiple values. - keywords = metadata.pop('Keywords', None) - if keywords is not None: - if isinstance(keywords, str): - keywords = [keywords] - _check_is_iterable_of_str(keywords, 'Keywords') - # Now we know that we have an iterable of str - mid = ensure_metadata(mid) - writer.start('dc:subject') - writer.start('rdf:Bag') - for keyword in keywords: - writer.element('rdf:li', text=keyword) - writer.end('rdf:Bag') - writer.end('dc:subject') - - if mid is not None: - writer.close(mid) - - if metadata: - raise ValueError('Unknown metadata key(s) passed to SVG writer: ' + - ','.join(metadata)) - - def _write_default_style(self): - writer = self.writer - default_style = _generate_css({ - 'stroke-linejoin': 'round', - 'stroke-linecap': 'butt'}) - writer.start('defs') - writer.element('style', type='text/css', text='*{%s}' % default_style) - writer.end('defs') - - def _make_id(self, type, content): - salt = mpl.rcParams['svg.hashsalt'] - if salt is None: - salt = str(uuid.uuid4()) - m = hashlib.sha256() - m.update(salt.encode('utf8')) - m.update(str(content).encode('utf8')) - return '%s%s' % (type, m.hexdigest()[:10]) - - def _make_flip_transform(self, transform): - return transform + Affine2D().scale(1, -1).translate(0, self.height) - - def _get_hatch(self, gc, rgbFace): - """ - Create a new hatch pattern - """ - if rgbFace is not None: - rgbFace = tuple(rgbFace) - edge = gc.get_hatch_color() - if edge is not None: - edge = tuple(edge) - dictkey = (gc.get_hatch(), rgbFace, edge) - oid = self._hatchd.get(dictkey) - if oid is None: - oid = self._make_id('h', dictkey) - self._hatchd[dictkey] = ((gc.get_hatch_path(), rgbFace, edge), oid) - else: - _, oid = oid - return oid - - def _write_hatches(self): - if not len(self._hatchd): - return - HATCH_SIZE = 72 - writer = self.writer - writer.start('defs') - for (path, face, stroke), oid in self._hatchd.values(): - writer.start( - 'pattern', - id=oid, - patternUnits="userSpaceOnUse", - x="0", y="0", width=str(HATCH_SIZE), - height=str(HATCH_SIZE)) - path_data = self._convert_path( - path, - Affine2D() - .scale(HATCH_SIZE).scale(1.0, -1.0).translate(0, HATCH_SIZE), - simplify=False) - if face is None: - fill = 'none' - else: - fill = rgb2hex(face) - writer.element( - 'rect', - x="0", y="0", width=str(HATCH_SIZE+1), - height=str(HATCH_SIZE+1), - fill=fill) - hatch_style = { - 'fill': rgb2hex(stroke), - 'stroke': rgb2hex(stroke), - 'stroke-width': str(mpl.rcParams['hatch.linewidth']), - 'stroke-linecap': 'butt', - 'stroke-linejoin': 'miter' - } - if stroke[3] < 1: - hatch_style['stroke-opacity'] = str(stroke[3]) - writer.element( - 'path', - d=path_data, - style=_generate_css(hatch_style) - ) - writer.end('pattern') - writer.end('defs') - - def _get_style_dict(self, gc, rgbFace): - """Generate a style string from the GraphicsContext and rgbFace.""" - attrib = {} - - forced_alpha = gc.get_forced_alpha() - - if gc.get_hatch() is not None: - attrib['fill'] = "url(#%s)" % self._get_hatch(gc, rgbFace) - if (rgbFace is not None and len(rgbFace) == 4 and rgbFace[3] != 1.0 - and not forced_alpha): - attrib['fill-opacity'] = _short_float_fmt(rgbFace[3]) - else: - if rgbFace is None: - attrib['fill'] = 'none' - else: - if tuple(rgbFace[:3]) != (0, 0, 0): - attrib['fill'] = rgb2hex(rgbFace) - if (len(rgbFace) == 4 and rgbFace[3] != 1.0 - and not forced_alpha): - attrib['fill-opacity'] = _short_float_fmt(rgbFace[3]) - - if forced_alpha and gc.get_alpha() != 1.0: - attrib['opacity'] = _short_float_fmt(gc.get_alpha()) - - offset, seq = gc.get_dashes() - if seq is not None: - attrib['stroke-dasharray'] = ','.join( - _short_float_fmt(val) for val in seq) - attrib['stroke-dashoffset'] = _short_float_fmt(float(offset)) - - linewidth = gc.get_linewidth() - if linewidth: - rgb = gc.get_rgb() - attrib['stroke'] = rgb2hex(rgb) - if not forced_alpha and rgb[3] != 1.0: - attrib['stroke-opacity'] = _short_float_fmt(rgb[3]) - if linewidth != 1.0: - attrib['stroke-width'] = _short_float_fmt(linewidth) - if gc.get_joinstyle() != 'round': - attrib['stroke-linejoin'] = gc.get_joinstyle() - if gc.get_capstyle() != 'butt': - attrib['stroke-linecap'] = _capstyle_d[gc.get_capstyle()] - - return attrib - - def _get_style(self, gc, rgbFace): - return _generate_css(self._get_style_dict(gc, rgbFace)) - - def _get_clip_attrs(self, gc): - cliprect = gc.get_clip_rectangle() - clippath, clippath_trans = gc.get_clip_path() - if clippath is not None: - clippath_trans = self._make_flip_transform(clippath_trans) - dictkey = (id(clippath), str(clippath_trans)) - elif cliprect is not None: - x, y, w, h = cliprect.bounds - y = self.height-(y+h) - dictkey = (x, y, w, h) - else: - return {} - clip = self._clipd.get(dictkey) - if clip is None: - oid = self._make_id('p', dictkey) - if clippath is not None: - self._clipd[dictkey] = ((clippath, clippath_trans), oid) - else: - self._clipd[dictkey] = (dictkey, oid) - else: - clip, oid = clip - return {'clip-path': f'url(#{oid})'} - - def _write_clips(self): - if not len(self._clipd): - return - writer = self.writer - writer.start('defs') - for clip, oid in self._clipd.values(): - writer.start('clipPath', id=oid) - if len(clip) == 2: - clippath, clippath_trans = clip - path_data = self._convert_path( - clippath, clippath_trans, simplify=False) - writer.element('path', d=path_data) - else: - x, y, w, h = clip - writer.element( - 'rect', - x=_short_float_fmt(x), - y=_short_float_fmt(y), - width=_short_float_fmt(w), - height=_short_float_fmt(h)) - writer.end('clipPath') - writer.end('defs') - - def open_group(self, s, gid=None): - # docstring inherited - if gid: - self.writer.start('g', id=gid) - else: - self._groupd[s] = self._groupd.get(s, 0) + 1 - self.writer.start('g', id="%s_%d" % (s, self._groupd[s])) - - def close_group(self, s): - # docstring inherited - self.writer.end('g') - - def option_image_nocomposite(self): - # docstring inherited - return not mpl.rcParams['image.composite_image'] - - def _convert_path(self, path, transform=None, clip=None, simplify=None, - sketch=None): - if clip: - clip = (0.0, 0.0, self.width, self.height) - else: - clip = None - return _path.convert_to_string( - path, transform, clip, simplify, sketch, 6, - [b'M', b'L', b'Q', b'C', b'z'], False).decode('ascii') - - def draw_path(self, gc, path, transform, rgbFace=None): - # docstring inherited - trans_and_flip = self._make_flip_transform(transform) - clip = (rgbFace is None and gc.get_hatch_path() is None) - simplify = path.should_simplify and clip - path_data = self._convert_path( - path, trans_and_flip, clip=clip, simplify=simplify, - sketch=gc.get_sketch_params()) - - if gc.get_url() is not None: - self.writer.start('a', {'xlink:href': gc.get_url()}) - self.writer.element('path', d=path_data, **self._get_clip_attrs(gc), - style=self._get_style(gc, rgbFace)) - if gc.get_url() is not None: - self.writer.end('a') - - def draw_markers( - self, gc, marker_path, marker_trans, path, trans, rgbFace=None): - # docstring inherited - - if not len(path.vertices): - return - - writer = self.writer - path_data = self._convert_path( - marker_path, - marker_trans + Affine2D().scale(1.0, -1.0), - simplify=False) - style = self._get_style_dict(gc, rgbFace) - dictkey = (path_data, _generate_css(style)) - oid = self._markers.get(dictkey) - style = _generate_css({k: v for k, v in style.items() - if k.startswith('stroke')}) - - if oid is None: - oid = self._make_id('m', dictkey) - writer.start('defs') - writer.element('path', id=oid, d=path_data, style=style) - writer.end('defs') - self._markers[dictkey] = oid - - writer.start('g', **self._get_clip_attrs(gc)) - trans_and_flip = self._make_flip_transform(trans) - attrib = {'xlink:href': '#%s' % oid} - clip = (0, 0, self.width*72, self.height*72) - for vertices, code in path.iter_segments( - trans_and_flip, clip=clip, simplify=False): - if len(vertices): - x, y = vertices[-2:] - attrib['x'] = _short_float_fmt(x) - attrib['y'] = _short_float_fmt(y) - attrib['style'] = self._get_style(gc, rgbFace) - writer.element('use', attrib=attrib) - writer.end('g') - - def draw_path_collection(self, gc, master_transform, paths, all_transforms, - offsets, offset_trans, facecolors, edgecolors, - linewidths, linestyles, antialiaseds, urls, - offset_position): - # Is the optimization worth it? Rough calculation: - # cost of emitting a path in-line is - # (len_path + 5) * uses_per_path - # cost of definition+use is - # (len_path + 3) + 9 * uses_per_path - len_path = len(paths[0].vertices) if len(paths) > 0 else 0 - uses_per_path = self._iter_collection_uses_per_path( - paths, all_transforms, offsets, facecolors, edgecolors) - should_do_optimization = \ - len_path + 9 * uses_per_path + 3 < (len_path + 5) * uses_per_path - if not should_do_optimization: - return super().draw_path_collection( - gc, master_transform, paths, all_transforms, - offsets, offset_trans, facecolors, edgecolors, - linewidths, linestyles, antialiaseds, urls, - offset_position) - - writer = self.writer - path_codes = [] - writer.start('defs') - for i, (path, transform) in enumerate(self._iter_collection_raw_paths( - master_transform, paths, all_transforms)): - transform = Affine2D(transform.get_matrix()).scale(1.0, -1.0) - d = self._convert_path(path, transform, simplify=False) - oid = 'C%x_%x_%s' % ( - self._path_collection_id, i, self._make_id('', d)) - writer.element('path', id=oid, d=d) - path_codes.append(oid) - writer.end('defs') - - for xo, yo, path_id, gc0, rgbFace in self._iter_collection( - gc, path_codes, offsets, offset_trans, - facecolors, edgecolors, linewidths, linestyles, - antialiaseds, urls, offset_position): - url = gc0.get_url() - if url is not None: - writer.start('a', attrib={'xlink:href': url}) - clip_attrs = self._get_clip_attrs(gc0) - if clip_attrs: - writer.start('g', **clip_attrs) - attrib = { - 'xlink:href': '#%s' % path_id, - 'x': _short_float_fmt(xo), - 'y': _short_float_fmt(self.height - yo), - 'style': self._get_style(gc0, rgbFace) - } - writer.element('use', attrib=attrib) - if clip_attrs: - writer.end('g') - if url is not None: - writer.end('a') - - self._path_collection_id += 1 - - def draw_gouraud_triangle(self, gc, points, colors, trans): - # docstring inherited - self._draw_gouraud_triangle(gc, points, colors, trans) - - def _draw_gouraud_triangle(self, gc, points, colors, trans): - # This uses a method described here: - # - # http://www.svgopen.org/2005/papers/Converting3DFaceToSVG/index.html - # - # that uses three overlapping linear gradients to simulate a - # Gouraud triangle. Each gradient goes from fully opaque in - # one corner to fully transparent along the opposite edge. - # The line between the stop points is perpendicular to the - # opposite edge. Underlying these three gradients is a solid - # triangle whose color is the average of all three points. - - writer = self.writer - if not self._has_gouraud: - self._has_gouraud = True - writer.start( - 'filter', - id='colorAdd') - writer.element( - 'feComposite', - attrib={'in': 'SourceGraphic'}, - in2='BackgroundImage', - operator='arithmetic', - k2="1", k3="1") - writer.end('filter') - # feColorMatrix filter to correct opacity - writer.start( - 'filter', - id='colorMat') - writer.element( - 'feColorMatrix', - attrib={'type': 'matrix'}, - values='1 0 0 0 0 \n0 1 0 0 0 \n0 0 1 0 0' + - ' \n1 1 1 1 0 \n0 0 0 0 1 ') - writer.end('filter') - - avg_color = np.average(colors, axis=0) - if avg_color[-1] == 0: - # Skip fully-transparent triangles - return - - trans_and_flip = self._make_flip_transform(trans) - tpoints = trans_and_flip.transform(points) - - writer.start('defs') - for i in range(3): - x1, y1 = tpoints[i] - x2, y2 = tpoints[(i + 1) % 3] - x3, y3 = tpoints[(i + 2) % 3] - rgba_color = colors[i] - - if x2 == x3: - xb = x2 - yb = y1 - elif y2 == y3: - xb = x1 - yb = y2 - else: - m1 = (y2 - y3) / (x2 - x3) - b1 = y2 - (m1 * x2) - m2 = -(1.0 / m1) - b2 = y1 - (m2 * x1) - xb = (-b1 + b2) / (m1 - m2) - yb = m2 * xb + b2 - - writer.start( - 'linearGradient', - id="GR%x_%d" % (self._n_gradients, i), - gradientUnits="userSpaceOnUse", - x1=_short_float_fmt(x1), y1=_short_float_fmt(y1), - x2=_short_float_fmt(xb), y2=_short_float_fmt(yb)) - writer.element( - 'stop', - offset='1', - style=_generate_css({ - 'stop-color': rgb2hex(avg_color), - 'stop-opacity': _short_float_fmt(rgba_color[-1])})) - writer.element( - 'stop', - offset='0', - style=_generate_css({'stop-color': rgb2hex(rgba_color), - 'stop-opacity': "0"})) - - writer.end('linearGradient') - - writer.end('defs') - - # triangle formation using "path" - dpath = "M " + _short_float_fmt(x1)+',' + _short_float_fmt(y1) - dpath += " L " + _short_float_fmt(x2) + ',' + _short_float_fmt(y2) - dpath += " " + _short_float_fmt(x3) + ',' + _short_float_fmt(y3) + " Z" - - writer.element( - 'path', - attrib={'d': dpath, - 'fill': rgb2hex(avg_color), - 'fill-opacity': '1', - 'shape-rendering': "crispEdges"}) - - writer.start( - 'g', - attrib={'stroke': "none", - 'stroke-width': "0", - 'shape-rendering': "crispEdges", - 'filter': "url(#colorMat)"}) - - writer.element( - 'path', - attrib={'d': dpath, - 'fill': 'url(#GR%x_0)' % self._n_gradients, - 'shape-rendering': "crispEdges"}) - - writer.element( - 'path', - attrib={'d': dpath, - 'fill': 'url(#GR%x_1)' % self._n_gradients, - 'filter': 'url(#colorAdd)', - 'shape-rendering': "crispEdges"}) - - writer.element( - 'path', - attrib={'d': dpath, - 'fill': 'url(#GR%x_2)' % self._n_gradients, - 'filter': 'url(#colorAdd)', - 'shape-rendering': "crispEdges"}) - - writer.end('g') - - self._n_gradients += 1 - - def draw_gouraud_triangles(self, gc, triangles_array, colors_array, - transform): - self.writer.start('g', **self._get_clip_attrs(gc)) - transform = transform.frozen() - for tri, col in zip(triangles_array, colors_array): - self._draw_gouraud_triangle(gc, tri, col, transform) - self.writer.end('g') - - def option_scale_image(self): - # docstring inherited - return True - - def get_image_magnification(self): - return self.image_dpi / 72.0 - - def draw_image(self, gc, x, y, im, transform=None): - # docstring inherited - - h, w = im.shape[:2] - - if w == 0 or h == 0: - return - - clip_attrs = self._get_clip_attrs(gc) - if clip_attrs: - # Can't apply clip-path directly to the image because the image has - # a transformation, which would also be applied to the clip-path. - self.writer.start('g', **clip_attrs) - - url = gc.get_url() - if url is not None: - self.writer.start('a', attrib={'xlink:href': url}) - - attrib = {} - oid = gc.get_gid() - if mpl.rcParams['svg.image_inline']: - buf = BytesIO() - Image.fromarray(im).save(buf, format="png") - oid = oid or self._make_id('image', buf.getvalue()) - attrib['xlink:href'] = ( - "data:image/png;base64,\n" + - base64.b64encode(buf.getvalue()).decode('ascii')) - else: - if self.basename is None: - raise ValueError("Cannot save image data to filesystem when " - "writing SVG to an in-memory buffer") - filename = '{}.image{}.png'.format( - self.basename, next(self._image_counter)) - _log.info('Writing image file for inclusion: %s', filename) - Image.fromarray(im).save(filename) - oid = oid or 'Im_' + self._make_id('image', filename) - attrib['xlink:href'] = filename - attrib['id'] = oid - - if transform is None: - w = 72.0 * w / self.image_dpi - h = 72.0 * h / self.image_dpi - - self.writer.element( - 'image', - transform=_generate_transform([ - ('scale', (1, -1)), ('translate', (0, -h))]), - x=_short_float_fmt(x), - y=_short_float_fmt(-(self.height - y - h)), - width=_short_float_fmt(w), height=_short_float_fmt(h), - attrib=attrib) - else: - alpha = gc.get_alpha() - if alpha != 1.0: - attrib['opacity'] = _short_float_fmt(alpha) - - flipped = ( - Affine2D().scale(1.0 / w, 1.0 / h) + - transform + - Affine2D() - .translate(x, y) - .scale(1.0, -1.0) - .translate(0.0, self.height)) - - attrib['transform'] = _generate_transform( - [('matrix', flipped.frozen())]) - attrib['style'] = ( - 'image-rendering:crisp-edges;' - 'image-rendering:pixelated') - self.writer.element( - 'image', - width=_short_float_fmt(w), height=_short_float_fmt(h), - attrib=attrib) - - if url is not None: - self.writer.end('a') - if clip_attrs: - self.writer.end('g') - - def _update_glyph_map_defs(self, glyph_map_new): - """ - Emit definitions for not-yet-defined glyphs, and record them as having - been defined. - """ - writer = self.writer - if glyph_map_new: - writer.start('defs') - for char_id, (vertices, codes) in glyph_map_new.items(): - char_id = self._adjust_char_id(char_id) - # x64 to go back to FreeType's internal (integral) units. - path_data = self._convert_path( - Path(vertices * 64, codes), simplify=False) - writer.element( - 'path', id=char_id, d=path_data, - transform=_generate_transform([('scale', (1 / 64,))])) - writer.end('defs') - self._glyph_map.update(glyph_map_new) - - def _adjust_char_id(self, char_id): - return char_id.replace("%20", "_") - - def _draw_text_as_path(self, gc, x, y, s, prop, angle, ismath, mtext=None): - # docstring inherited - writer = self.writer - - writer.comment(s) - - glyph_map = self._glyph_map - - text2path = self._text2path - color = rgb2hex(gc.get_rgb()) - fontsize = prop.get_size_in_points() - - style = {} - if color != '#000000': - style['fill'] = color - alpha = gc.get_alpha() if gc.get_forced_alpha() else gc.get_rgb()[3] - if alpha != 1: - style['opacity'] = _short_float_fmt(alpha) - font_scale = fontsize / text2path.FONT_SCALE - attrib = { - 'style': _generate_css(style), - 'transform': _generate_transform([ - ('translate', (x, y)), - ('rotate', (-angle,)), - ('scale', (font_scale, -font_scale))]), - } - writer.start('g', attrib=attrib) - - if not ismath: - font = text2path._get_font(prop) - _glyphs = text2path.get_glyphs_with_font( - font, s, glyph_map=glyph_map, return_new_glyphs_only=True) - glyph_info, glyph_map_new, rects = _glyphs - self._update_glyph_map_defs(glyph_map_new) - - for glyph_id, xposition, yposition, scale in glyph_info: - attrib = {'xlink:href': '#%s' % glyph_id} - if xposition != 0.0: - attrib['x'] = _short_float_fmt(xposition) - if yposition != 0.0: - attrib['y'] = _short_float_fmt(yposition) - writer.element('use', attrib=attrib) - - else: - if ismath == "TeX": - _glyphs = text2path.get_glyphs_tex( - prop, s, glyph_map=glyph_map, return_new_glyphs_only=True) - else: - _glyphs = text2path.get_glyphs_mathtext( - prop, s, glyph_map=glyph_map, return_new_glyphs_only=True) - glyph_info, glyph_map_new, rects = _glyphs - self._update_glyph_map_defs(glyph_map_new) - - for char_id, xposition, yposition, scale in glyph_info: - char_id = self._adjust_char_id(char_id) - writer.element( - 'use', - transform=_generate_transform([ - ('translate', (xposition, yposition)), - ('scale', (scale,)), - ]), - attrib={'xlink:href': '#%s' % char_id}) - - for verts, codes in rects: - path = Path(verts, codes) - path_data = self._convert_path(path, simplify=False) - writer.element('path', d=path_data) - - writer.end('g') - - def _draw_text_as_text(self, gc, x, y, s, prop, angle, ismath, mtext=None): - writer = self.writer - - color = rgb2hex(gc.get_rgb()) - style = {} - if color != '#000000': - style['fill'] = color - - alpha = gc.get_alpha() if gc.get_forced_alpha() else gc.get_rgb()[3] - if alpha != 1: - style['opacity'] = _short_float_fmt(alpha) - - if not ismath: - attrib = {} - - font_parts = [] - if prop.get_style() != 'normal': - font_parts.append(prop.get_style()) - if prop.get_variant() != 'normal': - font_parts.append(prop.get_variant()) - weight = fm.weight_dict[prop.get_weight()] - if weight != 400: - font_parts.append(f'{weight}') - - def _normalize_sans(name): - return 'sans-serif' if name in ['sans', 'sans serif'] else name - - def _expand_family_entry(fn): - fn = _normalize_sans(fn) - # prepend generic font families with all configured font names - if fn in fm.font_family_aliases: - # get all of the font names and fix spelling of sans-serif - # (we accept 3 ways CSS only supports 1) - for name in fm.FontManager._expand_aliases(fn): - yield _normalize_sans(name) - # whether a generic name or a family name, it must appear at - # least once - yield fn - - def _get_all_quoted_names(prop): - # only quote specific names, not generic names - return [name if name in fm.font_family_aliases else repr(name) - for entry in prop.get_family() - for name in _expand_family_entry(entry)] - - font_parts.extend([ - f'{_short_float_fmt(prop.get_size())}px', - # ensure expansion, quoting, and dedupe of font names - ", ".join(dict.fromkeys(_get_all_quoted_names(prop))) - ]) - style['font'] = ' '.join(font_parts) - if prop.get_stretch() != 'normal': - style['font-stretch'] = prop.get_stretch() - attrib['style'] = _generate_css(style) - - if mtext and (angle == 0 or mtext.get_rotation_mode() == "anchor"): - # If text anchoring can be supported, get the original - # coordinates and add alignment information. - - # Get anchor coordinates. - transform = mtext.get_transform() - ax, ay = transform.transform(mtext.get_unitless_position()) - ay = self.height - ay - - # Don't do vertical anchor alignment. Most applications do not - # support 'alignment-baseline' yet. Apply the vertical layout - # to the anchor point manually for now. - angle_rad = np.deg2rad(angle) - dir_vert = np.array([np.sin(angle_rad), np.cos(angle_rad)]) - v_offset = np.dot(dir_vert, [(x - ax), (y - ay)]) - ax = ax + v_offset * dir_vert[0] - ay = ay + v_offset * dir_vert[1] - - ha_mpl_to_svg = {'left': 'start', 'right': 'end', - 'center': 'middle'} - style['text-anchor'] = ha_mpl_to_svg[mtext.get_ha()] - - attrib['x'] = _short_float_fmt(ax) - attrib['y'] = _short_float_fmt(ay) - attrib['style'] = _generate_css(style) - attrib['transform'] = _generate_transform([ - ("rotate", (-angle, ax, ay))]) - - else: - attrib['transform'] = _generate_transform([ - ('translate', (x, y)), - ('rotate', (-angle,))]) - - writer.element('text', s, attrib=attrib) - - else: - writer.comment(s) - - width, height, descent, glyphs, rects = \ - self._text2path.mathtext_parser.parse(s, 72, prop) - - # Apply attributes to 'g', not 'text', because we likely have some - # rectangles as well with the same style and transformation. - writer.start('g', - style=_generate_css(style), - transform=_generate_transform([ - ('translate', (x, y)), - ('rotate', (-angle,))]), - ) - - writer.start('text') - - # Sort the characters by font, and output one tspan for each. - spans = {} - for font, fontsize, thetext, new_x, new_y in glyphs: - entry = fm.ttfFontProperty(font) - font_parts = [] - if entry.style != 'normal': - font_parts.append(entry.style) - if entry.variant != 'normal': - font_parts.append(entry.variant) - if entry.weight != 400: - font_parts.append(f'{entry.weight}') - font_parts.extend([ - f'{_short_float_fmt(fontsize)}px', - f'{entry.name!r}', # ensure quoting - ]) - style = {'font': ' '.join(font_parts)} - if entry.stretch != 'normal': - style['font-stretch'] = entry.stretch - style = _generate_css(style) - if thetext == 32: - thetext = 0xa0 # non-breaking space - spans.setdefault(style, []).append((new_x, -new_y, thetext)) - - for style, chars in spans.items(): - chars.sort() - - if len({y for x, y, t in chars}) == 1: # Are all y's the same? - ys = str(chars[0][1]) - else: - ys = ' '.join(str(c[1]) for c in chars) - - attrib = { - 'style': style, - 'x': ' '.join(_short_float_fmt(c[0]) for c in chars), - 'y': ys - } - - writer.element( - 'tspan', - ''.join(chr(c[2]) for c in chars), - attrib=attrib) - - writer.end('text') - - for x, y, width, height in rects: - writer.element( - 'rect', - x=_short_float_fmt(x), - y=_short_float_fmt(-y-1), - width=_short_float_fmt(width), - height=_short_float_fmt(height) - ) - - writer.end('g') - - def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): - # docstring inherited - - clip_attrs = self._get_clip_attrs(gc) - if clip_attrs: - # Cannot apply clip-path directly to the text, because - # it has a transformation - self.writer.start('g', **clip_attrs) - - if gc.get_url() is not None: - self.writer.start('a', {'xlink:href': gc.get_url()}) - - if mpl.rcParams['svg.fonttype'] == 'path': - self._draw_text_as_path(gc, x, y, s, prop, angle, ismath, mtext) - else: - self._draw_text_as_text(gc, x, y, s, prop, angle, ismath, mtext) - - if gc.get_url() is not None: - self.writer.end('a') - - if clip_attrs: - self.writer.end('g') - - def flipy(self): - # docstring inherited - return True - - def get_canvas_width_height(self): - # docstring inherited - return self.width, self.height - - def get_text_width_height_descent(self, s, prop, ismath): - # docstring inherited - return self._text2path.get_text_width_height_descent(s, prop, ismath) - - -class FigureCanvasSVG(FigureCanvasBase): - filetypes = {'svg': 'Scalable Vector Graphics', - 'svgz': 'Scalable Vector Graphics'} - - fixed_dpi = 72 - - def print_svg(self, filename, *, bbox_inches_restore=None, metadata=None): - """ - Parameters - ---------- - filename : str or path-like or file-like - Output target; if a string, a file will be opened for writing. - - metadata : dict[str, Any], optional - Metadata in the SVG file defined as key-value pairs of strings, - datetimes, or lists of strings, e.g., ``{'Creator': 'My software', - 'Contributor': ['Me', 'My Friend'], 'Title': 'Awesome'}``. - - The standard keys and their value types are: - - * *str*: ``'Coverage'``, ``'Description'``, ``'Format'``, - ``'Identifier'``, ``'Language'``, ``'Relation'``, ``'Source'``, - ``'Title'``, and ``'Type'``. - * *str* or *list of str*: ``'Contributor'``, ``'Creator'``, - ``'Keywords'``, ``'Publisher'``, and ``'Rights'``. - * *str*, *date*, *datetime*, or *tuple* of same: ``'Date'``. If a - non-*str*, then it will be formatted as ISO 8601. - - Values have been predefined for ``'Creator'``, ``'Date'``, - ``'Format'``, and ``'Type'``. They can be removed by setting them - to `None`. - - Information is encoded as `Dublin Core Metadata`__. - - .. _DC: https://www.dublincore.org/specifications/dublin-core/ - - __ DC_ - """ - with cbook.open_file_cm(filename, "w", encoding="utf-8") as fh: - if not cbook.file_requires_unicode(fh): - fh = codecs.getwriter('utf-8')(fh) - dpi = self.figure.dpi - self.figure.dpi = 72 - width, height = self.figure.get_size_inches() - w, h = width * 72, height * 72 - renderer = MixedModeRenderer( - self.figure, width, height, dpi, - RendererSVG(w, h, fh, image_dpi=dpi, metadata=metadata), - bbox_inches_restore=bbox_inches_restore) - self.figure.draw(renderer) - renderer.finalize() - - def print_svgz(self, filename, **kwargs): - with cbook.open_file_cm(filename, "wb") as fh, \ - gzip.GzipFile(mode='w', fileobj=fh) as gzipwriter: - return self.print_svg(gzipwriter, **kwargs) - - def get_default_filetype(self): - return 'svg' - - def draw(self): - self.figure.draw_without_rendering() - return super().draw() - - -FigureManagerSVG = FigureManagerBase - - -svgProlog = """\ - - -""" - - -@_Backend.export -class _BackendSVG(_Backend): - backend_version = mpl.__version__ - FigureCanvas = FigureCanvasSVG diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_template.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_template.py deleted file mode 100644 index d997ec1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_template.py +++ /dev/null @@ -1,213 +0,0 @@ -""" -A fully functional, do-nothing backend intended as a template for backend -writers. It is fully functional in that you can select it as a backend e.g. -with :: - - import matplotlib - matplotlib.use("template") - -and your program will (should!) run without error, though no output is -produced. This provides a starting point for backend writers; you can -selectively implement drawing methods (`~.RendererTemplate.draw_path`, -`~.RendererTemplate.draw_image`, etc.) and slowly see your figure come to life -instead having to have a full-blown implementation before getting any results. - -Copy this file to a directory outside the Matplotlib source tree, somewhere -where Python can import it (by adding the directory to your ``sys.path`` or by -packaging it as a normal Python package); if the backend is importable as -``import my.backend`` you can then select it using :: - - import matplotlib - matplotlib.use("module://my.backend") - -If your backend implements support for saving figures (i.e. has a `print_xyz` -method), you can register it as the default handler for a given file type:: - - from matplotlib.backend_bases import register_backend - register_backend('xyz', 'my_backend', 'XYZ File Format') - ... - plt.savefig("figure.xyz") -""" - -from matplotlib import _api -from matplotlib._pylab_helpers import Gcf -from matplotlib.backend_bases import ( - FigureCanvasBase, FigureManagerBase, GraphicsContextBase, RendererBase) -from matplotlib.figure import Figure - - -class RendererTemplate(RendererBase): - """ - The renderer handles drawing/rendering operations. - - This is a minimal do-nothing class that can be used to get started when - writing a new backend. Refer to `.backend_bases.RendererBase` for - documentation of the methods. - """ - - def __init__(self, dpi): - super().__init__() - self.dpi = dpi - - def draw_path(self, gc, path, transform, rgbFace=None): - pass - - # draw_markers is optional, and we get more correct relative - # timings by leaving it out. backend implementers concerned with - # performance will probably want to implement it -# def draw_markers(self, gc, marker_path, marker_trans, path, trans, -# rgbFace=None): -# pass - - # draw_path_collection is optional, and we get more correct - # relative timings by leaving it out. backend implementers concerned with - # performance will probably want to implement it -# def draw_path_collection(self, gc, master_transform, paths, -# all_transforms, offsets, offset_trans, -# facecolors, edgecolors, linewidths, linestyles, -# antialiaseds): -# pass - - # draw_quad_mesh is optional, and we get more correct - # relative timings by leaving it out. backend implementers concerned with - # performance will probably want to implement it -# def draw_quad_mesh(self, gc, master_transform, meshWidth, meshHeight, -# coordinates, offsets, offsetTrans, facecolors, -# antialiased, edgecolors): -# pass - - def draw_image(self, gc, x, y, im): - pass - - def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): - pass - - def flipy(self): - # docstring inherited - return True - - def get_canvas_width_height(self): - # docstring inherited - return 100, 100 - - def get_text_width_height_descent(self, s, prop, ismath): - return 1, 1, 1 - - def new_gc(self): - # docstring inherited - return GraphicsContextTemplate() - - def points_to_pixels(self, points): - # if backend doesn't have dpi, e.g., postscript or svg - return points - # elif backend assumes a value for pixels_per_inch - # return points/72.0 * self.dpi.get() * pixels_per_inch/72.0 - # else - # return points/72.0 * self.dpi.get() - - -class GraphicsContextTemplate(GraphicsContextBase): - """ - The graphics context provides the color, line styles, etc. See the cairo - and postscript backends for examples of mapping the graphics context - attributes (cap styles, join styles, line widths, colors) to a particular - backend. In cairo this is done by wrapping a cairo.Context object and - forwarding the appropriate calls to it using a dictionary mapping styles - to gdk constants. In Postscript, all the work is done by the renderer, - mapping line styles to postscript calls. - - If it's more appropriate to do the mapping at the renderer level (as in - the postscript backend), you don't need to override any of the GC methods. - If it's more appropriate to wrap an instance (as in the cairo backend) and - do the mapping here, you'll need to override several of the setter - methods. - - The base GraphicsContext stores colors as an RGB tuple on the unit - interval, e.g., (0.5, 0.0, 1.0). You may need to map this to colors - appropriate for your backend. - """ - - -######################################################################## -# -# The following functions and classes are for pyplot and implement -# window/figure managers, etc. -# -######################################################################## - - -class FigureManagerTemplate(FigureManagerBase): - """ - Helper class for pyplot mode, wraps everything up into a neat bundle. - - For non-interactive backends, the base class is sufficient. For - interactive backends, see the documentation of the `.FigureManagerBase` - class for the list of methods that can/should be overridden. - """ - - -class FigureCanvasTemplate(FigureCanvasBase): - """ - The canvas the figure renders into. Calls the draw and print fig - methods, creates the renderers, etc. - - Note: GUI templates will want to connect events for button presses, - mouse movements and key presses to functions that call the base - class methods button_press_event, button_release_event, - motion_notify_event, key_press_event, and key_release_event. See the - implementations of the interactive backends for examples. - - Attributes - ---------- - figure : `~matplotlib.figure.Figure` - A high-level Figure instance - """ - - # The instantiated manager class. For further customization, - # ``FigureManager.create_with_canvas`` can also be overridden; see the - # wx-based backends for an example. - manager_class = FigureManagerTemplate - - def draw(self): - """ - Draw the figure using the renderer. - - It is important that this method actually walk the artist tree - even if not output is produced because this will trigger - deferred work (like computing limits auto-limits and tick - values) that users may want access to before saving to disk. - """ - renderer = RendererTemplate(self.figure.dpi) - self.figure.draw(renderer) - - # You should provide a print_xxx function for every file format - # you can write. - - # If the file type is not in the base set of filetypes, - # you should add it to the class-scope filetypes dictionary as follows: - filetypes = {**FigureCanvasBase.filetypes, 'foo': 'My magic Foo format'} - - def print_foo(self, filename, **kwargs): - """ - Write out format foo. - - This method is normally called via `.Figure.savefig` and - `.FigureCanvasBase.print_figure`, which take care of setting the figure - facecolor, edgecolor, and dpi to the desired output values, and will - restore them to the original values. Therefore, `print_foo` does not - need to handle these settings. - """ - self.draw() - - def get_default_filetype(self): - return 'foo' - - -######################################################################## -# -# Now just provide the standard names that backend.__init__ is expecting -# -######################################################################## - -FigureCanvas = FigureCanvasTemplate -FigureManager = FigureManagerTemplate diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_tkagg.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_tkagg.py deleted file mode 100644 index f95b601..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_tkagg.py +++ /dev/null @@ -1,20 +0,0 @@ -from . import _backend_tk -from .backend_agg import FigureCanvasAgg -from ._backend_tk import _BackendTk, FigureCanvasTk -from ._backend_tk import ( # noqa: F401 # pylint: disable=W0611 - FigureManagerTk, NavigationToolbar2Tk) - - -class FigureCanvasTkAgg(FigureCanvasAgg, FigureCanvasTk): - def draw(self): - super().draw() - self.blit() - - def blit(self, bbox=None): - _backend_tk.blit(self._tkphoto, self.renderer.buffer_rgba(), - (0, 1, 2, 3), bbox=bbox) - - -@_BackendTk.export -class _BackendTkAgg(_BackendTk): - FigureCanvas = FigureCanvasTkAgg diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_tkcairo.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_tkcairo.py deleted file mode 100644 index a6951c0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_tkcairo.py +++ /dev/null @@ -1,26 +0,0 @@ -import sys - -import numpy as np - -from . import _backend_tk -from .backend_cairo import cairo, FigureCanvasCairo -from ._backend_tk import _BackendTk, FigureCanvasTk - - -class FigureCanvasTkCairo(FigureCanvasCairo, FigureCanvasTk): - def draw(self): - width = int(self.figure.bbox.width) - height = int(self.figure.bbox.height) - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) - self._renderer.set_context(cairo.Context(surface)) - self._renderer.dpi = self.figure.dpi - self.figure.draw(self._renderer) - buf = np.reshape(surface.get_data(), (height, width, 4)) - _backend_tk.blit( - self._tkphoto, buf, - (2, 1, 0, 3) if sys.byteorder == "little" else (1, 2, 3, 0)) - - -@_BackendTk.export -class _BackendTkCairo(_BackendTk): - FigureCanvas = FigureCanvasTkCairo diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_webagg.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_webagg.py deleted file mode 100644 index 17c12c0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_webagg.py +++ /dev/null @@ -1,339 +0,0 @@ -""" -Displays Agg images in the browser, with interactivity -""" - -# The WebAgg backend is divided into two modules: -# -# - `backend_webagg_core.py` contains code necessary to embed a WebAgg -# plot inside of a web application, and communicate in an abstract -# way over a web socket. -# -# - `backend_webagg.py` contains a concrete implementation of a basic -# application, implemented with tornado. - -from contextlib import contextmanager -import errno -from io import BytesIO -import json -import mimetypes -from pathlib import Path -import random -import sys -import signal -import socket -import threading - -try: - import tornado -except ImportError as err: - raise RuntimeError("The WebAgg backend requires Tornado.") from err - -import tornado.web -import tornado.ioloop -import tornado.websocket - -import matplotlib as mpl -from matplotlib.backend_bases import _Backend -from matplotlib._pylab_helpers import Gcf -from . import backend_webagg_core as core -from .backend_webagg_core import ( # noqa: F401 # pylint: disable=W0611 - TimerAsyncio, TimerTornado) - - -@mpl._api.deprecated("3.7") -class ServerThread(threading.Thread): - def run(self): - tornado.ioloop.IOLoop.instance().start() - - -webagg_server_thread = threading.Thread( - target=lambda: tornado.ioloop.IOLoop.instance().start()) - - -class FigureManagerWebAgg(core.FigureManagerWebAgg): - _toolbar2_class = core.NavigationToolbar2WebAgg - - @classmethod - def pyplot_show(cls, *, block=None): - WebAggApplication.initialize() - - url = "http://{address}:{port}{prefix}".format( - address=WebAggApplication.address, - port=WebAggApplication.port, - prefix=WebAggApplication.url_prefix) - - if mpl.rcParams['webagg.open_in_browser']: - import webbrowser - if not webbrowser.open(url): - print("To view figure, visit {0}".format(url)) - else: - print("To view figure, visit {0}".format(url)) - - WebAggApplication.start() - - -class FigureCanvasWebAgg(core.FigureCanvasWebAggCore): - manager_class = FigureManagerWebAgg - - -class WebAggApplication(tornado.web.Application): - initialized = False - started = False - - class FavIcon(tornado.web.RequestHandler): - def get(self): - self.set_header('Content-Type', 'image/png') - self.write(Path(mpl.get_data_path(), - 'images/matplotlib.png').read_bytes()) - - class SingleFigurePage(tornado.web.RequestHandler): - def __init__(self, application, request, *, url_prefix='', **kwargs): - self.url_prefix = url_prefix - super().__init__(application, request, **kwargs) - - def get(self, fignum): - fignum = int(fignum) - manager = Gcf.get_fig_manager(fignum) - - ws_uri = 'ws://{req.host}{prefix}/'.format(req=self.request, - prefix=self.url_prefix) - self.render( - "single_figure.html", - prefix=self.url_prefix, - ws_uri=ws_uri, - fig_id=fignum, - toolitems=core.NavigationToolbar2WebAgg.toolitems, - canvas=manager.canvas) - - class AllFiguresPage(tornado.web.RequestHandler): - def __init__(self, application, request, *, url_prefix='', **kwargs): - self.url_prefix = url_prefix - super().__init__(application, request, **kwargs) - - def get(self): - ws_uri = 'ws://{req.host}{prefix}/'.format(req=self.request, - prefix=self.url_prefix) - self.render( - "all_figures.html", - prefix=self.url_prefix, - ws_uri=ws_uri, - figures=sorted(Gcf.figs.items()), - toolitems=core.NavigationToolbar2WebAgg.toolitems) - - class MplJs(tornado.web.RequestHandler): - def get(self): - self.set_header('Content-Type', 'application/javascript') - - js_content = core.FigureManagerWebAgg.get_javascript() - - self.write(js_content) - - class Download(tornado.web.RequestHandler): - def get(self, fignum, fmt): - fignum = int(fignum) - manager = Gcf.get_fig_manager(fignum) - self.set_header( - 'Content-Type', mimetypes.types_map.get(fmt, 'binary')) - buff = BytesIO() - manager.canvas.figure.savefig(buff, format=fmt) - self.write(buff.getvalue()) - - class WebSocket(tornado.websocket.WebSocketHandler): - supports_binary = True - - def open(self, fignum): - self.fignum = int(fignum) - self.manager = Gcf.get_fig_manager(self.fignum) - self.manager.add_web_socket(self) - if hasattr(self, 'set_nodelay'): - self.set_nodelay(True) - - def on_close(self): - self.manager.remove_web_socket(self) - - def on_message(self, message): - message = json.loads(message) - # The 'supports_binary' message is on a client-by-client - # basis. The others affect the (shared) canvas as a - # whole. - if message['type'] == 'supports_binary': - self.supports_binary = message['value'] - else: - manager = Gcf.get_fig_manager(self.fignum) - # It is possible for a figure to be closed, - # but a stale figure UI is still sending messages - # from the browser. - if manager is not None: - manager.handle_json(message) - - def send_json(self, content): - self.write_message(json.dumps(content)) - - def send_binary(self, blob): - if self.supports_binary: - self.write_message(blob, binary=True) - else: - data_uri = "data:image/png;base64,{0}".format( - blob.encode('base64').replace('\n', '')) - self.write_message(data_uri) - - def __init__(self, url_prefix=''): - if url_prefix: - assert url_prefix[0] == '/' and url_prefix[-1] != '/', \ - 'url_prefix must start with a "/" and not end with one.' - - super().__init__( - [ - # Static files for the CSS and JS - (url_prefix + r'/_static/(.*)', - tornado.web.StaticFileHandler, - {'path': core.FigureManagerWebAgg.get_static_file_path()}), - - # Static images for the toolbar - (url_prefix + r'/_images/(.*)', - tornado.web.StaticFileHandler, - {'path': Path(mpl.get_data_path(), 'images')}), - - # A Matplotlib favicon - (url_prefix + r'/favicon.ico', self.FavIcon), - - # The page that contains all of the pieces - (url_prefix + r'/([0-9]+)', self.SingleFigurePage, - {'url_prefix': url_prefix}), - - # The page that contains all of the figures - (url_prefix + r'/?', self.AllFiguresPage, - {'url_prefix': url_prefix}), - - (url_prefix + r'/js/mpl.js', self.MplJs), - - # Sends images and events to the browser, and receives - # events from the browser - (url_prefix + r'/([0-9]+)/ws', self.WebSocket), - - # Handles the downloading (i.e., saving) of static images - (url_prefix + r'/([0-9]+)/download.([a-z0-9.]+)', - self.Download), - ], - template_path=core.FigureManagerWebAgg.get_static_file_path()) - - @classmethod - def initialize(cls, url_prefix='', port=None, address=None): - if cls.initialized: - return - - # Create the class instance - app = cls(url_prefix=url_prefix) - - cls.url_prefix = url_prefix - - # This port selection algorithm is borrowed, more or less - # verbatim, from IPython. - def random_ports(port, n): - """ - Generate a list of n random ports near the given port. - - The first 5 ports will be sequential, and the remaining n-5 will be - randomly selected in the range [port-2*n, port+2*n]. - """ - for i in range(min(5, n)): - yield port + i - for i in range(n - 5): - yield port + random.randint(-2 * n, 2 * n) - - if address is None: - cls.address = mpl.rcParams['webagg.address'] - else: - cls.address = address - cls.port = mpl.rcParams['webagg.port'] - for port in random_ports(cls.port, - mpl.rcParams['webagg.port_retries']): - try: - app.listen(port, cls.address) - except socket.error as e: - if e.errno != errno.EADDRINUSE: - raise - else: - cls.port = port - break - else: - raise SystemExit( - "The webagg server could not be started because an available " - "port could not be found") - - cls.initialized = True - - @classmethod - def start(cls): - import asyncio - try: - asyncio.get_running_loop() - except RuntimeError: - pass - else: - cls.started = True - - if cls.started: - return - - """ - IOLoop.running() was removed as of Tornado 2.4; see for example - https://groups.google.com/forum/#!topic/python-tornado/QLMzkpQBGOY - Thus there is no correct way to check if the loop has already been - launched. We may end up with two concurrently running loops in that - unlucky case with all the expected consequences. - """ - ioloop = tornado.ioloop.IOLoop.instance() - - def shutdown(): - ioloop.stop() - print("Server is stopped") - sys.stdout.flush() - cls.started = False - - @contextmanager - def catch_sigint(): - old_handler = signal.signal( - signal.SIGINT, - lambda sig, frame: ioloop.add_callback_from_signal(shutdown)) - try: - yield - finally: - signal.signal(signal.SIGINT, old_handler) - - # Set the flag to True *before* blocking on ioloop.start() - cls.started = True - - print("Press Ctrl+C to stop WebAgg server") - sys.stdout.flush() - with catch_sigint(): - ioloop.start() - - -def ipython_inline_display(figure): - import tornado.template - - WebAggApplication.initialize() - import asyncio - try: - asyncio.get_running_loop() - except RuntimeError: - if not webagg_server_thread.is_alive(): - webagg_server_thread.start() - - fignum = figure.number - tpl = Path(core.FigureManagerWebAgg.get_static_file_path(), - "ipython_inline_figure.html").read_text() - t = tornado.template.Template(tpl) - return t.generate( - prefix=WebAggApplication.url_prefix, - fig_id=fignum, - toolitems=core.NavigationToolbar2WebAgg.toolitems, - canvas=figure.canvas, - port=WebAggApplication.port).decode('utf-8') - - -@_Backend.export -class _BackendWebAgg(_Backend): - FigureCanvas = FigureCanvasWebAgg - FigureManager = FigureManagerWebAgg diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_webagg_core.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_webagg_core.py deleted file mode 100644 index 57cfa31..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_webagg_core.py +++ /dev/null @@ -1,517 +0,0 @@ -""" -Displays Agg images in the browser, with interactivity -""" -# The WebAgg backend is divided into two modules: -# -# - `backend_webagg_core.py` contains code necessary to embed a WebAgg -# plot inside of a web application, and communicate in an abstract -# way over a web socket. -# -# - `backend_webagg.py` contains a concrete implementation of a basic -# application, implemented with asyncio. - -import asyncio -import datetime -from io import BytesIO, StringIO -import json -import logging -import os -from pathlib import Path - -import numpy as np -from PIL import Image - -from matplotlib import _api, backend_bases, backend_tools -from matplotlib.backends import backend_agg -from matplotlib.backend_bases import ( - _Backend, KeyEvent, LocationEvent, MouseEvent, ResizeEvent) - -_log = logging.getLogger(__name__) - -_SPECIAL_KEYS_LUT = {'Alt': 'alt', - 'AltGraph': 'alt', - 'CapsLock': 'caps_lock', - 'Control': 'control', - 'Meta': 'meta', - 'NumLock': 'num_lock', - 'ScrollLock': 'scroll_lock', - 'Shift': 'shift', - 'Super': 'super', - 'Enter': 'enter', - 'Tab': 'tab', - 'ArrowDown': 'down', - 'ArrowLeft': 'left', - 'ArrowRight': 'right', - 'ArrowUp': 'up', - 'End': 'end', - 'Home': 'home', - 'PageDown': 'pagedown', - 'PageUp': 'pageup', - 'Backspace': 'backspace', - 'Delete': 'delete', - 'Insert': 'insert', - 'Escape': 'escape', - 'Pause': 'pause', - 'Select': 'select', - 'Dead': 'dead', - 'F1': 'f1', - 'F2': 'f2', - 'F3': 'f3', - 'F4': 'f4', - 'F5': 'f5', - 'F6': 'f6', - 'F7': 'f7', - 'F8': 'f8', - 'F9': 'f9', - 'F10': 'f10', - 'F11': 'f11', - 'F12': 'f12'} - - -def _handle_key(key): - """Handle key values""" - value = key[key.index('k') + 1:] - if 'shift+' in key: - if len(value) == 1: - key = key.replace('shift+', '') - if value in _SPECIAL_KEYS_LUT: - value = _SPECIAL_KEYS_LUT[value] - key = key[:key.index('k')] + value - return key - - -class TimerTornado(backend_bases.TimerBase): - def __init__(self, *args, **kwargs): - self._timer = None - super().__init__(*args, **kwargs) - - def _timer_start(self): - import tornado - - self._timer_stop() - if self._single: - ioloop = tornado.ioloop.IOLoop.instance() - self._timer = ioloop.add_timeout( - datetime.timedelta(milliseconds=self.interval), - self._on_timer) - else: - self._timer = tornado.ioloop.PeriodicCallback( - self._on_timer, - max(self.interval, 1e-6)) - self._timer.start() - - def _timer_stop(self): - import tornado - - if self._timer is None: - return - elif self._single: - ioloop = tornado.ioloop.IOLoop.instance() - ioloop.remove_timeout(self._timer) - else: - self._timer.stop() - self._timer = None - - def _timer_set_interval(self): - # Only stop and restart it if the timer has already been started - if self._timer is not None: - self._timer_stop() - self._timer_start() - - -class TimerAsyncio(backend_bases.TimerBase): - def __init__(self, *args, **kwargs): - self._task = None - super().__init__(*args, **kwargs) - - async def _timer_task(self, interval): - while True: - try: - await asyncio.sleep(interval) - self._on_timer() - - if self._single: - break - except asyncio.CancelledError: - break - - def _timer_start(self): - self._timer_stop() - - self._task = asyncio.ensure_future( - self._timer_task(max(self.interval / 1_000., 1e-6)) - ) - - def _timer_stop(self): - if self._task is not None: - self._task.cancel() - self._task = None - - def _timer_set_interval(self): - # Only stop and restart it if the timer has already been started - if self._task is not None: - self._timer_stop() - self._timer_start() - - -class FigureCanvasWebAggCore(backend_agg.FigureCanvasAgg): - manager_class = _api.classproperty(lambda cls: FigureManagerWebAgg) - _timer_cls = TimerAsyncio - # Webagg and friends having the right methods, but still - # having bugs in practice. Do not advertise that it works until - # we can debug this. - supports_blit = False - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - # Set to True when the renderer contains data that is newer - # than the PNG buffer. - self._png_is_old = True - # Set to True by the `refresh` message so that the next frame - # sent to the clients will be a full frame. - self._force_full = True - # The last buffer, for diff mode. - self._last_buff = np.empty((0, 0)) - # Store the current image mode so that at any point, clients can - # request the information. This should be changed by calling - # self.set_image_mode(mode) so that the notification can be given - # to the connected clients. - self._current_image_mode = 'full' - # Track mouse events to fill in the x, y position of key events. - self._last_mouse_xy = (None, None) - - def show(self): - # show the figure window - from matplotlib.pyplot import show - show() - - def draw(self): - self._png_is_old = True - try: - super().draw() - finally: - self.manager.refresh_all() # Swap the frames. - - def blit(self, bbox=None): - self._png_is_old = True - self.manager.refresh_all() - - def draw_idle(self): - self.send_event("draw") - - def set_cursor(self, cursor): - # docstring inherited - cursor = _api.check_getitem({ - backend_tools.Cursors.HAND: 'pointer', - backend_tools.Cursors.POINTER: 'default', - backend_tools.Cursors.SELECT_REGION: 'crosshair', - backend_tools.Cursors.MOVE: 'move', - backend_tools.Cursors.WAIT: 'wait', - backend_tools.Cursors.RESIZE_HORIZONTAL: 'ew-resize', - backend_tools.Cursors.RESIZE_VERTICAL: 'ns-resize', - }, cursor=cursor) - self.send_event('cursor', cursor=cursor) - - def set_image_mode(self, mode): - """ - Set the image mode for any subsequent images which will be sent - to the clients. The modes may currently be either 'full' or 'diff'. - - Note: diff images may not contain transparency, therefore upon - draw this mode may be changed if the resulting image has any - transparent component. - """ - _api.check_in_list(['full', 'diff'], mode=mode) - if self._current_image_mode != mode: - self._current_image_mode = mode - self.handle_send_image_mode(None) - - def get_diff_image(self): - if self._png_is_old: - renderer = self.get_renderer() - - pixels = np.asarray(renderer.buffer_rgba()) - # The buffer is created as type uint32 so that entire - # pixels can be compared in one numpy call, rather than - # needing to compare each plane separately. - buff = pixels.view(np.uint32).squeeze(2) - - if (self._force_full - # If the buffer has changed size we need to do a full draw. - or buff.shape != self._last_buff.shape - # If any pixels have transparency, we need to force a full - # draw as we cannot overlay new on top of old. - or (pixels[:, :, 3] != 255).any()): - self.set_image_mode('full') - output = buff - else: - self.set_image_mode('diff') - diff = buff != self._last_buff - output = np.where(diff, buff, 0) - - # Store the current buffer so we can compute the next diff. - self._last_buff = buff.copy() - self._force_full = False - self._png_is_old = False - - data = output.view(dtype=np.uint8).reshape((*output.shape, 4)) - with BytesIO() as png: - Image.fromarray(data).save(png, format="png") - return png.getvalue() - - def handle_event(self, event): - e_type = event['type'] - handler = getattr(self, 'handle_{0}'.format(e_type), - self.handle_unknown_event) - return handler(event) - - def handle_unknown_event(self, event): - _log.warning('Unhandled message type {0}. {1}'.format( - event['type'], event)) - - def handle_ack(self, event): - # Network latency tends to decrease if traffic is flowing - # in both directions. Therefore, the browser sends back - # an "ack" message after each image frame is received. - # This could also be used as a simple sanity check in the - # future, but for now the performance increase is enough - # to justify it, even if the server does nothing with it. - pass - - def handle_draw(self, event): - self.draw() - - def _handle_mouse(self, event): - x = event['x'] - y = event['y'] - y = self.get_renderer().height - y - self._last_mouse_xy = x, y - # JavaScript button numbers and Matplotlib button numbers are off by 1. - button = event['button'] + 1 - - e_type = event['type'] - modifiers = event['modifiers'] - guiEvent = event.get('guiEvent') - if e_type in ['button_press', 'button_release']: - MouseEvent(e_type + '_event', self, x, y, button, - modifiers=modifiers, guiEvent=guiEvent)._process() - elif e_type == 'dblclick': - MouseEvent('button_press_event', self, x, y, button, dblclick=True, - modifiers=modifiers, guiEvent=guiEvent)._process() - elif e_type == 'scroll': - MouseEvent('scroll_event', self, x, y, step=event['step'], - modifiers=modifiers, guiEvent=guiEvent)._process() - elif e_type == 'motion_notify': - MouseEvent(e_type + '_event', self, x, y, - modifiers=modifiers, guiEvent=guiEvent)._process() - elif e_type in ['figure_enter', 'figure_leave']: - LocationEvent(e_type + '_event', self, x, y, - modifiers=modifiers, guiEvent=guiEvent)._process() - handle_button_press = handle_button_release = handle_dblclick = \ - handle_figure_enter = handle_figure_leave = handle_motion_notify = \ - handle_scroll = _handle_mouse - - def _handle_key(self, event): - KeyEvent(event['type'] + '_event', self, - _handle_key(event['key']), *self._last_mouse_xy, - guiEvent=event.get('guiEvent'))._process() - handle_key_press = handle_key_release = _handle_key - - def handle_toolbar_button(self, event): - # TODO: Be more suspicious of the input - getattr(self.toolbar, event['name'])() - - def handle_refresh(self, event): - figure_label = self.figure.get_label() - if not figure_label: - figure_label = "Figure {0}".format(self.manager.num) - self.send_event('figure_label', label=figure_label) - self._force_full = True - if self.toolbar: - # Normal toolbar init would refresh this, but it happens before the - # browser canvas is set up. - self.toolbar.set_history_buttons() - self.draw_idle() - - def handle_resize(self, event): - x = int(event.get('width', 800)) * self.device_pixel_ratio - y = int(event.get('height', 800)) * self.device_pixel_ratio - fig = self.figure - # An attempt at approximating the figure size in pixels. - fig.set_size_inches(x / fig.dpi, y / fig.dpi, forward=False) - # Acknowledge the resize, and force the viewer to update the - # canvas size to the figure's new size (which is hopefully - # identical or within a pixel or so). - self._png_is_old = True - self.manager.resize(*fig.bbox.size, forward=False) - ResizeEvent('resize_event', self)._process() - self.draw_idle() - - def handle_send_image_mode(self, event): - # The client requests notification of what the current image mode is. - self.send_event('image_mode', mode=self._current_image_mode) - - def handle_set_device_pixel_ratio(self, event): - self._handle_set_device_pixel_ratio(event.get('device_pixel_ratio', 1)) - - def handle_set_dpi_ratio(self, event): - # This handler is for backwards-compatibility with older ipympl. - self._handle_set_device_pixel_ratio(event.get('dpi_ratio', 1)) - - def _handle_set_device_pixel_ratio(self, device_pixel_ratio): - if self._set_device_pixel_ratio(device_pixel_ratio): - self._force_full = True - self.draw_idle() - - def send_event(self, event_type, **kwargs): - if self.manager: - self.manager._send_event(event_type, **kwargs) - - -_ALLOWED_TOOL_ITEMS = { - 'home', - 'back', - 'forward', - 'pan', - 'zoom', - 'download', - None, -} - - -class NavigationToolbar2WebAgg(backend_bases.NavigationToolbar2): - - # Use the standard toolbar items + download button - toolitems = [ - (text, tooltip_text, image_file, name_of_method) - for text, tooltip_text, image_file, name_of_method - in (*backend_bases.NavigationToolbar2.toolitems, - ('Download', 'Download plot', 'filesave', 'download')) - if name_of_method in _ALLOWED_TOOL_ITEMS - ] - - def __init__(self, canvas): - self.message = '' - super().__init__(canvas) - - def set_message(self, message): - if message != self.message: - self.canvas.send_event("message", message=message) - self.message = message - - def draw_rubberband(self, event, x0, y0, x1, y1): - self.canvas.send_event("rubberband", x0=x0, y0=y0, x1=x1, y1=y1) - - def remove_rubberband(self): - self.canvas.send_event("rubberband", x0=-1, y0=-1, x1=-1, y1=-1) - - def save_figure(self, *args): - """Save the current figure""" - self.canvas.send_event('save') - - def pan(self): - super().pan() - self.canvas.send_event('navigate_mode', mode=self.mode.name) - - def zoom(self): - super().zoom() - self.canvas.send_event('navigate_mode', mode=self.mode.name) - - def set_history_buttons(self): - can_backward = self._nav_stack._pos > 0 - can_forward = self._nav_stack._pos < len(self._nav_stack._elements) - 1 - self.canvas.send_event('history_buttons', - Back=can_backward, Forward=can_forward) - - -class FigureManagerWebAgg(backend_bases.FigureManagerBase): - # This must be None to not break ipympl - _toolbar2_class = None - ToolbarCls = NavigationToolbar2WebAgg - - def __init__(self, canvas, num): - self.web_sockets = set() - super().__init__(canvas, num) - - def show(self): - pass - - def resize(self, w, h, forward=True): - self._send_event( - 'resize', - size=(w / self.canvas.device_pixel_ratio, - h / self.canvas.device_pixel_ratio), - forward=forward) - - def set_window_title(self, title): - self._send_event('figure_label', label=title) - - # The following methods are specific to FigureManagerWebAgg - - def add_web_socket(self, web_socket): - assert hasattr(web_socket, 'send_binary') - assert hasattr(web_socket, 'send_json') - self.web_sockets.add(web_socket) - self.resize(*self.canvas.figure.bbox.size) - self._send_event('refresh') - - def remove_web_socket(self, web_socket): - self.web_sockets.remove(web_socket) - - def handle_json(self, content): - self.canvas.handle_event(content) - - def refresh_all(self): - if self.web_sockets: - diff = self.canvas.get_diff_image() - if diff is not None: - for s in self.web_sockets: - s.send_binary(diff) - - @classmethod - def get_javascript(cls, stream=None): - if stream is None: - output = StringIO() - else: - output = stream - - output.write((Path(__file__).parent / "web_backend/js/mpl.js") - .read_text(encoding="utf-8")) - - toolitems = [] - for name, tooltip, image, method in cls.ToolbarCls.toolitems: - if name is None: - toolitems.append(['', '', '', '']) - else: - toolitems.append([name, tooltip, image, method]) - output.write("mpl.toolbar_items = {0};\n\n".format( - json.dumps(toolitems))) - - extensions = [] - for filetype, ext in sorted(FigureCanvasWebAggCore. - get_supported_filetypes_grouped(). - items()): - extensions.append(ext[0]) - output.write("mpl.extensions = {0};\n\n".format( - json.dumps(extensions))) - - output.write("mpl.default_extension = {0};".format( - json.dumps(FigureCanvasWebAggCore.get_default_filetype()))) - - if stream is None: - return output.getvalue() - - @classmethod - def get_static_file_path(cls): - return os.path.join(os.path.dirname(__file__), 'web_backend') - - def _send_event(self, event_type, **kwargs): - payload = {'type': event_type, **kwargs} - for s in self.web_sockets: - s.send_json(payload) - - -@_Backend.export -class _BackendWebAggCoreAgg(_Backend): - FigureCanvas = FigureCanvasWebAggCore - FigureManager = FigureManagerWebAgg diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_wx.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_wx.py deleted file mode 100644 index eeed515..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_wx.py +++ /dev/null @@ -1,1390 +0,0 @@ -""" -A wxPython backend for matplotlib. - -Originally contributed by Jeremy O'Donoghue (jeremy@o-donoghue.com) and John -Hunter (jdhunter@ace.bsd.uchicago.edu). - -Copyright (C) Jeremy O'Donoghue & John Hunter, 2003-4. -""" - -import functools -import logging -import math -import pathlib -import sys -import weakref - -import numpy as np -import PIL - -import matplotlib as mpl -from matplotlib.backend_bases import ( - _Backend, FigureCanvasBase, FigureManagerBase, - GraphicsContextBase, MouseButton, NavigationToolbar2, RendererBase, - TimerBase, ToolContainerBase, cursors, - CloseEvent, KeyEvent, LocationEvent, MouseEvent, ResizeEvent) - -from matplotlib import _api, cbook, backend_tools -from matplotlib._pylab_helpers import Gcf -from matplotlib.path import Path -from matplotlib.transforms import Affine2D - -import wx - -_log = logging.getLogger(__name__) - -# the True dots per inch on the screen; should be display dependent; see -# http://groups.google.com/d/msg/comp.lang.postscript/-/omHAc9FEuAsJ?hl=en -# for some info about screen dpi -PIXELS_PER_INCH = 75 - - -@_api.deprecated("3.6") -def error_msg_wx(msg, parent=None): - """Signal an error condition with a popup error dialog.""" - dialog = wx.MessageDialog(parent=parent, - message=msg, - caption='Matplotlib backend_wx error', - style=wx.OK | wx.CENTRE) - dialog.ShowModal() - dialog.Destroy() - return None - - -# lru_cache holds a reference to the App and prevents it from being gc'ed. -@functools.lru_cache(1) -def _create_wxapp(): - wxapp = wx.App(False) - wxapp.SetExitOnFrameDelete(True) - cbook._setup_new_guiapp() - return wxapp - - -class TimerWx(TimerBase): - """Subclass of `.TimerBase` using wx.Timer events.""" - - def __init__(self, *args, **kwargs): - self._timer = wx.Timer() - self._timer.Notify = self._on_timer - super().__init__(*args, **kwargs) - - def _timer_start(self): - self._timer.Start(self._interval, self._single) - - def _timer_stop(self): - self._timer.Stop() - - def _timer_set_interval(self): - if self._timer.IsRunning(): - self._timer_start() # Restart with new interval. - - -@_api.deprecated( - "2.0", name="wx", obj_type="backend", removal="the future", - alternative="wxagg", - addendum="See the Matplotlib usage FAQ for more info on backends.") -class RendererWx(RendererBase): - """ - The renderer handles all the drawing primitives using a graphics - context instance that controls the colors/styles. It acts as the - 'renderer' instance used by many classes in the hierarchy. - """ - # In wxPython, drawing is performed on a wxDC instance, which will - # generally be mapped to the client area of the window displaying - # the plot. Under wxPython, the wxDC instance has a wx.Pen which - # describes the colour and weight of any lines drawn, and a wxBrush - # which describes the fill colour of any closed polygon. - - # Font styles, families and weight. - fontweights = { - 100: wx.FONTWEIGHT_LIGHT, - 200: wx.FONTWEIGHT_LIGHT, - 300: wx.FONTWEIGHT_LIGHT, - 400: wx.FONTWEIGHT_NORMAL, - 500: wx.FONTWEIGHT_NORMAL, - 600: wx.FONTWEIGHT_NORMAL, - 700: wx.FONTWEIGHT_BOLD, - 800: wx.FONTWEIGHT_BOLD, - 900: wx.FONTWEIGHT_BOLD, - 'ultralight': wx.FONTWEIGHT_LIGHT, - 'light': wx.FONTWEIGHT_LIGHT, - 'normal': wx.FONTWEIGHT_NORMAL, - 'medium': wx.FONTWEIGHT_NORMAL, - 'semibold': wx.FONTWEIGHT_NORMAL, - 'bold': wx.FONTWEIGHT_BOLD, - 'heavy': wx.FONTWEIGHT_BOLD, - 'ultrabold': wx.FONTWEIGHT_BOLD, - 'black': wx.FONTWEIGHT_BOLD, - } - fontangles = { - 'italic': wx.FONTSTYLE_ITALIC, - 'normal': wx.FONTSTYLE_NORMAL, - 'oblique': wx.FONTSTYLE_SLANT, - } - - # wxPython allows for portable font styles, choosing them appropriately for - # the target platform. Map some standard font names to the portable styles. - # QUESTION: Is it wise to agree to standard fontnames across all backends? - fontnames = { - 'Sans': wx.FONTFAMILY_SWISS, - 'Roman': wx.FONTFAMILY_ROMAN, - 'Script': wx.FONTFAMILY_SCRIPT, - 'Decorative': wx.FONTFAMILY_DECORATIVE, - 'Modern': wx.FONTFAMILY_MODERN, - 'Courier': wx.FONTFAMILY_MODERN, - 'courier': wx.FONTFAMILY_MODERN, - } - - def __init__(self, bitmap, dpi): - """Initialise a wxWindows renderer instance.""" - super().__init__() - _log.debug("%s - __init__()", type(self)) - self.width = bitmap.GetWidth() - self.height = bitmap.GetHeight() - self.bitmap = bitmap - self.fontd = {} - self.dpi = dpi - self.gc = None - - def flipy(self): - # docstring inherited - return True - - @_api.deprecated("3.6") - def offset_text_height(self): - return True - - def get_text_width_height_descent(self, s, prop, ismath): - # docstring inherited - - if ismath: - s = cbook.strip_math(s) - - if self.gc is None: - gc = self.new_gc() - else: - gc = self.gc - gfx_ctx = gc.gfx_ctx - font = self.get_wx_font(s, prop) - gfx_ctx.SetFont(font, wx.BLACK) - w, h, descent, leading = gfx_ctx.GetFullTextExtent(s) - - return w, h, descent - - def get_canvas_width_height(self): - # docstring inherited - return self.width, self.height - - def handle_clip_rectangle(self, gc): - new_bounds = gc.get_clip_rectangle() - if new_bounds is not None: - new_bounds = new_bounds.bounds - gfx_ctx = gc.gfx_ctx - if gfx_ctx._lastcliprect != new_bounds: - gfx_ctx._lastcliprect = new_bounds - if new_bounds is None: - gfx_ctx.ResetClip() - else: - gfx_ctx.Clip(new_bounds[0], - self.height - new_bounds[1] - new_bounds[3], - new_bounds[2], new_bounds[3]) - - @staticmethod - def convert_path(gfx_ctx, path, transform): - wxpath = gfx_ctx.CreatePath() - for points, code in path.iter_segments(transform): - if code == Path.MOVETO: - wxpath.MoveToPoint(*points) - elif code == Path.LINETO: - wxpath.AddLineToPoint(*points) - elif code == Path.CURVE3: - wxpath.AddQuadCurveToPoint(*points) - elif code == Path.CURVE4: - wxpath.AddCurveToPoint(*points) - elif code == Path.CLOSEPOLY: - wxpath.CloseSubpath() - return wxpath - - def draw_path(self, gc, path, transform, rgbFace=None): - # docstring inherited - gc.select() - self.handle_clip_rectangle(gc) - gfx_ctx = gc.gfx_ctx - transform = transform + \ - Affine2D().scale(1.0, -1.0).translate(0.0, self.height) - wxpath = self.convert_path(gfx_ctx, path, transform) - if rgbFace is not None: - gfx_ctx.SetBrush(wx.Brush(gc.get_wxcolour(rgbFace))) - gfx_ctx.DrawPath(wxpath) - else: - gfx_ctx.StrokePath(wxpath) - gc.unselect() - - def draw_image(self, gc, x, y, im): - bbox = gc.get_clip_rectangle() - if bbox is not None: - l, b, w, h = bbox.bounds - else: - l = 0 - b = 0 - w = self.width - h = self.height - rows, cols = im.shape[:2] - bitmap = wx.Bitmap.FromBufferRGBA(cols, rows, im.tobytes()) - gc.select() - gc.gfx_ctx.DrawBitmap(bitmap, int(l), int(self.height - b), - int(w), int(-h)) - gc.unselect() - - def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): - # docstring inherited - - if ismath: - s = cbook.strip_math(s) - _log.debug("%s - draw_text()", type(self)) - gc.select() - self.handle_clip_rectangle(gc) - gfx_ctx = gc.gfx_ctx - - font = self.get_wx_font(s, prop) - color = gc.get_wxcolour(gc.get_rgb()) - gfx_ctx.SetFont(font, color) - - w, h, d = self.get_text_width_height_descent(s, prop, ismath) - x = int(x) - y = int(y - h) - - if angle == 0.0: - gfx_ctx.DrawText(s, x, y) - else: - rads = math.radians(angle) - xo = h * math.sin(rads) - yo = h * math.cos(rads) - gfx_ctx.DrawRotatedText(s, x - xo, y - yo, rads) - - gc.unselect() - - def new_gc(self): - # docstring inherited - _log.debug("%s - new_gc()", type(self)) - self.gc = GraphicsContextWx(self.bitmap, self) - self.gc.select() - self.gc.unselect() - return self.gc - - def get_wx_font(self, s, prop): - """Return a wx font. Cache font instances for efficiency.""" - _log.debug("%s - get_wx_font()", type(self)) - key = hash(prop) - font = self.fontd.get(key) - if font is not None: - return font - size = self.points_to_pixels(prop.get_size_in_points()) - # Font colour is determined by the active wx.Pen - # TODO: It may be wise to cache font information - self.fontd[key] = font = wx.Font( # Cache the font and gc. - pointSize=round(size), - family=self.fontnames.get(prop.get_name(), wx.ROMAN), - style=self.fontangles[prop.get_style()], - weight=self.fontweights[prop.get_weight()]) - return font - - def points_to_pixels(self, points): - # docstring inherited - return points * (PIXELS_PER_INCH / 72.0 * self.dpi / 72.0) - - -class GraphicsContextWx(GraphicsContextBase): - """ - The graphics context provides the color, line styles, etc. - - This class stores a reference to a wxMemoryDC, and a - wxGraphicsContext that draws to it. Creating a wxGraphicsContext - seems to be fairly heavy, so these objects are cached based on the - bitmap object that is passed in. - - The base GraphicsContext stores colors as an RGB tuple on the unit - interval, e.g., (0.5, 0.0, 1.0). wxPython uses an int interval, but - since wxPython colour management is rather simple, I have not chosen - to implement a separate colour manager class. - """ - _capd = {'butt': wx.CAP_BUTT, - 'projecting': wx.CAP_PROJECTING, - 'round': wx.CAP_ROUND} - - _joind = {'bevel': wx.JOIN_BEVEL, - 'miter': wx.JOIN_MITER, - 'round': wx.JOIN_ROUND} - - _cache = weakref.WeakKeyDictionary() - - def __init__(self, bitmap, renderer): - super().__init__() - # assert self.Ok(), "wxMemoryDC not OK to use" - _log.debug("%s - __init__(): %s", type(self), bitmap) - - dc, gfx_ctx = self._cache.get(bitmap, (None, None)) - if dc is None: - dc = wx.MemoryDC(bitmap) - gfx_ctx = wx.GraphicsContext.Create(dc) - gfx_ctx._lastcliprect = None - self._cache[bitmap] = dc, gfx_ctx - - self.bitmap = bitmap - self.dc = dc - self.gfx_ctx = gfx_ctx - self._pen = wx.Pen('BLACK', 1, wx.SOLID) - gfx_ctx.SetPen(self._pen) - self.renderer = renderer - - def select(self): - """Select the current bitmap into this wxDC instance.""" - if sys.platform == 'win32': - self.dc.SelectObject(self.bitmap) - self.IsSelected = True - - def unselect(self): - """Select a Null bitmap into this wxDC instance.""" - if sys.platform == 'win32': - self.dc.SelectObject(wx.NullBitmap) - self.IsSelected = False - - def set_foreground(self, fg, isRGBA=None): - # docstring inherited - # Implementation note: wxPython has a separate concept of pen and - # brush - the brush fills any outline trace left by the pen. - # Here we set both to the same colour - if a figure is not to be - # filled, the renderer will set the brush to be transparent - # Same goes for text foreground... - _log.debug("%s - set_foreground()", type(self)) - self.select() - super().set_foreground(fg, isRGBA) - - self._pen.SetColour(self.get_wxcolour(self.get_rgb())) - self.gfx_ctx.SetPen(self._pen) - self.unselect() - - def set_linewidth(self, w): - # docstring inherited - w = float(w) - _log.debug("%s - set_linewidth()", type(self)) - self.select() - if 0 < w < 1: - w = 1 - super().set_linewidth(w) - lw = int(self.renderer.points_to_pixels(self._linewidth)) - if lw == 0: - lw = 1 - self._pen.SetWidth(lw) - self.gfx_ctx.SetPen(self._pen) - self.unselect() - - def set_capstyle(self, cs): - # docstring inherited - _log.debug("%s - set_capstyle()", type(self)) - self.select() - super().set_capstyle(cs) - self._pen.SetCap(GraphicsContextWx._capd[self._capstyle]) - self.gfx_ctx.SetPen(self._pen) - self.unselect() - - def set_joinstyle(self, js): - # docstring inherited - _log.debug("%s - set_joinstyle()", type(self)) - self.select() - super().set_joinstyle(js) - self._pen.SetJoin(GraphicsContextWx._joind[self._joinstyle]) - self.gfx_ctx.SetPen(self._pen) - self.unselect() - - def get_wxcolour(self, color): - """Convert an RGB(A) color to a wx.Colour.""" - _log.debug("%s - get_wx_color()", type(self)) - return wx.Colour(*[int(255 * x) for x in color]) - - -class _FigureCanvasWxBase(FigureCanvasBase, wx.Panel): - """ - The FigureCanvas contains the figure and does event handling. - - In the wxPython backend, it is derived from wxPanel, and (usually) lives - inside a frame instantiated by a FigureManagerWx. The parent window - probably implements a wx.Sizer to control the displayed control size - but - we give a hint as to our preferred minimum size. - """ - - required_interactive_framework = "wx" - _timer_cls = TimerWx - manager_class = _api.classproperty(lambda cls: FigureManagerWx) - - keyvald = { - wx.WXK_CONTROL: 'control', - wx.WXK_SHIFT: 'shift', - wx.WXK_ALT: 'alt', - wx.WXK_CAPITAL: 'caps_lock', - wx.WXK_LEFT: 'left', - wx.WXK_UP: 'up', - wx.WXK_RIGHT: 'right', - wx.WXK_DOWN: 'down', - wx.WXK_ESCAPE: 'escape', - wx.WXK_F1: 'f1', - wx.WXK_F2: 'f2', - wx.WXK_F3: 'f3', - wx.WXK_F4: 'f4', - wx.WXK_F5: 'f5', - wx.WXK_F6: 'f6', - wx.WXK_F7: 'f7', - wx.WXK_F8: 'f8', - wx.WXK_F9: 'f9', - wx.WXK_F10: 'f10', - wx.WXK_F11: 'f11', - wx.WXK_F12: 'f12', - wx.WXK_SCROLL: 'scroll_lock', - wx.WXK_PAUSE: 'break', - wx.WXK_BACK: 'backspace', - wx.WXK_RETURN: 'enter', - wx.WXK_INSERT: 'insert', - wx.WXK_DELETE: 'delete', - wx.WXK_HOME: 'home', - wx.WXK_END: 'end', - wx.WXK_PAGEUP: 'pageup', - wx.WXK_PAGEDOWN: 'pagedown', - wx.WXK_NUMPAD0: '0', - wx.WXK_NUMPAD1: '1', - wx.WXK_NUMPAD2: '2', - wx.WXK_NUMPAD3: '3', - wx.WXK_NUMPAD4: '4', - wx.WXK_NUMPAD5: '5', - wx.WXK_NUMPAD6: '6', - wx.WXK_NUMPAD7: '7', - wx.WXK_NUMPAD8: '8', - wx.WXK_NUMPAD9: '9', - wx.WXK_NUMPAD_ADD: '+', - wx.WXK_NUMPAD_SUBTRACT: '-', - wx.WXK_NUMPAD_MULTIPLY: '*', - wx.WXK_NUMPAD_DIVIDE: '/', - wx.WXK_NUMPAD_DECIMAL: 'dec', - wx.WXK_NUMPAD_ENTER: 'enter', - wx.WXK_NUMPAD_UP: 'up', - wx.WXK_NUMPAD_RIGHT: 'right', - wx.WXK_NUMPAD_DOWN: 'down', - wx.WXK_NUMPAD_LEFT: 'left', - wx.WXK_NUMPAD_PAGEUP: 'pageup', - wx.WXK_NUMPAD_PAGEDOWN: 'pagedown', - wx.WXK_NUMPAD_HOME: 'home', - wx.WXK_NUMPAD_END: 'end', - wx.WXK_NUMPAD_INSERT: 'insert', - wx.WXK_NUMPAD_DELETE: 'delete', - } - - def __init__(self, parent, id, figure=None): - """ - Initialize a FigureWx instance. - - - Initialize the FigureCanvasBase and wxPanel parents. - - Set event handlers for resize, paint, and keyboard and mouse - interaction. - """ - - FigureCanvasBase.__init__(self, figure) - w, h = map(math.ceil, self.figure.bbox.size) - # Set preferred window size hint - helps the sizer, if one is connected - wx.Panel.__init__(self, parent, id, size=wx.Size(w, h)) - # Create the drawing bitmap - self.bitmap = wx.Bitmap(w, h) - _log.debug("%s - __init__() - bitmap w:%d h:%d", type(self), w, h) - self._isDrawn = False - self._rubberband_rect = None - self._rubberband_pen_black = wx.Pen('BLACK', 1, wx.PENSTYLE_SHORT_DASH) - self._rubberband_pen_white = wx.Pen('WHITE', 1, wx.PENSTYLE_SOLID) - - self.Bind(wx.EVT_SIZE, self._on_size) - self.Bind(wx.EVT_PAINT, self._on_paint) - self.Bind(wx.EVT_CHAR_HOOK, self._on_key_down) - self.Bind(wx.EVT_KEY_UP, self._on_key_up) - self.Bind(wx.EVT_LEFT_DOWN, self._on_mouse_button) - self.Bind(wx.EVT_LEFT_DCLICK, self._on_mouse_button) - self.Bind(wx.EVT_LEFT_UP, self._on_mouse_button) - self.Bind(wx.EVT_MIDDLE_DOWN, self._on_mouse_button) - self.Bind(wx.EVT_MIDDLE_DCLICK, self._on_mouse_button) - self.Bind(wx.EVT_MIDDLE_UP, self._on_mouse_button) - self.Bind(wx.EVT_RIGHT_DOWN, self._on_mouse_button) - self.Bind(wx.EVT_RIGHT_DCLICK, self._on_mouse_button) - self.Bind(wx.EVT_RIGHT_UP, self._on_mouse_button) - self.Bind(wx.EVT_MOUSE_AUX1_DOWN, self._on_mouse_button) - self.Bind(wx.EVT_MOUSE_AUX1_UP, self._on_mouse_button) - self.Bind(wx.EVT_MOUSE_AUX2_DOWN, self._on_mouse_button) - self.Bind(wx.EVT_MOUSE_AUX2_UP, self._on_mouse_button) - self.Bind(wx.EVT_MOUSE_AUX1_DCLICK, self._on_mouse_button) - self.Bind(wx.EVT_MOUSE_AUX2_DCLICK, self._on_mouse_button) - self.Bind(wx.EVT_MOUSEWHEEL, self._on_mouse_wheel) - self.Bind(wx.EVT_MOTION, self._on_motion) - self.Bind(wx.EVT_ENTER_WINDOW, self._on_enter) - self.Bind(wx.EVT_LEAVE_WINDOW, self._on_leave) - - self.Bind(wx.EVT_MOUSE_CAPTURE_CHANGED, self._on_capture_lost) - self.Bind(wx.EVT_MOUSE_CAPTURE_LOST, self._on_capture_lost) - - self.SetBackgroundStyle(wx.BG_STYLE_PAINT) # Reduce flicker. - self.SetBackgroundColour(wx.WHITE) - - def Copy_to_Clipboard(self, event=None): - """Copy bitmap of canvas to system clipboard.""" - bmp_obj = wx.BitmapDataObject() - bmp_obj.SetBitmap(self.bitmap) - - if not wx.TheClipboard.IsOpened(): - open_success = wx.TheClipboard.Open() - if open_success: - wx.TheClipboard.SetData(bmp_obj) - wx.TheClipboard.Close() - wx.TheClipboard.Flush() - - def draw_idle(self): - # docstring inherited - _log.debug("%s - draw_idle()", type(self)) - self._isDrawn = False # Force redraw - # Triggering a paint event is all that is needed to defer drawing - # until later. The platform will send the event when it thinks it is - # a good time (usually as soon as there are no other events pending). - self.Refresh(eraseBackground=False) - - def flush_events(self): - # docstring inherited - wx.Yield() - - def start_event_loop(self, timeout=0): - # docstring inherited - if hasattr(self, '_event_loop'): - raise RuntimeError("Event loop already running") - timer = wx.Timer(self, id=wx.ID_ANY) - if timeout > 0: - timer.Start(int(timeout * 1000), oneShot=True) - self.Bind(wx.EVT_TIMER, self.stop_event_loop, id=timer.GetId()) - # Event loop handler for start/stop event loop - self._event_loop = wx.GUIEventLoop() - self._event_loop.Run() - timer.Stop() - - def stop_event_loop(self, event=None): - # docstring inherited - if hasattr(self, '_event_loop'): - if self._event_loop.IsRunning(): - self._event_loop.Exit() - del self._event_loop - - def _get_imagesave_wildcards(self): - """Return the wildcard string for the filesave dialog.""" - default_filetype = self.get_default_filetype() - filetypes = self.get_supported_filetypes_grouped() - sorted_filetypes = sorted(filetypes.items()) - wildcards = [] - extensions = [] - filter_index = 0 - for i, (name, exts) in enumerate(sorted_filetypes): - ext_list = ';'.join(['*.%s' % ext for ext in exts]) - extensions.append(exts[0]) - wildcard = '%s (%s)|%s' % (name, ext_list, ext_list) - if default_filetype in exts: - filter_index = i - wildcards.append(wildcard) - wildcards = '|'.join(wildcards) - return wildcards, extensions, filter_index - - def gui_repaint(self, drawDC=None): - """ - Update the displayed image on the GUI canvas, using the supplied - wx.PaintDC device context. - - The 'WXAgg' backend sets origin accordingly. - """ - _log.debug("%s - gui_repaint()", type(self)) - # The "if self" check avoids a "wrapped C/C++ object has been deleted" - # RuntimeError if doing things after window is closed. - if not (self and self.IsShownOnScreen()): - return - if not drawDC: # not called from OnPaint use a ClientDC - drawDC = wx.ClientDC(self) - # For 'WX' backend on Windows, the bitmap can not be in use by another - # DC (see GraphicsContextWx._cache). - bmp = (self.bitmap.ConvertToImage().ConvertToBitmap() - if wx.Platform == '__WXMSW__' - and isinstance(self.figure.canvas.get_renderer(), RendererWx) - else self.bitmap) - drawDC.DrawBitmap(bmp, 0, 0) - if self._rubberband_rect is not None: - # Some versions of wx+python don't support numpy.float64 here. - x0, y0, x1, y1 = map(round, self._rubberband_rect) - rect = [(x0, y0, x1, y0), (x1, y0, x1, y1), - (x0, y0, x0, y1), (x0, y1, x1, y1)] - drawDC.DrawLineList(rect, self._rubberband_pen_white) - drawDC.DrawLineList(rect, self._rubberband_pen_black) - - filetypes = { - **FigureCanvasBase.filetypes, - 'bmp': 'Windows bitmap', - 'jpeg': 'JPEG', - 'jpg': 'JPEG', - 'pcx': 'PCX', - 'png': 'Portable Network Graphics', - 'tif': 'Tagged Image Format File', - 'tiff': 'Tagged Image Format File', - 'xpm': 'X pixmap', - } - - def print_figure(self, filename, *args, **kwargs): - # docstring inherited - super().print_figure(filename, *args, **kwargs) - # Restore the current view; this is needed because the artist contains - # methods rely on particular attributes of the rendered figure for - # determining things like bounding boxes. - if self._isDrawn: - self.draw() - - def _on_paint(self, event): - """Called when wxPaintEvt is generated.""" - _log.debug("%s - _on_paint()", type(self)) - drawDC = wx.PaintDC(self) - if not self._isDrawn: - self.draw(drawDC=drawDC) - else: - self.gui_repaint(drawDC=drawDC) - drawDC.Destroy() - - def _on_size(self, event): - """ - Called when wxEventSize is generated. - - In this application we attempt to resize to fit the window, so it - is better to take the performance hit and redraw the whole window. - """ - - _log.debug("%s - _on_size()", type(self)) - sz = self.GetParent().GetSizer() - if sz: - si = sz.GetItem(self) - if sz and si and not si.Proportion and not si.Flag & wx.EXPAND: - # managed by a sizer, but with a fixed size - size = self.GetMinSize() - else: - # variable size - size = self.GetClientSize() - # Do not allow size to become smaller than MinSize - size.IncTo(self.GetMinSize()) - if getattr(self, "_width", None): - if size == (self._width, self._height): - # no change in size - return - self._width, self._height = size - self._isDrawn = False - - if self._width <= 1 or self._height <= 1: - return # Empty figure - - # Create a new, correctly sized bitmap - self.bitmap = wx.Bitmap(self._width, self._height) - - dpival = self.figure.dpi - winch = self._width / dpival - hinch = self._height / dpival - self.figure.set_size_inches(winch, hinch, forward=False) - - # Rendering will happen on the associated paint event - # so no need to do anything here except to make sure - # the whole background is repainted. - self.Refresh(eraseBackground=False) - ResizeEvent("resize_event", self)._process() - self.draw_idle() - - @staticmethod - def _mpl_modifiers(event=None, *, exclude=None): - mod_table = [ - ("ctrl", wx.MOD_CONTROL, wx.WXK_CONTROL), - ("alt", wx.MOD_ALT, wx.WXK_ALT), - ("shift", wx.MOD_SHIFT, wx.WXK_SHIFT), - ] - if event is not None: - modifiers = event.GetModifiers() - return [name for name, mod, key in mod_table - if modifiers & mod and exclude != key] - else: - return [name for name, mod, key in mod_table - if wx.GetKeyState(key)] - - def _get_key(self, event): - keyval = event.KeyCode - if keyval in self.keyvald: - key = self.keyvald[keyval] - elif keyval < 256: - key = chr(keyval) - # wx always returns an uppercase, so make it lowercase if the shift - # key is not depressed (NOTE: this will not handle Caps Lock) - if not event.ShiftDown(): - key = key.lower() - else: - return None - mods = self._mpl_modifiers(event, exclude=keyval) - if "shift" in mods and key.isupper(): - mods.remove("shift") - return "+".join([*mods, key]) - - def _mpl_coords(self, pos=None): - """ - Convert a wx position, defaulting to the current cursor position, to - Matplotlib coordinates. - """ - if pos is None: - pos = wx.GetMouseState() - x, y = self.ScreenToClient(pos.X, pos.Y) - else: - x, y = pos.X, pos.Y - # flip y so y=0 is bottom of canvas - return x, self.figure.bbox.height - y - - def _on_key_down(self, event): - """Capture key press.""" - KeyEvent("key_press_event", self, - self._get_key(event), *self._mpl_coords(), - guiEvent=event)._process() - if self: - event.Skip() - - def _on_key_up(self, event): - """Release key.""" - KeyEvent("key_release_event", self, - self._get_key(event), *self._mpl_coords(), - guiEvent=event)._process() - if self: - event.Skip() - - def set_cursor(self, cursor): - # docstring inherited - cursor = wx.Cursor(_api.check_getitem({ - cursors.MOVE: wx.CURSOR_HAND, - cursors.HAND: wx.CURSOR_HAND, - cursors.POINTER: wx.CURSOR_ARROW, - cursors.SELECT_REGION: wx.CURSOR_CROSS, - cursors.WAIT: wx.CURSOR_WAIT, - cursors.RESIZE_HORIZONTAL: wx.CURSOR_SIZEWE, - cursors.RESIZE_VERTICAL: wx.CURSOR_SIZENS, - }, cursor=cursor)) - self.SetCursor(cursor) - self.Refresh() - - def _set_capture(self, capture=True): - """Control wx mouse capture.""" - if self.HasCapture(): - self.ReleaseMouse() - if capture: - self.CaptureMouse() - - def _on_capture_lost(self, event): - """Capture changed or lost""" - self._set_capture(False) - - def _on_mouse_button(self, event): - """Start measuring on an axis.""" - event.Skip() - self._set_capture(event.ButtonDown() or event.ButtonDClick()) - x, y = self._mpl_coords(event) - button_map = { - wx.MOUSE_BTN_LEFT: MouseButton.LEFT, - wx.MOUSE_BTN_MIDDLE: MouseButton.MIDDLE, - wx.MOUSE_BTN_RIGHT: MouseButton.RIGHT, - wx.MOUSE_BTN_AUX1: MouseButton.BACK, - wx.MOUSE_BTN_AUX2: MouseButton.FORWARD, - } - button = event.GetButton() - button = button_map.get(button, button) - modifiers = self._mpl_modifiers(event) - if event.ButtonDown(): - MouseEvent("button_press_event", self, x, y, button, - modifiers=modifiers, guiEvent=event)._process() - elif event.ButtonDClick(): - MouseEvent("button_press_event", self, x, y, button, - dblclick=True, modifiers=modifiers, - guiEvent=event)._process() - elif event.ButtonUp(): - MouseEvent("button_release_event", self, x, y, button, - modifiers=modifiers, guiEvent=event)._process() - - def _on_mouse_wheel(self, event): - """Translate mouse wheel events into matplotlib events""" - x, y = self._mpl_coords(event) - # Convert delta/rotation/rate into a floating point step size - step = event.LinesPerAction * event.WheelRotation / event.WheelDelta - # Done handling event - event.Skip() - # Mac gives two events for every wheel event; skip every second one. - if wx.Platform == '__WXMAC__': - if not hasattr(self, '_skipwheelevent'): - self._skipwheelevent = True - elif self._skipwheelevent: - self._skipwheelevent = False - return # Return without processing event - else: - self._skipwheelevent = True - MouseEvent("scroll_event", self, x, y, step=step, - modifiers=self._mpl_modifiers(event), - guiEvent=event)._process() - - def _on_motion(self, event): - """Start measuring on an axis.""" - event.Skip() - MouseEvent("motion_notify_event", self, - *self._mpl_coords(event), - modifiers=self._mpl_modifiers(event), - guiEvent=event)._process() - - def _on_enter(self, event): - """Mouse has entered the window.""" - event.Skip() - LocationEvent("figure_enter_event", self, - *self._mpl_coords(event), - modifiers=self._mpl_modifiers(), - guiEvent=event)._process() - - def _on_leave(self, event): - """Mouse has left the window.""" - event.Skip() - LocationEvent("figure_leave_event", self, - *self._mpl_coords(event), - modifiers=self._mpl_modifiers(), - guiEvent=event)._process() - - -class FigureCanvasWx(_FigureCanvasWxBase): - # Rendering to a Wx canvas using the deprecated Wx renderer. - - def draw(self, drawDC=None): - """ - Render the figure using RendererWx instance renderer, or using a - previously defined renderer if none is specified. - """ - _log.debug("%s - draw()", type(self)) - self.renderer = RendererWx(self.bitmap, self.figure.dpi) - self.figure.draw(self.renderer) - self._isDrawn = True - self.gui_repaint(drawDC=drawDC) - - def _print_image(self, filetype, filename): - bitmap = wx.Bitmap(math.ceil(self.figure.bbox.width), - math.ceil(self.figure.bbox.height)) - self.figure.draw(RendererWx(bitmap, self.figure.dpi)) - saved_obj = (bitmap.ConvertToImage() - if cbook.is_writable_file_like(filename) - else bitmap) - if not saved_obj.SaveFile(filename, filetype): - raise RuntimeError(f'Could not save figure to {filename}') - # draw() is required here since bits of state about the last renderer - # are strewn about the artist draw methods. Do not remove the draw - # without first verifying that these have been cleaned up. The artist - # contains() methods will fail otherwise. - if self._isDrawn: - self.draw() - # The "if self" check avoids a "wrapped C/C++ object has been deleted" - # RuntimeError if doing things after window is closed. - if self: - self.Refresh() - - print_bmp = functools.partialmethod( - _print_image, wx.BITMAP_TYPE_BMP) - print_jpeg = print_jpg = functools.partialmethod( - _print_image, wx.BITMAP_TYPE_JPEG) - print_pcx = functools.partialmethod( - _print_image, wx.BITMAP_TYPE_PCX) - print_png = functools.partialmethod( - _print_image, wx.BITMAP_TYPE_PNG) - print_tiff = print_tif = functools.partialmethod( - _print_image, wx.BITMAP_TYPE_TIF) - print_xpm = functools.partialmethod( - _print_image, wx.BITMAP_TYPE_XPM) - - -class FigureFrameWx(wx.Frame): - def __init__(self, num, fig, *, canvas_class=None): - # On non-Windows platform, explicitly set the position - fix - # positioning bug on some Linux platforms - if wx.Platform == '__WXMSW__': - pos = wx.DefaultPosition - else: - pos = wx.Point(20, 20) - super().__init__(parent=None, id=-1, pos=pos) - # Frame will be sized later by the Fit method - _log.debug("%s - __init__()", type(self)) - _set_frame_icon(self) - - # The parameter will become required after the deprecation elapses. - if canvas_class is not None: - self.canvas = canvas_class(self, -1, fig) - else: - _api.warn_deprecated( - "3.6", message="The canvas_class parameter will become " - "required after the deprecation period starting in Matplotlib " - "%(since)s elapses.") - self.canvas = self.get_canvas(fig) - - # Auto-attaches itself to self.canvas.manager - manager = FigureManagerWx(self.canvas, num, self) - - toolbar = self.canvas.manager.toolbar - if toolbar is not None: - self.SetToolBar(toolbar) - - # On Windows, canvas sizing must occur after toolbar addition; - # otherwise the toolbar further resizes the canvas. - w, h = map(math.ceil, fig.bbox.size) - self.canvas.SetInitialSize(wx.Size(w, h)) - self.canvas.SetMinSize((2, 2)) - self.canvas.SetFocus() - - self.Fit() - - self.Bind(wx.EVT_CLOSE, self._on_close) - - sizer = _api.deprecated("3.6", alternative="frame.GetSizer()")( - property(lambda self: self.GetSizer())) - figmgr = _api.deprecated("3.6", alternative="frame.canvas.manager")( - property(lambda self: self.canvas.manager)) - num = _api.deprecated("3.6", alternative="frame.canvas.manager.num")( - property(lambda self: self.canvas.manager.num)) - toolbar = _api.deprecated("3.6", alternative="frame.GetToolBar()")( - property(lambda self: self.GetToolBar())) - toolmanager = _api.deprecated( - "3.6", alternative="frame.canvas.manager.toolmanager")( - property(lambda self: self.canvas.manager.toolmanager)) - - @_api.deprecated( - "3.6", alternative="the canvas_class constructor parameter") - def get_canvas(self, fig): - return FigureCanvasWx(self, -1, fig) - - @_api.deprecated("3.6", alternative="frame.canvas.manager") - def get_figure_manager(self): - _log.debug("%s - get_figure_manager()", type(self)) - return self.canvas.manager - - def _on_close(self, event): - _log.debug("%s - on_close()", type(self)) - CloseEvent("close_event", self.canvas)._process() - self.canvas.stop_event_loop() - # set FigureManagerWx.frame to None to prevent repeated attempts to - # close this frame from FigureManagerWx.destroy() - self.canvas.manager.frame = None - # remove figure manager from Gcf.figs - Gcf.destroy(self.canvas.manager) - try: # See issue 2941338. - self.canvas.mpl_disconnect(self.canvas.toolbar._id_drag) - except AttributeError: # If there's no toolbar. - pass - # Carry on with close event propagation, frame & children destruction - event.Skip() - - -class FigureManagerWx(FigureManagerBase): - """ - Container/controller for the FigureCanvas and GUI frame. - - It is instantiated by Gcf whenever a new figure is created. Gcf is - responsible for managing multiple instances of FigureManagerWx. - - Attributes - ---------- - canvas : `FigureCanvas` - a FigureCanvasWx(wx.Panel) instance - window : wxFrame - a wxFrame instance - wxpython.org/Phoenix/docs/html/Frame.html - """ - - def __init__(self, canvas, num, frame): - _log.debug("%s - __init__()", type(self)) - self.frame = self.window = frame - super().__init__(canvas, num) - - @classmethod - def create_with_canvas(cls, canvas_class, figure, num): - # docstring inherited - wxapp = wx.GetApp() or _create_wxapp() - frame = FigureFrameWx(num, figure, canvas_class=canvas_class) - manager = figure.canvas.manager - if mpl.is_interactive(): - manager.frame.Show() - figure.canvas.draw_idle() - return manager - - @classmethod - def start_main_loop(cls): - if not wx.App.IsMainLoopRunning(): - wxapp = wx.GetApp() - if wxapp is not None: - wxapp.MainLoop() - - def show(self): - # docstring inherited - self.frame.Show() - self.canvas.draw() - if mpl.rcParams['figure.raise_window']: - self.frame.Raise() - - def destroy(self, *args): - # docstring inherited - _log.debug("%s - destroy()", type(self)) - frame = self.frame - if frame: # Else, may have been already deleted, e.g. when closing. - # As this can be called from non-GUI thread from plt.close use - # wx.CallAfter to ensure thread safety. - wx.CallAfter(frame.Close) - - def full_screen_toggle(self): - # docstring inherited - self.frame.ShowFullScreen(not self.frame.IsFullScreen()) - - def get_window_title(self): - # docstring inherited - return self.window.GetTitle() - - def set_window_title(self, title): - # docstring inherited - self.window.SetTitle(title) - - def resize(self, width, height): - # docstring inherited - # Directly using SetClientSize doesn't handle the toolbar on Windows. - self.window.SetSize(self.window.ClientToWindowSize(wx.Size( - math.ceil(width), math.ceil(height)))) - - -def _load_bitmap(filename): - """ - Load a wx.Bitmap from a file in the "images" directory of the Matplotlib - data. - """ - return wx.Bitmap(str(cbook._get_data_path('images', filename))) - - -def _set_frame_icon(frame): - bundle = wx.IconBundle() - for image in ('matplotlib.png', 'matplotlib_large.png'): - icon = wx.Icon(_load_bitmap(image)) - if not icon.IsOk(): - return - bundle.AddIcon(icon) - frame.SetIcons(bundle) - - -class NavigationToolbar2Wx(NavigationToolbar2, wx.ToolBar): - def __init__(self, canvas, coordinates=True, *, style=wx.TB_BOTTOM): - wx.ToolBar.__init__(self, canvas.GetParent(), -1, style=style) - - if 'wxMac' in wx.PlatformInfo: - self.SetToolBitmapSize((24, 24)) - self.wx_ids = {} - for text, tooltip_text, image_file, callback in self.toolitems: - if text is None: - self.AddSeparator() - continue - self.wx_ids[text] = ( - self.AddTool( - -1, - bitmap=self._icon(f"{image_file}.png"), - bmpDisabled=wx.NullBitmap, - label=text, shortHelp=tooltip_text, - kind=(wx.ITEM_CHECK if text in ["Pan", "Zoom"] - else wx.ITEM_NORMAL)) - .Id) - self.Bind(wx.EVT_TOOL, getattr(self, callback), - id=self.wx_ids[text]) - - self._coordinates = coordinates - if self._coordinates: - self.AddStretchableSpace() - self._label_text = wx.StaticText(self, style=wx.ALIGN_RIGHT) - self.AddControl(self._label_text) - - self.Realize() - - NavigationToolbar2.__init__(self, canvas) - - @staticmethod - def _icon(name): - """ - Construct a `wx.Bitmap` suitable for use as icon from an image file - *name*, including the extension and relative to Matplotlib's "images" - data directory. - """ - pilimg = PIL.Image.open(cbook._get_data_path("images", name)) - # ensure RGBA as wx BitMap expects RGBA format - image = np.array(pilimg.convert("RGBA")) - try: - dark = wx.SystemSettings.GetAppearance().IsDark() - except AttributeError: # wxpython < 4.1 - # copied from wx's IsUsingDarkBackground / GetLuminance. - bg = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW) - fg = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) - # See wx.Colour.GetLuminance. - bg_lum = (.299 * bg.red + .587 * bg.green + .114 * bg.blue) / 255 - fg_lum = (.299 * fg.red + .587 * fg.green + .114 * fg.blue) / 255 - dark = fg_lum - bg_lum > .2 - if dark: - fg = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT) - black_mask = (image[..., :3] == 0).all(axis=-1) - image[black_mask, :3] = (fg.Red(), fg.Green(), fg.Blue()) - return wx.Bitmap.FromBufferRGBA( - image.shape[1], image.shape[0], image.tobytes()) - - def _update_buttons_checked(self): - if "Pan" in self.wx_ids: - self.ToggleTool(self.wx_ids["Pan"], self.mode.name == "PAN") - if "Zoom" in self.wx_ids: - self.ToggleTool(self.wx_ids["Zoom"], self.mode.name == "ZOOM") - - def zoom(self, *args): - super().zoom(*args) - self._update_buttons_checked() - - def pan(self, *args): - super().pan(*args) - self._update_buttons_checked() - - def save_figure(self, *args): - # Fetch the required filename and file type. - filetypes, exts, filter_index = self.canvas._get_imagesave_wildcards() - default_file = self.canvas.get_default_filename() - dialog = wx.FileDialog( - self.canvas.GetParent(), "Save to file", - mpl.rcParams["savefig.directory"], default_file, filetypes, - wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) - dialog.SetFilterIndex(filter_index) - if dialog.ShowModal() == wx.ID_OK: - path = pathlib.Path(dialog.GetPath()) - _log.debug('%s - Save file path: %s', type(self), path) - fmt = exts[dialog.GetFilterIndex()] - ext = path.suffix[1:] - if ext in self.canvas.get_supported_filetypes() and fmt != ext: - # looks like they forgot to set the image type drop - # down, going with the extension. - _log.warning('extension %s did not match the selected ' - 'image type %s; going with %s', - ext, fmt, ext) - fmt = ext - # Save dir for next time, unless empty str (which means use cwd). - if mpl.rcParams["savefig.directory"]: - mpl.rcParams["savefig.directory"] = str(path.parent) - try: - self.canvas.figure.savefig(path, format=fmt) - except Exception as e: - dialog = wx.MessageDialog( - parent=self.canvas.GetParent(), message=str(e), - caption='Matplotlib error') - dialog.ShowModal() - dialog.Destroy() - - def draw_rubberband(self, event, x0, y0, x1, y1): - height = self.canvas.figure.bbox.height - self.canvas._rubberband_rect = (x0, height - y0, x1, height - y1) - self.canvas.Refresh() - - def remove_rubberband(self): - self.canvas._rubberband_rect = None - self.canvas.Refresh() - - def set_message(self, s): - if self._coordinates: - self._label_text.SetLabel(s) - - def set_history_buttons(self): - can_backward = self._nav_stack._pos > 0 - can_forward = self._nav_stack._pos < len(self._nav_stack._elements) - 1 - if 'Back' in self.wx_ids: - self.EnableTool(self.wx_ids['Back'], can_backward) - if 'Forward' in self.wx_ids: - self.EnableTool(self.wx_ids['Forward'], can_forward) - - -# tools for matplotlib.backend_managers.ToolManager: - -class ToolbarWx(ToolContainerBase, wx.ToolBar): - def __init__(self, toolmanager, parent=None, style=wx.TB_BOTTOM): - if parent is None: - parent = toolmanager.canvas.GetParent() - ToolContainerBase.__init__(self, toolmanager) - wx.ToolBar.__init__(self, parent, -1, style=style) - self._space = self.AddStretchableSpace() - self._label_text = wx.StaticText(self, style=wx.ALIGN_RIGHT) - self.AddControl(self._label_text) - self._toolitems = {} - self._groups = {} # Mapping of groups to the separator after them. - - def _get_tool_pos(self, tool): - """ - Find the position (index) of a wx.ToolBarToolBase in a ToolBar. - - ``ToolBar.GetToolPos`` is not useful because wx assigns the same Id to - all Separators and StretchableSpaces. - """ - pos, = [pos for pos in range(self.ToolsCount) - if self.GetToolByPos(pos) == tool] - return pos - - def add_toolitem(self, name, group, position, image_file, description, - toggle): - # Find or create the separator that follows this group. - if group not in self._groups: - self._groups[group] = self.InsertSeparator( - self._get_tool_pos(self._space)) - sep = self._groups[group] - # List all separators. - seps = [t for t in map(self.GetToolByPos, range(self.ToolsCount)) - if t.IsSeparator() and not t.IsStretchableSpace()] - # Find where to insert the tool. - if position >= 0: - # Find the start of the group by looking for the separator - # preceding this one; then move forward from it. - start = (0 if sep == seps[0] - else self._get_tool_pos(seps[seps.index(sep) - 1]) + 1) - else: - # Move backwards from this separator. - start = self._get_tool_pos(sep) + 1 - idx = start + position - if image_file: - bmp = NavigationToolbar2Wx._icon(image_file) - kind = wx.ITEM_NORMAL if not toggle else wx.ITEM_CHECK - tool = self.InsertTool(idx, -1, name, bmp, wx.NullBitmap, kind, - description or "") - else: - size = (self.GetTextExtent(name)[0] + 10, -1) - if toggle: - control = wx.ToggleButton(self, -1, name, size=size) - else: - control = wx.Button(self, -1, name, size=size) - tool = self.InsertControl(idx, control, label=name) - self.Realize() - - def handler(event): - self.trigger_tool(name) - - if image_file: - self.Bind(wx.EVT_TOOL, handler, tool) - else: - control.Bind(wx.EVT_LEFT_DOWN, handler) - - self._toolitems.setdefault(name, []) - self._toolitems[name].append((tool, handler)) - - def toggle_toolitem(self, name, toggled): - if name not in self._toolitems: - return - for tool, handler in self._toolitems[name]: - if not tool.IsControl(): - self.ToggleTool(tool.Id, toggled) - else: - tool.GetControl().SetValue(toggled) - self.Refresh() - - def remove_toolitem(self, name): - for tool, handler in self._toolitems[name]: - self.DeleteTool(tool.Id) - del self._toolitems[name] - - def set_message(self, s): - self._label_text.SetLabel(s) - - -@backend_tools._register_tool_class(_FigureCanvasWxBase) -class ConfigureSubplotsWx(backend_tools.ConfigureSubplotsBase): - def trigger(self, *args): - NavigationToolbar2Wx.configure_subplots(self) - - -@backend_tools._register_tool_class(_FigureCanvasWxBase) -class SaveFigureWx(backend_tools.SaveFigureBase): - def trigger(self, *args): - NavigationToolbar2Wx.save_figure( - self._make_classic_style_pseudo_toolbar()) - - -@backend_tools._register_tool_class(_FigureCanvasWxBase) -class RubberbandWx(backend_tools.RubberbandBase): - def draw_rubberband(self, x0, y0, x1, y1): - NavigationToolbar2Wx.draw_rubberband( - self._make_classic_style_pseudo_toolbar(), None, x0, y0, x1, y1) - - def remove_rubberband(self): - NavigationToolbar2Wx.remove_rubberband( - self._make_classic_style_pseudo_toolbar()) - - -class _HelpDialog(wx.Dialog): - _instance = None # a reference to an open dialog singleton - headers = [("Action", "Shortcuts", "Description")] - widths = [100, 140, 300] - - def __init__(self, parent, help_entries): - super().__init__(parent, title="Help", - style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) - - sizer = wx.BoxSizer(wx.VERTICAL) - grid_sizer = wx.FlexGridSizer(0, 3, 8, 6) - # create and add the entries - bold = self.GetFont().MakeBold() - for r, row in enumerate(self.headers + help_entries): - for (col, width) in zip(row, self.widths): - label = wx.StaticText(self, label=col) - if r == 0: - label.SetFont(bold) - label.Wrap(width) - grid_sizer.Add(label, 0, 0, 0) - # finalize layout, create button - sizer.Add(grid_sizer, 0, wx.ALL, 6) - ok = wx.Button(self, wx.ID_OK) - sizer.Add(ok, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 8) - self.SetSizer(sizer) - sizer.Fit(self) - self.Layout() - self.Bind(wx.EVT_CLOSE, self._on_close) - ok.Bind(wx.EVT_BUTTON, self._on_close) - - def _on_close(self, event): - _HelpDialog._instance = None # remove global reference - self.DestroyLater() - event.Skip() - - @classmethod - def show(cls, parent, help_entries): - # if no dialog is shown, create one; otherwise just re-raise it - if cls._instance: - cls._instance.Raise() - return - cls._instance = cls(parent, help_entries) - cls._instance.Show() - - -@backend_tools._register_tool_class(_FigureCanvasWxBase) -class HelpWx(backend_tools.ToolHelpBase): - def trigger(self, *args): - _HelpDialog.show(self.figure.canvas.GetTopLevelParent(), - self._get_help_entries()) - - -@backend_tools._register_tool_class(_FigureCanvasWxBase) -class ToolCopyToClipboardWx(backend_tools.ToolCopyToClipboardBase): - def trigger(self, *args, **kwargs): - if not self.canvas._isDrawn: - self.canvas.draw() - if not self.canvas.bitmap.IsOk() or not wx.TheClipboard.Open(): - return - try: - wx.TheClipboard.SetData(wx.BitmapDataObject(self.canvas.bitmap)) - finally: - wx.TheClipboard.Close() - - -FigureManagerWx._toolbar2_class = NavigationToolbar2Wx -FigureManagerWx._toolmanager_toolbar_class = ToolbarWx - - -@_Backend.export -class _BackendWx(_Backend): - FigureCanvas = FigureCanvasWx - FigureManager = FigureManagerWx - mainloop = FigureManagerWx.start_main_loop diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_wxagg.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_wxagg.py deleted file mode 100644 index ca7f915..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_wxagg.py +++ /dev/null @@ -1,61 +0,0 @@ -import wx - -from .. import _api -from .backend_agg import FigureCanvasAgg -from .backend_wx import _BackendWx, _FigureCanvasWxBase, FigureFrameWx -from .backend_wx import ( # noqa: F401 # pylint: disable=W0611 - NavigationToolbar2Wx as NavigationToolbar2WxAgg) - - -@_api.deprecated( - "3.6", alternative="FigureFrameWx(..., canvas_class=FigureCanvasWxAgg)") -class FigureFrameWxAgg(FigureFrameWx): - def get_canvas(self, fig): - return FigureCanvasWxAgg(self, -1, fig) - - -class FigureCanvasWxAgg(FigureCanvasAgg, _FigureCanvasWxBase): - """ - The FigureCanvas contains the figure and does event handling. - - In the wxPython backend, it is derived from wxPanel, and (usually) - lives inside a frame instantiated by a FigureManagerWx. The parent - window probably implements a wxSizer to control the displayed - control size - but we give a hint as to our preferred minimum - size. - """ - - def draw(self, drawDC=None): - """ - Render the figure using agg. - """ - FigureCanvasAgg.draw(self) - self.bitmap = _rgba_to_wx_bitmap(self.get_renderer().buffer_rgba()) - self._isDrawn = True - self.gui_repaint(drawDC=drawDC) - - def blit(self, bbox=None): - # docstring inherited - bitmap = _rgba_to_wx_bitmap(self.get_renderer().buffer_rgba()) - if bbox is None: - self.bitmap = bitmap - else: - srcDC = wx.MemoryDC(bitmap) - destDC = wx.MemoryDC(self.bitmap) - x = int(bbox.x0) - y = int(self.bitmap.GetHeight() - bbox.y1) - destDC.Blit(x, y, int(bbox.width), int(bbox.height), srcDC, x, y) - destDC.SelectObject(wx.NullBitmap) - srcDC.SelectObject(wx.NullBitmap) - self.gui_repaint() - - -def _rgba_to_wx_bitmap(rgba): - """Convert an RGBA buffer to a wx.Bitmap.""" - h, w, _ = rgba.shape - return wx.Bitmap.FromBufferRGBA(w, h, rgba) - - -@_BackendWx.export -class _BackendWxAgg(_BackendWx): - FigureCanvas = FigureCanvasWxAgg diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_wxcairo.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_wxcairo.py deleted file mode 100644 index 0416a18..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/backend_wxcairo.py +++ /dev/null @@ -1,40 +0,0 @@ -import wx.lib.wxcairo as wxcairo - -from .. import _api -from .backend_cairo import cairo, FigureCanvasCairo -from .backend_wx import _BackendWx, _FigureCanvasWxBase, FigureFrameWx -from .backend_wx import ( # noqa: F401 # pylint: disable=W0611 - NavigationToolbar2Wx as NavigationToolbar2WxCairo) - - -@_api.deprecated( - "3.6", alternative="FigureFrameWx(..., canvas_class=FigureCanvasWxCairo)") -class FigureFrameWxCairo(FigureFrameWx): - def get_canvas(self, fig): - return FigureCanvasWxCairo(self, -1, fig) - - -class FigureCanvasWxCairo(FigureCanvasCairo, _FigureCanvasWxBase): - """ - The FigureCanvas contains the figure and does event handling. - - In the wxPython backend, it is derived from wxPanel, and (usually) lives - inside a frame instantiated by a FigureManagerWx. The parent window - probably implements a wxSizer to control the displayed control size - but - we give a hint as to our preferred minimum size. - """ - - def draw(self, drawDC=None): - size = self.figure.bbox.size.astype(int) - surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, *size) - self._renderer.set_context(cairo.Context(surface)) - self._renderer.dpi = self.figure.dpi - self.figure.draw(self._renderer) - self.bitmap = wxcairo.BitmapFromImageSurface(surface) - self._isDrawn = True - self.gui_repaint(drawDC=drawDC) - - -@_BackendWx.export -class _BackendWxCairo(_BackendWx): - FigureCanvas = FigureCanvasWxCairo diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/qt_compat.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/qt_compat.py deleted file mode 100644 index 6636718..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/qt_compat.py +++ /dev/null @@ -1,245 +0,0 @@ -""" -Qt binding and backend selector. - -The selection logic is as follows: -- if any of PyQt6, PySide6, PyQt5, or PySide2 have already been - imported (checked in that order), use it; -- otherwise, if the QT_API environment variable (used by Enthought) is set, use - it to determine which binding to use; -- otherwise, use whatever the rcParams indicate. -""" - -import functools -import operator -import os -import platform -import sys -import signal -import socket -import contextlib - -from packaging.version import parse as parse_version - -import matplotlib as mpl - -from . import _QT_FORCE_QT5_BINDING - -QT_API_PYQT6 = "PyQt6" -QT_API_PYSIDE6 = "PySide6" -QT_API_PYQT5 = "PyQt5" -QT_API_PYSIDE2 = "PySide2" -QT_API_ENV = os.environ.get("QT_API") -if QT_API_ENV is not None: - QT_API_ENV = QT_API_ENV.lower() -_ETS = { # Mapping of QT_API_ENV to requested binding. - "pyqt6": QT_API_PYQT6, "pyside6": QT_API_PYSIDE6, - "pyqt5": QT_API_PYQT5, "pyside2": QT_API_PYSIDE2, -} -# First, check if anything is already imported. -if sys.modules.get("PyQt6.QtCore"): - QT_API = QT_API_PYQT6 -elif sys.modules.get("PySide6.QtCore"): - QT_API = QT_API_PYSIDE6 -elif sys.modules.get("PyQt5.QtCore"): - QT_API = QT_API_PYQT5 -elif sys.modules.get("PySide2.QtCore"): - QT_API = QT_API_PYSIDE2 -# Otherwise, check the QT_API environment variable (from Enthought). This can -# only override the binding, not the backend (in other words, we check that the -# requested backend actually matches). Use _get_backend_or_none to avoid -# triggering backend resolution (which can result in a partially but -# incompletely imported backend_qt5). -elif (mpl.rcParams._get_backend_or_none() or "").lower().startswith("qt5"): - if QT_API_ENV in ["pyqt5", "pyside2"]: - QT_API = _ETS[QT_API_ENV] - else: - _QT_FORCE_QT5_BINDING = True # noqa - QT_API = None -# A non-Qt backend was selected but we still got there (possible, e.g., when -# fully manually embedding Matplotlib in a Qt app without using pyplot). -elif QT_API_ENV is None: - QT_API = None -elif QT_API_ENV in _ETS: - QT_API = _ETS[QT_API_ENV] -else: - raise RuntimeError( - "The environment variable QT_API has the unrecognized value {!r}; " - "valid values are {}".format(QT_API_ENV, ", ".join(_ETS))) - - -def _setup_pyqt5plus(): - global QtCore, QtGui, QtWidgets, __version__ - global _getSaveFileName, _isdeleted, _to_int - - if QT_API == QT_API_PYQT6: - from PyQt6 import QtCore, QtGui, QtWidgets, sip - __version__ = QtCore.PYQT_VERSION_STR - QtCore.Signal = QtCore.pyqtSignal - QtCore.Slot = QtCore.pyqtSlot - QtCore.Property = QtCore.pyqtProperty - _isdeleted = sip.isdeleted - _to_int = operator.attrgetter('value') - elif QT_API == QT_API_PYSIDE6: - from PySide6 import QtCore, QtGui, QtWidgets, __version__ - import shiboken6 - def _isdeleted(obj): return not shiboken6.isValid(obj) - if parse_version(__version__) >= parse_version('6.4'): - _to_int = operator.attrgetter('value') - else: - _to_int = int - elif QT_API == QT_API_PYQT5: - from PyQt5 import QtCore, QtGui, QtWidgets - import sip - __version__ = QtCore.PYQT_VERSION_STR - QtCore.Signal = QtCore.pyqtSignal - QtCore.Slot = QtCore.pyqtSlot - QtCore.Property = QtCore.pyqtProperty - _isdeleted = sip.isdeleted - _to_int = int - elif QT_API == QT_API_PYSIDE2: - from PySide2 import QtCore, QtGui, QtWidgets, __version__ - try: - from PySide2 import shiboken2 - except ImportError: - import shiboken2 - def _isdeleted(obj): - return not shiboken2.isValid(obj) - _to_int = int - else: - raise AssertionError(f"Unexpected QT_API: {QT_API}") - _getSaveFileName = QtWidgets.QFileDialog.getSaveFileName - - -if QT_API in [QT_API_PYQT6, QT_API_PYQT5, QT_API_PYSIDE6, QT_API_PYSIDE2]: - _setup_pyqt5plus() -elif QT_API is None: # See above re: dict.__getitem__. - if _QT_FORCE_QT5_BINDING: - _candidates = [ - (_setup_pyqt5plus, QT_API_PYQT5), - (_setup_pyqt5plus, QT_API_PYSIDE2), - ] - else: - _candidates = [ - (_setup_pyqt5plus, QT_API_PYQT6), - (_setup_pyqt5plus, QT_API_PYSIDE6), - (_setup_pyqt5plus, QT_API_PYQT5), - (_setup_pyqt5plus, QT_API_PYSIDE2), - ] - for _setup, QT_API in _candidates: - try: - _setup() - except ImportError: - continue - break - else: - raise ImportError( - "Failed to import any of the following Qt binding modules: {}" - .format(", ".join(_ETS.values()))) -else: # We should not get there. - raise AssertionError(f"Unexpected QT_API: {QT_API}") -_version_info = tuple(QtCore.QLibraryInfo.version().segments()) - - -if _version_info < (5, 10): - raise ImportError( - f"The Qt version imported is " - f"{QtCore.QLibraryInfo.version().toString()} but Matplotlib requires " - f"Qt>=5.10") - - -# Fixes issues with Big Sur -# https://bugreports.qt.io/browse/QTBUG-87014, fixed in qt 5.15.2 -if (sys.platform == 'darwin' and - parse_version(platform.mac_ver()[0]) >= parse_version("10.16") and - _version_info < (5, 15, 2)): - os.environ.setdefault("QT_MAC_WANTS_LAYER", "1") - - -# PyQt6 enum compat helpers. - - -@functools.lru_cache(None) -def _enum(name): - # foo.bar.Enum.Entry (PyQt6) <=> foo.bar.Entry (non-PyQt6). - return operator.attrgetter( - name if QT_API == 'PyQt6' else name.rpartition(".")[0] - )(sys.modules[QtCore.__package__]) - - -# Backports. - - -def _exec(obj): - # exec on PyQt6, exec_ elsewhere. - obj.exec() if hasattr(obj, "exec") else obj.exec_() - - -@contextlib.contextmanager -def _maybe_allow_interrupt(qapp): - """ - This manager allows to terminate a plot by sending a SIGINT. It is - necessary because the running Qt backend prevents Python interpreter to - run and process signals (i.e., to raise KeyboardInterrupt exception). To - solve this one needs to somehow wake up the interpreter and make it close - the plot window. We do this by using the signal.set_wakeup_fd() function - which organizes a write of the signal number into a socketpair connected - to the QSocketNotifier (since it is part of the Qt backend, it can react - to that write event). Afterwards, the Qt handler empties the socketpair - by a recv() command to re-arm it (we need this if a signal different from - SIGINT was caught by set_wakeup_fd() and we shall continue waiting). If - the SIGINT was caught indeed, after exiting the on_signal() function the - interpreter reacts to the SIGINT according to the handle() function which - had been set up by a signal.signal() call: it causes the qt_object to - exit by calling its quit() method. Finally, we call the old SIGINT - handler with the same arguments that were given to our custom handle() - handler. - - We do this only if the old handler for SIGINT was not None, which means - that a non-python handler was installed, i.e. in Julia, and not SIG_IGN - which means we should ignore the interrupts. - """ - old_sigint_handler = signal.getsignal(signal.SIGINT) - handler_args = None - skip = False - if old_sigint_handler in (None, signal.SIG_IGN, signal.SIG_DFL): - skip = True - else: - wsock, rsock = socket.socketpair() - wsock.setblocking(False) - old_wakeup_fd = signal.set_wakeup_fd(wsock.fileno()) - sn = QtCore.QSocketNotifier( - rsock.fileno(), _enum('QtCore.QSocketNotifier.Type').Read - ) - - # We do not actually care about this value other than running some - # Python code to ensure that the interpreter has a chance to handle the - # signal in Python land. We also need to drain the socket because it - # will be written to as part of the wakeup! There are some cases where - # this may fire too soon / more than once on Windows so we should be - # forgiving about reading an empty socket. - rsock.setblocking(False) - # Clear the socket to re-arm the notifier. - @sn.activated.connect - def _may_clear_sock(*args): - try: - rsock.recv(1) - except BlockingIOError: - pass - - def handle(*args): - nonlocal handler_args - handler_args = args - qapp.quit() - - signal.signal(signal.SIGINT, handle) - try: - yield - finally: - if not skip: - wsock.close() - rsock.close() - sn.setEnabled(False) - signal.set_wakeup_fd(old_wakeup_fd) - signal.signal(signal.SIGINT, old_sigint_handler) - if handler_args is not None: - old_sigint_handler(*handler_args) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/qt_editor/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/qt_editor/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/qt_editor/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/qt_editor/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index a25bfb0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/qt_editor/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/qt_editor/__pycache__/_formlayout.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/qt_editor/__pycache__/_formlayout.cpython-38.pyc deleted file mode 100644 index ed395de..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/qt_editor/__pycache__/_formlayout.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/qt_editor/__pycache__/figureoptions.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/qt_editor/__pycache__/figureoptions.cpython-38.pyc deleted file mode 100644 index 706546c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/qt_editor/__pycache__/figureoptions.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/qt_editor/_formlayout.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/qt_editor/_formlayout.py deleted file mode 100644 index 1306e0c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/qt_editor/_formlayout.py +++ /dev/null @@ -1,593 +0,0 @@ -""" -formlayout -========== - -Module creating Qt form dialogs/layouts to edit various type of parameters - - -formlayout License Agreement (MIT License) ------------------------------------------- - -Copyright (c) 2009 Pierre Raybaut - -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. -""" - -# History: -# 1.0.10: added float validator -# (disable "Ok" and "Apply" button when not valid) -# 1.0.7: added support for "Apply" button -# 1.0.6: code cleaning - -__version__ = '1.0.10' -__license__ = __doc__ - -import copy -import datetime -import logging -from numbers import Integral, Real - -from matplotlib import _api, colors as mcolors -from matplotlib.backends.qt_compat import ( - QtGui, QtWidgets, QtCore, _enum, _to_int) - -_log = logging.getLogger(__name__) - -BLACKLIST = {"title", "label"} - - -class ColorButton(QtWidgets.QPushButton): - """ - Color choosing push button - """ - colorChanged = QtCore.Signal(QtGui.QColor) - - def __init__(self, parent=None): - super().__init__(parent) - self.setFixedSize(20, 20) - self.setIconSize(QtCore.QSize(12, 12)) - self.clicked.connect(self.choose_color) - self._color = QtGui.QColor() - - def choose_color(self): - color = QtWidgets.QColorDialog.getColor( - self._color, self.parentWidget(), "", - _enum("QtWidgets.QColorDialog.ColorDialogOption").ShowAlphaChannel) - if color.isValid(): - self.set_color(color) - - def get_color(self): - return self._color - - @QtCore.Slot(QtGui.QColor) - def set_color(self, color): - if color != self._color: - self._color = color - self.colorChanged.emit(self._color) - pixmap = QtGui.QPixmap(self.iconSize()) - pixmap.fill(color) - self.setIcon(QtGui.QIcon(pixmap)) - - color = QtCore.Property(QtGui.QColor, get_color, set_color) - - -def to_qcolor(color): - """Create a QColor from a matplotlib color""" - qcolor = QtGui.QColor() - try: - rgba = mcolors.to_rgba(color) - except ValueError: - _api.warn_external(f'Ignoring invalid color {color!r}') - return qcolor # return invalid QColor - qcolor.setRgbF(*rgba) - return qcolor - - -class ColorLayout(QtWidgets.QHBoxLayout): - """Color-specialized QLineEdit layout""" - def __init__(self, color, parent=None): - super().__init__() - assert isinstance(color, QtGui.QColor) - self.lineedit = QtWidgets.QLineEdit( - mcolors.to_hex(color.getRgbF(), keep_alpha=True), parent) - self.lineedit.editingFinished.connect(self.update_color) - self.addWidget(self.lineedit) - self.colorbtn = ColorButton(parent) - self.colorbtn.color = color - self.colorbtn.colorChanged.connect(self.update_text) - self.addWidget(self.colorbtn) - - def update_color(self): - color = self.text() - qcolor = to_qcolor(color) # defaults to black if not qcolor.isValid() - self.colorbtn.color = qcolor - - def update_text(self, color): - self.lineedit.setText(mcolors.to_hex(color.getRgbF(), keep_alpha=True)) - - def text(self): - return self.lineedit.text() - - -def font_is_installed(font): - """Check if font is installed""" - return [fam for fam in QtGui.QFontDatabase().families() - if str(fam) == font] - - -def tuple_to_qfont(tup): - """ - Create a QFont from tuple: - (family [string], size [int], italic [bool], bold [bool]) - """ - if not (isinstance(tup, tuple) and len(tup) == 4 - and font_is_installed(tup[0]) - and isinstance(tup[1], Integral) - and isinstance(tup[2], bool) - and isinstance(tup[3], bool)): - return None - font = QtGui.QFont() - family, size, italic, bold = tup - font.setFamily(family) - font.setPointSize(size) - font.setItalic(italic) - font.setBold(bold) - return font - - -def qfont_to_tuple(font): - return (str(font.family()), int(font.pointSize()), - font.italic(), font.bold()) - - -class FontLayout(QtWidgets.QGridLayout): - """Font selection""" - def __init__(self, value, parent=None): - super().__init__() - font = tuple_to_qfont(value) - assert font is not None - - # Font family - self.family = QtWidgets.QFontComboBox(parent) - self.family.setCurrentFont(font) - self.addWidget(self.family, 0, 0, 1, -1) - - # Font size - self.size = QtWidgets.QComboBox(parent) - self.size.setEditable(True) - sizelist = [*range(6, 12), *range(12, 30, 2), 36, 48, 72] - size = font.pointSize() - if size not in sizelist: - sizelist.append(size) - sizelist.sort() - self.size.addItems([str(s) for s in sizelist]) - self.size.setCurrentIndex(sizelist.index(size)) - self.addWidget(self.size, 1, 0) - - # Italic or not - self.italic = QtWidgets.QCheckBox(self.tr("Italic"), parent) - self.italic.setChecked(font.italic()) - self.addWidget(self.italic, 1, 1) - - # Bold or not - self.bold = QtWidgets.QCheckBox(self.tr("Bold"), parent) - self.bold.setChecked(font.bold()) - self.addWidget(self.bold, 1, 2) - - def get_font(self): - font = self.family.currentFont() - font.setItalic(self.italic.isChecked()) - font.setBold(self.bold.isChecked()) - font.setPointSize(int(self.size.currentText())) - return qfont_to_tuple(font) - - -def is_edit_valid(edit): - text = edit.text() - state = edit.validator().validate(text, 0)[0] - return state == _enum("QtGui.QDoubleValidator.State").Acceptable - - -class FormWidget(QtWidgets.QWidget): - update_buttons = QtCore.Signal() - - def __init__(self, data, comment="", with_margin=False, parent=None): - """ - Parameters - ---------- - data : list of (label, value) pairs - The data to be edited in the form. - comment : str, optional - with_margin : bool, default: False - If False, the form elements reach to the border of the widget. - This is the desired behavior if the FormWidget is used as a widget - alongside with other widgets such as a QComboBox, which also do - not have a margin around them. - However, a margin can be desired if the FormWidget is the only - widget within a container, e.g. a tab in a QTabWidget. - parent : QWidget or None - The parent widget. - """ - super().__init__(parent) - self.data = copy.deepcopy(data) - self.widgets = [] - self.formlayout = QtWidgets.QFormLayout(self) - if not with_margin: - self.formlayout.setContentsMargins(0, 0, 0, 0) - if comment: - self.formlayout.addRow(QtWidgets.QLabel(comment)) - self.formlayout.addRow(QtWidgets.QLabel(" ")) - - def get_dialog(self): - """Return FormDialog instance""" - dialog = self.parent() - while not isinstance(dialog, QtWidgets.QDialog): - dialog = dialog.parent() - return dialog - - def setup(self): - for label, value in self.data: - if label is None and value is None: - # Separator: (None, None) - self.formlayout.addRow(QtWidgets.QLabel(" "), - QtWidgets.QLabel(" ")) - self.widgets.append(None) - continue - elif label is None: - # Comment - self.formlayout.addRow(QtWidgets.QLabel(value)) - self.widgets.append(None) - continue - elif tuple_to_qfont(value) is not None: - field = FontLayout(value, self) - elif (label.lower() not in BLACKLIST - and mcolors.is_color_like(value)): - field = ColorLayout(to_qcolor(value), self) - elif isinstance(value, str): - field = QtWidgets.QLineEdit(value, self) - elif isinstance(value, (list, tuple)): - if isinstance(value, tuple): - value = list(value) - # Note: get() below checks the type of value[0] in self.data so - # it is essential that value gets modified in-place. - # This means that the code is actually broken in the case where - # value is a tuple, but fortunately we always pass a list... - selindex = value.pop(0) - field = QtWidgets.QComboBox(self) - if isinstance(value[0], (list, tuple)): - keys = [key for key, _val in value] - value = [val for _key, val in value] - else: - keys = value - field.addItems(value) - if selindex in value: - selindex = value.index(selindex) - elif selindex in keys: - selindex = keys.index(selindex) - elif not isinstance(selindex, Integral): - _log.warning( - "index '%s' is invalid (label: %s, value: %s)", - selindex, label, value) - selindex = 0 - field.setCurrentIndex(selindex) - elif isinstance(value, bool): - field = QtWidgets.QCheckBox(self) - field.setChecked(value) - elif isinstance(value, Integral): - field = QtWidgets.QSpinBox(self) - field.setRange(-10**9, 10**9) - field.setValue(value) - elif isinstance(value, Real): - field = QtWidgets.QLineEdit(repr(value), self) - field.setCursorPosition(0) - field.setValidator(QtGui.QDoubleValidator(field)) - field.validator().setLocale(QtCore.QLocale("C")) - dialog = self.get_dialog() - dialog.register_float_field(field) - field.textChanged.connect(lambda text: dialog.update_buttons()) - elif isinstance(value, datetime.datetime): - field = QtWidgets.QDateTimeEdit(self) - field.setDateTime(value) - elif isinstance(value, datetime.date): - field = QtWidgets.QDateEdit(self) - field.setDate(value) - else: - field = QtWidgets.QLineEdit(repr(value), self) - self.formlayout.addRow(label, field) - self.widgets.append(field) - - def get(self): - valuelist = [] - for index, (label, value) in enumerate(self.data): - field = self.widgets[index] - if label is None: - # Separator / Comment - continue - elif tuple_to_qfont(value) is not None: - value = field.get_font() - elif isinstance(value, str) or mcolors.is_color_like(value): - value = str(field.text()) - elif isinstance(value, (list, tuple)): - index = int(field.currentIndex()) - if isinstance(value[0], (list, tuple)): - value = value[index][0] - else: - value = value[index] - elif isinstance(value, bool): - value = field.isChecked() - elif isinstance(value, Integral): - value = int(field.value()) - elif isinstance(value, Real): - value = float(str(field.text())) - elif isinstance(value, datetime.datetime): - datetime_ = field.dateTime() - if hasattr(datetime_, "toPyDateTime"): - value = datetime_.toPyDateTime() - else: - value = datetime_.toPython() - elif isinstance(value, datetime.date): - date_ = field.date() - if hasattr(date_, "toPyDate"): - value = date_.toPyDate() - else: - value = date_.toPython() - else: - value = eval(str(field.text())) - valuelist.append(value) - return valuelist - - -class FormComboWidget(QtWidgets.QWidget): - update_buttons = QtCore.Signal() - - def __init__(self, datalist, comment="", parent=None): - super().__init__(parent) - layout = QtWidgets.QVBoxLayout() - self.setLayout(layout) - self.combobox = QtWidgets.QComboBox() - layout.addWidget(self.combobox) - - self.stackwidget = QtWidgets.QStackedWidget(self) - layout.addWidget(self.stackwidget) - self.combobox.currentIndexChanged.connect( - self.stackwidget.setCurrentIndex) - - self.widgetlist = [] - for data, title, comment in datalist: - self.combobox.addItem(title) - widget = FormWidget(data, comment=comment, parent=self) - self.stackwidget.addWidget(widget) - self.widgetlist.append(widget) - - def setup(self): - for widget in self.widgetlist: - widget.setup() - - def get(self): - return [widget.get() for widget in self.widgetlist] - - -class FormTabWidget(QtWidgets.QWidget): - update_buttons = QtCore.Signal() - - def __init__(self, datalist, comment="", parent=None): - super().__init__(parent) - layout = QtWidgets.QVBoxLayout() - self.tabwidget = QtWidgets.QTabWidget() - layout.addWidget(self.tabwidget) - layout.setContentsMargins(0, 0, 0, 0) - self.setLayout(layout) - self.widgetlist = [] - for data, title, comment in datalist: - if len(data[0]) == 3: - widget = FormComboWidget(data, comment=comment, parent=self) - else: - widget = FormWidget(data, with_margin=True, comment=comment, - parent=self) - index = self.tabwidget.addTab(widget, title) - self.tabwidget.setTabToolTip(index, comment) - self.widgetlist.append(widget) - - def setup(self): - for widget in self.widgetlist: - widget.setup() - - def get(self): - return [widget.get() for widget in self.widgetlist] - - -class FormDialog(QtWidgets.QDialog): - """Form Dialog""" - def __init__(self, data, title="", comment="", - icon=None, parent=None, apply=None): - super().__init__(parent) - - self.apply_callback = apply - - # Form - if isinstance(data[0][0], (list, tuple)): - self.formwidget = FormTabWidget(data, comment=comment, - parent=self) - elif len(data[0]) == 3: - self.formwidget = FormComboWidget(data, comment=comment, - parent=self) - else: - self.formwidget = FormWidget(data, comment=comment, - parent=self) - layout = QtWidgets.QVBoxLayout() - layout.addWidget(self.formwidget) - - self.float_fields = [] - self.formwidget.setup() - - # Button box - self.bbox = bbox = QtWidgets.QDialogButtonBox( - QtWidgets.QDialogButtonBox.StandardButton( - _to_int( - _enum("QtWidgets.QDialogButtonBox.StandardButton").Ok) | - _to_int( - _enum("QtWidgets.QDialogButtonBox.StandardButton").Cancel) - )) - self.formwidget.update_buttons.connect(self.update_buttons) - if self.apply_callback is not None: - apply_btn = bbox.addButton( - _enum("QtWidgets.QDialogButtonBox.StandardButton").Apply) - apply_btn.clicked.connect(self.apply) - - bbox.accepted.connect(self.accept) - bbox.rejected.connect(self.reject) - layout.addWidget(bbox) - - self.setLayout(layout) - - self.setWindowTitle(title) - if not isinstance(icon, QtGui.QIcon): - icon = QtWidgets.QWidget().style().standardIcon( - QtWidgets.QStyle.SP_MessageBoxQuestion) - self.setWindowIcon(icon) - - def register_float_field(self, field): - self.float_fields.append(field) - - def update_buttons(self): - valid = True - for field in self.float_fields: - if not is_edit_valid(field): - valid = False - for btn_type in ["Ok", "Apply"]: - btn = self.bbox.button( - getattr(_enum("QtWidgets.QDialogButtonBox.StandardButton"), - btn_type)) - if btn is not None: - btn.setEnabled(valid) - - def accept(self): - self.data = self.formwidget.get() - self.apply_callback(self.data) - super().accept() - - def reject(self): - self.data = None - super().reject() - - def apply(self): - self.apply_callback(self.formwidget.get()) - - def get(self): - """Return form result""" - return self.data - - -def fedit(data, title="", comment="", icon=None, parent=None, apply=None): - """ - Create form dialog - - data: datalist, datagroup - title: str - comment: str - icon: QIcon instance - parent: parent QWidget - apply: apply callback (function) - - datalist: list/tuple of (field_name, field_value) - datagroup: list/tuple of (datalist *or* datagroup, title, comment) - - -> one field for each member of a datalist - -> one tab for each member of a top-level datagroup - -> one page (of a multipage widget, each page can be selected with a combo - box) for each member of a datagroup inside a datagroup - - Supported types for field_value: - - int, float, str, bool - - colors: in Qt-compatible text form, i.e. in hex format or name - (red, ...) (automatically detected from a string) - - list/tuple: - * the first element will be the selected index (or value) - * the other elements can be couples (key, value) or only values - """ - - # Create a QApplication instance if no instance currently exists - # (e.g., if the module is used directly from the interpreter) - if QtWidgets.QApplication.startingUp(): - _app = QtWidgets.QApplication([]) - dialog = FormDialog(data, title, comment, icon, parent, apply) - - if parent is not None: - if hasattr(parent, "_fedit_dialog"): - parent._fedit_dialog.close() - parent._fedit_dialog = dialog - - dialog.show() - - -if __name__ == "__main__": - - _app = QtWidgets.QApplication([]) - - def create_datalist_example(): - return [('str', 'this is a string'), - ('list', [0, '1', '3', '4']), - ('list2', ['--', ('none', 'None'), ('--', 'Dashed'), - ('-.', 'DashDot'), ('-', 'Solid'), - ('steps', 'Steps'), (':', 'Dotted')]), - ('float', 1.2), - (None, 'Other:'), - ('int', 12), - ('font', ('Arial', 10, False, True)), - ('color', '#123409'), - ('bool', True), - ('date', datetime.date(2010, 10, 10)), - ('datetime', datetime.datetime(2010, 10, 10)), - ] - - def create_datagroup_example(): - datalist = create_datalist_example() - return ((datalist, "Category 1", "Category 1 comment"), - (datalist, "Category 2", "Category 2 comment"), - (datalist, "Category 3", "Category 3 comment")) - - # --------- datalist example - datalist = create_datalist_example() - - def apply_test(data): - print("data:", data) - fedit(datalist, title="Example", - comment="This is just an example.", - apply=apply_test) - - _app.exec() - - # --------- datagroup example - datagroup = create_datagroup_example() - fedit(datagroup, "Global title", - apply=apply_test) - _app.exec() - - # --------- datagroup inside a datagroup example - datalist = create_datalist_example() - datagroup = create_datagroup_example() - fedit(((datagroup, "Title 1", "Tab 1 comment"), - (datalist, "Title 2", "Tab 2 comment"), - (datalist, "Title 3", "Tab 3 comment")), - "Global title", - apply=apply_test) - _app.exec() diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/qt_editor/figureoptions.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/qt_editor/figureoptions.py deleted file mode 100644 index c744ccc..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/qt_editor/figureoptions.py +++ /dev/null @@ -1,263 +0,0 @@ -# Copyright © 2009 Pierre Raybaut -# Licensed under the terms of the MIT License -# see the Matplotlib licenses directory for a copy of the license - - -"""Module that provides a GUI-based editor for Matplotlib's figure options.""" - -from itertools import chain -from matplotlib import cbook, cm, colors as mcolors, markers, image as mimage -from matplotlib.backends.qt_compat import QtGui -from matplotlib.backends.qt_editor import _formlayout -from matplotlib.dates import DateConverter, num2date - -LINESTYLES = {'-': 'Solid', - '--': 'Dashed', - '-.': 'DashDot', - ':': 'Dotted', - 'None': 'None', - } - -DRAWSTYLES = { - 'default': 'Default', - 'steps-pre': 'Steps (Pre)', 'steps': 'Steps (Pre)', - 'steps-mid': 'Steps (Mid)', - 'steps-post': 'Steps (Post)'} - -MARKERS = markers.MarkerStyle.markers - - -def figure_edit(axes, parent=None): - """Edit matplotlib figure options""" - sep = (None, None) # separator - - # Get / General - def convert_limits(lim, converter): - """Convert axis limits for correct input editors.""" - if isinstance(converter, DateConverter): - return map(num2date, lim) - # Cast to builtin floats as they have nicer reprs. - return map(float, lim) - - axis_map = axes._axis_map - axis_limits = { - name: tuple(convert_limits( - getattr(axes, f'get_{name}lim')(), axis.converter - )) - for name, axis in axis_map.items() - } - general = [ - ('Title', axes.get_title()), - sep, - *chain.from_iterable([ - ( - (None, f"{name.title()}-Axis"), - ('Min', axis_limits[name][0]), - ('Max', axis_limits[name][1]), - ('Label', axis.get_label().get_text()), - ('Scale', [axis.get_scale(), - 'linear', 'log', 'symlog', 'logit']), - sep, - ) - for name, axis in axis_map.items() - ]), - ('(Re-)Generate automatic legend', False), - ] - - # Save the converter and unit data - axis_converter = { - name: axis.converter - for name, axis in axis_map.items() - } - axis_units = { - name: axis.get_units() - for name, axis in axis_map.items() - } - - # Get / Curves - labeled_lines = [] - for line in axes.get_lines(): - label = line.get_label() - if label == '_nolegend_': - continue - labeled_lines.append((label, line)) - curves = [] - - def prepare_data(d, init): - """ - Prepare entry for FormLayout. - - *d* is a mapping of shorthands to style names (a single style may - have multiple shorthands, in particular the shorthands `None`, - `"None"`, `"none"` and `""` are synonyms); *init* is one shorthand - of the initial style. - - This function returns an list suitable for initializing a - FormLayout combobox, namely `[initial_name, (shorthand, - style_name), (shorthand, style_name), ...]`. - """ - if init not in d: - d = {**d, init: str(init)} - # Drop duplicate shorthands from dict (by overwriting them during - # the dict comprehension). - name2short = {name: short for short, name in d.items()} - # Convert back to {shorthand: name}. - short2name = {short: name for name, short in name2short.items()} - # Find the kept shorthand for the style specified by init. - canonical_init = name2short[d[init]] - # Sort by representation and prepend the initial value. - return ([canonical_init] + - sorted(short2name.items(), - key=lambda short_and_name: short_and_name[1])) - - for label, line in labeled_lines: - color = mcolors.to_hex( - mcolors.to_rgba(line.get_color(), line.get_alpha()), - keep_alpha=True) - ec = mcolors.to_hex( - mcolors.to_rgba(line.get_markeredgecolor(), line.get_alpha()), - keep_alpha=True) - fc = mcolors.to_hex( - mcolors.to_rgba(line.get_markerfacecolor(), line.get_alpha()), - keep_alpha=True) - curvedata = [ - ('Label', label), - sep, - (None, 'Line'), - ('Line style', prepare_data(LINESTYLES, line.get_linestyle())), - ('Draw style', prepare_data(DRAWSTYLES, line.get_drawstyle())), - ('Width', line.get_linewidth()), - ('Color (RGBA)', color), - sep, - (None, 'Marker'), - ('Style', prepare_data(MARKERS, line.get_marker())), - ('Size', line.get_markersize()), - ('Face color (RGBA)', fc), - ('Edge color (RGBA)', ec)] - curves.append([curvedata, label, ""]) - # Is there a curve displayed? - has_curve = bool(curves) - - # Get ScalarMappables. - labeled_mappables = [] - for mappable in [*axes.images, *axes.collections]: - label = mappable.get_label() - if label == '_nolegend_' or mappable.get_array() is None: - continue - labeled_mappables.append((label, mappable)) - mappables = [] - cmaps = [(cmap, name) for name, cmap in sorted(cm._colormaps.items())] - for label, mappable in labeled_mappables: - cmap = mappable.get_cmap() - if cmap not in cm._colormaps.values(): - cmaps = [(cmap, cmap.name), *cmaps] - low, high = mappable.get_clim() - mappabledata = [ - ('Label', label), - ('Colormap', [cmap.name] + cmaps), - ('Min. value', low), - ('Max. value', high), - ] - if hasattr(mappable, "get_interpolation"): # Images. - interpolations = [ - (name, name) for name in sorted(mimage.interpolations_names)] - mappabledata.append(( - 'Interpolation', - [mappable.get_interpolation(), *interpolations])) - mappables.append([mappabledata, label, ""]) - # Is there a scalarmappable displayed? - has_sm = bool(mappables) - - datalist = [(general, "Axes", "")] - if curves: - datalist.append((curves, "Curves", "")) - if mappables: - datalist.append((mappables, "Images, etc.", "")) - - def apply_callback(data): - """A callback to apply changes.""" - orig_limits = { - name: getattr(axes, f"get_{name}lim")() - for name in axis_map - } - - general = data.pop(0) - curves = data.pop(0) if has_curve else [] - mappables = data.pop(0) if has_sm else [] - if data: - raise ValueError("Unexpected field") - - title = general.pop(0) - axes.set_title(title) - generate_legend = general.pop() - - for i, (name, axis) in enumerate(axis_map.items()): - axis_min = general[4*i] - axis_max = general[4*i + 1] - axis_label = general[4*i + 2] - axis_scale = general[4*i + 3] - if axis.get_scale() != axis_scale: - getattr(axes, f"set_{name}scale")(axis_scale) - - axis._set_lim(axis_min, axis_max, auto=False) - axis.set_label_text(axis_label) - - # Restore the unit data - axis.converter = axis_converter[name] - axis.set_units(axis_units[name]) - - # Set / Curves - for index, curve in enumerate(curves): - line = labeled_lines[index][1] - (label, linestyle, drawstyle, linewidth, color, marker, markersize, - markerfacecolor, markeredgecolor) = curve - line.set_label(label) - line.set_linestyle(linestyle) - line.set_drawstyle(drawstyle) - line.set_linewidth(linewidth) - rgba = mcolors.to_rgba(color) - line.set_alpha(None) - line.set_color(rgba) - if marker != 'none': - line.set_marker(marker) - line.set_markersize(markersize) - line.set_markerfacecolor(markerfacecolor) - line.set_markeredgecolor(markeredgecolor) - - # Set ScalarMappables. - for index, mappable_settings in enumerate(mappables): - mappable = labeled_mappables[index][1] - if len(mappable_settings) == 5: - label, cmap, low, high, interpolation = mappable_settings - mappable.set_interpolation(interpolation) - elif len(mappable_settings) == 4: - label, cmap, low, high = mappable_settings - mappable.set_label(label) - mappable.set_cmap(cmap) - mappable.set_clim(*sorted([low, high])) - - # re-generate legend, if checkbox is checked - if generate_legend: - draggable = None - ncols = 1 - if axes.legend_ is not None: - old_legend = axes.get_legend() - draggable = old_legend._draggable is not None - ncols = old_legend._ncols - new_legend = axes.legend(ncols=ncols) - if new_legend: - new_legend.set_draggable(draggable) - - # Redraw - figure = axes.get_figure() - figure.canvas.draw() - for name in axis_map: - if getattr(axes, f"get_{name}lim")() != orig_limits[name]: - figure.canvas.toolbar.push_current() - break - - _formlayout.fedit( - datalist, title="Figure options", parent=parent, - icon=QtGui.QIcon( - str(cbook._get_data_path('images', 'qt4_editor_options.svg'))), - apply=apply_callback) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/.eslintrc.js b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/.eslintrc.js deleted file mode 100644 index 6f3581a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/.eslintrc.js +++ /dev/null @@ -1,32 +0,0 @@ -module.exports = { - root: true, - ignorePatterns: ["jquery-ui-*/", "node_modules/"], - env: { - browser: true, - jquery: true, - }, - extends: ["eslint:recommended", "prettier"], - globals: { - IPython: "readonly", - MozWebSocket: "readonly", - }, - rules: { - indent: ["error", 2, { SwitchCase: 1 }], - "no-unused-vars": [ - "error", - { - argsIgnorePattern: "^_", - }, - ], - quotes: ["error", "double", { avoidEscape: true }], - }, - overrides: [ - { - files: "js/**/*.js", - rules: { - indent: ["error", 4, { SwitchCase: 1 }], - quotes: ["error", "single", { avoidEscape: true }], - }, - }, - ], -}; diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/.prettierignore b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/.prettierignore deleted file mode 100644 index 06a29c6..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/.prettierignore +++ /dev/null @@ -1,7 +0,0 @@ -node_modules/ - -# Vendored dependencies -css/boilerplate.css -css/fbm.css -css/page.css -jquery-ui-*/ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/.prettierrc b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/.prettierrc deleted file mode 100644 index fe8d711..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/.prettierrc +++ /dev/null @@ -1,11 +0,0 @@ -{ - "overrides": [ - { - "files": "js/**/*.js", - "options": { - "singleQuote": true, - "tabWidth": 4, - } - } - ] -} diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/all_figures.html b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/all_figures.html deleted file mode 100644 index 62f04b6..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/all_figures.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - MPL | WebAgg current figures - - - -
- -
- - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/css/boilerplate.css b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/css/boilerplate.css deleted file mode 100644 index 2b1535f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/css/boilerplate.css +++ /dev/null @@ -1,77 +0,0 @@ -/** - * HTML5 ✰ Boilerplate - * - * style.css contains a reset, font normalization and some base styles. - * - * Credit is left where credit is due. - * Much inspiration was taken from these projects: - * - yui.yahooapis.com/2.8.1/build/base/base.css - * - camendesign.com/design/ - * - praegnanz.de/weblog/htmlcssjs-kickstart - */ - - -/** - * html5doctor.com Reset Stylesheet (Eric Meyer's Reset Reloaded + HTML5 baseline) - * v1.6.1 2010-09-17 | Authors: Eric Meyer & Richard Clark - * html5doctor.com/html-5-reset-stylesheet/ - */ - -html, body, div, span, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, -small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, canvas, details, figcaption, figure, -footer, header, hgroup, menu, nav, section, summary, -time, mark, audio, video { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font: inherit; - vertical-align: baseline; -} - -sup { vertical-align: super; } -sub { vertical-align: sub; } - -article, aside, details, figcaption, figure, -footer, header, hgroup, menu, nav, section { - display: block; -} - -blockquote, q { quotes: none; } - -blockquote:before, blockquote:after, -q:before, q:after { content: ""; content: none; } - -ins { background-color: #ff9; color: #000; text-decoration: none; } - -mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; } - -del { text-decoration: line-through; } - -abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; } - -table { border-collapse: collapse; border-spacing: 0; } - -hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; } - -input, select { vertical-align: middle; } - - -/** - * Font normalization inspired by YUI Library's fonts.css: developer.yahoo.com/yui/ - */ - -body { font:13px/1.231 sans-serif; *font-size:small; } /* Hack retained to preserve specificity */ -select, input, textarea, button { font:99% sans-serif; } - -/* Normalize monospace sizing: - en.wikipedia.org/wiki/MediaWiki_talk:Common.css/Archive_11#Teletype_style_fix_for_Chrome */ -pre, code, kbd, samp { font-family: monospace, sans-serif; } - -em,i { font-style: italic; } -b,strong { font-weight: bold; } diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/css/fbm.css b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/css/fbm.css deleted file mode 100644 index ce35d99..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/css/fbm.css +++ /dev/null @@ -1,97 +0,0 @@ - -/* Flexible box model classes */ -/* Taken from Alex Russell https://infrequently.org/2009/08/css-3-progress/ */ - -.hbox { - display: -webkit-box; - -webkit-box-orient: horizontal; - -webkit-box-align: stretch; - - display: -moz-box; - -moz-box-orient: horizontal; - -moz-box-align: stretch; - - display: box; - box-orient: horizontal; - box-align: stretch; -} - -.hbox > * { - -webkit-box-flex: 0; - -moz-box-flex: 0; - box-flex: 0; -} - -.vbox { - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-box-align: stretch; - - display: -moz-box; - -moz-box-orient: vertical; - -moz-box-align: stretch; - - display: box; - box-orient: vertical; - box-align: stretch; -} - -.vbox > * { - -webkit-box-flex: 0; - -moz-box-flex: 0; - box-flex: 0; -} - -.reverse { - -webkit-box-direction: reverse; - -moz-box-direction: reverse; - box-direction: reverse; -} - -.box-flex0 { - -webkit-box-flex: 0; - -moz-box-flex: 0; - box-flex: 0; -} - -.box-flex1, .box-flex { - -webkit-box-flex: 1; - -moz-box-flex: 1; - box-flex: 1; -} - -.box-flex2 { - -webkit-box-flex: 2; - -moz-box-flex: 2; - box-flex: 2; -} - -.box-group1 { - -webkit-box-flex-group: 1; - -moz-box-flex-group: 1; - box-flex-group: 1; -} - -.box-group2 { - -webkit-box-flex-group: 2; - -moz-box-flex-group: 2; - box-flex-group: 2; -} - -.start { - -webkit-box-pack: start; - -moz-box-pack: start; - box-pack: start; -} - -.end { - -webkit-box-pack: end; - -moz-box-pack: end; - box-pack: end; -} - -.center { - -webkit-box-pack: center; - -moz-box-pack: center; - box-pack: center; -} diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/css/mpl.css b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/css/mpl.css deleted file mode 100644 index e55733d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/css/mpl.css +++ /dev/null @@ -1,84 +0,0 @@ -/* General styling */ -.ui-helper-clearfix:before, -.ui-helper-clearfix:after { - content: ""; - display: table; - border-collapse: collapse; -} -.ui-helper-clearfix:after { - clear: both; -} - -/* Header */ -.ui-widget-header { - border: 1px solid #dddddd; - border-top-left-radius: 6px; - border-top-right-radius: 6px; - background: #e9e9e9; - color: #333333; - font-weight: bold; -} - -/* Toolbar and items */ -.mpl-toolbar { - width: 100%; -} - -.mpl-toolbar div.mpl-button-group { - display: inline-block; -} - -.mpl-button-group + .mpl-button-group { - margin-left: 0.5em; -} - -.mpl-widget { - background-color: #fff; - border: 1px solid #ccc; - display: inline-block; - cursor: pointer; - color: #333; - padding: 6px; - vertical-align: middle; -} - -.mpl-widget:disabled, -.mpl-widget[disabled] { - background-color: #ddd; - border-color: #ddd !important; - cursor: not-allowed; -} - -.mpl-widget:disabled img, -.mpl-widget[disabled] img { - /* Convert black to grey */ - filter: contrast(0%); -} - -.mpl-widget.active img { - /* Convert black to tab:blue, approximately */ - filter: invert(34%) sepia(97%) saturate(468%) hue-rotate(162deg) brightness(96%) contrast(91%); -} - -button.mpl-widget:focus, -button.mpl-widget:hover { - background-color: #ddd; - border-color: #aaa; -} - -.mpl-button-group button.mpl-widget { - margin-left: -1px; -} -.mpl-button-group button.mpl-widget:first-child { - border-top-left-radius: 6px; - border-bottom-left-radius: 6px; - margin-left: 0px; -} -.mpl-button-group button.mpl-widget:last-child { - border-top-right-radius: 6px; - border-bottom-right-radius: 6px; -} - -select.mpl-widget { - cursor: default; -} diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/css/page.css b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/css/page.css deleted file mode 100644 index ded0d92..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/css/page.css +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Primary styles - * - * Author: IPython Development Team - */ - - -body { - background-color: white; - /* This makes sure that the body covers the entire window and needs to - be in a different element than the display: box in wrapper below */ - position: absolute; - left: 0px; - right: 0px; - top: 0px; - bottom: 0px; - overflow: visible; -} - - -div#header { - /* Initially hidden to prevent FLOUC */ - display: none; - position: relative; - height: 40px; - padding: 5px; - margin: 0px; - width: 100%; -} - -span#ipython_notebook { - position: absolute; - padding: 2px 2px 2px 5px; -} - -span#ipython_notebook img { - font-family: Verdana, "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif; - height: 24px; - text-decoration:none; - display: inline; - color: black; -} - -#site { - width: 100%; - display: none; -} - -/* We set the fonts by hand here to override the values in the theme */ -.ui-widget { - font-family: "Lucinda Grande", "Lucinda Sans Unicode", Helvetica, Arial, Verdana, sans-serif; -} - -.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { - font-family: "Lucinda Grande", "Lucinda Sans Unicode", Helvetica, Arial, Verdana, sans-serif; -} - -/* Smaller buttons */ -.ui-button .ui-button-text { - padding: 0.2em 0.8em; - font-size: 77%; -} - -input.ui-button { - padding: 0.3em 0.9em; -} - -span#login_widget { - float: right; -} - -.border-box-sizing { - box-sizing: border-box; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; -} - -#figure-div { - display: inline-block; - margin: 10px; - vertical-align: top; -} diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/ipython_inline_figure.html b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/ipython_inline_figure.html deleted file mode 100644 index b941d35..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/ipython_inline_figure.html +++ /dev/null @@ -1,34 +0,0 @@ - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/js/mpl.js b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/js/mpl.js deleted file mode 100644 index 140f590..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/js/mpl.js +++ /dev/null @@ -1,695 +0,0 @@ -/* Put everything inside the global mpl namespace */ -/* global mpl */ -window.mpl = {}; - -mpl.get_websocket_type = function () { - if (typeof WebSocket !== 'undefined') { - return WebSocket; - } else if (typeof MozWebSocket !== 'undefined') { - return MozWebSocket; - } else { - alert( - 'Your browser does not have WebSocket support. ' + - 'Please try Chrome, Safari or Firefox ≥ 6. ' + - 'Firefox 4 and 5 are also supported but you ' + - 'have to enable WebSockets in about:config.' - ); - } -}; - -mpl.figure = function (figure_id, websocket, ondownload, parent_element) { - this.id = figure_id; - - this.ws = websocket; - - this.supports_binary = this.ws.binaryType !== undefined; - - if (!this.supports_binary) { - var warnings = document.getElementById('mpl-warnings'); - if (warnings) { - warnings.style.display = 'block'; - warnings.textContent = - 'This browser does not support binary websocket messages. ' + - 'Performance may be slow.'; - } - } - - this.imageObj = new Image(); - - this.context = undefined; - this.message = undefined; - this.canvas = undefined; - this.rubberband_canvas = undefined; - this.rubberband_context = undefined; - this.format_dropdown = undefined; - - this.image_mode = 'full'; - - this.root = document.createElement('div'); - this.root.setAttribute('style', 'display: inline-block'); - this._root_extra_style(this.root); - - parent_element.appendChild(this.root); - - this._init_header(this); - this._init_canvas(this); - this._init_toolbar(this); - - var fig = this; - - this.waiting = false; - - this.ws.onopen = function () { - fig.send_message('supports_binary', { value: fig.supports_binary }); - fig.send_message('send_image_mode', {}); - if (fig.ratio !== 1) { - fig.send_message('set_device_pixel_ratio', { - device_pixel_ratio: fig.ratio, - }); - } - fig.send_message('refresh', {}); - }; - - this.imageObj.onload = function () { - if (fig.image_mode === 'full') { - // Full images could contain transparency (where diff images - // almost always do), so we need to clear the canvas so that - // there is no ghosting. - fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height); - } - fig.context.drawImage(fig.imageObj, 0, 0); - }; - - this.imageObj.onunload = function () { - fig.ws.close(); - }; - - this.ws.onmessage = this._make_on_message_function(this); - - this.ondownload = ondownload; -}; - -mpl.figure.prototype._init_header = function () { - var titlebar = document.createElement('div'); - titlebar.classList = - 'ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix'; - var titletext = document.createElement('div'); - titletext.classList = 'ui-dialog-title'; - titletext.setAttribute( - 'style', - 'width: 100%; text-align: center; padding: 3px;' - ); - titlebar.appendChild(titletext); - this.root.appendChild(titlebar); - this.header = titletext; -}; - -mpl.figure.prototype._canvas_extra_style = function (_canvas_div) {}; - -mpl.figure.prototype._root_extra_style = function (_canvas_div) {}; - -mpl.figure.prototype._init_canvas = function () { - var fig = this; - - var canvas_div = (this.canvas_div = document.createElement('div')); - canvas_div.setAttribute('tabindex', '0'); - canvas_div.setAttribute( - 'style', - 'border: 1px solid #ddd;' + - 'box-sizing: content-box;' + - 'clear: both;' + - 'min-height: 1px;' + - 'min-width: 1px;' + - 'outline: 0;' + - 'overflow: hidden;' + - 'position: relative;' + - 'resize: both;' + - 'z-index: 2;' - ); - - function on_keyboard_event_closure(name) { - return function (event) { - return fig.key_event(event, name); - }; - } - - canvas_div.addEventListener( - 'keydown', - on_keyboard_event_closure('key_press') - ); - canvas_div.addEventListener( - 'keyup', - on_keyboard_event_closure('key_release') - ); - - this._canvas_extra_style(canvas_div); - this.root.appendChild(canvas_div); - - var canvas = (this.canvas = document.createElement('canvas')); - canvas.classList.add('mpl-canvas'); - canvas.setAttribute( - 'style', - 'box-sizing: content-box;' + - 'pointer-events: none;' + - 'position: relative;' + - 'z-index: 0;' - ); - - this.context = canvas.getContext('2d'); - - var backingStore = - this.context.backingStorePixelRatio || - this.context.webkitBackingStorePixelRatio || - this.context.mozBackingStorePixelRatio || - this.context.msBackingStorePixelRatio || - this.context.oBackingStorePixelRatio || - this.context.backingStorePixelRatio || - 1; - - this.ratio = (window.devicePixelRatio || 1) / backingStore; - - var rubberband_canvas = (this.rubberband_canvas = document.createElement( - 'canvas' - )); - rubberband_canvas.setAttribute( - 'style', - 'box-sizing: content-box;' + - 'left: 0;' + - 'pointer-events: none;' + - 'position: absolute;' + - 'top: 0;' + - 'z-index: 1;' - ); - - // Apply a ponyfill if ResizeObserver is not implemented by browser. - if (this.ResizeObserver === undefined) { - if (window.ResizeObserver !== undefined) { - this.ResizeObserver = window.ResizeObserver; - } else { - var obs = _JSXTOOLS_RESIZE_OBSERVER({}); - this.ResizeObserver = obs.ResizeObserver; - } - } - - this.resizeObserverInstance = new this.ResizeObserver(function (entries) { - var nentries = entries.length; - for (var i = 0; i < nentries; i++) { - var entry = entries[i]; - var width, height; - if (entry.contentBoxSize) { - if (entry.contentBoxSize instanceof Array) { - // Chrome 84 implements new version of spec. - width = entry.contentBoxSize[0].inlineSize; - height = entry.contentBoxSize[0].blockSize; - } else { - // Firefox implements old version of spec. - width = entry.contentBoxSize.inlineSize; - height = entry.contentBoxSize.blockSize; - } - } else { - // Chrome <84 implements even older version of spec. - width = entry.contentRect.width; - height = entry.contentRect.height; - } - - // Keep the size of the canvas and rubber band canvas in sync with - // the canvas container. - if (entry.devicePixelContentBoxSize) { - // Chrome 84 implements new version of spec. - canvas.setAttribute( - 'width', - entry.devicePixelContentBoxSize[0].inlineSize - ); - canvas.setAttribute( - 'height', - entry.devicePixelContentBoxSize[0].blockSize - ); - } else { - canvas.setAttribute('width', width * fig.ratio); - canvas.setAttribute('height', height * fig.ratio); - } - /* This rescales the canvas back to display pixels, so that it - * appears correct on HiDPI screens. */ - canvas.style.width = width + 'px'; - canvas.style.height = height + 'px'; - - rubberband_canvas.setAttribute('width', width); - rubberband_canvas.setAttribute('height', height); - - // And update the size in Python. We ignore the initial 0/0 size - // that occurs as the element is placed into the DOM, which should - // otherwise not happen due to the minimum size styling. - if (fig.ws.readyState == 1 && width != 0 && height != 0) { - fig.request_resize(width, height); - } - } - }); - this.resizeObserverInstance.observe(canvas_div); - - function on_mouse_event_closure(name) { - /* User Agent sniffing is bad, but WebKit is busted: - * https://bugs.webkit.org/show_bug.cgi?id=144526 - * https://bugs.webkit.org/show_bug.cgi?id=181818 - * The worst that happens here is that they get an extra browser - * selection when dragging, if this check fails to catch them. - */ - var UA = navigator.userAgent; - var isWebKit = /AppleWebKit/.test(UA) && !/Chrome/.test(UA); - if(isWebKit) { - return function (event) { - /* This prevents the web browser from automatically changing to - * the text insertion cursor when the button is pressed. We - * want to control all of the cursor setting manually through - * the 'cursor' event from matplotlib */ - event.preventDefault() - return fig.mouse_event(event, name); - }; - } else { - return function (event) { - return fig.mouse_event(event, name); - }; - } - } - - canvas_div.addEventListener( - 'mousedown', - on_mouse_event_closure('button_press') - ); - canvas_div.addEventListener( - 'mouseup', - on_mouse_event_closure('button_release') - ); - canvas_div.addEventListener( - 'dblclick', - on_mouse_event_closure('dblclick') - ); - // Throttle sequential mouse events to 1 every 20ms. - canvas_div.addEventListener( - 'mousemove', - on_mouse_event_closure('motion_notify') - ); - - canvas_div.addEventListener( - 'mouseenter', - on_mouse_event_closure('figure_enter') - ); - canvas_div.addEventListener( - 'mouseleave', - on_mouse_event_closure('figure_leave') - ); - - canvas_div.addEventListener('wheel', function (event) { - if (event.deltaY < 0) { - event.step = 1; - } else { - event.step = -1; - } - on_mouse_event_closure('scroll')(event); - }); - - canvas_div.appendChild(canvas); - canvas_div.appendChild(rubberband_canvas); - - this.rubberband_context = rubberband_canvas.getContext('2d'); - this.rubberband_context.strokeStyle = '#000000'; - - this._resize_canvas = function (width, height, forward) { - if (forward) { - canvas_div.style.width = width + 'px'; - canvas_div.style.height = height + 'px'; - } - }; - - // Disable right mouse context menu. - canvas_div.addEventListener('contextmenu', function (_e) { - event.preventDefault(); - return false; - }); - - function set_focus() { - canvas.focus(); - canvas_div.focus(); - } - - window.setTimeout(set_focus, 100); -}; - -mpl.figure.prototype._init_toolbar = function () { - var fig = this; - - var toolbar = document.createElement('div'); - toolbar.classList = 'mpl-toolbar'; - this.root.appendChild(toolbar); - - function on_click_closure(name) { - return function (_event) { - return fig.toolbar_button_onclick(name); - }; - } - - function on_mouseover_closure(tooltip) { - return function (event) { - if (!event.currentTarget.disabled) { - return fig.toolbar_button_onmouseover(tooltip); - } - }; - } - - fig.buttons = {}; - var buttonGroup = document.createElement('div'); - buttonGroup.classList = 'mpl-button-group'; - for (var toolbar_ind in mpl.toolbar_items) { - var name = mpl.toolbar_items[toolbar_ind][0]; - var tooltip = mpl.toolbar_items[toolbar_ind][1]; - var image = mpl.toolbar_items[toolbar_ind][2]; - var method_name = mpl.toolbar_items[toolbar_ind][3]; - - if (!name) { - /* Instead of a spacer, we start a new button group. */ - if (buttonGroup.hasChildNodes()) { - toolbar.appendChild(buttonGroup); - } - buttonGroup = document.createElement('div'); - buttonGroup.classList = 'mpl-button-group'; - continue; - } - - var button = (fig.buttons[name] = document.createElement('button')); - button.classList = 'mpl-widget'; - button.setAttribute('role', 'button'); - button.setAttribute('aria-disabled', 'false'); - button.addEventListener('click', on_click_closure(method_name)); - button.addEventListener('mouseover', on_mouseover_closure(tooltip)); - - var icon_img = document.createElement('img'); - icon_img.src = '_images/' + image + '.png'; - icon_img.srcset = '_images/' + image + '_large.png 2x'; - icon_img.alt = tooltip; - button.appendChild(icon_img); - - buttonGroup.appendChild(button); - } - - if (buttonGroup.hasChildNodes()) { - toolbar.appendChild(buttonGroup); - } - - var fmt_picker = document.createElement('select'); - fmt_picker.classList = 'mpl-widget'; - toolbar.appendChild(fmt_picker); - this.format_dropdown = fmt_picker; - - for (var ind in mpl.extensions) { - var fmt = mpl.extensions[ind]; - var option = document.createElement('option'); - option.selected = fmt === mpl.default_extension; - option.innerHTML = fmt; - fmt_picker.appendChild(option); - } - - var status_bar = document.createElement('span'); - status_bar.classList = 'mpl-message'; - toolbar.appendChild(status_bar); - this.message = status_bar; -}; - -mpl.figure.prototype.request_resize = function (x_pixels, y_pixels) { - // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client, - // which will in turn request a refresh of the image. - this.send_message('resize', { width: x_pixels, height: y_pixels }); -}; - -mpl.figure.prototype.send_message = function (type, properties) { - properties['type'] = type; - properties['figure_id'] = this.id; - this.ws.send(JSON.stringify(properties)); -}; - -mpl.figure.prototype.send_draw_message = function () { - if (!this.waiting) { - this.waiting = true; - this.ws.send(JSON.stringify({ type: 'draw', figure_id: this.id })); - } -}; - -mpl.figure.prototype.handle_save = function (fig, _msg) { - var format_dropdown = fig.format_dropdown; - var format = format_dropdown.options[format_dropdown.selectedIndex].value; - fig.ondownload(fig, format); -}; - -mpl.figure.prototype.handle_resize = function (fig, msg) { - var size = msg['size']; - if (size[0] !== fig.canvas.width || size[1] !== fig.canvas.height) { - fig._resize_canvas(size[0], size[1], msg['forward']); - fig.send_message('refresh', {}); - } -}; - -mpl.figure.prototype.handle_rubberband = function (fig, msg) { - var x0 = msg['x0'] / fig.ratio; - var y0 = (fig.canvas.height - msg['y0']) / fig.ratio; - var x1 = msg['x1'] / fig.ratio; - var y1 = (fig.canvas.height - msg['y1']) / fig.ratio; - x0 = Math.floor(x0) + 0.5; - y0 = Math.floor(y0) + 0.5; - x1 = Math.floor(x1) + 0.5; - y1 = Math.floor(y1) + 0.5; - var min_x = Math.min(x0, x1); - var min_y = Math.min(y0, y1); - var width = Math.abs(x1 - x0); - var height = Math.abs(y1 - y0); - - fig.rubberband_context.clearRect( - 0, - 0, - fig.canvas.width / fig.ratio, - fig.canvas.height / fig.ratio - ); - - fig.rubberband_context.strokeRect(min_x, min_y, width, height); -}; - -mpl.figure.prototype.handle_figure_label = function (fig, msg) { - // Updates the figure title. - fig.header.textContent = msg['label']; -}; - -mpl.figure.prototype.handle_cursor = function (fig, msg) { - fig.canvas_div.style.cursor = msg['cursor']; -}; - -mpl.figure.prototype.handle_message = function (fig, msg) { - fig.message.textContent = msg['message']; -}; - -mpl.figure.prototype.handle_draw = function (fig, _msg) { - // Request the server to send over a new figure. - fig.send_draw_message(); -}; - -mpl.figure.prototype.handle_image_mode = function (fig, msg) { - fig.image_mode = msg['mode']; -}; - -mpl.figure.prototype.handle_history_buttons = function (fig, msg) { - for (var key in msg) { - if (!(key in fig.buttons)) { - continue; - } - fig.buttons[key].disabled = !msg[key]; - fig.buttons[key].setAttribute('aria-disabled', !msg[key]); - } -}; - -mpl.figure.prototype.handle_navigate_mode = function (fig, msg) { - if (msg['mode'] === 'PAN') { - fig.buttons['Pan'].classList.add('active'); - fig.buttons['Zoom'].classList.remove('active'); - } else if (msg['mode'] === 'ZOOM') { - fig.buttons['Pan'].classList.remove('active'); - fig.buttons['Zoom'].classList.add('active'); - } else { - fig.buttons['Pan'].classList.remove('active'); - fig.buttons['Zoom'].classList.remove('active'); - } -}; - -mpl.figure.prototype.updated_canvas_event = function () { - // Called whenever the canvas gets updated. - this.send_message('ack', {}); -}; - -// A function to construct a web socket function for onmessage handling. -// Called in the figure constructor. -mpl.figure.prototype._make_on_message_function = function (fig) { - return function socket_on_message(evt) { - if (evt.data instanceof Blob) { - var img = evt.data; - if (img.type !== 'image/png') { - /* FIXME: We get "Resource interpreted as Image but - * transferred with MIME type text/plain:" errors on - * Chrome. But how to set the MIME type? It doesn't seem - * to be part of the websocket stream */ - img.type = 'image/png'; - } - - /* Free the memory for the previous frames */ - if (fig.imageObj.src) { - (window.URL || window.webkitURL).revokeObjectURL( - fig.imageObj.src - ); - } - - fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL( - img - ); - fig.updated_canvas_event(); - fig.waiting = false; - return; - } else if ( - typeof evt.data === 'string' && - evt.data.slice(0, 21) === 'data:image/png;base64' - ) { - fig.imageObj.src = evt.data; - fig.updated_canvas_event(); - fig.waiting = false; - return; - } - - var msg = JSON.parse(evt.data); - var msg_type = msg['type']; - - // Call the "handle_{type}" callback, which takes - // the figure and JSON message as its only arguments. - try { - var callback = fig['handle_' + msg_type]; - } catch (e) { - console.log( - "No handler for the '" + msg_type + "' message type: ", - msg - ); - return; - } - - if (callback) { - try { - // console.log("Handling '" + msg_type + "' message: ", msg); - callback(fig, msg); - } catch (e) { - console.log( - "Exception inside the 'handler_" + msg_type + "' callback:", - e, - e.stack, - msg - ); - } - } - }; -}; - -function getModifiers(event) { - var mods = []; - if (event.ctrlKey) { - mods.push('ctrl'); - } - if (event.altKey) { - mods.push('alt'); - } - if (event.shiftKey) { - mods.push('shift'); - } - if (event.metaKey) { - mods.push('meta'); - } - return mods; -} - -/* - * return a copy of an object with only non-object keys - * we need this to avoid circular references - * https://stackoverflow.com/a/24161582/3208463 - */ -function simpleKeys(original) { - return Object.keys(original).reduce(function (obj, key) { - if (typeof original[key] !== 'object') { - obj[key] = original[key]; - } - return obj; - }, {}); -} - -mpl.figure.prototype.mouse_event = function (event, name) { - if (name === 'button_press') { - this.canvas.focus(); - this.canvas_div.focus(); - } - - // from https://stackoverflow.com/q/1114465 - var boundingRect = this.canvas.getBoundingClientRect(); - var x = (event.clientX - boundingRect.left) * this.ratio; - var y = (event.clientY - boundingRect.top) * this.ratio; - - this.send_message(name, { - x: x, - y: y, - button: event.button, - step: event.step, - modifiers: getModifiers(event), - guiEvent: simpleKeys(event), - }); - - return false; -}; - -mpl.figure.prototype._key_event_extra = function (_event, _name) { - // Handle any extra behaviour associated with a key event -}; - -mpl.figure.prototype.key_event = function (event, name) { - // Prevent repeat events - if (name === 'key_press') { - if (event.key === this._key) { - return; - } else { - this._key = event.key; - } - } - if (name === 'key_release') { - this._key = null; - } - - var value = ''; - if (event.ctrlKey && event.key !== 'Control') { - value += 'ctrl+'; - } - else if (event.altKey && event.key !== 'Alt') { - value += 'alt+'; - } - else if (event.shiftKey && event.key !== 'Shift') { - value += 'shift+'; - } - - value += 'k' + event.key; - - this._key_event_extra(event, name); - - this.send_message(name, { key: value, guiEvent: simpleKeys(event) }); - return false; -}; - -mpl.figure.prototype.toolbar_button_onclick = function (name) { - if (name === 'download') { - this.handle_save(this, null); - } else { - this.send_message('toolbar_button', { name: name }); - } -}; - -mpl.figure.prototype.toolbar_button_onmouseover = function (tooltip) { - this.message.textContent = tooltip; -}; - -///////////////// REMAINING CONTENT GENERATED BY embed_js.py ///////////////// -// prettier-ignore -var _JSXTOOLS_RESIZE_OBSERVER=function(A){var t,i=new WeakMap,n=new WeakMap,a=new WeakMap,r=new WeakMap,o=new Set;function s(e){if(!(this instanceof s))throw new TypeError("Constructor requires 'new' operator");i.set(this,e)}function h(){throw new TypeError("Function is not a constructor")}function c(e,t,i,n){e=0 in arguments?Number(arguments[0]):0,t=1 in arguments?Number(arguments[1]):0,i=2 in arguments?Number(arguments[2]):0,n=3 in arguments?Number(arguments[3]):0,this.right=(this.x=this.left=e)+(this.width=i),this.bottom=(this.y=this.top=t)+(this.height=n),Object.freeze(this)}function d(){t=requestAnimationFrame(d);var s=new WeakMap,p=new Set;o.forEach((function(t){r.get(t).forEach((function(i){var r=t instanceof window.SVGElement,o=a.get(t),d=r?0:parseFloat(o.paddingTop),f=r?0:parseFloat(o.paddingRight),l=r?0:parseFloat(o.paddingBottom),u=r?0:parseFloat(o.paddingLeft),g=r?0:parseFloat(o.borderTopWidth),m=r?0:parseFloat(o.borderRightWidth),w=r?0:parseFloat(o.borderBottomWidth),b=u+f,F=d+l,v=(r?0:parseFloat(o.borderLeftWidth))+m,W=g+w,y=r?0:t.offsetHeight-W-t.clientHeight,E=r?0:t.offsetWidth-v-t.clientWidth,R=b+v,z=F+W,M=r?t.width:parseFloat(o.width)-R-E,O=r?t.height:parseFloat(o.height)-z-y;if(n.has(t)){var k=n.get(t);if(k[0]===M&&k[1]===O)return}n.set(t,[M,O]);var S=Object.create(h.prototype);S.target=t,S.contentRect=new c(u,d,M,O),s.has(i)||(s.set(i,[]),p.add(i)),s.get(i).push(S)}))})),p.forEach((function(e){i.get(e).call(e,s.get(e),e)}))}return s.prototype.observe=function(i){if(i instanceof window.Element){r.has(i)||(r.set(i,new Set),o.add(i),a.set(i,window.getComputedStyle(i)));var n=r.get(i);n.has(this)||n.add(this),cancelAnimationFrame(t),t=requestAnimationFrame(d)}},s.prototype.unobserve=function(i){if(i instanceof window.Element&&r.has(i)){var n=r.get(i);n.has(this)&&(n.delete(this),n.size||(r.delete(i),o.delete(i))),n.size||r.delete(i),o.size||cancelAnimationFrame(t)}},A.DOMRectReadOnly=c,A.ResizeObserver=s,A.ResizeObserverEntry=h,A}; // eslint-disable-line diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/js/mpl_tornado.js b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/js/mpl_tornado.js deleted file mode 100644 index b3cab8b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/js/mpl_tornado.js +++ /dev/null @@ -1,8 +0,0 @@ -/* This .js file contains functions for matplotlib's built-in - tornado-based server, that are not relevant when embedding WebAgg - in another web application. */ - -/* exported mpl_ondownload */ -function mpl_ondownload(figure, format) { - window.open(figure.id + '/download.' + format, '_blank'); -} diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/js/nbagg_mpl.js b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/js/nbagg_mpl.js deleted file mode 100644 index 26d79ff..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/js/nbagg_mpl.js +++ /dev/null @@ -1,275 +0,0 @@ -/* global mpl */ - -var comm_websocket_adapter = function (comm) { - // Create a "websocket"-like object which calls the given IPython comm - // object with the appropriate methods. Currently this is a non binary - // socket, so there is still some room for performance tuning. - var ws = {}; - - ws.binaryType = comm.kernel.ws.binaryType; - ws.readyState = comm.kernel.ws.readyState; - function updateReadyState(_event) { - if (comm.kernel.ws) { - ws.readyState = comm.kernel.ws.readyState; - } else { - ws.readyState = 3; // Closed state. - } - } - comm.kernel.ws.addEventListener('open', updateReadyState); - comm.kernel.ws.addEventListener('close', updateReadyState); - comm.kernel.ws.addEventListener('error', updateReadyState); - - ws.close = function () { - comm.close(); - }; - ws.send = function (m) { - //console.log('sending', m); - comm.send(m); - }; - // Register the callback with on_msg. - comm.on_msg(function (msg) { - //console.log('receiving', msg['content']['data'], msg); - var data = msg['content']['data']; - if (data['blob'] !== undefined) { - data = { - data: new Blob(msg['buffers'], { type: data['blob'] }), - }; - } - // Pass the mpl event to the overridden (by mpl) onmessage function. - ws.onmessage(data); - }); - return ws; -}; - -mpl.mpl_figure_comm = function (comm, msg) { - // This is the function which gets called when the mpl process - // starts-up an IPython Comm through the "matplotlib" channel. - - var id = msg.content.data.id; - // Get hold of the div created by the display call when the Comm - // socket was opened in Python. - var element = document.getElementById(id); - var ws_proxy = comm_websocket_adapter(comm); - - function ondownload(figure, _format) { - window.open(figure.canvas.toDataURL()); - } - - var fig = new mpl.figure(id, ws_proxy, ondownload, element); - - // Call onopen now - mpl needs it, as it is assuming we've passed it a real - // web socket which is closed, not our websocket->open comm proxy. - ws_proxy.onopen(); - - fig.parent_element = element; - fig.cell_info = mpl.find_output_cell("
"); - if (!fig.cell_info) { - console.error('Failed to find cell for figure', id, fig); - return; - } - fig.cell_info[0].output_area.element.on( - 'cleared', - { fig: fig }, - fig._remove_fig_handler - ); -}; - -mpl.figure.prototype.handle_close = function (fig, msg) { - var width = fig.canvas.width / fig.ratio; - fig.cell_info[0].output_area.element.off( - 'cleared', - fig._remove_fig_handler - ); - fig.resizeObserverInstance.unobserve(fig.canvas_div); - - // Update the output cell to use the data from the current canvas. - fig.push_to_output(); - var dataURL = fig.canvas.toDataURL(); - // Re-enable the keyboard manager in IPython - without this line, in FF, - // the notebook keyboard shortcuts fail. - IPython.keyboard_manager.enable(); - fig.parent_element.innerHTML = - ''; - fig.close_ws(fig, msg); -}; - -mpl.figure.prototype.close_ws = function (fig, msg) { - fig.send_message('closing', msg); - // fig.ws.close() -}; - -mpl.figure.prototype.push_to_output = function (_remove_interactive) { - // Turn the data on the canvas into data in the output cell. - var width = this.canvas.width / this.ratio; - var dataURL = this.canvas.toDataURL(); - this.cell_info[1]['text/html'] = - ''; -}; - -mpl.figure.prototype.updated_canvas_event = function () { - // Tell IPython that the notebook contents must change. - IPython.notebook.set_dirty(true); - this.send_message('ack', {}); - var fig = this; - // Wait a second, then push the new image to the DOM so - // that it is saved nicely (might be nice to debounce this). - setTimeout(function () { - fig.push_to_output(); - }, 1000); -}; - -mpl.figure.prototype._init_toolbar = function () { - var fig = this; - - var toolbar = document.createElement('div'); - toolbar.classList = 'btn-toolbar'; - this.root.appendChild(toolbar); - - function on_click_closure(name) { - return function (_event) { - return fig.toolbar_button_onclick(name); - }; - } - - function on_mouseover_closure(tooltip) { - return function (event) { - if (!event.currentTarget.disabled) { - return fig.toolbar_button_onmouseover(tooltip); - } - }; - } - - fig.buttons = {}; - var buttonGroup = document.createElement('div'); - buttonGroup.classList = 'btn-group'; - var button; - for (var toolbar_ind in mpl.toolbar_items) { - var name = mpl.toolbar_items[toolbar_ind][0]; - var tooltip = mpl.toolbar_items[toolbar_ind][1]; - var image = mpl.toolbar_items[toolbar_ind][2]; - var method_name = mpl.toolbar_items[toolbar_ind][3]; - - if (!name) { - /* Instead of a spacer, we start a new button group. */ - if (buttonGroup.hasChildNodes()) { - toolbar.appendChild(buttonGroup); - } - buttonGroup = document.createElement('div'); - buttonGroup.classList = 'btn-group'; - continue; - } - - button = fig.buttons[name] = document.createElement('button'); - button.classList = 'btn btn-default'; - button.href = '#'; - button.title = name; - button.innerHTML = ''; - button.addEventListener('click', on_click_closure(method_name)); - button.addEventListener('mouseover', on_mouseover_closure(tooltip)); - buttonGroup.appendChild(button); - } - - if (buttonGroup.hasChildNodes()) { - toolbar.appendChild(buttonGroup); - } - - // Add the status bar. - var status_bar = document.createElement('span'); - status_bar.classList = 'mpl-message pull-right'; - toolbar.appendChild(status_bar); - this.message = status_bar; - - // Add the close button to the window. - var buttongrp = document.createElement('div'); - buttongrp.classList = 'btn-group inline pull-right'; - button = document.createElement('button'); - button.classList = 'btn btn-mini btn-primary'; - button.href = '#'; - button.title = 'Stop Interaction'; - button.innerHTML = ''; - button.addEventListener('click', function (_evt) { - fig.handle_close(fig, {}); - }); - button.addEventListener( - 'mouseover', - on_mouseover_closure('Stop Interaction') - ); - buttongrp.appendChild(button); - var titlebar = this.root.querySelector('.ui-dialog-titlebar'); - titlebar.insertBefore(buttongrp, titlebar.firstChild); -}; - -mpl.figure.prototype._remove_fig_handler = function (event) { - var fig = event.data.fig; - if (event.target !== this) { - // Ignore bubbled events from children. - return; - } - fig.close_ws(fig, {}); -}; - -mpl.figure.prototype._root_extra_style = function (el) { - el.style.boxSizing = 'content-box'; // override notebook setting of border-box. -}; - -mpl.figure.prototype._canvas_extra_style = function (el) { - // this is important to make the div 'focusable - el.setAttribute('tabindex', 0); - // reach out to IPython and tell the keyboard manager to turn it's self - // off when our div gets focus - - // location in version 3 - if (IPython.notebook.keyboard_manager) { - IPython.notebook.keyboard_manager.register_events(el); - } else { - // location in version 2 - IPython.keyboard_manager.register_events(el); - } -}; - -mpl.figure.prototype._key_event_extra = function (event, _name) { - // Check for shift+enter - if (event.shiftKey && event.which === 13) { - this.canvas_div.blur(); - // select the cell after this one - var index = IPython.notebook.find_cell_index(this.cell_info[0]); - IPython.notebook.select(index + 1); - } -}; - -mpl.figure.prototype.handle_save = function (fig, _msg) { - fig.ondownload(fig, null); -}; - -mpl.find_output_cell = function (html_output) { - // Return the cell and output element which can be found *uniquely* in the notebook. - // Note - this is a bit hacky, but it is done because the "notebook_saving.Notebook" - // IPython event is triggered only after the cells have been serialised, which for - // our purposes (turning an active figure into a static one), is too late. - var cells = IPython.notebook.get_cells(); - var ncells = cells.length; - for (var i = 0; i < ncells; i++) { - var cell = cells[i]; - if (cell.cell_type === 'code') { - for (var j = 0; j < cell.output_area.outputs.length; j++) { - var data = cell.output_area.outputs[j]; - if (data.data) { - // IPython >= 3 moved mimebundle to data attribute of output - data = data.data; - } - if (data['text/html'] === html_output) { - return [cell, data, j]; - } - } - } - } -}; - -// Register the function which deals with the matplotlib target/channel. -// The kernel may be null if the page has been refreshed. -if (IPython.notebook.kernel !== null) { - IPython.notebook.kernel.comm_manager.register_target( - 'matplotlib', - mpl.mpl_figure_comm - ); -} diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/nbagg_uat.ipynb b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/nbagg_uat.ipynb deleted file mode 100644 index e9fc62b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/nbagg_uat.ipynb +++ /dev/null @@ -1,631 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from imp import reload" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## UAT for NbAgg backend.\n", - "\n", - "The first line simply reloads matplotlib, uses the nbagg backend and then reloads the backend, just to ensure we have the latest modification to the backend code. Note: The underlying JavaScript will not be updated by this process, so a refresh of the browser after clearing the output and saving is necessary to clear everything fully." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib\n", - "reload(matplotlib)\n", - "\n", - "matplotlib.use('nbagg')\n", - "\n", - "import matplotlib.backends.backend_nbagg\n", - "reload(matplotlib.backends.backend_nbagg)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 1 - Simple figure creation using pyplot\n", - "\n", - "Should produce a figure window which is interactive with the pan and zoom buttons. (Do not press the close button, but any others may be used)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.backends.backend_webagg_core\n", - "reload(matplotlib.backends.backend_webagg_core)\n", - "\n", - "import matplotlib.pyplot as plt\n", - "plt.interactive(False)\n", - "\n", - "fig1 = plt.figure()\n", - "plt.plot(range(10))\n", - "\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 2 - Creation of another figure, without the need to do plt.figure.\n", - "\n", - "As above, a new figure should be created." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.plot([3, 2, 1])\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 3 - Connection info\n", - "\n", - "The printout should show that there are two figures which have active CommSockets, and no figures pending show." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(matplotlib.backends.backend_nbagg.connection_info())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 4 - Closing figures\n", - "\n", - "Closing a specific figure instance should turn the figure into a plain image - the UI should have been removed. In this case, scroll back to the first figure and assert this is the case." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.close(fig1)\n", - "plt.close('all')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 5 - No show without plt.show in non-interactive mode\n", - "\n", - "Simply doing a plt.plot should not show a new figure, nor indeed update an existing one (easily verified in UAT 6).\n", - "The output should simply be a list of Line2D instances." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.plot(range(10))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 6 - Connection information\n", - "\n", - "We just created a new figure, but didn't show it. Connection info should no longer have \"Figure 1\" (as we closed it in UAT 4) and should have figure 2 and 3, with Figure 3 without any connections. There should be 1 figure pending." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(matplotlib.backends.backend_nbagg.connection_info())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 7 - Show of previously created figure\n", - "\n", - "We should be able to show a figure we've previously created. The following should produce two figure windows." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.show()\n", - "plt.figure()\n", - "plt.plot(range(5))\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 8 - Interactive mode\n", - "\n", - "In interactive mode, creating a line should result in a figure being shown." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.interactive(True)\n", - "plt.figure()\n", - "plt.plot([3, 2, 1])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Subsequent lines should be added to the existing figure, rather than creating a new one." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.plot(range(3))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Calling connection_info in interactive mode should not show any pending figures." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(matplotlib.backends.backend_nbagg.connection_info())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Disable interactive mode again." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.interactive(False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 9 - Multiple shows\n", - "\n", - "Unlike most of the other matplotlib backends, we may want to see a figure multiple times (with or without synchronisation between the views, though the former is not yet implemented). Assert that plt.gcf().canvas.manager.reshow() results in another figure window which is synchronised upon pan & zoom." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plt.gcf().canvas.manager.reshow()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 10 - Saving notebook\n", - "\n", - "Saving the notebook (with CTRL+S or File->Save) should result in the saved notebook having static versions of the figures embedded within. The image should be the last update from user interaction and interactive plotting. (check by converting with ``ipython nbconvert ``)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 11 - Creation of a new figure on second show\n", - "\n", - "Create a figure, show it, then create a new axes and show it. The result should be a new figure.\n", - "\n", - "**BUG: Sometimes this doesn't work - not sure why (@pelson).**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fig = plt.figure()\n", - "plt.axes()\n", - "plt.show()\n", - "\n", - "plt.plot([1, 2, 3])\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 12 - OO interface\n", - "\n", - "Should produce a new figure and plot it." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from matplotlib.backends.backend_nbagg import new_figure_manager,show\n", - "\n", - "manager = new_figure_manager(1000)\n", - "fig = manager.canvas.figure\n", - "ax = fig.add_subplot(1,1,1)\n", - "ax.plot([1,2,3])\n", - "fig.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## UAT 13 - Animation\n", - "\n", - "The following should generate an animated line:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.animation as animation\n", - "import numpy as np\n", - "\n", - "fig, ax = plt.subplots()\n", - "\n", - "x = np.arange(0, 2*np.pi, 0.01) # x-array\n", - "line, = ax.plot(x, np.sin(x))\n", - "\n", - "def animate(i):\n", - " line.set_ydata(np.sin(x+i/10.0)) # update the data\n", - " return line,\n", - "\n", - "#Init only required for blitting to give a clean slate.\n", - "def init():\n", - " line.set_ydata(np.ma.array(x, mask=True))\n", - " return line,\n", - "\n", - "ani = animation.FuncAnimation(fig, animate, np.arange(1, 200), init_func=init,\n", - " interval=100., blit=True)\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 14 - Keyboard shortcuts in IPython after close of figure\n", - "\n", - "After closing the previous figure (with the close button above the figure) the IPython keyboard shortcuts should still function.\n", - "\n", - "### UAT 15 - Figure face colours\n", - "\n", - "The nbagg honours all colours apart from that of the figure.patch. The two plots below should produce a figure with a red background. There should be no yellow figure." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib\n", - "matplotlib.rcParams.update({'figure.facecolor': 'red',\n", - " 'savefig.facecolor': 'yellow'})\n", - "plt.figure()\n", - "plt.plot([3, 2, 1])\n", - "\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 16 - Events\n", - "\n", - "Pressing any keyboard key or mouse button (or scrolling) should cycle the line while the figure has focus. The figure should have focus by default when it is created and re-gain it by clicking on the canvas. Clicking anywhere outside of the figure should release focus, but moving the mouse out of the figure should not release focus." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import itertools\n", - "fig, ax = plt.subplots()\n", - "x = np.linspace(0,10,10000)\n", - "y = np.sin(x)\n", - "ln, = ax.plot(x,y)\n", - "evt = []\n", - "colors = iter(itertools.cycle(['r', 'g', 'b', 'k', 'c']))\n", - "def on_event(event):\n", - " if event.name.startswith('key'):\n", - " fig.suptitle('%s: %s' % (event.name, event.key))\n", - " elif event.name == 'scroll_event':\n", - " fig.suptitle('%s: %s' % (event.name, event.step))\n", - " else:\n", - " fig.suptitle('%s: %s' % (event.name, event.button))\n", - " evt.append(event)\n", - " ln.set_color(next(colors))\n", - " fig.canvas.draw()\n", - " fig.canvas.draw_idle()\n", - "\n", - "fig.canvas.mpl_connect('button_press_event', on_event)\n", - "fig.canvas.mpl_connect('button_release_event', on_event)\n", - "fig.canvas.mpl_connect('scroll_event', on_event)\n", - "fig.canvas.mpl_connect('key_press_event', on_event)\n", - "fig.canvas.mpl_connect('key_release_event', on_event)\n", - "\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 17 - Timers\n", - "\n", - "Single-shot timers follow a completely different code path in the nbagg backend than regular timers (such as those used in the animation example above.) The next set of tests ensures that both \"regular\" and \"single-shot\" timers work properly.\n", - "\n", - "The following should show a simple clock that updates twice a second:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import time\n", - "\n", - "fig, ax = plt.subplots()\n", - "text = ax.text(0.5, 0.5, '', ha='center')\n", - "\n", - "def update(text):\n", - " text.set(text=time.ctime())\n", - " text.axes.figure.canvas.draw()\n", - " \n", - "timer = fig.canvas.new_timer(500, [(update, [text], {})])\n", - "timer.start()\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "However, the following should only update once and then stop:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fig, ax = plt.subplots()\n", - "text = ax.text(0.5, 0.5, '', ha='center') \n", - "timer = fig.canvas.new_timer(500, [(update, [text], {})])\n", - "\n", - "timer.single_shot = True\n", - "timer.start()\n", - "\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And the next two examples should never show any visible text at all:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fig, ax = plt.subplots()\n", - "text = ax.text(0.5, 0.5, '', ha='center')\n", - "timer = fig.canvas.new_timer(500, [(update, [text], {})])\n", - "\n", - "timer.start()\n", - "timer.stop()\n", - "\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fig, ax = plt.subplots()\n", - "text = ax.text(0.5, 0.5, '', ha='center')\n", - "timer = fig.canvas.new_timer(500, [(update, [text], {})])\n", - "\n", - "timer.single_shot = True\n", - "timer.start()\n", - "timer.stop()\n", - "\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### UAT 18 - stopping figure when removed from DOM\n", - "\n", - "When the div that contains from the figure is removed from the DOM the figure should shut down it's comm, and if the python-side figure has no more active comms, it should destroy the figure. Repeatedly running the cell below should always have the same figure number" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fig, ax = plt.subplots()\n", - "ax.plot(range(5))\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Running the cell below will re-show the figure. After this, re-running the cell above should result in a new figure number." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fig.canvas.manager.reshow()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "### UAT 19 - Blitting\n", - "\n", - "Clicking on the figure should plot a green horizontal line moving up the axes." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import itertools\n", - "\n", - "cnt = itertools.count()\n", - "bg = None\n", - "\n", - "def onclick_handle(event):\n", - " \"\"\"Should draw elevating green line on each mouse click\"\"\"\n", - " global bg\n", - " if bg is None:\n", - " bg = ax.figure.canvas.copy_from_bbox(ax.bbox) \n", - " ax.figure.canvas.restore_region(bg)\n", - "\n", - " cur_y = (next(cnt) % 10) * 0.1\n", - " ln.set_ydata([cur_y, cur_y])\n", - " ax.draw_artist(ln)\n", - " ax.figure.canvas.blit(ax.bbox)\n", - "\n", - "fig, ax = plt.subplots()\n", - "ax.plot([0, 1], [0, 1], 'r')\n", - "ln, = ax.plot([0, 1], [0, 0], 'g', animated=True)\n", - "plt.show()\n", - "ax.figure.canvas.draw()\n", - "\n", - "ax.figure.canvas.mpl_connect('button_press_event', onclick_handle)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.5" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/package.json b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/package.json deleted file mode 100644 index 95bd8fd..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "devDependencies": { - "eslint": "^6.8.0", - "eslint-config-prettier": "^6.10.1", - "prettier": "^2.0.2" - }, - "scripts": { - "eslint": "eslint . --fix", - "eslint:check": "eslint .", - "lint": "npm run prettier && npm run eslint", - "lint:check": "npm run prettier:check && npm run eslint:check", - "prettier": "prettier --write \"**/*{.ts,.tsx,.js,.jsx,.css,.json}\"", - "prettier:check": "prettier --check \"**/*{.ts,.tsx,.js,.jsx,.css,.json}\"" - }, - "dependencies": { - "@jsxtools/resize-observer": "^1.0.4" - } -} diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/single_figure.html b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/single_figure.html deleted file mode 100644 index ceaaab0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/backends/web_backend/single_figure.html +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - matplotlib - - - -
-
- - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/bezier.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/bezier.py deleted file mode 100644 index f310f28..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/bezier.py +++ /dev/null @@ -1,594 +0,0 @@ -""" -A module providing some utility functions regarding Bézier path manipulation. -""" - -from functools import lru_cache -import math -import warnings - -import numpy as np - -from matplotlib import _api - - -# same algorithm as 3.8's math.comb -@np.vectorize -@lru_cache(maxsize=128) -def _comb(n, k): - if k > n: - return 0 - k = min(k, n - k) - i = np.arange(1, k + 1) - return np.prod((n + 1 - i)/i).astype(int) - - -class NonIntersectingPathException(ValueError): - pass - - -# some functions - - -def get_intersection(cx1, cy1, cos_t1, sin_t1, - cx2, cy2, cos_t2, sin_t2): - """ - Return the intersection between the line through (*cx1*, *cy1*) at angle - *t1* and the line through (*cx2*, *cy2*) at angle *t2*. - """ - - # line1 => sin_t1 * (x - cx1) - cos_t1 * (y - cy1) = 0. - # line1 => sin_t1 * x + cos_t1 * y = sin_t1*cx1 - cos_t1*cy1 - - line1_rhs = sin_t1 * cx1 - cos_t1 * cy1 - line2_rhs = sin_t2 * cx2 - cos_t2 * cy2 - - # rhs matrix - a, b = sin_t1, -cos_t1 - c, d = sin_t2, -cos_t2 - - ad_bc = a * d - b * c - if abs(ad_bc) < 1e-12: - raise ValueError("Given lines do not intersect. Please verify that " - "the angles are not equal or differ by 180 degrees.") - - # rhs_inverse - a_, b_ = d, -b - c_, d_ = -c, a - a_, b_, c_, d_ = [k / ad_bc for k in [a_, b_, c_, d_]] - - x = a_ * line1_rhs + b_ * line2_rhs - y = c_ * line1_rhs + d_ * line2_rhs - - return x, y - - -def get_normal_points(cx, cy, cos_t, sin_t, length): - """ - For a line passing through (*cx*, *cy*) and having an angle *t*, return - locations of the two points located along its perpendicular line at the - distance of *length*. - """ - - if length == 0.: - return cx, cy, cx, cy - - cos_t1, sin_t1 = sin_t, -cos_t - cos_t2, sin_t2 = -sin_t, cos_t - - x1, y1 = length * cos_t1 + cx, length * sin_t1 + cy - x2, y2 = length * cos_t2 + cx, length * sin_t2 + cy - - return x1, y1, x2, y2 - - -# BEZIER routines - -# subdividing bezier curve -# http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/bezier-sub.html - - -def _de_casteljau1(beta, t): - next_beta = beta[:-1] * (1 - t) + beta[1:] * t - return next_beta - - -def split_de_casteljau(beta, t): - """ - Split a Bézier segment defined by its control points *beta* into two - separate segments divided at *t* and return their control points. - """ - beta = np.asarray(beta) - beta_list = [beta] - while True: - beta = _de_casteljau1(beta, t) - beta_list.append(beta) - if len(beta) == 1: - break - left_beta = [beta[0] for beta in beta_list] - right_beta = [beta[-1] for beta in reversed(beta_list)] - - return left_beta, right_beta - - -def find_bezier_t_intersecting_with_closedpath( - bezier_point_at_t, inside_closedpath, t0=0., t1=1., tolerance=0.01): - """ - Find the intersection of the Bézier curve with a closed path. - - The intersection point *t* is approximated by two parameters *t0*, *t1* - such that *t0* <= *t* <= *t1*. - - Search starts from *t0* and *t1* and uses a simple bisecting algorithm - therefore one of the end points must be inside the path while the other - doesn't. The search stops when the distance of the points parametrized by - *t0* and *t1* gets smaller than the given *tolerance*. - - Parameters - ---------- - bezier_point_at_t : callable - A function returning x, y coordinates of the Bézier at parameter *t*. - It must have the signature:: - - bezier_point_at_t(t: float) -> tuple[float, float] - - inside_closedpath : callable - A function returning True if a given point (x, y) is inside the - closed path. It must have the signature:: - - inside_closedpath(point: tuple[float, float]) -> bool - - t0, t1 : float - Start parameters for the search. - - tolerance : float - Maximal allowed distance between the final points. - - Returns - ------- - t0, t1 : float - The Bézier path parameters. - """ - start = bezier_point_at_t(t0) - end = bezier_point_at_t(t1) - - start_inside = inside_closedpath(start) - end_inside = inside_closedpath(end) - - if start_inside == end_inside and start != end: - raise NonIntersectingPathException( - "Both points are on the same side of the closed path") - - while True: - - # return if the distance is smaller than the tolerance - if np.hypot(start[0] - end[0], start[1] - end[1]) < tolerance: - return t0, t1 - - # calculate the middle point - middle_t = 0.5 * (t0 + t1) - middle = bezier_point_at_t(middle_t) - middle_inside = inside_closedpath(middle) - - if start_inside ^ middle_inside: - t1 = middle_t - end = middle - else: - t0 = middle_t - start = middle - start_inside = middle_inside - - -class BezierSegment: - """ - A d-dimensional Bézier segment. - - Parameters - ---------- - control_points : (N, d) array - Location of the *N* control points. - """ - - def __init__(self, control_points): - self._cpoints = np.asarray(control_points) - self._N, self._d = self._cpoints.shape - self._orders = np.arange(self._N) - coeff = [math.factorial(self._N - 1) - // (math.factorial(i) * math.factorial(self._N - 1 - i)) - for i in range(self._N)] - self._px = (self._cpoints.T * coeff).T - - def __call__(self, t): - """ - Evaluate the Bézier curve at point(s) *t* in [0, 1]. - - Parameters - ---------- - t : (k,) array-like - Points at which to evaluate the curve. - - Returns - ------- - (k, d) array - Value of the curve for each point in *t*. - """ - t = np.asarray(t) - return (np.power.outer(1 - t, self._orders[::-1]) - * np.power.outer(t, self._orders)) @ self._px - - def point_at_t(self, t): - """ - Evaluate the curve at a single point, returning a tuple of *d* floats. - """ - return tuple(self(t)) - - @property - def control_points(self): - """The control points of the curve.""" - return self._cpoints - - @property - def dimension(self): - """The dimension of the curve.""" - return self._d - - @property - def degree(self): - """Degree of the polynomial. One less the number of control points.""" - return self._N - 1 - - @property - def polynomial_coefficients(self): - r""" - The polynomial coefficients of the Bézier curve. - - .. warning:: Follows opposite convention from `numpy.polyval`. - - Returns - ------- - (n+1, d) array - Coefficients after expanding in polynomial basis, where :math:`n` - is the degree of the Bézier curve and :math:`d` its dimension. - These are the numbers (:math:`C_j`) such that the curve can be - written :math:`\sum_{j=0}^n C_j t^j`. - - Notes - ----- - The coefficients are calculated as - - .. math:: - - {n \choose j} \sum_{i=0}^j (-1)^{i+j} {j \choose i} P_i - - where :math:`P_i` are the control points of the curve. - """ - n = self.degree - # matplotlib uses n <= 4. overflow plausible starting around n = 15. - if n > 10: - warnings.warn("Polynomial coefficients formula unstable for high " - "order Bezier curves!", RuntimeWarning) - P = self.control_points - j = np.arange(n+1)[:, None] - i = np.arange(n+1)[None, :] # _comb is non-zero for i <= j - prefactor = (-1)**(i + j) * _comb(j, i) # j on axis 0, i on axis 1 - return _comb(n, j) * prefactor @ P # j on axis 0, self.dimension on 1 - - def axis_aligned_extrema(self): - """ - Return the dimension and location of the curve's interior extrema. - - The extrema are the points along the curve where one of its partial - derivatives is zero. - - Returns - ------- - dims : array of int - Index :math:`i` of the partial derivative which is zero at each - interior extrema. - dzeros : array of float - Of same size as dims. The :math:`t` such that :math:`d/dx_i B(t) = - 0` - """ - n = self.degree - if n <= 1: - return np.array([]), np.array([]) - Cj = self.polynomial_coefficients - dCj = np.arange(1, n+1)[:, None] * Cj[1:] - dims = [] - roots = [] - for i, pi in enumerate(dCj.T): - r = np.roots(pi[::-1]) - roots.append(r) - dims.append(np.full_like(r, i)) - roots = np.concatenate(roots) - dims = np.concatenate(dims) - in_range = np.isreal(roots) & (roots >= 0) & (roots <= 1) - return dims[in_range], np.real(roots)[in_range] - - -def split_bezier_intersecting_with_closedpath( - bezier, inside_closedpath, tolerance=0.01): - """ - Split a Bézier curve into two at the intersection with a closed path. - - Parameters - ---------- - bezier : (N, 2) array-like - Control points of the Bézier segment. See `.BezierSegment`. - inside_closedpath : callable - A function returning True if a given point (x, y) is inside the - closed path. See also `.find_bezier_t_intersecting_with_closedpath`. - tolerance : float - The tolerance for the intersection. See also - `.find_bezier_t_intersecting_with_closedpath`. - - Returns - ------- - left, right - Lists of control points for the two Bézier segments. - """ - - bz = BezierSegment(bezier) - bezier_point_at_t = bz.point_at_t - - t0, t1 = find_bezier_t_intersecting_with_closedpath( - bezier_point_at_t, inside_closedpath, tolerance=tolerance) - - _left, _right = split_de_casteljau(bezier, (t0 + t1) / 2.) - return _left, _right - - -# matplotlib specific - - -def split_path_inout(path, inside, tolerance=0.01, reorder_inout=False): - """ - Divide a path into two segments at the point where ``inside(x, y)`` becomes - False. - """ - from .path import Path - path_iter = path.iter_segments() - - ctl_points, command = next(path_iter) - begin_inside = inside(ctl_points[-2:]) # true if begin point is inside - - ctl_points_old = ctl_points - - iold = 0 - i = 1 - - for ctl_points, command in path_iter: - iold = i - i += len(ctl_points) // 2 - if inside(ctl_points[-2:]) != begin_inside: - bezier_path = np.concatenate([ctl_points_old[-2:], ctl_points]) - break - ctl_points_old = ctl_points - else: - raise ValueError("The path does not intersect with the patch") - - bp = bezier_path.reshape((-1, 2)) - left, right = split_bezier_intersecting_with_closedpath( - bp, inside, tolerance) - if len(left) == 2: - codes_left = [Path.LINETO] - codes_right = [Path.MOVETO, Path.LINETO] - elif len(left) == 3: - codes_left = [Path.CURVE3, Path.CURVE3] - codes_right = [Path.MOVETO, Path.CURVE3, Path.CURVE3] - elif len(left) == 4: - codes_left = [Path.CURVE4, Path.CURVE4, Path.CURVE4] - codes_right = [Path.MOVETO, Path.CURVE4, Path.CURVE4, Path.CURVE4] - else: - raise AssertionError("This should never be reached") - - verts_left = left[1:] - verts_right = right[:] - - if path.codes is None: - path_in = Path(np.concatenate([path.vertices[:i], verts_left])) - path_out = Path(np.concatenate([verts_right, path.vertices[i:]])) - - else: - path_in = Path(np.concatenate([path.vertices[:iold], verts_left]), - np.concatenate([path.codes[:iold], codes_left])) - - path_out = Path(np.concatenate([verts_right, path.vertices[i:]]), - np.concatenate([codes_right, path.codes[i:]])) - - if reorder_inout and not begin_inside: - path_in, path_out = path_out, path_in - - return path_in, path_out - - -def inside_circle(cx, cy, r): - """ - Return a function that checks whether a point is in a circle with center - (*cx*, *cy*) and radius *r*. - - The returned function has the signature:: - - f(xy: tuple[float, float]) -> bool - """ - r2 = r ** 2 - - def _f(xy): - x, y = xy - return (x - cx) ** 2 + (y - cy) ** 2 < r2 - return _f - - -# quadratic Bezier lines - -def get_cos_sin(x0, y0, x1, y1): - dx, dy = x1 - x0, y1 - y0 - d = (dx * dx + dy * dy) ** .5 - # Account for divide by zero - if d == 0: - return 0.0, 0.0 - return dx / d, dy / d - - -def check_if_parallel(dx1, dy1, dx2, dy2, tolerance=1.e-5): - """ - Check if two lines are parallel. - - Parameters - ---------- - dx1, dy1, dx2, dy2 : float - The gradients *dy*/*dx* of the two lines. - tolerance : float - The angular tolerance in radians up to which the lines are considered - parallel. - - Returns - ------- - is_parallel - - 1 if two lines are parallel in same direction. - - -1 if two lines are parallel in opposite direction. - - False otherwise. - """ - theta1 = np.arctan2(dx1, dy1) - theta2 = np.arctan2(dx2, dy2) - dtheta = abs(theta1 - theta2) - if dtheta < tolerance: - return 1 - elif abs(dtheta - np.pi) < tolerance: - return -1 - else: - return False - - -def get_parallels(bezier2, width): - """ - Given the quadratic Bézier control points *bezier2*, returns - control points of quadratic Bézier lines roughly parallel to given - one separated by *width*. - """ - - # The parallel Bezier lines are constructed by following ways. - # c1 and c2 are control points representing the start and end of the - # Bezier line. - # cm is the middle point - - c1x, c1y = bezier2[0] - cmx, cmy = bezier2[1] - c2x, c2y = bezier2[2] - - parallel_test = check_if_parallel(c1x - cmx, c1y - cmy, - cmx - c2x, cmy - c2y) - - if parallel_test == -1: - _api.warn_external( - "Lines do not intersect. A straight line is used instead.") - cos_t1, sin_t1 = get_cos_sin(c1x, c1y, c2x, c2y) - cos_t2, sin_t2 = cos_t1, sin_t1 - else: - # t1 and t2 is the angle between c1 and cm, cm, c2. They are - # also an angle of the tangential line of the path at c1 and c2 - cos_t1, sin_t1 = get_cos_sin(c1x, c1y, cmx, cmy) - cos_t2, sin_t2 = get_cos_sin(cmx, cmy, c2x, c2y) - - # find c1_left, c1_right which are located along the lines - # through c1 and perpendicular to the tangential lines of the - # Bezier path at a distance of width. Same thing for c2_left and - # c2_right with respect to c2. - c1x_left, c1y_left, c1x_right, c1y_right = ( - get_normal_points(c1x, c1y, cos_t1, sin_t1, width) - ) - c2x_left, c2y_left, c2x_right, c2y_right = ( - get_normal_points(c2x, c2y, cos_t2, sin_t2, width) - ) - - # find cm_left which is the intersecting point of a line through - # c1_left with angle t1 and a line through c2_left with angle - # t2. Same with cm_right. - try: - cmx_left, cmy_left = get_intersection(c1x_left, c1y_left, cos_t1, - sin_t1, c2x_left, c2y_left, - cos_t2, sin_t2) - cmx_right, cmy_right = get_intersection(c1x_right, c1y_right, cos_t1, - sin_t1, c2x_right, c2y_right, - cos_t2, sin_t2) - except ValueError: - # Special case straight lines, i.e., angle between two lines is - # less than the threshold used by get_intersection (we don't use - # check_if_parallel as the threshold is not the same). - cmx_left, cmy_left = ( - 0.5 * (c1x_left + c2x_left), 0.5 * (c1y_left + c2y_left) - ) - cmx_right, cmy_right = ( - 0.5 * (c1x_right + c2x_right), 0.5 * (c1y_right + c2y_right) - ) - - # the parallel Bezier lines are created with control points of - # [c1_left, cm_left, c2_left] and [c1_right, cm_right, c2_right] - path_left = [(c1x_left, c1y_left), - (cmx_left, cmy_left), - (c2x_left, c2y_left)] - path_right = [(c1x_right, c1y_right), - (cmx_right, cmy_right), - (c2x_right, c2y_right)] - - return path_left, path_right - - -def find_control_points(c1x, c1y, mmx, mmy, c2x, c2y): - """ - Find control points of the Bézier curve passing through (*c1x*, *c1y*), - (*mmx*, *mmy*), and (*c2x*, *c2y*), at parametric values 0, 0.5, and 1. - """ - cmx = .5 * (4 * mmx - (c1x + c2x)) - cmy = .5 * (4 * mmy - (c1y + c2y)) - return [(c1x, c1y), (cmx, cmy), (c2x, c2y)] - - -def make_wedged_bezier2(bezier2, width, w1=1., wm=0.5, w2=0.): - """ - Being similar to `get_parallels`, returns control points of two quadratic - Bézier lines having a width roughly parallel to given one separated by - *width*. - """ - - # c1, cm, c2 - c1x, c1y = bezier2[0] - cmx, cmy = bezier2[1] - c3x, c3y = bezier2[2] - - # t1 and t2 is the angle between c1 and cm, cm, c3. - # They are also an angle of the tangential line of the path at c1 and c3 - cos_t1, sin_t1 = get_cos_sin(c1x, c1y, cmx, cmy) - cos_t2, sin_t2 = get_cos_sin(cmx, cmy, c3x, c3y) - - # find c1_left, c1_right which are located along the lines - # through c1 and perpendicular to the tangential lines of the - # Bezier path at a distance of width. Same thing for c3_left and - # c3_right with respect to c3. - c1x_left, c1y_left, c1x_right, c1y_right = ( - get_normal_points(c1x, c1y, cos_t1, sin_t1, width * w1) - ) - c3x_left, c3y_left, c3x_right, c3y_right = ( - get_normal_points(c3x, c3y, cos_t2, sin_t2, width * w2) - ) - - # find c12, c23 and c123 which are middle points of c1-cm, cm-c3 and - # c12-c23 - c12x, c12y = (c1x + cmx) * .5, (c1y + cmy) * .5 - c23x, c23y = (cmx + c3x) * .5, (cmy + c3y) * .5 - c123x, c123y = (c12x + c23x) * .5, (c12y + c23y) * .5 - - # tangential angle of c123 (angle between c12 and c23) - cos_t123, sin_t123 = get_cos_sin(c12x, c12y, c23x, c23y) - - c123x_left, c123y_left, c123x_right, c123y_right = ( - get_normal_points(c123x, c123y, cos_t123, sin_t123, width * wm) - ) - - path_left = find_control_points(c1x_left, c1y_left, - c123x_left, c123y_left, - c3x_left, c3y_left) - path_right = find_control_points(c1x_right, c1y_right, - c123x_right, c123y_right, - c3x_right, c3y_right) - - return path_left, path_right diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/category.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/category.py deleted file mode 100644 index 4ac2379..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/category.py +++ /dev/null @@ -1,233 +0,0 @@ -""" -Plotting of string "category" data: ``plot(['d', 'f', 'a'], [1, 2, 3])`` will -plot three points with x-axis values of 'd', 'f', 'a'. - -See :doc:`/gallery/lines_bars_and_markers/categorical_variables` for an -example. - -The module uses Matplotlib's `matplotlib.units` mechanism to convert from -strings to integers and provides a tick locator, a tick formatter, and the -`.UnitData` class that creates and stores the string-to-integer mapping. -""" - -from collections import OrderedDict -import dateutil.parser -import itertools -import logging - -import numpy as np - -from matplotlib import _api, ticker, units - - -_log = logging.getLogger(__name__) - - -class StrCategoryConverter(units.ConversionInterface): - @staticmethod - def convert(value, unit, axis): - """ - Convert strings in *value* to floats using mapping information stored - in the *unit* object. - - Parameters - ---------- - value : str or iterable - Value or list of values to be converted. - unit : `.UnitData` - An object mapping strings to integers. - axis : `~matplotlib.axis.Axis` - The axis on which the converted value is plotted. - - .. note:: *axis* is unused. - - Returns - ------- - float or `~numpy.ndarray` of float - """ - if unit is None: - raise ValueError( - 'Missing category information for StrCategoryConverter; ' - 'this might be caused by unintendedly mixing categorical and ' - 'numeric data') - StrCategoryConverter._validate_unit(unit) - # dtype = object preserves numerical pass throughs - values = np.atleast_1d(np.array(value, dtype=object)) - # force an update so it also does type checking - unit.update(values) - return np.vectorize(unit._mapping.__getitem__, otypes=[float])(values) - - @staticmethod - def axisinfo(unit, axis): - """ - Set the default axis ticks and labels. - - Parameters - ---------- - unit : `.UnitData` - object string unit information for value - axis : `~matplotlib.axis.Axis` - axis for which information is being set - - .. note:: *axis* is not used - - Returns - ------- - `~matplotlib.units.AxisInfo` - Information to support default tick labeling - - """ - StrCategoryConverter._validate_unit(unit) - # locator and formatter take mapping dict because - # args need to be pass by reference for updates - majloc = StrCategoryLocator(unit._mapping) - majfmt = StrCategoryFormatter(unit._mapping) - return units.AxisInfo(majloc=majloc, majfmt=majfmt) - - @staticmethod - def default_units(data, axis): - """ - Set and update the `~matplotlib.axis.Axis` units. - - Parameters - ---------- - data : str or iterable of str - axis : `~matplotlib.axis.Axis` - axis on which the data is plotted - - Returns - ------- - `.UnitData` - object storing string to integer mapping - """ - # the conversion call stack is default_units -> axis_info -> convert - if axis.units is None: - axis.set_units(UnitData(data)) - else: - axis.units.update(data) - return axis.units - - @staticmethod - def _validate_unit(unit): - if not hasattr(unit, '_mapping'): - raise ValueError( - f'Provided unit "{unit}" is not valid for a categorical ' - 'converter, as it does not have a _mapping attribute.') - - -class StrCategoryLocator(ticker.Locator): - """Tick at every integer mapping of the string data.""" - def __init__(self, units_mapping): - """ - Parameters - ---------- - units_mapping : dict - Mapping of category names (str) to indices (int). - """ - self._units = units_mapping - - def __call__(self): - # docstring inherited - return list(self._units.values()) - - def tick_values(self, vmin, vmax): - # docstring inherited - return self() - - -class StrCategoryFormatter(ticker.Formatter): - """String representation of the data at every tick.""" - def __init__(self, units_mapping): - """ - Parameters - ---------- - units_mapping : dict - Mapping of category names (str) to indices (int). - """ - self._units = units_mapping - - def __call__(self, x, pos=None): - # docstring inherited - return self.format_ticks([x])[0] - - def format_ticks(self, values): - # docstring inherited - r_mapping = {v: self._text(k) for k, v in self._units.items()} - return [r_mapping.get(round(val), '') for val in values] - - @staticmethod - def _text(value): - """Convert text values into utf-8 or ascii strings.""" - if isinstance(value, bytes): - value = value.decode(encoding='utf-8') - elif not isinstance(value, str): - value = str(value) - return value - - -class UnitData: - def __init__(self, data=None): - """ - Create mapping between unique categorical values and integer ids. - - Parameters - ---------- - data : iterable - sequence of string values - """ - self._mapping = OrderedDict() - self._counter = itertools.count() - if data is not None: - self.update(data) - - @staticmethod - def _str_is_convertible(val): - """ - Helper method to check whether a string can be parsed as float or date. - """ - try: - float(val) - except ValueError: - try: - dateutil.parser.parse(val) - except (ValueError, TypeError): - # TypeError if dateutil >= 2.8.1 else ValueError - return False - return True - - def update(self, data): - """ - Map new values to integer identifiers. - - Parameters - ---------- - data : iterable of str or bytes - - Raises - ------ - TypeError - If elements in *data* are neither str nor bytes. - """ - data = np.atleast_1d(np.array(data, dtype=object)) - # check if convertible to number: - convertible = True - for val in OrderedDict.fromkeys(data): - # OrderedDict just iterates over unique values in data. - _api.check_isinstance((str, bytes), value=val) - if convertible: - # this will only be called so long as convertible is True. - convertible = self._str_is_convertible(val) - if val not in self._mapping: - self._mapping[val] = next(self._counter) - if data.size and convertible: - _log.info('Using categorical units to plot a list of strings ' - 'that are all parsable as floats or dates. If these ' - 'strings should be plotted as numbers, cast to the ' - 'appropriate data type before plotting.') - - -# Register the converter with Matplotlib's unit framework -units.registry[str] = StrCategoryConverter() -units.registry[np.str_] = StrCategoryConverter() -units.registry[bytes] = StrCategoryConverter() -units.registry[np.bytes_] = StrCategoryConverter() diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/cbook/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/cbook/__init__.py deleted file mode 100644 index 30e2e37..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/cbook/__init__.py +++ /dev/null @@ -1,2341 +0,0 @@ -""" -A collection of utility functions and classes. Originally, many -(but not all) were from the Python Cookbook -- hence the name cbook. - -This module is safe to import from anywhere within Matplotlib; -it imports Matplotlib only at runtime. -""" - -import collections -import collections.abc -import contextlib -import functools -import gzip -import itertools -import math -import operator -import os -from pathlib import Path -import shlex -import subprocess -import sys -import time -import traceback -import types -import weakref - -import numpy as np - -try: - from numpy.exceptions import VisibleDeprecationWarning # numpy >= 1.25 -except ImportError: - from numpy import VisibleDeprecationWarning - -import matplotlib -from matplotlib import _api, _c_internal_utils - - -@_api.caching_module_getattr -class __getattr__: - # module-level deprecations - MatplotlibDeprecationWarning = _api.deprecated( - "3.6", obj_type="", - alternative="matplotlib.MatplotlibDeprecationWarning")( - property(lambda self: _api.deprecation.MatplotlibDeprecationWarning)) - mplDeprecation = _api.deprecated( - "3.6", obj_type="", - alternative="matplotlib.MatplotlibDeprecationWarning")( - property(lambda self: _api.deprecation.MatplotlibDeprecationWarning)) - - -def _get_running_interactive_framework(): - """ - Return the interactive framework whose event loop is currently running, if - any, or "headless" if no event loop can be started, or None. - - Returns - ------- - Optional[str] - One of the following values: "qt", "gtk3", "gtk4", "wx", "tk", - "macosx", "headless", ``None``. - """ - # Use ``sys.modules.get(name)`` rather than ``name in sys.modules`` as - # entries can also have been explicitly set to None. - QtWidgets = ( - sys.modules.get("PyQt6.QtWidgets") - or sys.modules.get("PySide6.QtWidgets") - or sys.modules.get("PyQt5.QtWidgets") - or sys.modules.get("PySide2.QtWidgets") - ) - if QtWidgets and QtWidgets.QApplication.instance(): - return "qt" - Gtk = sys.modules.get("gi.repository.Gtk") - if Gtk: - if Gtk.MAJOR_VERSION == 4: - from gi.repository import GLib - if GLib.main_depth(): - return "gtk4" - if Gtk.MAJOR_VERSION == 3 and Gtk.main_level(): - return "gtk3" - wx = sys.modules.get("wx") - if wx and wx.GetApp(): - return "wx" - tkinter = sys.modules.get("tkinter") - if tkinter: - codes = {tkinter.mainloop.__code__, tkinter.Misc.mainloop.__code__} - for frame in sys._current_frames().values(): - while frame: - if frame.f_code in codes: - return "tk" - frame = frame.f_back - # premetively break reference cycle between locals and the frame - del frame - macosx = sys.modules.get("matplotlib.backends._macosx") - if macosx and macosx.event_loop_is_running(): - return "macosx" - if not _c_internal_utils.display_is_valid(): - return "headless" - return None - - -def _exception_printer(exc): - if _get_running_interactive_framework() in ["headless", None]: - raise exc - else: - traceback.print_exc() - - -class _StrongRef: - """ - Wrapper similar to a weakref, but keeping a strong reference to the object. - """ - - def __init__(self, obj): - self._obj = obj - - def __call__(self): - return self._obj - - def __eq__(self, other): - return isinstance(other, _StrongRef) and self._obj == other._obj - - def __hash__(self): - return hash(self._obj) - - -def _weak_or_strong_ref(func, callback): - """ - Return a `WeakMethod` wrapping *func* if possible, else a `_StrongRef`. - """ - try: - return weakref.WeakMethod(func, callback) - except TypeError: - return _StrongRef(func) - - -class CallbackRegistry: - """ - Handle registering, processing, blocking, and disconnecting - for a set of signals and callbacks: - - >>> def oneat(x): - ... print('eat', x) - >>> def ondrink(x): - ... print('drink', x) - - >>> from matplotlib.cbook import CallbackRegistry - >>> callbacks = CallbackRegistry() - - >>> id_eat = callbacks.connect('eat', oneat) - >>> id_drink = callbacks.connect('drink', ondrink) - - >>> callbacks.process('drink', 123) - drink 123 - >>> callbacks.process('eat', 456) - eat 456 - >>> callbacks.process('be merry', 456) # nothing will be called - - >>> callbacks.disconnect(id_eat) - >>> callbacks.process('eat', 456) # nothing will be called - - >>> with callbacks.blocked(signal='drink'): - ... callbacks.process('drink', 123) # nothing will be called - >>> callbacks.process('drink', 123) - drink 123 - - In practice, one should always disconnect all callbacks when they are - no longer needed to avoid dangling references (and thus memory leaks). - However, real code in Matplotlib rarely does so, and due to its design, - it is rather difficult to place this kind of code. To get around this, - and prevent this class of memory leaks, we instead store weak references - to bound methods only, so when the destination object needs to die, the - CallbackRegistry won't keep it alive. - - Parameters - ---------- - exception_handler : callable, optional - If not None, *exception_handler* must be a function that takes an - `Exception` as single parameter. It gets called with any `Exception` - raised by the callbacks during `CallbackRegistry.process`, and may - either re-raise the exception or handle it in another manner. - - The default handler prints the exception (with `traceback.print_exc`) if - an interactive event loop is running; it re-raises the exception if no - interactive event loop is running. - - signals : list, optional - If not None, *signals* is a list of signals that this registry handles: - attempting to `process` or to `connect` to a signal not in the list - throws a `ValueError`. The default, None, does not restrict the - handled signals. - """ - - # We maintain two mappings: - # callbacks: signal -> {cid -> weakref-to-callback} - # _func_cid_map: signal -> {weakref-to-callback -> cid} - - def __init__(self, exception_handler=_exception_printer, *, signals=None): - self._signals = None if signals is None else list(signals) # Copy it. - self.exception_handler = exception_handler - self.callbacks = {} - self._cid_gen = itertools.count() - self._func_cid_map = {} - # A hidden variable that marks cids that need to be pickled. - self._pickled_cids = set() - - def __getstate__(self): - return { - **vars(self), - # In general, callbacks may not be pickled, so we just drop them, - # unless directed otherwise by self._pickled_cids. - "callbacks": {s: {cid: proxy() for cid, proxy in d.items() - if cid in self._pickled_cids} - for s, d in self.callbacks.items()}, - # It is simpler to reconstruct this from callbacks in __setstate__. - "_func_cid_map": None, - "_cid_gen": next(self._cid_gen) - } - - def __setstate__(self, state): - cid_count = state.pop('_cid_gen') - vars(self).update(state) - self.callbacks = { - s: {cid: _weak_or_strong_ref(func, self._remove_proxy) - for cid, func in d.items()} - for s, d in self.callbacks.items()} - self._func_cid_map = { - s: {proxy: cid for cid, proxy in d.items()} - for s, d in self.callbacks.items()} - self._cid_gen = itertools.count(cid_count) - - def connect(self, signal, func): - """Register *func* to be called when signal *signal* is generated.""" - if self._signals is not None: - _api.check_in_list(self._signals, signal=signal) - self._func_cid_map.setdefault(signal, {}) - proxy = _weak_or_strong_ref(func, self._remove_proxy) - if proxy in self._func_cid_map[signal]: - return self._func_cid_map[signal][proxy] - cid = next(self._cid_gen) - self._func_cid_map[signal][proxy] = cid - self.callbacks.setdefault(signal, {}) - self.callbacks[signal][cid] = proxy - return cid - - def _connect_picklable(self, signal, func): - """ - Like `.connect`, but the callback is kept when pickling/unpickling. - - Currently internal-use only. - """ - cid = self.connect(signal, func) - self._pickled_cids.add(cid) - return cid - - # Keep a reference to sys.is_finalizing, as sys may have been cleared out - # at that point. - def _remove_proxy(self, proxy, *, _is_finalizing=sys.is_finalizing): - if _is_finalizing(): - # Weakrefs can't be properly torn down at that point anymore. - return - for signal, proxy_to_cid in list(self._func_cid_map.items()): - cid = proxy_to_cid.pop(proxy, None) - if cid is not None: - del self.callbacks[signal][cid] - self._pickled_cids.discard(cid) - break - else: - # Not found - return - # Clean up empty dicts - if len(self.callbacks[signal]) == 0: - del self.callbacks[signal] - del self._func_cid_map[signal] - - def disconnect(self, cid): - """ - Disconnect the callback registered with callback id *cid*. - - No error is raised if such a callback does not exist. - """ - self._pickled_cids.discard(cid) - # Clean up callbacks - for signal, cid_to_proxy in list(self.callbacks.items()): - proxy = cid_to_proxy.pop(cid, None) - if proxy is not None: - break - else: - # Not found - return - - proxy_to_cid = self._func_cid_map[signal] - for current_proxy, current_cid in list(proxy_to_cid.items()): - if current_cid == cid: - assert proxy is current_proxy - del proxy_to_cid[current_proxy] - # Clean up empty dicts - if len(self.callbacks[signal]) == 0: - del self.callbacks[signal] - del self._func_cid_map[signal] - - def process(self, s, *args, **kwargs): - """ - Process signal *s*. - - All of the functions registered to receive callbacks on *s* will be - called with ``*args`` and ``**kwargs``. - """ - if self._signals is not None: - _api.check_in_list(self._signals, signal=s) - for cid, ref in list(self.callbacks.get(s, {}).items()): - func = ref() - if func is not None: - try: - func(*args, **kwargs) - # this does not capture KeyboardInterrupt, SystemExit, - # and GeneratorExit - except Exception as exc: - if self.exception_handler is not None: - self.exception_handler(exc) - else: - raise - - @contextlib.contextmanager - def blocked(self, *, signal=None): - """ - Block callback signals from being processed. - - A context manager to temporarily block/disable callback signals - from being processed by the registered listeners. - - Parameters - ---------- - signal : str, optional - The callback signal to block. The default is to block all signals. - """ - orig = self.callbacks - try: - if signal is None: - # Empty out the callbacks - self.callbacks = {} - else: - # Only remove the specific signal - self.callbacks = {k: orig[k] for k in orig if k != signal} - yield - finally: - self.callbacks = orig - - -class silent_list(list): - """ - A list with a short ``repr()``. - - This is meant to be used for a homogeneous list of artists, so that they - don't cause long, meaningless output. - - Instead of :: - - [, - , - ] - - one will get :: - - - - If ``self.type`` is None, the type name is obtained from the first item in - the list (if any). - """ - - def __init__(self, type, seq=None): - self.type = type - if seq is not None: - self.extend(seq) - - def __repr__(self): - if self.type is not None or len(self) != 0: - tp = self.type if self.type is not None else type(self[0]).__name__ - return f"" - else: - return "" - - -def _local_over_kwdict( - local_var, kwargs, *keys, - warning_cls=_api.MatplotlibDeprecationWarning): - out = local_var - for key in keys: - kwarg_val = kwargs.pop(key, None) - if kwarg_val is not None: - if out is None: - out = kwarg_val - else: - _api.warn_external(f'"{key}" keyword argument will be ignored', - warning_cls) - return out - - -def strip_math(s): - """ - Remove latex formatting from mathtext. - - Only handles fully math and fully non-math strings. - """ - if len(s) >= 2 and s[0] == s[-1] == "$": - s = s[1:-1] - for tex, plain in [ - (r"\times", "x"), # Specifically for Formatter support. - (r"\mathdefault", ""), - (r"\rm", ""), - (r"\cal", ""), - (r"\tt", ""), - (r"\it", ""), - ("\\", ""), - ("{", ""), - ("}", ""), - ]: - s = s.replace(tex, plain) - return s - - -def _strip_comment(s): - """Strip everything from the first unquoted #.""" - pos = 0 - while True: - quote_pos = s.find('"', pos) - hash_pos = s.find('#', pos) - if quote_pos < 0: - without_comment = s if hash_pos < 0 else s[:hash_pos] - return without_comment.strip() - elif 0 <= hash_pos < quote_pos: - return s[:hash_pos].strip() - else: - closing_quote_pos = s.find('"', quote_pos + 1) - if closing_quote_pos < 0: - raise ValueError( - f"Missing closing quote in: {s!r}. If you need a double-" - 'quote inside a string, use escaping: e.g. "the \" char"') - pos = closing_quote_pos + 1 # behind closing quote - - -def is_writable_file_like(obj): - """Return whether *obj* looks like a file object with a *write* method.""" - return callable(getattr(obj, 'write', None)) - - -def file_requires_unicode(x): - """ - Return whether the given writable file-like object requires Unicode to be - written to it. - """ - try: - x.write(b'') - except TypeError: - return True - else: - return False - - -def to_filehandle(fname, flag='r', return_opened=False, encoding=None): - """ - Convert a path to an open file handle or pass-through a file-like object. - - Consider using `open_file_cm` instead, as it allows one to properly close - newly created file objects more easily. - - Parameters - ---------- - fname : str or path-like or file-like - If `str` or `os.PathLike`, the file is opened using the flags specified - by *flag* and *encoding*. If a file-like object, it is passed through. - flag : str, default: 'r' - Passed as the *mode* argument to `open` when *fname* is `str` or - `os.PathLike`; ignored if *fname* is file-like. - return_opened : bool, default: False - If True, return both the file object and a boolean indicating whether - this was a new file (that the caller needs to close). If False, return - only the new file. - encoding : str or None, default: None - Passed as the *mode* argument to `open` when *fname* is `str` or - `os.PathLike`; ignored if *fname* is file-like. - - Returns - ------- - fh : file-like - opened : bool - *opened* is only returned if *return_opened* is True. - """ - if isinstance(fname, os.PathLike): - fname = os.fspath(fname) - if isinstance(fname, str): - if fname.endswith('.gz'): - fh = gzip.open(fname, flag) - elif fname.endswith('.bz2'): - # python may not be compiled with bz2 support, - # bury import until we need it - import bz2 - fh = bz2.BZ2File(fname, flag) - else: - fh = open(fname, flag, encoding=encoding) - opened = True - elif hasattr(fname, 'seek'): - fh = fname - opened = False - else: - raise ValueError('fname must be a PathLike or file handle') - if return_opened: - return fh, opened - return fh - - -def open_file_cm(path_or_file, mode="r", encoding=None): - r"""Pass through file objects and context-manage path-likes.""" - fh, opened = to_filehandle(path_or_file, mode, True, encoding) - return fh if opened else contextlib.nullcontext(fh) - - -def is_scalar_or_string(val): - """Return whether the given object is a scalar or string like.""" - return isinstance(val, str) or not np.iterable(val) - - -def get_sample_data(fname, asfileobj=True, *, np_load=False): - """ - Return a sample data file. *fname* is a path relative to the - :file:`mpl-data/sample_data` directory. If *asfileobj* is `True` - return a file object, otherwise just a file path. - - Sample data files are stored in the 'mpl-data/sample_data' directory within - the Matplotlib package. - - If the filename ends in .gz, the file is implicitly ungzipped. If the - filename ends with .npy or .npz, *asfileobj* is True, and *np_load* is - True, the file is loaded with `numpy.load`. *np_load* currently defaults - to False but will default to True in a future release. - """ - path = _get_data_path('sample_data', fname) - if asfileobj: - suffix = path.suffix.lower() - if suffix == '.gz': - return gzip.open(path) - elif suffix in ['.npy', '.npz']: - if np_load: - return np.load(path) - else: - _api.warn_deprecated( - "3.3", message="In a future release, get_sample_data " - "will automatically load numpy arrays. Set np_load to " - "True to get the array and suppress this warning. Set " - "asfileobj to False to get the path to the data file and " - "suppress this warning.") - return path.open('rb') - elif suffix in ['.csv', '.xrc', '.txt']: - return path.open('r') - else: - return path.open('rb') - else: - return str(path) - - -def _get_data_path(*args): - """ - Return the `pathlib.Path` to a resource file provided by Matplotlib. - - ``*args`` specify a path relative to the base data path. - """ - return Path(matplotlib.get_data_path(), *args) - - -def flatten(seq, scalarp=is_scalar_or_string): - """ - Return a generator of flattened nested containers. - - For example: - - >>> from matplotlib.cbook import flatten - >>> l = (('John', ['Hunter']), (1, 23), [[([42, (5, 23)], )]]) - >>> print(list(flatten(l))) - ['John', 'Hunter', 1, 23, 42, 5, 23] - - By: Composite of Holger Krekel and Luther Blissett - From: https://code.activestate.com/recipes/121294/ - and Recipe 1.12 in cookbook - """ - for item in seq: - if scalarp(item) or item is None: - yield item - else: - yield from flatten(item, scalarp) - - -@_api.deprecated("3.6", alternative="functools.lru_cache") -class maxdict(dict): - """ - A dictionary with a maximum size. - - Notes - ----- - This doesn't override all the relevant methods to constrain the size, - just ``__setitem__``, so use with caution. - """ - - def __init__(self, maxsize): - super().__init__() - self.maxsize = maxsize - - def __setitem__(self, k, v): - super().__setitem__(k, v) - while len(self) >= self.maxsize: - del self[next(iter(self))] - - -class Stack: - """ - Stack of elements with a movable cursor. - - Mimics home/back/forward in a web browser. - """ - - def __init__(self, default=None): - self.clear() - self._default = default - - def __call__(self): - """Return the current element, or None.""" - if not self._elements: - return self._default - else: - return self._elements[self._pos] - - def __len__(self): - return len(self._elements) - - def __getitem__(self, ind): - return self._elements[ind] - - def forward(self): - """Move the position forward and return the current element.""" - self._pos = min(self._pos + 1, len(self._elements) - 1) - return self() - - def back(self): - """Move the position back and return the current element.""" - if self._pos > 0: - self._pos -= 1 - return self() - - def push(self, o): - """ - Push *o* to the stack at current position. Discard all later elements. - - *o* is returned. - """ - self._elements = self._elements[:self._pos + 1] + [o] - self._pos = len(self._elements) - 1 - return self() - - def home(self): - """ - Push the first element onto the top of the stack. - - The first element is returned. - """ - if not self._elements: - return - self.push(self._elements[0]) - return self() - - def empty(self): - """Return whether the stack is empty.""" - return len(self._elements) == 0 - - def clear(self): - """Empty the stack.""" - self._pos = -1 - self._elements = [] - - def bubble(self, o): - """ - Raise all references of *o* to the top of the stack, and return it. - - Raises - ------ - ValueError - If *o* is not in the stack. - """ - if o not in self._elements: - raise ValueError('Given element not contained in the stack') - old_elements = self._elements.copy() - self.clear() - top_elements = [] - for elem in old_elements: - if elem == o: - top_elements.append(elem) - else: - self.push(elem) - for _ in top_elements: - self.push(o) - return o - - def remove(self, o): - """ - Remove *o* from the stack. - - Raises - ------ - ValueError - If *o* is not in the stack. - """ - if o not in self._elements: - raise ValueError('Given element not contained in the stack') - old_elements = self._elements.copy() - self.clear() - for elem in old_elements: - if elem != o: - self.push(elem) - - -def safe_masked_invalid(x, copy=False): - x = np.array(x, subok=True, copy=copy) - if not x.dtype.isnative: - # If we have already made a copy, do the byteswap in place, else make a - # copy with the byte order swapped. - x = x.byteswap(inplace=copy).newbyteorder('N') # Swap to native order. - try: - xm = np.ma.masked_invalid(x, copy=False) - xm.shrink_mask() - except TypeError: - return x - return xm - - -def print_cycles(objects, outstream=sys.stdout, show_progress=False): - """ - Print loops of cyclic references in the given *objects*. - - It is often useful to pass in ``gc.garbage`` to find the cycles that are - preventing some objects from being garbage collected. - - Parameters - ---------- - objects - A list of objects to find cycles in. - outstream - The stream for output. - show_progress : bool - If True, print the number of objects reached as they are found. - """ - import gc - - def print_path(path): - for i, step in enumerate(path): - # next "wraps around" - next = path[(i + 1) % len(path)] - - outstream.write(" %s -- " % type(step)) - if isinstance(step, dict): - for key, val in step.items(): - if val is next: - outstream.write("[{!r}]".format(key)) - break - if key is next: - outstream.write("[key] = {!r}".format(val)) - break - elif isinstance(step, list): - outstream.write("[%d]" % step.index(next)) - elif isinstance(step, tuple): - outstream.write("( tuple )") - else: - outstream.write(repr(step)) - outstream.write(" ->\n") - outstream.write("\n") - - def recurse(obj, start, all, current_path): - if show_progress: - outstream.write("%d\r" % len(all)) - - all[id(obj)] = None - - referents = gc.get_referents(obj) - for referent in referents: - # If we've found our way back to the start, this is - # a cycle, so print it out - if referent is start: - print_path(current_path) - - # Don't go back through the original list of objects, or - # through temporary references to the object, since those - # are just an artifact of the cycle detector itself. - elif referent is objects or isinstance(referent, types.FrameType): - continue - - # We haven't seen this object before, so recurse - elif id(referent) not in all: - recurse(referent, start, all, current_path + [obj]) - - for obj in objects: - outstream.write(f"Examining: {obj!r}\n") - recurse(obj, obj, {}, []) - - -class Grouper: - """ - A disjoint-set data structure. - - Objects can be joined using :meth:`join`, tested for connectedness - using :meth:`joined`, and all disjoint sets can be retrieved by - using the object as an iterator. - - The objects being joined must be hashable and weak-referenceable. - - Examples - -------- - >>> from matplotlib.cbook import Grouper - >>> class Foo: - ... def __init__(self, s): - ... self.s = s - ... def __repr__(self): - ... return self.s - ... - >>> a, b, c, d, e, f = [Foo(x) for x in 'abcdef'] - >>> grp = Grouper() - >>> grp.join(a, b) - >>> grp.join(b, c) - >>> grp.join(d, e) - >>> list(grp) - [[a, b, c], [d, e]] - >>> grp.joined(a, b) - True - >>> grp.joined(a, c) - True - >>> grp.joined(a, d) - False - """ - - def __init__(self, init=()): - self._mapping = {weakref.ref(x): [weakref.ref(x)] for x in init} - - def __contains__(self, item): - return weakref.ref(item) in self._mapping - - def clean(self): - """Clean dead weak references from the dictionary.""" - mapping = self._mapping - to_drop = [key for key in mapping if key() is None] - for key in to_drop: - val = mapping.pop(key) - val.remove(key) - - def join(self, a, *args): - """ - Join given arguments into the same set. Accepts one or more arguments. - """ - mapping = self._mapping - set_a = mapping.setdefault(weakref.ref(a), [weakref.ref(a)]) - - for arg in args: - set_b = mapping.get(weakref.ref(arg), [weakref.ref(arg)]) - if set_b is not set_a: - if len(set_b) > len(set_a): - set_a, set_b = set_b, set_a - set_a.extend(set_b) - for elem in set_b: - mapping[elem] = set_a - - self.clean() - - def joined(self, a, b): - """Return whether *a* and *b* are members of the same set.""" - self.clean() - return (self._mapping.get(weakref.ref(a), object()) - is self._mapping.get(weakref.ref(b))) - - def remove(self, a): - self.clean() - set_a = self._mapping.pop(weakref.ref(a), None) - if set_a: - set_a.remove(weakref.ref(a)) - - def __iter__(self): - """ - Iterate over each of the disjoint sets as a list. - - The iterator is invalid if interleaved with calls to join(). - """ - self.clean() - unique_groups = {id(group): group for group in self._mapping.values()} - for group in unique_groups.values(): - yield [x() for x in group] - - def get_siblings(self, a): - """Return all of the items joined with *a*, including itself.""" - self.clean() - siblings = self._mapping.get(weakref.ref(a), [weakref.ref(a)]) - return [x() for x in siblings] - - -class GrouperView: - """Immutable view over a `.Grouper`.""" - - def __init__(self, grouper): - self._grouper = grouper - - class _GrouperMethodForwarder: - def __init__(self, deprecated_kw=None): - self._deprecated_kw = deprecated_kw - - def __set_name__(self, owner, name): - wrapped = getattr(Grouper, name) - forwarder = functools.wraps(wrapped)( - lambda self, *args, **kwargs: wrapped( - self._grouper, *args, **kwargs)) - if self._deprecated_kw: - forwarder = _api.deprecated(**self._deprecated_kw)(forwarder) - setattr(owner, name, forwarder) - - __contains__ = _GrouperMethodForwarder() - __iter__ = _GrouperMethodForwarder() - joined = _GrouperMethodForwarder() - get_siblings = _GrouperMethodForwarder() - clean = _GrouperMethodForwarder(deprecated_kw=dict(since="3.6")) - join = _GrouperMethodForwarder(deprecated_kw=dict(since="3.6")) - remove = _GrouperMethodForwarder(deprecated_kw=dict(since="3.6")) - - -def simple_linear_interpolation(a, steps): - """ - Resample an array with ``steps - 1`` points between original point pairs. - - Along each column of *a*, ``(steps - 1)`` points are introduced between - each original values; the values are linearly interpolated. - - Parameters - ---------- - a : array, shape (n, ...) - steps : int - - Returns - ------- - array - shape ``((n - 1) * steps + 1, ...)`` - """ - fps = a.reshape((len(a), -1)) - xp = np.arange(len(a)) * steps - x = np.arange((len(a) - 1) * steps + 1) - return (np.column_stack([np.interp(x, xp, fp) for fp in fps.T]) - .reshape((len(x),) + a.shape[1:])) - - -def delete_masked_points(*args): - """ - Find all masked and/or non-finite points in a set of arguments, - and return the arguments with only the unmasked points remaining. - - Arguments can be in any of 5 categories: - - 1) 1-D masked arrays - 2) 1-D ndarrays - 3) ndarrays with more than one dimension - 4) other non-string iterables - 5) anything else - - The first argument must be in one of the first four categories; - any argument with a length differing from that of the first - argument (and hence anything in category 5) then will be - passed through unchanged. - - Masks are obtained from all arguments of the correct length - in categories 1, 2, and 4; a point is bad if masked in a masked - array or if it is a nan or inf. No attempt is made to - extract a mask from categories 2, 3, and 4 if `numpy.isfinite` - does not yield a Boolean array. - - All input arguments that are not passed unchanged are returned - as ndarrays after removing the points or rows corresponding to - masks in any of the arguments. - - A vastly simpler version of this function was originally - written as a helper for Axes.scatter(). - - """ - if not len(args): - return () - if is_scalar_or_string(args[0]): - raise ValueError("First argument must be a sequence") - nrecs = len(args[0]) - margs = [] - seqlist = [False] * len(args) - for i, x in enumerate(args): - if not isinstance(x, str) and np.iterable(x) and len(x) == nrecs: - seqlist[i] = True - if isinstance(x, np.ma.MaskedArray): - if x.ndim > 1: - raise ValueError("Masked arrays must be 1-D") - else: - x = np.asarray(x) - margs.append(x) - masks = [] # List of masks that are True where good. - for i, x in enumerate(margs): - if seqlist[i]: - if x.ndim > 1: - continue # Don't try to get nan locations unless 1-D. - if isinstance(x, np.ma.MaskedArray): - masks.append(~np.ma.getmaskarray(x)) # invert the mask - xd = x.data - else: - xd = x - try: - mask = np.isfinite(xd) - if isinstance(mask, np.ndarray): - masks.append(mask) - except Exception: # Fixme: put in tuple of possible exceptions? - pass - if len(masks): - mask = np.logical_and.reduce(masks) - igood = mask.nonzero()[0] - if len(igood) < nrecs: - for i, x in enumerate(margs): - if seqlist[i]: - margs[i] = x[igood] - for i, x in enumerate(margs): - if seqlist[i] and isinstance(x, np.ma.MaskedArray): - margs[i] = x.filled() - return margs - - -def _combine_masks(*args): - """ - Find all masked and/or non-finite points in a set of arguments, - and return the arguments as masked arrays with a common mask. - - Arguments can be in any of 5 categories: - - 1) 1-D masked arrays - 2) 1-D ndarrays - 3) ndarrays with more than one dimension - 4) other non-string iterables - 5) anything else - - The first argument must be in one of the first four categories; - any argument with a length differing from that of the first - argument (and hence anything in category 5) then will be - passed through unchanged. - - Masks are obtained from all arguments of the correct length - in categories 1, 2, and 4; a point is bad if masked in a masked - array or if it is a nan or inf. No attempt is made to - extract a mask from categories 2 and 4 if `numpy.isfinite` - does not yield a Boolean array. Category 3 is included to - support RGB or RGBA ndarrays, which are assumed to have only - valid values and which are passed through unchanged. - - All input arguments that are not passed unchanged are returned - as masked arrays if any masked points are found, otherwise as - ndarrays. - - """ - if not len(args): - return () - if is_scalar_or_string(args[0]): - raise ValueError("First argument must be a sequence") - nrecs = len(args[0]) - margs = [] # Output args; some may be modified. - seqlist = [False] * len(args) # Flags: True if output will be masked. - masks = [] # List of masks. - for i, x in enumerate(args): - if is_scalar_or_string(x) or len(x) != nrecs: - margs.append(x) # Leave it unmodified. - else: - if isinstance(x, np.ma.MaskedArray) and x.ndim > 1: - raise ValueError("Masked arrays must be 1-D") - try: - x = np.asanyarray(x) - except (VisibleDeprecationWarning, ValueError): - # NumPy 1.19 raises a warning about ragged arrays, but we want - # to accept basically anything here. - x = np.asanyarray(x, dtype=object) - if x.ndim == 1: - x = safe_masked_invalid(x) - seqlist[i] = True - if np.ma.is_masked(x): - masks.append(np.ma.getmaskarray(x)) - margs.append(x) # Possibly modified. - if len(masks): - mask = np.logical_or.reduce(masks) - for i, x in enumerate(margs): - if seqlist[i]: - margs[i] = np.ma.array(x, mask=mask) - return margs - - -def boxplot_stats(X, whis=1.5, bootstrap=None, labels=None, - autorange=False): - r""" - Return a list of dictionaries of statistics used to draw a series of box - and whisker plots using `~.Axes.bxp`. - - Parameters - ---------- - X : array-like - Data that will be represented in the boxplots. Should have 2 or - fewer dimensions. - - whis : float or (float, float), default: 1.5 - The position of the whiskers. - - If a float, the lower whisker is at the lowest datum above - ``Q1 - whis*(Q3-Q1)``, and the upper whisker at the highest datum below - ``Q3 + whis*(Q3-Q1)``, where Q1 and Q3 are the first and third - quartiles. The default value of ``whis = 1.5`` corresponds to Tukey's - original definition of boxplots. - - If a pair of floats, they indicate the percentiles at which to draw the - whiskers (e.g., (5, 95)). In particular, setting this to (0, 100) - results in whiskers covering the whole range of the data. - - In the edge case where ``Q1 == Q3``, *whis* is automatically set to - (0, 100) (cover the whole range of the data) if *autorange* is True. - - Beyond the whiskers, data are considered outliers and are plotted as - individual points. - - bootstrap : int, optional - Number of times the confidence intervals around the median - should be bootstrapped (percentile method). - - labels : array-like, optional - Labels for each dataset. Length must be compatible with - dimensions of *X*. - - autorange : bool, optional (False) - When `True` and the data are distributed such that the 25th and 75th - percentiles are equal, ``whis`` is set to (0, 100) such that the - whisker ends are at the minimum and maximum of the data. - - Returns - ------- - list of dict - A list of dictionaries containing the results for each column - of data. Keys of each dictionary are the following: - - ======== =================================== - Key Value Description - ======== =================================== - label tick label for the boxplot - mean arithmetic mean value - med 50th percentile - q1 first quartile (25th percentile) - q3 third quartile (75th percentile) - iqr interquartile range - cilo lower notch around the median - cihi upper notch around the median - whislo end of the lower whisker - whishi end of the upper whisker - fliers outliers - ======== =================================== - - Notes - ----- - Non-bootstrapping approach to confidence interval uses Gaussian-based - asymptotic approximation: - - .. math:: - - \mathrm{med} \pm 1.57 \times \frac{\mathrm{iqr}}{\sqrt{N}} - - General approach from: - McGill, R., Tukey, J.W., and Larsen, W.A. (1978) "Variations of - Boxplots", The American Statistician, 32:12-16. - """ - - def _bootstrap_median(data, N=5000): - # determine 95% confidence intervals of the median - M = len(data) - percentiles = [2.5, 97.5] - - bs_index = np.random.randint(M, size=(N, M)) - bsData = data[bs_index] - estimate = np.median(bsData, axis=1, overwrite_input=True) - - CI = np.percentile(estimate, percentiles) - return CI - - def _compute_conf_interval(data, med, iqr, bootstrap): - if bootstrap is not None: - # Do a bootstrap estimate of notch locations. - # get conf. intervals around median - CI = _bootstrap_median(data, N=bootstrap) - notch_min = CI[0] - notch_max = CI[1] - else: - - N = len(data) - notch_min = med - 1.57 * iqr / np.sqrt(N) - notch_max = med + 1.57 * iqr / np.sqrt(N) - - return notch_min, notch_max - - # output is a list of dicts - bxpstats = [] - - # convert X to a list of lists - X = _reshape_2D(X, "X") - - ncols = len(X) - if labels is None: - labels = itertools.repeat(None) - elif len(labels) != ncols: - raise ValueError("Dimensions of labels and X must be compatible") - - input_whis = whis - for ii, (x, label) in enumerate(zip(X, labels)): - - # empty dict - stats = {} - if label is not None: - stats['label'] = label - - # restore whis to the input values in case it got changed in the loop - whis = input_whis - - # note tricksiness, append up here and then mutate below - bxpstats.append(stats) - - # if empty, bail - if len(x) == 0: - stats['fliers'] = np.array([]) - stats['mean'] = np.nan - stats['med'] = np.nan - stats['q1'] = np.nan - stats['q3'] = np.nan - stats['iqr'] = np.nan - stats['cilo'] = np.nan - stats['cihi'] = np.nan - stats['whislo'] = np.nan - stats['whishi'] = np.nan - continue - - # up-convert to an array, just to be safe - x = np.asarray(x) - - # arithmetic mean - stats['mean'] = np.mean(x) - - # medians and quartiles - q1, med, q3 = np.percentile(x, [25, 50, 75]) - - # interquartile range - stats['iqr'] = q3 - q1 - if stats['iqr'] == 0 and autorange: - whis = (0, 100) - - # conf. interval around median - stats['cilo'], stats['cihi'] = _compute_conf_interval( - x, med, stats['iqr'], bootstrap - ) - - # lowest/highest non-outliers - if np.iterable(whis) and not isinstance(whis, str): - loval, hival = np.percentile(x, whis) - elif np.isreal(whis): - loval = q1 - whis * stats['iqr'] - hival = q3 + whis * stats['iqr'] - else: - raise ValueError('whis must be a float or list of percentiles') - - # get high extreme - wiskhi = x[x <= hival] - if len(wiskhi) == 0 or np.max(wiskhi) < q3: - stats['whishi'] = q3 - else: - stats['whishi'] = np.max(wiskhi) - - # get low extreme - wisklo = x[x >= loval] - if len(wisklo) == 0 or np.min(wisklo) > q1: - stats['whislo'] = q1 - else: - stats['whislo'] = np.min(wisklo) - - # compute a single array of outliers - stats['fliers'] = np.concatenate([ - x[x < stats['whislo']], - x[x > stats['whishi']], - ]) - - # add in the remaining stats - stats['q1'], stats['med'], stats['q3'] = q1, med, q3 - - return bxpstats - - -#: Maps short codes for line style to their full name used by backends. -ls_mapper = {'-': 'solid', '--': 'dashed', '-.': 'dashdot', ':': 'dotted'} -#: Maps full names for line styles used by backends to their short codes. -ls_mapper_r = {v: k for k, v in ls_mapper.items()} - - -def contiguous_regions(mask): - """ - Return a list of (ind0, ind1) such that ``mask[ind0:ind1].all()`` is - True and we cover all such regions. - """ - mask = np.asarray(mask, dtype=bool) - - if not mask.size: - return [] - - # Find the indices of region changes, and correct offset - idx, = np.nonzero(mask[:-1] != mask[1:]) - idx += 1 - - # List operations are faster for moderately sized arrays - idx = idx.tolist() - - # Add first and/or last index if needed - if mask[0]: - idx = [0] + idx - if mask[-1]: - idx.append(len(mask)) - - return list(zip(idx[::2], idx[1::2])) - - -def is_math_text(s): - """ - Return whether the string *s* contains math expressions. - - This is done by checking whether *s* contains an even number of - non-escaped dollar signs. - """ - s = str(s) - dollar_count = s.count(r'$') - s.count(r'\$') - even_dollars = (dollar_count > 0 and dollar_count % 2 == 0) - return even_dollars - - -def _to_unmasked_float_array(x): - """ - Convert a sequence to a float array; if input was a masked array, masked - values are converted to nans. - """ - if hasattr(x, 'mask'): - return np.ma.asarray(x, float).filled(np.nan) - else: - return np.asarray(x, float) - - -def _check_1d(x): - """Convert scalars to 1D arrays; pass-through arrays as is.""" - # Unpack in case of e.g. Pandas or xarray object - x = _unpack_to_numpy(x) - # plot requires `shape` and `ndim`. If passed an - # object that doesn't provide them, then force to numpy array. - # Note this will strip unit information. - if (not hasattr(x, 'shape') or - not hasattr(x, 'ndim') or - len(x.shape) < 1): - return np.atleast_1d(x) - else: - return x - - -def _reshape_2D(X, name): - """ - Use Fortran ordering to convert ndarrays and lists of iterables to lists of - 1D arrays. - - Lists of iterables are converted by applying `numpy.asanyarray` to each of - their elements. 1D ndarrays are returned in a singleton list containing - them. 2D ndarrays are converted to the list of their *columns*. - - *name* is used to generate the error message for invalid inputs. - """ - - # Unpack in case of e.g. Pandas or xarray object - X = _unpack_to_numpy(X) - - # Iterate over columns for ndarrays. - if isinstance(X, np.ndarray): - X = X.T - - if len(X) == 0: - return [[]] - elif X.ndim == 1 and np.ndim(X[0]) == 0: - # 1D array of scalars: directly return it. - return [X] - elif X.ndim in [1, 2]: - # 2D array, or 1D array of iterables: flatten them first. - return [np.reshape(x, -1) for x in X] - else: - raise ValueError(f'{name} must have 2 or fewer dimensions') - - # Iterate over list of iterables. - if len(X) == 0: - return [[]] - - result = [] - is_1d = True - for xi in X: - # check if this is iterable, except for strings which we - # treat as singletons. - if not isinstance(xi, str): - try: - iter(xi) - except TypeError: - pass - else: - is_1d = False - xi = np.asanyarray(xi) - nd = np.ndim(xi) - if nd > 1: - raise ValueError(f'{name} must have 2 or fewer dimensions') - result.append(xi.reshape(-1)) - - if is_1d: - # 1D array of scalars: directly return it. - return [np.reshape(result, -1)] - else: - # 2D array, or 1D array of iterables: use flattened version. - return result - - -def violin_stats(X, method, points=100, quantiles=None): - """ - Return a list of dictionaries of data which can be used to draw a series - of violin plots. - - See the ``Returns`` section below to view the required keys of the - dictionary. - - Users can skip this function and pass a user-defined set of dictionaries - with the same keys to `~.axes.Axes.violinplot` instead of using Matplotlib - to do the calculations. See the *Returns* section below for the keys - that must be present in the dictionaries. - - Parameters - ---------- - X : array-like - Sample data that will be used to produce the gaussian kernel density - estimates. Must have 2 or fewer dimensions. - - method : callable - The method used to calculate the kernel density estimate for each - column of data. When called via ``method(v, coords)``, it should - return a vector of the values of the KDE evaluated at the values - specified in coords. - - points : int, default: 100 - Defines the number of points to evaluate each of the gaussian kernel - density estimates at. - - quantiles : array-like, default: None - Defines (if not None) a list of floats in interval [0, 1] for each - column of data, which represents the quantiles that will be rendered - for that column of data. Must have 2 or fewer dimensions. 1D array will - be treated as a singleton list containing them. - - Returns - ------- - list of dict - A list of dictionaries containing the results for each column of data. - The dictionaries contain at least the following: - - - coords: A list of scalars containing the coordinates this particular - kernel density estimate was evaluated at. - - vals: A list of scalars containing the values of the kernel density - estimate at each of the coordinates given in *coords*. - - mean: The mean value for this column of data. - - median: The median value for this column of data. - - min: The minimum value for this column of data. - - max: The maximum value for this column of data. - - quantiles: The quantile values for this column of data. - """ - - # List of dictionaries describing each of the violins. - vpstats = [] - - # Want X to be a list of data sequences - X = _reshape_2D(X, "X") - - # Want quantiles to be as the same shape as data sequences - if quantiles is not None and len(quantiles) != 0: - quantiles = _reshape_2D(quantiles, "quantiles") - # Else, mock quantiles if it's none or empty - else: - quantiles = [[]] * len(X) - - # quantiles should have the same size as dataset - if len(X) != len(quantiles): - raise ValueError("List of violinplot statistics and quantiles values" - " must have the same length") - - # Zip x and quantiles - for (x, q) in zip(X, quantiles): - # Dictionary of results for this distribution - stats = {} - - # Calculate basic stats for the distribution - min_val = np.min(x) - max_val = np.max(x) - quantile_val = np.percentile(x, 100 * q) - - # Evaluate the kernel density estimate - coords = np.linspace(min_val, max_val, points) - stats['vals'] = method(x, coords) - stats['coords'] = coords - - # Store additional statistics for this distribution - stats['mean'] = np.mean(x) - stats['median'] = np.median(x) - stats['min'] = min_val - stats['max'] = max_val - stats['quantiles'] = np.atleast_1d(quantile_val) - - # Append to output - vpstats.append(stats) - - return vpstats - - -def pts_to_prestep(x, *args): - """ - Convert continuous line to pre-steps. - - Given a set of ``N`` points, convert to ``2N - 1`` points, which when - connected linearly give a step function which changes values at the - beginning of the intervals. - - Parameters - ---------- - x : array - The x location of the steps. May be empty. - - y1, ..., yp : array - y arrays to be turned into steps; all must be the same length as ``x``. - - Returns - ------- - array - The x and y values converted to steps in the same order as the input; - can be unpacked as ``x_out, y1_out, ..., yp_out``. If the input is - length ``N``, each of these arrays will be length ``2N + 1``. For - ``N=0``, the length will be 0. - - Examples - -------- - >>> x_s, y1_s, y2_s = pts_to_prestep(x, y1, y2) - """ - steps = np.zeros((1 + len(args), max(2 * len(x) - 1, 0))) - # In all `pts_to_*step` functions, only assign once using *x* and *args*, - # as converting to an array may be expensive. - steps[0, 0::2] = x - steps[0, 1::2] = steps[0, 0:-2:2] - steps[1:, 0::2] = args - steps[1:, 1::2] = steps[1:, 2::2] - return steps - - -def pts_to_poststep(x, *args): - """ - Convert continuous line to post-steps. - - Given a set of ``N`` points convert to ``2N + 1`` points, which when - connected linearly give a step function which changes values at the end of - the intervals. - - Parameters - ---------- - x : array - The x location of the steps. May be empty. - - y1, ..., yp : array - y arrays to be turned into steps; all must be the same length as ``x``. - - Returns - ------- - array - The x and y values converted to steps in the same order as the input; - can be unpacked as ``x_out, y1_out, ..., yp_out``. If the input is - length ``N``, each of these arrays will be length ``2N + 1``. For - ``N=0``, the length will be 0. - - Examples - -------- - >>> x_s, y1_s, y2_s = pts_to_poststep(x, y1, y2) - """ - steps = np.zeros((1 + len(args), max(2 * len(x) - 1, 0))) - steps[0, 0::2] = x - steps[0, 1::2] = steps[0, 2::2] - steps[1:, 0::2] = args - steps[1:, 1::2] = steps[1:, 0:-2:2] - return steps - - -def pts_to_midstep(x, *args): - """ - Convert continuous line to mid-steps. - - Given a set of ``N`` points convert to ``2N`` points which when connected - linearly give a step function which changes values at the middle of the - intervals. - - Parameters - ---------- - x : array - The x location of the steps. May be empty. - - y1, ..., yp : array - y arrays to be turned into steps; all must be the same length as - ``x``. - - Returns - ------- - array - The x and y values converted to steps in the same order as the input; - can be unpacked as ``x_out, y1_out, ..., yp_out``. If the input is - length ``N``, each of these arrays will be length ``2N``. - - Examples - -------- - >>> x_s, y1_s, y2_s = pts_to_midstep(x, y1, y2) - """ - steps = np.zeros((1 + len(args), 2 * len(x))) - x = np.asanyarray(x) - steps[0, 1:-1:2] = steps[0, 2::2] = (x[:-1] + x[1:]) / 2 - steps[0, :1] = x[:1] # Also works for zero-sized input. - steps[0, -1:] = x[-1:] - steps[1:, 0::2] = args - steps[1:, 1::2] = steps[1:, 0::2] - return steps - - -STEP_LOOKUP_MAP = {'default': lambda x, y: (x, y), - 'steps': pts_to_prestep, - 'steps-pre': pts_to_prestep, - 'steps-post': pts_to_poststep, - 'steps-mid': pts_to_midstep} - - -def index_of(y): - """ - A helper function to create reasonable x values for the given *y*. - - This is used for plotting (x, y) if x values are not explicitly given. - - First try ``y.index`` (assuming *y* is a `pandas.Series`), if that - fails, use ``range(len(y))``. - - This will be extended in the future to deal with more types of - labeled data. - - Parameters - ---------- - y : float or array-like - - Returns - ------- - x, y : ndarray - The x and y values to plot. - """ - try: - return y.index.to_numpy(), y.to_numpy() - except AttributeError: - pass - try: - y = _check_1d(y) - except (VisibleDeprecationWarning, ValueError): - # NumPy 1.19 will warn on ragged input, and we can't actually use it. - pass - else: - return np.arange(y.shape[0], dtype=float), y - raise ValueError('Input could not be cast to an at-least-1D NumPy array') - - -def safe_first_element(obj): - """ - Return the first element in *obj*. - - This is a type-independent way of obtaining the first element, - supporting both index access and the iterator protocol. - """ - return _safe_first_finite(obj, skip_nonfinite=False) - - -def _safe_first_finite(obj, *, skip_nonfinite=True): - """ - Return the first finite element in *obj* if one is available and skip_nonfinite is - True. Otherwise return the first element. - - This is a method for internal use. - - This is a type-independent way of obtaining the first finite element, supporting - both index access and the iterator protocol. - """ - def safe_isfinite(val): - if val is None: - return False - try: - return np.isfinite(val) if np.isscalar(val) else True - except TypeError: - # This is something that numpy can not make heads or tails - # of, assume "finite" - return True - if skip_nonfinite is False: - if isinstance(obj, collections.abc.Iterator): - # needed to accept `array.flat` as input. - # np.flatiter reports as an instance of collections.Iterator - # but can still be indexed via []. - # This has the side effect of re-setting the iterator, but - # that is acceptable. - try: - return obj[0] - except TypeError: - pass - raise RuntimeError("matplotlib does not support generators " - "as input") - return next(iter(obj)) - elif isinstance(obj, np.flatiter): - # TODO do the finite filtering on this - return obj[0] - elif isinstance(obj, collections.abc.Iterator): - raise RuntimeError("matplotlib does not " - "support generators as input") - else: - return next((val for val in obj if safe_isfinite(val)), safe_first_element(obj)) - - -def sanitize_sequence(data): - """ - Convert dictview objects to list. Other inputs are returned unchanged. - """ - return (list(data) if isinstance(data, collections.abc.MappingView) - else data) - - -def normalize_kwargs(kw, alias_mapping=None): - """ - Helper function to normalize kwarg inputs. - - Parameters - ---------- - kw : dict or None - A dict of keyword arguments. None is explicitly supported and treated - as an empty dict, to support functions with an optional parameter of - the form ``props=None``. - - alias_mapping : dict or Artist subclass or Artist instance, optional - A mapping between a canonical name to a list of aliases, in order of - precedence from lowest to highest. - - If the canonical value is not in the list it is assumed to have the - highest priority. - - If an Artist subclass or instance is passed, use its properties alias - mapping. - - Raises - ------ - TypeError - To match what Python raises if invalid arguments/keyword arguments are - passed to a callable. - """ - from matplotlib.artist import Artist - - if kw is None: - return {} - - # deal with default value of alias_mapping - if alias_mapping is None: - alias_mapping = dict() - elif (isinstance(alias_mapping, type) and issubclass(alias_mapping, Artist) - or isinstance(alias_mapping, Artist)): - alias_mapping = getattr(alias_mapping, "_alias_map", {}) - - to_canonical = {alias: canonical - for canonical, alias_list in alias_mapping.items() - for alias in alias_list} - canonical_to_seen = {} - ret = {} # output dictionary - - for k, v in kw.items(): - canonical = to_canonical.get(k, k) - if canonical in canonical_to_seen: - raise TypeError(f"Got both {canonical_to_seen[canonical]!r} and " - f"{k!r}, which are aliases of one another") - canonical_to_seen[canonical] = k - ret[canonical] = v - - return ret - - -@contextlib.contextmanager -def _lock_path(path): - """ - Context manager for locking a path. - - Usage:: - - with _lock_path(path): - ... - - Another thread or process that attempts to lock the same path will wait - until this context manager is exited. - - The lock is implemented by creating a temporary file in the parent - directory, so that directory must exist and be writable. - """ - path = Path(path) - lock_path = path.with_name(path.name + ".matplotlib-lock") - retries = 50 - sleeptime = 0.1 - for _ in range(retries): - try: - with lock_path.open("xb"): - break - except FileExistsError: - time.sleep(sleeptime) - else: - raise TimeoutError("""\ -Lock error: Matplotlib failed to acquire the following lock file: - {} -This maybe due to another process holding this lock file. If you are sure no -other Matplotlib process is running, remove this file and try again.""".format( - lock_path)) - try: - yield - finally: - lock_path.unlink() - - -def _topmost_artist( - artists, - _cached_max=functools.partial(max, key=operator.attrgetter("zorder"))): - """ - Get the topmost artist of a list. - - In case of a tie, return the *last* of the tied artists, as it will be - drawn on top of the others. `max` returns the first maximum in case of - ties, so we need to iterate over the list in reverse order. - """ - return _cached_max(reversed(artists)) - - -def _str_equal(obj, s): - """ - Return whether *obj* is a string equal to string *s*. - - This helper solely exists to handle the case where *obj* is a numpy array, - because in such cases, a naive ``obj == s`` would yield an array, which - cannot be used in a boolean context. - """ - return isinstance(obj, str) and obj == s - - -def _str_lower_equal(obj, s): - """ - Return whether *obj* is a string equal, when lowercased, to string *s*. - - This helper solely exists to handle the case where *obj* is a numpy array, - because in such cases, a naive ``obj == s`` would yield an array, which - cannot be used in a boolean context. - """ - return isinstance(obj, str) and obj.lower() == s - - -def _array_perimeter(arr): - """ - Get the elements on the perimeter of *arr*. - - Parameters - ---------- - arr : ndarray, shape (M, N) - The input array. - - Returns - ------- - ndarray, shape (2*(M - 1) + 2*(N - 1),) - The elements on the perimeter of the array:: - - [arr[0, 0], ..., arr[0, -1], ..., arr[-1, -1], ..., arr[-1, 0], ...] - - Examples - -------- - >>> i, j = np.ogrid[:3, :4] - >>> a = i*10 + j - >>> a - array([[ 0, 1, 2, 3], - [10, 11, 12, 13], - [20, 21, 22, 23]]) - >>> _array_perimeter(a) - array([ 0, 1, 2, 3, 13, 23, 22, 21, 20, 10]) - """ - # note we use Python's half-open ranges to avoid repeating - # the corners - forward = np.s_[0:-1] # [0 ... -1) - backward = np.s_[-1:0:-1] # [-1 ... 0) - return np.concatenate(( - arr[0, forward], - arr[forward, -1], - arr[-1, backward], - arr[backward, 0], - )) - - -def _unfold(arr, axis, size, step): - """ - Append an extra dimension containing sliding windows along *axis*. - - All windows are of size *size* and begin with every *step* elements. - - Parameters - ---------- - arr : ndarray, shape (N_1, ..., N_k) - The input array - axis : int - Axis along which the windows are extracted - size : int - Size of the windows - step : int - Stride between first elements of subsequent windows. - - Returns - ------- - ndarray, shape (N_1, ..., 1 + (N_axis-size)/step, ..., N_k, size) - - Examples - -------- - >>> i, j = np.ogrid[:3, :7] - >>> a = i*10 + j - >>> a - array([[ 0, 1, 2, 3, 4, 5, 6], - [10, 11, 12, 13, 14, 15, 16], - [20, 21, 22, 23, 24, 25, 26]]) - >>> _unfold(a, axis=1, size=3, step=2) - array([[[ 0, 1, 2], - [ 2, 3, 4], - [ 4, 5, 6]], - [[10, 11, 12], - [12, 13, 14], - [14, 15, 16]], - [[20, 21, 22], - [22, 23, 24], - [24, 25, 26]]]) - """ - new_shape = [*arr.shape, size] - new_strides = [*arr.strides, arr.strides[axis]] - new_shape[axis] = (new_shape[axis] - size) // step + 1 - new_strides[axis] = new_strides[axis] * step - return np.lib.stride_tricks.as_strided(arr, - shape=new_shape, - strides=new_strides, - writeable=False) - - -def _array_patch_perimeters(x, rstride, cstride): - """ - Extract perimeters of patches from *arr*. - - Extracted patches are of size (*rstride* + 1) x (*cstride* + 1) and - share perimeters with their neighbors. The ordering of the vertices matches - that returned by ``_array_perimeter``. - - Parameters - ---------- - x : ndarray, shape (N, M) - Input array - rstride : int - Vertical (row) stride between corresponding elements of each patch - cstride : int - Horizontal (column) stride between corresponding elements of each patch - - Returns - ------- - ndarray, shape (N/rstride * M/cstride, 2 * (rstride + cstride)) - """ - assert rstride > 0 and cstride > 0 - assert (x.shape[0] - 1) % rstride == 0 - assert (x.shape[1] - 1) % cstride == 0 - # We build up each perimeter from four half-open intervals. Here is an - # illustrated explanation for rstride == cstride == 3 - # - # T T T R - # L R - # L R - # L B B B - # - # where T means that this element will be in the top array, R for right, - # B for bottom and L for left. Each of the arrays below has a shape of: - # - # (number of perimeters that can be extracted vertically, - # number of perimeters that can be extracted horizontally, - # cstride for top and bottom and rstride for left and right) - # - # Note that _unfold doesn't incur any memory copies, so the only costly - # operation here is the np.concatenate. - top = _unfold(x[:-1:rstride, :-1], 1, cstride, cstride) - bottom = _unfold(x[rstride::rstride, 1:], 1, cstride, cstride)[..., ::-1] - right = _unfold(x[:-1, cstride::cstride], 0, rstride, rstride) - left = _unfold(x[1:, :-1:cstride], 0, rstride, rstride)[..., ::-1] - return (np.concatenate((top, right, bottom, left), axis=2) - .reshape(-1, 2 * (rstride + cstride))) - - -@contextlib.contextmanager -def _setattr_cm(obj, **kwargs): - """ - Temporarily set some attributes; restore original state at context exit. - """ - sentinel = object() - origs = {} - for attr in kwargs: - orig = getattr(obj, attr, sentinel) - if attr in obj.__dict__ or orig is sentinel: - # if we are pulling from the instance dict or the object - # does not have this attribute we can trust the above - origs[attr] = orig - else: - # if the attribute is not in the instance dict it must be - # from the class level - cls_orig = getattr(type(obj), attr) - # if we are dealing with a property (but not a general descriptor) - # we want to set the original value back. - if isinstance(cls_orig, property): - origs[attr] = orig - # otherwise this is _something_ we are going to shadow at - # the instance dict level from higher up in the MRO. We - # are going to assume we can delattr(obj, attr) to clean - # up after ourselves. It is possible that this code will - # fail if used with a non-property custom descriptor which - # implements __set__ (and __delete__ does not act like a - # stack). However, this is an internal tool and we do not - # currently have any custom descriptors. - else: - origs[attr] = sentinel - - try: - for attr, val in kwargs.items(): - setattr(obj, attr, val) - yield - finally: - for attr, orig in origs.items(): - if orig is sentinel: - delattr(obj, attr) - else: - setattr(obj, attr, orig) - - -class _OrderedSet(collections.abc.MutableSet): - def __init__(self): - self._od = collections.OrderedDict() - - def __contains__(self, key): - return key in self._od - - def __iter__(self): - return iter(self._od) - - def __len__(self): - return len(self._od) - - def add(self, key): - self._od.pop(key, None) - self._od[key] = None - - def discard(self, key): - self._od.pop(key, None) - - -# Agg's buffers are unmultiplied RGBA8888, which neither PyQt<=5.1 nor cairo -# support; however, both do support premultiplied ARGB32. - - -def _premultiplied_argb32_to_unmultiplied_rgba8888(buf): - """ - Convert a premultiplied ARGB32 buffer to an unmultiplied RGBA8888 buffer. - """ - rgba = np.take( # .take() ensures C-contiguity of the result. - buf, - [2, 1, 0, 3] if sys.byteorder == "little" else [1, 2, 3, 0], axis=2) - rgb = rgba[..., :-1] - alpha = rgba[..., -1] - # Un-premultiply alpha. The formula is the same as in cairo-png.c. - mask = alpha != 0 - for channel in np.rollaxis(rgb, -1): - channel[mask] = ( - (channel[mask].astype(int) * 255 + alpha[mask] // 2) - // alpha[mask]) - return rgba - - -def _unmultiplied_rgba8888_to_premultiplied_argb32(rgba8888): - """ - Convert an unmultiplied RGBA8888 buffer to a premultiplied ARGB32 buffer. - """ - if sys.byteorder == "little": - argb32 = np.take(rgba8888, [2, 1, 0, 3], axis=2) - rgb24 = argb32[..., :-1] - alpha8 = argb32[..., -1:] - else: - argb32 = np.take(rgba8888, [3, 0, 1, 2], axis=2) - alpha8 = argb32[..., :1] - rgb24 = argb32[..., 1:] - # Only bother premultiplying when the alpha channel is not fully opaque, - # as the cost is not negligible. The unsafe cast is needed to do the - # multiplication in-place in an integer buffer. - if alpha8.min() != 0xff: - np.multiply(rgb24, alpha8 / 0xff, out=rgb24, casting="unsafe") - return argb32 - - -def _get_nonzero_slices(buf): - """ - Return the bounds of the nonzero region of a 2D array as a pair of slices. - - ``buf[_get_nonzero_slices(buf)]`` is the smallest sub-rectangle in *buf* - that encloses all non-zero entries in *buf*. If *buf* is fully zero, then - ``(slice(0, 0), slice(0, 0))`` is returned. - """ - x_nz, = buf.any(axis=0).nonzero() - y_nz, = buf.any(axis=1).nonzero() - if len(x_nz) and len(y_nz): - l, r = x_nz[[0, -1]] - b, t = y_nz[[0, -1]] - return slice(b, t + 1), slice(l, r + 1) - else: - return slice(0, 0), slice(0, 0) - - -def _pformat_subprocess(command): - """Pretty-format a subprocess command for printing/logging purposes.""" - return (command if isinstance(command, str) - else " ".join(shlex.quote(os.fspath(arg)) for arg in command)) - - -def _check_and_log_subprocess(command, logger, **kwargs): - """ - Run *command*, returning its stdout output if it succeeds. - - If it fails (exits with nonzero return code), raise an exception whose text - includes the failed command and captured stdout and stderr output. - - Regardless of the return code, the command is logged at DEBUG level on - *logger*. In case of success, the output is likewise logged. - """ - logger.debug('%s', _pformat_subprocess(command)) - proc = subprocess.run( - command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs) - if proc.returncode: - stdout = proc.stdout - if isinstance(stdout, bytes): - stdout = stdout.decode() - stderr = proc.stderr - if isinstance(stderr, bytes): - stderr = stderr.decode() - raise RuntimeError( - f"The command\n" - f" {_pformat_subprocess(command)}\n" - f"failed and generated the following output:\n" - f"{stdout}\n" - f"and the following error:\n" - f"{stderr}") - if proc.stdout: - logger.debug("stdout:\n%s", proc.stdout) - if proc.stderr: - logger.debug("stderr:\n%s", proc.stderr) - return proc.stdout - - -def _backend_module_name(name): - """ - Convert a backend name (either a standard backend -- "Agg", "TkAgg", ... -- - or a custom backend -- "module://...") to the corresponding module name). - """ - return (name[9:] if name.startswith("module://") - else "matplotlib.backends.backend_{}".format(name.lower())) - - -def _setup_new_guiapp(): - """ - Perform OS-dependent setup when Matplotlib creates a new GUI application. - """ - # Windows: If not explicit app user model id has been set yet (so we're not - # already embedded), then set it to "matplotlib", so that taskbar icons are - # correct. - try: - _c_internal_utils.Win32_GetCurrentProcessExplicitAppUserModelID() - except OSError: - _c_internal_utils.Win32_SetCurrentProcessExplicitAppUserModelID( - "matplotlib") - - -def _format_approx(number, precision): - """ - Format the number with at most the number of decimals given as precision. - Remove trailing zeros and possibly the decimal point. - """ - return f'{number:.{precision}f}'.rstrip('0').rstrip('.') or '0' - - -def _g_sig_digits(value, delta): - """ - Return the number of significant digits to %g-format *value*, assuming that - it is known with an error of *delta*. - """ - if delta == 0: - # delta = 0 may occur when trying to format values over a tiny range; - # in that case, replace it by the distance to the closest float. - delta = abs(np.spacing(value)) - # If e.g. value = 45.67 and delta = 0.02, then we want to round to 2 digits - # after the decimal point (floor(log10(0.02)) = -2); 45.67 contributes 2 - # digits before the decimal point (floor(log10(45.67)) + 1 = 2): the total - # is 4 significant digits. A value of 0 contributes 1 "digit" before the - # decimal point. - # For inf or nan, the precision doesn't matter. - return max( - 0, - (math.floor(math.log10(abs(value))) + 1 if value else 1) - - math.floor(math.log10(delta))) if math.isfinite(value) else 0 - - -def _unikey_or_keysym_to_mplkey(unikey, keysym): - """ - Convert a Unicode key or X keysym to a Matplotlib key name. - - The Unicode key is checked first; this avoids having to list most printable - keysyms such as ``EuroSign``. - """ - # For non-printable characters, gtk3 passes "\0" whereas tk passes an "". - if unikey and unikey.isprintable(): - return unikey - key = keysym.lower() - if key.startswith("kp_"): # keypad_x (including kp_enter). - key = key[3:] - if key.startswith("page_"): # page_{up,down} - key = key.replace("page_", "page") - if key.endswith(("_l", "_r")): # alt_l, ctrl_l, shift_l. - key = key[:-2] - key = { - "return": "enter", - "prior": "pageup", # Used by tk. - "next": "pagedown", # Used by tk. - }.get(key, key) - return key - - -@functools.lru_cache(None) -def _make_class_factory(mixin_class, fmt, attr_name=None): - """ - Return a function that creates picklable classes inheriting from a mixin. - - After :: - - factory = _make_class_factory(FooMixin, fmt, attr_name) - FooAxes = factory(Axes) - - ``Foo`` is a class that inherits from ``FooMixin`` and ``Axes`` and **is - picklable** (picklability is what differentiates this from a plain call to - `type`). Its ``__name__`` is set to ``fmt.format(Axes.__name__)`` and the - base class is stored in the ``attr_name`` attribute, if not None. - - Moreover, the return value of ``factory`` is memoized: calls with the same - ``Axes`` class always return the same subclass. - """ - - @functools.lru_cache(None) - def class_factory(axes_class): - # if we have already wrapped this class, declare victory! - if issubclass(axes_class, mixin_class): - return axes_class - - # The parameter is named "axes_class" for backcompat but is really just - # a base class; no axes semantics are used. - base_class = axes_class - - class subcls(mixin_class, base_class): - # Better approximation than __module__ = "matplotlib.cbook". - __module__ = mixin_class.__module__ - - def __reduce__(self): - return (_picklable_class_constructor, - (mixin_class, fmt, attr_name, base_class), - self.__getstate__()) - - subcls.__name__ = subcls.__qualname__ = fmt.format(base_class.__name__) - if attr_name is not None: - setattr(subcls, attr_name, base_class) - return subcls - - class_factory.__module__ = mixin_class.__module__ - return class_factory - - -def _picklable_class_constructor(mixin_class, fmt, attr_name, base_class): - """Internal helper for _make_class_factory.""" - factory = _make_class_factory(mixin_class, fmt, attr_name) - cls = factory(base_class) - return cls.__new__(cls) - - -def _unpack_to_numpy(x): - """Internal helper to extract data from e.g. pandas and xarray objects.""" - if isinstance(x, np.ndarray): - # If numpy, return directly - return x - if hasattr(x, 'to_numpy'): - # Assume that any function to_numpy() do actually return a numpy array - return x.to_numpy() - if hasattr(x, 'values'): - xtmp = x.values - # For example a dict has a 'values' attribute, but it is not a property - # so in this case we do not want to return a function - if isinstance(xtmp, np.ndarray): - return xtmp - return x - - -def _auto_format_str(fmt, value): - """ - Apply *value* to the format string *fmt*. - - This works both with unnamed %-style formatting and - unnamed {}-style formatting. %-style formatting has priority. - If *fmt* is %-style formattable that will be used. Otherwise, - {}-formatting is applied. Strings without formatting placeholders - are passed through as is. - - Examples - -------- - >>> _auto_format_str('%.2f m', 0.2) - '0.20 m' - >>> _auto_format_str('{} m', 0.2) - '0.2 m' - >>> _auto_format_str('const', 0.2) - 'const' - >>> _auto_format_str('%d or {}', 0.2) - '0 or {}' - """ - try: - return fmt % (value,) - except (TypeError, ValueError): - return fmt.format(value) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/cbook/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/cbook/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index f15cd75..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/cbook/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/cm.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/cm.py deleted file mode 100644 index 0ae895a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/cm.py +++ /dev/null @@ -1,724 +0,0 @@ -""" -Builtin colormaps, colormap handling utilities, and the `ScalarMappable` mixin. - -.. seealso:: - - :doc:`/gallery/color/colormap_reference` for a list of builtin colormaps. - - :doc:`/tutorials/colors/colormap-manipulation` for examples of how to - make colormaps. - - :doc:`/tutorials/colors/colormaps` an in-depth discussion of - choosing colormaps. - - :doc:`/tutorials/colors/colormapnorms` for more details about data - normalization. -""" - -from collections.abc import Mapping -import functools - -import numpy as np -from numpy import ma - -import matplotlib as mpl -from matplotlib import _api, colors, cbook, scale -from matplotlib._cm import datad -from matplotlib._cm_listed import cmaps as cmaps_listed - - -_LUTSIZE = mpl.rcParams['image.lut'] - - -def _gen_cmap_registry(): - """ - Generate a dict mapping standard colormap names to standard colormaps, as - well as the reversed colormaps. - """ - cmap_d = {**cmaps_listed} - for name, spec in datad.items(): - cmap_d[name] = ( # Precache the cmaps at a fixed lutsize.. - colors.LinearSegmentedColormap(name, spec, _LUTSIZE) - if 'red' in spec else - colors.ListedColormap(spec['listed'], name) - if 'listed' in spec else - colors.LinearSegmentedColormap.from_list(name, spec, _LUTSIZE)) - # Generate reversed cmaps. - for cmap in list(cmap_d.values()): - rmap = cmap.reversed() - cmap_d[rmap.name] = rmap - return cmap_d - - -class ColormapRegistry(Mapping): - r""" - Container for colormaps that are known to Matplotlib by name. - - The universal registry instance is `matplotlib.colormaps`. There should be - no need for users to instantiate `.ColormapRegistry` themselves. - - Read access uses a dict-like interface mapping names to `.Colormap`\s:: - - import matplotlib as mpl - cmap = mpl.colormaps['viridis'] - - Returned `.Colormap`\s are copies, so that their modification does not - change the global definition of the colormap. - - Additional colormaps can be added via `.ColormapRegistry.register`:: - - mpl.colormaps.register(my_colormap) - """ - def __init__(self, cmaps): - self._cmaps = cmaps - self._builtin_cmaps = tuple(cmaps) - # A shim to allow register_cmap() to force an override - self._allow_override_builtin = False - - def __getitem__(self, item): - try: - return self._cmaps[item].copy() - except KeyError: - raise KeyError(f"{item!r} is not a known colormap name") from None - - def __iter__(self): - return iter(self._cmaps) - - def __len__(self): - return len(self._cmaps) - - def __str__(self): - return ('ColormapRegistry; available colormaps:\n' + - ', '.join(f"'{name}'" for name in self)) - - def __call__(self): - """ - Return a list of the registered colormap names. - - This exists only for backward-compatibility in `.pyplot` which had a - ``plt.colormaps()`` method. The recommended way to get this list is - now ``list(colormaps)``. - """ - return list(self) - - def register(self, cmap, *, name=None, force=False): - """ - Register a new colormap. - - The colormap name can then be used as a string argument to any ``cmap`` - parameter in Matplotlib. It is also available in ``pyplot.get_cmap``. - - The colormap registry stores a copy of the given colormap, so that - future changes to the original colormap instance do not affect the - registered colormap. Think of this as the registry taking a snapshot - of the colormap at registration. - - Parameters - ---------- - cmap : matplotlib.colors.Colormap - The colormap to register. - - name : str, optional - The name for the colormap. If not given, ``cmap.name`` is used. - - force : bool, default: False - If False, a ValueError is raised if trying to overwrite an already - registered name. True supports overwriting registered colormaps - other than the builtin colormaps. - """ - _api.check_isinstance(colors.Colormap, cmap=cmap) - - name = name or cmap.name - if name in self: - if not force: - # don't allow registering an already existing cmap - # unless explicitly asked to - raise ValueError( - f'A colormap named "{name}" is already registered.') - elif (name in self._builtin_cmaps - and not self._allow_override_builtin): - # We don't allow overriding a builtin unless privately - # coming from register_cmap() - raise ValueError("Re-registering the builtin cmap " - f"{name!r} is not allowed.") - - # Warn that we are updating an already existing colormap - _api.warn_external(f"Overwriting the cmap {name!r} " - "that was already in the registry.") - - self._cmaps[name] = cmap.copy() - - def unregister(self, name): - """ - Remove a colormap from the registry. - - You cannot remove built-in colormaps. - - If the named colormap is not registered, returns with no error, raises - if you try to de-register a default colormap. - - .. warning:: - - Colormap names are currently a shared namespace that may be used - by multiple packages. Use `unregister` only if you know you - have registered that name before. In particular, do not - unregister just in case to clean the name before registering a - new colormap. - - Parameters - ---------- - name : str - The name of the colormap to be removed. - - Raises - ------ - ValueError - If you try to remove a default built-in colormap. - """ - if name in self._builtin_cmaps: - raise ValueError(f"cannot unregister {name!r} which is a builtin " - "colormap.") - self._cmaps.pop(name, None) - - def get_cmap(self, cmap): - """ - Return a color map specified through *cmap*. - - Parameters - ---------- - cmap : str or `~matplotlib.colors.Colormap` or None - - - if a `.Colormap`, return it - - if a string, look it up in ``mpl.colormaps`` - - if None, return the Colormap defined in :rc:`image.cmap` - - Returns - ------- - Colormap - """ - # get the default color map - if cmap is None: - return self[mpl.rcParams["image.cmap"]] - - # if the user passed in a Colormap, simply return it - if isinstance(cmap, colors.Colormap): - return cmap - if isinstance(cmap, str): - _api.check_in_list(sorted(_colormaps), cmap=cmap) - # otherwise, it must be a string so look it up - return self[cmap] - raise TypeError( - 'get_cmap expects None or an instance of a str or Colormap . ' + - f'you passed {cmap!r} of type {type(cmap)}' - ) - - -# public access to the colormaps should be via `matplotlib.colormaps`. For now, -# we still create the registry here, but that should stay an implementation -# detail. -_colormaps = ColormapRegistry(_gen_cmap_registry()) -globals().update(_colormaps) - - -@_api.deprecated("3.7", alternative="``matplotlib.colormaps.register(name)``") -def register_cmap(name=None, cmap=None, *, override_builtin=False): - """ - Add a colormap to the set recognized by :func:`get_cmap`. - - Register a new colormap to be accessed by name :: - - LinearSegmentedColormap('swirly', data, lut) - register_cmap(cmap=swirly_cmap) - - Parameters - ---------- - name : str, optional - The name that can be used in :func:`get_cmap` or :rc:`image.cmap` - - If absent, the name will be the :attr:`~matplotlib.colors.Colormap.name` - attribute of the *cmap*. - - cmap : matplotlib.colors.Colormap - Despite being the second argument and having a default value, this - is a required argument. - - override_builtin : bool - - Allow built-in colormaps to be overridden by a user-supplied - colormap. - - Please do not use this unless you are sure you need it. - """ - _api.check_isinstance((str, None), name=name) - if name is None: - try: - name = cmap.name - except AttributeError as err: - raise ValueError("Arguments must include a name or a " - "Colormap") from err - # override_builtin is allowed here for backward compatibility - # this is just a shim to enable that to work privately in - # the global ColormapRegistry - _colormaps._allow_override_builtin = override_builtin - _colormaps.register(cmap, name=name, force=override_builtin) - _colormaps._allow_override_builtin = False - - -def _get_cmap(name=None, lut=None): - """ - Get a colormap instance, defaulting to rc values if *name* is None. - - Parameters - ---------- - name : `~matplotlib.colors.Colormap` or str or None, default: None - If a `.Colormap` instance, it will be returned. Otherwise, the name of - a colormap known to Matplotlib, which will be resampled by *lut*. The - default, None, means :rc:`image.cmap`. - lut : int or None, default: None - If *name* is not already a Colormap instance and *lut* is not None, the - colormap will be resampled to have *lut* entries in the lookup table. - - Returns - ------- - Colormap - """ - if name is None: - name = mpl.rcParams['image.cmap'] - if isinstance(name, colors.Colormap): - return name - _api.check_in_list(sorted(_colormaps), name=name) - if lut is None: - return _colormaps[name] - else: - return _colormaps[name].resampled(lut) - -# do it in two steps like this so we can have an un-deprecated version in -# pyplot. -get_cmap = _api.deprecated( - '3.7', - name='get_cmap', - alternative=( - "``matplotlib.colormaps[name]`` " + - "or ``matplotlib.colormaps.get_cmap(obj)``" - ) -)(_get_cmap) - - -@_api.deprecated("3.7", - alternative="``matplotlib.colormaps.unregister(name)``") -def unregister_cmap(name): - """ - Remove a colormap recognized by :func:`get_cmap`. - - You may not remove built-in colormaps. - - If the named colormap is not registered, returns with no error, raises - if you try to de-register a default colormap. - - .. warning:: - - Colormap names are currently a shared namespace that may be used - by multiple packages. Use `unregister_cmap` only if you know you - have registered that name before. In particular, do not - unregister just in case to clean the name before registering a - new colormap. - - Parameters - ---------- - name : str - The name of the colormap to be un-registered - - Returns - ------- - ColorMap or None - If the colormap was registered, return it if not return `None` - - Raises - ------ - ValueError - If you try to de-register a default built-in colormap. - """ - cmap = _colormaps.get(name, None) - _colormaps.unregister(name) - return cmap - - -def _auto_norm_from_scale(scale_cls): - """ - Automatically generate a norm class from *scale_cls*. - - This differs from `.colors.make_norm_from_scale` in the following points: - - - This function is not a class decorator, but directly returns a norm class - (as if decorating `.Normalize`). - - The scale is automatically constructed with ``nonpositive="mask"``, if it - supports such a parameter, to work around the difference in defaults - between standard scales (which use "clip") and norms (which use "mask"). - - Note that ``make_norm_from_scale`` caches the generated norm classes - (not the instances) and reuses them for later calls. For example, - ``type(_auto_norm_from_scale("log")) == LogNorm``. - """ - # Actually try to construct an instance, to verify whether - # ``nonpositive="mask"`` is supported. - try: - norm = colors.make_norm_from_scale( - functools.partial(scale_cls, nonpositive="mask"))( - colors.Normalize)() - except TypeError: - norm = colors.make_norm_from_scale(scale_cls)( - colors.Normalize)() - return type(norm) - - -class ScalarMappable: - """ - A mixin class to map scalar data to RGBA. - - The ScalarMappable applies data normalization before returning RGBA colors - from the given colormap. - """ - - def __init__(self, norm=None, cmap=None): - """ - Parameters - ---------- - norm : `.Normalize` (or subclass thereof) or str or None - The normalizing object which scales data, typically into the - interval ``[0, 1]``. - If a `str`, a `.Normalize` subclass is dynamically generated based - on the scale with the corresponding name. - If *None*, *norm* defaults to a *colors.Normalize* object which - initializes its scaling based on the first data processed. - cmap : str or `~matplotlib.colors.Colormap` - The colormap used to map normalized data values to RGBA colors. - """ - self._A = None - self._norm = None # So that the setter knows we're initializing. - self.set_norm(norm) # The Normalize instance of this ScalarMappable. - self.cmap = None # So that the setter knows we're initializing. - self.set_cmap(cmap) # The Colormap instance of this ScalarMappable. - #: The last colorbar associated with this ScalarMappable. May be None. - self.colorbar = None - self.callbacks = cbook.CallbackRegistry(signals=["changed"]) - - def _scale_norm(self, norm, vmin, vmax): - """ - Helper for initial scaling. - - Used by public functions that create a ScalarMappable and support - parameters *vmin*, *vmax* and *norm*. This makes sure that a *norm* - will take precedence over *vmin*, *vmax*. - - Note that this method does not set the norm. - """ - if vmin is not None or vmax is not None: - self.set_clim(vmin, vmax) - if isinstance(norm, colors.Normalize): - raise ValueError( - "Passing a Normalize instance simultaneously with " - "vmin/vmax is not supported. Please pass vmin/vmax " - "directly to the norm when creating it.") - - # always resolve the autoscaling so we have concrete limits - # rather than deferring to draw time. - self.autoscale_None() - - def to_rgba(self, x, alpha=None, bytes=False, norm=True): - """ - Return a normalized rgba array corresponding to *x*. - - In the normal case, *x* is a 1D or 2D sequence of scalars, and - the corresponding `~numpy.ndarray` of rgba values will be returned, - based on the norm and colormap set for this ScalarMappable. - - There is one special case, for handling images that are already - rgb or rgba, such as might have been read from an image file. - If *x* is an `~numpy.ndarray` with 3 dimensions, - and the last dimension is either 3 or 4, then it will be - treated as an rgb or rgba array, and no mapping will be done. - The array can be uint8, or it can be floating point with - values in the 0-1 range; otherwise a ValueError will be raised. - If it is a masked array, the mask will be ignored. - If the last dimension is 3, the *alpha* kwarg (defaulting to 1) - will be used to fill in the transparency. If the last dimension - is 4, the *alpha* kwarg is ignored; it does not - replace the preexisting alpha. A ValueError will be raised - if the third dimension is other than 3 or 4. - - In either case, if *bytes* is *False* (default), the rgba - array will be floats in the 0-1 range; if it is *True*, - the returned rgba array will be uint8 in the 0 to 255 range. - - If norm is False, no normalization of the input data is - performed, and it is assumed to be in the range (0-1). - - """ - # First check for special case, image input: - try: - if x.ndim == 3: - if x.shape[2] == 3: - if alpha is None: - alpha = 1 - if x.dtype == np.uint8: - alpha = np.uint8(alpha * 255) - m, n = x.shape[:2] - xx = np.empty(shape=(m, n, 4), dtype=x.dtype) - xx[:, :, :3] = x - xx[:, :, 3] = alpha - elif x.shape[2] == 4: - xx = x - else: - raise ValueError("Third dimension must be 3 or 4") - if xx.dtype.kind == 'f': - if norm and (xx.max() > 1 or xx.min() < 0): - raise ValueError("Floating point image RGB values " - "must be in the 0..1 range.") - if bytes: - xx = (xx * 255).astype(np.uint8) - elif xx.dtype == np.uint8: - if not bytes: - xx = xx.astype(np.float32) / 255 - else: - raise ValueError("Image RGB array must be uint8 or " - "floating point; found %s" % xx.dtype) - return xx - except AttributeError: - # e.g., x is not an ndarray; so try mapping it - pass - - # This is the normal case, mapping a scalar array: - x = ma.asarray(x) - if norm: - x = self.norm(x) - rgba = self.cmap(x, alpha=alpha, bytes=bytes) - return rgba - - def set_array(self, A): - """ - Set the value array from array-like *A*. - - Parameters - ---------- - A : array-like or None - The values that are mapped to colors. - - The base class `.ScalarMappable` does not make any assumptions on - the dimensionality and shape of the value array *A*. - """ - if A is None: - self._A = None - return - - A = cbook.safe_masked_invalid(A, copy=True) - if not np.can_cast(A.dtype, float, "same_kind"): - raise TypeError(f"Image data of dtype {A.dtype} cannot be " - "converted to float") - - self._A = A - - def get_array(self): - """ - Return the array of values, that are mapped to colors. - - The base class `.ScalarMappable` does not make any assumptions on - the dimensionality and shape of the array. - """ - return self._A - - def get_cmap(self): - """Return the `.Colormap` instance.""" - return self.cmap - - def get_clim(self): - """ - Return the values (min, max) that are mapped to the colormap limits. - """ - return self.norm.vmin, self.norm.vmax - - def set_clim(self, vmin=None, vmax=None): - """ - Set the norm limits for image scaling. - - Parameters - ---------- - vmin, vmax : float - The limits. - - The limits may also be passed as a tuple (*vmin*, *vmax*) as a - single positional argument. - - .. ACCEPTS: (vmin: float, vmax: float) - """ - # If the norm's limits are updated self.changed() will be called - # through the callbacks attached to the norm - if vmax is None: - try: - vmin, vmax = vmin - except (TypeError, ValueError): - pass - if vmin is not None: - self.norm.vmin = colors._sanitize_extrema(vmin) - if vmax is not None: - self.norm.vmax = colors._sanitize_extrema(vmax) - - def get_alpha(self): - """ - Returns - ------- - float - Always returns 1. - """ - # This method is intended to be overridden by Artist sub-classes - return 1. - - def set_cmap(self, cmap): - """ - Set the colormap for luminance data. - - Parameters - ---------- - cmap : `.Colormap` or str or None - """ - in_init = self.cmap is None - - self.cmap = _ensure_cmap(cmap) - if not in_init: - self.changed() # Things are not set up properly yet. - - @property - def norm(self): - return self._norm - - @norm.setter - def norm(self, norm): - _api.check_isinstance((colors.Normalize, str, None), norm=norm) - if norm is None: - norm = colors.Normalize() - elif isinstance(norm, str): - try: - scale_cls = scale._scale_mapping[norm] - except KeyError: - raise ValueError( - "Invalid norm str name; the following values are " - "supported: {}".format(", ".join(scale._scale_mapping)) - ) from None - norm = _auto_norm_from_scale(scale_cls)() - - if norm is self.norm: - # We aren't updating anything - return - - in_init = self.norm is None - # Remove the current callback and connect to the new one - if not in_init: - self.norm.callbacks.disconnect(self._id_norm) - self._norm = norm - self._id_norm = self.norm.callbacks.connect('changed', - self.changed) - if not in_init: - self.changed() - - def set_norm(self, norm): - """ - Set the normalization instance. - - Parameters - ---------- - norm : `.Normalize` or str or None - - Notes - ----- - If there are any colorbars using the mappable for this norm, setting - the norm of the mappable will reset the norm, locator, and formatters - on the colorbar to default. - """ - self.norm = norm - - def autoscale(self): - """ - Autoscale the scalar limits on the norm instance using the - current array - """ - if self._A is None: - raise TypeError('You must first set_array for mappable') - # If the norm's limits are updated self.changed() will be called - # through the callbacks attached to the norm - self.norm.autoscale(self._A) - - def autoscale_None(self): - """ - Autoscale the scalar limits on the norm instance using the - current array, changing only limits that are None - """ - if self._A is None: - raise TypeError('You must first set_array for mappable') - # If the norm's limits are updated self.changed() will be called - # through the callbacks attached to the norm - self.norm.autoscale_None(self._A) - - def changed(self): - """ - Call this whenever the mappable is changed to notify all the - callbackSM listeners to the 'changed' signal. - """ - self.callbacks.process('changed', self) - self.stale = True - - -# The docstrings here must be generic enough to apply to all relevant methods. -mpl._docstring.interpd.update( - cmap_doc="""\ -cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap` - The Colormap instance or registered colormap name used to map scalar data - to colors.""", - norm_doc="""\ -norm : str or `~matplotlib.colors.Normalize`, optional - The normalization method used to scale scalar data to the [0, 1] range - before mapping to colors using *cmap*. By default, a linear scaling is - used, mapping the lowest value to 0 and the highest to 1. - - If given, this can be one of the following: - - - An instance of `.Normalize` or one of its subclasses - (see :doc:`/tutorials/colors/colormapnorms`). - - A scale name, i.e. one of "linear", "log", "symlog", "logit", etc. For a - list of available scales, call `matplotlib.scale.get_scale_names()`. - In that case, a suitable `.Normalize` subclass is dynamically generated - and instantiated.""", - vmin_vmax_doc="""\ -vmin, vmax : float, optional - When using scalar data and no explicit *norm*, *vmin* and *vmax* define - the data range that the colormap covers. By default, the colormap covers - the complete value range of the supplied data. It is an error to use - *vmin*/*vmax* when a *norm* instance is given (but using a `str` *norm* - name together with *vmin*/*vmax* is acceptable).""", -) - - -def _ensure_cmap(cmap): - """ - Ensure that we have a `.Colormap` object. - - For internal use to preserve type stability of errors. - - Parameters - ---------- - cmap : None, str, Colormap - - - if a `Colormap`, return it - - if a string, look it up in mpl.colormaps - - if None, look up the default color map in mpl.colormaps - - Returns - ------- - Colormap - - """ - if isinstance(cmap, colors.Colormap): - return cmap - cmap_name = cmap if cmap is not None else mpl.rcParams["image.cmap"] - # use check_in_list to ensure type stability of the exception raised by - # the internal usage of this (ValueError vs KeyError) - _api.check_in_list(sorted(_colormaps), cmap=cmap_name) - return mpl.colormaps[cmap_name] diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/collections.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/collections.py deleted file mode 100644 index bf88dd2..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/collections.py +++ /dev/null @@ -1,2113 +0,0 @@ -""" -Classes for the efficient drawing of large collections of objects that -share most properties, e.g., a large number of line segments or -polygons. - -The classes are not meant to be as flexible as their single element -counterparts (e.g., you may not be able to select all line styles) but -they are meant to be fast for common use cases (e.g., a large set of solid -line segments). -""" - -import math -from numbers import Number -import warnings - -import numpy as np - -import matplotlib as mpl -from . import (_api, _path, artist, cbook, cm, colors as mcolors, _docstring, - hatch as mhatch, lines as mlines, path as mpath, transforms) -from ._enums import JoinStyle, CapStyle - - -# "color" is excluded; it is a compound setter, and its docstring differs -# in LineCollection. -@_api.define_aliases({ - "antialiased": ["antialiaseds", "aa"], - "edgecolor": ["edgecolors", "ec"], - "facecolor": ["facecolors", "fc"], - "linestyle": ["linestyles", "dashes", "ls"], - "linewidth": ["linewidths", "lw"], - "offset_transform": ["transOffset"], -}) -class Collection(artist.Artist, cm.ScalarMappable): - r""" - Base class for Collections. Must be subclassed to be usable. - - A Collection represents a sequence of `.Patch`\es that can be drawn - more efficiently together than individually. For example, when a single - path is being drawn repeatedly at different offsets, the renderer can - typically execute a ``draw_marker()`` call much more efficiently than a - series of repeated calls to ``draw_path()`` with the offsets put in - one-by-one. - - Most properties of a collection can be configured per-element. Therefore, - Collections have "plural" versions of many of the properties of a `.Patch` - (e.g. `.Collection.get_paths` instead of `.Patch.get_path`). Exceptions are - the *zorder*, *hatch*, *pickradius*, *capstyle* and *joinstyle* properties, - which can only be set globally for the whole collection. - - Besides these exceptions, all properties can be specified as single values - (applying to all elements) or sequences of values. The property of the - ``i``\th element of the collection is:: - - prop[i % len(prop)] - - Each Collection can optionally be used as its own `.ScalarMappable` by - passing the *norm* and *cmap* parameters to its constructor. If the - Collection's `.ScalarMappable` matrix ``_A`` has been set (via a call - to `.Collection.set_array`), then at draw time this internal scalar - mappable will be used to set the ``facecolors`` and ``edgecolors``, - ignoring those that were manually passed in. - """ - #: Either a list of 3x3 arrays or an Nx3x3 array (representing N - #: transforms), suitable for the `all_transforms` argument to - #: `~matplotlib.backend_bases.RendererBase.draw_path_collection`; - #: each 3x3 array is used to initialize an - #: `~matplotlib.transforms.Affine2D` object. - #: Each kind of collection defines this based on its arguments. - _transforms = np.empty((0, 3, 3)) - - # Whether to draw an edge by default. Set on a - # subclass-by-subclass basis. - _edge_default = False - - @_docstring.interpd - @_api.make_keyword_only("3.6", name="edgecolors") - def __init__(self, - edgecolors=None, - facecolors=None, - linewidths=None, - linestyles='solid', - capstyle=None, - joinstyle=None, - antialiaseds=None, - offsets=None, - offset_transform=None, - norm=None, # optional for ScalarMappable - cmap=None, # ditto - pickradius=5.0, - hatch=None, - urls=None, - *, - zorder=1, - **kwargs - ): - """ - Parameters - ---------- - edgecolors : color or list of colors, default: :rc:`patch.edgecolor` - Edge color for each patch making up the collection. The special - value 'face' can be passed to make the edgecolor match the - facecolor. - facecolors : color or list of colors, default: :rc:`patch.facecolor` - Face color for each patch making up the collection. - linewidths : float or list of floats, default: :rc:`patch.linewidth` - Line width for each patch making up the collection. - linestyles : str or tuple or list thereof, default: 'solid' - Valid strings are ['solid', 'dashed', 'dashdot', 'dotted', '-', - '--', '-.', ':']. Dash tuples should be of the form:: - - (offset, onoffseq), - - where *onoffseq* is an even length tuple of on and off ink lengths - in points. For examples, see - :doc:`/gallery/lines_bars_and_markers/linestyles`. - capstyle : `.CapStyle`-like, default: :rc:`patch.capstyle` - Style to use for capping lines for all paths in the collection. - Allowed values are %(CapStyle)s. - joinstyle : `.JoinStyle`-like, default: :rc:`patch.joinstyle` - Style to use for joining lines for all paths in the collection. - Allowed values are %(JoinStyle)s. - antialiaseds : bool or list of bool, default: :rc:`patch.antialiased` - Whether each patch in the collection should be drawn with - antialiasing. - offsets : (float, float) or list thereof, default: (0, 0) - A vector by which to translate each patch after rendering (default - is no translation). The translation is performed in screen (pixel) - coordinates (i.e. after the Artist's transform is applied). - offset_transform : `~.Transform`, default: `.IdentityTransform` - A single transform which will be applied to each *offsets* vector - before it is used. - cmap, norm - Data normalization and colormapping parameters. See - `.ScalarMappable` for a detailed description. - hatch : str, optional - Hatching pattern to use in filled paths, if any. Valid strings are - ['/', '\\', '|', '-', '+', 'x', 'o', 'O', '.', '*']. See - :doc:`/gallery/shapes_and_collections/hatch_style_reference` for - the meaning of each hatch type. - pickradius : float, default: 5.0 - If ``pickradius <= 0``, then `.Collection.contains` will return - ``True`` whenever the test point is inside of one of the polygons - formed by the control points of a Path in the Collection. On the - other hand, if it is greater than 0, then we instead check if the - test point is contained in a stroke of width ``2*pickradius`` - following any of the Paths in the Collection. - urls : list of str, default: None - A URL for each patch to link to once drawn. Currently only works - for the SVG backend. See :doc:`/gallery/misc/hyperlinks_sgskip` for - examples. - zorder : float, default: 1 - The drawing order, shared by all Patches in the Collection. See - :doc:`/gallery/misc/zorder_demo` for all defaults and examples. - """ - artist.Artist.__init__(self) - cm.ScalarMappable.__init__(self, norm, cmap) - # list of un-scaled dash patterns - # this is needed scaling the dash pattern by linewidth - self._us_linestyles = [(0, None)] - # list of dash patterns - self._linestyles = [(0, None)] - # list of unbroadcast/scaled linewidths - self._us_lw = [0] - self._linewidths = [0] - # Flags set by _set_mappable_flags: are colors from mapping an array? - self._face_is_mapped = None - self._edge_is_mapped = None - self._mapped_colors = None # calculated in update_scalarmappable - self._hatch_color = mcolors.to_rgba(mpl.rcParams['hatch.color']) - self.set_facecolor(facecolors) - self.set_edgecolor(edgecolors) - self.set_linewidth(linewidths) - self.set_linestyle(linestyles) - self.set_antialiased(antialiaseds) - self.set_pickradius(pickradius) - self.set_urls(urls) - self.set_hatch(hatch) - self.set_zorder(zorder) - - if capstyle: - self.set_capstyle(capstyle) - else: - self._capstyle = None - - if joinstyle: - self.set_joinstyle(joinstyle) - else: - self._joinstyle = None - - if offsets is not None: - offsets = np.asanyarray(offsets, float) - # Broadcast (2,) -> (1, 2) but nothing else. - if offsets.shape == (2,): - offsets = offsets[None, :] - - self._offsets = offsets - self._offset_transform = offset_transform - - self._path_effects = None - self._internal_update(kwargs) - self._paths = None - - def get_paths(self): - return self._paths - - def set_paths(self, paths): - raise NotImplementedError - - def get_transforms(self): - return self._transforms - - def get_offset_transform(self): - """Return the `.Transform` instance used by this artist offset.""" - if self._offset_transform is None: - self._offset_transform = transforms.IdentityTransform() - elif (not isinstance(self._offset_transform, transforms.Transform) - and hasattr(self._offset_transform, '_as_mpl_transform')): - self._offset_transform = \ - self._offset_transform._as_mpl_transform(self.axes) - return self._offset_transform - - @_api.rename_parameter("3.6", "transOffset", "offset_transform") - def set_offset_transform(self, offset_transform): - """ - Set the artist offset transform. - - Parameters - ---------- - offset_transform : `.Transform` - """ - self._offset_transform = offset_transform - - def get_datalim(self, transData): - # Calculate the data limits and return them as a `.Bbox`. - # - # This operation depends on the transforms for the data in the - # collection and whether the collection has offsets: - # - # 1. offsets = None, transform child of transData: use the paths for - # the automatic limits (i.e. for LineCollection in streamline). - # 2. offsets != None: offset_transform is child of transData: - # - # a. transform is child of transData: use the path + offset for - # limits (i.e for bar). - # b. transform is not a child of transData: just use the offsets - # for the limits (i.e. for scatter) - # - # 3. otherwise return a null Bbox. - - transform = self.get_transform() - offset_trf = self.get_offset_transform() - if not (isinstance(offset_trf, transforms.IdentityTransform) - or offset_trf.contains_branch(transData)): - # if the offsets are in some coords other than data, - # then don't use them for autoscaling. - return transforms.Bbox.null() - offsets = self.get_offsets() - - paths = self.get_paths() - if not len(paths): - # No paths to transform - return transforms.Bbox.null() - - if not transform.is_affine: - paths = [transform.transform_path_non_affine(p) for p in paths] - # Don't convert transform to transform.get_affine() here because - # we may have transform.contains_branch(transData) but not - # transforms.get_affine().contains_branch(transData). But later, - # be careful to only apply the affine part that remains. - - if any(transform.contains_branch_seperately(transData)): - # collections that are just in data units (like quiver) - # can properly have the axes limits set by their shape + - # offset. LineCollections that have no offsets can - # also use this algorithm (like streamplot). - if isinstance(offsets, np.ma.MaskedArray): - offsets = offsets.filled(np.nan) - # get_path_collection_extents handles nan but not masked arrays - return mpath.get_path_collection_extents( - transform.get_affine() - transData, paths, - self.get_transforms(), - offset_trf.transform_non_affine(offsets), - offset_trf.get_affine().frozen()) - - # NOTE: None is the default case where no offsets were passed in - if self._offsets is not None: - # this is for collections that have their paths (shapes) - # in physical, axes-relative, or figure-relative units - # (i.e. like scatter). We can't uniquely set limits based on - # those shapes, so we just set the limits based on their - # location. - offsets = (offset_trf - transData).transform(offsets) - # note A-B means A B^{-1} - offsets = np.ma.masked_invalid(offsets) - if not offsets.mask.all(): - bbox = transforms.Bbox.null() - bbox.update_from_data_xy(offsets) - return bbox - return transforms.Bbox.null() - - def get_window_extent(self, renderer=None): - # TODO: check to ensure that this does not fail for - # cases other than scatter plot legend - return self.get_datalim(transforms.IdentityTransform()) - - def _prepare_points(self): - # Helper for drawing and hit testing. - - transform = self.get_transform() - offset_trf = self.get_offset_transform() - offsets = self.get_offsets() - paths = self.get_paths() - - if self.have_units(): - paths = [] - for path in self.get_paths(): - vertices = path.vertices - xs, ys = vertices[:, 0], vertices[:, 1] - xs = self.convert_xunits(xs) - ys = self.convert_yunits(ys) - paths.append(mpath.Path(np.column_stack([xs, ys]), path.codes)) - xs = self.convert_xunits(offsets[:, 0]) - ys = self.convert_yunits(offsets[:, 1]) - offsets = np.ma.column_stack([xs, ys]) - - if not transform.is_affine: - paths = [transform.transform_path_non_affine(path) - for path in paths] - transform = transform.get_affine() - if not offset_trf.is_affine: - offsets = offset_trf.transform_non_affine(offsets) - # This might have changed an ndarray into a masked array. - offset_trf = offset_trf.get_affine() - - if isinstance(offsets, np.ma.MaskedArray): - offsets = offsets.filled(np.nan) - # Changing from a masked array to nan-filled ndarray - # is probably most efficient at this point. - - return transform, offset_trf, offsets, paths - - @artist.allow_rasterization - def draw(self, renderer): - if not self.get_visible(): - return - renderer.open_group(self.__class__.__name__, self.get_gid()) - - self.update_scalarmappable() - - transform, offset_trf, offsets, paths = self._prepare_points() - - gc = renderer.new_gc() - self._set_gc_clip(gc) - gc.set_snap(self.get_snap()) - - if self._hatch: - gc.set_hatch(self._hatch) - gc.set_hatch_color(self._hatch_color) - - if self.get_sketch_params() is not None: - gc.set_sketch_params(*self.get_sketch_params()) - - if self.get_path_effects(): - from matplotlib.patheffects import PathEffectRenderer - renderer = PathEffectRenderer(self.get_path_effects(), renderer) - - # If the collection is made up of a single shape/color/stroke, - # it can be rendered once and blitted multiple times, using - # `draw_markers` rather than `draw_path_collection`. This is - # *much* faster for Agg, and results in smaller file sizes in - # PDF/SVG/PS. - - trans = self.get_transforms() - facecolors = self.get_facecolor() - edgecolors = self.get_edgecolor() - do_single_path_optimization = False - if (len(paths) == 1 and len(trans) <= 1 and - len(facecolors) == 1 and len(edgecolors) == 1 and - len(self._linewidths) == 1 and - all(ls[1] is None for ls in self._linestyles) and - len(self._antialiaseds) == 1 and len(self._urls) == 1 and - self.get_hatch() is None): - if len(trans): - combined_transform = transforms.Affine2D(trans[0]) + transform - else: - combined_transform = transform - extents = paths[0].get_extents(combined_transform) - if (extents.width < self.figure.bbox.width - and extents.height < self.figure.bbox.height): - do_single_path_optimization = True - - if self._joinstyle: - gc.set_joinstyle(self._joinstyle) - - if self._capstyle: - gc.set_capstyle(self._capstyle) - - if do_single_path_optimization: - gc.set_foreground(tuple(edgecolors[0])) - gc.set_linewidth(self._linewidths[0]) - gc.set_dashes(*self._linestyles[0]) - gc.set_antialiased(self._antialiaseds[0]) - gc.set_url(self._urls[0]) - renderer.draw_markers( - gc, paths[0], combined_transform.frozen(), - mpath.Path(offsets), offset_trf, tuple(facecolors[0])) - else: - renderer.draw_path_collection( - gc, transform.frozen(), paths, - self.get_transforms(), offsets, offset_trf, - self.get_facecolor(), self.get_edgecolor(), - self._linewidths, self._linestyles, - self._antialiaseds, self._urls, - "screen") # offset_position, kept for backcompat. - - gc.restore() - renderer.close_group(self.__class__.__name__) - self.stale = False - - @_api.rename_parameter("3.6", "pr", "pickradius") - def set_pickradius(self, pickradius): - """ - Set the pick radius used for containment tests. - - Parameters - ---------- - pickradius : float - Pick radius, in points. - """ - self._pickradius = pickradius - - def get_pickradius(self): - return self._pickradius - - def contains(self, mouseevent): - """ - Test whether the mouse event occurred in the collection. - - Returns ``bool, dict(ind=itemlist)``, where every item in itemlist - contains the event. - """ - inside, info = self._default_contains(mouseevent) - if inside is not None: - return inside, info - - if not self.get_visible(): - return False, {} - - pickradius = ( - float(self._picker) - if isinstance(self._picker, Number) and - self._picker is not True # the bool, not just nonzero or 1 - else self._pickradius) - - if self.axes: - self.axes._unstale_viewLim() - - transform, offset_trf, offsets, paths = self._prepare_points() - - # Tests if the point is contained on one of the polygons formed - # by the control points of each of the paths. A point is considered - # "on" a path if it would lie within a stroke of width 2*pickradius - # following the path. If pickradius <= 0, then we instead simply check - # if the point is *inside* of the path instead. - ind = _path.point_in_path_collection( - mouseevent.x, mouseevent.y, pickradius, - transform.frozen(), paths, self.get_transforms(), - offsets, offset_trf, pickradius <= 0) - - return len(ind) > 0, dict(ind=ind) - - def set_urls(self, urls): - """ - Parameters - ---------- - urls : list of str or None - - Notes - ----- - URLs are currently only implemented by the SVG backend. They are - ignored by all other backends. - """ - self._urls = urls if urls is not None else [None] - self.stale = True - - def get_urls(self): - """ - Return a list of URLs, one for each element of the collection. - - The list contains *None* for elements without a URL. See - :doc:`/gallery/misc/hyperlinks_sgskip` for an example. - """ - return self._urls - - def set_hatch(self, hatch): - r""" - Set the hatching pattern - - *hatch* can be one of:: - - / - diagonal hatching - \ - back diagonal - | - vertical - - - horizontal - + - crossed - x - crossed diagonal - o - small circle - O - large circle - . - dots - * - stars - - Letters can be combined, in which case all the specified - hatchings are done. If same letter repeats, it increases the - density of hatching of that pattern. - - Hatching is supported in the PostScript, PDF, SVG and Agg - backends only. - - Unlike other properties such as linewidth and colors, hatching - can only be specified for the collection as a whole, not separately - for each member. - - Parameters - ---------- - hatch : {'/', '\\', '|', '-', '+', 'x', 'o', 'O', '.', '*'} - """ - # Use validate_hatch(list) after deprecation. - mhatch._validate_hatch_pattern(hatch) - self._hatch = hatch - self.stale = True - - def get_hatch(self): - """Return the current hatching pattern.""" - return self._hatch - - def set_offsets(self, offsets): - """ - Set the offsets for the collection. - - Parameters - ---------- - offsets : (N, 2) or (2,) array-like - """ - offsets = np.asanyarray(offsets) - if offsets.shape == (2,): # Broadcast (2,) -> (1, 2) but nothing else. - offsets = offsets[None, :] - cstack = (np.ma.column_stack if isinstance(offsets, np.ma.MaskedArray) - else np.column_stack) - self._offsets = cstack( - (np.asanyarray(self.convert_xunits(offsets[:, 0]), float), - np.asanyarray(self.convert_yunits(offsets[:, 1]), float))) - self.stale = True - - def get_offsets(self): - """Return the offsets for the collection.""" - # Default to zeros in the no-offset (None) case - return np.zeros((1, 2)) if self._offsets is None else self._offsets - - def _get_default_linewidth(self): - # This may be overridden in a subclass. - return mpl.rcParams['patch.linewidth'] # validated as float - - def set_linewidth(self, lw): - """ - Set the linewidth(s) for the collection. *lw* can be a scalar - or a sequence; if it is a sequence the patches will cycle - through the sequence - - Parameters - ---------- - lw : float or list of floats - """ - if lw is None: - lw = self._get_default_linewidth() - # get the un-scaled/broadcast lw - self._us_lw = np.atleast_1d(lw) - - # scale all of the dash patterns. - self._linewidths, self._linestyles = self._bcast_lwls( - self._us_lw, self._us_linestyles) - self.stale = True - - def set_linestyle(self, ls): - """ - Set the linestyle(s) for the collection. - - =========================== ================= - linestyle description - =========================== ================= - ``'-'`` or ``'solid'`` solid line - ``'--'`` or ``'dashed'`` dashed line - ``'-.'`` or ``'dashdot'`` dash-dotted line - ``':'`` or ``'dotted'`` dotted line - =========================== ================= - - Alternatively a dash tuple of the following form can be provided:: - - (offset, onoffseq), - - where ``onoffseq`` is an even length tuple of on and off ink in points. - - Parameters - ---------- - ls : str or tuple or list thereof - Valid values for individual linestyles include {'-', '--', '-.', - ':', '', (offset, on-off-seq)}. See `.Line2D.set_linestyle` for a - complete description. - """ - try: - dashes = [mlines._get_dash_pattern(ls)] - except ValueError: - try: - dashes = [mlines._get_dash_pattern(x) for x in ls] - except ValueError as err: - emsg = f'Do not know how to convert {ls!r} to dashes' - raise ValueError(emsg) from err - - # get the list of raw 'unscaled' dash patterns - self._us_linestyles = dashes - - # broadcast and scale the lw and dash patterns - self._linewidths, self._linestyles = self._bcast_lwls( - self._us_lw, self._us_linestyles) - - @_docstring.interpd - def set_capstyle(self, cs): - """ - Set the `.CapStyle` for the collection (for all its elements). - - Parameters - ---------- - cs : `.CapStyle` or %(CapStyle)s - """ - self._capstyle = CapStyle(cs) - - def get_capstyle(self): - return self._capstyle.name - - @_docstring.interpd - def set_joinstyle(self, js): - """ - Set the `.JoinStyle` for the collection (for all its elements). - - Parameters - ---------- - js : `.JoinStyle` or %(JoinStyle)s - """ - self._joinstyle = JoinStyle(js) - - def get_joinstyle(self): - return self._joinstyle.name - - @staticmethod - def _bcast_lwls(linewidths, dashes): - """ - Internal helper function to broadcast + scale ls/lw - - In the collection drawing code, the linewidth and linestyle are cycled - through as circular buffers (via ``v[i % len(v)]``). Thus, if we are - going to scale the dash pattern at set time (not draw time) we need to - do the broadcasting now and expand both lists to be the same length. - - Parameters - ---------- - linewidths : list - line widths of collection - dashes : list - dash specification (offset, (dash pattern tuple)) - - Returns - ------- - linewidths, dashes : list - Will be the same length, dashes are scaled by paired linewidth - """ - if mpl.rcParams['_internal.classic_mode']: - return linewidths, dashes - # make sure they are the same length so we can zip them - if len(dashes) != len(linewidths): - l_dashes = len(dashes) - l_lw = len(linewidths) - gcd = math.gcd(l_dashes, l_lw) - dashes = list(dashes) * (l_lw // gcd) - linewidths = list(linewidths) * (l_dashes // gcd) - - # scale the dash patterns - dashes = [mlines._scale_dashes(o, d, lw) - for (o, d), lw in zip(dashes, linewidths)] - - return linewidths, dashes - - def set_antialiased(self, aa): - """ - Set the antialiasing state for rendering. - - Parameters - ---------- - aa : bool or list of bools - """ - if aa is None: - aa = self._get_default_antialiased() - self._antialiaseds = np.atleast_1d(np.asarray(aa, bool)) - self.stale = True - - def _get_default_antialiased(self): - # This may be overridden in a subclass. - return mpl.rcParams['patch.antialiased'] - - def set_color(self, c): - """ - Set both the edgecolor and the facecolor. - - Parameters - ---------- - c : color or list of RGBA tuples - - See Also - -------- - Collection.set_facecolor, Collection.set_edgecolor - For setting the edge or face color individually. - """ - self.set_facecolor(c) - self.set_edgecolor(c) - - def _get_default_facecolor(self): - # This may be overridden in a subclass. - return mpl.rcParams['patch.facecolor'] - - def _set_facecolor(self, c): - if c is None: - c = self._get_default_facecolor() - - self._facecolors = mcolors.to_rgba_array(c, self._alpha) - self.stale = True - - def set_facecolor(self, c): - """ - Set the facecolor(s) of the collection. *c* can be a color (all patches - have same color), or a sequence of colors; if it is a sequence the - patches will cycle through the sequence. - - If *c* is 'none', the patch will not be filled. - - Parameters - ---------- - c : color or list of colors - """ - if isinstance(c, str) and c.lower() in ("none", "face"): - c = c.lower() - self._original_facecolor = c - self._set_facecolor(c) - - def get_facecolor(self): - return self._facecolors - - def get_edgecolor(self): - if cbook._str_equal(self._edgecolors, 'face'): - return self.get_facecolor() - else: - return self._edgecolors - - def _get_default_edgecolor(self): - # This may be overridden in a subclass. - return mpl.rcParams['patch.edgecolor'] - - def _set_edgecolor(self, c): - set_hatch_color = True - if c is None: - if (mpl.rcParams['patch.force_edgecolor'] - or self._edge_default - or cbook._str_equal(self._original_facecolor, 'none')): - c = self._get_default_edgecolor() - else: - c = 'none' - set_hatch_color = False - if cbook._str_lower_equal(c, 'face'): - self._edgecolors = 'face' - self.stale = True - return - self._edgecolors = mcolors.to_rgba_array(c, self._alpha) - if set_hatch_color and len(self._edgecolors): - self._hatch_color = tuple(self._edgecolors[0]) - self.stale = True - - def set_edgecolor(self, c): - """ - Set the edgecolor(s) of the collection. - - Parameters - ---------- - c : color or list of colors or 'face' - The collection edgecolor(s). If a sequence, the patches cycle - through it. If 'face', match the facecolor. - """ - # We pass through a default value for use in LineCollection. - # This allows us to maintain None as the default indicator in - # _original_edgecolor. - if isinstance(c, str) and c.lower() in ("none", "face"): - c = c.lower() - self._original_edgecolor = c - self._set_edgecolor(c) - - def set_alpha(self, alpha): - """ - Set the transparency of the collection. - - Parameters - ---------- - alpha : float or array of float or None - If not None, *alpha* values must be between 0 and 1, inclusive. - If an array is provided, its length must match the number of - elements in the collection. Masked values and nans are not - supported. - """ - artist.Artist._set_alpha_for_array(self, alpha) - self._set_facecolor(self._original_facecolor) - self._set_edgecolor(self._original_edgecolor) - - set_alpha.__doc__ = artist.Artist._set_alpha_for_array.__doc__ - - def get_linewidth(self): - return self._linewidths - - def get_linestyle(self): - return self._linestyles - - def _set_mappable_flags(self): - """ - Determine whether edges and/or faces are color-mapped. - - This is a helper for update_scalarmappable. - It sets Boolean flags '_edge_is_mapped' and '_face_is_mapped'. - - Returns - ------- - mapping_change : bool - True if either flag is True, or if a flag has changed. - """ - # The flags are initialized to None to ensure this returns True - # the first time it is called. - edge0 = self._edge_is_mapped - face0 = self._face_is_mapped - # After returning, the flags must be Booleans, not None. - self._edge_is_mapped = False - self._face_is_mapped = False - if self._A is not None: - if not cbook._str_equal(self._original_facecolor, 'none'): - self._face_is_mapped = True - if cbook._str_equal(self._original_edgecolor, 'face'): - self._edge_is_mapped = True - else: - if self._original_edgecolor is None: - self._edge_is_mapped = True - - mapped = self._face_is_mapped or self._edge_is_mapped - changed = (edge0 is None or face0 is None - or self._edge_is_mapped != edge0 - or self._face_is_mapped != face0) - return mapped or changed - - def update_scalarmappable(self): - """ - Update colors from the scalar mappable array, if any. - - Assign colors to edges and faces based on the array and/or - colors that were directly set, as appropriate. - """ - if not self._set_mappable_flags(): - return - # Allow possibility to call 'self.set_array(None)'. - if self._A is not None: - # QuadMesh can map 2d arrays (but pcolormesh supplies 1d array) - if self._A.ndim > 1 and not isinstance(self, QuadMesh): - raise ValueError('Collections can only map rank 1 arrays') - if np.iterable(self._alpha): - if self._alpha.size != self._A.size: - raise ValueError( - f'Data array shape, {self._A.shape} ' - 'is incompatible with alpha array shape, ' - f'{self._alpha.shape}. ' - 'This can occur with the deprecated ' - 'behavior of the "flat" shading option, ' - 'in which a row and/or column of the data ' - 'array is dropped.') - # pcolormesh, scatter, maybe others flatten their _A - self._alpha = self._alpha.reshape(self._A.shape) - self._mapped_colors = self.to_rgba(self._A, self._alpha) - - if self._face_is_mapped: - self._facecolors = self._mapped_colors - else: - self._set_facecolor(self._original_facecolor) - if self._edge_is_mapped: - self._edgecolors = self._mapped_colors - else: - self._set_edgecolor(self._original_edgecolor) - self.stale = True - - def get_fill(self): - """Return whether face is colored.""" - return not cbook._str_lower_equal(self._original_facecolor, "none") - - def update_from(self, other): - """Copy properties from other to self.""" - - artist.Artist.update_from(self, other) - self._antialiaseds = other._antialiaseds - self._mapped_colors = other._mapped_colors - self._edge_is_mapped = other._edge_is_mapped - self._original_edgecolor = other._original_edgecolor - self._edgecolors = other._edgecolors - self._face_is_mapped = other._face_is_mapped - self._original_facecolor = other._original_facecolor - self._facecolors = other._facecolors - self._linewidths = other._linewidths - self._linestyles = other._linestyles - self._us_linestyles = other._us_linestyles - self._pickradius = other._pickradius - self._hatch = other._hatch - - # update_from for scalarmappable - self._A = other._A - self.norm = other.norm - self.cmap = other.cmap - self.stale = True - - -class _CollectionWithSizes(Collection): - """ - Base class for collections that have an array of sizes. - """ - _factor = 1.0 - - def get_sizes(self): - """ - Return the sizes ('areas') of the elements in the collection. - - Returns - ------- - array - The 'area' of each element. - """ - return self._sizes - - def set_sizes(self, sizes, dpi=72.0): - """ - Set the sizes of each member of the collection. - - Parameters - ---------- - sizes : `numpy.ndarray` or None - The size to set for each element of the collection. The - value is the 'area' of the element. - dpi : float, default: 72 - The dpi of the canvas. - """ - if sizes is None: - self._sizes = np.array([]) - self._transforms = np.empty((0, 3, 3)) - else: - self._sizes = np.asarray(sizes) - self._transforms = np.zeros((len(self._sizes), 3, 3)) - scale = np.sqrt(self._sizes) * dpi / 72.0 * self._factor - self._transforms[:, 0, 0] = scale - self._transforms[:, 1, 1] = scale - self._transforms[:, 2, 2] = 1.0 - self.stale = True - - @artist.allow_rasterization - def draw(self, renderer): - self.set_sizes(self._sizes, self.figure.dpi) - super().draw(renderer) - - -class PathCollection(_CollectionWithSizes): - r""" - A collection of `~.path.Path`\s, as created by e.g. `~.Axes.scatter`. - """ - - def __init__(self, paths, sizes=None, **kwargs): - """ - Parameters - ---------- - paths : list of `.path.Path` - The paths that will make up the `.Collection`. - sizes : array-like - The factor by which to scale each drawn `~.path.Path`. One unit - squared in the Path's data space is scaled to be ``sizes**2`` - points when rendered. - **kwargs - Forwarded to `.Collection`. - """ - - super().__init__(**kwargs) - self.set_paths(paths) - self.set_sizes(sizes) - self.stale = True - - def set_paths(self, paths): - self._paths = paths - self.stale = True - - def get_paths(self): - return self._paths - - def legend_elements(self, prop="colors", num="auto", - fmt=None, func=lambda x: x, **kwargs): - """ - Create legend handles and labels for a PathCollection. - - Each legend handle is a `.Line2D` representing the Path that was drawn, - and each label is a string what each Path represents. - - This is useful for obtaining a legend for a `~.Axes.scatter` plot; - e.g.:: - - scatter = plt.scatter([1, 2, 3], [4, 5, 6], c=[7, 2, 3]) - plt.legend(*scatter.legend_elements()) - - creates three legend elements, one for each color with the numerical - values passed to *c* as the labels. - - Also see the :ref:`automatedlegendcreation` example. - - Parameters - ---------- - prop : {"colors", "sizes"}, default: "colors" - If "colors", the legend handles will show the different colors of - the collection. If "sizes", the legend will show the different - sizes. To set both, use *kwargs* to directly edit the `.Line2D` - properties. - num : int, None, "auto" (default), array-like, or `~.ticker.Locator` - Target number of elements to create. - If None, use all unique elements of the mappable array. If an - integer, target to use *num* elements in the normed range. - If *"auto"*, try to determine which option better suits the nature - of the data. - The number of created elements may slightly deviate from *num* due - to a `~.ticker.Locator` being used to find useful locations. - If a list or array, use exactly those elements for the legend. - Finally, a `~.ticker.Locator` can be provided. - fmt : str, `~matplotlib.ticker.Formatter`, or None (default) - The format or formatter to use for the labels. If a string must be - a valid input for a `.StrMethodFormatter`. If None (the default), - use a `.ScalarFormatter`. - func : function, default: ``lambda x: x`` - Function to calculate the labels. Often the size (or color) - argument to `~.Axes.scatter` will have been pre-processed by the - user using a function ``s = f(x)`` to make the markers visible; - e.g. ``size = np.log10(x)``. Providing the inverse of this - function here allows that pre-processing to be inverted, so that - the legend labels have the correct values; e.g. ``func = lambda - x: 10**x``. - **kwargs - Allowed keyword arguments are *color* and *size*. E.g. it may be - useful to set the color of the markers if *prop="sizes"* is used; - similarly to set the size of the markers if *prop="colors"* is - used. Any further parameters are passed onto the `.Line2D` - instance. This may be useful to e.g. specify a different - *markeredgecolor* or *alpha* for the legend handles. - - Returns - ------- - handles : list of `.Line2D` - Visual representation of each element of the legend. - labels : list of str - The string labels for elements of the legend. - """ - handles = [] - labels = [] - hasarray = self.get_array() is not None - if fmt is None: - fmt = mpl.ticker.ScalarFormatter(useOffset=False, useMathText=True) - elif isinstance(fmt, str): - fmt = mpl.ticker.StrMethodFormatter(fmt) - fmt.create_dummy_axis() - - if prop == "colors": - if not hasarray: - warnings.warn("Collection without array used. Make sure to " - "specify the values to be colormapped via the " - "`c` argument.") - return handles, labels - u = np.unique(self.get_array()) - size = kwargs.pop("size", mpl.rcParams["lines.markersize"]) - elif prop == "sizes": - u = np.unique(self.get_sizes()) - color = kwargs.pop("color", "k") - else: - raise ValueError("Valid values for `prop` are 'colors' or " - f"'sizes'. You supplied '{prop}' instead.") - - fu = func(u) - fmt.axis.set_view_interval(fu.min(), fu.max()) - fmt.axis.set_data_interval(fu.min(), fu.max()) - if num == "auto": - num = 9 - if len(u) <= num: - num = None - if num is None: - values = u - label_values = func(values) - else: - if prop == "colors": - arr = self.get_array() - elif prop == "sizes": - arr = self.get_sizes() - if isinstance(num, mpl.ticker.Locator): - loc = num - elif np.iterable(num): - loc = mpl.ticker.FixedLocator(num) - else: - num = int(num) - loc = mpl.ticker.MaxNLocator(nbins=num, min_n_ticks=num-1, - steps=[1, 2, 2.5, 3, 5, 6, 8, 10]) - label_values = loc.tick_values(func(arr).min(), func(arr).max()) - cond = ((label_values >= func(arr).min()) & - (label_values <= func(arr).max())) - label_values = label_values[cond] - yarr = np.linspace(arr.min(), arr.max(), 256) - xarr = func(yarr) - ix = np.argsort(xarr) - values = np.interp(label_values, xarr[ix], yarr[ix]) - - kw = {"markeredgewidth": self.get_linewidths()[0], - "alpha": self.get_alpha(), - **kwargs} - - for val, lab in zip(values, label_values): - if prop == "colors": - color = self.cmap(self.norm(val)) - elif prop == "sizes": - size = np.sqrt(val) - if np.isclose(size, 0.0): - continue - h = mlines.Line2D([0], [0], ls="", color=color, ms=size, - marker=self.get_paths()[0], **kw) - handles.append(h) - if hasattr(fmt, "set_locs"): - fmt.set_locs(label_values) - l = fmt(lab) - labels.append(l) - - return handles, labels - - -class PolyCollection(_CollectionWithSizes): - - @_api.make_keyword_only("3.6", name="closed") - def __init__(self, verts, sizes=None, closed=True, **kwargs): - """ - Parameters - ---------- - verts : list of array-like - The sequence of polygons [*verts0*, *verts1*, ...] where each - element *verts_i* defines the vertices of polygon *i* as a 2D - array-like of shape (M, 2). - sizes : array-like, default: None - Squared scaling factors for the polygons. The coordinates of each - polygon *verts_i* are multiplied by the square-root of the - corresponding entry in *sizes* (i.e., *sizes* specify the scaling - of areas). The scaling is applied before the Artist master - transform. - closed : bool, default: True - Whether the polygon should be closed by adding a CLOSEPOLY - connection at the end. - **kwargs - Forwarded to `.Collection`. - """ - super().__init__(**kwargs) - self.set_sizes(sizes) - self.set_verts(verts, closed) - self.stale = True - - def set_verts(self, verts, closed=True): - """ - Set the vertices of the polygons. - - Parameters - ---------- - verts : list of array-like - The sequence of polygons [*verts0*, *verts1*, ...] where each - element *verts_i* defines the vertices of polygon *i* as a 2D - array-like of shape (M, 2). - closed : bool, default: True - Whether the polygon should be closed by adding a CLOSEPOLY - connection at the end. - """ - self.stale = True - if isinstance(verts, np.ma.MaskedArray): - verts = verts.astype(float).filled(np.nan) - - # No need to do anything fancy if the path isn't closed. - if not closed: - self._paths = [mpath.Path(xy) for xy in verts] - return - - # Fast path for arrays - if isinstance(verts, np.ndarray) and len(verts.shape) == 3: - verts_pad = np.concatenate((verts, verts[:, :1]), axis=1) - # Creating the codes once is much faster than having Path do it - # separately each time by passing closed=True. - codes = np.empty(verts_pad.shape[1], dtype=mpath.Path.code_type) - codes[:] = mpath.Path.LINETO - codes[0] = mpath.Path.MOVETO - codes[-1] = mpath.Path.CLOSEPOLY - self._paths = [mpath.Path(xy, codes) for xy in verts_pad] - return - - self._paths = [] - for xy in verts: - if len(xy): - self._paths.append(mpath.Path._create_closed(xy)) - else: - self._paths.append(mpath.Path(xy)) - - set_paths = set_verts - - def set_verts_and_codes(self, verts, codes): - """Initialize vertices with path codes.""" - if len(verts) != len(codes): - raise ValueError("'codes' must be a 1D list or array " - "with the same length of 'verts'") - self._paths = [mpath.Path(xy, cds) if len(xy) else mpath.Path(xy) - for xy, cds in zip(verts, codes)] - self.stale = True - - @classmethod - @_api.deprecated("3.7", alternative="fill_between") - def span_where(cls, x, ymin, ymax, where, **kwargs): - """ - Return a `.BrokenBarHCollection` that plots horizontal bars from - over the regions in *x* where *where* is True. The bars range - on the y-axis from *ymin* to *ymax* - - *kwargs* are passed on to the collection. - """ - xranges = [] - for ind0, ind1 in cbook.contiguous_regions(where): - xslice = x[ind0:ind1] - if not len(xslice): - continue - xranges.append((xslice[0], xslice[-1] - xslice[0])) - return BrokenBarHCollection(xranges, [ymin, ymax - ymin], **kwargs) - - -@_api.deprecated("3.7") -class BrokenBarHCollection(PolyCollection): - """ - A collection of horizontal bars spanning *yrange* with a sequence of - *xranges*. - """ - def __init__(self, xranges, yrange, **kwargs): - """ - Parameters - ---------- - xranges : list of (float, float) - The sequence of (left-edge-position, width) pairs for each bar. - yrange : (float, float) - The (lower-edge, height) common to all bars. - **kwargs - Forwarded to `.Collection`. - """ - ymin, ywidth = yrange - ymax = ymin + ywidth - verts = [[(xmin, ymin), - (xmin, ymax), - (xmin + xwidth, ymax), - (xmin + xwidth, ymin), - (xmin, ymin)] for xmin, xwidth in xranges] - super().__init__(verts, **kwargs) - - -class RegularPolyCollection(_CollectionWithSizes): - """A collection of n-sided regular polygons.""" - - _path_generator = mpath.Path.unit_regular_polygon - _factor = np.pi ** (-1/2) - - @_api.make_keyword_only("3.6", name="rotation") - def __init__(self, - numsides, - rotation=0, - sizes=(1,), - **kwargs): - """ - Parameters - ---------- - numsides : int - The number of sides of the polygon. - rotation : float - The rotation of the polygon in radians. - sizes : tuple of float - The area of the circle circumscribing the polygon in points^2. - **kwargs - Forwarded to `.Collection`. - - Examples - -------- - See :doc:`/gallery/event_handling/lasso_demo` for a complete example:: - - offsets = np.random.rand(20, 2) - facecolors = [cm.jet(x) for x in np.random.rand(20)] - - collection = RegularPolyCollection( - numsides=5, # a pentagon - rotation=0, sizes=(50,), - facecolors=facecolors, - edgecolors=("black",), - linewidths=(1,), - offsets=offsets, - offset_transform=ax.transData, - ) - """ - super().__init__(**kwargs) - self.set_sizes(sizes) - self._numsides = numsides - self._paths = [self._path_generator(numsides)] - self._rotation = rotation - self.set_transform(transforms.IdentityTransform()) - - def get_numsides(self): - return self._numsides - - def get_rotation(self): - return self._rotation - - @artist.allow_rasterization - def draw(self, renderer): - self.set_sizes(self._sizes, self.figure.dpi) - self._transforms = [ - transforms.Affine2D(x).rotate(-self._rotation).get_matrix() - for x in self._transforms - ] - # Explicitly not super().draw, because set_sizes must be called before - # updating self._transforms. - Collection.draw(self, renderer) - - -class StarPolygonCollection(RegularPolyCollection): - """Draw a collection of regular stars with *numsides* points.""" - _path_generator = mpath.Path.unit_regular_star - - -class AsteriskPolygonCollection(RegularPolyCollection): - """Draw a collection of regular asterisks with *numsides* points.""" - _path_generator = mpath.Path.unit_regular_asterisk - - -class LineCollection(Collection): - r""" - Represents a sequence of `.Line2D`\s that should be drawn together. - - This class extends `.Collection` to represent a sequence of - `.Line2D`\s instead of just a sequence of `.Patch`\s. - Just as in `.Collection`, each property of a *LineCollection* may be either - a single value or a list of values. This list is then used cyclically for - each element of the LineCollection, so the property of the ``i``\th element - of the collection is:: - - prop[i % len(prop)] - - The properties of each member of a *LineCollection* default to their values - in :rc:`lines.*` instead of :rc:`patch.*`, and the property *colors* is - added in place of *edgecolors*. - """ - - _edge_default = True - - def __init__(self, segments, # Can be None. - *, - zorder=2, # Collection.zorder is 1 - **kwargs - ): - """ - Parameters - ---------- - segments : list of array-like - A sequence of (*line0*, *line1*, *line2*), where:: - - linen = (x0, y0), (x1, y1), ... (xm, ym) - - or the equivalent numpy array with two columns. Each line - can have a different number of segments. - linewidths : float or list of float, default: :rc:`lines.linewidth` - The width of each line in points. - colors : color or list of color, default: :rc:`lines.color` - A sequence of RGBA tuples (e.g., arbitrary color strings, etc, not - allowed). - antialiaseds : bool or list of bool, default: :rc:`lines.antialiased` - Whether to use antialiasing for each line. - zorder : float, default: 2 - zorder of the lines once drawn. - - facecolors : color or list of color, default: 'none' - When setting *facecolors*, each line is interpreted as a boundary - for an area, implicitly closing the path from the last point to the - first point. The enclosed area is filled with *facecolor*. - In order to manually specify what should count as the "interior" of - each line, please use `.PathCollection` instead, where the - "interior" can be specified by appropriate usage of - `~.path.Path.CLOSEPOLY`. - - **kwargs - Forwarded to `.Collection`. - """ - # Unfortunately, mplot3d needs this explicit setting of 'facecolors'. - kwargs.setdefault('facecolors', 'none') - super().__init__( - zorder=zorder, - **kwargs) - self.set_segments(segments) - - def set_segments(self, segments): - if segments is None: - return - - self._paths = [mpath.Path(seg) if isinstance(seg, np.ma.MaskedArray) - else mpath.Path(np.asarray(seg, float)) - for seg in segments] - self.stale = True - - set_verts = set_segments # for compatibility with PolyCollection - set_paths = set_segments - - def get_segments(self): - """ - Returns - ------- - list - List of segments in the LineCollection. Each list item contains an - array of vertices. - """ - segments = [] - - for path in self._paths: - vertices = [ - vertex - for vertex, _ - # Never simplify here, we want to get the data-space values - # back and there in no way to know the "right" simplification - # threshold so never try. - in path.iter_segments(simplify=False) - ] - vertices = np.asarray(vertices) - segments.append(vertices) - - return segments - - def _get_default_linewidth(self): - return mpl.rcParams['lines.linewidth'] - - def _get_default_antialiased(self): - return mpl.rcParams['lines.antialiased'] - - def _get_default_edgecolor(self): - return mpl.rcParams['lines.color'] - - def _get_default_facecolor(self): - return 'none' - - def set_color(self, c): - """ - Set the edgecolor(s) of the LineCollection. - - Parameters - ---------- - c : color or list of colors - Single color (all lines have same color), or a - sequence of RGBA tuples; if it is a sequence the lines will - cycle through the sequence. - """ - self.set_edgecolor(c) - - set_colors = set_color - - def get_color(self): - return self._edgecolors - - get_colors = get_color # for compatibility with old versions - - -class EventCollection(LineCollection): - """ - A collection of locations along a single axis at which an "event" occurred. - - The events are given by a 1-dimensional array. They do not have an - amplitude and are displayed as parallel lines. - """ - - _edge_default = True - - @_api.make_keyword_only("3.6", name="lineoffset") - def __init__(self, - positions, # Cannot be None. - orientation='horizontal', - lineoffset=0, - linelength=1, - linewidth=None, - color=None, - linestyle='solid', - antialiased=None, - **kwargs - ): - """ - Parameters - ---------- - positions : 1D array-like - Each value is an event. - orientation : {'horizontal', 'vertical'}, default: 'horizontal' - The sequence of events is plotted along this direction. - The marker lines of the single events are along the orthogonal - direction. - lineoffset : float, default: 0 - The offset of the center of the markers from the origin, in the - direction orthogonal to *orientation*. - linelength : float, default: 1 - The total height of the marker (i.e. the marker stretches from - ``lineoffset - linelength/2`` to ``lineoffset + linelength/2``). - linewidth : float or list thereof, default: :rc:`lines.linewidth` - The line width of the event lines, in points. - color : color or list of colors, default: :rc:`lines.color` - The color of the event lines. - linestyle : str or tuple or list thereof, default: 'solid' - Valid strings are ['solid', 'dashed', 'dashdot', 'dotted', - '-', '--', '-.', ':']. Dash tuples should be of the form:: - - (offset, onoffseq), - - where *onoffseq* is an even length tuple of on and off ink - in points. - antialiased : bool or list thereof, default: :rc:`lines.antialiased` - Whether to use antialiasing for drawing the lines. - **kwargs - Forwarded to `.LineCollection`. - - Examples - -------- - .. plot:: gallery/lines_bars_and_markers/eventcollection_demo.py - """ - super().__init__([], - linewidths=linewidth, linestyles=linestyle, - colors=color, antialiaseds=antialiased, - **kwargs) - self._is_horizontal = True # Initial value, may be switched below. - self._linelength = linelength - self._lineoffset = lineoffset - self.set_orientation(orientation) - self.set_positions(positions) - - def get_positions(self): - """ - Return an array containing the floating-point values of the positions. - """ - pos = 0 if self.is_horizontal() else 1 - return [segment[0, pos] for segment in self.get_segments()] - - def set_positions(self, positions): - """Set the positions of the events.""" - if positions is None: - positions = [] - if np.ndim(positions) != 1: - raise ValueError('positions must be one-dimensional') - lineoffset = self.get_lineoffset() - linelength = self.get_linelength() - pos_idx = 0 if self.is_horizontal() else 1 - segments = np.empty((len(positions), 2, 2)) - segments[:, :, pos_idx] = np.sort(positions)[:, None] - segments[:, 0, 1 - pos_idx] = lineoffset + linelength / 2 - segments[:, 1, 1 - pos_idx] = lineoffset - linelength / 2 - self.set_segments(segments) - - def add_positions(self, position): - """Add one or more events at the specified positions.""" - if position is None or (hasattr(position, 'len') and - len(position) == 0): - return - positions = self.get_positions() - positions = np.hstack([positions, np.asanyarray(position)]) - self.set_positions(positions) - extend_positions = append_positions = add_positions - - def is_horizontal(self): - """True if the eventcollection is horizontal, False if vertical.""" - return self._is_horizontal - - def get_orientation(self): - """ - Return the orientation of the event line ('horizontal' or 'vertical'). - """ - return 'horizontal' if self.is_horizontal() else 'vertical' - - def switch_orientation(self): - """ - Switch the orientation of the event line, either from vertical to - horizontal or vice versus. - """ - segments = self.get_segments() - for i, segment in enumerate(segments): - segments[i] = np.fliplr(segment) - self.set_segments(segments) - self._is_horizontal = not self.is_horizontal() - self.stale = True - - def set_orientation(self, orientation): - """ - Set the orientation of the event line. - - Parameters - ---------- - orientation : {'horizontal', 'vertical'} - """ - is_horizontal = _api.check_getitem( - {"horizontal": True, "vertical": False}, - orientation=orientation) - if is_horizontal == self.is_horizontal(): - return - self.switch_orientation() - - def get_linelength(self): - """Return the length of the lines used to mark each event.""" - return self._linelength - - def set_linelength(self, linelength): - """Set the length of the lines used to mark each event.""" - if linelength == self.get_linelength(): - return - lineoffset = self.get_lineoffset() - segments = self.get_segments() - pos = 1 if self.is_horizontal() else 0 - for segment in segments: - segment[0, pos] = lineoffset + linelength / 2. - segment[1, pos] = lineoffset - linelength / 2. - self.set_segments(segments) - self._linelength = linelength - - def get_lineoffset(self): - """Return the offset of the lines used to mark each event.""" - return self._lineoffset - - def set_lineoffset(self, lineoffset): - """Set the offset of the lines used to mark each event.""" - if lineoffset == self.get_lineoffset(): - return - linelength = self.get_linelength() - segments = self.get_segments() - pos = 1 if self.is_horizontal() else 0 - for segment in segments: - segment[0, pos] = lineoffset + linelength / 2. - segment[1, pos] = lineoffset - linelength / 2. - self.set_segments(segments) - self._lineoffset = lineoffset - - def get_linewidth(self): - """Get the width of the lines used to mark each event.""" - return super().get_linewidth()[0] - - def get_linewidths(self): - return super().get_linewidth() - - def get_color(self): - """Return the color of the lines used to mark each event.""" - return self.get_colors()[0] - - -class CircleCollection(_CollectionWithSizes): - """A collection of circles, drawn using splines.""" - - _factor = np.pi ** (-1/2) - - def __init__(self, sizes, **kwargs): - """ - Parameters - ---------- - sizes : float or array-like - The area of each circle in points^2. - **kwargs - Forwarded to `.Collection`. - """ - super().__init__(**kwargs) - self.set_sizes(sizes) - self.set_transform(transforms.IdentityTransform()) - self._paths = [mpath.Path.unit_circle()] - - -class EllipseCollection(Collection): - """A collection of ellipses, drawn using splines.""" - - @_api.make_keyword_only("3.6", name="units") - def __init__(self, widths, heights, angles, units='points', **kwargs): - """ - Parameters - ---------- - widths : array-like - The lengths of the first axes (e.g., major axis lengths). - heights : array-like - The lengths of second axes. - angles : array-like - The angles of the first axes, degrees CCW from the x-axis. - units : {'points', 'inches', 'dots', 'width', 'height', 'x', 'y', 'xy'} - The units in which majors and minors are given; 'width' and - 'height' refer to the dimensions of the axes, while 'x' and 'y' - refer to the *offsets* data units. 'xy' differs from all others in - that the angle as plotted varies with the aspect ratio, and equals - the specified angle only when the aspect ratio is unity. Hence - it behaves the same as the `~.patches.Ellipse` with - ``axes.transData`` as its transform. - **kwargs - Forwarded to `Collection`. - """ - super().__init__(**kwargs) - self._widths = 0.5 * np.asarray(widths).ravel() - self._heights = 0.5 * np.asarray(heights).ravel() - self._angles = np.deg2rad(angles).ravel() - self._units = units - self.set_transform(transforms.IdentityTransform()) - self._transforms = np.empty((0, 3, 3)) - self._paths = [mpath.Path.unit_circle()] - - def _set_transforms(self): - """Calculate transforms immediately before drawing.""" - - ax = self.axes - fig = self.figure - - if self._units == 'xy': - sc = 1 - elif self._units == 'x': - sc = ax.bbox.width / ax.viewLim.width - elif self._units == 'y': - sc = ax.bbox.height / ax.viewLim.height - elif self._units == 'inches': - sc = fig.dpi - elif self._units == 'points': - sc = fig.dpi / 72.0 - elif self._units == 'width': - sc = ax.bbox.width - elif self._units == 'height': - sc = ax.bbox.height - elif self._units == 'dots': - sc = 1.0 - else: - raise ValueError(f'Unrecognized units: {self._units!r}') - - self._transforms = np.zeros((len(self._widths), 3, 3)) - widths = self._widths * sc - heights = self._heights * sc - sin_angle = np.sin(self._angles) - cos_angle = np.cos(self._angles) - self._transforms[:, 0, 0] = widths * cos_angle - self._transforms[:, 0, 1] = heights * -sin_angle - self._transforms[:, 1, 0] = widths * sin_angle - self._transforms[:, 1, 1] = heights * cos_angle - self._transforms[:, 2, 2] = 1.0 - - _affine = transforms.Affine2D - if self._units == 'xy': - m = ax.transData.get_affine().get_matrix().copy() - m[:2, 2:] = 0 - self.set_transform(_affine(m)) - - @artist.allow_rasterization - def draw(self, renderer): - self._set_transforms() - super().draw(renderer) - - -class PatchCollection(Collection): - """ - A generic collection of patches. - - PatchCollection draws faster than a large number of equivalent individual - Patches. It also makes it easier to assign a colormap to a heterogeneous - collection of patches. - """ - - @_api.make_keyword_only("3.6", name="match_original") - def __init__(self, patches, match_original=False, **kwargs): - """ - Parameters - ---------- - patches : list of `.Patch` - A sequence of Patch objects. This list may include - a heterogeneous assortment of different patch types. - - match_original : bool, default: False - If True, use the colors and linewidths of the original - patches. If False, new colors may be assigned by - providing the standard collection arguments, facecolor, - edgecolor, linewidths, norm or cmap. - - **kwargs - All other parameters are forwarded to `.Collection`. - - If any of *edgecolors*, *facecolors*, *linewidths*, *antialiaseds* - are None, they default to their `.rcParams` patch setting, in - sequence form. - - Notes - ----- - The use of `~matplotlib.cm.ScalarMappable` functionality is optional. - If the `~matplotlib.cm.ScalarMappable` matrix ``_A`` has been set (via - a call to `~.ScalarMappable.set_array`), at draw time a call to scalar - mappable will be made to set the face colors. - """ - - if match_original: - def determine_facecolor(patch): - if patch.get_fill(): - return patch.get_facecolor() - return [0, 0, 0, 0] - - kwargs['facecolors'] = [determine_facecolor(p) for p in patches] - kwargs['edgecolors'] = [p.get_edgecolor() for p in patches] - kwargs['linewidths'] = [p.get_linewidth() for p in patches] - kwargs['linestyles'] = [p.get_linestyle() for p in patches] - kwargs['antialiaseds'] = [p.get_antialiased() for p in patches] - - super().__init__(**kwargs) - - self.set_paths(patches) - - def set_paths(self, patches): - paths = [p.get_transform().transform_path(p.get_path()) - for p in patches] - self._paths = paths - - -class TriMesh(Collection): - """ - Class for the efficient drawing of a triangular mesh using Gouraud shading. - - A triangular mesh is a `~matplotlib.tri.Triangulation` object. - """ - def __init__(self, triangulation, **kwargs): - super().__init__(**kwargs) - self._triangulation = triangulation - self._shading = 'gouraud' - - self._bbox = transforms.Bbox.unit() - - # Unfortunately this requires a copy, unless Triangulation - # was rewritten. - xy = np.hstack((triangulation.x.reshape(-1, 1), - triangulation.y.reshape(-1, 1))) - self._bbox.update_from_data_xy(xy) - - def get_paths(self): - if self._paths is None: - self.set_paths() - return self._paths - - def set_paths(self): - self._paths = self.convert_mesh_to_paths(self._triangulation) - - @staticmethod - def convert_mesh_to_paths(tri): - """ - Convert a given mesh into a sequence of `.Path` objects. - - This function is primarily of use to implementers of backends that do - not directly support meshes. - """ - triangles = tri.get_masked_triangles() - verts = np.stack((tri.x[triangles], tri.y[triangles]), axis=-1) - return [mpath.Path(x) for x in verts] - - @artist.allow_rasterization - def draw(self, renderer): - if not self.get_visible(): - return - renderer.open_group(self.__class__.__name__, gid=self.get_gid()) - transform = self.get_transform() - - # Get a list of triangles and the color at each vertex. - tri = self._triangulation - triangles = tri.get_masked_triangles() - - verts = np.stack((tri.x[triangles], tri.y[triangles]), axis=-1) - - self.update_scalarmappable() - colors = self._facecolors[triangles] - - gc = renderer.new_gc() - self._set_gc_clip(gc) - gc.set_linewidth(self.get_linewidth()[0]) - renderer.draw_gouraud_triangles(gc, verts, colors, transform.frozen()) - gc.restore() - renderer.close_group(self.__class__.__name__) - - -class QuadMesh(Collection): - r""" - Class for the efficient drawing of a quadrilateral mesh. - - A quadrilateral mesh is a grid of M by N adjacent quadrilaterals that are - defined via a (M+1, N+1) grid of vertices. The quadrilateral (m, n) is - defined by the vertices :: - - (m+1, n) ----------- (m+1, n+1) - / / - / / - / / - (m, n) -------- (m, n+1) - - The mesh need not be regular and the polygons need not be convex. - - Parameters - ---------- - coordinates : (M+1, N+1, 2) array-like - The vertices. ``coordinates[m, n]`` specifies the (x, y) coordinates - of vertex (m, n). - - antialiased : bool, default: True - - shading : {'flat', 'gouraud'}, default: 'flat' - - Notes - ----- - Unlike other `.Collection`\s, the default *pickradius* of `.QuadMesh` is 0, - i.e. `~.Artist.contains` checks whether the test point is within any of the - mesh quadrilaterals. - - """ - - def __init__(self, coordinates, *, antialiased=True, shading='flat', - **kwargs): - kwargs.setdefault("pickradius", 0) - # end of signature deprecation code - - _api.check_shape((None, None, 2), coordinates=coordinates) - self._coordinates = coordinates - self._antialiased = antialiased - self._shading = shading - self._bbox = transforms.Bbox.unit() - self._bbox.update_from_data_xy(self._coordinates.reshape(-1, 2)) - # super init delayed after own init because array kwarg requires - # self._coordinates and self._shading - super().__init__(**kwargs) - self.set_mouseover(False) - - def get_paths(self): - if self._paths is None: - self.set_paths() - return self._paths - - def set_paths(self): - self._paths = self._convert_mesh_to_paths(self._coordinates) - self.stale = True - - def set_array(self, A): - """ - Set the data values. - - Parameters - ---------- - A : array-like - The mesh data. Supported array shapes are: - - - (M, N) or (M*N,): a mesh with scalar data. The values are mapped - to colors using normalization and a colormap. See parameters - *norm*, *cmap*, *vmin*, *vmax*. - - (M, N, 3): an image with RGB values (0-1 float or 0-255 int). - - (M, N, 4): an image with RGBA values (0-1 float or 0-255 int), - i.e. including transparency. - - If the values are provided as a 2D grid, the shape must match the - coordinates grid. If the values are 1D, they are reshaped to 2D. - M, N follow from the coordinates grid, where the coordinates grid - shape is (M, N) for 'gouraud' *shading* and (M+1, N+1) for 'flat' - shading. - """ - height, width = self._coordinates.shape[0:-1] - if self._shading == 'flat': - h, w = height - 1, width - 1 - else: - h, w = height, width - ok_shapes = [(h, w, 3), (h, w, 4), (h, w), (h * w,)] - if A is not None: - shape = np.shape(A) - if shape not in ok_shapes: - raise ValueError( - f"For X ({width}) and Y ({height}) with {self._shading} " - f"shading, A should have shape " - f"{' or '.join(map(str, ok_shapes))}, not {A.shape}") - return super().set_array(A) - - def get_datalim(self, transData): - return (self.get_transform() - transData).transform_bbox(self._bbox) - - def get_coordinates(self): - """ - Return the vertices of the mesh as an (M+1, N+1, 2) array. - - M, N are the number of quadrilaterals in the rows / columns of the - mesh, corresponding to (M+1, N+1) vertices. - The last dimension specifies the components (x, y). - """ - return self._coordinates - - @staticmethod - def _convert_mesh_to_paths(coordinates): - """ - Convert a given mesh into a sequence of `.Path` objects. - - This function is primarily of use to implementers of backends that do - not directly support quadmeshes. - """ - if isinstance(coordinates, np.ma.MaskedArray): - c = coordinates.data - else: - c = coordinates - points = np.concatenate([ - c[:-1, :-1], - c[:-1, 1:], - c[1:, 1:], - c[1:, :-1], - c[:-1, :-1] - ], axis=2).reshape((-1, 5, 2)) - return [mpath.Path(x) for x in points] - - def _convert_mesh_to_triangles(self, coordinates): - """ - Convert a given mesh into a sequence of triangles, each point - with its own color. The result can be used to construct a call to - `~.RendererBase.draw_gouraud_triangles`. - """ - if isinstance(coordinates, np.ma.MaskedArray): - p = coordinates.data - else: - p = coordinates - - p_a = p[:-1, :-1] - p_b = p[:-1, 1:] - p_c = p[1:, 1:] - p_d = p[1:, :-1] - p_center = (p_a + p_b + p_c + p_d) / 4.0 - triangles = np.concatenate([ - p_a, p_b, p_center, - p_b, p_c, p_center, - p_c, p_d, p_center, - p_d, p_a, p_center, - ], axis=2).reshape((-1, 3, 2)) - - c = self.get_facecolor().reshape((*coordinates.shape[:2], 4)) - c_a = c[:-1, :-1] - c_b = c[:-1, 1:] - c_c = c[1:, 1:] - c_d = c[1:, :-1] - c_center = (c_a + c_b + c_c + c_d) / 4.0 - colors = np.concatenate([ - c_a, c_b, c_center, - c_b, c_c, c_center, - c_c, c_d, c_center, - c_d, c_a, c_center, - ], axis=2).reshape((-1, 3, 4)) - - return triangles, colors - - @artist.allow_rasterization - def draw(self, renderer): - if not self.get_visible(): - return - renderer.open_group(self.__class__.__name__, self.get_gid()) - transform = self.get_transform() - offset_trf = self.get_offset_transform() - offsets = self.get_offsets() - - if self.have_units(): - xs = self.convert_xunits(offsets[:, 0]) - ys = self.convert_yunits(offsets[:, 1]) - offsets = np.column_stack([xs, ys]) - - self.update_scalarmappable() - - if not transform.is_affine: - coordinates = self._coordinates.reshape((-1, 2)) - coordinates = transform.transform(coordinates) - coordinates = coordinates.reshape(self._coordinates.shape) - transform = transforms.IdentityTransform() - else: - coordinates = self._coordinates - - if not offset_trf.is_affine: - offsets = offset_trf.transform_non_affine(offsets) - offset_trf = offset_trf.get_affine() - - gc = renderer.new_gc() - gc.set_snap(self.get_snap()) - self._set_gc_clip(gc) - gc.set_linewidth(self.get_linewidth()[0]) - - if self._shading == 'gouraud': - triangles, colors = self._convert_mesh_to_triangles(coordinates) - renderer.draw_gouraud_triangles( - gc, triangles, colors, transform.frozen()) - else: - renderer.draw_quad_mesh( - gc, transform.frozen(), - coordinates.shape[1] - 1, coordinates.shape[0] - 1, - coordinates, offsets, offset_trf, - # Backends expect flattened rgba arrays (n*m, 4) for fc and ec - self.get_facecolor().reshape((-1, 4)), - self._antialiased, self.get_edgecolors().reshape((-1, 4))) - gc.restore() - renderer.close_group(self.__class__.__name__) - self.stale = False - - def get_cursor_data(self, event): - contained, info = self.contains(event) - if contained and self.get_array() is not None: - return self.get_array().ravel()[info["ind"]] - return None diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/colorbar.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/colorbar.py deleted file mode 100644 index c9726a6..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/colorbar.py +++ /dev/null @@ -1,1594 +0,0 @@ -""" -Colorbars are a visualization of the mapping from scalar values to colors. -In Matplotlib they are drawn into a dedicated `~.axes.Axes`. - -.. note:: - Colorbars are typically created through `.Figure.colorbar` or its pyplot - wrapper `.pyplot.colorbar`, which internally use `.Colorbar` together with - `.make_axes_gridspec` (for `.GridSpec`-positioned axes) or `.make_axes` (for - non-`.GridSpec`-positioned axes). - - End-users most likely won't need to directly use this module's API. -""" - -import logging - -import numpy as np - -import matplotlib as mpl -from matplotlib import _api, cbook, collections, cm, colors, contour, ticker -import matplotlib.artist as martist -import matplotlib.patches as mpatches -import matplotlib.path as mpath -import matplotlib.spines as mspines -import matplotlib.transforms as mtransforms -from matplotlib import _docstring - -_log = logging.getLogger(__name__) - -_docstring.interpd.update( - _make_axes_kw_doc=""" -location : None or {'left', 'right', 'top', 'bottom'} - The location, relative to the parent axes, where the colorbar axes - is created. It also determines the *orientation* of the colorbar - (colorbars on the left and right are vertical, colorbars at the top - and bottom are horizontal). If None, the location will come from the - *orientation* if it is set (vertical colorbars on the right, horizontal - ones at the bottom), or default to 'right' if *orientation* is unset. - -orientation : None or {'vertical', 'horizontal'} - The orientation of the colorbar. It is preferable to set the *location* - of the colorbar, as that also determines the *orientation*; passing - incompatible values for *location* and *orientation* raises an exception. - -fraction : float, default: 0.15 - Fraction of original axes to use for colorbar. - -shrink : float, default: 1.0 - Fraction by which to multiply the size of the colorbar. - -aspect : float, default: 20 - Ratio of long to short dimensions. - -pad : float, default: 0.05 if vertical, 0.15 if horizontal - Fraction of original axes between colorbar and new image axes. - -anchor : (float, float), optional - The anchor point of the colorbar axes. - Defaults to (0.0, 0.5) if vertical; (0.5, 1.0) if horizontal. - -panchor : (float, float), or *False*, optional - The anchor point of the colorbar parent axes. If *False*, the parent - axes' anchor will be unchanged. - Defaults to (1.0, 0.5) if vertical; (0.5, 0.0) if horizontal.""", - _colormap_kw_doc=""" -extend : {'neither', 'both', 'min', 'max'} - Make pointed end(s) for out-of-range values (unless 'neither'). These are - set for a given colormap using the colormap set_under and set_over methods. - -extendfrac : {*None*, 'auto', length, lengths} - If set to *None*, both the minimum and maximum triangular colorbar - extensions will have a length of 5% of the interior colorbar length (this - is the default setting). - - If set to 'auto', makes the triangular colorbar extensions the same lengths - as the interior boxes (when *spacing* is set to 'uniform') or the same - lengths as the respective adjacent interior boxes (when *spacing* is set to - 'proportional'). - - If a scalar, indicates the length of both the minimum and maximum - triangular colorbar extensions as a fraction of the interior colorbar - length. A two-element sequence of fractions may also be given, indicating - the lengths of the minimum and maximum colorbar extensions respectively as - a fraction of the interior colorbar length. - -extendrect : bool - If *False* the minimum and maximum colorbar extensions will be triangular - (the default). If *True* the extensions will be rectangular. - -spacing : {'uniform', 'proportional'} - For discrete colorbars (`.BoundaryNorm` or contours), 'uniform' gives each - color the same space; 'proportional' makes the space proportional to the - data interval. - -ticks : None or list of ticks or Locator - If None, ticks are determined automatically from the input. - -format : None or str or Formatter - If None, `~.ticker.ScalarFormatter` is used. - Format strings, e.g., ``"%4.2e"`` or ``"{x:.2e}"``, are supported. - An alternative `~.ticker.Formatter` may be given instead. - -drawedges : bool - Whether to draw lines at color boundaries. - -label : str - The label on the colorbar's long axis. - -boundaries, values : None or a sequence - If unset, the colormap will be displayed on a 0-1 scale. - If sequences, *values* must have a length 1 less than *boundaries*. For - each region delimited by adjacent entries in *boundaries*, the color mapped - to the corresponding value in values will be used. - Normally only useful for indexed colors (i.e. ``norm=NoNorm()``) or other - unusual circumstances.""") - - -def _set_ticks_on_axis_warn(*args, **kwargs): - # a top level function which gets put in at the axes' - # set_xticks and set_yticks by Colorbar.__init__. - _api.warn_external("Use the colorbar set_ticks() method instead.") - - -class _ColorbarSpine(mspines.Spine): - def __init__(self, axes): - self._ax = axes - super().__init__(axes, 'colorbar', mpath.Path(np.empty((0, 2)))) - mpatches.Patch.set_transform(self, axes.transAxes) - - def get_window_extent(self, renderer=None): - # This Spine has no Axis associated with it, and doesn't need to adjust - # its location, so we can directly get the window extent from the - # super-super-class. - return mpatches.Patch.get_window_extent(self, renderer=renderer) - - def set_xy(self, xy): - self._path = mpath.Path(xy, closed=True) - self._xy = xy - self.stale = True - - def draw(self, renderer): - ret = mpatches.Patch.draw(self, renderer) - self.stale = False - return ret - - -class _ColorbarAxesLocator: - """ - Shrink the axes if there are triangular or rectangular extends. - """ - def __init__(self, cbar): - self._cbar = cbar - self._orig_locator = cbar.ax._axes_locator - - def __call__(self, ax, renderer): - if self._orig_locator is not None: - pos = self._orig_locator(ax, renderer) - else: - pos = ax.get_position(original=True) - if self._cbar.extend == 'neither': - return pos - - y, extendlen = self._cbar._proportional_y() - if not self._cbar._extend_lower(): - extendlen[0] = 0 - if not self._cbar._extend_upper(): - extendlen[1] = 0 - len = sum(extendlen) + 1 - shrink = 1 / len - offset = extendlen[0] / len - # we need to reset the aspect ratio of the axes to account - # of the extends... - if hasattr(ax, '_colorbar_info'): - aspect = ax._colorbar_info['aspect'] - else: - aspect = False - # now shrink and/or offset to take into account the - # extend tri/rectangles. - if self._cbar.orientation == 'vertical': - if aspect: - self._cbar.ax.set_box_aspect(aspect*shrink) - pos = pos.shrunk(1, shrink).translated(0, offset * pos.height) - else: - if aspect: - self._cbar.ax.set_box_aspect(1/(aspect * shrink)) - pos = pos.shrunk(shrink, 1).translated(offset * pos.width, 0) - return pos - - def get_subplotspec(self): - # make tight_layout happy.. - return ( - self._cbar.ax.get_subplotspec() - or getattr(self._orig_locator, "get_subplotspec", lambda: None)()) - - -@_docstring.interpd -class Colorbar: - r""" - Draw a colorbar in an existing axes. - - Typically, colorbars are created using `.Figure.colorbar` or - `.pyplot.colorbar` and associated with `.ScalarMappable`\s (such as an - `.AxesImage` generated via `~.axes.Axes.imshow`). - - In order to draw a colorbar not associated with other elements in the - figure, e.g. when showing a colormap by itself, one can create an empty - `.ScalarMappable`, or directly pass *cmap* and *norm* instead of *mappable* - to `Colorbar`. - - Useful public methods are :meth:`set_label` and :meth:`add_lines`. - - Attributes - ---------- - ax : `~matplotlib.axes.Axes` - The `~.axes.Axes` instance in which the colorbar is drawn. - lines : list - A list of `.LineCollection` (empty if no lines were drawn). - dividers : `.LineCollection` - A LineCollection (empty if *drawedges* is ``False``). - - Parameters - ---------- - ax : `~matplotlib.axes.Axes` - The `~.axes.Axes` instance in which the colorbar is drawn. - - mappable : `.ScalarMappable` - The mappable whose colormap and norm will be used. - - To show the under- and over- value colors, the mappable's norm should - be specified as :: - - norm = colors.Normalize(clip=False) - - To show the colors versus index instead of on a 0-1 scale, use:: - - norm=colors.NoNorm() - - cmap : `~matplotlib.colors.Colormap`, default: :rc:`image.cmap` - The colormap to use. This parameter is ignored, unless *mappable* is - None. - - norm : `~matplotlib.colors.Normalize` - The normalization to use. This parameter is ignored, unless *mappable* - is None. - - alpha : float - The colorbar transparency between 0 (transparent) and 1 (opaque). - - orientation : None or {'vertical', 'horizontal'} - If None, use the value determined by *location*. If both - *orientation* and *location* are None then defaults to 'vertical'. - - ticklocation : {'auto', 'left', 'right', 'top', 'bottom'} - The location of the colorbar ticks. The *ticklocation* must match - *orientation*. For example, a horizontal colorbar can only have ticks - at the top or the bottom. If 'auto', the ticks will be the same as - *location*, so a colorbar to the left will have ticks to the left. If - *location* is None, the ticks will be at the bottom for a horizontal - colorbar and at the right for a vertical. - - drawedges : bool - Whether to draw lines at color boundaries. - - filled : bool - - %(_colormap_kw_doc)s - - location : None or {'left', 'right', 'top', 'bottom'} - Set the *orientation* and *ticklocation* of the colorbar using a - single argument. Colorbars on the left and right are vertical, - colorbars at the top and bottom are horizontal. The *ticklocation* is - the same as *location*, so if *location* is 'top', the ticks are on - the top. *orientation* and/or *ticklocation* can be provided as well - and overrides the value set by *location*, but there will be an error - for incompatible combinations. - - .. versionadded:: 3.7 - """ - - n_rasterize = 50 # rasterize solids if number of colors >= n_rasterize - - @_api.delete_parameter("3.6", "filled") - def __init__(self, ax, mappable=None, *, cmap=None, - norm=None, - alpha=None, - values=None, - boundaries=None, - orientation=None, - ticklocation='auto', - extend=None, - spacing='uniform', # uniform or proportional - ticks=None, - format=None, - drawedges=False, - filled=True, - extendfrac=None, - extendrect=False, - label='', - location=None, - ): - - if mappable is None: - mappable = cm.ScalarMappable(norm=norm, cmap=cmap) - - # Ensure the given mappable's norm has appropriate vmin and vmax - # set even if mappable.draw has not yet been called. - if mappable.get_array() is not None: - mappable.autoscale_None() - - self.mappable = mappable - cmap = mappable.cmap - norm = mappable.norm - - if isinstance(mappable, contour.ContourSet): - cs = mappable - alpha = cs.get_alpha() - boundaries = cs._levels - values = cs.cvalues - extend = cs.extend - filled = cs.filled - if ticks is None: - ticks = ticker.FixedLocator(cs.levels, nbins=10) - elif isinstance(mappable, martist.Artist): - alpha = mappable.get_alpha() - - mappable.colorbar = self - mappable.colorbar_cid = mappable.callbacks.connect( - 'changed', self.update_normal) - - location_orientation = _get_orientation_from_location(location) - - _api.check_in_list( - [None, 'vertical', 'horizontal'], orientation=orientation) - _api.check_in_list( - ['auto', 'left', 'right', 'top', 'bottom'], - ticklocation=ticklocation) - _api.check_in_list( - ['uniform', 'proportional'], spacing=spacing) - - if location_orientation is not None and orientation is not None: - if location_orientation != orientation: - raise TypeError( - "location and orientation are mutually exclusive") - else: - orientation = orientation or location_orientation or "vertical" - - self.ax = ax - self.ax._axes_locator = _ColorbarAxesLocator(self) - - if extend is None: - if (not isinstance(mappable, contour.ContourSet) - and getattr(cmap, 'colorbar_extend', False) is not False): - extend = cmap.colorbar_extend - elif hasattr(norm, 'extend'): - extend = norm.extend - else: - extend = 'neither' - self.alpha = None - # Call set_alpha to handle array-like alphas properly - self.set_alpha(alpha) - self.cmap = cmap - self.norm = norm - self.values = values - self.boundaries = boundaries - self.extend = extend - self._inside = _api.check_getitem( - {'neither': slice(0, None), 'both': slice(1, -1), - 'min': slice(1, None), 'max': slice(0, -1)}, - extend=extend) - self.spacing = spacing - self.orientation = orientation - self.drawedges = drawedges - self._filled = filled - self.extendfrac = extendfrac - self.extendrect = extendrect - self._extend_patches = [] - self.solids = None - self.solids_patches = [] - self.lines = [] - - for spine in self.ax.spines.values(): - spine.set_visible(False) - self.outline = self.ax.spines['outline'] = _ColorbarSpine(self.ax) - - self.dividers = collections.LineCollection( - [], - colors=[mpl.rcParams['axes.edgecolor']], - linewidths=[0.5 * mpl.rcParams['axes.linewidth']], - clip_on=False) - self.ax.add_collection(self.dividers) - - self._locator = None - self._minorlocator = None - self._formatter = None - self._minorformatter = None - - if ticklocation == 'auto': - ticklocation = _get_ticklocation_from_orientation( - orientation) if location is None else location - self.ticklocation = ticklocation - - self.set_label(label) - self._reset_locator_formatter_scale() - - if np.iterable(ticks): - self._locator = ticker.FixedLocator(ticks, nbins=len(ticks)) - else: - self._locator = ticks - - if isinstance(format, str): - # Check format between FormatStrFormatter and StrMethodFormatter - try: - self._formatter = ticker.FormatStrFormatter(format) - _ = self._formatter(0) - except TypeError: - self._formatter = ticker.StrMethodFormatter(format) - else: - self._formatter = format # Assume it is a Formatter or None - self._draw_all() - - if isinstance(mappable, contour.ContourSet) and not mappable.filled: - self.add_lines(mappable) - - # Link the Axes and Colorbar for interactive use - self.ax._colorbar = self - # Don't navigate on any of these types of mappables - if (isinstance(self.norm, (colors.BoundaryNorm, colors.NoNorm)) or - isinstance(self.mappable, contour.ContourSet)): - self.ax.set_navigate(False) - - # These are the functions that set up interactivity on this colorbar - self._interactive_funcs = ["_get_view", "_set_view", - "_set_view_from_bbox", "drag_pan"] - for x in self._interactive_funcs: - setattr(self.ax, x, getattr(self, x)) - # Set the cla function to the cbar's method to override it - self.ax.cla = self._cbar_cla - # Callbacks for the extend calculations to handle inverting the axis - self._extend_cid1 = self.ax.callbacks.connect( - "xlim_changed", self._do_extends) - self._extend_cid2 = self.ax.callbacks.connect( - "ylim_changed", self._do_extends) - - @property - def locator(self): - """Major tick `.Locator` for the colorbar.""" - return self._long_axis().get_major_locator() - - @locator.setter - def locator(self, loc): - self._long_axis().set_major_locator(loc) - self._locator = loc - - @property - def minorlocator(self): - """Minor tick `.Locator` for the colorbar.""" - return self._long_axis().get_minor_locator() - - @minorlocator.setter - def minorlocator(self, loc): - self._long_axis().set_minor_locator(loc) - self._minorlocator = loc - - @property - def formatter(self): - """Major tick label `.Formatter` for the colorbar.""" - return self._long_axis().get_major_formatter() - - @formatter.setter - def formatter(self, fmt): - self._long_axis().set_major_formatter(fmt) - self._formatter = fmt - - @property - def minorformatter(self): - """Minor tick `.Formatter` for the colorbar.""" - return self._long_axis().get_minor_formatter() - - @minorformatter.setter - def minorformatter(self, fmt): - self._long_axis().set_minor_formatter(fmt) - self._minorformatter = fmt - - def _cbar_cla(self): - """Function to clear the interactive colorbar state.""" - for x in self._interactive_funcs: - delattr(self.ax, x) - # We now restore the old cla() back and can call it directly - del self.ax.cla - self.ax.cla() - - filled = _api.deprecate_privatize_attribute("3.6") - - def update_normal(self, mappable): - """ - Update solid patches, lines, etc. - - This is meant to be called when the norm of the image or contour plot - to which this colorbar belongs changes. - - If the norm on the mappable is different than before, this resets the - locator and formatter for the axis, so if these have been customized, - they will need to be customized again. However, if the norm only - changes values of *vmin*, *vmax* or *cmap* then the old formatter - and locator will be preserved. - """ - _log.debug('colorbar update normal %r %r', mappable.norm, self.norm) - self.mappable = mappable - self.set_alpha(mappable.get_alpha()) - self.cmap = mappable.cmap - if mappable.norm != self.norm: - self.norm = mappable.norm - self._reset_locator_formatter_scale() - - self._draw_all() - if isinstance(self.mappable, contour.ContourSet): - CS = self.mappable - if not CS.filled: - self.add_lines(CS) - self.stale = True - - @_api.deprecated("3.6", alternative="fig.draw_without_rendering()") - def draw_all(self): - """ - Calculate any free parameters based on the current cmap and norm, - and do all the drawing. - """ - self._draw_all() - - def _draw_all(self): - """ - Calculate any free parameters based on the current cmap and norm, - and do all the drawing. - """ - if self.orientation == 'vertical': - if mpl.rcParams['ytick.minor.visible']: - self.minorticks_on() - else: - if mpl.rcParams['xtick.minor.visible']: - self.minorticks_on() - self._long_axis().set(label_position=self.ticklocation, - ticks_position=self.ticklocation) - self._short_axis().set_ticks([]) - self._short_axis().set_ticks([], minor=True) - - # Set self._boundaries and self._values, including extensions. - # self._boundaries are the edges of each square of color, and - # self._values are the value to map into the norm to get the - # color: - self._process_values() - # Set self.vmin and self.vmax to first and last boundary, excluding - # extensions: - self.vmin, self.vmax = self._boundaries[self._inside][[0, -1]] - # Compute the X/Y mesh. - X, Y = self._mesh() - # draw the extend triangles, and shrink the inner axes to accommodate. - # also adds the outline path to self.outline spine: - self._do_extends() - lower, upper = self.vmin, self.vmax - if self._long_axis().get_inverted(): - # If the axis is inverted, we need to swap the vmin/vmax - lower, upper = upper, lower - if self.orientation == 'vertical': - self.ax.set_xlim(0, 1) - self.ax.set_ylim(lower, upper) - else: - self.ax.set_ylim(0, 1) - self.ax.set_xlim(lower, upper) - - # set up the tick locators and formatters. A bit complicated because - # boundary norms + uniform spacing requires a manual locator. - self.update_ticks() - - if self._filled: - ind = np.arange(len(self._values)) - if self._extend_lower(): - ind = ind[1:] - if self._extend_upper(): - ind = ind[:-1] - self._add_solids(X, Y, self._values[ind, np.newaxis]) - - def _add_solids(self, X, Y, C): - """Draw the colors; optionally add separators.""" - # Cleanup previously set artists. - if self.solids is not None: - self.solids.remove() - for solid in self.solids_patches: - solid.remove() - # Add new artist(s), based on mappable type. Use individual patches if - # hatching is needed, pcolormesh otherwise. - mappable = getattr(self, 'mappable', None) - if (isinstance(mappable, contour.ContourSet) - and any(hatch is not None for hatch in mappable.hatches)): - self._add_solids_patches(X, Y, C, mappable) - else: - self.solids = self.ax.pcolormesh( - X, Y, C, cmap=self.cmap, norm=self.norm, alpha=self.alpha, - edgecolors='none', shading='flat') - if not self.drawedges: - if len(self._y) >= self.n_rasterize: - self.solids.set_rasterized(True) - self._update_dividers() - - def _update_dividers(self): - if not self.drawedges: - self.dividers.set_segments([]) - return - # Place all *internal* dividers. - if self.orientation == 'vertical': - lims = self.ax.get_ylim() - bounds = (lims[0] < self._y) & (self._y < lims[1]) - else: - lims = self.ax.get_xlim() - bounds = (lims[0] < self._y) & (self._y < lims[1]) - y = self._y[bounds] - # And then add outer dividers if extensions are on. - if self._extend_lower(): - y = np.insert(y, 0, lims[0]) - if self._extend_upper(): - y = np.append(y, lims[1]) - X, Y = np.meshgrid([0, 1], y) - if self.orientation == 'vertical': - segments = np.dstack([X, Y]) - else: - segments = np.dstack([Y, X]) - self.dividers.set_segments(segments) - - def _add_solids_patches(self, X, Y, C, mappable): - hatches = mappable.hatches * (len(C) + 1) # Have enough hatches. - if self._extend_lower(): - # remove first hatch that goes into the extend patch - hatches = hatches[1:] - patches = [] - for i in range(len(X) - 1): - xy = np.array([[X[i, 0], Y[i, 1]], - [X[i, 1], Y[i, 0]], - [X[i + 1, 1], Y[i + 1, 0]], - [X[i + 1, 0], Y[i + 1, 1]]]) - patch = mpatches.PathPatch(mpath.Path(xy), - facecolor=self.cmap(self.norm(C[i][0])), - hatch=hatches[i], linewidth=0, - antialiased=False, alpha=self.alpha) - self.ax.add_patch(patch) - patches.append(patch) - self.solids_patches = patches - - def _do_extends(self, ax=None): - """ - Add the extend tri/rectangles on the outside of the axes. - - ax is unused, but required due to the callbacks on xlim/ylim changed - """ - # Clean up any previous extend patches - for patch in self._extend_patches: - patch.remove() - self._extend_patches = [] - # extend lengths are fraction of the *inner* part of colorbar, - # not the total colorbar: - _, extendlen = self._proportional_y() - bot = 0 - (extendlen[0] if self._extend_lower() else 0) - top = 1 + (extendlen[1] if self._extend_upper() else 0) - - # xyout is the outline of the colorbar including the extend patches: - if not self.extendrect: - # triangle: - xyout = np.array([[0, 0], [0.5, bot], [1, 0], - [1, 1], [0.5, top], [0, 1], [0, 0]]) - else: - # rectangle: - xyout = np.array([[0, 0], [0, bot], [1, bot], [1, 0], - [1, 1], [1, top], [0, top], [0, 1], - [0, 0]]) - - if self.orientation == 'horizontal': - xyout = xyout[:, ::-1] - - # xyout is the path for the spine: - self.outline.set_xy(xyout) - if not self._filled: - return - - # Make extend triangles or rectangles filled patches. These are - # defined in the outer parent axes' coordinates: - mappable = getattr(self, 'mappable', None) - if (isinstance(mappable, contour.ContourSet) - and any(hatch is not None for hatch in mappable.hatches)): - hatches = mappable.hatches * (len(self._y) + 1) - else: - hatches = [None] * (len(self._y) + 1) - - if self._extend_lower(): - if not self.extendrect: - # triangle - xy = np.array([[0, 0], [0.5, bot], [1, 0]]) - else: - # rectangle - xy = np.array([[0, 0], [0, bot], [1., bot], [1, 0]]) - if self.orientation == 'horizontal': - xy = xy[:, ::-1] - # add the patch - val = -1 if self._long_axis().get_inverted() else 0 - color = self.cmap(self.norm(self._values[val])) - patch = mpatches.PathPatch( - mpath.Path(xy), facecolor=color, alpha=self.alpha, - linewidth=0, antialiased=False, - transform=self.ax.transAxes, - hatch=hatches[0], clip_on=False, - # Place it right behind the standard patches, which is - # needed if we updated the extends - zorder=np.nextafter(self.ax.patch.zorder, -np.inf)) - self.ax.add_patch(patch) - self._extend_patches.append(patch) - # remove first hatch that goes into the extend patch - hatches = hatches[1:] - if self._extend_upper(): - if not self.extendrect: - # triangle - xy = np.array([[0, 1], [0.5, top], [1, 1]]) - else: - # rectangle - xy = np.array([[0, 1], [0, top], [1, top], [1, 1]]) - if self.orientation == 'horizontal': - xy = xy[:, ::-1] - # add the patch - val = 0 if self._long_axis().get_inverted() else -1 - color = self.cmap(self.norm(self._values[val])) - hatch_idx = len(self._y) - 1 - patch = mpatches.PathPatch( - mpath.Path(xy), facecolor=color, alpha=self.alpha, - linewidth=0, antialiased=False, - transform=self.ax.transAxes, hatch=hatches[hatch_idx], - clip_on=False, - # Place it right behind the standard patches, which is - # needed if we updated the extends - zorder=np.nextafter(self.ax.patch.zorder, -np.inf)) - self.ax.add_patch(patch) - self._extend_patches.append(patch) - - self._update_dividers() - - def add_lines(self, *args, **kwargs): - """ - Draw lines on the colorbar. - - The lines are appended to the list :attr:`lines`. - - Parameters - ---------- - levels : array-like - The positions of the lines. - colors : color or list of colors - Either a single color applying to all lines or one color value for - each line. - linewidths : float or array-like - Either a single linewidth applying to all lines or one linewidth - for each line. - erase : bool, default: True - Whether to remove any previously added lines. - - Notes - ----- - Alternatively, this method can also be called with the signature - ``colorbar.add_lines(contour_set, erase=True)``, in which case - *levels*, *colors*, and *linewidths* are taken from *contour_set*. - """ - params = _api.select_matching_signature( - [lambda self, CS, erase=True: locals(), - lambda self, levels, colors, linewidths, erase=True: locals()], - self, *args, **kwargs) - if "CS" in params: - self, CS, erase = params.values() - if not isinstance(CS, contour.ContourSet) or CS.filled: - raise ValueError("If a single artist is passed to add_lines, " - "it must be a ContourSet of lines") - # TODO: Make colorbar lines auto-follow changes in contour lines. - return self.add_lines( - CS.levels, - [c[0] for c in CS.tcolors], - [t[0] for t in CS.tlinewidths], - erase=erase) - else: - self, levels, colors, linewidths, erase = params.values() - - y = self._locate(levels) - rtol = (self._y[-1] - self._y[0]) * 1e-10 - igood = (y < self._y[-1] + rtol) & (y > self._y[0] - rtol) - y = y[igood] - if np.iterable(colors): - colors = np.asarray(colors)[igood] - if np.iterable(linewidths): - linewidths = np.asarray(linewidths)[igood] - X, Y = np.meshgrid([0, 1], y) - if self.orientation == 'vertical': - xy = np.stack([X, Y], axis=-1) - else: - xy = np.stack([Y, X], axis=-1) - col = collections.LineCollection(xy, linewidths=linewidths, - colors=colors) - - if erase and self.lines: - for lc in self.lines: - lc.remove() - self.lines = [] - self.lines.append(col) - - # make a clip path that is just a linewidth bigger than the axes... - fac = np.max(linewidths) / 72 - xy = np.array([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]) - inches = self.ax.get_figure().dpi_scale_trans - # do in inches: - xy = inches.inverted().transform(self.ax.transAxes.transform(xy)) - xy[[0, 1, 4], 1] -= fac - xy[[2, 3], 1] += fac - # back to axes units... - xy = self.ax.transAxes.inverted().transform(inches.transform(xy)) - col.set_clip_path(mpath.Path(xy, closed=True), - self.ax.transAxes) - self.ax.add_collection(col) - self.stale = True - - def update_ticks(self): - """ - Set up the ticks and ticklabels. This should not be needed by users. - """ - # Get the locator and formatter; defaults to self._locator if not None. - self._get_ticker_locator_formatter() - self._long_axis().set_major_locator(self._locator) - self._long_axis().set_minor_locator(self._minorlocator) - self._long_axis().set_major_formatter(self._formatter) - - def _get_ticker_locator_formatter(self): - """ - Return the ``locator`` and ``formatter`` of the colorbar. - - If they have not been defined (i.e. are *None*), the formatter and - locator are retrieved from the axis, or from the value of the - boundaries for a boundary norm. - - Called by update_ticks... - """ - locator = self._locator - formatter = self._formatter - minorlocator = self._minorlocator - if isinstance(self.norm, colors.BoundaryNorm): - b = self.norm.boundaries - if locator is None: - locator = ticker.FixedLocator(b, nbins=10) - if minorlocator is None: - minorlocator = ticker.FixedLocator(b) - elif isinstance(self.norm, colors.NoNorm): - if locator is None: - # put ticks on integers between the boundaries of NoNorm - nv = len(self._values) - base = 1 + int(nv / 10) - locator = ticker.IndexLocator(base=base, offset=.5) - elif self.boundaries is not None: - b = self._boundaries[self._inside] - if locator is None: - locator = ticker.FixedLocator(b, nbins=10) - else: # most cases: - if locator is None: - # we haven't set the locator explicitly, so use the default - # for this axis: - locator = self._long_axis().get_major_locator() - if minorlocator is None: - minorlocator = self._long_axis().get_minor_locator() - - if minorlocator is None: - minorlocator = ticker.NullLocator() - - if formatter is None: - formatter = self._long_axis().get_major_formatter() - - self._locator = locator - self._formatter = formatter - self._minorlocator = minorlocator - _log.debug('locator: %r', locator) - - def set_ticks(self, ticks, *, labels=None, minor=False, **kwargs): - """ - Set tick locations. - - Parameters - ---------- - ticks : list of floats - List of tick locations. - labels : list of str, optional - List of tick labels. If not set, the labels show the data value. - minor : bool, default: False - If ``False``, set the major ticks; if ``True``, the minor ticks. - **kwargs - `.Text` properties for the labels. These take effect only if you - pass *labels*. In other cases, please use `~.Axes.tick_params`. - """ - if np.iterable(ticks): - self._long_axis().set_ticks(ticks, labels=labels, minor=minor, - **kwargs) - self._locator = self._long_axis().get_major_locator() - else: - self._locator = ticks - self._long_axis().set_major_locator(self._locator) - self.stale = True - - def get_ticks(self, minor=False): - """ - Return the ticks as a list of locations. - - Parameters - ---------- - minor : boolean, default: False - if True return the minor ticks. - """ - if minor: - return self._long_axis().get_minorticklocs() - else: - return self._long_axis().get_majorticklocs() - - def set_ticklabels(self, ticklabels, *, minor=False, **kwargs): - """ - [*Discouraged*] Set tick labels. - - .. admonition:: Discouraged - - The use of this method is discouraged, because of the dependency - on tick positions. In most cases, you'll want to use - ``set_ticks(positions, labels=labels)`` instead. - - If you are using this method, you should always fix the tick - positions before, e.g. by using `.Colorbar.set_ticks` or by - explicitly setting a `~.ticker.FixedLocator` on the long axis - of the colorbar. Otherwise, ticks are free to move and the - labels may end up in unexpected positions. - - Parameters - ---------- - ticklabels : sequence of str or of `.Text` - Texts for labeling each tick location in the sequence set by - `.Colorbar.set_ticks`; the number of labels must match the number - of locations. - - update_ticks : bool, default: True - This keyword argument is ignored and will be removed. - Deprecated - - minor : bool - If True, set minor ticks instead of major ticks. - - **kwargs - `.Text` properties for the labels. - """ - self._long_axis().set_ticklabels(ticklabels, minor=minor, **kwargs) - - def minorticks_on(self): - """ - Turn on colorbar minor ticks. - """ - self.ax.minorticks_on() - self._short_axis().set_minor_locator(ticker.NullLocator()) - - def minorticks_off(self): - """Turn the minor ticks of the colorbar off.""" - self._minorlocator = ticker.NullLocator() - self._long_axis().set_minor_locator(self._minorlocator) - - def set_label(self, label, *, loc=None, **kwargs): - """ - Add a label to the long axis of the colorbar. - - Parameters - ---------- - label : str - The label text. - loc : str, optional - The location of the label. - - - For horizontal orientation one of {'left', 'center', 'right'} - - For vertical orientation one of {'bottom', 'center', 'top'} - - Defaults to :rc:`xaxis.labellocation` or :rc:`yaxis.labellocation` - depending on the orientation. - **kwargs - Keyword arguments are passed to `~.Axes.set_xlabel` / - `~.Axes.set_ylabel`. - Supported keywords are *labelpad* and `.Text` properties. - """ - if self.orientation == "vertical": - self.ax.set_ylabel(label, loc=loc, **kwargs) - else: - self.ax.set_xlabel(label, loc=loc, **kwargs) - self.stale = True - - def set_alpha(self, alpha): - """ - Set the transparency between 0 (transparent) and 1 (opaque). - - If an array is provided, *alpha* will be set to None to use the - transparency values associated with the colormap. - """ - self.alpha = None if isinstance(alpha, np.ndarray) else alpha - - def _set_scale(self, scale, **kwargs): - """ - Set the colorbar long axis scale. - - Parameters - ---------- - scale : {"linear", "log", "symlog", "logit", ...} or `.ScaleBase` - The axis scale type to apply. - - **kwargs - Different keyword arguments are accepted, depending on the scale. - See the respective class keyword arguments: - - - `matplotlib.scale.LinearScale` - - `matplotlib.scale.LogScale` - - `matplotlib.scale.SymmetricalLogScale` - - `matplotlib.scale.LogitScale` - - `matplotlib.scale.FuncScale` - - Notes - ----- - By default, Matplotlib supports the above-mentioned scales. - Additionally, custom scales may be registered using - `matplotlib.scale.register_scale`. These scales can then also - be used here. - """ - self._long_axis()._set_axes_scale(scale, **kwargs) - - def remove(self): - """ - Remove this colorbar from the figure. - - If the colorbar was created with ``use_gridspec=True`` the previous - gridspec is restored. - """ - if hasattr(self.ax, '_colorbar_info'): - parents = self.ax._colorbar_info['parents'] - for a in parents: - if self.ax in a._colorbars: - a._colorbars.remove(self.ax) - - self.ax.remove() - - self.mappable.callbacks.disconnect(self.mappable.colorbar_cid) - self.mappable.colorbar = None - self.mappable.colorbar_cid = None - # Remove the extension callbacks - self.ax.callbacks.disconnect(self._extend_cid1) - self.ax.callbacks.disconnect(self._extend_cid2) - - try: - ax = self.mappable.axes - except AttributeError: - return - try: - gs = ax.get_subplotspec().get_gridspec() - subplotspec = gs.get_topmost_subplotspec() - except AttributeError: - # use_gridspec was False - pos = ax.get_position(original=True) - ax._set_position(pos) - else: - # use_gridspec was True - ax.set_subplotspec(subplotspec) - - def _process_values(self): - """ - Set `_boundaries` and `_values` based on the self.boundaries and - self.values if not None, or based on the size of the colormap and - the vmin/vmax of the norm. - """ - if self.values is not None: - # set self._boundaries from the values... - self._values = np.array(self.values) - if self.boundaries is None: - # bracket values by 1/2 dv: - b = np.zeros(len(self.values) + 1) - b[1:-1] = 0.5 * (self._values[:-1] + self._values[1:]) - b[0] = 2.0 * b[1] - b[2] - b[-1] = 2.0 * b[-2] - b[-3] - self._boundaries = b - return - self._boundaries = np.array(self.boundaries) - return - - # otherwise values are set from the boundaries - if isinstance(self.norm, colors.BoundaryNorm): - b = self.norm.boundaries - elif isinstance(self.norm, colors.NoNorm): - # NoNorm has N blocks, so N+1 boundaries, centered on integers: - b = np.arange(self.cmap.N + 1) - .5 - elif self.boundaries is not None: - b = self.boundaries - else: - # otherwise make the boundaries from the size of the cmap: - N = self.cmap.N + 1 - b, _ = self._uniform_y(N) - # add extra boundaries if needed: - if self._extend_lower(): - b = np.hstack((b[0] - 1, b)) - if self._extend_upper(): - b = np.hstack((b, b[-1] + 1)) - - # transform from 0-1 to vmin-vmax: - if not self.norm.scaled(): - self.norm.vmin = 0 - self.norm.vmax = 1 - self.norm.vmin, self.norm.vmax = mtransforms.nonsingular( - self.norm.vmin, self.norm.vmax, expander=0.1) - if (not isinstance(self.norm, colors.BoundaryNorm) and - (self.boundaries is None)): - b = self.norm.inverse(b) - - self._boundaries = np.asarray(b, dtype=float) - self._values = 0.5 * (self._boundaries[:-1] + self._boundaries[1:]) - if isinstance(self.norm, colors.NoNorm): - self._values = (self._values + 0.00001).astype(np.int16) - - def _mesh(self): - """ - Return the coordinate arrays for the colorbar pcolormesh/patches. - - These are scaled between vmin and vmax, and already handle colorbar - orientation. - """ - y, _ = self._proportional_y() - # Use the vmin and vmax of the colorbar, which may not be the same - # as the norm. There are situations where the colormap has a - # narrower range than the colorbar and we want to accommodate the - # extra contours. - if (isinstance(self.norm, (colors.BoundaryNorm, colors.NoNorm)) - or self.boundaries is not None): - # not using a norm. - y = y * (self.vmax - self.vmin) + self.vmin - else: - # Update the norm values in a context manager as it is only - # a temporary change and we don't want to propagate any signals - # attached to the norm (callbacks.blocked). - with self.norm.callbacks.blocked(), \ - cbook._setattr_cm(self.norm, - vmin=self.vmin, - vmax=self.vmax): - y = self.norm.inverse(y) - self._y = y - X, Y = np.meshgrid([0., 1.], y) - if self.orientation == 'vertical': - return (X, Y) - else: - return (Y, X) - - def _forward_boundaries(self, x): - # map boundaries equally between 0 and 1... - b = self._boundaries - y = np.interp(x, b, np.linspace(0, 1, len(b))) - # the following avoids ticks in the extends: - eps = (b[-1] - b[0]) * 1e-6 - # map these _well_ out of bounds to keep any ticks out - # of the extends region... - y[x < b[0]-eps] = -1 - y[x > b[-1]+eps] = 2 - return y - - def _inverse_boundaries(self, x): - # invert the above... - b = self._boundaries - return np.interp(x, np.linspace(0, 1, len(b)), b) - - def _reset_locator_formatter_scale(self): - """ - Reset the locator et al to defaults. Any user-hardcoded changes - need to be re-entered if this gets called (either at init, or when - the mappable normal gets changed: Colorbar.update_normal) - """ - self._process_values() - self._locator = None - self._minorlocator = None - self._formatter = None - self._minorformatter = None - if (isinstance(self.mappable, contour.ContourSet) and - isinstance(self.norm, colors.LogNorm)): - # if contours have lognorm, give them a log scale... - self._set_scale('log') - elif (self.boundaries is not None or - isinstance(self.norm, colors.BoundaryNorm)): - if self.spacing == 'uniform': - funcs = (self._forward_boundaries, self._inverse_boundaries) - self._set_scale('function', functions=funcs) - elif self.spacing == 'proportional': - self._set_scale('linear') - elif getattr(self.norm, '_scale', None): - # use the norm's scale (if it exists and is not None): - self._set_scale(self.norm._scale) - elif type(self.norm) is colors.Normalize: - # plain Normalize: - self._set_scale('linear') - else: - # norm._scale is None or not an attr: derive the scale from - # the Norm: - funcs = (self.norm, self.norm.inverse) - self._set_scale('function', functions=funcs) - - def _locate(self, x): - """ - Given a set of color data values, return their - corresponding colorbar data coordinates. - """ - if isinstance(self.norm, (colors.NoNorm, colors.BoundaryNorm)): - b = self._boundaries - xn = x - else: - # Do calculations using normalized coordinates so - # as to make the interpolation more accurate. - b = self.norm(self._boundaries, clip=False).filled() - xn = self.norm(x, clip=False).filled() - - bunique = b[self._inside] - yunique = self._y - - z = np.interp(xn, bunique, yunique) - return z - - # trivial helpers - - def _uniform_y(self, N): - """ - Return colorbar data coordinates for *N* uniformly - spaced boundaries, plus extension lengths if required. - """ - automin = automax = 1. / (N - 1.) - extendlength = self._get_extension_lengths(self.extendfrac, - automin, automax, - default=0.05) - y = np.linspace(0, 1, N) - return y, extendlength - - def _proportional_y(self): - """ - Return colorbar data coordinates for the boundaries of - a proportional colorbar, plus extension lengths if required: - """ - if (isinstance(self.norm, colors.BoundaryNorm) or - self.boundaries is not None): - y = (self._boundaries - self._boundaries[self._inside][0]) - y = y / (self._boundaries[self._inside][-1] - - self._boundaries[self._inside][0]) - # need yscaled the same as the axes scale to get - # the extend lengths. - if self.spacing == 'uniform': - yscaled = self._forward_boundaries(self._boundaries) - else: - yscaled = y - else: - y = self.norm(self._boundaries.copy()) - y = np.ma.filled(y, np.nan) - # the norm and the scale should be the same... - yscaled = y - y = y[self._inside] - yscaled = yscaled[self._inside] - # normalize from 0..1: - norm = colors.Normalize(y[0], y[-1]) - y = np.ma.filled(norm(y), np.nan) - norm = colors.Normalize(yscaled[0], yscaled[-1]) - yscaled = np.ma.filled(norm(yscaled), np.nan) - # make the lower and upper extend lengths proportional to the lengths - # of the first and last boundary spacing (if extendfrac='auto'): - automin = yscaled[1] - yscaled[0] - automax = yscaled[-1] - yscaled[-2] - extendlength = [0, 0] - if self._extend_lower() or self._extend_upper(): - extendlength = self._get_extension_lengths( - self.extendfrac, automin, automax, default=0.05) - return y, extendlength - - def _get_extension_lengths(self, frac, automin, automax, default=0.05): - """ - Return the lengths of colorbar extensions. - - This is a helper method for _uniform_y and _proportional_y. - """ - # Set the default value. - extendlength = np.array([default, default]) - if isinstance(frac, str): - _api.check_in_list(['auto'], extendfrac=frac.lower()) - # Use the provided values when 'auto' is required. - extendlength[:] = [automin, automax] - elif frac is not None: - try: - # Try to set min and max extension fractions directly. - extendlength[:] = frac - # If frac is a sequence containing None then NaN may - # be encountered. This is an error. - if np.isnan(extendlength).any(): - raise ValueError() - except (TypeError, ValueError) as err: - # Raise an error on encountering an invalid value for frac. - raise ValueError('invalid value for extendfrac') from err - return extendlength - - def _extend_lower(self): - """Return whether the lower limit is open ended.""" - minmax = "max" if self._long_axis().get_inverted() else "min" - return self.extend in ('both', minmax) - - def _extend_upper(self): - """Return whether the upper limit is open ended.""" - minmax = "min" if self._long_axis().get_inverted() else "max" - return self.extend in ('both', minmax) - - def _long_axis(self): - """Return the long axis""" - if self.orientation == 'vertical': - return self.ax.yaxis - return self.ax.xaxis - - def _short_axis(self): - """Return the short axis""" - if self.orientation == 'vertical': - return self.ax.xaxis - return self.ax.yaxis - - def _get_view(self): - # docstring inherited - # An interactive view for a colorbar is the norm's vmin/vmax - return self.norm.vmin, self.norm.vmax - - def _set_view(self, view): - # docstring inherited - # An interactive view for a colorbar is the norm's vmin/vmax - self.norm.vmin, self.norm.vmax = view - - def _set_view_from_bbox(self, bbox, direction='in', - mode=None, twinx=False, twiny=False): - # docstring inherited - # For colorbars, we use the zoom bbox to scale the norm's vmin/vmax - new_xbound, new_ybound = self.ax._prepare_view_from_bbox( - bbox, direction=direction, mode=mode, twinx=twinx, twiny=twiny) - if self.orientation == 'horizontal': - self.norm.vmin, self.norm.vmax = new_xbound - elif self.orientation == 'vertical': - self.norm.vmin, self.norm.vmax = new_ybound - - def drag_pan(self, button, key, x, y): - # docstring inherited - points = self.ax._get_pan_points(button, key, x, y) - if points is not None: - if self.orientation == 'horizontal': - self.norm.vmin, self.norm.vmax = points[:, 0] - elif self.orientation == 'vertical': - self.norm.vmin, self.norm.vmax = points[:, 1] - - -ColorbarBase = Colorbar # Backcompat API - - -def _normalize_location_orientation(location, orientation): - if location is None: - location = _get_ticklocation_from_orientation(orientation) - loc_settings = _api.check_getitem({ - "left": {"location": "left", "anchor": (1.0, 0.5), - "panchor": (0.0, 0.5), "pad": 0.10}, - "right": {"location": "right", "anchor": (0.0, 0.5), - "panchor": (1.0, 0.5), "pad": 0.05}, - "top": {"location": "top", "anchor": (0.5, 0.0), - "panchor": (0.5, 1.0), "pad": 0.05}, - "bottom": {"location": "bottom", "anchor": (0.5, 1.0), - "panchor": (0.5, 0.0), "pad": 0.15}, - }, location=location) - loc_settings["orientation"] = _get_orientation_from_location(location) - if orientation is not None and orientation != loc_settings["orientation"]: - # Allow the user to pass both if they are consistent. - raise TypeError("location and orientation are mutually exclusive") - return loc_settings - - -def _get_orientation_from_location(location): - return _api.check_getitem( - {None: None, "left": "vertical", "right": "vertical", - "top": "horizontal", "bottom": "horizontal"}, location=location) - - -def _get_ticklocation_from_orientation(orientation): - return _api.check_getitem( - {None: "right", "vertical": "right", "horizontal": "bottom"}, - orientation=orientation) - - -@_docstring.interpd -def make_axes(parents, location=None, orientation=None, fraction=0.15, - shrink=1.0, aspect=20, **kwargs): - """ - Create an `~.axes.Axes` suitable for a colorbar. - - The axes is placed in the figure of the *parents* axes, by resizing and - repositioning *parents*. - - Parameters - ---------- - parents : `~matplotlib.axes.Axes` or iterable or `numpy.ndarray` of `~.axes.Axes` - The Axes to use as parents for placing the colorbar. - %(_make_axes_kw_doc)s - - Returns - ------- - cax : `~matplotlib.axes.Axes` - The child axes. - kwargs : dict - The reduced keyword dictionary to be passed when creating the colorbar - instance. - """ - loc_settings = _normalize_location_orientation(location, orientation) - # put appropriate values into the kwargs dict for passing back to - # the Colorbar class - kwargs['orientation'] = loc_settings['orientation'] - location = kwargs['ticklocation'] = loc_settings['location'] - - anchor = kwargs.pop('anchor', loc_settings['anchor']) - panchor = kwargs.pop('panchor', loc_settings['panchor']) - aspect0 = aspect - # turn parents into a list if it is not already. Note we cannot - # use .flatten or .ravel as these copy the references rather than - # reuse them, leading to a memory leak - if isinstance(parents, np.ndarray): - parents = list(parents.flat) - elif np.iterable(parents): - parents = list(parents) - else: - parents = [parents] - - fig = parents[0].get_figure() - - pad0 = 0.05 if fig.get_constrained_layout() else loc_settings['pad'] - pad = kwargs.pop('pad', pad0) - - if not all(fig is ax.get_figure() for ax in parents): - raise ValueError('Unable to create a colorbar axes as not all ' - 'parents share the same figure.') - - # take a bounding box around all of the given axes - parents_bbox = mtransforms.Bbox.union( - [ax.get_position(original=True).frozen() for ax in parents]) - - pb = parents_bbox - if location in ('left', 'right'): - if location == 'left': - pbcb, _, pb1 = pb.splitx(fraction, fraction + pad) - else: - pb1, _, pbcb = pb.splitx(1 - fraction - pad, 1 - fraction) - pbcb = pbcb.shrunk(1.0, shrink).anchored(anchor, pbcb) - else: - if location == 'bottom': - pbcb, _, pb1 = pb.splity(fraction, fraction + pad) - else: - pb1, _, pbcb = pb.splity(1 - fraction - pad, 1 - fraction) - pbcb = pbcb.shrunk(shrink, 1.0).anchored(anchor, pbcb) - - # define the aspect ratio in terms of y's per x rather than x's per y - aspect = 1.0 / aspect - - # define a transform which takes us from old axes coordinates to - # new axes coordinates - shrinking_trans = mtransforms.BboxTransform(parents_bbox, pb1) - - # transform each of the axes in parents using the new transform - for ax in parents: - new_posn = shrinking_trans.transform(ax.get_position(original=True)) - new_posn = mtransforms.Bbox(new_posn) - ax._set_position(new_posn) - if panchor is not False: - ax.set_anchor(panchor) - - cax = fig.add_axes(pbcb, label="") - for a in parents: - # tell the parent it has a colorbar - a._colorbars += [cax] - cax._colorbar_info = dict( - parents=parents, - location=location, - shrink=shrink, - anchor=anchor, - panchor=panchor, - fraction=fraction, - aspect=aspect0, - pad=pad) - # and we need to set the aspect ratio by hand... - cax.set_anchor(anchor) - cax.set_box_aspect(aspect) - cax.set_aspect('auto') - - return cax, kwargs - - -@_docstring.interpd -def make_axes_gridspec(parent, *, location=None, orientation=None, - fraction=0.15, shrink=1.0, aspect=20, **kwargs): - """ - Create an `~.axes.Axes` suitable for a colorbar. - - The axes is placed in the figure of the *parent* axes, by resizing and - repositioning *parent*. - - This function is similar to `.make_axes` and mostly compatible with it. - Primary differences are - - - `.make_axes_gridspec` requires the *parent* to have a subplotspec. - - `.make_axes` positions the axes in figure coordinates; - `.make_axes_gridspec` positions it using a subplotspec. - - `.make_axes` updates the position of the parent. `.make_axes_gridspec` - replaces the parent gridspec with a new one. - - Parameters - ---------- - parent : `~matplotlib.axes.Axes` - The Axes to use as parent for placing the colorbar. - %(_make_axes_kw_doc)s - - Returns - ------- - cax : `~matplotlib.axes.Axes` - The child axes. - kwargs : dict - The reduced keyword dictionary to be passed when creating the colorbar - instance. - """ - - loc_settings = _normalize_location_orientation(location, orientation) - kwargs['orientation'] = loc_settings['orientation'] - location = kwargs['ticklocation'] = loc_settings['location'] - - aspect0 = aspect - anchor = kwargs.pop('anchor', loc_settings['anchor']) - panchor = kwargs.pop('panchor', loc_settings['panchor']) - pad = kwargs.pop('pad', loc_settings["pad"]) - wh_space = 2 * pad / (1 - pad) - - if location in ('left', 'right'): - # for shrinking - height_ratios = [ - (1-anchor[1])*(1-shrink), shrink, anchor[1]*(1-shrink)] - - if location == 'left': - gs = parent.get_subplotspec().subgridspec( - 1, 2, wspace=wh_space, - width_ratios=[fraction, 1-fraction-pad]) - ss_main = gs[1] - ss_cb = gs[0].subgridspec( - 3, 1, hspace=0, height_ratios=height_ratios)[1] - else: - gs = parent.get_subplotspec().subgridspec( - 1, 2, wspace=wh_space, - width_ratios=[1-fraction-pad, fraction]) - ss_main = gs[0] - ss_cb = gs[1].subgridspec( - 3, 1, hspace=0, height_ratios=height_ratios)[1] - else: - # for shrinking - width_ratios = [ - anchor[0]*(1-shrink), shrink, (1-anchor[0])*(1-shrink)] - - if location == 'bottom': - gs = parent.get_subplotspec().subgridspec( - 2, 1, hspace=wh_space, - height_ratios=[1-fraction-pad, fraction]) - ss_main = gs[0] - ss_cb = gs[1].subgridspec( - 1, 3, wspace=0, width_ratios=width_ratios)[1] - aspect = 1 / aspect - else: - gs = parent.get_subplotspec().subgridspec( - 2, 1, hspace=wh_space, - height_ratios=[fraction, 1-fraction-pad]) - ss_main = gs[1] - ss_cb = gs[0].subgridspec( - 1, 3, wspace=0, width_ratios=width_ratios)[1] - aspect = 1 / aspect - - parent.set_subplotspec(ss_main) - if panchor is not False: - parent.set_anchor(panchor) - - fig = parent.get_figure() - cax = fig.add_subplot(ss_cb, label="") - cax.set_anchor(anchor) - cax.set_box_aspect(aspect) - cax.set_aspect('auto') - cax._colorbar_info = dict( - location=location, - parents=[parent], - shrink=shrink, - anchor=anchor, - panchor=panchor, - fraction=fraction, - aspect=aspect0, - pad=pad) - - return cax, kwargs diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/colors.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/colors.py deleted file mode 100644 index a74650d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/colors.py +++ /dev/null @@ -1,2675 +0,0 @@ -""" -A module for converting numbers or color arguments to *RGB* or *RGBA*. - -*RGB* and *RGBA* are sequences of, respectively, 3 or 4 floats in the -range 0-1. - -This module includes functions and classes for color specification conversions, -and for mapping numbers to colors in a 1-D array of colors called a colormap. - -Mapping data onto colors using a colormap typically involves two steps: a data -array is first mapped onto the range 0-1 using a subclass of `Normalize`, -then this number is mapped to a color using a subclass of `Colormap`. Two -subclasses of `Colormap` provided here: `LinearSegmentedColormap`, which uses -piecewise-linear interpolation to define colormaps, and `ListedColormap`, which -makes a colormap from a list of colors. - -.. seealso:: - - :doc:`/tutorials/colors/colormap-manipulation` for examples of how to - make colormaps and - - :doc:`/tutorials/colors/colormaps` for a list of built-in colormaps. - - :doc:`/tutorials/colors/colormapnorms` for more details about data - normalization - - More colormaps are available at palettable_. - -The module also provides functions for checking whether an object can be -interpreted as a color (`is_color_like`), for converting such an object -to an RGBA tuple (`to_rgba`) or to an HTML-like hex string in the -"#rrggbb" format (`to_hex`), and a sequence of colors to an (n, 4) -RGBA array (`to_rgba_array`). Caching is used for efficiency. - -Colors that Matplotlib recognizes are listed at -:doc:`/tutorials/colors/colors`. - -.. _palettable: https://jiffyclub.github.io/palettable/ -.. _xkcd color survey: https://xkcd.com/color/rgb/ -""" - -import base64 -from collections.abc import Sized, Sequence, Mapping -import functools -import importlib -import inspect -import io -import itertools -from numbers import Number -import re -from PIL import Image -from PIL.PngImagePlugin import PngInfo - -import matplotlib as mpl -import numpy as np -from matplotlib import _api, _cm, cbook, scale -from ._color_data import BASE_COLORS, TABLEAU_COLORS, CSS4_COLORS, XKCD_COLORS - - -class _ColorMapping(dict): - def __init__(self, mapping): - super().__init__(mapping) - self.cache = {} - - def __setitem__(self, key, value): - super().__setitem__(key, value) - self.cache.clear() - - def __delitem__(self, key): - super().__delitem__(key) - self.cache.clear() - - -_colors_full_map = {} -# Set by reverse priority order. -_colors_full_map.update(XKCD_COLORS) -_colors_full_map.update({k.replace('grey', 'gray'): v - for k, v in XKCD_COLORS.items() - if 'grey' in k}) -_colors_full_map.update(CSS4_COLORS) -_colors_full_map.update(TABLEAU_COLORS) -_colors_full_map.update({k.replace('gray', 'grey'): v - for k, v in TABLEAU_COLORS.items() - if 'gray' in k}) -_colors_full_map.update(BASE_COLORS) -_colors_full_map = _ColorMapping(_colors_full_map) - -_REPR_PNG_SIZE = (512, 64) - - -def get_named_colors_mapping(): - """Return the global mapping of names to named colors.""" - return _colors_full_map - - -class ColorSequenceRegistry(Mapping): - r""" - Container for sequences of colors that are known to Matplotlib by name. - - The universal registry instance is `matplotlib.color_sequences`. There - should be no need for users to instantiate `.ColorSequenceRegistry` - themselves. - - Read access uses a dict-like interface mapping names to lists of colors:: - - import matplotlib as mpl - cmap = mpl.color_sequences['tab10'] - - The returned lists are copies, so that their modification does not change - the global definition of the color sequence. - - Additional color sequences can be added via - `.ColorSequenceRegistry.register`:: - - mpl.color_sequences.register('rgb', ['r', 'g', 'b']) - """ - - _BUILTIN_COLOR_SEQUENCES = { - 'tab10': _cm._tab10_data, - 'tab20': _cm._tab20_data, - 'tab20b': _cm._tab20b_data, - 'tab20c': _cm._tab20c_data, - 'Pastel1': _cm._Pastel1_data, - 'Pastel2': _cm._Pastel2_data, - 'Paired': _cm._Paired_data, - 'Accent': _cm._Accent_data, - 'Dark2': _cm._Dark2_data, - 'Set1': _cm._Set1_data, - 'Set2': _cm._Set1_data, - 'Set3': _cm._Set1_data, - } - - def __init__(self): - self._color_sequences = {**self._BUILTIN_COLOR_SEQUENCES} - - def __getitem__(self, item): - try: - return list(self._color_sequences[item]) - except KeyError: - raise KeyError(f"{item!r} is not a known color sequence name") - - def __iter__(self): - return iter(self._color_sequences) - - def __len__(self): - return len(self._color_sequences) - - def __str__(self): - return ('ColorSequenceRegistry; available colormaps:\n' + - ', '.join(f"'{name}'" for name in self)) - - def register(self, name, color_list): - """ - Register a new color sequence. - - The color sequence registry stores a copy of the given *color_list*, so - that future changes to the original list do not affect the registered - color sequence. Think of this as the registry taking a snapshot - of *color_list* at registration. - - Parameters - ---------- - name : str - The name for the color sequence. - - color_list : list of colors - An iterable returning valid Matplotlib colors when iterating over. - Note however that the returned color sequence will always be a - list regardless of the input type. - - """ - if name in self._BUILTIN_COLOR_SEQUENCES: - raise ValueError(f"{name!r} is a reserved name for a builtin " - "color sequence") - - color_list = list(color_list) # force copy and coerce type to list - for color in color_list: - try: - to_rgba(color) - except ValueError: - raise ValueError( - f"{color!r} is not a valid color specification") - - self._color_sequences[name] = color_list - - def unregister(self, name): - """ - Remove a sequence from the registry. - - You cannot remove built-in color sequences. - - If the name is not registered, returns with no error. - """ - if name in self._BUILTIN_COLOR_SEQUENCES: - raise ValueError( - f"Cannot unregister builtin color sequence {name!r}") - self._color_sequences.pop(name, None) - - -_color_sequences = ColorSequenceRegistry() - - -def _sanitize_extrema(ex): - if ex is None: - return ex - try: - ret = ex.item() - except AttributeError: - ret = float(ex) - return ret - - -def _is_nth_color(c): - """Return whether *c* can be interpreted as an item in the color cycle.""" - return isinstance(c, str) and re.match(r"\AC[0-9]+\Z", c) - - -def is_color_like(c): - """Return whether *c* can be interpreted as an RGB(A) color.""" - # Special-case nth color syntax because it cannot be parsed during setup. - if _is_nth_color(c): - return True - try: - to_rgba(c) - except ValueError: - return False - else: - return True - - -def _has_alpha_channel(c): - """Return whether *c* is a color with an alpha channel.""" - # 4-element sequences are interpreted as r, g, b, a - return not isinstance(c, str) and len(c) == 4 - - -def _check_color_like(**kwargs): - """ - For each *key, value* pair in *kwargs*, check that *value* is color-like. - """ - for k, v in kwargs.items(): - if not is_color_like(v): - raise ValueError(f"{v!r} is not a valid value for {k}") - - -def same_color(c1, c2): - """ - Return whether the colors *c1* and *c2* are the same. - - *c1*, *c2* can be single colors or lists/arrays of colors. - """ - c1 = to_rgba_array(c1) - c2 = to_rgba_array(c2) - n1 = max(c1.shape[0], 1) # 'none' results in shape (0, 4), but is 1-elem - n2 = max(c2.shape[0], 1) # 'none' results in shape (0, 4), but is 1-elem - - if n1 != n2: - raise ValueError('Different number of elements passed.') - # The following shape test is needed to correctly handle comparisons with - # 'none', which results in a shape (0, 4) array and thus cannot be tested - # via value comparison. - return c1.shape == c2.shape and (c1 == c2).all() - - -def to_rgba(c, alpha=None): - """ - Convert *c* to an RGBA color. - - Parameters - ---------- - c : Matplotlib color or ``np.ma.masked`` - - alpha : float, optional - If *alpha* is given, force the alpha value of the returned RGBA tuple - to *alpha*. - - If None, the alpha value from *c* is used. If *c* does not have an - alpha channel, then alpha defaults to 1. - - *alpha* is ignored for the color value ``"none"`` (case-insensitive), - which always maps to ``(0, 0, 0, 0)``. - - Returns - ------- - tuple - Tuple of floats ``(r, g, b, a)``, where each channel (red, green, blue, - alpha) can assume values between 0 and 1. - """ - # Special-case nth color syntax because it should not be cached. - if _is_nth_color(c): - prop_cycler = mpl.rcParams['axes.prop_cycle'] - colors = prop_cycler.by_key().get('color', ['k']) - c = colors[int(c[1:]) % len(colors)] - try: - rgba = _colors_full_map.cache[c, alpha] - except (KeyError, TypeError): # Not in cache, or unhashable. - rgba = None - if rgba is None: # Suppress exception chaining of cache lookup failure. - rgba = _to_rgba_no_colorcycle(c, alpha) - try: - _colors_full_map.cache[c, alpha] = rgba - except TypeError: - pass - return rgba - - -def _to_rgba_no_colorcycle(c, alpha=None): - """ - Convert *c* to an RGBA color, with no support for color-cycle syntax. - - If *alpha* is given, force the alpha value of the returned RGBA tuple - to *alpha*. Otherwise, the alpha value from *c* is used, if it has alpha - information, or defaults to 1. - - *alpha* is ignored for the color value ``"none"`` (case-insensitive), - which always maps to ``(0, 0, 0, 0)``. - """ - orig_c = c - if c is np.ma.masked: - return (0., 0., 0., 0.) - if isinstance(c, str): - if c.lower() == "none": - return (0., 0., 0., 0.) - # Named color. - try: - # This may turn c into a non-string, so we check again below. - c = _colors_full_map[c] - except KeyError: - if len(orig_c) != 1: - try: - c = _colors_full_map[c.lower()] - except KeyError: - pass - if isinstance(c, str): - # hex color in #rrggbb format. - match = re.match(r"\A#[a-fA-F0-9]{6}\Z", c) - if match: - return (tuple(int(n, 16) / 255 - for n in [c[1:3], c[3:5], c[5:7]]) - + (alpha if alpha is not None else 1.,)) - # hex color in #rgb format, shorthand for #rrggbb. - match = re.match(r"\A#[a-fA-F0-9]{3}\Z", c) - if match: - return (tuple(int(n, 16) / 255 - for n in [c[1]*2, c[2]*2, c[3]*2]) - + (alpha if alpha is not None else 1.,)) - # hex color with alpha in #rrggbbaa format. - match = re.match(r"\A#[a-fA-F0-9]{8}\Z", c) - if match: - color = [int(n, 16) / 255 - for n in [c[1:3], c[3:5], c[5:7], c[7:9]]] - if alpha is not None: - color[-1] = alpha - return tuple(color) - # hex color with alpha in #rgba format, shorthand for #rrggbbaa. - match = re.match(r"\A#[a-fA-F0-9]{4}\Z", c) - if match: - color = [int(n, 16) / 255 - for n in [c[1]*2, c[2]*2, c[3]*2, c[4]*2]] - if alpha is not None: - color[-1] = alpha - return tuple(color) - # string gray. - try: - c = float(c) - except ValueError: - pass - else: - if not (0 <= c <= 1): - raise ValueError( - f"Invalid string grayscale value {orig_c!r}. " - f"Value must be within 0-1 range") - return c, c, c, alpha if alpha is not None else 1. - raise ValueError(f"Invalid RGBA argument: {orig_c!r}") - # turn 2-D array into 1-D array - if isinstance(c, np.ndarray): - if c.ndim == 2 and c.shape[0] == 1: - c = c.reshape(-1) - # tuple color. - if not np.iterable(c): - raise ValueError(f"Invalid RGBA argument: {orig_c!r}") - if len(c) not in [3, 4]: - raise ValueError("RGBA sequence should have length 3 or 4") - if not all(isinstance(x, Number) for x in c): - # Checks that don't work: `map(float, ...)`, `np.array(..., float)` and - # `np.array(...).astype(float)` would all convert "0.5" to 0.5. - raise ValueError(f"Invalid RGBA argument: {orig_c!r}") - # Return a tuple to prevent the cached value from being modified. - c = tuple(map(float, c)) - if len(c) == 3 and alpha is None: - alpha = 1 - if alpha is not None: - c = c[:3] + (alpha,) - if any(elem < 0 or elem > 1 for elem in c): - raise ValueError("RGBA values should be within 0-1 range") - return c - - -def to_rgba_array(c, alpha=None): - """ - Convert *c* to a (n, 4) array of RGBA colors. - - Parameters - ---------- - c : Matplotlib color or array of colors - If *c* is a masked array, an `~numpy.ndarray` is returned with a - (0, 0, 0, 0) row for each masked value or row in *c*. - - alpha : float or sequence of floats, optional - If *alpha* is given, force the alpha value of the returned RGBA tuple - to *alpha*. - - If None, the alpha value from *c* is used. If *c* does not have an - alpha channel, then alpha defaults to 1. - - *alpha* is ignored for the color value ``"none"`` (case-insensitive), - which always maps to ``(0, 0, 0, 0)``. - - If *alpha* is a sequence and *c* is a single color, *c* will be - repeated to match the length of *alpha*. - - Returns - ------- - array - (n, 4) array of RGBA colors, where each channel (red, green, blue, - alpha) can assume values between 0 and 1. - """ - # Special-case inputs that are already arrays, for performance. (If the - # array has the wrong kind or shape, raise the error during one-at-a-time - # conversion.) - if np.iterable(alpha): - alpha = np.asarray(alpha).ravel() - if (isinstance(c, np.ndarray) and c.dtype.kind in "if" - and c.ndim == 2 and c.shape[1] in [3, 4]): - mask = c.mask.any(axis=1) if np.ma.is_masked(c) else None - c = np.ma.getdata(c) - if np.iterable(alpha): - if c.shape[0] == 1 and alpha.shape[0] > 1: - c = np.tile(c, (alpha.shape[0], 1)) - elif c.shape[0] != alpha.shape[0]: - raise ValueError("The number of colors must match the number" - " of alpha values if there are more than one" - " of each.") - if c.shape[1] == 3: - result = np.column_stack([c, np.zeros(len(c))]) - result[:, -1] = alpha if alpha is not None else 1. - elif c.shape[1] == 4: - result = c.copy() - if alpha is not None: - result[:, -1] = alpha - if mask is not None: - result[mask] = 0 - if np.any((result < 0) | (result > 1)): - raise ValueError("RGBA values should be within 0-1 range") - return result - # Handle single values. - # Note that this occurs *after* handling inputs that are already arrays, as - # `to_rgba(c, alpha)` (below) is expensive for such inputs, due to the need - # to format the array in the ValueError message(!). - if cbook._str_lower_equal(c, "none"): - return np.zeros((0, 4), float) - try: - if np.iterable(alpha): - return np.array([to_rgba(c, a) for a in alpha], float) - else: - return np.array([to_rgba(c, alpha)], float) - except (ValueError, TypeError): - pass - - if isinstance(c, str): - raise ValueError(f"{c!r} is not a valid color value.") - - if len(c) == 0: - return np.zeros((0, 4), float) - - # Quick path if the whole sequence can be directly converted to a numpy - # array in one shot. - if isinstance(c, Sequence): - lens = {len(cc) if isinstance(cc, (list, tuple)) else -1 for cc in c} - if lens == {3}: - rgba = np.column_stack([c, np.ones(len(c))]) - elif lens == {4}: - rgba = np.array(c) - else: - rgba = np.array([to_rgba(cc) for cc in c]) - else: - rgba = np.array([to_rgba(cc) for cc in c]) - - if alpha is not None: - rgba[:, 3] = alpha - return rgba - - -def to_rgb(c): - """Convert *c* to an RGB color, silently dropping the alpha channel.""" - return to_rgba(c)[:3] - - -def to_hex(c, keep_alpha=False): - """ - Convert *c* to a hex color. - - Parameters - ---------- - c : :doc:`color ` or `numpy.ma.masked` - - keep_alpha : bool, default: False - If False, use the ``#rrggbb`` format, otherwise use ``#rrggbbaa``. - - Returns - ------- - str - ``#rrggbb`` or ``#rrggbbaa`` hex color string - """ - c = to_rgba(c) - if not keep_alpha: - c = c[:3] - return "#" + "".join(format(round(val * 255), "02x") for val in c) - - -### Backwards-compatible color-conversion API - - -cnames = CSS4_COLORS -hexColorPattern = re.compile(r"\A#[a-fA-F0-9]{6}\Z") -rgb2hex = to_hex -hex2color = to_rgb - - -class ColorConverter: - """ - A class only kept for backwards compatibility. - - Its functionality is entirely provided by module-level functions. - """ - colors = _colors_full_map - cache = _colors_full_map.cache - to_rgb = staticmethod(to_rgb) - to_rgba = staticmethod(to_rgba) - to_rgba_array = staticmethod(to_rgba_array) - - -colorConverter = ColorConverter() - - -### End of backwards-compatible color-conversion API - - -def _create_lookup_table(N, data, gamma=1.0): - r""" - Create an *N* -element 1D lookup table. - - This assumes a mapping :math:`f : [0, 1] \rightarrow [0, 1]`. The returned - data is an array of N values :math:`y = f(x)` where x is sampled from - [0, 1]. - - By default (*gamma* = 1) x is equidistantly sampled from [0, 1]. The - *gamma* correction factor :math:`\gamma` distorts this equidistant - sampling by :math:`x \rightarrow x^\gamma`. - - Parameters - ---------- - N : int - The number of elements of the created lookup table; at least 1. - - data : (M, 3) array-like or callable - Defines the mapping :math:`f`. - - If a (M, 3) array-like, the rows define values (x, y0, y1). The x - values must start with x=0, end with x=1, and all x values be in - increasing order. - - A value between :math:`x_i` and :math:`x_{i+1}` is mapped to the range - :math:`y^1_{i-1} \ldots y^0_i` by linear interpolation. - - For the simple case of a y-continuous mapping, y0 and y1 are identical. - - The two values of y are to allow for discontinuous mapping functions. - E.g. a sawtooth with a period of 0.2 and an amplitude of 1 would be:: - - [(0, 1, 0), (0.2, 1, 0), (0.4, 1, 0), ..., [(1, 1, 0)] - - In the special case of ``N == 1``, by convention the returned value - is y0 for x == 1. - - If *data* is a callable, it must accept and return numpy arrays:: - - data(x : ndarray) -> ndarray - - and map values between 0 - 1 to 0 - 1. - - gamma : float - Gamma correction factor for input distribution x of the mapping. - - See also https://en.wikipedia.org/wiki/Gamma_correction. - - Returns - ------- - array - The lookup table where ``lut[x * (N-1)]`` gives the closest value - for values of x between 0 and 1. - - Notes - ----- - This function is internally used for `.LinearSegmentedColormap`. - """ - - if callable(data): - xind = np.linspace(0, 1, N) ** gamma - lut = np.clip(np.array(data(xind), dtype=float), 0, 1) - return lut - - try: - adata = np.array(data) - except Exception as err: - raise TypeError("data must be convertible to an array") from err - _api.check_shape((None, 3), data=adata) - - x = adata[:, 0] - y0 = adata[:, 1] - y1 = adata[:, 2] - - if x[0] != 0. or x[-1] != 1.0: - raise ValueError( - "data mapping points must start with x=0 and end with x=1") - if (np.diff(x) < 0).any(): - raise ValueError("data mapping points must have x in increasing order") - # begin generation of lookup table - if N == 1: - # convention: use the y = f(x=1) value for a 1-element lookup table - lut = np.array(y0[-1]) - else: - x = x * (N - 1) - xind = (N - 1) * np.linspace(0, 1, N) ** gamma - ind = np.searchsorted(x, xind)[1:-1] - - distance = (xind[1:-1] - x[ind - 1]) / (x[ind] - x[ind - 1]) - lut = np.concatenate([ - [y1[0]], - distance * (y0[ind] - y1[ind - 1]) + y1[ind - 1], - [y0[-1]], - ]) - # ensure that the lut is confined to values between 0 and 1 by clipping it - return np.clip(lut, 0.0, 1.0) - - -class Colormap: - """ - Baseclass for all scalar to RGBA mappings. - - Typically, Colormap instances are used to convert data values (floats) - from the interval ``[0, 1]`` to the RGBA color that the respective - Colormap represents. For scaling of data into the ``[0, 1]`` interval see - `matplotlib.colors.Normalize`. Subclasses of `matplotlib.cm.ScalarMappable` - make heavy use of this ``data -> normalize -> map-to-color`` processing - chain. - """ - - def __init__(self, name, N=256): - """ - Parameters - ---------- - name : str - The name of the colormap. - N : int - The number of RGB quantization levels. - """ - self.name = name - self.N = int(N) # ensure that N is always int - self._rgba_bad = (0.0, 0.0, 0.0, 0.0) # If bad, don't paint anything. - self._rgba_under = None - self._rgba_over = None - self._i_under = self.N - self._i_over = self.N + 1 - self._i_bad = self.N + 2 - self._isinit = False - #: When this colormap exists on a scalar mappable and colorbar_extend - #: is not False, colorbar creation will pick up ``colorbar_extend`` as - #: the default value for the ``extend`` keyword in the - #: `matplotlib.colorbar.Colorbar` constructor. - self.colorbar_extend = False - - def __call__(self, X, alpha=None, bytes=False): - """ - Parameters - ---------- - X : float or int, `~numpy.ndarray` or scalar - The data value(s) to convert to RGBA. - For floats, *X* should be in the interval ``[0.0, 1.0]`` to - return the RGBA values ``X*100`` percent along the Colormap line. - For integers, *X* should be in the interval ``[0, Colormap.N)`` to - return RGBA values *indexed* from the Colormap with index ``X``. - alpha : float or array-like or None - Alpha must be a scalar between 0 and 1, a sequence of such - floats with shape matching X, or None. - bytes : bool - If False (default), the returned RGBA values will be floats in the - interval ``[0, 1]`` otherwise they will be uint8s in the interval - ``[0, 255]``. - - Returns - ------- - Tuple of RGBA values if X is scalar, otherwise an array of - RGBA values with a shape of ``X.shape + (4, )``. - """ - if not self._isinit: - self._init() - - # Take the bad mask from a masked array, or in all other cases defer - # np.isnan() to after we have converted to an array. - mask_bad = X.mask if np.ma.is_masked(X) else None - xa = np.array(X, copy=True) - if mask_bad is None: - mask_bad = np.isnan(xa) - if not xa.dtype.isnative: - xa = xa.byteswap().newbyteorder() # Native byteorder is faster. - if xa.dtype.kind == "f": - xa *= self.N - # Negative values are out of range, but astype(int) would - # truncate them towards zero. - xa[xa < 0] = -1 - # xa == 1 (== N after multiplication) is not out of range. - xa[xa == self.N] = self.N - 1 - # Avoid converting large positive values to negative integers. - np.clip(xa, -1, self.N, out=xa) - with np.errstate(invalid="ignore"): - # We need this cast for unsigned ints as well as floats - xa = xa.astype(int) - # Set the over-range indices before the under-range; - # otherwise the under-range values get converted to over-range. - xa[xa > self.N - 1] = self._i_over - xa[xa < 0] = self._i_under - xa[mask_bad] = self._i_bad - - lut = self._lut - if bytes: - lut = (lut * 255).astype(np.uint8) - - rgba = lut.take(xa, axis=0, mode='clip') - - if alpha is not None: - alpha = np.clip(alpha, 0, 1) - if bytes: - alpha *= 255 # Will be cast to uint8 upon assignment. - if alpha.shape not in [(), xa.shape]: - raise ValueError( - f"alpha is array-like but its shape {alpha.shape} does " - f"not match that of X {xa.shape}") - rgba[..., -1] = alpha - - # If the "bad" color is all zeros, then ignore alpha input. - if (lut[-1] == 0).all() and np.any(mask_bad): - if np.iterable(mask_bad) and mask_bad.shape == xa.shape: - rgba[mask_bad] = (0, 0, 0, 0) - else: - rgba[..., :] = (0, 0, 0, 0) - - if not np.iterable(X): - rgba = tuple(rgba) - return rgba - - def __copy__(self): - cls = self.__class__ - cmapobject = cls.__new__(cls) - cmapobject.__dict__.update(self.__dict__) - if self._isinit: - cmapobject._lut = np.copy(self._lut) - return cmapobject - - def __eq__(self, other): - if (not isinstance(other, Colormap) or self.name != other.name or - self.colorbar_extend != other.colorbar_extend): - return False - # To compare lookup tables the Colormaps have to be initialized - if not self._isinit: - self._init() - if not other._isinit: - other._init() - return np.array_equal(self._lut, other._lut) - - def get_bad(self): - """Get the color for masked values.""" - if not self._isinit: - self._init() - return np.array(self._lut[self._i_bad]) - - def set_bad(self, color='k', alpha=None): - """Set the color for masked values.""" - self._rgba_bad = to_rgba(color, alpha) - if self._isinit: - self._set_extremes() - - def get_under(self): - """Get the color for low out-of-range values.""" - if not self._isinit: - self._init() - return np.array(self._lut[self._i_under]) - - def set_under(self, color='k', alpha=None): - """Set the color for low out-of-range values.""" - self._rgba_under = to_rgba(color, alpha) - if self._isinit: - self._set_extremes() - - def get_over(self): - """Get the color for high out-of-range values.""" - if not self._isinit: - self._init() - return np.array(self._lut[self._i_over]) - - def set_over(self, color='k', alpha=None): - """Set the color for high out-of-range values.""" - self._rgba_over = to_rgba(color, alpha) - if self._isinit: - self._set_extremes() - - def set_extremes(self, *, bad=None, under=None, over=None): - """ - Set the colors for masked (*bad*) values and, when ``norm.clip = - False``, low (*under*) and high (*over*) out-of-range values. - """ - if bad is not None: - self.set_bad(bad) - if under is not None: - self.set_under(under) - if over is not None: - self.set_over(over) - - def with_extremes(self, *, bad=None, under=None, over=None): - """ - Return a copy of the colormap, for which the colors for masked (*bad*) - values and, when ``norm.clip = False``, low (*under*) and high (*over*) - out-of-range values, have been set accordingly. - """ - new_cm = self.copy() - new_cm.set_extremes(bad=bad, under=under, over=over) - return new_cm - - def _set_extremes(self): - if self._rgba_under: - self._lut[self._i_under] = self._rgba_under - else: - self._lut[self._i_under] = self._lut[0] - if self._rgba_over: - self._lut[self._i_over] = self._rgba_over - else: - self._lut[self._i_over] = self._lut[self.N - 1] - self._lut[self._i_bad] = self._rgba_bad - - def _init(self): - """Generate the lookup table, ``self._lut``.""" - raise NotImplementedError("Abstract class only") - - def is_gray(self): - """Return whether the colormap is grayscale.""" - if not self._isinit: - self._init() - return (np.all(self._lut[:, 0] == self._lut[:, 1]) and - np.all(self._lut[:, 0] == self._lut[:, 2])) - - def resampled(self, lutsize): - """Return a new colormap with *lutsize* entries.""" - if hasattr(self, '_resample'): - _api.warn_external( - "The ability to resample a color map is now public API " - f"However the class {type(self)} still only implements " - "the previous private _resample method. Please update " - "your class." - ) - return self._resample(lutsize) - - raise NotImplementedError() - - def reversed(self, name=None): - """ - Return a reversed instance of the Colormap. - - .. note:: This function is not implemented for the base class. - - Parameters - ---------- - name : str, optional - The name for the reversed colormap. If None, the - name is set to ``self.name + "_r"``. - - See Also - -------- - LinearSegmentedColormap.reversed - ListedColormap.reversed - """ - raise NotImplementedError() - - def _repr_png_(self): - """Generate a PNG representation of the Colormap.""" - X = np.tile(np.linspace(0, 1, _REPR_PNG_SIZE[0]), - (_REPR_PNG_SIZE[1], 1)) - pixels = self(X, bytes=True) - png_bytes = io.BytesIO() - title = self.name + ' colormap' - author = f'Matplotlib v{mpl.__version__}, https://matplotlib.org' - pnginfo = PngInfo() - pnginfo.add_text('Title', title) - pnginfo.add_text('Description', title) - pnginfo.add_text('Author', author) - pnginfo.add_text('Software', author) - Image.fromarray(pixels).save(png_bytes, format='png', pnginfo=pnginfo) - return png_bytes.getvalue() - - def _repr_html_(self): - """Generate an HTML representation of the Colormap.""" - png_bytes = self._repr_png_() - png_base64 = base64.b64encode(png_bytes).decode('ascii') - def color_block(color): - hex_color = to_hex(color, keep_alpha=True) - return (f'
') - - return ('
' - f'{self.name} ' - '
' - '
' - '
' - '
' - f'{color_block(self.get_under())} under' - '
' - '
' - f'bad {color_block(self.get_bad())}' - '
' - '
' - f'over {color_block(self.get_over())}' - '
') - - def copy(self): - """Return a copy of the colormap.""" - return self.__copy__() - - -class LinearSegmentedColormap(Colormap): - """ - Colormap objects based on lookup tables using linear segments. - - The lookup table is generated using linear interpolation for each - primary color, with the 0-1 domain divided into any number of - segments. - """ - - def __init__(self, name, segmentdata, N=256, gamma=1.0): - """ - Create colormap from linear mapping segments - - segmentdata argument is a dictionary with a red, green and blue - entries. Each entry should be a list of *x*, *y0*, *y1* tuples, - forming rows in a table. Entries for alpha are optional. - - Example: suppose you want red to increase from 0 to 1 over - the bottom half, green to do the same over the middle half, - and blue over the top half. Then you would use:: - - cdict = {'red': [(0.0, 0.0, 0.0), - (0.5, 1.0, 1.0), - (1.0, 1.0, 1.0)], - - 'green': [(0.0, 0.0, 0.0), - (0.25, 0.0, 0.0), - (0.75, 1.0, 1.0), - (1.0, 1.0, 1.0)], - - 'blue': [(0.0, 0.0, 0.0), - (0.5, 0.0, 0.0), - (1.0, 1.0, 1.0)]} - - Each row in the table for a given color is a sequence of - *x*, *y0*, *y1* tuples. In each sequence, *x* must increase - monotonically from 0 to 1. For any input value *z* falling - between *x[i]* and *x[i+1]*, the output value of a given color - will be linearly interpolated between *y1[i]* and *y0[i+1]*:: - - row i: x y0 y1 - / - / - row i+1: x y0 y1 - - Hence y0 in the first row and y1 in the last row are never used. - - See Also - -------- - LinearSegmentedColormap.from_list - Static method; factory function for generating a smoothly-varying - LinearSegmentedColormap. - """ - # True only if all colors in map are identical; needed for contouring. - self.monochrome = False - super().__init__(name, N) - self._segmentdata = segmentdata - self._gamma = gamma - - def _init(self): - self._lut = np.ones((self.N + 3, 4), float) - self._lut[:-3, 0] = _create_lookup_table( - self.N, self._segmentdata['red'], self._gamma) - self._lut[:-3, 1] = _create_lookup_table( - self.N, self._segmentdata['green'], self._gamma) - self._lut[:-3, 2] = _create_lookup_table( - self.N, self._segmentdata['blue'], self._gamma) - if 'alpha' in self._segmentdata: - self._lut[:-3, 3] = _create_lookup_table( - self.N, self._segmentdata['alpha'], 1) - self._isinit = True - self._set_extremes() - - def set_gamma(self, gamma): - """Set a new gamma value and regenerate colormap.""" - self._gamma = gamma - self._init() - - @staticmethod - def from_list(name, colors, N=256, gamma=1.0): - """ - Create a `LinearSegmentedColormap` from a list of colors. - - Parameters - ---------- - name : str - The name of the colormap. - colors : array-like of colors or array-like of (value, color) - If only colors are given, they are equidistantly mapped from the - range :math:`[0, 1]`; i.e. 0 maps to ``colors[0]`` and 1 maps to - ``colors[-1]``. - If (value, color) pairs are given, the mapping is from *value* - to *color*. This can be used to divide the range unevenly. - N : int - The number of RGB quantization levels. - gamma : float - """ - if not np.iterable(colors): - raise ValueError('colors must be iterable') - - if (isinstance(colors[0], Sized) and len(colors[0]) == 2 - and not isinstance(colors[0], str)): - # List of value, color pairs - vals, colors = zip(*colors) - else: - vals = np.linspace(0, 1, len(colors)) - - r, g, b, a = to_rgba_array(colors).T - cdict = { - "red": np.column_stack([vals, r, r]), - "green": np.column_stack([vals, g, g]), - "blue": np.column_stack([vals, b, b]), - "alpha": np.column_stack([vals, a, a]), - } - - return LinearSegmentedColormap(name, cdict, N, gamma) - - def resampled(self, lutsize): - """Return a new colormap with *lutsize* entries.""" - new_cmap = LinearSegmentedColormap(self.name, self._segmentdata, - lutsize) - new_cmap._rgba_over = self._rgba_over - new_cmap._rgba_under = self._rgba_under - new_cmap._rgba_bad = self._rgba_bad - return new_cmap - - # Helper ensuring picklability of the reversed cmap. - @staticmethod - def _reverser(func, x): - return func(1 - x) - - def reversed(self, name=None): - """ - Return a reversed instance of the Colormap. - - Parameters - ---------- - name : str, optional - The name for the reversed colormap. If None, the - name is set to ``self.name + "_r"``. - - Returns - ------- - LinearSegmentedColormap - The reversed colormap. - """ - if name is None: - name = self.name + "_r" - - # Using a partial object keeps the cmap picklable. - data_r = {key: (functools.partial(self._reverser, data) - if callable(data) else - [(1.0 - x, y1, y0) for x, y0, y1 in reversed(data)]) - for key, data in self._segmentdata.items()} - - new_cmap = LinearSegmentedColormap(name, data_r, self.N, self._gamma) - # Reverse the over/under values too - new_cmap._rgba_over = self._rgba_under - new_cmap._rgba_under = self._rgba_over - new_cmap._rgba_bad = self._rgba_bad - return new_cmap - - -class ListedColormap(Colormap): - """ - Colormap object generated from a list of colors. - - This may be most useful when indexing directly into a colormap, - but it can also be used to generate special colormaps for ordinary - mapping. - - Parameters - ---------- - colors : list, array - List of Matplotlib color specifications, or an equivalent Nx3 or Nx4 - floating point array (*N* RGB or RGBA values). - name : str, optional - String to identify the colormap. - N : int, optional - Number of entries in the map. The default is *None*, in which case - there is one colormap entry for each element in the list of colors. - If :: - - N < len(colors) - - the list will be truncated at *N*. If :: - - N > len(colors) - - the list will be extended by repetition. - """ - def __init__(self, colors, name='from_list', N=None): - self.monochrome = False # Are all colors identical? (for contour.py) - if N is None: - self.colors = colors - N = len(colors) - else: - if isinstance(colors, str): - self.colors = [colors] * N - self.monochrome = True - elif np.iterable(colors): - if len(colors) == 1: - self.monochrome = True - self.colors = list( - itertools.islice(itertools.cycle(colors), N)) - else: - try: - gray = float(colors) - except TypeError: - pass - else: - self.colors = [gray] * N - self.monochrome = True - super().__init__(name, N) - - def _init(self): - self._lut = np.zeros((self.N + 3, 4), float) - self._lut[:-3] = to_rgba_array(self.colors) - self._isinit = True - self._set_extremes() - - def resampled(self, lutsize): - """Return a new colormap with *lutsize* entries.""" - colors = self(np.linspace(0, 1, lutsize)) - new_cmap = ListedColormap(colors, name=self.name) - # Keep the over/under values too - new_cmap._rgba_over = self._rgba_over - new_cmap._rgba_under = self._rgba_under - new_cmap._rgba_bad = self._rgba_bad - return new_cmap - - def reversed(self, name=None): - """ - Return a reversed instance of the Colormap. - - Parameters - ---------- - name : str, optional - The name for the reversed colormap. If None, the - name is set to ``self.name + "_r"``. - - Returns - ------- - ListedColormap - A reversed instance of the colormap. - """ - if name is None: - name = self.name + "_r" - - colors_r = list(reversed(self.colors)) - new_cmap = ListedColormap(colors_r, name=name, N=self.N) - # Reverse the over/under values too - new_cmap._rgba_over = self._rgba_under - new_cmap._rgba_under = self._rgba_over - new_cmap._rgba_bad = self._rgba_bad - return new_cmap - - -class Normalize: - """ - A class which, when called, linearly normalizes data into the - ``[0.0, 1.0]`` interval. - """ - - def __init__(self, vmin=None, vmax=None, clip=False): - """ - Parameters - ---------- - vmin, vmax : float or None - If *vmin* and/or *vmax* is not given, they are initialized from the - minimum and maximum value, respectively, of the first input - processed; i.e., ``__call__(A)`` calls ``autoscale_None(A)``. - - clip : bool, default: False - If ``True`` values falling outside the range ``[vmin, vmax]``, - are mapped to 0 or 1, whichever is closer, and masked values are - set to 1. If ``False`` masked values remain masked. - - Clipping silently defeats the purpose of setting the over, under, - and masked colors in a colormap, so it is likely to lead to - surprises; therefore the default is ``clip=False``. - - Notes - ----- - Returns 0 if ``vmin == vmax``. - """ - self._vmin = _sanitize_extrema(vmin) - self._vmax = _sanitize_extrema(vmax) - self._clip = clip - self._scale = None - self.callbacks = cbook.CallbackRegistry(signals=["changed"]) - - @property - def vmin(self): - return self._vmin - - @vmin.setter - def vmin(self, value): - value = _sanitize_extrema(value) - if value != self._vmin: - self._vmin = value - self._changed() - - @property - def vmax(self): - return self._vmax - - @vmax.setter - def vmax(self, value): - value = _sanitize_extrema(value) - if value != self._vmax: - self._vmax = value - self._changed() - - @property - def clip(self): - return self._clip - - @clip.setter - def clip(self, value): - if value != self._clip: - self._clip = value - self._changed() - - def _changed(self): - """ - Call this whenever the norm is changed to notify all the - callback listeners to the 'changed' signal. - """ - self.callbacks.process('changed') - - @staticmethod - def process_value(value): - """ - Homogenize the input *value* for easy and efficient normalization. - - *value* can be a scalar or sequence. - - Returns - ------- - result : masked array - Masked array with the same shape as *value*. - is_scalar : bool - Whether *value* is a scalar. - - Notes - ----- - Float dtypes are preserved; integer types with two bytes or smaller are - converted to np.float32, and larger types are converted to np.float64. - Preserving float32 when possible, and using in-place operations, - greatly improves speed for large arrays. - """ - is_scalar = not np.iterable(value) - if is_scalar: - value = [value] - dtype = np.min_scalar_type(value) - if np.issubdtype(dtype, np.integer) or dtype.type is np.bool_: - # bool_/int8/int16 -> float32; int32/int64 -> float64 - dtype = np.promote_types(dtype, np.float32) - # ensure data passed in as an ndarray subclass are interpreted as - # an ndarray. See issue #6622. - mask = np.ma.getmask(value) - data = np.asarray(value) - result = np.ma.array(data, mask=mask, dtype=dtype, copy=True) - return result, is_scalar - - def __call__(self, value, clip=None): - """ - Normalize *value* data in the ``[vmin, vmax]`` interval into the - ``[0.0, 1.0]`` interval and return it. - - Parameters - ---------- - value - Data to normalize. - clip : bool, optional - If ``None``, defaults to ``self.clip`` (which defaults to - ``False``). - - Notes - ----- - If not already initialized, ``self.vmin`` and ``self.vmax`` are - initialized using ``self.autoscale_None(value)``. - """ - if clip is None: - clip = self.clip - - result, is_scalar = self.process_value(value) - - if self.vmin is None or self.vmax is None: - self.autoscale_None(result) - # Convert at least to float, without losing precision. - (vmin,), _ = self.process_value(self.vmin) - (vmax,), _ = self.process_value(self.vmax) - if vmin == vmax: - result.fill(0) # Or should it be all masked? Or 0.5? - elif vmin > vmax: - raise ValueError("minvalue must be less than or equal to maxvalue") - else: - if clip: - mask = np.ma.getmask(result) - result = np.ma.array(np.clip(result.filled(vmax), vmin, vmax), - mask=mask) - # ma division is very slow; we can take a shortcut - resdat = result.data - resdat -= vmin - resdat /= (vmax - vmin) - result = np.ma.array(resdat, mask=result.mask, copy=False) - if is_scalar: - result = result[0] - return result - - def inverse(self, value): - if not self.scaled(): - raise ValueError("Not invertible until both vmin and vmax are set") - (vmin,), _ = self.process_value(self.vmin) - (vmax,), _ = self.process_value(self.vmax) - - if np.iterable(value): - val = np.ma.asarray(value) - return vmin + val * (vmax - vmin) - else: - return vmin + value * (vmax - vmin) - - def autoscale(self, A): - """Set *vmin*, *vmax* to min, max of *A*.""" - with self.callbacks.blocked(): - # Pause callbacks while we are updating so we only get - # a single update signal at the end - self.vmin = self.vmax = None - self.autoscale_None(A) - self._changed() - - def autoscale_None(self, A): - """If vmin or vmax are not set, use the min/max of *A* to set them.""" - A = np.asanyarray(A) - if self.vmin is None and A.size: - self.vmin = A.min() - if self.vmax is None and A.size: - self.vmax = A.max() - - def scaled(self): - """Return whether vmin and vmax are set.""" - return self.vmin is not None and self.vmax is not None - - -class TwoSlopeNorm(Normalize): - def __init__(self, vcenter, vmin=None, vmax=None): - """ - Normalize data with a set center. - - Useful when mapping data with an unequal rates of change around a - conceptual center, e.g., data that range from -2 to 4, with 0 as - the midpoint. - - Parameters - ---------- - vcenter : float - The data value that defines ``0.5`` in the normalization. - vmin : float, optional - The data value that defines ``0.0`` in the normalization. - Defaults to the min value of the dataset. - vmax : float, optional - The data value that defines ``1.0`` in the normalization. - Defaults to the max value of the dataset. - - Examples - -------- - This maps data value -4000 to 0., 0 to 0.5, and +10000 to 1.0; data - between is linearly interpolated:: - - >>> import matplotlib.colors as mcolors - >>> offset = mcolors.TwoSlopeNorm(vmin=-4000., - vcenter=0., vmax=10000) - >>> data = [-4000., -2000., 0., 2500., 5000., 7500., 10000.] - >>> offset(data) - array([0., 0.25, 0.5, 0.625, 0.75, 0.875, 1.0]) - """ - - super().__init__(vmin=vmin, vmax=vmax) - self._vcenter = vcenter - if vcenter is not None and vmax is not None and vcenter >= vmax: - raise ValueError('vmin, vcenter, and vmax must be in ' - 'ascending order') - if vcenter is not None and vmin is not None and vcenter <= vmin: - raise ValueError('vmin, vcenter, and vmax must be in ' - 'ascending order') - - @property - def vcenter(self): - return self._vcenter - - @vcenter.setter - def vcenter(self, value): - if value != self._vcenter: - self._vcenter = value - self._changed() - - def autoscale_None(self, A): - """ - Get vmin and vmax, and then clip at vcenter - """ - super().autoscale_None(A) - if self.vmin > self.vcenter: - self.vmin = self.vcenter - if self.vmax < self.vcenter: - self.vmax = self.vcenter - - def __call__(self, value, clip=None): - """ - Map value to the interval [0, 1]. The *clip* argument is unused. - """ - result, is_scalar = self.process_value(value) - self.autoscale_None(result) # sets self.vmin, self.vmax if None - - if not self.vmin <= self.vcenter <= self.vmax: - raise ValueError("vmin, vcenter, vmax must increase monotonically") - # note that we must extrapolate for tick locators: - result = np.ma.masked_array( - np.interp(result, [self.vmin, self.vcenter, self.vmax], - [0, 0.5, 1], left=-np.inf, right=np.inf), - mask=np.ma.getmask(result)) - if is_scalar: - result = np.atleast_1d(result)[0] - return result - - def inverse(self, value): - if not self.scaled(): - raise ValueError("Not invertible until both vmin and vmax are set") - (vmin,), _ = self.process_value(self.vmin) - (vmax,), _ = self.process_value(self.vmax) - (vcenter,), _ = self.process_value(self.vcenter) - result = np.interp(value, [0, 0.5, 1], [vmin, vcenter, vmax], - left=-np.inf, right=np.inf) - return result - - -class CenteredNorm(Normalize): - def __init__(self, vcenter=0, halfrange=None, clip=False): - """ - Normalize symmetrical data around a center (0 by default). - - Unlike `TwoSlopeNorm`, `CenteredNorm` applies an equal rate of change - around the center. - - Useful when mapping symmetrical data around a conceptual center - e.g., data that range from -2 to 4, with 0 as the midpoint, and - with equal rates of change around that midpoint. - - Parameters - ---------- - vcenter : float, default: 0 - The data value that defines ``0.5`` in the normalization. - halfrange : float, optional - The range of data values that defines a range of ``0.5`` in the - normalization, so that *vcenter* - *halfrange* is ``0.0`` and - *vcenter* + *halfrange* is ``1.0`` in the normalization. - Defaults to the largest absolute difference to *vcenter* for - the values in the dataset. - clip : bool, default: False - If ``True`` values falling outside the range ``[vmin, vmax]``, - are mapped to 0 or 1, whichever is closer, and masked values are - set to 1. If ``False`` masked values remain masked. - - Examples - -------- - This maps data values -2 to 0.25, 0 to 0.5, and 4 to 1.0 - (assuming equal rates of change above and below 0.0): - - >>> import matplotlib.colors as mcolors - >>> norm = mcolors.CenteredNorm(halfrange=4.0) - >>> data = [-2., 0., 4.] - >>> norm(data) - array([0.25, 0.5 , 1. ]) - """ - super().__init__(vmin=None, vmax=None, clip=clip) - self._vcenter = vcenter - # calling the halfrange setter to set vmin and vmax - self.halfrange = halfrange - - def autoscale(self, A): - """ - Set *halfrange* to ``max(abs(A-vcenter))``, then set *vmin* and *vmax*. - """ - A = np.asanyarray(A) - self.halfrange = max(self._vcenter-A.min(), - A.max()-self._vcenter) - - def autoscale_None(self, A): - """Set *vmin* and *vmax*.""" - A = np.asanyarray(A) - if self.halfrange is None and A.size: - self.autoscale(A) - - @property - def vmin(self): - return self._vmin - - @vmin.setter - def vmin(self, value): - value = _sanitize_extrema(value) - if value != self._vmin: - self._vmin = value - self._vmax = 2*self.vcenter - value - self._changed() - - @property - def vmax(self): - return self._vmax - - @vmax.setter - def vmax(self, value): - value = _sanitize_extrema(value) - if value != self._vmax: - self._vmax = value - self._vmin = 2*self.vcenter - value - self._changed() - - @property - def vcenter(self): - return self._vcenter - - @vcenter.setter - def vcenter(self, vcenter): - if vcenter != self._vcenter: - self._vcenter = vcenter - # Trigger an update of the vmin/vmax values through the setter - self.halfrange = self.halfrange - self._changed() - - @property - def halfrange(self): - if self.vmin is None or self.vmax is None: - return None - return (self.vmax - self.vmin) / 2 - - @halfrange.setter - def halfrange(self, halfrange): - if halfrange is None: - self.vmin = None - self.vmax = None - else: - self.vmin = self.vcenter - abs(halfrange) - self.vmax = self.vcenter + abs(halfrange) - - -def make_norm_from_scale(scale_cls, base_norm_cls=None, *, init=None): - """ - Decorator for building a `.Normalize` subclass from a `~.scale.ScaleBase` - subclass. - - After :: - - @make_norm_from_scale(scale_cls) - class norm_cls(Normalize): - ... - - *norm_cls* is filled with methods so that normalization computations are - forwarded to *scale_cls* (i.e., *scale_cls* is the scale that would be used - for the colorbar of a mappable normalized with *norm_cls*). - - If *init* is not passed, then the constructor signature of *norm_cls* - will be ``norm_cls(vmin=None, vmax=None, clip=False)``; these three - parameters will be forwarded to the base class (``Normalize.__init__``), - and a *scale_cls* object will be initialized with no arguments (other than - a dummy axis). - - If the *scale_cls* constructor takes additional parameters, then *init* - should be passed to `make_norm_from_scale`. It is a callable which is - *only* used for its signature. First, this signature will become the - signature of *norm_cls*. Second, the *norm_cls* constructor will bind the - parameters passed to it using this signature, extract the bound *vmin*, - *vmax*, and *clip* values, pass those to ``Normalize.__init__``, and - forward the remaining bound values (including any defaults defined by the - signature) to the *scale_cls* constructor. - """ - - if base_norm_cls is None: - return functools.partial(make_norm_from_scale, scale_cls, init=init) - - if isinstance(scale_cls, functools.partial): - scale_args = scale_cls.args - scale_kwargs_items = tuple(scale_cls.keywords.items()) - scale_cls = scale_cls.func - else: - scale_args = scale_kwargs_items = () - - if init is None: - def init(vmin=None, vmax=None, clip=False): pass - - return _make_norm_from_scale( - scale_cls, scale_args, scale_kwargs_items, - base_norm_cls, inspect.signature(init)) - - -@functools.lru_cache(None) -def _make_norm_from_scale( - scale_cls, scale_args, scale_kwargs_items, - base_norm_cls, bound_init_signature, -): - """ - Helper for `make_norm_from_scale`. - - This function is split out to enable caching (in particular so that - different unpickles reuse the same class). In order to do so, - - - ``functools.partial`` *scale_cls* is expanded into ``func, args, kwargs`` - to allow memoizing returned norms (partial instances always compare - unequal, but we can check identity based on ``func, args, kwargs``; - - *init* is replaced by *init_signature*, as signatures are picklable, - unlike to arbitrary lambdas. - """ - - class Norm(base_norm_cls): - def __reduce__(self): - cls = type(self) - # If the class is toplevel-accessible, it is possible to directly - # pickle it "by name". This is required to support norm classes - # defined at a module's toplevel, as the inner base_norm_cls is - # otherwise unpicklable (as it gets shadowed by the generated norm - # class). If either import or attribute access fails, fall back to - # the general path. - try: - if cls is getattr(importlib.import_module(cls.__module__), - cls.__qualname__): - return (_create_empty_object_of_class, (cls,), vars(self)) - except (ImportError, AttributeError): - pass - return (_picklable_norm_constructor, - (scale_cls, scale_args, scale_kwargs_items, - base_norm_cls, bound_init_signature), - vars(self)) - - def __init__(self, *args, **kwargs): - ba = bound_init_signature.bind(*args, **kwargs) - ba.apply_defaults() - super().__init__( - **{k: ba.arguments.pop(k) for k in ["vmin", "vmax", "clip"]}) - self._scale = functools.partial( - scale_cls, *scale_args, **dict(scale_kwargs_items))( - axis=None, **ba.arguments) - self._trf = self._scale.get_transform() - - __init__.__signature__ = bound_init_signature.replace(parameters=[ - inspect.Parameter("self", inspect.Parameter.POSITIONAL_OR_KEYWORD), - *bound_init_signature.parameters.values()]) - - def __call__(self, value, clip=None): - value, is_scalar = self.process_value(value) - if self.vmin is None or self.vmax is None: - self.autoscale_None(value) - if self.vmin > self.vmax: - raise ValueError("vmin must be less or equal to vmax") - if self.vmin == self.vmax: - return np.full_like(value, 0) - if clip is None: - clip = self.clip - if clip: - value = np.clip(value, self.vmin, self.vmax) - t_value = self._trf.transform(value).reshape(np.shape(value)) - t_vmin, t_vmax = self._trf.transform([self.vmin, self.vmax]) - if not np.isfinite([t_vmin, t_vmax]).all(): - raise ValueError("Invalid vmin or vmax") - t_value -= t_vmin - t_value /= (t_vmax - t_vmin) - t_value = np.ma.masked_invalid(t_value, copy=False) - return t_value[0] if is_scalar else t_value - - def inverse(self, value): - if not self.scaled(): - raise ValueError("Not invertible until scaled") - if self.vmin > self.vmax: - raise ValueError("vmin must be less or equal to vmax") - t_vmin, t_vmax = self._trf.transform([self.vmin, self.vmax]) - if not np.isfinite([t_vmin, t_vmax]).all(): - raise ValueError("Invalid vmin or vmax") - value, is_scalar = self.process_value(value) - rescaled = value * (t_vmax - t_vmin) - rescaled += t_vmin - value = (self._trf - .inverted() - .transform(rescaled) - .reshape(np.shape(value))) - return value[0] if is_scalar else value - - def autoscale_None(self, A): - # i.e. A[np.isfinite(...)], but also for non-array A's - in_trf_domain = np.extract(np.isfinite(self._trf.transform(A)), A) - if in_trf_domain.size == 0: - in_trf_domain = np.ma.masked - return super().autoscale_None(in_trf_domain) - - if base_norm_cls is Normalize: - Norm.__name__ = f"{scale_cls.__name__}Norm" - Norm.__qualname__ = f"{scale_cls.__qualname__}Norm" - else: - Norm.__name__ = base_norm_cls.__name__ - Norm.__qualname__ = base_norm_cls.__qualname__ - Norm.__module__ = base_norm_cls.__module__ - Norm.__doc__ = base_norm_cls.__doc__ - - return Norm - - -def _create_empty_object_of_class(cls): - return cls.__new__(cls) - - -def _picklable_norm_constructor(*args): - return _create_empty_object_of_class(_make_norm_from_scale(*args)) - - -@make_norm_from_scale( - scale.FuncScale, - init=lambda functions, vmin=None, vmax=None, clip=False: None) -class FuncNorm(Normalize): - """ - Arbitrary normalization using functions for the forward and inverse. - - Parameters - ---------- - functions : (callable, callable) - two-tuple of the forward and inverse functions for the normalization. - The forward function must be monotonic. - - Both functions must have the signature :: - - def forward(values: array-like) -> array-like - - vmin, vmax : float or None - If *vmin* and/or *vmax* is not given, they are initialized from the - minimum and maximum value, respectively, of the first input - processed; i.e., ``__call__(A)`` calls ``autoscale_None(A)``. - - clip : bool, default: False - If ``True`` values falling outside the range ``[vmin, vmax]``, - are mapped to 0 or 1, whichever is closer, and masked values are - set to 1. If ``False`` masked values remain masked. - - Clipping silently defeats the purpose of setting the over, under, - and masked colors in a colormap, so it is likely to lead to - surprises; therefore the default is ``clip=False``. - """ - - -LogNorm = make_norm_from_scale( - functools.partial(scale.LogScale, nonpositive="mask"))(Normalize) -LogNorm.__name__ = LogNorm.__qualname__ = "LogNorm" -LogNorm.__doc__ = "Normalize a given value to the 0-1 range on a log scale." - - -@make_norm_from_scale( - scale.SymmetricalLogScale, - init=lambda linthresh, linscale=1., vmin=None, vmax=None, clip=False, *, - base=10: None) -class SymLogNorm(Normalize): - """ - The symmetrical logarithmic scale is logarithmic in both the - positive and negative directions from the origin. - - Since the values close to zero tend toward infinity, there is a - need to have a range around zero that is linear. The parameter - *linthresh* allows the user to specify the size of this range - (-*linthresh*, *linthresh*). - - Parameters - ---------- - linthresh : float - The range within which the plot is linear (to avoid having the plot - go to infinity around zero). - linscale : float, default: 1 - This allows the linear range (-*linthresh* to *linthresh*) to be - stretched relative to the logarithmic range. Its value is the - number of decades to use for each half of the linear range. For - example, when *linscale* == 1.0 (the default), the space used for - the positive and negative halves of the linear range will be equal - to one decade in the logarithmic range. - base : float, default: 10 - """ - - @property - def linthresh(self): - return self._scale.linthresh - - @linthresh.setter - def linthresh(self, value): - self._scale.linthresh = value - - -@make_norm_from_scale( - scale.AsinhScale, - init=lambda linear_width=1, vmin=None, vmax=None, clip=False: None) -class AsinhNorm(Normalize): - """ - The inverse hyperbolic sine scale is approximately linear near - the origin, but becomes logarithmic for larger positive - or negative values. Unlike the `SymLogNorm`, the transition between - these linear and logarithmic regions is smooth, which may reduce - the risk of visual artifacts. - - .. note:: - - This API is provisional and may be revised in the future - based on early user feedback. - - Parameters - ---------- - linear_width : float, default: 1 - The effective width of the linear region, beyond which - the transformation becomes asymptotically logarithmic - """ - - @property - def linear_width(self): - return self._scale.linear_width - - @linear_width.setter - def linear_width(self, value): - self._scale.linear_width = value - - -class PowerNorm(Normalize): - """ - Linearly map a given value to the 0-1 range and then apply - a power-law normalization over that range. - """ - def __init__(self, gamma, vmin=None, vmax=None, clip=False): - super().__init__(vmin, vmax, clip) - self.gamma = gamma - - def __call__(self, value, clip=None): - if clip is None: - clip = self.clip - - result, is_scalar = self.process_value(value) - - self.autoscale_None(result) - gamma = self.gamma - vmin, vmax = self.vmin, self.vmax - if vmin > vmax: - raise ValueError("minvalue must be less than or equal to maxvalue") - elif vmin == vmax: - result.fill(0) - else: - if clip: - mask = np.ma.getmask(result) - result = np.ma.array(np.clip(result.filled(vmax), vmin, vmax), - mask=mask) - resdat = result.data - resdat -= vmin - resdat[resdat < 0] = 0 - np.power(resdat, gamma, resdat) - resdat /= (vmax - vmin) ** gamma - - result = np.ma.array(resdat, mask=result.mask, copy=False) - if is_scalar: - result = result[0] - return result - - def inverse(self, value): - if not self.scaled(): - raise ValueError("Not invertible until scaled") - gamma = self.gamma - vmin, vmax = self.vmin, self.vmax - - if np.iterable(value): - val = np.ma.asarray(value) - return np.ma.power(val, 1. / gamma) * (vmax - vmin) + vmin - else: - return pow(value, 1. / gamma) * (vmax - vmin) + vmin - - -class BoundaryNorm(Normalize): - """ - Generate a colormap index based on discrete intervals. - - Unlike `Normalize` or `LogNorm`, `BoundaryNorm` maps values to integers - instead of to the interval 0-1. - """ - - # Mapping to the 0-1 interval could have been done via piece-wise linear - # interpolation, but using integers seems simpler, and reduces the number - # of conversions back and forth between int and float. - - def __init__(self, boundaries, ncolors, clip=False, *, extend='neither'): - """ - Parameters - ---------- - boundaries : array-like - Monotonically increasing sequence of at least 2 bin edges: data - falling in the n-th bin will be mapped to the n-th color. - - ncolors : int - Number of colors in the colormap to be used. - - clip : bool, optional - If clip is ``True``, out of range values are mapped to 0 if they - are below ``boundaries[0]`` or mapped to ``ncolors - 1`` if they - are above ``boundaries[-1]``. - - If clip is ``False``, out of range values are mapped to -1 if - they are below ``boundaries[0]`` or mapped to *ncolors* if they are - above ``boundaries[-1]``. These are then converted to valid indices - by `Colormap.__call__`. - - extend : {'neither', 'both', 'min', 'max'}, default: 'neither' - Extend the number of bins to include one or both of the - regions beyond the boundaries. For example, if ``extend`` - is 'min', then the color to which the region between the first - pair of boundaries is mapped will be distinct from the first - color in the colormap, and by default a - `~matplotlib.colorbar.Colorbar` will be drawn with - the triangle extension on the left or lower end. - - Notes - ----- - If there are fewer bins (including extensions) than colors, then the - color index is chosen by linearly interpolating the ``[0, nbins - 1]`` - range onto the ``[0, ncolors - 1]`` range, effectively skipping some - colors in the middle of the colormap. - """ - if clip and extend != 'neither': - raise ValueError("'clip=True' is not compatible with 'extend'") - super().__init__(vmin=boundaries[0], vmax=boundaries[-1], clip=clip) - self.boundaries = np.asarray(boundaries) - self.N = len(self.boundaries) - if self.N < 2: - raise ValueError("You must provide at least 2 boundaries " - f"(1 region) but you passed in {boundaries!r}") - self.Ncmap = ncolors - self.extend = extend - - self._scale = None # don't use the default scale. - - self._n_regions = self.N - 1 # number of colors needed - self._offset = 0 - if extend in ('min', 'both'): - self._n_regions += 1 - self._offset = 1 - if extend in ('max', 'both'): - self._n_regions += 1 - if self._n_regions > self.Ncmap: - raise ValueError(f"There are {self._n_regions} color bins " - "including extensions, but ncolors = " - f"{ncolors}; ncolors must equal or exceed the " - "number of bins") - - def __call__(self, value, clip=None): - """ - This method behaves similarly to `.Normalize.__call__`, except that it - returns integers or arrays of int16. - """ - if clip is None: - clip = self.clip - - xx, is_scalar = self.process_value(value) - mask = np.ma.getmaskarray(xx) - # Fill masked values a value above the upper boundary - xx = np.atleast_1d(xx.filled(self.vmax + 1)) - if clip: - np.clip(xx, self.vmin, self.vmax, out=xx) - max_col = self.Ncmap - 1 - else: - max_col = self.Ncmap - # this gives us the bins in the lookup table in the range - # [0, _n_regions - 1] (the offset is set in the init) - iret = np.digitize(xx, self.boundaries) - 1 + self._offset - # if we have more colors than regions, stretch the region - # index computed above to full range of the color bins. This - # will make use of the full range (but skip some of the colors - # in the middle) such that the first region is mapped to the - # first color and the last region is mapped to the last color. - if self.Ncmap > self._n_regions: - if self._n_regions == 1: - # special case the 1 region case, pick the middle color - iret[iret == 0] = (self.Ncmap - 1) // 2 - else: - # otherwise linearly remap the values from the region index - # to the color index spaces - iret = (self.Ncmap - 1) / (self._n_regions - 1) * iret - # cast to 16bit integers in all cases - iret = iret.astype(np.int16) - iret[xx < self.vmin] = -1 - iret[xx >= self.vmax] = max_col - ret = np.ma.array(iret, mask=mask) - if is_scalar: - ret = int(ret[0]) # assume python scalar - return ret - - def inverse(self, value): - """ - Raises - ------ - ValueError - BoundaryNorm is not invertible, so calling this method will always - raise an error - """ - raise ValueError("BoundaryNorm is not invertible") - - -class NoNorm(Normalize): - """ - Dummy replacement for `Normalize`, for the case where we want to use - indices directly in a `~matplotlib.cm.ScalarMappable`. - """ - def __call__(self, value, clip=None): - return value - - def inverse(self, value): - return value - - -def rgb_to_hsv(arr): - """ - Convert float RGB values (in the range [0, 1]), in a numpy array to HSV - values. - - Parameters - ---------- - arr : (..., 3) array-like - All values must be in the range [0, 1] - - Returns - ------- - (..., 3) `~numpy.ndarray` - Colors converted to HSV values in range [0, 1] - """ - arr = np.asarray(arr) - - # check length of the last dimension, should be _some_ sort of rgb - if arr.shape[-1] != 3: - raise ValueError("Last dimension of input array must be 3; " - "shape {} was found.".format(arr.shape)) - - in_shape = arr.shape - arr = np.array( - arr, copy=False, - dtype=np.promote_types(arr.dtype, np.float32), # Don't work on ints. - ndmin=2, # In case input was 1D. - ) - out = np.zeros_like(arr) - arr_max = arr.max(-1) - ipos = arr_max > 0 - delta = arr.ptp(-1) - s = np.zeros_like(delta) - s[ipos] = delta[ipos] / arr_max[ipos] - ipos = delta > 0 - # red is max - idx = (arr[..., 0] == arr_max) & ipos - out[idx, 0] = (arr[idx, 1] - arr[idx, 2]) / delta[idx] - # green is max - idx = (arr[..., 1] == arr_max) & ipos - out[idx, 0] = 2. + (arr[idx, 2] - arr[idx, 0]) / delta[idx] - # blue is max - idx = (arr[..., 2] == arr_max) & ipos - out[idx, 0] = 4. + (arr[idx, 0] - arr[idx, 1]) / delta[idx] - - out[..., 0] = (out[..., 0] / 6.0) % 1.0 - out[..., 1] = s - out[..., 2] = arr_max - - return out.reshape(in_shape) - - -def hsv_to_rgb(hsv): - """ - Convert HSV values to RGB. - - Parameters - ---------- - hsv : (..., 3) array-like - All values assumed to be in range [0, 1] - - Returns - ------- - (..., 3) `~numpy.ndarray` - Colors converted to RGB values in range [0, 1] - """ - hsv = np.asarray(hsv) - - # check length of the last dimension, should be _some_ sort of rgb - if hsv.shape[-1] != 3: - raise ValueError("Last dimension of input array must be 3; " - "shape {shp} was found.".format(shp=hsv.shape)) - - in_shape = hsv.shape - hsv = np.array( - hsv, copy=False, - dtype=np.promote_types(hsv.dtype, np.float32), # Don't work on ints. - ndmin=2, # In case input was 1D. - ) - - h = hsv[..., 0] - s = hsv[..., 1] - v = hsv[..., 2] - - r = np.empty_like(h) - g = np.empty_like(h) - b = np.empty_like(h) - - i = (h * 6.0).astype(int) - f = (h * 6.0) - i - p = v * (1.0 - s) - q = v * (1.0 - s * f) - t = v * (1.0 - s * (1.0 - f)) - - idx = i % 6 == 0 - r[idx] = v[idx] - g[idx] = t[idx] - b[idx] = p[idx] - - idx = i == 1 - r[idx] = q[idx] - g[idx] = v[idx] - b[idx] = p[idx] - - idx = i == 2 - r[idx] = p[idx] - g[idx] = v[idx] - b[idx] = t[idx] - - idx = i == 3 - r[idx] = p[idx] - g[idx] = q[idx] - b[idx] = v[idx] - - idx = i == 4 - r[idx] = t[idx] - g[idx] = p[idx] - b[idx] = v[idx] - - idx = i == 5 - r[idx] = v[idx] - g[idx] = p[idx] - b[idx] = q[idx] - - idx = s == 0 - r[idx] = v[idx] - g[idx] = v[idx] - b[idx] = v[idx] - - rgb = np.stack([r, g, b], axis=-1) - - return rgb.reshape(in_shape) - - -def _vector_magnitude(arr): - # things that don't work here: - # * np.linalg.norm: drops mask from ma.array - # * np.sum: drops mask from ma.array unless entire vector is masked - sum_sq = 0 - for i in range(arr.shape[-1]): - sum_sq += arr[..., i, np.newaxis] ** 2 - return np.sqrt(sum_sq) - - -class LightSource: - """ - Create a light source coming from the specified azimuth and elevation. - Angles are in degrees, with the azimuth measured - clockwise from north and elevation up from the zero plane of the surface. - - `shade` is used to produce "shaded" RGB values for a data array. - `shade_rgb` can be used to combine an RGB image with an elevation map. - `hillshade` produces an illumination map of a surface. - """ - - def __init__(self, azdeg=315, altdeg=45, hsv_min_val=0, hsv_max_val=1, - hsv_min_sat=1, hsv_max_sat=0): - """ - Specify the azimuth (measured clockwise from south) and altitude - (measured up from the plane of the surface) of the light source - in degrees. - - Parameters - ---------- - azdeg : float, default: 315 degrees (from the northwest) - The azimuth (0-360, degrees clockwise from North) of the light - source. - altdeg : float, default: 45 degrees - The altitude (0-90, degrees up from horizontal) of the light - source. - - Notes - ----- - For backwards compatibility, the parameters *hsv_min_val*, - *hsv_max_val*, *hsv_min_sat*, and *hsv_max_sat* may be supplied at - initialization as well. However, these parameters will only be used if - "blend_mode='hsv'" is passed into `shade` or `shade_rgb`. - See the documentation for `blend_hsv` for more details. - """ - self.azdeg = azdeg - self.altdeg = altdeg - self.hsv_min_val = hsv_min_val - self.hsv_max_val = hsv_max_val - self.hsv_min_sat = hsv_min_sat - self.hsv_max_sat = hsv_max_sat - - @property - def direction(self): - """The unit vector direction towards the light source.""" - # Azimuth is in degrees clockwise from North. Convert to radians - # counterclockwise from East (mathematical notation). - az = np.radians(90 - self.azdeg) - alt = np.radians(self.altdeg) - return np.array([ - np.cos(az) * np.cos(alt), - np.sin(az) * np.cos(alt), - np.sin(alt) - ]) - - def hillshade(self, elevation, vert_exag=1, dx=1, dy=1, fraction=1.): - """ - Calculate the illumination intensity for a surface using the defined - azimuth and elevation for the light source. - - This computes the normal vectors for the surface, and then passes them - on to `shade_normals` - - Parameters - ---------- - elevation : 2D array-like - The height values used to generate an illumination map - vert_exag : number, optional - The amount to exaggerate the elevation values by when calculating - illumination. This can be used either to correct for differences in - units between the x-y coordinate system and the elevation - coordinate system (e.g. decimal degrees vs. meters) or to - exaggerate or de-emphasize topographic effects. - dx : number, optional - The x-spacing (columns) of the input *elevation* grid. - dy : number, optional - The y-spacing (rows) of the input *elevation* grid. - fraction : number, optional - Increases or decreases the contrast of the hillshade. Values - greater than one will cause intermediate values to move closer to - full illumination or shadow (and clipping any values that move - beyond 0 or 1). Note that this is not visually or mathematically - the same as vertical exaggeration. - - Returns - ------- - `~numpy.ndarray` - A 2D array of illumination values between 0-1, where 0 is - completely in shadow and 1 is completely illuminated. - """ - - # Because most image and raster GIS data has the first row in the array - # as the "top" of the image, dy is implicitly negative. This is - # consistent to what `imshow` assumes, as well. - dy = -dy - - # compute the normal vectors from the partial derivatives - e_dy, e_dx = np.gradient(vert_exag * elevation, dy, dx) - - # .view is to keep subclasses - normal = np.empty(elevation.shape + (3,)).view(type(elevation)) - normal[..., 0] = -e_dx - normal[..., 1] = -e_dy - normal[..., 2] = 1 - normal /= _vector_magnitude(normal) - - return self.shade_normals(normal, fraction) - - def shade_normals(self, normals, fraction=1.): - """ - Calculate the illumination intensity for the normal vectors of a - surface using the defined azimuth and elevation for the light source. - - Imagine an artificial sun placed at infinity in some azimuth and - elevation position illuminating our surface. The parts of the surface - that slope toward the sun should brighten while those sides facing away - should become darker. - - Parameters - ---------- - fraction : number, optional - Increases or decreases the contrast of the hillshade. Values - greater than one will cause intermediate values to move closer to - full illumination or shadow (and clipping any values that move - beyond 0 or 1). Note that this is not visually or mathematically - the same as vertical exaggeration. - - Returns - ------- - `~numpy.ndarray` - A 2D array of illumination values between 0-1, where 0 is - completely in shadow and 1 is completely illuminated. - """ - - intensity = normals.dot(self.direction) - - # Apply contrast stretch - imin, imax = intensity.min(), intensity.max() - intensity *= fraction - - # Rescale to 0-1, keeping range before contrast stretch - # If constant slope, keep relative scaling (i.e. flat should be 0.5, - # fully occluded 0, etc.) - if (imax - imin) > 1e-6: - # Strictly speaking, this is incorrect. Negative values should be - # clipped to 0 because they're fully occluded. However, rescaling - # in this manner is consistent with the previous implementation and - # visually appears better than a "hard" clip. - intensity -= imin - intensity /= (imax - imin) - intensity = np.clip(intensity, 0, 1) - - return intensity - - def shade(self, data, cmap, norm=None, blend_mode='overlay', vmin=None, - vmax=None, vert_exag=1, dx=1, dy=1, fraction=1, **kwargs): - """ - Combine colormapped data values with an illumination intensity map - (a.k.a. "hillshade") of the values. - - Parameters - ---------- - data : 2D array-like - The height values used to generate a shaded map. - cmap : `~matplotlib.colors.Colormap` - The colormap used to color the *data* array. Note that this must be - a `~matplotlib.colors.Colormap` instance. For example, rather than - passing in ``cmap='gist_earth'``, use - ``cmap=plt.get_cmap('gist_earth')`` instead. - norm : `~matplotlib.colors.Normalize` instance, optional - The normalization used to scale values before colormapping. If - None, the input will be linearly scaled between its min and max. - blend_mode : {'hsv', 'overlay', 'soft'} or callable, optional - The type of blending used to combine the colormapped data - values with the illumination intensity. Default is - "overlay". Note that for most topographic surfaces, - "overlay" or "soft" appear more visually realistic. If a - user-defined function is supplied, it is expected to - combine an MxNx3 RGB array of floats (ranging 0 to 1) with - an MxNx1 hillshade array (also 0 to 1). (Call signature - ``func(rgb, illum, **kwargs)``) Additional kwargs supplied - to this function will be passed on to the *blend_mode* - function. - vmin : float or None, optional - The minimum value used in colormapping *data*. If *None* the - minimum value in *data* is used. If *norm* is specified, then this - argument will be ignored. - vmax : float or None, optional - The maximum value used in colormapping *data*. If *None* the - maximum value in *data* is used. If *norm* is specified, then this - argument will be ignored. - vert_exag : number, optional - The amount to exaggerate the elevation values by when calculating - illumination. This can be used either to correct for differences in - units between the x-y coordinate system and the elevation - coordinate system (e.g. decimal degrees vs. meters) or to - exaggerate or de-emphasize topography. - dx : number, optional - The x-spacing (columns) of the input *elevation* grid. - dy : number, optional - The y-spacing (rows) of the input *elevation* grid. - fraction : number, optional - Increases or decreases the contrast of the hillshade. Values - greater than one will cause intermediate values to move closer to - full illumination or shadow (and clipping any values that move - beyond 0 or 1). Note that this is not visually or mathematically - the same as vertical exaggeration. - **kwargs - Additional kwargs are passed on to the *blend_mode* function. - - Returns - ------- - `~numpy.ndarray` - An MxNx4 array of floats ranging between 0-1. - """ - if vmin is None: - vmin = data.min() - if vmax is None: - vmax = data.max() - if norm is None: - norm = Normalize(vmin=vmin, vmax=vmax) - - rgb0 = cmap(norm(data)) - rgb1 = self.shade_rgb(rgb0, elevation=data, blend_mode=blend_mode, - vert_exag=vert_exag, dx=dx, dy=dy, - fraction=fraction, **kwargs) - # Don't overwrite the alpha channel, if present. - rgb0[..., :3] = rgb1[..., :3] - return rgb0 - - def shade_rgb(self, rgb, elevation, fraction=1., blend_mode='hsv', - vert_exag=1, dx=1, dy=1, **kwargs): - """ - Use this light source to adjust the colors of the *rgb* input array to - give the impression of a shaded relief map with the given *elevation*. - - Parameters - ---------- - rgb : array-like - An (M, N, 3) RGB array, assumed to be in the range of 0 to 1. - elevation : array-like - An (M, N) array of the height values used to generate a shaded map. - fraction : number - Increases or decreases the contrast of the hillshade. Values - greater than one will cause intermediate values to move closer to - full illumination or shadow (and clipping any values that move - beyond 0 or 1). Note that this is not visually or mathematically - the same as vertical exaggeration. - blend_mode : {'hsv', 'overlay', 'soft'} or callable, optional - The type of blending used to combine the colormapped data values - with the illumination intensity. For backwards compatibility, this - defaults to "hsv". Note that for most topographic surfaces, - "overlay" or "soft" appear more visually realistic. If a - user-defined function is supplied, it is expected to combine an - MxNx3 RGB array of floats (ranging 0 to 1) with an MxNx1 hillshade - array (also 0 to 1). (Call signature - ``func(rgb, illum, **kwargs)``) - Additional kwargs supplied to this function will be passed on to - the *blend_mode* function. - vert_exag : number, optional - The amount to exaggerate the elevation values by when calculating - illumination. This can be used either to correct for differences in - units between the x-y coordinate system and the elevation - coordinate system (e.g. decimal degrees vs. meters) or to - exaggerate or de-emphasize topography. - dx : number, optional - The x-spacing (columns) of the input *elevation* grid. - dy : number, optional - The y-spacing (rows) of the input *elevation* grid. - **kwargs - Additional kwargs are passed on to the *blend_mode* function. - - Returns - ------- - `~numpy.ndarray` - An (m, n, 3) array of floats ranging between 0-1. - """ - # Calculate the "hillshade" intensity. - intensity = self.hillshade(elevation, vert_exag, dx, dy, fraction) - intensity = intensity[..., np.newaxis] - - # Blend the hillshade and rgb data using the specified mode - lookup = { - 'hsv': self.blend_hsv, - 'soft': self.blend_soft_light, - 'overlay': self.blend_overlay, - } - if blend_mode in lookup: - blend = lookup[blend_mode](rgb, intensity, **kwargs) - else: - try: - blend = blend_mode(rgb, intensity, **kwargs) - except TypeError as err: - raise ValueError('"blend_mode" must be callable or one of {}' - .format(lookup.keys)) from err - - # Only apply result where hillshade intensity isn't masked - if np.ma.is_masked(intensity): - mask = intensity.mask[..., 0] - for i in range(3): - blend[..., i][mask] = rgb[..., i][mask] - - return blend - - def blend_hsv(self, rgb, intensity, hsv_max_sat=None, hsv_max_val=None, - hsv_min_val=None, hsv_min_sat=None): - """ - Take the input data array, convert to HSV values in the given colormap, - then adjust those color values to give the impression of a shaded - relief map with a specified light source. RGBA values are returned, - which can then be used to plot the shaded image with imshow. - - The color of the resulting image will be darkened by moving the (s, v) - values (in HSV colorspace) toward (hsv_min_sat, hsv_min_val) in the - shaded regions, or lightened by sliding (s, v) toward (hsv_max_sat, - hsv_max_val) in regions that are illuminated. The default extremes are - chose so that completely shaded points are nearly black (s = 1, v = 0) - and completely illuminated points are nearly white (s = 0, v = 1). - - Parameters - ---------- - rgb : `~numpy.ndarray` - An MxNx3 RGB array of floats ranging from 0 to 1 (color image). - intensity : `~numpy.ndarray` - An MxNx1 array of floats ranging from 0 to 1 (grayscale image). - hsv_max_sat : number, default: 1 - The maximum saturation value that the *intensity* map can shift the - output image to. - hsv_min_sat : number, optional - The minimum saturation value that the *intensity* map can shift the - output image to. Defaults to 0. - hsv_max_val : number, optional - The maximum value ("v" in "hsv") that the *intensity* map can shift - the output image to. Defaults to 1. - hsv_min_val : number, optional - The minimum value ("v" in "hsv") that the *intensity* map can shift - the output image to. Defaults to 0. - - Returns - ------- - `~numpy.ndarray` - An MxNx3 RGB array representing the combined images. - """ - # Backward compatibility... - if hsv_max_sat is None: - hsv_max_sat = self.hsv_max_sat - if hsv_max_val is None: - hsv_max_val = self.hsv_max_val - if hsv_min_sat is None: - hsv_min_sat = self.hsv_min_sat - if hsv_min_val is None: - hsv_min_val = self.hsv_min_val - - # Expects a 2D intensity array scaled between -1 to 1... - intensity = intensity[..., 0] - intensity = 2 * intensity - 1 - - # Convert to rgb, then rgb to hsv - hsv = rgb_to_hsv(rgb[:, :, 0:3]) - hue, sat, val = np.moveaxis(hsv, -1, 0) - - # Modify hsv values (in place) to simulate illumination. - # putmask(A, mask, B) <=> A[mask] = B[mask] - np.putmask(sat, (np.abs(sat) > 1.e-10) & (intensity > 0), - (1 - intensity) * sat + intensity * hsv_max_sat) - np.putmask(sat, (np.abs(sat) > 1.e-10) & (intensity < 0), - (1 + intensity) * sat - intensity * hsv_min_sat) - np.putmask(val, intensity > 0, - (1 - intensity) * val + intensity * hsv_max_val) - np.putmask(val, intensity < 0, - (1 + intensity) * val - intensity * hsv_min_val) - np.clip(hsv[:, :, 1:], 0, 1, out=hsv[:, :, 1:]) - - # Convert modified hsv back to rgb. - return hsv_to_rgb(hsv) - - def blend_soft_light(self, rgb, intensity): - """ - Combine an RGB image with an intensity map using "soft light" blending, - using the "pegtop" formula. - - Parameters - ---------- - rgb : `~numpy.ndarray` - An MxNx3 RGB array of floats ranging from 0 to 1 (color image). - intensity : `~numpy.ndarray` - An MxNx1 array of floats ranging from 0 to 1 (grayscale image). - - Returns - ------- - `~numpy.ndarray` - An MxNx3 RGB array representing the combined images. - """ - return 2 * intensity * rgb + (1 - 2 * intensity) * rgb**2 - - def blend_overlay(self, rgb, intensity): - """ - Combine an RGB image with an intensity map using "overlay" blending. - - Parameters - ---------- - rgb : `~numpy.ndarray` - An MxNx3 RGB array of floats ranging from 0 to 1 (color image). - intensity : `~numpy.ndarray` - An MxNx1 array of floats ranging from 0 to 1 (grayscale image). - - Returns - ------- - ndarray - An MxNx3 RGB array representing the combined images. - """ - low = 2 * intensity * rgb - high = 1 - 2 * (1 - intensity) * (1 - rgb) - return np.where(rgb <= 0.5, low, high) - - -def from_levels_and_colors(levels, colors, extend='neither'): - """ - A helper routine to generate a cmap and a norm instance which - behave similar to contourf's levels and colors arguments. - - Parameters - ---------- - levels : sequence of numbers - The quantization levels used to construct the `BoundaryNorm`. - Value ``v`` is quantized to level ``i`` if ``lev[i] <= v < lev[i+1]``. - colors : sequence of colors - The fill color to use for each level. If *extend* is "neither" there - must be ``n_level - 1`` colors. For an *extend* of "min" or "max" add - one extra color, and for an *extend* of "both" add two colors. - extend : {'neither', 'min', 'max', 'both'}, optional - The behaviour when a value falls out of range of the given levels. - See `~.Axes.contourf` for details. - - Returns - ------- - cmap : `~matplotlib.colors.Normalize` - norm : `~matplotlib.colors.Colormap` - """ - slice_map = { - 'both': slice(1, -1), - 'min': slice(1, None), - 'max': slice(0, -1), - 'neither': slice(0, None), - } - _api.check_in_list(slice_map, extend=extend) - color_slice = slice_map[extend] - - n_data_colors = len(levels) - 1 - n_expected = n_data_colors + color_slice.start - (color_slice.stop or 0) - if len(colors) != n_expected: - raise ValueError( - f'With extend == {extend!r} and {len(levels)} levels, ' - f'expected {n_expected} colors, but got {len(colors)}') - - cmap = ListedColormap(colors[color_slice], N=n_data_colors) - - if extend in ['min', 'both']: - cmap.set_under(colors[0]) - else: - cmap.set_under('none') - - if extend in ['max', 'both']: - cmap.set_over(colors[-1]) - else: - cmap.set_over('none') - - cmap.colorbar_extend = extend - - norm = BoundaryNorm(levels, ncolors=n_data_colors) - return cmap, norm diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/container.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/container.py deleted file mode 100644 index a58e55c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/container.py +++ /dev/null @@ -1,142 +0,0 @@ -from matplotlib import cbook -from matplotlib.artist import Artist - - -class Container(tuple): - """ - Base class for containers. - - Containers are classes that collect semantically related Artists such as - the bars of a bar plot. - """ - - def __repr__(self): - return ("<{} object of {} artists>" - .format(type(self).__name__, len(self))) - - def __new__(cls, *args, **kwargs): - return tuple.__new__(cls, args[0]) - - def __init__(self, kl, label=None): - self._callbacks = cbook.CallbackRegistry(signals=["pchanged"]) - self._remove_method = None - self.set_label(label) - - def remove(self): - for c in cbook.flatten( - self, scalarp=lambda x: isinstance(x, Artist)): - if c is not None: - c.remove() - if self._remove_method: - self._remove_method(self) - - def get_children(self): - return [child for child in cbook.flatten(self) if child is not None] - - get_label = Artist.get_label - set_label = Artist.set_label - add_callback = Artist.add_callback - remove_callback = Artist.remove_callback - pchanged = Artist.pchanged - - -class BarContainer(Container): - """ - Container for the artists of bar plots (e.g. created by `.Axes.bar`). - - The container can be treated as a tuple of the *patches* themselves. - Additionally, you can access these and further parameters by the - attributes. - - Attributes - ---------- - patches : list of :class:`~matplotlib.patches.Rectangle` - The artists of the bars. - - errorbar : None or :class:`~matplotlib.container.ErrorbarContainer` - A container for the error bar artists if error bars are present. - *None* otherwise. - - datavalues : None or array-like - The underlying data values corresponding to the bars. - - orientation : {'vertical', 'horizontal'}, default: None - If 'vertical', the bars are assumed to be vertical. - If 'horizontal', the bars are assumed to be horizontal. - - """ - - def __init__(self, patches, errorbar=None, *, datavalues=None, - orientation=None, **kwargs): - self.patches = patches - self.errorbar = errorbar - self.datavalues = datavalues - self.orientation = orientation - super().__init__(patches, **kwargs) - - -class ErrorbarContainer(Container): - """ - Container for the artists of error bars (e.g. created by `.Axes.errorbar`). - - The container can be treated as the *lines* tuple itself. - Additionally, you can access these and further parameters by the - attributes. - - Attributes - ---------- - lines : tuple - Tuple of ``(data_line, caplines, barlinecols)``. - - - data_line : :class:`~matplotlib.lines.Line2D` instance of - x, y plot markers and/or line. - - caplines : tuple of :class:`~matplotlib.lines.Line2D` instances of - the error bar caps. - - barlinecols : list of :class:`~matplotlib.collections.LineCollection` - with the horizontal and vertical error ranges. - - has_xerr, has_yerr : bool - ``True`` if the errorbar has x/y errors. - - """ - - def __init__(self, lines, has_xerr=False, has_yerr=False, **kwargs): - self.lines = lines - self.has_xerr = has_xerr - self.has_yerr = has_yerr - super().__init__(lines, **kwargs) - - -class StemContainer(Container): - """ - Container for the artists created in a :meth:`.Axes.stem` plot. - - The container can be treated like a namedtuple ``(markerline, stemlines, - baseline)``. - - Attributes - ---------- - markerline : :class:`~matplotlib.lines.Line2D` - The artist of the markers at the stem heads. - - stemlines : list of :class:`~matplotlib.lines.Line2D` - The artists of the vertical lines for all stems. - - baseline : :class:`~matplotlib.lines.Line2D` - The artist of the horizontal baseline. - """ - def __init__(self, markerline_stemlines_baseline, **kwargs): - """ - Parameters - ---------- - markerline_stemlines_baseline : tuple - Tuple of ``(markerline, stemlines, baseline)``. - ``markerline`` contains the `.LineCollection` of the markers, - ``stemlines`` is a `.LineCollection` of the main lines, - ``baseline`` is the `.Line2D` of the baseline. - """ - markerline, stemlines, baseline = markerline_stemlines_baseline - self.markerline = markerline - self.stemlines = stemlines - self.baseline = baseline - super().__init__(markerline_stemlines_baseline, **kwargs) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/contour.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/contour.py deleted file mode 100644 index ecb2547..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/contour.py +++ /dev/null @@ -1,1783 +0,0 @@ -""" -Classes to support contour plotting and labelling for the Axes class. -""" - -import functools -from numbers import Integral - -import numpy as np -from numpy import ma - -import matplotlib as mpl -from matplotlib import _api, _docstring -from matplotlib.backend_bases import MouseButton -from matplotlib.text import Text -import matplotlib.path as mpath -import matplotlib.ticker as ticker -import matplotlib.cm as cm -import matplotlib.colors as mcolors -import matplotlib.collections as mcoll -import matplotlib.font_manager as font_manager -import matplotlib.cbook as cbook -import matplotlib.patches as mpatches -import matplotlib.transforms as mtransforms - - -# We can't use a single line collection for contour because a line -# collection can have only a single line style, and we want to be able to have -# dashed negative contours, for example, and solid positive contours. -# We could use a single polygon collection for filled contours, but it -# seems better to keep line and filled contours similar, with one collection -# per level. - - -@_api.deprecated("3.7", alternative="Text.set_transform_rotates_text") -class ClabelText(Text): - """ - Unlike the ordinary text, the get_rotation returns an updated - angle in the pixel coordinate assuming that the input rotation is - an angle in data coordinate (or whatever transform set). - """ - - def get_rotation(self): - new_angle, = self.get_transform().transform_angles( - [super().get_rotation()], [self.get_position()]) - return new_angle - - -def _contour_labeler_event_handler(cs, inline, inline_spacing, event): - canvas = cs.axes.figure.canvas - is_button = event.name == "button_press_event" - is_key = event.name == "key_press_event" - # Quit (even if not in infinite mode; this is consistent with - # MATLAB and sometimes quite useful, but will require the user to - # test how many points were actually returned before using data). - if (is_button and event.button == MouseButton.MIDDLE - or is_key and event.key in ["escape", "enter"]): - canvas.stop_event_loop() - # Pop last click. - elif (is_button and event.button == MouseButton.RIGHT - or is_key and event.key in ["backspace", "delete"]): - # Unfortunately, if one is doing inline labels, then there is currently - # no way to fix the broken contour - once humpty-dumpty is broken, he - # can't be put back together. In inline mode, this does nothing. - if not inline: - cs.pop_label() - canvas.draw() - # Add new click. - elif (is_button and event.button == MouseButton.LEFT - # On macOS/gtk, some keys return None. - or is_key and event.key is not None): - if event.inaxes == cs.axes: - cs.add_label_near(event.x, event.y, transform=False, - inline=inline, inline_spacing=inline_spacing) - canvas.draw() - - -class ContourLabeler: - """Mixin to provide labelling capability to `.ContourSet`.""" - - def clabel(self, levels=None, *, - fontsize=None, inline=True, inline_spacing=5, fmt=None, - colors=None, use_clabeltext=False, manual=False, - rightside_up=True, zorder=None): - """ - Label a contour plot. - - Adds labels to line contours in this `.ContourSet` (which inherits from - this mixin class). - - Parameters - ---------- - levels : array-like, optional - A list of level values, that should be labeled. The list must be - a subset of ``cs.levels``. If not given, all levels are labeled. - - fontsize : str or float, default: :rc:`font.size` - Size in points or relative size e.g., 'smaller', 'x-large'. - See `.Text.set_size` for accepted string values. - - colors : color or colors or None, default: None - The label colors: - - - If *None*, the color of each label matches the color of - the corresponding contour. - - - If one string color, e.g., *colors* = 'r' or *colors* = - 'red', all labels will be plotted in this color. - - - If a tuple of colors (string, float, rgb, etc), different labels - will be plotted in different colors in the order specified. - - inline : bool, default: True - If ``True`` the underlying contour is removed where the label is - placed. - - inline_spacing : float, default: 5 - Space in pixels to leave on each side of label when placing inline. - - This spacing will be exact for labels at locations where the - contour is straight, less so for labels on curved contours. - - fmt : `.Formatter` or str or callable or dict, optional - How the levels are formatted: - - - If a `.Formatter`, it is used to format all levels at once, using - its `.Formatter.format_ticks` method. - - If a str, it is interpreted as a %-style format string. - - If a callable, it is called with one level at a time and should - return the corresponding label. - - If a dict, it should directly map levels to labels. - - The default is to use a standard `.ScalarFormatter`. - - manual : bool or iterable, default: False - If ``True``, contour labels will be placed manually using - mouse clicks. Click the first button near a contour to - add a label, click the second button (or potentially both - mouse buttons at once) to finish adding labels. The third - button can be used to remove the last label added, but - only if labels are not inline. Alternatively, the keyboard - can be used to select label locations (enter to end label - placement, delete or backspace act like the third mouse button, - and any other key will select a label location). - - *manual* can also be an iterable object of (x, y) tuples. - Contour labels will be created as if mouse is clicked at each - (x, y) position. - - rightside_up : bool, default: True - If ``True``, label rotations will always be plus - or minus 90 degrees from level. - - use_clabeltext : bool, default: False - If ``True``, use `.Text.set_transform_rotates_text` to ensure that - label rotation is updated whenever the axes aspect changes. - - zorder : float or None, default: ``(2 + contour.get_zorder())`` - zorder of the contour labels. - - Returns - ------- - labels - A list of `.Text` instances for the labels. - """ - - # clabel basically takes the input arguments and uses them to - # add a list of "label specific" attributes to the ContourSet - # object. These attributes are all of the form label* and names - # should be fairly self explanatory. - # - # Once these attributes are set, clabel passes control to the - # labels method (case of automatic label placement) or - # `BlockingContourLabeler` (case of manual label placement). - - if fmt is None: - fmt = ticker.ScalarFormatter(useOffset=False) - fmt.create_dummy_axis() - self.labelFmt = fmt - self._use_clabeltext = use_clabeltext - # Detect if manual selection is desired and remove from argument list. - self.labelManual = manual - self.rightside_up = rightside_up - if zorder is None: - self._clabel_zorder = 2+self._contour_zorder - else: - self._clabel_zorder = zorder - - if levels is None: - levels = self.levels - indices = list(range(len(self.cvalues))) - else: - levlabs = list(levels) - indices, levels = [], [] - for i, lev in enumerate(self.levels): - if lev in levlabs: - indices.append(i) - levels.append(lev) - if len(levels) < len(levlabs): - raise ValueError(f"Specified levels {levlabs} don't match " - f"available levels {self.levels}") - self.labelLevelList = levels - self.labelIndiceList = indices - - self._label_font_props = font_manager.FontProperties(size=fontsize) - - if colors is None: - self.labelMappable = self - self.labelCValueList = np.take(self.cvalues, self.labelIndiceList) - else: - cmap = mcolors.ListedColormap(colors, N=len(self.labelLevelList)) - self.labelCValueList = list(range(len(self.labelLevelList))) - self.labelMappable = cm.ScalarMappable(cmap=cmap, - norm=mcolors.NoNorm()) - - self.labelXYs = [] - - if np.iterable(manual): - for x, y in manual: - self.add_label_near(x, y, inline, inline_spacing) - elif manual: - print('Select label locations manually using first mouse button.') - print('End manual selection with second mouse button.') - if not inline: - print('Remove last label by clicking third mouse button.') - mpl._blocking_input.blocking_input_loop( - self.axes.figure, ["button_press_event", "key_press_event"], - timeout=-1, handler=functools.partial( - _contour_labeler_event_handler, - self, inline, inline_spacing)) - else: - self.labels(inline, inline_spacing) - - return cbook.silent_list('text.Text', self.labelTexts) - - @_api.deprecated("3.7", alternative="cs.labelTexts[0].get_font()") - @property - def labelFontProps(self): - return self._label_font_props - - @_api.deprecated("3.7", alternative=( - "[cs.labelTexts[0].get_font().get_size()] * len(cs.labelLevelList)")) - @property - def labelFontSizeList(self): - return [self._label_font_props.get_size()] * len(self.labelLevelList) - - @_api.deprecated("3.7", alternative="cs.labelTexts") - @property - def labelTextsList(self): - return cbook.silent_list('text.Text', self.labelTexts) - - def print_label(self, linecontour, labelwidth): - """Return whether a contour is long enough to hold a label.""" - return (len(linecontour) > 10 * labelwidth - or (np.ptp(linecontour, axis=0) > 1.2 * labelwidth).any()) - - def too_close(self, x, y, lw): - """Return whether a label is already near this location.""" - thresh = (1.2 * lw) ** 2 - return any((x - loc[0]) ** 2 + (y - loc[1]) ** 2 < thresh - for loc in self.labelXYs) - - def _get_nth_label_width(self, nth): - """Return the width of the *nth* label, in pixels.""" - fig = self.axes.figure - renderer = fig._get_renderer() - return (Text(0, 0, - self.get_text(self.labelLevelList[nth], self.labelFmt), - figure=fig, fontproperties=self._label_font_props) - .get_window_extent(renderer).width) - - @_api.deprecated("3.7", alternative="Artist.set") - def set_label_props(self, label, text, color): - """Set the label properties - color, fontsize, text.""" - label.set_text(text) - label.set_color(color) - label.set_fontproperties(self._label_font_props) - label.set_clip_box(self.axes.bbox) - - def get_text(self, lev, fmt): - """Get the text of the label.""" - if isinstance(lev, str): - return lev - elif isinstance(fmt, dict): - return fmt.get(lev, '%1.3f') - elif callable(getattr(fmt, "format_ticks", None)): - return fmt.format_ticks([*self.labelLevelList, lev])[-1] - elif callable(fmt): - return fmt(lev) - else: - return fmt % lev - - def locate_label(self, linecontour, labelwidth): - """ - Find good place to draw a label (relatively flat part of the contour). - """ - ctr_size = len(linecontour) - n_blocks = int(np.ceil(ctr_size / labelwidth)) if labelwidth > 1 else 1 - block_size = ctr_size if n_blocks == 1 else int(labelwidth) - # Split contour into blocks of length ``block_size``, filling the last - # block by cycling the contour start (per `np.resize` semantics). (Due - # to cycling, the index returned is taken modulo ctr_size.) - xx = np.resize(linecontour[:, 0], (n_blocks, block_size)) - yy = np.resize(linecontour[:, 1], (n_blocks, block_size)) - yfirst = yy[:, :1] - ylast = yy[:, -1:] - xfirst = xx[:, :1] - xlast = xx[:, -1:] - s = (yfirst - yy) * (xlast - xfirst) - (xfirst - xx) * (ylast - yfirst) - l = np.hypot(xlast - xfirst, ylast - yfirst) - # Ignore warning that divide by zero throws, as this is a valid option - with np.errstate(divide='ignore', invalid='ignore'): - distances = (abs(s) / l).sum(axis=-1) - # Labels are drawn in the middle of the block (``hbsize``) where the - # contour is the closest (per ``distances``) to a straight line, but - # not `too_close()` to a preexisting label. - hbsize = block_size // 2 - adist = np.argsort(distances) - # If all candidates are `too_close()`, go back to the straightest part - # (``adist[0]``). - for idx in np.append(adist, adist[0]): - x, y = xx[idx, hbsize], yy[idx, hbsize] - if not self.too_close(x, y, labelwidth): - break - return x, y, (idx * block_size + hbsize) % ctr_size - - def calc_label_rot_and_inline(self, slc, ind, lw, lc=None, spacing=5): - """ - Calculate the appropriate label rotation given the linecontour - coordinates in screen units, the index of the label location and the - label width. - - If *lc* is not None or empty, also break contours and compute - inlining. - - *spacing* is the empty space to leave around the label, in pixels. - - Both tasks are done together to avoid calculating path lengths - multiple times, which is relatively costly. - - The method used here involves computing the path length along the - contour in pixel coordinates and then looking approximately (label - width / 2) away from central point to determine rotation and then to - break contour if desired. - """ - - if lc is None: - lc = [] - # Half the label width - hlw = lw / 2.0 - - # Check if closed and, if so, rotate contour so label is at edge - closed = _is_closed_polygon(slc) - if closed: - slc = np.concatenate([slc[ind:-1], slc[:ind + 1]]) - if len(lc): # Rotate lc also if not empty - lc = np.concatenate([lc[ind:-1], lc[:ind + 1]]) - ind = 0 - - # Calculate path lengths - pl = np.zeros(slc.shape[0], dtype=float) - dx = np.diff(slc, axis=0) - pl[1:] = np.cumsum(np.hypot(dx[:, 0], dx[:, 1])) - pl = pl - pl[ind] - - # Use linear interpolation to get points around label - xi = np.array([-hlw, hlw]) - if closed: # Look at end also for closed contours - dp = np.array([pl[-1], 0]) - else: - dp = np.zeros_like(xi) - - # Get angle of vector between the two ends of the label - must be - # calculated in pixel space for text rotation to work correctly. - (dx,), (dy,) = (np.diff(np.interp(dp + xi, pl, slc_col)) - for slc_col in slc.T) - rotation = np.rad2deg(np.arctan2(dy, dx)) - - if self.rightside_up: - # Fix angle so text is never upside-down - rotation = (rotation + 90) % 180 - 90 - - # Break contour if desired - nlc = [] - if len(lc): - # Expand range by spacing - xi = dp + xi + np.array([-spacing, spacing]) - - # Get (integer) indices near points of interest; use -1 as marker - # for out of bounds. - I = np.interp(xi, pl, np.arange(len(pl)), left=-1, right=-1) - I = [np.floor(I[0]).astype(int), np.ceil(I[1]).astype(int)] - if I[0] != -1: - xy1 = [np.interp(xi[0], pl, lc_col) for lc_col in lc.T] - if I[1] != -1: - xy2 = [np.interp(xi[1], pl, lc_col) for lc_col in lc.T] - - # Actually break contours - if closed: - # This will remove contour if shorter than label - if all(i != -1 for i in I): - nlc.append(np.row_stack([xy2, lc[I[1]:I[0]+1], xy1])) - else: - # These will remove pieces of contour if they have length zero - if I[0] != -1: - nlc.append(np.row_stack([lc[:I[0]+1], xy1])) - if I[1] != -1: - nlc.append(np.row_stack([xy2, lc[I[1]:]])) - - # The current implementation removes contours completely - # covered by labels. Uncomment line below to keep - # original contour if this is the preferred behavior. - # if not len(nlc): nlc = [ lc ] - - return rotation, nlc - - def add_label(self, x, y, rotation, lev, cvalue): - """Add contour label without `.Text.set_transform_rotates_text`.""" - data_x, data_y = self.axes.transData.inverted().transform((x, y)) - t = Text( - data_x, data_y, - text=self.get_text(lev, self.labelFmt), - rotation=rotation, - horizontalalignment='center', verticalalignment='center', - zorder=self._clabel_zorder, - color=self.labelMappable.to_rgba(cvalue, alpha=self.alpha), - fontproperties=self._label_font_props, - clip_box=self.axes.bbox) - self.labelTexts.append(t) - self.labelCValues.append(cvalue) - self.labelXYs.append((x, y)) - # Add label to plot here - useful for manual mode label selection - self.axes.add_artist(t) - - def add_label_clabeltext(self, x, y, rotation, lev, cvalue): - """Add contour label with `.Text.set_transform_rotates_text`.""" - self.add_label(x, y, rotation, lev, cvalue) - # Grab the last added text, and reconfigure its rotation. - t = self.labelTexts[-1] - data_rotation, = self.axes.transData.inverted().transform_angles( - [rotation], [[x, y]]) - t.set(rotation=data_rotation, transform_rotates_text=True) - - def add_label_near(self, x, y, inline=True, inline_spacing=5, - transform=None): - """ - Add a label near the point ``(x, y)``. - - Parameters - ---------- - x, y : float - The approximate location of the label. - inline : bool, default: True - If *True* remove the segment of the contour beneath the label. - inline_spacing : int, default: 5 - Space in pixels to leave on each side of label when placing - inline. This spacing will be exact for labels at locations where - the contour is straight, less so for labels on curved contours. - transform : `.Transform` or `False`, default: ``self.axes.transData`` - A transform applied to ``(x, y)`` before labeling. The default - causes ``(x, y)`` to be interpreted as data coordinates. `False` - is a synonym for `.IdentityTransform`; i.e. ``(x, y)`` should be - interpreted as display coordinates. - """ - - if transform is None: - transform = self.axes.transData - if transform: - x, y = transform.transform((x, y)) - - # find the nearest contour _in screen units_ - conmin, segmin, imin, xmin, ymin = self.find_nearest_contour( - x, y, self.labelIndiceList)[:5] - - # calc_label_rot_and_inline() requires that (xmin, ymin) - # be a vertex in the path. So, if it isn't, add a vertex here - paths = self.collections[conmin].get_paths() # paths of correct coll. - lc = paths[segmin].vertices # vertices of correct segment - # Where should the new vertex be added in data-units? - xcmin = self.axes.transData.inverted().transform([xmin, ymin]) - if not np.allclose(xcmin, lc[imin]): - # No vertex is close enough, so add a new point in the vertices and - # replace the path by the new one. - lc = np.insert(lc, imin, xcmin, axis=0) - paths[segmin] = mpath.Path(lc) - - # Get index of nearest level in subset of levels used for labeling - lmin = self.labelIndiceList.index(conmin) - - # Get label width for rotating labels and breaking contours - lw = self._get_nth_label_width(lmin) - - # Figure out label rotation. - rotation, nlc = self.calc_label_rot_and_inline( - self.axes.transData.transform(lc), # to pixel space. - imin, lw, lc if inline else None, inline_spacing) - - self.add_label(xmin, ymin, rotation, self.labelLevelList[lmin], - self.labelCValueList[lmin]) - - if inline: - # Remove old, not looping over paths so we can do this up front - paths.pop(segmin) - - # Add paths if not empty or single point - paths.extend([mpath.Path(n) for n in nlc if len(n) > 1]) - - def pop_label(self, index=-1): - """Defaults to removing last label, but any index can be supplied""" - self.labelCValues.pop(index) - t = self.labelTexts.pop(index) - t.remove() - - def labels(self, inline, inline_spacing): - - if self._use_clabeltext: - add_label = self.add_label_clabeltext - else: - add_label = self.add_label - - for idx, (icon, lev, cvalue) in enumerate(zip( - self.labelIndiceList, - self.labelLevelList, - self.labelCValueList, - )): - - con = self.collections[icon] - trans = con.get_transform() - lw = self._get_nth_label_width(idx) - additions = [] - paths = con.get_paths() - for segNum, linepath in enumerate(paths): - lc = linepath.vertices # Line contour - slc = trans.transform(lc) # Line contour in screen coords - - # Check if long enough for a label - if self.print_label(slc, lw): - x, y, ind = self.locate_label(slc, lw) - - rotation, new = self.calc_label_rot_and_inline( - slc, ind, lw, lc if inline else None, inline_spacing) - - # Actually add the label - add_label(x, y, rotation, lev, cvalue) - - # If inline, add new contours - if inline: - for n in new: - # Add path if not empty or single point - if len(n) > 1: - additions.append(mpath.Path(n)) - else: # If not adding label, keep old path - additions.append(linepath) - - # After looping over all segments on a contour, replace old paths - # by new ones if inlining. - if inline: - paths[:] = additions - - def remove(self): - for text in self.labelTexts: - text.remove() - - -def _is_closed_polygon(X): - """ - Return whether first and last object in a sequence are the same. These are - presumably coordinates on a polygonal curve, in which case this function - tests if that curve is closed. - """ - return np.allclose(X[0], X[-1], rtol=1e-10, atol=1e-13) - - -def _find_closest_point_on_path(xys, p): - """ - Parameters - ---------- - xys : (N, 2) array-like - Coordinates of vertices. - p : (float, float) - Coordinates of point. - - Returns - ------- - d2min : float - Minimum square distance of *p* to *xys*. - proj : (float, float) - Projection of *p* onto *xys*. - imin : (int, int) - Consecutive indices of vertices of segment in *xys* where *proj* is. - Segments are considered as including their end-points; i.e. if the - closest point on the path is a node in *xys* with index *i*, this - returns ``(i-1, i)``. For the special case where *xys* is a single - point, this returns ``(0, 0)``. - """ - if len(xys) == 1: - return (((p - xys[0]) ** 2).sum(), xys[0], (0, 0)) - dxys = xys[1:] - xys[:-1] # Individual segment vectors. - norms = (dxys ** 2).sum(axis=1) - norms[norms == 0] = 1 # For zero-length segment, replace 0/0 by 0/1. - rel_projs = np.clip( # Project onto each segment in relative 0-1 coords. - ((p - xys[:-1]) * dxys).sum(axis=1) / norms, - 0, 1)[:, None] - projs = xys[:-1] + rel_projs * dxys # Projs. onto each segment, in (x, y). - d2s = ((projs - p) ** 2).sum(axis=1) # Squared distances. - imin = np.argmin(d2s) - return (d2s[imin], projs[imin], (imin, imin+1)) - - -_docstring.interpd.update(contour_set_attributes=r""" -Attributes ----------- -ax : `~matplotlib.axes.Axes` - The Axes object in which the contours are drawn. - -collections : `.silent_list` of `.PathCollection`\s - The `.Artist`\s representing the contour. This is a list of - `.PathCollection`\s for both line and filled contours. - -levels : array - The values of the contour levels. - -layers : array - Same as levels for line contours; half-way between - levels for filled contours. See ``ContourSet._process_colors``. -""") - - -@_docstring.dedent_interpd -class ContourSet(cm.ScalarMappable, ContourLabeler): - """ - Store a set of contour lines or filled regions. - - User-callable method: `~.Axes.clabel` - - Parameters - ---------- - ax : `~matplotlib.axes.Axes` - - levels : [level0, level1, ..., leveln] - A list of floating point numbers indicating the contour levels. - - allsegs : [level0segs, level1segs, ...] - List of all the polygon segments for all the *levels*. - For contour lines ``len(allsegs) == len(levels)``, and for - filled contour regions ``len(allsegs) = len(levels)-1``. The lists - should look like :: - - level0segs = [polygon0, polygon1, ...] - polygon0 = [[x0, y0], [x1, y1], ...] - - allkinds : ``None`` or [level0kinds, level1kinds, ...] - Optional list of all the polygon vertex kinds (code types), as - described and used in Path. This is used to allow multiply- - connected paths such as holes within filled polygons. - If not ``None``, ``len(allkinds) == len(allsegs)``. The lists - should look like :: - - level0kinds = [polygon0kinds, ...] - polygon0kinds = [vertexcode0, vertexcode1, ...] - - If *allkinds* is not ``None``, usually all polygons for a - particular contour level are grouped together so that - ``level0segs = [polygon0]`` and ``level0kinds = [polygon0kinds]``. - - **kwargs - Keyword arguments are as described in the docstring of - `~.Axes.contour`. - - %(contour_set_attributes)s - """ - - def __init__(self, ax, *args, - levels=None, filled=False, linewidths=None, linestyles=None, - hatches=(None,), alpha=None, origin=None, extent=None, - cmap=None, colors=None, norm=None, vmin=None, vmax=None, - extend='neither', antialiased=None, nchunk=0, locator=None, - transform=None, negative_linestyles=None, - **kwargs): - """ - Draw contour lines or filled regions, depending on - whether keyword arg *filled* is ``False`` (default) or ``True``. - - Call signature:: - - ContourSet(ax, levels, allsegs, [allkinds], **kwargs) - - Parameters - ---------- - ax : `~matplotlib.axes.Axes` - The `~.axes.Axes` object to draw on. - - levels : [level0, level1, ..., leveln] - A list of floating point numbers indicating the contour - levels. - - allsegs : [level0segs, level1segs, ...] - List of all the polygon segments for all the *levels*. - For contour lines ``len(allsegs) == len(levels)``, and for - filled contour regions ``len(allsegs) = len(levels)-1``. The lists - should look like :: - - level0segs = [polygon0, polygon1, ...] - polygon0 = [[x0, y0], [x1, y1], ...] - - allkinds : [level0kinds, level1kinds, ...], optional - Optional list of all the polygon vertex kinds (code types), as - described and used in Path. This is used to allow multiply- - connected paths such as holes within filled polygons. - If not ``None``, ``len(allkinds) == len(allsegs)``. The lists - should look like :: - - level0kinds = [polygon0kinds, ...] - polygon0kinds = [vertexcode0, vertexcode1, ...] - - If *allkinds* is not ``None``, usually all polygons for a - particular contour level are grouped together so that - ``level0segs = [polygon0]`` and ``level0kinds = [polygon0kinds]``. - - **kwargs - Keyword arguments are as described in the docstring of - `~.Axes.contour`. - """ - self.axes = ax - self.levels = levels - self.filled = filled - self.linewidths = linewidths - self.linestyles = linestyles - self.hatches = hatches - self.alpha = alpha - self.origin = origin - self.extent = extent - self.colors = colors - self.extend = extend - self.antialiased = antialiased - if self.antialiased is None and self.filled: - # Eliminate artifacts; we are not stroking the boundaries. - self.antialiased = False - # The default for line contours will be taken from the - # LineCollection default, which uses :rc:`lines.antialiased`. - - self.nchunk = nchunk - self.locator = locator - if (isinstance(norm, mcolors.LogNorm) - or isinstance(self.locator, ticker.LogLocator)): - self.logscale = True - if norm is None: - norm = mcolors.LogNorm() - else: - self.logscale = False - - _api.check_in_list([None, 'lower', 'upper', 'image'], origin=origin) - if self.extent is not None and len(self.extent) != 4: - raise ValueError( - "If given, 'extent' must be None or (x0, x1, y0, y1)") - if self.colors is not None and cmap is not None: - raise ValueError('Either colors or cmap must be None') - if self.origin == 'image': - self.origin = mpl.rcParams['image.origin'] - - self._transform = transform - - self.negative_linestyles = negative_linestyles - # If negative_linestyles was not defined as a keyword argument, define - # negative_linestyles with rcParams - if self.negative_linestyles is None: - self.negative_linestyles = \ - mpl.rcParams['contour.negative_linestyle'] - - kwargs = self._process_args(*args, **kwargs) - self._process_levels() - - self._extend_min = self.extend in ['min', 'both'] - self._extend_max = self.extend in ['max', 'both'] - if self.colors is not None: - ncolors = len(self.levels) - if self.filled: - ncolors -= 1 - i0 = 0 - - # Handle the case where colors are given for the extended - # parts of the contour. - - use_set_under_over = False - # if we are extending the lower end, and we've been given enough - # colors then skip the first color in the resulting cmap. For the - # extend_max case we don't need to worry about passing more colors - # than ncolors as ListedColormap will clip. - total_levels = (ncolors + - int(self._extend_min) + - int(self._extend_max)) - if (len(self.colors) == total_levels and - (self._extend_min or self._extend_max)): - use_set_under_over = True - if self._extend_min: - i0 = 1 - - cmap = mcolors.ListedColormap(self.colors[i0:None], N=ncolors) - - if use_set_under_over: - if self._extend_min: - cmap.set_under(self.colors[0]) - if self._extend_max: - cmap.set_over(self.colors[-1]) - - self.collections = cbook.silent_list(None) - - # label lists must be initialized here - self.labelTexts = [] - self.labelCValues = [] - - kw = {'cmap': cmap} - if norm is not None: - kw['norm'] = norm - # sets self.cmap, norm if needed; - cm.ScalarMappable.__init__(self, **kw) - if vmin is not None: - self.norm.vmin = vmin - if vmax is not None: - self.norm.vmax = vmax - self._process_colors() - - if getattr(self, 'allsegs', None) is None: - self.allsegs, self.allkinds = self._get_allsegs_and_allkinds() - elif self.allkinds is None: - # allsegs specified in constructor may or may not have allkinds as - # well. Must ensure allkinds can be zipped below. - self.allkinds = [None] * len(self.allsegs) - - if self.filled: - if self.linewidths is not None: - _api.warn_external('linewidths is ignored by contourf') - # Lower and upper contour levels. - lowers, uppers = self._get_lowers_and_uppers() - # Default zorder taken from Collection - self._contour_zorder = kwargs.pop('zorder', 1) - - self.collections[:] = [ - mcoll.PathCollection( - self._make_paths(segs, kinds), - antialiaseds=(self.antialiased,), - edgecolors='none', - alpha=self.alpha, - transform=self.get_transform(), - zorder=self._contour_zorder) - for level, level_upper, segs, kinds - in zip(lowers, uppers, self.allsegs, self.allkinds)] - else: - self.tlinewidths = tlinewidths = self._process_linewidths() - tlinestyles = self._process_linestyles() - aa = self.antialiased - if aa is not None: - aa = (self.antialiased,) - # Default zorder taken from LineCollection, which is higher than - # for filled contours so that lines are displayed on top. - self._contour_zorder = kwargs.pop('zorder', 2) - - self.collections[:] = [ - mcoll.PathCollection( - self._make_paths(segs, kinds), - facecolors="none", - antialiaseds=aa, - linewidths=width, - linestyles=[lstyle], - alpha=self.alpha, - transform=self.get_transform(), - zorder=self._contour_zorder, - label='_nolegend_') - for level, width, lstyle, segs, kinds - in zip(self.levels, tlinewidths, tlinestyles, self.allsegs, - self.allkinds)] - - for col in self.collections: - self.axes.add_collection(col, autolim=False) - col.sticky_edges.x[:] = [self._mins[0], self._maxs[0]] - col.sticky_edges.y[:] = [self._mins[1], self._maxs[1]] - self.axes.update_datalim([self._mins, self._maxs]) - self.axes.autoscale_view(tight=True) - - self.changed() # set the colors - - if kwargs: - _api.warn_external( - 'The following kwargs were not used by contour: ' + - ", ".join(map(repr, kwargs)) - ) - - def get_transform(self): - """Return the `.Transform` instance used by this ContourSet.""" - if self._transform is None: - self._transform = self.axes.transData - elif (not isinstance(self._transform, mtransforms.Transform) - and hasattr(self._transform, '_as_mpl_transform')): - self._transform = self._transform._as_mpl_transform(self.axes) - return self._transform - - def __getstate__(self): - state = self.__dict__.copy() - # the C object _contour_generator cannot currently be pickled. This - # isn't a big issue as it is not actually used once the contour has - # been calculated. - state['_contour_generator'] = None - return state - - def legend_elements(self, variable_name='x', str_format=str): - """ - Return a list of artists and labels suitable for passing through - to `~.Axes.legend` which represent this ContourSet. - - The labels have the form "0 < x <= 1" stating the data ranges which - the artists represent. - - Parameters - ---------- - variable_name : str - The string used inside the inequality used on the labels. - str_format : function: float -> str - Function used to format the numbers in the labels. - - Returns - ------- - artists : list[`.Artist`] - A list of the artists. - labels : list[str] - A list of the labels. - """ - artists = [] - labels = [] - - if self.filled: - lowers, uppers = self._get_lowers_and_uppers() - n_levels = len(self.collections) - - for i, (collection, lower, upper) in enumerate( - zip(self.collections, lowers, uppers)): - patch = mpatches.Rectangle( - (0, 0), 1, 1, - facecolor=collection.get_facecolor()[0], - hatch=collection.get_hatch(), - alpha=collection.get_alpha()) - artists.append(patch) - - lower = str_format(lower) - upper = str_format(upper) - - if i == 0 and self.extend in ('min', 'both'): - labels.append(fr'${variable_name} \leq {lower}s$') - elif i == n_levels - 1 and self.extend in ('max', 'both'): - labels.append(fr'${variable_name} > {upper}s$') - else: - labels.append(fr'${lower} < {variable_name} \leq {upper}$') - else: - for collection, level in zip(self.collections, self.levels): - - patch = mcoll.LineCollection(None) - patch.update_from(collection) - - artists.append(patch) - # format the level for insertion into the labels - level = str_format(level) - labels.append(fr'${variable_name} = {level}$') - - return artists, labels - - def _process_args(self, *args, **kwargs): - """ - Process *args* and *kwargs*; override in derived classes. - - Must set self.levels, self.zmin and self.zmax, and update axes limits. - """ - self.levels = args[0] - self.allsegs = args[1] - self.allkinds = args[2] if len(args) > 2 else None - self.zmax = np.max(self.levels) - self.zmin = np.min(self.levels) - - # Check lengths of levels and allsegs. - if self.filled: - if len(self.allsegs) != len(self.levels) - 1: - raise ValueError('must be one less number of segments as ' - 'levels') - else: - if len(self.allsegs) != len(self.levels): - raise ValueError('must be same number of segments as levels') - - # Check length of allkinds. - if (self.allkinds is not None and - len(self.allkinds) != len(self.allsegs)): - raise ValueError('allkinds has different length to allsegs') - - # Determine x, y bounds and update axes data limits. - flatseglist = [s for seg in self.allsegs for s in seg] - points = np.concatenate(flatseglist, axis=0) - self._mins = points.min(axis=0) - self._maxs = points.max(axis=0) - - return kwargs - - def _get_allsegs_and_allkinds(self): - """Compute ``allsegs`` and ``allkinds`` using C extension.""" - allsegs = [] - allkinds = [] - if self.filled: - lowers, uppers = self._get_lowers_and_uppers() - for level, level_upper in zip(lowers, uppers): - vertices, kinds = \ - self._contour_generator.create_filled_contour( - level, level_upper) - allsegs.append(vertices) - allkinds.append(kinds) - else: - for level in self.levels: - vertices, kinds = self._contour_generator.create_contour(level) - allsegs.append(vertices) - allkinds.append(kinds) - return allsegs, allkinds - - def _get_lowers_and_uppers(self): - """ - Return ``(lowers, uppers)`` for filled contours. - """ - lowers = self._levels[:-1] - if self.zmin == lowers[0]: - # Include minimum values in lowest interval - lowers = lowers.copy() # so we don't change self._levels - if self.logscale: - lowers[0] = 0.99 * self.zmin - else: - lowers[0] -= 1 - uppers = self._levels[1:] - return (lowers, uppers) - - def _make_paths(self, segs, kinds): - """ - Create and return Path objects for the specified segments and optional - kind codes. *segs* is a list of numpy arrays, each array is either a - closed line loop or open line strip of 2D points with a shape of - (npoints, 2). *kinds* is either None or a list (with the same length - as *segs*) of numpy arrays, each array is of shape (npoints,) and - contains the kind codes for the corresponding line in *segs*. If - *kinds* is None then the Path constructor creates the kind codes - assuming that the line is an open strip. - """ - if kinds is None: - return [mpath.Path(seg) for seg in segs] - else: - return [mpath.Path(seg, codes=kind) for seg, kind - in zip(segs, kinds)] - - def changed(self): - if not hasattr(self, "cvalues"): - # Just return after calling the super() changed function - cm.ScalarMappable.changed(self) - return - # Force an autoscale immediately because self.to_rgba() calls - # autoscale_None() internally with the data passed to it, - # so if vmin/vmax are not set yet, this would override them with - # content from *cvalues* rather than levels like we want - self.norm.autoscale_None(self.levels) - tcolors = [(tuple(rgba),) - for rgba in self.to_rgba(self.cvalues, alpha=self.alpha)] - self.tcolors = tcolors - hatches = self.hatches * len(tcolors) - for color, hatch, collection in zip(tcolors, hatches, - self.collections): - if self.filled: - collection.set_facecolor(color) - # update the collection's hatch (may be None) - collection.set_hatch(hatch) - else: - collection.set_edgecolor(color) - for label, cv in zip(self.labelTexts, self.labelCValues): - label.set_alpha(self.alpha) - label.set_color(self.labelMappable.to_rgba(cv)) - # add label colors - cm.ScalarMappable.changed(self) - - def _autolev(self, N): - """ - Select contour levels to span the data. - - The target number of levels, *N*, is used only when the - scale is not log and default locator is used. - - We need two more levels for filled contours than for - line contours, because for the latter we need to specify - the lower and upper boundary of each range. For example, - a single contour boundary, say at z = 0, requires only - one contour line, but two filled regions, and therefore - three levels to provide boundaries for both regions. - """ - if self.locator is None: - if self.logscale: - self.locator = ticker.LogLocator() - else: - self.locator = ticker.MaxNLocator(N + 1, min_n_ticks=1) - - lev = self.locator.tick_values(self.zmin, self.zmax) - - try: - if self.locator._symmetric: - return lev - except AttributeError: - pass - - # Trim excess levels the locator may have supplied. - under = np.nonzero(lev < self.zmin)[0] - i0 = under[-1] if len(under) else 0 - over = np.nonzero(lev > self.zmax)[0] - i1 = over[0] + 1 if len(over) else len(lev) - if self.extend in ('min', 'both'): - i0 += 1 - if self.extend in ('max', 'both'): - i1 -= 1 - - if i1 - i0 < 3: - i0, i1 = 0, len(lev) - - return lev[i0:i1] - - def _process_contour_level_args(self, args, z_dtype): - """ - Determine the contour levels and store in self.levels. - """ - if self.levels is None: - if args: - levels_arg = args[0] - elif np.issubdtype(z_dtype, bool): - if self.filled: - levels_arg = [0, .5, 1] - else: - levels_arg = [.5] - else: - levels_arg = 7 # Default, hard-wired. - else: - levels_arg = self.levels - if isinstance(levels_arg, Integral): - self.levels = self._autolev(levels_arg) - else: - self.levels = np.asarray(levels_arg, np.float64) - if self.filled and len(self.levels) < 2: - raise ValueError("Filled contours require at least 2 levels.") - if len(self.levels) > 1 and np.min(np.diff(self.levels)) <= 0.0: - raise ValueError("Contour levels must be increasing") - - def _process_levels(self): - """ - Assign values to :attr:`layers` based on :attr:`levels`, - adding extended layers as needed if contours are filled. - - For line contours, layers simply coincide with levels; - a line is a thin layer. No extended levels are needed - with line contours. - """ - # Make a private _levels to include extended regions; we - # want to leave the original levels attribute unchanged. - # (Colorbar needs this even for line contours.) - self._levels = list(self.levels) - - if self.logscale: - lower, upper = 1e-250, 1e250 - else: - lower, upper = -1e250, 1e250 - - if self.extend in ('both', 'min'): - self._levels.insert(0, lower) - if self.extend in ('both', 'max'): - self._levels.append(upper) - self._levels = np.asarray(self._levels) - - if not self.filled: - self.layers = self.levels - return - - # Layer values are mid-way between levels in screen space. - if self.logscale: - # Avoid overflow by taking sqrt before multiplying. - self.layers = (np.sqrt(self._levels[:-1]) - * np.sqrt(self._levels[1:])) - else: - self.layers = 0.5 * (self._levels[:-1] + self._levels[1:]) - - def _process_colors(self): - """ - Color argument processing for contouring. - - Note that we base the colormapping on the contour levels - and layers, not on the actual range of the Z values. This - means we don't have to worry about bad values in Z, and we - always have the full dynamic range available for the selected - levels. - - The color is based on the midpoint of the layer, except for - extended end layers. By default, the norm vmin and vmax - are the extreme values of the non-extended levels. Hence, - the layer color extremes are not the extreme values of - the colormap itself, but approach those values as the number - of levels increases. An advantage of this scheme is that - line contours, when added to filled contours, take on - colors that are consistent with those of the filled regions; - for example, a contour line on the boundary between two - regions will have a color intermediate between those - of the regions. - - """ - self.monochrome = self.cmap.monochrome - if self.colors is not None: - # Generate integers for direct indexing. - i0, i1 = 0, len(self.levels) - if self.filled: - i1 -= 1 - # Out of range indices for over and under: - if self.extend in ('both', 'min'): - i0 -= 1 - if self.extend in ('both', 'max'): - i1 += 1 - self.cvalues = list(range(i0, i1)) - self.set_norm(mcolors.NoNorm()) - else: - self.cvalues = self.layers - self.set_array(self.levels) - self.autoscale_None() - if self.extend in ('both', 'max', 'min'): - self.norm.clip = False - - # self.tcolors are set by the "changed" method - - def _process_linewidths(self): - linewidths = self.linewidths - Nlev = len(self.levels) - if linewidths is None: - default_linewidth = mpl.rcParams['contour.linewidth'] - if default_linewidth is None: - default_linewidth = mpl.rcParams['lines.linewidth'] - tlinewidths = [(default_linewidth,)] * Nlev - else: - if not np.iterable(linewidths): - linewidths = [linewidths] * Nlev - else: - linewidths = list(linewidths) - if len(linewidths) < Nlev: - nreps = int(np.ceil(Nlev / len(linewidths))) - linewidths = linewidths * nreps - if len(linewidths) > Nlev: - linewidths = linewidths[:Nlev] - tlinewidths = [(w,) for w in linewidths] - return tlinewidths - - def _process_linestyles(self): - linestyles = self.linestyles - Nlev = len(self.levels) - if linestyles is None: - tlinestyles = ['solid'] * Nlev - if self.monochrome: - eps = - (self.zmax - self.zmin) * 1e-15 - for i, lev in enumerate(self.levels): - if lev < eps: - tlinestyles[i] = self.negative_linestyles - else: - if isinstance(linestyles, str): - tlinestyles = [linestyles] * Nlev - elif np.iterable(linestyles): - tlinestyles = list(linestyles) - if len(tlinestyles) < Nlev: - nreps = int(np.ceil(Nlev / len(linestyles))) - tlinestyles = tlinestyles * nreps - if len(tlinestyles) > Nlev: - tlinestyles = tlinestyles[:Nlev] - else: - raise ValueError("Unrecognized type for linestyles kwarg") - return tlinestyles - - def get_alpha(self): - """Return alpha to be applied to all ContourSet artists.""" - return self.alpha - - def set_alpha(self, alpha): - """ - Set the alpha blending value for all ContourSet artists. - *alpha* must be between 0 (transparent) and 1 (opaque). - """ - self.alpha = alpha - self.changed() - - def find_nearest_contour(self, x, y, indices=None, pixel=True): - """ - Find the point in the contour plot that is closest to ``(x, y)``. - - This method does not support filled contours. - - Parameters - ---------- - x, y : float - The reference point. - indices : list of int or None, default: None - Indices of contour levels to consider. If None (the default), all - levels are considered. - pixel : bool, default: True - If *True*, measure distance in pixel (screen) space, which is - useful for manual contour labeling; else, measure distance in axes - space. - - Returns - ------- - contour : `.Collection` - The contour that is closest to ``(x, y)``. - segment : int - The index of the `.Path` in *contour* that is closest to - ``(x, y)``. - index : int - The index of the path segment in *segment* that is closest to - ``(x, y)``. - xmin, ymin : float - The point in the contour plot that is closest to ``(x, y)``. - d2 : float - The squared distance from ``(xmin, ymin)`` to ``(x, y)``. - """ - - # This function uses a method that is probably quite - # inefficient based on converting each contour segment to - # pixel coordinates and then comparing the given point to - # those coordinates for each contour. This will probably be - # quite slow for complex contours, but for normal use it works - # sufficiently well that the time is not noticeable. - # Nonetheless, improvements could probably be made. - - if self.filled: - raise ValueError("Method does not support filled contours.") - - if indices is None: - indices = range(len(self.collections)) - - d2min = np.inf - conmin = None - segmin = None - imin = None - xmin = None - ymin = None - - point = np.array([x, y]) - - for icon in indices: - con = self.collections[icon] - trans = con.get_transform() - paths = con.get_paths() - - for segNum, linepath in enumerate(paths): - lc = linepath.vertices - # transfer all data points to screen coordinates if desired - if pixel: - lc = trans.transform(lc) - - d2, xc, leg = _find_closest_point_on_path(lc, point) - if d2 < d2min: - d2min = d2 - conmin = icon - segmin = segNum - imin = leg[1] - xmin = xc[0] - ymin = xc[1] - - return (conmin, segmin, imin, xmin, ymin, d2min) - - def remove(self): - super().remove() - for coll in self.collections: - coll.remove() - - -@_docstring.dedent_interpd -class QuadContourSet(ContourSet): - """ - Create and store a set of contour lines or filled regions. - - This class is typically not instantiated directly by the user but by - `~.Axes.contour` and `~.Axes.contourf`. - - %(contour_set_attributes)s - """ - - def _process_args(self, *args, corner_mask=None, algorithm=None, **kwargs): - """ - Process args and kwargs. - """ - if isinstance(args[0], QuadContourSet): - if self.levels is None: - self.levels = args[0].levels - self.zmin = args[0].zmin - self.zmax = args[0].zmax - self._corner_mask = args[0]._corner_mask - contour_generator = args[0]._contour_generator - self._mins = args[0]._mins - self._maxs = args[0]._maxs - self._algorithm = args[0]._algorithm - else: - import contourpy - - if algorithm is None: - algorithm = mpl.rcParams['contour.algorithm'] - mpl.rcParams.validate["contour.algorithm"](algorithm) - self._algorithm = algorithm - - if corner_mask is None: - if self._algorithm == "mpl2005": - # mpl2005 does not support corner_mask=True so if not - # specifically requested then disable it. - corner_mask = False - else: - corner_mask = mpl.rcParams['contour.corner_mask'] - self._corner_mask = corner_mask - - x, y, z = self._contour_args(args, kwargs) - - contour_generator = contourpy.contour_generator( - x, y, z, name=self._algorithm, corner_mask=self._corner_mask, - line_type=contourpy.LineType.SeparateCode, - fill_type=contourpy.FillType.OuterCode, - chunk_size=self.nchunk) - - t = self.get_transform() - - # if the transform is not trans data, and some part of it - # contains transData, transform the xs and ys to data coordinates - if (t != self.axes.transData and - any(t.contains_branch_seperately(self.axes.transData))): - trans_to_data = t - self.axes.transData - pts = np.vstack([x.flat, y.flat]).T - transformed_pts = trans_to_data.transform(pts) - x = transformed_pts[..., 0] - y = transformed_pts[..., 1] - - self._mins = [ma.min(x), ma.min(y)] - self._maxs = [ma.max(x), ma.max(y)] - - self._contour_generator = contour_generator - - return kwargs - - def _contour_args(self, args, kwargs): - if self.filled: - fn = 'contourf' - else: - fn = 'contour' - nargs = len(args) - if nargs <= 2: - z, *args = args - z = ma.asarray(z) - x, y = self._initialize_x_y(z) - elif nargs <= 4: - x, y, z_orig, *args = args - x, y, z = self._check_xyz(x, y, z_orig, kwargs) - else: - raise _api.nargs_error(fn, takes="from 1 to 4", given=nargs) - z = ma.masked_invalid(z, copy=False) - self.zmax = float(z.max()) - self.zmin = float(z.min()) - if self.logscale and self.zmin <= 0: - z = ma.masked_where(z <= 0, z) - _api.warn_external('Log scale: values of z <= 0 have been masked') - self.zmin = float(z.min()) - self._process_contour_level_args(args, z.dtype) - return (x, y, z) - - def _check_xyz(self, x, y, z, kwargs): - """ - Check that the shapes of the input arrays match; if x and y are 1D, - convert them to 2D using meshgrid. - """ - x, y = self.axes._process_unit_info([("x", x), ("y", y)], kwargs) - - x = np.asarray(x, dtype=np.float64) - y = np.asarray(y, dtype=np.float64) - z = ma.asarray(z) - - if z.ndim != 2: - raise TypeError(f"Input z must be 2D, not {z.ndim}D") - if z.shape[0] < 2 or z.shape[1] < 2: - raise TypeError(f"Input z must be at least a (2, 2) shaped array, " - f"but has shape {z.shape}") - Ny, Nx = z.shape - - if x.ndim != y.ndim: - raise TypeError(f"Number of dimensions of x ({x.ndim}) and y " - f"({y.ndim}) do not match") - if x.ndim == 1: - nx, = x.shape - ny, = y.shape - if nx != Nx: - raise TypeError(f"Length of x ({nx}) must match number of " - f"columns in z ({Nx})") - if ny != Ny: - raise TypeError(f"Length of y ({ny}) must match number of " - f"rows in z ({Ny})") - x, y = np.meshgrid(x, y) - elif x.ndim == 2: - if x.shape != z.shape: - raise TypeError( - f"Shapes of x {x.shape} and z {z.shape} do not match") - if y.shape != z.shape: - raise TypeError( - f"Shapes of y {y.shape} and z {z.shape} do not match") - else: - raise TypeError(f"Inputs x and y must be 1D or 2D, not {x.ndim}D") - - return x, y, z - - def _initialize_x_y(self, z): - """ - Return X, Y arrays such that contour(Z) will match imshow(Z) - if origin is not None. - The center of pixel Z[i, j] depends on origin: - if origin is None, x = j, y = i; - if origin is 'lower', x = j + 0.5, y = i + 0.5; - if origin is 'upper', x = j + 0.5, y = Nrows - i - 0.5 - If extent is not None, x and y will be scaled to match, - as in imshow. - If origin is None and extent is not None, then extent - will give the minimum and maximum values of x and y. - """ - if z.ndim != 2: - raise TypeError(f"Input z must be 2D, not {z.ndim}D") - elif z.shape[0] < 2 or z.shape[1] < 2: - raise TypeError(f"Input z must be at least a (2, 2) shaped array, " - f"but has shape {z.shape}") - else: - Ny, Nx = z.shape - if self.origin is None: # Not for image-matching. - if self.extent is None: - return np.meshgrid(np.arange(Nx), np.arange(Ny)) - else: - x0, x1, y0, y1 = self.extent - x = np.linspace(x0, x1, Nx) - y = np.linspace(y0, y1, Ny) - return np.meshgrid(x, y) - # Match image behavior: - if self.extent is None: - x0, x1, y0, y1 = (0, Nx, 0, Ny) - else: - x0, x1, y0, y1 = self.extent - dx = (x1 - x0) / Nx - dy = (y1 - y0) / Ny - x = x0 + (np.arange(Nx) + 0.5) * dx - y = y0 + (np.arange(Ny) + 0.5) * dy - if self.origin == 'upper': - y = y[::-1] - return np.meshgrid(x, y) - - -_docstring.interpd.update(contour_doc=""" -`.contour` and `.contourf` draw contour lines and filled contours, -respectively. Except as noted, function signatures and return values -are the same for both versions. - -Parameters ----------- -X, Y : array-like, optional - The coordinates of the values in *Z*. - - *X* and *Y* must both be 2D with the same shape as *Z* (e.g. - created via `numpy.meshgrid`), or they must both be 1-D such - that ``len(X) == N`` is the number of columns in *Z* and - ``len(Y) == M`` is the number of rows in *Z*. - - *X* and *Y* must both be ordered monotonically. - - If not given, they are assumed to be integer indices, i.e. - ``X = range(N)``, ``Y = range(M)``. - -Z : (M, N) array-like - The height values over which the contour is drawn. Color-mapping is - controlled by *cmap*, *norm*, *vmin*, and *vmax*. - -levels : int or array-like, optional - Determines the number and positions of the contour lines / regions. - - If an int *n*, use `~matplotlib.ticker.MaxNLocator`, which tries - to automatically choose no more than *n+1* "nice" contour levels - between minimum and maximum numeric values of *Z*. - - If array-like, draw contour lines at the specified levels. - The values must be in increasing order. - -Returns -------- -`~.contour.QuadContourSet` - -Other Parameters ----------------- -corner_mask : bool, default: :rc:`contour.corner_mask` - Enable/disable corner masking, which only has an effect if *Z* is - a masked array. If ``False``, any quad touching a masked point is - masked out. If ``True``, only the triangular corners of quads - nearest those points are always masked out, other triangular - corners comprising three unmasked points are contoured as usual. - -colors : color string or sequence of colors, optional - The colors of the levels, i.e. the lines for `.contour` and the - areas for `.contourf`. - - The sequence is cycled for the levels in ascending order. If the - sequence is shorter than the number of levels, it's repeated. - - As a shortcut, single color strings may be used in place of - one-element lists, i.e. ``'red'`` instead of ``['red']`` to color - all levels with the same color. This shortcut does only work for - color strings, not for other ways of specifying colors. - - By default (value *None*), the colormap specified by *cmap* - will be used. - -alpha : float, default: 1 - The alpha blending value, between 0 (transparent) and 1 (opaque). - -%(cmap_doc)s - - This parameter is ignored if *colors* is set. - -%(norm_doc)s - - This parameter is ignored if *colors* is set. - -%(vmin_vmax_doc)s - - If *vmin* or *vmax* are not given, the default color scaling is based on - *levels*. - - This parameter is ignored if *colors* is set. - -origin : {*None*, 'upper', 'lower', 'image'}, default: None - Determines the orientation and exact position of *Z* by specifying - the position of ``Z[0, 0]``. This is only relevant, if *X*, *Y* - are not given. - - - *None*: ``Z[0, 0]`` is at X=0, Y=0 in the lower left corner. - - 'lower': ``Z[0, 0]`` is at X=0.5, Y=0.5 in the lower left corner. - - 'upper': ``Z[0, 0]`` is at X=N+0.5, Y=0.5 in the upper left - corner. - - 'image': Use the value from :rc:`image.origin`. - -extent : (x0, x1, y0, y1), optional - If *origin* is not *None*, then *extent* is interpreted as in - `.imshow`: it gives the outer pixel boundaries. In this case, the - position of Z[0, 0] is the center of the pixel, not a corner. If - *origin* is *None*, then (*x0*, *y0*) is the position of Z[0, 0], - and (*x1*, *y1*) is the position of Z[-1, -1]. - - This argument is ignored if *X* and *Y* are specified in the call - to contour. - -locator : ticker.Locator subclass, optional - The locator is used to determine the contour levels if they - are not given explicitly via *levels*. - Defaults to `~.ticker.MaxNLocator`. - -extend : {'neither', 'both', 'min', 'max'}, default: 'neither' - Determines the ``contourf``-coloring of values that are outside the - *levels* range. - - If 'neither', values outside the *levels* range are not colored. - If 'min', 'max' or 'both', color the values below, above or below - and above the *levels* range. - - Values below ``min(levels)`` and above ``max(levels)`` are mapped - to the under/over values of the `.Colormap`. Note that most - colormaps do not have dedicated colors for these by default, so - that the over and under values are the edge values of the colormap. - You may want to set these values explicitly using - `.Colormap.set_under` and `.Colormap.set_over`. - - .. note:: - - An existing `.QuadContourSet` does not get notified if - properties of its colormap are changed. Therefore, an explicit - call `.QuadContourSet.changed()` is needed after modifying the - colormap. The explicit call can be left out, if a colorbar is - assigned to the `.QuadContourSet` because it internally calls - `.QuadContourSet.changed()`. - - Example:: - - x = np.arange(1, 10) - y = x.reshape(-1, 1) - h = x * y - - cs = plt.contourf(h, levels=[10, 30, 50], - colors=['#808080', '#A0A0A0', '#C0C0C0'], extend='both') - cs.cmap.set_over('red') - cs.cmap.set_under('blue') - cs.changed() - -xunits, yunits : registered units, optional - Override axis units by specifying an instance of a - :class:`matplotlib.units.ConversionInterface`. - -antialiased : bool, optional - Enable antialiasing, overriding the defaults. For - filled contours, the default is *False*. For line contours, - it is taken from :rc:`lines.antialiased`. - -nchunk : int >= 0, optional - If 0, no subdivision of the domain. Specify a positive integer to - divide the domain into subdomains of *nchunk* by *nchunk* quads. - Chunking reduces the maximum length of polygons generated by the - contouring algorithm which reduces the rendering workload passed - on to the backend and also requires slightly less RAM. It can - however introduce rendering artifacts at chunk boundaries depending - on the backend, the *antialiased* flag and value of *alpha*. - -linewidths : float or array-like, default: :rc:`contour.linewidth` - *Only applies to* `.contour`. - - The line width of the contour lines. - - If a number, all levels will be plotted with this linewidth. - - If a sequence, the levels in ascending order will be plotted with - the linewidths in the order specified. - - If None, this falls back to :rc:`lines.linewidth`. - -linestyles : {*None*, 'solid', 'dashed', 'dashdot', 'dotted'}, optional - *Only applies to* `.contour`. - - If *linestyles* is *None*, the default is 'solid' unless the lines are - monochrome. In that case, negative contours will instead take their - linestyle from the *negative_linestyles* argument. - - *linestyles* can also be an iterable of the above strings specifying a set - of linestyles to be used. If this iterable is shorter than the number of - contour levels it will be repeated as necessary. - -negative_linestyles : {*None*, 'solid', 'dashed', 'dashdot', 'dotted'}, \ - optional - *Only applies to* `.contour`. - - If *linestyles* is *None* and the lines are monochrome, this argument - specifies the line style for negative contours. - - If *negative_linestyles* is *None*, the default is taken from - :rc:`contour.negative_linestyles`. - - *negative_linestyles* can also be an iterable of the above strings - specifying a set of linestyles to be used. If this iterable is shorter than - the number of contour levels it will be repeated as necessary. - -hatches : list[str], optional - *Only applies to* `.contourf`. - - A list of cross hatch patterns to use on the filled areas. - If None, no hatching will be added to the contour. - Hatching is supported in the PostScript, PDF, SVG and Agg - backends only. - -algorithm : {'mpl2005', 'mpl2014', 'serial', 'threaded'}, optional - Which contouring algorithm to use to calculate the contour lines and - polygons. The algorithms are implemented in - `ContourPy `_, consult the - `ContourPy documentation `_ for - further information. - - The default is taken from :rc:`contour.algorithm`. - -data : indexable object, optional - DATA_PARAMETER_PLACEHOLDER - -Notes ------ -1. `.contourf` differs from the MATLAB version in that it does not draw - the polygon edges. To draw edges, add line contours with calls to - `.contour`. - -2. `.contourf` fills intervals that are closed at the top; that is, for - boundaries *z1* and *z2*, the filled region is:: - - z1 < Z <= z2 - - except for the lowest interval, which is closed on both sides (i.e. - it includes the lowest value). - -3. `.contour` and `.contourf` use a `marching squares - `_ algorithm to - compute contour locations. More information can be found in - `ContourPy documentation `_. -""" % _docstring.interpd.params) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/dates.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/dates.py deleted file mode 100644 index 2c2293e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/dates.py +++ /dev/null @@ -1,1942 +0,0 @@ -""" -Matplotlib provides sophisticated date plotting capabilities, standing on the -shoulders of python :mod:`datetime` and the add-on module dateutil_. - -By default, Matplotlib uses the units machinery described in -`~matplotlib.units` to convert `datetime.datetime`, and `numpy.datetime64` -objects when plotted on an x- or y-axis. The user does not -need to do anything for dates to be formatted, but dates often have strict -formatting needs, so this module provides many axis locators and formatters. -A basic example using `numpy.datetime64` is:: - - import numpy as np - - times = np.arange(np.datetime64('2001-01-02'), - np.datetime64('2002-02-03'), np.timedelta64(75, 'm')) - y = np.random.randn(len(times)) - - fig, ax = plt.subplots() - ax.plot(times, y) - -.. seealso:: - - - :doc:`/gallery/text_labels_and_annotations/date` - - :doc:`/gallery/ticks/date_concise_formatter` - - :doc:`/gallery/ticks/date_demo_convert` - -.. _date-format: - -Matplotlib date format ----------------------- - -Matplotlib represents dates using floating point numbers specifying the number -of days since a default epoch of 1970-01-01 UTC; for example, -1970-01-01, 06:00 is the floating point number 0.25. The formatters and -locators require the use of `datetime.datetime` objects, so only dates between -year 0001 and 9999 can be represented. Microsecond precision -is achievable for (approximately) 70 years on either side of the epoch, and -20 microseconds for the rest of the allowable range of dates (year 0001 to -9999). The epoch can be changed at import time via `.dates.set_epoch` or -:rc:`dates.epoch` to other dates if necessary; see -:doc:`/gallery/ticks/date_precision_and_epochs` for a discussion. - -.. note:: - - Before Matplotlib 3.3, the epoch was 0000-12-31 which lost modern - microsecond precision and also made the default axis limit of 0 an invalid - datetime. In 3.3 the epoch was changed as above. To convert old - ordinal floats to the new epoch, users can do:: - - new_ordinal = old_ordinal + mdates.date2num(np.datetime64('0000-12-31')) - - -There are a number of helper functions to convert between :mod:`datetime` -objects and Matplotlib dates: - -.. currentmodule:: matplotlib.dates - -.. autosummary:: - :nosignatures: - - datestr2num - date2num - num2date - num2timedelta - drange - set_epoch - get_epoch - -.. note:: - - Like Python's `datetime.datetime`, Matplotlib uses the Gregorian calendar - for all conversions between dates and floating point numbers. This practice - is not universal, and calendar differences can cause confusing - differences between what Python and Matplotlib give as the number of days - since 0001-01-01 and what other software and databases yield. For - example, the US Naval Observatory uses a calendar that switches - from Julian to Gregorian in October, 1582. Hence, using their - calculator, the number of days between 0001-01-01 and 2006-04-01 is - 732403, whereas using the Gregorian calendar via the datetime - module we find:: - - In [1]: date(2006, 4, 1).toordinal() - date(1, 1, 1).toordinal() - Out[1]: 732401 - -All the Matplotlib date converters, tickers and formatters are timezone aware. -If no explicit timezone is provided, :rc:`timezone` is assumed, provided as a -string. If you want to use a different timezone, pass the *tz* keyword -argument of `num2date` to any date tickers or locators you create. This can -be either a `datetime.tzinfo` instance or a string with the timezone name that -can be parsed by `~dateutil.tz.gettz`. - -A wide range of specific and general purpose date tick locators and -formatters are provided in this module. See -:mod:`matplotlib.ticker` for general information on tick locators -and formatters. These are described below. - -The dateutil_ module provides additional code to handle date ticking, making it -easy to place ticks on any kinds of dates. See examples below. - -.. _dateutil: https://dateutil.readthedocs.io - -Date tickers ------------- - -Most of the date tickers can locate single or multiple values. For example:: - - # import constants for the days of the week - from matplotlib.dates import MO, TU, WE, TH, FR, SA, SU - - # tick on Mondays every week - loc = WeekdayLocator(byweekday=MO, tz=tz) - - # tick on Mondays and Saturdays - loc = WeekdayLocator(byweekday=(MO, SA)) - -In addition, most of the constructors take an interval argument:: - - # tick on Mondays every second week - loc = WeekdayLocator(byweekday=MO, interval=2) - -The rrule locator allows completely general date ticking:: - - # tick every 5th easter - rule = rrulewrapper(YEARLY, byeaster=1, interval=5) - loc = RRuleLocator(rule) - -The available date tickers are: - -* `MicrosecondLocator`: Locate microseconds. - -* `SecondLocator`: Locate seconds. - -* `MinuteLocator`: Locate minutes. - -* `HourLocator`: Locate hours. - -* `DayLocator`: Locate specified days of the month. - -* `WeekdayLocator`: Locate days of the week, e.g., MO, TU. - -* `MonthLocator`: Locate months, e.g., 7 for July. - -* `YearLocator`: Locate years that are multiples of base. - -* `RRuleLocator`: Locate using a `rrulewrapper`. - `rrulewrapper` is a simple wrapper around dateutil_'s `dateutil.rrule` - which allow almost arbitrary date tick specifications. - See :doc:`rrule example `. - -* `AutoDateLocator`: On autoscale, this class picks the best `DateLocator` - (e.g., `RRuleLocator`) to set the view limits and the tick locations. If - called with ``interval_multiples=True`` it will make ticks line up with - sensible multiples of the tick intervals. For example, if the interval is - 4 hours, it will pick hours 0, 4, 8, etc. as ticks. This behaviour is not - guaranteed by default. - -Date formatters ---------------- - -The available date formatters are: - -* `AutoDateFormatter`: attempts to figure out the best format to use. This is - most useful when used with the `AutoDateLocator`. - -* `ConciseDateFormatter`: also attempts to figure out the best format to use, - and to make the format as compact as possible while still having complete - date information. This is most useful when used with the `AutoDateLocator`. - -* `DateFormatter`: use `~datetime.datetime.strftime` format strings. -""" - -import datetime -import functools -import logging -import math -import re - -from dateutil.rrule import (rrule, MO, TU, WE, TH, FR, SA, SU, YEARLY, - MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, - SECONDLY) -from dateutil.relativedelta import relativedelta -import dateutil.parser -import dateutil.tz -import numpy as np - -import matplotlib as mpl -from matplotlib import _api, cbook, ticker, units - -__all__ = ('datestr2num', 'date2num', 'num2date', 'num2timedelta', 'drange', - 'set_epoch', 'get_epoch', 'DateFormatter', 'ConciseDateFormatter', - 'AutoDateFormatter', 'DateLocator', 'RRuleLocator', - 'AutoDateLocator', 'YearLocator', 'MonthLocator', 'WeekdayLocator', - 'DayLocator', 'HourLocator', 'MinuteLocator', - 'SecondLocator', 'MicrosecondLocator', - 'rrule', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU', - 'YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY', - 'HOURLY', 'MINUTELY', 'SECONDLY', 'MICROSECONDLY', 'relativedelta', - 'DateConverter', 'ConciseDateConverter', 'rrulewrapper') - - -_log = logging.getLogger(__name__) -UTC = datetime.timezone.utc - - -@_api.caching_module_getattr -class __getattr__: - JULIAN_OFFSET = _api.deprecated("3.7")(property(lambda self: 1721424.5)) - # Julian date at 0000-12-31 - # note that the Julian day epoch is achievable w/ - # np.datetime64('-4713-11-24T12:00:00'); datetime64 is proleptic - # Gregorian and BC has a one-year offset. So - # np.datetime64('0000-12-31') - np.datetime64('-4713-11-24T12:00') = - # 1721424.5 - # Ref: https://en.wikipedia.org/wiki/Julian_day - - -def _get_tzinfo(tz=None): - """ - Generate `~datetime.tzinfo` from a string or return `~datetime.tzinfo`. - If None, retrieve the preferred timezone from the rcParams dictionary. - """ - if tz is None: - tz = mpl.rcParams['timezone'] - if tz == 'UTC': - return UTC - if isinstance(tz, str): - tzinfo = dateutil.tz.gettz(tz) - if tzinfo is None: - raise ValueError(f"{tz} is not a valid timezone as parsed by" - " dateutil.tz.gettz.") - return tzinfo - if isinstance(tz, datetime.tzinfo): - return tz - raise TypeError("tz must be string or tzinfo subclass.") - - -# Time-related constants. -EPOCH_OFFSET = float(datetime.datetime(1970, 1, 1).toordinal()) -# EPOCH_OFFSET is not used by matplotlib -MICROSECONDLY = SECONDLY + 1 -HOURS_PER_DAY = 24. -MIN_PER_HOUR = 60. -SEC_PER_MIN = 60. -MONTHS_PER_YEAR = 12. - -DAYS_PER_WEEK = 7. -DAYS_PER_MONTH = 30. -DAYS_PER_YEAR = 365.0 - -MINUTES_PER_DAY = MIN_PER_HOUR * HOURS_PER_DAY - -SEC_PER_HOUR = SEC_PER_MIN * MIN_PER_HOUR -SEC_PER_DAY = SEC_PER_HOUR * HOURS_PER_DAY -SEC_PER_WEEK = SEC_PER_DAY * DAYS_PER_WEEK - -MUSECONDS_PER_DAY = 1e6 * SEC_PER_DAY - -MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY = ( - MO, TU, WE, TH, FR, SA, SU) -WEEKDAYS = (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) - -# default epoch: passed to np.datetime64... -_epoch = None - - -def _reset_epoch_test_example(): - """ - Reset the Matplotlib date epoch so it can be set again. - - Only for use in tests and examples. - """ - global _epoch - _epoch = None - - -def set_epoch(epoch): - """ - Set the epoch (origin for dates) for datetime calculations. - - The default epoch is :rc:`dates.epoch` (by default 1970-01-01T00:00). - - If microsecond accuracy is desired, the date being plotted needs to be - within approximately 70 years of the epoch. Matplotlib internally - represents dates as days since the epoch, so floating point dynamic - range needs to be within a factor of 2^52. - - `~.dates.set_epoch` must be called before any dates are converted - (i.e. near the import section) or a RuntimeError will be raised. - - See also :doc:`/gallery/ticks/date_precision_and_epochs`. - - Parameters - ---------- - epoch : str - valid UTC date parsable by `numpy.datetime64` (do not include - timezone). - - """ - global _epoch - if _epoch is not None: - raise RuntimeError('set_epoch must be called before dates plotted.') - _epoch = epoch - - -def get_epoch(): - """ - Get the epoch used by `.dates`. - - Returns - ------- - epoch : str - String for the epoch (parsable by `numpy.datetime64`). - """ - global _epoch - - if _epoch is None: - _epoch = mpl.rcParams['date.epoch'] - return _epoch - - -def _dt64_to_ordinalf(d): - """ - Convert `numpy.datetime64` or an `numpy.ndarray` of those types to - Gregorian date as UTC float relative to the epoch (see `.get_epoch`). - Roundoff is float64 precision. Practically: microseconds for dates - between 290301 BC, 294241 AD, milliseconds for larger dates - (see `numpy.datetime64`). - """ - - # the "extra" ensures that we at least allow the dynamic range out to - # seconds. That should get out to +/-2e11 years. - dseconds = d.astype('datetime64[s]') - extra = (d - dseconds).astype('timedelta64[ns]') - t0 = np.datetime64(get_epoch(), 's') - dt = (dseconds - t0).astype(np.float64) - dt += extra.astype(np.float64) / 1.0e9 - dt = dt / SEC_PER_DAY - - NaT_int = np.datetime64('NaT').astype(np.int64) - d_int = d.astype(np.int64) - dt[d_int == NaT_int] = np.nan - return dt - - -def _from_ordinalf(x, tz=None): - """ - Convert Gregorian float of the date, preserving hours, minutes, - seconds and microseconds. Return value is a `.datetime`. - - The input date *x* is a float in ordinal days at UTC, and the output will - be the specified `.datetime` object corresponding to that time in - timezone *tz*, or if *tz* is ``None``, in the timezone specified in - :rc:`timezone`. - """ - - tz = _get_tzinfo(tz) - - dt = (np.datetime64(get_epoch()) + - np.timedelta64(int(np.round(x * MUSECONDS_PER_DAY)), 'us')) - if dt < np.datetime64('0001-01-01') or dt >= np.datetime64('10000-01-01'): - raise ValueError(f'Date ordinal {x} converts to {dt} (using ' - f'epoch {get_epoch()}), but Matplotlib dates must be ' - 'between year 0001 and 9999.') - # convert from datetime64 to datetime: - dt = dt.tolist() - - # datetime64 is always UTC: - dt = dt.replace(tzinfo=dateutil.tz.gettz('UTC')) - # but maybe we are working in a different timezone so move. - dt = dt.astimezone(tz) - # fix round off errors - if np.abs(x) > 70 * 365: - # if x is big, round off to nearest twenty microseconds. - # This avoids floating point roundoff error - ms = round(dt.microsecond / 20) * 20 - if ms == 1000000: - dt = dt.replace(microsecond=0) + datetime.timedelta(seconds=1) - else: - dt = dt.replace(microsecond=ms) - - return dt - - -# a version of _from_ordinalf that can operate on numpy arrays -_from_ordinalf_np_vectorized = np.vectorize(_from_ordinalf, otypes="O") - - -# a version of dateutil.parser.parse that can operate on numpy arrays -_dateutil_parser_parse_np_vectorized = np.vectorize(dateutil.parser.parse) - - -def datestr2num(d, default=None): - """ - Convert a date string to a datenum using `dateutil.parser.parse`. - - Parameters - ---------- - d : str or sequence of str - The dates to convert. - - default : datetime.datetime, optional - The default date to use when fields are missing in *d*. - """ - if isinstance(d, str): - dt = dateutil.parser.parse(d, default=default) - return date2num(dt) - else: - if default is not None: - d = [date2num(dateutil.parser.parse(s, default=default)) - for s in d] - return np.asarray(d) - d = np.asarray(d) - if not d.size: - return d - return date2num(_dateutil_parser_parse_np_vectorized(d)) - - -def date2num(d): - """ - Convert datetime objects to Matplotlib dates. - - Parameters - ---------- - d : `datetime.datetime` or `numpy.datetime64` or sequences of these - - Returns - ------- - float or sequence of floats - Number of days since the epoch. See `.get_epoch` for the - epoch, which can be changed by :rc:`date.epoch` or `.set_epoch`. If - the epoch is "1970-01-01T00:00:00" (default) then noon Jan 1 1970 - ("1970-01-01T12:00:00") returns 0.5. - - Notes - ----- - The Gregorian calendar is assumed; this is not universal practice. - For details see the module docstring. - """ - # Unpack in case of e.g. Pandas or xarray object - d = cbook._unpack_to_numpy(d) - - # make an iterable, but save state to unpack later: - iterable = np.iterable(d) - if not iterable: - d = [d] - - masked = np.ma.is_masked(d) - mask = np.ma.getmask(d) - d = np.asarray(d) - - # convert to datetime64 arrays, if not already: - if not np.issubdtype(d.dtype, np.datetime64): - # datetime arrays - if not d.size: - # deals with an empty array... - return d - tzi = getattr(d[0], 'tzinfo', None) - if tzi is not None: - # make datetime naive: - d = [dt.astimezone(UTC).replace(tzinfo=None) for dt in d] - d = np.asarray(d) - d = d.astype('datetime64[us]') - - d = np.ma.masked_array(d, mask=mask) if masked else d - d = _dt64_to_ordinalf(d) - - return d if iterable else d[0] - - -@_api.deprecated("3.7") -def julian2num(j): - """ - Convert a Julian date (or sequence) to a Matplotlib date (or sequence). - - Parameters - ---------- - j : float or sequence of floats - Julian dates (days relative to 4713 BC Jan 1, 12:00:00 Julian - calendar or 4714 BC Nov 24, 12:00:00, proleptic Gregorian calendar). - - Returns - ------- - float or sequence of floats - Matplotlib dates (days relative to `.get_epoch`). - """ - ep = np.datetime64(get_epoch(), 'h').astype(float) / 24. - ep0 = np.datetime64('0000-12-31T00:00:00', 'h').astype(float) / 24. - # Julian offset defined above is relative to 0000-12-31, but we need - # relative to our current epoch: - dt = __getattr__("JULIAN_OFFSET") - ep0 + ep - return np.subtract(j, dt) # Handles both scalar & nonscalar j. - - -@_api.deprecated("3.7") -def num2julian(n): - """ - Convert a Matplotlib date (or sequence) to a Julian date (or sequence). - - Parameters - ---------- - n : float or sequence of floats - Matplotlib dates (days relative to `.get_epoch`). - - Returns - ------- - float or sequence of floats - Julian dates (days relative to 4713 BC Jan 1, 12:00:00). - """ - ep = np.datetime64(get_epoch(), 'h').astype(float) / 24. - ep0 = np.datetime64('0000-12-31T00:00:00', 'h').astype(float) / 24. - # Julian offset defined above is relative to 0000-12-31, but we need - # relative to our current epoch: - dt = __getattr__("JULIAN_OFFSET") - ep0 + ep - return np.add(n, dt) # Handles both scalar & nonscalar j. - - -def num2date(x, tz=None): - """ - Convert Matplotlib dates to `~datetime.datetime` objects. - - Parameters - ---------- - x : float or sequence of floats - Number of days (fraction part represents hours, minutes, seconds) - since the epoch. See `.get_epoch` for the - epoch, which can be changed by :rc:`date.epoch` or `.set_epoch`. - tz : str or `~datetime.tzinfo`, default: :rc:`timezone` - Timezone of *x*. If a string, *tz* is passed to `dateutil.tz`. - - Returns - ------- - `~datetime.datetime` or sequence of `~datetime.datetime` - Dates are returned in timezone *tz*. - - If *x* is a sequence, a sequence of `~datetime.datetime` objects will - be returned. - - Notes - ----- - The Gregorian calendar is assumed; this is not universal practice. - For details, see the module docstring. - """ - tz = _get_tzinfo(tz) - return _from_ordinalf_np_vectorized(x, tz).tolist() - - -_ordinalf_to_timedelta_np_vectorized = np.vectorize( - lambda x: datetime.timedelta(days=x), otypes="O") - - -def num2timedelta(x): - """ - Convert number of days to a `~datetime.timedelta` object. - - If *x* is a sequence, a sequence of `~datetime.timedelta` objects will - be returned. - - Parameters - ---------- - x : float, sequence of floats - Number of days. The fraction part represents hours, minutes, seconds. - - Returns - ------- - `datetime.timedelta` or list[`datetime.timedelta`] - """ - return _ordinalf_to_timedelta_np_vectorized(x).tolist() - - -def drange(dstart, dend, delta): - """ - Return a sequence of equally spaced Matplotlib dates. - - The dates start at *dstart* and reach up to, but not including *dend*. - They are spaced by *delta*. - - Parameters - ---------- - dstart, dend : `~datetime.datetime` - The date limits. - delta : `datetime.timedelta` - Spacing of the dates. - - Returns - ------- - `numpy.array` - A list floats representing Matplotlib dates. - - """ - f1 = date2num(dstart) - f2 = date2num(dend) - step = delta.total_seconds() / SEC_PER_DAY - - # calculate the difference between dend and dstart in times of delta - num = int(np.ceil((f2 - f1) / step)) - - # calculate end of the interval which will be generated - dinterval_end = dstart + num * delta - - # ensure, that an half open interval will be generated [dstart, dend) - if dinterval_end >= dend: - # if the endpoint is greater than or equal to dend, - # just subtract one delta - dinterval_end -= delta - num -= 1 - - f2 = date2num(dinterval_end) # new float-endpoint - return np.linspace(f1, f2, num + 1) - - -def _wrap_in_tex(text): - p = r'([a-zA-Z]+)' - ret_text = re.sub(p, r'}$\1$\\mathdefault{', text) - - # Braces ensure symbols are not spaced like binary operators. - ret_text = ret_text.replace('-', '{-}').replace(':', '{:}') - # To not concatenate space between numbers. - ret_text = ret_text.replace(' ', r'\;') - ret_text = '$\\mathdefault{' + ret_text + '}$' - ret_text = ret_text.replace('$\\mathdefault{}$', '') - return ret_text - - -## date tickers and formatters ### - - -class DateFormatter(ticker.Formatter): - """ - Format a tick (in days since the epoch) with a - `~datetime.datetime.strftime` format string. - """ - - def __init__(self, fmt, tz=None, *, usetex=None): - """ - Parameters - ---------- - fmt : str - `~datetime.datetime.strftime` format string - tz : str or `~datetime.tzinfo`, default: :rc:`timezone` - Ticks timezone. If a string, *tz* is passed to `dateutil.tz`. - usetex : bool, default: :rc:`text.usetex` - To enable/disable the use of TeX's math mode for rendering the - results of the formatter. - """ - self.tz = _get_tzinfo(tz) - self.fmt = fmt - self._usetex = (usetex if usetex is not None else - mpl.rcParams['text.usetex']) - - def __call__(self, x, pos=0): - result = num2date(x, self.tz).strftime(self.fmt) - return _wrap_in_tex(result) if self._usetex else result - - def set_tzinfo(self, tz): - self.tz = _get_tzinfo(tz) - - -class ConciseDateFormatter(ticker.Formatter): - """ - A `.Formatter` which attempts to figure out the best format to use for the - date, and to make it as compact as possible, but still be complete. This is - most useful when used with the `AutoDateLocator`:: - - >>> locator = AutoDateLocator() - >>> formatter = ConciseDateFormatter(locator) - - Parameters - ---------- - locator : `.ticker.Locator` - Locator that this axis is using. - - tz : str or `~datetime.tzinfo`, default: :rc:`timezone` - Ticks timezone, passed to `.dates.num2date`. - - formats : list of 6 strings, optional - Format strings for 6 levels of tick labelling: mostly years, - months, days, hours, minutes, and seconds. Strings use - the same format codes as `~datetime.datetime.strftime`. Default is - ``['%Y', '%b', '%d', '%H:%M', '%H:%M', '%S.%f']`` - - zero_formats : list of 6 strings, optional - Format strings for tick labels that are "zeros" for a given tick - level. For instance, if most ticks are months, ticks around 1 Jan 2005 - will be labeled "Dec", "2005", "Feb". The default is - ``['', '%Y', '%b', '%b-%d', '%H:%M', '%H:%M']`` - - offset_formats : list of 6 strings, optional - Format strings for the 6 levels that is applied to the "offset" - string found on the right side of an x-axis, or top of a y-axis. - Combined with the tick labels this should completely specify the - date. The default is:: - - ['', '%Y', '%Y-%b', '%Y-%b-%d', '%Y-%b-%d', '%Y-%b-%d %H:%M'] - - show_offset : bool, default: True - Whether to show the offset or not. - - usetex : bool, default: :rc:`text.usetex` - To enable/disable the use of TeX's math mode for rendering the results - of the formatter. - - Examples - -------- - See :doc:`/gallery/ticks/date_concise_formatter` - - .. plot:: - - import datetime - import matplotlib.dates as mdates - - base = datetime.datetime(2005, 2, 1) - dates = np.array([base + datetime.timedelta(hours=(2 * i)) - for i in range(732)]) - N = len(dates) - np.random.seed(19680801) - y = np.cumsum(np.random.randn(N)) - - fig, ax = plt.subplots(constrained_layout=True) - locator = mdates.AutoDateLocator() - formatter = mdates.ConciseDateFormatter(locator) - ax.xaxis.set_major_locator(locator) - ax.xaxis.set_major_formatter(formatter) - - ax.plot(dates, y) - ax.set_title('Concise Date Formatter') - - """ - - def __init__(self, locator, tz=None, formats=None, offset_formats=None, - zero_formats=None, show_offset=True, *, usetex=None): - """ - Autoformat the date labels. The default format is used to form an - initial string, and then redundant elements are removed. - """ - self._locator = locator - self._tz = tz - self.defaultfmt = '%Y' - # there are 6 levels with each level getting a specific format - # 0: mostly years, 1: months, 2: days, - # 3: hours, 4: minutes, 5: seconds - if formats: - if len(formats) != 6: - raise ValueError('formats argument must be a list of ' - '6 format strings (or None)') - self.formats = formats - else: - self.formats = ['%Y', # ticks are mostly years - '%b', # ticks are mostly months - '%d', # ticks are mostly days - '%H:%M', # hrs - '%H:%M', # min - '%S.%f', # secs - ] - # fmt for zeros ticks at this level. These are - # ticks that should be labeled w/ info the level above. - # like 1 Jan can just be labelled "Jan". 02:02:00 can - # just be labeled 02:02. - if zero_formats: - if len(zero_formats) != 6: - raise ValueError('zero_formats argument must be a list of ' - '6 format strings (or None)') - self.zero_formats = zero_formats - elif formats: - # use the users formats for the zero tick formats - self.zero_formats = [''] + self.formats[:-1] - else: - # make the defaults a bit nicer: - self.zero_formats = [''] + self.formats[:-1] - self.zero_formats[3] = '%b-%d' - - if offset_formats: - if len(offset_formats) != 6: - raise ValueError('offset_formats argument must be a list of ' - '6 format strings (or None)') - self.offset_formats = offset_formats - else: - self.offset_formats = ['', - '%Y', - '%Y-%b', - '%Y-%b-%d', - '%Y-%b-%d', - '%Y-%b-%d %H:%M'] - self.offset_string = '' - self.show_offset = show_offset - self._usetex = (usetex if usetex is not None else - mpl.rcParams['text.usetex']) - - def __call__(self, x, pos=None): - formatter = DateFormatter(self.defaultfmt, self._tz, - usetex=self._usetex) - return formatter(x, pos=pos) - - def format_ticks(self, values): - tickdatetime = [num2date(value, tz=self._tz) for value in values] - tickdate = np.array([tdt.timetuple()[:6] for tdt in tickdatetime]) - - # basic algorithm: - # 1) only display a part of the date if it changes over the ticks. - # 2) don't display the smaller part of the date if: - # it is always the same or if it is the start of the - # year, month, day etc. - # fmt for most ticks at this level - fmts = self.formats - # format beginnings of days, months, years, etc. - zerofmts = self.zero_formats - # offset fmt are for the offset in the upper left of the - # or lower right of the axis. - offsetfmts = self.offset_formats - show_offset = self.show_offset - - # determine the level we will label at: - # mostly 0: years, 1: months, 2: days, - # 3: hours, 4: minutes, 5: seconds, 6: microseconds - for level in range(5, -1, -1): - unique = np.unique(tickdate[:, level]) - if len(unique) > 1: - # if 1 is included in unique, the year is shown in ticks - if level < 2 and np.any(unique == 1): - show_offset = False - break - elif level == 0: - # all tickdate are the same, so only micros might be different - # set to the most precise (6: microseconds doesn't exist...) - level = 5 - - # level is the basic level we will label at. - # now loop through and decide the actual ticklabels - zerovals = [0, 1, 1, 0, 0, 0, 0] - labels = [''] * len(tickdate) - for nn in range(len(tickdate)): - if level < 5: - if tickdate[nn][level] == zerovals[level]: - fmt = zerofmts[level] - else: - fmt = fmts[level] - else: - # special handling for seconds + microseconds - if (tickdatetime[nn].second == tickdatetime[nn].microsecond - == 0): - fmt = zerofmts[level] - else: - fmt = fmts[level] - labels[nn] = tickdatetime[nn].strftime(fmt) - - # special handling of seconds and microseconds: - # strip extra zeros and decimal if possible. - # this is complicated by two factors. 1) we have some level-4 strings - # here (i.e. 03:00, '0.50000', '1.000') 2) we would like to have the - # same number of decimals for each string (i.e. 0.5 and 1.0). - if level >= 5: - trailing_zeros = min( - (len(s) - len(s.rstrip('0')) for s in labels if '.' in s), - default=None) - if trailing_zeros: - for nn in range(len(labels)): - if '.' in labels[nn]: - labels[nn] = labels[nn][:-trailing_zeros].rstrip('.') - - if show_offset: - # set the offset string: - self.offset_string = tickdatetime[-1].strftime(offsetfmts[level]) - if self._usetex: - self.offset_string = _wrap_in_tex(self.offset_string) - else: - self.offset_string = '' - - if self._usetex: - return [_wrap_in_tex(l) for l in labels] - else: - return labels - - def get_offset(self): - return self.offset_string - - def format_data_short(self, value): - return num2date(value, tz=self._tz).strftime('%Y-%m-%d %H:%M:%S') - - -class AutoDateFormatter(ticker.Formatter): - """ - A `.Formatter` which attempts to figure out the best format to use. This - is most useful when used with the `AutoDateLocator`. - - `.AutoDateFormatter` has a ``.scale`` dictionary that maps tick scales (the - interval in days between one major tick) to format strings; this dictionary - defaults to :: - - self.scaled = { - DAYS_PER_YEAR: rcParams['date.autoformatter.year'], - DAYS_PER_MONTH: rcParams['date.autoformatter.month'], - 1: rcParams['date.autoformatter.day'], - 1 / HOURS_PER_DAY: rcParams['date.autoformatter.hour'], - 1 / MINUTES_PER_DAY: rcParams['date.autoformatter.minute'], - 1 / SEC_PER_DAY: rcParams['date.autoformatter.second'], - 1 / MUSECONDS_PER_DAY: rcParams['date.autoformatter.microsecond'], - } - - The formatter uses the format string corresponding to the lowest key in - the dictionary that is greater or equal to the current scale. Dictionary - entries can be customized:: - - locator = AutoDateLocator() - formatter = AutoDateFormatter(locator) - formatter.scaled[1/(24*60)] = '%M:%S' # only show min and sec - - Custom callables can also be used instead of format strings. The following - example shows how to use a custom format function to strip trailing zeros - from decimal seconds and adds the date to the first ticklabel:: - - def my_format_function(x, pos=None): - x = matplotlib.dates.num2date(x) - if pos == 0: - fmt = '%D %H:%M:%S.%f' - else: - fmt = '%H:%M:%S.%f' - label = x.strftime(fmt) - label = label.rstrip("0") - label = label.rstrip(".") - return label - - formatter.scaled[1/(24*60)] = my_format_function - """ - - # This can be improved by providing some user-level direction on - # how to choose the best format (precedence, etc.). - - # Perhaps a 'struct' that has a field for each time-type where a - # zero would indicate "don't show" and a number would indicate - # "show" with some sort of priority. Same priorities could mean - # show all with the same priority. - - # Or more simply, perhaps just a format string for each - # possibility... - - def __init__(self, locator, tz=None, defaultfmt='%Y-%m-%d', *, - usetex=None): - """ - Autoformat the date labels. - - Parameters - ---------- - locator : `.ticker.Locator` - Locator that this axis is using. - - tz : str or `~datetime.tzinfo`, default: :rc:`timezone` - Ticks timezone. If a string, *tz* is passed to `dateutil.tz`. - - defaultfmt : str - The default format to use if none of the values in ``self.scaled`` - are greater than the unit returned by ``locator._get_unit()``. - - usetex : bool, default: :rc:`text.usetex` - To enable/disable the use of TeX's math mode for rendering the - results of the formatter. If any entries in ``self.scaled`` are set - as functions, then it is up to the customized function to enable or - disable TeX's math mode itself. - """ - self._locator = locator - self._tz = tz - self.defaultfmt = defaultfmt - self._formatter = DateFormatter(self.defaultfmt, tz) - rcParams = mpl.rcParams - self._usetex = (usetex if usetex is not None else - mpl.rcParams['text.usetex']) - self.scaled = { - DAYS_PER_YEAR: rcParams['date.autoformatter.year'], - DAYS_PER_MONTH: rcParams['date.autoformatter.month'], - 1: rcParams['date.autoformatter.day'], - 1 / HOURS_PER_DAY: rcParams['date.autoformatter.hour'], - 1 / MINUTES_PER_DAY: rcParams['date.autoformatter.minute'], - 1 / SEC_PER_DAY: rcParams['date.autoformatter.second'], - 1 / MUSECONDS_PER_DAY: rcParams['date.autoformatter.microsecond'] - } - - def _set_locator(self, locator): - self._locator = locator - - def __call__(self, x, pos=None): - try: - locator_unit_scale = float(self._locator._get_unit()) - except AttributeError: - locator_unit_scale = 1 - # Pick the first scale which is greater than the locator unit. - fmt = next((fmt for scale, fmt in sorted(self.scaled.items()) - if scale >= locator_unit_scale), - self.defaultfmt) - - if isinstance(fmt, str): - self._formatter = DateFormatter(fmt, self._tz, usetex=self._usetex) - result = self._formatter(x, pos) - elif callable(fmt): - result = fmt(x, pos) - else: - raise TypeError('Unexpected type passed to {0!r}.'.format(self)) - - return result - - -class rrulewrapper: - """ - A simple wrapper around a `dateutil.rrule` allowing flexible - date tick specifications. - """ - def __init__(self, freq, tzinfo=None, **kwargs): - """ - Parameters - ---------- - freq : {YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, SECONDLY} - Tick frequency. These constants are defined in `dateutil.rrule`, - but they are accessible from `matplotlib.dates` as well. - tzinfo : `datetime.tzinfo`, optional - Time zone information. The default is None. - **kwargs - Additional keyword arguments are passed to the `dateutil.rrule`. - """ - kwargs['freq'] = freq - self._base_tzinfo = tzinfo - - self._update_rrule(**kwargs) - - def set(self, **kwargs): - """Set parameters for an existing wrapper.""" - self._construct.update(kwargs) - - self._update_rrule(**self._construct) - - def _update_rrule(self, **kwargs): - tzinfo = self._base_tzinfo - - # rrule does not play nicely with timezones - especially pytz time - # zones, it's best to use naive zones and attach timezones once the - # datetimes are returned - if 'dtstart' in kwargs: - dtstart = kwargs['dtstart'] - if dtstart.tzinfo is not None: - if tzinfo is None: - tzinfo = dtstart.tzinfo - else: - dtstart = dtstart.astimezone(tzinfo) - - kwargs['dtstart'] = dtstart.replace(tzinfo=None) - - if 'until' in kwargs: - until = kwargs['until'] - if until.tzinfo is not None: - if tzinfo is not None: - until = until.astimezone(tzinfo) - else: - raise ValueError('until cannot be aware if dtstart ' - 'is naive and tzinfo is None') - - kwargs['until'] = until.replace(tzinfo=None) - - self._construct = kwargs.copy() - self._tzinfo = tzinfo - self._rrule = rrule(**self._construct) - - def _attach_tzinfo(self, dt, tzinfo): - # pytz zones are attached by "localizing" the datetime - if hasattr(tzinfo, 'localize'): - return tzinfo.localize(dt, is_dst=True) - - return dt.replace(tzinfo=tzinfo) - - def _aware_return_wrapper(self, f, returns_list=False): - """Decorator function that allows rrule methods to handle tzinfo.""" - # This is only necessary if we're actually attaching a tzinfo - if self._tzinfo is None: - return f - - # All datetime arguments must be naive. If they are not naive, they are - # converted to the _tzinfo zone before dropping the zone. - def normalize_arg(arg): - if isinstance(arg, datetime.datetime) and arg.tzinfo is not None: - if arg.tzinfo is not self._tzinfo: - arg = arg.astimezone(self._tzinfo) - - return arg.replace(tzinfo=None) - - return arg - - def normalize_args(args, kwargs): - args = tuple(normalize_arg(arg) for arg in args) - kwargs = {kw: normalize_arg(arg) for kw, arg in kwargs.items()} - - return args, kwargs - - # There are two kinds of functions we care about - ones that return - # dates and ones that return lists of dates. - if not returns_list: - def inner_func(*args, **kwargs): - args, kwargs = normalize_args(args, kwargs) - dt = f(*args, **kwargs) - return self._attach_tzinfo(dt, self._tzinfo) - else: - def inner_func(*args, **kwargs): - args, kwargs = normalize_args(args, kwargs) - dts = f(*args, **kwargs) - return [self._attach_tzinfo(dt, self._tzinfo) for dt in dts] - - return functools.wraps(f)(inner_func) - - def __getattr__(self, name): - if name in self.__dict__: - return self.__dict__[name] - - f = getattr(self._rrule, name) - - if name in {'after', 'before'}: - return self._aware_return_wrapper(f) - elif name in {'xafter', 'xbefore', 'between'}: - return self._aware_return_wrapper(f, returns_list=True) - else: - return f - - def __setstate__(self, state): - self.__dict__.update(state) - - -class DateLocator(ticker.Locator): - """ - Determines the tick locations when plotting dates. - - This class is subclassed by other Locators and - is not meant to be used on its own. - """ - hms0d = {'byhour': 0, 'byminute': 0, 'bysecond': 0} - - def __init__(self, tz=None): - """ - Parameters - ---------- - tz : str or `~datetime.tzinfo`, default: :rc:`timezone` - Ticks timezone. If a string, *tz* is passed to `dateutil.tz`. - """ - self.tz = _get_tzinfo(tz) - - def set_tzinfo(self, tz): - """ - Set timezone info. - - Parameters - ---------- - tz : str or `~datetime.tzinfo`, default: :rc:`timezone` - Ticks timezone. If a string, *tz* is passed to `dateutil.tz`. - """ - self.tz = _get_tzinfo(tz) - - def datalim_to_dt(self): - """Convert axis data interval to datetime objects.""" - dmin, dmax = self.axis.get_data_interval() - if dmin > dmax: - dmin, dmax = dmax, dmin - - return num2date(dmin, self.tz), num2date(dmax, self.tz) - - def viewlim_to_dt(self): - """Convert the view interval to datetime objects.""" - vmin, vmax = self.axis.get_view_interval() - if vmin > vmax: - vmin, vmax = vmax, vmin - return num2date(vmin, self.tz), num2date(vmax, self.tz) - - def _get_unit(self): - """ - Return how many days a unit of the locator is; used for - intelligent autoscaling. - """ - return 1 - - def _get_interval(self): - """ - Return the number of units for each tick. - """ - return 1 - - def nonsingular(self, vmin, vmax): - """ - Given the proposed upper and lower extent, adjust the range - if it is too close to being singular (i.e. a range of ~0). - """ - if not np.isfinite(vmin) or not np.isfinite(vmax): - # Except if there is no data, then use 1970 as default. - return (date2num(datetime.date(1970, 1, 1)), - date2num(datetime.date(1970, 1, 2))) - if vmax < vmin: - vmin, vmax = vmax, vmin - unit = self._get_unit() - interval = self._get_interval() - if abs(vmax - vmin) < 1e-6: - vmin -= 2 * unit * interval - vmax += 2 * unit * interval - return vmin, vmax - - -class RRuleLocator(DateLocator): - # use the dateutil rrule instance - - def __init__(self, o, tz=None): - super().__init__(tz) - self.rule = o - - def __call__(self): - # if no data have been set, this will tank with a ValueError - try: - dmin, dmax = self.viewlim_to_dt() - except ValueError: - return [] - - return self.tick_values(dmin, dmax) - - def tick_values(self, vmin, vmax): - start, stop = self._create_rrule(vmin, vmax) - dates = self.rule.between(start, stop, True) - if len(dates) == 0: - return date2num([vmin, vmax]) - return self.raise_if_exceeds(date2num(dates)) - - def _create_rrule(self, vmin, vmax): - # set appropriate rrule dtstart and until and return - # start and end - delta = relativedelta(vmax, vmin) - - # We need to cap at the endpoints of valid datetime - try: - start = vmin - delta - except (ValueError, OverflowError): - # cap - start = datetime.datetime(1, 1, 1, 0, 0, 0, - tzinfo=datetime.timezone.utc) - - try: - stop = vmax + delta - except (ValueError, OverflowError): - # cap - stop = datetime.datetime(9999, 12, 31, 23, 59, 59, - tzinfo=datetime.timezone.utc) - - self.rule.set(dtstart=start, until=stop) - - return vmin, vmax - - def _get_unit(self): - # docstring inherited - freq = self.rule._rrule._freq - return self.get_unit_generic(freq) - - @staticmethod - def get_unit_generic(freq): - if freq == YEARLY: - return DAYS_PER_YEAR - elif freq == MONTHLY: - return DAYS_PER_MONTH - elif freq == WEEKLY: - return DAYS_PER_WEEK - elif freq == DAILY: - return 1.0 - elif freq == HOURLY: - return 1.0 / HOURS_PER_DAY - elif freq == MINUTELY: - return 1.0 / MINUTES_PER_DAY - elif freq == SECONDLY: - return 1.0 / SEC_PER_DAY - else: - # error - return -1 # or should this just return '1'? - - def _get_interval(self): - return self.rule._rrule._interval - - -class AutoDateLocator(DateLocator): - """ - On autoscale, this class picks the best `DateLocator` to set the view - limits and the tick locations. - - Attributes - ---------- - intervald : dict - - Mapping of tick frequencies to multiples allowed for that ticking. - The default is :: - - self.intervald = { - YEARLY : [1, 2, 4, 5, 10, 20, 40, 50, 100, 200, 400, 500, - 1000, 2000, 4000, 5000, 10000], - MONTHLY : [1, 2, 3, 4, 6], - DAILY : [1, 2, 3, 7, 14, 21], - HOURLY : [1, 2, 3, 4, 6, 12], - MINUTELY: [1, 5, 10, 15, 30], - SECONDLY: [1, 5, 10, 15, 30], - MICROSECONDLY: [1, 2, 5, 10, 20, 50, 100, 200, 500, - 1000, 2000, 5000, 10000, 20000, 50000, - 100000, 200000, 500000, 1000000], - } - - where the keys are defined in `dateutil.rrule`. - - The interval is used to specify multiples that are appropriate for - the frequency of ticking. For instance, every 7 days is sensible - for daily ticks, but for minutes/seconds, 15 or 30 make sense. - - When customizing, you should only modify the values for the existing - keys. You should not add or delete entries. - - Example for forcing ticks every 3 hours:: - - locator = AutoDateLocator() - locator.intervald[HOURLY] = [3] # only show every 3 hours - """ - - def __init__(self, tz=None, minticks=5, maxticks=None, - interval_multiples=True): - """ - Parameters - ---------- - tz : str or `~datetime.tzinfo`, default: :rc:`timezone` - Ticks timezone. If a string, *tz* is passed to `dateutil.tz`. - minticks : int - The minimum number of ticks desired; controls whether ticks occur - yearly, monthly, etc. - maxticks : int - The maximum number of ticks desired; controls the interval between - ticks (ticking every other, every 3, etc.). For fine-grained - control, this can be a dictionary mapping individual rrule - frequency constants (YEARLY, MONTHLY, etc.) to their own maximum - number of ticks. This can be used to keep the number of ticks - appropriate to the format chosen in `AutoDateFormatter`. Any - frequency not specified in this dictionary is given a default - value. - interval_multiples : bool, default: True - Whether ticks should be chosen to be multiple of the interval, - locking them to 'nicer' locations. For example, this will force - the ticks to be at hours 0, 6, 12, 18 when hourly ticking is done - at 6 hour intervals. - """ - super().__init__(tz=tz) - self._freq = YEARLY - self._freqs = [YEARLY, MONTHLY, DAILY, HOURLY, MINUTELY, - SECONDLY, MICROSECONDLY] - self.minticks = minticks - - self.maxticks = {YEARLY: 11, MONTHLY: 12, DAILY: 11, HOURLY: 12, - MINUTELY: 11, SECONDLY: 11, MICROSECONDLY: 8} - if maxticks is not None: - try: - self.maxticks.update(maxticks) - except TypeError: - # Assume we were given an integer. Use this as the maximum - # number of ticks for every frequency and create a - # dictionary for this - self.maxticks = dict.fromkeys(self._freqs, maxticks) - self.interval_multiples = interval_multiples - self.intervald = { - YEARLY: [1, 2, 4, 5, 10, 20, 40, 50, 100, 200, 400, 500, - 1000, 2000, 4000, 5000, 10000], - MONTHLY: [1, 2, 3, 4, 6], - DAILY: [1, 2, 3, 7, 14, 21], - HOURLY: [1, 2, 3, 4, 6, 12], - MINUTELY: [1, 5, 10, 15, 30], - SECONDLY: [1, 5, 10, 15, 30], - MICROSECONDLY: [1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, - 5000, 10000, 20000, 50000, 100000, 200000, 500000, - 1000000], - } - if interval_multiples: - # Swap "3" for "4" in the DAILY list; If we use 3 we get bad - # tick loc for months w/ 31 days: 1, 4, ..., 28, 31, 1 - # If we use 4 then we get: 1, 5, ... 25, 29, 1 - self.intervald[DAILY] = [1, 2, 4, 7, 14] - - self._byranges = [None, range(1, 13), range(1, 32), - range(0, 24), range(0, 60), range(0, 60), None] - - def __call__(self): - # docstring inherited - dmin, dmax = self.viewlim_to_dt() - locator = self.get_locator(dmin, dmax) - return locator() - - def tick_values(self, vmin, vmax): - return self.get_locator(vmin, vmax).tick_values(vmin, vmax) - - def nonsingular(self, vmin, vmax): - # whatever is thrown at us, we can scale the unit. - # But default nonsingular date plots at an ~4 year period. - if not np.isfinite(vmin) or not np.isfinite(vmax): - # Except if there is no data, then use 1970 as default. - return (date2num(datetime.date(1970, 1, 1)), - date2num(datetime.date(1970, 1, 2))) - if vmax < vmin: - vmin, vmax = vmax, vmin - if vmin == vmax: - vmin = vmin - DAYS_PER_YEAR * 2 - vmax = vmax + DAYS_PER_YEAR * 2 - return vmin, vmax - - def _get_unit(self): - if self._freq in [MICROSECONDLY]: - return 1. / MUSECONDS_PER_DAY - else: - return RRuleLocator.get_unit_generic(self._freq) - - def get_locator(self, dmin, dmax): - """Pick the best locator based on a distance.""" - delta = relativedelta(dmax, dmin) - tdelta = dmax - dmin - - # take absolute difference - if dmin > dmax: - delta = -delta - tdelta = -tdelta - # The following uses a mix of calls to relativedelta and timedelta - # methods because there is incomplete overlap in the functionality of - # these similar functions, and it's best to avoid doing our own math - # whenever possible. - numYears = float(delta.years) - numMonths = numYears * MONTHS_PER_YEAR + delta.months - numDays = tdelta.days # Avoids estimates of days/month, days/year. - numHours = numDays * HOURS_PER_DAY + delta.hours - numMinutes = numHours * MIN_PER_HOUR + delta.minutes - numSeconds = np.floor(tdelta.total_seconds()) - numMicroseconds = np.floor(tdelta.total_seconds() * 1e6) - - nums = [numYears, numMonths, numDays, numHours, numMinutes, - numSeconds, numMicroseconds] - - use_rrule_locator = [True] * 6 + [False] - - # Default setting of bymonth, etc. to pass to rrule - # [unused (for year), bymonth, bymonthday, byhour, byminute, - # bysecond, unused (for microseconds)] - byranges = [None, 1, 1, 0, 0, 0, None] - - # Loop over all the frequencies and try to find one that gives at - # least a minticks tick positions. Once this is found, look for - # an interval from a list specific to that frequency that gives no - # more than maxticks tick positions. Also, set up some ranges - # (bymonth, etc.) as appropriate to be passed to rrulewrapper. - for i, (freq, num) in enumerate(zip(self._freqs, nums)): - # If this particular frequency doesn't give enough ticks, continue - if num < self.minticks: - # Since we're not using this particular frequency, set - # the corresponding by_ to None so the rrule can act as - # appropriate - byranges[i] = None - continue - - # Find the first available interval that doesn't give too many - # ticks - for interval in self.intervald[freq]: - if num <= interval * (self.maxticks[freq] - 1): - break - else: - if not (self.interval_multiples and freq == DAILY): - _api.warn_external( - f"AutoDateLocator was unable to pick an appropriate " - f"interval for this date range. It may be necessary " - f"to add an interval value to the AutoDateLocator's " - f"intervald dictionary. Defaulting to {interval}.") - - # Set some parameters as appropriate - self._freq = freq - - if self._byranges[i] and self.interval_multiples: - byranges[i] = self._byranges[i][::interval] - if i in (DAILY, WEEKLY): - if interval == 14: - # just make first and 15th. Avoids 30th. - byranges[i] = [1, 15] - elif interval == 7: - byranges[i] = [1, 8, 15, 22] - - interval = 1 - else: - byranges[i] = self._byranges[i] - break - else: - interval = 1 - - if (freq == YEARLY) and self.interval_multiples: - locator = YearLocator(interval, tz=self.tz) - elif use_rrule_locator[i]: - _, bymonth, bymonthday, byhour, byminute, bysecond, _ = byranges - rrule = rrulewrapper(self._freq, interval=interval, - dtstart=dmin, until=dmax, - bymonth=bymonth, bymonthday=bymonthday, - byhour=byhour, byminute=byminute, - bysecond=bysecond) - - locator = RRuleLocator(rrule, tz=self.tz) - else: - locator = MicrosecondLocator(interval, tz=self.tz) - if date2num(dmin) > 70 * 365 and interval < 1000: - _api.warn_external( - 'Plotting microsecond time intervals for dates far from ' - f'the epoch (time origin: {get_epoch()}) is not well-' - 'supported. See matplotlib.dates.set_epoch to change the ' - 'epoch.') - - locator.set_axis(self.axis) - return locator - - -class YearLocator(RRuleLocator): - """ - Make ticks on a given day of each year that is a multiple of base. - - Examples:: - - # Tick every year on Jan 1st - locator = YearLocator() - - # Tick every 5 years on July 4th - locator = YearLocator(5, month=7, day=4) - """ - def __init__(self, base=1, month=1, day=1, tz=None): - """ - Parameters - ---------- - base : int, default: 1 - Mark ticks every *base* years. - month : int, default: 1 - The month on which to place the ticks, starting from 1. Default is - January. - day : int, default: 1 - The day on which to place the ticks. - tz : str or `~datetime.tzinfo`, default: :rc:`timezone` - Ticks timezone. If a string, *tz* is passed to `dateutil.tz`. - """ - rule = rrulewrapper(YEARLY, interval=base, bymonth=month, - bymonthday=day, **self.hms0d) - super().__init__(rule, tz=tz) - self.base = ticker._Edge_integer(base, 0) - - def _create_rrule(self, vmin, vmax): - # 'start' needs to be a multiple of the interval to create ticks on - # interval multiples when the tick frequency is YEARLY - ymin = max(self.base.le(vmin.year) * self.base.step, 1) - ymax = min(self.base.ge(vmax.year) * self.base.step, 9999) - - c = self.rule._construct - replace = {'year': ymin, - 'month': c.get('bymonth', 1), - 'day': c.get('bymonthday', 1), - 'hour': 0, 'minute': 0, 'second': 0} - - start = vmin.replace(**replace) - stop = start.replace(year=ymax) - self.rule.set(dtstart=start, until=stop) - - return start, stop - - -class MonthLocator(RRuleLocator): - """ - Make ticks on occurrences of each month, e.g., 1, 3, 12. - """ - def __init__(self, bymonth=None, bymonthday=1, interval=1, tz=None): - """ - Parameters - ---------- - bymonth : int or list of int, default: all months - Ticks will be placed on every month in *bymonth*. Default is - ``range(1, 13)``, i.e. every month. - bymonthday : int, default: 1 - The day on which to place the ticks. - interval : int, default: 1 - The interval between each iteration. For example, if - ``interval=2``, mark every second occurrence. - tz : str or `~datetime.tzinfo`, default: :rc:`timezone` - Ticks timezone. If a string, *tz* is passed to `dateutil.tz`. - """ - if bymonth is None: - bymonth = range(1, 13) - - rule = rrulewrapper(MONTHLY, bymonth=bymonth, bymonthday=bymonthday, - interval=interval, **self.hms0d) - super().__init__(rule, tz=tz) - - -class WeekdayLocator(RRuleLocator): - """ - Make ticks on occurrences of each weekday. - """ - - def __init__(self, byweekday=1, interval=1, tz=None): - """ - Parameters - ---------- - byweekday : int or list of int, default: all days - Ticks will be placed on every weekday in *byweekday*. Default is - every day. - - Elements of *byweekday* must be one of MO, TU, WE, TH, FR, SA, - SU, the constants from :mod:`dateutil.rrule`, which have been - imported into the :mod:`matplotlib.dates` namespace. - interval : int, default: 1 - The interval between each iteration. For example, if - ``interval=2``, mark every second occurrence. - tz : str or `~datetime.tzinfo`, default: :rc:`timezone` - Ticks timezone. If a string, *tz* is passed to `dateutil.tz`. - """ - rule = rrulewrapper(DAILY, byweekday=byweekday, - interval=interval, **self.hms0d) - super().__init__(rule, tz=tz) - - -class DayLocator(RRuleLocator): - """ - Make ticks on occurrences of each day of the month. For example, - 1, 15, 30. - """ - def __init__(self, bymonthday=None, interval=1, tz=None): - """ - Parameters - ---------- - bymonthday : int or list of int, default: all days - Ticks will be placed on every day in *bymonthday*. Default is - ``bymonthday=range(1, 32)``, i.e., every day of the month. - interval : int, default: 1 - The interval between each iteration. For example, if - ``interval=2``, mark every second occurrence. - tz : str or `~datetime.tzinfo`, default: :rc:`timezone` - Ticks timezone. If a string, *tz* is passed to `dateutil.tz`. - """ - if interval != int(interval) or interval < 1: - raise ValueError("interval must be an integer greater than 0") - if bymonthday is None: - bymonthday = range(1, 32) - - rule = rrulewrapper(DAILY, bymonthday=bymonthday, - interval=interval, **self.hms0d) - super().__init__(rule, tz=tz) - - -class HourLocator(RRuleLocator): - """ - Make ticks on occurrences of each hour. - """ - def __init__(self, byhour=None, interval=1, tz=None): - """ - Parameters - ---------- - byhour : int or list of int, default: all hours - Ticks will be placed on every hour in *byhour*. Default is - ``byhour=range(24)``, i.e., every hour. - interval : int, default: 1 - The interval between each iteration. For example, if - ``interval=2``, mark every second occurrence. - tz : str or `~datetime.tzinfo`, default: :rc:`timezone` - Ticks timezone. If a string, *tz* is passed to `dateutil.tz`. - """ - if byhour is None: - byhour = range(24) - - rule = rrulewrapper(HOURLY, byhour=byhour, interval=interval, - byminute=0, bysecond=0) - super().__init__(rule, tz=tz) - - -class MinuteLocator(RRuleLocator): - """ - Make ticks on occurrences of each minute. - """ - def __init__(self, byminute=None, interval=1, tz=None): - """ - Parameters - ---------- - byminute : int or list of int, default: all minutes - Ticks will be placed on every minute in *byminute*. Default is - ``byminute=range(60)``, i.e., every minute. - interval : int, default: 1 - The interval between each iteration. For example, if - ``interval=2``, mark every second occurrence. - tz : str or `~datetime.tzinfo`, default: :rc:`timezone` - Ticks timezone. If a string, *tz* is passed to `dateutil.tz`. - """ - if byminute is None: - byminute = range(60) - - rule = rrulewrapper(MINUTELY, byminute=byminute, interval=interval, - bysecond=0) - super().__init__(rule, tz=tz) - - -class SecondLocator(RRuleLocator): - """ - Make ticks on occurrences of each second. - """ - def __init__(self, bysecond=None, interval=1, tz=None): - """ - Parameters - ---------- - bysecond : int or list of int, default: all seconds - Ticks will be placed on every second in *bysecond*. Default is - ``bysecond = range(60)``, i.e., every second. - interval : int, default: 1 - The interval between each iteration. For example, if - ``interval=2``, mark every second occurrence. - tz : str or `~datetime.tzinfo`, default: :rc:`timezone` - Ticks timezone. If a string, *tz* is passed to `dateutil.tz`. - """ - if bysecond is None: - bysecond = range(60) - - rule = rrulewrapper(SECONDLY, bysecond=bysecond, interval=interval) - super().__init__(rule, tz=tz) - - -class MicrosecondLocator(DateLocator): - """ - Make ticks on regular intervals of one or more microsecond(s). - - .. note:: - - By default, Matplotlib uses a floating point representation of time in - days since the epoch, so plotting data with - microsecond time resolution does not work well for - dates that are far (about 70 years) from the epoch (check with - `~.dates.get_epoch`). - - If you want sub-microsecond resolution time plots, it is strongly - recommended to use floating point seconds, not datetime-like - time representation. - - If you really must use datetime.datetime() or similar and still - need microsecond precision, change the time origin via - `.dates.set_epoch` to something closer to the dates being plotted. - See :doc:`/gallery/ticks/date_precision_and_epochs`. - - """ - def __init__(self, interval=1, tz=None): - """ - Parameters - ---------- - interval : int, default: 1 - The interval between each iteration. For example, if - ``interval=2``, mark every second occurrence. - tz : str or `~datetime.tzinfo`, default: :rc:`timezone` - Ticks timezone. If a string, *tz* is passed to `dateutil.tz`. - """ - super().__init__(tz=tz) - self._interval = interval - self._wrapped_locator = ticker.MultipleLocator(interval) - - def set_axis(self, axis): - self._wrapped_locator.set_axis(axis) - return super().set_axis(axis) - - def __call__(self): - # if no data have been set, this will tank with a ValueError - try: - dmin, dmax = self.viewlim_to_dt() - except ValueError: - return [] - - return self.tick_values(dmin, dmax) - - def tick_values(self, vmin, vmax): - nmin, nmax = date2num((vmin, vmax)) - t0 = np.floor(nmin) - nmax = nmax - t0 - nmin = nmin - t0 - nmin *= MUSECONDS_PER_DAY - nmax *= MUSECONDS_PER_DAY - - ticks = self._wrapped_locator.tick_values(nmin, nmax) - - ticks = ticks / MUSECONDS_PER_DAY + t0 - return ticks - - def _get_unit(self): - # docstring inherited - return 1. / MUSECONDS_PER_DAY - - def _get_interval(self): - # docstring inherited - return self._interval - - -@_api.deprecated("3.6", alternative="`AutoDateLocator` and `AutoDateFormatter`" - " or vendor the code") -def date_ticker_factory(span, tz=None, numticks=5): - """ - Create a date locator with *numticks* (approx) and a date formatter - for *span* in days. Return value is (locator, formatter). - """ - - if span == 0: - span = 1 / HOURS_PER_DAY - - mins = span * MINUTES_PER_DAY - hrs = span * HOURS_PER_DAY - days = span - wks = span / DAYS_PER_WEEK - months = span / DAYS_PER_MONTH # Approx - years = span / DAYS_PER_YEAR # Approx - - if years > numticks: - locator = YearLocator(int(years / numticks), tz=tz) # define - fmt = '%Y' - elif months > numticks: - locator = MonthLocator(tz=tz) - fmt = '%b %Y' - elif wks > numticks: - locator = WeekdayLocator(tz=tz) - fmt = '%a, %b %d' - elif days > numticks: - locator = DayLocator(interval=math.ceil(days / numticks), tz=tz) - fmt = '%b %d' - elif hrs > numticks: - locator = HourLocator(interval=math.ceil(hrs / numticks), tz=tz) - fmt = '%H:%M\n%b %d' - elif mins > numticks: - locator = MinuteLocator(interval=math.ceil(mins / numticks), tz=tz) - fmt = '%H:%M:%S' - else: - locator = MinuteLocator(tz=tz) - fmt = '%H:%M:%S' - - formatter = DateFormatter(fmt, tz=tz) - return locator, formatter - - -class DateConverter(units.ConversionInterface): - """ - Converter for `datetime.date` and `datetime.datetime` data, or for - date/time data represented as it would be converted by `date2num`. - - The 'unit' tag for such data is None or a `~datetime.tzinfo` instance. - """ - - def __init__(self, *, interval_multiples=True): - self._interval_multiples = interval_multiples - super().__init__() - - def axisinfo(self, unit, axis): - """ - Return the `~matplotlib.units.AxisInfo` for *unit*. - - *unit* is a `~datetime.tzinfo` instance or None. - The *axis* argument is required but not used. - """ - tz = unit - - majloc = AutoDateLocator(tz=tz, - interval_multiples=self._interval_multiples) - majfmt = AutoDateFormatter(majloc, tz=tz) - datemin = datetime.date(1970, 1, 1) - datemax = datetime.date(1970, 1, 2) - - return units.AxisInfo(majloc=majloc, majfmt=majfmt, label='', - default_limits=(datemin, datemax)) - - @staticmethod - def convert(value, unit, axis): - """ - If *value* is not already a number or sequence of numbers, convert it - with `date2num`. - - The *unit* and *axis* arguments are not used. - """ - return date2num(value) - - @staticmethod - def default_units(x, axis): - """ - Return the `~datetime.tzinfo` instance of *x* or of its first element, - or None - """ - if isinstance(x, np.ndarray): - x = x.ravel() - - try: - x = cbook._safe_first_finite(x) - except (TypeError, StopIteration): - pass - - try: - return x.tzinfo - except AttributeError: - pass - return None - - -class ConciseDateConverter(DateConverter): - # docstring inherited - - def __init__(self, formats=None, zero_formats=None, offset_formats=None, - show_offset=True, *, interval_multiples=True): - self._formats = formats - self._zero_formats = zero_formats - self._offset_formats = offset_formats - self._show_offset = show_offset - self._interval_multiples = interval_multiples - super().__init__() - - def axisinfo(self, unit, axis): - # docstring inherited - tz = unit - majloc = AutoDateLocator(tz=tz, - interval_multiples=self._interval_multiples) - majfmt = ConciseDateFormatter(majloc, tz=tz, formats=self._formats, - zero_formats=self._zero_formats, - offset_formats=self._offset_formats, - show_offset=self._show_offset) - datemin = datetime.date(1970, 1, 1) - datemax = datetime.date(1970, 1, 2) - return units.AxisInfo(majloc=majloc, majfmt=majfmt, label='', - default_limits=(datemin, datemax)) - - -class _SwitchableDateConverter: - """ - Helper converter-like object that generates and dispatches to - temporary ConciseDateConverter or DateConverter instances based on - :rc:`date.converter` and :rc:`date.interval_multiples`. - """ - - @staticmethod - def _get_converter(): - converter_cls = { - "concise": ConciseDateConverter, "auto": DateConverter}[ - mpl.rcParams["date.converter"]] - interval_multiples = mpl.rcParams["date.interval_multiples"] - return converter_cls(interval_multiples=interval_multiples) - - def axisinfo(self, *args, **kwargs): - return self._get_converter().axisinfo(*args, **kwargs) - - def default_units(self, *args, **kwargs): - return self._get_converter().default_units(*args, **kwargs) - - def convert(self, *args, **kwargs): - return self._get_converter().convert(*args, **kwargs) - - -units.registry[np.datetime64] = \ - units.registry[datetime.date] = \ - units.registry[datetime.datetime] = \ - _SwitchableDateConverter() diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/docstring.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/docstring.py deleted file mode 100644 index b6ddcf5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/docstring.py +++ /dev/null @@ -1,4 +0,0 @@ -from matplotlib._docstring import * # noqa: F401, F403 -from matplotlib import _api -_api.warn_deprecated( - "3.6", obj_type='module', name=f"{__name__}") diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/dviread.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/dviread.py deleted file mode 100644 index a37a606..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/dviread.py +++ /dev/null @@ -1,1165 +0,0 @@ -""" -A module for reading dvi files output by TeX. Several limitations make -this not (currently) useful as a general-purpose dvi preprocessor, but -it is currently used by the pdf backend for processing usetex text. - -Interface:: - - with Dvi(filename, 72) as dvi: - # iterate over pages: - for page in dvi: - w, h, d = page.width, page.height, page.descent - for x, y, font, glyph, width in page.text: - fontname = font.texname - pointsize = font.size - ... - for x, y, height, width in page.boxes: - ... -""" - -from collections import namedtuple -import enum -from functools import lru_cache, partial, wraps -import logging -import os -from pathlib import Path -import re -import struct -import subprocess -import sys - -import numpy as np - -from matplotlib import _api, cbook - -_log = logging.getLogger(__name__) - -# Many dvi related files are looked for by external processes, require -# additional parsing, and are used many times per rendering, which is why they -# are cached using lru_cache(). - -# Dvi is a bytecode format documented in -# https://ctan.org/pkg/dvitype -# https://texdoc.org/serve/dvitype.pdf/0 -# -# The file consists of a preamble, some number of pages, a postamble, -# and a finale. Different opcodes are allowed in different contexts, -# so the Dvi object has a parser state: -# -# pre: expecting the preamble -# outer: between pages (followed by a page or the postamble, -# also e.g. font definitions are allowed) -# page: processing a page -# post_post: state after the postamble (our current implementation -# just stops reading) -# finale: the finale (unimplemented in our current implementation) - -_dvistate = enum.Enum('DviState', 'pre outer inpage post_post finale') - -# The marks on a page consist of text and boxes. A page also has dimensions. -Page = namedtuple('Page', 'text boxes height width descent') -Box = namedtuple('Box', 'x y height width') - - -# Also a namedtuple, for backcompat. -class Text(namedtuple('Text', 'x y font glyph width')): - """ - A glyph in the dvi file. - - The *x* and *y* attributes directly position the glyph. The *font*, - *glyph*, and *width* attributes are kept public for back-compatibility, - but users wanting to draw the glyph themselves are encouraged to instead - load the font specified by `font_path` at `font_size`, warp it with the - effects specified by `font_effects`, and load the glyph specified by - `glyph_name_or_index`. - """ - - def _get_pdftexmap_entry(self): - return PsfontsMap(find_tex_file("pdftex.map"))[self.font.texname] - - @property - def font_path(self): - """The `~pathlib.Path` to the font for this glyph.""" - psfont = self._get_pdftexmap_entry() - if psfont.filename is None: - raise ValueError("No usable font file found for {} ({}); " - "the font may lack a Type-1 version" - .format(psfont.psname.decode("ascii"), - psfont.texname.decode("ascii"))) - return Path(psfont.filename) - - @property - def font_size(self): - """The font size.""" - return self.font.size - - @property - def font_effects(self): - """ - The "font effects" dict for this glyph. - - This dict contains the values for this glyph of SlantFont and - ExtendFont (if any), read off :file:`pdftex.map`. - """ - return self._get_pdftexmap_entry().effects - - @property - def glyph_name_or_index(self): - """ - Either the glyph name or the native charmap glyph index. - - If :file:`pdftex.map` specifies an encoding for this glyph's font, that - is a mapping of glyph indices to Adobe glyph names; use it to convert - dvi indices to glyph names. Callers can then convert glyph names to - glyph indices (with FT_Get_Name_Index/get_name_index), and load the - glyph using FT_Load_Glyph/load_glyph. - - If :file:`pdftex.map` specifies no encoding, the indices directly map - to the font's "native" charmap; glyphs should directly load using - FT_Load_Char/load_char after selecting the native charmap. - """ - entry = self._get_pdftexmap_entry() - return (_parse_enc(entry.encoding)[self.glyph] - if entry.encoding is not None else self.glyph) - - -# Opcode argument parsing -# -# Each of the following functions takes a Dvi object and delta, -# which is the difference between the opcode and the minimum opcode -# with the same meaning. Dvi opcodes often encode the number of -# argument bytes in this delta. - -def _arg_raw(dvi, delta): - """Return *delta* without reading anything more from the dvi file.""" - return delta - - -def _arg(nbytes, signed, dvi, _): - """ - Read *nbytes* bytes, returning the bytes interpreted as a signed integer - if *signed* is true, unsigned otherwise. - """ - return dvi._arg(nbytes, signed) - - -def _arg_slen(dvi, delta): - """ - Read *delta* bytes, returning None if *delta* is zero, and the bytes - interpreted as a signed integer otherwise. - """ - if delta == 0: - return None - return dvi._arg(delta, True) - - -def _arg_slen1(dvi, delta): - """ - Read *delta*+1 bytes, returning the bytes interpreted as signed. - """ - return dvi._arg(delta + 1, True) - - -def _arg_ulen1(dvi, delta): - """ - Read *delta*+1 bytes, returning the bytes interpreted as unsigned. - """ - return dvi._arg(delta + 1, False) - - -def _arg_olen1(dvi, delta): - """ - Read *delta*+1 bytes, returning the bytes interpreted as - unsigned integer for 0<=*delta*<3 and signed if *delta*==3. - """ - return dvi._arg(delta + 1, delta == 3) - - -_arg_mapping = dict(raw=_arg_raw, - u1=partial(_arg, 1, False), - u4=partial(_arg, 4, False), - s4=partial(_arg, 4, True), - slen=_arg_slen, - olen1=_arg_olen1, - slen1=_arg_slen1, - ulen1=_arg_ulen1) - - -def _dispatch(table, min, max=None, state=None, args=('raw',)): - """ - Decorator for dispatch by opcode. Sets the values in *table* - from *min* to *max* to this method, adds a check that the Dvi state - matches *state* if not None, reads arguments from the file according - to *args*. - - Parameters - ---------- - table : dict[int, callable] - The dispatch table to be filled in. - - min, max : int - Range of opcodes that calls the registered function; *max* defaults to - *min*. - - state : _dvistate, optional - State of the Dvi object in which these opcodes are allowed. - - args : list[str], default: ['raw'] - Sequence of argument specifications: - - - 'raw': opcode minus minimum - - 'u1': read one unsigned byte - - 'u4': read four bytes, treat as an unsigned number - - 's4': read four bytes, treat as a signed number - - 'slen': read (opcode - minimum) bytes, treat as signed - - 'slen1': read (opcode - minimum + 1) bytes, treat as signed - - 'ulen1': read (opcode - minimum + 1) bytes, treat as unsigned - - 'olen1': read (opcode - minimum + 1) bytes, treat as unsigned - if under four bytes, signed if four bytes - """ - def decorate(method): - get_args = [_arg_mapping[x] for x in args] - - @wraps(method) - def wrapper(self, byte): - if state is not None and self.state != state: - raise ValueError("state precondition failed") - return method(self, *[f(self, byte-min) for f in get_args]) - if max is None: - table[min] = wrapper - else: - for i in range(min, max+1): - assert table[i] is None - table[i] = wrapper - return wrapper - return decorate - - -class Dvi: - """ - A reader for a dvi ("device-independent") file, as produced by TeX. - - The current implementation can only iterate through pages in order, - and does not even attempt to verify the postamble. - - This class can be used as a context manager to close the underlying - file upon exit. Pages can be read via iteration. Here is an overly - simple way to extract text without trying to detect whitespace:: - - >>> with matplotlib.dviread.Dvi('input.dvi', 72) as dvi: - ... for page in dvi: - ... print(''.join(chr(t.glyph) for t in page.text)) - """ - # dispatch table - _dtable = [None] * 256 - _dispatch = partial(_dispatch, _dtable) - - def __init__(self, filename, dpi): - """ - Read the data from the file named *filename* and convert - TeX's internal units to units of *dpi* per inch. - *dpi* only sets the units and does not limit the resolution. - Use None to return TeX's internal units. - """ - _log.debug('Dvi: %s', filename) - self.file = open(filename, 'rb') - self.dpi = dpi - self.fonts = {} - self.state = _dvistate.pre - - def __enter__(self): - """Context manager enter method, does nothing.""" - return self - - def __exit__(self, etype, evalue, etrace): - """ - Context manager exit method, closes the underlying file if it is open. - """ - self.close() - - def __iter__(self): - """ - Iterate through the pages of the file. - - Yields - ------ - Page - Details of all the text and box objects on the page. - The Page tuple contains lists of Text and Box tuples and - the page dimensions, and the Text and Box tuples contain - coordinates transformed into a standard Cartesian - coordinate system at the dpi value given when initializing. - The coordinates are floating point numbers, but otherwise - precision is not lost and coordinate values are not clipped to - integers. - """ - while self._read(): - yield self._output() - - def close(self): - """Close the underlying file if it is open.""" - if not self.file.closed: - self.file.close() - - def _output(self): - """ - Output the text and boxes belonging to the most recent page. - page = dvi._output() - """ - minx, miny, maxx, maxy = np.inf, np.inf, -np.inf, -np.inf - maxy_pure = -np.inf - for elt in self.text + self.boxes: - if isinstance(elt, Box): - x, y, h, w = elt - e = 0 # zero depth - else: # glyph - x, y, font, g, w = elt - h, e = font._height_depth_of(g) - minx = min(minx, x) - miny = min(miny, y - h) - maxx = max(maxx, x + w) - maxy = max(maxy, y + e) - maxy_pure = max(maxy_pure, y) - if self._baseline_v is not None: - maxy_pure = self._baseline_v # This should normally be the case. - self._baseline_v = None - - if not self.text and not self.boxes: # Avoid infs/nans from inf+/-inf. - return Page(text=[], boxes=[], width=0, height=0, descent=0) - - if self.dpi is None: - # special case for ease of debugging: output raw dvi coordinates - return Page(text=self.text, boxes=self.boxes, - width=maxx-minx, height=maxy_pure-miny, - descent=maxy-maxy_pure) - - # convert from TeX's "scaled points" to dpi units - d = self.dpi / (72.27 * 2**16) - descent = (maxy - maxy_pure) * d - - text = [Text((x-minx)*d, (maxy-y)*d - descent, f, g, w*d) - for (x, y, f, g, w) in self.text] - boxes = [Box((x-minx)*d, (maxy-y)*d - descent, h*d, w*d) - for (x, y, h, w) in self.boxes] - - return Page(text=text, boxes=boxes, width=(maxx-minx)*d, - height=(maxy_pure-miny)*d, descent=descent) - - def _read(self): - """ - Read one page from the file. Return True if successful, - False if there were no more pages. - """ - # Pages appear to start with the sequence - # bop (begin of page) - # xxx comment - # # if using chemformula - # down - # push - # down - # # if using xcolor - # down - # push - # down (possibly multiple) - # push <= here, v is the baseline position. - # etc. - # (dviasm is useful to explore this structure.) - # Thus, we use the vertical position at the first time the stack depth - # reaches 3, while at least three "downs" have been executed (excluding - # those popped out (corresponding to the chemformula preamble)), as the - # baseline (the "down" count is necessary to handle xcolor). - down_stack = [0] - self._baseline_v = None - while True: - byte = self.file.read(1)[0] - self._dtable[byte](self, byte) - name = self._dtable[byte].__name__ - if name == "_push": - down_stack.append(down_stack[-1]) - elif name == "_pop": - down_stack.pop() - elif name == "_down": - down_stack[-1] += 1 - if (self._baseline_v is None - and len(getattr(self, "stack", [])) == 3 - and down_stack[-1] >= 4): - self._baseline_v = self.v - if byte == 140: # end of page - return True - if self.state is _dvistate.post_post: # end of file - self.close() - return False - - def _arg(self, nbytes, signed=False): - """ - Read and return an integer argument *nbytes* long. - Signedness is determined by the *signed* keyword. - """ - buf = self.file.read(nbytes) - value = buf[0] - if signed and value >= 0x80: - value = value - 0x100 - for b in buf[1:]: - value = 0x100*value + b - return value - - @_dispatch(min=0, max=127, state=_dvistate.inpage) - def _set_char_immediate(self, char): - self._put_char_real(char) - self.h += self.fonts[self.f]._width_of(char) - - @_dispatch(min=128, max=131, state=_dvistate.inpage, args=('olen1',)) - def _set_char(self, char): - self._put_char_real(char) - self.h += self.fonts[self.f]._width_of(char) - - @_dispatch(132, state=_dvistate.inpage, args=('s4', 's4')) - def _set_rule(self, a, b): - self._put_rule_real(a, b) - self.h += b - - @_dispatch(min=133, max=136, state=_dvistate.inpage, args=('olen1',)) - def _put_char(self, char): - self._put_char_real(char) - - def _put_char_real(self, char): - font = self.fonts[self.f] - if font._vf is None: - self.text.append(Text(self.h, self.v, font, char, - font._width_of(char))) - else: - scale = font._scale - for x, y, f, g, w in font._vf[char].text: - newf = DviFont(scale=_mul2012(scale, f._scale), - tfm=f._tfm, texname=f.texname, vf=f._vf) - self.text.append(Text(self.h + _mul2012(x, scale), - self.v + _mul2012(y, scale), - newf, g, newf._width_of(g))) - self.boxes.extend([Box(self.h + _mul2012(x, scale), - self.v + _mul2012(y, scale), - _mul2012(a, scale), _mul2012(b, scale)) - for x, y, a, b in font._vf[char].boxes]) - - @_dispatch(137, state=_dvistate.inpage, args=('s4', 's4')) - def _put_rule(self, a, b): - self._put_rule_real(a, b) - - def _put_rule_real(self, a, b): - if a > 0 and b > 0: - self.boxes.append(Box(self.h, self.v, a, b)) - - @_dispatch(138) - def _nop(self, _): - pass - - @_dispatch(139, state=_dvistate.outer, args=('s4',)*11) - def _bop(self, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, p): - self.state = _dvistate.inpage - self.h, self.v, self.w, self.x, self.y, self.z = 0, 0, 0, 0, 0, 0 - self.stack = [] - self.text = [] # list of Text objects - self.boxes = [] # list of Box objects - - @_dispatch(140, state=_dvistate.inpage) - def _eop(self, _): - self.state = _dvistate.outer - del self.h, self.v, self.w, self.x, self.y, self.z, self.stack - - @_dispatch(141, state=_dvistate.inpage) - def _push(self, _): - self.stack.append((self.h, self.v, self.w, self.x, self.y, self.z)) - - @_dispatch(142, state=_dvistate.inpage) - def _pop(self, _): - self.h, self.v, self.w, self.x, self.y, self.z = self.stack.pop() - - @_dispatch(min=143, max=146, state=_dvistate.inpage, args=('slen1',)) - def _right(self, b): - self.h += b - - @_dispatch(min=147, max=151, state=_dvistate.inpage, args=('slen',)) - def _right_w(self, new_w): - if new_w is not None: - self.w = new_w - self.h += self.w - - @_dispatch(min=152, max=156, state=_dvistate.inpage, args=('slen',)) - def _right_x(self, new_x): - if new_x is not None: - self.x = new_x - self.h += self.x - - @_dispatch(min=157, max=160, state=_dvistate.inpage, args=('slen1',)) - def _down(self, a): - self.v += a - - @_dispatch(min=161, max=165, state=_dvistate.inpage, args=('slen',)) - def _down_y(self, new_y): - if new_y is not None: - self.y = new_y - self.v += self.y - - @_dispatch(min=166, max=170, state=_dvistate.inpage, args=('slen',)) - def _down_z(self, new_z): - if new_z is not None: - self.z = new_z - self.v += self.z - - @_dispatch(min=171, max=234, state=_dvistate.inpage) - def _fnt_num_immediate(self, k): - self.f = k - - @_dispatch(min=235, max=238, state=_dvistate.inpage, args=('olen1',)) - def _fnt_num(self, new_f): - self.f = new_f - - @_dispatch(min=239, max=242, args=('ulen1',)) - def _xxx(self, datalen): - special = self.file.read(datalen) - _log.debug( - 'Dvi._xxx: encountered special: %s', - ''.join([chr(ch) if 32 <= ch < 127 else '<%02x>' % ch - for ch in special])) - - @_dispatch(min=243, max=246, args=('olen1', 'u4', 'u4', 'u4', 'u1', 'u1')) - def _fnt_def(self, k, c, s, d, a, l): - self._fnt_def_real(k, c, s, d, a, l) - - def _fnt_def_real(self, k, c, s, d, a, l): - n = self.file.read(a + l) - fontname = n[-l:].decode('ascii') - tfm = _tfmfile(fontname) - if c != 0 and tfm.checksum != 0 and c != tfm.checksum: - raise ValueError('tfm checksum mismatch: %s' % n) - try: - vf = _vffile(fontname) - except FileNotFoundError: - vf = None - self.fonts[k] = DviFont(scale=s, tfm=tfm, texname=n, vf=vf) - - @_dispatch(247, state=_dvistate.pre, args=('u1', 'u4', 'u4', 'u4', 'u1')) - def _pre(self, i, num, den, mag, k): - self.file.read(k) # comment in the dvi file - if i != 2: - raise ValueError("Unknown dvi format %d" % i) - if num != 25400000 or den != 7227 * 2**16: - raise ValueError("Nonstandard units in dvi file") - # meaning: TeX always uses those exact values, so it - # should be enough for us to support those - # (There are 72.27 pt to an inch so 7227 pt = - # 7227 * 2**16 sp to 100 in. The numerator is multiplied - # by 10^5 to get units of 10**-7 meters.) - if mag != 1000: - raise ValueError("Nonstandard magnification in dvi file") - # meaning: LaTeX seems to frown on setting \mag, so - # I think we can assume this is constant - self.state = _dvistate.outer - - @_dispatch(248, state=_dvistate.outer) - def _post(self, _): - self.state = _dvistate.post_post - # TODO: actually read the postamble and finale? - # currently post_post just triggers closing the file - - @_dispatch(249) - def _post_post(self, _): - raise NotImplementedError - - @_dispatch(min=250, max=255) - def _malformed(self, offset): - raise ValueError(f"unknown command: byte {250 + offset}") - - -class DviFont: - """ - Encapsulation of a font that a DVI file can refer to. - - This class holds a font's texname and size, supports comparison, - and knows the widths of glyphs in the same units as the AFM file. - There are also internal attributes (for use by dviread.py) that - are *not* used for comparison. - - The size is in Adobe points (converted from TeX points). - - Parameters - ---------- - scale : float - Factor by which the font is scaled from its natural size. - tfm : Tfm - TeX font metrics for this font - texname : bytes - Name of the font as used internally by TeX and friends, as an ASCII - bytestring. This is usually very different from any external font - names; `PsfontsMap` can be used to find the external name of the font. - vf : Vf - A TeX "virtual font" file, or None if this font is not virtual. - - Attributes - ---------- - texname : bytes - size : float - Size of the font in Adobe points, converted from the slightly - smaller TeX points. - widths : list - Widths of glyphs in glyph-space units, typically 1/1000ths of - the point size. - - """ - __slots__ = ('texname', 'size', 'widths', '_scale', '_vf', '_tfm') - - def __init__(self, scale, tfm, texname, vf): - _api.check_isinstance(bytes, texname=texname) - self._scale = scale - self._tfm = tfm - self.texname = texname - self._vf = vf - self.size = scale * (72.0 / (72.27 * 2**16)) - try: - nchars = max(tfm.width) + 1 - except ValueError: - nchars = 0 - self.widths = [(1000*tfm.width.get(char, 0)) >> 20 - for char in range(nchars)] - - def __eq__(self, other): - return (type(self) is type(other) - and self.texname == other.texname and self.size == other.size) - - def __ne__(self, other): - return not self.__eq__(other) - - def __repr__(self): - return "<{}: {}>".format(type(self).__name__, self.texname) - - def _width_of(self, char): - """Width of char in dvi units.""" - width = self._tfm.width.get(char, None) - if width is not None: - return _mul2012(width, self._scale) - _log.debug('No width for char %d in font %s.', char, self.texname) - return 0 - - def _height_depth_of(self, char): - """Height and depth of char in dvi units.""" - result = [] - for metric, name in ((self._tfm.height, "height"), - (self._tfm.depth, "depth")): - value = metric.get(char, None) - if value is None: - _log.debug('No %s for char %d in font %s', - name, char, self.texname) - result.append(0) - else: - result.append(_mul2012(value, self._scale)) - # cmsyXX (symbols font) glyph 0 ("minus") has a nonzero descent - # so that TeX aligns equations properly - # (https://tex.stackexchange.com/q/526103/) - # but we actually care about the rasterization depth to align - # the dvipng-generated images. - if re.match(br'^cmsy\d+$', self.texname) and char == 0: - result[-1] = 0 - return result - - -class Vf(Dvi): - r""" - A virtual font (\*.vf file) containing subroutines for dvi files. - - Parameters - ---------- - filename : str or path-like - - Notes - ----- - The virtual font format is a derivative of dvi: - http://mirrors.ctan.org/info/knuth/virtual-fonts - This class reuses some of the machinery of `Dvi` - but replaces the `_read` loop and dispatch mechanism. - - Examples - -------- - :: - - vf = Vf(filename) - glyph = vf[code] - glyph.text, glyph.boxes, glyph.width - """ - - def __init__(self, filename): - super().__init__(filename, 0) - try: - self._first_font = None - self._chars = {} - self._read() - finally: - self.close() - - def __getitem__(self, code): - return self._chars[code] - - def _read(self): - """ - Read one page from the file. Return True if successful, - False if there were no more pages. - """ - packet_char, packet_ends = None, None - packet_len, packet_width = None, None - while True: - byte = self.file.read(1)[0] - # If we are in a packet, execute the dvi instructions - if self.state is _dvistate.inpage: - byte_at = self.file.tell()-1 - if byte_at == packet_ends: - self._finalize_packet(packet_char, packet_width) - packet_len, packet_char, packet_width = None, None, None - # fall through to out-of-packet code - elif byte_at > packet_ends: - raise ValueError("Packet length mismatch in vf file") - else: - if byte in (139, 140) or byte >= 243: - raise ValueError( - "Inappropriate opcode %d in vf file" % byte) - Dvi._dtable[byte](self, byte) - continue - - # We are outside a packet - if byte < 242: # a short packet (length given by byte) - packet_len = byte - packet_char, packet_width = self._arg(1), self._arg(3) - packet_ends = self._init_packet(byte) - self.state = _dvistate.inpage - elif byte == 242: # a long packet - packet_len, packet_char, packet_width = \ - [self._arg(x) for x in (4, 4, 4)] - self._init_packet(packet_len) - elif 243 <= byte <= 246: - k = self._arg(byte - 242, byte == 246) - c, s, d, a, l = [self._arg(x) for x in (4, 4, 4, 1, 1)] - self._fnt_def_real(k, c, s, d, a, l) - if self._first_font is None: - self._first_font = k - elif byte == 247: # preamble - i, k = self._arg(1), self._arg(1) - x = self.file.read(k) - cs, ds = self._arg(4), self._arg(4) - self._pre(i, x, cs, ds) - elif byte == 248: # postamble (just some number of 248s) - break - else: - raise ValueError("Unknown vf opcode %d" % byte) - - def _init_packet(self, pl): - if self.state != _dvistate.outer: - raise ValueError("Misplaced packet in vf file") - self.h, self.v, self.w, self.x, self.y, self.z = 0, 0, 0, 0, 0, 0 - self.stack, self.text, self.boxes = [], [], [] - self.f = self._first_font - return self.file.tell() + pl - - def _finalize_packet(self, packet_char, packet_width): - self._chars[packet_char] = Page( - text=self.text, boxes=self.boxes, width=packet_width, - height=None, descent=None) - self.state = _dvistate.outer - - def _pre(self, i, x, cs, ds): - if self.state is not _dvistate.pre: - raise ValueError("pre command in middle of vf file") - if i != 202: - raise ValueError("Unknown vf format %d" % i) - if len(x): - _log.debug('vf file comment: %s', x) - self.state = _dvistate.outer - # cs = checksum, ds = design size - - -def _mul2012(num1, num2): - """Multiply two numbers in 20.12 fixed point format.""" - # Separated into a function because >> has surprising precedence - return (num1*num2) >> 20 - - -class Tfm: - """ - A TeX Font Metric file. - - This implementation covers only the bare minimum needed by the Dvi class. - - Parameters - ---------- - filename : str or path-like - - Attributes - ---------- - checksum : int - Used for verifying against the dvi file. - design_size : int - Design size of the font (unknown units) - width, height, depth : dict - Dimensions of each character, need to be scaled by the factor - specified in the dvi file. These are dicts because indexing may - not start from 0. - """ - __slots__ = ('checksum', 'design_size', 'width', 'height', 'depth') - - def __init__(self, filename): - _log.debug('opening tfm file %s', filename) - with open(filename, 'rb') as file: - header1 = file.read(24) - lh, bc, ec, nw, nh, nd = struct.unpack('!6H', header1[2:14]) - _log.debug('lh=%d, bc=%d, ec=%d, nw=%d, nh=%d, nd=%d', - lh, bc, ec, nw, nh, nd) - header2 = file.read(4*lh) - self.checksum, self.design_size = struct.unpack('!2I', header2[:8]) - # there is also encoding information etc. - char_info = file.read(4*(ec-bc+1)) - widths = struct.unpack(f'!{nw}i', file.read(4*nw)) - heights = struct.unpack(f'!{nh}i', file.read(4*nh)) - depths = struct.unpack(f'!{nd}i', file.read(4*nd)) - self.width, self.height, self.depth = {}, {}, {} - for idx, char in enumerate(range(bc, ec+1)): - byte0 = char_info[4*idx] - byte1 = char_info[4*idx+1] - self.width[char] = widths[byte0] - self.height[char] = heights[byte1 >> 4] - self.depth[char] = depths[byte1 & 0xf] - - -PsFont = namedtuple('PsFont', 'texname psname effects encoding filename') - - -class PsfontsMap: - """ - A psfonts.map formatted file, mapping TeX fonts to PS fonts. - - Parameters - ---------- - filename : str or path-like - - Notes - ----- - For historical reasons, TeX knows many Type-1 fonts by different - names than the outside world. (For one thing, the names have to - fit in eight characters.) Also, TeX's native fonts are not Type-1 - but Metafont, which is nontrivial to convert to PostScript except - as a bitmap. While high-quality conversions to Type-1 format exist - and are shipped with modern TeX distributions, we need to know - which Type-1 fonts are the counterparts of which native fonts. For - these reasons a mapping is needed from internal font names to font - file names. - - A texmf tree typically includes mapping files called e.g. - :file:`psfonts.map`, :file:`pdftex.map`, or :file:`dvipdfm.map`. - The file :file:`psfonts.map` is used by :program:`dvips`, - :file:`pdftex.map` by :program:`pdfTeX`, and :file:`dvipdfm.map` - by :program:`dvipdfm`. :file:`psfonts.map` might avoid embedding - the 35 PostScript fonts (i.e., have no filename for them, as in - the Times-Bold example above), while the pdf-related files perhaps - only avoid the "Base 14" pdf fonts. But the user may have - configured these files differently. - - Examples - -------- - >>> map = PsfontsMap(find_tex_file('pdftex.map')) - >>> entry = map[b'ptmbo8r'] - >>> entry.texname - b'ptmbo8r' - >>> entry.psname - b'Times-Bold' - >>> entry.encoding - '/usr/local/texlive/2008/texmf-dist/fonts/enc/dvips/base/8r.enc' - >>> entry.effects - {'slant': 0.16700000000000001} - >>> entry.filename - """ - __slots__ = ('_filename', '_unparsed', '_parsed') - - # Create a filename -> PsfontsMap cache, so that calling - # `PsfontsMap(filename)` with the same filename a second time immediately - # returns the same object. - @lru_cache() - def __new__(cls, filename): - self = object.__new__(cls) - self._filename = os.fsdecode(filename) - # Some TeX distributions have enormous pdftex.map files which would - # take hundreds of milliseconds to parse, but it is easy enough to just - # store the unparsed lines (keyed by the first word, which is the - # texname) and parse them on-demand. - with open(filename, 'rb') as file: - self._unparsed = {} - for line in file: - tfmname = line.split(b' ', 1)[0] - self._unparsed.setdefault(tfmname, []).append(line) - self._parsed = {} - return self - - def __getitem__(self, texname): - assert isinstance(texname, bytes) - if texname in self._unparsed: - for line in self._unparsed.pop(texname): - if self._parse_and_cache_line(line): - break - try: - return self._parsed[texname] - except KeyError: - raise LookupError( - f"An associated PostScript font (required by Matplotlib) " - f"could not be found for TeX font {texname.decode('ascii')!r} " - f"in {self._filename!r}; this problem can often be solved by " - f"installing a suitable PostScript font package in your TeX " - f"package manager") from None - - def _parse_and_cache_line(self, line): - """ - Parse a line in the font mapping file. - - The format is (partially) documented at - http://mirrors.ctan.org/systems/doc/pdftex/manual/pdftex-a.pdf - https://tug.org/texinfohtml/dvips.html#psfonts_002emap - Each line can have the following fields: - - - tfmname (first, only required field), - - psname (defaults to tfmname, must come immediately after tfmname if - present), - - fontflags (integer, must come immediately after psname if present, - ignored by us), - - special (SlantFont and ExtendFont, only field that is double-quoted), - - fontfile, encodingfile (optional, prefixed by <, <<, or <[; << always - precedes a font, <[ always precedes an encoding, < can precede either - but then an encoding file must have extension .enc; < and << also - request different font subsetting behaviors but we ignore that; < can - be separated from the filename by whitespace). - - special, fontfile, and encodingfile can appear in any order. - """ - # If the map file specifies multiple encodings for a font, we - # follow pdfTeX in choosing the last one specified. Such - # entries are probably mistakes but they have occurred. - # https://tex.stackexchange.com/q/10826/ - - if not line or line.startswith((b" ", b"%", b"*", b";", b"#")): - return - tfmname = basename = special = encodingfile = fontfile = None - is_subsetted = is_t1 = is_truetype = False - matches = re.finditer(br'"([^"]*)(?:"|$)|(\S+)', line) - for match in matches: - quoted, unquoted = match.groups() - if unquoted: - if unquoted.startswith(b"<<"): # font - fontfile = unquoted[2:] - elif unquoted.startswith(b"<["): # encoding - encodingfile = unquoted[2:] - elif unquoted.startswith(b"<"): # font or encoding - word = ( - # foo - unquoted[1:] - # < by itself => read the next word - or next(filter(None, next(matches).groups()))) - if word.endswith(b".enc"): - encodingfile = word - else: - fontfile = word - is_subsetted = True - elif tfmname is None: - tfmname = unquoted - elif basename is None: - basename = unquoted - elif quoted: - special = quoted - effects = {} - if special: - words = reversed(special.split()) - for word in words: - if word == b"SlantFont": - effects["slant"] = float(next(words)) - elif word == b"ExtendFont": - effects["extend"] = float(next(words)) - - # Verify some properties of the line that would cause it to be ignored - # otherwise. - if fontfile is not None: - if fontfile.endswith((b".ttf", b".ttc")): - is_truetype = True - elif not fontfile.endswith(b".otf"): - is_t1 = True - elif basename is not None: - is_t1 = True - if is_truetype and is_subsetted and encodingfile is None: - return - if not is_t1 and ("slant" in effects or "extend" in effects): - return - if abs(effects.get("slant", 0)) > 1: - return - if abs(effects.get("extend", 0)) > 2: - return - - if basename is None: - basename = tfmname - if encodingfile is not None: - encodingfile = _find_tex_file(encodingfile) - if fontfile is not None: - fontfile = _find_tex_file(fontfile) - self._parsed[tfmname] = PsFont( - texname=tfmname, psname=basename, effects=effects, - encoding=encodingfile, filename=fontfile) - return True - - -def _parse_enc(path): - r""" - Parse a \*.enc file referenced from a psfonts.map style file. - - The format supported by this function is a tiny subset of PostScript. - - Parameters - ---------- - path : `os.PathLike` - - Returns - ------- - list - The nth entry of the list is the PostScript glyph name of the nth - glyph. - """ - no_comments = re.sub("%.*", "", Path(path).read_text(encoding="ascii")) - array = re.search(r"(?s)\[(.*)\]", no_comments).group(1) - lines = [line for line in array.split() if line] - if all(line.startswith("/") for line in lines): - return [line[1:] for line in lines] - else: - raise ValueError( - "Failed to parse {} as Postscript encoding".format(path)) - - -class _LuatexKpsewhich: - @lru_cache() # A singleton. - def __new__(cls): - self = object.__new__(cls) - self._proc = self._new_proc() - return self - - def _new_proc(self): - return subprocess.Popen( - ["luatex", "--luaonly", - str(cbook._get_data_path("kpsewhich.lua"))], - stdin=subprocess.PIPE, stdout=subprocess.PIPE) - - def search(self, filename): - if self._proc.poll() is not None: # Dead, restart it. - self._proc = self._new_proc() - self._proc.stdin.write(os.fsencode(filename) + b"\n") - self._proc.stdin.flush() - out = self._proc.stdout.readline().rstrip() - return None if out == b"nil" else os.fsdecode(out) - - -@lru_cache() -def _find_tex_file(filename): - """ - Find a file in the texmf tree using kpathsea_. - - The kpathsea library, provided by most existing TeX distributions, both - on Unix-like systems and on Windows (MikTeX), is invoked via a long-lived - luatex process if luatex is installed, or via kpsewhich otherwise. - - .. _kpathsea: https://www.tug.org/kpathsea/ - - Parameters - ---------- - filename : str or path-like - - Raises - ------ - FileNotFoundError - If the file is not found. - """ - - # we expect these to always be ascii encoded, but use utf-8 - # out of caution - if isinstance(filename, bytes): - filename = filename.decode('utf-8', errors='replace') - - try: - lk = _LuatexKpsewhich() - except FileNotFoundError: - lk = None # Fallback to directly calling kpsewhich, as below. - - if lk: - path = lk.search(filename) - else: - if os.name == 'nt': - # On Windows only, kpathsea can use utf-8 for cmd args and output. - # The `command_line_encoding` environment variable is set to force - # it to always use utf-8 encoding. See Matplotlib issue #11848. - kwargs = {'env': {**os.environ, 'command_line_encoding': 'utf-8'}, - 'encoding': 'utf-8'} - else: # On POSIX, run through the equivalent of os.fsdecode(). - kwargs = {'encoding': sys.getfilesystemencoding(), - 'errors': 'surrogateescape'} - - try: - path = (cbook._check_and_log_subprocess(['kpsewhich', filename], - _log, **kwargs) - .rstrip('\n')) - except (FileNotFoundError, RuntimeError): - path = None - - if path: - return path - else: - raise FileNotFoundError( - f"Matplotlib's TeX implementation searched for a file named " - f"{filename!r} in your texmf tree, but could not find it") - - -# After the deprecation period elapses, delete this shim and rename -# _find_tex_file to find_tex_file everywhere. -def find_tex_file(filename): - try: - return _find_tex_file(filename) - except FileNotFoundError as exc: - _api.warn_deprecated( - "3.6", message=f"{exc.args[0]}; in the future, this will raise a " - f"FileNotFoundError.") - return "" - - -find_tex_file.__doc__ = _find_tex_file.__doc__ - - -@lru_cache() -def _fontfile(cls, suffix, texname): - return cls(_find_tex_file(texname + suffix)) - - -_tfmfile = partial(_fontfile, Tfm, ".tfm") -_vffile = partial(_fontfile, Vf, ".vf") - - -if __name__ == '__main__': - from argparse import ArgumentParser - import itertools - - parser = ArgumentParser() - parser.add_argument("filename") - parser.add_argument("dpi", nargs="?", type=float, default=None) - args = parser.parse_args() - with Dvi(args.filename, args.dpi) as dvi: - fontmap = PsfontsMap(_find_tex_file('pdftex.map')) - for page in dvi: - print(f"=== new page === " - f"(w: {page.width}, h: {page.height}, d: {page.descent})") - for font, group in itertools.groupby( - page.text, lambda text: text.font): - print(f"font: {font.texname.decode('latin-1')!r}\t" - f"scale: {font._scale / 2 ** 20}") - print("x", "y", "glyph", "chr", "w", "(glyphs)", sep="\t") - for text in group: - print(text.x, text.y, text.glyph, - chr(text.glyph) if chr(text.glyph).isprintable() - else ".", - text.width, sep="\t") - if page.boxes: - print("x", "y", "h", "w", "", "(boxes)", sep="\t") - for box in page.boxes: - print(box.x, box.y, box.height, box.width, sep="\t") diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/figure.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/figure.py deleted file mode 100644 index f79ec18..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/figure.py +++ /dev/null @@ -1,3629 +0,0 @@ -""" -`matplotlib.figure` implements the following classes: - -`Figure` - Top level `~matplotlib.artist.Artist`, which holds all plot elements. - Many methods are implemented in `FigureBase`. - -`SubFigure` - A logical figure inside a figure, usually added to a figure (or parent - `SubFigure`) with `Figure.add_subfigure` or `Figure.subfigures` methods - (provisional API v3.4). - -`SubplotParams` - Control the default spacing between subplots. - -Figures are typically created using pyplot methods `~.pyplot.figure`, -`~.pyplot.subplots`, and `~.pyplot.subplot_mosaic`. - -.. plot:: - :include-source: - - fig, ax = plt.subplots(figsize=(2, 2), facecolor='lightskyblue', - layout='constrained') - fig.suptitle('Figure') - ax.set_title('Axes', loc='left', fontstyle='oblique', fontsize='medium') - -Some situations call for directly instantiating a `~.figure.Figure` class, -usually inside an application of some sort (see :ref:`user_interfaces` for a -list of examples) . More information about Figures can be found at -:ref:`figure_explanation`. - -""" - -from contextlib import ExitStack -import inspect -import itertools -import logging -from numbers import Integral - -import numpy as np - -import matplotlib as mpl -from matplotlib import _blocking_input, backend_bases, _docstring, projections -from matplotlib.artist import ( - Artist, allow_rasterization, _finalize_rasterization) -from matplotlib.backend_bases import ( - DrawEvent, FigureCanvasBase, NonGuiException, MouseButton, _get_renderer) -import matplotlib._api as _api -import matplotlib.cbook as cbook -import matplotlib.colorbar as cbar -import matplotlib.image as mimage - -from matplotlib.axes import Axes -from matplotlib.gridspec import GridSpec -from matplotlib.layout_engine import ( - ConstrainedLayoutEngine, TightLayoutEngine, LayoutEngine, - PlaceHolderLayoutEngine -) -import matplotlib.legend as mlegend -from matplotlib.patches import Rectangle -from matplotlib.text import Text -from matplotlib.transforms import (Affine2D, Bbox, BboxTransformTo, - TransformedBbox) - -_log = logging.getLogger(__name__) - - -def _stale_figure_callback(self, val): - if self.figure: - self.figure.stale = val - - -class _AxesStack: - """ - Helper class to track axes in a figure. - - Axes are tracked both in the order in which they have been added - (``self._axes`` insertion/iteration order) and in the separate "gca" stack - (which is the index to which they map in the ``self._axes`` dict). - """ - - def __init__(self): - self._axes = {} # Mapping of axes to "gca" order. - self._counter = itertools.count() - - def as_list(self): - """List the axes that have been added to the figure.""" - return [*self._axes] # This relies on dict preserving order. - - def remove(self, a): - """Remove the axes from the stack.""" - self._axes.pop(a) - - def bubble(self, a): - """Move an axes, which must already exist in the stack, to the top.""" - if a not in self._axes: - raise ValueError("Axes has not been added yet") - self._axes[a] = next(self._counter) - - def add(self, a): - """Add an axes to the stack, ignoring it if already present.""" - if a not in self._axes: - self._axes[a] = next(self._counter) - - def current(self): - """Return the active axes, or None if the stack is empty.""" - return max(self._axes, key=self._axes.__getitem__, default=None) - - def __getstate__(self): - return { - **vars(self), - "_counter": max(self._axes.values(), default=0) - } - - def __setstate__(self, state): - next_counter = state.pop('_counter') - vars(self).update(state) - self._counter = itertools.count(next_counter) - - -class SubplotParams: - """ - A class to hold the parameters for a subplot. - """ - - def __init__(self, left=None, bottom=None, right=None, top=None, - wspace=None, hspace=None): - """ - Defaults are given by :rc:`figure.subplot.[name]`. - - Parameters - ---------- - left : float - The position of the left edge of the subplots, - as a fraction of the figure width. - right : float - The position of the right edge of the subplots, - as a fraction of the figure width. - bottom : float - The position of the bottom edge of the subplots, - as a fraction of the figure height. - top : float - The position of the top edge of the subplots, - as a fraction of the figure height. - wspace : float - The width of the padding between subplots, - as a fraction of the average Axes width. - hspace : float - The height of the padding between subplots, - as a fraction of the average Axes height. - """ - for key in ["left", "bottom", "right", "top", "wspace", "hspace"]: - setattr(self, key, mpl.rcParams[f"figure.subplot.{key}"]) - self.update(left, bottom, right, top, wspace, hspace) - - def update(self, left=None, bottom=None, right=None, top=None, - wspace=None, hspace=None): - """ - Update the dimensions of the passed parameters. *None* means unchanged. - """ - if ((left if left is not None else self.left) - >= (right if right is not None else self.right)): - raise ValueError('left cannot be >= right') - if ((bottom if bottom is not None else self.bottom) - >= (top if top is not None else self.top)): - raise ValueError('bottom cannot be >= top') - if left is not None: - self.left = left - if right is not None: - self.right = right - if bottom is not None: - self.bottom = bottom - if top is not None: - self.top = top - if wspace is not None: - self.wspace = wspace - if hspace is not None: - self.hspace = hspace - - -class FigureBase(Artist): - """ - Base class for `.Figure` and `.SubFigure` containing the methods that add - artists to the figure or subfigure, create Axes, etc. - """ - def __init__(self, **kwargs): - super().__init__() - # remove the non-figure artist _axes property - # as it makes no sense for a figure to be _in_ an Axes - # this is used by the property methods in the artist base class - # which are over-ridden in this class - del self._axes - - self._suptitle = None - self._supxlabel = None - self._supylabel = None - - # groupers to keep track of x and y labels we want to align. - # see self.align_xlabels and self.align_ylabels and - # axis._get_tick_boxes_siblings - self._align_label_groups = {"x": cbook.Grouper(), "y": cbook.Grouper()} - - self.figure = self - self._localaxes = [] # track all axes - self.artists = [] - self.lines = [] - self.patches = [] - self.texts = [] - self.images = [] - self.legends = [] - self.subfigs = [] - self.stale = True - self.suppressComposite = None - self.set(**kwargs) - - def _get_draw_artists(self, renderer): - """Also runs apply_aspect""" - artists = self.get_children() - for sfig in self.subfigs: - artists.remove(sfig) - childa = sfig.get_children() - for child in childa: - if child in artists: - artists.remove(child) - - artists.remove(self.patch) - artists = sorted( - (artist for artist in artists if not artist.get_animated()), - key=lambda artist: artist.get_zorder()) - for ax in self._localaxes: - locator = ax.get_axes_locator() - ax.apply_aspect(locator(ax, renderer) if locator else None) - - for child in ax.get_children(): - if hasattr(child, 'apply_aspect'): - locator = child.get_axes_locator() - child.apply_aspect( - locator(child, renderer) if locator else None) - return artists - - def autofmt_xdate( - self, bottom=0.2, rotation=30, ha='right', which='major'): - """ - Date ticklabels often overlap, so it is useful to rotate them - and right align them. Also, a common use case is a number of - subplots with shared x-axis where the x-axis is date data. The - ticklabels are often long, and it helps to rotate them on the - bottom subplot and turn them off on other subplots, as well as - turn off xlabels. - - Parameters - ---------- - bottom : float, default: 0.2 - The bottom of the subplots for `subplots_adjust`. - rotation : float, default: 30 degrees - The rotation angle of the xtick labels in degrees. - ha : {'left', 'center', 'right'}, default: 'right' - The horizontal alignment of the xticklabels. - which : {'major', 'minor', 'both'}, default: 'major' - Selects which ticklabels to rotate. - """ - _api.check_in_list(['major', 'minor', 'both'], which=which) - allsubplots = all(ax.get_subplotspec() for ax in self.axes) - if len(self.axes) == 1: - for label in self.axes[0].get_xticklabels(which=which): - label.set_ha(ha) - label.set_rotation(rotation) - else: - if allsubplots: - for ax in self.get_axes(): - if ax.get_subplotspec().is_last_row(): - for label in ax.get_xticklabels(which=which): - label.set_ha(ha) - label.set_rotation(rotation) - else: - for label in ax.get_xticklabels(which=which): - label.set_visible(False) - ax.set_xlabel('') - - if allsubplots: - self.subplots_adjust(bottom=bottom) - self.stale = True - - def get_children(self): - """Get a list of artists contained in the figure.""" - return [self.patch, - *self.artists, - *self._localaxes, - *self.lines, - *self.patches, - *self.texts, - *self.images, - *self.legends, - *self.subfigs] - - def contains(self, mouseevent): - """ - Test whether the mouse event occurred on the figure. - - Returns - ------- - bool, {} - """ - inside, info = self._default_contains(mouseevent, figure=self) - if inside is not None: - return inside, info - inside = self.bbox.contains(mouseevent.x, mouseevent.y) - return inside, {} - - @_api.delete_parameter("3.6", "args") - @_api.delete_parameter("3.6", "kwargs") - def get_window_extent(self, renderer=None, *args, **kwargs): - # docstring inherited - return self.bbox - - def _suplabels(self, t, info, **kwargs): - """ - Add a centered %(name)s to the figure. - - Parameters - ---------- - t : str - The %(name)s text. - x : float, default: %(x0)s - The x location of the text in figure coordinates. - y : float, default: %(y0)s - The y location of the text in figure coordinates. - horizontalalignment, ha : {'center', 'left', 'right'}, default: %(ha)s - The horizontal alignment of the text relative to (*x*, *y*). - verticalalignment, va : {'top', 'center', 'bottom', 'baseline'}, \ -default: %(va)s - The vertical alignment of the text relative to (*x*, *y*). - fontsize, size : default: :rc:`figure.%(rc)ssize` - The font size of the text. See `.Text.set_size` for possible - values. - fontweight, weight : default: :rc:`figure.%(rc)sweight` - The font weight of the text. See `.Text.set_weight` for possible - values. - - Returns - ------- - text - The `.Text` instance of the %(name)s. - - Other Parameters - ---------------- - fontproperties : None or dict, optional - A dict of font properties. If *fontproperties* is given the - default values for font size and weight are taken from the - `.FontProperties` defaults. :rc:`figure.%(rc)ssize` and - :rc:`figure.%(rc)sweight` are ignored in this case. - - **kwargs - Additional kwargs are `matplotlib.text.Text` properties. - """ - - suplab = getattr(self, info['name']) - - x = kwargs.pop('x', None) - y = kwargs.pop('y', None) - if info['name'] in ['_supxlabel', '_suptitle']: - autopos = y is None - elif info['name'] == '_supylabel': - autopos = x is None - if x is None: - x = info['x0'] - if y is None: - y = info['y0'] - - if 'horizontalalignment' not in kwargs and 'ha' not in kwargs: - kwargs['horizontalalignment'] = info['ha'] - if 'verticalalignment' not in kwargs and 'va' not in kwargs: - kwargs['verticalalignment'] = info['va'] - if 'rotation' not in kwargs: - kwargs['rotation'] = info['rotation'] - - if 'fontproperties' not in kwargs: - if 'fontsize' not in kwargs and 'size' not in kwargs: - kwargs['size'] = mpl.rcParams[info['size']] - if 'fontweight' not in kwargs and 'weight' not in kwargs: - kwargs['weight'] = mpl.rcParams[info['weight']] - - sup = self.text(x, y, t, **kwargs) - if suplab is not None: - suplab.set_text(t) - suplab.set_position((x, y)) - suplab.update_from(sup) - sup.remove() - else: - suplab = sup - suplab._autopos = autopos - setattr(self, info['name'], suplab) - self.stale = True - return suplab - - @_docstring.Substitution(x0=0.5, y0=0.98, name='suptitle', ha='center', - va='top', rc='title') - @_docstring.copy(_suplabels) - def suptitle(self, t, **kwargs): - # docstring from _suplabels... - info = {'name': '_suptitle', 'x0': 0.5, 'y0': 0.98, - 'ha': 'center', 'va': 'top', 'rotation': 0, - 'size': 'figure.titlesize', 'weight': 'figure.titleweight'} - return self._suplabels(t, info, **kwargs) - - @_docstring.Substitution(x0=0.5, y0=0.01, name='supxlabel', ha='center', - va='bottom', rc='label') - @_docstring.copy(_suplabels) - def supxlabel(self, t, **kwargs): - # docstring from _suplabels... - info = {'name': '_supxlabel', 'x0': 0.5, 'y0': 0.01, - 'ha': 'center', 'va': 'bottom', 'rotation': 0, - 'size': 'figure.labelsize', 'weight': 'figure.labelweight'} - return self._suplabels(t, info, **kwargs) - - @_docstring.Substitution(x0=0.02, y0=0.5, name='supylabel', ha='left', - va='center', rc='label') - @_docstring.copy(_suplabels) - def supylabel(self, t, **kwargs): - # docstring from _suplabels... - info = {'name': '_supylabel', 'x0': 0.02, 'y0': 0.5, - 'ha': 'left', 'va': 'center', 'rotation': 'vertical', - 'rotation_mode': 'anchor', 'size': 'figure.labelsize', - 'weight': 'figure.labelweight'} - return self._suplabels(t, info, **kwargs) - - def get_edgecolor(self): - """Get the edge color of the Figure rectangle.""" - return self.patch.get_edgecolor() - - def get_facecolor(self): - """Get the face color of the Figure rectangle.""" - return self.patch.get_facecolor() - - def get_frameon(self): - """ - Return the figure's background patch visibility, i.e. - whether the figure background will be drawn. Equivalent to - ``Figure.patch.get_visible()``. - """ - return self.patch.get_visible() - - def set_linewidth(self, linewidth): - """ - Set the line width of the Figure rectangle. - - Parameters - ---------- - linewidth : number - """ - self.patch.set_linewidth(linewidth) - - def get_linewidth(self): - """ - Get the line width of the Figure rectangle. - """ - return self.patch.get_linewidth() - - def set_edgecolor(self, color): - """ - Set the edge color of the Figure rectangle. - - Parameters - ---------- - color : color - """ - self.patch.set_edgecolor(color) - - def set_facecolor(self, color): - """ - Set the face color of the Figure rectangle. - - Parameters - ---------- - color : color - """ - self.patch.set_facecolor(color) - - def set_frameon(self, b): - """ - Set the figure's background patch visibility, i.e. - whether the figure background will be drawn. Equivalent to - ``Figure.patch.set_visible()``. - - Parameters - ---------- - b : bool - """ - self.patch.set_visible(b) - self.stale = True - - frameon = property(get_frameon, set_frameon) - - def add_artist(self, artist, clip=False): - """ - Add an `.Artist` to the figure. - - Usually artists are added to `~.axes.Axes` objects using - `.Axes.add_artist`; this method can be used in the rare cases where - one needs to add artists directly to the figure instead. - - Parameters - ---------- - artist : `~matplotlib.artist.Artist` - The artist to add to the figure. If the added artist has no - transform previously set, its transform will be set to - ``figure.transSubfigure``. - clip : bool, default: False - Whether the added artist should be clipped by the figure patch. - - Returns - ------- - `~matplotlib.artist.Artist` - The added artist. - """ - artist.set_figure(self) - self.artists.append(artist) - artist._remove_method = self.artists.remove - - if not artist.is_transform_set(): - artist.set_transform(self.transSubfigure) - - if clip: - artist.set_clip_path(self.patch) - - self.stale = True - return artist - - @_docstring.dedent_interpd - def add_axes(self, *args, **kwargs): - """ - Add an `~.axes.Axes` to the figure. - - Call signatures:: - - add_axes(rect, projection=None, polar=False, **kwargs) - add_axes(ax) - - Parameters - ---------- - rect : tuple (left, bottom, width, height) - The dimensions (left, bottom, width, height) of the new - `~.axes.Axes`. All quantities are in fractions of figure width and - height. - - projection : {None, 'aitoff', 'hammer', 'lambert', 'mollweide', \ -'polar', 'rectilinear', str}, optional - The projection type of the `~.axes.Axes`. *str* is the name of - a custom projection, see `~matplotlib.projections`. The default - None results in a 'rectilinear' projection. - - polar : bool, default: False - If True, equivalent to projection='polar'. - - axes_class : subclass type of `~.axes.Axes`, optional - The `.axes.Axes` subclass that is instantiated. This parameter - is incompatible with *projection* and *polar*. See - :ref:`axisartist_users-guide-index` for examples. - - sharex, sharey : `~matplotlib.axes.Axes`, optional - Share the x or y `~matplotlib.axis` with sharex and/or sharey. - The axis will have the same limits, ticks, and scale as the axis - of the shared axes. - - label : str - A label for the returned Axes. - - Returns - ------- - `~.axes.Axes`, or a subclass of `~.axes.Axes` - The returned axes class depends on the projection used. It is - `~.axes.Axes` if rectilinear projection is used and - `.projections.polar.PolarAxes` if polar projection is used. - - Other Parameters - ---------------- - **kwargs - This method also takes the keyword arguments for - the returned Axes class. The keyword arguments for the - rectilinear Axes class `~.axes.Axes` can be found in - the following table but there might also be other keyword - arguments if another projection is used, see the actual Axes - class. - - %(Axes:kwdoc)s - - Notes - ----- - In rare circumstances, `.add_axes` may be called with a single - argument, an Axes instance already created in the present figure but - not in the figure's list of Axes. - - See Also - -------- - .Figure.add_subplot - .pyplot.subplot - .pyplot.axes - .Figure.subplots - .pyplot.subplots - - Examples - -------- - Some simple examples:: - - rect = l, b, w, h - fig = plt.figure() - fig.add_axes(rect) - fig.add_axes(rect, frameon=False, facecolor='g') - fig.add_axes(rect, polar=True) - ax = fig.add_axes(rect, projection='polar') - fig.delaxes(ax) - fig.add_axes(ax) - """ - - if not len(args) and 'rect' not in kwargs: - raise TypeError( - "add_axes() missing 1 required positional argument: 'rect'") - elif 'rect' in kwargs: - if len(args): - raise TypeError( - "add_axes() got multiple values for argument 'rect'") - args = (kwargs.pop('rect'), ) - - if isinstance(args[0], Axes): - a = args[0] - key = a._projection_init - if a.get_figure() is not self: - raise ValueError( - "The Axes must have been created in the present figure") - else: - rect = args[0] - if not np.isfinite(rect).all(): - raise ValueError('all entries in rect must be finite ' - 'not {}'.format(rect)) - projection_class, pkw = self._process_projection_requirements( - *args, **kwargs) - - # create the new axes using the axes class given - a = projection_class(self, rect, **pkw) - key = (projection_class, pkw) - return self._add_axes_internal(a, key) - - @_docstring.dedent_interpd - def add_subplot(self, *args, **kwargs): - """ - Add an `~.axes.Axes` to the figure as part of a subplot arrangement. - - Call signatures:: - - add_subplot(nrows, ncols, index, **kwargs) - add_subplot(pos, **kwargs) - add_subplot(ax) - add_subplot() - - Parameters - ---------- - *args : int, (int, int, *index*), or `.SubplotSpec`, default: (1, 1, 1) - The position of the subplot described by one of - - - Three integers (*nrows*, *ncols*, *index*). The subplot will - take the *index* position on a grid with *nrows* rows and - *ncols* columns. *index* starts at 1 in the upper left corner - and increases to the right. *index* can also be a two-tuple - specifying the (*first*, *last*) indices (1-based, and including - *last*) of the subplot, e.g., ``fig.add_subplot(3, 1, (1, 2))`` - makes a subplot that spans the upper 2/3 of the figure. - - A 3-digit integer. The digits are interpreted as if given - separately as three single-digit integers, i.e. - ``fig.add_subplot(235)`` is the same as - ``fig.add_subplot(2, 3, 5)``. Note that this can only be used - if there are no more than 9 subplots. - - A `.SubplotSpec`. - - In rare circumstances, `.add_subplot` may be called with a single - argument, a subplot Axes instance already created in the - present figure but not in the figure's list of Axes. - - projection : {None, 'aitoff', 'hammer', 'lambert', 'mollweide', \ -'polar', 'rectilinear', str}, optional - The projection type of the subplot (`~.axes.Axes`). *str* is the - name of a custom projection, see `~matplotlib.projections`. The - default None results in a 'rectilinear' projection. - - polar : bool, default: False - If True, equivalent to projection='polar'. - - axes_class : subclass type of `~.axes.Axes`, optional - The `.axes.Axes` subclass that is instantiated. This parameter - is incompatible with *projection* and *polar*. See - :ref:`axisartist_users-guide-index` for examples. - - sharex, sharey : `~matplotlib.axes.Axes`, optional - Share the x or y `~matplotlib.axis` with sharex and/or sharey. - The axis will have the same limits, ticks, and scale as the axis - of the shared axes. - - label : str - A label for the returned Axes. - - Returns - ------- - `~.axes.Axes` - - The Axes of the subplot. The returned Axes can actually be an - instance of a subclass, such as `.projections.polar.PolarAxes` for - polar projections. - - Other Parameters - ---------------- - **kwargs - This method also takes the keyword arguments for the returned Axes - base class; except for the *figure* argument. The keyword arguments - for the rectilinear base class `~.axes.Axes` can be found in - the following table but there might also be other keyword - arguments if another projection is used. - - %(Axes:kwdoc)s - - See Also - -------- - .Figure.add_axes - .pyplot.subplot - .pyplot.axes - .Figure.subplots - .pyplot.subplots - - Examples - -------- - :: - - fig = plt.figure() - - fig.add_subplot(231) - ax1 = fig.add_subplot(2, 3, 1) # equivalent but more general - - fig.add_subplot(232, frameon=False) # subplot with no frame - fig.add_subplot(233, projection='polar') # polar subplot - fig.add_subplot(234, sharex=ax1) # subplot sharing x-axis with ax1 - fig.add_subplot(235, facecolor="red") # red subplot - - ax1.remove() # delete ax1 from the figure - fig.add_subplot(ax1) # add ax1 back to the figure - """ - if 'figure' in kwargs: - # Axes itself allows for a 'figure' kwarg, but since we want to - # bind the created Axes to self, it is not allowed here. - raise _api.kwarg_error("add_subplot", "figure") - - if (len(args) == 1 - and isinstance(args[0], mpl.axes._base._AxesBase) - and args[0].get_subplotspec()): - ax = args[0] - key = ax._projection_init - if ax.get_figure() is not self: - raise ValueError("The Axes must have been created in " - "the present figure") - else: - if not args: - args = (1, 1, 1) - # Normalize correct ijk values to (i, j, k) here so that - # add_subplot(211) == add_subplot(2, 1, 1). Invalid values will - # trigger errors later (via SubplotSpec._from_subplot_args). - if (len(args) == 1 and isinstance(args[0], Integral) - and 100 <= args[0] <= 999): - args = tuple(map(int, str(args[0]))) - projection_class, pkw = self._process_projection_requirements( - *args, **kwargs) - ax = projection_class(self, *args, **pkw) - key = (projection_class, pkw) - return self._add_axes_internal(ax, key) - - def _add_axes_internal(self, ax, key): - """Private helper for `add_axes` and `add_subplot`.""" - self._axstack.add(ax) - if ax not in self._localaxes: - self._localaxes.append(ax) - self.sca(ax) - ax._remove_method = self.delaxes - # this is to support plt.subplot's re-selection logic - ax._projection_init = key - self.stale = True - ax.stale_callback = _stale_figure_callback - return ax - - def subplots(self, nrows=1, ncols=1, *, sharex=False, sharey=False, - squeeze=True, width_ratios=None, height_ratios=None, - subplot_kw=None, gridspec_kw=None): - """ - Add a set of subplots to this figure. - - This utility wrapper makes it convenient to create common layouts of - subplots in a single call. - - Parameters - ---------- - nrows, ncols : int, default: 1 - Number of rows/columns of the subplot grid. - - sharex, sharey : bool or {'none', 'all', 'row', 'col'}, default: False - Controls sharing of x-axis (*sharex*) or y-axis (*sharey*): - - - True or 'all': x- or y-axis will be shared among all subplots. - - False or 'none': each subplot x- or y-axis will be independent. - - 'row': each subplot row will share an x- or y-axis. - - 'col': each subplot column will share an x- or y-axis. - - When subplots have a shared x-axis along a column, only the x tick - labels of the bottom subplot are created. Similarly, when subplots - have a shared y-axis along a row, only the y tick labels of the - first column subplot are created. To later turn other subplots' - ticklabels on, use `~matplotlib.axes.Axes.tick_params`. - - When subplots have a shared axis that has units, calling - `.Axis.set_units` will update each axis with the new units. - - squeeze : bool, default: True - - If True, extra dimensions are squeezed out from the returned - array of Axes: - - - if only one subplot is constructed (nrows=ncols=1), the - resulting single Axes object is returned as a scalar. - - for Nx1 or 1xM subplots, the returned object is a 1D numpy - object array of Axes objects. - - for NxM, subplots with N>1 and M>1 are returned as a 2D array. - - - If False, no squeezing at all is done: the returned Axes object - is always a 2D array containing Axes instances, even if it ends - up being 1x1. - - width_ratios : array-like of length *ncols*, optional - Defines the relative widths of the columns. Each column gets a - relative width of ``width_ratios[i] / sum(width_ratios)``. - If not given, all columns will have the same width. Equivalent - to ``gridspec_kw={'width_ratios': [...]}``. - - height_ratios : array-like of length *nrows*, optional - Defines the relative heights of the rows. Each row gets a - relative height of ``height_ratios[i] / sum(height_ratios)``. - If not given, all rows will have the same height. Equivalent - to ``gridspec_kw={'height_ratios': [...]}``. - - subplot_kw : dict, optional - Dict with keywords passed to the `.Figure.add_subplot` call used to - create each subplot. - - gridspec_kw : dict, optional - Dict with keywords passed to the - `~matplotlib.gridspec.GridSpec` constructor used to create - the grid the subplots are placed on. - - Returns - ------- - `~.axes.Axes` or array of Axes - Either a single `~matplotlib.axes.Axes` object or an array of Axes - objects if more than one subplot was created. The dimensions of the - resulting array can be controlled with the *squeeze* keyword, see - above. - - See Also - -------- - .pyplot.subplots - .Figure.add_subplot - .pyplot.subplot - - Examples - -------- - :: - - # First create some toy data: - x = np.linspace(0, 2*np.pi, 400) - y = np.sin(x**2) - - # Create a figure - plt.figure() - - # Create a subplot - ax = fig.subplots() - ax.plot(x, y) - ax.set_title('Simple plot') - - # Create two subplots and unpack the output array immediately - ax1, ax2 = fig.subplots(1, 2, sharey=True) - ax1.plot(x, y) - ax1.set_title('Sharing Y axis') - ax2.scatter(x, y) - - # Create four polar Axes and access them through the returned array - axes = fig.subplots(2, 2, subplot_kw=dict(projection='polar')) - axes[0, 0].plot(x, y) - axes[1, 1].scatter(x, y) - - # Share an X-axis with each column of subplots - fig.subplots(2, 2, sharex='col') - - # Share a Y-axis with each row of subplots - fig.subplots(2, 2, sharey='row') - - # Share both X- and Y-axes with all subplots - fig.subplots(2, 2, sharex='all', sharey='all') - - # Note that this is the same as - fig.subplots(2, 2, sharex=True, sharey=True) - """ - gridspec_kw = dict(gridspec_kw or {}) - if height_ratios is not None: - if 'height_ratios' in gridspec_kw: - raise ValueError("'height_ratios' must not be defined both as " - "parameter and as key in 'gridspec_kw'") - gridspec_kw['height_ratios'] = height_ratios - if width_ratios is not None: - if 'width_ratios' in gridspec_kw: - raise ValueError("'width_ratios' must not be defined both as " - "parameter and as key in 'gridspec_kw'") - gridspec_kw['width_ratios'] = width_ratios - - gs = self.add_gridspec(nrows, ncols, figure=self, **gridspec_kw) - axs = gs.subplots(sharex=sharex, sharey=sharey, squeeze=squeeze, - subplot_kw=subplot_kw) - return axs - - def delaxes(self, ax): - """ - Remove the `~.axes.Axes` *ax* from the figure; update the current Axes. - """ - - def _reset_locators_and_formatters(axis): - # Set the formatters and locators to be associated with axis - # (where previously they may have been associated with another - # Axis instance) - axis.get_major_formatter().set_axis(axis) - axis.get_major_locator().set_axis(axis) - axis.get_minor_formatter().set_axis(axis) - axis.get_minor_locator().set_axis(axis) - - def _break_share_link(ax, grouper): - siblings = grouper.get_siblings(ax) - if len(siblings) > 1: - grouper.remove(ax) - for last_ax in siblings: - if ax is not last_ax: - return last_ax - return None - - self._axstack.remove(ax) - self._axobservers.process("_axes_change_event", self) - self.stale = True - self._localaxes.remove(ax) - self.canvas.release_mouse(ax) - - # Break link between any shared axes - for name in ax._axis_names: - last_ax = _break_share_link(ax, ax._shared_axes[name]) - if last_ax is not None: - _reset_locators_and_formatters(getattr(last_ax, f"{name}axis")) - - # Break link between any twinned axes - _break_share_link(ax, ax._twinned_axes) - - def clear(self, keep_observers=False): - """ - Clear the figure. - - Parameters - ---------- - keep_observers : bool, default: False - Set *keep_observers* to True if, for example, - a gui widget is tracking the Axes in the figure. - """ - self.suppressComposite = None - - # first clear the axes in any subfigures - for subfig in self.subfigs: - subfig.clear(keep_observers=keep_observers) - self.subfigs = [] - - for ax in tuple(self.axes): # Iterate over the copy. - ax.clear() - self.delaxes(ax) # Remove ax from self._axstack. - - self.artists = [] - self.lines = [] - self.patches = [] - self.texts = [] - self.images = [] - self.legends = [] - if not keep_observers: - self._axobservers = cbook.CallbackRegistry() - self._suptitle = None - self._supxlabel = None - self._supylabel = None - - self.stale = True - - # synonym for `clear`. - def clf(self, keep_observers=False): - """ - [*Discouraged*] Alias for the `clear()` method. - - .. admonition:: Discouraged - - The use of ``clf()`` is discouraged. Use ``clear()`` instead. - - Parameters - ---------- - keep_observers : bool, default: False - Set *keep_observers* to True if, for example, - a gui widget is tracking the Axes in the figure. - """ - return self.clear(keep_observers=keep_observers) - - # Note: the docstring below is modified with replace for the pyplot - # version of this function because the method name differs (plt.figlegend) - # the replacements are: - # " legend(" -> " figlegend(" for the signatures - # "fig.legend(" -> "plt.figlegend" for the code examples - # "ax.plot" -> "plt.plot" for consistency in using pyplot when able - @_docstring.dedent_interpd - def legend(self, *args, **kwargs): - """ - Place a legend on the figure. - - Call signatures:: - - legend() - legend(handles, labels) - legend(handles=handles) - legend(labels) - - The call signatures correspond to the following different ways to use - this method: - - **1. Automatic detection of elements to be shown in the legend** - - The elements to be added to the legend are automatically determined, - when you do not pass in any extra arguments. - - In this case, the labels are taken from the artist. You can specify - them either at artist creation or by calling the - :meth:`~.Artist.set_label` method on the artist:: - - ax.plot([1, 2, 3], label='Inline label') - fig.legend() - - or:: - - line, = ax.plot([1, 2, 3]) - line.set_label('Label via method') - fig.legend() - - Specific lines can be excluded from the automatic legend element - selection by defining a label starting with an underscore. - This is default for all artists, so calling `.Figure.legend` without - any arguments and without setting the labels manually will result in - no legend being drawn. - - - **2. Explicitly listing the artists and labels in the legend** - - For full control of which artists have a legend entry, it is possible - to pass an iterable of legend artists followed by an iterable of - legend labels respectively:: - - fig.legend([line1, line2, line3], ['label1', 'label2', 'label3']) - - - **3. Explicitly listing the artists in the legend** - - This is similar to 2, but the labels are taken from the artists' - label properties. Example:: - - line1, = ax1.plot([1, 2, 3], label='label1') - line2, = ax2.plot([1, 2, 3], label='label2') - fig.legend(handles=[line1, line2]) - - - **4. Labeling existing plot elements** - - .. admonition:: Discouraged - - This call signature is discouraged, because the relation between - plot elements and labels is only implicit by their order and can - easily be mixed up. - - To make a legend for all artists on all Axes, call this function with - an iterable of strings, one for each legend item. For example:: - - fig, (ax1, ax2) = plt.subplots(1, 2) - ax1.plot([1, 3, 5], color='blue') - ax2.plot([2, 4, 6], color='red') - fig.legend(['the blues', 'the reds']) - - - Parameters - ---------- - handles : list of `.Artist`, optional - A list of Artists (lines, patches) to be added to the legend. - Use this together with *labels*, if you need full control on what - is shown in the legend and the automatic mechanism described above - is not sufficient. - - The length of handles and labels should be the same in this - case. If they are not, they are truncated to the smaller length. - - labels : list of str, optional - A list of labels to show next to the artists. - Use this together with *handles*, if you need full control on what - is shown in the legend and the automatic mechanism described above - is not sufficient. - - Returns - ------- - `~matplotlib.legend.Legend` - - Other Parameters - ---------------- - %(_legend_kw_figure)s - - See Also - -------- - .Axes.legend - - Notes - ----- - Some artists are not supported by this function. See - :doc:`/tutorials/intermediate/legend_guide` for details. - """ - - handles, labels, extra_args, kwargs = mlegend._parse_legend_args( - self.axes, - *args, - **kwargs) - # check for third arg - if len(extra_args): - # _api.warn_deprecated( - # "2.1", - # message="Figure.legend will accept no more than two " - # "positional arguments in the future. Use " - # "'fig.legend(handles, labels, loc=location)' " - # "instead.") - # kwargs['loc'] = extra_args[0] - # extra_args = extra_args[1:] - pass - transform = kwargs.pop('bbox_transform', self.transSubfigure) - # explicitly set the bbox transform if the user hasn't. - l = mlegend.Legend(self, handles, labels, *extra_args, - bbox_transform=transform, **kwargs) - self.legends.append(l) - l._remove_method = self.legends.remove - self.stale = True - return l - - @_docstring.dedent_interpd - def text(self, x, y, s, fontdict=None, **kwargs): - """ - Add text to figure. - - Parameters - ---------- - x, y : float - The position to place the text. By default, this is in figure - coordinates, floats in [0, 1]. The coordinate system can be changed - using the *transform* keyword. - - s : str - The text string. - - fontdict : dict, optional - A dictionary to override the default text properties. If not given, - the defaults are determined by :rc:`font.*`. Properties passed as - *kwargs* override the corresponding ones given in *fontdict*. - - Returns - ------- - `~.text.Text` - - Other Parameters - ---------------- - **kwargs : `~matplotlib.text.Text` properties - Other miscellaneous text parameters. - - %(Text:kwdoc)s - - See Also - -------- - .Axes.text - .pyplot.text - """ - effective_kwargs = { - 'transform': self.transSubfigure, - **(fontdict if fontdict is not None else {}), - **kwargs, - } - text = Text(x=x, y=y, text=s, **effective_kwargs) - text.set_figure(self) - text.stale_callback = _stale_figure_callback - - self.texts.append(text) - text._remove_method = self.texts.remove - self.stale = True - return text - - @_docstring.dedent_interpd - def colorbar( - self, mappable, cax=None, ax=None, use_gridspec=True, **kwargs): - """ - Add a colorbar to a plot. - - Parameters - ---------- - mappable - The `matplotlib.cm.ScalarMappable` (i.e., `.AxesImage`, - `.ContourSet`, etc.) described by this colorbar. This argument is - mandatory for the `.Figure.colorbar` method but optional for the - `.pyplot.colorbar` function, which sets the default to the current - image. - - Note that one can create a `.ScalarMappable` "on-the-fly" to - generate colorbars not attached to a previously drawn artist, e.g. - :: - - fig.colorbar(cm.ScalarMappable(norm=norm, cmap=cmap), ax=ax) - - cax : `~matplotlib.axes.Axes`, optional - Axes into which the colorbar will be drawn. If `None`, then a new - Axes is created and the space for it will be stolen from the Axes(s) - specified in *ax*. - - ax : `~matplotlib.axes.Axes` or iterable or `numpy.ndarray` of Axes, optional - The one or more parent Axes from which space for a new colorbar Axes - will be stolen. This parameter is only used if *cax* is not set. - - Defaults to the Axes that contains the mappable used to create the - colorbar. - - use_gridspec : bool, optional - If *cax* is ``None``, a new *cax* is created as an instance of - Axes. If *ax* is positioned with a subplotspec and *use_gridspec* - is ``True``, then *cax* is also positioned with a subplotspec. - - Returns - ------- - colorbar : `~matplotlib.colorbar.Colorbar` - - Other Parameters - ---------------- - %(_make_axes_kw_doc)s - %(_colormap_kw_doc)s - - Notes - ----- - If *mappable* is a `~.contour.ContourSet`, its *extend* kwarg is - included automatically. - - The *shrink* kwarg provides a simple way to scale the colorbar with - respect to the axes. Note that if *cax* is specified, it determines the - size of the colorbar and *shrink* and *aspect* kwargs are ignored. - - For more precise control, you can manually specify the positions of the - axes objects in which the mappable and the colorbar are drawn. In this - case, do not use any of the axes properties kwargs. - - It is known that some vector graphics viewers (svg and pdf) renders - white gaps between segments of the colorbar. This is due to bugs in - the viewers, not Matplotlib. As a workaround, the colorbar can be - rendered with overlapping segments:: - - cbar = colorbar() - cbar.solids.set_edgecolor("face") - draw() - - However, this has negative consequences in other circumstances, e.g. - with semi-transparent images (alpha < 1) and colorbar extensions; - therefore, this workaround is not used by default (see issue #1188). - - """ - - if ax is None: - ax = getattr(mappable, "axes", None) - - if (self.get_layout_engine() is not None and - not self.get_layout_engine().colorbar_gridspec): - use_gridspec = False - # Store the value of gca so that we can set it back later on. - if cax is None: - if ax is None: - _api.warn_deprecated("3.6", message=( - 'Unable to determine Axes to steal space for Colorbar. ' - 'Using gca(), but will raise in the future. ' - 'Either provide the *cax* argument to use as the Axes for ' - 'the Colorbar, provide the *ax* argument to steal space ' - 'from it, or add *mappable* to an Axes.')) - ax = self.gca() - current_ax = self.gca() - userax = False - if (use_gridspec - and isinstance(ax, mpl.axes._base._AxesBase) - and ax.get_subplotspec()): - cax, kwargs = cbar.make_axes_gridspec(ax, **kwargs) - else: - cax, kwargs = cbar.make_axes(ax, **kwargs) - cax.grid(visible=False, which='both', axis='both') - else: - userax = True - - # need to remove kws that cannot be passed to Colorbar - NON_COLORBAR_KEYS = ['fraction', 'pad', 'shrink', 'aspect', 'anchor', - 'panchor'] - cb_kw = {k: v for k, v in kwargs.items() if k not in NON_COLORBAR_KEYS} - - cb = cbar.Colorbar(cax, mappable, **cb_kw) - - if not userax: - self.sca(current_ax) - self.stale = True - return cb - - def subplots_adjust(self, left=None, bottom=None, right=None, top=None, - wspace=None, hspace=None): - """ - Adjust the subplot layout parameters. - - Unset parameters are left unmodified; initial values are given by - :rc:`figure.subplot.[name]`. - - Parameters - ---------- - left : float, optional - The position of the left edge of the subplots, - as a fraction of the figure width. - right : float, optional - The position of the right edge of the subplots, - as a fraction of the figure width. - bottom : float, optional - The position of the bottom edge of the subplots, - as a fraction of the figure height. - top : float, optional - The position of the top edge of the subplots, - as a fraction of the figure height. - wspace : float, optional - The width of the padding between subplots, - as a fraction of the average Axes width. - hspace : float, optional - The height of the padding between subplots, - as a fraction of the average Axes height. - """ - if (self.get_layout_engine() is not None and - not self.get_layout_engine().adjust_compatible): - _api.warn_external( - "This figure was using a layout engine that is " - "incompatible with subplots_adjust and/or tight_layout; " - "not calling subplots_adjust.") - return - self.subplotpars.update(left, bottom, right, top, wspace, hspace) - for ax in self.axes: - if ax.get_subplotspec() is not None: - ax._set_position(ax.get_subplotspec().get_position(self)) - self.stale = True - - def align_xlabels(self, axs=None): - """ - Align the xlabels of subplots in the same subplot column if label - alignment is being done automatically (i.e. the label position is - not manually set). - - Alignment persists for draw events after this is called. - - If a label is on the bottom, it is aligned with labels on Axes that - also have their label on the bottom and that have the same - bottom-most subplot row. If the label is on the top, - it is aligned with labels on Axes with the same top-most row. - - Parameters - ---------- - axs : list of `~matplotlib.axes.Axes` - Optional list of (or `~numpy.ndarray`) `~matplotlib.axes.Axes` - to align the xlabels. - Default is to align all Axes on the figure. - - See Also - -------- - matplotlib.figure.Figure.align_ylabels - matplotlib.figure.Figure.align_labels - - Notes - ----- - This assumes that ``axs`` are from the same `.GridSpec`, so that - their `.SubplotSpec` positions correspond to figure positions. - - Examples - -------- - Example with rotated xtick labels:: - - fig, axs = plt.subplots(1, 2) - for tick in axs[0].get_xticklabels(): - tick.set_rotation(55) - axs[0].set_xlabel('XLabel 0') - axs[1].set_xlabel('XLabel 1') - fig.align_xlabels() - """ - if axs is None: - axs = self.axes - axs = [ax for ax in np.ravel(axs) if ax.get_subplotspec() is not None] - for ax in axs: - _log.debug(' Working on: %s', ax.get_xlabel()) - rowspan = ax.get_subplotspec().rowspan - pos = ax.xaxis.get_label_position() # top or bottom - # Search through other axes for label positions that are same as - # this one and that share the appropriate row number. - # Add to a grouper associated with each axes of siblings. - # This list is inspected in `axis.draw` by - # `axis._update_label_position`. - for axc in axs: - if axc.xaxis.get_label_position() == pos: - rowspanc = axc.get_subplotspec().rowspan - if (pos == 'top' and rowspan.start == rowspanc.start or - pos == 'bottom' and rowspan.stop == rowspanc.stop): - # grouper for groups of xlabels to align - self._align_label_groups['x'].join(ax, axc) - - def align_ylabels(self, axs=None): - """ - Align the ylabels of subplots in the same subplot column if label - alignment is being done automatically (i.e. the label position is - not manually set). - - Alignment persists for draw events after this is called. - - If a label is on the left, it is aligned with labels on Axes that - also have their label on the left and that have the same - left-most subplot column. If the label is on the right, - it is aligned with labels on Axes with the same right-most column. - - Parameters - ---------- - axs : list of `~matplotlib.axes.Axes` - Optional list (or `~numpy.ndarray`) of `~matplotlib.axes.Axes` - to align the ylabels. - Default is to align all Axes on the figure. - - See Also - -------- - matplotlib.figure.Figure.align_xlabels - matplotlib.figure.Figure.align_labels - - Notes - ----- - This assumes that ``axs`` are from the same `.GridSpec`, so that - their `.SubplotSpec` positions correspond to figure positions. - - Examples - -------- - Example with large yticks labels:: - - fig, axs = plt.subplots(2, 1) - axs[0].plot(np.arange(0, 1000, 50)) - axs[0].set_ylabel('YLabel 0') - axs[1].set_ylabel('YLabel 1') - fig.align_ylabels() - """ - if axs is None: - axs = self.axes - axs = [ax for ax in np.ravel(axs) if ax.get_subplotspec() is not None] - for ax in axs: - _log.debug(' Working on: %s', ax.get_ylabel()) - colspan = ax.get_subplotspec().colspan - pos = ax.yaxis.get_label_position() # left or right - # Search through other axes for label positions that are same as - # this one and that share the appropriate column number. - # Add to a list associated with each axes of siblings. - # This list is inspected in `axis.draw` by - # `axis._update_label_position`. - for axc in axs: - if axc.yaxis.get_label_position() == pos: - colspanc = axc.get_subplotspec().colspan - if (pos == 'left' and colspan.start == colspanc.start or - pos == 'right' and colspan.stop == colspanc.stop): - # grouper for groups of ylabels to align - self._align_label_groups['y'].join(ax, axc) - - def align_labels(self, axs=None): - """ - Align the xlabels and ylabels of subplots with the same subplots - row or column (respectively) if label alignment is being - done automatically (i.e. the label position is not manually set). - - Alignment persists for draw events after this is called. - - Parameters - ---------- - axs : list of `~matplotlib.axes.Axes` - Optional list (or `~numpy.ndarray`) of `~matplotlib.axes.Axes` - to align the labels. - Default is to align all Axes on the figure. - - See Also - -------- - matplotlib.figure.Figure.align_xlabels - - matplotlib.figure.Figure.align_ylabels - """ - self.align_xlabels(axs=axs) - self.align_ylabels(axs=axs) - - def add_gridspec(self, nrows=1, ncols=1, **kwargs): - """ - Return a `.GridSpec` that has this figure as a parent. This allows - complex layout of Axes in the figure. - - Parameters - ---------- - nrows : int, default: 1 - Number of rows in grid. - - ncols : int, default: 1 - Number of columns in grid. - - Returns - ------- - `.GridSpec` - - Other Parameters - ---------------- - **kwargs - Keyword arguments are passed to `.GridSpec`. - - See Also - -------- - matplotlib.pyplot.subplots - - Examples - -------- - Adding a subplot that spans two rows:: - - fig = plt.figure() - gs = fig.add_gridspec(2, 2) - ax1 = fig.add_subplot(gs[0, 0]) - ax2 = fig.add_subplot(gs[1, 0]) - # spans two rows: - ax3 = fig.add_subplot(gs[:, 1]) - - """ - - _ = kwargs.pop('figure', None) # pop in case user has added this... - gs = GridSpec(nrows=nrows, ncols=ncols, figure=self, **kwargs) - return gs - - def subfigures(self, nrows=1, ncols=1, squeeze=True, - wspace=None, hspace=None, - width_ratios=None, height_ratios=None, - **kwargs): - """ - Add a set of subfigures to this figure or subfigure. - - A subfigure has the same artist methods as a figure, and is logically - the same as a figure, but cannot print itself. - See :doc:`/gallery/subplots_axes_and_figures/subfigures`. - - .. note:: - The *subfigure* concept is new in v3.4, and the API is still provisional. - - Parameters - ---------- - nrows, ncols : int, default: 1 - Number of rows/columns of the subfigure grid. - - squeeze : bool, default: True - If True, extra dimensions are squeezed out from the returned - array of subfigures. - - wspace, hspace : float, default: None - The amount of width/height reserved for space between subfigures, - expressed as a fraction of the average subfigure width/height. - If not given, the values will be inferred from rcParams if using - constrained layout (see `~.ConstrainedLayoutEngine`), or zero if - not using a layout engine. - - width_ratios : array-like of length *ncols*, optional - Defines the relative widths of the columns. Each column gets a - relative width of ``width_ratios[i] / sum(width_ratios)``. - If not given, all columns will have the same width. - - height_ratios : array-like of length *nrows*, optional - Defines the relative heights of the rows. Each row gets a - relative height of ``height_ratios[i] / sum(height_ratios)``. - If not given, all rows will have the same height. - """ - gs = GridSpec(nrows=nrows, ncols=ncols, figure=self, - wspace=wspace, hspace=hspace, - width_ratios=width_ratios, - height_ratios=height_ratios, - left=0, right=1, bottom=0, top=1) - - sfarr = np.empty((nrows, ncols), dtype=object) - for i in range(ncols): - for j in range(nrows): - sfarr[j, i] = self.add_subfigure(gs[j, i], **kwargs) - - if self.get_layout_engine() is None and (wspace is not None or - hspace is not None): - # Gridspec wspace and hspace is ignored on subfigure instantiation, - # and no space is left. So need to account for it here if required. - bottoms, tops, lefts, rights = gs.get_grid_positions(self) - for sfrow, bottom, top in zip(sfarr, bottoms, tops): - for sf, left, right in zip(sfrow, lefts, rights): - bbox = Bbox.from_extents(left, bottom, right, top) - sf._redo_transform_rel_fig(bbox=bbox) - - if squeeze: - # Discarding unneeded dimensions that equal 1. If we only have one - # subfigure, just return it instead of a 1-element array. - return sfarr.item() if sfarr.size == 1 else sfarr.squeeze() - else: - # Returned axis array will be always 2-d, even if nrows=ncols=1. - return sfarr - - def add_subfigure(self, subplotspec, **kwargs): - """ - Add a `.SubFigure` to the figure as part of a subplot arrangement. - - Parameters - ---------- - subplotspec : `.gridspec.SubplotSpec` - Defines the region in a parent gridspec where the subfigure will - be placed. - - Returns - ------- - `.SubFigure` - - Other Parameters - ---------------- - **kwargs - Are passed to the `.SubFigure` object. - - See Also - -------- - .Figure.subfigures - """ - sf = SubFigure(self, subplotspec, **kwargs) - self.subfigs += [sf] - return sf - - def sca(self, a): - """Set the current Axes to be *a* and return *a*.""" - self._axstack.bubble(a) - self._axobservers.process("_axes_change_event", self) - return a - - def gca(self): - """ - Get the current Axes. - - If there is currently no Axes on this Figure, a new one is created - using `.Figure.add_subplot`. (To test whether there is currently an - Axes on a Figure, check whether ``figure.axes`` is empty. To test - whether there is currently a Figure on the pyplot figure stack, check - whether `.pyplot.get_fignums()` is empty.) - """ - ax = self._axstack.current() - return ax if ax is not None else self.add_subplot() - - def _gci(self): - # Helper for `~matplotlib.pyplot.gci`. Do not use elsewhere. - """ - Get the current colorable artist. - - Specifically, returns the current `.ScalarMappable` instance (`.Image` - created by `imshow` or `figimage`, `.Collection` created by `pcolor` or - `scatter`, etc.), or *None* if no such instance has been defined. - - The current image is an attribute of the current Axes, or the nearest - earlier Axes in the current figure that contains an image. - - Notes - ----- - Historically, the only colorable artists were images; hence the name - ``gci`` (get current image). - """ - # Look first for an image in the current Axes. - ax = self._axstack.current() - if ax is None: - return None - im = ax._gci() - if im is not None: - return im - # If there is no image in the current Axes, search for - # one in a previously created Axes. Whether this makes - # sense is debatable, but it is the documented behavior. - for ax in reversed(self.axes): - im = ax._gci() - if im is not None: - return im - return None - - def _process_projection_requirements( - self, *args, axes_class=None, polar=False, projection=None, - **kwargs): - """ - Handle the args/kwargs to add_axes/add_subplot/gca, returning:: - - (axes_proj_class, proj_class_kwargs) - - which can be used for new Axes initialization/identification. - """ - if axes_class is not None: - if polar or projection is not None: - raise ValueError( - "Cannot combine 'axes_class' and 'projection' or 'polar'") - projection_class = axes_class - else: - - if polar: - if projection is not None and projection != 'polar': - raise ValueError( - f"polar={polar}, yet projection={projection!r}. " - "Only one of these arguments should be supplied." - ) - projection = 'polar' - - if isinstance(projection, str) or projection is None: - projection_class = projections.get_projection_class(projection) - elif hasattr(projection, '_as_mpl_axes'): - projection_class, extra_kwargs = projection._as_mpl_axes() - kwargs.update(**extra_kwargs) - else: - raise TypeError( - f"projection must be a string, None or implement a " - f"_as_mpl_axes method, not {projection!r}") - return projection_class, kwargs - - def get_default_bbox_extra_artists(self): - bbox_artists = [artist for artist in self.get_children() - if (artist.get_visible() and artist.get_in_layout())] - for ax in self.axes: - if ax.get_visible(): - bbox_artists.extend(ax.get_default_bbox_extra_artists()) - return bbox_artists - - def get_tightbbox(self, renderer=None, bbox_extra_artists=None): - """ - Return a (tight) bounding box of the figure *in inches*. - - Note that `.FigureBase` differs from all other artists, which return - their `.Bbox` in pixels. - - Artists that have ``artist.set_in_layout(False)`` are not included - in the bbox. - - Parameters - ---------- - renderer : `.RendererBase` subclass - Renderer that will be used to draw the figures (i.e. - ``fig.canvas.get_renderer()``) - - bbox_extra_artists : list of `.Artist` or ``None`` - List of artists to include in the tight bounding box. If - ``None`` (default), then all artist children of each Axes are - included in the tight bounding box. - - Returns - ------- - `.BboxBase` - containing the bounding box (in figure inches). - """ - - if renderer is None: - renderer = self.figure._get_renderer() - - bb = [] - if bbox_extra_artists is None: - artists = self.get_default_bbox_extra_artists() - else: - artists = bbox_extra_artists - - for a in artists: - bbox = a.get_tightbbox(renderer) - if bbox is not None: - bb.append(bbox) - - for ax in self.axes: - if ax.get_visible(): - # some axes don't take the bbox_extra_artists kwarg so we - # need this conditional.... - try: - bbox = ax.get_tightbbox( - renderer, bbox_extra_artists=bbox_extra_artists) - except TypeError: - bbox = ax.get_tightbbox(renderer) - bb.append(bbox) - bb = [b for b in bb - if (np.isfinite(b.width) and np.isfinite(b.height) - and (b.width != 0 or b.height != 0))] - - isfigure = hasattr(self, 'bbox_inches') - if len(bb) == 0: - if isfigure: - return self.bbox_inches - else: - # subfigures do not have bbox_inches, but do have a bbox - bb = [self.bbox] - - _bbox = Bbox.union(bb) - - if isfigure: - # transform from pixels to inches... - _bbox = TransformedBbox(_bbox, self.dpi_scale_trans.inverted()) - - return _bbox - - @staticmethod - def _norm_per_subplot_kw(per_subplot_kw): - expanded = {} - for k, v in per_subplot_kw.items(): - if isinstance(k, tuple): - for sub_key in k: - if sub_key in expanded: - raise ValueError( - f'The key {sub_key!r} appears multiple times.' - ) - expanded[sub_key] = v - else: - if k in expanded: - raise ValueError( - f'The key {k!r} appears multiple times.' - ) - expanded[k] = v - return expanded - - @staticmethod - def _normalize_grid_string(layout): - if '\n' not in layout: - # single-line string - return [list(ln) for ln in layout.split(';')] - else: - # multi-line string - layout = inspect.cleandoc(layout) - return [list(ln) for ln in layout.strip('\n').split('\n')] - - def subplot_mosaic(self, mosaic, *, sharex=False, sharey=False, - width_ratios=None, height_ratios=None, - empty_sentinel='.', - subplot_kw=None, per_subplot_kw=None, gridspec_kw=None): - """ - Build a layout of Axes based on ASCII art or nested lists. - - This is a helper function to build complex GridSpec layouts visually. - - See :doc:`/gallery/subplots_axes_and_figures/mosaic` - for an example and full API documentation - - Parameters - ---------- - mosaic : list of list of {hashable or nested} or str - - A visual layout of how you want your Axes to be arranged - labeled as strings. For example :: - - x = [['A panel', 'A panel', 'edge'], - ['C panel', '.', 'edge']] - - produces 4 Axes: - - - 'A panel' which is 1 row high and spans the first two columns - - 'edge' which is 2 rows high and is on the right edge - - 'C panel' which in 1 row and 1 column wide in the bottom left - - a blank space 1 row and 1 column wide in the bottom center - - Any of the entries in the layout can be a list of lists - of the same form to create nested layouts. - - If input is a str, then it can either be a multi-line string of - the form :: - - ''' - AAE - C.E - ''' - - where each character is a column and each line is a row. Or it - can be a single-line string where rows are separated by ``;``:: - - 'AB;CC' - - The string notation allows only single character Axes labels and - does not support nesting but is very terse. - - The Axes identifiers may be `str` or a non-iterable hashable - object (e.g. `tuple` s may not be used). - - sharex, sharey : bool, default: False - If True, the x-axis (*sharex*) or y-axis (*sharey*) will be shared - among all subplots. In that case, tick label visibility and axis - units behave as for `subplots`. If False, each subplot's x- or - y-axis will be independent. - - width_ratios : array-like of length *ncols*, optional - Defines the relative widths of the columns. Each column gets a - relative width of ``width_ratios[i] / sum(width_ratios)``. - If not given, all columns will have the same width. Equivalent - to ``gridspec_kw={'width_ratios': [...]}``. In the case of nested - layouts, this argument applies only to the outer layout. - - height_ratios : array-like of length *nrows*, optional - Defines the relative heights of the rows. Each row gets a - relative height of ``height_ratios[i] / sum(height_ratios)``. - If not given, all rows will have the same height. Equivalent - to ``gridspec_kw={'height_ratios': [...]}``. In the case of nested - layouts, this argument applies only to the outer layout. - - subplot_kw : dict, optional - Dictionary with keywords passed to the `.Figure.add_subplot` call - used to create each subplot. These values may be overridden by - values in *per_subplot_kw*. - - per_subplot_kw : dict, optional - A dictionary mapping the Axes identifiers or tuples of identifiers - to a dictionary of keyword arguments to be passed to the - `.Figure.add_subplot` call used to create each subplot. The values - in these dictionaries have precedence over the values in - *subplot_kw*. - - If *mosaic* is a string, and thus all keys are single characters, - it is possible to use a single string instead of a tuple as keys; - i.e. ``"AB"`` is equivalent to ``("A", "B")``. - - .. versionadded:: 3.7 - - gridspec_kw : dict, optional - Dictionary with keywords passed to the `.GridSpec` constructor used - to create the grid the subplots are placed on. In the case of - nested layouts, this argument applies only to the outer layout. - For more complex layouts, users should use `.Figure.subfigures` - to create the nesting. - - empty_sentinel : object, optional - Entry in the layout to mean "leave this space empty". Defaults - to ``'.'``. Note, if *layout* is a string, it is processed via - `inspect.cleandoc` to remove leading white space, which may - interfere with using white-space as the empty sentinel. - - Returns - ------- - dict[label, Axes] - A dictionary mapping the labels to the Axes objects. The order of - the axes is left-to-right and top-to-bottom of their position in the - total layout. - - """ - subplot_kw = subplot_kw or {} - gridspec_kw = dict(gridspec_kw or {}) - per_subplot_kw = per_subplot_kw or {} - - if height_ratios is not None: - if 'height_ratios' in gridspec_kw: - raise ValueError("'height_ratios' must not be defined both as " - "parameter and as key in 'gridspec_kw'") - gridspec_kw['height_ratios'] = height_ratios - if width_ratios is not None: - if 'width_ratios' in gridspec_kw: - raise ValueError("'width_ratios' must not be defined both as " - "parameter and as key in 'gridspec_kw'") - gridspec_kw['width_ratios'] = width_ratios - - # special-case string input - if isinstance(mosaic, str): - mosaic = self._normalize_grid_string(mosaic) - per_subplot_kw = { - tuple(k): v for k, v in per_subplot_kw.items() - } - - per_subplot_kw = self._norm_per_subplot_kw(per_subplot_kw) - - # Only accept strict bools to allow a possible future API expansion. - _api.check_isinstance(bool, sharex=sharex, sharey=sharey) - - def _make_array(inp): - """ - Convert input into 2D array - - We need to have this internal function rather than - ``np.asarray(..., dtype=object)`` so that a list of lists - of lists does not get converted to an array of dimension > - 2 - - Returns - ------- - 2D object array - - """ - r0, *rest = inp - if isinstance(r0, str): - raise ValueError('List mosaic specification must be 2D') - for j, r in enumerate(rest, start=1): - if isinstance(r, str): - raise ValueError('List mosaic specification must be 2D') - if len(r0) != len(r): - raise ValueError( - "All of the rows must be the same length, however " - f"the first row ({r0!r}) has length {len(r0)} " - f"and row {j} ({r!r}) has length {len(r)}." - ) - out = np.zeros((len(inp), len(r0)), dtype=object) - for j, r in enumerate(inp): - for k, v in enumerate(r): - out[j, k] = v - return out - - def _identify_keys_and_nested(mosaic): - """ - Given a 2D object array, identify unique IDs and nested mosaics - - Parameters - ---------- - mosaic : 2D numpy object array - - Returns - ------- - unique_ids : tuple - The unique non-sub mosaic entries in this mosaic - nested : dict[tuple[int, int]], 2D object array - """ - # make sure we preserve the user supplied order - unique_ids = cbook._OrderedSet() - nested = {} - for j, row in enumerate(mosaic): - for k, v in enumerate(row): - if v == empty_sentinel: - continue - elif not cbook.is_scalar_or_string(v): - nested[(j, k)] = _make_array(v) - else: - unique_ids.add(v) - - return tuple(unique_ids), nested - - def _do_layout(gs, mosaic, unique_ids, nested): - """ - Recursively do the mosaic. - - Parameters - ---------- - gs : GridSpec - mosaic : 2D object array - The input converted to a 2D numpy array for this level. - unique_ids : tuple - The identified scalar labels at this level of nesting. - nested : dict[tuple[int, int]], 2D object array - The identified nested mosaics, if any. - - Returns - ------- - dict[label, Axes] - A flat dict of all of the Axes created. - """ - output = dict() - - # we need to merge together the Axes at this level and the axes - # in the (recursively) nested sub-mosaics so that we can add - # them to the figure in the "natural" order if you were to - # ravel in c-order all of the Axes that will be created - # - # This will stash the upper left index of each object (axes or - # nested mosaic) at this level - this_level = dict() - - # go through the unique keys, - for name in unique_ids: - # sort out where each axes starts/ends - indx = np.argwhere(mosaic == name) - start_row, start_col = np.min(indx, axis=0) - end_row, end_col = np.max(indx, axis=0) + 1 - # and construct the slice object - slc = (slice(start_row, end_row), slice(start_col, end_col)) - # some light error checking - if (mosaic[slc] != name).any(): - raise ValueError( - f"While trying to layout\n{mosaic!r}\n" - f"we found that the label {name!r} specifies a " - "non-rectangular or non-contiguous area.") - # and stash this slice for later - this_level[(start_row, start_col)] = (name, slc, 'axes') - - # do the same thing for the nested mosaics (simpler because these - # can not be spans yet!) - for (j, k), nested_mosaic in nested.items(): - this_level[(j, k)] = (None, nested_mosaic, 'nested') - - # now go through the things in this level and add them - # in order left-to-right top-to-bottom - for key in sorted(this_level): - name, arg, method = this_level[key] - # we are doing some hokey function dispatch here based - # on the 'method' string stashed above to sort out if this - # element is an Axes or a nested mosaic. - if method == 'axes': - slc = arg - # add a single axes - if name in output: - raise ValueError(f"There are duplicate keys {name} " - f"in the layout\n{mosaic!r}") - ax = self.add_subplot( - gs[slc], **{ - 'label': str(name), - **subplot_kw, - **per_subplot_kw.get(name, {}) - } - ) - output[name] = ax - elif method == 'nested': - nested_mosaic = arg - j, k = key - # recursively add the nested mosaic - rows, cols = nested_mosaic.shape - nested_output = _do_layout( - gs[j, k].subgridspec(rows, cols), - nested_mosaic, - *_identify_keys_and_nested(nested_mosaic) - ) - overlap = set(output) & set(nested_output) - if overlap: - raise ValueError( - f"There are duplicate keys {overlap} " - f"between the outer layout\n{mosaic!r}\n" - f"and the nested layout\n{nested_mosaic}" - ) - output.update(nested_output) - else: - raise RuntimeError("This should never happen") - return output - - mosaic = _make_array(mosaic) - rows, cols = mosaic.shape - gs = self.add_gridspec(rows, cols, **gridspec_kw) - ret = _do_layout(gs, mosaic, *_identify_keys_and_nested(mosaic)) - ax0 = next(iter(ret.values())) - for ax in ret.values(): - if sharex: - ax.sharex(ax0) - ax._label_outer_xaxis(check_patch=True) - if sharey: - ax.sharey(ax0) - ax._label_outer_yaxis(check_patch=True) - if extra := set(per_subplot_kw) - set(ret): - raise ValueError( - f"The keys {extra} are in *per_subplot_kw* " - "but not in the mosaic." - ) - return ret - - def _set_artist_props(self, a): - if a != self: - a.set_figure(self) - a.stale_callback = _stale_figure_callback - a.set_transform(self.transSubfigure) - - -@_docstring.interpd -class SubFigure(FigureBase): - """ - Logical figure that can be placed inside a figure. - - Typically instantiated using `.Figure.add_subfigure` or - `.SubFigure.add_subfigure`, or `.SubFigure.subfigures`. A subfigure has - the same methods as a figure except for those particularly tied to the size - or dpi of the figure, and is confined to a prescribed region of the figure. - For example the following puts two subfigures side-by-side:: - - fig = plt.figure() - sfigs = fig.subfigures(1, 2) - axsL = sfigs[0].subplots(1, 2) - axsR = sfigs[1].subplots(2, 1) - - See :doc:`/gallery/subplots_axes_and_figures/subfigures` - - .. note:: - The *subfigure* concept is new in v3.4, and the API is still provisional. - """ - callbacks = _api.deprecated( - "3.6", alternative=("the 'resize_event' signal in " - "Figure.canvas.callbacks") - )(property(lambda self: self._fig_callbacks)) - - def __init__(self, parent, subplotspec, *, - facecolor=None, - edgecolor=None, - linewidth=0.0, - frameon=None, - **kwargs): - """ - Parameters - ---------- - parent : `.Figure` or `.SubFigure` - Figure or subfigure that contains the SubFigure. SubFigures - can be nested. - - subplotspec : `.gridspec.SubplotSpec` - Defines the region in a parent gridspec where the subfigure will - be placed. - - facecolor : default: :rc:`figure.facecolor` - The figure patch face color. - - edgecolor : default: :rc:`figure.edgecolor` - The figure patch edge color. - - linewidth : float - The linewidth of the frame (i.e. the edge linewidth of the figure - patch). - - frameon : bool, default: :rc:`figure.frameon` - If ``False``, suppress drawing the figure background patch. - - Other Parameters - ---------------- - **kwargs : `.SubFigure` properties, optional - - %(SubFigure:kwdoc)s - """ - super().__init__(**kwargs) - if facecolor is None: - facecolor = mpl.rcParams['figure.facecolor'] - if edgecolor is None: - edgecolor = mpl.rcParams['figure.edgecolor'] - if frameon is None: - frameon = mpl.rcParams['figure.frameon'] - - self._subplotspec = subplotspec - self._parent = parent - self.figure = parent.figure - self._fig_callbacks = parent._fig_callbacks - - # subfigures use the parent axstack - self._axstack = parent._axstack - self.subplotpars = parent.subplotpars - self.dpi_scale_trans = parent.dpi_scale_trans - self._axobservers = parent._axobservers - self.canvas = parent.canvas - self.transFigure = parent.transFigure - self.bbox_relative = None - self._redo_transform_rel_fig() - self.figbbox = self._parent.figbbox - self.bbox = TransformedBbox(self.bbox_relative, - self._parent.transSubfigure) - self.transSubfigure = BboxTransformTo(self.bbox) - - self.patch = Rectangle( - xy=(0, 0), width=1, height=1, visible=frameon, - facecolor=facecolor, edgecolor=edgecolor, linewidth=linewidth, - # Don't let the figure patch influence bbox calculation. - in_layout=False, transform=self.transSubfigure) - self._set_artist_props(self.patch) - self.patch.set_antialiased(False) - - @property - def dpi(self): - return self._parent.dpi - - @dpi.setter - def dpi(self, value): - self._parent.dpi = value - - def get_dpi(self): - """ - Return the resolution of the parent figure in dots-per-inch as a float. - """ - return self._parent.dpi - - def set_dpi(self, val): - """ - Set the resolution of parent figure in dots-per-inch. - - Parameters - ---------- - val : float - """ - self._parent.dpi = val - self.stale = True - - def _get_renderer(self): - return self._parent._get_renderer() - - def _redo_transform_rel_fig(self, bbox=None): - """ - Make the transSubfigure bbox relative to Figure transform. - - Parameters - ---------- - bbox : bbox or None - If not None, then the bbox is used for relative bounding box. - Otherwise, it is calculated from the subplotspec. - """ - if bbox is not None: - self.bbox_relative.p0 = bbox.p0 - self.bbox_relative.p1 = bbox.p1 - return - # need to figure out *where* this subplotspec is. - gs = self._subplotspec.get_gridspec() - wr = np.asarray(gs.get_width_ratios()) - hr = np.asarray(gs.get_height_ratios()) - dx = wr[self._subplotspec.colspan].sum() / wr.sum() - dy = hr[self._subplotspec.rowspan].sum() / hr.sum() - x0 = wr[:self._subplotspec.colspan.start].sum() / wr.sum() - y0 = 1 - hr[:self._subplotspec.rowspan.stop].sum() / hr.sum() - if self.bbox_relative is None: - self.bbox_relative = Bbox.from_bounds(x0, y0, dx, dy) - else: - self.bbox_relative.p0 = (x0, y0) - self.bbox_relative.p1 = (x0 + dx, y0 + dy) - - def get_constrained_layout(self): - """ - Return whether constrained layout is being used. - - See :doc:`/tutorials/intermediate/constrainedlayout_guide`. - """ - return self._parent.get_constrained_layout() - - def get_constrained_layout_pads(self, relative=False): - """ - Get padding for ``constrained_layout``. - - Returns a list of ``w_pad, h_pad`` in inches and - ``wspace`` and ``hspace`` as fractions of the subplot. - - See :doc:`/tutorials/intermediate/constrainedlayout_guide`. - - Parameters - ---------- - relative : bool - If `True`, then convert from inches to figure relative. - """ - return self._parent.get_constrained_layout_pads(relative=relative) - - def get_layout_engine(self): - return self._parent.get_layout_engine() - - @property - def axes(self): - """ - List of Axes in the SubFigure. You can access and modify the Axes - in the SubFigure through this list. - - Modifying this list has no effect. Instead, use `~.SubFigure.add_axes`, - `~.SubFigure.add_subplot` or `~.SubFigure.delaxes` to add or remove an - Axes. - - Note: The `.SubFigure.axes` property and `~.SubFigure.get_axes` method - are equivalent. - """ - return self._localaxes[:] - - get_axes = axes.fget - - def draw(self, renderer): - # docstring inherited - - # draw the figure bounding box, perhaps none for white figure - if not self.get_visible(): - return - - artists = self._get_draw_artists(renderer) - - try: - renderer.open_group('subfigure', gid=self.get_gid()) - self.patch.draw(renderer) - mimage._draw_list_compositing_images( - renderer, self, artists, self.figure.suppressComposite) - for sfig in self.subfigs: - sfig.draw(renderer) - renderer.close_group('subfigure') - - finally: - self.stale = False - - -@_docstring.interpd -class Figure(FigureBase): - """ - The top level container for all the plot elements. - - Attributes - ---------- - patch - The `.Rectangle` instance representing the figure background patch. - - suppressComposite - For multiple images, the figure will make composite images - depending on the renderer option_image_nocomposite function. If - *suppressComposite* is a boolean, this will override the renderer. - """ - # Remove the self._fig_callbacks properties on figure and subfigure - # after the deprecation expires. - callbacks = _api.deprecated( - "3.6", alternative=("the 'resize_event' signal in " - "Figure.canvas.callbacks") - )(property(lambda self: self._fig_callbacks)) - - def __str__(self): - return "Figure(%gx%g)" % tuple(self.bbox.size) - - def __repr__(self): - return "<{clsname} size {h:g}x{w:g} with {naxes} Axes>".format( - clsname=self.__class__.__name__, - h=self.bbox.size[0], w=self.bbox.size[1], - naxes=len(self.axes), - ) - - @_api.make_keyword_only("3.6", "facecolor") - def __init__(self, - figsize=None, - dpi=None, - facecolor=None, - edgecolor=None, - linewidth=0.0, - frameon=None, - subplotpars=None, # rc figure.subplot.* - tight_layout=None, # rc figure.autolayout - constrained_layout=None, # rc figure.constrained_layout.use - *, - layout=None, - **kwargs - ): - """ - Parameters - ---------- - figsize : 2-tuple of floats, default: :rc:`figure.figsize` - Figure dimension ``(width, height)`` in inches. - - dpi : float, default: :rc:`figure.dpi` - Dots per inch. - - facecolor : default: :rc:`figure.facecolor` - The figure patch facecolor. - - edgecolor : default: :rc:`figure.edgecolor` - The figure patch edge color. - - linewidth : float - The linewidth of the frame (i.e. the edge linewidth of the figure - patch). - - frameon : bool, default: :rc:`figure.frameon` - If ``False``, suppress drawing the figure background patch. - - subplotpars : `SubplotParams` - Subplot parameters. If not given, the default subplot - parameters :rc:`figure.subplot.*` are used. - - tight_layout : bool or dict, default: :rc:`figure.autolayout` - Whether to use the tight layout mechanism. See `.set_tight_layout`. - - .. admonition:: Discouraged - - The use of this parameter is discouraged. Please use - ``layout='tight'`` instead for the common case of - ``tight_layout=True`` and use `.set_tight_layout` otherwise. - - constrained_layout : bool, default: :rc:`figure.constrained_layout.use` - This is equal to ``layout='constrained'``. - - .. admonition:: Discouraged - - The use of this parameter is discouraged. Please use - ``layout='constrained'`` instead. - - layout : {'constrained', 'compressed', 'tight', 'none', `.LayoutEngine`, \ -None}, default: None - The layout mechanism for positioning of plot elements to avoid - overlapping Axes decorations (labels, ticks, etc). Note that - layout managers can have significant performance penalties. - - - 'constrained': The constrained layout solver adjusts axes sizes - to avoid overlapping axes decorations. Can handle complex plot - layouts and colorbars, and is thus recommended. - - See :doc:`/tutorials/intermediate/constrainedlayout_guide` - for examples. - - - 'compressed': uses the same algorithm as 'constrained', but - removes extra space between fixed-aspect-ratio Axes. Best for - simple grids of axes. - - - 'tight': Use the tight layout mechanism. This is a relatively - simple algorithm that adjusts the subplot parameters so that - decorations do not overlap. See `.Figure.set_tight_layout` for - further details. - - - 'none': Do not use a layout engine. - - - A `.LayoutEngine` instance. Builtin layout classes are - `.ConstrainedLayoutEngine` and `.TightLayoutEngine`, more easily - accessible by 'constrained' and 'tight'. Passing an instance - allows third parties to provide their own layout engine. - - If not given, fall back to using the parameters *tight_layout* and - *constrained_layout*, including their config defaults - :rc:`figure.autolayout` and :rc:`figure.constrained_layout.use`. - - Other Parameters - ---------------- - **kwargs : `.Figure` properties, optional - - %(Figure:kwdoc)s - """ - super().__init__(**kwargs) - self._layout_engine = None - - if layout is not None: - if (tight_layout is not None): - _api.warn_external( - "The Figure parameters 'layout' and 'tight_layout' cannot " - "be used together. Please use 'layout' only.") - if (constrained_layout is not None): - _api.warn_external( - "The Figure parameters 'layout' and 'constrained_layout' " - "cannot be used together. Please use 'layout' only.") - self.set_layout_engine(layout=layout) - elif tight_layout is not None: - if constrained_layout is not None: - _api.warn_external( - "The Figure parameters 'tight_layout' and " - "'constrained_layout' cannot be used together. Please use " - "'layout' parameter") - self.set_layout_engine(layout='tight') - if isinstance(tight_layout, dict): - self.get_layout_engine().set(**tight_layout) - elif constrained_layout is not None: - if isinstance(constrained_layout, dict): - self.set_layout_engine(layout='constrained') - self.get_layout_engine().set(**constrained_layout) - elif constrained_layout: - self.set_layout_engine(layout='constrained') - - else: - # everything is None, so use default: - self.set_layout_engine(layout=layout) - - self._fig_callbacks = cbook.CallbackRegistry(signals=["dpi_changed"]) - # Callbacks traditionally associated with the canvas (and exposed with - # a proxy property), but that actually need to be on the figure for - # pickling. - self._canvas_callbacks = cbook.CallbackRegistry( - signals=FigureCanvasBase.events) - connect = self._canvas_callbacks._connect_picklable - self._mouse_key_ids = [ - connect('key_press_event', backend_bases._key_handler), - connect('key_release_event', backend_bases._key_handler), - connect('key_release_event', backend_bases._key_handler), - connect('button_press_event', backend_bases._mouse_handler), - connect('button_release_event', backend_bases._mouse_handler), - connect('scroll_event', backend_bases._mouse_handler), - connect('motion_notify_event', backend_bases._mouse_handler), - ] - self._button_pick_id = connect('button_press_event', self.pick) - self._scroll_pick_id = connect('scroll_event', self.pick) - - if figsize is None: - figsize = mpl.rcParams['figure.figsize'] - if dpi is None: - dpi = mpl.rcParams['figure.dpi'] - if facecolor is None: - facecolor = mpl.rcParams['figure.facecolor'] - if edgecolor is None: - edgecolor = mpl.rcParams['figure.edgecolor'] - if frameon is None: - frameon = mpl.rcParams['figure.frameon'] - - if not np.isfinite(figsize).all() or (np.array(figsize) < 0).any(): - raise ValueError('figure size must be positive finite not ' - f'{figsize}') - self.bbox_inches = Bbox.from_bounds(0, 0, *figsize) - - self.dpi_scale_trans = Affine2D().scale(dpi) - # do not use property as it will trigger - self._dpi = dpi - self.bbox = TransformedBbox(self.bbox_inches, self.dpi_scale_trans) - self.figbbox = self.bbox - self.transFigure = BboxTransformTo(self.bbox) - self.transSubfigure = self.transFigure - - self.patch = Rectangle( - xy=(0, 0), width=1, height=1, visible=frameon, - facecolor=facecolor, edgecolor=edgecolor, linewidth=linewidth, - # Don't let the figure patch influence bbox calculation. - in_layout=False) - self._set_artist_props(self.patch) - self.patch.set_antialiased(False) - - FigureCanvasBase(self) # Set self.canvas. - - if subplotpars is None: - subplotpars = SubplotParams() - - self.subplotpars = subplotpars - - self._axstack = _AxesStack() # track all figure axes and current axes - self.clear() - - def pick(self, mouseevent): - if not self.canvas.widgetlock.locked(): - super().pick(mouseevent) - - def _check_layout_engines_compat(self, old, new): - """ - Helper for set_layout engine - - If the figure has used the old engine and added a colorbar then the - value of colorbar_gridspec must be the same on the new engine. - """ - if old is None or new is None: - return True - if old.colorbar_gridspec == new.colorbar_gridspec: - return True - # colorbar layout different, so check if any colorbars are on the - # figure... - for ax in self.axes: - if hasattr(ax, '_colorbar'): - # colorbars list themselves as a colorbar. - return False - return True - - def set_layout_engine(self, layout=None, **kwargs): - """ - Set the layout engine for this figure. - - Parameters - ---------- - layout: {'constrained', 'compressed', 'tight', 'none'} or \ -`LayoutEngine` or None - - - 'constrained' will use `~.ConstrainedLayoutEngine` - - 'compressed' will also use `~.ConstrainedLayoutEngine`, but with - a correction that attempts to make a good layout for fixed-aspect - ratio Axes. - - 'tight' uses `~.TightLayoutEngine` - - 'none' removes layout engine. - - If `None`, the behavior is controlled by :rc:`figure.autolayout` - (which if `True` behaves as if 'tight' was passed) and - :rc:`figure.constrained_layout.use` (which if `True` behaves as if - 'constrained' was passed). If both are `True`, - :rc:`figure.autolayout` takes priority. - - Users and libraries can define their own layout engines and pass - the instance directly as well. - - kwargs: dict - The keyword arguments are passed to the layout engine to set things - like padding and margin sizes. Only used if *layout* is a string. - - """ - if layout is None: - if mpl.rcParams['figure.autolayout']: - layout = 'tight' - elif mpl.rcParams['figure.constrained_layout.use']: - layout = 'constrained' - else: - self._layout_engine = None - return - if layout == 'tight': - new_layout_engine = TightLayoutEngine(**kwargs) - elif layout == 'constrained': - new_layout_engine = ConstrainedLayoutEngine(**kwargs) - elif layout == 'compressed': - new_layout_engine = ConstrainedLayoutEngine(compress=True, - **kwargs) - elif layout == 'none': - if self._layout_engine is not None: - new_layout_engine = PlaceHolderLayoutEngine( - self._layout_engine.adjust_compatible, - self._layout_engine.colorbar_gridspec - ) - else: - new_layout_engine = None - elif isinstance(layout, LayoutEngine): - new_layout_engine = layout - else: - raise ValueError(f"Invalid value for 'layout': {layout!r}") - - if self._check_layout_engines_compat(self._layout_engine, - new_layout_engine): - self._layout_engine = new_layout_engine - else: - raise RuntimeError('Colorbar layout of new layout engine not ' - 'compatible with old engine, and a colorbar ' - 'has been created. Engine not changed.') - - def get_layout_engine(self): - return self._layout_engine - - # TODO: I'd like to dynamically add the _repr_html_ method - # to the figure in the right context, but then IPython doesn't - # use it, for some reason. - - def _repr_html_(self): - # We can't use "isinstance" here, because then we'd end up importing - # webagg unconditionally. - if 'WebAgg' in type(self.canvas).__name__: - from matplotlib.backends import backend_webagg - return backend_webagg.ipython_inline_display(self) - - def show(self, warn=True): - """ - If using a GUI backend with pyplot, display the figure window. - - If the figure was not created using `~.pyplot.figure`, it will lack - a `~.backend_bases.FigureManagerBase`, and this method will raise an - AttributeError. - - .. warning:: - - This does not manage an GUI event loop. Consequently, the figure - may only be shown briefly or not shown at all if you or your - environment are not managing an event loop. - - Use cases for `.Figure.show` include running this from a GUI - application (where there is persistently an event loop running) or - from a shell, like IPython, that install an input hook to allow the - interactive shell to accept input while the figure is also being - shown and interactive. Some, but not all, GUI toolkits will - register an input hook on import. See :ref:`cp_integration` for - more details. - - If you're in a shell without input hook integration or executing a - python script, you should use `matplotlib.pyplot.show` with - ``block=True`` instead, which takes care of starting and running - the event loop for you. - - Parameters - ---------- - warn : bool, default: True - If ``True`` and we are not running headless (i.e. on Linux with an - unset DISPLAY), issue warning when called on a non-GUI backend. - - """ - if self.canvas.manager is None: - raise AttributeError( - "Figure.show works only for figures managed by pyplot, " - "normally created by pyplot.figure()") - try: - self.canvas.manager.show() - except NonGuiException as exc: - if warn: - _api.warn_external(str(exc)) - - @property - def axes(self): - """ - List of Axes in the Figure. You can access and modify the Axes in the - Figure through this list. - - Do not modify the list itself. Instead, use `~Figure.add_axes`, - `~.Figure.add_subplot` or `~.Figure.delaxes` to add or remove an Axes. - - Note: The `.Figure.axes` property and `~.Figure.get_axes` method are - equivalent. - """ - return self._axstack.as_list() - - get_axes = axes.fget - - def _get_renderer(self): - if hasattr(self.canvas, 'get_renderer'): - return self.canvas.get_renderer() - else: - return _get_renderer(self) - - def _get_dpi(self): - return self._dpi - - def _set_dpi(self, dpi, forward=True): - """ - Parameters - ---------- - dpi : float - - forward : bool - Passed on to `~.Figure.set_size_inches` - """ - if dpi == self._dpi: - # We don't want to cause undue events in backends. - return - self._dpi = dpi - self.dpi_scale_trans.clear().scale(dpi) - w, h = self.get_size_inches() - self.set_size_inches(w, h, forward=forward) - self._fig_callbacks.process('dpi_changed', self) - - dpi = property(_get_dpi, _set_dpi, doc="The resolution in dots per inch.") - - def get_tight_layout(self): - """Return whether `.tight_layout` is called when drawing.""" - return isinstance(self.get_layout_engine(), TightLayoutEngine) - - @_api.deprecated("3.6", alternative="set_layout_engine", - pending=True) - def set_tight_layout(self, tight): - """ - [*Discouraged*] Set whether and how `.tight_layout` is called when - drawing. - - .. admonition:: Discouraged - - This method is discouraged in favor of `~.set_layout_engine`. - - Parameters - ---------- - tight : bool or dict with keys "pad", "w_pad", "h_pad", "rect" or None - If a bool, sets whether to call `.tight_layout` upon drawing. - If ``None``, use :rc:`figure.autolayout` instead. - If a dict, pass it as kwargs to `.tight_layout`, overriding the - default paddings. - """ - if tight is None: - tight = mpl.rcParams['figure.autolayout'] - _tight = 'tight' if bool(tight) else 'none' - _tight_parameters = tight if isinstance(tight, dict) else {} - self.set_layout_engine(_tight, **_tight_parameters) - self.stale = True - - def get_constrained_layout(self): - """ - Return whether constrained layout is being used. - - See :doc:`/tutorials/intermediate/constrainedlayout_guide`. - """ - return isinstance(self.get_layout_engine(), ConstrainedLayoutEngine) - - @_api.deprecated("3.6", alternative="set_layout_engine('constrained')", - pending=True) - def set_constrained_layout(self, constrained): - """ - [*Discouraged*] Set whether ``constrained_layout`` is used upon - drawing. - - If None, :rc:`figure.constrained_layout.use` value will be used. - - When providing a dict containing the keys ``w_pad``, ``h_pad`` - the default ``constrained_layout`` paddings will be - overridden. These pads are in inches and default to 3.0/72.0. - ``w_pad`` is the width padding and ``h_pad`` is the height padding. - - .. admonition:: Discouraged - - This method is discouraged in favor of `~.set_layout_engine`. - - Parameters - ---------- - constrained : bool or dict or None - """ - if constrained is None: - constrained = mpl.rcParams['figure.constrained_layout.use'] - _constrained = 'constrained' if bool(constrained) else 'none' - _parameters = constrained if isinstance(constrained, dict) else {} - self.set_layout_engine(_constrained, **_parameters) - self.stale = True - - @_api.deprecated( - "3.6", alternative="figure.get_layout_engine().set()", - pending=True) - def set_constrained_layout_pads(self, **kwargs): - """ - Set padding for ``constrained_layout``. - - Tip: The parameters can be passed from a dictionary by using - ``fig.set_constrained_layout(**pad_dict)``. - - See :doc:`/tutorials/intermediate/constrainedlayout_guide`. - - Parameters - ---------- - w_pad : float, default: :rc:`figure.constrained_layout.w_pad` - Width padding in inches. This is the pad around Axes - and is meant to make sure there is enough room for fonts to - look good. Defaults to 3 pts = 0.04167 inches - - h_pad : float, default: :rc:`figure.constrained_layout.h_pad` - Height padding in inches. Defaults to 3 pts. - - wspace : float, default: :rc:`figure.constrained_layout.wspace` - Width padding between subplots, expressed as a fraction of the - subplot width. The total padding ends up being w_pad + wspace. - - hspace : float, default: :rc:`figure.constrained_layout.hspace` - Height padding between subplots, expressed as a fraction of the - subplot width. The total padding ends up being h_pad + hspace. - - """ - if isinstance(self.get_layout_engine(), ConstrainedLayoutEngine): - self.get_layout_engine().set(**kwargs) - - @_api.deprecated("3.6", alternative="fig.get_layout_engine().get()", - pending=True) - def get_constrained_layout_pads(self, relative=False): - """ - Get padding for ``constrained_layout``. - - Returns a list of ``w_pad, h_pad`` in inches and - ``wspace`` and ``hspace`` as fractions of the subplot. - All values are None if ``constrained_layout`` is not used. - - See :doc:`/tutorials/intermediate/constrainedlayout_guide`. - - Parameters - ---------- - relative : bool - If `True`, then convert from inches to figure relative. - """ - if not isinstance(self.get_layout_engine(), ConstrainedLayoutEngine): - return None, None, None, None - info = self.get_layout_engine().get() - w_pad = info['w_pad'] - h_pad = info['h_pad'] - wspace = info['wspace'] - hspace = info['hspace'] - - if relative and (w_pad is not None or h_pad is not None): - renderer = self._get_renderer() - dpi = renderer.dpi - w_pad = w_pad * dpi / renderer.width - h_pad = h_pad * dpi / renderer.height - - return w_pad, h_pad, wspace, hspace - - def set_canvas(self, canvas): - """ - Set the canvas that contains the figure - - Parameters - ---------- - canvas : FigureCanvas - """ - self.canvas = canvas - - @_docstring.interpd - def figimage(self, X, xo=0, yo=0, alpha=None, norm=None, cmap=None, - vmin=None, vmax=None, origin=None, resize=False, **kwargs): - """ - Add a non-resampled image to the figure. - - The image is attached to the lower or upper left corner depending on - *origin*. - - Parameters - ---------- - X - The image data. This is an array of one of the following shapes: - - - (M, N): an image with scalar data. Color-mapping is controlled - by *cmap*, *norm*, *vmin*, and *vmax*. - - (M, N, 3): an image with RGB values (0-1 float or 0-255 int). - - (M, N, 4): an image with RGBA values (0-1 float or 0-255 int), - i.e. including transparency. - - xo, yo : int - The *x*/*y* image offset in pixels. - - alpha : None or float - The alpha blending value. - - %(cmap_doc)s - - This parameter is ignored if *X* is RGB(A). - - %(norm_doc)s - - This parameter is ignored if *X* is RGB(A). - - %(vmin_vmax_doc)s - - This parameter is ignored if *X* is RGB(A). - - origin : {'upper', 'lower'}, default: :rc:`image.origin` - Indicates where the [0, 0] index of the array is in the upper left - or lower left corner of the axes. - - resize : bool - If *True*, resize the figure to match the given image size. - - Returns - ------- - `matplotlib.image.FigureImage` - - Other Parameters - ---------------- - **kwargs - Additional kwargs are `.Artist` kwargs passed on to `.FigureImage`. - - Notes - ----- - figimage complements the Axes image (`~matplotlib.axes.Axes.imshow`) - which will be resampled to fit the current Axes. If you want - a resampled image to fill the entire figure, you can define an - `~matplotlib.axes.Axes` with extent [0, 0, 1, 1]. - - Examples - -------- - :: - - f = plt.figure() - nx = int(f.get_figwidth() * f.dpi) - ny = int(f.get_figheight() * f.dpi) - data = np.random.random((ny, nx)) - f.figimage(data) - plt.show() - """ - if resize: - dpi = self.get_dpi() - figsize = [x / dpi for x in (X.shape[1], X.shape[0])] - self.set_size_inches(figsize, forward=True) - - im = mimage.FigureImage(self, cmap=cmap, norm=norm, - offsetx=xo, offsety=yo, - origin=origin, **kwargs) - im.stale_callback = _stale_figure_callback - - im.set_array(X) - im.set_alpha(alpha) - if norm is None: - im.set_clim(vmin, vmax) - self.images.append(im) - im._remove_method = self.images.remove - self.stale = True - return im - - def set_size_inches(self, w, h=None, forward=True): - """ - Set the figure size in inches. - - Call signatures:: - - fig.set_size_inches(w, h) # OR - fig.set_size_inches((w, h)) - - Parameters - ---------- - w : (float, float) or float - Width and height in inches (if height not specified as a separate - argument) or width. - h : float - Height in inches. - forward : bool, default: True - If ``True``, the canvas size is automatically updated, e.g., - you can resize the figure window from the shell. - - See Also - -------- - matplotlib.figure.Figure.get_size_inches - matplotlib.figure.Figure.set_figwidth - matplotlib.figure.Figure.set_figheight - - Notes - ----- - To transform from pixels to inches divide by `Figure.dpi`. - """ - if h is None: # Got called with a single pair as argument. - w, h = w - size = np.array([w, h]) - if not np.isfinite(size).all() or (size < 0).any(): - raise ValueError(f'figure size must be positive finite not {size}') - self.bbox_inches.p1 = size - if forward: - manager = self.canvas.manager - if manager is not None: - manager.resize(*(size * self.dpi).astype(int)) - self.stale = True - - def get_size_inches(self): - """ - Return the current size of the figure in inches. - - Returns - ------- - ndarray - The size (width, height) of the figure in inches. - - See Also - -------- - matplotlib.figure.Figure.set_size_inches - matplotlib.figure.Figure.get_figwidth - matplotlib.figure.Figure.get_figheight - - Notes - ----- - The size in pixels can be obtained by multiplying with `Figure.dpi`. - """ - return np.array(self.bbox_inches.p1) - - def get_figwidth(self): - """Return the figure width in inches.""" - return self.bbox_inches.width - - def get_figheight(self): - """Return the figure height in inches.""" - return self.bbox_inches.height - - def get_dpi(self): - """Return the resolution in dots per inch as a float.""" - return self.dpi - - def set_dpi(self, val): - """ - Set the resolution of the figure in dots-per-inch. - - Parameters - ---------- - val : float - """ - self.dpi = val - self.stale = True - - def set_figwidth(self, val, forward=True): - """ - Set the width of the figure in inches. - - Parameters - ---------- - val : float - forward : bool - See `set_size_inches`. - - See Also - -------- - matplotlib.figure.Figure.set_figheight - matplotlib.figure.Figure.set_size_inches - """ - self.set_size_inches(val, self.get_figheight(), forward=forward) - - def set_figheight(self, val, forward=True): - """ - Set the height of the figure in inches. - - Parameters - ---------- - val : float - forward : bool - See `set_size_inches`. - - See Also - -------- - matplotlib.figure.Figure.set_figwidth - matplotlib.figure.Figure.set_size_inches - """ - self.set_size_inches(self.get_figwidth(), val, forward=forward) - - def clear(self, keep_observers=False): - # docstring inherited - super().clear(keep_observers=keep_observers) - # FigureBase.clear does not clear toolbars, as - # only Figure can have toolbars - toolbar = self.canvas.toolbar - if toolbar is not None: - toolbar.update() - - @_finalize_rasterization - @allow_rasterization - def draw(self, renderer): - # docstring inherited - - # draw the figure bounding box, perhaps none for white figure - if not self.get_visible(): - return - - artists = self._get_draw_artists(renderer) - try: - renderer.open_group('figure', gid=self.get_gid()) - if self.axes and self.get_layout_engine() is not None: - try: - self.get_layout_engine().execute(self) - except ValueError: - pass - # ValueError can occur when resizing a window. - - self.patch.draw(renderer) - mimage._draw_list_compositing_images( - renderer, self, artists, self.suppressComposite) - - for sfig in self.subfigs: - sfig.draw(renderer) - - renderer.close_group('figure') - finally: - self.stale = False - - DrawEvent("draw_event", self.canvas, renderer)._process() - - def draw_without_rendering(self): - """ - Draw the figure with no output. Useful to get the final size of - artists that require a draw before their size is known (e.g. text). - """ - renderer = _get_renderer(self) - with renderer._draw_disabled(): - self.draw(renderer) - - def draw_artist(self, a): - """ - Draw `.Artist` *a* only. - """ - a.draw(self.canvas.get_renderer()) - - def __getstate__(self): - state = super().__getstate__() - - # The canvas cannot currently be pickled, but this has the benefit - # of meaning that a figure can be detached from one canvas, and - # re-attached to another. - state.pop("canvas") - - # discard any changes to the dpi due to pixel ratio changes - state["_dpi"] = state.get('_original_dpi', state['_dpi']) - - # add version information to the state - state['__mpl_version__'] = mpl.__version__ - - # check whether the figure manager (if any) is registered with pyplot - from matplotlib import _pylab_helpers - if self.canvas.manager in _pylab_helpers.Gcf.figs.values(): - state['_restore_to_pylab'] = True - return state - - def __setstate__(self, state): - version = state.pop('__mpl_version__') - restore_to_pylab = state.pop('_restore_to_pylab', False) - - if version != mpl.__version__: - _api.warn_external( - f"This figure was saved with matplotlib version {version} and " - f"is unlikely to function correctly.") - - self.__dict__ = state - - # re-initialise some of the unstored state information - FigureCanvasBase(self) # Set self.canvas. - - if restore_to_pylab: - # lazy import to avoid circularity - import matplotlib.pyplot as plt - import matplotlib._pylab_helpers as pylab_helpers - allnums = plt.get_fignums() - num = max(allnums) + 1 if allnums else 1 - backend = plt._get_backend_mod() - mgr = backend.new_figure_manager_given_figure(num, self) - pylab_helpers.Gcf._set_new_active_manager(mgr) - plt.draw_if_interactive() - - self.stale = True - - def add_axobserver(self, func): - """Whenever the Axes state change, ``func(self)`` will be called.""" - # Connect a wrapper lambda and not func itself, to avoid it being - # weakref-collected. - self._axobservers.connect("_axes_change_event", lambda arg: func(arg)) - - def savefig(self, fname, *, transparent=None, **kwargs): - """ - Save the current figure. - - Call signature:: - - savefig(fname, *, dpi='figure', format=None, metadata=None, - bbox_inches=None, pad_inches=0.1, - facecolor='auto', edgecolor='auto', - backend=None, **kwargs - ) - - The available output formats depend on the backend being used. - - Parameters - ---------- - fname : str or path-like or binary file-like - A path, or a Python file-like object, or - possibly some backend-dependent object such as - `matplotlib.backends.backend_pdf.PdfPages`. - - If *format* is set, it determines the output format, and the file - is saved as *fname*. Note that *fname* is used verbatim, and there - is no attempt to make the extension, if any, of *fname* match - *format*, and no extension is appended. - - If *format* is not set, then the format is inferred from the - extension of *fname*, if there is one. If *format* is not - set and *fname* has no extension, then the file is saved with - :rc:`savefig.format` and the appropriate extension is appended to - *fname*. - - Other Parameters - ---------------- - dpi : float or 'figure', default: :rc:`savefig.dpi` - The resolution in dots per inch. If 'figure', use the figure's - dpi value. - - format : str - The file format, e.g. 'png', 'pdf', 'svg', ... The behavior when - this is unset is documented under *fname*. - - metadata : dict, optional - Key/value pairs to store in the image metadata. The supported keys - and defaults depend on the image format and backend: - - - 'png' with Agg backend: See the parameter ``metadata`` of - `~.FigureCanvasAgg.print_png`. - - 'pdf' with pdf backend: See the parameter ``metadata`` of - `~.backend_pdf.PdfPages`. - - 'svg' with svg backend: See the parameter ``metadata`` of - `~.FigureCanvasSVG.print_svg`. - - 'eps' and 'ps' with PS backend: Only 'Creator' is supported. - - bbox_inches : str or `.Bbox`, default: :rc:`savefig.bbox` - Bounding box in inches: only the given portion of the figure is - saved. If 'tight', try to figure out the tight bbox of the figure. - - pad_inches : float, default: :rc:`savefig.pad_inches` - Amount of padding around the figure when bbox_inches is 'tight'. - - facecolor : color or 'auto', default: :rc:`savefig.facecolor` - The facecolor of the figure. If 'auto', use the current figure - facecolor. - - edgecolor : color or 'auto', default: :rc:`savefig.edgecolor` - The edgecolor of the figure. If 'auto', use the current figure - edgecolor. - - backend : str, optional - Use a non-default backend to render the file, e.g. to render a - png file with the "cairo" backend rather than the default "agg", - or a pdf file with the "pgf" backend rather than the default - "pdf". Note that the default backend is normally sufficient. See - :ref:`the-builtin-backends` for a list of valid backends for each - file format. Custom backends can be referenced as "module://...". - - orientation : {'landscape', 'portrait'} - Currently only supported by the postscript backend. - - papertype : str - One of 'letter', 'legal', 'executive', 'ledger', 'a0' through - 'a10', 'b0' through 'b10'. Only supported for postscript - output. - - transparent : bool - If *True*, the Axes patches will all be transparent; the - Figure patch will also be transparent unless *facecolor* - and/or *edgecolor* are specified via kwargs. - - If *False* has no effect and the color of the Axes and - Figure patches are unchanged (unless the Figure patch - is specified via the *facecolor* and/or *edgecolor* keyword - arguments in which case those colors are used). - - The transparency of these patches will be restored to their - original values upon exit of this function. - - This is useful, for example, for displaying - a plot on top of a colored background on a web page. - - bbox_extra_artists : list of `~matplotlib.artist.Artist`, optional - A list of extra artists that will be considered when the - tight bbox is calculated. - - pil_kwargs : dict, optional - Additional keyword arguments that are passed to - `PIL.Image.Image.save` when saving the figure. - - """ - - kwargs.setdefault('dpi', mpl.rcParams['savefig.dpi']) - if transparent is None: - transparent = mpl.rcParams['savefig.transparent'] - - with ExitStack() as stack: - if transparent: - kwargs.setdefault('facecolor', 'none') - kwargs.setdefault('edgecolor', 'none') - for ax in self.axes: - stack.enter_context( - ax.patch._cm_set(facecolor='none', edgecolor='none')) - - self.canvas.print_figure(fname, **kwargs) - - def ginput(self, n=1, timeout=30, show_clicks=True, - mouse_add=MouseButton.LEFT, - mouse_pop=MouseButton.RIGHT, - mouse_stop=MouseButton.MIDDLE): - """ - Blocking call to interact with a figure. - - Wait until the user clicks *n* times on the figure, and return the - coordinates of each click in a list. - - There are three possible interactions: - - - Add a point. - - Remove the most recently added point. - - Stop the interaction and return the points added so far. - - The actions are assigned to mouse buttons via the arguments - *mouse_add*, *mouse_pop* and *mouse_stop*. - - Parameters - ---------- - n : int, default: 1 - Number of mouse clicks to accumulate. If negative, accumulate - clicks until the input is terminated manually. - timeout : float, default: 30 seconds - Number of seconds to wait before timing out. If zero or negative - will never time out. - show_clicks : bool, default: True - If True, show a red cross at the location of each click. - mouse_add : `.MouseButton` or None, default: `.MouseButton.LEFT` - Mouse button used to add points. - mouse_pop : `.MouseButton` or None, default: `.MouseButton.RIGHT` - Mouse button used to remove the most recently added point. - mouse_stop : `.MouseButton` or None, default: `.MouseButton.MIDDLE` - Mouse button used to stop input. - - Returns - ------- - list of tuples - A list of the clicked (x, y) coordinates. - - Notes - ----- - The keyboard can also be used to select points in case your mouse - does not have one or more of the buttons. The delete and backspace - keys act like right-clicking (i.e., remove last point), the enter key - terminates input and any other key (not already used by the window - manager) selects a point. - """ - clicks = [] - marks = [] - - def handler(event): - is_button = event.name == "button_press_event" - is_key = event.name == "key_press_event" - # Quit (even if not in infinite mode; this is consistent with - # MATLAB and sometimes quite useful, but will require the user to - # test how many points were actually returned before using data). - if (is_button and event.button == mouse_stop - or is_key and event.key in ["escape", "enter"]): - self.canvas.stop_event_loop() - # Pop last click. - elif (is_button and event.button == mouse_pop - or is_key and event.key in ["backspace", "delete"]): - if clicks: - clicks.pop() - if show_clicks: - marks.pop().remove() - self.canvas.draw() - # Add new click. - elif (is_button and event.button == mouse_add - # On macOS/gtk, some keys return None. - or is_key and event.key is not None): - if event.inaxes: - clicks.append((event.xdata, event.ydata)) - _log.info("input %i: %f, %f", - len(clicks), event.xdata, event.ydata) - if show_clicks: - line = mpl.lines.Line2D([event.xdata], [event.ydata], - marker="+", color="r") - event.inaxes.add_line(line) - marks.append(line) - self.canvas.draw() - if len(clicks) == n and n > 0: - self.canvas.stop_event_loop() - - _blocking_input.blocking_input_loop( - self, ["button_press_event", "key_press_event"], timeout, handler) - - # Cleanup. - for mark in marks: - mark.remove() - self.canvas.draw() - - return clicks - - def waitforbuttonpress(self, timeout=-1): - """ - Blocking call to interact with the figure. - - Wait for user input and return True if a key was pressed, False if a - mouse button was pressed and None if no input was given within - *timeout* seconds. Negative values deactivate *timeout*. - """ - event = None - - def handler(ev): - nonlocal event - event = ev - self.canvas.stop_event_loop() - - _blocking_input.blocking_input_loop( - self, ["button_press_event", "key_press_event"], timeout, handler) - - return None if event is None else event.name == "key_press_event" - - @_api.deprecated("3.6", alternative="figure.get_layout_engine().execute()") - def execute_constrained_layout(self, renderer=None): - """ - Use ``layoutgrid`` to determine pos positions within Axes. - - See also `.set_constrained_layout_pads`. - - Returns - ------- - layoutgrid : private debugging object - """ - if not isinstance(self.get_layout_engine(), ConstrainedLayoutEngine): - return None - return self.get_layout_engine().execute(self) - - def tight_layout(self, *, pad=1.08, h_pad=None, w_pad=None, rect=None): - """ - Adjust the padding between and around subplots. - - To exclude an artist on the Axes from the bounding box calculation - that determines the subplot parameters (i.e. legend, or annotation), - set ``a.set_in_layout(False)`` for that artist. - - Parameters - ---------- - pad : float, default: 1.08 - Padding between the figure edge and the edges of subplots, - as a fraction of the font size. - h_pad, w_pad : float, default: *pad* - Padding (height/width) between edges of adjacent subplots, - as a fraction of the font size. - rect : tuple (left, bottom, right, top), default: (0, 0, 1, 1) - A rectangle in normalized figure coordinates into which the whole - subplots area (including labels) will fit. - - See Also - -------- - .Figure.set_layout_engine - .pyplot.tight_layout - """ - # note that here we do not permanently set the figures engine to - # tight_layout but rather just perform the layout in place and remove - # any previous engines. - engine = TightLayoutEngine(pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect) - try: - previous_engine = self.get_layout_engine() - self.set_layout_engine(engine) - engine.execute(self) - if previous_engine is not None and not isinstance( - previous_engine, (TightLayoutEngine, PlaceHolderLayoutEngine) - ): - _api.warn_external('The figure layout has changed to tight') - finally: - self.set_layout_engine('none') - - -def figaspect(arg): - """ - Calculate the width and height for a figure with a specified aspect ratio. - - While the height is taken from :rc:`figure.figsize`, the width is - adjusted to match the desired aspect ratio. Additionally, it is ensured - that the width is in the range [4., 16.] and the height is in the range - [2., 16.]. If necessary, the default height is adjusted to ensure this. - - Parameters - ---------- - arg : float or 2D array - If a float, this defines the aspect ratio (i.e. the ratio height / - width). - In case of an array the aspect ratio is number of rows / number of - columns, so that the array could be fitted in the figure undistorted. - - Returns - ------- - width, height : float - The figure size in inches. - - Notes - ----- - If you want to create an Axes within the figure, that still preserves the - aspect ratio, be sure to create it with equal width and height. See - examples below. - - Thanks to Fernando Perez for this function. - - Examples - -------- - Make a figure twice as tall as it is wide:: - - w, h = figaspect(2.) - fig = Figure(figsize=(w, h)) - ax = fig.add_axes([0.1, 0.1, 0.8, 0.8]) - ax.imshow(A, **kwargs) - - Make a figure with the proper aspect for an array:: - - A = rand(5, 3) - w, h = figaspect(A) - fig = Figure(figsize=(w, h)) - ax = fig.add_axes([0.1, 0.1, 0.8, 0.8]) - ax.imshow(A, **kwargs) - """ - - isarray = hasattr(arg, 'shape') and not np.isscalar(arg) - - # min/max sizes to respect when autoscaling. If John likes the idea, they - # could become rc parameters, for now they're hardwired. - figsize_min = np.array((4.0, 2.0)) # min length for width/height - figsize_max = np.array((16.0, 16.0)) # max length for width/height - - # Extract the aspect ratio of the array - if isarray: - nr, nc = arg.shape[:2] - arr_ratio = nr / nc - else: - arr_ratio = arg - - # Height of user figure defaults - fig_height = mpl.rcParams['figure.figsize'][1] - - # New size for the figure, keeping the aspect ratio of the caller - newsize = np.array((fig_height / arr_ratio, fig_height)) - - # Sanity checks, don't drop either dimension below figsize_min - newsize /= min(1.0, *(newsize / figsize_min)) - - # Avoid humongous windows as well - newsize /= max(1.0, *(newsize / figsize_max)) - - # Finally, if we have a really funky aspect ratio, break it but respect - # the min/max dimensions (we don't want figures 10 feet tall!) - newsize = np.clip(newsize, figsize_min, figsize_max) - return newsize diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/font_manager.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/font_manager.py deleted file mode 100644 index e570add..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/font_manager.py +++ /dev/null @@ -1,1553 +0,0 @@ -""" -A module for finding, managing, and using fonts across platforms. - -This module provides a single `FontManager` instance, ``fontManager``, that can -be shared across backends and platforms. The `findfont` -function returns the best TrueType (TTF) font file in the local or -system font path that matches the specified `FontProperties` -instance. The `FontManager` also handles Adobe Font Metrics -(AFM) font files for use by the PostScript backend. - -The design is based on the `W3C Cascading Style Sheet, Level 1 (CSS1) -font specification `_. -Future versions may implement the Level 2 or 2.1 specifications. -""" - -# KNOWN ISSUES -# -# - documentation -# - font variant is untested -# - font stretch is incomplete -# - font size is incomplete -# - default font algorithm needs improvement and testing -# - setWeights function needs improvement -# - 'light' is an invalid weight value, remove it. - -from base64 import b64encode -from collections import namedtuple -import copy -import dataclasses -from functools import lru_cache -from io import BytesIO -import json -import logging -from numbers import Number -import os -from pathlib import Path -import re -import subprocess -import sys -import threading - -import matplotlib as mpl -from matplotlib import _api, _afm, cbook, ft2font -from matplotlib._fontconfig_pattern import ( - parse_fontconfig_pattern, generate_fontconfig_pattern) -from matplotlib.rcsetup import _validators - -_log = logging.getLogger(__name__) - -font_scalings = { - 'xx-small': 0.579, - 'x-small': 0.694, - 'small': 0.833, - 'medium': 1.0, - 'large': 1.200, - 'x-large': 1.440, - 'xx-large': 1.728, - 'larger': 1.2, - 'smaller': 0.833, - None: 1.0, -} -stretch_dict = { - 'ultra-condensed': 100, - 'extra-condensed': 200, - 'condensed': 300, - 'semi-condensed': 400, - 'normal': 500, - 'semi-expanded': 600, - 'semi-extended': 600, - 'expanded': 700, - 'extended': 700, - 'extra-expanded': 800, - 'extra-extended': 800, - 'ultra-expanded': 900, - 'ultra-extended': 900, -} -weight_dict = { - 'ultralight': 100, - 'light': 200, - 'normal': 400, - 'regular': 400, - 'book': 400, - 'medium': 500, - 'roman': 500, - 'semibold': 600, - 'demibold': 600, - 'demi': 600, - 'bold': 700, - 'heavy': 800, - 'extra bold': 800, - 'black': 900, -} -_weight_regexes = [ - # From fontconfig's FcFreeTypeQueryFaceInternal; not the same as - # weight_dict! - ("thin", 100), - ("extralight", 200), - ("ultralight", 200), - ("demilight", 350), - ("semilight", 350), - ("light", 300), # Needs to come *after* demi/semilight! - ("book", 380), - ("regular", 400), - ("normal", 400), - ("medium", 500), - ("demibold", 600), - ("demi", 600), - ("semibold", 600), - ("extrabold", 800), - ("superbold", 800), - ("ultrabold", 800), - ("bold", 700), # Needs to come *after* extra/super/ultrabold! - ("ultrablack", 1000), - ("superblack", 1000), - ("extrablack", 1000), - (r"\bultra", 1000), - ("black", 900), # Needs to come *after* ultra/super/extrablack! - ("heavy", 900), -] -font_family_aliases = { - 'serif', - 'sans-serif', - 'sans serif', - 'cursive', - 'fantasy', - 'monospace', - 'sans', -} - -_ExceptionProxy = namedtuple('_ExceptionProxy', ['klass', 'message']) - -# OS Font paths -try: - _HOME = Path.home() -except Exception: # Exceptions thrown by home() are not specified... - _HOME = Path(os.devnull) # Just an arbitrary path with no children. -MSFolders = \ - r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders' -MSFontDirectories = [ - r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts', - r'SOFTWARE\Microsoft\Windows\CurrentVersion\Fonts'] -MSUserFontDirectories = [ - str(_HOME / 'AppData/Local/Microsoft/Windows/Fonts'), - str(_HOME / 'AppData/Roaming/Microsoft/Windows/Fonts'), -] -X11FontDirectories = [ - # an old standard installation point - "/usr/X11R6/lib/X11/fonts/TTF/", - "/usr/X11/lib/X11/fonts", - # here is the new standard location for fonts - "/usr/share/fonts/", - # documented as a good place to install new fonts - "/usr/local/share/fonts/", - # common application, not really useful - "/usr/lib/openoffice/share/fonts/truetype/", - # user fonts - str((Path(os.environ.get('XDG_DATA_HOME') or _HOME / ".local/share")) - / "fonts"), - str(_HOME / ".fonts"), -] -OSXFontDirectories = [ - "/Library/Fonts/", - "/Network/Library/Fonts/", - "/System/Library/Fonts/", - # fonts installed via MacPorts - "/opt/local/share/fonts", - # user fonts - str(_HOME / "Library/Fonts"), -] - - -def get_fontext_synonyms(fontext): - """ - Return a list of file extensions that are synonyms for - the given file extension *fileext*. - """ - return { - 'afm': ['afm'], - 'otf': ['otf', 'ttc', 'ttf'], - 'ttc': ['otf', 'ttc', 'ttf'], - 'ttf': ['otf', 'ttc', 'ttf'], - }[fontext] - - -def list_fonts(directory, extensions): - """ - Return a list of all fonts matching any of the extensions, found - recursively under the directory. - """ - extensions = ["." + ext for ext in extensions] - return [os.path.join(dirpath, filename) - # os.walk ignores access errors, unlike Path.glob. - for dirpath, _, filenames in os.walk(directory) - for filename in filenames - if Path(filename).suffix.lower() in extensions] - - -def win32FontDirectory(): - r""" - Return the user-specified font directory for Win32. This is - looked up from the registry key :: - - \\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Fonts - - If the key is not found, ``%WINDIR%\Fonts`` will be returned. - """ - import winreg - try: - with winreg.OpenKey(winreg.HKEY_CURRENT_USER, MSFolders) as user: - return winreg.QueryValueEx(user, 'Fonts')[0] - except OSError: - return os.path.join(os.environ['WINDIR'], 'Fonts') - - -def _get_win32_installed_fonts(): - """List the font paths known to the Windows registry.""" - import winreg - items = set() - # Search and resolve fonts listed in the registry. - for domain, base_dirs in [ - (winreg.HKEY_LOCAL_MACHINE, [win32FontDirectory()]), # System. - (winreg.HKEY_CURRENT_USER, MSUserFontDirectories), # User. - ]: - for base_dir in base_dirs: - for reg_path in MSFontDirectories: - try: - with winreg.OpenKey(domain, reg_path) as local: - for j in range(winreg.QueryInfoKey(local)[1]): - # value may contain the filename of the font or its - # absolute path. - key, value, tp = winreg.EnumValue(local, j) - if not isinstance(value, str): - continue - try: - # If value contains already an absolute path, - # then it is not changed further. - path = Path(base_dir, value).resolve() - except RuntimeError: - # Don't fail with invalid entries. - continue - items.add(path) - except (OSError, MemoryError): - continue - return items - - -@lru_cache() -def _get_fontconfig_fonts(): - """Cache and list the font paths known to ``fc-list``.""" - try: - if b'--format' not in subprocess.check_output(['fc-list', '--help']): - _log.warning( # fontconfig 2.7 implemented --format. - 'Matplotlib needs fontconfig>=2.7 to query system fonts.') - return [] - out = subprocess.check_output(['fc-list', '--format=%{file}\\n']) - except (OSError, subprocess.CalledProcessError): - return [] - return [Path(os.fsdecode(fname)) for fname in out.split(b'\n')] - - -def findSystemFonts(fontpaths=None, fontext='ttf'): - """ - Search for fonts in the specified font paths. If no paths are - given, will use a standard set of system paths, as well as the - list of fonts tracked by fontconfig if fontconfig is installed and - available. A list of TrueType fonts are returned by default with - AFM fonts as an option. - """ - fontfiles = set() - fontexts = get_fontext_synonyms(fontext) - - if fontpaths is None: - if sys.platform == 'win32': - installed_fonts = _get_win32_installed_fonts() - fontpaths = [] - else: - installed_fonts = _get_fontconfig_fonts() - if sys.platform == 'darwin': - fontpaths = [*X11FontDirectories, *OSXFontDirectories] - else: - fontpaths = X11FontDirectories - fontfiles.update(str(path) for path in installed_fonts - if path.suffix.lower()[1:] in fontexts) - - elif isinstance(fontpaths, str): - fontpaths = [fontpaths] - - for path in fontpaths: - fontfiles.update(map(os.path.abspath, list_fonts(path, fontexts))) - - return [fname for fname in fontfiles if os.path.exists(fname)] - - -def _fontentry_helper_repr_png(fontent): - from matplotlib.figure import Figure # Circular import. - fig = Figure() - font_path = Path(fontent.fname) if fontent.fname != '' else None - fig.text(0, 0, fontent.name, font=font_path) - with BytesIO() as buf: - fig.savefig(buf, bbox_inches='tight', transparent=True) - return buf.getvalue() - - -def _fontentry_helper_repr_html(fontent): - png_stream = _fontentry_helper_repr_png(fontent) - png_b64 = b64encode(png_stream).decode() - return f"" - - -FontEntry = dataclasses.make_dataclass( - 'FontEntry', [ - ('fname', str, dataclasses.field(default='')), - ('name', str, dataclasses.field(default='')), - ('style', str, dataclasses.field(default='normal')), - ('variant', str, dataclasses.field(default='normal')), - ('weight', str, dataclasses.field(default='normal')), - ('stretch', str, dataclasses.field(default='normal')), - ('size', str, dataclasses.field(default='medium')), - ], - namespace={ - '__doc__': """ - A class for storing Font properties. - - It is used when populating the font lookup dictionary. - """, - '_repr_html_': lambda self: _fontentry_helper_repr_html(self), - '_repr_png_': lambda self: _fontentry_helper_repr_png(self), - } -) - - -def ttfFontProperty(font): - """ - Extract information from a TrueType font file. - - Parameters - ---------- - font : `.FT2Font` - The TrueType font file from which information will be extracted. - - Returns - ------- - `FontEntry` - The extracted font properties. - - """ - name = font.family_name - - # Styles are: italic, oblique, and normal (default) - - sfnt = font.get_sfnt() - mac_key = (1, # platform: macintosh - 0, # id: roman - 0) # langid: english - ms_key = (3, # platform: microsoft - 1, # id: unicode_cs - 0x0409) # langid: english_united_states - - # These tables are actually mac_roman-encoded, but mac_roman support may be - # missing in some alternative Python implementations and we are only going - # to look for ASCII substrings, where any ASCII-compatible encoding works - # - or big-endian UTF-16, since important Microsoft fonts use that. - sfnt2 = (sfnt.get((*mac_key, 2), b'').decode('latin-1').lower() or - sfnt.get((*ms_key, 2), b'').decode('utf_16_be').lower()) - sfnt4 = (sfnt.get((*mac_key, 4), b'').decode('latin-1').lower() or - sfnt.get((*ms_key, 4), b'').decode('utf_16_be').lower()) - - if sfnt4.find('oblique') >= 0: - style = 'oblique' - elif sfnt4.find('italic') >= 0: - style = 'italic' - elif sfnt2.find('regular') >= 0: - style = 'normal' - elif font.style_flags & ft2font.ITALIC: - style = 'italic' - else: - style = 'normal' - - # Variants are: small-caps and normal (default) - - # !!!! Untested - if name.lower() in ['capitals', 'small-caps']: - variant = 'small-caps' - else: - variant = 'normal' - - # The weight-guessing algorithm is directly translated from fontconfig - # 2.13.1's FcFreeTypeQueryFaceInternal (fcfreetype.c). - wws_subfamily = 22 - typographic_subfamily = 16 - font_subfamily = 2 - styles = [ - sfnt.get((*mac_key, wws_subfamily), b'').decode('latin-1'), - sfnt.get((*mac_key, typographic_subfamily), b'').decode('latin-1'), - sfnt.get((*mac_key, font_subfamily), b'').decode('latin-1'), - sfnt.get((*ms_key, wws_subfamily), b'').decode('utf-16-be'), - sfnt.get((*ms_key, typographic_subfamily), b'').decode('utf-16-be'), - sfnt.get((*ms_key, font_subfamily), b'').decode('utf-16-be'), - ] - styles = [*filter(None, styles)] or [font.style_name] - - def get_weight(): # From fontconfig's FcFreeTypeQueryFaceInternal. - # OS/2 table weight. - os2 = font.get_sfnt_table("OS/2") - if os2 and os2["version"] != 0xffff: - return os2["usWeightClass"] - # PostScript font info weight. - try: - ps_font_info_weight = ( - font.get_ps_font_info()["weight"].replace(" ", "") or "") - except ValueError: - pass - else: - for regex, weight in _weight_regexes: - if re.fullmatch(regex, ps_font_info_weight, re.I): - return weight - # Style name weight. - for style in styles: - style = style.replace(" ", "") - for regex, weight in _weight_regexes: - if re.search(regex, style, re.I): - return weight - if font.style_flags & ft2font.BOLD: - return 700 # "bold" - return 500 # "medium", not "regular"! - - weight = int(get_weight()) - - # Stretch can be absolute and relative - # Absolute stretches are: ultra-condensed, extra-condensed, condensed, - # semi-condensed, normal, semi-expanded, expanded, extra-expanded, - # and ultra-expanded. - # Relative stretches are: wider, narrower - # Child value is: inherit - - if any(word in sfnt4 for word in ['narrow', 'condensed', 'cond']): - stretch = 'condensed' - elif 'demi cond' in sfnt4: - stretch = 'semi-condensed' - elif any(word in sfnt4 for word in ['wide', 'expanded', 'extended']): - stretch = 'expanded' - else: - stretch = 'normal' - - # Sizes can be absolute and relative. - # Absolute sizes are: xx-small, x-small, small, medium, large, x-large, - # and xx-large. - # Relative sizes are: larger, smaller - # Length value is an absolute font size, e.g., 12pt - # Percentage values are in 'em's. Most robust specification. - - if not font.scalable: - raise NotImplementedError("Non-scalable fonts are not supported") - size = 'scalable' - - return FontEntry(font.fname, name, style, variant, weight, stretch, size) - - -def afmFontProperty(fontpath, font): - """ - Extract information from an AFM font file. - - Parameters - ---------- - font : AFM - The AFM font file from which information will be extracted. - - Returns - ------- - `FontEntry` - The extracted font properties. - """ - - name = font.get_familyname() - fontname = font.get_fontname().lower() - - # Styles are: italic, oblique, and normal (default) - - if font.get_angle() != 0 or 'italic' in name.lower(): - style = 'italic' - elif 'oblique' in name.lower(): - style = 'oblique' - else: - style = 'normal' - - # Variants are: small-caps and normal (default) - - # !!!! Untested - if name.lower() in ['capitals', 'small-caps']: - variant = 'small-caps' - else: - variant = 'normal' - - weight = font.get_weight().lower() - if weight not in weight_dict: - weight = 'normal' - - # Stretch can be absolute and relative - # Absolute stretches are: ultra-condensed, extra-condensed, condensed, - # semi-condensed, normal, semi-expanded, expanded, extra-expanded, - # and ultra-expanded. - # Relative stretches are: wider, narrower - # Child value is: inherit - if 'demi cond' in fontname: - stretch = 'semi-condensed' - elif any(word in fontname for word in ['narrow', 'cond']): - stretch = 'condensed' - elif any(word in fontname for word in ['wide', 'expanded', 'extended']): - stretch = 'expanded' - else: - stretch = 'normal' - - # Sizes can be absolute and relative. - # Absolute sizes are: xx-small, x-small, small, medium, large, x-large, - # and xx-large. - # Relative sizes are: larger, smaller - # Length value is an absolute font size, e.g., 12pt - # Percentage values are in 'em's. Most robust specification. - - # All AFM fonts are apparently scalable. - - size = 'scalable' - - return FontEntry(fontpath, name, style, variant, weight, stretch, size) - - -class FontProperties: - """ - A class for storing and manipulating font properties. - - The font properties are the six properties described in the - `W3C Cascading Style Sheet, Level 1 - `_ font - specification and *math_fontfamily* for math fonts: - - - family: A list of font names in decreasing order of priority. - The items may include a generic font family name, either 'sans-serif', - 'serif', 'cursive', 'fantasy', or 'monospace'. In that case, the actual - font to be used will be looked up from the associated rcParam during the - search process in `.findfont`. Default: :rc:`font.family` - - - style: Either 'normal', 'italic' or 'oblique'. - Default: :rc:`font.style` - - - variant: Either 'normal' or 'small-caps'. - Default: :rc:`font.variant` - - - stretch: A numeric value in the range 0-1000 or one of - 'ultra-condensed', 'extra-condensed', 'condensed', - 'semi-condensed', 'normal', 'semi-expanded', 'expanded', - 'extra-expanded' or 'ultra-expanded'. Default: :rc:`font.stretch` - - - weight: A numeric value in the range 0-1000 or one of - 'ultralight', 'light', 'normal', 'regular', 'book', 'medium', - 'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy', - 'extra bold', 'black'. Default: :rc:`font.weight` - - - size: Either a relative value of 'xx-small', 'x-small', - 'small', 'medium', 'large', 'x-large', 'xx-large' or an - absolute font size, e.g., 10. Default: :rc:`font.size` - - - math_fontfamily: The family of fonts used to render math text. - Supported values are: 'dejavusans', 'dejavuserif', 'cm', - 'stix', 'stixsans' and 'custom'. Default: :rc:`mathtext.fontset` - - Alternatively, a font may be specified using the absolute path to a font - file, by using the *fname* kwarg. However, in this case, it is typically - simpler to just pass the path (as a `pathlib.Path`, not a `str`) to the - *font* kwarg of the `.Text` object. - - The preferred usage of font sizes is to use the relative values, - e.g., 'large', instead of absolute font sizes, e.g., 12. This - approach allows all text sizes to be made larger or smaller based - on the font manager's default font size. - - This class will also accept a fontconfig_ pattern_, if it is the only - argument provided. This support does not depend on fontconfig; we are - merely borrowing its pattern syntax for use here. - - .. _fontconfig: https://www.freedesktop.org/wiki/Software/fontconfig/ - .. _pattern: - https://www.freedesktop.org/software/fontconfig/fontconfig-user.html - - Note that Matplotlib's internal font manager and fontconfig use a - different algorithm to lookup fonts, so the results of the same pattern - may be different in Matplotlib than in other applications that use - fontconfig. - """ - - def __init__(self, family=None, style=None, variant=None, weight=None, - stretch=None, size=None, - fname=None, # if set, it's a hardcoded filename to use - math_fontfamily=None): - self.set_family(family) - self.set_style(style) - self.set_variant(variant) - self.set_weight(weight) - self.set_stretch(stretch) - self.set_file(fname) - self.set_size(size) - self.set_math_fontfamily(math_fontfamily) - # Treat family as a fontconfig pattern if it is the only parameter - # provided. Even in that case, call the other setters first to set - # attributes not specified by the pattern to the rcParams defaults. - if (isinstance(family, str) - and style is None and variant is None and weight is None - and stretch is None and size is None and fname is None): - self.set_fontconfig_pattern(family) - - @classmethod - def _from_any(cls, arg): - """ - Generic constructor which can build a `.FontProperties` from any of the - following: - - - a `.FontProperties`: it is passed through as is; - - `None`: a `.FontProperties` using rc values is used; - - an `os.PathLike`: it is used as path to the font file; - - a `str`: it is parsed as a fontconfig pattern; - - a `dict`: it is passed as ``**kwargs`` to `.FontProperties`. - """ - if isinstance(arg, cls): - return arg - elif arg is None: - return cls() - elif isinstance(arg, os.PathLike): - return cls(fname=arg) - elif isinstance(arg, str): - return cls(arg) - else: - return cls(**arg) - - def __hash__(self): - l = (tuple(self.get_family()), - self.get_slant(), - self.get_variant(), - self.get_weight(), - self.get_stretch(), - self.get_size(), - self.get_file(), - self.get_math_fontfamily()) - return hash(l) - - def __eq__(self, other): - return hash(self) == hash(other) - - def __str__(self): - return self.get_fontconfig_pattern() - - def get_family(self): - """ - Return a list of individual font family names or generic family names. - - The font families or generic font families (which will be resolved - from their respective rcParams when searching for a matching font) in - the order of preference. - """ - return self._family - - def get_name(self): - """ - Return the name of the font that best matches the font properties. - """ - return get_font(findfont(self)).family_name - - def get_style(self): - """ - Return the font style. Values are: 'normal', 'italic' or 'oblique'. - """ - return self._slant - - def get_variant(self): - """ - Return the font variant. Values are: 'normal' or 'small-caps'. - """ - return self._variant - - def get_weight(self): - """ - Set the font weight. Options are: A numeric value in the - range 0-1000 or one of 'light', 'normal', 'regular', 'book', - 'medium', 'roman', 'semibold', 'demibold', 'demi', 'bold', - 'heavy', 'extra bold', 'black' - """ - return self._weight - - def get_stretch(self): - """ - Return the font stretch or width. Options are: 'ultra-condensed', - 'extra-condensed', 'condensed', 'semi-condensed', 'normal', - 'semi-expanded', 'expanded', 'extra-expanded', 'ultra-expanded'. - """ - return self._stretch - - def get_size(self): - """ - Return the font size. - """ - return self._size - - def get_file(self): - """ - Return the filename of the associated font. - """ - return self._file - - def get_fontconfig_pattern(self): - """ - Get a fontconfig_ pattern_ suitable for looking up the font as - specified with fontconfig's ``fc-match`` utility. - - This support does not depend on fontconfig; we are merely borrowing its - pattern syntax for use here. - """ - return generate_fontconfig_pattern(self) - - def set_family(self, family): - """ - Change the font family. Can be either an alias (generic name - is CSS parlance), such as: 'serif', 'sans-serif', 'cursive', - 'fantasy', or 'monospace', a real font name or a list of real - font names. Real font names are not supported when - :rc:`text.usetex` is `True`. Default: :rc:`font.family` - """ - if family is None: - family = mpl.rcParams['font.family'] - if isinstance(family, str): - family = [family] - self._family = family - - def set_style(self, style): - """ - Set the font style. - - Parameters - ---------- - style : {'normal', 'italic', 'oblique'}, default: :rc:`font.style` - """ - if style is None: - style = mpl.rcParams['font.style'] - _api.check_in_list(['normal', 'italic', 'oblique'], style=style) - self._slant = style - - def set_variant(self, variant): - """ - Set the font variant. - - Parameters - ---------- - variant : {'normal', 'small-caps'}, default: :rc:`font.variant` - """ - if variant is None: - variant = mpl.rcParams['font.variant'] - _api.check_in_list(['normal', 'small-caps'], variant=variant) - self._variant = variant - - def set_weight(self, weight): - """ - Set the font weight. - - Parameters - ---------- - weight : int or {'ultralight', 'light', 'normal', 'regular', 'book', \ -'medium', 'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy', \ -'extra bold', 'black'}, default: :rc:`font.weight` - If int, must be in the range 0-1000. - """ - if weight is None: - weight = mpl.rcParams['font.weight'] - if weight in weight_dict: - self._weight = weight - return - try: - weight = int(weight) - except ValueError: - pass - else: - if 0 <= weight <= 1000: - self._weight = weight - return - raise ValueError(f"{weight=} is invalid") - - def set_stretch(self, stretch): - """ - Set the font stretch or width. - - Parameters - ---------- - stretch : int or {'ultra-condensed', 'extra-condensed', 'condensed', \ -'semi-condensed', 'normal', 'semi-expanded', 'expanded', 'extra-expanded', \ -'ultra-expanded'}, default: :rc:`font.stretch` - If int, must be in the range 0-1000. - """ - if stretch is None: - stretch = mpl.rcParams['font.stretch'] - if stretch in stretch_dict: - self._stretch = stretch - return - try: - stretch = int(stretch) - except ValueError: - pass - else: - if 0 <= stretch <= 1000: - self._stretch = stretch - return - raise ValueError(f"{stretch=} is invalid") - - def set_size(self, size): - """ - Set the font size. - - Parameters - ---------- - size : float or {'xx-small', 'x-small', 'small', 'medium', \ -'large', 'x-large', 'xx-large'}, default: :rc:`font.size` - If a float, the font size in points. The string values denote - sizes relative to the default font size. - """ - if size is None: - size = mpl.rcParams['font.size'] - try: - size = float(size) - except ValueError: - try: - scale = font_scalings[size] - except KeyError as err: - raise ValueError( - "Size is invalid. Valid font size are " - + ", ".join(map(str, font_scalings))) from err - else: - size = scale * FontManager.get_default_size() - if size < 1.0: - _log.info('Fontsize %1.2f < 1.0 pt not allowed by FreeType. ' - 'Setting fontsize = 1 pt', size) - size = 1.0 - self._size = size - - def set_file(self, file): - """ - Set the filename of the fontfile to use. In this case, all - other properties will be ignored. - """ - self._file = os.fspath(file) if file is not None else None - - def set_fontconfig_pattern(self, pattern): - """ - Set the properties by parsing a fontconfig_ *pattern*. - - This support does not depend on fontconfig; we are merely borrowing its - pattern syntax for use here. - """ - for key, val in parse_fontconfig_pattern(pattern).items(): - if type(val) is list: - getattr(self, "set_" + key)(val[0]) - else: - getattr(self, "set_" + key)(val) - - def get_math_fontfamily(self): - """ - Return the name of the font family used for math text. - - The default font is :rc:`mathtext.fontset`. - """ - return self._math_fontfamily - - def set_math_fontfamily(self, fontfamily): - """ - Set the font family for text in math mode. - - If not set explicitly, :rc:`mathtext.fontset` will be used. - - Parameters - ---------- - fontfamily : str - The name of the font family. - - Available font families are defined in the - :ref:`default matplotlibrc file `. - - See Also - -------- - .text.Text.get_math_fontfamily - """ - if fontfamily is None: - fontfamily = mpl.rcParams['mathtext.fontset'] - else: - valid_fonts = _validators['mathtext.fontset'].valid.values() - # _check_in_list() Validates the parameter math_fontfamily as - # if it were passed to rcParams['mathtext.fontset'] - _api.check_in_list(valid_fonts, math_fontfamily=fontfamily) - self._math_fontfamily = fontfamily - - def copy(self): - """Return a copy of self.""" - return copy.copy(self) - - # Aliases - set_name = set_family - get_slant = get_style - set_slant = set_style - get_size_in_points = get_size - - -class _JSONEncoder(json.JSONEncoder): - def default(self, o): - if isinstance(o, FontManager): - return dict(o.__dict__, __class__='FontManager') - elif isinstance(o, FontEntry): - d = dict(o.__dict__, __class__='FontEntry') - try: - # Cache paths of fonts shipped with Matplotlib relative to the - # Matplotlib data path, which helps in the presence of venvs. - d["fname"] = str( - Path(d["fname"]).relative_to(mpl.get_data_path())) - except ValueError: - pass - return d - else: - return super().default(o) - - -def _json_decode(o): - cls = o.pop('__class__', None) - if cls is None: - return o - elif cls == 'FontManager': - r = FontManager.__new__(FontManager) - r.__dict__.update(o) - return r - elif cls == 'FontEntry': - r = FontEntry.__new__(FontEntry) - r.__dict__.update(o) - if not os.path.isabs(r.fname): - r.fname = os.path.join(mpl.get_data_path(), r.fname) - return r - else: - raise ValueError("Don't know how to deserialize __class__=%s" % cls) - - -def json_dump(data, filename): - """ - Dump `FontManager` *data* as JSON to the file named *filename*. - - See Also - -------- - json_load - - Notes - ----- - File paths that are children of the Matplotlib data path (typically, fonts - shipped with Matplotlib) are stored relative to that data path (to remain - valid across virtualenvs). - - This function temporarily locks the output file to prevent multiple - processes from overwriting one another's output. - """ - with cbook._lock_path(filename), open(filename, 'w') as fh: - try: - json.dump(data, fh, cls=_JSONEncoder, indent=2) - except OSError as e: - _log.warning('Could not save font_manager cache {}'.format(e)) - - -def json_load(filename): - """ - Load a `FontManager` from the JSON file named *filename*. - - See Also - -------- - json_dump - """ - with open(filename) as fh: - return json.load(fh, object_hook=_json_decode) - - -class FontManager: - """ - On import, the `FontManager` singleton instance creates a list of ttf and - afm fonts and caches their `FontProperties`. The `FontManager.findfont` - method does a nearest neighbor search to find the font that most closely - matches the specification. If no good enough match is found, the default - font is returned. - """ - # Increment this version number whenever the font cache data - # format or behavior has changed and requires an existing font - # cache files to be rebuilt. - __version__ = 330 - - def __init__(self, size=None, weight='normal'): - self._version = self.__version__ - - self.__default_weight = weight - self.default_size = size - - # Create list of font paths. - paths = [cbook._get_data_path('fonts', subdir) - for subdir in ['ttf', 'afm', 'pdfcorefonts']] - _log.debug('font search path %s', paths) - - self.defaultFamily = { - 'ttf': 'DejaVu Sans', - 'afm': 'Helvetica'} - - self.afmlist = [] - self.ttflist = [] - - # Delay the warning by 5s. - timer = threading.Timer(5, lambda: _log.warning( - 'Matplotlib is building the font cache; this may take a moment.')) - timer.start() - try: - for fontext in ["afm", "ttf"]: - for path in [*findSystemFonts(paths, fontext=fontext), - *findSystemFonts(fontext=fontext)]: - try: - self.addfont(path) - except OSError as exc: - _log.info("Failed to open font file %s: %s", path, exc) - except Exception as exc: - _log.info("Failed to extract font properties from %s: " - "%s", path, exc) - finally: - timer.cancel() - - def addfont(self, path): - """ - Cache the properties of the font at *path* to make it available to the - `FontManager`. The type of font is inferred from the path suffix. - - Parameters - ---------- - path : str or path-like - """ - # Convert to string in case of a path as - # afmFontProperty and FT2Font expect this - path = os.fsdecode(path) - if Path(path).suffix.lower() == ".afm": - with open(path, "rb") as fh: - font = _afm.AFM(fh) - prop = afmFontProperty(path, font) - self.afmlist.append(prop) - else: - font = ft2font.FT2Font(path) - prop = ttfFontProperty(font) - self.ttflist.append(prop) - self._findfont_cached.cache_clear() - - @property - def defaultFont(self): - # Lazily evaluated (findfont then caches the result) to avoid including - # the venv path in the json serialization. - return {ext: self.findfont(family, fontext=ext) - for ext, family in self.defaultFamily.items()} - - def get_default_weight(self): - """ - Return the default font weight. - """ - return self.__default_weight - - @staticmethod - def get_default_size(): - """ - Return the default font size. - """ - return mpl.rcParams['font.size'] - - def set_default_weight(self, weight): - """ - Set the default font weight. The initial value is 'normal'. - """ - self.__default_weight = weight - - @staticmethod - def _expand_aliases(family): - if family in ('sans', 'sans serif'): - family = 'sans-serif' - return mpl.rcParams['font.' + family] - - # Each of the scoring functions below should return a value between - # 0.0 (perfect match) and 1.0 (terrible match) - def score_family(self, families, family2): - """ - Return a match score between the list of font families in - *families* and the font family name *family2*. - - An exact match at the head of the list returns 0.0. - - A match further down the list will return between 0 and 1. - - No match will return 1.0. - """ - if not isinstance(families, (list, tuple)): - families = [families] - elif len(families) == 0: - return 1.0 - family2 = family2.lower() - step = 1 / len(families) - for i, family1 in enumerate(families): - family1 = family1.lower() - if family1 in font_family_aliases: - options = [*map(str.lower, self._expand_aliases(family1))] - if family2 in options: - idx = options.index(family2) - return (i + (idx / len(options))) * step - elif family1 == family2: - # The score should be weighted by where in the - # list the font was found. - return i * step - return 1.0 - - def score_style(self, style1, style2): - """ - Return a match score between *style1* and *style2*. - - An exact match returns 0.0. - - A match between 'italic' and 'oblique' returns 0.1. - - No match returns 1.0. - """ - if style1 == style2: - return 0.0 - elif (style1 in ('italic', 'oblique') - and style2 in ('italic', 'oblique')): - return 0.1 - return 1.0 - - def score_variant(self, variant1, variant2): - """ - Return a match score between *variant1* and *variant2*. - - An exact match returns 0.0, otherwise 1.0. - """ - if variant1 == variant2: - return 0.0 - else: - return 1.0 - - def score_stretch(self, stretch1, stretch2): - """ - Return a match score between *stretch1* and *stretch2*. - - The result is the absolute value of the difference between the - CSS numeric values of *stretch1* and *stretch2*, normalized - between 0.0 and 1.0. - """ - try: - stretchval1 = int(stretch1) - except ValueError: - stretchval1 = stretch_dict.get(stretch1, 500) - try: - stretchval2 = int(stretch2) - except ValueError: - stretchval2 = stretch_dict.get(stretch2, 500) - return abs(stretchval1 - stretchval2) / 1000.0 - - def score_weight(self, weight1, weight2): - """ - Return a match score between *weight1* and *weight2*. - - The result is 0.0 if both weight1 and weight 2 are given as strings - and have the same value. - - Otherwise, the result is the absolute value of the difference between - the CSS numeric values of *weight1* and *weight2*, normalized between - 0.05 and 1.0. - """ - # exact match of the weight names, e.g. weight1 == weight2 == "regular" - if cbook._str_equal(weight1, weight2): - return 0.0 - w1 = weight1 if isinstance(weight1, Number) else weight_dict[weight1] - w2 = weight2 if isinstance(weight2, Number) else weight_dict[weight2] - return 0.95 * (abs(w1 - w2) / 1000) + 0.05 - - def score_size(self, size1, size2): - """ - Return a match score between *size1* and *size2*. - - If *size2* (the size specified in the font file) is 'scalable', this - function always returns 0.0, since any font size can be generated. - - Otherwise, the result is the absolute distance between *size1* and - *size2*, normalized so that the usual range of font sizes (6pt - - 72pt) will lie between 0.0 and 1.0. - """ - if size2 == 'scalable': - return 0.0 - # Size value should have already been - try: - sizeval1 = float(size1) - except ValueError: - sizeval1 = self.default_size * font_scalings[size1] - try: - sizeval2 = float(size2) - except ValueError: - return 1.0 - return abs(sizeval1 - sizeval2) / 72 - - def findfont(self, prop, fontext='ttf', directory=None, - fallback_to_default=True, rebuild_if_missing=True): - """ - Find a font that most closely matches the given font properties. - - Parameters - ---------- - prop : str or `~matplotlib.font_manager.FontProperties` - The font properties to search for. This can be either a - `.FontProperties` object or a string defining a - `fontconfig patterns`_. - - fontext : {'ttf', 'afm'}, default: 'ttf' - The extension of the font file: - - - 'ttf': TrueType and OpenType fonts (.ttf, .ttc, .otf) - - 'afm': Adobe Font Metrics (.afm) - - directory : str, optional - If given, only search this directory and its subdirectories. - - fallback_to_default : bool - If True, will fall back to the default font family (usually - "DejaVu Sans" or "Helvetica") if the first lookup hard-fails. - - rebuild_if_missing : bool - Whether to rebuild the font cache and search again if the first - match appears to point to a nonexisting font (i.e., the font cache - contains outdated entries). - - Returns - ------- - str - The filename of the best matching font. - - Notes - ----- - This performs a nearest neighbor search. Each font is given a - similarity score to the target font properties. The first font with - the highest score is returned. If no matches below a certain - threshold are found, the default font (usually DejaVu Sans) is - returned. - - The result is cached, so subsequent lookups don't have to - perform the O(n) nearest neighbor search. - - See the `W3C Cascading Style Sheet, Level 1 - `_ documentation - for a description of the font finding algorithm. - - .. _fontconfig patterns: - https://www.freedesktop.org/software/fontconfig/fontconfig-user.html - """ - # Pass the relevant rcParams (and the font manager, as `self`) to - # _findfont_cached so to prevent using a stale cache entry after an - # rcParam was changed. - rc_params = tuple(tuple(mpl.rcParams[key]) for key in [ - "font.serif", "font.sans-serif", "font.cursive", "font.fantasy", - "font.monospace"]) - ret = self._findfont_cached( - prop, fontext, directory, fallback_to_default, rebuild_if_missing, - rc_params) - if isinstance(ret, _ExceptionProxy): - raise ret.klass(ret.message) - return ret - - def get_font_names(self): - """Return the list of available fonts.""" - return list(set([font.name for font in self.ttflist])) - - def _find_fonts_by_props(self, prop, fontext='ttf', directory=None, - fallback_to_default=True, rebuild_if_missing=True): - """ - Find font families that most closely match the given properties. - - Parameters - ---------- - prop : str or `~matplotlib.font_manager.FontProperties` - The font properties to search for. This can be either a - `.FontProperties` object or a string defining a - `fontconfig patterns`_. - - fontext : {'ttf', 'afm'}, default: 'ttf' - The extension of the font file: - - - 'ttf': TrueType and OpenType fonts (.ttf, .ttc, .otf) - - 'afm': Adobe Font Metrics (.afm) - - directory : str, optional - If given, only search this directory and its subdirectories. - - fallback_to_default : bool - If True, will fall back to the default font family (usually - "DejaVu Sans" or "Helvetica") if none of the families were found. - - rebuild_if_missing : bool - Whether to rebuild the font cache and search again if the first - match appears to point to a nonexisting font (i.e., the font cache - contains outdated entries). - - Returns - ------- - list[str] - The paths of the fonts found - - Notes - ----- - This is an extension/wrapper of the original findfont API, which only - returns a single font for given font properties. Instead, this API - returns a dict containing multiple fonts and their filepaths - which closely match the given font properties. Since this internally - uses the original API, there's no change to the logic of performing the - nearest neighbor search. See `findfont` for more details. - """ - - prop = FontProperties._from_any(prop) - - fpaths = [] - for family in prop.get_family(): - cprop = prop.copy() - cprop.set_family(family) # set current prop's family - - try: - fpaths.append( - self.findfont( - cprop, fontext, directory, - fallback_to_default=False, # don't fallback to default - rebuild_if_missing=rebuild_if_missing, - ) - ) - except ValueError: - if family in font_family_aliases: - _log.warning( - "findfont: Generic family %r not found because " - "none of the following families were found: %s", - family, ", ".join(self._expand_aliases(family)) - ) - else: - _log.warning("findfont: Font family %r not found.", family) - - # only add default family if no other font was found and - # fallback_to_default is enabled - if not fpaths: - if fallback_to_default: - dfamily = self.defaultFamily[fontext] - cprop = prop.copy() - cprop.set_family(dfamily) - fpaths.append( - self.findfont( - cprop, fontext, directory, - fallback_to_default=True, - rebuild_if_missing=rebuild_if_missing, - ) - ) - else: - raise ValueError("Failed to find any font, and fallback " - "to the default font was disabled") - - return fpaths - - @lru_cache(1024) - def _findfont_cached(self, prop, fontext, directory, fallback_to_default, - rebuild_if_missing, rc_params): - - prop = FontProperties._from_any(prop) - - fname = prop.get_file() - if fname is not None: - return fname - - if fontext == 'afm': - fontlist = self.afmlist - else: - fontlist = self.ttflist - - best_score = 1e64 - best_font = None - - _log.debug('findfont: Matching %s.', prop) - for font in fontlist: - if (directory is not None and - Path(directory) not in Path(font.fname).parents): - continue - # Matching family should have top priority, so multiply it by 10. - score = (self.score_family(prop.get_family(), font.name) * 10 - + self.score_style(prop.get_style(), font.style) - + self.score_variant(prop.get_variant(), font.variant) - + self.score_weight(prop.get_weight(), font.weight) - + self.score_stretch(prop.get_stretch(), font.stretch) - + self.score_size(prop.get_size(), font.size)) - _log.debug('findfont: score(%s) = %s', font, score) - if score < best_score: - best_score = score - best_font = font - if score == 0: - break - - if best_font is None or best_score >= 10.0: - if fallback_to_default: - _log.warning( - 'findfont: Font family %s not found. Falling back to %s.', - prop.get_family(), self.defaultFamily[fontext]) - for family in map(str.lower, prop.get_family()): - if family in font_family_aliases: - _log.warning( - "findfont: Generic family %r not found because " - "none of the following families were found: %s", - family, ", ".join(self._expand_aliases(family))) - default_prop = prop.copy() - default_prop.set_family(self.defaultFamily[fontext]) - return self.findfont(default_prop, fontext, directory, - fallback_to_default=False) - else: - # This return instead of raise is intentional, as we wish to - # cache that it was not found, which will not occur if it was - # actually raised. - return _ExceptionProxy( - ValueError, - f"Failed to find font {prop}, and fallback to the default font was disabled" - ) - else: - _log.debug('findfont: Matching %s to %s (%r) with score of %f.', - prop, best_font.name, best_font.fname, best_score) - result = best_font.fname - - if not os.path.isfile(result): - if rebuild_if_missing: - _log.info( - 'findfont: Found a missing font file. Rebuilding cache.') - new_fm = _load_fontmanager(try_read_cache=False) - # Replace self by the new fontmanager, because users may have - # a reference to this specific instance. - # TODO: _load_fontmanager should really be (used by) a method - # modifying the instance in place. - vars(self).update(vars(new_fm)) - return self.findfont( - prop, fontext, directory, rebuild_if_missing=False) - else: - # This return instead of raise is intentional, as we wish to - # cache that it was not found, which will not occur if it was - # actually raised. - return _ExceptionProxy(ValueError, "No valid font could be found") - - return _cached_realpath(result) - - -@lru_cache() -def is_opentype_cff_font(filename): - """ - Return whether the given font is a Postscript Compact Font Format Font - embedded in an OpenType wrapper. Used by the PostScript and PDF backends - that can not subset these fonts. - """ - if os.path.splitext(filename)[1].lower() == '.otf': - with open(filename, 'rb') as fd: - return fd.read(4) == b"OTTO" - else: - return False - - -@lru_cache(64) -def _get_font(font_filepaths, hinting_factor, *, _kerning_factor, thread_id): - first_fontpath, *rest = font_filepaths - return ft2font.FT2Font( - first_fontpath, hinting_factor, - _fallback_list=[ - ft2font.FT2Font( - fpath, hinting_factor, - _kerning_factor=_kerning_factor - ) - for fpath in rest - ], - _kerning_factor=_kerning_factor - ) - - -# FT2Font objects cannot be used across fork()s because they reference the same -# FT_Library object. While invalidating *all* existing FT2Fonts after a fork -# would be too complicated to be worth it, the main way FT2Fonts get reused is -# via the cache of _get_font, which we can empty upon forking (not on Windows, -# which has no fork() or register_at_fork()). -if hasattr(os, "register_at_fork"): - os.register_at_fork(after_in_child=_get_font.cache_clear) - - -@lru_cache(64) -def _cached_realpath(path): - # Resolving the path avoids embedding the font twice in pdf/ps output if a - # single font is selected using two different relative paths. - return os.path.realpath(path) - - -@_api.rename_parameter('3.6', "filepath", "font_filepaths") -def get_font(font_filepaths, hinting_factor=None): - """ - Get an `.ft2font.FT2Font` object given a list of file paths. - - Parameters - ---------- - font_filepaths : Iterable[str, Path, bytes], str, Path, bytes - Relative or absolute paths to the font files to be used. - - If a single string, bytes, or `pathlib.Path`, then it will be treated - as a list with that entry only. - - If more than one filepath is passed, then the returned FT2Font object - will fall back through the fonts, in the order given, to find a needed - glyph. - - Returns - ------- - `.ft2font.FT2Font` - - """ - if isinstance(font_filepaths, (str, Path, bytes)): - paths = (_cached_realpath(font_filepaths),) - else: - paths = tuple(_cached_realpath(fname) for fname in font_filepaths) - - if hinting_factor is None: - hinting_factor = mpl.rcParams['text.hinting_factor'] - - return _get_font( - # must be a tuple to be cached - paths, - hinting_factor, - _kerning_factor=mpl.rcParams['text.kerning_factor'], - # also key on the thread ID to prevent segfaults with multi-threading - thread_id=threading.get_ident() - ) - - -def _load_fontmanager(*, try_read_cache=True): - fm_path = Path( - mpl.get_cachedir(), f"fontlist-v{FontManager.__version__}.json") - if try_read_cache: - try: - fm = json_load(fm_path) - except Exception: - pass - else: - if getattr(fm, "_version", object()) == FontManager.__version__: - _log.debug("Using fontManager instance from %s", fm_path) - return fm - fm = FontManager() - json_dump(fm, fm_path) - _log.info("generated new fontManager") - return fm - - -fontManager = _load_fontmanager() -findfont = fontManager.findfont -get_font_names = fontManager.get_font_names diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/fontconfig_pattern.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/fontconfig_pattern.py deleted file mode 100644 index 292435b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/fontconfig_pattern.py +++ /dev/null @@ -1,20 +0,0 @@ -import re -from pyparsing import ParseException - -from matplotlib._fontconfig_pattern import * # noqa: F401, F403 -from matplotlib._fontconfig_pattern import ( - parse_fontconfig_pattern, _family_punc, _value_punc) -from matplotlib import _api -_api.warn_deprecated("3.6", name=__name__, obj_type="module") - - -family_unescape = re.compile(r'\\([%s])' % _family_punc).sub -value_unescape = re.compile(r'\\([%s])' % _value_punc).sub -family_escape = re.compile(r'([%s])' % _family_punc).sub -value_escape = re.compile(r'([%s])' % _value_punc).sub - - -class FontconfigPatternParser: - ParseException = ParseException - - def parse(self, pattern): return parse_fontconfig_pattern(pattern) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/ft2font.cpython-38-x86_64-linux-gnu.so b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/ft2font.cpython-38-x86_64-linux-gnu.so deleted file mode 100755 index 6034aa6..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/ft2font.cpython-38-x86_64-linux-gnu.so and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/gridspec.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/gridspec.py deleted file mode 100644 index ee045ce..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/gridspec.py +++ /dev/null @@ -1,739 +0,0 @@ -r""" -:mod:`~matplotlib.gridspec` contains classes that help to layout multiple -`~.axes.Axes` in a grid-like pattern within a figure. - -The `GridSpec` specifies the overall grid structure. Individual cells within -the grid are referenced by `SubplotSpec`\s. - -Often, users need not access this module directly, and can use higher-level -methods like `~.pyplot.subplots`, `~.pyplot.subplot_mosaic` and -`~.Figure.subfigures`. See the tutorial -:doc:`/tutorials/intermediate/arranging_axes` for a guide. -""" - -import copy -import logging -from numbers import Integral - -import numpy as np - -import matplotlib as mpl -from matplotlib import _api, _pylab_helpers, _tight_layout -from matplotlib.transforms import Bbox - -_log = logging.getLogger(__name__) - - -class GridSpecBase: - """ - A base class of GridSpec that specifies the geometry of the grid - that a subplot will be placed. - """ - - def __init__(self, nrows, ncols, height_ratios=None, width_ratios=None): - """ - Parameters - ---------- - nrows, ncols : int - The number of rows and columns of the grid. - width_ratios : array-like of length *ncols*, optional - Defines the relative widths of the columns. Each column gets a - relative width of ``width_ratios[i] / sum(width_ratios)``. - If not given, all columns will have the same width. - height_ratios : array-like of length *nrows*, optional - Defines the relative heights of the rows. Each row gets a - relative height of ``height_ratios[i] / sum(height_ratios)``. - If not given, all rows will have the same height. - """ - if not isinstance(nrows, Integral) or nrows <= 0: - raise ValueError( - f"Number of rows must be a positive integer, not {nrows!r}") - if not isinstance(ncols, Integral) or ncols <= 0: - raise ValueError( - f"Number of columns must be a positive integer, not {ncols!r}") - self._nrows, self._ncols = nrows, ncols - self.set_height_ratios(height_ratios) - self.set_width_ratios(width_ratios) - - def __repr__(self): - height_arg = (', height_ratios=%r' % (self._row_height_ratios,) - if len(set(self._row_height_ratios)) != 1 else '') - width_arg = (', width_ratios=%r' % (self._col_width_ratios,) - if len(set(self._col_width_ratios)) != 1 else '') - return '{clsname}({nrows}, {ncols}{optionals})'.format( - clsname=self.__class__.__name__, - nrows=self._nrows, - ncols=self._ncols, - optionals=height_arg + width_arg, - ) - - nrows = property(lambda self: self._nrows, - doc="The number of rows in the grid.") - ncols = property(lambda self: self._ncols, - doc="The number of columns in the grid.") - - def get_geometry(self): - """ - Return a tuple containing the number of rows and columns in the grid. - """ - return self._nrows, self._ncols - - def get_subplot_params(self, figure=None): - # Must be implemented in subclasses - pass - - def new_subplotspec(self, loc, rowspan=1, colspan=1): - """ - Create and return a `.SubplotSpec` instance. - - Parameters - ---------- - loc : (int, int) - The position of the subplot in the grid as - ``(row_index, column_index)``. - rowspan, colspan : int, default: 1 - The number of rows and columns the subplot should span in the grid. - """ - loc1, loc2 = loc - subplotspec = self[loc1:loc1+rowspan, loc2:loc2+colspan] - return subplotspec - - def set_width_ratios(self, width_ratios): - """ - Set the relative widths of the columns. - - *width_ratios* must be of length *ncols*. Each column gets a relative - width of ``width_ratios[i] / sum(width_ratios)``. - """ - if width_ratios is None: - width_ratios = [1] * self._ncols - elif len(width_ratios) != self._ncols: - raise ValueError('Expected the given number of width ratios to ' - 'match the number of columns of the grid') - self._col_width_ratios = width_ratios - - def get_width_ratios(self): - """ - Return the width ratios. - - This is *None* if no width ratios have been set explicitly. - """ - return self._col_width_ratios - - def set_height_ratios(self, height_ratios): - """ - Set the relative heights of the rows. - - *height_ratios* must be of length *nrows*. Each row gets a relative - height of ``height_ratios[i] / sum(height_ratios)``. - """ - if height_ratios is None: - height_ratios = [1] * self._nrows - elif len(height_ratios) != self._nrows: - raise ValueError('Expected the given number of height ratios to ' - 'match the number of rows of the grid') - self._row_height_ratios = height_ratios - - def get_height_ratios(self): - """ - Return the height ratios. - - This is *None* if no height ratios have been set explicitly. - """ - return self._row_height_ratios - - @_api.delete_parameter("3.7", "raw") - def get_grid_positions(self, fig, raw=False): - """ - Return the positions of the grid cells in figure coordinates. - - Parameters - ---------- - fig : `~matplotlib.figure.Figure` - The figure the grid should be applied to. The subplot parameters - (margins and spacing between subplots) are taken from *fig*. - raw : bool, default: False - If *True*, the subplot parameters of the figure are not taken - into account. The grid spans the range [0, 1] in both directions - without margins and there is no space between grid cells. This is - used for constrained_layout. - - Returns - ------- - bottoms, tops, lefts, rights : array - The bottom, top, left, right positions of the grid cells in - figure coordinates. - """ - nrows, ncols = self.get_geometry() - - if raw: - left = 0. - right = 1. - bottom = 0. - top = 1. - wspace = 0. - hspace = 0. - else: - subplot_params = self.get_subplot_params(fig) - left = subplot_params.left - right = subplot_params.right - bottom = subplot_params.bottom - top = subplot_params.top - wspace = subplot_params.wspace - hspace = subplot_params.hspace - tot_width = right - left - tot_height = top - bottom - - # calculate accumulated heights of columns - cell_h = tot_height / (nrows + hspace*(nrows-1)) - sep_h = hspace * cell_h - norm = cell_h * nrows / sum(self._row_height_ratios) - cell_heights = [r * norm for r in self._row_height_ratios] - sep_heights = [0] + ([sep_h] * (nrows-1)) - cell_hs = np.cumsum(np.column_stack([sep_heights, cell_heights]).flat) - - # calculate accumulated widths of rows - cell_w = tot_width / (ncols + wspace*(ncols-1)) - sep_w = wspace * cell_w - norm = cell_w * ncols / sum(self._col_width_ratios) - cell_widths = [r * norm for r in self._col_width_ratios] - sep_widths = [0] + ([sep_w] * (ncols-1)) - cell_ws = np.cumsum(np.column_stack([sep_widths, cell_widths]).flat) - - fig_tops, fig_bottoms = (top - cell_hs).reshape((-1, 2)).T - fig_lefts, fig_rights = (left + cell_ws).reshape((-1, 2)).T - return fig_bottoms, fig_tops, fig_lefts, fig_rights - - @staticmethod - def _check_gridspec_exists(figure, nrows, ncols): - """ - Check if the figure already has a gridspec with these dimensions, - or create a new one - """ - for ax in figure.get_axes(): - gs = ax.get_gridspec() - if gs is not None: - if hasattr(gs, 'get_topmost_subplotspec'): - # This is needed for colorbar gridspec layouts. - # This is probably OK because this whole logic tree - # is for when the user is doing simple things with the - # add_subplot command. For complicated layouts - # like subgridspecs the proper gridspec is passed in... - gs = gs.get_topmost_subplotspec().get_gridspec() - if gs.get_geometry() == (nrows, ncols): - return gs - # else gridspec not found: - return GridSpec(nrows, ncols, figure=figure) - - def __getitem__(self, key): - """Create and return a `.SubplotSpec` instance.""" - nrows, ncols = self.get_geometry() - - def _normalize(key, size, axis): # Includes last index. - orig_key = key - if isinstance(key, slice): - start, stop, _ = key.indices(size) - if stop > start: - return start, stop - 1 - raise IndexError("GridSpec slice would result in no space " - "allocated for subplot") - else: - if key < 0: - key = key + size - if 0 <= key < size: - return key, key - elif axis is not None: - raise IndexError(f"index {orig_key} is out of bounds for " - f"axis {axis} with size {size}") - else: # flat index - raise IndexError(f"index {orig_key} is out of bounds for " - f"GridSpec with size {size}") - - if isinstance(key, tuple): - try: - k1, k2 = key - except ValueError as err: - raise ValueError("Unrecognized subplot spec") from err - num1, num2 = np.ravel_multi_index( - [_normalize(k1, nrows, 0), _normalize(k2, ncols, 1)], - (nrows, ncols)) - else: # Single key - num1, num2 = _normalize(key, nrows * ncols, None) - - return SubplotSpec(self, num1, num2) - - def subplots(self, *, sharex=False, sharey=False, squeeze=True, - subplot_kw=None): - """ - Add all subplots specified by this `GridSpec` to its parent figure. - - See `.Figure.subplots` for detailed documentation. - """ - - figure = self.figure - - if figure is None: - raise ValueError("GridSpec.subplots() only works for GridSpecs " - "created with a parent figure") - - if not isinstance(sharex, str): - sharex = "all" if sharex else "none" - if not isinstance(sharey, str): - sharey = "all" if sharey else "none" - - _api.check_in_list(["all", "row", "col", "none", False, True], - sharex=sharex, sharey=sharey) - if subplot_kw is None: - subplot_kw = {} - # don't mutate kwargs passed by user... - subplot_kw = subplot_kw.copy() - - # Create array to hold all axes. - axarr = np.empty((self._nrows, self._ncols), dtype=object) - for row in range(self._nrows): - for col in range(self._ncols): - shared_with = {"none": None, "all": axarr[0, 0], - "row": axarr[row, 0], "col": axarr[0, col]} - subplot_kw["sharex"] = shared_with[sharex] - subplot_kw["sharey"] = shared_with[sharey] - axarr[row, col] = figure.add_subplot( - self[row, col], **subplot_kw) - - # turn off redundant tick labeling - if sharex in ["col", "all"]: - for ax in axarr.flat: - ax._label_outer_xaxis(check_patch=True) - if sharey in ["row", "all"]: - for ax in axarr.flat: - ax._label_outer_yaxis(check_patch=True) - - if squeeze: - # Discarding unneeded dimensions that equal 1. If we only have one - # subplot, just return it instead of a 1-element array. - return axarr.item() if axarr.size == 1 else axarr.squeeze() - else: - # Returned axis array will be always 2-d, even if nrows=ncols=1. - return axarr - - -class GridSpec(GridSpecBase): - """ - A grid layout to place subplots within a figure. - - The location of the grid cells is determined in a similar way to - `~.figure.SubplotParams` using *left*, *right*, *top*, *bottom*, *wspace* - and *hspace*. - - Indexing a GridSpec instance returns a `.SubplotSpec`. - """ - def __init__(self, nrows, ncols, figure=None, - left=None, bottom=None, right=None, top=None, - wspace=None, hspace=None, - width_ratios=None, height_ratios=None): - """ - Parameters - ---------- - nrows, ncols : int - The number of rows and columns of the grid. - - figure : `.Figure`, optional - Only used for constrained layout to create a proper layoutgrid. - - left, right, top, bottom : float, optional - Extent of the subplots as a fraction of figure width or height. - Left cannot be larger than right, and bottom cannot be larger than - top. If not given, the values will be inferred from a figure or - rcParams at draw time. See also `GridSpec.get_subplot_params`. - - wspace : float, optional - The amount of width reserved for space between subplots, - expressed as a fraction of the average axis width. - If not given, the values will be inferred from a figure or - rcParams when necessary. See also `GridSpec.get_subplot_params`. - - hspace : float, optional - The amount of height reserved for space between subplots, - expressed as a fraction of the average axis height. - If not given, the values will be inferred from a figure or - rcParams when necessary. See also `GridSpec.get_subplot_params`. - - width_ratios : array-like of length *ncols*, optional - Defines the relative widths of the columns. Each column gets a - relative width of ``width_ratios[i] / sum(width_ratios)``. - If not given, all columns will have the same width. - - height_ratios : array-like of length *nrows*, optional - Defines the relative heights of the rows. Each row gets a - relative height of ``height_ratios[i] / sum(height_ratios)``. - If not given, all rows will have the same height. - - """ - self.left = left - self.bottom = bottom - self.right = right - self.top = top - self.wspace = wspace - self.hspace = hspace - self.figure = figure - - super().__init__(nrows, ncols, - width_ratios=width_ratios, - height_ratios=height_ratios) - - _AllowedKeys = ["left", "bottom", "right", "top", "wspace", "hspace"] - - def update(self, **kwargs): - """ - Update the subplot parameters of the grid. - - Parameters that are not explicitly given are not changed. Setting a - parameter to *None* resets it to :rc:`figure.subplot.*`. - - Parameters - ---------- - left, right, top, bottom : float or None, optional - Extent of the subplots as a fraction of figure width or height. - wspace, hspace : float, optional - Spacing between the subplots as a fraction of the average subplot - width / height. - """ - for k, v in kwargs.items(): - if k in self._AllowedKeys: - setattr(self, k, v) - else: - raise AttributeError(f"{k} is an unknown keyword") - for figmanager in _pylab_helpers.Gcf.figs.values(): - for ax in figmanager.canvas.figure.axes: - if ax.get_subplotspec() is not None: - ss = ax.get_subplotspec().get_topmost_subplotspec() - if ss.get_gridspec() == self: - ax._set_position( - ax.get_subplotspec().get_position(ax.figure)) - - def get_subplot_params(self, figure=None): - """ - Return the `.SubplotParams` for the GridSpec. - - In order of precedence the values are taken from - - - non-*None* attributes of the GridSpec - - the provided *figure* - - :rc:`figure.subplot.*` - - Note that the ``figure`` attribute of the GridSpec is always ignored. - """ - if figure is None: - kw = {k: mpl.rcParams["figure.subplot."+k] - for k in self._AllowedKeys} - subplotpars = mpl.figure.SubplotParams(**kw) - else: - subplotpars = copy.copy(figure.subplotpars) - - subplotpars.update(**{k: getattr(self, k) for k in self._AllowedKeys}) - - return subplotpars - - def locally_modified_subplot_params(self): - """ - Return a list of the names of the subplot parameters explicitly set - in the GridSpec. - - This is a subset of the attributes of `.SubplotParams`. - """ - return [k for k in self._AllowedKeys if getattr(self, k)] - - def tight_layout(self, figure, renderer=None, - pad=1.08, h_pad=None, w_pad=None, rect=None): - """ - Adjust subplot parameters to give specified padding. - - Parameters - ---------- - figure : `.Figure` - The figure. - renderer : `.RendererBase` subclass, optional - The renderer to be used. - pad : float - Padding between the figure edge and the edges of subplots, as a - fraction of the font-size. - h_pad, w_pad : float, optional - Padding (height/width) between edges of adjacent subplots. - Defaults to *pad*. - rect : tuple (left, bottom, right, top), default: None - (left, bottom, right, top) rectangle in normalized figure - coordinates that the whole subplots area (including labels) will - fit into. Default (None) is the whole figure. - """ - if renderer is None: - renderer = figure._get_renderer() - kwargs = _tight_layout.get_tight_layout_figure( - figure, figure.axes, - _tight_layout.get_subplotspec_list(figure.axes, grid_spec=self), - renderer, pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect) - if kwargs: - self.update(**kwargs) - - -class GridSpecFromSubplotSpec(GridSpecBase): - """ - GridSpec whose subplot layout parameters are inherited from the - location specified by a given SubplotSpec. - """ - def __init__(self, nrows, ncols, - subplot_spec, - wspace=None, hspace=None, - height_ratios=None, width_ratios=None): - """ - Parameters - ---------- - nrows, ncols : int - Number of rows and number of columns of the grid. - subplot_spec : SubplotSpec - Spec from which the layout parameters are inherited. - wspace, hspace : float, optional - See `GridSpec` for more details. If not specified default values - (from the figure or rcParams) are used. - height_ratios : array-like of length *nrows*, optional - See `GridSpecBase` for details. - width_ratios : array-like of length *ncols*, optional - See `GridSpecBase` for details. - """ - self._wspace = wspace - self._hspace = hspace - self._subplot_spec = subplot_spec - self.figure = self._subplot_spec.get_gridspec().figure - super().__init__(nrows, ncols, - width_ratios=width_ratios, - height_ratios=height_ratios) - - def get_subplot_params(self, figure=None): - """Return a dictionary of subplot layout parameters.""" - hspace = (self._hspace if self._hspace is not None - else figure.subplotpars.hspace if figure is not None - else mpl.rcParams["figure.subplot.hspace"]) - wspace = (self._wspace if self._wspace is not None - else figure.subplotpars.wspace if figure is not None - else mpl.rcParams["figure.subplot.wspace"]) - - figbox = self._subplot_spec.get_position(figure) - left, bottom, right, top = figbox.extents - - return mpl.figure.SubplotParams(left=left, right=right, - bottom=bottom, top=top, - wspace=wspace, hspace=hspace) - - def get_topmost_subplotspec(self): - """ - Return the topmost `.SubplotSpec` instance associated with the subplot. - """ - return self._subplot_spec.get_topmost_subplotspec() - - -class SubplotSpec: - """ - The location of a subplot in a `GridSpec`. - - .. note:: - - Likely, you will never instantiate a `SubplotSpec` yourself. Instead, - you will typically obtain one from a `GridSpec` using item-access. - - Parameters - ---------- - gridspec : `~matplotlib.gridspec.GridSpec` - The GridSpec, which the subplot is referencing. - num1, num2 : int - The subplot will occupy the *num1*-th cell of the given - *gridspec*. If *num2* is provided, the subplot will span between - *num1*-th cell and *num2*-th cell **inclusive**. - - The index starts from 0. - """ - def __init__(self, gridspec, num1, num2=None): - self._gridspec = gridspec - self.num1 = num1 - self.num2 = num2 - - def __repr__(self): - return (f"{self.get_gridspec()}[" - f"{self.rowspan.start}:{self.rowspan.stop}, " - f"{self.colspan.start}:{self.colspan.stop}]") - - @staticmethod - def _from_subplot_args(figure, args): - """ - Construct a `.SubplotSpec` from a parent `.Figure` and either - - - a `.SubplotSpec` -- returned as is; - - one or three numbers -- a MATLAB-style subplot specifier. - """ - if len(args) == 1: - arg, = args - if isinstance(arg, SubplotSpec): - return arg - elif not isinstance(arg, Integral): - raise ValueError( - f"Single argument to subplot must be a three-digit " - f"integer, not {arg!r}") - try: - rows, cols, num = map(int, str(arg)) - except ValueError: - raise ValueError( - f"Single argument to subplot must be a three-digit " - f"integer, not {arg!r}") from None - elif len(args) == 3: - rows, cols, num = args - else: - raise _api.nargs_error("subplot", takes="1 or 3", given=len(args)) - - gs = GridSpec._check_gridspec_exists(figure, rows, cols) - if gs is None: - gs = GridSpec(rows, cols, figure=figure) - if isinstance(num, tuple) and len(num) == 2: - if not all(isinstance(n, Integral) for n in num): - raise ValueError( - f"Subplot specifier tuple must contain integers, not {num}" - ) - i, j = num - else: - if not isinstance(num, Integral) or num < 1 or num > rows*cols: - raise ValueError( - f"num must be an integer with 1 <= num <= {rows*cols}, " - f"not {num!r}" - ) - i = j = num - return gs[i-1:j] - - # num2 is a property only to handle the case where it is None and someone - # mutates num1. - - @property - def num2(self): - return self.num1 if self._num2 is None else self._num2 - - @num2.setter - def num2(self, value): - self._num2 = value - - def get_gridspec(self): - return self._gridspec - - def get_geometry(self): - """ - Return the subplot geometry as tuple ``(n_rows, n_cols, start, stop)``. - - The indices *start* and *stop* define the range of the subplot within - the `GridSpec`. *stop* is inclusive (i.e. for a single cell - ``start == stop``). - """ - rows, cols = self.get_gridspec().get_geometry() - return rows, cols, self.num1, self.num2 - - @property - def rowspan(self): - """The rows spanned by this subplot, as a `range` object.""" - ncols = self.get_gridspec().ncols - return range(self.num1 // ncols, self.num2 // ncols + 1) - - @property - def colspan(self): - """The columns spanned by this subplot, as a `range` object.""" - ncols = self.get_gridspec().ncols - # We explicitly support num2 referring to a column on num1's *left*, so - # we must sort the column indices here so that the range makes sense. - c1, c2 = sorted([self.num1 % ncols, self.num2 % ncols]) - return range(c1, c2 + 1) - - def is_first_row(self): - return self.rowspan.start == 0 - - def is_last_row(self): - return self.rowspan.stop == self.get_gridspec().nrows - - def is_first_col(self): - return self.colspan.start == 0 - - def is_last_col(self): - return self.colspan.stop == self.get_gridspec().ncols - - def get_position(self, figure): - """ - Update the subplot position from ``figure.subplotpars``. - """ - gridspec = self.get_gridspec() - nrows, ncols = gridspec.get_geometry() - rows, cols = np.unravel_index([self.num1, self.num2], (nrows, ncols)) - fig_bottoms, fig_tops, fig_lefts, fig_rights = \ - gridspec.get_grid_positions(figure) - - fig_bottom = fig_bottoms[rows].min() - fig_top = fig_tops[rows].max() - fig_left = fig_lefts[cols].min() - fig_right = fig_rights[cols].max() - return Bbox.from_extents(fig_left, fig_bottom, fig_right, fig_top) - - def get_topmost_subplotspec(self): - """ - Return the topmost `SubplotSpec` instance associated with the subplot. - """ - gridspec = self.get_gridspec() - if hasattr(gridspec, "get_topmost_subplotspec"): - return gridspec.get_topmost_subplotspec() - else: - return self - - def __eq__(self, other): - """ - Two SubplotSpecs are considered equal if they refer to the same - position(s) in the same `GridSpec`. - """ - # other may not even have the attributes we are checking. - return ((self._gridspec, self.num1, self.num2) - == (getattr(other, "_gridspec", object()), - getattr(other, "num1", object()), - getattr(other, "num2", object()))) - - def __hash__(self): - return hash((self._gridspec, self.num1, self.num2)) - - def subgridspec(self, nrows, ncols, **kwargs): - """ - Create a GridSpec within this subplot. - - The created `.GridSpecFromSubplotSpec` will have this `SubplotSpec` as - a parent. - - Parameters - ---------- - nrows : int - Number of rows in grid. - - ncols : int - Number of columns in grid. - - Returns - ------- - `.GridSpecFromSubplotSpec` - - Other Parameters - ---------------- - **kwargs - All other parameters are passed to `.GridSpecFromSubplotSpec`. - - See Also - -------- - matplotlib.pyplot.subplots - - Examples - -------- - Adding three subplots in the space occupied by a single subplot:: - - fig = plt.figure() - gs0 = fig.add_gridspec(3, 1) - ax1 = fig.add_subplot(gs0[0]) - ax2 = fig.add_subplot(gs0[1]) - gssub = gs0[2].subgridspec(1, 3) - for i in range(3): - fig.add_subplot(gssub[0, i]) - """ - return GridSpecFromSubplotSpec(nrows, ncols, self, **kwargs) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/hatch.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/hatch.py deleted file mode 100644 index 396baa5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/hatch.py +++ /dev/null @@ -1,225 +0,0 @@ -"""Contains classes for generating hatch patterns.""" - -import numpy as np - -from matplotlib import _api -from matplotlib.path import Path - - -class HatchPatternBase: - """The base class for a hatch pattern.""" - pass - - -class HorizontalHatch(HatchPatternBase): - def __init__(self, hatch, density): - self.num_lines = int((hatch.count('-') + hatch.count('+')) * density) - self.num_vertices = self.num_lines * 2 - - def set_vertices_and_codes(self, vertices, codes): - steps, stepsize = np.linspace(0.0, 1.0, self.num_lines, False, - retstep=True) - steps += stepsize / 2. - vertices[0::2, 0] = 0.0 - vertices[0::2, 1] = steps - vertices[1::2, 0] = 1.0 - vertices[1::2, 1] = steps - codes[0::2] = Path.MOVETO - codes[1::2] = Path.LINETO - - -class VerticalHatch(HatchPatternBase): - def __init__(self, hatch, density): - self.num_lines = int((hatch.count('|') + hatch.count('+')) * density) - self.num_vertices = self.num_lines * 2 - - def set_vertices_and_codes(self, vertices, codes): - steps, stepsize = np.linspace(0.0, 1.0, self.num_lines, False, - retstep=True) - steps += stepsize / 2. - vertices[0::2, 0] = steps - vertices[0::2, 1] = 0.0 - vertices[1::2, 0] = steps - vertices[1::2, 1] = 1.0 - codes[0::2] = Path.MOVETO - codes[1::2] = Path.LINETO - - -class NorthEastHatch(HatchPatternBase): - def __init__(self, hatch, density): - self.num_lines = int( - (hatch.count('/') + hatch.count('x') + hatch.count('X')) * density) - if self.num_lines: - self.num_vertices = (self.num_lines + 1) * 2 - else: - self.num_vertices = 0 - - def set_vertices_and_codes(self, vertices, codes): - steps = np.linspace(-0.5, 0.5, self.num_lines + 1) - vertices[0::2, 0] = 0.0 + steps - vertices[0::2, 1] = 0.0 - steps - vertices[1::2, 0] = 1.0 + steps - vertices[1::2, 1] = 1.0 - steps - codes[0::2] = Path.MOVETO - codes[1::2] = Path.LINETO - - -class SouthEastHatch(HatchPatternBase): - def __init__(self, hatch, density): - self.num_lines = int( - (hatch.count('\\') + hatch.count('x') + hatch.count('X')) - * density) - if self.num_lines: - self.num_vertices = (self.num_lines + 1) * 2 - else: - self.num_vertices = 0 - - def set_vertices_and_codes(self, vertices, codes): - steps = np.linspace(-0.5, 0.5, self.num_lines + 1) - vertices[0::2, 0] = 0.0 + steps - vertices[0::2, 1] = 1.0 + steps - vertices[1::2, 0] = 1.0 + steps - vertices[1::2, 1] = 0.0 + steps - codes[0::2] = Path.MOVETO - codes[1::2] = Path.LINETO - - -class Shapes(HatchPatternBase): - filled = False - - def __init__(self, hatch, density): - if self.num_rows == 0: - self.num_shapes = 0 - self.num_vertices = 0 - else: - self.num_shapes = ((self.num_rows // 2 + 1) * (self.num_rows + 1) + - (self.num_rows // 2) * self.num_rows) - self.num_vertices = (self.num_shapes * - len(self.shape_vertices) * - (1 if self.filled else 2)) - - def set_vertices_and_codes(self, vertices, codes): - offset = 1.0 / self.num_rows - shape_vertices = self.shape_vertices * offset * self.size - shape_codes = self.shape_codes - if not self.filled: - shape_vertices = np.concatenate( # Forward, then backward. - [shape_vertices, shape_vertices[::-1] * 0.9]) - shape_codes = np.concatenate([shape_codes, shape_codes]) - vertices_parts = [] - codes_parts = [] - for row in range(self.num_rows + 1): - if row % 2 == 0: - cols = np.linspace(0, 1, self.num_rows + 1) - else: - cols = np.linspace(offset / 2, 1 - offset / 2, self.num_rows) - row_pos = row * offset - for col_pos in cols: - vertices_parts.append(shape_vertices + [col_pos, row_pos]) - codes_parts.append(shape_codes) - np.concatenate(vertices_parts, out=vertices) - np.concatenate(codes_parts, out=codes) - - -class Circles(Shapes): - def __init__(self, hatch, density): - path = Path.unit_circle() - self.shape_vertices = path.vertices - self.shape_codes = path.codes - super().__init__(hatch, density) - - -class SmallCircles(Circles): - size = 0.2 - - def __init__(self, hatch, density): - self.num_rows = (hatch.count('o')) * density - super().__init__(hatch, density) - - -class LargeCircles(Circles): - size = 0.35 - - def __init__(self, hatch, density): - self.num_rows = (hatch.count('O')) * density - super().__init__(hatch, density) - - -class SmallFilledCircles(Circles): - size = 0.1 - filled = True - - def __init__(self, hatch, density): - self.num_rows = (hatch.count('.')) * density - super().__init__(hatch, density) - - -class Stars(Shapes): - size = 1.0 / 3.0 - filled = True - - def __init__(self, hatch, density): - self.num_rows = (hatch.count('*')) * density - path = Path.unit_regular_star(5) - self.shape_vertices = path.vertices - self.shape_codes = np.full(len(self.shape_vertices), Path.LINETO, - dtype=Path.code_type) - self.shape_codes[0] = Path.MOVETO - super().__init__(hatch, density) - -_hatch_types = [ - HorizontalHatch, - VerticalHatch, - NorthEastHatch, - SouthEastHatch, - SmallCircles, - LargeCircles, - SmallFilledCircles, - Stars - ] - - -def _validate_hatch_pattern(hatch): - valid_hatch_patterns = set(r'-+|/\xXoO.*') - if hatch is not None: - invalids = set(hatch).difference(valid_hatch_patterns) - if invalids: - valid = ''.join(sorted(valid_hatch_patterns)) - invalids = ''.join(sorted(invalids)) - _api.warn_deprecated( - '3.4', - removal='3.8', # one release after custom hatches (#20690) - message=f'hatch must consist of a string of "{valid}" or ' - 'None, but found the following invalid values ' - f'"{invalids}". Passing invalid values is deprecated ' - 'since %(since)s and will become an error %(removal)s.' - ) - - -def get_path(hatchpattern, density=6): - """ - Given a hatch specifier, *hatchpattern*, generates Path to render - the hatch in a unit square. *density* is the number of lines per - unit square. - """ - density = int(density) - - patterns = [hatch_type(hatchpattern, density) - for hatch_type in _hatch_types] - num_vertices = sum([pattern.num_vertices for pattern in patterns]) - - if num_vertices == 0: - return Path(np.empty((0, 2))) - - vertices = np.empty((num_vertices, 2)) - codes = np.empty(num_vertices, Path.code_type) - - cursor = 0 - for pattern in patterns: - if pattern.num_vertices != 0: - vertices_chunk = vertices[cursor:cursor + pattern.num_vertices] - codes_chunk = codes[cursor:cursor + pattern.num_vertices] - pattern.set_vertices_and_codes(vertices_chunk, codes_chunk) - cursor += pattern.num_vertices - - return Path(vertices, codes) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/image.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/image.py deleted file mode 100644 index 1f5aaea..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/image.py +++ /dev/null @@ -1,1818 +0,0 @@ -""" -The image module supports basic image loading, rescaling and display -operations. -""" - -import math -import os -import logging -from pathlib import Path -import warnings - -import numpy as np -import PIL.PngImagePlugin - -import matplotlib as mpl -from matplotlib import _api, cbook, cm -# For clarity, names from _image are given explicitly in this module -from matplotlib import _image -# For user convenience, the names from _image are also imported into -# the image namespace -from matplotlib._image import * -import matplotlib.artist as martist -from matplotlib.backend_bases import FigureCanvasBase -import matplotlib.colors as mcolors -from matplotlib.transforms import ( - Affine2D, BboxBase, Bbox, BboxTransform, BboxTransformTo, - IdentityTransform, TransformedBbox) - -_log = logging.getLogger(__name__) - -# map interpolation strings to module constants -_interpd_ = { - 'antialiased': _image.NEAREST, # this will use nearest or Hanning... - 'none': _image.NEAREST, # fall back to nearest when not supported - 'nearest': _image.NEAREST, - 'bilinear': _image.BILINEAR, - 'bicubic': _image.BICUBIC, - 'spline16': _image.SPLINE16, - 'spline36': _image.SPLINE36, - 'hanning': _image.HANNING, - 'hamming': _image.HAMMING, - 'hermite': _image.HERMITE, - 'kaiser': _image.KAISER, - 'quadric': _image.QUADRIC, - 'catrom': _image.CATROM, - 'gaussian': _image.GAUSSIAN, - 'bessel': _image.BESSEL, - 'mitchell': _image.MITCHELL, - 'sinc': _image.SINC, - 'lanczos': _image.LANCZOS, - 'blackman': _image.BLACKMAN, -} - -interpolations_names = set(_interpd_) - - -def composite_images(images, renderer, magnification=1.0): - """ - Composite a number of RGBA images into one. The images are - composited in the order in which they appear in the *images* list. - - Parameters - ---------- - images : list of Images - Each must have a `make_image` method. For each image, - `can_composite` should return `True`, though this is not - enforced by this function. Each image must have a purely - affine transformation with no shear. - - renderer : `.RendererBase` - - magnification : float, default: 1 - The additional magnification to apply for the renderer in use. - - Returns - ------- - image : uint8 array (M, N, 4) - The composited RGBA image. - offset_x, offset_y : float - The (left, bottom) offset where the composited image should be placed - in the output figure. - """ - if len(images) == 0: - return np.empty((0, 0, 4), dtype=np.uint8), 0, 0 - - parts = [] - bboxes = [] - for image in images: - data, x, y, trans = image.make_image(renderer, magnification) - if data is not None: - x *= magnification - y *= magnification - parts.append((data, x, y, image._get_scalar_alpha())) - bboxes.append( - Bbox([[x, y], [x + data.shape[1], y + data.shape[0]]])) - - if len(parts) == 0: - return np.empty((0, 0, 4), dtype=np.uint8), 0, 0 - - bbox = Bbox.union(bboxes) - - output = np.zeros( - (int(bbox.height), int(bbox.width), 4), dtype=np.uint8) - - for data, x, y, alpha in parts: - trans = Affine2D().translate(x - bbox.x0, y - bbox.y0) - _image.resample(data, output, trans, _image.NEAREST, - resample=False, alpha=alpha) - - return output, bbox.x0 / magnification, bbox.y0 / magnification - - -def _draw_list_compositing_images( - renderer, parent, artists, suppress_composite=None): - """ - Draw a sorted list of artists, compositing images into a single - image where possible. - - For internal Matplotlib use only: It is here to reduce duplication - between `Figure.draw` and `Axes.draw`, but otherwise should not be - generally useful. - """ - has_images = any(isinstance(x, _ImageBase) for x in artists) - - # override the renderer default if suppressComposite is not None - not_composite = (suppress_composite if suppress_composite is not None - else renderer.option_image_nocomposite()) - - if not_composite or not has_images: - for a in artists: - a.draw(renderer) - else: - # Composite any adjacent images together - image_group = [] - mag = renderer.get_image_magnification() - - def flush_images(): - if len(image_group) == 1: - image_group[0].draw(renderer) - elif len(image_group) > 1: - data, l, b = composite_images(image_group, renderer, mag) - if data.size != 0: - gc = renderer.new_gc() - gc.set_clip_rectangle(parent.bbox) - gc.set_clip_path(parent.get_clip_path()) - renderer.draw_image(gc, round(l), round(b), data) - gc.restore() - del image_group[:] - - for a in artists: - if (isinstance(a, _ImageBase) and a.can_composite() and - a.get_clip_on() and not a.get_clip_path()): - image_group.append(a) - else: - flush_images() - a.draw(renderer) - flush_images() - - -def _resample( - image_obj, data, out_shape, transform, *, resample=None, alpha=1): - """ - Convenience wrapper around `._image.resample` to resample *data* to - *out_shape* (with a third dimension if *data* is RGBA) that takes care of - allocating the output array and fetching the relevant properties from the - Image object *image_obj*. - """ - # AGG can only handle coordinates smaller than 24-bit signed integers, - # so raise errors if the input data is larger than _image.resample can - # handle. - msg = ('Data with more than {n} cannot be accurately displayed. ' - 'Downsampling to less than {n} before displaying. ' - 'To remove this warning, manually downsample your data.') - if data.shape[1] > 2**23: - warnings.warn(msg.format(n='2**23 columns')) - step = int(np.ceil(data.shape[1] / 2**23)) - data = data[:, ::step] - transform = Affine2D().scale(step, 1) + transform - if data.shape[0] > 2**24: - warnings.warn(msg.format(n='2**24 rows')) - step = int(np.ceil(data.shape[0] / 2**24)) - data = data[::step, :] - transform = Affine2D().scale(1, step) + transform - # decide if we need to apply anti-aliasing if the data is upsampled: - # compare the number of displayed pixels to the number of - # the data pixels. - interpolation = image_obj.get_interpolation() - if interpolation == 'antialiased': - # don't antialias if upsampling by an integer number or - # if zooming in more than a factor of 3 - pos = np.array([[0, 0], [data.shape[1], data.shape[0]]]) - disp = transform.transform(pos) - dispx = np.abs(np.diff(disp[:, 0])) - dispy = np.abs(np.diff(disp[:, 1])) - if ((dispx > 3 * data.shape[1] or - dispx == data.shape[1] or - dispx == 2 * data.shape[1]) and - (dispy > 3 * data.shape[0] or - dispy == data.shape[0] or - dispy == 2 * data.shape[0])): - interpolation = 'nearest' - else: - interpolation = 'hanning' - out = np.zeros(out_shape + data.shape[2:], data.dtype) # 2D->2D, 3D->3D. - if resample is None: - resample = image_obj.get_resample() - _image.resample(data, out, transform, - _interpd_[interpolation], - resample, - alpha, - image_obj.get_filternorm(), - image_obj.get_filterrad()) - return out - - -def _rgb_to_rgba(A): - """ - Convert an RGB image to RGBA, as required by the image resample C++ - extension. - """ - rgba = np.zeros((A.shape[0], A.shape[1], 4), dtype=A.dtype) - rgba[:, :, :3] = A - if rgba.dtype == np.uint8: - rgba[:, :, 3] = 255 - else: - rgba[:, :, 3] = 1.0 - return rgba - - -class _ImageBase(martist.Artist, cm.ScalarMappable): - """ - Base class for images. - - interpolation and cmap default to their rc settings - - cmap is a colors.Colormap instance - norm is a colors.Normalize instance to map luminance to 0-1 - - extent is data axes (left, right, bottom, top) for making image plots - registered with data plots. Default is to label the pixel - centers with the zero-based row and column indices. - - Additional kwargs are matplotlib.artist properties - """ - zorder = 0 - - def __init__(self, ax, - cmap=None, - norm=None, - interpolation=None, - origin=None, - filternorm=True, - filterrad=4.0, - resample=False, - *, - interpolation_stage=None, - **kwargs - ): - martist.Artist.__init__(self) - cm.ScalarMappable.__init__(self, norm, cmap) - if origin is None: - origin = mpl.rcParams['image.origin'] - _api.check_in_list(["upper", "lower"], origin=origin) - self.origin = origin - self.set_filternorm(filternorm) - self.set_filterrad(filterrad) - self.set_interpolation(interpolation) - self.set_interpolation_stage(interpolation_stage) - self.set_resample(resample) - self.axes = ax - - self._imcache = None - - self._internal_update(kwargs) - - def __str__(self): - try: - size = self.get_size() - return f"{type(self).__name__}(size={size!r})" - except RuntimeError: - return type(self).__name__ - - def __getstate__(self): - # Save some space on the pickle by not saving the cache. - return {**super().__getstate__(), "_imcache": None} - - def get_size(self): - """Return the size of the image as tuple (numrows, numcols).""" - if self._A is None: - raise RuntimeError('You must first set the image array') - - return self._A.shape[:2] - - def set_alpha(self, alpha): - """ - Set the alpha value used for blending - not supported on all backends. - - Parameters - ---------- - alpha : float or 2D array-like or None - """ - martist.Artist._set_alpha_for_array(self, alpha) - if np.ndim(alpha) not in (0, 2): - raise TypeError('alpha must be a float, two-dimensional ' - 'array, or None') - self._imcache = None - - def _get_scalar_alpha(self): - """ - Get a scalar alpha value to be applied to the artist as a whole. - - If the alpha value is a matrix, the method returns 1.0 because pixels - have individual alpha values (see `~._ImageBase._make_image` for - details). If the alpha value is a scalar, the method returns said value - to be applied to the artist as a whole because pixels do not have - individual alpha values. - """ - return 1.0 if self._alpha is None or np.ndim(self._alpha) > 0 \ - else self._alpha - - def changed(self): - """ - Call this whenever the mappable is changed so observers can update. - """ - self._imcache = None - cm.ScalarMappable.changed(self) - - def _make_image(self, A, in_bbox, out_bbox, clip_bbox, magnification=1.0, - unsampled=False, round_to_pixel_border=True): - """ - Normalize, rescale, and colormap the image *A* from the given *in_bbox* - (in data space), to the given *out_bbox* (in pixel space) clipped to - the given *clip_bbox* (also in pixel space), and magnified by the - *magnification* factor. - - *A* may be a greyscale image (M, N) with a dtype of float32, float64, - float128, uint16 or uint8, or an (M, N, 4) RGBA image with a dtype of - float32, float64, float128, or uint8. - - If *unsampled* is True, the image will not be scaled, but an - appropriate affine transformation will be returned instead. - - If *round_to_pixel_border* is True, the output image size will be - rounded to the nearest pixel boundary. This makes the images align - correctly with the axes. It should not be used if exact scaling is - needed, such as for `FigureImage`. - - Returns - ------- - image : (M, N, 4) uint8 array - The RGBA image, resampled unless *unsampled* is True. - x, y : float - The upper left corner where the image should be drawn, in pixel - space. - trans : Affine2D - The affine transformation from image to pixel space. - """ - if A is None: - raise RuntimeError('You must first set the image ' - 'array or the image attribute') - if A.size == 0: - raise RuntimeError("_make_image must get a non-empty image. " - "Your Artist's draw method must filter before " - "this method is called.") - - clipped_bbox = Bbox.intersection(out_bbox, clip_bbox) - - if clipped_bbox is None: - return None, 0, 0, None - - out_width_base = clipped_bbox.width * magnification - out_height_base = clipped_bbox.height * magnification - - if out_width_base == 0 or out_height_base == 0: - return None, 0, 0, None - - if self.origin == 'upper': - # Flip the input image using a transform. This avoids the - # problem with flipping the array, which results in a copy - # when it is converted to contiguous in the C wrapper - t0 = Affine2D().translate(0, -A.shape[0]).scale(1, -1) - else: - t0 = IdentityTransform() - - t0 += ( - Affine2D() - .scale( - in_bbox.width / A.shape[1], - in_bbox.height / A.shape[0]) - .translate(in_bbox.x0, in_bbox.y0) - + self.get_transform()) - - t = (t0 - + (Affine2D() - .translate(-clipped_bbox.x0, -clipped_bbox.y0) - .scale(magnification))) - - # So that the image is aligned with the edge of the axes, we want to - # round up the output width to the next integer. This also means - # scaling the transform slightly to account for the extra subpixel. - if ((not unsampled) and t.is_affine and round_to_pixel_border and - (out_width_base % 1.0 != 0.0 or out_height_base % 1.0 != 0.0)): - out_width = math.ceil(out_width_base) - out_height = math.ceil(out_height_base) - extra_width = (out_width - out_width_base) / out_width_base - extra_height = (out_height - out_height_base) / out_height_base - t += Affine2D().scale(1.0 + extra_width, 1.0 + extra_height) - else: - out_width = int(out_width_base) - out_height = int(out_height_base) - out_shape = (out_height, out_width) - - if not unsampled: - if not (A.ndim == 2 or A.ndim == 3 and A.shape[-1] in (3, 4)): - raise ValueError(f"Invalid shape {A.shape} for image data") - if A.ndim == 2 and self._interpolation_stage != 'rgba': - # if we are a 2D array, then we are running through the - # norm + colormap transformation. However, in general the - # input data is not going to match the size on the screen so we - # have to resample to the correct number of pixels - - # TODO slice input array first - a_min = A.min() - a_max = A.max() - if a_min is np.ma.masked: # All masked; values don't matter. - a_min, a_max = np.int32(0), np.int32(1) - if A.dtype.kind == 'f': # Float dtype: scale to same dtype. - scaled_dtype = np.dtype( - np.float64 if A.dtype.itemsize > 4 else np.float32) - if scaled_dtype.itemsize < A.dtype.itemsize: - _api.warn_external(f"Casting input data from {A.dtype}" - f" to {scaled_dtype} for imshow.") - else: # Int dtype, likely. - # Scale to appropriately sized float: use float32 if the - # dynamic range is small, to limit the memory footprint. - da = a_max.astype(np.float64) - a_min.astype(np.float64) - scaled_dtype = np.float64 if da > 1e8 else np.float32 - - # Scale the input data to [.1, .9]. The Agg interpolators clip - # to [0, 1] internally, and we use a smaller input scale to - # identify the interpolated points that need to be flagged as - # over/under. This may introduce numeric instabilities in very - # broadly scaled data. - - # Always copy, and don't allow array subtypes. - A_scaled = np.array(A, dtype=scaled_dtype) - # Clip scaled data around norm if necessary. This is necessary - # for big numbers at the edge of float64's ability to represent - # changes. Applying a norm first would be good, but ruins the - # interpolation of over numbers. - self.norm.autoscale_None(A) - dv = np.float64(self.norm.vmax) - np.float64(self.norm.vmin) - vmid = np.float64(self.norm.vmin) + dv / 2 - fact = 1e7 if scaled_dtype == np.float64 else 1e4 - newmin = vmid - dv * fact - if newmin < a_min: - newmin = None - else: - a_min = np.float64(newmin) - newmax = vmid + dv * fact - if newmax > a_max: - newmax = None - else: - a_max = np.float64(newmax) - if newmax is not None or newmin is not None: - np.clip(A_scaled, newmin, newmax, out=A_scaled) - - # Rescale the raw data to [offset, 1-offset] so that the - # resampling code will run cleanly. Using dyadic numbers here - # could reduce the error, but would not fully eliminate it and - # breaks a number of tests (due to the slightly different - # error bouncing some pixels across a boundary in the (very - # quantized) colormapping step). - offset = .1 - frac = .8 - # Run vmin/vmax through the same rescaling as the raw data; - # otherwise, data values close or equal to the boundaries can - # end up on the wrong side due to floating point error. - vmin, vmax = self.norm.vmin, self.norm.vmax - if vmin is np.ma.masked: - vmin, vmax = a_min, a_max - vrange = np.array([vmin, vmax], dtype=scaled_dtype) - - A_scaled -= a_min - vrange -= a_min - # .item() handles a_min/a_max being ndarray subclasses. - a_min = a_min.astype(scaled_dtype).item() - a_max = a_max.astype(scaled_dtype).item() - - if a_min != a_max: - A_scaled /= ((a_max - a_min) / frac) - vrange /= ((a_max - a_min) / frac) - A_scaled += offset - vrange += offset - # resample the input data to the correct resolution and shape - A_resampled = _resample(self, A_scaled, out_shape, t) - del A_scaled # Make sure we don't use A_scaled anymore! - # Un-scale the resampled data to approximately the original - # range. Things that interpolated to outside the original range - # will still be outside, but possibly clipped in the case of - # higher order interpolation + drastically changing data. - A_resampled -= offset - vrange -= offset - if a_min != a_max: - A_resampled *= ((a_max - a_min) / frac) - vrange *= ((a_max - a_min) / frac) - A_resampled += a_min - vrange += a_min - # if using NoNorm, cast back to the original datatype - if isinstance(self.norm, mcolors.NoNorm): - A_resampled = A_resampled.astype(A.dtype) - - mask = (np.where(A.mask, np.float32(np.nan), np.float32(1)) - if A.mask.shape == A.shape # nontrivial mask - else np.ones_like(A, np.float32)) - # we always have to interpolate the mask to account for - # non-affine transformations - out_alpha = _resample(self, mask, out_shape, t, resample=True) - del mask # Make sure we don't use mask anymore! - # Agg updates out_alpha in place. If the pixel has no image - # data it will not be updated (and still be 0 as we initialized - # it), if input data that would go into that output pixel than - # it will be `nan`, if all the input data for a pixel is good - # it will be 1, and if there is _some_ good data in that output - # pixel it will be between [0, 1] (such as a rotated image). - out_mask = np.isnan(out_alpha) - out_alpha[out_mask] = 1 - # Apply the pixel-by-pixel alpha values if present - alpha = self.get_alpha() - if alpha is not None and np.ndim(alpha) > 0: - out_alpha *= _resample(self, alpha, out_shape, - t, resample=True) - # mask and run through the norm - resampled_masked = np.ma.masked_array(A_resampled, out_mask) - # we have re-set the vmin/vmax to account for small errors - # that may have moved input values in/out of range - s_vmin, s_vmax = vrange - if isinstance(self.norm, mcolors.LogNorm) and s_vmin <= 0: - # Don't give 0 or negative values to LogNorm - s_vmin = np.finfo(scaled_dtype).eps - # Block the norm from sending an update signal during the - # temporary vmin/vmax change - with self.norm.callbacks.blocked(), \ - cbook._setattr_cm(self.norm, vmin=s_vmin, vmax=s_vmax): - output = self.norm(resampled_masked) - else: - if A.ndim == 2: # _interpolation_stage == 'rgba' - self.norm.autoscale_None(A) - A = self.to_rgba(A) - if A.shape[2] == 3: - A = _rgb_to_rgba(A) - alpha = self._get_scalar_alpha() - output_alpha = _resample( # resample alpha channel - self, A[..., 3], out_shape, t, alpha=alpha) - output = _resample( # resample rgb channels - self, _rgb_to_rgba(A[..., :3]), out_shape, t, alpha=alpha) - output[..., 3] = output_alpha # recombine rgb and alpha - - # output is now either a 2D array of normed (int or float) data - # or an RGBA array of re-sampled input - output = self.to_rgba(output, bytes=True, norm=False) - # output is now a correctly sized RGBA array of uint8 - - # Apply alpha *after* if the input was greyscale without a mask - if A.ndim == 2: - alpha = self._get_scalar_alpha() - alpha_channel = output[:, :, 3] - alpha_channel[:] = ( # Assignment will cast to uint8. - alpha_channel.astype(np.float32) * out_alpha * alpha) - - else: - if self._imcache is None: - self._imcache = self.to_rgba(A, bytes=True, norm=(A.ndim == 2)) - output = self._imcache - - # Subset the input image to only the part that will be displayed. - subset = TransformedBbox(clip_bbox, t0.inverted()).frozen() - output = output[ - int(max(subset.ymin, 0)): - int(min(subset.ymax + 1, output.shape[0])), - int(max(subset.xmin, 0)): - int(min(subset.xmax + 1, output.shape[1]))] - - t = Affine2D().translate( - int(max(subset.xmin, 0)), int(max(subset.ymin, 0))) + t - - return output, clipped_bbox.x0, clipped_bbox.y0, t - - def make_image(self, renderer, magnification=1.0, unsampled=False): - """ - Normalize, rescale, and colormap this image's data for rendering using - *renderer*, with the given *magnification*. - - If *unsampled* is True, the image will not be scaled, but an - appropriate affine transformation will be returned instead. - - Returns - ------- - image : (M, N, 4) uint8 array - The RGBA image, resampled unless *unsampled* is True. - x, y : float - The upper left corner where the image should be drawn, in pixel - space. - trans : Affine2D - The affine transformation from image to pixel space. - """ - raise NotImplementedError('The make_image method must be overridden') - - def _check_unsampled_image(self): - """ - Return whether the image is better to be drawn unsampled. - - The derived class needs to override it. - """ - return False - - @martist.allow_rasterization - def draw(self, renderer, *args, **kwargs): - # if not visible, declare victory and return - if not self.get_visible(): - self.stale = False - return - # for empty images, there is nothing to draw! - if self.get_array().size == 0: - self.stale = False - return - # actually render the image. - gc = renderer.new_gc() - self._set_gc_clip(gc) - gc.set_alpha(self._get_scalar_alpha()) - gc.set_url(self.get_url()) - gc.set_gid(self.get_gid()) - if (renderer.option_scale_image() # Renderer supports transform kwarg. - and self._check_unsampled_image() - and self.get_transform().is_affine): - im, l, b, trans = self.make_image(renderer, unsampled=True) - if im is not None: - trans = Affine2D().scale(im.shape[1], im.shape[0]) + trans - renderer.draw_image(gc, l, b, im, trans) - else: - im, l, b, trans = self.make_image( - renderer, renderer.get_image_magnification()) - if im is not None: - renderer.draw_image(gc, l, b, im) - gc.restore() - self.stale = False - - def contains(self, mouseevent): - """Test whether the mouse event occurred within the image.""" - inside, info = self._default_contains(mouseevent) - if inside is not None: - return inside, info - # 1) This doesn't work for figimage; but figimage also needs a fix - # below (as the check cannot use x/ydata and extents). - # 2) As long as the check below uses x/ydata, we need to test axes - # identity instead of `self.axes.contains(event)` because even if - # axes overlap, x/ydata is only valid for event.inaxes anyways. - if self.axes is not mouseevent.inaxes: - return False, {} - # TODO: make sure this is consistent with patch and patch - # collection on nonlinear transformed coordinates. - # TODO: consider returning image coordinates (shouldn't - # be too difficult given that the image is rectilinear - trans = self.get_transform().inverted() - x, y = trans.transform([mouseevent.x, mouseevent.y]) - xmin, xmax, ymin, ymax = self.get_extent() - if xmin > xmax: - xmin, xmax = xmax, xmin - if ymin > ymax: - ymin, ymax = ymax, ymin - - if x is not None and y is not None: - inside = (xmin <= x <= xmax) and (ymin <= y <= ymax) - else: - inside = False - - return inside, {} - - def write_png(self, fname): - """Write the image to png file *fname*.""" - im = self.to_rgba(self._A[::-1] if self.origin == 'lower' else self._A, - bytes=True, norm=True) - PIL.Image.fromarray(im).save(fname, format="png") - - def set_data(self, A): - """ - Set the image array. - - Note that this function does *not* update the normalization used. - - Parameters - ---------- - A : array-like or `PIL.Image.Image` - """ - if isinstance(A, PIL.Image.Image): - A = pil_to_array(A) # Needed e.g. to apply png palette. - self._A = cbook.safe_masked_invalid(A, copy=True) - - if (self._A.dtype != np.uint8 and - not np.can_cast(self._A.dtype, float, "same_kind")): - raise TypeError("Image data of dtype {} cannot be converted to " - "float".format(self._A.dtype)) - - if self._A.ndim == 3 and self._A.shape[-1] == 1: - # If just one dimension assume scalar and apply colormap - self._A = self._A[:, :, 0] - - if not (self._A.ndim == 2 - or self._A.ndim == 3 and self._A.shape[-1] in [3, 4]): - raise TypeError("Invalid shape {} for image data" - .format(self._A.shape)) - - if self._A.ndim == 3: - # If the input data has values outside the valid range (after - # normalisation), we issue a warning and then clip X to the bounds - # - otherwise casting wraps extreme values, hiding outliers and - # making reliable interpretation impossible. - high = 255 if np.issubdtype(self._A.dtype, np.integer) else 1 - if self._A.min() < 0 or high < self._A.max(): - _log.warning( - 'Clipping input data to the valid range for imshow with ' - 'RGB data ([0..1] for floats or [0..255] for integers).' - ) - self._A = np.clip(self._A, 0, high) - # Cast unsupported integer types to uint8 - if self._A.dtype != np.uint8 and np.issubdtype(self._A.dtype, - np.integer): - self._A = self._A.astype(np.uint8) - - self._imcache = None - self.stale = True - - def set_array(self, A): - """ - Retained for backwards compatibility - use set_data instead. - - Parameters - ---------- - A : array-like - """ - # This also needs to be here to override the inherited - # cm.ScalarMappable.set_array method so it is not invoked by mistake. - self.set_data(A) - - def get_interpolation(self): - """ - Return the interpolation method the image uses when resizing. - - One of 'antialiased', 'nearest', 'bilinear', 'bicubic', 'spline16', - 'spline36', 'hanning', 'hamming', 'hermite', 'kaiser', 'quadric', - 'catrom', 'gaussian', 'bessel', 'mitchell', 'sinc', 'lanczos', - or 'none'. - """ - return self._interpolation - - def set_interpolation(self, s): - """ - Set the interpolation method the image uses when resizing. - - If None, use :rc:`image.interpolation`. If 'none', the image is - shown as is without interpolating. 'none' is only supported in - agg, ps and pdf backends and will fall back to 'nearest' mode - for other backends. - - Parameters - ---------- - s : {'antialiased', 'nearest', 'bilinear', 'bicubic', 'spline16', \ -'spline36', 'hanning', 'hamming', 'hermite', 'kaiser', 'quadric', 'catrom', \ -'gaussian', 'bessel', 'mitchell', 'sinc', 'lanczos', 'none'} or None - """ - if s is None: - s = mpl.rcParams['image.interpolation'] - s = s.lower() - _api.check_in_list(_interpd_, interpolation=s) - self._interpolation = s - self.stale = True - - def set_interpolation_stage(self, s): - """ - Set when interpolation happens during the transform to RGBA. - - Parameters - ---------- - s : {'data', 'rgba'} or None - Whether to apply up/downsampling interpolation in data or rgba - space. - """ - if s is None: - s = "data" # placeholder for maybe having rcParam - _api.check_in_list(['data', 'rgba'], s=s) - self._interpolation_stage = s - self.stale = True - - def can_composite(self): - """Return whether the image can be composited with its neighbors.""" - trans = self.get_transform() - return ( - self._interpolation != 'none' and - trans.is_affine and - trans.is_separable) - - def set_resample(self, v): - """ - Set whether image resampling is used. - - Parameters - ---------- - v : bool or None - If None, use :rc:`image.resample`. - """ - if v is None: - v = mpl.rcParams['image.resample'] - self._resample = v - self.stale = True - - def get_resample(self): - """Return whether image resampling is used.""" - return self._resample - - def set_filternorm(self, filternorm): - """ - Set whether the resize filter normalizes the weights. - - See help for `~.Axes.imshow`. - - Parameters - ---------- - filternorm : bool - """ - self._filternorm = bool(filternorm) - self.stale = True - - def get_filternorm(self): - """Return whether the resize filter normalizes the weights.""" - return self._filternorm - - def set_filterrad(self, filterrad): - """ - Set the resize filter radius only applicable to some - interpolation schemes -- see help for imshow - - Parameters - ---------- - filterrad : positive float - """ - r = float(filterrad) - if r <= 0: - raise ValueError("The filter radius must be a positive number") - self._filterrad = r - self.stale = True - - def get_filterrad(self): - """Return the filterrad setting.""" - return self._filterrad - - -class AxesImage(_ImageBase): - """ - An image attached to an Axes. - - Parameters - ---------- - ax : `~matplotlib.axes.Axes` - The axes the image will belong to. - cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap` - The Colormap instance or registered colormap name used to map scalar - data to colors. - norm : str or `~matplotlib.colors.Normalize` - Maps luminance to 0-1. - interpolation : str, default: :rc:`image.interpolation` - Supported values are 'none', 'antialiased', 'nearest', 'bilinear', - 'bicubic', 'spline16', 'spline36', 'hanning', 'hamming', 'hermite', - 'kaiser', 'quadric', 'catrom', 'gaussian', 'bessel', 'mitchell', - 'sinc', 'lanczos', 'blackman'. - interpolation_stage : {'data', 'rgba'}, default: 'data' - If 'data', interpolation - is carried out on the data provided by the user. If 'rgba', the - interpolation is carried out after the colormapping has been - applied (visual interpolation). - origin : {'upper', 'lower'}, default: :rc:`image.origin` - Place the [0, 0] index of the array in the upper left or lower left - corner of the axes. The convention 'upper' is typically used for - matrices and images. - extent : tuple, optional - The data axes (left, right, bottom, top) for making image plots - registered with data plots. Default is to label the pixel - centers with the zero-based row and column indices. - filternorm : bool, default: True - A parameter for the antigrain image resize filter - (see the antigrain documentation). - If filternorm is set, the filter normalizes integer values and corrects - the rounding errors. It doesn't do anything with the source floating - point values, it corrects only integers according to the rule of 1.0 - which means that any sum of pixel weights must be equal to 1.0. So, - the filter function must produce a graph of the proper shape. - filterrad : float > 0, default: 4 - The filter radius for filters that have a radius parameter, i.e. when - interpolation is one of: 'sinc', 'lanczos' or 'blackman'. - resample : bool, default: False - When True, use a full resampling method. When False, only resample when - the output image is larger than the input image. - **kwargs : `~matplotlib.artist.Artist` properties - """ - - @_api.make_keyword_only("3.6", name="cmap") - def __init__(self, ax, - cmap=None, - norm=None, - interpolation=None, - origin=None, - extent=None, - filternorm=True, - filterrad=4.0, - resample=False, - *, - interpolation_stage=None, - **kwargs - ): - - self._extent = extent - - super().__init__( - ax, - cmap=cmap, - norm=norm, - interpolation=interpolation, - origin=origin, - filternorm=filternorm, - filterrad=filterrad, - resample=resample, - interpolation_stage=interpolation_stage, - **kwargs - ) - - def get_window_extent(self, renderer=None): - x0, x1, y0, y1 = self._extent - bbox = Bbox.from_extents([x0, y0, x1, y1]) - return bbox.transformed(self.get_transform()) - - def make_image(self, renderer, magnification=1.0, unsampled=False): - # docstring inherited - trans = self.get_transform() - # image is created in the canvas coordinate. - x1, x2, y1, y2 = self.get_extent() - bbox = Bbox(np.array([[x1, y1], [x2, y2]])) - transformed_bbox = TransformedBbox(bbox, trans) - clip = ((self.get_clip_box() or self.axes.bbox) if self.get_clip_on() - else self.figure.bbox) - return self._make_image(self._A, bbox, transformed_bbox, clip, - magnification, unsampled=unsampled) - - def _check_unsampled_image(self): - """Return whether the image would be better drawn unsampled.""" - return self.get_interpolation() == "none" - - def set_extent(self, extent, **kwargs): - """ - Set the image extent. - - Parameters - ---------- - extent : 4-tuple of float - The position and size of the image as tuple - ``(left, right, bottom, top)`` in data coordinates. - **kwargs - Other parameters from which unit info (i.e., the *xunits*, - *yunits*, *zunits* (for 3D axes), *runits* and *thetaunits* (for - polar axes) entries are applied, if present. - - Notes - ----- - This updates ``ax.dataLim``, and, if autoscaling, sets ``ax.viewLim`` - to tightly fit the image, regardless of ``dataLim``. Autoscaling - state is not changed, so following this with ``ax.autoscale_view()`` - will redo the autoscaling in accord with ``dataLim``. - """ - (xmin, xmax), (ymin, ymax) = self.axes._process_unit_info( - [("x", [extent[0], extent[1]]), - ("y", [extent[2], extent[3]])], - kwargs) - if kwargs: - raise _api.kwarg_error("set_extent", kwargs) - xmin = self.axes._validate_converted_limits( - xmin, self.convert_xunits) - xmax = self.axes._validate_converted_limits( - xmax, self.convert_xunits) - ymin = self.axes._validate_converted_limits( - ymin, self.convert_yunits) - ymax = self.axes._validate_converted_limits( - ymax, self.convert_yunits) - extent = [xmin, xmax, ymin, ymax] - - self._extent = extent - corners = (xmin, ymin), (xmax, ymax) - self.axes.update_datalim(corners) - self.sticky_edges.x[:] = [xmin, xmax] - self.sticky_edges.y[:] = [ymin, ymax] - if self.axes.get_autoscalex_on(): - self.axes.set_xlim((xmin, xmax), auto=None) - if self.axes.get_autoscaley_on(): - self.axes.set_ylim((ymin, ymax), auto=None) - self.stale = True - - def get_extent(self): - """Return the image extent as tuple (left, right, bottom, top).""" - if self._extent is not None: - return self._extent - else: - sz = self.get_size() - numrows, numcols = sz - if self.origin == 'upper': - return (-0.5, numcols-0.5, numrows-0.5, -0.5) - else: - return (-0.5, numcols-0.5, -0.5, numrows-0.5) - - def get_cursor_data(self, event): - """ - Return the image value at the event position or *None* if the event is - outside the image. - - See Also - -------- - matplotlib.artist.Artist.get_cursor_data - """ - xmin, xmax, ymin, ymax = self.get_extent() - if self.origin == 'upper': - ymin, ymax = ymax, ymin - arr = self.get_array() - data_extent = Bbox([[xmin, ymin], [xmax, ymax]]) - array_extent = Bbox([[0, 0], [arr.shape[1], arr.shape[0]]]) - trans = self.get_transform().inverted() - trans += BboxTransform(boxin=data_extent, boxout=array_extent) - point = trans.transform([event.x, event.y]) - if any(np.isnan(point)): - return None - j, i = point.astype(int) - # Clip the coordinates at array bounds - if not (0 <= i < arr.shape[0]) or not (0 <= j < arr.shape[1]): - return None - else: - return arr[i, j] - - -class NonUniformImage(AxesImage): - mouseover = False # This class still needs its own get_cursor_data impl. - - def __init__(self, ax, *, interpolation='nearest', **kwargs): - """ - Parameters - ---------- - ax : `~matplotlib.axes.Axes` - The axes the image will belong to. - interpolation : {'nearest', 'bilinear'}, default: 'nearest' - The interpolation scheme used in the resampling. - **kwargs - All other keyword arguments are identical to those of `.AxesImage`. - """ - super().__init__(ax, **kwargs) - self.set_interpolation(interpolation) - - def _check_unsampled_image(self): - """Return False. Do not use unsampled image.""" - return False - - def make_image(self, renderer, magnification=1.0, unsampled=False): - # docstring inherited - if self._A is None: - raise RuntimeError('You must first set the image array') - if unsampled: - raise ValueError('unsampled not supported on NonUniformImage') - A = self._A - if A.ndim == 2: - if A.dtype != np.uint8: - A = self.to_rgba(A, bytes=True) - else: - A = np.repeat(A[:, :, np.newaxis], 4, 2) - A[:, :, 3] = 255 - else: - if A.dtype != np.uint8: - A = (255*A).astype(np.uint8) - if A.shape[2] == 3: - B = np.zeros(tuple([*A.shape[0:2], 4]), np.uint8) - B[:, :, 0:3] = A - B[:, :, 3] = 255 - A = B - vl = self.axes.viewLim - l, b, r, t = self.axes.bbox.extents - width = int(((round(r) + 0.5) - (round(l) - 0.5)) * magnification) - height = int(((round(t) + 0.5) - (round(b) - 0.5)) * magnification) - x_pix = np.linspace(vl.x0, vl.x1, width) - y_pix = np.linspace(vl.y0, vl.y1, height) - if self._interpolation == "nearest": - x_mid = (self._Ax[:-1] + self._Ax[1:]) / 2 - y_mid = (self._Ay[:-1] + self._Ay[1:]) / 2 - x_int = x_mid.searchsorted(x_pix) - y_int = y_mid.searchsorted(y_pix) - # The following is equal to `A[y_int[:, None], x_int[None, :]]`, - # but many times faster. Both casting to uint32 (to have an - # effectively 1D array) and manual index flattening matter. - im = ( - np.ascontiguousarray(A).view(np.uint32).ravel()[ - np.add.outer(y_int * A.shape[1], x_int)] - .view(np.uint8).reshape((height, width, 4))) - else: # self._interpolation == "bilinear" - # Use np.interp to compute x_int/x_float has similar speed. - x_int = np.clip( - self._Ax.searchsorted(x_pix) - 1, 0, len(self._Ax) - 2) - y_int = np.clip( - self._Ay.searchsorted(y_pix) - 1, 0, len(self._Ay) - 2) - idx_int = np.add.outer(y_int * A.shape[1], x_int) - x_frac = np.clip( - np.divide(x_pix - self._Ax[x_int], np.diff(self._Ax)[x_int], - dtype=np.float32), # Downcasting helps with speed. - 0, 1) - y_frac = np.clip( - np.divide(y_pix - self._Ay[y_int], np.diff(self._Ay)[y_int], - dtype=np.float32), - 0, 1) - f00 = np.outer(1 - y_frac, 1 - x_frac) - f10 = np.outer(y_frac, 1 - x_frac) - f01 = np.outer(1 - y_frac, x_frac) - f11 = np.outer(y_frac, x_frac) - im = np.empty((height, width, 4), np.uint8) - for chan in range(4): - ac = A[:, :, chan].reshape(-1) # reshape(-1) avoids a copy. - # Shifting the buffer start (`ac[offset:]`) avoids an array - # addition (`ac[idx_int + offset]`). - buf = f00 * ac[idx_int] - buf += f10 * ac[A.shape[1]:][idx_int] - buf += f01 * ac[1:][idx_int] - buf += f11 * ac[A.shape[1] + 1:][idx_int] - im[:, :, chan] = buf # Implicitly casts to uint8. - return im, l, b, IdentityTransform() - - def set_data(self, x, y, A): - """ - Set the grid for the pixel centers, and the pixel values. - - Parameters - ---------- - x, y : 1D array-like - Monotonic arrays of shapes (N,) and (M,), respectively, specifying - pixel centers. - A : array-like - (M, N) `~numpy.ndarray` or masked array of values to be - colormapped, or (M, N, 3) RGB array, or (M, N, 4) RGBA array. - """ - x = np.array(x, np.float32) - y = np.array(y, np.float32) - A = cbook.safe_masked_invalid(A, copy=True) - if not (x.ndim == y.ndim == 1 and A.shape[0:2] == y.shape + x.shape): - raise TypeError("Axes don't match array shape") - if A.ndim not in [2, 3]: - raise TypeError("Can only plot 2D or 3D data") - if A.ndim == 3 and A.shape[2] not in [1, 3, 4]: - raise TypeError("3D arrays must have three (RGB) " - "or four (RGBA) color components") - if A.ndim == 3 and A.shape[2] == 1: - A = A.squeeze(axis=-1) - self._A = A - self._Ax = x - self._Ay = y - self._imcache = None - - self.stale = True - - def set_array(self, *args): - raise NotImplementedError('Method not supported') - - def set_interpolation(self, s): - """ - Parameters - ---------- - s : {'nearest', 'bilinear'} or None - If None, use :rc:`image.interpolation`. - """ - if s is not None and s not in ('nearest', 'bilinear'): - raise NotImplementedError('Only nearest neighbor and ' - 'bilinear interpolations are supported') - super().set_interpolation(s) - - def get_extent(self): - if self._A is None: - raise RuntimeError('Must set data first') - return self._Ax[0], self._Ax[-1], self._Ay[0], self._Ay[-1] - - def set_filternorm(self, s): - pass - - def set_filterrad(self, s): - pass - - def set_norm(self, norm): - if self._A is not None: - raise RuntimeError('Cannot change colors after loading data') - super().set_norm(norm) - - def set_cmap(self, cmap): - if self._A is not None: - raise RuntimeError('Cannot change colors after loading data') - super().set_cmap(cmap) - - -class PcolorImage(AxesImage): - """ - Make a pcolor-style plot with an irregular rectangular grid. - - This uses a variation of the original irregular image code, - and it is used by pcolorfast for the corresponding grid type. - """ - - @_api.make_keyword_only("3.6", name="cmap") - def __init__(self, ax, - x=None, - y=None, - A=None, - cmap=None, - norm=None, - **kwargs - ): - """ - Parameters - ---------- - ax : `~matplotlib.axes.Axes` - The axes the image will belong to. - x, y : 1D array-like, optional - Monotonic arrays of length N+1 and M+1, respectively, specifying - rectangle boundaries. If not given, will default to - ``range(N + 1)`` and ``range(M + 1)``, respectively. - A : array-like - The data to be color-coded. The interpretation depends on the - shape: - - - (M, N) `~numpy.ndarray` or masked array: values to be colormapped - - (M, N, 3): RGB array - - (M, N, 4): RGBA array - - cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap` - The Colormap instance or registered colormap name used to map - scalar data to colors. - norm : str or `~matplotlib.colors.Normalize` - Maps luminance to 0-1. - **kwargs : `~matplotlib.artist.Artist` properties - """ - super().__init__(ax, norm=norm, cmap=cmap) - self._internal_update(kwargs) - if A is not None: - self.set_data(x, y, A) - - def make_image(self, renderer, magnification=1.0, unsampled=False): - # docstring inherited - if self._A is None: - raise RuntimeError('You must first set the image array') - if unsampled: - raise ValueError('unsampled not supported on PColorImage') - - if self._imcache is None: - A = self.to_rgba(self._A, bytes=True) - self._imcache = np.pad(A, [(1, 1), (1, 1), (0, 0)], "constant") - padded_A = self._imcache - bg = mcolors.to_rgba(self.axes.patch.get_facecolor(), 0) - bg = (np.array(bg) * 255).astype(np.uint8) - if (padded_A[0, 0] != bg).all(): - padded_A[[0, -1], :] = padded_A[:, [0, -1]] = bg - - l, b, r, t = self.axes.bbox.extents - width = (round(r) + 0.5) - (round(l) - 0.5) - height = (round(t) + 0.5) - (round(b) - 0.5) - width = round(width * magnification) - height = round(height * magnification) - vl = self.axes.viewLim - - x_pix = np.linspace(vl.x0, vl.x1, width) - y_pix = np.linspace(vl.y0, vl.y1, height) - x_int = self._Ax.searchsorted(x_pix) - y_int = self._Ay.searchsorted(y_pix) - im = ( # See comment in NonUniformImage.make_image re: performance. - padded_A.view(np.uint32).ravel()[ - np.add.outer(y_int * padded_A.shape[1], x_int)] - .view(np.uint8).reshape((height, width, 4))) - return im, l, b, IdentityTransform() - - def _check_unsampled_image(self): - return False - - def set_data(self, x, y, A): - """ - Set the grid for the rectangle boundaries, and the data values. - - Parameters - ---------- - x, y : 1D array-like, optional - Monotonic arrays of length N+1 and M+1, respectively, specifying - rectangle boundaries. If not given, will default to - ``range(N + 1)`` and ``range(M + 1)``, respectively. - A : array-like - The data to be color-coded. The interpretation depends on the - shape: - - - (M, N) `~numpy.ndarray` or masked array: values to be colormapped - - (M, N, 3): RGB array - - (M, N, 4): RGBA array - """ - A = cbook.safe_masked_invalid(A, copy=True) - if x is None: - x = np.arange(0, A.shape[1]+1, dtype=np.float64) - else: - x = np.array(x, np.float64).ravel() - if y is None: - y = np.arange(0, A.shape[0]+1, dtype=np.float64) - else: - y = np.array(y, np.float64).ravel() - - if A.shape[:2] != (y.size-1, x.size-1): - raise ValueError( - "Axes don't match array shape. Got %s, expected %s." % - (A.shape[:2], (y.size - 1, x.size - 1))) - if A.ndim not in [2, 3]: - raise ValueError("A must be 2D or 3D") - if A.ndim == 3: - if A.shape[2] == 1: - A = A.squeeze(axis=-1) - elif A.shape[2] not in [3, 4]: - raise ValueError("3D arrays must have RGB or RGBA as last dim") - - # For efficient cursor readout, ensure x and y are increasing. - if x[-1] < x[0]: - x = x[::-1] - A = A[:, ::-1] - if y[-1] < y[0]: - y = y[::-1] - A = A[::-1] - - self._A = A - self._Ax = x - self._Ay = y - self._imcache = None - self.stale = True - - def set_array(self, *args): - raise NotImplementedError('Method not supported') - - def get_cursor_data(self, event): - # docstring inherited - x, y = event.xdata, event.ydata - if (x < self._Ax[0] or x > self._Ax[-1] or - y < self._Ay[0] or y > self._Ay[-1]): - return None - j = np.searchsorted(self._Ax, x) - 1 - i = np.searchsorted(self._Ay, y) - 1 - try: - return self._A[i, j] - except IndexError: - return None - - -class FigureImage(_ImageBase): - """An image attached to a figure.""" - - zorder = 0 - - _interpolation = 'nearest' - - @_api.make_keyword_only("3.6", name="cmap") - def __init__(self, fig, - cmap=None, - norm=None, - offsetx=0, - offsety=0, - origin=None, - **kwargs - ): - """ - cmap is a colors.Colormap instance - norm is a colors.Normalize instance to map luminance to 0-1 - - kwargs are an optional list of Artist keyword args - """ - super().__init__( - None, - norm=norm, - cmap=cmap, - origin=origin - ) - self.figure = fig - self.ox = offsetx - self.oy = offsety - self._internal_update(kwargs) - self.magnification = 1.0 - - def get_extent(self): - """Return the image extent as tuple (left, right, bottom, top).""" - numrows, numcols = self.get_size() - return (-0.5 + self.ox, numcols-0.5 + self.ox, - -0.5 + self.oy, numrows-0.5 + self.oy) - - def make_image(self, renderer, magnification=1.0, unsampled=False): - # docstring inherited - fac = renderer.dpi/self.figure.dpi - # fac here is to account for pdf, eps, svg backends where - # figure.dpi is set to 72. This means we need to scale the - # image (using magnification) and offset it appropriately. - bbox = Bbox([[self.ox/fac, self.oy/fac], - [(self.ox/fac + self._A.shape[1]), - (self.oy/fac + self._A.shape[0])]]) - width, height = self.figure.get_size_inches() - width *= renderer.dpi - height *= renderer.dpi - clip = Bbox([[0, 0], [width, height]]) - return self._make_image( - self._A, bbox, bbox, clip, magnification=magnification / fac, - unsampled=unsampled, round_to_pixel_border=False) - - def set_data(self, A): - """Set the image array.""" - cm.ScalarMappable.set_array(self, A) - self.stale = True - - -class BboxImage(_ImageBase): - """The Image class whose size is determined by the given bbox.""" - - @_api.make_keyword_only("3.6", name="cmap") - def __init__(self, bbox, - cmap=None, - norm=None, - interpolation=None, - origin=None, - filternorm=True, - filterrad=4.0, - resample=False, - **kwargs - ): - """ - cmap is a colors.Colormap instance - norm is a colors.Normalize instance to map luminance to 0-1 - - kwargs are an optional list of Artist keyword args - """ - super().__init__( - None, - cmap=cmap, - norm=norm, - interpolation=interpolation, - origin=origin, - filternorm=filternorm, - filterrad=filterrad, - resample=resample, - **kwargs - ) - self.bbox = bbox - - def get_window_extent(self, renderer=None): - if renderer is None: - renderer = self.get_figure()._get_renderer() - - if isinstance(self.bbox, BboxBase): - return self.bbox - elif callable(self.bbox): - return self.bbox(renderer) - else: - raise ValueError("Unknown type of bbox") - - def contains(self, mouseevent): - """Test whether the mouse event occurred within the image.""" - inside, info = self._default_contains(mouseevent) - if inside is not None: - return inside, info - - if not self.get_visible(): # or self.get_figure()._renderer is None: - return False, {} - - x, y = mouseevent.x, mouseevent.y - inside = self.get_window_extent().contains(x, y) - - return inside, {} - - def make_image(self, renderer, magnification=1.0, unsampled=False): - # docstring inherited - width, height = renderer.get_canvas_width_height() - bbox_in = self.get_window_extent(renderer).frozen() - bbox_in._points /= [width, height] - bbox_out = self.get_window_extent(renderer) - clip = Bbox([[0, 0], [width, height]]) - self._transform = BboxTransformTo(clip) - return self._make_image( - self._A, - bbox_in, bbox_out, clip, magnification, unsampled=unsampled) - - -def imread(fname, format=None): - """ - Read an image from a file into an array. - - .. note:: - - This function exists for historical reasons. It is recommended to - use `PIL.Image.open` instead for loading images. - - Parameters - ---------- - fname : str or file-like - The image file to read: a filename, a URL or a file-like object opened - in read-binary mode. - - Passing a URL is deprecated. Please open the URL - for reading and pass the result to Pillow, e.g. with - ``np.array(PIL.Image.open(urllib.request.urlopen(url)))``. - format : str, optional - The image file format assumed for reading the data. The image is - loaded as a PNG file if *format* is set to "png", if *fname* is a path - or opened file with a ".png" extension, or if it is a URL. In all - other cases, *format* is ignored and the format is auto-detected by - `PIL.Image.open`. - - Returns - ------- - `numpy.array` - The image data. The returned array has shape - - - (M, N) for grayscale images. - - (M, N, 3) for RGB images. - - (M, N, 4) for RGBA images. - - PNG images are returned as float arrays (0-1). All other formats are - returned as int arrays, with a bit depth determined by the file's - contents. - """ - # hide imports to speed initial import on systems with slow linkers - from urllib import parse - - if format is None: - if isinstance(fname, str): - parsed = parse.urlparse(fname) - # If the string is a URL (Windows paths appear as if they have a - # length-1 scheme), assume png. - if len(parsed.scheme) > 1: - ext = 'png' - else: - ext = Path(fname).suffix.lower()[1:] - elif hasattr(fname, 'geturl'): # Returned by urlopen(). - # We could try to parse the url's path and use the extension, but - # returning png is consistent with the block above. Note that this - # if clause has to come before checking for fname.name as - # urlopen("file:///...") also has a name attribute (with the fixed - # value ""). - ext = 'png' - elif hasattr(fname, 'name'): - ext = Path(fname.name).suffix.lower()[1:] - else: - ext = 'png' - else: - ext = format - img_open = ( - PIL.PngImagePlugin.PngImageFile if ext == 'png' else PIL.Image.open) - if isinstance(fname, str) and len(parse.urlparse(fname).scheme) > 1: - # Pillow doesn't handle URLs directly. - raise ValueError( - "Please open the URL for reading and pass the " - "result to Pillow, e.g. with " - "``np.array(PIL.Image.open(urllib.request.urlopen(url)))``." - ) - with img_open(fname) as image: - return (_pil_png_to_float_array(image) - if isinstance(image, PIL.PngImagePlugin.PngImageFile) else - pil_to_array(image)) - - -def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None, - origin=None, dpi=100, *, metadata=None, pil_kwargs=None): - """ - Colormap and save an array as an image file. - - RGB(A) images are passed through. Single channel images will be - colormapped according to *cmap* and *norm*. - - .. note:: - - If you want to save a single channel image as gray scale please use an - image I/O library (such as pillow, tifffile, or imageio) directly. - - Parameters - ---------- - fname : str or path-like or file-like - A path or a file-like object to store the image in. - If *format* is not set, then the output format is inferred from the - extension of *fname*, if any, and from :rc:`savefig.format` otherwise. - If *format* is set, it determines the output format. - arr : array-like - The image data. The shape can be one of - MxN (luminance), MxNx3 (RGB) or MxNx4 (RGBA). - vmin, vmax : float, optional - *vmin* and *vmax* set the color scaling for the image by fixing the - values that map to the colormap color limits. If either *vmin* - or *vmax* is None, that limit is determined from the *arr* - min/max value. - cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap` - A Colormap instance or registered colormap name. The colormap - maps scalar data to colors. It is ignored for RGB(A) data. - format : str, optional - The file format, e.g. 'png', 'pdf', 'svg', ... The behavior when this - is unset is documented under *fname*. - origin : {'upper', 'lower'}, default: :rc:`image.origin` - Indicates whether the ``(0, 0)`` index of the array is in the upper - left or lower left corner of the axes. - dpi : float - The DPI to store in the metadata of the file. This does not affect the - resolution of the output image. Depending on file format, this may be - rounded to the nearest integer. - metadata : dict, optional - Metadata in the image file. The supported keys depend on the output - format, see the documentation of the respective backends for more - information. - pil_kwargs : dict, optional - Keyword arguments passed to `PIL.Image.Image.save`. If the 'pnginfo' - key is present, it completely overrides *metadata*, including the - default 'Software' key. - """ - from matplotlib.figure import Figure - if isinstance(fname, os.PathLike): - fname = os.fspath(fname) - if format is None: - format = (Path(fname).suffix[1:] if isinstance(fname, str) - else mpl.rcParams["savefig.format"]).lower() - if format in ["pdf", "ps", "eps", "svg"]: - # Vector formats that are not handled by PIL. - if pil_kwargs is not None: - raise ValueError( - f"Cannot use 'pil_kwargs' when saving to {format}") - fig = Figure(dpi=dpi, frameon=False) - fig.figimage(arr, cmap=cmap, vmin=vmin, vmax=vmax, origin=origin, - resize=True) - fig.savefig(fname, dpi=dpi, format=format, transparent=True, - metadata=metadata) - else: - # Don't bother creating an image; this avoids rounding errors on the - # size when dividing and then multiplying by dpi. - if origin is None: - origin = mpl.rcParams["image.origin"] - if origin == "lower": - arr = arr[::-1] - if (isinstance(arr, memoryview) and arr.format == "B" - and arr.ndim == 3 and arr.shape[-1] == 4): - # Such an ``arr`` would also be handled fine by sm.to_rgba below - # (after casting with asarray), but it is useful to special-case it - # because that's what backend_agg passes, and can be in fact used - # as is, saving a few operations. - rgba = arr - else: - sm = cm.ScalarMappable(cmap=cmap) - sm.set_clim(vmin, vmax) - rgba = sm.to_rgba(arr, bytes=True) - if pil_kwargs is None: - pil_kwargs = {} - else: - # we modify this below, so make a copy (don't modify caller's dict) - pil_kwargs = pil_kwargs.copy() - pil_shape = (rgba.shape[1], rgba.shape[0]) - image = PIL.Image.frombuffer( - "RGBA", pil_shape, rgba, "raw", "RGBA", 0, 1) - if format == "png": - # Only use the metadata kwarg if pnginfo is not set, because the - # semantics of duplicate keys in pnginfo is unclear. - if "pnginfo" in pil_kwargs: - if metadata: - _api.warn_external("'metadata' is overridden by the " - "'pnginfo' entry in 'pil_kwargs'.") - else: - metadata = { - "Software": (f"Matplotlib version{mpl.__version__}, " - f"https://matplotlib.org/"), - **(metadata if metadata is not None else {}), - } - pil_kwargs["pnginfo"] = pnginfo = PIL.PngImagePlugin.PngInfo() - for k, v in metadata.items(): - if v is not None: - pnginfo.add_text(k, v) - if format in ["jpg", "jpeg"]: - format = "jpeg" # Pillow doesn't recognize "jpg". - facecolor = mpl.rcParams["savefig.facecolor"] - if cbook._str_equal(facecolor, "auto"): - facecolor = mpl.rcParams["figure.facecolor"] - color = tuple(int(x * 255) for x in mcolors.to_rgb(facecolor)) - background = PIL.Image.new("RGB", pil_shape, color) - background.paste(image, image) - image = background - pil_kwargs.setdefault("format", format) - pil_kwargs.setdefault("dpi", (dpi, dpi)) - image.save(fname, **pil_kwargs) - - -def pil_to_array(pilImage): - """ - Load a `PIL image`_ and return it as a numpy int array. - - .. _PIL image: https://pillow.readthedocs.io/en/latest/reference/Image.html - - Returns - ------- - numpy.array - - The array shape depends on the image type: - - - (M, N) for grayscale images. - - (M, N, 3) for RGB images. - - (M, N, 4) for RGBA images. - """ - if pilImage.mode in ['RGBA', 'RGBX', 'RGB', 'L']: - # return MxNx4 RGBA, MxNx3 RBA, or MxN luminance array - return np.asarray(pilImage) - elif pilImage.mode.startswith('I;16'): - # return MxN luminance array of uint16 - raw = pilImage.tobytes('raw', pilImage.mode) - if pilImage.mode.endswith('B'): - x = np.frombuffer(raw, '>u2') - else: - x = np.frombuffer(raw, '`. - -The `Legend` class is a container of legend handles and legend texts. - -The legend handler map specifies how to create legend handles from artists -(lines, patches, etc.) in the axes or figures. Default legend handlers are -defined in the :mod:`~matplotlib.legend_handler` module. While not all artist -types are covered by the default legend handlers, custom legend handlers can be -defined to support arbitrary objects. - -See the :doc:`legend guide ` for more -information. -""" - -import itertools -import logging -import time - -import numpy as np - -import matplotlib as mpl -from matplotlib import _api, _docstring, colors, offsetbox -from matplotlib.artist import Artist, allow_rasterization -from matplotlib.cbook import silent_list -from matplotlib.font_manager import FontProperties -from matplotlib.lines import Line2D -from matplotlib.patches import (Patch, Rectangle, Shadow, FancyBboxPatch, - StepPatch) -from matplotlib.collections import ( - Collection, CircleCollection, LineCollection, PathCollection, - PolyCollection, RegularPolyCollection) -from matplotlib.text import Text -from matplotlib.transforms import Bbox, BboxBase, TransformedBbox -from matplotlib.transforms import BboxTransformTo, BboxTransformFrom -from matplotlib.offsetbox import ( - AnchoredOffsetbox, DraggableOffsetBox, - HPacker, VPacker, - DrawingArea, TextArea, -) -from matplotlib.container import ErrorbarContainer, BarContainer, StemContainer -from . import legend_handler - - -class DraggableLegend(DraggableOffsetBox): - def __init__(self, legend, use_blit=False, update="loc"): - """ - Wrapper around a `.Legend` to support mouse dragging. - - Parameters - ---------- - legend : `.Legend` - The `.Legend` instance to wrap. - use_blit : bool, optional - Use blitting for faster image composition. For details see - :ref:`func-animation`. - update : {'loc', 'bbox'}, optional - If "loc", update the *loc* parameter of the legend upon finalizing. - If "bbox", update the *bbox_to_anchor* parameter. - """ - self.legend = legend - - _api.check_in_list(["loc", "bbox"], update=update) - self._update = update - - super().__init__(legend, legend._legend_box, use_blit=use_blit) - - def finalize_offset(self): - if self._update == "loc": - self._update_loc(self.get_loc_in_canvas()) - elif self._update == "bbox": - self._update_bbox_to_anchor(self.get_loc_in_canvas()) - - def _update_loc(self, loc_in_canvas): - bbox = self.legend.get_bbox_to_anchor() - # if bbox has zero width or height, the transformation is - # ill-defined. Fall back to the default bbox_to_anchor. - if bbox.width == 0 or bbox.height == 0: - self.legend.set_bbox_to_anchor(None) - bbox = self.legend.get_bbox_to_anchor() - _bbox_transform = BboxTransformFrom(bbox) - self.legend._loc = tuple(_bbox_transform.transform(loc_in_canvas)) - - def _update_bbox_to_anchor(self, loc_in_canvas): - loc_in_bbox = self.legend.axes.transAxes.transform(loc_in_canvas) - self.legend.set_bbox_to_anchor(loc_in_bbox) - - -_legend_kw_doc_base = """ -bbox_to_anchor : `.BboxBase`, 2-tuple, or 4-tuple of floats - Box that is used to position the legend in conjunction with *loc*. - Defaults to `axes.bbox` (if called as a method to `.Axes.legend`) or - `figure.bbox` (if `.Figure.legend`). This argument allows arbitrary - placement of the legend. - - Bbox coordinates are interpreted in the coordinate system given by - *bbox_transform*, with the default transform - Axes or Figure coordinates, depending on which ``legend`` is called. - - If a 4-tuple or `.BboxBase` is given, then it specifies the bbox - ``(x, y, width, height)`` that the legend is placed in. - To put the legend in the best location in the bottom right - quadrant of the axes (or figure):: - - loc='best', bbox_to_anchor=(0.5, 0., 0.5, 0.5) - - A 2-tuple ``(x, y)`` places the corner of the legend specified by *loc* at - x, y. For example, to put the legend's upper right-hand corner in the - center of the axes (or figure) the following keywords can be used:: - - loc='upper right', bbox_to_anchor=(0.5, 0.5) - -ncols : int, default: 1 - The number of columns that the legend has. - - For backward compatibility, the spelling *ncol* is also supported - but it is discouraged. If both are given, *ncols* takes precedence. - -prop : None or `~matplotlib.font_manager.FontProperties` or dict - The font properties of the legend. If None (default), the current - :data:`matplotlib.rcParams` will be used. - -fontsize : int or {'xx-small', 'x-small', 'small', 'medium', 'large', \ -'x-large', 'xx-large'} - The font size of the legend. If the value is numeric the size will be the - absolute font size in points. String values are relative to the current - default font size. This argument is only used if *prop* is not specified. - -labelcolor : str or list, default: :rc:`legend.labelcolor` - The color of the text in the legend. Either a valid color string - (for example, 'red'), or a list of color strings. The labelcolor can - also be made to match the color of the line or marker using 'linecolor', - 'markerfacecolor' (or 'mfc'), or 'markeredgecolor' (or 'mec'). - - Labelcolor can be set globally using :rc:`legend.labelcolor`. If None, - use :rc:`text.color`. - -numpoints : int, default: :rc:`legend.numpoints` - The number of marker points in the legend when creating a legend - entry for a `.Line2D` (line). - -scatterpoints : int, default: :rc:`legend.scatterpoints` - The number of marker points in the legend when creating - a legend entry for a `.PathCollection` (scatter plot). - -scatteryoffsets : iterable of floats, default: ``[0.375, 0.5, 0.3125]`` - The vertical offset (relative to the font size) for the markers - created for a scatter plot legend entry. 0.0 is at the base the - legend text, and 1.0 is at the top. To draw all markers at the - same height, set to ``[0.5]``. - -markerscale : float, default: :rc:`legend.markerscale` - The relative size of legend markers compared to the originally drawn ones. - -markerfirst : bool, default: True - If *True*, legend marker is placed to the left of the legend label. - If *False*, legend marker is placed to the right of the legend label. - -reverse : bool, default: False - If *True*, the legend labels are displayed in reverse order from the input. - If *False*, the legend labels are displayed in the same order as the input. - - .. versionadded:: 3.7 - -frameon : bool, default: :rc:`legend.frameon` - Whether the legend should be drawn on a patch (frame). - -fancybox : bool, default: :rc:`legend.fancybox` - Whether round edges should be enabled around the `.FancyBboxPatch` which - makes up the legend's background. - -shadow : bool, default: :rc:`legend.shadow` - Whether to draw a shadow behind the legend. - -framealpha : float, default: :rc:`legend.framealpha` - The alpha transparency of the legend's background. - If *shadow* is activated and *framealpha* is ``None``, the default value is - ignored. - -facecolor : "inherit" or color, default: :rc:`legend.facecolor` - The legend's background color. - If ``"inherit"``, use :rc:`axes.facecolor`. - -edgecolor : "inherit" or color, default: :rc:`legend.edgecolor` - The legend's background patch edge color. - If ``"inherit"``, use take :rc:`axes.edgecolor`. - -mode : {"expand", None} - If *mode* is set to ``"expand"`` the legend will be horizontally - expanded to fill the axes area (or *bbox_to_anchor* if defines - the legend's size). - -bbox_transform : None or `~matplotlib.transforms.Transform` - The transform for the bounding box (*bbox_to_anchor*). For a value - of ``None`` (default) the Axes' - :data:`~matplotlib.axes.Axes.transAxes` transform will be used. - -title : str or None - The legend's title. Default is no title (``None``). - -title_fontproperties : None or `~matplotlib.font_manager.FontProperties` or dict - The font properties of the legend's title. If None (default), the - *title_fontsize* argument will be used if present; if *title_fontsize* is - also None, the current :rc:`legend.title_fontsize` will be used. - -title_fontsize : int or {'xx-small', 'x-small', 'small', 'medium', 'large', \ -'x-large', 'xx-large'}, default: :rc:`legend.title_fontsize` - The font size of the legend's title. - Note: This cannot be combined with *title_fontproperties*. If you want - to set the fontsize alongside other font properties, use the *size* - parameter in *title_fontproperties*. - -alignment : {'center', 'left', 'right'}, default: 'center' - The alignment of the legend title and the box of entries. The entries - are aligned as a single block, so that markers always lined up. - -borderpad : float, default: :rc:`legend.borderpad` - The fractional whitespace inside the legend border, in font-size units. - -labelspacing : float, default: :rc:`legend.labelspacing` - The vertical space between the legend entries, in font-size units. - -handlelength : float, default: :rc:`legend.handlelength` - The length of the legend handles, in font-size units. - -handleheight : float, default: :rc:`legend.handleheight` - The height of the legend handles, in font-size units. - -handletextpad : float, default: :rc:`legend.handletextpad` - The pad between the legend handle and text, in font-size units. - -borderaxespad : float, default: :rc:`legend.borderaxespad` - The pad between the axes and legend border, in font-size units. - -columnspacing : float, default: :rc:`legend.columnspacing` - The spacing between columns, in font-size units. - -handler_map : dict or None - The custom dictionary mapping instances or types to a legend - handler. This *handler_map* updates the default handler map - found at `matplotlib.legend.Legend.get_legend_handler_map`. - -draggable : bool, default: False - Whether the legend can be dragged with the mouse. -""" - -_loc_doc_base = """ -loc : str or pair of floats, default: {default} - The location of the legend. - - The strings ``'upper left'``, ``'upper right'``, ``'lower left'``, - ``'lower right'`` place the legend at the corresponding corner of the - {parent}. - - The strings ``'upper center'``, ``'lower center'``, ``'center left'``, - ``'center right'`` place the legend at the center of the corresponding edge - of the {parent}. - - The string ``'center'`` places the legend at the center of the {parent}. -{best} - The location can also be a 2-tuple giving the coordinates of the lower-left - corner of the legend in {parent} coordinates (in which case *bbox_to_anchor* - will be ignored). - - For back-compatibility, ``'center right'`` (but no other location) can also - be spelled ``'right'``, and each "string" location can also be given as a - numeric value: - - ================== ============= - Location String Location Code - ================== ============= - 'best' (Axes only) 0 - 'upper right' 1 - 'upper left' 2 - 'lower left' 3 - 'lower right' 4 - 'right' 5 - 'center left' 6 - 'center right' 7 - 'lower center' 8 - 'upper center' 9 - 'center' 10 - ================== ============= - {outside}""" - -_loc_doc_best = """ - The string ``'best'`` places the legend at the location, among the nine - locations defined so far, with the minimum overlap with other drawn - artists. This option can be quite slow for plots with large amounts of - data; your plotting speed may benefit from providing a specific location. -""" - -_legend_kw_axes_st = ( - _loc_doc_base.format(parent='axes', default=':rc:`legend.loc`', - best=_loc_doc_best, outside='') + - _legend_kw_doc_base) -_docstring.interpd.update(_legend_kw_axes=_legend_kw_axes_st) - -_outside_doc = """ - If a figure is using the constrained layout manager, the string codes - of the *loc* keyword argument can get better layout behaviour using the - prefix 'outside'. There is ambiguity at the corners, so 'outside - upper right' will make space for the legend above the rest of the - axes in the layout, and 'outside right upper' will make space on the - right side of the layout. In addition to the values of *loc* - listed above, we have 'outside right upper', 'outside right lower', - 'outside left upper', and 'outside left lower'. See - :doc:`/tutorials/intermediate/legend_guide` for more details. -""" - -_legend_kw_figure_st = ( - _loc_doc_base.format(parent='figure', default="'upper right'", - best='', outside=_outside_doc) + - _legend_kw_doc_base) -_docstring.interpd.update(_legend_kw_figure=_legend_kw_figure_st) - -_legend_kw_both_st = ( - _loc_doc_base.format(parent='axes/figure', - default=":rc:`legend.loc` for Axes, 'upper right' for Figure", - best=_loc_doc_best, outside=_outside_doc) + - _legend_kw_doc_base) -_docstring.interpd.update(_legend_kw_doc=_legend_kw_both_st) - - -class Legend(Artist): - """ - Place a legend on the figure/axes. - """ - - # 'best' is only implemented for axes legends - codes = {'best': 0, **AnchoredOffsetbox.codes} - zorder = 5 - - def __str__(self): - return "Legend" - - @_api.make_keyword_only("3.6", "loc") - @_docstring.dedent_interpd - def __init__( - self, parent, handles, labels, - loc=None, - numpoints=None, # number of points in the legend line - markerscale=None, # relative size of legend markers vs. original - markerfirst=True, # left/right ordering of legend marker and label - reverse=False, # reverse ordering of legend marker and label - scatterpoints=None, # number of scatter points - scatteryoffsets=None, - prop=None, # properties for the legend texts - fontsize=None, # keyword to set font size directly - labelcolor=None, # keyword to set the text color - - # spacing & pad defined as a fraction of the font-size - borderpad=None, # whitespace inside the legend border - labelspacing=None, # vertical space between the legend entries - handlelength=None, # length of the legend handles - handleheight=None, # height of the legend handles - handletextpad=None, # pad between the legend handle and text - borderaxespad=None, # pad between the axes and legend border - columnspacing=None, # spacing between columns - - ncols=1, # number of columns - mode=None, # horizontal distribution of columns: None or "expand" - - fancybox=None, # True: fancy box, False: rounded box, None: rcParam - shadow=None, - title=None, # legend title - title_fontsize=None, # legend title font size - framealpha=None, # set frame alpha - edgecolor=None, # frame patch edgecolor - facecolor=None, # frame patch facecolor - - bbox_to_anchor=None, # bbox to which the legend will be anchored - bbox_transform=None, # transform for the bbox - frameon=None, # draw frame - handler_map=None, - title_fontproperties=None, # properties for the legend title - alignment="center", # control the alignment within the legend box - *, - ncol=1, # synonym for ncols (backward compatibility) - draggable=False # whether the legend can be dragged with the mouse - ): - """ - Parameters - ---------- - parent : `~matplotlib.axes.Axes` or `.Figure` - The artist that contains the legend. - - handles : list of `.Artist` - A list of Artists (lines, patches) to be added to the legend. - - labels : list of str - A list of labels to show next to the artists. The length of handles - and labels should be the same. If they are not, they are truncated - to the length of the shorter list. - - Other Parameters - ---------------- - %(_legend_kw_doc)s - - Attributes - ---------- - legend_handles - List of `.Artist` objects added as legend entries. - - .. versionadded:: 3.7 - """ - # local import only to avoid circularity - from matplotlib.axes import Axes - from matplotlib.figure import FigureBase - - super().__init__() - - if prop is None: - if fontsize is not None: - self.prop = FontProperties(size=fontsize) - else: - self.prop = FontProperties( - size=mpl.rcParams["legend.fontsize"]) - else: - self.prop = FontProperties._from_any(prop) - if isinstance(prop, dict) and "size" not in prop: - self.prop.set_size(mpl.rcParams["legend.fontsize"]) - - self._fontsize = self.prop.get_size_in_points() - - self.texts = [] - self.legend_handles = [] - self._legend_title_box = None - - #: A dictionary with the extra handler mappings for this Legend - #: instance. - self._custom_handler_map = handler_map - - def val_or_rc(val, rc_name): - return val if val is not None else mpl.rcParams[rc_name] - - self.numpoints = val_or_rc(numpoints, 'legend.numpoints') - self.markerscale = val_or_rc(markerscale, 'legend.markerscale') - self.scatterpoints = val_or_rc(scatterpoints, 'legend.scatterpoints') - self.borderpad = val_or_rc(borderpad, 'legend.borderpad') - self.labelspacing = val_or_rc(labelspacing, 'legend.labelspacing') - self.handlelength = val_or_rc(handlelength, 'legend.handlelength') - self.handleheight = val_or_rc(handleheight, 'legend.handleheight') - self.handletextpad = val_or_rc(handletextpad, 'legend.handletextpad') - self.borderaxespad = val_or_rc(borderaxespad, 'legend.borderaxespad') - self.columnspacing = val_or_rc(columnspacing, 'legend.columnspacing') - self.shadow = val_or_rc(shadow, 'legend.shadow') - # trim handles and labels if illegal label... - _lab, _hand = [], [] - for label, handle in zip(labels, handles): - if isinstance(label, str) and label.startswith('_'): - _api.warn_external(f"The label {label!r} of {handle!r} starts " - "with '_'. It is thus excluded from the " - "legend.") - else: - _lab.append(label) - _hand.append(handle) - labels, handles = _lab, _hand - - if reverse: - labels.reverse() - handles.reverse() - - if len(handles) < 2: - ncols = 1 - self._ncols = ncols if ncols != 1 else ncol - - if self.numpoints <= 0: - raise ValueError("numpoints must be > 0; it was %d" % numpoints) - - # introduce y-offset for handles of the scatter plot - if scatteryoffsets is None: - self._scatteryoffsets = np.array([3. / 8., 4. / 8., 2.5 / 8.]) - else: - self._scatteryoffsets = np.asarray(scatteryoffsets) - reps = self.scatterpoints // len(self._scatteryoffsets) + 1 - self._scatteryoffsets = np.tile(self._scatteryoffsets, - reps)[:self.scatterpoints] - - # _legend_box is a VPacker instance that contains all - # legend items and will be initialized from _init_legend_box() - # method. - self._legend_box = None - - if isinstance(parent, Axes): - self.isaxes = True - self.axes = parent - self.set_figure(parent.figure) - elif isinstance(parent, FigureBase): - self.isaxes = False - self.set_figure(parent) - else: - raise TypeError( - "Legend needs either Axes or FigureBase as parent" - ) - self.parent = parent - - loc0 = loc - self._loc_used_default = loc is None - if loc is None: - loc = mpl.rcParams["legend.loc"] - if not self.isaxes and loc in [0, 'best']: - loc = 'upper right' - - # handle outside legends: - self._outside_loc = None - if isinstance(loc, str): - if loc.split()[0] == 'outside': - # strip outside: - loc = loc.split('outside ')[1] - # strip "center" at the beginning - self._outside_loc = loc.replace('center ', '') - # strip first - self._outside_loc = self._outside_loc.split()[0] - locs = loc.split() - if len(locs) > 1 and locs[0] in ('right', 'left'): - # locs doesn't accept "left upper", etc, so swap - if locs[0] != 'center': - locs = locs[::-1] - loc = locs[0] + ' ' + locs[1] - # check that loc is in acceptable strings - loc = _api.check_getitem(self.codes, loc=loc) - - if self.isaxes and self._outside_loc: - raise ValueError( - f"'outside' option for loc='{loc0}' keyword argument only " - "works for figure legends") - - if not self.isaxes and loc == 0: - raise ValueError( - "Automatic legend placement (loc='best') not implemented for " - "figure legend") - - self._mode = mode - self.set_bbox_to_anchor(bbox_to_anchor, bbox_transform) - - # We use FancyBboxPatch to draw a legend frame. The location - # and size of the box will be updated during the drawing time. - - if facecolor is None: - facecolor = mpl.rcParams["legend.facecolor"] - if facecolor == 'inherit': - facecolor = mpl.rcParams["axes.facecolor"] - - if edgecolor is None: - edgecolor = mpl.rcParams["legend.edgecolor"] - if edgecolor == 'inherit': - edgecolor = mpl.rcParams["axes.edgecolor"] - - if fancybox is None: - fancybox = mpl.rcParams["legend.fancybox"] - - self.legendPatch = FancyBboxPatch( - xy=(0, 0), width=1, height=1, - facecolor=facecolor, edgecolor=edgecolor, - # If shadow is used, default to alpha=1 (#8943). - alpha=(framealpha if framealpha is not None - else 1 if shadow - else mpl.rcParams["legend.framealpha"]), - # The width and height of the legendPatch will be set (in draw()) - # to the length that includes the padding. Thus we set pad=0 here. - boxstyle=("round,pad=0,rounding_size=0.2" if fancybox - else "square,pad=0"), - mutation_scale=self._fontsize, - snap=True, - visible=(frameon if frameon is not None - else mpl.rcParams["legend.frameon"]) - ) - self._set_artist_props(self.legendPatch) - - _api.check_in_list(["center", "left", "right"], alignment=alignment) - self._alignment = alignment - - # init with null renderer - self._init_legend_box(handles, labels, markerfirst) - - tmp = self._loc_used_default - self._set_loc(loc) - self._loc_used_default = tmp # ignore changes done by _set_loc - - # figure out title font properties: - if title_fontsize is not None and title_fontproperties is not None: - raise ValueError( - "title_fontsize and title_fontproperties can't be specified " - "at the same time. Only use one of them. ") - title_prop_fp = FontProperties._from_any(title_fontproperties) - if isinstance(title_fontproperties, dict): - if "size" not in title_fontproperties: - title_fontsize = mpl.rcParams["legend.title_fontsize"] - title_prop_fp.set_size(title_fontsize) - elif title_fontsize is not None: - title_prop_fp.set_size(title_fontsize) - elif not isinstance(title_fontproperties, FontProperties): - title_fontsize = mpl.rcParams["legend.title_fontsize"] - title_prop_fp.set_size(title_fontsize) - - self.set_title(title, prop=title_prop_fp) - - self._draggable = None - self.set_draggable(state=draggable) - - # set the text color - - color_getters = { # getter function depends on line or patch - 'linecolor': ['get_color', 'get_facecolor'], - 'markerfacecolor': ['get_markerfacecolor', 'get_facecolor'], - 'mfc': ['get_markerfacecolor', 'get_facecolor'], - 'markeredgecolor': ['get_markeredgecolor', 'get_edgecolor'], - 'mec': ['get_markeredgecolor', 'get_edgecolor'], - } - if labelcolor is None: - if mpl.rcParams['legend.labelcolor'] is not None: - labelcolor = mpl.rcParams['legend.labelcolor'] - else: - labelcolor = mpl.rcParams['text.color'] - if isinstance(labelcolor, str) and labelcolor in color_getters: - getter_names = color_getters[labelcolor] - for handle, text in zip(self.legend_handles, self.texts): - try: - if handle.get_array() is not None: - continue - except AttributeError: - pass - for getter_name in getter_names: - try: - color = getattr(handle, getter_name)() - if isinstance(color, np.ndarray): - if ( - color.shape[0] == 1 - or np.isclose(color, color[0]).all() - ): - text.set_color(color[0]) - else: - pass - else: - text.set_color(color) - break - except AttributeError: - pass - elif isinstance(labelcolor, str) and labelcolor == 'none': - for text in self.texts: - text.set_color(labelcolor) - elif np.iterable(labelcolor): - for text, color in zip(self.texts, - itertools.cycle( - colors.to_rgba_array(labelcolor))): - text.set_color(color) - else: - raise ValueError(f"Invalid labelcolor: {labelcolor!r}") - - legendHandles = _api.deprecated('3.7', alternative="legend_handles")( - property(lambda self: self.legend_handles)) - - def _set_artist_props(self, a): - """ - Set the boilerplate props for artists added to axes. - """ - a.set_figure(self.figure) - if self.isaxes: - # a.set_axes(self.axes) - a.axes = self.axes - - a.set_transform(self.get_transform()) - - def _set_loc(self, loc): - # find_offset function will be provided to _legend_box and - # _legend_box will draw itself at the location of the return - # value of the find_offset. - self._loc_used_default = False - self._loc_real = loc - self.stale = True - self._legend_box.set_offset(self._findoffset) - - def set_ncols(self, ncols): - """Set the number of columns.""" - self._ncols = ncols - - def _get_loc(self): - return self._loc_real - - _loc = property(_get_loc, _set_loc) - - def _findoffset(self, width, height, xdescent, ydescent, renderer): - """Helper function to locate the legend.""" - - if self._loc == 0: # "best". - x, y = self._find_best_position(width, height, renderer) - elif self._loc in Legend.codes.values(): # Fixed location. - bbox = Bbox.from_bounds(0, 0, width, height) - x, y = self._get_anchored_bbox(self._loc, bbox, - self.get_bbox_to_anchor(), - renderer) - else: # Axes or figure coordinates. - fx, fy = self._loc - bbox = self.get_bbox_to_anchor() - x, y = bbox.x0 + bbox.width * fx, bbox.y0 + bbox.height * fy - - return x + xdescent, y + ydescent - - @allow_rasterization - def draw(self, renderer): - # docstring inherited - if not self.get_visible(): - return - - renderer.open_group('legend', gid=self.get_gid()) - - fontsize = renderer.points_to_pixels(self._fontsize) - - # if mode == fill, set the width of the legend_box to the - # width of the parent (minus pads) - if self._mode in ["expand"]: - pad = 2 * (self.borderaxespad + self.borderpad) * fontsize - self._legend_box.set_width(self.get_bbox_to_anchor().width - pad) - - # update the location and size of the legend. This needs to - # be done in any case to clip the figure right. - bbox = self._legend_box.get_window_extent(renderer) - self.legendPatch.set_bounds(bbox.bounds) - self.legendPatch.set_mutation_scale(fontsize) - - if self.shadow: - Shadow(self.legendPatch, 2, -2).draw(renderer) - - self.legendPatch.draw(renderer) - self._legend_box.draw(renderer) - - renderer.close_group('legend') - self.stale = False - - # _default_handler_map defines the default mapping between plot - # elements and the legend handlers. - - _default_handler_map = { - StemContainer: legend_handler.HandlerStem(), - ErrorbarContainer: legend_handler.HandlerErrorbar(), - Line2D: legend_handler.HandlerLine2D(), - Patch: legend_handler.HandlerPatch(), - StepPatch: legend_handler.HandlerStepPatch(), - LineCollection: legend_handler.HandlerLineCollection(), - RegularPolyCollection: legend_handler.HandlerRegularPolyCollection(), - CircleCollection: legend_handler.HandlerCircleCollection(), - BarContainer: legend_handler.HandlerPatch( - update_func=legend_handler.update_from_first_child), - tuple: legend_handler.HandlerTuple(), - PathCollection: legend_handler.HandlerPathCollection(), - PolyCollection: legend_handler.HandlerPolyCollection() - } - - # (get|set|update)_default_handler_maps are public interfaces to - # modify the default handler map. - - @classmethod - def get_default_handler_map(cls): - """Return the global default handler map, shared by all legends.""" - return cls._default_handler_map - - @classmethod - def set_default_handler_map(cls, handler_map): - """Set the global default handler map, shared by all legends.""" - cls._default_handler_map = handler_map - - @classmethod - def update_default_handler_map(cls, handler_map): - """Update the global default handler map, shared by all legends.""" - cls._default_handler_map.update(handler_map) - - def get_legend_handler_map(self): - """Return this legend instance's handler map.""" - default_handler_map = self.get_default_handler_map() - return ({**default_handler_map, **self._custom_handler_map} - if self._custom_handler_map else default_handler_map) - - @staticmethod - def get_legend_handler(legend_handler_map, orig_handle): - """ - Return a legend handler from *legend_handler_map* that - corresponds to *orig_handler*. - - *legend_handler_map* should be a dictionary object (that is - returned by the get_legend_handler_map method). - - It first checks if the *orig_handle* itself is a key in the - *legend_handler_map* and return the associated value. - Otherwise, it checks for each of the classes in its - method-resolution-order. If no matching key is found, it - returns ``None``. - """ - try: - return legend_handler_map[orig_handle] - except (TypeError, KeyError): # TypeError if unhashable. - pass - for handle_type in type(orig_handle).mro(): - try: - return legend_handler_map[handle_type] - except KeyError: - pass - return None - - def _init_legend_box(self, handles, labels, markerfirst=True): - """ - Initialize the legend_box. The legend_box is an instance of - the OffsetBox, which is packed with legend handles and - texts. Once packed, their location is calculated during the - drawing time. - """ - - fontsize = self._fontsize - - # legend_box is a HPacker, horizontally packed with columns. - # Each column is a VPacker, vertically packed with legend items. - # Each legend item is a HPacker packed with: - # - handlebox: a DrawingArea which contains the legend handle. - # - labelbox: a TextArea which contains the legend text. - - text_list = [] # the list of text instances - handle_list = [] # the list of handle instances - handles_and_labels = [] - - # The approximate height and descent of text. These values are - # only used for plotting the legend handle. - descent = 0.35 * fontsize * (self.handleheight - 0.7) # heuristic. - height = fontsize * self.handleheight - descent - # each handle needs to be drawn inside a box of (x, y, w, h) = - # (0, -descent, width, height). And their coordinates should - # be given in the display coordinates. - - # The transformation of each handle will be automatically set - # to self.get_transform(). If the artist does not use its - # default transform (e.g., Collections), you need to - # manually set their transform to the self.get_transform(). - legend_handler_map = self.get_legend_handler_map() - - for orig_handle, label in zip(handles, labels): - handler = self.get_legend_handler(legend_handler_map, orig_handle) - if handler is None: - _api.warn_external( - "Legend does not support handles for {0} " - "instances.\nA proxy artist may be used " - "instead.\nSee: https://matplotlib.org/" - "stable/tutorials/intermediate/legend_guide.html" - "#controlling-the-legend-entries".format( - type(orig_handle).__name__)) - # No handle for this artist, so we just defer to None. - handle_list.append(None) - else: - textbox = TextArea(label, multilinebaseline=True, - textprops=dict( - verticalalignment='baseline', - horizontalalignment='left', - fontproperties=self.prop)) - handlebox = DrawingArea(width=self.handlelength * fontsize, - height=height, - xdescent=0., ydescent=descent) - - text_list.append(textbox._text) - # Create the artist for the legend which represents the - # original artist/handle. - handle_list.append(handler.legend_artist(self, orig_handle, - fontsize, handlebox)) - handles_and_labels.append((handlebox, textbox)) - - columnbox = [] - # array_split splits n handles_and_labels into ncols columns, with the - # first n%ncols columns having an extra entry. filter(len, ...) - # handles the case where n < ncols: the last ncols-n columns are empty - # and get filtered out. - for handles_and_labels_column in filter( - len, np.array_split(handles_and_labels, self._ncols)): - # pack handlebox and labelbox into itembox - itemboxes = [HPacker(pad=0, - sep=self.handletextpad * fontsize, - children=[h, t] if markerfirst else [t, h], - align="baseline") - for h, t in handles_and_labels_column] - # pack columnbox - alignment = "baseline" if markerfirst else "right" - columnbox.append(VPacker(pad=0, - sep=self.labelspacing * fontsize, - align=alignment, - children=itemboxes)) - - mode = "expand" if self._mode == "expand" else "fixed" - sep = self.columnspacing * fontsize - self._legend_handle_box = HPacker(pad=0, - sep=sep, align="baseline", - mode=mode, - children=columnbox) - self._legend_title_box = TextArea("") - self._legend_box = VPacker(pad=self.borderpad * fontsize, - sep=self.labelspacing * fontsize, - align=self._alignment, - children=[self._legend_title_box, - self._legend_handle_box]) - self._legend_box.set_figure(self.figure) - self._legend_box.axes = self.axes - self.texts = text_list - self.legend_handles = handle_list - - def _auto_legend_data(self): - """ - Return display coordinates for hit testing for "best" positioning. - - Returns - ------- - bboxes - List of bounding boxes of all patches. - lines - List of `.Path` corresponding to each line. - offsets - List of (x, y) offsets of all collection. - """ - assert self.isaxes # always holds, as this is only called internally - bboxes = [] - lines = [] - offsets = [] - for artist in self.parent._children: - if isinstance(artist, Line2D): - lines.append( - artist.get_transform().transform_path(artist.get_path())) - elif isinstance(artist, Rectangle): - bboxes.append( - artist.get_bbox().transformed(artist.get_data_transform())) - elif isinstance(artist, Patch): - lines.append( - artist.get_transform().transform_path(artist.get_path())) - elif isinstance(artist, Collection): - transform, transOffset, hoffsets, _ = artist._prepare_points() - if len(hoffsets): - for offset in transOffset.transform(hoffsets): - offsets.append(offset) - - return bboxes, lines, offsets - - def get_children(self): - # docstring inherited - return [self._legend_box, self.get_frame()] - - def get_frame(self): - """Return the `~.patches.Rectangle` used to frame the legend.""" - return self.legendPatch - - def get_lines(self): - r"""Return the list of `~.lines.Line2D`\s in the legend.""" - return [h for h in self.legend_handles if isinstance(h, Line2D)] - - def get_patches(self): - r"""Return the list of `~.patches.Patch`\s in the legend.""" - return silent_list('Patch', - [h for h in self.legend_handles - if isinstance(h, Patch)]) - - def get_texts(self): - r"""Return the list of `~.text.Text`\s in the legend.""" - return silent_list('Text', self.texts) - - def set_alignment(self, alignment): - """ - Set the alignment of the legend title and the box of entries. - - The entries are aligned as a single block, so that markers always - lined up. - - Parameters - ---------- - alignment : {'center', 'left', 'right'}. - - """ - _api.check_in_list(["center", "left", "right"], alignment=alignment) - self._alignment = alignment - self._legend_box.align = alignment - - def get_alignment(self): - """Get the alignment value of the legend box""" - return self._legend_box.align - - def set_title(self, title, prop=None): - """ - Set legend title and title style. - - Parameters - ---------- - title : str - The legend title. - - prop : `.font_manager.FontProperties` or `str` or `pathlib.Path` - The font properties of the legend title. - If a `str`, it is interpreted as a fontconfig pattern parsed by - `.FontProperties`. If a `pathlib.Path`, it is interpreted as the - absolute path to a font file. - - """ - self._legend_title_box._text.set_text(title) - if title: - self._legend_title_box._text.set_visible(True) - self._legend_title_box.set_visible(True) - else: - self._legend_title_box._text.set_visible(False) - self._legend_title_box.set_visible(False) - - if prop is not None: - self._legend_title_box._text.set_fontproperties(prop) - - self.stale = True - - def get_title(self): - """Return the `.Text` instance for the legend title.""" - return self._legend_title_box._text - - def get_window_extent(self, renderer=None): - # docstring inherited - if renderer is None: - renderer = self.figure._get_renderer() - return self._legend_box.get_window_extent(renderer=renderer) - - def get_tightbbox(self, renderer=None): - # docstring inherited - return self._legend_box.get_window_extent(renderer) - - def get_frame_on(self): - """Get whether the legend box patch is drawn.""" - return self.legendPatch.get_visible() - - def set_frame_on(self, b): - """ - Set whether the legend box patch is drawn. - - Parameters - ---------- - b : bool - """ - self.legendPatch.set_visible(b) - self.stale = True - - draw_frame = set_frame_on # Backcompat alias. - - def get_bbox_to_anchor(self): - """Return the bbox that the legend will be anchored to.""" - if self._bbox_to_anchor is None: - return self.parent.bbox - else: - return self._bbox_to_anchor - - def set_bbox_to_anchor(self, bbox, transform=None): - """ - Set the bbox that the legend will be anchored to. - - Parameters - ---------- - bbox : `~matplotlib.transforms.BboxBase` or tuple - The bounding box can be specified in the following ways: - - - A `.BboxBase` instance - - A tuple of ``(left, bottom, width, height)`` in the given - transform (normalized axes coordinate if None) - - A tuple of ``(left, bottom)`` where the width and height will be - assumed to be zero. - - *None*, to remove the bbox anchoring, and use the parent bbox. - - transform : `~matplotlib.transforms.Transform`, optional - A transform to apply to the bounding box. If not specified, this - will use a transform to the bounding box of the parent. - """ - if bbox is None: - self._bbox_to_anchor = None - return - elif isinstance(bbox, BboxBase): - self._bbox_to_anchor = bbox - else: - try: - l = len(bbox) - except TypeError as err: - raise ValueError(f"Invalid bbox: {bbox}") from err - - if l == 2: - bbox = [bbox[0], bbox[1], 0, 0] - - self._bbox_to_anchor = Bbox.from_bounds(*bbox) - - if transform is None: - transform = BboxTransformTo(self.parent.bbox) - - self._bbox_to_anchor = TransformedBbox(self._bbox_to_anchor, - transform) - self.stale = True - - def _get_anchored_bbox(self, loc, bbox, parentbbox, renderer): - """ - Place the *bbox* inside the *parentbbox* according to a given - location code. Return the (x, y) coordinate of the bbox. - - Parameters - ---------- - loc : int - A location code in range(1, 11). This corresponds to the possible - values for ``self._loc``, excluding "best". - bbox : `~matplotlib.transforms.Bbox` - bbox to be placed, in display coordinates. - parentbbox : `~matplotlib.transforms.Bbox` - A parent box which will contain the bbox, in display coordinates. - """ - return offsetbox._get_anchored_bbox( - loc, bbox, parentbbox, - self.borderaxespad * renderer.points_to_pixels(self._fontsize)) - - def _find_best_position(self, width, height, renderer, consider=None): - """ - Determine the best location to place the legend. - - *consider* is a list of ``(x, y)`` pairs to consider as a potential - lower-left corner of the legend. All are display coords. - """ - assert self.isaxes # always holds, as this is only called internally - - start_time = time.perf_counter() - - bboxes, lines, offsets = self._auto_legend_data() - - bbox = Bbox.from_bounds(0, 0, width, height) - if consider is None: - consider = [self._get_anchored_bbox(x, bbox, - self.get_bbox_to_anchor(), - renderer) - for x in range(1, len(self.codes))] - - candidates = [] - for idx, (l, b) in enumerate(consider): - legendBox = Bbox.from_bounds(l, b, width, height) - badness = 0 - # XXX TODO: If markers are present, it would be good to take them - # into account when checking vertex overlaps in the next line. - badness = (sum(legendBox.count_contains(line.vertices) - for line in lines) - + legendBox.count_contains(offsets) - + legendBox.count_overlaps(bboxes) - + sum(line.intersects_bbox(legendBox, filled=False) - for line in lines)) - if badness == 0: - return l, b - # Include the index to favor lower codes in case of a tie. - candidates.append((badness, idx, (l, b))) - - _, _, (l, b) = min(candidates) - - if self._loc_used_default and time.perf_counter() - start_time > 1: - _api.warn_external( - 'Creating legend with loc="best" can be slow with large ' - 'amounts of data.') - - return l, b - - def contains(self, event): - inside, info = self._default_contains(event) - if inside is not None: - return inside, info - return self.legendPatch.contains(event) - - def set_draggable(self, state, use_blit=False, update='loc'): - """ - Enable or disable mouse dragging support of the legend. - - Parameters - ---------- - state : bool - Whether mouse dragging is enabled. - use_blit : bool, optional - Use blitting for faster image composition. For details see - :ref:`func-animation`. - update : {'loc', 'bbox'}, optional - The legend parameter to be changed when dragged: - - - 'loc': update the *loc* parameter of the legend - - 'bbox': update the *bbox_to_anchor* parameter of the legend - - Returns - ------- - `.DraggableLegend` or *None* - If *state* is ``True`` this returns the `.DraggableLegend` helper - instance. Otherwise this returns *None*. - """ - if state: - if self._draggable is None: - self._draggable = DraggableLegend(self, - use_blit, - update=update) - else: - if self._draggable is not None: - self._draggable.disconnect() - self._draggable = None - return self._draggable - - def get_draggable(self): - """Return ``True`` if the legend is draggable, ``False`` otherwise.""" - return self._draggable is not None - - -# Helper functions to parse legend arguments for both `figure.legend` and -# `axes.legend`: -def _get_legend_handles(axs, legend_handler_map=None): - """Yield artists that can be used as handles in a legend.""" - handles_original = [] - for ax in axs: - handles_original += [ - *(a for a in ax._children - if isinstance(a, (Line2D, Patch, Collection, Text))), - *ax.containers] - # support parasite axes: - if hasattr(ax, 'parasites'): - for axx in ax.parasites: - handles_original += [ - *(a for a in axx._children - if isinstance(a, (Line2D, Patch, Collection, Text))), - *axx.containers] - - handler_map = {**Legend.get_default_handler_map(), - **(legend_handler_map or {})} - has_handler = Legend.get_legend_handler - for handle in handles_original: - label = handle.get_label() - if label != '_nolegend_' and has_handler(handler_map, handle): - yield handle - elif (label and not label.startswith('_') and - not has_handler(handler_map, handle)): - _api.warn_external( - "Legend does not support handles for {0} " - "instances.\nSee: https://matplotlib.org/stable/" - "tutorials/intermediate/legend_guide.html" - "#implementing-a-custom-legend-handler".format( - type(handle).__name__)) - continue - - -def _get_legend_handles_labels(axs, legend_handler_map=None): - """Return handles and labels for legend.""" - handles = [] - labels = [] - for handle in _get_legend_handles(axs, legend_handler_map): - label = handle.get_label() - if label and not label.startswith('_'): - handles.append(handle) - labels.append(label) - return handles, labels - - -def _parse_legend_args(axs, *args, handles=None, labels=None, **kwargs): - """ - Get the handles and labels from the calls to either ``figure.legend`` - or ``axes.legend``. - - The parser is a bit involved because we support:: - - legend() - legend(labels) - legend(handles, labels) - legend(labels=labels) - legend(handles=handles) - legend(handles=handles, labels=labels) - - The behavior for a mixture of positional and keyword handles and labels - is undefined and issues a warning. - - Parameters - ---------- - axs : list of `.Axes` - If handles are not given explicitly, the artists in these Axes are - used as handles. - *args : tuple - Positional parameters passed to ``legend()``. - handles - The value of the keyword argument ``legend(handles=...)``, or *None* - if that keyword argument was not used. - labels - The value of the keyword argument ``legend(labels=...)``, or *None* - if that keyword argument was not used. - **kwargs - All other keyword arguments passed to ``legend()``. - - Returns - ------- - handles : list of `.Artist` - The legend handles. - labels : list of str - The legend labels. - extra_args : tuple - *args* with positional handles and labels removed. - kwargs : dict - *kwargs* with keywords handles and labels removed. - - """ - log = logging.getLogger(__name__) - - handlers = kwargs.get('handler_map') - extra_args = () - - if (handles is not None or labels is not None) and args: - _api.warn_external("You have mixed positional and keyword arguments, " - "some input may be discarded.") - - # if got both handles and labels as kwargs, make same length - if handles and labels: - handles, labels = zip(*zip(handles, labels)) - - elif handles is not None and labels is None: - labels = [handle.get_label() for handle in handles] - - elif labels is not None and handles is None: - # Get as many handles as there are labels. - handles = [handle for handle, label - in zip(_get_legend_handles(axs, handlers), labels)] - - # No arguments - automatically detect labels and handles. - elif len(args) == 0: - handles, labels = _get_legend_handles_labels(axs, handlers) - if not handles: - log.warning( - "No artists with labels found to put in legend. Note that " - "artists whose label start with an underscore are ignored " - "when legend() is called with no argument.") - - # One argument. User defined labels - automatic handle detection. - elif len(args) == 1: - labels, = args - if any(isinstance(l, Artist) for l in labels): - raise TypeError("A single argument passed to legend() must be a " - "list of labels, but found an Artist in there.") - - # Get as many handles as there are labels. - handles = [handle for handle, label - in zip(_get_legend_handles(axs, handlers), labels)] - - # Two arguments: - # * user defined handles and labels - elif len(args) >= 2: - handles, labels = args[:2] - extra_args = args[2:] - - else: - raise TypeError('Invalid arguments to legend.') - - return handles, labels, extra_args, kwargs diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/legend_handler.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/legend_handler.py deleted file mode 100644 index 95158d9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/legend_handler.py +++ /dev/null @@ -1,817 +0,0 @@ -""" -Default legend handlers. - -.. important:: - - This is a low-level legend API, which most end users do not need. - - We recommend that you are familiar with the :doc:`legend guide - ` before reading this documentation. - -Legend handlers are expected to be a callable object with a following -signature:: - - legend_handler(legend, orig_handle, fontsize, handlebox) - -Where *legend* is the legend itself, *orig_handle* is the original -plot, *fontsize* is the fontsize in pixels, and *handlebox* is an -`.OffsetBox` instance. Within the call, you should create relevant -artists (using relevant properties from the *legend* and/or -*orig_handle*) and add them into the *handlebox*. The artists need to -be scaled according to the *fontsize* (note that the size is in pixels, -i.e., this is dpi-scaled value). - -This module includes definition of several legend handler classes -derived from the base class (HandlerBase) with the following method:: - - def legend_artist(self, legend, orig_handle, fontsize, handlebox) -""" - -from itertools import cycle - -import numpy as np - -from matplotlib import _api, cbook -from matplotlib.lines import Line2D -from matplotlib.patches import Rectangle -import matplotlib.collections as mcoll - - -def update_from_first_child(tgt, src): - first_child = next(iter(src.get_children()), None) - if first_child is not None: - tgt.update_from(first_child) - - -class HandlerBase: - """ - A base class for default legend handlers. - - The derived classes are meant to override *create_artists* method, which - has the following signature:: - - def create_artists(self, legend, orig_handle, - xdescent, ydescent, width, height, fontsize, - trans): - - The overridden method needs to create artists of the given - transform that fits in the given dimension (xdescent, ydescent, - width, height) that are scaled by fontsize if necessary. - - """ - def __init__(self, xpad=0., ypad=0., update_func=None): - """ - Parameters - ---------- - - xpad : float, optional - Padding in x-direction. - ypad : float, optional - Padding in y-direction. - update_func : callable, optional - Function for updating the legend handler properties from another - legend handler, used by `~HandlerBase.update_prop`. - """ - self._xpad, self._ypad = xpad, ypad - self._update_prop_func = update_func - - def _update_prop(self, legend_handle, orig_handle): - if self._update_prop_func is None: - self._default_update_prop(legend_handle, orig_handle) - else: - self._update_prop_func(legend_handle, orig_handle) - - def _default_update_prop(self, legend_handle, orig_handle): - legend_handle.update_from(orig_handle) - - def update_prop(self, legend_handle, orig_handle, legend): - - self._update_prop(legend_handle, orig_handle) - - legend._set_artist_props(legend_handle) - legend_handle.set_clip_box(None) - legend_handle.set_clip_path(None) - - def adjust_drawing_area(self, legend, orig_handle, - xdescent, ydescent, width, height, fontsize, - ): - xdescent = xdescent - self._xpad * fontsize - ydescent = ydescent - self._ypad * fontsize - width = width - self._xpad * fontsize - height = height - self._ypad * fontsize - return xdescent, ydescent, width, height - - def legend_artist(self, legend, orig_handle, - fontsize, handlebox): - """ - Return the artist that this HandlerBase generates for the given - original artist/handle. - - Parameters - ---------- - legend : `~matplotlib.legend.Legend` - The legend for which these legend artists are being created. - orig_handle : :class:`matplotlib.artist.Artist` or similar - The object for which these legend artists are being created. - fontsize : int - The fontsize in pixels. The artists being created should - be scaled according to the given fontsize. - handlebox : `~matplotlib.offsetbox.OffsetBox` - The box which has been created to hold this legend entry's - artists. Artists created in the `legend_artist` method must - be added to this handlebox inside this method. - - """ - xdescent, ydescent, width, height = self.adjust_drawing_area( - legend, orig_handle, - handlebox.xdescent, handlebox.ydescent, - handlebox.width, handlebox.height, - fontsize) - artists = self.create_artists(legend, orig_handle, - xdescent, ydescent, width, height, - fontsize, handlebox.get_transform()) - - # create_artists will return a list of artists. - for a in artists: - handlebox.add_artist(a) - - # we only return the first artist - return artists[0] - - def create_artists(self, legend, orig_handle, - xdescent, ydescent, width, height, fontsize, - trans): - """ - Return the legend artists generated. - - Parameters - ---------- - legend : `~matplotlib.legend.Legend` - The legend for which these legend artists are being created. - orig_handle : `~matplotlib.artist.Artist` or similar - The object for which these legend artists are being created. - xdescent, ydescent, width, height : int - The rectangle (*xdescent*, *ydescent*, *width*, *height*) that the - legend artists being created should fit within. - fontsize : int - The fontsize in pixels. The legend artists being created should - be scaled according to the given fontsize. - trans : `~matplotlib.transforms.Transform` - The transform that is applied to the legend artists being created. - Typically from unit coordinates in the handler box to screen - coordinates. - """ - raise NotImplementedError('Derived must override') - - -class HandlerNpoints(HandlerBase): - """ - A legend handler that shows *numpoints* points in the legend entry. - """ - - def __init__(self, marker_pad=0.3, numpoints=None, **kwargs): - """ - Parameters - ---------- - marker_pad : float - Padding between points in legend entry. - numpoints : int - Number of points to show in legend entry. - **kwargs - Keyword arguments forwarded to `.HandlerBase`. - """ - super().__init__(**kwargs) - - self._numpoints = numpoints - self._marker_pad = marker_pad - - def get_numpoints(self, legend): - if self._numpoints is None: - return legend.numpoints - else: - return self._numpoints - - def get_xdata(self, legend, xdescent, ydescent, width, height, fontsize): - numpoints = self.get_numpoints(legend) - if numpoints > 1: - # we put some pad here to compensate the size of the marker - pad = self._marker_pad * fontsize - xdata = np.linspace(-xdescent + pad, - -xdescent + width - pad, - numpoints) - xdata_marker = xdata - else: - xdata = [-xdescent, -xdescent + width] - xdata_marker = [-xdescent + 0.5 * width] - return xdata, xdata_marker - - -class HandlerNpointsYoffsets(HandlerNpoints): - """ - A legend handler that shows *numpoints* in the legend, and allows them to - be individually offset in the y-direction. - """ - - def __init__(self, numpoints=None, yoffsets=None, **kwargs): - """ - Parameters - ---------- - numpoints : int - Number of points to show in legend entry. - yoffsets : array of floats - Length *numpoints* list of y offsets for each point in - legend entry. - **kwargs - Keyword arguments forwarded to `.HandlerNpoints`. - """ - super().__init__(numpoints=numpoints, **kwargs) - self._yoffsets = yoffsets - - def get_ydata(self, legend, xdescent, ydescent, width, height, fontsize): - if self._yoffsets is None: - ydata = height * legend._scatteryoffsets - else: - ydata = height * np.asarray(self._yoffsets) - - return ydata - - -class HandlerLine2DCompound(HandlerNpoints): - """ - Original handler for `.Line2D` instances, that relies on combining - a line-only with a marker-only artist. May be deprecated in the future. - """ - - def create_artists(self, legend, orig_handle, - xdescent, ydescent, width, height, fontsize, - trans): - # docstring inherited - xdata, xdata_marker = self.get_xdata(legend, xdescent, ydescent, - width, height, fontsize) - - ydata = np.full_like(xdata, ((height - ydescent) / 2)) - legline = Line2D(xdata, ydata) - - self.update_prop(legline, orig_handle, legend) - legline.set_drawstyle('default') - legline.set_marker("") - - legline_marker = Line2D(xdata_marker, ydata[:len(xdata_marker)]) - self.update_prop(legline_marker, orig_handle, legend) - legline_marker.set_linestyle('None') - if legend.markerscale != 1: - newsz = legline_marker.get_markersize() * legend.markerscale - legline_marker.set_markersize(newsz) - # we don't want to add this to the return list because - # the texts and handles are assumed to be in one-to-one - # correspondence. - legline._legmarker = legline_marker - - legline.set_transform(trans) - legline_marker.set_transform(trans) - - return [legline, legline_marker] - - -class HandlerLine2D(HandlerNpoints): - """ - Handler for `.Line2D` instances. - - See Also - -------- - HandlerLine2DCompound : An earlier handler implementation, which used one - artist for the line and another for the marker(s). - """ - - def create_artists(self, legend, orig_handle, - xdescent, ydescent, width, height, fontsize, - trans): - # docstring inherited - xdata, xdata_marker = self.get_xdata(legend, xdescent, ydescent, - width, height, fontsize) - - markevery = None - if self.get_numpoints(legend) == 1: - # Special case: one wants a single marker in the center - # and a line that extends on both sides. One will use a - # 3 points line, but only mark the #1 (i.e. middle) point. - xdata = np.linspace(xdata[0], xdata[-1], 3) - markevery = [1] - - ydata = np.full_like(xdata, (height - ydescent) / 2) - legline = Line2D(xdata, ydata, markevery=markevery) - - self.update_prop(legline, orig_handle, legend) - - if legend.markerscale != 1: - newsz = legline.get_markersize() * legend.markerscale - legline.set_markersize(newsz) - - legline.set_transform(trans) - - return [legline] - - -class HandlerPatch(HandlerBase): - """ - Handler for `.Patch` instances. - """ - - def __init__(self, patch_func=None, **kwargs): - """ - Parameters - ---------- - patch_func : callable, optional - The function that creates the legend key artist. - *patch_func* should have the signature:: - - def patch_func(legend=legend, orig_handle=orig_handle, - xdescent=xdescent, ydescent=ydescent, - width=width, height=height, fontsize=fontsize) - - Subsequently, the created artist will have its ``update_prop`` - method called and the appropriate transform will be applied. - - **kwargs - Keyword arguments forwarded to `.HandlerBase`. - """ - super().__init__(**kwargs) - self._patch_func = patch_func - - def _create_patch(self, legend, orig_handle, - xdescent, ydescent, width, height, fontsize): - if self._patch_func is None: - p = Rectangle(xy=(-xdescent, -ydescent), - width=width, height=height) - else: - p = self._patch_func(legend=legend, orig_handle=orig_handle, - xdescent=xdescent, ydescent=ydescent, - width=width, height=height, fontsize=fontsize) - return p - - def create_artists(self, legend, orig_handle, - xdescent, ydescent, width, height, fontsize, trans): - # docstring inherited - p = self._create_patch(legend, orig_handle, - xdescent, ydescent, width, height, fontsize) - self.update_prop(p, orig_handle, legend) - p.set_transform(trans) - return [p] - - -class HandlerStepPatch(HandlerBase): - """ - Handler for `~.matplotlib.patches.StepPatch` instances. - """ - - @staticmethod - def _create_patch(orig_handle, xdescent, ydescent, width, height): - return Rectangle(xy=(-xdescent, -ydescent), width=width, - height=height, color=orig_handle.get_facecolor()) - - @staticmethod - def _create_line(orig_handle, width, height): - # Unfilled StepPatch should show as a line - legline = Line2D([0, width], [height/2, height/2], - color=orig_handle.get_edgecolor(), - linestyle=orig_handle.get_linestyle(), - linewidth=orig_handle.get_linewidth(), - ) - - # Overwrite manually because patch and line properties don't mix - legline.set_drawstyle('default') - legline.set_marker("") - return legline - - def create_artists(self, legend, orig_handle, - xdescent, ydescent, width, height, fontsize, trans): - # docstring inherited - if orig_handle.get_fill() or (orig_handle.get_hatch() is not None): - p = self._create_patch(orig_handle, xdescent, ydescent, width, - height) - self.update_prop(p, orig_handle, legend) - else: - p = self._create_line(orig_handle, width, height) - p.set_transform(trans) - return [p] - - -class HandlerLineCollection(HandlerLine2D): - """ - Handler for `.LineCollection` instances. - """ - def get_numpoints(self, legend): - if self._numpoints is None: - return legend.scatterpoints - else: - return self._numpoints - - def _default_update_prop(self, legend_handle, orig_handle): - lw = orig_handle.get_linewidths()[0] - dashes = orig_handle._us_linestyles[0] - color = orig_handle.get_colors()[0] - legend_handle.set_color(color) - legend_handle.set_linestyle(dashes) - legend_handle.set_linewidth(lw) - - def create_artists(self, legend, orig_handle, - xdescent, ydescent, width, height, fontsize, trans): - # docstring inherited - xdata, xdata_marker = self.get_xdata(legend, xdescent, ydescent, - width, height, fontsize) - ydata = np.full_like(xdata, (height - ydescent) / 2) - legline = Line2D(xdata, ydata) - - self.update_prop(legline, orig_handle, legend) - legline.set_transform(trans) - - return [legline] - - -class HandlerRegularPolyCollection(HandlerNpointsYoffsets): - r"""Handler for `.RegularPolyCollection`\s.""" - - def __init__(self, yoffsets=None, sizes=None, **kwargs): - super().__init__(yoffsets=yoffsets, **kwargs) - - self._sizes = sizes - - def get_numpoints(self, legend): - if self._numpoints is None: - return legend.scatterpoints - else: - return self._numpoints - - def get_sizes(self, legend, orig_handle, - xdescent, ydescent, width, height, fontsize): - if self._sizes is None: - handle_sizes = orig_handle.get_sizes() - if not len(handle_sizes): - handle_sizes = [1] - size_max = max(handle_sizes) * legend.markerscale ** 2 - size_min = min(handle_sizes) * legend.markerscale ** 2 - - numpoints = self.get_numpoints(legend) - if numpoints < 4: - sizes = [.5 * (size_max + size_min), size_max, - size_min][:numpoints] - else: - rng = (size_max - size_min) - sizes = rng * np.linspace(0, 1, numpoints) + size_min - else: - sizes = self._sizes - - return sizes - - def update_prop(self, legend_handle, orig_handle, legend): - - self._update_prop(legend_handle, orig_handle) - - legend_handle.set_figure(legend.figure) - # legend._set_artist_props(legend_handle) - legend_handle.set_clip_box(None) - legend_handle.set_clip_path(None) - - @_api.rename_parameter("3.6", "transOffset", "offset_transform") - def create_collection(self, orig_handle, sizes, offsets, offset_transform): - return type(orig_handle)( - orig_handle.get_numsides(), - rotation=orig_handle.get_rotation(), sizes=sizes, - offsets=offsets, offset_transform=offset_transform, - ) - - def create_artists(self, legend, orig_handle, - xdescent, ydescent, width, height, fontsize, - trans): - # docstring inherited - xdata, xdata_marker = self.get_xdata(legend, xdescent, ydescent, - width, height, fontsize) - - ydata = self.get_ydata(legend, xdescent, ydescent, - width, height, fontsize) - - sizes = self.get_sizes(legend, orig_handle, xdescent, ydescent, - width, height, fontsize) - - p = self.create_collection( - orig_handle, sizes, - offsets=list(zip(xdata_marker, ydata)), offset_transform=trans) - - self.update_prop(p, orig_handle, legend) - p.set_offset_transform(trans) - return [p] - - -class HandlerPathCollection(HandlerRegularPolyCollection): - r"""Handler for `.PathCollection`\s, which are used by `~.Axes.scatter`.""" - - @_api.rename_parameter("3.6", "transOffset", "offset_transform") - def create_collection(self, orig_handle, sizes, offsets, offset_transform): - return type(orig_handle)( - [orig_handle.get_paths()[0]], sizes=sizes, - offsets=offsets, offset_transform=offset_transform, - ) - - -class HandlerCircleCollection(HandlerRegularPolyCollection): - r"""Handler for `.CircleCollection`\s.""" - - @_api.rename_parameter("3.6", "transOffset", "offset_transform") - def create_collection(self, orig_handle, sizes, offsets, offset_transform): - return type(orig_handle)( - sizes, offsets=offsets, offset_transform=offset_transform) - - -class HandlerErrorbar(HandlerLine2D): - """Handler for Errorbars.""" - - def __init__(self, xerr_size=0.5, yerr_size=None, - marker_pad=0.3, numpoints=None, **kwargs): - - self._xerr_size = xerr_size - self._yerr_size = yerr_size - - super().__init__(marker_pad=marker_pad, numpoints=numpoints, **kwargs) - - def get_err_size(self, legend, xdescent, ydescent, - width, height, fontsize): - xerr_size = self._xerr_size * fontsize - - if self._yerr_size is None: - yerr_size = xerr_size - else: - yerr_size = self._yerr_size * fontsize - - return xerr_size, yerr_size - - def create_artists(self, legend, orig_handle, - xdescent, ydescent, width, height, fontsize, - trans): - # docstring inherited - plotlines, caplines, barlinecols = orig_handle - - xdata, xdata_marker = self.get_xdata(legend, xdescent, ydescent, - width, height, fontsize) - - ydata = np.full_like(xdata, (height - ydescent) / 2) - legline = Line2D(xdata, ydata) - - xdata_marker = np.asarray(xdata_marker) - ydata_marker = np.asarray(ydata[:len(xdata_marker)]) - - xerr_size, yerr_size = self.get_err_size(legend, xdescent, ydescent, - width, height, fontsize) - - legline_marker = Line2D(xdata_marker, ydata_marker) - - # when plotlines are None (only errorbars are drawn), we just - # make legline invisible. - if plotlines is None: - legline.set_visible(False) - legline_marker.set_visible(False) - else: - self.update_prop(legline, plotlines, legend) - - legline.set_drawstyle('default') - legline.set_marker('none') - - self.update_prop(legline_marker, plotlines, legend) - legline_marker.set_linestyle('None') - - if legend.markerscale != 1: - newsz = legline_marker.get_markersize() * legend.markerscale - legline_marker.set_markersize(newsz) - - handle_barlinecols = [] - handle_caplines = [] - - if orig_handle.has_xerr: - verts = [((x - xerr_size, y), (x + xerr_size, y)) - for x, y in zip(xdata_marker, ydata_marker)] - coll = mcoll.LineCollection(verts) - self.update_prop(coll, barlinecols[0], legend) - handle_barlinecols.append(coll) - - if caplines: - capline_left = Line2D(xdata_marker - xerr_size, ydata_marker) - capline_right = Line2D(xdata_marker + xerr_size, ydata_marker) - self.update_prop(capline_left, caplines[0], legend) - self.update_prop(capline_right, caplines[0], legend) - capline_left.set_marker("|") - capline_right.set_marker("|") - - handle_caplines.append(capline_left) - handle_caplines.append(capline_right) - - if orig_handle.has_yerr: - verts = [((x, y - yerr_size), (x, y + yerr_size)) - for x, y in zip(xdata_marker, ydata_marker)] - coll = mcoll.LineCollection(verts) - self.update_prop(coll, barlinecols[0], legend) - handle_barlinecols.append(coll) - - if caplines: - capline_left = Line2D(xdata_marker, ydata_marker - yerr_size) - capline_right = Line2D(xdata_marker, ydata_marker + yerr_size) - self.update_prop(capline_left, caplines[0], legend) - self.update_prop(capline_right, caplines[0], legend) - capline_left.set_marker("_") - capline_right.set_marker("_") - - handle_caplines.append(capline_left) - handle_caplines.append(capline_right) - - artists = [ - *handle_barlinecols, *handle_caplines, legline, legline_marker, - ] - for artist in artists: - artist.set_transform(trans) - return artists - - -class HandlerStem(HandlerNpointsYoffsets): - """ - Handler for plots produced by `~.Axes.stem`. - """ - - def __init__(self, marker_pad=0.3, numpoints=None, - bottom=None, yoffsets=None, **kwargs): - """ - Parameters - ---------- - marker_pad : float, default: 0.3 - Padding between points in legend entry. - numpoints : int, optional - Number of points to show in legend entry. - bottom : float, optional - - yoffsets : array of floats, optional - Length *numpoints* list of y offsets for each point in - legend entry. - **kwargs - Keyword arguments forwarded to `.HandlerNpointsYoffsets`. - """ - super().__init__(marker_pad=marker_pad, numpoints=numpoints, - yoffsets=yoffsets, **kwargs) - self._bottom = bottom - - def get_ydata(self, legend, xdescent, ydescent, width, height, fontsize): - if self._yoffsets is None: - ydata = height * (0.5 * legend._scatteryoffsets + 0.5) - else: - ydata = height * np.asarray(self._yoffsets) - - return ydata - - def create_artists(self, legend, orig_handle, - xdescent, ydescent, width, height, fontsize, - trans): - # docstring inherited - markerline, stemlines, baseline = orig_handle - # Check to see if the stemcontainer is storing lines as a list or a - # LineCollection. Eventually using a list will be removed, and this - # logic can also be removed. - using_linecoll = isinstance(stemlines, mcoll.LineCollection) - - xdata, xdata_marker = self.get_xdata(legend, xdescent, ydescent, - width, height, fontsize) - - ydata = self.get_ydata(legend, xdescent, ydescent, - width, height, fontsize) - - if self._bottom is None: - bottom = 0. - else: - bottom = self._bottom - - leg_markerline = Line2D(xdata_marker, ydata[:len(xdata_marker)]) - self.update_prop(leg_markerline, markerline, legend) - - leg_stemlines = [Line2D([x, x], [bottom, y]) - for x, y in zip(xdata_marker, ydata)] - - if using_linecoll: - # change the function used by update_prop() from the default - # to one that handles LineCollection - with cbook._setattr_cm( - self, _update_prop_func=self._copy_collection_props): - for line in leg_stemlines: - self.update_prop(line, stemlines, legend) - - else: - for lm, m in zip(leg_stemlines, stemlines): - self.update_prop(lm, m, legend) - - leg_baseline = Line2D([np.min(xdata), np.max(xdata)], - [bottom, bottom]) - self.update_prop(leg_baseline, baseline, legend) - - artists = [*leg_stemlines, leg_baseline, leg_markerline] - for artist in artists: - artist.set_transform(trans) - return artists - - def _copy_collection_props(self, legend_handle, orig_handle): - """ - Copy properties from the `.LineCollection` *orig_handle* to the - `.Line2D` *legend_handle*. - """ - legend_handle.set_color(orig_handle.get_color()[0]) - legend_handle.set_linestyle(orig_handle.get_linestyle()[0]) - - -class HandlerTuple(HandlerBase): - """ - Handler for Tuple. - """ - - def __init__(self, ndivide=1, pad=None, **kwargs): - """ - Parameters - ---------- - ndivide : int, default: 1 - The number of sections to divide the legend area into. If None, - use the length of the input tuple. - pad : float, default: :rc:`legend.borderpad` - Padding in units of fraction of font size. - **kwargs - Keyword arguments forwarded to `.HandlerBase`. - """ - self._ndivide = ndivide - self._pad = pad - super().__init__(**kwargs) - - def create_artists(self, legend, orig_handle, - xdescent, ydescent, width, height, fontsize, - trans): - # docstring inherited - handler_map = legend.get_legend_handler_map() - - if self._ndivide is None: - ndivide = len(orig_handle) - else: - ndivide = self._ndivide - - if self._pad is None: - pad = legend.borderpad * fontsize - else: - pad = self._pad * fontsize - - if ndivide > 1: - width = (width - pad * (ndivide - 1)) / ndivide - - xds_cycle = cycle(xdescent - (width + pad) * np.arange(ndivide)) - - a_list = [] - for handle1 in orig_handle: - handler = legend.get_legend_handler(handler_map, handle1) - _a_list = handler.create_artists( - legend, handle1, - next(xds_cycle), ydescent, width, height, fontsize, trans) - a_list.extend(_a_list) - - return a_list - - -class HandlerPolyCollection(HandlerBase): - """ - Handler for `.PolyCollection` used in `~.Axes.fill_between` and - `~.Axes.stackplot`. - """ - def _update_prop(self, legend_handle, orig_handle): - def first_color(colors): - if colors.size == 0: - return (0, 0, 0, 0) - return tuple(colors[0]) - - def get_first(prop_array): - if len(prop_array): - return prop_array[0] - else: - return None - - # orig_handle is a PolyCollection and legend_handle is a Patch. - # Directly set Patch color attributes (must be RGBA tuples). - legend_handle._facecolor = first_color(orig_handle.get_facecolor()) - legend_handle._edgecolor = first_color(orig_handle.get_edgecolor()) - legend_handle._original_facecolor = orig_handle._original_facecolor - legend_handle._original_edgecolor = orig_handle._original_edgecolor - legend_handle._fill = orig_handle.get_fill() - legend_handle._hatch = orig_handle.get_hatch() - # Hatch color is anomalous in having no getters and setters. - legend_handle._hatch_color = orig_handle._hatch_color - # Setters are fine for the remaining attributes. - legend_handle.set_linewidth(get_first(orig_handle.get_linewidths())) - legend_handle.set_linestyle(get_first(orig_handle.get_linestyles())) - legend_handle.set_transform(get_first(orig_handle.get_transforms())) - legend_handle.set_figure(orig_handle.get_figure()) - # Alpha is already taken into account by the color attributes. - - def create_artists(self, legend, orig_handle, - xdescent, ydescent, width, height, fontsize, trans): - # docstring inherited - p = Rectangle(xy=(-xdescent, -ydescent), - width=width, height=height) - self.update_prop(p, orig_handle, legend) - p.set_transform(trans) - return [p] diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/lines.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/lines.py deleted file mode 100644 index 129970b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/lines.py +++ /dev/null @@ -1,1605 +0,0 @@ -""" -2D lines with support for a variety of line styles, markers, colors, etc. -""" - -import copy - -from numbers import Integral, Number, Real -import logging - -import numpy as np - -import matplotlib as mpl -from . import _api, cbook, colors as mcolors, _docstring -from .artist import Artist, allow_rasterization -from .cbook import ( - _to_unmasked_float_array, ls_mapper, ls_mapper_r, STEP_LOOKUP_MAP) -from .markers import MarkerStyle -from .path import Path -from .transforms import Bbox, BboxTransformTo, TransformedPath -from ._enums import JoinStyle, CapStyle - -# Imported here for backward compatibility, even though they don't -# really belong. -from . import _path -from .markers import ( # noqa - CARETLEFT, CARETRIGHT, CARETUP, CARETDOWN, - CARETLEFTBASE, CARETRIGHTBASE, CARETUPBASE, CARETDOWNBASE, - TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN) - -_log = logging.getLogger(__name__) - - -def _get_dash_pattern(style): - """Convert linestyle to dash pattern.""" - # go from short hand -> full strings - if isinstance(style, str): - style = ls_mapper.get(style, style) - # un-dashed styles - if style in ['solid', 'None']: - offset = 0 - dashes = None - # dashed styles - elif style in ['dashed', 'dashdot', 'dotted']: - offset = 0 - dashes = tuple(mpl.rcParams['lines.{}_pattern'.format(style)]) - # - elif isinstance(style, tuple): - offset, dashes = style - if offset is None: - raise ValueError(f'Unrecognized linestyle: {style!r}') - else: - raise ValueError(f'Unrecognized linestyle: {style!r}') - - # normalize offset to be positive and shorter than the dash cycle - if dashes is not None: - dsum = sum(dashes) - if dsum: - offset %= dsum - - return offset, dashes - - -def _scale_dashes(offset, dashes, lw): - if not mpl.rcParams['lines.scale_dashes']: - return offset, dashes - scaled_offset = offset * lw - scaled_dashes = ([x * lw if x is not None else None for x in dashes] - if dashes is not None else None) - return scaled_offset, scaled_dashes - - -def segment_hits(cx, cy, x, y, radius): - """ - Return the indices of the segments in the polyline with coordinates (*cx*, - *cy*) that are within a distance *radius* of the point (*x*, *y*). - """ - # Process single points specially - if len(x) <= 1: - res, = np.nonzero((cx - x) ** 2 + (cy - y) ** 2 <= radius ** 2) - return res - - # We need to lop the last element off a lot. - xr, yr = x[:-1], y[:-1] - - # Only look at line segments whose nearest point to C on the line - # lies within the segment. - dx, dy = x[1:] - xr, y[1:] - yr - Lnorm_sq = dx ** 2 + dy ** 2 # Possibly want to eliminate Lnorm==0 - u = ((cx - xr) * dx + (cy - yr) * dy) / Lnorm_sq - candidates = (u >= 0) & (u <= 1) - - # Note that there is a little area near one side of each point - # which will be near neither segment, and another which will - # be near both, depending on the angle of the lines. The - # following radius test eliminates these ambiguities. - point_hits = (cx - x) ** 2 + (cy - y) ** 2 <= radius ** 2 - candidates = candidates & ~(point_hits[:-1] | point_hits[1:]) - - # For those candidates which remain, determine how far they lie away - # from the line. - px, py = xr + u * dx, yr + u * dy - line_hits = (cx - px) ** 2 + (cy - py) ** 2 <= radius ** 2 - line_hits = line_hits & candidates - points, = point_hits.ravel().nonzero() - lines, = line_hits.ravel().nonzero() - return np.concatenate((points, lines)) - - -def _mark_every_path(markevery, tpath, affine, ax): - """ - Helper function that sorts out how to deal the input - `markevery` and returns the points where markers should be drawn. - - Takes in the `markevery` value and the line path and returns the - sub-sampled path. - """ - # pull out the two bits of data we want from the path - codes, verts = tpath.codes, tpath.vertices - - def _slice_or_none(in_v, slc): - """Helper function to cope with `codes` being an ndarray or `None`.""" - if in_v is None: - return None - return in_v[slc] - - # if just an int, assume starting at 0 and make a tuple - if isinstance(markevery, Integral): - markevery = (0, markevery) - # if just a float, assume starting at 0.0 and make a tuple - elif isinstance(markevery, Real): - markevery = (0.0, markevery) - - if isinstance(markevery, tuple): - if len(markevery) != 2: - raise ValueError('`markevery` is a tuple but its len is not 2; ' - 'markevery={}'.format(markevery)) - start, step = markevery - # if step is an int, old behavior - if isinstance(step, Integral): - # tuple of 2 int is for backwards compatibility, - if not isinstance(start, Integral): - raise ValueError( - '`markevery` is a tuple with len 2 and second element is ' - 'an int, but the first element is not an int; markevery={}' - .format(markevery)) - # just return, we are done here - - return Path(verts[slice(start, None, step)], - _slice_or_none(codes, slice(start, None, step))) - - elif isinstance(step, Real): - if not isinstance(start, Real): - raise ValueError( - '`markevery` is a tuple with len 2 and second element is ' - 'a float, but the first element is not a float or an int; ' - 'markevery={}'.format(markevery)) - if ax is None: - raise ValueError( - "markevery is specified relative to the axes size, but " - "the line does not have a Axes as parent") - - # calc cumulative distance along path (in display coords): - fin = np.isfinite(verts).all(axis=1) - fverts = verts[fin] - disp_coords = affine.transform(fverts) - - delta = np.empty((len(disp_coords), 2)) - delta[0, :] = 0 - delta[1:, :] = disp_coords[1:, :] - disp_coords[:-1, :] - delta = np.hypot(*delta.T).cumsum() - # calc distance between markers along path based on the axes - # bounding box diagonal being a distance of unity: - (x0, y0), (x1, y1) = ax.transAxes.transform([[0, 0], [1, 1]]) - scale = np.hypot(x1 - x0, y1 - y0) - marker_delta = np.arange(start * scale, delta[-1], step * scale) - # find closest actual data point that is closest to - # the theoretical distance along the path: - inds = np.abs(delta[np.newaxis, :] - marker_delta[:, np.newaxis]) - inds = inds.argmin(axis=1) - inds = np.unique(inds) - # return, we are done here - return Path(fverts[inds], _slice_or_none(codes, inds)) - else: - raise ValueError( - f"markevery={markevery!r} is a tuple with len 2, but its " - f"second element is not an int or a float") - - elif isinstance(markevery, slice): - # mazol tov, it's already a slice, just return - return Path(verts[markevery], _slice_or_none(codes, markevery)) - - elif np.iterable(markevery): - # fancy indexing - try: - return Path(verts[markevery], _slice_or_none(codes, markevery)) - except (ValueError, IndexError) as err: - raise ValueError( - f"markevery={markevery!r} is iterable but not a valid numpy " - f"fancy index") from err - else: - raise ValueError(f"markevery={markevery!r} is not a recognized value") - - -@_docstring.interpd -@_api.define_aliases({ - "antialiased": ["aa"], - "color": ["c"], - "drawstyle": ["ds"], - "linestyle": ["ls"], - "linewidth": ["lw"], - "markeredgecolor": ["mec"], - "markeredgewidth": ["mew"], - "markerfacecolor": ["mfc"], - "markerfacecoloralt": ["mfcalt"], - "markersize": ["ms"], -}) -class Line2D(Artist): - """ - A line - the line can have both a solid linestyle connecting all - the vertices, and a marker at each vertex. Additionally, the - drawing of the solid line is influenced by the drawstyle, e.g., one - can create "stepped" lines in various styles. - """ - - lineStyles = _lineStyles = { # hidden names deprecated - '-': '_draw_solid', - '--': '_draw_dashed', - '-.': '_draw_dash_dot', - ':': '_draw_dotted', - 'None': '_draw_nothing', - ' ': '_draw_nothing', - '': '_draw_nothing', - } - - _drawStyles_l = { - 'default': '_draw_lines', - 'steps-mid': '_draw_steps_mid', - 'steps-pre': '_draw_steps_pre', - 'steps-post': '_draw_steps_post', - } - - _drawStyles_s = { - 'steps': '_draw_steps_pre', - } - - # drawStyles should now be deprecated. - drawStyles = {**_drawStyles_l, **_drawStyles_s} - # Need a list ordered with long names first: - drawStyleKeys = [*_drawStyles_l, *_drawStyles_s] - - # Referenced here to maintain API. These are defined in - # MarkerStyle - markers = MarkerStyle.markers - filled_markers = MarkerStyle.filled_markers - fillStyles = MarkerStyle.fillstyles - - zorder = 2 - - _subslice_optim_min_size = 1000 - - def __str__(self): - if self._label != "": - return f"Line2D({self._label})" - elif self._x is None: - return "Line2D()" - elif len(self._x) > 3: - return "Line2D((%g,%g),(%g,%g),...,(%g,%g))" % ( - self._x[0], self._y[0], self._x[0], - self._y[0], self._x[-1], self._y[-1]) - else: - return "Line2D(%s)" % ",".join( - map("({:g},{:g})".format, self._x, self._y)) - - @_api.make_keyword_only("3.6", name="linewidth") - def __init__(self, xdata, ydata, - linewidth=None, # all Nones default to rc - linestyle=None, - color=None, - gapcolor=None, - marker=None, - markersize=None, - markeredgewidth=None, - markeredgecolor=None, - markerfacecolor=None, - markerfacecoloralt='none', - fillstyle=None, - antialiased=None, - dash_capstyle=None, - solid_capstyle=None, - dash_joinstyle=None, - solid_joinstyle=None, - pickradius=5, - drawstyle=None, - markevery=None, - **kwargs - ): - """ - Create a `.Line2D` instance with *x* and *y* data in sequences of - *xdata*, *ydata*. - - Additional keyword arguments are `.Line2D` properties: - - %(Line2D:kwdoc)s - - See :meth:`set_linestyle` for a description of the line styles, - :meth:`set_marker` for a description of the markers, and - :meth:`set_drawstyle` for a description of the draw styles. - - """ - super().__init__() - - # Convert sequences to NumPy arrays. - if not np.iterable(xdata): - raise RuntimeError('xdata must be a sequence') - if not np.iterable(ydata): - raise RuntimeError('ydata must be a sequence') - - if linewidth is None: - linewidth = mpl.rcParams['lines.linewidth'] - - if linestyle is None: - linestyle = mpl.rcParams['lines.linestyle'] - if marker is None: - marker = mpl.rcParams['lines.marker'] - if color is None: - color = mpl.rcParams['lines.color'] - - if markersize is None: - markersize = mpl.rcParams['lines.markersize'] - if antialiased is None: - antialiased = mpl.rcParams['lines.antialiased'] - if dash_capstyle is None: - dash_capstyle = mpl.rcParams['lines.dash_capstyle'] - if dash_joinstyle is None: - dash_joinstyle = mpl.rcParams['lines.dash_joinstyle'] - if solid_capstyle is None: - solid_capstyle = mpl.rcParams['lines.solid_capstyle'] - if solid_joinstyle is None: - solid_joinstyle = mpl.rcParams['lines.solid_joinstyle'] - - if drawstyle is None: - drawstyle = 'default' - - self._dashcapstyle = None - self._dashjoinstyle = None - self._solidjoinstyle = None - self._solidcapstyle = None - self.set_dash_capstyle(dash_capstyle) - self.set_dash_joinstyle(dash_joinstyle) - self.set_solid_capstyle(solid_capstyle) - self.set_solid_joinstyle(solid_joinstyle) - - self._linestyles = None - self._drawstyle = None - self._linewidth = linewidth - self._unscaled_dash_pattern = (0, None) # offset, dash - self._dash_pattern = (0, None) # offset, dash (scaled by linewidth) - - self.set_linewidth(linewidth) - self.set_linestyle(linestyle) - self.set_drawstyle(drawstyle) - - self._color = None - self.set_color(color) - if marker is None: - marker = 'none' # Default. - if not isinstance(marker, MarkerStyle): - self._marker = MarkerStyle(marker, fillstyle) - else: - self._marker = marker - - self._gapcolor = None - self.set_gapcolor(gapcolor) - - self._markevery = None - self._markersize = None - self._antialiased = None - - self.set_markevery(markevery) - self.set_antialiased(antialiased) - self.set_markersize(markersize) - - self._markeredgecolor = None - self._markeredgewidth = None - self._markerfacecolor = None - self._markerfacecoloralt = None - - self.set_markerfacecolor(markerfacecolor) # Normalizes None to rc. - self.set_markerfacecoloralt(markerfacecoloralt) - self.set_markeredgecolor(markeredgecolor) # Normalizes None to rc. - self.set_markeredgewidth(markeredgewidth) - - # update kwargs before updating data to give the caller a - # chance to init axes (and hence unit support) - self._internal_update(kwargs) - self._pickradius = pickradius - self.ind_offset = 0 - if (isinstance(self._picker, Number) and - not isinstance(self._picker, bool)): - self._pickradius = self._picker - - self._xorig = np.asarray([]) - self._yorig = np.asarray([]) - self._invalidx = True - self._invalidy = True - self._x = None - self._y = None - self._xy = None - self._path = None - self._transformed_path = None - self._subslice = False - self._x_filled = None # used in subslicing; only x is needed - - self.set_data(xdata, ydata) - - def contains(self, mouseevent): - """ - Test whether *mouseevent* occurred on the line. - - An event is deemed to have occurred "on" the line if it is less - than ``self.pickradius`` (default: 5 points) away from it. Use - `~.Line2D.get_pickradius` or `~.Line2D.set_pickradius` to get or set - the pick radius. - - Parameters - ---------- - mouseevent : `~matplotlib.backend_bases.MouseEvent` - - Returns - ------- - contains : bool - Whether any values are within the radius. - details : dict - A dictionary ``{'ind': pointlist}``, where *pointlist* is a - list of points of the line that are within the pickradius around - the event position. - - TODO: sort returned indices by distance - """ - inside, info = self._default_contains(mouseevent) - if inside is not None: - return inside, info - - # Make sure we have data to plot - if self._invalidy or self._invalidx: - self.recache() - if len(self._xy) == 0: - return False, {} - - # Convert points to pixels - transformed_path = self._get_transformed_path() - path, affine = transformed_path.get_transformed_path_and_affine() - path = affine.transform_path(path) - xy = path.vertices - xt = xy[:, 0] - yt = xy[:, 1] - - # Convert pick radius from points to pixels - if self.figure is None: - _log.warning('no figure set when check if mouse is on line') - pixels = self._pickradius - else: - pixels = self.figure.dpi / 72. * self._pickradius - - # The math involved in checking for containment (here and inside of - # segment_hits) assumes that it is OK to overflow, so temporarily set - # the error flags accordingly. - with np.errstate(all='ignore'): - # Check for collision - if self._linestyle in ['None', None]: - # If no line, return the nearby point(s) - ind, = np.nonzero( - (xt - mouseevent.x) ** 2 + (yt - mouseevent.y) ** 2 - <= pixels ** 2) - else: - # If line, return the nearby segment(s) - ind = segment_hits(mouseevent.x, mouseevent.y, xt, yt, pixels) - if self._drawstyle.startswith("steps"): - ind //= 2 - - ind += self.ind_offset - - # Return the point(s) within radius - return len(ind) > 0, dict(ind=ind) - - def get_pickradius(self): - """ - Return the pick radius used for containment tests. - - See `.contains` for more details. - """ - return self._pickradius - - @_api.rename_parameter("3.6", "d", "pickradius") - def set_pickradius(self, pickradius): - """ - Set the pick radius used for containment tests. - - See `.contains` for more details. - - Parameters - ---------- - pickradius : float - Pick radius, in points. - """ - if not isinstance(pickradius, Number) or pickradius < 0: - raise ValueError("pick radius should be a distance") - self._pickradius = pickradius - - pickradius = property(get_pickradius, set_pickradius) - - def get_fillstyle(self): - """ - Return the marker fill style. - - See also `~.Line2D.set_fillstyle`. - """ - return self._marker.get_fillstyle() - - def set_fillstyle(self, fs): - """ - Set the marker fill style. - - Parameters - ---------- - fs : {'full', 'left', 'right', 'bottom', 'top', 'none'} - Possible values: - - - 'full': Fill the whole marker with the *markerfacecolor*. - - 'left', 'right', 'bottom', 'top': Fill the marker half at - the given side with the *markerfacecolor*. The other - half of the marker is filled with *markerfacecoloralt*. - - 'none': No filling. - - For examples see :ref:`marker_fill_styles`. - """ - self.set_marker(MarkerStyle(self._marker.get_marker(), fs)) - self.stale = True - - def set_markevery(self, every): - """ - Set the markevery property to subsample the plot when using markers. - - e.g., if ``every=5``, every 5-th marker will be plotted. - - Parameters - ---------- - every : None or int or (int, int) or slice or list[int] or float or \ -(float, float) or list[bool] - Which markers to plot. - - - ``every=None``: every point will be plotted. - - ``every=N``: every N-th marker will be plotted starting with - marker 0. - - ``every=(start, N)``: every N-th marker, starting at index - *start*, will be plotted. - - ``every=slice(start, end, N)``: every N-th marker, starting at - index *start*, up to but not including index *end*, will be - plotted. - - ``every=[i, j, m, ...]``: only markers at the given indices - will be plotted. - - ``every=[True, False, True, ...]``: only positions that are True - will be plotted. The list must have the same length as the data - points. - - ``every=0.1``, (i.e. a float): markers will be spaced at - approximately equal visual distances along the line; the distance - along the line between markers is determined by multiplying the - display-coordinate distance of the axes bounding-box diagonal - by the value of *every*. - - ``every=(0.5, 0.1)`` (i.e. a length-2 tuple of float): similar - to ``every=0.1`` but the first marker will be offset along the - line by 0.5 multiplied by the - display-coordinate-diagonal-distance along the line. - - For examples see - :doc:`/gallery/lines_bars_and_markers/markevery_demo`. - - Notes - ----- - Setting *markevery* will still only draw markers at actual data points. - While the float argument form aims for uniform visual spacing, it has - to coerce from the ideal spacing to the nearest available data point. - Depending on the number and distribution of data points, the result - may still not look evenly spaced. - - When using a start offset to specify the first marker, the offset will - be from the first data point which may be different from the first - the visible data point if the plot is zoomed in. - - If zooming in on a plot when using float arguments then the actual - data points that have markers will change because the distance between - markers is always determined from the display-coordinates - axes-bounding-box-diagonal regardless of the actual axes data limits. - - """ - self._markevery = every - self.stale = True - - def get_markevery(self): - """ - Return the markevery setting for marker subsampling. - - See also `~.Line2D.set_markevery`. - """ - return self._markevery - - def set_picker(self, p): - """ - Set the event picker details for the line. - - Parameters - ---------- - p : float or callable[[Artist, Event], tuple[bool, dict]] - If a float, it is used as the pick radius in points. - """ - if not callable(p): - self.set_pickradius(p) - self._picker = p - - def get_bbox(self): - """Get the bounding box of this line.""" - bbox = Bbox([[0, 0], [0, 0]]) - bbox.update_from_data_xy(self.get_xydata()) - return bbox - - def get_window_extent(self, renderer=None): - bbox = Bbox([[0, 0], [0, 0]]) - trans_data_to_xy = self.get_transform().transform - bbox.update_from_data_xy(trans_data_to_xy(self.get_xydata()), - ignore=True) - # correct for marker size, if any - if self._marker: - ms = (self._markersize / 72.0 * self.figure.dpi) * 0.5 - bbox = bbox.padded(ms) - return bbox - - def set_data(self, *args): - """ - Set the x and y data. - - Parameters - ---------- - *args : (2, N) array or two 1D arrays - """ - if len(args) == 1: - (x, y), = args - else: - x, y = args - - self.set_xdata(x) - self.set_ydata(y) - - def recache_always(self): - self.recache(always=True) - - def recache(self, always=False): - if always or self._invalidx: - xconv = self.convert_xunits(self._xorig) - x = _to_unmasked_float_array(xconv).ravel() - else: - x = self._x - if always or self._invalidy: - yconv = self.convert_yunits(self._yorig) - y = _to_unmasked_float_array(yconv).ravel() - else: - y = self._y - - self._xy = np.column_stack(np.broadcast_arrays(x, y)).astype(float) - self._x, self._y = self._xy.T # views - - self._subslice = False - if (self.axes - and len(x) > self._subslice_optim_min_size - and _path.is_sorted_and_has_non_nan(x) - and self.axes.name == 'rectilinear' - and self.axes.get_xscale() == 'linear' - and self._markevery is None - and self.get_clip_on() - and self.get_transform() == self.axes.transData): - self._subslice = True - nanmask = np.isnan(x) - if nanmask.any(): - self._x_filled = self._x.copy() - indices = np.arange(len(x)) - self._x_filled[nanmask] = np.interp( - indices[nanmask], indices[~nanmask], self._x[~nanmask]) - else: - self._x_filled = self._x - - if self._path is not None: - interpolation_steps = self._path._interpolation_steps - else: - interpolation_steps = 1 - xy = STEP_LOOKUP_MAP[self._drawstyle](*self._xy.T) - self._path = Path(np.asarray(xy).T, - _interpolation_steps=interpolation_steps) - self._transformed_path = None - self._invalidx = False - self._invalidy = False - - def _transform_path(self, subslice=None): - """ - Put a TransformedPath instance at self._transformed_path; - all invalidation of the transform is then handled by the - TransformedPath instance. - """ - # Masked arrays are now handled by the Path class itself - if subslice is not None: - xy = STEP_LOOKUP_MAP[self._drawstyle](*self._xy[subslice, :].T) - _path = Path(np.asarray(xy).T, - _interpolation_steps=self._path._interpolation_steps) - else: - _path = self._path - self._transformed_path = TransformedPath(_path, self.get_transform()) - - def _get_transformed_path(self): - """Return this line's `~matplotlib.transforms.TransformedPath`.""" - if self._transformed_path is None: - self._transform_path() - return self._transformed_path - - def set_transform(self, t): - # docstring inherited - self._invalidx = True - self._invalidy = True - super().set_transform(t) - - @allow_rasterization - def draw(self, renderer): - # docstring inherited - - if not self.get_visible(): - return - - if self._invalidy or self._invalidx: - self.recache() - self.ind_offset = 0 # Needed for contains() method. - if self._subslice and self.axes: - x0, x1 = self.axes.get_xbound() - i0 = self._x_filled.searchsorted(x0, 'left') - i1 = self._x_filled.searchsorted(x1, 'right') - subslice = slice(max(i0 - 1, 0), i1 + 1) - self.ind_offset = subslice.start - self._transform_path(subslice) - else: - subslice = None - - if self.get_path_effects(): - from matplotlib.patheffects import PathEffectRenderer - renderer = PathEffectRenderer(self.get_path_effects(), renderer) - - renderer.open_group('line2d', self.get_gid()) - if self._lineStyles[self._linestyle] != '_draw_nothing': - tpath, affine = (self._get_transformed_path() - .get_transformed_path_and_affine()) - if len(tpath.vertices): - gc = renderer.new_gc() - self._set_gc_clip(gc) - gc.set_url(self.get_url()) - - gc.set_antialiased(self._antialiased) - gc.set_linewidth(self._linewidth) - - if self.is_dashed(): - cap = self._dashcapstyle - join = self._dashjoinstyle - else: - cap = self._solidcapstyle - join = self._solidjoinstyle - gc.set_joinstyle(join) - gc.set_capstyle(cap) - gc.set_snap(self.get_snap()) - if self.get_sketch_params() is not None: - gc.set_sketch_params(*self.get_sketch_params()) - - # We first draw a path within the gaps if needed. - if self.is_dashed() and self._gapcolor is not None: - lc_rgba = mcolors.to_rgba(self._gapcolor, self._alpha) - gc.set_foreground(lc_rgba, isRGBA=True) - - # Define the inverse pattern by moving the last gap to the - # start of the sequence. - dashes = self._dash_pattern[1] - gaps = dashes[-1:] + dashes[:-1] - # Set the offset so that this new first segment is skipped - # (see backend_bases.GraphicsContextBase.set_dashes for - # offset definition). - offset_gaps = self._dash_pattern[0] + dashes[-1] - - gc.set_dashes(offset_gaps, gaps) - renderer.draw_path(gc, tpath, affine.frozen()) - - lc_rgba = mcolors.to_rgba(self._color, self._alpha) - gc.set_foreground(lc_rgba, isRGBA=True) - - gc.set_dashes(*self._dash_pattern) - renderer.draw_path(gc, tpath, affine.frozen()) - gc.restore() - - if self._marker and self._markersize > 0: - gc = renderer.new_gc() - self._set_gc_clip(gc) - gc.set_url(self.get_url()) - gc.set_linewidth(self._markeredgewidth) - gc.set_antialiased(self._antialiased) - - ec_rgba = mcolors.to_rgba( - self.get_markeredgecolor(), self._alpha) - fc_rgba = mcolors.to_rgba( - self._get_markerfacecolor(), self._alpha) - fcalt_rgba = mcolors.to_rgba( - self._get_markerfacecolor(alt=True), self._alpha) - # If the edgecolor is "auto", it is set according to the *line* - # color but inherits the alpha value of the *face* color, if any. - if (cbook._str_equal(self._markeredgecolor, "auto") - and not cbook._str_lower_equal( - self.get_markerfacecolor(), "none")): - ec_rgba = ec_rgba[:3] + (fc_rgba[3],) - gc.set_foreground(ec_rgba, isRGBA=True) - if self.get_sketch_params() is not None: - scale, length, randomness = self.get_sketch_params() - gc.set_sketch_params(scale/2, length/2, 2*randomness) - - marker = self._marker - - # Markers *must* be drawn ignoring the drawstyle (but don't pay the - # recaching if drawstyle is already "default"). - if self.get_drawstyle() != "default": - with cbook._setattr_cm( - self, _drawstyle="default", _transformed_path=None): - self.recache() - self._transform_path(subslice) - tpath, affine = (self._get_transformed_path() - .get_transformed_points_and_affine()) - else: - tpath, affine = (self._get_transformed_path() - .get_transformed_points_and_affine()) - - if len(tpath.vertices): - # subsample the markers if markevery is not None - markevery = self.get_markevery() - if markevery is not None: - subsampled = _mark_every_path( - markevery, tpath, affine, self.axes) - else: - subsampled = tpath - - snap = marker.get_snap_threshold() - if isinstance(snap, Real): - snap = renderer.points_to_pixels(self._markersize) >= snap - gc.set_snap(snap) - gc.set_joinstyle(marker.get_joinstyle()) - gc.set_capstyle(marker.get_capstyle()) - marker_path = marker.get_path() - marker_trans = marker.get_transform() - w = renderer.points_to_pixels(self._markersize) - - if cbook._str_equal(marker.get_marker(), ","): - gc.set_linewidth(0) - else: - # Don't scale for pixels, and don't stroke them - marker_trans = marker_trans.scale(w) - renderer.draw_markers(gc, marker_path, marker_trans, - subsampled, affine.frozen(), - fc_rgba) - - alt_marker_path = marker.get_alt_path() - if alt_marker_path: - alt_marker_trans = marker.get_alt_transform() - alt_marker_trans = alt_marker_trans.scale(w) - renderer.draw_markers( - gc, alt_marker_path, alt_marker_trans, subsampled, - affine.frozen(), fcalt_rgba) - - gc.restore() - - renderer.close_group('line2d') - self.stale = False - - def get_antialiased(self): - """Return whether antialiased rendering is used.""" - return self._antialiased - - def get_color(self): - """ - Return the line color. - - See also `~.Line2D.set_color`. - """ - return self._color - - def get_drawstyle(self): - """ - Return the drawstyle. - - See also `~.Line2D.set_drawstyle`. - """ - return self._drawstyle - - def get_gapcolor(self): - """ - Return the line gapcolor. - - See also `~.Line2D.set_gapcolor`. - """ - return self._gapcolor - - def get_linestyle(self): - """ - Return the linestyle. - - See also `~.Line2D.set_linestyle`. - """ - return self._linestyle - - def get_linewidth(self): - """ - Return the linewidth in points. - - See also `~.Line2D.set_linewidth`. - """ - return self._linewidth - - def get_marker(self): - """ - Return the line marker. - - See also `~.Line2D.set_marker`. - """ - return self._marker.get_marker() - - def get_markeredgecolor(self): - """ - Return the marker edge color. - - See also `~.Line2D.set_markeredgecolor`. - """ - mec = self._markeredgecolor - if cbook._str_equal(mec, 'auto'): - if mpl.rcParams['_internal.classic_mode']: - if self._marker.get_marker() in ('.', ','): - return self._color - if (self._marker.is_filled() - and self._marker.get_fillstyle() != 'none'): - return 'k' # Bad hard-wired default... - return self._color - else: - return mec - - def get_markeredgewidth(self): - """ - Return the marker edge width in points. - - See also `~.Line2D.set_markeredgewidth`. - """ - return self._markeredgewidth - - def _get_markerfacecolor(self, alt=False): - if self._marker.get_fillstyle() == 'none': - return 'none' - fc = self._markerfacecoloralt if alt else self._markerfacecolor - if cbook._str_lower_equal(fc, 'auto'): - return self._color - else: - return fc - - def get_markerfacecolor(self): - """ - Return the marker face color. - - See also `~.Line2D.set_markerfacecolor`. - """ - return self._get_markerfacecolor(alt=False) - - def get_markerfacecoloralt(self): - """ - Return the alternate marker face color. - - See also `~.Line2D.set_markerfacecoloralt`. - """ - return self._get_markerfacecolor(alt=True) - - def get_markersize(self): - """ - Return the marker size in points. - - See also `~.Line2D.set_markersize`. - """ - return self._markersize - - def get_data(self, orig=True): - """ - Return the line data as an ``(xdata, ydata)`` pair. - - If *orig* is *True*, return the original data. - """ - return self.get_xdata(orig=orig), self.get_ydata(orig=orig) - - def get_xdata(self, orig=True): - """ - Return the xdata. - - If *orig* is *True*, return the original data, else the - processed data. - """ - if orig: - return self._xorig - if self._invalidx: - self.recache() - return self._x - - def get_ydata(self, orig=True): - """ - Return the ydata. - - If *orig* is *True*, return the original data, else the - processed data. - """ - if orig: - return self._yorig - if self._invalidy: - self.recache() - return self._y - - def get_path(self): - """Return the `~matplotlib.path.Path` associated with this line.""" - if self._invalidy or self._invalidx: - self.recache() - return self._path - - def get_xydata(self): - """ - Return the *xy* data as a Nx2 numpy array. - """ - if self._invalidy or self._invalidx: - self.recache() - return self._xy - - def set_antialiased(self, b): - """ - Set whether to use antialiased rendering. - - Parameters - ---------- - b : bool - """ - if self._antialiased != b: - self.stale = True - self._antialiased = b - - def set_color(self, color): - """ - Set the color of the line. - - Parameters - ---------- - color : color - """ - mcolors._check_color_like(color=color) - self._color = color - self.stale = True - - def set_drawstyle(self, drawstyle): - """ - Set the drawstyle of the plot. - - The drawstyle determines how the points are connected. - - Parameters - ---------- - drawstyle : {'default', 'steps', 'steps-pre', 'steps-mid', \ -'steps-post'}, default: 'default' - For 'default', the points are connected with straight lines. - - The steps variants connect the points with step-like lines, - i.e. horizontal lines with vertical steps. They differ in the - location of the step: - - - 'steps-pre': The step is at the beginning of the line segment, - i.e. the line will be at the y-value of point to the right. - - 'steps-mid': The step is halfway between the points. - - 'steps-post: The step is at the end of the line segment, - i.e. the line will be at the y-value of the point to the left. - - 'steps' is equal to 'steps-pre' and is maintained for - backward-compatibility. - - For examples see :doc:`/gallery/lines_bars_and_markers/step_demo`. - """ - if drawstyle is None: - drawstyle = 'default' - _api.check_in_list(self.drawStyles, drawstyle=drawstyle) - if self._drawstyle != drawstyle: - self.stale = True - # invalidate to trigger a recache of the path - self._invalidx = True - self._drawstyle = drawstyle - - def set_gapcolor(self, gapcolor): - """ - Set a color to fill the gaps in the dashed line style. - - .. note:: - - Striped lines are created by drawing two interleaved dashed lines. - There can be overlaps between those two, which may result in - artifacts when using transparency. - - This functionality is experimental and may change. - - Parameters - ---------- - gapcolor : color or None - The color with which to fill the gaps. If None, the gaps are - unfilled. - """ - if gapcolor is not None: - mcolors._check_color_like(color=gapcolor) - self._gapcolor = gapcolor - self.stale = True - - def set_linewidth(self, w): - """ - Set the line width in points. - - Parameters - ---------- - w : float - Line width, in points. - """ - w = float(w) - if self._linewidth != w: - self.stale = True - self._linewidth = w - self._dash_pattern = _scale_dashes(*self._unscaled_dash_pattern, w) - - def set_linestyle(self, ls): - """ - Set the linestyle of the line. - - Parameters - ---------- - ls : {'-', '--', '-.', ':', '', (offset, on-off-seq), ...} - Possible values: - - - A string: - - ========================================== ================= - linestyle description - ========================================== ================= - ``'-'`` or ``'solid'`` solid line - ``'--'`` or ``'dashed'`` dashed line - ``'-.'`` or ``'dashdot'`` dash-dotted line - ``':'`` or ``'dotted'`` dotted line - ``'none'``, ``'None'``, ``' '``, or ``''`` draw nothing - ========================================== ================= - - - Alternatively a dash tuple of the following form can be - provided:: - - (offset, onoffseq) - - where ``onoffseq`` is an even length tuple of on and off ink - in points. See also :meth:`set_dashes`. - - For examples see :doc:`/gallery/lines_bars_and_markers/linestyles`. - """ - if isinstance(ls, str): - if ls in [' ', '', 'none']: - ls = 'None' - _api.check_in_list([*self._lineStyles, *ls_mapper_r], ls=ls) - if ls not in self._lineStyles: - ls = ls_mapper_r[ls] - self._linestyle = ls - else: - self._linestyle = '--' - self._unscaled_dash_pattern = _get_dash_pattern(ls) - self._dash_pattern = _scale_dashes( - *self._unscaled_dash_pattern, self._linewidth) - self.stale = True - - @_docstring.interpd - def set_marker(self, marker): - """ - Set the line marker. - - Parameters - ---------- - marker : marker style string, `~.path.Path` or `~.markers.MarkerStyle` - See `~matplotlib.markers` for full description of possible - arguments. - """ - self._marker = MarkerStyle(marker, self._marker.get_fillstyle()) - self.stale = True - - def _set_markercolor(self, name, has_rcdefault, val): - if val is None: - val = mpl.rcParams[f"lines.{name}"] if has_rcdefault else "auto" - attr = f"_{name}" - current = getattr(self, attr) - if current is None: - self.stale = True - else: - neq = current != val - # Much faster than `np.any(current != val)` if no arrays are used. - if neq.any() if isinstance(neq, np.ndarray) else neq: - self.stale = True - setattr(self, attr, val) - - def set_markeredgecolor(self, ec): - """ - Set the marker edge color. - - Parameters - ---------- - ec : color - """ - self._set_markercolor("markeredgecolor", True, ec) - - def set_markerfacecolor(self, fc): - """ - Set the marker face color. - - Parameters - ---------- - fc : color - """ - self._set_markercolor("markerfacecolor", True, fc) - - def set_markerfacecoloralt(self, fc): - """ - Set the alternate marker face color. - - Parameters - ---------- - fc : color - """ - self._set_markercolor("markerfacecoloralt", False, fc) - - def set_markeredgewidth(self, ew): - """ - Set the marker edge width in points. - - Parameters - ---------- - ew : float - Marker edge width, in points. - """ - if ew is None: - ew = mpl.rcParams['lines.markeredgewidth'] - if self._markeredgewidth != ew: - self.stale = True - self._markeredgewidth = ew - - def set_markersize(self, sz): - """ - Set the marker size in points. - - Parameters - ---------- - sz : float - Marker size, in points. - """ - sz = float(sz) - if self._markersize != sz: - self.stale = True - self._markersize = sz - - def set_xdata(self, x): - """ - Set the data array for x. - - Parameters - ---------- - x : 1D array - """ - if not np.iterable(x): - # When deprecation cycle is completed - # raise RuntimeError('x must be a sequence') - _api.warn_deprecated( - since=3.7, - message="Setting data with a non sequence type " - "is deprecated since %(since)s and will be " - "remove %(removal)s") - x = [x, ] - self._xorig = copy.copy(x) - self._invalidx = True - self.stale = True - - def set_ydata(self, y): - """ - Set the data array for y. - - Parameters - ---------- - y : 1D array - """ - if not np.iterable(y): - # When deprecation cycle is completed - # raise RuntimeError('y must be a sequence') - _api.warn_deprecated( - since=3.7, - message="Setting data with a non sequence type " - "is deprecated since %(since)s and will be " - "remove %(removal)s") - y = [y, ] - self._yorig = copy.copy(y) - self._invalidy = True - self.stale = True - - def set_dashes(self, seq): - """ - Set the dash sequence. - - The dash sequence is a sequence of floats of even length describing - the length of dashes and spaces in points. - - For example, (5, 2, 1, 2) describes a sequence of 5 point and 1 point - dashes separated by 2 point spaces. - - See also `~.Line2D.set_gapcolor`, which allows those spaces to be - filled with a color. - - Parameters - ---------- - seq : sequence of floats (on/off ink in points) or (None, None) - If *seq* is empty or ``(None, None)``, the linestyle will be set - to solid. - """ - if seq == (None, None) or len(seq) == 0: - self.set_linestyle('-') - else: - self.set_linestyle((0, seq)) - - def update_from(self, other): - """Copy properties from *other* to self.""" - super().update_from(other) - self._linestyle = other._linestyle - self._linewidth = other._linewidth - self._color = other._color - self._gapcolor = other._gapcolor - self._markersize = other._markersize - self._markerfacecolor = other._markerfacecolor - self._markerfacecoloralt = other._markerfacecoloralt - self._markeredgecolor = other._markeredgecolor - self._markeredgewidth = other._markeredgewidth - self._unscaled_dash_pattern = other._unscaled_dash_pattern - self._dash_pattern = other._dash_pattern - self._dashcapstyle = other._dashcapstyle - self._dashjoinstyle = other._dashjoinstyle - self._solidcapstyle = other._solidcapstyle - self._solidjoinstyle = other._solidjoinstyle - - self._linestyle = other._linestyle - self._marker = MarkerStyle(marker=other._marker) - self._drawstyle = other._drawstyle - - @_docstring.interpd - def set_dash_joinstyle(self, s): - """ - How to join segments of the line if it `~Line2D.is_dashed`. - - The default joinstyle is :rc:`lines.dash_joinstyle`. - - Parameters - ---------- - s : `.JoinStyle` or %(JoinStyle)s - """ - js = JoinStyle(s) - if self._dashjoinstyle != js: - self.stale = True - self._dashjoinstyle = js - - @_docstring.interpd - def set_solid_joinstyle(self, s): - """ - How to join segments if the line is solid (not `~Line2D.is_dashed`). - - The default joinstyle is :rc:`lines.solid_joinstyle`. - - Parameters - ---------- - s : `.JoinStyle` or %(JoinStyle)s - """ - js = JoinStyle(s) - if self._solidjoinstyle != js: - self.stale = True - self._solidjoinstyle = js - - def get_dash_joinstyle(self): - """ - Return the `.JoinStyle` for dashed lines. - - See also `~.Line2D.set_dash_joinstyle`. - """ - return self._dashjoinstyle.name - - def get_solid_joinstyle(self): - """ - Return the `.JoinStyle` for solid lines. - - See also `~.Line2D.set_solid_joinstyle`. - """ - return self._solidjoinstyle.name - - @_docstring.interpd - def set_dash_capstyle(self, s): - """ - How to draw the end caps if the line is `~Line2D.is_dashed`. - - The default capstyle is :rc:`lines.dash_capstyle`. - - Parameters - ---------- - s : `.CapStyle` or %(CapStyle)s - """ - cs = CapStyle(s) - if self._dashcapstyle != cs: - self.stale = True - self._dashcapstyle = cs - - @_docstring.interpd - def set_solid_capstyle(self, s): - """ - How to draw the end caps if the line is solid (not `~Line2D.is_dashed`) - - The default capstyle is :rc:`lines.solid_capstyle`. - - Parameters - ---------- - s : `.CapStyle` or %(CapStyle)s - """ - cs = CapStyle(s) - if self._solidcapstyle != cs: - self.stale = True - self._solidcapstyle = cs - - def get_dash_capstyle(self): - """ - Return the `.CapStyle` for dashed lines. - - See also `~.Line2D.set_dash_capstyle`. - """ - return self._dashcapstyle.name - - def get_solid_capstyle(self): - """ - Return the `.CapStyle` for solid lines. - - See also `~.Line2D.set_solid_capstyle`. - """ - return self._solidcapstyle.name - - def is_dashed(self): - """ - Return whether line has a dashed linestyle. - - A custom linestyle is assumed to be dashed, we do not inspect the - ``onoffseq`` directly. - - See also `~.Line2D.set_linestyle`. - """ - return self._linestyle in ('--', '-.', ':') - - -class _AxLine(Line2D): - """ - A helper class that implements `~.Axes.axline`, by recomputing the artist - transform at draw time. - """ - - def __init__(self, xy1, xy2, slope, **kwargs): - super().__init__([0, 1], [0, 1], **kwargs) - - if (xy2 is None and slope is None or - xy2 is not None and slope is not None): - raise TypeError( - "Exactly one of 'xy2' and 'slope' must be given") - - self._slope = slope - self._xy1 = xy1 - self._xy2 = xy2 - - def get_transform(self): - ax = self.axes - points_transform = self._transform - ax.transData + ax.transScale - - if self._xy2 is not None: - # two points were given - (x1, y1), (x2, y2) = \ - points_transform.transform([self._xy1, self._xy2]) - dx = x2 - x1 - dy = y2 - y1 - if np.allclose(x1, x2): - if np.allclose(y1, y2): - raise ValueError( - f"Cannot draw a line through two identical points " - f"(x={(x1, x2)}, y={(y1, y2)})") - slope = np.inf - else: - slope = dy / dx - else: - # one point and a slope were given - x1, y1 = points_transform.transform(self._xy1) - slope = self._slope - (vxlo, vylo), (vxhi, vyhi) = ax.transScale.transform(ax.viewLim) - # General case: find intersections with view limits in either - # direction, and draw between the middle two points. - if np.isclose(slope, 0): - start = vxlo, y1 - stop = vxhi, y1 - elif np.isinf(slope): - start = x1, vylo - stop = x1, vyhi - else: - _, start, stop, _ = sorted([ - (vxlo, y1 + (vxlo - x1) * slope), - (vxhi, y1 + (vxhi - x1) * slope), - (x1 + (vylo - y1) / slope, vylo), - (x1 + (vyhi - y1) / slope, vyhi), - ]) - return (BboxTransformTo(Bbox([start, stop])) - + ax.transLimits + ax.transAxes) - - def draw(self, renderer): - self._transformed_path = None # Force regen. - super().draw(renderer) - - -class VertexSelector: - """ - Manage the callbacks to maintain a list of selected vertices for `.Line2D`. - Derived classes should override the `process_selected` method to do - something with the picks. - - Here is an example which highlights the selected verts with red circles:: - - import numpy as np - import matplotlib.pyplot as plt - import matplotlib.lines as lines - - class HighlightSelected(lines.VertexSelector): - def __init__(self, line, fmt='ro', **kwargs): - super().__init__(line) - self.markers, = self.axes.plot([], [], fmt, **kwargs) - - def process_selected(self, ind, xs, ys): - self.markers.set_data(xs, ys) - self.canvas.draw() - - fig, ax = plt.subplots() - x, y = np.random.rand(2, 30) - line, = ax.plot(x, y, 'bs-', picker=5) - - selector = HighlightSelected(line) - plt.show() - """ - - def __init__(self, line): - """ - Parameters - ---------- - line : `~matplotlib.lines.Line2D` - The line must already have been added to an `~.axes.Axes` and must - have its picker property set. - """ - if line.axes is None: - raise RuntimeError('You must first add the line to the Axes') - if line.get_picker() is None: - raise RuntimeError('You must first set the picker property ' - 'of the line') - self.axes = line.axes - self.line = line - self.cid = self.canvas.callbacks._connect_picklable( - 'pick_event', self.onpick) - self.ind = set() - - canvas = property(lambda self: self.axes.figure.canvas) - - def process_selected(self, ind, xs, ys): - """ - Default "do nothing" implementation of the `process_selected` method. - - Parameters - ---------- - ind : list of int - The indices of the selected vertices. - xs, ys : array-like - The coordinates of the selected vertices. - """ - pass - - def onpick(self, event): - """When the line is picked, update the set of selected indices.""" - if event.artist is not self.line: - return - self.ind ^= set(event.ind) - ind = sorted(self.ind) - xdata, ydata = self.line.get_data() - self.process_selected(ind, xdata[ind], ydata[ind]) - - -lineStyles = Line2D._lineStyles -lineMarkers = MarkerStyle.markers -drawStyles = Line2D.drawStyles -fillStyles = MarkerStyle.fillstyles diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/markers.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/markers.py deleted file mode 100644 index c9fc014..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/markers.py +++ /dev/null @@ -1,943 +0,0 @@ -r""" -Functions to handle markers; used by the marker functionality of -`~matplotlib.axes.Axes.plot`, `~matplotlib.axes.Axes.scatter`, and -`~matplotlib.axes.Axes.errorbar`. - -All possible markers are defined here: - -============================== ====== ========================================= -marker symbol description -============================== ====== ========================================= -``"."`` |m00| point -``","`` |m01| pixel -``"o"`` |m02| circle -``"v"`` |m03| triangle_down -``"^"`` |m04| triangle_up -``"<"`` |m05| triangle_left -``">"`` |m06| triangle_right -``"1"`` |m07| tri_down -``"2"`` |m08| tri_up -``"3"`` |m09| tri_left -``"4"`` |m10| tri_right -``"8"`` |m11| octagon -``"s"`` |m12| square -``"p"`` |m13| pentagon -``"P"`` |m23| plus (filled) -``"*"`` |m14| star -``"h"`` |m15| hexagon1 -``"H"`` |m16| hexagon2 -``"+"`` |m17| plus -``"x"`` |m18| x -``"X"`` |m24| x (filled) -``"D"`` |m19| diamond -``"d"`` |m20| thin_diamond -``"|"`` |m21| vline -``"_"`` |m22| hline -``0`` (``TICKLEFT``) |m25| tickleft -``1`` (``TICKRIGHT``) |m26| tickright -``2`` (``TICKUP``) |m27| tickup -``3`` (``TICKDOWN``) |m28| tickdown -``4`` (``CARETLEFT``) |m29| caretleft -``5`` (``CARETRIGHT``) |m30| caretright -``6`` (``CARETUP``) |m31| caretup -``7`` (``CARETDOWN``) |m32| caretdown -``8`` (``CARETLEFTBASE``) |m33| caretleft (centered at base) -``9`` (``CARETRIGHTBASE``) |m34| caretright (centered at base) -``10`` (``CARETUPBASE``) |m35| caretup (centered at base) -``11`` (``CARETDOWNBASE``) |m36| caretdown (centered at base) -``"none"`` or ``"None"`` nothing -``" "`` or ``""`` nothing -``'$...$'`` |m37| Render the string using mathtext. - E.g ``"$f$"`` for marker showing the - letter ``f``. -``verts`` A list of (x, y) pairs used for Path - vertices. The center of the marker is - located at (0, 0) and the size is - normalized, such that the created path - is encapsulated inside the unit cell. -path A `~matplotlib.path.Path` instance. -``(numsides, 0, angle)`` A regular polygon with ``numsides`` - sides, rotated by ``angle``. -``(numsides, 1, angle)`` A star-like symbol with ``numsides`` - sides, rotated by ``angle``. -``(numsides, 2, angle)`` An asterisk with ``numsides`` sides, - rotated by ``angle``. -============================== ====== ========================================= - -As a deprecated feature, ``None`` also means 'nothing' when directly -constructing a `.MarkerStyle`, but note that there are other contexts where -``marker=None`` instead means "the default marker" (e.g. :rc:`scatter.marker` -for `.Axes.scatter`). - -Note that special symbols can be defined via the -:doc:`STIX math font `, -e.g. ``"$\u266B$"``. For an overview over the STIX font symbols refer to the -`STIX font table `_. -Also see the :doc:`/gallery/text_labels_and_annotations/stix_fonts_demo`. - -Integer numbers from ``0`` to ``11`` create lines and triangles. Those are -equally accessible via capitalized variables, like ``CARETDOWNBASE``. -Hence the following are equivalent:: - - plt.plot([1, 2, 3], marker=11) - plt.plot([1, 2, 3], marker=matplotlib.markers.CARETDOWNBASE) - -Markers join and cap styles can be customized by creating a new instance of -MarkerStyle. -A MarkerStyle can also have a custom `~matplotlib.transforms.Transform` -allowing it to be arbitrarily rotated or offset. - -Examples showing the use of markers: - -* :doc:`/gallery/lines_bars_and_markers/marker_reference` -* :doc:`/gallery/lines_bars_and_markers/scatter_star_poly` -* :doc:`/gallery/lines_bars_and_markers/multivariate_marker_plot` - -.. |m00| image:: /_static/markers/m00.png -.. |m01| image:: /_static/markers/m01.png -.. |m02| image:: /_static/markers/m02.png -.. |m03| image:: /_static/markers/m03.png -.. |m04| image:: /_static/markers/m04.png -.. |m05| image:: /_static/markers/m05.png -.. |m06| image:: /_static/markers/m06.png -.. |m07| image:: /_static/markers/m07.png -.. |m08| image:: /_static/markers/m08.png -.. |m09| image:: /_static/markers/m09.png -.. |m10| image:: /_static/markers/m10.png -.. |m11| image:: /_static/markers/m11.png -.. |m12| image:: /_static/markers/m12.png -.. |m13| image:: /_static/markers/m13.png -.. |m14| image:: /_static/markers/m14.png -.. |m15| image:: /_static/markers/m15.png -.. |m16| image:: /_static/markers/m16.png -.. |m17| image:: /_static/markers/m17.png -.. |m18| image:: /_static/markers/m18.png -.. |m19| image:: /_static/markers/m19.png -.. |m20| image:: /_static/markers/m20.png -.. |m21| image:: /_static/markers/m21.png -.. |m22| image:: /_static/markers/m22.png -.. |m23| image:: /_static/markers/m23.png -.. |m24| image:: /_static/markers/m24.png -.. |m25| image:: /_static/markers/m25.png -.. |m26| image:: /_static/markers/m26.png -.. |m27| image:: /_static/markers/m27.png -.. |m28| image:: /_static/markers/m28.png -.. |m29| image:: /_static/markers/m29.png -.. |m30| image:: /_static/markers/m30.png -.. |m31| image:: /_static/markers/m31.png -.. |m32| image:: /_static/markers/m32.png -.. |m33| image:: /_static/markers/m33.png -.. |m34| image:: /_static/markers/m34.png -.. |m35| image:: /_static/markers/m35.png -.. |m36| image:: /_static/markers/m36.png -.. |m37| image:: /_static/markers/m37.png -""" -import copy - -from collections.abc import Sized -import inspect - -import numpy as np - -import matplotlib as mpl -from . import _api, cbook -from .path import Path -from .transforms import IdentityTransform, Affine2D -from ._enums import JoinStyle, CapStyle - -# special-purpose marker identifiers: -(TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN, - CARETLEFT, CARETRIGHT, CARETUP, CARETDOWN, - CARETLEFTBASE, CARETRIGHTBASE, CARETUPBASE, CARETDOWNBASE) = range(12) - -_empty_path = Path(np.empty((0, 2))) - - -class MarkerStyle: - """ - A class representing marker types. - - Instances are immutable. If you need to change anything, create a new - instance. - - Attributes - ---------- - markers : list - All known markers. - filled_markers : list - All known filled markers. This is a subset of *markers*. - fillstyles : list - The supported fillstyles. - """ - - markers = { - '.': 'point', - ',': 'pixel', - 'o': 'circle', - 'v': 'triangle_down', - '^': 'triangle_up', - '<': 'triangle_left', - '>': 'triangle_right', - '1': 'tri_down', - '2': 'tri_up', - '3': 'tri_left', - '4': 'tri_right', - '8': 'octagon', - 's': 'square', - 'p': 'pentagon', - '*': 'star', - 'h': 'hexagon1', - 'H': 'hexagon2', - '+': 'plus', - 'x': 'x', - 'D': 'diamond', - 'd': 'thin_diamond', - '|': 'vline', - '_': 'hline', - 'P': 'plus_filled', - 'X': 'x_filled', - TICKLEFT: 'tickleft', - TICKRIGHT: 'tickright', - TICKUP: 'tickup', - TICKDOWN: 'tickdown', - CARETLEFT: 'caretleft', - CARETRIGHT: 'caretright', - CARETUP: 'caretup', - CARETDOWN: 'caretdown', - CARETLEFTBASE: 'caretleftbase', - CARETRIGHTBASE: 'caretrightbase', - CARETUPBASE: 'caretupbase', - CARETDOWNBASE: 'caretdownbase', - "None": 'nothing', - "none": 'nothing', - ' ': 'nothing', - '': 'nothing' - } - - # Just used for informational purposes. is_filled() - # is calculated in the _set_* functions. - filled_markers = ( - '.', 'o', 'v', '^', '<', '>', '8', 's', 'p', '*', 'h', 'H', 'D', 'd', - 'P', 'X') - - fillstyles = ('full', 'left', 'right', 'bottom', 'top', 'none') - _half_fillstyles = ('left', 'right', 'bottom', 'top') - - _unset = object() # For deprecation of MarkerStyle(). - - def __init__(self, marker=_unset, fillstyle=None, - transform=None, capstyle=None, joinstyle=None): - """ - Parameters - ---------- - marker : str, array-like, Path, MarkerStyle, or None - - Another instance of *MarkerStyle* copies the details of that - ``marker``. - - *None* means no marker. This is the deprecated default. - - For other possible marker values, see the module docstring - `matplotlib.markers`. - - fillstyle : str, default: :rc:`markers.fillstyle` - One of 'full', 'left', 'right', 'bottom', 'top', 'none'. - - transform : transforms.Transform, default: None - Transform that will be combined with the native transform of the - marker. - - capstyle : CapStyle, default: None - Cap style that will override the default cap style of the marker. - - joinstyle : JoinStyle, default: None - Join style that will override the default join style of the marker. - """ - self._marker_function = None - self._user_transform = transform - self._user_capstyle = capstyle - self._user_joinstyle = joinstyle - self._set_fillstyle(fillstyle) - # Remove _unset and signature rewriting after deprecation elapses. - if marker is self._unset: - marker = "" - _api.warn_deprecated( - "3.6", message="Calling MarkerStyle() with no parameters is " - "deprecated since %(since)s; support will be removed " - "%(removal)s. Use MarkerStyle('') to construct an empty " - "MarkerStyle.") - if marker is None: - marker = "" - _api.warn_deprecated( - "3.6", message="MarkerStyle(None) is deprecated since " - "%(since)s; support will be removed %(removal)s. Use " - "MarkerStyle('') to construct an empty MarkerStyle.") - self._set_marker(marker) - - __init__.__signature__ = inspect.signature( # Only for deprecation period. - lambda self, marker, fillstyle=None: None) - - def _recache(self): - if self._marker_function is None: - return - self._path = _empty_path - self._transform = IdentityTransform() - self._alt_path = None - self._alt_transform = None - self._snap_threshold = None - self._joinstyle = JoinStyle.round - self._capstyle = self._user_capstyle or CapStyle.butt - # Initial guess: Assume the marker is filled unless the fillstyle is - # set to 'none'. The marker function will override this for unfilled - # markers. - self._filled = self._fillstyle != 'none' - self._marker_function() - - def __bool__(self): - return bool(len(self._path.vertices)) - - def is_filled(self): - return self._filled - - def get_fillstyle(self): - return self._fillstyle - - def _set_fillstyle(self, fillstyle): - """ - Set the fillstyle. - - Parameters - ---------- - fillstyle : {'full', 'left', 'right', 'bottom', 'top', 'none'} - The part of the marker surface that is colored with - markerfacecolor. - """ - if fillstyle is None: - fillstyle = mpl.rcParams['markers.fillstyle'] - _api.check_in_list(self.fillstyles, fillstyle=fillstyle) - self._fillstyle = fillstyle - self._recache() - - def get_joinstyle(self): - return self._joinstyle.name - - def get_capstyle(self): - return self._capstyle.name - - def get_marker(self): - return self._marker - - def _set_marker(self, marker): - """ - Set the marker. - - Parameters - ---------- - marker : str, array-like, Path, MarkerStyle, or None, default: None - - Another instance of *MarkerStyle* copies the details of that - ``marker``. - - *None* means no marker. - - For other possible marker values see the module docstring - `matplotlib.markers`. - """ - if (isinstance(marker, np.ndarray) and marker.ndim == 2 and - marker.shape[1] == 2): - self._marker_function = self._set_vertices - elif isinstance(marker, str) and cbook.is_math_text(marker): - self._marker_function = self._set_mathtext_path - elif isinstance(marker, Path): - self._marker_function = self._set_path_marker - elif (isinstance(marker, Sized) and len(marker) in (2, 3) and - marker[1] in (0, 1, 2)): - self._marker_function = self._set_tuple_marker - elif (not isinstance(marker, (np.ndarray, list)) and - marker in self.markers): - self._marker_function = getattr( - self, '_set_' + self.markers[marker]) - elif isinstance(marker, MarkerStyle): - self.__dict__ = copy.deepcopy(marker.__dict__) - - else: - try: - Path(marker) - self._marker_function = self._set_vertices - except ValueError as err: - raise ValueError('Unrecognized marker style {!r}' - .format(marker)) from err - - if not isinstance(marker, MarkerStyle): - self._marker = marker - self._recache() - - def get_path(self): - """ - Return a `.Path` for the primary part of the marker. - - For unfilled markers this is the whole marker, for filled markers, - this is the area to be drawn with *markerfacecolor*. - """ - return self._path - - def get_transform(self): - """ - Return the transform to be applied to the `.Path` from - `MarkerStyle.get_path()`. - """ - if self._user_transform is None: - return self._transform.frozen() - else: - return (self._transform + self._user_transform).frozen() - - def get_alt_path(self): - """ - Return a `.Path` for the alternate part of the marker. - - For unfilled markers, this is *None*; for filled markers, this is the - area to be drawn with *markerfacecoloralt*. - """ - return self._alt_path - - def get_alt_transform(self): - """ - Return the transform to be applied to the `.Path` from - `MarkerStyle.get_alt_path()`. - """ - if self._user_transform is None: - return self._alt_transform.frozen() - else: - return (self._alt_transform + self._user_transform).frozen() - - def get_snap_threshold(self): - return self._snap_threshold - - def get_user_transform(self): - """Return user supplied part of marker transform.""" - if self._user_transform is not None: - return self._user_transform.frozen() - - def transformed(self, transform: Affine2D): - """ - Return a new version of this marker with the transform applied. - - Parameters - ---------- - transform : Affine2D, default: None - Transform will be combined with current user supplied transform. - """ - new_marker = MarkerStyle(self) - if new_marker._user_transform is not None: - new_marker._user_transform += transform - else: - new_marker._user_transform = transform - return new_marker - - def rotated(self, *, deg=None, rad=None): - """ - Return a new version of this marker rotated by specified angle. - - Parameters - ---------- - deg : float, default: None - Rotation angle in degrees. - - rad : float, default: None - Rotation angle in radians. - - .. note:: You must specify exactly one of deg or rad. - """ - if deg is None and rad is None: - raise ValueError('One of deg or rad is required') - if deg is not None and rad is not None: - raise ValueError('Only one of deg and rad can be supplied') - new_marker = MarkerStyle(self) - if new_marker._user_transform is None: - new_marker._user_transform = Affine2D() - - if deg is not None: - new_marker._user_transform.rotate_deg(deg) - if rad is not None: - new_marker._user_transform.rotate(rad) - - return new_marker - - def scaled(self, sx, sy=None): - """ - Return new marker scaled by specified scale factors. - - If *sy* is None, the same scale is applied in both the *x*- and - *y*-directions. - - Parameters - ---------- - sx : float - *X*-direction scaling factor. - sy : float, default: None - *Y*-direction scaling factor. - """ - if sy is None: - sy = sx - - new_marker = MarkerStyle(self) - _transform = new_marker._user_transform or Affine2D() - new_marker._user_transform = _transform.scale(sx, sy) - return new_marker - - def _set_nothing(self): - self._filled = False - - def _set_custom_marker(self, path): - rescale = np.max(np.abs(path.vertices)) # max of x's and y's. - self._transform = Affine2D().scale(0.5 / rescale) - self._path = path - - def _set_path_marker(self): - self._set_custom_marker(self._marker) - - def _set_vertices(self): - self._set_custom_marker(Path(self._marker)) - - def _set_tuple_marker(self): - marker = self._marker - if len(marker) == 2: - numsides, rotation = marker[0], 0.0 - elif len(marker) == 3: - numsides, rotation = marker[0], marker[2] - symstyle = marker[1] - if symstyle == 0: - self._path = Path.unit_regular_polygon(numsides) - self._joinstyle = self._user_joinstyle or JoinStyle.miter - elif symstyle == 1: - self._path = Path.unit_regular_star(numsides) - self._joinstyle = self._user_joinstyle or JoinStyle.bevel - elif symstyle == 2: - self._path = Path.unit_regular_asterisk(numsides) - self._filled = False - self._joinstyle = self._user_joinstyle or JoinStyle.bevel - else: - raise ValueError(f"Unexpected tuple marker: {marker}") - self._transform = Affine2D().scale(0.5).rotate_deg(rotation) - - def _set_mathtext_path(self): - """ - Draw mathtext markers '$...$' using `.TextPath` object. - - Submitted by tcb - """ - from matplotlib.text import TextPath - - # again, the properties could be initialised just once outside - # this function - text = TextPath(xy=(0, 0), s=self.get_marker(), - usetex=mpl.rcParams['text.usetex']) - if len(text.vertices) == 0: - return - - xmin, ymin = text.vertices.min(axis=0) - xmax, ymax = text.vertices.max(axis=0) - width = xmax - xmin - height = ymax - ymin - max_dim = max(width, height) - self._transform = Affine2D() \ - .translate(-xmin + 0.5 * -width, -ymin + 0.5 * -height) \ - .scale(1.0 / max_dim) - self._path = text - self._snap = False - - def _half_fill(self): - return self.get_fillstyle() in self._half_fillstyles - - def _set_circle(self, size=1.0): - self._transform = Affine2D().scale(0.5 * size) - self._snap_threshold = np.inf - if not self._half_fill(): - self._path = Path.unit_circle() - else: - self._path = self._alt_path = Path.unit_circle_righthalf() - fs = self.get_fillstyle() - self._transform.rotate_deg( - {'right': 0, 'top': 90, 'left': 180, 'bottom': 270}[fs]) - self._alt_transform = self._transform.frozen().rotate_deg(180.) - - def _set_point(self): - self._set_circle(size=0.5) - - def _set_pixel(self): - self._path = Path.unit_rectangle() - # Ideally, you'd want -0.5, -0.5 here, but then the snapping - # algorithm in the Agg backend will round this to a 2x2 - # rectangle from (-1, -1) to (1, 1). By offsetting it - # slightly, we can force it to be (0, 0) to (1, 1), which both - # makes it only be a single pixel and places it correctly - # aligned to 1-width stroking (i.e. the ticks). This hack is - # the best of a number of bad alternatives, mainly because the - # backends are not aware of what marker is actually being used - # beyond just its path data. - self._transform = Affine2D().translate(-0.49999, -0.49999) - self._snap_threshold = None - - _triangle_path = Path._create_closed([[0, 1], [-1, -1], [1, -1]]) - # Going down halfway looks to small. Golden ratio is too far. - _triangle_path_u = Path._create_closed([[0, 1], [-3/5, -1/5], [3/5, -1/5]]) - _triangle_path_d = Path._create_closed( - [[-3/5, -1/5], [3/5, -1/5], [1, -1], [-1, -1]]) - _triangle_path_l = Path._create_closed([[0, 1], [0, -1], [-1, -1]]) - _triangle_path_r = Path._create_closed([[0, 1], [0, -1], [1, -1]]) - - def _set_triangle(self, rot, skip): - self._transform = Affine2D().scale(0.5).rotate_deg(rot) - self._snap_threshold = 5.0 - - if not self._half_fill(): - self._path = self._triangle_path - else: - mpaths = [self._triangle_path_u, - self._triangle_path_l, - self._triangle_path_d, - self._triangle_path_r] - - fs = self.get_fillstyle() - if fs == 'top': - self._path = mpaths[(0 + skip) % 4] - self._alt_path = mpaths[(2 + skip) % 4] - elif fs == 'bottom': - self._path = mpaths[(2 + skip) % 4] - self._alt_path = mpaths[(0 + skip) % 4] - elif fs == 'left': - self._path = mpaths[(1 + skip) % 4] - self._alt_path = mpaths[(3 + skip) % 4] - else: - self._path = mpaths[(3 + skip) % 4] - self._alt_path = mpaths[(1 + skip) % 4] - - self._alt_transform = self._transform - - self._joinstyle = self._user_joinstyle or JoinStyle.miter - - def _set_triangle_up(self): - return self._set_triangle(0.0, 0) - - def _set_triangle_down(self): - return self._set_triangle(180.0, 2) - - def _set_triangle_left(self): - return self._set_triangle(90.0, 3) - - def _set_triangle_right(self): - return self._set_triangle(270.0, 1) - - def _set_square(self): - self._transform = Affine2D().translate(-0.5, -0.5) - self._snap_threshold = 2.0 - if not self._half_fill(): - self._path = Path.unit_rectangle() - else: - # Build a bottom filled square out of two rectangles, one filled. - self._path = Path([[0.0, 0.0], [1.0, 0.0], [1.0, 0.5], - [0.0, 0.5], [0.0, 0.0]]) - self._alt_path = Path([[0.0, 0.5], [1.0, 0.5], [1.0, 1.0], - [0.0, 1.0], [0.0, 0.5]]) - fs = self.get_fillstyle() - rotate = {'bottom': 0, 'right': 90, 'top': 180, 'left': 270}[fs] - self._transform.rotate_deg(rotate) - self._alt_transform = self._transform - - self._joinstyle = self._user_joinstyle or JoinStyle.miter - - def _set_diamond(self): - self._transform = Affine2D().translate(-0.5, -0.5).rotate_deg(45) - self._snap_threshold = 5.0 - if not self._half_fill(): - self._path = Path.unit_rectangle() - else: - self._path = Path([[0, 0], [1, 0], [1, 1], [0, 0]]) - self._alt_path = Path([[0, 0], [0, 1], [1, 1], [0, 0]]) - fs = self.get_fillstyle() - rotate = {'right': 0, 'top': 90, 'left': 180, 'bottom': 270}[fs] - self._transform.rotate_deg(rotate) - self._alt_transform = self._transform - self._joinstyle = self._user_joinstyle or JoinStyle.miter - - def _set_thin_diamond(self): - self._set_diamond() - self._transform.scale(0.6, 1.0) - - def _set_pentagon(self): - self._transform = Affine2D().scale(0.5) - self._snap_threshold = 5.0 - - polypath = Path.unit_regular_polygon(5) - - if not self._half_fill(): - self._path = polypath - else: - verts = polypath.vertices - y = (1 + np.sqrt(5)) / 4. - top = Path(verts[[0, 1, 4, 0]]) - bottom = Path(verts[[1, 2, 3, 4, 1]]) - left = Path([verts[0], verts[1], verts[2], [0, -y], verts[0]]) - right = Path([verts[0], verts[4], verts[3], [0, -y], verts[0]]) - self._path, self._alt_path = { - 'top': (top, bottom), 'bottom': (bottom, top), - 'left': (left, right), 'right': (right, left), - }[self.get_fillstyle()] - self._alt_transform = self._transform - - self._joinstyle = self._user_joinstyle or JoinStyle.miter - - def _set_star(self): - self._transform = Affine2D().scale(0.5) - self._snap_threshold = 5.0 - - polypath = Path.unit_regular_star(5, innerCircle=0.381966) - - if not self._half_fill(): - self._path = polypath - else: - verts = polypath.vertices - top = Path(np.concatenate([verts[0:4], verts[7:10], verts[0:1]])) - bottom = Path(np.concatenate([verts[3:8], verts[3:4]])) - left = Path(np.concatenate([verts[0:6], verts[0:1]])) - right = Path(np.concatenate([verts[0:1], verts[5:10], verts[0:1]])) - self._path, self._alt_path = { - 'top': (top, bottom), 'bottom': (bottom, top), - 'left': (left, right), 'right': (right, left), - }[self.get_fillstyle()] - self._alt_transform = self._transform - - self._joinstyle = self._user_joinstyle or JoinStyle.bevel - - def _set_hexagon1(self): - self._transform = Affine2D().scale(0.5) - self._snap_threshold = None - - polypath = Path.unit_regular_polygon(6) - - if not self._half_fill(): - self._path = polypath - else: - verts = polypath.vertices - # not drawing inside lines - x = np.abs(np.cos(5 * np.pi / 6.)) - top = Path(np.concatenate([[(-x, 0)], verts[[1, 0, 5]], [(x, 0)]])) - bottom = Path(np.concatenate([[(-x, 0)], verts[2:5], [(x, 0)]])) - left = Path(verts[0:4]) - right = Path(verts[[0, 5, 4, 3]]) - self._path, self._alt_path = { - 'top': (top, bottom), 'bottom': (bottom, top), - 'left': (left, right), 'right': (right, left), - }[self.get_fillstyle()] - self._alt_transform = self._transform - - self._joinstyle = self._user_joinstyle or JoinStyle.miter - - def _set_hexagon2(self): - self._transform = Affine2D().scale(0.5).rotate_deg(30) - self._snap_threshold = None - - polypath = Path.unit_regular_polygon(6) - - if not self._half_fill(): - self._path = polypath - else: - verts = polypath.vertices - # not drawing inside lines - x, y = np.sqrt(3) / 4, 3 / 4. - top = Path(verts[[1, 0, 5, 4, 1]]) - bottom = Path(verts[1:5]) - left = Path(np.concatenate([ - [(x, y)], verts[:3], [(-x, -y), (x, y)]])) - right = Path(np.concatenate([ - [(x, y)], verts[5:2:-1], [(-x, -y)]])) - self._path, self._alt_path = { - 'top': (top, bottom), 'bottom': (bottom, top), - 'left': (left, right), 'right': (right, left), - }[self.get_fillstyle()] - self._alt_transform = self._transform - - self._joinstyle = self._user_joinstyle or JoinStyle.miter - - def _set_octagon(self): - self._transform = Affine2D().scale(0.5) - self._snap_threshold = 5.0 - - polypath = Path.unit_regular_polygon(8) - - if not self._half_fill(): - self._transform.rotate_deg(22.5) - self._path = polypath - else: - x = np.sqrt(2.) / 4. - self._path = self._alt_path = Path( - [[0, -1], [0, 1], [-x, 1], [-1, x], - [-1, -x], [-x, -1], [0, -1]]) - fs = self.get_fillstyle() - self._transform.rotate_deg( - {'left': 0, 'bottom': 90, 'right': 180, 'top': 270}[fs]) - self._alt_transform = self._transform.frozen().rotate_deg(180.0) - - self._joinstyle = self._user_joinstyle or JoinStyle.miter - - _line_marker_path = Path([[0.0, -1.0], [0.0, 1.0]]) - - def _set_vline(self): - self._transform = Affine2D().scale(0.5) - self._snap_threshold = 1.0 - self._filled = False - self._path = self._line_marker_path - - def _set_hline(self): - self._set_vline() - self._transform = self._transform.rotate_deg(90) - - _tickhoriz_path = Path([[0.0, 0.0], [1.0, 0.0]]) - - def _set_tickleft(self): - self._transform = Affine2D().scale(-1.0, 1.0) - self._snap_threshold = 1.0 - self._filled = False - self._path = self._tickhoriz_path - - def _set_tickright(self): - self._transform = Affine2D().scale(1.0, 1.0) - self._snap_threshold = 1.0 - self._filled = False - self._path = self._tickhoriz_path - - _tickvert_path = Path([[-0.0, 0.0], [-0.0, 1.0]]) - - def _set_tickup(self): - self._transform = Affine2D().scale(1.0, 1.0) - self._snap_threshold = 1.0 - self._filled = False - self._path = self._tickvert_path - - def _set_tickdown(self): - self._transform = Affine2D().scale(1.0, -1.0) - self._snap_threshold = 1.0 - self._filled = False - self._path = self._tickvert_path - - _tri_path = Path([[0.0, 0.0], [0.0, -1.0], - [0.0, 0.0], [0.8, 0.5], - [0.0, 0.0], [-0.8, 0.5]], - [Path.MOVETO, Path.LINETO, - Path.MOVETO, Path.LINETO, - Path.MOVETO, Path.LINETO]) - - def _set_tri_down(self): - self._transform = Affine2D().scale(0.5) - self._snap_threshold = 5.0 - self._filled = False - self._path = self._tri_path - - def _set_tri_up(self): - self._set_tri_down() - self._transform = self._transform.rotate_deg(180) - - def _set_tri_left(self): - self._set_tri_down() - self._transform = self._transform.rotate_deg(270) - - def _set_tri_right(self): - self._set_tri_down() - self._transform = self._transform.rotate_deg(90) - - _caret_path = Path([[-1.0, 1.5], [0.0, 0.0], [1.0, 1.5]]) - - def _set_caretdown(self): - self._transform = Affine2D().scale(0.5) - self._snap_threshold = 3.0 - self._filled = False - self._path = self._caret_path - self._joinstyle = self._user_joinstyle or JoinStyle.miter - - def _set_caretup(self): - self._set_caretdown() - self._transform = self._transform.rotate_deg(180) - - def _set_caretleft(self): - self._set_caretdown() - self._transform = self._transform.rotate_deg(270) - - def _set_caretright(self): - self._set_caretdown() - self._transform = self._transform.rotate_deg(90) - - _caret_path_base = Path([[-1.0, 0.0], [0.0, -1.5], [1.0, 0]]) - - def _set_caretdownbase(self): - self._set_caretdown() - self._path = self._caret_path_base - - def _set_caretupbase(self): - self._set_caretdownbase() - self._transform = self._transform.rotate_deg(180) - - def _set_caretleftbase(self): - self._set_caretdownbase() - self._transform = self._transform.rotate_deg(270) - - def _set_caretrightbase(self): - self._set_caretdownbase() - self._transform = self._transform.rotate_deg(90) - - _plus_path = Path([[-1.0, 0.0], [1.0, 0.0], - [0.0, -1.0], [0.0, 1.0]], - [Path.MOVETO, Path.LINETO, - Path.MOVETO, Path.LINETO]) - - def _set_plus(self): - self._transform = Affine2D().scale(0.5) - self._snap_threshold = 1.0 - self._filled = False - self._path = self._plus_path - - _x_path = Path([[-1.0, -1.0], [1.0, 1.0], - [-1.0, 1.0], [1.0, -1.0]], - [Path.MOVETO, Path.LINETO, - Path.MOVETO, Path.LINETO]) - - def _set_x(self): - self._transform = Affine2D().scale(0.5) - self._snap_threshold = 3.0 - self._filled = False - self._path = self._x_path - - _plus_filled_path = Path._create_closed(np.array([ - (-1, -3), (+1, -3), (+1, -1), (+3, -1), (+3, +1), (+1, +1), - (+1, +3), (-1, +3), (-1, +1), (-3, +1), (-3, -1), (-1, -1)]) / 6) - _plus_filled_path_t = Path._create_closed(np.array([ - (+3, 0), (+3, +1), (+1, +1), (+1, +3), - (-1, +3), (-1, +1), (-3, +1), (-3, 0)]) / 6) - - def _set_plus_filled(self): - self._transform = Affine2D() - self._snap_threshold = 5.0 - self._joinstyle = self._user_joinstyle or JoinStyle.miter - if not self._half_fill(): - self._path = self._plus_filled_path - else: - # Rotate top half path to support all partitions - self._path = self._alt_path = self._plus_filled_path_t - fs = self.get_fillstyle() - self._transform.rotate_deg( - {'top': 0, 'left': 90, 'bottom': 180, 'right': 270}[fs]) - self._alt_transform = self._transform.frozen().rotate_deg(180) - - _x_filled_path = Path._create_closed(np.array([ - (-1, -2), (0, -1), (+1, -2), (+2, -1), (+1, 0), (+2, +1), - (+1, +2), (0, +1), (-1, +2), (-2, +1), (-1, 0), (-2, -1)]) / 4) - _x_filled_path_t = Path._create_closed(np.array([ - (+1, 0), (+2, +1), (+1, +2), (0, +1), - (-1, +2), (-2, +1), (-1, 0)]) / 4) - - def _set_x_filled(self): - self._transform = Affine2D() - self._snap_threshold = 5.0 - self._joinstyle = self._user_joinstyle or JoinStyle.miter - if not self._half_fill(): - self._path = self._x_filled_path - else: - # Rotate top half path to support all partitions - self._path = self._alt_path = self._x_filled_path_t - fs = self.get_fillstyle() - self._transform.rotate_deg( - {'top': 0, 'left': 90, 'bottom': 180, 'right': 270}[fs]) - self._alt_transform = self._transform.frozen().rotate_deg(180) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mathtext.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mathtext.py deleted file mode 100644 index fc677e8..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mathtext.py +++ /dev/null @@ -1,287 +0,0 @@ -r""" -A module for parsing a subset of the TeX math syntax and rendering it to a -Matplotlib backend. - -For a tutorial of its usage, see :doc:`/tutorials/text/mathtext`. This -document is primarily concerned with implementation details. - -The module uses pyparsing_ to parse the TeX expression. - -.. _pyparsing: https://pypi.org/project/pyparsing/ - -The Bakoma distribution of the TeX Computer Modern fonts, and STIX -fonts are supported. There is experimental support for using -arbitrary fonts, but results may vary without proper tweaking and -metrics for those fonts. -""" - -from collections import namedtuple -import functools -import logging - -import numpy as np - -import matplotlib as mpl -from matplotlib import _api, _mathtext -from matplotlib.ft2font import FT2Image, LOAD_NO_HINTING -from matplotlib.font_manager import FontProperties -from ._mathtext import ( # noqa: reexported API - RasterParse, VectorParse, get_unicode_index) - -_log = logging.getLogger(__name__) - - -get_unicode_index.__module__ = __name__ - - -@_api.deprecated("3.6") -class MathtextBackend: - """ - The base class for the mathtext backend-specific code. `MathtextBackend` - subclasses interface between mathtext and specific Matplotlib graphics - backends. - - Subclasses need to override the following: - - - :meth:`render_glyph` - - :meth:`render_rect_filled` - - :meth:`get_results` - - And optionally, if you need to use a FreeType hinting style: - - - :meth:`get_hinting_type` - """ - def __init__(self): - self.width = 0 - self.height = 0 - self.depth = 0 - - def set_canvas_size(self, w, h, d): - """Set the dimension of the drawing canvas.""" - self.width = w - self.height = h - self.depth = d - - def render_glyph(self, ox, oy, info): - """ - Draw a glyph described by *info* to the reference point (*ox*, - *oy*). - """ - raise NotImplementedError() - - def render_rect_filled(self, x1, y1, x2, y2): - """ - Draw a filled black rectangle from (*x1*, *y1*) to (*x2*, *y2*). - """ - raise NotImplementedError() - - def get_results(self, box): - """ - Return a backend-specific tuple to return to the backend after - all processing is done. - """ - raise NotImplementedError() - - def get_hinting_type(self): - """ - Get the FreeType hinting type to use with this particular - backend. - """ - return LOAD_NO_HINTING - - -@_api.deprecated("3.6") -class MathtextBackendAgg(MathtextBackend): - """ - Render glyphs and rectangles to an FTImage buffer, which is later - transferred to the Agg image by the Agg backend. - """ - def __init__(self): - self.ox = 0 - self.oy = 0 - self.image = None - self.mode = 'bbox' - self.bbox = [0, 0, 0, 0] - super().__init__() - - def _update_bbox(self, x1, y1, x2, y2): - self.bbox = [min(self.bbox[0], x1), - min(self.bbox[1], y1), - max(self.bbox[2], x2), - max(self.bbox[3], y2)] - - def set_canvas_size(self, w, h, d): - super().set_canvas_size(w, h, d) - if self.mode != 'bbox': - self.image = FT2Image(np.ceil(w), np.ceil(h + max(d, 0))) - - def render_glyph(self, ox, oy, info): - if self.mode == 'bbox': - self._update_bbox(ox + info.metrics.xmin, - oy - info.metrics.ymax, - ox + info.metrics.xmax, - oy - info.metrics.ymin) - else: - info.font.draw_glyph_to_bitmap( - self.image, ox, oy - info.metrics.iceberg, info.glyph, - antialiased=mpl.rcParams['text.antialiased']) - - def render_rect_filled(self, x1, y1, x2, y2): - if self.mode == 'bbox': - self._update_bbox(x1, y1, x2, y2) - else: - height = max(int(y2 - y1) - 1, 0) - if height == 0: - center = (y2 + y1) / 2.0 - y = int(center - (height + 1) / 2.0) - else: - y = int(y1) - self.image.draw_rect_filled(int(x1), y, np.ceil(x2), y + height) - - def get_results(self, box): - self.image = None - self.mode = 'render' - return _mathtext.ship(box).to_raster() - - def get_hinting_type(self): - from matplotlib.backends import backend_agg - return backend_agg.get_hinting_flag() - - -@_api.deprecated("3.6") -class MathtextBackendPath(MathtextBackend): - """ - Store information to write a mathtext rendering to the text path - machinery. - """ - - _Result = namedtuple("_Result", "width height depth glyphs rects") - - def __init__(self): - super().__init__() - self.glyphs = [] - self.rects = [] - - def render_glyph(self, ox, oy, info): - oy = self.height - oy + info.offset - self.glyphs.append((info.font, info.fontsize, info.num, ox, oy)) - - def render_rect_filled(self, x1, y1, x2, y2): - self.rects.append((x1, self.height - y2, x2 - x1, y2 - y1)) - - def get_results(self, box): - return _mathtext.ship(box).to_vector() - - -@_api.deprecated("3.6") -class MathTextWarning(Warning): - pass - - -############################################################################## -# MAIN - - -class MathTextParser: - _parser = None - _font_type_mapping = { - 'cm': _mathtext.BakomaFonts, - 'dejavuserif': _mathtext.DejaVuSerifFonts, - 'dejavusans': _mathtext.DejaVuSansFonts, - 'stix': _mathtext.StixFonts, - 'stixsans': _mathtext.StixSansFonts, - 'custom': _mathtext.UnicodeFonts, - } - - def __init__(self, output): - """ - Create a MathTextParser for the given backend *output*. - - Parameters - ---------- - output : {"path", "agg"} - Whether to return a `VectorParse` ("path") or a - `RasterParse` ("agg", or its synonym "macosx"). - """ - self._output_type = _api.check_getitem( - {"path": "vector", "agg": "raster", "macosx": "raster"}, - output=output.lower()) - - def parse(self, s, dpi=72, prop=None): - """ - Parse the given math expression *s* at the given *dpi*. If *prop* is - provided, it is a `.FontProperties` object specifying the "default" - font to use in the math expression, used for all non-math text. - - The results are cached, so multiple calls to `parse` - with the same expression should be fast. - - Depending on the *output* type, this returns either a `VectorParse` or - a `RasterParse`. - """ - # lru_cache can't decorate parse() directly because prop - # is mutable; key the cache using an internal copy (see - # text._get_text_metrics_with_cache for a similar case). - prop = prop.copy() if prop is not None else None - return self._parse_cached(s, dpi, prop) - - @functools.lru_cache(50) - def _parse_cached(self, s, dpi, prop): - from matplotlib.backends import backend_agg - - if prop is None: - prop = FontProperties() - fontset_class = _api.check_getitem( - self._font_type_mapping, fontset=prop.get_math_fontfamily()) - load_glyph_flags = { - "vector": LOAD_NO_HINTING, - "raster": backend_agg.get_hinting_flag(), - }[self._output_type] - fontset = fontset_class(prop, load_glyph_flags) - - fontsize = prop.get_size_in_points() - - if self._parser is None: # Cache the parser globally. - self.__class__._parser = _mathtext.Parser() - - box = self._parser.parse(s, fontset, fontsize, dpi) - output = _mathtext.ship(box) - if self._output_type == "vector": - return output.to_vector() - elif self._output_type == "raster": - return output.to_raster() - - -def math_to_image(s, filename_or_obj, prop=None, dpi=None, format=None, - *, color=None): - """ - Given a math expression, renders it in a closely-clipped bounding - box to an image file. - - Parameters - ---------- - s : str - A math expression. The math portion must be enclosed in dollar signs. - filename_or_obj : str or path-like or file-like - Where to write the image data. - prop : `.FontProperties`, optional - The size and style of the text. - dpi : float, optional - The output dpi. If not set, the dpi is determined as for - `.Figure.savefig`. - format : str, optional - The output format, e.g., 'svg', 'pdf', 'ps' or 'png'. If not set, the - format is determined as for `.Figure.savefig`. - color : str, optional - Foreground color, defaults to :rc:`text.color`. - """ - from matplotlib import figure - - parser = MathTextParser('path') - width, height, depth, _, _ = parser.parse(s, dpi=72, prop=prop) - - fig = figure.Figure(figsize=(width / 72.0, height / 72.0)) - fig.text(0, depth/height, s, fontproperties=prop, color=color) - fig.savefig(filename_or_obj, dpi=dpi, format=format) - - return depth diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mlab.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mlab.py deleted file mode 100644 index 059cf0f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mlab.py +++ /dev/null @@ -1,987 +0,0 @@ -""" -Numerical Python functions written for compatibility with MATLAB -commands with the same names. Most numerical Python functions can be found in -the `NumPy`_ and `SciPy`_ libraries. What remains here is code for performing -spectral computations and kernel density estimations. - -.. _NumPy: https://numpy.org -.. _SciPy: https://www.scipy.org - -Spectral functions ------------------- - -`cohere` - Coherence (normalized cross spectral density) - -`csd` - Cross spectral density using Welch's average periodogram - -`detrend` - Remove the mean or best fit line from an array - -`psd` - Power spectral density using Welch's average periodogram - -`specgram` - Spectrogram (spectrum over segments of time) - -`complex_spectrum` - Return the complex-valued frequency spectrum of a signal - -`magnitude_spectrum` - Return the magnitude of the frequency spectrum of a signal - -`angle_spectrum` - Return the angle (wrapped phase) of the frequency spectrum of a signal - -`phase_spectrum` - Return the phase (unwrapped angle) of the frequency spectrum of a signal - -`detrend_mean` - Remove the mean from a line. - -`detrend_linear` - Remove the best fit line from a line. - -`detrend_none` - Return the original line. - -`stride_windows` - Get all windows in an array in a memory-efficient manner -""" - -import functools -from numbers import Number - -import numpy as np - -from matplotlib import _api, _docstring, cbook - - -def window_hanning(x): - """ - Return *x* times the Hanning (or Hann) window of len(*x*). - - See Also - -------- - window_none : Another window algorithm. - """ - return np.hanning(len(x))*x - - -def window_none(x): - """ - No window function; simply return *x*. - - See Also - -------- - window_hanning : Another window algorithm. - """ - return x - - -def detrend(x, key=None, axis=None): - """ - Return *x* with its trend removed. - - Parameters - ---------- - x : array or sequence - Array or sequence containing the data. - - key : {'default', 'constant', 'mean', 'linear', 'none'} or function - The detrending algorithm to use. 'default', 'mean', and 'constant' are - the same as `detrend_mean`. 'linear' is the same as `detrend_linear`. - 'none' is the same as `detrend_none`. The default is 'mean'. See the - corresponding functions for more details regarding the algorithms. Can - also be a function that carries out the detrend operation. - - axis : int - The axis along which to do the detrending. - - See Also - -------- - detrend_mean : Implementation of the 'mean' algorithm. - detrend_linear : Implementation of the 'linear' algorithm. - detrend_none : Implementation of the 'none' algorithm. - """ - if key is None or key in ['constant', 'mean', 'default']: - return detrend(x, key=detrend_mean, axis=axis) - elif key == 'linear': - return detrend(x, key=detrend_linear, axis=axis) - elif key == 'none': - return detrend(x, key=detrend_none, axis=axis) - elif callable(key): - x = np.asarray(x) - if axis is not None and axis + 1 > x.ndim: - raise ValueError(f'axis(={axis}) out of bounds') - if (axis is None and x.ndim == 0) or (not axis and x.ndim == 1): - return key(x) - # try to use the 'axis' argument if the function supports it, - # otherwise use apply_along_axis to do it - try: - return key(x, axis=axis) - except TypeError: - return np.apply_along_axis(key, axis=axis, arr=x) - else: - raise ValueError( - f"Unknown value for key: {key!r}, must be one of: 'default', " - f"'constant', 'mean', 'linear', or a function") - - -def detrend_mean(x, axis=None): - """ - Return *x* minus the mean(*x*). - - Parameters - ---------- - x : array or sequence - Array or sequence containing the data - Can have any dimensionality - - axis : int - The axis along which to take the mean. See `numpy.mean` for a - description of this argument. - - See Also - -------- - detrend_linear : Another detrend algorithm. - detrend_none : Another detrend algorithm. - detrend : A wrapper around all the detrend algorithms. - """ - x = np.asarray(x) - - if axis is not None and axis+1 > x.ndim: - raise ValueError('axis(=%s) out of bounds' % axis) - - return x - x.mean(axis, keepdims=True) - - -def detrend_none(x, axis=None): - """ - Return *x*: no detrending. - - Parameters - ---------- - x : any object - An object containing the data - - axis : int - This parameter is ignored. - It is included for compatibility with detrend_mean - - See Also - -------- - detrend_mean : Another detrend algorithm. - detrend_linear : Another detrend algorithm. - detrend : A wrapper around all the detrend algorithms. - """ - return x - - -def detrend_linear(y): - """ - Return *x* minus best fit line; 'linear' detrending. - - Parameters - ---------- - y : 0-D or 1-D array or sequence - Array or sequence containing the data - - See Also - -------- - detrend_mean : Another detrend algorithm. - detrend_none : Another detrend algorithm. - detrend : A wrapper around all the detrend algorithms. - """ - # This is faster than an algorithm based on linalg.lstsq. - y = np.asarray(y) - - if y.ndim > 1: - raise ValueError('y cannot have ndim > 1') - - # short-circuit 0-D array. - if not y.ndim: - return np.array(0., dtype=y.dtype) - - x = np.arange(y.size, dtype=float) - - C = np.cov(x, y, bias=1) - b = C[0, 1]/C[0, 0] - - a = y.mean() - b*x.mean() - return y - (b*x + a) - - -@_api.deprecated("3.6") -def stride_windows(x, n, noverlap=None, axis=0): - """ - Get all windows of *x* with length *n* as a single array, - using strides to avoid data duplication. - - .. warning:: - - It is not safe to write to the output array. Multiple - elements may point to the same piece of memory, - so modifying one value may change others. - - Parameters - ---------- - x : 1D array or sequence - Array or sequence containing the data. - n : int - The number of data points in each window. - noverlap : int, default: 0 (no overlap) - The overlap between adjacent windows. - axis : int - The axis along which the windows will run. - - References - ---------- - `stackoverflow: Rolling window for 1D arrays in Numpy? - `_ - `stackoverflow: Using strides for an efficient moving average filter - `_ - """ - if noverlap is None: - noverlap = 0 - if np.ndim(x) != 1: - raise ValueError('only 1-dimensional arrays can be used') - return _stride_windows(x, n, noverlap, axis) - - -def _stride_windows(x, n, noverlap=0, axis=0): - # np>=1.20 provides sliding_window_view, and we only ever use axis=0. - if hasattr(np.lib.stride_tricks, "sliding_window_view") and axis == 0: - if noverlap >= n: - raise ValueError('noverlap must be less than n') - return np.lib.stride_tricks.sliding_window_view( - x, n, axis=0)[::n - noverlap].T - - if noverlap >= n: - raise ValueError('noverlap must be less than n') - if n < 1: - raise ValueError('n cannot be less than 1') - - x = np.asarray(x) - - if n == 1 and noverlap == 0: - if axis == 0: - return x[np.newaxis] - else: - return x[np.newaxis].T - if n > x.size: - raise ValueError('n cannot be greater than the length of x') - - # np.lib.stride_tricks.as_strided easily leads to memory corruption for - # non integer shape and strides, i.e. noverlap or n. See #3845. - noverlap = int(noverlap) - n = int(n) - - step = n - noverlap - if axis == 0: - shape = (n, (x.shape[-1]-noverlap)//step) - strides = (x.strides[0], step*x.strides[0]) - else: - shape = ((x.shape[-1]-noverlap)//step, n) - strides = (step*x.strides[0], x.strides[0]) - return np.lib.stride_tricks.as_strided(x, shape=shape, strides=strides) - - -def _spectral_helper(x, y=None, NFFT=None, Fs=None, detrend_func=None, - window=None, noverlap=None, pad_to=None, - sides=None, scale_by_freq=None, mode=None): - """ - Private helper implementing the common parts between the psd, csd, - spectrogram and complex, magnitude, angle, and phase spectrums. - """ - if y is None: - # if y is None use x for y - same_data = True - else: - # The checks for if y is x are so that we can use the same function to - # implement the core of psd(), csd(), and spectrogram() without doing - # extra calculations. We return the unaveraged Pxy, freqs, and t. - same_data = y is x - - if Fs is None: - Fs = 2 - if noverlap is None: - noverlap = 0 - if detrend_func is None: - detrend_func = detrend_none - if window is None: - window = window_hanning - - # if NFFT is set to None use the whole signal - if NFFT is None: - NFFT = 256 - - if mode is None or mode == 'default': - mode = 'psd' - _api.check_in_list( - ['default', 'psd', 'complex', 'magnitude', 'angle', 'phase'], - mode=mode) - - if not same_data and mode != 'psd': - raise ValueError("x and y must be equal if mode is not 'psd'") - - # Make sure we're dealing with a numpy array. If y and x were the same - # object to start with, keep them that way - x = np.asarray(x) - if not same_data: - y = np.asarray(y) - - if sides is None or sides == 'default': - if np.iscomplexobj(x): - sides = 'twosided' - else: - sides = 'onesided' - _api.check_in_list(['default', 'onesided', 'twosided'], sides=sides) - - # zero pad x and y up to NFFT if they are shorter than NFFT - if len(x) < NFFT: - n = len(x) - x = np.resize(x, NFFT) - x[n:] = 0 - - if not same_data and len(y) < NFFT: - n = len(y) - y = np.resize(y, NFFT) - y[n:] = 0 - - if pad_to is None: - pad_to = NFFT - - if mode != 'psd': - scale_by_freq = False - elif scale_by_freq is None: - scale_by_freq = True - - # For real x, ignore the negative frequencies unless told otherwise - if sides == 'twosided': - numFreqs = pad_to - if pad_to % 2: - freqcenter = (pad_to - 1)//2 + 1 - else: - freqcenter = pad_to//2 - scaling_factor = 1. - elif sides == 'onesided': - if pad_to % 2: - numFreqs = (pad_to + 1)//2 - else: - numFreqs = pad_to//2 + 1 - scaling_factor = 2. - - if not np.iterable(window): - window = window(np.ones(NFFT, x.dtype)) - if len(window) != NFFT: - raise ValueError( - "The window length must match the data's first dimension") - - result = _stride_windows(x, NFFT, noverlap) - result = detrend(result, detrend_func, axis=0) - result = result * window.reshape((-1, 1)) - result = np.fft.fft(result, n=pad_to, axis=0)[:numFreqs, :] - freqs = np.fft.fftfreq(pad_to, 1/Fs)[:numFreqs] - - if not same_data: - # if same_data is False, mode must be 'psd' - resultY = _stride_windows(y, NFFT, noverlap) - resultY = detrend(resultY, detrend_func, axis=0) - resultY = resultY * window.reshape((-1, 1)) - resultY = np.fft.fft(resultY, n=pad_to, axis=0)[:numFreqs, :] - result = np.conj(result) * resultY - elif mode == 'psd': - result = np.conj(result) * result - elif mode == 'magnitude': - result = np.abs(result) / window.sum() - elif mode == 'angle' or mode == 'phase': - # we unwrap the phase later to handle the onesided vs. twosided case - result = np.angle(result) - elif mode == 'complex': - result /= window.sum() - - if mode == 'psd': - - # Also include scaling factors for one-sided densities and dividing by - # the sampling frequency, if desired. Scale everything, except the DC - # component and the NFFT/2 component: - - # if we have a even number of frequencies, don't scale NFFT/2 - if not NFFT % 2: - slc = slice(1, -1, None) - # if we have an odd number, just don't scale DC - else: - slc = slice(1, None, None) - - result[slc] *= scaling_factor - - # MATLAB divides by the sampling frequency so that density function - # has units of dB/Hz and can be integrated by the plotted frequency - # values. Perform the same scaling here. - if scale_by_freq: - result /= Fs - # Scale the spectrum by the norm of the window to compensate for - # windowing loss; see Bendat & Piersol Sec 11.5.2. - result /= (window**2).sum() - else: - # In this case, preserve power in the segment, not amplitude - result /= window.sum()**2 - - t = np.arange(NFFT/2, len(x) - NFFT/2 + 1, NFFT - noverlap)/Fs - - if sides == 'twosided': - # center the frequency range at zero - freqs = np.roll(freqs, -freqcenter, axis=0) - result = np.roll(result, -freqcenter, axis=0) - elif not pad_to % 2: - # get the last value correctly, it is negative otherwise - freqs[-1] *= -1 - - # we unwrap the phase here to handle the onesided vs. twosided case - if mode == 'phase': - result = np.unwrap(result, axis=0) - - return result, freqs, t - - -def _single_spectrum_helper( - mode, x, Fs=None, window=None, pad_to=None, sides=None): - """ - Private helper implementing the commonality between the complex, magnitude, - angle, and phase spectrums. - """ - _api.check_in_list(['complex', 'magnitude', 'angle', 'phase'], mode=mode) - - if pad_to is None: - pad_to = len(x) - - spec, freqs, _ = _spectral_helper(x=x, y=None, NFFT=len(x), Fs=Fs, - detrend_func=detrend_none, window=window, - noverlap=0, pad_to=pad_to, - sides=sides, - scale_by_freq=False, - mode=mode) - if mode != 'complex': - spec = spec.real - - if spec.ndim == 2 and spec.shape[1] == 1: - spec = spec[:, 0] - - return spec, freqs - - -# Split out these keyword docs so that they can be used elsewhere -_docstring.interpd.update( - Spectral="""\ -Fs : float, default: 2 - The sampling frequency (samples per time unit). It is used to calculate - the Fourier frequencies, *freqs*, in cycles per time unit. - -window : callable or ndarray, default: `.window_hanning` - A function or a vector of length *NFFT*. To create window vectors see - `.window_hanning`, `.window_none`, `numpy.blackman`, `numpy.hamming`, - `numpy.bartlett`, `scipy.signal`, `scipy.signal.get_window`, etc. If a - function is passed as the argument, it must take a data segment as an - argument and return the windowed version of the segment. - -sides : {'default', 'onesided', 'twosided'}, optional - Which sides of the spectrum to return. 'default' is one-sided for real - data and two-sided for complex data. 'onesided' forces the return of a - one-sided spectrum, while 'twosided' forces two-sided.""", - - Single_Spectrum="""\ -pad_to : int, optional - The number of points to which the data segment is padded when performing - the FFT. While not increasing the actual resolution of the spectrum (the - minimum distance between resolvable peaks), this can give more points in - the plot, allowing for more detail. This corresponds to the *n* parameter - in the call to `~numpy.fft.fft`. The default is None, which sets *pad_to* - equal to the length of the input signal (i.e. no padding).""", - - PSD="""\ -pad_to : int, optional - The number of points to which the data segment is padded when performing - the FFT. This can be different from *NFFT*, which specifies the number - of data points used. While not increasing the actual resolution of the - spectrum (the minimum distance between resolvable peaks), this can give - more points in the plot, allowing for more detail. This corresponds to - the *n* parameter in the call to `~numpy.fft.fft`. The default is None, - which sets *pad_to* equal to *NFFT* - -NFFT : int, default: 256 - The number of data points used in each block for the FFT. A power 2 is - most efficient. This should *NOT* be used to get zero padding, or the - scaling of the result will be incorrect; use *pad_to* for this instead. - -detrend : {'none', 'mean', 'linear'} or callable, default: 'none' - The function applied to each segment before fft-ing, designed to remove - the mean or linear trend. Unlike in MATLAB, where the *detrend* parameter - is a vector, in Matplotlib it is a function. The :mod:`~matplotlib.mlab` - module defines `.detrend_none`, `.detrend_mean`, and `.detrend_linear`, - but you can use a custom function as well. You can also use a string to - choose one of the functions: 'none' calls `.detrend_none`. 'mean' calls - `.detrend_mean`. 'linear' calls `.detrend_linear`. - -scale_by_freq : bool, default: True - Whether the resulting density values should be scaled by the scaling - frequency, which gives density in units of 1/Hz. This allows for - integration over the returned frequency values. The default is True for - MATLAB compatibility.""") - - -@_docstring.dedent_interpd -def psd(x, NFFT=None, Fs=None, detrend=None, window=None, - noverlap=None, pad_to=None, sides=None, scale_by_freq=None): - r""" - Compute the power spectral density. - - The power spectral density :math:`P_{xx}` by Welch's average - periodogram method. The vector *x* is divided into *NFFT* length - segments. Each segment is detrended by function *detrend* and - windowed by function *window*. *noverlap* gives the length of - the overlap between segments. The :math:`|\mathrm{fft}(i)|^2` - of each segment :math:`i` are averaged to compute :math:`P_{xx}`. - - If len(*x*) < *NFFT*, it will be zero padded to *NFFT*. - - Parameters - ---------- - x : 1-D array or sequence - Array or sequence containing the data - - %(Spectral)s - - %(PSD)s - - noverlap : int, default: 0 (no overlap) - The number of points of overlap between segments. - - Returns - ------- - Pxx : 1-D array - The values for the power spectrum :math:`P_{xx}` (real valued) - - freqs : 1-D array - The frequencies corresponding to the elements in *Pxx* - - References - ---------- - Bendat & Piersol -- Random Data: Analysis and Measurement Procedures, John - Wiley & Sons (1986) - - See Also - -------- - specgram - `specgram` differs in the default overlap; in not returning the mean of - the segment periodograms; and in returning the times of the segments. - - magnitude_spectrum : returns the magnitude spectrum. - - csd : returns the spectral density between two signals. - """ - Pxx, freqs = csd(x=x, y=None, NFFT=NFFT, Fs=Fs, detrend=detrend, - window=window, noverlap=noverlap, pad_to=pad_to, - sides=sides, scale_by_freq=scale_by_freq) - return Pxx.real, freqs - - -@_docstring.dedent_interpd -def csd(x, y, NFFT=None, Fs=None, detrend=None, window=None, - noverlap=None, pad_to=None, sides=None, scale_by_freq=None): - """ - Compute the cross-spectral density. - - The cross spectral density :math:`P_{xy}` by Welch's average - periodogram method. The vectors *x* and *y* are divided into - *NFFT* length segments. Each segment is detrended by function - *detrend* and windowed by function *window*. *noverlap* gives - the length of the overlap between segments. The product of - the direct FFTs of *x* and *y* are averaged over each segment - to compute :math:`P_{xy}`, with a scaling to correct for power - loss due to windowing. - - If len(*x*) < *NFFT* or len(*y*) < *NFFT*, they will be zero - padded to *NFFT*. - - Parameters - ---------- - x, y : 1-D arrays or sequences - Arrays or sequences containing the data - - %(Spectral)s - - %(PSD)s - - noverlap : int, default: 0 (no overlap) - The number of points of overlap between segments. - - Returns - ------- - Pxy : 1-D array - The values for the cross spectrum :math:`P_{xy}` before scaling (real - valued) - - freqs : 1-D array - The frequencies corresponding to the elements in *Pxy* - - References - ---------- - Bendat & Piersol -- Random Data: Analysis and Measurement Procedures, John - Wiley & Sons (1986) - - See Also - -------- - psd : equivalent to setting ``y = x``. - """ - if NFFT is None: - NFFT = 256 - Pxy, freqs, _ = _spectral_helper(x=x, y=y, NFFT=NFFT, Fs=Fs, - detrend_func=detrend, window=window, - noverlap=noverlap, pad_to=pad_to, - sides=sides, scale_by_freq=scale_by_freq, - mode='psd') - - if Pxy.ndim == 2: - if Pxy.shape[1] > 1: - Pxy = Pxy.mean(axis=1) - else: - Pxy = Pxy[:, 0] - return Pxy, freqs - - -_single_spectrum_docs = """\ -Compute the {quantity} of *x*. -Data is padded to a length of *pad_to* and the windowing function *window* is -applied to the signal. - -Parameters ----------- -x : 1-D array or sequence - Array or sequence containing the data - -{Spectral} - -{Single_Spectrum} - -Returns -------- -spectrum : 1-D array - The {quantity}. -freqs : 1-D array - The frequencies corresponding to the elements in *spectrum*. - -See Also --------- -psd - Returns the power spectral density. -complex_spectrum - Returns the complex-valued frequency spectrum. -magnitude_spectrum - Returns the absolute value of the `complex_spectrum`. -angle_spectrum - Returns the angle of the `complex_spectrum`. -phase_spectrum - Returns the phase (unwrapped angle) of the `complex_spectrum`. -specgram - Can return the complex spectrum of segments within the signal. -""" - - -complex_spectrum = functools.partial(_single_spectrum_helper, "complex") -complex_spectrum.__doc__ = _single_spectrum_docs.format( - quantity="complex-valued frequency spectrum", - **_docstring.interpd.params) -magnitude_spectrum = functools.partial(_single_spectrum_helper, "magnitude") -magnitude_spectrum.__doc__ = _single_spectrum_docs.format( - quantity="magnitude (absolute value) of the frequency spectrum", - **_docstring.interpd.params) -angle_spectrum = functools.partial(_single_spectrum_helper, "angle") -angle_spectrum.__doc__ = _single_spectrum_docs.format( - quantity="angle of the frequency spectrum (wrapped phase spectrum)", - **_docstring.interpd.params) -phase_spectrum = functools.partial(_single_spectrum_helper, "phase") -phase_spectrum.__doc__ = _single_spectrum_docs.format( - quantity="phase of the frequency spectrum (unwrapped phase spectrum)", - **_docstring.interpd.params) - - -@_docstring.dedent_interpd -def specgram(x, NFFT=None, Fs=None, detrend=None, window=None, - noverlap=None, pad_to=None, sides=None, scale_by_freq=None, - mode=None): - """ - Compute a spectrogram. - - Compute and plot a spectrogram of data in *x*. Data are split into - *NFFT* length segments and the spectrum of each section is - computed. The windowing function *window* is applied to each - segment, and the amount of overlap of each segment is - specified with *noverlap*. - - Parameters - ---------- - x : array-like - 1-D array or sequence. - - %(Spectral)s - - %(PSD)s - - noverlap : int, default: 128 - The number of points of overlap between blocks. - mode : str, default: 'psd' - What sort of spectrum to use: - 'psd' - Returns the power spectral density. - 'complex' - Returns the complex-valued frequency spectrum. - 'magnitude' - Returns the magnitude spectrum. - 'angle' - Returns the phase spectrum without unwrapping. - 'phase' - Returns the phase spectrum with unwrapping. - - Returns - ------- - spectrum : array-like - 2D array, columns are the periodograms of successive segments. - - freqs : array-like - 1-D array, frequencies corresponding to the rows in *spectrum*. - - t : array-like - 1-D array, the times corresponding to midpoints of segments - (i.e the columns in *spectrum*). - - See Also - -------- - psd : differs in the overlap and in the return values. - complex_spectrum : similar, but with complex valued frequencies. - magnitude_spectrum : similar single segment when *mode* is 'magnitude'. - angle_spectrum : similar to single segment when *mode* is 'angle'. - phase_spectrum : similar to single segment when *mode* is 'phase'. - - Notes - ----- - *detrend* and *scale_by_freq* only apply when *mode* is set to 'psd'. - - """ - if noverlap is None: - noverlap = 128 # default in _spectral_helper() is noverlap = 0 - if NFFT is None: - NFFT = 256 # same default as in _spectral_helper() - if len(x) <= NFFT: - _api.warn_external("Only one segment is calculated since parameter " - f"NFFT (={NFFT}) >= signal length (={len(x)}).") - - spec, freqs, t = _spectral_helper(x=x, y=None, NFFT=NFFT, Fs=Fs, - detrend_func=detrend, window=window, - noverlap=noverlap, pad_to=pad_to, - sides=sides, - scale_by_freq=scale_by_freq, - mode=mode) - - if mode != 'complex': - spec = spec.real # Needed since helper implements generically - - return spec, freqs, t - - -@_docstring.dedent_interpd -def cohere(x, y, NFFT=256, Fs=2, detrend=detrend_none, window=window_hanning, - noverlap=0, pad_to=None, sides='default', scale_by_freq=None): - r""" - The coherence between *x* and *y*. Coherence is the normalized - cross spectral density: - - .. math:: - - C_{xy} = \frac{|P_{xy}|^2}{P_{xx}P_{yy}} - - Parameters - ---------- - x, y - Array or sequence containing the data - - %(Spectral)s - - %(PSD)s - - noverlap : int, default: 0 (no overlap) - The number of points of overlap between segments. - - Returns - ------- - Cxy : 1-D array - The coherence vector. - freqs : 1-D array - The frequencies for the elements in *Cxy*. - - See Also - -------- - :func:`psd`, :func:`csd` : - For information about the methods used to compute :math:`P_{xy}`, - :math:`P_{xx}` and :math:`P_{yy}`. - """ - if len(x) < 2 * NFFT: - raise ValueError( - "Coherence is calculated by averaging over *NFFT* length " - "segments. Your signal is too short for your choice of *NFFT*.") - Pxx, f = psd(x, NFFT, Fs, detrend, window, noverlap, pad_to, sides, - scale_by_freq) - Pyy, f = psd(y, NFFT, Fs, detrend, window, noverlap, pad_to, sides, - scale_by_freq) - Pxy, f = csd(x, y, NFFT, Fs, detrend, window, noverlap, pad_to, sides, - scale_by_freq) - Cxy = np.abs(Pxy) ** 2 / (Pxx * Pyy) - return Cxy, f - - -class GaussianKDE: - """ - Representation of a kernel-density estimate using Gaussian kernels. - - Parameters - ---------- - dataset : array-like - Datapoints to estimate from. In case of univariate data this is a 1-D - array, otherwise a 2D array with shape (# of dims, # of data). - bw_method : str, scalar or callable, optional - The method used to calculate the estimator bandwidth. This can be - 'scott', 'silverman', a scalar constant or a callable. If a - scalar, this will be used directly as `kde.factor`. If a - callable, it should take a `GaussianKDE` instance as only - parameter and return a scalar. If None (default), 'scott' is used. - - Attributes - ---------- - dataset : ndarray - The dataset passed to the constructor. - dim : int - Number of dimensions. - num_dp : int - Number of datapoints. - factor : float - The bandwidth factor, obtained from `kde.covariance_factor`, with which - the covariance matrix is multiplied. - covariance : ndarray - The covariance matrix of *dataset*, scaled by the calculated bandwidth - (`kde.factor`). - inv_cov : ndarray - The inverse of *covariance*. - - Methods - ------- - kde.evaluate(points) : ndarray - Evaluate the estimated pdf on a provided set of points. - kde(points) : ndarray - Same as kde.evaluate(points) - """ - - # This implementation with minor modification was too good to pass up. - # from scipy: https://github.com/scipy/scipy/blob/master/scipy/stats/kde.py - - def __init__(self, dataset, bw_method=None): - self.dataset = np.atleast_2d(dataset) - if not np.array(self.dataset).size > 1: - raise ValueError("`dataset` input should have multiple elements.") - - self.dim, self.num_dp = np.array(self.dataset).shape - - if bw_method is None: - pass - elif cbook._str_equal(bw_method, 'scott'): - self.covariance_factor = self.scotts_factor - elif cbook._str_equal(bw_method, 'silverman'): - self.covariance_factor = self.silverman_factor - elif isinstance(bw_method, Number): - self._bw_method = 'use constant' - self.covariance_factor = lambda: bw_method - elif callable(bw_method): - self._bw_method = bw_method - self.covariance_factor = lambda: self._bw_method(self) - else: - raise ValueError("`bw_method` should be 'scott', 'silverman', a " - "scalar or a callable") - - # Computes the covariance matrix for each Gaussian kernel using - # covariance_factor(). - - self.factor = self.covariance_factor() - # Cache covariance and inverse covariance of the data - if not hasattr(self, '_data_inv_cov'): - self.data_covariance = np.atleast_2d( - np.cov( - self.dataset, - rowvar=1, - bias=False)) - self.data_inv_cov = np.linalg.inv(self.data_covariance) - - self.covariance = self.data_covariance * self.factor ** 2 - self.inv_cov = self.data_inv_cov / self.factor ** 2 - self.norm_factor = (np.sqrt(np.linalg.det(2 * np.pi * self.covariance)) - * self.num_dp) - - def scotts_factor(self): - return np.power(self.num_dp, -1. / (self.dim + 4)) - - def silverman_factor(self): - return np.power( - self.num_dp * (self.dim + 2.0) / 4.0, -1. / (self.dim + 4)) - - # Default method to calculate bandwidth, can be overwritten by subclass - covariance_factor = scotts_factor - - def evaluate(self, points): - """ - Evaluate the estimated pdf on a set of points. - - Parameters - ---------- - points : (# of dimensions, # of points)-array - Alternatively, a (# of dimensions,) vector can be passed in and - treated as a single point. - - Returns - ------- - (# of points,)-array - The values at each point. - - Raises - ------ - ValueError : if the dimensionality of the input points is different - than the dimensionality of the KDE. - - """ - points = np.atleast_2d(points) - - dim, num_m = np.array(points).shape - if dim != self.dim: - raise ValueError("points have dimension {}, dataset has dimension " - "{}".format(dim, self.dim)) - - result = np.zeros(num_m) - - if num_m >= self.num_dp: - # there are more points than data, so loop over data - for i in range(self.num_dp): - diff = self.dataset[:, i, np.newaxis] - points - tdiff = np.dot(self.inv_cov, diff) - energy = np.sum(diff * tdiff, axis=0) / 2.0 - result = result + np.exp(-energy) - else: - # loop over points - for i in range(num_m): - diff = self.dataset - points[:, i, np.newaxis] - tdiff = np.dot(self.inv_cov, diff) - energy = np.sum(diff * tdiff, axis=0) / 2.0 - result[i] = np.sum(np.exp(-energy), axis=0) - - result = result / self.norm_factor - - return result - - __call__ = evaluate diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/cmex10.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/cmex10.afm deleted file mode 100644 index b9e318f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/cmex10.afm +++ /dev/null @@ -1,220 +0,0 @@ -StartFontMetrics 2.0 -Comment Creation Date: Thu Jun 21 22:23:20 1990 -Comment UniqueID 5000774 -FontName CMEX10 -EncodingScheme FontSpecific -FullName CMEX10 -FamilyName Computer Modern -Weight Medium -ItalicAngle 0 -IsFixedPitch false -Version 1.00 -Notice Copyright (c) 1997 American Mathematical Society. All Rights Reserved. -Comment Computer Modern fonts were designed by Donald E. Knuth -FontBBox -24 -2960 1454 772 -XHeight 430.556 -Comment CapHeight 0 -Ascender 750 -Comment Descender -1760 -Descender -2960 -Comment FontID CMEX -Comment DesignSize 10 (pts) -Comment CharacterCodingScheme TeX math extension -Comment Space 0 0 0 -Comment ExtraSpace 0 -Comment Quad 1000 -Comment DefaultRuleThickness 40 -Comment BigOpSpacing 111.111 166.667 200 600 100 -Comment Ascendible characters (74) % macro - PS charname -Comment Ascending 0, 16, 18, 32, 48 % ( - parenleft -Comment Ascending 1, 17, 19, 33, 49 % ) - parenright -Comment Ascending 2, 104, 20, 34, 50 % [ - bracketleft -Comment Ascending 3, 105, 21, 35, 51 % ] - bracketright -Comment Ascending 4, 106, 22, 36, 52 % lfloor - floorleft -Comment Ascending 5, 107, 23, 37, 53 % rfloor - floorright -Comment Ascending 6, 108, 24, 38, 54 % lceil - ceilingleft -Comment Ascending 7, 109, 25, 39, 55 % rceil - ceilingright -Comment Ascending 8, 110, 26, 40, 56 % { - braceleft -Comment Ascending 9, 111, 27, 41, 57 % } - braceright -Comment Ascending 10, 68, 28, 42 % < - anglebracketleft -Comment Ascending 11, 69, 29, 43 % > - anglebracketright -Comment Ascending 14, 46, 30, 44 % / - slash -Comment Ascending 15, 47, 31, 45 % \ - backslash -Comment Ascending 70, 71 % bigsqcup - unionsq -Comment Ascending 72, 73 % oint - contintegral -Comment Ascending 74, 75 % bigodot - circledot -Comment Ascending 76, 77 % bigoplus - circleplus -Comment Ascending 78, 79 % bigotimes - circlemultiply -Comment Ascending 80, 88 % sum - summation -Comment Ascending 81, 89 % prod - product -Comment Ascending 82, 90 % int - integral -Comment Ascending 83, 91 % bigcup - union -Comment Ascending 84, 92 % bigcap - intersection -Comment Ascending 85, 93 % biguplus - unionmulti -Comment Ascending 86, 94 % bigwedge - logicaland -Comment Ascending 87, 95 % bigvee - logicalor -Comment Ascending 96, 97 % coprod - coproduct -Comment Ascending 98, 99, 100 % widehat - hatwide -Comment Ascending 101, 102, 103 % widetilde - tildewide -Comment Ascending 112, 113, 114, 115, 116 % radical - sqrt -Comment Extensible characters (28) -Comment Extensible 12 top 0 mid 0 bot 0 rep 12 % vert - thin bar -Comment Extensible 13 top 0 mid 0 bot 0 rep 13 % Vert - thin double bar -Comment Extensible 48 top 48 mid 0 bot 64 rep 66 % ( - parenleft -Comment Extensible 49 top 49 mid 0 bot 65 rep 67 % ) - parenright -Comment Extensible 50 top 50 mid 0 bot 52 rep 54 % [ - bracketleft -Comment Extensible 51 top 51 mid 0 bot 53 rep 55 % ] - bracketright -Comment Extensible 52 top 0 mid 0 bot 52 rep 54 % lfloor - floorleft -Comment Extensible 53 top 0 mid 0 bot 53 rep 55 % rfloor - floorright -Comment Extensible 54 top 50 mid 0 bot 0 rep 54 % lceil - ceilingleft -Comment Extensible 55 top 51 mid 0 bot 0 rep 55 % rceil - ceilingright -Comment Extensible 56 top 56 mid 60 bot 58 rep 62 % { - braceleft -Comment Extensible 57 top 57 mid 61 bot 59 rep 62 % } - braceright -Comment Extensible 58 top 56 mid 0 bot 58 rep 62 % lgroup -Comment Extensible 59 top 57 mid 0 bot 59 rep 62 % rgroup -Comment Extensible 60 top 0 mid 0 bot 0 rep 63 % arrowvert -Comment Extensible 61 top 0 mid 0 bot 0 rep 119 % Arrowvert -Comment Extensible 62 top 0 mid 0 bot 0 rep 62 % bracevert -Comment Extensible 63 top 120 mid 0 bot 121 rep 63 % updownarrow -Comment Extensible 64 top 56 mid 0 bot 59 rep 62 % lmoustache -Comment Extensible 65 top 57 mid 0 bot 58 rep 62 % rmoustache -Comment Extensible 66 top 0 mid 0 bot 0 rep 66 % parenleftexten -Comment Extensible 67 top 0 mid 0 bot 0 rep 67 % parenrightexten -Comment Extensible 116 top 118 mid 0 bot 116 rep 117 % radical -Comment Extensible 119 top 126 mid 0 bot 127 rep 119 % Updownarrow -Comment Extensible 120 top 120 mid 0 bot 0 rep 63 % uparrow -Comment Extensible 121 top 0 mid 0 bot 121 rep 63 % downarrow -Comment Extensible 126 top 126 mid 0 bot 0 rep 119 % Uparrow -Comment Extensible 127 top 0 mid 0 bot 127 rep 119 % Downarrow -StartCharMetrics 129 -C 0 ; WX 458.333 ; N parenleftbig ; B 152 -1159 413 40 ; -C 1 ; WX 458.333 ; N parenrightbig ; B 44 -1159 305 40 ; -C 2 ; WX 416.667 ; N bracketleftbig ; B 202 -1159 394 40 ; -C 3 ; WX 416.667 ; N bracketrightbig ; B 22 -1159 214 40 ; -C 4 ; WX 472.222 ; N floorleftbig ; B 202 -1159 449 40 ; -C 5 ; WX 472.222 ; N floorrightbig ; B 22 -1159 269 40 ; -C 6 ; WX 472.222 ; N ceilingleftbig ; B 202 -1159 449 40 ; -C 7 ; WX 472.222 ; N ceilingrightbig ; B 22 -1159 269 40 ; -C 8 ; WX 583.333 ; N braceleftbig ; B 113 -1159 469 40 ; -C 9 ; WX 583.333 ; N bracerightbig ; B 113 -1159 469 40 ; -C 10 ; WX 472.222 ; N angbracketleftbig ; B 98 -1160 393 40 ; -C 11 ; WX 472.222 ; N angbracketrightbig ; B 78 -1160 373 40 ; -C 12 ; WX 333.333 ; N vextendsingle ; B 145 -621 188 21 ; -C 13 ; WX 555.556 ; N vextenddouble ; B 145 -621 410 21 ; -C 14 ; WX 577.778 ; N slashbig ; B 56 -1159 521 40 ; -C 15 ; WX 577.778 ; N backslashbig ; B 56 -1159 521 40 ; -C 16 ; WX 597.222 ; N parenleftBig ; B 180 -1759 560 40 ; -C 17 ; WX 597.222 ; N parenrightBig ; B 36 -1759 416 40 ; -C 18 ; WX 736.111 ; N parenleftbigg ; B 208 -2359 700 40 ; -C 19 ; WX 736.111 ; N parenrightbigg ; B 35 -2359 527 40 ; -C 20 ; WX 527.778 ; N bracketleftbigg ; B 250 -2359 513 40 ; -C 21 ; WX 527.778 ; N bracketrightbigg ; B 14 -2359 277 40 ; -C 22 ; WX 583.333 ; N floorleftbigg ; B 250 -2359 568 40 ; -C 23 ; WX 583.333 ; N floorrightbigg ; B 14 -2359 332 40 ; -C 24 ; WX 583.333 ; N ceilingleftbigg ; B 250 -2359 568 40 ; -C 25 ; WX 583.333 ; N ceilingrightbigg ; B 14 -2359 332 40 ; -C 26 ; WX 750 ; N braceleftbigg ; B 131 -2359 618 40 ; -C 27 ; WX 750 ; N bracerightbigg ; B 131 -2359 618 40 ; -C 28 ; WX 750 ; N angbracketleftbigg ; B 125 -2359 652 40 ; -C 29 ; WX 750 ; N angbracketrightbigg ; B 97 -2359 624 40 ; -C 30 ; WX 1044.44 ; N slashbigg ; B 56 -2359 987 40 ; -C 31 ; WX 1044.44 ; N backslashbigg ; B 56 -2359 987 40 ; -C 32 ; WX 791.667 ; N parenleftBigg ; B 236 -2959 757 40 ; -C 33 ; WX 791.667 ; N parenrightBigg ; B 34 -2959 555 40 ; -C 34 ; WX 583.333 ; N bracketleftBigg ; B 275 -2959 571 40 ; -C 35 ; WX 583.333 ; N bracketrightBigg ; B 11 -2959 307 40 ; -C 36 ; WX 638.889 ; N floorleftBigg ; B 275 -2959 627 40 ; -C 37 ; WX 638.889 ; N floorrightBigg ; B 11 -2959 363 40 ; -C 38 ; WX 638.889 ; N ceilingleftBigg ; B 275 -2959 627 40 ; -C 39 ; WX 638.889 ; N ceilingrightBigg ; B 11 -2959 363 40 ; -C 40 ; WX 805.556 ; N braceleftBigg ; B 144 -2959 661 40 ; -C 41 ; WX 805.556 ; N bracerightBigg ; B 144 -2959 661 40 ; -C 42 ; WX 805.556 ; N angbracketleftBigg ; B 139 -2960 697 40 ; -C 43 ; WX 805.556 ; N angbracketrightBigg ; B 108 -2960 666 40 ; -C 44 ; WX 1277.78 ; N slashBigg ; B 56 -2959 1221 40 ; -C 45 ; WX 1277.78 ; N backslashBigg ; B 56 -2959 1221 40 ; -C 46 ; WX 811.111 ; N slashBig ; B 56 -1759 754 40 ; -C 47 ; WX 811.111 ; N backslashBig ; B 56 -1759 754 40 ; -C 48 ; WX 875 ; N parenlefttp ; B 291 -1770 842 39 ; -C 49 ; WX 875 ; N parenrighttp ; B 32 -1770 583 39 ; -C 50 ; WX 666.667 ; N bracketlefttp ; B 326 -1760 659 39 ; -C 51 ; WX 666.667 ; N bracketrighttp ; B 7 -1760 340 39 ; -C 52 ; WX 666.667 ; N bracketleftbt ; B 326 -1759 659 40 ; -C 53 ; WX 666.667 ; N bracketrightbt ; B 7 -1759 340 40 ; -C 54 ; WX 666.667 ; N bracketleftex ; B 326 -601 395 1 ; -C 55 ; WX 666.667 ; N bracketrightex ; B 271 -601 340 1 ; -C 56 ; WX 888.889 ; N bracelefttp ; B 384 -910 718 -1 ; -C 57 ; WX 888.889 ; N bracerighttp ; B 170 -910 504 -1 ; -C 58 ; WX 888.889 ; N braceleftbt ; B 384 -899 718 10 ; -C 59 ; WX 888.889 ; N bracerightbt ; B 170 -899 504 10 ; -C 60 ; WX 888.889 ; N braceleftmid ; B 170 -1810 504 10 ; -C 61 ; WX 888.889 ; N bracerightmid ; B 384 -1810 718 10 ; -C 62 ; WX 888.889 ; N braceex ; B 384 -310 504 10 ; -C 63 ; WX 666.667 ; N arrowvertex ; B 312 -601 355 1 ; -C 64 ; WX 875 ; N parenleftbt ; B 291 -1759 842 50 ; -C 65 ; WX 875 ; N parenrightbt ; B 32 -1759 583 50 ; -C 66 ; WX 875 ; N parenleftex ; B 291 -610 402 10 ; -C 67 ; WX 875 ; N parenrightex ; B 472 -610 583 10 ; -C 68 ; WX 611.111 ; N angbracketleftBig ; B 112 -1759 522 40 ; -C 69 ; WX 611.111 ; N angbracketrightBig ; B 88 -1759 498 40 ; -C 70 ; WX 833.333 ; N unionsqtext ; B 56 -1000 776 0 ; -C 71 ; WX 1111.11 ; N unionsqdisplay ; B 56 -1400 1054 0 ; -C 72 ; WX 472.222 ; N contintegraltext ; B 56 -1111 609 0 ; -C 73 ; WX 555.556 ; N contintegraldisplay ; B 56 -2222 943 0 ; -C 74 ; WX 1111.11 ; N circledottext ; B 56 -1000 1054 0 ; -C 75 ; WX 1511.11 ; N circledotdisplay ; B 56 -1400 1454 0 ; -C 76 ; WX 1111.11 ; N circleplustext ; B 56 -1000 1054 0 ; -C 77 ; WX 1511.11 ; N circleplusdisplay ; B 56 -1400 1454 0 ; -C 78 ; WX 1111.11 ; N circlemultiplytext ; B 56 -1000 1054 0 ; -C 79 ; WX 1511.11 ; N circlemultiplydisplay ; B 56 -1400 1454 0 ; -C 80 ; WX 1055.56 ; N summationtext ; B 56 -1000 999 0 ; -C 81 ; WX 944.444 ; N producttext ; B 56 -1000 887 0 ; -C 82 ; WX 472.222 ; N integraltext ; B 56 -1111 609 0 ; -C 83 ; WX 833.333 ; N uniontext ; B 56 -1000 776 0 ; -C 84 ; WX 833.333 ; N intersectiontext ; B 56 -1000 776 0 ; -C 85 ; WX 833.333 ; N unionmultitext ; B 56 -1000 776 0 ; -C 86 ; WX 833.333 ; N logicalandtext ; B 56 -1000 776 0 ; -C 87 ; WX 833.333 ; N logicalortext ; B 56 -1000 776 0 ; -C 88 ; WX 1444.44 ; N summationdisplay ; B 56 -1400 1387 0 ; -C 89 ; WX 1277.78 ; N productdisplay ; B 56 -1400 1221 0 ; -C 90 ; WX 555.556 ; N integraldisplay ; B 56 -2222 943 0 ; -C 91 ; WX 1111.11 ; N uniondisplay ; B 56 -1400 1054 0 ; -C 92 ; WX 1111.11 ; N intersectiondisplay ; B 56 -1400 1054 0 ; -C 93 ; WX 1111.11 ; N unionmultidisplay ; B 56 -1400 1054 0 ; -C 94 ; WX 1111.11 ; N logicalanddisplay ; B 56 -1400 1054 0 ; -C 95 ; WX 1111.11 ; N logicalordisplay ; B 56 -1400 1054 0 ; -C 96 ; WX 944.444 ; N coproducttext ; B 56 -1000 887 0 ; -C 97 ; WX 1277.78 ; N coproductdisplay ; B 56 -1400 1221 0 ; -C 98 ; WX 555.556 ; N hatwide ; B -5 562 561 744 ; -C 99 ; WX 1000 ; N hatwider ; B -4 575 1003 772 ; -C 100 ; WX 1444.44 ; N hatwidest ; B -3 575 1446 772 ; -C 101 ; WX 555.556 ; N tildewide ; B 0 608 555 722 ; -C 102 ; WX 1000 ; N tildewider ; B 0 624 999 750 ; -C 103 ; WX 1444.44 ; N tildewidest ; B 0 623 1443 750 ; -C 104 ; WX 472.222 ; N bracketleftBig ; B 226 -1759 453 40 ; -C 105 ; WX 472.222 ; N bracketrightBig ; B 18 -1759 245 40 ; -C 106 ; WX 527.778 ; N floorleftBig ; B 226 -1759 509 40 ; -C 107 ; WX 527.778 ; N floorrightBig ; B 18 -1759 301 40 ; -C 108 ; WX 527.778 ; N ceilingleftBig ; B 226 -1759 509 40 ; -C 109 ; WX 527.778 ; N ceilingrightBig ; B 18 -1759 301 40 ; -C 110 ; WX 666.667 ; N braceleftBig ; B 119 -1759 547 40 ; -C 111 ; WX 666.667 ; N bracerightBig ; B 119 -1759 547 40 ; -C 112 ; WX 1000 ; N radicalbig ; B 110 -1160 1020 40 ; -C 113 ; WX 1000 ; N radicalBig ; B 110 -1760 1020 40 ; -C 114 ; WX 1000 ; N radicalbigg ; B 111 -2360 1020 40 ; -C 115 ; WX 1000 ; N radicalBigg ; B 111 -2960 1020 40 ; -C 116 ; WX 1055.56 ; N radicalbt ; B 111 -1800 742 20 ; -C 117 ; WX 1055.56 ; N radicalvertex ; B 702 -620 742 20 ; -C 118 ; WX 1055.56 ; N radicaltp ; B 702 -580 1076 40 ; -C 119 ; WX 777.778 ; N arrowvertexdbl ; B 257 -601 521 1 ; -C 120 ; WX 666.667 ; N arrowtp ; B 111 -600 556 0 ; -C 121 ; WX 666.667 ; N arrowbt ; B 111 -600 556 0 ; -C 122 ; WX 450 ; N bracehtipdownleft ; B -24 -214 460 120 ; -C 123 ; WX 450 ; N bracehtipdownright ; B -10 -214 474 120 ; -C 124 ; WX 450 ; N bracehtipupleft ; B -24 0 460 334 ; -C 125 ; WX 450 ; N bracehtipupright ; B -10 0 474 334 ; -C 126 ; WX 777.778 ; N arrowdbltp ; B 56 -600 722 -1 ; -C 127 ; WX 777.778 ; N arrowdblbt ; B 56 -599 722 0 ; -C -1 ; WX 333.333 ; N space ; B 0 0 0 0 ; -EndCharMetrics -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/cmmi10.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/cmmi10.afm deleted file mode 100644 index f47d6ba..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/cmmi10.afm +++ /dev/null @@ -1,326 +0,0 @@ -StartFontMetrics 2.0 -Comment Creation Date: Thu Jun 21 22:23:22 1990 -Comment UniqueID 5000785 -FontName CMMI10 -EncodingScheme FontSpecific -FullName CMMI10 -FamilyName Computer Modern -Weight Medium -ItalicAngle -14.04 -IsFixedPitch false -Version 1.00A -Notice Copyright (c) 1997 American Mathematical Society. All Rights Reserved. -Comment Computer Modern fonts were designed by Donald E. Knuth -FontBBox -32 -250 1048 750 -CapHeight 683.333 -XHeight 430.556 -Ascender 694.444 -Descender -194.444 -Comment FontID CMMI -Comment DesignSize 10 (pts) -Comment CharacterCodingScheme TeX math italic -Comment Space 0 0 0 -Comment Quad 1000 -StartCharMetrics 129 -C 0 ; WX 615.276 ; N Gamma ; B 39 0 723 680 ; -C 1 ; WX 833.333 ; N Delta ; B 49 0 787 716 ; -C 2 ; WX 762.774 ; N Theta ; B 50 -22 739 705 ; -C 3 ; WX 694.444 ; N Lambda ; B 35 0 666 716 ; -C 4 ; WX 742.361 ; N Xi ; B 53 0 777 677 ; -C 5 ; WX 831.25 ; N Pi ; B 39 0 880 680 ; -C 6 ; WX 779.861 ; N Sigma ; B 59 0 807 683 ; -C 7 ; WX 583.333 ; N Upsilon ; B 29 0 700 705 ; -C 8 ; WX 666.667 ; N Phi ; B 24 0 642 683 ; -C 9 ; WX 612.221 ; N Psi ; B 28 0 692 683 ; -C 10 ; WX 772.396 ; N Omega ; B 80 0 785 705 ; -C 11 ; WX 639.7 ; N alpha ; B 41 -11 601 442 ; -C 12 ; WX 565.625 ; N beta ; B 25 -194 590 705 ; -C 13 ; WX 517.73 ; N gamma ; B 18 -215 542 442 ; -C 14 ; WX 444.444 ; N delta ; B 41 -12 452 705 ; -C 15 ; WX 405.902 ; N epsilon1 ; B 47 -11 376 431 ; -C 16 ; WX 437.5 ; N zeta ; B 47 -205 474 697 ; -C 17 ; WX 496.53 ; N eta ; B 29 -216 496 442 ; -C 18 ; WX 469.442 ; N theta ; B 42 -11 455 705 ; -C 19 ; WX 353.935 ; N iota ; B 56 -11 324 442 ; -C 20 ; WX 576.159 ; N kappa ; B 55 -11 546 442 ; -C 21 ; WX 583.333 ; N lambda ; B 53 -13 547 694 ; -C 22 ; WX 602.548 ; N mu ; B 30 -216 572 442 ; -C 23 ; WX 493.981 ; N nu ; B 53 0 524 442 ; -C 24 ; WX 437.5 ; N xi ; B 24 -205 446 697 ; -C 25 ; WX 570.025 ; N pi ; B 27 -11 567 431 ; -C 26 ; WX 517.014 ; N rho ; B 30 -216 502 442 ; -C 27 ; WX 571.429 ; N sigma ; B 38 -11 567 431 ; -C 28 ; WX 437.153 ; N tau ; B 27 -12 511 431 ; -C 29 ; WX 540.278 ; N upsilon ; B 29 -11 524 443 ; -C 30 ; WX 595.833 ; N phi ; B 49 -205 573 694 ; -C 31 ; WX 625.691 ; N chi ; B 32 -205 594 442 ; -C 32 ; WX 651.39 ; N psi ; B 29 -205 635 694 ; -C 33 ; WX 622.453 ; N omega ; B 13 -11 605 443 ; -C 34 ; WX 466.316 ; N epsilon ; B 27 -22 428 453 ; -C 35 ; WX 591.438 ; N theta1 ; B 29 -11 561 705 ; -C 36 ; WX 828.125 ; N pi1 ; B 27 -11 817 431 ; -C 37 ; WX 517.014 ; N rho1 ; B 74 -194 502 442 ; -C 38 ; WX 362.846 ; N sigma1 ; B 32 -108 408 442 ; -C 39 ; WX 654.165 ; N phi1 ; B 50 -218 619 442 ; -C 40 ; WX 1000 ; N arrowlefttophalf ; B 56 230 943 428 ; -C 41 ; WX 1000 ; N arrowleftbothalf ; B 56 72 943 270 ; -C 42 ; WX 1000 ; N arrowrighttophalf ; B 56 230 943 428 ; -C 43 ; WX 1000 ; N arrowrightbothalf ; B 56 72 943 270 ; -C 44 ; WX 277.778 ; N arrowhookleft ; B 56 230 221 464 ; -C 45 ; WX 277.778 ; N arrowhookright ; B 56 230 221 464 ; -C 46 ; WX 500 ; N triangleright ; B 27 -4 472 504 ; -C 47 ; WX 500 ; N triangleleft ; B 27 -4 472 504 ; -C 48 ; WX 500 ; N zerooldstyle ; B 40 -22 459 453 ; -C 49 ; WX 500 ; N oneoldstyle ; B 92 0 418 453 ; -C 50 ; WX 500 ; N twooldstyle ; B 44 0 449 453 ; -C 51 ; WX 500 ; N threeoldstyle ; B 42 -216 457 453 ; -C 52 ; WX 500 ; N fouroldstyle ; B 28 -194 471 464 ; -C 53 ; WX 500 ; N fiveoldstyle ; B 50 -216 449 453 ; -C 54 ; WX 500 ; N sixoldstyle ; B 42 -22 457 666 ; -C 55 ; WX 500 ; N sevenoldstyle ; B 56 -216 485 463 ; -C 56 ; WX 500 ; N eightoldstyle ; B 42 -22 457 666 ; -C 57 ; WX 500 ; N nineoldstyle ; B 42 -216 457 453 ; -C 58 ; WX 277.778 ; N period ; B 86 0 192 106 ; -C 59 ; WX 277.778 ; N comma ; B 86 -193 203 106 ; -C 60 ; WX 777.778 ; N less ; B 83 -39 694 539 ; -C 61 ; WX 500 ; N slash ; B 56 -250 443 750 ; -C 62 ; WX 777.778 ; N greater ; B 83 -39 694 539 ; -C 63 ; WX 500 ; N star ; B 4 16 496 486 ; -C 64 ; WX 530.902 ; N partialdiff ; B 40 -22 566 716 ; -C 65 ; WX 750 ; N A ; B 35 0 722 716 ; -C 66 ; WX 758.508 ; N B ; B 42 0 756 683 ; -C 67 ; WX 714.72 ; N C ; B 51 -22 759 705 ; -C 68 ; WX 827.915 ; N D ; B 41 0 803 683 ; -C 69 ; WX 738.193 ; N E ; B 39 0 765 680 ; -C 70 ; WX 643.055 ; N F ; B 39 0 751 680 ; -C 71 ; WX 786.247 ; N G ; B 51 -22 760 705 ; -C 72 ; WX 831.25 ; N H ; B 39 0 881 683 ; -C 73 ; WX 439.583 ; N I ; B 34 0 498 683 ; -C 74 ; WX 554.512 ; N J ; B 73 -22 633 683 ; -C 75 ; WX 849.305 ; N K ; B 39 0 889 683 ; -C 76 ; WX 680.556 ; N L ; B 39 0 643 683 ; -C 77 ; WX 970.138 ; N M ; B 43 0 1044 683 ; -C 78 ; WX 803.471 ; N N ; B 39 0 881 683 ; -C 79 ; WX 762.774 ; N O ; B 50 -22 739 705 ; -C 80 ; WX 642.012 ; N P ; B 41 0 753 683 ; -C 81 ; WX 790.553 ; N Q ; B 50 -194 739 705 ; -C 82 ; WX 759.288 ; N R ; B 41 -22 755 683 ; -C 83 ; WX 613.193 ; N S ; B 53 -22 645 705 ; -C 84 ; WX 584.375 ; N T ; B 24 0 704 677 ; -C 85 ; WX 682.776 ; N U ; B 68 -22 760 683 ; -C 86 ; WX 583.333 ; N V ; B 56 -22 769 683 ; -C 87 ; WX 944.444 ; N W ; B 55 -22 1048 683 ; -C 88 ; WX 828.472 ; N X ; B 27 0 851 683 ; -C 89 ; WX 580.556 ; N Y ; B 34 0 762 683 ; -C 90 ; WX 682.638 ; N Z ; B 59 0 722 683 ; -C 91 ; WX 388.889 ; N flat ; B 56 -22 332 750 ; -C 92 ; WX 388.889 ; N natural ; B 79 -217 309 728 ; -C 93 ; WX 388.889 ; N sharp ; B 56 -216 332 716 ; -C 94 ; WX 1000 ; N slurbelow ; B 56 133 943 371 ; -C 95 ; WX 1000 ; N slurabove ; B 56 130 943 381 ; -C 96 ; WX 416.667 ; N lscript ; B 11 -12 398 705 ; -C 97 ; WX 528.588 ; N a ; B 40 -11 498 442 ; -C 98 ; WX 429.165 ; N b ; B 47 -11 415 694 ; -C 99 ; WX 432.755 ; N c ; B 41 -11 430 442 ; -C 100 ; WX 520.486 ; N d ; B 40 -11 517 694 ; -C 101 ; WX 465.625 ; N e ; B 46 -11 430 442 ; -C 102 ; WX 489.583 ; N f ; B 53 -205 552 705 ; -C 103 ; WX 476.967 ; N g ; B 16 -205 474 442 ; -C 104 ; WX 576.159 ; N h ; B 55 -11 546 694 ; -C 105 ; WX 344.511 ; N i ; B 29 -11 293 661 ; -C 106 ; WX 411.805 ; N j ; B -13 -205 397 661 ; -C 107 ; WX 520.602 ; N k ; B 55 -11 508 694 ; -C 108 ; WX 298.378 ; N l ; B 46 -11 260 694 ; -C 109 ; WX 878.012 ; N m ; B 29 -11 848 442 ; -C 110 ; WX 600.233 ; N n ; B 29 -11 571 442 ; -C 111 ; WX 484.721 ; N o ; B 41 -11 469 442 ; -C 112 ; WX 503.125 ; N p ; B -32 -194 490 442 ; -C 113 ; WX 446.412 ; N q ; B 40 -194 453 442 ; -C 114 ; WX 451.158 ; N r ; B 29 -11 436 442 ; -C 115 ; WX 468.75 ; N s ; B 52 -11 419 442 ; -C 116 ; WX 361.111 ; N t ; B 23 -11 330 626 ; -C 117 ; WX 572.456 ; N u ; B 29 -11 543 442 ; -C 118 ; WX 484.722 ; N v ; B 29 -11 468 443 ; -C 119 ; WX 715.916 ; N w ; B 29 -11 691 443 ; -C 120 ; WX 571.527 ; N x ; B 29 -11 527 442 ; -C 121 ; WX 490.28 ; N y ; B 29 -205 490 442 ; -C 122 ; WX 465.048 ; N z ; B 43 -11 467 442 ; -C 123 ; WX 322.454 ; N dotlessi ; B 29 -11 293 442 ; -C 124 ; WX 384.028 ; N dotlessj ; B -13 -205 360 442 ; -C 125 ; WX 636.457 ; N weierstrass ; B 76 -216 618 453 ; -C 126 ; WX 500 ; N vector ; B 182 516 625 714 ; -C 127 ; WX 277.778 ; N tie ; B 264 538 651 665 ; -C -1 ; WX 333.333 ; N space ; B 0 0 0 0 ; -EndCharMetrics -Comment The following are bogus kern pairs for TeX positioning of accents -StartKernData -StartKernPairs 166 -KPX Gamma slash -55.556 -KPX Gamma comma -111.111 -KPX Gamma period -111.111 -KPX Gamma tie 83.333 -KPX Delta tie 166.667 -KPX Theta tie 83.333 -KPX Lambda tie 166.667 -KPX Xi tie 83.333 -KPX Pi slash -55.556 -KPX Pi comma -55.556 -KPX Pi period -55.556 -KPX Pi tie 55.556 -KPX Sigma tie 83.333 -KPX Upsilon slash -55.556 -KPX Upsilon comma -111.111 -KPX Upsilon period -111.111 -KPX Upsilon tie 55.556 -KPX Phi tie 83.333 -KPX Psi slash -55.556 -KPX Psi comma -55.556 -KPX Psi period -55.556 -KPX Psi tie 55.556 -KPX Omega tie 83.333 -KPX alpha tie 27.778 -KPX beta tie 83.333 -KPX delta comma -55.556 -KPX delta period -55.556 -KPX delta tie 55.556 -KPX epsilon1 tie 55.556 -KPX zeta tie 83.333 -KPX eta tie 55.556 -KPX theta tie 83.333 -KPX iota tie 55.556 -KPX mu tie 27.778 -KPX nu comma -55.556 -KPX nu period -55.556 -KPX nu tie 27.778 -KPX xi tie 111.111 -KPX rho tie 83.333 -KPX sigma comma -55.556 -KPX sigma period -55.556 -KPX tau comma -55.556 -KPX tau period -55.556 -KPX tau tie 27.778 -KPX upsilon tie 27.778 -KPX phi tie 83.333 -KPX chi tie 55.556 -KPX psi tie 111.111 -KPX epsilon tie 83.333 -KPX theta1 tie 83.333 -KPX rho1 tie 83.333 -KPX sigma1 tie 83.333 -KPX phi1 tie 83.333 -KPX slash Delta -55.556 -KPX slash A -55.556 -KPX slash M -55.556 -KPX slash N -55.556 -KPX slash Y 55.556 -KPX slash Z -55.556 -KPX partialdiff tie 83.333 -KPX A tie 138.889 -KPX B tie 83.333 -KPX C slash -27.778 -KPX C comma -55.556 -KPX C period -55.556 -KPX C tie 83.333 -KPX D tie 55.556 -KPX E tie 83.333 -KPX F slash -55.556 -KPX F comma -111.111 -KPX F period -111.111 -KPX F tie 83.333 -KPX G tie 83.333 -KPX H slash -55.556 -KPX H comma -55.556 -KPX H period -55.556 -KPX H tie 55.556 -KPX I tie 111.111 -KPX J slash -55.556 -KPX J comma -111.111 -KPX J period -111.111 -KPX J tie 166.667 -KPX K slash -55.556 -KPX K comma -55.556 -KPX K period -55.556 -KPX K tie 55.556 -KPX L tie 27.778 -KPX M slash -55.556 -KPX M comma -55.556 -KPX M period -55.556 -KPX M tie 83.333 -KPX N slash -83.333 -KPX N slash -27.778 -KPX N comma -55.556 -KPX N period -55.556 -KPX N tie 83.333 -KPX O tie 83.333 -KPX P slash -55.556 -KPX P comma -111.111 -KPX P period -111.111 -KPX P tie 83.333 -KPX Q tie 83.333 -KPX R tie 83.333 -KPX S slash -55.556 -KPX S comma -55.556 -KPX S period -55.556 -KPX S tie 83.333 -KPX T slash -27.778 -KPX T comma -55.556 -KPX T period -55.556 -KPX T tie 83.333 -KPX U comma -111.111 -KPX U period -111.111 -KPX U slash -55.556 -KPX U tie 27.778 -KPX V comma -166.667 -KPX V period -166.667 -KPX V slash -111.111 -KPX W comma -166.667 -KPX W period -166.667 -KPX W slash -111.111 -KPX X slash -83.333 -KPX X slash -27.778 -KPX X comma -55.556 -KPX X period -55.556 -KPX X tie 83.333 -KPX Y comma -166.667 -KPX Y period -166.667 -KPX Y slash -111.111 -KPX Z slash -55.556 -KPX Z comma -55.556 -KPX Z period -55.556 -KPX Z tie 83.333 -KPX lscript tie 111.111 -KPX c tie 55.556 -KPX d Y 55.556 -KPX d Z -55.556 -KPX d j -111.111 -KPX d f -166.667 -KPX d tie 166.667 -KPX e tie 55.556 -KPX f comma -55.556 -KPX f period -55.556 -KPX f tie 166.667 -KPX g tie 27.778 -KPX h tie -27.778 -KPX j comma -55.556 -KPX j period -55.556 -KPX l tie 83.333 -KPX o tie 55.556 -KPX p tie 83.333 -KPX q tie 83.333 -KPX r comma -55.556 -KPX r period -55.556 -KPX r tie 55.556 -KPX s tie 55.556 -KPX t tie 83.333 -KPX u tie 27.778 -KPX v tie 27.778 -KPX w tie 83.333 -KPX x tie 27.778 -KPX y tie 55.556 -KPX z tie 55.556 -KPX dotlessi tie 27.778 -KPX dotlessj tie 83.333 -KPX weierstrass tie 111.111 -EndKernPairs -EndKernData -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/cmr10.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/cmr10.afm deleted file mode 100644 index 4d586fe..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/cmr10.afm +++ /dev/null @@ -1,343 +0,0 @@ -StartFontMetrics 2.0 -Comment Creation Date: Thu Jun 21 22:23:28 1990 -Comment UniqueID 5000793 -FontName CMR10 -EncodingScheme FontSpecific -FullName CMR10 -FamilyName Computer Modern -Weight Medium -ItalicAngle 0.0 -IsFixedPitch false -Version 1.00B -Notice Copyright (c) 1997 American Mathematical Society. All Rights Reserved. -Comment Computer Modern fonts were designed by Donald E. Knuth -FontBBox -40 -250 1009 969 -CapHeight 683.333 -XHeight 430.556 -Ascender 694.444 -Descender -194.444 -Comment FontID CMR -Comment DesignSize 10 (pts) -Comment CharacterCodingScheme TeX text -Comment Space 333.333 166.667 111.111 -Comment ExtraSpace 111.111 -Comment Quad 1000 -StartCharMetrics 129 -C 0 ; WX 625 ; N Gamma ; B 33 0 582 680 ; -C 1 ; WX 833.333 ; N Delta ; B 47 0 785 716 ; -C 2 ; WX 777.778 ; N Theta ; B 56 -22 721 705 ; -C 3 ; WX 694.444 ; N Lambda ; B 32 0 661 716 ; -C 4 ; WX 666.667 ; N Xi ; B 42 0 624 677 ; -C 5 ; WX 750 ; N Pi ; B 33 0 716 680 ; -C 6 ; WX 722.222 ; N Sigma ; B 56 0 665 683 ; -C 7 ; WX 777.778 ; N Upsilon ; B 56 0 721 705 ; -C 8 ; WX 722.222 ; N Phi ; B 56 0 665 683 ; -C 9 ; WX 777.778 ; N Psi ; B 57 0 720 683 ; -C 10 ; WX 722.222 ; N Omega ; B 44 0 677 705 ; -C 11 ; WX 583.333 ; N ff ; B 27 0 628 705 ; L i ffi ; L l ffl ; -C 12 ; WX 555.556 ; N fi ; B 27 0 527 705 ; -C 13 ; WX 555.556 ; N fl ; B 27 0 527 705 ; -C 14 ; WX 833.333 ; N ffi ; B 27 0 804 705 ; -C 15 ; WX 833.333 ; N ffl ; B 27 0 804 705 ; -C 16 ; WX 277.778 ; N dotlessi ; B 33 0 247 442 ; -C 17 ; WX 305.556 ; N dotlessj ; B -40 -205 210 442 ; -C 18 ; WX 500 ; N grave ; B 107 510 293 698 ; -C 19 ; WX 500 ; N acute ; B 206 510 392 698 ; -C 20 ; WX 500 ; N caron ; B 118 516 381 638 ; -C 21 ; WX 500 ; N breve ; B 100 522 399 694 ; -C 22 ; WX 500 ; N macron ; B 69 559 430 590 ; -C 23 ; WX 750 ; N ring ; B 279 541 470 716 ; -C 24 ; WX 444.444 ; N cedilla ; B 131 -203 367 -22 ; -C 25 ; WX 500 ; N germandbls ; B 28 -11 471 705 ; -C 26 ; WX 722.222 ; N ae ; B 45 -11 693 448 ; -C 27 ; WX 777.778 ; N oe ; B 28 -11 749 448 ; -C 28 ; WX 500 ; N oslash ; B 35 -102 464 534 ; -C 29 ; WX 902.778 ; N AE ; B 32 0 874 683 ; -C 30 ; WX 1013.89 ; N OE ; B 70 -22 985 705 ; -C 31 ; WX 777.778 ; N Oslash ; B 56 -56 721 739 ; -C 32 ; WX 277.778 ; N suppress ; B 27 280 262 392 ; -C 33 ; WX 277.778 ; N exclam ; B 86 0 192 716 ; L quoteleft exclamdown ; -C 34 ; WX 500 ; N quotedblright ; B 33 395 347 694 ; -C 35 ; WX 833.333 ; N numbersign ; B 56 -194 776 694 ; -C 36 ; WX 500 ; N dollar ; B 56 -56 443 750 ; -C 37 ; WX 833.333 ; N percent ; B 56 -56 776 750 ; -C 38 ; WX 777.778 ; N ampersand ; B 42 -22 727 716 ; -C 39 ; WX 277.778 ; N quoteright ; B 86 395 206 694 ; L quoteright quotedblright ; -C 40 ; WX 388.889 ; N parenleft ; B 99 -250 331 750 ; -C 41 ; WX 388.889 ; N parenright ; B 57 -250 289 750 ; -C 42 ; WX 500 ; N asterisk ; B 65 319 434 750 ; -C 43 ; WX 777.778 ; N plus ; B 56 -83 721 583 ; -C 44 ; WX 277.778 ; N comma ; B 86 -193 203 106 ; -C 45 ; WX 333.333 ; N hyphen ; B 11 187 276 245 ; L hyphen endash ; -C 46 ; WX 277.778 ; N period ; B 86 0 192 106 ; -C 47 ; WX 500 ; N slash ; B 56 -250 443 750 ; -C 48 ; WX 500 ; N zero ; B 39 -22 460 666 ; -C 49 ; WX 500 ; N one ; B 89 0 419 666 ; -C 50 ; WX 500 ; N two ; B 50 0 449 666 ; -C 51 ; WX 500 ; N three ; B 42 -22 457 666 ; -C 52 ; WX 500 ; N four ; B 28 0 471 677 ; -C 53 ; WX 500 ; N five ; B 50 -22 449 666 ; -C 54 ; WX 500 ; N six ; B 42 -22 457 666 ; -C 55 ; WX 500 ; N seven ; B 56 -22 485 676 ; -C 56 ; WX 500 ; N eight ; B 42 -22 457 666 ; -C 57 ; WX 500 ; N nine ; B 42 -22 457 666 ; -C 58 ; WX 277.778 ; N colon ; B 86 0 192 431 ; -C 59 ; WX 277.778 ; N semicolon ; B 86 -193 195 431 ; -C 60 ; WX 277.778 ; N exclamdown ; B 86 -216 192 500 ; -C 61 ; WX 777.778 ; N equal ; B 56 133 721 367 ; -C 62 ; WX 472.222 ; N questiondown ; B 56 -205 415 500 ; -C 63 ; WX 472.222 ; N question ; B 56 0 415 705 ; L quoteleft questiondown ; -C 64 ; WX 777.778 ; N at ; B 56 -11 721 705 ; -C 65 ; WX 750 ; N A ; B 32 0 717 716 ; -C 66 ; WX 708.333 ; N B ; B 36 0 651 683 ; -C 67 ; WX 722.222 ; N C ; B 56 -22 665 705 ; -C 68 ; WX 763.889 ; N D ; B 35 0 707 683 ; -C 69 ; WX 680.556 ; N E ; B 33 0 652 680 ; -C 70 ; WX 652.778 ; N F ; B 33 0 610 680 ; -C 71 ; WX 784.722 ; N G ; B 56 -22 735 705 ; -C 72 ; WX 750 ; N H ; B 33 0 716 683 ; -C 73 ; WX 361.111 ; N I ; B 28 0 333 683 ; -C 74 ; WX 513.889 ; N J ; B 41 -22 465 683 ; -C 75 ; WX 777.778 ; N K ; B 33 0 736 683 ; -C 76 ; WX 625 ; N L ; B 33 0 582 683 ; -C 77 ; WX 916.667 ; N M ; B 37 0 879 683 ; -C 78 ; WX 750 ; N N ; B 33 0 716 683 ; -C 79 ; WX 777.778 ; N O ; B 56 -22 721 705 ; -C 80 ; WX 680.556 ; N P ; B 35 0 624 683 ; -C 81 ; WX 777.778 ; N Q ; B 56 -194 727 705 ; -C 82 ; WX 736.111 ; N R ; B 35 -22 732 683 ; -C 83 ; WX 555.556 ; N S ; B 56 -22 499 705 ; -C 84 ; WX 722.222 ; N T ; B 36 0 685 677 ; -C 85 ; WX 750 ; N U ; B 33 -22 716 683 ; -C 86 ; WX 750 ; N V ; B 19 -22 730 683 ; -C 87 ; WX 1027.78 ; N W ; B 18 -22 1009 683 ; -C 88 ; WX 750 ; N X ; B 24 0 726 683 ; -C 89 ; WX 750 ; N Y ; B 11 0 738 683 ; -C 90 ; WX 611.111 ; N Z ; B 56 0 560 683 ; -C 91 ; WX 277.778 ; N bracketleft ; B 118 -250 255 750 ; -C 92 ; WX 500 ; N quotedblleft ; B 152 394 466 693 ; -C 93 ; WX 277.778 ; N bracketright ; B 22 -250 159 750 ; -C 94 ; WX 500 ; N circumflex ; B 116 540 383 694 ; -C 95 ; WX 277.778 ; N dotaccent ; B 85 563 192 669 ; -C 96 ; WX 277.778 ; N quoteleft ; B 72 394 192 693 ; L quoteleft quotedblleft ; -C 97 ; WX 500 ; N a ; B 42 -11 493 448 ; -C 98 ; WX 555.556 ; N b ; B 28 -11 521 694 ; -C 99 ; WX 444.444 ; N c ; B 34 -11 415 448 ; -C 100 ; WX 555.556 ; N d ; B 34 -11 527 694 ; -C 101 ; WX 444.444 ; N e ; B 28 -11 415 448 ; -C 102 ; WX 305.556 ; N f ; B 33 0 357 705 ; L i fi ; L f ff ; L l fl ; -C 103 ; WX 500 ; N g ; B 28 -206 485 453 ; -C 104 ; WX 555.556 ; N h ; B 32 0 535 694 ; -C 105 ; WX 277.778 ; N i ; B 33 0 247 669 ; -C 106 ; WX 305.556 ; N j ; B -40 -205 210 669 ; -C 107 ; WX 527.778 ; N k ; B 28 0 511 694 ; -C 108 ; WX 277.778 ; N l ; B 33 0 255 694 ; -C 109 ; WX 833.333 ; N m ; B 32 0 813 442 ; -C 110 ; WX 555.556 ; N n ; B 32 0 535 442 ; -C 111 ; WX 500 ; N o ; B 28 -11 471 448 ; -C 112 ; WX 555.556 ; N p ; B 28 -194 521 442 ; -C 113 ; WX 527.778 ; N q ; B 34 -194 527 442 ; -C 114 ; WX 391.667 ; N r ; B 28 0 364 442 ; -C 115 ; WX 394.444 ; N s ; B 33 -11 360 448 ; -C 116 ; WX 388.889 ; N t ; B 19 -11 332 615 ; -C 117 ; WX 555.556 ; N u ; B 32 -11 535 442 ; -C 118 ; WX 527.778 ; N v ; B 19 -11 508 431 ; -C 119 ; WX 722.222 ; N w ; B 18 -11 703 431 ; -C 120 ; WX 527.778 ; N x ; B 12 0 516 431 ; -C 121 ; WX 527.778 ; N y ; B 19 -205 508 431 ; -C 122 ; WX 444.444 ; N z ; B 28 0 401 431 ; -C 123 ; WX 500 ; N endash ; B 0 255 499 277 ; L hyphen emdash ; -C 124 ; WX 1000 ; N emdash ; B 0 255 999 277 ; -C 125 ; WX 500 ; N hungarumlaut ; B 128 513 420 699 ; -C 126 ; WX 500 ; N tilde ; B 83 575 416 668 ; -C 127 ; WX 500 ; N dieresis ; B 103 569 396 669 ; -C -1 ; WX 333.333 ; N space ; B 0 0 0 0 ; -EndCharMetrics -StartKernData -StartKernPairs 183 -KPX ff quoteright 77.778 -KPX ff question 77.778 -KPX ff exclam 77.778 -KPX ff parenright 77.778 -KPX ff bracketright 77.778 -KPX suppress l -277.778 -KPX suppress L -319.444 -KPX quoteright question 111.111 -KPX quoteright exclam 111.111 -KPX A t -27.778 -KPX A C -27.778 -KPX A O -27.778 -KPX A G -27.778 -KPX A U -27.778 -KPX A Q -27.778 -KPX A T -83.333 -KPX A Y -83.333 -KPX A V -111.111 -KPX A W -111.111 -KPX D X -27.778 -KPX D W -27.778 -KPX D A -27.778 -KPX D V -27.778 -KPX D Y -27.778 -KPX F o -83.333 -KPX F e -83.333 -KPX F u -83.333 -KPX F r -83.333 -KPX F a -83.333 -KPX F A -111.111 -KPX F O -27.778 -KPX F C -27.778 -KPX F G -27.778 -KPX F Q -27.778 -KPX I I 27.778 -KPX K O -27.778 -KPX K C -27.778 -KPX K G -27.778 -KPX K Q -27.778 -KPX L T -83.333 -KPX L Y -83.333 -KPX L V -111.111 -KPX L W -111.111 -KPX O X -27.778 -KPX O W -27.778 -KPX O A -27.778 -KPX O V -27.778 -KPX O Y -27.778 -KPX P A -83.333 -KPX P o -27.778 -KPX P e -27.778 -KPX P a -27.778 -KPX P period -83.333 -KPX P comma -83.333 -KPX R t -27.778 -KPX R C -27.778 -KPX R O -27.778 -KPX R G -27.778 -KPX R U -27.778 -KPX R Q -27.778 -KPX R T -83.333 -KPX R Y -83.333 -KPX R V -111.111 -KPX R W -111.111 -KPX T y -27.778 -KPX T e -83.333 -KPX T o -83.333 -KPX T r -83.333 -KPX T a -83.333 -KPX T A -83.333 -KPX T u -83.333 -KPX V o -83.333 -KPX V e -83.333 -KPX V u -83.333 -KPX V r -83.333 -KPX V a -83.333 -KPX V A -111.111 -KPX V O -27.778 -KPX V C -27.778 -KPX V G -27.778 -KPX V Q -27.778 -KPX W o -83.333 -KPX W e -83.333 -KPX W u -83.333 -KPX W r -83.333 -KPX W a -83.333 -KPX W A -111.111 -KPX W O -27.778 -KPX W C -27.778 -KPX W G -27.778 -KPX W Q -27.778 -KPX X O -27.778 -KPX X C -27.778 -KPX X G -27.778 -KPX X Q -27.778 -KPX Y e -83.333 -KPX Y o -83.333 -KPX Y r -83.333 -KPX Y a -83.333 -KPX Y A -83.333 -KPX Y u -83.333 -KPX a v -27.778 -KPX a j 55.556 -KPX a y -27.778 -KPX a w -27.778 -KPX b e 27.778 -KPX b o 27.778 -KPX b x -27.778 -KPX b d 27.778 -KPX b c 27.778 -KPX b q 27.778 -KPX b v -27.778 -KPX b j 55.556 -KPX b y -27.778 -KPX b w -27.778 -KPX c h -27.778 -KPX c k -27.778 -KPX f quoteright 77.778 -KPX f question 77.778 -KPX f exclam 77.778 -KPX f parenright 77.778 -KPX f bracketright 77.778 -KPX g j 27.778 -KPX h t -27.778 -KPX h u -27.778 -KPX h b -27.778 -KPX h y -27.778 -KPX h v -27.778 -KPX h w -27.778 -KPX k a -55.556 -KPX k e -27.778 -KPX k a -27.778 -KPX k o -27.778 -KPX k c -27.778 -KPX m t -27.778 -KPX m u -27.778 -KPX m b -27.778 -KPX m y -27.778 -KPX m v -27.778 -KPX m w -27.778 -KPX n t -27.778 -KPX n u -27.778 -KPX n b -27.778 -KPX n y -27.778 -KPX n v -27.778 -KPX n w -27.778 -KPX o e 27.778 -KPX o o 27.778 -KPX o x -27.778 -KPX o d 27.778 -KPX o c 27.778 -KPX o q 27.778 -KPX o v -27.778 -KPX o j 55.556 -KPX o y -27.778 -KPX o w -27.778 -KPX p e 27.778 -KPX p o 27.778 -KPX p x -27.778 -KPX p d 27.778 -KPX p c 27.778 -KPX p q 27.778 -KPX p v -27.778 -KPX p j 55.556 -KPX p y -27.778 -KPX p w -27.778 -KPX t y -27.778 -KPX t w -27.778 -KPX u w -27.778 -KPX v a -55.556 -KPX v e -27.778 -KPX v a -27.778 -KPX v o -27.778 -KPX v c -27.778 -KPX w e -27.778 -KPX w a -27.778 -KPX w o -27.778 -KPX w c -27.778 -KPX y o -27.778 -KPX y e -27.778 -KPX y a -27.778 -KPX y period -83.333 -KPX y comma -83.333 -EndKernPairs -EndKernData -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/cmsy10.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/cmsy10.afm deleted file mode 100644 index 09e9487..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/cmsy10.afm +++ /dev/null @@ -1,195 +0,0 @@ -StartFontMetrics 2.0 -Comment Creation Date: Thu Jun 21 22:23:44 1990 -Comment UniqueID 5000820 -FontName CMSY10 -EncodingScheme FontSpecific -FullName CMSY10 -FamilyName Computer Modern -Weight Medium -ItalicAngle -14.035 -IsFixedPitch false -Version 1.00 -Notice Copyright (c) 1997 American Mathematical Society. All Rights Reserved. -Comment Computer Modern fonts were designed by Donald E. Knuth -FontBBox -29 -960 1116 775 -CapHeight 683.333 -XHeight 430.556 -Ascender 694.444 -Descender -960 -Comment FontID CMSY -Comment DesignSize 10 (pts) -Comment CharacterCodingScheme TeX math symbols -Comment Space 0 0 0 -Comment ExtraSpace 0 -Comment Quad 1000 -Comment Num 676.508 393.732 443.731 -Comment Denom 685.951 344.841 -Comment Sup 412.892 362.892 288.889 -Comment Sub 150 247.217 -Comment Supdrop 386.108 -Comment Subdrop 50 -Comment Delim 2390 1010 -Comment Axisheight 250 -StartCharMetrics 129 -C 0 ; WX 777.778 ; N minus ; B 83 230 694 270 ; -C 1 ; WX 277.778 ; N periodcentered ; B 86 197 192 303 ; -C 2 ; WX 777.778 ; N multiply ; B 147 9 630 491 ; -C 3 ; WX 500 ; N asteriskmath ; B 65 34 434 465 ; -C 4 ; WX 777.778 ; N divide ; B 56 -30 722 530 ; -C 5 ; WX 500 ; N diamondmath ; B 11 11 489 489 ; -C 6 ; WX 777.778 ; N plusminus ; B 56 0 721 666 ; -C 7 ; WX 777.778 ; N minusplus ; B 56 -166 721 500 ; -C 8 ; WX 777.778 ; N circleplus ; B 56 -83 721 583 ; -C 9 ; WX 777.778 ; N circleminus ; B 56 -83 721 583 ; -C 10 ; WX 777.778 ; N circlemultiply ; B 56 -83 721 583 ; -C 11 ; WX 777.778 ; N circledivide ; B 56 -83 721 583 ; -C 12 ; WX 777.778 ; N circledot ; B 56 -83 721 583 ; -C 13 ; WX 1000 ; N circlecopyrt ; B 56 -216 943 716 ; -C 14 ; WX 500 ; N openbullet ; B 56 56 443 444 ; -C 15 ; WX 500 ; N bullet ; B 56 56 443 444 ; -C 16 ; WX 777.778 ; N equivasymptotic ; B 56 16 721 484 ; -C 17 ; WX 777.778 ; N equivalence ; B 56 36 721 464 ; -C 18 ; WX 777.778 ; N reflexsubset ; B 83 -137 694 636 ; -C 19 ; WX 777.778 ; N reflexsuperset ; B 83 -137 694 636 ; -C 20 ; WX 777.778 ; N lessequal ; B 83 -137 694 636 ; -C 21 ; WX 777.778 ; N greaterequal ; B 83 -137 694 636 ; -C 22 ; WX 777.778 ; N precedesequal ; B 83 -137 694 636 ; -C 23 ; WX 777.778 ; N followsequal ; B 83 -137 694 636 ; -C 24 ; WX 777.778 ; N similar ; B 56 133 721 367 ; -C 25 ; WX 777.778 ; N approxequal ; B 56 56 721 483 ; -C 26 ; WX 777.778 ; N propersubset ; B 83 -40 694 540 ; -C 27 ; WX 777.778 ; N propersuperset ; B 83 -40 694 540 ; -C 28 ; WX 1000 ; N lessmuch ; B 56 -66 943 566 ; -C 29 ; WX 1000 ; N greatermuch ; B 56 -66 943 566 ; -C 30 ; WX 777.778 ; N precedes ; B 83 -40 694 539 ; -C 31 ; WX 777.778 ; N follows ; B 83 -40 694 539 ; -C 32 ; WX 1000 ; N arrowleft ; B 57 72 943 428 ; -C 33 ; WX 1000 ; N arrowright ; B 56 72 942 428 ; -C 34 ; WX 500 ; N arrowup ; B 72 -194 428 693 ; -C 35 ; WX 500 ; N arrowdown ; B 72 -193 428 694 ; -C 36 ; WX 1000 ; N arrowboth ; B 57 72 942 428 ; -C 37 ; WX 1000 ; N arrownortheast ; B 56 -193 946 697 ; -C 38 ; WX 1000 ; N arrowsoutheast ; B 56 -197 946 693 ; -C 39 ; WX 777.778 ; N similarequal ; B 56 36 721 464 ; -C 40 ; WX 1000 ; N arrowdblleft ; B 57 -25 943 525 ; -C 41 ; WX 1000 ; N arrowdblright ; B 56 -25 942 525 ; -C 42 ; WX 611.111 ; N arrowdblup ; B 30 -194 580 694 ; -C 43 ; WX 611.111 ; N arrowdbldown ; B 30 -194 580 694 ; -C 44 ; WX 1000 ; N arrowdblboth ; B 35 -25 964 525 ; -C 45 ; WX 1000 ; N arrownorthwest ; B 53 -193 943 697 ; -C 46 ; WX 1000 ; N arrowsouthwest ; B 53 -197 943 693 ; -C 47 ; WX 777.778 ; N proportional ; B 56 -11 722 442 ; -C 48 ; WX 275 ; N prime ; B 29 45 262 559 ; -C 49 ; WX 1000 ; N infinity ; B 56 -11 943 442 ; -C 50 ; WX 666.667 ; N element ; B 83 -40 583 540 ; -C 51 ; WX 666.667 ; N owner ; B 83 -40 583 540 ; -C 52 ; WX 888.889 ; N triangle ; B 59 0 829 716 ; -C 53 ; WX 888.889 ; N triangleinv ; B 59 -216 829 500 ; -C 54 ; WX 0 ; N negationslash ; B 139 -216 638 716 ; -C 55 ; WX 0 ; N mapsto ; B 56 64 124 436 ; -C 56 ; WX 555.556 ; N universal ; B 0 -22 556 694 ; -C 57 ; WX 555.556 ; N existential ; B 56 0 499 694 ; -C 58 ; WX 666.667 ; N logicalnot ; B 56 89 610 356 ; -C 59 ; WX 500 ; N emptyset ; B 47 -78 452 772 ; -C 60 ; WX 722.222 ; N Rfractur ; B 46 -22 714 716 ; -C 61 ; WX 722.222 ; N Ifractur ; B 56 -11 693 705 ; -C 62 ; WX 777.778 ; N latticetop ; B 56 0 722 666 ; -C 63 ; WX 777.778 ; N perpendicular ; B 56 0 722 666 ; -C 64 ; WX 611.111 ; N aleph ; B 56 0 554 693 ; -C 65 ; WX 798.469 ; N A ; B 27 -50 798 722 ; -C 66 ; WX 656.808 ; N B ; B 30 -22 665 706 ; -C 67 ; WX 526.527 ; N C ; B 12 -24 534 705 ; -C 68 ; WX 771.391 ; N D ; B 20 0 766 683 ; -C 69 ; WX 527.778 ; N E ; B 28 -22 565 705 ; -C 70 ; WX 718.75 ; N F ; B 17 -33 829 683 ; -C 71 ; WX 594.864 ; N G ; B 44 -119 601 705 ; -C 72 ; WX 844.516 ; N H ; B 20 -47 818 683 ; -C 73 ; WX 544.513 ; N I ; B -24 0 635 683 ; -C 74 ; WX 677.778 ; N J ; B 47 -119 840 683 ; -C 75 ; WX 761.949 ; N K ; B 30 -22 733 705 ; -C 76 ; WX 689.723 ; N L ; B 31 -22 656 705 ; -C 77 ; WX 1200.9 ; N M ; B 27 -50 1116 705 ; -C 78 ; WX 820.489 ; N N ; B -29 -50 978 775 ; -C 79 ; WX 796.112 ; N O ; B 57 -22 777 705 ; -C 80 ; WX 695.558 ; N P ; B 20 -50 733 683 ; -C 81 ; WX 816.667 ; N Q ; B 113 -124 788 705 ; -C 82 ; WX 847.502 ; N R ; B 20 -22 837 683 ; -C 83 ; WX 605.556 ; N S ; B 18 -22 642 705 ; -C 84 ; WX 544.643 ; N T ; B 29 0 798 717 ; -C 85 ; WX 625.83 ; N U ; B -17 -28 688 683 ; -C 86 ; WX 612.781 ; N V ; B 35 -45 660 683 ; -C 87 ; WX 987.782 ; N W ; B 35 -45 1036 683 ; -C 88 ; WX 713.295 ; N X ; B 50 0 808 683 ; -C 89 ; WX 668.335 ; N Y ; B 31 -135 717 683 ; -C 90 ; WX 724.724 ; N Z ; B 37 0 767 683 ; -C 91 ; WX 666.667 ; N union ; B 56 -22 610 598 ; -C 92 ; WX 666.667 ; N intersection ; B 56 -22 610 598 ; -C 93 ; WX 666.667 ; N unionmulti ; B 56 -22 610 598 ; -C 94 ; WX 666.667 ; N logicaland ; B 56 -22 610 598 ; -C 95 ; WX 666.667 ; N logicalor ; B 56 -22 610 598 ; -C 96 ; WX 611.111 ; N turnstileleft ; B 56 0 554 694 ; -C 97 ; WX 611.111 ; N turnstileright ; B 56 0 554 694 ; -C 98 ; WX 444.444 ; N floorleft ; B 174 -250 422 750 ; -C 99 ; WX 444.444 ; N floorright ; B 21 -250 269 750 ; -C 100 ; WX 444.444 ; N ceilingleft ; B 174 -250 422 750 ; -C 101 ; WX 444.444 ; N ceilingright ; B 21 -250 269 750 ; -C 102 ; WX 500 ; N braceleft ; B 72 -250 427 750 ; -C 103 ; WX 500 ; N braceright ; B 72 -250 427 750 ; -C 104 ; WX 388.889 ; N angbracketleft ; B 110 -250 332 750 ; -C 105 ; WX 388.889 ; N angbracketright ; B 56 -250 278 750 ; -C 106 ; WX 277.778 ; N bar ; B 119 -250 159 750 ; -C 107 ; WX 500 ; N bardbl ; B 132 -250 367 750 ; -C 108 ; WX 500 ; N arrowbothv ; B 72 -272 428 772 ; -C 109 ; WX 611.111 ; N arrowdblbothv ; B 30 -272 580 772 ; -C 110 ; WX 500 ; N backslash ; B 56 -250 443 750 ; -C 111 ; WX 277.778 ; N wreathproduct ; B 56 -83 221 583 ; -C 112 ; WX 833.333 ; N radical ; B 73 -960 853 40 ; -C 113 ; WX 750 ; N coproduct ; B 36 0 713 683 ; -C 114 ; WX 833.333 ; N nabla ; B 47 -33 785 683 ; -C 115 ; WX 416.667 ; N integral ; B 56 -216 471 716 ; -C 116 ; WX 666.667 ; N unionsq ; B 61 0 605 598 ; -C 117 ; WX 666.667 ; N intersectionsq ; B 61 0 605 598 ; -C 118 ; WX 777.778 ; N subsetsqequal ; B 83 -137 714 636 ; -C 119 ; WX 777.778 ; N supersetsqequal ; B 63 -137 694 636 ; -C 120 ; WX 444.444 ; N section ; B 69 -205 374 705 ; -C 121 ; WX 444.444 ; N dagger ; B 56 -216 387 705 ; -C 122 ; WX 444.444 ; N daggerdbl ; B 56 -205 387 705 ; -C 123 ; WX 611.111 ; N paragraph ; B 56 -194 582 694 ; -C 124 ; WX 777.778 ; N club ; B 28 -130 750 727 ; -C 125 ; WX 777.778 ; N diamond ; B 56 -163 722 727 ; -C 126 ; WX 777.778 ; N heart ; B 56 -33 722 716 ; -C 127 ; WX 777.778 ; N spade ; B 56 -130 722 727 ; -C -1 ; WX 333.333 ; N space ; B 0 0 0 0 ; -EndCharMetrics -Comment The following are bogus kern pairs for TeX positioning of accents -StartKernData -StartKernPairs 26 -KPX A prime 194.444 -KPX B prime 138.889 -KPX C prime 138.889 -KPX D prime 83.333 -KPX E prime 111.111 -KPX F prime 111.111 -KPX G prime 111.111 -KPX H prime 111.111 -KPX I prime 27.778 -KPX J prime 166.667 -KPX K prime 55.556 -KPX L prime 138.889 -KPX M prime 138.889 -KPX N prime 83.333 -KPX O prime 111.111 -KPX P prime 83.333 -KPX Q prime 111.111 -KPX R prime 83.333 -KPX S prime 138.889 -KPX T prime 27.778 -KPX U prime 83.333 -KPX V prime 27.778 -KPX W prime 83.333 -KPX X prime 138.889 -KPX Y prime 83.333 -KPX Z prime 138.889 -EndKernPairs -EndKernData -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/cmtt10.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/cmtt10.afm deleted file mode 100644 index d6ec19b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/cmtt10.afm +++ /dev/null @@ -1,156 +0,0 @@ -StartFontMetrics 2.0 -Comment Creation Date: Thu Jun 21 22:23:51 1990 -Comment UniqueID 5000832 -FontName CMTT10 -EncodingScheme FontSpecific -FullName CMTT10 -FamilyName Computer Modern -Weight Medium -ItalicAngle 0.0 -IsFixedPitch true -Version 1.00B -Notice Copyright (c) 1997 American Mathematical Society. All Rights Reserved. -Comment Computer Modern fonts were designed by Donald E. Knuth -FontBBox -4 -235 731 800 -CapHeight 611.111 -XHeight 430.556 -Ascender 611.111 -Descender -222.222 -Comment FontID CMTT -Comment DesignSize 10 (pts) -Comment CharacterCodingScheme TeX typewriter text -Comment Space 525 0 0 -Comment ExtraSpace 525 -Comment Quad 1050 -StartCharMetrics 129 -C 0 ; WX 525 ; N Gamma ; B 32 0 488 611 ; -C 1 ; WX 525 ; N Delta ; B 34 0 490 623 ; -C 2 ; WX 525 ; N Theta ; B 56 -11 468 622 ; -C 3 ; WX 525 ; N Lambda ; B 29 0 495 623 ; -C 4 ; WX 525 ; N Xi ; B 33 0 491 611 ; -C 5 ; WX 525 ; N Pi ; B 22 0 502 611 ; -C 6 ; WX 525 ; N Sigma ; B 40 0 484 611 ; -C 7 ; WX 525 ; N Upsilon ; B 38 0 486 622 ; -C 8 ; WX 525 ; N Phi ; B 40 0 484 611 ; -C 9 ; WX 525 ; N Psi ; B 38 0 486 611 ; -C 10 ; WX 525 ; N Omega ; B 32 0 492 622 ; -C 11 ; WX 525 ; N arrowup ; B 59 0 465 611 ; -C 12 ; WX 525 ; N arrowdown ; B 59 0 465 611 ; -C 13 ; WX 525 ; N quotesingle ; B 217 328 309 622 ; -C 14 ; WX 525 ; N exclamdown ; B 212 -233 312 389 ; -C 15 ; WX 525 ; N questiondown ; B 62 -228 462 389 ; -C 16 ; WX 525 ; N dotlessi ; B 78 0 455 431 ; -C 17 ; WX 525 ; N dotlessj ; B 48 -228 368 431 ; -C 18 ; WX 525 ; N grave ; B 117 477 329 611 ; -C 19 ; WX 525 ; N acute ; B 195 477 407 611 ; -C 20 ; WX 525 ; N caron ; B 101 454 423 572 ; -C 21 ; WX 525 ; N breve ; B 86 498 438 611 ; -C 22 ; WX 525 ; N macron ; B 73 514 451 577 ; -C 23 ; WX 525 ; N ring ; B 181 499 343 619 ; -C 24 ; WX 525 ; N cedilla ; B 162 -208 428 45 ; -C 25 ; WX 525 ; N germandbls ; B 17 -6 495 617 ; -C 26 ; WX 525 ; N ae ; B 33 -6 504 440 ; -C 27 ; WX 525 ; N oe ; B 19 -6 505 440 ; -C 28 ; WX 525 ; N oslash ; B 43 -140 481 571 ; -C 29 ; WX 525 ; N AE ; B 23 0 499 611 ; -C 30 ; WX 525 ; N OE ; B 29 -11 502 622 ; -C 31 ; WX 525 ; N Oslash ; B 56 -85 468 696 ; -C 32 ; WX 525 ; N visiblespace ; B 44 -132 480 240 ; -C 33 ; WX 525 ; N exclam ; B 212 0 312 622 ; L quoteleft exclamdown ; -C 34 ; WX 525 ; N quotedbl ; B 126 328 398 622 ; -C 35 ; WX 525 ; N numbersign ; B 35 0 489 611 ; -C 36 ; WX 525 ; N dollar ; B 58 -83 466 694 ; -C 37 ; WX 525 ; N percent ; B 35 -83 489 694 ; -C 38 ; WX 525 ; N ampersand ; B 28 -11 490 622 ; -C 39 ; WX 525 ; N quoteright ; B 180 302 341 611 ; -C 40 ; WX 525 ; N parenleft ; B 173 -82 437 694 ; -C 41 ; WX 525 ; N parenright ; B 88 -82 352 694 ; -C 42 ; WX 525 ; N asterisk ; B 68 90 456 521 ; -C 43 ; WX 525 ; N plus ; B 38 81 486 531 ; -C 44 ; WX 525 ; N comma ; B 180 -139 346 125 ; -C 45 ; WX 525 ; N hyphen ; B 56 271 468 341 ; -C 46 ; WX 525 ; N period ; B 200 0 325 125 ; -C 47 ; WX 525 ; N slash ; B 58 -83 466 694 ; -C 48 ; WX 525 ; N zero ; B 50 -11 474 622 ; -C 49 ; WX 525 ; N one ; B 105 0 442 622 ; -C 50 ; WX 525 ; N two ; B 52 0 472 622 ; -C 51 ; WX 525 ; N three ; B 44 -11 480 622 ; -C 52 ; WX 525 ; N four ; B 29 0 495 623 ; -C 53 ; WX 525 ; N five ; B 52 -11 472 611 ; -C 54 ; WX 525 ; N six ; B 53 -11 471 622 ; -C 55 ; WX 525 ; N seven ; B 44 -11 480 627 ; -C 56 ; WX 525 ; N eight ; B 44 -11 480 622 ; -C 57 ; WX 525 ; N nine ; B 53 -11 471 622 ; -C 58 ; WX 525 ; N colon ; B 200 0 325 431 ; -C 59 ; WX 525 ; N semicolon ; B 180 -139 330 431 ; -C 60 ; WX 525 ; N less ; B 56 56 468 556 ; -C 61 ; WX 525 ; N equal ; B 38 195 486 417 ; -C 62 ; WX 525 ; N greater ; B 56 56 468 556 ; -C 63 ; WX 525 ; N question ; B 62 0 462 617 ; L quoteleft questiondown ; -C 64 ; WX 525 ; N at ; B 44 -6 480 617 ; -C 65 ; WX 525 ; N A ; B 27 0 497 623 ; -C 66 ; WX 525 ; N B ; B 23 0 482 611 ; -C 67 ; WX 525 ; N C ; B 40 -11 484 622 ; -C 68 ; WX 525 ; N D ; B 19 0 485 611 ; -C 69 ; WX 525 ; N E ; B 26 0 502 611 ; -C 70 ; WX 525 ; N F ; B 28 0 490 611 ; -C 71 ; WX 525 ; N G ; B 38 -11 496 622 ; -C 72 ; WX 525 ; N H ; B 22 0 502 611 ; -C 73 ; WX 525 ; N I ; B 79 0 446 611 ; -C 74 ; WX 525 ; N J ; B 71 -11 478 611 ; -C 75 ; WX 525 ; N K ; B 26 0 495 611 ; -C 76 ; WX 525 ; N L ; B 32 0 488 611 ; -C 77 ; WX 525 ; N M ; B 17 0 507 611 ; -C 78 ; WX 525 ; N N ; B 28 0 496 611 ; -C 79 ; WX 525 ; N O ; B 56 -11 468 622 ; -C 80 ; WX 525 ; N P ; B 26 0 480 611 ; -C 81 ; WX 525 ; N Q ; B 56 -139 468 622 ; -C 82 ; WX 525 ; N R ; B 22 -11 522 611 ; -C 83 ; WX 525 ; N S ; B 52 -11 472 622 ; -C 84 ; WX 525 ; N T ; B 26 0 498 611 ; -C 85 ; WX 525 ; N U ; B 4 -11 520 611 ; -C 86 ; WX 525 ; N V ; B 18 -8 506 611 ; -C 87 ; WX 525 ; N W ; B 11 -8 513 611 ; -C 88 ; WX 525 ; N X ; B 27 0 496 611 ; -C 89 ; WX 525 ; N Y ; B 19 0 505 611 ; -C 90 ; WX 525 ; N Z ; B 48 0 481 611 ; -C 91 ; WX 525 ; N bracketleft ; B 222 -83 483 694 ; -C 92 ; WX 525 ; N backslash ; B 58 -83 466 694 ; -C 93 ; WX 525 ; N bracketright ; B 41 -83 302 694 ; -C 94 ; WX 525 ; N asciicircum ; B 100 471 424 611 ; -C 95 ; WX 525 ; N underscore ; B 56 -95 468 -25 ; -C 96 ; WX 525 ; N quoteleft ; B 183 372 344 681 ; -C 97 ; WX 525 ; N a ; B 55 -6 524 440 ; -C 98 ; WX 525 ; N b ; B 12 -6 488 611 ; -C 99 ; WX 525 ; N c ; B 73 -6 466 440 ; -C 100 ; WX 525 ; N d ; B 36 -6 512 611 ; -C 101 ; WX 525 ; N e ; B 55 -6 464 440 ; -C 102 ; WX 525 ; N f ; B 42 0 437 617 ; -C 103 ; WX 525 ; N g ; B 29 -229 509 442 ; -C 104 ; WX 525 ; N h ; B 12 0 512 611 ; -C 105 ; WX 525 ; N i ; B 78 0 455 612 ; -C 106 ; WX 525 ; N j ; B 48 -228 368 612 ; -C 107 ; WX 525 ; N k ; B 21 0 508 611 ; -C 108 ; WX 525 ; N l ; B 58 0 467 611 ; -C 109 ; WX 525 ; N m ; B -4 0 516 437 ; -C 110 ; WX 525 ; N n ; B 12 0 512 437 ; -C 111 ; WX 525 ; N o ; B 57 -6 467 440 ; -C 112 ; WX 525 ; N p ; B 12 -222 488 437 ; -C 113 ; WX 525 ; N q ; B 40 -222 537 437 ; -C 114 ; WX 525 ; N r ; B 32 0 487 437 ; -C 115 ; WX 525 ; N s ; B 72 -6 459 440 ; -C 116 ; WX 525 ; N t ; B 25 -6 449 554 ; -C 117 ; WX 525 ; N u ; B 12 -6 512 431 ; -C 118 ; WX 525 ; N v ; B 24 -4 500 431 ; -C 119 ; WX 525 ; N w ; B 16 -4 508 431 ; -C 120 ; WX 525 ; N x ; B 27 0 496 431 ; -C 121 ; WX 525 ; N y ; B 26 -228 500 431 ; -C 122 ; WX 525 ; N z ; B 33 0 475 431 ; -C 123 ; WX 525 ; N braceleft ; B 57 -83 467 694 ; -C 124 ; WX 525 ; N bar ; B 227 -83 297 694 ; -C 125 ; WX 525 ; N braceright ; B 57 -83 467 694 ; -C 126 ; WX 525 ; N asciitilde ; B 87 491 437 611 ; -C 127 ; WX 525 ; N dieresis ; B 110 512 414 612 ; -C -1 ; WX 525 ; N space ; B 0 0 0 0 ; -EndCharMetrics -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pagd8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pagd8a.afm deleted file mode 100644 index 69eebba..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pagd8a.afm +++ /dev/null @@ -1,576 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990, 1991 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Mon Mar 4 13:46:34 1991 -Comment UniqueID 34370 -Comment VMusage 24954 31846 -FontName AvantGarde-Demi -FullName ITC Avant Garde Gothic Demi -FamilyName ITC Avant Garde Gothic -Weight Demi -ItalicAngle 0 -IsFixedPitch false -FontBBox -123 -251 1222 1021 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.007 -Notice Copyright (c) 1985, 1987, 1989, 1990, 1991 Adobe Systems Incorporated. All Rights Reserved.ITC Avant Garde Gothic is a registered trademark of International Typeface Corporation. -EncodingScheme AdobeStandardEncoding -CapHeight 740 -XHeight 555 -Ascender 740 -Descender -185 -StartCharMetrics 228 -C 32 ; WX 280 ; N space ; B 0 0 0 0 ; -C 33 ; WX 280 ; N exclam ; B 73 0 206 740 ; -C 34 ; WX 360 ; N quotedbl ; B 19 444 341 740 ; -C 35 ; WX 560 ; N numbersign ; B 29 0 525 700 ; -C 36 ; WX 560 ; N dollar ; B 58 -86 501 857 ; -C 37 ; WX 860 ; N percent ; B 36 -15 822 755 ; -C 38 ; WX 680 ; N ampersand ; B 34 -15 665 755 ; -C 39 ; WX 280 ; N quoteright ; B 72 466 205 740 ; -C 40 ; WX 380 ; N parenleft ; B 74 -157 350 754 ; -C 41 ; WX 380 ; N parenright ; B 37 -157 313 754 ; -C 42 ; WX 440 ; N asterisk ; B 67 457 374 755 ; -C 43 ; WX 600 ; N plus ; B 48 0 552 506 ; -C 44 ; WX 280 ; N comma ; B 73 -141 206 133 ; -C 45 ; WX 420 ; N hyphen ; B 71 230 349 348 ; -C 46 ; WX 280 ; N period ; B 73 0 206 133 ; -C 47 ; WX 460 ; N slash ; B 6 -100 454 740 ; -C 48 ; WX 560 ; N zero ; B 32 -15 529 755 ; -C 49 ; WX 560 ; N one ; B 137 0 363 740 ; -C 50 ; WX 560 ; N two ; B 36 0 523 755 ; -C 51 ; WX 560 ; N three ; B 28 -15 532 755 ; -C 52 ; WX 560 ; N four ; B 15 0 545 740 ; -C 53 ; WX 560 ; N five ; B 25 -15 535 740 ; -C 54 ; WX 560 ; N six ; B 23 -15 536 739 ; -C 55 ; WX 560 ; N seven ; B 62 0 498 740 ; -C 56 ; WX 560 ; N eight ; B 33 -15 527 755 ; -C 57 ; WX 560 ; N nine ; B 24 0 537 754 ; -C 58 ; WX 280 ; N colon ; B 73 0 206 555 ; -C 59 ; WX 280 ; N semicolon ; B 73 -141 206 555 ; -C 60 ; WX 600 ; N less ; B 46 -8 554 514 ; -C 61 ; WX 600 ; N equal ; B 48 81 552 425 ; -C 62 ; WX 600 ; N greater ; B 46 -8 554 514 ; -C 63 ; WX 560 ; N question ; B 38 0 491 755 ; -C 64 ; WX 740 ; N at ; B 50 -12 750 712 ; -C 65 ; WX 740 ; N A ; B 7 0 732 740 ; -C 66 ; WX 580 ; N B ; B 70 0 551 740 ; -C 67 ; WX 780 ; N C ; B 34 -15 766 755 ; -C 68 ; WX 700 ; N D ; B 63 0 657 740 ; -C 69 ; WX 520 ; N E ; B 61 0 459 740 ; -C 70 ; WX 480 ; N F ; B 61 0 438 740 ; -C 71 ; WX 840 ; N G ; B 27 -15 817 755 ; -C 72 ; WX 680 ; N H ; B 71 0 610 740 ; -C 73 ; WX 280 ; N I ; B 72 0 209 740 ; -C 74 ; WX 480 ; N J ; B 2 -15 409 740 ; -C 75 ; WX 620 ; N K ; B 89 0 620 740 ; -C 76 ; WX 440 ; N L ; B 72 0 435 740 ; -C 77 ; WX 900 ; N M ; B 63 0 837 740 ; -C 78 ; WX 740 ; N N ; B 70 0 671 740 ; -C 79 ; WX 840 ; N O ; B 33 -15 807 755 ; -C 80 ; WX 560 ; N P ; B 72 0 545 740 ; -C 81 ; WX 840 ; N Q ; B 32 -15 824 755 ; -C 82 ; WX 580 ; N R ; B 64 0 565 740 ; -C 83 ; WX 520 ; N S ; B 12 -15 493 755 ; -C 84 ; WX 420 ; N T ; B 6 0 418 740 ; -C 85 ; WX 640 ; N U ; B 55 -15 585 740 ; -C 86 ; WX 700 ; N V ; B 8 0 695 740 ; -C 87 ; WX 900 ; N W ; B 7 0 899 740 ; -C 88 ; WX 680 ; N X ; B 4 0 676 740 ; -C 89 ; WX 620 ; N Y ; B -2 0 622 740 ; -C 90 ; WX 500 ; N Z ; B 19 0 481 740 ; -C 91 ; WX 320 ; N bracketleft ; B 66 -157 284 754 ; -C 92 ; WX 640 ; N backslash ; B 96 -100 544 740 ; -C 93 ; WX 320 ; N bracketright ; B 36 -157 254 754 ; -C 94 ; WX 600 ; N asciicircum ; B 73 375 527 740 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 280 ; N quoteleft ; B 72 466 205 740 ; -C 97 ; WX 660 ; N a ; B 27 -18 613 574 ; -C 98 ; WX 660 ; N b ; B 47 -18 632 740 ; -C 99 ; WX 640 ; N c ; B 37 -18 610 574 ; -C 100 ; WX 660 ; N d ; B 34 -18 618 740 ; -C 101 ; WX 640 ; N e ; B 31 -18 610 577 ; -C 102 ; WX 280 ; N f ; B 15 0 280 755 ; L i fi ; L l fl ; -C 103 ; WX 660 ; N g ; B 32 -226 623 574 ; -C 104 ; WX 600 ; N h ; B 54 0 546 740 ; -C 105 ; WX 240 ; N i ; B 53 0 186 740 ; -C 106 ; WX 260 ; N j ; B 16 -185 205 740 ; -C 107 ; WX 580 ; N k ; B 80 0 571 740 ; -C 108 ; WX 240 ; N l ; B 54 0 187 740 ; -C 109 ; WX 940 ; N m ; B 54 0 887 574 ; -C 110 ; WX 600 ; N n ; B 54 0 547 574 ; -C 111 ; WX 640 ; N o ; B 25 -18 615 574 ; -C 112 ; WX 660 ; N p ; B 47 -185 629 574 ; -C 113 ; WX 660 ; N q ; B 31 -185 613 574 ; -C 114 ; WX 320 ; N r ; B 63 0 317 574 ; -C 115 ; WX 440 ; N s ; B 19 -18 421 574 ; -C 116 ; WX 300 ; N t ; B 21 0 299 740 ; -C 117 ; WX 600 ; N u ; B 50 -18 544 555 ; -C 118 ; WX 560 ; N v ; B 3 0 556 555 ; -C 119 ; WX 800 ; N w ; B 11 0 789 555 ; -C 120 ; WX 560 ; N x ; B 3 0 556 555 ; -C 121 ; WX 580 ; N y ; B 8 -185 571 555 ; -C 122 ; WX 460 ; N z ; B 20 0 442 555 ; -C 123 ; WX 340 ; N braceleft ; B -3 -191 317 747 ; -C 124 ; WX 600 ; N bar ; B 233 -100 366 740 ; -C 125 ; WX 340 ; N braceright ; B 23 -191 343 747 ; -C 126 ; WX 600 ; N asciitilde ; B 67 160 533 347 ; -C 161 ; WX 280 ; N exclamdown ; B 74 -185 207 555 ; -C 162 ; WX 560 ; N cent ; B 43 39 517 715 ; -C 163 ; WX 560 ; N sterling ; B -2 0 562 755 ; -C 164 ; WX 160 ; N fraction ; B -123 0 282 740 ; -C 165 ; WX 560 ; N yen ; B -10 0 570 740 ; -C 166 ; WX 560 ; N florin ; B 0 -151 512 824 ; -C 167 ; WX 560 ; N section ; B 28 -158 530 755 ; -C 168 ; WX 560 ; N currency ; B 27 69 534 577 ; -C 169 ; WX 220 ; N quotesingle ; B 44 444 177 740 ; -C 170 ; WX 480 ; N quotedblleft ; B 70 466 410 740 ; -C 171 ; WX 460 ; N guillemotleft ; B 61 108 400 469 ; -C 172 ; WX 240 ; N guilsinglleft ; B 50 108 190 469 ; -C 173 ; WX 240 ; N guilsinglright ; B 50 108 190 469 ; -C 174 ; WX 520 ; N fi ; B 25 0 461 755 ; -C 175 ; WX 520 ; N fl ; B 25 0 461 755 ; -C 177 ; WX 500 ; N endash ; B 35 230 465 348 ; -C 178 ; WX 560 ; N dagger ; B 51 -142 509 740 ; -C 179 ; WX 560 ; N daggerdbl ; B 51 -142 509 740 ; -C 180 ; WX 280 ; N periodcentered ; B 73 187 206 320 ; -C 182 ; WX 600 ; N paragraph ; B -7 -103 607 740 ; -C 183 ; WX 600 ; N bullet ; B 148 222 453 532 ; -C 184 ; WX 280 ; N quotesinglbase ; B 72 -141 205 133 ; -C 185 ; WX 480 ; N quotedblbase ; B 70 -141 410 133 ; -C 186 ; WX 480 ; N quotedblright ; B 70 466 410 740 ; -C 187 ; WX 460 ; N guillemotright ; B 61 108 400 469 ; -C 188 ; WX 1000 ; N ellipsis ; B 100 0 899 133 ; -C 189 ; WX 1280 ; N perthousand ; B 36 -15 1222 755 ; -C 191 ; WX 560 ; N questiondown ; B 68 -200 521 555 ; -C 193 ; WX 420 ; N grave ; B 50 624 329 851 ; -C 194 ; WX 420 ; N acute ; B 91 624 370 849 ; -C 195 ; WX 540 ; N circumflex ; B 71 636 470 774 ; -C 196 ; WX 480 ; N tilde ; B 44 636 437 767 ; -C 197 ; WX 420 ; N macron ; B 72 648 349 759 ; -C 198 ; WX 480 ; N breve ; B 42 633 439 770 ; -C 199 ; WX 280 ; N dotaccent ; B 74 636 207 769 ; -C 200 ; WX 500 ; N dieresis ; B 78 636 422 769 ; -C 202 ; WX 360 ; N ring ; B 73 619 288 834 ; -C 203 ; WX 340 ; N cedilla ; B 98 -251 298 6 ; -C 205 ; WX 700 ; N hungarumlaut ; B 132 610 609 862 ; -C 206 ; WX 340 ; N ogonek ; B 79 -195 262 9 ; -C 207 ; WX 540 ; N caron ; B 71 636 470 774 ; -C 208 ; WX 1000 ; N emdash ; B 35 230 965 348 ; -C 225 ; WX 900 ; N AE ; B -5 0 824 740 ; -C 227 ; WX 360 ; N ordfeminine ; B 19 438 334 755 ; -C 232 ; WX 480 ; N Lslash ; B 26 0 460 740 ; -C 233 ; WX 840 ; N Oslash ; B 33 -71 807 814 ; -C 234 ; WX 1060 ; N OE ; B 37 -15 1007 755 ; -C 235 ; WX 360 ; N ordmasculine ; B 23 438 338 755 ; -C 241 ; WX 1080 ; N ae ; B 29 -18 1048 574 ; -C 245 ; WX 240 ; N dotlessi ; B 53 0 186 555 ; -C 248 ; WX 320 ; N lslash ; B 34 0 305 740 ; -C 249 ; WX 660 ; N oslash ; B 35 -50 625 608 ; -C 250 ; WX 1080 ; N oe ; B 30 -18 1050 574 ; -C 251 ; WX 600 ; N germandbls ; B 51 -18 585 755 ; -C -1 ; WX 640 ; N ecircumflex ; B 31 -18 610 774 ; -C -1 ; WX 640 ; N edieresis ; B 31 -18 610 769 ; -C -1 ; WX 660 ; N aacute ; B 27 -18 613 849 ; -C -1 ; WX 740 ; N registered ; B -12 -12 752 752 ; -C -1 ; WX 240 ; N icircumflex ; B -79 0 320 774 ; -C -1 ; WX 600 ; N udieresis ; B 50 -18 544 769 ; -C -1 ; WX 640 ; N ograve ; B 25 -18 615 851 ; -C -1 ; WX 600 ; N uacute ; B 50 -18 544 849 ; -C -1 ; WX 600 ; N ucircumflex ; B 50 -18 544 774 ; -C -1 ; WX 740 ; N Aacute ; B 7 0 732 1019 ; -C -1 ; WX 240 ; N igrave ; B -65 0 214 851 ; -C -1 ; WX 280 ; N Icircumflex ; B -59 0 340 944 ; -C -1 ; WX 640 ; N ccedilla ; B 37 -251 610 574 ; -C -1 ; WX 660 ; N adieresis ; B 27 -18 613 769 ; -C -1 ; WX 520 ; N Ecircumflex ; B 61 0 460 944 ; -C -1 ; WX 440 ; N scaron ; B 19 -18 421 774 ; -C -1 ; WX 660 ; N thorn ; B 47 -185 629 740 ; -C -1 ; WX 1000 ; N trademark ; B 9 296 821 740 ; -C -1 ; WX 640 ; N egrave ; B 31 -18 610 851 ; -C -1 ; WX 336 ; N threesuperior ; B 8 287 328 749 ; -C -1 ; WX 460 ; N zcaron ; B 20 0 455 774 ; -C -1 ; WX 660 ; N atilde ; B 27 -18 613 767 ; -C -1 ; WX 660 ; N aring ; B 27 -18 613 834 ; -C -1 ; WX 640 ; N ocircumflex ; B 25 -18 615 774 ; -C -1 ; WX 520 ; N Edieresis ; B 61 0 459 939 ; -C -1 ; WX 840 ; N threequarters ; B 18 0 803 749 ; -C -1 ; WX 580 ; N ydieresis ; B 8 -185 571 769 ; -C -1 ; WX 580 ; N yacute ; B 8 -185 571 849 ; -C -1 ; WX 240 ; N iacute ; B 26 0 305 849 ; -C -1 ; WX 740 ; N Acircumflex ; B 7 0 732 944 ; -C -1 ; WX 640 ; N Uacute ; B 55 -15 585 1019 ; -C -1 ; WX 640 ; N eacute ; B 31 -18 610 849 ; -C -1 ; WX 840 ; N Ograve ; B 33 -15 807 1021 ; -C -1 ; WX 660 ; N agrave ; B 27 -18 613 851 ; -C -1 ; WX 640 ; N Udieresis ; B 55 -15 585 939 ; -C -1 ; WX 660 ; N acircumflex ; B 27 -18 613 774 ; -C -1 ; WX 280 ; N Igrave ; B -45 0 234 1021 ; -C -1 ; WX 336 ; N twosuperior ; B 13 296 322 749 ; -C -1 ; WX 640 ; N Ugrave ; B 55 -15 585 1021 ; -C -1 ; WX 840 ; N onequarter ; B 92 0 746 740 ; -C -1 ; WX 640 ; N Ucircumflex ; B 55 -15 585 944 ; -C -1 ; WX 520 ; N Scaron ; B 12 -15 493 944 ; -C -1 ; WX 280 ; N Idieresis ; B -32 0 312 939 ; -C -1 ; WX 240 ; N idieresis ; B -52 0 292 769 ; -C -1 ; WX 520 ; N Egrave ; B 61 0 459 1021 ; -C -1 ; WX 840 ; N Oacute ; B 33 -15 807 1019 ; -C -1 ; WX 600 ; N divide ; B 48 -20 552 526 ; -C -1 ; WX 740 ; N Atilde ; B 7 0 732 937 ; -C -1 ; WX 740 ; N Aring ; B 7 0 732 969 ; -C -1 ; WX 840 ; N Odieresis ; B 33 -15 807 939 ; -C -1 ; WX 740 ; N Adieresis ; B 7 0 732 939 ; -C -1 ; WX 740 ; N Ntilde ; B 70 0 671 937 ; -C -1 ; WX 500 ; N Zcaron ; B 19 0 481 944 ; -C -1 ; WX 560 ; N Thorn ; B 72 0 545 740 ; -C -1 ; WX 280 ; N Iacute ; B 46 0 325 1019 ; -C -1 ; WX 600 ; N plusminus ; B 48 -62 552 556 ; -C -1 ; WX 600 ; N multiply ; B 59 12 541 494 ; -C -1 ; WX 520 ; N Eacute ; B 61 0 459 1019 ; -C -1 ; WX 620 ; N Ydieresis ; B -2 0 622 939 ; -C -1 ; WX 336 ; N onesuperior ; B 72 296 223 740 ; -C -1 ; WX 600 ; N ugrave ; B 50 -18 544 851 ; -C -1 ; WX 600 ; N logicalnot ; B 48 108 552 425 ; -C -1 ; WX 600 ; N ntilde ; B 54 0 547 767 ; -C -1 ; WX 840 ; N Otilde ; B 33 -15 807 937 ; -C -1 ; WX 640 ; N otilde ; B 25 -18 615 767 ; -C -1 ; WX 780 ; N Ccedilla ; B 34 -251 766 755 ; -C -1 ; WX 740 ; N Agrave ; B 7 0 732 1021 ; -C -1 ; WX 840 ; N onehalf ; B 62 0 771 740 ; -C -1 ; WX 742 ; N Eth ; B 25 0 691 740 ; -C -1 ; WX 400 ; N degree ; B 57 426 343 712 ; -C -1 ; WX 620 ; N Yacute ; B -2 0 622 1019 ; -C -1 ; WX 840 ; N Ocircumflex ; B 33 -15 807 944 ; -C -1 ; WX 640 ; N oacute ; B 25 -18 615 849 ; -C -1 ; WX 576 ; N mu ; B 38 -187 539 555 ; -C -1 ; WX 600 ; N minus ; B 48 193 552 313 ; -C -1 ; WX 640 ; N eth ; B 27 -18 616 754 ; -C -1 ; WX 640 ; N odieresis ; B 25 -18 615 769 ; -C -1 ; WX 740 ; N copyright ; B -12 -12 752 752 ; -C -1 ; WX 600 ; N brokenbar ; B 233 -100 366 740 ; -EndCharMetrics -StartKernData -StartKernPairs 218 - -KPX A y -50 -KPX A w -65 -KPX A v -70 -KPX A u -20 -KPX A quoteright -90 -KPX A Y -80 -KPX A W -60 -KPX A V -102 -KPX A U -40 -KPX A T -25 -KPX A Q -50 -KPX A O -50 -KPX A G -40 -KPX A C -40 - -KPX B A -10 - -KPX C A -40 - -KPX D period -20 -KPX D comma -20 -KPX D Y -45 -KPX D W -25 -KPX D V -50 -KPX D A -50 - -KPX F period -129 -KPX F e -20 -KPX F comma -162 -KPX F a -20 -KPX F A -75 - -KPX G period -20 -KPX G comma -20 -KPX G Y -15 - -KPX J period -15 -KPX J a -20 -KPX J A -30 - -KPX K y -20 -KPX K u -15 -KPX K o -45 -KPX K e -40 -KPX K O -30 - -KPX L y -23 -KPX L quoteright -30 -KPX L quotedblright -30 -KPX L Y -80 -KPX L W -55 -KPX L V -85 -KPX L T -46 - -KPX O period -30 -KPX O comma -30 -KPX O Y -30 -KPX O X -30 -KPX O W -20 -KPX O V -45 -KPX O T -15 -KPX O A -60 - -KPX P period -200 -KPX P o -20 -KPX P e -20 -KPX P comma -220 -KPX P a -20 -KPX P A -100 - -KPX Q comma 20 - -KPX R W 25 -KPX R V -10 -KPX R U 25 -KPX R T 40 -KPX R O 25 - -KPX S comma 20 - -KPX T y -10 -KPX T w -55 -KPX T u -46 -KPX T semicolon -29 -KPX T r -30 -KPX T period -91 -KPX T o -49 -KPX T hyphen -75 -KPX T e -49 -KPX T comma -82 -KPX T colon -15 -KPX T a -70 -KPX T O -15 -KPX T A -25 - -KPX U period -20 -KPX U comma -20 -KPX U A -40 - -KPX V u -55 -KPX V semicolon -33 -KPX V period -145 -KPX V o -101 -KPX V i -15 -KPX V hyphen -75 -KPX V e -101 -KPX V comma -145 -KPX V colon -18 -KPX V a -95 -KPX V O -45 -KPX V G -20 -KPX V A -102 - -KPX W y -15 -KPX W u -30 -KPX W semicolon -33 -KPX W period -106 -KPX W o -46 -KPX W i -10 -KPX W hyphen -35 -KPX W e -47 -KPX W comma -106 -KPX W colon -15 -KPX W a -50 -KPX W O -20 -KPX W A -58 - -KPX Y u -52 -KPX Y semicolon -23 -KPX Y period -145 -KPX Y o -89 -KPX Y hyphen -100 -KPX Y e -89 -KPX Y comma -145 -KPX Y colon -10 -KPX Y a -93 -KPX Y O -30 -KPX Y A -80 - -KPX a t 5 -KPX a p 20 -KPX a b 5 - -KPX b y -20 -KPX b v -20 - -KPX c y -20 -KPX c l -15 -KPX c k -15 - -KPX comma space -50 -KPX comma quoteright -70 -KPX comma quotedblright -70 - -KPX e y -20 -KPX e x -20 -KPX e w -20 -KPX e v -20 - -KPX f period -40 -KPX f o -20 -KPX f l -15 -KPX f i -15 -KPX f f -20 -KPX f dotlessi -15 -KPX f comma -40 -KPX f a -15 - -KPX g i 25 -KPX g a 15 - -KPX h y -30 - -KPX k y -5 -KPX k o -30 -KPX k e -40 - -KPX m y -20 -KPX m u -20 - -KPX n y -15 -KPX n v -30 - -KPX o y -20 -KPX o x -30 -KPX o w -20 -KPX o v -30 - -KPX p y -20 - -KPX period space -50 -KPX period quoteright -70 -KPX period quotedblright -70 - -KPX quotedblleft A -50 - -KPX quotedblright space -50 - -KPX quoteleft quoteleft -80 -KPX quoteleft A -50 - -KPX quoteright v -10 -KPX quoteright t 10 -KPX quoteright space -50 -KPX quoteright s -15 -KPX quoteright r -20 -KPX quoteright quoteright -80 -KPX quoteright d -50 - -KPX r y 40 -KPX r v 40 -KPX r u 20 -KPX r t 20 -KPX r s 20 -KPX r q -8 -KPX r period -73 -KPX r p 20 -KPX r o -15 -KPX r n 21 -KPX r m 15 -KPX r l 20 -KPX r k 5 -KPX r i 20 -KPX r hyphen -60 -KPX r g 1 -KPX r e -4 -KPX r d -6 -KPX r comma -75 -KPX r c -7 - -KPX s period 20 -KPX s comma 20 - -KPX space quoteleft -50 -KPX space quotedblleft -50 -KPX space Y -60 -KPX space W -25 -KPX space V -80 -KPX space T -25 -KPX space A -20 - -KPX v period -90 -KPX v o -20 -KPX v e -20 -KPX v comma -90 -KPX v a -30 - -KPX w period -90 -KPX w o -30 -KPX w e -20 -KPX w comma -90 -KPX w a -30 - -KPX x e -20 - -KPX y period -100 -KPX y o -30 -KPX y e -20 -KPX y comma -100 -KPX y c -35 -KPX y a -30 -EndKernPairs -EndKernData -StartComposites 56 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 160 170 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 100 170 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 120 170 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 160 170 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 190 135 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 130 170 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 50 170 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex -10 170 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 10 170 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 50 170 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute -45 170 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex -130 170 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis -110 170 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave -95 170 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 130 170 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 210 170 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 150 170 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 170 170 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 210 170 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 180 170 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron -10 170 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 145 170 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 50 170 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 70 170 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 75 170 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 135 170 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 60 170 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 5 170 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 120 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 60 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 80 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 120 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 150 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 90 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 110 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 50 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 70 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 110 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -65 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -150 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -130 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -115 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 60 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 110 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 50 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 70 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 110 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 80 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron -50 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 125 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 30 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 50 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 55 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 115 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 40 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron -15 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pagdo8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pagdo8a.afm deleted file mode 100644 index c348b11..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pagdo8a.afm +++ /dev/null @@ -1,576 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990, 1991 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Mon Mar 4 13:49:44 1991 -Comment UniqueID 34373 -Comment VMusage 6550 39938 -FontName AvantGarde-DemiOblique -FullName ITC Avant Garde Gothic Demi Oblique -FamilyName ITC Avant Garde Gothic -Weight Demi -ItalicAngle -10.5 -IsFixedPitch false -FontBBox -123 -251 1256 1021 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.007 -Notice Copyright (c) 1985, 1987, 1989, 1990, 1991 Adobe Systems Incorporated. All Rights Reserved.ITC Avant Garde Gothic is a registered trademark of International Typeface Corporation. -EncodingScheme AdobeStandardEncoding -CapHeight 740 -XHeight 555 -Ascender 740 -Descender -185 -StartCharMetrics 228 -C 32 ; WX 280 ; N space ; B 0 0 0 0 ; -C 33 ; WX 280 ; N exclam ; B 73 0 343 740 ; -C 34 ; WX 360 ; N quotedbl ; B 127 444 478 740 ; -C 35 ; WX 560 ; N numbersign ; B 66 0 618 700 ; -C 36 ; WX 560 ; N dollar ; B 99 -86 582 857 ; -C 37 ; WX 860 ; N percent ; B 139 -15 856 755 ; -C 38 ; WX 680 ; N ampersand ; B 71 -15 742 755 ; -C 39 ; WX 280 ; N quoteright ; B 159 466 342 740 ; -C 40 ; WX 380 ; N parenleft ; B 120 -157 490 754 ; -C 41 ; WX 380 ; N parenright ; B 8 -157 378 754 ; -C 42 ; WX 440 ; N asterisk ; B 174 457 492 755 ; -C 43 ; WX 600 ; N plus ; B 84 0 610 506 ; -C 44 ; WX 280 ; N comma ; B 48 -141 231 133 ; -C 45 ; WX 420 ; N hyphen ; B 114 230 413 348 ; -C 46 ; WX 280 ; N period ; B 73 0 231 133 ; -C 47 ; WX 460 ; N slash ; B -13 -100 591 740 ; -C 48 ; WX 560 ; N zero ; B 70 -15 628 755 ; -C 49 ; WX 560 ; N one ; B 230 0 500 740 ; -C 50 ; WX 560 ; N two ; B 44 0 622 755 ; -C 51 ; WX 560 ; N three ; B 67 -15 585 755 ; -C 52 ; WX 560 ; N four ; B 36 0 604 740 ; -C 53 ; WX 560 ; N five ; B 64 -15 600 740 ; -C 54 ; WX 560 ; N six ; B 64 -15 587 739 ; -C 55 ; WX 560 ; N seven ; B 83 0 635 740 ; -C 56 ; WX 560 ; N eight ; B 71 -15 590 755 ; -C 57 ; WX 560 ; N nine ; B 110 0 633 754 ; -C 58 ; WX 280 ; N colon ; B 73 0 309 555 ; -C 59 ; WX 280 ; N semicolon ; B 48 -141 309 555 ; -C 60 ; WX 600 ; N less ; B 84 -8 649 514 ; -C 61 ; WX 600 ; N equal ; B 63 81 631 425 ; -C 62 ; WX 600 ; N greater ; B 45 -8 610 514 ; -C 63 ; WX 560 ; N question ; B 135 0 593 755 ; -C 64 ; WX 740 ; N at ; B 109 -12 832 712 ; -C 65 ; WX 740 ; N A ; B 7 0 732 740 ; -C 66 ; WX 580 ; N B ; B 70 0 610 740 ; -C 67 ; WX 780 ; N C ; B 97 -15 864 755 ; -C 68 ; WX 700 ; N D ; B 63 0 732 740 ; -C 69 ; WX 520 ; N E ; B 61 0 596 740 ; -C 70 ; WX 480 ; N F ; B 61 0 575 740 ; -C 71 ; WX 840 ; N G ; B 89 -15 887 755 ; -C 72 ; WX 680 ; N H ; B 71 0 747 740 ; -C 73 ; WX 280 ; N I ; B 72 0 346 740 ; -C 74 ; WX 480 ; N J ; B 34 -15 546 740 ; -C 75 ; WX 620 ; N K ; B 89 0 757 740 ; -C 76 ; WX 440 ; N L ; B 72 0 459 740 ; -C 77 ; WX 900 ; N M ; B 63 0 974 740 ; -C 78 ; WX 740 ; N N ; B 70 0 808 740 ; -C 79 ; WX 840 ; N O ; B 95 -15 882 755 ; -C 80 ; WX 560 ; N P ; B 72 0 645 740 ; -C 81 ; WX 840 ; N Q ; B 94 -15 882 755 ; -C 82 ; WX 580 ; N R ; B 64 0 656 740 ; -C 83 ; WX 520 ; N S ; B 49 -15 578 755 ; -C 84 ; WX 420 ; N T ; B 119 0 555 740 ; -C 85 ; WX 640 ; N U ; B 97 -15 722 740 ; -C 86 ; WX 700 ; N V ; B 145 0 832 740 ; -C 87 ; WX 900 ; N W ; B 144 0 1036 740 ; -C 88 ; WX 680 ; N X ; B 4 0 813 740 ; -C 89 ; WX 620 ; N Y ; B 135 0 759 740 ; -C 90 ; WX 500 ; N Z ; B 19 0 599 740 ; -C 91 ; WX 320 ; N bracketleft ; B 89 -157 424 754 ; -C 92 ; WX 640 ; N backslash ; B 233 -100 525 740 ; -C 93 ; WX 320 ; N bracketright ; B 7 -157 342 754 ; -C 94 ; WX 600 ; N asciicircum ; B 142 375 596 740 ; -C 95 ; WX 500 ; N underscore ; B -23 -125 486 -75 ; -C 96 ; WX 280 ; N quoteleft ; B 158 466 341 740 ; -C 97 ; WX 660 ; N a ; B 73 -18 716 574 ; -C 98 ; WX 660 ; N b ; B 47 -18 689 740 ; -C 99 ; WX 640 ; N c ; B 84 -18 679 574 ; -C 100 ; WX 660 ; N d ; B 80 -18 755 740 ; -C 101 ; WX 640 ; N e ; B 77 -18 667 577 ; -C 102 ; WX 280 ; N f ; B 62 0 420 755 ; L i fi ; L l fl ; -C 103 ; WX 660 ; N g ; B 33 -226 726 574 ; -C 104 ; WX 600 ; N h ; B 54 0 614 740 ; -C 105 ; WX 240 ; N i ; B 53 0 323 740 ; -C 106 ; WX 260 ; N j ; B -18 -185 342 740 ; -C 107 ; WX 580 ; N k ; B 80 0 648 740 ; -C 108 ; WX 240 ; N l ; B 54 0 324 740 ; -C 109 ; WX 940 ; N m ; B 54 0 954 574 ; -C 110 ; WX 600 ; N n ; B 54 0 613 574 ; -C 111 ; WX 640 ; N o ; B 71 -18 672 574 ; -C 112 ; WX 660 ; N p ; B 13 -185 686 574 ; -C 113 ; WX 660 ; N q ; B 78 -185 716 574 ; -C 114 ; WX 320 ; N r ; B 63 0 423 574 ; -C 115 ; WX 440 ; N s ; B 49 -18 483 574 ; -C 116 ; WX 300 ; N t ; B 86 0 402 740 ; -C 117 ; WX 600 ; N u ; B 87 -18 647 555 ; -C 118 ; WX 560 ; N v ; B 106 0 659 555 ; -C 119 ; WX 800 ; N w ; B 114 0 892 555 ; -C 120 ; WX 560 ; N x ; B 3 0 632 555 ; -C 121 ; WX 580 ; N y ; B 75 -185 674 555 ; -C 122 ; WX 460 ; N z ; B 20 0 528 555 ; -C 123 ; WX 340 ; N braceleft ; B 40 -191 455 747 ; -C 124 ; WX 600 ; N bar ; B 214 -100 503 740 ; -C 125 ; WX 340 ; N braceright ; B -12 -191 405 747 ; -C 126 ; WX 600 ; N asciitilde ; B 114 160 579 347 ; -C 161 ; WX 280 ; N exclamdown ; B 40 -185 310 555 ; -C 162 ; WX 560 ; N cent ; B 110 39 599 715 ; -C 163 ; WX 560 ; N sterling ; B 38 0 615 755 ; -C 164 ; WX 160 ; N fraction ; B -123 0 419 740 ; -C 165 ; WX 560 ; N yen ; B 83 0 707 740 ; -C 166 ; WX 560 ; N florin ; B -27 -151 664 824 ; -C 167 ; WX 560 ; N section ; B 65 -158 602 755 ; -C 168 ; WX 560 ; N currency ; B 53 69 628 577 ; -C 169 ; WX 220 ; N quotesingle ; B 152 444 314 740 ; -C 170 ; WX 480 ; N quotedblleft ; B 156 466 546 740 ; -C 171 ; WX 460 ; N guillemotleft ; B 105 108 487 469 ; -C 172 ; WX 240 ; N guilsinglleft ; B 94 108 277 469 ; -C 173 ; WX 240 ; N guilsinglright ; B 70 108 253 469 ; -C 174 ; WX 520 ; N fi ; B 72 0 598 755 ; -C 175 ; WX 520 ; N fl ; B 72 0 598 755 ; -C 177 ; WX 500 ; N endash ; B 78 230 529 348 ; -C 178 ; WX 560 ; N dagger ; B 133 -142 612 740 ; -C 179 ; WX 560 ; N daggerdbl ; B 63 -142 618 740 ; -C 180 ; WX 280 ; N periodcentered ; B 108 187 265 320 ; -C 182 ; WX 600 ; N paragraph ; B 90 -103 744 740 ; -C 183 ; WX 600 ; N bullet ; B 215 222 526 532 ; -C 184 ; WX 280 ; N quotesinglbase ; B 47 -141 230 133 ; -C 185 ; WX 480 ; N quotedblbase ; B 45 -141 435 133 ; -C 186 ; WX 480 ; N quotedblright ; B 157 466 547 740 ; -C 187 ; WX 460 ; N guillemotright ; B 81 108 463 469 ; -C 188 ; WX 1000 ; N ellipsis ; B 100 0 924 133 ; -C 189 ; WX 1280 ; N perthousand ; B 139 -15 1256 755 ; -C 191 ; WX 560 ; N questiondown ; B 69 -200 527 555 ; -C 193 ; WX 420 ; N grave ; B 189 624 462 851 ; -C 194 ; WX 420 ; N acute ; B 224 624 508 849 ; -C 195 ; WX 540 ; N circumflex ; B 189 636 588 774 ; -C 196 ; WX 480 ; N tilde ; B 178 636 564 767 ; -C 197 ; WX 420 ; N macron ; B 192 648 490 759 ; -C 198 ; WX 480 ; N breve ; B 185 633 582 770 ; -C 199 ; WX 280 ; N dotaccent ; B 192 636 350 769 ; -C 200 ; WX 500 ; N dieresis ; B 196 636 565 769 ; -C 202 ; WX 360 ; N ring ; B 206 619 424 834 ; -C 203 ; WX 340 ; N cedilla ; B 67 -251 272 6 ; -C 205 ; WX 700 ; N hungarumlaut ; B 258 610 754 862 ; -C 206 ; WX 340 ; N ogonek ; B 59 -195 243 9 ; -C 207 ; WX 540 ; N caron ; B 214 636 613 774 ; -C 208 ; WX 1000 ; N emdash ; B 78 230 1029 348 ; -C 225 ; WX 900 ; N AE ; B -5 0 961 740 ; -C 227 ; WX 360 ; N ordfeminine ; B 127 438 472 755 ; -C 232 ; WX 480 ; N Lslash ; B 68 0 484 740 ; -C 233 ; WX 840 ; N Oslash ; B 94 -71 891 814 ; -C 234 ; WX 1060 ; N OE ; B 98 -15 1144 755 ; -C 235 ; WX 360 ; N ordmasculine ; B 131 438 451 755 ; -C 241 ; WX 1080 ; N ae ; B 75 -18 1105 574 ; -C 245 ; WX 240 ; N dotlessi ; B 53 0 289 555 ; -C 248 ; WX 320 ; N lslash ; B 74 0 404 740 ; -C 249 ; WX 660 ; N oslash ; B 81 -50 685 608 ; -C 250 ; WX 1080 ; N oe ; B 76 -18 1108 574 ; -C 251 ; WX 600 ; N germandbls ; B 51 -18 629 755 ; -C -1 ; WX 640 ; N ecircumflex ; B 77 -18 667 774 ; -C -1 ; WX 640 ; N edieresis ; B 77 -18 667 769 ; -C -1 ; WX 660 ; N aacute ; B 73 -18 716 849 ; -C -1 ; WX 740 ; N registered ; B 50 -12 827 752 ; -C -1 ; WX 240 ; N icircumflex ; B 39 0 438 774 ; -C -1 ; WX 600 ; N udieresis ; B 87 -18 647 769 ; -C -1 ; WX 640 ; N ograve ; B 71 -18 672 851 ; -C -1 ; WX 600 ; N uacute ; B 87 -18 647 849 ; -C -1 ; WX 600 ; N ucircumflex ; B 87 -18 647 774 ; -C -1 ; WX 740 ; N Aacute ; B 7 0 732 1019 ; -C -1 ; WX 240 ; N igrave ; B 53 0 347 851 ; -C -1 ; WX 280 ; N Icircumflex ; B 72 0 489 944 ; -C -1 ; WX 640 ; N ccedilla ; B 83 -251 679 574 ; -C -1 ; WX 660 ; N adieresis ; B 73 -18 716 769 ; -C -1 ; WX 520 ; N Ecircumflex ; B 61 0 609 944 ; -C -1 ; WX 440 ; N scaron ; B 49 -18 563 774 ; -C -1 ; WX 660 ; N thorn ; B 13 -185 686 740 ; -C -1 ; WX 1000 ; N trademark ; B 131 296 958 740 ; -C -1 ; WX 640 ; N egrave ; B 77 -18 667 851 ; -C -1 ; WX 336 ; N threesuperior ; B 87 287 413 749 ; -C -1 ; WX 460 ; N zcaron ; B 20 0 598 774 ; -C -1 ; WX 660 ; N atilde ; B 73 -18 716 767 ; -C -1 ; WX 660 ; N aring ; B 73 -18 716 834 ; -C -1 ; WX 640 ; N ocircumflex ; B 71 -18 672 774 ; -C -1 ; WX 520 ; N Edieresis ; B 61 0 606 939 ; -C -1 ; WX 840 ; N threequarters ; B 97 0 836 749 ; -C -1 ; WX 580 ; N ydieresis ; B 75 -185 674 769 ; -C -1 ; WX 580 ; N yacute ; B 75 -185 674 849 ; -C -1 ; WX 240 ; N iacute ; B 53 0 443 849 ; -C -1 ; WX 740 ; N Acircumflex ; B 7 0 732 944 ; -C -1 ; WX 640 ; N Uacute ; B 97 -15 722 1019 ; -C -1 ; WX 640 ; N eacute ; B 77 -18 667 849 ; -C -1 ; WX 840 ; N Ograve ; B 95 -15 882 1021 ; -C -1 ; WX 660 ; N agrave ; B 73 -18 716 851 ; -C -1 ; WX 640 ; N Udieresis ; B 97 -15 722 939 ; -C -1 ; WX 660 ; N acircumflex ; B 73 -18 716 774 ; -C -1 ; WX 280 ; N Igrave ; B 72 0 398 1021 ; -C -1 ; WX 336 ; N twosuperior ; B 73 296 436 749 ; -C -1 ; WX 640 ; N Ugrave ; B 97 -15 722 1021 ; -C -1 ; WX 840 ; N onequarter ; B 187 0 779 740 ; -C -1 ; WX 640 ; N Ucircumflex ; B 97 -15 722 944 ; -C -1 ; WX 520 ; N Scaron ; B 49 -15 635 944 ; -C -1 ; WX 280 ; N Idieresis ; B 72 0 486 939 ; -C -1 ; WX 240 ; N idieresis ; B 53 0 435 769 ; -C -1 ; WX 520 ; N Egrave ; B 61 0 596 1021 ; -C -1 ; WX 840 ; N Oacute ; B 95 -15 882 1019 ; -C -1 ; WX 600 ; N divide ; B 84 -20 610 526 ; -C -1 ; WX 740 ; N Atilde ; B 7 0 732 937 ; -C -1 ; WX 740 ; N Aring ; B 7 0 732 969 ; -C -1 ; WX 840 ; N Odieresis ; B 95 -15 882 939 ; -C -1 ; WX 740 ; N Adieresis ; B 7 0 732 939 ; -C -1 ; WX 740 ; N Ntilde ; B 70 0 808 937 ; -C -1 ; WX 500 ; N Zcaron ; B 19 0 650 944 ; -C -1 ; WX 560 ; N Thorn ; B 72 0 619 740 ; -C -1 ; WX 280 ; N Iacute ; B 72 0 494 1019 ; -C -1 ; WX 600 ; N plusminus ; B 37 -62 626 556 ; -C -1 ; WX 600 ; N multiply ; B 76 12 617 494 ; -C -1 ; WX 520 ; N Eacute ; B 61 0 596 1019 ; -C -1 ; WX 620 ; N Ydieresis ; B 135 0 759 939 ; -C -1 ; WX 336 ; N onesuperior ; B 182 296 360 740 ; -C -1 ; WX 600 ; N ugrave ; B 87 -18 647 851 ; -C -1 ; WX 600 ; N logicalnot ; B 105 108 631 425 ; -C -1 ; WX 600 ; N ntilde ; B 54 0 624 767 ; -C -1 ; WX 840 ; N Otilde ; B 95 -15 882 937 ; -C -1 ; WX 640 ; N otilde ; B 71 -18 672 767 ; -C -1 ; WX 780 ; N Ccedilla ; B 97 -251 864 755 ; -C -1 ; WX 740 ; N Agrave ; B 7 0 732 1021 ; -C -1 ; WX 840 ; N onehalf ; B 157 0 830 740 ; -C -1 ; WX 742 ; N Eth ; B 83 0 766 740 ; -C -1 ; WX 400 ; N degree ; B 160 426 451 712 ; -C -1 ; WX 620 ; N Yacute ; B 135 0 759 1019 ; -C -1 ; WX 840 ; N Ocircumflex ; B 95 -15 882 944 ; -C -1 ; WX 640 ; N oacute ; B 71 -18 672 849 ; -C -1 ; WX 576 ; N mu ; B 3 -187 642 555 ; -C -1 ; WX 600 ; N minus ; B 84 193 610 313 ; -C -1 ; WX 640 ; N eth ; B 73 -18 699 754 ; -C -1 ; WX 640 ; N odieresis ; B 71 -18 672 769 ; -C -1 ; WX 740 ; N copyright ; B 50 -12 827 752 ; -C -1 ; WX 600 ; N brokenbar ; B 214 -100 503 740 ; -EndCharMetrics -StartKernData -StartKernPairs 218 - -KPX A y -50 -KPX A w -65 -KPX A v -70 -KPX A u -20 -KPX A quoteright -90 -KPX A Y -80 -KPX A W -60 -KPX A V -102 -KPX A U -40 -KPX A T -25 -KPX A Q -50 -KPX A O -50 -KPX A G -40 -KPX A C -40 - -KPX B A -10 - -KPX C A -40 - -KPX D period -20 -KPX D comma -20 -KPX D Y -45 -KPX D W -25 -KPX D V -50 -KPX D A -50 - -KPX F period -129 -KPX F e -20 -KPX F comma -162 -KPX F a -20 -KPX F A -75 - -KPX G period -20 -KPX G comma -20 -KPX G Y -15 - -KPX J period -15 -KPX J a -20 -KPX J A -30 - -KPX K y -20 -KPX K u -15 -KPX K o -45 -KPX K e -40 -KPX K O -30 - -KPX L y -23 -KPX L quoteright -30 -KPX L quotedblright -30 -KPX L Y -80 -KPX L W -55 -KPX L V -85 -KPX L T -46 - -KPX O period -30 -KPX O comma -30 -KPX O Y -30 -KPX O X -30 -KPX O W -20 -KPX O V -45 -KPX O T -15 -KPX O A -60 - -KPX P period -200 -KPX P o -20 -KPX P e -20 -KPX P comma -220 -KPX P a -20 -KPX P A -100 - -KPX Q comma 20 - -KPX R W 25 -KPX R V -10 -KPX R U 25 -KPX R T 40 -KPX R O 25 - -KPX S comma 20 - -KPX T y -10 -KPX T w -55 -KPX T u -46 -KPX T semicolon -29 -KPX T r -30 -KPX T period -91 -KPX T o -49 -KPX T hyphen -75 -KPX T e -49 -KPX T comma -82 -KPX T colon -15 -KPX T a -70 -KPX T O -15 -KPX T A -25 - -KPX U period -20 -KPX U comma -20 -KPX U A -40 - -KPX V u -55 -KPX V semicolon -33 -KPX V period -145 -KPX V o -101 -KPX V i -15 -KPX V hyphen -75 -KPX V e -101 -KPX V comma -145 -KPX V colon -18 -KPX V a -95 -KPX V O -45 -KPX V G -20 -KPX V A -102 - -KPX W y -15 -KPX W u -30 -KPX W semicolon -33 -KPX W period -106 -KPX W o -46 -KPX W i -10 -KPX W hyphen -35 -KPX W e -47 -KPX W comma -106 -KPX W colon -15 -KPX W a -50 -KPX W O -20 -KPX W A -58 - -KPX Y u -52 -KPX Y semicolon -23 -KPX Y period -145 -KPX Y o -89 -KPX Y hyphen -100 -KPX Y e -89 -KPX Y comma -145 -KPX Y colon -10 -KPX Y a -93 -KPX Y O -30 -KPX Y A -80 - -KPX a t 5 -KPX a p 20 -KPX a b 5 - -KPX b y -20 -KPX b v -20 - -KPX c y -20 -KPX c l -15 -KPX c k -15 - -KPX comma space -50 -KPX comma quoteright -70 -KPX comma quotedblright -70 - -KPX e y -20 -KPX e x -20 -KPX e w -20 -KPX e v -20 - -KPX f period -40 -KPX f o -20 -KPX f l -15 -KPX f i -15 -KPX f f -20 -KPX f dotlessi -15 -KPX f comma -40 -KPX f a -15 - -KPX g i 25 -KPX g a 15 - -KPX h y -30 - -KPX k y -5 -KPX k o -30 -KPX k e -40 - -KPX m y -20 -KPX m u -20 - -KPX n y -15 -KPX n v -30 - -KPX o y -20 -KPX o x -30 -KPX o w -20 -KPX o v -30 - -KPX p y -20 - -KPX period space -50 -KPX period quoteright -70 -KPX period quotedblright -70 - -KPX quotedblleft A -50 - -KPX quotedblright space -50 - -KPX quoteleft quoteleft -80 -KPX quoteleft A -50 - -KPX quoteright v -10 -KPX quoteright t 10 -KPX quoteright space -50 -KPX quoteright s -15 -KPX quoteright r -20 -KPX quoteright quoteright -80 -KPX quoteright d -50 - -KPX r y 40 -KPX r v 40 -KPX r u 20 -KPX r t 20 -KPX r s 20 -KPX r q -8 -KPX r period -73 -KPX r p 20 -KPX r o -15 -KPX r n 21 -KPX r m 15 -KPX r l 20 -KPX r k 5 -KPX r i 20 -KPX r hyphen -60 -KPX r g 1 -KPX r e -4 -KPX r d -6 -KPX r comma -75 -KPX r c -7 - -KPX s period 20 -KPX s comma 20 - -KPX space quoteleft -50 -KPX space quotedblleft -50 -KPX space Y -60 -KPX space W -25 -KPX space V -80 -KPX space T -25 -KPX space A -20 - -KPX v period -90 -KPX v o -20 -KPX v e -20 -KPX v comma -90 -KPX v a -30 - -KPX w period -90 -KPX w o -30 -KPX w e -20 -KPX w comma -90 -KPX w a -30 - -KPX x e -20 - -KPX y period -100 -KPX y o -30 -KPX y e -20 -KPX y comma -100 -KPX y c -35 -KPX y a -30 -EndKernPairs -EndKernData -StartComposites 56 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 192 170 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 132 170 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 152 170 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 192 170 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 215 135 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 162 170 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 82 170 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 22 170 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 42 170 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 82 170 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute -13 170 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex -98 170 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis -78 170 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave -63 170 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 162 170 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 242 170 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 182 170 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 202 170 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 242 170 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 212 170 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 22 170 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 177 170 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 82 170 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 102 170 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 107 170 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 167 170 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 92 170 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 37 170 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 120 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 60 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 80 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 120 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 150 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 90 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 110 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 50 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 70 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 110 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -65 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -150 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -130 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -115 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 60 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 110 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 50 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 70 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 110 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 80 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron -50 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 125 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 30 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 50 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 55 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 115 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 40 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron -15 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pagk8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pagk8a.afm deleted file mode 100644 index 53b03bb..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pagk8a.afm +++ /dev/null @@ -1,573 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990, 1991 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Mon Mar 4 13:37:31 1991 -Comment UniqueID 34364 -Comment VMusage 24225 31117 -FontName AvantGarde-Book -FullName ITC Avant Garde Gothic Book -FamilyName ITC Avant Garde Gothic -Weight Book -ItalicAngle 0 -IsFixedPitch false -FontBBox -113 -222 1148 955 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.006 -Notice Copyright (c) 1985, 1987, 1989, 1990, 1991 Adobe Systems Incorporated. All Rights Reserved.ITC Avant Garde Gothic is a registered trademark of International Typeface Corporation. -EncodingScheme AdobeStandardEncoding -CapHeight 740 -XHeight 547 -Ascender 740 -Descender -192 -StartCharMetrics 228 -C 32 ; WX 277 ; N space ; B 0 0 0 0 ; -C 33 ; WX 295 ; N exclam ; B 111 0 185 740 ; -C 34 ; WX 309 ; N quotedbl ; B 36 444 273 740 ; -C 35 ; WX 554 ; N numbersign ; B 33 0 521 740 ; -C 36 ; WX 554 ; N dollar ; B 70 -70 485 811 ; -C 37 ; WX 775 ; N percent ; B 21 -13 753 751 ; -C 38 ; WX 757 ; N ampersand ; B 56 -12 736 753 ; -C 39 ; WX 351 ; N quoteright ; B 94 546 256 740 ; -C 40 ; WX 369 ; N parenleft ; B 47 -205 355 757 ; -C 41 ; WX 369 ; N parenright ; B 14 -205 322 757 ; -C 42 ; WX 425 ; N asterisk ; B 58 446 367 740 ; -C 43 ; WX 606 ; N plus ; B 51 0 555 506 ; -C 44 ; WX 277 ; N comma ; B 14 -67 176 126 ; -C 45 ; WX 332 ; N hyphen ; B 30 248 302 315 ; -C 46 ; WX 277 ; N period ; B 102 0 176 126 ; -C 47 ; WX 437 ; N slash ; B 44 -100 403 740 ; -C 48 ; WX 554 ; N zero ; B 29 -13 525 753 ; -C 49 ; WX 554 ; N one ; B 135 0 336 740 ; -C 50 ; WX 554 ; N two ; B 40 0 514 753 ; -C 51 ; WX 554 ; N three ; B 34 -13 506 753 ; -C 52 ; WX 554 ; N four ; B 14 0 528 740 ; -C 53 ; WX 554 ; N five ; B 26 -13 530 740 ; -C 54 ; WX 554 ; N six ; B 24 -13 530 739 ; -C 55 ; WX 554 ; N seven ; B 63 0 491 740 ; -C 56 ; WX 554 ; N eight ; B 41 -13 513 753 ; -C 57 ; WX 554 ; N nine ; B 24 0 530 752 ; -C 58 ; WX 277 ; N colon ; B 102 0 176 548 ; -C 59 ; WX 277 ; N semicolon ; B 14 -67 176 548 ; -C 60 ; WX 606 ; N less ; B 46 -8 554 514 ; -C 61 ; WX 606 ; N equal ; B 51 118 555 388 ; -C 62 ; WX 606 ; N greater ; B 52 -8 560 514 ; -C 63 ; WX 591 ; N question ; B 64 0 526 752 ; -C 64 ; WX 867 ; N at ; B 65 -13 803 753 ; -C 65 ; WX 740 ; N A ; B 12 0 729 740 ; -C 66 ; WX 574 ; N B ; B 74 0 544 740 ; -C 67 ; WX 813 ; N C ; B 43 -13 771 752 ; -C 68 ; WX 744 ; N D ; B 74 0 699 740 ; -C 69 ; WX 536 ; N E ; B 70 0 475 740 ; -C 70 ; WX 485 ; N F ; B 70 0 444 740 ; -C 71 ; WX 872 ; N G ; B 40 -13 828 753 ; -C 72 ; WX 683 ; N H ; B 76 0 607 740 ; -C 73 ; WX 226 ; N I ; B 76 0 150 740 ; -C 74 ; WX 482 ; N J ; B 6 -13 402 740 ; -C 75 ; WX 591 ; N K ; B 81 0 591 740 ; -C 76 ; WX 462 ; N L ; B 82 0 462 740 ; -C 77 ; WX 919 ; N M ; B 76 0 843 740 ; -C 78 ; WX 740 ; N N ; B 75 0 664 740 ; -C 79 ; WX 869 ; N O ; B 43 -13 826 753 ; -C 80 ; WX 592 ; N P ; B 75 0 564 740 ; -C 81 ; WX 871 ; N Q ; B 40 -13 837 753 ; -C 82 ; WX 607 ; N R ; B 70 0 572 740 ; -C 83 ; WX 498 ; N S ; B 22 -13 473 753 ; -C 84 ; WX 426 ; N T ; B 6 0 419 740 ; -C 85 ; WX 655 ; N U ; B 75 -13 579 740 ; -C 86 ; WX 702 ; N V ; B 8 0 693 740 ; -C 87 ; WX 960 ; N W ; B 11 0 950 740 ; -C 88 ; WX 609 ; N X ; B 8 0 602 740 ; -C 89 ; WX 592 ; N Y ; B 1 0 592 740 ; -C 90 ; WX 480 ; N Z ; B 12 0 470 740 ; -C 91 ; WX 351 ; N bracketleft ; B 133 -179 337 753 ; -C 92 ; WX 605 ; N backslash ; B 118 -100 477 740 ; -C 93 ; WX 351 ; N bracketright ; B 14 -179 218 753 ; -C 94 ; WX 606 ; N asciicircum ; B 53 307 553 740 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 351 ; N quoteleft ; B 95 546 257 740 ; -C 97 ; WX 683 ; N a ; B 42 -13 621 561 ; -C 98 ; WX 682 ; N b ; B 68 -13 647 740 ; -C 99 ; WX 647 ; N c ; B 41 -13 607 561 ; -C 100 ; WX 685 ; N d ; B 39 -13 618 740 ; -C 101 ; WX 650 ; N e ; B 38 -13 608 561 ; -C 102 ; WX 314 ; N f ; B 19 0 314 753 ; L i fi ; L l fl ; -C 103 ; WX 673 ; N g ; B 37 -215 606 561 ; -C 104 ; WX 610 ; N h ; B 62 0 543 740 ; -C 105 ; WX 200 ; N i ; B 65 0 135 740 ; -C 106 ; WX 203 ; N j ; B -44 -192 137 740 ; -C 107 ; WX 502 ; N k ; B 70 0 498 740 ; -C 108 ; WX 200 ; N l ; B 65 0 135 740 ; -C 109 ; WX 938 ; N m ; B 66 0 872 561 ; -C 110 ; WX 610 ; N n ; B 65 0 546 561 ; -C 111 ; WX 655 ; N o ; B 42 -13 614 561 ; -C 112 ; WX 682 ; N p ; B 64 -192 643 561 ; -C 113 ; WX 682 ; N q ; B 37 -192 616 561 ; -C 114 ; WX 301 ; N r ; B 65 0 291 561 ; -C 115 ; WX 388 ; N s ; B 24 -13 364 561 ; -C 116 ; WX 339 ; N t ; B 14 0 330 740 ; -C 117 ; WX 608 ; N u ; B 62 -13 541 547 ; -C 118 ; WX 554 ; N v ; B 7 0 546 547 ; -C 119 ; WX 831 ; N w ; B 13 0 820 547 ; -C 120 ; WX 480 ; N x ; B 12 0 468 547 ; -C 121 ; WX 536 ; N y ; B 15 -192 523 547 ; -C 122 ; WX 425 ; N z ; B 10 0 415 547 ; -C 123 ; WX 351 ; N braceleft ; B 70 -189 331 740 ; -C 124 ; WX 672 ; N bar ; B 299 -100 373 740 ; -C 125 ; WX 351 ; N braceright ; B 20 -189 281 740 ; -C 126 ; WX 606 ; N asciitilde ; B 72 179 534 319 ; -C 161 ; WX 295 ; N exclamdown ; B 110 -192 184 548 ; -C 162 ; WX 554 ; N cent ; B 48 62 510 707 ; -C 163 ; WX 554 ; N sterling ; B 4 0 552 753 ; -C 164 ; WX 166 ; N fraction ; B -113 0 280 740 ; -C 165 ; WX 554 ; N yen ; B 4 0 550 740 ; -C 166 ; WX 554 ; N florin ; B -12 -153 518 818 ; -C 167 ; WX 615 ; N section ; B 85 -141 529 753 ; -C 168 ; WX 554 ; N currency ; B 8 42 546 580 ; -C 169 ; WX 198 ; N quotesingle ; B 59 444 140 740 ; -C 170 ; WX 502 ; N quotedblleft ; B 97 546 406 740 ; -C 171 ; WX 425 ; N guillemotleft ; B 40 81 386 481 ; -C 172 ; WX 251 ; N guilsinglleft ; B 40 81 212 481 ; -C 173 ; WX 251 ; N guilsinglright ; B 39 81 211 481 ; -C 174 ; WX 487 ; N fi ; B 19 0 422 753 ; -C 175 ; WX 485 ; N fl ; B 19 0 420 753 ; -C 177 ; WX 500 ; N endash ; B 35 248 465 315 ; -C 178 ; WX 553 ; N dagger ; B 59 -133 493 740 ; -C 179 ; WX 553 ; N daggerdbl ; B 59 -133 493 740 ; -C 180 ; WX 277 ; N periodcentered ; B 102 190 176 316 ; -C 182 ; WX 564 ; N paragraph ; B 22 -110 551 740 ; -C 183 ; WX 606 ; N bullet ; B 150 222 455 532 ; -C 184 ; WX 354 ; N quotesinglbase ; B 89 -68 251 126 ; -C 185 ; WX 502 ; N quotedblbase ; B 89 -68 399 126 ; -C 186 ; WX 484 ; N quotedblright ; B 96 546 405 740 ; -C 187 ; WX 425 ; N guillemotright ; B 39 81 385 481 ; -C 188 ; WX 1000 ; N ellipsis ; B 130 0 870 126 ; -C 189 ; WX 1174 ; N perthousand ; B 25 -13 1148 751 ; -C 191 ; WX 591 ; N questiondown ; B 65 -205 527 548 ; -C 193 ; WX 378 ; N grave ; B 69 619 300 786 ; -C 194 ; WX 375 ; N acute ; B 78 619 309 786 ; -C 195 ; WX 502 ; N circumflex ; B 74 639 428 764 ; -C 196 ; WX 439 ; N tilde ; B 47 651 392 754 ; -C 197 ; WX 485 ; N macron ; B 73 669 411 736 ; -C 198 ; WX 453 ; N breve ; B 52 651 401 754 ; -C 199 ; WX 222 ; N dotaccent ; B 74 639 148 765 ; -C 200 ; WX 369 ; N dieresis ; B 73 639 295 765 ; -C 202 ; WX 332 ; N ring ; B 62 600 269 807 ; -C 203 ; WX 324 ; N cedilla ; B 80 -222 254 0 ; -C 205 ; WX 552 ; N hungarumlaut ; B 119 605 453 800 ; -C 206 ; WX 302 ; N ogonek ; B 73 -191 228 0 ; -C 207 ; WX 502 ; N caron ; B 68 639 423 764 ; -C 208 ; WX 1000 ; N emdash ; B 35 248 965 315 ; -C 225 ; WX 992 ; N AE ; B -20 0 907 740 ; -C 227 ; WX 369 ; N ordfeminine ; B -3 407 356 753 ; -C 232 ; WX 517 ; N Lslash ; B 59 0 517 740 ; -C 233 ; WX 868 ; N Oslash ; B 43 -83 826 819 ; -C 234 ; WX 1194 ; N OE ; B 45 -13 1142 753 ; -C 235 ; WX 369 ; N ordmasculine ; B 12 407 356 753 ; -C 241 ; WX 1157 ; N ae ; B 34 -13 1113 561 ; -C 245 ; WX 200 ; N dotlessi ; B 65 0 135 547 ; -C 248 ; WX 300 ; N lslash ; B 43 0 259 740 ; -C 249 ; WX 653 ; N oslash ; B 41 -64 613 614 ; -C 250 ; WX 1137 ; N oe ; B 34 -13 1104 561 ; -C 251 ; WX 554 ; N germandbls ; B 61 -13 525 753 ; -C -1 ; WX 650 ; N ecircumflex ; B 38 -13 608 764 ; -C -1 ; WX 650 ; N edieresis ; B 38 -13 608 765 ; -C -1 ; WX 683 ; N aacute ; B 42 -13 621 786 ; -C -1 ; WX 747 ; N registered ; B -9 -12 755 752 ; -C -1 ; WX 200 ; N icircumflex ; B -77 0 277 764 ; -C -1 ; WX 608 ; N udieresis ; B 62 -13 541 765 ; -C -1 ; WX 655 ; N ograve ; B 42 -13 614 786 ; -C -1 ; WX 608 ; N uacute ; B 62 -13 541 786 ; -C -1 ; WX 608 ; N ucircumflex ; B 62 -13 541 764 ; -C -1 ; WX 740 ; N Aacute ; B 12 0 729 949 ; -C -1 ; WX 200 ; N igrave ; B -60 0 171 786 ; -C -1 ; WX 226 ; N Icircumflex ; B -64 0 290 927 ; -C -1 ; WX 647 ; N ccedilla ; B 41 -222 607 561 ; -C -1 ; WX 683 ; N adieresis ; B 42 -13 621 765 ; -C -1 ; WX 536 ; N Ecircumflex ; B 70 0 475 927 ; -C -1 ; WX 388 ; N scaron ; B 11 -13 366 764 ; -C -1 ; WX 682 ; N thorn ; B 64 -192 643 740 ; -C -1 ; WX 1000 ; N trademark ; B 9 296 816 740 ; -C -1 ; WX 650 ; N egrave ; B 38 -13 608 786 ; -C -1 ; WX 332 ; N threesuperior ; B 18 289 318 747 ; -C -1 ; WX 425 ; N zcaron ; B 10 0 415 764 ; -C -1 ; WX 683 ; N atilde ; B 42 -13 621 754 ; -C -1 ; WX 683 ; N aring ; B 42 -13 621 807 ; -C -1 ; WX 655 ; N ocircumflex ; B 42 -13 614 764 ; -C -1 ; WX 536 ; N Edieresis ; B 70 0 475 928 ; -C -1 ; WX 831 ; N threequarters ; B 46 0 784 747 ; -C -1 ; WX 536 ; N ydieresis ; B 15 -192 523 765 ; -C -1 ; WX 536 ; N yacute ; B 15 -192 523 786 ; -C -1 ; WX 200 ; N iacute ; B 31 0 262 786 ; -C -1 ; WX 740 ; N Acircumflex ; B 12 0 729 927 ; -C -1 ; WX 655 ; N Uacute ; B 75 -13 579 949 ; -C -1 ; WX 650 ; N eacute ; B 38 -13 608 786 ; -C -1 ; WX 869 ; N Ograve ; B 43 -13 826 949 ; -C -1 ; WX 683 ; N agrave ; B 42 -13 621 786 ; -C -1 ; WX 655 ; N Udieresis ; B 75 -13 579 928 ; -C -1 ; WX 683 ; N acircumflex ; B 42 -13 621 764 ; -C -1 ; WX 226 ; N Igrave ; B -47 0 184 949 ; -C -1 ; WX 332 ; N twosuperior ; B 19 296 318 747 ; -C -1 ; WX 655 ; N Ugrave ; B 75 -13 579 949 ; -C -1 ; WX 831 ; N onequarter ; B 100 0 729 740 ; -C -1 ; WX 655 ; N Ucircumflex ; B 75 -13 579 927 ; -C -1 ; WX 498 ; N Scaron ; B 22 -13 473 927 ; -C -1 ; WX 226 ; N Idieresis ; B 2 0 224 928 ; -C -1 ; WX 200 ; N idieresis ; B -11 0 211 765 ; -C -1 ; WX 536 ; N Egrave ; B 70 0 475 949 ; -C -1 ; WX 869 ; N Oacute ; B 43 -13 826 949 ; -C -1 ; WX 606 ; N divide ; B 51 -13 555 519 ; -C -1 ; WX 740 ; N Atilde ; B 12 0 729 917 ; -C -1 ; WX 740 ; N Aring ; B 12 0 729 955 ; -C -1 ; WX 869 ; N Odieresis ; B 43 -13 826 928 ; -C -1 ; WX 740 ; N Adieresis ; B 12 0 729 928 ; -C -1 ; WX 740 ; N Ntilde ; B 75 0 664 917 ; -C -1 ; WX 480 ; N Zcaron ; B 12 0 470 927 ; -C -1 ; WX 592 ; N Thorn ; B 60 0 549 740 ; -C -1 ; WX 226 ; N Iacute ; B 44 0 275 949 ; -C -1 ; WX 606 ; N plusminus ; B 51 -24 555 518 ; -C -1 ; WX 606 ; N multiply ; B 74 24 533 482 ; -C -1 ; WX 536 ; N Eacute ; B 70 0 475 949 ; -C -1 ; WX 592 ; N Ydieresis ; B 1 0 592 928 ; -C -1 ; WX 332 ; N onesuperior ; B 63 296 198 740 ; -C -1 ; WX 608 ; N ugrave ; B 62 -13 541 786 ; -C -1 ; WX 606 ; N logicalnot ; B 51 109 555 388 ; -C -1 ; WX 610 ; N ntilde ; B 65 0 546 754 ; -C -1 ; WX 869 ; N Otilde ; B 43 -13 826 917 ; -C -1 ; WX 655 ; N otilde ; B 42 -13 614 754 ; -C -1 ; WX 813 ; N Ccedilla ; B 43 -222 771 752 ; -C -1 ; WX 740 ; N Agrave ; B 12 0 729 949 ; -C -1 ; WX 831 ; N onehalf ; B 81 0 750 740 ; -C -1 ; WX 790 ; N Eth ; B 40 0 739 740 ; -C -1 ; WX 400 ; N degree ; B 56 421 344 709 ; -C -1 ; WX 592 ; N Yacute ; B 1 0 592 949 ; -C -1 ; WX 869 ; N Ocircumflex ; B 43 -13 826 927 ; -C -1 ; WX 655 ; N oacute ; B 42 -13 614 786 ; -C -1 ; WX 608 ; N mu ; B 80 -184 527 547 ; -C -1 ; WX 606 ; N minus ; B 51 219 555 287 ; -C -1 ; WX 655 ; N eth ; B 42 -12 614 753 ; -C -1 ; WX 655 ; N odieresis ; B 42 -13 614 765 ; -C -1 ; WX 747 ; N copyright ; B -9 -12 755 752 ; -C -1 ; WX 672 ; N brokenbar ; B 299 -100 373 740 ; -EndCharMetrics -StartKernData -StartKernPairs 216 - -KPX A y -62 -KPX A w -65 -KPX A v -70 -KPX A u -20 -KPX A quoteright -100 -KPX A quotedblright -100 -KPX A Y -92 -KPX A W -60 -KPX A V -102 -KPX A U -40 -KPX A T -45 -KPX A Q -40 -KPX A O -50 -KPX A G -40 -KPX A C -40 - -KPX B A -10 - -KPX C A -40 - -KPX D period -20 -KPX D comma -20 -KPX D Y -30 -KPX D W -10 -KPX D V -50 -KPX D A -50 - -KPX F period -160 -KPX F e -20 -KPX F comma -180 -KPX F a -20 -KPX F A -75 - -KPX G period -20 -KPX G comma -20 -KPX G Y -20 - -KPX J period -15 -KPX J a -20 -KPX J A -30 - -KPX K o -15 -KPX K e -20 -KPX K O -20 - -KPX L y -23 -KPX L quoteright -130 -KPX L quotedblright -130 -KPX L Y -91 -KPX L W -67 -KPX L V -113 -KPX L T -46 - -KPX O period -30 -KPX O comma -30 -KPX O Y -30 -KPX O X -30 -KPX O W -20 -KPX O V -60 -KPX O T -30 -KPX O A -60 - -KPX P period -300 -KPX P o -60 -KPX P e -20 -KPX P comma -280 -KPX P a -20 -KPX P A -114 - -KPX Q comma 20 - -KPX R Y -10 -KPX R W 10 -KPX R V -10 -KPX R T 6 - -KPX S comma 20 - -KPX T y -50 -KPX T w -55 -KPX T u -46 -KPX T semicolon -29 -KPX T r -30 -KPX T period -91 -KPX T o -70 -KPX T i 10 -KPX T hyphen -75 -KPX T e -49 -KPX T comma -82 -KPX T colon -15 -KPX T a -90 -KPX T O -30 -KPX T A -45 - -KPX U period -20 -KPX U comma -20 -KPX U A -40 - -KPX V u -40 -KPX V semicolon -33 -KPX V period -165 -KPX V o -101 -KPX V i -5 -KPX V hyphen -75 -KPX V e -101 -KPX V comma -145 -KPX V colon -18 -KPX V a -104 -KPX V O -60 -KPX V G -20 -KPX V A -102 - -KPX W y -2 -KPX W u -30 -KPX W semicolon -33 -KPX W period -106 -KPX W o -46 -KPX W i 6 -KPX W hyphen -35 -KPX W e -47 -KPX W comma -106 -KPX W colon -15 -KPX W a -50 -KPX W O -20 -KPX W A -58 - -KPX Y u -52 -KPX Y semicolon -23 -KPX Y period -175 -KPX Y o -89 -KPX Y hyphen -85 -KPX Y e -89 -KPX Y comma -145 -KPX Y colon -10 -KPX Y a -93 -KPX Y O -30 -KPX Y A -92 - -KPX a p 20 -KPX a b 20 - -KPX b y -20 -KPX b v -20 - -KPX c y -20 -KPX c k -15 - -KPX comma space -110 -KPX comma quoteright -120 -KPX comma quotedblright -120 - -KPX e y -20 -KPX e w -20 -KPX e v -20 - -KPX f period -50 -KPX f o -40 -KPX f l -30 -KPX f i -34 -KPX f f -60 -KPX f e -20 -KPX f dotlessi -34 -KPX f comma -50 -KPX f a -40 - -KPX g a -15 - -KPX h y -30 - -KPX k y -5 -KPX k e -15 - -KPX m y -20 -KPX m u -20 -KPX m a -20 - -KPX n y -15 -KPX n v -20 - -KPX o y -20 -KPX o x -15 -KPX o w -20 -KPX o v -30 - -KPX p y -20 - -KPX period space -110 -KPX period quoteright -120 -KPX period quotedblright -120 - -KPX quotedblleft quoteleft -35 -KPX quotedblleft A -100 - -KPX quotedblright space -110 - -KPX quoteleft quoteleft -203 -KPX quoteleft A -100 - -KPX quoteright v -30 -KPX quoteright t 10 -KPX quoteright space -110 -KPX quoteright s -15 -KPX quoteright r -20 -KPX quoteright quoteright -203 -KPX quoteright quotedblright -35 -KPX quoteright d -110 - -KPX r y 40 -KPX r v 40 -KPX r u 20 -KPX r t 20 -KPX r s 20 -KPX r q -8 -KPX r period -73 -KPX r p 20 -KPX r o -20 -KPX r n 21 -KPX r m 28 -KPX r l 20 -KPX r k 20 -KPX r i 20 -KPX r hyphen -60 -KPX r g -15 -KPX r e -4 -KPX r d -6 -KPX r comma -75 -KPX r c -20 -KPX r a -20 - -KPX s period 20 -KPX s comma 20 - -KPX space quoteleft -110 -KPX space quotedblleft -110 -KPX space Y -60 -KPX space W -25 -KPX space V -50 -KPX space T -25 -KPX space A -20 - -KPX v period -130 -KPX v o -30 -KPX v e -20 -KPX v comma -100 -KPX v a -30 - -KPX w period -100 -KPX w o -30 -KPX w h 15 -KPX w e -20 -KPX w comma -90 -KPX w a -30 - -KPX y period -125 -KPX y o -30 -KPX y e -20 -KPX y comma -110 -KPX y a -30 -EndKernPairs -EndKernData -StartComposites 56 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 183 163 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 119 163 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 186 163 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 181 163 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 204 148 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 151 163 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 81 163 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 17 163 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 84 163 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 79 163 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute -34 163 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex -138 163 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis -71 163 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave -116 163 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 151 163 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 247 163 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 184 163 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 250 163 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 246 163 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 215 163 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron -2 163 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 160 163 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 77 163 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 143 163 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 119 163 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 129 163 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 112 163 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron -11 163 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 154 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 91 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 157 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 153 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 176 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 122 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 138 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 74 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 141 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 136 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -47 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -151 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -84 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -129 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 86 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 140 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 77 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 143 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 139 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 108 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron -57 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 137 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 53 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 120 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 95 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 101 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 84 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron -38 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pagko8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pagko8a.afm deleted file mode 100644 index e0e75f3..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pagko8a.afm +++ /dev/null @@ -1,573 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990, 1991 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Mon Mar 4 13:41:11 1991 -Comment UniqueID 34367 -Comment VMusage 6555 39267 -FontName AvantGarde-BookOblique -FullName ITC Avant Garde Gothic Book Oblique -FamilyName ITC Avant Garde Gothic -Weight Book -ItalicAngle -10.5 -IsFixedPitch false -FontBBox -113 -222 1279 955 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.006 -Notice Copyright (c) 1985, 1987, 1989, 1990, 1991 Adobe Systems Incorporated. All Rights Reserved.ITC Avant Garde Gothic is a registered trademark of International Typeface Corporation. -EncodingScheme AdobeStandardEncoding -CapHeight 740 -XHeight 547 -Ascender 740 -Descender -192 -StartCharMetrics 228 -C 32 ; WX 277 ; N space ; B 0 0 0 0 ; -C 33 ; WX 295 ; N exclam ; B 111 0 322 740 ; -C 34 ; WX 309 ; N quotedbl ; B 130 444 410 740 ; -C 35 ; WX 554 ; N numbersign ; B 71 0 620 740 ; -C 36 ; WX 554 ; N dollar ; B 107 -70 581 811 ; -C 37 ; WX 775 ; N percent ; B 124 -13 787 751 ; -C 38 ; WX 757 ; N ampersand ; B 92 -12 775 753 ; -C 39 ; WX 351 ; N quoteright ; B 195 546 393 740 ; -C 40 ; WX 369 ; N parenleft ; B 89 -205 495 757 ; -C 41 ; WX 369 ; N parenright ; B -24 -205 382 757 ; -C 42 ; WX 425 ; N asterisk ; B 170 446 479 740 ; -C 43 ; WX 606 ; N plus ; B 92 0 608 506 ; -C 44 ; WX 277 ; N comma ; B 2 -67 199 126 ; -C 45 ; WX 332 ; N hyphen ; B 76 248 360 315 ; -C 46 ; WX 277 ; N period ; B 102 0 199 126 ; -C 47 ; WX 437 ; N slash ; B 25 -100 540 740 ; -C 48 ; WX 554 ; N zero ; B 71 -13 622 753 ; -C 49 ; WX 554 ; N one ; B 260 0 473 740 ; -C 50 ; WX 554 ; N two ; B 40 0 615 753 ; -C 51 ; WX 554 ; N three ; B 73 -13 565 753 ; -C 52 ; WX 554 ; N four ; B 39 0 598 740 ; -C 53 ; WX 554 ; N five ; B 69 -13 605 740 ; -C 54 ; WX 554 ; N six ; B 65 -13 580 739 ; -C 55 ; WX 554 ; N seven ; B 110 0 628 740 ; -C 56 ; WX 554 ; N eight ; B 77 -13 580 753 ; -C 57 ; WX 554 ; N nine ; B 111 0 626 752 ; -C 58 ; WX 277 ; N colon ; B 102 0 278 548 ; -C 59 ; WX 277 ; N semicolon ; B 2 -67 278 548 ; -C 60 ; WX 606 ; N less ; B 87 -8 649 514 ; -C 61 ; WX 606 ; N equal ; B 73 118 627 388 ; -C 62 ; WX 606 ; N greater ; B 51 -8 613 514 ; -C 63 ; WX 591 ; N question ; B 158 0 628 752 ; -C 64 ; WX 867 ; N at ; B 126 -13 888 753 ; -C 65 ; WX 740 ; N A ; B 12 0 729 740 ; -C 66 ; WX 574 ; N B ; B 74 0 606 740 ; -C 67 ; WX 813 ; N C ; B 105 -13 870 752 ; -C 68 ; WX 744 ; N D ; B 74 0 773 740 ; -C 69 ; WX 536 ; N E ; B 70 0 612 740 ; -C 70 ; WX 485 ; N F ; B 70 0 581 740 ; -C 71 ; WX 872 ; N G ; B 103 -13 891 753 ; -C 72 ; WX 683 ; N H ; B 76 0 744 740 ; -C 73 ; WX 226 ; N I ; B 76 0 287 740 ; -C 74 ; WX 482 ; N J ; B 37 -13 539 740 ; -C 75 ; WX 591 ; N K ; B 81 0 728 740 ; -C 76 ; WX 462 ; N L ; B 82 0 474 740 ; -C 77 ; WX 919 ; N M ; B 76 0 980 740 ; -C 78 ; WX 740 ; N N ; B 75 0 801 740 ; -C 79 ; WX 869 ; N O ; B 105 -13 901 753 ; -C 80 ; WX 592 ; N P ; B 75 0 664 740 ; -C 81 ; WX 871 ; N Q ; B 102 -13 912 753 ; -C 82 ; WX 607 ; N R ; B 70 0 669 740 ; -C 83 ; WX 498 ; N S ; B 57 -13 561 753 ; -C 84 ; WX 426 ; N T ; B 131 0 556 740 ; -C 85 ; WX 655 ; N U ; B 118 -13 716 740 ; -C 86 ; WX 702 ; N V ; B 145 0 830 740 ; -C 87 ; WX 960 ; N W ; B 148 0 1087 740 ; -C 88 ; WX 609 ; N X ; B 8 0 724 740 ; -C 89 ; WX 592 ; N Y ; B 138 0 729 740 ; -C 90 ; WX 480 ; N Z ; B 12 0 596 740 ; -C 91 ; WX 351 ; N bracketleft ; B 145 -179 477 753 ; -C 92 ; WX 605 ; N backslash ; B 255 -100 458 740 ; -C 93 ; WX 351 ; N bracketright ; B -19 -179 312 753 ; -C 94 ; WX 606 ; N asciicircum ; B 110 307 610 740 ; -C 95 ; WX 500 ; N underscore ; B -23 -125 486 -75 ; -C 96 ; WX 351 ; N quoteleft ; B 232 546 358 740 ; -C 97 ; WX 683 ; N a ; B 88 -13 722 561 ; -C 98 ; WX 682 ; N b ; B 68 -13 703 740 ; -C 99 ; WX 647 ; N c ; B 87 -13 678 561 ; -C 100 ; WX 685 ; N d ; B 85 -13 755 740 ; -C 101 ; WX 650 ; N e ; B 84 -13 664 561 ; -C 102 ; WX 314 ; N f ; B 104 0 454 753 ; L i fi ; L l fl ; -C 103 ; WX 673 ; N g ; B 56 -215 707 561 ; -C 104 ; WX 610 ; N h ; B 62 0 606 740 ; -C 105 ; WX 200 ; N i ; B 65 0 272 740 ; -C 106 ; WX 203 ; N j ; B -80 -192 274 740 ; -C 107 ; WX 502 ; N k ; B 70 0 588 740 ; -C 108 ; WX 200 ; N l ; B 65 0 272 740 ; -C 109 ; WX 938 ; N m ; B 66 0 938 561 ; -C 110 ; WX 610 ; N n ; B 65 0 609 561 ; -C 111 ; WX 655 ; N o ; B 88 -13 669 561 ; -C 112 ; WX 682 ; N p ; B 28 -192 699 561 ; -C 113 ; WX 682 ; N q ; B 83 -192 717 561 ; -C 114 ; WX 301 ; N r ; B 65 0 395 561 ; -C 115 ; WX 388 ; N s ; B 49 -13 424 561 ; -C 116 ; WX 339 ; N t ; B 104 0 431 740 ; -C 117 ; WX 608 ; N u ; B 100 -13 642 547 ; -C 118 ; WX 554 ; N v ; B 108 0 647 547 ; -C 119 ; WX 831 ; N w ; B 114 0 921 547 ; -C 120 ; WX 480 ; N x ; B 12 0 569 547 ; -C 121 ; WX 536 ; N y ; B 97 -192 624 547 ; -C 122 ; WX 425 ; N z ; B 10 0 498 547 ; -C 123 ; WX 351 ; N braceleft ; B 115 -189 468 740 ; -C 124 ; WX 672 ; N bar ; B 280 -100 510 740 ; -C 125 ; WX 351 ; N braceright ; B -15 -189 338 740 ; -C 126 ; WX 606 ; N asciitilde ; B 114 179 584 319 ; -C 161 ; WX 295 ; N exclamdown ; B 74 -192 286 548 ; -C 162 ; WX 554 ; N cent ; B 115 62 596 707 ; -C 163 ; WX 554 ; N sterling ; B 29 0 614 753 ; -C 164 ; WX 166 ; N fraction ; B -113 0 417 740 ; -C 165 ; WX 554 ; N yen ; B 75 0 687 740 ; -C 166 ; WX 554 ; N florin ; B -39 -153 669 818 ; -C 167 ; WX 615 ; N section ; B 118 -141 597 753 ; -C 168 ; WX 554 ; N currency ; B 24 42 645 580 ; -C 169 ; WX 198 ; N quotesingle ; B 153 444 277 740 ; -C 170 ; WX 502 ; N quotedblleft ; B 234 546 507 740 ; -C 171 ; WX 425 ; N guillemotleft ; B 92 81 469 481 ; -C 172 ; WX 251 ; N guilsinglleft ; B 92 81 295 481 ; -C 173 ; WX 251 ; N guilsinglright ; B 60 81 263 481 ; -C 174 ; WX 487 ; N fi ; B 104 0 559 753 ; -C 175 ; WX 485 ; N fl ; B 104 0 557 753 ; -C 177 ; WX 500 ; N endash ; B 81 248 523 315 ; -C 178 ; WX 553 ; N dagger ; B 146 -133 593 740 ; -C 179 ; WX 553 ; N daggerdbl ; B 72 -133 593 740 ; -C 180 ; WX 277 ; N periodcentered ; B 137 190 235 316 ; -C 182 ; WX 564 ; N paragraph ; B 119 -110 688 740 ; -C 183 ; WX 606 ; N bullet ; B 217 222 528 532 ; -C 184 ; WX 354 ; N quotesinglbase ; B 76 -68 274 126 ; -C 185 ; WX 502 ; N quotedblbase ; B 76 -68 422 126 ; -C 186 ; WX 484 ; N quotedblright ; B 197 546 542 740 ; -C 187 ; WX 425 ; N guillemotright ; B 60 81 437 481 ; -C 188 ; WX 1000 ; N ellipsis ; B 130 0 893 126 ; -C 189 ; WX 1174 ; N perthousand ; B 128 -13 1182 751 ; -C 191 ; WX 591 ; N questiondown ; B 64 -205 534 548 ; -C 193 ; WX 378 ; N grave ; B 204 619 425 786 ; -C 194 ; WX 375 ; N acute ; B 203 619 444 786 ; -C 195 ; WX 502 ; N circumflex ; B 192 639 546 764 ; -C 196 ; WX 439 ; N tilde ; B 179 651 520 754 ; -C 197 ; WX 485 ; N macron ; B 197 669 547 736 ; -C 198 ; WX 453 ; N breve ; B 192 651 541 754 ; -C 199 ; WX 222 ; N dotaccent ; B 192 639 290 765 ; -C 200 ; WX 369 ; N dieresis ; B 191 639 437 765 ; -C 202 ; WX 332 ; N ring ; B 191 600 401 807 ; -C 203 ; WX 324 ; N cedilla ; B 52 -222 231 0 ; -C 205 ; WX 552 ; N hungarumlaut ; B 239 605 594 800 ; -C 206 ; WX 302 ; N ogonek ; B 53 -191 202 0 ; -C 207 ; WX 502 ; N caron ; B 210 639 565 764 ; -C 208 ; WX 1000 ; N emdash ; B 81 248 1023 315 ; -C 225 ; WX 992 ; N AE ; B -20 0 1044 740 ; -C 227 ; WX 369 ; N ordfeminine ; B 102 407 494 753 ; -C 232 ; WX 517 ; N Lslash ; B 107 0 529 740 ; -C 233 ; WX 868 ; N Oslash ; B 76 -83 929 819 ; -C 234 ; WX 1194 ; N OE ; B 107 -13 1279 753 ; -C 235 ; WX 369 ; N ordmasculine ; B 116 407 466 753 ; -C 241 ; WX 1157 ; N ae ; B 80 -13 1169 561 ; -C 245 ; WX 200 ; N dotlessi ; B 65 0 236 547 ; -C 248 ; WX 300 ; N lslash ; B 95 0 354 740 ; -C 249 ; WX 653 ; N oslash ; B 51 -64 703 614 ; -C 250 ; WX 1137 ; N oe ; B 80 -13 1160 561 ; -C 251 ; WX 554 ; N germandbls ; B 61 -13 578 753 ; -C -1 ; WX 650 ; N ecircumflex ; B 84 -13 664 764 ; -C -1 ; WX 650 ; N edieresis ; B 84 -13 664 765 ; -C -1 ; WX 683 ; N aacute ; B 88 -13 722 786 ; -C -1 ; WX 747 ; N registered ; B 53 -12 830 752 ; -C -1 ; WX 200 ; N icircumflex ; B 41 0 395 764 ; -C -1 ; WX 608 ; N udieresis ; B 100 -13 642 765 ; -C -1 ; WX 655 ; N ograve ; B 88 -13 669 786 ; -C -1 ; WX 608 ; N uacute ; B 100 -13 642 786 ; -C -1 ; WX 608 ; N ucircumflex ; B 100 -13 642 764 ; -C -1 ; WX 740 ; N Aacute ; B 12 0 729 949 ; -C -1 ; WX 200 ; N igrave ; B 65 0 296 786 ; -C -1 ; WX 226 ; N Icircumflex ; B 76 0 439 927 ; -C -1 ; WX 647 ; N ccedilla ; B 87 -222 678 561 ; -C -1 ; WX 683 ; N adieresis ; B 88 -13 722 765 ; -C -1 ; WX 536 ; N Ecircumflex ; B 70 0 612 927 ; -C -1 ; WX 388 ; N scaron ; B 49 -13 508 764 ; -C -1 ; WX 682 ; N thorn ; B 28 -192 699 740 ; -C -1 ; WX 1000 ; N trademark ; B 137 296 953 740 ; -C -1 ; WX 650 ; N egrave ; B 84 -13 664 786 ; -C -1 ; WX 332 ; N threesuperior ; B 98 289 408 747 ; -C -1 ; WX 425 ; N zcaron ; B 10 0 527 764 ; -C -1 ; WX 683 ; N atilde ; B 88 -13 722 754 ; -C -1 ; WX 683 ; N aring ; B 88 -13 722 807 ; -C -1 ; WX 655 ; N ocircumflex ; B 88 -13 669 764 ; -C -1 ; WX 536 ; N Edieresis ; B 70 0 612 928 ; -C -1 ; WX 831 ; N threequarters ; B 126 0 825 747 ; -C -1 ; WX 536 ; N ydieresis ; B 97 -192 624 765 ; -C -1 ; WX 536 ; N yacute ; B 97 -192 624 786 ; -C -1 ; WX 200 ; N iacute ; B 65 0 397 786 ; -C -1 ; WX 740 ; N Acircumflex ; B 12 0 729 927 ; -C -1 ; WX 655 ; N Uacute ; B 118 -13 716 949 ; -C -1 ; WX 650 ; N eacute ; B 84 -13 664 786 ; -C -1 ; WX 869 ; N Ograve ; B 105 -13 901 949 ; -C -1 ; WX 683 ; N agrave ; B 88 -13 722 786 ; -C -1 ; WX 655 ; N Udieresis ; B 118 -13 716 928 ; -C -1 ; WX 683 ; N acircumflex ; B 88 -13 722 764 ; -C -1 ; WX 226 ; N Igrave ; B 76 0 340 949 ; -C -1 ; WX 332 ; N twosuperior ; B 74 296 433 747 ; -C -1 ; WX 655 ; N Ugrave ; B 118 -13 716 949 ; -C -1 ; WX 831 ; N onequarter ; B 183 0 770 740 ; -C -1 ; WX 655 ; N Ucircumflex ; B 118 -13 716 927 ; -C -1 ; WX 498 ; N Scaron ; B 57 -13 593 927 ; -C -1 ; WX 226 ; N Idieresis ; B 76 0 396 928 ; -C -1 ; WX 200 ; N idieresis ; B 65 0 353 765 ; -C -1 ; WX 536 ; N Egrave ; B 70 0 612 949 ; -C -1 ; WX 869 ; N Oacute ; B 105 -13 901 949 ; -C -1 ; WX 606 ; N divide ; B 92 -13 608 519 ; -C -1 ; WX 740 ; N Atilde ; B 12 0 729 917 ; -C -1 ; WX 740 ; N Aring ; B 12 0 729 955 ; -C -1 ; WX 869 ; N Odieresis ; B 105 -13 901 928 ; -C -1 ; WX 740 ; N Adieresis ; B 12 0 729 928 ; -C -1 ; WX 740 ; N Ntilde ; B 75 0 801 917 ; -C -1 ; WX 480 ; N Zcaron ; B 12 0 596 927 ; -C -1 ; WX 592 ; N Thorn ; B 60 0 621 740 ; -C -1 ; WX 226 ; N Iacute ; B 76 0 440 949 ; -C -1 ; WX 606 ; N plusminus ; B 47 -24 618 518 ; -C -1 ; WX 606 ; N multiply ; B 87 24 612 482 ; -C -1 ; WX 536 ; N Eacute ; B 70 0 612 949 ; -C -1 ; WX 592 ; N Ydieresis ; B 138 0 729 928 ; -C -1 ; WX 332 ; N onesuperior ; B 190 296 335 740 ; -C -1 ; WX 608 ; N ugrave ; B 100 -13 642 786 ; -C -1 ; WX 606 ; N logicalnot ; B 110 109 627 388 ; -C -1 ; WX 610 ; N ntilde ; B 65 0 609 754 ; -C -1 ; WX 869 ; N Otilde ; B 105 -13 901 917 ; -C -1 ; WX 655 ; N otilde ; B 88 -13 669 754 ; -C -1 ; WX 813 ; N Ccedilla ; B 105 -222 870 752 ; -C -1 ; WX 740 ; N Agrave ; B 12 0 729 949 ; -C -1 ; WX 831 ; N onehalf ; B 164 0 810 740 ; -C -1 ; WX 790 ; N Eth ; B 104 0 813 740 ; -C -1 ; WX 400 ; N degree ; B 158 421 451 709 ; -C -1 ; WX 592 ; N Yacute ; B 138 0 729 949 ; -C -1 ; WX 869 ; N Ocircumflex ; B 105 -13 901 927 ; -C -1 ; WX 655 ; N oacute ; B 88 -13 669 786 ; -C -1 ; WX 608 ; N mu ; B 46 -184 628 547 ; -C -1 ; WX 606 ; N minus ; B 92 219 608 287 ; -C -1 ; WX 655 ; N eth ; B 88 -12 675 753 ; -C -1 ; WX 655 ; N odieresis ; B 88 -13 669 765 ; -C -1 ; WX 747 ; N copyright ; B 53 -12 830 752 ; -C -1 ; WX 672 ; N brokenbar ; B 280 -100 510 740 ; -EndCharMetrics -StartKernData -StartKernPairs 216 - -KPX A y -62 -KPX A w -65 -KPX A v -70 -KPX A u -20 -KPX A quoteright -100 -KPX A quotedblright -100 -KPX A Y -92 -KPX A W -60 -KPX A V -102 -KPX A U -40 -KPX A T -45 -KPX A Q -40 -KPX A O -50 -KPX A G -40 -KPX A C -40 - -KPX B A -10 - -KPX C A -40 - -KPX D period -20 -KPX D comma -20 -KPX D Y -30 -KPX D W -10 -KPX D V -50 -KPX D A -50 - -KPX F period -160 -KPX F e -20 -KPX F comma -180 -KPX F a -20 -KPX F A -75 - -KPX G period -20 -KPX G comma -20 -KPX G Y -20 - -KPX J period -15 -KPX J a -20 -KPX J A -30 - -KPX K o -15 -KPX K e -20 -KPX K O -20 - -KPX L y -23 -KPX L quoteright -130 -KPX L quotedblright -130 -KPX L Y -91 -KPX L W -67 -KPX L V -113 -KPX L T -46 - -KPX O period -30 -KPX O comma -30 -KPX O Y -30 -KPX O X -30 -KPX O W -20 -KPX O V -60 -KPX O T -30 -KPX O A -60 - -KPX P period -300 -KPX P o -60 -KPX P e -20 -KPX P comma -280 -KPX P a -20 -KPX P A -114 - -KPX Q comma 20 - -KPX R Y -10 -KPX R W 10 -KPX R V -10 -KPX R T 6 - -KPX S comma 20 - -KPX T y -50 -KPX T w -55 -KPX T u -46 -KPX T semicolon -29 -KPX T r -30 -KPX T period -91 -KPX T o -70 -KPX T i 10 -KPX T hyphen -75 -KPX T e -49 -KPX T comma -82 -KPX T colon -15 -KPX T a -90 -KPX T O -30 -KPX T A -45 - -KPX U period -20 -KPX U comma -20 -KPX U A -40 - -KPX V u -40 -KPX V semicolon -33 -KPX V period -165 -KPX V o -101 -KPX V i -5 -KPX V hyphen -75 -KPX V e -101 -KPX V comma -145 -KPX V colon -18 -KPX V a -104 -KPX V O -60 -KPX V G -20 -KPX V A -102 - -KPX W y -2 -KPX W u -30 -KPX W semicolon -33 -KPX W period -106 -KPX W o -46 -KPX W i 6 -KPX W hyphen -35 -KPX W e -47 -KPX W comma -106 -KPX W colon -15 -KPX W a -50 -KPX W O -20 -KPX W A -58 - -KPX Y u -52 -KPX Y semicolon -23 -KPX Y period -175 -KPX Y o -89 -KPX Y hyphen -85 -KPX Y e -89 -KPX Y comma -145 -KPX Y colon -10 -KPX Y a -93 -KPX Y O -30 -KPX Y A -92 - -KPX a p 20 -KPX a b 20 - -KPX b y -20 -KPX b v -20 - -KPX c y -20 -KPX c k -15 - -KPX comma space -110 -KPX comma quoteright -120 -KPX comma quotedblright -120 - -KPX e y -20 -KPX e w -20 -KPX e v -20 - -KPX f period -50 -KPX f o -40 -KPX f l -30 -KPX f i -34 -KPX f f -60 -KPX f e -20 -KPX f dotlessi -34 -KPX f comma -50 -KPX f a -40 - -KPX g a -15 - -KPX h y -30 - -KPX k y -5 -KPX k e -15 - -KPX m y -20 -KPX m u -20 -KPX m a -20 - -KPX n y -15 -KPX n v -20 - -KPX o y -20 -KPX o x -15 -KPX o w -20 -KPX o v -30 - -KPX p y -20 - -KPX period space -110 -KPX period quoteright -120 -KPX period quotedblright -120 - -KPX quotedblleft quoteleft -35 -KPX quotedblleft A -100 - -KPX quotedblright space -110 - -KPX quoteleft quoteleft -203 -KPX quoteleft A -100 - -KPX quoteright v -30 -KPX quoteright t 10 -KPX quoteright space -110 -KPX quoteright s -15 -KPX quoteright r -20 -KPX quoteright quoteright -203 -KPX quoteright quotedblright -35 -KPX quoteright d -110 - -KPX r y 40 -KPX r v 40 -KPX r u 20 -KPX r t 20 -KPX r s 20 -KPX r q -8 -KPX r period -73 -KPX r p 20 -KPX r o -20 -KPX r n 21 -KPX r m 28 -KPX r l 20 -KPX r k 20 -KPX r i 20 -KPX r hyphen -60 -KPX r g -15 -KPX r e -4 -KPX r d -6 -KPX r comma -75 -KPX r c -20 -KPX r a -20 - -KPX s period 20 -KPX s comma 20 - -KPX space quoteleft -110 -KPX space quotedblleft -110 -KPX space Y -60 -KPX space W -25 -KPX space V -50 -KPX space T -25 -KPX space A -20 - -KPX v period -130 -KPX v o -30 -KPX v e -20 -KPX v comma -100 -KPX v a -30 - -KPX w period -100 -KPX w o -30 -KPX w h 15 -KPX w e -20 -KPX w comma -90 -KPX w a -30 - -KPX y period -125 -KPX y o -30 -KPX y e -20 -KPX y comma -110 -KPX y a -30 -EndKernPairs -EndKernData -StartComposites 56 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 213 163 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 149 163 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 216 163 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 211 163 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 231 148 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 181 163 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 111 163 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 47 163 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 114 163 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 109 163 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute -4 163 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex -108 163 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis -41 163 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave -86 163 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 181 163 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 277 163 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 214 163 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 280 163 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 276 163 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 245 163 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 28 163 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 190 163 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 107 163 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 173 163 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 149 163 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 159 163 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 142 163 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 19 163 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 154 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 91 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 157 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 153 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 176 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 122 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 138 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 74 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 141 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 136 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -47 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -151 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -84 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -129 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 86 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 140 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 77 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 143 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 139 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 108 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron -57 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 137 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 53 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 120 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 95 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 101 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 84 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron -38 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pbkd8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pbkd8a.afm deleted file mode 100644 index 036be6d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pbkd8a.afm +++ /dev/null @@ -1,415 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1992 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Tue Jan 21 16:13:29 1992 -Comment UniqueID 37831 -Comment VMusage 31983 38875 -FontName Bookman-Demi -FullName ITC Bookman Demi -FamilyName ITC Bookman -Weight Demi -ItalicAngle 0 -IsFixedPitch false -FontBBox -194 -250 1346 934 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.004 -Notice Copyright (c) 1985, 1987, 1989, 1992 Adobe Systems Incorporated. All Rights Reserved.ITC Bookman is a registered trademark of International Typeface Corporation. -EncodingScheme AdobeStandardEncoding -CapHeight 681 -XHeight 502 -Ascender 725 -Descender -212 -StartCharMetrics 228 -C 32 ; WX 340 ; N space ; B 0 0 0 0 ; -C 33 ; WX 360 ; N exclam ; B 82 -8 282 698 ; -C 34 ; WX 420 ; N quotedbl ; B 11 379 369 698 ; -C 35 ; WX 660 ; N numbersign ; B 84 0 576 681 ; -C 36 ; WX 660 ; N dollar ; B 48 -119 620 805 ; -C 37 ; WX 940 ; N percent ; B 12 -8 924 698 ; -C 38 ; WX 800 ; N ampersand ; B 21 -17 772 698 ; -C 39 ; WX 320 ; N quoteright ; B 82 440 242 698 ; -C 40 ; WX 320 ; N parenleft ; B 48 -150 289 749 ; -C 41 ; WX 320 ; N parenright ; B 20 -150 262 749 ; -C 42 ; WX 460 ; N asterisk ; B 62 317 405 697 ; -C 43 ; WX 600 ; N plus ; B 51 9 555 514 ; -C 44 ; WX 340 ; N comma ; B 78 -124 257 162 ; -C 45 ; WX 360 ; N hyphen ; B 20 210 340 318 ; -C 46 ; WX 340 ; N period ; B 76 -8 258 172 ; -C 47 ; WX 600 ; N slash ; B 50 -149 555 725 ; -C 48 ; WX 660 ; N zero ; B 30 -17 639 698 ; -C 49 ; WX 660 ; N one ; B 137 0 568 681 ; -C 50 ; WX 660 ; N two ; B 41 0 628 698 ; -C 51 ; WX 660 ; N three ; B 37 -17 631 698 ; -C 52 ; WX 660 ; N four ; B 19 0 649 681 ; -C 53 ; WX 660 ; N five ; B 44 -17 623 723 ; -C 54 ; WX 660 ; N six ; B 34 -17 634 698 ; -C 55 ; WX 660 ; N seven ; B 36 0 632 681 ; -C 56 ; WX 660 ; N eight ; B 36 -17 633 698 ; -C 57 ; WX 660 ; N nine ; B 33 -17 636 698 ; -C 58 ; WX 340 ; N colon ; B 76 -8 258 515 ; -C 59 ; WX 340 ; N semicolon ; B 75 -124 259 515 ; -C 60 ; WX 600 ; N less ; B 49 -9 558 542 ; -C 61 ; WX 600 ; N equal ; B 51 109 555 421 ; -C 62 ; WX 600 ; N greater ; B 48 -9 557 542 ; -C 63 ; WX 660 ; N question ; B 61 -8 608 698 ; -C 64 ; WX 820 ; N at ; B 60 -17 758 698 ; -C 65 ; WX 720 ; N A ; B -34 0 763 681 ; -C 66 ; WX 720 ; N B ; B 20 0 693 681 ; -C 67 ; WX 740 ; N C ; B 35 -17 724 698 ; -C 68 ; WX 780 ; N D ; B 20 0 748 681 ; -C 69 ; WX 720 ; N E ; B 20 0 724 681 ; -C 70 ; WX 680 ; N F ; B 20 0 686 681 ; -C 71 ; WX 780 ; N G ; B 35 -17 773 698 ; -C 72 ; WX 820 ; N H ; B 20 0 800 681 ; -C 73 ; WX 400 ; N I ; B 20 0 379 681 ; -C 74 ; WX 640 ; N J ; B -12 -17 622 681 ; -C 75 ; WX 800 ; N K ; B 20 0 796 681 ; -C 76 ; WX 640 ; N L ; B 20 0 668 681 ; -C 77 ; WX 940 ; N M ; B 20 0 924 681 ; -C 78 ; WX 740 ; N N ; B 20 0 724 681 ; -C 79 ; WX 800 ; N O ; B 35 -17 769 698 ; -C 80 ; WX 660 ; N P ; B 20 0 658 681 ; -C 81 ; WX 800 ; N Q ; B 35 -226 775 698 ; -C 82 ; WX 780 ; N R ; B 20 0 783 681 ; -C 83 ; WX 660 ; N S ; B 21 -17 639 698 ; -C 84 ; WX 700 ; N T ; B -4 0 703 681 ; -C 85 ; WX 740 ; N U ; B 15 -17 724 681 ; -C 86 ; WX 720 ; N V ; B -20 0 730 681 ; -C 87 ; WX 940 ; N W ; B -20 0 963 681 ; -C 88 ; WX 780 ; N X ; B 1 0 770 681 ; -C 89 ; WX 700 ; N Y ; B -20 0 718 681 ; -C 90 ; WX 640 ; N Z ; B 6 0 635 681 ; -C 91 ; WX 300 ; N bracketleft ; B 75 -138 285 725 ; -C 92 ; WX 600 ; N backslash ; B 50 0 555 725 ; -C 93 ; WX 300 ; N bracketright ; B 21 -138 231 725 ; -C 94 ; WX 600 ; N asciicircum ; B 52 281 554 681 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 320 ; N quoteleft ; B 82 440 242 698 ; -C 97 ; WX 580 ; N a ; B 28 -8 588 515 ; -C 98 ; WX 600 ; N b ; B -20 -8 568 725 ; -C 99 ; WX 580 ; N c ; B 31 -8 550 515 ; -C 100 ; WX 640 ; N d ; B 31 -8 622 725 ; -C 101 ; WX 580 ; N e ; B 31 -8 548 515 ; -C 102 ; WX 380 ; N f ; B 22 0 461 741 ; L i fi ; L l fl ; -C 103 ; WX 580 ; N g ; B 9 -243 583 595 ; -C 104 ; WX 680 ; N h ; B 22 0 654 725 ; -C 105 ; WX 360 ; N i ; B 22 0 335 729 ; -C 106 ; WX 340 ; N j ; B -94 -221 278 729 ; -C 107 ; WX 660 ; N k ; B 22 0 643 725 ; -C 108 ; WX 340 ; N l ; B 9 0 322 725 ; -C 109 ; WX 1000 ; N m ; B 22 0 980 515 ; -C 110 ; WX 680 ; N n ; B 22 0 652 515 ; -C 111 ; WX 620 ; N o ; B 31 -8 585 515 ; -C 112 ; WX 640 ; N p ; B 22 -212 611 515 ; -C 113 ; WX 620 ; N q ; B 31 -212 633 515 ; -C 114 ; WX 460 ; N r ; B 22 0 462 502 ; -C 115 ; WX 520 ; N s ; B 22 -8 492 515 ; -C 116 ; WX 460 ; N t ; B 22 -8 445 660 ; -C 117 ; WX 660 ; N u ; B 22 -8 653 502 ; -C 118 ; WX 600 ; N v ; B -6 0 593 502 ; -C 119 ; WX 800 ; N w ; B -6 0 810 502 ; -C 120 ; WX 600 ; N x ; B 8 0 591 502 ; -C 121 ; WX 620 ; N y ; B 6 -221 613 502 ; -C 122 ; WX 560 ; N z ; B 22 0 547 502 ; -C 123 ; WX 320 ; N braceleft ; B 14 -139 301 726 ; -C 124 ; WX 600 ; N bar ; B 243 -250 362 750 ; -C 125 ; WX 320 ; N braceright ; B 15 -140 302 725 ; -C 126 ; WX 600 ; N asciitilde ; B 51 162 555 368 ; -C 161 ; WX 360 ; N exclamdown ; B 84 -191 284 515 ; -C 162 ; WX 660 ; N cent ; B 133 17 535 674 ; -C 163 ; WX 660 ; N sterling ; B 10 -17 659 698 ; -C 164 ; WX 120 ; N fraction ; B -194 0 312 681 ; -C 165 ; WX 660 ; N yen ; B -28 0 696 681 ; -C 166 ; WX 660 ; N florin ; B -46 -209 674 749 ; -C 167 ; WX 600 ; N section ; B 36 -153 560 698 ; -C 168 ; WX 660 ; N currency ; B 77 88 584 593 ; -C 169 ; WX 240 ; N quotesingle ; B 42 379 178 698 ; -C 170 ; WX 540 ; N quotedblleft ; B 82 439 449 698 ; -C 171 ; WX 400 ; N guillemotleft ; B 34 101 360 457 ; -C 172 ; WX 220 ; N guilsinglleft ; B 34 101 188 457 ; -C 173 ; WX 220 ; N guilsinglright ; B 34 101 188 457 ; -C 174 ; WX 740 ; N fi ; B 22 0 710 741 ; -C 175 ; WX 740 ; N fl ; B 22 0 710 741 ; -C 177 ; WX 500 ; N endash ; B -25 212 525 318 ; -C 178 ; WX 440 ; N dagger ; B 33 -156 398 698 ; -C 179 ; WX 380 ; N daggerdbl ; B 8 -156 380 698 ; -C 180 ; WX 340 ; N periodcentered ; B 76 175 258 355 ; -C 182 ; WX 800 ; N paragraph ; B 51 0 698 681 ; -C 183 ; WX 460 ; N bullet ; B 60 170 404 511 ; -C 184 ; WX 320 ; N quotesinglbase ; B 82 -114 242 144 ; -C 185 ; WX 540 ; N quotedblbase ; B 82 -114 450 144 ; -C 186 ; WX 540 ; N quotedblright ; B 82 440 449 698 ; -C 187 ; WX 400 ; N guillemotright ; B 34 101 360 457 ; -C 188 ; WX 1000 ; N ellipsis ; B 76 -8 924 172 ; -C 189 ; WX 1360 ; N perthousand ; B 12 -8 1346 698 ; -C 191 ; WX 660 ; N questiondown ; B 62 -191 609 515 ; -C 193 ; WX 400 ; N grave ; B 68 547 327 730 ; -C 194 ; WX 400 ; N acute ; B 68 547 327 731 ; -C 195 ; WX 500 ; N circumflex ; B 68 555 430 731 ; -C 196 ; WX 480 ; N tilde ; B 69 556 421 691 ; -C 197 ; WX 460 ; N macron ; B 68 577 383 663 ; -C 198 ; WX 500 ; N breve ; B 68 553 429 722 ; -C 199 ; WX 320 ; N dotaccent ; B 68 536 259 730 ; -C 200 ; WX 500 ; N dieresis ; B 68 560 441 698 ; -C 202 ; WX 340 ; N ring ; B 68 552 275 755 ; -C 203 ; WX 360 ; N cedilla ; B 68 -213 284 0 ; -C 205 ; WX 440 ; N hungarumlaut ; B 68 554 365 741 ; -C 206 ; WX 320 ; N ogonek ; B 68 -163 246 0 ; -C 207 ; WX 500 ; N caron ; B 68 541 430 717 ; -C 208 ; WX 1000 ; N emdash ; B -25 212 1025 318 ; -C 225 ; WX 1140 ; N AE ; B -34 0 1149 681 ; -C 227 ; WX 400 ; N ordfeminine ; B 27 383 396 698 ; -C 232 ; WX 640 ; N Lslash ; B 20 0 668 681 ; -C 233 ; WX 800 ; N Oslash ; B 35 -110 771 781 ; -C 234 ; WX 1220 ; N OE ; B 35 -17 1219 698 ; -C 235 ; WX 400 ; N ordmasculine ; B 17 383 383 698 ; -C 241 ; WX 880 ; N ae ; B 28 -8 852 515 ; -C 245 ; WX 360 ; N dotlessi ; B 22 0 335 502 ; -C 248 ; WX 340 ; N lslash ; B 9 0 322 725 ; -C 249 ; WX 620 ; N oslash ; B 31 -40 586 551 ; -C 250 ; WX 940 ; N oe ; B 31 -8 908 515 ; -C 251 ; WX 660 ; N germandbls ; B -61 -91 644 699 ; -C -1 ; WX 580 ; N ecircumflex ; B 31 -8 548 731 ; -C -1 ; WX 580 ; N edieresis ; B 31 -8 548 698 ; -C -1 ; WX 580 ; N aacute ; B 28 -8 588 731 ; -C -1 ; WX 740 ; N registered ; B 23 -17 723 698 ; -C -1 ; WX 360 ; N icircumflex ; B -2 0 360 731 ; -C -1 ; WX 660 ; N udieresis ; B 22 -8 653 698 ; -C -1 ; WX 620 ; N ograve ; B 31 -8 585 730 ; -C -1 ; WX 660 ; N uacute ; B 22 -8 653 731 ; -C -1 ; WX 660 ; N ucircumflex ; B 22 -8 653 731 ; -C -1 ; WX 720 ; N Aacute ; B -34 0 763 910 ; -C -1 ; WX 360 ; N igrave ; B 22 0 335 730 ; -C -1 ; WX 400 ; N Icircumflex ; B 18 0 380 910 ; -C -1 ; WX 580 ; N ccedilla ; B 31 -213 550 515 ; -C -1 ; WX 580 ; N adieresis ; B 28 -8 588 698 ; -C -1 ; WX 720 ; N Ecircumflex ; B 20 0 724 910 ; -C -1 ; WX 520 ; N scaron ; B 22 -8 492 717 ; -C -1 ; WX 640 ; N thorn ; B 22 -212 611 725 ; -C -1 ; WX 980 ; N trademark ; B 42 277 982 681 ; -C -1 ; WX 580 ; N egrave ; B 31 -8 548 730 ; -C -1 ; WX 396 ; N threesuperior ; B 5 269 391 698 ; -C -1 ; WX 560 ; N zcaron ; B 22 0 547 717 ; -C -1 ; WX 580 ; N atilde ; B 28 -8 588 691 ; -C -1 ; WX 580 ; N aring ; B 28 -8 588 755 ; -C -1 ; WX 620 ; N ocircumflex ; B 31 -8 585 731 ; -C -1 ; WX 720 ; N Edieresis ; B 20 0 724 877 ; -C -1 ; WX 990 ; N threequarters ; B 15 0 967 692 ; -C -1 ; WX 620 ; N ydieresis ; B 6 -221 613 698 ; -C -1 ; WX 620 ; N yacute ; B 6 -221 613 731 ; -C -1 ; WX 360 ; N iacute ; B 22 0 335 731 ; -C -1 ; WX 720 ; N Acircumflex ; B -34 0 763 910 ; -C -1 ; WX 740 ; N Uacute ; B 15 -17 724 910 ; -C -1 ; WX 580 ; N eacute ; B 31 -8 548 731 ; -C -1 ; WX 800 ; N Ograve ; B 35 -17 769 909 ; -C -1 ; WX 580 ; N agrave ; B 28 -8 588 730 ; -C -1 ; WX 740 ; N Udieresis ; B 15 -17 724 877 ; -C -1 ; WX 580 ; N acircumflex ; B 28 -8 588 731 ; -C -1 ; WX 400 ; N Igrave ; B 20 0 379 909 ; -C -1 ; WX 396 ; N twosuperior ; B 14 279 396 698 ; -C -1 ; WX 740 ; N Ugrave ; B 15 -17 724 909 ; -C -1 ; WX 990 ; N onequarter ; B 65 0 967 681 ; -C -1 ; WX 740 ; N Ucircumflex ; B 15 -17 724 910 ; -C -1 ; WX 660 ; N Scaron ; B 21 -17 639 896 ; -C -1 ; WX 400 ; N Idieresis ; B 18 0 391 877 ; -C -1 ; WX 360 ; N idieresis ; B -2 0 371 698 ; -C -1 ; WX 720 ; N Egrave ; B 20 0 724 909 ; -C -1 ; WX 800 ; N Oacute ; B 35 -17 769 910 ; -C -1 ; WX 600 ; N divide ; B 51 9 555 521 ; -C -1 ; WX 720 ; N Atilde ; B -34 0 763 870 ; -C -1 ; WX 720 ; N Aring ; B -34 0 763 934 ; -C -1 ; WX 800 ; N Odieresis ; B 35 -17 769 877 ; -C -1 ; WX 720 ; N Adieresis ; B -34 0 763 877 ; -C -1 ; WX 740 ; N Ntilde ; B 20 0 724 870 ; -C -1 ; WX 640 ; N Zcaron ; B 6 0 635 896 ; -C -1 ; WX 660 ; N Thorn ; B 20 0 658 681 ; -C -1 ; WX 400 ; N Iacute ; B 20 0 379 910 ; -C -1 ; WX 600 ; N plusminus ; B 51 0 555 514 ; -C -1 ; WX 600 ; N multiply ; B 48 10 552 514 ; -C -1 ; WX 720 ; N Eacute ; B 20 0 724 910 ; -C -1 ; WX 700 ; N Ydieresis ; B -20 0 718 877 ; -C -1 ; WX 396 ; N onesuperior ; B 65 279 345 687 ; -C -1 ; WX 660 ; N ugrave ; B 22 -8 653 730 ; -C -1 ; WX 600 ; N logicalnot ; B 51 129 555 421 ; -C -1 ; WX 680 ; N ntilde ; B 22 0 652 691 ; -C -1 ; WX 800 ; N Otilde ; B 35 -17 769 870 ; -C -1 ; WX 620 ; N otilde ; B 31 -8 585 691 ; -C -1 ; WX 740 ; N Ccedilla ; B 35 -213 724 698 ; -C -1 ; WX 720 ; N Agrave ; B -34 0 763 909 ; -C -1 ; WX 990 ; N onehalf ; B 65 0 980 681 ; -C -1 ; WX 780 ; N Eth ; B 20 0 748 681 ; -C -1 ; WX 400 ; N degree ; B 50 398 350 698 ; -C -1 ; WX 700 ; N Yacute ; B -20 0 718 910 ; -C -1 ; WX 800 ; N Ocircumflex ; B 35 -17 769 910 ; -C -1 ; WX 620 ; N oacute ; B 31 -8 585 731 ; -C -1 ; WX 660 ; N mu ; B 22 -221 653 502 ; -C -1 ; WX 600 ; N minus ; B 51 207 555 323 ; -C -1 ; WX 620 ; N eth ; B 31 -8 585 741 ; -C -1 ; WX 620 ; N odieresis ; B 31 -8 585 698 ; -C -1 ; WX 740 ; N copyright ; B 23 -17 723 698 ; -C -1 ; WX 600 ; N brokenbar ; B 243 -175 362 675 ; -EndCharMetrics -StartKernData -StartKernPairs 90 - -KPX A y -1 -KPX A w -9 -KPX A v -8 -KPX A Y -52 -KPX A W -20 -KPX A V -68 -KPX A T -40 - -KPX F period -132 -KPX F comma -130 -KPX F A -59 - -KPX L y 19 -KPX L Y -35 -KPX L W -41 -KPX L V -50 -KPX L T -4 - -KPX P period -128 -KPX P comma -129 -KPX P A -46 - -KPX R y -8 -KPX R Y -20 -KPX R W -24 -KPX R V -29 -KPX R T -4 - -KPX T semicolon 5 -KPX T s -10 -KPX T r 27 -KPX T period -122 -KPX T o -28 -KPX T i 27 -KPX T hyphen -10 -KPX T e -29 -KPX T comma -122 -KPX T colon 7 -KPX T c -29 -KPX T a -24 -KPX T A -42 - -KPX V y 12 -KPX V u -11 -KPX V semicolon -38 -KPX V r -15 -KPX V period -105 -KPX V o -79 -KPX V i 15 -KPX V hyphen -10 -KPX V e -80 -KPX V comma -103 -KPX V colon -37 -KPX V a -74 -KPX V A -88 - -KPX W y 12 -KPX W u -11 -KPX W semicolon -38 -KPX W r -15 -KPX W period -105 -KPX W o -78 -KPX W i 15 -KPX W hyphen -10 -KPX W e -79 -KPX W comma -103 -KPX W colon -37 -KPX W a -73 -KPX W A -60 - -KPX Y v 24 -KPX Y u -13 -KPX Y semicolon -34 -KPX Y q -66 -KPX Y period -105 -KPX Y p -23 -KPX Y o -66 -KPX Y i 2 -KPX Y hyphen -10 -KPX Y e -67 -KPX Y comma -103 -KPX Y colon -32 -KPX Y a -60 -KPX Y A -56 - -KPX f f 21 - -KPX r q -9 -KPX r period -102 -KPX r o -9 -KPX r n 20 -KPX r m 20 -KPX r hyphen -10 -KPX r h -23 -KPX r g -9 -KPX r f 20 -KPX r e -10 -KPX r d -10 -KPX r comma -101 -KPX r c -9 -EndKernPairs -EndKernData -StartComposites 56 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 160 179 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 110 179 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 110 179 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 160 179 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 190 179 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 120 179 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 160 179 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 110 179 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 110 179 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 160 179 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 0 179 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex -50 179 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis -50 179 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 0 179 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 130 179 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 200 179 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 150 179 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 150 179 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 200 179 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 160 179 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 80 179 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 170 179 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 120 179 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 120 179 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 170 179 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 150 179 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 100 179 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 70 179 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 90 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 40 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 40 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 90 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 100 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 30 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 90 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 40 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 40 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 90 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -20 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -70 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -70 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -20 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 80 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 110 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 60 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 60 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 110 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 50 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 10 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 130 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 80 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 80 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 130 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 110 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 60 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 30 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pbkdi8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pbkdi8a.afm deleted file mode 100644 index c2da47a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pbkdi8a.afm +++ /dev/null @@ -1,417 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1992 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Tue Jan 21 16:12:43 1992 -Comment UniqueID 37832 -Comment VMusage 32139 39031 -FontName Bookman-DemiItalic -FullName ITC Bookman Demi Italic -FamilyName ITC Bookman -Weight Demi -ItalicAngle -10 -IsFixedPitch false -FontBBox -231 -250 1333 941 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.004 -Notice Copyright (c) 1985, 1987, 1989, 1992 Adobe Systems Incorporated. All Rights Reserved.ITC Bookman is a registered trademark of International Typeface Corporation. -EncodingScheme AdobeStandardEncoding -CapHeight 681 -XHeight 515 -Ascender 732 -Descender -213 -StartCharMetrics 228 -C 32 ; WX 340 ; N space ; B 0 0 0 0 ; -C 33 ; WX 320 ; N exclam ; B 86 -8 366 698 ; -C 34 ; WX 380 ; N quotedbl ; B 140 371 507 697 ; -C 35 ; WX 680 ; N numbersign ; B 157 0 649 681 ; -C 36 ; WX 680 ; N dollar ; B 45 -164 697 790 ; -C 37 ; WX 880 ; N percent ; B 106 -17 899 698 ; -C 38 ; WX 980 ; N ampersand ; B 48 -17 1016 698 ; -C 39 ; WX 320 ; N quoteright ; B 171 420 349 698 ; -C 40 ; WX 260 ; N parenleft ; B 31 -134 388 741 ; -C 41 ; WX 260 ; N parenright ; B -35 -134 322 741 ; -C 42 ; WX 460 ; N asterisk ; B 126 346 508 698 ; -C 43 ; WX 600 ; N plus ; B 91 9 595 514 ; -C 44 ; WX 340 ; N comma ; B 100 -124 298 185 ; -C 45 ; WX 280 ; N hyphen ; B 59 218 319 313 ; -C 46 ; WX 340 ; N period ; B 106 -8 296 177 ; -C 47 ; WX 360 ; N slash ; B 9 -106 502 742 ; -C 48 ; WX 680 ; N zero ; B 87 -17 703 698 ; -C 49 ; WX 680 ; N one ; B 123 0 565 681 ; -C 50 ; WX 680 ; N two ; B 67 0 674 698 ; -C 51 ; WX 680 ; N three ; B 72 -17 683 698 ; -C 52 ; WX 680 ; N four ; B 63 0 708 681 ; -C 53 ; WX 680 ; N five ; B 78 -17 669 681 ; -C 54 ; WX 680 ; N six ; B 88 -17 704 698 ; -C 55 ; WX 680 ; N seven ; B 123 0 739 681 ; -C 56 ; WX 680 ; N eight ; B 68 -17 686 698 ; -C 57 ; WX 680 ; N nine ; B 71 -17 712 698 ; -C 58 ; WX 340 ; N colon ; B 106 -8 356 515 ; -C 59 ; WX 340 ; N semicolon ; B 100 -124 352 515 ; -C 60 ; WX 620 ; N less ; B 79 -9 588 540 ; -C 61 ; WX 600 ; N equal ; B 91 109 595 421 ; -C 62 ; WX 620 ; N greater ; B 89 -9 598 540 ; -C 63 ; WX 620 ; N question ; B 145 -8 668 698 ; -C 64 ; WX 780 ; N at ; B 80 -17 790 698 ; -C 65 ; WX 720 ; N A ; B -27 0 769 681 ; -C 66 ; WX 720 ; N B ; B 14 0 762 681 ; -C 67 ; WX 700 ; N C ; B 78 -17 754 698 ; -C 68 ; WX 760 ; N D ; B 14 0 805 681 ; -C 69 ; WX 720 ; N E ; B 14 0 777 681 ; -C 70 ; WX 660 ; N F ; B 14 0 763 681 ; -C 71 ; WX 760 ; N G ; B 77 -17 828 698 ; -C 72 ; WX 800 ; N H ; B 14 0 910 681 ; -C 73 ; WX 380 ; N I ; B 14 0 485 681 ; -C 74 ; WX 620 ; N J ; B 8 -17 721 681 ; -C 75 ; WX 780 ; N K ; B 14 0 879 681 ; -C 76 ; WX 640 ; N L ; B 14 0 725 681 ; -C 77 ; WX 860 ; N M ; B 14 0 970 681 ; -C 78 ; WX 740 ; N N ; B 14 0 845 681 ; -C 79 ; WX 760 ; N O ; B 78 -17 806 698 ; -C 80 ; WX 640 ; N P ; B -6 0 724 681 ; -C 81 ; WX 760 ; N Q ; B 37 -213 805 698 ; -C 82 ; WX 740 ; N R ; B 14 0 765 681 ; -C 83 ; WX 700 ; N S ; B 59 -17 731 698 ; -C 84 ; WX 700 ; N T ; B 70 0 802 681 ; -C 85 ; WX 740 ; N U ; B 112 -17 855 681 ; -C 86 ; WX 660 ; N V ; B 72 0 819 681 ; -C 87 ; WX 1000 ; N W ; B 72 0 1090 681 ; -C 88 ; WX 740 ; N X ; B -7 0 835 681 ; -C 89 ; WX 660 ; N Y ; B 72 0 817 681 ; -C 90 ; WX 680 ; N Z ; B 23 0 740 681 ; -C 91 ; WX 260 ; N bracketleft ; B 9 -118 374 741 ; -C 92 ; WX 580 ; N backslash ; B 73 0 575 741 ; -C 93 ; WX 260 ; N bracketright ; B -18 -118 347 741 ; -C 94 ; WX 620 ; N asciicircum ; B 92 281 594 681 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 320 ; N quoteleft ; B 155 420 333 698 ; -C 97 ; WX 680 ; N a ; B 84 -8 735 515 ; -C 98 ; WX 600 ; N b ; B 57 -8 633 732 ; -C 99 ; WX 560 ; N c ; B 58 -8 597 515 ; -C 100 ; WX 680 ; N d ; B 60 -8 714 732 ; -C 101 ; WX 560 ; N e ; B 59 -8 596 515 ; -C 102 ; WX 420 ; N f ; B -192 -213 641 741 ; L i fi ; L l fl ; -C 103 ; WX 620 ; N g ; B 21 -213 669 515 ; -C 104 ; WX 700 ; N h ; B 93 -8 736 732 ; -C 105 ; WX 380 ; N i ; B 83 -8 420 755 ; -C 106 ; WX 320 ; N j ; B -160 -213 392 755 ; -C 107 ; WX 700 ; N k ; B 97 -8 732 732 ; -C 108 ; WX 380 ; N l ; B 109 -8 410 732 ; -C 109 ; WX 960 ; N m ; B 83 -8 996 515 ; -C 110 ; WX 680 ; N n ; B 83 -8 715 515 ; -C 111 ; WX 600 ; N o ; B 59 -8 627 515 ; -C 112 ; WX 660 ; N p ; B -24 -213 682 515 ; -C 113 ; WX 620 ; N q ; B 60 -213 640 515 ; -C 114 ; WX 500 ; N r ; B 84 0 582 515 ; -C 115 ; WX 540 ; N s ; B 32 -8 573 515 ; -C 116 ; WX 440 ; N t ; B 106 -8 488 658 ; -C 117 ; WX 680 ; N u ; B 83 -8 720 507 ; -C 118 ; WX 540 ; N v ; B 56 -8 572 515 ; -C 119 ; WX 860 ; N w ; B 56 -8 891 515 ; -C 120 ; WX 620 ; N x ; B 10 -8 654 515 ; -C 121 ; WX 600 ; N y ; B 25 -213 642 507 ; -C 122 ; WX 560 ; N z ; B 36 -8 586 515 ; -C 123 ; WX 300 ; N braceleft ; B 49 -123 413 742 ; -C 124 ; WX 620 ; N bar ; B 303 -250 422 750 ; -C 125 ; WX 300 ; N braceright ; B -8 -114 356 751 ; -C 126 ; WX 620 ; N asciitilde ; B 101 162 605 368 ; -C 161 ; WX 320 ; N exclamdown ; B 64 -191 344 515 ; -C 162 ; WX 680 ; N cent ; B 161 25 616 718 ; -C 163 ; WX 680 ; N sterling ; B 0 -17 787 698 ; -C 164 ; WX 120 ; N fraction ; B -144 0 382 681 ; -C 165 ; WX 680 ; N yen ; B 92 0 782 681 ; -C 166 ; WX 680 ; N florin ; B -28 -199 743 741 ; -C 167 ; WX 620 ; N section ; B 46 -137 638 698 ; -C 168 ; WX 680 ; N currency ; B 148 85 637 571 ; -C 169 ; WX 180 ; N quotesingle ; B 126 370 295 696 ; -C 170 ; WX 520 ; N quotedblleft ; B 156 420 545 698 ; -C 171 ; WX 380 ; N guillemotleft ; B 62 84 406 503 ; -C 172 ; WX 220 ; N guilsinglleft ; B 62 84 249 503 ; -C 173 ; WX 220 ; N guilsinglright ; B 62 84 249 503 ; -C 174 ; WX 820 ; N fi ; B -191 -213 850 741 ; -C 175 ; WX 820 ; N fl ; B -191 -213 850 741 ; -C 177 ; WX 500 ; N endash ; B 40 219 573 311 ; -C 178 ; WX 420 ; N dagger ; B 89 -137 466 698 ; -C 179 ; WX 420 ; N daggerdbl ; B 79 -137 486 698 ; -C 180 ; WX 340 ; N periodcentered ; B 126 173 316 358 ; -C 182 ; WX 680 ; N paragraph ; B 137 0 715 681 ; -C 183 ; WX 360 ; N bullet ; B 60 170 404 511 ; -C 184 ; WX 300 ; N quotesinglbase ; B 106 -112 284 166 ; -C 185 ; WX 520 ; N quotedblbase ; B 106 -112 495 166 ; -C 186 ; WX 520 ; N quotedblright ; B 171 420 560 698 ; -C 187 ; WX 380 ; N guillemotright ; B 62 84 406 503 ; -C 188 ; WX 1000 ; N ellipsis ; B 86 -8 942 177 ; -C 189 ; WX 1360 ; N perthousand ; B 106 -17 1333 698 ; -C 191 ; WX 620 ; N questiondown ; B 83 -189 606 515 ; -C 193 ; WX 380 ; N grave ; B 193 566 424 771 ; -C 194 ; WX 340 ; N acute ; B 176 566 407 771 ; -C 195 ; WX 480 ; N circumflex ; B 183 582 523 749 ; -C 196 ; WX 480 ; N tilde ; B 178 587 533 709 ; -C 197 ; WX 480 ; N macron ; B 177 603 531 691 ; -C 198 ; WX 460 ; N breve ; B 177 577 516 707 ; -C 199 ; WX 380 ; N dotaccent ; B 180 570 345 734 ; -C 200 ; WX 520 ; N dieresis ; B 180 570 569 734 ; -C 202 ; WX 360 ; N ring ; B 185 558 406 775 ; -C 203 ; WX 360 ; N cedilla ; B 68 -220 289 -8 ; -C 205 ; WX 560 ; N hungarumlaut ; B 181 560 616 775 ; -C 206 ; WX 320 ; N ogonek ; B 68 -182 253 0 ; -C 207 ; WX 480 ; N caron ; B 183 582 523 749 ; -C 208 ; WX 1000 ; N emdash ; B 40 219 1073 311 ; -C 225 ; WX 1140 ; N AE ; B -27 0 1207 681 ; -C 227 ; WX 440 ; N ordfeminine ; B 118 400 495 685 ; -C 232 ; WX 640 ; N Lslash ; B 14 0 724 681 ; -C 233 ; WX 760 ; N Oslash ; B 21 -29 847 725 ; -C 234 ; WX 1180 ; N OE ; B 94 -17 1245 698 ; -C 235 ; WX 440 ; N ordmasculine ; B 127 400 455 685 ; -C 241 ; WX 880 ; N ae ; B 39 -8 913 515 ; -C 245 ; WX 380 ; N dotlessi ; B 83 -8 420 507 ; -C 248 ; WX 380 ; N lslash ; B 63 -8 412 732 ; -C 249 ; WX 600 ; N oslash ; B 17 -54 661 571 ; -C 250 ; WX 920 ; N oe ; B 48 -8 961 515 ; -C 251 ; WX 660 ; N germandbls ; B -231 -213 702 741 ; -C -1 ; WX 560 ; N ecircumflex ; B 59 -8 596 749 ; -C -1 ; WX 560 ; N edieresis ; B 59 -8 596 734 ; -C -1 ; WX 680 ; N aacute ; B 84 -8 735 771 ; -C -1 ; WX 780 ; N registered ; B 83 -17 783 698 ; -C -1 ; WX 380 ; N icircumflex ; B 83 -8 433 749 ; -C -1 ; WX 680 ; N udieresis ; B 83 -8 720 734 ; -C -1 ; WX 600 ; N ograve ; B 59 -8 627 771 ; -C -1 ; WX 680 ; N uacute ; B 83 -8 720 771 ; -C -1 ; WX 680 ; N ucircumflex ; B 83 -8 720 749 ; -C -1 ; WX 720 ; N Aacute ; B -27 0 769 937 ; -C -1 ; WX 380 ; N igrave ; B 83 -8 424 771 ; -C -1 ; WX 380 ; N Icircumflex ; B 14 0 493 915 ; -C -1 ; WX 560 ; N ccedilla ; B 58 -220 597 515 ; -C -1 ; WX 680 ; N adieresis ; B 84 -8 735 734 ; -C -1 ; WX 720 ; N Ecircumflex ; B 14 0 777 915 ; -C -1 ; WX 540 ; N scaron ; B 32 -8 573 749 ; -C -1 ; WX 660 ; N thorn ; B -24 -213 682 732 ; -C -1 ; WX 940 ; N trademark ; B 42 277 982 681 ; -C -1 ; WX 560 ; N egrave ; B 59 -8 596 771 ; -C -1 ; WX 408 ; N threesuperior ; B 86 269 483 698 ; -C -1 ; WX 560 ; N zcaron ; B 36 -8 586 749 ; -C -1 ; WX 680 ; N atilde ; B 84 -8 735 709 ; -C -1 ; WX 680 ; N aring ; B 84 -8 735 775 ; -C -1 ; WX 600 ; N ocircumflex ; B 59 -8 627 749 ; -C -1 ; WX 720 ; N Edieresis ; B 14 0 777 900 ; -C -1 ; WX 1020 ; N threequarters ; B 86 0 1054 691 ; -C -1 ; WX 600 ; N ydieresis ; B 25 -213 642 734 ; -C -1 ; WX 600 ; N yacute ; B 25 -213 642 771 ; -C -1 ; WX 380 ; N iacute ; B 83 -8 420 771 ; -C -1 ; WX 720 ; N Acircumflex ; B -27 0 769 915 ; -C -1 ; WX 740 ; N Uacute ; B 112 -17 855 937 ; -C -1 ; WX 560 ; N eacute ; B 59 -8 596 771 ; -C -1 ; WX 760 ; N Ograve ; B 78 -17 806 937 ; -C -1 ; WX 680 ; N agrave ; B 84 -8 735 771 ; -C -1 ; WX 740 ; N Udieresis ; B 112 -17 855 900 ; -C -1 ; WX 680 ; N acircumflex ; B 84 -8 735 749 ; -C -1 ; WX 380 ; N Igrave ; B 14 0 485 937 ; -C -1 ; WX 408 ; N twosuperior ; B 91 279 485 698 ; -C -1 ; WX 740 ; N Ugrave ; B 112 -17 855 937 ; -C -1 ; WX 1020 ; N onequarter ; B 118 0 1054 681 ; -C -1 ; WX 740 ; N Ucircumflex ; B 112 -17 855 915 ; -C -1 ; WX 700 ; N Scaron ; B 59 -17 731 915 ; -C -1 ; WX 380 ; N Idieresis ; B 14 0 499 900 ; -C -1 ; WX 380 ; N idieresis ; B 83 -8 479 734 ; -C -1 ; WX 720 ; N Egrave ; B 14 0 777 937 ; -C -1 ; WX 760 ; N Oacute ; B 78 -17 806 937 ; -C -1 ; WX 600 ; N divide ; B 91 9 595 521 ; -C -1 ; WX 720 ; N Atilde ; B -27 0 769 875 ; -C -1 ; WX 720 ; N Aring ; B -27 0 769 941 ; -C -1 ; WX 760 ; N Odieresis ; B 78 -17 806 900 ; -C -1 ; WX 720 ; N Adieresis ; B -27 0 769 900 ; -C -1 ; WX 740 ; N Ntilde ; B 14 0 845 875 ; -C -1 ; WX 680 ; N Zcaron ; B 23 0 740 915 ; -C -1 ; WX 640 ; N Thorn ; B -6 0 701 681 ; -C -1 ; WX 380 ; N Iacute ; B 14 0 485 937 ; -C -1 ; WX 600 ; N plusminus ; B 91 0 595 514 ; -C -1 ; WX 600 ; N multiply ; B 91 10 595 514 ; -C -1 ; WX 720 ; N Eacute ; B 14 0 777 937 ; -C -1 ; WX 660 ; N Ydieresis ; B 72 0 817 900 ; -C -1 ; WX 408 ; N onesuperior ; B 118 279 406 688 ; -C -1 ; WX 680 ; N ugrave ; B 83 -8 720 771 ; -C -1 ; WX 620 ; N logicalnot ; B 81 129 585 421 ; -C -1 ; WX 680 ; N ntilde ; B 83 -8 715 709 ; -C -1 ; WX 760 ; N Otilde ; B 78 -17 806 875 ; -C -1 ; WX 600 ; N otilde ; B 59 -8 627 709 ; -C -1 ; WX 700 ; N Ccedilla ; B 78 -220 754 698 ; -C -1 ; WX 720 ; N Agrave ; B -27 0 769 937 ; -C -1 ; WX 1020 ; N onehalf ; B 118 0 1036 681 ; -C -1 ; WX 760 ; N Eth ; B 14 0 805 681 ; -C -1 ; WX 400 ; N degree ; B 130 398 430 698 ; -C -1 ; WX 660 ; N Yacute ; B 72 0 817 937 ; -C -1 ; WX 760 ; N Ocircumflex ; B 78 -17 806 915 ; -C -1 ; WX 600 ; N oacute ; B 59 -8 627 771 ; -C -1 ; WX 680 ; N mu ; B 54 -213 720 507 ; -C -1 ; WX 600 ; N minus ; B 91 207 595 323 ; -C -1 ; WX 600 ; N eth ; B 59 -8 662 741 ; -C -1 ; WX 600 ; N odieresis ; B 59 -8 627 734 ; -C -1 ; WX 780 ; N copyright ; B 83 -17 783 698 ; -C -1 ; WX 620 ; N brokenbar ; B 303 -175 422 675 ; -EndCharMetrics -StartKernData -StartKernPairs 92 - -KPX A y 20 -KPX A w 20 -KPX A v 20 -KPX A Y -25 -KPX A W -35 -KPX A V -40 -KPX A T -17 - -KPX F period -105 -KPX F comma -98 -KPX F A -35 - -KPX L y 62 -KPX L Y -5 -KPX L W -15 -KPX L V -19 -KPX L T -26 - -KPX P period -105 -KPX P comma -98 -KPX P A -31 - -KPX R y 27 -KPX R Y 4 -KPX R W -4 -KPX R V -8 -KPX R T -3 - -KPX T y 56 -KPX T w 69 -KPX T u 42 -KPX T semicolon 31 -KPX T s -1 -KPX T r 41 -KPX T period -107 -KPX T o -5 -KPX T i 42 -KPX T hyphen -20 -KPX T e -10 -KPX T comma -100 -KPX T colon 26 -KPX T c -8 -KPX T a -8 -KPX T A -42 - -KPX V y 17 -KPX V u -1 -KPX V semicolon -22 -KPX V r 2 -KPX V period -115 -KPX V o -50 -KPX V i 32 -KPX V hyphen -20 -KPX V e -50 -KPX V comma -137 -KPX V colon -28 -KPX V a -50 -KPX V A -50 - -KPX W y -51 -KPX W u -69 -KPX W semicolon -81 -KPX W r -66 -KPX W period -183 -KPX W o -100 -KPX W i -36 -KPX W hyphen -22 -KPX W e -100 -KPX W comma -201 -KPX W colon -86 -KPX W a -100 -KPX W A -77 - -KPX Y v 26 -KPX Y u -1 -KPX Y semicolon -4 -KPX Y q -43 -KPX Y period -113 -KPX Y o -41 -KPX Y i 20 -KPX Y hyphen -20 -KPX Y e -46 -KPX Y comma -106 -KPX Y colon -9 -KPX Y a -45 -KPX Y A -30 - -KPX f f 10 - -KPX r q -3 -KPX r period -120 -KPX r o -1 -KPX r n 39 -KPX r m 39 -KPX r hyphen -20 -KPX r h -35 -KPX r g -23 -KPX r f 42 -KPX r e -6 -KPX r d -3 -KPX r comma -113 -KPX r c -5 -EndKernPairs -EndKernData -StartComposites 56 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 190 166 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 120 166 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 100 166 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 170 166 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 200 166 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 120 166 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 190 166 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 120 166 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 100 166 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 170 166 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 20 166 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex -30 166 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis -70 166 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 0 166 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 130 166 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 210 166 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 140 166 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 140 166 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 190 166 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 140 166 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 110 166 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 200 166 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 130 166 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 130 166 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 180 166 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 160 166 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 70 166 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 100 166 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 170 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 100 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 80 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 150 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 160 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 100 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 110 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 60 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 20 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 90 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute 0 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -90 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -90 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave 0 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 60 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 130 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 60 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 40 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 110 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 60 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 30 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 170 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 100 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 80 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 150 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 130 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 40 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 40 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pbkl8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pbkl8a.afm deleted file mode 100644 index 8b79ea7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pbkl8a.afm +++ /dev/null @@ -1,407 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1992 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Tue Jan 21 16:15:53 1992 -Comment UniqueID 37833 -Comment VMusage 32321 39213 -FontName Bookman-Light -FullName ITC Bookman Light -FamilyName ITC Bookman -Weight Light -ItalicAngle 0 -IsFixedPitch false -FontBBox -188 -251 1266 908 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.004 -Notice Copyright (c) 1985, 1987, 1989, 1992 Adobe Systems Incorporated. All Rights Reserved.ITC Bookman is a registered trademark of International Typeface Corporation. -EncodingScheme AdobeStandardEncoding -CapHeight 681 -XHeight 484 -Ascender 717 -Descender -228 -StartCharMetrics 228 -C 32 ; WX 320 ; N space ; B 0 0 0 0 ; -C 33 ; WX 300 ; N exclam ; B 75 -8 219 698 ; -C 34 ; WX 380 ; N quotedbl ; B 56 458 323 698 ; -C 35 ; WX 620 ; N numbersign ; B 65 0 556 681 ; -C 36 ; WX 620 ; N dollar ; B 34 -109 593 791 ; -C 37 ; WX 900 ; N percent ; B 22 -8 873 698 ; -C 38 ; WX 800 ; N ampersand ; B 45 -17 787 698 ; -C 39 ; WX 220 ; N quoteright ; B 46 480 178 698 ; -C 40 ; WX 300 ; N parenleft ; B 76 -145 278 727 ; -C 41 ; WX 300 ; N parenright ; B 17 -146 219 727 ; -C 42 ; WX 440 ; N asterisk ; B 54 325 391 698 ; -C 43 ; WX 600 ; N plus ; B 51 8 555 513 ; -C 44 ; WX 320 ; N comma ; B 90 -114 223 114 ; -C 45 ; WX 400 ; N hyphen ; B 50 232 350 292 ; -C 46 ; WX 320 ; N period ; B 92 -8 220 123 ; -C 47 ; WX 600 ; N slash ; B 74 -149 532 717 ; -C 48 ; WX 620 ; N zero ; B 40 -17 586 698 ; -C 49 ; WX 620 ; N one ; B 160 0 501 681 ; -C 50 ; WX 620 ; N two ; B 42 0 576 698 ; -C 51 ; WX 620 ; N three ; B 40 -17 576 698 ; -C 52 ; WX 620 ; N four ; B 25 0 600 681 ; -C 53 ; WX 620 ; N five ; B 60 -17 584 717 ; -C 54 ; WX 620 ; N six ; B 45 -17 590 698 ; -C 55 ; WX 620 ; N seven ; B 60 0 586 681 ; -C 56 ; WX 620 ; N eight ; B 44 -17 583 698 ; -C 57 ; WX 620 ; N nine ; B 37 -17 576 698 ; -C 58 ; WX 320 ; N colon ; B 92 -8 220 494 ; -C 59 ; WX 320 ; N semicolon ; B 90 -114 223 494 ; -C 60 ; WX 600 ; N less ; B 49 -2 558 526 ; -C 61 ; WX 600 ; N equal ; B 51 126 555 398 ; -C 62 ; WX 600 ; N greater ; B 48 -2 557 526 ; -C 63 ; WX 540 ; N question ; B 27 -8 514 698 ; -C 64 ; WX 820 ; N at ; B 55 -17 755 698 ; -C 65 ; WX 680 ; N A ; B -37 0 714 681 ; -C 66 ; WX 740 ; N B ; B 31 0 702 681 ; -C 67 ; WX 740 ; N C ; B 44 -17 702 698 ; -C 68 ; WX 800 ; N D ; B 31 0 752 681 ; -C 69 ; WX 720 ; N E ; B 31 0 705 681 ; -C 70 ; WX 640 ; N F ; B 31 0 654 681 ; -C 71 ; WX 800 ; N G ; B 44 -17 778 698 ; -C 72 ; WX 800 ; N H ; B 31 0 769 681 ; -C 73 ; WX 340 ; N I ; B 31 0 301 681 ; -C 74 ; WX 600 ; N J ; B -23 -17 567 681 ; -C 75 ; WX 720 ; N K ; B 31 0 750 681 ; -C 76 ; WX 600 ; N L ; B 31 0 629 681 ; -C 77 ; WX 920 ; N M ; B 26 0 894 681 ; -C 78 ; WX 740 ; N N ; B 26 0 722 681 ; -C 79 ; WX 800 ; N O ; B 44 -17 758 698 ; -C 80 ; WX 620 ; N P ; B 31 0 613 681 ; -C 81 ; WX 820 ; N Q ; B 44 -189 769 698 ; -C 82 ; WX 720 ; N R ; B 31 0 757 681 ; -C 83 ; WX 660 ; N S ; B 28 -17 634 698 ; -C 84 ; WX 620 ; N T ; B -37 0 656 681 ; -C 85 ; WX 780 ; N U ; B 25 -17 754 681 ; -C 86 ; WX 700 ; N V ; B -30 0 725 681 ; -C 87 ; WX 960 ; N W ; B -30 0 984 681 ; -C 88 ; WX 720 ; N X ; B -30 0 755 681 ; -C 89 ; WX 640 ; N Y ; B -30 0 666 681 ; -C 90 ; WX 640 ; N Z ; B 10 0 656 681 ; -C 91 ; WX 300 ; N bracketleft ; B 92 -136 258 717 ; -C 92 ; WX 600 ; N backslash ; B 74 0 532 717 ; -C 93 ; WX 300 ; N bracketright ; B 41 -136 207 717 ; -C 94 ; WX 600 ; N asciicircum ; B 52 276 554 681 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 220 ; N quoteleft ; B 46 479 178 698 ; -C 97 ; WX 580 ; N a ; B 35 -8 587 494 ; -C 98 ; WX 620 ; N b ; B -2 -8 582 717 ; -C 99 ; WX 520 ; N c ; B 37 -8 498 494 ; -C 100 ; WX 620 ; N d ; B 37 -8 591 717 ; -C 101 ; WX 520 ; N e ; B 37 -8 491 494 ; -C 102 ; WX 320 ; N f ; B 20 0 414 734 ; L i fi ; L l fl ; -C 103 ; WX 540 ; N g ; B 17 -243 542 567 ; -C 104 ; WX 660 ; N h ; B 20 0 643 717 ; -C 105 ; WX 300 ; N i ; B 20 0 288 654 ; -C 106 ; WX 300 ; N j ; B -109 -251 214 654 ; -C 107 ; WX 620 ; N k ; B 20 0 628 717 ; -C 108 ; WX 300 ; N l ; B 20 0 286 717 ; -C 109 ; WX 940 ; N m ; B 17 0 928 494 ; -C 110 ; WX 660 ; N n ; B 20 0 649 494 ; -C 111 ; WX 560 ; N o ; B 37 -8 526 494 ; -C 112 ; WX 620 ; N p ; B 20 -228 583 494 ; -C 113 ; WX 580 ; N q ; B 37 -228 589 494 ; -C 114 ; WX 440 ; N r ; B 20 0 447 494 ; -C 115 ; WX 520 ; N s ; B 40 -8 487 494 ; -C 116 ; WX 380 ; N t ; B 20 -8 388 667 ; -C 117 ; WX 680 ; N u ; B 20 -8 653 484 ; -C 118 ; WX 520 ; N v ; B -23 0 534 484 ; -C 119 ; WX 780 ; N w ; B -19 0 804 484 ; -C 120 ; WX 560 ; N x ; B -16 0 576 484 ; -C 121 ; WX 540 ; N y ; B -23 -236 549 484 ; -C 122 ; WX 480 ; N z ; B 7 0 476 484 ; -C 123 ; WX 280 ; N braceleft ; B 21 -136 260 717 ; -C 124 ; WX 600 ; N bar ; B 264 -250 342 750 ; -C 125 ; WX 280 ; N braceright ; B 21 -136 260 717 ; -C 126 ; WX 600 ; N asciitilde ; B 52 173 556 352 ; -C 161 ; WX 300 ; N exclamdown ; B 75 -214 219 494 ; -C 162 ; WX 620 ; N cent ; B 116 20 511 651 ; -C 163 ; WX 620 ; N sterling ; B 8 -17 631 698 ; -C 164 ; WX 140 ; N fraction ; B -188 0 335 681 ; -C 165 ; WX 620 ; N yen ; B -22 0 647 681 ; -C 166 ; WX 620 ; N florin ; B -29 -155 633 749 ; -C 167 ; WX 520 ; N section ; B 33 -178 486 698 ; -C 168 ; WX 620 ; N currency ; B 58 89 563 591 ; -C 169 ; WX 220 ; N quotesingle ; B 67 458 153 698 ; -C 170 ; WX 400 ; N quotedblleft ; B 46 479 348 698 ; -C 171 ; WX 360 ; N guillemotleft ; B 51 89 312 437 ; -C 172 ; WX 240 ; N guilsinglleft ; B 51 89 189 437 ; -C 173 ; WX 240 ; N guilsinglright ; B 51 89 189 437 ; -C 174 ; WX 620 ; N fi ; B 20 0 608 734 ; -C 175 ; WX 620 ; N fl ; B 20 0 606 734 ; -C 177 ; WX 500 ; N endash ; B -15 232 515 292 ; -C 178 ; WX 540 ; N dagger ; B 79 -156 455 698 ; -C 179 ; WX 540 ; N daggerdbl ; B 79 -156 455 698 ; -C 180 ; WX 320 ; N periodcentered ; B 92 196 220 327 ; -C 182 ; WX 600 ; N paragraph ; B 14 0 577 681 ; -C 183 ; WX 460 ; N bullet ; B 60 170 404 511 ; -C 184 ; WX 220 ; N quotesinglbase ; B 46 -108 178 110 ; -C 185 ; WX 400 ; N quotedblbase ; B 46 -108 348 110 ; -C 186 ; WX 400 ; N quotedblright ; B 46 480 348 698 ; -C 187 ; WX 360 ; N guillemotright ; B 51 89 312 437 ; -C 188 ; WX 1000 ; N ellipsis ; B 101 -8 898 123 ; -C 189 ; WX 1280 ; N perthousand ; B 22 -8 1266 698 ; -C 191 ; WX 540 ; N questiondown ; B 23 -217 510 494 ; -C 193 ; WX 340 ; N grave ; B 68 571 274 689 ; -C 194 ; WX 340 ; N acute ; B 68 571 274 689 ; -C 195 ; WX 420 ; N circumflex ; B 68 567 352 685 ; -C 196 ; WX 440 ; N tilde ; B 68 572 375 661 ; -C 197 ; WX 440 ; N macron ; B 68 587 364 635 ; -C 198 ; WX 460 ; N breve ; B 68 568 396 687 ; -C 199 ; WX 260 ; N dotaccent ; B 68 552 186 672 ; -C 200 ; WX 420 ; N dieresis ; B 68 552 349 674 ; -C 202 ; WX 320 ; N ring ; B 68 546 252 731 ; -C 203 ; WX 320 ; N cedilla ; B 68 -200 257 0 ; -C 205 ; WX 380 ; N hungarumlaut ; B 68 538 311 698 ; -C 206 ; WX 320 ; N ogonek ; B 68 -145 245 0 ; -C 207 ; WX 420 ; N caron ; B 68 554 352 672 ; -C 208 ; WX 1000 ; N emdash ; B -15 232 1015 292 ; -C 225 ; WX 1260 ; N AE ; B -36 0 1250 681 ; -C 227 ; WX 420 ; N ordfeminine ; B 49 395 393 698 ; -C 232 ; WX 600 ; N Lslash ; B 31 0 629 681 ; -C 233 ; WX 800 ; N Oslash ; B 44 -53 758 733 ; -C 234 ; WX 1240 ; N OE ; B 44 -17 1214 698 ; -C 235 ; WX 420 ; N ordmasculine ; B 56 394 361 698 ; -C 241 ; WX 860 ; N ae ; B 35 -8 832 494 ; -C 245 ; WX 300 ; N dotlessi ; B 20 0 288 484 ; -C 248 ; WX 320 ; N lslash ; B 20 0 291 717 ; -C 249 ; WX 560 ; N oslash ; B 37 -40 526 534 ; -C 250 ; WX 900 ; N oe ; B 37 -8 876 494 ; -C 251 ; WX 660 ; N germandbls ; B -109 -110 614 698 ; -C -1 ; WX 520 ; N ecircumflex ; B 37 -8 491 685 ; -C -1 ; WX 520 ; N edieresis ; B 37 -8 491 674 ; -C -1 ; WX 580 ; N aacute ; B 35 -8 587 689 ; -C -1 ; WX 740 ; N registered ; B 23 -17 723 698 ; -C -1 ; WX 300 ; N icircumflex ; B 8 0 292 685 ; -C -1 ; WX 680 ; N udieresis ; B 20 -8 653 674 ; -C -1 ; WX 560 ; N ograve ; B 37 -8 526 689 ; -C -1 ; WX 680 ; N uacute ; B 20 -8 653 689 ; -C -1 ; WX 680 ; N ucircumflex ; B 20 -8 653 685 ; -C -1 ; WX 680 ; N Aacute ; B -37 0 714 866 ; -C -1 ; WX 300 ; N igrave ; B 20 0 288 689 ; -C -1 ; WX 340 ; N Icircumflex ; B 28 0 312 862 ; -C -1 ; WX 520 ; N ccedilla ; B 37 -200 498 494 ; -C -1 ; WX 580 ; N adieresis ; B 35 -8 587 674 ; -C -1 ; WX 720 ; N Ecircumflex ; B 31 0 705 862 ; -C -1 ; WX 520 ; N scaron ; B 40 -8 487 672 ; -C -1 ; WX 620 ; N thorn ; B 20 -228 583 717 ; -C -1 ; WX 980 ; N trademark ; B 34 277 930 681 ; -C -1 ; WX 520 ; N egrave ; B 37 -8 491 689 ; -C -1 ; WX 372 ; N threesuperior ; B 12 269 360 698 ; -C -1 ; WX 480 ; N zcaron ; B 7 0 476 672 ; -C -1 ; WX 580 ; N atilde ; B 35 -8 587 661 ; -C -1 ; WX 580 ; N aring ; B 35 -8 587 731 ; -C -1 ; WX 560 ; N ocircumflex ; B 37 -8 526 685 ; -C -1 ; WX 720 ; N Edieresis ; B 31 0 705 851 ; -C -1 ; WX 930 ; N threequarters ; B 52 0 889 691 ; -C -1 ; WX 540 ; N ydieresis ; B -23 -236 549 674 ; -C -1 ; WX 540 ; N yacute ; B -23 -236 549 689 ; -C -1 ; WX 300 ; N iacute ; B 20 0 288 689 ; -C -1 ; WX 680 ; N Acircumflex ; B -37 0 714 862 ; -C -1 ; WX 780 ; N Uacute ; B 25 -17 754 866 ; -C -1 ; WX 520 ; N eacute ; B 37 -8 491 689 ; -C -1 ; WX 800 ; N Ograve ; B 44 -17 758 866 ; -C -1 ; WX 580 ; N agrave ; B 35 -8 587 689 ; -C -1 ; WX 780 ; N Udieresis ; B 25 -17 754 851 ; -C -1 ; WX 580 ; N acircumflex ; B 35 -8 587 685 ; -C -1 ; WX 340 ; N Igrave ; B 31 0 301 866 ; -C -1 ; WX 372 ; N twosuperior ; B 20 279 367 698 ; -C -1 ; WX 780 ; N Ugrave ; B 25 -17 754 866 ; -C -1 ; WX 930 ; N onequarter ; B 80 0 869 681 ; -C -1 ; WX 780 ; N Ucircumflex ; B 25 -17 754 862 ; -C -1 ; WX 660 ; N Scaron ; B 28 -17 634 849 ; -C -1 ; WX 340 ; N Idieresis ; B 28 0 309 851 ; -C -1 ; WX 300 ; N idieresis ; B 8 0 289 674 ; -C -1 ; WX 720 ; N Egrave ; B 31 0 705 866 ; -C -1 ; WX 800 ; N Oacute ; B 44 -17 758 866 ; -C -1 ; WX 600 ; N divide ; B 51 10 555 514 ; -C -1 ; WX 680 ; N Atilde ; B -37 0 714 838 ; -C -1 ; WX 680 ; N Aring ; B -37 0 714 908 ; -C -1 ; WX 800 ; N Odieresis ; B 44 -17 758 851 ; -C -1 ; WX 680 ; N Adieresis ; B -37 0 714 851 ; -C -1 ; WX 740 ; N Ntilde ; B 26 0 722 838 ; -C -1 ; WX 640 ; N Zcaron ; B 10 0 656 849 ; -C -1 ; WX 620 ; N Thorn ; B 31 0 613 681 ; -C -1 ; WX 340 ; N Iacute ; B 31 0 301 866 ; -C -1 ; WX 600 ; N plusminus ; B 51 0 555 513 ; -C -1 ; WX 600 ; N multiply ; B 51 9 555 513 ; -C -1 ; WX 720 ; N Eacute ; B 31 0 705 866 ; -C -1 ; WX 640 ; N Ydieresis ; B -30 0 666 851 ; -C -1 ; WX 372 ; N onesuperior ; B 80 279 302 688 ; -C -1 ; WX 680 ; N ugrave ; B 20 -8 653 689 ; -C -1 ; WX 600 ; N logicalnot ; B 51 128 555 398 ; -C -1 ; WX 660 ; N ntilde ; B 20 0 649 661 ; -C -1 ; WX 800 ; N Otilde ; B 44 -17 758 838 ; -C -1 ; WX 560 ; N otilde ; B 37 -8 526 661 ; -C -1 ; WX 740 ; N Ccedilla ; B 44 -200 702 698 ; -C -1 ; WX 680 ; N Agrave ; B -37 0 714 866 ; -C -1 ; WX 930 ; N onehalf ; B 80 0 885 681 ; -C -1 ; WX 800 ; N Eth ; B 31 0 752 681 ; -C -1 ; WX 400 ; N degree ; B 50 398 350 698 ; -C -1 ; WX 640 ; N Yacute ; B -30 0 666 866 ; -C -1 ; WX 800 ; N Ocircumflex ; B 44 -17 758 862 ; -C -1 ; WX 560 ; N oacute ; B 37 -8 526 689 ; -C -1 ; WX 680 ; N mu ; B 20 -251 653 484 ; -C -1 ; WX 600 ; N minus ; B 51 224 555 300 ; -C -1 ; WX 560 ; N eth ; B 37 -8 526 734 ; -C -1 ; WX 560 ; N odieresis ; B 37 -8 526 674 ; -C -1 ; WX 740 ; N copyright ; B 24 -17 724 698 ; -C -1 ; WX 600 ; N brokenbar ; B 264 -175 342 675 ; -EndCharMetrics -StartKernData -StartKernPairs 82 - -KPX A y 32 -KPX A w 4 -KPX A v 7 -KPX A Y -35 -KPX A W -40 -KPX A V -56 -KPX A T 1 - -KPX F period -46 -KPX F comma -41 -KPX F A -21 - -KPX L y 79 -KPX L Y 13 -KPX L W 1 -KPX L V -4 -KPX L T 28 - -KPX P period -60 -KPX P comma -55 -KPX P A -8 - -KPX R y 59 -KPX R Y 26 -KPX R W 13 -KPX R V 8 -KPX R T 71 - -KPX T s 16 -KPX T r 38 -KPX T period -33 -KPX T o 15 -KPX T i 42 -KPX T hyphen 90 -KPX T e 13 -KPX T comma -28 -KPX T c 14 -KPX T a 17 -KPX T A 1 - -KPX V y 15 -KPX V u -38 -KPX V r -41 -KPX V period -40 -KPX V o -71 -KPX V i -20 -KPX V hyphen 11 -KPX V e -72 -KPX V comma -34 -KPX V a -69 -KPX V A -66 - -KPX W y 15 -KPX W u -38 -KPX W r -41 -KPX W period -40 -KPX W o -68 -KPX W i -20 -KPX W hyphen 11 -KPX W e -69 -KPX W comma -34 -KPX W a -66 -KPX W A -64 - -KPX Y v 15 -KPX Y u -38 -KPX Y q -55 -KPX Y period -40 -KPX Y p -31 -KPX Y o -57 -KPX Y i -37 -KPX Y hyphen 11 -KPX Y e -58 -KPX Y comma -34 -KPX Y a -54 -KPX Y A -53 - -KPX f f 29 - -KPX r q 9 -KPX r period -64 -KPX r o 8 -KPX r n 31 -KPX r m 31 -KPX r hyphen 70 -KPX r h -21 -KPX r g -4 -KPX r f 33 -KPX r e 7 -KPX r d 7 -KPX r comma -58 -KPX r c 7 -EndKernPairs -EndKernData -StartComposites 56 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 200 177 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 130 177 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 130 177 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 140 177 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 180 177 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 120 177 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 220 177 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 150 177 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 150 177 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 160 177 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 20 177 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex -40 177 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis -40 177 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave -20 177 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 150 177 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 260 177 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 190 177 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 190 177 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 200 177 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 180 177 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 120 177 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 250 177 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 180 177 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 180 177 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 190 177 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 150 177 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 110 177 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 110 177 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 120 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 80 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 80 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 120 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 130 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 70 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 90 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 50 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 50 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 90 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -20 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -60 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -60 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -20 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 110 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 110 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 70 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 70 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 110 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 60 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 50 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 170 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 130 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 130 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 170 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 100 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 60 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 30 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pbkli8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pbkli8a.afm deleted file mode 100644 index 419c319..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pbkli8a.afm +++ /dev/null @@ -1,410 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1992 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Tue Jan 21 16:12:06 1992 -Comment UniqueID 37830 -Comment VMusage 33139 40031 -FontName Bookman-LightItalic -FullName ITC Bookman Light Italic -FamilyName ITC Bookman -Weight Light -ItalicAngle -10 -IsFixedPitch false -FontBBox -228 -250 1269 883 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.004 -Notice Copyright (c) 1985, 1987, 1989, 1992 Adobe Systems Incorporated. All Rights Reserved.ITC Bookman is a registered trademark of International Typeface Corporation. -EncodingScheme AdobeStandardEncoding -CapHeight 681 -XHeight 494 -Ascender 717 -Descender -212 -StartCharMetrics 228 -C 32 ; WX 300 ; N space ; B 0 0 0 0 ; -C 33 ; WX 320 ; N exclam ; B 103 -8 342 698 ; -C 34 ; WX 360 ; N quotedbl ; B 107 468 402 698 ; -C 35 ; WX 620 ; N numbersign ; B 107 0 598 681 ; -C 36 ; WX 620 ; N dollar ; B 78 -85 619 762 ; -C 37 ; WX 800 ; N percent ; B 56 -8 811 691 ; -C 38 ; WX 820 ; N ampersand ; B 65 -18 848 698 ; -C 39 ; WX 280 ; N quoteright ; B 148 470 288 698 ; -C 40 ; WX 280 ; N parenleft ; B 96 -146 383 727 ; -C 41 ; WX 280 ; N parenright ; B -8 -146 279 727 ; -C 42 ; WX 440 ; N asterisk ; B 139 324 505 698 ; -C 43 ; WX 600 ; N plus ; B 91 43 595 548 ; -C 44 ; WX 300 ; N comma ; B 88 -115 227 112 ; -C 45 ; WX 320 ; N hyphen ; B 78 269 336 325 ; -C 46 ; WX 300 ; N period ; B 96 -8 231 127 ; -C 47 ; WX 600 ; N slash ; B 104 -149 562 717 ; -C 48 ; WX 620 ; N zero ; B 86 -17 646 698 ; -C 49 ; WX 620 ; N one ; B 154 0 500 681 ; -C 50 ; WX 620 ; N two ; B 66 0 636 698 ; -C 51 ; WX 620 ; N three ; B 55 -17 622 698 ; -C 52 ; WX 620 ; N four ; B 69 0 634 681 ; -C 53 ; WX 620 ; N five ; B 70 -17 614 681 ; -C 54 ; WX 620 ; N six ; B 89 -17 657 698 ; -C 55 ; WX 620 ; N seven ; B 143 0 672 681 ; -C 56 ; WX 620 ; N eight ; B 61 -17 655 698 ; -C 57 ; WX 620 ; N nine ; B 77 -17 649 698 ; -C 58 ; WX 300 ; N colon ; B 96 -8 292 494 ; -C 59 ; WX 300 ; N semicolon ; B 88 -114 292 494 ; -C 60 ; WX 600 ; N less ; B 79 33 588 561 ; -C 61 ; WX 600 ; N equal ; B 91 161 595 433 ; -C 62 ; WX 600 ; N greater ; B 93 33 602 561 ; -C 63 ; WX 540 ; N question ; B 114 -8 604 698 ; -C 64 ; WX 780 ; N at ; B 102 -17 802 698 ; -C 65 ; WX 700 ; N A ; B -25 0 720 681 ; -C 66 ; WX 720 ; N B ; B 21 0 746 681 ; -C 67 ; WX 720 ; N C ; B 88 -17 746 698 ; -C 68 ; WX 740 ; N D ; B 21 0 782 681 ; -C 69 ; WX 680 ; N E ; B 21 0 736 681 ; -C 70 ; WX 620 ; N F ; B 21 0 743 681 ; -C 71 ; WX 760 ; N G ; B 88 -17 813 698 ; -C 72 ; WX 800 ; N H ; B 21 0 888 681 ; -C 73 ; WX 320 ; N I ; B 21 0 412 681 ; -C 74 ; WX 560 ; N J ; B -2 -17 666 681 ; -C 75 ; WX 720 ; N K ; B 21 0 804 681 ; -C 76 ; WX 580 ; N L ; B 21 0 656 681 ; -C 77 ; WX 860 ; N M ; B 18 0 956 681 ; -C 78 ; WX 720 ; N N ; B 18 0 823 681 ; -C 79 ; WX 760 ; N O ; B 88 -17 799 698 ; -C 80 ; WX 600 ; N P ; B 21 0 681 681 ; -C 81 ; WX 780 ; N Q ; B 61 -191 812 698 ; -C 82 ; WX 700 ; N R ; B 21 0 736 681 ; -C 83 ; WX 640 ; N S ; B 61 -17 668 698 ; -C 84 ; WX 600 ; N T ; B 50 0 725 681 ; -C 85 ; WX 720 ; N U ; B 118 -17 842 681 ; -C 86 ; WX 680 ; N V ; B 87 0 815 681 ; -C 87 ; WX 960 ; N W ; B 87 0 1095 681 ; -C 88 ; WX 700 ; N X ; B -25 0 815 681 ; -C 89 ; WX 660 ; N Y ; B 87 0 809 681 ; -C 90 ; WX 580 ; N Z ; B 8 0 695 681 ; -C 91 ; WX 260 ; N bracketleft ; B 56 -136 351 717 ; -C 92 ; WX 600 ; N backslash ; B 84 0 542 717 ; -C 93 ; WX 260 ; N bracketright ; B 15 -136 309 717 ; -C 94 ; WX 600 ; N asciicircum ; B 97 276 599 681 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 280 ; N quoteleft ; B 191 470 330 698 ; -C 97 ; WX 620 ; N a ; B 71 -8 686 494 ; -C 98 ; WX 600 ; N b ; B 88 -8 621 717 ; -C 99 ; WX 480 ; N c ; B 65 -8 522 494 ; -C 100 ; WX 640 ; N d ; B 65 -8 695 717 ; -C 101 ; WX 540 ; N e ; B 65 -8 575 494 ; -C 102 ; WX 340 ; N f ; B -160 -218 557 725 ; L i fi ; L l fl ; -C 103 ; WX 560 ; N g ; B 4 -221 581 494 ; -C 104 ; WX 620 ; N h ; B 88 -8 689 717 ; -C 105 ; WX 280 ; N i ; B 88 -8 351 663 ; -C 106 ; WX 280 ; N j ; B -200 -221 308 663 ; -C 107 ; WX 600 ; N k ; B 88 -8 657 717 ; -C 108 ; WX 280 ; N l ; B 100 -8 342 717 ; -C 109 ; WX 880 ; N m ; B 88 -8 952 494 ; -C 110 ; WX 620 ; N n ; B 88 -8 673 494 ; -C 111 ; WX 540 ; N o ; B 65 -8 572 494 ; -C 112 ; WX 600 ; N p ; B -24 -212 620 494 ; -C 113 ; WX 560 ; N q ; B 65 -212 584 494 ; -C 114 ; WX 400 ; N r ; B 88 0 481 494 ; -C 115 ; WX 540 ; N s ; B 65 -8 547 494 ; -C 116 ; WX 340 ; N t ; B 88 -8 411 664 ; -C 117 ; WX 620 ; N u ; B 88 -8 686 484 ; -C 118 ; WX 540 ; N v ; B 88 -8 562 494 ; -C 119 ; WX 880 ; N w ; B 88 -8 893 494 ; -C 120 ; WX 540 ; N x ; B 9 -8 626 494 ; -C 121 ; WX 600 ; N y ; B 60 -221 609 484 ; -C 122 ; WX 520 ; N z ; B 38 -8 561 494 ; -C 123 ; WX 360 ; N braceleft ; B 122 -191 442 717 ; -C 124 ; WX 600 ; N bar ; B 294 -250 372 750 ; -C 125 ; WX 380 ; N braceright ; B 13 -191 333 717 ; -C 126 ; WX 600 ; N asciitilde ; B 91 207 595 386 ; -C 161 ; WX 320 ; N exclamdown ; B 73 -213 301 494 ; -C 162 ; WX 620 ; N cent ; B 148 -29 596 715 ; -C 163 ; WX 620 ; N sterling ; B 4 -17 702 698 ; -C 164 ; WX 20 ; N fraction ; B -228 0 323 681 ; -C 165 ; WX 620 ; N yen ; B 71 0 735 681 ; -C 166 ; WX 620 ; N florin ; B -26 -218 692 725 ; -C 167 ; WX 620 ; N section ; B 38 -178 638 698 ; -C 168 ; WX 620 ; N currency ; B 100 89 605 591 ; -C 169 ; WX 200 ; N quotesingle ; B 99 473 247 698 ; -C 170 ; WX 440 ; N quotedblleft ; B 191 470 493 698 ; -C 171 ; WX 300 ; N guillemotleft ; B 70 129 313 434 ; -C 172 ; WX 180 ; N guilsinglleft ; B 75 129 208 434 ; -C 173 ; WX 180 ; N guilsinglright ; B 70 129 203 434 ; -C 174 ; WX 640 ; N fi ; B -159 -222 709 725 ; -C 175 ; WX 660 ; N fl ; B -159 -218 713 725 ; -C 177 ; WX 500 ; N endash ; B 33 269 561 325 ; -C 178 ; WX 620 ; N dagger ; B 192 -130 570 698 ; -C 179 ; WX 620 ; N daggerdbl ; B 144 -122 566 698 ; -C 180 ; WX 300 ; N periodcentered ; B 137 229 272 364 ; -C 182 ; WX 620 ; N paragraph ; B 112 0 718 681 ; -C 183 ; WX 460 ; N bullet ; B 100 170 444 511 ; -C 184 ; WX 320 ; N quotesinglbase ; B 87 -114 226 113 ; -C 185 ; WX 480 ; N quotedblbase ; B 87 -114 390 113 ; -C 186 ; WX 440 ; N quotedblright ; B 148 470 451 698 ; -C 187 ; WX 300 ; N guillemotright ; B 60 129 303 434 ; -C 188 ; WX 1000 ; N ellipsis ; B 99 -8 900 127 ; -C 189 ; WX 1180 ; N perthousand ; B 56 -8 1199 691 ; -C 191 ; WX 540 ; N questiondown ; B 18 -212 508 494 ; -C 193 ; WX 340 ; N grave ; B 182 551 377 706 ; -C 194 ; WX 320 ; N acute ; B 178 551 373 706 ; -C 195 ; WX 440 ; N circumflex ; B 176 571 479 685 ; -C 196 ; WX 440 ; N tilde ; B 180 586 488 671 ; -C 197 ; WX 440 ; N macron ; B 178 599 484 658 ; -C 198 ; WX 440 ; N breve ; B 191 577 500 680 ; -C 199 ; WX 260 ; N dotaccent ; B 169 543 290 664 ; -C 200 ; WX 420 ; N dieresis ; B 185 569 467 688 ; -C 202 ; WX 300 ; N ring ; B 178 551 334 706 ; -C 203 ; WX 320 ; N cedilla ; B 45 -178 240 0 ; -C 205 ; WX 340 ; N hungarumlaut ; B 167 547 402 738 ; -C 206 ; WX 260 ; N ogonek ; B 51 -173 184 0 ; -C 207 ; WX 440 ; N caron ; B 178 571 481 684 ; -C 208 ; WX 1000 ; N emdash ; B 33 269 1061 325 ; -C 225 ; WX 1220 ; N AE ; B -45 0 1269 681 ; -C 227 ; WX 440 ; N ordfeminine ; B 130 396 513 698 ; -C 232 ; WX 580 ; N Lslash ; B 21 0 656 681 ; -C 233 ; WX 760 ; N Oslash ; B 88 -95 799 777 ; -C 234 ; WX 1180 ; N OE ; B 88 -17 1237 698 ; -C 235 ; WX 400 ; N ordmasculine ; B 139 396 455 698 ; -C 241 ; WX 880 ; N ae ; B 71 -8 918 494 ; -C 245 ; WX 280 ; N dotlessi ; B 88 -8 351 484 ; -C 248 ; WX 340 ; N lslash ; B 50 -8 398 717 ; -C 249 ; WX 540 ; N oslash ; B 65 -49 571 532 ; -C 250 ; WX 900 ; N oe ; B 65 -8 948 494 ; -C 251 ; WX 620 ; N germandbls ; B -121 -111 653 698 ; -C -1 ; WX 540 ; N ecircumflex ; B 65 -8 575 685 ; -C -1 ; WX 540 ; N edieresis ; B 65 -8 575 688 ; -C -1 ; WX 620 ; N aacute ; B 71 -8 686 706 ; -C -1 ; WX 740 ; N registered ; B 84 -17 784 698 ; -C -1 ; WX 280 ; N icircumflex ; B 76 -8 379 685 ; -C -1 ; WX 620 ; N udieresis ; B 88 -8 686 688 ; -C -1 ; WX 540 ; N ograve ; B 65 -8 572 706 ; -C -1 ; WX 620 ; N uacute ; B 88 -8 686 706 ; -C -1 ; WX 620 ; N ucircumflex ; B 88 -8 686 685 ; -C -1 ; WX 700 ; N Aacute ; B -25 0 720 883 ; -C -1 ; WX 280 ; N igrave ; B 88 -8 351 706 ; -C -1 ; WX 320 ; N Icircumflex ; B 21 0 449 862 ; -C -1 ; WX 480 ; N ccedilla ; B 65 -178 522 494 ; -C -1 ; WX 620 ; N adieresis ; B 71 -8 686 688 ; -C -1 ; WX 680 ; N Ecircumflex ; B 21 0 736 862 ; -C -1 ; WX 540 ; N scaron ; B 65 -8 547 684 ; -C -1 ; WX 600 ; N thorn ; B -24 -212 620 717 ; -C -1 ; WX 980 ; N trademark ; B 69 277 965 681 ; -C -1 ; WX 540 ; N egrave ; B 65 -8 575 706 ; -C -1 ; WX 372 ; N threesuperior ; B 70 269 439 698 ; -C -1 ; WX 520 ; N zcaron ; B 38 -8 561 684 ; -C -1 ; WX 620 ; N atilde ; B 71 -8 686 671 ; -C -1 ; WX 620 ; N aring ; B 71 -8 686 706 ; -C -1 ; WX 540 ; N ocircumflex ; B 65 -8 572 685 ; -C -1 ; WX 680 ; N Edieresis ; B 21 0 736 865 ; -C -1 ; WX 930 ; N threequarters ; B 99 0 913 691 ; -C -1 ; WX 600 ; N ydieresis ; B 60 -221 609 688 ; -C -1 ; WX 600 ; N yacute ; B 60 -221 609 706 ; -C -1 ; WX 280 ; N iacute ; B 88 -8 351 706 ; -C -1 ; WX 700 ; N Acircumflex ; B -25 0 720 862 ; -C -1 ; WX 720 ; N Uacute ; B 118 -17 842 883 ; -C -1 ; WX 540 ; N eacute ; B 65 -8 575 706 ; -C -1 ; WX 760 ; N Ograve ; B 88 -17 799 883 ; -C -1 ; WX 620 ; N agrave ; B 71 -8 686 706 ; -C -1 ; WX 720 ; N Udieresis ; B 118 -17 842 865 ; -C -1 ; WX 620 ; N acircumflex ; B 71 -8 686 685 ; -C -1 ; WX 320 ; N Igrave ; B 21 0 412 883 ; -C -1 ; WX 372 ; N twosuperior ; B 68 279 439 698 ; -C -1 ; WX 720 ; N Ugrave ; B 118 -17 842 883 ; -C -1 ; WX 930 ; N onequarter ; B 91 0 913 681 ; -C -1 ; WX 720 ; N Ucircumflex ; B 118 -17 842 862 ; -C -1 ; WX 640 ; N Scaron ; B 61 -17 668 861 ; -C -1 ; WX 320 ; N Idieresis ; B 21 0 447 865 ; -C -1 ; WX 280 ; N idieresis ; B 88 -8 377 688 ; -C -1 ; WX 680 ; N Egrave ; B 21 0 736 883 ; -C -1 ; WX 760 ; N Oacute ; B 88 -17 799 883 ; -C -1 ; WX 600 ; N divide ; B 91 46 595 548 ; -C -1 ; WX 700 ; N Atilde ; B -25 0 720 848 ; -C -1 ; WX 700 ; N Aring ; B -25 0 720 883 ; -C -1 ; WX 760 ; N Odieresis ; B 88 -17 799 865 ; -C -1 ; WX 700 ; N Adieresis ; B -25 0 720 865 ; -C -1 ; WX 720 ; N Ntilde ; B 18 0 823 848 ; -C -1 ; WX 580 ; N Zcaron ; B 8 0 695 861 ; -C -1 ; WX 600 ; N Thorn ; B 21 0 656 681 ; -C -1 ; WX 320 ; N Iacute ; B 21 0 412 883 ; -C -1 ; WX 600 ; N plusminus ; B 91 0 595 548 ; -C -1 ; WX 600 ; N multiply ; B 91 44 595 548 ; -C -1 ; WX 680 ; N Eacute ; B 21 0 736 883 ; -C -1 ; WX 660 ; N Ydieresis ; B 87 0 809 865 ; -C -1 ; WX 372 ; N onesuperior ; B 114 279 339 688 ; -C -1 ; WX 620 ; N ugrave ; B 88 -8 686 706 ; -C -1 ; WX 600 ; N logicalnot ; B 91 163 595 433 ; -C -1 ; WX 620 ; N ntilde ; B 88 -8 673 671 ; -C -1 ; WX 760 ; N Otilde ; B 88 -17 799 848 ; -C -1 ; WX 540 ; N otilde ; B 65 -8 572 671 ; -C -1 ; WX 720 ; N Ccedilla ; B 88 -178 746 698 ; -C -1 ; WX 700 ; N Agrave ; B -25 0 720 883 ; -C -1 ; WX 930 ; N onehalf ; B 91 0 925 681 ; -C -1 ; WX 740 ; N Eth ; B 21 0 782 681 ; -C -1 ; WX 400 ; N degree ; B 120 398 420 698 ; -C -1 ; WX 660 ; N Yacute ; B 87 0 809 883 ; -C -1 ; WX 760 ; N Ocircumflex ; B 88 -17 799 862 ; -C -1 ; WX 540 ; N oacute ; B 65 -8 572 706 ; -C -1 ; WX 620 ; N mu ; B 53 -221 686 484 ; -C -1 ; WX 600 ; N minus ; B 91 259 595 335 ; -C -1 ; WX 540 ; N eth ; B 65 -8 642 725 ; -C -1 ; WX 540 ; N odieresis ; B 65 -8 572 688 ; -C -1 ; WX 740 ; N copyright ; B 84 -17 784 698 ; -C -1 ; WX 600 ; N brokenbar ; B 294 -175 372 675 ; -EndCharMetrics -StartKernData -StartKernPairs 85 - -KPX A Y -62 -KPX A W -73 -KPX A V -78 -KPX A T -5 - -KPX F period -97 -KPX F comma -98 -KPX F A -16 - -KPX L y 20 -KPX L Y 7 -KPX L W 9 -KPX L V 4 - -KPX P period -105 -KPX P comma -106 -KPX P A -30 - -KPX R Y 11 -KPX R W 2 -KPX R V 2 -KPX R T 65 - -KPX T semicolon 48 -KPX T s -7 -KPX T r 67 -KPX T period -78 -KPX T o 14 -KPX T i 71 -KPX T hyphen 20 -KPX T e 10 -KPX T comma -79 -KPX T colon 48 -KPX T c 16 -KPX T a 9 -KPX T A -14 - -KPX V y -14 -KPX V u -10 -KPX V semicolon -44 -KPX V r -20 -KPX V period -100 -KPX V o -70 -KPX V i 3 -KPX V hyphen 20 -KPX V e -70 -KPX V comma -109 -KPX V colon -35 -KPX V a -70 -KPX V A -70 - -KPX W y -14 -KPX W u -20 -KPX W semicolon -42 -KPX W r -30 -KPX W period -100 -KPX W o -60 -KPX W i 3 -KPX W hyphen 20 -KPX W e -60 -KPX W comma -109 -KPX W colon -35 -KPX W a -60 -KPX W A -60 - -KPX Y v -19 -KPX Y u -31 -KPX Y semicolon -40 -KPX Y q -72 -KPX Y period -100 -KPX Y p -37 -KPX Y o -75 -KPX Y i -11 -KPX Y hyphen 20 -KPX Y e -78 -KPX Y comma -109 -KPX Y colon -35 -KPX Y a -79 -KPX Y A -82 - -KPX f f -19 - -KPX r q -14 -KPX r period -134 -KPX r o -10 -KPX r n 38 -KPX r m 37 -KPX r hyphen 20 -KPX r h -20 -KPX r g -3 -KPX r f -9 -KPX r e -15 -KPX r d -9 -KPX r comma -143 -KPX r c -8 -EndKernPairs -EndKernData -StartComposites 56 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 200 177 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 130 177 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 140 177 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 160 177 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 220 177 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 130 177 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 210 177 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 140 177 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 150 177 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 150 177 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 30 177 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex -30 177 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis -20 177 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave -30 177 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 130 177 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 250 177 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 190 177 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 200 177 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 210 177 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 190 177 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 100 177 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 230 177 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 170 177 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 180 177 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 170 177 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 200 177 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 140 177 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 70 177 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 120 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 70 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 80 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 110 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 140 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 60 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 90 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 30 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 40 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 80 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -40 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -100 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -90 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -60 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 60 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 80 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 20 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 40 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 80 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 30 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 30 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 120 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 60 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 70 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 110 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 140 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 70 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 20 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pcrb8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pcrb8a.afm deleted file mode 100644 index baf3a51..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pcrb8a.afm +++ /dev/null @@ -1,344 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1989, 1990, 1991, Adobe Systems Incorporated. All rights reserved. -Comment Creation Date: Tue Sep 17 14:02:41 1991 -Comment UniqueID 36384 -Comment VMusage 31992 40360 -FontName Courier-Bold -FullName Courier Bold -FamilyName Courier -Weight Bold -ItalicAngle 0 -IsFixedPitch true -FontBBox -113 -250 749 801 -UnderlinePosition -100 -UnderlineThickness 50 -Version 002.004 -Notice Copyright (c) 1989, 1990, 1991, Adobe Systems Incorporated. All rights reserved. -EncodingScheme AdobeStandardEncoding -CapHeight 562 -XHeight 439 -Ascender 626 -Descender -142 -StartCharMetrics 260 -C 32 ; WX 600 ; N space ; B 0 0 0 0 ; -C 33 ; WX 600 ; N exclam ; B 202 -15 398 572 ; -C 34 ; WX 600 ; N quotedbl ; B 135 277 465 562 ; -C 35 ; WX 600 ; N numbersign ; B 56 -45 544 651 ; -C 36 ; WX 600 ; N dollar ; B 82 -126 519 666 ; -C 37 ; WX 600 ; N percent ; B 5 -15 595 616 ; -C 38 ; WX 600 ; N ampersand ; B 36 -15 546 543 ; -C 39 ; WX 600 ; N quoteright ; B 171 277 423 562 ; -C 40 ; WX 600 ; N parenleft ; B 219 -102 461 616 ; -C 41 ; WX 600 ; N parenright ; B 139 -102 381 616 ; -C 42 ; WX 600 ; N asterisk ; B 91 219 509 601 ; -C 43 ; WX 600 ; N plus ; B 71 39 529 478 ; -C 44 ; WX 600 ; N comma ; B 123 -111 393 174 ; -C 45 ; WX 600 ; N hyphen ; B 100 203 500 313 ; -C 46 ; WX 600 ; N period ; B 192 -15 408 171 ; -C 47 ; WX 600 ; N slash ; B 98 -77 502 626 ; -C 48 ; WX 600 ; N zero ; B 87 -15 513 616 ; -C 49 ; WX 600 ; N one ; B 81 0 539 616 ; -C 50 ; WX 600 ; N two ; B 61 0 499 616 ; -C 51 ; WX 600 ; N three ; B 63 -15 501 616 ; -C 52 ; WX 600 ; N four ; B 53 0 507 616 ; -C 53 ; WX 600 ; N five ; B 70 -15 521 601 ; -C 54 ; WX 600 ; N six ; B 90 -15 521 616 ; -C 55 ; WX 600 ; N seven ; B 55 0 494 601 ; -C 56 ; WX 600 ; N eight ; B 83 -15 517 616 ; -C 57 ; WX 600 ; N nine ; B 79 -15 510 616 ; -C 58 ; WX 600 ; N colon ; B 191 -15 407 425 ; -C 59 ; WX 600 ; N semicolon ; B 123 -111 408 425 ; -C 60 ; WX 600 ; N less ; B 66 15 523 501 ; -C 61 ; WX 600 ; N equal ; B 71 118 529 398 ; -C 62 ; WX 600 ; N greater ; B 77 15 534 501 ; -C 63 ; WX 600 ; N question ; B 98 -14 501 580 ; -C 64 ; WX 600 ; N at ; B 16 -15 584 616 ; -C 65 ; WX 600 ; N A ; B -9 0 609 562 ; -C 66 ; WX 600 ; N B ; B 30 0 573 562 ; -C 67 ; WX 600 ; N C ; B 22 -18 560 580 ; -C 68 ; WX 600 ; N D ; B 30 0 594 562 ; -C 69 ; WX 600 ; N E ; B 25 0 560 562 ; -C 70 ; WX 600 ; N F ; B 39 0 570 562 ; -C 71 ; WX 600 ; N G ; B 22 -18 594 580 ; -C 72 ; WX 600 ; N H ; B 20 0 580 562 ; -C 73 ; WX 600 ; N I ; B 77 0 523 562 ; -C 74 ; WX 600 ; N J ; B 37 -18 601 562 ; -C 75 ; WX 600 ; N K ; B 21 0 599 562 ; -C 76 ; WX 600 ; N L ; B 39 0 578 562 ; -C 77 ; WX 600 ; N M ; B -2 0 602 562 ; -C 78 ; WX 600 ; N N ; B 8 -12 610 562 ; -C 79 ; WX 600 ; N O ; B 22 -18 578 580 ; -C 80 ; WX 600 ; N P ; B 48 0 559 562 ; -C 81 ; WX 600 ; N Q ; B 32 -138 578 580 ; -C 82 ; WX 600 ; N R ; B 24 0 599 562 ; -C 83 ; WX 600 ; N S ; B 47 -22 553 582 ; -C 84 ; WX 600 ; N T ; B 21 0 579 562 ; -C 85 ; WX 600 ; N U ; B 4 -18 596 562 ; -C 86 ; WX 600 ; N V ; B -13 0 613 562 ; -C 87 ; WX 600 ; N W ; B -18 0 618 562 ; -C 88 ; WX 600 ; N X ; B 12 0 588 562 ; -C 89 ; WX 600 ; N Y ; B 12 0 589 562 ; -C 90 ; WX 600 ; N Z ; B 62 0 539 562 ; -C 91 ; WX 600 ; N bracketleft ; B 245 -102 475 616 ; -C 92 ; WX 600 ; N backslash ; B 99 -77 503 626 ; -C 93 ; WX 600 ; N bracketright ; B 125 -102 355 616 ; -C 94 ; WX 600 ; N asciicircum ; B 108 250 492 616 ; -C 95 ; WX 600 ; N underscore ; B 0 -125 600 -75 ; -C 96 ; WX 600 ; N quoteleft ; B 178 277 428 562 ; -C 97 ; WX 600 ; N a ; B 35 -15 570 454 ; -C 98 ; WX 600 ; N b ; B 0 -15 584 626 ; -C 99 ; WX 600 ; N c ; B 40 -15 545 459 ; -C 100 ; WX 600 ; N d ; B 20 -15 591 626 ; -C 101 ; WX 600 ; N e ; B 40 -15 563 454 ; -C 102 ; WX 600 ; N f ; B 83 0 547 626 ; L i fi ; L l fl ; -C 103 ; WX 600 ; N g ; B 30 -146 580 454 ; -C 104 ; WX 600 ; N h ; B 5 0 592 626 ; -C 105 ; WX 600 ; N i ; B 77 0 523 658 ; -C 106 ; WX 600 ; N j ; B 63 -146 440 658 ; -C 107 ; WX 600 ; N k ; B 20 0 585 626 ; -C 108 ; WX 600 ; N l ; B 77 0 523 626 ; -C 109 ; WX 600 ; N m ; B -22 0 626 454 ; -C 110 ; WX 600 ; N n ; B 18 0 592 454 ; -C 111 ; WX 600 ; N o ; B 30 -15 570 454 ; -C 112 ; WX 600 ; N p ; B -1 -142 570 454 ; -C 113 ; WX 600 ; N q ; B 20 -142 591 454 ; -C 114 ; WX 600 ; N r ; B 47 0 580 454 ; -C 115 ; WX 600 ; N s ; B 68 -17 535 459 ; -C 116 ; WX 600 ; N t ; B 47 -15 532 562 ; -C 117 ; WX 600 ; N u ; B -1 -15 569 439 ; -C 118 ; WX 600 ; N v ; B -1 0 601 439 ; -C 119 ; WX 600 ; N w ; B -18 0 618 439 ; -C 120 ; WX 600 ; N x ; B 6 0 594 439 ; -C 121 ; WX 600 ; N y ; B -4 -142 601 439 ; -C 122 ; WX 600 ; N z ; B 81 0 520 439 ; -C 123 ; WX 600 ; N braceleft ; B 160 -102 464 616 ; -C 124 ; WX 600 ; N bar ; B 255 -250 345 750 ; -C 125 ; WX 600 ; N braceright ; B 136 -102 440 616 ; -C 126 ; WX 600 ; N asciitilde ; B 71 153 530 356 ; -C 161 ; WX 600 ; N exclamdown ; B 202 -146 398 449 ; -C 162 ; WX 600 ; N cent ; B 66 -49 518 614 ; -C 163 ; WX 600 ; N sterling ; B 72 -28 558 611 ; -C 164 ; WX 600 ; N fraction ; B 25 -60 576 661 ; -C 165 ; WX 600 ; N yen ; B 10 0 590 562 ; -C 166 ; WX 600 ; N florin ; B -30 -131 572 616 ; -C 167 ; WX 600 ; N section ; B 83 -70 517 580 ; -C 168 ; WX 600 ; N currency ; B 54 49 546 517 ; -C 169 ; WX 600 ; N quotesingle ; B 227 277 373 562 ; -C 170 ; WX 600 ; N quotedblleft ; B 71 277 535 562 ; -C 171 ; WX 600 ; N guillemotleft ; B 8 70 553 446 ; -C 172 ; WX 600 ; N guilsinglleft ; B 141 70 459 446 ; -C 173 ; WX 600 ; N guilsinglright ; B 141 70 459 446 ; -C 174 ; WX 600 ; N fi ; B 12 0 593 626 ; -C 175 ; WX 600 ; N fl ; B 12 0 593 626 ; -C 177 ; WX 600 ; N endash ; B 65 203 535 313 ; -C 178 ; WX 600 ; N dagger ; B 106 -70 494 580 ; -C 179 ; WX 600 ; N daggerdbl ; B 106 -70 494 580 ; -C 180 ; WX 600 ; N periodcentered ; B 196 165 404 351 ; -C 182 ; WX 600 ; N paragraph ; B 6 -70 576 580 ; -C 183 ; WX 600 ; N bullet ; B 140 132 460 430 ; -C 184 ; WX 600 ; N quotesinglbase ; B 175 -142 427 143 ; -C 185 ; WX 600 ; N quotedblbase ; B 65 -142 529 143 ; -C 186 ; WX 600 ; N quotedblright ; B 61 277 525 562 ; -C 187 ; WX 600 ; N guillemotright ; B 47 70 592 446 ; -C 188 ; WX 600 ; N ellipsis ; B 26 -15 574 116 ; -C 189 ; WX 600 ; N perthousand ; B -113 -15 713 616 ; -C 191 ; WX 600 ; N questiondown ; B 99 -146 502 449 ; -C 193 ; WX 600 ; N grave ; B 132 508 395 661 ; -C 194 ; WX 600 ; N acute ; B 205 508 468 661 ; -C 195 ; WX 600 ; N circumflex ; B 103 483 497 657 ; -C 196 ; WX 600 ; N tilde ; B 89 493 512 636 ; -C 197 ; WX 600 ; N macron ; B 88 505 512 585 ; -C 198 ; WX 600 ; N breve ; B 83 468 517 631 ; -C 199 ; WX 600 ; N dotaccent ; B 230 485 370 625 ; -C 200 ; WX 600 ; N dieresis ; B 128 485 472 625 ; -C 202 ; WX 600 ; N ring ; B 198 481 402 678 ; -C 203 ; WX 600 ; N cedilla ; B 205 -206 387 0 ; -C 205 ; WX 600 ; N hungarumlaut ; B 68 488 588 661 ; -C 206 ; WX 600 ; N ogonek ; B 169 -199 367 0 ; -C 207 ; WX 600 ; N caron ; B 103 493 497 667 ; -C 208 ; WX 600 ; N emdash ; B -10 203 610 313 ; -C 225 ; WX 600 ; N AE ; B -29 0 602 562 ; -C 227 ; WX 600 ; N ordfeminine ; B 147 196 453 580 ; -C 232 ; WX 600 ; N Lslash ; B 39 0 578 562 ; -C 233 ; WX 600 ; N Oslash ; B 22 -22 578 584 ; -C 234 ; WX 600 ; N OE ; B -25 0 595 562 ; -C 235 ; WX 600 ; N ordmasculine ; B 147 196 453 580 ; -C 241 ; WX 600 ; N ae ; B -4 -15 601 454 ; -C 245 ; WX 600 ; N dotlessi ; B 77 0 523 439 ; -C 248 ; WX 600 ; N lslash ; B 77 0 523 626 ; -C 249 ; WX 600 ; N oslash ; B 30 -24 570 463 ; -C 250 ; WX 600 ; N oe ; B -18 -15 611 454 ; -C 251 ; WX 600 ; N germandbls ; B 22 -15 596 626 ; -C -1 ; WX 600 ; N Odieresis ; B 22 -18 578 748 ; -C -1 ; WX 600 ; N logicalnot ; B 71 103 529 413 ; -C -1 ; WX 600 ; N minus ; B 71 203 529 313 ; -C -1 ; WX 600 ; N merge ; B 137 -15 464 487 ; -C -1 ; WX 600 ; N degree ; B 86 243 474 616 ; -C -1 ; WX 600 ; N dectab ; B 8 0 592 320 ; -C -1 ; WX 600 ; N ll ; B -12 0 600 626 ; -C -1 ; WX 600 ; N IJ ; B -8 -18 622 562 ; -C -1 ; WX 600 ; N Eacute ; B 25 0 560 784 ; -C -1 ; WX 600 ; N Ocircumflex ; B 22 -18 578 780 ; -C -1 ; WX 600 ; N ucircumflex ; B -1 -15 569 657 ; -C -1 ; WX 600 ; N left ; B 65 44 535 371 ; -C -1 ; WX 600 ; N threesuperior ; B 138 222 433 616 ; -C -1 ; WX 600 ; N up ; B 136 0 463 447 ; -C -1 ; WX 600 ; N multiply ; B 81 39 520 478 ; -C -1 ; WX 600 ; N Scaron ; B 47 -22 553 790 ; -C -1 ; WX 600 ; N tab ; B 19 0 581 562 ; -C -1 ; WX 600 ; N Ucircumflex ; B 4 -18 596 780 ; -C -1 ; WX 600 ; N divide ; B 71 16 529 500 ; -C -1 ; WX 600 ; N Acircumflex ; B -9 0 609 780 ; -C -1 ; WX 600 ; N eacute ; B 40 -15 563 661 ; -C -1 ; WX 600 ; N uacute ; B -1 -15 569 661 ; -C -1 ; WX 600 ; N Aacute ; B -9 0 609 784 ; -C -1 ; WX 600 ; N copyright ; B 0 -18 600 580 ; -C -1 ; WX 600 ; N twosuperior ; B 143 230 436 616 ; -C -1 ; WX 600 ; N Ecircumflex ; B 25 0 560 780 ; -C -1 ; WX 600 ; N ntilde ; B 18 0 592 636 ; -C -1 ; WX 600 ; N down ; B 137 -15 464 439 ; -C -1 ; WX 600 ; N center ; B 40 14 560 580 ; -C -1 ; WX 600 ; N onesuperior ; B 153 230 447 616 ; -C -1 ; WX 600 ; N ij ; B 6 -146 574 658 ; -C -1 ; WX 600 ; N edieresis ; B 40 -15 563 625 ; -C -1 ; WX 600 ; N graybox ; B 76 0 525 599 ; -C -1 ; WX 600 ; N odieresis ; B 30 -15 570 625 ; -C -1 ; WX 600 ; N Ograve ; B 22 -18 578 784 ; -C -1 ; WX 600 ; N threequarters ; B -47 -60 648 661 ; -C -1 ; WX 600 ; N plusminus ; B 71 24 529 515 ; -C -1 ; WX 600 ; N prescription ; B 24 -15 599 562 ; -C -1 ; WX 600 ; N eth ; B 58 -27 543 626 ; -C -1 ; WX 600 ; N largebullet ; B 248 229 352 333 ; -C -1 ; WX 600 ; N egrave ; B 40 -15 563 661 ; -C -1 ; WX 600 ; N ccedilla ; B 40 -206 545 459 ; -C -1 ; WX 600 ; N notegraphic ; B 77 -15 523 572 ; -C -1 ; WX 600 ; N Udieresis ; B 4 -18 596 748 ; -C -1 ; WX 600 ; N Gcaron ; B 22 -18 594 790 ; -C -1 ; WX 600 ; N arrowdown ; B 144 -15 456 608 ; -C -1 ; WX 600 ; N format ; B 5 -146 115 601 ; -C -1 ; WX 600 ; N Otilde ; B 22 -18 578 759 ; -C -1 ; WX 600 ; N Idieresis ; B 77 0 523 748 ; -C -1 ; WX 600 ; N adieresis ; B 35 -15 570 625 ; -C -1 ; WX 600 ; N ecircumflex ; B 40 -15 563 657 ; -C -1 ; WX 600 ; N Eth ; B 30 0 594 562 ; -C -1 ; WX 600 ; N onequarter ; B -56 -60 656 661 ; -C -1 ; WX 600 ; N LL ; B -45 0 645 562 ; -C -1 ; WX 600 ; N agrave ; B 35 -15 570 661 ; -C -1 ; WX 600 ; N Zcaron ; B 62 0 539 790 ; -C -1 ; WX 600 ; N Scedilla ; B 47 -206 553 582 ; -C -1 ; WX 600 ; N Idot ; B 77 0 523 748 ; -C -1 ; WX 600 ; N Iacute ; B 77 0 523 784 ; -C -1 ; WX 600 ; N indent ; B 65 45 535 372 ; -C -1 ; WX 600 ; N Ugrave ; B 4 -18 596 784 ; -C -1 ; WX 600 ; N scaron ; B 68 -17 535 667 ; -C -1 ; WX 600 ; N overscore ; B 0 579 600 629 ; -C -1 ; WX 600 ; N Aring ; B -9 0 609 801 ; -C -1 ; WX 600 ; N Ccedilla ; B 22 -206 560 580 ; -C -1 ; WX 600 ; N Igrave ; B 77 0 523 784 ; -C -1 ; WX 600 ; N brokenbar ; B 255 -175 345 675 ; -C -1 ; WX 600 ; N Oacute ; B 22 -18 578 784 ; -C -1 ; WX 600 ; N otilde ; B 30 -15 570 636 ; -C -1 ; WX 600 ; N Yacute ; B 12 0 589 784 ; -C -1 ; WX 600 ; N lira ; B 72 -28 558 611 ; -C -1 ; WX 600 ; N Icircumflex ; B 77 0 523 780 ; -C -1 ; WX 600 ; N Atilde ; B -9 0 609 759 ; -C -1 ; WX 600 ; N Uacute ; B 4 -18 596 784 ; -C -1 ; WX 600 ; N Ydieresis ; B 12 0 589 748 ; -C -1 ; WX 600 ; N ydieresis ; B -4 -142 601 625 ; -C -1 ; WX 600 ; N idieresis ; B 77 0 523 625 ; -C -1 ; WX 600 ; N Adieresis ; B -9 0 609 748 ; -C -1 ; WX 600 ; N mu ; B -1 -142 569 439 ; -C -1 ; WX 600 ; N trademark ; B -9 230 749 562 ; -C -1 ; WX 600 ; N oacute ; B 30 -15 570 661 ; -C -1 ; WX 600 ; N acircumflex ; B 35 -15 570 657 ; -C -1 ; WX 600 ; N Agrave ; B -9 0 609 784 ; -C -1 ; WX 600 ; N return ; B 19 0 581 562 ; -C -1 ; WX 600 ; N atilde ; B 35 -15 570 636 ; -C -1 ; WX 600 ; N square ; B 19 0 581 562 ; -C -1 ; WX 600 ; N registered ; B 0 -18 600 580 ; -C -1 ; WX 600 ; N stop ; B 19 0 581 562 ; -C -1 ; WX 600 ; N udieresis ; B -1 -15 569 625 ; -C -1 ; WX 600 ; N arrowup ; B 144 3 456 626 ; -C -1 ; WX 600 ; N igrave ; B 77 0 523 661 ; -C -1 ; WX 600 ; N Edieresis ; B 25 0 560 748 ; -C -1 ; WX 600 ; N zcaron ; B 81 0 520 667 ; -C -1 ; WX 600 ; N arrowboth ; B -24 143 624 455 ; -C -1 ; WX 600 ; N gcaron ; B 30 -146 580 667 ; -C -1 ; WX 600 ; N arrowleft ; B -24 143 634 455 ; -C -1 ; WX 600 ; N aacute ; B 35 -15 570 661 ; -C -1 ; WX 600 ; N ocircumflex ; B 30 -15 570 657 ; -C -1 ; WX 600 ; N scedilla ; B 68 -206 535 459 ; -C -1 ; WX 600 ; N ograve ; B 30 -15 570 661 ; -C -1 ; WX 600 ; N onehalf ; B -47 -60 648 661 ; -C -1 ; WX 600 ; N ugrave ; B -1 -15 569 661 ; -C -1 ; WX 600 ; N Ntilde ; B 8 -12 610 759 ; -C -1 ; WX 600 ; N iacute ; B 77 0 523 661 ; -C -1 ; WX 600 ; N arrowright ; B -34 143 624 455 ; -C -1 ; WX 600 ; N Thorn ; B 48 0 557 562 ; -C -1 ; WX 600 ; N Egrave ; B 25 0 560 784 ; -C -1 ; WX 600 ; N thorn ; B -14 -142 570 626 ; -C -1 ; WX 600 ; N aring ; B 35 -15 570 678 ; -C -1 ; WX 600 ; N yacute ; B -4 -142 601 661 ; -C -1 ; WX 600 ; N icircumflex ; B 63 0 523 657 ; -EndCharMetrics -StartComposites 58 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 30 123 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex -30 123 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis -20 123 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave -50 123 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring -10 123 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde -30 123 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 30 123 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 0 123 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 0 123 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 0 123 ; -CC Gcaron 2 ; PCC G 0 0 ; PCC caron 10 123 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 0 123 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 0 123 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 0 123 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 0 123 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 0 123 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 0 123 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 0 123 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 0 123 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 0 123 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 0 123 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 0 123 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 30 123 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 0 123 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 0 123 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave -30 123 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 30 123 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 0 123 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 0 123 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 0 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex -20 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis -10 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave -30 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 0 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 0 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 0 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 0 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 0 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 0 0 ; -CC gcaron 2 ; PCC g 0 0 ; PCC caron -40 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute 0 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -40 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -40 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave 0 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 0 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 0 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 0 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 0 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 0 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 0 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 0 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 0 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex -20 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis -20 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave -30 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 30 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 10 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 0 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pcrbo8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pcrbo8a.afm deleted file mode 100644 index 6e2c742..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pcrbo8a.afm +++ /dev/null @@ -1,344 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1989, 1990, 1991, Adobe Systems Incorporated. All rights reserved. -Comment Creation Date: Tue Sep 17 14:13:24 1991 -Comment UniqueID 36389 -Comment VMusage 10055 54684 -FontName Courier-BoldOblique -FullName Courier Bold Oblique -FamilyName Courier -Weight Bold -ItalicAngle -12 -IsFixedPitch true -FontBBox -56 -250 868 801 -UnderlinePosition -100 -UnderlineThickness 50 -Version 002.004 -Notice Copyright (c) 1989, 1990, 1991, Adobe Systems Incorporated. All rights reserved. -EncodingScheme AdobeStandardEncoding -CapHeight 562 -XHeight 439 -Ascender 626 -Descender -142 -StartCharMetrics 260 -C 32 ; WX 600 ; N space ; B 0 0 0 0 ; -C 33 ; WX 600 ; N exclam ; B 216 -15 495 572 ; -C 34 ; WX 600 ; N quotedbl ; B 212 277 584 562 ; -C 35 ; WX 600 ; N numbersign ; B 88 -45 640 651 ; -C 36 ; WX 600 ; N dollar ; B 87 -126 629 666 ; -C 37 ; WX 600 ; N percent ; B 102 -15 624 616 ; -C 38 ; WX 600 ; N ampersand ; B 62 -15 594 543 ; -C 39 ; WX 600 ; N quoteright ; B 230 277 542 562 ; -C 40 ; WX 600 ; N parenleft ; B 266 -102 592 616 ; -C 41 ; WX 600 ; N parenright ; B 117 -102 444 616 ; -C 42 ; WX 600 ; N asterisk ; B 179 219 597 601 ; -C 43 ; WX 600 ; N plus ; B 114 39 596 478 ; -C 44 ; WX 600 ; N comma ; B 99 -111 430 174 ; -C 45 ; WX 600 ; N hyphen ; B 143 203 567 313 ; -C 46 ; WX 600 ; N period ; B 207 -15 426 171 ; -C 47 ; WX 600 ; N slash ; B 91 -77 626 626 ; -C 48 ; WX 600 ; N zero ; B 136 -15 592 616 ; -C 49 ; WX 600 ; N one ; B 93 0 561 616 ; -C 50 ; WX 600 ; N two ; B 61 0 593 616 ; -C 51 ; WX 600 ; N three ; B 72 -15 571 616 ; -C 52 ; WX 600 ; N four ; B 82 0 558 616 ; -C 53 ; WX 600 ; N five ; B 77 -15 621 601 ; -C 54 ; WX 600 ; N six ; B 136 -15 652 616 ; -C 55 ; WX 600 ; N seven ; B 147 0 622 601 ; -C 56 ; WX 600 ; N eight ; B 115 -15 604 616 ; -C 57 ; WX 600 ; N nine ; B 76 -15 592 616 ; -C 58 ; WX 600 ; N colon ; B 206 -15 479 425 ; -C 59 ; WX 600 ; N semicolon ; B 99 -111 480 425 ; -C 60 ; WX 600 ; N less ; B 121 15 612 501 ; -C 61 ; WX 600 ; N equal ; B 96 118 614 398 ; -C 62 ; WX 600 ; N greater ; B 97 15 589 501 ; -C 63 ; WX 600 ; N question ; B 183 -14 591 580 ; -C 64 ; WX 600 ; N at ; B 66 -15 641 616 ; -C 65 ; WX 600 ; N A ; B -9 0 631 562 ; -C 66 ; WX 600 ; N B ; B 30 0 629 562 ; -C 67 ; WX 600 ; N C ; B 75 -18 674 580 ; -C 68 ; WX 600 ; N D ; B 30 0 664 562 ; -C 69 ; WX 600 ; N E ; B 25 0 669 562 ; -C 70 ; WX 600 ; N F ; B 39 0 683 562 ; -C 71 ; WX 600 ; N G ; B 75 -18 674 580 ; -C 72 ; WX 600 ; N H ; B 20 0 699 562 ; -C 73 ; WX 600 ; N I ; B 77 0 642 562 ; -C 74 ; WX 600 ; N J ; B 59 -18 720 562 ; -C 75 ; WX 600 ; N K ; B 21 0 691 562 ; -C 76 ; WX 600 ; N L ; B 39 0 635 562 ; -C 77 ; WX 600 ; N M ; B -2 0 721 562 ; -C 78 ; WX 600 ; N N ; B 8 -12 729 562 ; -C 79 ; WX 600 ; N O ; B 74 -18 645 580 ; -C 80 ; WX 600 ; N P ; B 48 0 642 562 ; -C 81 ; WX 600 ; N Q ; B 84 -138 636 580 ; -C 82 ; WX 600 ; N R ; B 24 0 617 562 ; -C 83 ; WX 600 ; N S ; B 54 -22 672 582 ; -C 84 ; WX 600 ; N T ; B 86 0 678 562 ; -C 85 ; WX 600 ; N U ; B 101 -18 715 562 ; -C 86 ; WX 600 ; N V ; B 84 0 732 562 ; -C 87 ; WX 600 ; N W ; B 84 0 737 562 ; -C 88 ; WX 600 ; N X ; B 12 0 689 562 ; -C 89 ; WX 600 ; N Y ; B 109 0 708 562 ; -C 90 ; WX 600 ; N Z ; B 62 0 636 562 ; -C 91 ; WX 600 ; N bracketleft ; B 223 -102 606 616 ; -C 92 ; WX 600 ; N backslash ; B 223 -77 496 626 ; -C 93 ; WX 600 ; N bracketright ; B 103 -102 486 616 ; -C 94 ; WX 600 ; N asciicircum ; B 171 250 555 616 ; -C 95 ; WX 600 ; N underscore ; B -27 -125 584 -75 ; -C 96 ; WX 600 ; N quoteleft ; B 297 277 487 562 ; -C 97 ; WX 600 ; N a ; B 62 -15 592 454 ; -C 98 ; WX 600 ; N b ; B 13 -15 636 626 ; -C 99 ; WX 600 ; N c ; B 81 -15 631 459 ; -C 100 ; WX 600 ; N d ; B 61 -15 644 626 ; -C 101 ; WX 600 ; N e ; B 81 -15 604 454 ; -C 102 ; WX 600 ; N f ; B 83 0 677 626 ; L i fi ; L l fl ; -C 103 ; WX 600 ; N g ; B 41 -146 673 454 ; -C 104 ; WX 600 ; N h ; B 18 0 614 626 ; -C 105 ; WX 600 ; N i ; B 77 0 545 658 ; -C 106 ; WX 600 ; N j ; B 37 -146 580 658 ; -C 107 ; WX 600 ; N k ; B 33 0 642 626 ; -C 108 ; WX 600 ; N l ; B 77 0 545 626 ; -C 109 ; WX 600 ; N m ; B -22 0 648 454 ; -C 110 ; WX 600 ; N n ; B 18 0 614 454 ; -C 111 ; WX 600 ; N o ; B 71 -15 622 454 ; -C 112 ; WX 600 ; N p ; B -31 -142 622 454 ; -C 113 ; WX 600 ; N q ; B 61 -142 684 454 ; -C 114 ; WX 600 ; N r ; B 47 0 654 454 ; -C 115 ; WX 600 ; N s ; B 67 -17 607 459 ; -C 116 ; WX 600 ; N t ; B 118 -15 566 562 ; -C 117 ; WX 600 ; N u ; B 70 -15 591 439 ; -C 118 ; WX 600 ; N v ; B 70 0 694 439 ; -C 119 ; WX 600 ; N w ; B 53 0 711 439 ; -C 120 ; WX 600 ; N x ; B 6 0 670 439 ; -C 121 ; WX 600 ; N y ; B -20 -142 694 439 ; -C 122 ; WX 600 ; N z ; B 81 0 613 439 ; -C 123 ; WX 600 ; N braceleft ; B 204 -102 595 616 ; -C 124 ; WX 600 ; N bar ; B 202 -250 504 750 ; -C 125 ; WX 600 ; N braceright ; B 114 -102 506 616 ; -C 126 ; WX 600 ; N asciitilde ; B 120 153 589 356 ; -C 161 ; WX 600 ; N exclamdown ; B 197 -146 477 449 ; -C 162 ; WX 600 ; N cent ; B 121 -49 604 614 ; -C 163 ; WX 600 ; N sterling ; B 107 -28 650 611 ; -C 164 ; WX 600 ; N fraction ; B 22 -60 707 661 ; -C 165 ; WX 600 ; N yen ; B 98 0 709 562 ; -C 166 ; WX 600 ; N florin ; B -56 -131 701 616 ; -C 167 ; WX 600 ; N section ; B 74 -70 619 580 ; -C 168 ; WX 600 ; N currency ; B 77 49 643 517 ; -C 169 ; WX 600 ; N quotesingle ; B 304 277 492 562 ; -C 170 ; WX 600 ; N quotedblleft ; B 190 277 594 562 ; -C 171 ; WX 600 ; N guillemotleft ; B 63 70 638 446 ; -C 172 ; WX 600 ; N guilsinglleft ; B 196 70 544 446 ; -C 173 ; WX 600 ; N guilsinglright ; B 166 70 514 446 ; -C 174 ; WX 600 ; N fi ; B 12 0 643 626 ; -C 175 ; WX 600 ; N fl ; B 12 0 643 626 ; -C 177 ; WX 600 ; N endash ; B 108 203 602 313 ; -C 178 ; WX 600 ; N dagger ; B 176 -70 586 580 ; -C 179 ; WX 600 ; N daggerdbl ; B 122 -70 586 580 ; -C 180 ; WX 600 ; N periodcentered ; B 249 165 461 351 ; -C 182 ; WX 600 ; N paragraph ; B 61 -70 699 580 ; -C 183 ; WX 600 ; N bullet ; B 197 132 523 430 ; -C 184 ; WX 600 ; N quotesinglbase ; B 145 -142 457 143 ; -C 185 ; WX 600 ; N quotedblbase ; B 35 -142 559 143 ; -C 186 ; WX 600 ; N quotedblright ; B 120 277 644 562 ; -C 187 ; WX 600 ; N guillemotright ; B 72 70 647 446 ; -C 188 ; WX 600 ; N ellipsis ; B 35 -15 586 116 ; -C 189 ; WX 600 ; N perthousand ; B -44 -15 742 616 ; -C 191 ; WX 600 ; N questiondown ; B 101 -146 509 449 ; -C 193 ; WX 600 ; N grave ; B 272 508 503 661 ; -C 194 ; WX 600 ; N acute ; B 313 508 608 661 ; -C 195 ; WX 600 ; N circumflex ; B 212 483 606 657 ; -C 196 ; WX 600 ; N tilde ; B 200 493 642 636 ; -C 197 ; WX 600 ; N macron ; B 195 505 636 585 ; -C 198 ; WX 600 ; N breve ; B 217 468 651 631 ; -C 199 ; WX 600 ; N dotaccent ; B 346 485 490 625 ; -C 200 ; WX 600 ; N dieresis ; B 244 485 592 625 ; -C 202 ; WX 600 ; N ring ; B 319 481 528 678 ; -C 203 ; WX 600 ; N cedilla ; B 169 -206 367 0 ; -C 205 ; WX 600 ; N hungarumlaut ; B 172 488 728 661 ; -C 206 ; WX 600 ; N ogonek ; B 144 -199 350 0 ; -C 207 ; WX 600 ; N caron ; B 238 493 632 667 ; -C 208 ; WX 600 ; N emdash ; B 33 203 677 313 ; -C 225 ; WX 600 ; N AE ; B -29 0 707 562 ; -C 227 ; WX 600 ; N ordfeminine ; B 189 196 526 580 ; -C 232 ; WX 600 ; N Lslash ; B 39 0 635 562 ; -C 233 ; WX 600 ; N Oslash ; B 48 -22 672 584 ; -C 234 ; WX 600 ; N OE ; B 26 0 700 562 ; -C 235 ; WX 600 ; N ordmasculine ; B 189 196 542 580 ; -C 241 ; WX 600 ; N ae ; B 21 -15 651 454 ; -C 245 ; WX 600 ; N dotlessi ; B 77 0 545 439 ; -C 248 ; WX 600 ; N lslash ; B 77 0 578 626 ; -C 249 ; WX 600 ; N oslash ; B 55 -24 637 463 ; -C 250 ; WX 600 ; N oe ; B 19 -15 661 454 ; -C 251 ; WX 600 ; N germandbls ; B 22 -15 628 626 ; -C -1 ; WX 600 ; N Odieresis ; B 74 -18 645 748 ; -C -1 ; WX 600 ; N logicalnot ; B 135 103 617 413 ; -C -1 ; WX 600 ; N minus ; B 114 203 596 313 ; -C -1 ; WX 600 ; N merge ; B 168 -15 533 487 ; -C -1 ; WX 600 ; N degree ; B 173 243 569 616 ; -C -1 ; WX 600 ; N dectab ; B 8 0 615 320 ; -C -1 ; WX 600 ; N ll ; B 1 0 653 626 ; -C -1 ; WX 600 ; N IJ ; B -8 -18 741 562 ; -C -1 ; WX 600 ; N Eacute ; B 25 0 669 784 ; -C -1 ; WX 600 ; N Ocircumflex ; B 74 -18 645 780 ; -C -1 ; WX 600 ; N ucircumflex ; B 70 -15 591 657 ; -C -1 ; WX 600 ; N left ; B 109 44 589 371 ; -C -1 ; WX 600 ; N threesuperior ; B 193 222 525 616 ; -C -1 ; WX 600 ; N up ; B 196 0 523 447 ; -C -1 ; WX 600 ; N multiply ; B 105 39 606 478 ; -C -1 ; WX 600 ; N Scaron ; B 54 -22 672 790 ; -C -1 ; WX 600 ; N tab ; B 19 0 641 562 ; -C -1 ; WX 600 ; N Ucircumflex ; B 101 -18 715 780 ; -C -1 ; WX 600 ; N divide ; B 114 16 596 500 ; -C -1 ; WX 600 ; N Acircumflex ; B -9 0 631 780 ; -C -1 ; WX 600 ; N eacute ; B 81 -15 608 661 ; -C -1 ; WX 600 ; N uacute ; B 70 -15 608 661 ; -C -1 ; WX 600 ; N Aacute ; B -9 0 665 784 ; -C -1 ; WX 600 ; N copyright ; B 53 -18 667 580 ; -C -1 ; WX 600 ; N twosuperior ; B 192 230 541 616 ; -C -1 ; WX 600 ; N Ecircumflex ; B 25 0 669 780 ; -C -1 ; WX 600 ; N ntilde ; B 18 0 642 636 ; -C -1 ; WX 600 ; N down ; B 168 -15 496 439 ; -C -1 ; WX 600 ; N center ; B 103 14 623 580 ; -C -1 ; WX 600 ; N onesuperior ; B 213 230 514 616 ; -C -1 ; WX 600 ; N ij ; B 6 -146 714 658 ; -C -1 ; WX 600 ; N edieresis ; B 81 -15 604 625 ; -C -1 ; WX 600 ; N graybox ; B 76 0 652 599 ; -C -1 ; WX 600 ; N odieresis ; B 71 -15 622 625 ; -C -1 ; WX 600 ; N Ograve ; B 74 -18 645 784 ; -C -1 ; WX 600 ; N threequarters ; B 8 -60 698 661 ; -C -1 ; WX 600 ; N plusminus ; B 76 24 614 515 ; -C -1 ; WX 600 ; N prescription ; B 24 -15 632 562 ; -C -1 ; WX 600 ; N eth ; B 93 -27 661 626 ; -C -1 ; WX 600 ; N largebullet ; B 307 229 413 333 ; -C -1 ; WX 600 ; N egrave ; B 81 -15 604 661 ; -C -1 ; WX 600 ; N ccedilla ; B 81 -206 631 459 ; -C -1 ; WX 600 ; N notegraphic ; B 91 -15 619 572 ; -C -1 ; WX 600 ; N Udieresis ; B 101 -18 715 748 ; -C -1 ; WX 600 ; N Gcaron ; B 75 -18 674 790 ; -C -1 ; WX 600 ; N arrowdown ; B 174 -15 486 608 ; -C -1 ; WX 600 ; N format ; B -26 -146 243 601 ; -C -1 ; WX 600 ; N Otilde ; B 74 -18 668 759 ; -C -1 ; WX 600 ; N Idieresis ; B 77 0 642 748 ; -C -1 ; WX 600 ; N adieresis ; B 62 -15 592 625 ; -C -1 ; WX 600 ; N ecircumflex ; B 81 -15 606 657 ; -C -1 ; WX 600 ; N Eth ; B 30 0 664 562 ; -C -1 ; WX 600 ; N onequarter ; B 14 -60 706 661 ; -C -1 ; WX 600 ; N LL ; B -45 0 694 562 ; -C -1 ; WX 600 ; N agrave ; B 62 -15 592 661 ; -C -1 ; WX 600 ; N Zcaron ; B 62 0 659 790 ; -C -1 ; WX 600 ; N Scedilla ; B 54 -206 672 582 ; -C -1 ; WX 600 ; N Idot ; B 77 0 642 748 ; -C -1 ; WX 600 ; N Iacute ; B 77 0 642 784 ; -C -1 ; WX 600 ; N indent ; B 99 45 579 372 ; -C -1 ; WX 600 ; N Ugrave ; B 101 -18 715 784 ; -C -1 ; WX 600 ; N scaron ; B 67 -17 632 667 ; -C -1 ; WX 600 ; N overscore ; B 123 579 734 629 ; -C -1 ; WX 600 ; N Aring ; B -9 0 631 801 ; -C -1 ; WX 600 ; N Ccedilla ; B 74 -206 674 580 ; -C -1 ; WX 600 ; N Igrave ; B 77 0 642 784 ; -C -1 ; WX 600 ; N brokenbar ; B 218 -175 488 675 ; -C -1 ; WX 600 ; N Oacute ; B 74 -18 645 784 ; -C -1 ; WX 600 ; N otilde ; B 71 -15 642 636 ; -C -1 ; WX 600 ; N Yacute ; B 109 0 708 784 ; -C -1 ; WX 600 ; N lira ; B 107 -28 650 611 ; -C -1 ; WX 600 ; N Icircumflex ; B 77 0 642 780 ; -C -1 ; WX 600 ; N Atilde ; B -9 0 638 759 ; -C -1 ; WX 600 ; N Uacute ; B 101 -18 715 784 ; -C -1 ; WX 600 ; N Ydieresis ; B 109 0 708 748 ; -C -1 ; WX 600 ; N ydieresis ; B -20 -142 694 625 ; -C -1 ; WX 600 ; N idieresis ; B 77 0 552 625 ; -C -1 ; WX 600 ; N Adieresis ; B -9 0 631 748 ; -C -1 ; WX 600 ; N mu ; B 50 -142 591 439 ; -C -1 ; WX 600 ; N trademark ; B 86 230 868 562 ; -C -1 ; WX 600 ; N oacute ; B 71 -15 622 661 ; -C -1 ; WX 600 ; N acircumflex ; B 62 -15 592 657 ; -C -1 ; WX 600 ; N Agrave ; B -9 0 631 784 ; -C -1 ; WX 600 ; N return ; B 79 0 700 562 ; -C -1 ; WX 600 ; N atilde ; B 62 -15 642 636 ; -C -1 ; WX 600 ; N square ; B 19 0 700 562 ; -C -1 ; WX 600 ; N registered ; B 53 -18 667 580 ; -C -1 ; WX 600 ; N stop ; B 19 0 700 562 ; -C -1 ; WX 600 ; N udieresis ; B 70 -15 591 625 ; -C -1 ; WX 600 ; N arrowup ; B 244 3 556 626 ; -C -1 ; WX 600 ; N igrave ; B 77 0 545 661 ; -C -1 ; WX 600 ; N Edieresis ; B 25 0 669 748 ; -C -1 ; WX 600 ; N zcaron ; B 81 0 632 667 ; -C -1 ; WX 600 ; N arrowboth ; B 40 143 688 455 ; -C -1 ; WX 600 ; N gcaron ; B 41 -146 673 667 ; -C -1 ; WX 600 ; N arrowleft ; B 40 143 708 455 ; -C -1 ; WX 600 ; N aacute ; B 62 -15 608 661 ; -C -1 ; WX 600 ; N ocircumflex ; B 71 -15 622 657 ; -C -1 ; WX 600 ; N scedilla ; B 67 -206 607 459 ; -C -1 ; WX 600 ; N ograve ; B 71 -15 622 661 ; -C -1 ; WX 600 ; N onehalf ; B 23 -60 715 661 ; -C -1 ; WX 600 ; N ugrave ; B 70 -15 591 661 ; -C -1 ; WX 600 ; N Ntilde ; B 8 -12 729 759 ; -C -1 ; WX 600 ; N iacute ; B 77 0 608 661 ; -C -1 ; WX 600 ; N arrowright ; B 20 143 688 455 ; -C -1 ; WX 600 ; N Thorn ; B 48 0 619 562 ; -C -1 ; WX 600 ; N Egrave ; B 25 0 669 784 ; -C -1 ; WX 600 ; N thorn ; B -31 -142 622 626 ; -C -1 ; WX 600 ; N aring ; B 62 -15 592 678 ; -C -1 ; WX 600 ; N yacute ; B -20 -142 694 661 ; -C -1 ; WX 600 ; N icircumflex ; B 77 0 566 657 ; -EndCharMetrics -StartComposites 58 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 56 123 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex -4 123 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 6 123 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave -24 123 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 16 123 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde -4 123 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 56 123 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 26 123 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 26 123 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 26 123 ; -CC Gcaron 2 ; PCC G 0 0 ; PCC caron 36 123 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 26 123 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 26 123 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 26 123 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 26 123 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 26 123 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 26 123 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 26 123 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 26 123 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 26 123 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 26 123 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 26 123 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 56 123 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 26 123 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 26 123 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave -4 123 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 56 123 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 26 123 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 26 123 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 0 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex -20 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis -10 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave -30 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 0 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 0 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 0 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 0 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 0 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 0 0 ; -CC gcaron 2 ; PCC g 0 0 ; PCC caron -40 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute 0 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -40 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -40 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave 0 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 0 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 0 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 0 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 0 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 0 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 0 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 0 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 0 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex -20 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis -20 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave -30 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 30 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 10 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 0 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pcrr8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pcrr8a.afm deleted file mode 100644 index f60ec94..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pcrr8a.afm +++ /dev/null @@ -1,344 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1989, 1990, 1991 Adobe Systems Incorporated. All rights reserved. -Comment Creation Date: Tue Sep 17 07:47:21 1991 -Comment UniqueID 36347 -Comment VMusage 31037 39405 -FontName Courier -FullName Courier -FamilyName Courier -Weight Medium -ItalicAngle 0 -IsFixedPitch true -FontBBox -28 -250 628 805 -UnderlinePosition -100 -UnderlineThickness 50 -Version 002.004 -Notice Copyright (c) 1989, 1990, 1991 Adobe Systems Incorporated. All rights reserved. -EncodingScheme AdobeStandardEncoding -CapHeight 562 -XHeight 426 -Ascender 629 -Descender -157 -StartCharMetrics 260 -C 32 ; WX 600 ; N space ; B 0 0 0 0 ; -C 33 ; WX 600 ; N exclam ; B 236 -15 364 572 ; -C 34 ; WX 600 ; N quotedbl ; B 187 328 413 562 ; -C 35 ; WX 600 ; N numbersign ; B 93 -32 507 639 ; -C 36 ; WX 600 ; N dollar ; B 105 -126 496 662 ; -C 37 ; WX 600 ; N percent ; B 81 -15 518 622 ; -C 38 ; WX 600 ; N ampersand ; B 63 -15 538 543 ; -C 39 ; WX 600 ; N quoteright ; B 213 328 376 562 ; -C 40 ; WX 600 ; N parenleft ; B 269 -108 440 622 ; -C 41 ; WX 600 ; N parenright ; B 160 -108 331 622 ; -C 42 ; WX 600 ; N asterisk ; B 116 257 484 607 ; -C 43 ; WX 600 ; N plus ; B 80 44 520 470 ; -C 44 ; WX 600 ; N comma ; B 181 -112 344 122 ; -C 45 ; WX 600 ; N hyphen ; B 103 231 497 285 ; -C 46 ; WX 600 ; N period ; B 229 -15 371 109 ; -C 47 ; WX 600 ; N slash ; B 125 -80 475 629 ; -C 48 ; WX 600 ; N zero ; B 106 -15 494 622 ; -C 49 ; WX 600 ; N one ; B 96 0 505 622 ; -C 50 ; WX 600 ; N two ; B 70 0 471 622 ; -C 51 ; WX 600 ; N three ; B 75 -15 466 622 ; -C 52 ; WX 600 ; N four ; B 78 0 500 622 ; -C 53 ; WX 600 ; N five ; B 92 -15 497 607 ; -C 54 ; WX 600 ; N six ; B 111 -15 497 622 ; -C 55 ; WX 600 ; N seven ; B 82 0 483 607 ; -C 56 ; WX 600 ; N eight ; B 102 -15 498 622 ; -C 57 ; WX 600 ; N nine ; B 96 -15 489 622 ; -C 58 ; WX 600 ; N colon ; B 229 -15 371 385 ; -C 59 ; WX 600 ; N semicolon ; B 181 -112 371 385 ; -C 60 ; WX 600 ; N less ; B 41 42 519 472 ; -C 61 ; WX 600 ; N equal ; B 80 138 520 376 ; -C 62 ; WX 600 ; N greater ; B 66 42 544 472 ; -C 63 ; WX 600 ; N question ; B 129 -15 492 572 ; -C 64 ; WX 600 ; N at ; B 77 -15 533 622 ; -C 65 ; WX 600 ; N A ; B 3 0 597 562 ; -C 66 ; WX 600 ; N B ; B 43 0 559 562 ; -C 67 ; WX 600 ; N C ; B 41 -18 540 580 ; -C 68 ; WX 600 ; N D ; B 43 0 574 562 ; -C 69 ; WX 600 ; N E ; B 53 0 550 562 ; -C 70 ; WX 600 ; N F ; B 53 0 545 562 ; -C 71 ; WX 600 ; N G ; B 31 -18 575 580 ; -C 72 ; WX 600 ; N H ; B 32 0 568 562 ; -C 73 ; WX 600 ; N I ; B 96 0 504 562 ; -C 74 ; WX 600 ; N J ; B 34 -18 566 562 ; -C 75 ; WX 600 ; N K ; B 38 0 582 562 ; -C 76 ; WX 600 ; N L ; B 47 0 554 562 ; -C 77 ; WX 600 ; N M ; B 4 0 596 562 ; -C 78 ; WX 600 ; N N ; B 7 -13 593 562 ; -C 79 ; WX 600 ; N O ; B 43 -18 557 580 ; -C 80 ; WX 600 ; N P ; B 79 0 558 562 ; -C 81 ; WX 600 ; N Q ; B 43 -138 557 580 ; -C 82 ; WX 600 ; N R ; B 38 0 588 562 ; -C 83 ; WX 600 ; N S ; B 72 -20 529 580 ; -C 84 ; WX 600 ; N T ; B 38 0 563 562 ; -C 85 ; WX 600 ; N U ; B 17 -18 583 562 ; -C 86 ; WX 600 ; N V ; B -4 -13 604 562 ; -C 87 ; WX 600 ; N W ; B -3 -13 603 562 ; -C 88 ; WX 600 ; N X ; B 23 0 577 562 ; -C 89 ; WX 600 ; N Y ; B 24 0 576 562 ; -C 90 ; WX 600 ; N Z ; B 86 0 514 562 ; -C 91 ; WX 600 ; N bracketleft ; B 269 -108 442 622 ; -C 92 ; WX 600 ; N backslash ; B 118 -80 482 629 ; -C 93 ; WX 600 ; N bracketright ; B 158 -108 331 622 ; -C 94 ; WX 600 ; N asciicircum ; B 94 354 506 622 ; -C 95 ; WX 600 ; N underscore ; B 0 -125 600 -75 ; -C 96 ; WX 600 ; N quoteleft ; B 224 328 387 562 ; -C 97 ; WX 600 ; N a ; B 53 -15 559 441 ; -C 98 ; WX 600 ; N b ; B 14 -15 575 629 ; -C 99 ; WX 600 ; N c ; B 66 -15 529 441 ; -C 100 ; WX 600 ; N d ; B 45 -15 591 629 ; -C 101 ; WX 600 ; N e ; B 66 -15 548 441 ; -C 102 ; WX 600 ; N f ; B 114 0 531 629 ; L i fi ; L l fl ; -C 103 ; WX 600 ; N g ; B 45 -157 566 441 ; -C 104 ; WX 600 ; N h ; B 18 0 582 629 ; -C 105 ; WX 600 ; N i ; B 95 0 505 657 ; -C 106 ; WX 600 ; N j ; B 82 -157 410 657 ; -C 107 ; WX 600 ; N k ; B 43 0 580 629 ; -C 108 ; WX 600 ; N l ; B 95 0 505 629 ; -C 109 ; WX 600 ; N m ; B -5 0 605 441 ; -C 110 ; WX 600 ; N n ; B 26 0 575 441 ; -C 111 ; WX 600 ; N o ; B 62 -15 538 441 ; -C 112 ; WX 600 ; N p ; B 9 -157 555 441 ; -C 113 ; WX 600 ; N q ; B 45 -157 591 441 ; -C 114 ; WX 600 ; N r ; B 60 0 559 441 ; -C 115 ; WX 600 ; N s ; B 80 -15 513 441 ; -C 116 ; WX 600 ; N t ; B 87 -15 530 561 ; -C 117 ; WX 600 ; N u ; B 21 -15 562 426 ; -C 118 ; WX 600 ; N v ; B 10 -10 590 426 ; -C 119 ; WX 600 ; N w ; B -4 -10 604 426 ; -C 120 ; WX 600 ; N x ; B 20 0 580 426 ; -C 121 ; WX 600 ; N y ; B 7 -157 592 426 ; -C 122 ; WX 600 ; N z ; B 99 0 502 426 ; -C 123 ; WX 600 ; N braceleft ; B 182 -108 437 622 ; -C 124 ; WX 600 ; N bar ; B 275 -250 326 750 ; -C 125 ; WX 600 ; N braceright ; B 163 -108 418 622 ; -C 126 ; WX 600 ; N asciitilde ; B 63 197 540 320 ; -C 161 ; WX 600 ; N exclamdown ; B 236 -157 364 430 ; -C 162 ; WX 600 ; N cent ; B 96 -49 500 614 ; -C 163 ; WX 600 ; N sterling ; B 84 -21 521 611 ; -C 164 ; WX 600 ; N fraction ; B 92 -57 509 665 ; -C 165 ; WX 600 ; N yen ; B 26 0 574 562 ; -C 166 ; WX 600 ; N florin ; B 4 -143 539 622 ; -C 167 ; WX 600 ; N section ; B 113 -78 488 580 ; -C 168 ; WX 600 ; N currency ; B 73 58 527 506 ; -C 169 ; WX 600 ; N quotesingle ; B 259 328 341 562 ; -C 170 ; WX 600 ; N quotedblleft ; B 143 328 471 562 ; -C 171 ; WX 600 ; N guillemotleft ; B 37 70 563 446 ; -C 172 ; WX 600 ; N guilsinglleft ; B 149 70 451 446 ; -C 173 ; WX 600 ; N guilsinglright ; B 149 70 451 446 ; -C 174 ; WX 600 ; N fi ; B 3 0 597 629 ; -C 175 ; WX 600 ; N fl ; B 3 0 597 629 ; -C 177 ; WX 600 ; N endash ; B 75 231 525 285 ; -C 178 ; WX 600 ; N dagger ; B 141 -78 459 580 ; -C 179 ; WX 600 ; N daggerdbl ; B 141 -78 459 580 ; -C 180 ; WX 600 ; N periodcentered ; B 222 189 378 327 ; -C 182 ; WX 600 ; N paragraph ; B 50 -78 511 562 ; -C 183 ; WX 600 ; N bullet ; B 172 130 428 383 ; -C 184 ; WX 600 ; N quotesinglbase ; B 213 -134 376 100 ; -C 185 ; WX 600 ; N quotedblbase ; B 143 -134 457 100 ; -C 186 ; WX 600 ; N quotedblright ; B 143 328 457 562 ; -C 187 ; WX 600 ; N guillemotright ; B 37 70 563 446 ; -C 188 ; WX 600 ; N ellipsis ; B 37 -15 563 111 ; -C 189 ; WX 600 ; N perthousand ; B 3 -15 600 622 ; -C 191 ; WX 600 ; N questiondown ; B 108 -157 471 430 ; -C 193 ; WX 600 ; N grave ; B 151 497 378 672 ; -C 194 ; WX 600 ; N acute ; B 242 497 469 672 ; -C 195 ; WX 600 ; N circumflex ; B 124 477 476 654 ; -C 196 ; WX 600 ; N tilde ; B 105 489 503 606 ; -C 197 ; WX 600 ; N macron ; B 120 525 480 565 ; -C 198 ; WX 600 ; N breve ; B 153 501 447 609 ; -C 199 ; WX 600 ; N dotaccent ; B 249 477 352 580 ; -C 200 ; WX 600 ; N dieresis ; B 148 492 453 595 ; -C 202 ; WX 600 ; N ring ; B 218 463 382 627 ; -C 203 ; WX 600 ; N cedilla ; B 224 -151 362 10 ; -C 205 ; WX 600 ; N hungarumlaut ; B 133 497 540 672 ; -C 206 ; WX 600 ; N ogonek ; B 227 -151 370 0 ; -C 207 ; WX 600 ; N caron ; B 124 492 476 669 ; -C 208 ; WX 600 ; N emdash ; B 0 231 600 285 ; -C 225 ; WX 600 ; N AE ; B 3 0 550 562 ; -C 227 ; WX 600 ; N ordfeminine ; B 156 249 442 580 ; -C 232 ; WX 600 ; N Lslash ; B 47 0 554 562 ; -C 233 ; WX 600 ; N Oslash ; B 43 -80 557 629 ; -C 234 ; WX 600 ; N OE ; B 7 0 567 562 ; -C 235 ; WX 600 ; N ordmasculine ; B 157 249 443 580 ; -C 241 ; WX 600 ; N ae ; B 19 -15 570 441 ; -C 245 ; WX 600 ; N dotlessi ; B 95 0 505 426 ; -C 248 ; WX 600 ; N lslash ; B 95 0 505 629 ; -C 249 ; WX 600 ; N oslash ; B 62 -80 538 506 ; -C 250 ; WX 600 ; N oe ; B 19 -15 559 441 ; -C 251 ; WX 600 ; N germandbls ; B 48 -15 588 629 ; -C -1 ; WX 600 ; N Odieresis ; B 43 -18 557 731 ; -C -1 ; WX 600 ; N logicalnot ; B 87 108 513 369 ; -C -1 ; WX 600 ; N minus ; B 80 232 520 283 ; -C -1 ; WX 600 ; N merge ; B 160 -15 440 436 ; -C -1 ; WX 600 ; N degree ; B 123 269 477 622 ; -C -1 ; WX 600 ; N dectab ; B 18 0 582 227 ; -C -1 ; WX 600 ; N ll ; B 18 0 567 629 ; -C -1 ; WX 600 ; N IJ ; B 32 -18 583 562 ; -C -1 ; WX 600 ; N Eacute ; B 53 0 550 793 ; -C -1 ; WX 600 ; N Ocircumflex ; B 43 -18 557 775 ; -C -1 ; WX 600 ; N ucircumflex ; B 21 -15 562 654 ; -C -1 ; WX 600 ; N left ; B 70 68 530 348 ; -C -1 ; WX 600 ; N threesuperior ; B 155 240 406 622 ; -C -1 ; WX 600 ; N up ; B 160 0 440 437 ; -C -1 ; WX 600 ; N multiply ; B 87 43 515 470 ; -C -1 ; WX 600 ; N Scaron ; B 72 -20 529 805 ; -C -1 ; WX 600 ; N tab ; B 19 0 581 562 ; -C -1 ; WX 600 ; N Ucircumflex ; B 17 -18 583 775 ; -C -1 ; WX 600 ; N divide ; B 87 48 513 467 ; -C -1 ; WX 600 ; N Acircumflex ; B 3 0 597 775 ; -C -1 ; WX 600 ; N eacute ; B 66 -15 548 672 ; -C -1 ; WX 600 ; N uacute ; B 21 -15 562 672 ; -C -1 ; WX 600 ; N Aacute ; B 3 0 597 793 ; -C -1 ; WX 600 ; N copyright ; B 0 -18 600 580 ; -C -1 ; WX 600 ; N twosuperior ; B 177 249 424 622 ; -C -1 ; WX 600 ; N Ecircumflex ; B 53 0 550 775 ; -C -1 ; WX 600 ; N ntilde ; B 26 0 575 606 ; -C -1 ; WX 600 ; N down ; B 160 -15 440 426 ; -C -1 ; WX 600 ; N center ; B 40 14 560 580 ; -C -1 ; WX 600 ; N onesuperior ; B 172 249 428 622 ; -C -1 ; WX 600 ; N ij ; B 37 -157 490 657 ; -C -1 ; WX 600 ; N edieresis ; B 66 -15 548 595 ; -C -1 ; WX 600 ; N graybox ; B 76 0 525 599 ; -C -1 ; WX 600 ; N odieresis ; B 62 -15 538 595 ; -C -1 ; WX 600 ; N Ograve ; B 43 -18 557 793 ; -C -1 ; WX 600 ; N threequarters ; B 8 -56 593 666 ; -C -1 ; WX 600 ; N plusminus ; B 87 44 513 558 ; -C -1 ; WX 600 ; N prescription ; B 27 -15 577 562 ; -C -1 ; WX 600 ; N eth ; B 62 -15 538 629 ; -C -1 ; WX 600 ; N largebullet ; B 261 220 339 297 ; -C -1 ; WX 600 ; N egrave ; B 66 -15 548 672 ; -C -1 ; WX 600 ; N ccedilla ; B 66 -151 529 441 ; -C -1 ; WX 600 ; N notegraphic ; B 136 -15 464 572 ; -C -1 ; WX 600 ; N Udieresis ; B 17 -18 583 731 ; -C -1 ; WX 600 ; N Gcaron ; B 31 -18 575 805 ; -C -1 ; WX 600 ; N arrowdown ; B 116 -15 484 608 ; -C -1 ; WX 600 ; N format ; B 5 -157 56 607 ; -C -1 ; WX 600 ; N Otilde ; B 43 -18 557 732 ; -C -1 ; WX 600 ; N Idieresis ; B 96 0 504 731 ; -C -1 ; WX 600 ; N adieresis ; B 53 -15 559 595 ; -C -1 ; WX 600 ; N ecircumflex ; B 66 -15 548 654 ; -C -1 ; WX 600 ; N Eth ; B 30 0 574 562 ; -C -1 ; WX 600 ; N onequarter ; B 0 -57 600 665 ; -C -1 ; WX 600 ; N LL ; B 8 0 592 562 ; -C -1 ; WX 600 ; N agrave ; B 53 -15 559 672 ; -C -1 ; WX 600 ; N Zcaron ; B 86 0 514 805 ; -C -1 ; WX 600 ; N Scedilla ; B 72 -151 529 580 ; -C -1 ; WX 600 ; N Idot ; B 96 0 504 716 ; -C -1 ; WX 600 ; N Iacute ; B 96 0 504 793 ; -C -1 ; WX 600 ; N indent ; B 70 68 530 348 ; -C -1 ; WX 600 ; N Ugrave ; B 17 -18 583 793 ; -C -1 ; WX 600 ; N scaron ; B 80 -15 513 669 ; -C -1 ; WX 600 ; N overscore ; B 0 579 600 629 ; -C -1 ; WX 600 ; N Aring ; B 3 0 597 753 ; -C -1 ; WX 600 ; N Ccedilla ; B 41 -151 540 580 ; -C -1 ; WX 600 ; N Igrave ; B 96 0 504 793 ; -C -1 ; WX 600 ; N brokenbar ; B 275 -175 326 675 ; -C -1 ; WX 600 ; N Oacute ; B 43 -18 557 793 ; -C -1 ; WX 600 ; N otilde ; B 62 -15 538 606 ; -C -1 ; WX 600 ; N Yacute ; B 24 0 576 793 ; -C -1 ; WX 600 ; N lira ; B 73 -21 521 611 ; -C -1 ; WX 600 ; N Icircumflex ; B 96 0 504 775 ; -C -1 ; WX 600 ; N Atilde ; B 3 0 597 732 ; -C -1 ; WX 600 ; N Uacute ; B 17 -18 583 793 ; -C -1 ; WX 600 ; N Ydieresis ; B 24 0 576 731 ; -C -1 ; WX 600 ; N ydieresis ; B 7 -157 592 595 ; -C -1 ; WX 600 ; N idieresis ; B 95 0 505 595 ; -C -1 ; WX 600 ; N Adieresis ; B 3 0 597 731 ; -C -1 ; WX 600 ; N mu ; B 21 -157 562 426 ; -C -1 ; WX 600 ; N trademark ; B -23 263 623 562 ; -C -1 ; WX 600 ; N oacute ; B 62 -15 538 672 ; -C -1 ; WX 600 ; N acircumflex ; B 53 -15 559 654 ; -C -1 ; WX 600 ; N Agrave ; B 3 0 597 793 ; -C -1 ; WX 600 ; N return ; B 19 0 581 562 ; -C -1 ; WX 600 ; N atilde ; B 53 -15 559 606 ; -C -1 ; WX 600 ; N square ; B 19 0 581 562 ; -C -1 ; WX 600 ; N registered ; B 0 -18 600 580 ; -C -1 ; WX 600 ; N stop ; B 19 0 581 562 ; -C -1 ; WX 600 ; N udieresis ; B 21 -15 562 595 ; -C -1 ; WX 600 ; N arrowup ; B 116 0 484 623 ; -C -1 ; WX 600 ; N igrave ; B 95 0 505 672 ; -C -1 ; WX 600 ; N Edieresis ; B 53 0 550 731 ; -C -1 ; WX 600 ; N zcaron ; B 99 0 502 669 ; -C -1 ; WX 600 ; N arrowboth ; B -28 115 628 483 ; -C -1 ; WX 600 ; N gcaron ; B 45 -157 566 669 ; -C -1 ; WX 600 ; N arrowleft ; B -24 115 624 483 ; -C -1 ; WX 600 ; N aacute ; B 53 -15 559 672 ; -C -1 ; WX 600 ; N ocircumflex ; B 62 -15 538 654 ; -C -1 ; WX 600 ; N scedilla ; B 80 -151 513 441 ; -C -1 ; WX 600 ; N ograve ; B 62 -15 538 672 ; -C -1 ; WX 600 ; N onehalf ; B 0 -57 611 665 ; -C -1 ; WX 600 ; N ugrave ; B 21 -15 562 672 ; -C -1 ; WX 600 ; N Ntilde ; B 7 -13 593 732 ; -C -1 ; WX 600 ; N iacute ; B 95 0 505 672 ; -C -1 ; WX 600 ; N arrowright ; B -24 115 624 483 ; -C -1 ; WX 600 ; N Thorn ; B 79 0 538 562 ; -C -1 ; WX 600 ; N Egrave ; B 53 0 550 793 ; -C -1 ; WX 600 ; N thorn ; B -6 -157 555 629 ; -C -1 ; WX 600 ; N aring ; B 53 -15 559 627 ; -C -1 ; WX 600 ; N yacute ; B 7 -157 592 672 ; -C -1 ; WX 600 ; N icircumflex ; B 94 0 505 654 ; -EndCharMetrics -StartComposites 58 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 20 121 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex -30 121 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis -30 136 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave -30 121 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring -15 126 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 0 126 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 30 121 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 0 121 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 0 136 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 0 121 ; -CC Gcaron 2 ; PCC G 0 0 ; PCC caron 0 136 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 0 121 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 0 121 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 0 136 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 0 121 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 0 126 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 0 121 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 0 121 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 0 136 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 0 121 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 0 126 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 30 136 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 30 121 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 0 121 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 0 136 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave -30 121 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 30 121 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 0 136 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 0 136 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 0 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 0 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 0 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 0 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 0 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 0 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 0 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 0 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 0 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 0 0 ; -CC gcaron 2 ; PCC g 0 0 ; PCC caron -30 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute 0 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -30 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -30 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -30 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 0 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 0 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 0 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 0 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 0 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 0 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 0 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute -10 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex -10 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 0 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave -30 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute -20 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis -10 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 10 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pcrro8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pcrro8a.afm deleted file mode 100644 index b053a4c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pcrro8a.afm +++ /dev/null @@ -1,344 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1989, 1990, 1991 Adobe Systems Incorporated. All rights reserved. -Comment Creation Date: Tue Sep 17 09:42:19 1991 -Comment UniqueID 36350 -Comment VMusage 9174 52297 -FontName Courier-Oblique -FullName Courier Oblique -FamilyName Courier -Weight Medium -ItalicAngle -12 -IsFixedPitch true -FontBBox -28 -250 742 805 -UnderlinePosition -100 -UnderlineThickness 50 -Version 002.004 -Notice Copyright (c) 1989, 1990, 1991 Adobe Systems Incorporated. All rights reserved. -EncodingScheme AdobeStandardEncoding -CapHeight 562 -XHeight 426 -Ascender 629 -Descender -157 -StartCharMetrics 260 -C 32 ; WX 600 ; N space ; B 0 0 0 0 ; -C 33 ; WX 600 ; N exclam ; B 243 -15 464 572 ; -C 34 ; WX 600 ; N quotedbl ; B 273 328 532 562 ; -C 35 ; WX 600 ; N numbersign ; B 133 -32 596 639 ; -C 36 ; WX 600 ; N dollar ; B 108 -126 596 662 ; -C 37 ; WX 600 ; N percent ; B 134 -15 599 622 ; -C 38 ; WX 600 ; N ampersand ; B 87 -15 580 543 ; -C 39 ; WX 600 ; N quoteright ; B 283 328 495 562 ; -C 40 ; WX 600 ; N parenleft ; B 313 -108 572 622 ; -C 41 ; WX 600 ; N parenright ; B 137 -108 396 622 ; -C 42 ; WX 600 ; N asterisk ; B 212 257 580 607 ; -C 43 ; WX 600 ; N plus ; B 129 44 580 470 ; -C 44 ; WX 600 ; N comma ; B 157 -112 370 122 ; -C 45 ; WX 600 ; N hyphen ; B 152 231 558 285 ; -C 46 ; WX 600 ; N period ; B 238 -15 382 109 ; -C 47 ; WX 600 ; N slash ; B 112 -80 604 629 ; -C 48 ; WX 600 ; N zero ; B 154 -15 575 622 ; -C 49 ; WX 600 ; N one ; B 98 0 515 622 ; -C 50 ; WX 600 ; N two ; B 70 0 568 622 ; -C 51 ; WX 600 ; N three ; B 82 -15 538 622 ; -C 52 ; WX 600 ; N four ; B 108 0 541 622 ; -C 53 ; WX 600 ; N five ; B 99 -15 589 607 ; -C 54 ; WX 600 ; N six ; B 155 -15 629 622 ; -C 55 ; WX 600 ; N seven ; B 182 0 612 607 ; -C 56 ; WX 600 ; N eight ; B 132 -15 588 622 ; -C 57 ; WX 600 ; N nine ; B 93 -15 574 622 ; -C 58 ; WX 600 ; N colon ; B 238 -15 441 385 ; -C 59 ; WX 600 ; N semicolon ; B 157 -112 441 385 ; -C 60 ; WX 600 ; N less ; B 96 42 610 472 ; -C 61 ; WX 600 ; N equal ; B 109 138 600 376 ; -C 62 ; WX 600 ; N greater ; B 85 42 599 472 ; -C 63 ; WX 600 ; N question ; B 222 -15 583 572 ; -C 64 ; WX 600 ; N at ; B 127 -15 582 622 ; -C 65 ; WX 600 ; N A ; B 3 0 607 562 ; -C 66 ; WX 600 ; N B ; B 43 0 616 562 ; -C 67 ; WX 600 ; N C ; B 93 -18 655 580 ; -C 68 ; WX 600 ; N D ; B 43 0 645 562 ; -C 69 ; WX 600 ; N E ; B 53 0 660 562 ; -C 70 ; WX 600 ; N F ; B 53 0 660 562 ; -C 71 ; WX 600 ; N G ; B 83 -18 645 580 ; -C 72 ; WX 600 ; N H ; B 32 0 687 562 ; -C 73 ; WX 600 ; N I ; B 96 0 623 562 ; -C 74 ; WX 600 ; N J ; B 52 -18 685 562 ; -C 75 ; WX 600 ; N K ; B 38 0 671 562 ; -C 76 ; WX 600 ; N L ; B 47 0 607 562 ; -C 77 ; WX 600 ; N M ; B 4 0 715 562 ; -C 78 ; WX 600 ; N N ; B 7 -13 712 562 ; -C 79 ; WX 600 ; N O ; B 94 -18 625 580 ; -C 80 ; WX 600 ; N P ; B 79 0 644 562 ; -C 81 ; WX 600 ; N Q ; B 95 -138 625 580 ; -C 82 ; WX 600 ; N R ; B 38 0 598 562 ; -C 83 ; WX 600 ; N S ; B 76 -20 650 580 ; -C 84 ; WX 600 ; N T ; B 108 0 665 562 ; -C 85 ; WX 600 ; N U ; B 125 -18 702 562 ; -C 86 ; WX 600 ; N V ; B 105 -13 723 562 ; -C 87 ; WX 600 ; N W ; B 106 -13 722 562 ; -C 88 ; WX 600 ; N X ; B 23 0 675 562 ; -C 89 ; WX 600 ; N Y ; B 133 0 695 562 ; -C 90 ; WX 600 ; N Z ; B 86 0 610 562 ; -C 91 ; WX 600 ; N bracketleft ; B 246 -108 574 622 ; -C 92 ; WX 600 ; N backslash ; B 249 -80 468 629 ; -C 93 ; WX 600 ; N bracketright ; B 135 -108 463 622 ; -C 94 ; WX 600 ; N asciicircum ; B 175 354 587 622 ; -C 95 ; WX 600 ; N underscore ; B -27 -125 584 -75 ; -C 96 ; WX 600 ; N quoteleft ; B 343 328 457 562 ; -C 97 ; WX 600 ; N a ; B 76 -15 569 441 ; -C 98 ; WX 600 ; N b ; B 29 -15 625 629 ; -C 99 ; WX 600 ; N c ; B 106 -15 608 441 ; -C 100 ; WX 600 ; N d ; B 85 -15 640 629 ; -C 101 ; WX 600 ; N e ; B 106 -15 598 441 ; -C 102 ; WX 600 ; N f ; B 114 0 662 629 ; L i fi ; L l fl ; -C 103 ; WX 600 ; N g ; B 61 -157 657 441 ; -C 104 ; WX 600 ; N h ; B 33 0 592 629 ; -C 105 ; WX 600 ; N i ; B 95 0 515 657 ; -C 106 ; WX 600 ; N j ; B 52 -157 550 657 ; -C 107 ; WX 600 ; N k ; B 58 0 633 629 ; -C 108 ; WX 600 ; N l ; B 95 0 515 629 ; -C 109 ; WX 600 ; N m ; B -5 0 615 441 ; -C 110 ; WX 600 ; N n ; B 26 0 585 441 ; -C 111 ; WX 600 ; N o ; B 102 -15 588 441 ; -C 112 ; WX 600 ; N p ; B -24 -157 605 441 ; -C 113 ; WX 600 ; N q ; B 85 -157 682 441 ; -C 114 ; WX 600 ; N r ; B 60 0 636 441 ; -C 115 ; WX 600 ; N s ; B 78 -15 584 441 ; -C 116 ; WX 600 ; N t ; B 167 -15 561 561 ; -C 117 ; WX 600 ; N u ; B 101 -15 572 426 ; -C 118 ; WX 600 ; N v ; B 90 -10 681 426 ; -C 119 ; WX 600 ; N w ; B 76 -10 695 426 ; -C 120 ; WX 600 ; N x ; B 20 0 655 426 ; -C 121 ; WX 600 ; N y ; B -4 -157 683 426 ; -C 122 ; WX 600 ; N z ; B 99 0 593 426 ; -C 123 ; WX 600 ; N braceleft ; B 233 -108 569 622 ; -C 124 ; WX 600 ; N bar ; B 222 -250 485 750 ; -C 125 ; WX 600 ; N braceright ; B 140 -108 477 622 ; -C 126 ; WX 600 ; N asciitilde ; B 116 197 600 320 ; -C 161 ; WX 600 ; N exclamdown ; B 225 -157 445 430 ; -C 162 ; WX 600 ; N cent ; B 151 -49 588 614 ; -C 163 ; WX 600 ; N sterling ; B 124 -21 621 611 ; -C 164 ; WX 600 ; N fraction ; B 84 -57 646 665 ; -C 165 ; WX 600 ; N yen ; B 120 0 693 562 ; -C 166 ; WX 600 ; N florin ; B -26 -143 671 622 ; -C 167 ; WX 600 ; N section ; B 104 -78 590 580 ; -C 168 ; WX 600 ; N currency ; B 94 58 628 506 ; -C 169 ; WX 600 ; N quotesingle ; B 345 328 460 562 ; -C 170 ; WX 600 ; N quotedblleft ; B 262 328 541 562 ; -C 171 ; WX 600 ; N guillemotleft ; B 92 70 652 446 ; -C 172 ; WX 600 ; N guilsinglleft ; B 204 70 540 446 ; -C 173 ; WX 600 ; N guilsinglright ; B 170 70 506 446 ; -C 174 ; WX 600 ; N fi ; B 3 0 619 629 ; -C 175 ; WX 600 ; N fl ; B 3 0 619 629 ; -C 177 ; WX 600 ; N endash ; B 124 231 586 285 ; -C 178 ; WX 600 ; N dagger ; B 217 -78 546 580 ; -C 179 ; WX 600 ; N daggerdbl ; B 163 -78 546 580 ; -C 180 ; WX 600 ; N periodcentered ; B 275 189 434 327 ; -C 182 ; WX 600 ; N paragraph ; B 100 -78 630 562 ; -C 183 ; WX 600 ; N bullet ; B 224 130 485 383 ; -C 184 ; WX 600 ; N quotesinglbase ; B 185 -134 397 100 ; -C 185 ; WX 600 ; N quotedblbase ; B 115 -134 478 100 ; -C 186 ; WX 600 ; N quotedblright ; B 213 328 576 562 ; -C 187 ; WX 600 ; N guillemotright ; B 58 70 618 446 ; -C 188 ; WX 600 ; N ellipsis ; B 46 -15 575 111 ; -C 189 ; WX 600 ; N perthousand ; B 59 -15 627 622 ; -C 191 ; WX 600 ; N questiondown ; B 105 -157 466 430 ; -C 193 ; WX 600 ; N grave ; B 294 497 484 672 ; -C 194 ; WX 600 ; N acute ; B 348 497 612 672 ; -C 195 ; WX 600 ; N circumflex ; B 229 477 581 654 ; -C 196 ; WX 600 ; N tilde ; B 212 489 629 606 ; -C 197 ; WX 600 ; N macron ; B 232 525 600 565 ; -C 198 ; WX 600 ; N breve ; B 279 501 576 609 ; -C 199 ; WX 600 ; N dotaccent ; B 360 477 466 580 ; -C 200 ; WX 600 ; N dieresis ; B 262 492 570 595 ; -C 202 ; WX 600 ; N ring ; B 332 463 500 627 ; -C 203 ; WX 600 ; N cedilla ; B 197 -151 344 10 ; -C 205 ; WX 600 ; N hungarumlaut ; B 239 497 683 672 ; -C 206 ; WX 600 ; N ogonek ; B 207 -151 348 0 ; -C 207 ; WX 600 ; N caron ; B 262 492 614 669 ; -C 208 ; WX 600 ; N emdash ; B 49 231 661 285 ; -C 225 ; WX 600 ; N AE ; B 3 0 655 562 ; -C 227 ; WX 600 ; N ordfeminine ; B 209 249 512 580 ; -C 232 ; WX 600 ; N Lslash ; B 47 0 607 562 ; -C 233 ; WX 600 ; N Oslash ; B 94 -80 625 629 ; -C 234 ; WX 600 ; N OE ; B 59 0 672 562 ; -C 235 ; WX 600 ; N ordmasculine ; B 210 249 535 580 ; -C 241 ; WX 600 ; N ae ; B 41 -15 626 441 ; -C 245 ; WX 600 ; N dotlessi ; B 95 0 515 426 ; -C 248 ; WX 600 ; N lslash ; B 95 0 583 629 ; -C 249 ; WX 600 ; N oslash ; B 102 -80 588 506 ; -C 250 ; WX 600 ; N oe ; B 54 -15 615 441 ; -C 251 ; WX 600 ; N germandbls ; B 48 -15 617 629 ; -C -1 ; WX 600 ; N Odieresis ; B 94 -18 625 731 ; -C -1 ; WX 600 ; N logicalnot ; B 155 108 591 369 ; -C -1 ; WX 600 ; N minus ; B 129 232 580 283 ; -C -1 ; WX 600 ; N merge ; B 187 -15 503 436 ; -C -1 ; WX 600 ; N degree ; B 214 269 576 622 ; -C -1 ; WX 600 ; N dectab ; B 18 0 593 227 ; -C -1 ; WX 600 ; N ll ; B 33 0 616 629 ; -C -1 ; WX 600 ; N IJ ; B 32 -18 702 562 ; -C -1 ; WX 600 ; N Eacute ; B 53 0 668 793 ; -C -1 ; WX 600 ; N Ocircumflex ; B 94 -18 625 775 ; -C -1 ; WX 600 ; N ucircumflex ; B 101 -15 572 654 ; -C -1 ; WX 600 ; N left ; B 114 68 580 348 ; -C -1 ; WX 600 ; N threesuperior ; B 213 240 501 622 ; -C -1 ; WX 600 ; N up ; B 223 0 503 437 ; -C -1 ; WX 600 ; N multiply ; B 103 43 607 470 ; -C -1 ; WX 600 ; N Scaron ; B 76 -20 673 805 ; -C -1 ; WX 600 ; N tab ; B 19 0 641 562 ; -C -1 ; WX 600 ; N Ucircumflex ; B 125 -18 702 775 ; -C -1 ; WX 600 ; N divide ; B 136 48 573 467 ; -C -1 ; WX 600 ; N Acircumflex ; B 3 0 607 775 ; -C -1 ; WX 600 ; N eacute ; B 106 -15 612 672 ; -C -1 ; WX 600 ; N uacute ; B 101 -15 602 672 ; -C -1 ; WX 600 ; N Aacute ; B 3 0 658 793 ; -C -1 ; WX 600 ; N copyright ; B 53 -18 667 580 ; -C -1 ; WX 600 ; N twosuperior ; B 230 249 535 622 ; -C -1 ; WX 600 ; N Ecircumflex ; B 53 0 660 775 ; -C -1 ; WX 600 ; N ntilde ; B 26 0 629 606 ; -C -1 ; WX 600 ; N down ; B 187 -15 467 426 ; -C -1 ; WX 600 ; N center ; B 103 14 623 580 ; -C -1 ; WX 600 ; N onesuperior ; B 231 249 491 622 ; -C -1 ; WX 600 ; N ij ; B 37 -157 630 657 ; -C -1 ; WX 600 ; N edieresis ; B 106 -15 598 595 ; -C -1 ; WX 600 ; N graybox ; B 76 0 652 599 ; -C -1 ; WX 600 ; N odieresis ; B 102 -15 588 595 ; -C -1 ; WX 600 ; N Ograve ; B 94 -18 625 793 ; -C -1 ; WX 600 ; N threequarters ; B 73 -56 659 666 ; -C -1 ; WX 600 ; N plusminus ; B 96 44 594 558 ; -C -1 ; WX 600 ; N prescription ; B 27 -15 617 562 ; -C -1 ; WX 600 ; N eth ; B 102 -15 639 629 ; -C -1 ; WX 600 ; N largebullet ; B 315 220 395 297 ; -C -1 ; WX 600 ; N egrave ; B 106 -15 598 672 ; -C -1 ; WX 600 ; N ccedilla ; B 106 -151 614 441 ; -C -1 ; WX 600 ; N notegraphic ; B 143 -15 564 572 ; -C -1 ; WX 600 ; N Udieresis ; B 125 -18 702 731 ; -C -1 ; WX 600 ; N Gcaron ; B 83 -18 645 805 ; -C -1 ; WX 600 ; N arrowdown ; B 152 -15 520 608 ; -C -1 ; WX 600 ; N format ; B -28 -157 185 607 ; -C -1 ; WX 600 ; N Otilde ; B 94 -18 656 732 ; -C -1 ; WX 600 ; N Idieresis ; B 96 0 623 731 ; -C -1 ; WX 600 ; N adieresis ; B 76 -15 570 595 ; -C -1 ; WX 600 ; N ecircumflex ; B 106 -15 598 654 ; -C -1 ; WX 600 ; N Eth ; B 43 0 645 562 ; -C -1 ; WX 600 ; N onequarter ; B 65 -57 674 665 ; -C -1 ; WX 600 ; N LL ; B 8 0 647 562 ; -C -1 ; WX 600 ; N agrave ; B 76 -15 569 672 ; -C -1 ; WX 600 ; N Zcaron ; B 86 0 643 805 ; -C -1 ; WX 600 ; N Scedilla ; B 76 -151 650 580 ; -C -1 ; WX 600 ; N Idot ; B 96 0 623 716 ; -C -1 ; WX 600 ; N Iacute ; B 96 0 638 793 ; -C -1 ; WX 600 ; N indent ; B 108 68 574 348 ; -C -1 ; WX 600 ; N Ugrave ; B 125 -18 702 793 ; -C -1 ; WX 600 ; N scaron ; B 78 -15 614 669 ; -C -1 ; WX 600 ; N overscore ; B 123 579 734 629 ; -C -1 ; WX 600 ; N Aring ; B 3 0 607 753 ; -C -1 ; WX 600 ; N Ccedilla ; B 93 -151 658 580 ; -C -1 ; WX 600 ; N Igrave ; B 96 0 623 793 ; -C -1 ; WX 600 ; N brokenbar ; B 238 -175 469 675 ; -C -1 ; WX 600 ; N Oacute ; B 94 -18 638 793 ; -C -1 ; WX 600 ; N otilde ; B 102 -15 629 606 ; -C -1 ; WX 600 ; N Yacute ; B 133 0 695 793 ; -C -1 ; WX 600 ; N lira ; B 118 -21 621 611 ; -C -1 ; WX 600 ; N Icircumflex ; B 96 0 623 775 ; -C -1 ; WX 600 ; N Atilde ; B 3 0 656 732 ; -C -1 ; WX 600 ; N Uacute ; B 125 -18 702 793 ; -C -1 ; WX 600 ; N Ydieresis ; B 133 0 695 731 ; -C -1 ; WX 600 ; N ydieresis ; B -4 -157 683 595 ; -C -1 ; WX 600 ; N idieresis ; B 95 0 540 595 ; -C -1 ; WX 600 ; N Adieresis ; B 3 0 607 731 ; -C -1 ; WX 600 ; N mu ; B 72 -157 572 426 ; -C -1 ; WX 600 ; N trademark ; B 75 263 742 562 ; -C -1 ; WX 600 ; N oacute ; B 102 -15 612 672 ; -C -1 ; WX 600 ; N acircumflex ; B 76 -15 581 654 ; -C -1 ; WX 600 ; N Agrave ; B 3 0 607 793 ; -C -1 ; WX 600 ; N return ; B 79 0 700 562 ; -C -1 ; WX 600 ; N atilde ; B 76 -15 629 606 ; -C -1 ; WX 600 ; N square ; B 19 0 700 562 ; -C -1 ; WX 600 ; N registered ; B 53 -18 667 580 ; -C -1 ; WX 600 ; N stop ; B 19 0 700 562 ; -C -1 ; WX 600 ; N udieresis ; B 101 -15 572 595 ; -C -1 ; WX 600 ; N arrowup ; B 209 0 577 623 ; -C -1 ; WX 600 ; N igrave ; B 95 0 515 672 ; -C -1 ; WX 600 ; N Edieresis ; B 53 0 660 731 ; -C -1 ; WX 600 ; N zcaron ; B 99 0 624 669 ; -C -1 ; WX 600 ; N arrowboth ; B 36 115 692 483 ; -C -1 ; WX 600 ; N gcaron ; B 61 -157 657 669 ; -C -1 ; WX 600 ; N arrowleft ; B 40 115 693 483 ; -C -1 ; WX 600 ; N aacute ; B 76 -15 612 672 ; -C -1 ; WX 600 ; N ocircumflex ; B 102 -15 588 654 ; -C -1 ; WX 600 ; N scedilla ; B 78 -151 584 441 ; -C -1 ; WX 600 ; N ograve ; B 102 -15 588 672 ; -C -1 ; WX 600 ; N onehalf ; B 65 -57 669 665 ; -C -1 ; WX 600 ; N ugrave ; B 101 -15 572 672 ; -C -1 ; WX 600 ; N Ntilde ; B 7 -13 712 732 ; -C -1 ; WX 600 ; N iacute ; B 95 0 612 672 ; -C -1 ; WX 600 ; N arrowright ; B 34 115 688 483 ; -C -1 ; WX 600 ; N Thorn ; B 79 0 606 562 ; -C -1 ; WX 600 ; N Egrave ; B 53 0 660 793 ; -C -1 ; WX 600 ; N thorn ; B -24 -157 605 629 ; -C -1 ; WX 600 ; N aring ; B 76 -15 569 627 ; -C -1 ; WX 600 ; N yacute ; B -4 -157 683 672 ; -C -1 ; WX 600 ; N icircumflex ; B 95 0 551 654 ; -EndCharMetrics -StartComposites 58 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 46 121 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex -4 121 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis -1 136 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave -4 121 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 12 126 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 27 126 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 56 121 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 26 121 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 29 136 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 26 121 ; -CC Gcaron 2 ; PCC G 0 0 ; PCC caron 29 136 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 26 121 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 26 121 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 29 136 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 26 121 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 27 126 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 26 121 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 26 121 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 29 136 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 26 121 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 27 126 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 59 136 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 56 121 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 26 121 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 29 136 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave -4 121 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 56 121 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 29 136 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 29 136 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 0 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 0 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 0 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 0 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 0 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 0 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 0 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 0 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 0 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 0 0 ; -CC gcaron 2 ; PCC g 0 0 ; PCC caron -30 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute 0 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -30 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -30 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -30 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 0 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 0 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 0 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 0 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 0 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 0 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 0 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute -10 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex -10 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 0 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave -30 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute -20 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis -10 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 10 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvb8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvb8a.afm deleted file mode 100644 index a1e1b33..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvb8a.afm +++ /dev/null @@ -1,570 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Thu Mar 15 09:43:00 1990 -Comment UniqueID 28357 -Comment VMusage 26878 33770 -FontName Helvetica-Bold -FullName Helvetica Bold -FamilyName Helvetica -Weight Bold -ItalicAngle 0 -IsFixedPitch false -FontBBox -170 -228 1003 962 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.007 -Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved.Helvetica is a trademark of Linotype AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 718 -XHeight 532 -Ascender 718 -Descender -207 -StartCharMetrics 228 -C 32 ; WX 278 ; N space ; B 0 0 0 0 ; -C 33 ; WX 333 ; N exclam ; B 90 0 244 718 ; -C 34 ; WX 474 ; N quotedbl ; B 98 447 376 718 ; -C 35 ; WX 556 ; N numbersign ; B 18 0 538 698 ; -C 36 ; WX 556 ; N dollar ; B 30 -115 523 775 ; -C 37 ; WX 889 ; N percent ; B 28 -19 861 710 ; -C 38 ; WX 722 ; N ampersand ; B 54 -19 701 718 ; -C 39 ; WX 278 ; N quoteright ; B 69 445 209 718 ; -C 40 ; WX 333 ; N parenleft ; B 35 -208 314 734 ; -C 41 ; WX 333 ; N parenright ; B 19 -208 298 734 ; -C 42 ; WX 389 ; N asterisk ; B 27 387 362 718 ; -C 43 ; WX 584 ; N plus ; B 40 0 544 506 ; -C 44 ; WX 278 ; N comma ; B 64 -168 214 146 ; -C 45 ; WX 333 ; N hyphen ; B 27 215 306 345 ; -C 46 ; WX 278 ; N period ; B 64 0 214 146 ; -C 47 ; WX 278 ; N slash ; B -33 -19 311 737 ; -C 48 ; WX 556 ; N zero ; B 32 -19 524 710 ; -C 49 ; WX 556 ; N one ; B 69 0 378 710 ; -C 50 ; WX 556 ; N two ; B 26 0 511 710 ; -C 51 ; WX 556 ; N three ; B 27 -19 516 710 ; -C 52 ; WX 556 ; N four ; B 27 0 526 710 ; -C 53 ; WX 556 ; N five ; B 27 -19 516 698 ; -C 54 ; WX 556 ; N six ; B 31 -19 520 710 ; -C 55 ; WX 556 ; N seven ; B 25 0 528 698 ; -C 56 ; WX 556 ; N eight ; B 32 -19 524 710 ; -C 57 ; WX 556 ; N nine ; B 30 -19 522 710 ; -C 58 ; WX 333 ; N colon ; B 92 0 242 512 ; -C 59 ; WX 333 ; N semicolon ; B 92 -168 242 512 ; -C 60 ; WX 584 ; N less ; B 38 -8 546 514 ; -C 61 ; WX 584 ; N equal ; B 40 87 544 419 ; -C 62 ; WX 584 ; N greater ; B 38 -8 546 514 ; -C 63 ; WX 611 ; N question ; B 60 0 556 727 ; -C 64 ; WX 975 ; N at ; B 118 -19 856 737 ; -C 65 ; WX 722 ; N A ; B 20 0 702 718 ; -C 66 ; WX 722 ; N B ; B 76 0 669 718 ; -C 67 ; WX 722 ; N C ; B 44 -19 684 737 ; -C 68 ; WX 722 ; N D ; B 76 0 685 718 ; -C 69 ; WX 667 ; N E ; B 76 0 621 718 ; -C 70 ; WX 611 ; N F ; B 76 0 587 718 ; -C 71 ; WX 778 ; N G ; B 44 -19 713 737 ; -C 72 ; WX 722 ; N H ; B 71 0 651 718 ; -C 73 ; WX 278 ; N I ; B 64 0 214 718 ; -C 74 ; WX 556 ; N J ; B 22 -18 484 718 ; -C 75 ; WX 722 ; N K ; B 87 0 722 718 ; -C 76 ; WX 611 ; N L ; B 76 0 583 718 ; -C 77 ; WX 833 ; N M ; B 69 0 765 718 ; -C 78 ; WX 722 ; N N ; B 69 0 654 718 ; -C 79 ; WX 778 ; N O ; B 44 -19 734 737 ; -C 80 ; WX 667 ; N P ; B 76 0 627 718 ; -C 81 ; WX 778 ; N Q ; B 44 -52 737 737 ; -C 82 ; WX 722 ; N R ; B 76 0 677 718 ; -C 83 ; WX 667 ; N S ; B 39 -19 629 737 ; -C 84 ; WX 611 ; N T ; B 14 0 598 718 ; -C 85 ; WX 722 ; N U ; B 72 -19 651 718 ; -C 86 ; WX 667 ; N V ; B 19 0 648 718 ; -C 87 ; WX 944 ; N W ; B 16 0 929 718 ; -C 88 ; WX 667 ; N X ; B 14 0 653 718 ; -C 89 ; WX 667 ; N Y ; B 15 0 653 718 ; -C 90 ; WX 611 ; N Z ; B 25 0 586 718 ; -C 91 ; WX 333 ; N bracketleft ; B 63 -196 309 722 ; -C 92 ; WX 278 ; N backslash ; B -33 -19 311 737 ; -C 93 ; WX 333 ; N bracketright ; B 24 -196 270 722 ; -C 94 ; WX 584 ; N asciicircum ; B 62 323 522 698 ; -C 95 ; WX 556 ; N underscore ; B 0 -125 556 -75 ; -C 96 ; WX 278 ; N quoteleft ; B 69 454 209 727 ; -C 97 ; WX 556 ; N a ; B 29 -14 527 546 ; -C 98 ; WX 611 ; N b ; B 61 -14 578 718 ; -C 99 ; WX 556 ; N c ; B 34 -14 524 546 ; -C 100 ; WX 611 ; N d ; B 34 -14 551 718 ; -C 101 ; WX 556 ; N e ; B 23 -14 528 546 ; -C 102 ; WX 333 ; N f ; B 10 0 318 727 ; L i fi ; L l fl ; -C 103 ; WX 611 ; N g ; B 40 -217 553 546 ; -C 104 ; WX 611 ; N h ; B 65 0 546 718 ; -C 105 ; WX 278 ; N i ; B 69 0 209 725 ; -C 106 ; WX 278 ; N j ; B 3 -214 209 725 ; -C 107 ; WX 556 ; N k ; B 69 0 562 718 ; -C 108 ; WX 278 ; N l ; B 69 0 209 718 ; -C 109 ; WX 889 ; N m ; B 64 0 826 546 ; -C 110 ; WX 611 ; N n ; B 65 0 546 546 ; -C 111 ; WX 611 ; N o ; B 34 -14 578 546 ; -C 112 ; WX 611 ; N p ; B 62 -207 578 546 ; -C 113 ; WX 611 ; N q ; B 34 -207 552 546 ; -C 114 ; WX 389 ; N r ; B 64 0 373 546 ; -C 115 ; WX 556 ; N s ; B 30 -14 519 546 ; -C 116 ; WX 333 ; N t ; B 10 -6 309 676 ; -C 117 ; WX 611 ; N u ; B 66 -14 545 532 ; -C 118 ; WX 556 ; N v ; B 13 0 543 532 ; -C 119 ; WX 778 ; N w ; B 10 0 769 532 ; -C 120 ; WX 556 ; N x ; B 15 0 541 532 ; -C 121 ; WX 556 ; N y ; B 10 -214 539 532 ; -C 122 ; WX 500 ; N z ; B 20 0 480 532 ; -C 123 ; WX 389 ; N braceleft ; B 48 -196 365 722 ; -C 124 ; WX 280 ; N bar ; B 84 -19 196 737 ; -C 125 ; WX 389 ; N braceright ; B 24 -196 341 722 ; -C 126 ; WX 584 ; N asciitilde ; B 61 163 523 343 ; -C 161 ; WX 333 ; N exclamdown ; B 90 -186 244 532 ; -C 162 ; WX 556 ; N cent ; B 34 -118 524 628 ; -C 163 ; WX 556 ; N sterling ; B 28 -16 541 718 ; -C 164 ; WX 167 ; N fraction ; B -170 -19 336 710 ; -C 165 ; WX 556 ; N yen ; B -9 0 565 698 ; -C 166 ; WX 556 ; N florin ; B -10 -210 516 737 ; -C 167 ; WX 556 ; N section ; B 34 -184 522 727 ; -C 168 ; WX 556 ; N currency ; B -3 76 559 636 ; -C 169 ; WX 238 ; N quotesingle ; B 70 447 168 718 ; -C 170 ; WX 500 ; N quotedblleft ; B 64 454 436 727 ; -C 171 ; WX 556 ; N guillemotleft ; B 88 76 468 484 ; -C 172 ; WX 333 ; N guilsinglleft ; B 83 76 250 484 ; -C 173 ; WX 333 ; N guilsinglright ; B 83 76 250 484 ; -C 174 ; WX 611 ; N fi ; B 10 0 542 727 ; -C 175 ; WX 611 ; N fl ; B 10 0 542 727 ; -C 177 ; WX 556 ; N endash ; B 0 227 556 333 ; -C 178 ; WX 556 ; N dagger ; B 36 -171 520 718 ; -C 179 ; WX 556 ; N daggerdbl ; B 36 -171 520 718 ; -C 180 ; WX 278 ; N periodcentered ; B 58 172 220 334 ; -C 182 ; WX 556 ; N paragraph ; B -8 -191 539 700 ; -C 183 ; WX 350 ; N bullet ; B 10 194 340 524 ; -C 184 ; WX 278 ; N quotesinglbase ; B 69 -146 209 127 ; -C 185 ; WX 500 ; N quotedblbase ; B 64 -146 436 127 ; -C 186 ; WX 500 ; N quotedblright ; B 64 445 436 718 ; -C 187 ; WX 556 ; N guillemotright ; B 88 76 468 484 ; -C 188 ; WX 1000 ; N ellipsis ; B 92 0 908 146 ; -C 189 ; WX 1000 ; N perthousand ; B -3 -19 1003 710 ; -C 191 ; WX 611 ; N questiondown ; B 55 -195 551 532 ; -C 193 ; WX 333 ; N grave ; B -23 604 225 750 ; -C 194 ; WX 333 ; N acute ; B 108 604 356 750 ; -C 195 ; WX 333 ; N circumflex ; B -10 604 343 750 ; -C 196 ; WX 333 ; N tilde ; B -17 610 350 737 ; -C 197 ; WX 333 ; N macron ; B -6 604 339 678 ; -C 198 ; WX 333 ; N breve ; B -2 604 335 750 ; -C 199 ; WX 333 ; N dotaccent ; B 104 614 230 729 ; -C 200 ; WX 333 ; N dieresis ; B 6 614 327 729 ; -C 202 ; WX 333 ; N ring ; B 59 568 275 776 ; -C 203 ; WX 333 ; N cedilla ; B 6 -228 245 0 ; -C 205 ; WX 333 ; N hungarumlaut ; B 9 604 486 750 ; -C 206 ; WX 333 ; N ogonek ; B 71 -228 304 0 ; -C 207 ; WX 333 ; N caron ; B -10 604 343 750 ; -C 208 ; WX 1000 ; N emdash ; B 0 227 1000 333 ; -C 225 ; WX 1000 ; N AE ; B 5 0 954 718 ; -C 227 ; WX 370 ; N ordfeminine ; B 22 276 347 737 ; -C 232 ; WX 611 ; N Lslash ; B -20 0 583 718 ; -C 233 ; WX 778 ; N Oslash ; B 33 -27 744 745 ; -C 234 ; WX 1000 ; N OE ; B 37 -19 961 737 ; -C 235 ; WX 365 ; N ordmasculine ; B 6 276 360 737 ; -C 241 ; WX 889 ; N ae ; B 29 -14 858 546 ; -C 245 ; WX 278 ; N dotlessi ; B 69 0 209 532 ; -C 248 ; WX 278 ; N lslash ; B -18 0 296 718 ; -C 249 ; WX 611 ; N oslash ; B 22 -29 589 560 ; -C 250 ; WX 944 ; N oe ; B 34 -14 912 546 ; -C 251 ; WX 611 ; N germandbls ; B 69 -14 579 731 ; -C -1 ; WX 611 ; N Zcaron ; B 25 0 586 936 ; -C -1 ; WX 556 ; N ccedilla ; B 34 -228 524 546 ; -C -1 ; WX 556 ; N ydieresis ; B 10 -214 539 729 ; -C -1 ; WX 556 ; N atilde ; B 29 -14 527 737 ; -C -1 ; WX 278 ; N icircumflex ; B -37 0 316 750 ; -C -1 ; WX 333 ; N threesuperior ; B 8 271 326 710 ; -C -1 ; WX 556 ; N ecircumflex ; B 23 -14 528 750 ; -C -1 ; WX 611 ; N thorn ; B 62 -208 578 718 ; -C -1 ; WX 556 ; N egrave ; B 23 -14 528 750 ; -C -1 ; WX 333 ; N twosuperior ; B 9 283 324 710 ; -C -1 ; WX 556 ; N eacute ; B 23 -14 528 750 ; -C -1 ; WX 611 ; N otilde ; B 34 -14 578 737 ; -C -1 ; WX 722 ; N Aacute ; B 20 0 702 936 ; -C -1 ; WX 611 ; N ocircumflex ; B 34 -14 578 750 ; -C -1 ; WX 556 ; N yacute ; B 10 -214 539 750 ; -C -1 ; WX 611 ; N udieresis ; B 66 -14 545 729 ; -C -1 ; WX 834 ; N threequarters ; B 16 -19 799 710 ; -C -1 ; WX 556 ; N acircumflex ; B 29 -14 527 750 ; -C -1 ; WX 722 ; N Eth ; B -5 0 685 718 ; -C -1 ; WX 556 ; N edieresis ; B 23 -14 528 729 ; -C -1 ; WX 611 ; N ugrave ; B 66 -14 545 750 ; -C -1 ; WX 1000 ; N trademark ; B 44 306 956 718 ; -C -1 ; WX 611 ; N ograve ; B 34 -14 578 750 ; -C -1 ; WX 556 ; N scaron ; B 30 -14 519 750 ; -C -1 ; WX 278 ; N Idieresis ; B -21 0 300 915 ; -C -1 ; WX 611 ; N uacute ; B 66 -14 545 750 ; -C -1 ; WX 556 ; N agrave ; B 29 -14 527 750 ; -C -1 ; WX 611 ; N ntilde ; B 65 0 546 737 ; -C -1 ; WX 556 ; N aring ; B 29 -14 527 776 ; -C -1 ; WX 500 ; N zcaron ; B 20 0 480 750 ; -C -1 ; WX 278 ; N Icircumflex ; B -37 0 316 936 ; -C -1 ; WX 722 ; N Ntilde ; B 69 0 654 923 ; -C -1 ; WX 611 ; N ucircumflex ; B 66 -14 545 750 ; -C -1 ; WX 667 ; N Ecircumflex ; B 76 0 621 936 ; -C -1 ; WX 278 ; N Iacute ; B 64 0 329 936 ; -C -1 ; WX 722 ; N Ccedilla ; B 44 -228 684 737 ; -C -1 ; WX 778 ; N Odieresis ; B 44 -19 734 915 ; -C -1 ; WX 667 ; N Scaron ; B 39 -19 629 936 ; -C -1 ; WX 667 ; N Edieresis ; B 76 0 621 915 ; -C -1 ; WX 278 ; N Igrave ; B -50 0 214 936 ; -C -1 ; WX 556 ; N adieresis ; B 29 -14 527 729 ; -C -1 ; WX 778 ; N Ograve ; B 44 -19 734 936 ; -C -1 ; WX 667 ; N Egrave ; B 76 0 621 936 ; -C -1 ; WX 667 ; N Ydieresis ; B 15 0 653 915 ; -C -1 ; WX 737 ; N registered ; B -11 -19 748 737 ; -C -1 ; WX 778 ; N Otilde ; B 44 -19 734 923 ; -C -1 ; WX 834 ; N onequarter ; B 26 -19 766 710 ; -C -1 ; WX 722 ; N Ugrave ; B 72 -19 651 936 ; -C -1 ; WX 722 ; N Ucircumflex ; B 72 -19 651 936 ; -C -1 ; WX 667 ; N Thorn ; B 76 0 627 718 ; -C -1 ; WX 584 ; N divide ; B 40 -42 544 548 ; -C -1 ; WX 722 ; N Atilde ; B 20 0 702 923 ; -C -1 ; WX 722 ; N Uacute ; B 72 -19 651 936 ; -C -1 ; WX 778 ; N Ocircumflex ; B 44 -19 734 936 ; -C -1 ; WX 584 ; N logicalnot ; B 40 108 544 419 ; -C -1 ; WX 722 ; N Aring ; B 20 0 702 962 ; -C -1 ; WX 278 ; N idieresis ; B -21 0 300 729 ; -C -1 ; WX 278 ; N iacute ; B 69 0 329 750 ; -C -1 ; WX 556 ; N aacute ; B 29 -14 527 750 ; -C -1 ; WX 584 ; N plusminus ; B 40 0 544 506 ; -C -1 ; WX 584 ; N multiply ; B 40 1 545 505 ; -C -1 ; WX 722 ; N Udieresis ; B 72 -19 651 915 ; -C -1 ; WX 584 ; N minus ; B 40 197 544 309 ; -C -1 ; WX 333 ; N onesuperior ; B 26 283 237 710 ; -C -1 ; WX 667 ; N Eacute ; B 76 0 621 936 ; -C -1 ; WX 722 ; N Acircumflex ; B 20 0 702 936 ; -C -1 ; WX 737 ; N copyright ; B -11 -19 749 737 ; -C -1 ; WX 722 ; N Agrave ; B 20 0 702 936 ; -C -1 ; WX 611 ; N odieresis ; B 34 -14 578 729 ; -C -1 ; WX 611 ; N oacute ; B 34 -14 578 750 ; -C -1 ; WX 400 ; N degree ; B 57 426 343 712 ; -C -1 ; WX 278 ; N igrave ; B -50 0 209 750 ; -C -1 ; WX 611 ; N mu ; B 66 -207 545 532 ; -C -1 ; WX 778 ; N Oacute ; B 44 -19 734 936 ; -C -1 ; WX 611 ; N eth ; B 34 -14 578 737 ; -C -1 ; WX 722 ; N Adieresis ; B 20 0 702 915 ; -C -1 ; WX 667 ; N Yacute ; B 15 0 653 936 ; -C -1 ; WX 280 ; N brokenbar ; B 84 -19 196 737 ; -C -1 ; WX 834 ; N onehalf ; B 26 -19 794 710 ; -EndCharMetrics -StartKernData -StartKernPairs 209 - -KPX A y -30 -KPX A w -30 -KPX A v -40 -KPX A u -30 -KPX A Y -110 -KPX A W -60 -KPX A V -80 -KPX A U -50 -KPX A T -90 -KPX A Q -40 -KPX A O -40 -KPX A G -50 -KPX A C -40 - -KPX B U -10 -KPX B A -30 - -KPX D period -30 -KPX D comma -30 -KPX D Y -70 -KPX D W -40 -KPX D V -40 -KPX D A -40 - -KPX F period -100 -KPX F comma -100 -KPX F a -20 -KPX F A -80 - -KPX J u -20 -KPX J period -20 -KPX J comma -20 -KPX J A -20 - -KPX K y -40 -KPX K u -30 -KPX K o -35 -KPX K e -15 -KPX K O -30 - -KPX L y -30 -KPX L quoteright -140 -KPX L quotedblright -140 -KPX L Y -120 -KPX L W -80 -KPX L V -110 -KPX L T -90 - -KPX O period -40 -KPX O comma -40 -KPX O Y -70 -KPX O X -50 -KPX O W -50 -KPX O V -50 -KPX O T -40 -KPX O A -50 - -KPX P period -120 -KPX P o -40 -KPX P e -30 -KPX P comma -120 -KPX P a -30 -KPX P A -100 - -KPX Q period 20 -KPX Q comma 20 -KPX Q U -10 - -KPX R Y -50 -KPX R W -40 -KPX R V -50 -KPX R U -20 -KPX R T -20 -KPX R O -20 - -KPX T y -60 -KPX T w -60 -KPX T u -90 -KPX T semicolon -40 -KPX T r -80 -KPX T period -80 -KPX T o -80 -KPX T hyphen -120 -KPX T e -60 -KPX T comma -80 -KPX T colon -40 -KPX T a -80 -KPX T O -40 -KPX T A -90 - -KPX U period -30 -KPX U comma -30 -KPX U A -50 - -KPX V u -60 -KPX V semicolon -40 -KPX V period -120 -KPX V o -90 -KPX V hyphen -80 -KPX V e -50 -KPX V comma -120 -KPX V colon -40 -KPX V a -60 -KPX V O -50 -KPX V G -50 -KPX V A -80 - -KPX W y -20 -KPX W u -45 -KPX W semicolon -10 -KPX W period -80 -KPX W o -60 -KPX W hyphen -40 -KPX W e -35 -KPX W comma -80 -KPX W colon -10 -KPX W a -40 -KPX W O -20 -KPX W A -60 - -KPX Y u -100 -KPX Y semicolon -50 -KPX Y period -100 -KPX Y o -100 -KPX Y e -80 -KPX Y comma -100 -KPX Y colon -50 -KPX Y a -90 -KPX Y O -70 -KPX Y A -110 - -KPX a y -20 -KPX a w -15 -KPX a v -15 -KPX a g -10 - -KPX b y -20 -KPX b v -20 -KPX b u -20 -KPX b l -10 - -KPX c y -10 -KPX c l -20 -KPX c k -20 -KPX c h -10 - -KPX colon space -40 - -KPX comma space -40 -KPX comma quoteright -120 -KPX comma quotedblright -120 - -KPX d y -15 -KPX d w -15 -KPX d v -15 -KPX d d -10 - -KPX e y -15 -KPX e x -15 -KPX e w -15 -KPX e v -15 -KPX e period 20 -KPX e comma 10 - -KPX f quoteright 30 -KPX f quotedblright 30 -KPX f period -10 -KPX f o -20 -KPX f e -10 -KPX f comma -10 - -KPX g g -10 -KPX g e 10 - -KPX h y -20 - -KPX k o -15 - -KPX l y -15 -KPX l w -15 - -KPX m y -30 -KPX m u -20 - -KPX n y -20 -KPX n v -40 -KPX n u -10 - -KPX o y -20 -KPX o x -30 -KPX o w -15 -KPX o v -20 - -KPX p y -15 - -KPX period space -40 -KPX period quoteright -120 -KPX period quotedblright -120 - -KPX quotedblright space -80 - -KPX quoteleft quoteleft -46 - -KPX quoteright v -20 -KPX quoteright space -80 -KPX quoteright s -60 -KPX quoteright r -40 -KPX quoteright quoteright -46 -KPX quoteright l -20 -KPX quoteright d -80 - -KPX r y 10 -KPX r v 10 -KPX r t 20 -KPX r s -15 -KPX r q -20 -KPX r period -60 -KPX r o -20 -KPX r hyphen -20 -KPX r g -15 -KPX r d -20 -KPX r comma -60 -KPX r c -20 - -KPX s w -15 - -KPX semicolon space -40 - -KPX space quoteleft -60 -KPX space quotedblleft -80 -KPX space Y -120 -KPX space W -80 -KPX space V -80 -KPX space T -100 - -KPX v period -80 -KPX v o -30 -KPX v comma -80 -KPX v a -20 - -KPX w period -40 -KPX w o -20 -KPX w comma -40 - -KPX x e -10 - -KPX y period -80 -KPX y o -25 -KPX y e -10 -KPX y comma -80 -KPX y a -30 - -KPX z e 10 -EndKernPairs -EndKernData -StartComposites 58 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 195 186 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 195 186 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 195 186 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 195 186 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 195 186 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 195 186 ; -CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 215 0 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 167 186 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 167 186 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 167 186 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 167 186 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute -27 186 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex -27 186 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis -27 186 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave -27 186 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 195 186 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 223 186 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 223 186 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 223 186 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 223 186 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 223 186 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 167 186 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 195 186 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 195 186 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 195 186 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 195 186 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 167 186 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 167 186 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 139 186 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 112 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 112 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 112 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 112 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 112 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 112 0 ; -CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 132 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 112 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 112 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 112 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 112 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -27 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -27 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -27 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -27 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 139 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 139 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 139 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 139 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 139 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 139 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 112 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 139 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 139 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 139 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 139 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 112 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 112 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 84 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvb8an.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvb8an.afm deleted file mode 100644 index b7c6969..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvb8an.afm +++ /dev/null @@ -1,570 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Thu Mar 15 11:47:27 1990 -Comment UniqueID 28398 -Comment VMusage 7614 43068 -FontName Helvetica-Narrow-Bold -FullName Helvetica Narrow Bold -FamilyName Helvetica -Weight Bold -ItalicAngle 0 -IsFixedPitch false -FontBBox -139 -228 822 962 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.007 -Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved.Helvetica is a trademark of Linotype AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 718 -XHeight 532 -Ascender 718 -Descender -207 -StartCharMetrics 228 -C 32 ; WX 228 ; N space ; B 0 0 0 0 ; -C 33 ; WX 273 ; N exclam ; B 74 0 200 718 ; -C 34 ; WX 389 ; N quotedbl ; B 80 447 308 718 ; -C 35 ; WX 456 ; N numbersign ; B 15 0 441 698 ; -C 36 ; WX 456 ; N dollar ; B 25 -115 429 775 ; -C 37 ; WX 729 ; N percent ; B 23 -19 706 710 ; -C 38 ; WX 592 ; N ampersand ; B 44 -19 575 718 ; -C 39 ; WX 228 ; N quoteright ; B 57 445 171 718 ; -C 40 ; WX 273 ; N parenleft ; B 29 -208 257 734 ; -C 41 ; WX 273 ; N parenright ; B 16 -208 244 734 ; -C 42 ; WX 319 ; N asterisk ; B 22 387 297 718 ; -C 43 ; WX 479 ; N plus ; B 33 0 446 506 ; -C 44 ; WX 228 ; N comma ; B 52 -168 175 146 ; -C 45 ; WX 273 ; N hyphen ; B 22 215 251 345 ; -C 46 ; WX 228 ; N period ; B 52 0 175 146 ; -C 47 ; WX 228 ; N slash ; B -27 -19 255 737 ; -C 48 ; WX 456 ; N zero ; B 26 -19 430 710 ; -C 49 ; WX 456 ; N one ; B 57 0 310 710 ; -C 50 ; WX 456 ; N two ; B 21 0 419 710 ; -C 51 ; WX 456 ; N three ; B 22 -19 423 710 ; -C 52 ; WX 456 ; N four ; B 22 0 431 710 ; -C 53 ; WX 456 ; N five ; B 22 -19 423 698 ; -C 54 ; WX 456 ; N six ; B 25 -19 426 710 ; -C 55 ; WX 456 ; N seven ; B 20 0 433 698 ; -C 56 ; WX 456 ; N eight ; B 26 -19 430 710 ; -C 57 ; WX 456 ; N nine ; B 25 -19 428 710 ; -C 58 ; WX 273 ; N colon ; B 75 0 198 512 ; -C 59 ; WX 273 ; N semicolon ; B 75 -168 198 512 ; -C 60 ; WX 479 ; N less ; B 31 -8 448 514 ; -C 61 ; WX 479 ; N equal ; B 33 87 446 419 ; -C 62 ; WX 479 ; N greater ; B 31 -8 448 514 ; -C 63 ; WX 501 ; N question ; B 49 0 456 727 ; -C 64 ; WX 800 ; N at ; B 97 -19 702 737 ; -C 65 ; WX 592 ; N A ; B 16 0 576 718 ; -C 66 ; WX 592 ; N B ; B 62 0 549 718 ; -C 67 ; WX 592 ; N C ; B 36 -19 561 737 ; -C 68 ; WX 592 ; N D ; B 62 0 562 718 ; -C 69 ; WX 547 ; N E ; B 62 0 509 718 ; -C 70 ; WX 501 ; N F ; B 62 0 481 718 ; -C 71 ; WX 638 ; N G ; B 36 -19 585 737 ; -C 72 ; WX 592 ; N H ; B 58 0 534 718 ; -C 73 ; WX 228 ; N I ; B 52 0 175 718 ; -C 74 ; WX 456 ; N J ; B 18 -18 397 718 ; -C 75 ; WX 592 ; N K ; B 71 0 592 718 ; -C 76 ; WX 501 ; N L ; B 62 0 478 718 ; -C 77 ; WX 683 ; N M ; B 57 0 627 718 ; -C 78 ; WX 592 ; N N ; B 57 0 536 718 ; -C 79 ; WX 638 ; N O ; B 36 -19 602 737 ; -C 80 ; WX 547 ; N P ; B 62 0 514 718 ; -C 81 ; WX 638 ; N Q ; B 36 -52 604 737 ; -C 82 ; WX 592 ; N R ; B 62 0 555 718 ; -C 83 ; WX 547 ; N S ; B 32 -19 516 737 ; -C 84 ; WX 501 ; N T ; B 11 0 490 718 ; -C 85 ; WX 592 ; N U ; B 59 -19 534 718 ; -C 86 ; WX 547 ; N V ; B 16 0 531 718 ; -C 87 ; WX 774 ; N W ; B 13 0 762 718 ; -C 88 ; WX 547 ; N X ; B 11 0 535 718 ; -C 89 ; WX 547 ; N Y ; B 12 0 535 718 ; -C 90 ; WX 501 ; N Z ; B 20 0 481 718 ; -C 91 ; WX 273 ; N bracketleft ; B 52 -196 253 722 ; -C 92 ; WX 228 ; N backslash ; B -27 -19 255 737 ; -C 93 ; WX 273 ; N bracketright ; B 20 -196 221 722 ; -C 94 ; WX 479 ; N asciicircum ; B 51 323 428 698 ; -C 95 ; WX 456 ; N underscore ; B 0 -125 456 -75 ; -C 96 ; WX 228 ; N quoteleft ; B 57 454 171 727 ; -C 97 ; WX 456 ; N a ; B 24 -14 432 546 ; -C 98 ; WX 501 ; N b ; B 50 -14 474 718 ; -C 99 ; WX 456 ; N c ; B 28 -14 430 546 ; -C 100 ; WX 501 ; N d ; B 28 -14 452 718 ; -C 101 ; WX 456 ; N e ; B 19 -14 433 546 ; -C 102 ; WX 273 ; N f ; B 8 0 261 727 ; L i fi ; L l fl ; -C 103 ; WX 501 ; N g ; B 33 -217 453 546 ; -C 104 ; WX 501 ; N h ; B 53 0 448 718 ; -C 105 ; WX 228 ; N i ; B 57 0 171 725 ; -C 106 ; WX 228 ; N j ; B 2 -214 171 725 ; -C 107 ; WX 456 ; N k ; B 57 0 461 718 ; -C 108 ; WX 228 ; N l ; B 57 0 171 718 ; -C 109 ; WX 729 ; N m ; B 52 0 677 546 ; -C 110 ; WX 501 ; N n ; B 53 0 448 546 ; -C 111 ; WX 501 ; N o ; B 28 -14 474 546 ; -C 112 ; WX 501 ; N p ; B 51 -207 474 546 ; -C 113 ; WX 501 ; N q ; B 28 -207 453 546 ; -C 114 ; WX 319 ; N r ; B 52 0 306 546 ; -C 115 ; WX 456 ; N s ; B 25 -14 426 546 ; -C 116 ; WX 273 ; N t ; B 8 -6 253 676 ; -C 117 ; WX 501 ; N u ; B 54 -14 447 532 ; -C 118 ; WX 456 ; N v ; B 11 0 445 532 ; -C 119 ; WX 638 ; N w ; B 8 0 631 532 ; -C 120 ; WX 456 ; N x ; B 12 0 444 532 ; -C 121 ; WX 456 ; N y ; B 8 -214 442 532 ; -C 122 ; WX 410 ; N z ; B 16 0 394 532 ; -C 123 ; WX 319 ; N braceleft ; B 39 -196 299 722 ; -C 124 ; WX 230 ; N bar ; B 69 -19 161 737 ; -C 125 ; WX 319 ; N braceright ; B 20 -196 280 722 ; -C 126 ; WX 479 ; N asciitilde ; B 50 163 429 343 ; -C 161 ; WX 273 ; N exclamdown ; B 74 -186 200 532 ; -C 162 ; WX 456 ; N cent ; B 28 -118 430 628 ; -C 163 ; WX 456 ; N sterling ; B 23 -16 444 718 ; -C 164 ; WX 137 ; N fraction ; B -139 -19 276 710 ; -C 165 ; WX 456 ; N yen ; B -7 0 463 698 ; -C 166 ; WX 456 ; N florin ; B -8 -210 423 737 ; -C 167 ; WX 456 ; N section ; B 28 -184 428 727 ; -C 168 ; WX 456 ; N currency ; B -2 76 458 636 ; -C 169 ; WX 195 ; N quotesingle ; B 57 447 138 718 ; -C 170 ; WX 410 ; N quotedblleft ; B 52 454 358 727 ; -C 171 ; WX 456 ; N guillemotleft ; B 72 76 384 484 ; -C 172 ; WX 273 ; N guilsinglleft ; B 68 76 205 484 ; -C 173 ; WX 273 ; N guilsinglright ; B 68 76 205 484 ; -C 174 ; WX 501 ; N fi ; B 8 0 444 727 ; -C 175 ; WX 501 ; N fl ; B 8 0 444 727 ; -C 177 ; WX 456 ; N endash ; B 0 227 456 333 ; -C 178 ; WX 456 ; N dagger ; B 30 -171 426 718 ; -C 179 ; WX 456 ; N daggerdbl ; B 30 -171 426 718 ; -C 180 ; WX 228 ; N periodcentered ; B 48 172 180 334 ; -C 182 ; WX 456 ; N paragraph ; B -7 -191 442 700 ; -C 183 ; WX 287 ; N bullet ; B 8 194 279 524 ; -C 184 ; WX 228 ; N quotesinglbase ; B 57 -146 171 127 ; -C 185 ; WX 410 ; N quotedblbase ; B 52 -146 358 127 ; -C 186 ; WX 410 ; N quotedblright ; B 52 445 358 718 ; -C 187 ; WX 456 ; N guillemotright ; B 72 76 384 484 ; -C 188 ; WX 820 ; N ellipsis ; B 75 0 745 146 ; -C 189 ; WX 820 ; N perthousand ; B -2 -19 822 710 ; -C 191 ; WX 501 ; N questiondown ; B 45 -195 452 532 ; -C 193 ; WX 273 ; N grave ; B -19 604 184 750 ; -C 194 ; WX 273 ; N acute ; B 89 604 292 750 ; -C 195 ; WX 273 ; N circumflex ; B -8 604 281 750 ; -C 196 ; WX 273 ; N tilde ; B -14 610 287 737 ; -C 197 ; WX 273 ; N macron ; B -5 604 278 678 ; -C 198 ; WX 273 ; N breve ; B -2 604 275 750 ; -C 199 ; WX 273 ; N dotaccent ; B 85 614 189 729 ; -C 200 ; WX 273 ; N dieresis ; B 5 614 268 729 ; -C 202 ; WX 273 ; N ring ; B 48 568 225 776 ; -C 203 ; WX 273 ; N cedilla ; B 5 -228 201 0 ; -C 205 ; WX 273 ; N hungarumlaut ; B 7 604 399 750 ; -C 206 ; WX 273 ; N ogonek ; B 58 -228 249 0 ; -C 207 ; WX 273 ; N caron ; B -8 604 281 750 ; -C 208 ; WX 820 ; N emdash ; B 0 227 820 333 ; -C 225 ; WX 820 ; N AE ; B 4 0 782 718 ; -C 227 ; WX 303 ; N ordfeminine ; B 18 276 285 737 ; -C 232 ; WX 501 ; N Lslash ; B -16 0 478 718 ; -C 233 ; WX 638 ; N Oslash ; B 27 -27 610 745 ; -C 234 ; WX 820 ; N OE ; B 30 -19 788 737 ; -C 235 ; WX 299 ; N ordmasculine ; B 5 276 295 737 ; -C 241 ; WX 729 ; N ae ; B 24 -14 704 546 ; -C 245 ; WX 228 ; N dotlessi ; B 57 0 171 532 ; -C 248 ; WX 228 ; N lslash ; B -15 0 243 718 ; -C 249 ; WX 501 ; N oslash ; B 18 -29 483 560 ; -C 250 ; WX 774 ; N oe ; B 28 -14 748 546 ; -C 251 ; WX 501 ; N germandbls ; B 57 -14 475 731 ; -C -1 ; WX 501 ; N Zcaron ; B 20 0 481 936 ; -C -1 ; WX 456 ; N ccedilla ; B 28 -228 430 546 ; -C -1 ; WX 456 ; N ydieresis ; B 8 -214 442 729 ; -C -1 ; WX 456 ; N atilde ; B 24 -14 432 737 ; -C -1 ; WX 228 ; N icircumflex ; B -30 0 259 750 ; -C -1 ; WX 273 ; N threesuperior ; B 7 271 267 710 ; -C -1 ; WX 456 ; N ecircumflex ; B 19 -14 433 750 ; -C -1 ; WX 501 ; N thorn ; B 51 -208 474 718 ; -C -1 ; WX 456 ; N egrave ; B 19 -14 433 750 ; -C -1 ; WX 273 ; N twosuperior ; B 7 283 266 710 ; -C -1 ; WX 456 ; N eacute ; B 19 -14 433 750 ; -C -1 ; WX 501 ; N otilde ; B 28 -14 474 737 ; -C -1 ; WX 592 ; N Aacute ; B 16 0 576 936 ; -C -1 ; WX 501 ; N ocircumflex ; B 28 -14 474 750 ; -C -1 ; WX 456 ; N yacute ; B 8 -214 442 750 ; -C -1 ; WX 501 ; N udieresis ; B 54 -14 447 729 ; -C -1 ; WX 684 ; N threequarters ; B 13 -19 655 710 ; -C -1 ; WX 456 ; N acircumflex ; B 24 -14 432 750 ; -C -1 ; WX 592 ; N Eth ; B -4 0 562 718 ; -C -1 ; WX 456 ; N edieresis ; B 19 -14 433 729 ; -C -1 ; WX 501 ; N ugrave ; B 54 -14 447 750 ; -C -1 ; WX 820 ; N trademark ; B 36 306 784 718 ; -C -1 ; WX 501 ; N ograve ; B 28 -14 474 750 ; -C -1 ; WX 456 ; N scaron ; B 25 -14 426 750 ; -C -1 ; WX 228 ; N Idieresis ; B -17 0 246 915 ; -C -1 ; WX 501 ; N uacute ; B 54 -14 447 750 ; -C -1 ; WX 456 ; N agrave ; B 24 -14 432 750 ; -C -1 ; WX 501 ; N ntilde ; B 53 0 448 737 ; -C -1 ; WX 456 ; N aring ; B 24 -14 432 776 ; -C -1 ; WX 410 ; N zcaron ; B 16 0 394 750 ; -C -1 ; WX 228 ; N Icircumflex ; B -30 0 259 936 ; -C -1 ; WX 592 ; N Ntilde ; B 57 0 536 923 ; -C -1 ; WX 501 ; N ucircumflex ; B 54 -14 447 750 ; -C -1 ; WX 547 ; N Ecircumflex ; B 62 0 509 936 ; -C -1 ; WX 228 ; N Iacute ; B 52 0 270 936 ; -C -1 ; WX 592 ; N Ccedilla ; B 36 -228 561 737 ; -C -1 ; WX 638 ; N Odieresis ; B 36 -19 602 915 ; -C -1 ; WX 547 ; N Scaron ; B 32 -19 516 936 ; -C -1 ; WX 547 ; N Edieresis ; B 62 0 509 915 ; -C -1 ; WX 228 ; N Igrave ; B -41 0 175 936 ; -C -1 ; WX 456 ; N adieresis ; B 24 -14 432 729 ; -C -1 ; WX 638 ; N Ograve ; B 36 -19 602 936 ; -C -1 ; WX 547 ; N Egrave ; B 62 0 509 936 ; -C -1 ; WX 547 ; N Ydieresis ; B 12 0 535 915 ; -C -1 ; WX 604 ; N registered ; B -9 -19 613 737 ; -C -1 ; WX 638 ; N Otilde ; B 36 -19 602 923 ; -C -1 ; WX 684 ; N onequarter ; B 21 -19 628 710 ; -C -1 ; WX 592 ; N Ugrave ; B 59 -19 534 936 ; -C -1 ; WX 592 ; N Ucircumflex ; B 59 -19 534 936 ; -C -1 ; WX 547 ; N Thorn ; B 62 0 514 718 ; -C -1 ; WX 479 ; N divide ; B 33 -42 446 548 ; -C -1 ; WX 592 ; N Atilde ; B 16 0 576 923 ; -C -1 ; WX 592 ; N Uacute ; B 59 -19 534 936 ; -C -1 ; WX 638 ; N Ocircumflex ; B 36 -19 602 936 ; -C -1 ; WX 479 ; N logicalnot ; B 33 108 446 419 ; -C -1 ; WX 592 ; N Aring ; B 16 0 576 962 ; -C -1 ; WX 228 ; N idieresis ; B -17 0 246 729 ; -C -1 ; WX 228 ; N iacute ; B 57 0 270 750 ; -C -1 ; WX 456 ; N aacute ; B 24 -14 432 750 ; -C -1 ; WX 479 ; N plusminus ; B 33 0 446 506 ; -C -1 ; WX 479 ; N multiply ; B 33 1 447 505 ; -C -1 ; WX 592 ; N Udieresis ; B 59 -19 534 915 ; -C -1 ; WX 479 ; N minus ; B 33 197 446 309 ; -C -1 ; WX 273 ; N onesuperior ; B 21 283 194 710 ; -C -1 ; WX 547 ; N Eacute ; B 62 0 509 936 ; -C -1 ; WX 592 ; N Acircumflex ; B 16 0 576 936 ; -C -1 ; WX 604 ; N copyright ; B -9 -19 614 737 ; -C -1 ; WX 592 ; N Agrave ; B 16 0 576 936 ; -C -1 ; WX 501 ; N odieresis ; B 28 -14 474 729 ; -C -1 ; WX 501 ; N oacute ; B 28 -14 474 750 ; -C -1 ; WX 328 ; N degree ; B 47 426 281 712 ; -C -1 ; WX 228 ; N igrave ; B -41 0 171 750 ; -C -1 ; WX 501 ; N mu ; B 54 -207 447 532 ; -C -1 ; WX 638 ; N Oacute ; B 36 -19 602 936 ; -C -1 ; WX 501 ; N eth ; B 28 -14 474 737 ; -C -1 ; WX 592 ; N Adieresis ; B 16 0 576 915 ; -C -1 ; WX 547 ; N Yacute ; B 12 0 535 936 ; -C -1 ; WX 230 ; N brokenbar ; B 69 -19 161 737 ; -C -1 ; WX 684 ; N onehalf ; B 21 -19 651 710 ; -EndCharMetrics -StartKernData -StartKernPairs 209 - -KPX A y -24 -KPX A w -24 -KPX A v -32 -KPX A u -24 -KPX A Y -89 -KPX A W -48 -KPX A V -65 -KPX A U -40 -KPX A T -73 -KPX A Q -32 -KPX A O -32 -KPX A G -40 -KPX A C -32 - -KPX B U -7 -KPX B A -24 - -KPX D period -24 -KPX D comma -24 -KPX D Y -56 -KPX D W -32 -KPX D V -32 -KPX D A -32 - -KPX F period -81 -KPX F comma -81 -KPX F a -15 -KPX F A -65 - -KPX J u -15 -KPX J period -15 -KPX J comma -15 -KPX J A -15 - -KPX K y -32 -KPX K u -24 -KPX K o -28 -KPX K e -11 -KPX K O -24 - -KPX L y -24 -KPX L quoteright -114 -KPX L quotedblright -114 -KPX L Y -97 -KPX L W -65 -KPX L V -89 -KPX L T -73 - -KPX O period -32 -KPX O comma -32 -KPX O Y -56 -KPX O X -40 -KPX O W -40 -KPX O V -40 -KPX O T -32 -KPX O A -40 - -KPX P period -97 -KPX P o -32 -KPX P e -24 -KPX P comma -97 -KPX P a -24 -KPX P A -81 - -KPX Q period 16 -KPX Q comma 16 -KPX Q U -7 - -KPX R Y -40 -KPX R W -32 -KPX R V -40 -KPX R U -15 -KPX R T -15 -KPX R O -15 - -KPX T y -48 -KPX T w -48 -KPX T u -73 -KPX T semicolon -32 -KPX T r -65 -KPX T period -65 -KPX T o -65 -KPX T hyphen -97 -KPX T e -48 -KPX T comma -65 -KPX T colon -32 -KPX T a -65 -KPX T O -32 -KPX T A -73 - -KPX U period -24 -KPX U comma -24 -KPX U A -40 - -KPX V u -48 -KPX V semicolon -32 -KPX V period -97 -KPX V o -73 -KPX V hyphen -65 -KPX V e -40 -KPX V comma -97 -KPX V colon -32 -KPX V a -48 -KPX V O -40 -KPX V G -40 -KPX V A -65 - -KPX W y -15 -KPX W u -36 -KPX W semicolon -7 -KPX W period -65 -KPX W o -48 -KPX W hyphen -32 -KPX W e -28 -KPX W comma -65 -KPX W colon -7 -KPX W a -32 -KPX W O -15 -KPX W A -48 - -KPX Y u -81 -KPX Y semicolon -40 -KPX Y period -81 -KPX Y o -81 -KPX Y e -65 -KPX Y comma -81 -KPX Y colon -40 -KPX Y a -73 -KPX Y O -56 -KPX Y A -89 - -KPX a y -15 -KPX a w -11 -KPX a v -11 -KPX a g -7 - -KPX b y -15 -KPX b v -15 -KPX b u -15 -KPX b l -7 - -KPX c y -7 -KPX c l -15 -KPX c k -15 -KPX c h -7 - -KPX colon space -32 - -KPX comma space -32 -KPX comma quoteright -97 -KPX comma quotedblright -97 - -KPX d y -11 -KPX d w -11 -KPX d v -11 -KPX d d -7 - -KPX e y -11 -KPX e x -11 -KPX e w -11 -KPX e v -11 -KPX e period 16 -KPX e comma 8 - -KPX f quoteright 25 -KPX f quotedblright 25 -KPX f period -7 -KPX f o -15 -KPX f e -7 -KPX f comma -7 - -KPX g g -7 -KPX g e 8 - -KPX h y -15 - -KPX k o -11 - -KPX l y -11 -KPX l w -11 - -KPX m y -24 -KPX m u -15 - -KPX n y -15 -KPX n v -32 -KPX n u -7 - -KPX o y -15 -KPX o x -24 -KPX o w -11 -KPX o v -15 - -KPX p y -11 - -KPX period space -32 -KPX period quoteright -97 -KPX period quotedblright -97 - -KPX quotedblright space -65 - -KPX quoteleft quoteleft -37 - -KPX quoteright v -15 -KPX quoteright space -65 -KPX quoteright s -48 -KPX quoteright r -32 -KPX quoteright quoteright -37 -KPX quoteright l -15 -KPX quoteright d -65 - -KPX r y 8 -KPX r v 8 -KPX r t 16 -KPX r s -11 -KPX r q -15 -KPX r period -48 -KPX r o -15 -KPX r hyphen -15 -KPX r g -11 -KPX r d -15 -KPX r comma -48 -KPX r c -15 - -KPX s w -11 - -KPX semicolon space -32 - -KPX space quoteleft -48 -KPX space quotedblleft -65 -KPX space Y -97 -KPX space W -65 -KPX space V -65 -KPX space T -81 - -KPX v period -65 -KPX v o -24 -KPX v comma -65 -KPX v a -15 - -KPX w period -32 -KPX w o -15 -KPX w comma -32 - -KPX x e -7 - -KPX y period -65 -KPX y o -20 -KPX y e -7 -KPX y comma -65 -KPX y a -24 - -KPX z e 8 -EndKernPairs -EndKernData -StartComposites 58 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 160 186 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 160 186 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 160 186 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 160 186 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 160 186 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 160 186 ; -CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 176 0 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 137 186 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 137 186 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 137 186 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 137 186 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute -22 186 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex -22 186 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis -22 186 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave -22 186 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 160 186 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 183 186 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 183 186 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 183 186 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 183 186 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 183 186 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 137 186 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 160 186 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 160 186 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 160 186 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 160 186 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 137 186 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 137 186 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 114 186 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 92 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 92 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 92 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 92 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 92 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 92 0 ; -CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 108 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 92 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 92 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 92 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 92 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -22 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -22 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -22 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -22 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 114 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 114 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 114 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 114 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 114 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 114 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 92 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 114 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 114 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 114 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 114 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 92 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 92 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 69 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvbo8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvbo8a.afm deleted file mode 100644 index b6cff41..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvbo8a.afm +++ /dev/null @@ -1,570 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Thu Mar 15 10:44:33 1990 -Comment UniqueID 28371 -Comment VMusage 7614 43068 -FontName Helvetica-BoldOblique -FullName Helvetica Bold Oblique -FamilyName Helvetica -Weight Bold -ItalicAngle -12 -IsFixedPitch false -FontBBox -174 -228 1114 962 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.007 -Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved.Helvetica is a trademark of Linotype AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 718 -XHeight 532 -Ascender 718 -Descender -207 -StartCharMetrics 228 -C 32 ; WX 278 ; N space ; B 0 0 0 0 ; -C 33 ; WX 333 ; N exclam ; B 94 0 397 718 ; -C 34 ; WX 474 ; N quotedbl ; B 193 447 529 718 ; -C 35 ; WX 556 ; N numbersign ; B 60 0 644 698 ; -C 36 ; WX 556 ; N dollar ; B 67 -115 622 775 ; -C 37 ; WX 889 ; N percent ; B 136 -19 901 710 ; -C 38 ; WX 722 ; N ampersand ; B 89 -19 732 718 ; -C 39 ; WX 278 ; N quoteright ; B 167 445 362 718 ; -C 40 ; WX 333 ; N parenleft ; B 76 -208 470 734 ; -C 41 ; WX 333 ; N parenright ; B -25 -208 369 734 ; -C 42 ; WX 389 ; N asterisk ; B 146 387 481 718 ; -C 43 ; WX 584 ; N plus ; B 82 0 610 506 ; -C 44 ; WX 278 ; N comma ; B 28 -168 245 146 ; -C 45 ; WX 333 ; N hyphen ; B 73 215 379 345 ; -C 46 ; WX 278 ; N period ; B 64 0 245 146 ; -C 47 ; WX 278 ; N slash ; B -37 -19 468 737 ; -C 48 ; WX 556 ; N zero ; B 86 -19 617 710 ; -C 49 ; WX 556 ; N one ; B 173 0 529 710 ; -C 50 ; WX 556 ; N two ; B 26 0 619 710 ; -C 51 ; WX 556 ; N three ; B 65 -19 608 710 ; -C 52 ; WX 556 ; N four ; B 60 0 598 710 ; -C 53 ; WX 556 ; N five ; B 64 -19 636 698 ; -C 54 ; WX 556 ; N six ; B 85 -19 619 710 ; -C 55 ; WX 556 ; N seven ; B 125 0 676 698 ; -C 56 ; WX 556 ; N eight ; B 69 -19 616 710 ; -C 57 ; WX 556 ; N nine ; B 78 -19 615 710 ; -C 58 ; WX 333 ; N colon ; B 92 0 351 512 ; -C 59 ; WX 333 ; N semicolon ; B 56 -168 351 512 ; -C 60 ; WX 584 ; N less ; B 82 -8 655 514 ; -C 61 ; WX 584 ; N equal ; B 58 87 633 419 ; -C 62 ; WX 584 ; N greater ; B 36 -8 609 514 ; -C 63 ; WX 611 ; N question ; B 165 0 671 727 ; -C 64 ; WX 975 ; N at ; B 186 -19 954 737 ; -C 65 ; WX 722 ; N A ; B 20 0 702 718 ; -C 66 ; WX 722 ; N B ; B 76 0 764 718 ; -C 67 ; WX 722 ; N C ; B 107 -19 789 737 ; -C 68 ; WX 722 ; N D ; B 76 0 777 718 ; -C 69 ; WX 667 ; N E ; B 76 0 757 718 ; -C 70 ; WX 611 ; N F ; B 76 0 740 718 ; -C 71 ; WX 778 ; N G ; B 108 -19 817 737 ; -C 72 ; WX 722 ; N H ; B 71 0 804 718 ; -C 73 ; WX 278 ; N I ; B 64 0 367 718 ; -C 74 ; WX 556 ; N J ; B 60 -18 637 718 ; -C 75 ; WX 722 ; N K ; B 87 0 858 718 ; -C 76 ; WX 611 ; N L ; B 76 0 611 718 ; -C 77 ; WX 833 ; N M ; B 69 0 918 718 ; -C 78 ; WX 722 ; N N ; B 69 0 807 718 ; -C 79 ; WX 778 ; N O ; B 107 -19 823 737 ; -C 80 ; WX 667 ; N P ; B 76 0 738 718 ; -C 81 ; WX 778 ; N Q ; B 107 -52 823 737 ; -C 82 ; WX 722 ; N R ; B 76 0 778 718 ; -C 83 ; WX 667 ; N S ; B 81 -19 718 737 ; -C 84 ; WX 611 ; N T ; B 140 0 751 718 ; -C 85 ; WX 722 ; N U ; B 116 -19 804 718 ; -C 86 ; WX 667 ; N V ; B 172 0 801 718 ; -C 87 ; WX 944 ; N W ; B 169 0 1082 718 ; -C 88 ; WX 667 ; N X ; B 14 0 791 718 ; -C 89 ; WX 667 ; N Y ; B 168 0 806 718 ; -C 90 ; WX 611 ; N Z ; B 25 0 737 718 ; -C 91 ; WX 333 ; N bracketleft ; B 21 -196 462 722 ; -C 92 ; WX 278 ; N backslash ; B 124 -19 307 737 ; -C 93 ; WX 333 ; N bracketright ; B -18 -196 423 722 ; -C 94 ; WX 584 ; N asciicircum ; B 131 323 591 698 ; -C 95 ; WX 556 ; N underscore ; B -27 -125 540 -75 ; -C 96 ; WX 278 ; N quoteleft ; B 165 454 361 727 ; -C 97 ; WX 556 ; N a ; B 55 -14 583 546 ; -C 98 ; WX 611 ; N b ; B 61 -14 645 718 ; -C 99 ; WX 556 ; N c ; B 79 -14 599 546 ; -C 100 ; WX 611 ; N d ; B 82 -14 704 718 ; -C 101 ; WX 556 ; N e ; B 70 -14 593 546 ; -C 102 ; WX 333 ; N f ; B 87 0 469 727 ; L i fi ; L l fl ; -C 103 ; WX 611 ; N g ; B 38 -217 666 546 ; -C 104 ; WX 611 ; N h ; B 65 0 629 718 ; -C 105 ; WX 278 ; N i ; B 69 0 363 725 ; -C 106 ; WX 278 ; N j ; B -42 -214 363 725 ; -C 107 ; WX 556 ; N k ; B 69 0 670 718 ; -C 108 ; WX 278 ; N l ; B 69 0 362 718 ; -C 109 ; WX 889 ; N m ; B 64 0 909 546 ; -C 110 ; WX 611 ; N n ; B 65 0 629 546 ; -C 111 ; WX 611 ; N o ; B 82 -14 643 546 ; -C 112 ; WX 611 ; N p ; B 18 -207 645 546 ; -C 113 ; WX 611 ; N q ; B 80 -207 665 546 ; -C 114 ; WX 389 ; N r ; B 64 0 489 546 ; -C 115 ; WX 556 ; N s ; B 63 -14 584 546 ; -C 116 ; WX 333 ; N t ; B 100 -6 422 676 ; -C 117 ; WX 611 ; N u ; B 98 -14 658 532 ; -C 118 ; WX 556 ; N v ; B 126 0 656 532 ; -C 119 ; WX 778 ; N w ; B 123 0 882 532 ; -C 120 ; WX 556 ; N x ; B 15 0 648 532 ; -C 121 ; WX 556 ; N y ; B 42 -214 652 532 ; -C 122 ; WX 500 ; N z ; B 20 0 583 532 ; -C 123 ; WX 389 ; N braceleft ; B 94 -196 518 722 ; -C 124 ; WX 280 ; N bar ; B 80 -19 353 737 ; -C 125 ; WX 389 ; N braceright ; B -18 -196 407 722 ; -C 126 ; WX 584 ; N asciitilde ; B 115 163 577 343 ; -C 161 ; WX 333 ; N exclamdown ; B 50 -186 353 532 ; -C 162 ; WX 556 ; N cent ; B 79 -118 599 628 ; -C 163 ; WX 556 ; N sterling ; B 50 -16 635 718 ; -C 164 ; WX 167 ; N fraction ; B -174 -19 487 710 ; -C 165 ; WX 556 ; N yen ; B 60 0 713 698 ; -C 166 ; WX 556 ; N florin ; B -50 -210 669 737 ; -C 167 ; WX 556 ; N section ; B 61 -184 598 727 ; -C 168 ; WX 556 ; N currency ; B 27 76 680 636 ; -C 169 ; WX 238 ; N quotesingle ; B 165 447 321 718 ; -C 170 ; WX 500 ; N quotedblleft ; B 160 454 588 727 ; -C 171 ; WX 556 ; N guillemotleft ; B 135 76 571 484 ; -C 172 ; WX 333 ; N guilsinglleft ; B 130 76 353 484 ; -C 173 ; WX 333 ; N guilsinglright ; B 99 76 322 484 ; -C 174 ; WX 611 ; N fi ; B 87 0 696 727 ; -C 175 ; WX 611 ; N fl ; B 87 0 695 727 ; -C 177 ; WX 556 ; N endash ; B 48 227 627 333 ; -C 178 ; WX 556 ; N dagger ; B 118 -171 626 718 ; -C 179 ; WX 556 ; N daggerdbl ; B 46 -171 628 718 ; -C 180 ; WX 278 ; N periodcentered ; B 110 172 276 334 ; -C 182 ; WX 556 ; N paragraph ; B 98 -191 688 700 ; -C 183 ; WX 350 ; N bullet ; B 83 194 420 524 ; -C 184 ; WX 278 ; N quotesinglbase ; B 41 -146 236 127 ; -C 185 ; WX 500 ; N quotedblbase ; B 36 -146 463 127 ; -C 186 ; WX 500 ; N quotedblright ; B 162 445 589 718 ; -C 187 ; WX 556 ; N guillemotright ; B 104 76 540 484 ; -C 188 ; WX 1000 ; N ellipsis ; B 92 0 939 146 ; -C 189 ; WX 1000 ; N perthousand ; B 76 -19 1038 710 ; -C 191 ; WX 611 ; N questiondown ; B 53 -195 559 532 ; -C 193 ; WX 333 ; N grave ; B 136 604 353 750 ; -C 194 ; WX 333 ; N acute ; B 236 604 515 750 ; -C 195 ; WX 333 ; N circumflex ; B 118 604 471 750 ; -C 196 ; WX 333 ; N tilde ; B 113 610 507 737 ; -C 197 ; WX 333 ; N macron ; B 122 604 483 678 ; -C 198 ; WX 333 ; N breve ; B 156 604 494 750 ; -C 199 ; WX 333 ; N dotaccent ; B 235 614 385 729 ; -C 200 ; WX 333 ; N dieresis ; B 137 614 482 729 ; -C 202 ; WX 333 ; N ring ; B 200 568 420 776 ; -C 203 ; WX 333 ; N cedilla ; B -37 -228 220 0 ; -C 205 ; WX 333 ; N hungarumlaut ; B 137 604 645 750 ; -C 206 ; WX 333 ; N ogonek ; B 41 -228 264 0 ; -C 207 ; WX 333 ; N caron ; B 149 604 502 750 ; -C 208 ; WX 1000 ; N emdash ; B 48 227 1071 333 ; -C 225 ; WX 1000 ; N AE ; B 5 0 1100 718 ; -C 227 ; WX 370 ; N ordfeminine ; B 92 276 465 737 ; -C 232 ; WX 611 ; N Lslash ; B 34 0 611 718 ; -C 233 ; WX 778 ; N Oslash ; B 35 -27 894 745 ; -C 234 ; WX 1000 ; N OE ; B 99 -19 1114 737 ; -C 235 ; WX 365 ; N ordmasculine ; B 92 276 485 737 ; -C 241 ; WX 889 ; N ae ; B 56 -14 923 546 ; -C 245 ; WX 278 ; N dotlessi ; B 69 0 322 532 ; -C 248 ; WX 278 ; N lslash ; B 40 0 407 718 ; -C 249 ; WX 611 ; N oslash ; B 22 -29 701 560 ; -C 250 ; WX 944 ; N oe ; B 82 -14 977 546 ; -C 251 ; WX 611 ; N germandbls ; B 69 -14 657 731 ; -C -1 ; WX 611 ; N Zcaron ; B 25 0 737 936 ; -C -1 ; WX 556 ; N ccedilla ; B 79 -228 599 546 ; -C -1 ; WX 556 ; N ydieresis ; B 42 -214 652 729 ; -C -1 ; WX 556 ; N atilde ; B 55 -14 619 737 ; -C -1 ; WX 278 ; N icircumflex ; B 69 0 444 750 ; -C -1 ; WX 333 ; N threesuperior ; B 91 271 441 710 ; -C -1 ; WX 556 ; N ecircumflex ; B 70 -14 593 750 ; -C -1 ; WX 611 ; N thorn ; B 18 -208 645 718 ; -C -1 ; WX 556 ; N egrave ; B 70 -14 593 750 ; -C -1 ; WX 333 ; N twosuperior ; B 69 283 449 710 ; -C -1 ; WX 556 ; N eacute ; B 70 -14 627 750 ; -C -1 ; WX 611 ; N otilde ; B 82 -14 646 737 ; -C -1 ; WX 722 ; N Aacute ; B 20 0 750 936 ; -C -1 ; WX 611 ; N ocircumflex ; B 82 -14 643 750 ; -C -1 ; WX 556 ; N yacute ; B 42 -214 652 750 ; -C -1 ; WX 611 ; N udieresis ; B 98 -14 658 729 ; -C -1 ; WX 834 ; N threequarters ; B 99 -19 839 710 ; -C -1 ; WX 556 ; N acircumflex ; B 55 -14 583 750 ; -C -1 ; WX 722 ; N Eth ; B 62 0 777 718 ; -C -1 ; WX 556 ; N edieresis ; B 70 -14 594 729 ; -C -1 ; WX 611 ; N ugrave ; B 98 -14 658 750 ; -C -1 ; WX 1000 ; N trademark ; B 179 306 1109 718 ; -C -1 ; WX 611 ; N ograve ; B 82 -14 643 750 ; -C -1 ; WX 556 ; N scaron ; B 63 -14 614 750 ; -C -1 ; WX 278 ; N Idieresis ; B 64 0 494 915 ; -C -1 ; WX 611 ; N uacute ; B 98 -14 658 750 ; -C -1 ; WX 556 ; N agrave ; B 55 -14 583 750 ; -C -1 ; WX 611 ; N ntilde ; B 65 0 646 737 ; -C -1 ; WX 556 ; N aring ; B 55 -14 583 776 ; -C -1 ; WX 500 ; N zcaron ; B 20 0 586 750 ; -C -1 ; WX 278 ; N Icircumflex ; B 64 0 484 936 ; -C -1 ; WX 722 ; N Ntilde ; B 69 0 807 923 ; -C -1 ; WX 611 ; N ucircumflex ; B 98 -14 658 750 ; -C -1 ; WX 667 ; N Ecircumflex ; B 76 0 757 936 ; -C -1 ; WX 278 ; N Iacute ; B 64 0 528 936 ; -C -1 ; WX 722 ; N Ccedilla ; B 107 -228 789 737 ; -C -1 ; WX 778 ; N Odieresis ; B 107 -19 823 915 ; -C -1 ; WX 667 ; N Scaron ; B 81 -19 718 936 ; -C -1 ; WX 667 ; N Edieresis ; B 76 0 757 915 ; -C -1 ; WX 278 ; N Igrave ; B 64 0 367 936 ; -C -1 ; WX 556 ; N adieresis ; B 55 -14 594 729 ; -C -1 ; WX 778 ; N Ograve ; B 107 -19 823 936 ; -C -1 ; WX 667 ; N Egrave ; B 76 0 757 936 ; -C -1 ; WX 667 ; N Ydieresis ; B 168 0 806 915 ; -C -1 ; WX 737 ; N registered ; B 55 -19 834 737 ; -C -1 ; WX 778 ; N Otilde ; B 107 -19 823 923 ; -C -1 ; WX 834 ; N onequarter ; B 132 -19 806 710 ; -C -1 ; WX 722 ; N Ugrave ; B 116 -19 804 936 ; -C -1 ; WX 722 ; N Ucircumflex ; B 116 -19 804 936 ; -C -1 ; WX 667 ; N Thorn ; B 76 0 716 718 ; -C -1 ; WX 584 ; N divide ; B 82 -42 610 548 ; -C -1 ; WX 722 ; N Atilde ; B 20 0 741 923 ; -C -1 ; WX 722 ; N Uacute ; B 116 -19 804 936 ; -C -1 ; WX 778 ; N Ocircumflex ; B 107 -19 823 936 ; -C -1 ; WX 584 ; N logicalnot ; B 105 108 633 419 ; -C -1 ; WX 722 ; N Aring ; B 20 0 702 962 ; -C -1 ; WX 278 ; N idieresis ; B 69 0 455 729 ; -C -1 ; WX 278 ; N iacute ; B 69 0 488 750 ; -C -1 ; WX 556 ; N aacute ; B 55 -14 627 750 ; -C -1 ; WX 584 ; N plusminus ; B 40 0 625 506 ; -C -1 ; WX 584 ; N multiply ; B 57 1 635 505 ; -C -1 ; WX 722 ; N Udieresis ; B 116 -19 804 915 ; -C -1 ; WX 584 ; N minus ; B 82 197 610 309 ; -C -1 ; WX 333 ; N onesuperior ; B 148 283 388 710 ; -C -1 ; WX 667 ; N Eacute ; B 76 0 757 936 ; -C -1 ; WX 722 ; N Acircumflex ; B 20 0 706 936 ; -C -1 ; WX 737 ; N copyright ; B 56 -19 835 737 ; -C -1 ; WX 722 ; N Agrave ; B 20 0 702 936 ; -C -1 ; WX 611 ; N odieresis ; B 82 -14 643 729 ; -C -1 ; WX 611 ; N oacute ; B 82 -14 654 750 ; -C -1 ; WX 400 ; N degree ; B 175 426 467 712 ; -C -1 ; WX 278 ; N igrave ; B 69 0 326 750 ; -C -1 ; WX 611 ; N mu ; B 22 -207 658 532 ; -C -1 ; WX 778 ; N Oacute ; B 107 -19 823 936 ; -C -1 ; WX 611 ; N eth ; B 82 -14 670 737 ; -C -1 ; WX 722 ; N Adieresis ; B 20 0 716 915 ; -C -1 ; WX 667 ; N Yacute ; B 168 0 806 936 ; -C -1 ; WX 280 ; N brokenbar ; B 80 -19 353 737 ; -C -1 ; WX 834 ; N onehalf ; B 132 -19 858 710 ; -EndCharMetrics -StartKernData -StartKernPairs 209 - -KPX A y -30 -KPX A w -30 -KPX A v -40 -KPX A u -30 -KPX A Y -110 -KPX A W -60 -KPX A V -80 -KPX A U -50 -KPX A T -90 -KPX A Q -40 -KPX A O -40 -KPX A G -50 -KPX A C -40 - -KPX B U -10 -KPX B A -30 - -KPX D period -30 -KPX D comma -30 -KPX D Y -70 -KPX D W -40 -KPX D V -40 -KPX D A -40 - -KPX F period -100 -KPX F comma -100 -KPX F a -20 -KPX F A -80 - -KPX J u -20 -KPX J period -20 -KPX J comma -20 -KPX J A -20 - -KPX K y -40 -KPX K u -30 -KPX K o -35 -KPX K e -15 -KPX K O -30 - -KPX L y -30 -KPX L quoteright -140 -KPX L quotedblright -140 -KPX L Y -120 -KPX L W -80 -KPX L V -110 -KPX L T -90 - -KPX O period -40 -KPX O comma -40 -KPX O Y -70 -KPX O X -50 -KPX O W -50 -KPX O V -50 -KPX O T -40 -KPX O A -50 - -KPX P period -120 -KPX P o -40 -KPX P e -30 -KPX P comma -120 -KPX P a -30 -KPX P A -100 - -KPX Q period 20 -KPX Q comma 20 -KPX Q U -10 - -KPX R Y -50 -KPX R W -40 -KPX R V -50 -KPX R U -20 -KPX R T -20 -KPX R O -20 - -KPX T y -60 -KPX T w -60 -KPX T u -90 -KPX T semicolon -40 -KPX T r -80 -KPX T period -80 -KPX T o -80 -KPX T hyphen -120 -KPX T e -60 -KPX T comma -80 -KPX T colon -40 -KPX T a -80 -KPX T O -40 -KPX T A -90 - -KPX U period -30 -KPX U comma -30 -KPX U A -50 - -KPX V u -60 -KPX V semicolon -40 -KPX V period -120 -KPX V o -90 -KPX V hyphen -80 -KPX V e -50 -KPX V comma -120 -KPX V colon -40 -KPX V a -60 -KPX V O -50 -KPX V G -50 -KPX V A -80 - -KPX W y -20 -KPX W u -45 -KPX W semicolon -10 -KPX W period -80 -KPX W o -60 -KPX W hyphen -40 -KPX W e -35 -KPX W comma -80 -KPX W colon -10 -KPX W a -40 -KPX W O -20 -KPX W A -60 - -KPX Y u -100 -KPX Y semicolon -50 -KPX Y period -100 -KPX Y o -100 -KPX Y e -80 -KPX Y comma -100 -KPX Y colon -50 -KPX Y a -90 -KPX Y O -70 -KPX Y A -110 - -KPX a y -20 -KPX a w -15 -KPX a v -15 -KPX a g -10 - -KPX b y -20 -KPX b v -20 -KPX b u -20 -KPX b l -10 - -KPX c y -10 -KPX c l -20 -KPX c k -20 -KPX c h -10 - -KPX colon space -40 - -KPX comma space -40 -KPX comma quoteright -120 -KPX comma quotedblright -120 - -KPX d y -15 -KPX d w -15 -KPX d v -15 -KPX d d -10 - -KPX e y -15 -KPX e x -15 -KPX e w -15 -KPX e v -15 -KPX e period 20 -KPX e comma 10 - -KPX f quoteright 30 -KPX f quotedblright 30 -KPX f period -10 -KPX f o -20 -KPX f e -10 -KPX f comma -10 - -KPX g g -10 -KPX g e 10 - -KPX h y -20 - -KPX k o -15 - -KPX l y -15 -KPX l w -15 - -KPX m y -30 -KPX m u -20 - -KPX n y -20 -KPX n v -40 -KPX n u -10 - -KPX o y -20 -KPX o x -30 -KPX o w -15 -KPX o v -20 - -KPX p y -15 - -KPX period space -40 -KPX period quoteright -120 -KPX period quotedblright -120 - -KPX quotedblright space -80 - -KPX quoteleft quoteleft -46 - -KPX quoteright v -20 -KPX quoteright space -80 -KPX quoteright s -60 -KPX quoteright r -40 -KPX quoteright quoteright -46 -KPX quoteright l -20 -KPX quoteright d -80 - -KPX r y 10 -KPX r v 10 -KPX r t 20 -KPX r s -15 -KPX r q -20 -KPX r period -60 -KPX r o -20 -KPX r hyphen -20 -KPX r g -15 -KPX r d -20 -KPX r comma -60 -KPX r c -20 - -KPX s w -15 - -KPX semicolon space -40 - -KPX space quoteleft -60 -KPX space quotedblleft -80 -KPX space Y -120 -KPX space W -80 -KPX space V -80 -KPX space T -100 - -KPX v period -80 -KPX v o -30 -KPX v comma -80 -KPX v a -20 - -KPX w period -40 -KPX w o -20 -KPX w comma -40 - -KPX x e -10 - -KPX y period -80 -KPX y o -25 -KPX y e -10 -KPX y comma -80 -KPX y a -30 - -KPX z e 10 -EndKernPairs -EndKernData -StartComposites 58 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 235 186 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 235 186 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 235 186 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 235 186 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 235 186 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 235 186 ; -CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 215 0 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 207 186 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 207 186 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 207 186 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 207 186 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 13 186 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 13 186 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 13 186 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 13 186 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 235 186 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 263 186 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 263 186 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 263 186 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 263 186 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 263 186 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 207 186 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 235 186 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 235 186 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 235 186 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 235 186 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 207 186 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 207 186 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 179 186 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 112 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 112 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 112 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 112 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 112 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 112 0 ; -CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 132 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 112 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 112 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 112 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 112 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -27 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -27 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -27 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -27 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 139 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 139 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 139 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 139 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 139 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 139 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 112 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 139 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 139 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 139 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 139 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 112 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 112 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 84 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvbo8an.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvbo8an.afm deleted file mode 100644 index 1a38001..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvbo8an.afm +++ /dev/null @@ -1,570 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Thu Mar 15 12:08:57 1990 -Comment UniqueID 28407 -Comment VMusage 7614 43068 -FontName Helvetica-Narrow-BoldOblique -FullName Helvetica Narrow Bold Oblique -FamilyName Helvetica -Weight Bold -ItalicAngle -12 -IsFixedPitch false -FontBBox -143 -228 913 962 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.007 -Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved.Helvetica is a trademark of Linotype AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 718 -XHeight 532 -Ascender 718 -Descender -207 -StartCharMetrics 228 -C 32 ; WX 228 ; N space ; B 0 0 0 0 ; -C 33 ; WX 273 ; N exclam ; B 77 0 325 718 ; -C 34 ; WX 389 ; N quotedbl ; B 158 447 433 718 ; -C 35 ; WX 456 ; N numbersign ; B 49 0 528 698 ; -C 36 ; WX 456 ; N dollar ; B 55 -115 510 775 ; -C 37 ; WX 729 ; N percent ; B 112 -19 739 710 ; -C 38 ; WX 592 ; N ampersand ; B 73 -19 600 718 ; -C 39 ; WX 228 ; N quoteright ; B 137 445 297 718 ; -C 40 ; WX 273 ; N parenleft ; B 62 -208 385 734 ; -C 41 ; WX 273 ; N parenright ; B -21 -208 302 734 ; -C 42 ; WX 319 ; N asterisk ; B 120 387 394 718 ; -C 43 ; WX 479 ; N plus ; B 67 0 500 506 ; -C 44 ; WX 228 ; N comma ; B 23 -168 201 146 ; -C 45 ; WX 273 ; N hyphen ; B 60 215 311 345 ; -C 46 ; WX 228 ; N period ; B 52 0 201 146 ; -C 47 ; WX 228 ; N slash ; B -30 -19 383 737 ; -C 48 ; WX 456 ; N zero ; B 71 -19 506 710 ; -C 49 ; WX 456 ; N one ; B 142 0 434 710 ; -C 50 ; WX 456 ; N two ; B 21 0 508 710 ; -C 51 ; WX 456 ; N three ; B 54 -19 499 710 ; -C 52 ; WX 456 ; N four ; B 50 0 490 710 ; -C 53 ; WX 456 ; N five ; B 53 -19 522 698 ; -C 54 ; WX 456 ; N six ; B 70 -19 507 710 ; -C 55 ; WX 456 ; N seven ; B 102 0 555 698 ; -C 56 ; WX 456 ; N eight ; B 57 -19 505 710 ; -C 57 ; WX 456 ; N nine ; B 64 -19 504 710 ; -C 58 ; WX 273 ; N colon ; B 75 0 288 512 ; -C 59 ; WX 273 ; N semicolon ; B 46 -168 288 512 ; -C 60 ; WX 479 ; N less ; B 67 -8 537 514 ; -C 61 ; WX 479 ; N equal ; B 48 87 519 419 ; -C 62 ; WX 479 ; N greater ; B 30 -8 500 514 ; -C 63 ; WX 501 ; N question ; B 135 0 550 727 ; -C 64 ; WX 800 ; N at ; B 152 -19 782 737 ; -C 65 ; WX 592 ; N A ; B 16 0 576 718 ; -C 66 ; WX 592 ; N B ; B 62 0 626 718 ; -C 67 ; WX 592 ; N C ; B 88 -19 647 737 ; -C 68 ; WX 592 ; N D ; B 62 0 637 718 ; -C 69 ; WX 547 ; N E ; B 62 0 620 718 ; -C 70 ; WX 501 ; N F ; B 62 0 606 718 ; -C 71 ; WX 638 ; N G ; B 89 -19 670 737 ; -C 72 ; WX 592 ; N H ; B 58 0 659 718 ; -C 73 ; WX 228 ; N I ; B 52 0 301 718 ; -C 74 ; WX 456 ; N J ; B 49 -18 522 718 ; -C 75 ; WX 592 ; N K ; B 71 0 703 718 ; -C 76 ; WX 501 ; N L ; B 62 0 501 718 ; -C 77 ; WX 683 ; N M ; B 57 0 752 718 ; -C 78 ; WX 592 ; N N ; B 57 0 661 718 ; -C 79 ; WX 638 ; N O ; B 88 -19 675 737 ; -C 80 ; WX 547 ; N P ; B 62 0 605 718 ; -C 81 ; WX 638 ; N Q ; B 88 -52 675 737 ; -C 82 ; WX 592 ; N R ; B 62 0 638 718 ; -C 83 ; WX 547 ; N S ; B 66 -19 588 737 ; -C 84 ; WX 501 ; N T ; B 114 0 615 718 ; -C 85 ; WX 592 ; N U ; B 96 -19 659 718 ; -C 86 ; WX 547 ; N V ; B 141 0 656 718 ; -C 87 ; WX 774 ; N W ; B 138 0 887 718 ; -C 88 ; WX 547 ; N X ; B 11 0 648 718 ; -C 89 ; WX 547 ; N Y ; B 137 0 661 718 ; -C 90 ; WX 501 ; N Z ; B 20 0 604 718 ; -C 91 ; WX 273 ; N bracketleft ; B 17 -196 379 722 ; -C 92 ; WX 228 ; N backslash ; B 101 -19 252 737 ; -C 93 ; WX 273 ; N bracketright ; B -14 -196 347 722 ; -C 94 ; WX 479 ; N asciicircum ; B 107 323 484 698 ; -C 95 ; WX 456 ; N underscore ; B -22 -125 443 -75 ; -C 96 ; WX 228 ; N quoteleft ; B 136 454 296 727 ; -C 97 ; WX 456 ; N a ; B 45 -14 478 546 ; -C 98 ; WX 501 ; N b ; B 50 -14 529 718 ; -C 99 ; WX 456 ; N c ; B 65 -14 491 546 ; -C 100 ; WX 501 ; N d ; B 67 -14 577 718 ; -C 101 ; WX 456 ; N e ; B 58 -14 486 546 ; -C 102 ; WX 273 ; N f ; B 71 0 385 727 ; L i fi ; L l fl ; -C 103 ; WX 501 ; N g ; B 31 -217 546 546 ; -C 104 ; WX 501 ; N h ; B 53 0 516 718 ; -C 105 ; WX 228 ; N i ; B 57 0 298 725 ; -C 106 ; WX 228 ; N j ; B -35 -214 298 725 ; -C 107 ; WX 456 ; N k ; B 57 0 549 718 ; -C 108 ; WX 228 ; N l ; B 57 0 297 718 ; -C 109 ; WX 729 ; N m ; B 52 0 746 546 ; -C 110 ; WX 501 ; N n ; B 53 0 516 546 ; -C 111 ; WX 501 ; N o ; B 67 -14 527 546 ; -C 112 ; WX 501 ; N p ; B 15 -207 529 546 ; -C 113 ; WX 501 ; N q ; B 66 -207 545 546 ; -C 114 ; WX 319 ; N r ; B 52 0 401 546 ; -C 115 ; WX 456 ; N s ; B 52 -14 479 546 ; -C 116 ; WX 273 ; N t ; B 82 -6 346 676 ; -C 117 ; WX 501 ; N u ; B 80 -14 540 532 ; -C 118 ; WX 456 ; N v ; B 103 0 538 532 ; -C 119 ; WX 638 ; N w ; B 101 0 723 532 ; -C 120 ; WX 456 ; N x ; B 12 0 531 532 ; -C 121 ; WX 456 ; N y ; B 34 -214 535 532 ; -C 122 ; WX 410 ; N z ; B 16 0 478 532 ; -C 123 ; WX 319 ; N braceleft ; B 77 -196 425 722 ; -C 124 ; WX 230 ; N bar ; B 66 -19 289 737 ; -C 125 ; WX 319 ; N braceright ; B -14 -196 333 722 ; -C 126 ; WX 479 ; N asciitilde ; B 94 163 473 343 ; -C 161 ; WX 273 ; N exclamdown ; B 41 -186 290 532 ; -C 162 ; WX 456 ; N cent ; B 65 -118 491 628 ; -C 163 ; WX 456 ; N sterling ; B 41 -16 520 718 ; -C 164 ; WX 137 ; N fraction ; B -143 -19 399 710 ; -C 165 ; WX 456 ; N yen ; B 49 0 585 698 ; -C 166 ; WX 456 ; N florin ; B -41 -210 548 737 ; -C 167 ; WX 456 ; N section ; B 50 -184 491 727 ; -C 168 ; WX 456 ; N currency ; B 22 76 558 636 ; -C 169 ; WX 195 ; N quotesingle ; B 135 447 263 718 ; -C 170 ; WX 410 ; N quotedblleft ; B 132 454 482 727 ; -C 171 ; WX 456 ; N guillemotleft ; B 111 76 468 484 ; -C 172 ; WX 273 ; N guilsinglleft ; B 106 76 289 484 ; -C 173 ; WX 273 ; N guilsinglright ; B 81 76 264 484 ; -C 174 ; WX 501 ; N fi ; B 71 0 571 727 ; -C 175 ; WX 501 ; N fl ; B 71 0 570 727 ; -C 177 ; WX 456 ; N endash ; B 40 227 514 333 ; -C 178 ; WX 456 ; N dagger ; B 97 -171 513 718 ; -C 179 ; WX 456 ; N daggerdbl ; B 38 -171 515 718 ; -C 180 ; WX 228 ; N periodcentered ; B 90 172 226 334 ; -C 182 ; WX 456 ; N paragraph ; B 80 -191 564 700 ; -C 183 ; WX 287 ; N bullet ; B 68 194 345 524 ; -C 184 ; WX 228 ; N quotesinglbase ; B 34 -146 194 127 ; -C 185 ; WX 410 ; N quotedblbase ; B 29 -146 380 127 ; -C 186 ; WX 410 ; N quotedblright ; B 132 445 483 718 ; -C 187 ; WX 456 ; N guillemotright ; B 85 76 443 484 ; -C 188 ; WX 820 ; N ellipsis ; B 75 0 770 146 ; -C 189 ; WX 820 ; N perthousand ; B 62 -19 851 710 ; -C 191 ; WX 501 ; N questiondown ; B 44 -195 459 532 ; -C 193 ; WX 273 ; N grave ; B 112 604 290 750 ; -C 194 ; WX 273 ; N acute ; B 194 604 423 750 ; -C 195 ; WX 273 ; N circumflex ; B 97 604 387 750 ; -C 196 ; WX 273 ; N tilde ; B 92 610 415 737 ; -C 197 ; WX 273 ; N macron ; B 100 604 396 678 ; -C 198 ; WX 273 ; N breve ; B 128 604 405 750 ; -C 199 ; WX 273 ; N dotaccent ; B 192 614 316 729 ; -C 200 ; WX 273 ; N dieresis ; B 112 614 395 729 ; -C 202 ; WX 273 ; N ring ; B 164 568 344 776 ; -C 203 ; WX 273 ; N cedilla ; B -30 -228 180 0 ; -C 205 ; WX 273 ; N hungarumlaut ; B 113 604 529 750 ; -C 206 ; WX 273 ; N ogonek ; B 33 -228 216 0 ; -C 207 ; WX 273 ; N caron ; B 123 604 412 750 ; -C 208 ; WX 820 ; N emdash ; B 40 227 878 333 ; -C 225 ; WX 820 ; N AE ; B 4 0 902 718 ; -C 227 ; WX 303 ; N ordfeminine ; B 75 276 381 737 ; -C 232 ; WX 501 ; N Lslash ; B 28 0 501 718 ; -C 233 ; WX 638 ; N Oslash ; B 29 -27 733 745 ; -C 234 ; WX 820 ; N OE ; B 81 -19 913 737 ; -C 235 ; WX 299 ; N ordmasculine ; B 75 276 398 737 ; -C 241 ; WX 729 ; N ae ; B 46 -14 757 546 ; -C 245 ; WX 228 ; N dotlessi ; B 57 0 264 532 ; -C 248 ; WX 228 ; N lslash ; B 33 0 334 718 ; -C 249 ; WX 501 ; N oslash ; B 18 -29 575 560 ; -C 250 ; WX 774 ; N oe ; B 67 -14 801 546 ; -C 251 ; WX 501 ; N germandbls ; B 57 -14 539 731 ; -C -1 ; WX 501 ; N Zcaron ; B 20 0 604 936 ; -C -1 ; WX 456 ; N ccedilla ; B 65 -228 491 546 ; -C -1 ; WX 456 ; N ydieresis ; B 34 -214 535 729 ; -C -1 ; WX 456 ; N atilde ; B 45 -14 507 737 ; -C -1 ; WX 228 ; N icircumflex ; B 57 0 364 750 ; -C -1 ; WX 273 ; N threesuperior ; B 75 271 361 710 ; -C -1 ; WX 456 ; N ecircumflex ; B 58 -14 486 750 ; -C -1 ; WX 501 ; N thorn ; B 15 -208 529 718 ; -C -1 ; WX 456 ; N egrave ; B 58 -14 486 750 ; -C -1 ; WX 273 ; N twosuperior ; B 57 283 368 710 ; -C -1 ; WX 456 ; N eacute ; B 58 -14 514 750 ; -C -1 ; WX 501 ; N otilde ; B 67 -14 529 737 ; -C -1 ; WX 592 ; N Aacute ; B 16 0 615 936 ; -C -1 ; WX 501 ; N ocircumflex ; B 67 -14 527 750 ; -C -1 ; WX 456 ; N yacute ; B 34 -214 535 750 ; -C -1 ; WX 501 ; N udieresis ; B 80 -14 540 729 ; -C -1 ; WX 684 ; N threequarters ; B 82 -19 688 710 ; -C -1 ; WX 456 ; N acircumflex ; B 45 -14 478 750 ; -C -1 ; WX 592 ; N Eth ; B 51 0 637 718 ; -C -1 ; WX 456 ; N edieresis ; B 58 -14 487 729 ; -C -1 ; WX 501 ; N ugrave ; B 80 -14 540 750 ; -C -1 ; WX 820 ; N trademark ; B 146 306 909 718 ; -C -1 ; WX 501 ; N ograve ; B 67 -14 527 750 ; -C -1 ; WX 456 ; N scaron ; B 52 -14 504 750 ; -C -1 ; WX 228 ; N Idieresis ; B 52 0 405 915 ; -C -1 ; WX 501 ; N uacute ; B 80 -14 540 750 ; -C -1 ; WX 456 ; N agrave ; B 45 -14 478 750 ; -C -1 ; WX 501 ; N ntilde ; B 53 0 529 737 ; -C -1 ; WX 456 ; N aring ; B 45 -14 478 776 ; -C -1 ; WX 410 ; N zcaron ; B 16 0 481 750 ; -C -1 ; WX 228 ; N Icircumflex ; B 52 0 397 936 ; -C -1 ; WX 592 ; N Ntilde ; B 57 0 661 923 ; -C -1 ; WX 501 ; N ucircumflex ; B 80 -14 540 750 ; -C -1 ; WX 547 ; N Ecircumflex ; B 62 0 620 936 ; -C -1 ; WX 228 ; N Iacute ; B 52 0 433 936 ; -C -1 ; WX 592 ; N Ccedilla ; B 88 -228 647 737 ; -C -1 ; WX 638 ; N Odieresis ; B 88 -19 675 915 ; -C -1 ; WX 547 ; N Scaron ; B 66 -19 588 936 ; -C -1 ; WX 547 ; N Edieresis ; B 62 0 620 915 ; -C -1 ; WX 228 ; N Igrave ; B 52 0 301 936 ; -C -1 ; WX 456 ; N adieresis ; B 45 -14 487 729 ; -C -1 ; WX 638 ; N Ograve ; B 88 -19 675 936 ; -C -1 ; WX 547 ; N Egrave ; B 62 0 620 936 ; -C -1 ; WX 547 ; N Ydieresis ; B 137 0 661 915 ; -C -1 ; WX 604 ; N registered ; B 45 -19 684 737 ; -C -1 ; WX 638 ; N Otilde ; B 88 -19 675 923 ; -C -1 ; WX 684 ; N onequarter ; B 108 -19 661 710 ; -C -1 ; WX 592 ; N Ugrave ; B 96 -19 659 936 ; -C -1 ; WX 592 ; N Ucircumflex ; B 96 -19 659 936 ; -C -1 ; WX 547 ; N Thorn ; B 62 0 588 718 ; -C -1 ; WX 479 ; N divide ; B 67 -42 500 548 ; -C -1 ; WX 592 ; N Atilde ; B 16 0 608 923 ; -C -1 ; WX 592 ; N Uacute ; B 96 -19 659 936 ; -C -1 ; WX 638 ; N Ocircumflex ; B 88 -19 675 936 ; -C -1 ; WX 479 ; N logicalnot ; B 86 108 519 419 ; -C -1 ; WX 592 ; N Aring ; B 16 0 576 962 ; -C -1 ; WX 228 ; N idieresis ; B 57 0 373 729 ; -C -1 ; WX 228 ; N iacute ; B 57 0 400 750 ; -C -1 ; WX 456 ; N aacute ; B 45 -14 514 750 ; -C -1 ; WX 479 ; N plusminus ; B 33 0 512 506 ; -C -1 ; WX 479 ; N multiply ; B 47 1 520 505 ; -C -1 ; WX 592 ; N Udieresis ; B 96 -19 659 915 ; -C -1 ; WX 479 ; N minus ; B 67 197 500 309 ; -C -1 ; WX 273 ; N onesuperior ; B 121 283 318 710 ; -C -1 ; WX 547 ; N Eacute ; B 62 0 620 936 ; -C -1 ; WX 592 ; N Acircumflex ; B 16 0 579 936 ; -C -1 ; WX 604 ; N copyright ; B 46 -19 685 737 ; -C -1 ; WX 592 ; N Agrave ; B 16 0 576 936 ; -C -1 ; WX 501 ; N odieresis ; B 67 -14 527 729 ; -C -1 ; WX 501 ; N oacute ; B 67 -14 537 750 ; -C -1 ; WX 328 ; N degree ; B 143 426 383 712 ; -C -1 ; WX 228 ; N igrave ; B 57 0 268 750 ; -C -1 ; WX 501 ; N mu ; B 18 -207 540 532 ; -C -1 ; WX 638 ; N Oacute ; B 88 -19 675 936 ; -C -1 ; WX 501 ; N eth ; B 67 -14 549 737 ; -C -1 ; WX 592 ; N Adieresis ; B 16 0 588 915 ; -C -1 ; WX 547 ; N Yacute ; B 137 0 661 936 ; -C -1 ; WX 230 ; N brokenbar ; B 66 -19 289 737 ; -C -1 ; WX 684 ; N onehalf ; B 108 -19 704 710 ; -EndCharMetrics -StartKernData -StartKernPairs 209 - -KPX A y -30 -KPX A w -30 -KPX A v -40 -KPX A u -30 -KPX A Y -110 -KPX A W -60 -KPX A V -80 -KPX A U -50 -KPX A T -90 -KPX A Q -40 -KPX A O -40 -KPX A G -50 -KPX A C -40 - -KPX B U -10 -KPX B A -30 - -KPX D period -30 -KPX D comma -30 -KPX D Y -70 -KPX D W -40 -KPX D V -40 -KPX D A -40 - -KPX F period -100 -KPX F comma -100 -KPX F a -20 -KPX F A -80 - -KPX J u -20 -KPX J period -20 -KPX J comma -20 -KPX J A -20 - -KPX K y -40 -KPX K u -30 -KPX K o -35 -KPX K e -15 -KPX K O -30 - -KPX L y -30 -KPX L quoteright -140 -KPX L quotedblright -140 -KPX L Y -120 -KPX L W -80 -KPX L V -110 -KPX L T -90 - -KPX O period -40 -KPX O comma -40 -KPX O Y -70 -KPX O X -50 -KPX O W -50 -KPX O V -50 -KPX O T -40 -KPX O A -50 - -KPX P period -120 -KPX P o -40 -KPX P e -30 -KPX P comma -120 -KPX P a -30 -KPX P A -100 - -KPX Q period 20 -KPX Q comma 20 -KPX Q U -10 - -KPX R Y -50 -KPX R W -40 -KPX R V -50 -KPX R U -20 -KPX R T -20 -KPX R O -20 - -KPX T y -60 -KPX T w -60 -KPX T u -90 -KPX T semicolon -40 -KPX T r -80 -KPX T period -80 -KPX T o -80 -KPX T hyphen -120 -KPX T e -60 -KPX T comma -80 -KPX T colon -40 -KPX T a -80 -KPX T O -40 -KPX T A -90 - -KPX U period -30 -KPX U comma -30 -KPX U A -50 - -KPX V u -60 -KPX V semicolon -40 -KPX V period -120 -KPX V o -90 -KPX V hyphen -80 -KPX V e -50 -KPX V comma -120 -KPX V colon -40 -KPX V a -60 -KPX V O -50 -KPX V G -50 -KPX V A -80 - -KPX W y -20 -KPX W u -45 -KPX W semicolon -10 -KPX W period -80 -KPX W o -60 -KPX W hyphen -40 -KPX W e -35 -KPX W comma -80 -KPX W colon -10 -KPX W a -40 -KPX W O -20 -KPX W A -60 - -KPX Y u -100 -KPX Y semicolon -50 -KPX Y period -100 -KPX Y o -100 -KPX Y e -80 -KPX Y comma -100 -KPX Y colon -50 -KPX Y a -90 -KPX Y O -70 -KPX Y A -110 - -KPX a y -20 -KPX a w -15 -KPX a v -15 -KPX a g -10 - -KPX b y -20 -KPX b v -20 -KPX b u -20 -KPX b l -10 - -KPX c y -10 -KPX c l -20 -KPX c k -20 -KPX c h -10 - -KPX colon space -40 - -KPX comma space -40 -KPX comma quoteright -120 -KPX comma quotedblright -120 - -KPX d y -15 -KPX d w -15 -KPX d v -15 -KPX d d -10 - -KPX e y -15 -KPX e x -15 -KPX e w -15 -KPX e v -15 -KPX e period 20 -KPX e comma 10 - -KPX f quoteright 30 -KPX f quotedblright 30 -KPX f period -10 -KPX f o -20 -KPX f e -10 -KPX f comma -10 - -KPX g g -10 -KPX g e 10 - -KPX h y -20 - -KPX k o -15 - -KPX l y -15 -KPX l w -15 - -KPX m y -30 -KPX m u -20 - -KPX n y -20 -KPX n v -40 -KPX n u -10 - -KPX o y -20 -KPX o x -30 -KPX o w -15 -KPX o v -20 - -KPX p y -15 - -KPX period space -40 -KPX period quoteright -120 -KPX period quotedblright -120 - -KPX quotedblright space -80 - -KPX quoteleft quoteleft -46 - -KPX quoteright v -20 -KPX quoteright space -80 -KPX quoteright s -60 -KPX quoteright r -40 -KPX quoteright quoteright -46 -KPX quoteright l -20 -KPX quoteright d -80 - -KPX r y 10 -KPX r v 10 -KPX r t 20 -KPX r s -15 -KPX r q -20 -KPX r period -60 -KPX r o -20 -KPX r hyphen -20 -KPX r g -15 -KPX r d -20 -KPX r comma -60 -KPX r c -20 - -KPX s w -15 - -KPX semicolon space -40 - -KPX space quoteleft -60 -KPX space quotedblleft -80 -KPX space Y -120 -KPX space W -80 -KPX space V -80 -KPX space T -100 - -KPX v period -80 -KPX v o -30 -KPX v comma -80 -KPX v a -20 - -KPX w period -40 -KPX w o -20 -KPX w comma -40 - -KPX x e -10 - -KPX y period -80 -KPX y o -25 -KPX y e -10 -KPX y comma -80 -KPX y a -30 - -KPX z e 10 -EndKernPairs -EndKernData -StartComposites 58 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 192 186 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 192 186 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 192 186 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 192 186 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 192 186 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 192 186 ; -CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 176 0 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 169 186 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 169 186 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 169 186 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 169 186 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 10 186 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 10 186 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 10 186 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 10 186 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 192 186 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 215 186 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 215 186 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 215 186 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 215 186 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 215 186 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 169 186 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 192 186 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 192 186 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 192 186 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 192 186 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 169 186 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 169 186 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 146 186 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 92 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 92 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 92 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 92 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 92 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 92 0 ; -CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 108 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 92 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 92 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 92 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 92 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -22 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -22 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -22 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -22 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 114 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 114 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 114 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 114 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 114 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 114 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 92 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 114 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 114 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 114 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 114 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 92 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 92 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 69 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvl8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvl8a.afm deleted file mode 100644 index b02ffac..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvl8a.afm +++ /dev/null @@ -1,445 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1988 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date:Mon Jan 11 16:46:06 PST 1988 -FontName Helvetica-Light -EncodingScheme AdobeStandardEncoding -FullName Helvetica Light -FamilyName Helvetica -Weight Light -ItalicAngle 0.0 -IsFixedPitch false -UnderlinePosition -90 -UnderlineThickness 58 -Version 001.002 -Notice Copyright (c) 1985, 1987, 1988 Adobe Systems Incorporated. All Rights Reserved.Helvetica is a trademark of Linotype Company. -FontBBox -164 -212 1000 979 -CapHeight 720 -XHeight 518 -Descender -204 -Ascender 720 -StartCharMetrics 228 -C 32 ; WX 278 ; N space ; B 0 0 0 0 ; -C 33 ; WX 333 ; N exclam ; B 130 0 203 720 ; -C 34 ; WX 278 ; N quotedbl ; B 57 494 220 720 ; -C 35 ; WX 556 ; N numbersign ; B 27 0 530 698 ; -C 36 ; WX 556 ; N dollar ; B 37 -95 518 766 ; -C 37 ; WX 889 ; N percent ; B 67 -14 821 705 ; -C 38 ; WX 667 ; N ampersand ; B 41 -19 644 720 ; -C 39 ; WX 222 ; N quoteright ; B 80 495 153 720 ; -C 40 ; WX 333 ; N parenleft ; B 55 -191 277 739 ; -C 41 ; WX 333 ; N parenright ; B 56 -191 278 739 ; -C 42 ; WX 389 ; N asterisk ; B 44 434 344 720 ; -C 43 ; WX 660 ; N plus ; B 80 0 580 500 ; -C 44 ; WX 278 ; N comma ; B 102 -137 175 88 ; -C 45 ; WX 333 ; N hyphen ; B 40 229 293 291 ; -C 46 ; WX 278 ; N period ; B 102 0 175 88 ; -C 47 ; WX 278 ; N slash ; B -3 -90 288 739 ; -C 48 ; WX 556 ; N zero ; B 39 -14 516 705 ; -C 49 ; WX 556 ; N one ; B 120 0 366 705 ; -C 50 ; WX 556 ; N two ; B 48 0 515 705 ; -C 51 ; WX 556 ; N three ; B 34 -14 512 705 ; -C 52 ; WX 556 ; N four ; B 36 0 520 698 ; -C 53 ; WX 556 ; N five ; B 35 -14 506 698 ; -C 54 ; WX 556 ; N six ; B 41 -14 514 705 ; -C 55 ; WX 556 ; N seven ; B 59 0 508 698 ; -C 56 ; WX 556 ; N eight ; B 44 -14 512 705 ; -C 57 ; WX 556 ; N nine ; B 41 -14 515 705 ; -C 58 ; WX 278 ; N colon ; B 102 0 175 492 ; -C 59 ; WX 278 ; N semicolon ; B 102 -137 175 492 ; -C 60 ; WX 660 ; N less ; B 80 -6 580 505 ; -C 61 ; WX 660 ; N equal ; B 80 124 580 378 ; -C 62 ; WX 660 ; N greater ; B 80 -6 580 505 ; -C 63 ; WX 500 ; N question ; B 37 0 472 739 ; -C 64 ; WX 800 ; N at ; B 40 -19 760 739 ; -C 65 ; WX 667 ; N A ; B 15 0 651 720 ; -C 66 ; WX 667 ; N B ; B 81 0 610 720 ; -C 67 ; WX 722 ; N C ; B 48 -19 670 739 ; -C 68 ; WX 722 ; N D ; B 81 0 669 720 ; -C 69 ; WX 611 ; N E ; B 81 0 570 720 ; -C 70 ; WX 556 ; N F ; B 74 0 538 720 ; -C 71 ; WX 778 ; N G ; B 53 -19 695 739 ; -C 72 ; WX 722 ; N H ; B 80 0 642 720 ; -C 73 ; WX 278 ; N I ; B 105 0 173 720 ; -C 74 ; WX 500 ; N J ; B 22 -19 415 720 ; -C 75 ; WX 667 ; N K ; B 85 0 649 720 ; -C 76 ; WX 556 ; N L ; B 81 0 535 720 ; -C 77 ; WX 833 ; N M ; B 78 0 755 720 ; -C 78 ; WX 722 ; N N ; B 79 0 642 720 ; -C 79 ; WX 778 ; N O ; B 53 -19 724 739 ; -C 80 ; WX 611 ; N P ; B 78 0 576 720 ; -C 81 ; WX 778 ; N Q ; B 48 -52 719 739 ; -C 82 ; WX 667 ; N R ; B 80 0 612 720 ; -C 83 ; WX 611 ; N S ; B 43 -19 567 739 ; -C 84 ; WX 556 ; N T ; B 16 0 540 720 ; -C 85 ; WX 722 ; N U ; B 82 -19 640 720 ; -C 86 ; WX 611 ; N V ; B 18 0 593 720 ; -C 87 ; WX 889 ; N W ; B 14 0 875 720 ; -C 88 ; WX 611 ; N X ; B 18 0 592 720 ; -C 89 ; WX 611 ; N Y ; B 12 0 598 720 ; -C 90 ; WX 611 ; N Z ; B 31 0 579 720 ; -C 91 ; WX 333 ; N bracketleft ; B 91 -191 282 739 ; -C 92 ; WX 278 ; N backslash ; B -46 0 324 739 ; -C 93 ; WX 333 ; N bracketright ; B 51 -191 242 739 ; -C 94 ; WX 660 ; N asciicircum ; B 73 245 586 698 ; -C 95 ; WX 500 ; N underscore ; B 0 -119 500 -61 ; -C 96 ; WX 222 ; N quoteleft ; B 69 495 142 720 ; -C 97 ; WX 556 ; N a ; B 46 -14 534 532 ; -C 98 ; WX 611 ; N b ; B 79 -14 555 720 ; -C 99 ; WX 556 ; N c ; B 47 -14 508 532 ; -C 100 ; WX 611 ; N d ; B 56 -14 532 720 ; -C 101 ; WX 556 ; N e ; B 45 -14 511 532 ; -C 102 ; WX 278 ; N f ; B 20 0 257 734 ; L i fi ; L l fl ; -C 103 ; WX 611 ; N g ; B 56 -212 532 532 ; -C 104 ; WX 556 ; N h ; B 72 0 483 720 ; -C 105 ; WX 222 ; N i ; B 78 0 144 720 ; -C 106 ; WX 222 ; N j ; B 5 -204 151 720 ; -C 107 ; WX 500 ; N k ; B 68 0 487 720 ; -C 108 ; WX 222 ; N l ; B 81 0 141 720 ; -C 109 ; WX 833 ; N m ; B 64 0 768 532 ; -C 110 ; WX 556 ; N n ; B 72 0 483 532 ; -C 111 ; WX 556 ; N o ; B 38 -14 518 532 ; -C 112 ; WX 611 ; N p ; B 79 -204 555 532 ; -C 113 ; WX 611 ; N q ; B 56 -204 532 532 ; -C 114 ; WX 333 ; N r ; B 75 0 306 532 ; -C 115 ; WX 500 ; N s ; B 46 -14 454 532 ; -C 116 ; WX 278 ; N t ; B 20 -14 254 662 ; -C 117 ; WX 556 ; N u ; B 72 -14 483 518 ; -C 118 ; WX 500 ; N v ; B 17 0 483 518 ; -C 119 ; WX 722 ; N w ; B 15 0 707 518 ; -C 120 ; WX 500 ; N x ; B 18 0 481 518 ; -C 121 ; WX 500 ; N y ; B 18 -204 482 518 ; -C 122 ; WX 500 ; N z ; B 33 0 467 518 ; -C 123 ; WX 333 ; N braceleft ; B 45 -191 279 739 ; -C 124 ; WX 222 ; N bar ; B 81 0 141 739 ; -C 125 ; WX 333 ; N braceright ; B 51 -187 285 743 ; -C 126 ; WX 660 ; N asciitilde ; B 80 174 580 339 ; -C 161 ; WX 333 ; N exclamdown ; B 130 -187 203 532 ; -C 162 ; WX 556 ; N cent ; B 45 -141 506 647 ; -C 163 ; WX 556 ; N sterling ; B 25 -14 530 705 ; -C 164 ; WX 167 ; N fraction ; B -164 -14 331 705 ; -C 165 ; WX 556 ; N yen ; B 4 0 552 720 ; -C 166 ; WX 556 ; N florin ; B 13 -196 539 734 ; -C 167 ; WX 556 ; N section ; B 48 -181 508 739 ; -C 168 ; WX 556 ; N currency ; B 27 50 529 553 ; -C 169 ; WX 222 ; N quotesingle ; B 85 494 137 720 ; -C 170 ; WX 389 ; N quotedblleft ; B 86 495 310 720 ; -C 171 ; WX 556 ; N guillemotleft ; B 113 117 443 404 ; -C 172 ; WX 389 ; N guilsinglleft ; B 121 117 267 404 ; -C 173 ; WX 389 ; N guilsinglright ; B 122 117 268 404 ; -C 174 ; WX 500 ; N fi ; B 13 0 435 734 ; -C 175 ; WX 500 ; N fl ; B 13 0 432 734 ; -C 177 ; WX 500 ; N endash ; B 0 238 500 282 ; -C 178 ; WX 556 ; N dagger ; B 37 -166 519 720 ; -C 179 ; WX 556 ; N daggerdbl ; B 37 -166 519 720 ; -C 180 ; WX 278 ; N periodcentered ; B 90 301 187 398 ; -C 182 ; WX 650 ; N paragraph ; B 66 -146 506 720 ; -C 183 ; WX 500 ; N bullet ; B 70 180 430 540 ; -C 184 ; WX 222 ; N quotesinglbase ; B 80 -137 153 88 ; -C 185 ; WX 389 ; N quotedblbase ; B 79 -137 303 88 ; -C 186 ; WX 389 ; N quotedblright ; B 79 495 303 720 ; -C 187 ; WX 556 ; N guillemotright ; B 113 117 443 404 ; -C 188 ; WX 1000 ; N ellipsis ; B 131 0 870 88 ; -C 189 ; WX 1000 ; N perthousand ; B 14 -14 985 705 ; -C 191 ; WX 500 ; N questiondown ; B 28 -207 463 532 ; -C 193 ; WX 333 ; N grave ; B 45 574 234 713 ; -C 194 ; WX 333 ; N acute ; B 109 574 297 713 ; -C 195 ; WX 333 ; N circumflex ; B 24 574 318 713 ; -C 196 ; WX 333 ; N tilde ; B 16 586 329 688 ; -C 197 ; WX 333 ; N macron ; B 23 612 319 657 ; -C 198 ; WX 333 ; N breve ; B 28 580 316 706 ; -C 199 ; WX 333 ; N dotaccent ; B 134 584 199 686 ; -C 200 ; WX 333 ; N dieresis ; B 60 584 284 686 ; -C 202 ; WX 333 ; N ring ; B 67 578 266 777 ; -C 203 ; WX 333 ; N cedilla ; B 54 -207 257 0 ; -C 205 ; WX 333 ; N hungarumlaut ; B 109 574 459 713 ; -C 206 ; WX 333 ; N ogonek ; B 74 -190 228 0 ; -C 207 ; WX 333 ; N caron ; B 24 574 318 713 ; -C 208 ; WX 1000 ; N emdash ; B 0 238 1000 282 ; -C 225 ; WX 1000 ; N AE ; B 5 0 960 720 ; -C 227 ; WX 334 ; N ordfeminine ; B 8 307 325 739 ; -C 232 ; WX 556 ; N Lslash ; B 0 0 535 720 ; -C 233 ; WX 778 ; N Oslash ; B 42 -37 736 747 ; -C 234 ; WX 1000 ; N OE ; B 41 -19 967 739 ; -C 235 ; WX 334 ; N ordmasculine ; B 11 307 323 739 ; -C 241 ; WX 889 ; N ae ; B 39 -14 847 532 ; -C 245 ; WX 222 ; N dotlessi ; B 78 0 138 518 ; -C 248 ; WX 222 ; N lslash ; B 10 0 212 720 ; -C 249 ; WX 556 ; N oslash ; B 35 -23 521 541 ; -C 250 ; WX 944 ; N oe ; B 36 -14 904 532 ; -C 251 ; WX 500 ; N germandbls ; B 52 -14 459 734 ; -C -1 ; WX 667 ; N Aacute ; B 15 0 651 915 ; -C -1 ; WX 667 ; N Acircumflex ; B 15 0 651 915 ; -C -1 ; WX 667 ; N Adieresis ; B 15 0 651 888 ; -C -1 ; WX 667 ; N Agrave ; B 15 0 651 915 ; -C -1 ; WX 667 ; N Aring ; B 15 0 651 979 ; -C -1 ; WX 667 ; N Atilde ; B 15 0 651 890 ; -C -1 ; WX 722 ; N Ccedilla ; B 48 -207 670 739 ; -C -1 ; WX 611 ; N Eacute ; B 81 0 570 915 ; -C -1 ; WX 611 ; N Ecircumflex ; B 81 0 570 915 ; -C -1 ; WX 611 ; N Edieresis ; B 81 0 570 888 ; -C -1 ; WX 611 ; N Egrave ; B 81 0 570 915 ; -C -1 ; WX 722 ; N Eth ; B 10 0 669 720 ; -C -1 ; WX 278 ; N Iacute ; B 62 0 250 915 ; -C -1 ; WX 278 ; N Icircumflex ; B -23 0 271 915 ; -C -1 ; WX 278 ; N Idieresis ; B 13 0 237 888 ; -C -1 ; WX 278 ; N Igrave ; B 18 0 207 915 ; -C -1 ; WX 722 ; N Ntilde ; B 79 0 642 890 ; -C -1 ; WX 778 ; N Oacute ; B 53 -19 724 915 ; -C -1 ; WX 778 ; N Ocircumflex ; B 53 -19 724 915 ; -C -1 ; WX 778 ; N Odieresis ; B 53 -19 724 888 ; -C -1 ; WX 778 ; N Ograve ; B 53 -19 724 915 ; -C -1 ; WX 778 ; N Otilde ; B 53 -19 724 890 ; -C -1 ; WX 611 ; N Scaron ; B 43 -19 567 915 ; -C -1 ; WX 611 ; N Thorn ; B 78 0 576 720 ; -C -1 ; WX 722 ; N Uacute ; B 82 -19 640 915 ; -C -1 ; WX 722 ; N Ucircumflex ; B 82 -19 640 915 ; -C -1 ; WX 722 ; N Udieresis ; B 82 -19 640 888 ; -C -1 ; WX 722 ; N Ugrave ; B 82 -19 640 915 ; -C -1 ; WX 611 ; N Yacute ; B 12 0 598 915 ; -C -1 ; WX 611 ; N Ydieresis ; B 12 0 598 888 ; -C -1 ; WX 611 ; N Zcaron ; B 31 0 579 915 ; -C -1 ; WX 556 ; N aacute ; B 46 -14 534 713 ; -C -1 ; WX 556 ; N acircumflex ; B 46 -14 534 713 ; -C -1 ; WX 556 ; N adieresis ; B 46 -14 534 686 ; -C -1 ; WX 556 ; N agrave ; B 46 -14 534 713 ; -C -1 ; WX 556 ; N aring ; B 46 -14 534 777 ; -C -1 ; WX 556 ; N atilde ; B 46 -14 534 688 ; -C -1 ; WX 222 ; N brokenbar ; B 81 0 141 739 ; -C -1 ; WX 556 ; N ccedilla ; B 47 -207 508 532 ; -C -1 ; WX 800 ; N copyright ; B 21 -19 779 739 ; -C -1 ; WX 400 ; N degree ; B 50 405 350 705 ; -C -1 ; WX 660 ; N divide ; B 80 0 580 500 ; -C -1 ; WX 556 ; N eacute ; B 45 -14 511 713 ; -C -1 ; WX 556 ; N ecircumflex ; B 45 -14 511 713 ; -C -1 ; WX 556 ; N edieresis ; B 45 -14 511 686 ; -C -1 ; WX 556 ; N egrave ; B 45 -14 511 713 ; -C -1 ; WX 556 ; N eth ; B 38 -14 518 739 ; -C -1 ; WX 222 ; N iacute ; B 34 0 222 713 ; -C -1 ; WX 222 ; N icircumflex ; B -51 0 243 713 ; -C -1 ; WX 222 ; N idieresis ; B -15 0 209 686 ; -C -1 ; WX 222 ; N igrave ; B -10 0 179 713 ; -C -1 ; WX 660 ; N logicalnot ; B 80 112 580 378 ; -C -1 ; WX 660 ; N minus ; B 80 220 580 280 ; -C -1 ; WX 556 ; N mu ; B 72 -204 483 518 ; -C -1 ; WX 660 ; N multiply ; B 83 6 578 500 ; -C -1 ; WX 556 ; N ntilde ; B 72 0 483 688 ; -C -1 ; WX 556 ; N oacute ; B 38 -14 518 713 ; -C -1 ; WX 556 ; N ocircumflex ; B 38 -14 518 713 ; -C -1 ; WX 556 ; N odieresis ; B 38 -14 518 686 ; -C -1 ; WX 556 ; N ograve ; B 38 -14 518 713 ; -C -1 ; WX 834 ; N onehalf ; B 40 -14 794 739 ; -C -1 ; WX 834 ; N onequarter ; B 40 -14 794 739 ; -C -1 ; WX 333 ; N onesuperior ; B 87 316 247 739 ; -C -1 ; WX 556 ; N otilde ; B 38 -14 518 688 ; -C -1 ; WX 660 ; N plusminus ; B 80 0 580 500 ; -C -1 ; WX 800 ; N registered ; B 21 -19 779 739 ; -C -1 ; WX 500 ; N scaron ; B 46 -14 454 713 ; -C -1 ; WX 611 ; N thorn ; B 79 -204 555 720 ; -C -1 ; WX 834 ; N threequarters ; B 40 -14 794 739 ; -C -1 ; WX 333 ; N threesuperior ; B 11 308 322 739 ; -C -1 ; WX 940 ; N trademark ; B 29 299 859 720 ; -C -1 ; WX 333 ; N twosuperior ; B 15 316 318 739 ; -C -1 ; WX 556 ; N uacute ; B 72 -14 483 713 ; -C -1 ; WX 556 ; N ucircumflex ; B 72 -14 483 713 ; -C -1 ; WX 556 ; N udieresis ; B 72 -14 483 686 ; -C -1 ; WX 556 ; N ugrave ; B 72 -14 483 713 ; -C -1 ; WX 500 ; N yacute ; B 18 -204 482 713 ; -C -1 ; WX 500 ; N ydieresis ; B 18 -204 482 686 ; -C -1 ; WX 500 ; N zcaron ; B 33 0 467 713 ; -EndCharMetrics -StartKernData -StartKernPairs 115 - -KPX A y -18 -KPX A w -18 -KPX A v -18 -KPX A quoteright -74 -KPX A Y -74 -KPX A W -37 -KPX A V -74 -KPX A T -92 - -KPX F period -129 -KPX F comma -129 -KPX F A -55 - -KPX L y -37 -KPX L quoteright -74 -KPX L Y -111 -KPX L W -55 -KPX L V -92 -KPX L T -92 - -KPX P period -129 -KPX P comma -129 -KPX P A -74 - -KPX R y 0 -KPX R Y -37 -KPX R W -18 -KPX R V -18 -KPX R T -18 - -KPX T y -84 -KPX T w -84 -KPX T u -92 -KPX T semicolon -111 -KPX T s -111 -KPX T r -92 -KPX T period -111 -KPX T o -111 -KPX T i 0 -KPX T hyphen -129 -KPX T e -111 -KPX T comma -111 -KPX T colon -111 -KPX T c -111 -KPX T a -111 -KPX T A -92 - -KPX V y -18 -KPX V u -37 -KPX V semicolon -74 -KPX V r -37 -KPX V period -129 -KPX V o -55 -KPX V i -18 -KPX V hyphen -55 -KPX V e -55 -KPX V comma -129 -KPX V colon -74 -KPX V a -55 -KPX V A -74 - -KPX W y 0 -KPX W u -18 -KPX W semicolon -18 -KPX W r -18 -KPX W period -74 -KPX W o -18 -KPX W i 0 -KPX W hyphen 0 -KPX W e -18 -KPX W comma -74 -KPX W colon -18 -KPX W a -37 -KPX W A -37 - -KPX Y v -40 -KPX Y u -37 -KPX Y semicolon -92 -KPX Y q -92 -KPX Y period -111 -KPX Y p -37 -KPX Y o -92 -KPX Y i -20 -KPX Y hyphen -111 -KPX Y e -92 -KPX Y comma -111 -KPX Y colon -92 -KPX Y a -92 -KPX Y A -74 - -KPX f quoteright 18 -KPX f f -18 - -KPX quoteleft quoteleft -18 - -KPX quoteright t -18 -KPX quoteright s -74 -KPX quoteright quoteright -18 - -KPX r z 0 -KPX r y 18 -KPX r x 0 -KPX r w 0 -KPX r v 0 -KPX r u 0 -KPX r t 18 -KPX r r 0 -KPX r quoteright 0 -KPX r q -18 -KPX r period -92 -KPX r o -18 -KPX r n 18 -KPX r m 18 -KPX r hyphen -55 -KPX r h 0 -KPX r g 0 -KPX r f 18 -KPX r e -18 -KPX r d -18 -KPX r comma -92 -KPX r c -18 - -KPX v period -74 -KPX v comma -74 - -KPX w period -55 -KPX w comma -55 - -KPX y period -92 -KPX y comma -92 -EndKernPairs -EndKernData -StartComposites 58 -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 139 202 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 83 0 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 139 202 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 83 0 ; -CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 194 0 ; -CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 111 0 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 139 202 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 83 0 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 139 202 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 83 0 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 194 202 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 194 202 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 194 202 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 194 202 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 111 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 111 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 111 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 111 0 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute -47 202 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex -47 202 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis -47 202 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave -27 202 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -75 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -75 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -75 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -55 0 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 139 202 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 139 202 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 139 202 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 139 202 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 111 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 111 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 111 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 111 0 ; -CC Aacute 2 ; PCC A 0 0 ; PCC acute 167 202 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 167 202 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 167 202 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 167 202 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 111 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 111 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 111 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 111 0 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 222 202 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 222 202 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 222 202 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 222 202 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 111 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 111 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 111 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 111 0 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 167 202 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 111 0 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 194 202 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 111 0 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 222 202 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 111 0 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 187 202 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 111 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvlo8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvlo8a.afm deleted file mode 100644 index 96612d1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvlo8a.afm +++ /dev/null @@ -1,445 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1988 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date:Mon Jan 11 17:38:44 PST 1988 -FontName Helvetica-LightOblique -EncodingScheme AdobeStandardEncoding -FullName Helvetica Light Oblique -FamilyName Helvetica -Weight Light -ItalicAngle -12.0 -IsFixedPitch false -UnderlinePosition -90 -UnderlineThickness 58 -Version 001.002 -Notice Copyright (c) 1985, 1987, 1988 Adobe Systems Incorporated. All Rights Reserved.Helvetica is a trademark of Linotype Company. -FontBBox -167 -212 1110 979 -CapHeight 720 -XHeight 518 -Descender -204 -Ascender 720 -StartCharMetrics 228 -C 32 ; WX 278 ; N space ; B 0 0 0 0 ; -C 33 ; WX 333 ; N exclam ; B 130 0 356 720 ; -C 34 ; WX 278 ; N quotedbl ; B 162 494 373 720 ; -C 35 ; WX 556 ; N numbersign ; B 75 0 633 698 ; -C 36 ; WX 556 ; N dollar ; B 75 -95 613 766 ; -C 37 ; WX 889 ; N percent ; B 176 -14 860 705 ; -C 38 ; WX 667 ; N ampersand ; B 77 -19 646 720 ; -C 39 ; WX 222 ; N quoteright ; B 185 495 306 720 ; -C 40 ; WX 333 ; N parenleft ; B 97 -191 434 739 ; -C 41 ; WX 333 ; N parenright ; B 15 -191 353 739 ; -C 42 ; WX 389 ; N asterisk ; B 172 434 472 720 ; -C 43 ; WX 660 ; N plus ; B 127 0 640 500 ; -C 44 ; WX 278 ; N comma ; B 73 -137 194 88 ; -C 45 ; WX 333 ; N hyphen ; B 89 229 355 291 ; -C 46 ; WX 278 ; N period ; B 102 0 194 88 ; -C 47 ; WX 278 ; N slash ; B -22 -90 445 739 ; -C 48 ; WX 556 ; N zero ; B 93 -14 609 705 ; -C 49 ; WX 556 ; N one ; B 231 0 516 705 ; -C 50 ; WX 556 ; N two ; B 48 0 628 705 ; -C 51 ; WX 556 ; N three ; B 74 -14 605 705 ; -C 52 ; WX 556 ; N four ; B 73 0 570 698 ; -C 53 ; WX 556 ; N five ; B 71 -14 616 698 ; -C 54 ; WX 556 ; N six ; B 94 -14 617 705 ; -C 55 ; WX 556 ; N seven ; B 152 0 656 698 ; -C 56 ; WX 556 ; N eight ; B 80 -14 601 705 ; -C 57 ; WX 556 ; N nine ; B 84 -14 607 705 ; -C 58 ; WX 278 ; N colon ; B 102 0 280 492 ; -C 59 ; WX 278 ; N semicolon ; B 73 -137 280 492 ; -C 60 ; WX 660 ; N less ; B 129 -6 687 505 ; -C 61 ; WX 660 ; N equal ; B 106 124 660 378 ; -C 62 ; WX 660 ; N greater ; B 79 -6 640 505 ; -C 63 ; WX 500 ; N question ; B 148 0 594 739 ; -C 64 ; WX 800 ; N at ; B 108 -19 857 739 ; -C 65 ; WX 667 ; N A ; B 15 0 651 720 ; -C 66 ; WX 667 ; N B ; B 81 0 697 720 ; -C 67 ; WX 722 ; N C ; B 111 -19 771 739 ; -C 68 ; WX 722 ; N D ; B 81 0 758 720 ; -C 69 ; WX 611 ; N E ; B 81 0 713 720 ; -C 70 ; WX 556 ; N F ; B 74 0 691 720 ; -C 71 ; WX 778 ; N G ; B 116 -19 796 739 ; -C 72 ; WX 722 ; N H ; B 80 0 795 720 ; -C 73 ; WX 278 ; N I ; B 105 0 326 720 ; -C 74 ; WX 500 ; N J ; B 58 -19 568 720 ; -C 75 ; WX 667 ; N K ; B 85 0 752 720 ; -C 76 ; WX 556 ; N L ; B 81 0 547 720 ; -C 77 ; WX 833 ; N M ; B 78 0 908 720 ; -C 78 ; WX 722 ; N N ; B 79 0 795 720 ; -C 79 ; WX 778 ; N O ; B 117 -19 812 739 ; -C 80 ; WX 611 ; N P ; B 78 0 693 720 ; -C 81 ; WX 778 ; N Q ; B 112 -52 808 739 ; -C 82 ; WX 667 ; N R ; B 80 0 726 720 ; -C 83 ; WX 611 ; N S ; B 82 -19 663 739 ; -C 84 ; WX 556 ; N T ; B 157 0 693 720 ; -C 85 ; WX 722 ; N U ; B 129 -19 793 720 ; -C 86 ; WX 611 ; N V ; B 171 0 746 720 ; -C 87 ; WX 889 ; N W ; B 167 0 1028 720 ; -C 88 ; WX 611 ; N X ; B 18 0 734 720 ; -C 89 ; WX 611 ; N Y ; B 165 0 751 720 ; -C 90 ; WX 611 ; N Z ; B 31 0 729 720 ; -C 91 ; WX 333 ; N bracketleft ; B 50 -191 439 739 ; -C 92 ; WX 278 ; N backslash ; B 111 0 324 739 ; -C 93 ; WX 333 ; N bracketright ; B 10 -191 399 739 ; -C 94 ; WX 660 ; N asciicircum ; B 125 245 638 698 ; -C 95 ; WX 500 ; N underscore ; B -25 -119 487 -61 ; -C 96 ; WX 222 ; N quoteleft ; B 174 495 295 720 ; -C 97 ; WX 556 ; N a ; B 71 -14 555 532 ; -C 98 ; WX 611 ; N b ; B 79 -14 619 720 ; -C 99 ; WX 556 ; N c ; B 92 -14 576 532 ; -C 100 ; WX 611 ; N d ; B 101 -14 685 720 ; -C 101 ; WX 556 ; N e ; B 90 -14 575 532 ; -C 102 ; WX 278 ; N f ; B 97 0 412 734 ; L i fi ; L l fl ; -C 103 ; WX 611 ; N g ; B 56 -212 642 532 ; -C 104 ; WX 556 ; N h ; B 72 0 565 720 ; -C 105 ; WX 222 ; N i ; B 81 0 297 720 ; -C 106 ; WX 222 ; N j ; B -38 -204 304 720 ; -C 107 ; WX 500 ; N k ; B 68 0 574 720 ; -C 108 ; WX 222 ; N l ; B 81 0 294 720 ; -C 109 ; WX 833 ; N m ; B 64 0 848 532 ; -C 110 ; WX 556 ; N n ; B 72 0 565 532 ; -C 111 ; WX 556 ; N o ; B 84 -14 582 532 ; -C 112 ; WX 611 ; N p ; B 36 -204 620 532 ; -C 113 ; WX 611 ; N q ; B 102 -204 642 532 ; -C 114 ; WX 333 ; N r ; B 75 0 419 532 ; -C 115 ; WX 500 ; N s ; B 78 -14 519 532 ; -C 116 ; WX 278 ; N t ; B 108 -14 360 662 ; -C 117 ; WX 556 ; N u ; B 103 -14 593 518 ; -C 118 ; WX 500 ; N v ; B 127 0 593 518 ; -C 119 ; WX 722 ; N w ; B 125 0 817 518 ; -C 120 ; WX 500 ; N x ; B 18 0 584 518 ; -C 121 ; WX 500 ; N y ; B 26 -204 592 518 ; -C 122 ; WX 500 ; N z ; B 33 0 564 518 ; -C 123 ; WX 333 ; N braceleft ; B 103 -191 436 739 ; -C 124 ; WX 222 ; N bar ; B 81 0 298 739 ; -C 125 ; WX 333 ; N braceright ; B 12 -187 344 743 ; -C 126 ; WX 660 ; N asciitilde ; B 127 174 645 339 ; -C 161 ; WX 333 ; N exclamdown ; B 90 -187 316 532 ; -C 162 ; WX 556 ; N cent ; B 90 -141 574 647 ; -C 163 ; WX 556 ; N sterling ; B 51 -14 613 705 ; -C 164 ; WX 167 ; N fraction ; B -167 -14 481 705 ; -C 165 ; WX 556 ; N yen ; B 110 0 705 720 ; -C 166 ; WX 556 ; N florin ; B -26 -196 691 734 ; -C 167 ; WX 556 ; N section ; B 91 -181 581 739 ; -C 168 ; WX 556 ; N currency ; B 55 50 629 553 ; -C 169 ; WX 222 ; N quotesingle ; B 190 494 290 720 ; -C 170 ; WX 389 ; N quotedblleft ; B 191 495 463 720 ; -C 171 ; WX 556 ; N guillemotleft ; B 161 117 529 404 ; -C 172 ; WX 389 ; N guilsinglleft ; B 169 117 353 404 ; -C 173 ; WX 389 ; N guilsinglright ; B 147 117 330 404 ; -C 174 ; WX 500 ; N fi ; B 92 0 588 734 ; -C 175 ; WX 500 ; N fl ; B 92 0 585 734 ; -C 177 ; WX 500 ; N endash ; B 51 238 560 282 ; -C 178 ; WX 556 ; N dagger ; B 130 -166 623 720 ; -C 179 ; WX 556 ; N daggerdbl ; B 49 -166 625 720 ; -C 180 ; WX 278 ; N periodcentered ; B 163 301 262 398 ; -C 182 ; WX 650 ; N paragraph ; B 174 -146 659 720 ; -C 183 ; WX 500 ; N bullet ; B 142 180 510 540 ; -C 184 ; WX 222 ; N quotesinglbase ; B 51 -137 172 88 ; -C 185 ; WX 389 ; N quotedblbase ; B 50 -137 322 88 ; -C 186 ; WX 389 ; N quotedblright ; B 184 495 456 720 ; -C 187 ; WX 556 ; N guillemotright ; B 138 117 505 404 ; -C 188 ; WX 1000 ; N ellipsis ; B 131 0 889 88 ; -C 189 ; WX 1000 ; N perthousand ; B 83 -14 1020 705 ; -C 191 ; WX 500 ; N questiondown ; B 19 -207 465 532 ; -C 193 ; WX 333 ; N grave ; B 197 574 356 713 ; -C 194 ; WX 333 ; N acute ; B 231 574 449 713 ; -C 195 ; WX 333 ; N circumflex ; B 146 574 440 713 ; -C 196 ; WX 333 ; N tilde ; B 141 586 475 688 ; -C 197 ; WX 333 ; N macron ; B 153 612 459 657 ; -C 198 ; WX 333 ; N breve ; B 177 580 466 706 ; -C 199 ; WX 333 ; N dotaccent ; B 258 584 345 686 ; -C 200 ; WX 333 ; N dieresis ; B 184 584 430 686 ; -C 202 ; WX 333 ; N ring ; B 209 578 412 777 ; -C 203 ; WX 333 ; N cedilla ; B 14 -207 233 0 ; -C 205 ; WX 333 ; N hungarumlaut ; B 231 574 611 713 ; -C 206 ; WX 333 ; N ogonek ; B 50 -190 199 0 ; -C 207 ; WX 333 ; N caron ; B 176 574 470 713 ; -C 208 ; WX 1000 ; N emdash ; B 51 238 1060 282 ; -C 225 ; WX 1000 ; N AE ; B 5 0 1101 720 ; -C 227 ; WX 334 ; N ordfeminine ; B 73 307 423 739 ; -C 232 ; WX 556 ; N Lslash ; B 68 0 547 720 ; -C 233 ; WX 778 ; N Oslash ; B 41 -37 887 747 ; -C 234 ; WX 1000 ; N OE ; B 104 -19 1110 739 ; -C 235 ; WX 334 ; N ordmasculine ; B 76 307 450 739 ; -C 241 ; WX 889 ; N ae ; B 63 -14 913 532 ; -C 245 ; WX 222 ; N dotlessi ; B 78 0 248 518 ; -C 248 ; WX 222 ; N lslash ; B 74 0 316 720 ; -C 249 ; WX 556 ; N oslash ; B 36 -23 629 541 ; -C 250 ; WX 944 ; N oe ; B 82 -14 970 532 ; -C 251 ; WX 500 ; N germandbls ; B 52 -14 554 734 ; -C -1 ; WX 667 ; N Aacute ; B 15 0 659 915 ; -C -1 ; WX 667 ; N Acircumflex ; B 15 0 651 915 ; -C -1 ; WX 667 ; N Adieresis ; B 15 0 651 888 ; -C -1 ; WX 667 ; N Agrave ; B 15 0 651 915 ; -C -1 ; WX 667 ; N Aring ; B 15 0 651 979 ; -C -1 ; WX 667 ; N Atilde ; B 15 0 685 890 ; -C -1 ; WX 722 ; N Ccedilla ; B 111 -207 771 739 ; -C -1 ; WX 611 ; N Eacute ; B 81 0 713 915 ; -C -1 ; WX 611 ; N Ecircumflex ; B 81 0 713 915 ; -C -1 ; WX 611 ; N Edieresis ; B 81 0 713 888 ; -C -1 ; WX 611 ; N Egrave ; B 81 0 713 915 ; -C -1 ; WX 722 ; N Eth ; B 81 0 758 720 ; -C -1 ; WX 278 ; N Iacute ; B 105 0 445 915 ; -C -1 ; WX 278 ; N Icircumflex ; B 105 0 436 915 ; -C -1 ; WX 278 ; N Idieresis ; B 105 0 426 888 ; -C -1 ; WX 278 ; N Igrave ; B 105 0 372 915 ; -C -1 ; WX 722 ; N Ntilde ; B 79 0 795 890 ; -C -1 ; WX 778 ; N Oacute ; B 117 -19 812 915 ; -C -1 ; WX 778 ; N Ocircumflex ; B 117 -19 812 915 ; -C -1 ; WX 778 ; N Odieresis ; B 117 -19 812 888 ; -C -1 ; WX 778 ; N Ograve ; B 117 -19 812 915 ; -C -1 ; WX 778 ; N Otilde ; B 117 -19 812 890 ; -C -1 ; WX 611 ; N Scaron ; B 82 -19 663 915 ; -C -1 ; WX 611 ; N Thorn ; B 78 0 661 720 ; -C -1 ; WX 722 ; N Uacute ; B 129 -19 793 915 ; -C -1 ; WX 722 ; N Ucircumflex ; B 129 -19 793 915 ; -C -1 ; WX 722 ; N Udieresis ; B 129 -19 793 888 ; -C -1 ; WX 722 ; N Ugrave ; B 129 -19 793 915 ; -C -1 ; WX 611 ; N Yacute ; B 165 0 751 915 ; -C -1 ; WX 611 ; N Ydieresis ; B 165 0 751 888 ; -C -1 ; WX 611 ; N Zcaron ; B 31 0 729 915 ; -C -1 ; WX 556 ; N aacute ; B 71 -14 561 713 ; -C -1 ; WX 556 ; N acircumflex ; B 71 -14 555 713 ; -C -1 ; WX 556 ; N adieresis ; B 71 -14 555 686 ; -C -1 ; WX 556 ; N agrave ; B 71 -14 555 713 ; -C -1 ; WX 556 ; N aring ; B 71 -14 555 777 ; -C -1 ; WX 556 ; N atilde ; B 71 -14 587 688 ; -C -1 ; WX 222 ; N brokenbar ; B 81 0 298 739 ; -C -1 ; WX 556 ; N ccedilla ; B 92 -207 576 532 ; -C -1 ; WX 800 ; N copyright ; B 89 -19 864 739 ; -C -1 ; WX 400 ; N degree ; B 165 405 471 705 ; -C -1 ; WX 660 ; N divide ; B 127 0 640 500 ; -C -1 ; WX 556 ; N eacute ; B 90 -14 575 713 ; -C -1 ; WX 556 ; N ecircumflex ; B 90 -14 575 713 ; -C -1 ; WX 556 ; N edieresis ; B 90 -14 575 686 ; -C -1 ; WX 556 ; N egrave ; B 90 -14 575 713 ; -C -1 ; WX 556 ; N eth ; B 84 -14 582 739 ; -C -1 ; WX 222 ; N iacute ; B 78 0 374 713 ; -C -1 ; WX 222 ; N icircumflex ; B 71 0 365 713 ; -C -1 ; WX 222 ; N idieresis ; B 78 0 355 686 ; -C -1 ; WX 222 ; N igrave ; B 78 0 301 713 ; -C -1 ; WX 660 ; N logicalnot ; B 148 112 660 378 ; -C -1 ; WX 660 ; N minus ; B 127 220 640 280 ; -C -1 ; WX 556 ; N mu ; B 29 -204 593 518 ; -C -1 ; WX 660 ; N multiply ; B 92 6 677 500 ; -C -1 ; WX 556 ; N ntilde ; B 72 0 587 688 ; -C -1 ; WX 556 ; N oacute ; B 84 -14 582 713 ; -C -1 ; WX 556 ; N ocircumflex ; B 84 -14 582 713 ; -C -1 ; WX 556 ; N odieresis ; B 84 -14 582 686 ; -C -1 ; WX 556 ; N ograve ; B 84 -14 582 713 ; -C -1 ; WX 834 ; N onehalf ; B 125 -14 862 739 ; -C -1 ; WX 834 ; N onequarter ; B 165 -14 823 739 ; -C -1 ; WX 333 ; N onesuperior ; B 221 316 404 739 ; -C -1 ; WX 556 ; N otilde ; B 84 -14 587 688 ; -C -1 ; WX 660 ; N plusminus ; B 80 0 650 500 ; -C -1 ; WX 800 ; N registered ; B 89 -19 864 739 ; -C -1 ; WX 500 ; N scaron ; B 78 -14 554 713 ; -C -1 ; WX 611 ; N thorn ; B 36 -204 620 720 ; -C -1 ; WX 834 ; N threequarters ; B 131 -14 853 739 ; -C -1 ; WX 333 ; N threesuperior ; B 102 308 444 739 ; -C -1 ; WX 940 ; N trademark ; B 174 299 1012 720 ; -C -1 ; WX 333 ; N twosuperior ; B 82 316 453 739 ; -C -1 ; WX 556 ; N uacute ; B 103 -14 593 713 ; -C -1 ; WX 556 ; N ucircumflex ; B 103 -14 593 713 ; -C -1 ; WX 556 ; N udieresis ; B 103 -14 593 686 ; -C -1 ; WX 556 ; N ugrave ; B 103 -14 593 713 ; -C -1 ; WX 500 ; N yacute ; B 26 -204 592 713 ; -C -1 ; WX 500 ; N ydieresis ; B 26 -204 592 686 ; -C -1 ; WX 500 ; N zcaron ; B 33 0 564 713 ; -EndCharMetrics -StartKernData -StartKernPairs 115 - -KPX A y -18 -KPX A w -18 -KPX A v -18 -KPX A quoteright -74 -KPX A Y -74 -KPX A W -37 -KPX A V -74 -KPX A T -92 - -KPX F period -129 -KPX F comma -129 -KPX F A -55 - -KPX L y -37 -KPX L quoteright -74 -KPX L Y -111 -KPX L W -55 -KPX L V -92 -KPX L T -92 - -KPX P period -129 -KPX P comma -129 -KPX P A -74 - -KPX R y 0 -KPX R Y -37 -KPX R W -18 -KPX R V -18 -KPX R T -18 - -KPX T y -84 -KPX T w -84 -KPX T u -92 -KPX T semicolon -111 -KPX T s -111 -KPX T r -92 -KPX T period -111 -KPX T o -111 -KPX T i 0 -KPX T hyphen -129 -KPX T e -111 -KPX T comma -111 -KPX T colon -111 -KPX T c -111 -KPX T a -111 -KPX T A -92 - -KPX V y -18 -KPX V u -37 -KPX V semicolon -74 -KPX V r -37 -KPX V period -129 -KPX V o -55 -KPX V i -18 -KPX V hyphen -55 -KPX V e -55 -KPX V comma -129 -KPX V colon -74 -KPX V a -55 -KPX V A -74 - -KPX W y 0 -KPX W u -18 -KPX W semicolon -18 -KPX W r -18 -KPX W period -74 -KPX W o -18 -KPX W i 0 -KPX W hyphen 0 -KPX W e -18 -KPX W comma -74 -KPX W colon -18 -KPX W a -37 -KPX W A -37 - -KPX Y v -40 -KPX Y u -37 -KPX Y semicolon -92 -KPX Y q -92 -KPX Y period -111 -KPX Y p -37 -KPX Y o -92 -KPX Y i -20 -KPX Y hyphen -111 -KPX Y e -92 -KPX Y comma -111 -KPX Y colon -92 -KPX Y a -92 -KPX Y A -74 - -KPX f quoteright 18 -KPX f f -18 - -KPX quoteleft quoteleft -18 - -KPX quoteright t -18 -KPX quoteright s -74 -KPX quoteright quoteright -18 - -KPX r z 0 -KPX r y 18 -KPX r x 0 -KPX r w 0 -KPX r v 0 -KPX r u 0 -KPX r t 18 -KPX r r 0 -KPX r quoteright 0 -KPX r q -18 -KPX r period -92 -KPX r o -18 -KPX r n 18 -KPX r m 18 -KPX r hyphen -55 -KPX r h 0 -KPX r g 0 -KPX r f 18 -KPX r e -18 -KPX r d -18 -KPX r comma -92 -KPX r c -18 - -KPX v period -74 -KPX v comma -74 - -KPX w period -55 -KPX w comma -55 - -KPX y period -92 -KPX y comma -92 -EndKernPairs -EndKernData -StartComposites 58 -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 139 202 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 83 0 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 139 202 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 83 0 ; -CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 194 0 ; -CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 111 0 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 139 202 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 83 0 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 139 202 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 83 0 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 194 202 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 194 202 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 194 202 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 194 202 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 111 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 111 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 111 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 111 0 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute -47 202 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex -47 202 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis -47 202 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave -27 202 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -75 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -75 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -75 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -55 0 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 139 202 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 139 202 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 139 202 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 139 202 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 111 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 111 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 111 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 111 0 ; -CC Aacute 2 ; PCC A 0 0 ; PCC acute 167 202 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 167 202 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 167 202 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 167 202 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 111 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 111 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 111 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 111 0 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 222 202 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 222 202 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 222 202 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 222 202 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 111 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 111 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 111 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 111 0 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 167 202 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 111 0 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 194 202 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 111 0 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 222 202 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 111 0 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 187 202 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 111 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvr8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvr8a.afm deleted file mode 100644 index 1eb3b44..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvr8a.afm +++ /dev/null @@ -1,612 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All rights reserved. -Comment Creation Date: Thu Mar 15 08:58:00 1990 -Comment UniqueID 28352 -Comment VMusage 26389 33281 -FontName Helvetica -FullName Helvetica -FamilyName Helvetica -Weight Medium -ItalicAngle 0 -IsFixedPitch false -FontBBox -166 -225 1000 931 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.006 -Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All rights reserved.Helvetica is a trademark of Linotype AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 718 -XHeight 523 -Ascender 718 -Descender -207 -StartCharMetrics 228 -C 32 ; WX 278 ; N space ; B 0 0 0 0 ; -C 33 ; WX 278 ; N exclam ; B 90 0 187 718 ; -C 34 ; WX 355 ; N quotedbl ; B 70 463 285 718 ; -C 35 ; WX 556 ; N numbersign ; B 28 0 529 688 ; -C 36 ; WX 556 ; N dollar ; B 32 -115 520 775 ; -C 37 ; WX 889 ; N percent ; B 39 -19 850 703 ; -C 38 ; WX 667 ; N ampersand ; B 44 -15 645 718 ; -C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ; -C 40 ; WX 333 ; N parenleft ; B 68 -207 299 733 ; -C 41 ; WX 333 ; N parenright ; B 34 -207 265 733 ; -C 42 ; WX 389 ; N asterisk ; B 39 431 349 718 ; -C 43 ; WX 584 ; N plus ; B 39 0 545 505 ; -C 44 ; WX 278 ; N comma ; B 87 -147 191 106 ; -C 45 ; WX 333 ; N hyphen ; B 44 232 289 322 ; -C 46 ; WX 278 ; N period ; B 87 0 191 106 ; -C 47 ; WX 278 ; N slash ; B -17 -19 295 737 ; -C 48 ; WX 556 ; N zero ; B 37 -19 519 703 ; -C 49 ; WX 556 ; N one ; B 101 0 359 703 ; -C 50 ; WX 556 ; N two ; B 26 0 507 703 ; -C 51 ; WX 556 ; N three ; B 34 -19 522 703 ; -C 52 ; WX 556 ; N four ; B 25 0 523 703 ; -C 53 ; WX 556 ; N five ; B 32 -19 514 688 ; -C 54 ; WX 556 ; N six ; B 38 -19 518 703 ; -C 55 ; WX 556 ; N seven ; B 37 0 523 688 ; -C 56 ; WX 556 ; N eight ; B 38 -19 517 703 ; -C 57 ; WX 556 ; N nine ; B 42 -19 514 703 ; -C 58 ; WX 278 ; N colon ; B 87 0 191 516 ; -C 59 ; WX 278 ; N semicolon ; B 87 -147 191 516 ; -C 60 ; WX 584 ; N less ; B 48 11 536 495 ; -C 61 ; WX 584 ; N equal ; B 39 115 545 390 ; -C 62 ; WX 584 ; N greater ; B 48 11 536 495 ; -C 63 ; WX 556 ; N question ; B 56 0 492 727 ; -C 64 ; WX 1015 ; N at ; B 147 -19 868 737 ; -C 65 ; WX 667 ; N A ; B 14 0 654 718 ; -C 66 ; WX 667 ; N B ; B 74 0 627 718 ; -C 67 ; WX 722 ; N C ; B 44 -19 681 737 ; -C 68 ; WX 722 ; N D ; B 81 0 674 718 ; -C 69 ; WX 667 ; N E ; B 86 0 616 718 ; -C 70 ; WX 611 ; N F ; B 86 0 583 718 ; -C 71 ; WX 778 ; N G ; B 48 -19 704 737 ; -C 72 ; WX 722 ; N H ; B 77 0 646 718 ; -C 73 ; WX 278 ; N I ; B 91 0 188 718 ; -C 74 ; WX 500 ; N J ; B 17 -19 428 718 ; -C 75 ; WX 667 ; N K ; B 76 0 663 718 ; -C 76 ; WX 556 ; N L ; B 76 0 537 718 ; -C 77 ; WX 833 ; N M ; B 73 0 761 718 ; -C 78 ; WX 722 ; N N ; B 76 0 646 718 ; -C 79 ; WX 778 ; N O ; B 39 -19 739 737 ; -C 80 ; WX 667 ; N P ; B 86 0 622 718 ; -C 81 ; WX 778 ; N Q ; B 39 -56 739 737 ; -C 82 ; WX 722 ; N R ; B 88 0 684 718 ; -C 83 ; WX 667 ; N S ; B 49 -19 620 737 ; -C 84 ; WX 611 ; N T ; B 14 0 597 718 ; -C 85 ; WX 722 ; N U ; B 79 -19 644 718 ; -C 86 ; WX 667 ; N V ; B 20 0 647 718 ; -C 87 ; WX 944 ; N W ; B 16 0 928 718 ; -C 88 ; WX 667 ; N X ; B 19 0 648 718 ; -C 89 ; WX 667 ; N Y ; B 14 0 653 718 ; -C 90 ; WX 611 ; N Z ; B 23 0 588 718 ; -C 91 ; WX 278 ; N bracketleft ; B 63 -196 250 722 ; -C 92 ; WX 278 ; N backslash ; B -17 -19 295 737 ; -C 93 ; WX 278 ; N bracketright ; B 28 -196 215 722 ; -C 94 ; WX 469 ; N asciicircum ; B -14 264 483 688 ; -C 95 ; WX 556 ; N underscore ; B 0 -125 556 -75 ; -C 96 ; WX 222 ; N quoteleft ; B 65 470 169 725 ; -C 97 ; WX 556 ; N a ; B 36 -15 530 538 ; -C 98 ; WX 556 ; N b ; B 58 -15 517 718 ; -C 99 ; WX 500 ; N c ; B 30 -15 477 538 ; -C 100 ; WX 556 ; N d ; B 35 -15 499 718 ; -C 101 ; WX 556 ; N e ; B 40 -15 516 538 ; -C 102 ; WX 278 ; N f ; B 14 0 262 728 ; L i fi ; L l fl ; -C 103 ; WX 556 ; N g ; B 40 -220 499 538 ; -C 104 ; WX 556 ; N h ; B 65 0 491 718 ; -C 105 ; WX 222 ; N i ; B 67 0 155 718 ; -C 106 ; WX 222 ; N j ; B -16 -210 155 718 ; -C 107 ; WX 500 ; N k ; B 67 0 501 718 ; -C 108 ; WX 222 ; N l ; B 67 0 155 718 ; -C 109 ; WX 833 ; N m ; B 65 0 769 538 ; -C 110 ; WX 556 ; N n ; B 65 0 491 538 ; -C 111 ; WX 556 ; N o ; B 35 -14 521 538 ; -C 112 ; WX 556 ; N p ; B 58 -207 517 538 ; -C 113 ; WX 556 ; N q ; B 35 -207 494 538 ; -C 114 ; WX 333 ; N r ; B 77 0 332 538 ; -C 115 ; WX 500 ; N s ; B 32 -15 464 538 ; -C 116 ; WX 278 ; N t ; B 14 -7 257 669 ; -C 117 ; WX 556 ; N u ; B 68 -15 489 523 ; -C 118 ; WX 500 ; N v ; B 8 0 492 523 ; -C 119 ; WX 722 ; N w ; B 14 0 709 523 ; -C 120 ; WX 500 ; N x ; B 11 0 490 523 ; -C 121 ; WX 500 ; N y ; B 11 -214 489 523 ; -C 122 ; WX 500 ; N z ; B 31 0 469 523 ; -C 123 ; WX 334 ; N braceleft ; B 42 -196 292 722 ; -C 124 ; WX 260 ; N bar ; B 94 -19 167 737 ; -C 125 ; WX 334 ; N braceright ; B 42 -196 292 722 ; -C 126 ; WX 584 ; N asciitilde ; B 61 180 523 326 ; -C 161 ; WX 333 ; N exclamdown ; B 118 -195 215 523 ; -C 162 ; WX 556 ; N cent ; B 51 -115 513 623 ; -C 163 ; WX 556 ; N sterling ; B 33 -16 539 718 ; -C 164 ; WX 167 ; N fraction ; B -166 -19 333 703 ; -C 165 ; WX 556 ; N yen ; B 3 0 553 688 ; -C 166 ; WX 556 ; N florin ; B -11 -207 501 737 ; -C 167 ; WX 556 ; N section ; B 43 -191 512 737 ; -C 168 ; WX 556 ; N currency ; B 28 99 528 603 ; -C 169 ; WX 191 ; N quotesingle ; B 59 463 132 718 ; -C 170 ; WX 333 ; N quotedblleft ; B 38 470 307 725 ; -C 171 ; WX 556 ; N guillemotleft ; B 97 108 459 446 ; -C 172 ; WX 333 ; N guilsinglleft ; B 88 108 245 446 ; -C 173 ; WX 333 ; N guilsinglright ; B 88 108 245 446 ; -C 174 ; WX 500 ; N fi ; B 14 0 434 728 ; -C 175 ; WX 500 ; N fl ; B 14 0 432 728 ; -C 177 ; WX 556 ; N endash ; B 0 240 556 313 ; -C 178 ; WX 556 ; N dagger ; B 43 -159 514 718 ; -C 179 ; WX 556 ; N daggerdbl ; B 43 -159 514 718 ; -C 180 ; WX 278 ; N periodcentered ; B 77 190 202 315 ; -C 182 ; WX 537 ; N paragraph ; B 18 -173 497 718 ; -C 183 ; WX 350 ; N bullet ; B 18 202 333 517 ; -C 184 ; WX 222 ; N quotesinglbase ; B 53 -149 157 106 ; -C 185 ; WX 333 ; N quotedblbase ; B 26 -149 295 106 ; -C 186 ; WX 333 ; N quotedblright ; B 26 463 295 718 ; -C 187 ; WX 556 ; N guillemotright ; B 97 108 459 446 ; -C 188 ; WX 1000 ; N ellipsis ; B 115 0 885 106 ; -C 189 ; WX 1000 ; N perthousand ; B 7 -19 994 703 ; -C 191 ; WX 611 ; N questiondown ; B 91 -201 527 525 ; -C 193 ; WX 333 ; N grave ; B 14 593 211 734 ; -C 194 ; WX 333 ; N acute ; B 122 593 319 734 ; -C 195 ; WX 333 ; N circumflex ; B 21 593 312 734 ; -C 196 ; WX 333 ; N tilde ; B -4 606 337 722 ; -C 197 ; WX 333 ; N macron ; B 10 627 323 684 ; -C 198 ; WX 333 ; N breve ; B 13 595 321 731 ; -C 199 ; WX 333 ; N dotaccent ; B 121 604 212 706 ; -C 200 ; WX 333 ; N dieresis ; B 40 604 293 706 ; -C 202 ; WX 333 ; N ring ; B 75 572 259 756 ; -C 203 ; WX 333 ; N cedilla ; B 45 -225 259 0 ; -C 205 ; WX 333 ; N hungarumlaut ; B 31 593 409 734 ; -C 206 ; WX 333 ; N ogonek ; B 73 -225 287 0 ; -C 207 ; WX 333 ; N caron ; B 21 593 312 734 ; -C 208 ; WX 1000 ; N emdash ; B 0 240 1000 313 ; -C 225 ; WX 1000 ; N AE ; B 8 0 951 718 ; -C 227 ; WX 370 ; N ordfeminine ; B 24 304 346 737 ; -C 232 ; WX 556 ; N Lslash ; B -20 0 537 718 ; -C 233 ; WX 778 ; N Oslash ; B 39 -19 740 737 ; -C 234 ; WX 1000 ; N OE ; B 36 -19 965 737 ; -C 235 ; WX 365 ; N ordmasculine ; B 25 304 341 737 ; -C 241 ; WX 889 ; N ae ; B 36 -15 847 538 ; -C 245 ; WX 278 ; N dotlessi ; B 95 0 183 523 ; -C 248 ; WX 222 ; N lslash ; B -20 0 242 718 ; -C 249 ; WX 611 ; N oslash ; B 28 -22 537 545 ; -C 250 ; WX 944 ; N oe ; B 35 -15 902 538 ; -C 251 ; WX 611 ; N germandbls ; B 67 -15 571 728 ; -C -1 ; WX 611 ; N Zcaron ; B 23 0 588 929 ; -C -1 ; WX 500 ; N ccedilla ; B 30 -225 477 538 ; -C -1 ; WX 500 ; N ydieresis ; B 11 -214 489 706 ; -C -1 ; WX 556 ; N atilde ; B 36 -15 530 722 ; -C -1 ; WX 278 ; N icircumflex ; B -6 0 285 734 ; -C -1 ; WX 333 ; N threesuperior ; B 5 270 325 703 ; -C -1 ; WX 556 ; N ecircumflex ; B 40 -15 516 734 ; -C -1 ; WX 556 ; N thorn ; B 58 -207 517 718 ; -C -1 ; WX 556 ; N egrave ; B 40 -15 516 734 ; -C -1 ; WX 333 ; N twosuperior ; B 4 281 323 703 ; -C -1 ; WX 556 ; N eacute ; B 40 -15 516 734 ; -C -1 ; WX 556 ; N otilde ; B 35 -14 521 722 ; -C -1 ; WX 667 ; N Aacute ; B 14 0 654 929 ; -C -1 ; WX 556 ; N ocircumflex ; B 35 -14 521 734 ; -C -1 ; WX 500 ; N yacute ; B 11 -214 489 734 ; -C -1 ; WX 556 ; N udieresis ; B 68 -15 489 706 ; -C -1 ; WX 834 ; N threequarters ; B 45 -19 810 703 ; -C -1 ; WX 556 ; N acircumflex ; B 36 -15 530 734 ; -C -1 ; WX 722 ; N Eth ; B 0 0 674 718 ; -C -1 ; WX 556 ; N edieresis ; B 40 -15 516 706 ; -C -1 ; WX 556 ; N ugrave ; B 68 -15 489 734 ; -C -1 ; WX 1000 ; N trademark ; B 46 306 903 718 ; -C -1 ; WX 556 ; N ograve ; B 35 -14 521 734 ; -C -1 ; WX 500 ; N scaron ; B 32 -15 464 734 ; -C -1 ; WX 278 ; N Idieresis ; B 13 0 266 901 ; -C -1 ; WX 556 ; N uacute ; B 68 -15 489 734 ; -C -1 ; WX 556 ; N agrave ; B 36 -15 530 734 ; -C -1 ; WX 556 ; N ntilde ; B 65 0 491 722 ; -C -1 ; WX 556 ; N aring ; B 36 -15 530 756 ; -C -1 ; WX 500 ; N zcaron ; B 31 0 469 734 ; -C -1 ; WX 278 ; N Icircumflex ; B -6 0 285 929 ; -C -1 ; WX 722 ; N Ntilde ; B 76 0 646 917 ; -C -1 ; WX 556 ; N ucircumflex ; B 68 -15 489 734 ; -C -1 ; WX 667 ; N Ecircumflex ; B 86 0 616 929 ; -C -1 ; WX 278 ; N Iacute ; B 91 0 292 929 ; -C -1 ; WX 722 ; N Ccedilla ; B 44 -225 681 737 ; -C -1 ; WX 778 ; N Odieresis ; B 39 -19 739 901 ; -C -1 ; WX 667 ; N Scaron ; B 49 -19 620 929 ; -C -1 ; WX 667 ; N Edieresis ; B 86 0 616 901 ; -C -1 ; WX 278 ; N Igrave ; B -13 0 188 929 ; -C -1 ; WX 556 ; N adieresis ; B 36 -15 530 706 ; -C -1 ; WX 778 ; N Ograve ; B 39 -19 739 929 ; -C -1 ; WX 667 ; N Egrave ; B 86 0 616 929 ; -C -1 ; WX 667 ; N Ydieresis ; B 14 0 653 901 ; -C -1 ; WX 737 ; N registered ; B -14 -19 752 737 ; -C -1 ; WX 778 ; N Otilde ; B 39 -19 739 917 ; -C -1 ; WX 834 ; N onequarter ; B 73 -19 756 703 ; -C -1 ; WX 722 ; N Ugrave ; B 79 -19 644 929 ; -C -1 ; WX 722 ; N Ucircumflex ; B 79 -19 644 929 ; -C -1 ; WX 667 ; N Thorn ; B 86 0 622 718 ; -C -1 ; WX 584 ; N divide ; B 39 -19 545 524 ; -C -1 ; WX 667 ; N Atilde ; B 14 0 654 917 ; -C -1 ; WX 722 ; N Uacute ; B 79 -19 644 929 ; -C -1 ; WX 778 ; N Ocircumflex ; B 39 -19 739 929 ; -C -1 ; WX 584 ; N logicalnot ; B 39 108 545 390 ; -C -1 ; WX 667 ; N Aring ; B 14 0 654 931 ; -C -1 ; WX 278 ; N idieresis ; B 13 0 266 706 ; -C -1 ; WX 278 ; N iacute ; B 95 0 292 734 ; -C -1 ; WX 556 ; N aacute ; B 36 -15 530 734 ; -C -1 ; WX 584 ; N plusminus ; B 39 0 545 506 ; -C -1 ; WX 584 ; N multiply ; B 39 0 545 506 ; -C -1 ; WX 722 ; N Udieresis ; B 79 -19 644 901 ; -C -1 ; WX 584 ; N minus ; B 39 216 545 289 ; -C -1 ; WX 333 ; N onesuperior ; B 43 281 222 703 ; -C -1 ; WX 667 ; N Eacute ; B 86 0 616 929 ; -C -1 ; WX 667 ; N Acircumflex ; B 14 0 654 929 ; -C -1 ; WX 737 ; N copyright ; B -14 -19 752 737 ; -C -1 ; WX 667 ; N Agrave ; B 14 0 654 929 ; -C -1 ; WX 556 ; N odieresis ; B 35 -14 521 706 ; -C -1 ; WX 556 ; N oacute ; B 35 -14 521 734 ; -C -1 ; WX 400 ; N degree ; B 54 411 346 703 ; -C -1 ; WX 278 ; N igrave ; B -13 0 184 734 ; -C -1 ; WX 556 ; N mu ; B 68 -207 489 523 ; -C -1 ; WX 778 ; N Oacute ; B 39 -19 739 929 ; -C -1 ; WX 556 ; N eth ; B 35 -15 522 737 ; -C -1 ; WX 667 ; N Adieresis ; B 14 0 654 901 ; -C -1 ; WX 667 ; N Yacute ; B 14 0 653 929 ; -C -1 ; WX 260 ; N brokenbar ; B 94 -19 167 737 ; -C -1 ; WX 834 ; N onehalf ; B 43 -19 773 703 ; -EndCharMetrics -StartKernData -StartKernPairs 250 - -KPX A y -40 -KPX A w -40 -KPX A v -40 -KPX A u -30 -KPX A Y -100 -KPX A W -50 -KPX A V -70 -KPX A U -50 -KPX A T -120 -KPX A Q -30 -KPX A O -30 -KPX A G -30 -KPX A C -30 - -KPX B period -20 -KPX B comma -20 -KPX B U -10 - -KPX C period -30 -KPX C comma -30 - -KPX D period -70 -KPX D comma -70 -KPX D Y -90 -KPX D W -40 -KPX D V -70 -KPX D A -40 - -KPX F r -45 -KPX F period -150 -KPX F o -30 -KPX F e -30 -KPX F comma -150 -KPX F a -50 -KPX F A -80 - -KPX J u -20 -KPX J period -30 -KPX J comma -30 -KPX J a -20 -KPX J A -20 - -KPX K y -50 -KPX K u -30 -KPX K o -40 -KPX K e -40 -KPX K O -50 - -KPX L y -30 -KPX L quoteright -160 -KPX L quotedblright -140 -KPX L Y -140 -KPX L W -70 -KPX L V -110 -KPX L T -110 - -KPX O period -40 -KPX O comma -40 -KPX O Y -70 -KPX O X -60 -KPX O W -30 -KPX O V -50 -KPX O T -40 -KPX O A -20 - -KPX P period -180 -KPX P o -50 -KPX P e -50 -KPX P comma -180 -KPX P a -40 -KPX P A -120 - -KPX Q U -10 - -KPX R Y -50 -KPX R W -30 -KPX R V -50 -KPX R U -40 -KPX R T -30 -KPX R O -20 - -KPX S period -20 -KPX S comma -20 - -KPX T y -120 -KPX T w -120 -KPX T u -120 -KPX T semicolon -20 -KPX T r -120 -KPX T period -120 -KPX T o -120 -KPX T hyphen -140 -KPX T e -120 -KPX T comma -120 -KPX T colon -20 -KPX T a -120 -KPX T O -40 -KPX T A -120 - -KPX U period -40 -KPX U comma -40 -KPX U A -40 - -KPX V u -70 -KPX V semicolon -40 -KPX V period -125 -KPX V o -80 -KPX V hyphen -80 -KPX V e -80 -KPX V comma -125 -KPX V colon -40 -KPX V a -70 -KPX V O -40 -KPX V G -40 -KPX V A -80 - -KPX W y -20 -KPX W u -30 -KPX W period -80 -KPX W o -30 -KPX W hyphen -40 -KPX W e -30 -KPX W comma -80 -KPX W a -40 -KPX W O -20 -KPX W A -50 - -KPX Y u -110 -KPX Y semicolon -60 -KPX Y period -140 -KPX Y o -140 -KPX Y i -20 -KPX Y hyphen -140 -KPX Y e -140 -KPX Y comma -140 -KPX Y colon -60 -KPX Y a -140 -KPX Y O -85 -KPX Y A -110 - -KPX a y -30 -KPX a w -20 -KPX a v -20 - -KPX b y -20 -KPX b v -20 -KPX b u -20 -KPX b period -40 -KPX b l -20 -KPX b comma -40 -KPX b b -10 - -KPX c k -20 -KPX c comma -15 - -KPX colon space -50 - -KPX comma quoteright -100 -KPX comma quotedblright -100 - -KPX e y -20 -KPX e x -30 -KPX e w -20 -KPX e v -30 -KPX e period -15 -KPX e comma -15 - -KPX f quoteright 50 -KPX f quotedblright 60 -KPX f period -30 -KPX f o -30 -KPX f e -30 -KPX f dotlessi -28 -KPX f comma -30 -KPX f a -30 - -KPX g r -10 - -KPX h y -30 - -KPX k o -20 -KPX k e -20 - -KPX m y -15 -KPX m u -10 - -KPX n y -15 -KPX n v -20 -KPX n u -10 - -KPX o y -30 -KPX o x -30 -KPX o w -15 -KPX o v -15 -KPX o period -40 -KPX o comma -40 - -KPX oslash z -55 -KPX oslash y -70 -KPX oslash x -85 -KPX oslash w -70 -KPX oslash v -70 -KPX oslash u -55 -KPX oslash t -55 -KPX oslash s -55 -KPX oslash r -55 -KPX oslash q -55 -KPX oslash period -95 -KPX oslash p -55 -KPX oslash o -55 -KPX oslash n -55 -KPX oslash m -55 -KPX oslash l -55 -KPX oslash k -55 -KPX oslash j -55 -KPX oslash i -55 -KPX oslash h -55 -KPX oslash g -55 -KPX oslash f -55 -KPX oslash e -55 -KPX oslash d -55 -KPX oslash comma -95 -KPX oslash c -55 -KPX oslash b -55 -KPX oslash a -55 - -KPX p y -30 -KPX p period -35 -KPX p comma -35 - -KPX period space -60 -KPX period quoteright -100 -KPX period quotedblright -100 - -KPX quotedblright space -40 - -KPX quoteleft quoteleft -57 - -KPX quoteright space -70 -KPX quoteright s -50 -KPX quoteright r -50 -KPX quoteright quoteright -57 -KPX quoteright d -50 - -KPX r y 30 -KPX r v 30 -KPX r u 15 -KPX r t 40 -KPX r semicolon 30 -KPX r period -50 -KPX r p 30 -KPX r n 25 -KPX r m 25 -KPX r l 15 -KPX r k 15 -KPX r i 15 -KPX r comma -50 -KPX r colon 30 -KPX r a -10 - -KPX s w -30 -KPX s period -15 -KPX s comma -15 - -KPX semicolon space -50 - -KPX space quoteleft -60 -KPX space quotedblleft -30 -KPX space Y -90 -KPX space W -40 -KPX space V -50 -KPX space T -50 - -KPX v period -80 -KPX v o -25 -KPX v e -25 -KPX v comma -80 -KPX v a -25 - -KPX w period -60 -KPX w o -10 -KPX w e -10 -KPX w comma -60 -KPX w a -15 - -KPX x e -30 - -KPX y period -100 -KPX y o -20 -KPX y e -20 -KPX y comma -100 -KPX y a -20 - -KPX z o -15 -KPX z e -15 -EndKernPairs -EndKernData -StartComposites 58 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 167 195 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 167 195 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 167 195 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 167 195 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 167 175 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 167 195 ; -CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 195 0 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 167 195 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 167 195 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 167 195 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 167 195 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute -27 195 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex -27 195 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis -27 195 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave -27 195 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 205 195 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 223 195 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 223 195 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 223 195 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 223 195 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 223 195 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 167 195 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 195 195 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 195 195 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 195 195 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 195 195 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 167 195 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 167 195 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 139 195 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 112 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 112 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 112 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 112 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 112 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 102 0 ; -CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 84 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 112 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 112 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 112 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 112 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -27 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -27 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -27 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -27 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 102 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 112 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 112 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 112 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 112 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 112 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 84 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 112 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 112 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 112 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 112 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 84 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 84 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 84 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvr8an.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvr8an.afm deleted file mode 100644 index 5a08aa8..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvr8an.afm +++ /dev/null @@ -1,612 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All rights reserved. -Comment Creation Date: Thu Mar 15 11:04:57 1990 -Comment UniqueID 28380 -Comment VMusage 7572 42473 -FontName Helvetica-Narrow -FullName Helvetica Narrow -FamilyName Helvetica -Weight Medium -ItalicAngle 0 -IsFixedPitch false -FontBBox -136 -225 820 931 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.006 -Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All rights reserved.Helvetica is a trademark of Linotype AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 718 -XHeight 523 -Ascender 718 -Descender -207 -StartCharMetrics 228 -C 32 ; WX 228 ; N space ; B 0 0 0 0 ; -C 33 ; WX 228 ; N exclam ; B 74 0 153 718 ; -C 34 ; WX 291 ; N quotedbl ; B 57 463 234 718 ; -C 35 ; WX 456 ; N numbersign ; B 23 0 434 688 ; -C 36 ; WX 456 ; N dollar ; B 26 -115 426 775 ; -C 37 ; WX 729 ; N percent ; B 32 -19 697 703 ; -C 38 ; WX 547 ; N ampersand ; B 36 -15 529 718 ; -C 39 ; WX 182 ; N quoteright ; B 43 463 129 718 ; -C 40 ; WX 273 ; N parenleft ; B 56 -207 245 733 ; -C 41 ; WX 273 ; N parenright ; B 28 -207 217 733 ; -C 42 ; WX 319 ; N asterisk ; B 32 431 286 718 ; -C 43 ; WX 479 ; N plus ; B 32 0 447 505 ; -C 44 ; WX 228 ; N comma ; B 71 -147 157 106 ; -C 45 ; WX 273 ; N hyphen ; B 36 232 237 322 ; -C 46 ; WX 228 ; N period ; B 71 0 157 106 ; -C 47 ; WX 228 ; N slash ; B -14 -19 242 737 ; -C 48 ; WX 456 ; N zero ; B 30 -19 426 703 ; -C 49 ; WX 456 ; N one ; B 83 0 294 703 ; -C 50 ; WX 456 ; N two ; B 21 0 416 703 ; -C 51 ; WX 456 ; N three ; B 28 -19 428 703 ; -C 52 ; WX 456 ; N four ; B 20 0 429 703 ; -C 53 ; WX 456 ; N five ; B 26 -19 421 688 ; -C 54 ; WX 456 ; N six ; B 31 -19 425 703 ; -C 55 ; WX 456 ; N seven ; B 30 0 429 688 ; -C 56 ; WX 456 ; N eight ; B 31 -19 424 703 ; -C 57 ; WX 456 ; N nine ; B 34 -19 421 703 ; -C 58 ; WX 228 ; N colon ; B 71 0 157 516 ; -C 59 ; WX 228 ; N semicolon ; B 71 -147 157 516 ; -C 60 ; WX 479 ; N less ; B 39 11 440 495 ; -C 61 ; WX 479 ; N equal ; B 32 115 447 390 ; -C 62 ; WX 479 ; N greater ; B 39 11 440 495 ; -C 63 ; WX 456 ; N question ; B 46 0 403 727 ; -C 64 ; WX 832 ; N at ; B 121 -19 712 737 ; -C 65 ; WX 547 ; N A ; B 11 0 536 718 ; -C 66 ; WX 547 ; N B ; B 61 0 514 718 ; -C 67 ; WX 592 ; N C ; B 36 -19 558 737 ; -C 68 ; WX 592 ; N D ; B 66 0 553 718 ; -C 69 ; WX 547 ; N E ; B 71 0 505 718 ; -C 70 ; WX 501 ; N F ; B 71 0 478 718 ; -C 71 ; WX 638 ; N G ; B 39 -19 577 737 ; -C 72 ; WX 592 ; N H ; B 63 0 530 718 ; -C 73 ; WX 228 ; N I ; B 75 0 154 718 ; -C 74 ; WX 410 ; N J ; B 14 -19 351 718 ; -C 75 ; WX 547 ; N K ; B 62 0 544 718 ; -C 76 ; WX 456 ; N L ; B 62 0 440 718 ; -C 77 ; WX 683 ; N M ; B 60 0 624 718 ; -C 78 ; WX 592 ; N N ; B 62 0 530 718 ; -C 79 ; WX 638 ; N O ; B 32 -19 606 737 ; -C 80 ; WX 547 ; N P ; B 71 0 510 718 ; -C 81 ; WX 638 ; N Q ; B 32 -56 606 737 ; -C 82 ; WX 592 ; N R ; B 72 0 561 718 ; -C 83 ; WX 547 ; N S ; B 40 -19 508 737 ; -C 84 ; WX 501 ; N T ; B 11 0 490 718 ; -C 85 ; WX 592 ; N U ; B 65 -19 528 718 ; -C 86 ; WX 547 ; N V ; B 16 0 531 718 ; -C 87 ; WX 774 ; N W ; B 13 0 761 718 ; -C 88 ; WX 547 ; N X ; B 16 0 531 718 ; -C 89 ; WX 547 ; N Y ; B 11 0 535 718 ; -C 90 ; WX 501 ; N Z ; B 19 0 482 718 ; -C 91 ; WX 228 ; N bracketleft ; B 52 -196 205 722 ; -C 92 ; WX 228 ; N backslash ; B -14 -19 242 737 ; -C 93 ; WX 228 ; N bracketright ; B 23 -196 176 722 ; -C 94 ; WX 385 ; N asciicircum ; B -11 264 396 688 ; -C 95 ; WX 456 ; N underscore ; B 0 -125 456 -75 ; -C 96 ; WX 182 ; N quoteleft ; B 53 470 139 725 ; -C 97 ; WX 456 ; N a ; B 30 -15 435 538 ; -C 98 ; WX 456 ; N b ; B 48 -15 424 718 ; -C 99 ; WX 410 ; N c ; B 25 -15 391 538 ; -C 100 ; WX 456 ; N d ; B 29 -15 409 718 ; -C 101 ; WX 456 ; N e ; B 33 -15 423 538 ; -C 102 ; WX 228 ; N f ; B 11 0 215 728 ; L i fi ; L l fl ; -C 103 ; WX 456 ; N g ; B 33 -220 409 538 ; -C 104 ; WX 456 ; N h ; B 53 0 403 718 ; -C 105 ; WX 182 ; N i ; B 55 0 127 718 ; -C 106 ; WX 182 ; N j ; B -13 -210 127 718 ; -C 107 ; WX 410 ; N k ; B 55 0 411 718 ; -C 108 ; WX 182 ; N l ; B 55 0 127 718 ; -C 109 ; WX 683 ; N m ; B 53 0 631 538 ; -C 110 ; WX 456 ; N n ; B 53 0 403 538 ; -C 111 ; WX 456 ; N o ; B 29 -14 427 538 ; -C 112 ; WX 456 ; N p ; B 48 -207 424 538 ; -C 113 ; WX 456 ; N q ; B 29 -207 405 538 ; -C 114 ; WX 273 ; N r ; B 63 0 272 538 ; -C 115 ; WX 410 ; N s ; B 26 -15 380 538 ; -C 116 ; WX 228 ; N t ; B 11 -7 211 669 ; -C 117 ; WX 456 ; N u ; B 56 -15 401 523 ; -C 118 ; WX 410 ; N v ; B 7 0 403 523 ; -C 119 ; WX 592 ; N w ; B 11 0 581 523 ; -C 120 ; WX 410 ; N x ; B 9 0 402 523 ; -C 121 ; WX 410 ; N y ; B 9 -214 401 523 ; -C 122 ; WX 410 ; N z ; B 25 0 385 523 ; -C 123 ; WX 274 ; N braceleft ; B 34 -196 239 722 ; -C 124 ; WX 213 ; N bar ; B 77 -19 137 737 ; -C 125 ; WX 274 ; N braceright ; B 34 -196 239 722 ; -C 126 ; WX 479 ; N asciitilde ; B 50 180 429 326 ; -C 161 ; WX 273 ; N exclamdown ; B 97 -195 176 523 ; -C 162 ; WX 456 ; N cent ; B 42 -115 421 623 ; -C 163 ; WX 456 ; N sterling ; B 27 -16 442 718 ; -C 164 ; WX 137 ; N fraction ; B -136 -19 273 703 ; -C 165 ; WX 456 ; N yen ; B 2 0 453 688 ; -C 166 ; WX 456 ; N florin ; B -9 -207 411 737 ; -C 167 ; WX 456 ; N section ; B 35 -191 420 737 ; -C 168 ; WX 456 ; N currency ; B 23 99 433 603 ; -C 169 ; WX 157 ; N quotesingle ; B 48 463 108 718 ; -C 170 ; WX 273 ; N quotedblleft ; B 31 470 252 725 ; -C 171 ; WX 456 ; N guillemotleft ; B 80 108 376 446 ; -C 172 ; WX 273 ; N guilsinglleft ; B 72 108 201 446 ; -C 173 ; WX 273 ; N guilsinglright ; B 72 108 201 446 ; -C 174 ; WX 410 ; N fi ; B 11 0 356 728 ; -C 175 ; WX 410 ; N fl ; B 11 0 354 728 ; -C 177 ; WX 456 ; N endash ; B 0 240 456 313 ; -C 178 ; WX 456 ; N dagger ; B 35 -159 421 718 ; -C 179 ; WX 456 ; N daggerdbl ; B 35 -159 421 718 ; -C 180 ; WX 228 ; N periodcentered ; B 63 190 166 315 ; -C 182 ; WX 440 ; N paragraph ; B 15 -173 408 718 ; -C 183 ; WX 287 ; N bullet ; B 15 202 273 517 ; -C 184 ; WX 182 ; N quotesinglbase ; B 43 -149 129 106 ; -C 185 ; WX 273 ; N quotedblbase ; B 21 -149 242 106 ; -C 186 ; WX 273 ; N quotedblright ; B 21 463 242 718 ; -C 187 ; WX 456 ; N guillemotright ; B 80 108 376 446 ; -C 188 ; WX 820 ; N ellipsis ; B 94 0 726 106 ; -C 189 ; WX 820 ; N perthousand ; B 6 -19 815 703 ; -C 191 ; WX 501 ; N questiondown ; B 75 -201 432 525 ; -C 193 ; WX 273 ; N grave ; B 11 593 173 734 ; -C 194 ; WX 273 ; N acute ; B 100 593 262 734 ; -C 195 ; WX 273 ; N circumflex ; B 17 593 256 734 ; -C 196 ; WX 273 ; N tilde ; B -3 606 276 722 ; -C 197 ; WX 273 ; N macron ; B 8 627 265 684 ; -C 198 ; WX 273 ; N breve ; B 11 595 263 731 ; -C 199 ; WX 273 ; N dotaccent ; B 99 604 174 706 ; -C 200 ; WX 273 ; N dieresis ; B 33 604 240 706 ; -C 202 ; WX 273 ; N ring ; B 61 572 212 756 ; -C 203 ; WX 273 ; N cedilla ; B 37 -225 212 0 ; -C 205 ; WX 273 ; N hungarumlaut ; B 25 593 335 734 ; -C 206 ; WX 273 ; N ogonek ; B 60 -225 235 0 ; -C 207 ; WX 273 ; N caron ; B 17 593 256 734 ; -C 208 ; WX 820 ; N emdash ; B 0 240 820 313 ; -C 225 ; WX 820 ; N AE ; B 7 0 780 718 ; -C 227 ; WX 303 ; N ordfeminine ; B 20 304 284 737 ; -C 232 ; WX 456 ; N Lslash ; B -16 0 440 718 ; -C 233 ; WX 638 ; N Oslash ; B 32 -19 607 737 ; -C 234 ; WX 820 ; N OE ; B 30 -19 791 737 ; -C 235 ; WX 299 ; N ordmasculine ; B 20 304 280 737 ; -C 241 ; WX 729 ; N ae ; B 30 -15 695 538 ; -C 245 ; WX 228 ; N dotlessi ; B 78 0 150 523 ; -C 248 ; WX 182 ; N lslash ; B -16 0 198 718 ; -C 249 ; WX 501 ; N oslash ; B 23 -22 440 545 ; -C 250 ; WX 774 ; N oe ; B 29 -15 740 538 ; -C 251 ; WX 501 ; N germandbls ; B 55 -15 468 728 ; -C -1 ; WX 501 ; N Zcaron ; B 19 0 482 929 ; -C -1 ; WX 410 ; N ccedilla ; B 25 -225 391 538 ; -C -1 ; WX 410 ; N ydieresis ; B 9 -214 401 706 ; -C -1 ; WX 456 ; N atilde ; B 30 -15 435 722 ; -C -1 ; WX 228 ; N icircumflex ; B -5 0 234 734 ; -C -1 ; WX 273 ; N threesuperior ; B 4 270 266 703 ; -C -1 ; WX 456 ; N ecircumflex ; B 33 -15 423 734 ; -C -1 ; WX 456 ; N thorn ; B 48 -207 424 718 ; -C -1 ; WX 456 ; N egrave ; B 33 -15 423 734 ; -C -1 ; WX 273 ; N twosuperior ; B 3 281 265 703 ; -C -1 ; WX 456 ; N eacute ; B 33 -15 423 734 ; -C -1 ; WX 456 ; N otilde ; B 29 -14 427 722 ; -C -1 ; WX 547 ; N Aacute ; B 11 0 536 929 ; -C -1 ; WX 456 ; N ocircumflex ; B 29 -14 427 734 ; -C -1 ; WX 410 ; N yacute ; B 9 -214 401 734 ; -C -1 ; WX 456 ; N udieresis ; B 56 -15 401 706 ; -C -1 ; WX 684 ; N threequarters ; B 37 -19 664 703 ; -C -1 ; WX 456 ; N acircumflex ; B 30 -15 435 734 ; -C -1 ; WX 592 ; N Eth ; B 0 0 553 718 ; -C -1 ; WX 456 ; N edieresis ; B 33 -15 423 706 ; -C -1 ; WX 456 ; N ugrave ; B 56 -15 401 734 ; -C -1 ; WX 820 ; N trademark ; B 38 306 740 718 ; -C -1 ; WX 456 ; N ograve ; B 29 -14 427 734 ; -C -1 ; WX 410 ; N scaron ; B 26 -15 380 734 ; -C -1 ; WX 228 ; N Idieresis ; B 11 0 218 901 ; -C -1 ; WX 456 ; N uacute ; B 56 -15 401 734 ; -C -1 ; WX 456 ; N agrave ; B 30 -15 435 734 ; -C -1 ; WX 456 ; N ntilde ; B 53 0 403 722 ; -C -1 ; WX 456 ; N aring ; B 30 -15 435 756 ; -C -1 ; WX 410 ; N zcaron ; B 25 0 385 734 ; -C -1 ; WX 228 ; N Icircumflex ; B -5 0 234 929 ; -C -1 ; WX 592 ; N Ntilde ; B 62 0 530 917 ; -C -1 ; WX 456 ; N ucircumflex ; B 56 -15 401 734 ; -C -1 ; WX 547 ; N Ecircumflex ; B 71 0 505 929 ; -C -1 ; WX 228 ; N Iacute ; B 75 0 239 929 ; -C -1 ; WX 592 ; N Ccedilla ; B 36 -225 558 737 ; -C -1 ; WX 638 ; N Odieresis ; B 32 -19 606 901 ; -C -1 ; WX 547 ; N Scaron ; B 40 -19 508 929 ; -C -1 ; WX 547 ; N Edieresis ; B 71 0 505 901 ; -C -1 ; WX 228 ; N Igrave ; B -11 0 154 929 ; -C -1 ; WX 456 ; N adieresis ; B 30 -15 435 706 ; -C -1 ; WX 638 ; N Ograve ; B 32 -19 606 929 ; -C -1 ; WX 547 ; N Egrave ; B 71 0 505 929 ; -C -1 ; WX 547 ; N Ydieresis ; B 11 0 535 901 ; -C -1 ; WX 604 ; N registered ; B -11 -19 617 737 ; -C -1 ; WX 638 ; N Otilde ; B 32 -19 606 917 ; -C -1 ; WX 684 ; N onequarter ; B 60 -19 620 703 ; -C -1 ; WX 592 ; N Ugrave ; B 65 -19 528 929 ; -C -1 ; WX 592 ; N Ucircumflex ; B 65 -19 528 929 ; -C -1 ; WX 547 ; N Thorn ; B 71 0 510 718 ; -C -1 ; WX 479 ; N divide ; B 32 -19 447 524 ; -C -1 ; WX 547 ; N Atilde ; B 11 0 536 917 ; -C -1 ; WX 592 ; N Uacute ; B 65 -19 528 929 ; -C -1 ; WX 638 ; N Ocircumflex ; B 32 -19 606 929 ; -C -1 ; WX 479 ; N logicalnot ; B 32 108 447 390 ; -C -1 ; WX 547 ; N Aring ; B 11 0 536 931 ; -C -1 ; WX 228 ; N idieresis ; B 11 0 218 706 ; -C -1 ; WX 228 ; N iacute ; B 78 0 239 734 ; -C -1 ; WX 456 ; N aacute ; B 30 -15 435 734 ; -C -1 ; WX 479 ; N plusminus ; B 32 0 447 506 ; -C -1 ; WX 479 ; N multiply ; B 32 0 447 506 ; -C -1 ; WX 592 ; N Udieresis ; B 65 -19 528 901 ; -C -1 ; WX 479 ; N minus ; B 32 216 447 289 ; -C -1 ; WX 273 ; N onesuperior ; B 35 281 182 703 ; -C -1 ; WX 547 ; N Eacute ; B 71 0 505 929 ; -C -1 ; WX 547 ; N Acircumflex ; B 11 0 536 929 ; -C -1 ; WX 604 ; N copyright ; B -11 -19 617 737 ; -C -1 ; WX 547 ; N Agrave ; B 11 0 536 929 ; -C -1 ; WX 456 ; N odieresis ; B 29 -14 427 706 ; -C -1 ; WX 456 ; N oacute ; B 29 -14 427 734 ; -C -1 ; WX 328 ; N degree ; B 44 411 284 703 ; -C -1 ; WX 228 ; N igrave ; B -11 0 151 734 ; -C -1 ; WX 456 ; N mu ; B 56 -207 401 523 ; -C -1 ; WX 638 ; N Oacute ; B 32 -19 606 929 ; -C -1 ; WX 456 ; N eth ; B 29 -15 428 737 ; -C -1 ; WX 547 ; N Adieresis ; B 11 0 536 901 ; -C -1 ; WX 547 ; N Yacute ; B 11 0 535 929 ; -C -1 ; WX 213 ; N brokenbar ; B 77 -19 137 737 ; -C -1 ; WX 684 ; N onehalf ; B 35 -19 634 703 ; -EndCharMetrics -StartKernData -StartKernPairs 250 - -KPX A y -32 -KPX A w -32 -KPX A v -32 -KPX A u -24 -KPX A Y -81 -KPX A W -40 -KPX A V -56 -KPX A U -40 -KPX A T -97 -KPX A Q -24 -KPX A O -24 -KPX A G -24 -KPX A C -24 - -KPX B period -15 -KPX B comma -15 -KPX B U -7 - -KPX C period -24 -KPX C comma -24 - -KPX D period -56 -KPX D comma -56 -KPX D Y -73 -KPX D W -32 -KPX D V -56 -KPX D A -32 - -KPX F r -36 -KPX F period -122 -KPX F o -24 -KPX F e -24 -KPX F comma -122 -KPX F a -40 -KPX F A -65 - -KPX J u -15 -KPX J period -24 -KPX J comma -24 -KPX J a -15 -KPX J A -15 - -KPX K y -40 -KPX K u -24 -KPX K o -32 -KPX K e -32 -KPX K O -40 - -KPX L y -24 -KPX L quoteright -130 -KPX L quotedblright -114 -KPX L Y -114 -KPX L W -56 -KPX L V -89 -KPX L T -89 - -KPX O period -32 -KPX O comma -32 -KPX O Y -56 -KPX O X -48 -KPX O W -24 -KPX O V -40 -KPX O T -32 -KPX O A -15 - -KPX P period -147 -KPX P o -40 -KPX P e -40 -KPX P comma -147 -KPX P a -32 -KPX P A -97 - -KPX Q U -7 - -KPX R Y -40 -KPX R W -24 -KPX R V -40 -KPX R U -32 -KPX R T -24 -KPX R O -15 - -KPX S period -15 -KPX S comma -15 - -KPX T y -97 -KPX T w -97 -KPX T u -97 -KPX T semicolon -15 -KPX T r -97 -KPX T period -97 -KPX T o -97 -KPX T hyphen -114 -KPX T e -97 -KPX T comma -97 -KPX T colon -15 -KPX T a -97 -KPX T O -32 -KPX T A -97 - -KPX U period -32 -KPX U comma -32 -KPX U A -32 - -KPX V u -56 -KPX V semicolon -32 -KPX V period -102 -KPX V o -65 -KPX V hyphen -65 -KPX V e -65 -KPX V comma -102 -KPX V colon -32 -KPX V a -56 -KPX V O -32 -KPX V G -32 -KPX V A -65 - -KPX W y -15 -KPX W u -24 -KPX W period -65 -KPX W o -24 -KPX W hyphen -32 -KPX W e -24 -KPX W comma -65 -KPX W a -32 -KPX W O -15 -KPX W A -40 - -KPX Y u -89 -KPX Y semicolon -48 -KPX Y period -114 -KPX Y o -114 -KPX Y i -15 -KPX Y hyphen -114 -KPX Y e -114 -KPX Y comma -114 -KPX Y colon -48 -KPX Y a -114 -KPX Y O -69 -KPX Y A -89 - -KPX a y -24 -KPX a w -15 -KPX a v -15 - -KPX b y -15 -KPX b v -15 -KPX b u -15 -KPX b period -32 -KPX b l -15 -KPX b comma -32 -KPX b b -7 - -KPX c k -15 -KPX c comma -11 - -KPX colon space -40 - -KPX comma quoteright -81 -KPX comma quotedblright -81 - -KPX e y -15 -KPX e x -24 -KPX e w -15 -KPX e v -24 -KPX e period -11 -KPX e comma -11 - -KPX f quoteright 41 -KPX f quotedblright 49 -KPX f period -24 -KPX f o -24 -KPX f e -24 -KPX f dotlessi -22 -KPX f comma -24 -KPX f a -24 - -KPX g r -7 - -KPX h y -24 - -KPX k o -15 -KPX k e -15 - -KPX m y -11 -KPX m u -7 - -KPX n y -11 -KPX n v -15 -KPX n u -7 - -KPX o y -24 -KPX o x -24 -KPX o w -11 -KPX o v -11 -KPX o period -32 -KPX o comma -32 - -KPX oslash z -44 -KPX oslash y -56 -KPX oslash x -69 -KPX oslash w -56 -KPX oslash v -56 -KPX oslash u -44 -KPX oslash t -44 -KPX oslash s -44 -KPX oslash r -44 -KPX oslash q -44 -KPX oslash period -77 -KPX oslash p -44 -KPX oslash o -44 -KPX oslash n -44 -KPX oslash m -44 -KPX oslash l -44 -KPX oslash k -44 -KPX oslash j -44 -KPX oslash i -44 -KPX oslash h -44 -KPX oslash g -44 -KPX oslash f -44 -KPX oslash e -44 -KPX oslash d -44 -KPX oslash comma -77 -KPX oslash c -44 -KPX oslash b -44 -KPX oslash a -44 - -KPX p y -24 -KPX p period -28 -KPX p comma -28 - -KPX period space -48 -KPX period quoteright -81 -KPX period quotedblright -81 - -KPX quotedblright space -32 - -KPX quoteleft quoteleft -46 - -KPX quoteright space -56 -KPX quoteright s -40 -KPX quoteright r -40 -KPX quoteright quoteright -46 -KPX quoteright d -40 - -KPX r y 25 -KPX r v 25 -KPX r u 12 -KPX r t 33 -KPX r semicolon 25 -KPX r period -40 -KPX r p 25 -KPX r n 21 -KPX r m 21 -KPX r l 12 -KPX r k 12 -KPX r i 12 -KPX r comma -40 -KPX r colon 25 -KPX r a -7 - -KPX s w -24 -KPX s period -11 -KPX s comma -11 - -KPX semicolon space -40 - -KPX space quoteleft -48 -KPX space quotedblleft -24 -KPX space Y -73 -KPX space W -32 -KPX space V -40 -KPX space T -40 - -KPX v period -65 -KPX v o -20 -KPX v e -20 -KPX v comma -65 -KPX v a -20 - -KPX w period -48 -KPX w o -7 -KPX w e -7 -KPX w comma -48 -KPX w a -11 - -KPX x e -24 - -KPX y period -81 -KPX y o -15 -KPX y e -15 -KPX y comma -81 -KPX y a -15 - -KPX z o -11 -KPX z e -11 -EndKernPairs -EndKernData -StartComposites 58 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 137 195 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 137 195 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 137 195 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 137 195 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 137 175 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 137 195 ; -CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 160 0 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 137 195 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 137 195 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 137 195 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 137 195 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute -22 195 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex -22 195 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis -22 195 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave -22 195 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 168 195 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 183 195 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 183 195 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 183 195 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 183 195 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 183 195 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 137 195 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 160 195 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 160 195 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 160 195 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 160 195 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 137 195 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 137 195 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 114 195 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 92 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 92 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 92 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 92 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 92 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 84 0 ; -CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 69 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 92 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 92 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 92 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 92 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -22 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -22 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -22 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -22 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 84 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 92 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 92 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 92 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 92 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 92 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 69 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 92 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 92 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 92 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 92 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 69 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 69 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 69 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvro8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvro8a.afm deleted file mode 100644 index 3d69eb7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvro8a.afm +++ /dev/null @@ -1,612 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All rights reserved. -Comment Creation Date: Thu Mar 15 10:24:18 1990 -Comment UniqueID 28362 -Comment VMusage 7572 42473 -FontName Helvetica-Oblique -FullName Helvetica Oblique -FamilyName Helvetica -Weight Medium -ItalicAngle -12 -IsFixedPitch false -FontBBox -170 -225 1116 931 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.006 -Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All rights reserved.Helvetica is a trademark of Linotype AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 718 -XHeight 523 -Ascender 718 -Descender -207 -StartCharMetrics 228 -C 32 ; WX 278 ; N space ; B 0 0 0 0 ; -C 33 ; WX 278 ; N exclam ; B 90 0 340 718 ; -C 34 ; WX 355 ; N quotedbl ; B 168 463 438 718 ; -C 35 ; WX 556 ; N numbersign ; B 73 0 631 688 ; -C 36 ; WX 556 ; N dollar ; B 69 -115 617 775 ; -C 37 ; WX 889 ; N percent ; B 147 -19 889 703 ; -C 38 ; WX 667 ; N ampersand ; B 77 -15 647 718 ; -C 39 ; WX 222 ; N quoteright ; B 151 463 310 718 ; -C 40 ; WX 333 ; N parenleft ; B 108 -207 454 733 ; -C 41 ; WX 333 ; N parenright ; B -9 -207 337 733 ; -C 42 ; WX 389 ; N asterisk ; B 165 431 475 718 ; -C 43 ; WX 584 ; N plus ; B 85 0 606 505 ; -C 44 ; WX 278 ; N comma ; B 56 -147 214 106 ; -C 45 ; WX 333 ; N hyphen ; B 93 232 357 322 ; -C 46 ; WX 278 ; N period ; B 87 0 214 106 ; -C 47 ; WX 278 ; N slash ; B -21 -19 452 737 ; -C 48 ; WX 556 ; N zero ; B 93 -19 608 703 ; -C 49 ; WX 556 ; N one ; B 207 0 508 703 ; -C 50 ; WX 556 ; N two ; B 26 0 617 703 ; -C 51 ; WX 556 ; N three ; B 75 -19 610 703 ; -C 52 ; WX 556 ; N four ; B 61 0 576 703 ; -C 53 ; WX 556 ; N five ; B 68 -19 621 688 ; -C 54 ; WX 556 ; N six ; B 91 -19 615 703 ; -C 55 ; WX 556 ; N seven ; B 137 0 669 688 ; -C 56 ; WX 556 ; N eight ; B 74 -19 607 703 ; -C 57 ; WX 556 ; N nine ; B 82 -19 609 703 ; -C 58 ; WX 278 ; N colon ; B 87 0 301 516 ; -C 59 ; WX 278 ; N semicolon ; B 56 -147 301 516 ; -C 60 ; WX 584 ; N less ; B 94 11 641 495 ; -C 61 ; WX 584 ; N equal ; B 63 115 628 390 ; -C 62 ; WX 584 ; N greater ; B 50 11 597 495 ; -C 63 ; WX 556 ; N question ; B 161 0 610 727 ; -C 64 ; WX 1015 ; N at ; B 215 -19 965 737 ; -C 65 ; WX 667 ; N A ; B 14 0 654 718 ; -C 66 ; WX 667 ; N B ; B 74 0 712 718 ; -C 67 ; WX 722 ; N C ; B 108 -19 782 737 ; -C 68 ; WX 722 ; N D ; B 81 0 764 718 ; -C 69 ; WX 667 ; N E ; B 86 0 762 718 ; -C 70 ; WX 611 ; N F ; B 86 0 736 718 ; -C 71 ; WX 778 ; N G ; B 111 -19 799 737 ; -C 72 ; WX 722 ; N H ; B 77 0 799 718 ; -C 73 ; WX 278 ; N I ; B 91 0 341 718 ; -C 74 ; WX 500 ; N J ; B 47 -19 581 718 ; -C 75 ; WX 667 ; N K ; B 76 0 808 718 ; -C 76 ; WX 556 ; N L ; B 76 0 555 718 ; -C 77 ; WX 833 ; N M ; B 73 0 914 718 ; -C 78 ; WX 722 ; N N ; B 76 0 799 718 ; -C 79 ; WX 778 ; N O ; B 105 -19 826 737 ; -C 80 ; WX 667 ; N P ; B 86 0 737 718 ; -C 81 ; WX 778 ; N Q ; B 105 -56 826 737 ; -C 82 ; WX 722 ; N R ; B 88 0 773 718 ; -C 83 ; WX 667 ; N S ; B 90 -19 713 737 ; -C 84 ; WX 611 ; N T ; B 148 0 750 718 ; -C 85 ; WX 722 ; N U ; B 123 -19 797 718 ; -C 86 ; WX 667 ; N V ; B 173 0 800 718 ; -C 87 ; WX 944 ; N W ; B 169 0 1081 718 ; -C 88 ; WX 667 ; N X ; B 19 0 790 718 ; -C 89 ; WX 667 ; N Y ; B 167 0 806 718 ; -C 90 ; WX 611 ; N Z ; B 23 0 741 718 ; -C 91 ; WX 278 ; N bracketleft ; B 21 -196 403 722 ; -C 92 ; WX 278 ; N backslash ; B 140 -19 291 737 ; -C 93 ; WX 278 ; N bracketright ; B -14 -196 368 722 ; -C 94 ; WX 469 ; N asciicircum ; B 42 264 539 688 ; -C 95 ; WX 556 ; N underscore ; B -27 -125 540 -75 ; -C 96 ; WX 222 ; N quoteleft ; B 165 470 323 725 ; -C 97 ; WX 556 ; N a ; B 61 -15 559 538 ; -C 98 ; WX 556 ; N b ; B 58 -15 584 718 ; -C 99 ; WX 500 ; N c ; B 74 -15 553 538 ; -C 100 ; WX 556 ; N d ; B 84 -15 652 718 ; -C 101 ; WX 556 ; N e ; B 84 -15 578 538 ; -C 102 ; WX 278 ; N f ; B 86 0 416 728 ; L i fi ; L l fl ; -C 103 ; WX 556 ; N g ; B 42 -220 610 538 ; -C 104 ; WX 556 ; N h ; B 65 0 573 718 ; -C 105 ; WX 222 ; N i ; B 67 0 308 718 ; -C 106 ; WX 222 ; N j ; B -60 -210 308 718 ; -C 107 ; WX 500 ; N k ; B 67 0 600 718 ; -C 108 ; WX 222 ; N l ; B 67 0 308 718 ; -C 109 ; WX 833 ; N m ; B 65 0 852 538 ; -C 110 ; WX 556 ; N n ; B 65 0 573 538 ; -C 111 ; WX 556 ; N o ; B 83 -14 585 538 ; -C 112 ; WX 556 ; N p ; B 14 -207 584 538 ; -C 113 ; WX 556 ; N q ; B 84 -207 605 538 ; -C 114 ; WX 333 ; N r ; B 77 0 446 538 ; -C 115 ; WX 500 ; N s ; B 63 -15 529 538 ; -C 116 ; WX 278 ; N t ; B 102 -7 368 669 ; -C 117 ; WX 556 ; N u ; B 94 -15 600 523 ; -C 118 ; WX 500 ; N v ; B 119 0 603 523 ; -C 119 ; WX 722 ; N w ; B 125 0 820 523 ; -C 120 ; WX 500 ; N x ; B 11 0 594 523 ; -C 121 ; WX 500 ; N y ; B 15 -214 600 523 ; -C 122 ; WX 500 ; N z ; B 31 0 571 523 ; -C 123 ; WX 334 ; N braceleft ; B 92 -196 445 722 ; -C 124 ; WX 260 ; N bar ; B 90 -19 324 737 ; -C 125 ; WX 334 ; N braceright ; B 0 -196 354 722 ; -C 126 ; WX 584 ; N asciitilde ; B 111 180 580 326 ; -C 161 ; WX 333 ; N exclamdown ; B 77 -195 326 523 ; -C 162 ; WX 556 ; N cent ; B 95 -115 584 623 ; -C 163 ; WX 556 ; N sterling ; B 49 -16 634 718 ; -C 164 ; WX 167 ; N fraction ; B -170 -19 482 703 ; -C 165 ; WX 556 ; N yen ; B 81 0 699 688 ; -C 166 ; WX 556 ; N florin ; B -52 -207 654 737 ; -C 167 ; WX 556 ; N section ; B 76 -191 584 737 ; -C 168 ; WX 556 ; N currency ; B 60 99 646 603 ; -C 169 ; WX 191 ; N quotesingle ; B 157 463 285 718 ; -C 170 ; WX 333 ; N quotedblleft ; B 138 470 461 725 ; -C 171 ; WX 556 ; N guillemotleft ; B 146 108 554 446 ; -C 172 ; WX 333 ; N guilsinglleft ; B 137 108 340 446 ; -C 173 ; WX 333 ; N guilsinglright ; B 111 108 314 446 ; -C 174 ; WX 500 ; N fi ; B 86 0 587 728 ; -C 175 ; WX 500 ; N fl ; B 86 0 585 728 ; -C 177 ; WX 556 ; N endash ; B 51 240 623 313 ; -C 178 ; WX 556 ; N dagger ; B 135 -159 622 718 ; -C 179 ; WX 556 ; N daggerdbl ; B 52 -159 623 718 ; -C 180 ; WX 278 ; N periodcentered ; B 129 190 257 315 ; -C 182 ; WX 537 ; N paragraph ; B 126 -173 650 718 ; -C 183 ; WX 350 ; N bullet ; B 91 202 413 517 ; -C 184 ; WX 222 ; N quotesinglbase ; B 21 -149 180 106 ; -C 185 ; WX 333 ; N quotedblbase ; B -6 -149 318 106 ; -C 186 ; WX 333 ; N quotedblright ; B 124 463 448 718 ; -C 187 ; WX 556 ; N guillemotright ; B 120 108 528 446 ; -C 188 ; WX 1000 ; N ellipsis ; B 115 0 908 106 ; -C 189 ; WX 1000 ; N perthousand ; B 88 -19 1029 703 ; -C 191 ; WX 611 ; N questiondown ; B 85 -201 534 525 ; -C 193 ; WX 333 ; N grave ; B 170 593 337 734 ; -C 194 ; WX 333 ; N acute ; B 248 593 475 734 ; -C 195 ; WX 333 ; N circumflex ; B 147 593 438 734 ; -C 196 ; WX 333 ; N tilde ; B 125 606 490 722 ; -C 197 ; WX 333 ; N macron ; B 143 627 468 684 ; -C 198 ; WX 333 ; N breve ; B 167 595 476 731 ; -C 199 ; WX 333 ; N dotaccent ; B 249 604 362 706 ; -C 200 ; WX 333 ; N dieresis ; B 168 604 443 706 ; -C 202 ; WX 333 ; N ring ; B 214 572 402 756 ; -C 203 ; WX 333 ; N cedilla ; B 2 -225 232 0 ; -C 205 ; WX 333 ; N hungarumlaut ; B 157 593 565 734 ; -C 206 ; WX 333 ; N ogonek ; B 43 -225 249 0 ; -C 207 ; WX 333 ; N caron ; B 177 593 468 734 ; -C 208 ; WX 1000 ; N emdash ; B 51 240 1067 313 ; -C 225 ; WX 1000 ; N AE ; B 8 0 1097 718 ; -C 227 ; WX 370 ; N ordfeminine ; B 100 304 449 737 ; -C 232 ; WX 556 ; N Lslash ; B 41 0 555 718 ; -C 233 ; WX 778 ; N Oslash ; B 43 -19 890 737 ; -C 234 ; WX 1000 ; N OE ; B 98 -19 1116 737 ; -C 235 ; WX 365 ; N ordmasculine ; B 100 304 468 737 ; -C 241 ; WX 889 ; N ae ; B 61 -15 909 538 ; -C 245 ; WX 278 ; N dotlessi ; B 95 0 294 523 ; -C 248 ; WX 222 ; N lslash ; B 41 0 347 718 ; -C 249 ; WX 611 ; N oslash ; B 29 -22 647 545 ; -C 250 ; WX 944 ; N oe ; B 83 -15 964 538 ; -C 251 ; WX 611 ; N germandbls ; B 67 -15 658 728 ; -C -1 ; WX 611 ; N Zcaron ; B 23 0 741 929 ; -C -1 ; WX 500 ; N ccedilla ; B 74 -225 553 538 ; -C -1 ; WX 500 ; N ydieresis ; B 15 -214 600 706 ; -C -1 ; WX 556 ; N atilde ; B 61 -15 592 722 ; -C -1 ; WX 278 ; N icircumflex ; B 95 0 411 734 ; -C -1 ; WX 333 ; N threesuperior ; B 90 270 436 703 ; -C -1 ; WX 556 ; N ecircumflex ; B 84 -15 578 734 ; -C -1 ; WX 556 ; N thorn ; B 14 -207 584 718 ; -C -1 ; WX 556 ; N egrave ; B 84 -15 578 734 ; -C -1 ; WX 333 ; N twosuperior ; B 64 281 449 703 ; -C -1 ; WX 556 ; N eacute ; B 84 -15 587 734 ; -C -1 ; WX 556 ; N otilde ; B 83 -14 602 722 ; -C -1 ; WX 667 ; N Aacute ; B 14 0 683 929 ; -C -1 ; WX 556 ; N ocircumflex ; B 83 -14 585 734 ; -C -1 ; WX 500 ; N yacute ; B 15 -214 600 734 ; -C -1 ; WX 556 ; N udieresis ; B 94 -15 600 706 ; -C -1 ; WX 834 ; N threequarters ; B 130 -19 861 703 ; -C -1 ; WX 556 ; N acircumflex ; B 61 -15 559 734 ; -C -1 ; WX 722 ; N Eth ; B 69 0 764 718 ; -C -1 ; WX 556 ; N edieresis ; B 84 -15 578 706 ; -C -1 ; WX 556 ; N ugrave ; B 94 -15 600 734 ; -C -1 ; WX 1000 ; N trademark ; B 186 306 1056 718 ; -C -1 ; WX 556 ; N ograve ; B 83 -14 585 734 ; -C -1 ; WX 500 ; N scaron ; B 63 -15 552 734 ; -C -1 ; WX 278 ; N Idieresis ; B 91 0 458 901 ; -C -1 ; WX 556 ; N uacute ; B 94 -15 600 734 ; -C -1 ; WX 556 ; N agrave ; B 61 -15 559 734 ; -C -1 ; WX 556 ; N ntilde ; B 65 0 592 722 ; -C -1 ; WX 556 ; N aring ; B 61 -15 559 756 ; -C -1 ; WX 500 ; N zcaron ; B 31 0 571 734 ; -C -1 ; WX 278 ; N Icircumflex ; B 91 0 452 929 ; -C -1 ; WX 722 ; N Ntilde ; B 76 0 799 917 ; -C -1 ; WX 556 ; N ucircumflex ; B 94 -15 600 734 ; -C -1 ; WX 667 ; N Ecircumflex ; B 86 0 762 929 ; -C -1 ; WX 278 ; N Iacute ; B 91 0 489 929 ; -C -1 ; WX 722 ; N Ccedilla ; B 108 -225 782 737 ; -C -1 ; WX 778 ; N Odieresis ; B 105 -19 826 901 ; -C -1 ; WX 667 ; N Scaron ; B 90 -19 713 929 ; -C -1 ; WX 667 ; N Edieresis ; B 86 0 762 901 ; -C -1 ; WX 278 ; N Igrave ; B 91 0 351 929 ; -C -1 ; WX 556 ; N adieresis ; B 61 -15 559 706 ; -C -1 ; WX 778 ; N Ograve ; B 105 -19 826 929 ; -C -1 ; WX 667 ; N Egrave ; B 86 0 762 929 ; -C -1 ; WX 667 ; N Ydieresis ; B 167 0 806 901 ; -C -1 ; WX 737 ; N registered ; B 54 -19 837 737 ; -C -1 ; WX 778 ; N Otilde ; B 105 -19 826 917 ; -C -1 ; WX 834 ; N onequarter ; B 150 -19 802 703 ; -C -1 ; WX 722 ; N Ugrave ; B 123 -19 797 929 ; -C -1 ; WX 722 ; N Ucircumflex ; B 123 -19 797 929 ; -C -1 ; WX 667 ; N Thorn ; B 86 0 712 718 ; -C -1 ; WX 584 ; N divide ; B 85 -19 606 524 ; -C -1 ; WX 667 ; N Atilde ; B 14 0 699 917 ; -C -1 ; WX 722 ; N Uacute ; B 123 -19 797 929 ; -C -1 ; WX 778 ; N Ocircumflex ; B 105 -19 826 929 ; -C -1 ; WX 584 ; N logicalnot ; B 106 108 628 390 ; -C -1 ; WX 667 ; N Aring ; B 14 0 654 931 ; -C -1 ; WX 278 ; N idieresis ; B 95 0 416 706 ; -C -1 ; WX 278 ; N iacute ; B 95 0 448 734 ; -C -1 ; WX 556 ; N aacute ; B 61 -15 587 734 ; -C -1 ; WX 584 ; N plusminus ; B 39 0 618 506 ; -C -1 ; WX 584 ; N multiply ; B 50 0 642 506 ; -C -1 ; WX 722 ; N Udieresis ; B 123 -19 797 901 ; -C -1 ; WX 584 ; N minus ; B 85 216 606 289 ; -C -1 ; WX 333 ; N onesuperior ; B 166 281 371 703 ; -C -1 ; WX 667 ; N Eacute ; B 86 0 762 929 ; -C -1 ; WX 667 ; N Acircumflex ; B 14 0 654 929 ; -C -1 ; WX 737 ; N copyright ; B 54 -19 837 737 ; -C -1 ; WX 667 ; N Agrave ; B 14 0 654 929 ; -C -1 ; WX 556 ; N odieresis ; B 83 -14 585 706 ; -C -1 ; WX 556 ; N oacute ; B 83 -14 587 734 ; -C -1 ; WX 400 ; N degree ; B 169 411 468 703 ; -C -1 ; WX 278 ; N igrave ; B 95 0 310 734 ; -C -1 ; WX 556 ; N mu ; B 24 -207 600 523 ; -C -1 ; WX 778 ; N Oacute ; B 105 -19 826 929 ; -C -1 ; WX 556 ; N eth ; B 81 -15 617 737 ; -C -1 ; WX 667 ; N Adieresis ; B 14 0 654 901 ; -C -1 ; WX 667 ; N Yacute ; B 167 0 806 929 ; -C -1 ; WX 260 ; N brokenbar ; B 90 -19 324 737 ; -C -1 ; WX 834 ; N onehalf ; B 114 -19 839 703 ; -EndCharMetrics -StartKernData -StartKernPairs 250 - -KPX A y -40 -KPX A w -40 -KPX A v -40 -KPX A u -30 -KPX A Y -100 -KPX A W -50 -KPX A V -70 -KPX A U -50 -KPX A T -120 -KPX A Q -30 -KPX A O -30 -KPX A G -30 -KPX A C -30 - -KPX B period -20 -KPX B comma -20 -KPX B U -10 - -KPX C period -30 -KPX C comma -30 - -KPX D period -70 -KPX D comma -70 -KPX D Y -90 -KPX D W -40 -KPX D V -70 -KPX D A -40 - -KPX F r -45 -KPX F period -150 -KPX F o -30 -KPX F e -30 -KPX F comma -150 -KPX F a -50 -KPX F A -80 - -KPX J u -20 -KPX J period -30 -KPX J comma -30 -KPX J a -20 -KPX J A -20 - -KPX K y -50 -KPX K u -30 -KPX K o -40 -KPX K e -40 -KPX K O -50 - -KPX L y -30 -KPX L quoteright -160 -KPX L quotedblright -140 -KPX L Y -140 -KPX L W -70 -KPX L V -110 -KPX L T -110 - -KPX O period -40 -KPX O comma -40 -KPX O Y -70 -KPX O X -60 -KPX O W -30 -KPX O V -50 -KPX O T -40 -KPX O A -20 - -KPX P period -180 -KPX P o -50 -KPX P e -50 -KPX P comma -180 -KPX P a -40 -KPX P A -120 - -KPX Q U -10 - -KPX R Y -50 -KPX R W -30 -KPX R V -50 -KPX R U -40 -KPX R T -30 -KPX R O -20 - -KPX S period -20 -KPX S comma -20 - -KPX T y -120 -KPX T w -120 -KPX T u -120 -KPX T semicolon -20 -KPX T r -120 -KPX T period -120 -KPX T o -120 -KPX T hyphen -140 -KPX T e -120 -KPX T comma -120 -KPX T colon -20 -KPX T a -120 -KPX T O -40 -KPX T A -120 - -KPX U period -40 -KPX U comma -40 -KPX U A -40 - -KPX V u -70 -KPX V semicolon -40 -KPX V period -125 -KPX V o -80 -KPX V hyphen -80 -KPX V e -80 -KPX V comma -125 -KPX V colon -40 -KPX V a -70 -KPX V O -40 -KPX V G -40 -KPX V A -80 - -KPX W y -20 -KPX W u -30 -KPX W period -80 -KPX W o -30 -KPX W hyphen -40 -KPX W e -30 -KPX W comma -80 -KPX W a -40 -KPX W O -20 -KPX W A -50 - -KPX Y u -110 -KPX Y semicolon -60 -KPX Y period -140 -KPX Y o -140 -KPX Y i -20 -KPX Y hyphen -140 -KPX Y e -140 -KPX Y comma -140 -KPX Y colon -60 -KPX Y a -140 -KPX Y O -85 -KPX Y A -110 - -KPX a y -30 -KPX a w -20 -KPX a v -20 - -KPX b y -20 -KPX b v -20 -KPX b u -20 -KPX b period -40 -KPX b l -20 -KPX b comma -40 -KPX b b -10 - -KPX c k -20 -KPX c comma -15 - -KPX colon space -50 - -KPX comma quoteright -100 -KPX comma quotedblright -100 - -KPX e y -20 -KPX e x -30 -KPX e w -20 -KPX e v -30 -KPX e period -15 -KPX e comma -15 - -KPX f quoteright 50 -KPX f quotedblright 60 -KPX f period -30 -KPX f o -30 -KPX f e -30 -KPX f dotlessi -28 -KPX f comma -30 -KPX f a -30 - -KPX g r -10 - -KPX h y -30 - -KPX k o -20 -KPX k e -20 - -KPX m y -15 -KPX m u -10 - -KPX n y -15 -KPX n v -20 -KPX n u -10 - -KPX o y -30 -KPX o x -30 -KPX o w -15 -KPX o v -15 -KPX o period -40 -KPX o comma -40 - -KPX oslash z -55 -KPX oslash y -70 -KPX oslash x -85 -KPX oslash w -70 -KPX oslash v -70 -KPX oslash u -55 -KPX oslash t -55 -KPX oslash s -55 -KPX oslash r -55 -KPX oslash q -55 -KPX oslash period -95 -KPX oslash p -55 -KPX oslash o -55 -KPX oslash n -55 -KPX oslash m -55 -KPX oslash l -55 -KPX oslash k -55 -KPX oslash j -55 -KPX oslash i -55 -KPX oslash h -55 -KPX oslash g -55 -KPX oslash f -55 -KPX oslash e -55 -KPX oslash d -55 -KPX oslash comma -95 -KPX oslash c -55 -KPX oslash b -55 -KPX oslash a -55 - -KPX p y -30 -KPX p period -35 -KPX p comma -35 - -KPX period space -60 -KPX period quoteright -100 -KPX period quotedblright -100 - -KPX quotedblright space -40 - -KPX quoteleft quoteleft -57 - -KPX quoteright space -70 -KPX quoteright s -50 -KPX quoteright r -50 -KPX quoteright quoteright -57 -KPX quoteright d -50 - -KPX r y 30 -KPX r v 30 -KPX r u 15 -KPX r t 40 -KPX r semicolon 30 -KPX r period -50 -KPX r p 30 -KPX r n 25 -KPX r m 25 -KPX r l 15 -KPX r k 15 -KPX r i 15 -KPX r comma -50 -KPX r colon 30 -KPX r a -10 - -KPX s w -30 -KPX s period -15 -KPX s comma -15 - -KPX semicolon space -50 - -KPX space quoteleft -60 -KPX space quotedblleft -30 -KPX space Y -90 -KPX space W -40 -KPX space V -50 -KPX space T -50 - -KPX v period -80 -KPX v o -25 -KPX v e -25 -KPX v comma -80 -KPX v a -25 - -KPX w period -60 -KPX w o -10 -KPX w e -10 -KPX w comma -60 -KPX w a -15 - -KPX x e -30 - -KPX y period -100 -KPX y o -20 -KPX y e -20 -KPX y comma -100 -KPX y a -20 - -KPX z o -15 -KPX z e -15 -EndKernPairs -EndKernData -StartComposites 58 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 208 195 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 208 195 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 208 195 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 208 195 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 204 175 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 208 195 ; -CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 195 0 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 208 195 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 208 195 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 208 195 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 208 195 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 14 195 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 14 195 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 14 195 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 14 195 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 246 195 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 264 195 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 264 195 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 264 195 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 264 195 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 264 195 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 208 195 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 236 195 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 236 195 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 236 195 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 236 195 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 208 195 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 208 195 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 180 195 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 112 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 112 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 112 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 112 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 112 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 102 0 ; -CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 84 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 112 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 112 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 112 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 112 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -27 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -27 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -27 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -27 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 102 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 112 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 112 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 112 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 112 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 112 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 84 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 112 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 112 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 112 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 112 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 84 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 84 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 84 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvro8an.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvro8an.afm deleted file mode 100644 index f757319..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/phvro8an.afm +++ /dev/null @@ -1,612 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All rights reserved. -Comment Creation Date: Thu Mar 15 11:25:48 1990 -Comment UniqueID 28389 -Comment VMusage 7572 42473 -FontName Helvetica-Narrow-Oblique -FullName Helvetica Narrow Oblique -FamilyName Helvetica -Weight Medium -ItalicAngle -12 -IsFixedPitch false -FontBBox -139 -225 915 931 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.006 -Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All rights reserved.Helvetica is a trademark of Linotype AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 718 -XHeight 523 -Ascender 718 -Descender -207 -StartCharMetrics 228 -C 32 ; WX 228 ; N space ; B 0 0 0 0 ; -C 33 ; WX 228 ; N exclam ; B 74 0 278 718 ; -C 34 ; WX 291 ; N quotedbl ; B 138 463 359 718 ; -C 35 ; WX 456 ; N numbersign ; B 60 0 517 688 ; -C 36 ; WX 456 ; N dollar ; B 57 -115 506 775 ; -C 37 ; WX 729 ; N percent ; B 120 -19 729 703 ; -C 38 ; WX 547 ; N ampersand ; B 63 -15 530 718 ; -C 39 ; WX 182 ; N quoteright ; B 124 463 254 718 ; -C 40 ; WX 273 ; N parenleft ; B 89 -207 372 733 ; -C 41 ; WX 273 ; N parenright ; B -7 -207 276 733 ; -C 42 ; WX 319 ; N asterisk ; B 135 431 389 718 ; -C 43 ; WX 479 ; N plus ; B 70 0 497 505 ; -C 44 ; WX 228 ; N comma ; B 46 -147 175 106 ; -C 45 ; WX 273 ; N hyphen ; B 77 232 293 322 ; -C 46 ; WX 228 ; N period ; B 71 0 175 106 ; -C 47 ; WX 228 ; N slash ; B -17 -19 370 737 ; -C 48 ; WX 456 ; N zero ; B 77 -19 499 703 ; -C 49 ; WX 456 ; N one ; B 170 0 417 703 ; -C 50 ; WX 456 ; N two ; B 21 0 506 703 ; -C 51 ; WX 456 ; N three ; B 61 -19 500 703 ; -C 52 ; WX 456 ; N four ; B 50 0 472 703 ; -C 53 ; WX 456 ; N five ; B 55 -19 509 688 ; -C 54 ; WX 456 ; N six ; B 74 -19 504 703 ; -C 55 ; WX 456 ; N seven ; B 112 0 549 688 ; -C 56 ; WX 456 ; N eight ; B 60 -19 497 703 ; -C 57 ; WX 456 ; N nine ; B 67 -19 499 703 ; -C 58 ; WX 228 ; N colon ; B 71 0 247 516 ; -C 59 ; WX 228 ; N semicolon ; B 46 -147 247 516 ; -C 60 ; WX 479 ; N less ; B 77 11 526 495 ; -C 61 ; WX 479 ; N equal ; B 52 115 515 390 ; -C 62 ; WX 479 ; N greater ; B 41 11 490 495 ; -C 63 ; WX 456 ; N question ; B 132 0 500 727 ; -C 64 ; WX 832 ; N at ; B 176 -19 791 737 ; -C 65 ; WX 547 ; N A ; B 11 0 536 718 ; -C 66 ; WX 547 ; N B ; B 61 0 583 718 ; -C 67 ; WX 592 ; N C ; B 88 -19 640 737 ; -C 68 ; WX 592 ; N D ; B 66 0 626 718 ; -C 69 ; WX 547 ; N E ; B 71 0 625 718 ; -C 70 ; WX 501 ; N F ; B 71 0 603 718 ; -C 71 ; WX 638 ; N G ; B 91 -19 655 737 ; -C 72 ; WX 592 ; N H ; B 63 0 655 718 ; -C 73 ; WX 228 ; N I ; B 75 0 279 718 ; -C 74 ; WX 410 ; N J ; B 39 -19 476 718 ; -C 75 ; WX 547 ; N K ; B 62 0 662 718 ; -C 76 ; WX 456 ; N L ; B 62 0 455 718 ; -C 77 ; WX 683 ; N M ; B 60 0 749 718 ; -C 78 ; WX 592 ; N N ; B 62 0 655 718 ; -C 79 ; WX 638 ; N O ; B 86 -19 677 737 ; -C 80 ; WX 547 ; N P ; B 71 0 604 718 ; -C 81 ; WX 638 ; N Q ; B 86 -56 677 737 ; -C 82 ; WX 592 ; N R ; B 72 0 634 718 ; -C 83 ; WX 547 ; N S ; B 74 -19 584 737 ; -C 84 ; WX 501 ; N T ; B 122 0 615 718 ; -C 85 ; WX 592 ; N U ; B 101 -19 653 718 ; -C 86 ; WX 547 ; N V ; B 142 0 656 718 ; -C 87 ; WX 774 ; N W ; B 138 0 886 718 ; -C 88 ; WX 547 ; N X ; B 16 0 647 718 ; -C 89 ; WX 547 ; N Y ; B 137 0 661 718 ; -C 90 ; WX 501 ; N Z ; B 19 0 607 718 ; -C 91 ; WX 228 ; N bracketleft ; B 17 -196 331 722 ; -C 92 ; WX 228 ; N backslash ; B 115 -19 239 737 ; -C 93 ; WX 228 ; N bracketright ; B -11 -196 302 722 ; -C 94 ; WX 385 ; N asciicircum ; B 35 264 442 688 ; -C 95 ; WX 456 ; N underscore ; B -22 -125 443 -75 ; -C 96 ; WX 182 ; N quoteleft ; B 135 470 265 725 ; -C 97 ; WX 456 ; N a ; B 50 -15 458 538 ; -C 98 ; WX 456 ; N b ; B 48 -15 479 718 ; -C 99 ; WX 410 ; N c ; B 61 -15 454 538 ; -C 100 ; WX 456 ; N d ; B 69 -15 534 718 ; -C 101 ; WX 456 ; N e ; B 69 -15 474 538 ; -C 102 ; WX 228 ; N f ; B 71 0 341 728 ; L i fi ; L l fl ; -C 103 ; WX 456 ; N g ; B 34 -220 500 538 ; -C 104 ; WX 456 ; N h ; B 53 0 470 718 ; -C 105 ; WX 182 ; N i ; B 55 0 252 718 ; -C 106 ; WX 182 ; N j ; B -49 -210 252 718 ; -C 107 ; WX 410 ; N k ; B 55 0 492 718 ; -C 108 ; WX 182 ; N l ; B 55 0 252 718 ; -C 109 ; WX 683 ; N m ; B 53 0 699 538 ; -C 110 ; WX 456 ; N n ; B 53 0 470 538 ; -C 111 ; WX 456 ; N o ; B 68 -14 479 538 ; -C 112 ; WX 456 ; N p ; B 11 -207 479 538 ; -C 113 ; WX 456 ; N q ; B 69 -207 496 538 ; -C 114 ; WX 273 ; N r ; B 63 0 365 538 ; -C 115 ; WX 410 ; N s ; B 52 -15 434 538 ; -C 116 ; WX 228 ; N t ; B 84 -7 302 669 ; -C 117 ; WX 456 ; N u ; B 77 -15 492 523 ; -C 118 ; WX 410 ; N v ; B 98 0 495 523 ; -C 119 ; WX 592 ; N w ; B 103 0 673 523 ; -C 120 ; WX 410 ; N x ; B 9 0 487 523 ; -C 121 ; WX 410 ; N y ; B 12 -214 492 523 ; -C 122 ; WX 410 ; N z ; B 25 0 468 523 ; -C 123 ; WX 274 ; N braceleft ; B 75 -196 365 722 ; -C 124 ; WX 213 ; N bar ; B 74 -19 265 737 ; -C 125 ; WX 274 ; N braceright ; B 0 -196 291 722 ; -C 126 ; WX 479 ; N asciitilde ; B 91 180 476 326 ; -C 161 ; WX 273 ; N exclamdown ; B 63 -195 267 523 ; -C 162 ; WX 456 ; N cent ; B 78 -115 479 623 ; -C 163 ; WX 456 ; N sterling ; B 40 -16 520 718 ; -C 164 ; WX 137 ; N fraction ; B -139 -19 396 703 ; -C 165 ; WX 456 ; N yen ; B 67 0 573 688 ; -C 166 ; WX 456 ; N florin ; B -43 -207 537 737 ; -C 167 ; WX 456 ; N section ; B 63 -191 479 737 ; -C 168 ; WX 456 ; N currency ; B 49 99 530 603 ; -C 169 ; WX 157 ; N quotesingle ; B 129 463 233 718 ; -C 170 ; WX 273 ; N quotedblleft ; B 113 470 378 725 ; -C 171 ; WX 456 ; N guillemotleft ; B 120 108 454 446 ; -C 172 ; WX 273 ; N guilsinglleft ; B 112 108 279 446 ; -C 173 ; WX 273 ; N guilsinglright ; B 91 108 257 446 ; -C 174 ; WX 410 ; N fi ; B 71 0 481 728 ; -C 175 ; WX 410 ; N fl ; B 71 0 479 728 ; -C 177 ; WX 456 ; N endash ; B 42 240 510 313 ; -C 178 ; WX 456 ; N dagger ; B 110 -159 510 718 ; -C 179 ; WX 456 ; N daggerdbl ; B 43 -159 511 718 ; -C 180 ; WX 228 ; N periodcentered ; B 106 190 211 315 ; -C 182 ; WX 440 ; N paragraph ; B 103 -173 533 718 ; -C 183 ; WX 287 ; N bullet ; B 74 202 339 517 ; -C 184 ; WX 182 ; N quotesinglbase ; B 17 -149 147 106 ; -C 185 ; WX 273 ; N quotedblbase ; B -5 -149 260 106 ; -C 186 ; WX 273 ; N quotedblright ; B 102 463 367 718 ; -C 187 ; WX 456 ; N guillemotright ; B 98 108 433 446 ; -C 188 ; WX 820 ; N ellipsis ; B 94 0 744 106 ; -C 189 ; WX 820 ; N perthousand ; B 72 -19 844 703 ; -C 191 ; WX 501 ; N questiondown ; B 70 -201 438 525 ; -C 193 ; WX 273 ; N grave ; B 139 593 276 734 ; -C 194 ; WX 273 ; N acute ; B 203 593 390 734 ; -C 195 ; WX 273 ; N circumflex ; B 121 593 359 734 ; -C 196 ; WX 273 ; N tilde ; B 102 606 402 722 ; -C 197 ; WX 273 ; N macron ; B 117 627 384 684 ; -C 198 ; WX 273 ; N breve ; B 137 595 391 731 ; -C 199 ; WX 273 ; N dotaccent ; B 204 604 297 706 ; -C 200 ; WX 273 ; N dieresis ; B 138 604 363 706 ; -C 202 ; WX 273 ; N ring ; B 175 572 330 756 ; -C 203 ; WX 273 ; N cedilla ; B 2 -225 191 0 ; -C 205 ; WX 273 ; N hungarumlaut ; B 129 593 463 734 ; -C 206 ; WX 273 ; N ogonek ; B 35 -225 204 0 ; -C 207 ; WX 273 ; N caron ; B 145 593 384 734 ; -C 208 ; WX 820 ; N emdash ; B 42 240 875 313 ; -C 225 ; WX 820 ; N AE ; B 7 0 899 718 ; -C 227 ; WX 303 ; N ordfeminine ; B 82 304 368 737 ; -C 232 ; WX 456 ; N Lslash ; B 34 0 455 718 ; -C 233 ; WX 638 ; N Oslash ; B 35 -19 730 737 ; -C 234 ; WX 820 ; N OE ; B 80 -19 915 737 ; -C 235 ; WX 299 ; N ordmasculine ; B 82 304 384 737 ; -C 241 ; WX 729 ; N ae ; B 50 -15 746 538 ; -C 245 ; WX 228 ; N dotlessi ; B 78 0 241 523 ; -C 248 ; WX 182 ; N lslash ; B 34 0 284 718 ; -C 249 ; WX 501 ; N oslash ; B 24 -22 531 545 ; -C 250 ; WX 774 ; N oe ; B 68 -15 791 538 ; -C 251 ; WX 501 ; N germandbls ; B 55 -15 539 728 ; -C -1 ; WX 501 ; N Zcaron ; B 19 0 607 929 ; -C -1 ; WX 410 ; N ccedilla ; B 61 -225 454 538 ; -C -1 ; WX 410 ; N ydieresis ; B 12 -214 492 706 ; -C -1 ; WX 456 ; N atilde ; B 50 -15 486 722 ; -C -1 ; WX 228 ; N icircumflex ; B 78 0 337 734 ; -C -1 ; WX 273 ; N threesuperior ; B 74 270 358 703 ; -C -1 ; WX 456 ; N ecircumflex ; B 69 -15 474 734 ; -C -1 ; WX 456 ; N thorn ; B 11 -207 479 718 ; -C -1 ; WX 456 ; N egrave ; B 69 -15 474 734 ; -C -1 ; WX 273 ; N twosuperior ; B 52 281 368 703 ; -C -1 ; WX 456 ; N eacute ; B 69 -15 481 734 ; -C -1 ; WX 456 ; N otilde ; B 68 -14 494 722 ; -C -1 ; WX 547 ; N Aacute ; B 11 0 560 929 ; -C -1 ; WX 456 ; N ocircumflex ; B 68 -14 479 734 ; -C -1 ; WX 410 ; N yacute ; B 12 -214 492 734 ; -C -1 ; WX 456 ; N udieresis ; B 77 -15 492 706 ; -C -1 ; WX 684 ; N threequarters ; B 106 -19 706 703 ; -C -1 ; WX 456 ; N acircumflex ; B 50 -15 458 734 ; -C -1 ; WX 592 ; N Eth ; B 57 0 626 718 ; -C -1 ; WX 456 ; N edieresis ; B 69 -15 474 706 ; -C -1 ; WX 456 ; N ugrave ; B 77 -15 492 734 ; -C -1 ; WX 820 ; N trademark ; B 152 306 866 718 ; -C -1 ; WX 456 ; N ograve ; B 68 -14 479 734 ; -C -1 ; WX 410 ; N scaron ; B 52 -15 453 734 ; -C -1 ; WX 228 ; N Idieresis ; B 75 0 375 901 ; -C -1 ; WX 456 ; N uacute ; B 77 -15 492 734 ; -C -1 ; WX 456 ; N agrave ; B 50 -15 458 734 ; -C -1 ; WX 456 ; N ntilde ; B 53 0 486 722 ; -C -1 ; WX 456 ; N aring ; B 50 -15 458 756 ; -C -1 ; WX 410 ; N zcaron ; B 25 0 468 734 ; -C -1 ; WX 228 ; N Icircumflex ; B 75 0 371 929 ; -C -1 ; WX 592 ; N Ntilde ; B 62 0 655 917 ; -C -1 ; WX 456 ; N ucircumflex ; B 77 -15 492 734 ; -C -1 ; WX 547 ; N Ecircumflex ; B 71 0 625 929 ; -C -1 ; WX 228 ; N Iacute ; B 75 0 401 929 ; -C -1 ; WX 592 ; N Ccedilla ; B 88 -225 640 737 ; -C -1 ; WX 638 ; N Odieresis ; B 86 -19 677 901 ; -C -1 ; WX 547 ; N Scaron ; B 74 -19 584 929 ; -C -1 ; WX 547 ; N Edieresis ; B 71 0 625 901 ; -C -1 ; WX 228 ; N Igrave ; B 75 0 288 929 ; -C -1 ; WX 456 ; N adieresis ; B 50 -15 458 706 ; -C -1 ; WX 638 ; N Ograve ; B 86 -19 677 929 ; -C -1 ; WX 547 ; N Egrave ; B 71 0 625 929 ; -C -1 ; WX 547 ; N Ydieresis ; B 137 0 661 901 ; -C -1 ; WX 604 ; N registered ; B 44 -19 687 737 ; -C -1 ; WX 638 ; N Otilde ; B 86 -19 677 917 ; -C -1 ; WX 684 ; N onequarter ; B 123 -19 658 703 ; -C -1 ; WX 592 ; N Ugrave ; B 101 -19 653 929 ; -C -1 ; WX 592 ; N Ucircumflex ; B 101 -19 653 929 ; -C -1 ; WX 547 ; N Thorn ; B 71 0 584 718 ; -C -1 ; WX 479 ; N divide ; B 70 -19 497 524 ; -C -1 ; WX 547 ; N Atilde ; B 11 0 573 917 ; -C -1 ; WX 592 ; N Uacute ; B 101 -19 653 929 ; -C -1 ; WX 638 ; N Ocircumflex ; B 86 -19 677 929 ; -C -1 ; WX 479 ; N logicalnot ; B 87 108 515 390 ; -C -1 ; WX 547 ; N Aring ; B 11 0 536 931 ; -C -1 ; WX 228 ; N idieresis ; B 78 0 341 706 ; -C -1 ; WX 228 ; N iacute ; B 78 0 367 734 ; -C -1 ; WX 456 ; N aacute ; B 50 -15 481 734 ; -C -1 ; WX 479 ; N plusminus ; B 32 0 507 506 ; -C -1 ; WX 479 ; N multiply ; B 41 0 526 506 ; -C -1 ; WX 592 ; N Udieresis ; B 101 -19 653 901 ; -C -1 ; WX 479 ; N minus ; B 70 216 497 289 ; -C -1 ; WX 273 ; N onesuperior ; B 136 281 305 703 ; -C -1 ; WX 547 ; N Eacute ; B 71 0 625 929 ; -C -1 ; WX 547 ; N Acircumflex ; B 11 0 536 929 ; -C -1 ; WX 604 ; N copyright ; B 44 -19 687 737 ; -C -1 ; WX 547 ; N Agrave ; B 11 0 536 929 ; -C -1 ; WX 456 ; N odieresis ; B 68 -14 479 706 ; -C -1 ; WX 456 ; N oacute ; B 68 -14 481 734 ; -C -1 ; WX 328 ; N degree ; B 138 411 384 703 ; -C -1 ; WX 228 ; N igrave ; B 78 0 254 734 ; -C -1 ; WX 456 ; N mu ; B 20 -207 492 523 ; -C -1 ; WX 638 ; N Oacute ; B 86 -19 677 929 ; -C -1 ; WX 456 ; N eth ; B 67 -15 506 737 ; -C -1 ; WX 547 ; N Adieresis ; B 11 0 536 901 ; -C -1 ; WX 547 ; N Yacute ; B 137 0 661 929 ; -C -1 ; WX 213 ; N brokenbar ; B 74 -19 265 737 ; -C -1 ; WX 684 ; N onehalf ; B 93 -19 688 703 ; -EndCharMetrics -StartKernData -StartKernPairs 250 - -KPX A y -40 -KPX A w -40 -KPX A v -40 -KPX A u -30 -KPX A Y -100 -KPX A W -50 -KPX A V -70 -KPX A U -50 -KPX A T -120 -KPX A Q -30 -KPX A O -30 -KPX A G -30 -KPX A C -30 - -KPX B period -20 -KPX B comma -20 -KPX B U -10 - -KPX C period -30 -KPX C comma -30 - -KPX D period -70 -KPX D comma -70 -KPX D Y -90 -KPX D W -40 -KPX D V -70 -KPX D A -40 - -KPX F r -45 -KPX F period -150 -KPX F o -30 -KPX F e -30 -KPX F comma -150 -KPX F a -50 -KPX F A -80 - -KPX J u -20 -KPX J period -30 -KPX J comma -30 -KPX J a -20 -KPX J A -20 - -KPX K y -50 -KPX K u -30 -KPX K o -40 -KPX K e -40 -KPX K O -50 - -KPX L y -30 -KPX L quoteright -160 -KPX L quotedblright -140 -KPX L Y -140 -KPX L W -70 -KPX L V -110 -KPX L T -110 - -KPX O period -40 -KPX O comma -40 -KPX O Y -70 -KPX O X -60 -KPX O W -30 -KPX O V -50 -KPX O T -40 -KPX O A -20 - -KPX P period -180 -KPX P o -50 -KPX P e -50 -KPX P comma -180 -KPX P a -40 -KPX P A -120 - -KPX Q U -10 - -KPX R Y -50 -KPX R W -30 -KPX R V -50 -KPX R U -40 -KPX R T -30 -KPX R O -20 - -KPX S period -20 -KPX S comma -20 - -KPX T y -120 -KPX T w -120 -KPX T u -120 -KPX T semicolon -20 -KPX T r -120 -KPX T period -120 -KPX T o -120 -KPX T hyphen -140 -KPX T e -120 -KPX T comma -120 -KPX T colon -20 -KPX T a -120 -KPX T O -40 -KPX T A -120 - -KPX U period -40 -KPX U comma -40 -KPX U A -40 - -KPX V u -70 -KPX V semicolon -40 -KPX V period -125 -KPX V o -80 -KPX V hyphen -80 -KPX V e -80 -KPX V comma -125 -KPX V colon -40 -KPX V a -70 -KPX V O -40 -KPX V G -40 -KPX V A -80 - -KPX W y -20 -KPX W u -30 -KPX W period -80 -KPX W o -30 -KPX W hyphen -40 -KPX W e -30 -KPX W comma -80 -KPX W a -40 -KPX W O -20 -KPX W A -50 - -KPX Y u -110 -KPX Y semicolon -60 -KPX Y period -140 -KPX Y o -140 -KPX Y i -20 -KPX Y hyphen -140 -KPX Y e -140 -KPX Y comma -140 -KPX Y colon -60 -KPX Y a -140 -KPX Y O -85 -KPX Y A -110 - -KPX a y -30 -KPX a w -20 -KPX a v -20 - -KPX b y -20 -KPX b v -20 -KPX b u -20 -KPX b period -40 -KPX b l -20 -KPX b comma -40 -KPX b b -10 - -KPX c k -20 -KPX c comma -15 - -KPX colon space -50 - -KPX comma quoteright -100 -KPX comma quotedblright -100 - -KPX e y -20 -KPX e x -30 -KPX e w -20 -KPX e v -30 -KPX e period -15 -KPX e comma -15 - -KPX f quoteright 50 -KPX f quotedblright 60 -KPX f period -30 -KPX f o -30 -KPX f e -30 -KPX f dotlessi -28 -KPX f comma -30 -KPX f a -30 - -KPX g r -10 - -KPX h y -30 - -KPX k o -20 -KPX k e -20 - -KPX m y -15 -KPX m u -10 - -KPX n y -15 -KPX n v -20 -KPX n u -10 - -KPX o y -30 -KPX o x -30 -KPX o w -15 -KPX o v -15 -KPX o period -40 -KPX o comma -40 - -KPX oslash z -55 -KPX oslash y -70 -KPX oslash x -85 -KPX oslash w -70 -KPX oslash v -70 -KPX oslash u -55 -KPX oslash t -55 -KPX oslash s -55 -KPX oslash r -55 -KPX oslash q -55 -KPX oslash period -95 -KPX oslash p -55 -KPX oslash o -55 -KPX oslash n -55 -KPX oslash m -55 -KPX oslash l -55 -KPX oslash k -55 -KPX oslash j -55 -KPX oslash i -55 -KPX oslash h -55 -KPX oslash g -55 -KPX oslash f -55 -KPX oslash e -55 -KPX oslash d -55 -KPX oslash comma -95 -KPX oslash c -55 -KPX oslash b -55 -KPX oslash a -55 - -KPX p y -30 -KPX p period -35 -KPX p comma -35 - -KPX period space -60 -KPX period quoteright -100 -KPX period quotedblright -100 - -KPX quotedblright space -40 - -KPX quoteleft quoteleft -57 - -KPX quoteright space -70 -KPX quoteright s -50 -KPX quoteright r -50 -KPX quoteright quoteright -57 -KPX quoteright d -50 - -KPX r y 30 -KPX r v 30 -KPX r u 15 -KPX r t 40 -KPX r semicolon 30 -KPX r period -50 -KPX r p 30 -KPX r n 25 -KPX r m 25 -KPX r l 15 -KPX r k 15 -KPX r i 15 -KPX r comma -50 -KPX r colon 30 -KPX r a -10 - -KPX s w -30 -KPX s period -15 -KPX s comma -15 - -KPX semicolon space -50 - -KPX space quoteleft -60 -KPX space quotedblleft -30 -KPX space Y -90 -KPX space W -40 -KPX space V -50 -KPX space T -50 - -KPX v period -80 -KPX v o -25 -KPX v e -25 -KPX v comma -80 -KPX v a -25 - -KPX w period -60 -KPX w o -10 -KPX w e -10 -KPX w comma -60 -KPX w a -15 - -KPX x e -30 - -KPX y period -100 -KPX y o -20 -KPX y e -20 -KPX y comma -100 -KPX y a -20 - -KPX z o -15 -KPX z e -15 -EndKernPairs -EndKernData -StartComposites 58 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 171 195 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 171 195 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 171 195 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 171 195 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 167 175 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 171 195 ; -CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 160 0 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 171 195 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 171 195 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 171 195 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 171 195 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 12 195 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 12 195 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 12 195 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 12 195 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 202 195 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 217 195 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 217 195 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 217 195 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 217 195 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 217 195 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 171 195 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 194 195 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 194 195 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 194 195 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 194 195 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 171 195 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 171 195 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 148 195 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 92 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 92 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 92 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 92 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 92 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 84 0 ; -CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 69 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 92 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 92 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 92 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 92 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -22 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -22 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -22 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -22 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 84 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 92 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 92 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 92 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 92 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 92 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 69 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 92 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 92 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 92 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 92 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 69 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 69 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 69 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pncb8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pncb8a.afm deleted file mode 100644 index ba1fed6..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pncb8a.afm +++ /dev/null @@ -1,472 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1988, 1991 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Tue May 28 16:48:12 1991 -Comment UniqueID 35031 -Comment VMusage 30773 37665 -FontName NewCenturySchlbk-Bold -FullName New Century Schoolbook Bold -FamilyName New Century Schoolbook -Weight Bold -ItalicAngle 0 -IsFixedPitch false -FontBBox -165 -250 1000 988 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.009 -Notice Copyright (c) 1985, 1987, 1988, 1991 Adobe Systems Incorporated. All Rights Reserved. -EncodingScheme AdobeStandardEncoding -CapHeight 722 -XHeight 475 -Ascender 737 -Descender -205 -StartCharMetrics 228 -C 32 ; WX 287 ; N space ; B 0 0 0 0 ; -C 33 ; WX 296 ; N exclam ; B 53 -15 243 737 ; -C 34 ; WX 333 ; N quotedbl ; B 0 378 333 737 ; -C 35 ; WX 574 ; N numbersign ; B 36 0 538 690 ; -C 36 ; WX 574 ; N dollar ; B 25 -141 549 810 ; -C 37 ; WX 833 ; N percent ; B 14 -15 819 705 ; -C 38 ; WX 852 ; N ampersand ; B 34 -15 818 737 ; -C 39 ; WX 241 ; N quoteright ; B 22 378 220 737 ; -C 40 ; WX 389 ; N parenleft ; B 77 -117 345 745 ; -C 41 ; WX 389 ; N parenright ; B 44 -117 312 745 ; -C 42 ; WX 500 ; N asterisk ; B 54 302 446 737 ; -C 43 ; WX 606 ; N plus ; B 50 0 556 506 ; -C 44 ; WX 278 ; N comma ; B 40 -184 238 175 ; -C 45 ; WX 333 ; N hyphen ; B 42 174 291 302 ; -C 46 ; WX 278 ; N period ; B 44 -15 234 175 ; -C 47 ; WX 278 ; N slash ; B -42 -15 320 737 ; -C 48 ; WX 574 ; N zero ; B 27 -15 547 705 ; -C 49 ; WX 574 ; N one ; B 83 0 491 705 ; -C 50 ; WX 574 ; N two ; B 19 0 531 705 ; -C 51 ; WX 574 ; N three ; B 23 -15 531 705 ; -C 52 ; WX 574 ; N four ; B 19 0 547 705 ; -C 53 ; WX 574 ; N five ; B 32 -15 534 705 ; -C 54 ; WX 574 ; N six ; B 27 -15 547 705 ; -C 55 ; WX 574 ; N seven ; B 45 -15 547 705 ; -C 56 ; WX 574 ; N eight ; B 27 -15 548 705 ; -C 57 ; WX 574 ; N nine ; B 27 -15 547 705 ; -C 58 ; WX 278 ; N colon ; B 44 -15 234 485 ; -C 59 ; WX 278 ; N semicolon ; B 40 -184 238 485 ; -C 60 ; WX 606 ; N less ; B 50 -9 556 515 ; -C 61 ; WX 606 ; N equal ; B 50 103 556 403 ; -C 62 ; WX 606 ; N greater ; B 50 -9 556 515 ; -C 63 ; WX 500 ; N question ; B 23 -15 477 737 ; -C 64 ; WX 747 ; N at ; B -2 -15 750 737 ; -C 65 ; WX 759 ; N A ; B -19 0 778 737 ; -C 66 ; WX 778 ; N B ; B 19 0 739 722 ; -C 67 ; WX 778 ; N C ; B 39 -15 723 737 ; -C 68 ; WX 833 ; N D ; B 19 0 794 722 ; -C 69 ; WX 759 ; N E ; B 19 0 708 722 ; -C 70 ; WX 722 ; N F ; B 19 0 697 722 ; -C 71 ; WX 833 ; N G ; B 39 -15 818 737 ; -C 72 ; WX 870 ; N H ; B 19 0 851 722 ; -C 73 ; WX 444 ; N I ; B 29 0 415 722 ; -C 74 ; WX 648 ; N J ; B 6 -15 642 722 ; -C 75 ; WX 815 ; N K ; B 19 0 822 722 ; -C 76 ; WX 722 ; N L ; B 19 0 703 722 ; -C 77 ; WX 981 ; N M ; B 10 0 971 722 ; -C 78 ; WX 833 ; N N ; B 5 -10 828 722 ; -C 79 ; WX 833 ; N O ; B 39 -15 794 737 ; -C 80 ; WX 759 ; N P ; B 24 0 735 722 ; -C 81 ; WX 833 ; N Q ; B 39 -189 808 737 ; -C 82 ; WX 815 ; N R ; B 19 -15 815 722 ; -C 83 ; WX 667 ; N S ; B 51 -15 634 737 ; -C 84 ; WX 722 ; N T ; B 16 0 706 722 ; -C 85 ; WX 833 ; N U ; B 14 -15 825 722 ; -C 86 ; WX 759 ; N V ; B -19 -10 778 722 ; -C 87 ; WX 981 ; N W ; B 7 -10 974 722 ; -C 88 ; WX 722 ; N X ; B -12 0 734 722 ; -C 89 ; WX 722 ; N Y ; B -12 0 734 722 ; -C 90 ; WX 667 ; N Z ; B 28 0 639 722 ; -C 91 ; WX 389 ; N bracketleft ; B 84 -109 339 737 ; -C 92 ; WX 606 ; N backslash ; B 122 -15 484 737 ; -C 93 ; WX 389 ; N bracketright ; B 50 -109 305 737 ; -C 94 ; WX 606 ; N asciicircum ; B 66 325 540 690 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 241 ; N quoteleft ; B 22 378 220 737 ; -C 97 ; WX 611 ; N a ; B 40 -15 601 485 ; -C 98 ; WX 648 ; N b ; B 4 -15 616 737 ; -C 99 ; WX 556 ; N c ; B 32 -15 524 485 ; -C 100 ; WX 667 ; N d ; B 32 -15 644 737 ; -C 101 ; WX 574 ; N e ; B 32 -15 542 485 ; -C 102 ; WX 389 ; N f ; B 11 0 461 737 ; L i fi ; L l fl ; -C 103 ; WX 611 ; N g ; B 30 -205 623 535 ; -C 104 ; WX 685 ; N h ; B 17 0 662 737 ; -C 105 ; WX 370 ; N i ; B 26 0 338 737 ; -C 106 ; WX 352 ; N j ; B -86 -205 271 737 ; -C 107 ; WX 667 ; N k ; B 17 0 662 737 ; -C 108 ; WX 352 ; N l ; B 17 0 329 737 ; -C 109 ; WX 963 ; N m ; B 17 0 940 485 ; -C 110 ; WX 685 ; N n ; B 17 0 662 485 ; -C 111 ; WX 611 ; N o ; B 32 -15 579 485 ; -C 112 ; WX 667 ; N p ; B 17 -205 629 485 ; -C 113 ; WX 648 ; N q ; B 32 -205 638 485 ; -C 114 ; WX 519 ; N r ; B 17 0 516 485 ; -C 115 ; WX 500 ; N s ; B 48 -15 476 485 ; -C 116 ; WX 426 ; N t ; B 21 -15 405 675 ; -C 117 ; WX 685 ; N u ; B 17 -15 668 475 ; -C 118 ; WX 611 ; N v ; B 12 -10 599 475 ; -C 119 ; WX 889 ; N w ; B 16 -10 873 475 ; -C 120 ; WX 611 ; N x ; B 12 0 599 475 ; -C 121 ; WX 611 ; N y ; B 12 -205 599 475 ; -C 122 ; WX 537 ; N z ; B 38 0 499 475 ; -C 123 ; WX 389 ; N braceleft ; B 36 -109 313 737 ; -C 124 ; WX 606 ; N bar ; B 249 -250 357 750 ; -C 125 ; WX 389 ; N braceright ; B 76 -109 353 737 ; -C 126 ; WX 606 ; N asciitilde ; B 72 160 534 346 ; -C 161 ; WX 296 ; N exclamdown ; B 53 -205 243 547 ; -C 162 ; WX 574 ; N cent ; B 32 -102 528 572 ; -C 163 ; WX 574 ; N sterling ; B 16 -15 558 705 ; -C 164 ; WX 167 ; N fraction ; B -165 -15 332 705 ; -C 165 ; WX 574 ; N yen ; B -10 0 584 690 ; -C 166 ; WX 574 ; N florin ; B 14 -205 548 737 ; -C 167 ; WX 500 ; N section ; B 62 -86 438 737 ; -C 168 ; WX 574 ; N currency ; B 27 84 547 605 ; -C 169 ; WX 241 ; N quotesingle ; B 53 378 189 737 ; -C 170 ; WX 481 ; N quotedblleft ; B 22 378 459 737 ; -C 171 ; WX 500 ; N guillemotleft ; B 46 79 454 397 ; -C 172 ; WX 333 ; N guilsinglleft ; B 62 79 271 397 ; -C 173 ; WX 333 ; N guilsinglright ; B 62 79 271 397 ; -C 174 ; WX 685 ; N fi ; B 11 0 666 737 ; -C 175 ; WX 685 ; N fl ; B 11 0 666 737 ; -C 177 ; WX 500 ; N endash ; B 0 184 500 292 ; -C 178 ; WX 500 ; N dagger ; B 39 -101 461 737 ; -C 179 ; WX 500 ; N daggerdbl ; B 39 -89 461 737 ; -C 180 ; WX 278 ; N periodcentered ; B 53 200 225 372 ; -C 182 ; WX 747 ; N paragraph ; B 96 -71 631 722 ; -C 183 ; WX 606 ; N bullet ; B 122 180 484 542 ; -C 184 ; WX 241 ; N quotesinglbase ; B 22 -184 220 175 ; -C 185 ; WX 481 ; N quotedblbase ; B 22 -184 459 175 ; -C 186 ; WX 481 ; N quotedblright ; B 22 378 459 737 ; -C 187 ; WX 500 ; N guillemotright ; B 46 79 454 397 ; -C 188 ; WX 1000 ; N ellipsis ; B 72 -15 928 175 ; -C 189 ; WX 1000 ; N perthousand ; B 7 -15 993 705 ; -C 191 ; WX 500 ; N questiondown ; B 23 -205 477 547 ; -C 193 ; WX 333 ; N grave ; B 2 547 249 737 ; -C 194 ; WX 333 ; N acute ; B 84 547 331 737 ; -C 195 ; WX 333 ; N circumflex ; B -10 547 344 725 ; -C 196 ; WX 333 ; N tilde ; B -24 563 357 705 ; -C 197 ; WX 333 ; N macron ; B -6 582 339 664 ; -C 198 ; WX 333 ; N breve ; B 9 547 324 714 ; -C 199 ; WX 333 ; N dotaccent ; B 95 552 237 694 ; -C 200 ; WX 333 ; N dieresis ; B -12 552 345 694 ; -C 202 ; WX 333 ; N ring ; B 58 545 274 761 ; -C 203 ; WX 333 ; N cedilla ; B 17 -224 248 0 ; -C 205 ; WX 333 ; N hungarumlaut ; B -16 547 431 737 ; -C 206 ; WX 333 ; N ogonek ; B 168 -163 346 3 ; -C 207 ; WX 333 ; N caron ; B -10 547 344 725 ; -C 208 ; WX 1000 ; N emdash ; B 0 184 1000 292 ; -C 225 ; WX 981 ; N AE ; B -29 0 963 722 ; -C 227 ; WX 367 ; N ordfeminine ; B 1 407 393 705 ; -C 232 ; WX 722 ; N Lslash ; B 19 0 703 722 ; -C 233 ; WX 833 ; N Oslash ; B 39 -53 794 775 ; -C 234 ; WX 1000 ; N OE ; B 0 0 982 722 ; -C 235 ; WX 367 ; N ordmasculine ; B 1 407 366 705 ; -C 241 ; WX 870 ; N ae ; B 32 -15 838 485 ; -C 245 ; WX 370 ; N dotlessi ; B 26 0 338 475 ; -C 248 ; WX 352 ; N lslash ; B 17 0 329 737 ; -C 249 ; WX 611 ; N oslash ; B 32 -103 579 573 ; -C 250 ; WX 907 ; N oe ; B 32 -15 875 485 ; -C 251 ; WX 611 ; N germandbls ; B -2 -15 580 737 ; -C -1 ; WX 574 ; N ecircumflex ; B 32 -15 542 725 ; -C -1 ; WX 574 ; N edieresis ; B 32 -15 542 694 ; -C -1 ; WX 611 ; N aacute ; B 40 -15 601 737 ; -C -1 ; WX 747 ; N registered ; B -2 -15 750 737 ; -C -1 ; WX 370 ; N icircumflex ; B 9 0 363 725 ; -C -1 ; WX 685 ; N udieresis ; B 17 -15 668 694 ; -C -1 ; WX 611 ; N ograve ; B 32 -15 579 737 ; -C -1 ; WX 685 ; N uacute ; B 17 -15 668 737 ; -C -1 ; WX 685 ; N ucircumflex ; B 17 -15 668 725 ; -C -1 ; WX 759 ; N Aacute ; B -19 0 778 964 ; -C -1 ; WX 370 ; N igrave ; B 21 0 338 737 ; -C -1 ; WX 444 ; N Icircumflex ; B 29 0 415 952 ; -C -1 ; WX 556 ; N ccedilla ; B 32 -224 524 485 ; -C -1 ; WX 611 ; N adieresis ; B 40 -15 601 694 ; -C -1 ; WX 759 ; N Ecircumflex ; B 19 0 708 952 ; -C -1 ; WX 500 ; N scaron ; B 48 -15 476 725 ; -C -1 ; WX 667 ; N thorn ; B 17 -205 629 737 ; -C -1 ; WX 1000 ; N trademark ; B 6 317 982 722 ; -C -1 ; WX 574 ; N egrave ; B 32 -15 542 737 ; -C -1 ; WX 344 ; N threesuperior ; B -3 273 355 705 ; -C -1 ; WX 537 ; N zcaron ; B 38 0 499 725 ; -C -1 ; WX 611 ; N atilde ; B 40 -15 601 705 ; -C -1 ; WX 611 ; N aring ; B 40 -15 601 761 ; -C -1 ; WX 611 ; N ocircumflex ; B 32 -15 579 725 ; -C -1 ; WX 759 ; N Edieresis ; B 19 0 708 921 ; -C -1 ; WX 861 ; N threequarters ; B 15 -15 838 705 ; -C -1 ; WX 611 ; N ydieresis ; B 12 -205 599 694 ; -C -1 ; WX 611 ; N yacute ; B 12 -205 599 737 ; -C -1 ; WX 370 ; N iacute ; B 26 0 350 737 ; -C -1 ; WX 759 ; N Acircumflex ; B -19 0 778 952 ; -C -1 ; WX 833 ; N Uacute ; B 14 -15 825 964 ; -C -1 ; WX 574 ; N eacute ; B 32 -15 542 737 ; -C -1 ; WX 833 ; N Ograve ; B 39 -15 794 964 ; -C -1 ; WX 611 ; N agrave ; B 40 -15 601 737 ; -C -1 ; WX 833 ; N Udieresis ; B 14 -15 825 921 ; -C -1 ; WX 611 ; N acircumflex ; B 40 -15 601 725 ; -C -1 ; WX 444 ; N Igrave ; B 29 0 415 964 ; -C -1 ; WX 344 ; N twosuperior ; B -3 282 350 705 ; -C -1 ; WX 833 ; N Ugrave ; B 14 -15 825 964 ; -C -1 ; WX 861 ; N onequarter ; B 31 -15 838 705 ; -C -1 ; WX 833 ; N Ucircumflex ; B 14 -15 825 952 ; -C -1 ; WX 667 ; N Scaron ; B 51 -15 634 952 ; -C -1 ; WX 444 ; N Idieresis ; B 29 0 415 921 ; -C -1 ; WX 370 ; N idieresis ; B 7 0 364 694 ; -C -1 ; WX 759 ; N Egrave ; B 19 0 708 964 ; -C -1 ; WX 833 ; N Oacute ; B 39 -15 794 964 ; -C -1 ; WX 606 ; N divide ; B 50 -40 556 546 ; -C -1 ; WX 759 ; N Atilde ; B -19 0 778 932 ; -C -1 ; WX 759 ; N Aring ; B -19 0 778 988 ; -C -1 ; WX 833 ; N Odieresis ; B 39 -15 794 921 ; -C -1 ; WX 759 ; N Adieresis ; B -19 0 778 921 ; -C -1 ; WX 833 ; N Ntilde ; B 5 -10 828 932 ; -C -1 ; WX 667 ; N Zcaron ; B 28 0 639 952 ; -C -1 ; WX 759 ; N Thorn ; B 24 0 735 722 ; -C -1 ; WX 444 ; N Iacute ; B 29 0 415 964 ; -C -1 ; WX 606 ; N plusminus ; B 50 0 556 506 ; -C -1 ; WX 606 ; N multiply ; B 65 15 541 491 ; -C -1 ; WX 759 ; N Eacute ; B 19 0 708 964 ; -C -1 ; WX 722 ; N Ydieresis ; B -12 0 734 921 ; -C -1 ; WX 344 ; N onesuperior ; B 31 282 309 705 ; -C -1 ; WX 685 ; N ugrave ; B 17 -15 668 737 ; -C -1 ; WX 606 ; N logicalnot ; B 50 103 556 403 ; -C -1 ; WX 685 ; N ntilde ; B 17 0 662 705 ; -C -1 ; WX 833 ; N Otilde ; B 39 -15 794 932 ; -C -1 ; WX 611 ; N otilde ; B 32 -15 579 705 ; -C -1 ; WX 778 ; N Ccedilla ; B 39 -224 723 737 ; -C -1 ; WX 759 ; N Agrave ; B -19 0 778 964 ; -C -1 ; WX 861 ; N onehalf ; B 31 -15 838 705 ; -C -1 ; WX 833 ; N Eth ; B 19 0 794 722 ; -C -1 ; WX 400 ; N degree ; B 57 419 343 705 ; -C -1 ; WX 722 ; N Yacute ; B -12 0 734 964 ; -C -1 ; WX 833 ; N Ocircumflex ; B 39 -15 794 952 ; -C -1 ; WX 611 ; N oacute ; B 32 -15 579 737 ; -C -1 ; WX 685 ; N mu ; B 17 -205 668 475 ; -C -1 ; WX 606 ; N minus ; B 50 199 556 307 ; -C -1 ; WX 611 ; N eth ; B 32 -15 579 737 ; -C -1 ; WX 611 ; N odieresis ; B 32 -15 579 694 ; -C -1 ; WX 747 ; N copyright ; B -2 -15 750 737 ; -C -1 ; WX 606 ; N brokenbar ; B 249 -175 357 675 ; -EndCharMetrics -StartKernData -StartKernPairs 128 - -KPX A y -18 -KPX A w -18 -KPX A v -18 -KPX A quoteright -74 -KPX A quotedblright -74 -KPX A Y -91 -KPX A W -74 -KPX A V -74 -KPX A U -18 -KPX A T -55 - -KPX C period -18 -KPX C comma -18 - -KPX D period -25 -KPX D comma -25 - -KPX F r -18 -KPX F period -125 -KPX F o -55 -KPX F i -18 -KPX F e -55 -KPX F comma -125 -KPX F a -74 - -KPX J u -18 -KPX J period -55 -KPX J o -18 -KPX J e -18 -KPX J comma -55 -KPX J a -18 -KPX J A -18 - -KPX K y -25 -KPX K u -18 - -KPX L y -25 -KPX L quoteright -100 -KPX L quotedblright -100 -KPX L Y -74 -KPX L W -74 -KPX L V -100 -KPX L T -100 - -KPX N period -18 -KPX N comma -18 - -KPX O period -25 -KPX O comma -25 -KPX O T 10 - -KPX P period -150 -KPX P o -55 -KPX P e -55 -KPX P comma -150 -KPX P a -55 -KPX P A -74 - -KPX S period -18 -KPX S comma -18 - -KPX T u -18 -KPX T r -18 -KPX T period -100 -KPX T o -74 -KPX T i -18 -KPX T hyphen -125 -KPX T e -74 -KPX T comma -100 -KPX T a -74 -KPX T O 10 -KPX T A -55 - -KPX U period -25 -KPX U comma -25 -KPX U A -18 - -KPX V u -55 -KPX V semicolon -37 -KPX V period -125 -KPX V o -74 -KPX V i -18 -KPX V hyphen -100 -KPX V e -74 -KPX V comma -125 -KPX V colon -37 -KPX V a -74 -KPX V A -74 - -KPX W y -25 -KPX W u -37 -KPX W semicolon -55 -KPX W period -100 -KPX W o -74 -KPX W i -18 -KPX W hyphen -100 -KPX W e -74 -KPX W comma -100 -KPX W colon -55 -KPX W a -74 -KPX W A -74 - -KPX Y u -55 -KPX Y semicolon -25 -KPX Y period -100 -KPX Y o -100 -KPX Y i -18 -KPX Y hyphen -125 -KPX Y e -100 -KPX Y comma -100 -KPX Y colon -25 -KPX Y a -100 -KPX Y A -91 - -KPX colon space -18 - -KPX comma space -18 -KPX comma quoteright -18 -KPX comma quotedblright -18 - -KPX f quoteright 75 -KPX f quotedblright 75 - -KPX period space -18 -KPX period quoteright -18 -KPX period quotedblright -18 - -KPX quotedblleft A -74 - -KPX quotedblright space -18 - -KPX quoteleft A -74 - -KPX quoteright s -25 -KPX quoteright d -25 - -KPX r period -74 -KPX r comma -74 - -KPX semicolon space -18 - -KPX space quoteleft -18 -KPX space quotedblleft -18 -KPX space Y -18 -KPX space W -18 -KPX space V -18 -KPX space T -18 -KPX space A -18 - -KPX v period -100 -KPX v comma -100 - -KPX w period -100 -KPX w comma -100 - -KPX y period -100 -KPX y comma -100 -EndKernPairs -EndKernData -StartComposites 56 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 213 227 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 213 227 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 213 227 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 213 227 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 213 227 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 213 227 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 213 227 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 213 227 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 213 227 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 213 227 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 56 227 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 56 227 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 56 227 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 56 227 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 250 227 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 250 227 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 250 227 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 250 227 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 250 227 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 250 227 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 167 227 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 250 227 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 250 227 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 250 227 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 250 227 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 195 227 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 195 227 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 167 227 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 139 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 139 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 139 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 139 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 139 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 139 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 121 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 121 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 121 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 121 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute 19 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex 19 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis 19 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave 19 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 176 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 139 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 139 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 139 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 139 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 139 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 84 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 176 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 176 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 176 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 176 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 139 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 139 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 102 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pncbi8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pncbi8a.afm deleted file mode 100644 index 7871147..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pncbi8a.afm +++ /dev/null @@ -1,602 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1991 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Tue May 28 16:56:07 1991 -Comment UniqueID 35034 -Comment VMusage 31030 37922 -FontName NewCenturySchlbk-BoldItalic -FullName New Century Schoolbook Bold Italic -FamilyName New Century Schoolbook -Weight Bold -ItalicAngle -16 -IsFixedPitch false -FontBBox -205 -250 1147 991 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.007 -Notice Copyright (c) 1985, 1987, 1989, 1991 Adobe Systems Incorporated. All Rights Reserved. -EncodingScheme AdobeStandardEncoding -CapHeight 722 -XHeight 477 -Ascender 737 -Descender -205 -StartCharMetrics 228 -C 32 ; WX 287 ; N space ; B 0 0 0 0 ; -C 33 ; WX 333 ; N exclam ; B 0 -15 333 737 ; -C 34 ; WX 400 ; N quotedbl ; B 66 388 428 737 ; -C 35 ; WX 574 ; N numbersign ; B 30 0 544 690 ; -C 36 ; WX 574 ; N dollar ; B 9 -120 565 810 ; -C 37 ; WX 889 ; N percent ; B 54 -28 835 727 ; -C 38 ; WX 889 ; N ampersand ; B 32 -15 823 737 ; -C 39 ; WX 259 ; N quoteright ; B 48 388 275 737 ; -C 40 ; WX 407 ; N parenleft ; B 72 -117 454 745 ; -C 41 ; WX 407 ; N parenright ; B -70 -117 310 745 ; -C 42 ; WX 500 ; N asterisk ; B 58 301 498 737 ; -C 43 ; WX 606 ; N plus ; B 50 0 556 506 ; -C 44 ; WX 287 ; N comma ; B -57 -192 170 157 ; -C 45 ; WX 333 ; N hyphen ; B 2 177 263 299 ; -C 46 ; WX 287 ; N period ; B -20 -15 152 157 ; -C 47 ; WX 278 ; N slash ; B -41 -15 320 737 ; -C 48 ; WX 574 ; N zero ; B 21 -15 553 705 ; -C 49 ; WX 574 ; N one ; B 25 0 489 705 ; -C 50 ; WX 574 ; N two ; B -38 -3 538 705 ; -C 51 ; WX 574 ; N three ; B -7 -15 536 705 ; -C 52 ; WX 574 ; N four ; B -13 0 544 705 ; -C 53 ; WX 574 ; N five ; B 0 -15 574 705 ; -C 54 ; WX 574 ; N six ; B 31 -15 574 705 ; -C 55 ; WX 574 ; N seven ; B 64 -15 593 705 ; -C 56 ; WX 574 ; N eight ; B 0 -15 552 705 ; -C 57 ; WX 574 ; N nine ; B 0 -15 543 705 ; -C 58 ; WX 287 ; N colon ; B -20 -15 237 477 ; -C 59 ; WX 287 ; N semicolon ; B -57 -192 237 477 ; -C 60 ; WX 606 ; N less ; B 50 -9 556 515 ; -C 61 ; WX 606 ; N equal ; B 50 103 556 403 ; -C 62 ; WX 606 ; N greater ; B 50 -8 556 514 ; -C 63 ; WX 481 ; N question ; B 79 -15 451 737 ; -C 64 ; WX 747 ; N at ; B -4 -15 751 737 ; -C 65 ; WX 741 ; N A ; B -75 0 716 737 ; -C 66 ; WX 759 ; N B ; B -50 0 721 722 ; -C 67 ; WX 759 ; N C ; B 37 -15 759 737 ; -C 68 ; WX 833 ; N D ; B -47 0 796 722 ; -C 69 ; WX 741 ; N E ; B -41 0 730 722 ; -C 70 ; WX 704 ; N F ; B -41 0 730 722 ; -C 71 ; WX 815 ; N G ; B 37 -15 805 737 ; -C 72 ; WX 870 ; N H ; B -41 0 911 722 ; -C 73 ; WX 444 ; N I ; B -41 0 485 722 ; -C 74 ; WX 667 ; N J ; B -20 -15 708 722 ; -C 75 ; WX 778 ; N K ; B -41 0 832 722 ; -C 76 ; WX 704 ; N L ; B -41 0 670 722 ; -C 77 ; WX 944 ; N M ; B -44 0 988 722 ; -C 78 ; WX 852 ; N N ; B -61 -10 913 722 ; -C 79 ; WX 833 ; N O ; B 37 -15 796 737 ; -C 80 ; WX 741 ; N P ; B -41 0 730 722 ; -C 81 ; WX 833 ; N Q ; B 37 -189 796 737 ; -C 82 ; WX 796 ; N R ; B -41 -15 749 722 ; -C 83 ; WX 685 ; N S ; B 1 -15 666 737 ; -C 84 ; WX 722 ; N T ; B 41 0 759 722 ; -C 85 ; WX 833 ; N U ; B 88 -15 900 722 ; -C 86 ; WX 741 ; N V ; B 32 -10 802 722 ; -C 87 ; WX 944 ; N W ; B 40 -10 1000 722 ; -C 88 ; WX 741 ; N X ; B -82 0 801 722 ; -C 89 ; WX 704 ; N Y ; B 13 0 775 722 ; -C 90 ; WX 704 ; N Z ; B -33 0 711 722 ; -C 91 ; WX 407 ; N bracketleft ; B 1 -109 464 737 ; -C 92 ; WX 606 ; N backslash ; B 161 -15 445 737 ; -C 93 ; WX 407 ; N bracketright ; B -101 -109 362 737 ; -C 94 ; WX 606 ; N asciicircum ; B 66 325 540 690 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 259 ; N quoteleft ; B 47 388 274 737 ; -C 97 ; WX 667 ; N a ; B 6 -15 636 477 ; -C 98 ; WX 611 ; N b ; B 29 -15 557 737 ; -C 99 ; WX 537 ; N c ; B 0 -15 482 477 ; -C 100 ; WX 667 ; N d ; B 0 -15 660 737 ; -C 101 ; WX 519 ; N e ; B 0 -15 479 477 ; -C 102 ; WX 389 ; N f ; B -48 -205 550 737 ; L i fi ; L l fl ; -C 103 ; WX 611 ; N g ; B -63 -205 604 528 ; -C 104 ; WX 685 ; N h ; B 0 -15 639 737 ; -C 105 ; WX 389 ; N i ; B 32 -15 345 737 ; -C 106 ; WX 370 ; N j ; B -205 -205 347 737 ; -C 107 ; WX 648 ; N k ; B -11 -15 578 737 ; -C 108 ; WX 389 ; N l ; B 32 -15 375 737 ; -C 109 ; WX 944 ; N m ; B 0 -15 909 477 ; -C 110 ; WX 685 ; N n ; B 0 -15 639 477 ; -C 111 ; WX 574 ; N o ; B 0 -15 530 477 ; -C 112 ; WX 648 ; N p ; B -119 -205 590 477 ; -C 113 ; WX 630 ; N q ; B 0 -205 587 477 ; -C 114 ; WX 519 ; N r ; B 0 0 527 486 ; -C 115 ; WX 481 ; N s ; B 0 -15 435 477 ; -C 116 ; WX 407 ; N t ; B 24 -15 403 650 ; -C 117 ; WX 685 ; N u ; B 30 -15 635 477 ; -C 118 ; WX 556 ; N v ; B 30 -15 496 477 ; -C 119 ; WX 833 ; N w ; B 30 -15 773 477 ; -C 120 ; WX 574 ; N x ; B -46 -15 574 477 ; -C 121 ; WX 519 ; N y ; B -66 -205 493 477 ; -C 122 ; WX 519 ; N z ; B -19 -15 473 477 ; -C 123 ; WX 407 ; N braceleft ; B 52 -109 408 737 ; -C 124 ; WX 606 ; N bar ; B 249 -250 357 750 ; -C 125 ; WX 407 ; N braceright ; B -25 -109 331 737 ; -C 126 ; WX 606 ; N asciitilde ; B 72 160 534 346 ; -C 161 ; WX 333 ; N exclamdown ; B -44 -205 289 547 ; -C 162 ; WX 574 ; N cent ; B 30 -144 512 578 ; -C 163 ; WX 574 ; N sterling ; B -18 -15 566 705 ; -C 164 ; WX 167 ; N fraction ; B -166 -15 333 705 ; -C 165 ; WX 574 ; N yen ; B 17 0 629 690 ; -C 166 ; WX 574 ; N florin ; B -43 -205 575 737 ; -C 167 ; WX 500 ; N section ; B -30 -146 515 737 ; -C 168 ; WX 574 ; N currency ; B 27 84 547 605 ; -C 169 ; WX 287 ; N quotesingle ; B 112 388 250 737 ; -C 170 ; WX 481 ; N quotedblleft ; B 54 388 521 737 ; -C 171 ; WX 481 ; N guillemotleft ; B -35 69 449 407 ; -C 172 ; WX 278 ; N guilsinglleft ; B -25 69 244 407 ; -C 173 ; WX 278 ; N guilsinglright ; B -26 69 243 407 ; -C 174 ; WX 685 ; N fi ; B -70 -205 641 737 ; -C 175 ; WX 685 ; N fl ; B -70 -205 671 737 ; -C 177 ; WX 500 ; N endash ; B -47 189 479 287 ; -C 178 ; WX 500 ; N dagger ; B 48 -146 508 737 ; -C 179 ; WX 500 ; N daggerdbl ; B -60 -150 508 737 ; -C 180 ; WX 287 ; N periodcentered ; B 57 200 229 372 ; -C 182 ; WX 650 ; N paragraph ; B 25 -131 681 722 ; -C 183 ; WX 606 ; N bullet ; B 122 180 484 542 ; -C 184 ; WX 259 ; N quotesinglbase ; B -57 -192 170 157 ; -C 185 ; WX 481 ; N quotedblbase ; B -57 -192 412 157 ; -C 186 ; WX 481 ; N quotedblright ; B 43 388 510 737 ; -C 187 ; WX 481 ; N guillemotright ; B -31 69 453 407 ; -C 188 ; WX 1000 ; N ellipsis ; B 81 -15 919 157 ; -C 189 ; WX 1167 ; N perthousand ; B 20 -28 1147 727 ; -C 191 ; WX 481 ; N questiondown ; B 0 -205 372 547 ; -C 193 ; WX 333 ; N grave ; B 74 538 294 722 ; -C 194 ; WX 333 ; N acute ; B 123 538 372 722 ; -C 195 ; WX 333 ; N circumflex ; B 23 533 365 705 ; -C 196 ; WX 333 ; N tilde ; B 28 561 398 690 ; -C 197 ; WX 333 ; N macron ; B 47 573 404 649 ; -C 198 ; WX 333 ; N breve ; B 67 535 390 698 ; -C 199 ; WX 333 ; N dotaccent ; B 145 546 289 690 ; -C 200 ; WX 333 ; N dieresis ; B 33 546 393 690 ; -C 202 ; WX 333 ; N ring ; B 111 522 335 746 ; -C 203 ; WX 333 ; N cedilla ; B -21 -220 225 3 ; -C 205 ; WX 333 ; N hungarumlaut ; B 15 538 480 722 ; -C 206 ; WX 333 ; N ogonek ; B 68 -155 246 -10 ; -C 207 ; WX 333 ; N caron ; B 60 531 403 705 ; -C 208 ; WX 1000 ; N emdash ; B -47 189 979 287 ; -C 225 ; WX 889 ; N AE ; B -86 0 915 722 ; -C 227 ; WX 412 ; N ordfeminine ; B 47 407 460 705 ; -C 232 ; WX 704 ; N Lslash ; B -41 0 670 722 ; -C 233 ; WX 833 ; N Oslash ; B 35 -68 798 790 ; -C 234 ; WX 963 ; N OE ; B 29 0 989 722 ; -C 235 ; WX 356 ; N ordmasculine ; B 42 407 394 705 ; -C 241 ; WX 815 ; N ae ; B -18 -15 775 477 ; -C 245 ; WX 389 ; N dotlessi ; B 32 -15 345 477 ; -C 248 ; WX 389 ; N lslash ; B 5 -15 390 737 ; -C 249 ; WX 574 ; N oslash ; B 0 -121 530 583 ; -C 250 ; WX 852 ; N oe ; B -6 -15 812 477 ; -C 251 ; WX 574 ; N germandbls ; B -91 -205 540 737 ; -C -1 ; WX 519 ; N ecircumflex ; B 0 -15 479 705 ; -C -1 ; WX 519 ; N edieresis ; B 0 -15 486 690 ; -C -1 ; WX 667 ; N aacute ; B 6 -15 636 722 ; -C -1 ; WX 747 ; N registered ; B -2 -15 750 737 ; -C -1 ; WX 389 ; N icircumflex ; B 21 -15 363 698 ; -C -1 ; WX 685 ; N udieresis ; B 30 -15 635 690 ; -C -1 ; WX 574 ; N ograve ; B 0 -15 530 722 ; -C -1 ; WX 685 ; N uacute ; B 30 -15 635 722 ; -C -1 ; WX 685 ; N ucircumflex ; B 30 -15 635 705 ; -C -1 ; WX 741 ; N Aacute ; B -75 0 716 947 ; -C -1 ; WX 389 ; N igrave ; B 32 -15 345 715 ; -C -1 ; WX 444 ; N Icircumflex ; B -41 0 485 930 ; -C -1 ; WX 537 ; N ccedilla ; B 0 -220 482 477 ; -C -1 ; WX 667 ; N adieresis ; B 6 -15 636 690 ; -C -1 ; WX 741 ; N Ecircumflex ; B -41 0 730 930 ; -C -1 ; WX 481 ; N scaron ; B 0 -15 477 705 ; -C -1 ; WX 648 ; N thorn ; B -119 -205 590 737 ; -C -1 ; WX 950 ; N trademark ; B 42 317 1017 722 ; -C -1 ; WX 519 ; N egrave ; B 0 -15 479 722 ; -C -1 ; WX 344 ; N threesuperior ; B 3 273 361 705 ; -C -1 ; WX 519 ; N zcaron ; B -19 -15 473 695 ; -C -1 ; WX 667 ; N atilde ; B 6 -15 636 690 ; -C -1 ; WX 667 ; N aring ; B 6 -15 636 746 ; -C -1 ; WX 574 ; N ocircumflex ; B 0 -15 530 705 ; -C -1 ; WX 741 ; N Edieresis ; B -41 0 730 915 ; -C -1 ; WX 861 ; N threequarters ; B 35 -15 789 705 ; -C -1 ; WX 519 ; N ydieresis ; B -66 -205 493 690 ; -C -1 ; WX 519 ; N yacute ; B -66 -205 493 722 ; -C -1 ; WX 389 ; N iacute ; B 32 -15 370 715 ; -C -1 ; WX 741 ; N Acircumflex ; B -75 0 716 930 ; -C -1 ; WX 833 ; N Uacute ; B 88 -15 900 947 ; -C -1 ; WX 519 ; N eacute ; B 0 -15 479 722 ; -C -1 ; WX 833 ; N Ograve ; B 37 -15 796 947 ; -C -1 ; WX 667 ; N agrave ; B 6 -15 636 722 ; -C -1 ; WX 833 ; N Udieresis ; B 88 -15 900 915 ; -C -1 ; WX 667 ; N acircumflex ; B 6 -15 636 705 ; -C -1 ; WX 444 ; N Igrave ; B -41 0 485 947 ; -C -1 ; WX 344 ; N twosuperior ; B -17 280 362 705 ; -C -1 ; WX 833 ; N Ugrave ; B 88 -15 900 947 ; -C -1 ; WX 861 ; N onequarter ; B 17 -15 789 705 ; -C -1 ; WX 833 ; N Ucircumflex ; B 88 -15 900 930 ; -C -1 ; WX 685 ; N Scaron ; B 1 -15 666 930 ; -C -1 ; WX 444 ; N Idieresis ; B -41 0 509 915 ; -C -1 ; WX 389 ; N idieresis ; B 31 -15 391 683 ; -C -1 ; WX 741 ; N Egrave ; B -41 0 730 947 ; -C -1 ; WX 833 ; N Oacute ; B 37 -15 796 947 ; -C -1 ; WX 606 ; N divide ; B 50 -40 556 546 ; -C -1 ; WX 741 ; N Atilde ; B -75 0 716 915 ; -C -1 ; WX 741 ; N Aring ; B -75 0 716 991 ; -C -1 ; WX 833 ; N Odieresis ; B 37 -15 796 915 ; -C -1 ; WX 741 ; N Adieresis ; B -75 0 716 915 ; -C -1 ; WX 852 ; N Ntilde ; B -61 -10 913 915 ; -C -1 ; WX 704 ; N Zcaron ; B -33 0 711 930 ; -C -1 ; WX 741 ; N Thorn ; B -41 0 690 722 ; -C -1 ; WX 444 ; N Iacute ; B -41 0 488 947 ; -C -1 ; WX 606 ; N plusminus ; B 50 0 556 506 ; -C -1 ; WX 606 ; N multiply ; B 65 15 541 491 ; -C -1 ; WX 741 ; N Eacute ; B -41 0 730 947 ; -C -1 ; WX 704 ; N Ydieresis ; B 13 0 775 915 ; -C -1 ; WX 344 ; N onesuperior ; B 19 282 326 705 ; -C -1 ; WX 685 ; N ugrave ; B 30 -15 635 722 ; -C -1 ; WX 606 ; N logicalnot ; B 50 103 556 403 ; -C -1 ; WX 685 ; N ntilde ; B 0 -15 639 690 ; -C -1 ; WX 833 ; N Otilde ; B 37 -15 796 915 ; -C -1 ; WX 574 ; N otilde ; B 0 -15 530 690 ; -C -1 ; WX 759 ; N Ccedilla ; B 37 -220 759 737 ; -C -1 ; WX 741 ; N Agrave ; B -75 0 716 947 ; -C -1 ; WX 861 ; N onehalf ; B 17 -15 798 705 ; -C -1 ; WX 833 ; N Eth ; B -47 0 796 722 ; -C -1 ; WX 400 ; N degree ; B 86 419 372 705 ; -C -1 ; WX 704 ; N Yacute ; B 13 0 775 947 ; -C -1 ; WX 833 ; N Ocircumflex ; B 37 -15 796 930 ; -C -1 ; WX 574 ; N oacute ; B 0 -15 530 722 ; -C -1 ; WX 685 ; N mu ; B -89 -205 635 477 ; -C -1 ; WX 606 ; N minus ; B 50 199 556 307 ; -C -1 ; WX 574 ; N eth ; B 0 -15 530 752 ; -C -1 ; WX 574 ; N odieresis ; B 0 -15 530 690 ; -C -1 ; WX 747 ; N copyright ; B -2 -15 750 737 ; -C -1 ; WX 606 ; N brokenbar ; B 249 -175 357 675 ; -EndCharMetrics -StartKernData -StartKernPairs 239 - -KPX A y -33 -KPX A w -25 -KPX A v -10 -KPX A u -15 -KPX A quoteright -95 -KPX A quotedblright -95 -KPX A Y -70 -KPX A W -84 -KPX A V -100 -KPX A U -32 -KPX A T 5 -KPX A Q 5 -KPX A O 5 -KPX A G 5 -KPX A C 5 - -KPX B period 15 -KPX B comma 15 -KPX B U 15 -KPX B A -11 - -KPX C A -5 - -KPX D period -11 -KPX D comma -11 -KPX D Y 6 -KPX D W -11 -KPX D V -18 - -KPX F r -27 -KPX F period -91 -KPX F o -47 -KPX F i -41 -KPX F e -41 -KPX F comma -91 -KPX F a -47 -KPX F A -79 - -KPX J u -39 -KPX J period -74 -KPX J o -40 -KPX J e -33 -KPX J comma -74 -KPX J a -40 -KPX J A -30 - -KPX K y -48 -KPX K u -4 -KPX K o -4 -KPX K e 18 - -KPX L y -30 -KPX L quoteright -100 -KPX L quotedblright -100 -KPX L Y -55 -KPX L W -69 -KPX L V -97 -KPX L T -75 - -KPX N period -49 -KPX N comma -49 - -KPX O period -18 -KPX O comma -18 -KPX O X -18 -KPX O W -15 -KPX O V -24 -KPX O A -5 - -KPX P period -100 -KPX P o -40 -KPX P e -33 -KPX P comma -100 -KPX P a -40 -KPX P A -80 - -KPX R W -14 -KPX R V -24 - -KPX S period -18 -KPX S comma -18 - -KPX T y -30 -KPX T w -30 -KPX T u -22 -KPX T r -9 -KPX T period -55 -KPX T o -40 -KPX T i -22 -KPX T hyphen -75 -KPX T h -9 -KPX T e -33 -KPX T comma -55 -KPX T a -40 -KPX T O 11 -KPX T A -60 - -KPX U period -25 -KPX U comma -25 -KPX U A -42 - -KPX V u -70 -KPX V semicolon 6 -KPX V period -94 -KPX V o -71 -KPX V i -35 -KPX V hyphen -94 -KPX V e -66 -KPX V comma -94 -KPX V colon -49 -KPX V a -55 -KPX V O -19 -KPX V G -12 -KPX V A -100 - -KPX W y -41 -KPX W u -25 -KPX W semicolon -22 -KPX W period -86 -KPX W o -33 -KPX W i -27 -KPX W hyphen -61 -KPX W h 5 -KPX W e -39 -KPX W comma -86 -KPX W colon -22 -KPX W a -33 -KPX W O -11 -KPX W A -66 - -KPX Y u -58 -KPX Y semicolon -55 -KPX Y period -91 -KPX Y o -77 -KPX Y i -22 -KPX Y hyphen -91 -KPX Y e -71 -KPX Y comma -91 -KPX Y colon -55 -KPX Y a -77 -KPX Y A -79 - -KPX a y -8 -KPX a w -8 -KPX a v 6 - -KPX b y -6 -KPX b v 8 -KPX b period 6 -KPX b comma 6 - -KPX c y -20 -KPX c period -8 -KPX c l -13 -KPX c k -8 -KPX c h -18 -KPX c comma -8 - -KPX colon space -18 - -KPX comma space -18 -KPX comma quoteright -18 -KPX comma quotedblright -18 - -KPX d y -15 -KPX d w -15 - -KPX e y -15 -KPX e x -5 -KPX e w -15 -KPX e p -11 -KPX e g -4 -KPX e b -8 - -KPX f quoteright 105 -KPX f quotedblright 105 -KPX f period -28 -KPX f o 7 -KPX f l 7 -KPX f i 7 -KPX f e 14 -KPX f dotlessi 7 -KPX f comma -28 -KPX f a 8 - -KPX g y -11 -KPX g r 11 -KPX g period -5 -KPX g comma -5 - -KPX h y -20 - -KPX i v 7 - -KPX k y -15 -KPX k o -22 -KPX k e -16 - -KPX l y -7 -KPX l w -7 - -KPX m y -20 -KPX m u -11 - -KPX n y -20 -KPX n v -7 -KPX n u -11 - -KPX o y -11 -KPX o w -8 -KPX o v 6 - -KPX p y -4 -KPX p period 8 -KPX p comma 8 - -KPX period space -18 -KPX period quoteright -18 -KPX period quotedblright -18 - -KPX quotedblleft quoteleft 20 -KPX quotedblleft A -60 - -KPX quotedblright space -18 - -KPX quoteleft A -80 - -KPX quoteright v -16 -KPX quoteright t -22 -KPX quoteright s -46 -KPX quoteright r -9 -KPX quoteright l -22 -KPX quoteright d -41 - -KPX r y -20 -KPX r v -7 -KPX r u -11 -KPX r t -11 -KPX r semicolon 9 -KPX r s -20 -KPX r quoteright 9 -KPX r period -90 -KPX r p -17 -KPX r o -11 -KPX r l -14 -KPX r k 9 -KPX r i -14 -KPX r hyphen -16 -KPX r g -11 -KPX r e -7 -KPX r d -7 -KPX r comma -90 -KPX r colon 9 -KPX r a -11 - -KPX s period 11 -KPX s comma 11 - -KPX semicolon space -18 - -KPX space quotedblleft -18 -KPX space Y -18 -KPX space W -33 -KPX space V -24 -KPX space T -18 -KPX space A -22 - -KPX v period -11 -KPX v o -6 -KPX v comma -11 -KPX v a -6 - -KPX w period -17 -KPX w o -14 -KPX w e -8 -KPX w comma -17 -KPX w a -14 - -KPX x e 5 - -KPX y period -25 -KPX y o 8 -KPX y e 15 -KPX y comma -25 -KPX y a 8 - -KPX z e 4 -EndKernPairs -EndKernData -StartComposites 56 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 259 225 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 259 225 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 259 225 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 259 225 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 229 245 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 259 225 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 296 225 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 296 225 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 296 225 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 296 225 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 116 225 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 116 225 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 116 225 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 116 225 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 326 225 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 315 225 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 315 225 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 315 225 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 315 225 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 315 225 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 206 225 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 340 225 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 340 225 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 340 225 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 340 225 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 246 225 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 236 225 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 226 225 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 167 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 167 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 167 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 167 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 167 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 167 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 93 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 93 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 93 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 93 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -2 -7 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -2 -7 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -2 -7 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -2 -7 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 176 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 121 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 121 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 121 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 121 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 121 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 74 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 176 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 176 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 176 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 176 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 93 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 93 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 63 -10 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pncr8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pncr8a.afm deleted file mode 100644 index b9f616c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pncr8a.afm +++ /dev/null @@ -1,524 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1991 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Tue May 28 16:31:51 1991 -Comment UniqueID 35025 -Comment VMusage 30420 37312 -FontName NewCenturySchlbk-Roman -FullName New Century Schoolbook Roman -FamilyName New Century Schoolbook -Weight Roman -ItalicAngle 0 -IsFixedPitch false -FontBBox -195 -250 1000 965 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.007 -Notice Copyright (c) 1985, 1987, 1989, 1991 Adobe Systems Incorporated. All Rights Reserved. -EncodingScheme AdobeStandardEncoding -CapHeight 722 -XHeight 464 -Ascender 737 -Descender -205 -StartCharMetrics 228 -C 32 ; WX 278 ; N space ; B 0 0 0 0 ; -C 33 ; WX 296 ; N exclam ; B 86 -15 210 737 ; -C 34 ; WX 389 ; N quotedbl ; B 61 443 328 737 ; -C 35 ; WX 556 ; N numbersign ; B 28 0 528 690 ; -C 36 ; WX 556 ; N dollar ; B 45 -138 511 813 ; -C 37 ; WX 833 ; N percent ; B 43 -15 790 705 ; -C 38 ; WX 815 ; N ampersand ; B 51 -15 775 737 ; -C 39 ; WX 204 ; N quoteright ; B 25 443 179 737 ; -C 40 ; WX 333 ; N parenleft ; B 40 -117 279 745 ; -C 41 ; WX 333 ; N parenright ; B 54 -117 293 745 ; -C 42 ; WX 500 ; N asterisk ; B 57 306 443 737 ; -C 43 ; WX 606 ; N plus ; B 50 0 556 506 ; -C 44 ; WX 278 ; N comma ; B 62 -185 216 109 ; -C 45 ; WX 333 ; N hyphen ; B 42 199 291 277 ; -C 46 ; WX 278 ; N period ; B 77 -15 201 109 ; -C 47 ; WX 278 ; N slash ; B -32 -15 310 737 ; -C 48 ; WX 556 ; N zero ; B 42 -15 514 705 ; -C 49 ; WX 556 ; N one ; B 100 0 496 705 ; -C 50 ; WX 556 ; N two ; B 35 0 505 705 ; -C 51 ; WX 556 ; N three ; B 42 -15 498 705 ; -C 52 ; WX 556 ; N four ; B 28 0 528 705 ; -C 53 ; WX 556 ; N five ; B 46 -15 502 705 ; -C 54 ; WX 556 ; N six ; B 41 -15 515 705 ; -C 55 ; WX 556 ; N seven ; B 59 -15 508 705 ; -C 56 ; WX 556 ; N eight ; B 42 -15 514 705 ; -C 57 ; WX 556 ; N nine ; B 41 -15 515 705 ; -C 58 ; WX 278 ; N colon ; B 77 -15 201 474 ; -C 59 ; WX 278 ; N semicolon ; B 62 -185 216 474 ; -C 60 ; WX 606 ; N less ; B 50 -8 556 514 ; -C 61 ; WX 606 ; N equal ; B 50 117 556 389 ; -C 62 ; WX 606 ; N greater ; B 50 -8 556 514 ; -C 63 ; WX 444 ; N question ; B 29 -15 415 737 ; -C 64 ; WX 737 ; N at ; B -8 -15 744 737 ; -C 65 ; WX 722 ; N A ; B -8 0 730 737 ; -C 66 ; WX 722 ; N B ; B 29 0 669 722 ; -C 67 ; WX 722 ; N C ; B 45 -15 668 737 ; -C 68 ; WX 778 ; N D ; B 29 0 733 722 ; -C 69 ; WX 722 ; N E ; B 29 0 663 722 ; -C 70 ; WX 667 ; N F ; B 29 0 638 722 ; -C 71 ; WX 778 ; N G ; B 45 -15 775 737 ; -C 72 ; WX 833 ; N H ; B 29 0 804 722 ; -C 73 ; WX 407 ; N I ; B 38 0 369 722 ; -C 74 ; WX 556 ; N J ; B 5 -15 540 722 ; -C 75 ; WX 778 ; N K ; B 29 0 803 722 ; -C 76 ; WX 667 ; N L ; B 29 0 644 722 ; -C 77 ; WX 944 ; N M ; B 29 0 915 722 ; -C 78 ; WX 815 ; N N ; B 24 -15 791 722 ; -C 79 ; WX 778 ; N O ; B 45 -15 733 737 ; -C 80 ; WX 667 ; N P ; B 29 0 650 722 ; -C 81 ; WX 778 ; N Q ; B 45 -190 748 737 ; -C 82 ; WX 722 ; N R ; B 29 -15 713 722 ; -C 83 ; WX 630 ; N S ; B 47 -15 583 737 ; -C 84 ; WX 667 ; N T ; B 19 0 648 722 ; -C 85 ; WX 815 ; N U ; B 16 -15 799 722 ; -C 86 ; WX 722 ; N V ; B -8 -10 730 722 ; -C 87 ; WX 981 ; N W ; B 5 -10 976 722 ; -C 88 ; WX 704 ; N X ; B -8 0 712 722 ; -C 89 ; WX 704 ; N Y ; B -11 0 715 722 ; -C 90 ; WX 611 ; N Z ; B 24 0 576 722 ; -C 91 ; WX 333 ; N bracketleft ; B 126 -109 315 737 ; -C 92 ; WX 606 ; N backslash ; B 132 -15 474 737 ; -C 93 ; WX 333 ; N bracketright ; B 18 -109 207 737 ; -C 94 ; WX 606 ; N asciicircum ; B 89 325 517 690 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 204 ; N quoteleft ; B 25 443 179 737 ; -C 97 ; WX 556 ; N a ; B 44 -15 542 479 ; -C 98 ; WX 556 ; N b ; B 10 -15 522 737 ; -C 99 ; WX 444 ; N c ; B 34 -15 426 479 ; -C 100 ; WX 574 ; N d ; B 34 -15 552 737 ; -C 101 ; WX 500 ; N e ; B 34 -15 466 479 ; -C 102 ; WX 333 ; N f ; B 18 0 437 737 ; L i fi ; L l fl ; -C 103 ; WX 537 ; N g ; B 23 -205 542 494 ; -C 104 ; WX 611 ; N h ; B 7 0 592 737 ; -C 105 ; WX 315 ; N i ; B 18 0 286 722 ; -C 106 ; WX 296 ; N j ; B -86 -205 216 722 ; -C 107 ; WX 593 ; N k ; B 10 0 589 737 ; -C 108 ; WX 315 ; N l ; B 18 0 286 737 ; -C 109 ; WX 889 ; N m ; B 26 0 863 479 ; -C 110 ; WX 611 ; N n ; B 22 0 589 479 ; -C 111 ; WX 500 ; N o ; B 34 -15 466 479 ; -C 112 ; WX 574 ; N p ; B 22 -205 540 479 ; -C 113 ; WX 556 ; N q ; B 34 -205 552 479 ; -C 114 ; WX 444 ; N r ; B 18 0 434 479 ; -C 115 ; WX 463 ; N s ; B 46 -15 417 479 ; -C 116 ; WX 389 ; N t ; B 18 -15 371 666 ; -C 117 ; WX 611 ; N u ; B 22 -15 589 464 ; -C 118 ; WX 537 ; N v ; B -6 -10 515 464 ; -C 119 ; WX 778 ; N w ; B 1 -10 749 464 ; -C 120 ; WX 537 ; N x ; B 8 0 529 464 ; -C 121 ; WX 537 ; N y ; B 4 -205 533 464 ; -C 122 ; WX 481 ; N z ; B 42 0 439 464 ; -C 123 ; WX 333 ; N braceleft ; B 54 -109 279 737 ; -C 124 ; WX 606 ; N bar ; B 267 -250 339 750 ; -C 125 ; WX 333 ; N braceright ; B 54 -109 279 737 ; -C 126 ; WX 606 ; N asciitilde ; B 72 184 534 322 ; -C 161 ; WX 296 ; N exclamdown ; B 86 -205 210 547 ; -C 162 ; WX 556 ; N cent ; B 74 -141 482 584 ; -C 163 ; WX 556 ; N sterling ; B 18 -15 538 705 ; -C 164 ; WX 167 ; N fraction ; B -195 -15 362 705 ; -C 165 ; WX 556 ; N yen ; B -1 0 557 690 ; -C 166 ; WX 556 ; N florin ; B 0 -205 538 737 ; -C 167 ; WX 500 ; N section ; B 55 -147 445 737 ; -C 168 ; WX 556 ; N currency ; B 26 93 530 597 ; -C 169 ; WX 204 ; N quotesingle ; B 59 443 145 737 ; -C 170 ; WX 389 ; N quotedblleft ; B 25 443 364 737 ; -C 171 ; WX 426 ; N guillemotleft ; B 39 78 387 398 ; -C 172 ; WX 259 ; N guilsinglleft ; B 39 78 220 398 ; -C 173 ; WX 259 ; N guilsinglright ; B 39 78 220 398 ; -C 174 ; WX 611 ; N fi ; B 18 0 582 737 ; -C 175 ; WX 611 ; N fl ; B 18 0 582 737 ; -C 177 ; WX 556 ; N endash ; B 0 208 556 268 ; -C 178 ; WX 500 ; N dagger ; B 42 -147 458 737 ; -C 179 ; WX 500 ; N daggerdbl ; B 42 -149 458 737 ; -C 180 ; WX 278 ; N periodcentered ; B 71 238 207 374 ; -C 182 ; WX 606 ; N paragraph ; B 60 -132 546 722 ; -C 183 ; WX 606 ; N bullet ; B 122 180 484 542 ; -C 184 ; WX 204 ; N quotesinglbase ; B 25 -185 179 109 ; -C 185 ; WX 389 ; N quotedblbase ; B 25 -185 364 109 ; -C 186 ; WX 389 ; N quotedblright ; B 25 443 364 737 ; -C 187 ; WX 426 ; N guillemotright ; B 39 78 387 398 ; -C 188 ; WX 1000 ; N ellipsis ; B 105 -15 895 109 ; -C 189 ; WX 1000 ; N perthousand ; B 6 -15 994 705 ; -C 191 ; WX 444 ; N questiondown ; B 29 -205 415 547 ; -C 193 ; WX 333 ; N grave ; B 17 528 242 699 ; -C 194 ; WX 333 ; N acute ; B 91 528 316 699 ; -C 195 ; WX 333 ; N circumflex ; B 10 528 323 695 ; -C 196 ; WX 333 ; N tilde ; B 1 553 332 655 ; -C 197 ; WX 333 ; N macron ; B 10 568 323 623 ; -C 198 ; WX 333 ; N breve ; B 25 528 308 685 ; -C 199 ; WX 333 ; N dotaccent ; B 116 543 218 645 ; -C 200 ; WX 333 ; N dieresis ; B 16 543 317 645 ; -C 202 ; WX 333 ; N ring ; B 66 522 266 722 ; -C 203 ; WX 333 ; N cedilla ; B 29 -215 237 0 ; -C 205 ; WX 333 ; N hungarumlaut ; B -9 528 416 699 ; -C 206 ; WX 333 ; N ogonek ; B 68 -215 254 0 ; -C 207 ; WX 333 ; N caron ; B 10 528 323 695 ; -C 208 ; WX 1000 ; N emdash ; B 0 208 1000 268 ; -C 225 ; WX 1000 ; N AE ; B 0 0 962 722 ; -C 227 ; WX 334 ; N ordfeminine ; B -4 407 338 705 ; -C 232 ; WX 667 ; N Lslash ; B 29 0 644 722 ; -C 233 ; WX 778 ; N Oslash ; B 45 -56 733 778 ; -C 234 ; WX 1000 ; N OE ; B 21 0 979 722 ; -C 235 ; WX 300 ; N ordmasculine ; B 4 407 296 705 ; -C 241 ; WX 796 ; N ae ; B 34 -15 762 479 ; -C 245 ; WX 315 ; N dotlessi ; B 18 0 286 464 ; -C 248 ; WX 315 ; N lslash ; B 18 0 286 737 ; -C 249 ; WX 500 ; N oslash ; B 34 -97 466 561 ; -C 250 ; WX 833 ; N oe ; B 34 -15 799 479 ; -C 251 ; WX 574 ; N germandbls ; B 30 -15 537 737 ; -C -1 ; WX 500 ; N ecircumflex ; B 34 -15 466 695 ; -C -1 ; WX 500 ; N edieresis ; B 34 -15 466 645 ; -C -1 ; WX 556 ; N aacute ; B 44 -15 542 699 ; -C -1 ; WX 737 ; N registered ; B -8 -15 744 737 ; -C -1 ; WX 315 ; N icircumflex ; B 1 0 314 695 ; -C -1 ; WX 611 ; N udieresis ; B 22 -15 589 645 ; -C -1 ; WX 500 ; N ograve ; B 34 -15 466 699 ; -C -1 ; WX 611 ; N uacute ; B 22 -15 589 699 ; -C -1 ; WX 611 ; N ucircumflex ; B 22 -15 589 695 ; -C -1 ; WX 722 ; N Aacute ; B -8 0 730 937 ; -C -1 ; WX 315 ; N igrave ; B 8 0 286 699 ; -C -1 ; WX 407 ; N Icircumflex ; B 38 0 369 933 ; -C -1 ; WX 444 ; N ccedilla ; B 34 -215 426 479 ; -C -1 ; WX 556 ; N adieresis ; B 44 -15 542 645 ; -C -1 ; WX 722 ; N Ecircumflex ; B 29 0 663 933 ; -C -1 ; WX 463 ; N scaron ; B 46 -15 417 695 ; -C -1 ; WX 574 ; N thorn ; B 22 -205 540 737 ; -C -1 ; WX 1000 ; N trademark ; B 32 318 968 722 ; -C -1 ; WX 500 ; N egrave ; B 34 -15 466 699 ; -C -1 ; WX 333 ; N threesuperior ; B 18 273 315 705 ; -C -1 ; WX 481 ; N zcaron ; B 42 0 439 695 ; -C -1 ; WX 556 ; N atilde ; B 44 -15 542 655 ; -C -1 ; WX 556 ; N aring ; B 44 -15 542 732 ; -C -1 ; WX 500 ; N ocircumflex ; B 34 -15 466 695 ; -C -1 ; WX 722 ; N Edieresis ; B 29 0 663 883 ; -C -1 ; WX 834 ; N threequarters ; B 28 -15 795 705 ; -C -1 ; WX 537 ; N ydieresis ; B 4 -205 533 645 ; -C -1 ; WX 537 ; N yacute ; B 4 -205 533 699 ; -C -1 ; WX 315 ; N iacute ; B 18 0 307 699 ; -C -1 ; WX 722 ; N Acircumflex ; B -8 0 730 933 ; -C -1 ; WX 815 ; N Uacute ; B 16 -15 799 937 ; -C -1 ; WX 500 ; N eacute ; B 34 -15 466 699 ; -C -1 ; WX 778 ; N Ograve ; B 45 -15 733 937 ; -C -1 ; WX 556 ; N agrave ; B 44 -15 542 699 ; -C -1 ; WX 815 ; N Udieresis ; B 16 -15 799 883 ; -C -1 ; WX 556 ; N acircumflex ; B 44 -15 542 695 ; -C -1 ; WX 407 ; N Igrave ; B 38 0 369 937 ; -C -1 ; WX 333 ; N twosuperior ; B 14 282 319 705 ; -C -1 ; WX 815 ; N Ugrave ; B 16 -15 799 937 ; -C -1 ; WX 834 ; N onequarter ; B 39 -15 795 705 ; -C -1 ; WX 815 ; N Ucircumflex ; B 16 -15 799 933 ; -C -1 ; WX 630 ; N Scaron ; B 47 -15 583 933 ; -C -1 ; WX 407 ; N Idieresis ; B 38 0 369 883 ; -C -1 ; WX 315 ; N idieresis ; B 7 0 308 645 ; -C -1 ; WX 722 ; N Egrave ; B 29 0 663 937 ; -C -1 ; WX 778 ; N Oacute ; B 45 -15 733 937 ; -C -1 ; WX 606 ; N divide ; B 50 -22 556 528 ; -C -1 ; WX 722 ; N Atilde ; B -8 0 730 893 ; -C -1 ; WX 722 ; N Aring ; B -8 0 730 965 ; -C -1 ; WX 778 ; N Odieresis ; B 45 -15 733 883 ; -C -1 ; WX 722 ; N Adieresis ; B -8 0 730 883 ; -C -1 ; WX 815 ; N Ntilde ; B 24 -15 791 893 ; -C -1 ; WX 611 ; N Zcaron ; B 24 0 576 933 ; -C -1 ; WX 667 ; N Thorn ; B 29 0 650 722 ; -C -1 ; WX 407 ; N Iacute ; B 38 0 369 937 ; -C -1 ; WX 606 ; N plusminus ; B 50 0 556 506 ; -C -1 ; WX 606 ; N multiply ; B 74 24 532 482 ; -C -1 ; WX 722 ; N Eacute ; B 29 0 663 937 ; -C -1 ; WX 704 ; N Ydieresis ; B -11 0 715 883 ; -C -1 ; WX 333 ; N onesuperior ; B 39 282 294 705 ; -C -1 ; WX 611 ; N ugrave ; B 22 -15 589 699 ; -C -1 ; WX 606 ; N logicalnot ; B 50 108 556 389 ; -C -1 ; WX 611 ; N ntilde ; B 22 0 589 655 ; -C -1 ; WX 778 ; N Otilde ; B 45 -15 733 893 ; -C -1 ; WX 500 ; N otilde ; B 34 -15 466 655 ; -C -1 ; WX 722 ; N Ccedilla ; B 45 -215 668 737 ; -C -1 ; WX 722 ; N Agrave ; B -8 0 730 937 ; -C -1 ; WX 834 ; N onehalf ; B 39 -15 820 705 ; -C -1 ; WX 778 ; N Eth ; B 29 0 733 722 ; -C -1 ; WX 400 ; N degree ; B 57 419 343 705 ; -C -1 ; WX 704 ; N Yacute ; B -11 0 715 937 ; -C -1 ; WX 778 ; N Ocircumflex ; B 45 -15 733 933 ; -C -1 ; WX 500 ; N oacute ; B 34 -15 466 699 ; -C -1 ; WX 611 ; N mu ; B 22 -205 589 464 ; -C -1 ; WX 606 ; N minus ; B 50 217 556 289 ; -C -1 ; WX 500 ; N eth ; B 34 -15 466 752 ; -C -1 ; WX 500 ; N odieresis ; B 34 -15 466 645 ; -C -1 ; WX 737 ; N copyright ; B -8 -15 744 737 ; -C -1 ; WX 606 ; N brokenbar ; B 267 -175 339 675 ; -EndCharMetrics -StartKernData -StartKernPairs 169 - -KPX A y -37 -KPX A w -25 -KPX A v -37 -KPX A quoteright -74 -KPX A quotedblright -74 -KPX A Y -75 -KPX A W -50 -KPX A V -75 -KPX A U -30 -KPX A T -18 - -KPX B period -37 -KPX B comma -37 -KPX B A -18 - -KPX C period -37 -KPX C comma -37 -KPX C A -18 - -KPX D period -37 -KPX D comma -37 -KPX D Y -18 -KPX D V -18 - -KPX F r -10 -KPX F period -125 -KPX F o -55 -KPX F i -10 -KPX F e -55 -KPX F comma -125 -KPX F a -65 -KPX F A -50 - -KPX G period -37 -KPX G comma -37 - -KPX J u -25 -KPX J period -74 -KPX J o -25 -KPX J e -25 -KPX J comma -74 -KPX J a -25 -KPX J A -18 - -KPX K y -25 -KPX K o 10 -KPX K e 10 - -KPX L y -25 -KPX L quoteright -100 -KPX L quotedblright -100 -KPX L Y -74 -KPX L W -74 -KPX L V -91 -KPX L T -75 - -KPX N period -55 -KPX N comma -55 - -KPX O period -37 -KPX O comma -37 -KPX O Y -18 -KPX O V -18 -KPX O T 10 - -KPX P period -125 -KPX P o -37 -KPX P e -37 -KPX P comma -125 -KPX P a -37 -KPX P A -55 - -KPX Q period -25 -KPX Q comma -25 - -KPX S period -37 -KPX S comma -37 - -KPX T semicolon -37 -KPX T period -125 -KPX T o -55 -KPX T hyphen -100 -KPX T e -55 -KPX T comma -125 -KPX T colon -37 -KPX T a -55 -KPX T O 10 -KPX T A -18 - -KPX U period -100 -KPX U comma -100 -KPX U A -30 - -KPX V u -75 -KPX V semicolon -75 -KPX V period -125 -KPX V o -75 -KPX V i -18 -KPX V hyphen -100 -KPX V e -75 -KPX V comma -125 -KPX V colon -75 -KPX V a -85 -KPX V O -18 -KPX V A -74 - -KPX W y -55 -KPX W u -55 -KPX W semicolon -100 -KPX W period -125 -KPX W o -60 -KPX W i -18 -KPX W hyphen -100 -KPX W e -60 -KPX W comma -125 -KPX W colon -100 -KPX W a -75 -KPX W A -50 - -KPX Y u -91 -KPX Y semicolon -75 -KPX Y period -100 -KPX Y o -100 -KPX Y i -18 -KPX Y hyphen -125 -KPX Y e -100 -KPX Y comma -100 -KPX Y colon -75 -KPX Y a -100 -KPX Y O -18 -KPX Y A -75 - -KPX a y -10 -KPX a w -10 -KPX a v -10 - -KPX b period -18 -KPX b comma -18 - -KPX c period -18 -KPX c l -7 -KPX c k -7 -KPX c h -7 -KPX c comma -18 - -KPX colon space -37 - -KPX comma space -37 -KPX comma quoteright -37 -KPX comma quotedblright -37 - -KPX e period -18 -KPX e comma -18 - -KPX f quoteright 100 -KPX f quotedblright 100 -KPX f period -37 -KPX f comma -37 - -KPX g period -25 -KPX g comma -25 - -KPX o period -18 -KPX o comma -18 - -KPX p period -18 -KPX p comma -18 - -KPX period space -37 -KPX period quoteright -37 -KPX period quotedblright -37 - -KPX quotedblleft A -74 - -KPX quotedblright space -37 - -KPX quoteleft quoteleft -25 -KPX quoteleft A -74 - -KPX quoteright s -25 -KPX quoteright quoteright -25 -KPX quoteright d -37 - -KPX r period -100 -KPX r hyphen -37 -KPX r comma -100 - -KPX s period -25 -KPX s comma -25 - -KPX semicolon space -37 - -KPX space quoteleft -37 -KPX space quotedblleft -37 -KPX space Y -37 -KPX space W -37 -KPX space V -37 -KPX space T -37 -KPX space A -37 - -KPX v period -125 -KPX v comma -125 - -KPX w period -125 -KPX w comma -125 -KPX w a -18 - -KPX y period -125 -KPX y comma -125 -EndKernPairs -EndKernData -StartComposites 56 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 195 238 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 195 238 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 195 238 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 195 238 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 195 243 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 195 238 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 195 238 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 195 238 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 195 238 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 195 238 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 37 238 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 37 238 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 37 238 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 37 238 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 241 238 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 223 238 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 223 238 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 223 238 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 223 238 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 223 238 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 149 238 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 241 238 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 241 238 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 241 238 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 241 238 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 216 238 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 186 238 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 139 238 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 112 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 112 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 112 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 112 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 112 10 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 112 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 84 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 84 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 84 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 84 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -9 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -9 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -9 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -9 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 139 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 84 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 84 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 84 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 84 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 84 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 65 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 139 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 139 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 139 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 139 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 102 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 102 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 74 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pncri8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pncri8a.afm deleted file mode 100644 index 6dfd6a2..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pncri8a.afm +++ /dev/null @@ -1,536 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1991 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Tue May 28 16:40:04 1991 -Comment UniqueID 35028 -Comment VMusage 31423 38315 -FontName NewCenturySchlbk-Italic -FullName New Century Schoolbook Italic -FamilyName New Century Schoolbook -Weight Medium -ItalicAngle -16 -IsFixedPitch false -FontBBox -166 -250 994 958 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.006 -Notice Copyright (c) 1985, 1987, 1989, 1991 Adobe Systems Incorporated. All Rights Reserved. -EncodingScheme AdobeStandardEncoding -CapHeight 722 -XHeight 466 -Ascender 737 -Descender -205 -StartCharMetrics 228 -C 32 ; WX 278 ; N space ; B 0 0 0 0 ; -C 33 ; WX 333 ; N exclam ; B 17 -15 303 737 ; -C 34 ; WX 400 ; N quotedbl ; B 127 463 363 737 ; -C 35 ; WX 556 ; N numbersign ; B 28 0 528 690 ; -C 36 ; WX 556 ; N dollar ; B 4 -142 536 808 ; -C 37 ; WX 833 ; N percent ; B 43 -15 790 705 ; -C 38 ; WX 852 ; N ampersand ; B 24 -15 773 737 ; -C 39 ; WX 204 ; N quoteright ; B 39 463 229 737 ; -C 40 ; WX 333 ; N parenleft ; B 53 -117 411 745 ; -C 41 ; WX 333 ; N parenright ; B -93 -117 265 745 ; -C 42 ; WX 500 ; N asterisk ; B 80 318 500 737 ; -C 43 ; WX 606 ; N plus ; B 50 0 556 506 ; -C 44 ; WX 278 ; N comma ; B -39 -165 151 109 ; -C 45 ; WX 333 ; N hyphen ; B 32 202 259 274 ; -C 46 ; WX 278 ; N period ; B 17 -15 141 109 ; -C 47 ; WX 606 ; N slash ; B 132 -15 474 737 ; -C 48 ; WX 556 ; N zero ; B 30 -15 526 705 ; -C 49 ; WX 556 ; N one ; B 50 0 459 705 ; -C 50 ; WX 556 ; N two ; B -37 0 506 705 ; -C 51 ; WX 556 ; N three ; B -2 -15 506 705 ; -C 52 ; WX 556 ; N four ; B -8 0 512 705 ; -C 53 ; WX 556 ; N five ; B 4 -15 540 705 ; -C 54 ; WX 556 ; N six ; B 36 -15 548 705 ; -C 55 ; WX 556 ; N seven ; B 69 -15 561 705 ; -C 56 ; WX 556 ; N eight ; B 6 -15 526 705 ; -C 57 ; WX 556 ; N nine ; B 8 -15 520 705 ; -C 58 ; WX 278 ; N colon ; B 17 -15 229 466 ; -C 59 ; WX 278 ; N semicolon ; B -39 -165 229 466 ; -C 60 ; WX 606 ; N less ; B 36 -8 542 514 ; -C 61 ; WX 606 ; N equal ; B 50 117 556 389 ; -C 62 ; WX 606 ; N greater ; B 64 -8 570 514 ; -C 63 ; WX 444 ; N question ; B 102 -15 417 737 ; -C 64 ; WX 747 ; N at ; B -2 -15 750 737 ; -C 65 ; WX 704 ; N A ; B -87 0 668 737 ; -C 66 ; WX 722 ; N B ; B -33 0 670 722 ; -C 67 ; WX 722 ; N C ; B 40 -15 712 737 ; -C 68 ; WX 778 ; N D ; B -33 0 738 722 ; -C 69 ; WX 722 ; N E ; B -33 0 700 722 ; -C 70 ; WX 667 ; N F ; B -33 0 700 722 ; -C 71 ; WX 778 ; N G ; B 40 -15 763 737 ; -C 72 ; WX 833 ; N H ; B -33 0 866 722 ; -C 73 ; WX 407 ; N I ; B -33 0 435 722 ; -C 74 ; WX 611 ; N J ; B -14 -15 651 722 ; -C 75 ; WX 741 ; N K ; B -33 0 816 722 ; -C 76 ; WX 667 ; N L ; B -33 0 627 722 ; -C 77 ; WX 944 ; N M ; B -33 0 977 722 ; -C 78 ; WX 815 ; N N ; B -51 -15 866 722 ; -C 79 ; WX 778 ; N O ; B 40 -15 738 737 ; -C 80 ; WX 667 ; N P ; B -33 0 667 722 ; -C 81 ; WX 778 ; N Q ; B 40 -190 738 737 ; -C 82 ; WX 741 ; N R ; B -45 -15 692 722 ; -C 83 ; WX 667 ; N S ; B -6 -15 638 737 ; -C 84 ; WX 685 ; N T ; B 40 0 725 722 ; -C 85 ; WX 815 ; N U ; B 93 -15 867 722 ; -C 86 ; WX 704 ; N V ; B 36 -10 779 722 ; -C 87 ; WX 926 ; N W ; B 53 -10 978 722 ; -C 88 ; WX 704 ; N X ; B -75 0 779 722 ; -C 89 ; WX 685 ; N Y ; B 31 0 760 722 ; -C 90 ; WX 667 ; N Z ; B -25 0 667 722 ; -C 91 ; WX 333 ; N bracketleft ; B -55 -109 388 737 ; -C 92 ; WX 606 ; N backslash ; B 132 -15 474 737 ; -C 93 ; WX 333 ; N bracketright ; B -77 -109 366 737 ; -C 94 ; WX 606 ; N asciicircum ; B 89 325 517 690 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 204 ; N quoteleft ; B 39 463 229 737 ; -C 97 ; WX 574 ; N a ; B 2 -15 524 466 ; -C 98 ; WX 556 ; N b ; B 32 -15 488 737 ; -C 99 ; WX 444 ; N c ; B 2 -15 394 466 ; -C 100 ; WX 611 ; N d ; B 2 -15 585 737 ; -C 101 ; WX 444 ; N e ; B -6 -15 388 466 ; -C 102 ; WX 333 ; N f ; B -68 -205 470 737 ; L i fi ; L l fl ; -C 103 ; WX 537 ; N g ; B -79 -205 523 497 ; -C 104 ; WX 611 ; N h ; B 14 -15 562 737 ; -C 105 ; WX 333 ; N i ; B 29 -15 282 715 ; -C 106 ; WX 315 ; N j ; B -166 -205 318 715 ; -C 107 ; WX 556 ; N k ; B 0 -15 497 737 ; -C 108 ; WX 333 ; N l ; B 14 -15 292 737 ; -C 109 ; WX 889 ; N m ; B 14 -15 840 466 ; -C 110 ; WX 611 ; N n ; B 14 -15 562 466 ; -C 111 ; WX 500 ; N o ; B 2 -15 450 466 ; -C 112 ; WX 574 ; N p ; B -101 -205 506 466 ; -C 113 ; WX 556 ; N q ; B 2 -205 500 466 ; -C 114 ; WX 444 ; N r ; B 10 0 434 466 ; -C 115 ; WX 444 ; N s ; B 2 -15 394 466 ; -C 116 ; WX 352 ; N t ; B 24 -15 328 619 ; -C 117 ; WX 611 ; N u ; B 44 -15 556 466 ; -C 118 ; WX 519 ; N v ; B 31 -15 447 466 ; -C 119 ; WX 778 ; N w ; B 31 -15 706 466 ; -C 120 ; WX 500 ; N x ; B -33 -15 471 466 ; -C 121 ; WX 500 ; N y ; B -83 -205 450 466 ; -C 122 ; WX 463 ; N z ; B -33 -15 416 466 ; -C 123 ; WX 333 ; N braceleft ; B 38 -109 394 737 ; -C 124 ; WX 606 ; N bar ; B 267 -250 339 750 ; -C 125 ; WX 333 ; N braceright ; B -87 -109 269 737 ; -C 126 ; WX 606 ; N asciitilde ; B 72 184 534 322 ; -C 161 ; WX 333 ; N exclamdown ; B -22 -205 264 547 ; -C 162 ; WX 556 ; N cent ; B 62 -144 486 580 ; -C 163 ; WX 556 ; N sterling ; B -13 -15 544 705 ; -C 164 ; WX 167 ; N fraction ; B -134 -15 301 705 ; -C 165 ; WX 556 ; N yen ; B 40 0 624 690 ; -C 166 ; WX 556 ; N florin ; B -58 -205 569 737 ; -C 167 ; WX 500 ; N section ; B -10 -147 480 737 ; -C 168 ; WX 556 ; N currency ; B 26 93 530 597 ; -C 169 ; WX 278 ; N quotesingle ; B 151 463 237 737 ; -C 170 ; WX 389 ; N quotedblleft ; B 39 463 406 737 ; -C 171 ; WX 426 ; N guillemotleft ; B -15 74 402 402 ; -C 172 ; WX 333 ; N guilsinglleft ; B 40 74 259 402 ; -C 173 ; WX 333 ; N guilsinglright ; B 40 74 259 402 ; -C 174 ; WX 611 ; N fi ; B -68 -205 555 737 ; -C 175 ; WX 611 ; N fl ; B -68 -205 587 737 ; -C 177 ; WX 500 ; N endash ; B -27 208 487 268 ; -C 178 ; WX 500 ; N dagger ; B 51 -147 506 737 ; -C 179 ; WX 500 ; N daggerdbl ; B -54 -147 506 737 ; -C 180 ; WX 278 ; N periodcentered ; B 71 238 207 374 ; -C 182 ; WX 650 ; N paragraph ; B 48 -132 665 722 ; -C 183 ; WX 606 ; N bullet ; B 122 180 484 542 ; -C 184 ; WX 204 ; N quotesinglbase ; B -78 -165 112 109 ; -C 185 ; WX 389 ; N quotedblbase ; B -78 -165 289 109 ; -C 186 ; WX 389 ; N quotedblright ; B 39 463 406 737 ; -C 187 ; WX 426 ; N guillemotright ; B -15 74 402 402 ; -C 188 ; WX 1000 ; N ellipsis ; B 59 -15 849 109 ; -C 189 ; WX 1000 ; N perthousand ; B 6 -15 994 705 ; -C 191 ; WX 444 ; N questiondown ; B -3 -205 312 547 ; -C 193 ; WX 333 ; N grave ; B 71 518 262 690 ; -C 194 ; WX 333 ; N acute ; B 132 518 355 690 ; -C 195 ; WX 333 ; N circumflex ; B 37 518 331 690 ; -C 196 ; WX 333 ; N tilde ; B 52 547 383 649 ; -C 197 ; WX 333 ; N macron ; B 52 560 363 610 ; -C 198 ; WX 333 ; N breve ; B 69 518 370 677 ; -C 199 ; WX 333 ; N dotaccent ; B 146 544 248 646 ; -C 200 ; WX 333 ; N dieresis ; B 59 544 359 646 ; -C 202 ; WX 333 ; N ring ; B 114 512 314 712 ; -C 203 ; WX 333 ; N cedilla ; B 3 -215 215 0 ; -C 205 ; WX 333 ; N hungarumlaut ; B 32 518 455 690 ; -C 206 ; WX 333 ; N ogonek ; B 68 -215 254 0 ; -C 207 ; WX 333 ; N caron ; B 73 518 378 690 ; -C 208 ; WX 1000 ; N emdash ; B -27 208 987 268 ; -C 225 ; WX 870 ; N AE ; B -87 0 888 722 ; -C 227 ; WX 422 ; N ordfeminine ; B 72 416 420 705 ; -C 232 ; WX 667 ; N Lslash ; B -33 0 627 722 ; -C 233 ; WX 778 ; N Oslash ; B 16 -68 748 780 ; -C 234 ; WX 981 ; N OE ; B 40 0 975 722 ; -C 235 ; WX 372 ; N ordmasculine ; B 66 416 370 705 ; -C 241 ; WX 722 ; N ae ; B -18 -15 666 466 ; -C 245 ; WX 333 ; N dotlessi ; B 29 -15 282 466 ; -C 248 ; WX 333 ; N lslash ; B -25 -15 340 737 ; -C 249 ; WX 500 ; N oslash ; B 2 -121 450 549 ; -C 250 ; WX 778 ; N oe ; B 2 -15 722 466 ; -C 251 ; WX 556 ; N germandbls ; B -76 -205 525 737 ; -C -1 ; WX 444 ; N ecircumflex ; B -6 -15 388 690 ; -C -1 ; WX 444 ; N edieresis ; B -6 -15 415 646 ; -C -1 ; WX 574 ; N aacute ; B 2 -15 524 690 ; -C -1 ; WX 747 ; N registered ; B -2 -15 750 737 ; -C -1 ; WX 333 ; N icircumflex ; B 29 -15 331 690 ; -C -1 ; WX 611 ; N udieresis ; B 44 -15 556 646 ; -C -1 ; WX 500 ; N ograve ; B 2 -15 450 690 ; -C -1 ; WX 611 ; N uacute ; B 44 -15 556 690 ; -C -1 ; WX 611 ; N ucircumflex ; B 44 -15 556 690 ; -C -1 ; WX 704 ; N Aacute ; B -87 0 668 946 ; -C -1 ; WX 333 ; N igrave ; B 29 -15 282 690 ; -C -1 ; WX 407 ; N Icircumflex ; B -33 0 435 946 ; -C -1 ; WX 444 ; N ccedilla ; B 2 -215 394 466 ; -C -1 ; WX 574 ; N adieresis ; B 2 -15 524 646 ; -C -1 ; WX 722 ; N Ecircumflex ; B -33 0 700 946 ; -C -1 ; WX 444 ; N scaron ; B 2 -15 434 690 ; -C -1 ; WX 574 ; N thorn ; B -101 -205 506 737 ; -C -1 ; WX 950 ; N trademark ; B 32 318 968 722 ; -C -1 ; WX 444 ; N egrave ; B -6 -15 388 690 ; -C -1 ; WX 333 ; N threesuperior ; B 22 273 359 705 ; -C -1 ; WX 463 ; N zcaron ; B -33 -15 443 690 ; -C -1 ; WX 574 ; N atilde ; B 2 -15 524 649 ; -C -1 ; WX 574 ; N aring ; B 2 -15 524 712 ; -C -1 ; WX 500 ; N ocircumflex ; B 2 -15 450 690 ; -C -1 ; WX 722 ; N Edieresis ; B -33 0 700 902 ; -C -1 ; WX 834 ; N threequarters ; B 22 -15 782 705 ; -C -1 ; WX 500 ; N ydieresis ; B -83 -205 450 646 ; -C -1 ; WX 500 ; N yacute ; B -83 -205 450 690 ; -C -1 ; WX 333 ; N iacute ; B 29 -15 355 690 ; -C -1 ; WX 704 ; N Acircumflex ; B -87 0 668 946 ; -C -1 ; WX 815 ; N Uacute ; B 93 -15 867 946 ; -C -1 ; WX 444 ; N eacute ; B -6 -15 411 690 ; -C -1 ; WX 778 ; N Ograve ; B 40 -15 738 946 ; -C -1 ; WX 574 ; N agrave ; B 2 -15 524 690 ; -C -1 ; WX 815 ; N Udieresis ; B 93 -15 867 902 ; -C -1 ; WX 574 ; N acircumflex ; B 2 -15 524 690 ; -C -1 ; WX 407 ; N Igrave ; B -33 0 435 946 ; -C -1 ; WX 333 ; N twosuperior ; B 0 282 359 705 ; -C -1 ; WX 815 ; N Ugrave ; B 93 -15 867 946 ; -C -1 ; WX 834 ; N onequarter ; B 34 -15 782 705 ; -C -1 ; WX 815 ; N Ucircumflex ; B 93 -15 867 946 ; -C -1 ; WX 667 ; N Scaron ; B -6 -15 638 946 ; -C -1 ; WX 407 ; N Idieresis ; B -33 0 456 902 ; -C -1 ; WX 333 ; N idieresis ; B 29 -15 359 646 ; -C -1 ; WX 722 ; N Egrave ; B -33 0 700 946 ; -C -1 ; WX 778 ; N Oacute ; B 40 -15 738 946 ; -C -1 ; WX 606 ; N divide ; B 50 -22 556 528 ; -C -1 ; WX 704 ; N Atilde ; B -87 0 668 905 ; -C -1 ; WX 704 ; N Aring ; B -87 0 668 958 ; -C -1 ; WX 778 ; N Odieresis ; B 40 -15 738 902 ; -C -1 ; WX 704 ; N Adieresis ; B -87 0 668 902 ; -C -1 ; WX 815 ; N Ntilde ; B -51 -15 866 905 ; -C -1 ; WX 667 ; N Zcaron ; B -25 0 667 946 ; -C -1 ; WX 667 ; N Thorn ; B -33 0 627 722 ; -C -1 ; WX 407 ; N Iacute ; B -33 0 452 946 ; -C -1 ; WX 606 ; N plusminus ; B 50 0 556 506 ; -C -1 ; WX 606 ; N multiply ; B 74 24 532 482 ; -C -1 ; WX 722 ; N Eacute ; B -33 0 700 946 ; -C -1 ; WX 685 ; N Ydieresis ; B 31 0 760 902 ; -C -1 ; WX 333 ; N onesuperior ; B 34 282 311 705 ; -C -1 ; WX 611 ; N ugrave ; B 44 -15 556 690 ; -C -1 ; WX 606 ; N logicalnot ; B 50 108 556 389 ; -C -1 ; WX 611 ; N ntilde ; B 14 -15 562 649 ; -C -1 ; WX 778 ; N Otilde ; B 40 -15 738 905 ; -C -1 ; WX 500 ; N otilde ; B 2 -15 467 649 ; -C -1 ; WX 722 ; N Ccedilla ; B 40 -215 712 737 ; -C -1 ; WX 704 ; N Agrave ; B -87 0 668 946 ; -C -1 ; WX 834 ; N onehalf ; B 34 -15 776 705 ; -C -1 ; WX 778 ; N Eth ; B -33 0 738 722 ; -C -1 ; WX 400 ; N degree ; B 86 419 372 705 ; -C -1 ; WX 685 ; N Yacute ; B 31 0 760 946 ; -C -1 ; WX 778 ; N Ocircumflex ; B 40 -15 738 946 ; -C -1 ; WX 500 ; N oacute ; B 2 -15 450 690 ; -C -1 ; WX 611 ; N mu ; B -60 -205 556 466 ; -C -1 ; WX 606 ; N minus ; B 50 217 556 289 ; -C -1 ; WX 500 ; N eth ; B 2 -15 450 737 ; -C -1 ; WX 500 ; N odieresis ; B 2 -15 450 646 ; -C -1 ; WX 747 ; N copyright ; B -2 -15 750 737 ; -C -1 ; WX 606 ; N brokenbar ; B 267 -175 339 675 ; -EndCharMetrics -StartKernData -StartKernPairs 181 - -KPX A y -55 -KPX A w -18 -KPX A v -18 -KPX A u -18 -KPX A quoteright -125 -KPX A quotedblright -125 -KPX A Y -55 -KPX A W -74 -KPX A V -74 -KPX A U -37 -KPX A T -30 -KPX A Q -18 -KPX A O -18 -KPX A G -18 -KPX A C -18 - -KPX B period -50 -KPX B comma -50 - -KPX C period -50 -KPX C comma -50 - -KPX D period -50 -KPX D comma -50 -KPX D Y -18 -KPX D W -18 -KPX D V -18 - -KPX F r -55 -KPX F period -125 -KPX F o -55 -KPX F i -10 -KPX F e -55 -KPX F comma -125 -KPX F a -55 -KPX F A -35 - -KPX G period -50 -KPX G comma -50 - -KPX J u -18 -KPX J period -100 -KPX J o -37 -KPX J e -37 -KPX J comma -100 -KPX J a -37 -KPX J A -18 - -KPX L y -50 -KPX L quoteright -125 -KPX L quotedblright -125 -KPX L Y -100 -KPX L W -100 -KPX L V -100 -KPX L T -100 - -KPX N period -60 -KPX N comma -60 - -KPX O period -50 -KPX O comma -50 -KPX O Y -18 -KPX O X -18 -KPX O V -18 -KPX O T 18 - -KPX P period -125 -KPX P o -55 -KPX P e -55 -KPX P comma -125 -KPX P a -55 -KPX P A -50 - -KPX Q period -20 -KPX Q comma -20 - -KPX R Y -18 -KPX R W -18 -KPX R V -18 -KPX R U -18 - -KPX S period -50 -KPX S comma -50 - -KPX T y -50 -KPX T w -50 -KPX T u -50 -KPX T semicolon -50 -KPX T r -50 -KPX T period -100 -KPX T o -74 -KPX T i -18 -KPX T hyphen -100 -KPX T h -25 -KPX T e -74 -KPX T comma -100 -KPX T colon -50 -KPX T a -74 -KPX T O 18 - -KPX U period -100 -KPX U comma -100 -KPX U A -18 - -KPX V u -75 -KPX V semicolon -75 -KPX V period -100 -KPX V o -75 -KPX V i -50 -KPX V hyphen -100 -KPX V e -75 -KPX V comma -100 -KPX V colon -75 -KPX V a -75 -KPX V A -37 - -KPX W y -55 -KPX W u -55 -KPX W semicolon -75 -KPX W period -100 -KPX W o -55 -KPX W i -20 -KPX W hyphen -75 -KPX W h -20 -KPX W e -55 -KPX W comma -100 -KPX W colon -75 -KPX W a -55 -KPX W A -55 - -KPX Y u -100 -KPX Y semicolon -75 -KPX Y period -100 -KPX Y o -100 -KPX Y i -25 -KPX Y hyphen -100 -KPX Y e -100 -KPX Y comma -100 -KPX Y colon -75 -KPX Y a -100 -KPX Y A -55 - -KPX b period -50 -KPX b comma -50 -KPX b b -10 - -KPX c period -50 -KPX c k -18 -KPX c h -18 -KPX c comma -50 - -KPX colon space -37 - -KPX comma space -37 -KPX comma quoteright -37 -KPX comma quotedblright -37 - -KPX e period -37 -KPX e comma -37 - -KPX f quoteright 75 -KPX f quotedblright 75 -KPX f period -75 -KPX f o -10 -KPX f comma -75 - -KPX g period -50 -KPX g comma -50 - -KPX l y -10 - -KPX o period -50 -KPX o comma -50 - -KPX p period -50 -KPX p comma -50 - -KPX period space -37 -KPX period quoteright -37 -KPX period quotedblright -37 - -KPX quotedblleft A -75 - -KPX quotedblright space -37 - -KPX quoteleft quoteleft -37 -KPX quoteleft A -75 - -KPX quoteright s -25 -KPX quoteright quoteright -37 -KPX quoteright d -37 - -KPX r semicolon -25 -KPX r s -10 -KPX r period -125 -KPX r k -18 -KPX r hyphen -75 -KPX r comma -125 -KPX r colon -25 - -KPX s period -50 -KPX s comma -50 - -KPX semicolon space -37 - -KPX space quoteleft -37 -KPX space quotedblleft -37 -KPX space Y -37 -KPX space W -37 -KPX space V -37 -KPX space T -37 -KPX space A -37 - -KPX v period -75 -KPX v comma -75 - -KPX w period -75 -KPX w comma -75 - -KPX y period -75 -KPX y comma -75 -EndKernPairs -EndKernData -StartComposites 56 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 246 256 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 246 256 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 231 256 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 246 256 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 216 246 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 231 256 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 255 256 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 255 256 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 255 256 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 255 256 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 97 256 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 97 256 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 97 256 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 97 256 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 301 256 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 283 256 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 283 256 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 283 256 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 283 256 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 283 256 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 227 256 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 301 256 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 301 256 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 301 256 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 301 256 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 256 256 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 236 256 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 227 256 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 121 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 121 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 121 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 121 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 121 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 121 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 56 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 56 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 56 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 56 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute 0 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex 0 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis 0 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave 0 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 139 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 84 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 84 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 84 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 84 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 84 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 56 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 139 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 139 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 139 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 139 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 84 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 84 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 65 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pplb8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pplb8a.afm deleted file mode 100644 index de7698d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pplb8a.afm +++ /dev/null @@ -1,434 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Mon Jul 2 22:26:30 1990 -Comment UniqueID 31793 -Comment VMusage 36031 46923 -FontName Palatino-Bold -FullName Palatino Bold -FamilyName Palatino -Weight Bold -ItalicAngle 0 -IsFixedPitch false -FontBBox -152 -266 1000 924 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.005 -Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved.Palatino is a trademark of Linotype AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 681 -XHeight 471 -Ascender 720 -Descender -258 -StartCharMetrics 228 -C 32 ; WX 250 ; N space ; B 0 0 0 0 ; -C 33 ; WX 278 ; N exclam ; B 63 -12 219 688 ; -C 34 ; WX 402 ; N quotedbl ; B 22 376 380 695 ; -C 35 ; WX 500 ; N numbersign ; B 4 0 496 673 ; -C 36 ; WX 500 ; N dollar ; B 28 -114 472 721 ; -C 37 ; WX 889 ; N percent ; B 61 -9 828 714 ; -C 38 ; WX 833 ; N ampersand ; B 52 -17 813 684 ; -C 39 ; WX 278 ; N quoteright ; B 29 405 249 695 ; -C 40 ; WX 333 ; N parenleft ; B 65 -104 305 723 ; -C 41 ; WX 333 ; N parenright ; B 28 -104 268 723 ; -C 42 ; WX 444 ; N asterisk ; B 44 332 399 695 ; -C 43 ; WX 606 ; N plus ; B 51 0 555 505 ; -C 44 ; WX 250 ; N comma ; B -6 -166 227 141 ; -C 45 ; WX 333 ; N hyphen ; B 16 195 317 305 ; -C 46 ; WX 250 ; N period ; B 47 -12 203 144 ; -C 47 ; WX 296 ; N slash ; B -9 -17 305 720 ; -C 48 ; WX 500 ; N zero ; B 33 -17 468 660 ; -C 49 ; WX 500 ; N one ; B 35 -3 455 670 ; -C 50 ; WX 500 ; N two ; B 25 -3 472 660 ; -C 51 ; WX 500 ; N three ; B 22 -17 458 660 ; -C 52 ; WX 500 ; N four ; B 12 -3 473 672 ; -C 53 ; WX 500 ; N five ; B 42 -17 472 656 ; -C 54 ; WX 500 ; N six ; B 37 -17 469 660 ; -C 55 ; WX 500 ; N seven ; B 46 -3 493 656 ; -C 56 ; WX 500 ; N eight ; B 34 -17 467 660 ; -C 57 ; WX 500 ; N nine ; B 31 -17 463 660 ; -C 58 ; WX 250 ; N colon ; B 47 -12 203 454 ; -C 59 ; WX 250 ; N semicolon ; B -6 -166 227 454 ; -C 60 ; WX 606 ; N less ; B 49 -15 558 519 ; -C 61 ; WX 606 ; N equal ; B 51 114 555 396 ; -C 62 ; WX 606 ; N greater ; B 49 -15 558 519 ; -C 63 ; WX 444 ; N question ; B 43 -12 411 687 ; -C 64 ; WX 747 ; N at ; B 42 -12 704 681 ; -C 65 ; WX 778 ; N A ; B 24 -3 757 686 ; -C 66 ; WX 667 ; N B ; B 39 -3 611 681 ; -C 67 ; WX 722 ; N C ; B 44 -17 695 695 ; -C 68 ; WX 833 ; N D ; B 35 -3 786 681 ; -C 69 ; WX 611 ; N E ; B 39 -4 577 681 ; -C 70 ; WX 556 ; N F ; B 28 -3 539 681 ; -C 71 ; WX 833 ; N G ; B 47 -17 776 695 ; -C 72 ; WX 833 ; N H ; B 36 -3 796 681 ; -C 73 ; WX 389 ; N I ; B 39 -3 350 681 ; -C 74 ; WX 389 ; N J ; B -11 -213 350 681 ; -C 75 ; WX 778 ; N K ; B 39 -3 763 681 ; -C 76 ; WX 611 ; N L ; B 39 -4 577 681 ; -C 77 ; WX 1000 ; N M ; B 32 -10 968 681 ; -C 78 ; WX 833 ; N N ; B 35 -16 798 681 ; -C 79 ; WX 833 ; N O ; B 47 -17 787 695 ; -C 80 ; WX 611 ; N P ; B 39 -3 594 681 ; -C 81 ; WX 833 ; N Q ; B 47 -184 787 695 ; -C 82 ; WX 722 ; N R ; B 39 -3 708 681 ; -C 83 ; WX 611 ; N S ; B 57 -17 559 695 ; -C 84 ; WX 667 ; N T ; B 17 -3 650 681 ; -C 85 ; WX 778 ; N U ; B 26 -17 760 681 ; -C 86 ; WX 778 ; N V ; B 20 -3 763 681 ; -C 87 ; WX 1000 ; N W ; B 17 -3 988 686 ; -C 88 ; WX 667 ; N X ; B 17 -3 650 695 ; -C 89 ; WX 667 ; N Y ; B 15 -3 660 695 ; -C 90 ; WX 667 ; N Z ; B 24 -3 627 681 ; -C 91 ; WX 333 ; N bracketleft ; B 73 -104 291 720 ; -C 92 ; WX 606 ; N backslash ; B 72 0 534 720 ; -C 93 ; WX 333 ; N bracketright ; B 42 -104 260 720 ; -C 94 ; WX 606 ; N asciicircum ; B 52 275 554 678 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 278 ; N quoteleft ; B 29 405 249 695 ; -C 97 ; WX 500 ; N a ; B 40 -17 478 471 ; -C 98 ; WX 611 ; N b ; B 10 -17 556 720 ; -C 99 ; WX 444 ; N c ; B 37 -17 414 471 ; -C 100 ; WX 611 ; N d ; B 42 -17 577 720 ; -C 101 ; WX 500 ; N e ; B 42 -17 461 471 ; -C 102 ; WX 389 ; N f ; B 34 -3 381 720 ; L i fi ; L l fl ; -C 103 ; WX 556 ; N g ; B 26 -266 535 471 ; -C 104 ; WX 611 ; N h ; B 24 -3 587 720 ; -C 105 ; WX 333 ; N i ; B 34 -3 298 706 ; -C 106 ; WX 333 ; N j ; B 3 -266 241 706 ; -C 107 ; WX 611 ; N k ; B 21 -3 597 720 ; -C 108 ; WX 333 ; N l ; B 24 -3 296 720 ; -C 109 ; WX 889 ; N m ; B 24 -3 864 471 ; -C 110 ; WX 611 ; N n ; B 24 -3 587 471 ; -C 111 ; WX 556 ; N o ; B 40 -17 517 471 ; -C 112 ; WX 611 ; N p ; B 29 -258 567 471 ; -C 113 ; WX 611 ; N q ; B 52 -258 589 471 ; -C 114 ; WX 389 ; N r ; B 30 -3 389 471 ; -C 115 ; WX 444 ; N s ; B 39 -17 405 471 ; -C 116 ; WX 333 ; N t ; B 22 -17 324 632 ; -C 117 ; WX 611 ; N u ; B 25 -17 583 471 ; -C 118 ; WX 556 ; N v ; B 11 -3 545 459 ; -C 119 ; WX 833 ; N w ; B 13 -3 820 471 ; -C 120 ; WX 500 ; N x ; B 20 -3 483 471 ; -C 121 ; WX 556 ; N y ; B 10 -266 546 459 ; -C 122 ; WX 500 ; N z ; B 16 -3 464 459 ; -C 123 ; WX 310 ; N braceleft ; B 5 -117 288 725 ; -C 124 ; WX 606 ; N bar ; B 260 0 346 720 ; -C 125 ; WX 310 ; N braceright ; B 22 -117 305 725 ; -C 126 ; WX 606 ; N asciitilde ; B 51 155 555 342 ; -C 161 ; WX 278 ; N exclamdown ; B 59 -227 215 471 ; -C 162 ; WX 500 ; N cent ; B 73 -106 450 554 ; -C 163 ; WX 500 ; N sterling ; B -2 -19 501 676 ; -C 164 ; WX 167 ; N fraction ; B -152 0 320 660 ; -C 165 ; WX 500 ; N yen ; B 17 -3 483 695 ; -C 166 ; WX 500 ; N florin ; B 11 -242 490 703 ; -C 167 ; WX 500 ; N section ; B 30 -217 471 695 ; -C 168 ; WX 500 ; N currency ; B 32 96 468 533 ; -C 169 ; WX 227 ; N quotesingle ; B 45 376 181 695 ; -C 170 ; WX 500 ; N quotedblleft ; B 34 405 466 695 ; -C 171 ; WX 500 ; N guillemotleft ; B 36 44 463 438 ; -C 172 ; WX 389 ; N guilsinglleft ; B 82 44 307 438 ; -C 173 ; WX 389 ; N guilsinglright ; B 82 44 307 438 ; -C 174 ; WX 611 ; N fi ; B 10 -3 595 720 ; -C 175 ; WX 611 ; N fl ; B 17 -3 593 720 ; -C 177 ; WX 500 ; N endash ; B 0 208 500 291 ; -C 178 ; WX 500 ; N dagger ; B 29 -6 472 682 ; -C 179 ; WX 500 ; N daggerdbl ; B 32 -245 468 682 ; -C 180 ; WX 250 ; N periodcentered ; B 47 179 203 335 ; -C 182 ; WX 641 ; N paragraph ; B 19 -161 599 683 ; -C 183 ; WX 606 ; N bullet ; B 131 172 475 516 ; -C 184 ; WX 333 ; N quotesinglbase ; B 56 -160 276 130 ; -C 185 ; WX 500 ; N quotedblbase ; B 34 -160 466 130 ; -C 186 ; WX 500 ; N quotedblright ; B 34 405 466 695 ; -C 187 ; WX 500 ; N guillemotright ; B 37 44 464 438 ; -C 188 ; WX 1000 ; N ellipsis ; B 89 -12 911 144 ; -C 189 ; WX 1000 ; N perthousand ; B 33 -9 982 724 ; -C 191 ; WX 444 ; N questiondown ; B 33 -231 401 471 ; -C 193 ; WX 333 ; N grave ; B 18 506 256 691 ; -C 194 ; WX 333 ; N acute ; B 78 506 316 691 ; -C 195 ; WX 333 ; N circumflex ; B -2 506 335 681 ; -C 196 ; WX 333 ; N tilde ; B -16 535 349 661 ; -C 197 ; WX 333 ; N macron ; B 1 538 332 609 ; -C 198 ; WX 333 ; N breve ; B 15 506 318 669 ; -C 199 ; WX 333 ; N dotaccent ; B 100 537 234 671 ; -C 200 ; WX 333 ; N dieresis ; B -8 537 341 671 ; -C 202 ; WX 333 ; N ring ; B 67 500 267 700 ; -C 203 ; WX 333 ; N cedilla ; B 73 -225 300 -7 ; -C 205 ; WX 333 ; N hungarumlaut ; B -56 506 390 691 ; -C 206 ; WX 333 ; N ogonek ; B 60 -246 274 -17 ; -C 207 ; WX 333 ; N caron ; B -2 510 335 685 ; -C 208 ; WX 1000 ; N emdash ; B 0 208 1000 291 ; -C 225 ; WX 1000 ; N AE ; B 12 -4 954 681 ; -C 227 ; WX 438 ; N ordfeminine ; B 77 367 361 660 ; -C 232 ; WX 611 ; N Lslash ; B 16 -4 577 681 ; -C 233 ; WX 833 ; N Oslash ; B 32 -20 808 698 ; -C 234 ; WX 1000 ; N OE ; B 43 -17 985 695 ; -C 235 ; WX 488 ; N ordmasculine ; B 89 367 399 660 ; -C 241 ; WX 778 ; N ae ; B 46 -17 731 471 ; -C 245 ; WX 333 ; N dotlessi ; B 34 -3 298 471 ; -C 248 ; WX 333 ; N lslash ; B -4 -3 334 720 ; -C 249 ; WX 556 ; N oslash ; B 23 -18 534 471 ; -C 250 ; WX 833 ; N oe ; B 48 -17 799 471 ; -C 251 ; WX 611 ; N germandbls ; B 30 -17 565 720 ; -C -1 ; WX 667 ; N Zcaron ; B 24 -3 627 909 ; -C -1 ; WX 444 ; N ccedilla ; B 37 -225 414 471 ; -C -1 ; WX 556 ; N ydieresis ; B 10 -266 546 691 ; -C -1 ; WX 500 ; N atilde ; B 40 -17 478 673 ; -C -1 ; WX 333 ; N icircumflex ; B -2 -3 335 701 ; -C -1 ; WX 300 ; N threesuperior ; B 9 261 292 667 ; -C -1 ; WX 500 ; N ecircumflex ; B 42 -17 461 701 ; -C -1 ; WX 611 ; N thorn ; B 17 -258 563 720 ; -C -1 ; WX 500 ; N egrave ; B 42 -17 461 711 ; -C -1 ; WX 300 ; N twosuperior ; B 5 261 295 660 ; -C -1 ; WX 500 ; N eacute ; B 42 -17 461 711 ; -C -1 ; WX 556 ; N otilde ; B 40 -17 517 673 ; -C -1 ; WX 778 ; N Aacute ; B 24 -3 757 915 ; -C -1 ; WX 556 ; N ocircumflex ; B 40 -17 517 701 ; -C -1 ; WX 556 ; N yacute ; B 10 -266 546 711 ; -C -1 ; WX 611 ; N udieresis ; B 25 -17 583 691 ; -C -1 ; WX 750 ; N threequarters ; B 15 -2 735 667 ; -C -1 ; WX 500 ; N acircumflex ; B 40 -17 478 701 ; -C -1 ; WX 833 ; N Eth ; B 10 -3 786 681 ; -C -1 ; WX 500 ; N edieresis ; B 42 -17 461 691 ; -C -1 ; WX 611 ; N ugrave ; B 25 -17 583 711 ; -C -1 ; WX 998 ; N trademark ; B 38 274 961 678 ; -C -1 ; WX 556 ; N ograve ; B 40 -17 517 711 ; -C -1 ; WX 444 ; N scaron ; B 39 -17 405 693 ; -C -1 ; WX 389 ; N Idieresis ; B 20 -3 369 895 ; -C -1 ; WX 611 ; N uacute ; B 25 -17 583 711 ; -C -1 ; WX 500 ; N agrave ; B 40 -17 478 711 ; -C -1 ; WX 611 ; N ntilde ; B 24 -3 587 673 ; -C -1 ; WX 500 ; N aring ; B 40 -17 478 700 ; -C -1 ; WX 500 ; N zcaron ; B 16 -3 464 693 ; -C -1 ; WX 389 ; N Icircumflex ; B 26 -3 363 905 ; -C -1 ; WX 833 ; N Ntilde ; B 35 -16 798 885 ; -C -1 ; WX 611 ; N ucircumflex ; B 25 -17 583 701 ; -C -1 ; WX 611 ; N Ecircumflex ; B 39 -4 577 905 ; -C -1 ; WX 389 ; N Iacute ; B 39 -3 350 915 ; -C -1 ; WX 722 ; N Ccedilla ; B 44 -225 695 695 ; -C -1 ; WX 833 ; N Odieresis ; B 47 -17 787 895 ; -C -1 ; WX 611 ; N Scaron ; B 57 -17 559 909 ; -C -1 ; WX 611 ; N Edieresis ; B 39 -4 577 895 ; -C -1 ; WX 389 ; N Igrave ; B 39 -3 350 915 ; -C -1 ; WX 500 ; N adieresis ; B 40 -17 478 691 ; -C -1 ; WX 833 ; N Ograve ; B 47 -17 787 915 ; -C -1 ; WX 611 ; N Egrave ; B 39 -4 577 915 ; -C -1 ; WX 667 ; N Ydieresis ; B 15 -3 660 895 ; -C -1 ; WX 747 ; N registered ; B 26 -17 720 695 ; -C -1 ; WX 833 ; N Otilde ; B 47 -17 787 885 ; -C -1 ; WX 750 ; N onequarter ; B 19 -2 735 665 ; -C -1 ; WX 778 ; N Ugrave ; B 26 -17 760 915 ; -C -1 ; WX 778 ; N Ucircumflex ; B 26 -17 760 905 ; -C -1 ; WX 611 ; N Thorn ; B 39 -3 574 681 ; -C -1 ; WX 606 ; N divide ; B 51 0 555 510 ; -C -1 ; WX 778 ; N Atilde ; B 24 -3 757 885 ; -C -1 ; WX 778 ; N Uacute ; B 26 -17 760 915 ; -C -1 ; WX 833 ; N Ocircumflex ; B 47 -17 787 905 ; -C -1 ; WX 606 ; N logicalnot ; B 51 114 555 396 ; -C -1 ; WX 778 ; N Aring ; B 24 -3 757 924 ; -C -1 ; WX 333 ; N idieresis ; B -8 -3 341 691 ; -C -1 ; WX 333 ; N iacute ; B 34 -3 316 711 ; -C -1 ; WX 500 ; N aacute ; B 40 -17 478 711 ; -C -1 ; WX 606 ; N plusminus ; B 51 0 555 505 ; -C -1 ; WX 606 ; N multiply ; B 72 21 534 483 ; -C -1 ; WX 778 ; N Udieresis ; B 26 -17 760 895 ; -C -1 ; WX 606 ; N minus ; B 51 212 555 298 ; -C -1 ; WX 300 ; N onesuperior ; B 14 261 287 665 ; -C -1 ; WX 611 ; N Eacute ; B 39 -4 577 915 ; -C -1 ; WX 778 ; N Acircumflex ; B 24 -3 757 905 ; -C -1 ; WX 747 ; N copyright ; B 26 -17 720 695 ; -C -1 ; WX 778 ; N Agrave ; B 24 -3 757 915 ; -C -1 ; WX 556 ; N odieresis ; B 40 -17 517 691 ; -C -1 ; WX 556 ; N oacute ; B 40 -17 517 711 ; -C -1 ; WX 400 ; N degree ; B 50 360 350 660 ; -C -1 ; WX 333 ; N igrave ; B 18 -3 298 711 ; -C -1 ; WX 611 ; N mu ; B 25 -225 583 471 ; -C -1 ; WX 833 ; N Oacute ; B 47 -17 787 915 ; -C -1 ; WX 556 ; N eth ; B 40 -17 517 720 ; -C -1 ; WX 778 ; N Adieresis ; B 24 -3 757 895 ; -C -1 ; WX 667 ; N Yacute ; B 15 -3 660 915 ; -C -1 ; WX 606 ; N brokenbar ; B 260 0 346 720 ; -C -1 ; WX 750 ; N onehalf ; B 9 -2 745 665 ; -EndCharMetrics -StartKernData -StartKernPairs 101 - -KPX A y -70 -KPX A w -70 -KPX A v -70 -KPX A space -18 -KPX A quoteright -92 -KPX A Y -111 -KPX A W -90 -KPX A V -129 -KPX A T -92 - -KPX F period -111 -KPX F comma -111 -KPX F A -55 - -KPX L y -74 -KPX L space -18 -KPX L quoteright -74 -KPX L Y -92 -KPX L W -92 -KPX L V -92 -KPX L T -74 - -KPX P period -129 -KPX P comma -129 -KPX P A -74 - -KPX R y -30 -KPX R Y -55 -KPX R W -37 -KPX R V -74 -KPX R T -55 - -KPX T y -90 -KPX T w -90 -KPX T u -129 -KPX T semicolon -74 -KPX T s -111 -KPX T r -111 -KPX T period -92 -KPX T o -111 -KPX T i -55 -KPX T hyphen -92 -KPX T e -111 -KPX T comma -92 -KPX T colon -74 -KPX T c -129 -KPX T a -111 -KPX T A -92 - -KPX V y -90 -KPX V u -92 -KPX V semicolon -74 -KPX V r -111 -KPX V period -129 -KPX V o -111 -KPX V i -55 -KPX V hyphen -92 -KPX V e -111 -KPX V comma -129 -KPX V colon -74 -KPX V a -111 -KPX V A -129 - -KPX W y -74 -KPX W u -74 -KPX W semicolon -37 -KPX W r -74 -KPX W period -37 -KPX W o -74 -KPX W i -37 -KPX W hyphen -37 -KPX W e -74 -KPX W comma -92 -KPX W colon -37 -KPX W a -74 -KPX W A -90 - -KPX Y v -74 -KPX Y u -74 -KPX Y semicolon -55 -KPX Y q -92 -KPX Y period -74 -KPX Y p -74 -KPX Y o -74 -KPX Y i -55 -KPX Y hyphen -74 -KPX Y e -74 -KPX Y comma -74 -KPX Y colon -55 -KPX Y a -74 -KPX Y A -55 - -KPX f quoteright 37 -KPX f f -18 - -KPX one one -37 - -KPX quoteleft quoteleft -55 - -KPX quoteright t -18 -KPX quoteright space -55 -KPX quoteright s -55 -KPX quoteright quoteright -55 - -KPX r quoteright 55 -KPX r period -55 -KPX r hyphen -18 -KPX r comma -55 - -KPX v period -111 -KPX v comma -111 - -KPX w period -92 -KPX w comma -92 - -KPX y period -92 -KPX y comma -92 -EndKernPairs -EndKernData -StartComposites 58 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 223 224 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 211 224 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 223 224 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 215 224 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 223 224 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 223 224 ; -CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 195 0 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 139 224 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 139 224 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 139 224 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 139 224 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 28 224 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 28 224 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 28 224 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 28 224 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 250 224 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 250 224 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 250 224 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 250 224 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 250 224 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 250 224 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 139 224 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 235 224 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 235 224 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 235 224 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 223 224 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 211 224 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 199 224 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 167 224 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 84 20 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 84 20 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 84 20 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 84 20 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 84 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 84 12 ; -CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 56 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 84 20 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 96 20 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 92 20 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 84 20 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute 0 20 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex 0 20 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis 0 20 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave 0 20 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 139 12 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 112 20 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 112 20 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 112 20 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 112 20 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 112 12 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 56 8 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 151 20 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 139 20 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 139 20 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 131 20 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 144 20 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 124 20 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 84 8 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pplbi8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pplbi8a.afm deleted file mode 100644 index e161d04..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pplbi8a.afm +++ /dev/null @@ -1,441 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Mon Jul 2 22:48:39 1990 -Comment UniqueID 31799 -Comment VMusage 37656 48548 -FontName Palatino-BoldItalic -FullName Palatino Bold Italic -FamilyName Palatino -Weight Bold -ItalicAngle -10 -IsFixedPitch false -FontBBox -170 -271 1073 926 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.005 -Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved.Palatino is a trademark of Linotype AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 681 -XHeight 469 -Ascender 726 -Descender -271 -StartCharMetrics 228 -C 32 ; WX 250 ; N space ; B 0 0 0 0 ; -C 33 ; WX 333 ; N exclam ; B 58 -17 322 695 ; -C 34 ; WX 500 ; N quotedbl ; B 137 467 493 720 ; -C 35 ; WX 500 ; N numbersign ; B 4 0 496 673 ; -C 36 ; WX 500 ; N dollar ; B 20 -108 477 737 ; -C 37 ; WX 889 ; N percent ; B 56 -17 790 697 ; -C 38 ; WX 833 ; N ampersand ; B 74 -17 811 695 ; -C 39 ; WX 278 ; N quoteright ; B 76 431 302 720 ; -C 40 ; WX 333 ; N parenleft ; B 58 -129 368 723 ; -C 41 ; WX 333 ; N parenright ; B -12 -129 298 723 ; -C 42 ; WX 444 ; N asterisk ; B 84 332 439 695 ; -C 43 ; WX 606 ; N plus ; B 50 -5 556 501 ; -C 44 ; WX 250 ; N comma ; B -33 -164 208 147 ; -C 45 ; WX 389 ; N hyphen ; B 37 198 362 300 ; -C 46 ; WX 250 ; N period ; B 48 -17 187 135 ; -C 47 ; WX 315 ; N slash ; B 1 -17 315 720 ; -C 48 ; WX 500 ; N zero ; B 42 -17 490 683 ; -C 49 ; WX 500 ; N one ; B 41 -3 434 678 ; -C 50 ; WX 500 ; N two ; B 1 -3 454 683 ; -C 51 ; WX 500 ; N three ; B 8 -17 450 683 ; -C 52 ; WX 500 ; N four ; B 3 -3 487 683 ; -C 53 ; WX 500 ; N five ; B 14 -17 481 675 ; -C 54 ; WX 500 ; N six ; B 39 -17 488 683 ; -C 55 ; WX 500 ; N seven ; B 69 -3 544 674 ; -C 56 ; WX 500 ; N eight ; B 26 -17 484 683 ; -C 57 ; WX 500 ; N nine ; B 27 -17 491 683 ; -C 58 ; WX 250 ; N colon ; B 38 -17 236 452 ; -C 59 ; WX 250 ; N semicolon ; B -33 -164 247 452 ; -C 60 ; WX 606 ; N less ; B 49 -21 558 517 ; -C 61 ; WX 606 ; N equal ; B 51 106 555 390 ; -C 62 ; WX 606 ; N greater ; B 48 -21 557 517 ; -C 63 ; WX 444 ; N question ; B 91 -17 450 695 ; -C 64 ; WX 833 ; N at ; B 82 -12 744 681 ; -C 65 ; WX 722 ; N A ; B -35 -3 685 683 ; -C 66 ; WX 667 ; N B ; B 8 -3 629 681 ; -C 67 ; WX 685 ; N C ; B 69 -17 695 695 ; -C 68 ; WX 778 ; N D ; B 0 -3 747 682 ; -C 69 ; WX 611 ; N E ; B 11 -3 606 681 ; -C 70 ; WX 556 ; N F ; B -6 -3 593 681 ; -C 71 ; WX 778 ; N G ; B 72 -17 750 695 ; -C 72 ; WX 778 ; N H ; B -12 -3 826 681 ; -C 73 ; WX 389 ; N I ; B -1 -3 412 681 ; -C 74 ; WX 389 ; N J ; B -29 -207 417 681 ; -C 75 ; WX 722 ; N K ; B -10 -3 746 681 ; -C 76 ; WX 611 ; N L ; B 26 -3 578 681 ; -C 77 ; WX 944 ; N M ; B -23 -17 985 681 ; -C 78 ; WX 778 ; N N ; B -2 -3 829 681 ; -C 79 ; WX 833 ; N O ; B 76 -17 794 695 ; -C 80 ; WX 667 ; N P ; B 11 -3 673 681 ; -C 81 ; WX 833 ; N Q ; B 76 -222 794 695 ; -C 82 ; WX 722 ; N R ; B 4 -3 697 681 ; -C 83 ; WX 556 ; N S ; B 50 -17 517 695 ; -C 84 ; WX 611 ; N T ; B 56 -3 674 681 ; -C 85 ; WX 778 ; N U ; B 83 -17 825 681 ; -C 86 ; WX 667 ; N V ; B 67 -3 745 681 ; -C 87 ; WX 1000 ; N W ; B 67 -3 1073 689 ; -C 88 ; WX 722 ; N X ; B -9 -3 772 681 ; -C 89 ; WX 611 ; N Y ; B 54 -3 675 695 ; -C 90 ; WX 667 ; N Z ; B 1 -3 676 681 ; -C 91 ; WX 333 ; N bracketleft ; B 45 -102 381 723 ; -C 92 ; WX 606 ; N backslash ; B 72 0 534 720 ; -C 93 ; WX 333 ; N bracketright ; B -21 -102 315 723 ; -C 94 ; WX 606 ; N asciicircum ; B 63 275 543 678 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 278 ; N quoteleft ; B 65 431 291 720 ; -C 97 ; WX 556 ; N a ; B 44 -17 519 470 ; -C 98 ; WX 537 ; N b ; B 44 -17 494 726 ; -C 99 ; WX 444 ; N c ; B 32 -17 436 469 ; -C 100 ; WX 556 ; N d ; B 38 -17 550 726 ; -C 101 ; WX 444 ; N e ; B 28 -17 418 469 ; -C 102 ; WX 333 ; N f ; B -130 -271 449 726 ; L i fi ; L l fl ; -C 103 ; WX 500 ; N g ; B -50 -271 529 469 ; -C 104 ; WX 556 ; N h ; B 22 -17 522 726 ; -C 105 ; WX 333 ; N i ; B 26 -17 312 695 ; -C 106 ; WX 333 ; N j ; B -64 -271 323 695 ; -C 107 ; WX 556 ; N k ; B 34 -17 528 726 ; -C 108 ; WX 333 ; N l ; B 64 -17 318 726 ; -C 109 ; WX 833 ; N m ; B 19 -17 803 469 ; -C 110 ; WX 556 ; N n ; B 17 -17 521 469 ; -C 111 ; WX 556 ; N o ; B 48 -17 502 469 ; -C 112 ; WX 556 ; N p ; B -21 -271 516 469 ; -C 113 ; WX 537 ; N q ; B 32 -271 513 469 ; -C 114 ; WX 389 ; N r ; B 20 -17 411 469 ; -C 115 ; WX 444 ; N s ; B 25 -17 406 469 ; -C 116 ; WX 389 ; N t ; B 42 -17 409 636 ; -C 117 ; WX 556 ; N u ; B 22 -17 521 469 ; -C 118 ; WX 556 ; N v ; B 19 -17 513 469 ; -C 119 ; WX 833 ; N w ; B 27 -17 802 469 ; -C 120 ; WX 500 ; N x ; B -8 -17 500 469 ; -C 121 ; WX 556 ; N y ; B 13 -271 541 469 ; -C 122 ; WX 500 ; N z ; B 31 -17 470 469 ; -C 123 ; WX 333 ; N braceleft ; B 18 -105 334 720 ; -C 124 ; WX 606 ; N bar ; B 259 0 347 720 ; -C 125 ; WX 333 ; N braceright ; B -1 -105 315 720 ; -C 126 ; WX 606 ; N asciitilde ; B 51 151 555 346 ; -C 161 ; WX 333 ; N exclamdown ; B 2 -225 259 479 ; -C 162 ; WX 500 ; N cent ; B 52 -105 456 547 ; -C 163 ; WX 500 ; N sterling ; B 21 -5 501 683 ; -C 164 ; WX 167 ; N fraction ; B -170 0 338 683 ; -C 165 ; WX 500 ; N yen ; B 11 -3 538 695 ; -C 166 ; WX 500 ; N florin ; B 8 -242 479 690 ; -C 167 ; WX 556 ; N section ; B 47 -151 497 695 ; -C 168 ; WX 500 ; N currency ; B 32 96 468 533 ; -C 169 ; WX 250 ; N quotesingle ; B 127 467 293 720 ; -C 170 ; WX 500 ; N quotedblleft ; B 65 431 511 720 ; -C 171 ; WX 500 ; N guillemotleft ; B 35 43 458 446 ; -C 172 ; WX 333 ; N guilsinglleft ; B 60 43 292 446 ; -C 173 ; WX 333 ; N guilsinglright ; B 35 40 267 443 ; -C 174 ; WX 611 ; N fi ; B -130 -271 588 726 ; -C 175 ; WX 611 ; N fl ; B -130 -271 631 726 ; -C 177 ; WX 500 ; N endash ; B -12 214 512 282 ; -C 178 ; WX 556 ; N dagger ; B 67 -3 499 685 ; -C 179 ; WX 556 ; N daggerdbl ; B 33 -153 537 693 ; -C 180 ; WX 250 ; N periodcentered ; B 67 172 206 324 ; -C 182 ; WX 556 ; N paragraph ; B 14 -204 629 681 ; -C 183 ; WX 606 ; N bullet ; B 131 172 475 516 ; -C 184 ; WX 250 ; N quotesinglbase ; B -3 -144 220 145 ; -C 185 ; WX 500 ; N quotedblbase ; B -18 -144 424 145 ; -C 186 ; WX 500 ; N quotedblright ; B 73 431 519 720 ; -C 187 ; WX 500 ; N guillemotright ; B 35 40 458 443 ; -C 188 ; WX 1000 ; N ellipsis ; B 91 -17 896 135 ; -C 189 ; WX 1000 ; N perthousand ; B 65 -17 912 691 ; -C 191 ; WX 444 ; N questiondown ; B -12 -226 347 479 ; -C 193 ; WX 333 ; N grave ; B 110 518 322 699 ; -C 194 ; WX 333 ; N acute ; B 153 518 392 699 ; -C 195 ; WX 333 ; N circumflex ; B 88 510 415 684 ; -C 196 ; WX 333 ; N tilde ; B 82 535 441 654 ; -C 197 ; WX 333 ; N macron ; B 76 538 418 608 ; -C 198 ; WX 333 ; N breve ; B 96 518 412 680 ; -C 199 ; WX 333 ; N dotaccent ; B 202 537 325 668 ; -C 200 ; WX 333 ; N dieresis ; B 90 537 426 668 ; -C 202 ; WX 556 ; N ring ; B 277 514 477 714 ; -C 203 ; WX 333 ; N cedilla ; B 12 -218 248 5 ; -C 205 ; WX 333 ; N hungarumlaut ; B -28 518 409 699 ; -C 206 ; WX 333 ; N ogonek ; B 32 -206 238 -17 ; -C 207 ; WX 333 ; N caron ; B 113 510 445 684 ; -C 208 ; WX 1000 ; N emdash ; B -12 214 1012 282 ; -C 225 ; WX 944 ; N AE ; B -29 -3 927 681 ; -C 227 ; WX 333 ; N ordfeminine ; B 47 391 355 684 ; -C 232 ; WX 611 ; N Lslash ; B 6 -3 578 681 ; -C 233 ; WX 833 ; N Oslash ; B 57 -54 797 730 ; -C 234 ; WX 944 ; N OE ; B 39 -17 961 695 ; -C 235 ; WX 333 ; N ordmasculine ; B 51 391 346 683 ; -C 241 ; WX 738 ; N ae ; B 44 -17 711 469 ; -C 245 ; WX 333 ; N dotlessi ; B 26 -17 293 469 ; -C 248 ; WX 333 ; N lslash ; B 13 -17 365 726 ; -C 249 ; WX 556 ; N oslash ; B 14 -50 522 506 ; -C 250 ; WX 778 ; N oe ; B 48 -17 755 469 ; -C 251 ; WX 556 ; N germandbls ; B -131 -271 549 726 ; -C -1 ; WX 667 ; N Zcaron ; B 1 -3 676 896 ; -C -1 ; WX 444 ; N ccedilla ; B 32 -218 436 469 ; -C -1 ; WX 556 ; N ydieresis ; B 13 -271 541 688 ; -C -1 ; WX 556 ; N atilde ; B 44 -17 553 666 ; -C -1 ; WX 333 ; N icircumflex ; B 26 -17 403 704 ; -C -1 ; WX 300 ; N threesuperior ; B 23 263 310 683 ; -C -1 ; WX 444 ; N ecircumflex ; B 28 -17 471 704 ; -C -1 ; WX 556 ; N thorn ; B -21 -271 516 726 ; -C -1 ; WX 444 ; N egrave ; B 28 -17 418 719 ; -C -1 ; WX 300 ; N twosuperior ; B 26 271 321 683 ; -C -1 ; WX 444 ; N eacute ; B 28 -17 448 719 ; -C -1 ; WX 556 ; N otilde ; B 48 -17 553 666 ; -C -1 ; WX 722 ; N Aacute ; B -35 -3 685 911 ; -C -1 ; WX 556 ; N ocircumflex ; B 48 -17 515 704 ; -C -1 ; WX 556 ; N yacute ; B 13 -271 541 719 ; -C -1 ; WX 556 ; N udieresis ; B 22 -17 538 688 ; -C -1 ; WX 750 ; N threequarters ; B 18 -2 732 683 ; -C -1 ; WX 556 ; N acircumflex ; B 44 -17 527 704 ; -C -1 ; WX 778 ; N Eth ; B 0 -3 747 682 ; -C -1 ; WX 444 ; N edieresis ; B 28 -17 482 688 ; -C -1 ; WX 556 ; N ugrave ; B 22 -17 521 719 ; -C -1 ; WX 1000 ; N trademark ; B 38 274 961 678 ; -C -1 ; WX 556 ; N ograve ; B 48 -17 502 719 ; -C -1 ; WX 444 ; N scaron ; B 25 -17 489 692 ; -C -1 ; WX 389 ; N Idieresis ; B -1 -3 454 880 ; -C -1 ; WX 556 ; N uacute ; B 22 -17 521 719 ; -C -1 ; WX 556 ; N agrave ; B 44 -17 519 719 ; -C -1 ; WX 556 ; N ntilde ; B 17 -17 553 666 ; -C -1 ; WX 556 ; N aring ; B 44 -17 519 714 ; -C -1 ; WX 500 ; N zcaron ; B 31 -17 517 692 ; -C -1 ; WX 389 ; N Icircumflex ; B -1 -3 443 896 ; -C -1 ; WX 778 ; N Ntilde ; B -2 -3 829 866 ; -C -1 ; WX 556 ; N ucircumflex ; B 22 -17 521 704 ; -C -1 ; WX 611 ; N Ecircumflex ; B 11 -3 606 896 ; -C -1 ; WX 389 ; N Iacute ; B -1 -3 420 911 ; -C -1 ; WX 685 ; N Ccedilla ; B 69 -218 695 695 ; -C -1 ; WX 833 ; N Odieresis ; B 76 -17 794 880 ; -C -1 ; WX 556 ; N Scaron ; B 50 -17 557 896 ; -C -1 ; WX 611 ; N Edieresis ; B 11 -3 606 880 ; -C -1 ; WX 389 ; N Igrave ; B -1 -3 412 911 ; -C -1 ; WX 556 ; N adieresis ; B 44 -17 538 688 ; -C -1 ; WX 833 ; N Ograve ; B 76 -17 794 911 ; -C -1 ; WX 611 ; N Egrave ; B 11 -3 606 911 ; -C -1 ; WX 611 ; N Ydieresis ; B 54 -3 675 880 ; -C -1 ; WX 747 ; N registered ; B 26 -17 720 695 ; -C -1 ; WX 833 ; N Otilde ; B 76 -17 794 866 ; -C -1 ; WX 750 ; N onequarter ; B 18 -2 732 683 ; -C -1 ; WX 778 ; N Ugrave ; B 83 -17 825 911 ; -C -1 ; WX 778 ; N Ucircumflex ; B 83 -17 825 896 ; -C -1 ; WX 667 ; N Thorn ; B 11 -3 644 681 ; -C -1 ; WX 606 ; N divide ; B 50 -5 556 501 ; -C -1 ; WX 722 ; N Atilde ; B -35 -3 685 866 ; -C -1 ; WX 778 ; N Uacute ; B 83 -17 825 911 ; -C -1 ; WX 833 ; N Ocircumflex ; B 76 -17 794 896 ; -C -1 ; WX 606 ; N logicalnot ; B 51 107 555 390 ; -C -1 ; WX 722 ; N Aring ; B -35 -3 685 926 ; -C -1 ; WX 333 ; N idieresis ; B 26 -17 426 688 ; -C -1 ; WX 333 ; N iacute ; B 26 -17 392 719 ; -C -1 ; WX 556 ; N aacute ; B 44 -17 519 719 ; -C -1 ; WX 606 ; N plusminus ; B 50 0 556 501 ; -C -1 ; WX 606 ; N multiply ; B 72 17 534 479 ; -C -1 ; WX 778 ; N Udieresis ; B 83 -17 825 880 ; -C -1 ; WX 606 ; N minus ; B 51 204 555 292 ; -C -1 ; WX 300 ; N onesuperior ; B 41 271 298 680 ; -C -1 ; WX 611 ; N Eacute ; B 11 -3 606 911 ; -C -1 ; WX 722 ; N Acircumflex ; B -35 -3 685 896 ; -C -1 ; WX 747 ; N copyright ; B 26 -17 720 695 ; -C -1 ; WX 722 ; N Agrave ; B -35 -3 685 911 ; -C -1 ; WX 556 ; N odieresis ; B 48 -17 538 688 ; -C -1 ; WX 556 ; N oacute ; B 48 -17 504 719 ; -C -1 ; WX 400 ; N degree ; B 50 383 350 683 ; -C -1 ; WX 333 ; N igrave ; B 26 -17 322 719 ; -C -1 ; WX 556 ; N mu ; B -15 -232 521 469 ; -C -1 ; WX 833 ; N Oacute ; B 76 -17 794 911 ; -C -1 ; WX 556 ; N eth ; B 48 -17 546 726 ; -C -1 ; WX 722 ; N Adieresis ; B -35 -3 685 880 ; -C -1 ; WX 611 ; N Yacute ; B 54 -3 675 911 ; -C -1 ; WX 606 ; N brokenbar ; B 259 0 347 720 ; -C -1 ; WX 750 ; N onehalf ; B 14 -2 736 683 ; -EndCharMetrics -StartKernData -StartKernPairs 108 - -KPX A y -55 -KPX A w -37 -KPX A v -55 -KPX A space -55 -KPX A quoteright -55 -KPX A Y -74 -KPX A W -74 -KPX A V -74 -KPX A T -55 - -KPX F space -18 -KPX F period -111 -KPX F comma -111 -KPX F A -74 - -KPX L y -37 -KPX L space -18 -KPX L quoteright -55 -KPX L Y -74 -KPX L W -74 -KPX L V -74 -KPX L T -74 - -KPX P space -55 -KPX P period -129 -KPX P comma -129 -KPX P A -92 - -KPX R y -20 -KPX R Y -37 -KPX R W -55 -KPX R V -55 -KPX R T -37 - -KPX T y -80 -KPX T w -50 -KPX T u -92 -KPX T semicolon -55 -KPX T s -92 -KPX T r -92 -KPX T period -55 -KPX T o -111 -KPX T i -74 -KPX T hyphen -92 -KPX T e -111 -KPX T comma -55 -KPX T colon -55 -KPX T c -92 -KPX T a -111 -KPX T O -18 -KPX T A -55 - -KPX V y -50 -KPX V u -50 -KPX V semicolon -37 -KPX V r -74 -KPX V period -111 -KPX V o -74 -KPX V i -50 -KPX V hyphen -37 -KPX V e -74 -KPX V comma -111 -KPX V colon -37 -KPX V a -92 -KPX V A -74 - -KPX W y -30 -KPX W u -30 -KPX W semicolon -18 -KPX W r -30 -KPX W period -55 -KPX W o -55 -KPX W i -30 -KPX W e -55 -KPX W comma -55 -KPX W colon -28 -KPX W a -74 -KPX W A -74 - -KPX Y v -30 -KPX Y u -50 -KPX Y semicolon -55 -KPX Y q -92 -KPX Y period -55 -KPX Y p -74 -KPX Y o -111 -KPX Y i -54 -KPX Y hyphen -55 -KPX Y e -92 -KPX Y comma -55 -KPX Y colon -55 -KPX Y a -111 -KPX Y A -55 - -KPX f quoteright 37 -KPX f f -37 - -KPX one one -55 - -KPX quoteleft quoteleft -55 - -KPX quoteright t -18 -KPX quoteright space -37 -KPX quoteright s -37 -KPX quoteright quoteright -55 - -KPX r quoteright 55 -KPX r q -18 -KPX r period -55 -KPX r o -18 -KPX r h -18 -KPX r g -18 -KPX r e -18 -KPX r comma -55 -KPX r c -18 - -KPX v period -55 -KPX v comma -55 - -KPX w period -55 -KPX w comma -55 - -KPX y period -37 -KPX y comma -37 -EndKernPairs -EndKernData -StartComposites 58 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 195 212 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 195 212 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 195 212 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 195 212 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 83 212 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 195 212 ; -CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 176 0 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 139 212 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 139 212 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 139 212 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 139 212 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 28 212 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 28 212 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 28 212 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 28 212 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 223 212 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 250 212 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 250 212 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 250 212 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 250 212 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 250 212 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 112 212 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 223 212 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 223 212 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 223 212 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 211 212 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 151 212 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 139 212 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 167 212 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 112 20 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 112 20 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 112 20 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 112 20 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 0 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 112 12 ; -CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 56 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 56 20 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 56 20 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 56 20 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 56 20 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute 0 20 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -12 20 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis 0 20 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave 0 20 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 112 12 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 112 20 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 100 20 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 112 20 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 112 20 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 112 12 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 44 8 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 112 20 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 100 20 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 112 20 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 112 20 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 112 20 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 112 20 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 72 8 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pplr8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pplr8a.afm deleted file mode 100644 index 6566b16..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pplr8a.afm +++ /dev/null @@ -1,445 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Mon Jul 2 22:14:17 1990 -Comment UniqueID 31790 -Comment VMusage 36445 47337 -FontName Palatino-Roman -FullName Palatino Roman -FamilyName Palatino -Weight Roman -ItalicAngle 0 -IsFixedPitch false -FontBBox -166 -283 1021 927 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.005 -Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved.Palatino is a trademark of Linotype AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 692 -XHeight 469 -Ascender 726 -Descender -281 -StartCharMetrics 228 -C 32 ; WX 250 ; N space ; B 0 0 0 0 ; -C 33 ; WX 278 ; N exclam ; B 81 -5 197 694 ; -C 34 ; WX 371 ; N quotedbl ; B 52 469 319 709 ; -C 35 ; WX 500 ; N numbersign ; B 4 0 495 684 ; -C 36 ; WX 500 ; N dollar ; B 30 -116 471 731 ; -C 37 ; WX 840 ; N percent ; B 39 -20 802 709 ; -C 38 ; WX 778 ; N ampersand ; B 43 -20 753 689 ; -C 39 ; WX 278 ; N quoteright ; B 45 446 233 709 ; -C 40 ; WX 333 ; N parenleft ; B 60 -215 301 726 ; -C 41 ; WX 333 ; N parenright ; B 32 -215 273 726 ; -C 42 ; WX 389 ; N asterisk ; B 32 342 359 689 ; -C 43 ; WX 606 ; N plus ; B 51 7 555 512 ; -C 44 ; WX 250 ; N comma ; B 16 -155 218 123 ; -C 45 ; WX 333 ; N hyphen ; B 17 215 312 287 ; -C 46 ; WX 250 ; N period ; B 67 -5 183 111 ; -C 47 ; WX 606 ; N slash ; B 87 -119 519 726 ; -C 48 ; WX 500 ; N zero ; B 29 -20 465 689 ; -C 49 ; WX 500 ; N one ; B 60 -3 418 694 ; -C 50 ; WX 500 ; N two ; B 16 -3 468 689 ; -C 51 ; WX 500 ; N three ; B 15 -20 462 689 ; -C 52 ; WX 500 ; N four ; B 2 -3 472 694 ; -C 53 ; WX 500 ; N five ; B 13 -20 459 689 ; -C 54 ; WX 500 ; N six ; B 32 -20 468 689 ; -C 55 ; WX 500 ; N seven ; B 44 -3 497 689 ; -C 56 ; WX 500 ; N eight ; B 30 -20 464 689 ; -C 57 ; WX 500 ; N nine ; B 20 -20 457 689 ; -C 58 ; WX 250 ; N colon ; B 66 -5 182 456 ; -C 59 ; WX 250 ; N semicolon ; B 16 -153 218 456 ; -C 60 ; WX 606 ; N less ; B 57 0 558 522 ; -C 61 ; WX 606 ; N equal ; B 51 136 555 386 ; -C 62 ; WX 606 ; N greater ; B 48 0 549 522 ; -C 63 ; WX 444 ; N question ; B 43 -5 395 694 ; -C 64 ; WX 747 ; N at ; B 24 -20 724 694 ; -C 65 ; WX 778 ; N A ; B 15 -3 756 700 ; -C 66 ; WX 611 ; N B ; B 26 -3 576 692 ; -C 67 ; WX 709 ; N C ; B 22 -20 670 709 ; -C 68 ; WX 774 ; N D ; B 22 -3 751 692 ; -C 69 ; WX 611 ; N E ; B 22 -3 572 692 ; -C 70 ; WX 556 ; N F ; B 22 -3 536 692 ; -C 71 ; WX 763 ; N G ; B 22 -20 728 709 ; -C 72 ; WX 832 ; N H ; B 22 -3 810 692 ; -C 73 ; WX 337 ; N I ; B 22 -3 315 692 ; -C 74 ; WX 333 ; N J ; B -15 -194 311 692 ; -C 75 ; WX 726 ; N K ; B 22 -3 719 692 ; -C 76 ; WX 611 ; N L ; B 22 -3 586 692 ; -C 77 ; WX 946 ; N M ; B 16 -13 926 692 ; -C 78 ; WX 831 ; N N ; B 17 -20 813 692 ; -C 79 ; WX 786 ; N O ; B 22 -20 764 709 ; -C 80 ; WX 604 ; N P ; B 22 -3 580 692 ; -C 81 ; WX 786 ; N Q ; B 22 -176 764 709 ; -C 82 ; WX 668 ; N R ; B 22 -3 669 692 ; -C 83 ; WX 525 ; N S ; B 24 -20 503 709 ; -C 84 ; WX 613 ; N T ; B 18 -3 595 692 ; -C 85 ; WX 778 ; N U ; B 12 -20 759 692 ; -C 86 ; WX 722 ; N V ; B 8 -9 706 692 ; -C 87 ; WX 1000 ; N W ; B 8 -9 984 700 ; -C 88 ; WX 667 ; N X ; B 14 -3 648 700 ; -C 89 ; WX 667 ; N Y ; B 9 -3 654 704 ; -C 90 ; WX 667 ; N Z ; B 15 -3 638 692 ; -C 91 ; WX 333 ; N bracketleft ; B 79 -184 288 726 ; -C 92 ; WX 606 ; N backslash ; B 81 0 512 726 ; -C 93 ; WX 333 ; N bracketright ; B 45 -184 254 726 ; -C 94 ; WX 606 ; N asciicircum ; B 51 283 554 689 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 278 ; N quoteleft ; B 45 446 233 709 ; -C 97 ; WX 500 ; N a ; B 32 -12 471 469 ; -C 98 ; WX 553 ; N b ; B -15 -12 508 726 ; -C 99 ; WX 444 ; N c ; B 26 -20 413 469 ; -C 100 ; WX 611 ; N d ; B 35 -12 579 726 ; -C 101 ; WX 479 ; N e ; B 26 -20 448 469 ; -C 102 ; WX 333 ; N f ; B 23 -3 341 728 ; L i fi ; L l fl ; -C 103 ; WX 556 ; N g ; B 32 -283 544 469 ; -C 104 ; WX 582 ; N h ; B 6 -3 572 726 ; -C 105 ; WX 291 ; N i ; B 21 -3 271 687 ; -C 106 ; WX 234 ; N j ; B -40 -283 167 688 ; -C 107 ; WX 556 ; N k ; B 21 -12 549 726 ; -C 108 ; WX 291 ; N l ; B 21 -3 271 726 ; -C 109 ; WX 883 ; N m ; B 16 -3 869 469 ; -C 110 ; WX 582 ; N n ; B 6 -3 572 469 ; -C 111 ; WX 546 ; N o ; B 32 -20 514 469 ; -C 112 ; WX 601 ; N p ; B 8 -281 554 469 ; -C 113 ; WX 560 ; N q ; B 35 -281 560 469 ; -C 114 ; WX 395 ; N r ; B 21 -3 374 469 ; -C 115 ; WX 424 ; N s ; B 30 -20 391 469 ; -C 116 ; WX 326 ; N t ; B 22 -12 319 621 ; -C 117 ; WX 603 ; N u ; B 18 -12 581 469 ; -C 118 ; WX 565 ; N v ; B 6 -7 539 459 ; -C 119 ; WX 834 ; N w ; B 6 -7 808 469 ; -C 120 ; WX 516 ; N x ; B 20 -3 496 469 ; -C 121 ; WX 556 ; N y ; B 12 -283 544 459 ; -C 122 ; WX 500 ; N z ; B 16 -3 466 462 ; -C 123 ; WX 333 ; N braceleft ; B 58 -175 289 726 ; -C 124 ; WX 606 ; N bar ; B 275 0 331 726 ; -C 125 ; WX 333 ; N braceright ; B 44 -175 275 726 ; -C 126 ; WX 606 ; N asciitilde ; B 51 176 555 347 ; -C 161 ; WX 278 ; N exclamdown ; B 81 -225 197 469 ; -C 162 ; WX 500 ; N cent ; B 61 -101 448 562 ; -C 163 ; WX 500 ; N sterling ; B 12 -13 478 694 ; -C 164 ; WX 167 ; N fraction ; B -166 0 337 689 ; -C 165 ; WX 500 ; N yen ; B 5 -3 496 701 ; -C 166 ; WX 500 ; N florin ; B 0 -262 473 706 ; -C 167 ; WX 500 ; N section ; B 26 -219 465 709 ; -C 168 ; WX 500 ; N currency ; B 30 96 470 531 ; -C 169 ; WX 208 ; N quotesingle ; B 61 469 147 709 ; -C 170 ; WX 500 ; N quotedblleft ; B 51 446 449 709 ; -C 171 ; WX 500 ; N guillemotleft ; B 50 71 450 428 ; -C 172 ; WX 331 ; N guilsinglleft ; B 66 71 265 428 ; -C 173 ; WX 331 ; N guilsinglright ; B 66 71 265 428 ; -C 174 ; WX 605 ; N fi ; B 23 -3 587 728 ; -C 175 ; WX 608 ; N fl ; B 23 -3 590 728 ; -C 177 ; WX 500 ; N endash ; B 0 219 500 277 ; -C 178 ; WX 500 ; N dagger ; B 34 -5 466 694 ; -C 179 ; WX 500 ; N daggerdbl ; B 34 -249 466 694 ; -C 180 ; WX 250 ; N periodcentered ; B 67 203 183 319 ; -C 182 ; WX 628 ; N paragraph ; B 39 -150 589 694 ; -C 183 ; WX 606 ; N bullet ; B 131 172 475 516 ; -C 184 ; WX 278 ; N quotesinglbase ; B 22 -153 210 110 ; -C 185 ; WX 500 ; N quotedblbase ; B 51 -153 449 110 ; -C 186 ; WX 500 ; N quotedblright ; B 51 446 449 709 ; -C 187 ; WX 500 ; N guillemotright ; B 50 71 450 428 ; -C 188 ; WX 1000 ; N ellipsis ; B 109 -5 891 111 ; -C 189 ; WX 1144 ; N perthousand ; B 123 -20 1021 709 ; -C 191 ; WX 444 ; N questiondown ; B 43 -231 395 469 ; -C 193 ; WX 333 ; N grave ; B 31 506 255 677 ; -C 194 ; WX 333 ; N acute ; B 78 506 302 677 ; -C 195 ; WX 333 ; N circumflex ; B 11 510 323 677 ; -C 196 ; WX 333 ; N tilde ; B 2 535 332 640 ; -C 197 ; WX 333 ; N macron ; B 11 538 323 591 ; -C 198 ; WX 333 ; N breve ; B 26 506 308 664 ; -C 199 ; WX 250 ; N dotaccent ; B 75 537 175 637 ; -C 200 ; WX 333 ; N dieresis ; B 17 537 316 637 ; -C 202 ; WX 333 ; N ring ; B 67 496 267 696 ; -C 203 ; WX 333 ; N cedilla ; B 96 -225 304 -10 ; -C 205 ; WX 380 ; N hungarumlaut ; B 3 506 377 687 ; -C 206 ; WX 313 ; N ogonek ; B 68 -165 245 -20 ; -C 207 ; WX 333 ; N caron ; B 11 510 323 677 ; -C 208 ; WX 1000 ; N emdash ; B 0 219 1000 277 ; -C 225 ; WX 944 ; N AE ; B -10 -3 908 692 ; -C 227 ; WX 333 ; N ordfeminine ; B 24 422 310 709 ; -C 232 ; WX 611 ; N Lslash ; B 6 -3 586 692 ; -C 233 ; WX 833 ; N Oslash ; B 30 -20 797 709 ; -C 234 ; WX 998 ; N OE ; B 22 -20 962 709 ; -C 235 ; WX 333 ; N ordmasculine ; B 10 416 323 709 ; -C 241 ; WX 758 ; N ae ; B 30 -20 732 469 ; -C 245 ; WX 287 ; N dotlessi ; B 21 -3 271 469 ; -C 248 ; WX 291 ; N lslash ; B -14 -3 306 726 ; -C 249 ; WX 556 ; N oslash ; B 16 -23 530 474 ; -C 250 ; WX 827 ; N oe ; B 32 -20 800 469 ; -C 251 ; WX 556 ; N germandbls ; B 23 -9 519 731 ; -C -1 ; WX 667 ; N Zcaron ; B 15 -3 638 908 ; -C -1 ; WX 444 ; N ccedilla ; B 26 -225 413 469 ; -C -1 ; WX 556 ; N ydieresis ; B 12 -283 544 657 ; -C -1 ; WX 500 ; N atilde ; B 32 -12 471 652 ; -C -1 ; WX 287 ; N icircumflex ; B -12 -3 300 697 ; -C -1 ; WX 300 ; N threesuperior ; B 1 266 299 689 ; -C -1 ; WX 479 ; N ecircumflex ; B 26 -20 448 697 ; -C -1 ; WX 601 ; N thorn ; B -2 -281 544 726 ; -C -1 ; WX 479 ; N egrave ; B 26 -20 448 697 ; -C -1 ; WX 300 ; N twosuperior ; B 0 273 301 689 ; -C -1 ; WX 479 ; N eacute ; B 26 -20 448 697 ; -C -1 ; WX 546 ; N otilde ; B 32 -20 514 652 ; -C -1 ; WX 778 ; N Aacute ; B 15 -3 756 908 ; -C -1 ; WX 546 ; N ocircumflex ; B 32 -20 514 697 ; -C -1 ; WX 556 ; N yacute ; B 12 -283 544 697 ; -C -1 ; WX 603 ; N udieresis ; B 18 -12 581 657 ; -C -1 ; WX 750 ; N threequarters ; B 15 -3 735 689 ; -C -1 ; WX 500 ; N acircumflex ; B 32 -12 471 697 ; -C -1 ; WX 774 ; N Eth ; B 14 -3 751 692 ; -C -1 ; WX 479 ; N edieresis ; B 26 -20 448 657 ; -C -1 ; WX 603 ; N ugrave ; B 18 -12 581 697 ; -C -1 ; WX 979 ; N trademark ; B 40 285 939 689 ; -C -1 ; WX 546 ; N ograve ; B 32 -20 514 697 ; -C -1 ; WX 424 ; N scaron ; B 30 -20 391 685 ; -C -1 ; WX 337 ; N Idieresis ; B 19 -3 318 868 ; -C -1 ; WX 603 ; N uacute ; B 18 -12 581 697 ; -C -1 ; WX 500 ; N agrave ; B 32 -12 471 697 ; -C -1 ; WX 582 ; N ntilde ; B 6 -3 572 652 ; -C -1 ; WX 500 ; N aring ; B 32 -12 471 716 ; -C -1 ; WX 500 ; N zcaron ; B 16 -3 466 685 ; -C -1 ; WX 337 ; N Icircumflex ; B 13 -3 325 908 ; -C -1 ; WX 831 ; N Ntilde ; B 17 -20 813 871 ; -C -1 ; WX 603 ; N ucircumflex ; B 18 -12 581 697 ; -C -1 ; WX 611 ; N Ecircumflex ; B 22 -3 572 908 ; -C -1 ; WX 337 ; N Iacute ; B 22 -3 315 908 ; -C -1 ; WX 709 ; N Ccedilla ; B 22 -225 670 709 ; -C -1 ; WX 786 ; N Odieresis ; B 22 -20 764 868 ; -C -1 ; WX 525 ; N Scaron ; B 24 -20 503 908 ; -C -1 ; WX 611 ; N Edieresis ; B 22 -3 572 868 ; -C -1 ; WX 337 ; N Igrave ; B 22 -3 315 908 ; -C -1 ; WX 500 ; N adieresis ; B 32 -12 471 657 ; -C -1 ; WX 786 ; N Ograve ; B 22 -20 764 908 ; -C -1 ; WX 611 ; N Egrave ; B 22 -3 572 908 ; -C -1 ; WX 667 ; N Ydieresis ; B 9 -3 654 868 ; -C -1 ; WX 747 ; N registered ; B 11 -18 736 706 ; -C -1 ; WX 786 ; N Otilde ; B 22 -20 764 883 ; -C -1 ; WX 750 ; N onequarter ; B 30 -3 727 692 ; -C -1 ; WX 778 ; N Ugrave ; B 12 -20 759 908 ; -C -1 ; WX 778 ; N Ucircumflex ; B 12 -20 759 908 ; -C -1 ; WX 604 ; N Thorn ; B 32 -3 574 692 ; -C -1 ; WX 606 ; N divide ; B 51 10 555 512 ; -C -1 ; WX 778 ; N Atilde ; B 15 -3 756 871 ; -C -1 ; WX 778 ; N Uacute ; B 12 -20 759 908 ; -C -1 ; WX 786 ; N Ocircumflex ; B 22 -20 764 908 ; -C -1 ; WX 606 ; N logicalnot ; B 51 120 551 386 ; -C -1 ; WX 778 ; N Aring ; B 15 -3 756 927 ; -C -1 ; WX 287 ; N idieresis ; B -6 -3 293 657 ; -C -1 ; WX 287 ; N iacute ; B 21 -3 279 697 ; -C -1 ; WX 500 ; N aacute ; B 32 -12 471 697 ; -C -1 ; WX 606 ; N plusminus ; B 51 0 555 512 ; -C -1 ; WX 606 ; N multiply ; B 83 36 523 474 ; -C -1 ; WX 778 ; N Udieresis ; B 12 -20 759 868 ; -C -1 ; WX 606 ; N minus ; B 51 233 555 289 ; -C -1 ; WX 300 ; N onesuperior ; B 31 273 269 692 ; -C -1 ; WX 611 ; N Eacute ; B 22 -3 572 908 ; -C -1 ; WX 778 ; N Acircumflex ; B 15 -3 756 908 ; -C -1 ; WX 747 ; N copyright ; B 11 -18 736 706 ; -C -1 ; WX 778 ; N Agrave ; B 15 -3 756 908 ; -C -1 ; WX 546 ; N odieresis ; B 32 -20 514 657 ; -C -1 ; WX 546 ; N oacute ; B 32 -20 514 697 ; -C -1 ; WX 400 ; N degree ; B 50 389 350 689 ; -C -1 ; WX 287 ; N igrave ; B 8 -3 271 697 ; -C -1 ; WX 603 ; N mu ; B 18 -236 581 469 ; -C -1 ; WX 786 ; N Oacute ; B 22 -20 764 908 ; -C -1 ; WX 546 ; N eth ; B 32 -20 504 728 ; -C -1 ; WX 778 ; N Adieresis ; B 15 -3 756 868 ; -C -1 ; WX 667 ; N Yacute ; B 9 -3 654 908 ; -C -1 ; WX 606 ; N brokenbar ; B 275 0 331 726 ; -C -1 ; WX 750 ; N onehalf ; B 15 -3 735 692 ; -EndCharMetrics -StartKernData -StartKernPairs 111 - -KPX A y -74 -KPX A w -74 -KPX A v -92 -KPX A space -55 -KPX A quoteright -74 -KPX A Y -111 -KPX A W -74 -KPX A V -111 -KPX A T -74 - -KPX F period -92 -KPX F comma -92 -KPX F A -74 - -KPX L y -55 -KPX L space -37 -KPX L quoteright -74 -KPX L Y -92 -KPX L W -74 -KPX L V -92 -KPX L T -74 - -KPX P space -18 -KPX P period -129 -KPX P comma -129 -KPX P A -92 - -KPX R y -37 -KPX R Y -37 -KPX R W -37 -KPX R V -55 -KPX R T -37 - -KPX T y -90 -KPX T w -90 -KPX T u -90 -KPX T semicolon -55 -KPX T s -90 -KPX T r -90 -KPX T period -74 -KPX T o -92 -KPX T i -55 -KPX T hyphen -55 -KPX T e -92 -KPX T comma -74 -KPX T colon -55 -KPX T c -111 -KPX T a -92 -KPX T O -18 -KPX T A -74 - -KPX V y -92 -KPX V u -92 -KPX V semicolon -55 -KPX V r -92 -KPX V period -129 -KPX V o -111 -KPX V i -55 -KPX V hyphen -74 -KPX V e -111 -KPX V comma -129 -KPX V colon -55 -KPX V a -92 -KPX V A -111 - -KPX W y -50 -KPX W u -50 -KPX W semicolon -18 -KPX W r -74 -KPX W period -92 -KPX W o -92 -KPX W i -55 -KPX W hyphen -55 -KPX W e -92 -KPX W comma -92 -KPX W colon -18 -KPX W a -92 -KPX W A -92 - -KPX Y v -90 -KPX Y u -90 -KPX Y space -18 -KPX Y semicolon -74 -KPX Y q -90 -KPX Y period -111 -KPX Y p -111 -KPX Y o -92 -KPX Y i -55 -KPX Y hyphen -92 -KPX Y e -92 -KPX Y comma -111 -KPX Y colon -74 -KPX Y a -92 -KPX Y A -92 - -KPX f quoteright 55 -KPX f f -18 - -KPX one one -55 - -KPX quoteleft quoteleft -37 - -KPX quoteright quoteright -37 - -KPX r u -8 -KPX r quoteright 74 -KPX r q -18 -KPX r period -74 -KPX r o -18 -KPX r hyphen -18 -KPX r h -18 -KPX r g -18 -KPX r e -18 -KPX r d -18 -KPX r comma -74 -KPX r c -18 - -KPX space Y -18 -KPX space A -37 - -KPX v period -111 -KPX v comma -111 - -KPX w period -92 -KPX w comma -92 - -KPX y period -111 -KPX y comma -111 -EndKernPairs -EndKernData -StartComposites 58 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 229 231 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 223 231 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 223 231 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 215 231 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 223 231 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 223 231 ; -CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 188 0 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 139 231 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 139 231 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 139 231 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 139 231 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 2 231 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 2 231 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 2 231 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 2 231 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 249 231 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 227 231 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 227 231 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 227 231 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 227 231 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 227 243 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 96 231 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 255 231 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 247 231 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 223 231 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 223 231 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 203 231 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 191 231 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 179 231 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 84 20 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 72 20 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 72 20 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 60 20 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 72 20 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 72 12 ; -CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 56 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 97 20 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 85 20 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 73 20 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 73 20 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -23 20 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -23 20 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -23 20 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -23 20 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 113 12 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 107 20 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 107 20 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 107 20 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 95 20 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 107 12 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 46 8 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 159 20 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 135 20 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 135 20 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 111 20 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 144 20 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 112 20 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 84 8 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pplri8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pplri8a.afm deleted file mode 100644 index 01bdcf0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pplri8a.afm +++ /dev/null @@ -1,439 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Mon Jul 2 22:37:33 1990 -Comment UniqueID 31796 -Comment VMusage 37415 48307 -FontName Palatino-Italic -FullName Palatino Italic -FamilyName Palatino -Weight Medium -ItalicAngle -10 -IsFixedPitch false -FontBBox -170 -276 1010 918 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.005 -Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved.Palatino is a trademark of Linotype AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 692 -XHeight 482 -Ascender 733 -Descender -276 -StartCharMetrics 228 -C 32 ; WX 250 ; N space ; B 0 0 0 0 ; -C 33 ; WX 333 ; N exclam ; B 76 -8 292 733 ; -C 34 ; WX 500 ; N quotedbl ; B 140 508 455 733 ; -C 35 ; WX 500 ; N numbersign ; B 4 0 495 692 ; -C 36 ; WX 500 ; N dollar ; B 15 -113 452 733 ; -C 37 ; WX 889 ; N percent ; B 74 -7 809 710 ; -C 38 ; WX 778 ; N ampersand ; B 47 -18 766 692 ; -C 39 ; WX 278 ; N quoteright ; B 78 488 258 733 ; -C 40 ; WX 333 ; N parenleft ; B 54 -106 331 733 ; -C 41 ; WX 333 ; N parenright ; B 2 -106 279 733 ; -C 42 ; WX 389 ; N asterisk ; B 76 368 400 706 ; -C 43 ; WX 606 ; N plus ; B 51 0 555 504 ; -C 44 ; WX 250 ; N comma ; B 8 -143 203 123 ; -C 45 ; WX 333 ; N hyphen ; B 19 223 304 281 ; -C 46 ; WX 250 ; N period ; B 53 -5 158 112 ; -C 47 ; WX 296 ; N slash ; B -40 -119 392 733 ; -C 48 ; WX 500 ; N zero ; B 36 -11 480 699 ; -C 49 ; WX 500 ; N one ; B 54 -3 398 699 ; -C 50 ; WX 500 ; N two ; B 12 -3 437 699 ; -C 51 ; WX 500 ; N three ; B 22 -11 447 699 ; -C 52 ; WX 500 ; N four ; B 15 -3 478 699 ; -C 53 ; WX 500 ; N five ; B 14 -11 491 693 ; -C 54 ; WX 500 ; N six ; B 49 -11 469 699 ; -C 55 ; WX 500 ; N seven ; B 53 -3 502 692 ; -C 56 ; WX 500 ; N eight ; B 36 -11 469 699 ; -C 57 ; WX 500 ; N nine ; B 32 -11 468 699 ; -C 58 ; WX 250 ; N colon ; B 44 -5 207 458 ; -C 59 ; WX 250 ; N semicolon ; B -9 -146 219 456 ; -C 60 ; WX 606 ; N less ; B 53 -6 554 516 ; -C 61 ; WX 606 ; N equal ; B 51 126 555 378 ; -C 62 ; WX 606 ; N greater ; B 53 -6 554 516 ; -C 63 ; WX 500 ; N question ; B 114 -8 427 706 ; -C 64 ; WX 747 ; N at ; B 27 -18 718 706 ; -C 65 ; WX 722 ; N A ; B -19 -3 677 705 ; -C 66 ; WX 611 ; N B ; B 26 -6 559 692 ; -C 67 ; WX 667 ; N C ; B 45 -18 651 706 ; -C 68 ; WX 778 ; N D ; B 28 -3 741 692 ; -C 69 ; WX 611 ; N E ; B 30 -3 570 692 ; -C 70 ; WX 556 ; N F ; B 0 -3 548 692 ; -C 71 ; WX 722 ; N G ; B 50 -18 694 706 ; -C 72 ; WX 778 ; N H ; B -3 -3 800 692 ; -C 73 ; WX 333 ; N I ; B 7 -3 354 692 ; -C 74 ; WX 333 ; N J ; B -35 -206 358 692 ; -C 75 ; WX 667 ; N K ; B 13 -3 683 692 ; -C 76 ; WX 556 ; N L ; B 16 -3 523 692 ; -C 77 ; WX 944 ; N M ; B -19 -18 940 692 ; -C 78 ; WX 778 ; N N ; B 2 -11 804 692 ; -C 79 ; WX 778 ; N O ; B 53 -18 748 706 ; -C 80 ; WX 611 ; N P ; B 9 -3 594 692 ; -C 81 ; WX 778 ; N Q ; B 53 -201 748 706 ; -C 82 ; WX 667 ; N R ; B 9 -3 639 692 ; -C 83 ; WX 556 ; N S ; B 42 -18 506 706 ; -C 84 ; WX 611 ; N T ; B 53 -3 635 692 ; -C 85 ; WX 778 ; N U ; B 88 -18 798 692 ; -C 86 ; WX 722 ; N V ; B 75 -8 754 692 ; -C 87 ; WX 944 ; N W ; B 71 -8 980 700 ; -C 88 ; WX 722 ; N X ; B 20 -3 734 692 ; -C 89 ; WX 667 ; N Y ; B 52 -3 675 705 ; -C 90 ; WX 667 ; N Z ; B 20 -3 637 692 ; -C 91 ; WX 333 ; N bracketleft ; B 18 -100 326 733 ; -C 92 ; WX 606 ; N backslash ; B 81 0 513 733 ; -C 93 ; WX 333 ; N bracketright ; B 7 -100 315 733 ; -C 94 ; WX 606 ; N asciicircum ; B 51 283 554 689 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 278 ; N quoteleft ; B 78 488 258 733 ; -C 97 ; WX 444 ; N a ; B 4 -11 406 482 ; -C 98 ; WX 463 ; N b ; B 37 -11 433 733 ; -C 99 ; WX 407 ; N c ; B 25 -11 389 482 ; -C 100 ; WX 500 ; N d ; B 17 -11 483 733 ; -C 101 ; WX 389 ; N e ; B 15 -11 374 482 ; -C 102 ; WX 278 ; N f ; B -162 -276 413 733 ; L i fi ; L l fl ; -C 103 ; WX 500 ; N g ; B -37 -276 498 482 ; -C 104 ; WX 500 ; N h ; B 10 -9 471 733 ; -C 105 ; WX 278 ; N i ; B 34 -9 264 712 ; -C 106 ; WX 278 ; N j ; B -70 -276 265 712 ; -C 107 ; WX 444 ; N k ; B 8 -9 449 733 ; -C 108 ; WX 278 ; N l ; B 36 -9 251 733 ; -C 109 ; WX 778 ; N m ; B 24 -9 740 482 ; -C 110 ; WX 556 ; N n ; B 24 -9 514 482 ; -C 111 ; WX 444 ; N o ; B 17 -11 411 482 ; -C 112 ; WX 500 ; N p ; B -7 -276 465 482 ; -C 113 ; WX 463 ; N q ; B 24 -276 432 482 ; -C 114 ; WX 389 ; N r ; B 26 -9 384 482 ; -C 115 ; WX 389 ; N s ; B 9 -11 345 482 ; -C 116 ; WX 333 ; N t ; B 41 -9 310 646 ; -C 117 ; WX 556 ; N u ; B 32 -11 512 482 ; -C 118 ; WX 500 ; N v ; B 21 -11 477 482 ; -C 119 ; WX 722 ; N w ; B 21 -11 699 482 ; -C 120 ; WX 500 ; N x ; B 9 -11 484 482 ; -C 121 ; WX 500 ; N y ; B -8 -276 490 482 ; -C 122 ; WX 444 ; N z ; B -1 -11 416 482 ; -C 123 ; WX 333 ; N braceleft ; B 15 -100 319 733 ; -C 124 ; WX 606 ; N bar ; B 275 0 331 733 ; -C 125 ; WX 333 ; N braceright ; B 14 -100 318 733 ; -C 126 ; WX 606 ; N asciitilde ; B 51 168 555 339 ; -C 161 ; WX 333 ; N exclamdown ; B 15 -276 233 467 ; -C 162 ; WX 500 ; N cent ; B 56 -96 418 551 ; -C 163 ; WX 500 ; N sterling ; B 2 -18 479 708 ; -C 164 ; WX 167 ; N fraction ; B -170 0 337 699 ; -C 165 ; WX 500 ; N yen ; B 35 -3 512 699 ; -C 166 ; WX 500 ; N florin ; B 5 -276 470 708 ; -C 167 ; WX 500 ; N section ; B 14 -220 463 706 ; -C 168 ; WX 500 ; N currency ; B 14 115 486 577 ; -C 169 ; WX 333 ; N quotesingle ; B 140 508 288 733 ; -C 170 ; WX 500 ; N quotedblleft ; B 98 488 475 733 ; -C 171 ; WX 500 ; N guillemotleft ; B 57 70 437 440 ; -C 172 ; WX 333 ; N guilsinglleft ; B 57 70 270 440 ; -C 173 ; WX 333 ; N guilsinglright ; B 63 70 276 440 ; -C 174 ; WX 528 ; N fi ; B -162 -276 502 733 ; -C 175 ; WX 545 ; N fl ; B -162 -276 520 733 ; -C 177 ; WX 500 ; N endash ; B -10 228 510 278 ; -C 178 ; WX 500 ; N dagger ; B 48 0 469 692 ; -C 179 ; WX 500 ; N daggerdbl ; B 10 -162 494 692 ; -C 180 ; WX 250 ; N periodcentered ; B 53 195 158 312 ; -C 182 ; WX 500 ; N paragraph ; B 33 -224 611 692 ; -C 183 ; WX 500 ; N bullet ; B 86 182 430 526 ; -C 184 ; WX 278 ; N quotesinglbase ; B 27 -122 211 120 ; -C 185 ; WX 500 ; N quotedblbase ; B 43 -122 424 120 ; -C 186 ; WX 500 ; N quotedblright ; B 98 488 475 733 ; -C 187 ; WX 500 ; N guillemotright ; B 63 70 443 440 ; -C 188 ; WX 1000 ; N ellipsis ; B 102 -5 873 112 ; -C 189 ; WX 1000 ; N perthousand ; B 72 -6 929 717 ; -C 191 ; WX 500 ; N questiondown ; B 57 -246 370 467 ; -C 193 ; WX 333 ; N grave ; B 86 518 310 687 ; -C 194 ; WX 333 ; N acute ; B 122 518 346 687 ; -C 195 ; WX 333 ; N circumflex ; B 56 510 350 679 ; -C 196 ; WX 333 ; N tilde ; B 63 535 390 638 ; -C 197 ; WX 333 ; N macron ; B 74 538 386 589 ; -C 198 ; WX 333 ; N breve ; B 92 518 393 677 ; -C 199 ; WX 333 ; N dotaccent ; B 175 537 283 645 ; -C 200 ; WX 333 ; N dieresis ; B 78 537 378 637 ; -C 202 ; WX 333 ; N ring ; B 159 508 359 708 ; -C 203 ; WX 333 ; N cedilla ; B -9 -216 202 0 ; -C 205 ; WX 333 ; N hungarumlaut ; B 46 518 385 730 ; -C 206 ; WX 333 ; N ogonek ; B 38 -207 196 -18 ; -C 207 ; WX 333 ; N caron ; B 104 510 409 679 ; -C 208 ; WX 1000 ; N emdash ; B -10 228 1010 278 ; -C 225 ; WX 941 ; N AE ; B -4 -3 902 692 ; -C 227 ; WX 333 ; N ordfeminine ; B 60 404 321 699 ; -C 232 ; WX 556 ; N Lslash ; B -16 -3 523 692 ; -C 233 ; WX 778 ; N Oslash ; B 32 -39 762 721 ; -C 234 ; WX 1028 ; N OE ; B 56 -18 989 706 ; -C 235 ; WX 333 ; N ordmasculine ; B 66 404 322 699 ; -C 241 ; WX 638 ; N ae ; B 1 -11 623 482 ; -C 245 ; WX 278 ; N dotlessi ; B 34 -9 241 482 ; -C 248 ; WX 278 ; N lslash ; B -10 -9 302 733 ; -C 249 ; WX 444 ; N oslash ; B -18 -24 460 510 ; -C 250 ; WX 669 ; N oe ; B 17 -11 654 482 ; -C 251 ; WX 500 ; N germandbls ; B -160 -276 488 733 ; -C -1 ; WX 667 ; N Zcaron ; B 20 -3 637 907 ; -C -1 ; WX 407 ; N ccedilla ; B 25 -216 389 482 ; -C -1 ; WX 500 ; N ydieresis ; B -8 -276 490 657 ; -C -1 ; WX 444 ; N atilde ; B 4 -11 446 650 ; -C -1 ; WX 278 ; N icircumflex ; B 29 -9 323 699 ; -C -1 ; WX 300 ; N threesuperior ; B 28 273 304 699 ; -C -1 ; WX 389 ; N ecircumflex ; B 15 -11 398 699 ; -C -1 ; WX 500 ; N thorn ; B -39 -276 433 733 ; -C -1 ; WX 389 ; N egrave ; B 15 -11 374 707 ; -C -1 ; WX 300 ; N twosuperior ; B 13 278 290 699 ; -C -1 ; WX 389 ; N eacute ; B 15 -11 394 707 ; -C -1 ; WX 444 ; N otilde ; B 17 -11 446 650 ; -C -1 ; WX 722 ; N Aacute ; B -19 -3 677 897 ; -C -1 ; WX 444 ; N ocircumflex ; B 17 -11 411 699 ; -C -1 ; WX 500 ; N yacute ; B -8 -276 490 707 ; -C -1 ; WX 556 ; N udieresis ; B 32 -11 512 657 ; -C -1 ; WX 750 ; N threequarters ; B 35 -2 715 699 ; -C -1 ; WX 444 ; N acircumflex ; B 4 -11 406 699 ; -C -1 ; WX 778 ; N Eth ; B 19 -3 741 692 ; -C -1 ; WX 389 ; N edieresis ; B 15 -11 406 657 ; -C -1 ; WX 556 ; N ugrave ; B 32 -11 512 707 ; -C -1 ; WX 1000 ; N trademark ; B 52 285 951 689 ; -C -1 ; WX 444 ; N ograve ; B 17 -11 411 707 ; -C -1 ; WX 389 ; N scaron ; B 9 -11 419 687 ; -C -1 ; WX 333 ; N Idieresis ; B 7 -3 418 847 ; -C -1 ; WX 556 ; N uacute ; B 32 -11 512 707 ; -C -1 ; WX 444 ; N agrave ; B 4 -11 406 707 ; -C -1 ; WX 556 ; N ntilde ; B 24 -9 514 650 ; -C -1 ; WX 444 ; N aring ; B 4 -11 406 728 ; -C -1 ; WX 444 ; N zcaron ; B -1 -11 447 687 ; -C -1 ; WX 333 ; N Icircumflex ; B 7 -3 390 889 ; -C -1 ; WX 778 ; N Ntilde ; B 2 -11 804 866 ; -C -1 ; WX 556 ; N ucircumflex ; B 32 -11 512 699 ; -C -1 ; WX 611 ; N Ecircumflex ; B 30 -3 570 889 ; -C -1 ; WX 333 ; N Iacute ; B 7 -3 406 897 ; -C -1 ; WX 667 ; N Ccedilla ; B 45 -216 651 706 ; -C -1 ; WX 778 ; N Odieresis ; B 53 -18 748 847 ; -C -1 ; WX 556 ; N Scaron ; B 42 -18 539 907 ; -C -1 ; WX 611 ; N Edieresis ; B 30 -3 570 847 ; -C -1 ; WX 333 ; N Igrave ; B 7 -3 354 897 ; -C -1 ; WX 444 ; N adieresis ; B 4 -11 434 657 ; -C -1 ; WX 778 ; N Ograve ; B 53 -18 748 897 ; -C -1 ; WX 611 ; N Egrave ; B 30 -3 570 897 ; -C -1 ; WX 667 ; N Ydieresis ; B 52 -3 675 847 ; -C -1 ; WX 747 ; N registered ; B 11 -18 736 706 ; -C -1 ; WX 778 ; N Otilde ; B 53 -18 748 866 ; -C -1 ; WX 750 ; N onequarter ; B 31 -2 715 699 ; -C -1 ; WX 778 ; N Ugrave ; B 88 -18 798 897 ; -C -1 ; WX 778 ; N Ucircumflex ; B 88 -18 798 889 ; -C -1 ; WX 611 ; N Thorn ; B 9 -3 570 692 ; -C -1 ; WX 606 ; N divide ; B 51 0 555 504 ; -C -1 ; WX 722 ; N Atilde ; B -19 -3 677 866 ; -C -1 ; WX 778 ; N Uacute ; B 88 -18 798 897 ; -C -1 ; WX 778 ; N Ocircumflex ; B 53 -18 748 889 ; -C -1 ; WX 606 ; N logicalnot ; B 51 118 555 378 ; -C -1 ; WX 722 ; N Aring ; B -19 -3 677 918 ; -C -1 ; WX 278 ; N idieresis ; B 34 -9 351 657 ; -C -1 ; WX 278 ; N iacute ; B 34 -9 331 707 ; -C -1 ; WX 444 ; N aacute ; B 4 -11 414 707 ; -C -1 ; WX 606 ; N plusminus ; B 51 0 555 504 ; -C -1 ; WX 606 ; N multiply ; B 83 36 523 474 ; -C -1 ; WX 778 ; N Udieresis ; B 88 -18 798 847 ; -C -1 ; WX 606 ; N minus ; B 51 224 555 280 ; -C -1 ; WX 300 ; N onesuperior ; B 61 278 285 699 ; -C -1 ; WX 611 ; N Eacute ; B 30 -3 570 897 ; -C -1 ; WX 722 ; N Acircumflex ; B -19 -3 677 889 ; -C -1 ; WX 747 ; N copyright ; B 11 -18 736 706 ; -C -1 ; WX 722 ; N Agrave ; B -19 -3 677 897 ; -C -1 ; WX 444 ; N odieresis ; B 17 -11 434 657 ; -C -1 ; WX 444 ; N oacute ; B 17 -11 414 707 ; -C -1 ; WX 400 ; N degree ; B 90 389 390 689 ; -C -1 ; WX 278 ; N igrave ; B 34 -9 271 707 ; -C -1 ; WX 556 ; N mu ; B 15 -226 512 482 ; -C -1 ; WX 778 ; N Oacute ; B 53 -18 748 897 ; -C -1 ; WX 444 ; N eth ; B 17 -11 478 733 ; -C -1 ; WX 722 ; N Adieresis ; B -19 -3 677 847 ; -C -1 ; WX 667 ; N Yacute ; B 52 -3 675 897 ; -C -1 ; WX 606 ; N brokenbar ; B 275 0 331 733 ; -C -1 ; WX 750 ; N onehalf ; B 31 -2 721 699 ; -EndCharMetrics -StartKernData -StartKernPairs 106 - -KPX A y -55 -KPX A w -37 -KPX A v -37 -KPX A space -37 -KPX A quoteright -55 -KPX A Y -55 -KPX A W -55 -KPX A V -74 -KPX A T -55 - -KPX F period -111 -KPX F comma -111 -KPX F A -111 - -KPX L y -37 -KPX L space -18 -KPX L quoteright -37 -KPX L Y -74 -KPX L W -74 -KPX L V -74 -KPX L T -74 - -KPX P period -129 -KPX P comma -129 -KPX P A -129 - -KPX R y -37 -KPX R Y -55 -KPX R W -55 -KPX R V -74 -KPX R T -55 - -KPX T y -92 -KPX T w -92 -KPX T u -111 -KPX T semicolon -74 -KPX T s -111 -KPX T r -111 -KPX T period -74 -KPX T o -111 -KPX T i -55 -KPX T hyphen -55 -KPX T e -111 -KPX T comma -74 -KPX T colon -74 -KPX T c -111 -KPX T a -111 -KPX T O -18 -KPX T A -92 - -KPX V y -74 -KPX V u -74 -KPX V semicolon -37 -KPX V r -92 -KPX V period -129 -KPX V o -74 -KPX V i -74 -KPX V hyphen -55 -KPX V e -92 -KPX V comma -129 -KPX V colon -37 -KPX V a -74 -KPX V A -210 - -KPX W y -20 -KPX W u -20 -KPX W semicolon -18 -KPX W r -20 -KPX W period -55 -KPX W o -20 -KPX W i -20 -KPX W hyphen -18 -KPX W e -20 -KPX W comma -55 -KPX W colon -18 -KPX W a -20 -KPX W A -92 - -KPX Y v -74 -KPX Y u -92 -KPX Y semicolon -74 -KPX Y q -92 -KPX Y period -92 -KPX Y p -74 -KPX Y o -111 -KPX Y i -55 -KPX Y hyphen -74 -KPX Y e -111 -KPX Y comma -92 -KPX Y colon -74 -KPX Y a -92 -KPX Y A -92 - -KPX f quoteright 55 - -KPX one one -55 - -KPX quoteleft quoteleft -74 - -KPX quoteright t -37 -KPX quoteright space -55 -KPX quoteright s -55 -KPX quoteright quoteright -74 - -KPX r quoteright 37 -KPX r q -18 -KPX r period -74 -KPX r o -18 -KPX r h -18 -KPX r g -18 -KPX r e -18 -KPX r comma -74 -KPX r c -18 - -KPX v period -55 -KPX v comma -55 - -KPX w period -55 -KPX w comma -55 - -KPX y period -37 -KPX y comma -37 -EndKernPairs -EndKernData -StartComposites 58 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 271 210 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 261 210 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 255 210 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 235 210 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 235 210 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 255 228 ; -CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 207 0 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 199 210 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 179 210 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 179 210 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 167 210 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 60 210 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 40 210 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 40 210 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 28 210 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 263 228 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 283 210 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 263 210 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 255 210 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 251 210 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 263 228 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 130 228 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 277 210 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 255 210 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 235 210 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 235 210 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 227 210 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 187 210 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 179 228 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 68 20 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 56 20 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 56 20 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 44 20 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 36 20 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 56 12 ; -CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 37 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 48 20 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 48 20 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 28 20 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 16 20 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -15 20 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -27 20 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -27 20 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -39 20 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 112 12 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 68 20 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 56 20 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 56 20 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 36 20 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 56 12 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 10 8 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 124 20 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 112 20 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 112 20 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 100 20 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 96 20 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 84 20 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 38 8 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/psyr.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/psyr.afm deleted file mode 100644 index 1cdbdae..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/psyr.afm +++ /dev/null @@ -1,209 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All rights reserved. -Comment Creation Date: Wed Jan 17 21:48:26 1990 -Comment UniqueID 27004 -Comment VMusage 28489 37622 -FontName Symbol -FullName Symbol -FamilyName Symbol -Weight Medium -ItalicAngle 0 -IsFixedPitch false -FontBBox -180 -293 1090 1010 -UnderlinePosition -98 -UnderlineThickness 54 -Version 001.007 -Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All rights reserved. -EncodingScheme FontSpecific -StartCharMetrics 189 -C 32 ; WX 250 ; N space ; B 0 0 0 0 ; -C 33 ; WX 333 ; N exclam ; B 128 -17 240 672 ; -C 34 ; WX 713 ; N universal ; B 31 0 681 705 ; -C 35 ; WX 500 ; N numbersign ; B 20 -16 481 673 ; -C 36 ; WX 549 ; N existential ; B 25 0 478 707 ; -C 37 ; WX 833 ; N percent ; B 63 -36 771 655 ; -C 38 ; WX 778 ; N ampersand ; B 41 -18 750 661 ; -C 39 ; WX 439 ; N suchthat ; B 48 -17 414 500 ; -C 40 ; WX 333 ; N parenleft ; B 53 -191 300 673 ; -C 41 ; WX 333 ; N parenright ; B 30 -191 277 673 ; -C 42 ; WX 500 ; N asteriskmath ; B 65 134 427 551 ; -C 43 ; WX 549 ; N plus ; B 10 0 539 533 ; -C 44 ; WX 250 ; N comma ; B 56 -152 194 104 ; -C 45 ; WX 549 ; N minus ; B 11 233 535 288 ; -C 46 ; WX 250 ; N period ; B 69 -17 181 95 ; -C 47 ; WX 278 ; N slash ; B 0 -18 254 646 ; -C 48 ; WX 500 ; N zero ; B 23 -17 471 685 ; -C 49 ; WX 500 ; N one ; B 117 0 390 673 ; -C 50 ; WX 500 ; N two ; B 25 0 475 686 ; -C 51 ; WX 500 ; N three ; B 39 -17 435 685 ; -C 52 ; WX 500 ; N four ; B 16 0 469 685 ; -C 53 ; WX 500 ; N five ; B 29 -17 443 685 ; -C 54 ; WX 500 ; N six ; B 36 -17 467 685 ; -C 55 ; WX 500 ; N seven ; B 24 -16 448 673 ; -C 56 ; WX 500 ; N eight ; B 54 -18 440 685 ; -C 57 ; WX 500 ; N nine ; B 31 -18 460 685 ; -C 58 ; WX 278 ; N colon ; B 81 -17 193 460 ; -C 59 ; WX 278 ; N semicolon ; B 83 -152 221 460 ; -C 60 ; WX 549 ; N less ; B 26 0 523 522 ; -C 61 ; WX 549 ; N equal ; B 11 141 537 390 ; -C 62 ; WX 549 ; N greater ; B 26 0 523 522 ; -C 63 ; WX 444 ; N question ; B 70 -17 412 686 ; -C 64 ; WX 549 ; N congruent ; B 11 0 537 475 ; -C 65 ; WX 722 ; N Alpha ; B 4 0 684 673 ; -C 66 ; WX 667 ; N Beta ; B 29 0 592 673 ; -C 67 ; WX 722 ; N Chi ; B -9 0 704 673 ; -C 68 ; WX 612 ; N Delta ; B 6 0 608 688 ; -C 69 ; WX 611 ; N Epsilon ; B 32 0 617 673 ; -C 70 ; WX 763 ; N Phi ; B 26 0 741 673 ; -C 71 ; WX 603 ; N Gamma ; B 24 0 609 673 ; -C 72 ; WX 722 ; N Eta ; B 39 0 729 673 ; -C 73 ; WX 333 ; N Iota ; B 32 0 316 673 ; -C 74 ; WX 631 ; N theta1 ; B 18 -18 623 689 ; -C 75 ; WX 722 ; N Kappa ; B 35 0 722 673 ; -C 76 ; WX 686 ; N Lambda ; B 6 0 680 688 ; -C 77 ; WX 889 ; N Mu ; B 28 0 887 673 ; -C 78 ; WX 722 ; N Nu ; B 29 -8 720 673 ; -C 79 ; WX 722 ; N Omicron ; B 41 -17 715 685 ; -C 80 ; WX 768 ; N Pi ; B 25 0 745 673 ; -C 81 ; WX 741 ; N Theta ; B 41 -17 715 685 ; -C 82 ; WX 556 ; N Rho ; B 28 0 563 673 ; -C 83 ; WX 592 ; N Sigma ; B 5 0 589 673 ; -C 84 ; WX 611 ; N Tau ; B 33 0 607 673 ; -C 85 ; WX 690 ; N Upsilon ; B -8 0 694 673 ; -C 86 ; WX 439 ; N sigma1 ; B 40 -233 436 500 ; -C 87 ; WX 768 ; N Omega ; B 34 0 736 688 ; -C 88 ; WX 645 ; N Xi ; B 40 0 599 673 ; -C 89 ; WX 795 ; N Psi ; B 15 0 781 684 ; -C 90 ; WX 611 ; N Zeta ; B 44 0 636 673 ; -C 91 ; WX 333 ; N bracketleft ; B 86 -155 299 674 ; -C 92 ; WX 863 ; N therefore ; B 163 0 701 478 ; -C 93 ; WX 333 ; N bracketright ; B 33 -155 246 674 ; -C 94 ; WX 658 ; N perpendicular ; B 15 0 652 674 ; -C 95 ; WX 500 ; N underscore ; B -2 -252 502 -206 ; -C 96 ; WX 500 ; N radicalex ; B 480 881 1090 917 ; -C 97 ; WX 631 ; N alpha ; B 41 -18 622 500 ; -C 98 ; WX 549 ; N beta ; B 61 -223 515 741 ; -C 99 ; WX 549 ; N chi ; B 12 -231 522 499 ; -C 100 ; WX 494 ; N delta ; B 40 -19 481 740 ; -C 101 ; WX 439 ; N epsilon ; B 22 -19 427 502 ; -C 102 ; WX 521 ; N phi ; B 27 -224 490 671 ; -C 103 ; WX 411 ; N gamma ; B 5 -225 484 499 ; -C 104 ; WX 603 ; N eta ; B 0 -202 527 514 ; -C 105 ; WX 329 ; N iota ; B 0 -17 301 503 ; -C 106 ; WX 603 ; N phi1 ; B 36 -224 587 499 ; -C 107 ; WX 549 ; N kappa ; B 33 0 558 501 ; -C 108 ; WX 549 ; N lambda ; B 24 -17 548 739 ; -C 109 ; WX 576 ; N mu ; B 33 -223 567 500 ; -C 110 ; WX 521 ; N nu ; B -9 -16 475 507 ; -C 111 ; WX 549 ; N omicron ; B 35 -19 501 499 ; -C 112 ; WX 549 ; N pi ; B 10 -19 530 487 ; -C 113 ; WX 521 ; N theta ; B 43 -17 485 690 ; -C 114 ; WX 549 ; N rho ; B 50 -230 490 499 ; -C 115 ; WX 603 ; N sigma ; B 30 -21 588 500 ; -C 116 ; WX 439 ; N tau ; B 10 -19 418 500 ; -C 117 ; WX 576 ; N upsilon ; B 7 -18 535 507 ; -C 118 ; WX 713 ; N omega1 ; B 12 -18 671 583 ; -C 119 ; WX 686 ; N omega ; B 42 -17 684 500 ; -C 120 ; WX 493 ; N xi ; B 27 -224 469 766 ; -C 121 ; WX 686 ; N psi ; B 12 -228 701 500 ; -C 122 ; WX 494 ; N zeta ; B 60 -225 467 756 ; -C 123 ; WX 480 ; N braceleft ; B 58 -183 397 673 ; -C 124 ; WX 200 ; N bar ; B 65 -177 135 673 ; -C 125 ; WX 480 ; N braceright ; B 79 -183 418 673 ; -C 126 ; WX 549 ; N similar ; B 17 203 529 307 ; -C 161 ; WX 620 ; N Upsilon1 ; B -2 0 610 685 ; -C 162 ; WX 247 ; N minute ; B 27 459 228 735 ; -C 163 ; WX 549 ; N lessequal ; B 29 0 526 639 ; -C 164 ; WX 167 ; N fraction ; B -180 -12 340 677 ; -C 165 ; WX 713 ; N infinity ; B 26 124 688 404 ; -C 166 ; WX 500 ; N florin ; B 2 -193 494 686 ; -C 167 ; WX 753 ; N club ; B 86 -26 660 533 ; -C 168 ; WX 753 ; N diamond ; B 142 -36 600 550 ; -C 169 ; WX 753 ; N heart ; B 117 -33 631 532 ; -C 170 ; WX 753 ; N spade ; B 113 -36 629 548 ; -C 171 ; WX 1042 ; N arrowboth ; B 24 -15 1024 511 ; -C 172 ; WX 987 ; N arrowleft ; B 32 -15 942 511 ; -C 173 ; WX 603 ; N arrowup ; B 45 0 571 910 ; -C 174 ; WX 987 ; N arrowright ; B 49 -15 959 511 ; -C 175 ; WX 603 ; N arrowdown ; B 45 -22 571 888 ; -C 176 ; WX 400 ; N degree ; B 50 385 350 685 ; -C 177 ; WX 549 ; N plusminus ; B 10 0 539 645 ; -C 178 ; WX 411 ; N second ; B 20 459 413 737 ; -C 179 ; WX 549 ; N greaterequal ; B 29 0 526 639 ; -C 180 ; WX 549 ; N multiply ; B 17 8 533 524 ; -C 181 ; WX 713 ; N proportional ; B 27 123 639 404 ; -C 182 ; WX 494 ; N partialdiff ; B 26 -20 462 746 ; -C 183 ; WX 460 ; N bullet ; B 50 113 410 473 ; -C 184 ; WX 549 ; N divide ; B 10 71 536 456 ; -C 185 ; WX 549 ; N notequal ; B 15 -25 540 549 ; -C 186 ; WX 549 ; N equivalence ; B 14 82 538 443 ; -C 187 ; WX 549 ; N approxequal ; B 14 135 527 394 ; -C 188 ; WX 1000 ; N ellipsis ; B 111 -17 889 95 ; -C 189 ; WX 603 ; N arrowvertex ; B 280 -120 336 1010 ; -C 190 ; WX 1000 ; N arrowhorizex ; B -60 220 1050 276 ; -C 191 ; WX 658 ; N carriagereturn ; B 15 -16 602 629 ; -C 192 ; WX 823 ; N aleph ; B 175 -18 661 658 ; -C 193 ; WX 686 ; N Ifraktur ; B 10 -53 578 740 ; -C 194 ; WX 795 ; N Rfraktur ; B 26 -15 759 734 ; -C 195 ; WX 987 ; N weierstrass ; B 159 -211 870 573 ; -C 196 ; WX 768 ; N circlemultiply ; B 43 -17 733 673 ; -C 197 ; WX 768 ; N circleplus ; B 43 -15 733 675 ; -C 198 ; WX 823 ; N emptyset ; B 39 -24 781 719 ; -C 199 ; WX 768 ; N intersection ; B 40 0 732 509 ; -C 200 ; WX 768 ; N union ; B 40 -17 732 492 ; -C 201 ; WX 713 ; N propersuperset ; B 20 0 673 470 ; -C 202 ; WX 713 ; N reflexsuperset ; B 20 -125 673 470 ; -C 203 ; WX 713 ; N notsubset ; B 36 -70 690 540 ; -C 204 ; WX 713 ; N propersubset ; B 37 0 690 470 ; -C 205 ; WX 713 ; N reflexsubset ; B 37 -125 690 470 ; -C 206 ; WX 713 ; N element ; B 45 0 505 468 ; -C 207 ; WX 713 ; N notelement ; B 45 -58 505 555 ; -C 208 ; WX 768 ; N angle ; B 26 0 738 673 ; -C 209 ; WX 713 ; N gradient ; B 36 -19 681 718 ; -C 210 ; WX 790 ; N registerserif ; B 50 -17 740 673 ; -C 211 ; WX 790 ; N copyrightserif ; B 51 -15 741 675 ; -C 212 ; WX 890 ; N trademarkserif ; B 18 293 855 673 ; -C 213 ; WX 823 ; N product ; B 25 -101 803 751 ; -C 214 ; WX 549 ; N radical ; B 10 -38 515 917 ; -C 215 ; WX 250 ; N dotmath ; B 69 210 169 310 ; -C 216 ; WX 713 ; N logicalnot ; B 15 0 680 288 ; -C 217 ; WX 603 ; N logicaland ; B 23 0 583 454 ; -C 218 ; WX 603 ; N logicalor ; B 30 0 578 477 ; -C 219 ; WX 1042 ; N arrowdblboth ; B 27 -20 1023 510 ; -C 220 ; WX 987 ; N arrowdblleft ; B 30 -15 939 513 ; -C 221 ; WX 603 ; N arrowdblup ; B 39 2 567 911 ; -C 222 ; WX 987 ; N arrowdblright ; B 45 -20 954 508 ; -C 223 ; WX 603 ; N arrowdbldown ; B 44 -19 572 890 ; -C 224 ; WX 494 ; N lozenge ; B 18 0 466 745 ; -C 225 ; WX 329 ; N angleleft ; B 25 -198 306 746 ; -C 226 ; WX 790 ; N registersans ; B 50 -20 740 670 ; -C 227 ; WX 790 ; N copyrightsans ; B 49 -15 739 675 ; -C 228 ; WX 786 ; N trademarksans ; B 5 293 725 673 ; -C 229 ; WX 713 ; N summation ; B 14 -108 695 752 ; -C 230 ; WX 384 ; N parenlefttp ; B 40 -293 436 926 ; -C 231 ; WX 384 ; N parenleftex ; B 40 -85 92 925 ; -C 232 ; WX 384 ; N parenleftbt ; B 40 -293 436 926 ; -C 233 ; WX 384 ; N bracketlefttp ; B 0 -80 341 926 ; -C 234 ; WX 384 ; N bracketleftex ; B 0 -79 55 925 ; -C 235 ; WX 384 ; N bracketleftbt ; B 0 -80 340 926 ; -C 236 ; WX 494 ; N bracelefttp ; B 201 -75 439 926 ; -C 237 ; WX 494 ; N braceleftmid ; B 14 -85 255 935 ; -C 238 ; WX 494 ; N braceleftbt ; B 201 -70 439 926 ; -C 239 ; WX 494 ; N braceex ; B 201 -80 255 935 ; -C 241 ; WX 329 ; N angleright ; B 21 -198 302 746 ; -C 242 ; WX 274 ; N integral ; B 2 -107 291 916 ; -C 243 ; WX 686 ; N integraltp ; B 332 -83 715 921 ; -C 244 ; WX 686 ; N integralex ; B 332 -88 415 975 ; -C 245 ; WX 686 ; N integralbt ; B 39 -81 415 921 ; -C 246 ; WX 384 ; N parenrighttp ; B 54 -293 450 926 ; -C 247 ; WX 384 ; N parenrightex ; B 398 -85 450 925 ; -C 248 ; WX 384 ; N parenrightbt ; B 54 -293 450 926 ; -C 249 ; WX 384 ; N bracketrighttp ; B 22 -80 360 926 ; -C 250 ; WX 384 ; N bracketrightex ; B 305 -79 360 925 ; -C 251 ; WX 384 ; N bracketrightbt ; B 20 -80 360 926 ; -C 252 ; WX 494 ; N bracerighttp ; B 17 -75 255 926 ; -C 253 ; WX 494 ; N bracerightmid ; B 201 -85 442 935 ; -C 254 ; WX 494 ; N bracerightbt ; B 17 -70 255 926 ; -C -1 ; WX 790 ; N apple ; B 56 -3 733 808 ; -EndCharMetrics -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/ptmb8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/ptmb8a.afm deleted file mode 100644 index 55207f9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/ptmb8a.afm +++ /dev/null @@ -1,648 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Tue Mar 20 12:17:14 1990 -Comment UniqueID 28417 -Comment VMusage 30458 37350 -FontName Times-Bold -FullName Times Bold -FamilyName Times -Weight Bold -ItalicAngle 0 -IsFixedPitch false -FontBBox -168 -218 1000 935 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.007 -Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved.Times is a trademark of Linotype AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 676 -XHeight 461 -Ascender 676 -Descender -205 -StartCharMetrics 228 -C 32 ; WX 250 ; N space ; B 0 0 0 0 ; -C 33 ; WX 333 ; N exclam ; B 81 -13 251 691 ; -C 34 ; WX 555 ; N quotedbl ; B 83 404 472 691 ; -C 35 ; WX 500 ; N numbersign ; B 4 0 496 700 ; -C 36 ; WX 500 ; N dollar ; B 29 -99 472 750 ; -C 37 ; WX 1000 ; N percent ; B 124 -14 877 692 ; -C 38 ; WX 833 ; N ampersand ; B 62 -16 787 691 ; -C 39 ; WX 333 ; N quoteright ; B 79 356 263 691 ; -C 40 ; WX 333 ; N parenleft ; B 46 -168 306 694 ; -C 41 ; WX 333 ; N parenright ; B 27 -168 287 694 ; -C 42 ; WX 500 ; N asterisk ; B 56 255 447 691 ; -C 43 ; WX 570 ; N plus ; B 33 0 537 506 ; -C 44 ; WX 250 ; N comma ; B 39 -180 223 155 ; -C 45 ; WX 333 ; N hyphen ; B 44 171 287 287 ; -C 46 ; WX 250 ; N period ; B 41 -13 210 156 ; -C 47 ; WX 278 ; N slash ; B -24 -19 302 691 ; -C 48 ; WX 500 ; N zero ; B 24 -13 476 688 ; -C 49 ; WX 500 ; N one ; B 65 0 442 688 ; -C 50 ; WX 500 ; N two ; B 17 0 478 688 ; -C 51 ; WX 500 ; N three ; B 16 -14 468 688 ; -C 52 ; WX 500 ; N four ; B 19 0 475 688 ; -C 53 ; WX 500 ; N five ; B 22 -8 470 676 ; -C 54 ; WX 500 ; N six ; B 28 -13 475 688 ; -C 55 ; WX 500 ; N seven ; B 17 0 477 676 ; -C 56 ; WX 500 ; N eight ; B 28 -13 472 688 ; -C 57 ; WX 500 ; N nine ; B 26 -13 473 688 ; -C 58 ; WX 333 ; N colon ; B 82 -13 251 472 ; -C 59 ; WX 333 ; N semicolon ; B 82 -180 266 472 ; -C 60 ; WX 570 ; N less ; B 31 -8 539 514 ; -C 61 ; WX 570 ; N equal ; B 33 107 537 399 ; -C 62 ; WX 570 ; N greater ; B 31 -8 539 514 ; -C 63 ; WX 500 ; N question ; B 57 -13 445 689 ; -C 64 ; WX 930 ; N at ; B 108 -19 822 691 ; -C 65 ; WX 722 ; N A ; B 9 0 689 690 ; -C 66 ; WX 667 ; N B ; B 16 0 619 676 ; -C 67 ; WX 722 ; N C ; B 49 -19 687 691 ; -C 68 ; WX 722 ; N D ; B 14 0 690 676 ; -C 69 ; WX 667 ; N E ; B 16 0 641 676 ; -C 70 ; WX 611 ; N F ; B 16 0 583 676 ; -C 71 ; WX 778 ; N G ; B 37 -19 755 691 ; -C 72 ; WX 778 ; N H ; B 21 0 759 676 ; -C 73 ; WX 389 ; N I ; B 20 0 370 676 ; -C 74 ; WX 500 ; N J ; B 3 -96 479 676 ; -C 75 ; WX 778 ; N K ; B 30 0 769 676 ; -C 76 ; WX 667 ; N L ; B 19 0 638 676 ; -C 77 ; WX 944 ; N M ; B 14 0 921 676 ; -C 78 ; WX 722 ; N N ; B 16 -18 701 676 ; -C 79 ; WX 778 ; N O ; B 35 -19 743 691 ; -C 80 ; WX 611 ; N P ; B 16 0 600 676 ; -C 81 ; WX 778 ; N Q ; B 35 -176 743 691 ; -C 82 ; WX 722 ; N R ; B 26 0 715 676 ; -C 83 ; WX 556 ; N S ; B 35 -19 513 692 ; -C 84 ; WX 667 ; N T ; B 31 0 636 676 ; -C 85 ; WX 722 ; N U ; B 16 -19 701 676 ; -C 86 ; WX 722 ; N V ; B 16 -18 701 676 ; -C 87 ; WX 1000 ; N W ; B 19 -15 981 676 ; -C 88 ; WX 722 ; N X ; B 16 0 699 676 ; -C 89 ; WX 722 ; N Y ; B 15 0 699 676 ; -C 90 ; WX 667 ; N Z ; B 28 0 634 676 ; -C 91 ; WX 333 ; N bracketleft ; B 67 -149 301 678 ; -C 92 ; WX 278 ; N backslash ; B -25 -19 303 691 ; -C 93 ; WX 333 ; N bracketright ; B 32 -149 266 678 ; -C 94 ; WX 581 ; N asciicircum ; B 73 311 509 676 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 333 ; N quoteleft ; B 70 356 254 691 ; -C 97 ; WX 500 ; N a ; B 25 -14 488 473 ; -C 98 ; WX 556 ; N b ; B 17 -14 521 676 ; -C 99 ; WX 444 ; N c ; B 25 -14 430 473 ; -C 100 ; WX 556 ; N d ; B 25 -14 534 676 ; -C 101 ; WX 444 ; N e ; B 25 -14 426 473 ; -C 102 ; WX 333 ; N f ; B 14 0 389 691 ; L i fi ; L l fl ; -C 103 ; WX 500 ; N g ; B 28 -206 483 473 ; -C 104 ; WX 556 ; N h ; B 16 0 534 676 ; -C 105 ; WX 278 ; N i ; B 16 0 255 691 ; -C 106 ; WX 333 ; N j ; B -57 -203 263 691 ; -C 107 ; WX 556 ; N k ; B 22 0 543 676 ; -C 108 ; WX 278 ; N l ; B 16 0 255 676 ; -C 109 ; WX 833 ; N m ; B 16 0 814 473 ; -C 110 ; WX 556 ; N n ; B 21 0 539 473 ; -C 111 ; WX 500 ; N o ; B 25 -14 476 473 ; -C 112 ; WX 556 ; N p ; B 19 -205 524 473 ; -C 113 ; WX 556 ; N q ; B 34 -205 536 473 ; -C 114 ; WX 444 ; N r ; B 29 0 434 473 ; -C 115 ; WX 389 ; N s ; B 25 -14 361 473 ; -C 116 ; WX 333 ; N t ; B 20 -12 332 630 ; -C 117 ; WX 556 ; N u ; B 16 -14 537 461 ; -C 118 ; WX 500 ; N v ; B 21 -14 485 461 ; -C 119 ; WX 722 ; N w ; B 23 -14 707 461 ; -C 120 ; WX 500 ; N x ; B 12 0 484 461 ; -C 121 ; WX 500 ; N y ; B 16 -205 480 461 ; -C 122 ; WX 444 ; N z ; B 21 0 420 461 ; -C 123 ; WX 394 ; N braceleft ; B 22 -175 340 698 ; -C 124 ; WX 220 ; N bar ; B 66 -19 154 691 ; -C 125 ; WX 394 ; N braceright ; B 54 -175 372 698 ; -C 126 ; WX 520 ; N asciitilde ; B 29 173 491 333 ; -C 161 ; WX 333 ; N exclamdown ; B 82 -203 252 501 ; -C 162 ; WX 500 ; N cent ; B 53 -140 458 588 ; -C 163 ; WX 500 ; N sterling ; B 21 -14 477 684 ; -C 164 ; WX 167 ; N fraction ; B -168 -12 329 688 ; -C 165 ; WX 500 ; N yen ; B -64 0 547 676 ; -C 166 ; WX 500 ; N florin ; B 0 -155 498 706 ; -C 167 ; WX 500 ; N section ; B 57 -132 443 691 ; -C 168 ; WX 500 ; N currency ; B -26 61 526 613 ; -C 169 ; WX 278 ; N quotesingle ; B 75 404 204 691 ; -C 170 ; WX 500 ; N quotedblleft ; B 32 356 486 691 ; -C 171 ; WX 500 ; N guillemotleft ; B 23 36 473 415 ; -C 172 ; WX 333 ; N guilsinglleft ; B 51 36 305 415 ; -C 173 ; WX 333 ; N guilsinglright ; B 28 36 282 415 ; -C 174 ; WX 556 ; N fi ; B 14 0 536 691 ; -C 175 ; WX 556 ; N fl ; B 14 0 536 691 ; -C 177 ; WX 500 ; N endash ; B 0 181 500 271 ; -C 178 ; WX 500 ; N dagger ; B 47 -134 453 691 ; -C 179 ; WX 500 ; N daggerdbl ; B 45 -132 456 691 ; -C 180 ; WX 250 ; N periodcentered ; B 41 248 210 417 ; -C 182 ; WX 540 ; N paragraph ; B 0 -186 519 676 ; -C 183 ; WX 350 ; N bullet ; B 35 198 315 478 ; -C 184 ; WX 333 ; N quotesinglbase ; B 79 -180 263 155 ; -C 185 ; WX 500 ; N quotedblbase ; B 14 -180 468 155 ; -C 186 ; WX 500 ; N quotedblright ; B 14 356 468 691 ; -C 187 ; WX 500 ; N guillemotright ; B 27 36 477 415 ; -C 188 ; WX 1000 ; N ellipsis ; B 82 -13 917 156 ; -C 189 ; WX 1000 ; N perthousand ; B 7 -29 995 706 ; -C 191 ; WX 500 ; N questiondown ; B 55 -201 443 501 ; -C 193 ; WX 333 ; N grave ; B 8 528 246 713 ; -C 194 ; WX 333 ; N acute ; B 86 528 324 713 ; -C 195 ; WX 333 ; N circumflex ; B -2 528 335 704 ; -C 196 ; WX 333 ; N tilde ; B -16 547 349 674 ; -C 197 ; WX 333 ; N macron ; B 1 565 331 637 ; -C 198 ; WX 333 ; N breve ; B 15 528 318 691 ; -C 199 ; WX 333 ; N dotaccent ; B 103 537 230 667 ; -C 200 ; WX 333 ; N dieresis ; B -2 537 335 667 ; -C 202 ; WX 333 ; N ring ; B 60 527 273 740 ; -C 203 ; WX 333 ; N cedilla ; B 68 -218 294 0 ; -C 205 ; WX 333 ; N hungarumlaut ; B -13 528 425 713 ; -C 206 ; WX 333 ; N ogonek ; B 90 -173 319 44 ; -C 207 ; WX 333 ; N caron ; B -2 528 335 704 ; -C 208 ; WX 1000 ; N emdash ; B 0 181 1000 271 ; -C 225 ; WX 1000 ; N AE ; B 4 0 951 676 ; -C 227 ; WX 300 ; N ordfeminine ; B -1 397 301 688 ; -C 232 ; WX 667 ; N Lslash ; B 19 0 638 676 ; -C 233 ; WX 778 ; N Oslash ; B 35 -74 743 737 ; -C 234 ; WX 1000 ; N OE ; B 22 -5 981 684 ; -C 235 ; WX 330 ; N ordmasculine ; B 18 397 312 688 ; -C 241 ; WX 722 ; N ae ; B 33 -14 693 473 ; -C 245 ; WX 278 ; N dotlessi ; B 16 0 255 461 ; -C 248 ; WX 278 ; N lslash ; B -22 0 303 676 ; -C 249 ; WX 500 ; N oslash ; B 25 -92 476 549 ; -C 250 ; WX 722 ; N oe ; B 22 -14 696 473 ; -C 251 ; WX 556 ; N germandbls ; B 19 -12 517 691 ; -C -1 ; WX 667 ; N Zcaron ; B 28 0 634 914 ; -C -1 ; WX 444 ; N ccedilla ; B 25 -218 430 473 ; -C -1 ; WX 500 ; N ydieresis ; B 16 -205 480 667 ; -C -1 ; WX 500 ; N atilde ; B 25 -14 488 674 ; -C -1 ; WX 278 ; N icircumflex ; B -36 0 301 704 ; -C -1 ; WX 300 ; N threesuperior ; B 3 268 297 688 ; -C -1 ; WX 444 ; N ecircumflex ; B 25 -14 426 704 ; -C -1 ; WX 556 ; N thorn ; B 19 -205 524 676 ; -C -1 ; WX 444 ; N egrave ; B 25 -14 426 713 ; -C -1 ; WX 300 ; N twosuperior ; B 0 275 300 688 ; -C -1 ; WX 444 ; N eacute ; B 25 -14 426 713 ; -C -1 ; WX 500 ; N otilde ; B 25 -14 476 674 ; -C -1 ; WX 722 ; N Aacute ; B 9 0 689 923 ; -C -1 ; WX 500 ; N ocircumflex ; B 25 -14 476 704 ; -C -1 ; WX 500 ; N yacute ; B 16 -205 480 713 ; -C -1 ; WX 556 ; N udieresis ; B 16 -14 537 667 ; -C -1 ; WX 750 ; N threequarters ; B 23 -12 733 688 ; -C -1 ; WX 500 ; N acircumflex ; B 25 -14 488 704 ; -C -1 ; WX 722 ; N Eth ; B 6 0 690 676 ; -C -1 ; WX 444 ; N edieresis ; B 25 -14 426 667 ; -C -1 ; WX 556 ; N ugrave ; B 16 -14 537 713 ; -C -1 ; WX 1000 ; N trademark ; B 24 271 977 676 ; -C -1 ; WX 500 ; N ograve ; B 25 -14 476 713 ; -C -1 ; WX 389 ; N scaron ; B 25 -14 363 704 ; -C -1 ; WX 389 ; N Idieresis ; B 20 0 370 877 ; -C -1 ; WX 556 ; N uacute ; B 16 -14 537 713 ; -C -1 ; WX 500 ; N agrave ; B 25 -14 488 713 ; -C -1 ; WX 556 ; N ntilde ; B 21 0 539 674 ; -C -1 ; WX 500 ; N aring ; B 25 -14 488 740 ; -C -1 ; WX 444 ; N zcaron ; B 21 0 420 704 ; -C -1 ; WX 389 ; N Icircumflex ; B 20 0 370 914 ; -C -1 ; WX 722 ; N Ntilde ; B 16 -18 701 884 ; -C -1 ; WX 556 ; N ucircumflex ; B 16 -14 537 704 ; -C -1 ; WX 667 ; N Ecircumflex ; B 16 0 641 914 ; -C -1 ; WX 389 ; N Iacute ; B 20 0 370 923 ; -C -1 ; WX 722 ; N Ccedilla ; B 49 -218 687 691 ; -C -1 ; WX 778 ; N Odieresis ; B 35 -19 743 877 ; -C -1 ; WX 556 ; N Scaron ; B 35 -19 513 914 ; -C -1 ; WX 667 ; N Edieresis ; B 16 0 641 877 ; -C -1 ; WX 389 ; N Igrave ; B 20 0 370 923 ; -C -1 ; WX 500 ; N adieresis ; B 25 -14 488 667 ; -C -1 ; WX 778 ; N Ograve ; B 35 -19 743 923 ; -C -1 ; WX 667 ; N Egrave ; B 16 0 641 923 ; -C -1 ; WX 722 ; N Ydieresis ; B 15 0 699 877 ; -C -1 ; WX 747 ; N registered ; B 26 -19 721 691 ; -C -1 ; WX 778 ; N Otilde ; B 35 -19 743 884 ; -C -1 ; WX 750 ; N onequarter ; B 28 -12 743 688 ; -C -1 ; WX 722 ; N Ugrave ; B 16 -19 701 923 ; -C -1 ; WX 722 ; N Ucircumflex ; B 16 -19 701 914 ; -C -1 ; WX 611 ; N Thorn ; B 16 0 600 676 ; -C -1 ; WX 570 ; N divide ; B 33 -31 537 537 ; -C -1 ; WX 722 ; N Atilde ; B 9 0 689 884 ; -C -1 ; WX 722 ; N Uacute ; B 16 -19 701 923 ; -C -1 ; WX 778 ; N Ocircumflex ; B 35 -19 743 914 ; -C -1 ; WX 570 ; N logicalnot ; B 33 108 537 399 ; -C -1 ; WX 722 ; N Aring ; B 9 0 689 935 ; -C -1 ; WX 278 ; N idieresis ; B -36 0 301 667 ; -C -1 ; WX 278 ; N iacute ; B 16 0 290 713 ; -C -1 ; WX 500 ; N aacute ; B 25 -14 488 713 ; -C -1 ; WX 570 ; N plusminus ; B 33 0 537 506 ; -C -1 ; WX 570 ; N multiply ; B 48 16 522 490 ; -C -1 ; WX 722 ; N Udieresis ; B 16 -19 701 877 ; -C -1 ; WX 570 ; N minus ; B 33 209 537 297 ; -C -1 ; WX 300 ; N onesuperior ; B 28 275 273 688 ; -C -1 ; WX 667 ; N Eacute ; B 16 0 641 923 ; -C -1 ; WX 722 ; N Acircumflex ; B 9 0 689 914 ; -C -1 ; WX 747 ; N copyright ; B 26 -19 721 691 ; -C -1 ; WX 722 ; N Agrave ; B 9 0 689 923 ; -C -1 ; WX 500 ; N odieresis ; B 25 -14 476 667 ; -C -1 ; WX 500 ; N oacute ; B 25 -14 476 713 ; -C -1 ; WX 400 ; N degree ; B 57 402 343 688 ; -C -1 ; WX 278 ; N igrave ; B -26 0 255 713 ; -C -1 ; WX 556 ; N mu ; B 33 -206 536 461 ; -C -1 ; WX 778 ; N Oacute ; B 35 -19 743 923 ; -C -1 ; WX 500 ; N eth ; B 25 -14 476 691 ; -C -1 ; WX 722 ; N Adieresis ; B 9 0 689 877 ; -C -1 ; WX 722 ; N Yacute ; B 15 0 699 928 ; -C -1 ; WX 220 ; N brokenbar ; B 66 -19 154 691 ; -C -1 ; WX 750 ; N onehalf ; B -7 -12 775 688 ; -EndCharMetrics -StartKernData -StartKernPairs 283 - -KPX A y -74 -KPX A w -90 -KPX A v -100 -KPX A u -50 -KPX A quoteright -74 -KPX A quotedblright 0 -KPX A p -25 -KPX A Y -100 -KPX A W -130 -KPX A V -145 -KPX A U -50 -KPX A T -95 -KPX A Q -45 -KPX A O -45 -KPX A G -55 -KPX A C -55 - -KPX B period 0 -KPX B comma 0 -KPX B U -10 -KPX B A -30 - -KPX D period -20 -KPX D comma 0 -KPX D Y -40 -KPX D W -40 -KPX D V -40 -KPX D A -35 - -KPX F r 0 -KPX F period -110 -KPX F o -25 -KPX F i 0 -KPX F e -25 -KPX F comma -92 -KPX F a -25 -KPX F A -90 - -KPX G period 0 -KPX G comma 0 - -KPX J u -15 -KPX J period -20 -KPX J o -15 -KPX J e -15 -KPX J comma 0 -KPX J a -15 -KPX J A -30 - -KPX K y -45 -KPX K u -15 -KPX K o -25 -KPX K e -25 -KPX K O -30 - -KPX L y -55 -KPX L quoteright -110 -KPX L quotedblright -20 -KPX L Y -92 -KPX L W -92 -KPX L V -92 -KPX L T -92 - -KPX N period 0 -KPX N comma 0 -KPX N A -20 - -KPX O period 0 -KPX O comma 0 -KPX O Y -50 -KPX O X -40 -KPX O W -50 -KPX O V -50 -KPX O T -40 -KPX O A -40 - -KPX P period -110 -KPX P o -20 -KPX P e -20 -KPX P comma -92 -KPX P a -10 -KPX P A -74 - -KPX Q period -20 -KPX Q comma 0 -KPX Q U -10 - -KPX R Y -35 -KPX R W -35 -KPX R V -55 -KPX R U -30 -KPX R T -40 -KPX R O -30 - -KPX S period 0 -KPX S comma 0 - -KPX T y -74 -KPX T w -74 -KPX T u -92 -KPX T semicolon -74 -KPX T r -74 -KPX T period -90 -KPX T o -92 -KPX T i -18 -KPX T hyphen -92 -KPX T h 0 -KPX T e -92 -KPX T comma -74 -KPX T colon -74 -KPX T a -92 -KPX T O -18 -KPX T A -90 - -KPX U period -50 -KPX U comma -50 -KPX U A -60 - -KPX V u -92 -KPX V semicolon -92 -KPX V period -145 -KPX V o -100 -KPX V i -37 -KPX V hyphen -74 -KPX V e -100 -KPX V comma -129 -KPX V colon -92 -KPX V a -92 -KPX V O -45 -KPX V G -30 -KPX V A -135 - -KPX W y -60 -KPX W u -50 -KPX W semicolon -55 -KPX W period -92 -KPX W o -75 -KPX W i -18 -KPX W hyphen -37 -KPX W h 0 -KPX W e -65 -KPX W comma -92 -KPX W colon -55 -KPX W a -65 -KPX W O -10 -KPX W A -120 - -KPX Y u -92 -KPX Y semicolon -92 -KPX Y period -92 -KPX Y o -111 -KPX Y i -37 -KPX Y hyphen -92 -KPX Y e -111 -KPX Y comma -92 -KPX Y colon -92 -KPX Y a -85 -KPX Y O -35 -KPX Y A -110 - -KPX a y 0 -KPX a w 0 -KPX a v -25 -KPX a t 0 -KPX a p 0 -KPX a g 0 -KPX a b 0 - -KPX b y 0 -KPX b v -15 -KPX b u -20 -KPX b period -40 -KPX b l 0 -KPX b comma 0 -KPX b b -10 - -KPX c y 0 -KPX c period 0 -KPX c l 0 -KPX c k 0 -KPX c h 0 -KPX c comma 0 - -KPX colon space 0 - -KPX comma space 0 -KPX comma quoteright -55 -KPX comma quotedblright -45 - -KPX d y 0 -KPX d w -15 -KPX d v 0 -KPX d period 0 -KPX d d 0 -KPX d comma 0 - -KPX e y 0 -KPX e x 0 -KPX e w 0 -KPX e v -15 -KPX e period 0 -KPX e p 0 -KPX e g 0 -KPX e comma 0 -KPX e b 0 - -KPX f quoteright 55 -KPX f quotedblright 50 -KPX f period -15 -KPX f o -25 -KPX f l 0 -KPX f i -25 -KPX f f 0 -KPX f e 0 -KPX f dotlessi -35 -KPX f comma -15 -KPX f a 0 - -KPX g y 0 -KPX g r 0 -KPX g period -15 -KPX g o 0 -KPX g i 0 -KPX g g 0 -KPX g e 0 -KPX g comma 0 -KPX g a 0 - -KPX h y -15 - -KPX i v -10 - -KPX k y -15 -KPX k o -15 -KPX k e -10 - -KPX l y 0 -KPX l w 0 - -KPX m y 0 -KPX m u 0 - -KPX n y 0 -KPX n v -40 -KPX n u 0 - -KPX o y 0 -KPX o x 0 -KPX o w -10 -KPX o v -10 -KPX o g 0 - -KPX p y 0 - -KPX period quoteright -55 -KPX period quotedblright -55 - -KPX quotedblleft quoteleft 0 -KPX quotedblleft A -10 - -KPX quotedblright space 0 - -KPX quoteleft quoteleft -63 -KPX quoteleft A -10 - -KPX quoteright v -20 -KPX quoteright t 0 -KPX quoteright space -74 -KPX quoteright s -37 -KPX quoteright r -20 -KPX quoteright quoteright -63 -KPX quoteright quotedblright 0 -KPX quoteright l 0 -KPX quoteright d -20 - -KPX r y 0 -KPX r v -10 -KPX r u 0 -KPX r t 0 -KPX r s 0 -KPX r r 0 -KPX r q -18 -KPX r period -100 -KPX r p -10 -KPX r o -18 -KPX r n -15 -KPX r m 0 -KPX r l 0 -KPX r k 0 -KPX r i 0 -KPX r hyphen -37 -KPX r g -10 -KPX r e -18 -KPX r d 0 -KPX r comma -92 -KPX r c -18 -KPX r a 0 - -KPX s w 0 - -KPX space quoteleft 0 -KPX space quotedblleft 0 -KPX space Y -55 -KPX space W -30 -KPX space V -45 -KPX space T -30 -KPX space A -55 - -KPX v period -70 -KPX v o -10 -KPX v e -10 -KPX v comma -55 -KPX v a -10 - -KPX w period -70 -KPX w o -10 -KPX w h 0 -KPX w e 0 -KPX w comma -55 -KPX w a 0 - -KPX x e 0 - -KPX y period -70 -KPX y o -25 -KPX y e -10 -KPX y comma -55 -KPX y a 0 - -KPX z o 0 -KPX z e 0 -EndKernPairs -EndKernData -StartComposites 58 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 188 210 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 188 210 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 188 210 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 188 210 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 180 195 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 188 210 ; -CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 208 0 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 174 210 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 174 210 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 174 210 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 174 210 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 28 210 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 28 210 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 28 210 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 28 210 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 195 210 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 223 210 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 223 210 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 223 210 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 223 210 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 223 210 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 112 210 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 222 210 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 222 210 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 222 210 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 222 210 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 210 215 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 215 210 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 167 210 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 77 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 77 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 77 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 77 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 77 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 77 0 ; -CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 69 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 62 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 62 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 62 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 62 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -34 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -34 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -34 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -34 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 112 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 84 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 84 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 84 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 84 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 84 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 28 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 105 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 105 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 105 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 105 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 84 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 84 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 56 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/ptmbi8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/ptmbi8a.afm deleted file mode 100644 index 25ab54e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/ptmbi8a.afm +++ /dev/null @@ -1,648 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Tue Mar 20 13:14:55 1990 -Comment UniqueID 28425 -Comment VMusage 32721 39613 -FontName Times-BoldItalic -FullName Times Bold Italic -FamilyName Times -Weight Bold -ItalicAngle -15 -IsFixedPitch false -FontBBox -200 -218 996 921 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.009 -Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved.Times is a trademark of Linotype AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 669 -XHeight 462 -Ascender 699 -Descender -205 -StartCharMetrics 228 -C 32 ; WX 250 ; N space ; B 0 0 0 0 ; -C 33 ; WX 389 ; N exclam ; B 67 -13 370 684 ; -C 34 ; WX 555 ; N quotedbl ; B 136 398 536 685 ; -C 35 ; WX 500 ; N numbersign ; B -33 0 533 700 ; -C 36 ; WX 500 ; N dollar ; B -20 -100 497 733 ; -C 37 ; WX 833 ; N percent ; B 39 -10 793 692 ; -C 38 ; WX 778 ; N ampersand ; B 5 -19 699 682 ; -C 39 ; WX 333 ; N quoteright ; B 98 369 302 685 ; -C 40 ; WX 333 ; N parenleft ; B 28 -179 344 685 ; -C 41 ; WX 333 ; N parenright ; B -44 -179 271 685 ; -C 42 ; WX 500 ; N asterisk ; B 65 249 456 685 ; -C 43 ; WX 570 ; N plus ; B 33 0 537 506 ; -C 44 ; WX 250 ; N comma ; B -60 -182 144 134 ; -C 45 ; WX 333 ; N hyphen ; B 2 166 271 282 ; -C 46 ; WX 250 ; N period ; B -9 -13 139 135 ; -C 47 ; WX 278 ; N slash ; B -64 -18 342 685 ; -C 48 ; WX 500 ; N zero ; B 17 -14 477 683 ; -C 49 ; WX 500 ; N one ; B 5 0 419 683 ; -C 50 ; WX 500 ; N two ; B -27 0 446 683 ; -C 51 ; WX 500 ; N three ; B -15 -13 450 683 ; -C 52 ; WX 500 ; N four ; B -15 0 503 683 ; -C 53 ; WX 500 ; N five ; B -11 -13 487 669 ; -C 54 ; WX 500 ; N six ; B 23 -15 509 679 ; -C 55 ; WX 500 ; N seven ; B 52 0 525 669 ; -C 56 ; WX 500 ; N eight ; B 3 -13 476 683 ; -C 57 ; WX 500 ; N nine ; B -12 -10 475 683 ; -C 58 ; WX 333 ; N colon ; B 23 -13 264 459 ; -C 59 ; WX 333 ; N semicolon ; B -25 -183 264 459 ; -C 60 ; WX 570 ; N less ; B 31 -8 539 514 ; -C 61 ; WX 570 ; N equal ; B 33 107 537 399 ; -C 62 ; WX 570 ; N greater ; B 31 -8 539 514 ; -C 63 ; WX 500 ; N question ; B 79 -13 470 684 ; -C 64 ; WX 832 ; N at ; B 63 -18 770 685 ; -C 65 ; WX 667 ; N A ; B -67 0 593 683 ; -C 66 ; WX 667 ; N B ; B -24 0 624 669 ; -C 67 ; WX 667 ; N C ; B 32 -18 677 685 ; -C 68 ; WX 722 ; N D ; B -46 0 685 669 ; -C 69 ; WX 667 ; N E ; B -27 0 653 669 ; -C 70 ; WX 667 ; N F ; B -13 0 660 669 ; -C 71 ; WX 722 ; N G ; B 21 -18 706 685 ; -C 72 ; WX 778 ; N H ; B -24 0 799 669 ; -C 73 ; WX 389 ; N I ; B -32 0 406 669 ; -C 74 ; WX 500 ; N J ; B -46 -99 524 669 ; -C 75 ; WX 667 ; N K ; B -21 0 702 669 ; -C 76 ; WX 611 ; N L ; B -22 0 590 669 ; -C 77 ; WX 889 ; N M ; B -29 -12 917 669 ; -C 78 ; WX 722 ; N N ; B -27 -15 748 669 ; -C 79 ; WX 722 ; N O ; B 27 -18 691 685 ; -C 80 ; WX 611 ; N P ; B -27 0 613 669 ; -C 81 ; WX 722 ; N Q ; B 27 -208 691 685 ; -C 82 ; WX 667 ; N R ; B -29 0 623 669 ; -C 83 ; WX 556 ; N S ; B 2 -18 526 685 ; -C 84 ; WX 611 ; N T ; B 50 0 650 669 ; -C 85 ; WX 722 ; N U ; B 67 -18 744 669 ; -C 86 ; WX 667 ; N V ; B 65 -18 715 669 ; -C 87 ; WX 889 ; N W ; B 65 -18 940 669 ; -C 88 ; WX 667 ; N X ; B -24 0 694 669 ; -C 89 ; WX 611 ; N Y ; B 73 0 659 669 ; -C 90 ; WX 611 ; N Z ; B -11 0 590 669 ; -C 91 ; WX 333 ; N bracketleft ; B -37 -159 362 674 ; -C 92 ; WX 278 ; N backslash ; B -1 -18 279 685 ; -C 93 ; WX 333 ; N bracketright ; B -56 -157 343 674 ; -C 94 ; WX 570 ; N asciicircum ; B 67 304 503 669 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 333 ; N quoteleft ; B 128 369 332 685 ; -C 97 ; WX 500 ; N a ; B -21 -14 455 462 ; -C 98 ; WX 500 ; N b ; B -14 -13 444 699 ; -C 99 ; WX 444 ; N c ; B -5 -13 392 462 ; -C 100 ; WX 500 ; N d ; B -21 -13 517 699 ; -C 101 ; WX 444 ; N e ; B 5 -13 398 462 ; -C 102 ; WX 333 ; N f ; B -169 -205 446 698 ; L i fi ; L l fl ; -C 103 ; WX 500 ; N g ; B -52 -203 478 462 ; -C 104 ; WX 556 ; N h ; B -13 -9 498 699 ; -C 105 ; WX 278 ; N i ; B 2 -9 263 684 ; -C 106 ; WX 278 ; N j ; B -189 -207 279 684 ; -C 107 ; WX 500 ; N k ; B -23 -8 483 699 ; -C 108 ; WX 278 ; N l ; B 2 -9 290 699 ; -C 109 ; WX 778 ; N m ; B -14 -9 722 462 ; -C 110 ; WX 556 ; N n ; B -6 -9 493 462 ; -C 111 ; WX 500 ; N o ; B -3 -13 441 462 ; -C 112 ; WX 500 ; N p ; B -120 -205 446 462 ; -C 113 ; WX 500 ; N q ; B 1 -205 471 462 ; -C 114 ; WX 389 ; N r ; B -21 0 389 462 ; -C 115 ; WX 389 ; N s ; B -19 -13 333 462 ; -C 116 ; WX 278 ; N t ; B -11 -9 281 594 ; -C 117 ; WX 556 ; N u ; B 15 -9 492 462 ; -C 118 ; WX 444 ; N v ; B 16 -13 401 462 ; -C 119 ; WX 667 ; N w ; B 16 -13 614 462 ; -C 120 ; WX 500 ; N x ; B -46 -13 469 462 ; -C 121 ; WX 444 ; N y ; B -94 -205 392 462 ; -C 122 ; WX 389 ; N z ; B -43 -78 368 449 ; -C 123 ; WX 348 ; N braceleft ; B 5 -187 436 686 ; -C 124 ; WX 220 ; N bar ; B 66 -18 154 685 ; -C 125 ; WX 348 ; N braceright ; B -129 -187 302 686 ; -C 126 ; WX 570 ; N asciitilde ; B 54 173 516 333 ; -C 161 ; WX 389 ; N exclamdown ; B 19 -205 322 492 ; -C 162 ; WX 500 ; N cent ; B 42 -143 439 576 ; -C 163 ; WX 500 ; N sterling ; B -32 -12 510 683 ; -C 164 ; WX 167 ; N fraction ; B -169 -14 324 683 ; -C 165 ; WX 500 ; N yen ; B 33 0 628 669 ; -C 166 ; WX 500 ; N florin ; B -87 -156 537 707 ; -C 167 ; WX 500 ; N section ; B 36 -143 459 685 ; -C 168 ; WX 500 ; N currency ; B -26 34 526 586 ; -C 169 ; WX 278 ; N quotesingle ; B 128 398 268 685 ; -C 170 ; WX 500 ; N quotedblleft ; B 53 369 513 685 ; -C 171 ; WX 500 ; N guillemotleft ; B 12 32 468 415 ; -C 172 ; WX 333 ; N guilsinglleft ; B 32 32 303 415 ; -C 173 ; WX 333 ; N guilsinglright ; B 10 32 281 415 ; -C 174 ; WX 556 ; N fi ; B -188 -205 514 703 ; -C 175 ; WX 556 ; N fl ; B -186 -205 553 704 ; -C 177 ; WX 500 ; N endash ; B -40 178 477 269 ; -C 178 ; WX 500 ; N dagger ; B 91 -145 494 685 ; -C 179 ; WX 500 ; N daggerdbl ; B 10 -139 493 685 ; -C 180 ; WX 250 ; N periodcentered ; B 51 257 199 405 ; -C 182 ; WX 500 ; N paragraph ; B -57 -193 562 669 ; -C 183 ; WX 350 ; N bullet ; B 0 175 350 525 ; -C 184 ; WX 333 ; N quotesinglbase ; B -5 -182 199 134 ; -C 185 ; WX 500 ; N quotedblbase ; B -57 -182 403 134 ; -C 186 ; WX 500 ; N quotedblright ; B 53 369 513 685 ; -C 187 ; WX 500 ; N guillemotright ; B 12 32 468 415 ; -C 188 ; WX 1000 ; N ellipsis ; B 40 -13 852 135 ; -C 189 ; WX 1000 ; N perthousand ; B 7 -29 996 706 ; -C 191 ; WX 500 ; N questiondown ; B 30 -205 421 492 ; -C 193 ; WX 333 ; N grave ; B 85 516 297 697 ; -C 194 ; WX 333 ; N acute ; B 139 516 379 697 ; -C 195 ; WX 333 ; N circumflex ; B 40 516 367 690 ; -C 196 ; WX 333 ; N tilde ; B 48 536 407 655 ; -C 197 ; WX 333 ; N macron ; B 51 553 393 623 ; -C 198 ; WX 333 ; N breve ; B 71 516 387 678 ; -C 199 ; WX 333 ; N dotaccent ; B 163 525 293 655 ; -C 200 ; WX 333 ; N dieresis ; B 55 525 397 655 ; -C 202 ; WX 333 ; N ring ; B 127 516 340 729 ; -C 203 ; WX 333 ; N cedilla ; B -80 -218 156 5 ; -C 205 ; WX 333 ; N hungarumlaut ; B 69 516 498 697 ; -C 206 ; WX 333 ; N ogonek ; B -40 -173 189 44 ; -C 207 ; WX 333 ; N caron ; B 79 516 411 690 ; -C 208 ; WX 1000 ; N emdash ; B -40 178 977 269 ; -C 225 ; WX 944 ; N AE ; B -64 0 918 669 ; -C 227 ; WX 266 ; N ordfeminine ; B 16 399 330 685 ; -C 232 ; WX 611 ; N Lslash ; B -22 0 590 669 ; -C 233 ; WX 722 ; N Oslash ; B 27 -125 691 764 ; -C 234 ; WX 944 ; N OE ; B 23 -8 946 677 ; -C 235 ; WX 300 ; N ordmasculine ; B 56 400 347 685 ; -C 241 ; WX 722 ; N ae ; B -5 -13 673 462 ; -C 245 ; WX 278 ; N dotlessi ; B 2 -9 238 462 ; -C 248 ; WX 278 ; N lslash ; B -13 -9 301 699 ; -C 249 ; WX 500 ; N oslash ; B -3 -119 441 560 ; -C 250 ; WX 722 ; N oe ; B 6 -13 674 462 ; -C 251 ; WX 500 ; N germandbls ; B -200 -200 473 705 ; -C -1 ; WX 611 ; N Zcaron ; B -11 0 590 897 ; -C -1 ; WX 444 ; N ccedilla ; B -24 -218 392 462 ; -C -1 ; WX 444 ; N ydieresis ; B -94 -205 438 655 ; -C -1 ; WX 500 ; N atilde ; B -21 -14 491 655 ; -C -1 ; WX 278 ; N icircumflex ; B -2 -9 325 690 ; -C -1 ; WX 300 ; N threesuperior ; B 17 265 321 683 ; -C -1 ; WX 444 ; N ecircumflex ; B 5 -13 423 690 ; -C -1 ; WX 500 ; N thorn ; B -120 -205 446 699 ; -C -1 ; WX 444 ; N egrave ; B 5 -13 398 697 ; -C -1 ; WX 300 ; N twosuperior ; B 2 274 313 683 ; -C -1 ; WX 444 ; N eacute ; B 5 -13 435 697 ; -C -1 ; WX 500 ; N otilde ; B -3 -13 491 655 ; -C -1 ; WX 667 ; N Aacute ; B -67 0 593 904 ; -C -1 ; WX 500 ; N ocircumflex ; B -3 -13 451 690 ; -C -1 ; WX 444 ; N yacute ; B -94 -205 435 697 ; -C -1 ; WX 556 ; N udieresis ; B 15 -9 494 655 ; -C -1 ; WX 750 ; N threequarters ; B 7 -14 726 683 ; -C -1 ; WX 500 ; N acircumflex ; B -21 -14 455 690 ; -C -1 ; WX 722 ; N Eth ; B -31 0 700 669 ; -C -1 ; WX 444 ; N edieresis ; B 5 -13 443 655 ; -C -1 ; WX 556 ; N ugrave ; B 15 -9 492 697 ; -C -1 ; WX 1000 ; N trademark ; B 32 263 968 669 ; -C -1 ; WX 500 ; N ograve ; B -3 -13 441 697 ; -C -1 ; WX 389 ; N scaron ; B -19 -13 439 690 ; -C -1 ; WX 389 ; N Idieresis ; B -32 0 445 862 ; -C -1 ; WX 556 ; N uacute ; B 15 -9 492 697 ; -C -1 ; WX 500 ; N agrave ; B -21 -14 455 697 ; -C -1 ; WX 556 ; N ntilde ; B -6 -9 504 655 ; -C -1 ; WX 500 ; N aring ; B -21 -14 455 729 ; -C -1 ; WX 389 ; N zcaron ; B -43 -78 424 690 ; -C -1 ; WX 389 ; N Icircumflex ; B -32 0 420 897 ; -C -1 ; WX 722 ; N Ntilde ; B -27 -15 748 862 ; -C -1 ; WX 556 ; N ucircumflex ; B 15 -9 492 690 ; -C -1 ; WX 667 ; N Ecircumflex ; B -27 0 653 897 ; -C -1 ; WX 389 ; N Iacute ; B -32 0 412 904 ; -C -1 ; WX 667 ; N Ccedilla ; B 32 -218 677 685 ; -C -1 ; WX 722 ; N Odieresis ; B 27 -18 691 862 ; -C -1 ; WX 556 ; N Scaron ; B 2 -18 526 897 ; -C -1 ; WX 667 ; N Edieresis ; B -27 0 653 862 ; -C -1 ; WX 389 ; N Igrave ; B -32 0 406 904 ; -C -1 ; WX 500 ; N adieresis ; B -21 -14 471 655 ; -C -1 ; WX 722 ; N Ograve ; B 27 -18 691 904 ; -C -1 ; WX 667 ; N Egrave ; B -27 0 653 904 ; -C -1 ; WX 611 ; N Ydieresis ; B 73 0 659 862 ; -C -1 ; WX 747 ; N registered ; B 30 -18 718 685 ; -C -1 ; WX 722 ; N Otilde ; B 27 -18 691 862 ; -C -1 ; WX 750 ; N onequarter ; B 7 -14 721 683 ; -C -1 ; WX 722 ; N Ugrave ; B 67 -18 744 904 ; -C -1 ; WX 722 ; N Ucircumflex ; B 67 -18 744 897 ; -C -1 ; WX 611 ; N Thorn ; B -27 0 573 669 ; -C -1 ; WX 570 ; N divide ; B 33 -29 537 535 ; -C -1 ; WX 667 ; N Atilde ; B -67 0 593 862 ; -C -1 ; WX 722 ; N Uacute ; B 67 -18 744 904 ; -C -1 ; WX 722 ; N Ocircumflex ; B 27 -18 691 897 ; -C -1 ; WX 606 ; N logicalnot ; B 51 108 555 399 ; -C -1 ; WX 667 ; N Aring ; B -67 0 593 921 ; -C -1 ; WX 278 ; N idieresis ; B 2 -9 360 655 ; -C -1 ; WX 278 ; N iacute ; B 2 -9 352 697 ; -C -1 ; WX 500 ; N aacute ; B -21 -14 463 697 ; -C -1 ; WX 570 ; N plusminus ; B 33 0 537 506 ; -C -1 ; WX 570 ; N multiply ; B 48 16 522 490 ; -C -1 ; WX 722 ; N Udieresis ; B 67 -18 744 862 ; -C -1 ; WX 606 ; N minus ; B 51 209 555 297 ; -C -1 ; WX 300 ; N onesuperior ; B 30 274 301 683 ; -C -1 ; WX 667 ; N Eacute ; B -27 0 653 904 ; -C -1 ; WX 667 ; N Acircumflex ; B -67 0 593 897 ; -C -1 ; WX 747 ; N copyright ; B 30 -18 718 685 ; -C -1 ; WX 667 ; N Agrave ; B -67 0 593 904 ; -C -1 ; WX 500 ; N odieresis ; B -3 -13 466 655 ; -C -1 ; WX 500 ; N oacute ; B -3 -13 463 697 ; -C -1 ; WX 400 ; N degree ; B 83 397 369 683 ; -C -1 ; WX 278 ; N igrave ; B 2 -9 260 697 ; -C -1 ; WX 576 ; N mu ; B -60 -207 516 449 ; -C -1 ; WX 722 ; N Oacute ; B 27 -18 691 904 ; -C -1 ; WX 500 ; N eth ; B -3 -13 454 699 ; -C -1 ; WX 667 ; N Adieresis ; B -67 0 593 862 ; -C -1 ; WX 611 ; N Yacute ; B 73 0 659 904 ; -C -1 ; WX 220 ; N brokenbar ; B 66 -18 154 685 ; -C -1 ; WX 750 ; N onehalf ; B -9 -14 723 683 ; -EndCharMetrics -StartKernData -StartKernPairs 283 - -KPX A y -74 -KPX A w -74 -KPX A v -74 -KPX A u -30 -KPX A quoteright -74 -KPX A quotedblright 0 -KPX A p 0 -KPX A Y -70 -KPX A W -100 -KPX A V -95 -KPX A U -50 -KPX A T -55 -KPX A Q -55 -KPX A O -50 -KPX A G -60 -KPX A C -65 - -KPX B period 0 -KPX B comma 0 -KPX B U -10 -KPX B A -25 - -KPX D period 0 -KPX D comma 0 -KPX D Y -50 -KPX D W -40 -KPX D V -50 -KPX D A -25 - -KPX F r -50 -KPX F period -129 -KPX F o -70 -KPX F i -40 -KPX F e -100 -KPX F comma -129 -KPX F a -95 -KPX F A -100 - -KPX G period 0 -KPX G comma 0 - -KPX J u -40 -KPX J period -10 -KPX J o -40 -KPX J e -40 -KPX J comma -10 -KPX J a -40 -KPX J A -25 - -KPX K y -20 -KPX K u -20 -KPX K o -25 -KPX K e -25 -KPX K O -30 - -KPX L y -37 -KPX L quoteright -55 -KPX L quotedblright 0 -KPX L Y -37 -KPX L W -37 -KPX L V -37 -KPX L T -18 - -KPX N period 0 -KPX N comma 0 -KPX N A -30 - -KPX O period 0 -KPX O comma 0 -KPX O Y -50 -KPX O X -40 -KPX O W -50 -KPX O V -50 -KPX O T -40 -KPX O A -40 - -KPX P period -129 -KPX P o -55 -KPX P e -50 -KPX P comma -129 -KPX P a -40 -KPX P A -85 - -KPX Q period 0 -KPX Q comma 0 -KPX Q U -10 - -KPX R Y -18 -KPX R W -18 -KPX R V -18 -KPX R U -40 -KPX R T -30 -KPX R O -40 - -KPX S period 0 -KPX S comma 0 - -KPX T y -37 -KPX T w -37 -KPX T u -37 -KPX T semicolon -74 -KPX T r -37 -KPX T period -92 -KPX T o -95 -KPX T i -37 -KPX T hyphen -92 -KPX T h 0 -KPX T e -92 -KPX T comma -92 -KPX T colon -74 -KPX T a -92 -KPX T O -18 -KPX T A -55 - -KPX U period 0 -KPX U comma 0 -KPX U A -45 - -KPX V u -55 -KPX V semicolon -74 -KPX V period -129 -KPX V o -111 -KPX V i -55 -KPX V hyphen -70 -KPX V e -111 -KPX V comma -129 -KPX V colon -74 -KPX V a -111 -KPX V O -30 -KPX V G -10 -KPX V A -85 - -KPX W y -55 -KPX W u -55 -KPX W semicolon -55 -KPX W period -74 -KPX W o -80 -KPX W i -37 -KPX W hyphen -50 -KPX W h 0 -KPX W e -90 -KPX W comma -74 -KPX W colon -55 -KPX W a -85 -KPX W O -15 -KPX W A -74 - -KPX Y u -92 -KPX Y semicolon -92 -KPX Y period -74 -KPX Y o -111 -KPX Y i -55 -KPX Y hyphen -92 -KPX Y e -111 -KPX Y comma -92 -KPX Y colon -92 -KPX Y a -92 -KPX Y O -25 -KPX Y A -74 - -KPX a y 0 -KPX a w 0 -KPX a v 0 -KPX a t 0 -KPX a p 0 -KPX a g 0 -KPX a b 0 - -KPX b y 0 -KPX b v 0 -KPX b u -20 -KPX b period -40 -KPX b l 0 -KPX b comma 0 -KPX b b -10 - -KPX c y 0 -KPX c period 0 -KPX c l 0 -KPX c k -10 -KPX c h -10 -KPX c comma 0 - -KPX colon space 0 - -KPX comma space 0 -KPX comma quoteright -95 -KPX comma quotedblright -95 - -KPX d y 0 -KPX d w 0 -KPX d v 0 -KPX d period 0 -KPX d d 0 -KPX d comma 0 - -KPX e y 0 -KPX e x 0 -KPX e w 0 -KPX e v 0 -KPX e period 0 -KPX e p 0 -KPX e g 0 -KPX e comma 0 -KPX e b -10 - -KPX f quoteright 55 -KPX f quotedblright 0 -KPX f period -10 -KPX f o -10 -KPX f l 0 -KPX f i 0 -KPX f f -18 -KPX f e -10 -KPX f dotlessi -30 -KPX f comma -10 -KPX f a 0 - -KPX g y 0 -KPX g r 0 -KPX g period 0 -KPX g o 0 -KPX g i 0 -KPX g g 0 -KPX g e 0 -KPX g comma 0 -KPX g a 0 - -KPX h y 0 - -KPX i v 0 - -KPX k y 0 -KPX k o -10 -KPX k e -30 - -KPX l y 0 -KPX l w 0 - -KPX m y 0 -KPX m u 0 - -KPX n y 0 -KPX n v -40 -KPX n u 0 - -KPX o y -10 -KPX o x -10 -KPX o w -25 -KPX o v -15 -KPX o g 0 - -KPX p y 0 - -KPX period quoteright -95 -KPX period quotedblright -95 - -KPX quotedblleft quoteleft 0 -KPX quotedblleft A 0 - -KPX quotedblright space 0 - -KPX quoteleft quoteleft -74 -KPX quoteleft A 0 - -KPX quoteright v -15 -KPX quoteright t -37 -KPX quoteright space -74 -KPX quoteright s -74 -KPX quoteright r -15 -KPX quoteright quoteright -74 -KPX quoteright quotedblright 0 -KPX quoteright l 0 -KPX quoteright d -15 - -KPX r y 0 -KPX r v 0 -KPX r u 0 -KPX r t 0 -KPX r s 0 -KPX r r 0 -KPX r q 0 -KPX r period -65 -KPX r p 0 -KPX r o 0 -KPX r n 0 -KPX r m 0 -KPX r l 0 -KPX r k 0 -KPX r i 0 -KPX r hyphen 0 -KPX r g 0 -KPX r e 0 -KPX r d 0 -KPX r comma -65 -KPX r c 0 -KPX r a 0 - -KPX s w 0 - -KPX space quoteleft 0 -KPX space quotedblleft 0 -KPX space Y -70 -KPX space W -70 -KPX space V -70 -KPX space T 0 -KPX space A -37 - -KPX v period -37 -KPX v o -15 -KPX v e -15 -KPX v comma -37 -KPX v a 0 - -KPX w period -37 -KPX w o -15 -KPX w h 0 -KPX w e -10 -KPX w comma -37 -KPX w a -10 - -KPX x e -10 - -KPX y period -37 -KPX y o 0 -KPX y e 0 -KPX y comma -37 -KPX y a 0 - -KPX z o 0 -KPX z e 0 -EndKernPairs -EndKernData -StartComposites 58 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 172 207 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 187 207 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 167 207 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 172 207 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 157 192 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 167 207 ; -CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 167 0 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 172 207 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 187 207 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 187 207 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 172 207 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 33 207 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 53 207 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 48 207 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 33 207 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 210 207 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 200 207 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 230 207 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 215 207 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 200 207 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 215 207 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 112 207 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 210 207 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 230 207 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 230 207 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 200 207 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 154 207 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 169 207 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 139 207 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 84 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 84 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 74 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 74 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 84 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 84 0 ; -CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 56 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 56 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 56 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 46 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 46 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -27 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -42 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -37 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -37 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 97 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 84 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 84 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 69 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 74 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 84 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 28 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 112 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 112 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 97 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 102 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 56 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 41 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 13 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/ptmr8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/ptmr8a.afm deleted file mode 100644 index e5092b5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/ptmr8a.afm +++ /dev/null @@ -1,648 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Tue Mar 20 12:15:44 1990 -Comment UniqueID 28416 -Comment VMusage 30487 37379 -FontName Times-Roman -FullName Times Roman -FamilyName Times -Weight Roman -ItalicAngle 0 -IsFixedPitch false -FontBBox -168 -218 1000 898 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.007 -Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved.Times is a trademark of Linotype AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 662 -XHeight 450 -Ascender 683 -Descender -217 -StartCharMetrics 228 -C 32 ; WX 250 ; N space ; B 0 0 0 0 ; -C 33 ; WX 333 ; N exclam ; B 130 -9 238 676 ; -C 34 ; WX 408 ; N quotedbl ; B 77 431 331 676 ; -C 35 ; WX 500 ; N numbersign ; B 5 0 496 662 ; -C 36 ; WX 500 ; N dollar ; B 44 -87 457 727 ; -C 37 ; WX 833 ; N percent ; B 61 -13 772 676 ; -C 38 ; WX 778 ; N ampersand ; B 42 -13 750 676 ; -C 39 ; WX 333 ; N quoteright ; B 79 433 218 676 ; -C 40 ; WX 333 ; N parenleft ; B 48 -177 304 676 ; -C 41 ; WX 333 ; N parenright ; B 29 -177 285 676 ; -C 42 ; WX 500 ; N asterisk ; B 69 265 432 676 ; -C 43 ; WX 564 ; N plus ; B 30 0 534 506 ; -C 44 ; WX 250 ; N comma ; B 56 -141 195 102 ; -C 45 ; WX 333 ; N hyphen ; B 39 194 285 257 ; -C 46 ; WX 250 ; N period ; B 70 -11 181 100 ; -C 47 ; WX 278 ; N slash ; B -9 -14 287 676 ; -C 48 ; WX 500 ; N zero ; B 24 -14 476 676 ; -C 49 ; WX 500 ; N one ; B 111 0 394 676 ; -C 50 ; WX 500 ; N two ; B 30 0 475 676 ; -C 51 ; WX 500 ; N three ; B 43 -14 431 676 ; -C 52 ; WX 500 ; N four ; B 12 0 472 676 ; -C 53 ; WX 500 ; N five ; B 32 -14 438 688 ; -C 54 ; WX 500 ; N six ; B 34 -14 468 684 ; -C 55 ; WX 500 ; N seven ; B 20 -8 449 662 ; -C 56 ; WX 500 ; N eight ; B 56 -14 445 676 ; -C 57 ; WX 500 ; N nine ; B 30 -22 459 676 ; -C 58 ; WX 278 ; N colon ; B 81 -11 192 459 ; -C 59 ; WX 278 ; N semicolon ; B 80 -141 219 459 ; -C 60 ; WX 564 ; N less ; B 28 -8 536 514 ; -C 61 ; WX 564 ; N equal ; B 30 120 534 386 ; -C 62 ; WX 564 ; N greater ; B 28 -8 536 514 ; -C 63 ; WX 444 ; N question ; B 68 -8 414 676 ; -C 64 ; WX 921 ; N at ; B 116 -14 809 676 ; -C 65 ; WX 722 ; N A ; B 15 0 706 674 ; -C 66 ; WX 667 ; N B ; B 17 0 593 662 ; -C 67 ; WX 667 ; N C ; B 28 -14 633 676 ; -C 68 ; WX 722 ; N D ; B 16 0 685 662 ; -C 69 ; WX 611 ; N E ; B 12 0 597 662 ; -C 70 ; WX 556 ; N F ; B 12 0 546 662 ; -C 71 ; WX 722 ; N G ; B 32 -14 709 676 ; -C 72 ; WX 722 ; N H ; B 19 0 702 662 ; -C 73 ; WX 333 ; N I ; B 18 0 315 662 ; -C 74 ; WX 389 ; N J ; B 10 -14 370 662 ; -C 75 ; WX 722 ; N K ; B 34 0 723 662 ; -C 76 ; WX 611 ; N L ; B 12 0 598 662 ; -C 77 ; WX 889 ; N M ; B 12 0 863 662 ; -C 78 ; WX 722 ; N N ; B 12 -11 707 662 ; -C 79 ; WX 722 ; N O ; B 34 -14 688 676 ; -C 80 ; WX 556 ; N P ; B 16 0 542 662 ; -C 81 ; WX 722 ; N Q ; B 34 -178 701 676 ; -C 82 ; WX 667 ; N R ; B 17 0 659 662 ; -C 83 ; WX 556 ; N S ; B 42 -14 491 676 ; -C 84 ; WX 611 ; N T ; B 17 0 593 662 ; -C 85 ; WX 722 ; N U ; B 14 -14 705 662 ; -C 86 ; WX 722 ; N V ; B 16 -11 697 662 ; -C 87 ; WX 944 ; N W ; B 5 -11 932 662 ; -C 88 ; WX 722 ; N X ; B 10 0 704 662 ; -C 89 ; WX 722 ; N Y ; B 22 0 703 662 ; -C 90 ; WX 611 ; N Z ; B 9 0 597 662 ; -C 91 ; WX 333 ; N bracketleft ; B 88 -156 299 662 ; -C 92 ; WX 278 ; N backslash ; B -9 -14 287 676 ; -C 93 ; WX 333 ; N bracketright ; B 34 -156 245 662 ; -C 94 ; WX 469 ; N asciicircum ; B 24 297 446 662 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 333 ; N quoteleft ; B 115 433 254 676 ; -C 97 ; WX 444 ; N a ; B 37 -10 442 460 ; -C 98 ; WX 500 ; N b ; B 3 -10 468 683 ; -C 99 ; WX 444 ; N c ; B 25 -10 412 460 ; -C 100 ; WX 500 ; N d ; B 27 -10 491 683 ; -C 101 ; WX 444 ; N e ; B 25 -10 424 460 ; -C 102 ; WX 333 ; N f ; B 20 0 383 683 ; L i fi ; L l fl ; -C 103 ; WX 500 ; N g ; B 28 -218 470 460 ; -C 104 ; WX 500 ; N h ; B 9 0 487 683 ; -C 105 ; WX 278 ; N i ; B 16 0 253 683 ; -C 106 ; WX 278 ; N j ; B -70 -218 194 683 ; -C 107 ; WX 500 ; N k ; B 7 0 505 683 ; -C 108 ; WX 278 ; N l ; B 19 0 257 683 ; -C 109 ; WX 778 ; N m ; B 16 0 775 460 ; -C 110 ; WX 500 ; N n ; B 16 0 485 460 ; -C 111 ; WX 500 ; N o ; B 29 -10 470 460 ; -C 112 ; WX 500 ; N p ; B 5 -217 470 460 ; -C 113 ; WX 500 ; N q ; B 24 -217 488 460 ; -C 114 ; WX 333 ; N r ; B 5 0 335 460 ; -C 115 ; WX 389 ; N s ; B 51 -10 348 460 ; -C 116 ; WX 278 ; N t ; B 13 -10 279 579 ; -C 117 ; WX 500 ; N u ; B 9 -10 479 450 ; -C 118 ; WX 500 ; N v ; B 19 -14 477 450 ; -C 119 ; WX 722 ; N w ; B 21 -14 694 450 ; -C 120 ; WX 500 ; N x ; B 17 0 479 450 ; -C 121 ; WX 500 ; N y ; B 14 -218 475 450 ; -C 122 ; WX 444 ; N z ; B 27 0 418 450 ; -C 123 ; WX 480 ; N braceleft ; B 100 -181 350 680 ; -C 124 ; WX 200 ; N bar ; B 67 -14 133 676 ; -C 125 ; WX 480 ; N braceright ; B 130 -181 380 680 ; -C 126 ; WX 541 ; N asciitilde ; B 40 183 502 323 ; -C 161 ; WX 333 ; N exclamdown ; B 97 -218 205 467 ; -C 162 ; WX 500 ; N cent ; B 53 -138 448 579 ; -C 163 ; WX 500 ; N sterling ; B 12 -8 490 676 ; -C 164 ; WX 167 ; N fraction ; B -168 -14 331 676 ; -C 165 ; WX 500 ; N yen ; B -53 0 512 662 ; -C 166 ; WX 500 ; N florin ; B 7 -189 490 676 ; -C 167 ; WX 500 ; N section ; B 70 -148 426 676 ; -C 168 ; WX 500 ; N currency ; B -22 58 522 602 ; -C 169 ; WX 180 ; N quotesingle ; B 48 431 133 676 ; -C 170 ; WX 444 ; N quotedblleft ; B 43 433 414 676 ; -C 171 ; WX 500 ; N guillemotleft ; B 42 33 456 416 ; -C 172 ; WX 333 ; N guilsinglleft ; B 63 33 285 416 ; -C 173 ; WX 333 ; N guilsinglright ; B 48 33 270 416 ; -C 174 ; WX 556 ; N fi ; B 31 0 521 683 ; -C 175 ; WX 556 ; N fl ; B 32 0 521 683 ; -C 177 ; WX 500 ; N endash ; B 0 201 500 250 ; -C 178 ; WX 500 ; N dagger ; B 59 -149 442 676 ; -C 179 ; WX 500 ; N daggerdbl ; B 58 -153 442 676 ; -C 180 ; WX 250 ; N periodcentered ; B 70 199 181 310 ; -C 182 ; WX 453 ; N paragraph ; B -22 -154 450 662 ; -C 183 ; WX 350 ; N bullet ; B 40 196 310 466 ; -C 184 ; WX 333 ; N quotesinglbase ; B 79 -141 218 102 ; -C 185 ; WX 444 ; N quotedblbase ; B 45 -141 416 102 ; -C 186 ; WX 444 ; N quotedblright ; B 30 433 401 676 ; -C 187 ; WX 500 ; N guillemotright ; B 44 33 458 416 ; -C 188 ; WX 1000 ; N ellipsis ; B 111 -11 888 100 ; -C 189 ; WX 1000 ; N perthousand ; B 7 -19 994 706 ; -C 191 ; WX 444 ; N questiondown ; B 30 -218 376 466 ; -C 193 ; WX 333 ; N grave ; B 19 507 242 678 ; -C 194 ; WX 333 ; N acute ; B 93 507 317 678 ; -C 195 ; WX 333 ; N circumflex ; B 11 507 322 674 ; -C 196 ; WX 333 ; N tilde ; B 1 532 331 638 ; -C 197 ; WX 333 ; N macron ; B 11 547 322 601 ; -C 198 ; WX 333 ; N breve ; B 26 507 307 664 ; -C 199 ; WX 333 ; N dotaccent ; B 118 523 216 623 ; -C 200 ; WX 333 ; N dieresis ; B 18 523 315 623 ; -C 202 ; WX 333 ; N ring ; B 67 512 266 711 ; -C 203 ; WX 333 ; N cedilla ; B 52 -215 261 0 ; -C 205 ; WX 333 ; N hungarumlaut ; B -3 507 377 678 ; -C 206 ; WX 333 ; N ogonek ; B 64 -165 249 0 ; -C 207 ; WX 333 ; N caron ; B 11 507 322 674 ; -C 208 ; WX 1000 ; N emdash ; B 0 201 1000 250 ; -C 225 ; WX 889 ; N AE ; B 0 0 863 662 ; -C 227 ; WX 276 ; N ordfeminine ; B 4 394 270 676 ; -C 232 ; WX 611 ; N Lslash ; B 12 0 598 662 ; -C 233 ; WX 722 ; N Oslash ; B 34 -80 688 734 ; -C 234 ; WX 889 ; N OE ; B 30 -6 885 668 ; -C 235 ; WX 310 ; N ordmasculine ; B 6 394 304 676 ; -C 241 ; WX 667 ; N ae ; B 38 -10 632 460 ; -C 245 ; WX 278 ; N dotlessi ; B 16 0 253 460 ; -C 248 ; WX 278 ; N lslash ; B 19 0 259 683 ; -C 249 ; WX 500 ; N oslash ; B 29 -112 470 551 ; -C 250 ; WX 722 ; N oe ; B 30 -10 690 460 ; -C 251 ; WX 500 ; N germandbls ; B 12 -9 468 683 ; -C -1 ; WX 611 ; N Zcaron ; B 9 0 597 886 ; -C -1 ; WX 444 ; N ccedilla ; B 25 -215 412 460 ; -C -1 ; WX 500 ; N ydieresis ; B 14 -218 475 623 ; -C -1 ; WX 444 ; N atilde ; B 37 -10 442 638 ; -C -1 ; WX 278 ; N icircumflex ; B -16 0 295 674 ; -C -1 ; WX 300 ; N threesuperior ; B 15 262 291 676 ; -C -1 ; WX 444 ; N ecircumflex ; B 25 -10 424 674 ; -C -1 ; WX 500 ; N thorn ; B 5 -217 470 683 ; -C -1 ; WX 444 ; N egrave ; B 25 -10 424 678 ; -C -1 ; WX 300 ; N twosuperior ; B 1 270 296 676 ; -C -1 ; WX 444 ; N eacute ; B 25 -10 424 678 ; -C -1 ; WX 500 ; N otilde ; B 29 -10 470 638 ; -C -1 ; WX 722 ; N Aacute ; B 15 0 706 890 ; -C -1 ; WX 500 ; N ocircumflex ; B 29 -10 470 674 ; -C -1 ; WX 500 ; N yacute ; B 14 -218 475 678 ; -C -1 ; WX 500 ; N udieresis ; B 9 -10 479 623 ; -C -1 ; WX 750 ; N threequarters ; B 15 -14 718 676 ; -C -1 ; WX 444 ; N acircumflex ; B 37 -10 442 674 ; -C -1 ; WX 722 ; N Eth ; B 16 0 685 662 ; -C -1 ; WX 444 ; N edieresis ; B 25 -10 424 623 ; -C -1 ; WX 500 ; N ugrave ; B 9 -10 479 678 ; -C -1 ; WX 980 ; N trademark ; B 30 256 957 662 ; -C -1 ; WX 500 ; N ograve ; B 29 -10 470 678 ; -C -1 ; WX 389 ; N scaron ; B 39 -10 350 674 ; -C -1 ; WX 333 ; N Idieresis ; B 18 0 315 835 ; -C -1 ; WX 500 ; N uacute ; B 9 -10 479 678 ; -C -1 ; WX 444 ; N agrave ; B 37 -10 442 678 ; -C -1 ; WX 500 ; N ntilde ; B 16 0 485 638 ; -C -1 ; WX 444 ; N aring ; B 37 -10 442 711 ; -C -1 ; WX 444 ; N zcaron ; B 27 0 418 674 ; -C -1 ; WX 333 ; N Icircumflex ; B 11 0 322 886 ; -C -1 ; WX 722 ; N Ntilde ; B 12 -11 707 850 ; -C -1 ; WX 500 ; N ucircumflex ; B 9 -10 479 674 ; -C -1 ; WX 611 ; N Ecircumflex ; B 12 0 597 886 ; -C -1 ; WX 333 ; N Iacute ; B 18 0 317 890 ; -C -1 ; WX 667 ; N Ccedilla ; B 28 -215 633 676 ; -C -1 ; WX 722 ; N Odieresis ; B 34 -14 688 835 ; -C -1 ; WX 556 ; N Scaron ; B 42 -14 491 886 ; -C -1 ; WX 611 ; N Edieresis ; B 12 0 597 835 ; -C -1 ; WX 333 ; N Igrave ; B 18 0 315 890 ; -C -1 ; WX 444 ; N adieresis ; B 37 -10 442 623 ; -C -1 ; WX 722 ; N Ograve ; B 34 -14 688 890 ; -C -1 ; WX 611 ; N Egrave ; B 12 0 597 890 ; -C -1 ; WX 722 ; N Ydieresis ; B 22 0 703 835 ; -C -1 ; WX 760 ; N registered ; B 38 -14 722 676 ; -C -1 ; WX 722 ; N Otilde ; B 34 -14 688 850 ; -C -1 ; WX 750 ; N onequarter ; B 37 -14 718 676 ; -C -1 ; WX 722 ; N Ugrave ; B 14 -14 705 890 ; -C -1 ; WX 722 ; N Ucircumflex ; B 14 -14 705 886 ; -C -1 ; WX 556 ; N Thorn ; B 16 0 542 662 ; -C -1 ; WX 564 ; N divide ; B 30 -10 534 516 ; -C -1 ; WX 722 ; N Atilde ; B 15 0 706 850 ; -C -1 ; WX 722 ; N Uacute ; B 14 -14 705 890 ; -C -1 ; WX 722 ; N Ocircumflex ; B 34 -14 688 886 ; -C -1 ; WX 564 ; N logicalnot ; B 30 108 534 386 ; -C -1 ; WX 722 ; N Aring ; B 15 0 706 898 ; -C -1 ; WX 278 ; N idieresis ; B -9 0 288 623 ; -C -1 ; WX 278 ; N iacute ; B 16 0 290 678 ; -C -1 ; WX 444 ; N aacute ; B 37 -10 442 678 ; -C -1 ; WX 564 ; N plusminus ; B 30 0 534 506 ; -C -1 ; WX 564 ; N multiply ; B 38 8 527 497 ; -C -1 ; WX 722 ; N Udieresis ; B 14 -14 705 835 ; -C -1 ; WX 564 ; N minus ; B 30 220 534 286 ; -C -1 ; WX 300 ; N onesuperior ; B 57 270 248 676 ; -C -1 ; WX 611 ; N Eacute ; B 12 0 597 890 ; -C -1 ; WX 722 ; N Acircumflex ; B 15 0 706 886 ; -C -1 ; WX 760 ; N copyright ; B 38 -14 722 676 ; -C -1 ; WX 722 ; N Agrave ; B 15 0 706 890 ; -C -1 ; WX 500 ; N odieresis ; B 29 -10 470 623 ; -C -1 ; WX 500 ; N oacute ; B 29 -10 470 678 ; -C -1 ; WX 400 ; N degree ; B 57 390 343 676 ; -C -1 ; WX 278 ; N igrave ; B -8 0 253 678 ; -C -1 ; WX 500 ; N mu ; B 36 -218 512 450 ; -C -1 ; WX 722 ; N Oacute ; B 34 -14 688 890 ; -C -1 ; WX 500 ; N eth ; B 29 -10 471 686 ; -C -1 ; WX 722 ; N Adieresis ; B 15 0 706 835 ; -C -1 ; WX 722 ; N Yacute ; B 22 0 703 890 ; -C -1 ; WX 200 ; N brokenbar ; B 67 -14 133 676 ; -C -1 ; WX 750 ; N onehalf ; B 31 -14 746 676 ; -EndCharMetrics -StartKernData -StartKernPairs 283 - -KPX A y -92 -KPX A w -92 -KPX A v -74 -KPX A u 0 -KPX A quoteright -111 -KPX A quotedblright 0 -KPX A p 0 -KPX A Y -105 -KPX A W -90 -KPX A V -135 -KPX A U -55 -KPX A T -111 -KPX A Q -55 -KPX A O -55 -KPX A G -40 -KPX A C -40 - -KPX B period 0 -KPX B comma 0 -KPX B U -10 -KPX B A -35 - -KPX D period 0 -KPX D comma 0 -KPX D Y -55 -KPX D W -30 -KPX D V -40 -KPX D A -40 - -KPX F r 0 -KPX F period -80 -KPX F o -15 -KPX F i 0 -KPX F e 0 -KPX F comma -80 -KPX F a -15 -KPX F A -74 - -KPX G period 0 -KPX G comma 0 - -KPX J u 0 -KPX J period 0 -KPX J o 0 -KPX J e 0 -KPX J comma 0 -KPX J a 0 -KPX J A -60 - -KPX K y -25 -KPX K u -15 -KPX K o -35 -KPX K e -25 -KPX K O -30 - -KPX L y -55 -KPX L quoteright -92 -KPX L quotedblright 0 -KPX L Y -100 -KPX L W -74 -KPX L V -100 -KPX L T -92 - -KPX N period 0 -KPX N comma 0 -KPX N A -35 - -KPX O period 0 -KPX O comma 0 -KPX O Y -50 -KPX O X -40 -KPX O W -35 -KPX O V -50 -KPX O T -40 -KPX O A -35 - -KPX P period -111 -KPX P o 0 -KPX P e 0 -KPX P comma -111 -KPX P a -15 -KPX P A -92 - -KPX Q period 0 -KPX Q comma 0 -KPX Q U -10 - -KPX R Y -65 -KPX R W -55 -KPX R V -80 -KPX R U -40 -KPX R T -60 -KPX R O -40 - -KPX S period 0 -KPX S comma 0 - -KPX T y -80 -KPX T w -80 -KPX T u -45 -KPX T semicolon -55 -KPX T r -35 -KPX T period -74 -KPX T o -80 -KPX T i -35 -KPX T hyphen -92 -KPX T h 0 -KPX T e -70 -KPX T comma -74 -KPX T colon -50 -KPX T a -80 -KPX T O -18 -KPX T A -93 - -KPX U period 0 -KPX U comma 0 -KPX U A -40 - -KPX V u -75 -KPX V semicolon -74 -KPX V period -129 -KPX V o -129 -KPX V i -60 -KPX V hyphen -100 -KPX V e -111 -KPX V comma -129 -KPX V colon -74 -KPX V a -111 -KPX V O -40 -KPX V G -15 -KPX V A -135 - -KPX W y -73 -KPX W u -50 -KPX W semicolon -37 -KPX W period -92 -KPX W o -80 -KPX W i -40 -KPX W hyphen -65 -KPX W h 0 -KPX W e -80 -KPX W comma -92 -KPX W colon -37 -KPX W a -80 -KPX W O -10 -KPX W A -120 - -KPX Y u -111 -KPX Y semicolon -92 -KPX Y period -129 -KPX Y o -110 -KPX Y i -55 -KPX Y hyphen -111 -KPX Y e -100 -KPX Y comma -129 -KPX Y colon -92 -KPX Y a -100 -KPX Y O -30 -KPX Y A -120 - -KPX a y 0 -KPX a w -15 -KPX a v -20 -KPX a t 0 -KPX a p 0 -KPX a g 0 -KPX a b 0 - -KPX b y 0 -KPX b v -15 -KPX b u -20 -KPX b period -40 -KPX b l 0 -KPX b comma 0 -KPX b b 0 - -KPX c y -15 -KPX c period 0 -KPX c l 0 -KPX c k 0 -KPX c h 0 -KPX c comma 0 - -KPX colon space 0 - -KPX comma space 0 -KPX comma quoteright -70 -KPX comma quotedblright -70 - -KPX d y 0 -KPX d w 0 -KPX d v 0 -KPX d period 0 -KPX d d 0 -KPX d comma 0 - -KPX e y -15 -KPX e x -15 -KPX e w -25 -KPX e v -25 -KPX e period 0 -KPX e p 0 -KPX e g -15 -KPX e comma 0 -KPX e b 0 - -KPX f quoteright 55 -KPX f quotedblright 0 -KPX f period 0 -KPX f o 0 -KPX f l 0 -KPX f i -20 -KPX f f -25 -KPX f e 0 -KPX f dotlessi -50 -KPX f comma 0 -KPX f a -10 - -KPX g y 0 -KPX g r 0 -KPX g period 0 -KPX g o 0 -KPX g i 0 -KPX g g 0 -KPX g e 0 -KPX g comma 0 -KPX g a -5 - -KPX h y -5 - -KPX i v -25 - -KPX k y -15 -KPX k o -10 -KPX k e -10 - -KPX l y 0 -KPX l w -10 - -KPX m y 0 -KPX m u 0 - -KPX n y -15 -KPX n v -40 -KPX n u 0 - -KPX o y -10 -KPX o x 0 -KPX o w -25 -KPX o v -15 -KPX o g 0 - -KPX p y -10 - -KPX period quoteright -70 -KPX period quotedblright -70 - -KPX quotedblleft quoteleft 0 -KPX quotedblleft A -80 - -KPX quotedblright space 0 - -KPX quoteleft quoteleft -74 -KPX quoteleft A -80 - -KPX quoteright v -50 -KPX quoteright t -18 -KPX quoteright space -74 -KPX quoteright s -55 -KPX quoteright r -50 -KPX quoteright quoteright -74 -KPX quoteright quotedblright 0 -KPX quoteright l -10 -KPX quoteright d -50 - -KPX r y 0 -KPX r v 0 -KPX r u 0 -KPX r t 0 -KPX r s 0 -KPX r r 0 -KPX r q 0 -KPX r period -55 -KPX r p 0 -KPX r o 0 -KPX r n 0 -KPX r m 0 -KPX r l 0 -KPX r k 0 -KPX r i 0 -KPX r hyphen -20 -KPX r g -18 -KPX r e 0 -KPX r d 0 -KPX r comma -40 -KPX r c 0 -KPX r a 0 - -KPX s w 0 - -KPX space quoteleft 0 -KPX space quotedblleft 0 -KPX space Y -90 -KPX space W -30 -KPX space V -50 -KPX space T -18 -KPX space A -55 - -KPX v period -65 -KPX v o -20 -KPX v e -15 -KPX v comma -65 -KPX v a -25 - -KPX w period -65 -KPX w o -10 -KPX w h 0 -KPX w e 0 -KPX w comma -65 -KPX w a -10 - -KPX x e -15 - -KPX y period -65 -KPX y o 0 -KPX y e 0 -KPX y comma -65 -KPX y a 0 - -KPX z o 0 -KPX z e 0 -EndKernPairs -EndKernData -StartComposites 58 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 195 212 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 195 212 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 195 212 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 195 212 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 185 187 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 195 212 ; -CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 167 0 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 139 212 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 139 212 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 139 212 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 139 212 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 0 212 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 0 212 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 0 212 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 0 212 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 195 212 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 195 212 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 195 212 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 195 212 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 195 212 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 195 212 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 112 212 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 195 212 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 195 212 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 195 212 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 195 212 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 195 212 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 195 212 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 139 212 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 56 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 56 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 56 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 56 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 56 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 56 0 ; -CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 56 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 56 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 56 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 56 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 56 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -27 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -27 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -27 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -27 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 84 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 84 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 84 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 84 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 84 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 84 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 28 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 84 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 84 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 84 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 84 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 84 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 84 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 56 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/ptmri8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/ptmri8a.afm deleted file mode 100644 index 6d7a003..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/ptmri8a.afm +++ /dev/null @@ -1,648 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Tue Mar 20 13:14:56 1990 -Comment UniqueID 28427 -Comment VMusage 32912 39804 -FontName Times-Italic -FullName Times Italic -FamilyName Times -Weight Medium -ItalicAngle -15.5 -IsFixedPitch false -FontBBox -169 -217 1010 883 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.007 -Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved.Times is a trademark of Linotype AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 653 -XHeight 441 -Ascender 683 -Descender -205 -StartCharMetrics 228 -C 32 ; WX 250 ; N space ; B 0 0 0 0 ; -C 33 ; WX 333 ; N exclam ; B 39 -11 302 667 ; -C 34 ; WX 420 ; N quotedbl ; B 144 421 432 666 ; -C 35 ; WX 500 ; N numbersign ; B 2 0 540 676 ; -C 36 ; WX 500 ; N dollar ; B 31 -89 497 731 ; -C 37 ; WX 833 ; N percent ; B 79 -13 790 676 ; -C 38 ; WX 778 ; N ampersand ; B 76 -18 723 666 ; -C 39 ; WX 333 ; N quoteright ; B 151 436 290 666 ; -C 40 ; WX 333 ; N parenleft ; B 42 -181 315 669 ; -C 41 ; WX 333 ; N parenright ; B 16 -180 289 669 ; -C 42 ; WX 500 ; N asterisk ; B 128 255 492 666 ; -C 43 ; WX 675 ; N plus ; B 86 0 590 506 ; -C 44 ; WX 250 ; N comma ; B -4 -129 135 101 ; -C 45 ; WX 333 ; N hyphen ; B 49 192 282 255 ; -C 46 ; WX 250 ; N period ; B 27 -11 138 100 ; -C 47 ; WX 278 ; N slash ; B -65 -18 386 666 ; -C 48 ; WX 500 ; N zero ; B 32 -7 497 676 ; -C 49 ; WX 500 ; N one ; B 49 0 409 676 ; -C 50 ; WX 500 ; N two ; B 12 0 452 676 ; -C 51 ; WX 500 ; N three ; B 15 -7 465 676 ; -C 52 ; WX 500 ; N four ; B 1 0 479 676 ; -C 53 ; WX 500 ; N five ; B 15 -7 491 666 ; -C 54 ; WX 500 ; N six ; B 30 -7 521 686 ; -C 55 ; WX 500 ; N seven ; B 75 -8 537 666 ; -C 56 ; WX 500 ; N eight ; B 30 -7 493 676 ; -C 57 ; WX 500 ; N nine ; B 23 -17 492 676 ; -C 58 ; WX 333 ; N colon ; B 50 -11 261 441 ; -C 59 ; WX 333 ; N semicolon ; B 27 -129 261 441 ; -C 60 ; WX 675 ; N less ; B 84 -8 592 514 ; -C 61 ; WX 675 ; N equal ; B 86 120 590 386 ; -C 62 ; WX 675 ; N greater ; B 84 -8 592 514 ; -C 63 ; WX 500 ; N question ; B 132 -12 472 664 ; -C 64 ; WX 920 ; N at ; B 118 -18 806 666 ; -C 65 ; WX 611 ; N A ; B -51 0 564 668 ; -C 66 ; WX 611 ; N B ; B -8 0 588 653 ; -C 67 ; WX 667 ; N C ; B 66 -18 689 666 ; -C 68 ; WX 722 ; N D ; B -8 0 700 653 ; -C 69 ; WX 611 ; N E ; B -1 0 634 653 ; -C 70 ; WX 611 ; N F ; B 8 0 645 653 ; -C 71 ; WX 722 ; N G ; B 52 -18 722 666 ; -C 72 ; WX 722 ; N H ; B -8 0 767 653 ; -C 73 ; WX 333 ; N I ; B -8 0 384 653 ; -C 74 ; WX 444 ; N J ; B -6 -18 491 653 ; -C 75 ; WX 667 ; N K ; B 7 0 722 653 ; -C 76 ; WX 556 ; N L ; B -8 0 559 653 ; -C 77 ; WX 833 ; N M ; B -18 0 873 653 ; -C 78 ; WX 667 ; N N ; B -20 -15 727 653 ; -C 79 ; WX 722 ; N O ; B 60 -18 699 666 ; -C 80 ; WX 611 ; N P ; B 0 0 605 653 ; -C 81 ; WX 722 ; N Q ; B 59 -182 699 666 ; -C 82 ; WX 611 ; N R ; B -13 0 588 653 ; -C 83 ; WX 500 ; N S ; B 17 -18 508 667 ; -C 84 ; WX 556 ; N T ; B 59 0 633 653 ; -C 85 ; WX 722 ; N U ; B 102 -18 765 653 ; -C 86 ; WX 611 ; N V ; B 76 -18 688 653 ; -C 87 ; WX 833 ; N W ; B 71 -18 906 653 ; -C 88 ; WX 611 ; N X ; B -29 0 655 653 ; -C 89 ; WX 556 ; N Y ; B 78 0 633 653 ; -C 90 ; WX 556 ; N Z ; B -6 0 606 653 ; -C 91 ; WX 389 ; N bracketleft ; B 21 -153 391 663 ; -C 92 ; WX 278 ; N backslash ; B -41 -18 319 666 ; -C 93 ; WX 389 ; N bracketright ; B 12 -153 382 663 ; -C 94 ; WX 422 ; N asciicircum ; B 0 301 422 666 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 333 ; N quoteleft ; B 171 436 310 666 ; -C 97 ; WX 500 ; N a ; B 17 -11 476 441 ; -C 98 ; WX 500 ; N b ; B 23 -11 473 683 ; -C 99 ; WX 444 ; N c ; B 30 -11 425 441 ; -C 100 ; WX 500 ; N d ; B 15 -13 527 683 ; -C 101 ; WX 444 ; N e ; B 31 -11 412 441 ; -C 102 ; WX 278 ; N f ; B -147 -207 424 678 ; L i fi ; L l fl ; -C 103 ; WX 500 ; N g ; B 8 -206 472 441 ; -C 104 ; WX 500 ; N h ; B 19 -9 478 683 ; -C 105 ; WX 278 ; N i ; B 49 -11 264 654 ; -C 106 ; WX 278 ; N j ; B -124 -207 276 654 ; -C 107 ; WX 444 ; N k ; B 14 -11 461 683 ; -C 108 ; WX 278 ; N l ; B 41 -11 279 683 ; -C 109 ; WX 722 ; N m ; B 12 -9 704 441 ; -C 110 ; WX 500 ; N n ; B 14 -9 474 441 ; -C 111 ; WX 500 ; N o ; B 27 -11 468 441 ; -C 112 ; WX 500 ; N p ; B -75 -205 469 441 ; -C 113 ; WX 500 ; N q ; B 25 -209 483 441 ; -C 114 ; WX 389 ; N r ; B 45 0 412 441 ; -C 115 ; WX 389 ; N s ; B 16 -13 366 442 ; -C 116 ; WX 278 ; N t ; B 37 -11 296 546 ; -C 117 ; WX 500 ; N u ; B 42 -11 475 441 ; -C 118 ; WX 444 ; N v ; B 21 -18 426 441 ; -C 119 ; WX 667 ; N w ; B 16 -18 648 441 ; -C 120 ; WX 444 ; N x ; B -27 -11 447 441 ; -C 121 ; WX 444 ; N y ; B -24 -206 426 441 ; -C 122 ; WX 389 ; N z ; B -2 -81 380 428 ; -C 123 ; WX 400 ; N braceleft ; B 51 -177 407 687 ; -C 124 ; WX 275 ; N bar ; B 105 -18 171 666 ; -C 125 ; WX 400 ; N braceright ; B -7 -177 349 687 ; -C 126 ; WX 541 ; N asciitilde ; B 40 183 502 323 ; -C 161 ; WX 389 ; N exclamdown ; B 59 -205 322 473 ; -C 162 ; WX 500 ; N cent ; B 77 -143 472 560 ; -C 163 ; WX 500 ; N sterling ; B 10 -6 517 670 ; -C 164 ; WX 167 ; N fraction ; B -169 -10 337 676 ; -C 165 ; WX 500 ; N yen ; B 27 0 603 653 ; -C 166 ; WX 500 ; N florin ; B 25 -182 507 682 ; -C 167 ; WX 500 ; N section ; B 53 -162 461 666 ; -C 168 ; WX 500 ; N currency ; B -22 53 522 597 ; -C 169 ; WX 214 ; N quotesingle ; B 132 421 241 666 ; -C 170 ; WX 556 ; N quotedblleft ; B 166 436 514 666 ; -C 171 ; WX 500 ; N guillemotleft ; B 53 37 445 403 ; -C 172 ; WX 333 ; N guilsinglleft ; B 51 37 281 403 ; -C 173 ; WX 333 ; N guilsinglright ; B 52 37 282 403 ; -C 174 ; WX 500 ; N fi ; B -141 -207 481 681 ; -C 175 ; WX 500 ; N fl ; B -141 -204 518 682 ; -C 177 ; WX 500 ; N endash ; B -6 197 505 243 ; -C 178 ; WX 500 ; N dagger ; B 101 -159 488 666 ; -C 179 ; WX 500 ; N daggerdbl ; B 22 -143 491 666 ; -C 180 ; WX 250 ; N periodcentered ; B 70 199 181 310 ; -C 182 ; WX 523 ; N paragraph ; B 55 -123 616 653 ; -C 183 ; WX 350 ; N bullet ; B 40 191 310 461 ; -C 184 ; WX 333 ; N quotesinglbase ; B 44 -129 183 101 ; -C 185 ; WX 556 ; N quotedblbase ; B 57 -129 405 101 ; -C 186 ; WX 556 ; N quotedblright ; B 151 436 499 666 ; -C 187 ; WX 500 ; N guillemotright ; B 55 37 447 403 ; -C 188 ; WX 889 ; N ellipsis ; B 57 -11 762 100 ; -C 189 ; WX 1000 ; N perthousand ; B 25 -19 1010 706 ; -C 191 ; WX 500 ; N questiondown ; B 28 -205 368 471 ; -C 193 ; WX 333 ; N grave ; B 121 492 311 664 ; -C 194 ; WX 333 ; N acute ; B 180 494 403 664 ; -C 195 ; WX 333 ; N circumflex ; B 91 492 385 661 ; -C 196 ; WX 333 ; N tilde ; B 100 517 427 624 ; -C 197 ; WX 333 ; N macron ; B 99 532 411 583 ; -C 198 ; WX 333 ; N breve ; B 117 492 418 650 ; -C 199 ; WX 333 ; N dotaccent ; B 207 508 305 606 ; -C 200 ; WX 333 ; N dieresis ; B 107 508 405 606 ; -C 202 ; WX 333 ; N ring ; B 155 492 355 691 ; -C 203 ; WX 333 ; N cedilla ; B -30 -217 182 0 ; -C 205 ; WX 333 ; N hungarumlaut ; B 93 494 486 664 ; -C 206 ; WX 333 ; N ogonek ; B -20 -169 200 40 ; -C 207 ; WX 333 ; N caron ; B 121 492 426 661 ; -C 208 ; WX 889 ; N emdash ; B -6 197 894 243 ; -C 225 ; WX 889 ; N AE ; B -27 0 911 653 ; -C 227 ; WX 276 ; N ordfeminine ; B 42 406 352 676 ; -C 232 ; WX 556 ; N Lslash ; B -8 0 559 653 ; -C 233 ; WX 722 ; N Oslash ; B 60 -105 699 722 ; -C 234 ; WX 944 ; N OE ; B 49 -8 964 666 ; -C 235 ; WX 310 ; N ordmasculine ; B 67 406 362 676 ; -C 241 ; WX 667 ; N ae ; B 23 -11 640 441 ; -C 245 ; WX 278 ; N dotlessi ; B 49 -11 235 441 ; -C 248 ; WX 278 ; N lslash ; B 37 -11 307 683 ; -C 249 ; WX 500 ; N oslash ; B 28 -135 469 554 ; -C 250 ; WX 667 ; N oe ; B 20 -12 646 441 ; -C 251 ; WX 500 ; N germandbls ; B -168 -207 493 679 ; -C -1 ; WX 556 ; N Zcaron ; B -6 0 606 873 ; -C -1 ; WX 444 ; N ccedilla ; B 26 -217 425 441 ; -C -1 ; WX 444 ; N ydieresis ; B -24 -206 441 606 ; -C -1 ; WX 500 ; N atilde ; B 17 -11 511 624 ; -C -1 ; WX 278 ; N icircumflex ; B 34 -11 328 661 ; -C -1 ; WX 300 ; N threesuperior ; B 43 268 339 676 ; -C -1 ; WX 444 ; N ecircumflex ; B 31 -11 441 661 ; -C -1 ; WX 500 ; N thorn ; B -75 -205 469 683 ; -C -1 ; WX 444 ; N egrave ; B 31 -11 412 664 ; -C -1 ; WX 300 ; N twosuperior ; B 33 271 324 676 ; -C -1 ; WX 444 ; N eacute ; B 31 -11 459 664 ; -C -1 ; WX 500 ; N otilde ; B 27 -11 496 624 ; -C -1 ; WX 611 ; N Aacute ; B -51 0 564 876 ; -C -1 ; WX 500 ; N ocircumflex ; B 27 -11 468 661 ; -C -1 ; WX 444 ; N yacute ; B -24 -206 459 664 ; -C -1 ; WX 500 ; N udieresis ; B 42 -11 479 606 ; -C -1 ; WX 750 ; N threequarters ; B 23 -10 736 676 ; -C -1 ; WX 500 ; N acircumflex ; B 17 -11 476 661 ; -C -1 ; WX 722 ; N Eth ; B -8 0 700 653 ; -C -1 ; WX 444 ; N edieresis ; B 31 -11 451 606 ; -C -1 ; WX 500 ; N ugrave ; B 42 -11 475 664 ; -C -1 ; WX 980 ; N trademark ; B 30 247 957 653 ; -C -1 ; WX 500 ; N ograve ; B 27 -11 468 664 ; -C -1 ; WX 389 ; N scaron ; B 16 -13 454 661 ; -C -1 ; WX 333 ; N Idieresis ; B -8 0 435 818 ; -C -1 ; WX 500 ; N uacute ; B 42 -11 477 664 ; -C -1 ; WX 500 ; N agrave ; B 17 -11 476 664 ; -C -1 ; WX 500 ; N ntilde ; B 14 -9 476 624 ; -C -1 ; WX 500 ; N aring ; B 17 -11 476 691 ; -C -1 ; WX 389 ; N zcaron ; B -2 -81 434 661 ; -C -1 ; WX 333 ; N Icircumflex ; B -8 0 425 873 ; -C -1 ; WX 667 ; N Ntilde ; B -20 -15 727 836 ; -C -1 ; WX 500 ; N ucircumflex ; B 42 -11 475 661 ; -C -1 ; WX 611 ; N Ecircumflex ; B -1 0 634 873 ; -C -1 ; WX 333 ; N Iacute ; B -8 0 413 876 ; -C -1 ; WX 667 ; N Ccedilla ; B 66 -217 689 666 ; -C -1 ; WX 722 ; N Odieresis ; B 60 -18 699 818 ; -C -1 ; WX 500 ; N Scaron ; B 17 -18 520 873 ; -C -1 ; WX 611 ; N Edieresis ; B -1 0 634 818 ; -C -1 ; WX 333 ; N Igrave ; B -8 0 384 876 ; -C -1 ; WX 500 ; N adieresis ; B 17 -11 489 606 ; -C -1 ; WX 722 ; N Ograve ; B 60 -18 699 876 ; -C -1 ; WX 611 ; N Egrave ; B -1 0 634 876 ; -C -1 ; WX 556 ; N Ydieresis ; B 78 0 633 818 ; -C -1 ; WX 760 ; N registered ; B 41 -18 719 666 ; -C -1 ; WX 722 ; N Otilde ; B 60 -18 699 836 ; -C -1 ; WX 750 ; N onequarter ; B 33 -10 736 676 ; -C -1 ; WX 722 ; N Ugrave ; B 102 -18 765 876 ; -C -1 ; WX 722 ; N Ucircumflex ; B 102 -18 765 873 ; -C -1 ; WX 611 ; N Thorn ; B 0 0 569 653 ; -C -1 ; WX 675 ; N divide ; B 86 -11 590 517 ; -C -1 ; WX 611 ; N Atilde ; B -51 0 566 836 ; -C -1 ; WX 722 ; N Uacute ; B 102 -18 765 876 ; -C -1 ; WX 722 ; N Ocircumflex ; B 60 -18 699 873 ; -C -1 ; WX 675 ; N logicalnot ; B 86 108 590 386 ; -C -1 ; WX 611 ; N Aring ; B -51 0 564 883 ; -C -1 ; WX 278 ; N idieresis ; B 49 -11 353 606 ; -C -1 ; WX 278 ; N iacute ; B 49 -11 356 664 ; -C -1 ; WX 500 ; N aacute ; B 17 -11 487 664 ; -C -1 ; WX 675 ; N plusminus ; B 86 0 590 506 ; -C -1 ; WX 675 ; N multiply ; B 93 8 582 497 ; -C -1 ; WX 722 ; N Udieresis ; B 102 -18 765 818 ; -C -1 ; WX 675 ; N minus ; B 86 220 590 286 ; -C -1 ; WX 300 ; N onesuperior ; B 43 271 284 676 ; -C -1 ; WX 611 ; N Eacute ; B -1 0 634 876 ; -C -1 ; WX 611 ; N Acircumflex ; B -51 0 564 873 ; -C -1 ; WX 760 ; N copyright ; B 41 -18 719 666 ; -C -1 ; WX 611 ; N Agrave ; B -51 0 564 876 ; -C -1 ; WX 500 ; N odieresis ; B 27 -11 489 606 ; -C -1 ; WX 500 ; N oacute ; B 27 -11 487 664 ; -C -1 ; WX 400 ; N degree ; B 101 390 387 676 ; -C -1 ; WX 278 ; N igrave ; B 49 -11 284 664 ; -C -1 ; WX 500 ; N mu ; B -30 -209 497 428 ; -C -1 ; WX 722 ; N Oacute ; B 60 -18 699 876 ; -C -1 ; WX 500 ; N eth ; B 27 -11 482 683 ; -C -1 ; WX 611 ; N Adieresis ; B -51 0 564 818 ; -C -1 ; WX 556 ; N Yacute ; B 78 0 633 876 ; -C -1 ; WX 275 ; N brokenbar ; B 105 -18 171 666 ; -C -1 ; WX 750 ; N onehalf ; B 34 -10 749 676 ; -EndCharMetrics -StartKernData -StartKernPairs 283 - -KPX A y -55 -KPX A w -55 -KPX A v -55 -KPX A u -20 -KPX A quoteright -37 -KPX A quotedblright 0 -KPX A p 0 -KPX A Y -55 -KPX A W -95 -KPX A V -105 -KPX A U -50 -KPX A T -37 -KPX A Q -40 -KPX A O -40 -KPX A G -35 -KPX A C -30 - -KPX B period 0 -KPX B comma 0 -KPX B U -10 -KPX B A -25 - -KPX D period 0 -KPX D comma 0 -KPX D Y -40 -KPX D W -40 -KPX D V -40 -KPX D A -35 - -KPX F r -55 -KPX F period -135 -KPX F o -105 -KPX F i -45 -KPX F e -75 -KPX F comma -135 -KPX F a -75 -KPX F A -115 - -KPX G period 0 -KPX G comma 0 - -KPX J u -35 -KPX J period -25 -KPX J o -25 -KPX J e -25 -KPX J comma -25 -KPX J a -35 -KPX J A -40 - -KPX K y -40 -KPX K u -40 -KPX K o -40 -KPX K e -35 -KPX K O -50 - -KPX L y -30 -KPX L quoteright -37 -KPX L quotedblright 0 -KPX L Y -20 -KPX L W -55 -KPX L V -55 -KPX L T -20 - -KPX N period 0 -KPX N comma 0 -KPX N A -27 - -KPX O period 0 -KPX O comma 0 -KPX O Y -50 -KPX O X -40 -KPX O W -50 -KPX O V -50 -KPX O T -40 -KPX O A -55 - -KPX P period -135 -KPX P o -80 -KPX P e -80 -KPX P comma -135 -KPX P a -80 -KPX P A -90 - -KPX Q period 0 -KPX Q comma 0 -KPX Q U -10 - -KPX R Y -18 -KPX R W -18 -KPX R V -18 -KPX R U -40 -KPX R T 0 -KPX R O -40 - -KPX S period 0 -KPX S comma 0 - -KPX T y -74 -KPX T w -74 -KPX T u -55 -KPX T semicolon -65 -KPX T r -55 -KPX T period -74 -KPX T o -92 -KPX T i -55 -KPX T hyphen -74 -KPX T h 0 -KPX T e -92 -KPX T comma -74 -KPX T colon -55 -KPX T a -92 -KPX T O -18 -KPX T A -50 - -KPX U period -25 -KPX U comma -25 -KPX U A -40 - -KPX V u -74 -KPX V semicolon -74 -KPX V period -129 -KPX V o -111 -KPX V i -74 -KPX V hyphen -55 -KPX V e -111 -KPX V comma -129 -KPX V colon -65 -KPX V a -111 -KPX V O -30 -KPX V G 0 -KPX V A -60 - -KPX W y -70 -KPX W u -55 -KPX W semicolon -65 -KPX W period -92 -KPX W o -92 -KPX W i -55 -KPX W hyphen -37 -KPX W h 0 -KPX W e -92 -KPX W comma -92 -KPX W colon -65 -KPX W a -92 -KPX W O -25 -KPX W A -60 - -KPX Y u -92 -KPX Y semicolon -65 -KPX Y period -92 -KPX Y o -92 -KPX Y i -74 -KPX Y hyphen -74 -KPX Y e -92 -KPX Y comma -92 -KPX Y colon -65 -KPX Y a -92 -KPX Y O -15 -KPX Y A -50 - -KPX a y 0 -KPX a w 0 -KPX a v 0 -KPX a t 0 -KPX a p 0 -KPX a g -10 -KPX a b 0 - -KPX b y 0 -KPX b v 0 -KPX b u -20 -KPX b period -40 -KPX b l 0 -KPX b comma 0 -KPX b b 0 - -KPX c y 0 -KPX c period 0 -KPX c l 0 -KPX c k -20 -KPX c h -15 -KPX c comma 0 - -KPX colon space 0 - -KPX comma space 0 -KPX comma quoteright -140 -KPX comma quotedblright -140 - -KPX d y 0 -KPX d w 0 -KPX d v 0 -KPX d period 0 -KPX d d 0 -KPX d comma 0 - -KPX e y -30 -KPX e x -20 -KPX e w -15 -KPX e v -15 -KPX e period -15 -KPX e p 0 -KPX e g -40 -KPX e comma -10 -KPX e b 0 - -KPX f quoteright 92 -KPX f quotedblright 0 -KPX f period -15 -KPX f o 0 -KPX f l 0 -KPX f i -20 -KPX f f -18 -KPX f e 0 -KPX f dotlessi -60 -KPX f comma -10 -KPX f a 0 - -KPX g y 0 -KPX g r 0 -KPX g period -15 -KPX g o 0 -KPX g i 0 -KPX g g -10 -KPX g e -10 -KPX g comma -10 -KPX g a 0 - -KPX h y 0 - -KPX i v 0 - -KPX k y -10 -KPX k o -10 -KPX k e -10 - -KPX l y 0 -KPX l w 0 - -KPX m y 0 -KPX m u 0 - -KPX n y 0 -KPX n v -40 -KPX n u 0 - -KPX o y 0 -KPX o x 0 -KPX o w 0 -KPX o v -10 -KPX o g -10 - -KPX p y 0 - -KPX period quoteright -140 -KPX period quotedblright -140 - -KPX quotedblleft quoteleft 0 -KPX quotedblleft A 0 - -KPX quotedblright space 0 - -KPX quoteleft quoteleft -111 -KPX quoteleft A 0 - -KPX quoteright v -10 -KPX quoteright t -30 -KPX quoteright space -111 -KPX quoteright s -40 -KPX quoteright r -25 -KPX quoteright quoteright -111 -KPX quoteright quotedblright 0 -KPX quoteright l 0 -KPX quoteright d -25 - -KPX r y 0 -KPX r v 0 -KPX r u 0 -KPX r t 0 -KPX r s -10 -KPX r r 0 -KPX r q -37 -KPX r period -111 -KPX r p 0 -KPX r o -45 -KPX r n 0 -KPX r m 0 -KPX r l 0 -KPX r k 0 -KPX r i 0 -KPX r hyphen -20 -KPX r g -37 -KPX r e -37 -KPX r d -37 -KPX r comma -111 -KPX r c -37 -KPX r a -15 - -KPX s w 0 - -KPX space quoteleft 0 -KPX space quotedblleft 0 -KPX space Y -75 -KPX space W -40 -KPX space V -35 -KPX space T -18 -KPX space A -18 - -KPX v period -74 -KPX v o 0 -KPX v e 0 -KPX v comma -74 -KPX v a 0 - -KPX w period -74 -KPX w o 0 -KPX w h 0 -KPX w e 0 -KPX w comma -74 -KPX w a 0 - -KPX x e 0 - -KPX y period -55 -KPX y o 0 -KPX y e 0 -KPX y comma -55 -KPX y a 0 - -KPX z o 0 -KPX z e 0 -EndKernPairs -EndKernData -StartComposites 58 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 139 212 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 144 212 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 139 212 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 149 212 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 129 192 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 139 212 ; -CC Ccedilla 2 ; PCC C 0 0 ; PCC cedilla 167 0 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 149 212 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 169 212 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 159 212 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 149 212 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 10 212 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 40 212 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 30 212 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 10 212 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 177 212 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 195 212 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 230 212 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 230 212 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 205 212 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 215 212 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 94 212 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 195 212 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 215 212 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 225 212 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 215 212 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 132 212 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 142 212 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 112 212 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 84 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 84 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 84 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 84 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 84 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 84 0 ; -CC ccedilla 2 ; PCC c 0 0 ; PCC cedilla 56 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 56 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex 56 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis 46 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 56 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -47 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -57 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -52 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -27 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 49 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 84 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 74 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 84 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 84 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde 69 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron 28 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 74 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 74 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 74 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 84 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 56 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 36 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 8 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/putb8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/putb8a.afm deleted file mode 100644 index 2eaa540..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/putb8a.afm +++ /dev/null @@ -1,1005 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1989, 1991 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Fri Jan 17 15:08:52 1992 -Comment UniqueID 37705 -Comment VMusage 33078 39970 -FontName Utopia-Bold -FullName Utopia Bold -FamilyName Utopia -Weight Bold -ItalicAngle 0 -IsFixedPitch false -FontBBox -155 -250 1249 916 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.002 -Notice Copyright (c) 1989, 1991 Adobe Systems Incorporated. All Rights Reserved.Utopia is a registered trademark of Adobe Systems Incorporated. -EncodingScheme AdobeStandardEncoding -CapHeight 692 -XHeight 490 -Ascender 742 -Descender -230 -StartCharMetrics 228 -C 32 ; WX 210 ; N space ; B 0 0 0 0 ; -C 33 ; WX 278 ; N exclam ; B 47 -12 231 707 ; -C 34 ; WX 473 ; N quotedbl ; B 71 407 402 707 ; -C 35 ; WX 560 ; N numbersign ; B 14 0 547 668 ; -C 36 ; WX 560 ; N dollar ; B 38 -104 524 748 ; -C 37 ; WX 887 ; N percent ; B 40 -31 847 701 ; -C 38 ; WX 748 ; N ampersand ; B 45 -12 734 680 ; -C 39 ; WX 252 ; N quoteright ; B 40 387 212 707 ; -C 40 ; WX 365 ; N parenleft ; B 99 -135 344 699 ; -C 41 ; WX 365 ; N parenright ; B 21 -135 266 699 ; -C 42 ; WX 442 ; N asterisk ; B 40 315 402 707 ; -C 43 ; WX 600 ; N plus ; B 58 0 542 490 ; -C 44 ; WX 280 ; N comma ; B 40 -167 226 180 ; -C 45 ; WX 392 ; N hyphen ; B 65 203 328 298 ; -C 46 ; WX 280 ; N period ; B 48 -12 232 174 ; -C 47 ; WX 378 ; N slash ; B 34 -15 344 707 ; -C 48 ; WX 560 ; N zero ; B 31 -12 530 680 ; -C 49 ; WX 560 ; N one ; B 102 0 459 680 ; -C 50 ; WX 560 ; N two ; B 30 0 539 680 ; -C 51 ; WX 560 ; N three ; B 27 -12 519 680 ; -C 52 ; WX 560 ; N four ; B 19 0 533 668 ; -C 53 ; WX 560 ; N five ; B 43 -12 519 668 ; -C 54 ; WX 560 ; N six ; B 30 -12 537 680 ; -C 55 ; WX 560 ; N seven ; B 34 -12 530 668 ; -C 56 ; WX 560 ; N eight ; B 27 -12 533 680 ; -C 57 ; WX 560 ; N nine ; B 34 -12 523 680 ; -C 58 ; WX 280 ; N colon ; B 48 -12 232 490 ; -C 59 ; WX 280 ; N semicolon ; B 40 -167 232 490 ; -C 60 ; WX 600 ; N less ; B 61 5 539 493 ; -C 61 ; WX 600 ; N equal ; B 58 103 542 397 ; -C 62 ; WX 600 ; N greater ; B 61 5 539 493 ; -C 63 ; WX 456 ; N question ; B 20 -12 433 707 ; -C 64 ; WX 833 ; N at ; B 45 -15 797 707 ; -C 65 ; WX 644 ; N A ; B -28 0 663 692 ; -C 66 ; WX 683 ; N B ; B 33 0 645 692 ; -C 67 ; WX 689 ; N C ; B 42 -15 654 707 ; -C 68 ; WX 777 ; N D ; B 33 0 735 692 ; -C 69 ; WX 629 ; N E ; B 33 0 604 692 ; -C 70 ; WX 593 ; N F ; B 37 0 568 692 ; -C 71 ; WX 726 ; N G ; B 42 -15 709 707 ; -C 72 ; WX 807 ; N H ; B 33 0 774 692 ; -C 73 ; WX 384 ; N I ; B 33 0 351 692 ; -C 74 ; WX 386 ; N J ; B 6 -114 361 692 ; -C 75 ; WX 707 ; N K ; B 33 -6 719 692 ; -C 76 ; WX 585 ; N L ; B 33 0 584 692 ; -C 77 ; WX 918 ; N M ; B 23 0 885 692 ; -C 78 ; WX 739 ; N N ; B 25 0 719 692 ; -C 79 ; WX 768 ; N O ; B 42 -15 726 707 ; -C 80 ; WX 650 ; N P ; B 33 0 623 692 ; -C 81 ; WX 768 ; N Q ; B 42 -193 726 707 ; -C 82 ; WX 684 ; N R ; B 33 0 686 692 ; -C 83 ; WX 561 ; N S ; B 42 -15 533 707 ; -C 84 ; WX 624 ; N T ; B 15 0 609 692 ; -C 85 ; WX 786 ; N U ; B 29 -15 757 692 ; -C 86 ; WX 645 ; N V ; B -16 0 679 692 ; -C 87 ; WX 933 ; N W ; B -10 0 960 692 ; -C 88 ; WX 634 ; N X ; B -19 0 671 692 ; -C 89 ; WX 617 ; N Y ; B -12 0 655 692 ; -C 90 ; WX 614 ; N Z ; B 0 0 606 692 ; -C 91 ; WX 335 ; N bracketleft ; B 123 -128 308 692 ; -C 92 ; WX 379 ; N backslash ; B 34 -15 345 707 ; -C 93 ; WX 335 ; N bracketright ; B 27 -128 212 692 ; -C 94 ; WX 600 ; N asciicircum ; B 56 215 544 668 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 252 ; N quoteleft ; B 40 399 212 719 ; -C 97 ; WX 544 ; N a ; B 41 -12 561 502 ; -C 98 ; WX 605 ; N b ; B 15 -12 571 742 ; -C 99 ; WX 494 ; N c ; B 34 -12 484 502 ; -C 100 ; WX 605 ; N d ; B 34 -12 596 742 ; -C 101 ; WX 519 ; N e ; B 34 -12 505 502 ; -C 102 ; WX 342 ; N f ; B 27 0 421 742 ; L i fi ; L l fl ; -C 103 ; WX 533 ; N g ; B 25 -242 546 512 ; -C 104 ; WX 631 ; N h ; B 19 0 622 742 ; -C 105 ; WX 316 ; N i ; B 26 0 307 720 ; -C 106 ; WX 316 ; N j ; B -12 -232 260 720 ; -C 107 ; WX 582 ; N k ; B 19 0 595 742 ; -C 108 ; WX 309 ; N l ; B 19 0 300 742 ; -C 109 ; WX 948 ; N m ; B 26 0 939 502 ; -C 110 ; WX 638 ; N n ; B 26 0 629 502 ; -C 111 ; WX 585 ; N o ; B 34 -12 551 502 ; -C 112 ; WX 615 ; N p ; B 19 -230 581 502 ; -C 113 ; WX 597 ; N q ; B 34 -230 596 502 ; -C 114 ; WX 440 ; N r ; B 26 0 442 502 ; -C 115 ; WX 446 ; N s ; B 38 -12 425 502 ; -C 116 ; WX 370 ; N t ; B 32 -12 373 616 ; -C 117 ; WX 629 ; N u ; B 23 -12 620 502 ; -C 118 ; WX 520 ; N v ; B -8 0 546 490 ; -C 119 ; WX 774 ; N w ; B -10 0 802 490 ; -C 120 ; WX 522 ; N x ; B -15 0 550 490 ; -C 121 ; WX 524 ; N y ; B -12 -242 557 490 ; -C 122 ; WX 483 ; N z ; B -1 0 480 490 ; -C 123 ; WX 365 ; N braceleft ; B 74 -128 325 692 ; -C 124 ; WX 284 ; N bar ; B 94 -250 190 750 ; -C 125 ; WX 365 ; N braceright ; B 40 -128 291 692 ; -C 126 ; WX 600 ; N asciitilde ; B 50 158 551 339 ; -C 161 ; WX 278 ; N exclamdown ; B 47 -217 231 502 ; -C 162 ; WX 560 ; N cent ; B 39 -15 546 678 ; -C 163 ; WX 560 ; N sterling ; B 21 0 555 679 ; -C 164 ; WX 100 ; N fraction ; B -155 -27 255 695 ; -C 165 ; WX 560 ; N yen ; B 3 0 562 668 ; -C 166 ; WX 560 ; N florin ; B -40 -135 562 691 ; -C 167 ; WX 566 ; N section ; B 35 -115 531 707 ; -C 168 ; WX 560 ; N currency ; B 21 73 539 596 ; -C 169 ; WX 252 ; N quotesingle ; B 57 407 196 707 ; -C 170 ; WX 473 ; N quotedblleft ; B 40 399 433 719 ; -C 171 ; WX 487 ; N guillemotleft ; B 40 37 452 464 ; -C 172 ; WX 287 ; N guilsinglleft ; B 40 37 252 464 ; -C 173 ; WX 287 ; N guilsinglright ; B 35 37 247 464 ; -C 174 ; WX 639 ; N fi ; B 27 0 630 742 ; -C 175 ; WX 639 ; N fl ; B 27 0 630 742 ; -C 177 ; WX 500 ; N endash ; B 0 209 500 292 ; -C 178 ; WX 510 ; N dagger ; B 35 -125 475 707 ; -C 179 ; WX 486 ; N daggerdbl ; B 35 -119 451 707 ; -C 180 ; WX 280 ; N periodcentered ; B 48 156 232 342 ; -C 182 ; WX 552 ; N paragraph ; B 35 -101 527 692 ; -C 183 ; WX 455 ; N bullet ; B 50 174 405 529 ; -C 184 ; WX 252 ; N quotesinglbase ; B 40 -153 212 167 ; -C 185 ; WX 473 ; N quotedblbase ; B 40 -153 433 167 ; -C 186 ; WX 473 ; N quotedblright ; B 40 387 433 707 ; -C 187 ; WX 487 ; N guillemotright ; B 35 37 447 464 ; -C 188 ; WX 1000 ; N ellipsis ; B 75 -12 925 174 ; -C 189 ; WX 1289 ; N perthousand ; B 40 -31 1249 701 ; -C 191 ; WX 456 ; N questiondown ; B 23 -217 436 502 ; -C 193 ; WX 430 ; N grave ; B 40 511 312 740 ; -C 194 ; WX 430 ; N acute ; B 119 511 391 740 ; -C 195 ; WX 430 ; N circumflex ; B 28 520 402 747 ; -C 196 ; WX 430 ; N tilde ; B 2 553 427 706 ; -C 197 ; WX 430 ; N macron ; B 60 587 371 674 ; -C 198 ; WX 430 ; N breve ; B 56 556 375 716 ; -C 199 ; WX 430 ; N dotaccent ; B 136 561 294 710 ; -C 200 ; WX 430 ; N dieresis ; B 16 561 414 710 ; -C 202 ; WX 430 ; N ring ; B 96 540 334 762 ; -C 203 ; WX 430 ; N cedilla ; B 136 -246 335 0 ; -C 205 ; WX 430 ; N hungarumlaut ; B 64 521 446 751 ; -C 206 ; WX 430 ; N ogonek ; B 105 -246 325 0 ; -C 207 ; WX 430 ; N caron ; B 28 520 402 747 ; -C 208 ; WX 1000 ; N emdash ; B 0 209 1000 292 ; -C 225 ; WX 879 ; N AE ; B -77 0 854 692 ; -C 227 ; WX 405 ; N ordfeminine ; B 28 265 395 590 ; -C 232 ; WX 591 ; N Lslash ; B 30 0 590 692 ; -C 233 ; WX 768 ; N Oslash ; B 42 -61 726 747 ; -C 234 ; WX 1049 ; N OE ; B 42 0 1024 692 ; -C 235 ; WX 427 ; N ordmasculine ; B 28 265 399 590 ; -C 241 ; WX 806 ; N ae ; B 41 -12 792 502 ; -C 245 ; WX 316 ; N dotlessi ; B 26 0 307 502 ; -C 248 ; WX 321 ; N lslash ; B 16 0 332 742 ; -C 249 ; WX 585 ; N oslash ; B 34 -51 551 535 ; -C 250 ; WX 866 ; N oe ; B 34 -12 852 502 ; -C 251 ; WX 662 ; N germandbls ; B 29 -12 647 742 ; -C -1 ; WX 402 ; N onesuperior ; B 71 272 324 680 ; -C -1 ; WX 600 ; N minus ; B 58 210 542 290 ; -C -1 ; WX 396 ; N degree ; B 35 360 361 680 ; -C -1 ; WX 585 ; N oacute ; B 34 -12 551 755 ; -C -1 ; WX 768 ; N Odieresis ; B 42 -15 726 881 ; -C -1 ; WX 585 ; N odieresis ; B 34 -12 551 710 ; -C -1 ; WX 629 ; N Eacute ; B 33 0 604 904 ; -C -1 ; WX 629 ; N ucircumflex ; B 23 -12 620 747 ; -C -1 ; WX 900 ; N onequarter ; B 73 -27 814 695 ; -C -1 ; WX 600 ; N logicalnot ; B 58 95 542 397 ; -C -1 ; WX 629 ; N Ecircumflex ; B 33 0 604 905 ; -C -1 ; WX 900 ; N onehalf ; B 53 -27 849 695 ; -C -1 ; WX 768 ; N Otilde ; B 42 -15 726 876 ; -C -1 ; WX 629 ; N uacute ; B 23 -12 620 740 ; -C -1 ; WX 519 ; N eacute ; B 34 -12 505 755 ; -C -1 ; WX 316 ; N iacute ; B 26 0 369 740 ; -C -1 ; WX 629 ; N Egrave ; B 33 0 604 904 ; -C -1 ; WX 316 ; N icircumflex ; B -28 0 346 747 ; -C -1 ; WX 629 ; N mu ; B 23 -242 620 502 ; -C -1 ; WX 284 ; N brokenbar ; B 94 -175 190 675 ; -C -1 ; WX 609 ; N thorn ; B 13 -230 575 722 ; -C -1 ; WX 644 ; N Aring ; B -28 0 663 872 ; -C -1 ; WX 524 ; N yacute ; B -12 -242 557 740 ; -C -1 ; WX 617 ; N Ydieresis ; B -12 0 655 881 ; -C -1 ; WX 1090 ; N trademark ; B 38 277 1028 692 ; -C -1 ; WX 800 ; N registered ; B 36 -15 764 707 ; -C -1 ; WX 585 ; N ocircumflex ; B 34 -12 551 747 ; -C -1 ; WX 644 ; N Agrave ; B -28 0 663 904 ; -C -1 ; WX 561 ; N Scaron ; B 42 -15 533 916 ; -C -1 ; WX 786 ; N Ugrave ; B 29 -15 757 904 ; -C -1 ; WX 629 ; N Edieresis ; B 33 0 604 881 ; -C -1 ; WX 786 ; N Uacute ; B 29 -15 757 904 ; -C -1 ; WX 585 ; N otilde ; B 34 -12 551 706 ; -C -1 ; WX 638 ; N ntilde ; B 26 0 629 706 ; -C -1 ; WX 524 ; N ydieresis ; B -12 -242 557 710 ; -C -1 ; WX 644 ; N Aacute ; B -28 0 663 904 ; -C -1 ; WX 585 ; N eth ; B 34 -12 551 742 ; -C -1 ; WX 544 ; N acircumflex ; B 41 -12 561 747 ; -C -1 ; WX 544 ; N aring ; B 41 -12 561 762 ; -C -1 ; WX 768 ; N Ograve ; B 42 -15 726 904 ; -C -1 ; WX 494 ; N ccedilla ; B 34 -246 484 502 ; -C -1 ; WX 600 ; N multiply ; B 75 20 525 476 ; -C -1 ; WX 600 ; N divide ; B 58 6 542 494 ; -C -1 ; WX 402 ; N twosuperior ; B 29 272 382 680 ; -C -1 ; WX 739 ; N Ntilde ; B 25 0 719 876 ; -C -1 ; WX 629 ; N ugrave ; B 23 -12 620 740 ; -C -1 ; WX 786 ; N Ucircumflex ; B 29 -15 757 905 ; -C -1 ; WX 644 ; N Atilde ; B -28 0 663 876 ; -C -1 ; WX 483 ; N zcaron ; B -1 0 480 747 ; -C -1 ; WX 316 ; N idieresis ; B -37 0 361 710 ; -C -1 ; WX 644 ; N Acircumflex ; B -28 0 663 905 ; -C -1 ; WX 384 ; N Icircumflex ; B 4 0 380 905 ; -C -1 ; WX 617 ; N Yacute ; B -12 0 655 904 ; -C -1 ; WX 768 ; N Oacute ; B 42 -15 726 904 ; -C -1 ; WX 644 ; N Adieresis ; B -28 0 663 881 ; -C -1 ; WX 614 ; N Zcaron ; B 0 0 606 916 ; -C -1 ; WX 544 ; N agrave ; B 41 -12 561 755 ; -C -1 ; WX 402 ; N threesuperior ; B 30 265 368 680 ; -C -1 ; WX 585 ; N ograve ; B 34 -12 551 755 ; -C -1 ; WX 900 ; N threequarters ; B 40 -27 842 695 ; -C -1 ; WX 783 ; N Eth ; B 35 0 741 692 ; -C -1 ; WX 600 ; N plusminus ; B 58 0 542 549 ; -C -1 ; WX 629 ; N udieresis ; B 23 -12 620 710 ; -C -1 ; WX 519 ; N edieresis ; B 34 -12 505 710 ; -C -1 ; WX 544 ; N aacute ; B 41 -12 561 755 ; -C -1 ; WX 316 ; N igrave ; B -47 0 307 740 ; -C -1 ; WX 384 ; N Idieresis ; B -13 0 397 881 ; -C -1 ; WX 544 ; N adieresis ; B 41 -12 561 710 ; -C -1 ; WX 384 ; N Iacute ; B 33 0 423 904 ; -C -1 ; WX 800 ; N copyright ; B 36 -15 764 707 ; -C -1 ; WX 384 ; N Igrave ; B -31 0 351 904 ; -C -1 ; WX 689 ; N Ccedilla ; B 42 -246 654 707 ; -C -1 ; WX 446 ; N scaron ; B 38 -12 425 747 ; -C -1 ; WX 519 ; N egrave ; B 34 -12 505 755 ; -C -1 ; WX 768 ; N Ocircumflex ; B 42 -15 726 905 ; -C -1 ; WX 640 ; N Thorn ; B 33 0 622 692 ; -C -1 ; WX 544 ; N atilde ; B 41 -12 561 706 ; -C -1 ; WX 786 ; N Udieresis ; B 29 -15 757 881 ; -C -1 ; WX 519 ; N ecircumflex ; B 34 -12 505 747 ; -EndCharMetrics -StartKernData -StartKernPairs 685 - -KPX A z 25 -KPX A y -40 -KPX A w -42 -KPX A v -48 -KPX A u -18 -KPX A t -12 -KPX A s 6 -KPX A quoteright -110 -KPX A quotedblright -80 -KPX A q -6 -KPX A p -18 -KPX A o -12 -KPX A e -6 -KPX A d -12 -KPX A c -12 -KPX A b -12 -KPX A a -6 -KPX A Y -64 -KPX A X -18 -KPX A W -54 -KPX A V -70 -KPX A U -40 -KPX A T -58 -KPX A Q -18 -KPX A O -18 -KPX A G -18 -KPX A C -18 - -KPX B y -18 -KPX B u -12 -KPX B r -12 -KPX B o -6 -KPX B l -15 -KPX B k -15 -KPX B i -12 -KPX B h -15 -KPX B e -6 -KPX B b -10 -KPX B a -12 -KPX B W -20 -KPX B V -20 -KPX B U -25 -KPX B T -20 - -KPX C z -5 -KPX C y -24 -KPX C u -18 -KPX C r -6 -KPX C o -12 -KPX C e -12 -KPX C a -16 -KPX C Q -6 -KPX C O -6 -KPX C G -6 -KPX C C -6 - -KPX D u -12 -KPX D r -12 -KPX D period -40 -KPX D o -5 -KPX D i -12 -KPX D h -18 -KPX D e -5 -KPX D comma -40 -KPX D a -15 -KPX D Y -60 -KPX D W -40 -KPX D V -40 - -KPX E y -30 -KPX E w -24 -KPX E v -24 -KPX E u -12 -KPX E t -18 -KPX E s -12 -KPX E r -4 -KPX E q -6 -KPX E period 10 -KPX E p -18 -KPX E o -6 -KPX E n -4 -KPX E m -4 -KPX E j -6 -KPX E i -6 -KPX E g -6 -KPX E e -6 -KPX E d -6 -KPX E comma 10 -KPX E c -6 -KPX E b -5 -KPX E a -4 -KPX E Y -6 -KPX E W -6 -KPX E V -6 - -KPX F y -18 -KPX F u -12 -KPX F r -36 -KPX F quoteright 20 -KPX F quotedblright 20 -KPX F period -150 -KPX F o -36 -KPX F l -12 -KPX F i -22 -KPX F e -36 -KPX F comma -150 -KPX F a -48 -KPX F A -60 - -KPX G y -12 -KPX G u -12 -KPX G r -18 -KPX G quotedblright -20 -KPX G n -18 -KPX G l -6 -KPX G i -12 -KPX G h -12 -KPX G a -12 - -KPX H y -24 -KPX H u -26 -KPX H o -30 -KPX H i -18 -KPX H e -30 -KPX H a -25 - -KPX I z -6 -KPX I y -6 -KPX I x -6 -KPX I w -18 -KPX I v -24 -KPX I u -26 -KPX I t -24 -KPX I s -18 -KPX I r -12 -KPX I p -26 -KPX I o -30 -KPX I n -18 -KPX I m -18 -KPX I l -6 -KPX I k -6 -KPX I h -6 -KPX I g -6 -KPX I f -6 -KPX I e -30 -KPX I d -30 -KPX I c -30 -KPX I b -6 -KPX I a -24 - -KPX J y -20 -KPX J u -36 -KPX J o -35 -KPX J i -20 -KPX J e -35 -KPX J bracketright 15 -KPX J braceright 15 -KPX J a -36 - -KPX K y -70 -KPX K w -60 -KPX K v -80 -KPX K u -42 -KPX K o -30 -KPX K l 10 -KPX K i 6 -KPX K h 10 -KPX K e -18 -KPX K a -6 -KPX K Q -36 -KPX K O -36 -KPX K G -36 -KPX K C -36 -KPX K A 20 - -KPX L y -52 -KPX L w -58 -KPX L u -12 -KPX L quoteright -130 -KPX L quotedblright -130 -KPX L l 6 -KPX L j -6 -KPX L Y -70 -KPX L W -78 -KPX L V -95 -KPX L U -32 -KPX L T -80 -KPX L Q -12 -KPX L O -12 -KPX L G -12 -KPX L C -12 -KPX L A 30 - -KPX M y -24 -KPX M u -36 -KPX M o -30 -KPX M n -6 -KPX M j -12 -KPX M i -12 -KPX M e -30 -KPX M d -30 -KPX M c -30 -KPX M a -25 - -KPX N y -24 -KPX N u -30 -KPX N o -30 -KPX N i -24 -KPX N e -30 -KPX N a -30 - -KPX O z -6 -KPX O u -6 -KPX O t -6 -KPX O s -6 -KPX O r -10 -KPX O q -6 -KPX O period -40 -KPX O p -10 -KPX O o -6 -KPX O n -10 -KPX O m -10 -KPX O l -15 -KPX O k -15 -KPX O i -6 -KPX O h -15 -KPX O g -6 -KPX O e -6 -KPX O d -6 -KPX O comma -40 -KPX O c -6 -KPX O b -15 -KPX O a -12 -KPX O Y -50 -KPX O X -40 -KPX O W -35 -KPX O V -35 -KPX O T -40 -KPX O A -30 - -KPX P y 10 -KPX P u -18 -KPX P t -6 -KPX P s -30 -KPX P r -12 -KPX P quoteright 20 -KPX P quotedblright 20 -KPX P period -200 -KPX P o -36 -KPX P n -12 -KPX P l -15 -KPX P i -6 -KPX P hyphen -30 -KPX P h -15 -KPX P e -36 -KPX P comma -200 -KPX P a -36 -KPX P I -20 -KPX P H -20 -KPX P E -20 -KPX P A -85 - -KPX Q u -6 -KPX Q a -18 -KPX Q Y -50 -KPX Q X -40 -KPX Q W -35 -KPX Q V -35 -KPX Q U -25 -KPX Q T -40 -KPX Q A -30 - -KPX R y -20 -KPX R u -12 -KPX R t -25 -KPX R quoteright -10 -KPX R quotedblright -10 -KPX R o -12 -KPX R e -18 -KPX R a -6 -KPX R Y -32 -KPX R X 20 -KPX R W -18 -KPX R V -26 -KPX R U -30 -KPX R T -20 -KPX R Q -10 -KPX R O -10 -KPX R G -10 -KPX R C -10 - -KPX S y -35 -KPX S w -30 -KPX S v -40 -KPX S u -24 -KPX S t -24 -KPX S r -10 -KPX S quoteright -15 -KPX S quotedblright -15 -KPX S p -24 -KPX S n -24 -KPX S m -24 -KPX S l -18 -KPX S k -24 -KPX S j -30 -KPX S i -12 -KPX S h -12 -KPX S a -18 - -KPX T z -64 -KPX T y -74 -KPX T w -72 -KPX T u -74 -KPX T semicolon -50 -KPX T s -82 -KPX T r -74 -KPX T quoteright 24 -KPX T quotedblright 24 -KPX T period -95 -KPX T parenright 40 -KPX T o -90 -KPX T m -72 -KPX T i -28 -KPX T hyphen -110 -KPX T endash -40 -KPX T emdash -60 -KPX T e -80 -KPX T comma -95 -KPX T bracketright 40 -KPX T braceright 30 -KPX T a -90 -KPX T Y 12 -KPX T X 10 -KPX T W 15 -KPX T V 6 -KPX T T 30 -KPX T S -12 -KPX T Q -25 -KPX T O -25 -KPX T G -25 -KPX T C -25 -KPX T A -52 - -KPX U z -35 -KPX U y -30 -KPX U x -30 -KPX U v -30 -KPX U t -36 -KPX U s -45 -KPX U r -50 -KPX U p -50 -KPX U n -50 -KPX U m -50 -KPX U l -12 -KPX U k -12 -KPX U i -22 -KPX U h -6 -KPX U g -40 -KPX U f -20 -KPX U d -40 -KPX U c -40 -KPX U b -12 -KPX U a -50 -KPX U A -50 - -KPX V y -36 -KPX V u -50 -KPX V semicolon -45 -KPX V r -75 -KPX V quoteright 50 -KPX V quotedblright 36 -KPX V period -135 -KPX V parenright 80 -KPX V o -70 -KPX V i 20 -KPX V hyphen -90 -KPX V emdash -20 -KPX V e -70 -KPX V comma -135 -KPX V colon -45 -KPX V bracketright 80 -KPX V braceright 80 -KPX V a -70 -KPX V Q -20 -KPX V O -20 -KPX V G -20 -KPX V C -20 -KPX V A -60 - -KPX W y -50 -KPX W u -46 -KPX W t -30 -KPX W semicolon -40 -KPX W r -50 -KPX W quoteright 40 -KPX W quotedblright 24 -KPX W period -100 -KPX W parenright 80 -KPX W o -60 -KPX W m -50 -KPX W i 5 -KPX W hyphen -70 -KPX W h 20 -KPX W e -60 -KPX W d -60 -KPX W comma -100 -KPX W colon -40 -KPX W bracketright 80 -KPX W braceright 70 -KPX W a -75 -KPX W T 30 -KPX W Q -20 -KPX W O -20 -KPX W G -20 -KPX W C -20 -KPX W A -58 - -KPX X y -40 -KPX X u -24 -KPX X quoteright 15 -KPX X e -6 -KPX X a -6 -KPX X Q -24 -KPX X O -30 -KPX X G -30 -KPX X C -30 -KPX X A 20 - -KPX Y v -50 -KPX Y u -65 -KPX Y t -46 -KPX Y semicolon -37 -KPX Y quoteright 50 -KPX Y quotedblright 36 -KPX Y q -100 -KPX Y period -90 -KPX Y parenright 60 -KPX Y o -90 -KPX Y l 25 -KPX Y i 15 -KPX Y hyphen -100 -KPX Y endash -30 -KPX Y emdash -50 -KPX Y e -90 -KPX Y d -90 -KPX Y comma -90 -KPX Y colon -60 -KPX Y bracketright 80 -KPX Y braceright 64 -KPX Y a -80 -KPX Y Y 12 -KPX Y X 12 -KPX Y W 12 -KPX Y V 12 -KPX Y T 30 -KPX Y Q -40 -KPX Y O -40 -KPX Y G -40 -KPX Y C -40 -KPX Y A -55 - -KPX Z y -36 -KPX Z w -36 -KPX Z u -6 -KPX Z o -12 -KPX Z i -12 -KPX Z e -6 -KPX Z a -6 -KPX Z Q -18 -KPX Z O -18 -KPX Z G -18 -KPX Z C -18 -KPX Z A 25 - -KPX a quoteright -45 -KPX a quotedblright -40 - -KPX b y -15 -KPX b w -20 -KPX b v -20 -KPX b quoteright -45 -KPX b quotedblright -40 -KPX b period -10 -KPX b comma -10 - -KPX braceleft Y 64 -KPX braceleft W 64 -KPX braceleft V 64 -KPX braceleft T 25 -KPX braceleft J 50 - -KPX bracketleft Y 64 -KPX bracketleft W 64 -KPX bracketleft V 64 -KPX bracketleft T 35 -KPX bracketleft J 60 - -KPX c quoteright -5 - -KPX colon space -20 - -KPX comma space -40 -KPX comma quoteright -100 -KPX comma quotedblright -100 - -KPX d quoteright -24 -KPX d quotedblright -24 - -KPX e z -4 -KPX e quoteright -25 -KPX e quotedblright -20 - -KPX f quotesingle 70 -KPX f quoteright 68 -KPX f quotedblright 68 -KPX f period -10 -KPX f parenright 110 -KPX f comma -20 -KPX f bracketright 100 -KPX f braceright 80 - -KPX g y 20 -KPX g p 20 -KPX g f 20 -KPX g comma 10 - -KPX h quoteright -60 -KPX h quotedblright -60 - -KPX i quoteright -20 -KPX i quotedblright -20 - -KPX j quoteright -20 -KPX j quotedblright -20 -KPX j period -10 -KPX j comma -10 - -KPX k quoteright -30 -KPX k quotedblright -30 - -KPX l quoteright -24 -KPX l quotedblright -24 - -KPX m quoteright -60 -KPX m quotedblright -60 - -KPX n quoteright -60 -KPX n quotedblright -60 - -KPX o z -12 -KPX o y -25 -KPX o x -18 -KPX o w -30 -KPX o v -30 -KPX o quoteright -45 -KPX o quotedblright -40 -KPX o period -10 -KPX o comma -10 - -KPX p z -10 -KPX p y -15 -KPX p w -15 -KPX p quoteright -45 -KPX p quotedblright -60 -KPX p period -10 -KPX p comma -10 - -KPX parenleft Y 64 -KPX parenleft W 64 -KPX parenleft V 64 -KPX parenleft T 50 -KPX parenleft J 50 - -KPX period space -40 -KPX period quoteright -100 -KPX period quotedblright -100 - -KPX q quoteright -50 -KPX q quotedblright -50 -KPX q period -10 -KPX q comma -10 - -KPX quotedblleft z -26 -KPX quotedblleft w 10 -KPX quotedblleft u -40 -KPX quotedblleft t -40 -KPX quotedblleft s -32 -KPX quotedblleft r -40 -KPX quotedblleft q -70 -KPX quotedblleft p -40 -KPX quotedblleft o -70 -KPX quotedblleft n -40 -KPX quotedblleft m -40 -KPX quotedblleft g -50 -KPX quotedblleft f -30 -KPX quotedblleft e -70 -KPX quotedblleft d -70 -KPX quotedblleft c -70 -KPX quotedblleft a -60 -KPX quotedblleft Y 30 -KPX quotedblleft X 20 -KPX quotedblleft W 40 -KPX quotedblleft V 40 -KPX quotedblleft T 18 -KPX quotedblleft J -24 -KPX quotedblleft A -122 - -KPX quotedblright space -40 -KPX quotedblright period -100 -KPX quotedblright comma -100 - -KPX quoteleft z -26 -KPX quoteleft y -5 -KPX quoteleft x -5 -KPX quoteleft w 5 -KPX quoteleft v -5 -KPX quoteleft u -25 -KPX quoteleft t -25 -KPX quoteleft s -40 -KPX quoteleft r -40 -KPX quoteleft quoteleft -30 -KPX quoteleft q -70 -KPX quoteleft p -40 -KPX quoteleft o -70 -KPX quoteleft n -40 -KPX quoteleft m -40 -KPX quoteleft g -50 -KPX quoteleft f -10 -KPX quoteleft e -70 -KPX quoteleft d -70 -KPX quoteleft c -70 -KPX quoteleft a -60 -KPX quoteleft Y 35 -KPX quoteleft X 30 -KPX quoteleft W 35 -KPX quoteleft V 35 -KPX quoteleft T 35 -KPX quoteleft J -24 -KPX quoteleft A -122 - -KPX quoteright v -20 -KPX quoteright t -50 -KPX quoteright space -40 -KPX quoteright s -70 -KPX quoteright r -42 -KPX quoteright quoteright -30 -KPX quoteright period -100 -KPX quoteright m -42 -KPX quoteright l -6 -KPX quoteright d -100 -KPX quoteright comma -100 - -KPX r z 20 -KPX r y 18 -KPX r x 12 -KPX r w 30 -KPX r v 30 -KPX r u 8 -KPX r t 8 -KPX r semicolon 20 -KPX r quoteright -20 -KPX r quotedblright -10 -KPX r q -6 -KPX r period -60 -KPX r o -6 -KPX r n 8 -KPX r m 8 -KPX r l -10 -KPX r k -10 -KPX r i 8 -KPX r hyphen -60 -KPX r h -10 -KPX r g 5 -KPX r f 8 -KPX r emdash -20 -KPX r e -20 -KPX r d -20 -KPX r comma -80 -KPX r colon 20 -KPX r c -20 - -KPX s quoteright -40 -KPX s quotedblright -40 - -KPX semicolon space -20 - -KPX space quotesinglbase -100 -KPX space quoteleft -40 -KPX space quotedblleft -40 -KPX space quotedblbase -100 -KPX space Y -60 -KPX space W -60 -KPX space V -60 -KPX space T -40 - -KPX t period 15 -KPX t comma 10 - -KPX u quoteright -60 -KPX u quotedblright -60 - -KPX v semicolon 20 -KPX v quoteright 5 -KPX v quotedblright 10 -KPX v q -15 -KPX v period -75 -KPX v o -15 -KPX v e -15 -KPX v d -15 -KPX v comma -90 -KPX v colon 20 -KPX v c -15 -KPX v a -15 - -KPX w semicolon 20 -KPX w quoteright 15 -KPX w quotedblright 20 -KPX w q -10 -KPX w period -60 -KPX w o -10 -KPX w e -10 -KPX w d -10 -KPX w comma -68 -KPX w colon 20 -KPX w c -10 - -KPX x quoteright -25 -KPX x quotedblright -20 -KPX x q -6 -KPX x o -6 -KPX x e -12 -KPX x d -12 -KPX x c -12 - -KPX y semicolon 20 -KPX y quoteright 5 -KPX y quotedblright 10 -KPX y period -72 -KPX y hyphen -20 -KPX y comma -72 -KPX y colon 20 - -KPX z quoteright -20 -KPX z quotedblright -20 -KPX z o -6 -KPX z e -6 -KPX z d -6 -KPX z c -6 -EndKernPairs -EndKernData -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/putbi8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/putbi8a.afm deleted file mode 100644 index 5e83848..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/putbi8a.afm +++ /dev/null @@ -1,1017 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1989, 1991 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Fri Jan 17 15:47:44 1992 -Comment UniqueID 37716 -Comment VMusage 34427 41319 -FontName Utopia-BoldItalic -FullName Utopia Bold Italic -FamilyName Utopia -Weight Bold -ItalicAngle -13 -IsFixedPitch false -FontBBox -176 -250 1262 916 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.002 -Notice Copyright (c) 1989, 1991 Adobe Systems Incorporated. All Rights Reserved.Utopia is a registered trademark of Adobe Systems Incorporated. -EncodingScheme AdobeStandardEncoding -CapHeight 692 -XHeight 502 -Ascender 742 -Descender -242 -StartCharMetrics 228 -C 32 ; WX 210 ; N space ; B 0 0 0 0 ; -C 33 ; WX 285 ; N exclam ; B 35 -12 336 707 ; -C 34 ; WX 455 ; N quotedbl ; B 142 407 496 707 ; -C 35 ; WX 560 ; N numbersign ; B 37 0 606 668 ; -C 36 ; WX 560 ; N dollar ; B 32 -104 588 748 ; -C 37 ; WX 896 ; N percent ; B 106 -31 861 702 ; -C 38 ; WX 752 ; N ampersand ; B 62 -12 736 680 ; -C 39 ; WX 246 ; N quoteright ; B 95 387 294 707 ; -C 40 ; WX 350 ; N parenleft ; B 87 -135 438 699 ; -C 41 ; WX 350 ; N parenright ; B -32 -135 319 699 ; -C 42 ; WX 500 ; N asterisk ; B 121 315 528 707 ; -C 43 ; WX 600 ; N plus ; B 83 0 567 490 ; -C 44 ; WX 280 ; N comma ; B -9 -167 207 180 ; -C 45 ; WX 392 ; N hyphen ; B 71 203 354 298 ; -C 46 ; WX 280 ; N period ; B 32 -12 212 166 ; -C 47 ; WX 260 ; N slash ; B -16 -15 370 707 ; -C 48 ; WX 560 ; N zero ; B 57 -12 583 680 ; -C 49 ; WX 560 ; N one ; B 72 0 470 680 ; -C 50 ; WX 560 ; N two ; B 4 0 578 680 ; -C 51 ; WX 560 ; N three ; B 21 -12 567 680 ; -C 52 ; WX 560 ; N four ; B 28 0 557 668 ; -C 53 ; WX 560 ; N five ; B 23 -12 593 668 ; -C 54 ; WX 560 ; N six ; B 56 -12 586 680 ; -C 55 ; WX 560 ; N seven ; B 112 -12 632 668 ; -C 56 ; WX 560 ; N eight ; B 37 -12 584 680 ; -C 57 ; WX 560 ; N nine ; B 48 -12 570 680 ; -C 58 ; WX 280 ; N colon ; B 32 -12 280 490 ; -C 59 ; WX 280 ; N semicolon ; B -9 -167 280 490 ; -C 60 ; WX 600 ; N less ; B 66 5 544 495 ; -C 61 ; WX 600 ; N equal ; B 83 103 567 397 ; -C 62 ; WX 600 ; N greater ; B 86 5 564 495 ; -C 63 ; WX 454 ; N question ; B 115 -12 515 707 ; -C 64 ; WX 828 ; N at ; B 90 -15 842 707 ; -C 65 ; WX 634 ; N A ; B -59 0 639 692 ; -C 66 ; WX 680 ; N B ; B 5 0 689 692 ; -C 67 ; WX 672 ; N C ; B 76 -15 742 707 ; -C 68 ; WX 774 ; N D ; B 5 0 784 692 ; -C 69 ; WX 622 ; N E ; B 5 0 687 692 ; -C 70 ; WX 585 ; N F ; B 5 0 683 692 ; -C 71 ; WX 726 ; N G ; B 76 -15 756 707 ; -C 72 ; WX 800 ; N H ; B 5 0 880 692 ; -C 73 ; WX 386 ; N I ; B 5 0 466 692 ; -C 74 ; WX 388 ; N J ; B -50 -114 477 692 ; -C 75 ; WX 688 ; N K ; B 5 -6 823 692 ; -C 76 ; WX 586 ; N L ; B 5 0 591 692 ; -C 77 ; WX 921 ; N M ; B 0 0 998 692 ; -C 78 ; WX 741 ; N N ; B -5 0 838 692 ; -C 79 ; WX 761 ; N O ; B 78 -15 768 707 ; -C 80 ; WX 660 ; N P ; B 5 0 694 692 ; -C 81 ; WX 761 ; N Q ; B 78 -193 768 707 ; -C 82 ; WX 681 ; N R ; B 5 0 696 692 ; -C 83 ; WX 551 ; N S ; B 31 -15 570 707 ; -C 84 ; WX 616 ; N T ; B 91 0 722 692 ; -C 85 ; WX 776 ; N U ; B 115 -15 867 692 ; -C 86 ; WX 630 ; N V ; B 92 0 783 692 ; -C 87 ; WX 920 ; N W ; B 80 0 1062 692 ; -C 88 ; WX 630 ; N X ; B -56 0 744 692 ; -C 89 ; WX 622 ; N Y ; B 92 0 765 692 ; -C 90 ; WX 618 ; N Z ; B -30 0 714 692 ; -C 91 ; WX 350 ; N bracketleft ; B 56 -128 428 692 ; -C 92 ; WX 460 ; N backslash ; B 114 -15 425 707 ; -C 93 ; WX 350 ; N bracketright ; B -22 -128 350 692 ; -C 94 ; WX 600 ; N asciicircum ; B 79 215 567 668 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 246 ; N quoteleft ; B 114 399 313 719 ; -C 97 ; WX 596 ; N a ; B 26 -12 612 502 ; -C 98 ; WX 586 ; N b ; B 34 -12 592 742 ; -C 99 ; WX 456 ; N c ; B 38 -12 498 502 ; -C 100 ; WX 609 ; N d ; B 29 -12 651 742 ; -C 101 ; WX 476 ; N e ; B 38 -12 497 502 ; -C 102 ; WX 348 ; N f ; B -129 -242 553 742 ; L i fi ; L l fl ; -C 103 ; WX 522 ; N g ; B -14 -242 609 512 ; -C 104 ; WX 629 ; N h ; B 44 -12 631 742 ; -C 105 ; WX 339 ; N i ; B 66 -12 357 720 ; -C 106 ; WX 333 ; N j ; B -120 -242 364 720 ; -C 107 ; WX 570 ; N k ; B 39 -12 604 742 ; -C 108 ; WX 327 ; N l ; B 62 -12 360 742 ; -C 109 ; WX 914 ; N m ; B 46 -12 917 502 ; -C 110 ; WX 635 ; N n ; B 45 -12 639 502 ; -C 111 ; WX 562 ; N o ; B 42 -12 556 502 ; -C 112 ; WX 606 ; N p ; B 0 -242 613 502 ; -C 113 ; WX 584 ; N q ; B 29 -242 604 513 ; -C 114 ; WX 440 ; N r ; B 51 -12 497 502 ; -C 115 ; WX 417 ; N s ; B 10 -12 432 502 ; -C 116 ; WX 359 ; N t ; B 68 -12 428 641 ; -C 117 ; WX 634 ; N u ; B 71 -12 643 502 ; -C 118 ; WX 518 ; N v ; B 68 -12 547 502 ; -C 119 ; WX 795 ; N w ; B 70 -12 826 502 ; -C 120 ; WX 516 ; N x ; B -26 -12 546 502 ; -C 121 ; WX 489 ; N y ; B -49 -242 532 502 ; -C 122 ; WX 466 ; N z ; B -17 -12 506 490 ; -C 123 ; WX 340 ; N braceleft ; B 90 -128 439 692 ; -C 124 ; WX 265 ; N bar ; B 117 -250 221 750 ; -C 125 ; WX 340 ; N braceright ; B -42 -128 307 692 ; -C 126 ; WX 600 ; N asciitilde ; B 70 157 571 338 ; -C 161 ; WX 285 ; N exclamdown ; B -13 -217 288 502 ; -C 162 ; WX 560 ; N cent ; B 80 -21 611 668 ; -C 163 ; WX 560 ; N sterling ; B -4 0 583 679 ; -C 164 ; WX 100 ; N fraction ; B -176 -27 370 695 ; -C 165 ; WX 560 ; N yen ; B 65 0 676 668 ; -C 166 ; WX 560 ; N florin ; B -16 -135 635 691 ; -C 167 ; WX 568 ; N section ; B 64 -115 559 707 ; -C 168 ; WX 560 ; N currency ; B 60 73 578 596 ; -C 169 ; WX 246 ; N quotesingle ; B 134 376 285 707 ; -C 170 ; WX 455 ; N quotedblleft ; B 114 399 522 719 ; -C 171 ; WX 560 ; N guillemotleft ; B 90 37 533 464 ; -C 172 ; WX 360 ; N guilsinglleft ; B 90 37 333 464 ; -C 173 ; WX 360 ; N guilsinglright ; B 58 37 301 464 ; -C 174 ; WX 651 ; N fi ; B -129 -242 655 742 ; -C 175 ; WX 652 ; N fl ; B -129 -242 685 742 ; -C 177 ; WX 500 ; N endash ; B 12 209 531 292 ; -C 178 ; WX 514 ; N dagger ; B 101 -125 545 707 ; -C 179 ; WX 490 ; N daggerdbl ; B 32 -119 528 707 ; -C 180 ; WX 280 ; N periodcentered ; B 67 161 247 339 ; -C 182 ; WX 580 ; N paragraph ; B 110 -101 653 692 ; -C 183 ; WX 465 ; N bullet ; B 99 174 454 529 ; -C 184 ; WX 246 ; N quotesinglbase ; B -17 -153 182 167 ; -C 185 ; WX 455 ; N quotedblbase ; B -17 -153 391 167 ; -C 186 ; WX 455 ; N quotedblright ; B 95 387 503 707 ; -C 187 ; WX 560 ; N guillemotright ; B 58 37 502 464 ; -C 188 ; WX 1000 ; N ellipsis ; B 85 -12 931 166 ; -C 189 ; WX 1297 ; N perthousand ; B 106 -31 1262 702 ; -C 191 ; WX 454 ; N questiondown ; B -10 -217 391 502 ; -C 193 ; WX 400 ; N grave ; B 109 511 381 740 ; -C 194 ; WX 400 ; N acute ; B 186 511 458 740 ; -C 195 ; WX 400 ; N circumflex ; B 93 520 471 747 ; -C 196 ; WX 400 ; N tilde ; B 94 549 502 697 ; -C 197 ; WX 400 ; N macron ; B 133 592 459 664 ; -C 198 ; WX 400 ; N breve ; B 146 556 469 714 ; -C 199 ; WX 402 ; N dotaccent ; B 220 561 378 710 ; -C 200 ; WX 400 ; N dieresis ; B 106 561 504 710 ; -C 202 ; WX 400 ; N ring ; B 166 529 423 762 ; -C 203 ; WX 400 ; N cedilla ; B 85 -246 292 0 ; -C 205 ; WX 400 ; N hungarumlaut ; B 158 546 482 750 ; -C 206 ; WX 350 ; N ogonek ; B 38 -246 253 0 ; -C 207 ; WX 400 ; N caron ; B 130 520 508 747 ; -C 208 ; WX 1000 ; N emdash ; B 12 209 1031 292 ; -C 225 ; WX 890 ; N AE ; B -107 0 958 692 ; -C 227 ; WX 444 ; N ordfeminine ; B 62 265 482 590 ; -C 232 ; WX 592 ; N Lslash ; B 11 0 597 692 ; -C 233 ; WX 761 ; N Oslash ; B 77 -51 769 734 ; -C 234 ; WX 1016 ; N OE ; B 76 0 1084 692 ; -C 235 ; WX 412 ; N ordmasculine ; B 86 265 446 590 ; -C 241 ; WX 789 ; N ae ; B 26 -12 810 509 ; -C 245 ; WX 339 ; N dotlessi ; B 66 -12 343 502 ; -C 248 ; WX 339 ; N lslash ; B 18 -12 420 742 ; -C 249 ; WX 562 ; N oslash ; B 42 -69 556 549 ; -C 250 ; WX 811 ; N oe ; B 42 -12 832 502 ; -C 251 ; WX 628 ; N germandbls ; B -129 -242 692 742 ; -C -1 ; WX 402 ; N onesuperior ; B 84 272 361 680 ; -C -1 ; WX 600 ; N minus ; B 83 210 567 290 ; -C -1 ; WX 375 ; N degree ; B 93 360 425 680 ; -C -1 ; WX 562 ; N oacute ; B 42 -12 572 755 ; -C -1 ; WX 761 ; N Odieresis ; B 78 -15 768 881 ; -C -1 ; WX 562 ; N odieresis ; B 42 -12 585 710 ; -C -1 ; WX 622 ; N Eacute ; B 5 0 687 904 ; -C -1 ; WX 634 ; N ucircumflex ; B 71 -12 643 747 ; -C -1 ; WX 940 ; N onequarter ; B 104 -27 849 695 ; -C -1 ; WX 600 ; N logicalnot ; B 83 95 567 397 ; -C -1 ; WX 622 ; N Ecircumflex ; B 5 0 687 905 ; -C -1 ; WX 940 ; N onehalf ; B 90 -27 898 695 ; -C -1 ; WX 761 ; N Otilde ; B 78 -15 768 876 ; -C -1 ; WX 634 ; N uacute ; B 71 -12 643 740 ; -C -1 ; WX 476 ; N eacute ; B 38 -12 545 755 ; -C -1 ; WX 339 ; N iacute ; B 66 -12 438 740 ; -C -1 ; WX 622 ; N Egrave ; B 5 0 687 904 ; -C -1 ; WX 339 ; N icircumflex ; B 38 -12 416 747 ; -C -1 ; WX 634 ; N mu ; B -3 -230 643 502 ; -C -1 ; WX 265 ; N brokenbar ; B 117 -175 221 675 ; -C -1 ; WX 600 ; N thorn ; B -6 -242 607 700 ; -C -1 ; WX 634 ; N Aring ; B -59 0 639 879 ; -C -1 ; WX 489 ; N yacute ; B -49 -242 553 740 ; -C -1 ; WX 622 ; N Ydieresis ; B 92 0 765 881 ; -C -1 ; WX 1100 ; N trademark ; B 103 277 1093 692 ; -C -1 ; WX 824 ; N registered ; B 91 -15 819 707 ; -C -1 ; WX 562 ; N ocircumflex ; B 42 -12 556 747 ; -C -1 ; WX 634 ; N Agrave ; B -59 0 639 904 ; -C -1 ; WX 551 ; N Scaron ; B 31 -15 612 916 ; -C -1 ; WX 776 ; N Ugrave ; B 115 -15 867 904 ; -C -1 ; WX 622 ; N Edieresis ; B 5 0 687 881 ; -C -1 ; WX 776 ; N Uacute ; B 115 -15 867 904 ; -C -1 ; WX 562 ; N otilde ; B 42 -12 583 697 ; -C -1 ; WX 635 ; N ntilde ; B 45 -12 639 697 ; -C -1 ; WX 489 ; N ydieresis ; B -49 -242 532 710 ; -C -1 ; WX 634 ; N Aacute ; B -59 0 678 904 ; -C -1 ; WX 562 ; N eth ; B 42 -12 558 742 ; -C -1 ; WX 596 ; N acircumflex ; B 26 -12 612 747 ; -C -1 ; WX 596 ; N aring ; B 26 -12 612 762 ; -C -1 ; WX 761 ; N Ograve ; B 78 -15 768 904 ; -C -1 ; WX 456 ; N ccedilla ; B 38 -246 498 502 ; -C -1 ; WX 600 ; N multiply ; B 110 22 560 478 ; -C -1 ; WX 600 ; N divide ; B 63 7 547 493 ; -C -1 ; WX 402 ; N twosuperior ; B 29 272 423 680 ; -C -1 ; WX 741 ; N Ntilde ; B -5 0 838 876 ; -C -1 ; WX 634 ; N ugrave ; B 71 -12 643 740 ; -C -1 ; WX 776 ; N Ucircumflex ; B 115 -15 867 905 ; -C -1 ; WX 634 ; N Atilde ; B -59 0 662 876 ; -C -1 ; WX 466 ; N zcaron ; B -17 -12 526 747 ; -C -1 ; WX 339 ; N idieresis ; B 46 -12 444 710 ; -C -1 ; WX 634 ; N Acircumflex ; B -59 0 639 905 ; -C -1 ; WX 386 ; N Icircumflex ; B 5 0 506 905 ; -C -1 ; WX 622 ; N Yacute ; B 92 0 765 904 ; -C -1 ; WX 761 ; N Oacute ; B 78 -15 768 904 ; -C -1 ; WX 634 ; N Adieresis ; B -59 0 652 881 ; -C -1 ; WX 618 ; N Zcaron ; B -30 0 714 916 ; -C -1 ; WX 596 ; N agrave ; B 26 -12 612 755 ; -C -1 ; WX 402 ; N threesuperior ; B 59 265 421 680 ; -C -1 ; WX 562 ; N ograve ; B 42 -12 556 755 ; -C -1 ; WX 940 ; N threequarters ; B 95 -27 876 695 ; -C -1 ; WX 780 ; N Eth ; B 11 0 790 692 ; -C -1 ; WX 600 ; N plusminus ; B 83 0 567 549 ; -C -1 ; WX 634 ; N udieresis ; B 71 -12 643 710 ; -C -1 ; WX 476 ; N edieresis ; B 38 -12 542 710 ; -C -1 ; WX 596 ; N aacute ; B 26 -12 621 755 ; -C -1 ; WX 339 ; N igrave ; B 39 -12 343 740 ; -C -1 ; WX 386 ; N Idieresis ; B 5 0 533 881 ; -C -1 ; WX 596 ; N adieresis ; B 26 -12 612 710 ; -C -1 ; WX 386 ; N Iacute ; B 5 0 549 904 ; -C -1 ; WX 824 ; N copyright ; B 91 -15 819 707 ; -C -1 ; WX 386 ; N Igrave ; B 5 0 466 904 ; -C -1 ; WX 672 ; N Ccedilla ; B 76 -246 742 707 ; -C -1 ; WX 417 ; N scaron ; B 10 -12 522 747 ; -C -1 ; WX 476 ; N egrave ; B 38 -12 497 755 ; -C -1 ; WX 761 ; N Ocircumflex ; B 78 -15 768 905 ; -C -1 ; WX 629 ; N Thorn ; B 5 0 660 692 ; -C -1 ; WX 596 ; N atilde ; B 26 -12 612 697 ; -C -1 ; WX 776 ; N Udieresis ; B 115 -15 867 881 ; -C -1 ; WX 476 ; N ecircumflex ; B 38 -12 524 747 ; -EndCharMetrics -StartKernData -StartKernPairs 697 - -KPX A z 18 -KPX A y -40 -KPX A x 16 -KPX A w -30 -KPX A v -30 -KPX A u -18 -KPX A t -6 -KPX A s 6 -KPX A r -6 -KPX A quoteright -92 -KPX A quotedblright -92 -KPX A p -6 -KPX A o -18 -KPX A n -12 -KPX A m -12 -KPX A l -18 -KPX A h -6 -KPX A d 4 -KPX A c -6 -KPX A b -6 -KPX A a 10 -KPX A Y -56 -KPX A X -8 -KPX A W -46 -KPX A V -75 -KPX A U -50 -KPX A T -60 -KPX A Q -30 -KPX A O -30 -KPX A G -30 -KPX A C -30 - -KPX B y -6 -KPX B u -12 -KPX B r -6 -KPX B quoteright -20 -KPX B quotedblright -32 -KPX B o 6 -KPX B l -20 -KPX B k -10 -KPX B i -12 -KPX B h -15 -KPX B e 4 -KPX B a 10 -KPX B W -30 -KPX B V -45 -KPX B U -30 -KPX B T -20 - -KPX C z -6 -KPX C y -18 -KPX C u -12 -KPX C r -12 -KPX C quoteright 12 -KPX C quotedblright 20 -KPX C i -6 -KPX C e -6 -KPX C a -6 -KPX C Q -12 -KPX C O -12 -KPX C G -12 -KPX C C -12 - -KPX D y 18 -KPX D quoteright -20 -KPX D quotedblright -20 -KPX D period -20 -KPX D o 6 -KPX D h -15 -KPX D e 6 -KPX D comma -20 -KPX D a 6 -KPX D Y -80 -KPX D W -40 -KPX D V -65 - -KPX E z -6 -KPX E y -24 -KPX E x 15 -KPX E w -30 -KPX E v -18 -KPX E u -24 -KPX E t -18 -KPX E s -6 -KPX E r -6 -KPX E quoteright 10 -KPX E q 10 -KPX E period 15 -KPX E p -12 -KPX E n -12 -KPX E m -12 -KPX E l -6 -KPX E j -6 -KPX E i -12 -KPX E g -12 -KPX E d 10 -KPX E comma 15 -KPX E a 10 - -KPX F y -12 -KPX F u -24 -KPX F r -12 -KPX F quoteright 40 -KPX F quotedblright 35 -KPX F period -120 -KPX F o -24 -KPX F i -6 -KPX F e -24 -KPX F comma -110 -KPX F a -30 -KPX F A -45 - -KPX G y -25 -KPX G u -22 -KPX G r -22 -KPX G quoteright -30 -KPX G quotedblright -30 -KPX G n -22 -KPX G l -24 -KPX G i -12 -KPX G h -18 -KPX G e 5 - -KPX H y -18 -KPX H u -30 -KPX H o -25 -KPX H i -25 -KPX H e -25 -KPX H a -25 - -KPX I z -20 -KPX I y -6 -KPX I x -6 -KPX I w -30 -KPX I v -30 -KPX I u -30 -KPX I t -18 -KPX I s -18 -KPX I r -12 -KPX I p -18 -KPX I o -25 -KPX I n -18 -KPX I m -18 -KPX I l -6 -KPX I k -6 -KPX I j -20 -KPX I i -10 -KPX I g -24 -KPX I f -6 -KPX I e -25 -KPX I d -15 -KPX I c -25 -KPX I b -6 -KPX I a -15 - -KPX J y -12 -KPX J u -32 -KPX J quoteright 6 -KPX J quotedblright 6 -KPX J o -36 -KPX J i -30 -KPX J e -30 -KPX J braceright 15 -KPX J a -36 - -KPX K y -70 -KPX K w -36 -KPX K v -30 -KPX K u -30 -KPX K r -24 -KPX K quoteright 36 -KPX K quotedblright 36 -KPX K o -30 -KPX K n -24 -KPX K l 10 -KPX K i -12 -KPX K h 15 -KPX K e -30 -KPX K a -12 -KPX K Q -50 -KPX K O -50 -KPX K G -50 -KPX K C -50 -KPX K A 15 - -KPX L y -70 -KPX L w -30 -KPX L u -18 -KPX L quoteright -110 -KPX L quotedblright -110 -KPX L l -16 -KPX L j -18 -KPX L i -18 -KPX L Y -80 -KPX L W -78 -KPX L V -110 -KPX L U -42 -KPX L T -100 -KPX L Q -48 -KPX L O -48 -KPX L G -48 -KPX L C -48 -KPX L A 40 - -KPX M y -18 -KPX M u -24 -KPX M quoteright 6 -KPX M quotedblright 6 -KPX M o -25 -KPX M n -20 -KPX M j -35 -KPX M i -20 -KPX M e -25 -KPX M d -20 -KPX M c -25 -KPX M a -20 - -KPX N y -18 -KPX N u -24 -KPX N o -18 -KPX N i -12 -KPX N e -16 -KPX N a -22 - -KPX O z -6 -KPX O y 12 -KPX O u -6 -KPX O t -6 -KPX O s -6 -KPX O r -6 -KPX O quoteright -20 -KPX O quotedblright -20 -KPX O q 6 -KPX O period -10 -KPX O p -6 -KPX O n -6 -KPX O m -6 -KPX O l -15 -KPX O k -10 -KPX O j -6 -KPX O h -10 -KPX O g -6 -KPX O e 6 -KPX O d 6 -KPX O comma -10 -KPX O a 6 -KPX O Y -70 -KPX O X -30 -KPX O W -35 -KPX O V -50 -KPX O T -42 -KPX O A -8 - -KPX P y 6 -KPX P u -18 -KPX P t -6 -KPX P s -24 -KPX P r -6 -KPX P quoteright -12 -KPX P period -170 -KPX P o -24 -KPX P n -12 -KPX P l -20 -KPX P h -20 -KPX P e -24 -KPX P comma -170 -KPX P a -40 -KPX P I -45 -KPX P H -45 -KPX P E -45 -KPX P A -70 - -KPX Q u -6 -KPX Q quoteright -20 -KPX Q quotedblright -38 -KPX Q a -6 -KPX Q Y -70 -KPX Q X -12 -KPX Q W -35 -KPX Q V -50 -KPX Q U -30 -KPX Q T -36 -KPX Q A -18 - -KPX R y -6 -KPX R u -12 -KPX R quoteright -22 -KPX R quotedblright -22 -KPX R o -20 -KPX R e -12 -KPX R Y -45 -KPX R X 15 -KPX R W -25 -KPX R V -35 -KPX R U -40 -KPX R T -18 -KPX R Q -8 -KPX R O -8 -KPX R G -8 -KPX R C -8 -KPX R A 15 - -KPX S y -30 -KPX S w -30 -KPX S v -20 -KPX S u -18 -KPX S t -18 -KPX S r -20 -KPX S quoteright -38 -KPX S quotedblright -50 -KPX S p -18 -KPX S n -24 -KPX S m -24 -KPX S l -20 -KPX S k -18 -KPX S j -25 -KPX S i -20 -KPX S h -12 -KPX S e -6 - -KPX T z -48 -KPX T y -52 -KPX T w -54 -KPX T u -54 -KPX T semicolon -6 -KPX T s -60 -KPX T r -54 -KPX T quoteright 36 -KPX T quotedblright 36 -KPX T period -70 -KPX T parenright 25 -KPX T o -78 -KPX T m -54 -KPX T i -22 -KPX T hyphen -100 -KPX T h 6 -KPX T endash -40 -KPX T emdash -40 -KPX T e -78 -KPX T comma -90 -KPX T bracketright 20 -KPX T braceright 30 -KPX T a -78 -KPX T Y 12 -KPX T X 18 -KPX T W 30 -KPX T V 20 -KPX T T 40 -KPX T Q -6 -KPX T O -6 -KPX T G -6 -KPX T C -6 -KPX T A -40 - -KPX U z -18 -KPX U x -30 -KPX U v -20 -KPX U t -24 -KPX U s -40 -KPX U r -30 -KPX U p -30 -KPX U n -30 -KPX U m -30 -KPX U l -12 -KPX U k -12 -KPX U i -24 -KPX U h -6 -KPX U g -30 -KPX U f -10 -KPX U d -30 -KPX U c -30 -KPX U b -6 -KPX U a -30 -KPX U A -40 - -KPX V y -34 -KPX V u -42 -KPX V semicolon -45 -KPX V r -55 -KPX V quoteright 46 -KPX V quotedblright 60 -KPX V period -110 -KPX V parenright 64 -KPX V o -55 -KPX V i 15 -KPX V hyphen -60 -KPX V endash -20 -KPX V emdash -20 -KPX V e -55 -KPX V comma -110 -KPX V colon -18 -KPX V bracketright 64 -KPX V braceright 64 -KPX V a -80 -KPX V T 12 -KPX V A -70 - -KPX W y -36 -KPX W u -30 -KPX W t -10 -KPX W semicolon -12 -KPX W r -30 -KPX W quoteright 42 -KPX W quotedblright 55 -KPX W period -80 -KPX W parenright 55 -KPX W o -55 -KPX W m -30 -KPX W i 5 -KPX W hyphen -40 -KPX W h 16 -KPX W e -55 -KPX W d -60 -KPX W comma -80 -KPX W colon -12 -KPX W bracketright 64 -KPX W braceright 64 -KPX W a -60 -KPX W T 30 -KPX W Q -5 -KPX W O -5 -KPX W G -5 -KPX W C -5 -KPX W A -45 - -KPX X y -40 -KPX X u -30 -KPX X r -6 -KPX X quoteright 24 -KPX X quotedblright 40 -KPX X i -6 -KPX X e -18 -KPX X a -6 -KPX X Y -6 -KPX X W -6 -KPX X Q -45 -KPX X O -45 -KPX X G -45 -KPX X C -45 - -KPX Y v -60 -KPX Y u -70 -KPX Y t -32 -KPX Y semicolon -20 -KPX Y quoteright 56 -KPX Y quotedblright 70 -KPX Y q -100 -KPX Y period -80 -KPX Y parenright 5 -KPX Y o -95 -KPX Y l 15 -KPX Y i 15 -KPX Y hyphen -110 -KPX Y endash -40 -KPX Y emdash -40 -KPX Y e -95 -KPX Y d -85 -KPX Y comma -80 -KPX Y colon -20 -KPX Y bracketright 64 -KPX Y braceright 64 -KPX Y a -85 -KPX Y Y 12 -KPX Y X 12 -KPX Y W 12 -KPX Y V 6 -KPX Y T 30 -KPX Y Q -25 -KPX Y O -25 -KPX Y G -25 -KPX Y C -25 -KPX Y A -40 - -KPX Z y -36 -KPX Z w -36 -KPX Z u -12 -KPX Z quoteright 18 -KPX Z quotedblright 18 -KPX Z o -6 -KPX Z i -12 -KPX Z e -6 -KPX Z a -6 -KPX Z Q -20 -KPX Z O -20 -KPX Z G -20 -KPX Z C -20 -KPX Z A 30 - -KPX a quoteright -54 -KPX a quotedblright -54 - -KPX b y -6 -KPX b w -5 -KPX b v -5 -KPX b quoteright -30 -KPX b quotedblright -30 -KPX b period -15 -KPX b comma -15 - -KPX braceleft Y 64 -KPX braceleft W 64 -KPX braceleft V 64 -KPX braceleft T 40 -KPX braceleft J 60 - -KPX bracketleft Y 60 -KPX bracketleft W 64 -KPX bracketleft V 64 -KPX bracketleft T 35 -KPX bracketleft J 30 - -KPX c quoteright 5 -KPX c quotedblright 5 - -KPX colon space -30 - -KPX comma space -40 -KPX comma quoteright -100 -KPX comma quotedblright -100 - -KPX d quoteright -12 -KPX d quotedblright -12 -KPX d period 15 -KPX d comma 15 - -KPX e y 6 -KPX e x -10 -KPX e w -10 -KPX e v -10 -KPX e quoteright -25 -KPX e quotedblright -25 - -KPX f quoteright 120 -KPX f quotedblright 120 -KPX f period -30 -KPX f parenright 100 -KPX f comma -30 -KPX f bracketright 110 -KPX f braceright 110 - -KPX g y 50 -KPX g quotedblright -20 -KPX g p 30 -KPX g f 42 -KPX g comma 20 - -KPX h quoteright -78 -KPX h quotedblright -78 - -KPX i quoteright -20 -KPX i quotedblright -20 - -KPX j quoteright -20 -KPX j quotedblright -20 -KPX j period -20 -KPX j comma -20 - -KPX k quoteright -38 -KPX k quotedblright -38 - -KPX l quoteright -12 -KPX l quotedblright -12 - -KPX m quoteright -78 -KPX m quotedblright -78 - -KPX n quoteright -88 -KPX n quotedblright -88 - -KPX o y -12 -KPX o x -20 -KPX o w -25 -KPX o v -25 -KPX o quoteright -50 -KPX o quotedblright -50 -KPX o period -10 -KPX o comma -10 - -KPX p w -6 -KPX p quoteright -30 -KPX p quotedblright -52 -KPX p period -15 -KPX p comma -15 - -KPX parenleft Y 64 -KPX parenleft W 64 -KPX parenleft V 64 -KPX parenleft T 30 -KPX parenleft J 50 - -KPX period space -40 -KPX period quoteright -100 -KPX period quotedblright -100 - -KPX q quoteright -40 -KPX q quotedblright -40 -KPX q period -10 -KPX q comma -5 - -KPX quotedblleft z -30 -KPX quotedblleft x -60 -KPX quotedblleft w -12 -KPX quotedblleft v -12 -KPX quotedblleft u -12 -KPX quotedblleft t 5 -KPX quotedblleft s -30 -KPX quotedblleft r -12 -KPX quotedblleft q -50 -KPX quotedblleft p -12 -KPX quotedblleft o -30 -KPX quotedblleft n -12 -KPX quotedblleft m -12 -KPX quotedblleft l 10 -KPX quotedblleft k 10 -KPX quotedblleft h 10 -KPX quotedblleft g -30 -KPX quotedblleft e -30 -KPX quotedblleft d -50 -KPX quotedblleft c -30 -KPX quotedblleft b 24 -KPX quotedblleft a -50 -KPX quotedblleft Y 30 -KPX quotedblleft X 45 -KPX quotedblleft W 55 -KPX quotedblleft V 40 -KPX quotedblleft T 36 -KPX quotedblleft A -100 - -KPX quotedblright space -50 -KPX quotedblright period -200 -KPX quotedblright comma -200 - -KPX quoteleft z -30 -KPX quoteleft y 30 -KPX quoteleft x -10 -KPX quoteleft w -12 -KPX quoteleft u -12 -KPX quoteleft t -30 -KPX quoteleft s -30 -KPX quoteleft r -12 -KPX quoteleft q -30 -KPX quoteleft p -12 -KPX quoteleft o -30 -KPX quoteleft n -12 -KPX quoteleft m -12 -KPX quoteleft l 10 -KPX quoteleft k 10 -KPX quoteleft h 10 -KPX quoteleft g -30 -KPX quoteleft e -30 -KPX quoteleft d -30 -KPX quoteleft c -30 -KPX quoteleft b 24 -KPX quoteleft a -30 -KPX quoteleft Y 12 -KPX quoteleft X 46 -KPX quoteleft W 46 -KPX quoteleft V 28 -KPX quoteleft T 36 -KPX quoteleft A -100 - -KPX quoteright v -20 -KPX quoteright space -50 -KPX quoteright s -45 -KPX quoteright r -12 -KPX quoteright period -140 -KPX quoteright m -12 -KPX quoteright l -12 -KPX quoteright d -65 -KPX quoteright comma -140 - -KPX r z 20 -KPX r y 18 -KPX r x 12 -KPX r w 6 -KPX r v 6 -KPX r t 8 -KPX r semicolon 20 -KPX r quoteright -6 -KPX r quotedblright -6 -KPX r q -24 -KPX r period -100 -KPX r o -6 -KPX r l -12 -KPX r k -12 -KPX r hyphen -40 -KPX r h -10 -KPX r f 8 -KPX r endash -20 -KPX r e -26 -KPX r d -25 -KPX r comma -100 -KPX r colon 20 -KPX r c -12 -KPX r a -25 - -KPX s quoteright -25 -KPX s quotedblright -30 - -KPX semicolon space -30 - -KPX space quotesinglbase -60 -KPX space quoteleft -60 -KPX space quotedblleft -60 -KPX space quotedblbase -60 -KPX space Y -70 -KPX space W -50 -KPX space V -70 -KPX space T -50 -KPX space A -50 - -KPX t quoteright 15 -KPX t quotedblright 15 -KPX t period 15 -KPX t comma 15 - -KPX u quoteright -65 -KPX u quotedblright -78 -KPX u period 20 -KPX u comma 20 - -KPX v quoteright -10 -KPX v quotedblright -10 -KPX v q -6 -KPX v period -62 -KPX v o -6 -KPX v e -6 -KPX v d -6 -KPX v comma -62 -KPX v c -6 -KPX v a -6 - -KPX w quoteright -10 -KPX w quotedblright -10 -KPX w period -40 -KPX w comma -50 - -KPX x y 12 -KPX x w -6 -KPX x quoteright -30 -KPX x quotedblright -30 -KPX x q -6 -KPX x o -6 -KPX x e -6 -KPX x d -6 -KPX x c -6 - -KPX y quoteright -10 -KPX y quotedblright -10 -KPX y q -10 -KPX y period -56 -KPX y d -10 -KPX y comma -56 - -KPX z quoteright -40 -KPX z quotedblright -40 -KPX z o -6 -KPX z e -6 -KPX z d -6 -KPX z c -6 -EndKernPairs -EndKernData -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/putr8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/putr8a.afm deleted file mode 100644 index be1bb78..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/putr8a.afm +++ /dev/null @@ -1,1029 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1989, 1991 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Fri Jan 17 13:38:17 1992 -Comment UniqueID 37674 -Comment VMusage 32991 39883 -FontName Utopia-Regular -FullName Utopia Regular -FamilyName Utopia -Weight Regular -ItalicAngle 0 -IsFixedPitch false -FontBBox -158 -250 1158 890 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.002 -Notice Copyright (c) 1989, 1991 Adobe Systems Incorporated. All Rights Reserved.Utopia is a registered trademark of Adobe Systems Incorporated. -EncodingScheme AdobeStandardEncoding -CapHeight 692 -XHeight 490 -Ascender 742 -Descender -230 -StartCharMetrics 228 -C 32 ; WX 225 ; N space ; B 0 0 0 0 ; -C 33 ; WX 242 ; N exclam ; B 58 -12 184 707 ; -C 34 ; WX 458 ; N quotedbl ; B 101 464 358 742 ; -C 35 ; WX 530 ; N numbersign ; B 11 0 519 668 ; -C 36 ; WX 530 ; N dollar ; B 44 -102 487 743 ; -C 37 ; WX 838 ; N percent ; B 50 -25 788 700 ; -C 38 ; WX 706 ; N ampersand ; B 46 -12 692 680 ; -C 39 ; WX 278 ; N quoteright ; B 72 472 207 742 ; -C 40 ; WX 350 ; N parenleft ; B 105 -128 325 692 ; -C 41 ; WX 350 ; N parenright ; B 25 -128 245 692 ; -C 42 ; WX 412 ; N asterisk ; B 50 356 363 707 ; -C 43 ; WX 570 ; N plus ; B 43 0 527 490 ; -C 44 ; WX 265 ; N comma ; B 51 -141 193 141 ; -C 45 ; WX 392 ; N hyphen ; B 74 216 319 286 ; -C 46 ; WX 265 ; N period ; B 70 -12 196 116 ; -C 47 ; WX 460 ; N slash ; B 92 -15 369 707 ; -C 48 ; WX 530 ; N zero ; B 41 -12 489 680 ; -C 49 ; WX 530 ; N one ; B 109 0 437 680 ; -C 50 ; WX 530 ; N two ; B 27 0 485 680 ; -C 51 ; WX 530 ; N three ; B 27 -12 473 680 ; -C 52 ; WX 530 ; N four ; B 19 0 493 668 ; -C 53 ; WX 530 ; N five ; B 40 -12 480 668 ; -C 54 ; WX 530 ; N six ; B 44 -12 499 680 ; -C 55 ; WX 530 ; N seven ; B 41 -12 497 668 ; -C 56 ; WX 530 ; N eight ; B 42 -12 488 680 ; -C 57 ; WX 530 ; N nine ; B 36 -12 477 680 ; -C 58 ; WX 265 ; N colon ; B 70 -12 196 490 ; -C 59 ; WX 265 ; N semicolon ; B 51 -141 196 490 ; -C 60 ; WX 570 ; N less ; B 46 1 524 499 ; -C 61 ; WX 570 ; N equal ; B 43 111 527 389 ; -C 62 ; WX 570 ; N greater ; B 46 1 524 499 ; -C 63 ; WX 389 ; N question ; B 29 -12 359 707 ; -C 64 ; WX 793 ; N at ; B 46 -15 755 707 ; -C 65 ; WX 635 ; N A ; B -29 0 650 692 ; -C 66 ; WX 646 ; N B ; B 35 0 595 692 ; -C 67 ; WX 684 ; N C ; B 48 -15 649 707 ; -C 68 ; WX 779 ; N D ; B 35 0 731 692 ; -C 69 ; WX 606 ; N E ; B 35 0 577 692 ; -C 70 ; WX 580 ; N F ; B 35 0 543 692 ; -C 71 ; WX 734 ; N G ; B 48 -15 725 707 ; -C 72 ; WX 798 ; N H ; B 35 0 763 692 ; -C 73 ; WX 349 ; N I ; B 35 0 314 692 ; -C 74 ; WX 350 ; N J ; B 0 -114 323 692 ; -C 75 ; WX 658 ; N K ; B 35 -5 671 692 ; -C 76 ; WX 568 ; N L ; B 35 0 566 692 ; -C 77 ; WX 944 ; N M ; B 33 0 909 692 ; -C 78 ; WX 780 ; N N ; B 34 0 753 692 ; -C 79 ; WX 762 ; N O ; B 48 -15 714 707 ; -C 80 ; WX 600 ; N P ; B 35 0 574 692 ; -C 81 ; WX 762 ; N Q ; B 48 -193 714 707 ; -C 82 ; WX 644 ; N R ; B 35 0 638 692 ; -C 83 ; WX 541 ; N S ; B 50 -15 504 707 ; -C 84 ; WX 621 ; N T ; B 22 0 599 692 ; -C 85 ; WX 791 ; N U ; B 29 -15 762 692 ; -C 86 ; WX 634 ; N V ; B -18 0 678 692 ; -C 87 ; WX 940 ; N W ; B -13 0 977 692 ; -C 88 ; WX 624 ; N X ; B -19 0 657 692 ; -C 89 ; WX 588 ; N Y ; B -12 0 632 692 ; -C 90 ; WX 610 ; N Z ; B 9 0 594 692 ; -C 91 ; WX 330 ; N bracketleft ; B 133 -128 292 692 ; -C 92 ; WX 460 ; N backslash ; B 91 -15 369 707 ; -C 93 ; WX 330 ; N bracketright ; B 38 -128 197 692 ; -C 94 ; WX 570 ; N asciicircum ; B 56 228 514 668 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 278 ; N quoteleft ; B 72 478 207 748 ; -C 97 ; WX 523 ; N a ; B 49 -12 525 502 ; -C 98 ; WX 598 ; N b ; B 20 -12 549 742 ; -C 99 ; WX 496 ; N c ; B 49 -12 473 502 ; -C 100 ; WX 598 ; N d ; B 49 -12 583 742 ; -C 101 ; WX 514 ; N e ; B 49 -12 481 502 ; -C 102 ; WX 319 ; N f ; B 30 0 389 742 ; L i fi ; L l fl ; -C 103 ; WX 520 ; N g ; B 42 -242 525 512 ; -C 104 ; WX 607 ; N h ; B 21 0 592 742 ; -C 105 ; WX 291 ; N i ; B 32 0 276 715 ; -C 106 ; WX 280 ; N j ; B -33 -242 214 715 ; -C 107 ; WX 524 ; N k ; B 20 -5 538 742 ; -C 108 ; WX 279 ; N l ; B 20 0 264 742 ; -C 109 ; WX 923 ; N m ; B 32 0 908 502 ; -C 110 ; WX 619 ; N n ; B 32 0 604 502 ; -C 111 ; WX 577 ; N o ; B 49 -12 528 502 ; -C 112 ; WX 608 ; N p ; B 25 -230 559 502 ; -C 113 ; WX 591 ; N q ; B 49 -230 583 502 ; -C 114 ; WX 389 ; N r ; B 32 0 386 502 ; -C 115 ; WX 436 ; N s ; B 47 -12 400 502 ; -C 116 ; WX 344 ; N t ; B 31 -12 342 616 ; -C 117 ; WX 606 ; N u ; B 26 -12 591 502 ; -C 118 ; WX 504 ; N v ; B 1 0 529 490 ; -C 119 ; WX 768 ; N w ; B -2 0 792 490 ; -C 120 ; WX 486 ; N x ; B 1 0 509 490 ; -C 121 ; WX 506 ; N y ; B -5 -242 528 490 ; -C 122 ; WX 480 ; N z ; B 19 0 462 490 ; -C 123 ; WX 340 ; N braceleft ; B 79 -128 298 692 ; -C 124 ; WX 228 ; N bar ; B 80 -250 148 750 ; -C 125 ; WX 340 ; N braceright ; B 42 -128 261 692 ; -C 126 ; WX 570 ; N asciitilde ; B 73 175 497 317 ; -C 161 ; WX 242 ; N exclamdown ; B 58 -217 184 502 ; -C 162 ; WX 530 ; N cent ; B 37 -10 487 675 ; -C 163 ; WX 530 ; N sterling ; B 27 0 510 680 ; -C 164 ; WX 150 ; N fraction ; B -158 -27 308 695 ; -C 165 ; WX 530 ; N yen ; B -2 0 525 668 ; -C 166 ; WX 530 ; N florin ; B -2 -135 522 691 ; -C 167 ; WX 554 ; N section ; B 46 -115 507 707 ; -C 168 ; WX 530 ; N currency ; B 25 90 505 578 ; -C 169 ; WX 278 ; N quotesingle ; B 93 464 185 742 ; -C 170 ; WX 458 ; N quotedblleft ; B 72 478 387 748 ; -C 171 ; WX 442 ; N guillemotleft ; B 41 41 401 435 ; -C 172 ; WX 257 ; N guilsinglleft ; B 41 41 216 435 ; -C 173 ; WX 257 ; N guilsinglright ; B 41 41 216 435 ; -C 174 ; WX 610 ; N fi ; B 30 0 595 742 ; -C 175 ; WX 610 ; N fl ; B 30 0 595 742 ; -C 177 ; WX 500 ; N endash ; B 0 221 500 279 ; -C 178 ; WX 504 ; N dagger ; B 45 -125 459 717 ; -C 179 ; WX 488 ; N daggerdbl ; B 45 -119 443 717 ; -C 180 ; WX 265 ; N periodcentered ; B 70 188 196 316 ; -C 182 ; WX 555 ; N paragraph ; B 64 -101 529 692 ; -C 183 ; WX 409 ; N bullet ; B 45 192 364 512 ; -C 184 ; WX 278 ; N quotesinglbase ; B 72 -125 207 145 ; -C 185 ; WX 458 ; N quotedblbase ; B 72 -125 387 145 ; -C 186 ; WX 458 ; N quotedblright ; B 72 472 387 742 ; -C 187 ; WX 442 ; N guillemotright ; B 41 41 401 435 ; -C 188 ; WX 1000 ; N ellipsis ; B 104 -12 896 116 ; -C 189 ; WX 1208 ; N perthousand ; B 50 -25 1158 700 ; -C 191 ; WX 389 ; N questiondown ; B 30 -217 360 502 ; -C 193 ; WX 400 ; N grave ; B 49 542 271 723 ; -C 194 ; WX 400 ; N acute ; B 129 542 351 723 ; -C 195 ; WX 400 ; N circumflex ; B 47 541 353 720 ; -C 196 ; WX 400 ; N tilde ; B 22 563 377 682 ; -C 197 ; WX 400 ; N macron ; B 56 597 344 656 ; -C 198 ; WX 400 ; N breve ; B 63 568 337 704 ; -C 199 ; WX 400 ; N dotaccent ; B 140 570 260 683 ; -C 200 ; WX 400 ; N dieresis ; B 36 570 364 683 ; -C 202 ; WX 400 ; N ring ; B 92 550 308 752 ; -C 203 ; WX 400 ; N cedilla ; B 163 -230 329 0 ; -C 205 ; WX 400 ; N hungarumlaut ; B 101 546 380 750 ; -C 206 ; WX 400 ; N ogonek ; B 103 -230 295 0 ; -C 207 ; WX 400 ; N caron ; B 47 541 353 720 ; -C 208 ; WX 1000 ; N emdash ; B 0 221 1000 279 ; -C 225 ; WX 876 ; N AE ; B -63 0 847 692 ; -C 227 ; WX 390 ; N ordfeminine ; B 40 265 364 590 ; -C 232 ; WX 574 ; N Lslash ; B 36 0 572 692 ; -C 233 ; WX 762 ; N Oslash ; B 48 -53 714 739 ; -C 234 ; WX 1025 ; N OE ; B 48 0 996 692 ; -C 235 ; WX 398 ; N ordmasculine ; B 35 265 363 590 ; -C 241 ; WX 797 ; N ae ; B 49 -12 764 502 ; -C 245 ; WX 291 ; N dotlessi ; B 32 0 276 502 ; -C 248 ; WX 294 ; N lslash ; B 14 0 293 742 ; -C 249 ; WX 577 ; N oslash ; B 49 -41 528 532 ; -C 250 ; WX 882 ; N oe ; B 49 -12 849 502 ; -C 251 ; WX 601 ; N germandbls ; B 22 -12 573 742 ; -C -1 ; WX 380 ; N onesuperior ; B 81 272 307 680 ; -C -1 ; WX 570 ; N minus ; B 43 221 527 279 ; -C -1 ; WX 350 ; N degree ; B 37 404 313 680 ; -C -1 ; WX 577 ; N oacute ; B 49 -12 528 723 ; -C -1 ; WX 762 ; N Odieresis ; B 48 -15 714 841 ; -C -1 ; WX 577 ; N odieresis ; B 49 -12 528 683 ; -C -1 ; WX 606 ; N Eacute ; B 35 0 577 890 ; -C -1 ; WX 606 ; N ucircumflex ; B 26 -12 591 720 ; -C -1 ; WX 860 ; N onequarter ; B 65 -27 795 695 ; -C -1 ; WX 570 ; N logicalnot ; B 43 102 527 389 ; -C -1 ; WX 606 ; N Ecircumflex ; B 35 0 577 876 ; -C -1 ; WX 860 ; N onehalf ; B 58 -27 807 695 ; -C -1 ; WX 762 ; N Otilde ; B 48 -15 714 842 ; -C -1 ; WX 606 ; N uacute ; B 26 -12 591 723 ; -C -1 ; WX 514 ; N eacute ; B 49 -12 481 723 ; -C -1 ; WX 291 ; N iacute ; B 32 0 317 723 ; -C -1 ; WX 606 ; N Egrave ; B 35 0 577 890 ; -C -1 ; WX 291 ; N icircumflex ; B -3 0 304 720 ; -C -1 ; WX 606 ; N mu ; B 26 -246 591 502 ; -C -1 ; WX 228 ; N brokenbar ; B 80 -175 148 675 ; -C -1 ; WX 606 ; N thorn ; B 23 -230 557 722 ; -C -1 ; WX 627 ; N Aring ; B -32 0 647 861 ; -C -1 ; WX 506 ; N yacute ; B -5 -242 528 723 ; -C -1 ; WX 588 ; N Ydieresis ; B -12 0 632 841 ; -C -1 ; WX 1100 ; N trademark ; B 45 277 1048 692 ; -C -1 ; WX 818 ; N registered ; B 45 -15 773 707 ; -C -1 ; WX 577 ; N ocircumflex ; B 49 -12 528 720 ; -C -1 ; WX 635 ; N Agrave ; B -29 0 650 890 ; -C -1 ; WX 541 ; N Scaron ; B 50 -15 504 882 ; -C -1 ; WX 791 ; N Ugrave ; B 29 -15 762 890 ; -C -1 ; WX 606 ; N Edieresis ; B 35 0 577 841 ; -C -1 ; WX 791 ; N Uacute ; B 29 -15 762 890 ; -C -1 ; WX 577 ; N otilde ; B 49 -12 528 682 ; -C -1 ; WX 619 ; N ntilde ; B 32 0 604 682 ; -C -1 ; WX 506 ; N ydieresis ; B -5 -242 528 683 ; -C -1 ; WX 635 ; N Aacute ; B -29 0 650 890 ; -C -1 ; WX 577 ; N eth ; B 49 -12 528 742 ; -C -1 ; WX 523 ; N acircumflex ; B 49 -12 525 720 ; -C -1 ; WX 523 ; N aring ; B 49 -12 525 752 ; -C -1 ; WX 762 ; N Ograve ; B 48 -15 714 890 ; -C -1 ; WX 496 ; N ccedilla ; B 49 -230 473 502 ; -C -1 ; WX 570 ; N multiply ; B 63 22 507 478 ; -C -1 ; WX 570 ; N divide ; B 43 26 527 474 ; -C -1 ; WX 380 ; N twosuperior ; B 32 272 348 680 ; -C -1 ; WX 780 ; N Ntilde ; B 34 0 753 842 ; -C -1 ; WX 606 ; N ugrave ; B 26 -12 591 723 ; -C -1 ; WX 791 ; N Ucircumflex ; B 29 -15 762 876 ; -C -1 ; WX 635 ; N Atilde ; B -29 0 650 842 ; -C -1 ; WX 480 ; N zcaron ; B 19 0 462 720 ; -C -1 ; WX 291 ; N idieresis ; B -19 0 310 683 ; -C -1 ; WX 635 ; N Acircumflex ; B -29 0 650 876 ; -C -1 ; WX 349 ; N Icircumflex ; B 22 0 328 876 ; -C -1 ; WX 588 ; N Yacute ; B -12 0 632 890 ; -C -1 ; WX 762 ; N Oacute ; B 48 -15 714 890 ; -C -1 ; WX 635 ; N Adieresis ; B -29 0 650 841 ; -C -1 ; WX 610 ; N Zcaron ; B 9 0 594 882 ; -C -1 ; WX 523 ; N agrave ; B 49 -12 525 723 ; -C -1 ; WX 380 ; N threesuperior ; B 36 265 339 680 ; -C -1 ; WX 577 ; N ograve ; B 49 -12 528 723 ; -C -1 ; WX 860 ; N threequarters ; B 50 -27 808 695 ; -C -1 ; WX 785 ; N Eth ; B 20 0 737 692 ; -C -1 ; WX 570 ; N plusminus ; B 43 0 527 556 ; -C -1 ; WX 606 ; N udieresis ; B 26 -12 591 683 ; -C -1 ; WX 514 ; N edieresis ; B 49 -12 481 683 ; -C -1 ; WX 523 ; N aacute ; B 49 -12 525 723 ; -C -1 ; WX 291 ; N igrave ; B -35 0 276 723 ; -C -1 ; WX 349 ; N Idieresis ; B 13 0 337 841 ; -C -1 ; WX 523 ; N adieresis ; B 49 -12 525 683 ; -C -1 ; WX 349 ; N Iacute ; B 35 0 371 890 ; -C -1 ; WX 818 ; N copyright ; B 45 -15 773 707 ; -C -1 ; WX 349 ; N Igrave ; B -17 0 314 890 ; -C -1 ; WX 680 ; N Ccedilla ; B 48 -230 649 707 ; -C -1 ; WX 436 ; N scaron ; B 47 -12 400 720 ; -C -1 ; WX 514 ; N egrave ; B 49 -12 481 723 ; -C -1 ; WX 762 ; N Ocircumflex ; B 48 -15 714 876 ; -C -1 ; WX 593 ; N Thorn ; B 35 0 556 692 ; -C -1 ; WX 523 ; N atilde ; B 49 -12 525 682 ; -C -1 ; WX 791 ; N Udieresis ; B 29 -15 762 841 ; -C -1 ; WX 514 ; N ecircumflex ; B 49 -12 481 720 ; -EndCharMetrics -StartKernData -StartKernPairs 712 - -KPX A z 6 -KPX A y -50 -KPX A w -45 -KPX A v -60 -KPX A u -25 -KPX A t -12 -KPX A quoteright -120 -KPX A quotedblright -120 -KPX A q -6 -KPX A p -18 -KPX A o -12 -KPX A e -6 -KPX A d -12 -KPX A c -12 -KPX A b -12 -KPX A Y -70 -KPX A X -6 -KPX A W -58 -KPX A V -72 -KPX A U -50 -KPX A T -70 -KPX A Q -24 -KPX A O -24 -KPX A G -24 -KPX A C -24 - -KPX B y -18 -KPX B u -12 -KPX B r -12 -KPX B period -30 -KPX B o -6 -KPX B l -12 -KPX B i -12 -KPX B h -12 -KPX B e -6 -KPX B comma -20 -KPX B a -12 -KPX B W -25 -KPX B V -20 -KPX B U -20 -KPX B T -20 - -KPX C z -18 -KPX C y -24 -KPX C u -18 -KPX C r -6 -KPX C o -12 -KPX C e -12 -KPX C a -12 -KPX C Q -6 -KPX C O -6 -KPX C G -6 -KPX C C -6 - -KPX D y 6 -KPX D u -12 -KPX D r -12 -KPX D quoteright -20 -KPX D quotedblright -20 -KPX D period -60 -KPX D i -6 -KPX D h -12 -KPX D e -6 -KPX D comma -50 -KPX D a -6 -KPX D Y -45 -KPX D W -35 -KPX D V -35 - -KPX E z -6 -KPX E y -30 -KPX E x -6 -KPX E w -24 -KPX E v -24 -KPX E u -12 -KPX E t -18 -KPX E r -4 -KPX E q -6 -KPX E p -18 -KPX E o -6 -KPX E n -4 -KPX E m -4 -KPX E l 5 -KPX E k 5 -KPX E j -6 -KPX E i -6 -KPX E g -6 -KPX E f -12 -KPX E e -6 -KPX E d -6 -KPX E c -6 -KPX E b -12 -KPX E Y -6 -KPX E W -6 -KPX E V -6 - -KPX F y -18 -KPX F u -12 -KPX F r -20 -KPX F period -180 -KPX F o -36 -KPX F l -12 -KPX F i -10 -KPX F endash 20 -KPX F e -36 -KPX F comma -180 -KPX F a -48 -KPX F A -60 - -KPX G y -18 -KPX G u -12 -KPX G r -5 -KPX G o 5 -KPX G n -5 -KPX G l -6 -KPX G i -12 -KPX G h -12 -KPX G e 5 -KPX G a -12 - -KPX H y -24 -KPX H u -26 -KPX H o -30 -KPX H i -18 -KPX H e -30 -KPX H a -24 - -KPX I z -6 -KPX I y -6 -KPX I x -6 -KPX I w -18 -KPX I v -24 -KPX I u -26 -KPX I t -24 -KPX I s -18 -KPX I r -12 -KPX I p -26 -KPX I o -30 -KPX I n -18 -KPX I m -18 -KPX I l -6 -KPX I k -6 -KPX I h -6 -KPX I g -10 -KPX I f -6 -KPX I e -30 -KPX I d -30 -KPX I c -30 -KPX I b -6 -KPX I a -24 - -KPX J y -12 -KPX J u -36 -KPX J o -30 -KPX J i -20 -KPX J e -30 -KPX J bracketright 20 -KPX J braceright 20 -KPX J a -36 - -KPX K y -60 -KPX K w -70 -KPX K v -70 -KPX K u -42 -KPX K o -30 -KPX K i 6 -KPX K e -24 -KPX K a -12 -KPX K Q -42 -KPX K O -42 -KPX K G -42 -KPX K C -42 - -KPX L y -52 -KPX L w -58 -KPX L u -12 -KPX L quoteright -130 -KPX L quotedblright -50 -KPX L l 6 -KPX L j -6 -KPX L Y -70 -KPX L W -90 -KPX L V -100 -KPX L U -24 -KPX L T -100 -KPX L Q -18 -KPX L O -10 -KPX L G -18 -KPX L C -18 -KPX L A 12 - -KPX M y -24 -KPX M u -36 -KPX M o -30 -KPX M n -6 -KPX M j -12 -KPX M i -12 -KPX M e -30 -KPX M d -30 -KPX M c -30 -KPX M a -12 - -KPX N y -24 -KPX N u -30 -KPX N o -30 -KPX N i -24 -KPX N e -30 -KPX N a -30 - -KPX O z -6 -KPX O u -6 -KPX O t -6 -KPX O s -6 -KPX O q -6 -KPX O period -60 -KPX O p -6 -KPX O o -6 -KPX O n -5 -KPX O m -5 -KPX O l -6 -KPX O k -6 -KPX O i -5 -KPX O h -12 -KPX O g -6 -KPX O e -6 -KPX O d -6 -KPX O comma -50 -KPX O c -6 -KPX O a -12 -KPX O Y -55 -KPX O X -24 -KPX O W -30 -KPX O V -18 -KPX O T -30 -KPX O A -18 - -KPX P u -12 -KPX P t -6 -KPX P s -24 -KPX P r -12 -KPX P period -200 -KPX P o -30 -KPX P n -12 -KPX P l -6 -KPX P hyphen -40 -KPX P h -6 -KPX P e -30 -KPX P comma -200 -KPX P a -36 -KPX P I -6 -KPX P H -12 -KPX P E -6 -KPX P A -55 - -KPX Q u -6 -KPX Q a -18 -KPX Q Y -30 -KPX Q X -24 -KPX Q W -24 -KPX Q V -18 -KPX Q U -30 -KPX Q T -24 -KPX Q A -18 - -KPX R y -20 -KPX R u -12 -KPX R quoteright -20 -KPX R quotedblright -20 -KPX R o -20 -KPX R hyphen -30 -KPX R e -20 -KPX R d -20 -KPX R a -12 -KPX R Y -45 -KPX R W -24 -KPX R V -32 -KPX R U -30 -KPX R T -32 -KPX R Q -24 -KPX R O -24 -KPX R G -24 -KPX R C -24 - -KPX S y -25 -KPX S w -30 -KPX S v -30 -KPX S u -24 -KPX S t -24 -KPX S r -20 -KPX S quoteright -10 -KPX S quotedblright -10 -KPX S q -5 -KPX S p -24 -KPX S o -12 -KPX S n -20 -KPX S m -20 -KPX S l -18 -KPX S k -24 -KPX S j -12 -KPX S i -20 -KPX S h -12 -KPX S e -12 -KPX S a -18 - -KPX T z -64 -KPX T y -84 -KPX T w -100 -KPX T u -82 -KPX T semicolon -56 -KPX T s -82 -KPX T r -82 -KPX T quoteright 24 -KPX T period -110 -KPX T parenright 54 -KPX T o -100 -KPX T m -82 -KPX T i -34 -KPX T hyphen -100 -KPX T endash -50 -KPX T emdash -50 -KPX T e -100 -KPX T comma -110 -KPX T colon -50 -KPX T bracketright 54 -KPX T braceright 54 -KPX T a -100 -KPX T Y 12 -KPX T X 18 -KPX T W 6 -KPX T V 6 -KPX T T 12 -KPX T S -12 -KPX T Q -18 -KPX T O -18 -KPX T G -18 -KPX T C -18 -KPX T A -65 - -KPX U z -30 -KPX U y -20 -KPX U x -30 -KPX U v -20 -KPX U t -36 -KPX U s -40 -KPX U r -40 -KPX U p -42 -KPX U n -40 -KPX U m -40 -KPX U l -12 -KPX U k -12 -KPX U i -28 -KPX U h -6 -KPX U g -50 -KPX U f -12 -KPX U d -45 -KPX U c -45 -KPX U b -12 -KPX U a -40 -KPX U A -40 - -KPX V y -36 -KPX V u -40 -KPX V semicolon -45 -KPX V r -70 -KPX V quoteright 36 -KPX V quotedblright 20 -KPX V period -140 -KPX V parenright 85 -KPX V o -70 -KPX V i 6 -KPX V hyphen -60 -KPX V endash -20 -KPX V emdash -20 -KPX V e -70 -KPX V comma -140 -KPX V colon -45 -KPX V bracketright 64 -KPX V braceright 64 -KPX V a -60 -KPX V T 6 -KPX V Q -12 -KPX V O -12 -KPX V G -12 -KPX V C -12 -KPX V A -60 - -KPX W y -50 -KPX W u -46 -KPX W semicolon -40 -KPX W r -45 -KPX W quoteright 36 -KPX W quotedblright 20 -KPX W period -110 -KPX W parenright 85 -KPX W o -65 -KPX W m -45 -KPX W i -10 -KPX W hyphen -40 -KPX W e -65 -KPX W d -65 -KPX W comma -100 -KPX W colon -40 -KPX W bracketright 64 -KPX W braceright 64 -KPX W a -60 -KPX W T 18 -KPX W Q -6 -KPX W O -6 -KPX W G -6 -KPX W C -6 -KPX W A -48 - -KPX X y -18 -KPX X u -24 -KPX X quoteright 15 -KPX X e -6 -KPX X a -6 -KPX X Q -24 -KPX X O -30 -KPX X G -30 -KPX X C -30 -KPX X A 6 - -KPX Y v -50 -KPX Y u -54 -KPX Y t -46 -KPX Y semicolon -37 -KPX Y quoteright 36 -KPX Y quotedblright 20 -KPX Y q -100 -KPX Y period -90 -KPX Y parenright 60 -KPX Y o -90 -KPX Y l 10 -KPX Y hyphen -50 -KPX Y emdash -20 -KPX Y e -90 -KPX Y d -90 -KPX Y comma -90 -KPX Y colon -50 -KPX Y bracketright 64 -KPX Y braceright 64 -KPX Y a -68 -KPX Y Y 12 -KPX Y X 12 -KPX Y W 12 -KPX Y V 12 -KPX Y T 12 -KPX Y Q -18 -KPX Y O -18 -KPX Y G -18 -KPX Y C -18 -KPX Y A -32 - -KPX Z y -36 -KPX Z w -36 -KPX Z u -6 -KPX Z o -12 -KPX Z i -12 -KPX Z e -6 -KPX Z a -6 -KPX Z Q -20 -KPX Z O -20 -KPX Z G -30 -KPX Z C -20 -KPX Z A 20 - -KPX a quoteright -70 -KPX a quotedblright -80 - -KPX b y -25 -KPX b w -30 -KPX b v -35 -KPX b quoteright -70 -KPX b quotedblright -70 -KPX b period -40 -KPX b comma -40 - -KPX braceleft Y 64 -KPX braceleft W 64 -KPX braceleft V 64 -KPX braceleft T 54 -KPX braceleft J 80 - -KPX bracketleft Y 64 -KPX bracketleft W 64 -KPX bracketleft V 64 -KPX bracketleft T 54 -KPX bracketleft J 80 - -KPX c quoteright -28 -KPX c quotedblright -28 -KPX c period -10 - -KPX comma quoteright -50 -KPX comma quotedblright -50 - -KPX d quoteright -24 -KPX d quotedblright -24 - -KPX e z -4 -KPX e quoteright -60 -KPX e quotedblright -60 -KPX e period -20 -KPX e comma -20 - -KPX f quotesingle 30 -KPX f quoteright 65 -KPX f quotedblright 56 -KPX f quotedbl 30 -KPX f parenright 100 -KPX f bracketright 100 -KPX f braceright 100 - -KPX g quoteright -18 -KPX g quotedblright -10 - -KPX h quoteright -80 -KPX h quotedblright -80 - -KPX j quoteright -20 -KPX j quotedblright -20 -KPX j period -30 -KPX j comma -30 - -KPX k quoteright -40 -KPX k quotedblright -40 - -KPX l quoteright -10 -KPX l quotedblright -10 - -KPX m quoteright -80 -KPX m quotedblright -80 - -KPX n quoteright -80 -KPX n quotedblright -80 - -KPX o z -12 -KPX o y -30 -KPX o x -18 -KPX o w -30 -KPX o v -30 -KPX o quoteright -70 -KPX o quotedblright -70 -KPX o period -40 -KPX o comma -40 - -KPX p z -20 -KPX p y -25 -KPX p w -30 -KPX p quoteright -70 -KPX p quotedblright -70 -KPX p period -40 -KPX p comma -40 - -KPX parenleft Y 64 -KPX parenleft W 64 -KPX parenleft V 64 -KPX parenleft T 64 -KPX parenleft J 80 - -KPX period quoteright -50 -KPX period quotedblright -50 - -KPX q quoteright -50 -KPX q quotedblright -50 -KPX q period -20 -KPX q comma -10 - -KPX quotedblleft z -60 -KPX quotedblleft y -30 -KPX quotedblleft x -40 -KPX quotedblleft w -20 -KPX quotedblleft v -20 -KPX quotedblleft u -40 -KPX quotedblleft t -40 -KPX quotedblleft s -50 -KPX quotedblleft r -50 -KPX quotedblleft q -80 -KPX quotedblleft p -50 -KPX quotedblleft o -80 -KPX quotedblleft n -50 -KPX quotedblleft m -50 -KPX quotedblleft g -70 -KPX quotedblleft f -50 -KPX quotedblleft e -80 -KPX quotedblleft d -80 -KPX quotedblleft c -80 -KPX quotedblleft a -70 -KPX quotedblleft Z -20 -KPX quotedblleft Y 12 -KPX quotedblleft W 18 -KPX quotedblleft V 18 -KPX quotedblleft U -20 -KPX quotedblleft T 10 -KPX quotedblleft S -20 -KPX quotedblleft R -20 -KPX quotedblleft Q -20 -KPX quotedblleft P -20 -KPX quotedblleft O -30 -KPX quotedblleft N -20 -KPX quotedblleft M -20 -KPX quotedblleft L -20 -KPX quotedblleft K -20 -KPX quotedblleft J -40 -KPX quotedblleft I -20 -KPX quotedblleft H -20 -KPX quotedblleft G -30 -KPX quotedblleft F -20 -KPX quotedblleft E -20 -KPX quotedblleft D -20 -KPX quotedblleft C -30 -KPX quotedblleft B -20 -KPX quotedblleft A -130 - -KPX quotedblright period -130 -KPX quotedblright comma -130 - -KPX quoteleft z -40 -KPX quoteleft y -35 -KPX quoteleft x -30 -KPX quoteleft w -20 -KPX quoteleft v -20 -KPX quoteleft u -50 -KPX quoteleft t -40 -KPX quoteleft s -45 -KPX quoteleft r -50 -KPX quoteleft quoteleft -72 -KPX quoteleft q -70 -KPX quoteleft p -50 -KPX quoteleft o -70 -KPX quoteleft n -50 -KPX quoteleft m -50 -KPX quoteleft g -65 -KPX quoteleft f -40 -KPX quoteleft e -70 -KPX quoteleft d -70 -KPX quoteleft c -70 -KPX quoteleft a -60 -KPX quoteleft Z -20 -KPX quoteleft Y 18 -KPX quoteleft X 12 -KPX quoteleft W 18 -KPX quoteleft V 18 -KPX quoteleft U -20 -KPX quoteleft T 10 -KPX quoteleft R -20 -KPX quoteleft Q -20 -KPX quoteleft P -20 -KPX quoteleft O -30 -KPX quoteleft N -20 -KPX quoteleft M -20 -KPX quoteleft L -20 -KPX quoteleft K -20 -KPX quoteleft J -40 -KPX quoteleft I -20 -KPX quoteleft H -20 -KPX quoteleft G -40 -KPX quoteleft F -20 -KPX quoteleft E -20 -KPX quoteleft D -20 -KPX quoteleft C -30 -KPX quoteleft B -20 -KPX quoteleft A -130 - -KPX quoteright v -40 -KPX quoteright t -75 -KPX quoteright s -110 -KPX quoteright r -70 -KPX quoteright quoteright -72 -KPX quoteright period -130 -KPX quoteright m -70 -KPX quoteright l -6 -KPX quoteright d -120 -KPX quoteright comma -130 - -KPX r z 10 -KPX r y 18 -KPX r x 12 -KPX r w 18 -KPX r v 18 -KPX r u 8 -KPX r t 8 -KPX r semicolon 10 -KPX r quoteright -20 -KPX r quotedblright -20 -KPX r q -6 -KPX r period -60 -KPX r o -6 -KPX r n 8 -KPX r m 8 -KPX r k -6 -KPX r i 8 -KPX r hyphen -20 -KPX r h 6 -KPX r g -6 -KPX r f 8 -KPX r e -20 -KPX r d -20 -KPX r comma -60 -KPX r colon 10 -KPX r c -20 -KPX r a -10 - -KPX s quoteright -40 -KPX s quotedblright -40 -KPX s period -20 -KPX s comma -10 - -KPX space quotesinglbase -60 -KPX space quoteleft -40 -KPX space quotedblleft -40 -KPX space quotedblbase -60 -KPX space Y -60 -KPX space W -60 -KPX space V -60 -KPX space T -36 - -KPX t quoteright -18 -KPX t quotedblright -18 - -KPX u quoteright -30 -KPX u quotedblright -30 - -KPX v semicolon 10 -KPX v quoteright 20 -KPX v quotedblright 20 -KPX v q -10 -KPX v period -90 -KPX v o -5 -KPX v e -5 -KPX v d -10 -KPX v comma -90 -KPX v colon 10 -KPX v c -6 -KPX v a -6 - -KPX w semicolon 10 -KPX w quoteright 20 -KPX w quotedblright 20 -KPX w q -6 -KPX w period -80 -KPX w e -6 -KPX w d -6 -KPX w comma -75 -KPX w colon 10 -KPX w c -6 - -KPX x quoteright -10 -KPX x quotedblright -20 -KPX x q -6 -KPX x o -6 -KPX x d -12 -KPX x c -12 - -KPX y semicolon 10 -KPX y q -6 -KPX y period -95 -KPX y o -6 -KPX y hyphen -30 -KPX y e -6 -KPX y d -6 -KPX y comma -85 -KPX y colon 10 -KPX y c -6 - -KPX z quoteright -20 -KPX z quotedblright -30 -KPX z o -6 -KPX z e -6 -KPX z d -6 -KPX z c -6 -EndKernPairs -EndKernData -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/putri8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/putri8a.afm deleted file mode 100644 index b3dd45b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/putri8a.afm +++ /dev/null @@ -1,1008 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1989, 1991 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Fri Jan 17 13:15:45 1992 -Comment UniqueID 37666 -Comment VMusage 34143 41035 -FontName Utopia-Italic -FullName Utopia Italic -FamilyName Utopia -Weight Regular -ItalicAngle -13 -IsFixedPitch false -FontBBox -201 -250 1170 890 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.002 -Notice Copyright (c) 1989, 1991 Adobe Systems Incorporated. All Rights Reserved.Utopia is a registered trademark of Adobe Systems Incorporated. -EncodingScheme AdobeStandardEncoding -CapHeight 692 -XHeight 502 -Ascender 742 -Descender -242 -StartCharMetrics 228 -C 32 ; WX 225 ; N space ; B 0 0 0 0 ; -C 33 ; WX 240 ; N exclam ; B 34 -12 290 707 ; -C 34 ; WX 402 ; N quotedbl ; B 171 469 454 742 ; -C 35 ; WX 530 ; N numbersign ; B 54 0 585 668 ; -C 36 ; WX 530 ; N dollar ; B 31 -109 551 743 ; -C 37 ; WX 826 ; N percent ; B 98 -25 795 702 ; -C 38 ; WX 725 ; N ampersand ; B 60 -12 703 680 ; -C 39 ; WX 216 ; N quoteright ; B 112 482 265 742 ; -C 40 ; WX 350 ; N parenleft ; B 106 -128 458 692 ; -C 41 ; WX 350 ; N parenright ; B -46 -128 306 692 ; -C 42 ; WX 412 ; N asterisk ; B 106 356 458 707 ; -C 43 ; WX 570 ; N plus ; B 58 0 542 490 ; -C 44 ; WX 265 ; N comma ; B 11 -134 173 142 ; -C 45 ; WX 392 ; N hyphen ; B 82 216 341 286 ; -C 46 ; WX 265 ; N period ; B 47 -12 169 113 ; -C 47 ; WX 270 ; N slash ; B 0 -15 341 707 ; -C 48 ; WX 530 ; N zero ; B 60 -12 541 680 ; -C 49 ; WX 530 ; N one ; B 74 0 429 680 ; -C 50 ; WX 530 ; N two ; B -2 0 538 680 ; -C 51 ; WX 530 ; N three ; B 19 -12 524 680 ; -C 52 ; WX 530 ; N four ; B 32 0 509 668 ; -C 53 ; WX 530 ; N five ; B 24 -12 550 668 ; -C 54 ; WX 530 ; N six ; B 56 -12 551 680 ; -C 55 ; WX 530 ; N seven ; B 130 -12 600 668 ; -C 56 ; WX 530 ; N eight ; B 46 -12 535 680 ; -C 57 ; WX 530 ; N nine ; B 51 -12 536 680 ; -C 58 ; WX 265 ; N colon ; B 47 -12 248 490 ; -C 59 ; WX 265 ; N semicolon ; B 11 -134 248 490 ; -C 60 ; WX 570 ; N less ; B 51 1 529 497 ; -C 61 ; WX 570 ; N equal ; B 58 111 542 389 ; -C 62 ; WX 570 ; N greater ; B 51 1 529 497 ; -C 63 ; WX 425 ; N question ; B 115 -12 456 707 ; -C 64 ; WX 794 ; N at ; B 88 -15 797 707 ; -C 65 ; WX 624 ; N A ; B -58 0 623 692 ; -C 66 ; WX 632 ; N B ; B 3 0 636 692 ; -C 67 ; WX 661 ; N C ; B 79 -15 723 707 ; -C 68 ; WX 763 ; N D ; B 5 0 767 692 ; -C 69 ; WX 596 ; N E ; B 3 0 657 692 ; -C 70 ; WX 571 ; N F ; B 3 0 660 692 ; -C 71 ; WX 709 ; N G ; B 79 -15 737 707 ; -C 72 ; WX 775 ; N H ; B 5 0 857 692 ; -C 73 ; WX 345 ; N I ; B 5 0 428 692 ; -C 74 ; WX 352 ; N J ; B -78 -119 436 692 ; -C 75 ; WX 650 ; N K ; B 5 -5 786 692 ; -C 76 ; WX 565 ; N L ; B 5 0 568 692 ; -C 77 ; WX 920 ; N M ; B -4 0 1002 692 ; -C 78 ; WX 763 ; N N ; B -4 0 855 692 ; -C 79 ; WX 753 ; N O ; B 79 -15 754 707 ; -C 80 ; WX 614 ; N P ; B 5 0 646 692 ; -C 81 ; WX 753 ; N Q ; B 79 -203 754 707 ; -C 82 ; WX 640 ; N R ; B 5 0 642 692 ; -C 83 ; WX 533 ; N S ; B 34 -15 542 707 ; -C 84 ; WX 606 ; N T ; B 102 0 708 692 ; -C 85 ; WX 794 ; N U ; B 131 -15 880 692 ; -C 86 ; WX 637 ; N V ; B 96 0 786 692 ; -C 87 ; WX 946 ; N W ; B 86 0 1075 692 ; -C 88 ; WX 632 ; N X ; B -36 0 735 692 ; -C 89 ; WX 591 ; N Y ; B 96 0 744 692 ; -C 90 ; WX 622 ; N Z ; B -20 0 703 692 ; -C 91 ; WX 330 ; N bracketleft ; B 69 -128 414 692 ; -C 92 ; WX 390 ; N backslash ; B 89 -15 371 707 ; -C 93 ; WX 330 ; N bracketright ; B -21 -128 324 692 ; -C 94 ; WX 570 ; N asciicircum ; B 83 228 547 668 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 216 ; N quoteleft ; B 130 488 283 748 ; -C 97 ; WX 561 ; N a ; B 31 -12 563 502 ; -C 98 ; WX 559 ; N b ; B 47 -12 557 742 ; -C 99 ; WX 441 ; N c ; B 46 -12 465 502 ; -C 100 ; WX 587 ; N d ; B 37 -12 612 742 ; -C 101 ; WX 453 ; N e ; B 45 -12 471 502 ; -C 102 ; WX 315 ; N f ; B -107 -242 504 742 ; L i fi ; L l fl ; -C 103 ; WX 499 ; N g ; B -5 -242 573 512 ; -C 104 ; WX 607 ; N h ; B 57 -12 588 742 ; -C 105 ; WX 317 ; N i ; B 79 -12 328 715 ; -C 106 ; WX 309 ; N j ; B -95 -242 330 715 ; -C 107 ; WX 545 ; N k ; B 57 -12 567 742 ; -C 108 ; WX 306 ; N l ; B 76 -12 331 742 ; -C 109 ; WX 912 ; N m ; B 63 -12 894 502 ; -C 110 ; WX 618 ; N n ; B 63 -12 600 502 ; -C 111 ; WX 537 ; N o ; B 49 -12 522 502 ; -C 112 ; WX 590 ; N p ; B 22 -242 586 502 ; -C 113 ; WX 559 ; N q ; B 38 -242 567 525 ; -C 114 ; WX 402 ; N r ; B 69 -12 448 502 ; -C 115 ; WX 389 ; N s ; B 19 -12 397 502 ; -C 116 ; WX 341 ; N t ; B 84 -12 404 616 ; -C 117 ; WX 618 ; N u ; B 89 -12 609 502 ; -C 118 ; WX 510 ; N v ; B 84 -12 528 502 ; -C 119 ; WX 785 ; N w ; B 87 -12 808 502 ; -C 120 ; WX 516 ; N x ; B -4 -12 531 502 ; -C 121 ; WX 468 ; N y ; B -40 -242 505 502 ; -C 122 ; WX 468 ; N z ; B 4 -12 483 490 ; -C 123 ; WX 340 ; N braceleft ; B 100 -128 423 692 ; -C 124 ; WX 270 ; N bar ; B 130 -250 198 750 ; -C 125 ; WX 340 ; N braceright ; B -20 -128 302 692 ; -C 126 ; WX 570 ; N asciitilde ; B 98 176 522 318 ; -C 161 ; WX 240 ; N exclamdown ; B -18 -217 238 502 ; -C 162 ; WX 530 ; N cent ; B 94 -21 563 669 ; -C 163 ; WX 530 ; N sterling ; B 9 0 549 680 ; -C 164 ; WX 100 ; N fraction ; B -201 -24 369 698 ; -C 165 ; WX 530 ; N yen ; B 72 0 645 668 ; -C 166 ; WX 530 ; N florin ; B 4 -135 588 691 ; -C 167 ; WX 530 ; N section ; B 55 -115 533 707 ; -C 168 ; WX 530 ; N currency ; B 56 90 536 578 ; -C 169 ; WX 216 ; N quotesingle ; B 161 469 274 742 ; -C 170 ; WX 402 ; N quotedblleft ; B 134 488 473 748 ; -C 171 ; WX 462 ; N guillemotleft ; B 79 41 470 435 ; -C 172 ; WX 277 ; N guilsinglleft ; B 71 41 267 435 ; -C 173 ; WX 277 ; N guilsinglright ; B 44 41 240 435 ; -C 174 ; WX 607 ; N fi ; B -107 -242 589 742 ; -C 175 ; WX 603 ; N fl ; B -107 -242 628 742 ; -C 177 ; WX 500 ; N endash ; B 12 221 524 279 ; -C 178 ; WX 500 ; N dagger ; B 101 -125 519 717 ; -C 179 ; WX 490 ; N daggerdbl ; B 39 -119 509 717 ; -C 180 ; WX 265 ; N periodcentered ; B 89 187 211 312 ; -C 182 ; WX 560 ; N paragraph ; B 109 -101 637 692 ; -C 183 ; WX 500 ; N bullet ; B 110 192 429 512 ; -C 184 ; WX 216 ; N quotesinglbase ; B -7 -109 146 151 ; -C 185 ; WX 402 ; N quotedblbase ; B -7 -109 332 151 ; -C 186 ; WX 402 ; N quotedblright ; B 107 484 446 744 ; -C 187 ; WX 462 ; N guillemotright ; B 29 41 420 435 ; -C 188 ; WX 1000 ; N ellipsis ; B 85 -12 873 113 ; -C 189 ; WX 1200 ; N perthousand ; B 98 -25 1170 702 ; -C 191 ; WX 425 ; N questiondown ; B 3 -217 344 502 ; -C 193 ; WX 400 ; N grave ; B 146 542 368 723 ; -C 194 ; WX 400 ; N acute ; B 214 542 436 723 ; -C 195 ; WX 400 ; N circumflex ; B 187 546 484 720 ; -C 196 ; WX 400 ; N tilde ; B 137 563 492 682 ; -C 197 ; WX 400 ; N macron ; B 193 597 489 656 ; -C 198 ; WX 400 ; N breve ; B 227 568 501 698 ; -C 199 ; WX 402 ; N dotaccent ; B 252 570 359 680 ; -C 200 ; WX 400 ; N dieresis ; B 172 572 487 682 ; -C 202 ; WX 400 ; N ring ; B 186 550 402 752 ; -C 203 ; WX 400 ; N cedilla ; B 62 -230 241 0 ; -C 205 ; WX 400 ; N hungarumlaut ; B 176 546 455 750 ; -C 206 ; WX 350 ; N ogonek ; B 68 -219 248 0 ; -C 207 ; WX 400 ; N caron ; B 213 557 510 731 ; -C 208 ; WX 1000 ; N emdash ; B 12 221 1024 279 ; -C 225 ; WX 880 ; N AE ; B -88 0 941 692 ; -C 227 ; WX 425 ; N ordfeminine ; B 77 265 460 590 ; -C 232 ; WX 571 ; N Lslash ; B 11 0 574 692 ; -C 233 ; WX 753 ; N Oslash ; B 79 -45 754 736 ; -C 234 ; WX 1020 ; N OE ; B 79 0 1081 692 ; -C 235 ; WX 389 ; N ordmasculine ; B 86 265 420 590 ; -C 241 ; WX 779 ; N ae ; B 34 -12 797 514 ; -C 245 ; WX 317 ; N dotlessi ; B 79 -12 299 502 ; -C 248 ; WX 318 ; N lslash ; B 45 -12 376 742 ; -C 249 ; WX 537 ; N oslash ; B 49 -39 522 529 ; -C 250 ; WX 806 ; N oe ; B 49 -12 824 502 ; -C 251 ; WX 577 ; N germandbls ; B -107 -242 630 742 ; -C -1 ; WX 370 ; N onesuperior ; B 90 272 326 680 ; -C -1 ; WX 570 ; N minus ; B 58 221 542 279 ; -C -1 ; WX 400 ; N degree ; B 152 404 428 680 ; -C -1 ; WX 537 ; N oacute ; B 49 -12 530 723 ; -C -1 ; WX 753 ; N Odieresis ; B 79 -15 754 848 ; -C -1 ; WX 537 ; N odieresis ; B 49 -12 532 682 ; -C -1 ; WX 596 ; N Eacute ; B 3 0 657 890 ; -C -1 ; WX 618 ; N ucircumflex ; B 89 -12 609 720 ; -C -1 ; WX 890 ; N onequarter ; B 97 -24 805 698 ; -C -1 ; WX 570 ; N logicalnot ; B 58 102 542 389 ; -C -1 ; WX 596 ; N Ecircumflex ; B 3 0 657 876 ; -C -1 ; WX 890 ; N onehalf ; B 71 -24 812 698 ; -C -1 ; WX 753 ; N Otilde ; B 79 -15 754 842 ; -C -1 ; WX 618 ; N uacute ; B 89 -12 609 723 ; -C -1 ; WX 453 ; N eacute ; B 45 -12 508 723 ; -C -1 ; WX 317 ; N iacute ; B 79 -12 398 723 ; -C -1 ; WX 596 ; N Egrave ; B 3 0 657 890 ; -C -1 ; WX 317 ; N icircumflex ; B 79 -12 383 720 ; -C -1 ; WX 618 ; N mu ; B 11 -232 609 502 ; -C -1 ; WX 270 ; N brokenbar ; B 130 -175 198 675 ; -C -1 ; WX 584 ; N thorn ; B 16 -242 580 700 ; -C -1 ; WX 624 ; N Aring ; B -58 0 623 861 ; -C -1 ; WX 468 ; N yacute ; B -40 -242 505 723 ; -C -1 ; WX 591 ; N Ydieresis ; B 96 0 744 848 ; -C -1 ; WX 1100 ; N trademark ; B 91 277 1094 692 ; -C -1 ; WX 836 ; N registered ; B 91 -15 819 707 ; -C -1 ; WX 537 ; N ocircumflex ; B 49 -12 522 720 ; -C -1 ; WX 624 ; N Agrave ; B -58 0 623 890 ; -C -1 ; WX 533 ; N Scaron ; B 34 -15 561 888 ; -C -1 ; WX 794 ; N Ugrave ; B 131 -15 880 890 ; -C -1 ; WX 596 ; N Edieresis ; B 3 0 657 848 ; -C -1 ; WX 794 ; N Uacute ; B 131 -15 880 890 ; -C -1 ; WX 537 ; N otilde ; B 49 -12 525 682 ; -C -1 ; WX 618 ; N ntilde ; B 63 -12 600 682 ; -C -1 ; WX 468 ; N ydieresis ; B -40 -242 513 682 ; -C -1 ; WX 624 ; N Aacute ; B -58 0 642 890 ; -C -1 ; WX 537 ; N eth ; B 47 -12 521 742 ; -C -1 ; WX 561 ; N acircumflex ; B 31 -12 563 720 ; -C -1 ; WX 561 ; N aring ; B 31 -12 563 752 ; -C -1 ; WX 753 ; N Ograve ; B 79 -15 754 890 ; -C -1 ; WX 441 ; N ccedilla ; B 46 -230 465 502 ; -C -1 ; WX 570 ; N multiply ; B 88 22 532 478 ; -C -1 ; WX 570 ; N divide ; B 58 25 542 475 ; -C -1 ; WX 370 ; N twosuperior ; B 35 272 399 680 ; -C -1 ; WX 763 ; N Ntilde ; B -4 0 855 842 ; -C -1 ; WX 618 ; N ugrave ; B 89 -12 609 723 ; -C -1 ; WX 794 ; N Ucircumflex ; B 131 -15 880 876 ; -C -1 ; WX 624 ; N Atilde ; B -58 0 623 842 ; -C -1 ; WX 468 ; N zcaron ; B 4 -12 484 731 ; -C -1 ; WX 317 ; N idieresis ; B 79 -12 398 682 ; -C -1 ; WX 624 ; N Acircumflex ; B -58 0 623 876 ; -C -1 ; WX 345 ; N Icircumflex ; B 5 0 453 876 ; -C -1 ; WX 591 ; N Yacute ; B 96 0 744 890 ; -C -1 ; WX 753 ; N Oacute ; B 79 -15 754 890 ; -C -1 ; WX 624 ; N Adieresis ; B -58 0 623 848 ; -C -1 ; WX 622 ; N Zcaron ; B -20 0 703 888 ; -C -1 ; WX 561 ; N agrave ; B 31 -12 563 723 ; -C -1 ; WX 370 ; N threesuperior ; B 59 265 389 680 ; -C -1 ; WX 537 ; N ograve ; B 49 -12 522 723 ; -C -1 ; WX 890 ; N threequarters ; B 105 -24 816 698 ; -C -1 ; WX 770 ; N Eth ; B 12 0 774 692 ; -C -1 ; WX 570 ; N plusminus ; B 58 0 542 556 ; -C -1 ; WX 618 ; N udieresis ; B 89 -12 609 682 ; -C -1 ; WX 453 ; N edieresis ; B 45 -12 490 682 ; -C -1 ; WX 561 ; N aacute ; B 31 -12 571 723 ; -C -1 ; WX 317 ; N igrave ; B 55 -12 299 723 ; -C -1 ; WX 345 ; N Idieresis ; B 5 0 461 848 ; -C -1 ; WX 561 ; N adieresis ; B 31 -12 563 682 ; -C -1 ; WX 345 ; N Iacute ; B 5 0 506 890 ; -C -1 ; WX 836 ; N copyright ; B 91 -15 819 707 ; -C -1 ; WX 345 ; N Igrave ; B 5 0 428 890 ; -C -1 ; WX 661 ; N Ccedilla ; B 79 -230 723 707 ; -C -1 ; WX 389 ; N scaron ; B 19 -12 457 731 ; -C -1 ; WX 453 ; N egrave ; B 45 -12 471 723 ; -C -1 ; WX 753 ; N Ocircumflex ; B 79 -15 754 876 ; -C -1 ; WX 604 ; N Thorn ; B 5 0 616 692 ; -C -1 ; WX 561 ; N atilde ; B 31 -12 563 682 ; -C -1 ; WX 794 ; N Udieresis ; B 131 -15 880 848 ; -C -1 ; WX 453 ; N ecircumflex ; B 45 -12 475 720 ; -EndCharMetrics -StartKernData -StartKernPairs 690 - -KPX A y -20 -KPX A x 10 -KPX A w -30 -KPX A v -30 -KPX A u -10 -KPX A t -6 -KPX A s 15 -KPX A r -12 -KPX A quoteright -110 -KPX A quotedblright -110 -KPX A q 10 -KPX A p -12 -KPX A o -10 -KPX A n -18 -KPX A m -18 -KPX A l -18 -KPX A j 6 -KPX A h -6 -KPX A d 10 -KPX A c -6 -KPX A b -6 -KPX A a 12 -KPX A Y -76 -KPX A X -8 -KPX A W -80 -KPX A V -90 -KPX A U -60 -KPX A T -72 -KPX A Q -30 -KPX A O -30 -KPX A G -30 -KPX A C -30 - -KPX B y -6 -KPX B u -20 -KPX B r -15 -KPX B quoteright -40 -KPX B quotedblright -30 -KPX B o 6 -KPX B l -20 -KPX B k -15 -KPX B i -12 -KPX B h -15 -KPX B e 6 -KPX B a 12 -KPX B W -20 -KPX B V -50 -KPX B U -50 -KPX B T -20 - -KPX C z -6 -KPX C y -18 -KPX C u -18 -KPX C quotedblright 20 -KPX C i -5 -KPX C e -6 -KPX C a -6 - -KPX D y 18 -KPX D u -10 -KPX D quoteright -40 -KPX D quotedblright -50 -KPX D period -30 -KPX D o 6 -KPX D i 6 -KPX D h -25 -KPX D e 6 -KPX D comma -20 -KPX D a 6 -KPX D Y -70 -KPX D W -50 -KPX D V -60 - -KPX E z -6 -KPX E y -18 -KPX E x 5 -KPX E w -20 -KPX E v -18 -KPX E u -24 -KPX E t -18 -KPX E s 5 -KPX E r -6 -KPX E quoteright 10 -KPX E quotedblright 10 -KPX E q 10 -KPX E period 10 -KPX E p -12 -KPX E o -6 -KPX E n -12 -KPX E m -12 -KPX E l -12 -KPX E k -10 -KPX E j -6 -KPX E i -12 -KPX E g -12 -KPX E e 5 -KPX E d 10 -KPX E comma 10 -KPX E b -6 - -KPX F y -12 -KPX F u -30 -KPX F r -18 -KPX F quoteright 15 -KPX F quotedblright 35 -KPX F period -180 -KPX F o -30 -KPX F l -6 -KPX F i -12 -KPX F e -30 -KPX F comma -170 -KPX F a -30 -KPX F A -45 - -KPX G y -16 -KPX G u -22 -KPX G r -22 -KPX G quoteright -20 -KPX G quotedblright -20 -KPX G o 10 -KPX G n -22 -KPX G l -24 -KPX G i -12 -KPX G h -18 -KPX G e 10 -KPX G a 5 - -KPX H y -18 -KPX H u -30 -KPX H quoteright 10 -KPX H quotedblright 10 -KPX H o -12 -KPX H i -12 -KPX H e -12 -KPX H a -12 - -KPX I z -20 -KPX I y -6 -KPX I x -6 -KPX I w -30 -KPX I v -30 -KPX I u -30 -KPX I t -18 -KPX I s -18 -KPX I r -12 -KPX I quoteright 10 -KPX I quotedblright 10 -KPX I p -18 -KPX I o -12 -KPX I n -18 -KPX I m -18 -KPX I l -6 -KPX I k -6 -KPX I g -12 -KPX I f -6 -KPX I d -6 -KPX I c -12 -KPX I b -6 -KPX I a -6 - -KPX J y -12 -KPX J u -36 -KPX J quoteright 6 -KPX J quotedblright 15 -KPX J o -36 -KPX J i -30 -KPX J e -36 -KPX J braceright 10 -KPX J a -36 - -KPX K y -40 -KPX K w -30 -KPX K v -20 -KPX K u -24 -KPX K r -12 -KPX K quoteright 25 -KPX K quotedblright 40 -KPX K o -24 -KPX K n -18 -KPX K i -6 -KPX K h 6 -KPX K e -12 -KPX K a -6 -KPX K Q -24 -KPX K O -24 -KPX K G -24 -KPX K C -24 - -KPX L y -55 -KPX L w -30 -KPX L u -18 -KPX L quoteright -110 -KPX L quotedblright -110 -KPX L l -16 -KPX L j -18 -KPX L i -18 -KPX L a 10 -KPX L Y -80 -KPX L W -90 -KPX L V -110 -KPX L U -42 -KPX L T -80 -KPX L Q -48 -KPX L O -48 -KPX L G -48 -KPX L C -48 -KPX L A 30 - -KPX M y -18 -KPX M u -24 -KPX M quoteright 6 -KPX M quotedblright 15 -KPX M o -25 -KPX M n -12 -KPX M j -18 -KPX M i -12 -KPX M e -20 -KPX M d -10 -KPX M c -20 -KPX M a -6 - -KPX N y -18 -KPX N u -24 -KPX N quoteright 10 -KPX N quotedblright 10 -KPX N o -25 -KPX N i -12 -KPX N e -20 -KPX N a -22 - -KPX O z -6 -KPX O y 12 -KPX O w -10 -KPX O v -10 -KPX O u -6 -KPX O t -6 -KPX O s -6 -KPX O r -6 -KPX O quoteright -40 -KPX O quotedblright -40 -KPX O q 5 -KPX O period -20 -KPX O p -6 -KPX O n -6 -KPX O m -6 -KPX O l -20 -KPX O k -10 -KPX O j -6 -KPX O h -10 -KPX O g -6 -KPX O e 5 -KPX O d 6 -KPX O comma -10 -KPX O c 5 -KPX O b -6 -KPX O a 5 -KPX O Y -75 -KPX O X -30 -KPX O W -40 -KPX O V -60 -KPX O T -48 -KPX O A -18 - -KPX P y 6 -KPX P u -18 -KPX P t -6 -KPX P s -24 -KPX P r -6 -KPX P period -220 -KPX P o -24 -KPX P n -12 -KPX P l -25 -KPX P h -15 -KPX P e -24 -KPX P comma -220 -KPX P a -24 -KPX P I -30 -KPX P H -30 -KPX P E -30 -KPX P A -75 - -KPX Q u -6 -KPX Q quoteright -40 -KPX Q quotedblright -50 -KPX Q a -6 -KPX Q Y -70 -KPX Q X -12 -KPX Q W -35 -KPX Q V -60 -KPX Q U -35 -KPX Q T -36 -KPX Q A -18 - -KPX R y -14 -KPX R u -12 -KPX R quoteright -30 -KPX R quotedblright -20 -KPX R o -12 -KPX R hyphen -20 -KPX R e -12 -KPX R Y -50 -KPX R W -30 -KPX R V -40 -KPX R U -40 -KPX R T -30 -KPX R Q -10 -KPX R O -10 -KPX R G -10 -KPX R C -10 -KPX R A -6 - -KPX S y -30 -KPX S w -30 -KPX S v -30 -KPX S u -18 -KPX S t -30 -KPX S r -20 -KPX S quoteright -38 -KPX S quotedblright -30 -KPX S p -18 -KPX S n -24 -KPX S m -24 -KPX S l -30 -KPX S k -24 -KPX S j -25 -KPX S i -30 -KPX S h -30 -KPX S e -6 - -KPX T z -70 -KPX T y -60 -KPX T w -64 -KPX T u -74 -KPX T semicolon -36 -KPX T s -72 -KPX T r -64 -KPX T quoteright 45 -KPX T quotedblright 50 -KPX T period -100 -KPX T parenright 54 -KPX T o -90 -KPX T m -64 -KPX T i -34 -KPX T hyphen -100 -KPX T endash -60 -KPX T emdash -60 -KPX T e -90 -KPX T comma -110 -KPX T colon -10 -KPX T bracketright 45 -KPX T braceright 54 -KPX T a -90 -KPX T Y 12 -KPX T X 18 -KPX T W 6 -KPX T T 18 -KPX T Q -12 -KPX T O -12 -KPX T G -12 -KPX T C -12 -KPX T A -56 - -KPX U z -30 -KPX U x -40 -KPX U t -24 -KPX U s -30 -KPX U r -30 -KPX U quoteright 10 -KPX U quotedblright 10 -KPX U p -40 -KPX U n -45 -KPX U m -45 -KPX U l -12 -KPX U k -12 -KPX U i -24 -KPX U h -6 -KPX U g -30 -KPX U d -40 -KPX U c -35 -KPX U b -6 -KPX U a -40 -KPX U A -45 - -KPX V y -46 -KPX V u -42 -KPX V semicolon -35 -KPX V r -50 -KPX V quoteright 75 -KPX V quotedblright 70 -KPX V period -130 -KPX V parenright 64 -KPX V o -62 -KPX V i -10 -KPX V hyphen -60 -KPX V endash -20 -KPX V emdash -20 -KPX V e -52 -KPX V comma -120 -KPX V colon -18 -KPX V bracketright 64 -KPX V braceright 64 -KPX V a -60 -KPX V T 6 -KPX V A -70 - -KPX W y -42 -KPX W u -56 -KPX W t -20 -KPX W semicolon -28 -KPX W r -40 -KPX W quoteright 55 -KPX W quotedblright 60 -KPX W period -108 -KPX W parenright 64 -KPX W o -60 -KPX W m -35 -KPX W i -10 -KPX W hyphen -40 -KPX W endash -2 -KPX W emdash -10 -KPX W e -54 -KPX W d -50 -KPX W comma -108 -KPX W colon -28 -KPX W bracketright 55 -KPX W braceright 64 -KPX W a -60 -KPX W T 12 -KPX W Q -10 -KPX W O -10 -KPX W G -10 -KPX W C -10 -KPX W A -58 - -KPX X y -35 -KPX X u -30 -KPX X r -6 -KPX X quoteright 35 -KPX X quotedblright 15 -KPX X i -6 -KPX X e -10 -KPX X a 5 -KPX X Y -6 -KPX X W -6 -KPX X Q -30 -KPX X O -30 -KPX X G -30 -KPX X C -30 -KPX X A -18 - -KPX Y v -50 -KPX Y u -58 -KPX Y t -32 -KPX Y semicolon -36 -KPX Y quoteright 65 -KPX Y quotedblright 70 -KPX Y q -100 -KPX Y period -90 -KPX Y parenright 60 -KPX Y o -72 -KPX Y l 10 -KPX Y hyphen -95 -KPX Y endash -20 -KPX Y emdash -20 -KPX Y e -72 -KPX Y d -80 -KPX Y comma -80 -KPX Y colon -36 -KPX Y bracketright 64 -KPX Y braceright 75 -KPX Y a -82 -KPX Y Y 12 -KPX Y X 12 -KPX Y W 12 -KPX Y V 6 -KPX Y T 25 -KPX Y Q -5 -KPX Y O -5 -KPX Y G -5 -KPX Y C -5 -KPX Y A -36 - -KPX Z y -36 -KPX Z w -36 -KPX Z u -12 -KPX Z quoteright 10 -KPX Z quotedblright 10 -KPX Z o -6 -KPX Z i -12 -KPX Z e -6 -KPX Z a -6 -KPX Z Q -30 -KPX Z O -30 -KPX Z G -30 -KPX Z C -30 -KPX Z A 12 - -KPX a quoteright -40 -KPX a quotedblright -40 - -KPX b y -6 -KPX b w -15 -KPX b v -15 -KPX b quoteright -50 -KPX b quotedblright -50 -KPX b period -40 -KPX b comma -30 - -KPX braceleft Y 64 -KPX braceleft W 64 -KPX braceleft V 64 -KPX braceleft T 54 -KPX braceleft J 80 - -KPX bracketleft Y 64 -KPX bracketleft W 64 -KPX bracketleft V 64 -KPX bracketleft T 54 -KPX bracketleft J 80 - -KPX c quoteright -20 -KPX c quotedblright -20 - -KPX colon space -30 - -KPX comma space -40 -KPX comma quoteright -80 -KPX comma quotedblright -80 - -KPX d quoteright -12 -KPX d quotedblright -12 - -KPX e x -10 -KPX e w -10 -KPX e quoteright -30 -KPX e quotedblright -30 - -KPX f quoteright 110 -KPX f quotedblright 110 -KPX f period -20 -KPX f parenright 100 -KPX f comma -20 -KPX f bracketright 90 -KPX f braceright 90 - -KPX g y 30 -KPX g p 12 -KPX g f 42 - -KPX h quoteright -80 -KPX h quotedblright -80 - -KPX j quoteright -20 -KPX j quotedblright -20 -KPX j period -35 -KPX j comma -20 - -KPX k quoteright -30 -KPX k quotedblright -50 - -KPX m quoteright -80 -KPX m quotedblright -80 - -KPX n quoteright -80 -KPX n quotedblright -80 - -KPX o z -10 -KPX o y -20 -KPX o x -20 -KPX o w -30 -KPX o v -35 -KPX o quoteright -60 -KPX o quotedblright -50 -KPX o period -30 -KPX o comma -20 - -KPX p z -10 -KPX p w -15 -KPX p quoteright -50 -KPX p quotedblright -70 -KPX p period -30 -KPX p comma -20 - -KPX parenleft Y 75 -KPX parenleft W 75 -KPX parenleft V 75 -KPX parenleft T 64 -KPX parenleft J 80 - -KPX period space -40 -KPX period quoteright -80 -KPX period quotedblright -80 - -KPX q quoteright -20 -KPX q quotedblright -30 -KPX q period -20 -KPX q comma -10 - -KPX quotedblleft z -30 -KPX quotedblleft x -40 -KPX quotedblleft w -12 -KPX quotedblleft v -12 -KPX quotedblleft u -12 -KPX quotedblleft t -12 -KPX quotedblleft s -30 -KPX quotedblleft r -12 -KPX quotedblleft q -40 -KPX quotedblleft p -12 -KPX quotedblleft o -30 -KPX quotedblleft n -12 -KPX quotedblleft m -12 -KPX quotedblleft l 10 -KPX quotedblleft k 10 -KPX quotedblleft h 10 -KPX quotedblleft g -30 -KPX quotedblleft e -40 -KPX quotedblleft d -40 -KPX quotedblleft c -40 -KPX quotedblleft b 24 -KPX quotedblleft a -60 -KPX quotedblleft Y 12 -KPX quotedblleft X 28 -KPX quotedblleft W 28 -KPX quotedblleft V 28 -KPX quotedblleft T 36 -KPX quotedblleft A -90 - -KPX quotedblright space -40 -KPX quotedblright period -100 -KPX quotedblright comma -100 - -KPX quoteleft z -30 -KPX quoteleft y -10 -KPX quoteleft x -40 -KPX quoteleft w -12 -KPX quoteleft v -12 -KPX quoteleft u -12 -KPX quoteleft t -12 -KPX quoteleft s -30 -KPX quoteleft r -12 -KPX quoteleft quoteleft -18 -KPX quoteleft q -30 -KPX quoteleft p -12 -KPX quoteleft o -30 -KPX quoteleft n -12 -KPX quoteleft m -12 -KPX quoteleft l 10 -KPX quoteleft k 10 -KPX quoteleft h 10 -KPX quoteleft g -30 -KPX quoteleft e -30 -KPX quoteleft d -30 -KPX quoteleft c -30 -KPX quoteleft b 24 -KPX quoteleft a -45 -KPX quoteleft Y 12 -KPX quoteleft X 28 -KPX quoteleft W 28 -KPX quoteleft V 28 -KPX quoteleft T 36 -KPX quoteleft A -90 - -KPX quoteright v -35 -KPX quoteright t -35 -KPX quoteright space -40 -KPX quoteright s -55 -KPX quoteright r -25 -KPX quoteright quoteright -18 -KPX quoteright period -100 -KPX quoteright m -25 -KPX quoteright l -12 -KPX quoteright d -70 -KPX quoteright comma -100 - -KPX r y 18 -KPX r w 6 -KPX r v 6 -KPX r t 8 -KPX r quotedblright -15 -KPX r q -24 -KPX r period -120 -KPX r o -6 -KPX r l -20 -KPX r k -20 -KPX r hyphen -30 -KPX r h -20 -KPX r f 8 -KPX r emdash -20 -KPX r e -26 -KPX r d -26 -KPX r comma -110 -KPX r c -12 -KPX r a -20 - -KPX s quoteright -40 -KPX s quotedblright -45 - -KPX semicolon space -30 - -KPX space quotesinglbase -30 -KPX space quoteleft -40 -KPX space quotedblleft -40 -KPX space quotedblbase -30 -KPX space Y -70 -KPX space W -70 -KPX space V -70 - -KPX t quoteright 10 -KPX t quotedblright -10 - -KPX u quoteright -55 -KPX u quotedblright -50 - -KPX v quoteright -20 -KPX v quotedblright -30 -KPX v q -6 -KPX v period -70 -KPX v o -6 -KPX v e -6 -KPX v d -6 -KPX v comma -70 -KPX v c -6 -KPX v a -6 - -KPX w quoteright -20 -KPX w quotedblright -30 -KPX w period -62 -KPX w comma -62 - -KPX x y 12 -KPX x w -6 -KPX x quoteright -40 -KPX x quotedblright -50 -KPX x q -6 -KPX x o -6 -KPX x e -6 -KPX x d -6 -KPX x c -6 - -KPX y quoteright -10 -KPX y quotedblright -20 -KPX y period -70 -KPX y emdash 40 -KPX y comma -60 - -KPX z quoteright -40 -KPX z quotedblright -50 -KPX z o -6 -KPX z e -6 -KPX z d -6 -KPX z c -6 -EndKernPairs -EndKernData -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pzcmi8a.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pzcmi8a.afm deleted file mode 100644 index 6efb57a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pzcmi8a.afm +++ /dev/null @@ -1,480 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Fri Dec 28 16:35:46 1990 -Comment UniqueID 33936 -Comment VMusage 34559 41451 -FontName ZapfChancery-MediumItalic -FullName ITC Zapf Chancery Medium Italic -FamilyName ITC Zapf Chancery -Weight Medium -ItalicAngle -14 -IsFixedPitch false -FontBBox -181 -314 1065 831 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.007 -Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All Rights Reserved.ITC Zapf Chancery is a registered trademark of International Typeface Corporation. -EncodingScheme AdobeStandardEncoding -CapHeight 708 -XHeight 438 -Ascender 714 -Descender -314 -StartCharMetrics 228 -C 32 ; WX 220 ; N space ; B 0 0 0 0 ; -C 33 ; WX 280 ; N exclam ; B 119 -14 353 610 ; -C 34 ; WX 220 ; N quotedbl ; B 120 343 333 610 ; -C 35 ; WX 440 ; N numbersign ; B 83 0 521 594 ; -C 36 ; WX 440 ; N dollar ; B 60 -144 508 709 ; -C 37 ; WX 680 ; N percent ; B 132 -160 710 700 ; -C 38 ; WX 780 ; N ampersand ; B 126 -16 915 610 ; -C 39 ; WX 240 ; N quoteright ; B 168 343 338 610 ; -C 40 ; WX 260 ; N parenleft ; B 96 -216 411 664 ; -C 41 ; WX 220 ; N parenright ; B -13 -216 302 664 ; -C 42 ; WX 420 ; N asterisk ; B 139 263 479 610 ; -C 43 ; WX 520 ; N plus ; B 117 0 543 426 ; -C 44 ; WX 220 ; N comma ; B 25 -140 213 148 ; -C 45 ; WX 280 ; N hyphen ; B 69 190 334 248 ; -C 46 ; WX 220 ; N period ; B 102 -14 228 128 ; -C 47 ; WX 340 ; N slash ; B 74 -16 458 610 ; -C 48 ; WX 440 ; N zero ; B 79 -16 538 610 ; -C 49 ; WX 440 ; N one ; B 41 0 428 610 ; -C 50 ; WX 440 ; N two ; B 17 -16 485 610 ; -C 51 ; WX 440 ; N three ; B 1 -16 485 610 ; -C 52 ; WX 440 ; N four ; B 77 -35 499 610 ; -C 53 ; WX 440 ; N five ; B 60 -16 595 679 ; -C 54 ; WX 440 ; N six ; B 90 -16 556 610 ; -C 55 ; WX 440 ; N seven ; B 157 -33 561 645 ; -C 56 ; WX 440 ; N eight ; B 65 -16 529 610 ; -C 57 ; WX 440 ; N nine ; B 32 -16 517 610 ; -C 58 ; WX 260 ; N colon ; B 98 -14 296 438 ; -C 59 ; WX 240 ; N semicolon ; B 29 -140 299 438 ; -C 60 ; WX 520 ; N less ; B 139 0 527 468 ; -C 61 ; WX 520 ; N equal ; B 117 86 543 340 ; -C 62 ; WX 520 ; N greater ; B 139 0 527 468 ; -C 63 ; WX 380 ; N question ; B 150 -14 455 610 ; -C 64 ; WX 700 ; N at ; B 127 -16 753 610 ; -C 65 ; WX 620 ; N A ; B 13 -16 697 632 ; -C 66 ; WX 600 ; N B ; B 85 -6 674 640 ; -C 67 ; WX 520 ; N C ; B 93 -16 631 610 ; -C 68 ; WX 700 ; N D ; B 86 -6 768 640 ; -C 69 ; WX 620 ; N E ; B 91 -12 709 618 ; -C 70 ; WX 580 ; N F ; B 120 -118 793 629 ; -C 71 ; WX 620 ; N G ; B 148 -242 709 610 ; -C 72 ; WX 680 ; N H ; B 18 -16 878 708 ; -C 73 ; WX 380 ; N I ; B 99 0 504 594 ; -C 74 ; WX 400 ; N J ; B -14 -147 538 594 ; -C 75 ; WX 660 ; N K ; B 53 -153 844 610 ; -C 76 ; WX 580 ; N L ; B 53 -16 657 610 ; -C 77 ; WX 840 ; N M ; B 58 -16 1020 722 ; -C 78 ; WX 700 ; N N ; B 85 -168 915 708 ; -C 79 ; WX 600 ; N O ; B 94 -16 660 610 ; -C 80 ; WX 540 ; N P ; B 42 0 658 628 ; -C 81 ; WX 600 ; N Q ; B 84 -177 775 610 ; -C 82 ; WX 600 ; N R ; B 58 -168 805 640 ; -C 83 ; WX 460 ; N S ; B 45 -81 558 610 ; -C 84 ; WX 500 ; N T ; B 63 0 744 667 ; -C 85 ; WX 740 ; N U ; B 126 -16 792 617 ; -C 86 ; WX 640 ; N V ; B 124 -16 810 714 ; -C 87 ; WX 880 ; N W ; B 94 -16 1046 723 ; -C 88 ; WX 560 ; N X ; B -30 -16 699 610 ; -C 89 ; WX 560 ; N Y ; B 41 -168 774 647 ; -C 90 ; WX 620 ; N Z ; B 42 -19 669 624 ; -C 91 ; WX 240 ; N bracketleft ; B -13 -207 405 655 ; -C 92 ; WX 480 ; N backslash ; B 140 -16 524 610 ; -C 93 ; WX 320 ; N bracketright ; B -27 -207 391 655 ; -C 94 ; WX 520 ; N asciicircum ; B 132 239 532 594 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 240 ; N quoteleft ; B 169 343 339 610 ; -C 97 ; WX 420 ; N a ; B 92 -15 485 438 ; -C 98 ; WX 420 ; N b ; B 82 -23 492 714 ; -C 99 ; WX 340 ; N c ; B 87 -14 406 438 ; -C 100 ; WX 440 ; N d ; B 102 -14 651 714 ; -C 101 ; WX 340 ; N e ; B 87 -14 403 438 ; -C 102 ; WX 320 ; N f ; B -119 -314 547 714 ; L i fi ; L l fl ; -C 103 ; WX 400 ; N g ; B -108 -314 503 438 ; -C 104 ; WX 440 ; N h ; B 55 -14 524 714 ; -C 105 ; WX 240 ; N i ; B 100 -14 341 635 ; -C 106 ; WX 220 ; N j ; B -112 -314 332 635 ; -C 107 ; WX 440 ; N k ; B 87 -184 628 714 ; -C 108 ; WX 240 ; N l ; B 102 -14 480 714 ; -C 109 ; WX 620 ; N m ; B 86 -14 704 438 ; -C 110 ; WX 460 ; N n ; B 101 -14 544 438 ; -C 111 ; WX 400 ; N o ; B 87 -14 449 438 ; -C 112 ; WX 440 ; N p ; B -23 -314 484 432 ; -C 113 ; WX 400 ; N q ; B 87 -300 490 510 ; -C 114 ; WX 300 ; N r ; B 101 -14 424 438 ; -C 115 ; WX 320 ; N s ; B 46 -14 403 438 ; -C 116 ; WX 320 ; N t ; B 106 -14 426 539 ; -C 117 ; WX 460 ; N u ; B 102 -14 528 438 ; -C 118 ; WX 440 ; N v ; B 87 -14 533 488 ; -C 119 ; WX 680 ; N w ; B 87 -14 782 488 ; -C 120 ; WX 420 ; N x ; B 70 -195 589 438 ; -C 121 ; WX 400 ; N y ; B -24 -314 483 438 ; -C 122 ; WX 440 ; N z ; B 26 -14 508 445 ; -C 123 ; WX 240 ; N braceleft ; B 55 -207 383 655 ; -C 124 ; WX 520 ; N bar ; B 320 -16 378 714 ; -C 125 ; WX 240 ; N braceright ; B -10 -207 318 655 ; -C 126 ; WX 520 ; N asciitilde ; B 123 186 539 320 ; -C 161 ; WX 280 ; N exclamdown ; B 72 -186 306 438 ; -C 162 ; WX 440 ; N cent ; B 122 -134 476 543 ; -C 163 ; WX 440 ; N sterling ; B -16 -52 506 610 ; -C 164 ; WX 60 ; N fraction ; B -181 -16 320 610 ; -C 165 ; WX 440 ; N yen ; B -1 -168 613 647 ; -C 166 ; WX 440 ; N florin ; B -64 -314 582 610 ; -C 167 ; WX 420 ; N section ; B 53 -215 514 610 ; -C 168 ; WX 440 ; N currency ; B 50 85 474 509 ; -C 169 ; WX 160 ; N quotesingle ; B 145 343 215 610 ; -C 170 ; WX 340 ; N quotedblleft ; B 169 343 464 610 ; -C 171 ; WX 340 ; N guillemotleft ; B 98 24 356 414 ; -C 172 ; WX 240 ; N guilsinglleft ; B 98 24 258 414 ; -C 173 ; WX 260 ; N guilsinglright ; B 106 24 266 414 ; -C 174 ; WX 520 ; N fi ; B -124 -314 605 714 ; -C 175 ; WX 520 ; N fl ; B -124 -314 670 714 ; -C 177 ; WX 500 ; N endash ; B 51 199 565 239 ; -C 178 ; WX 460 ; N dagger ; B 138 -37 568 610 ; -C 179 ; WX 480 ; N daggerdbl ; B 138 -59 533 610 ; -C 180 ; WX 220 ; N periodcentered ; B 139 208 241 310 ; -C 182 ; WX 500 ; N paragraph ; B 105 -199 638 594 ; -C 183 ; WX 600 ; N bullet ; B 228 149 524 445 ; -C 184 ; WX 180 ; N quotesinglbase ; B 21 -121 191 146 ; -C 185 ; WX 280 ; N quotedblbase ; B -14 -121 281 146 ; -C 186 ; WX 360 ; N quotedblright ; B 158 343 453 610 ; -C 187 ; WX 380 ; N guillemotright ; B 117 24 375 414 ; -C 188 ; WX 1000 ; N ellipsis ; B 124 -14 916 128 ; -C 189 ; WX 960 ; N perthousand ; B 112 -160 1005 700 ; -C 191 ; WX 400 ; N questiondown ; B 82 -186 387 438 ; -C 193 ; WX 220 ; N grave ; B 193 492 339 659 ; -C 194 ; WX 300 ; N acute ; B 265 492 422 659 ; -C 195 ; WX 340 ; N circumflex ; B 223 482 443 649 ; -C 196 ; WX 440 ; N tilde ; B 243 543 522 619 ; -C 197 ; WX 440 ; N macron ; B 222 544 465 578 ; -C 198 ; WX 440 ; N breve ; B 253 522 501 631 ; -C 199 ; WX 220 ; N dotaccent ; B 236 522 328 610 ; -C 200 ; WX 360 ; N dieresis ; B 243 522 469 610 ; -C 202 ; WX 300 ; N ring ; B 240 483 416 659 ; -C 203 ; WX 300 ; N cedilla ; B 12 -191 184 6 ; -C 205 ; WX 400 ; N hungarumlaut ; B 208 492 495 659 ; -C 206 ; WX 280 ; N ogonek ; B 38 -191 233 6 ; -C 207 ; WX 340 ; N caron ; B 254 492 474 659 ; -C 208 ; WX 1000 ; N emdash ; B 51 199 1065 239 ; -C 225 ; WX 740 ; N AE ; B -21 -16 799 594 ; -C 227 ; WX 260 ; N ordfeminine ; B 111 338 386 610 ; -C 232 ; WX 580 ; N Lslash ; B 49 -16 657 610 ; -C 233 ; WX 660 ; N Oslash ; B 83 -78 751 672 ; -C 234 ; WX 820 ; N OE ; B 63 -16 909 610 ; -C 235 ; WX 260 ; N ordmasculine ; B 128 339 373 610 ; -C 241 ; WX 540 ; N ae ; B 67 -14 624 468 ; -C 245 ; WX 240 ; N dotlessi ; B 100 -14 306 438 ; -C 248 ; WX 300 ; N lslash ; B 121 -14 515 714 ; -C 249 ; WX 440 ; N oslash ; B 46 -64 540 488 ; -C 250 ; WX 560 ; N oe ; B 78 -14 628 438 ; -C 251 ; WX 420 ; N germandbls ; B -127 -314 542 714 ; -C -1 ; WX 340 ; N ecircumflex ; B 87 -14 433 649 ; -C -1 ; WX 340 ; N edieresis ; B 87 -14 449 610 ; -C -1 ; WX 420 ; N aacute ; B 92 -15 492 659 ; -C -1 ; WX 740 ; N registered ; B 137 -16 763 610 ; -C -1 ; WX 240 ; N icircumflex ; B 100 -14 363 649 ; -C -1 ; WX 460 ; N udieresis ; B 102 -14 528 610 ; -C -1 ; WX 400 ; N ograve ; B 87 -14 449 659 ; -C -1 ; WX 460 ; N uacute ; B 102 -14 528 659 ; -C -1 ; WX 460 ; N ucircumflex ; B 102 -14 528 649 ; -C -1 ; WX 620 ; N Aacute ; B 13 -16 702 821 ; -C -1 ; WX 240 ; N igrave ; B 100 -14 306 659 ; -C -1 ; WX 380 ; N Icircumflex ; B 99 0 504 821 ; -C -1 ; WX 340 ; N ccedilla ; B 62 -191 406 438 ; -C -1 ; WX 420 ; N adieresis ; B 92 -15 485 610 ; -C -1 ; WX 620 ; N Ecircumflex ; B 91 -12 709 821 ; -C -1 ; WX 320 ; N scaron ; B 46 -14 464 659 ; -C -1 ; WX 440 ; N thorn ; B -38 -314 505 714 ; -C -1 ; WX 1000 ; N trademark ; B 127 187 1046 594 ; -C -1 ; WX 340 ; N egrave ; B 87 -14 403 659 ; -C -1 ; WX 264 ; N threesuperior ; B 59 234 348 610 ; -C -1 ; WX 440 ; N zcaron ; B 26 -14 514 659 ; -C -1 ; WX 420 ; N atilde ; B 92 -15 522 619 ; -C -1 ; WX 420 ; N aring ; B 92 -15 485 659 ; -C -1 ; WX 400 ; N ocircumflex ; B 87 -14 453 649 ; -C -1 ; WX 620 ; N Edieresis ; B 91 -12 709 762 ; -C -1 ; WX 660 ; N threequarters ; B 39 -16 706 610 ; -C -1 ; WX 400 ; N ydieresis ; B -24 -314 483 610 ; -C -1 ; WX 400 ; N yacute ; B -24 -314 483 659 ; -C -1 ; WX 240 ; N iacute ; B 100 -14 392 659 ; -C -1 ; WX 620 ; N Acircumflex ; B 13 -16 697 821 ; -C -1 ; WX 740 ; N Uacute ; B 126 -16 792 821 ; -C -1 ; WX 340 ; N eacute ; B 87 -14 462 659 ; -C -1 ; WX 600 ; N Ograve ; B 94 -16 660 821 ; -C -1 ; WX 420 ; N agrave ; B 92 -15 485 659 ; -C -1 ; WX 740 ; N Udieresis ; B 126 -16 792 762 ; -C -1 ; WX 420 ; N acircumflex ; B 92 -15 485 649 ; -C -1 ; WX 380 ; N Igrave ; B 99 0 504 821 ; -C -1 ; WX 264 ; N twosuperior ; B 72 234 354 610 ; -C -1 ; WX 740 ; N Ugrave ; B 126 -16 792 821 ; -C -1 ; WX 660 ; N onequarter ; B 56 -16 702 610 ; -C -1 ; WX 740 ; N Ucircumflex ; B 126 -16 792 821 ; -C -1 ; WX 460 ; N Scaron ; B 45 -81 594 831 ; -C -1 ; WX 380 ; N Idieresis ; B 99 0 519 762 ; -C -1 ; WX 240 ; N idieresis ; B 100 -14 369 610 ; -C -1 ; WX 620 ; N Egrave ; B 91 -12 709 821 ; -C -1 ; WX 600 ; N Oacute ; B 94 -16 660 821 ; -C -1 ; WX 520 ; N divide ; B 117 -14 543 440 ; -C -1 ; WX 620 ; N Atilde ; B 13 -16 702 771 ; -C -1 ; WX 620 ; N Aring ; B 13 -16 697 831 ; -C -1 ; WX 600 ; N Odieresis ; B 94 -16 660 762 ; -C -1 ; WX 620 ; N Adieresis ; B 13 -16 709 762 ; -C -1 ; WX 700 ; N Ntilde ; B 85 -168 915 761 ; -C -1 ; WX 620 ; N Zcaron ; B 42 -19 669 831 ; -C -1 ; WX 540 ; N Thorn ; B 52 0 647 623 ; -C -1 ; WX 380 ; N Iacute ; B 99 0 532 821 ; -C -1 ; WX 520 ; N plusminus ; B 117 0 543 436 ; -C -1 ; WX 520 ; N multiply ; B 133 16 527 410 ; -C -1 ; WX 620 ; N Eacute ; B 91 -12 709 821 ; -C -1 ; WX 560 ; N Ydieresis ; B 41 -168 774 762 ; -C -1 ; WX 264 ; N onesuperior ; B 83 244 311 610 ; -C -1 ; WX 460 ; N ugrave ; B 102 -14 528 659 ; -C -1 ; WX 520 ; N logicalnot ; B 117 86 543 340 ; -C -1 ; WX 460 ; N ntilde ; B 101 -14 544 619 ; -C -1 ; WX 600 ; N Otilde ; B 94 -16 660 761 ; -C -1 ; WX 400 ; N otilde ; B 87 -14 502 619 ; -C -1 ; WX 520 ; N Ccedilla ; B 93 -191 631 610 ; -C -1 ; WX 620 ; N Agrave ; B 13 -16 697 821 ; -C -1 ; WX 660 ; N onehalf ; B 56 -16 702 610 ; -C -1 ; WX 700 ; N Eth ; B 86 -6 768 640 ; -C -1 ; WX 400 ; N degree ; B 171 324 457 610 ; -C -1 ; WX 560 ; N Yacute ; B 41 -168 774 821 ; -C -1 ; WX 600 ; N Ocircumflex ; B 94 -16 660 821 ; -C -1 ; WX 400 ; N oacute ; B 87 -14 482 659 ; -C -1 ; WX 460 ; N mu ; B 7 -314 523 438 ; -C -1 ; WX 520 ; N minus ; B 117 184 543 242 ; -C -1 ; WX 400 ; N eth ; B 87 -14 522 714 ; -C -1 ; WX 400 ; N odieresis ; B 87 -14 479 610 ; -C -1 ; WX 740 ; N copyright ; B 137 -16 763 610 ; -C -1 ; WX 520 ; N brokenbar ; B 320 -16 378 714 ; -EndCharMetrics -StartKernData -StartKernPairs 131 - -KPX A quoteright -40 -KPX A quotedblright -40 -KPX A U -10 -KPX A T 10 -KPX A Q 10 -KPX A O 10 -KPX A G -30 -KPX A C 20 - -KPX D period -30 -KPX D comma -20 -KPX D Y 10 -KPX D A -10 - -KPX F period -40 -KPX F i 10 -KPX F comma -30 - -KPX G period -20 -KPX G comma -10 - -KPX J period -20 -KPX J comma -10 - -KPX K u -20 -KPX K o -20 -KPX K e -20 - -KPX L y -10 -KPX L quoteright -25 -KPX L quotedblright -25 -KPX L W -10 -KPX L V -20 - -KPX O period -20 -KPX O comma -10 -KPX O Y 10 -KPX O T 20 -KPX O A -20 - -KPX P period -50 -KPX P o -10 -KPX P e -10 -KPX P comma -40 -KPX P a -20 -KPX P A -10 - -KPX Q U -10 - -KPX R Y 10 -KPX R W 10 -KPX R T 20 - -KPX T o -20 -KPX T i 20 -KPX T hyphen -20 -KPX T h 20 -KPX T e -20 -KPX T a -20 -KPX T O 30 -KPX T A 10 - -KPX V period -100 -KPX V o -20 -KPX V e -20 -KPX V comma -90 -KPX V a -20 -KPX V O 10 -KPX V G -20 - -KPX W period -50 -KPX W o -20 -KPX W i 10 -KPX W h 10 -KPX W e -20 -KPX W comma -40 -KPX W a -20 -KPX W O 10 - -KPX Y u -20 -KPX Y period -50 -KPX Y o -50 -KPX Y i 10 -KPX Y e -40 -KPX Y comma -40 -KPX Y a -60 - -KPX b period -30 -KPX b l -20 -KPX b comma -20 -KPX b b -20 - -KPX c k -10 - -KPX comma quoteright -70 -KPX comma quotedblright -70 - -KPX d w -20 -KPX d v -10 -KPX d d -40 - -KPX e y 10 - -KPX f quoteright 30 -KPX f quotedblright 30 -KPX f period -50 -KPX f f -50 -KPX f e -10 -KPX f comma -40 -KPX f a -20 - -KPX g y 10 -KPX g period -30 -KPX g i 10 -KPX g e 10 -KPX g comma -20 -KPX g a 10 - -KPX k y 10 -KPX k o -10 -KPX k e -20 - -KPX m y 10 -KPX m u 10 - -KPX n y 20 - -KPX o period -30 -KPX o comma -20 - -KPX p period -30 -KPX p p -10 -KPX p comma -20 - -KPX period quoteright -80 -KPX period quotedblright -80 - -KPX quotedblleft quoteleft 20 -KPX quotedblleft A 10 - -KPX quoteleft quoteleft -115 -KPX quoteleft A 10 - -KPX quoteright v 30 -KPX quoteright t 20 -KPX quoteright s -25 -KPX quoteright r 30 -KPX quoteright quoteright -115 -KPX quoteright quotedblright 20 -KPX quoteright l 20 - -KPX r period -50 -KPX r i 10 -KPX r comma -40 - -KPX s period -20 -KPX s comma -10 - -KPX v period -30 -KPX v comma -20 - -KPX w period -30 -KPX w o 10 -KPX w h 20 -KPX w comma -20 -EndKernPairs -EndKernData -StartComposites 56 -CC Aacute 2 ; PCC A 0 0 ; PCC acute 280 162 ; -CC Acircumflex 2 ; PCC A 0 0 ; PCC circumflex 240 172 ; -CC Adieresis 2 ; PCC A 0 0 ; PCC dieresis 240 152 ; -CC Agrave 2 ; PCC A 0 0 ; PCC grave 250 162 ; -CC Aring 2 ; PCC A 0 0 ; PCC ring 260 172 ; -CC Atilde 2 ; PCC A 0 0 ; PCC tilde 180 152 ; -CC Eacute 2 ; PCC E 0 0 ; PCC acute 230 162 ; -CC Ecircumflex 2 ; PCC E 0 0 ; PCC circumflex 180 172 ; -CC Edieresis 2 ; PCC E 0 0 ; PCC dieresis 170 152 ; -CC Egrave 2 ; PCC E 0 0 ; PCC grave 220 162 ; -CC Iacute 2 ; PCC I 0 0 ; PCC acute 110 162 ; -CC Icircumflex 2 ; PCC I 0 0 ; PCC circumflex 60 172 ; -CC Idieresis 2 ; PCC I 0 0 ; PCC dieresis 50 152 ; -CC Igrave 2 ; PCC I 0 0 ; PCC grave 100 162 ; -CC Ntilde 2 ; PCC N 0 0 ; PCC tilde 210 142 ; -CC Oacute 2 ; PCC O 0 0 ; PCC acute 160 162 ; -CC Ocircumflex 2 ; PCC O 0 0 ; PCC circumflex 130 172 ; -CC Odieresis 2 ; PCC O 0 0 ; PCC dieresis 120 152 ; -CC Ograve 2 ; PCC O 0 0 ; PCC grave 150 162 ; -CC Otilde 2 ; PCC O 0 0 ; PCC tilde 90 142 ; -CC Scaron 2 ; PCC S 0 0 ; PCC caron 120 172 ; -CC Uacute 2 ; PCC U 0 0 ; PCC acute 310 162 ; -CC Ucircumflex 2 ; PCC U 0 0 ; PCC circumflex 260 172 ; -CC Udieresis 2 ; PCC U 0 0 ; PCC dieresis 260 152 ; -CC Ugrave 2 ; PCC U 0 0 ; PCC grave 270 162 ; -CC Yacute 2 ; PCC Y 0 0 ; PCC acute 220 162 ; -CC Ydieresis 2 ; PCC Y 0 0 ; PCC dieresis 170 152 ; -CC Zcaron 2 ; PCC Z 0 0 ; PCC caron 130 172 ; -CC aacute 2 ; PCC a 0 0 ; PCC acute 70 0 ; -CC acircumflex 2 ; PCC a 0 0 ; PCC circumflex 20 0 ; -CC adieresis 2 ; PCC a 0 0 ; PCC dieresis 10 0 ; -CC agrave 2 ; PCC a 0 0 ; PCC grave 80 0 ; -CC aring 2 ; PCC a 0 0 ; PCC ring 60 0 ; -CC atilde 2 ; PCC a 0 0 ; PCC tilde 0 0 ; -CC eacute 2 ; PCC e 0 0 ; PCC acute 40 0 ; -CC ecircumflex 2 ; PCC e 0 0 ; PCC circumflex -10 0 ; -CC edieresis 2 ; PCC e 0 0 ; PCC dieresis -20 0 ; -CC egrave 2 ; PCC e 0 0 ; PCC grave 30 0 ; -CC iacute 2 ; PCC dotlessi 0 0 ; PCC acute -30 0 ; -CC icircumflex 2 ; PCC dotlessi 0 0 ; PCC circumflex -80 0 ; -CC idieresis 2 ; PCC dotlessi 0 0 ; PCC dieresis -100 0 ; -CC igrave 2 ; PCC dotlessi 0 0 ; PCC grave -40 0 ; -CC ntilde 2 ; PCC n 0 0 ; PCC tilde 10 0 ; -CC oacute 2 ; PCC o 0 0 ; PCC acute 60 0 ; -CC ocircumflex 2 ; PCC o 0 0 ; PCC circumflex 10 0 ; -CC odieresis 2 ; PCC o 0 0 ; PCC dieresis 10 0 ; -CC ograve 2 ; PCC o 0 0 ; PCC grave 60 0 ; -CC otilde 2 ; PCC o 0 0 ; PCC tilde -20 0 ; -CC scaron 2 ; PCC s 0 0 ; PCC caron -10 0 ; -CC uacute 2 ; PCC u 0 0 ; PCC acute 70 0 ; -CC ucircumflex 2 ; PCC u 0 0 ; PCC circumflex 30 0 ; -CC udieresis 2 ; PCC u 0 0 ; PCC dieresis 20 0 ; -CC ugrave 2 ; PCC u 0 0 ; PCC grave 50 0 ; -CC yacute 2 ; PCC y 0 0 ; PCC acute 60 0 ; -CC ydieresis 2 ; PCC y 0 0 ; PCC dieresis 0 0 ; -CC zcaron 2 ; PCC z 0 0 ; PCC caron 40 0 ; -EndComposites -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pzdr.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pzdr.afm deleted file mode 100644 index 6b98e8d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/afm/pzdr.afm +++ /dev/null @@ -1,222 +0,0 @@ -StartFontMetrics 2.0 -Comment Copyright (c) 1985, 1987, 1988, 1989 Adobe Systems Incorporated. All rights reserved. -Comment Creation Date: Fri Dec 1 12:57:42 1989 -Comment UniqueID 26200 -Comment VMusage 39281 49041 -FontName ZapfDingbats -FullName ITC Zapf Dingbats -FamilyName ITC Zapf Dingbats -Weight Medium -ItalicAngle 0 -IsFixedPitch false -FontBBox -1 -143 981 820 -UnderlinePosition -98 -UnderlineThickness 54 -Version 001.004 -Notice Copyright (c) 1985, 1987, 1988, 1989 Adobe Systems Incorporated. All rights reserved.ITC Zapf Dingbats is a registered trademark of International Typeface Corporation. -EncodingScheme FontSpecific -StartCharMetrics 202 -C 32 ; WX 278 ; N space ; B 0 0 0 0 ; -C 33 ; WX 974 ; N a1 ; B 35 72 939 621 ; -C 34 ; WX 961 ; N a2 ; B 35 81 927 611 ; -C 35 ; WX 974 ; N a202 ; B 35 72 939 621 ; -C 36 ; WX 980 ; N a3 ; B 35 0 945 692 ; -C 37 ; WX 719 ; N a4 ; B 34 139 685 566 ; -C 38 ; WX 789 ; N a5 ; B 35 -14 755 705 ; -C 39 ; WX 790 ; N a119 ; B 35 -14 755 705 ; -C 40 ; WX 791 ; N a118 ; B 35 -13 761 705 ; -C 41 ; WX 690 ; N a117 ; B 35 138 655 553 ; -C 42 ; WX 960 ; N a11 ; B 35 123 925 568 ; -C 43 ; WX 939 ; N a12 ; B 35 134 904 559 ; -C 44 ; WX 549 ; N a13 ; B 29 -11 516 705 ; -C 45 ; WX 855 ; N a14 ; B 34 59 820 632 ; -C 46 ; WX 911 ; N a15 ; B 35 50 876 642 ; -C 47 ; WX 933 ; N a16 ; B 35 139 899 550 ; -C 48 ; WX 911 ; N a105 ; B 35 50 876 642 ; -C 49 ; WX 945 ; N a17 ; B 35 139 909 553 ; -C 50 ; WX 974 ; N a18 ; B 35 104 938 587 ; -C 51 ; WX 755 ; N a19 ; B 34 -13 721 705 ; -C 52 ; WX 846 ; N a20 ; B 36 -14 811 705 ; -C 53 ; WX 762 ; N a21 ; B 35 0 727 692 ; -C 54 ; WX 761 ; N a22 ; B 35 0 727 692 ; -C 55 ; WX 571 ; N a23 ; B -1 -68 571 661 ; -C 56 ; WX 677 ; N a24 ; B 36 -13 642 705 ; -C 57 ; WX 763 ; N a25 ; B 35 0 728 692 ; -C 58 ; WX 760 ; N a26 ; B 35 0 726 692 ; -C 59 ; WX 759 ; N a27 ; B 35 0 725 692 ; -C 60 ; WX 754 ; N a28 ; B 35 0 720 692 ; -C 61 ; WX 494 ; N a6 ; B 35 0 460 692 ; -C 62 ; WX 552 ; N a7 ; B 35 0 517 692 ; -C 63 ; WX 537 ; N a8 ; B 35 0 503 692 ; -C 64 ; WX 577 ; N a9 ; B 35 96 542 596 ; -C 65 ; WX 692 ; N a10 ; B 35 -14 657 705 ; -C 66 ; WX 786 ; N a29 ; B 35 -14 751 705 ; -C 67 ; WX 788 ; N a30 ; B 35 -14 752 705 ; -C 68 ; WX 788 ; N a31 ; B 35 -14 753 705 ; -C 69 ; WX 790 ; N a32 ; B 35 -14 756 705 ; -C 70 ; WX 793 ; N a33 ; B 35 -13 759 705 ; -C 71 ; WX 794 ; N a34 ; B 35 -13 759 705 ; -C 72 ; WX 816 ; N a35 ; B 35 -14 782 705 ; -C 73 ; WX 823 ; N a36 ; B 35 -14 787 705 ; -C 74 ; WX 789 ; N a37 ; B 35 -14 754 705 ; -C 75 ; WX 841 ; N a38 ; B 35 -14 807 705 ; -C 76 ; WX 823 ; N a39 ; B 35 -14 789 705 ; -C 77 ; WX 833 ; N a40 ; B 35 -14 798 705 ; -C 78 ; WX 816 ; N a41 ; B 35 -13 782 705 ; -C 79 ; WX 831 ; N a42 ; B 35 -14 796 705 ; -C 80 ; WX 923 ; N a43 ; B 35 -14 888 705 ; -C 81 ; WX 744 ; N a44 ; B 35 0 710 692 ; -C 82 ; WX 723 ; N a45 ; B 35 0 688 692 ; -C 83 ; WX 749 ; N a46 ; B 35 0 714 692 ; -C 84 ; WX 790 ; N a47 ; B 34 -14 756 705 ; -C 85 ; WX 792 ; N a48 ; B 35 -14 758 705 ; -C 86 ; WX 695 ; N a49 ; B 35 -14 661 706 ; -C 87 ; WX 776 ; N a50 ; B 35 -6 741 699 ; -C 88 ; WX 768 ; N a51 ; B 35 -7 734 699 ; -C 89 ; WX 792 ; N a52 ; B 35 -14 757 705 ; -C 90 ; WX 759 ; N a53 ; B 35 0 725 692 ; -C 91 ; WX 707 ; N a54 ; B 35 -13 672 704 ; -C 92 ; WX 708 ; N a55 ; B 35 -14 672 705 ; -C 93 ; WX 682 ; N a56 ; B 35 -14 647 705 ; -C 94 ; WX 701 ; N a57 ; B 35 -14 666 705 ; -C 95 ; WX 826 ; N a58 ; B 35 -14 791 705 ; -C 96 ; WX 815 ; N a59 ; B 35 -14 780 705 ; -C 97 ; WX 789 ; N a60 ; B 35 -14 754 705 ; -C 98 ; WX 789 ; N a61 ; B 35 -14 754 705 ; -C 99 ; WX 707 ; N a62 ; B 34 -14 673 705 ; -C 100 ; WX 687 ; N a63 ; B 36 0 651 692 ; -C 101 ; WX 696 ; N a64 ; B 35 0 661 691 ; -C 102 ; WX 689 ; N a65 ; B 35 0 655 692 ; -C 103 ; WX 786 ; N a66 ; B 34 -14 751 705 ; -C 104 ; WX 787 ; N a67 ; B 35 -14 752 705 ; -C 105 ; WX 713 ; N a68 ; B 35 -14 678 705 ; -C 106 ; WX 791 ; N a69 ; B 35 -14 756 705 ; -C 107 ; WX 785 ; N a70 ; B 36 -14 751 705 ; -C 108 ; WX 791 ; N a71 ; B 35 -14 757 705 ; -C 109 ; WX 873 ; N a72 ; B 35 -14 838 705 ; -C 110 ; WX 761 ; N a73 ; B 35 0 726 692 ; -C 111 ; WX 762 ; N a74 ; B 35 0 727 692 ; -C 112 ; WX 762 ; N a203 ; B 35 0 727 692 ; -C 113 ; WX 759 ; N a75 ; B 35 0 725 692 ; -C 114 ; WX 759 ; N a204 ; B 35 0 725 692 ; -C 115 ; WX 892 ; N a76 ; B 35 0 858 705 ; -C 116 ; WX 892 ; N a77 ; B 35 -14 858 692 ; -C 117 ; WX 788 ; N a78 ; B 35 -14 754 705 ; -C 118 ; WX 784 ; N a79 ; B 35 -14 749 705 ; -C 119 ; WX 438 ; N a81 ; B 35 -14 403 705 ; -C 120 ; WX 138 ; N a82 ; B 35 0 104 692 ; -C 121 ; WX 277 ; N a83 ; B 35 0 242 692 ; -C 122 ; WX 415 ; N a84 ; B 35 0 380 692 ; -C 123 ; WX 392 ; N a97 ; B 35 263 357 705 ; -C 124 ; WX 392 ; N a98 ; B 34 263 357 705 ; -C 125 ; WX 668 ; N a99 ; B 35 263 633 705 ; -C 126 ; WX 668 ; N a100 ; B 36 263 634 705 ; -C 161 ; WX 732 ; N a101 ; B 35 -143 697 806 ; -C 162 ; WX 544 ; N a102 ; B 56 -14 488 706 ; -C 163 ; WX 544 ; N a103 ; B 34 -14 508 705 ; -C 164 ; WX 910 ; N a104 ; B 35 40 875 651 ; -C 165 ; WX 667 ; N a106 ; B 35 -14 633 705 ; -C 166 ; WX 760 ; N a107 ; B 35 -14 726 705 ; -C 167 ; WX 760 ; N a108 ; B 0 121 758 569 ; -C 168 ; WX 776 ; N a112 ; B 35 0 741 705 ; -C 169 ; WX 595 ; N a111 ; B 34 -14 560 705 ; -C 170 ; WX 694 ; N a110 ; B 35 -14 659 705 ; -C 171 ; WX 626 ; N a109 ; B 34 0 591 705 ; -C 172 ; WX 788 ; N a120 ; B 35 -14 754 705 ; -C 173 ; WX 788 ; N a121 ; B 35 -14 754 705 ; -C 174 ; WX 788 ; N a122 ; B 35 -14 754 705 ; -C 175 ; WX 788 ; N a123 ; B 35 -14 754 705 ; -C 176 ; WX 788 ; N a124 ; B 35 -14 754 705 ; -C 177 ; WX 788 ; N a125 ; B 35 -14 754 705 ; -C 178 ; WX 788 ; N a126 ; B 35 -14 754 705 ; -C 179 ; WX 788 ; N a127 ; B 35 -14 754 705 ; -C 180 ; WX 788 ; N a128 ; B 35 -14 754 705 ; -C 181 ; WX 788 ; N a129 ; B 35 -14 754 705 ; -C 182 ; WX 788 ; N a130 ; B 35 -14 754 705 ; -C 183 ; WX 788 ; N a131 ; B 35 -14 754 705 ; -C 184 ; WX 788 ; N a132 ; B 35 -14 754 705 ; -C 185 ; WX 788 ; N a133 ; B 35 -14 754 705 ; -C 186 ; WX 788 ; N a134 ; B 35 -14 754 705 ; -C 187 ; WX 788 ; N a135 ; B 35 -14 754 705 ; -C 188 ; WX 788 ; N a136 ; B 35 -14 754 705 ; -C 189 ; WX 788 ; N a137 ; B 35 -14 754 705 ; -C 190 ; WX 788 ; N a138 ; B 35 -14 754 705 ; -C 191 ; WX 788 ; N a139 ; B 35 -14 754 705 ; -C 192 ; WX 788 ; N a140 ; B 35 -14 754 705 ; -C 193 ; WX 788 ; N a141 ; B 35 -14 754 705 ; -C 194 ; WX 788 ; N a142 ; B 35 -14 754 705 ; -C 195 ; WX 788 ; N a143 ; B 35 -14 754 705 ; -C 196 ; WX 788 ; N a144 ; B 35 -14 754 705 ; -C 197 ; WX 788 ; N a145 ; B 35 -14 754 705 ; -C 198 ; WX 788 ; N a146 ; B 35 -14 754 705 ; -C 199 ; WX 788 ; N a147 ; B 35 -14 754 705 ; -C 200 ; WX 788 ; N a148 ; B 35 -14 754 705 ; -C 201 ; WX 788 ; N a149 ; B 35 -14 754 705 ; -C 202 ; WX 788 ; N a150 ; B 35 -14 754 705 ; -C 203 ; WX 788 ; N a151 ; B 35 -14 754 705 ; -C 204 ; WX 788 ; N a152 ; B 35 -14 754 705 ; -C 205 ; WX 788 ; N a153 ; B 35 -14 754 705 ; -C 206 ; WX 788 ; N a154 ; B 35 -14 754 705 ; -C 207 ; WX 788 ; N a155 ; B 35 -14 754 705 ; -C 208 ; WX 788 ; N a156 ; B 35 -14 754 705 ; -C 209 ; WX 788 ; N a157 ; B 35 -14 754 705 ; -C 210 ; WX 788 ; N a158 ; B 35 -14 754 705 ; -C 211 ; WX 788 ; N a159 ; B 35 -14 754 705 ; -C 212 ; WX 894 ; N a160 ; B 35 58 860 634 ; -C 213 ; WX 838 ; N a161 ; B 35 152 803 540 ; -C 214 ; WX 1016 ; N a163 ; B 34 152 981 540 ; -C 215 ; WX 458 ; N a164 ; B 35 -127 422 820 ; -C 216 ; WX 748 ; N a196 ; B 35 94 698 597 ; -C 217 ; WX 924 ; N a165 ; B 35 140 890 552 ; -C 218 ; WX 748 ; N a192 ; B 35 94 698 597 ; -C 219 ; WX 918 ; N a166 ; B 35 166 884 526 ; -C 220 ; WX 927 ; N a167 ; B 35 32 892 660 ; -C 221 ; WX 928 ; N a168 ; B 35 129 891 562 ; -C 222 ; WX 928 ; N a169 ; B 35 128 893 563 ; -C 223 ; WX 834 ; N a170 ; B 35 155 799 537 ; -C 224 ; WX 873 ; N a171 ; B 35 93 838 599 ; -C 225 ; WX 828 ; N a172 ; B 35 104 791 588 ; -C 226 ; WX 924 ; N a173 ; B 35 98 889 594 ; -C 227 ; WX 924 ; N a162 ; B 35 98 889 594 ; -C 228 ; WX 917 ; N a174 ; B 35 0 882 692 ; -C 229 ; WX 930 ; N a175 ; B 35 84 896 608 ; -C 230 ; WX 931 ; N a176 ; B 35 84 896 608 ; -C 231 ; WX 463 ; N a177 ; B 35 -99 429 791 ; -C 232 ; WX 883 ; N a178 ; B 35 71 848 623 ; -C 233 ; WX 836 ; N a179 ; B 35 44 802 648 ; -C 234 ; WX 836 ; N a193 ; B 35 44 802 648 ; -C 235 ; WX 867 ; N a180 ; B 35 101 832 591 ; -C 236 ; WX 867 ; N a199 ; B 35 101 832 591 ; -C 237 ; WX 696 ; N a181 ; B 35 44 661 648 ; -C 238 ; WX 696 ; N a200 ; B 35 44 661 648 ; -C 239 ; WX 874 ; N a182 ; B 35 77 840 619 ; -C 241 ; WX 874 ; N a201 ; B 35 73 840 615 ; -C 242 ; WX 760 ; N a183 ; B 35 0 725 692 ; -C 243 ; WX 946 ; N a184 ; B 35 160 911 533 ; -C 244 ; WX 771 ; N a197 ; B 34 37 736 655 ; -C 245 ; WX 865 ; N a185 ; B 35 207 830 481 ; -C 246 ; WX 771 ; N a194 ; B 34 37 736 655 ; -C 247 ; WX 888 ; N a198 ; B 34 -19 853 712 ; -C 248 ; WX 967 ; N a186 ; B 35 124 932 568 ; -C 249 ; WX 888 ; N a195 ; B 34 -19 853 712 ; -C 250 ; WX 831 ; N a187 ; B 35 113 796 579 ; -C 251 ; WX 873 ; N a188 ; B 36 118 838 578 ; -C 252 ; WX 927 ; N a189 ; B 35 150 891 542 ; -C 253 ; WX 970 ; N a190 ; B 35 76 931 616 ; -C 254 ; WX 918 ; N a191 ; B 34 99 884 593 ; -C -1 ; WX 410 ; N a86 ; B 35 0 375 692 ; -C -1 ; WX 509 ; N a85 ; B 35 0 475 692 ; -C -1 ; WX 334 ; N a95 ; B 35 0 299 692 ; -C -1 ; WX 509 ; N a205 ; B 35 0 475 692 ; -C -1 ; WX 390 ; N a89 ; B 35 -14 356 705 ; -C -1 ; WX 234 ; N a87 ; B 35 -14 199 705 ; -C -1 ; WX 276 ; N a91 ; B 35 0 242 692 ; -C -1 ; WX 390 ; N a90 ; B 35 -14 355 705 ; -C -1 ; WX 410 ; N a206 ; B 35 0 375 692 ; -C -1 ; WX 317 ; N a94 ; B 35 0 283 692 ; -C -1 ; WX 317 ; N a93 ; B 35 0 283 692 ; -C -1 ; WX 276 ; N a92 ; B 35 0 242 692 ; -C -1 ; WX 334 ; N a96 ; B 35 0 299 692 ; -C -1 ; WX 234 ; N a88 ; B 35 -14 199 705 ; -EndCharMetrics -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Courier-Bold.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Courier-Bold.afm deleted file mode 100644 index eb80542..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Courier-Bold.afm +++ /dev/null @@ -1,342 +0,0 @@ -StartFontMetrics 4.1 -Comment Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Mon Jun 23 16:28:00 1997 -Comment UniqueID 43048 -Comment VMusage 41139 52164 -FontName Courier-Bold -FullName Courier Bold -FamilyName Courier -Weight Bold -ItalicAngle 0 -IsFixedPitch true -CharacterSet ExtendedRoman -FontBBox -113 -250 749 801 -UnderlinePosition -100 -UnderlineThickness 50 -Version 003.000 -Notice Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. -EncodingScheme AdobeStandardEncoding -CapHeight 562 -XHeight 439 -Ascender 629 -Descender -157 -StdHW 84 -StdVW 106 -StartCharMetrics 315 -C 32 ; WX 600 ; N space ; B 0 0 0 0 ; -C 33 ; WX 600 ; N exclam ; B 202 -15 398 572 ; -C 34 ; WX 600 ; N quotedbl ; B 135 277 465 562 ; -C 35 ; WX 600 ; N numbersign ; B 56 -45 544 651 ; -C 36 ; WX 600 ; N dollar ; B 82 -126 519 666 ; -C 37 ; WX 600 ; N percent ; B 5 -15 595 616 ; -C 38 ; WX 600 ; N ampersand ; B 36 -15 546 543 ; -C 39 ; WX 600 ; N quoteright ; B 171 277 423 562 ; -C 40 ; WX 600 ; N parenleft ; B 219 -102 461 616 ; -C 41 ; WX 600 ; N parenright ; B 139 -102 381 616 ; -C 42 ; WX 600 ; N asterisk ; B 91 219 509 601 ; -C 43 ; WX 600 ; N plus ; B 71 39 529 478 ; -C 44 ; WX 600 ; N comma ; B 123 -111 393 174 ; -C 45 ; WX 600 ; N hyphen ; B 100 203 500 313 ; -C 46 ; WX 600 ; N period ; B 192 -15 408 171 ; -C 47 ; WX 600 ; N slash ; B 98 -77 502 626 ; -C 48 ; WX 600 ; N zero ; B 87 -15 513 616 ; -C 49 ; WX 600 ; N one ; B 81 0 539 616 ; -C 50 ; WX 600 ; N two ; B 61 0 499 616 ; -C 51 ; WX 600 ; N three ; B 63 -15 501 616 ; -C 52 ; WX 600 ; N four ; B 53 0 507 616 ; -C 53 ; WX 600 ; N five ; B 70 -15 521 601 ; -C 54 ; WX 600 ; N six ; B 90 -15 521 616 ; -C 55 ; WX 600 ; N seven ; B 55 0 494 601 ; -C 56 ; WX 600 ; N eight ; B 83 -15 517 616 ; -C 57 ; WX 600 ; N nine ; B 79 -15 510 616 ; -C 58 ; WX 600 ; N colon ; B 191 -15 407 425 ; -C 59 ; WX 600 ; N semicolon ; B 123 -111 408 425 ; -C 60 ; WX 600 ; N less ; B 66 15 523 501 ; -C 61 ; WX 600 ; N equal ; B 71 118 529 398 ; -C 62 ; WX 600 ; N greater ; B 77 15 534 501 ; -C 63 ; WX 600 ; N question ; B 98 -14 501 580 ; -C 64 ; WX 600 ; N at ; B 16 -15 584 616 ; -C 65 ; WX 600 ; N A ; B -9 0 609 562 ; -C 66 ; WX 600 ; N B ; B 30 0 573 562 ; -C 67 ; WX 600 ; N C ; B 22 -18 560 580 ; -C 68 ; WX 600 ; N D ; B 30 0 594 562 ; -C 69 ; WX 600 ; N E ; B 25 0 560 562 ; -C 70 ; WX 600 ; N F ; B 39 0 570 562 ; -C 71 ; WX 600 ; N G ; B 22 -18 594 580 ; -C 72 ; WX 600 ; N H ; B 20 0 580 562 ; -C 73 ; WX 600 ; N I ; B 77 0 523 562 ; -C 74 ; WX 600 ; N J ; B 37 -18 601 562 ; -C 75 ; WX 600 ; N K ; B 21 0 599 562 ; -C 76 ; WX 600 ; N L ; B 39 0 578 562 ; -C 77 ; WX 600 ; N M ; B -2 0 602 562 ; -C 78 ; WX 600 ; N N ; B 8 -12 610 562 ; -C 79 ; WX 600 ; N O ; B 22 -18 578 580 ; -C 80 ; WX 600 ; N P ; B 48 0 559 562 ; -C 81 ; WX 600 ; N Q ; B 32 -138 578 580 ; -C 82 ; WX 600 ; N R ; B 24 0 599 562 ; -C 83 ; WX 600 ; N S ; B 47 -22 553 582 ; -C 84 ; WX 600 ; N T ; B 21 0 579 562 ; -C 85 ; WX 600 ; N U ; B 4 -18 596 562 ; -C 86 ; WX 600 ; N V ; B -13 0 613 562 ; -C 87 ; WX 600 ; N W ; B -18 0 618 562 ; -C 88 ; WX 600 ; N X ; B 12 0 588 562 ; -C 89 ; WX 600 ; N Y ; B 12 0 589 562 ; -C 90 ; WX 600 ; N Z ; B 62 0 539 562 ; -C 91 ; WX 600 ; N bracketleft ; B 245 -102 475 616 ; -C 92 ; WX 600 ; N backslash ; B 99 -77 503 626 ; -C 93 ; WX 600 ; N bracketright ; B 125 -102 355 616 ; -C 94 ; WX 600 ; N asciicircum ; B 108 250 492 616 ; -C 95 ; WX 600 ; N underscore ; B 0 -125 600 -75 ; -C 96 ; WX 600 ; N quoteleft ; B 178 277 428 562 ; -C 97 ; WX 600 ; N a ; B 35 -15 570 454 ; -C 98 ; WX 600 ; N b ; B 0 -15 584 626 ; -C 99 ; WX 600 ; N c ; B 40 -15 545 459 ; -C 100 ; WX 600 ; N d ; B 20 -15 591 626 ; -C 101 ; WX 600 ; N e ; B 40 -15 563 454 ; -C 102 ; WX 600 ; N f ; B 83 0 547 626 ; L i fi ; L l fl ; -C 103 ; WX 600 ; N g ; B 30 -146 580 454 ; -C 104 ; WX 600 ; N h ; B 5 0 592 626 ; -C 105 ; WX 600 ; N i ; B 77 0 523 658 ; -C 106 ; WX 600 ; N j ; B 63 -146 440 658 ; -C 107 ; WX 600 ; N k ; B 20 0 585 626 ; -C 108 ; WX 600 ; N l ; B 77 0 523 626 ; -C 109 ; WX 600 ; N m ; B -22 0 626 454 ; -C 110 ; WX 600 ; N n ; B 18 0 592 454 ; -C 111 ; WX 600 ; N o ; B 30 -15 570 454 ; -C 112 ; WX 600 ; N p ; B -1 -142 570 454 ; -C 113 ; WX 600 ; N q ; B 20 -142 591 454 ; -C 114 ; WX 600 ; N r ; B 47 0 580 454 ; -C 115 ; WX 600 ; N s ; B 68 -17 535 459 ; -C 116 ; WX 600 ; N t ; B 47 -15 532 562 ; -C 117 ; WX 600 ; N u ; B -1 -15 569 439 ; -C 118 ; WX 600 ; N v ; B -1 0 601 439 ; -C 119 ; WX 600 ; N w ; B -18 0 618 439 ; -C 120 ; WX 600 ; N x ; B 6 0 594 439 ; -C 121 ; WX 600 ; N y ; B -4 -142 601 439 ; -C 122 ; WX 600 ; N z ; B 81 0 520 439 ; -C 123 ; WX 600 ; N braceleft ; B 160 -102 464 616 ; -C 124 ; WX 600 ; N bar ; B 255 -250 345 750 ; -C 125 ; WX 600 ; N braceright ; B 136 -102 440 616 ; -C 126 ; WX 600 ; N asciitilde ; B 71 153 530 356 ; -C 161 ; WX 600 ; N exclamdown ; B 202 -146 398 449 ; -C 162 ; WX 600 ; N cent ; B 66 -49 518 614 ; -C 163 ; WX 600 ; N sterling ; B 72 -28 558 611 ; -C 164 ; WX 600 ; N fraction ; B 25 -60 576 661 ; -C 165 ; WX 600 ; N yen ; B 10 0 590 562 ; -C 166 ; WX 600 ; N florin ; B -30 -131 572 616 ; -C 167 ; WX 600 ; N section ; B 83 -70 517 580 ; -C 168 ; WX 600 ; N currency ; B 54 49 546 517 ; -C 169 ; WX 600 ; N quotesingle ; B 227 277 373 562 ; -C 170 ; WX 600 ; N quotedblleft ; B 71 277 535 562 ; -C 171 ; WX 600 ; N guillemotleft ; B 8 70 553 446 ; -C 172 ; WX 600 ; N guilsinglleft ; B 141 70 459 446 ; -C 173 ; WX 600 ; N guilsinglright ; B 141 70 459 446 ; -C 174 ; WX 600 ; N fi ; B 12 0 593 626 ; -C 175 ; WX 600 ; N fl ; B 12 0 593 626 ; -C 177 ; WX 600 ; N endash ; B 65 203 535 313 ; -C 178 ; WX 600 ; N dagger ; B 106 -70 494 580 ; -C 179 ; WX 600 ; N daggerdbl ; B 106 -70 494 580 ; -C 180 ; WX 600 ; N periodcentered ; B 196 165 404 351 ; -C 182 ; WX 600 ; N paragraph ; B 6 -70 576 580 ; -C 183 ; WX 600 ; N bullet ; B 140 132 460 430 ; -C 184 ; WX 600 ; N quotesinglbase ; B 175 -142 427 143 ; -C 185 ; WX 600 ; N quotedblbase ; B 65 -142 529 143 ; -C 186 ; WX 600 ; N quotedblright ; B 61 277 525 562 ; -C 187 ; WX 600 ; N guillemotright ; B 47 70 592 446 ; -C 188 ; WX 600 ; N ellipsis ; B 26 -15 574 116 ; -C 189 ; WX 600 ; N perthousand ; B -113 -15 713 616 ; -C 191 ; WX 600 ; N questiondown ; B 99 -146 502 449 ; -C 193 ; WX 600 ; N grave ; B 132 508 395 661 ; -C 194 ; WX 600 ; N acute ; B 205 508 468 661 ; -C 195 ; WX 600 ; N circumflex ; B 103 483 497 657 ; -C 196 ; WX 600 ; N tilde ; B 89 493 512 636 ; -C 197 ; WX 600 ; N macron ; B 88 505 512 585 ; -C 198 ; WX 600 ; N breve ; B 83 468 517 631 ; -C 199 ; WX 600 ; N dotaccent ; B 230 498 370 638 ; -C 200 ; WX 600 ; N dieresis ; B 128 498 472 638 ; -C 202 ; WX 600 ; N ring ; B 198 481 402 678 ; -C 203 ; WX 600 ; N cedilla ; B 205 -206 387 0 ; -C 205 ; WX 600 ; N hungarumlaut ; B 68 488 588 661 ; -C 206 ; WX 600 ; N ogonek ; B 169 -199 400 0 ; -C 207 ; WX 600 ; N caron ; B 103 493 497 667 ; -C 208 ; WX 600 ; N emdash ; B -10 203 610 313 ; -C 225 ; WX 600 ; N AE ; B -29 0 602 562 ; -C 227 ; WX 600 ; N ordfeminine ; B 147 196 453 580 ; -C 232 ; WX 600 ; N Lslash ; B 39 0 578 562 ; -C 233 ; WX 600 ; N Oslash ; B 22 -22 578 584 ; -C 234 ; WX 600 ; N OE ; B -25 0 595 562 ; -C 235 ; WX 600 ; N ordmasculine ; B 147 196 453 580 ; -C 241 ; WX 600 ; N ae ; B -4 -15 601 454 ; -C 245 ; WX 600 ; N dotlessi ; B 77 0 523 439 ; -C 248 ; WX 600 ; N lslash ; B 77 0 523 626 ; -C 249 ; WX 600 ; N oslash ; B 30 -24 570 463 ; -C 250 ; WX 600 ; N oe ; B -18 -15 611 454 ; -C 251 ; WX 600 ; N germandbls ; B 22 -15 596 626 ; -C -1 ; WX 600 ; N Idieresis ; B 77 0 523 761 ; -C -1 ; WX 600 ; N eacute ; B 40 -15 563 661 ; -C -1 ; WX 600 ; N abreve ; B 35 -15 570 661 ; -C -1 ; WX 600 ; N uhungarumlaut ; B -1 -15 628 661 ; -C -1 ; WX 600 ; N ecaron ; B 40 -15 563 667 ; -C -1 ; WX 600 ; N Ydieresis ; B 12 0 589 761 ; -C -1 ; WX 600 ; N divide ; B 71 16 529 500 ; -C -1 ; WX 600 ; N Yacute ; B 12 0 589 784 ; -C -1 ; WX 600 ; N Acircumflex ; B -9 0 609 780 ; -C -1 ; WX 600 ; N aacute ; B 35 -15 570 661 ; -C -1 ; WX 600 ; N Ucircumflex ; B 4 -18 596 780 ; -C -1 ; WX 600 ; N yacute ; B -4 -142 601 661 ; -C -1 ; WX 600 ; N scommaaccent ; B 68 -250 535 459 ; -C -1 ; WX 600 ; N ecircumflex ; B 40 -15 563 657 ; -C -1 ; WX 600 ; N Uring ; B 4 -18 596 801 ; -C -1 ; WX 600 ; N Udieresis ; B 4 -18 596 761 ; -C -1 ; WX 600 ; N aogonek ; B 35 -199 586 454 ; -C -1 ; WX 600 ; N Uacute ; B 4 -18 596 784 ; -C -1 ; WX 600 ; N uogonek ; B -1 -199 585 439 ; -C -1 ; WX 600 ; N Edieresis ; B 25 0 560 761 ; -C -1 ; WX 600 ; N Dcroat ; B 30 0 594 562 ; -C -1 ; WX 600 ; N commaaccent ; B 205 -250 397 -57 ; -C -1 ; WX 600 ; N copyright ; B 0 -18 600 580 ; -C -1 ; WX 600 ; N Emacron ; B 25 0 560 708 ; -C -1 ; WX 600 ; N ccaron ; B 40 -15 545 667 ; -C -1 ; WX 600 ; N aring ; B 35 -15 570 678 ; -C -1 ; WX 600 ; N Ncommaaccent ; B 8 -250 610 562 ; -C -1 ; WX 600 ; N lacute ; B 77 0 523 801 ; -C -1 ; WX 600 ; N agrave ; B 35 -15 570 661 ; -C -1 ; WX 600 ; N Tcommaaccent ; B 21 -250 579 562 ; -C -1 ; WX 600 ; N Cacute ; B 22 -18 560 784 ; -C -1 ; WX 600 ; N atilde ; B 35 -15 570 636 ; -C -1 ; WX 600 ; N Edotaccent ; B 25 0 560 761 ; -C -1 ; WX 600 ; N scaron ; B 68 -17 535 667 ; -C -1 ; WX 600 ; N scedilla ; B 68 -206 535 459 ; -C -1 ; WX 600 ; N iacute ; B 77 0 523 661 ; -C -1 ; WX 600 ; N lozenge ; B 66 0 534 740 ; -C -1 ; WX 600 ; N Rcaron ; B 24 0 599 790 ; -C -1 ; WX 600 ; N Gcommaaccent ; B 22 -250 594 580 ; -C -1 ; WX 600 ; N ucircumflex ; B -1 -15 569 657 ; -C -1 ; WX 600 ; N acircumflex ; B 35 -15 570 657 ; -C -1 ; WX 600 ; N Amacron ; B -9 0 609 708 ; -C -1 ; WX 600 ; N rcaron ; B 47 0 580 667 ; -C -1 ; WX 600 ; N ccedilla ; B 40 -206 545 459 ; -C -1 ; WX 600 ; N Zdotaccent ; B 62 0 539 761 ; -C -1 ; WX 600 ; N Thorn ; B 48 0 557 562 ; -C -1 ; WX 600 ; N Omacron ; B 22 -18 578 708 ; -C -1 ; WX 600 ; N Racute ; B 24 0 599 784 ; -C -1 ; WX 600 ; N Sacute ; B 47 -22 553 784 ; -C -1 ; WX 600 ; N dcaron ; B 20 -15 727 626 ; -C -1 ; WX 600 ; N Umacron ; B 4 -18 596 708 ; -C -1 ; WX 600 ; N uring ; B -1 -15 569 678 ; -C -1 ; WX 600 ; N threesuperior ; B 138 222 433 616 ; -C -1 ; WX 600 ; N Ograve ; B 22 -18 578 784 ; -C -1 ; WX 600 ; N Agrave ; B -9 0 609 784 ; -C -1 ; WX 600 ; N Abreve ; B -9 0 609 784 ; -C -1 ; WX 600 ; N multiply ; B 81 39 520 478 ; -C -1 ; WX 600 ; N uacute ; B -1 -15 569 661 ; -C -1 ; WX 600 ; N Tcaron ; B 21 0 579 790 ; -C -1 ; WX 600 ; N partialdiff ; B 63 -38 537 728 ; -C -1 ; WX 600 ; N ydieresis ; B -4 -142 601 638 ; -C -1 ; WX 600 ; N Nacute ; B 8 -12 610 784 ; -C -1 ; WX 600 ; N icircumflex ; B 73 0 523 657 ; -C -1 ; WX 600 ; N Ecircumflex ; B 25 0 560 780 ; -C -1 ; WX 600 ; N adieresis ; B 35 -15 570 638 ; -C -1 ; WX 600 ; N edieresis ; B 40 -15 563 638 ; -C -1 ; WX 600 ; N cacute ; B 40 -15 545 661 ; -C -1 ; WX 600 ; N nacute ; B 18 0 592 661 ; -C -1 ; WX 600 ; N umacron ; B -1 -15 569 585 ; -C -1 ; WX 600 ; N Ncaron ; B 8 -12 610 790 ; -C -1 ; WX 600 ; N Iacute ; B 77 0 523 784 ; -C -1 ; WX 600 ; N plusminus ; B 71 24 529 515 ; -C -1 ; WX 600 ; N brokenbar ; B 255 -175 345 675 ; -C -1 ; WX 600 ; N registered ; B 0 -18 600 580 ; -C -1 ; WX 600 ; N Gbreve ; B 22 -18 594 784 ; -C -1 ; WX 600 ; N Idotaccent ; B 77 0 523 761 ; -C -1 ; WX 600 ; N summation ; B 15 -10 586 706 ; -C -1 ; WX 600 ; N Egrave ; B 25 0 560 784 ; -C -1 ; WX 600 ; N racute ; B 47 0 580 661 ; -C -1 ; WX 600 ; N omacron ; B 30 -15 570 585 ; -C -1 ; WX 600 ; N Zacute ; B 62 0 539 784 ; -C -1 ; WX 600 ; N Zcaron ; B 62 0 539 790 ; -C -1 ; WX 600 ; N greaterequal ; B 26 0 523 696 ; -C -1 ; WX 600 ; N Eth ; B 30 0 594 562 ; -C -1 ; WX 600 ; N Ccedilla ; B 22 -206 560 580 ; -C -1 ; WX 600 ; N lcommaaccent ; B 77 -250 523 626 ; -C -1 ; WX 600 ; N tcaron ; B 47 -15 532 703 ; -C -1 ; WX 600 ; N eogonek ; B 40 -199 563 454 ; -C -1 ; WX 600 ; N Uogonek ; B 4 -199 596 562 ; -C -1 ; WX 600 ; N Aacute ; B -9 0 609 784 ; -C -1 ; WX 600 ; N Adieresis ; B -9 0 609 761 ; -C -1 ; WX 600 ; N egrave ; B 40 -15 563 661 ; -C -1 ; WX 600 ; N zacute ; B 81 0 520 661 ; -C -1 ; WX 600 ; N iogonek ; B 77 -199 523 658 ; -C -1 ; WX 600 ; N Oacute ; B 22 -18 578 784 ; -C -1 ; WX 600 ; N oacute ; B 30 -15 570 661 ; -C -1 ; WX 600 ; N amacron ; B 35 -15 570 585 ; -C -1 ; WX 600 ; N sacute ; B 68 -17 535 661 ; -C -1 ; WX 600 ; N idieresis ; B 77 0 523 618 ; -C -1 ; WX 600 ; N Ocircumflex ; B 22 -18 578 780 ; -C -1 ; WX 600 ; N Ugrave ; B 4 -18 596 784 ; -C -1 ; WX 600 ; N Delta ; B 6 0 594 688 ; -C -1 ; WX 600 ; N thorn ; B -14 -142 570 626 ; -C -1 ; WX 600 ; N twosuperior ; B 143 230 436 616 ; -C -1 ; WX 600 ; N Odieresis ; B 22 -18 578 761 ; -C -1 ; WX 600 ; N mu ; B -1 -142 569 439 ; -C -1 ; WX 600 ; N igrave ; B 77 0 523 661 ; -C -1 ; WX 600 ; N ohungarumlaut ; B 30 -15 668 661 ; -C -1 ; WX 600 ; N Eogonek ; B 25 -199 576 562 ; -C -1 ; WX 600 ; N dcroat ; B 20 -15 591 626 ; -C -1 ; WX 600 ; N threequarters ; B -47 -60 648 661 ; -C -1 ; WX 600 ; N Scedilla ; B 47 -206 553 582 ; -C -1 ; WX 600 ; N lcaron ; B 77 0 597 626 ; -C -1 ; WX 600 ; N Kcommaaccent ; B 21 -250 599 562 ; -C -1 ; WX 600 ; N Lacute ; B 39 0 578 784 ; -C -1 ; WX 600 ; N trademark ; B -9 230 749 562 ; -C -1 ; WX 600 ; N edotaccent ; B 40 -15 563 638 ; -C -1 ; WX 600 ; N Igrave ; B 77 0 523 784 ; -C -1 ; WX 600 ; N Imacron ; B 77 0 523 708 ; -C -1 ; WX 600 ; N Lcaron ; B 39 0 637 562 ; -C -1 ; WX 600 ; N onehalf ; B -47 -60 648 661 ; -C -1 ; WX 600 ; N lessequal ; B 26 0 523 696 ; -C -1 ; WX 600 ; N ocircumflex ; B 30 -15 570 657 ; -C -1 ; WX 600 ; N ntilde ; B 18 0 592 636 ; -C -1 ; WX 600 ; N Uhungarumlaut ; B 4 -18 638 784 ; -C -1 ; WX 600 ; N Eacute ; B 25 0 560 784 ; -C -1 ; WX 600 ; N emacron ; B 40 -15 563 585 ; -C -1 ; WX 600 ; N gbreve ; B 30 -146 580 661 ; -C -1 ; WX 600 ; N onequarter ; B -56 -60 656 661 ; -C -1 ; WX 600 ; N Scaron ; B 47 -22 553 790 ; -C -1 ; WX 600 ; N Scommaaccent ; B 47 -250 553 582 ; -C -1 ; WX 600 ; N Ohungarumlaut ; B 22 -18 628 784 ; -C -1 ; WX 600 ; N degree ; B 86 243 474 616 ; -C -1 ; WX 600 ; N ograve ; B 30 -15 570 661 ; -C -1 ; WX 600 ; N Ccaron ; B 22 -18 560 790 ; -C -1 ; WX 600 ; N ugrave ; B -1 -15 569 661 ; -C -1 ; WX 600 ; N radical ; B -19 -104 473 778 ; -C -1 ; WX 600 ; N Dcaron ; B 30 0 594 790 ; -C -1 ; WX 600 ; N rcommaaccent ; B 47 -250 580 454 ; -C -1 ; WX 600 ; N Ntilde ; B 8 -12 610 759 ; -C -1 ; WX 600 ; N otilde ; B 30 -15 570 636 ; -C -1 ; WX 600 ; N Rcommaaccent ; B 24 -250 599 562 ; -C -1 ; WX 600 ; N Lcommaaccent ; B 39 -250 578 562 ; -C -1 ; WX 600 ; N Atilde ; B -9 0 609 759 ; -C -1 ; WX 600 ; N Aogonek ; B -9 -199 625 562 ; -C -1 ; WX 600 ; N Aring ; B -9 0 609 801 ; -C -1 ; WX 600 ; N Otilde ; B 22 -18 578 759 ; -C -1 ; WX 600 ; N zdotaccent ; B 81 0 520 638 ; -C -1 ; WX 600 ; N Ecaron ; B 25 0 560 790 ; -C -1 ; WX 600 ; N Iogonek ; B 77 -199 523 562 ; -C -1 ; WX 600 ; N kcommaaccent ; B 20 -250 585 626 ; -C -1 ; WX 600 ; N minus ; B 71 203 529 313 ; -C -1 ; WX 600 ; N Icircumflex ; B 77 0 523 780 ; -C -1 ; WX 600 ; N ncaron ; B 18 0 592 667 ; -C -1 ; WX 600 ; N tcommaaccent ; B 47 -250 532 562 ; -C -1 ; WX 600 ; N logicalnot ; B 71 103 529 413 ; -C -1 ; WX 600 ; N odieresis ; B 30 -15 570 638 ; -C -1 ; WX 600 ; N udieresis ; B -1 -15 569 638 ; -C -1 ; WX 600 ; N notequal ; B 12 -47 537 563 ; -C -1 ; WX 600 ; N gcommaaccent ; B 30 -146 580 714 ; -C -1 ; WX 600 ; N eth ; B 58 -27 543 626 ; -C -1 ; WX 600 ; N zcaron ; B 81 0 520 667 ; -C -1 ; WX 600 ; N ncommaaccent ; B 18 -250 592 454 ; -C -1 ; WX 600 ; N onesuperior ; B 153 230 447 616 ; -C -1 ; WX 600 ; N imacron ; B 77 0 523 585 ; -C -1 ; WX 600 ; N Euro ; B 0 0 0 0 ; -EndCharMetrics -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Courier-BoldOblique.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Courier-BoldOblique.afm deleted file mode 100644 index 29d3b8b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Courier-BoldOblique.afm +++ /dev/null @@ -1,342 +0,0 @@ -StartFontMetrics 4.1 -Comment Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Mon Jun 23 16:28:46 1997 -Comment UniqueID 43049 -Comment VMusage 17529 79244 -FontName Courier-BoldOblique -FullName Courier Bold Oblique -FamilyName Courier -Weight Bold -ItalicAngle -12 -IsFixedPitch true -CharacterSet ExtendedRoman -FontBBox -57 -250 869 801 -UnderlinePosition -100 -UnderlineThickness 50 -Version 003.000 -Notice Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. -EncodingScheme AdobeStandardEncoding -CapHeight 562 -XHeight 439 -Ascender 629 -Descender -157 -StdHW 84 -StdVW 106 -StartCharMetrics 315 -C 32 ; WX 600 ; N space ; B 0 0 0 0 ; -C 33 ; WX 600 ; N exclam ; B 215 -15 495 572 ; -C 34 ; WX 600 ; N quotedbl ; B 211 277 585 562 ; -C 35 ; WX 600 ; N numbersign ; B 88 -45 641 651 ; -C 36 ; WX 600 ; N dollar ; B 87 -126 630 666 ; -C 37 ; WX 600 ; N percent ; B 101 -15 625 616 ; -C 38 ; WX 600 ; N ampersand ; B 61 -15 595 543 ; -C 39 ; WX 600 ; N quoteright ; B 229 277 543 562 ; -C 40 ; WX 600 ; N parenleft ; B 265 -102 592 616 ; -C 41 ; WX 600 ; N parenright ; B 117 -102 444 616 ; -C 42 ; WX 600 ; N asterisk ; B 179 219 598 601 ; -C 43 ; WX 600 ; N plus ; B 114 39 596 478 ; -C 44 ; WX 600 ; N comma ; B 99 -111 430 174 ; -C 45 ; WX 600 ; N hyphen ; B 143 203 567 313 ; -C 46 ; WX 600 ; N period ; B 206 -15 427 171 ; -C 47 ; WX 600 ; N slash ; B 90 -77 626 626 ; -C 48 ; WX 600 ; N zero ; B 135 -15 593 616 ; -C 49 ; WX 600 ; N one ; B 93 0 562 616 ; -C 50 ; WX 600 ; N two ; B 61 0 594 616 ; -C 51 ; WX 600 ; N three ; B 71 -15 571 616 ; -C 52 ; WX 600 ; N four ; B 81 0 559 616 ; -C 53 ; WX 600 ; N five ; B 77 -15 621 601 ; -C 54 ; WX 600 ; N six ; B 135 -15 652 616 ; -C 55 ; WX 600 ; N seven ; B 147 0 622 601 ; -C 56 ; WX 600 ; N eight ; B 115 -15 604 616 ; -C 57 ; WX 600 ; N nine ; B 75 -15 592 616 ; -C 58 ; WX 600 ; N colon ; B 205 -15 480 425 ; -C 59 ; WX 600 ; N semicolon ; B 99 -111 481 425 ; -C 60 ; WX 600 ; N less ; B 120 15 613 501 ; -C 61 ; WX 600 ; N equal ; B 96 118 614 398 ; -C 62 ; WX 600 ; N greater ; B 97 15 589 501 ; -C 63 ; WX 600 ; N question ; B 183 -14 592 580 ; -C 64 ; WX 600 ; N at ; B 65 -15 642 616 ; -C 65 ; WX 600 ; N A ; B -9 0 632 562 ; -C 66 ; WX 600 ; N B ; B 30 0 630 562 ; -C 67 ; WX 600 ; N C ; B 74 -18 675 580 ; -C 68 ; WX 600 ; N D ; B 30 0 664 562 ; -C 69 ; WX 600 ; N E ; B 25 0 670 562 ; -C 70 ; WX 600 ; N F ; B 39 0 684 562 ; -C 71 ; WX 600 ; N G ; B 74 -18 675 580 ; -C 72 ; WX 600 ; N H ; B 20 0 700 562 ; -C 73 ; WX 600 ; N I ; B 77 0 643 562 ; -C 74 ; WX 600 ; N J ; B 58 -18 721 562 ; -C 75 ; WX 600 ; N K ; B 21 0 692 562 ; -C 76 ; WX 600 ; N L ; B 39 0 636 562 ; -C 77 ; WX 600 ; N M ; B -2 0 722 562 ; -C 78 ; WX 600 ; N N ; B 8 -12 730 562 ; -C 79 ; WX 600 ; N O ; B 74 -18 645 580 ; -C 80 ; WX 600 ; N P ; B 48 0 643 562 ; -C 81 ; WX 600 ; N Q ; B 83 -138 636 580 ; -C 82 ; WX 600 ; N R ; B 24 0 617 562 ; -C 83 ; WX 600 ; N S ; B 54 -22 673 582 ; -C 84 ; WX 600 ; N T ; B 86 0 679 562 ; -C 85 ; WX 600 ; N U ; B 101 -18 716 562 ; -C 86 ; WX 600 ; N V ; B 84 0 733 562 ; -C 87 ; WX 600 ; N W ; B 79 0 738 562 ; -C 88 ; WX 600 ; N X ; B 12 0 690 562 ; -C 89 ; WX 600 ; N Y ; B 109 0 709 562 ; -C 90 ; WX 600 ; N Z ; B 62 0 637 562 ; -C 91 ; WX 600 ; N bracketleft ; B 223 -102 606 616 ; -C 92 ; WX 600 ; N backslash ; B 222 -77 496 626 ; -C 93 ; WX 600 ; N bracketright ; B 103 -102 486 616 ; -C 94 ; WX 600 ; N asciicircum ; B 171 250 556 616 ; -C 95 ; WX 600 ; N underscore ; B -27 -125 585 -75 ; -C 96 ; WX 600 ; N quoteleft ; B 297 277 487 562 ; -C 97 ; WX 600 ; N a ; B 61 -15 593 454 ; -C 98 ; WX 600 ; N b ; B 13 -15 636 626 ; -C 99 ; WX 600 ; N c ; B 81 -15 631 459 ; -C 100 ; WX 600 ; N d ; B 60 -15 645 626 ; -C 101 ; WX 600 ; N e ; B 81 -15 605 454 ; -C 102 ; WX 600 ; N f ; B 83 0 677 626 ; L i fi ; L l fl ; -C 103 ; WX 600 ; N g ; B 40 -146 674 454 ; -C 104 ; WX 600 ; N h ; B 18 0 615 626 ; -C 105 ; WX 600 ; N i ; B 77 0 546 658 ; -C 106 ; WX 600 ; N j ; B 36 -146 580 658 ; -C 107 ; WX 600 ; N k ; B 33 0 643 626 ; -C 108 ; WX 600 ; N l ; B 77 0 546 626 ; -C 109 ; WX 600 ; N m ; B -22 0 649 454 ; -C 110 ; WX 600 ; N n ; B 18 0 615 454 ; -C 111 ; WX 600 ; N o ; B 71 -15 622 454 ; -C 112 ; WX 600 ; N p ; B -32 -142 622 454 ; -C 113 ; WX 600 ; N q ; B 60 -142 685 454 ; -C 114 ; WX 600 ; N r ; B 47 0 655 454 ; -C 115 ; WX 600 ; N s ; B 66 -17 608 459 ; -C 116 ; WX 600 ; N t ; B 118 -15 567 562 ; -C 117 ; WX 600 ; N u ; B 70 -15 592 439 ; -C 118 ; WX 600 ; N v ; B 70 0 695 439 ; -C 119 ; WX 600 ; N w ; B 53 0 712 439 ; -C 120 ; WX 600 ; N x ; B 6 0 671 439 ; -C 121 ; WX 600 ; N y ; B -21 -142 695 439 ; -C 122 ; WX 600 ; N z ; B 81 0 614 439 ; -C 123 ; WX 600 ; N braceleft ; B 203 -102 595 616 ; -C 124 ; WX 600 ; N bar ; B 201 -250 505 750 ; -C 125 ; WX 600 ; N braceright ; B 114 -102 506 616 ; -C 126 ; WX 600 ; N asciitilde ; B 120 153 590 356 ; -C 161 ; WX 600 ; N exclamdown ; B 196 -146 477 449 ; -C 162 ; WX 600 ; N cent ; B 121 -49 605 614 ; -C 163 ; WX 600 ; N sterling ; B 106 -28 650 611 ; -C 164 ; WX 600 ; N fraction ; B 22 -60 708 661 ; -C 165 ; WX 600 ; N yen ; B 98 0 710 562 ; -C 166 ; WX 600 ; N florin ; B -57 -131 702 616 ; -C 167 ; WX 600 ; N section ; B 74 -70 620 580 ; -C 168 ; WX 600 ; N currency ; B 77 49 644 517 ; -C 169 ; WX 600 ; N quotesingle ; B 303 277 493 562 ; -C 170 ; WX 600 ; N quotedblleft ; B 190 277 594 562 ; -C 171 ; WX 600 ; N guillemotleft ; B 62 70 639 446 ; -C 172 ; WX 600 ; N guilsinglleft ; B 195 70 545 446 ; -C 173 ; WX 600 ; N guilsinglright ; B 165 70 514 446 ; -C 174 ; WX 600 ; N fi ; B 12 0 644 626 ; -C 175 ; WX 600 ; N fl ; B 12 0 644 626 ; -C 177 ; WX 600 ; N endash ; B 108 203 602 313 ; -C 178 ; WX 600 ; N dagger ; B 175 -70 586 580 ; -C 179 ; WX 600 ; N daggerdbl ; B 121 -70 587 580 ; -C 180 ; WX 600 ; N periodcentered ; B 248 165 461 351 ; -C 182 ; WX 600 ; N paragraph ; B 61 -70 700 580 ; -C 183 ; WX 600 ; N bullet ; B 196 132 523 430 ; -C 184 ; WX 600 ; N quotesinglbase ; B 144 -142 458 143 ; -C 185 ; WX 600 ; N quotedblbase ; B 34 -142 560 143 ; -C 186 ; WX 600 ; N quotedblright ; B 119 277 645 562 ; -C 187 ; WX 600 ; N guillemotright ; B 71 70 647 446 ; -C 188 ; WX 600 ; N ellipsis ; B 35 -15 587 116 ; -C 189 ; WX 600 ; N perthousand ; B -45 -15 743 616 ; -C 191 ; WX 600 ; N questiondown ; B 100 -146 509 449 ; -C 193 ; WX 600 ; N grave ; B 272 508 503 661 ; -C 194 ; WX 600 ; N acute ; B 312 508 609 661 ; -C 195 ; WX 600 ; N circumflex ; B 212 483 607 657 ; -C 196 ; WX 600 ; N tilde ; B 199 493 643 636 ; -C 197 ; WX 600 ; N macron ; B 195 505 637 585 ; -C 198 ; WX 600 ; N breve ; B 217 468 652 631 ; -C 199 ; WX 600 ; N dotaccent ; B 348 498 493 638 ; -C 200 ; WX 600 ; N dieresis ; B 246 498 595 638 ; -C 202 ; WX 600 ; N ring ; B 319 481 528 678 ; -C 203 ; WX 600 ; N cedilla ; B 168 -206 368 0 ; -C 205 ; WX 600 ; N hungarumlaut ; B 171 488 729 661 ; -C 206 ; WX 600 ; N ogonek ; B 143 -199 367 0 ; -C 207 ; WX 600 ; N caron ; B 238 493 633 667 ; -C 208 ; WX 600 ; N emdash ; B 33 203 677 313 ; -C 225 ; WX 600 ; N AE ; B -29 0 708 562 ; -C 227 ; WX 600 ; N ordfeminine ; B 188 196 526 580 ; -C 232 ; WX 600 ; N Lslash ; B 39 0 636 562 ; -C 233 ; WX 600 ; N Oslash ; B 48 -22 673 584 ; -C 234 ; WX 600 ; N OE ; B 26 0 701 562 ; -C 235 ; WX 600 ; N ordmasculine ; B 188 196 543 580 ; -C 241 ; WX 600 ; N ae ; B 21 -15 652 454 ; -C 245 ; WX 600 ; N dotlessi ; B 77 0 546 439 ; -C 248 ; WX 600 ; N lslash ; B 77 0 587 626 ; -C 249 ; WX 600 ; N oslash ; B 54 -24 638 463 ; -C 250 ; WX 600 ; N oe ; B 18 -15 662 454 ; -C 251 ; WX 600 ; N germandbls ; B 22 -15 629 626 ; -C -1 ; WX 600 ; N Idieresis ; B 77 0 643 761 ; -C -1 ; WX 600 ; N eacute ; B 81 -15 609 661 ; -C -1 ; WX 600 ; N abreve ; B 61 -15 658 661 ; -C -1 ; WX 600 ; N uhungarumlaut ; B 70 -15 769 661 ; -C -1 ; WX 600 ; N ecaron ; B 81 -15 633 667 ; -C -1 ; WX 600 ; N Ydieresis ; B 109 0 709 761 ; -C -1 ; WX 600 ; N divide ; B 114 16 596 500 ; -C -1 ; WX 600 ; N Yacute ; B 109 0 709 784 ; -C -1 ; WX 600 ; N Acircumflex ; B -9 0 632 780 ; -C -1 ; WX 600 ; N aacute ; B 61 -15 609 661 ; -C -1 ; WX 600 ; N Ucircumflex ; B 101 -18 716 780 ; -C -1 ; WX 600 ; N yacute ; B -21 -142 695 661 ; -C -1 ; WX 600 ; N scommaaccent ; B 66 -250 608 459 ; -C -1 ; WX 600 ; N ecircumflex ; B 81 -15 607 657 ; -C -1 ; WX 600 ; N Uring ; B 101 -18 716 801 ; -C -1 ; WX 600 ; N Udieresis ; B 101 -18 716 761 ; -C -1 ; WX 600 ; N aogonek ; B 61 -199 593 454 ; -C -1 ; WX 600 ; N Uacute ; B 101 -18 716 784 ; -C -1 ; WX 600 ; N uogonek ; B 70 -199 592 439 ; -C -1 ; WX 600 ; N Edieresis ; B 25 0 670 761 ; -C -1 ; WX 600 ; N Dcroat ; B 30 0 664 562 ; -C -1 ; WX 600 ; N commaaccent ; B 151 -250 385 -57 ; -C -1 ; WX 600 ; N copyright ; B 53 -18 667 580 ; -C -1 ; WX 600 ; N Emacron ; B 25 0 670 708 ; -C -1 ; WX 600 ; N ccaron ; B 81 -15 633 667 ; -C -1 ; WX 600 ; N aring ; B 61 -15 593 678 ; -C -1 ; WX 600 ; N Ncommaaccent ; B 8 -250 730 562 ; -C -1 ; WX 600 ; N lacute ; B 77 0 639 801 ; -C -1 ; WX 600 ; N agrave ; B 61 -15 593 661 ; -C -1 ; WX 600 ; N Tcommaaccent ; B 86 -250 679 562 ; -C -1 ; WX 600 ; N Cacute ; B 74 -18 675 784 ; -C -1 ; WX 600 ; N atilde ; B 61 -15 643 636 ; -C -1 ; WX 600 ; N Edotaccent ; B 25 0 670 761 ; -C -1 ; WX 600 ; N scaron ; B 66 -17 633 667 ; -C -1 ; WX 600 ; N scedilla ; B 66 -206 608 459 ; -C -1 ; WX 600 ; N iacute ; B 77 0 609 661 ; -C -1 ; WX 600 ; N lozenge ; B 145 0 614 740 ; -C -1 ; WX 600 ; N Rcaron ; B 24 0 659 790 ; -C -1 ; WX 600 ; N Gcommaaccent ; B 74 -250 675 580 ; -C -1 ; WX 600 ; N ucircumflex ; B 70 -15 597 657 ; -C -1 ; WX 600 ; N acircumflex ; B 61 -15 607 657 ; -C -1 ; WX 600 ; N Amacron ; B -9 0 633 708 ; -C -1 ; WX 600 ; N rcaron ; B 47 0 655 667 ; -C -1 ; WX 600 ; N ccedilla ; B 81 -206 631 459 ; -C -1 ; WX 600 ; N Zdotaccent ; B 62 0 637 761 ; -C -1 ; WX 600 ; N Thorn ; B 48 0 620 562 ; -C -1 ; WX 600 ; N Omacron ; B 74 -18 663 708 ; -C -1 ; WX 600 ; N Racute ; B 24 0 665 784 ; -C -1 ; WX 600 ; N Sacute ; B 54 -22 673 784 ; -C -1 ; WX 600 ; N dcaron ; B 60 -15 861 626 ; -C -1 ; WX 600 ; N Umacron ; B 101 -18 716 708 ; -C -1 ; WX 600 ; N uring ; B 70 -15 592 678 ; -C -1 ; WX 600 ; N threesuperior ; B 193 222 526 616 ; -C -1 ; WX 600 ; N Ograve ; B 74 -18 645 784 ; -C -1 ; WX 600 ; N Agrave ; B -9 0 632 784 ; -C -1 ; WX 600 ; N Abreve ; B -9 0 684 784 ; -C -1 ; WX 600 ; N multiply ; B 104 39 606 478 ; -C -1 ; WX 600 ; N uacute ; B 70 -15 599 661 ; -C -1 ; WX 600 ; N Tcaron ; B 86 0 679 790 ; -C -1 ; WX 600 ; N partialdiff ; B 91 -38 627 728 ; -C -1 ; WX 600 ; N ydieresis ; B -21 -142 695 638 ; -C -1 ; WX 600 ; N Nacute ; B 8 -12 730 784 ; -C -1 ; WX 600 ; N icircumflex ; B 77 0 577 657 ; -C -1 ; WX 600 ; N Ecircumflex ; B 25 0 670 780 ; -C -1 ; WX 600 ; N adieresis ; B 61 -15 595 638 ; -C -1 ; WX 600 ; N edieresis ; B 81 -15 605 638 ; -C -1 ; WX 600 ; N cacute ; B 81 -15 649 661 ; -C -1 ; WX 600 ; N nacute ; B 18 0 639 661 ; -C -1 ; WX 600 ; N umacron ; B 70 -15 637 585 ; -C -1 ; WX 600 ; N Ncaron ; B 8 -12 730 790 ; -C -1 ; WX 600 ; N Iacute ; B 77 0 643 784 ; -C -1 ; WX 600 ; N plusminus ; B 76 24 614 515 ; -C -1 ; WX 600 ; N brokenbar ; B 217 -175 489 675 ; -C -1 ; WX 600 ; N registered ; B 53 -18 667 580 ; -C -1 ; WX 600 ; N Gbreve ; B 74 -18 684 784 ; -C -1 ; WX 600 ; N Idotaccent ; B 77 0 643 761 ; -C -1 ; WX 600 ; N summation ; B 15 -10 672 706 ; -C -1 ; WX 600 ; N Egrave ; B 25 0 670 784 ; -C -1 ; WX 600 ; N racute ; B 47 0 655 661 ; -C -1 ; WX 600 ; N omacron ; B 71 -15 637 585 ; -C -1 ; WX 600 ; N Zacute ; B 62 0 665 784 ; -C -1 ; WX 600 ; N Zcaron ; B 62 0 659 790 ; -C -1 ; WX 600 ; N greaterequal ; B 26 0 627 696 ; -C -1 ; WX 600 ; N Eth ; B 30 0 664 562 ; -C -1 ; WX 600 ; N Ccedilla ; B 74 -206 675 580 ; -C -1 ; WX 600 ; N lcommaaccent ; B 77 -250 546 626 ; -C -1 ; WX 600 ; N tcaron ; B 118 -15 627 703 ; -C -1 ; WX 600 ; N eogonek ; B 81 -199 605 454 ; -C -1 ; WX 600 ; N Uogonek ; B 101 -199 716 562 ; -C -1 ; WX 600 ; N Aacute ; B -9 0 655 784 ; -C -1 ; WX 600 ; N Adieresis ; B -9 0 632 761 ; -C -1 ; WX 600 ; N egrave ; B 81 -15 605 661 ; -C -1 ; WX 600 ; N zacute ; B 81 0 614 661 ; -C -1 ; WX 600 ; N iogonek ; B 77 -199 546 658 ; -C -1 ; WX 600 ; N Oacute ; B 74 -18 645 784 ; -C -1 ; WX 600 ; N oacute ; B 71 -15 649 661 ; -C -1 ; WX 600 ; N amacron ; B 61 -15 637 585 ; -C -1 ; WX 600 ; N sacute ; B 66 -17 609 661 ; -C -1 ; WX 600 ; N idieresis ; B 77 0 561 618 ; -C -1 ; WX 600 ; N Ocircumflex ; B 74 -18 645 780 ; -C -1 ; WX 600 ; N Ugrave ; B 101 -18 716 784 ; -C -1 ; WX 600 ; N Delta ; B 6 0 594 688 ; -C -1 ; WX 600 ; N thorn ; B -32 -142 622 626 ; -C -1 ; WX 600 ; N twosuperior ; B 191 230 542 616 ; -C -1 ; WX 600 ; N Odieresis ; B 74 -18 645 761 ; -C -1 ; WX 600 ; N mu ; B 49 -142 592 439 ; -C -1 ; WX 600 ; N igrave ; B 77 0 546 661 ; -C -1 ; WX 600 ; N ohungarumlaut ; B 71 -15 809 661 ; -C -1 ; WX 600 ; N Eogonek ; B 25 -199 670 562 ; -C -1 ; WX 600 ; N dcroat ; B 60 -15 712 626 ; -C -1 ; WX 600 ; N threequarters ; B 8 -60 699 661 ; -C -1 ; WX 600 ; N Scedilla ; B 54 -206 673 582 ; -C -1 ; WX 600 ; N lcaron ; B 77 0 731 626 ; -C -1 ; WX 600 ; N Kcommaaccent ; B 21 -250 692 562 ; -C -1 ; WX 600 ; N Lacute ; B 39 0 636 784 ; -C -1 ; WX 600 ; N trademark ; B 86 230 869 562 ; -C -1 ; WX 600 ; N edotaccent ; B 81 -15 605 638 ; -C -1 ; WX 600 ; N Igrave ; B 77 0 643 784 ; -C -1 ; WX 600 ; N Imacron ; B 77 0 663 708 ; -C -1 ; WX 600 ; N Lcaron ; B 39 0 757 562 ; -C -1 ; WX 600 ; N onehalf ; B 22 -60 716 661 ; -C -1 ; WX 600 ; N lessequal ; B 26 0 671 696 ; -C -1 ; WX 600 ; N ocircumflex ; B 71 -15 622 657 ; -C -1 ; WX 600 ; N ntilde ; B 18 0 643 636 ; -C -1 ; WX 600 ; N Uhungarumlaut ; B 101 -18 805 784 ; -C -1 ; WX 600 ; N Eacute ; B 25 0 670 784 ; -C -1 ; WX 600 ; N emacron ; B 81 -15 637 585 ; -C -1 ; WX 600 ; N gbreve ; B 40 -146 674 661 ; -C -1 ; WX 600 ; N onequarter ; B 13 -60 707 661 ; -C -1 ; WX 600 ; N Scaron ; B 54 -22 689 790 ; -C -1 ; WX 600 ; N Scommaaccent ; B 54 -250 673 582 ; -C -1 ; WX 600 ; N Ohungarumlaut ; B 74 -18 795 784 ; -C -1 ; WX 600 ; N degree ; B 173 243 570 616 ; -C -1 ; WX 600 ; N ograve ; B 71 -15 622 661 ; -C -1 ; WX 600 ; N Ccaron ; B 74 -18 689 790 ; -C -1 ; WX 600 ; N ugrave ; B 70 -15 592 661 ; -C -1 ; WX 600 ; N radical ; B 67 -104 635 778 ; -C -1 ; WX 600 ; N Dcaron ; B 30 0 664 790 ; -C -1 ; WX 600 ; N rcommaaccent ; B 47 -250 655 454 ; -C -1 ; WX 600 ; N Ntilde ; B 8 -12 730 759 ; -C -1 ; WX 600 ; N otilde ; B 71 -15 643 636 ; -C -1 ; WX 600 ; N Rcommaaccent ; B 24 -250 617 562 ; -C -1 ; WX 600 ; N Lcommaaccent ; B 39 -250 636 562 ; -C -1 ; WX 600 ; N Atilde ; B -9 0 669 759 ; -C -1 ; WX 600 ; N Aogonek ; B -9 -199 632 562 ; -C -1 ; WX 600 ; N Aring ; B -9 0 632 801 ; -C -1 ; WX 600 ; N Otilde ; B 74 -18 669 759 ; -C -1 ; WX 600 ; N zdotaccent ; B 81 0 614 638 ; -C -1 ; WX 600 ; N Ecaron ; B 25 0 670 790 ; -C -1 ; WX 600 ; N Iogonek ; B 77 -199 643 562 ; -C -1 ; WX 600 ; N kcommaaccent ; B 33 -250 643 626 ; -C -1 ; WX 600 ; N minus ; B 114 203 596 313 ; -C -1 ; WX 600 ; N Icircumflex ; B 77 0 643 780 ; -C -1 ; WX 600 ; N ncaron ; B 18 0 633 667 ; -C -1 ; WX 600 ; N tcommaaccent ; B 118 -250 567 562 ; -C -1 ; WX 600 ; N logicalnot ; B 135 103 617 413 ; -C -1 ; WX 600 ; N odieresis ; B 71 -15 622 638 ; -C -1 ; WX 600 ; N udieresis ; B 70 -15 595 638 ; -C -1 ; WX 600 ; N notequal ; B 30 -47 626 563 ; -C -1 ; WX 600 ; N gcommaaccent ; B 40 -146 674 714 ; -C -1 ; WX 600 ; N eth ; B 93 -27 661 626 ; -C -1 ; WX 600 ; N zcaron ; B 81 0 643 667 ; -C -1 ; WX 600 ; N ncommaaccent ; B 18 -250 615 454 ; -C -1 ; WX 600 ; N onesuperior ; B 212 230 514 616 ; -C -1 ; WX 600 ; N imacron ; B 77 0 575 585 ; -C -1 ; WX 600 ; N Euro ; B 0 0 0 0 ; -EndCharMetrics -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Courier-Oblique.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Courier-Oblique.afm deleted file mode 100644 index 3dc163f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Courier-Oblique.afm +++ /dev/null @@ -1,342 +0,0 @@ -StartFontMetrics 4.1 -Comment Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Thu May 1 17:37:52 1997 -Comment UniqueID 43051 -Comment VMusage 16248 75829 -FontName Courier-Oblique -FullName Courier Oblique -FamilyName Courier -Weight Medium -ItalicAngle -12 -IsFixedPitch true -CharacterSet ExtendedRoman -FontBBox -27 -250 849 805 -UnderlinePosition -100 -UnderlineThickness 50 -Version 003.000 -Notice Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. -EncodingScheme AdobeStandardEncoding -CapHeight 562 -XHeight 426 -Ascender 629 -Descender -157 -StdHW 51 -StdVW 51 -StartCharMetrics 315 -C 32 ; WX 600 ; N space ; B 0 0 0 0 ; -C 33 ; WX 600 ; N exclam ; B 243 -15 464 572 ; -C 34 ; WX 600 ; N quotedbl ; B 273 328 532 562 ; -C 35 ; WX 600 ; N numbersign ; B 133 -32 596 639 ; -C 36 ; WX 600 ; N dollar ; B 108 -126 596 662 ; -C 37 ; WX 600 ; N percent ; B 134 -15 599 622 ; -C 38 ; WX 600 ; N ampersand ; B 87 -15 580 543 ; -C 39 ; WX 600 ; N quoteright ; B 283 328 495 562 ; -C 40 ; WX 600 ; N parenleft ; B 313 -108 572 622 ; -C 41 ; WX 600 ; N parenright ; B 137 -108 396 622 ; -C 42 ; WX 600 ; N asterisk ; B 212 257 580 607 ; -C 43 ; WX 600 ; N plus ; B 129 44 580 470 ; -C 44 ; WX 600 ; N comma ; B 157 -112 370 122 ; -C 45 ; WX 600 ; N hyphen ; B 152 231 558 285 ; -C 46 ; WX 600 ; N period ; B 238 -15 382 109 ; -C 47 ; WX 600 ; N slash ; B 112 -80 604 629 ; -C 48 ; WX 600 ; N zero ; B 154 -15 575 622 ; -C 49 ; WX 600 ; N one ; B 98 0 515 622 ; -C 50 ; WX 600 ; N two ; B 70 0 568 622 ; -C 51 ; WX 600 ; N three ; B 82 -15 538 622 ; -C 52 ; WX 600 ; N four ; B 108 0 541 622 ; -C 53 ; WX 600 ; N five ; B 99 -15 589 607 ; -C 54 ; WX 600 ; N six ; B 155 -15 629 622 ; -C 55 ; WX 600 ; N seven ; B 182 0 612 607 ; -C 56 ; WX 600 ; N eight ; B 132 -15 588 622 ; -C 57 ; WX 600 ; N nine ; B 93 -15 574 622 ; -C 58 ; WX 600 ; N colon ; B 238 -15 441 385 ; -C 59 ; WX 600 ; N semicolon ; B 157 -112 441 385 ; -C 60 ; WX 600 ; N less ; B 96 42 610 472 ; -C 61 ; WX 600 ; N equal ; B 109 138 600 376 ; -C 62 ; WX 600 ; N greater ; B 85 42 599 472 ; -C 63 ; WX 600 ; N question ; B 222 -15 583 572 ; -C 64 ; WX 600 ; N at ; B 127 -15 582 622 ; -C 65 ; WX 600 ; N A ; B 3 0 607 562 ; -C 66 ; WX 600 ; N B ; B 43 0 616 562 ; -C 67 ; WX 600 ; N C ; B 93 -18 655 580 ; -C 68 ; WX 600 ; N D ; B 43 0 645 562 ; -C 69 ; WX 600 ; N E ; B 53 0 660 562 ; -C 70 ; WX 600 ; N F ; B 53 0 660 562 ; -C 71 ; WX 600 ; N G ; B 83 -18 645 580 ; -C 72 ; WX 600 ; N H ; B 32 0 687 562 ; -C 73 ; WX 600 ; N I ; B 96 0 623 562 ; -C 74 ; WX 600 ; N J ; B 52 -18 685 562 ; -C 75 ; WX 600 ; N K ; B 38 0 671 562 ; -C 76 ; WX 600 ; N L ; B 47 0 607 562 ; -C 77 ; WX 600 ; N M ; B 4 0 715 562 ; -C 78 ; WX 600 ; N N ; B 7 -13 712 562 ; -C 79 ; WX 600 ; N O ; B 94 -18 625 580 ; -C 80 ; WX 600 ; N P ; B 79 0 644 562 ; -C 81 ; WX 600 ; N Q ; B 95 -138 625 580 ; -C 82 ; WX 600 ; N R ; B 38 0 598 562 ; -C 83 ; WX 600 ; N S ; B 76 -20 650 580 ; -C 84 ; WX 600 ; N T ; B 108 0 665 562 ; -C 85 ; WX 600 ; N U ; B 125 -18 702 562 ; -C 86 ; WX 600 ; N V ; B 105 -13 723 562 ; -C 87 ; WX 600 ; N W ; B 106 -13 722 562 ; -C 88 ; WX 600 ; N X ; B 23 0 675 562 ; -C 89 ; WX 600 ; N Y ; B 133 0 695 562 ; -C 90 ; WX 600 ; N Z ; B 86 0 610 562 ; -C 91 ; WX 600 ; N bracketleft ; B 246 -108 574 622 ; -C 92 ; WX 600 ; N backslash ; B 249 -80 468 629 ; -C 93 ; WX 600 ; N bracketright ; B 135 -108 463 622 ; -C 94 ; WX 600 ; N asciicircum ; B 175 354 587 622 ; -C 95 ; WX 600 ; N underscore ; B -27 -125 584 -75 ; -C 96 ; WX 600 ; N quoteleft ; B 343 328 457 562 ; -C 97 ; WX 600 ; N a ; B 76 -15 569 441 ; -C 98 ; WX 600 ; N b ; B 29 -15 625 629 ; -C 99 ; WX 600 ; N c ; B 106 -15 608 441 ; -C 100 ; WX 600 ; N d ; B 85 -15 640 629 ; -C 101 ; WX 600 ; N e ; B 106 -15 598 441 ; -C 102 ; WX 600 ; N f ; B 114 0 662 629 ; L i fi ; L l fl ; -C 103 ; WX 600 ; N g ; B 61 -157 657 441 ; -C 104 ; WX 600 ; N h ; B 33 0 592 629 ; -C 105 ; WX 600 ; N i ; B 95 0 515 657 ; -C 106 ; WX 600 ; N j ; B 52 -157 550 657 ; -C 107 ; WX 600 ; N k ; B 58 0 633 629 ; -C 108 ; WX 600 ; N l ; B 95 0 515 629 ; -C 109 ; WX 600 ; N m ; B -5 0 615 441 ; -C 110 ; WX 600 ; N n ; B 26 0 585 441 ; -C 111 ; WX 600 ; N o ; B 102 -15 588 441 ; -C 112 ; WX 600 ; N p ; B -24 -157 605 441 ; -C 113 ; WX 600 ; N q ; B 85 -157 682 441 ; -C 114 ; WX 600 ; N r ; B 60 0 636 441 ; -C 115 ; WX 600 ; N s ; B 78 -15 584 441 ; -C 116 ; WX 600 ; N t ; B 167 -15 561 561 ; -C 117 ; WX 600 ; N u ; B 101 -15 572 426 ; -C 118 ; WX 600 ; N v ; B 90 -10 681 426 ; -C 119 ; WX 600 ; N w ; B 76 -10 695 426 ; -C 120 ; WX 600 ; N x ; B 20 0 655 426 ; -C 121 ; WX 600 ; N y ; B -4 -157 683 426 ; -C 122 ; WX 600 ; N z ; B 99 0 593 426 ; -C 123 ; WX 600 ; N braceleft ; B 233 -108 569 622 ; -C 124 ; WX 600 ; N bar ; B 222 -250 485 750 ; -C 125 ; WX 600 ; N braceright ; B 140 -108 477 622 ; -C 126 ; WX 600 ; N asciitilde ; B 116 197 600 320 ; -C 161 ; WX 600 ; N exclamdown ; B 225 -157 445 430 ; -C 162 ; WX 600 ; N cent ; B 151 -49 588 614 ; -C 163 ; WX 600 ; N sterling ; B 124 -21 621 611 ; -C 164 ; WX 600 ; N fraction ; B 84 -57 646 665 ; -C 165 ; WX 600 ; N yen ; B 120 0 693 562 ; -C 166 ; WX 600 ; N florin ; B -26 -143 671 622 ; -C 167 ; WX 600 ; N section ; B 104 -78 590 580 ; -C 168 ; WX 600 ; N currency ; B 94 58 628 506 ; -C 169 ; WX 600 ; N quotesingle ; B 345 328 460 562 ; -C 170 ; WX 600 ; N quotedblleft ; B 262 328 541 562 ; -C 171 ; WX 600 ; N guillemotleft ; B 92 70 652 446 ; -C 172 ; WX 600 ; N guilsinglleft ; B 204 70 540 446 ; -C 173 ; WX 600 ; N guilsinglright ; B 170 70 506 446 ; -C 174 ; WX 600 ; N fi ; B 3 0 619 629 ; -C 175 ; WX 600 ; N fl ; B 3 0 619 629 ; -C 177 ; WX 600 ; N endash ; B 124 231 586 285 ; -C 178 ; WX 600 ; N dagger ; B 217 -78 546 580 ; -C 179 ; WX 600 ; N daggerdbl ; B 163 -78 546 580 ; -C 180 ; WX 600 ; N periodcentered ; B 275 189 434 327 ; -C 182 ; WX 600 ; N paragraph ; B 100 -78 630 562 ; -C 183 ; WX 600 ; N bullet ; B 224 130 485 383 ; -C 184 ; WX 600 ; N quotesinglbase ; B 185 -134 397 100 ; -C 185 ; WX 600 ; N quotedblbase ; B 115 -134 478 100 ; -C 186 ; WX 600 ; N quotedblright ; B 213 328 576 562 ; -C 187 ; WX 600 ; N guillemotright ; B 58 70 618 446 ; -C 188 ; WX 600 ; N ellipsis ; B 46 -15 575 111 ; -C 189 ; WX 600 ; N perthousand ; B 59 -15 627 622 ; -C 191 ; WX 600 ; N questiondown ; B 105 -157 466 430 ; -C 193 ; WX 600 ; N grave ; B 294 497 484 672 ; -C 194 ; WX 600 ; N acute ; B 348 497 612 672 ; -C 195 ; WX 600 ; N circumflex ; B 229 477 581 654 ; -C 196 ; WX 600 ; N tilde ; B 212 489 629 606 ; -C 197 ; WX 600 ; N macron ; B 232 525 600 565 ; -C 198 ; WX 600 ; N breve ; B 279 501 576 609 ; -C 199 ; WX 600 ; N dotaccent ; B 373 537 478 640 ; -C 200 ; WX 600 ; N dieresis ; B 272 537 579 640 ; -C 202 ; WX 600 ; N ring ; B 332 463 500 627 ; -C 203 ; WX 600 ; N cedilla ; B 197 -151 344 10 ; -C 205 ; WX 600 ; N hungarumlaut ; B 239 497 683 672 ; -C 206 ; WX 600 ; N ogonek ; B 189 -172 377 4 ; -C 207 ; WX 600 ; N caron ; B 262 492 614 669 ; -C 208 ; WX 600 ; N emdash ; B 49 231 661 285 ; -C 225 ; WX 600 ; N AE ; B 3 0 655 562 ; -C 227 ; WX 600 ; N ordfeminine ; B 209 249 512 580 ; -C 232 ; WX 600 ; N Lslash ; B 47 0 607 562 ; -C 233 ; WX 600 ; N Oslash ; B 94 -80 625 629 ; -C 234 ; WX 600 ; N OE ; B 59 0 672 562 ; -C 235 ; WX 600 ; N ordmasculine ; B 210 249 535 580 ; -C 241 ; WX 600 ; N ae ; B 41 -15 626 441 ; -C 245 ; WX 600 ; N dotlessi ; B 95 0 515 426 ; -C 248 ; WX 600 ; N lslash ; B 95 0 587 629 ; -C 249 ; WX 600 ; N oslash ; B 102 -80 588 506 ; -C 250 ; WX 600 ; N oe ; B 54 -15 615 441 ; -C 251 ; WX 600 ; N germandbls ; B 48 -15 617 629 ; -C -1 ; WX 600 ; N Idieresis ; B 96 0 623 753 ; -C -1 ; WX 600 ; N eacute ; B 106 -15 612 672 ; -C -1 ; WX 600 ; N abreve ; B 76 -15 576 609 ; -C -1 ; WX 600 ; N uhungarumlaut ; B 101 -15 723 672 ; -C -1 ; WX 600 ; N ecaron ; B 106 -15 614 669 ; -C -1 ; WX 600 ; N Ydieresis ; B 133 0 695 753 ; -C -1 ; WX 600 ; N divide ; B 136 48 573 467 ; -C -1 ; WX 600 ; N Yacute ; B 133 0 695 805 ; -C -1 ; WX 600 ; N Acircumflex ; B 3 0 607 787 ; -C -1 ; WX 600 ; N aacute ; B 76 -15 612 672 ; -C -1 ; WX 600 ; N Ucircumflex ; B 125 -18 702 787 ; -C -1 ; WX 600 ; N yacute ; B -4 -157 683 672 ; -C -1 ; WX 600 ; N scommaaccent ; B 78 -250 584 441 ; -C -1 ; WX 600 ; N ecircumflex ; B 106 -15 598 654 ; -C -1 ; WX 600 ; N Uring ; B 125 -18 702 760 ; -C -1 ; WX 600 ; N Udieresis ; B 125 -18 702 753 ; -C -1 ; WX 600 ; N aogonek ; B 76 -172 569 441 ; -C -1 ; WX 600 ; N Uacute ; B 125 -18 702 805 ; -C -1 ; WX 600 ; N uogonek ; B 101 -172 572 426 ; -C -1 ; WX 600 ; N Edieresis ; B 53 0 660 753 ; -C -1 ; WX 600 ; N Dcroat ; B 43 0 645 562 ; -C -1 ; WX 600 ; N commaaccent ; B 145 -250 323 -58 ; -C -1 ; WX 600 ; N copyright ; B 53 -18 667 580 ; -C -1 ; WX 600 ; N Emacron ; B 53 0 660 698 ; -C -1 ; WX 600 ; N ccaron ; B 106 -15 614 669 ; -C -1 ; WX 600 ; N aring ; B 76 -15 569 627 ; -C -1 ; WX 600 ; N Ncommaaccent ; B 7 -250 712 562 ; -C -1 ; WX 600 ; N lacute ; B 95 0 640 805 ; -C -1 ; WX 600 ; N agrave ; B 76 -15 569 672 ; -C -1 ; WX 600 ; N Tcommaaccent ; B 108 -250 665 562 ; -C -1 ; WX 600 ; N Cacute ; B 93 -18 655 805 ; -C -1 ; WX 600 ; N atilde ; B 76 -15 629 606 ; -C -1 ; WX 600 ; N Edotaccent ; B 53 0 660 753 ; -C -1 ; WX 600 ; N scaron ; B 78 -15 614 669 ; -C -1 ; WX 600 ; N scedilla ; B 78 -151 584 441 ; -C -1 ; WX 600 ; N iacute ; B 95 0 612 672 ; -C -1 ; WX 600 ; N lozenge ; B 94 0 519 706 ; -C -1 ; WX 600 ; N Rcaron ; B 38 0 642 802 ; -C -1 ; WX 600 ; N Gcommaaccent ; B 83 -250 645 580 ; -C -1 ; WX 600 ; N ucircumflex ; B 101 -15 572 654 ; -C -1 ; WX 600 ; N acircumflex ; B 76 -15 581 654 ; -C -1 ; WX 600 ; N Amacron ; B 3 0 607 698 ; -C -1 ; WX 600 ; N rcaron ; B 60 0 636 669 ; -C -1 ; WX 600 ; N ccedilla ; B 106 -151 614 441 ; -C -1 ; WX 600 ; N Zdotaccent ; B 86 0 610 753 ; -C -1 ; WX 600 ; N Thorn ; B 79 0 606 562 ; -C -1 ; WX 600 ; N Omacron ; B 94 -18 628 698 ; -C -1 ; WX 600 ; N Racute ; B 38 0 670 805 ; -C -1 ; WX 600 ; N Sacute ; B 76 -20 650 805 ; -C -1 ; WX 600 ; N dcaron ; B 85 -15 849 629 ; -C -1 ; WX 600 ; N Umacron ; B 125 -18 702 698 ; -C -1 ; WX 600 ; N uring ; B 101 -15 572 627 ; -C -1 ; WX 600 ; N threesuperior ; B 213 240 501 622 ; -C -1 ; WX 600 ; N Ograve ; B 94 -18 625 805 ; -C -1 ; WX 600 ; N Agrave ; B 3 0 607 805 ; -C -1 ; WX 600 ; N Abreve ; B 3 0 607 732 ; -C -1 ; WX 600 ; N multiply ; B 103 43 607 470 ; -C -1 ; WX 600 ; N uacute ; B 101 -15 602 672 ; -C -1 ; WX 600 ; N Tcaron ; B 108 0 665 802 ; -C -1 ; WX 600 ; N partialdiff ; B 45 -38 546 710 ; -C -1 ; WX 600 ; N ydieresis ; B -4 -157 683 620 ; -C -1 ; WX 600 ; N Nacute ; B 7 -13 712 805 ; -C -1 ; WX 600 ; N icircumflex ; B 95 0 551 654 ; -C -1 ; WX 600 ; N Ecircumflex ; B 53 0 660 787 ; -C -1 ; WX 600 ; N adieresis ; B 76 -15 575 620 ; -C -1 ; WX 600 ; N edieresis ; B 106 -15 598 620 ; -C -1 ; WX 600 ; N cacute ; B 106 -15 612 672 ; -C -1 ; WX 600 ; N nacute ; B 26 0 602 672 ; -C -1 ; WX 600 ; N umacron ; B 101 -15 600 565 ; -C -1 ; WX 600 ; N Ncaron ; B 7 -13 712 802 ; -C -1 ; WX 600 ; N Iacute ; B 96 0 640 805 ; -C -1 ; WX 600 ; N plusminus ; B 96 44 594 558 ; -C -1 ; WX 600 ; N brokenbar ; B 238 -175 469 675 ; -C -1 ; WX 600 ; N registered ; B 53 -18 667 580 ; -C -1 ; WX 600 ; N Gbreve ; B 83 -18 645 732 ; -C -1 ; WX 600 ; N Idotaccent ; B 96 0 623 753 ; -C -1 ; WX 600 ; N summation ; B 15 -10 670 706 ; -C -1 ; WX 600 ; N Egrave ; B 53 0 660 805 ; -C -1 ; WX 600 ; N racute ; B 60 0 636 672 ; -C -1 ; WX 600 ; N omacron ; B 102 -15 600 565 ; -C -1 ; WX 600 ; N Zacute ; B 86 0 670 805 ; -C -1 ; WX 600 ; N Zcaron ; B 86 0 642 802 ; -C -1 ; WX 600 ; N greaterequal ; B 98 0 594 710 ; -C -1 ; WX 600 ; N Eth ; B 43 0 645 562 ; -C -1 ; WX 600 ; N Ccedilla ; B 93 -151 658 580 ; -C -1 ; WX 600 ; N lcommaaccent ; B 95 -250 515 629 ; -C -1 ; WX 600 ; N tcaron ; B 167 -15 587 717 ; -C -1 ; WX 600 ; N eogonek ; B 106 -172 598 441 ; -C -1 ; WX 600 ; N Uogonek ; B 124 -172 702 562 ; -C -1 ; WX 600 ; N Aacute ; B 3 0 660 805 ; -C -1 ; WX 600 ; N Adieresis ; B 3 0 607 753 ; -C -1 ; WX 600 ; N egrave ; B 106 -15 598 672 ; -C -1 ; WX 600 ; N zacute ; B 99 0 612 672 ; -C -1 ; WX 600 ; N iogonek ; B 95 -172 515 657 ; -C -1 ; WX 600 ; N Oacute ; B 94 -18 640 805 ; -C -1 ; WX 600 ; N oacute ; B 102 -15 612 672 ; -C -1 ; WX 600 ; N amacron ; B 76 -15 600 565 ; -C -1 ; WX 600 ; N sacute ; B 78 -15 612 672 ; -C -1 ; WX 600 ; N idieresis ; B 95 0 545 620 ; -C -1 ; WX 600 ; N Ocircumflex ; B 94 -18 625 787 ; -C -1 ; WX 600 ; N Ugrave ; B 125 -18 702 805 ; -C -1 ; WX 600 ; N Delta ; B 6 0 598 688 ; -C -1 ; WX 600 ; N thorn ; B -24 -157 605 629 ; -C -1 ; WX 600 ; N twosuperior ; B 230 249 535 622 ; -C -1 ; WX 600 ; N Odieresis ; B 94 -18 625 753 ; -C -1 ; WX 600 ; N mu ; B 72 -157 572 426 ; -C -1 ; WX 600 ; N igrave ; B 95 0 515 672 ; -C -1 ; WX 600 ; N ohungarumlaut ; B 102 -15 723 672 ; -C -1 ; WX 600 ; N Eogonek ; B 53 -172 660 562 ; -C -1 ; WX 600 ; N dcroat ; B 85 -15 704 629 ; -C -1 ; WX 600 ; N threequarters ; B 73 -56 659 666 ; -C -1 ; WX 600 ; N Scedilla ; B 76 -151 650 580 ; -C -1 ; WX 600 ; N lcaron ; B 95 0 667 629 ; -C -1 ; WX 600 ; N Kcommaaccent ; B 38 -250 671 562 ; -C -1 ; WX 600 ; N Lacute ; B 47 0 607 805 ; -C -1 ; WX 600 ; N trademark ; B 75 263 742 562 ; -C -1 ; WX 600 ; N edotaccent ; B 106 -15 598 620 ; -C -1 ; WX 600 ; N Igrave ; B 96 0 623 805 ; -C -1 ; WX 600 ; N Imacron ; B 96 0 628 698 ; -C -1 ; WX 600 ; N Lcaron ; B 47 0 632 562 ; -C -1 ; WX 600 ; N onehalf ; B 65 -57 669 665 ; -C -1 ; WX 600 ; N lessequal ; B 98 0 645 710 ; -C -1 ; WX 600 ; N ocircumflex ; B 102 -15 588 654 ; -C -1 ; WX 600 ; N ntilde ; B 26 0 629 606 ; -C -1 ; WX 600 ; N Uhungarumlaut ; B 125 -18 761 805 ; -C -1 ; WX 600 ; N Eacute ; B 53 0 670 805 ; -C -1 ; WX 600 ; N emacron ; B 106 -15 600 565 ; -C -1 ; WX 600 ; N gbreve ; B 61 -157 657 609 ; -C -1 ; WX 600 ; N onequarter ; B 65 -57 674 665 ; -C -1 ; WX 600 ; N Scaron ; B 76 -20 672 802 ; -C -1 ; WX 600 ; N Scommaaccent ; B 76 -250 650 580 ; -C -1 ; WX 600 ; N Ohungarumlaut ; B 94 -18 751 805 ; -C -1 ; WX 600 ; N degree ; B 214 269 576 622 ; -C -1 ; WX 600 ; N ograve ; B 102 -15 588 672 ; -C -1 ; WX 600 ; N Ccaron ; B 93 -18 672 802 ; -C -1 ; WX 600 ; N ugrave ; B 101 -15 572 672 ; -C -1 ; WX 600 ; N radical ; B 85 -15 765 792 ; -C -1 ; WX 600 ; N Dcaron ; B 43 0 645 802 ; -C -1 ; WX 600 ; N rcommaaccent ; B 60 -250 636 441 ; -C -1 ; WX 600 ; N Ntilde ; B 7 -13 712 729 ; -C -1 ; WX 600 ; N otilde ; B 102 -15 629 606 ; -C -1 ; WX 600 ; N Rcommaaccent ; B 38 -250 598 562 ; -C -1 ; WX 600 ; N Lcommaaccent ; B 47 -250 607 562 ; -C -1 ; WX 600 ; N Atilde ; B 3 0 655 729 ; -C -1 ; WX 600 ; N Aogonek ; B 3 -172 607 562 ; -C -1 ; WX 600 ; N Aring ; B 3 0 607 750 ; -C -1 ; WX 600 ; N Otilde ; B 94 -18 655 729 ; -C -1 ; WX 600 ; N zdotaccent ; B 99 0 593 620 ; -C -1 ; WX 600 ; N Ecaron ; B 53 0 660 802 ; -C -1 ; WX 600 ; N Iogonek ; B 96 -172 623 562 ; -C -1 ; WX 600 ; N kcommaaccent ; B 58 -250 633 629 ; -C -1 ; WX 600 ; N minus ; B 129 232 580 283 ; -C -1 ; WX 600 ; N Icircumflex ; B 96 0 623 787 ; -C -1 ; WX 600 ; N ncaron ; B 26 0 614 669 ; -C -1 ; WX 600 ; N tcommaaccent ; B 165 -250 561 561 ; -C -1 ; WX 600 ; N logicalnot ; B 155 108 591 369 ; -C -1 ; WX 600 ; N odieresis ; B 102 -15 588 620 ; -C -1 ; WX 600 ; N udieresis ; B 101 -15 575 620 ; -C -1 ; WX 600 ; N notequal ; B 43 -16 621 529 ; -C -1 ; WX 600 ; N gcommaaccent ; B 61 -157 657 708 ; -C -1 ; WX 600 ; N eth ; B 102 -15 639 629 ; -C -1 ; WX 600 ; N zcaron ; B 99 0 624 669 ; -C -1 ; WX 600 ; N ncommaaccent ; B 26 -250 585 441 ; -C -1 ; WX 600 ; N onesuperior ; B 231 249 491 622 ; -C -1 ; WX 600 ; N imacron ; B 95 0 543 565 ; -C -1 ; WX 600 ; N Euro ; B 0 0 0 0 ; -EndCharMetrics -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Courier.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Courier.afm deleted file mode 100644 index 2f7be81..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Courier.afm +++ /dev/null @@ -1,342 +0,0 @@ -StartFontMetrics 4.1 -Comment Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Thu May 1 17:27:09 1997 -Comment UniqueID 43050 -Comment VMusage 39754 50779 -FontName Courier -FullName Courier -FamilyName Courier -Weight Medium -ItalicAngle 0 -IsFixedPitch true -CharacterSet ExtendedRoman -FontBBox -23 -250 715 805 -UnderlinePosition -100 -UnderlineThickness 50 -Version 003.000 -Notice Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. -EncodingScheme AdobeStandardEncoding -CapHeight 562 -XHeight 426 -Ascender 629 -Descender -157 -StdHW 51 -StdVW 51 -StartCharMetrics 315 -C 32 ; WX 600 ; N space ; B 0 0 0 0 ; -C 33 ; WX 600 ; N exclam ; B 236 -15 364 572 ; -C 34 ; WX 600 ; N quotedbl ; B 187 328 413 562 ; -C 35 ; WX 600 ; N numbersign ; B 93 -32 507 639 ; -C 36 ; WX 600 ; N dollar ; B 105 -126 496 662 ; -C 37 ; WX 600 ; N percent ; B 81 -15 518 622 ; -C 38 ; WX 600 ; N ampersand ; B 63 -15 538 543 ; -C 39 ; WX 600 ; N quoteright ; B 213 328 376 562 ; -C 40 ; WX 600 ; N parenleft ; B 269 -108 440 622 ; -C 41 ; WX 600 ; N parenright ; B 160 -108 331 622 ; -C 42 ; WX 600 ; N asterisk ; B 116 257 484 607 ; -C 43 ; WX 600 ; N plus ; B 80 44 520 470 ; -C 44 ; WX 600 ; N comma ; B 181 -112 344 122 ; -C 45 ; WX 600 ; N hyphen ; B 103 231 497 285 ; -C 46 ; WX 600 ; N period ; B 229 -15 371 109 ; -C 47 ; WX 600 ; N slash ; B 125 -80 475 629 ; -C 48 ; WX 600 ; N zero ; B 106 -15 494 622 ; -C 49 ; WX 600 ; N one ; B 96 0 505 622 ; -C 50 ; WX 600 ; N two ; B 70 0 471 622 ; -C 51 ; WX 600 ; N three ; B 75 -15 466 622 ; -C 52 ; WX 600 ; N four ; B 78 0 500 622 ; -C 53 ; WX 600 ; N five ; B 92 -15 497 607 ; -C 54 ; WX 600 ; N six ; B 111 -15 497 622 ; -C 55 ; WX 600 ; N seven ; B 82 0 483 607 ; -C 56 ; WX 600 ; N eight ; B 102 -15 498 622 ; -C 57 ; WX 600 ; N nine ; B 96 -15 489 622 ; -C 58 ; WX 600 ; N colon ; B 229 -15 371 385 ; -C 59 ; WX 600 ; N semicolon ; B 181 -112 371 385 ; -C 60 ; WX 600 ; N less ; B 41 42 519 472 ; -C 61 ; WX 600 ; N equal ; B 80 138 520 376 ; -C 62 ; WX 600 ; N greater ; B 66 42 544 472 ; -C 63 ; WX 600 ; N question ; B 129 -15 492 572 ; -C 64 ; WX 600 ; N at ; B 77 -15 533 622 ; -C 65 ; WX 600 ; N A ; B 3 0 597 562 ; -C 66 ; WX 600 ; N B ; B 43 0 559 562 ; -C 67 ; WX 600 ; N C ; B 41 -18 540 580 ; -C 68 ; WX 600 ; N D ; B 43 0 574 562 ; -C 69 ; WX 600 ; N E ; B 53 0 550 562 ; -C 70 ; WX 600 ; N F ; B 53 0 545 562 ; -C 71 ; WX 600 ; N G ; B 31 -18 575 580 ; -C 72 ; WX 600 ; N H ; B 32 0 568 562 ; -C 73 ; WX 600 ; N I ; B 96 0 504 562 ; -C 74 ; WX 600 ; N J ; B 34 -18 566 562 ; -C 75 ; WX 600 ; N K ; B 38 0 582 562 ; -C 76 ; WX 600 ; N L ; B 47 0 554 562 ; -C 77 ; WX 600 ; N M ; B 4 0 596 562 ; -C 78 ; WX 600 ; N N ; B 7 -13 593 562 ; -C 79 ; WX 600 ; N O ; B 43 -18 557 580 ; -C 80 ; WX 600 ; N P ; B 79 0 558 562 ; -C 81 ; WX 600 ; N Q ; B 43 -138 557 580 ; -C 82 ; WX 600 ; N R ; B 38 0 588 562 ; -C 83 ; WX 600 ; N S ; B 72 -20 529 580 ; -C 84 ; WX 600 ; N T ; B 38 0 563 562 ; -C 85 ; WX 600 ; N U ; B 17 -18 583 562 ; -C 86 ; WX 600 ; N V ; B -4 -13 604 562 ; -C 87 ; WX 600 ; N W ; B -3 -13 603 562 ; -C 88 ; WX 600 ; N X ; B 23 0 577 562 ; -C 89 ; WX 600 ; N Y ; B 24 0 576 562 ; -C 90 ; WX 600 ; N Z ; B 86 0 514 562 ; -C 91 ; WX 600 ; N bracketleft ; B 269 -108 442 622 ; -C 92 ; WX 600 ; N backslash ; B 118 -80 482 629 ; -C 93 ; WX 600 ; N bracketright ; B 158 -108 331 622 ; -C 94 ; WX 600 ; N asciicircum ; B 94 354 506 622 ; -C 95 ; WX 600 ; N underscore ; B 0 -125 600 -75 ; -C 96 ; WX 600 ; N quoteleft ; B 224 328 387 562 ; -C 97 ; WX 600 ; N a ; B 53 -15 559 441 ; -C 98 ; WX 600 ; N b ; B 14 -15 575 629 ; -C 99 ; WX 600 ; N c ; B 66 -15 529 441 ; -C 100 ; WX 600 ; N d ; B 45 -15 591 629 ; -C 101 ; WX 600 ; N e ; B 66 -15 548 441 ; -C 102 ; WX 600 ; N f ; B 114 0 531 629 ; L i fi ; L l fl ; -C 103 ; WX 600 ; N g ; B 45 -157 566 441 ; -C 104 ; WX 600 ; N h ; B 18 0 582 629 ; -C 105 ; WX 600 ; N i ; B 95 0 505 657 ; -C 106 ; WX 600 ; N j ; B 82 -157 410 657 ; -C 107 ; WX 600 ; N k ; B 43 0 580 629 ; -C 108 ; WX 600 ; N l ; B 95 0 505 629 ; -C 109 ; WX 600 ; N m ; B -5 0 605 441 ; -C 110 ; WX 600 ; N n ; B 26 0 575 441 ; -C 111 ; WX 600 ; N o ; B 62 -15 538 441 ; -C 112 ; WX 600 ; N p ; B 9 -157 555 441 ; -C 113 ; WX 600 ; N q ; B 45 -157 591 441 ; -C 114 ; WX 600 ; N r ; B 60 0 559 441 ; -C 115 ; WX 600 ; N s ; B 80 -15 513 441 ; -C 116 ; WX 600 ; N t ; B 87 -15 530 561 ; -C 117 ; WX 600 ; N u ; B 21 -15 562 426 ; -C 118 ; WX 600 ; N v ; B 10 -10 590 426 ; -C 119 ; WX 600 ; N w ; B -4 -10 604 426 ; -C 120 ; WX 600 ; N x ; B 20 0 580 426 ; -C 121 ; WX 600 ; N y ; B 7 -157 592 426 ; -C 122 ; WX 600 ; N z ; B 99 0 502 426 ; -C 123 ; WX 600 ; N braceleft ; B 182 -108 437 622 ; -C 124 ; WX 600 ; N bar ; B 275 -250 326 750 ; -C 125 ; WX 600 ; N braceright ; B 163 -108 418 622 ; -C 126 ; WX 600 ; N asciitilde ; B 63 197 540 320 ; -C 161 ; WX 600 ; N exclamdown ; B 236 -157 364 430 ; -C 162 ; WX 600 ; N cent ; B 96 -49 500 614 ; -C 163 ; WX 600 ; N sterling ; B 84 -21 521 611 ; -C 164 ; WX 600 ; N fraction ; B 92 -57 509 665 ; -C 165 ; WX 600 ; N yen ; B 26 0 574 562 ; -C 166 ; WX 600 ; N florin ; B 4 -143 539 622 ; -C 167 ; WX 600 ; N section ; B 113 -78 488 580 ; -C 168 ; WX 600 ; N currency ; B 73 58 527 506 ; -C 169 ; WX 600 ; N quotesingle ; B 259 328 341 562 ; -C 170 ; WX 600 ; N quotedblleft ; B 143 328 471 562 ; -C 171 ; WX 600 ; N guillemotleft ; B 37 70 563 446 ; -C 172 ; WX 600 ; N guilsinglleft ; B 149 70 451 446 ; -C 173 ; WX 600 ; N guilsinglright ; B 149 70 451 446 ; -C 174 ; WX 600 ; N fi ; B 3 0 597 629 ; -C 175 ; WX 600 ; N fl ; B 3 0 597 629 ; -C 177 ; WX 600 ; N endash ; B 75 231 525 285 ; -C 178 ; WX 600 ; N dagger ; B 141 -78 459 580 ; -C 179 ; WX 600 ; N daggerdbl ; B 141 -78 459 580 ; -C 180 ; WX 600 ; N periodcentered ; B 222 189 378 327 ; -C 182 ; WX 600 ; N paragraph ; B 50 -78 511 562 ; -C 183 ; WX 600 ; N bullet ; B 172 130 428 383 ; -C 184 ; WX 600 ; N quotesinglbase ; B 213 -134 376 100 ; -C 185 ; WX 600 ; N quotedblbase ; B 143 -134 457 100 ; -C 186 ; WX 600 ; N quotedblright ; B 143 328 457 562 ; -C 187 ; WX 600 ; N guillemotright ; B 37 70 563 446 ; -C 188 ; WX 600 ; N ellipsis ; B 37 -15 563 111 ; -C 189 ; WX 600 ; N perthousand ; B 3 -15 600 622 ; -C 191 ; WX 600 ; N questiondown ; B 108 -157 471 430 ; -C 193 ; WX 600 ; N grave ; B 151 497 378 672 ; -C 194 ; WX 600 ; N acute ; B 242 497 469 672 ; -C 195 ; WX 600 ; N circumflex ; B 124 477 476 654 ; -C 196 ; WX 600 ; N tilde ; B 105 489 503 606 ; -C 197 ; WX 600 ; N macron ; B 120 525 480 565 ; -C 198 ; WX 600 ; N breve ; B 153 501 447 609 ; -C 199 ; WX 600 ; N dotaccent ; B 249 537 352 640 ; -C 200 ; WX 600 ; N dieresis ; B 148 537 453 640 ; -C 202 ; WX 600 ; N ring ; B 218 463 382 627 ; -C 203 ; WX 600 ; N cedilla ; B 224 -151 362 10 ; -C 205 ; WX 600 ; N hungarumlaut ; B 133 497 540 672 ; -C 206 ; WX 600 ; N ogonek ; B 211 -172 407 4 ; -C 207 ; WX 600 ; N caron ; B 124 492 476 669 ; -C 208 ; WX 600 ; N emdash ; B 0 231 600 285 ; -C 225 ; WX 600 ; N AE ; B 3 0 550 562 ; -C 227 ; WX 600 ; N ordfeminine ; B 156 249 442 580 ; -C 232 ; WX 600 ; N Lslash ; B 47 0 554 562 ; -C 233 ; WX 600 ; N Oslash ; B 43 -80 557 629 ; -C 234 ; WX 600 ; N OE ; B 7 0 567 562 ; -C 235 ; WX 600 ; N ordmasculine ; B 157 249 443 580 ; -C 241 ; WX 600 ; N ae ; B 19 -15 570 441 ; -C 245 ; WX 600 ; N dotlessi ; B 95 0 505 426 ; -C 248 ; WX 600 ; N lslash ; B 95 0 505 629 ; -C 249 ; WX 600 ; N oslash ; B 62 -80 538 506 ; -C 250 ; WX 600 ; N oe ; B 19 -15 559 441 ; -C 251 ; WX 600 ; N germandbls ; B 48 -15 588 629 ; -C -1 ; WX 600 ; N Idieresis ; B 96 0 504 753 ; -C -1 ; WX 600 ; N eacute ; B 66 -15 548 672 ; -C -1 ; WX 600 ; N abreve ; B 53 -15 559 609 ; -C -1 ; WX 600 ; N uhungarumlaut ; B 21 -15 580 672 ; -C -1 ; WX 600 ; N ecaron ; B 66 -15 548 669 ; -C -1 ; WX 600 ; N Ydieresis ; B 24 0 576 753 ; -C -1 ; WX 600 ; N divide ; B 87 48 513 467 ; -C -1 ; WX 600 ; N Yacute ; B 24 0 576 805 ; -C -1 ; WX 600 ; N Acircumflex ; B 3 0 597 787 ; -C -1 ; WX 600 ; N aacute ; B 53 -15 559 672 ; -C -1 ; WX 600 ; N Ucircumflex ; B 17 -18 583 787 ; -C -1 ; WX 600 ; N yacute ; B 7 -157 592 672 ; -C -1 ; WX 600 ; N scommaaccent ; B 80 -250 513 441 ; -C -1 ; WX 600 ; N ecircumflex ; B 66 -15 548 654 ; -C -1 ; WX 600 ; N Uring ; B 17 -18 583 760 ; -C -1 ; WX 600 ; N Udieresis ; B 17 -18 583 753 ; -C -1 ; WX 600 ; N aogonek ; B 53 -172 587 441 ; -C -1 ; WX 600 ; N Uacute ; B 17 -18 583 805 ; -C -1 ; WX 600 ; N uogonek ; B 21 -172 590 426 ; -C -1 ; WX 600 ; N Edieresis ; B 53 0 550 753 ; -C -1 ; WX 600 ; N Dcroat ; B 30 0 574 562 ; -C -1 ; WX 600 ; N commaaccent ; B 198 -250 335 -58 ; -C -1 ; WX 600 ; N copyright ; B 0 -18 600 580 ; -C -1 ; WX 600 ; N Emacron ; B 53 0 550 698 ; -C -1 ; WX 600 ; N ccaron ; B 66 -15 529 669 ; -C -1 ; WX 600 ; N aring ; B 53 -15 559 627 ; -C -1 ; WX 600 ; N Ncommaaccent ; B 7 -250 593 562 ; -C -1 ; WX 600 ; N lacute ; B 95 0 505 805 ; -C -1 ; WX 600 ; N agrave ; B 53 -15 559 672 ; -C -1 ; WX 600 ; N Tcommaaccent ; B 38 -250 563 562 ; -C -1 ; WX 600 ; N Cacute ; B 41 -18 540 805 ; -C -1 ; WX 600 ; N atilde ; B 53 -15 559 606 ; -C -1 ; WX 600 ; N Edotaccent ; B 53 0 550 753 ; -C -1 ; WX 600 ; N scaron ; B 80 -15 513 669 ; -C -1 ; WX 600 ; N scedilla ; B 80 -151 513 441 ; -C -1 ; WX 600 ; N iacute ; B 95 0 505 672 ; -C -1 ; WX 600 ; N lozenge ; B 18 0 443 706 ; -C -1 ; WX 600 ; N Rcaron ; B 38 0 588 802 ; -C -1 ; WX 600 ; N Gcommaaccent ; B 31 -250 575 580 ; -C -1 ; WX 600 ; N ucircumflex ; B 21 -15 562 654 ; -C -1 ; WX 600 ; N acircumflex ; B 53 -15 559 654 ; -C -1 ; WX 600 ; N Amacron ; B 3 0 597 698 ; -C -1 ; WX 600 ; N rcaron ; B 60 0 559 669 ; -C -1 ; WX 600 ; N ccedilla ; B 66 -151 529 441 ; -C -1 ; WX 600 ; N Zdotaccent ; B 86 0 514 753 ; -C -1 ; WX 600 ; N Thorn ; B 79 0 538 562 ; -C -1 ; WX 600 ; N Omacron ; B 43 -18 557 698 ; -C -1 ; WX 600 ; N Racute ; B 38 0 588 805 ; -C -1 ; WX 600 ; N Sacute ; B 72 -20 529 805 ; -C -1 ; WX 600 ; N dcaron ; B 45 -15 715 629 ; -C -1 ; WX 600 ; N Umacron ; B 17 -18 583 698 ; -C -1 ; WX 600 ; N uring ; B 21 -15 562 627 ; -C -1 ; WX 600 ; N threesuperior ; B 155 240 406 622 ; -C -1 ; WX 600 ; N Ograve ; B 43 -18 557 805 ; -C -1 ; WX 600 ; N Agrave ; B 3 0 597 805 ; -C -1 ; WX 600 ; N Abreve ; B 3 0 597 732 ; -C -1 ; WX 600 ; N multiply ; B 87 43 515 470 ; -C -1 ; WX 600 ; N uacute ; B 21 -15 562 672 ; -C -1 ; WX 600 ; N Tcaron ; B 38 0 563 802 ; -C -1 ; WX 600 ; N partialdiff ; B 17 -38 459 710 ; -C -1 ; WX 600 ; N ydieresis ; B 7 -157 592 620 ; -C -1 ; WX 600 ; N Nacute ; B 7 -13 593 805 ; -C -1 ; WX 600 ; N icircumflex ; B 94 0 505 654 ; -C -1 ; WX 600 ; N Ecircumflex ; B 53 0 550 787 ; -C -1 ; WX 600 ; N adieresis ; B 53 -15 559 620 ; -C -1 ; WX 600 ; N edieresis ; B 66 -15 548 620 ; -C -1 ; WX 600 ; N cacute ; B 66 -15 529 672 ; -C -1 ; WX 600 ; N nacute ; B 26 0 575 672 ; -C -1 ; WX 600 ; N umacron ; B 21 -15 562 565 ; -C -1 ; WX 600 ; N Ncaron ; B 7 -13 593 802 ; -C -1 ; WX 600 ; N Iacute ; B 96 0 504 805 ; -C -1 ; WX 600 ; N plusminus ; B 87 44 513 558 ; -C -1 ; WX 600 ; N brokenbar ; B 275 -175 326 675 ; -C -1 ; WX 600 ; N registered ; B 0 -18 600 580 ; -C -1 ; WX 600 ; N Gbreve ; B 31 -18 575 732 ; -C -1 ; WX 600 ; N Idotaccent ; B 96 0 504 753 ; -C -1 ; WX 600 ; N summation ; B 15 -10 585 706 ; -C -1 ; WX 600 ; N Egrave ; B 53 0 550 805 ; -C -1 ; WX 600 ; N racute ; B 60 0 559 672 ; -C -1 ; WX 600 ; N omacron ; B 62 -15 538 565 ; -C -1 ; WX 600 ; N Zacute ; B 86 0 514 805 ; -C -1 ; WX 600 ; N Zcaron ; B 86 0 514 802 ; -C -1 ; WX 600 ; N greaterequal ; B 98 0 502 710 ; -C -1 ; WX 600 ; N Eth ; B 30 0 574 562 ; -C -1 ; WX 600 ; N Ccedilla ; B 41 -151 540 580 ; -C -1 ; WX 600 ; N lcommaaccent ; B 95 -250 505 629 ; -C -1 ; WX 600 ; N tcaron ; B 87 -15 530 717 ; -C -1 ; WX 600 ; N eogonek ; B 66 -172 548 441 ; -C -1 ; WX 600 ; N Uogonek ; B 17 -172 583 562 ; -C -1 ; WX 600 ; N Aacute ; B 3 0 597 805 ; -C -1 ; WX 600 ; N Adieresis ; B 3 0 597 753 ; -C -1 ; WX 600 ; N egrave ; B 66 -15 548 672 ; -C -1 ; WX 600 ; N zacute ; B 99 0 502 672 ; -C -1 ; WX 600 ; N iogonek ; B 95 -172 505 657 ; -C -1 ; WX 600 ; N Oacute ; B 43 -18 557 805 ; -C -1 ; WX 600 ; N oacute ; B 62 -15 538 672 ; -C -1 ; WX 600 ; N amacron ; B 53 -15 559 565 ; -C -1 ; WX 600 ; N sacute ; B 80 -15 513 672 ; -C -1 ; WX 600 ; N idieresis ; B 95 0 505 620 ; -C -1 ; WX 600 ; N Ocircumflex ; B 43 -18 557 787 ; -C -1 ; WX 600 ; N Ugrave ; B 17 -18 583 805 ; -C -1 ; WX 600 ; N Delta ; B 6 0 598 688 ; -C -1 ; WX 600 ; N thorn ; B -6 -157 555 629 ; -C -1 ; WX 600 ; N twosuperior ; B 177 249 424 622 ; -C -1 ; WX 600 ; N Odieresis ; B 43 -18 557 753 ; -C -1 ; WX 600 ; N mu ; B 21 -157 562 426 ; -C -1 ; WX 600 ; N igrave ; B 95 0 505 672 ; -C -1 ; WX 600 ; N ohungarumlaut ; B 62 -15 580 672 ; -C -1 ; WX 600 ; N Eogonek ; B 53 -172 561 562 ; -C -1 ; WX 600 ; N dcroat ; B 45 -15 591 629 ; -C -1 ; WX 600 ; N threequarters ; B 8 -56 593 666 ; -C -1 ; WX 600 ; N Scedilla ; B 72 -151 529 580 ; -C -1 ; WX 600 ; N lcaron ; B 95 0 533 629 ; -C -1 ; WX 600 ; N Kcommaaccent ; B 38 -250 582 562 ; -C -1 ; WX 600 ; N Lacute ; B 47 0 554 805 ; -C -1 ; WX 600 ; N trademark ; B -23 263 623 562 ; -C -1 ; WX 600 ; N edotaccent ; B 66 -15 548 620 ; -C -1 ; WX 600 ; N Igrave ; B 96 0 504 805 ; -C -1 ; WX 600 ; N Imacron ; B 96 0 504 698 ; -C -1 ; WX 600 ; N Lcaron ; B 47 0 554 562 ; -C -1 ; WX 600 ; N onehalf ; B 0 -57 611 665 ; -C -1 ; WX 600 ; N lessequal ; B 98 0 502 710 ; -C -1 ; WX 600 ; N ocircumflex ; B 62 -15 538 654 ; -C -1 ; WX 600 ; N ntilde ; B 26 0 575 606 ; -C -1 ; WX 600 ; N Uhungarumlaut ; B 17 -18 590 805 ; -C -1 ; WX 600 ; N Eacute ; B 53 0 550 805 ; -C -1 ; WX 600 ; N emacron ; B 66 -15 548 565 ; -C -1 ; WX 600 ; N gbreve ; B 45 -157 566 609 ; -C -1 ; WX 600 ; N onequarter ; B 0 -57 600 665 ; -C -1 ; WX 600 ; N Scaron ; B 72 -20 529 802 ; -C -1 ; WX 600 ; N Scommaaccent ; B 72 -250 529 580 ; -C -1 ; WX 600 ; N Ohungarumlaut ; B 43 -18 580 805 ; -C -1 ; WX 600 ; N degree ; B 123 269 477 622 ; -C -1 ; WX 600 ; N ograve ; B 62 -15 538 672 ; -C -1 ; WX 600 ; N Ccaron ; B 41 -18 540 802 ; -C -1 ; WX 600 ; N ugrave ; B 21 -15 562 672 ; -C -1 ; WX 600 ; N radical ; B 3 -15 597 792 ; -C -1 ; WX 600 ; N Dcaron ; B 43 0 574 802 ; -C -1 ; WX 600 ; N rcommaaccent ; B 60 -250 559 441 ; -C -1 ; WX 600 ; N Ntilde ; B 7 -13 593 729 ; -C -1 ; WX 600 ; N otilde ; B 62 -15 538 606 ; -C -1 ; WX 600 ; N Rcommaaccent ; B 38 -250 588 562 ; -C -1 ; WX 600 ; N Lcommaaccent ; B 47 -250 554 562 ; -C -1 ; WX 600 ; N Atilde ; B 3 0 597 729 ; -C -1 ; WX 600 ; N Aogonek ; B 3 -172 608 562 ; -C -1 ; WX 600 ; N Aring ; B 3 0 597 750 ; -C -1 ; WX 600 ; N Otilde ; B 43 -18 557 729 ; -C -1 ; WX 600 ; N zdotaccent ; B 99 0 502 620 ; -C -1 ; WX 600 ; N Ecaron ; B 53 0 550 802 ; -C -1 ; WX 600 ; N Iogonek ; B 96 -172 504 562 ; -C -1 ; WX 600 ; N kcommaaccent ; B 43 -250 580 629 ; -C -1 ; WX 600 ; N minus ; B 80 232 520 283 ; -C -1 ; WX 600 ; N Icircumflex ; B 96 0 504 787 ; -C -1 ; WX 600 ; N ncaron ; B 26 0 575 669 ; -C -1 ; WX 600 ; N tcommaaccent ; B 87 -250 530 561 ; -C -1 ; WX 600 ; N logicalnot ; B 87 108 513 369 ; -C -1 ; WX 600 ; N odieresis ; B 62 -15 538 620 ; -C -1 ; WX 600 ; N udieresis ; B 21 -15 562 620 ; -C -1 ; WX 600 ; N notequal ; B 15 -16 540 529 ; -C -1 ; WX 600 ; N gcommaaccent ; B 45 -157 566 708 ; -C -1 ; WX 600 ; N eth ; B 62 -15 538 629 ; -C -1 ; WX 600 ; N zcaron ; B 99 0 502 669 ; -C -1 ; WX 600 ; N ncommaaccent ; B 26 -250 575 441 ; -C -1 ; WX 600 ; N onesuperior ; B 172 249 428 622 ; -C -1 ; WX 600 ; N imacron ; B 95 0 505 565 ; -C -1 ; WX 600 ; N Euro ; B 0 0 0 0 ; -EndCharMetrics -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Helvetica-Bold.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Helvetica-Bold.afm deleted file mode 100644 index 837c594..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Helvetica-Bold.afm +++ /dev/null @@ -1,2827 +0,0 @@ -StartFontMetrics 4.1 -Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Thu May 1 12:43:52 1997 -Comment UniqueID 43052 -Comment VMusage 37169 48194 -FontName Helvetica-Bold -FullName Helvetica Bold -FamilyName Helvetica -Weight Bold -ItalicAngle 0 -IsFixedPitch false -CharacterSet ExtendedRoman -FontBBox -170 -228 1003 962 -UnderlinePosition -100 -UnderlineThickness 50 -Version 002.000 -Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved.Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 718 -XHeight 532 -Ascender 718 -Descender -207 -StdHW 118 -StdVW 140 -StartCharMetrics 315 -C 32 ; WX 278 ; N space ; B 0 0 0 0 ; -C 33 ; WX 333 ; N exclam ; B 90 0 244 718 ; -C 34 ; WX 474 ; N quotedbl ; B 98 447 376 718 ; -C 35 ; WX 556 ; N numbersign ; B 18 0 538 698 ; -C 36 ; WX 556 ; N dollar ; B 30 -115 523 775 ; -C 37 ; WX 889 ; N percent ; B 28 -19 861 710 ; -C 38 ; WX 722 ; N ampersand ; B 54 -19 701 718 ; -C 39 ; WX 278 ; N quoteright ; B 69 445 209 718 ; -C 40 ; WX 333 ; N parenleft ; B 35 -208 314 734 ; -C 41 ; WX 333 ; N parenright ; B 19 -208 298 734 ; -C 42 ; WX 389 ; N asterisk ; B 27 387 362 718 ; -C 43 ; WX 584 ; N plus ; B 40 0 544 506 ; -C 44 ; WX 278 ; N comma ; B 64 -168 214 146 ; -C 45 ; WX 333 ; N hyphen ; B 27 215 306 345 ; -C 46 ; WX 278 ; N period ; B 64 0 214 146 ; -C 47 ; WX 278 ; N slash ; B -33 -19 311 737 ; -C 48 ; WX 556 ; N zero ; B 32 -19 524 710 ; -C 49 ; WX 556 ; N one ; B 69 0 378 710 ; -C 50 ; WX 556 ; N two ; B 26 0 511 710 ; -C 51 ; WX 556 ; N three ; B 27 -19 516 710 ; -C 52 ; WX 556 ; N four ; B 27 0 526 710 ; -C 53 ; WX 556 ; N five ; B 27 -19 516 698 ; -C 54 ; WX 556 ; N six ; B 31 -19 520 710 ; -C 55 ; WX 556 ; N seven ; B 25 0 528 698 ; -C 56 ; WX 556 ; N eight ; B 32 -19 524 710 ; -C 57 ; WX 556 ; N nine ; B 30 -19 522 710 ; -C 58 ; WX 333 ; N colon ; B 92 0 242 512 ; -C 59 ; WX 333 ; N semicolon ; B 92 -168 242 512 ; -C 60 ; WX 584 ; N less ; B 38 -8 546 514 ; -C 61 ; WX 584 ; N equal ; B 40 87 544 419 ; -C 62 ; WX 584 ; N greater ; B 38 -8 546 514 ; -C 63 ; WX 611 ; N question ; B 60 0 556 727 ; -C 64 ; WX 975 ; N at ; B 118 -19 856 737 ; -C 65 ; WX 722 ; N A ; B 20 0 702 718 ; -C 66 ; WX 722 ; N B ; B 76 0 669 718 ; -C 67 ; WX 722 ; N C ; B 44 -19 684 737 ; -C 68 ; WX 722 ; N D ; B 76 0 685 718 ; -C 69 ; WX 667 ; N E ; B 76 0 621 718 ; -C 70 ; WX 611 ; N F ; B 76 0 587 718 ; -C 71 ; WX 778 ; N G ; B 44 -19 713 737 ; -C 72 ; WX 722 ; N H ; B 71 0 651 718 ; -C 73 ; WX 278 ; N I ; B 64 0 214 718 ; -C 74 ; WX 556 ; N J ; B 22 -18 484 718 ; -C 75 ; WX 722 ; N K ; B 87 0 722 718 ; -C 76 ; WX 611 ; N L ; B 76 0 583 718 ; -C 77 ; WX 833 ; N M ; B 69 0 765 718 ; -C 78 ; WX 722 ; N N ; B 69 0 654 718 ; -C 79 ; WX 778 ; N O ; B 44 -19 734 737 ; -C 80 ; WX 667 ; N P ; B 76 0 627 718 ; -C 81 ; WX 778 ; N Q ; B 44 -52 737 737 ; -C 82 ; WX 722 ; N R ; B 76 0 677 718 ; -C 83 ; WX 667 ; N S ; B 39 -19 629 737 ; -C 84 ; WX 611 ; N T ; B 14 0 598 718 ; -C 85 ; WX 722 ; N U ; B 72 -19 651 718 ; -C 86 ; WX 667 ; N V ; B 19 0 648 718 ; -C 87 ; WX 944 ; N W ; B 16 0 929 718 ; -C 88 ; WX 667 ; N X ; B 14 0 653 718 ; -C 89 ; WX 667 ; N Y ; B 15 0 653 718 ; -C 90 ; WX 611 ; N Z ; B 25 0 586 718 ; -C 91 ; WX 333 ; N bracketleft ; B 63 -196 309 722 ; -C 92 ; WX 278 ; N backslash ; B -33 -19 311 737 ; -C 93 ; WX 333 ; N bracketright ; B 24 -196 270 722 ; -C 94 ; WX 584 ; N asciicircum ; B 62 323 522 698 ; -C 95 ; WX 556 ; N underscore ; B 0 -125 556 -75 ; -C 96 ; WX 278 ; N quoteleft ; B 69 454 209 727 ; -C 97 ; WX 556 ; N a ; B 29 -14 527 546 ; -C 98 ; WX 611 ; N b ; B 61 -14 578 718 ; -C 99 ; WX 556 ; N c ; B 34 -14 524 546 ; -C 100 ; WX 611 ; N d ; B 34 -14 551 718 ; -C 101 ; WX 556 ; N e ; B 23 -14 528 546 ; -C 102 ; WX 333 ; N f ; B 10 0 318 727 ; L i fi ; L l fl ; -C 103 ; WX 611 ; N g ; B 40 -217 553 546 ; -C 104 ; WX 611 ; N h ; B 65 0 546 718 ; -C 105 ; WX 278 ; N i ; B 69 0 209 725 ; -C 106 ; WX 278 ; N j ; B 3 -214 209 725 ; -C 107 ; WX 556 ; N k ; B 69 0 562 718 ; -C 108 ; WX 278 ; N l ; B 69 0 209 718 ; -C 109 ; WX 889 ; N m ; B 64 0 826 546 ; -C 110 ; WX 611 ; N n ; B 65 0 546 546 ; -C 111 ; WX 611 ; N o ; B 34 -14 578 546 ; -C 112 ; WX 611 ; N p ; B 62 -207 578 546 ; -C 113 ; WX 611 ; N q ; B 34 -207 552 546 ; -C 114 ; WX 389 ; N r ; B 64 0 373 546 ; -C 115 ; WX 556 ; N s ; B 30 -14 519 546 ; -C 116 ; WX 333 ; N t ; B 10 -6 309 676 ; -C 117 ; WX 611 ; N u ; B 66 -14 545 532 ; -C 118 ; WX 556 ; N v ; B 13 0 543 532 ; -C 119 ; WX 778 ; N w ; B 10 0 769 532 ; -C 120 ; WX 556 ; N x ; B 15 0 541 532 ; -C 121 ; WX 556 ; N y ; B 10 -214 539 532 ; -C 122 ; WX 500 ; N z ; B 20 0 480 532 ; -C 123 ; WX 389 ; N braceleft ; B 48 -196 365 722 ; -C 124 ; WX 280 ; N bar ; B 84 -225 196 775 ; -C 125 ; WX 389 ; N braceright ; B 24 -196 341 722 ; -C 126 ; WX 584 ; N asciitilde ; B 61 163 523 343 ; -C 161 ; WX 333 ; N exclamdown ; B 90 -186 244 532 ; -C 162 ; WX 556 ; N cent ; B 34 -118 524 628 ; -C 163 ; WX 556 ; N sterling ; B 28 -16 541 718 ; -C 164 ; WX 167 ; N fraction ; B -170 -19 336 710 ; -C 165 ; WX 556 ; N yen ; B -9 0 565 698 ; -C 166 ; WX 556 ; N florin ; B -10 -210 516 737 ; -C 167 ; WX 556 ; N section ; B 34 -184 522 727 ; -C 168 ; WX 556 ; N currency ; B -3 76 559 636 ; -C 169 ; WX 238 ; N quotesingle ; B 70 447 168 718 ; -C 170 ; WX 500 ; N quotedblleft ; B 64 454 436 727 ; -C 171 ; WX 556 ; N guillemotleft ; B 88 76 468 484 ; -C 172 ; WX 333 ; N guilsinglleft ; B 83 76 250 484 ; -C 173 ; WX 333 ; N guilsinglright ; B 83 76 250 484 ; -C 174 ; WX 611 ; N fi ; B 10 0 542 727 ; -C 175 ; WX 611 ; N fl ; B 10 0 542 727 ; -C 177 ; WX 556 ; N endash ; B 0 227 556 333 ; -C 178 ; WX 556 ; N dagger ; B 36 -171 520 718 ; -C 179 ; WX 556 ; N daggerdbl ; B 36 -171 520 718 ; -C 180 ; WX 278 ; N periodcentered ; B 58 172 220 334 ; -C 182 ; WX 556 ; N paragraph ; B -8 -191 539 700 ; -C 183 ; WX 350 ; N bullet ; B 10 194 340 524 ; -C 184 ; WX 278 ; N quotesinglbase ; B 69 -146 209 127 ; -C 185 ; WX 500 ; N quotedblbase ; B 64 -146 436 127 ; -C 186 ; WX 500 ; N quotedblright ; B 64 445 436 718 ; -C 187 ; WX 556 ; N guillemotright ; B 88 76 468 484 ; -C 188 ; WX 1000 ; N ellipsis ; B 92 0 908 146 ; -C 189 ; WX 1000 ; N perthousand ; B -3 -19 1003 710 ; -C 191 ; WX 611 ; N questiondown ; B 55 -195 551 532 ; -C 193 ; WX 333 ; N grave ; B -23 604 225 750 ; -C 194 ; WX 333 ; N acute ; B 108 604 356 750 ; -C 195 ; WX 333 ; N circumflex ; B -10 604 343 750 ; -C 196 ; WX 333 ; N tilde ; B -17 610 350 737 ; -C 197 ; WX 333 ; N macron ; B -6 604 339 678 ; -C 198 ; WX 333 ; N breve ; B -2 604 335 750 ; -C 199 ; WX 333 ; N dotaccent ; B 104 614 230 729 ; -C 200 ; WX 333 ; N dieresis ; B 6 614 327 729 ; -C 202 ; WX 333 ; N ring ; B 59 568 275 776 ; -C 203 ; WX 333 ; N cedilla ; B 6 -228 245 0 ; -C 205 ; WX 333 ; N hungarumlaut ; B 9 604 486 750 ; -C 206 ; WX 333 ; N ogonek ; B 71 -228 304 0 ; -C 207 ; WX 333 ; N caron ; B -10 604 343 750 ; -C 208 ; WX 1000 ; N emdash ; B 0 227 1000 333 ; -C 225 ; WX 1000 ; N AE ; B 5 0 954 718 ; -C 227 ; WX 370 ; N ordfeminine ; B 22 401 347 737 ; -C 232 ; WX 611 ; N Lslash ; B -20 0 583 718 ; -C 233 ; WX 778 ; N Oslash ; B 33 -27 744 745 ; -C 234 ; WX 1000 ; N OE ; B 37 -19 961 737 ; -C 235 ; WX 365 ; N ordmasculine ; B 6 401 360 737 ; -C 241 ; WX 889 ; N ae ; B 29 -14 858 546 ; -C 245 ; WX 278 ; N dotlessi ; B 69 0 209 532 ; -C 248 ; WX 278 ; N lslash ; B -18 0 296 718 ; -C 249 ; WX 611 ; N oslash ; B 22 -29 589 560 ; -C 250 ; WX 944 ; N oe ; B 34 -14 912 546 ; -C 251 ; WX 611 ; N germandbls ; B 69 -14 579 731 ; -C -1 ; WX 278 ; N Idieresis ; B -21 0 300 915 ; -C -1 ; WX 556 ; N eacute ; B 23 -14 528 750 ; -C -1 ; WX 556 ; N abreve ; B 29 -14 527 750 ; -C -1 ; WX 611 ; N uhungarumlaut ; B 66 -14 625 750 ; -C -1 ; WX 556 ; N ecaron ; B 23 -14 528 750 ; -C -1 ; WX 667 ; N Ydieresis ; B 15 0 653 915 ; -C -1 ; WX 584 ; N divide ; B 40 -42 544 548 ; -C -1 ; WX 667 ; N Yacute ; B 15 0 653 936 ; -C -1 ; WX 722 ; N Acircumflex ; B 20 0 702 936 ; -C -1 ; WX 556 ; N aacute ; B 29 -14 527 750 ; -C -1 ; WX 722 ; N Ucircumflex ; B 72 -19 651 936 ; -C -1 ; WX 556 ; N yacute ; B 10 -214 539 750 ; -C -1 ; WX 556 ; N scommaaccent ; B 30 -228 519 546 ; -C -1 ; WX 556 ; N ecircumflex ; B 23 -14 528 750 ; -C -1 ; WX 722 ; N Uring ; B 72 -19 651 962 ; -C -1 ; WX 722 ; N Udieresis ; B 72 -19 651 915 ; -C -1 ; WX 556 ; N aogonek ; B 29 -224 545 546 ; -C -1 ; WX 722 ; N Uacute ; B 72 -19 651 936 ; -C -1 ; WX 611 ; N uogonek ; B 66 -228 545 532 ; -C -1 ; WX 667 ; N Edieresis ; B 76 0 621 915 ; -C -1 ; WX 722 ; N Dcroat ; B -5 0 685 718 ; -C -1 ; WX 250 ; N commaaccent ; B 64 -228 199 -50 ; -C -1 ; WX 737 ; N copyright ; B -11 -19 749 737 ; -C -1 ; WX 667 ; N Emacron ; B 76 0 621 864 ; -C -1 ; WX 556 ; N ccaron ; B 34 -14 524 750 ; -C -1 ; WX 556 ; N aring ; B 29 -14 527 776 ; -C -1 ; WX 722 ; N Ncommaaccent ; B 69 -228 654 718 ; -C -1 ; WX 278 ; N lacute ; B 69 0 329 936 ; -C -1 ; WX 556 ; N agrave ; B 29 -14 527 750 ; -C -1 ; WX 611 ; N Tcommaaccent ; B 14 -228 598 718 ; -C -1 ; WX 722 ; N Cacute ; B 44 -19 684 936 ; -C -1 ; WX 556 ; N atilde ; B 29 -14 527 737 ; -C -1 ; WX 667 ; N Edotaccent ; B 76 0 621 915 ; -C -1 ; WX 556 ; N scaron ; B 30 -14 519 750 ; -C -1 ; WX 556 ; N scedilla ; B 30 -228 519 546 ; -C -1 ; WX 278 ; N iacute ; B 69 0 329 750 ; -C -1 ; WX 494 ; N lozenge ; B 10 0 484 745 ; -C -1 ; WX 722 ; N Rcaron ; B 76 0 677 936 ; -C -1 ; WX 778 ; N Gcommaaccent ; B 44 -228 713 737 ; -C -1 ; WX 611 ; N ucircumflex ; B 66 -14 545 750 ; -C -1 ; WX 556 ; N acircumflex ; B 29 -14 527 750 ; -C -1 ; WX 722 ; N Amacron ; B 20 0 702 864 ; -C -1 ; WX 389 ; N rcaron ; B 18 0 373 750 ; -C -1 ; WX 556 ; N ccedilla ; B 34 -228 524 546 ; -C -1 ; WX 611 ; N Zdotaccent ; B 25 0 586 915 ; -C -1 ; WX 667 ; N Thorn ; B 76 0 627 718 ; -C -1 ; WX 778 ; N Omacron ; B 44 -19 734 864 ; -C -1 ; WX 722 ; N Racute ; B 76 0 677 936 ; -C -1 ; WX 667 ; N Sacute ; B 39 -19 629 936 ; -C -1 ; WX 743 ; N dcaron ; B 34 -14 750 718 ; -C -1 ; WX 722 ; N Umacron ; B 72 -19 651 864 ; -C -1 ; WX 611 ; N uring ; B 66 -14 545 776 ; -C -1 ; WX 333 ; N threesuperior ; B 8 271 326 710 ; -C -1 ; WX 778 ; N Ograve ; B 44 -19 734 936 ; -C -1 ; WX 722 ; N Agrave ; B 20 0 702 936 ; -C -1 ; WX 722 ; N Abreve ; B 20 0 702 936 ; -C -1 ; WX 584 ; N multiply ; B 40 1 545 505 ; -C -1 ; WX 611 ; N uacute ; B 66 -14 545 750 ; -C -1 ; WX 611 ; N Tcaron ; B 14 0 598 936 ; -C -1 ; WX 494 ; N partialdiff ; B 11 -21 494 750 ; -C -1 ; WX 556 ; N ydieresis ; B 10 -214 539 729 ; -C -1 ; WX 722 ; N Nacute ; B 69 0 654 936 ; -C -1 ; WX 278 ; N icircumflex ; B -37 0 316 750 ; -C -1 ; WX 667 ; N Ecircumflex ; B 76 0 621 936 ; -C -1 ; WX 556 ; N adieresis ; B 29 -14 527 729 ; -C -1 ; WX 556 ; N edieresis ; B 23 -14 528 729 ; -C -1 ; WX 556 ; N cacute ; B 34 -14 524 750 ; -C -1 ; WX 611 ; N nacute ; B 65 0 546 750 ; -C -1 ; WX 611 ; N umacron ; B 66 -14 545 678 ; -C -1 ; WX 722 ; N Ncaron ; B 69 0 654 936 ; -C -1 ; WX 278 ; N Iacute ; B 64 0 329 936 ; -C -1 ; WX 584 ; N plusminus ; B 40 0 544 506 ; -C -1 ; WX 280 ; N brokenbar ; B 84 -150 196 700 ; -C -1 ; WX 737 ; N registered ; B -11 -19 748 737 ; -C -1 ; WX 778 ; N Gbreve ; B 44 -19 713 936 ; -C -1 ; WX 278 ; N Idotaccent ; B 64 0 214 915 ; -C -1 ; WX 600 ; N summation ; B 14 -10 585 706 ; -C -1 ; WX 667 ; N Egrave ; B 76 0 621 936 ; -C -1 ; WX 389 ; N racute ; B 64 0 384 750 ; -C -1 ; WX 611 ; N omacron ; B 34 -14 578 678 ; -C -1 ; WX 611 ; N Zacute ; B 25 0 586 936 ; -C -1 ; WX 611 ; N Zcaron ; B 25 0 586 936 ; -C -1 ; WX 549 ; N greaterequal ; B 26 0 523 704 ; -C -1 ; WX 722 ; N Eth ; B -5 0 685 718 ; -C -1 ; WX 722 ; N Ccedilla ; B 44 -228 684 737 ; -C -1 ; WX 278 ; N lcommaaccent ; B 69 -228 213 718 ; -C -1 ; WX 389 ; N tcaron ; B 10 -6 421 878 ; -C -1 ; WX 556 ; N eogonek ; B 23 -228 528 546 ; -C -1 ; WX 722 ; N Uogonek ; B 72 -228 651 718 ; -C -1 ; WX 722 ; N Aacute ; B 20 0 702 936 ; -C -1 ; WX 722 ; N Adieresis ; B 20 0 702 915 ; -C -1 ; WX 556 ; N egrave ; B 23 -14 528 750 ; -C -1 ; WX 500 ; N zacute ; B 20 0 480 750 ; -C -1 ; WX 278 ; N iogonek ; B 16 -224 249 725 ; -C -1 ; WX 778 ; N Oacute ; B 44 -19 734 936 ; -C -1 ; WX 611 ; N oacute ; B 34 -14 578 750 ; -C -1 ; WX 556 ; N amacron ; B 29 -14 527 678 ; -C -1 ; WX 556 ; N sacute ; B 30 -14 519 750 ; -C -1 ; WX 278 ; N idieresis ; B -21 0 300 729 ; -C -1 ; WX 778 ; N Ocircumflex ; B 44 -19 734 936 ; -C -1 ; WX 722 ; N Ugrave ; B 72 -19 651 936 ; -C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ; -C -1 ; WX 611 ; N thorn ; B 62 -208 578 718 ; -C -1 ; WX 333 ; N twosuperior ; B 9 283 324 710 ; -C -1 ; WX 778 ; N Odieresis ; B 44 -19 734 915 ; -C -1 ; WX 611 ; N mu ; B 66 -207 545 532 ; -C -1 ; WX 278 ; N igrave ; B -50 0 209 750 ; -C -1 ; WX 611 ; N ohungarumlaut ; B 34 -14 625 750 ; -C -1 ; WX 667 ; N Eogonek ; B 76 -224 639 718 ; -C -1 ; WX 611 ; N dcroat ; B 34 -14 650 718 ; -C -1 ; WX 834 ; N threequarters ; B 16 -19 799 710 ; -C -1 ; WX 667 ; N Scedilla ; B 39 -228 629 737 ; -C -1 ; WX 400 ; N lcaron ; B 69 0 408 718 ; -C -1 ; WX 722 ; N Kcommaaccent ; B 87 -228 722 718 ; -C -1 ; WX 611 ; N Lacute ; B 76 0 583 936 ; -C -1 ; WX 1000 ; N trademark ; B 44 306 956 718 ; -C -1 ; WX 556 ; N edotaccent ; B 23 -14 528 729 ; -C -1 ; WX 278 ; N Igrave ; B -50 0 214 936 ; -C -1 ; WX 278 ; N Imacron ; B -33 0 312 864 ; -C -1 ; WX 611 ; N Lcaron ; B 76 0 583 718 ; -C -1 ; WX 834 ; N onehalf ; B 26 -19 794 710 ; -C -1 ; WX 549 ; N lessequal ; B 29 0 526 704 ; -C -1 ; WX 611 ; N ocircumflex ; B 34 -14 578 750 ; -C -1 ; WX 611 ; N ntilde ; B 65 0 546 737 ; -C -1 ; WX 722 ; N Uhungarumlaut ; B 72 -19 681 936 ; -C -1 ; WX 667 ; N Eacute ; B 76 0 621 936 ; -C -1 ; WX 556 ; N emacron ; B 23 -14 528 678 ; -C -1 ; WX 611 ; N gbreve ; B 40 -217 553 750 ; -C -1 ; WX 834 ; N onequarter ; B 26 -19 766 710 ; -C -1 ; WX 667 ; N Scaron ; B 39 -19 629 936 ; -C -1 ; WX 667 ; N Scommaaccent ; B 39 -228 629 737 ; -C -1 ; WX 778 ; N Ohungarumlaut ; B 44 -19 734 936 ; -C -1 ; WX 400 ; N degree ; B 57 426 343 712 ; -C -1 ; WX 611 ; N ograve ; B 34 -14 578 750 ; -C -1 ; WX 722 ; N Ccaron ; B 44 -19 684 936 ; -C -1 ; WX 611 ; N ugrave ; B 66 -14 545 750 ; -C -1 ; WX 549 ; N radical ; B 10 -46 512 850 ; -C -1 ; WX 722 ; N Dcaron ; B 76 0 685 936 ; -C -1 ; WX 389 ; N rcommaaccent ; B 64 -228 373 546 ; -C -1 ; WX 722 ; N Ntilde ; B 69 0 654 923 ; -C -1 ; WX 611 ; N otilde ; B 34 -14 578 737 ; -C -1 ; WX 722 ; N Rcommaaccent ; B 76 -228 677 718 ; -C -1 ; WX 611 ; N Lcommaaccent ; B 76 -228 583 718 ; -C -1 ; WX 722 ; N Atilde ; B 20 0 702 923 ; -C -1 ; WX 722 ; N Aogonek ; B 20 -224 742 718 ; -C -1 ; WX 722 ; N Aring ; B 20 0 702 962 ; -C -1 ; WX 778 ; N Otilde ; B 44 -19 734 923 ; -C -1 ; WX 500 ; N zdotaccent ; B 20 0 480 729 ; -C -1 ; WX 667 ; N Ecaron ; B 76 0 621 936 ; -C -1 ; WX 278 ; N Iogonek ; B -11 -228 222 718 ; -C -1 ; WX 556 ; N kcommaaccent ; B 69 -228 562 718 ; -C -1 ; WX 584 ; N minus ; B 40 197 544 309 ; -C -1 ; WX 278 ; N Icircumflex ; B -37 0 316 936 ; -C -1 ; WX 611 ; N ncaron ; B 65 0 546 750 ; -C -1 ; WX 333 ; N tcommaaccent ; B 10 -228 309 676 ; -C -1 ; WX 584 ; N logicalnot ; B 40 108 544 419 ; -C -1 ; WX 611 ; N odieresis ; B 34 -14 578 729 ; -C -1 ; WX 611 ; N udieresis ; B 66 -14 545 729 ; -C -1 ; WX 549 ; N notequal ; B 15 -49 540 570 ; -C -1 ; WX 611 ; N gcommaaccent ; B 40 -217 553 850 ; -C -1 ; WX 611 ; N eth ; B 34 -14 578 737 ; -C -1 ; WX 500 ; N zcaron ; B 20 0 480 750 ; -C -1 ; WX 611 ; N ncommaaccent ; B 65 -228 546 546 ; -C -1 ; WX 333 ; N onesuperior ; B 26 283 237 710 ; -C -1 ; WX 278 ; N imacron ; B -8 0 285 678 ; -C -1 ; WX 556 ; N Euro ; B 0 0 0 0 ; -EndCharMetrics -StartKernData -StartKernPairs 2481 -KPX A C -40 -KPX A Cacute -40 -KPX A Ccaron -40 -KPX A Ccedilla -40 -KPX A G -50 -KPX A Gbreve -50 -KPX A Gcommaaccent -50 -KPX A O -40 -KPX A Oacute -40 -KPX A Ocircumflex -40 -KPX A Odieresis -40 -KPX A Ograve -40 -KPX A Ohungarumlaut -40 -KPX A Omacron -40 -KPX A Oslash -40 -KPX A Otilde -40 -KPX A Q -40 -KPX A T -90 -KPX A Tcaron -90 -KPX A Tcommaaccent -90 -KPX A U -50 -KPX A Uacute -50 -KPX A Ucircumflex -50 -KPX A Udieresis -50 -KPX A Ugrave -50 -KPX A Uhungarumlaut -50 -KPX A Umacron -50 -KPX A Uogonek -50 -KPX A Uring -50 -KPX A V -80 -KPX A W -60 -KPX A Y -110 -KPX A Yacute -110 -KPX A Ydieresis -110 -KPX A u -30 -KPX A uacute -30 -KPX A ucircumflex -30 -KPX A udieresis -30 -KPX A ugrave -30 -KPX A uhungarumlaut -30 -KPX A umacron -30 -KPX A uogonek -30 -KPX A uring -30 -KPX A v -40 -KPX A w -30 -KPX A y -30 -KPX A yacute -30 -KPX A ydieresis -30 -KPX Aacute C -40 -KPX Aacute Cacute -40 -KPX Aacute Ccaron -40 -KPX Aacute Ccedilla -40 -KPX Aacute G -50 -KPX Aacute Gbreve -50 -KPX Aacute Gcommaaccent -50 -KPX Aacute O -40 -KPX Aacute Oacute -40 -KPX Aacute Ocircumflex -40 -KPX Aacute Odieresis -40 -KPX Aacute Ograve -40 -KPX Aacute Ohungarumlaut -40 -KPX Aacute Omacron -40 -KPX Aacute Oslash -40 -KPX Aacute Otilde -40 -KPX Aacute Q -40 -KPX Aacute T -90 -KPX Aacute Tcaron -90 -KPX Aacute Tcommaaccent -90 -KPX Aacute U -50 -KPX Aacute Uacute -50 -KPX Aacute Ucircumflex -50 -KPX Aacute Udieresis -50 -KPX Aacute Ugrave -50 -KPX Aacute Uhungarumlaut -50 -KPX Aacute Umacron -50 -KPX Aacute Uogonek -50 -KPX Aacute Uring -50 -KPX Aacute V -80 -KPX Aacute W -60 -KPX Aacute Y -110 -KPX Aacute Yacute -110 -KPX Aacute Ydieresis -110 -KPX Aacute u -30 -KPX Aacute uacute -30 -KPX Aacute ucircumflex -30 -KPX Aacute udieresis -30 -KPX Aacute ugrave -30 -KPX Aacute uhungarumlaut -30 -KPX Aacute umacron -30 -KPX Aacute uogonek -30 -KPX Aacute uring -30 -KPX Aacute v -40 -KPX Aacute w -30 -KPX Aacute y -30 -KPX Aacute yacute -30 -KPX Aacute ydieresis -30 -KPX Abreve C -40 -KPX Abreve Cacute -40 -KPX Abreve Ccaron -40 -KPX Abreve Ccedilla -40 -KPX Abreve G -50 -KPX Abreve Gbreve -50 -KPX Abreve Gcommaaccent -50 -KPX Abreve O -40 -KPX Abreve Oacute -40 -KPX Abreve Ocircumflex -40 -KPX Abreve Odieresis -40 -KPX Abreve Ograve -40 -KPX Abreve Ohungarumlaut -40 -KPX Abreve Omacron -40 -KPX Abreve Oslash -40 -KPX Abreve Otilde -40 -KPX Abreve Q -40 -KPX Abreve T -90 -KPX Abreve Tcaron -90 -KPX Abreve Tcommaaccent -90 -KPX Abreve U -50 -KPX Abreve Uacute -50 -KPX Abreve Ucircumflex -50 -KPX Abreve Udieresis -50 -KPX Abreve Ugrave -50 -KPX Abreve Uhungarumlaut -50 -KPX Abreve Umacron -50 -KPX Abreve Uogonek -50 -KPX Abreve Uring -50 -KPX Abreve V -80 -KPX Abreve W -60 -KPX Abreve Y -110 -KPX Abreve Yacute -110 -KPX Abreve Ydieresis -110 -KPX Abreve u -30 -KPX Abreve uacute -30 -KPX Abreve ucircumflex -30 -KPX Abreve udieresis -30 -KPX Abreve ugrave -30 -KPX Abreve uhungarumlaut -30 -KPX Abreve umacron -30 -KPX Abreve uogonek -30 -KPX Abreve uring -30 -KPX Abreve v -40 -KPX Abreve w -30 -KPX Abreve y -30 -KPX Abreve yacute -30 -KPX Abreve ydieresis -30 -KPX Acircumflex C -40 -KPX Acircumflex Cacute -40 -KPX Acircumflex Ccaron -40 -KPX Acircumflex Ccedilla -40 -KPX Acircumflex G -50 -KPX Acircumflex Gbreve -50 -KPX Acircumflex Gcommaaccent -50 -KPX Acircumflex O -40 -KPX Acircumflex Oacute -40 -KPX Acircumflex Ocircumflex -40 -KPX Acircumflex Odieresis -40 -KPX Acircumflex Ograve -40 -KPX Acircumflex Ohungarumlaut -40 -KPX Acircumflex Omacron -40 -KPX Acircumflex Oslash -40 -KPX Acircumflex Otilde -40 -KPX Acircumflex Q -40 -KPX Acircumflex T -90 -KPX Acircumflex Tcaron -90 -KPX Acircumflex Tcommaaccent -90 -KPX Acircumflex U -50 -KPX Acircumflex Uacute -50 -KPX Acircumflex Ucircumflex -50 -KPX Acircumflex Udieresis -50 -KPX Acircumflex Ugrave -50 -KPX Acircumflex Uhungarumlaut -50 -KPX Acircumflex Umacron -50 -KPX Acircumflex Uogonek -50 -KPX Acircumflex Uring -50 -KPX Acircumflex V -80 -KPX Acircumflex W -60 -KPX Acircumflex Y -110 -KPX Acircumflex Yacute -110 -KPX Acircumflex Ydieresis -110 -KPX Acircumflex u -30 -KPX Acircumflex uacute -30 -KPX Acircumflex ucircumflex -30 -KPX Acircumflex udieresis -30 -KPX Acircumflex ugrave -30 -KPX Acircumflex uhungarumlaut -30 -KPX Acircumflex umacron -30 -KPX Acircumflex uogonek -30 -KPX Acircumflex uring -30 -KPX Acircumflex v -40 -KPX Acircumflex w -30 -KPX Acircumflex y -30 -KPX Acircumflex yacute -30 -KPX Acircumflex ydieresis -30 -KPX Adieresis C -40 -KPX Adieresis Cacute -40 -KPX Adieresis Ccaron -40 -KPX Adieresis Ccedilla -40 -KPX Adieresis G -50 -KPX Adieresis Gbreve -50 -KPX Adieresis Gcommaaccent -50 -KPX Adieresis O -40 -KPX Adieresis Oacute -40 -KPX Adieresis Ocircumflex -40 -KPX Adieresis Odieresis -40 -KPX Adieresis Ograve -40 -KPX Adieresis Ohungarumlaut -40 -KPX Adieresis Omacron -40 -KPX Adieresis Oslash -40 -KPX Adieresis Otilde -40 -KPX Adieresis Q -40 -KPX Adieresis T -90 -KPX Adieresis Tcaron -90 -KPX Adieresis Tcommaaccent -90 -KPX Adieresis U -50 -KPX Adieresis Uacute -50 -KPX Adieresis Ucircumflex -50 -KPX Adieresis Udieresis -50 -KPX Adieresis Ugrave -50 -KPX Adieresis Uhungarumlaut -50 -KPX Adieresis Umacron -50 -KPX Adieresis Uogonek -50 -KPX Adieresis Uring -50 -KPX Adieresis V -80 -KPX Adieresis W -60 -KPX Adieresis Y -110 -KPX Adieresis Yacute -110 -KPX Adieresis Ydieresis -110 -KPX Adieresis u -30 -KPX Adieresis uacute -30 -KPX Adieresis ucircumflex -30 -KPX Adieresis udieresis -30 -KPX Adieresis ugrave -30 -KPX Adieresis uhungarumlaut -30 -KPX Adieresis umacron -30 -KPX Adieresis uogonek -30 -KPX Adieresis uring -30 -KPX Adieresis v -40 -KPX Adieresis w -30 -KPX Adieresis y -30 -KPX Adieresis yacute -30 -KPX Adieresis ydieresis -30 -KPX Agrave C -40 -KPX Agrave Cacute -40 -KPX Agrave Ccaron -40 -KPX Agrave Ccedilla -40 -KPX Agrave G -50 -KPX Agrave Gbreve -50 -KPX Agrave Gcommaaccent -50 -KPX Agrave O -40 -KPX Agrave Oacute -40 -KPX Agrave Ocircumflex -40 -KPX Agrave Odieresis -40 -KPX Agrave Ograve -40 -KPX Agrave Ohungarumlaut -40 -KPX Agrave Omacron -40 -KPX Agrave Oslash -40 -KPX Agrave Otilde -40 -KPX Agrave Q -40 -KPX Agrave T -90 -KPX Agrave Tcaron -90 -KPX Agrave Tcommaaccent -90 -KPX Agrave U -50 -KPX Agrave Uacute -50 -KPX Agrave Ucircumflex -50 -KPX Agrave Udieresis -50 -KPX Agrave Ugrave -50 -KPX Agrave Uhungarumlaut -50 -KPX Agrave Umacron -50 -KPX Agrave Uogonek -50 -KPX Agrave Uring -50 -KPX Agrave V -80 -KPX Agrave W -60 -KPX Agrave Y -110 -KPX Agrave Yacute -110 -KPX Agrave Ydieresis -110 -KPX Agrave u -30 -KPX Agrave uacute -30 -KPX Agrave ucircumflex -30 -KPX Agrave udieresis -30 -KPX Agrave ugrave -30 -KPX Agrave uhungarumlaut -30 -KPX Agrave umacron -30 -KPX Agrave uogonek -30 -KPX Agrave uring -30 -KPX Agrave v -40 -KPX Agrave w -30 -KPX Agrave y -30 -KPX Agrave yacute -30 -KPX Agrave ydieresis -30 -KPX Amacron C -40 -KPX Amacron Cacute -40 -KPX Amacron Ccaron -40 -KPX Amacron Ccedilla -40 -KPX Amacron G -50 -KPX Amacron Gbreve -50 -KPX Amacron Gcommaaccent -50 -KPX Amacron O -40 -KPX Amacron Oacute -40 -KPX Amacron Ocircumflex -40 -KPX Amacron Odieresis -40 -KPX Amacron Ograve -40 -KPX Amacron Ohungarumlaut -40 -KPX Amacron Omacron -40 -KPX Amacron Oslash -40 -KPX Amacron Otilde -40 -KPX Amacron Q -40 -KPX Amacron T -90 -KPX Amacron Tcaron -90 -KPX Amacron Tcommaaccent -90 -KPX Amacron U -50 -KPX Amacron Uacute -50 -KPX Amacron Ucircumflex -50 -KPX Amacron Udieresis -50 -KPX Amacron Ugrave -50 -KPX Amacron Uhungarumlaut -50 -KPX Amacron Umacron -50 -KPX Amacron Uogonek -50 -KPX Amacron Uring -50 -KPX Amacron V -80 -KPX Amacron W -60 -KPX Amacron Y -110 -KPX Amacron Yacute -110 -KPX Amacron Ydieresis -110 -KPX Amacron u -30 -KPX Amacron uacute -30 -KPX Amacron ucircumflex -30 -KPX Amacron udieresis -30 -KPX Amacron ugrave -30 -KPX Amacron uhungarumlaut -30 -KPX Amacron umacron -30 -KPX Amacron uogonek -30 -KPX Amacron uring -30 -KPX Amacron v -40 -KPX Amacron w -30 -KPX Amacron y -30 -KPX Amacron yacute -30 -KPX Amacron ydieresis -30 -KPX Aogonek C -40 -KPX Aogonek Cacute -40 -KPX Aogonek Ccaron -40 -KPX Aogonek Ccedilla -40 -KPX Aogonek G -50 -KPX Aogonek Gbreve -50 -KPX Aogonek Gcommaaccent -50 -KPX Aogonek O -40 -KPX Aogonek Oacute -40 -KPX Aogonek Ocircumflex -40 -KPX Aogonek Odieresis -40 -KPX Aogonek Ograve -40 -KPX Aogonek Ohungarumlaut -40 -KPX Aogonek Omacron -40 -KPX Aogonek Oslash -40 -KPX Aogonek Otilde -40 -KPX Aogonek Q -40 -KPX Aogonek T -90 -KPX Aogonek Tcaron -90 -KPX Aogonek Tcommaaccent -90 -KPX Aogonek U -50 -KPX Aogonek Uacute -50 -KPX Aogonek Ucircumflex -50 -KPX Aogonek Udieresis -50 -KPX Aogonek Ugrave -50 -KPX Aogonek Uhungarumlaut -50 -KPX Aogonek Umacron -50 -KPX Aogonek Uogonek -50 -KPX Aogonek Uring -50 -KPX Aogonek V -80 -KPX Aogonek W -60 -KPX Aogonek Y -110 -KPX Aogonek Yacute -110 -KPX Aogonek Ydieresis -110 -KPX Aogonek u -30 -KPX Aogonek uacute -30 -KPX Aogonek ucircumflex -30 -KPX Aogonek udieresis -30 -KPX Aogonek ugrave -30 -KPX Aogonek uhungarumlaut -30 -KPX Aogonek umacron -30 -KPX Aogonek uogonek -30 -KPX Aogonek uring -30 -KPX Aogonek v -40 -KPX Aogonek w -30 -KPX Aogonek y -30 -KPX Aogonek yacute -30 -KPX Aogonek ydieresis -30 -KPX Aring C -40 -KPX Aring Cacute -40 -KPX Aring Ccaron -40 -KPX Aring Ccedilla -40 -KPX Aring G -50 -KPX Aring Gbreve -50 -KPX Aring Gcommaaccent -50 -KPX Aring O -40 -KPX Aring Oacute -40 -KPX Aring Ocircumflex -40 -KPX Aring Odieresis -40 -KPX Aring Ograve -40 -KPX Aring Ohungarumlaut -40 -KPX Aring Omacron -40 -KPX Aring Oslash -40 -KPX Aring Otilde -40 -KPX Aring Q -40 -KPX Aring T -90 -KPX Aring Tcaron -90 -KPX Aring Tcommaaccent -90 -KPX Aring U -50 -KPX Aring Uacute -50 -KPX Aring Ucircumflex -50 -KPX Aring Udieresis -50 -KPX Aring Ugrave -50 -KPX Aring Uhungarumlaut -50 -KPX Aring Umacron -50 -KPX Aring Uogonek -50 -KPX Aring Uring -50 -KPX Aring V -80 -KPX Aring W -60 -KPX Aring Y -110 -KPX Aring Yacute -110 -KPX Aring Ydieresis -110 -KPX Aring u -30 -KPX Aring uacute -30 -KPX Aring ucircumflex -30 -KPX Aring udieresis -30 -KPX Aring ugrave -30 -KPX Aring uhungarumlaut -30 -KPX Aring umacron -30 -KPX Aring uogonek -30 -KPX Aring uring -30 -KPX Aring v -40 -KPX Aring w -30 -KPX Aring y -30 -KPX Aring yacute -30 -KPX Aring ydieresis -30 -KPX Atilde C -40 -KPX Atilde Cacute -40 -KPX Atilde Ccaron -40 -KPX Atilde Ccedilla -40 -KPX Atilde G -50 -KPX Atilde Gbreve -50 -KPX Atilde Gcommaaccent -50 -KPX Atilde O -40 -KPX Atilde Oacute -40 -KPX Atilde Ocircumflex -40 -KPX Atilde Odieresis -40 -KPX Atilde Ograve -40 -KPX Atilde Ohungarumlaut -40 -KPX Atilde Omacron -40 -KPX Atilde Oslash -40 -KPX Atilde Otilde -40 -KPX Atilde Q -40 -KPX Atilde T -90 -KPX Atilde Tcaron -90 -KPX Atilde Tcommaaccent -90 -KPX Atilde U -50 -KPX Atilde Uacute -50 -KPX Atilde Ucircumflex -50 -KPX Atilde Udieresis -50 -KPX Atilde Ugrave -50 -KPX Atilde Uhungarumlaut -50 -KPX Atilde Umacron -50 -KPX Atilde Uogonek -50 -KPX Atilde Uring -50 -KPX Atilde V -80 -KPX Atilde W -60 -KPX Atilde Y -110 -KPX Atilde Yacute -110 -KPX Atilde Ydieresis -110 -KPX Atilde u -30 -KPX Atilde uacute -30 -KPX Atilde ucircumflex -30 -KPX Atilde udieresis -30 -KPX Atilde ugrave -30 -KPX Atilde uhungarumlaut -30 -KPX Atilde umacron -30 -KPX Atilde uogonek -30 -KPX Atilde uring -30 -KPX Atilde v -40 -KPX Atilde w -30 -KPX Atilde y -30 -KPX Atilde yacute -30 -KPX Atilde ydieresis -30 -KPX B A -30 -KPX B Aacute -30 -KPX B Abreve -30 -KPX B Acircumflex -30 -KPX B Adieresis -30 -KPX B Agrave -30 -KPX B Amacron -30 -KPX B Aogonek -30 -KPX B Aring -30 -KPX B Atilde -30 -KPX B U -10 -KPX B Uacute -10 -KPX B Ucircumflex -10 -KPX B Udieresis -10 -KPX B Ugrave -10 -KPX B Uhungarumlaut -10 -KPX B Umacron -10 -KPX B Uogonek -10 -KPX B Uring -10 -KPX D A -40 -KPX D Aacute -40 -KPX D Abreve -40 -KPX D Acircumflex -40 -KPX D Adieresis -40 -KPX D Agrave -40 -KPX D Amacron -40 -KPX D Aogonek -40 -KPX D Aring -40 -KPX D Atilde -40 -KPX D V -40 -KPX D W -40 -KPX D Y -70 -KPX D Yacute -70 -KPX D Ydieresis -70 -KPX D comma -30 -KPX D period -30 -KPX Dcaron A -40 -KPX Dcaron Aacute -40 -KPX Dcaron Abreve -40 -KPX Dcaron Acircumflex -40 -KPX Dcaron Adieresis -40 -KPX Dcaron Agrave -40 -KPX Dcaron Amacron -40 -KPX Dcaron Aogonek -40 -KPX Dcaron Aring -40 -KPX Dcaron Atilde -40 -KPX Dcaron V -40 -KPX Dcaron W -40 -KPX Dcaron Y -70 -KPX Dcaron Yacute -70 -KPX Dcaron Ydieresis -70 -KPX Dcaron comma -30 -KPX Dcaron period -30 -KPX Dcroat A -40 -KPX Dcroat Aacute -40 -KPX Dcroat Abreve -40 -KPX Dcroat Acircumflex -40 -KPX Dcroat Adieresis -40 -KPX Dcroat Agrave -40 -KPX Dcroat Amacron -40 -KPX Dcroat Aogonek -40 -KPX Dcroat Aring -40 -KPX Dcroat Atilde -40 -KPX Dcroat V -40 -KPX Dcroat W -40 -KPX Dcroat Y -70 -KPX Dcroat Yacute -70 -KPX Dcroat Ydieresis -70 -KPX Dcroat comma -30 -KPX Dcroat period -30 -KPX F A -80 -KPX F Aacute -80 -KPX F Abreve -80 -KPX F Acircumflex -80 -KPX F Adieresis -80 -KPX F Agrave -80 -KPX F Amacron -80 -KPX F Aogonek -80 -KPX F Aring -80 -KPX F Atilde -80 -KPX F a -20 -KPX F aacute -20 -KPX F abreve -20 -KPX F acircumflex -20 -KPX F adieresis -20 -KPX F agrave -20 -KPX F amacron -20 -KPX F aogonek -20 -KPX F aring -20 -KPX F atilde -20 -KPX F comma -100 -KPX F period -100 -KPX J A -20 -KPX J Aacute -20 -KPX J Abreve -20 -KPX J Acircumflex -20 -KPX J Adieresis -20 -KPX J Agrave -20 -KPX J Amacron -20 -KPX J Aogonek -20 -KPX J Aring -20 -KPX J Atilde -20 -KPX J comma -20 -KPX J period -20 -KPX J u -20 -KPX J uacute -20 -KPX J ucircumflex -20 -KPX J udieresis -20 -KPX J ugrave -20 -KPX J uhungarumlaut -20 -KPX J umacron -20 -KPX J uogonek -20 -KPX J uring -20 -KPX K O -30 -KPX K Oacute -30 -KPX K Ocircumflex -30 -KPX K Odieresis -30 -KPX K Ograve -30 -KPX K Ohungarumlaut -30 -KPX K Omacron -30 -KPX K Oslash -30 -KPX K Otilde -30 -KPX K e -15 -KPX K eacute -15 -KPX K ecaron -15 -KPX K ecircumflex -15 -KPX K edieresis -15 -KPX K edotaccent -15 -KPX K egrave -15 -KPX K emacron -15 -KPX K eogonek -15 -KPX K o -35 -KPX K oacute -35 -KPX K ocircumflex -35 -KPX K odieresis -35 -KPX K ograve -35 -KPX K ohungarumlaut -35 -KPX K omacron -35 -KPX K oslash -35 -KPX K otilde -35 -KPX K u -30 -KPX K uacute -30 -KPX K ucircumflex -30 -KPX K udieresis -30 -KPX K ugrave -30 -KPX K uhungarumlaut -30 -KPX K umacron -30 -KPX K uogonek -30 -KPX K uring -30 -KPX K y -40 -KPX K yacute -40 -KPX K ydieresis -40 -KPX Kcommaaccent O -30 -KPX Kcommaaccent Oacute -30 -KPX Kcommaaccent Ocircumflex -30 -KPX Kcommaaccent Odieresis -30 -KPX Kcommaaccent Ograve -30 -KPX Kcommaaccent Ohungarumlaut -30 -KPX Kcommaaccent Omacron -30 -KPX Kcommaaccent Oslash -30 -KPX Kcommaaccent Otilde -30 -KPX Kcommaaccent e -15 -KPX Kcommaaccent eacute -15 -KPX Kcommaaccent ecaron -15 -KPX Kcommaaccent ecircumflex -15 -KPX Kcommaaccent edieresis -15 -KPX Kcommaaccent edotaccent -15 -KPX Kcommaaccent egrave -15 -KPX Kcommaaccent emacron -15 -KPX Kcommaaccent eogonek -15 -KPX Kcommaaccent o -35 -KPX Kcommaaccent oacute -35 -KPX Kcommaaccent ocircumflex -35 -KPX Kcommaaccent odieresis -35 -KPX Kcommaaccent ograve -35 -KPX Kcommaaccent ohungarumlaut -35 -KPX Kcommaaccent omacron -35 -KPX Kcommaaccent oslash -35 -KPX Kcommaaccent otilde -35 -KPX Kcommaaccent u -30 -KPX Kcommaaccent uacute -30 -KPX Kcommaaccent ucircumflex -30 -KPX Kcommaaccent udieresis -30 -KPX Kcommaaccent ugrave -30 -KPX Kcommaaccent uhungarumlaut -30 -KPX Kcommaaccent umacron -30 -KPX Kcommaaccent uogonek -30 -KPX Kcommaaccent uring -30 -KPX Kcommaaccent y -40 -KPX Kcommaaccent yacute -40 -KPX Kcommaaccent ydieresis -40 -KPX L T -90 -KPX L Tcaron -90 -KPX L Tcommaaccent -90 -KPX L V -110 -KPX L W -80 -KPX L Y -120 -KPX L Yacute -120 -KPX L Ydieresis -120 -KPX L quotedblright -140 -KPX L quoteright -140 -KPX L y -30 -KPX L yacute -30 -KPX L ydieresis -30 -KPX Lacute T -90 -KPX Lacute Tcaron -90 -KPX Lacute Tcommaaccent -90 -KPX Lacute V -110 -KPX Lacute W -80 -KPX Lacute Y -120 -KPX Lacute Yacute -120 -KPX Lacute Ydieresis -120 -KPX Lacute quotedblright -140 -KPX Lacute quoteright -140 -KPX Lacute y -30 -KPX Lacute yacute -30 -KPX Lacute ydieresis -30 -KPX Lcommaaccent T -90 -KPX Lcommaaccent Tcaron -90 -KPX Lcommaaccent Tcommaaccent -90 -KPX Lcommaaccent V -110 -KPX Lcommaaccent W -80 -KPX Lcommaaccent Y -120 -KPX Lcommaaccent Yacute -120 -KPX Lcommaaccent Ydieresis -120 -KPX Lcommaaccent quotedblright -140 -KPX Lcommaaccent quoteright -140 -KPX Lcommaaccent y -30 -KPX Lcommaaccent yacute -30 -KPX Lcommaaccent ydieresis -30 -KPX Lslash T -90 -KPX Lslash Tcaron -90 -KPX Lslash Tcommaaccent -90 -KPX Lslash V -110 -KPX Lslash W -80 -KPX Lslash Y -120 -KPX Lslash Yacute -120 -KPX Lslash Ydieresis -120 -KPX Lslash quotedblright -140 -KPX Lslash quoteright -140 -KPX Lslash y -30 -KPX Lslash yacute -30 -KPX Lslash ydieresis -30 -KPX O A -50 -KPX O Aacute -50 -KPX O Abreve -50 -KPX O Acircumflex -50 -KPX O Adieresis -50 -KPX O Agrave -50 -KPX O Amacron -50 -KPX O Aogonek -50 -KPX O Aring -50 -KPX O Atilde -50 -KPX O T -40 -KPX O Tcaron -40 -KPX O Tcommaaccent -40 -KPX O V -50 -KPX O W -50 -KPX O X -50 -KPX O Y -70 -KPX O Yacute -70 -KPX O Ydieresis -70 -KPX O comma -40 -KPX O period -40 -KPX Oacute A -50 -KPX Oacute Aacute -50 -KPX Oacute Abreve -50 -KPX Oacute Acircumflex -50 -KPX Oacute Adieresis -50 -KPX Oacute Agrave -50 -KPX Oacute Amacron -50 -KPX Oacute Aogonek -50 -KPX Oacute Aring -50 -KPX Oacute Atilde -50 -KPX Oacute T -40 -KPX Oacute Tcaron -40 -KPX Oacute Tcommaaccent -40 -KPX Oacute V -50 -KPX Oacute W -50 -KPX Oacute X -50 -KPX Oacute Y -70 -KPX Oacute Yacute -70 -KPX Oacute Ydieresis -70 -KPX Oacute comma -40 -KPX Oacute period -40 -KPX Ocircumflex A -50 -KPX Ocircumflex Aacute -50 -KPX Ocircumflex Abreve -50 -KPX Ocircumflex Acircumflex -50 -KPX Ocircumflex Adieresis -50 -KPX Ocircumflex Agrave -50 -KPX Ocircumflex Amacron -50 -KPX Ocircumflex Aogonek -50 -KPX Ocircumflex Aring -50 -KPX Ocircumflex Atilde -50 -KPX Ocircumflex T -40 -KPX Ocircumflex Tcaron -40 -KPX Ocircumflex Tcommaaccent -40 -KPX Ocircumflex V -50 -KPX Ocircumflex W -50 -KPX Ocircumflex X -50 -KPX Ocircumflex Y -70 -KPX Ocircumflex Yacute -70 -KPX Ocircumflex Ydieresis -70 -KPX Ocircumflex comma -40 -KPX Ocircumflex period -40 -KPX Odieresis A -50 -KPX Odieresis Aacute -50 -KPX Odieresis Abreve -50 -KPX Odieresis Acircumflex -50 -KPX Odieresis Adieresis -50 -KPX Odieresis Agrave -50 -KPX Odieresis Amacron -50 -KPX Odieresis Aogonek -50 -KPX Odieresis Aring -50 -KPX Odieresis Atilde -50 -KPX Odieresis T -40 -KPX Odieresis Tcaron -40 -KPX Odieresis Tcommaaccent -40 -KPX Odieresis V -50 -KPX Odieresis W -50 -KPX Odieresis X -50 -KPX Odieresis Y -70 -KPX Odieresis Yacute -70 -KPX Odieresis Ydieresis -70 -KPX Odieresis comma -40 -KPX Odieresis period -40 -KPX Ograve A -50 -KPX Ograve Aacute -50 -KPX Ograve Abreve -50 -KPX Ograve Acircumflex -50 -KPX Ograve Adieresis -50 -KPX Ograve Agrave -50 -KPX Ograve Amacron -50 -KPX Ograve Aogonek -50 -KPX Ograve Aring -50 -KPX Ograve Atilde -50 -KPX Ograve T -40 -KPX Ograve Tcaron -40 -KPX Ograve Tcommaaccent -40 -KPX Ograve V -50 -KPX Ograve W -50 -KPX Ograve X -50 -KPX Ograve Y -70 -KPX Ograve Yacute -70 -KPX Ograve Ydieresis -70 -KPX Ograve comma -40 -KPX Ograve period -40 -KPX Ohungarumlaut A -50 -KPX Ohungarumlaut Aacute -50 -KPX Ohungarumlaut Abreve -50 -KPX Ohungarumlaut Acircumflex -50 -KPX Ohungarumlaut Adieresis -50 -KPX Ohungarumlaut Agrave -50 -KPX Ohungarumlaut Amacron -50 -KPX Ohungarumlaut Aogonek -50 -KPX Ohungarumlaut Aring -50 -KPX Ohungarumlaut Atilde -50 -KPX Ohungarumlaut T -40 -KPX Ohungarumlaut Tcaron -40 -KPX Ohungarumlaut Tcommaaccent -40 -KPX Ohungarumlaut V -50 -KPX Ohungarumlaut W -50 -KPX Ohungarumlaut X -50 -KPX Ohungarumlaut Y -70 -KPX Ohungarumlaut Yacute -70 -KPX Ohungarumlaut Ydieresis -70 -KPX Ohungarumlaut comma -40 -KPX Ohungarumlaut period -40 -KPX Omacron A -50 -KPX Omacron Aacute -50 -KPX Omacron Abreve -50 -KPX Omacron Acircumflex -50 -KPX Omacron Adieresis -50 -KPX Omacron Agrave -50 -KPX Omacron Amacron -50 -KPX Omacron Aogonek -50 -KPX Omacron Aring -50 -KPX Omacron Atilde -50 -KPX Omacron T -40 -KPX Omacron Tcaron -40 -KPX Omacron Tcommaaccent -40 -KPX Omacron V -50 -KPX Omacron W -50 -KPX Omacron X -50 -KPX Omacron Y -70 -KPX Omacron Yacute -70 -KPX Omacron Ydieresis -70 -KPX Omacron comma -40 -KPX Omacron period -40 -KPX Oslash A -50 -KPX Oslash Aacute -50 -KPX Oslash Abreve -50 -KPX Oslash Acircumflex -50 -KPX Oslash Adieresis -50 -KPX Oslash Agrave -50 -KPX Oslash Amacron -50 -KPX Oslash Aogonek -50 -KPX Oslash Aring -50 -KPX Oslash Atilde -50 -KPX Oslash T -40 -KPX Oslash Tcaron -40 -KPX Oslash Tcommaaccent -40 -KPX Oslash V -50 -KPX Oslash W -50 -KPX Oslash X -50 -KPX Oslash Y -70 -KPX Oslash Yacute -70 -KPX Oslash Ydieresis -70 -KPX Oslash comma -40 -KPX Oslash period -40 -KPX Otilde A -50 -KPX Otilde Aacute -50 -KPX Otilde Abreve -50 -KPX Otilde Acircumflex -50 -KPX Otilde Adieresis -50 -KPX Otilde Agrave -50 -KPX Otilde Amacron -50 -KPX Otilde Aogonek -50 -KPX Otilde Aring -50 -KPX Otilde Atilde -50 -KPX Otilde T -40 -KPX Otilde Tcaron -40 -KPX Otilde Tcommaaccent -40 -KPX Otilde V -50 -KPX Otilde W -50 -KPX Otilde X -50 -KPX Otilde Y -70 -KPX Otilde Yacute -70 -KPX Otilde Ydieresis -70 -KPX Otilde comma -40 -KPX Otilde period -40 -KPX P A -100 -KPX P Aacute -100 -KPX P Abreve -100 -KPX P Acircumflex -100 -KPX P Adieresis -100 -KPX P Agrave -100 -KPX P Amacron -100 -KPX P Aogonek -100 -KPX P Aring -100 -KPX P Atilde -100 -KPX P a -30 -KPX P aacute -30 -KPX P abreve -30 -KPX P acircumflex -30 -KPX P adieresis -30 -KPX P agrave -30 -KPX P amacron -30 -KPX P aogonek -30 -KPX P aring -30 -KPX P atilde -30 -KPX P comma -120 -KPX P e -30 -KPX P eacute -30 -KPX P ecaron -30 -KPX P ecircumflex -30 -KPX P edieresis -30 -KPX P edotaccent -30 -KPX P egrave -30 -KPX P emacron -30 -KPX P eogonek -30 -KPX P o -40 -KPX P oacute -40 -KPX P ocircumflex -40 -KPX P odieresis -40 -KPX P ograve -40 -KPX P ohungarumlaut -40 -KPX P omacron -40 -KPX P oslash -40 -KPX P otilde -40 -KPX P period -120 -KPX Q U -10 -KPX Q Uacute -10 -KPX Q Ucircumflex -10 -KPX Q Udieresis -10 -KPX Q Ugrave -10 -KPX Q Uhungarumlaut -10 -KPX Q Umacron -10 -KPX Q Uogonek -10 -KPX Q Uring -10 -KPX Q comma 20 -KPX Q period 20 -KPX R O -20 -KPX R Oacute -20 -KPX R Ocircumflex -20 -KPX R Odieresis -20 -KPX R Ograve -20 -KPX R Ohungarumlaut -20 -KPX R Omacron -20 -KPX R Oslash -20 -KPX R Otilde -20 -KPX R T -20 -KPX R Tcaron -20 -KPX R Tcommaaccent -20 -KPX R U -20 -KPX R Uacute -20 -KPX R Ucircumflex -20 -KPX R Udieresis -20 -KPX R Ugrave -20 -KPX R Uhungarumlaut -20 -KPX R Umacron -20 -KPX R Uogonek -20 -KPX R Uring -20 -KPX R V -50 -KPX R W -40 -KPX R Y -50 -KPX R Yacute -50 -KPX R Ydieresis -50 -KPX Racute O -20 -KPX Racute Oacute -20 -KPX Racute Ocircumflex -20 -KPX Racute Odieresis -20 -KPX Racute Ograve -20 -KPX Racute Ohungarumlaut -20 -KPX Racute Omacron -20 -KPX Racute Oslash -20 -KPX Racute Otilde -20 -KPX Racute T -20 -KPX Racute Tcaron -20 -KPX Racute Tcommaaccent -20 -KPX Racute U -20 -KPX Racute Uacute -20 -KPX Racute Ucircumflex -20 -KPX Racute Udieresis -20 -KPX Racute Ugrave -20 -KPX Racute Uhungarumlaut -20 -KPX Racute Umacron -20 -KPX Racute Uogonek -20 -KPX Racute Uring -20 -KPX Racute V -50 -KPX Racute W -40 -KPX Racute Y -50 -KPX Racute Yacute -50 -KPX Racute Ydieresis -50 -KPX Rcaron O -20 -KPX Rcaron Oacute -20 -KPX Rcaron Ocircumflex -20 -KPX Rcaron Odieresis -20 -KPX Rcaron Ograve -20 -KPX Rcaron Ohungarumlaut -20 -KPX Rcaron Omacron -20 -KPX Rcaron Oslash -20 -KPX Rcaron Otilde -20 -KPX Rcaron T -20 -KPX Rcaron Tcaron -20 -KPX Rcaron Tcommaaccent -20 -KPX Rcaron U -20 -KPX Rcaron Uacute -20 -KPX Rcaron Ucircumflex -20 -KPX Rcaron Udieresis -20 -KPX Rcaron Ugrave -20 -KPX Rcaron Uhungarumlaut -20 -KPX Rcaron Umacron -20 -KPX Rcaron Uogonek -20 -KPX Rcaron Uring -20 -KPX Rcaron V -50 -KPX Rcaron W -40 -KPX Rcaron Y -50 -KPX Rcaron Yacute -50 -KPX Rcaron Ydieresis -50 -KPX Rcommaaccent O -20 -KPX Rcommaaccent Oacute -20 -KPX Rcommaaccent Ocircumflex -20 -KPX Rcommaaccent Odieresis -20 -KPX Rcommaaccent Ograve -20 -KPX Rcommaaccent Ohungarumlaut -20 -KPX Rcommaaccent Omacron -20 -KPX Rcommaaccent Oslash -20 -KPX Rcommaaccent Otilde -20 -KPX Rcommaaccent T -20 -KPX Rcommaaccent Tcaron -20 -KPX Rcommaaccent Tcommaaccent -20 -KPX Rcommaaccent U -20 -KPX Rcommaaccent Uacute -20 -KPX Rcommaaccent Ucircumflex -20 -KPX Rcommaaccent Udieresis -20 -KPX Rcommaaccent Ugrave -20 -KPX Rcommaaccent Uhungarumlaut -20 -KPX Rcommaaccent Umacron -20 -KPX Rcommaaccent Uogonek -20 -KPX Rcommaaccent Uring -20 -KPX Rcommaaccent V -50 -KPX Rcommaaccent W -40 -KPX Rcommaaccent Y -50 -KPX Rcommaaccent Yacute -50 -KPX Rcommaaccent Ydieresis -50 -KPX T A -90 -KPX T Aacute -90 -KPX T Abreve -90 -KPX T Acircumflex -90 -KPX T Adieresis -90 -KPX T Agrave -90 -KPX T Amacron -90 -KPX T Aogonek -90 -KPX T Aring -90 -KPX T Atilde -90 -KPX T O -40 -KPX T Oacute -40 -KPX T Ocircumflex -40 -KPX T Odieresis -40 -KPX T Ograve -40 -KPX T Ohungarumlaut -40 -KPX T Omacron -40 -KPX T Oslash -40 -KPX T Otilde -40 -KPX T a -80 -KPX T aacute -80 -KPX T abreve -80 -KPX T acircumflex -80 -KPX T adieresis -80 -KPX T agrave -80 -KPX T amacron -80 -KPX T aogonek -80 -KPX T aring -80 -KPX T atilde -80 -KPX T colon -40 -KPX T comma -80 -KPX T e -60 -KPX T eacute -60 -KPX T ecaron -60 -KPX T ecircumflex -60 -KPX T edieresis -60 -KPX T edotaccent -60 -KPX T egrave -60 -KPX T emacron -60 -KPX T eogonek -60 -KPX T hyphen -120 -KPX T o -80 -KPX T oacute -80 -KPX T ocircumflex -80 -KPX T odieresis -80 -KPX T ograve -80 -KPX T ohungarumlaut -80 -KPX T omacron -80 -KPX T oslash -80 -KPX T otilde -80 -KPX T period -80 -KPX T r -80 -KPX T racute -80 -KPX T rcommaaccent -80 -KPX T semicolon -40 -KPX T u -90 -KPX T uacute -90 -KPX T ucircumflex -90 -KPX T udieresis -90 -KPX T ugrave -90 -KPX T uhungarumlaut -90 -KPX T umacron -90 -KPX T uogonek -90 -KPX T uring -90 -KPX T w -60 -KPX T y -60 -KPX T yacute -60 -KPX T ydieresis -60 -KPX Tcaron A -90 -KPX Tcaron Aacute -90 -KPX Tcaron Abreve -90 -KPX Tcaron Acircumflex -90 -KPX Tcaron Adieresis -90 -KPX Tcaron Agrave -90 -KPX Tcaron Amacron -90 -KPX Tcaron Aogonek -90 -KPX Tcaron Aring -90 -KPX Tcaron Atilde -90 -KPX Tcaron O -40 -KPX Tcaron Oacute -40 -KPX Tcaron Ocircumflex -40 -KPX Tcaron Odieresis -40 -KPX Tcaron Ograve -40 -KPX Tcaron Ohungarumlaut -40 -KPX Tcaron Omacron -40 -KPX Tcaron Oslash -40 -KPX Tcaron Otilde -40 -KPX Tcaron a -80 -KPX Tcaron aacute -80 -KPX Tcaron abreve -80 -KPX Tcaron acircumflex -80 -KPX Tcaron adieresis -80 -KPX Tcaron agrave -80 -KPX Tcaron amacron -80 -KPX Tcaron aogonek -80 -KPX Tcaron aring -80 -KPX Tcaron atilde -80 -KPX Tcaron colon -40 -KPX Tcaron comma -80 -KPX Tcaron e -60 -KPX Tcaron eacute -60 -KPX Tcaron ecaron -60 -KPX Tcaron ecircumflex -60 -KPX Tcaron edieresis -60 -KPX Tcaron edotaccent -60 -KPX Tcaron egrave -60 -KPX Tcaron emacron -60 -KPX Tcaron eogonek -60 -KPX Tcaron hyphen -120 -KPX Tcaron o -80 -KPX Tcaron oacute -80 -KPX Tcaron ocircumflex -80 -KPX Tcaron odieresis -80 -KPX Tcaron ograve -80 -KPX Tcaron ohungarumlaut -80 -KPX Tcaron omacron -80 -KPX Tcaron oslash -80 -KPX Tcaron otilde -80 -KPX Tcaron period -80 -KPX Tcaron r -80 -KPX Tcaron racute -80 -KPX Tcaron rcommaaccent -80 -KPX Tcaron semicolon -40 -KPX Tcaron u -90 -KPX Tcaron uacute -90 -KPX Tcaron ucircumflex -90 -KPX Tcaron udieresis -90 -KPX Tcaron ugrave -90 -KPX Tcaron uhungarumlaut -90 -KPX Tcaron umacron -90 -KPX Tcaron uogonek -90 -KPX Tcaron uring -90 -KPX Tcaron w -60 -KPX Tcaron y -60 -KPX Tcaron yacute -60 -KPX Tcaron ydieresis -60 -KPX Tcommaaccent A -90 -KPX Tcommaaccent Aacute -90 -KPX Tcommaaccent Abreve -90 -KPX Tcommaaccent Acircumflex -90 -KPX Tcommaaccent Adieresis -90 -KPX Tcommaaccent Agrave -90 -KPX Tcommaaccent Amacron -90 -KPX Tcommaaccent Aogonek -90 -KPX Tcommaaccent Aring -90 -KPX Tcommaaccent Atilde -90 -KPX Tcommaaccent O -40 -KPX Tcommaaccent Oacute -40 -KPX Tcommaaccent Ocircumflex -40 -KPX Tcommaaccent Odieresis -40 -KPX Tcommaaccent Ograve -40 -KPX Tcommaaccent Ohungarumlaut -40 -KPX Tcommaaccent Omacron -40 -KPX Tcommaaccent Oslash -40 -KPX Tcommaaccent Otilde -40 -KPX Tcommaaccent a -80 -KPX Tcommaaccent aacute -80 -KPX Tcommaaccent abreve -80 -KPX Tcommaaccent acircumflex -80 -KPX Tcommaaccent adieresis -80 -KPX Tcommaaccent agrave -80 -KPX Tcommaaccent amacron -80 -KPX Tcommaaccent aogonek -80 -KPX Tcommaaccent aring -80 -KPX Tcommaaccent atilde -80 -KPX Tcommaaccent colon -40 -KPX Tcommaaccent comma -80 -KPX Tcommaaccent e -60 -KPX Tcommaaccent eacute -60 -KPX Tcommaaccent ecaron -60 -KPX Tcommaaccent ecircumflex -60 -KPX Tcommaaccent edieresis -60 -KPX Tcommaaccent edotaccent -60 -KPX Tcommaaccent egrave -60 -KPX Tcommaaccent emacron -60 -KPX Tcommaaccent eogonek -60 -KPX Tcommaaccent hyphen -120 -KPX Tcommaaccent o -80 -KPX Tcommaaccent oacute -80 -KPX Tcommaaccent ocircumflex -80 -KPX Tcommaaccent odieresis -80 -KPX Tcommaaccent ograve -80 -KPX Tcommaaccent ohungarumlaut -80 -KPX Tcommaaccent omacron -80 -KPX Tcommaaccent oslash -80 -KPX Tcommaaccent otilde -80 -KPX Tcommaaccent period -80 -KPX Tcommaaccent r -80 -KPX Tcommaaccent racute -80 -KPX Tcommaaccent rcommaaccent -80 -KPX Tcommaaccent semicolon -40 -KPX Tcommaaccent u -90 -KPX Tcommaaccent uacute -90 -KPX Tcommaaccent ucircumflex -90 -KPX Tcommaaccent udieresis -90 -KPX Tcommaaccent ugrave -90 -KPX Tcommaaccent uhungarumlaut -90 -KPX Tcommaaccent umacron -90 -KPX Tcommaaccent uogonek -90 -KPX Tcommaaccent uring -90 -KPX Tcommaaccent w -60 -KPX Tcommaaccent y -60 -KPX Tcommaaccent yacute -60 -KPX Tcommaaccent ydieresis -60 -KPX U A -50 -KPX U Aacute -50 -KPX U Abreve -50 -KPX U Acircumflex -50 -KPX U Adieresis -50 -KPX U Agrave -50 -KPX U Amacron -50 -KPX U Aogonek -50 -KPX U Aring -50 -KPX U Atilde -50 -KPX U comma -30 -KPX U period -30 -KPX Uacute A -50 -KPX Uacute Aacute -50 -KPX Uacute Abreve -50 -KPX Uacute Acircumflex -50 -KPX Uacute Adieresis -50 -KPX Uacute Agrave -50 -KPX Uacute Amacron -50 -KPX Uacute Aogonek -50 -KPX Uacute Aring -50 -KPX Uacute Atilde -50 -KPX Uacute comma -30 -KPX Uacute period -30 -KPX Ucircumflex A -50 -KPX Ucircumflex Aacute -50 -KPX Ucircumflex Abreve -50 -KPX Ucircumflex Acircumflex -50 -KPX Ucircumflex Adieresis -50 -KPX Ucircumflex Agrave -50 -KPX Ucircumflex Amacron -50 -KPX Ucircumflex Aogonek -50 -KPX Ucircumflex Aring -50 -KPX Ucircumflex Atilde -50 -KPX Ucircumflex comma -30 -KPX Ucircumflex period -30 -KPX Udieresis A -50 -KPX Udieresis Aacute -50 -KPX Udieresis Abreve -50 -KPX Udieresis Acircumflex -50 -KPX Udieresis Adieresis -50 -KPX Udieresis Agrave -50 -KPX Udieresis Amacron -50 -KPX Udieresis Aogonek -50 -KPX Udieresis Aring -50 -KPX Udieresis Atilde -50 -KPX Udieresis comma -30 -KPX Udieresis period -30 -KPX Ugrave A -50 -KPX Ugrave Aacute -50 -KPX Ugrave Abreve -50 -KPX Ugrave Acircumflex -50 -KPX Ugrave Adieresis -50 -KPX Ugrave Agrave -50 -KPX Ugrave Amacron -50 -KPX Ugrave Aogonek -50 -KPX Ugrave Aring -50 -KPX Ugrave Atilde -50 -KPX Ugrave comma -30 -KPX Ugrave period -30 -KPX Uhungarumlaut A -50 -KPX Uhungarumlaut Aacute -50 -KPX Uhungarumlaut Abreve -50 -KPX Uhungarumlaut Acircumflex -50 -KPX Uhungarumlaut Adieresis -50 -KPX Uhungarumlaut Agrave -50 -KPX Uhungarumlaut Amacron -50 -KPX Uhungarumlaut Aogonek -50 -KPX Uhungarumlaut Aring -50 -KPX Uhungarumlaut Atilde -50 -KPX Uhungarumlaut comma -30 -KPX Uhungarumlaut period -30 -KPX Umacron A -50 -KPX Umacron Aacute -50 -KPX Umacron Abreve -50 -KPX Umacron Acircumflex -50 -KPX Umacron Adieresis -50 -KPX Umacron Agrave -50 -KPX Umacron Amacron -50 -KPX Umacron Aogonek -50 -KPX Umacron Aring -50 -KPX Umacron Atilde -50 -KPX Umacron comma -30 -KPX Umacron period -30 -KPX Uogonek A -50 -KPX Uogonek Aacute -50 -KPX Uogonek Abreve -50 -KPX Uogonek Acircumflex -50 -KPX Uogonek Adieresis -50 -KPX Uogonek Agrave -50 -KPX Uogonek Amacron -50 -KPX Uogonek Aogonek -50 -KPX Uogonek Aring -50 -KPX Uogonek Atilde -50 -KPX Uogonek comma -30 -KPX Uogonek period -30 -KPX Uring A -50 -KPX Uring Aacute -50 -KPX Uring Abreve -50 -KPX Uring Acircumflex -50 -KPX Uring Adieresis -50 -KPX Uring Agrave -50 -KPX Uring Amacron -50 -KPX Uring Aogonek -50 -KPX Uring Aring -50 -KPX Uring Atilde -50 -KPX Uring comma -30 -KPX Uring period -30 -KPX V A -80 -KPX V Aacute -80 -KPX V Abreve -80 -KPX V Acircumflex -80 -KPX V Adieresis -80 -KPX V Agrave -80 -KPX V Amacron -80 -KPX V Aogonek -80 -KPX V Aring -80 -KPX V Atilde -80 -KPX V G -50 -KPX V Gbreve -50 -KPX V Gcommaaccent -50 -KPX V O -50 -KPX V Oacute -50 -KPX V Ocircumflex -50 -KPX V Odieresis -50 -KPX V Ograve -50 -KPX V Ohungarumlaut -50 -KPX V Omacron -50 -KPX V Oslash -50 -KPX V Otilde -50 -KPX V a -60 -KPX V aacute -60 -KPX V abreve -60 -KPX V acircumflex -60 -KPX V adieresis -60 -KPX V agrave -60 -KPX V amacron -60 -KPX V aogonek -60 -KPX V aring -60 -KPX V atilde -60 -KPX V colon -40 -KPX V comma -120 -KPX V e -50 -KPX V eacute -50 -KPX V ecaron -50 -KPX V ecircumflex -50 -KPX V edieresis -50 -KPX V edotaccent -50 -KPX V egrave -50 -KPX V emacron -50 -KPX V eogonek -50 -KPX V hyphen -80 -KPX V o -90 -KPX V oacute -90 -KPX V ocircumflex -90 -KPX V odieresis -90 -KPX V ograve -90 -KPX V ohungarumlaut -90 -KPX V omacron -90 -KPX V oslash -90 -KPX V otilde -90 -KPX V period -120 -KPX V semicolon -40 -KPX V u -60 -KPX V uacute -60 -KPX V ucircumflex -60 -KPX V udieresis -60 -KPX V ugrave -60 -KPX V uhungarumlaut -60 -KPX V umacron -60 -KPX V uogonek -60 -KPX V uring -60 -KPX W A -60 -KPX W Aacute -60 -KPX W Abreve -60 -KPX W Acircumflex -60 -KPX W Adieresis -60 -KPX W Agrave -60 -KPX W Amacron -60 -KPX W Aogonek -60 -KPX W Aring -60 -KPX W Atilde -60 -KPX W O -20 -KPX W Oacute -20 -KPX W Ocircumflex -20 -KPX W Odieresis -20 -KPX W Ograve -20 -KPX W Ohungarumlaut -20 -KPX W Omacron -20 -KPX W Oslash -20 -KPX W Otilde -20 -KPX W a -40 -KPX W aacute -40 -KPX W abreve -40 -KPX W acircumflex -40 -KPX W adieresis -40 -KPX W agrave -40 -KPX W amacron -40 -KPX W aogonek -40 -KPX W aring -40 -KPX W atilde -40 -KPX W colon -10 -KPX W comma -80 -KPX W e -35 -KPX W eacute -35 -KPX W ecaron -35 -KPX W ecircumflex -35 -KPX W edieresis -35 -KPX W edotaccent -35 -KPX W egrave -35 -KPX W emacron -35 -KPX W eogonek -35 -KPX W hyphen -40 -KPX W o -60 -KPX W oacute -60 -KPX W ocircumflex -60 -KPX W odieresis -60 -KPX W ograve -60 -KPX W ohungarumlaut -60 -KPX W omacron -60 -KPX W oslash -60 -KPX W otilde -60 -KPX W period -80 -KPX W semicolon -10 -KPX W u -45 -KPX W uacute -45 -KPX W ucircumflex -45 -KPX W udieresis -45 -KPX W ugrave -45 -KPX W uhungarumlaut -45 -KPX W umacron -45 -KPX W uogonek -45 -KPX W uring -45 -KPX W y -20 -KPX W yacute -20 -KPX W ydieresis -20 -KPX Y A -110 -KPX Y Aacute -110 -KPX Y Abreve -110 -KPX Y Acircumflex -110 -KPX Y Adieresis -110 -KPX Y Agrave -110 -KPX Y Amacron -110 -KPX Y Aogonek -110 -KPX Y Aring -110 -KPX Y Atilde -110 -KPX Y O -70 -KPX Y Oacute -70 -KPX Y Ocircumflex -70 -KPX Y Odieresis -70 -KPX Y Ograve -70 -KPX Y Ohungarumlaut -70 -KPX Y Omacron -70 -KPX Y Oslash -70 -KPX Y Otilde -70 -KPX Y a -90 -KPX Y aacute -90 -KPX Y abreve -90 -KPX Y acircumflex -90 -KPX Y adieresis -90 -KPX Y agrave -90 -KPX Y amacron -90 -KPX Y aogonek -90 -KPX Y aring -90 -KPX Y atilde -90 -KPX Y colon -50 -KPX Y comma -100 -KPX Y e -80 -KPX Y eacute -80 -KPX Y ecaron -80 -KPX Y ecircumflex -80 -KPX Y edieresis -80 -KPX Y edotaccent -80 -KPX Y egrave -80 -KPX Y emacron -80 -KPX Y eogonek -80 -KPX Y o -100 -KPX Y oacute -100 -KPX Y ocircumflex -100 -KPX Y odieresis -100 -KPX Y ograve -100 -KPX Y ohungarumlaut -100 -KPX Y omacron -100 -KPX Y oslash -100 -KPX Y otilde -100 -KPX Y period -100 -KPX Y semicolon -50 -KPX Y u -100 -KPX Y uacute -100 -KPX Y ucircumflex -100 -KPX Y udieresis -100 -KPX Y ugrave -100 -KPX Y uhungarumlaut -100 -KPX Y umacron -100 -KPX Y uogonek -100 -KPX Y uring -100 -KPX Yacute A -110 -KPX Yacute Aacute -110 -KPX Yacute Abreve -110 -KPX Yacute Acircumflex -110 -KPX Yacute Adieresis -110 -KPX Yacute Agrave -110 -KPX Yacute Amacron -110 -KPX Yacute Aogonek -110 -KPX Yacute Aring -110 -KPX Yacute Atilde -110 -KPX Yacute O -70 -KPX Yacute Oacute -70 -KPX Yacute Ocircumflex -70 -KPX Yacute Odieresis -70 -KPX Yacute Ograve -70 -KPX Yacute Ohungarumlaut -70 -KPX Yacute Omacron -70 -KPX Yacute Oslash -70 -KPX Yacute Otilde -70 -KPX Yacute a -90 -KPX Yacute aacute -90 -KPX Yacute abreve -90 -KPX Yacute acircumflex -90 -KPX Yacute adieresis -90 -KPX Yacute agrave -90 -KPX Yacute amacron -90 -KPX Yacute aogonek -90 -KPX Yacute aring -90 -KPX Yacute atilde -90 -KPX Yacute colon -50 -KPX Yacute comma -100 -KPX Yacute e -80 -KPX Yacute eacute -80 -KPX Yacute ecaron -80 -KPX Yacute ecircumflex -80 -KPX Yacute edieresis -80 -KPX Yacute edotaccent -80 -KPX Yacute egrave -80 -KPX Yacute emacron -80 -KPX Yacute eogonek -80 -KPX Yacute o -100 -KPX Yacute oacute -100 -KPX Yacute ocircumflex -100 -KPX Yacute odieresis -100 -KPX Yacute ograve -100 -KPX Yacute ohungarumlaut -100 -KPX Yacute omacron -100 -KPX Yacute oslash -100 -KPX Yacute otilde -100 -KPX Yacute period -100 -KPX Yacute semicolon -50 -KPX Yacute u -100 -KPX Yacute uacute -100 -KPX Yacute ucircumflex -100 -KPX Yacute udieresis -100 -KPX Yacute ugrave -100 -KPX Yacute uhungarumlaut -100 -KPX Yacute umacron -100 -KPX Yacute uogonek -100 -KPX Yacute uring -100 -KPX Ydieresis A -110 -KPX Ydieresis Aacute -110 -KPX Ydieresis Abreve -110 -KPX Ydieresis Acircumflex -110 -KPX Ydieresis Adieresis -110 -KPX Ydieresis Agrave -110 -KPX Ydieresis Amacron -110 -KPX Ydieresis Aogonek -110 -KPX Ydieresis Aring -110 -KPX Ydieresis Atilde -110 -KPX Ydieresis O -70 -KPX Ydieresis Oacute -70 -KPX Ydieresis Ocircumflex -70 -KPX Ydieresis Odieresis -70 -KPX Ydieresis Ograve -70 -KPX Ydieresis Ohungarumlaut -70 -KPX Ydieresis Omacron -70 -KPX Ydieresis Oslash -70 -KPX Ydieresis Otilde -70 -KPX Ydieresis a -90 -KPX Ydieresis aacute -90 -KPX Ydieresis abreve -90 -KPX Ydieresis acircumflex -90 -KPX Ydieresis adieresis -90 -KPX Ydieresis agrave -90 -KPX Ydieresis amacron -90 -KPX Ydieresis aogonek -90 -KPX Ydieresis aring -90 -KPX Ydieresis atilde -90 -KPX Ydieresis colon -50 -KPX Ydieresis comma -100 -KPX Ydieresis e -80 -KPX Ydieresis eacute -80 -KPX Ydieresis ecaron -80 -KPX Ydieresis ecircumflex -80 -KPX Ydieresis edieresis -80 -KPX Ydieresis edotaccent -80 -KPX Ydieresis egrave -80 -KPX Ydieresis emacron -80 -KPX Ydieresis eogonek -80 -KPX Ydieresis o -100 -KPX Ydieresis oacute -100 -KPX Ydieresis ocircumflex -100 -KPX Ydieresis odieresis -100 -KPX Ydieresis ograve -100 -KPX Ydieresis ohungarumlaut -100 -KPX Ydieresis omacron -100 -KPX Ydieresis oslash -100 -KPX Ydieresis otilde -100 -KPX Ydieresis period -100 -KPX Ydieresis semicolon -50 -KPX Ydieresis u -100 -KPX Ydieresis uacute -100 -KPX Ydieresis ucircumflex -100 -KPX Ydieresis udieresis -100 -KPX Ydieresis ugrave -100 -KPX Ydieresis uhungarumlaut -100 -KPX Ydieresis umacron -100 -KPX Ydieresis uogonek -100 -KPX Ydieresis uring -100 -KPX a g -10 -KPX a gbreve -10 -KPX a gcommaaccent -10 -KPX a v -15 -KPX a w -15 -KPX a y -20 -KPX a yacute -20 -KPX a ydieresis -20 -KPX aacute g -10 -KPX aacute gbreve -10 -KPX aacute gcommaaccent -10 -KPX aacute v -15 -KPX aacute w -15 -KPX aacute y -20 -KPX aacute yacute -20 -KPX aacute ydieresis -20 -KPX abreve g -10 -KPX abreve gbreve -10 -KPX abreve gcommaaccent -10 -KPX abreve v -15 -KPX abreve w -15 -KPX abreve y -20 -KPX abreve yacute -20 -KPX abreve ydieresis -20 -KPX acircumflex g -10 -KPX acircumflex gbreve -10 -KPX acircumflex gcommaaccent -10 -KPX acircumflex v -15 -KPX acircumflex w -15 -KPX acircumflex y -20 -KPX acircumflex yacute -20 -KPX acircumflex ydieresis -20 -KPX adieresis g -10 -KPX adieresis gbreve -10 -KPX adieresis gcommaaccent -10 -KPX adieresis v -15 -KPX adieresis w -15 -KPX adieresis y -20 -KPX adieresis yacute -20 -KPX adieresis ydieresis -20 -KPX agrave g -10 -KPX agrave gbreve -10 -KPX agrave gcommaaccent -10 -KPX agrave v -15 -KPX agrave w -15 -KPX agrave y -20 -KPX agrave yacute -20 -KPX agrave ydieresis -20 -KPX amacron g -10 -KPX amacron gbreve -10 -KPX amacron gcommaaccent -10 -KPX amacron v -15 -KPX amacron w -15 -KPX amacron y -20 -KPX amacron yacute -20 -KPX amacron ydieresis -20 -KPX aogonek g -10 -KPX aogonek gbreve -10 -KPX aogonek gcommaaccent -10 -KPX aogonek v -15 -KPX aogonek w -15 -KPX aogonek y -20 -KPX aogonek yacute -20 -KPX aogonek ydieresis -20 -KPX aring g -10 -KPX aring gbreve -10 -KPX aring gcommaaccent -10 -KPX aring v -15 -KPX aring w -15 -KPX aring y -20 -KPX aring yacute -20 -KPX aring ydieresis -20 -KPX atilde g -10 -KPX atilde gbreve -10 -KPX atilde gcommaaccent -10 -KPX atilde v -15 -KPX atilde w -15 -KPX atilde y -20 -KPX atilde yacute -20 -KPX atilde ydieresis -20 -KPX b l -10 -KPX b lacute -10 -KPX b lcommaaccent -10 -KPX b lslash -10 -KPX b u -20 -KPX b uacute -20 -KPX b ucircumflex -20 -KPX b udieresis -20 -KPX b ugrave -20 -KPX b uhungarumlaut -20 -KPX b umacron -20 -KPX b uogonek -20 -KPX b uring -20 -KPX b v -20 -KPX b y -20 -KPX b yacute -20 -KPX b ydieresis -20 -KPX c h -10 -KPX c k -20 -KPX c kcommaaccent -20 -KPX c l -20 -KPX c lacute -20 -KPX c lcommaaccent -20 -KPX c lslash -20 -KPX c y -10 -KPX c yacute -10 -KPX c ydieresis -10 -KPX cacute h -10 -KPX cacute k -20 -KPX cacute kcommaaccent -20 -KPX cacute l -20 -KPX cacute lacute -20 -KPX cacute lcommaaccent -20 -KPX cacute lslash -20 -KPX cacute y -10 -KPX cacute yacute -10 -KPX cacute ydieresis -10 -KPX ccaron h -10 -KPX ccaron k -20 -KPX ccaron kcommaaccent -20 -KPX ccaron l -20 -KPX ccaron lacute -20 -KPX ccaron lcommaaccent -20 -KPX ccaron lslash -20 -KPX ccaron y -10 -KPX ccaron yacute -10 -KPX ccaron ydieresis -10 -KPX ccedilla h -10 -KPX ccedilla k -20 -KPX ccedilla kcommaaccent -20 -KPX ccedilla l -20 -KPX ccedilla lacute -20 -KPX ccedilla lcommaaccent -20 -KPX ccedilla lslash -20 -KPX ccedilla y -10 -KPX ccedilla yacute -10 -KPX ccedilla ydieresis -10 -KPX colon space -40 -KPX comma quotedblright -120 -KPX comma quoteright -120 -KPX comma space -40 -KPX d d -10 -KPX d dcroat -10 -KPX d v -15 -KPX d w -15 -KPX d y -15 -KPX d yacute -15 -KPX d ydieresis -15 -KPX dcroat d -10 -KPX dcroat dcroat -10 -KPX dcroat v -15 -KPX dcroat w -15 -KPX dcroat y -15 -KPX dcroat yacute -15 -KPX dcroat ydieresis -15 -KPX e comma 10 -KPX e period 20 -KPX e v -15 -KPX e w -15 -KPX e x -15 -KPX e y -15 -KPX e yacute -15 -KPX e ydieresis -15 -KPX eacute comma 10 -KPX eacute period 20 -KPX eacute v -15 -KPX eacute w -15 -KPX eacute x -15 -KPX eacute y -15 -KPX eacute yacute -15 -KPX eacute ydieresis -15 -KPX ecaron comma 10 -KPX ecaron period 20 -KPX ecaron v -15 -KPX ecaron w -15 -KPX ecaron x -15 -KPX ecaron y -15 -KPX ecaron yacute -15 -KPX ecaron ydieresis -15 -KPX ecircumflex comma 10 -KPX ecircumflex period 20 -KPX ecircumflex v -15 -KPX ecircumflex w -15 -KPX ecircumflex x -15 -KPX ecircumflex y -15 -KPX ecircumflex yacute -15 -KPX ecircumflex ydieresis -15 -KPX edieresis comma 10 -KPX edieresis period 20 -KPX edieresis v -15 -KPX edieresis w -15 -KPX edieresis x -15 -KPX edieresis y -15 -KPX edieresis yacute -15 -KPX edieresis ydieresis -15 -KPX edotaccent comma 10 -KPX edotaccent period 20 -KPX edotaccent v -15 -KPX edotaccent w -15 -KPX edotaccent x -15 -KPX edotaccent y -15 -KPX edotaccent yacute -15 -KPX edotaccent ydieresis -15 -KPX egrave comma 10 -KPX egrave period 20 -KPX egrave v -15 -KPX egrave w -15 -KPX egrave x -15 -KPX egrave y -15 -KPX egrave yacute -15 -KPX egrave ydieresis -15 -KPX emacron comma 10 -KPX emacron period 20 -KPX emacron v -15 -KPX emacron w -15 -KPX emacron x -15 -KPX emacron y -15 -KPX emacron yacute -15 -KPX emacron ydieresis -15 -KPX eogonek comma 10 -KPX eogonek period 20 -KPX eogonek v -15 -KPX eogonek w -15 -KPX eogonek x -15 -KPX eogonek y -15 -KPX eogonek yacute -15 -KPX eogonek ydieresis -15 -KPX f comma -10 -KPX f e -10 -KPX f eacute -10 -KPX f ecaron -10 -KPX f ecircumflex -10 -KPX f edieresis -10 -KPX f edotaccent -10 -KPX f egrave -10 -KPX f emacron -10 -KPX f eogonek -10 -KPX f o -20 -KPX f oacute -20 -KPX f ocircumflex -20 -KPX f odieresis -20 -KPX f ograve -20 -KPX f ohungarumlaut -20 -KPX f omacron -20 -KPX f oslash -20 -KPX f otilde -20 -KPX f period -10 -KPX f quotedblright 30 -KPX f quoteright 30 -KPX g e 10 -KPX g eacute 10 -KPX g ecaron 10 -KPX g ecircumflex 10 -KPX g edieresis 10 -KPX g edotaccent 10 -KPX g egrave 10 -KPX g emacron 10 -KPX g eogonek 10 -KPX g g -10 -KPX g gbreve -10 -KPX g gcommaaccent -10 -KPX gbreve e 10 -KPX gbreve eacute 10 -KPX gbreve ecaron 10 -KPX gbreve ecircumflex 10 -KPX gbreve edieresis 10 -KPX gbreve edotaccent 10 -KPX gbreve egrave 10 -KPX gbreve emacron 10 -KPX gbreve eogonek 10 -KPX gbreve g -10 -KPX gbreve gbreve -10 -KPX gbreve gcommaaccent -10 -KPX gcommaaccent e 10 -KPX gcommaaccent eacute 10 -KPX gcommaaccent ecaron 10 -KPX gcommaaccent ecircumflex 10 -KPX gcommaaccent edieresis 10 -KPX gcommaaccent edotaccent 10 -KPX gcommaaccent egrave 10 -KPX gcommaaccent emacron 10 -KPX gcommaaccent eogonek 10 -KPX gcommaaccent g -10 -KPX gcommaaccent gbreve -10 -KPX gcommaaccent gcommaaccent -10 -KPX h y -20 -KPX h yacute -20 -KPX h ydieresis -20 -KPX k o -15 -KPX k oacute -15 -KPX k ocircumflex -15 -KPX k odieresis -15 -KPX k ograve -15 -KPX k ohungarumlaut -15 -KPX k omacron -15 -KPX k oslash -15 -KPX k otilde -15 -KPX kcommaaccent o -15 -KPX kcommaaccent oacute -15 -KPX kcommaaccent ocircumflex -15 -KPX kcommaaccent odieresis -15 -KPX kcommaaccent ograve -15 -KPX kcommaaccent ohungarumlaut -15 -KPX kcommaaccent omacron -15 -KPX kcommaaccent oslash -15 -KPX kcommaaccent otilde -15 -KPX l w -15 -KPX l y -15 -KPX l yacute -15 -KPX l ydieresis -15 -KPX lacute w -15 -KPX lacute y -15 -KPX lacute yacute -15 -KPX lacute ydieresis -15 -KPX lcommaaccent w -15 -KPX lcommaaccent y -15 -KPX lcommaaccent yacute -15 -KPX lcommaaccent ydieresis -15 -KPX lslash w -15 -KPX lslash y -15 -KPX lslash yacute -15 -KPX lslash ydieresis -15 -KPX m u -20 -KPX m uacute -20 -KPX m ucircumflex -20 -KPX m udieresis -20 -KPX m ugrave -20 -KPX m uhungarumlaut -20 -KPX m umacron -20 -KPX m uogonek -20 -KPX m uring -20 -KPX m y -30 -KPX m yacute -30 -KPX m ydieresis -30 -KPX n u -10 -KPX n uacute -10 -KPX n ucircumflex -10 -KPX n udieresis -10 -KPX n ugrave -10 -KPX n uhungarumlaut -10 -KPX n umacron -10 -KPX n uogonek -10 -KPX n uring -10 -KPX n v -40 -KPX n y -20 -KPX n yacute -20 -KPX n ydieresis -20 -KPX nacute u -10 -KPX nacute uacute -10 -KPX nacute ucircumflex -10 -KPX nacute udieresis -10 -KPX nacute ugrave -10 -KPX nacute uhungarumlaut -10 -KPX nacute umacron -10 -KPX nacute uogonek -10 -KPX nacute uring -10 -KPX nacute v -40 -KPX nacute y -20 -KPX nacute yacute -20 -KPX nacute ydieresis -20 -KPX ncaron u -10 -KPX ncaron uacute -10 -KPX ncaron ucircumflex -10 -KPX ncaron udieresis -10 -KPX ncaron ugrave -10 -KPX ncaron uhungarumlaut -10 -KPX ncaron umacron -10 -KPX ncaron uogonek -10 -KPX ncaron uring -10 -KPX ncaron v -40 -KPX ncaron y -20 -KPX ncaron yacute -20 -KPX ncaron ydieresis -20 -KPX ncommaaccent u -10 -KPX ncommaaccent uacute -10 -KPX ncommaaccent ucircumflex -10 -KPX ncommaaccent udieresis -10 -KPX ncommaaccent ugrave -10 -KPX ncommaaccent uhungarumlaut -10 -KPX ncommaaccent umacron -10 -KPX ncommaaccent uogonek -10 -KPX ncommaaccent uring -10 -KPX ncommaaccent v -40 -KPX ncommaaccent y -20 -KPX ncommaaccent yacute -20 -KPX ncommaaccent ydieresis -20 -KPX ntilde u -10 -KPX ntilde uacute -10 -KPX ntilde ucircumflex -10 -KPX ntilde udieresis -10 -KPX ntilde ugrave -10 -KPX ntilde uhungarumlaut -10 -KPX ntilde umacron -10 -KPX ntilde uogonek -10 -KPX ntilde uring -10 -KPX ntilde v -40 -KPX ntilde y -20 -KPX ntilde yacute -20 -KPX ntilde ydieresis -20 -KPX o v -20 -KPX o w -15 -KPX o x -30 -KPX o y -20 -KPX o yacute -20 -KPX o ydieresis -20 -KPX oacute v -20 -KPX oacute w -15 -KPX oacute x -30 -KPX oacute y -20 -KPX oacute yacute -20 -KPX oacute ydieresis -20 -KPX ocircumflex v -20 -KPX ocircumflex w -15 -KPX ocircumflex x -30 -KPX ocircumflex y -20 -KPX ocircumflex yacute -20 -KPX ocircumflex ydieresis -20 -KPX odieresis v -20 -KPX odieresis w -15 -KPX odieresis x -30 -KPX odieresis y -20 -KPX odieresis yacute -20 -KPX odieresis ydieresis -20 -KPX ograve v -20 -KPX ograve w -15 -KPX ograve x -30 -KPX ograve y -20 -KPX ograve yacute -20 -KPX ograve ydieresis -20 -KPX ohungarumlaut v -20 -KPX ohungarumlaut w -15 -KPX ohungarumlaut x -30 -KPX ohungarumlaut y -20 -KPX ohungarumlaut yacute -20 -KPX ohungarumlaut ydieresis -20 -KPX omacron v -20 -KPX omacron w -15 -KPX omacron x -30 -KPX omacron y -20 -KPX omacron yacute -20 -KPX omacron ydieresis -20 -KPX oslash v -20 -KPX oslash w -15 -KPX oslash x -30 -KPX oslash y -20 -KPX oslash yacute -20 -KPX oslash ydieresis -20 -KPX otilde v -20 -KPX otilde w -15 -KPX otilde x -30 -KPX otilde y -20 -KPX otilde yacute -20 -KPX otilde ydieresis -20 -KPX p y -15 -KPX p yacute -15 -KPX p ydieresis -15 -KPX period quotedblright -120 -KPX period quoteright -120 -KPX period space -40 -KPX quotedblright space -80 -KPX quoteleft quoteleft -46 -KPX quoteright d -80 -KPX quoteright dcroat -80 -KPX quoteright l -20 -KPX quoteright lacute -20 -KPX quoteright lcommaaccent -20 -KPX quoteright lslash -20 -KPX quoteright quoteright -46 -KPX quoteright r -40 -KPX quoteright racute -40 -KPX quoteright rcaron -40 -KPX quoteright rcommaaccent -40 -KPX quoteright s -60 -KPX quoteright sacute -60 -KPX quoteright scaron -60 -KPX quoteright scedilla -60 -KPX quoteright scommaaccent -60 -KPX quoteright space -80 -KPX quoteright v -20 -KPX r c -20 -KPX r cacute -20 -KPX r ccaron -20 -KPX r ccedilla -20 -KPX r comma -60 -KPX r d -20 -KPX r dcroat -20 -KPX r g -15 -KPX r gbreve -15 -KPX r gcommaaccent -15 -KPX r hyphen -20 -KPX r o -20 -KPX r oacute -20 -KPX r ocircumflex -20 -KPX r odieresis -20 -KPX r ograve -20 -KPX r ohungarumlaut -20 -KPX r omacron -20 -KPX r oslash -20 -KPX r otilde -20 -KPX r period -60 -KPX r q -20 -KPX r s -15 -KPX r sacute -15 -KPX r scaron -15 -KPX r scedilla -15 -KPX r scommaaccent -15 -KPX r t 20 -KPX r tcommaaccent 20 -KPX r v 10 -KPX r y 10 -KPX r yacute 10 -KPX r ydieresis 10 -KPX racute c -20 -KPX racute cacute -20 -KPX racute ccaron -20 -KPX racute ccedilla -20 -KPX racute comma -60 -KPX racute d -20 -KPX racute dcroat -20 -KPX racute g -15 -KPX racute gbreve -15 -KPX racute gcommaaccent -15 -KPX racute hyphen -20 -KPX racute o -20 -KPX racute oacute -20 -KPX racute ocircumflex -20 -KPX racute odieresis -20 -KPX racute ograve -20 -KPX racute ohungarumlaut -20 -KPX racute omacron -20 -KPX racute oslash -20 -KPX racute otilde -20 -KPX racute period -60 -KPX racute q -20 -KPX racute s -15 -KPX racute sacute -15 -KPX racute scaron -15 -KPX racute scedilla -15 -KPX racute scommaaccent -15 -KPX racute t 20 -KPX racute tcommaaccent 20 -KPX racute v 10 -KPX racute y 10 -KPX racute yacute 10 -KPX racute ydieresis 10 -KPX rcaron c -20 -KPX rcaron cacute -20 -KPX rcaron ccaron -20 -KPX rcaron ccedilla -20 -KPX rcaron comma -60 -KPX rcaron d -20 -KPX rcaron dcroat -20 -KPX rcaron g -15 -KPX rcaron gbreve -15 -KPX rcaron gcommaaccent -15 -KPX rcaron hyphen -20 -KPX rcaron o -20 -KPX rcaron oacute -20 -KPX rcaron ocircumflex -20 -KPX rcaron odieresis -20 -KPX rcaron ograve -20 -KPX rcaron ohungarumlaut -20 -KPX rcaron omacron -20 -KPX rcaron oslash -20 -KPX rcaron otilde -20 -KPX rcaron period -60 -KPX rcaron q -20 -KPX rcaron s -15 -KPX rcaron sacute -15 -KPX rcaron scaron -15 -KPX rcaron scedilla -15 -KPX rcaron scommaaccent -15 -KPX rcaron t 20 -KPX rcaron tcommaaccent 20 -KPX rcaron v 10 -KPX rcaron y 10 -KPX rcaron yacute 10 -KPX rcaron ydieresis 10 -KPX rcommaaccent c -20 -KPX rcommaaccent cacute -20 -KPX rcommaaccent ccaron -20 -KPX rcommaaccent ccedilla -20 -KPX rcommaaccent comma -60 -KPX rcommaaccent d -20 -KPX rcommaaccent dcroat -20 -KPX rcommaaccent g -15 -KPX rcommaaccent gbreve -15 -KPX rcommaaccent gcommaaccent -15 -KPX rcommaaccent hyphen -20 -KPX rcommaaccent o -20 -KPX rcommaaccent oacute -20 -KPX rcommaaccent ocircumflex -20 -KPX rcommaaccent odieresis -20 -KPX rcommaaccent ograve -20 -KPX rcommaaccent ohungarumlaut -20 -KPX rcommaaccent omacron -20 -KPX rcommaaccent oslash -20 -KPX rcommaaccent otilde -20 -KPX rcommaaccent period -60 -KPX rcommaaccent q -20 -KPX rcommaaccent s -15 -KPX rcommaaccent sacute -15 -KPX rcommaaccent scaron -15 -KPX rcommaaccent scedilla -15 -KPX rcommaaccent scommaaccent -15 -KPX rcommaaccent t 20 -KPX rcommaaccent tcommaaccent 20 -KPX rcommaaccent v 10 -KPX rcommaaccent y 10 -KPX rcommaaccent yacute 10 -KPX rcommaaccent ydieresis 10 -KPX s w -15 -KPX sacute w -15 -KPX scaron w -15 -KPX scedilla w -15 -KPX scommaaccent w -15 -KPX semicolon space -40 -KPX space T -100 -KPX space Tcaron -100 -KPX space Tcommaaccent -100 -KPX space V -80 -KPX space W -80 -KPX space Y -120 -KPX space Yacute -120 -KPX space Ydieresis -120 -KPX space quotedblleft -80 -KPX space quoteleft -60 -KPX v a -20 -KPX v aacute -20 -KPX v abreve -20 -KPX v acircumflex -20 -KPX v adieresis -20 -KPX v agrave -20 -KPX v amacron -20 -KPX v aogonek -20 -KPX v aring -20 -KPX v atilde -20 -KPX v comma -80 -KPX v o -30 -KPX v oacute -30 -KPX v ocircumflex -30 -KPX v odieresis -30 -KPX v ograve -30 -KPX v ohungarumlaut -30 -KPX v omacron -30 -KPX v oslash -30 -KPX v otilde -30 -KPX v period -80 -KPX w comma -40 -KPX w o -20 -KPX w oacute -20 -KPX w ocircumflex -20 -KPX w odieresis -20 -KPX w ograve -20 -KPX w ohungarumlaut -20 -KPX w omacron -20 -KPX w oslash -20 -KPX w otilde -20 -KPX w period -40 -KPX x e -10 -KPX x eacute -10 -KPX x ecaron -10 -KPX x ecircumflex -10 -KPX x edieresis -10 -KPX x edotaccent -10 -KPX x egrave -10 -KPX x emacron -10 -KPX x eogonek -10 -KPX y a -30 -KPX y aacute -30 -KPX y abreve -30 -KPX y acircumflex -30 -KPX y adieresis -30 -KPX y agrave -30 -KPX y amacron -30 -KPX y aogonek -30 -KPX y aring -30 -KPX y atilde -30 -KPX y comma -80 -KPX y e -10 -KPX y eacute -10 -KPX y ecaron -10 -KPX y ecircumflex -10 -KPX y edieresis -10 -KPX y edotaccent -10 -KPX y egrave -10 -KPX y emacron -10 -KPX y eogonek -10 -KPX y o -25 -KPX y oacute -25 -KPX y ocircumflex -25 -KPX y odieresis -25 -KPX y ograve -25 -KPX y ohungarumlaut -25 -KPX y omacron -25 -KPX y oslash -25 -KPX y otilde -25 -KPX y period -80 -KPX yacute a -30 -KPX yacute aacute -30 -KPX yacute abreve -30 -KPX yacute acircumflex -30 -KPX yacute adieresis -30 -KPX yacute agrave -30 -KPX yacute amacron -30 -KPX yacute aogonek -30 -KPX yacute aring -30 -KPX yacute atilde -30 -KPX yacute comma -80 -KPX yacute e -10 -KPX yacute eacute -10 -KPX yacute ecaron -10 -KPX yacute ecircumflex -10 -KPX yacute edieresis -10 -KPX yacute edotaccent -10 -KPX yacute egrave -10 -KPX yacute emacron -10 -KPX yacute eogonek -10 -KPX yacute o -25 -KPX yacute oacute -25 -KPX yacute ocircumflex -25 -KPX yacute odieresis -25 -KPX yacute ograve -25 -KPX yacute ohungarumlaut -25 -KPX yacute omacron -25 -KPX yacute oslash -25 -KPX yacute otilde -25 -KPX yacute period -80 -KPX ydieresis a -30 -KPX ydieresis aacute -30 -KPX ydieresis abreve -30 -KPX ydieresis acircumflex -30 -KPX ydieresis adieresis -30 -KPX ydieresis agrave -30 -KPX ydieresis amacron -30 -KPX ydieresis aogonek -30 -KPX ydieresis aring -30 -KPX ydieresis atilde -30 -KPX ydieresis comma -80 -KPX ydieresis e -10 -KPX ydieresis eacute -10 -KPX ydieresis ecaron -10 -KPX ydieresis ecircumflex -10 -KPX ydieresis edieresis -10 -KPX ydieresis edotaccent -10 -KPX ydieresis egrave -10 -KPX ydieresis emacron -10 -KPX ydieresis eogonek -10 -KPX ydieresis o -25 -KPX ydieresis oacute -25 -KPX ydieresis ocircumflex -25 -KPX ydieresis odieresis -25 -KPX ydieresis ograve -25 -KPX ydieresis ohungarumlaut -25 -KPX ydieresis omacron -25 -KPX ydieresis oslash -25 -KPX ydieresis otilde -25 -KPX ydieresis period -80 -KPX z e 10 -KPX z eacute 10 -KPX z ecaron 10 -KPX z ecircumflex 10 -KPX z edieresis 10 -KPX z edotaccent 10 -KPX z egrave 10 -KPX z emacron 10 -KPX z eogonek 10 -KPX zacute e 10 -KPX zacute eacute 10 -KPX zacute ecaron 10 -KPX zacute ecircumflex 10 -KPX zacute edieresis 10 -KPX zacute edotaccent 10 -KPX zacute egrave 10 -KPX zacute emacron 10 -KPX zacute eogonek 10 -KPX zcaron e 10 -KPX zcaron eacute 10 -KPX zcaron ecaron 10 -KPX zcaron ecircumflex 10 -KPX zcaron edieresis 10 -KPX zcaron edotaccent 10 -KPX zcaron egrave 10 -KPX zcaron emacron 10 -KPX zcaron eogonek 10 -KPX zdotaccent e 10 -KPX zdotaccent eacute 10 -KPX zdotaccent ecaron 10 -KPX zdotaccent ecircumflex 10 -KPX zdotaccent edieresis 10 -KPX zdotaccent edotaccent 10 -KPX zdotaccent egrave 10 -KPX zdotaccent emacron 10 -KPX zdotaccent eogonek 10 -EndKernPairs -EndKernData -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Helvetica-BoldOblique.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Helvetica-BoldOblique.afm deleted file mode 100644 index 1715b21..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Helvetica-BoldOblique.afm +++ /dev/null @@ -1,2827 +0,0 @@ -StartFontMetrics 4.1 -Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Thu May 1 12:45:12 1997 -Comment UniqueID 43053 -Comment VMusage 14482 68586 -FontName Helvetica-BoldOblique -FullName Helvetica Bold Oblique -FamilyName Helvetica -Weight Bold -ItalicAngle -12 -IsFixedPitch false -CharacterSet ExtendedRoman -FontBBox -174 -228 1114 962 -UnderlinePosition -100 -UnderlineThickness 50 -Version 002.000 -Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved.Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 718 -XHeight 532 -Ascender 718 -Descender -207 -StdHW 118 -StdVW 140 -StartCharMetrics 315 -C 32 ; WX 278 ; N space ; B 0 0 0 0 ; -C 33 ; WX 333 ; N exclam ; B 94 0 397 718 ; -C 34 ; WX 474 ; N quotedbl ; B 193 447 529 718 ; -C 35 ; WX 556 ; N numbersign ; B 60 0 644 698 ; -C 36 ; WX 556 ; N dollar ; B 67 -115 622 775 ; -C 37 ; WX 889 ; N percent ; B 136 -19 901 710 ; -C 38 ; WX 722 ; N ampersand ; B 89 -19 732 718 ; -C 39 ; WX 278 ; N quoteright ; B 167 445 362 718 ; -C 40 ; WX 333 ; N parenleft ; B 76 -208 470 734 ; -C 41 ; WX 333 ; N parenright ; B -25 -208 369 734 ; -C 42 ; WX 389 ; N asterisk ; B 146 387 481 718 ; -C 43 ; WX 584 ; N plus ; B 82 0 610 506 ; -C 44 ; WX 278 ; N comma ; B 28 -168 245 146 ; -C 45 ; WX 333 ; N hyphen ; B 73 215 379 345 ; -C 46 ; WX 278 ; N period ; B 64 0 245 146 ; -C 47 ; WX 278 ; N slash ; B -37 -19 468 737 ; -C 48 ; WX 556 ; N zero ; B 86 -19 617 710 ; -C 49 ; WX 556 ; N one ; B 173 0 529 710 ; -C 50 ; WX 556 ; N two ; B 26 0 619 710 ; -C 51 ; WX 556 ; N three ; B 65 -19 608 710 ; -C 52 ; WX 556 ; N four ; B 60 0 598 710 ; -C 53 ; WX 556 ; N five ; B 64 -19 636 698 ; -C 54 ; WX 556 ; N six ; B 85 -19 619 710 ; -C 55 ; WX 556 ; N seven ; B 125 0 676 698 ; -C 56 ; WX 556 ; N eight ; B 69 -19 616 710 ; -C 57 ; WX 556 ; N nine ; B 78 -19 615 710 ; -C 58 ; WX 333 ; N colon ; B 92 0 351 512 ; -C 59 ; WX 333 ; N semicolon ; B 56 -168 351 512 ; -C 60 ; WX 584 ; N less ; B 82 -8 655 514 ; -C 61 ; WX 584 ; N equal ; B 58 87 633 419 ; -C 62 ; WX 584 ; N greater ; B 36 -8 609 514 ; -C 63 ; WX 611 ; N question ; B 165 0 671 727 ; -C 64 ; WX 975 ; N at ; B 186 -19 954 737 ; -C 65 ; WX 722 ; N A ; B 20 0 702 718 ; -C 66 ; WX 722 ; N B ; B 76 0 764 718 ; -C 67 ; WX 722 ; N C ; B 107 -19 789 737 ; -C 68 ; WX 722 ; N D ; B 76 0 777 718 ; -C 69 ; WX 667 ; N E ; B 76 0 757 718 ; -C 70 ; WX 611 ; N F ; B 76 0 740 718 ; -C 71 ; WX 778 ; N G ; B 108 -19 817 737 ; -C 72 ; WX 722 ; N H ; B 71 0 804 718 ; -C 73 ; WX 278 ; N I ; B 64 0 367 718 ; -C 74 ; WX 556 ; N J ; B 60 -18 637 718 ; -C 75 ; WX 722 ; N K ; B 87 0 858 718 ; -C 76 ; WX 611 ; N L ; B 76 0 611 718 ; -C 77 ; WX 833 ; N M ; B 69 0 918 718 ; -C 78 ; WX 722 ; N N ; B 69 0 807 718 ; -C 79 ; WX 778 ; N O ; B 107 -19 823 737 ; -C 80 ; WX 667 ; N P ; B 76 0 738 718 ; -C 81 ; WX 778 ; N Q ; B 107 -52 823 737 ; -C 82 ; WX 722 ; N R ; B 76 0 778 718 ; -C 83 ; WX 667 ; N S ; B 81 -19 718 737 ; -C 84 ; WX 611 ; N T ; B 140 0 751 718 ; -C 85 ; WX 722 ; N U ; B 116 -19 804 718 ; -C 86 ; WX 667 ; N V ; B 172 0 801 718 ; -C 87 ; WX 944 ; N W ; B 169 0 1082 718 ; -C 88 ; WX 667 ; N X ; B 14 0 791 718 ; -C 89 ; WX 667 ; N Y ; B 168 0 806 718 ; -C 90 ; WX 611 ; N Z ; B 25 0 737 718 ; -C 91 ; WX 333 ; N bracketleft ; B 21 -196 462 722 ; -C 92 ; WX 278 ; N backslash ; B 124 -19 307 737 ; -C 93 ; WX 333 ; N bracketright ; B -18 -196 423 722 ; -C 94 ; WX 584 ; N asciicircum ; B 131 323 591 698 ; -C 95 ; WX 556 ; N underscore ; B -27 -125 540 -75 ; -C 96 ; WX 278 ; N quoteleft ; B 165 454 361 727 ; -C 97 ; WX 556 ; N a ; B 55 -14 583 546 ; -C 98 ; WX 611 ; N b ; B 61 -14 645 718 ; -C 99 ; WX 556 ; N c ; B 79 -14 599 546 ; -C 100 ; WX 611 ; N d ; B 82 -14 704 718 ; -C 101 ; WX 556 ; N e ; B 70 -14 593 546 ; -C 102 ; WX 333 ; N f ; B 87 0 469 727 ; L i fi ; L l fl ; -C 103 ; WX 611 ; N g ; B 38 -217 666 546 ; -C 104 ; WX 611 ; N h ; B 65 0 629 718 ; -C 105 ; WX 278 ; N i ; B 69 0 363 725 ; -C 106 ; WX 278 ; N j ; B -42 -214 363 725 ; -C 107 ; WX 556 ; N k ; B 69 0 670 718 ; -C 108 ; WX 278 ; N l ; B 69 0 362 718 ; -C 109 ; WX 889 ; N m ; B 64 0 909 546 ; -C 110 ; WX 611 ; N n ; B 65 0 629 546 ; -C 111 ; WX 611 ; N o ; B 82 -14 643 546 ; -C 112 ; WX 611 ; N p ; B 18 -207 645 546 ; -C 113 ; WX 611 ; N q ; B 80 -207 665 546 ; -C 114 ; WX 389 ; N r ; B 64 0 489 546 ; -C 115 ; WX 556 ; N s ; B 63 -14 584 546 ; -C 116 ; WX 333 ; N t ; B 100 -6 422 676 ; -C 117 ; WX 611 ; N u ; B 98 -14 658 532 ; -C 118 ; WX 556 ; N v ; B 126 0 656 532 ; -C 119 ; WX 778 ; N w ; B 123 0 882 532 ; -C 120 ; WX 556 ; N x ; B 15 0 648 532 ; -C 121 ; WX 556 ; N y ; B 42 -214 652 532 ; -C 122 ; WX 500 ; N z ; B 20 0 583 532 ; -C 123 ; WX 389 ; N braceleft ; B 94 -196 518 722 ; -C 124 ; WX 280 ; N bar ; B 36 -225 361 775 ; -C 125 ; WX 389 ; N braceright ; B -18 -196 407 722 ; -C 126 ; WX 584 ; N asciitilde ; B 115 163 577 343 ; -C 161 ; WX 333 ; N exclamdown ; B 50 -186 353 532 ; -C 162 ; WX 556 ; N cent ; B 79 -118 599 628 ; -C 163 ; WX 556 ; N sterling ; B 50 -16 635 718 ; -C 164 ; WX 167 ; N fraction ; B -174 -19 487 710 ; -C 165 ; WX 556 ; N yen ; B 60 0 713 698 ; -C 166 ; WX 556 ; N florin ; B -50 -210 669 737 ; -C 167 ; WX 556 ; N section ; B 61 -184 598 727 ; -C 168 ; WX 556 ; N currency ; B 27 76 680 636 ; -C 169 ; WX 238 ; N quotesingle ; B 165 447 321 718 ; -C 170 ; WX 500 ; N quotedblleft ; B 160 454 588 727 ; -C 171 ; WX 556 ; N guillemotleft ; B 135 76 571 484 ; -C 172 ; WX 333 ; N guilsinglleft ; B 130 76 353 484 ; -C 173 ; WX 333 ; N guilsinglright ; B 99 76 322 484 ; -C 174 ; WX 611 ; N fi ; B 87 0 696 727 ; -C 175 ; WX 611 ; N fl ; B 87 0 695 727 ; -C 177 ; WX 556 ; N endash ; B 48 227 627 333 ; -C 178 ; WX 556 ; N dagger ; B 118 -171 626 718 ; -C 179 ; WX 556 ; N daggerdbl ; B 46 -171 628 718 ; -C 180 ; WX 278 ; N periodcentered ; B 110 172 276 334 ; -C 182 ; WX 556 ; N paragraph ; B 98 -191 688 700 ; -C 183 ; WX 350 ; N bullet ; B 83 194 420 524 ; -C 184 ; WX 278 ; N quotesinglbase ; B 41 -146 236 127 ; -C 185 ; WX 500 ; N quotedblbase ; B 36 -146 463 127 ; -C 186 ; WX 500 ; N quotedblright ; B 162 445 589 718 ; -C 187 ; WX 556 ; N guillemotright ; B 104 76 540 484 ; -C 188 ; WX 1000 ; N ellipsis ; B 92 0 939 146 ; -C 189 ; WX 1000 ; N perthousand ; B 76 -19 1038 710 ; -C 191 ; WX 611 ; N questiondown ; B 53 -195 559 532 ; -C 193 ; WX 333 ; N grave ; B 136 604 353 750 ; -C 194 ; WX 333 ; N acute ; B 236 604 515 750 ; -C 195 ; WX 333 ; N circumflex ; B 118 604 471 750 ; -C 196 ; WX 333 ; N tilde ; B 113 610 507 737 ; -C 197 ; WX 333 ; N macron ; B 122 604 483 678 ; -C 198 ; WX 333 ; N breve ; B 156 604 494 750 ; -C 199 ; WX 333 ; N dotaccent ; B 235 614 385 729 ; -C 200 ; WX 333 ; N dieresis ; B 137 614 482 729 ; -C 202 ; WX 333 ; N ring ; B 200 568 420 776 ; -C 203 ; WX 333 ; N cedilla ; B -37 -228 220 0 ; -C 205 ; WX 333 ; N hungarumlaut ; B 137 604 645 750 ; -C 206 ; WX 333 ; N ogonek ; B 41 -228 264 0 ; -C 207 ; WX 333 ; N caron ; B 149 604 502 750 ; -C 208 ; WX 1000 ; N emdash ; B 48 227 1071 333 ; -C 225 ; WX 1000 ; N AE ; B 5 0 1100 718 ; -C 227 ; WX 370 ; N ordfeminine ; B 125 401 465 737 ; -C 232 ; WX 611 ; N Lslash ; B 34 0 611 718 ; -C 233 ; WX 778 ; N Oslash ; B 35 -27 894 745 ; -C 234 ; WX 1000 ; N OE ; B 99 -19 1114 737 ; -C 235 ; WX 365 ; N ordmasculine ; B 123 401 485 737 ; -C 241 ; WX 889 ; N ae ; B 56 -14 923 546 ; -C 245 ; WX 278 ; N dotlessi ; B 69 0 322 532 ; -C 248 ; WX 278 ; N lslash ; B 40 0 407 718 ; -C 249 ; WX 611 ; N oslash ; B 22 -29 701 560 ; -C 250 ; WX 944 ; N oe ; B 82 -14 977 546 ; -C 251 ; WX 611 ; N germandbls ; B 69 -14 657 731 ; -C -1 ; WX 278 ; N Idieresis ; B 64 0 494 915 ; -C -1 ; WX 556 ; N eacute ; B 70 -14 627 750 ; -C -1 ; WX 556 ; N abreve ; B 55 -14 606 750 ; -C -1 ; WX 611 ; N uhungarumlaut ; B 98 -14 784 750 ; -C -1 ; WX 556 ; N ecaron ; B 70 -14 614 750 ; -C -1 ; WX 667 ; N Ydieresis ; B 168 0 806 915 ; -C -1 ; WX 584 ; N divide ; B 82 -42 610 548 ; -C -1 ; WX 667 ; N Yacute ; B 168 0 806 936 ; -C -1 ; WX 722 ; N Acircumflex ; B 20 0 706 936 ; -C -1 ; WX 556 ; N aacute ; B 55 -14 627 750 ; -C -1 ; WX 722 ; N Ucircumflex ; B 116 -19 804 936 ; -C -1 ; WX 556 ; N yacute ; B 42 -214 652 750 ; -C -1 ; WX 556 ; N scommaaccent ; B 63 -228 584 546 ; -C -1 ; WX 556 ; N ecircumflex ; B 70 -14 593 750 ; -C -1 ; WX 722 ; N Uring ; B 116 -19 804 962 ; -C -1 ; WX 722 ; N Udieresis ; B 116 -19 804 915 ; -C -1 ; WX 556 ; N aogonek ; B 55 -224 583 546 ; -C -1 ; WX 722 ; N Uacute ; B 116 -19 804 936 ; -C -1 ; WX 611 ; N uogonek ; B 98 -228 658 532 ; -C -1 ; WX 667 ; N Edieresis ; B 76 0 757 915 ; -C -1 ; WX 722 ; N Dcroat ; B 62 0 777 718 ; -C -1 ; WX 250 ; N commaaccent ; B 16 -228 188 -50 ; -C -1 ; WX 737 ; N copyright ; B 56 -19 835 737 ; -C -1 ; WX 667 ; N Emacron ; B 76 0 757 864 ; -C -1 ; WX 556 ; N ccaron ; B 79 -14 614 750 ; -C -1 ; WX 556 ; N aring ; B 55 -14 583 776 ; -C -1 ; WX 722 ; N Ncommaaccent ; B 69 -228 807 718 ; -C -1 ; WX 278 ; N lacute ; B 69 0 528 936 ; -C -1 ; WX 556 ; N agrave ; B 55 -14 583 750 ; -C -1 ; WX 611 ; N Tcommaaccent ; B 140 -228 751 718 ; -C -1 ; WX 722 ; N Cacute ; B 107 -19 789 936 ; -C -1 ; WX 556 ; N atilde ; B 55 -14 619 737 ; -C -1 ; WX 667 ; N Edotaccent ; B 76 0 757 915 ; -C -1 ; WX 556 ; N scaron ; B 63 -14 614 750 ; -C -1 ; WX 556 ; N scedilla ; B 63 -228 584 546 ; -C -1 ; WX 278 ; N iacute ; B 69 0 488 750 ; -C -1 ; WX 494 ; N lozenge ; B 90 0 564 745 ; -C -1 ; WX 722 ; N Rcaron ; B 76 0 778 936 ; -C -1 ; WX 778 ; N Gcommaaccent ; B 108 -228 817 737 ; -C -1 ; WX 611 ; N ucircumflex ; B 98 -14 658 750 ; -C -1 ; WX 556 ; N acircumflex ; B 55 -14 583 750 ; -C -1 ; WX 722 ; N Amacron ; B 20 0 718 864 ; -C -1 ; WX 389 ; N rcaron ; B 64 0 530 750 ; -C -1 ; WX 556 ; N ccedilla ; B 79 -228 599 546 ; -C -1 ; WX 611 ; N Zdotaccent ; B 25 0 737 915 ; -C -1 ; WX 667 ; N Thorn ; B 76 0 716 718 ; -C -1 ; WX 778 ; N Omacron ; B 107 -19 823 864 ; -C -1 ; WX 722 ; N Racute ; B 76 0 778 936 ; -C -1 ; WX 667 ; N Sacute ; B 81 -19 722 936 ; -C -1 ; WX 743 ; N dcaron ; B 82 -14 903 718 ; -C -1 ; WX 722 ; N Umacron ; B 116 -19 804 864 ; -C -1 ; WX 611 ; N uring ; B 98 -14 658 776 ; -C -1 ; WX 333 ; N threesuperior ; B 91 271 441 710 ; -C -1 ; WX 778 ; N Ograve ; B 107 -19 823 936 ; -C -1 ; WX 722 ; N Agrave ; B 20 0 702 936 ; -C -1 ; WX 722 ; N Abreve ; B 20 0 729 936 ; -C -1 ; WX 584 ; N multiply ; B 57 1 635 505 ; -C -1 ; WX 611 ; N uacute ; B 98 -14 658 750 ; -C -1 ; WX 611 ; N Tcaron ; B 140 0 751 936 ; -C -1 ; WX 494 ; N partialdiff ; B 43 -21 585 750 ; -C -1 ; WX 556 ; N ydieresis ; B 42 -214 652 729 ; -C -1 ; WX 722 ; N Nacute ; B 69 0 807 936 ; -C -1 ; WX 278 ; N icircumflex ; B 69 0 444 750 ; -C -1 ; WX 667 ; N Ecircumflex ; B 76 0 757 936 ; -C -1 ; WX 556 ; N adieresis ; B 55 -14 594 729 ; -C -1 ; WX 556 ; N edieresis ; B 70 -14 594 729 ; -C -1 ; WX 556 ; N cacute ; B 79 -14 627 750 ; -C -1 ; WX 611 ; N nacute ; B 65 0 654 750 ; -C -1 ; WX 611 ; N umacron ; B 98 -14 658 678 ; -C -1 ; WX 722 ; N Ncaron ; B 69 0 807 936 ; -C -1 ; WX 278 ; N Iacute ; B 64 0 528 936 ; -C -1 ; WX 584 ; N plusminus ; B 40 0 625 506 ; -C -1 ; WX 280 ; N brokenbar ; B 52 -150 345 700 ; -C -1 ; WX 737 ; N registered ; B 55 -19 834 737 ; -C -1 ; WX 778 ; N Gbreve ; B 108 -19 817 936 ; -C -1 ; WX 278 ; N Idotaccent ; B 64 0 397 915 ; -C -1 ; WX 600 ; N summation ; B 14 -10 670 706 ; -C -1 ; WX 667 ; N Egrave ; B 76 0 757 936 ; -C -1 ; WX 389 ; N racute ; B 64 0 543 750 ; -C -1 ; WX 611 ; N omacron ; B 82 -14 643 678 ; -C -1 ; WX 611 ; N Zacute ; B 25 0 737 936 ; -C -1 ; WX 611 ; N Zcaron ; B 25 0 737 936 ; -C -1 ; WX 549 ; N greaterequal ; B 26 0 629 704 ; -C -1 ; WX 722 ; N Eth ; B 62 0 777 718 ; -C -1 ; WX 722 ; N Ccedilla ; B 107 -228 789 737 ; -C -1 ; WX 278 ; N lcommaaccent ; B 30 -228 362 718 ; -C -1 ; WX 389 ; N tcaron ; B 100 -6 608 878 ; -C -1 ; WX 556 ; N eogonek ; B 70 -228 593 546 ; -C -1 ; WX 722 ; N Uogonek ; B 116 -228 804 718 ; -C -1 ; WX 722 ; N Aacute ; B 20 0 750 936 ; -C -1 ; WX 722 ; N Adieresis ; B 20 0 716 915 ; -C -1 ; WX 556 ; N egrave ; B 70 -14 593 750 ; -C -1 ; WX 500 ; N zacute ; B 20 0 599 750 ; -C -1 ; WX 278 ; N iogonek ; B -14 -224 363 725 ; -C -1 ; WX 778 ; N Oacute ; B 107 -19 823 936 ; -C -1 ; WX 611 ; N oacute ; B 82 -14 654 750 ; -C -1 ; WX 556 ; N amacron ; B 55 -14 595 678 ; -C -1 ; WX 556 ; N sacute ; B 63 -14 627 750 ; -C -1 ; WX 278 ; N idieresis ; B 69 0 455 729 ; -C -1 ; WX 778 ; N Ocircumflex ; B 107 -19 823 936 ; -C -1 ; WX 722 ; N Ugrave ; B 116 -19 804 936 ; -C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ; -C -1 ; WX 611 ; N thorn ; B 18 -208 645 718 ; -C -1 ; WX 333 ; N twosuperior ; B 69 283 449 710 ; -C -1 ; WX 778 ; N Odieresis ; B 107 -19 823 915 ; -C -1 ; WX 611 ; N mu ; B 22 -207 658 532 ; -C -1 ; WX 278 ; N igrave ; B 69 0 326 750 ; -C -1 ; WX 611 ; N ohungarumlaut ; B 82 -14 784 750 ; -C -1 ; WX 667 ; N Eogonek ; B 76 -224 757 718 ; -C -1 ; WX 611 ; N dcroat ; B 82 -14 789 718 ; -C -1 ; WX 834 ; N threequarters ; B 99 -19 839 710 ; -C -1 ; WX 667 ; N Scedilla ; B 81 -228 718 737 ; -C -1 ; WX 400 ; N lcaron ; B 69 0 561 718 ; -C -1 ; WX 722 ; N Kcommaaccent ; B 87 -228 858 718 ; -C -1 ; WX 611 ; N Lacute ; B 76 0 611 936 ; -C -1 ; WX 1000 ; N trademark ; B 179 306 1109 718 ; -C -1 ; WX 556 ; N edotaccent ; B 70 -14 593 729 ; -C -1 ; WX 278 ; N Igrave ; B 64 0 367 936 ; -C -1 ; WX 278 ; N Imacron ; B 64 0 496 864 ; -C -1 ; WX 611 ; N Lcaron ; B 76 0 643 718 ; -C -1 ; WX 834 ; N onehalf ; B 132 -19 858 710 ; -C -1 ; WX 549 ; N lessequal ; B 29 0 676 704 ; -C -1 ; WX 611 ; N ocircumflex ; B 82 -14 643 750 ; -C -1 ; WX 611 ; N ntilde ; B 65 0 646 737 ; -C -1 ; WX 722 ; N Uhungarumlaut ; B 116 -19 880 936 ; -C -1 ; WX 667 ; N Eacute ; B 76 0 757 936 ; -C -1 ; WX 556 ; N emacron ; B 70 -14 595 678 ; -C -1 ; WX 611 ; N gbreve ; B 38 -217 666 750 ; -C -1 ; WX 834 ; N onequarter ; B 132 -19 806 710 ; -C -1 ; WX 667 ; N Scaron ; B 81 -19 718 936 ; -C -1 ; WX 667 ; N Scommaaccent ; B 81 -228 718 737 ; -C -1 ; WX 778 ; N Ohungarumlaut ; B 107 -19 908 936 ; -C -1 ; WX 400 ; N degree ; B 175 426 467 712 ; -C -1 ; WX 611 ; N ograve ; B 82 -14 643 750 ; -C -1 ; WX 722 ; N Ccaron ; B 107 -19 789 936 ; -C -1 ; WX 611 ; N ugrave ; B 98 -14 658 750 ; -C -1 ; WX 549 ; N radical ; B 112 -46 689 850 ; -C -1 ; WX 722 ; N Dcaron ; B 76 0 777 936 ; -C -1 ; WX 389 ; N rcommaaccent ; B 26 -228 489 546 ; -C -1 ; WX 722 ; N Ntilde ; B 69 0 807 923 ; -C -1 ; WX 611 ; N otilde ; B 82 -14 646 737 ; -C -1 ; WX 722 ; N Rcommaaccent ; B 76 -228 778 718 ; -C -1 ; WX 611 ; N Lcommaaccent ; B 76 -228 611 718 ; -C -1 ; WX 722 ; N Atilde ; B 20 0 741 923 ; -C -1 ; WX 722 ; N Aogonek ; B 20 -224 702 718 ; -C -1 ; WX 722 ; N Aring ; B 20 0 702 962 ; -C -1 ; WX 778 ; N Otilde ; B 107 -19 823 923 ; -C -1 ; WX 500 ; N zdotaccent ; B 20 0 583 729 ; -C -1 ; WX 667 ; N Ecaron ; B 76 0 757 936 ; -C -1 ; WX 278 ; N Iogonek ; B -41 -228 367 718 ; -C -1 ; WX 556 ; N kcommaaccent ; B 69 -228 670 718 ; -C -1 ; WX 584 ; N minus ; B 82 197 610 309 ; -C -1 ; WX 278 ; N Icircumflex ; B 64 0 484 936 ; -C -1 ; WX 611 ; N ncaron ; B 65 0 641 750 ; -C -1 ; WX 333 ; N tcommaaccent ; B 58 -228 422 676 ; -C -1 ; WX 584 ; N logicalnot ; B 105 108 633 419 ; -C -1 ; WX 611 ; N odieresis ; B 82 -14 643 729 ; -C -1 ; WX 611 ; N udieresis ; B 98 -14 658 729 ; -C -1 ; WX 549 ; N notequal ; B 32 -49 630 570 ; -C -1 ; WX 611 ; N gcommaaccent ; B 38 -217 666 850 ; -C -1 ; WX 611 ; N eth ; B 82 -14 670 737 ; -C -1 ; WX 500 ; N zcaron ; B 20 0 586 750 ; -C -1 ; WX 611 ; N ncommaaccent ; B 65 -228 629 546 ; -C -1 ; WX 333 ; N onesuperior ; B 148 283 388 710 ; -C -1 ; WX 278 ; N imacron ; B 69 0 429 678 ; -C -1 ; WX 556 ; N Euro ; B 0 0 0 0 ; -EndCharMetrics -StartKernData -StartKernPairs 2481 -KPX A C -40 -KPX A Cacute -40 -KPX A Ccaron -40 -KPX A Ccedilla -40 -KPX A G -50 -KPX A Gbreve -50 -KPX A Gcommaaccent -50 -KPX A O -40 -KPX A Oacute -40 -KPX A Ocircumflex -40 -KPX A Odieresis -40 -KPX A Ograve -40 -KPX A Ohungarumlaut -40 -KPX A Omacron -40 -KPX A Oslash -40 -KPX A Otilde -40 -KPX A Q -40 -KPX A T -90 -KPX A Tcaron -90 -KPX A Tcommaaccent -90 -KPX A U -50 -KPX A Uacute -50 -KPX A Ucircumflex -50 -KPX A Udieresis -50 -KPX A Ugrave -50 -KPX A Uhungarumlaut -50 -KPX A Umacron -50 -KPX A Uogonek -50 -KPX A Uring -50 -KPX A V -80 -KPX A W -60 -KPX A Y -110 -KPX A Yacute -110 -KPX A Ydieresis -110 -KPX A u -30 -KPX A uacute -30 -KPX A ucircumflex -30 -KPX A udieresis -30 -KPX A ugrave -30 -KPX A uhungarumlaut -30 -KPX A umacron -30 -KPX A uogonek -30 -KPX A uring -30 -KPX A v -40 -KPX A w -30 -KPX A y -30 -KPX A yacute -30 -KPX A ydieresis -30 -KPX Aacute C -40 -KPX Aacute Cacute -40 -KPX Aacute Ccaron -40 -KPX Aacute Ccedilla -40 -KPX Aacute G -50 -KPX Aacute Gbreve -50 -KPX Aacute Gcommaaccent -50 -KPX Aacute O -40 -KPX Aacute Oacute -40 -KPX Aacute Ocircumflex -40 -KPX Aacute Odieresis -40 -KPX Aacute Ograve -40 -KPX Aacute Ohungarumlaut -40 -KPX Aacute Omacron -40 -KPX Aacute Oslash -40 -KPX Aacute Otilde -40 -KPX Aacute Q -40 -KPX Aacute T -90 -KPX Aacute Tcaron -90 -KPX Aacute Tcommaaccent -90 -KPX Aacute U -50 -KPX Aacute Uacute -50 -KPX Aacute Ucircumflex -50 -KPX Aacute Udieresis -50 -KPX Aacute Ugrave -50 -KPX Aacute Uhungarumlaut -50 -KPX Aacute Umacron -50 -KPX Aacute Uogonek -50 -KPX Aacute Uring -50 -KPX Aacute V -80 -KPX Aacute W -60 -KPX Aacute Y -110 -KPX Aacute Yacute -110 -KPX Aacute Ydieresis -110 -KPX Aacute u -30 -KPX Aacute uacute -30 -KPX Aacute ucircumflex -30 -KPX Aacute udieresis -30 -KPX Aacute ugrave -30 -KPX Aacute uhungarumlaut -30 -KPX Aacute umacron -30 -KPX Aacute uogonek -30 -KPX Aacute uring -30 -KPX Aacute v -40 -KPX Aacute w -30 -KPX Aacute y -30 -KPX Aacute yacute -30 -KPX Aacute ydieresis -30 -KPX Abreve C -40 -KPX Abreve Cacute -40 -KPX Abreve Ccaron -40 -KPX Abreve Ccedilla -40 -KPX Abreve G -50 -KPX Abreve Gbreve -50 -KPX Abreve Gcommaaccent -50 -KPX Abreve O -40 -KPX Abreve Oacute -40 -KPX Abreve Ocircumflex -40 -KPX Abreve Odieresis -40 -KPX Abreve Ograve -40 -KPX Abreve Ohungarumlaut -40 -KPX Abreve Omacron -40 -KPX Abreve Oslash -40 -KPX Abreve Otilde -40 -KPX Abreve Q -40 -KPX Abreve T -90 -KPX Abreve Tcaron -90 -KPX Abreve Tcommaaccent -90 -KPX Abreve U -50 -KPX Abreve Uacute -50 -KPX Abreve Ucircumflex -50 -KPX Abreve Udieresis -50 -KPX Abreve Ugrave -50 -KPX Abreve Uhungarumlaut -50 -KPX Abreve Umacron -50 -KPX Abreve Uogonek -50 -KPX Abreve Uring -50 -KPX Abreve V -80 -KPX Abreve W -60 -KPX Abreve Y -110 -KPX Abreve Yacute -110 -KPX Abreve Ydieresis -110 -KPX Abreve u -30 -KPX Abreve uacute -30 -KPX Abreve ucircumflex -30 -KPX Abreve udieresis -30 -KPX Abreve ugrave -30 -KPX Abreve uhungarumlaut -30 -KPX Abreve umacron -30 -KPX Abreve uogonek -30 -KPX Abreve uring -30 -KPX Abreve v -40 -KPX Abreve w -30 -KPX Abreve y -30 -KPX Abreve yacute -30 -KPX Abreve ydieresis -30 -KPX Acircumflex C -40 -KPX Acircumflex Cacute -40 -KPX Acircumflex Ccaron -40 -KPX Acircumflex Ccedilla -40 -KPX Acircumflex G -50 -KPX Acircumflex Gbreve -50 -KPX Acircumflex Gcommaaccent -50 -KPX Acircumflex O -40 -KPX Acircumflex Oacute -40 -KPX Acircumflex Ocircumflex -40 -KPX Acircumflex Odieresis -40 -KPX Acircumflex Ograve -40 -KPX Acircumflex Ohungarumlaut -40 -KPX Acircumflex Omacron -40 -KPX Acircumflex Oslash -40 -KPX Acircumflex Otilde -40 -KPX Acircumflex Q -40 -KPX Acircumflex T -90 -KPX Acircumflex Tcaron -90 -KPX Acircumflex Tcommaaccent -90 -KPX Acircumflex U -50 -KPX Acircumflex Uacute -50 -KPX Acircumflex Ucircumflex -50 -KPX Acircumflex Udieresis -50 -KPX Acircumflex Ugrave -50 -KPX Acircumflex Uhungarumlaut -50 -KPX Acircumflex Umacron -50 -KPX Acircumflex Uogonek -50 -KPX Acircumflex Uring -50 -KPX Acircumflex V -80 -KPX Acircumflex W -60 -KPX Acircumflex Y -110 -KPX Acircumflex Yacute -110 -KPX Acircumflex Ydieresis -110 -KPX Acircumflex u -30 -KPX Acircumflex uacute -30 -KPX Acircumflex ucircumflex -30 -KPX Acircumflex udieresis -30 -KPX Acircumflex ugrave -30 -KPX Acircumflex uhungarumlaut -30 -KPX Acircumflex umacron -30 -KPX Acircumflex uogonek -30 -KPX Acircumflex uring -30 -KPX Acircumflex v -40 -KPX Acircumflex w -30 -KPX Acircumflex y -30 -KPX Acircumflex yacute -30 -KPX Acircumflex ydieresis -30 -KPX Adieresis C -40 -KPX Adieresis Cacute -40 -KPX Adieresis Ccaron -40 -KPX Adieresis Ccedilla -40 -KPX Adieresis G -50 -KPX Adieresis Gbreve -50 -KPX Adieresis Gcommaaccent -50 -KPX Adieresis O -40 -KPX Adieresis Oacute -40 -KPX Adieresis Ocircumflex -40 -KPX Adieresis Odieresis -40 -KPX Adieresis Ograve -40 -KPX Adieresis Ohungarumlaut -40 -KPX Adieresis Omacron -40 -KPX Adieresis Oslash -40 -KPX Adieresis Otilde -40 -KPX Adieresis Q -40 -KPX Adieresis T -90 -KPX Adieresis Tcaron -90 -KPX Adieresis Tcommaaccent -90 -KPX Adieresis U -50 -KPX Adieresis Uacute -50 -KPX Adieresis Ucircumflex -50 -KPX Adieresis Udieresis -50 -KPX Adieresis Ugrave -50 -KPX Adieresis Uhungarumlaut -50 -KPX Adieresis Umacron -50 -KPX Adieresis Uogonek -50 -KPX Adieresis Uring -50 -KPX Adieresis V -80 -KPX Adieresis W -60 -KPX Adieresis Y -110 -KPX Adieresis Yacute -110 -KPX Adieresis Ydieresis -110 -KPX Adieresis u -30 -KPX Adieresis uacute -30 -KPX Adieresis ucircumflex -30 -KPX Adieresis udieresis -30 -KPX Adieresis ugrave -30 -KPX Adieresis uhungarumlaut -30 -KPX Adieresis umacron -30 -KPX Adieresis uogonek -30 -KPX Adieresis uring -30 -KPX Adieresis v -40 -KPX Adieresis w -30 -KPX Adieresis y -30 -KPX Adieresis yacute -30 -KPX Adieresis ydieresis -30 -KPX Agrave C -40 -KPX Agrave Cacute -40 -KPX Agrave Ccaron -40 -KPX Agrave Ccedilla -40 -KPX Agrave G -50 -KPX Agrave Gbreve -50 -KPX Agrave Gcommaaccent -50 -KPX Agrave O -40 -KPX Agrave Oacute -40 -KPX Agrave Ocircumflex -40 -KPX Agrave Odieresis -40 -KPX Agrave Ograve -40 -KPX Agrave Ohungarumlaut -40 -KPX Agrave Omacron -40 -KPX Agrave Oslash -40 -KPX Agrave Otilde -40 -KPX Agrave Q -40 -KPX Agrave T -90 -KPX Agrave Tcaron -90 -KPX Agrave Tcommaaccent -90 -KPX Agrave U -50 -KPX Agrave Uacute -50 -KPX Agrave Ucircumflex -50 -KPX Agrave Udieresis -50 -KPX Agrave Ugrave -50 -KPX Agrave Uhungarumlaut -50 -KPX Agrave Umacron -50 -KPX Agrave Uogonek -50 -KPX Agrave Uring -50 -KPX Agrave V -80 -KPX Agrave W -60 -KPX Agrave Y -110 -KPX Agrave Yacute -110 -KPX Agrave Ydieresis -110 -KPX Agrave u -30 -KPX Agrave uacute -30 -KPX Agrave ucircumflex -30 -KPX Agrave udieresis -30 -KPX Agrave ugrave -30 -KPX Agrave uhungarumlaut -30 -KPX Agrave umacron -30 -KPX Agrave uogonek -30 -KPX Agrave uring -30 -KPX Agrave v -40 -KPX Agrave w -30 -KPX Agrave y -30 -KPX Agrave yacute -30 -KPX Agrave ydieresis -30 -KPX Amacron C -40 -KPX Amacron Cacute -40 -KPX Amacron Ccaron -40 -KPX Amacron Ccedilla -40 -KPX Amacron G -50 -KPX Amacron Gbreve -50 -KPX Amacron Gcommaaccent -50 -KPX Amacron O -40 -KPX Amacron Oacute -40 -KPX Amacron Ocircumflex -40 -KPX Amacron Odieresis -40 -KPX Amacron Ograve -40 -KPX Amacron Ohungarumlaut -40 -KPX Amacron Omacron -40 -KPX Amacron Oslash -40 -KPX Amacron Otilde -40 -KPX Amacron Q -40 -KPX Amacron T -90 -KPX Amacron Tcaron -90 -KPX Amacron Tcommaaccent -90 -KPX Amacron U -50 -KPX Amacron Uacute -50 -KPX Amacron Ucircumflex -50 -KPX Amacron Udieresis -50 -KPX Amacron Ugrave -50 -KPX Amacron Uhungarumlaut -50 -KPX Amacron Umacron -50 -KPX Amacron Uogonek -50 -KPX Amacron Uring -50 -KPX Amacron V -80 -KPX Amacron W -60 -KPX Amacron Y -110 -KPX Amacron Yacute -110 -KPX Amacron Ydieresis -110 -KPX Amacron u -30 -KPX Amacron uacute -30 -KPX Amacron ucircumflex -30 -KPX Amacron udieresis -30 -KPX Amacron ugrave -30 -KPX Amacron uhungarumlaut -30 -KPX Amacron umacron -30 -KPX Amacron uogonek -30 -KPX Amacron uring -30 -KPX Amacron v -40 -KPX Amacron w -30 -KPX Amacron y -30 -KPX Amacron yacute -30 -KPX Amacron ydieresis -30 -KPX Aogonek C -40 -KPX Aogonek Cacute -40 -KPX Aogonek Ccaron -40 -KPX Aogonek Ccedilla -40 -KPX Aogonek G -50 -KPX Aogonek Gbreve -50 -KPX Aogonek Gcommaaccent -50 -KPX Aogonek O -40 -KPX Aogonek Oacute -40 -KPX Aogonek Ocircumflex -40 -KPX Aogonek Odieresis -40 -KPX Aogonek Ograve -40 -KPX Aogonek Ohungarumlaut -40 -KPX Aogonek Omacron -40 -KPX Aogonek Oslash -40 -KPX Aogonek Otilde -40 -KPX Aogonek Q -40 -KPX Aogonek T -90 -KPX Aogonek Tcaron -90 -KPX Aogonek Tcommaaccent -90 -KPX Aogonek U -50 -KPX Aogonek Uacute -50 -KPX Aogonek Ucircumflex -50 -KPX Aogonek Udieresis -50 -KPX Aogonek Ugrave -50 -KPX Aogonek Uhungarumlaut -50 -KPX Aogonek Umacron -50 -KPX Aogonek Uogonek -50 -KPX Aogonek Uring -50 -KPX Aogonek V -80 -KPX Aogonek W -60 -KPX Aogonek Y -110 -KPX Aogonek Yacute -110 -KPX Aogonek Ydieresis -110 -KPX Aogonek u -30 -KPX Aogonek uacute -30 -KPX Aogonek ucircumflex -30 -KPX Aogonek udieresis -30 -KPX Aogonek ugrave -30 -KPX Aogonek uhungarumlaut -30 -KPX Aogonek umacron -30 -KPX Aogonek uogonek -30 -KPX Aogonek uring -30 -KPX Aogonek v -40 -KPX Aogonek w -30 -KPX Aogonek y -30 -KPX Aogonek yacute -30 -KPX Aogonek ydieresis -30 -KPX Aring C -40 -KPX Aring Cacute -40 -KPX Aring Ccaron -40 -KPX Aring Ccedilla -40 -KPX Aring G -50 -KPX Aring Gbreve -50 -KPX Aring Gcommaaccent -50 -KPX Aring O -40 -KPX Aring Oacute -40 -KPX Aring Ocircumflex -40 -KPX Aring Odieresis -40 -KPX Aring Ograve -40 -KPX Aring Ohungarumlaut -40 -KPX Aring Omacron -40 -KPX Aring Oslash -40 -KPX Aring Otilde -40 -KPX Aring Q -40 -KPX Aring T -90 -KPX Aring Tcaron -90 -KPX Aring Tcommaaccent -90 -KPX Aring U -50 -KPX Aring Uacute -50 -KPX Aring Ucircumflex -50 -KPX Aring Udieresis -50 -KPX Aring Ugrave -50 -KPX Aring Uhungarumlaut -50 -KPX Aring Umacron -50 -KPX Aring Uogonek -50 -KPX Aring Uring -50 -KPX Aring V -80 -KPX Aring W -60 -KPX Aring Y -110 -KPX Aring Yacute -110 -KPX Aring Ydieresis -110 -KPX Aring u -30 -KPX Aring uacute -30 -KPX Aring ucircumflex -30 -KPX Aring udieresis -30 -KPX Aring ugrave -30 -KPX Aring uhungarumlaut -30 -KPX Aring umacron -30 -KPX Aring uogonek -30 -KPX Aring uring -30 -KPX Aring v -40 -KPX Aring w -30 -KPX Aring y -30 -KPX Aring yacute -30 -KPX Aring ydieresis -30 -KPX Atilde C -40 -KPX Atilde Cacute -40 -KPX Atilde Ccaron -40 -KPX Atilde Ccedilla -40 -KPX Atilde G -50 -KPX Atilde Gbreve -50 -KPX Atilde Gcommaaccent -50 -KPX Atilde O -40 -KPX Atilde Oacute -40 -KPX Atilde Ocircumflex -40 -KPX Atilde Odieresis -40 -KPX Atilde Ograve -40 -KPX Atilde Ohungarumlaut -40 -KPX Atilde Omacron -40 -KPX Atilde Oslash -40 -KPX Atilde Otilde -40 -KPX Atilde Q -40 -KPX Atilde T -90 -KPX Atilde Tcaron -90 -KPX Atilde Tcommaaccent -90 -KPX Atilde U -50 -KPX Atilde Uacute -50 -KPX Atilde Ucircumflex -50 -KPX Atilde Udieresis -50 -KPX Atilde Ugrave -50 -KPX Atilde Uhungarumlaut -50 -KPX Atilde Umacron -50 -KPX Atilde Uogonek -50 -KPX Atilde Uring -50 -KPX Atilde V -80 -KPX Atilde W -60 -KPX Atilde Y -110 -KPX Atilde Yacute -110 -KPX Atilde Ydieresis -110 -KPX Atilde u -30 -KPX Atilde uacute -30 -KPX Atilde ucircumflex -30 -KPX Atilde udieresis -30 -KPX Atilde ugrave -30 -KPX Atilde uhungarumlaut -30 -KPX Atilde umacron -30 -KPX Atilde uogonek -30 -KPX Atilde uring -30 -KPX Atilde v -40 -KPX Atilde w -30 -KPX Atilde y -30 -KPX Atilde yacute -30 -KPX Atilde ydieresis -30 -KPX B A -30 -KPX B Aacute -30 -KPX B Abreve -30 -KPX B Acircumflex -30 -KPX B Adieresis -30 -KPX B Agrave -30 -KPX B Amacron -30 -KPX B Aogonek -30 -KPX B Aring -30 -KPX B Atilde -30 -KPX B U -10 -KPX B Uacute -10 -KPX B Ucircumflex -10 -KPX B Udieresis -10 -KPX B Ugrave -10 -KPX B Uhungarumlaut -10 -KPX B Umacron -10 -KPX B Uogonek -10 -KPX B Uring -10 -KPX D A -40 -KPX D Aacute -40 -KPX D Abreve -40 -KPX D Acircumflex -40 -KPX D Adieresis -40 -KPX D Agrave -40 -KPX D Amacron -40 -KPX D Aogonek -40 -KPX D Aring -40 -KPX D Atilde -40 -KPX D V -40 -KPX D W -40 -KPX D Y -70 -KPX D Yacute -70 -KPX D Ydieresis -70 -KPX D comma -30 -KPX D period -30 -KPX Dcaron A -40 -KPX Dcaron Aacute -40 -KPX Dcaron Abreve -40 -KPX Dcaron Acircumflex -40 -KPX Dcaron Adieresis -40 -KPX Dcaron Agrave -40 -KPX Dcaron Amacron -40 -KPX Dcaron Aogonek -40 -KPX Dcaron Aring -40 -KPX Dcaron Atilde -40 -KPX Dcaron V -40 -KPX Dcaron W -40 -KPX Dcaron Y -70 -KPX Dcaron Yacute -70 -KPX Dcaron Ydieresis -70 -KPX Dcaron comma -30 -KPX Dcaron period -30 -KPX Dcroat A -40 -KPX Dcroat Aacute -40 -KPX Dcroat Abreve -40 -KPX Dcroat Acircumflex -40 -KPX Dcroat Adieresis -40 -KPX Dcroat Agrave -40 -KPX Dcroat Amacron -40 -KPX Dcroat Aogonek -40 -KPX Dcroat Aring -40 -KPX Dcroat Atilde -40 -KPX Dcroat V -40 -KPX Dcroat W -40 -KPX Dcroat Y -70 -KPX Dcroat Yacute -70 -KPX Dcroat Ydieresis -70 -KPX Dcroat comma -30 -KPX Dcroat period -30 -KPX F A -80 -KPX F Aacute -80 -KPX F Abreve -80 -KPX F Acircumflex -80 -KPX F Adieresis -80 -KPX F Agrave -80 -KPX F Amacron -80 -KPX F Aogonek -80 -KPX F Aring -80 -KPX F Atilde -80 -KPX F a -20 -KPX F aacute -20 -KPX F abreve -20 -KPX F acircumflex -20 -KPX F adieresis -20 -KPX F agrave -20 -KPX F amacron -20 -KPX F aogonek -20 -KPX F aring -20 -KPX F atilde -20 -KPX F comma -100 -KPX F period -100 -KPX J A -20 -KPX J Aacute -20 -KPX J Abreve -20 -KPX J Acircumflex -20 -KPX J Adieresis -20 -KPX J Agrave -20 -KPX J Amacron -20 -KPX J Aogonek -20 -KPX J Aring -20 -KPX J Atilde -20 -KPX J comma -20 -KPX J period -20 -KPX J u -20 -KPX J uacute -20 -KPX J ucircumflex -20 -KPX J udieresis -20 -KPX J ugrave -20 -KPX J uhungarumlaut -20 -KPX J umacron -20 -KPX J uogonek -20 -KPX J uring -20 -KPX K O -30 -KPX K Oacute -30 -KPX K Ocircumflex -30 -KPX K Odieresis -30 -KPX K Ograve -30 -KPX K Ohungarumlaut -30 -KPX K Omacron -30 -KPX K Oslash -30 -KPX K Otilde -30 -KPX K e -15 -KPX K eacute -15 -KPX K ecaron -15 -KPX K ecircumflex -15 -KPX K edieresis -15 -KPX K edotaccent -15 -KPX K egrave -15 -KPX K emacron -15 -KPX K eogonek -15 -KPX K o -35 -KPX K oacute -35 -KPX K ocircumflex -35 -KPX K odieresis -35 -KPX K ograve -35 -KPX K ohungarumlaut -35 -KPX K omacron -35 -KPX K oslash -35 -KPX K otilde -35 -KPX K u -30 -KPX K uacute -30 -KPX K ucircumflex -30 -KPX K udieresis -30 -KPX K ugrave -30 -KPX K uhungarumlaut -30 -KPX K umacron -30 -KPX K uogonek -30 -KPX K uring -30 -KPX K y -40 -KPX K yacute -40 -KPX K ydieresis -40 -KPX Kcommaaccent O -30 -KPX Kcommaaccent Oacute -30 -KPX Kcommaaccent Ocircumflex -30 -KPX Kcommaaccent Odieresis -30 -KPX Kcommaaccent Ograve -30 -KPX Kcommaaccent Ohungarumlaut -30 -KPX Kcommaaccent Omacron -30 -KPX Kcommaaccent Oslash -30 -KPX Kcommaaccent Otilde -30 -KPX Kcommaaccent e -15 -KPX Kcommaaccent eacute -15 -KPX Kcommaaccent ecaron -15 -KPX Kcommaaccent ecircumflex -15 -KPX Kcommaaccent edieresis -15 -KPX Kcommaaccent edotaccent -15 -KPX Kcommaaccent egrave -15 -KPX Kcommaaccent emacron -15 -KPX Kcommaaccent eogonek -15 -KPX Kcommaaccent o -35 -KPX Kcommaaccent oacute -35 -KPX Kcommaaccent ocircumflex -35 -KPX Kcommaaccent odieresis -35 -KPX Kcommaaccent ograve -35 -KPX Kcommaaccent ohungarumlaut -35 -KPX Kcommaaccent omacron -35 -KPX Kcommaaccent oslash -35 -KPX Kcommaaccent otilde -35 -KPX Kcommaaccent u -30 -KPX Kcommaaccent uacute -30 -KPX Kcommaaccent ucircumflex -30 -KPX Kcommaaccent udieresis -30 -KPX Kcommaaccent ugrave -30 -KPX Kcommaaccent uhungarumlaut -30 -KPX Kcommaaccent umacron -30 -KPX Kcommaaccent uogonek -30 -KPX Kcommaaccent uring -30 -KPX Kcommaaccent y -40 -KPX Kcommaaccent yacute -40 -KPX Kcommaaccent ydieresis -40 -KPX L T -90 -KPX L Tcaron -90 -KPX L Tcommaaccent -90 -KPX L V -110 -KPX L W -80 -KPX L Y -120 -KPX L Yacute -120 -KPX L Ydieresis -120 -KPX L quotedblright -140 -KPX L quoteright -140 -KPX L y -30 -KPX L yacute -30 -KPX L ydieresis -30 -KPX Lacute T -90 -KPX Lacute Tcaron -90 -KPX Lacute Tcommaaccent -90 -KPX Lacute V -110 -KPX Lacute W -80 -KPX Lacute Y -120 -KPX Lacute Yacute -120 -KPX Lacute Ydieresis -120 -KPX Lacute quotedblright -140 -KPX Lacute quoteright -140 -KPX Lacute y -30 -KPX Lacute yacute -30 -KPX Lacute ydieresis -30 -KPX Lcommaaccent T -90 -KPX Lcommaaccent Tcaron -90 -KPX Lcommaaccent Tcommaaccent -90 -KPX Lcommaaccent V -110 -KPX Lcommaaccent W -80 -KPX Lcommaaccent Y -120 -KPX Lcommaaccent Yacute -120 -KPX Lcommaaccent Ydieresis -120 -KPX Lcommaaccent quotedblright -140 -KPX Lcommaaccent quoteright -140 -KPX Lcommaaccent y -30 -KPX Lcommaaccent yacute -30 -KPX Lcommaaccent ydieresis -30 -KPX Lslash T -90 -KPX Lslash Tcaron -90 -KPX Lslash Tcommaaccent -90 -KPX Lslash V -110 -KPX Lslash W -80 -KPX Lslash Y -120 -KPX Lslash Yacute -120 -KPX Lslash Ydieresis -120 -KPX Lslash quotedblright -140 -KPX Lslash quoteright -140 -KPX Lslash y -30 -KPX Lslash yacute -30 -KPX Lslash ydieresis -30 -KPX O A -50 -KPX O Aacute -50 -KPX O Abreve -50 -KPX O Acircumflex -50 -KPX O Adieresis -50 -KPX O Agrave -50 -KPX O Amacron -50 -KPX O Aogonek -50 -KPX O Aring -50 -KPX O Atilde -50 -KPX O T -40 -KPX O Tcaron -40 -KPX O Tcommaaccent -40 -KPX O V -50 -KPX O W -50 -KPX O X -50 -KPX O Y -70 -KPX O Yacute -70 -KPX O Ydieresis -70 -KPX O comma -40 -KPX O period -40 -KPX Oacute A -50 -KPX Oacute Aacute -50 -KPX Oacute Abreve -50 -KPX Oacute Acircumflex -50 -KPX Oacute Adieresis -50 -KPX Oacute Agrave -50 -KPX Oacute Amacron -50 -KPX Oacute Aogonek -50 -KPX Oacute Aring -50 -KPX Oacute Atilde -50 -KPX Oacute T -40 -KPX Oacute Tcaron -40 -KPX Oacute Tcommaaccent -40 -KPX Oacute V -50 -KPX Oacute W -50 -KPX Oacute X -50 -KPX Oacute Y -70 -KPX Oacute Yacute -70 -KPX Oacute Ydieresis -70 -KPX Oacute comma -40 -KPX Oacute period -40 -KPX Ocircumflex A -50 -KPX Ocircumflex Aacute -50 -KPX Ocircumflex Abreve -50 -KPX Ocircumflex Acircumflex -50 -KPX Ocircumflex Adieresis -50 -KPX Ocircumflex Agrave -50 -KPX Ocircumflex Amacron -50 -KPX Ocircumflex Aogonek -50 -KPX Ocircumflex Aring -50 -KPX Ocircumflex Atilde -50 -KPX Ocircumflex T -40 -KPX Ocircumflex Tcaron -40 -KPX Ocircumflex Tcommaaccent -40 -KPX Ocircumflex V -50 -KPX Ocircumflex W -50 -KPX Ocircumflex X -50 -KPX Ocircumflex Y -70 -KPX Ocircumflex Yacute -70 -KPX Ocircumflex Ydieresis -70 -KPX Ocircumflex comma -40 -KPX Ocircumflex period -40 -KPX Odieresis A -50 -KPX Odieresis Aacute -50 -KPX Odieresis Abreve -50 -KPX Odieresis Acircumflex -50 -KPX Odieresis Adieresis -50 -KPX Odieresis Agrave -50 -KPX Odieresis Amacron -50 -KPX Odieresis Aogonek -50 -KPX Odieresis Aring -50 -KPX Odieresis Atilde -50 -KPX Odieresis T -40 -KPX Odieresis Tcaron -40 -KPX Odieresis Tcommaaccent -40 -KPX Odieresis V -50 -KPX Odieresis W -50 -KPX Odieresis X -50 -KPX Odieresis Y -70 -KPX Odieresis Yacute -70 -KPX Odieresis Ydieresis -70 -KPX Odieresis comma -40 -KPX Odieresis period -40 -KPX Ograve A -50 -KPX Ograve Aacute -50 -KPX Ograve Abreve -50 -KPX Ograve Acircumflex -50 -KPX Ograve Adieresis -50 -KPX Ograve Agrave -50 -KPX Ograve Amacron -50 -KPX Ograve Aogonek -50 -KPX Ograve Aring -50 -KPX Ograve Atilde -50 -KPX Ograve T -40 -KPX Ograve Tcaron -40 -KPX Ograve Tcommaaccent -40 -KPX Ograve V -50 -KPX Ograve W -50 -KPX Ograve X -50 -KPX Ograve Y -70 -KPX Ograve Yacute -70 -KPX Ograve Ydieresis -70 -KPX Ograve comma -40 -KPX Ograve period -40 -KPX Ohungarumlaut A -50 -KPX Ohungarumlaut Aacute -50 -KPX Ohungarumlaut Abreve -50 -KPX Ohungarumlaut Acircumflex -50 -KPX Ohungarumlaut Adieresis -50 -KPX Ohungarumlaut Agrave -50 -KPX Ohungarumlaut Amacron -50 -KPX Ohungarumlaut Aogonek -50 -KPX Ohungarumlaut Aring -50 -KPX Ohungarumlaut Atilde -50 -KPX Ohungarumlaut T -40 -KPX Ohungarumlaut Tcaron -40 -KPX Ohungarumlaut Tcommaaccent -40 -KPX Ohungarumlaut V -50 -KPX Ohungarumlaut W -50 -KPX Ohungarumlaut X -50 -KPX Ohungarumlaut Y -70 -KPX Ohungarumlaut Yacute -70 -KPX Ohungarumlaut Ydieresis -70 -KPX Ohungarumlaut comma -40 -KPX Ohungarumlaut period -40 -KPX Omacron A -50 -KPX Omacron Aacute -50 -KPX Omacron Abreve -50 -KPX Omacron Acircumflex -50 -KPX Omacron Adieresis -50 -KPX Omacron Agrave -50 -KPX Omacron Amacron -50 -KPX Omacron Aogonek -50 -KPX Omacron Aring -50 -KPX Omacron Atilde -50 -KPX Omacron T -40 -KPX Omacron Tcaron -40 -KPX Omacron Tcommaaccent -40 -KPX Omacron V -50 -KPX Omacron W -50 -KPX Omacron X -50 -KPX Omacron Y -70 -KPX Omacron Yacute -70 -KPX Omacron Ydieresis -70 -KPX Omacron comma -40 -KPX Omacron period -40 -KPX Oslash A -50 -KPX Oslash Aacute -50 -KPX Oslash Abreve -50 -KPX Oslash Acircumflex -50 -KPX Oslash Adieresis -50 -KPX Oslash Agrave -50 -KPX Oslash Amacron -50 -KPX Oslash Aogonek -50 -KPX Oslash Aring -50 -KPX Oslash Atilde -50 -KPX Oslash T -40 -KPX Oslash Tcaron -40 -KPX Oslash Tcommaaccent -40 -KPX Oslash V -50 -KPX Oslash W -50 -KPX Oslash X -50 -KPX Oslash Y -70 -KPX Oslash Yacute -70 -KPX Oslash Ydieresis -70 -KPX Oslash comma -40 -KPX Oslash period -40 -KPX Otilde A -50 -KPX Otilde Aacute -50 -KPX Otilde Abreve -50 -KPX Otilde Acircumflex -50 -KPX Otilde Adieresis -50 -KPX Otilde Agrave -50 -KPX Otilde Amacron -50 -KPX Otilde Aogonek -50 -KPX Otilde Aring -50 -KPX Otilde Atilde -50 -KPX Otilde T -40 -KPX Otilde Tcaron -40 -KPX Otilde Tcommaaccent -40 -KPX Otilde V -50 -KPX Otilde W -50 -KPX Otilde X -50 -KPX Otilde Y -70 -KPX Otilde Yacute -70 -KPX Otilde Ydieresis -70 -KPX Otilde comma -40 -KPX Otilde period -40 -KPX P A -100 -KPX P Aacute -100 -KPX P Abreve -100 -KPX P Acircumflex -100 -KPX P Adieresis -100 -KPX P Agrave -100 -KPX P Amacron -100 -KPX P Aogonek -100 -KPX P Aring -100 -KPX P Atilde -100 -KPX P a -30 -KPX P aacute -30 -KPX P abreve -30 -KPX P acircumflex -30 -KPX P adieresis -30 -KPX P agrave -30 -KPX P amacron -30 -KPX P aogonek -30 -KPX P aring -30 -KPX P atilde -30 -KPX P comma -120 -KPX P e -30 -KPX P eacute -30 -KPX P ecaron -30 -KPX P ecircumflex -30 -KPX P edieresis -30 -KPX P edotaccent -30 -KPX P egrave -30 -KPX P emacron -30 -KPX P eogonek -30 -KPX P o -40 -KPX P oacute -40 -KPX P ocircumflex -40 -KPX P odieresis -40 -KPX P ograve -40 -KPX P ohungarumlaut -40 -KPX P omacron -40 -KPX P oslash -40 -KPX P otilde -40 -KPX P period -120 -KPX Q U -10 -KPX Q Uacute -10 -KPX Q Ucircumflex -10 -KPX Q Udieresis -10 -KPX Q Ugrave -10 -KPX Q Uhungarumlaut -10 -KPX Q Umacron -10 -KPX Q Uogonek -10 -KPX Q Uring -10 -KPX Q comma 20 -KPX Q period 20 -KPX R O -20 -KPX R Oacute -20 -KPX R Ocircumflex -20 -KPX R Odieresis -20 -KPX R Ograve -20 -KPX R Ohungarumlaut -20 -KPX R Omacron -20 -KPX R Oslash -20 -KPX R Otilde -20 -KPX R T -20 -KPX R Tcaron -20 -KPX R Tcommaaccent -20 -KPX R U -20 -KPX R Uacute -20 -KPX R Ucircumflex -20 -KPX R Udieresis -20 -KPX R Ugrave -20 -KPX R Uhungarumlaut -20 -KPX R Umacron -20 -KPX R Uogonek -20 -KPX R Uring -20 -KPX R V -50 -KPX R W -40 -KPX R Y -50 -KPX R Yacute -50 -KPX R Ydieresis -50 -KPX Racute O -20 -KPX Racute Oacute -20 -KPX Racute Ocircumflex -20 -KPX Racute Odieresis -20 -KPX Racute Ograve -20 -KPX Racute Ohungarumlaut -20 -KPX Racute Omacron -20 -KPX Racute Oslash -20 -KPX Racute Otilde -20 -KPX Racute T -20 -KPX Racute Tcaron -20 -KPX Racute Tcommaaccent -20 -KPX Racute U -20 -KPX Racute Uacute -20 -KPX Racute Ucircumflex -20 -KPX Racute Udieresis -20 -KPX Racute Ugrave -20 -KPX Racute Uhungarumlaut -20 -KPX Racute Umacron -20 -KPX Racute Uogonek -20 -KPX Racute Uring -20 -KPX Racute V -50 -KPX Racute W -40 -KPX Racute Y -50 -KPX Racute Yacute -50 -KPX Racute Ydieresis -50 -KPX Rcaron O -20 -KPX Rcaron Oacute -20 -KPX Rcaron Ocircumflex -20 -KPX Rcaron Odieresis -20 -KPX Rcaron Ograve -20 -KPX Rcaron Ohungarumlaut -20 -KPX Rcaron Omacron -20 -KPX Rcaron Oslash -20 -KPX Rcaron Otilde -20 -KPX Rcaron T -20 -KPX Rcaron Tcaron -20 -KPX Rcaron Tcommaaccent -20 -KPX Rcaron U -20 -KPX Rcaron Uacute -20 -KPX Rcaron Ucircumflex -20 -KPX Rcaron Udieresis -20 -KPX Rcaron Ugrave -20 -KPX Rcaron Uhungarumlaut -20 -KPX Rcaron Umacron -20 -KPX Rcaron Uogonek -20 -KPX Rcaron Uring -20 -KPX Rcaron V -50 -KPX Rcaron W -40 -KPX Rcaron Y -50 -KPX Rcaron Yacute -50 -KPX Rcaron Ydieresis -50 -KPX Rcommaaccent O -20 -KPX Rcommaaccent Oacute -20 -KPX Rcommaaccent Ocircumflex -20 -KPX Rcommaaccent Odieresis -20 -KPX Rcommaaccent Ograve -20 -KPX Rcommaaccent Ohungarumlaut -20 -KPX Rcommaaccent Omacron -20 -KPX Rcommaaccent Oslash -20 -KPX Rcommaaccent Otilde -20 -KPX Rcommaaccent T -20 -KPX Rcommaaccent Tcaron -20 -KPX Rcommaaccent Tcommaaccent -20 -KPX Rcommaaccent U -20 -KPX Rcommaaccent Uacute -20 -KPX Rcommaaccent Ucircumflex -20 -KPX Rcommaaccent Udieresis -20 -KPX Rcommaaccent Ugrave -20 -KPX Rcommaaccent Uhungarumlaut -20 -KPX Rcommaaccent Umacron -20 -KPX Rcommaaccent Uogonek -20 -KPX Rcommaaccent Uring -20 -KPX Rcommaaccent V -50 -KPX Rcommaaccent W -40 -KPX Rcommaaccent Y -50 -KPX Rcommaaccent Yacute -50 -KPX Rcommaaccent Ydieresis -50 -KPX T A -90 -KPX T Aacute -90 -KPX T Abreve -90 -KPX T Acircumflex -90 -KPX T Adieresis -90 -KPX T Agrave -90 -KPX T Amacron -90 -KPX T Aogonek -90 -KPX T Aring -90 -KPX T Atilde -90 -KPX T O -40 -KPX T Oacute -40 -KPX T Ocircumflex -40 -KPX T Odieresis -40 -KPX T Ograve -40 -KPX T Ohungarumlaut -40 -KPX T Omacron -40 -KPX T Oslash -40 -KPX T Otilde -40 -KPX T a -80 -KPX T aacute -80 -KPX T abreve -80 -KPX T acircumflex -80 -KPX T adieresis -80 -KPX T agrave -80 -KPX T amacron -80 -KPX T aogonek -80 -KPX T aring -80 -KPX T atilde -80 -KPX T colon -40 -KPX T comma -80 -KPX T e -60 -KPX T eacute -60 -KPX T ecaron -60 -KPX T ecircumflex -60 -KPX T edieresis -60 -KPX T edotaccent -60 -KPX T egrave -60 -KPX T emacron -60 -KPX T eogonek -60 -KPX T hyphen -120 -KPX T o -80 -KPX T oacute -80 -KPX T ocircumflex -80 -KPX T odieresis -80 -KPX T ograve -80 -KPX T ohungarumlaut -80 -KPX T omacron -80 -KPX T oslash -80 -KPX T otilde -80 -KPX T period -80 -KPX T r -80 -KPX T racute -80 -KPX T rcommaaccent -80 -KPX T semicolon -40 -KPX T u -90 -KPX T uacute -90 -KPX T ucircumflex -90 -KPX T udieresis -90 -KPX T ugrave -90 -KPX T uhungarumlaut -90 -KPX T umacron -90 -KPX T uogonek -90 -KPX T uring -90 -KPX T w -60 -KPX T y -60 -KPX T yacute -60 -KPX T ydieresis -60 -KPX Tcaron A -90 -KPX Tcaron Aacute -90 -KPX Tcaron Abreve -90 -KPX Tcaron Acircumflex -90 -KPX Tcaron Adieresis -90 -KPX Tcaron Agrave -90 -KPX Tcaron Amacron -90 -KPX Tcaron Aogonek -90 -KPX Tcaron Aring -90 -KPX Tcaron Atilde -90 -KPX Tcaron O -40 -KPX Tcaron Oacute -40 -KPX Tcaron Ocircumflex -40 -KPX Tcaron Odieresis -40 -KPX Tcaron Ograve -40 -KPX Tcaron Ohungarumlaut -40 -KPX Tcaron Omacron -40 -KPX Tcaron Oslash -40 -KPX Tcaron Otilde -40 -KPX Tcaron a -80 -KPX Tcaron aacute -80 -KPX Tcaron abreve -80 -KPX Tcaron acircumflex -80 -KPX Tcaron adieresis -80 -KPX Tcaron agrave -80 -KPX Tcaron amacron -80 -KPX Tcaron aogonek -80 -KPX Tcaron aring -80 -KPX Tcaron atilde -80 -KPX Tcaron colon -40 -KPX Tcaron comma -80 -KPX Tcaron e -60 -KPX Tcaron eacute -60 -KPX Tcaron ecaron -60 -KPX Tcaron ecircumflex -60 -KPX Tcaron edieresis -60 -KPX Tcaron edotaccent -60 -KPX Tcaron egrave -60 -KPX Tcaron emacron -60 -KPX Tcaron eogonek -60 -KPX Tcaron hyphen -120 -KPX Tcaron o -80 -KPX Tcaron oacute -80 -KPX Tcaron ocircumflex -80 -KPX Tcaron odieresis -80 -KPX Tcaron ograve -80 -KPX Tcaron ohungarumlaut -80 -KPX Tcaron omacron -80 -KPX Tcaron oslash -80 -KPX Tcaron otilde -80 -KPX Tcaron period -80 -KPX Tcaron r -80 -KPX Tcaron racute -80 -KPX Tcaron rcommaaccent -80 -KPX Tcaron semicolon -40 -KPX Tcaron u -90 -KPX Tcaron uacute -90 -KPX Tcaron ucircumflex -90 -KPX Tcaron udieresis -90 -KPX Tcaron ugrave -90 -KPX Tcaron uhungarumlaut -90 -KPX Tcaron umacron -90 -KPX Tcaron uogonek -90 -KPX Tcaron uring -90 -KPX Tcaron w -60 -KPX Tcaron y -60 -KPX Tcaron yacute -60 -KPX Tcaron ydieresis -60 -KPX Tcommaaccent A -90 -KPX Tcommaaccent Aacute -90 -KPX Tcommaaccent Abreve -90 -KPX Tcommaaccent Acircumflex -90 -KPX Tcommaaccent Adieresis -90 -KPX Tcommaaccent Agrave -90 -KPX Tcommaaccent Amacron -90 -KPX Tcommaaccent Aogonek -90 -KPX Tcommaaccent Aring -90 -KPX Tcommaaccent Atilde -90 -KPX Tcommaaccent O -40 -KPX Tcommaaccent Oacute -40 -KPX Tcommaaccent Ocircumflex -40 -KPX Tcommaaccent Odieresis -40 -KPX Tcommaaccent Ograve -40 -KPX Tcommaaccent Ohungarumlaut -40 -KPX Tcommaaccent Omacron -40 -KPX Tcommaaccent Oslash -40 -KPX Tcommaaccent Otilde -40 -KPX Tcommaaccent a -80 -KPX Tcommaaccent aacute -80 -KPX Tcommaaccent abreve -80 -KPX Tcommaaccent acircumflex -80 -KPX Tcommaaccent adieresis -80 -KPX Tcommaaccent agrave -80 -KPX Tcommaaccent amacron -80 -KPX Tcommaaccent aogonek -80 -KPX Tcommaaccent aring -80 -KPX Tcommaaccent atilde -80 -KPX Tcommaaccent colon -40 -KPX Tcommaaccent comma -80 -KPX Tcommaaccent e -60 -KPX Tcommaaccent eacute -60 -KPX Tcommaaccent ecaron -60 -KPX Tcommaaccent ecircumflex -60 -KPX Tcommaaccent edieresis -60 -KPX Tcommaaccent edotaccent -60 -KPX Tcommaaccent egrave -60 -KPX Tcommaaccent emacron -60 -KPX Tcommaaccent eogonek -60 -KPX Tcommaaccent hyphen -120 -KPX Tcommaaccent o -80 -KPX Tcommaaccent oacute -80 -KPX Tcommaaccent ocircumflex -80 -KPX Tcommaaccent odieresis -80 -KPX Tcommaaccent ograve -80 -KPX Tcommaaccent ohungarumlaut -80 -KPX Tcommaaccent omacron -80 -KPX Tcommaaccent oslash -80 -KPX Tcommaaccent otilde -80 -KPX Tcommaaccent period -80 -KPX Tcommaaccent r -80 -KPX Tcommaaccent racute -80 -KPX Tcommaaccent rcommaaccent -80 -KPX Tcommaaccent semicolon -40 -KPX Tcommaaccent u -90 -KPX Tcommaaccent uacute -90 -KPX Tcommaaccent ucircumflex -90 -KPX Tcommaaccent udieresis -90 -KPX Tcommaaccent ugrave -90 -KPX Tcommaaccent uhungarumlaut -90 -KPX Tcommaaccent umacron -90 -KPX Tcommaaccent uogonek -90 -KPX Tcommaaccent uring -90 -KPX Tcommaaccent w -60 -KPX Tcommaaccent y -60 -KPX Tcommaaccent yacute -60 -KPX Tcommaaccent ydieresis -60 -KPX U A -50 -KPX U Aacute -50 -KPX U Abreve -50 -KPX U Acircumflex -50 -KPX U Adieresis -50 -KPX U Agrave -50 -KPX U Amacron -50 -KPX U Aogonek -50 -KPX U Aring -50 -KPX U Atilde -50 -KPX U comma -30 -KPX U period -30 -KPX Uacute A -50 -KPX Uacute Aacute -50 -KPX Uacute Abreve -50 -KPX Uacute Acircumflex -50 -KPX Uacute Adieresis -50 -KPX Uacute Agrave -50 -KPX Uacute Amacron -50 -KPX Uacute Aogonek -50 -KPX Uacute Aring -50 -KPX Uacute Atilde -50 -KPX Uacute comma -30 -KPX Uacute period -30 -KPX Ucircumflex A -50 -KPX Ucircumflex Aacute -50 -KPX Ucircumflex Abreve -50 -KPX Ucircumflex Acircumflex -50 -KPX Ucircumflex Adieresis -50 -KPX Ucircumflex Agrave -50 -KPX Ucircumflex Amacron -50 -KPX Ucircumflex Aogonek -50 -KPX Ucircumflex Aring -50 -KPX Ucircumflex Atilde -50 -KPX Ucircumflex comma -30 -KPX Ucircumflex period -30 -KPX Udieresis A -50 -KPX Udieresis Aacute -50 -KPX Udieresis Abreve -50 -KPX Udieresis Acircumflex -50 -KPX Udieresis Adieresis -50 -KPX Udieresis Agrave -50 -KPX Udieresis Amacron -50 -KPX Udieresis Aogonek -50 -KPX Udieresis Aring -50 -KPX Udieresis Atilde -50 -KPX Udieresis comma -30 -KPX Udieresis period -30 -KPX Ugrave A -50 -KPX Ugrave Aacute -50 -KPX Ugrave Abreve -50 -KPX Ugrave Acircumflex -50 -KPX Ugrave Adieresis -50 -KPX Ugrave Agrave -50 -KPX Ugrave Amacron -50 -KPX Ugrave Aogonek -50 -KPX Ugrave Aring -50 -KPX Ugrave Atilde -50 -KPX Ugrave comma -30 -KPX Ugrave period -30 -KPX Uhungarumlaut A -50 -KPX Uhungarumlaut Aacute -50 -KPX Uhungarumlaut Abreve -50 -KPX Uhungarumlaut Acircumflex -50 -KPX Uhungarumlaut Adieresis -50 -KPX Uhungarumlaut Agrave -50 -KPX Uhungarumlaut Amacron -50 -KPX Uhungarumlaut Aogonek -50 -KPX Uhungarumlaut Aring -50 -KPX Uhungarumlaut Atilde -50 -KPX Uhungarumlaut comma -30 -KPX Uhungarumlaut period -30 -KPX Umacron A -50 -KPX Umacron Aacute -50 -KPX Umacron Abreve -50 -KPX Umacron Acircumflex -50 -KPX Umacron Adieresis -50 -KPX Umacron Agrave -50 -KPX Umacron Amacron -50 -KPX Umacron Aogonek -50 -KPX Umacron Aring -50 -KPX Umacron Atilde -50 -KPX Umacron comma -30 -KPX Umacron period -30 -KPX Uogonek A -50 -KPX Uogonek Aacute -50 -KPX Uogonek Abreve -50 -KPX Uogonek Acircumflex -50 -KPX Uogonek Adieresis -50 -KPX Uogonek Agrave -50 -KPX Uogonek Amacron -50 -KPX Uogonek Aogonek -50 -KPX Uogonek Aring -50 -KPX Uogonek Atilde -50 -KPX Uogonek comma -30 -KPX Uogonek period -30 -KPX Uring A -50 -KPX Uring Aacute -50 -KPX Uring Abreve -50 -KPX Uring Acircumflex -50 -KPX Uring Adieresis -50 -KPX Uring Agrave -50 -KPX Uring Amacron -50 -KPX Uring Aogonek -50 -KPX Uring Aring -50 -KPX Uring Atilde -50 -KPX Uring comma -30 -KPX Uring period -30 -KPX V A -80 -KPX V Aacute -80 -KPX V Abreve -80 -KPX V Acircumflex -80 -KPX V Adieresis -80 -KPX V Agrave -80 -KPX V Amacron -80 -KPX V Aogonek -80 -KPX V Aring -80 -KPX V Atilde -80 -KPX V G -50 -KPX V Gbreve -50 -KPX V Gcommaaccent -50 -KPX V O -50 -KPX V Oacute -50 -KPX V Ocircumflex -50 -KPX V Odieresis -50 -KPX V Ograve -50 -KPX V Ohungarumlaut -50 -KPX V Omacron -50 -KPX V Oslash -50 -KPX V Otilde -50 -KPX V a -60 -KPX V aacute -60 -KPX V abreve -60 -KPX V acircumflex -60 -KPX V adieresis -60 -KPX V agrave -60 -KPX V amacron -60 -KPX V aogonek -60 -KPX V aring -60 -KPX V atilde -60 -KPX V colon -40 -KPX V comma -120 -KPX V e -50 -KPX V eacute -50 -KPX V ecaron -50 -KPX V ecircumflex -50 -KPX V edieresis -50 -KPX V edotaccent -50 -KPX V egrave -50 -KPX V emacron -50 -KPX V eogonek -50 -KPX V hyphen -80 -KPX V o -90 -KPX V oacute -90 -KPX V ocircumflex -90 -KPX V odieresis -90 -KPX V ograve -90 -KPX V ohungarumlaut -90 -KPX V omacron -90 -KPX V oslash -90 -KPX V otilde -90 -KPX V period -120 -KPX V semicolon -40 -KPX V u -60 -KPX V uacute -60 -KPX V ucircumflex -60 -KPX V udieresis -60 -KPX V ugrave -60 -KPX V uhungarumlaut -60 -KPX V umacron -60 -KPX V uogonek -60 -KPX V uring -60 -KPX W A -60 -KPX W Aacute -60 -KPX W Abreve -60 -KPX W Acircumflex -60 -KPX W Adieresis -60 -KPX W Agrave -60 -KPX W Amacron -60 -KPX W Aogonek -60 -KPX W Aring -60 -KPX W Atilde -60 -KPX W O -20 -KPX W Oacute -20 -KPX W Ocircumflex -20 -KPX W Odieresis -20 -KPX W Ograve -20 -KPX W Ohungarumlaut -20 -KPX W Omacron -20 -KPX W Oslash -20 -KPX W Otilde -20 -KPX W a -40 -KPX W aacute -40 -KPX W abreve -40 -KPX W acircumflex -40 -KPX W adieresis -40 -KPX W agrave -40 -KPX W amacron -40 -KPX W aogonek -40 -KPX W aring -40 -KPX W atilde -40 -KPX W colon -10 -KPX W comma -80 -KPX W e -35 -KPX W eacute -35 -KPX W ecaron -35 -KPX W ecircumflex -35 -KPX W edieresis -35 -KPX W edotaccent -35 -KPX W egrave -35 -KPX W emacron -35 -KPX W eogonek -35 -KPX W hyphen -40 -KPX W o -60 -KPX W oacute -60 -KPX W ocircumflex -60 -KPX W odieresis -60 -KPX W ograve -60 -KPX W ohungarumlaut -60 -KPX W omacron -60 -KPX W oslash -60 -KPX W otilde -60 -KPX W period -80 -KPX W semicolon -10 -KPX W u -45 -KPX W uacute -45 -KPX W ucircumflex -45 -KPX W udieresis -45 -KPX W ugrave -45 -KPX W uhungarumlaut -45 -KPX W umacron -45 -KPX W uogonek -45 -KPX W uring -45 -KPX W y -20 -KPX W yacute -20 -KPX W ydieresis -20 -KPX Y A -110 -KPX Y Aacute -110 -KPX Y Abreve -110 -KPX Y Acircumflex -110 -KPX Y Adieresis -110 -KPX Y Agrave -110 -KPX Y Amacron -110 -KPX Y Aogonek -110 -KPX Y Aring -110 -KPX Y Atilde -110 -KPX Y O -70 -KPX Y Oacute -70 -KPX Y Ocircumflex -70 -KPX Y Odieresis -70 -KPX Y Ograve -70 -KPX Y Ohungarumlaut -70 -KPX Y Omacron -70 -KPX Y Oslash -70 -KPX Y Otilde -70 -KPX Y a -90 -KPX Y aacute -90 -KPX Y abreve -90 -KPX Y acircumflex -90 -KPX Y adieresis -90 -KPX Y agrave -90 -KPX Y amacron -90 -KPX Y aogonek -90 -KPX Y aring -90 -KPX Y atilde -90 -KPX Y colon -50 -KPX Y comma -100 -KPX Y e -80 -KPX Y eacute -80 -KPX Y ecaron -80 -KPX Y ecircumflex -80 -KPX Y edieresis -80 -KPX Y edotaccent -80 -KPX Y egrave -80 -KPX Y emacron -80 -KPX Y eogonek -80 -KPX Y o -100 -KPX Y oacute -100 -KPX Y ocircumflex -100 -KPX Y odieresis -100 -KPX Y ograve -100 -KPX Y ohungarumlaut -100 -KPX Y omacron -100 -KPX Y oslash -100 -KPX Y otilde -100 -KPX Y period -100 -KPX Y semicolon -50 -KPX Y u -100 -KPX Y uacute -100 -KPX Y ucircumflex -100 -KPX Y udieresis -100 -KPX Y ugrave -100 -KPX Y uhungarumlaut -100 -KPX Y umacron -100 -KPX Y uogonek -100 -KPX Y uring -100 -KPX Yacute A -110 -KPX Yacute Aacute -110 -KPX Yacute Abreve -110 -KPX Yacute Acircumflex -110 -KPX Yacute Adieresis -110 -KPX Yacute Agrave -110 -KPX Yacute Amacron -110 -KPX Yacute Aogonek -110 -KPX Yacute Aring -110 -KPX Yacute Atilde -110 -KPX Yacute O -70 -KPX Yacute Oacute -70 -KPX Yacute Ocircumflex -70 -KPX Yacute Odieresis -70 -KPX Yacute Ograve -70 -KPX Yacute Ohungarumlaut -70 -KPX Yacute Omacron -70 -KPX Yacute Oslash -70 -KPX Yacute Otilde -70 -KPX Yacute a -90 -KPX Yacute aacute -90 -KPX Yacute abreve -90 -KPX Yacute acircumflex -90 -KPX Yacute adieresis -90 -KPX Yacute agrave -90 -KPX Yacute amacron -90 -KPX Yacute aogonek -90 -KPX Yacute aring -90 -KPX Yacute atilde -90 -KPX Yacute colon -50 -KPX Yacute comma -100 -KPX Yacute e -80 -KPX Yacute eacute -80 -KPX Yacute ecaron -80 -KPX Yacute ecircumflex -80 -KPX Yacute edieresis -80 -KPX Yacute edotaccent -80 -KPX Yacute egrave -80 -KPX Yacute emacron -80 -KPX Yacute eogonek -80 -KPX Yacute o -100 -KPX Yacute oacute -100 -KPX Yacute ocircumflex -100 -KPX Yacute odieresis -100 -KPX Yacute ograve -100 -KPX Yacute ohungarumlaut -100 -KPX Yacute omacron -100 -KPX Yacute oslash -100 -KPX Yacute otilde -100 -KPX Yacute period -100 -KPX Yacute semicolon -50 -KPX Yacute u -100 -KPX Yacute uacute -100 -KPX Yacute ucircumflex -100 -KPX Yacute udieresis -100 -KPX Yacute ugrave -100 -KPX Yacute uhungarumlaut -100 -KPX Yacute umacron -100 -KPX Yacute uogonek -100 -KPX Yacute uring -100 -KPX Ydieresis A -110 -KPX Ydieresis Aacute -110 -KPX Ydieresis Abreve -110 -KPX Ydieresis Acircumflex -110 -KPX Ydieresis Adieresis -110 -KPX Ydieresis Agrave -110 -KPX Ydieresis Amacron -110 -KPX Ydieresis Aogonek -110 -KPX Ydieresis Aring -110 -KPX Ydieresis Atilde -110 -KPX Ydieresis O -70 -KPX Ydieresis Oacute -70 -KPX Ydieresis Ocircumflex -70 -KPX Ydieresis Odieresis -70 -KPX Ydieresis Ograve -70 -KPX Ydieresis Ohungarumlaut -70 -KPX Ydieresis Omacron -70 -KPX Ydieresis Oslash -70 -KPX Ydieresis Otilde -70 -KPX Ydieresis a -90 -KPX Ydieresis aacute -90 -KPX Ydieresis abreve -90 -KPX Ydieresis acircumflex -90 -KPX Ydieresis adieresis -90 -KPX Ydieresis agrave -90 -KPX Ydieresis amacron -90 -KPX Ydieresis aogonek -90 -KPX Ydieresis aring -90 -KPX Ydieresis atilde -90 -KPX Ydieresis colon -50 -KPX Ydieresis comma -100 -KPX Ydieresis e -80 -KPX Ydieresis eacute -80 -KPX Ydieresis ecaron -80 -KPX Ydieresis ecircumflex -80 -KPX Ydieresis edieresis -80 -KPX Ydieresis edotaccent -80 -KPX Ydieresis egrave -80 -KPX Ydieresis emacron -80 -KPX Ydieresis eogonek -80 -KPX Ydieresis o -100 -KPX Ydieresis oacute -100 -KPX Ydieresis ocircumflex -100 -KPX Ydieresis odieresis -100 -KPX Ydieresis ograve -100 -KPX Ydieresis ohungarumlaut -100 -KPX Ydieresis omacron -100 -KPX Ydieresis oslash -100 -KPX Ydieresis otilde -100 -KPX Ydieresis period -100 -KPX Ydieresis semicolon -50 -KPX Ydieresis u -100 -KPX Ydieresis uacute -100 -KPX Ydieresis ucircumflex -100 -KPX Ydieresis udieresis -100 -KPX Ydieresis ugrave -100 -KPX Ydieresis uhungarumlaut -100 -KPX Ydieresis umacron -100 -KPX Ydieresis uogonek -100 -KPX Ydieresis uring -100 -KPX a g -10 -KPX a gbreve -10 -KPX a gcommaaccent -10 -KPX a v -15 -KPX a w -15 -KPX a y -20 -KPX a yacute -20 -KPX a ydieresis -20 -KPX aacute g -10 -KPX aacute gbreve -10 -KPX aacute gcommaaccent -10 -KPX aacute v -15 -KPX aacute w -15 -KPX aacute y -20 -KPX aacute yacute -20 -KPX aacute ydieresis -20 -KPX abreve g -10 -KPX abreve gbreve -10 -KPX abreve gcommaaccent -10 -KPX abreve v -15 -KPX abreve w -15 -KPX abreve y -20 -KPX abreve yacute -20 -KPX abreve ydieresis -20 -KPX acircumflex g -10 -KPX acircumflex gbreve -10 -KPX acircumflex gcommaaccent -10 -KPX acircumflex v -15 -KPX acircumflex w -15 -KPX acircumflex y -20 -KPX acircumflex yacute -20 -KPX acircumflex ydieresis -20 -KPX adieresis g -10 -KPX adieresis gbreve -10 -KPX adieresis gcommaaccent -10 -KPX adieresis v -15 -KPX adieresis w -15 -KPX adieresis y -20 -KPX adieresis yacute -20 -KPX adieresis ydieresis -20 -KPX agrave g -10 -KPX agrave gbreve -10 -KPX agrave gcommaaccent -10 -KPX agrave v -15 -KPX agrave w -15 -KPX agrave y -20 -KPX agrave yacute -20 -KPX agrave ydieresis -20 -KPX amacron g -10 -KPX amacron gbreve -10 -KPX amacron gcommaaccent -10 -KPX amacron v -15 -KPX amacron w -15 -KPX amacron y -20 -KPX amacron yacute -20 -KPX amacron ydieresis -20 -KPX aogonek g -10 -KPX aogonek gbreve -10 -KPX aogonek gcommaaccent -10 -KPX aogonek v -15 -KPX aogonek w -15 -KPX aogonek y -20 -KPX aogonek yacute -20 -KPX aogonek ydieresis -20 -KPX aring g -10 -KPX aring gbreve -10 -KPX aring gcommaaccent -10 -KPX aring v -15 -KPX aring w -15 -KPX aring y -20 -KPX aring yacute -20 -KPX aring ydieresis -20 -KPX atilde g -10 -KPX atilde gbreve -10 -KPX atilde gcommaaccent -10 -KPX atilde v -15 -KPX atilde w -15 -KPX atilde y -20 -KPX atilde yacute -20 -KPX atilde ydieresis -20 -KPX b l -10 -KPX b lacute -10 -KPX b lcommaaccent -10 -KPX b lslash -10 -KPX b u -20 -KPX b uacute -20 -KPX b ucircumflex -20 -KPX b udieresis -20 -KPX b ugrave -20 -KPX b uhungarumlaut -20 -KPX b umacron -20 -KPX b uogonek -20 -KPX b uring -20 -KPX b v -20 -KPX b y -20 -KPX b yacute -20 -KPX b ydieresis -20 -KPX c h -10 -KPX c k -20 -KPX c kcommaaccent -20 -KPX c l -20 -KPX c lacute -20 -KPX c lcommaaccent -20 -KPX c lslash -20 -KPX c y -10 -KPX c yacute -10 -KPX c ydieresis -10 -KPX cacute h -10 -KPX cacute k -20 -KPX cacute kcommaaccent -20 -KPX cacute l -20 -KPX cacute lacute -20 -KPX cacute lcommaaccent -20 -KPX cacute lslash -20 -KPX cacute y -10 -KPX cacute yacute -10 -KPX cacute ydieresis -10 -KPX ccaron h -10 -KPX ccaron k -20 -KPX ccaron kcommaaccent -20 -KPX ccaron l -20 -KPX ccaron lacute -20 -KPX ccaron lcommaaccent -20 -KPX ccaron lslash -20 -KPX ccaron y -10 -KPX ccaron yacute -10 -KPX ccaron ydieresis -10 -KPX ccedilla h -10 -KPX ccedilla k -20 -KPX ccedilla kcommaaccent -20 -KPX ccedilla l -20 -KPX ccedilla lacute -20 -KPX ccedilla lcommaaccent -20 -KPX ccedilla lslash -20 -KPX ccedilla y -10 -KPX ccedilla yacute -10 -KPX ccedilla ydieresis -10 -KPX colon space -40 -KPX comma quotedblright -120 -KPX comma quoteright -120 -KPX comma space -40 -KPX d d -10 -KPX d dcroat -10 -KPX d v -15 -KPX d w -15 -KPX d y -15 -KPX d yacute -15 -KPX d ydieresis -15 -KPX dcroat d -10 -KPX dcroat dcroat -10 -KPX dcroat v -15 -KPX dcroat w -15 -KPX dcroat y -15 -KPX dcroat yacute -15 -KPX dcroat ydieresis -15 -KPX e comma 10 -KPX e period 20 -KPX e v -15 -KPX e w -15 -KPX e x -15 -KPX e y -15 -KPX e yacute -15 -KPX e ydieresis -15 -KPX eacute comma 10 -KPX eacute period 20 -KPX eacute v -15 -KPX eacute w -15 -KPX eacute x -15 -KPX eacute y -15 -KPX eacute yacute -15 -KPX eacute ydieresis -15 -KPX ecaron comma 10 -KPX ecaron period 20 -KPX ecaron v -15 -KPX ecaron w -15 -KPX ecaron x -15 -KPX ecaron y -15 -KPX ecaron yacute -15 -KPX ecaron ydieresis -15 -KPX ecircumflex comma 10 -KPX ecircumflex period 20 -KPX ecircumflex v -15 -KPX ecircumflex w -15 -KPX ecircumflex x -15 -KPX ecircumflex y -15 -KPX ecircumflex yacute -15 -KPX ecircumflex ydieresis -15 -KPX edieresis comma 10 -KPX edieresis period 20 -KPX edieresis v -15 -KPX edieresis w -15 -KPX edieresis x -15 -KPX edieresis y -15 -KPX edieresis yacute -15 -KPX edieresis ydieresis -15 -KPX edotaccent comma 10 -KPX edotaccent period 20 -KPX edotaccent v -15 -KPX edotaccent w -15 -KPX edotaccent x -15 -KPX edotaccent y -15 -KPX edotaccent yacute -15 -KPX edotaccent ydieresis -15 -KPX egrave comma 10 -KPX egrave period 20 -KPX egrave v -15 -KPX egrave w -15 -KPX egrave x -15 -KPX egrave y -15 -KPX egrave yacute -15 -KPX egrave ydieresis -15 -KPX emacron comma 10 -KPX emacron period 20 -KPX emacron v -15 -KPX emacron w -15 -KPX emacron x -15 -KPX emacron y -15 -KPX emacron yacute -15 -KPX emacron ydieresis -15 -KPX eogonek comma 10 -KPX eogonek period 20 -KPX eogonek v -15 -KPX eogonek w -15 -KPX eogonek x -15 -KPX eogonek y -15 -KPX eogonek yacute -15 -KPX eogonek ydieresis -15 -KPX f comma -10 -KPX f e -10 -KPX f eacute -10 -KPX f ecaron -10 -KPX f ecircumflex -10 -KPX f edieresis -10 -KPX f edotaccent -10 -KPX f egrave -10 -KPX f emacron -10 -KPX f eogonek -10 -KPX f o -20 -KPX f oacute -20 -KPX f ocircumflex -20 -KPX f odieresis -20 -KPX f ograve -20 -KPX f ohungarumlaut -20 -KPX f omacron -20 -KPX f oslash -20 -KPX f otilde -20 -KPX f period -10 -KPX f quotedblright 30 -KPX f quoteright 30 -KPX g e 10 -KPX g eacute 10 -KPX g ecaron 10 -KPX g ecircumflex 10 -KPX g edieresis 10 -KPX g edotaccent 10 -KPX g egrave 10 -KPX g emacron 10 -KPX g eogonek 10 -KPX g g -10 -KPX g gbreve -10 -KPX g gcommaaccent -10 -KPX gbreve e 10 -KPX gbreve eacute 10 -KPX gbreve ecaron 10 -KPX gbreve ecircumflex 10 -KPX gbreve edieresis 10 -KPX gbreve edotaccent 10 -KPX gbreve egrave 10 -KPX gbreve emacron 10 -KPX gbreve eogonek 10 -KPX gbreve g -10 -KPX gbreve gbreve -10 -KPX gbreve gcommaaccent -10 -KPX gcommaaccent e 10 -KPX gcommaaccent eacute 10 -KPX gcommaaccent ecaron 10 -KPX gcommaaccent ecircumflex 10 -KPX gcommaaccent edieresis 10 -KPX gcommaaccent edotaccent 10 -KPX gcommaaccent egrave 10 -KPX gcommaaccent emacron 10 -KPX gcommaaccent eogonek 10 -KPX gcommaaccent g -10 -KPX gcommaaccent gbreve -10 -KPX gcommaaccent gcommaaccent -10 -KPX h y -20 -KPX h yacute -20 -KPX h ydieresis -20 -KPX k o -15 -KPX k oacute -15 -KPX k ocircumflex -15 -KPX k odieresis -15 -KPX k ograve -15 -KPX k ohungarumlaut -15 -KPX k omacron -15 -KPX k oslash -15 -KPX k otilde -15 -KPX kcommaaccent o -15 -KPX kcommaaccent oacute -15 -KPX kcommaaccent ocircumflex -15 -KPX kcommaaccent odieresis -15 -KPX kcommaaccent ograve -15 -KPX kcommaaccent ohungarumlaut -15 -KPX kcommaaccent omacron -15 -KPX kcommaaccent oslash -15 -KPX kcommaaccent otilde -15 -KPX l w -15 -KPX l y -15 -KPX l yacute -15 -KPX l ydieresis -15 -KPX lacute w -15 -KPX lacute y -15 -KPX lacute yacute -15 -KPX lacute ydieresis -15 -KPX lcommaaccent w -15 -KPX lcommaaccent y -15 -KPX lcommaaccent yacute -15 -KPX lcommaaccent ydieresis -15 -KPX lslash w -15 -KPX lslash y -15 -KPX lslash yacute -15 -KPX lslash ydieresis -15 -KPX m u -20 -KPX m uacute -20 -KPX m ucircumflex -20 -KPX m udieresis -20 -KPX m ugrave -20 -KPX m uhungarumlaut -20 -KPX m umacron -20 -KPX m uogonek -20 -KPX m uring -20 -KPX m y -30 -KPX m yacute -30 -KPX m ydieresis -30 -KPX n u -10 -KPX n uacute -10 -KPX n ucircumflex -10 -KPX n udieresis -10 -KPX n ugrave -10 -KPX n uhungarumlaut -10 -KPX n umacron -10 -KPX n uogonek -10 -KPX n uring -10 -KPX n v -40 -KPX n y -20 -KPX n yacute -20 -KPX n ydieresis -20 -KPX nacute u -10 -KPX nacute uacute -10 -KPX nacute ucircumflex -10 -KPX nacute udieresis -10 -KPX nacute ugrave -10 -KPX nacute uhungarumlaut -10 -KPX nacute umacron -10 -KPX nacute uogonek -10 -KPX nacute uring -10 -KPX nacute v -40 -KPX nacute y -20 -KPX nacute yacute -20 -KPX nacute ydieresis -20 -KPX ncaron u -10 -KPX ncaron uacute -10 -KPX ncaron ucircumflex -10 -KPX ncaron udieresis -10 -KPX ncaron ugrave -10 -KPX ncaron uhungarumlaut -10 -KPX ncaron umacron -10 -KPX ncaron uogonek -10 -KPX ncaron uring -10 -KPX ncaron v -40 -KPX ncaron y -20 -KPX ncaron yacute -20 -KPX ncaron ydieresis -20 -KPX ncommaaccent u -10 -KPX ncommaaccent uacute -10 -KPX ncommaaccent ucircumflex -10 -KPX ncommaaccent udieresis -10 -KPX ncommaaccent ugrave -10 -KPX ncommaaccent uhungarumlaut -10 -KPX ncommaaccent umacron -10 -KPX ncommaaccent uogonek -10 -KPX ncommaaccent uring -10 -KPX ncommaaccent v -40 -KPX ncommaaccent y -20 -KPX ncommaaccent yacute -20 -KPX ncommaaccent ydieresis -20 -KPX ntilde u -10 -KPX ntilde uacute -10 -KPX ntilde ucircumflex -10 -KPX ntilde udieresis -10 -KPX ntilde ugrave -10 -KPX ntilde uhungarumlaut -10 -KPX ntilde umacron -10 -KPX ntilde uogonek -10 -KPX ntilde uring -10 -KPX ntilde v -40 -KPX ntilde y -20 -KPX ntilde yacute -20 -KPX ntilde ydieresis -20 -KPX o v -20 -KPX o w -15 -KPX o x -30 -KPX o y -20 -KPX o yacute -20 -KPX o ydieresis -20 -KPX oacute v -20 -KPX oacute w -15 -KPX oacute x -30 -KPX oacute y -20 -KPX oacute yacute -20 -KPX oacute ydieresis -20 -KPX ocircumflex v -20 -KPX ocircumflex w -15 -KPX ocircumflex x -30 -KPX ocircumflex y -20 -KPX ocircumflex yacute -20 -KPX ocircumflex ydieresis -20 -KPX odieresis v -20 -KPX odieresis w -15 -KPX odieresis x -30 -KPX odieresis y -20 -KPX odieresis yacute -20 -KPX odieresis ydieresis -20 -KPX ograve v -20 -KPX ograve w -15 -KPX ograve x -30 -KPX ograve y -20 -KPX ograve yacute -20 -KPX ograve ydieresis -20 -KPX ohungarumlaut v -20 -KPX ohungarumlaut w -15 -KPX ohungarumlaut x -30 -KPX ohungarumlaut y -20 -KPX ohungarumlaut yacute -20 -KPX ohungarumlaut ydieresis -20 -KPX omacron v -20 -KPX omacron w -15 -KPX omacron x -30 -KPX omacron y -20 -KPX omacron yacute -20 -KPX omacron ydieresis -20 -KPX oslash v -20 -KPX oslash w -15 -KPX oslash x -30 -KPX oslash y -20 -KPX oslash yacute -20 -KPX oslash ydieresis -20 -KPX otilde v -20 -KPX otilde w -15 -KPX otilde x -30 -KPX otilde y -20 -KPX otilde yacute -20 -KPX otilde ydieresis -20 -KPX p y -15 -KPX p yacute -15 -KPX p ydieresis -15 -KPX period quotedblright -120 -KPX period quoteright -120 -KPX period space -40 -KPX quotedblright space -80 -KPX quoteleft quoteleft -46 -KPX quoteright d -80 -KPX quoteright dcroat -80 -KPX quoteright l -20 -KPX quoteright lacute -20 -KPX quoteright lcommaaccent -20 -KPX quoteright lslash -20 -KPX quoteright quoteright -46 -KPX quoteright r -40 -KPX quoteright racute -40 -KPX quoteright rcaron -40 -KPX quoteright rcommaaccent -40 -KPX quoteright s -60 -KPX quoteright sacute -60 -KPX quoteright scaron -60 -KPX quoteright scedilla -60 -KPX quoteright scommaaccent -60 -KPX quoteright space -80 -KPX quoteright v -20 -KPX r c -20 -KPX r cacute -20 -KPX r ccaron -20 -KPX r ccedilla -20 -KPX r comma -60 -KPX r d -20 -KPX r dcroat -20 -KPX r g -15 -KPX r gbreve -15 -KPX r gcommaaccent -15 -KPX r hyphen -20 -KPX r o -20 -KPX r oacute -20 -KPX r ocircumflex -20 -KPX r odieresis -20 -KPX r ograve -20 -KPX r ohungarumlaut -20 -KPX r omacron -20 -KPX r oslash -20 -KPX r otilde -20 -KPX r period -60 -KPX r q -20 -KPX r s -15 -KPX r sacute -15 -KPX r scaron -15 -KPX r scedilla -15 -KPX r scommaaccent -15 -KPX r t 20 -KPX r tcommaaccent 20 -KPX r v 10 -KPX r y 10 -KPX r yacute 10 -KPX r ydieresis 10 -KPX racute c -20 -KPX racute cacute -20 -KPX racute ccaron -20 -KPX racute ccedilla -20 -KPX racute comma -60 -KPX racute d -20 -KPX racute dcroat -20 -KPX racute g -15 -KPX racute gbreve -15 -KPX racute gcommaaccent -15 -KPX racute hyphen -20 -KPX racute o -20 -KPX racute oacute -20 -KPX racute ocircumflex -20 -KPX racute odieresis -20 -KPX racute ograve -20 -KPX racute ohungarumlaut -20 -KPX racute omacron -20 -KPX racute oslash -20 -KPX racute otilde -20 -KPX racute period -60 -KPX racute q -20 -KPX racute s -15 -KPX racute sacute -15 -KPX racute scaron -15 -KPX racute scedilla -15 -KPX racute scommaaccent -15 -KPX racute t 20 -KPX racute tcommaaccent 20 -KPX racute v 10 -KPX racute y 10 -KPX racute yacute 10 -KPX racute ydieresis 10 -KPX rcaron c -20 -KPX rcaron cacute -20 -KPX rcaron ccaron -20 -KPX rcaron ccedilla -20 -KPX rcaron comma -60 -KPX rcaron d -20 -KPX rcaron dcroat -20 -KPX rcaron g -15 -KPX rcaron gbreve -15 -KPX rcaron gcommaaccent -15 -KPX rcaron hyphen -20 -KPX rcaron o -20 -KPX rcaron oacute -20 -KPX rcaron ocircumflex -20 -KPX rcaron odieresis -20 -KPX rcaron ograve -20 -KPX rcaron ohungarumlaut -20 -KPX rcaron omacron -20 -KPX rcaron oslash -20 -KPX rcaron otilde -20 -KPX rcaron period -60 -KPX rcaron q -20 -KPX rcaron s -15 -KPX rcaron sacute -15 -KPX rcaron scaron -15 -KPX rcaron scedilla -15 -KPX rcaron scommaaccent -15 -KPX rcaron t 20 -KPX rcaron tcommaaccent 20 -KPX rcaron v 10 -KPX rcaron y 10 -KPX rcaron yacute 10 -KPX rcaron ydieresis 10 -KPX rcommaaccent c -20 -KPX rcommaaccent cacute -20 -KPX rcommaaccent ccaron -20 -KPX rcommaaccent ccedilla -20 -KPX rcommaaccent comma -60 -KPX rcommaaccent d -20 -KPX rcommaaccent dcroat -20 -KPX rcommaaccent g -15 -KPX rcommaaccent gbreve -15 -KPX rcommaaccent gcommaaccent -15 -KPX rcommaaccent hyphen -20 -KPX rcommaaccent o -20 -KPX rcommaaccent oacute -20 -KPX rcommaaccent ocircumflex -20 -KPX rcommaaccent odieresis -20 -KPX rcommaaccent ograve -20 -KPX rcommaaccent ohungarumlaut -20 -KPX rcommaaccent omacron -20 -KPX rcommaaccent oslash -20 -KPX rcommaaccent otilde -20 -KPX rcommaaccent period -60 -KPX rcommaaccent q -20 -KPX rcommaaccent s -15 -KPX rcommaaccent sacute -15 -KPX rcommaaccent scaron -15 -KPX rcommaaccent scedilla -15 -KPX rcommaaccent scommaaccent -15 -KPX rcommaaccent t 20 -KPX rcommaaccent tcommaaccent 20 -KPX rcommaaccent v 10 -KPX rcommaaccent y 10 -KPX rcommaaccent yacute 10 -KPX rcommaaccent ydieresis 10 -KPX s w -15 -KPX sacute w -15 -KPX scaron w -15 -KPX scedilla w -15 -KPX scommaaccent w -15 -KPX semicolon space -40 -KPX space T -100 -KPX space Tcaron -100 -KPX space Tcommaaccent -100 -KPX space V -80 -KPX space W -80 -KPX space Y -120 -KPX space Yacute -120 -KPX space Ydieresis -120 -KPX space quotedblleft -80 -KPX space quoteleft -60 -KPX v a -20 -KPX v aacute -20 -KPX v abreve -20 -KPX v acircumflex -20 -KPX v adieresis -20 -KPX v agrave -20 -KPX v amacron -20 -KPX v aogonek -20 -KPX v aring -20 -KPX v atilde -20 -KPX v comma -80 -KPX v o -30 -KPX v oacute -30 -KPX v ocircumflex -30 -KPX v odieresis -30 -KPX v ograve -30 -KPX v ohungarumlaut -30 -KPX v omacron -30 -KPX v oslash -30 -KPX v otilde -30 -KPX v period -80 -KPX w comma -40 -KPX w o -20 -KPX w oacute -20 -KPX w ocircumflex -20 -KPX w odieresis -20 -KPX w ograve -20 -KPX w ohungarumlaut -20 -KPX w omacron -20 -KPX w oslash -20 -KPX w otilde -20 -KPX w period -40 -KPX x e -10 -KPX x eacute -10 -KPX x ecaron -10 -KPX x ecircumflex -10 -KPX x edieresis -10 -KPX x edotaccent -10 -KPX x egrave -10 -KPX x emacron -10 -KPX x eogonek -10 -KPX y a -30 -KPX y aacute -30 -KPX y abreve -30 -KPX y acircumflex -30 -KPX y adieresis -30 -KPX y agrave -30 -KPX y amacron -30 -KPX y aogonek -30 -KPX y aring -30 -KPX y atilde -30 -KPX y comma -80 -KPX y e -10 -KPX y eacute -10 -KPX y ecaron -10 -KPX y ecircumflex -10 -KPX y edieresis -10 -KPX y edotaccent -10 -KPX y egrave -10 -KPX y emacron -10 -KPX y eogonek -10 -KPX y o -25 -KPX y oacute -25 -KPX y ocircumflex -25 -KPX y odieresis -25 -KPX y ograve -25 -KPX y ohungarumlaut -25 -KPX y omacron -25 -KPX y oslash -25 -KPX y otilde -25 -KPX y period -80 -KPX yacute a -30 -KPX yacute aacute -30 -KPX yacute abreve -30 -KPX yacute acircumflex -30 -KPX yacute adieresis -30 -KPX yacute agrave -30 -KPX yacute amacron -30 -KPX yacute aogonek -30 -KPX yacute aring -30 -KPX yacute atilde -30 -KPX yacute comma -80 -KPX yacute e -10 -KPX yacute eacute -10 -KPX yacute ecaron -10 -KPX yacute ecircumflex -10 -KPX yacute edieresis -10 -KPX yacute edotaccent -10 -KPX yacute egrave -10 -KPX yacute emacron -10 -KPX yacute eogonek -10 -KPX yacute o -25 -KPX yacute oacute -25 -KPX yacute ocircumflex -25 -KPX yacute odieresis -25 -KPX yacute ograve -25 -KPX yacute ohungarumlaut -25 -KPX yacute omacron -25 -KPX yacute oslash -25 -KPX yacute otilde -25 -KPX yacute period -80 -KPX ydieresis a -30 -KPX ydieresis aacute -30 -KPX ydieresis abreve -30 -KPX ydieresis acircumflex -30 -KPX ydieresis adieresis -30 -KPX ydieresis agrave -30 -KPX ydieresis amacron -30 -KPX ydieresis aogonek -30 -KPX ydieresis aring -30 -KPX ydieresis atilde -30 -KPX ydieresis comma -80 -KPX ydieresis e -10 -KPX ydieresis eacute -10 -KPX ydieresis ecaron -10 -KPX ydieresis ecircumflex -10 -KPX ydieresis edieresis -10 -KPX ydieresis edotaccent -10 -KPX ydieresis egrave -10 -KPX ydieresis emacron -10 -KPX ydieresis eogonek -10 -KPX ydieresis o -25 -KPX ydieresis oacute -25 -KPX ydieresis ocircumflex -25 -KPX ydieresis odieresis -25 -KPX ydieresis ograve -25 -KPX ydieresis ohungarumlaut -25 -KPX ydieresis omacron -25 -KPX ydieresis oslash -25 -KPX ydieresis otilde -25 -KPX ydieresis period -80 -KPX z e 10 -KPX z eacute 10 -KPX z ecaron 10 -KPX z ecircumflex 10 -KPX z edieresis 10 -KPX z edotaccent 10 -KPX z egrave 10 -KPX z emacron 10 -KPX z eogonek 10 -KPX zacute e 10 -KPX zacute eacute 10 -KPX zacute ecaron 10 -KPX zacute ecircumflex 10 -KPX zacute edieresis 10 -KPX zacute edotaccent 10 -KPX zacute egrave 10 -KPX zacute emacron 10 -KPX zacute eogonek 10 -KPX zcaron e 10 -KPX zcaron eacute 10 -KPX zcaron ecaron 10 -KPX zcaron ecircumflex 10 -KPX zcaron edieresis 10 -KPX zcaron edotaccent 10 -KPX zcaron egrave 10 -KPX zcaron emacron 10 -KPX zcaron eogonek 10 -KPX zdotaccent e 10 -KPX zdotaccent eacute 10 -KPX zdotaccent ecaron 10 -KPX zdotaccent ecircumflex 10 -KPX zdotaccent edieresis 10 -KPX zdotaccent edotaccent 10 -KPX zdotaccent egrave 10 -KPX zdotaccent emacron 10 -KPX zdotaccent eogonek 10 -EndKernPairs -EndKernData -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Helvetica-Oblique.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Helvetica-Oblique.afm deleted file mode 100644 index 7a7af00..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Helvetica-Oblique.afm +++ /dev/null @@ -1,3051 +0,0 @@ -StartFontMetrics 4.1 -Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Thu May 1 12:44:31 1997 -Comment UniqueID 43055 -Comment VMusage 14960 69346 -FontName Helvetica-Oblique -FullName Helvetica Oblique -FamilyName Helvetica -Weight Medium -ItalicAngle -12 -IsFixedPitch false -CharacterSet ExtendedRoman -FontBBox -170 -225 1116 931 -UnderlinePosition -100 -UnderlineThickness 50 -Version 002.000 -Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved.Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 718 -XHeight 523 -Ascender 718 -Descender -207 -StdHW 76 -StdVW 88 -StartCharMetrics 315 -C 32 ; WX 278 ; N space ; B 0 0 0 0 ; -C 33 ; WX 278 ; N exclam ; B 90 0 340 718 ; -C 34 ; WX 355 ; N quotedbl ; B 168 463 438 718 ; -C 35 ; WX 556 ; N numbersign ; B 73 0 631 688 ; -C 36 ; WX 556 ; N dollar ; B 69 -115 617 775 ; -C 37 ; WX 889 ; N percent ; B 147 -19 889 703 ; -C 38 ; WX 667 ; N ampersand ; B 77 -15 647 718 ; -C 39 ; WX 222 ; N quoteright ; B 151 463 310 718 ; -C 40 ; WX 333 ; N parenleft ; B 108 -207 454 733 ; -C 41 ; WX 333 ; N parenright ; B -9 -207 337 733 ; -C 42 ; WX 389 ; N asterisk ; B 165 431 475 718 ; -C 43 ; WX 584 ; N plus ; B 85 0 606 505 ; -C 44 ; WX 278 ; N comma ; B 56 -147 214 106 ; -C 45 ; WX 333 ; N hyphen ; B 93 232 357 322 ; -C 46 ; WX 278 ; N period ; B 87 0 214 106 ; -C 47 ; WX 278 ; N slash ; B -21 -19 452 737 ; -C 48 ; WX 556 ; N zero ; B 93 -19 608 703 ; -C 49 ; WX 556 ; N one ; B 207 0 508 703 ; -C 50 ; WX 556 ; N two ; B 26 0 617 703 ; -C 51 ; WX 556 ; N three ; B 75 -19 610 703 ; -C 52 ; WX 556 ; N four ; B 61 0 576 703 ; -C 53 ; WX 556 ; N five ; B 68 -19 621 688 ; -C 54 ; WX 556 ; N six ; B 91 -19 615 703 ; -C 55 ; WX 556 ; N seven ; B 137 0 669 688 ; -C 56 ; WX 556 ; N eight ; B 74 -19 607 703 ; -C 57 ; WX 556 ; N nine ; B 82 -19 609 703 ; -C 58 ; WX 278 ; N colon ; B 87 0 301 516 ; -C 59 ; WX 278 ; N semicolon ; B 56 -147 301 516 ; -C 60 ; WX 584 ; N less ; B 94 11 641 495 ; -C 61 ; WX 584 ; N equal ; B 63 115 628 390 ; -C 62 ; WX 584 ; N greater ; B 50 11 597 495 ; -C 63 ; WX 556 ; N question ; B 161 0 610 727 ; -C 64 ; WX 1015 ; N at ; B 215 -19 965 737 ; -C 65 ; WX 667 ; N A ; B 14 0 654 718 ; -C 66 ; WX 667 ; N B ; B 74 0 712 718 ; -C 67 ; WX 722 ; N C ; B 108 -19 782 737 ; -C 68 ; WX 722 ; N D ; B 81 0 764 718 ; -C 69 ; WX 667 ; N E ; B 86 0 762 718 ; -C 70 ; WX 611 ; N F ; B 86 0 736 718 ; -C 71 ; WX 778 ; N G ; B 111 -19 799 737 ; -C 72 ; WX 722 ; N H ; B 77 0 799 718 ; -C 73 ; WX 278 ; N I ; B 91 0 341 718 ; -C 74 ; WX 500 ; N J ; B 47 -19 581 718 ; -C 75 ; WX 667 ; N K ; B 76 0 808 718 ; -C 76 ; WX 556 ; N L ; B 76 0 555 718 ; -C 77 ; WX 833 ; N M ; B 73 0 914 718 ; -C 78 ; WX 722 ; N N ; B 76 0 799 718 ; -C 79 ; WX 778 ; N O ; B 105 -19 826 737 ; -C 80 ; WX 667 ; N P ; B 86 0 737 718 ; -C 81 ; WX 778 ; N Q ; B 105 -56 826 737 ; -C 82 ; WX 722 ; N R ; B 88 0 773 718 ; -C 83 ; WX 667 ; N S ; B 90 -19 713 737 ; -C 84 ; WX 611 ; N T ; B 148 0 750 718 ; -C 85 ; WX 722 ; N U ; B 123 -19 797 718 ; -C 86 ; WX 667 ; N V ; B 173 0 800 718 ; -C 87 ; WX 944 ; N W ; B 169 0 1081 718 ; -C 88 ; WX 667 ; N X ; B 19 0 790 718 ; -C 89 ; WX 667 ; N Y ; B 167 0 806 718 ; -C 90 ; WX 611 ; N Z ; B 23 0 741 718 ; -C 91 ; WX 278 ; N bracketleft ; B 21 -196 403 722 ; -C 92 ; WX 278 ; N backslash ; B 140 -19 291 737 ; -C 93 ; WX 278 ; N bracketright ; B -14 -196 368 722 ; -C 94 ; WX 469 ; N asciicircum ; B 42 264 539 688 ; -C 95 ; WX 556 ; N underscore ; B -27 -125 540 -75 ; -C 96 ; WX 222 ; N quoteleft ; B 165 470 323 725 ; -C 97 ; WX 556 ; N a ; B 61 -15 559 538 ; -C 98 ; WX 556 ; N b ; B 58 -15 584 718 ; -C 99 ; WX 500 ; N c ; B 74 -15 553 538 ; -C 100 ; WX 556 ; N d ; B 84 -15 652 718 ; -C 101 ; WX 556 ; N e ; B 84 -15 578 538 ; -C 102 ; WX 278 ; N f ; B 86 0 416 728 ; L i fi ; L l fl ; -C 103 ; WX 556 ; N g ; B 42 -220 610 538 ; -C 104 ; WX 556 ; N h ; B 65 0 573 718 ; -C 105 ; WX 222 ; N i ; B 67 0 308 718 ; -C 106 ; WX 222 ; N j ; B -60 -210 308 718 ; -C 107 ; WX 500 ; N k ; B 67 0 600 718 ; -C 108 ; WX 222 ; N l ; B 67 0 308 718 ; -C 109 ; WX 833 ; N m ; B 65 0 852 538 ; -C 110 ; WX 556 ; N n ; B 65 0 573 538 ; -C 111 ; WX 556 ; N o ; B 83 -14 585 538 ; -C 112 ; WX 556 ; N p ; B 14 -207 584 538 ; -C 113 ; WX 556 ; N q ; B 84 -207 605 538 ; -C 114 ; WX 333 ; N r ; B 77 0 446 538 ; -C 115 ; WX 500 ; N s ; B 63 -15 529 538 ; -C 116 ; WX 278 ; N t ; B 102 -7 368 669 ; -C 117 ; WX 556 ; N u ; B 94 -15 600 523 ; -C 118 ; WX 500 ; N v ; B 119 0 603 523 ; -C 119 ; WX 722 ; N w ; B 125 0 820 523 ; -C 120 ; WX 500 ; N x ; B 11 0 594 523 ; -C 121 ; WX 500 ; N y ; B 15 -214 600 523 ; -C 122 ; WX 500 ; N z ; B 31 0 571 523 ; -C 123 ; WX 334 ; N braceleft ; B 92 -196 445 722 ; -C 124 ; WX 260 ; N bar ; B 46 -225 332 775 ; -C 125 ; WX 334 ; N braceright ; B 0 -196 354 722 ; -C 126 ; WX 584 ; N asciitilde ; B 111 180 580 326 ; -C 161 ; WX 333 ; N exclamdown ; B 77 -195 326 523 ; -C 162 ; WX 556 ; N cent ; B 95 -115 584 623 ; -C 163 ; WX 556 ; N sterling ; B 49 -16 634 718 ; -C 164 ; WX 167 ; N fraction ; B -170 -19 482 703 ; -C 165 ; WX 556 ; N yen ; B 81 0 699 688 ; -C 166 ; WX 556 ; N florin ; B -52 -207 654 737 ; -C 167 ; WX 556 ; N section ; B 76 -191 584 737 ; -C 168 ; WX 556 ; N currency ; B 60 99 646 603 ; -C 169 ; WX 191 ; N quotesingle ; B 157 463 285 718 ; -C 170 ; WX 333 ; N quotedblleft ; B 138 470 461 725 ; -C 171 ; WX 556 ; N guillemotleft ; B 146 108 554 446 ; -C 172 ; WX 333 ; N guilsinglleft ; B 137 108 340 446 ; -C 173 ; WX 333 ; N guilsinglright ; B 111 108 314 446 ; -C 174 ; WX 500 ; N fi ; B 86 0 587 728 ; -C 175 ; WX 500 ; N fl ; B 86 0 585 728 ; -C 177 ; WX 556 ; N endash ; B 51 240 623 313 ; -C 178 ; WX 556 ; N dagger ; B 135 -159 622 718 ; -C 179 ; WX 556 ; N daggerdbl ; B 52 -159 623 718 ; -C 180 ; WX 278 ; N periodcentered ; B 129 190 257 315 ; -C 182 ; WX 537 ; N paragraph ; B 126 -173 650 718 ; -C 183 ; WX 350 ; N bullet ; B 91 202 413 517 ; -C 184 ; WX 222 ; N quotesinglbase ; B 21 -149 180 106 ; -C 185 ; WX 333 ; N quotedblbase ; B -6 -149 318 106 ; -C 186 ; WX 333 ; N quotedblright ; B 124 463 448 718 ; -C 187 ; WX 556 ; N guillemotright ; B 120 108 528 446 ; -C 188 ; WX 1000 ; N ellipsis ; B 115 0 908 106 ; -C 189 ; WX 1000 ; N perthousand ; B 88 -19 1029 703 ; -C 191 ; WX 611 ; N questiondown ; B 85 -201 534 525 ; -C 193 ; WX 333 ; N grave ; B 170 593 337 734 ; -C 194 ; WX 333 ; N acute ; B 248 593 475 734 ; -C 195 ; WX 333 ; N circumflex ; B 147 593 438 734 ; -C 196 ; WX 333 ; N tilde ; B 125 606 490 722 ; -C 197 ; WX 333 ; N macron ; B 143 627 468 684 ; -C 198 ; WX 333 ; N breve ; B 167 595 476 731 ; -C 199 ; WX 333 ; N dotaccent ; B 249 604 362 706 ; -C 200 ; WX 333 ; N dieresis ; B 168 604 443 706 ; -C 202 ; WX 333 ; N ring ; B 214 572 402 756 ; -C 203 ; WX 333 ; N cedilla ; B 2 -225 232 0 ; -C 205 ; WX 333 ; N hungarumlaut ; B 157 593 565 734 ; -C 206 ; WX 333 ; N ogonek ; B 43 -225 249 0 ; -C 207 ; WX 333 ; N caron ; B 177 593 468 734 ; -C 208 ; WX 1000 ; N emdash ; B 51 240 1067 313 ; -C 225 ; WX 1000 ; N AE ; B 8 0 1097 718 ; -C 227 ; WX 370 ; N ordfeminine ; B 127 405 449 737 ; -C 232 ; WX 556 ; N Lslash ; B 41 0 555 718 ; -C 233 ; WX 778 ; N Oslash ; B 43 -19 890 737 ; -C 234 ; WX 1000 ; N OE ; B 98 -19 1116 737 ; -C 235 ; WX 365 ; N ordmasculine ; B 141 405 468 737 ; -C 241 ; WX 889 ; N ae ; B 61 -15 909 538 ; -C 245 ; WX 278 ; N dotlessi ; B 95 0 294 523 ; -C 248 ; WX 222 ; N lslash ; B 41 0 347 718 ; -C 249 ; WX 611 ; N oslash ; B 29 -22 647 545 ; -C 250 ; WX 944 ; N oe ; B 83 -15 964 538 ; -C 251 ; WX 611 ; N germandbls ; B 67 -15 658 728 ; -C -1 ; WX 278 ; N Idieresis ; B 91 0 458 901 ; -C -1 ; WX 556 ; N eacute ; B 84 -15 587 734 ; -C -1 ; WX 556 ; N abreve ; B 61 -15 578 731 ; -C -1 ; WX 556 ; N uhungarumlaut ; B 94 -15 677 734 ; -C -1 ; WX 556 ; N ecaron ; B 84 -15 580 734 ; -C -1 ; WX 667 ; N Ydieresis ; B 167 0 806 901 ; -C -1 ; WX 584 ; N divide ; B 85 -19 606 524 ; -C -1 ; WX 667 ; N Yacute ; B 167 0 806 929 ; -C -1 ; WX 667 ; N Acircumflex ; B 14 0 654 929 ; -C -1 ; WX 556 ; N aacute ; B 61 -15 587 734 ; -C -1 ; WX 722 ; N Ucircumflex ; B 123 -19 797 929 ; -C -1 ; WX 500 ; N yacute ; B 15 -214 600 734 ; -C -1 ; WX 500 ; N scommaaccent ; B 63 -225 529 538 ; -C -1 ; WX 556 ; N ecircumflex ; B 84 -15 578 734 ; -C -1 ; WX 722 ; N Uring ; B 123 -19 797 931 ; -C -1 ; WX 722 ; N Udieresis ; B 123 -19 797 901 ; -C -1 ; WX 556 ; N aogonek ; B 61 -220 559 538 ; -C -1 ; WX 722 ; N Uacute ; B 123 -19 797 929 ; -C -1 ; WX 556 ; N uogonek ; B 94 -225 600 523 ; -C -1 ; WX 667 ; N Edieresis ; B 86 0 762 901 ; -C -1 ; WX 722 ; N Dcroat ; B 69 0 764 718 ; -C -1 ; WX 250 ; N commaaccent ; B 39 -225 172 -40 ; -C -1 ; WX 737 ; N copyright ; B 54 -19 837 737 ; -C -1 ; WX 667 ; N Emacron ; B 86 0 762 879 ; -C -1 ; WX 500 ; N ccaron ; B 74 -15 553 734 ; -C -1 ; WX 556 ; N aring ; B 61 -15 559 756 ; -C -1 ; WX 722 ; N Ncommaaccent ; B 76 -225 799 718 ; -C -1 ; WX 222 ; N lacute ; B 67 0 461 929 ; -C -1 ; WX 556 ; N agrave ; B 61 -15 559 734 ; -C -1 ; WX 611 ; N Tcommaaccent ; B 148 -225 750 718 ; -C -1 ; WX 722 ; N Cacute ; B 108 -19 782 929 ; -C -1 ; WX 556 ; N atilde ; B 61 -15 592 722 ; -C -1 ; WX 667 ; N Edotaccent ; B 86 0 762 901 ; -C -1 ; WX 500 ; N scaron ; B 63 -15 552 734 ; -C -1 ; WX 500 ; N scedilla ; B 63 -225 529 538 ; -C -1 ; WX 278 ; N iacute ; B 95 0 448 734 ; -C -1 ; WX 471 ; N lozenge ; B 88 0 540 728 ; -C -1 ; WX 722 ; N Rcaron ; B 88 0 773 929 ; -C -1 ; WX 778 ; N Gcommaaccent ; B 111 -225 799 737 ; -C -1 ; WX 556 ; N ucircumflex ; B 94 -15 600 734 ; -C -1 ; WX 556 ; N acircumflex ; B 61 -15 559 734 ; -C -1 ; WX 667 ; N Amacron ; B 14 0 677 879 ; -C -1 ; WX 333 ; N rcaron ; B 77 0 508 734 ; -C -1 ; WX 500 ; N ccedilla ; B 74 -225 553 538 ; -C -1 ; WX 611 ; N Zdotaccent ; B 23 0 741 901 ; -C -1 ; WX 667 ; N Thorn ; B 86 0 712 718 ; -C -1 ; WX 778 ; N Omacron ; B 105 -19 826 879 ; -C -1 ; WX 722 ; N Racute ; B 88 0 773 929 ; -C -1 ; WX 667 ; N Sacute ; B 90 -19 713 929 ; -C -1 ; WX 643 ; N dcaron ; B 84 -15 808 718 ; -C -1 ; WX 722 ; N Umacron ; B 123 -19 797 879 ; -C -1 ; WX 556 ; N uring ; B 94 -15 600 756 ; -C -1 ; WX 333 ; N threesuperior ; B 90 270 436 703 ; -C -1 ; WX 778 ; N Ograve ; B 105 -19 826 929 ; -C -1 ; WX 667 ; N Agrave ; B 14 0 654 929 ; -C -1 ; WX 667 ; N Abreve ; B 14 0 685 926 ; -C -1 ; WX 584 ; N multiply ; B 50 0 642 506 ; -C -1 ; WX 556 ; N uacute ; B 94 -15 600 734 ; -C -1 ; WX 611 ; N Tcaron ; B 148 0 750 929 ; -C -1 ; WX 476 ; N partialdiff ; B 41 -38 550 714 ; -C -1 ; WX 500 ; N ydieresis ; B 15 -214 600 706 ; -C -1 ; WX 722 ; N Nacute ; B 76 0 799 929 ; -C -1 ; WX 278 ; N icircumflex ; B 95 0 411 734 ; -C -1 ; WX 667 ; N Ecircumflex ; B 86 0 762 929 ; -C -1 ; WX 556 ; N adieresis ; B 61 -15 559 706 ; -C -1 ; WX 556 ; N edieresis ; B 84 -15 578 706 ; -C -1 ; WX 500 ; N cacute ; B 74 -15 559 734 ; -C -1 ; WX 556 ; N nacute ; B 65 0 587 734 ; -C -1 ; WX 556 ; N umacron ; B 94 -15 600 684 ; -C -1 ; WX 722 ; N Ncaron ; B 76 0 799 929 ; -C -1 ; WX 278 ; N Iacute ; B 91 0 489 929 ; -C -1 ; WX 584 ; N plusminus ; B 39 0 618 506 ; -C -1 ; WX 260 ; N brokenbar ; B 62 -150 316 700 ; -C -1 ; WX 737 ; N registered ; B 54 -19 837 737 ; -C -1 ; WX 778 ; N Gbreve ; B 111 -19 799 926 ; -C -1 ; WX 278 ; N Idotaccent ; B 91 0 377 901 ; -C -1 ; WX 600 ; N summation ; B 15 -10 671 706 ; -C -1 ; WX 667 ; N Egrave ; B 86 0 762 929 ; -C -1 ; WX 333 ; N racute ; B 77 0 475 734 ; -C -1 ; WX 556 ; N omacron ; B 83 -14 585 684 ; -C -1 ; WX 611 ; N Zacute ; B 23 0 741 929 ; -C -1 ; WX 611 ; N Zcaron ; B 23 0 741 929 ; -C -1 ; WX 549 ; N greaterequal ; B 26 0 620 674 ; -C -1 ; WX 722 ; N Eth ; B 69 0 764 718 ; -C -1 ; WX 722 ; N Ccedilla ; B 108 -225 782 737 ; -C -1 ; WX 222 ; N lcommaaccent ; B 25 -225 308 718 ; -C -1 ; WX 317 ; N tcaron ; B 102 -7 501 808 ; -C -1 ; WX 556 ; N eogonek ; B 84 -225 578 538 ; -C -1 ; WX 722 ; N Uogonek ; B 123 -225 797 718 ; -C -1 ; WX 667 ; N Aacute ; B 14 0 683 929 ; -C -1 ; WX 667 ; N Adieresis ; B 14 0 654 901 ; -C -1 ; WX 556 ; N egrave ; B 84 -15 578 734 ; -C -1 ; WX 500 ; N zacute ; B 31 0 571 734 ; -C -1 ; WX 222 ; N iogonek ; B -61 -225 308 718 ; -C -1 ; WX 778 ; N Oacute ; B 105 -19 826 929 ; -C -1 ; WX 556 ; N oacute ; B 83 -14 587 734 ; -C -1 ; WX 556 ; N amacron ; B 61 -15 580 684 ; -C -1 ; WX 500 ; N sacute ; B 63 -15 559 734 ; -C -1 ; WX 278 ; N idieresis ; B 95 0 416 706 ; -C -1 ; WX 778 ; N Ocircumflex ; B 105 -19 826 929 ; -C -1 ; WX 722 ; N Ugrave ; B 123 -19 797 929 ; -C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ; -C -1 ; WX 556 ; N thorn ; B 14 -207 584 718 ; -C -1 ; WX 333 ; N twosuperior ; B 64 281 449 703 ; -C -1 ; WX 778 ; N Odieresis ; B 105 -19 826 901 ; -C -1 ; WX 556 ; N mu ; B 24 -207 600 523 ; -C -1 ; WX 278 ; N igrave ; B 95 0 310 734 ; -C -1 ; WX 556 ; N ohungarumlaut ; B 83 -14 677 734 ; -C -1 ; WX 667 ; N Eogonek ; B 86 -220 762 718 ; -C -1 ; WX 556 ; N dcroat ; B 84 -15 689 718 ; -C -1 ; WX 834 ; N threequarters ; B 130 -19 861 703 ; -C -1 ; WX 667 ; N Scedilla ; B 90 -225 713 737 ; -C -1 ; WX 299 ; N lcaron ; B 67 0 464 718 ; -C -1 ; WX 667 ; N Kcommaaccent ; B 76 -225 808 718 ; -C -1 ; WX 556 ; N Lacute ; B 76 0 555 929 ; -C -1 ; WX 1000 ; N trademark ; B 186 306 1056 718 ; -C -1 ; WX 556 ; N edotaccent ; B 84 -15 578 706 ; -C -1 ; WX 278 ; N Igrave ; B 91 0 351 929 ; -C -1 ; WX 278 ; N Imacron ; B 91 0 483 879 ; -C -1 ; WX 556 ; N Lcaron ; B 76 0 570 718 ; -C -1 ; WX 834 ; N onehalf ; B 114 -19 839 703 ; -C -1 ; WX 549 ; N lessequal ; B 26 0 666 674 ; -C -1 ; WX 556 ; N ocircumflex ; B 83 -14 585 734 ; -C -1 ; WX 556 ; N ntilde ; B 65 0 592 722 ; -C -1 ; WX 722 ; N Uhungarumlaut ; B 123 -19 801 929 ; -C -1 ; WX 667 ; N Eacute ; B 86 0 762 929 ; -C -1 ; WX 556 ; N emacron ; B 84 -15 580 684 ; -C -1 ; WX 556 ; N gbreve ; B 42 -220 610 731 ; -C -1 ; WX 834 ; N onequarter ; B 150 -19 802 703 ; -C -1 ; WX 667 ; N Scaron ; B 90 -19 713 929 ; -C -1 ; WX 667 ; N Scommaaccent ; B 90 -225 713 737 ; -C -1 ; WX 778 ; N Ohungarumlaut ; B 105 -19 829 929 ; -C -1 ; WX 400 ; N degree ; B 169 411 468 703 ; -C -1 ; WX 556 ; N ograve ; B 83 -14 585 734 ; -C -1 ; WX 722 ; N Ccaron ; B 108 -19 782 929 ; -C -1 ; WX 556 ; N ugrave ; B 94 -15 600 734 ; -C -1 ; WX 453 ; N radical ; B 79 -80 617 762 ; -C -1 ; WX 722 ; N Dcaron ; B 81 0 764 929 ; -C -1 ; WX 333 ; N rcommaaccent ; B 30 -225 446 538 ; -C -1 ; WX 722 ; N Ntilde ; B 76 0 799 917 ; -C -1 ; WX 556 ; N otilde ; B 83 -14 602 722 ; -C -1 ; WX 722 ; N Rcommaaccent ; B 88 -225 773 718 ; -C -1 ; WX 556 ; N Lcommaaccent ; B 76 -225 555 718 ; -C -1 ; WX 667 ; N Atilde ; B 14 0 699 917 ; -C -1 ; WX 667 ; N Aogonek ; B 14 -225 654 718 ; -C -1 ; WX 667 ; N Aring ; B 14 0 654 931 ; -C -1 ; WX 778 ; N Otilde ; B 105 -19 826 917 ; -C -1 ; WX 500 ; N zdotaccent ; B 31 0 571 706 ; -C -1 ; WX 667 ; N Ecaron ; B 86 0 762 929 ; -C -1 ; WX 278 ; N Iogonek ; B -33 -225 341 718 ; -C -1 ; WX 500 ; N kcommaaccent ; B 67 -225 600 718 ; -C -1 ; WX 584 ; N minus ; B 85 216 606 289 ; -C -1 ; WX 278 ; N Icircumflex ; B 91 0 452 929 ; -C -1 ; WX 556 ; N ncaron ; B 65 0 580 734 ; -C -1 ; WX 278 ; N tcommaaccent ; B 63 -225 368 669 ; -C -1 ; WX 584 ; N logicalnot ; B 106 108 628 390 ; -C -1 ; WX 556 ; N odieresis ; B 83 -14 585 706 ; -C -1 ; WX 556 ; N udieresis ; B 94 -15 600 706 ; -C -1 ; WX 549 ; N notequal ; B 34 -35 623 551 ; -C -1 ; WX 556 ; N gcommaaccent ; B 42 -220 610 822 ; -C -1 ; WX 556 ; N eth ; B 81 -15 617 737 ; -C -1 ; WX 500 ; N zcaron ; B 31 0 571 734 ; -C -1 ; WX 556 ; N ncommaaccent ; B 65 -225 573 538 ; -C -1 ; WX 333 ; N onesuperior ; B 166 281 371 703 ; -C -1 ; WX 278 ; N imacron ; B 95 0 417 684 ; -C -1 ; WX 556 ; N Euro ; B 0 0 0 0 ; -EndCharMetrics -StartKernData -StartKernPairs 2705 -KPX A C -30 -KPX A Cacute -30 -KPX A Ccaron -30 -KPX A Ccedilla -30 -KPX A G -30 -KPX A Gbreve -30 -KPX A Gcommaaccent -30 -KPX A O -30 -KPX A Oacute -30 -KPX A Ocircumflex -30 -KPX A Odieresis -30 -KPX A Ograve -30 -KPX A Ohungarumlaut -30 -KPX A Omacron -30 -KPX A Oslash -30 -KPX A Otilde -30 -KPX A Q -30 -KPX A T -120 -KPX A Tcaron -120 -KPX A Tcommaaccent -120 -KPX A U -50 -KPX A Uacute -50 -KPX A Ucircumflex -50 -KPX A Udieresis -50 -KPX A Ugrave -50 -KPX A Uhungarumlaut -50 -KPX A Umacron -50 -KPX A Uogonek -50 -KPX A Uring -50 -KPX A V -70 -KPX A W -50 -KPX A Y -100 -KPX A Yacute -100 -KPX A Ydieresis -100 -KPX A u -30 -KPX A uacute -30 -KPX A ucircumflex -30 -KPX A udieresis -30 -KPX A ugrave -30 -KPX A uhungarumlaut -30 -KPX A umacron -30 -KPX A uogonek -30 -KPX A uring -30 -KPX A v -40 -KPX A w -40 -KPX A y -40 -KPX A yacute -40 -KPX A ydieresis -40 -KPX Aacute C -30 -KPX Aacute Cacute -30 -KPX Aacute Ccaron -30 -KPX Aacute Ccedilla -30 -KPX Aacute G -30 -KPX Aacute Gbreve -30 -KPX Aacute Gcommaaccent -30 -KPX Aacute O -30 -KPX Aacute Oacute -30 -KPX Aacute Ocircumflex -30 -KPX Aacute Odieresis -30 -KPX Aacute Ograve -30 -KPX Aacute Ohungarumlaut -30 -KPX Aacute Omacron -30 -KPX Aacute Oslash -30 -KPX Aacute Otilde -30 -KPX Aacute Q -30 -KPX Aacute T -120 -KPX Aacute Tcaron -120 -KPX Aacute Tcommaaccent -120 -KPX Aacute U -50 -KPX Aacute Uacute -50 -KPX Aacute Ucircumflex -50 -KPX Aacute Udieresis -50 -KPX Aacute Ugrave -50 -KPX Aacute Uhungarumlaut -50 -KPX Aacute Umacron -50 -KPX Aacute Uogonek -50 -KPX Aacute Uring -50 -KPX Aacute V -70 -KPX Aacute W -50 -KPX Aacute Y -100 -KPX Aacute Yacute -100 -KPX Aacute Ydieresis -100 -KPX Aacute u -30 -KPX Aacute uacute -30 -KPX Aacute ucircumflex -30 -KPX Aacute udieresis -30 -KPX Aacute ugrave -30 -KPX Aacute uhungarumlaut -30 -KPX Aacute umacron -30 -KPX Aacute uogonek -30 -KPX Aacute uring -30 -KPX Aacute v -40 -KPX Aacute w -40 -KPX Aacute y -40 -KPX Aacute yacute -40 -KPX Aacute ydieresis -40 -KPX Abreve C -30 -KPX Abreve Cacute -30 -KPX Abreve Ccaron -30 -KPX Abreve Ccedilla -30 -KPX Abreve G -30 -KPX Abreve Gbreve -30 -KPX Abreve Gcommaaccent -30 -KPX Abreve O -30 -KPX Abreve Oacute -30 -KPX Abreve Ocircumflex -30 -KPX Abreve Odieresis -30 -KPX Abreve Ograve -30 -KPX Abreve Ohungarumlaut -30 -KPX Abreve Omacron -30 -KPX Abreve Oslash -30 -KPX Abreve Otilde -30 -KPX Abreve Q -30 -KPX Abreve T -120 -KPX Abreve Tcaron -120 -KPX Abreve Tcommaaccent -120 -KPX Abreve U -50 -KPX Abreve Uacute -50 -KPX Abreve Ucircumflex -50 -KPX Abreve Udieresis -50 -KPX Abreve Ugrave -50 -KPX Abreve Uhungarumlaut -50 -KPX Abreve Umacron -50 -KPX Abreve Uogonek -50 -KPX Abreve Uring -50 -KPX Abreve V -70 -KPX Abreve W -50 -KPX Abreve Y -100 -KPX Abreve Yacute -100 -KPX Abreve Ydieresis -100 -KPX Abreve u -30 -KPX Abreve uacute -30 -KPX Abreve ucircumflex -30 -KPX Abreve udieresis -30 -KPX Abreve ugrave -30 -KPX Abreve uhungarumlaut -30 -KPX Abreve umacron -30 -KPX Abreve uogonek -30 -KPX Abreve uring -30 -KPX Abreve v -40 -KPX Abreve w -40 -KPX Abreve y -40 -KPX Abreve yacute -40 -KPX Abreve ydieresis -40 -KPX Acircumflex C -30 -KPX Acircumflex Cacute -30 -KPX Acircumflex Ccaron -30 -KPX Acircumflex Ccedilla -30 -KPX Acircumflex G -30 -KPX Acircumflex Gbreve -30 -KPX Acircumflex Gcommaaccent -30 -KPX Acircumflex O -30 -KPX Acircumflex Oacute -30 -KPX Acircumflex Ocircumflex -30 -KPX Acircumflex Odieresis -30 -KPX Acircumflex Ograve -30 -KPX Acircumflex Ohungarumlaut -30 -KPX Acircumflex Omacron -30 -KPX Acircumflex Oslash -30 -KPX Acircumflex Otilde -30 -KPX Acircumflex Q -30 -KPX Acircumflex T -120 -KPX Acircumflex Tcaron -120 -KPX Acircumflex Tcommaaccent -120 -KPX Acircumflex U -50 -KPX Acircumflex Uacute -50 -KPX Acircumflex Ucircumflex -50 -KPX Acircumflex Udieresis -50 -KPX Acircumflex Ugrave -50 -KPX Acircumflex Uhungarumlaut -50 -KPX Acircumflex Umacron -50 -KPX Acircumflex Uogonek -50 -KPX Acircumflex Uring -50 -KPX Acircumflex V -70 -KPX Acircumflex W -50 -KPX Acircumflex Y -100 -KPX Acircumflex Yacute -100 -KPX Acircumflex Ydieresis -100 -KPX Acircumflex u -30 -KPX Acircumflex uacute -30 -KPX Acircumflex ucircumflex -30 -KPX Acircumflex udieresis -30 -KPX Acircumflex ugrave -30 -KPX Acircumflex uhungarumlaut -30 -KPX Acircumflex umacron -30 -KPX Acircumflex uogonek -30 -KPX Acircumflex uring -30 -KPX Acircumflex v -40 -KPX Acircumflex w -40 -KPX Acircumflex y -40 -KPX Acircumflex yacute -40 -KPX Acircumflex ydieresis -40 -KPX Adieresis C -30 -KPX Adieresis Cacute -30 -KPX Adieresis Ccaron -30 -KPX Adieresis Ccedilla -30 -KPX Adieresis G -30 -KPX Adieresis Gbreve -30 -KPX Adieresis Gcommaaccent -30 -KPX Adieresis O -30 -KPX Adieresis Oacute -30 -KPX Adieresis Ocircumflex -30 -KPX Adieresis Odieresis -30 -KPX Adieresis Ograve -30 -KPX Adieresis Ohungarumlaut -30 -KPX Adieresis Omacron -30 -KPX Adieresis Oslash -30 -KPX Adieresis Otilde -30 -KPX Adieresis Q -30 -KPX Adieresis T -120 -KPX Adieresis Tcaron -120 -KPX Adieresis Tcommaaccent -120 -KPX Adieresis U -50 -KPX Adieresis Uacute -50 -KPX Adieresis Ucircumflex -50 -KPX Adieresis Udieresis -50 -KPX Adieresis Ugrave -50 -KPX Adieresis Uhungarumlaut -50 -KPX Adieresis Umacron -50 -KPX Adieresis Uogonek -50 -KPX Adieresis Uring -50 -KPX Adieresis V -70 -KPX Adieresis W -50 -KPX Adieresis Y -100 -KPX Adieresis Yacute -100 -KPX Adieresis Ydieresis -100 -KPX Adieresis u -30 -KPX Adieresis uacute -30 -KPX Adieresis ucircumflex -30 -KPX Adieresis udieresis -30 -KPX Adieresis ugrave -30 -KPX Adieresis uhungarumlaut -30 -KPX Adieresis umacron -30 -KPX Adieresis uogonek -30 -KPX Adieresis uring -30 -KPX Adieresis v -40 -KPX Adieresis w -40 -KPX Adieresis y -40 -KPX Adieresis yacute -40 -KPX Adieresis ydieresis -40 -KPX Agrave C -30 -KPX Agrave Cacute -30 -KPX Agrave Ccaron -30 -KPX Agrave Ccedilla -30 -KPX Agrave G -30 -KPX Agrave Gbreve -30 -KPX Agrave Gcommaaccent -30 -KPX Agrave O -30 -KPX Agrave Oacute -30 -KPX Agrave Ocircumflex -30 -KPX Agrave Odieresis -30 -KPX Agrave Ograve -30 -KPX Agrave Ohungarumlaut -30 -KPX Agrave Omacron -30 -KPX Agrave Oslash -30 -KPX Agrave Otilde -30 -KPX Agrave Q -30 -KPX Agrave T -120 -KPX Agrave Tcaron -120 -KPX Agrave Tcommaaccent -120 -KPX Agrave U -50 -KPX Agrave Uacute -50 -KPX Agrave Ucircumflex -50 -KPX Agrave Udieresis -50 -KPX Agrave Ugrave -50 -KPX Agrave Uhungarumlaut -50 -KPX Agrave Umacron -50 -KPX Agrave Uogonek -50 -KPX Agrave Uring -50 -KPX Agrave V -70 -KPX Agrave W -50 -KPX Agrave Y -100 -KPX Agrave Yacute -100 -KPX Agrave Ydieresis -100 -KPX Agrave u -30 -KPX Agrave uacute -30 -KPX Agrave ucircumflex -30 -KPX Agrave udieresis -30 -KPX Agrave ugrave -30 -KPX Agrave uhungarumlaut -30 -KPX Agrave umacron -30 -KPX Agrave uogonek -30 -KPX Agrave uring -30 -KPX Agrave v -40 -KPX Agrave w -40 -KPX Agrave y -40 -KPX Agrave yacute -40 -KPX Agrave ydieresis -40 -KPX Amacron C -30 -KPX Amacron Cacute -30 -KPX Amacron Ccaron -30 -KPX Amacron Ccedilla -30 -KPX Amacron G -30 -KPX Amacron Gbreve -30 -KPX Amacron Gcommaaccent -30 -KPX Amacron O -30 -KPX Amacron Oacute -30 -KPX Amacron Ocircumflex -30 -KPX Amacron Odieresis -30 -KPX Amacron Ograve -30 -KPX Amacron Ohungarumlaut -30 -KPX Amacron Omacron -30 -KPX Amacron Oslash -30 -KPX Amacron Otilde -30 -KPX Amacron Q -30 -KPX Amacron T -120 -KPX Amacron Tcaron -120 -KPX Amacron Tcommaaccent -120 -KPX Amacron U -50 -KPX Amacron Uacute -50 -KPX Amacron Ucircumflex -50 -KPX Amacron Udieresis -50 -KPX Amacron Ugrave -50 -KPX Amacron Uhungarumlaut -50 -KPX Amacron Umacron -50 -KPX Amacron Uogonek -50 -KPX Amacron Uring -50 -KPX Amacron V -70 -KPX Amacron W -50 -KPX Amacron Y -100 -KPX Amacron Yacute -100 -KPX Amacron Ydieresis -100 -KPX Amacron u -30 -KPX Amacron uacute -30 -KPX Amacron ucircumflex -30 -KPX Amacron udieresis -30 -KPX Amacron ugrave -30 -KPX Amacron uhungarumlaut -30 -KPX Amacron umacron -30 -KPX Amacron uogonek -30 -KPX Amacron uring -30 -KPX Amacron v -40 -KPX Amacron w -40 -KPX Amacron y -40 -KPX Amacron yacute -40 -KPX Amacron ydieresis -40 -KPX Aogonek C -30 -KPX Aogonek Cacute -30 -KPX Aogonek Ccaron -30 -KPX Aogonek Ccedilla -30 -KPX Aogonek G -30 -KPX Aogonek Gbreve -30 -KPX Aogonek Gcommaaccent -30 -KPX Aogonek O -30 -KPX Aogonek Oacute -30 -KPX Aogonek Ocircumflex -30 -KPX Aogonek Odieresis -30 -KPX Aogonek Ograve -30 -KPX Aogonek Ohungarumlaut -30 -KPX Aogonek Omacron -30 -KPX Aogonek Oslash -30 -KPX Aogonek Otilde -30 -KPX Aogonek Q -30 -KPX Aogonek T -120 -KPX Aogonek Tcaron -120 -KPX Aogonek Tcommaaccent -120 -KPX Aogonek U -50 -KPX Aogonek Uacute -50 -KPX Aogonek Ucircumflex -50 -KPX Aogonek Udieresis -50 -KPX Aogonek Ugrave -50 -KPX Aogonek Uhungarumlaut -50 -KPX Aogonek Umacron -50 -KPX Aogonek Uogonek -50 -KPX Aogonek Uring -50 -KPX Aogonek V -70 -KPX Aogonek W -50 -KPX Aogonek Y -100 -KPX Aogonek Yacute -100 -KPX Aogonek Ydieresis -100 -KPX Aogonek u -30 -KPX Aogonek uacute -30 -KPX Aogonek ucircumflex -30 -KPX Aogonek udieresis -30 -KPX Aogonek ugrave -30 -KPX Aogonek uhungarumlaut -30 -KPX Aogonek umacron -30 -KPX Aogonek uogonek -30 -KPX Aogonek uring -30 -KPX Aogonek v -40 -KPX Aogonek w -40 -KPX Aogonek y -40 -KPX Aogonek yacute -40 -KPX Aogonek ydieresis -40 -KPX Aring C -30 -KPX Aring Cacute -30 -KPX Aring Ccaron -30 -KPX Aring Ccedilla -30 -KPX Aring G -30 -KPX Aring Gbreve -30 -KPX Aring Gcommaaccent -30 -KPX Aring O -30 -KPX Aring Oacute -30 -KPX Aring Ocircumflex -30 -KPX Aring Odieresis -30 -KPX Aring Ograve -30 -KPX Aring Ohungarumlaut -30 -KPX Aring Omacron -30 -KPX Aring Oslash -30 -KPX Aring Otilde -30 -KPX Aring Q -30 -KPX Aring T -120 -KPX Aring Tcaron -120 -KPX Aring Tcommaaccent -120 -KPX Aring U -50 -KPX Aring Uacute -50 -KPX Aring Ucircumflex -50 -KPX Aring Udieresis -50 -KPX Aring Ugrave -50 -KPX Aring Uhungarumlaut -50 -KPX Aring Umacron -50 -KPX Aring Uogonek -50 -KPX Aring Uring -50 -KPX Aring V -70 -KPX Aring W -50 -KPX Aring Y -100 -KPX Aring Yacute -100 -KPX Aring Ydieresis -100 -KPX Aring u -30 -KPX Aring uacute -30 -KPX Aring ucircumflex -30 -KPX Aring udieresis -30 -KPX Aring ugrave -30 -KPX Aring uhungarumlaut -30 -KPX Aring umacron -30 -KPX Aring uogonek -30 -KPX Aring uring -30 -KPX Aring v -40 -KPX Aring w -40 -KPX Aring y -40 -KPX Aring yacute -40 -KPX Aring ydieresis -40 -KPX Atilde C -30 -KPX Atilde Cacute -30 -KPX Atilde Ccaron -30 -KPX Atilde Ccedilla -30 -KPX Atilde G -30 -KPX Atilde Gbreve -30 -KPX Atilde Gcommaaccent -30 -KPX Atilde O -30 -KPX Atilde Oacute -30 -KPX Atilde Ocircumflex -30 -KPX Atilde Odieresis -30 -KPX Atilde Ograve -30 -KPX Atilde Ohungarumlaut -30 -KPX Atilde Omacron -30 -KPX Atilde Oslash -30 -KPX Atilde Otilde -30 -KPX Atilde Q -30 -KPX Atilde T -120 -KPX Atilde Tcaron -120 -KPX Atilde Tcommaaccent -120 -KPX Atilde U -50 -KPX Atilde Uacute -50 -KPX Atilde Ucircumflex -50 -KPX Atilde Udieresis -50 -KPX Atilde Ugrave -50 -KPX Atilde Uhungarumlaut -50 -KPX Atilde Umacron -50 -KPX Atilde Uogonek -50 -KPX Atilde Uring -50 -KPX Atilde V -70 -KPX Atilde W -50 -KPX Atilde Y -100 -KPX Atilde Yacute -100 -KPX Atilde Ydieresis -100 -KPX Atilde u -30 -KPX Atilde uacute -30 -KPX Atilde ucircumflex -30 -KPX Atilde udieresis -30 -KPX Atilde ugrave -30 -KPX Atilde uhungarumlaut -30 -KPX Atilde umacron -30 -KPX Atilde uogonek -30 -KPX Atilde uring -30 -KPX Atilde v -40 -KPX Atilde w -40 -KPX Atilde y -40 -KPX Atilde yacute -40 -KPX Atilde ydieresis -40 -KPX B U -10 -KPX B Uacute -10 -KPX B Ucircumflex -10 -KPX B Udieresis -10 -KPX B Ugrave -10 -KPX B Uhungarumlaut -10 -KPX B Umacron -10 -KPX B Uogonek -10 -KPX B Uring -10 -KPX B comma -20 -KPX B period -20 -KPX C comma -30 -KPX C period -30 -KPX Cacute comma -30 -KPX Cacute period -30 -KPX Ccaron comma -30 -KPX Ccaron period -30 -KPX Ccedilla comma -30 -KPX Ccedilla period -30 -KPX D A -40 -KPX D Aacute -40 -KPX D Abreve -40 -KPX D Acircumflex -40 -KPX D Adieresis -40 -KPX D Agrave -40 -KPX D Amacron -40 -KPX D Aogonek -40 -KPX D Aring -40 -KPX D Atilde -40 -KPX D V -70 -KPX D W -40 -KPX D Y -90 -KPX D Yacute -90 -KPX D Ydieresis -90 -KPX D comma -70 -KPX D period -70 -KPX Dcaron A -40 -KPX Dcaron Aacute -40 -KPX Dcaron Abreve -40 -KPX Dcaron Acircumflex -40 -KPX Dcaron Adieresis -40 -KPX Dcaron Agrave -40 -KPX Dcaron Amacron -40 -KPX Dcaron Aogonek -40 -KPX Dcaron Aring -40 -KPX Dcaron Atilde -40 -KPX Dcaron V -70 -KPX Dcaron W -40 -KPX Dcaron Y -90 -KPX Dcaron Yacute -90 -KPX Dcaron Ydieresis -90 -KPX Dcaron comma -70 -KPX Dcaron period -70 -KPX Dcroat A -40 -KPX Dcroat Aacute -40 -KPX Dcroat Abreve -40 -KPX Dcroat Acircumflex -40 -KPX Dcroat Adieresis -40 -KPX Dcroat Agrave -40 -KPX Dcroat Amacron -40 -KPX Dcroat Aogonek -40 -KPX Dcroat Aring -40 -KPX Dcroat Atilde -40 -KPX Dcroat V -70 -KPX Dcroat W -40 -KPX Dcroat Y -90 -KPX Dcroat Yacute -90 -KPX Dcroat Ydieresis -90 -KPX Dcroat comma -70 -KPX Dcroat period -70 -KPX F A -80 -KPX F Aacute -80 -KPX F Abreve -80 -KPX F Acircumflex -80 -KPX F Adieresis -80 -KPX F Agrave -80 -KPX F Amacron -80 -KPX F Aogonek -80 -KPX F Aring -80 -KPX F Atilde -80 -KPX F a -50 -KPX F aacute -50 -KPX F abreve -50 -KPX F acircumflex -50 -KPX F adieresis -50 -KPX F agrave -50 -KPX F amacron -50 -KPX F aogonek -50 -KPX F aring -50 -KPX F atilde -50 -KPX F comma -150 -KPX F e -30 -KPX F eacute -30 -KPX F ecaron -30 -KPX F ecircumflex -30 -KPX F edieresis -30 -KPX F edotaccent -30 -KPX F egrave -30 -KPX F emacron -30 -KPX F eogonek -30 -KPX F o -30 -KPX F oacute -30 -KPX F ocircumflex -30 -KPX F odieresis -30 -KPX F ograve -30 -KPX F ohungarumlaut -30 -KPX F omacron -30 -KPX F oslash -30 -KPX F otilde -30 -KPX F period -150 -KPX F r -45 -KPX F racute -45 -KPX F rcaron -45 -KPX F rcommaaccent -45 -KPX J A -20 -KPX J Aacute -20 -KPX J Abreve -20 -KPX J Acircumflex -20 -KPX J Adieresis -20 -KPX J Agrave -20 -KPX J Amacron -20 -KPX J Aogonek -20 -KPX J Aring -20 -KPX J Atilde -20 -KPX J a -20 -KPX J aacute -20 -KPX J abreve -20 -KPX J acircumflex -20 -KPX J adieresis -20 -KPX J agrave -20 -KPX J amacron -20 -KPX J aogonek -20 -KPX J aring -20 -KPX J atilde -20 -KPX J comma -30 -KPX J period -30 -KPX J u -20 -KPX J uacute -20 -KPX J ucircumflex -20 -KPX J udieresis -20 -KPX J ugrave -20 -KPX J uhungarumlaut -20 -KPX J umacron -20 -KPX J uogonek -20 -KPX J uring -20 -KPX K O -50 -KPX K Oacute -50 -KPX K Ocircumflex -50 -KPX K Odieresis -50 -KPX K Ograve -50 -KPX K Ohungarumlaut -50 -KPX K Omacron -50 -KPX K Oslash -50 -KPX K Otilde -50 -KPX K e -40 -KPX K eacute -40 -KPX K ecaron -40 -KPX K ecircumflex -40 -KPX K edieresis -40 -KPX K edotaccent -40 -KPX K egrave -40 -KPX K emacron -40 -KPX K eogonek -40 -KPX K o -40 -KPX K oacute -40 -KPX K ocircumflex -40 -KPX K odieresis -40 -KPX K ograve -40 -KPX K ohungarumlaut -40 -KPX K omacron -40 -KPX K oslash -40 -KPX K otilde -40 -KPX K u -30 -KPX K uacute -30 -KPX K ucircumflex -30 -KPX K udieresis -30 -KPX K ugrave -30 -KPX K uhungarumlaut -30 -KPX K umacron -30 -KPX K uogonek -30 -KPX K uring -30 -KPX K y -50 -KPX K yacute -50 -KPX K ydieresis -50 -KPX Kcommaaccent O -50 -KPX Kcommaaccent Oacute -50 -KPX Kcommaaccent Ocircumflex -50 -KPX Kcommaaccent Odieresis -50 -KPX Kcommaaccent Ograve -50 -KPX Kcommaaccent Ohungarumlaut -50 -KPX Kcommaaccent Omacron -50 -KPX Kcommaaccent Oslash -50 -KPX Kcommaaccent Otilde -50 -KPX Kcommaaccent e -40 -KPX Kcommaaccent eacute -40 -KPX Kcommaaccent ecaron -40 -KPX Kcommaaccent ecircumflex -40 -KPX Kcommaaccent edieresis -40 -KPX Kcommaaccent edotaccent -40 -KPX Kcommaaccent egrave -40 -KPX Kcommaaccent emacron -40 -KPX Kcommaaccent eogonek -40 -KPX Kcommaaccent o -40 -KPX Kcommaaccent oacute -40 -KPX Kcommaaccent ocircumflex -40 -KPX Kcommaaccent odieresis -40 -KPX Kcommaaccent ograve -40 -KPX Kcommaaccent ohungarumlaut -40 -KPX Kcommaaccent omacron -40 -KPX Kcommaaccent oslash -40 -KPX Kcommaaccent otilde -40 -KPX Kcommaaccent u -30 -KPX Kcommaaccent uacute -30 -KPX Kcommaaccent ucircumflex -30 -KPX Kcommaaccent udieresis -30 -KPX Kcommaaccent ugrave -30 -KPX Kcommaaccent uhungarumlaut -30 -KPX Kcommaaccent umacron -30 -KPX Kcommaaccent uogonek -30 -KPX Kcommaaccent uring -30 -KPX Kcommaaccent y -50 -KPX Kcommaaccent yacute -50 -KPX Kcommaaccent ydieresis -50 -KPX L T -110 -KPX L Tcaron -110 -KPX L Tcommaaccent -110 -KPX L V -110 -KPX L W -70 -KPX L Y -140 -KPX L Yacute -140 -KPX L Ydieresis -140 -KPX L quotedblright -140 -KPX L quoteright -160 -KPX L y -30 -KPX L yacute -30 -KPX L ydieresis -30 -KPX Lacute T -110 -KPX Lacute Tcaron -110 -KPX Lacute Tcommaaccent -110 -KPX Lacute V -110 -KPX Lacute W -70 -KPX Lacute Y -140 -KPX Lacute Yacute -140 -KPX Lacute Ydieresis -140 -KPX Lacute quotedblright -140 -KPX Lacute quoteright -160 -KPX Lacute y -30 -KPX Lacute yacute -30 -KPX Lacute ydieresis -30 -KPX Lcaron T -110 -KPX Lcaron Tcaron -110 -KPX Lcaron Tcommaaccent -110 -KPX Lcaron V -110 -KPX Lcaron W -70 -KPX Lcaron Y -140 -KPX Lcaron Yacute -140 -KPX Lcaron Ydieresis -140 -KPX Lcaron quotedblright -140 -KPX Lcaron quoteright -160 -KPX Lcaron y -30 -KPX Lcaron yacute -30 -KPX Lcaron ydieresis -30 -KPX Lcommaaccent T -110 -KPX Lcommaaccent Tcaron -110 -KPX Lcommaaccent Tcommaaccent -110 -KPX Lcommaaccent V -110 -KPX Lcommaaccent W -70 -KPX Lcommaaccent Y -140 -KPX Lcommaaccent Yacute -140 -KPX Lcommaaccent Ydieresis -140 -KPX Lcommaaccent quotedblright -140 -KPX Lcommaaccent quoteright -160 -KPX Lcommaaccent y -30 -KPX Lcommaaccent yacute -30 -KPX Lcommaaccent ydieresis -30 -KPX Lslash T -110 -KPX Lslash Tcaron -110 -KPX Lslash Tcommaaccent -110 -KPX Lslash V -110 -KPX Lslash W -70 -KPX Lslash Y -140 -KPX Lslash Yacute -140 -KPX Lslash Ydieresis -140 -KPX Lslash quotedblright -140 -KPX Lslash quoteright -160 -KPX Lslash y -30 -KPX Lslash yacute -30 -KPX Lslash ydieresis -30 -KPX O A -20 -KPX O Aacute -20 -KPX O Abreve -20 -KPX O Acircumflex -20 -KPX O Adieresis -20 -KPX O Agrave -20 -KPX O Amacron -20 -KPX O Aogonek -20 -KPX O Aring -20 -KPX O Atilde -20 -KPX O T -40 -KPX O Tcaron -40 -KPX O Tcommaaccent -40 -KPX O V -50 -KPX O W -30 -KPX O X -60 -KPX O Y -70 -KPX O Yacute -70 -KPX O Ydieresis -70 -KPX O comma -40 -KPX O period -40 -KPX Oacute A -20 -KPX Oacute Aacute -20 -KPX Oacute Abreve -20 -KPX Oacute Acircumflex -20 -KPX Oacute Adieresis -20 -KPX Oacute Agrave -20 -KPX Oacute Amacron -20 -KPX Oacute Aogonek -20 -KPX Oacute Aring -20 -KPX Oacute Atilde -20 -KPX Oacute T -40 -KPX Oacute Tcaron -40 -KPX Oacute Tcommaaccent -40 -KPX Oacute V -50 -KPX Oacute W -30 -KPX Oacute X -60 -KPX Oacute Y -70 -KPX Oacute Yacute -70 -KPX Oacute Ydieresis -70 -KPX Oacute comma -40 -KPX Oacute period -40 -KPX Ocircumflex A -20 -KPX Ocircumflex Aacute -20 -KPX Ocircumflex Abreve -20 -KPX Ocircumflex Acircumflex -20 -KPX Ocircumflex Adieresis -20 -KPX Ocircumflex Agrave -20 -KPX Ocircumflex Amacron -20 -KPX Ocircumflex Aogonek -20 -KPX Ocircumflex Aring -20 -KPX Ocircumflex Atilde -20 -KPX Ocircumflex T -40 -KPX Ocircumflex Tcaron -40 -KPX Ocircumflex Tcommaaccent -40 -KPX Ocircumflex V -50 -KPX Ocircumflex W -30 -KPX Ocircumflex X -60 -KPX Ocircumflex Y -70 -KPX Ocircumflex Yacute -70 -KPX Ocircumflex Ydieresis -70 -KPX Ocircumflex comma -40 -KPX Ocircumflex period -40 -KPX Odieresis A -20 -KPX Odieresis Aacute -20 -KPX Odieresis Abreve -20 -KPX Odieresis Acircumflex -20 -KPX Odieresis Adieresis -20 -KPX Odieresis Agrave -20 -KPX Odieresis Amacron -20 -KPX Odieresis Aogonek -20 -KPX Odieresis Aring -20 -KPX Odieresis Atilde -20 -KPX Odieresis T -40 -KPX Odieresis Tcaron -40 -KPX Odieresis Tcommaaccent -40 -KPX Odieresis V -50 -KPX Odieresis W -30 -KPX Odieresis X -60 -KPX Odieresis Y -70 -KPX Odieresis Yacute -70 -KPX Odieresis Ydieresis -70 -KPX Odieresis comma -40 -KPX Odieresis period -40 -KPX Ograve A -20 -KPX Ograve Aacute -20 -KPX Ograve Abreve -20 -KPX Ograve Acircumflex -20 -KPX Ograve Adieresis -20 -KPX Ograve Agrave -20 -KPX Ograve Amacron -20 -KPX Ograve Aogonek -20 -KPX Ograve Aring -20 -KPX Ograve Atilde -20 -KPX Ograve T -40 -KPX Ograve Tcaron -40 -KPX Ograve Tcommaaccent -40 -KPX Ograve V -50 -KPX Ograve W -30 -KPX Ograve X -60 -KPX Ograve Y -70 -KPX Ograve Yacute -70 -KPX Ograve Ydieresis -70 -KPX Ograve comma -40 -KPX Ograve period -40 -KPX Ohungarumlaut A -20 -KPX Ohungarumlaut Aacute -20 -KPX Ohungarumlaut Abreve -20 -KPX Ohungarumlaut Acircumflex -20 -KPX Ohungarumlaut Adieresis -20 -KPX Ohungarumlaut Agrave -20 -KPX Ohungarumlaut Amacron -20 -KPX Ohungarumlaut Aogonek -20 -KPX Ohungarumlaut Aring -20 -KPX Ohungarumlaut Atilde -20 -KPX Ohungarumlaut T -40 -KPX Ohungarumlaut Tcaron -40 -KPX Ohungarumlaut Tcommaaccent -40 -KPX Ohungarumlaut V -50 -KPX Ohungarumlaut W -30 -KPX Ohungarumlaut X -60 -KPX Ohungarumlaut Y -70 -KPX Ohungarumlaut Yacute -70 -KPX Ohungarumlaut Ydieresis -70 -KPX Ohungarumlaut comma -40 -KPX Ohungarumlaut period -40 -KPX Omacron A -20 -KPX Omacron Aacute -20 -KPX Omacron Abreve -20 -KPX Omacron Acircumflex -20 -KPX Omacron Adieresis -20 -KPX Omacron Agrave -20 -KPX Omacron Amacron -20 -KPX Omacron Aogonek -20 -KPX Omacron Aring -20 -KPX Omacron Atilde -20 -KPX Omacron T -40 -KPX Omacron Tcaron -40 -KPX Omacron Tcommaaccent -40 -KPX Omacron V -50 -KPX Omacron W -30 -KPX Omacron X -60 -KPX Omacron Y -70 -KPX Omacron Yacute -70 -KPX Omacron Ydieresis -70 -KPX Omacron comma -40 -KPX Omacron period -40 -KPX Oslash A -20 -KPX Oslash Aacute -20 -KPX Oslash Abreve -20 -KPX Oslash Acircumflex -20 -KPX Oslash Adieresis -20 -KPX Oslash Agrave -20 -KPX Oslash Amacron -20 -KPX Oslash Aogonek -20 -KPX Oslash Aring -20 -KPX Oslash Atilde -20 -KPX Oslash T -40 -KPX Oslash Tcaron -40 -KPX Oslash Tcommaaccent -40 -KPX Oslash V -50 -KPX Oslash W -30 -KPX Oslash X -60 -KPX Oslash Y -70 -KPX Oslash Yacute -70 -KPX Oslash Ydieresis -70 -KPX Oslash comma -40 -KPX Oslash period -40 -KPX Otilde A -20 -KPX Otilde Aacute -20 -KPX Otilde Abreve -20 -KPX Otilde Acircumflex -20 -KPX Otilde Adieresis -20 -KPX Otilde Agrave -20 -KPX Otilde Amacron -20 -KPX Otilde Aogonek -20 -KPX Otilde Aring -20 -KPX Otilde Atilde -20 -KPX Otilde T -40 -KPX Otilde Tcaron -40 -KPX Otilde Tcommaaccent -40 -KPX Otilde V -50 -KPX Otilde W -30 -KPX Otilde X -60 -KPX Otilde Y -70 -KPX Otilde Yacute -70 -KPX Otilde Ydieresis -70 -KPX Otilde comma -40 -KPX Otilde period -40 -KPX P A -120 -KPX P Aacute -120 -KPX P Abreve -120 -KPX P Acircumflex -120 -KPX P Adieresis -120 -KPX P Agrave -120 -KPX P Amacron -120 -KPX P Aogonek -120 -KPX P Aring -120 -KPX P Atilde -120 -KPX P a -40 -KPX P aacute -40 -KPX P abreve -40 -KPX P acircumflex -40 -KPX P adieresis -40 -KPX P agrave -40 -KPX P amacron -40 -KPX P aogonek -40 -KPX P aring -40 -KPX P atilde -40 -KPX P comma -180 -KPX P e -50 -KPX P eacute -50 -KPX P ecaron -50 -KPX P ecircumflex -50 -KPX P edieresis -50 -KPX P edotaccent -50 -KPX P egrave -50 -KPX P emacron -50 -KPX P eogonek -50 -KPX P o -50 -KPX P oacute -50 -KPX P ocircumflex -50 -KPX P odieresis -50 -KPX P ograve -50 -KPX P ohungarumlaut -50 -KPX P omacron -50 -KPX P oslash -50 -KPX P otilde -50 -KPX P period -180 -KPX Q U -10 -KPX Q Uacute -10 -KPX Q Ucircumflex -10 -KPX Q Udieresis -10 -KPX Q Ugrave -10 -KPX Q Uhungarumlaut -10 -KPX Q Umacron -10 -KPX Q Uogonek -10 -KPX Q Uring -10 -KPX R O -20 -KPX R Oacute -20 -KPX R Ocircumflex -20 -KPX R Odieresis -20 -KPX R Ograve -20 -KPX R Ohungarumlaut -20 -KPX R Omacron -20 -KPX R Oslash -20 -KPX R Otilde -20 -KPX R T -30 -KPX R Tcaron -30 -KPX R Tcommaaccent -30 -KPX R U -40 -KPX R Uacute -40 -KPX R Ucircumflex -40 -KPX R Udieresis -40 -KPX R Ugrave -40 -KPX R Uhungarumlaut -40 -KPX R Umacron -40 -KPX R Uogonek -40 -KPX R Uring -40 -KPX R V -50 -KPX R W -30 -KPX R Y -50 -KPX R Yacute -50 -KPX R Ydieresis -50 -KPX Racute O -20 -KPX Racute Oacute -20 -KPX Racute Ocircumflex -20 -KPX Racute Odieresis -20 -KPX Racute Ograve -20 -KPX Racute Ohungarumlaut -20 -KPX Racute Omacron -20 -KPX Racute Oslash -20 -KPX Racute Otilde -20 -KPX Racute T -30 -KPX Racute Tcaron -30 -KPX Racute Tcommaaccent -30 -KPX Racute U -40 -KPX Racute Uacute -40 -KPX Racute Ucircumflex -40 -KPX Racute Udieresis -40 -KPX Racute Ugrave -40 -KPX Racute Uhungarumlaut -40 -KPX Racute Umacron -40 -KPX Racute Uogonek -40 -KPX Racute Uring -40 -KPX Racute V -50 -KPX Racute W -30 -KPX Racute Y -50 -KPX Racute Yacute -50 -KPX Racute Ydieresis -50 -KPX Rcaron O -20 -KPX Rcaron Oacute -20 -KPX Rcaron Ocircumflex -20 -KPX Rcaron Odieresis -20 -KPX Rcaron Ograve -20 -KPX Rcaron Ohungarumlaut -20 -KPX Rcaron Omacron -20 -KPX Rcaron Oslash -20 -KPX Rcaron Otilde -20 -KPX Rcaron T -30 -KPX Rcaron Tcaron -30 -KPX Rcaron Tcommaaccent -30 -KPX Rcaron U -40 -KPX Rcaron Uacute -40 -KPX Rcaron Ucircumflex -40 -KPX Rcaron Udieresis -40 -KPX Rcaron Ugrave -40 -KPX Rcaron Uhungarumlaut -40 -KPX Rcaron Umacron -40 -KPX Rcaron Uogonek -40 -KPX Rcaron Uring -40 -KPX Rcaron V -50 -KPX Rcaron W -30 -KPX Rcaron Y -50 -KPX Rcaron Yacute -50 -KPX Rcaron Ydieresis -50 -KPX Rcommaaccent O -20 -KPX Rcommaaccent Oacute -20 -KPX Rcommaaccent Ocircumflex -20 -KPX Rcommaaccent Odieresis -20 -KPX Rcommaaccent Ograve -20 -KPX Rcommaaccent Ohungarumlaut -20 -KPX Rcommaaccent Omacron -20 -KPX Rcommaaccent Oslash -20 -KPX Rcommaaccent Otilde -20 -KPX Rcommaaccent T -30 -KPX Rcommaaccent Tcaron -30 -KPX Rcommaaccent Tcommaaccent -30 -KPX Rcommaaccent U -40 -KPX Rcommaaccent Uacute -40 -KPX Rcommaaccent Ucircumflex -40 -KPX Rcommaaccent Udieresis -40 -KPX Rcommaaccent Ugrave -40 -KPX Rcommaaccent Uhungarumlaut -40 -KPX Rcommaaccent Umacron -40 -KPX Rcommaaccent Uogonek -40 -KPX Rcommaaccent Uring -40 -KPX Rcommaaccent V -50 -KPX Rcommaaccent W -30 -KPX Rcommaaccent Y -50 -KPX Rcommaaccent Yacute -50 -KPX Rcommaaccent Ydieresis -50 -KPX S comma -20 -KPX S period -20 -KPX Sacute comma -20 -KPX Sacute period -20 -KPX Scaron comma -20 -KPX Scaron period -20 -KPX Scedilla comma -20 -KPX Scedilla period -20 -KPX Scommaaccent comma -20 -KPX Scommaaccent period -20 -KPX T A -120 -KPX T Aacute -120 -KPX T Abreve -120 -KPX T Acircumflex -120 -KPX T Adieresis -120 -KPX T Agrave -120 -KPX T Amacron -120 -KPX T Aogonek -120 -KPX T Aring -120 -KPX T Atilde -120 -KPX T O -40 -KPX T Oacute -40 -KPX T Ocircumflex -40 -KPX T Odieresis -40 -KPX T Ograve -40 -KPX T Ohungarumlaut -40 -KPX T Omacron -40 -KPX T Oslash -40 -KPX T Otilde -40 -KPX T a -120 -KPX T aacute -120 -KPX T abreve -60 -KPX T acircumflex -120 -KPX T adieresis -120 -KPX T agrave -120 -KPX T amacron -60 -KPX T aogonek -120 -KPX T aring -120 -KPX T atilde -60 -KPX T colon -20 -KPX T comma -120 -KPX T e -120 -KPX T eacute -120 -KPX T ecaron -120 -KPX T ecircumflex -120 -KPX T edieresis -120 -KPX T edotaccent -120 -KPX T egrave -60 -KPX T emacron -60 -KPX T eogonek -120 -KPX T hyphen -140 -KPX T o -120 -KPX T oacute -120 -KPX T ocircumflex -120 -KPX T odieresis -120 -KPX T ograve -120 -KPX T ohungarumlaut -120 -KPX T omacron -60 -KPX T oslash -120 -KPX T otilde -60 -KPX T period -120 -KPX T r -120 -KPX T racute -120 -KPX T rcaron -120 -KPX T rcommaaccent -120 -KPX T semicolon -20 -KPX T u -120 -KPX T uacute -120 -KPX T ucircumflex -120 -KPX T udieresis -120 -KPX T ugrave -120 -KPX T uhungarumlaut -120 -KPX T umacron -60 -KPX T uogonek -120 -KPX T uring -120 -KPX T w -120 -KPX T y -120 -KPX T yacute -120 -KPX T ydieresis -60 -KPX Tcaron A -120 -KPX Tcaron Aacute -120 -KPX Tcaron Abreve -120 -KPX Tcaron Acircumflex -120 -KPX Tcaron Adieresis -120 -KPX Tcaron Agrave -120 -KPX Tcaron Amacron -120 -KPX Tcaron Aogonek -120 -KPX Tcaron Aring -120 -KPX Tcaron Atilde -120 -KPX Tcaron O -40 -KPX Tcaron Oacute -40 -KPX Tcaron Ocircumflex -40 -KPX Tcaron Odieresis -40 -KPX Tcaron Ograve -40 -KPX Tcaron Ohungarumlaut -40 -KPX Tcaron Omacron -40 -KPX Tcaron Oslash -40 -KPX Tcaron Otilde -40 -KPX Tcaron a -120 -KPX Tcaron aacute -120 -KPX Tcaron abreve -60 -KPX Tcaron acircumflex -120 -KPX Tcaron adieresis -120 -KPX Tcaron agrave -120 -KPX Tcaron amacron -60 -KPX Tcaron aogonek -120 -KPX Tcaron aring -120 -KPX Tcaron atilde -60 -KPX Tcaron colon -20 -KPX Tcaron comma -120 -KPX Tcaron e -120 -KPX Tcaron eacute -120 -KPX Tcaron ecaron -120 -KPX Tcaron ecircumflex -120 -KPX Tcaron edieresis -120 -KPX Tcaron edotaccent -120 -KPX Tcaron egrave -60 -KPX Tcaron emacron -60 -KPX Tcaron eogonek -120 -KPX Tcaron hyphen -140 -KPX Tcaron o -120 -KPX Tcaron oacute -120 -KPX Tcaron ocircumflex -120 -KPX Tcaron odieresis -120 -KPX Tcaron ograve -120 -KPX Tcaron ohungarumlaut -120 -KPX Tcaron omacron -60 -KPX Tcaron oslash -120 -KPX Tcaron otilde -60 -KPX Tcaron period -120 -KPX Tcaron r -120 -KPX Tcaron racute -120 -KPX Tcaron rcaron -120 -KPX Tcaron rcommaaccent -120 -KPX Tcaron semicolon -20 -KPX Tcaron u -120 -KPX Tcaron uacute -120 -KPX Tcaron ucircumflex -120 -KPX Tcaron udieresis -120 -KPX Tcaron ugrave -120 -KPX Tcaron uhungarumlaut -120 -KPX Tcaron umacron -60 -KPX Tcaron uogonek -120 -KPX Tcaron uring -120 -KPX Tcaron w -120 -KPX Tcaron y -120 -KPX Tcaron yacute -120 -KPX Tcaron ydieresis -60 -KPX Tcommaaccent A -120 -KPX Tcommaaccent Aacute -120 -KPX Tcommaaccent Abreve -120 -KPX Tcommaaccent Acircumflex -120 -KPX Tcommaaccent Adieresis -120 -KPX Tcommaaccent Agrave -120 -KPX Tcommaaccent Amacron -120 -KPX Tcommaaccent Aogonek -120 -KPX Tcommaaccent Aring -120 -KPX Tcommaaccent Atilde -120 -KPX Tcommaaccent O -40 -KPX Tcommaaccent Oacute -40 -KPX Tcommaaccent Ocircumflex -40 -KPX Tcommaaccent Odieresis -40 -KPX Tcommaaccent Ograve -40 -KPX Tcommaaccent Ohungarumlaut -40 -KPX Tcommaaccent Omacron -40 -KPX Tcommaaccent Oslash -40 -KPX Tcommaaccent Otilde -40 -KPX Tcommaaccent a -120 -KPX Tcommaaccent aacute -120 -KPX Tcommaaccent abreve -60 -KPX Tcommaaccent acircumflex -120 -KPX Tcommaaccent adieresis -120 -KPX Tcommaaccent agrave -120 -KPX Tcommaaccent amacron -60 -KPX Tcommaaccent aogonek -120 -KPX Tcommaaccent aring -120 -KPX Tcommaaccent atilde -60 -KPX Tcommaaccent colon -20 -KPX Tcommaaccent comma -120 -KPX Tcommaaccent e -120 -KPX Tcommaaccent eacute -120 -KPX Tcommaaccent ecaron -120 -KPX Tcommaaccent ecircumflex -120 -KPX Tcommaaccent edieresis -120 -KPX Tcommaaccent edotaccent -120 -KPX Tcommaaccent egrave -60 -KPX Tcommaaccent emacron -60 -KPX Tcommaaccent eogonek -120 -KPX Tcommaaccent hyphen -140 -KPX Tcommaaccent o -120 -KPX Tcommaaccent oacute -120 -KPX Tcommaaccent ocircumflex -120 -KPX Tcommaaccent odieresis -120 -KPX Tcommaaccent ograve -120 -KPX Tcommaaccent ohungarumlaut -120 -KPX Tcommaaccent omacron -60 -KPX Tcommaaccent oslash -120 -KPX Tcommaaccent otilde -60 -KPX Tcommaaccent period -120 -KPX Tcommaaccent r -120 -KPX Tcommaaccent racute -120 -KPX Tcommaaccent rcaron -120 -KPX Tcommaaccent rcommaaccent -120 -KPX Tcommaaccent semicolon -20 -KPX Tcommaaccent u -120 -KPX Tcommaaccent uacute -120 -KPX Tcommaaccent ucircumflex -120 -KPX Tcommaaccent udieresis -120 -KPX Tcommaaccent ugrave -120 -KPX Tcommaaccent uhungarumlaut -120 -KPX Tcommaaccent umacron -60 -KPX Tcommaaccent uogonek -120 -KPX Tcommaaccent uring -120 -KPX Tcommaaccent w -120 -KPX Tcommaaccent y -120 -KPX Tcommaaccent yacute -120 -KPX Tcommaaccent ydieresis -60 -KPX U A -40 -KPX U Aacute -40 -KPX U Abreve -40 -KPX U Acircumflex -40 -KPX U Adieresis -40 -KPX U Agrave -40 -KPX U Amacron -40 -KPX U Aogonek -40 -KPX U Aring -40 -KPX U Atilde -40 -KPX U comma -40 -KPX U period -40 -KPX Uacute A -40 -KPX Uacute Aacute -40 -KPX Uacute Abreve -40 -KPX Uacute Acircumflex -40 -KPX Uacute Adieresis -40 -KPX Uacute Agrave -40 -KPX Uacute Amacron -40 -KPX Uacute Aogonek -40 -KPX Uacute Aring -40 -KPX Uacute Atilde -40 -KPX Uacute comma -40 -KPX Uacute period -40 -KPX Ucircumflex A -40 -KPX Ucircumflex Aacute -40 -KPX Ucircumflex Abreve -40 -KPX Ucircumflex Acircumflex -40 -KPX Ucircumflex Adieresis -40 -KPX Ucircumflex Agrave -40 -KPX Ucircumflex Amacron -40 -KPX Ucircumflex Aogonek -40 -KPX Ucircumflex Aring -40 -KPX Ucircumflex Atilde -40 -KPX Ucircumflex comma -40 -KPX Ucircumflex period -40 -KPX Udieresis A -40 -KPX Udieresis Aacute -40 -KPX Udieresis Abreve -40 -KPX Udieresis Acircumflex -40 -KPX Udieresis Adieresis -40 -KPX Udieresis Agrave -40 -KPX Udieresis Amacron -40 -KPX Udieresis Aogonek -40 -KPX Udieresis Aring -40 -KPX Udieresis Atilde -40 -KPX Udieresis comma -40 -KPX Udieresis period -40 -KPX Ugrave A -40 -KPX Ugrave Aacute -40 -KPX Ugrave Abreve -40 -KPX Ugrave Acircumflex -40 -KPX Ugrave Adieresis -40 -KPX Ugrave Agrave -40 -KPX Ugrave Amacron -40 -KPX Ugrave Aogonek -40 -KPX Ugrave Aring -40 -KPX Ugrave Atilde -40 -KPX Ugrave comma -40 -KPX Ugrave period -40 -KPX Uhungarumlaut A -40 -KPX Uhungarumlaut Aacute -40 -KPX Uhungarumlaut Abreve -40 -KPX Uhungarumlaut Acircumflex -40 -KPX Uhungarumlaut Adieresis -40 -KPX Uhungarumlaut Agrave -40 -KPX Uhungarumlaut Amacron -40 -KPX Uhungarumlaut Aogonek -40 -KPX Uhungarumlaut Aring -40 -KPX Uhungarumlaut Atilde -40 -KPX Uhungarumlaut comma -40 -KPX Uhungarumlaut period -40 -KPX Umacron A -40 -KPX Umacron Aacute -40 -KPX Umacron Abreve -40 -KPX Umacron Acircumflex -40 -KPX Umacron Adieresis -40 -KPX Umacron Agrave -40 -KPX Umacron Amacron -40 -KPX Umacron Aogonek -40 -KPX Umacron Aring -40 -KPX Umacron Atilde -40 -KPX Umacron comma -40 -KPX Umacron period -40 -KPX Uogonek A -40 -KPX Uogonek Aacute -40 -KPX Uogonek Abreve -40 -KPX Uogonek Acircumflex -40 -KPX Uogonek Adieresis -40 -KPX Uogonek Agrave -40 -KPX Uogonek Amacron -40 -KPX Uogonek Aogonek -40 -KPX Uogonek Aring -40 -KPX Uogonek Atilde -40 -KPX Uogonek comma -40 -KPX Uogonek period -40 -KPX Uring A -40 -KPX Uring Aacute -40 -KPX Uring Abreve -40 -KPX Uring Acircumflex -40 -KPX Uring Adieresis -40 -KPX Uring Agrave -40 -KPX Uring Amacron -40 -KPX Uring Aogonek -40 -KPX Uring Aring -40 -KPX Uring Atilde -40 -KPX Uring comma -40 -KPX Uring period -40 -KPX V A -80 -KPX V Aacute -80 -KPX V Abreve -80 -KPX V Acircumflex -80 -KPX V Adieresis -80 -KPX V Agrave -80 -KPX V Amacron -80 -KPX V Aogonek -80 -KPX V Aring -80 -KPX V Atilde -80 -KPX V G -40 -KPX V Gbreve -40 -KPX V Gcommaaccent -40 -KPX V O -40 -KPX V Oacute -40 -KPX V Ocircumflex -40 -KPX V Odieresis -40 -KPX V Ograve -40 -KPX V Ohungarumlaut -40 -KPX V Omacron -40 -KPX V Oslash -40 -KPX V Otilde -40 -KPX V a -70 -KPX V aacute -70 -KPX V abreve -70 -KPX V acircumflex -70 -KPX V adieresis -70 -KPX V agrave -70 -KPX V amacron -70 -KPX V aogonek -70 -KPX V aring -70 -KPX V atilde -70 -KPX V colon -40 -KPX V comma -125 -KPX V e -80 -KPX V eacute -80 -KPX V ecaron -80 -KPX V ecircumflex -80 -KPX V edieresis -80 -KPX V edotaccent -80 -KPX V egrave -80 -KPX V emacron -80 -KPX V eogonek -80 -KPX V hyphen -80 -KPX V o -80 -KPX V oacute -80 -KPX V ocircumflex -80 -KPX V odieresis -80 -KPX V ograve -80 -KPX V ohungarumlaut -80 -KPX V omacron -80 -KPX V oslash -80 -KPX V otilde -80 -KPX V period -125 -KPX V semicolon -40 -KPX V u -70 -KPX V uacute -70 -KPX V ucircumflex -70 -KPX V udieresis -70 -KPX V ugrave -70 -KPX V uhungarumlaut -70 -KPX V umacron -70 -KPX V uogonek -70 -KPX V uring -70 -KPX W A -50 -KPX W Aacute -50 -KPX W Abreve -50 -KPX W Acircumflex -50 -KPX W Adieresis -50 -KPX W Agrave -50 -KPX W Amacron -50 -KPX W Aogonek -50 -KPX W Aring -50 -KPX W Atilde -50 -KPX W O -20 -KPX W Oacute -20 -KPX W Ocircumflex -20 -KPX W Odieresis -20 -KPX W Ograve -20 -KPX W Ohungarumlaut -20 -KPX W Omacron -20 -KPX W Oslash -20 -KPX W Otilde -20 -KPX W a -40 -KPX W aacute -40 -KPX W abreve -40 -KPX W acircumflex -40 -KPX W adieresis -40 -KPX W agrave -40 -KPX W amacron -40 -KPX W aogonek -40 -KPX W aring -40 -KPX W atilde -40 -KPX W comma -80 -KPX W e -30 -KPX W eacute -30 -KPX W ecaron -30 -KPX W ecircumflex -30 -KPX W edieresis -30 -KPX W edotaccent -30 -KPX W egrave -30 -KPX W emacron -30 -KPX W eogonek -30 -KPX W hyphen -40 -KPX W o -30 -KPX W oacute -30 -KPX W ocircumflex -30 -KPX W odieresis -30 -KPX W ograve -30 -KPX W ohungarumlaut -30 -KPX W omacron -30 -KPX W oslash -30 -KPX W otilde -30 -KPX W period -80 -KPX W u -30 -KPX W uacute -30 -KPX W ucircumflex -30 -KPX W udieresis -30 -KPX W ugrave -30 -KPX W uhungarumlaut -30 -KPX W umacron -30 -KPX W uogonek -30 -KPX W uring -30 -KPX W y -20 -KPX W yacute -20 -KPX W ydieresis -20 -KPX Y A -110 -KPX Y Aacute -110 -KPX Y Abreve -110 -KPX Y Acircumflex -110 -KPX Y Adieresis -110 -KPX Y Agrave -110 -KPX Y Amacron -110 -KPX Y Aogonek -110 -KPX Y Aring -110 -KPX Y Atilde -110 -KPX Y O -85 -KPX Y Oacute -85 -KPX Y Ocircumflex -85 -KPX Y Odieresis -85 -KPX Y Ograve -85 -KPX Y Ohungarumlaut -85 -KPX Y Omacron -85 -KPX Y Oslash -85 -KPX Y Otilde -85 -KPX Y a -140 -KPX Y aacute -140 -KPX Y abreve -70 -KPX Y acircumflex -140 -KPX Y adieresis -140 -KPX Y agrave -140 -KPX Y amacron -70 -KPX Y aogonek -140 -KPX Y aring -140 -KPX Y atilde -140 -KPX Y colon -60 -KPX Y comma -140 -KPX Y e -140 -KPX Y eacute -140 -KPX Y ecaron -140 -KPX Y ecircumflex -140 -KPX Y edieresis -140 -KPX Y edotaccent -140 -KPX Y egrave -140 -KPX Y emacron -70 -KPX Y eogonek -140 -KPX Y hyphen -140 -KPX Y i -20 -KPX Y iacute -20 -KPX Y iogonek -20 -KPX Y o -140 -KPX Y oacute -140 -KPX Y ocircumflex -140 -KPX Y odieresis -140 -KPX Y ograve -140 -KPX Y ohungarumlaut -140 -KPX Y omacron -140 -KPX Y oslash -140 -KPX Y otilde -140 -KPX Y period -140 -KPX Y semicolon -60 -KPX Y u -110 -KPX Y uacute -110 -KPX Y ucircumflex -110 -KPX Y udieresis -110 -KPX Y ugrave -110 -KPX Y uhungarumlaut -110 -KPX Y umacron -110 -KPX Y uogonek -110 -KPX Y uring -110 -KPX Yacute A -110 -KPX Yacute Aacute -110 -KPX Yacute Abreve -110 -KPX Yacute Acircumflex -110 -KPX Yacute Adieresis -110 -KPX Yacute Agrave -110 -KPX Yacute Amacron -110 -KPX Yacute Aogonek -110 -KPX Yacute Aring -110 -KPX Yacute Atilde -110 -KPX Yacute O -85 -KPX Yacute Oacute -85 -KPX Yacute Ocircumflex -85 -KPX Yacute Odieresis -85 -KPX Yacute Ograve -85 -KPX Yacute Ohungarumlaut -85 -KPX Yacute Omacron -85 -KPX Yacute Oslash -85 -KPX Yacute Otilde -85 -KPX Yacute a -140 -KPX Yacute aacute -140 -KPX Yacute abreve -70 -KPX Yacute acircumflex -140 -KPX Yacute adieresis -140 -KPX Yacute agrave -140 -KPX Yacute amacron -70 -KPX Yacute aogonek -140 -KPX Yacute aring -140 -KPX Yacute atilde -70 -KPX Yacute colon -60 -KPX Yacute comma -140 -KPX Yacute e -140 -KPX Yacute eacute -140 -KPX Yacute ecaron -140 -KPX Yacute ecircumflex -140 -KPX Yacute edieresis -140 -KPX Yacute edotaccent -140 -KPX Yacute egrave -140 -KPX Yacute emacron -70 -KPX Yacute eogonek -140 -KPX Yacute hyphen -140 -KPX Yacute i -20 -KPX Yacute iacute -20 -KPX Yacute iogonek -20 -KPX Yacute o -140 -KPX Yacute oacute -140 -KPX Yacute ocircumflex -140 -KPX Yacute odieresis -140 -KPX Yacute ograve -140 -KPX Yacute ohungarumlaut -140 -KPX Yacute omacron -70 -KPX Yacute oslash -140 -KPX Yacute otilde -140 -KPX Yacute period -140 -KPX Yacute semicolon -60 -KPX Yacute u -110 -KPX Yacute uacute -110 -KPX Yacute ucircumflex -110 -KPX Yacute udieresis -110 -KPX Yacute ugrave -110 -KPX Yacute uhungarumlaut -110 -KPX Yacute umacron -110 -KPX Yacute uogonek -110 -KPX Yacute uring -110 -KPX Ydieresis A -110 -KPX Ydieresis Aacute -110 -KPX Ydieresis Abreve -110 -KPX Ydieresis Acircumflex -110 -KPX Ydieresis Adieresis -110 -KPX Ydieresis Agrave -110 -KPX Ydieresis Amacron -110 -KPX Ydieresis Aogonek -110 -KPX Ydieresis Aring -110 -KPX Ydieresis Atilde -110 -KPX Ydieresis O -85 -KPX Ydieresis Oacute -85 -KPX Ydieresis Ocircumflex -85 -KPX Ydieresis Odieresis -85 -KPX Ydieresis Ograve -85 -KPX Ydieresis Ohungarumlaut -85 -KPX Ydieresis Omacron -85 -KPX Ydieresis Oslash -85 -KPX Ydieresis Otilde -85 -KPX Ydieresis a -140 -KPX Ydieresis aacute -140 -KPX Ydieresis abreve -70 -KPX Ydieresis acircumflex -140 -KPX Ydieresis adieresis -140 -KPX Ydieresis agrave -140 -KPX Ydieresis amacron -70 -KPX Ydieresis aogonek -140 -KPX Ydieresis aring -140 -KPX Ydieresis atilde -70 -KPX Ydieresis colon -60 -KPX Ydieresis comma -140 -KPX Ydieresis e -140 -KPX Ydieresis eacute -140 -KPX Ydieresis ecaron -140 -KPX Ydieresis ecircumflex -140 -KPX Ydieresis edieresis -140 -KPX Ydieresis edotaccent -140 -KPX Ydieresis egrave -140 -KPX Ydieresis emacron -70 -KPX Ydieresis eogonek -140 -KPX Ydieresis hyphen -140 -KPX Ydieresis i -20 -KPX Ydieresis iacute -20 -KPX Ydieresis iogonek -20 -KPX Ydieresis o -140 -KPX Ydieresis oacute -140 -KPX Ydieresis ocircumflex -140 -KPX Ydieresis odieresis -140 -KPX Ydieresis ograve -140 -KPX Ydieresis ohungarumlaut -140 -KPX Ydieresis omacron -140 -KPX Ydieresis oslash -140 -KPX Ydieresis otilde -140 -KPX Ydieresis period -140 -KPX Ydieresis semicolon -60 -KPX Ydieresis u -110 -KPX Ydieresis uacute -110 -KPX Ydieresis ucircumflex -110 -KPX Ydieresis udieresis -110 -KPX Ydieresis ugrave -110 -KPX Ydieresis uhungarumlaut -110 -KPX Ydieresis umacron -110 -KPX Ydieresis uogonek -110 -KPX Ydieresis uring -110 -KPX a v -20 -KPX a w -20 -KPX a y -30 -KPX a yacute -30 -KPX a ydieresis -30 -KPX aacute v -20 -KPX aacute w -20 -KPX aacute y -30 -KPX aacute yacute -30 -KPX aacute ydieresis -30 -KPX abreve v -20 -KPX abreve w -20 -KPX abreve y -30 -KPX abreve yacute -30 -KPX abreve ydieresis -30 -KPX acircumflex v -20 -KPX acircumflex w -20 -KPX acircumflex y -30 -KPX acircumflex yacute -30 -KPX acircumflex ydieresis -30 -KPX adieresis v -20 -KPX adieresis w -20 -KPX adieresis y -30 -KPX adieresis yacute -30 -KPX adieresis ydieresis -30 -KPX agrave v -20 -KPX agrave w -20 -KPX agrave y -30 -KPX agrave yacute -30 -KPX agrave ydieresis -30 -KPX amacron v -20 -KPX amacron w -20 -KPX amacron y -30 -KPX amacron yacute -30 -KPX amacron ydieresis -30 -KPX aogonek v -20 -KPX aogonek w -20 -KPX aogonek y -30 -KPX aogonek yacute -30 -KPX aogonek ydieresis -30 -KPX aring v -20 -KPX aring w -20 -KPX aring y -30 -KPX aring yacute -30 -KPX aring ydieresis -30 -KPX atilde v -20 -KPX atilde w -20 -KPX atilde y -30 -KPX atilde yacute -30 -KPX atilde ydieresis -30 -KPX b b -10 -KPX b comma -40 -KPX b l -20 -KPX b lacute -20 -KPX b lcommaaccent -20 -KPX b lslash -20 -KPX b period -40 -KPX b u -20 -KPX b uacute -20 -KPX b ucircumflex -20 -KPX b udieresis -20 -KPX b ugrave -20 -KPX b uhungarumlaut -20 -KPX b umacron -20 -KPX b uogonek -20 -KPX b uring -20 -KPX b v -20 -KPX b y -20 -KPX b yacute -20 -KPX b ydieresis -20 -KPX c comma -15 -KPX c k -20 -KPX c kcommaaccent -20 -KPX cacute comma -15 -KPX cacute k -20 -KPX cacute kcommaaccent -20 -KPX ccaron comma -15 -KPX ccaron k -20 -KPX ccaron kcommaaccent -20 -KPX ccedilla comma -15 -KPX ccedilla k -20 -KPX ccedilla kcommaaccent -20 -KPX colon space -50 -KPX comma quotedblright -100 -KPX comma quoteright -100 -KPX e comma -15 -KPX e period -15 -KPX e v -30 -KPX e w -20 -KPX e x -30 -KPX e y -20 -KPX e yacute -20 -KPX e ydieresis -20 -KPX eacute comma -15 -KPX eacute period -15 -KPX eacute v -30 -KPX eacute w -20 -KPX eacute x -30 -KPX eacute y -20 -KPX eacute yacute -20 -KPX eacute ydieresis -20 -KPX ecaron comma -15 -KPX ecaron period -15 -KPX ecaron v -30 -KPX ecaron w -20 -KPX ecaron x -30 -KPX ecaron y -20 -KPX ecaron yacute -20 -KPX ecaron ydieresis -20 -KPX ecircumflex comma -15 -KPX ecircumflex period -15 -KPX ecircumflex v -30 -KPX ecircumflex w -20 -KPX ecircumflex x -30 -KPX ecircumflex y -20 -KPX ecircumflex yacute -20 -KPX ecircumflex ydieresis -20 -KPX edieresis comma -15 -KPX edieresis period -15 -KPX edieresis v -30 -KPX edieresis w -20 -KPX edieresis x -30 -KPX edieresis y -20 -KPX edieresis yacute -20 -KPX edieresis ydieresis -20 -KPX edotaccent comma -15 -KPX edotaccent period -15 -KPX edotaccent v -30 -KPX edotaccent w -20 -KPX edotaccent x -30 -KPX edotaccent y -20 -KPX edotaccent yacute -20 -KPX edotaccent ydieresis -20 -KPX egrave comma -15 -KPX egrave period -15 -KPX egrave v -30 -KPX egrave w -20 -KPX egrave x -30 -KPX egrave y -20 -KPX egrave yacute -20 -KPX egrave ydieresis -20 -KPX emacron comma -15 -KPX emacron period -15 -KPX emacron v -30 -KPX emacron w -20 -KPX emacron x -30 -KPX emacron y -20 -KPX emacron yacute -20 -KPX emacron ydieresis -20 -KPX eogonek comma -15 -KPX eogonek period -15 -KPX eogonek v -30 -KPX eogonek w -20 -KPX eogonek x -30 -KPX eogonek y -20 -KPX eogonek yacute -20 -KPX eogonek ydieresis -20 -KPX f a -30 -KPX f aacute -30 -KPX f abreve -30 -KPX f acircumflex -30 -KPX f adieresis -30 -KPX f agrave -30 -KPX f amacron -30 -KPX f aogonek -30 -KPX f aring -30 -KPX f atilde -30 -KPX f comma -30 -KPX f dotlessi -28 -KPX f e -30 -KPX f eacute -30 -KPX f ecaron -30 -KPX f ecircumflex -30 -KPX f edieresis -30 -KPX f edotaccent -30 -KPX f egrave -30 -KPX f emacron -30 -KPX f eogonek -30 -KPX f o -30 -KPX f oacute -30 -KPX f ocircumflex -30 -KPX f odieresis -30 -KPX f ograve -30 -KPX f ohungarumlaut -30 -KPX f omacron -30 -KPX f oslash -30 -KPX f otilde -30 -KPX f period -30 -KPX f quotedblright 60 -KPX f quoteright 50 -KPX g r -10 -KPX g racute -10 -KPX g rcaron -10 -KPX g rcommaaccent -10 -KPX gbreve r -10 -KPX gbreve racute -10 -KPX gbreve rcaron -10 -KPX gbreve rcommaaccent -10 -KPX gcommaaccent r -10 -KPX gcommaaccent racute -10 -KPX gcommaaccent rcaron -10 -KPX gcommaaccent rcommaaccent -10 -KPX h y -30 -KPX h yacute -30 -KPX h ydieresis -30 -KPX k e -20 -KPX k eacute -20 -KPX k ecaron -20 -KPX k ecircumflex -20 -KPX k edieresis -20 -KPX k edotaccent -20 -KPX k egrave -20 -KPX k emacron -20 -KPX k eogonek -20 -KPX k o -20 -KPX k oacute -20 -KPX k ocircumflex -20 -KPX k odieresis -20 -KPX k ograve -20 -KPX k ohungarumlaut -20 -KPX k omacron -20 -KPX k oslash -20 -KPX k otilde -20 -KPX kcommaaccent e -20 -KPX kcommaaccent eacute -20 -KPX kcommaaccent ecaron -20 -KPX kcommaaccent ecircumflex -20 -KPX kcommaaccent edieresis -20 -KPX kcommaaccent edotaccent -20 -KPX kcommaaccent egrave -20 -KPX kcommaaccent emacron -20 -KPX kcommaaccent eogonek -20 -KPX kcommaaccent o -20 -KPX kcommaaccent oacute -20 -KPX kcommaaccent ocircumflex -20 -KPX kcommaaccent odieresis -20 -KPX kcommaaccent ograve -20 -KPX kcommaaccent ohungarumlaut -20 -KPX kcommaaccent omacron -20 -KPX kcommaaccent oslash -20 -KPX kcommaaccent otilde -20 -KPX m u -10 -KPX m uacute -10 -KPX m ucircumflex -10 -KPX m udieresis -10 -KPX m ugrave -10 -KPX m uhungarumlaut -10 -KPX m umacron -10 -KPX m uogonek -10 -KPX m uring -10 -KPX m y -15 -KPX m yacute -15 -KPX m ydieresis -15 -KPX n u -10 -KPX n uacute -10 -KPX n ucircumflex -10 -KPX n udieresis -10 -KPX n ugrave -10 -KPX n uhungarumlaut -10 -KPX n umacron -10 -KPX n uogonek -10 -KPX n uring -10 -KPX n v -20 -KPX n y -15 -KPX n yacute -15 -KPX n ydieresis -15 -KPX nacute u -10 -KPX nacute uacute -10 -KPX nacute ucircumflex -10 -KPX nacute udieresis -10 -KPX nacute ugrave -10 -KPX nacute uhungarumlaut -10 -KPX nacute umacron -10 -KPX nacute uogonek -10 -KPX nacute uring -10 -KPX nacute v -20 -KPX nacute y -15 -KPX nacute yacute -15 -KPX nacute ydieresis -15 -KPX ncaron u -10 -KPX ncaron uacute -10 -KPX ncaron ucircumflex -10 -KPX ncaron udieresis -10 -KPX ncaron ugrave -10 -KPX ncaron uhungarumlaut -10 -KPX ncaron umacron -10 -KPX ncaron uogonek -10 -KPX ncaron uring -10 -KPX ncaron v -20 -KPX ncaron y -15 -KPX ncaron yacute -15 -KPX ncaron ydieresis -15 -KPX ncommaaccent u -10 -KPX ncommaaccent uacute -10 -KPX ncommaaccent ucircumflex -10 -KPX ncommaaccent udieresis -10 -KPX ncommaaccent ugrave -10 -KPX ncommaaccent uhungarumlaut -10 -KPX ncommaaccent umacron -10 -KPX ncommaaccent uogonek -10 -KPX ncommaaccent uring -10 -KPX ncommaaccent v -20 -KPX ncommaaccent y -15 -KPX ncommaaccent yacute -15 -KPX ncommaaccent ydieresis -15 -KPX ntilde u -10 -KPX ntilde uacute -10 -KPX ntilde ucircumflex -10 -KPX ntilde udieresis -10 -KPX ntilde ugrave -10 -KPX ntilde uhungarumlaut -10 -KPX ntilde umacron -10 -KPX ntilde uogonek -10 -KPX ntilde uring -10 -KPX ntilde v -20 -KPX ntilde y -15 -KPX ntilde yacute -15 -KPX ntilde ydieresis -15 -KPX o comma -40 -KPX o period -40 -KPX o v -15 -KPX o w -15 -KPX o x -30 -KPX o y -30 -KPX o yacute -30 -KPX o ydieresis -30 -KPX oacute comma -40 -KPX oacute period -40 -KPX oacute v -15 -KPX oacute w -15 -KPX oacute x -30 -KPX oacute y -30 -KPX oacute yacute -30 -KPX oacute ydieresis -30 -KPX ocircumflex comma -40 -KPX ocircumflex period -40 -KPX ocircumflex v -15 -KPX ocircumflex w -15 -KPX ocircumflex x -30 -KPX ocircumflex y -30 -KPX ocircumflex yacute -30 -KPX ocircumflex ydieresis -30 -KPX odieresis comma -40 -KPX odieresis period -40 -KPX odieresis v -15 -KPX odieresis w -15 -KPX odieresis x -30 -KPX odieresis y -30 -KPX odieresis yacute -30 -KPX odieresis ydieresis -30 -KPX ograve comma -40 -KPX ograve period -40 -KPX ograve v -15 -KPX ograve w -15 -KPX ograve x -30 -KPX ograve y -30 -KPX ograve yacute -30 -KPX ograve ydieresis -30 -KPX ohungarumlaut comma -40 -KPX ohungarumlaut period -40 -KPX ohungarumlaut v -15 -KPX ohungarumlaut w -15 -KPX ohungarumlaut x -30 -KPX ohungarumlaut y -30 -KPX ohungarumlaut yacute -30 -KPX ohungarumlaut ydieresis -30 -KPX omacron comma -40 -KPX omacron period -40 -KPX omacron v -15 -KPX omacron w -15 -KPX omacron x -30 -KPX omacron y -30 -KPX omacron yacute -30 -KPX omacron ydieresis -30 -KPX oslash a -55 -KPX oslash aacute -55 -KPX oslash abreve -55 -KPX oslash acircumflex -55 -KPX oslash adieresis -55 -KPX oslash agrave -55 -KPX oslash amacron -55 -KPX oslash aogonek -55 -KPX oslash aring -55 -KPX oslash atilde -55 -KPX oslash b -55 -KPX oslash c -55 -KPX oslash cacute -55 -KPX oslash ccaron -55 -KPX oslash ccedilla -55 -KPX oslash comma -95 -KPX oslash d -55 -KPX oslash dcroat -55 -KPX oslash e -55 -KPX oslash eacute -55 -KPX oslash ecaron -55 -KPX oslash ecircumflex -55 -KPX oslash edieresis -55 -KPX oslash edotaccent -55 -KPX oslash egrave -55 -KPX oslash emacron -55 -KPX oslash eogonek -55 -KPX oslash f -55 -KPX oslash g -55 -KPX oslash gbreve -55 -KPX oslash gcommaaccent -55 -KPX oslash h -55 -KPX oslash i -55 -KPX oslash iacute -55 -KPX oslash icircumflex -55 -KPX oslash idieresis -55 -KPX oslash igrave -55 -KPX oslash imacron -55 -KPX oslash iogonek -55 -KPX oslash j -55 -KPX oslash k -55 -KPX oslash kcommaaccent -55 -KPX oslash l -55 -KPX oslash lacute -55 -KPX oslash lcommaaccent -55 -KPX oslash lslash -55 -KPX oslash m -55 -KPX oslash n -55 -KPX oslash nacute -55 -KPX oslash ncaron -55 -KPX oslash ncommaaccent -55 -KPX oslash ntilde -55 -KPX oslash o -55 -KPX oslash oacute -55 -KPX oslash ocircumflex -55 -KPX oslash odieresis -55 -KPX oslash ograve -55 -KPX oslash ohungarumlaut -55 -KPX oslash omacron -55 -KPX oslash oslash -55 -KPX oslash otilde -55 -KPX oslash p -55 -KPX oslash period -95 -KPX oslash q -55 -KPX oslash r -55 -KPX oslash racute -55 -KPX oslash rcaron -55 -KPX oslash rcommaaccent -55 -KPX oslash s -55 -KPX oslash sacute -55 -KPX oslash scaron -55 -KPX oslash scedilla -55 -KPX oslash scommaaccent -55 -KPX oslash t -55 -KPX oslash tcommaaccent -55 -KPX oslash u -55 -KPX oslash uacute -55 -KPX oslash ucircumflex -55 -KPX oslash udieresis -55 -KPX oslash ugrave -55 -KPX oslash uhungarumlaut -55 -KPX oslash umacron -55 -KPX oslash uogonek -55 -KPX oslash uring -55 -KPX oslash v -70 -KPX oslash w -70 -KPX oslash x -85 -KPX oslash y -70 -KPX oslash yacute -70 -KPX oslash ydieresis -70 -KPX oslash z -55 -KPX oslash zacute -55 -KPX oslash zcaron -55 -KPX oslash zdotaccent -55 -KPX otilde comma -40 -KPX otilde period -40 -KPX otilde v -15 -KPX otilde w -15 -KPX otilde x -30 -KPX otilde y -30 -KPX otilde yacute -30 -KPX otilde ydieresis -30 -KPX p comma -35 -KPX p period -35 -KPX p y -30 -KPX p yacute -30 -KPX p ydieresis -30 -KPX period quotedblright -100 -KPX period quoteright -100 -KPX period space -60 -KPX quotedblright space -40 -KPX quoteleft quoteleft -57 -KPX quoteright d -50 -KPX quoteright dcroat -50 -KPX quoteright quoteright -57 -KPX quoteright r -50 -KPX quoteright racute -50 -KPX quoteright rcaron -50 -KPX quoteright rcommaaccent -50 -KPX quoteright s -50 -KPX quoteright sacute -50 -KPX quoteright scaron -50 -KPX quoteright scedilla -50 -KPX quoteright scommaaccent -50 -KPX quoteright space -70 -KPX r a -10 -KPX r aacute -10 -KPX r abreve -10 -KPX r acircumflex -10 -KPX r adieresis -10 -KPX r agrave -10 -KPX r amacron -10 -KPX r aogonek -10 -KPX r aring -10 -KPX r atilde -10 -KPX r colon 30 -KPX r comma -50 -KPX r i 15 -KPX r iacute 15 -KPX r icircumflex 15 -KPX r idieresis 15 -KPX r igrave 15 -KPX r imacron 15 -KPX r iogonek 15 -KPX r k 15 -KPX r kcommaaccent 15 -KPX r l 15 -KPX r lacute 15 -KPX r lcommaaccent 15 -KPX r lslash 15 -KPX r m 25 -KPX r n 25 -KPX r nacute 25 -KPX r ncaron 25 -KPX r ncommaaccent 25 -KPX r ntilde 25 -KPX r p 30 -KPX r period -50 -KPX r semicolon 30 -KPX r t 40 -KPX r tcommaaccent 40 -KPX r u 15 -KPX r uacute 15 -KPX r ucircumflex 15 -KPX r udieresis 15 -KPX r ugrave 15 -KPX r uhungarumlaut 15 -KPX r umacron 15 -KPX r uogonek 15 -KPX r uring 15 -KPX r v 30 -KPX r y 30 -KPX r yacute 30 -KPX r ydieresis 30 -KPX racute a -10 -KPX racute aacute -10 -KPX racute abreve -10 -KPX racute acircumflex -10 -KPX racute adieresis -10 -KPX racute agrave -10 -KPX racute amacron -10 -KPX racute aogonek -10 -KPX racute aring -10 -KPX racute atilde -10 -KPX racute colon 30 -KPX racute comma -50 -KPX racute i 15 -KPX racute iacute 15 -KPX racute icircumflex 15 -KPX racute idieresis 15 -KPX racute igrave 15 -KPX racute imacron 15 -KPX racute iogonek 15 -KPX racute k 15 -KPX racute kcommaaccent 15 -KPX racute l 15 -KPX racute lacute 15 -KPX racute lcommaaccent 15 -KPX racute lslash 15 -KPX racute m 25 -KPX racute n 25 -KPX racute nacute 25 -KPX racute ncaron 25 -KPX racute ncommaaccent 25 -KPX racute ntilde 25 -KPX racute p 30 -KPX racute period -50 -KPX racute semicolon 30 -KPX racute t 40 -KPX racute tcommaaccent 40 -KPX racute u 15 -KPX racute uacute 15 -KPX racute ucircumflex 15 -KPX racute udieresis 15 -KPX racute ugrave 15 -KPX racute uhungarumlaut 15 -KPX racute umacron 15 -KPX racute uogonek 15 -KPX racute uring 15 -KPX racute v 30 -KPX racute y 30 -KPX racute yacute 30 -KPX racute ydieresis 30 -KPX rcaron a -10 -KPX rcaron aacute -10 -KPX rcaron abreve -10 -KPX rcaron acircumflex -10 -KPX rcaron adieresis -10 -KPX rcaron agrave -10 -KPX rcaron amacron -10 -KPX rcaron aogonek -10 -KPX rcaron aring -10 -KPX rcaron atilde -10 -KPX rcaron colon 30 -KPX rcaron comma -50 -KPX rcaron i 15 -KPX rcaron iacute 15 -KPX rcaron icircumflex 15 -KPX rcaron idieresis 15 -KPX rcaron igrave 15 -KPX rcaron imacron 15 -KPX rcaron iogonek 15 -KPX rcaron k 15 -KPX rcaron kcommaaccent 15 -KPX rcaron l 15 -KPX rcaron lacute 15 -KPX rcaron lcommaaccent 15 -KPX rcaron lslash 15 -KPX rcaron m 25 -KPX rcaron n 25 -KPX rcaron nacute 25 -KPX rcaron ncaron 25 -KPX rcaron ncommaaccent 25 -KPX rcaron ntilde 25 -KPX rcaron p 30 -KPX rcaron period -50 -KPX rcaron semicolon 30 -KPX rcaron t 40 -KPX rcaron tcommaaccent 40 -KPX rcaron u 15 -KPX rcaron uacute 15 -KPX rcaron ucircumflex 15 -KPX rcaron udieresis 15 -KPX rcaron ugrave 15 -KPX rcaron uhungarumlaut 15 -KPX rcaron umacron 15 -KPX rcaron uogonek 15 -KPX rcaron uring 15 -KPX rcaron v 30 -KPX rcaron y 30 -KPX rcaron yacute 30 -KPX rcaron ydieresis 30 -KPX rcommaaccent a -10 -KPX rcommaaccent aacute -10 -KPX rcommaaccent abreve -10 -KPX rcommaaccent acircumflex -10 -KPX rcommaaccent adieresis -10 -KPX rcommaaccent agrave -10 -KPX rcommaaccent amacron -10 -KPX rcommaaccent aogonek -10 -KPX rcommaaccent aring -10 -KPX rcommaaccent atilde -10 -KPX rcommaaccent colon 30 -KPX rcommaaccent comma -50 -KPX rcommaaccent i 15 -KPX rcommaaccent iacute 15 -KPX rcommaaccent icircumflex 15 -KPX rcommaaccent idieresis 15 -KPX rcommaaccent igrave 15 -KPX rcommaaccent imacron 15 -KPX rcommaaccent iogonek 15 -KPX rcommaaccent k 15 -KPX rcommaaccent kcommaaccent 15 -KPX rcommaaccent l 15 -KPX rcommaaccent lacute 15 -KPX rcommaaccent lcommaaccent 15 -KPX rcommaaccent lslash 15 -KPX rcommaaccent m 25 -KPX rcommaaccent n 25 -KPX rcommaaccent nacute 25 -KPX rcommaaccent ncaron 25 -KPX rcommaaccent ncommaaccent 25 -KPX rcommaaccent ntilde 25 -KPX rcommaaccent p 30 -KPX rcommaaccent period -50 -KPX rcommaaccent semicolon 30 -KPX rcommaaccent t 40 -KPX rcommaaccent tcommaaccent 40 -KPX rcommaaccent u 15 -KPX rcommaaccent uacute 15 -KPX rcommaaccent ucircumflex 15 -KPX rcommaaccent udieresis 15 -KPX rcommaaccent ugrave 15 -KPX rcommaaccent uhungarumlaut 15 -KPX rcommaaccent umacron 15 -KPX rcommaaccent uogonek 15 -KPX rcommaaccent uring 15 -KPX rcommaaccent v 30 -KPX rcommaaccent y 30 -KPX rcommaaccent yacute 30 -KPX rcommaaccent ydieresis 30 -KPX s comma -15 -KPX s period -15 -KPX s w -30 -KPX sacute comma -15 -KPX sacute period -15 -KPX sacute w -30 -KPX scaron comma -15 -KPX scaron period -15 -KPX scaron w -30 -KPX scedilla comma -15 -KPX scedilla period -15 -KPX scedilla w -30 -KPX scommaaccent comma -15 -KPX scommaaccent period -15 -KPX scommaaccent w -30 -KPX semicolon space -50 -KPX space T -50 -KPX space Tcaron -50 -KPX space Tcommaaccent -50 -KPX space V -50 -KPX space W -40 -KPX space Y -90 -KPX space Yacute -90 -KPX space Ydieresis -90 -KPX space quotedblleft -30 -KPX space quoteleft -60 -KPX v a -25 -KPX v aacute -25 -KPX v abreve -25 -KPX v acircumflex -25 -KPX v adieresis -25 -KPX v agrave -25 -KPX v amacron -25 -KPX v aogonek -25 -KPX v aring -25 -KPX v atilde -25 -KPX v comma -80 -KPX v e -25 -KPX v eacute -25 -KPX v ecaron -25 -KPX v ecircumflex -25 -KPX v edieresis -25 -KPX v edotaccent -25 -KPX v egrave -25 -KPX v emacron -25 -KPX v eogonek -25 -KPX v o -25 -KPX v oacute -25 -KPX v ocircumflex -25 -KPX v odieresis -25 -KPX v ograve -25 -KPX v ohungarumlaut -25 -KPX v omacron -25 -KPX v oslash -25 -KPX v otilde -25 -KPX v period -80 -KPX w a -15 -KPX w aacute -15 -KPX w abreve -15 -KPX w acircumflex -15 -KPX w adieresis -15 -KPX w agrave -15 -KPX w amacron -15 -KPX w aogonek -15 -KPX w aring -15 -KPX w atilde -15 -KPX w comma -60 -KPX w e -10 -KPX w eacute -10 -KPX w ecaron -10 -KPX w ecircumflex -10 -KPX w edieresis -10 -KPX w edotaccent -10 -KPX w egrave -10 -KPX w emacron -10 -KPX w eogonek -10 -KPX w o -10 -KPX w oacute -10 -KPX w ocircumflex -10 -KPX w odieresis -10 -KPX w ograve -10 -KPX w ohungarumlaut -10 -KPX w omacron -10 -KPX w oslash -10 -KPX w otilde -10 -KPX w period -60 -KPX x e -30 -KPX x eacute -30 -KPX x ecaron -30 -KPX x ecircumflex -30 -KPX x edieresis -30 -KPX x edotaccent -30 -KPX x egrave -30 -KPX x emacron -30 -KPX x eogonek -30 -KPX y a -20 -KPX y aacute -20 -KPX y abreve -20 -KPX y acircumflex -20 -KPX y adieresis -20 -KPX y agrave -20 -KPX y amacron -20 -KPX y aogonek -20 -KPX y aring -20 -KPX y atilde -20 -KPX y comma -100 -KPX y e -20 -KPX y eacute -20 -KPX y ecaron -20 -KPX y ecircumflex -20 -KPX y edieresis -20 -KPX y edotaccent -20 -KPX y egrave -20 -KPX y emacron -20 -KPX y eogonek -20 -KPX y o -20 -KPX y oacute -20 -KPX y ocircumflex -20 -KPX y odieresis -20 -KPX y ograve -20 -KPX y ohungarumlaut -20 -KPX y omacron -20 -KPX y oslash -20 -KPX y otilde -20 -KPX y period -100 -KPX yacute a -20 -KPX yacute aacute -20 -KPX yacute abreve -20 -KPX yacute acircumflex -20 -KPX yacute adieresis -20 -KPX yacute agrave -20 -KPX yacute amacron -20 -KPX yacute aogonek -20 -KPX yacute aring -20 -KPX yacute atilde -20 -KPX yacute comma -100 -KPX yacute e -20 -KPX yacute eacute -20 -KPX yacute ecaron -20 -KPX yacute ecircumflex -20 -KPX yacute edieresis -20 -KPX yacute edotaccent -20 -KPX yacute egrave -20 -KPX yacute emacron -20 -KPX yacute eogonek -20 -KPX yacute o -20 -KPX yacute oacute -20 -KPX yacute ocircumflex -20 -KPX yacute odieresis -20 -KPX yacute ograve -20 -KPX yacute ohungarumlaut -20 -KPX yacute omacron -20 -KPX yacute oslash -20 -KPX yacute otilde -20 -KPX yacute period -100 -KPX ydieresis a -20 -KPX ydieresis aacute -20 -KPX ydieresis abreve -20 -KPX ydieresis acircumflex -20 -KPX ydieresis adieresis -20 -KPX ydieresis agrave -20 -KPX ydieresis amacron -20 -KPX ydieresis aogonek -20 -KPX ydieresis aring -20 -KPX ydieresis atilde -20 -KPX ydieresis comma -100 -KPX ydieresis e -20 -KPX ydieresis eacute -20 -KPX ydieresis ecaron -20 -KPX ydieresis ecircumflex -20 -KPX ydieresis edieresis -20 -KPX ydieresis edotaccent -20 -KPX ydieresis egrave -20 -KPX ydieresis emacron -20 -KPX ydieresis eogonek -20 -KPX ydieresis o -20 -KPX ydieresis oacute -20 -KPX ydieresis ocircumflex -20 -KPX ydieresis odieresis -20 -KPX ydieresis ograve -20 -KPX ydieresis ohungarumlaut -20 -KPX ydieresis omacron -20 -KPX ydieresis oslash -20 -KPX ydieresis otilde -20 -KPX ydieresis period -100 -KPX z e -15 -KPX z eacute -15 -KPX z ecaron -15 -KPX z ecircumflex -15 -KPX z edieresis -15 -KPX z edotaccent -15 -KPX z egrave -15 -KPX z emacron -15 -KPX z eogonek -15 -KPX z o -15 -KPX z oacute -15 -KPX z ocircumflex -15 -KPX z odieresis -15 -KPX z ograve -15 -KPX z ohungarumlaut -15 -KPX z omacron -15 -KPX z oslash -15 -KPX z otilde -15 -KPX zacute e -15 -KPX zacute eacute -15 -KPX zacute ecaron -15 -KPX zacute ecircumflex -15 -KPX zacute edieresis -15 -KPX zacute edotaccent -15 -KPX zacute egrave -15 -KPX zacute emacron -15 -KPX zacute eogonek -15 -KPX zacute o -15 -KPX zacute oacute -15 -KPX zacute ocircumflex -15 -KPX zacute odieresis -15 -KPX zacute ograve -15 -KPX zacute ohungarumlaut -15 -KPX zacute omacron -15 -KPX zacute oslash -15 -KPX zacute otilde -15 -KPX zcaron e -15 -KPX zcaron eacute -15 -KPX zcaron ecaron -15 -KPX zcaron ecircumflex -15 -KPX zcaron edieresis -15 -KPX zcaron edotaccent -15 -KPX zcaron egrave -15 -KPX zcaron emacron -15 -KPX zcaron eogonek -15 -KPX zcaron o -15 -KPX zcaron oacute -15 -KPX zcaron ocircumflex -15 -KPX zcaron odieresis -15 -KPX zcaron ograve -15 -KPX zcaron ohungarumlaut -15 -KPX zcaron omacron -15 -KPX zcaron oslash -15 -KPX zcaron otilde -15 -KPX zdotaccent e -15 -KPX zdotaccent eacute -15 -KPX zdotaccent ecaron -15 -KPX zdotaccent ecircumflex -15 -KPX zdotaccent edieresis -15 -KPX zdotaccent edotaccent -15 -KPX zdotaccent egrave -15 -KPX zdotaccent emacron -15 -KPX zdotaccent eogonek -15 -KPX zdotaccent o -15 -KPX zdotaccent oacute -15 -KPX zdotaccent ocircumflex -15 -KPX zdotaccent odieresis -15 -KPX zdotaccent ograve -15 -KPX zdotaccent ohungarumlaut -15 -KPX zdotaccent omacron -15 -KPX zdotaccent oslash -15 -KPX zdotaccent otilde -15 -EndKernPairs -EndKernData -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Helvetica.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Helvetica.afm deleted file mode 100644 index bd32af5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Helvetica.afm +++ /dev/null @@ -1,3051 +0,0 @@ -StartFontMetrics 4.1 -Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Thu May 1 12:38:23 1997 -Comment UniqueID 43054 -Comment VMusage 37069 48094 -FontName Helvetica -FullName Helvetica -FamilyName Helvetica -Weight Medium -ItalicAngle 0 -IsFixedPitch false -CharacterSet ExtendedRoman -FontBBox -166 -225 1000 931 -UnderlinePosition -100 -UnderlineThickness 50 -Version 002.000 -Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved.Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 718 -XHeight 523 -Ascender 718 -Descender -207 -StdHW 76 -StdVW 88 -StartCharMetrics 315 -C 32 ; WX 278 ; N space ; B 0 0 0 0 ; -C 33 ; WX 278 ; N exclam ; B 90 0 187 718 ; -C 34 ; WX 355 ; N quotedbl ; B 70 463 285 718 ; -C 35 ; WX 556 ; N numbersign ; B 28 0 529 688 ; -C 36 ; WX 556 ; N dollar ; B 32 -115 520 775 ; -C 37 ; WX 889 ; N percent ; B 39 -19 850 703 ; -C 38 ; WX 667 ; N ampersand ; B 44 -15 645 718 ; -C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ; -C 40 ; WX 333 ; N parenleft ; B 68 -207 299 733 ; -C 41 ; WX 333 ; N parenright ; B 34 -207 265 733 ; -C 42 ; WX 389 ; N asterisk ; B 39 431 349 718 ; -C 43 ; WX 584 ; N plus ; B 39 0 545 505 ; -C 44 ; WX 278 ; N comma ; B 87 -147 191 106 ; -C 45 ; WX 333 ; N hyphen ; B 44 232 289 322 ; -C 46 ; WX 278 ; N period ; B 87 0 191 106 ; -C 47 ; WX 278 ; N slash ; B -17 -19 295 737 ; -C 48 ; WX 556 ; N zero ; B 37 -19 519 703 ; -C 49 ; WX 556 ; N one ; B 101 0 359 703 ; -C 50 ; WX 556 ; N two ; B 26 0 507 703 ; -C 51 ; WX 556 ; N three ; B 34 -19 522 703 ; -C 52 ; WX 556 ; N four ; B 25 0 523 703 ; -C 53 ; WX 556 ; N five ; B 32 -19 514 688 ; -C 54 ; WX 556 ; N six ; B 38 -19 518 703 ; -C 55 ; WX 556 ; N seven ; B 37 0 523 688 ; -C 56 ; WX 556 ; N eight ; B 38 -19 517 703 ; -C 57 ; WX 556 ; N nine ; B 42 -19 514 703 ; -C 58 ; WX 278 ; N colon ; B 87 0 191 516 ; -C 59 ; WX 278 ; N semicolon ; B 87 -147 191 516 ; -C 60 ; WX 584 ; N less ; B 48 11 536 495 ; -C 61 ; WX 584 ; N equal ; B 39 115 545 390 ; -C 62 ; WX 584 ; N greater ; B 48 11 536 495 ; -C 63 ; WX 556 ; N question ; B 56 0 492 727 ; -C 64 ; WX 1015 ; N at ; B 147 -19 868 737 ; -C 65 ; WX 667 ; N A ; B 14 0 654 718 ; -C 66 ; WX 667 ; N B ; B 74 0 627 718 ; -C 67 ; WX 722 ; N C ; B 44 -19 681 737 ; -C 68 ; WX 722 ; N D ; B 81 0 674 718 ; -C 69 ; WX 667 ; N E ; B 86 0 616 718 ; -C 70 ; WX 611 ; N F ; B 86 0 583 718 ; -C 71 ; WX 778 ; N G ; B 48 -19 704 737 ; -C 72 ; WX 722 ; N H ; B 77 0 646 718 ; -C 73 ; WX 278 ; N I ; B 91 0 188 718 ; -C 74 ; WX 500 ; N J ; B 17 -19 428 718 ; -C 75 ; WX 667 ; N K ; B 76 0 663 718 ; -C 76 ; WX 556 ; N L ; B 76 0 537 718 ; -C 77 ; WX 833 ; N M ; B 73 0 761 718 ; -C 78 ; WX 722 ; N N ; B 76 0 646 718 ; -C 79 ; WX 778 ; N O ; B 39 -19 739 737 ; -C 80 ; WX 667 ; N P ; B 86 0 622 718 ; -C 81 ; WX 778 ; N Q ; B 39 -56 739 737 ; -C 82 ; WX 722 ; N R ; B 88 0 684 718 ; -C 83 ; WX 667 ; N S ; B 49 -19 620 737 ; -C 84 ; WX 611 ; N T ; B 14 0 597 718 ; -C 85 ; WX 722 ; N U ; B 79 -19 644 718 ; -C 86 ; WX 667 ; N V ; B 20 0 647 718 ; -C 87 ; WX 944 ; N W ; B 16 0 928 718 ; -C 88 ; WX 667 ; N X ; B 19 0 648 718 ; -C 89 ; WX 667 ; N Y ; B 14 0 653 718 ; -C 90 ; WX 611 ; N Z ; B 23 0 588 718 ; -C 91 ; WX 278 ; N bracketleft ; B 63 -196 250 722 ; -C 92 ; WX 278 ; N backslash ; B -17 -19 295 737 ; -C 93 ; WX 278 ; N bracketright ; B 28 -196 215 722 ; -C 94 ; WX 469 ; N asciicircum ; B -14 264 483 688 ; -C 95 ; WX 556 ; N underscore ; B 0 -125 556 -75 ; -C 96 ; WX 222 ; N quoteleft ; B 65 470 169 725 ; -C 97 ; WX 556 ; N a ; B 36 -15 530 538 ; -C 98 ; WX 556 ; N b ; B 58 -15 517 718 ; -C 99 ; WX 500 ; N c ; B 30 -15 477 538 ; -C 100 ; WX 556 ; N d ; B 35 -15 499 718 ; -C 101 ; WX 556 ; N e ; B 40 -15 516 538 ; -C 102 ; WX 278 ; N f ; B 14 0 262 728 ; L i fi ; L l fl ; -C 103 ; WX 556 ; N g ; B 40 -220 499 538 ; -C 104 ; WX 556 ; N h ; B 65 0 491 718 ; -C 105 ; WX 222 ; N i ; B 67 0 155 718 ; -C 106 ; WX 222 ; N j ; B -16 -210 155 718 ; -C 107 ; WX 500 ; N k ; B 67 0 501 718 ; -C 108 ; WX 222 ; N l ; B 67 0 155 718 ; -C 109 ; WX 833 ; N m ; B 65 0 769 538 ; -C 110 ; WX 556 ; N n ; B 65 0 491 538 ; -C 111 ; WX 556 ; N o ; B 35 -14 521 538 ; -C 112 ; WX 556 ; N p ; B 58 -207 517 538 ; -C 113 ; WX 556 ; N q ; B 35 -207 494 538 ; -C 114 ; WX 333 ; N r ; B 77 0 332 538 ; -C 115 ; WX 500 ; N s ; B 32 -15 464 538 ; -C 116 ; WX 278 ; N t ; B 14 -7 257 669 ; -C 117 ; WX 556 ; N u ; B 68 -15 489 523 ; -C 118 ; WX 500 ; N v ; B 8 0 492 523 ; -C 119 ; WX 722 ; N w ; B 14 0 709 523 ; -C 120 ; WX 500 ; N x ; B 11 0 490 523 ; -C 121 ; WX 500 ; N y ; B 11 -214 489 523 ; -C 122 ; WX 500 ; N z ; B 31 0 469 523 ; -C 123 ; WX 334 ; N braceleft ; B 42 -196 292 722 ; -C 124 ; WX 260 ; N bar ; B 94 -225 167 775 ; -C 125 ; WX 334 ; N braceright ; B 42 -196 292 722 ; -C 126 ; WX 584 ; N asciitilde ; B 61 180 523 326 ; -C 161 ; WX 333 ; N exclamdown ; B 118 -195 215 523 ; -C 162 ; WX 556 ; N cent ; B 51 -115 513 623 ; -C 163 ; WX 556 ; N sterling ; B 33 -16 539 718 ; -C 164 ; WX 167 ; N fraction ; B -166 -19 333 703 ; -C 165 ; WX 556 ; N yen ; B 3 0 553 688 ; -C 166 ; WX 556 ; N florin ; B -11 -207 501 737 ; -C 167 ; WX 556 ; N section ; B 43 -191 512 737 ; -C 168 ; WX 556 ; N currency ; B 28 99 528 603 ; -C 169 ; WX 191 ; N quotesingle ; B 59 463 132 718 ; -C 170 ; WX 333 ; N quotedblleft ; B 38 470 307 725 ; -C 171 ; WX 556 ; N guillemotleft ; B 97 108 459 446 ; -C 172 ; WX 333 ; N guilsinglleft ; B 88 108 245 446 ; -C 173 ; WX 333 ; N guilsinglright ; B 88 108 245 446 ; -C 174 ; WX 500 ; N fi ; B 14 0 434 728 ; -C 175 ; WX 500 ; N fl ; B 14 0 432 728 ; -C 177 ; WX 556 ; N endash ; B 0 240 556 313 ; -C 178 ; WX 556 ; N dagger ; B 43 -159 514 718 ; -C 179 ; WX 556 ; N daggerdbl ; B 43 -159 514 718 ; -C 180 ; WX 278 ; N periodcentered ; B 77 190 202 315 ; -C 182 ; WX 537 ; N paragraph ; B 18 -173 497 718 ; -C 183 ; WX 350 ; N bullet ; B 18 202 333 517 ; -C 184 ; WX 222 ; N quotesinglbase ; B 53 -149 157 106 ; -C 185 ; WX 333 ; N quotedblbase ; B 26 -149 295 106 ; -C 186 ; WX 333 ; N quotedblright ; B 26 463 295 718 ; -C 187 ; WX 556 ; N guillemotright ; B 97 108 459 446 ; -C 188 ; WX 1000 ; N ellipsis ; B 115 0 885 106 ; -C 189 ; WX 1000 ; N perthousand ; B 7 -19 994 703 ; -C 191 ; WX 611 ; N questiondown ; B 91 -201 527 525 ; -C 193 ; WX 333 ; N grave ; B 14 593 211 734 ; -C 194 ; WX 333 ; N acute ; B 122 593 319 734 ; -C 195 ; WX 333 ; N circumflex ; B 21 593 312 734 ; -C 196 ; WX 333 ; N tilde ; B -4 606 337 722 ; -C 197 ; WX 333 ; N macron ; B 10 627 323 684 ; -C 198 ; WX 333 ; N breve ; B 13 595 321 731 ; -C 199 ; WX 333 ; N dotaccent ; B 121 604 212 706 ; -C 200 ; WX 333 ; N dieresis ; B 40 604 293 706 ; -C 202 ; WX 333 ; N ring ; B 75 572 259 756 ; -C 203 ; WX 333 ; N cedilla ; B 45 -225 259 0 ; -C 205 ; WX 333 ; N hungarumlaut ; B 31 593 409 734 ; -C 206 ; WX 333 ; N ogonek ; B 73 -225 287 0 ; -C 207 ; WX 333 ; N caron ; B 21 593 312 734 ; -C 208 ; WX 1000 ; N emdash ; B 0 240 1000 313 ; -C 225 ; WX 1000 ; N AE ; B 8 0 951 718 ; -C 227 ; WX 370 ; N ordfeminine ; B 24 405 346 737 ; -C 232 ; WX 556 ; N Lslash ; B -20 0 537 718 ; -C 233 ; WX 778 ; N Oslash ; B 39 -19 740 737 ; -C 234 ; WX 1000 ; N OE ; B 36 -19 965 737 ; -C 235 ; WX 365 ; N ordmasculine ; B 25 405 341 737 ; -C 241 ; WX 889 ; N ae ; B 36 -15 847 538 ; -C 245 ; WX 278 ; N dotlessi ; B 95 0 183 523 ; -C 248 ; WX 222 ; N lslash ; B -20 0 242 718 ; -C 249 ; WX 611 ; N oslash ; B 28 -22 537 545 ; -C 250 ; WX 944 ; N oe ; B 35 -15 902 538 ; -C 251 ; WX 611 ; N germandbls ; B 67 -15 571 728 ; -C -1 ; WX 278 ; N Idieresis ; B 13 0 266 901 ; -C -1 ; WX 556 ; N eacute ; B 40 -15 516 734 ; -C -1 ; WX 556 ; N abreve ; B 36 -15 530 731 ; -C -1 ; WX 556 ; N uhungarumlaut ; B 68 -15 521 734 ; -C -1 ; WX 556 ; N ecaron ; B 40 -15 516 734 ; -C -1 ; WX 667 ; N Ydieresis ; B 14 0 653 901 ; -C -1 ; WX 584 ; N divide ; B 39 -19 545 524 ; -C -1 ; WX 667 ; N Yacute ; B 14 0 653 929 ; -C -1 ; WX 667 ; N Acircumflex ; B 14 0 654 929 ; -C -1 ; WX 556 ; N aacute ; B 36 -15 530 734 ; -C -1 ; WX 722 ; N Ucircumflex ; B 79 -19 644 929 ; -C -1 ; WX 500 ; N yacute ; B 11 -214 489 734 ; -C -1 ; WX 500 ; N scommaaccent ; B 32 -225 464 538 ; -C -1 ; WX 556 ; N ecircumflex ; B 40 -15 516 734 ; -C -1 ; WX 722 ; N Uring ; B 79 -19 644 931 ; -C -1 ; WX 722 ; N Udieresis ; B 79 -19 644 901 ; -C -1 ; WX 556 ; N aogonek ; B 36 -220 547 538 ; -C -1 ; WX 722 ; N Uacute ; B 79 -19 644 929 ; -C -1 ; WX 556 ; N uogonek ; B 68 -225 519 523 ; -C -1 ; WX 667 ; N Edieresis ; B 86 0 616 901 ; -C -1 ; WX 722 ; N Dcroat ; B 0 0 674 718 ; -C -1 ; WX 250 ; N commaaccent ; B 87 -225 181 -40 ; -C -1 ; WX 737 ; N copyright ; B -14 -19 752 737 ; -C -1 ; WX 667 ; N Emacron ; B 86 0 616 879 ; -C -1 ; WX 500 ; N ccaron ; B 30 -15 477 734 ; -C -1 ; WX 556 ; N aring ; B 36 -15 530 756 ; -C -1 ; WX 722 ; N Ncommaaccent ; B 76 -225 646 718 ; -C -1 ; WX 222 ; N lacute ; B 67 0 264 929 ; -C -1 ; WX 556 ; N agrave ; B 36 -15 530 734 ; -C -1 ; WX 611 ; N Tcommaaccent ; B 14 -225 597 718 ; -C -1 ; WX 722 ; N Cacute ; B 44 -19 681 929 ; -C -1 ; WX 556 ; N atilde ; B 36 -15 530 722 ; -C -1 ; WX 667 ; N Edotaccent ; B 86 0 616 901 ; -C -1 ; WX 500 ; N scaron ; B 32 -15 464 734 ; -C -1 ; WX 500 ; N scedilla ; B 32 -225 464 538 ; -C -1 ; WX 278 ; N iacute ; B 95 0 292 734 ; -C -1 ; WX 471 ; N lozenge ; B 10 0 462 728 ; -C -1 ; WX 722 ; N Rcaron ; B 88 0 684 929 ; -C -1 ; WX 778 ; N Gcommaaccent ; B 48 -225 704 737 ; -C -1 ; WX 556 ; N ucircumflex ; B 68 -15 489 734 ; -C -1 ; WX 556 ; N acircumflex ; B 36 -15 530 734 ; -C -1 ; WX 667 ; N Amacron ; B 14 0 654 879 ; -C -1 ; WX 333 ; N rcaron ; B 61 0 352 734 ; -C -1 ; WX 500 ; N ccedilla ; B 30 -225 477 538 ; -C -1 ; WX 611 ; N Zdotaccent ; B 23 0 588 901 ; -C -1 ; WX 667 ; N Thorn ; B 86 0 622 718 ; -C -1 ; WX 778 ; N Omacron ; B 39 -19 739 879 ; -C -1 ; WX 722 ; N Racute ; B 88 0 684 929 ; -C -1 ; WX 667 ; N Sacute ; B 49 -19 620 929 ; -C -1 ; WX 643 ; N dcaron ; B 35 -15 655 718 ; -C -1 ; WX 722 ; N Umacron ; B 79 -19 644 879 ; -C -1 ; WX 556 ; N uring ; B 68 -15 489 756 ; -C -1 ; WX 333 ; N threesuperior ; B 5 270 325 703 ; -C -1 ; WX 778 ; N Ograve ; B 39 -19 739 929 ; -C -1 ; WX 667 ; N Agrave ; B 14 0 654 929 ; -C -1 ; WX 667 ; N Abreve ; B 14 0 654 926 ; -C -1 ; WX 584 ; N multiply ; B 39 0 545 506 ; -C -1 ; WX 556 ; N uacute ; B 68 -15 489 734 ; -C -1 ; WX 611 ; N Tcaron ; B 14 0 597 929 ; -C -1 ; WX 476 ; N partialdiff ; B 13 -38 463 714 ; -C -1 ; WX 500 ; N ydieresis ; B 11 -214 489 706 ; -C -1 ; WX 722 ; N Nacute ; B 76 0 646 929 ; -C -1 ; WX 278 ; N icircumflex ; B -6 0 285 734 ; -C -1 ; WX 667 ; N Ecircumflex ; B 86 0 616 929 ; -C -1 ; WX 556 ; N adieresis ; B 36 -15 530 706 ; -C -1 ; WX 556 ; N edieresis ; B 40 -15 516 706 ; -C -1 ; WX 500 ; N cacute ; B 30 -15 477 734 ; -C -1 ; WX 556 ; N nacute ; B 65 0 491 734 ; -C -1 ; WX 556 ; N umacron ; B 68 -15 489 684 ; -C -1 ; WX 722 ; N Ncaron ; B 76 0 646 929 ; -C -1 ; WX 278 ; N Iacute ; B 91 0 292 929 ; -C -1 ; WX 584 ; N plusminus ; B 39 0 545 506 ; -C -1 ; WX 260 ; N brokenbar ; B 94 -150 167 700 ; -C -1 ; WX 737 ; N registered ; B -14 -19 752 737 ; -C -1 ; WX 778 ; N Gbreve ; B 48 -19 704 926 ; -C -1 ; WX 278 ; N Idotaccent ; B 91 0 188 901 ; -C -1 ; WX 600 ; N summation ; B 15 -10 586 706 ; -C -1 ; WX 667 ; N Egrave ; B 86 0 616 929 ; -C -1 ; WX 333 ; N racute ; B 77 0 332 734 ; -C -1 ; WX 556 ; N omacron ; B 35 -14 521 684 ; -C -1 ; WX 611 ; N Zacute ; B 23 0 588 929 ; -C -1 ; WX 611 ; N Zcaron ; B 23 0 588 929 ; -C -1 ; WX 549 ; N greaterequal ; B 26 0 523 674 ; -C -1 ; WX 722 ; N Eth ; B 0 0 674 718 ; -C -1 ; WX 722 ; N Ccedilla ; B 44 -225 681 737 ; -C -1 ; WX 222 ; N lcommaaccent ; B 67 -225 167 718 ; -C -1 ; WX 317 ; N tcaron ; B 14 -7 329 808 ; -C -1 ; WX 556 ; N eogonek ; B 40 -225 516 538 ; -C -1 ; WX 722 ; N Uogonek ; B 79 -225 644 718 ; -C -1 ; WX 667 ; N Aacute ; B 14 0 654 929 ; -C -1 ; WX 667 ; N Adieresis ; B 14 0 654 901 ; -C -1 ; WX 556 ; N egrave ; B 40 -15 516 734 ; -C -1 ; WX 500 ; N zacute ; B 31 0 469 734 ; -C -1 ; WX 222 ; N iogonek ; B -31 -225 183 718 ; -C -1 ; WX 778 ; N Oacute ; B 39 -19 739 929 ; -C -1 ; WX 556 ; N oacute ; B 35 -14 521 734 ; -C -1 ; WX 556 ; N amacron ; B 36 -15 530 684 ; -C -1 ; WX 500 ; N sacute ; B 32 -15 464 734 ; -C -1 ; WX 278 ; N idieresis ; B 13 0 266 706 ; -C -1 ; WX 778 ; N Ocircumflex ; B 39 -19 739 929 ; -C -1 ; WX 722 ; N Ugrave ; B 79 -19 644 929 ; -C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ; -C -1 ; WX 556 ; N thorn ; B 58 -207 517 718 ; -C -1 ; WX 333 ; N twosuperior ; B 4 281 323 703 ; -C -1 ; WX 778 ; N Odieresis ; B 39 -19 739 901 ; -C -1 ; WX 556 ; N mu ; B 68 -207 489 523 ; -C -1 ; WX 278 ; N igrave ; B -13 0 184 734 ; -C -1 ; WX 556 ; N ohungarumlaut ; B 35 -14 521 734 ; -C -1 ; WX 667 ; N Eogonek ; B 86 -220 633 718 ; -C -1 ; WX 556 ; N dcroat ; B 35 -15 550 718 ; -C -1 ; WX 834 ; N threequarters ; B 45 -19 810 703 ; -C -1 ; WX 667 ; N Scedilla ; B 49 -225 620 737 ; -C -1 ; WX 299 ; N lcaron ; B 67 0 311 718 ; -C -1 ; WX 667 ; N Kcommaaccent ; B 76 -225 663 718 ; -C -1 ; WX 556 ; N Lacute ; B 76 0 537 929 ; -C -1 ; WX 1000 ; N trademark ; B 46 306 903 718 ; -C -1 ; WX 556 ; N edotaccent ; B 40 -15 516 706 ; -C -1 ; WX 278 ; N Igrave ; B -13 0 188 929 ; -C -1 ; WX 278 ; N Imacron ; B -17 0 296 879 ; -C -1 ; WX 556 ; N Lcaron ; B 76 0 537 718 ; -C -1 ; WX 834 ; N onehalf ; B 43 -19 773 703 ; -C -1 ; WX 549 ; N lessequal ; B 26 0 523 674 ; -C -1 ; WX 556 ; N ocircumflex ; B 35 -14 521 734 ; -C -1 ; WX 556 ; N ntilde ; B 65 0 491 722 ; -C -1 ; WX 722 ; N Uhungarumlaut ; B 79 -19 644 929 ; -C -1 ; WX 667 ; N Eacute ; B 86 0 616 929 ; -C -1 ; WX 556 ; N emacron ; B 40 -15 516 684 ; -C -1 ; WX 556 ; N gbreve ; B 40 -220 499 731 ; -C -1 ; WX 834 ; N onequarter ; B 73 -19 756 703 ; -C -1 ; WX 667 ; N Scaron ; B 49 -19 620 929 ; -C -1 ; WX 667 ; N Scommaaccent ; B 49 -225 620 737 ; -C -1 ; WX 778 ; N Ohungarumlaut ; B 39 -19 739 929 ; -C -1 ; WX 400 ; N degree ; B 54 411 346 703 ; -C -1 ; WX 556 ; N ograve ; B 35 -14 521 734 ; -C -1 ; WX 722 ; N Ccaron ; B 44 -19 681 929 ; -C -1 ; WX 556 ; N ugrave ; B 68 -15 489 734 ; -C -1 ; WX 453 ; N radical ; B -4 -80 458 762 ; -C -1 ; WX 722 ; N Dcaron ; B 81 0 674 929 ; -C -1 ; WX 333 ; N rcommaaccent ; B 77 -225 332 538 ; -C -1 ; WX 722 ; N Ntilde ; B 76 0 646 917 ; -C -1 ; WX 556 ; N otilde ; B 35 -14 521 722 ; -C -1 ; WX 722 ; N Rcommaaccent ; B 88 -225 684 718 ; -C -1 ; WX 556 ; N Lcommaaccent ; B 76 -225 537 718 ; -C -1 ; WX 667 ; N Atilde ; B 14 0 654 917 ; -C -1 ; WX 667 ; N Aogonek ; B 14 -225 654 718 ; -C -1 ; WX 667 ; N Aring ; B 14 0 654 931 ; -C -1 ; WX 778 ; N Otilde ; B 39 -19 739 917 ; -C -1 ; WX 500 ; N zdotaccent ; B 31 0 469 706 ; -C -1 ; WX 667 ; N Ecaron ; B 86 0 616 929 ; -C -1 ; WX 278 ; N Iogonek ; B -3 -225 211 718 ; -C -1 ; WX 500 ; N kcommaaccent ; B 67 -225 501 718 ; -C -1 ; WX 584 ; N minus ; B 39 216 545 289 ; -C -1 ; WX 278 ; N Icircumflex ; B -6 0 285 929 ; -C -1 ; WX 556 ; N ncaron ; B 65 0 491 734 ; -C -1 ; WX 278 ; N tcommaaccent ; B 14 -225 257 669 ; -C -1 ; WX 584 ; N logicalnot ; B 39 108 545 390 ; -C -1 ; WX 556 ; N odieresis ; B 35 -14 521 706 ; -C -1 ; WX 556 ; N udieresis ; B 68 -15 489 706 ; -C -1 ; WX 549 ; N notequal ; B 12 -35 537 551 ; -C -1 ; WX 556 ; N gcommaaccent ; B 40 -220 499 822 ; -C -1 ; WX 556 ; N eth ; B 35 -15 522 737 ; -C -1 ; WX 500 ; N zcaron ; B 31 0 469 734 ; -C -1 ; WX 556 ; N ncommaaccent ; B 65 -225 491 538 ; -C -1 ; WX 333 ; N onesuperior ; B 43 281 222 703 ; -C -1 ; WX 278 ; N imacron ; B 5 0 272 684 ; -C -1 ; WX 556 ; N Euro ; B 0 0 0 0 ; -EndCharMetrics -StartKernData -StartKernPairs 2705 -KPX A C -30 -KPX A Cacute -30 -KPX A Ccaron -30 -KPX A Ccedilla -30 -KPX A G -30 -KPX A Gbreve -30 -KPX A Gcommaaccent -30 -KPX A O -30 -KPX A Oacute -30 -KPX A Ocircumflex -30 -KPX A Odieresis -30 -KPX A Ograve -30 -KPX A Ohungarumlaut -30 -KPX A Omacron -30 -KPX A Oslash -30 -KPX A Otilde -30 -KPX A Q -30 -KPX A T -120 -KPX A Tcaron -120 -KPX A Tcommaaccent -120 -KPX A U -50 -KPX A Uacute -50 -KPX A Ucircumflex -50 -KPX A Udieresis -50 -KPX A Ugrave -50 -KPX A Uhungarumlaut -50 -KPX A Umacron -50 -KPX A Uogonek -50 -KPX A Uring -50 -KPX A V -70 -KPX A W -50 -KPX A Y -100 -KPX A Yacute -100 -KPX A Ydieresis -100 -KPX A u -30 -KPX A uacute -30 -KPX A ucircumflex -30 -KPX A udieresis -30 -KPX A ugrave -30 -KPX A uhungarumlaut -30 -KPX A umacron -30 -KPX A uogonek -30 -KPX A uring -30 -KPX A v -40 -KPX A w -40 -KPX A y -40 -KPX A yacute -40 -KPX A ydieresis -40 -KPX Aacute C -30 -KPX Aacute Cacute -30 -KPX Aacute Ccaron -30 -KPX Aacute Ccedilla -30 -KPX Aacute G -30 -KPX Aacute Gbreve -30 -KPX Aacute Gcommaaccent -30 -KPX Aacute O -30 -KPX Aacute Oacute -30 -KPX Aacute Ocircumflex -30 -KPX Aacute Odieresis -30 -KPX Aacute Ograve -30 -KPX Aacute Ohungarumlaut -30 -KPX Aacute Omacron -30 -KPX Aacute Oslash -30 -KPX Aacute Otilde -30 -KPX Aacute Q -30 -KPX Aacute T -120 -KPX Aacute Tcaron -120 -KPX Aacute Tcommaaccent -120 -KPX Aacute U -50 -KPX Aacute Uacute -50 -KPX Aacute Ucircumflex -50 -KPX Aacute Udieresis -50 -KPX Aacute Ugrave -50 -KPX Aacute Uhungarumlaut -50 -KPX Aacute Umacron -50 -KPX Aacute Uogonek -50 -KPX Aacute Uring -50 -KPX Aacute V -70 -KPX Aacute W -50 -KPX Aacute Y -100 -KPX Aacute Yacute -100 -KPX Aacute Ydieresis -100 -KPX Aacute u -30 -KPX Aacute uacute -30 -KPX Aacute ucircumflex -30 -KPX Aacute udieresis -30 -KPX Aacute ugrave -30 -KPX Aacute uhungarumlaut -30 -KPX Aacute umacron -30 -KPX Aacute uogonek -30 -KPX Aacute uring -30 -KPX Aacute v -40 -KPX Aacute w -40 -KPX Aacute y -40 -KPX Aacute yacute -40 -KPX Aacute ydieresis -40 -KPX Abreve C -30 -KPX Abreve Cacute -30 -KPX Abreve Ccaron -30 -KPX Abreve Ccedilla -30 -KPX Abreve G -30 -KPX Abreve Gbreve -30 -KPX Abreve Gcommaaccent -30 -KPX Abreve O -30 -KPX Abreve Oacute -30 -KPX Abreve Ocircumflex -30 -KPX Abreve Odieresis -30 -KPX Abreve Ograve -30 -KPX Abreve Ohungarumlaut -30 -KPX Abreve Omacron -30 -KPX Abreve Oslash -30 -KPX Abreve Otilde -30 -KPX Abreve Q -30 -KPX Abreve T -120 -KPX Abreve Tcaron -120 -KPX Abreve Tcommaaccent -120 -KPX Abreve U -50 -KPX Abreve Uacute -50 -KPX Abreve Ucircumflex -50 -KPX Abreve Udieresis -50 -KPX Abreve Ugrave -50 -KPX Abreve Uhungarumlaut -50 -KPX Abreve Umacron -50 -KPX Abreve Uogonek -50 -KPX Abreve Uring -50 -KPX Abreve V -70 -KPX Abreve W -50 -KPX Abreve Y -100 -KPX Abreve Yacute -100 -KPX Abreve Ydieresis -100 -KPX Abreve u -30 -KPX Abreve uacute -30 -KPX Abreve ucircumflex -30 -KPX Abreve udieresis -30 -KPX Abreve ugrave -30 -KPX Abreve uhungarumlaut -30 -KPX Abreve umacron -30 -KPX Abreve uogonek -30 -KPX Abreve uring -30 -KPX Abreve v -40 -KPX Abreve w -40 -KPX Abreve y -40 -KPX Abreve yacute -40 -KPX Abreve ydieresis -40 -KPX Acircumflex C -30 -KPX Acircumflex Cacute -30 -KPX Acircumflex Ccaron -30 -KPX Acircumflex Ccedilla -30 -KPX Acircumflex G -30 -KPX Acircumflex Gbreve -30 -KPX Acircumflex Gcommaaccent -30 -KPX Acircumflex O -30 -KPX Acircumflex Oacute -30 -KPX Acircumflex Ocircumflex -30 -KPX Acircumflex Odieresis -30 -KPX Acircumflex Ograve -30 -KPX Acircumflex Ohungarumlaut -30 -KPX Acircumflex Omacron -30 -KPX Acircumflex Oslash -30 -KPX Acircumflex Otilde -30 -KPX Acircumflex Q -30 -KPX Acircumflex T -120 -KPX Acircumflex Tcaron -120 -KPX Acircumflex Tcommaaccent -120 -KPX Acircumflex U -50 -KPX Acircumflex Uacute -50 -KPX Acircumflex Ucircumflex -50 -KPX Acircumflex Udieresis -50 -KPX Acircumflex Ugrave -50 -KPX Acircumflex Uhungarumlaut -50 -KPX Acircumflex Umacron -50 -KPX Acircumflex Uogonek -50 -KPX Acircumflex Uring -50 -KPX Acircumflex V -70 -KPX Acircumflex W -50 -KPX Acircumflex Y -100 -KPX Acircumflex Yacute -100 -KPX Acircumflex Ydieresis -100 -KPX Acircumflex u -30 -KPX Acircumflex uacute -30 -KPX Acircumflex ucircumflex -30 -KPX Acircumflex udieresis -30 -KPX Acircumflex ugrave -30 -KPX Acircumflex uhungarumlaut -30 -KPX Acircumflex umacron -30 -KPX Acircumflex uogonek -30 -KPX Acircumflex uring -30 -KPX Acircumflex v -40 -KPX Acircumflex w -40 -KPX Acircumflex y -40 -KPX Acircumflex yacute -40 -KPX Acircumflex ydieresis -40 -KPX Adieresis C -30 -KPX Adieresis Cacute -30 -KPX Adieresis Ccaron -30 -KPX Adieresis Ccedilla -30 -KPX Adieresis G -30 -KPX Adieresis Gbreve -30 -KPX Adieresis Gcommaaccent -30 -KPX Adieresis O -30 -KPX Adieresis Oacute -30 -KPX Adieresis Ocircumflex -30 -KPX Adieresis Odieresis -30 -KPX Adieresis Ograve -30 -KPX Adieresis Ohungarumlaut -30 -KPX Adieresis Omacron -30 -KPX Adieresis Oslash -30 -KPX Adieresis Otilde -30 -KPX Adieresis Q -30 -KPX Adieresis T -120 -KPX Adieresis Tcaron -120 -KPX Adieresis Tcommaaccent -120 -KPX Adieresis U -50 -KPX Adieresis Uacute -50 -KPX Adieresis Ucircumflex -50 -KPX Adieresis Udieresis -50 -KPX Adieresis Ugrave -50 -KPX Adieresis Uhungarumlaut -50 -KPX Adieresis Umacron -50 -KPX Adieresis Uogonek -50 -KPX Adieresis Uring -50 -KPX Adieresis V -70 -KPX Adieresis W -50 -KPX Adieresis Y -100 -KPX Adieresis Yacute -100 -KPX Adieresis Ydieresis -100 -KPX Adieresis u -30 -KPX Adieresis uacute -30 -KPX Adieresis ucircumflex -30 -KPX Adieresis udieresis -30 -KPX Adieresis ugrave -30 -KPX Adieresis uhungarumlaut -30 -KPX Adieresis umacron -30 -KPX Adieresis uogonek -30 -KPX Adieresis uring -30 -KPX Adieresis v -40 -KPX Adieresis w -40 -KPX Adieresis y -40 -KPX Adieresis yacute -40 -KPX Adieresis ydieresis -40 -KPX Agrave C -30 -KPX Agrave Cacute -30 -KPX Agrave Ccaron -30 -KPX Agrave Ccedilla -30 -KPX Agrave G -30 -KPX Agrave Gbreve -30 -KPX Agrave Gcommaaccent -30 -KPX Agrave O -30 -KPX Agrave Oacute -30 -KPX Agrave Ocircumflex -30 -KPX Agrave Odieresis -30 -KPX Agrave Ograve -30 -KPX Agrave Ohungarumlaut -30 -KPX Agrave Omacron -30 -KPX Agrave Oslash -30 -KPX Agrave Otilde -30 -KPX Agrave Q -30 -KPX Agrave T -120 -KPX Agrave Tcaron -120 -KPX Agrave Tcommaaccent -120 -KPX Agrave U -50 -KPX Agrave Uacute -50 -KPX Agrave Ucircumflex -50 -KPX Agrave Udieresis -50 -KPX Agrave Ugrave -50 -KPX Agrave Uhungarumlaut -50 -KPX Agrave Umacron -50 -KPX Agrave Uogonek -50 -KPX Agrave Uring -50 -KPX Agrave V -70 -KPX Agrave W -50 -KPX Agrave Y -100 -KPX Agrave Yacute -100 -KPX Agrave Ydieresis -100 -KPX Agrave u -30 -KPX Agrave uacute -30 -KPX Agrave ucircumflex -30 -KPX Agrave udieresis -30 -KPX Agrave ugrave -30 -KPX Agrave uhungarumlaut -30 -KPX Agrave umacron -30 -KPX Agrave uogonek -30 -KPX Agrave uring -30 -KPX Agrave v -40 -KPX Agrave w -40 -KPX Agrave y -40 -KPX Agrave yacute -40 -KPX Agrave ydieresis -40 -KPX Amacron C -30 -KPX Amacron Cacute -30 -KPX Amacron Ccaron -30 -KPX Amacron Ccedilla -30 -KPX Amacron G -30 -KPX Amacron Gbreve -30 -KPX Amacron Gcommaaccent -30 -KPX Amacron O -30 -KPX Amacron Oacute -30 -KPX Amacron Ocircumflex -30 -KPX Amacron Odieresis -30 -KPX Amacron Ograve -30 -KPX Amacron Ohungarumlaut -30 -KPX Amacron Omacron -30 -KPX Amacron Oslash -30 -KPX Amacron Otilde -30 -KPX Amacron Q -30 -KPX Amacron T -120 -KPX Amacron Tcaron -120 -KPX Amacron Tcommaaccent -120 -KPX Amacron U -50 -KPX Amacron Uacute -50 -KPX Amacron Ucircumflex -50 -KPX Amacron Udieresis -50 -KPX Amacron Ugrave -50 -KPX Amacron Uhungarumlaut -50 -KPX Amacron Umacron -50 -KPX Amacron Uogonek -50 -KPX Amacron Uring -50 -KPX Amacron V -70 -KPX Amacron W -50 -KPX Amacron Y -100 -KPX Amacron Yacute -100 -KPX Amacron Ydieresis -100 -KPX Amacron u -30 -KPX Amacron uacute -30 -KPX Amacron ucircumflex -30 -KPX Amacron udieresis -30 -KPX Amacron ugrave -30 -KPX Amacron uhungarumlaut -30 -KPX Amacron umacron -30 -KPX Amacron uogonek -30 -KPX Amacron uring -30 -KPX Amacron v -40 -KPX Amacron w -40 -KPX Amacron y -40 -KPX Amacron yacute -40 -KPX Amacron ydieresis -40 -KPX Aogonek C -30 -KPX Aogonek Cacute -30 -KPX Aogonek Ccaron -30 -KPX Aogonek Ccedilla -30 -KPX Aogonek G -30 -KPX Aogonek Gbreve -30 -KPX Aogonek Gcommaaccent -30 -KPX Aogonek O -30 -KPX Aogonek Oacute -30 -KPX Aogonek Ocircumflex -30 -KPX Aogonek Odieresis -30 -KPX Aogonek Ograve -30 -KPX Aogonek Ohungarumlaut -30 -KPX Aogonek Omacron -30 -KPX Aogonek Oslash -30 -KPX Aogonek Otilde -30 -KPX Aogonek Q -30 -KPX Aogonek T -120 -KPX Aogonek Tcaron -120 -KPX Aogonek Tcommaaccent -120 -KPX Aogonek U -50 -KPX Aogonek Uacute -50 -KPX Aogonek Ucircumflex -50 -KPX Aogonek Udieresis -50 -KPX Aogonek Ugrave -50 -KPX Aogonek Uhungarumlaut -50 -KPX Aogonek Umacron -50 -KPX Aogonek Uogonek -50 -KPX Aogonek Uring -50 -KPX Aogonek V -70 -KPX Aogonek W -50 -KPX Aogonek Y -100 -KPX Aogonek Yacute -100 -KPX Aogonek Ydieresis -100 -KPX Aogonek u -30 -KPX Aogonek uacute -30 -KPX Aogonek ucircumflex -30 -KPX Aogonek udieresis -30 -KPX Aogonek ugrave -30 -KPX Aogonek uhungarumlaut -30 -KPX Aogonek umacron -30 -KPX Aogonek uogonek -30 -KPX Aogonek uring -30 -KPX Aogonek v -40 -KPX Aogonek w -40 -KPX Aogonek y -40 -KPX Aogonek yacute -40 -KPX Aogonek ydieresis -40 -KPX Aring C -30 -KPX Aring Cacute -30 -KPX Aring Ccaron -30 -KPX Aring Ccedilla -30 -KPX Aring G -30 -KPX Aring Gbreve -30 -KPX Aring Gcommaaccent -30 -KPX Aring O -30 -KPX Aring Oacute -30 -KPX Aring Ocircumflex -30 -KPX Aring Odieresis -30 -KPX Aring Ograve -30 -KPX Aring Ohungarumlaut -30 -KPX Aring Omacron -30 -KPX Aring Oslash -30 -KPX Aring Otilde -30 -KPX Aring Q -30 -KPX Aring T -120 -KPX Aring Tcaron -120 -KPX Aring Tcommaaccent -120 -KPX Aring U -50 -KPX Aring Uacute -50 -KPX Aring Ucircumflex -50 -KPX Aring Udieresis -50 -KPX Aring Ugrave -50 -KPX Aring Uhungarumlaut -50 -KPX Aring Umacron -50 -KPX Aring Uogonek -50 -KPX Aring Uring -50 -KPX Aring V -70 -KPX Aring W -50 -KPX Aring Y -100 -KPX Aring Yacute -100 -KPX Aring Ydieresis -100 -KPX Aring u -30 -KPX Aring uacute -30 -KPX Aring ucircumflex -30 -KPX Aring udieresis -30 -KPX Aring ugrave -30 -KPX Aring uhungarumlaut -30 -KPX Aring umacron -30 -KPX Aring uogonek -30 -KPX Aring uring -30 -KPX Aring v -40 -KPX Aring w -40 -KPX Aring y -40 -KPX Aring yacute -40 -KPX Aring ydieresis -40 -KPX Atilde C -30 -KPX Atilde Cacute -30 -KPX Atilde Ccaron -30 -KPX Atilde Ccedilla -30 -KPX Atilde G -30 -KPX Atilde Gbreve -30 -KPX Atilde Gcommaaccent -30 -KPX Atilde O -30 -KPX Atilde Oacute -30 -KPX Atilde Ocircumflex -30 -KPX Atilde Odieresis -30 -KPX Atilde Ograve -30 -KPX Atilde Ohungarumlaut -30 -KPX Atilde Omacron -30 -KPX Atilde Oslash -30 -KPX Atilde Otilde -30 -KPX Atilde Q -30 -KPX Atilde T -120 -KPX Atilde Tcaron -120 -KPX Atilde Tcommaaccent -120 -KPX Atilde U -50 -KPX Atilde Uacute -50 -KPX Atilde Ucircumflex -50 -KPX Atilde Udieresis -50 -KPX Atilde Ugrave -50 -KPX Atilde Uhungarumlaut -50 -KPX Atilde Umacron -50 -KPX Atilde Uogonek -50 -KPX Atilde Uring -50 -KPX Atilde V -70 -KPX Atilde W -50 -KPX Atilde Y -100 -KPX Atilde Yacute -100 -KPX Atilde Ydieresis -100 -KPX Atilde u -30 -KPX Atilde uacute -30 -KPX Atilde ucircumflex -30 -KPX Atilde udieresis -30 -KPX Atilde ugrave -30 -KPX Atilde uhungarumlaut -30 -KPX Atilde umacron -30 -KPX Atilde uogonek -30 -KPX Atilde uring -30 -KPX Atilde v -40 -KPX Atilde w -40 -KPX Atilde y -40 -KPX Atilde yacute -40 -KPX Atilde ydieresis -40 -KPX B U -10 -KPX B Uacute -10 -KPX B Ucircumflex -10 -KPX B Udieresis -10 -KPX B Ugrave -10 -KPX B Uhungarumlaut -10 -KPX B Umacron -10 -KPX B Uogonek -10 -KPX B Uring -10 -KPX B comma -20 -KPX B period -20 -KPX C comma -30 -KPX C period -30 -KPX Cacute comma -30 -KPX Cacute period -30 -KPX Ccaron comma -30 -KPX Ccaron period -30 -KPX Ccedilla comma -30 -KPX Ccedilla period -30 -KPX D A -40 -KPX D Aacute -40 -KPX D Abreve -40 -KPX D Acircumflex -40 -KPX D Adieresis -40 -KPX D Agrave -40 -KPX D Amacron -40 -KPX D Aogonek -40 -KPX D Aring -40 -KPX D Atilde -40 -KPX D V -70 -KPX D W -40 -KPX D Y -90 -KPX D Yacute -90 -KPX D Ydieresis -90 -KPX D comma -70 -KPX D period -70 -KPX Dcaron A -40 -KPX Dcaron Aacute -40 -KPX Dcaron Abreve -40 -KPX Dcaron Acircumflex -40 -KPX Dcaron Adieresis -40 -KPX Dcaron Agrave -40 -KPX Dcaron Amacron -40 -KPX Dcaron Aogonek -40 -KPX Dcaron Aring -40 -KPX Dcaron Atilde -40 -KPX Dcaron V -70 -KPX Dcaron W -40 -KPX Dcaron Y -90 -KPX Dcaron Yacute -90 -KPX Dcaron Ydieresis -90 -KPX Dcaron comma -70 -KPX Dcaron period -70 -KPX Dcroat A -40 -KPX Dcroat Aacute -40 -KPX Dcroat Abreve -40 -KPX Dcroat Acircumflex -40 -KPX Dcroat Adieresis -40 -KPX Dcroat Agrave -40 -KPX Dcroat Amacron -40 -KPX Dcroat Aogonek -40 -KPX Dcroat Aring -40 -KPX Dcroat Atilde -40 -KPX Dcroat V -70 -KPX Dcroat W -40 -KPX Dcroat Y -90 -KPX Dcroat Yacute -90 -KPX Dcroat Ydieresis -90 -KPX Dcroat comma -70 -KPX Dcroat period -70 -KPX F A -80 -KPX F Aacute -80 -KPX F Abreve -80 -KPX F Acircumflex -80 -KPX F Adieresis -80 -KPX F Agrave -80 -KPX F Amacron -80 -KPX F Aogonek -80 -KPX F Aring -80 -KPX F Atilde -80 -KPX F a -50 -KPX F aacute -50 -KPX F abreve -50 -KPX F acircumflex -50 -KPX F adieresis -50 -KPX F agrave -50 -KPX F amacron -50 -KPX F aogonek -50 -KPX F aring -50 -KPX F atilde -50 -KPX F comma -150 -KPX F e -30 -KPX F eacute -30 -KPX F ecaron -30 -KPX F ecircumflex -30 -KPX F edieresis -30 -KPX F edotaccent -30 -KPX F egrave -30 -KPX F emacron -30 -KPX F eogonek -30 -KPX F o -30 -KPX F oacute -30 -KPX F ocircumflex -30 -KPX F odieresis -30 -KPX F ograve -30 -KPX F ohungarumlaut -30 -KPX F omacron -30 -KPX F oslash -30 -KPX F otilde -30 -KPX F period -150 -KPX F r -45 -KPX F racute -45 -KPX F rcaron -45 -KPX F rcommaaccent -45 -KPX J A -20 -KPX J Aacute -20 -KPX J Abreve -20 -KPX J Acircumflex -20 -KPX J Adieresis -20 -KPX J Agrave -20 -KPX J Amacron -20 -KPX J Aogonek -20 -KPX J Aring -20 -KPX J Atilde -20 -KPX J a -20 -KPX J aacute -20 -KPX J abreve -20 -KPX J acircumflex -20 -KPX J adieresis -20 -KPX J agrave -20 -KPX J amacron -20 -KPX J aogonek -20 -KPX J aring -20 -KPX J atilde -20 -KPX J comma -30 -KPX J period -30 -KPX J u -20 -KPX J uacute -20 -KPX J ucircumflex -20 -KPX J udieresis -20 -KPX J ugrave -20 -KPX J uhungarumlaut -20 -KPX J umacron -20 -KPX J uogonek -20 -KPX J uring -20 -KPX K O -50 -KPX K Oacute -50 -KPX K Ocircumflex -50 -KPX K Odieresis -50 -KPX K Ograve -50 -KPX K Ohungarumlaut -50 -KPX K Omacron -50 -KPX K Oslash -50 -KPX K Otilde -50 -KPX K e -40 -KPX K eacute -40 -KPX K ecaron -40 -KPX K ecircumflex -40 -KPX K edieresis -40 -KPX K edotaccent -40 -KPX K egrave -40 -KPX K emacron -40 -KPX K eogonek -40 -KPX K o -40 -KPX K oacute -40 -KPX K ocircumflex -40 -KPX K odieresis -40 -KPX K ograve -40 -KPX K ohungarumlaut -40 -KPX K omacron -40 -KPX K oslash -40 -KPX K otilde -40 -KPX K u -30 -KPX K uacute -30 -KPX K ucircumflex -30 -KPX K udieresis -30 -KPX K ugrave -30 -KPX K uhungarumlaut -30 -KPX K umacron -30 -KPX K uogonek -30 -KPX K uring -30 -KPX K y -50 -KPX K yacute -50 -KPX K ydieresis -50 -KPX Kcommaaccent O -50 -KPX Kcommaaccent Oacute -50 -KPX Kcommaaccent Ocircumflex -50 -KPX Kcommaaccent Odieresis -50 -KPX Kcommaaccent Ograve -50 -KPX Kcommaaccent Ohungarumlaut -50 -KPX Kcommaaccent Omacron -50 -KPX Kcommaaccent Oslash -50 -KPX Kcommaaccent Otilde -50 -KPX Kcommaaccent e -40 -KPX Kcommaaccent eacute -40 -KPX Kcommaaccent ecaron -40 -KPX Kcommaaccent ecircumflex -40 -KPX Kcommaaccent edieresis -40 -KPX Kcommaaccent edotaccent -40 -KPX Kcommaaccent egrave -40 -KPX Kcommaaccent emacron -40 -KPX Kcommaaccent eogonek -40 -KPX Kcommaaccent o -40 -KPX Kcommaaccent oacute -40 -KPX Kcommaaccent ocircumflex -40 -KPX Kcommaaccent odieresis -40 -KPX Kcommaaccent ograve -40 -KPX Kcommaaccent ohungarumlaut -40 -KPX Kcommaaccent omacron -40 -KPX Kcommaaccent oslash -40 -KPX Kcommaaccent otilde -40 -KPX Kcommaaccent u -30 -KPX Kcommaaccent uacute -30 -KPX Kcommaaccent ucircumflex -30 -KPX Kcommaaccent udieresis -30 -KPX Kcommaaccent ugrave -30 -KPX Kcommaaccent uhungarumlaut -30 -KPX Kcommaaccent umacron -30 -KPX Kcommaaccent uogonek -30 -KPX Kcommaaccent uring -30 -KPX Kcommaaccent y -50 -KPX Kcommaaccent yacute -50 -KPX Kcommaaccent ydieresis -50 -KPX L T -110 -KPX L Tcaron -110 -KPX L Tcommaaccent -110 -KPX L V -110 -KPX L W -70 -KPX L Y -140 -KPX L Yacute -140 -KPX L Ydieresis -140 -KPX L quotedblright -140 -KPX L quoteright -160 -KPX L y -30 -KPX L yacute -30 -KPX L ydieresis -30 -KPX Lacute T -110 -KPX Lacute Tcaron -110 -KPX Lacute Tcommaaccent -110 -KPX Lacute V -110 -KPX Lacute W -70 -KPX Lacute Y -140 -KPX Lacute Yacute -140 -KPX Lacute Ydieresis -140 -KPX Lacute quotedblright -140 -KPX Lacute quoteright -160 -KPX Lacute y -30 -KPX Lacute yacute -30 -KPX Lacute ydieresis -30 -KPX Lcaron T -110 -KPX Lcaron Tcaron -110 -KPX Lcaron Tcommaaccent -110 -KPX Lcaron V -110 -KPX Lcaron W -70 -KPX Lcaron Y -140 -KPX Lcaron Yacute -140 -KPX Lcaron Ydieresis -140 -KPX Lcaron quotedblright -140 -KPX Lcaron quoteright -160 -KPX Lcaron y -30 -KPX Lcaron yacute -30 -KPX Lcaron ydieresis -30 -KPX Lcommaaccent T -110 -KPX Lcommaaccent Tcaron -110 -KPX Lcommaaccent Tcommaaccent -110 -KPX Lcommaaccent V -110 -KPX Lcommaaccent W -70 -KPX Lcommaaccent Y -140 -KPX Lcommaaccent Yacute -140 -KPX Lcommaaccent Ydieresis -140 -KPX Lcommaaccent quotedblright -140 -KPX Lcommaaccent quoteright -160 -KPX Lcommaaccent y -30 -KPX Lcommaaccent yacute -30 -KPX Lcommaaccent ydieresis -30 -KPX Lslash T -110 -KPX Lslash Tcaron -110 -KPX Lslash Tcommaaccent -110 -KPX Lslash V -110 -KPX Lslash W -70 -KPX Lslash Y -140 -KPX Lslash Yacute -140 -KPX Lslash Ydieresis -140 -KPX Lslash quotedblright -140 -KPX Lslash quoteright -160 -KPX Lslash y -30 -KPX Lslash yacute -30 -KPX Lslash ydieresis -30 -KPX O A -20 -KPX O Aacute -20 -KPX O Abreve -20 -KPX O Acircumflex -20 -KPX O Adieresis -20 -KPX O Agrave -20 -KPX O Amacron -20 -KPX O Aogonek -20 -KPX O Aring -20 -KPX O Atilde -20 -KPX O T -40 -KPX O Tcaron -40 -KPX O Tcommaaccent -40 -KPX O V -50 -KPX O W -30 -KPX O X -60 -KPX O Y -70 -KPX O Yacute -70 -KPX O Ydieresis -70 -KPX O comma -40 -KPX O period -40 -KPX Oacute A -20 -KPX Oacute Aacute -20 -KPX Oacute Abreve -20 -KPX Oacute Acircumflex -20 -KPX Oacute Adieresis -20 -KPX Oacute Agrave -20 -KPX Oacute Amacron -20 -KPX Oacute Aogonek -20 -KPX Oacute Aring -20 -KPX Oacute Atilde -20 -KPX Oacute T -40 -KPX Oacute Tcaron -40 -KPX Oacute Tcommaaccent -40 -KPX Oacute V -50 -KPX Oacute W -30 -KPX Oacute X -60 -KPX Oacute Y -70 -KPX Oacute Yacute -70 -KPX Oacute Ydieresis -70 -KPX Oacute comma -40 -KPX Oacute period -40 -KPX Ocircumflex A -20 -KPX Ocircumflex Aacute -20 -KPX Ocircumflex Abreve -20 -KPX Ocircumflex Acircumflex -20 -KPX Ocircumflex Adieresis -20 -KPX Ocircumflex Agrave -20 -KPX Ocircumflex Amacron -20 -KPX Ocircumflex Aogonek -20 -KPX Ocircumflex Aring -20 -KPX Ocircumflex Atilde -20 -KPX Ocircumflex T -40 -KPX Ocircumflex Tcaron -40 -KPX Ocircumflex Tcommaaccent -40 -KPX Ocircumflex V -50 -KPX Ocircumflex W -30 -KPX Ocircumflex X -60 -KPX Ocircumflex Y -70 -KPX Ocircumflex Yacute -70 -KPX Ocircumflex Ydieresis -70 -KPX Ocircumflex comma -40 -KPX Ocircumflex period -40 -KPX Odieresis A -20 -KPX Odieresis Aacute -20 -KPX Odieresis Abreve -20 -KPX Odieresis Acircumflex -20 -KPX Odieresis Adieresis -20 -KPX Odieresis Agrave -20 -KPX Odieresis Amacron -20 -KPX Odieresis Aogonek -20 -KPX Odieresis Aring -20 -KPX Odieresis Atilde -20 -KPX Odieresis T -40 -KPX Odieresis Tcaron -40 -KPX Odieresis Tcommaaccent -40 -KPX Odieresis V -50 -KPX Odieresis W -30 -KPX Odieresis X -60 -KPX Odieresis Y -70 -KPX Odieresis Yacute -70 -KPX Odieresis Ydieresis -70 -KPX Odieresis comma -40 -KPX Odieresis period -40 -KPX Ograve A -20 -KPX Ograve Aacute -20 -KPX Ograve Abreve -20 -KPX Ograve Acircumflex -20 -KPX Ograve Adieresis -20 -KPX Ograve Agrave -20 -KPX Ograve Amacron -20 -KPX Ograve Aogonek -20 -KPX Ograve Aring -20 -KPX Ograve Atilde -20 -KPX Ograve T -40 -KPX Ograve Tcaron -40 -KPX Ograve Tcommaaccent -40 -KPX Ograve V -50 -KPX Ograve W -30 -KPX Ograve X -60 -KPX Ograve Y -70 -KPX Ograve Yacute -70 -KPX Ograve Ydieresis -70 -KPX Ograve comma -40 -KPX Ograve period -40 -KPX Ohungarumlaut A -20 -KPX Ohungarumlaut Aacute -20 -KPX Ohungarumlaut Abreve -20 -KPX Ohungarumlaut Acircumflex -20 -KPX Ohungarumlaut Adieresis -20 -KPX Ohungarumlaut Agrave -20 -KPX Ohungarumlaut Amacron -20 -KPX Ohungarumlaut Aogonek -20 -KPX Ohungarumlaut Aring -20 -KPX Ohungarumlaut Atilde -20 -KPX Ohungarumlaut T -40 -KPX Ohungarumlaut Tcaron -40 -KPX Ohungarumlaut Tcommaaccent -40 -KPX Ohungarumlaut V -50 -KPX Ohungarumlaut W -30 -KPX Ohungarumlaut X -60 -KPX Ohungarumlaut Y -70 -KPX Ohungarumlaut Yacute -70 -KPX Ohungarumlaut Ydieresis -70 -KPX Ohungarumlaut comma -40 -KPX Ohungarumlaut period -40 -KPX Omacron A -20 -KPX Omacron Aacute -20 -KPX Omacron Abreve -20 -KPX Omacron Acircumflex -20 -KPX Omacron Adieresis -20 -KPX Omacron Agrave -20 -KPX Omacron Amacron -20 -KPX Omacron Aogonek -20 -KPX Omacron Aring -20 -KPX Omacron Atilde -20 -KPX Omacron T -40 -KPX Omacron Tcaron -40 -KPX Omacron Tcommaaccent -40 -KPX Omacron V -50 -KPX Omacron W -30 -KPX Omacron X -60 -KPX Omacron Y -70 -KPX Omacron Yacute -70 -KPX Omacron Ydieresis -70 -KPX Omacron comma -40 -KPX Omacron period -40 -KPX Oslash A -20 -KPX Oslash Aacute -20 -KPX Oslash Abreve -20 -KPX Oslash Acircumflex -20 -KPX Oslash Adieresis -20 -KPX Oslash Agrave -20 -KPX Oslash Amacron -20 -KPX Oslash Aogonek -20 -KPX Oslash Aring -20 -KPX Oslash Atilde -20 -KPX Oslash T -40 -KPX Oslash Tcaron -40 -KPX Oslash Tcommaaccent -40 -KPX Oslash V -50 -KPX Oslash W -30 -KPX Oslash X -60 -KPX Oslash Y -70 -KPX Oslash Yacute -70 -KPX Oslash Ydieresis -70 -KPX Oslash comma -40 -KPX Oslash period -40 -KPX Otilde A -20 -KPX Otilde Aacute -20 -KPX Otilde Abreve -20 -KPX Otilde Acircumflex -20 -KPX Otilde Adieresis -20 -KPX Otilde Agrave -20 -KPX Otilde Amacron -20 -KPX Otilde Aogonek -20 -KPX Otilde Aring -20 -KPX Otilde Atilde -20 -KPX Otilde T -40 -KPX Otilde Tcaron -40 -KPX Otilde Tcommaaccent -40 -KPX Otilde V -50 -KPX Otilde W -30 -KPX Otilde X -60 -KPX Otilde Y -70 -KPX Otilde Yacute -70 -KPX Otilde Ydieresis -70 -KPX Otilde comma -40 -KPX Otilde period -40 -KPX P A -120 -KPX P Aacute -120 -KPX P Abreve -120 -KPX P Acircumflex -120 -KPX P Adieresis -120 -KPX P Agrave -120 -KPX P Amacron -120 -KPX P Aogonek -120 -KPX P Aring -120 -KPX P Atilde -120 -KPX P a -40 -KPX P aacute -40 -KPX P abreve -40 -KPX P acircumflex -40 -KPX P adieresis -40 -KPX P agrave -40 -KPX P amacron -40 -KPX P aogonek -40 -KPX P aring -40 -KPX P atilde -40 -KPX P comma -180 -KPX P e -50 -KPX P eacute -50 -KPX P ecaron -50 -KPX P ecircumflex -50 -KPX P edieresis -50 -KPX P edotaccent -50 -KPX P egrave -50 -KPX P emacron -50 -KPX P eogonek -50 -KPX P o -50 -KPX P oacute -50 -KPX P ocircumflex -50 -KPX P odieresis -50 -KPX P ograve -50 -KPX P ohungarumlaut -50 -KPX P omacron -50 -KPX P oslash -50 -KPX P otilde -50 -KPX P period -180 -KPX Q U -10 -KPX Q Uacute -10 -KPX Q Ucircumflex -10 -KPX Q Udieresis -10 -KPX Q Ugrave -10 -KPX Q Uhungarumlaut -10 -KPX Q Umacron -10 -KPX Q Uogonek -10 -KPX Q Uring -10 -KPX R O -20 -KPX R Oacute -20 -KPX R Ocircumflex -20 -KPX R Odieresis -20 -KPX R Ograve -20 -KPX R Ohungarumlaut -20 -KPX R Omacron -20 -KPX R Oslash -20 -KPX R Otilde -20 -KPX R T -30 -KPX R Tcaron -30 -KPX R Tcommaaccent -30 -KPX R U -40 -KPX R Uacute -40 -KPX R Ucircumflex -40 -KPX R Udieresis -40 -KPX R Ugrave -40 -KPX R Uhungarumlaut -40 -KPX R Umacron -40 -KPX R Uogonek -40 -KPX R Uring -40 -KPX R V -50 -KPX R W -30 -KPX R Y -50 -KPX R Yacute -50 -KPX R Ydieresis -50 -KPX Racute O -20 -KPX Racute Oacute -20 -KPX Racute Ocircumflex -20 -KPX Racute Odieresis -20 -KPX Racute Ograve -20 -KPX Racute Ohungarumlaut -20 -KPX Racute Omacron -20 -KPX Racute Oslash -20 -KPX Racute Otilde -20 -KPX Racute T -30 -KPX Racute Tcaron -30 -KPX Racute Tcommaaccent -30 -KPX Racute U -40 -KPX Racute Uacute -40 -KPX Racute Ucircumflex -40 -KPX Racute Udieresis -40 -KPX Racute Ugrave -40 -KPX Racute Uhungarumlaut -40 -KPX Racute Umacron -40 -KPX Racute Uogonek -40 -KPX Racute Uring -40 -KPX Racute V -50 -KPX Racute W -30 -KPX Racute Y -50 -KPX Racute Yacute -50 -KPX Racute Ydieresis -50 -KPX Rcaron O -20 -KPX Rcaron Oacute -20 -KPX Rcaron Ocircumflex -20 -KPX Rcaron Odieresis -20 -KPX Rcaron Ograve -20 -KPX Rcaron Ohungarumlaut -20 -KPX Rcaron Omacron -20 -KPX Rcaron Oslash -20 -KPX Rcaron Otilde -20 -KPX Rcaron T -30 -KPX Rcaron Tcaron -30 -KPX Rcaron Tcommaaccent -30 -KPX Rcaron U -40 -KPX Rcaron Uacute -40 -KPX Rcaron Ucircumflex -40 -KPX Rcaron Udieresis -40 -KPX Rcaron Ugrave -40 -KPX Rcaron Uhungarumlaut -40 -KPX Rcaron Umacron -40 -KPX Rcaron Uogonek -40 -KPX Rcaron Uring -40 -KPX Rcaron V -50 -KPX Rcaron W -30 -KPX Rcaron Y -50 -KPX Rcaron Yacute -50 -KPX Rcaron Ydieresis -50 -KPX Rcommaaccent O -20 -KPX Rcommaaccent Oacute -20 -KPX Rcommaaccent Ocircumflex -20 -KPX Rcommaaccent Odieresis -20 -KPX Rcommaaccent Ograve -20 -KPX Rcommaaccent Ohungarumlaut -20 -KPX Rcommaaccent Omacron -20 -KPX Rcommaaccent Oslash -20 -KPX Rcommaaccent Otilde -20 -KPX Rcommaaccent T -30 -KPX Rcommaaccent Tcaron -30 -KPX Rcommaaccent Tcommaaccent -30 -KPX Rcommaaccent U -40 -KPX Rcommaaccent Uacute -40 -KPX Rcommaaccent Ucircumflex -40 -KPX Rcommaaccent Udieresis -40 -KPX Rcommaaccent Ugrave -40 -KPX Rcommaaccent Uhungarumlaut -40 -KPX Rcommaaccent Umacron -40 -KPX Rcommaaccent Uogonek -40 -KPX Rcommaaccent Uring -40 -KPX Rcommaaccent V -50 -KPX Rcommaaccent W -30 -KPX Rcommaaccent Y -50 -KPX Rcommaaccent Yacute -50 -KPX Rcommaaccent Ydieresis -50 -KPX S comma -20 -KPX S period -20 -KPX Sacute comma -20 -KPX Sacute period -20 -KPX Scaron comma -20 -KPX Scaron period -20 -KPX Scedilla comma -20 -KPX Scedilla period -20 -KPX Scommaaccent comma -20 -KPX Scommaaccent period -20 -KPX T A -120 -KPX T Aacute -120 -KPX T Abreve -120 -KPX T Acircumflex -120 -KPX T Adieresis -120 -KPX T Agrave -120 -KPX T Amacron -120 -KPX T Aogonek -120 -KPX T Aring -120 -KPX T Atilde -120 -KPX T O -40 -KPX T Oacute -40 -KPX T Ocircumflex -40 -KPX T Odieresis -40 -KPX T Ograve -40 -KPX T Ohungarumlaut -40 -KPX T Omacron -40 -KPX T Oslash -40 -KPX T Otilde -40 -KPX T a -120 -KPX T aacute -120 -KPX T abreve -60 -KPX T acircumflex -120 -KPX T adieresis -120 -KPX T agrave -120 -KPX T amacron -60 -KPX T aogonek -120 -KPX T aring -120 -KPX T atilde -60 -KPX T colon -20 -KPX T comma -120 -KPX T e -120 -KPX T eacute -120 -KPX T ecaron -120 -KPX T ecircumflex -120 -KPX T edieresis -120 -KPX T edotaccent -120 -KPX T egrave -60 -KPX T emacron -60 -KPX T eogonek -120 -KPX T hyphen -140 -KPX T o -120 -KPX T oacute -120 -KPX T ocircumflex -120 -KPX T odieresis -120 -KPX T ograve -120 -KPX T ohungarumlaut -120 -KPX T omacron -60 -KPX T oslash -120 -KPX T otilde -60 -KPX T period -120 -KPX T r -120 -KPX T racute -120 -KPX T rcaron -120 -KPX T rcommaaccent -120 -KPX T semicolon -20 -KPX T u -120 -KPX T uacute -120 -KPX T ucircumflex -120 -KPX T udieresis -120 -KPX T ugrave -120 -KPX T uhungarumlaut -120 -KPX T umacron -60 -KPX T uogonek -120 -KPX T uring -120 -KPX T w -120 -KPX T y -120 -KPX T yacute -120 -KPX T ydieresis -60 -KPX Tcaron A -120 -KPX Tcaron Aacute -120 -KPX Tcaron Abreve -120 -KPX Tcaron Acircumflex -120 -KPX Tcaron Adieresis -120 -KPX Tcaron Agrave -120 -KPX Tcaron Amacron -120 -KPX Tcaron Aogonek -120 -KPX Tcaron Aring -120 -KPX Tcaron Atilde -120 -KPX Tcaron O -40 -KPX Tcaron Oacute -40 -KPX Tcaron Ocircumflex -40 -KPX Tcaron Odieresis -40 -KPX Tcaron Ograve -40 -KPX Tcaron Ohungarumlaut -40 -KPX Tcaron Omacron -40 -KPX Tcaron Oslash -40 -KPX Tcaron Otilde -40 -KPX Tcaron a -120 -KPX Tcaron aacute -120 -KPX Tcaron abreve -60 -KPX Tcaron acircumflex -120 -KPX Tcaron adieresis -120 -KPX Tcaron agrave -120 -KPX Tcaron amacron -60 -KPX Tcaron aogonek -120 -KPX Tcaron aring -120 -KPX Tcaron atilde -60 -KPX Tcaron colon -20 -KPX Tcaron comma -120 -KPX Tcaron e -120 -KPX Tcaron eacute -120 -KPX Tcaron ecaron -120 -KPX Tcaron ecircumflex -120 -KPX Tcaron edieresis -120 -KPX Tcaron edotaccent -120 -KPX Tcaron egrave -60 -KPX Tcaron emacron -60 -KPX Tcaron eogonek -120 -KPX Tcaron hyphen -140 -KPX Tcaron o -120 -KPX Tcaron oacute -120 -KPX Tcaron ocircumflex -120 -KPX Tcaron odieresis -120 -KPX Tcaron ograve -120 -KPX Tcaron ohungarumlaut -120 -KPX Tcaron omacron -60 -KPX Tcaron oslash -120 -KPX Tcaron otilde -60 -KPX Tcaron period -120 -KPX Tcaron r -120 -KPX Tcaron racute -120 -KPX Tcaron rcaron -120 -KPX Tcaron rcommaaccent -120 -KPX Tcaron semicolon -20 -KPX Tcaron u -120 -KPX Tcaron uacute -120 -KPX Tcaron ucircumflex -120 -KPX Tcaron udieresis -120 -KPX Tcaron ugrave -120 -KPX Tcaron uhungarumlaut -120 -KPX Tcaron umacron -60 -KPX Tcaron uogonek -120 -KPX Tcaron uring -120 -KPX Tcaron w -120 -KPX Tcaron y -120 -KPX Tcaron yacute -120 -KPX Tcaron ydieresis -60 -KPX Tcommaaccent A -120 -KPX Tcommaaccent Aacute -120 -KPX Tcommaaccent Abreve -120 -KPX Tcommaaccent Acircumflex -120 -KPX Tcommaaccent Adieresis -120 -KPX Tcommaaccent Agrave -120 -KPX Tcommaaccent Amacron -120 -KPX Tcommaaccent Aogonek -120 -KPX Tcommaaccent Aring -120 -KPX Tcommaaccent Atilde -120 -KPX Tcommaaccent O -40 -KPX Tcommaaccent Oacute -40 -KPX Tcommaaccent Ocircumflex -40 -KPX Tcommaaccent Odieresis -40 -KPX Tcommaaccent Ograve -40 -KPX Tcommaaccent Ohungarumlaut -40 -KPX Tcommaaccent Omacron -40 -KPX Tcommaaccent Oslash -40 -KPX Tcommaaccent Otilde -40 -KPX Tcommaaccent a -120 -KPX Tcommaaccent aacute -120 -KPX Tcommaaccent abreve -60 -KPX Tcommaaccent acircumflex -120 -KPX Tcommaaccent adieresis -120 -KPX Tcommaaccent agrave -120 -KPX Tcommaaccent amacron -60 -KPX Tcommaaccent aogonek -120 -KPX Tcommaaccent aring -120 -KPX Tcommaaccent atilde -60 -KPX Tcommaaccent colon -20 -KPX Tcommaaccent comma -120 -KPX Tcommaaccent e -120 -KPX Tcommaaccent eacute -120 -KPX Tcommaaccent ecaron -120 -KPX Tcommaaccent ecircumflex -120 -KPX Tcommaaccent edieresis -120 -KPX Tcommaaccent edotaccent -120 -KPX Tcommaaccent egrave -60 -KPX Tcommaaccent emacron -60 -KPX Tcommaaccent eogonek -120 -KPX Tcommaaccent hyphen -140 -KPX Tcommaaccent o -120 -KPX Tcommaaccent oacute -120 -KPX Tcommaaccent ocircumflex -120 -KPX Tcommaaccent odieresis -120 -KPX Tcommaaccent ograve -120 -KPX Tcommaaccent ohungarumlaut -120 -KPX Tcommaaccent omacron -60 -KPX Tcommaaccent oslash -120 -KPX Tcommaaccent otilde -60 -KPX Tcommaaccent period -120 -KPX Tcommaaccent r -120 -KPX Tcommaaccent racute -120 -KPX Tcommaaccent rcaron -120 -KPX Tcommaaccent rcommaaccent -120 -KPX Tcommaaccent semicolon -20 -KPX Tcommaaccent u -120 -KPX Tcommaaccent uacute -120 -KPX Tcommaaccent ucircumflex -120 -KPX Tcommaaccent udieresis -120 -KPX Tcommaaccent ugrave -120 -KPX Tcommaaccent uhungarumlaut -120 -KPX Tcommaaccent umacron -60 -KPX Tcommaaccent uogonek -120 -KPX Tcommaaccent uring -120 -KPX Tcommaaccent w -120 -KPX Tcommaaccent y -120 -KPX Tcommaaccent yacute -120 -KPX Tcommaaccent ydieresis -60 -KPX U A -40 -KPX U Aacute -40 -KPX U Abreve -40 -KPX U Acircumflex -40 -KPX U Adieresis -40 -KPX U Agrave -40 -KPX U Amacron -40 -KPX U Aogonek -40 -KPX U Aring -40 -KPX U Atilde -40 -KPX U comma -40 -KPX U period -40 -KPX Uacute A -40 -KPX Uacute Aacute -40 -KPX Uacute Abreve -40 -KPX Uacute Acircumflex -40 -KPX Uacute Adieresis -40 -KPX Uacute Agrave -40 -KPX Uacute Amacron -40 -KPX Uacute Aogonek -40 -KPX Uacute Aring -40 -KPX Uacute Atilde -40 -KPX Uacute comma -40 -KPX Uacute period -40 -KPX Ucircumflex A -40 -KPX Ucircumflex Aacute -40 -KPX Ucircumflex Abreve -40 -KPX Ucircumflex Acircumflex -40 -KPX Ucircumflex Adieresis -40 -KPX Ucircumflex Agrave -40 -KPX Ucircumflex Amacron -40 -KPX Ucircumflex Aogonek -40 -KPX Ucircumflex Aring -40 -KPX Ucircumflex Atilde -40 -KPX Ucircumflex comma -40 -KPX Ucircumflex period -40 -KPX Udieresis A -40 -KPX Udieresis Aacute -40 -KPX Udieresis Abreve -40 -KPX Udieresis Acircumflex -40 -KPX Udieresis Adieresis -40 -KPX Udieresis Agrave -40 -KPX Udieresis Amacron -40 -KPX Udieresis Aogonek -40 -KPX Udieresis Aring -40 -KPX Udieresis Atilde -40 -KPX Udieresis comma -40 -KPX Udieresis period -40 -KPX Ugrave A -40 -KPX Ugrave Aacute -40 -KPX Ugrave Abreve -40 -KPX Ugrave Acircumflex -40 -KPX Ugrave Adieresis -40 -KPX Ugrave Agrave -40 -KPX Ugrave Amacron -40 -KPX Ugrave Aogonek -40 -KPX Ugrave Aring -40 -KPX Ugrave Atilde -40 -KPX Ugrave comma -40 -KPX Ugrave period -40 -KPX Uhungarumlaut A -40 -KPX Uhungarumlaut Aacute -40 -KPX Uhungarumlaut Abreve -40 -KPX Uhungarumlaut Acircumflex -40 -KPX Uhungarumlaut Adieresis -40 -KPX Uhungarumlaut Agrave -40 -KPX Uhungarumlaut Amacron -40 -KPX Uhungarumlaut Aogonek -40 -KPX Uhungarumlaut Aring -40 -KPX Uhungarumlaut Atilde -40 -KPX Uhungarumlaut comma -40 -KPX Uhungarumlaut period -40 -KPX Umacron A -40 -KPX Umacron Aacute -40 -KPX Umacron Abreve -40 -KPX Umacron Acircumflex -40 -KPX Umacron Adieresis -40 -KPX Umacron Agrave -40 -KPX Umacron Amacron -40 -KPX Umacron Aogonek -40 -KPX Umacron Aring -40 -KPX Umacron Atilde -40 -KPX Umacron comma -40 -KPX Umacron period -40 -KPX Uogonek A -40 -KPX Uogonek Aacute -40 -KPX Uogonek Abreve -40 -KPX Uogonek Acircumflex -40 -KPX Uogonek Adieresis -40 -KPX Uogonek Agrave -40 -KPX Uogonek Amacron -40 -KPX Uogonek Aogonek -40 -KPX Uogonek Aring -40 -KPX Uogonek Atilde -40 -KPX Uogonek comma -40 -KPX Uogonek period -40 -KPX Uring A -40 -KPX Uring Aacute -40 -KPX Uring Abreve -40 -KPX Uring Acircumflex -40 -KPX Uring Adieresis -40 -KPX Uring Agrave -40 -KPX Uring Amacron -40 -KPX Uring Aogonek -40 -KPX Uring Aring -40 -KPX Uring Atilde -40 -KPX Uring comma -40 -KPX Uring period -40 -KPX V A -80 -KPX V Aacute -80 -KPX V Abreve -80 -KPX V Acircumflex -80 -KPX V Adieresis -80 -KPX V Agrave -80 -KPX V Amacron -80 -KPX V Aogonek -80 -KPX V Aring -80 -KPX V Atilde -80 -KPX V G -40 -KPX V Gbreve -40 -KPX V Gcommaaccent -40 -KPX V O -40 -KPX V Oacute -40 -KPX V Ocircumflex -40 -KPX V Odieresis -40 -KPX V Ograve -40 -KPX V Ohungarumlaut -40 -KPX V Omacron -40 -KPX V Oslash -40 -KPX V Otilde -40 -KPX V a -70 -KPX V aacute -70 -KPX V abreve -70 -KPX V acircumflex -70 -KPX V adieresis -70 -KPX V agrave -70 -KPX V amacron -70 -KPX V aogonek -70 -KPX V aring -70 -KPX V atilde -70 -KPX V colon -40 -KPX V comma -125 -KPX V e -80 -KPX V eacute -80 -KPX V ecaron -80 -KPX V ecircumflex -80 -KPX V edieresis -80 -KPX V edotaccent -80 -KPX V egrave -80 -KPX V emacron -80 -KPX V eogonek -80 -KPX V hyphen -80 -KPX V o -80 -KPX V oacute -80 -KPX V ocircumflex -80 -KPX V odieresis -80 -KPX V ograve -80 -KPX V ohungarumlaut -80 -KPX V omacron -80 -KPX V oslash -80 -KPX V otilde -80 -KPX V period -125 -KPX V semicolon -40 -KPX V u -70 -KPX V uacute -70 -KPX V ucircumflex -70 -KPX V udieresis -70 -KPX V ugrave -70 -KPX V uhungarumlaut -70 -KPX V umacron -70 -KPX V uogonek -70 -KPX V uring -70 -KPX W A -50 -KPX W Aacute -50 -KPX W Abreve -50 -KPX W Acircumflex -50 -KPX W Adieresis -50 -KPX W Agrave -50 -KPX W Amacron -50 -KPX W Aogonek -50 -KPX W Aring -50 -KPX W Atilde -50 -KPX W O -20 -KPX W Oacute -20 -KPX W Ocircumflex -20 -KPX W Odieresis -20 -KPX W Ograve -20 -KPX W Ohungarumlaut -20 -KPX W Omacron -20 -KPX W Oslash -20 -KPX W Otilde -20 -KPX W a -40 -KPX W aacute -40 -KPX W abreve -40 -KPX W acircumflex -40 -KPX W adieresis -40 -KPX W agrave -40 -KPX W amacron -40 -KPX W aogonek -40 -KPX W aring -40 -KPX W atilde -40 -KPX W comma -80 -KPX W e -30 -KPX W eacute -30 -KPX W ecaron -30 -KPX W ecircumflex -30 -KPX W edieresis -30 -KPX W edotaccent -30 -KPX W egrave -30 -KPX W emacron -30 -KPX W eogonek -30 -KPX W hyphen -40 -KPX W o -30 -KPX W oacute -30 -KPX W ocircumflex -30 -KPX W odieresis -30 -KPX W ograve -30 -KPX W ohungarumlaut -30 -KPX W omacron -30 -KPX W oslash -30 -KPX W otilde -30 -KPX W period -80 -KPX W u -30 -KPX W uacute -30 -KPX W ucircumflex -30 -KPX W udieresis -30 -KPX W ugrave -30 -KPX W uhungarumlaut -30 -KPX W umacron -30 -KPX W uogonek -30 -KPX W uring -30 -KPX W y -20 -KPX W yacute -20 -KPX W ydieresis -20 -KPX Y A -110 -KPX Y Aacute -110 -KPX Y Abreve -110 -KPX Y Acircumflex -110 -KPX Y Adieresis -110 -KPX Y Agrave -110 -KPX Y Amacron -110 -KPX Y Aogonek -110 -KPX Y Aring -110 -KPX Y Atilde -110 -KPX Y O -85 -KPX Y Oacute -85 -KPX Y Ocircumflex -85 -KPX Y Odieresis -85 -KPX Y Ograve -85 -KPX Y Ohungarumlaut -85 -KPX Y Omacron -85 -KPX Y Oslash -85 -KPX Y Otilde -85 -KPX Y a -140 -KPX Y aacute -140 -KPX Y abreve -70 -KPX Y acircumflex -140 -KPX Y adieresis -140 -KPX Y agrave -140 -KPX Y amacron -70 -KPX Y aogonek -140 -KPX Y aring -140 -KPX Y atilde -140 -KPX Y colon -60 -KPX Y comma -140 -KPX Y e -140 -KPX Y eacute -140 -KPX Y ecaron -140 -KPX Y ecircumflex -140 -KPX Y edieresis -140 -KPX Y edotaccent -140 -KPX Y egrave -140 -KPX Y emacron -70 -KPX Y eogonek -140 -KPX Y hyphen -140 -KPX Y i -20 -KPX Y iacute -20 -KPX Y iogonek -20 -KPX Y o -140 -KPX Y oacute -140 -KPX Y ocircumflex -140 -KPX Y odieresis -140 -KPX Y ograve -140 -KPX Y ohungarumlaut -140 -KPX Y omacron -140 -KPX Y oslash -140 -KPX Y otilde -140 -KPX Y period -140 -KPX Y semicolon -60 -KPX Y u -110 -KPX Y uacute -110 -KPX Y ucircumflex -110 -KPX Y udieresis -110 -KPX Y ugrave -110 -KPX Y uhungarumlaut -110 -KPX Y umacron -110 -KPX Y uogonek -110 -KPX Y uring -110 -KPX Yacute A -110 -KPX Yacute Aacute -110 -KPX Yacute Abreve -110 -KPX Yacute Acircumflex -110 -KPX Yacute Adieresis -110 -KPX Yacute Agrave -110 -KPX Yacute Amacron -110 -KPX Yacute Aogonek -110 -KPX Yacute Aring -110 -KPX Yacute Atilde -110 -KPX Yacute O -85 -KPX Yacute Oacute -85 -KPX Yacute Ocircumflex -85 -KPX Yacute Odieresis -85 -KPX Yacute Ograve -85 -KPX Yacute Ohungarumlaut -85 -KPX Yacute Omacron -85 -KPX Yacute Oslash -85 -KPX Yacute Otilde -85 -KPX Yacute a -140 -KPX Yacute aacute -140 -KPX Yacute abreve -70 -KPX Yacute acircumflex -140 -KPX Yacute adieresis -140 -KPX Yacute agrave -140 -KPX Yacute amacron -70 -KPX Yacute aogonek -140 -KPX Yacute aring -140 -KPX Yacute atilde -70 -KPX Yacute colon -60 -KPX Yacute comma -140 -KPX Yacute e -140 -KPX Yacute eacute -140 -KPX Yacute ecaron -140 -KPX Yacute ecircumflex -140 -KPX Yacute edieresis -140 -KPX Yacute edotaccent -140 -KPX Yacute egrave -140 -KPX Yacute emacron -70 -KPX Yacute eogonek -140 -KPX Yacute hyphen -140 -KPX Yacute i -20 -KPX Yacute iacute -20 -KPX Yacute iogonek -20 -KPX Yacute o -140 -KPX Yacute oacute -140 -KPX Yacute ocircumflex -140 -KPX Yacute odieresis -140 -KPX Yacute ograve -140 -KPX Yacute ohungarumlaut -140 -KPX Yacute omacron -70 -KPX Yacute oslash -140 -KPX Yacute otilde -140 -KPX Yacute period -140 -KPX Yacute semicolon -60 -KPX Yacute u -110 -KPX Yacute uacute -110 -KPX Yacute ucircumflex -110 -KPX Yacute udieresis -110 -KPX Yacute ugrave -110 -KPX Yacute uhungarumlaut -110 -KPX Yacute umacron -110 -KPX Yacute uogonek -110 -KPX Yacute uring -110 -KPX Ydieresis A -110 -KPX Ydieresis Aacute -110 -KPX Ydieresis Abreve -110 -KPX Ydieresis Acircumflex -110 -KPX Ydieresis Adieresis -110 -KPX Ydieresis Agrave -110 -KPX Ydieresis Amacron -110 -KPX Ydieresis Aogonek -110 -KPX Ydieresis Aring -110 -KPX Ydieresis Atilde -110 -KPX Ydieresis O -85 -KPX Ydieresis Oacute -85 -KPX Ydieresis Ocircumflex -85 -KPX Ydieresis Odieresis -85 -KPX Ydieresis Ograve -85 -KPX Ydieresis Ohungarumlaut -85 -KPX Ydieresis Omacron -85 -KPX Ydieresis Oslash -85 -KPX Ydieresis Otilde -85 -KPX Ydieresis a -140 -KPX Ydieresis aacute -140 -KPX Ydieresis abreve -70 -KPX Ydieresis acircumflex -140 -KPX Ydieresis adieresis -140 -KPX Ydieresis agrave -140 -KPX Ydieresis amacron -70 -KPX Ydieresis aogonek -140 -KPX Ydieresis aring -140 -KPX Ydieresis atilde -70 -KPX Ydieresis colon -60 -KPX Ydieresis comma -140 -KPX Ydieresis e -140 -KPX Ydieresis eacute -140 -KPX Ydieresis ecaron -140 -KPX Ydieresis ecircumflex -140 -KPX Ydieresis edieresis -140 -KPX Ydieresis edotaccent -140 -KPX Ydieresis egrave -140 -KPX Ydieresis emacron -70 -KPX Ydieresis eogonek -140 -KPX Ydieresis hyphen -140 -KPX Ydieresis i -20 -KPX Ydieresis iacute -20 -KPX Ydieresis iogonek -20 -KPX Ydieresis o -140 -KPX Ydieresis oacute -140 -KPX Ydieresis ocircumflex -140 -KPX Ydieresis odieresis -140 -KPX Ydieresis ograve -140 -KPX Ydieresis ohungarumlaut -140 -KPX Ydieresis omacron -140 -KPX Ydieresis oslash -140 -KPX Ydieresis otilde -140 -KPX Ydieresis period -140 -KPX Ydieresis semicolon -60 -KPX Ydieresis u -110 -KPX Ydieresis uacute -110 -KPX Ydieresis ucircumflex -110 -KPX Ydieresis udieresis -110 -KPX Ydieresis ugrave -110 -KPX Ydieresis uhungarumlaut -110 -KPX Ydieresis umacron -110 -KPX Ydieresis uogonek -110 -KPX Ydieresis uring -110 -KPX a v -20 -KPX a w -20 -KPX a y -30 -KPX a yacute -30 -KPX a ydieresis -30 -KPX aacute v -20 -KPX aacute w -20 -KPX aacute y -30 -KPX aacute yacute -30 -KPX aacute ydieresis -30 -KPX abreve v -20 -KPX abreve w -20 -KPX abreve y -30 -KPX abreve yacute -30 -KPX abreve ydieresis -30 -KPX acircumflex v -20 -KPX acircumflex w -20 -KPX acircumflex y -30 -KPX acircumflex yacute -30 -KPX acircumflex ydieresis -30 -KPX adieresis v -20 -KPX adieresis w -20 -KPX adieresis y -30 -KPX adieresis yacute -30 -KPX adieresis ydieresis -30 -KPX agrave v -20 -KPX agrave w -20 -KPX agrave y -30 -KPX agrave yacute -30 -KPX agrave ydieresis -30 -KPX amacron v -20 -KPX amacron w -20 -KPX amacron y -30 -KPX amacron yacute -30 -KPX amacron ydieresis -30 -KPX aogonek v -20 -KPX aogonek w -20 -KPX aogonek y -30 -KPX aogonek yacute -30 -KPX aogonek ydieresis -30 -KPX aring v -20 -KPX aring w -20 -KPX aring y -30 -KPX aring yacute -30 -KPX aring ydieresis -30 -KPX atilde v -20 -KPX atilde w -20 -KPX atilde y -30 -KPX atilde yacute -30 -KPX atilde ydieresis -30 -KPX b b -10 -KPX b comma -40 -KPX b l -20 -KPX b lacute -20 -KPX b lcommaaccent -20 -KPX b lslash -20 -KPX b period -40 -KPX b u -20 -KPX b uacute -20 -KPX b ucircumflex -20 -KPX b udieresis -20 -KPX b ugrave -20 -KPX b uhungarumlaut -20 -KPX b umacron -20 -KPX b uogonek -20 -KPX b uring -20 -KPX b v -20 -KPX b y -20 -KPX b yacute -20 -KPX b ydieresis -20 -KPX c comma -15 -KPX c k -20 -KPX c kcommaaccent -20 -KPX cacute comma -15 -KPX cacute k -20 -KPX cacute kcommaaccent -20 -KPX ccaron comma -15 -KPX ccaron k -20 -KPX ccaron kcommaaccent -20 -KPX ccedilla comma -15 -KPX ccedilla k -20 -KPX ccedilla kcommaaccent -20 -KPX colon space -50 -KPX comma quotedblright -100 -KPX comma quoteright -100 -KPX e comma -15 -KPX e period -15 -KPX e v -30 -KPX e w -20 -KPX e x -30 -KPX e y -20 -KPX e yacute -20 -KPX e ydieresis -20 -KPX eacute comma -15 -KPX eacute period -15 -KPX eacute v -30 -KPX eacute w -20 -KPX eacute x -30 -KPX eacute y -20 -KPX eacute yacute -20 -KPX eacute ydieresis -20 -KPX ecaron comma -15 -KPX ecaron period -15 -KPX ecaron v -30 -KPX ecaron w -20 -KPX ecaron x -30 -KPX ecaron y -20 -KPX ecaron yacute -20 -KPX ecaron ydieresis -20 -KPX ecircumflex comma -15 -KPX ecircumflex period -15 -KPX ecircumflex v -30 -KPX ecircumflex w -20 -KPX ecircumflex x -30 -KPX ecircumflex y -20 -KPX ecircumflex yacute -20 -KPX ecircumflex ydieresis -20 -KPX edieresis comma -15 -KPX edieresis period -15 -KPX edieresis v -30 -KPX edieresis w -20 -KPX edieresis x -30 -KPX edieresis y -20 -KPX edieresis yacute -20 -KPX edieresis ydieresis -20 -KPX edotaccent comma -15 -KPX edotaccent period -15 -KPX edotaccent v -30 -KPX edotaccent w -20 -KPX edotaccent x -30 -KPX edotaccent y -20 -KPX edotaccent yacute -20 -KPX edotaccent ydieresis -20 -KPX egrave comma -15 -KPX egrave period -15 -KPX egrave v -30 -KPX egrave w -20 -KPX egrave x -30 -KPX egrave y -20 -KPX egrave yacute -20 -KPX egrave ydieresis -20 -KPX emacron comma -15 -KPX emacron period -15 -KPX emacron v -30 -KPX emacron w -20 -KPX emacron x -30 -KPX emacron y -20 -KPX emacron yacute -20 -KPX emacron ydieresis -20 -KPX eogonek comma -15 -KPX eogonek period -15 -KPX eogonek v -30 -KPX eogonek w -20 -KPX eogonek x -30 -KPX eogonek y -20 -KPX eogonek yacute -20 -KPX eogonek ydieresis -20 -KPX f a -30 -KPX f aacute -30 -KPX f abreve -30 -KPX f acircumflex -30 -KPX f adieresis -30 -KPX f agrave -30 -KPX f amacron -30 -KPX f aogonek -30 -KPX f aring -30 -KPX f atilde -30 -KPX f comma -30 -KPX f dotlessi -28 -KPX f e -30 -KPX f eacute -30 -KPX f ecaron -30 -KPX f ecircumflex -30 -KPX f edieresis -30 -KPX f edotaccent -30 -KPX f egrave -30 -KPX f emacron -30 -KPX f eogonek -30 -KPX f o -30 -KPX f oacute -30 -KPX f ocircumflex -30 -KPX f odieresis -30 -KPX f ograve -30 -KPX f ohungarumlaut -30 -KPX f omacron -30 -KPX f oslash -30 -KPX f otilde -30 -KPX f period -30 -KPX f quotedblright 60 -KPX f quoteright 50 -KPX g r -10 -KPX g racute -10 -KPX g rcaron -10 -KPX g rcommaaccent -10 -KPX gbreve r -10 -KPX gbreve racute -10 -KPX gbreve rcaron -10 -KPX gbreve rcommaaccent -10 -KPX gcommaaccent r -10 -KPX gcommaaccent racute -10 -KPX gcommaaccent rcaron -10 -KPX gcommaaccent rcommaaccent -10 -KPX h y -30 -KPX h yacute -30 -KPX h ydieresis -30 -KPX k e -20 -KPX k eacute -20 -KPX k ecaron -20 -KPX k ecircumflex -20 -KPX k edieresis -20 -KPX k edotaccent -20 -KPX k egrave -20 -KPX k emacron -20 -KPX k eogonek -20 -KPX k o -20 -KPX k oacute -20 -KPX k ocircumflex -20 -KPX k odieresis -20 -KPX k ograve -20 -KPX k ohungarumlaut -20 -KPX k omacron -20 -KPX k oslash -20 -KPX k otilde -20 -KPX kcommaaccent e -20 -KPX kcommaaccent eacute -20 -KPX kcommaaccent ecaron -20 -KPX kcommaaccent ecircumflex -20 -KPX kcommaaccent edieresis -20 -KPX kcommaaccent edotaccent -20 -KPX kcommaaccent egrave -20 -KPX kcommaaccent emacron -20 -KPX kcommaaccent eogonek -20 -KPX kcommaaccent o -20 -KPX kcommaaccent oacute -20 -KPX kcommaaccent ocircumflex -20 -KPX kcommaaccent odieresis -20 -KPX kcommaaccent ograve -20 -KPX kcommaaccent ohungarumlaut -20 -KPX kcommaaccent omacron -20 -KPX kcommaaccent oslash -20 -KPX kcommaaccent otilde -20 -KPX m u -10 -KPX m uacute -10 -KPX m ucircumflex -10 -KPX m udieresis -10 -KPX m ugrave -10 -KPX m uhungarumlaut -10 -KPX m umacron -10 -KPX m uogonek -10 -KPX m uring -10 -KPX m y -15 -KPX m yacute -15 -KPX m ydieresis -15 -KPX n u -10 -KPX n uacute -10 -KPX n ucircumflex -10 -KPX n udieresis -10 -KPX n ugrave -10 -KPX n uhungarumlaut -10 -KPX n umacron -10 -KPX n uogonek -10 -KPX n uring -10 -KPX n v -20 -KPX n y -15 -KPX n yacute -15 -KPX n ydieresis -15 -KPX nacute u -10 -KPX nacute uacute -10 -KPX nacute ucircumflex -10 -KPX nacute udieresis -10 -KPX nacute ugrave -10 -KPX nacute uhungarumlaut -10 -KPX nacute umacron -10 -KPX nacute uogonek -10 -KPX nacute uring -10 -KPX nacute v -20 -KPX nacute y -15 -KPX nacute yacute -15 -KPX nacute ydieresis -15 -KPX ncaron u -10 -KPX ncaron uacute -10 -KPX ncaron ucircumflex -10 -KPX ncaron udieresis -10 -KPX ncaron ugrave -10 -KPX ncaron uhungarumlaut -10 -KPX ncaron umacron -10 -KPX ncaron uogonek -10 -KPX ncaron uring -10 -KPX ncaron v -20 -KPX ncaron y -15 -KPX ncaron yacute -15 -KPX ncaron ydieresis -15 -KPX ncommaaccent u -10 -KPX ncommaaccent uacute -10 -KPX ncommaaccent ucircumflex -10 -KPX ncommaaccent udieresis -10 -KPX ncommaaccent ugrave -10 -KPX ncommaaccent uhungarumlaut -10 -KPX ncommaaccent umacron -10 -KPX ncommaaccent uogonek -10 -KPX ncommaaccent uring -10 -KPX ncommaaccent v -20 -KPX ncommaaccent y -15 -KPX ncommaaccent yacute -15 -KPX ncommaaccent ydieresis -15 -KPX ntilde u -10 -KPX ntilde uacute -10 -KPX ntilde ucircumflex -10 -KPX ntilde udieresis -10 -KPX ntilde ugrave -10 -KPX ntilde uhungarumlaut -10 -KPX ntilde umacron -10 -KPX ntilde uogonek -10 -KPX ntilde uring -10 -KPX ntilde v -20 -KPX ntilde y -15 -KPX ntilde yacute -15 -KPX ntilde ydieresis -15 -KPX o comma -40 -KPX o period -40 -KPX o v -15 -KPX o w -15 -KPX o x -30 -KPX o y -30 -KPX o yacute -30 -KPX o ydieresis -30 -KPX oacute comma -40 -KPX oacute period -40 -KPX oacute v -15 -KPX oacute w -15 -KPX oacute x -30 -KPX oacute y -30 -KPX oacute yacute -30 -KPX oacute ydieresis -30 -KPX ocircumflex comma -40 -KPX ocircumflex period -40 -KPX ocircumflex v -15 -KPX ocircumflex w -15 -KPX ocircumflex x -30 -KPX ocircumflex y -30 -KPX ocircumflex yacute -30 -KPX ocircumflex ydieresis -30 -KPX odieresis comma -40 -KPX odieresis period -40 -KPX odieresis v -15 -KPX odieresis w -15 -KPX odieresis x -30 -KPX odieresis y -30 -KPX odieresis yacute -30 -KPX odieresis ydieresis -30 -KPX ograve comma -40 -KPX ograve period -40 -KPX ograve v -15 -KPX ograve w -15 -KPX ograve x -30 -KPX ograve y -30 -KPX ograve yacute -30 -KPX ograve ydieresis -30 -KPX ohungarumlaut comma -40 -KPX ohungarumlaut period -40 -KPX ohungarumlaut v -15 -KPX ohungarumlaut w -15 -KPX ohungarumlaut x -30 -KPX ohungarumlaut y -30 -KPX ohungarumlaut yacute -30 -KPX ohungarumlaut ydieresis -30 -KPX omacron comma -40 -KPX omacron period -40 -KPX omacron v -15 -KPX omacron w -15 -KPX omacron x -30 -KPX omacron y -30 -KPX omacron yacute -30 -KPX omacron ydieresis -30 -KPX oslash a -55 -KPX oslash aacute -55 -KPX oslash abreve -55 -KPX oslash acircumflex -55 -KPX oslash adieresis -55 -KPX oslash agrave -55 -KPX oslash amacron -55 -KPX oslash aogonek -55 -KPX oslash aring -55 -KPX oslash atilde -55 -KPX oslash b -55 -KPX oslash c -55 -KPX oslash cacute -55 -KPX oslash ccaron -55 -KPX oslash ccedilla -55 -KPX oslash comma -95 -KPX oslash d -55 -KPX oslash dcroat -55 -KPX oslash e -55 -KPX oslash eacute -55 -KPX oslash ecaron -55 -KPX oslash ecircumflex -55 -KPX oslash edieresis -55 -KPX oslash edotaccent -55 -KPX oslash egrave -55 -KPX oslash emacron -55 -KPX oslash eogonek -55 -KPX oslash f -55 -KPX oslash g -55 -KPX oslash gbreve -55 -KPX oslash gcommaaccent -55 -KPX oslash h -55 -KPX oslash i -55 -KPX oslash iacute -55 -KPX oslash icircumflex -55 -KPX oslash idieresis -55 -KPX oslash igrave -55 -KPX oslash imacron -55 -KPX oslash iogonek -55 -KPX oslash j -55 -KPX oslash k -55 -KPX oslash kcommaaccent -55 -KPX oslash l -55 -KPX oslash lacute -55 -KPX oslash lcommaaccent -55 -KPX oslash lslash -55 -KPX oslash m -55 -KPX oslash n -55 -KPX oslash nacute -55 -KPX oslash ncaron -55 -KPX oslash ncommaaccent -55 -KPX oslash ntilde -55 -KPX oslash o -55 -KPX oslash oacute -55 -KPX oslash ocircumflex -55 -KPX oslash odieresis -55 -KPX oslash ograve -55 -KPX oslash ohungarumlaut -55 -KPX oslash omacron -55 -KPX oslash oslash -55 -KPX oslash otilde -55 -KPX oslash p -55 -KPX oslash period -95 -KPX oslash q -55 -KPX oslash r -55 -KPX oslash racute -55 -KPX oslash rcaron -55 -KPX oslash rcommaaccent -55 -KPX oslash s -55 -KPX oslash sacute -55 -KPX oslash scaron -55 -KPX oslash scedilla -55 -KPX oslash scommaaccent -55 -KPX oslash t -55 -KPX oslash tcommaaccent -55 -KPX oslash u -55 -KPX oslash uacute -55 -KPX oslash ucircumflex -55 -KPX oslash udieresis -55 -KPX oslash ugrave -55 -KPX oslash uhungarumlaut -55 -KPX oslash umacron -55 -KPX oslash uogonek -55 -KPX oslash uring -55 -KPX oslash v -70 -KPX oslash w -70 -KPX oslash x -85 -KPX oslash y -70 -KPX oslash yacute -70 -KPX oslash ydieresis -70 -KPX oslash z -55 -KPX oslash zacute -55 -KPX oslash zcaron -55 -KPX oslash zdotaccent -55 -KPX otilde comma -40 -KPX otilde period -40 -KPX otilde v -15 -KPX otilde w -15 -KPX otilde x -30 -KPX otilde y -30 -KPX otilde yacute -30 -KPX otilde ydieresis -30 -KPX p comma -35 -KPX p period -35 -KPX p y -30 -KPX p yacute -30 -KPX p ydieresis -30 -KPX period quotedblright -100 -KPX period quoteright -100 -KPX period space -60 -KPX quotedblright space -40 -KPX quoteleft quoteleft -57 -KPX quoteright d -50 -KPX quoteright dcroat -50 -KPX quoteright quoteright -57 -KPX quoteright r -50 -KPX quoteright racute -50 -KPX quoteright rcaron -50 -KPX quoteright rcommaaccent -50 -KPX quoteright s -50 -KPX quoteright sacute -50 -KPX quoteright scaron -50 -KPX quoteright scedilla -50 -KPX quoteright scommaaccent -50 -KPX quoteright space -70 -KPX r a -10 -KPX r aacute -10 -KPX r abreve -10 -KPX r acircumflex -10 -KPX r adieresis -10 -KPX r agrave -10 -KPX r amacron -10 -KPX r aogonek -10 -KPX r aring -10 -KPX r atilde -10 -KPX r colon 30 -KPX r comma -50 -KPX r i 15 -KPX r iacute 15 -KPX r icircumflex 15 -KPX r idieresis 15 -KPX r igrave 15 -KPX r imacron 15 -KPX r iogonek 15 -KPX r k 15 -KPX r kcommaaccent 15 -KPX r l 15 -KPX r lacute 15 -KPX r lcommaaccent 15 -KPX r lslash 15 -KPX r m 25 -KPX r n 25 -KPX r nacute 25 -KPX r ncaron 25 -KPX r ncommaaccent 25 -KPX r ntilde 25 -KPX r p 30 -KPX r period -50 -KPX r semicolon 30 -KPX r t 40 -KPX r tcommaaccent 40 -KPX r u 15 -KPX r uacute 15 -KPX r ucircumflex 15 -KPX r udieresis 15 -KPX r ugrave 15 -KPX r uhungarumlaut 15 -KPX r umacron 15 -KPX r uogonek 15 -KPX r uring 15 -KPX r v 30 -KPX r y 30 -KPX r yacute 30 -KPX r ydieresis 30 -KPX racute a -10 -KPX racute aacute -10 -KPX racute abreve -10 -KPX racute acircumflex -10 -KPX racute adieresis -10 -KPX racute agrave -10 -KPX racute amacron -10 -KPX racute aogonek -10 -KPX racute aring -10 -KPX racute atilde -10 -KPX racute colon 30 -KPX racute comma -50 -KPX racute i 15 -KPX racute iacute 15 -KPX racute icircumflex 15 -KPX racute idieresis 15 -KPX racute igrave 15 -KPX racute imacron 15 -KPX racute iogonek 15 -KPX racute k 15 -KPX racute kcommaaccent 15 -KPX racute l 15 -KPX racute lacute 15 -KPX racute lcommaaccent 15 -KPX racute lslash 15 -KPX racute m 25 -KPX racute n 25 -KPX racute nacute 25 -KPX racute ncaron 25 -KPX racute ncommaaccent 25 -KPX racute ntilde 25 -KPX racute p 30 -KPX racute period -50 -KPX racute semicolon 30 -KPX racute t 40 -KPX racute tcommaaccent 40 -KPX racute u 15 -KPX racute uacute 15 -KPX racute ucircumflex 15 -KPX racute udieresis 15 -KPX racute ugrave 15 -KPX racute uhungarumlaut 15 -KPX racute umacron 15 -KPX racute uogonek 15 -KPX racute uring 15 -KPX racute v 30 -KPX racute y 30 -KPX racute yacute 30 -KPX racute ydieresis 30 -KPX rcaron a -10 -KPX rcaron aacute -10 -KPX rcaron abreve -10 -KPX rcaron acircumflex -10 -KPX rcaron adieresis -10 -KPX rcaron agrave -10 -KPX rcaron amacron -10 -KPX rcaron aogonek -10 -KPX rcaron aring -10 -KPX rcaron atilde -10 -KPX rcaron colon 30 -KPX rcaron comma -50 -KPX rcaron i 15 -KPX rcaron iacute 15 -KPX rcaron icircumflex 15 -KPX rcaron idieresis 15 -KPX rcaron igrave 15 -KPX rcaron imacron 15 -KPX rcaron iogonek 15 -KPX rcaron k 15 -KPX rcaron kcommaaccent 15 -KPX rcaron l 15 -KPX rcaron lacute 15 -KPX rcaron lcommaaccent 15 -KPX rcaron lslash 15 -KPX rcaron m 25 -KPX rcaron n 25 -KPX rcaron nacute 25 -KPX rcaron ncaron 25 -KPX rcaron ncommaaccent 25 -KPX rcaron ntilde 25 -KPX rcaron p 30 -KPX rcaron period -50 -KPX rcaron semicolon 30 -KPX rcaron t 40 -KPX rcaron tcommaaccent 40 -KPX rcaron u 15 -KPX rcaron uacute 15 -KPX rcaron ucircumflex 15 -KPX rcaron udieresis 15 -KPX rcaron ugrave 15 -KPX rcaron uhungarumlaut 15 -KPX rcaron umacron 15 -KPX rcaron uogonek 15 -KPX rcaron uring 15 -KPX rcaron v 30 -KPX rcaron y 30 -KPX rcaron yacute 30 -KPX rcaron ydieresis 30 -KPX rcommaaccent a -10 -KPX rcommaaccent aacute -10 -KPX rcommaaccent abreve -10 -KPX rcommaaccent acircumflex -10 -KPX rcommaaccent adieresis -10 -KPX rcommaaccent agrave -10 -KPX rcommaaccent amacron -10 -KPX rcommaaccent aogonek -10 -KPX rcommaaccent aring -10 -KPX rcommaaccent atilde -10 -KPX rcommaaccent colon 30 -KPX rcommaaccent comma -50 -KPX rcommaaccent i 15 -KPX rcommaaccent iacute 15 -KPX rcommaaccent icircumflex 15 -KPX rcommaaccent idieresis 15 -KPX rcommaaccent igrave 15 -KPX rcommaaccent imacron 15 -KPX rcommaaccent iogonek 15 -KPX rcommaaccent k 15 -KPX rcommaaccent kcommaaccent 15 -KPX rcommaaccent l 15 -KPX rcommaaccent lacute 15 -KPX rcommaaccent lcommaaccent 15 -KPX rcommaaccent lslash 15 -KPX rcommaaccent m 25 -KPX rcommaaccent n 25 -KPX rcommaaccent nacute 25 -KPX rcommaaccent ncaron 25 -KPX rcommaaccent ncommaaccent 25 -KPX rcommaaccent ntilde 25 -KPX rcommaaccent p 30 -KPX rcommaaccent period -50 -KPX rcommaaccent semicolon 30 -KPX rcommaaccent t 40 -KPX rcommaaccent tcommaaccent 40 -KPX rcommaaccent u 15 -KPX rcommaaccent uacute 15 -KPX rcommaaccent ucircumflex 15 -KPX rcommaaccent udieresis 15 -KPX rcommaaccent ugrave 15 -KPX rcommaaccent uhungarumlaut 15 -KPX rcommaaccent umacron 15 -KPX rcommaaccent uogonek 15 -KPX rcommaaccent uring 15 -KPX rcommaaccent v 30 -KPX rcommaaccent y 30 -KPX rcommaaccent yacute 30 -KPX rcommaaccent ydieresis 30 -KPX s comma -15 -KPX s period -15 -KPX s w -30 -KPX sacute comma -15 -KPX sacute period -15 -KPX sacute w -30 -KPX scaron comma -15 -KPX scaron period -15 -KPX scaron w -30 -KPX scedilla comma -15 -KPX scedilla period -15 -KPX scedilla w -30 -KPX scommaaccent comma -15 -KPX scommaaccent period -15 -KPX scommaaccent w -30 -KPX semicolon space -50 -KPX space T -50 -KPX space Tcaron -50 -KPX space Tcommaaccent -50 -KPX space V -50 -KPX space W -40 -KPX space Y -90 -KPX space Yacute -90 -KPX space Ydieresis -90 -KPX space quotedblleft -30 -KPX space quoteleft -60 -KPX v a -25 -KPX v aacute -25 -KPX v abreve -25 -KPX v acircumflex -25 -KPX v adieresis -25 -KPX v agrave -25 -KPX v amacron -25 -KPX v aogonek -25 -KPX v aring -25 -KPX v atilde -25 -KPX v comma -80 -KPX v e -25 -KPX v eacute -25 -KPX v ecaron -25 -KPX v ecircumflex -25 -KPX v edieresis -25 -KPX v edotaccent -25 -KPX v egrave -25 -KPX v emacron -25 -KPX v eogonek -25 -KPX v o -25 -KPX v oacute -25 -KPX v ocircumflex -25 -KPX v odieresis -25 -KPX v ograve -25 -KPX v ohungarumlaut -25 -KPX v omacron -25 -KPX v oslash -25 -KPX v otilde -25 -KPX v period -80 -KPX w a -15 -KPX w aacute -15 -KPX w abreve -15 -KPX w acircumflex -15 -KPX w adieresis -15 -KPX w agrave -15 -KPX w amacron -15 -KPX w aogonek -15 -KPX w aring -15 -KPX w atilde -15 -KPX w comma -60 -KPX w e -10 -KPX w eacute -10 -KPX w ecaron -10 -KPX w ecircumflex -10 -KPX w edieresis -10 -KPX w edotaccent -10 -KPX w egrave -10 -KPX w emacron -10 -KPX w eogonek -10 -KPX w o -10 -KPX w oacute -10 -KPX w ocircumflex -10 -KPX w odieresis -10 -KPX w ograve -10 -KPX w ohungarumlaut -10 -KPX w omacron -10 -KPX w oslash -10 -KPX w otilde -10 -KPX w period -60 -KPX x e -30 -KPX x eacute -30 -KPX x ecaron -30 -KPX x ecircumflex -30 -KPX x edieresis -30 -KPX x edotaccent -30 -KPX x egrave -30 -KPX x emacron -30 -KPX x eogonek -30 -KPX y a -20 -KPX y aacute -20 -KPX y abreve -20 -KPX y acircumflex -20 -KPX y adieresis -20 -KPX y agrave -20 -KPX y amacron -20 -KPX y aogonek -20 -KPX y aring -20 -KPX y atilde -20 -KPX y comma -100 -KPX y e -20 -KPX y eacute -20 -KPX y ecaron -20 -KPX y ecircumflex -20 -KPX y edieresis -20 -KPX y edotaccent -20 -KPX y egrave -20 -KPX y emacron -20 -KPX y eogonek -20 -KPX y o -20 -KPX y oacute -20 -KPX y ocircumflex -20 -KPX y odieresis -20 -KPX y ograve -20 -KPX y ohungarumlaut -20 -KPX y omacron -20 -KPX y oslash -20 -KPX y otilde -20 -KPX y period -100 -KPX yacute a -20 -KPX yacute aacute -20 -KPX yacute abreve -20 -KPX yacute acircumflex -20 -KPX yacute adieresis -20 -KPX yacute agrave -20 -KPX yacute amacron -20 -KPX yacute aogonek -20 -KPX yacute aring -20 -KPX yacute atilde -20 -KPX yacute comma -100 -KPX yacute e -20 -KPX yacute eacute -20 -KPX yacute ecaron -20 -KPX yacute ecircumflex -20 -KPX yacute edieresis -20 -KPX yacute edotaccent -20 -KPX yacute egrave -20 -KPX yacute emacron -20 -KPX yacute eogonek -20 -KPX yacute o -20 -KPX yacute oacute -20 -KPX yacute ocircumflex -20 -KPX yacute odieresis -20 -KPX yacute ograve -20 -KPX yacute ohungarumlaut -20 -KPX yacute omacron -20 -KPX yacute oslash -20 -KPX yacute otilde -20 -KPX yacute period -100 -KPX ydieresis a -20 -KPX ydieresis aacute -20 -KPX ydieresis abreve -20 -KPX ydieresis acircumflex -20 -KPX ydieresis adieresis -20 -KPX ydieresis agrave -20 -KPX ydieresis amacron -20 -KPX ydieresis aogonek -20 -KPX ydieresis aring -20 -KPX ydieresis atilde -20 -KPX ydieresis comma -100 -KPX ydieresis e -20 -KPX ydieresis eacute -20 -KPX ydieresis ecaron -20 -KPX ydieresis ecircumflex -20 -KPX ydieresis edieresis -20 -KPX ydieresis edotaccent -20 -KPX ydieresis egrave -20 -KPX ydieresis emacron -20 -KPX ydieresis eogonek -20 -KPX ydieresis o -20 -KPX ydieresis oacute -20 -KPX ydieresis ocircumflex -20 -KPX ydieresis odieresis -20 -KPX ydieresis ograve -20 -KPX ydieresis ohungarumlaut -20 -KPX ydieresis omacron -20 -KPX ydieresis oslash -20 -KPX ydieresis otilde -20 -KPX ydieresis period -100 -KPX z e -15 -KPX z eacute -15 -KPX z ecaron -15 -KPX z ecircumflex -15 -KPX z edieresis -15 -KPX z edotaccent -15 -KPX z egrave -15 -KPX z emacron -15 -KPX z eogonek -15 -KPX z o -15 -KPX z oacute -15 -KPX z ocircumflex -15 -KPX z odieresis -15 -KPX z ograve -15 -KPX z ohungarumlaut -15 -KPX z omacron -15 -KPX z oslash -15 -KPX z otilde -15 -KPX zacute e -15 -KPX zacute eacute -15 -KPX zacute ecaron -15 -KPX zacute ecircumflex -15 -KPX zacute edieresis -15 -KPX zacute edotaccent -15 -KPX zacute egrave -15 -KPX zacute emacron -15 -KPX zacute eogonek -15 -KPX zacute o -15 -KPX zacute oacute -15 -KPX zacute ocircumflex -15 -KPX zacute odieresis -15 -KPX zacute ograve -15 -KPX zacute ohungarumlaut -15 -KPX zacute omacron -15 -KPX zacute oslash -15 -KPX zacute otilde -15 -KPX zcaron e -15 -KPX zcaron eacute -15 -KPX zcaron ecaron -15 -KPX zcaron ecircumflex -15 -KPX zcaron edieresis -15 -KPX zcaron edotaccent -15 -KPX zcaron egrave -15 -KPX zcaron emacron -15 -KPX zcaron eogonek -15 -KPX zcaron o -15 -KPX zcaron oacute -15 -KPX zcaron ocircumflex -15 -KPX zcaron odieresis -15 -KPX zcaron ograve -15 -KPX zcaron ohungarumlaut -15 -KPX zcaron omacron -15 -KPX zcaron oslash -15 -KPX zcaron otilde -15 -KPX zdotaccent e -15 -KPX zdotaccent eacute -15 -KPX zdotaccent ecaron -15 -KPX zdotaccent ecircumflex -15 -KPX zdotaccent edieresis -15 -KPX zdotaccent edotaccent -15 -KPX zdotaccent egrave -15 -KPX zdotaccent emacron -15 -KPX zdotaccent eogonek -15 -KPX zdotaccent o -15 -KPX zdotaccent oacute -15 -KPX zdotaccent ocircumflex -15 -KPX zdotaccent odieresis -15 -KPX zdotaccent ograve -15 -KPX zdotaccent ohungarumlaut -15 -KPX zdotaccent omacron -15 -KPX zdotaccent oslash -15 -KPX zdotaccent otilde -15 -EndKernPairs -EndKernData -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Symbol.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Symbol.afm deleted file mode 100644 index 6a5386a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Symbol.afm +++ /dev/null @@ -1,213 +0,0 @@ -StartFontMetrics 4.1 -Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All rights reserved. -Comment Creation Date: Thu May 1 15:12:25 1997 -Comment UniqueID 43064 -Comment VMusage 30820 39997 -FontName Symbol -FullName Symbol -FamilyName Symbol -Weight Medium -ItalicAngle 0 -IsFixedPitch false -CharacterSet Special -FontBBox -180 -293 1090 1010 -UnderlinePosition -100 -UnderlineThickness 50 -Version 001.008 -Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All rights reserved. -EncodingScheme FontSpecific -StdHW 92 -StdVW 85 -StartCharMetrics 190 -C 32 ; WX 250 ; N space ; B 0 0 0 0 ; -C 33 ; WX 333 ; N exclam ; B 128 -17 240 672 ; -C 34 ; WX 713 ; N universal ; B 31 0 681 705 ; -C 35 ; WX 500 ; N numbersign ; B 20 -16 481 673 ; -C 36 ; WX 549 ; N existential ; B 25 0 478 707 ; -C 37 ; WX 833 ; N percent ; B 63 -36 771 655 ; -C 38 ; WX 778 ; N ampersand ; B 41 -18 750 661 ; -C 39 ; WX 439 ; N suchthat ; B 48 -17 414 500 ; -C 40 ; WX 333 ; N parenleft ; B 53 -191 300 673 ; -C 41 ; WX 333 ; N parenright ; B 30 -191 277 673 ; -C 42 ; WX 500 ; N asteriskmath ; B 65 134 427 551 ; -C 43 ; WX 549 ; N plus ; B 10 0 539 533 ; -C 44 ; WX 250 ; N comma ; B 56 -152 194 104 ; -C 45 ; WX 549 ; N minus ; B 11 233 535 288 ; -C 46 ; WX 250 ; N period ; B 69 -17 181 95 ; -C 47 ; WX 278 ; N slash ; B 0 -18 254 646 ; -C 48 ; WX 500 ; N zero ; B 24 -14 476 685 ; -C 49 ; WX 500 ; N one ; B 117 0 390 673 ; -C 50 ; WX 500 ; N two ; B 25 0 475 685 ; -C 51 ; WX 500 ; N three ; B 43 -14 435 685 ; -C 52 ; WX 500 ; N four ; B 15 0 469 685 ; -C 53 ; WX 500 ; N five ; B 32 -14 445 690 ; -C 54 ; WX 500 ; N six ; B 34 -14 468 685 ; -C 55 ; WX 500 ; N seven ; B 24 -16 448 673 ; -C 56 ; WX 500 ; N eight ; B 56 -14 445 685 ; -C 57 ; WX 500 ; N nine ; B 30 -18 459 685 ; -C 58 ; WX 278 ; N colon ; B 81 -17 193 460 ; -C 59 ; WX 278 ; N semicolon ; B 83 -152 221 460 ; -C 60 ; WX 549 ; N less ; B 26 0 523 522 ; -C 61 ; WX 549 ; N equal ; B 11 141 537 390 ; -C 62 ; WX 549 ; N greater ; B 26 0 523 522 ; -C 63 ; WX 444 ; N question ; B 70 -17 412 686 ; -C 64 ; WX 549 ; N congruent ; B 11 0 537 475 ; -C 65 ; WX 722 ; N Alpha ; B 4 0 684 673 ; -C 66 ; WX 667 ; N Beta ; B 29 0 592 673 ; -C 67 ; WX 722 ; N Chi ; B -9 0 704 673 ; -C 68 ; WX 612 ; N Delta ; B 6 0 608 688 ; -C 69 ; WX 611 ; N Epsilon ; B 32 0 617 673 ; -C 70 ; WX 763 ; N Phi ; B 26 0 741 673 ; -C 71 ; WX 603 ; N Gamma ; B 24 0 609 673 ; -C 72 ; WX 722 ; N Eta ; B 39 0 729 673 ; -C 73 ; WX 333 ; N Iota ; B 32 0 316 673 ; -C 74 ; WX 631 ; N theta1 ; B 18 -18 623 689 ; -C 75 ; WX 722 ; N Kappa ; B 35 0 722 673 ; -C 76 ; WX 686 ; N Lambda ; B 6 0 680 688 ; -C 77 ; WX 889 ; N Mu ; B 28 0 887 673 ; -C 78 ; WX 722 ; N Nu ; B 29 -8 720 673 ; -C 79 ; WX 722 ; N Omicron ; B 41 -17 715 685 ; -C 80 ; WX 768 ; N Pi ; B 25 0 745 673 ; -C 81 ; WX 741 ; N Theta ; B 41 -17 715 685 ; -C 82 ; WX 556 ; N Rho ; B 28 0 563 673 ; -C 83 ; WX 592 ; N Sigma ; B 5 0 589 673 ; -C 84 ; WX 611 ; N Tau ; B 33 0 607 673 ; -C 85 ; WX 690 ; N Upsilon ; B -8 0 694 673 ; -C 86 ; WX 439 ; N sigma1 ; B 40 -233 436 500 ; -C 87 ; WX 768 ; N Omega ; B 34 0 736 688 ; -C 88 ; WX 645 ; N Xi ; B 40 0 599 673 ; -C 89 ; WX 795 ; N Psi ; B 15 0 781 684 ; -C 90 ; WX 611 ; N Zeta ; B 44 0 636 673 ; -C 91 ; WX 333 ; N bracketleft ; B 86 -155 299 674 ; -C 92 ; WX 863 ; N therefore ; B 163 0 701 487 ; -C 93 ; WX 333 ; N bracketright ; B 33 -155 246 674 ; -C 94 ; WX 658 ; N perpendicular ; B 15 0 652 674 ; -C 95 ; WX 500 ; N underscore ; B -2 -125 502 -75 ; -C 96 ; WX 500 ; N radicalex ; B 480 881 1090 917 ; -C 97 ; WX 631 ; N alpha ; B 41 -18 622 500 ; -C 98 ; WX 549 ; N beta ; B 61 -223 515 741 ; -C 99 ; WX 549 ; N chi ; B 12 -231 522 499 ; -C 100 ; WX 494 ; N delta ; B 40 -19 481 740 ; -C 101 ; WX 439 ; N epsilon ; B 22 -19 427 502 ; -C 102 ; WX 521 ; N phi ; B 28 -224 492 673 ; -C 103 ; WX 411 ; N gamma ; B 5 -225 484 499 ; -C 104 ; WX 603 ; N eta ; B 0 -202 527 514 ; -C 105 ; WX 329 ; N iota ; B 0 -17 301 503 ; -C 106 ; WX 603 ; N phi1 ; B 36 -224 587 499 ; -C 107 ; WX 549 ; N kappa ; B 33 0 558 501 ; -C 108 ; WX 549 ; N lambda ; B 24 -17 548 739 ; -C 109 ; WX 576 ; N mu ; B 33 -223 567 500 ; -C 110 ; WX 521 ; N nu ; B -9 -16 475 507 ; -C 111 ; WX 549 ; N omicron ; B 35 -19 501 499 ; -C 112 ; WX 549 ; N pi ; B 10 -19 530 487 ; -C 113 ; WX 521 ; N theta ; B 43 -17 485 690 ; -C 114 ; WX 549 ; N rho ; B 50 -230 490 499 ; -C 115 ; WX 603 ; N sigma ; B 30 -21 588 500 ; -C 116 ; WX 439 ; N tau ; B 10 -19 418 500 ; -C 117 ; WX 576 ; N upsilon ; B 7 -18 535 507 ; -C 118 ; WX 713 ; N omega1 ; B 12 -18 671 583 ; -C 119 ; WX 686 ; N omega ; B 42 -17 684 500 ; -C 120 ; WX 493 ; N xi ; B 27 -224 469 766 ; -C 121 ; WX 686 ; N psi ; B 12 -228 701 500 ; -C 122 ; WX 494 ; N zeta ; B 60 -225 467 756 ; -C 123 ; WX 480 ; N braceleft ; B 58 -183 397 673 ; -C 124 ; WX 200 ; N bar ; B 65 -293 135 707 ; -C 125 ; WX 480 ; N braceright ; B 79 -183 418 673 ; -C 126 ; WX 549 ; N similar ; B 17 203 529 307 ; -C 160 ; WX 750 ; N Euro ; B 20 -12 714 685 ; -C 161 ; WX 620 ; N Upsilon1 ; B -2 0 610 685 ; -C 162 ; WX 247 ; N minute ; B 27 459 228 735 ; -C 163 ; WX 549 ; N lessequal ; B 29 0 526 639 ; -C 164 ; WX 167 ; N fraction ; B -180 -12 340 677 ; -C 165 ; WX 713 ; N infinity ; B 26 124 688 404 ; -C 166 ; WX 500 ; N florin ; B 2 -193 494 686 ; -C 167 ; WX 753 ; N club ; B 86 -26 660 533 ; -C 168 ; WX 753 ; N diamond ; B 142 -36 600 550 ; -C 169 ; WX 753 ; N heart ; B 117 -33 631 532 ; -C 170 ; WX 753 ; N spade ; B 113 -36 629 548 ; -C 171 ; WX 1042 ; N arrowboth ; B 24 -15 1024 511 ; -C 172 ; WX 987 ; N arrowleft ; B 32 -15 942 511 ; -C 173 ; WX 603 ; N arrowup ; B 45 0 571 910 ; -C 174 ; WX 987 ; N arrowright ; B 49 -15 959 511 ; -C 175 ; WX 603 ; N arrowdown ; B 45 -22 571 888 ; -C 176 ; WX 400 ; N degree ; B 50 385 350 685 ; -C 177 ; WX 549 ; N plusminus ; B 10 0 539 645 ; -C 178 ; WX 411 ; N second ; B 20 459 413 737 ; -C 179 ; WX 549 ; N greaterequal ; B 29 0 526 639 ; -C 180 ; WX 549 ; N multiply ; B 17 8 533 524 ; -C 181 ; WX 713 ; N proportional ; B 27 123 639 404 ; -C 182 ; WX 494 ; N partialdiff ; B 26 -20 462 746 ; -C 183 ; WX 460 ; N bullet ; B 50 113 410 473 ; -C 184 ; WX 549 ; N divide ; B 10 71 536 456 ; -C 185 ; WX 549 ; N notequal ; B 15 -25 540 549 ; -C 186 ; WX 549 ; N equivalence ; B 14 82 538 443 ; -C 187 ; WX 549 ; N approxequal ; B 14 135 527 394 ; -C 188 ; WX 1000 ; N ellipsis ; B 111 -17 889 95 ; -C 189 ; WX 603 ; N arrowvertex ; B 280 -120 336 1010 ; -C 190 ; WX 1000 ; N arrowhorizex ; B -60 220 1050 276 ; -C 191 ; WX 658 ; N carriagereturn ; B 15 -16 602 629 ; -C 192 ; WX 823 ; N aleph ; B 175 -18 661 658 ; -C 193 ; WX 686 ; N Ifraktur ; B 10 -53 578 740 ; -C 194 ; WX 795 ; N Rfraktur ; B 26 -15 759 734 ; -C 195 ; WX 987 ; N weierstrass ; B 159 -211 870 573 ; -C 196 ; WX 768 ; N circlemultiply ; B 43 -17 733 673 ; -C 197 ; WX 768 ; N circleplus ; B 43 -15 733 675 ; -C 198 ; WX 823 ; N emptyset ; B 39 -24 781 719 ; -C 199 ; WX 768 ; N intersection ; B 40 0 732 509 ; -C 200 ; WX 768 ; N union ; B 40 -17 732 492 ; -C 201 ; WX 713 ; N propersuperset ; B 20 0 673 470 ; -C 202 ; WX 713 ; N reflexsuperset ; B 20 -125 673 470 ; -C 203 ; WX 713 ; N notsubset ; B 36 -70 690 540 ; -C 204 ; WX 713 ; N propersubset ; B 37 0 690 470 ; -C 205 ; WX 713 ; N reflexsubset ; B 37 -125 690 470 ; -C 206 ; WX 713 ; N element ; B 45 0 505 468 ; -C 207 ; WX 713 ; N notelement ; B 45 -58 505 555 ; -C 208 ; WX 768 ; N angle ; B 26 0 738 673 ; -C 209 ; WX 713 ; N gradient ; B 36 -19 681 718 ; -C 210 ; WX 790 ; N registerserif ; B 50 -17 740 673 ; -C 211 ; WX 790 ; N copyrightserif ; B 51 -15 741 675 ; -C 212 ; WX 890 ; N trademarkserif ; B 18 293 855 673 ; -C 213 ; WX 823 ; N product ; B 25 -101 803 751 ; -C 214 ; WX 549 ; N radical ; B 10 -38 515 917 ; -C 215 ; WX 250 ; N dotmath ; B 69 210 169 310 ; -C 216 ; WX 713 ; N logicalnot ; B 15 0 680 288 ; -C 217 ; WX 603 ; N logicaland ; B 23 0 583 454 ; -C 218 ; WX 603 ; N logicalor ; B 30 0 578 477 ; -C 219 ; WX 1042 ; N arrowdblboth ; B 27 -20 1023 510 ; -C 220 ; WX 987 ; N arrowdblleft ; B 30 -15 939 513 ; -C 221 ; WX 603 ; N arrowdblup ; B 39 2 567 911 ; -C 222 ; WX 987 ; N arrowdblright ; B 45 -20 954 508 ; -C 223 ; WX 603 ; N arrowdbldown ; B 44 -19 572 890 ; -C 224 ; WX 494 ; N lozenge ; B 18 0 466 745 ; -C 225 ; WX 329 ; N angleleft ; B 25 -198 306 746 ; -C 226 ; WX 790 ; N registersans ; B 50 -20 740 670 ; -C 227 ; WX 790 ; N copyrightsans ; B 49 -15 739 675 ; -C 228 ; WX 786 ; N trademarksans ; B 5 293 725 673 ; -C 229 ; WX 713 ; N summation ; B 14 -108 695 752 ; -C 230 ; WX 384 ; N parenlefttp ; B 24 -293 436 926 ; -C 231 ; WX 384 ; N parenleftex ; B 24 -85 108 925 ; -C 232 ; WX 384 ; N parenleftbt ; B 24 -293 436 926 ; -C 233 ; WX 384 ; N bracketlefttp ; B 0 -80 349 926 ; -C 234 ; WX 384 ; N bracketleftex ; B 0 -79 77 925 ; -C 235 ; WX 384 ; N bracketleftbt ; B 0 -80 349 926 ; -C 236 ; WX 494 ; N bracelefttp ; B 209 -85 445 925 ; -C 237 ; WX 494 ; N braceleftmid ; B 20 -85 284 935 ; -C 238 ; WX 494 ; N braceleftbt ; B 209 -75 445 935 ; -C 239 ; WX 494 ; N braceex ; B 209 -85 284 935 ; -C 241 ; WX 329 ; N angleright ; B 21 -198 302 746 ; -C 242 ; WX 274 ; N integral ; B 2 -107 291 916 ; -C 243 ; WX 686 ; N integraltp ; B 308 -88 675 920 ; -C 244 ; WX 686 ; N integralex ; B 308 -88 378 975 ; -C 245 ; WX 686 ; N integralbt ; B 11 -87 378 921 ; -C 246 ; WX 384 ; N parenrighttp ; B 54 -293 466 926 ; -C 247 ; WX 384 ; N parenrightex ; B 382 -85 466 925 ; -C 248 ; WX 384 ; N parenrightbt ; B 54 -293 466 926 ; -C 249 ; WX 384 ; N bracketrighttp ; B 22 -80 371 926 ; -C 250 ; WX 384 ; N bracketrightex ; B 294 -79 371 925 ; -C 251 ; WX 384 ; N bracketrightbt ; B 22 -80 371 926 ; -C 252 ; WX 494 ; N bracerighttp ; B 48 -85 284 925 ; -C 253 ; WX 494 ; N bracerightmid ; B 209 -85 473 935 ; -C 254 ; WX 494 ; N bracerightbt ; B 48 -75 284 935 ; -C -1 ; WX 790 ; N apple ; B 56 -3 733 808 ; -EndCharMetrics -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Times-Bold.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Times-Bold.afm deleted file mode 100644 index 559ebae..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Times-Bold.afm +++ /dev/null @@ -1,2588 +0,0 @@ -StartFontMetrics 4.1 -Comment Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Thu May 1 12:52:56 1997 -Comment UniqueID 43065 -Comment VMusage 41636 52661 -FontName Times-Bold -FullName Times Bold -FamilyName Times -Weight Bold -ItalicAngle 0 -IsFixedPitch false -CharacterSet ExtendedRoman -FontBBox -168 -218 1000 935 -UnderlinePosition -100 -UnderlineThickness 50 -Version 002.000 -Notice Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.Times is a trademark of Linotype-Hell AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 676 -XHeight 461 -Ascender 683 -Descender -217 -StdHW 44 -StdVW 139 -StartCharMetrics 315 -C 32 ; WX 250 ; N space ; B 0 0 0 0 ; -C 33 ; WX 333 ; N exclam ; B 81 -13 251 691 ; -C 34 ; WX 555 ; N quotedbl ; B 83 404 472 691 ; -C 35 ; WX 500 ; N numbersign ; B 4 0 496 700 ; -C 36 ; WX 500 ; N dollar ; B 29 -99 472 750 ; -C 37 ; WX 1000 ; N percent ; B 124 -14 877 692 ; -C 38 ; WX 833 ; N ampersand ; B 62 -16 787 691 ; -C 39 ; WX 333 ; N quoteright ; B 79 356 263 691 ; -C 40 ; WX 333 ; N parenleft ; B 46 -168 306 694 ; -C 41 ; WX 333 ; N parenright ; B 27 -168 287 694 ; -C 42 ; WX 500 ; N asterisk ; B 56 255 447 691 ; -C 43 ; WX 570 ; N plus ; B 33 0 537 506 ; -C 44 ; WX 250 ; N comma ; B 39 -180 223 155 ; -C 45 ; WX 333 ; N hyphen ; B 44 171 287 287 ; -C 46 ; WX 250 ; N period ; B 41 -13 210 156 ; -C 47 ; WX 278 ; N slash ; B -24 -19 302 691 ; -C 48 ; WX 500 ; N zero ; B 24 -13 476 688 ; -C 49 ; WX 500 ; N one ; B 65 0 442 688 ; -C 50 ; WX 500 ; N two ; B 17 0 478 688 ; -C 51 ; WX 500 ; N three ; B 16 -14 468 688 ; -C 52 ; WX 500 ; N four ; B 19 0 475 688 ; -C 53 ; WX 500 ; N five ; B 22 -8 470 676 ; -C 54 ; WX 500 ; N six ; B 28 -13 475 688 ; -C 55 ; WX 500 ; N seven ; B 17 0 477 676 ; -C 56 ; WX 500 ; N eight ; B 28 -13 472 688 ; -C 57 ; WX 500 ; N nine ; B 26 -13 473 688 ; -C 58 ; WX 333 ; N colon ; B 82 -13 251 472 ; -C 59 ; WX 333 ; N semicolon ; B 82 -180 266 472 ; -C 60 ; WX 570 ; N less ; B 31 -8 539 514 ; -C 61 ; WX 570 ; N equal ; B 33 107 537 399 ; -C 62 ; WX 570 ; N greater ; B 31 -8 539 514 ; -C 63 ; WX 500 ; N question ; B 57 -13 445 689 ; -C 64 ; WX 930 ; N at ; B 108 -19 822 691 ; -C 65 ; WX 722 ; N A ; B 9 0 689 690 ; -C 66 ; WX 667 ; N B ; B 16 0 619 676 ; -C 67 ; WX 722 ; N C ; B 49 -19 687 691 ; -C 68 ; WX 722 ; N D ; B 14 0 690 676 ; -C 69 ; WX 667 ; N E ; B 16 0 641 676 ; -C 70 ; WX 611 ; N F ; B 16 0 583 676 ; -C 71 ; WX 778 ; N G ; B 37 -19 755 691 ; -C 72 ; WX 778 ; N H ; B 21 0 759 676 ; -C 73 ; WX 389 ; N I ; B 20 0 370 676 ; -C 74 ; WX 500 ; N J ; B 3 -96 479 676 ; -C 75 ; WX 778 ; N K ; B 30 0 769 676 ; -C 76 ; WX 667 ; N L ; B 19 0 638 676 ; -C 77 ; WX 944 ; N M ; B 14 0 921 676 ; -C 78 ; WX 722 ; N N ; B 16 -18 701 676 ; -C 79 ; WX 778 ; N O ; B 35 -19 743 691 ; -C 80 ; WX 611 ; N P ; B 16 0 600 676 ; -C 81 ; WX 778 ; N Q ; B 35 -176 743 691 ; -C 82 ; WX 722 ; N R ; B 26 0 715 676 ; -C 83 ; WX 556 ; N S ; B 35 -19 513 692 ; -C 84 ; WX 667 ; N T ; B 31 0 636 676 ; -C 85 ; WX 722 ; N U ; B 16 -19 701 676 ; -C 86 ; WX 722 ; N V ; B 16 -18 701 676 ; -C 87 ; WX 1000 ; N W ; B 19 -15 981 676 ; -C 88 ; WX 722 ; N X ; B 16 0 699 676 ; -C 89 ; WX 722 ; N Y ; B 15 0 699 676 ; -C 90 ; WX 667 ; N Z ; B 28 0 634 676 ; -C 91 ; WX 333 ; N bracketleft ; B 67 -149 301 678 ; -C 92 ; WX 278 ; N backslash ; B -25 -19 303 691 ; -C 93 ; WX 333 ; N bracketright ; B 32 -149 266 678 ; -C 94 ; WX 581 ; N asciicircum ; B 73 311 509 676 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 333 ; N quoteleft ; B 70 356 254 691 ; -C 97 ; WX 500 ; N a ; B 25 -14 488 473 ; -C 98 ; WX 556 ; N b ; B 17 -14 521 676 ; -C 99 ; WX 444 ; N c ; B 25 -14 430 473 ; -C 100 ; WX 556 ; N d ; B 25 -14 534 676 ; -C 101 ; WX 444 ; N e ; B 25 -14 426 473 ; -C 102 ; WX 333 ; N f ; B 14 0 389 691 ; L i fi ; L l fl ; -C 103 ; WX 500 ; N g ; B 28 -206 483 473 ; -C 104 ; WX 556 ; N h ; B 16 0 534 676 ; -C 105 ; WX 278 ; N i ; B 16 0 255 691 ; -C 106 ; WX 333 ; N j ; B -57 -203 263 691 ; -C 107 ; WX 556 ; N k ; B 22 0 543 676 ; -C 108 ; WX 278 ; N l ; B 16 0 255 676 ; -C 109 ; WX 833 ; N m ; B 16 0 814 473 ; -C 110 ; WX 556 ; N n ; B 21 0 539 473 ; -C 111 ; WX 500 ; N o ; B 25 -14 476 473 ; -C 112 ; WX 556 ; N p ; B 19 -205 524 473 ; -C 113 ; WX 556 ; N q ; B 34 -205 536 473 ; -C 114 ; WX 444 ; N r ; B 29 0 434 473 ; -C 115 ; WX 389 ; N s ; B 25 -14 361 473 ; -C 116 ; WX 333 ; N t ; B 20 -12 332 630 ; -C 117 ; WX 556 ; N u ; B 16 -14 537 461 ; -C 118 ; WX 500 ; N v ; B 21 -14 485 461 ; -C 119 ; WX 722 ; N w ; B 23 -14 707 461 ; -C 120 ; WX 500 ; N x ; B 12 0 484 461 ; -C 121 ; WX 500 ; N y ; B 16 -205 480 461 ; -C 122 ; WX 444 ; N z ; B 21 0 420 461 ; -C 123 ; WX 394 ; N braceleft ; B 22 -175 340 698 ; -C 124 ; WX 220 ; N bar ; B 66 -218 154 782 ; -C 125 ; WX 394 ; N braceright ; B 54 -175 372 698 ; -C 126 ; WX 520 ; N asciitilde ; B 29 173 491 333 ; -C 161 ; WX 333 ; N exclamdown ; B 82 -203 252 501 ; -C 162 ; WX 500 ; N cent ; B 53 -140 458 588 ; -C 163 ; WX 500 ; N sterling ; B 21 -14 477 684 ; -C 164 ; WX 167 ; N fraction ; B -168 -12 329 688 ; -C 165 ; WX 500 ; N yen ; B -64 0 547 676 ; -C 166 ; WX 500 ; N florin ; B 0 -155 498 706 ; -C 167 ; WX 500 ; N section ; B 57 -132 443 691 ; -C 168 ; WX 500 ; N currency ; B -26 61 526 613 ; -C 169 ; WX 278 ; N quotesingle ; B 75 404 204 691 ; -C 170 ; WX 500 ; N quotedblleft ; B 32 356 486 691 ; -C 171 ; WX 500 ; N guillemotleft ; B 23 36 473 415 ; -C 172 ; WX 333 ; N guilsinglleft ; B 51 36 305 415 ; -C 173 ; WX 333 ; N guilsinglright ; B 28 36 282 415 ; -C 174 ; WX 556 ; N fi ; B 14 0 536 691 ; -C 175 ; WX 556 ; N fl ; B 14 0 536 691 ; -C 177 ; WX 500 ; N endash ; B 0 181 500 271 ; -C 178 ; WX 500 ; N dagger ; B 47 -134 453 691 ; -C 179 ; WX 500 ; N daggerdbl ; B 45 -132 456 691 ; -C 180 ; WX 250 ; N periodcentered ; B 41 248 210 417 ; -C 182 ; WX 540 ; N paragraph ; B 0 -186 519 676 ; -C 183 ; WX 350 ; N bullet ; B 35 198 315 478 ; -C 184 ; WX 333 ; N quotesinglbase ; B 79 -180 263 155 ; -C 185 ; WX 500 ; N quotedblbase ; B 14 -180 468 155 ; -C 186 ; WX 500 ; N quotedblright ; B 14 356 468 691 ; -C 187 ; WX 500 ; N guillemotright ; B 27 36 477 415 ; -C 188 ; WX 1000 ; N ellipsis ; B 82 -13 917 156 ; -C 189 ; WX 1000 ; N perthousand ; B 7 -29 995 706 ; -C 191 ; WX 500 ; N questiondown ; B 55 -201 443 501 ; -C 193 ; WX 333 ; N grave ; B 8 528 246 713 ; -C 194 ; WX 333 ; N acute ; B 86 528 324 713 ; -C 195 ; WX 333 ; N circumflex ; B -2 528 335 704 ; -C 196 ; WX 333 ; N tilde ; B -16 547 349 674 ; -C 197 ; WX 333 ; N macron ; B 1 565 331 637 ; -C 198 ; WX 333 ; N breve ; B 15 528 318 691 ; -C 199 ; WX 333 ; N dotaccent ; B 103 536 258 691 ; -C 200 ; WX 333 ; N dieresis ; B -2 537 335 667 ; -C 202 ; WX 333 ; N ring ; B 60 527 273 740 ; -C 203 ; WX 333 ; N cedilla ; B 68 -218 294 0 ; -C 205 ; WX 333 ; N hungarumlaut ; B -13 528 425 713 ; -C 206 ; WX 333 ; N ogonek ; B 90 -193 319 24 ; -C 207 ; WX 333 ; N caron ; B -2 528 335 704 ; -C 208 ; WX 1000 ; N emdash ; B 0 181 1000 271 ; -C 225 ; WX 1000 ; N AE ; B 4 0 951 676 ; -C 227 ; WX 300 ; N ordfeminine ; B -1 397 301 688 ; -C 232 ; WX 667 ; N Lslash ; B 19 0 638 676 ; -C 233 ; WX 778 ; N Oslash ; B 35 -74 743 737 ; -C 234 ; WX 1000 ; N OE ; B 22 -5 981 684 ; -C 235 ; WX 330 ; N ordmasculine ; B 18 397 312 688 ; -C 241 ; WX 722 ; N ae ; B 33 -14 693 473 ; -C 245 ; WX 278 ; N dotlessi ; B 16 0 255 461 ; -C 248 ; WX 278 ; N lslash ; B -22 0 303 676 ; -C 249 ; WX 500 ; N oslash ; B 25 -92 476 549 ; -C 250 ; WX 722 ; N oe ; B 22 -14 696 473 ; -C 251 ; WX 556 ; N germandbls ; B 19 -12 517 691 ; -C -1 ; WX 389 ; N Idieresis ; B 20 0 370 877 ; -C -1 ; WX 444 ; N eacute ; B 25 -14 426 713 ; -C -1 ; WX 500 ; N abreve ; B 25 -14 488 691 ; -C -1 ; WX 556 ; N uhungarumlaut ; B 16 -14 557 713 ; -C -1 ; WX 444 ; N ecaron ; B 25 -14 426 704 ; -C -1 ; WX 722 ; N Ydieresis ; B 15 0 699 877 ; -C -1 ; WX 570 ; N divide ; B 33 -31 537 537 ; -C -1 ; WX 722 ; N Yacute ; B 15 0 699 923 ; -C -1 ; WX 722 ; N Acircumflex ; B 9 0 689 914 ; -C -1 ; WX 500 ; N aacute ; B 25 -14 488 713 ; -C -1 ; WX 722 ; N Ucircumflex ; B 16 -19 701 914 ; -C -1 ; WX 500 ; N yacute ; B 16 -205 480 713 ; -C -1 ; WX 389 ; N scommaaccent ; B 25 -218 361 473 ; -C -1 ; WX 444 ; N ecircumflex ; B 25 -14 426 704 ; -C -1 ; WX 722 ; N Uring ; B 16 -19 701 935 ; -C -1 ; WX 722 ; N Udieresis ; B 16 -19 701 877 ; -C -1 ; WX 500 ; N aogonek ; B 25 -193 504 473 ; -C -1 ; WX 722 ; N Uacute ; B 16 -19 701 923 ; -C -1 ; WX 556 ; N uogonek ; B 16 -193 539 461 ; -C -1 ; WX 667 ; N Edieresis ; B 16 0 641 877 ; -C -1 ; WX 722 ; N Dcroat ; B 6 0 690 676 ; -C -1 ; WX 250 ; N commaaccent ; B 47 -218 203 -50 ; -C -1 ; WX 747 ; N copyright ; B 26 -19 721 691 ; -C -1 ; WX 667 ; N Emacron ; B 16 0 641 847 ; -C -1 ; WX 444 ; N ccaron ; B 25 -14 430 704 ; -C -1 ; WX 500 ; N aring ; B 25 -14 488 740 ; -C -1 ; WX 722 ; N Ncommaaccent ; B 16 -188 701 676 ; -C -1 ; WX 278 ; N lacute ; B 16 0 297 923 ; -C -1 ; WX 500 ; N agrave ; B 25 -14 488 713 ; -C -1 ; WX 667 ; N Tcommaaccent ; B 31 -218 636 676 ; -C -1 ; WX 722 ; N Cacute ; B 49 -19 687 923 ; -C -1 ; WX 500 ; N atilde ; B 25 -14 488 674 ; -C -1 ; WX 667 ; N Edotaccent ; B 16 0 641 901 ; -C -1 ; WX 389 ; N scaron ; B 25 -14 363 704 ; -C -1 ; WX 389 ; N scedilla ; B 25 -218 361 473 ; -C -1 ; WX 278 ; N iacute ; B 16 0 289 713 ; -C -1 ; WX 494 ; N lozenge ; B 10 0 484 745 ; -C -1 ; WX 722 ; N Rcaron ; B 26 0 715 914 ; -C -1 ; WX 778 ; N Gcommaaccent ; B 37 -218 755 691 ; -C -1 ; WX 556 ; N ucircumflex ; B 16 -14 537 704 ; -C -1 ; WX 500 ; N acircumflex ; B 25 -14 488 704 ; -C -1 ; WX 722 ; N Amacron ; B 9 0 689 847 ; -C -1 ; WX 444 ; N rcaron ; B 29 0 434 704 ; -C -1 ; WX 444 ; N ccedilla ; B 25 -218 430 473 ; -C -1 ; WX 667 ; N Zdotaccent ; B 28 0 634 901 ; -C -1 ; WX 611 ; N Thorn ; B 16 0 600 676 ; -C -1 ; WX 778 ; N Omacron ; B 35 -19 743 847 ; -C -1 ; WX 722 ; N Racute ; B 26 0 715 923 ; -C -1 ; WX 556 ; N Sacute ; B 35 -19 513 923 ; -C -1 ; WX 672 ; N dcaron ; B 25 -14 681 682 ; -C -1 ; WX 722 ; N Umacron ; B 16 -19 701 847 ; -C -1 ; WX 556 ; N uring ; B 16 -14 537 740 ; -C -1 ; WX 300 ; N threesuperior ; B 3 268 297 688 ; -C -1 ; WX 778 ; N Ograve ; B 35 -19 743 923 ; -C -1 ; WX 722 ; N Agrave ; B 9 0 689 923 ; -C -1 ; WX 722 ; N Abreve ; B 9 0 689 901 ; -C -1 ; WX 570 ; N multiply ; B 48 16 522 490 ; -C -1 ; WX 556 ; N uacute ; B 16 -14 537 713 ; -C -1 ; WX 667 ; N Tcaron ; B 31 0 636 914 ; -C -1 ; WX 494 ; N partialdiff ; B 11 -21 494 750 ; -C -1 ; WX 500 ; N ydieresis ; B 16 -205 480 667 ; -C -1 ; WX 722 ; N Nacute ; B 16 -18 701 923 ; -C -1 ; WX 278 ; N icircumflex ; B -37 0 300 704 ; -C -1 ; WX 667 ; N Ecircumflex ; B 16 0 641 914 ; -C -1 ; WX 500 ; N adieresis ; B 25 -14 488 667 ; -C -1 ; WX 444 ; N edieresis ; B 25 -14 426 667 ; -C -1 ; WX 444 ; N cacute ; B 25 -14 430 713 ; -C -1 ; WX 556 ; N nacute ; B 21 0 539 713 ; -C -1 ; WX 556 ; N umacron ; B 16 -14 537 637 ; -C -1 ; WX 722 ; N Ncaron ; B 16 -18 701 914 ; -C -1 ; WX 389 ; N Iacute ; B 20 0 370 923 ; -C -1 ; WX 570 ; N plusminus ; B 33 0 537 506 ; -C -1 ; WX 220 ; N brokenbar ; B 66 -143 154 707 ; -C -1 ; WX 747 ; N registered ; B 26 -19 721 691 ; -C -1 ; WX 778 ; N Gbreve ; B 37 -19 755 901 ; -C -1 ; WX 389 ; N Idotaccent ; B 20 0 370 901 ; -C -1 ; WX 600 ; N summation ; B 14 -10 585 706 ; -C -1 ; WX 667 ; N Egrave ; B 16 0 641 923 ; -C -1 ; WX 444 ; N racute ; B 29 0 434 713 ; -C -1 ; WX 500 ; N omacron ; B 25 -14 476 637 ; -C -1 ; WX 667 ; N Zacute ; B 28 0 634 923 ; -C -1 ; WX 667 ; N Zcaron ; B 28 0 634 914 ; -C -1 ; WX 549 ; N greaterequal ; B 26 0 523 704 ; -C -1 ; WX 722 ; N Eth ; B 6 0 690 676 ; -C -1 ; WX 722 ; N Ccedilla ; B 49 -218 687 691 ; -C -1 ; WX 278 ; N lcommaaccent ; B 16 -218 255 676 ; -C -1 ; WX 416 ; N tcaron ; B 20 -12 425 815 ; -C -1 ; WX 444 ; N eogonek ; B 25 -193 426 473 ; -C -1 ; WX 722 ; N Uogonek ; B 16 -193 701 676 ; -C -1 ; WX 722 ; N Aacute ; B 9 0 689 923 ; -C -1 ; WX 722 ; N Adieresis ; B 9 0 689 877 ; -C -1 ; WX 444 ; N egrave ; B 25 -14 426 713 ; -C -1 ; WX 444 ; N zacute ; B 21 0 420 713 ; -C -1 ; WX 278 ; N iogonek ; B 16 -193 274 691 ; -C -1 ; WX 778 ; N Oacute ; B 35 -19 743 923 ; -C -1 ; WX 500 ; N oacute ; B 25 -14 476 713 ; -C -1 ; WX 500 ; N amacron ; B 25 -14 488 637 ; -C -1 ; WX 389 ; N sacute ; B 25 -14 361 713 ; -C -1 ; WX 278 ; N idieresis ; B -37 0 300 667 ; -C -1 ; WX 778 ; N Ocircumflex ; B 35 -19 743 914 ; -C -1 ; WX 722 ; N Ugrave ; B 16 -19 701 923 ; -C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ; -C -1 ; WX 556 ; N thorn ; B 19 -205 524 676 ; -C -1 ; WX 300 ; N twosuperior ; B 0 275 300 688 ; -C -1 ; WX 778 ; N Odieresis ; B 35 -19 743 877 ; -C -1 ; WX 556 ; N mu ; B 33 -206 536 461 ; -C -1 ; WX 278 ; N igrave ; B -27 0 255 713 ; -C -1 ; WX 500 ; N ohungarumlaut ; B 25 -14 529 713 ; -C -1 ; WX 667 ; N Eogonek ; B 16 -193 644 676 ; -C -1 ; WX 556 ; N dcroat ; B 25 -14 534 676 ; -C -1 ; WX 750 ; N threequarters ; B 23 -12 733 688 ; -C -1 ; WX 556 ; N Scedilla ; B 35 -218 513 692 ; -C -1 ; WX 394 ; N lcaron ; B 16 0 412 682 ; -C -1 ; WX 778 ; N Kcommaaccent ; B 30 -218 769 676 ; -C -1 ; WX 667 ; N Lacute ; B 19 0 638 923 ; -C -1 ; WX 1000 ; N trademark ; B 24 271 977 676 ; -C -1 ; WX 444 ; N edotaccent ; B 25 -14 426 691 ; -C -1 ; WX 389 ; N Igrave ; B 20 0 370 923 ; -C -1 ; WX 389 ; N Imacron ; B 20 0 370 847 ; -C -1 ; WX 667 ; N Lcaron ; B 19 0 652 682 ; -C -1 ; WX 750 ; N onehalf ; B -7 -12 775 688 ; -C -1 ; WX 549 ; N lessequal ; B 29 0 526 704 ; -C -1 ; WX 500 ; N ocircumflex ; B 25 -14 476 704 ; -C -1 ; WX 556 ; N ntilde ; B 21 0 539 674 ; -C -1 ; WX 722 ; N Uhungarumlaut ; B 16 -19 701 923 ; -C -1 ; WX 667 ; N Eacute ; B 16 0 641 923 ; -C -1 ; WX 444 ; N emacron ; B 25 -14 426 637 ; -C -1 ; WX 500 ; N gbreve ; B 28 -206 483 691 ; -C -1 ; WX 750 ; N onequarter ; B 28 -12 743 688 ; -C -1 ; WX 556 ; N Scaron ; B 35 -19 513 914 ; -C -1 ; WX 556 ; N Scommaaccent ; B 35 -218 513 692 ; -C -1 ; WX 778 ; N Ohungarumlaut ; B 35 -19 743 923 ; -C -1 ; WX 400 ; N degree ; B 57 402 343 688 ; -C -1 ; WX 500 ; N ograve ; B 25 -14 476 713 ; -C -1 ; WX 722 ; N Ccaron ; B 49 -19 687 914 ; -C -1 ; WX 556 ; N ugrave ; B 16 -14 537 713 ; -C -1 ; WX 549 ; N radical ; B 10 -46 512 850 ; -C -1 ; WX 722 ; N Dcaron ; B 14 0 690 914 ; -C -1 ; WX 444 ; N rcommaaccent ; B 29 -218 434 473 ; -C -1 ; WX 722 ; N Ntilde ; B 16 -18 701 884 ; -C -1 ; WX 500 ; N otilde ; B 25 -14 476 674 ; -C -1 ; WX 722 ; N Rcommaaccent ; B 26 -218 715 676 ; -C -1 ; WX 667 ; N Lcommaaccent ; B 19 -218 638 676 ; -C -1 ; WX 722 ; N Atilde ; B 9 0 689 884 ; -C -1 ; WX 722 ; N Aogonek ; B 9 -193 699 690 ; -C -1 ; WX 722 ; N Aring ; B 9 0 689 935 ; -C -1 ; WX 778 ; N Otilde ; B 35 -19 743 884 ; -C -1 ; WX 444 ; N zdotaccent ; B 21 0 420 691 ; -C -1 ; WX 667 ; N Ecaron ; B 16 0 641 914 ; -C -1 ; WX 389 ; N Iogonek ; B 20 -193 370 676 ; -C -1 ; WX 556 ; N kcommaaccent ; B 22 -218 543 676 ; -C -1 ; WX 570 ; N minus ; B 33 209 537 297 ; -C -1 ; WX 389 ; N Icircumflex ; B 20 0 370 914 ; -C -1 ; WX 556 ; N ncaron ; B 21 0 539 704 ; -C -1 ; WX 333 ; N tcommaaccent ; B 20 -218 332 630 ; -C -1 ; WX 570 ; N logicalnot ; B 33 108 537 399 ; -C -1 ; WX 500 ; N odieresis ; B 25 -14 476 667 ; -C -1 ; WX 556 ; N udieresis ; B 16 -14 537 667 ; -C -1 ; WX 549 ; N notequal ; B 15 -49 540 570 ; -C -1 ; WX 500 ; N gcommaaccent ; B 28 -206 483 829 ; -C -1 ; WX 500 ; N eth ; B 25 -14 476 691 ; -C -1 ; WX 444 ; N zcaron ; B 21 0 420 704 ; -C -1 ; WX 556 ; N ncommaaccent ; B 21 -218 539 473 ; -C -1 ; WX 300 ; N onesuperior ; B 28 275 273 688 ; -C -1 ; WX 278 ; N imacron ; B -8 0 272 637 ; -C -1 ; WX 500 ; N Euro ; B 0 0 0 0 ; -EndCharMetrics -StartKernData -StartKernPairs 2242 -KPX A C -55 -KPX A Cacute -55 -KPX A Ccaron -55 -KPX A Ccedilla -55 -KPX A G -55 -KPX A Gbreve -55 -KPX A Gcommaaccent -55 -KPX A O -45 -KPX A Oacute -45 -KPX A Ocircumflex -45 -KPX A Odieresis -45 -KPX A Ograve -45 -KPX A Ohungarumlaut -45 -KPX A Omacron -45 -KPX A Oslash -45 -KPX A Otilde -45 -KPX A Q -45 -KPX A T -95 -KPX A Tcaron -95 -KPX A Tcommaaccent -95 -KPX A U -50 -KPX A Uacute -50 -KPX A Ucircumflex -50 -KPX A Udieresis -50 -KPX A Ugrave -50 -KPX A Uhungarumlaut -50 -KPX A Umacron -50 -KPX A Uogonek -50 -KPX A Uring -50 -KPX A V -145 -KPX A W -130 -KPX A Y -100 -KPX A Yacute -100 -KPX A Ydieresis -100 -KPX A p -25 -KPX A quoteright -74 -KPX A u -50 -KPX A uacute -50 -KPX A ucircumflex -50 -KPX A udieresis -50 -KPX A ugrave -50 -KPX A uhungarumlaut -50 -KPX A umacron -50 -KPX A uogonek -50 -KPX A uring -50 -KPX A v -100 -KPX A w -90 -KPX A y -74 -KPX A yacute -74 -KPX A ydieresis -74 -KPX Aacute C -55 -KPX Aacute Cacute -55 -KPX Aacute Ccaron -55 -KPX Aacute Ccedilla -55 -KPX Aacute G -55 -KPX Aacute Gbreve -55 -KPX Aacute Gcommaaccent -55 -KPX Aacute O -45 -KPX Aacute Oacute -45 -KPX Aacute Ocircumflex -45 -KPX Aacute Odieresis -45 -KPX Aacute Ograve -45 -KPX Aacute Ohungarumlaut -45 -KPX Aacute Omacron -45 -KPX Aacute Oslash -45 -KPX Aacute Otilde -45 -KPX Aacute Q -45 -KPX Aacute T -95 -KPX Aacute Tcaron -95 -KPX Aacute Tcommaaccent -95 -KPX Aacute U -50 -KPX Aacute Uacute -50 -KPX Aacute Ucircumflex -50 -KPX Aacute Udieresis -50 -KPX Aacute Ugrave -50 -KPX Aacute Uhungarumlaut -50 -KPX Aacute Umacron -50 -KPX Aacute Uogonek -50 -KPX Aacute Uring -50 -KPX Aacute V -145 -KPX Aacute W -130 -KPX Aacute Y -100 -KPX Aacute Yacute -100 -KPX Aacute Ydieresis -100 -KPX Aacute p -25 -KPX Aacute quoteright -74 -KPX Aacute u -50 -KPX Aacute uacute -50 -KPX Aacute ucircumflex -50 -KPX Aacute udieresis -50 -KPX Aacute ugrave -50 -KPX Aacute uhungarumlaut -50 -KPX Aacute umacron -50 -KPX Aacute uogonek -50 -KPX Aacute uring -50 -KPX Aacute v -100 -KPX Aacute w -90 -KPX Aacute y -74 -KPX Aacute yacute -74 -KPX Aacute ydieresis -74 -KPX Abreve C -55 -KPX Abreve Cacute -55 -KPX Abreve Ccaron -55 -KPX Abreve Ccedilla -55 -KPX Abreve G -55 -KPX Abreve Gbreve -55 -KPX Abreve Gcommaaccent -55 -KPX Abreve O -45 -KPX Abreve Oacute -45 -KPX Abreve Ocircumflex -45 -KPX Abreve Odieresis -45 -KPX Abreve Ograve -45 -KPX Abreve Ohungarumlaut -45 -KPX Abreve Omacron -45 -KPX Abreve Oslash -45 -KPX Abreve Otilde -45 -KPX Abreve Q -45 -KPX Abreve T -95 -KPX Abreve Tcaron -95 -KPX Abreve Tcommaaccent -95 -KPX Abreve U -50 -KPX Abreve Uacute -50 -KPX Abreve Ucircumflex -50 -KPX Abreve Udieresis -50 -KPX Abreve Ugrave -50 -KPX Abreve Uhungarumlaut -50 -KPX Abreve Umacron -50 -KPX Abreve Uogonek -50 -KPX Abreve Uring -50 -KPX Abreve V -145 -KPX Abreve W -130 -KPX Abreve Y -100 -KPX Abreve Yacute -100 -KPX Abreve Ydieresis -100 -KPX Abreve p -25 -KPX Abreve quoteright -74 -KPX Abreve u -50 -KPX Abreve uacute -50 -KPX Abreve ucircumflex -50 -KPX Abreve udieresis -50 -KPX Abreve ugrave -50 -KPX Abreve uhungarumlaut -50 -KPX Abreve umacron -50 -KPX Abreve uogonek -50 -KPX Abreve uring -50 -KPX Abreve v -100 -KPX Abreve w -90 -KPX Abreve y -74 -KPX Abreve yacute -74 -KPX Abreve ydieresis -74 -KPX Acircumflex C -55 -KPX Acircumflex Cacute -55 -KPX Acircumflex Ccaron -55 -KPX Acircumflex Ccedilla -55 -KPX Acircumflex G -55 -KPX Acircumflex Gbreve -55 -KPX Acircumflex Gcommaaccent -55 -KPX Acircumflex O -45 -KPX Acircumflex Oacute -45 -KPX Acircumflex Ocircumflex -45 -KPX Acircumflex Odieresis -45 -KPX Acircumflex Ograve -45 -KPX Acircumflex Ohungarumlaut -45 -KPX Acircumflex Omacron -45 -KPX Acircumflex Oslash -45 -KPX Acircumflex Otilde -45 -KPX Acircumflex Q -45 -KPX Acircumflex T -95 -KPX Acircumflex Tcaron -95 -KPX Acircumflex Tcommaaccent -95 -KPX Acircumflex U -50 -KPX Acircumflex Uacute -50 -KPX Acircumflex Ucircumflex -50 -KPX Acircumflex Udieresis -50 -KPX Acircumflex Ugrave -50 -KPX Acircumflex Uhungarumlaut -50 -KPX Acircumflex Umacron -50 -KPX Acircumflex Uogonek -50 -KPX Acircumflex Uring -50 -KPX Acircumflex V -145 -KPX Acircumflex W -130 -KPX Acircumflex Y -100 -KPX Acircumflex Yacute -100 -KPX Acircumflex Ydieresis -100 -KPX Acircumflex p -25 -KPX Acircumflex quoteright -74 -KPX Acircumflex u -50 -KPX Acircumflex uacute -50 -KPX Acircumflex ucircumflex -50 -KPX Acircumflex udieresis -50 -KPX Acircumflex ugrave -50 -KPX Acircumflex uhungarumlaut -50 -KPX Acircumflex umacron -50 -KPX Acircumflex uogonek -50 -KPX Acircumflex uring -50 -KPX Acircumflex v -100 -KPX Acircumflex w -90 -KPX Acircumflex y -74 -KPX Acircumflex yacute -74 -KPX Acircumflex ydieresis -74 -KPX Adieresis C -55 -KPX Adieresis Cacute -55 -KPX Adieresis Ccaron -55 -KPX Adieresis Ccedilla -55 -KPX Adieresis G -55 -KPX Adieresis Gbreve -55 -KPX Adieresis Gcommaaccent -55 -KPX Adieresis O -45 -KPX Adieresis Oacute -45 -KPX Adieresis Ocircumflex -45 -KPX Adieresis Odieresis -45 -KPX Adieresis Ograve -45 -KPX Adieresis Ohungarumlaut -45 -KPX Adieresis Omacron -45 -KPX Adieresis Oslash -45 -KPX Adieresis Otilde -45 -KPX Adieresis Q -45 -KPX Adieresis T -95 -KPX Adieresis Tcaron -95 -KPX Adieresis Tcommaaccent -95 -KPX Adieresis U -50 -KPX Adieresis Uacute -50 -KPX Adieresis Ucircumflex -50 -KPX Adieresis Udieresis -50 -KPX Adieresis Ugrave -50 -KPX Adieresis Uhungarumlaut -50 -KPX Adieresis Umacron -50 -KPX Adieresis Uogonek -50 -KPX Adieresis Uring -50 -KPX Adieresis V -145 -KPX Adieresis W -130 -KPX Adieresis Y -100 -KPX Adieresis Yacute -100 -KPX Adieresis Ydieresis -100 -KPX Adieresis p -25 -KPX Adieresis quoteright -74 -KPX Adieresis u -50 -KPX Adieresis uacute -50 -KPX Adieresis ucircumflex -50 -KPX Adieresis udieresis -50 -KPX Adieresis ugrave -50 -KPX Adieresis uhungarumlaut -50 -KPX Adieresis umacron -50 -KPX Adieresis uogonek -50 -KPX Adieresis uring -50 -KPX Adieresis v -100 -KPX Adieresis w -90 -KPX Adieresis y -74 -KPX Adieresis yacute -74 -KPX Adieresis ydieresis -74 -KPX Agrave C -55 -KPX Agrave Cacute -55 -KPX Agrave Ccaron -55 -KPX Agrave Ccedilla -55 -KPX Agrave G -55 -KPX Agrave Gbreve -55 -KPX Agrave Gcommaaccent -55 -KPX Agrave O -45 -KPX Agrave Oacute -45 -KPX Agrave Ocircumflex -45 -KPX Agrave Odieresis -45 -KPX Agrave Ograve -45 -KPX Agrave Ohungarumlaut -45 -KPX Agrave Omacron -45 -KPX Agrave Oslash -45 -KPX Agrave Otilde -45 -KPX Agrave Q -45 -KPX Agrave T -95 -KPX Agrave Tcaron -95 -KPX Agrave Tcommaaccent -95 -KPX Agrave U -50 -KPX Agrave Uacute -50 -KPX Agrave Ucircumflex -50 -KPX Agrave Udieresis -50 -KPX Agrave Ugrave -50 -KPX Agrave Uhungarumlaut -50 -KPX Agrave Umacron -50 -KPX Agrave Uogonek -50 -KPX Agrave Uring -50 -KPX Agrave V -145 -KPX Agrave W -130 -KPX Agrave Y -100 -KPX Agrave Yacute -100 -KPX Agrave Ydieresis -100 -KPX Agrave p -25 -KPX Agrave quoteright -74 -KPX Agrave u -50 -KPX Agrave uacute -50 -KPX Agrave ucircumflex -50 -KPX Agrave udieresis -50 -KPX Agrave ugrave -50 -KPX Agrave uhungarumlaut -50 -KPX Agrave umacron -50 -KPX Agrave uogonek -50 -KPX Agrave uring -50 -KPX Agrave v -100 -KPX Agrave w -90 -KPX Agrave y -74 -KPX Agrave yacute -74 -KPX Agrave ydieresis -74 -KPX Amacron C -55 -KPX Amacron Cacute -55 -KPX Amacron Ccaron -55 -KPX Amacron Ccedilla -55 -KPX Amacron G -55 -KPX Amacron Gbreve -55 -KPX Amacron Gcommaaccent -55 -KPX Amacron O -45 -KPX Amacron Oacute -45 -KPX Amacron Ocircumflex -45 -KPX Amacron Odieresis -45 -KPX Amacron Ograve -45 -KPX Amacron Ohungarumlaut -45 -KPX Amacron Omacron -45 -KPX Amacron Oslash -45 -KPX Amacron Otilde -45 -KPX Amacron Q -45 -KPX Amacron T -95 -KPX Amacron Tcaron -95 -KPX Amacron Tcommaaccent -95 -KPX Amacron U -50 -KPX Amacron Uacute -50 -KPX Amacron Ucircumflex -50 -KPX Amacron Udieresis -50 -KPX Amacron Ugrave -50 -KPX Amacron Uhungarumlaut -50 -KPX Amacron Umacron -50 -KPX Amacron Uogonek -50 -KPX Amacron Uring -50 -KPX Amacron V -145 -KPX Amacron W -130 -KPX Amacron Y -100 -KPX Amacron Yacute -100 -KPX Amacron Ydieresis -100 -KPX Amacron p -25 -KPX Amacron quoteright -74 -KPX Amacron u -50 -KPX Amacron uacute -50 -KPX Amacron ucircumflex -50 -KPX Amacron udieresis -50 -KPX Amacron ugrave -50 -KPX Amacron uhungarumlaut -50 -KPX Amacron umacron -50 -KPX Amacron uogonek -50 -KPX Amacron uring -50 -KPX Amacron v -100 -KPX Amacron w -90 -KPX Amacron y -74 -KPX Amacron yacute -74 -KPX Amacron ydieresis -74 -KPX Aogonek C -55 -KPX Aogonek Cacute -55 -KPX Aogonek Ccaron -55 -KPX Aogonek Ccedilla -55 -KPX Aogonek G -55 -KPX Aogonek Gbreve -55 -KPX Aogonek Gcommaaccent -55 -KPX Aogonek O -45 -KPX Aogonek Oacute -45 -KPX Aogonek Ocircumflex -45 -KPX Aogonek Odieresis -45 -KPX Aogonek Ograve -45 -KPX Aogonek Ohungarumlaut -45 -KPX Aogonek Omacron -45 -KPX Aogonek Oslash -45 -KPX Aogonek Otilde -45 -KPX Aogonek Q -45 -KPX Aogonek T -95 -KPX Aogonek Tcaron -95 -KPX Aogonek Tcommaaccent -95 -KPX Aogonek U -50 -KPX Aogonek Uacute -50 -KPX Aogonek Ucircumflex -50 -KPX Aogonek Udieresis -50 -KPX Aogonek Ugrave -50 -KPX Aogonek Uhungarumlaut -50 -KPX Aogonek Umacron -50 -KPX Aogonek Uogonek -50 -KPX Aogonek Uring -50 -KPX Aogonek V -145 -KPX Aogonek W -130 -KPX Aogonek Y -100 -KPX Aogonek Yacute -100 -KPX Aogonek Ydieresis -100 -KPX Aogonek p -25 -KPX Aogonek quoteright -74 -KPX Aogonek u -50 -KPX Aogonek uacute -50 -KPX Aogonek ucircumflex -50 -KPX Aogonek udieresis -50 -KPX Aogonek ugrave -50 -KPX Aogonek uhungarumlaut -50 -KPX Aogonek umacron -50 -KPX Aogonek uogonek -50 -KPX Aogonek uring -50 -KPX Aogonek v -100 -KPX Aogonek w -90 -KPX Aogonek y -34 -KPX Aogonek yacute -34 -KPX Aogonek ydieresis -34 -KPX Aring C -55 -KPX Aring Cacute -55 -KPX Aring Ccaron -55 -KPX Aring Ccedilla -55 -KPX Aring G -55 -KPX Aring Gbreve -55 -KPX Aring Gcommaaccent -55 -KPX Aring O -45 -KPX Aring Oacute -45 -KPX Aring Ocircumflex -45 -KPX Aring Odieresis -45 -KPX Aring Ograve -45 -KPX Aring Ohungarumlaut -45 -KPX Aring Omacron -45 -KPX Aring Oslash -45 -KPX Aring Otilde -45 -KPX Aring Q -45 -KPX Aring T -95 -KPX Aring Tcaron -95 -KPX Aring Tcommaaccent -95 -KPX Aring U -50 -KPX Aring Uacute -50 -KPX Aring Ucircumflex -50 -KPX Aring Udieresis -50 -KPX Aring Ugrave -50 -KPX Aring Uhungarumlaut -50 -KPX Aring Umacron -50 -KPX Aring Uogonek -50 -KPX Aring Uring -50 -KPX Aring V -145 -KPX Aring W -130 -KPX Aring Y -100 -KPX Aring Yacute -100 -KPX Aring Ydieresis -100 -KPX Aring p -25 -KPX Aring quoteright -74 -KPX Aring u -50 -KPX Aring uacute -50 -KPX Aring ucircumflex -50 -KPX Aring udieresis -50 -KPX Aring ugrave -50 -KPX Aring uhungarumlaut -50 -KPX Aring umacron -50 -KPX Aring uogonek -50 -KPX Aring uring -50 -KPX Aring v -100 -KPX Aring w -90 -KPX Aring y -74 -KPX Aring yacute -74 -KPX Aring ydieresis -74 -KPX Atilde C -55 -KPX Atilde Cacute -55 -KPX Atilde Ccaron -55 -KPX Atilde Ccedilla -55 -KPX Atilde G -55 -KPX Atilde Gbreve -55 -KPX Atilde Gcommaaccent -55 -KPX Atilde O -45 -KPX Atilde Oacute -45 -KPX Atilde Ocircumflex -45 -KPX Atilde Odieresis -45 -KPX Atilde Ograve -45 -KPX Atilde Ohungarumlaut -45 -KPX Atilde Omacron -45 -KPX Atilde Oslash -45 -KPX Atilde Otilde -45 -KPX Atilde Q -45 -KPX Atilde T -95 -KPX Atilde Tcaron -95 -KPX Atilde Tcommaaccent -95 -KPX Atilde U -50 -KPX Atilde Uacute -50 -KPX Atilde Ucircumflex -50 -KPX Atilde Udieresis -50 -KPX Atilde Ugrave -50 -KPX Atilde Uhungarumlaut -50 -KPX Atilde Umacron -50 -KPX Atilde Uogonek -50 -KPX Atilde Uring -50 -KPX Atilde V -145 -KPX Atilde W -130 -KPX Atilde Y -100 -KPX Atilde Yacute -100 -KPX Atilde Ydieresis -100 -KPX Atilde p -25 -KPX Atilde quoteright -74 -KPX Atilde u -50 -KPX Atilde uacute -50 -KPX Atilde ucircumflex -50 -KPX Atilde udieresis -50 -KPX Atilde ugrave -50 -KPX Atilde uhungarumlaut -50 -KPX Atilde umacron -50 -KPX Atilde uogonek -50 -KPX Atilde uring -50 -KPX Atilde v -100 -KPX Atilde w -90 -KPX Atilde y -74 -KPX Atilde yacute -74 -KPX Atilde ydieresis -74 -KPX B A -30 -KPX B Aacute -30 -KPX B Abreve -30 -KPX B Acircumflex -30 -KPX B Adieresis -30 -KPX B Agrave -30 -KPX B Amacron -30 -KPX B Aogonek -30 -KPX B Aring -30 -KPX B Atilde -30 -KPX B U -10 -KPX B Uacute -10 -KPX B Ucircumflex -10 -KPX B Udieresis -10 -KPX B Ugrave -10 -KPX B Uhungarumlaut -10 -KPX B Umacron -10 -KPX B Uogonek -10 -KPX B Uring -10 -KPX D A -35 -KPX D Aacute -35 -KPX D Abreve -35 -KPX D Acircumflex -35 -KPX D Adieresis -35 -KPX D Agrave -35 -KPX D Amacron -35 -KPX D Aogonek -35 -KPX D Aring -35 -KPX D Atilde -35 -KPX D V -40 -KPX D W -40 -KPX D Y -40 -KPX D Yacute -40 -KPX D Ydieresis -40 -KPX D period -20 -KPX Dcaron A -35 -KPX Dcaron Aacute -35 -KPX Dcaron Abreve -35 -KPX Dcaron Acircumflex -35 -KPX Dcaron Adieresis -35 -KPX Dcaron Agrave -35 -KPX Dcaron Amacron -35 -KPX Dcaron Aogonek -35 -KPX Dcaron Aring -35 -KPX Dcaron Atilde -35 -KPX Dcaron V -40 -KPX Dcaron W -40 -KPX Dcaron Y -40 -KPX Dcaron Yacute -40 -KPX Dcaron Ydieresis -40 -KPX Dcaron period -20 -KPX Dcroat A -35 -KPX Dcroat Aacute -35 -KPX Dcroat Abreve -35 -KPX Dcroat Acircumflex -35 -KPX Dcroat Adieresis -35 -KPX Dcroat Agrave -35 -KPX Dcroat Amacron -35 -KPX Dcroat Aogonek -35 -KPX Dcroat Aring -35 -KPX Dcroat Atilde -35 -KPX Dcroat V -40 -KPX Dcroat W -40 -KPX Dcroat Y -40 -KPX Dcroat Yacute -40 -KPX Dcroat Ydieresis -40 -KPX Dcroat period -20 -KPX F A -90 -KPX F Aacute -90 -KPX F Abreve -90 -KPX F Acircumflex -90 -KPX F Adieresis -90 -KPX F Agrave -90 -KPX F Amacron -90 -KPX F Aogonek -90 -KPX F Aring -90 -KPX F Atilde -90 -KPX F a -25 -KPX F aacute -25 -KPX F abreve -25 -KPX F acircumflex -25 -KPX F adieresis -25 -KPX F agrave -25 -KPX F amacron -25 -KPX F aogonek -25 -KPX F aring -25 -KPX F atilde -25 -KPX F comma -92 -KPX F e -25 -KPX F eacute -25 -KPX F ecaron -25 -KPX F ecircumflex -25 -KPX F edieresis -25 -KPX F edotaccent -25 -KPX F egrave -25 -KPX F emacron -25 -KPX F eogonek -25 -KPX F o -25 -KPX F oacute -25 -KPX F ocircumflex -25 -KPX F odieresis -25 -KPX F ograve -25 -KPX F ohungarumlaut -25 -KPX F omacron -25 -KPX F oslash -25 -KPX F otilde -25 -KPX F period -110 -KPX J A -30 -KPX J Aacute -30 -KPX J Abreve -30 -KPX J Acircumflex -30 -KPX J Adieresis -30 -KPX J Agrave -30 -KPX J Amacron -30 -KPX J Aogonek -30 -KPX J Aring -30 -KPX J Atilde -30 -KPX J a -15 -KPX J aacute -15 -KPX J abreve -15 -KPX J acircumflex -15 -KPX J adieresis -15 -KPX J agrave -15 -KPX J amacron -15 -KPX J aogonek -15 -KPX J aring -15 -KPX J atilde -15 -KPX J e -15 -KPX J eacute -15 -KPX J ecaron -15 -KPX J ecircumflex -15 -KPX J edieresis -15 -KPX J edotaccent -15 -KPX J egrave -15 -KPX J emacron -15 -KPX J eogonek -15 -KPX J o -15 -KPX J oacute -15 -KPX J ocircumflex -15 -KPX J odieresis -15 -KPX J ograve -15 -KPX J ohungarumlaut -15 -KPX J omacron -15 -KPX J oslash -15 -KPX J otilde -15 -KPX J period -20 -KPX J u -15 -KPX J uacute -15 -KPX J ucircumflex -15 -KPX J udieresis -15 -KPX J ugrave -15 -KPX J uhungarumlaut -15 -KPX J umacron -15 -KPX J uogonek -15 -KPX J uring -15 -KPX K O -30 -KPX K Oacute -30 -KPX K Ocircumflex -30 -KPX K Odieresis -30 -KPX K Ograve -30 -KPX K Ohungarumlaut -30 -KPX K Omacron -30 -KPX K Oslash -30 -KPX K Otilde -30 -KPX K e -25 -KPX K eacute -25 -KPX K ecaron -25 -KPX K ecircumflex -25 -KPX K edieresis -25 -KPX K edotaccent -25 -KPX K egrave -25 -KPX K emacron -25 -KPX K eogonek -25 -KPX K o -25 -KPX K oacute -25 -KPX K ocircumflex -25 -KPX K odieresis -25 -KPX K ograve -25 -KPX K ohungarumlaut -25 -KPX K omacron -25 -KPX K oslash -25 -KPX K otilde -25 -KPX K u -15 -KPX K uacute -15 -KPX K ucircumflex -15 -KPX K udieresis -15 -KPX K ugrave -15 -KPX K uhungarumlaut -15 -KPX K umacron -15 -KPX K uogonek -15 -KPX K uring -15 -KPX K y -45 -KPX K yacute -45 -KPX K ydieresis -45 -KPX Kcommaaccent O -30 -KPX Kcommaaccent Oacute -30 -KPX Kcommaaccent Ocircumflex -30 -KPX Kcommaaccent Odieresis -30 -KPX Kcommaaccent Ograve -30 -KPX Kcommaaccent Ohungarumlaut -30 -KPX Kcommaaccent Omacron -30 -KPX Kcommaaccent Oslash -30 -KPX Kcommaaccent Otilde -30 -KPX Kcommaaccent e -25 -KPX Kcommaaccent eacute -25 -KPX Kcommaaccent ecaron -25 -KPX Kcommaaccent ecircumflex -25 -KPX Kcommaaccent edieresis -25 -KPX Kcommaaccent edotaccent -25 -KPX Kcommaaccent egrave -25 -KPX Kcommaaccent emacron -25 -KPX Kcommaaccent eogonek -25 -KPX Kcommaaccent o -25 -KPX Kcommaaccent oacute -25 -KPX Kcommaaccent ocircumflex -25 -KPX Kcommaaccent odieresis -25 -KPX Kcommaaccent ograve -25 -KPX Kcommaaccent ohungarumlaut -25 -KPX Kcommaaccent omacron -25 -KPX Kcommaaccent oslash -25 -KPX Kcommaaccent otilde -25 -KPX Kcommaaccent u -15 -KPX Kcommaaccent uacute -15 -KPX Kcommaaccent ucircumflex -15 -KPX Kcommaaccent udieresis -15 -KPX Kcommaaccent ugrave -15 -KPX Kcommaaccent uhungarumlaut -15 -KPX Kcommaaccent umacron -15 -KPX Kcommaaccent uogonek -15 -KPX Kcommaaccent uring -15 -KPX Kcommaaccent y -45 -KPX Kcommaaccent yacute -45 -KPX Kcommaaccent ydieresis -45 -KPX L T -92 -KPX L Tcaron -92 -KPX L Tcommaaccent -92 -KPX L V -92 -KPX L W -92 -KPX L Y -92 -KPX L Yacute -92 -KPX L Ydieresis -92 -KPX L quotedblright -20 -KPX L quoteright -110 -KPX L y -55 -KPX L yacute -55 -KPX L ydieresis -55 -KPX Lacute T -92 -KPX Lacute Tcaron -92 -KPX Lacute Tcommaaccent -92 -KPX Lacute V -92 -KPX Lacute W -92 -KPX Lacute Y -92 -KPX Lacute Yacute -92 -KPX Lacute Ydieresis -92 -KPX Lacute quotedblright -20 -KPX Lacute quoteright -110 -KPX Lacute y -55 -KPX Lacute yacute -55 -KPX Lacute ydieresis -55 -KPX Lcommaaccent T -92 -KPX Lcommaaccent Tcaron -92 -KPX Lcommaaccent Tcommaaccent -92 -KPX Lcommaaccent V -92 -KPX Lcommaaccent W -92 -KPX Lcommaaccent Y -92 -KPX Lcommaaccent Yacute -92 -KPX Lcommaaccent Ydieresis -92 -KPX Lcommaaccent quotedblright -20 -KPX Lcommaaccent quoteright -110 -KPX Lcommaaccent y -55 -KPX Lcommaaccent yacute -55 -KPX Lcommaaccent ydieresis -55 -KPX Lslash T -92 -KPX Lslash Tcaron -92 -KPX Lslash Tcommaaccent -92 -KPX Lslash V -92 -KPX Lslash W -92 -KPX Lslash Y -92 -KPX Lslash Yacute -92 -KPX Lslash Ydieresis -92 -KPX Lslash quotedblright -20 -KPX Lslash quoteright -110 -KPX Lslash y -55 -KPX Lslash yacute -55 -KPX Lslash ydieresis -55 -KPX N A -20 -KPX N Aacute -20 -KPX N Abreve -20 -KPX N Acircumflex -20 -KPX N Adieresis -20 -KPX N Agrave -20 -KPX N Amacron -20 -KPX N Aogonek -20 -KPX N Aring -20 -KPX N Atilde -20 -KPX Nacute A -20 -KPX Nacute Aacute -20 -KPX Nacute Abreve -20 -KPX Nacute Acircumflex -20 -KPX Nacute Adieresis -20 -KPX Nacute Agrave -20 -KPX Nacute Amacron -20 -KPX Nacute Aogonek -20 -KPX Nacute Aring -20 -KPX Nacute Atilde -20 -KPX Ncaron A -20 -KPX Ncaron Aacute -20 -KPX Ncaron Abreve -20 -KPX Ncaron Acircumflex -20 -KPX Ncaron Adieresis -20 -KPX Ncaron Agrave -20 -KPX Ncaron Amacron -20 -KPX Ncaron Aogonek -20 -KPX Ncaron Aring -20 -KPX Ncaron Atilde -20 -KPX Ncommaaccent A -20 -KPX Ncommaaccent Aacute -20 -KPX Ncommaaccent Abreve -20 -KPX Ncommaaccent Acircumflex -20 -KPX Ncommaaccent Adieresis -20 -KPX Ncommaaccent Agrave -20 -KPX Ncommaaccent Amacron -20 -KPX Ncommaaccent Aogonek -20 -KPX Ncommaaccent Aring -20 -KPX Ncommaaccent Atilde -20 -KPX Ntilde A -20 -KPX Ntilde Aacute -20 -KPX Ntilde Abreve -20 -KPX Ntilde Acircumflex -20 -KPX Ntilde Adieresis -20 -KPX Ntilde Agrave -20 -KPX Ntilde Amacron -20 -KPX Ntilde Aogonek -20 -KPX Ntilde Aring -20 -KPX Ntilde Atilde -20 -KPX O A -40 -KPX O Aacute -40 -KPX O Abreve -40 -KPX O Acircumflex -40 -KPX O Adieresis -40 -KPX O Agrave -40 -KPX O Amacron -40 -KPX O Aogonek -40 -KPX O Aring -40 -KPX O Atilde -40 -KPX O T -40 -KPX O Tcaron -40 -KPX O Tcommaaccent -40 -KPX O V -50 -KPX O W -50 -KPX O X -40 -KPX O Y -50 -KPX O Yacute -50 -KPX O Ydieresis -50 -KPX Oacute A -40 -KPX Oacute Aacute -40 -KPX Oacute Abreve -40 -KPX Oacute Acircumflex -40 -KPX Oacute Adieresis -40 -KPX Oacute Agrave -40 -KPX Oacute Amacron -40 -KPX Oacute Aogonek -40 -KPX Oacute Aring -40 -KPX Oacute Atilde -40 -KPX Oacute T -40 -KPX Oacute Tcaron -40 -KPX Oacute Tcommaaccent -40 -KPX Oacute V -50 -KPX Oacute W -50 -KPX Oacute X -40 -KPX Oacute Y -50 -KPX Oacute Yacute -50 -KPX Oacute Ydieresis -50 -KPX Ocircumflex A -40 -KPX Ocircumflex Aacute -40 -KPX Ocircumflex Abreve -40 -KPX Ocircumflex Acircumflex -40 -KPX Ocircumflex Adieresis -40 -KPX Ocircumflex Agrave -40 -KPX Ocircumflex Amacron -40 -KPX Ocircumflex Aogonek -40 -KPX Ocircumflex Aring -40 -KPX Ocircumflex Atilde -40 -KPX Ocircumflex T -40 -KPX Ocircumflex Tcaron -40 -KPX Ocircumflex Tcommaaccent -40 -KPX Ocircumflex V -50 -KPX Ocircumflex W -50 -KPX Ocircumflex X -40 -KPX Ocircumflex Y -50 -KPX Ocircumflex Yacute -50 -KPX Ocircumflex Ydieresis -50 -KPX Odieresis A -40 -KPX Odieresis Aacute -40 -KPX Odieresis Abreve -40 -KPX Odieresis Acircumflex -40 -KPX Odieresis Adieresis -40 -KPX Odieresis Agrave -40 -KPX Odieresis Amacron -40 -KPX Odieresis Aogonek -40 -KPX Odieresis Aring -40 -KPX Odieresis Atilde -40 -KPX Odieresis T -40 -KPX Odieresis Tcaron -40 -KPX Odieresis Tcommaaccent -40 -KPX Odieresis V -50 -KPX Odieresis W -50 -KPX Odieresis X -40 -KPX Odieresis Y -50 -KPX Odieresis Yacute -50 -KPX Odieresis Ydieresis -50 -KPX Ograve A -40 -KPX Ograve Aacute -40 -KPX Ograve Abreve -40 -KPX Ograve Acircumflex -40 -KPX Ograve Adieresis -40 -KPX Ograve Agrave -40 -KPX Ograve Amacron -40 -KPX Ograve Aogonek -40 -KPX Ograve Aring -40 -KPX Ograve Atilde -40 -KPX Ograve T -40 -KPX Ograve Tcaron -40 -KPX Ograve Tcommaaccent -40 -KPX Ograve V -50 -KPX Ograve W -50 -KPX Ograve X -40 -KPX Ograve Y -50 -KPX Ograve Yacute -50 -KPX Ograve Ydieresis -50 -KPX Ohungarumlaut A -40 -KPX Ohungarumlaut Aacute -40 -KPX Ohungarumlaut Abreve -40 -KPX Ohungarumlaut Acircumflex -40 -KPX Ohungarumlaut Adieresis -40 -KPX Ohungarumlaut Agrave -40 -KPX Ohungarumlaut Amacron -40 -KPX Ohungarumlaut Aogonek -40 -KPX Ohungarumlaut Aring -40 -KPX Ohungarumlaut Atilde -40 -KPX Ohungarumlaut T -40 -KPX Ohungarumlaut Tcaron -40 -KPX Ohungarumlaut Tcommaaccent -40 -KPX Ohungarumlaut V -50 -KPX Ohungarumlaut W -50 -KPX Ohungarumlaut X -40 -KPX Ohungarumlaut Y -50 -KPX Ohungarumlaut Yacute -50 -KPX Ohungarumlaut Ydieresis -50 -KPX Omacron A -40 -KPX Omacron Aacute -40 -KPX Omacron Abreve -40 -KPX Omacron Acircumflex -40 -KPX Omacron Adieresis -40 -KPX Omacron Agrave -40 -KPX Omacron Amacron -40 -KPX Omacron Aogonek -40 -KPX Omacron Aring -40 -KPX Omacron Atilde -40 -KPX Omacron T -40 -KPX Omacron Tcaron -40 -KPX Omacron Tcommaaccent -40 -KPX Omacron V -50 -KPX Omacron W -50 -KPX Omacron X -40 -KPX Omacron Y -50 -KPX Omacron Yacute -50 -KPX Omacron Ydieresis -50 -KPX Oslash A -40 -KPX Oslash Aacute -40 -KPX Oslash Abreve -40 -KPX Oslash Acircumflex -40 -KPX Oslash Adieresis -40 -KPX Oslash Agrave -40 -KPX Oslash Amacron -40 -KPX Oslash Aogonek -40 -KPX Oslash Aring -40 -KPX Oslash Atilde -40 -KPX Oslash T -40 -KPX Oslash Tcaron -40 -KPX Oslash Tcommaaccent -40 -KPX Oslash V -50 -KPX Oslash W -50 -KPX Oslash X -40 -KPX Oslash Y -50 -KPX Oslash Yacute -50 -KPX Oslash Ydieresis -50 -KPX Otilde A -40 -KPX Otilde Aacute -40 -KPX Otilde Abreve -40 -KPX Otilde Acircumflex -40 -KPX Otilde Adieresis -40 -KPX Otilde Agrave -40 -KPX Otilde Amacron -40 -KPX Otilde Aogonek -40 -KPX Otilde Aring -40 -KPX Otilde Atilde -40 -KPX Otilde T -40 -KPX Otilde Tcaron -40 -KPX Otilde Tcommaaccent -40 -KPX Otilde V -50 -KPX Otilde W -50 -KPX Otilde X -40 -KPX Otilde Y -50 -KPX Otilde Yacute -50 -KPX Otilde Ydieresis -50 -KPX P A -74 -KPX P Aacute -74 -KPX P Abreve -74 -KPX P Acircumflex -74 -KPX P Adieresis -74 -KPX P Agrave -74 -KPX P Amacron -74 -KPX P Aogonek -74 -KPX P Aring -74 -KPX P Atilde -74 -KPX P a -10 -KPX P aacute -10 -KPX P abreve -10 -KPX P acircumflex -10 -KPX P adieresis -10 -KPX P agrave -10 -KPX P amacron -10 -KPX P aogonek -10 -KPX P aring -10 -KPX P atilde -10 -KPX P comma -92 -KPX P e -20 -KPX P eacute -20 -KPX P ecaron -20 -KPX P ecircumflex -20 -KPX P edieresis -20 -KPX P edotaccent -20 -KPX P egrave -20 -KPX P emacron -20 -KPX P eogonek -20 -KPX P o -20 -KPX P oacute -20 -KPX P ocircumflex -20 -KPX P odieresis -20 -KPX P ograve -20 -KPX P ohungarumlaut -20 -KPX P omacron -20 -KPX P oslash -20 -KPX P otilde -20 -KPX P period -110 -KPX Q U -10 -KPX Q Uacute -10 -KPX Q Ucircumflex -10 -KPX Q Udieresis -10 -KPX Q Ugrave -10 -KPX Q Uhungarumlaut -10 -KPX Q Umacron -10 -KPX Q Uogonek -10 -KPX Q Uring -10 -KPX Q period -20 -KPX R O -30 -KPX R Oacute -30 -KPX R Ocircumflex -30 -KPX R Odieresis -30 -KPX R Ograve -30 -KPX R Ohungarumlaut -30 -KPX R Omacron -30 -KPX R Oslash -30 -KPX R Otilde -30 -KPX R T -40 -KPX R Tcaron -40 -KPX R Tcommaaccent -40 -KPX R U -30 -KPX R Uacute -30 -KPX R Ucircumflex -30 -KPX R Udieresis -30 -KPX R Ugrave -30 -KPX R Uhungarumlaut -30 -KPX R Umacron -30 -KPX R Uogonek -30 -KPX R Uring -30 -KPX R V -55 -KPX R W -35 -KPX R Y -35 -KPX R Yacute -35 -KPX R Ydieresis -35 -KPX Racute O -30 -KPX Racute Oacute -30 -KPX Racute Ocircumflex -30 -KPX Racute Odieresis -30 -KPX Racute Ograve -30 -KPX Racute Ohungarumlaut -30 -KPX Racute Omacron -30 -KPX Racute Oslash -30 -KPX Racute Otilde -30 -KPX Racute T -40 -KPX Racute Tcaron -40 -KPX Racute Tcommaaccent -40 -KPX Racute U -30 -KPX Racute Uacute -30 -KPX Racute Ucircumflex -30 -KPX Racute Udieresis -30 -KPX Racute Ugrave -30 -KPX Racute Uhungarumlaut -30 -KPX Racute Umacron -30 -KPX Racute Uogonek -30 -KPX Racute Uring -30 -KPX Racute V -55 -KPX Racute W -35 -KPX Racute Y -35 -KPX Racute Yacute -35 -KPX Racute Ydieresis -35 -KPX Rcaron O -30 -KPX Rcaron Oacute -30 -KPX Rcaron Ocircumflex -30 -KPX Rcaron Odieresis -30 -KPX Rcaron Ograve -30 -KPX Rcaron Ohungarumlaut -30 -KPX Rcaron Omacron -30 -KPX Rcaron Oslash -30 -KPX Rcaron Otilde -30 -KPX Rcaron T -40 -KPX Rcaron Tcaron -40 -KPX Rcaron Tcommaaccent -40 -KPX Rcaron U -30 -KPX Rcaron Uacute -30 -KPX Rcaron Ucircumflex -30 -KPX Rcaron Udieresis -30 -KPX Rcaron Ugrave -30 -KPX Rcaron Uhungarumlaut -30 -KPX Rcaron Umacron -30 -KPX Rcaron Uogonek -30 -KPX Rcaron Uring -30 -KPX Rcaron V -55 -KPX Rcaron W -35 -KPX Rcaron Y -35 -KPX Rcaron Yacute -35 -KPX Rcaron Ydieresis -35 -KPX Rcommaaccent O -30 -KPX Rcommaaccent Oacute -30 -KPX Rcommaaccent Ocircumflex -30 -KPX Rcommaaccent Odieresis -30 -KPX Rcommaaccent Ograve -30 -KPX Rcommaaccent Ohungarumlaut -30 -KPX Rcommaaccent Omacron -30 -KPX Rcommaaccent Oslash -30 -KPX Rcommaaccent Otilde -30 -KPX Rcommaaccent T -40 -KPX Rcommaaccent Tcaron -40 -KPX Rcommaaccent Tcommaaccent -40 -KPX Rcommaaccent U -30 -KPX Rcommaaccent Uacute -30 -KPX Rcommaaccent Ucircumflex -30 -KPX Rcommaaccent Udieresis -30 -KPX Rcommaaccent Ugrave -30 -KPX Rcommaaccent Uhungarumlaut -30 -KPX Rcommaaccent Umacron -30 -KPX Rcommaaccent Uogonek -30 -KPX Rcommaaccent Uring -30 -KPX Rcommaaccent V -55 -KPX Rcommaaccent W -35 -KPX Rcommaaccent Y -35 -KPX Rcommaaccent Yacute -35 -KPX Rcommaaccent Ydieresis -35 -KPX T A -90 -KPX T Aacute -90 -KPX T Abreve -90 -KPX T Acircumflex -90 -KPX T Adieresis -90 -KPX T Agrave -90 -KPX T Amacron -90 -KPX T Aogonek -90 -KPX T Aring -90 -KPX T Atilde -90 -KPX T O -18 -KPX T Oacute -18 -KPX T Ocircumflex -18 -KPX T Odieresis -18 -KPX T Ograve -18 -KPX T Ohungarumlaut -18 -KPX T Omacron -18 -KPX T Oslash -18 -KPX T Otilde -18 -KPX T a -92 -KPX T aacute -92 -KPX T abreve -52 -KPX T acircumflex -52 -KPX T adieresis -52 -KPX T agrave -52 -KPX T amacron -52 -KPX T aogonek -92 -KPX T aring -92 -KPX T atilde -52 -KPX T colon -74 -KPX T comma -74 -KPX T e -92 -KPX T eacute -92 -KPX T ecaron -92 -KPX T ecircumflex -92 -KPX T edieresis -52 -KPX T edotaccent -92 -KPX T egrave -52 -KPX T emacron -52 -KPX T eogonek -92 -KPX T hyphen -92 -KPX T i -18 -KPX T iacute -18 -KPX T iogonek -18 -KPX T o -92 -KPX T oacute -92 -KPX T ocircumflex -92 -KPX T odieresis -92 -KPX T ograve -92 -KPX T ohungarumlaut -92 -KPX T omacron -92 -KPX T oslash -92 -KPX T otilde -92 -KPX T period -90 -KPX T r -74 -KPX T racute -74 -KPX T rcaron -74 -KPX T rcommaaccent -74 -KPX T semicolon -74 -KPX T u -92 -KPX T uacute -92 -KPX T ucircumflex -92 -KPX T udieresis -92 -KPX T ugrave -92 -KPX T uhungarumlaut -92 -KPX T umacron -92 -KPX T uogonek -92 -KPX T uring -92 -KPX T w -74 -KPX T y -34 -KPX T yacute -34 -KPX T ydieresis -34 -KPX Tcaron A -90 -KPX Tcaron Aacute -90 -KPX Tcaron Abreve -90 -KPX Tcaron Acircumflex -90 -KPX Tcaron Adieresis -90 -KPX Tcaron Agrave -90 -KPX Tcaron Amacron -90 -KPX Tcaron Aogonek -90 -KPX Tcaron Aring -90 -KPX Tcaron Atilde -90 -KPX Tcaron O -18 -KPX Tcaron Oacute -18 -KPX Tcaron Ocircumflex -18 -KPX Tcaron Odieresis -18 -KPX Tcaron Ograve -18 -KPX Tcaron Ohungarumlaut -18 -KPX Tcaron Omacron -18 -KPX Tcaron Oslash -18 -KPX Tcaron Otilde -18 -KPX Tcaron a -92 -KPX Tcaron aacute -92 -KPX Tcaron abreve -52 -KPX Tcaron acircumflex -52 -KPX Tcaron adieresis -52 -KPX Tcaron agrave -52 -KPX Tcaron amacron -52 -KPX Tcaron aogonek -92 -KPX Tcaron aring -92 -KPX Tcaron atilde -52 -KPX Tcaron colon -74 -KPX Tcaron comma -74 -KPX Tcaron e -92 -KPX Tcaron eacute -92 -KPX Tcaron ecaron -92 -KPX Tcaron ecircumflex -92 -KPX Tcaron edieresis -52 -KPX Tcaron edotaccent -92 -KPX Tcaron egrave -52 -KPX Tcaron emacron -52 -KPX Tcaron eogonek -92 -KPX Tcaron hyphen -92 -KPX Tcaron i -18 -KPX Tcaron iacute -18 -KPX Tcaron iogonek -18 -KPX Tcaron o -92 -KPX Tcaron oacute -92 -KPX Tcaron ocircumflex -92 -KPX Tcaron odieresis -92 -KPX Tcaron ograve -92 -KPX Tcaron ohungarumlaut -92 -KPX Tcaron omacron -92 -KPX Tcaron oslash -92 -KPX Tcaron otilde -92 -KPX Tcaron period -90 -KPX Tcaron r -74 -KPX Tcaron racute -74 -KPX Tcaron rcaron -74 -KPX Tcaron rcommaaccent -74 -KPX Tcaron semicolon -74 -KPX Tcaron u -92 -KPX Tcaron uacute -92 -KPX Tcaron ucircumflex -92 -KPX Tcaron udieresis -92 -KPX Tcaron ugrave -92 -KPX Tcaron uhungarumlaut -92 -KPX Tcaron umacron -92 -KPX Tcaron uogonek -92 -KPX Tcaron uring -92 -KPX Tcaron w -74 -KPX Tcaron y -34 -KPX Tcaron yacute -34 -KPX Tcaron ydieresis -34 -KPX Tcommaaccent A -90 -KPX Tcommaaccent Aacute -90 -KPX Tcommaaccent Abreve -90 -KPX Tcommaaccent Acircumflex -90 -KPX Tcommaaccent Adieresis -90 -KPX Tcommaaccent Agrave -90 -KPX Tcommaaccent Amacron -90 -KPX Tcommaaccent Aogonek -90 -KPX Tcommaaccent Aring -90 -KPX Tcommaaccent Atilde -90 -KPX Tcommaaccent O -18 -KPX Tcommaaccent Oacute -18 -KPX Tcommaaccent Ocircumflex -18 -KPX Tcommaaccent Odieresis -18 -KPX Tcommaaccent Ograve -18 -KPX Tcommaaccent Ohungarumlaut -18 -KPX Tcommaaccent Omacron -18 -KPX Tcommaaccent Oslash -18 -KPX Tcommaaccent Otilde -18 -KPX Tcommaaccent a -92 -KPX Tcommaaccent aacute -92 -KPX Tcommaaccent abreve -52 -KPX Tcommaaccent acircumflex -52 -KPX Tcommaaccent adieresis -52 -KPX Tcommaaccent agrave -52 -KPX Tcommaaccent amacron -52 -KPX Tcommaaccent aogonek -92 -KPX Tcommaaccent aring -92 -KPX Tcommaaccent atilde -52 -KPX Tcommaaccent colon -74 -KPX Tcommaaccent comma -74 -KPX Tcommaaccent e -92 -KPX Tcommaaccent eacute -92 -KPX Tcommaaccent ecaron -92 -KPX Tcommaaccent ecircumflex -92 -KPX Tcommaaccent edieresis -52 -KPX Tcommaaccent edotaccent -92 -KPX Tcommaaccent egrave -52 -KPX Tcommaaccent emacron -52 -KPX Tcommaaccent eogonek -92 -KPX Tcommaaccent hyphen -92 -KPX Tcommaaccent i -18 -KPX Tcommaaccent iacute -18 -KPX Tcommaaccent iogonek -18 -KPX Tcommaaccent o -92 -KPX Tcommaaccent oacute -92 -KPX Tcommaaccent ocircumflex -92 -KPX Tcommaaccent odieresis -92 -KPX Tcommaaccent ograve -92 -KPX Tcommaaccent ohungarumlaut -92 -KPX Tcommaaccent omacron -92 -KPX Tcommaaccent oslash -92 -KPX Tcommaaccent otilde -92 -KPX Tcommaaccent period -90 -KPX Tcommaaccent r -74 -KPX Tcommaaccent racute -74 -KPX Tcommaaccent rcaron -74 -KPX Tcommaaccent rcommaaccent -74 -KPX Tcommaaccent semicolon -74 -KPX Tcommaaccent u -92 -KPX Tcommaaccent uacute -92 -KPX Tcommaaccent ucircumflex -92 -KPX Tcommaaccent udieresis -92 -KPX Tcommaaccent ugrave -92 -KPX Tcommaaccent uhungarumlaut -92 -KPX Tcommaaccent umacron -92 -KPX Tcommaaccent uogonek -92 -KPX Tcommaaccent uring -92 -KPX Tcommaaccent w -74 -KPX Tcommaaccent y -34 -KPX Tcommaaccent yacute -34 -KPX Tcommaaccent ydieresis -34 -KPX U A -60 -KPX U Aacute -60 -KPX U Abreve -60 -KPX U Acircumflex -60 -KPX U Adieresis -60 -KPX U Agrave -60 -KPX U Amacron -60 -KPX U Aogonek -60 -KPX U Aring -60 -KPX U Atilde -60 -KPX U comma -50 -KPX U period -50 -KPX Uacute A -60 -KPX Uacute Aacute -60 -KPX Uacute Abreve -60 -KPX Uacute Acircumflex -60 -KPX Uacute Adieresis -60 -KPX Uacute Agrave -60 -KPX Uacute Amacron -60 -KPX Uacute Aogonek -60 -KPX Uacute Aring -60 -KPX Uacute Atilde -60 -KPX Uacute comma -50 -KPX Uacute period -50 -KPX Ucircumflex A -60 -KPX Ucircumflex Aacute -60 -KPX Ucircumflex Abreve -60 -KPX Ucircumflex Acircumflex -60 -KPX Ucircumflex Adieresis -60 -KPX Ucircumflex Agrave -60 -KPX Ucircumflex Amacron -60 -KPX Ucircumflex Aogonek -60 -KPX Ucircumflex Aring -60 -KPX Ucircumflex Atilde -60 -KPX Ucircumflex comma -50 -KPX Ucircumflex period -50 -KPX Udieresis A -60 -KPX Udieresis Aacute -60 -KPX Udieresis Abreve -60 -KPX Udieresis Acircumflex -60 -KPX Udieresis Adieresis -60 -KPX Udieresis Agrave -60 -KPX Udieresis Amacron -60 -KPX Udieresis Aogonek -60 -KPX Udieresis Aring -60 -KPX Udieresis Atilde -60 -KPX Udieresis comma -50 -KPX Udieresis period -50 -KPX Ugrave A -60 -KPX Ugrave Aacute -60 -KPX Ugrave Abreve -60 -KPX Ugrave Acircumflex -60 -KPX Ugrave Adieresis -60 -KPX Ugrave Agrave -60 -KPX Ugrave Amacron -60 -KPX Ugrave Aogonek -60 -KPX Ugrave Aring -60 -KPX Ugrave Atilde -60 -KPX Ugrave comma -50 -KPX Ugrave period -50 -KPX Uhungarumlaut A -60 -KPX Uhungarumlaut Aacute -60 -KPX Uhungarumlaut Abreve -60 -KPX Uhungarumlaut Acircumflex -60 -KPX Uhungarumlaut Adieresis -60 -KPX Uhungarumlaut Agrave -60 -KPX Uhungarumlaut Amacron -60 -KPX Uhungarumlaut Aogonek -60 -KPX Uhungarumlaut Aring -60 -KPX Uhungarumlaut Atilde -60 -KPX Uhungarumlaut comma -50 -KPX Uhungarumlaut period -50 -KPX Umacron A -60 -KPX Umacron Aacute -60 -KPX Umacron Abreve -60 -KPX Umacron Acircumflex -60 -KPX Umacron Adieresis -60 -KPX Umacron Agrave -60 -KPX Umacron Amacron -60 -KPX Umacron Aogonek -60 -KPX Umacron Aring -60 -KPX Umacron Atilde -60 -KPX Umacron comma -50 -KPX Umacron period -50 -KPX Uogonek A -60 -KPX Uogonek Aacute -60 -KPX Uogonek Abreve -60 -KPX Uogonek Acircumflex -60 -KPX Uogonek Adieresis -60 -KPX Uogonek Agrave -60 -KPX Uogonek Amacron -60 -KPX Uogonek Aogonek -60 -KPX Uogonek Aring -60 -KPX Uogonek Atilde -60 -KPX Uogonek comma -50 -KPX Uogonek period -50 -KPX Uring A -60 -KPX Uring Aacute -60 -KPX Uring Abreve -60 -KPX Uring Acircumflex -60 -KPX Uring Adieresis -60 -KPX Uring Agrave -60 -KPX Uring Amacron -60 -KPX Uring Aogonek -60 -KPX Uring Aring -60 -KPX Uring Atilde -60 -KPX Uring comma -50 -KPX Uring period -50 -KPX V A -135 -KPX V Aacute -135 -KPX V Abreve -135 -KPX V Acircumflex -135 -KPX V Adieresis -135 -KPX V Agrave -135 -KPX V Amacron -135 -KPX V Aogonek -135 -KPX V Aring -135 -KPX V Atilde -135 -KPX V G -30 -KPX V Gbreve -30 -KPX V Gcommaaccent -30 -KPX V O -45 -KPX V Oacute -45 -KPX V Ocircumflex -45 -KPX V Odieresis -45 -KPX V Ograve -45 -KPX V Ohungarumlaut -45 -KPX V Omacron -45 -KPX V Oslash -45 -KPX V Otilde -45 -KPX V a -92 -KPX V aacute -92 -KPX V abreve -92 -KPX V acircumflex -92 -KPX V adieresis -92 -KPX V agrave -92 -KPX V amacron -92 -KPX V aogonek -92 -KPX V aring -92 -KPX V atilde -92 -KPX V colon -92 -KPX V comma -129 -KPX V e -100 -KPX V eacute -100 -KPX V ecaron -100 -KPX V ecircumflex -100 -KPX V edieresis -100 -KPX V edotaccent -100 -KPX V egrave -100 -KPX V emacron -100 -KPX V eogonek -100 -KPX V hyphen -74 -KPX V i -37 -KPX V iacute -37 -KPX V icircumflex -37 -KPX V idieresis -37 -KPX V igrave -37 -KPX V imacron -37 -KPX V iogonek -37 -KPX V o -100 -KPX V oacute -100 -KPX V ocircumflex -100 -KPX V odieresis -100 -KPX V ograve -100 -KPX V ohungarumlaut -100 -KPX V omacron -100 -KPX V oslash -100 -KPX V otilde -100 -KPX V period -145 -KPX V semicolon -92 -KPX V u -92 -KPX V uacute -92 -KPX V ucircumflex -92 -KPX V udieresis -92 -KPX V ugrave -92 -KPX V uhungarumlaut -92 -KPX V umacron -92 -KPX V uogonek -92 -KPX V uring -92 -KPX W A -120 -KPX W Aacute -120 -KPX W Abreve -120 -KPX W Acircumflex -120 -KPX W Adieresis -120 -KPX W Agrave -120 -KPX W Amacron -120 -KPX W Aogonek -120 -KPX W Aring -120 -KPX W Atilde -120 -KPX W O -10 -KPX W Oacute -10 -KPX W Ocircumflex -10 -KPX W Odieresis -10 -KPX W Ograve -10 -KPX W Ohungarumlaut -10 -KPX W Omacron -10 -KPX W Oslash -10 -KPX W Otilde -10 -KPX W a -65 -KPX W aacute -65 -KPX W abreve -65 -KPX W acircumflex -65 -KPX W adieresis -65 -KPX W agrave -65 -KPX W amacron -65 -KPX W aogonek -65 -KPX W aring -65 -KPX W atilde -65 -KPX W colon -55 -KPX W comma -92 -KPX W e -65 -KPX W eacute -65 -KPX W ecaron -65 -KPX W ecircumflex -65 -KPX W edieresis -65 -KPX W edotaccent -65 -KPX W egrave -65 -KPX W emacron -65 -KPX W eogonek -65 -KPX W hyphen -37 -KPX W i -18 -KPX W iacute -18 -KPX W iogonek -18 -KPX W o -75 -KPX W oacute -75 -KPX W ocircumflex -75 -KPX W odieresis -75 -KPX W ograve -75 -KPX W ohungarumlaut -75 -KPX W omacron -75 -KPX W oslash -75 -KPX W otilde -75 -KPX W period -92 -KPX W semicolon -55 -KPX W u -50 -KPX W uacute -50 -KPX W ucircumflex -50 -KPX W udieresis -50 -KPX W ugrave -50 -KPX W uhungarumlaut -50 -KPX W umacron -50 -KPX W uogonek -50 -KPX W uring -50 -KPX W y -60 -KPX W yacute -60 -KPX W ydieresis -60 -KPX Y A -110 -KPX Y Aacute -110 -KPX Y Abreve -110 -KPX Y Acircumflex -110 -KPX Y Adieresis -110 -KPX Y Agrave -110 -KPX Y Amacron -110 -KPX Y Aogonek -110 -KPX Y Aring -110 -KPX Y Atilde -110 -KPX Y O -35 -KPX Y Oacute -35 -KPX Y Ocircumflex -35 -KPX Y Odieresis -35 -KPX Y Ograve -35 -KPX Y Ohungarumlaut -35 -KPX Y Omacron -35 -KPX Y Oslash -35 -KPX Y Otilde -35 -KPX Y a -85 -KPX Y aacute -85 -KPX Y abreve -85 -KPX Y acircumflex -85 -KPX Y adieresis -85 -KPX Y agrave -85 -KPX Y amacron -85 -KPX Y aogonek -85 -KPX Y aring -85 -KPX Y atilde -85 -KPX Y colon -92 -KPX Y comma -92 -KPX Y e -111 -KPX Y eacute -111 -KPX Y ecaron -111 -KPX Y ecircumflex -111 -KPX Y edieresis -71 -KPX Y edotaccent -111 -KPX Y egrave -71 -KPX Y emacron -71 -KPX Y eogonek -111 -KPX Y hyphen -92 -KPX Y i -37 -KPX Y iacute -37 -KPX Y iogonek -37 -KPX Y o -111 -KPX Y oacute -111 -KPX Y ocircumflex -111 -KPX Y odieresis -111 -KPX Y ograve -111 -KPX Y ohungarumlaut -111 -KPX Y omacron -111 -KPX Y oslash -111 -KPX Y otilde -111 -KPX Y period -92 -KPX Y semicolon -92 -KPX Y u -92 -KPX Y uacute -92 -KPX Y ucircumflex -92 -KPX Y udieresis -92 -KPX Y ugrave -92 -KPX Y uhungarumlaut -92 -KPX Y umacron -92 -KPX Y uogonek -92 -KPX Y uring -92 -KPX Yacute A -110 -KPX Yacute Aacute -110 -KPX Yacute Abreve -110 -KPX Yacute Acircumflex -110 -KPX Yacute Adieresis -110 -KPX Yacute Agrave -110 -KPX Yacute Amacron -110 -KPX Yacute Aogonek -110 -KPX Yacute Aring -110 -KPX Yacute Atilde -110 -KPX Yacute O -35 -KPX Yacute Oacute -35 -KPX Yacute Ocircumflex -35 -KPX Yacute Odieresis -35 -KPX Yacute Ograve -35 -KPX Yacute Ohungarumlaut -35 -KPX Yacute Omacron -35 -KPX Yacute Oslash -35 -KPX Yacute Otilde -35 -KPX Yacute a -85 -KPX Yacute aacute -85 -KPX Yacute abreve -85 -KPX Yacute acircumflex -85 -KPX Yacute adieresis -85 -KPX Yacute agrave -85 -KPX Yacute amacron -85 -KPX Yacute aogonek -85 -KPX Yacute aring -85 -KPX Yacute atilde -85 -KPX Yacute colon -92 -KPX Yacute comma -92 -KPX Yacute e -111 -KPX Yacute eacute -111 -KPX Yacute ecaron -111 -KPX Yacute ecircumflex -111 -KPX Yacute edieresis -71 -KPX Yacute edotaccent -111 -KPX Yacute egrave -71 -KPX Yacute emacron -71 -KPX Yacute eogonek -111 -KPX Yacute hyphen -92 -KPX Yacute i -37 -KPX Yacute iacute -37 -KPX Yacute iogonek -37 -KPX Yacute o -111 -KPX Yacute oacute -111 -KPX Yacute ocircumflex -111 -KPX Yacute odieresis -111 -KPX Yacute ograve -111 -KPX Yacute ohungarumlaut -111 -KPX Yacute omacron -111 -KPX Yacute oslash -111 -KPX Yacute otilde -111 -KPX Yacute period -92 -KPX Yacute semicolon -92 -KPX Yacute u -92 -KPX Yacute uacute -92 -KPX Yacute ucircumflex -92 -KPX Yacute udieresis -92 -KPX Yacute ugrave -92 -KPX Yacute uhungarumlaut -92 -KPX Yacute umacron -92 -KPX Yacute uogonek -92 -KPX Yacute uring -92 -KPX Ydieresis A -110 -KPX Ydieresis Aacute -110 -KPX Ydieresis Abreve -110 -KPX Ydieresis Acircumflex -110 -KPX Ydieresis Adieresis -110 -KPX Ydieresis Agrave -110 -KPX Ydieresis Amacron -110 -KPX Ydieresis Aogonek -110 -KPX Ydieresis Aring -110 -KPX Ydieresis Atilde -110 -KPX Ydieresis O -35 -KPX Ydieresis Oacute -35 -KPX Ydieresis Ocircumflex -35 -KPX Ydieresis Odieresis -35 -KPX Ydieresis Ograve -35 -KPX Ydieresis Ohungarumlaut -35 -KPX Ydieresis Omacron -35 -KPX Ydieresis Oslash -35 -KPX Ydieresis Otilde -35 -KPX Ydieresis a -85 -KPX Ydieresis aacute -85 -KPX Ydieresis abreve -85 -KPX Ydieresis acircumflex -85 -KPX Ydieresis adieresis -85 -KPX Ydieresis agrave -85 -KPX Ydieresis amacron -85 -KPX Ydieresis aogonek -85 -KPX Ydieresis aring -85 -KPX Ydieresis atilde -85 -KPX Ydieresis colon -92 -KPX Ydieresis comma -92 -KPX Ydieresis e -111 -KPX Ydieresis eacute -111 -KPX Ydieresis ecaron -111 -KPX Ydieresis ecircumflex -111 -KPX Ydieresis edieresis -71 -KPX Ydieresis edotaccent -111 -KPX Ydieresis egrave -71 -KPX Ydieresis emacron -71 -KPX Ydieresis eogonek -111 -KPX Ydieresis hyphen -92 -KPX Ydieresis i -37 -KPX Ydieresis iacute -37 -KPX Ydieresis iogonek -37 -KPX Ydieresis o -111 -KPX Ydieresis oacute -111 -KPX Ydieresis ocircumflex -111 -KPX Ydieresis odieresis -111 -KPX Ydieresis ograve -111 -KPX Ydieresis ohungarumlaut -111 -KPX Ydieresis omacron -111 -KPX Ydieresis oslash -111 -KPX Ydieresis otilde -111 -KPX Ydieresis period -92 -KPX Ydieresis semicolon -92 -KPX Ydieresis u -92 -KPX Ydieresis uacute -92 -KPX Ydieresis ucircumflex -92 -KPX Ydieresis udieresis -92 -KPX Ydieresis ugrave -92 -KPX Ydieresis uhungarumlaut -92 -KPX Ydieresis umacron -92 -KPX Ydieresis uogonek -92 -KPX Ydieresis uring -92 -KPX a v -25 -KPX aacute v -25 -KPX abreve v -25 -KPX acircumflex v -25 -KPX adieresis v -25 -KPX agrave v -25 -KPX amacron v -25 -KPX aogonek v -25 -KPX aring v -25 -KPX atilde v -25 -KPX b b -10 -KPX b period -40 -KPX b u -20 -KPX b uacute -20 -KPX b ucircumflex -20 -KPX b udieresis -20 -KPX b ugrave -20 -KPX b uhungarumlaut -20 -KPX b umacron -20 -KPX b uogonek -20 -KPX b uring -20 -KPX b v -15 -KPX comma quotedblright -45 -KPX comma quoteright -55 -KPX d w -15 -KPX dcroat w -15 -KPX e v -15 -KPX eacute v -15 -KPX ecaron v -15 -KPX ecircumflex v -15 -KPX edieresis v -15 -KPX edotaccent v -15 -KPX egrave v -15 -KPX emacron v -15 -KPX eogonek v -15 -KPX f comma -15 -KPX f dotlessi -35 -KPX f i -25 -KPX f o -25 -KPX f oacute -25 -KPX f ocircumflex -25 -KPX f odieresis -25 -KPX f ograve -25 -KPX f ohungarumlaut -25 -KPX f omacron -25 -KPX f oslash -25 -KPX f otilde -25 -KPX f period -15 -KPX f quotedblright 50 -KPX f quoteright 55 -KPX g period -15 -KPX gbreve period -15 -KPX gcommaaccent period -15 -KPX h y -15 -KPX h yacute -15 -KPX h ydieresis -15 -KPX i v -10 -KPX iacute v -10 -KPX icircumflex v -10 -KPX idieresis v -10 -KPX igrave v -10 -KPX imacron v -10 -KPX iogonek v -10 -KPX k e -10 -KPX k eacute -10 -KPX k ecaron -10 -KPX k ecircumflex -10 -KPX k edieresis -10 -KPX k edotaccent -10 -KPX k egrave -10 -KPX k emacron -10 -KPX k eogonek -10 -KPX k o -15 -KPX k oacute -15 -KPX k ocircumflex -15 -KPX k odieresis -15 -KPX k ograve -15 -KPX k ohungarumlaut -15 -KPX k omacron -15 -KPX k oslash -15 -KPX k otilde -15 -KPX k y -15 -KPX k yacute -15 -KPX k ydieresis -15 -KPX kcommaaccent e -10 -KPX kcommaaccent eacute -10 -KPX kcommaaccent ecaron -10 -KPX kcommaaccent ecircumflex -10 -KPX kcommaaccent edieresis -10 -KPX kcommaaccent edotaccent -10 -KPX kcommaaccent egrave -10 -KPX kcommaaccent emacron -10 -KPX kcommaaccent eogonek -10 -KPX kcommaaccent o -15 -KPX kcommaaccent oacute -15 -KPX kcommaaccent ocircumflex -15 -KPX kcommaaccent odieresis -15 -KPX kcommaaccent ograve -15 -KPX kcommaaccent ohungarumlaut -15 -KPX kcommaaccent omacron -15 -KPX kcommaaccent oslash -15 -KPX kcommaaccent otilde -15 -KPX kcommaaccent y -15 -KPX kcommaaccent yacute -15 -KPX kcommaaccent ydieresis -15 -KPX n v -40 -KPX nacute v -40 -KPX ncaron v -40 -KPX ncommaaccent v -40 -KPX ntilde v -40 -KPX o v -10 -KPX o w -10 -KPX oacute v -10 -KPX oacute w -10 -KPX ocircumflex v -10 -KPX ocircumflex w -10 -KPX odieresis v -10 -KPX odieresis w -10 -KPX ograve v -10 -KPX ograve w -10 -KPX ohungarumlaut v -10 -KPX ohungarumlaut w -10 -KPX omacron v -10 -KPX omacron w -10 -KPX oslash v -10 -KPX oslash w -10 -KPX otilde v -10 -KPX otilde w -10 -KPX period quotedblright -55 -KPX period quoteright -55 -KPX quotedblleft A -10 -KPX quotedblleft Aacute -10 -KPX quotedblleft Abreve -10 -KPX quotedblleft Acircumflex -10 -KPX quotedblleft Adieresis -10 -KPX quotedblleft Agrave -10 -KPX quotedblleft Amacron -10 -KPX quotedblleft Aogonek -10 -KPX quotedblleft Aring -10 -KPX quotedblleft Atilde -10 -KPX quoteleft A -10 -KPX quoteleft Aacute -10 -KPX quoteleft Abreve -10 -KPX quoteleft Acircumflex -10 -KPX quoteleft Adieresis -10 -KPX quoteleft Agrave -10 -KPX quoteleft Amacron -10 -KPX quoteleft Aogonek -10 -KPX quoteleft Aring -10 -KPX quoteleft Atilde -10 -KPX quoteleft quoteleft -63 -KPX quoteright d -20 -KPX quoteright dcroat -20 -KPX quoteright quoteright -63 -KPX quoteright r -20 -KPX quoteright racute -20 -KPX quoteright rcaron -20 -KPX quoteright rcommaaccent -20 -KPX quoteright s -37 -KPX quoteright sacute -37 -KPX quoteright scaron -37 -KPX quoteright scedilla -37 -KPX quoteright scommaaccent -37 -KPX quoteright space -74 -KPX quoteright v -20 -KPX r c -18 -KPX r cacute -18 -KPX r ccaron -18 -KPX r ccedilla -18 -KPX r comma -92 -KPX r e -18 -KPX r eacute -18 -KPX r ecaron -18 -KPX r ecircumflex -18 -KPX r edieresis -18 -KPX r edotaccent -18 -KPX r egrave -18 -KPX r emacron -18 -KPX r eogonek -18 -KPX r g -10 -KPX r gbreve -10 -KPX r gcommaaccent -10 -KPX r hyphen -37 -KPX r n -15 -KPX r nacute -15 -KPX r ncaron -15 -KPX r ncommaaccent -15 -KPX r ntilde -15 -KPX r o -18 -KPX r oacute -18 -KPX r ocircumflex -18 -KPX r odieresis -18 -KPX r ograve -18 -KPX r ohungarumlaut -18 -KPX r omacron -18 -KPX r oslash -18 -KPX r otilde -18 -KPX r p -10 -KPX r period -100 -KPX r q -18 -KPX r v -10 -KPX racute c -18 -KPX racute cacute -18 -KPX racute ccaron -18 -KPX racute ccedilla -18 -KPX racute comma -92 -KPX racute e -18 -KPX racute eacute -18 -KPX racute ecaron -18 -KPX racute ecircumflex -18 -KPX racute edieresis -18 -KPX racute edotaccent -18 -KPX racute egrave -18 -KPX racute emacron -18 -KPX racute eogonek -18 -KPX racute g -10 -KPX racute gbreve -10 -KPX racute gcommaaccent -10 -KPX racute hyphen -37 -KPX racute n -15 -KPX racute nacute -15 -KPX racute ncaron -15 -KPX racute ncommaaccent -15 -KPX racute ntilde -15 -KPX racute o -18 -KPX racute oacute -18 -KPX racute ocircumflex -18 -KPX racute odieresis -18 -KPX racute ograve -18 -KPX racute ohungarumlaut -18 -KPX racute omacron -18 -KPX racute oslash -18 -KPX racute otilde -18 -KPX racute p -10 -KPX racute period -100 -KPX racute q -18 -KPX racute v -10 -KPX rcaron c -18 -KPX rcaron cacute -18 -KPX rcaron ccaron -18 -KPX rcaron ccedilla -18 -KPX rcaron comma -92 -KPX rcaron e -18 -KPX rcaron eacute -18 -KPX rcaron ecaron -18 -KPX rcaron ecircumflex -18 -KPX rcaron edieresis -18 -KPX rcaron edotaccent -18 -KPX rcaron egrave -18 -KPX rcaron emacron -18 -KPX rcaron eogonek -18 -KPX rcaron g -10 -KPX rcaron gbreve -10 -KPX rcaron gcommaaccent -10 -KPX rcaron hyphen -37 -KPX rcaron n -15 -KPX rcaron nacute -15 -KPX rcaron ncaron -15 -KPX rcaron ncommaaccent -15 -KPX rcaron ntilde -15 -KPX rcaron o -18 -KPX rcaron oacute -18 -KPX rcaron ocircumflex -18 -KPX rcaron odieresis -18 -KPX rcaron ograve -18 -KPX rcaron ohungarumlaut -18 -KPX rcaron omacron -18 -KPX rcaron oslash -18 -KPX rcaron otilde -18 -KPX rcaron p -10 -KPX rcaron period -100 -KPX rcaron q -18 -KPX rcaron v -10 -KPX rcommaaccent c -18 -KPX rcommaaccent cacute -18 -KPX rcommaaccent ccaron -18 -KPX rcommaaccent ccedilla -18 -KPX rcommaaccent comma -92 -KPX rcommaaccent e -18 -KPX rcommaaccent eacute -18 -KPX rcommaaccent ecaron -18 -KPX rcommaaccent ecircumflex -18 -KPX rcommaaccent edieresis -18 -KPX rcommaaccent edotaccent -18 -KPX rcommaaccent egrave -18 -KPX rcommaaccent emacron -18 -KPX rcommaaccent eogonek -18 -KPX rcommaaccent g -10 -KPX rcommaaccent gbreve -10 -KPX rcommaaccent gcommaaccent -10 -KPX rcommaaccent hyphen -37 -KPX rcommaaccent n -15 -KPX rcommaaccent nacute -15 -KPX rcommaaccent ncaron -15 -KPX rcommaaccent ncommaaccent -15 -KPX rcommaaccent ntilde -15 -KPX rcommaaccent o -18 -KPX rcommaaccent oacute -18 -KPX rcommaaccent ocircumflex -18 -KPX rcommaaccent odieresis -18 -KPX rcommaaccent ograve -18 -KPX rcommaaccent ohungarumlaut -18 -KPX rcommaaccent omacron -18 -KPX rcommaaccent oslash -18 -KPX rcommaaccent otilde -18 -KPX rcommaaccent p -10 -KPX rcommaaccent period -100 -KPX rcommaaccent q -18 -KPX rcommaaccent v -10 -KPX space A -55 -KPX space Aacute -55 -KPX space Abreve -55 -KPX space Acircumflex -55 -KPX space Adieresis -55 -KPX space Agrave -55 -KPX space Amacron -55 -KPX space Aogonek -55 -KPX space Aring -55 -KPX space Atilde -55 -KPX space T -30 -KPX space Tcaron -30 -KPX space Tcommaaccent -30 -KPX space V -45 -KPX space W -30 -KPX space Y -55 -KPX space Yacute -55 -KPX space Ydieresis -55 -KPX v a -10 -KPX v aacute -10 -KPX v abreve -10 -KPX v acircumflex -10 -KPX v adieresis -10 -KPX v agrave -10 -KPX v amacron -10 -KPX v aogonek -10 -KPX v aring -10 -KPX v atilde -10 -KPX v comma -55 -KPX v e -10 -KPX v eacute -10 -KPX v ecaron -10 -KPX v ecircumflex -10 -KPX v edieresis -10 -KPX v edotaccent -10 -KPX v egrave -10 -KPX v emacron -10 -KPX v eogonek -10 -KPX v o -10 -KPX v oacute -10 -KPX v ocircumflex -10 -KPX v odieresis -10 -KPX v ograve -10 -KPX v ohungarumlaut -10 -KPX v omacron -10 -KPX v oslash -10 -KPX v otilde -10 -KPX v period -70 -KPX w comma -55 -KPX w o -10 -KPX w oacute -10 -KPX w ocircumflex -10 -KPX w odieresis -10 -KPX w ograve -10 -KPX w ohungarumlaut -10 -KPX w omacron -10 -KPX w oslash -10 -KPX w otilde -10 -KPX w period -70 -KPX y comma -55 -KPX y e -10 -KPX y eacute -10 -KPX y ecaron -10 -KPX y ecircumflex -10 -KPX y edieresis -10 -KPX y edotaccent -10 -KPX y egrave -10 -KPX y emacron -10 -KPX y eogonek -10 -KPX y o -25 -KPX y oacute -25 -KPX y ocircumflex -25 -KPX y odieresis -25 -KPX y ograve -25 -KPX y ohungarumlaut -25 -KPX y omacron -25 -KPX y oslash -25 -KPX y otilde -25 -KPX y period -70 -KPX yacute comma -55 -KPX yacute e -10 -KPX yacute eacute -10 -KPX yacute ecaron -10 -KPX yacute ecircumflex -10 -KPX yacute edieresis -10 -KPX yacute edotaccent -10 -KPX yacute egrave -10 -KPX yacute emacron -10 -KPX yacute eogonek -10 -KPX yacute o -25 -KPX yacute oacute -25 -KPX yacute ocircumflex -25 -KPX yacute odieresis -25 -KPX yacute ograve -25 -KPX yacute ohungarumlaut -25 -KPX yacute omacron -25 -KPX yacute oslash -25 -KPX yacute otilde -25 -KPX yacute period -70 -KPX ydieresis comma -55 -KPX ydieresis e -10 -KPX ydieresis eacute -10 -KPX ydieresis ecaron -10 -KPX ydieresis ecircumflex -10 -KPX ydieresis edieresis -10 -KPX ydieresis edotaccent -10 -KPX ydieresis egrave -10 -KPX ydieresis emacron -10 -KPX ydieresis eogonek -10 -KPX ydieresis o -25 -KPX ydieresis oacute -25 -KPX ydieresis ocircumflex -25 -KPX ydieresis odieresis -25 -KPX ydieresis ograve -25 -KPX ydieresis ohungarumlaut -25 -KPX ydieresis omacron -25 -KPX ydieresis oslash -25 -KPX ydieresis otilde -25 -KPX ydieresis period -70 -EndKernPairs -EndKernData -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Times-BoldItalic.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Times-BoldItalic.afm deleted file mode 100644 index 2301dfd..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Times-BoldItalic.afm +++ /dev/null @@ -1,2384 +0,0 @@ -StartFontMetrics 4.1 -Comment Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Thu May 1 13:04:06 1997 -Comment UniqueID 43066 -Comment VMusage 45874 56899 -FontName Times-BoldItalic -FullName Times Bold Italic -FamilyName Times -Weight Bold -ItalicAngle -15 -IsFixedPitch false -CharacterSet ExtendedRoman -FontBBox -200 -218 996 921 -UnderlinePosition -100 -UnderlineThickness 50 -Version 002.000 -Notice Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.Times is a trademark of Linotype-Hell AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 669 -XHeight 462 -Ascender 683 -Descender -217 -StdHW 42 -StdVW 121 -StartCharMetrics 315 -C 32 ; WX 250 ; N space ; B 0 0 0 0 ; -C 33 ; WX 389 ; N exclam ; B 67 -13 370 684 ; -C 34 ; WX 555 ; N quotedbl ; B 136 398 536 685 ; -C 35 ; WX 500 ; N numbersign ; B -33 0 533 700 ; -C 36 ; WX 500 ; N dollar ; B -20 -100 497 733 ; -C 37 ; WX 833 ; N percent ; B 39 -10 793 692 ; -C 38 ; WX 778 ; N ampersand ; B 5 -19 699 682 ; -C 39 ; WX 333 ; N quoteright ; B 98 369 302 685 ; -C 40 ; WX 333 ; N parenleft ; B 28 -179 344 685 ; -C 41 ; WX 333 ; N parenright ; B -44 -179 271 685 ; -C 42 ; WX 500 ; N asterisk ; B 65 249 456 685 ; -C 43 ; WX 570 ; N plus ; B 33 0 537 506 ; -C 44 ; WX 250 ; N comma ; B -60 -182 144 134 ; -C 45 ; WX 333 ; N hyphen ; B 2 166 271 282 ; -C 46 ; WX 250 ; N period ; B -9 -13 139 135 ; -C 47 ; WX 278 ; N slash ; B -64 -18 342 685 ; -C 48 ; WX 500 ; N zero ; B 17 -14 477 683 ; -C 49 ; WX 500 ; N one ; B 5 0 419 683 ; -C 50 ; WX 500 ; N two ; B -27 0 446 683 ; -C 51 ; WX 500 ; N three ; B -15 -13 450 683 ; -C 52 ; WX 500 ; N four ; B -15 0 503 683 ; -C 53 ; WX 500 ; N five ; B -11 -13 487 669 ; -C 54 ; WX 500 ; N six ; B 23 -15 509 679 ; -C 55 ; WX 500 ; N seven ; B 52 0 525 669 ; -C 56 ; WX 500 ; N eight ; B 3 -13 476 683 ; -C 57 ; WX 500 ; N nine ; B -12 -10 475 683 ; -C 58 ; WX 333 ; N colon ; B 23 -13 264 459 ; -C 59 ; WX 333 ; N semicolon ; B -25 -183 264 459 ; -C 60 ; WX 570 ; N less ; B 31 -8 539 514 ; -C 61 ; WX 570 ; N equal ; B 33 107 537 399 ; -C 62 ; WX 570 ; N greater ; B 31 -8 539 514 ; -C 63 ; WX 500 ; N question ; B 79 -13 470 684 ; -C 64 ; WX 832 ; N at ; B 63 -18 770 685 ; -C 65 ; WX 667 ; N A ; B -67 0 593 683 ; -C 66 ; WX 667 ; N B ; B -24 0 624 669 ; -C 67 ; WX 667 ; N C ; B 32 -18 677 685 ; -C 68 ; WX 722 ; N D ; B -46 0 685 669 ; -C 69 ; WX 667 ; N E ; B -27 0 653 669 ; -C 70 ; WX 667 ; N F ; B -13 0 660 669 ; -C 71 ; WX 722 ; N G ; B 21 -18 706 685 ; -C 72 ; WX 778 ; N H ; B -24 0 799 669 ; -C 73 ; WX 389 ; N I ; B -32 0 406 669 ; -C 74 ; WX 500 ; N J ; B -46 -99 524 669 ; -C 75 ; WX 667 ; N K ; B -21 0 702 669 ; -C 76 ; WX 611 ; N L ; B -22 0 590 669 ; -C 77 ; WX 889 ; N M ; B -29 -12 917 669 ; -C 78 ; WX 722 ; N N ; B -27 -15 748 669 ; -C 79 ; WX 722 ; N O ; B 27 -18 691 685 ; -C 80 ; WX 611 ; N P ; B -27 0 613 669 ; -C 81 ; WX 722 ; N Q ; B 27 -208 691 685 ; -C 82 ; WX 667 ; N R ; B -29 0 623 669 ; -C 83 ; WX 556 ; N S ; B 2 -18 526 685 ; -C 84 ; WX 611 ; N T ; B 50 0 650 669 ; -C 85 ; WX 722 ; N U ; B 67 -18 744 669 ; -C 86 ; WX 667 ; N V ; B 65 -18 715 669 ; -C 87 ; WX 889 ; N W ; B 65 -18 940 669 ; -C 88 ; WX 667 ; N X ; B -24 0 694 669 ; -C 89 ; WX 611 ; N Y ; B 73 0 659 669 ; -C 90 ; WX 611 ; N Z ; B -11 0 590 669 ; -C 91 ; WX 333 ; N bracketleft ; B -37 -159 362 674 ; -C 92 ; WX 278 ; N backslash ; B -1 -18 279 685 ; -C 93 ; WX 333 ; N bracketright ; B -56 -157 343 674 ; -C 94 ; WX 570 ; N asciicircum ; B 67 304 503 669 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 333 ; N quoteleft ; B 128 369 332 685 ; -C 97 ; WX 500 ; N a ; B -21 -14 455 462 ; -C 98 ; WX 500 ; N b ; B -14 -13 444 699 ; -C 99 ; WX 444 ; N c ; B -5 -13 392 462 ; -C 100 ; WX 500 ; N d ; B -21 -13 517 699 ; -C 101 ; WX 444 ; N e ; B 5 -13 398 462 ; -C 102 ; WX 333 ; N f ; B -169 -205 446 698 ; L i fi ; L l fl ; -C 103 ; WX 500 ; N g ; B -52 -203 478 462 ; -C 104 ; WX 556 ; N h ; B -13 -9 498 699 ; -C 105 ; WX 278 ; N i ; B 2 -9 263 684 ; -C 106 ; WX 278 ; N j ; B -189 -207 279 684 ; -C 107 ; WX 500 ; N k ; B -23 -8 483 699 ; -C 108 ; WX 278 ; N l ; B 2 -9 290 699 ; -C 109 ; WX 778 ; N m ; B -14 -9 722 462 ; -C 110 ; WX 556 ; N n ; B -6 -9 493 462 ; -C 111 ; WX 500 ; N o ; B -3 -13 441 462 ; -C 112 ; WX 500 ; N p ; B -120 -205 446 462 ; -C 113 ; WX 500 ; N q ; B 1 -205 471 462 ; -C 114 ; WX 389 ; N r ; B -21 0 389 462 ; -C 115 ; WX 389 ; N s ; B -19 -13 333 462 ; -C 116 ; WX 278 ; N t ; B -11 -9 281 594 ; -C 117 ; WX 556 ; N u ; B 15 -9 492 462 ; -C 118 ; WX 444 ; N v ; B 16 -13 401 462 ; -C 119 ; WX 667 ; N w ; B 16 -13 614 462 ; -C 120 ; WX 500 ; N x ; B -46 -13 469 462 ; -C 121 ; WX 444 ; N y ; B -94 -205 392 462 ; -C 122 ; WX 389 ; N z ; B -43 -78 368 449 ; -C 123 ; WX 348 ; N braceleft ; B 5 -187 436 686 ; -C 124 ; WX 220 ; N bar ; B 66 -218 154 782 ; -C 125 ; WX 348 ; N braceright ; B -129 -187 302 686 ; -C 126 ; WX 570 ; N asciitilde ; B 54 173 516 333 ; -C 161 ; WX 389 ; N exclamdown ; B 19 -205 322 492 ; -C 162 ; WX 500 ; N cent ; B 42 -143 439 576 ; -C 163 ; WX 500 ; N sterling ; B -32 -12 510 683 ; -C 164 ; WX 167 ; N fraction ; B -169 -14 324 683 ; -C 165 ; WX 500 ; N yen ; B 33 0 628 669 ; -C 166 ; WX 500 ; N florin ; B -87 -156 537 707 ; -C 167 ; WX 500 ; N section ; B 36 -143 459 685 ; -C 168 ; WX 500 ; N currency ; B -26 34 526 586 ; -C 169 ; WX 278 ; N quotesingle ; B 128 398 268 685 ; -C 170 ; WX 500 ; N quotedblleft ; B 53 369 513 685 ; -C 171 ; WX 500 ; N guillemotleft ; B 12 32 468 415 ; -C 172 ; WX 333 ; N guilsinglleft ; B 32 32 303 415 ; -C 173 ; WX 333 ; N guilsinglright ; B 10 32 281 415 ; -C 174 ; WX 556 ; N fi ; B -188 -205 514 703 ; -C 175 ; WX 556 ; N fl ; B -186 -205 553 704 ; -C 177 ; WX 500 ; N endash ; B -40 178 477 269 ; -C 178 ; WX 500 ; N dagger ; B 91 -145 494 685 ; -C 179 ; WX 500 ; N daggerdbl ; B 10 -139 493 685 ; -C 180 ; WX 250 ; N periodcentered ; B 51 257 199 405 ; -C 182 ; WX 500 ; N paragraph ; B -57 -193 562 669 ; -C 183 ; WX 350 ; N bullet ; B 0 175 350 525 ; -C 184 ; WX 333 ; N quotesinglbase ; B -5 -182 199 134 ; -C 185 ; WX 500 ; N quotedblbase ; B -57 -182 403 134 ; -C 186 ; WX 500 ; N quotedblright ; B 53 369 513 685 ; -C 187 ; WX 500 ; N guillemotright ; B 12 32 468 415 ; -C 188 ; WX 1000 ; N ellipsis ; B 40 -13 852 135 ; -C 189 ; WX 1000 ; N perthousand ; B 7 -29 996 706 ; -C 191 ; WX 500 ; N questiondown ; B 30 -205 421 492 ; -C 193 ; WX 333 ; N grave ; B 85 516 297 697 ; -C 194 ; WX 333 ; N acute ; B 139 516 379 697 ; -C 195 ; WX 333 ; N circumflex ; B 40 516 367 690 ; -C 196 ; WX 333 ; N tilde ; B 48 536 407 655 ; -C 197 ; WX 333 ; N macron ; B 51 553 393 623 ; -C 198 ; WX 333 ; N breve ; B 71 516 387 678 ; -C 199 ; WX 333 ; N dotaccent ; B 163 550 298 684 ; -C 200 ; WX 333 ; N dieresis ; B 55 550 402 684 ; -C 202 ; WX 333 ; N ring ; B 127 516 340 729 ; -C 203 ; WX 333 ; N cedilla ; B -80 -218 156 5 ; -C 205 ; WX 333 ; N hungarumlaut ; B 69 516 498 697 ; -C 206 ; WX 333 ; N ogonek ; B 15 -183 244 34 ; -C 207 ; WX 333 ; N caron ; B 79 516 411 690 ; -C 208 ; WX 1000 ; N emdash ; B -40 178 977 269 ; -C 225 ; WX 944 ; N AE ; B -64 0 918 669 ; -C 227 ; WX 266 ; N ordfeminine ; B 16 399 330 685 ; -C 232 ; WX 611 ; N Lslash ; B -22 0 590 669 ; -C 233 ; WX 722 ; N Oslash ; B 27 -125 691 764 ; -C 234 ; WX 944 ; N OE ; B 23 -8 946 677 ; -C 235 ; WX 300 ; N ordmasculine ; B 56 400 347 685 ; -C 241 ; WX 722 ; N ae ; B -5 -13 673 462 ; -C 245 ; WX 278 ; N dotlessi ; B 2 -9 238 462 ; -C 248 ; WX 278 ; N lslash ; B -7 -9 307 699 ; -C 249 ; WX 500 ; N oslash ; B -3 -119 441 560 ; -C 250 ; WX 722 ; N oe ; B 6 -13 674 462 ; -C 251 ; WX 500 ; N germandbls ; B -200 -200 473 705 ; -C -1 ; WX 389 ; N Idieresis ; B -32 0 450 862 ; -C -1 ; WX 444 ; N eacute ; B 5 -13 435 697 ; -C -1 ; WX 500 ; N abreve ; B -21 -14 471 678 ; -C -1 ; WX 556 ; N uhungarumlaut ; B 15 -9 610 697 ; -C -1 ; WX 444 ; N ecaron ; B 5 -13 467 690 ; -C -1 ; WX 611 ; N Ydieresis ; B 73 0 659 862 ; -C -1 ; WX 570 ; N divide ; B 33 -29 537 535 ; -C -1 ; WX 611 ; N Yacute ; B 73 0 659 904 ; -C -1 ; WX 667 ; N Acircumflex ; B -67 0 593 897 ; -C -1 ; WX 500 ; N aacute ; B -21 -14 463 697 ; -C -1 ; WX 722 ; N Ucircumflex ; B 67 -18 744 897 ; -C -1 ; WX 444 ; N yacute ; B -94 -205 435 697 ; -C -1 ; WX 389 ; N scommaaccent ; B -19 -218 333 462 ; -C -1 ; WX 444 ; N ecircumflex ; B 5 -13 423 690 ; -C -1 ; WX 722 ; N Uring ; B 67 -18 744 921 ; -C -1 ; WX 722 ; N Udieresis ; B 67 -18 744 862 ; -C -1 ; WX 500 ; N aogonek ; B -21 -183 455 462 ; -C -1 ; WX 722 ; N Uacute ; B 67 -18 744 904 ; -C -1 ; WX 556 ; N uogonek ; B 15 -183 492 462 ; -C -1 ; WX 667 ; N Edieresis ; B -27 0 653 862 ; -C -1 ; WX 722 ; N Dcroat ; B -31 0 700 669 ; -C -1 ; WX 250 ; N commaaccent ; B -36 -218 131 -50 ; -C -1 ; WX 747 ; N copyright ; B 30 -18 718 685 ; -C -1 ; WX 667 ; N Emacron ; B -27 0 653 830 ; -C -1 ; WX 444 ; N ccaron ; B -5 -13 467 690 ; -C -1 ; WX 500 ; N aring ; B -21 -14 455 729 ; -C -1 ; WX 722 ; N Ncommaaccent ; B -27 -218 748 669 ; -C -1 ; WX 278 ; N lacute ; B 2 -9 392 904 ; -C -1 ; WX 500 ; N agrave ; B -21 -14 455 697 ; -C -1 ; WX 611 ; N Tcommaaccent ; B 50 -218 650 669 ; -C -1 ; WX 667 ; N Cacute ; B 32 -18 677 904 ; -C -1 ; WX 500 ; N atilde ; B -21 -14 491 655 ; -C -1 ; WX 667 ; N Edotaccent ; B -27 0 653 862 ; -C -1 ; WX 389 ; N scaron ; B -19 -13 424 690 ; -C -1 ; WX 389 ; N scedilla ; B -19 -218 333 462 ; -C -1 ; WX 278 ; N iacute ; B 2 -9 352 697 ; -C -1 ; WX 494 ; N lozenge ; B 10 0 484 745 ; -C -1 ; WX 667 ; N Rcaron ; B -29 0 623 897 ; -C -1 ; WX 722 ; N Gcommaaccent ; B 21 -218 706 685 ; -C -1 ; WX 556 ; N ucircumflex ; B 15 -9 492 690 ; -C -1 ; WX 500 ; N acircumflex ; B -21 -14 455 690 ; -C -1 ; WX 667 ; N Amacron ; B -67 0 593 830 ; -C -1 ; WX 389 ; N rcaron ; B -21 0 424 690 ; -C -1 ; WX 444 ; N ccedilla ; B -5 -218 392 462 ; -C -1 ; WX 611 ; N Zdotaccent ; B -11 0 590 862 ; -C -1 ; WX 611 ; N Thorn ; B -27 0 573 669 ; -C -1 ; WX 722 ; N Omacron ; B 27 -18 691 830 ; -C -1 ; WX 667 ; N Racute ; B -29 0 623 904 ; -C -1 ; WX 556 ; N Sacute ; B 2 -18 531 904 ; -C -1 ; WX 608 ; N dcaron ; B -21 -13 675 708 ; -C -1 ; WX 722 ; N Umacron ; B 67 -18 744 830 ; -C -1 ; WX 556 ; N uring ; B 15 -9 492 729 ; -C -1 ; WX 300 ; N threesuperior ; B 17 265 321 683 ; -C -1 ; WX 722 ; N Ograve ; B 27 -18 691 904 ; -C -1 ; WX 667 ; N Agrave ; B -67 0 593 904 ; -C -1 ; WX 667 ; N Abreve ; B -67 0 593 885 ; -C -1 ; WX 570 ; N multiply ; B 48 16 522 490 ; -C -1 ; WX 556 ; N uacute ; B 15 -9 492 697 ; -C -1 ; WX 611 ; N Tcaron ; B 50 0 650 897 ; -C -1 ; WX 494 ; N partialdiff ; B 11 -21 494 750 ; -C -1 ; WX 444 ; N ydieresis ; B -94 -205 443 655 ; -C -1 ; WX 722 ; N Nacute ; B -27 -15 748 904 ; -C -1 ; WX 278 ; N icircumflex ; B -3 -9 324 690 ; -C -1 ; WX 667 ; N Ecircumflex ; B -27 0 653 897 ; -C -1 ; WX 500 ; N adieresis ; B -21 -14 476 655 ; -C -1 ; WX 444 ; N edieresis ; B 5 -13 448 655 ; -C -1 ; WX 444 ; N cacute ; B -5 -13 435 697 ; -C -1 ; WX 556 ; N nacute ; B -6 -9 493 697 ; -C -1 ; WX 556 ; N umacron ; B 15 -9 492 623 ; -C -1 ; WX 722 ; N Ncaron ; B -27 -15 748 897 ; -C -1 ; WX 389 ; N Iacute ; B -32 0 432 904 ; -C -1 ; WX 570 ; N plusminus ; B 33 0 537 506 ; -C -1 ; WX 220 ; N brokenbar ; B 66 -143 154 707 ; -C -1 ; WX 747 ; N registered ; B 30 -18 718 685 ; -C -1 ; WX 722 ; N Gbreve ; B 21 -18 706 885 ; -C -1 ; WX 389 ; N Idotaccent ; B -32 0 406 862 ; -C -1 ; WX 600 ; N summation ; B 14 -10 585 706 ; -C -1 ; WX 667 ; N Egrave ; B -27 0 653 904 ; -C -1 ; WX 389 ; N racute ; B -21 0 407 697 ; -C -1 ; WX 500 ; N omacron ; B -3 -13 462 623 ; -C -1 ; WX 611 ; N Zacute ; B -11 0 590 904 ; -C -1 ; WX 611 ; N Zcaron ; B -11 0 590 897 ; -C -1 ; WX 549 ; N greaterequal ; B 26 0 523 704 ; -C -1 ; WX 722 ; N Eth ; B -31 0 700 669 ; -C -1 ; WX 667 ; N Ccedilla ; B 32 -218 677 685 ; -C -1 ; WX 278 ; N lcommaaccent ; B -42 -218 290 699 ; -C -1 ; WX 366 ; N tcaron ; B -11 -9 434 754 ; -C -1 ; WX 444 ; N eogonek ; B 5 -183 398 462 ; -C -1 ; WX 722 ; N Uogonek ; B 67 -183 744 669 ; -C -1 ; WX 667 ; N Aacute ; B -67 0 593 904 ; -C -1 ; WX 667 ; N Adieresis ; B -67 0 593 862 ; -C -1 ; WX 444 ; N egrave ; B 5 -13 398 697 ; -C -1 ; WX 389 ; N zacute ; B -43 -78 407 697 ; -C -1 ; WX 278 ; N iogonek ; B -20 -183 263 684 ; -C -1 ; WX 722 ; N Oacute ; B 27 -18 691 904 ; -C -1 ; WX 500 ; N oacute ; B -3 -13 463 697 ; -C -1 ; WX 500 ; N amacron ; B -21 -14 467 623 ; -C -1 ; WX 389 ; N sacute ; B -19 -13 407 697 ; -C -1 ; WX 278 ; N idieresis ; B 2 -9 364 655 ; -C -1 ; WX 722 ; N Ocircumflex ; B 27 -18 691 897 ; -C -1 ; WX 722 ; N Ugrave ; B 67 -18 744 904 ; -C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ; -C -1 ; WX 500 ; N thorn ; B -120 -205 446 699 ; -C -1 ; WX 300 ; N twosuperior ; B 2 274 313 683 ; -C -1 ; WX 722 ; N Odieresis ; B 27 -18 691 862 ; -C -1 ; WX 576 ; N mu ; B -60 -207 516 449 ; -C -1 ; WX 278 ; N igrave ; B 2 -9 259 697 ; -C -1 ; WX 500 ; N ohungarumlaut ; B -3 -13 582 697 ; -C -1 ; WX 667 ; N Eogonek ; B -27 -183 653 669 ; -C -1 ; WX 500 ; N dcroat ; B -21 -13 552 699 ; -C -1 ; WX 750 ; N threequarters ; B 7 -14 726 683 ; -C -1 ; WX 556 ; N Scedilla ; B 2 -218 526 685 ; -C -1 ; WX 382 ; N lcaron ; B 2 -9 448 708 ; -C -1 ; WX 667 ; N Kcommaaccent ; B -21 -218 702 669 ; -C -1 ; WX 611 ; N Lacute ; B -22 0 590 904 ; -C -1 ; WX 1000 ; N trademark ; B 32 263 968 669 ; -C -1 ; WX 444 ; N edotaccent ; B 5 -13 398 655 ; -C -1 ; WX 389 ; N Igrave ; B -32 0 406 904 ; -C -1 ; WX 389 ; N Imacron ; B -32 0 461 830 ; -C -1 ; WX 611 ; N Lcaron ; B -22 0 671 718 ; -C -1 ; WX 750 ; N onehalf ; B -9 -14 723 683 ; -C -1 ; WX 549 ; N lessequal ; B 29 0 526 704 ; -C -1 ; WX 500 ; N ocircumflex ; B -3 -13 451 690 ; -C -1 ; WX 556 ; N ntilde ; B -6 -9 504 655 ; -C -1 ; WX 722 ; N Uhungarumlaut ; B 67 -18 744 904 ; -C -1 ; WX 667 ; N Eacute ; B -27 0 653 904 ; -C -1 ; WX 444 ; N emacron ; B 5 -13 439 623 ; -C -1 ; WX 500 ; N gbreve ; B -52 -203 478 678 ; -C -1 ; WX 750 ; N onequarter ; B 7 -14 721 683 ; -C -1 ; WX 556 ; N Scaron ; B 2 -18 553 897 ; -C -1 ; WX 556 ; N Scommaaccent ; B 2 -218 526 685 ; -C -1 ; WX 722 ; N Ohungarumlaut ; B 27 -18 723 904 ; -C -1 ; WX 400 ; N degree ; B 83 397 369 683 ; -C -1 ; WX 500 ; N ograve ; B -3 -13 441 697 ; -C -1 ; WX 667 ; N Ccaron ; B 32 -18 677 897 ; -C -1 ; WX 556 ; N ugrave ; B 15 -9 492 697 ; -C -1 ; WX 549 ; N radical ; B 10 -46 512 850 ; -C -1 ; WX 722 ; N Dcaron ; B -46 0 685 897 ; -C -1 ; WX 389 ; N rcommaaccent ; B -67 -218 389 462 ; -C -1 ; WX 722 ; N Ntilde ; B -27 -15 748 862 ; -C -1 ; WX 500 ; N otilde ; B -3 -13 491 655 ; -C -1 ; WX 667 ; N Rcommaaccent ; B -29 -218 623 669 ; -C -1 ; WX 611 ; N Lcommaaccent ; B -22 -218 590 669 ; -C -1 ; WX 667 ; N Atilde ; B -67 0 593 862 ; -C -1 ; WX 667 ; N Aogonek ; B -67 -183 604 683 ; -C -1 ; WX 667 ; N Aring ; B -67 0 593 921 ; -C -1 ; WX 722 ; N Otilde ; B 27 -18 691 862 ; -C -1 ; WX 389 ; N zdotaccent ; B -43 -78 368 655 ; -C -1 ; WX 667 ; N Ecaron ; B -27 0 653 897 ; -C -1 ; WX 389 ; N Iogonek ; B -32 -183 406 669 ; -C -1 ; WX 500 ; N kcommaaccent ; B -23 -218 483 699 ; -C -1 ; WX 606 ; N minus ; B 51 209 555 297 ; -C -1 ; WX 389 ; N Icircumflex ; B -32 0 450 897 ; -C -1 ; WX 556 ; N ncaron ; B -6 -9 523 690 ; -C -1 ; WX 278 ; N tcommaaccent ; B -62 -218 281 594 ; -C -1 ; WX 606 ; N logicalnot ; B 51 108 555 399 ; -C -1 ; WX 500 ; N odieresis ; B -3 -13 471 655 ; -C -1 ; WX 556 ; N udieresis ; B 15 -9 499 655 ; -C -1 ; WX 549 ; N notequal ; B 15 -49 540 570 ; -C -1 ; WX 500 ; N gcommaaccent ; B -52 -203 478 767 ; -C -1 ; WX 500 ; N eth ; B -3 -13 454 699 ; -C -1 ; WX 389 ; N zcaron ; B -43 -78 424 690 ; -C -1 ; WX 556 ; N ncommaaccent ; B -6 -218 493 462 ; -C -1 ; WX 300 ; N onesuperior ; B 30 274 301 683 ; -C -1 ; WX 278 ; N imacron ; B 2 -9 294 623 ; -C -1 ; WX 500 ; N Euro ; B 0 0 0 0 ; -EndCharMetrics -StartKernData -StartKernPairs 2038 -KPX A C -65 -KPX A Cacute -65 -KPX A Ccaron -65 -KPX A Ccedilla -65 -KPX A G -60 -KPX A Gbreve -60 -KPX A Gcommaaccent -60 -KPX A O -50 -KPX A Oacute -50 -KPX A Ocircumflex -50 -KPX A Odieresis -50 -KPX A Ograve -50 -KPX A Ohungarumlaut -50 -KPX A Omacron -50 -KPX A Oslash -50 -KPX A Otilde -50 -KPX A Q -55 -KPX A T -55 -KPX A Tcaron -55 -KPX A Tcommaaccent -55 -KPX A U -50 -KPX A Uacute -50 -KPX A Ucircumflex -50 -KPX A Udieresis -50 -KPX A Ugrave -50 -KPX A Uhungarumlaut -50 -KPX A Umacron -50 -KPX A Uogonek -50 -KPX A Uring -50 -KPX A V -95 -KPX A W -100 -KPX A Y -70 -KPX A Yacute -70 -KPX A Ydieresis -70 -KPX A quoteright -74 -KPX A u -30 -KPX A uacute -30 -KPX A ucircumflex -30 -KPX A udieresis -30 -KPX A ugrave -30 -KPX A uhungarumlaut -30 -KPX A umacron -30 -KPX A uogonek -30 -KPX A uring -30 -KPX A v -74 -KPX A w -74 -KPX A y -74 -KPX A yacute -74 -KPX A ydieresis -74 -KPX Aacute C -65 -KPX Aacute Cacute -65 -KPX Aacute Ccaron -65 -KPX Aacute Ccedilla -65 -KPX Aacute G -60 -KPX Aacute Gbreve -60 -KPX Aacute Gcommaaccent -60 -KPX Aacute O -50 -KPX Aacute Oacute -50 -KPX Aacute Ocircumflex -50 -KPX Aacute Odieresis -50 -KPX Aacute Ograve -50 -KPX Aacute Ohungarumlaut -50 -KPX Aacute Omacron -50 -KPX Aacute Oslash -50 -KPX Aacute Otilde -50 -KPX Aacute Q -55 -KPX Aacute T -55 -KPX Aacute Tcaron -55 -KPX Aacute Tcommaaccent -55 -KPX Aacute U -50 -KPX Aacute Uacute -50 -KPX Aacute Ucircumflex -50 -KPX Aacute Udieresis -50 -KPX Aacute Ugrave -50 -KPX Aacute Uhungarumlaut -50 -KPX Aacute Umacron -50 -KPX Aacute Uogonek -50 -KPX Aacute Uring -50 -KPX Aacute V -95 -KPX Aacute W -100 -KPX Aacute Y -70 -KPX Aacute Yacute -70 -KPX Aacute Ydieresis -70 -KPX Aacute quoteright -74 -KPX Aacute u -30 -KPX Aacute uacute -30 -KPX Aacute ucircumflex -30 -KPX Aacute udieresis -30 -KPX Aacute ugrave -30 -KPX Aacute uhungarumlaut -30 -KPX Aacute umacron -30 -KPX Aacute uogonek -30 -KPX Aacute uring -30 -KPX Aacute v -74 -KPX Aacute w -74 -KPX Aacute y -74 -KPX Aacute yacute -74 -KPX Aacute ydieresis -74 -KPX Abreve C -65 -KPX Abreve Cacute -65 -KPX Abreve Ccaron -65 -KPX Abreve Ccedilla -65 -KPX Abreve G -60 -KPX Abreve Gbreve -60 -KPX Abreve Gcommaaccent -60 -KPX Abreve O -50 -KPX Abreve Oacute -50 -KPX Abreve Ocircumflex -50 -KPX Abreve Odieresis -50 -KPX Abreve Ograve -50 -KPX Abreve Ohungarumlaut -50 -KPX Abreve Omacron -50 -KPX Abreve Oslash -50 -KPX Abreve Otilde -50 -KPX Abreve Q -55 -KPX Abreve T -55 -KPX Abreve Tcaron -55 -KPX Abreve Tcommaaccent -55 -KPX Abreve U -50 -KPX Abreve Uacute -50 -KPX Abreve Ucircumflex -50 -KPX Abreve Udieresis -50 -KPX Abreve Ugrave -50 -KPX Abreve Uhungarumlaut -50 -KPX Abreve Umacron -50 -KPX Abreve Uogonek -50 -KPX Abreve Uring -50 -KPX Abreve V -95 -KPX Abreve W -100 -KPX Abreve Y -70 -KPX Abreve Yacute -70 -KPX Abreve Ydieresis -70 -KPX Abreve quoteright -74 -KPX Abreve u -30 -KPX Abreve uacute -30 -KPX Abreve ucircumflex -30 -KPX Abreve udieresis -30 -KPX Abreve ugrave -30 -KPX Abreve uhungarumlaut -30 -KPX Abreve umacron -30 -KPX Abreve uogonek -30 -KPX Abreve uring -30 -KPX Abreve v -74 -KPX Abreve w -74 -KPX Abreve y -74 -KPX Abreve yacute -74 -KPX Abreve ydieresis -74 -KPX Acircumflex C -65 -KPX Acircumflex Cacute -65 -KPX Acircumflex Ccaron -65 -KPX Acircumflex Ccedilla -65 -KPX Acircumflex G -60 -KPX Acircumflex Gbreve -60 -KPX Acircumflex Gcommaaccent -60 -KPX Acircumflex O -50 -KPX Acircumflex Oacute -50 -KPX Acircumflex Ocircumflex -50 -KPX Acircumflex Odieresis -50 -KPX Acircumflex Ograve -50 -KPX Acircumflex Ohungarumlaut -50 -KPX Acircumflex Omacron -50 -KPX Acircumflex Oslash -50 -KPX Acircumflex Otilde -50 -KPX Acircumflex Q -55 -KPX Acircumflex T -55 -KPX Acircumflex Tcaron -55 -KPX Acircumflex Tcommaaccent -55 -KPX Acircumflex U -50 -KPX Acircumflex Uacute -50 -KPX Acircumflex Ucircumflex -50 -KPX Acircumflex Udieresis -50 -KPX Acircumflex Ugrave -50 -KPX Acircumflex Uhungarumlaut -50 -KPX Acircumflex Umacron -50 -KPX Acircumflex Uogonek -50 -KPX Acircumflex Uring -50 -KPX Acircumflex V -95 -KPX Acircumflex W -100 -KPX Acircumflex Y -70 -KPX Acircumflex Yacute -70 -KPX Acircumflex Ydieresis -70 -KPX Acircumflex quoteright -74 -KPX Acircumflex u -30 -KPX Acircumflex uacute -30 -KPX Acircumflex ucircumflex -30 -KPX Acircumflex udieresis -30 -KPX Acircumflex ugrave -30 -KPX Acircumflex uhungarumlaut -30 -KPX Acircumflex umacron -30 -KPX Acircumflex uogonek -30 -KPX Acircumflex uring -30 -KPX Acircumflex v -74 -KPX Acircumflex w -74 -KPX Acircumflex y -74 -KPX Acircumflex yacute -74 -KPX Acircumflex ydieresis -74 -KPX Adieresis C -65 -KPX Adieresis Cacute -65 -KPX Adieresis Ccaron -65 -KPX Adieresis Ccedilla -65 -KPX Adieresis G -60 -KPX Adieresis Gbreve -60 -KPX Adieresis Gcommaaccent -60 -KPX Adieresis O -50 -KPX Adieresis Oacute -50 -KPX Adieresis Ocircumflex -50 -KPX Adieresis Odieresis -50 -KPX Adieresis Ograve -50 -KPX Adieresis Ohungarumlaut -50 -KPX Adieresis Omacron -50 -KPX Adieresis Oslash -50 -KPX Adieresis Otilde -50 -KPX Adieresis Q -55 -KPX Adieresis T -55 -KPX Adieresis Tcaron -55 -KPX Adieresis Tcommaaccent -55 -KPX Adieresis U -50 -KPX Adieresis Uacute -50 -KPX Adieresis Ucircumflex -50 -KPX Adieresis Udieresis -50 -KPX Adieresis Ugrave -50 -KPX Adieresis Uhungarumlaut -50 -KPX Adieresis Umacron -50 -KPX Adieresis Uogonek -50 -KPX Adieresis Uring -50 -KPX Adieresis V -95 -KPX Adieresis W -100 -KPX Adieresis Y -70 -KPX Adieresis Yacute -70 -KPX Adieresis Ydieresis -70 -KPX Adieresis quoteright -74 -KPX Adieresis u -30 -KPX Adieresis uacute -30 -KPX Adieresis ucircumflex -30 -KPX Adieresis udieresis -30 -KPX Adieresis ugrave -30 -KPX Adieresis uhungarumlaut -30 -KPX Adieresis umacron -30 -KPX Adieresis uogonek -30 -KPX Adieresis uring -30 -KPX Adieresis v -74 -KPX Adieresis w -74 -KPX Adieresis y -74 -KPX Adieresis yacute -74 -KPX Adieresis ydieresis -74 -KPX Agrave C -65 -KPX Agrave Cacute -65 -KPX Agrave Ccaron -65 -KPX Agrave Ccedilla -65 -KPX Agrave G -60 -KPX Agrave Gbreve -60 -KPX Agrave Gcommaaccent -60 -KPX Agrave O -50 -KPX Agrave Oacute -50 -KPX Agrave Ocircumflex -50 -KPX Agrave Odieresis -50 -KPX Agrave Ograve -50 -KPX Agrave Ohungarumlaut -50 -KPX Agrave Omacron -50 -KPX Agrave Oslash -50 -KPX Agrave Otilde -50 -KPX Agrave Q -55 -KPX Agrave T -55 -KPX Agrave Tcaron -55 -KPX Agrave Tcommaaccent -55 -KPX Agrave U -50 -KPX Agrave Uacute -50 -KPX Agrave Ucircumflex -50 -KPX Agrave Udieresis -50 -KPX Agrave Ugrave -50 -KPX Agrave Uhungarumlaut -50 -KPX Agrave Umacron -50 -KPX Agrave Uogonek -50 -KPX Agrave Uring -50 -KPX Agrave V -95 -KPX Agrave W -100 -KPX Agrave Y -70 -KPX Agrave Yacute -70 -KPX Agrave Ydieresis -70 -KPX Agrave quoteright -74 -KPX Agrave u -30 -KPX Agrave uacute -30 -KPX Agrave ucircumflex -30 -KPX Agrave udieresis -30 -KPX Agrave ugrave -30 -KPX Agrave uhungarumlaut -30 -KPX Agrave umacron -30 -KPX Agrave uogonek -30 -KPX Agrave uring -30 -KPX Agrave v -74 -KPX Agrave w -74 -KPX Agrave y -74 -KPX Agrave yacute -74 -KPX Agrave ydieresis -74 -KPX Amacron C -65 -KPX Amacron Cacute -65 -KPX Amacron Ccaron -65 -KPX Amacron Ccedilla -65 -KPX Amacron G -60 -KPX Amacron Gbreve -60 -KPX Amacron Gcommaaccent -60 -KPX Amacron O -50 -KPX Amacron Oacute -50 -KPX Amacron Ocircumflex -50 -KPX Amacron Odieresis -50 -KPX Amacron Ograve -50 -KPX Amacron Ohungarumlaut -50 -KPX Amacron Omacron -50 -KPX Amacron Oslash -50 -KPX Amacron Otilde -50 -KPX Amacron Q -55 -KPX Amacron T -55 -KPX Amacron Tcaron -55 -KPX Amacron Tcommaaccent -55 -KPX Amacron U -50 -KPX Amacron Uacute -50 -KPX Amacron Ucircumflex -50 -KPX Amacron Udieresis -50 -KPX Amacron Ugrave -50 -KPX Amacron Uhungarumlaut -50 -KPX Amacron Umacron -50 -KPX Amacron Uogonek -50 -KPX Amacron Uring -50 -KPX Amacron V -95 -KPX Amacron W -100 -KPX Amacron Y -70 -KPX Amacron Yacute -70 -KPX Amacron Ydieresis -70 -KPX Amacron quoteright -74 -KPX Amacron u -30 -KPX Amacron uacute -30 -KPX Amacron ucircumflex -30 -KPX Amacron udieresis -30 -KPX Amacron ugrave -30 -KPX Amacron uhungarumlaut -30 -KPX Amacron umacron -30 -KPX Amacron uogonek -30 -KPX Amacron uring -30 -KPX Amacron v -74 -KPX Amacron w -74 -KPX Amacron y -74 -KPX Amacron yacute -74 -KPX Amacron ydieresis -74 -KPX Aogonek C -65 -KPX Aogonek Cacute -65 -KPX Aogonek Ccaron -65 -KPX Aogonek Ccedilla -65 -KPX Aogonek G -60 -KPX Aogonek Gbreve -60 -KPX Aogonek Gcommaaccent -60 -KPX Aogonek O -50 -KPX Aogonek Oacute -50 -KPX Aogonek Ocircumflex -50 -KPX Aogonek Odieresis -50 -KPX Aogonek Ograve -50 -KPX Aogonek Ohungarumlaut -50 -KPX Aogonek Omacron -50 -KPX Aogonek Oslash -50 -KPX Aogonek Otilde -50 -KPX Aogonek Q -55 -KPX Aogonek T -55 -KPX Aogonek Tcaron -55 -KPX Aogonek Tcommaaccent -55 -KPX Aogonek U -50 -KPX Aogonek Uacute -50 -KPX Aogonek Ucircumflex -50 -KPX Aogonek Udieresis -50 -KPX Aogonek Ugrave -50 -KPX Aogonek Uhungarumlaut -50 -KPX Aogonek Umacron -50 -KPX Aogonek Uogonek -50 -KPX Aogonek Uring -50 -KPX Aogonek V -95 -KPX Aogonek W -100 -KPX Aogonek Y -70 -KPX Aogonek Yacute -70 -KPX Aogonek Ydieresis -70 -KPX Aogonek quoteright -74 -KPX Aogonek u -30 -KPX Aogonek uacute -30 -KPX Aogonek ucircumflex -30 -KPX Aogonek udieresis -30 -KPX Aogonek ugrave -30 -KPX Aogonek uhungarumlaut -30 -KPX Aogonek umacron -30 -KPX Aogonek uogonek -30 -KPX Aogonek uring -30 -KPX Aogonek v -74 -KPX Aogonek w -74 -KPX Aogonek y -34 -KPX Aogonek yacute -34 -KPX Aogonek ydieresis -34 -KPX Aring C -65 -KPX Aring Cacute -65 -KPX Aring Ccaron -65 -KPX Aring Ccedilla -65 -KPX Aring G -60 -KPX Aring Gbreve -60 -KPX Aring Gcommaaccent -60 -KPX Aring O -50 -KPX Aring Oacute -50 -KPX Aring Ocircumflex -50 -KPX Aring Odieresis -50 -KPX Aring Ograve -50 -KPX Aring Ohungarumlaut -50 -KPX Aring Omacron -50 -KPX Aring Oslash -50 -KPX Aring Otilde -50 -KPX Aring Q -55 -KPX Aring T -55 -KPX Aring Tcaron -55 -KPX Aring Tcommaaccent -55 -KPX Aring U -50 -KPX Aring Uacute -50 -KPX Aring Ucircumflex -50 -KPX Aring Udieresis -50 -KPX Aring Ugrave -50 -KPX Aring Uhungarumlaut -50 -KPX Aring Umacron -50 -KPX Aring Uogonek -50 -KPX Aring Uring -50 -KPX Aring V -95 -KPX Aring W -100 -KPX Aring Y -70 -KPX Aring Yacute -70 -KPX Aring Ydieresis -70 -KPX Aring quoteright -74 -KPX Aring u -30 -KPX Aring uacute -30 -KPX Aring ucircumflex -30 -KPX Aring udieresis -30 -KPX Aring ugrave -30 -KPX Aring uhungarumlaut -30 -KPX Aring umacron -30 -KPX Aring uogonek -30 -KPX Aring uring -30 -KPX Aring v -74 -KPX Aring w -74 -KPX Aring y -74 -KPX Aring yacute -74 -KPX Aring ydieresis -74 -KPX Atilde C -65 -KPX Atilde Cacute -65 -KPX Atilde Ccaron -65 -KPX Atilde Ccedilla -65 -KPX Atilde G -60 -KPX Atilde Gbreve -60 -KPX Atilde Gcommaaccent -60 -KPX Atilde O -50 -KPX Atilde Oacute -50 -KPX Atilde Ocircumflex -50 -KPX Atilde Odieresis -50 -KPX Atilde Ograve -50 -KPX Atilde Ohungarumlaut -50 -KPX Atilde Omacron -50 -KPX Atilde Oslash -50 -KPX Atilde Otilde -50 -KPX Atilde Q -55 -KPX Atilde T -55 -KPX Atilde Tcaron -55 -KPX Atilde Tcommaaccent -55 -KPX Atilde U -50 -KPX Atilde Uacute -50 -KPX Atilde Ucircumflex -50 -KPX Atilde Udieresis -50 -KPX Atilde Ugrave -50 -KPX Atilde Uhungarumlaut -50 -KPX Atilde Umacron -50 -KPX Atilde Uogonek -50 -KPX Atilde Uring -50 -KPX Atilde V -95 -KPX Atilde W -100 -KPX Atilde Y -70 -KPX Atilde Yacute -70 -KPX Atilde Ydieresis -70 -KPX Atilde quoteright -74 -KPX Atilde u -30 -KPX Atilde uacute -30 -KPX Atilde ucircumflex -30 -KPX Atilde udieresis -30 -KPX Atilde ugrave -30 -KPX Atilde uhungarumlaut -30 -KPX Atilde umacron -30 -KPX Atilde uogonek -30 -KPX Atilde uring -30 -KPX Atilde v -74 -KPX Atilde w -74 -KPX Atilde y -74 -KPX Atilde yacute -74 -KPX Atilde ydieresis -74 -KPX B A -25 -KPX B Aacute -25 -KPX B Abreve -25 -KPX B Acircumflex -25 -KPX B Adieresis -25 -KPX B Agrave -25 -KPX B Amacron -25 -KPX B Aogonek -25 -KPX B Aring -25 -KPX B Atilde -25 -KPX B U -10 -KPX B Uacute -10 -KPX B Ucircumflex -10 -KPX B Udieresis -10 -KPX B Ugrave -10 -KPX B Uhungarumlaut -10 -KPX B Umacron -10 -KPX B Uogonek -10 -KPX B Uring -10 -KPX D A -25 -KPX D Aacute -25 -KPX D Abreve -25 -KPX D Acircumflex -25 -KPX D Adieresis -25 -KPX D Agrave -25 -KPX D Amacron -25 -KPX D Aogonek -25 -KPX D Aring -25 -KPX D Atilde -25 -KPX D V -50 -KPX D W -40 -KPX D Y -50 -KPX D Yacute -50 -KPX D Ydieresis -50 -KPX Dcaron A -25 -KPX Dcaron Aacute -25 -KPX Dcaron Abreve -25 -KPX Dcaron Acircumflex -25 -KPX Dcaron Adieresis -25 -KPX Dcaron Agrave -25 -KPX Dcaron Amacron -25 -KPX Dcaron Aogonek -25 -KPX Dcaron Aring -25 -KPX Dcaron Atilde -25 -KPX Dcaron V -50 -KPX Dcaron W -40 -KPX Dcaron Y -50 -KPX Dcaron Yacute -50 -KPX Dcaron Ydieresis -50 -KPX Dcroat A -25 -KPX Dcroat Aacute -25 -KPX Dcroat Abreve -25 -KPX Dcroat Acircumflex -25 -KPX Dcroat Adieresis -25 -KPX Dcroat Agrave -25 -KPX Dcroat Amacron -25 -KPX Dcroat Aogonek -25 -KPX Dcroat Aring -25 -KPX Dcroat Atilde -25 -KPX Dcroat V -50 -KPX Dcroat W -40 -KPX Dcroat Y -50 -KPX Dcroat Yacute -50 -KPX Dcroat Ydieresis -50 -KPX F A -100 -KPX F Aacute -100 -KPX F Abreve -100 -KPX F Acircumflex -100 -KPX F Adieresis -100 -KPX F Agrave -100 -KPX F Amacron -100 -KPX F Aogonek -100 -KPX F Aring -100 -KPX F Atilde -100 -KPX F a -95 -KPX F aacute -95 -KPX F abreve -95 -KPX F acircumflex -95 -KPX F adieresis -95 -KPX F agrave -95 -KPX F amacron -95 -KPX F aogonek -95 -KPX F aring -95 -KPX F atilde -95 -KPX F comma -129 -KPX F e -100 -KPX F eacute -100 -KPX F ecaron -100 -KPX F ecircumflex -100 -KPX F edieresis -100 -KPX F edotaccent -100 -KPX F egrave -100 -KPX F emacron -100 -KPX F eogonek -100 -KPX F i -40 -KPX F iacute -40 -KPX F icircumflex -40 -KPX F idieresis -40 -KPX F igrave -40 -KPX F imacron -40 -KPX F iogonek -40 -KPX F o -70 -KPX F oacute -70 -KPX F ocircumflex -70 -KPX F odieresis -70 -KPX F ograve -70 -KPX F ohungarumlaut -70 -KPX F omacron -70 -KPX F oslash -70 -KPX F otilde -70 -KPX F period -129 -KPX F r -50 -KPX F racute -50 -KPX F rcaron -50 -KPX F rcommaaccent -50 -KPX J A -25 -KPX J Aacute -25 -KPX J Abreve -25 -KPX J Acircumflex -25 -KPX J Adieresis -25 -KPX J Agrave -25 -KPX J Amacron -25 -KPX J Aogonek -25 -KPX J Aring -25 -KPX J Atilde -25 -KPX J a -40 -KPX J aacute -40 -KPX J abreve -40 -KPX J acircumflex -40 -KPX J adieresis -40 -KPX J agrave -40 -KPX J amacron -40 -KPX J aogonek -40 -KPX J aring -40 -KPX J atilde -40 -KPX J comma -10 -KPX J e -40 -KPX J eacute -40 -KPX J ecaron -40 -KPX J ecircumflex -40 -KPX J edieresis -40 -KPX J edotaccent -40 -KPX J egrave -40 -KPX J emacron -40 -KPX J eogonek -40 -KPX J o -40 -KPX J oacute -40 -KPX J ocircumflex -40 -KPX J odieresis -40 -KPX J ograve -40 -KPX J ohungarumlaut -40 -KPX J omacron -40 -KPX J oslash -40 -KPX J otilde -40 -KPX J period -10 -KPX J u -40 -KPX J uacute -40 -KPX J ucircumflex -40 -KPX J udieresis -40 -KPX J ugrave -40 -KPX J uhungarumlaut -40 -KPX J umacron -40 -KPX J uogonek -40 -KPX J uring -40 -KPX K O -30 -KPX K Oacute -30 -KPX K Ocircumflex -30 -KPX K Odieresis -30 -KPX K Ograve -30 -KPX K Ohungarumlaut -30 -KPX K Omacron -30 -KPX K Oslash -30 -KPX K Otilde -30 -KPX K e -25 -KPX K eacute -25 -KPX K ecaron -25 -KPX K ecircumflex -25 -KPX K edieresis -25 -KPX K edotaccent -25 -KPX K egrave -25 -KPX K emacron -25 -KPX K eogonek -25 -KPX K o -25 -KPX K oacute -25 -KPX K ocircumflex -25 -KPX K odieresis -25 -KPX K ograve -25 -KPX K ohungarumlaut -25 -KPX K omacron -25 -KPX K oslash -25 -KPX K otilde -25 -KPX K u -20 -KPX K uacute -20 -KPX K ucircumflex -20 -KPX K udieresis -20 -KPX K ugrave -20 -KPX K uhungarumlaut -20 -KPX K umacron -20 -KPX K uogonek -20 -KPX K uring -20 -KPX K y -20 -KPX K yacute -20 -KPX K ydieresis -20 -KPX Kcommaaccent O -30 -KPX Kcommaaccent Oacute -30 -KPX Kcommaaccent Ocircumflex -30 -KPX Kcommaaccent Odieresis -30 -KPX Kcommaaccent Ograve -30 -KPX Kcommaaccent Ohungarumlaut -30 -KPX Kcommaaccent Omacron -30 -KPX Kcommaaccent Oslash -30 -KPX Kcommaaccent Otilde -30 -KPX Kcommaaccent e -25 -KPX Kcommaaccent eacute -25 -KPX Kcommaaccent ecaron -25 -KPX Kcommaaccent ecircumflex -25 -KPX Kcommaaccent edieresis -25 -KPX Kcommaaccent edotaccent -25 -KPX Kcommaaccent egrave -25 -KPX Kcommaaccent emacron -25 -KPX Kcommaaccent eogonek -25 -KPX Kcommaaccent o -25 -KPX Kcommaaccent oacute -25 -KPX Kcommaaccent ocircumflex -25 -KPX Kcommaaccent odieresis -25 -KPX Kcommaaccent ograve -25 -KPX Kcommaaccent ohungarumlaut -25 -KPX Kcommaaccent omacron -25 -KPX Kcommaaccent oslash -25 -KPX Kcommaaccent otilde -25 -KPX Kcommaaccent u -20 -KPX Kcommaaccent uacute -20 -KPX Kcommaaccent ucircumflex -20 -KPX Kcommaaccent udieresis -20 -KPX Kcommaaccent ugrave -20 -KPX Kcommaaccent uhungarumlaut -20 -KPX Kcommaaccent umacron -20 -KPX Kcommaaccent uogonek -20 -KPX Kcommaaccent uring -20 -KPX Kcommaaccent y -20 -KPX Kcommaaccent yacute -20 -KPX Kcommaaccent ydieresis -20 -KPX L T -18 -KPX L Tcaron -18 -KPX L Tcommaaccent -18 -KPX L V -37 -KPX L W -37 -KPX L Y -37 -KPX L Yacute -37 -KPX L Ydieresis -37 -KPX L quoteright -55 -KPX L y -37 -KPX L yacute -37 -KPX L ydieresis -37 -KPX Lacute T -18 -KPX Lacute Tcaron -18 -KPX Lacute Tcommaaccent -18 -KPX Lacute V -37 -KPX Lacute W -37 -KPX Lacute Y -37 -KPX Lacute Yacute -37 -KPX Lacute Ydieresis -37 -KPX Lacute quoteright -55 -KPX Lacute y -37 -KPX Lacute yacute -37 -KPX Lacute ydieresis -37 -KPX Lcommaaccent T -18 -KPX Lcommaaccent Tcaron -18 -KPX Lcommaaccent Tcommaaccent -18 -KPX Lcommaaccent V -37 -KPX Lcommaaccent W -37 -KPX Lcommaaccent Y -37 -KPX Lcommaaccent Yacute -37 -KPX Lcommaaccent Ydieresis -37 -KPX Lcommaaccent quoteright -55 -KPX Lcommaaccent y -37 -KPX Lcommaaccent yacute -37 -KPX Lcommaaccent ydieresis -37 -KPX Lslash T -18 -KPX Lslash Tcaron -18 -KPX Lslash Tcommaaccent -18 -KPX Lslash V -37 -KPX Lslash W -37 -KPX Lslash Y -37 -KPX Lslash Yacute -37 -KPX Lslash Ydieresis -37 -KPX Lslash quoteright -55 -KPX Lslash y -37 -KPX Lslash yacute -37 -KPX Lslash ydieresis -37 -KPX N A -30 -KPX N Aacute -30 -KPX N Abreve -30 -KPX N Acircumflex -30 -KPX N Adieresis -30 -KPX N Agrave -30 -KPX N Amacron -30 -KPX N Aogonek -30 -KPX N Aring -30 -KPX N Atilde -30 -KPX Nacute A -30 -KPX Nacute Aacute -30 -KPX Nacute Abreve -30 -KPX Nacute Acircumflex -30 -KPX Nacute Adieresis -30 -KPX Nacute Agrave -30 -KPX Nacute Amacron -30 -KPX Nacute Aogonek -30 -KPX Nacute Aring -30 -KPX Nacute Atilde -30 -KPX Ncaron A -30 -KPX Ncaron Aacute -30 -KPX Ncaron Abreve -30 -KPX Ncaron Acircumflex -30 -KPX Ncaron Adieresis -30 -KPX Ncaron Agrave -30 -KPX Ncaron Amacron -30 -KPX Ncaron Aogonek -30 -KPX Ncaron Aring -30 -KPX Ncaron Atilde -30 -KPX Ncommaaccent A -30 -KPX Ncommaaccent Aacute -30 -KPX Ncommaaccent Abreve -30 -KPX Ncommaaccent Acircumflex -30 -KPX Ncommaaccent Adieresis -30 -KPX Ncommaaccent Agrave -30 -KPX Ncommaaccent Amacron -30 -KPX Ncommaaccent Aogonek -30 -KPX Ncommaaccent Aring -30 -KPX Ncommaaccent Atilde -30 -KPX Ntilde A -30 -KPX Ntilde Aacute -30 -KPX Ntilde Abreve -30 -KPX Ntilde Acircumflex -30 -KPX Ntilde Adieresis -30 -KPX Ntilde Agrave -30 -KPX Ntilde Amacron -30 -KPX Ntilde Aogonek -30 -KPX Ntilde Aring -30 -KPX Ntilde Atilde -30 -KPX O A -40 -KPX O Aacute -40 -KPX O Abreve -40 -KPX O Acircumflex -40 -KPX O Adieresis -40 -KPX O Agrave -40 -KPX O Amacron -40 -KPX O Aogonek -40 -KPX O Aring -40 -KPX O Atilde -40 -KPX O T -40 -KPX O Tcaron -40 -KPX O Tcommaaccent -40 -KPX O V -50 -KPX O W -50 -KPX O X -40 -KPX O Y -50 -KPX O Yacute -50 -KPX O Ydieresis -50 -KPX Oacute A -40 -KPX Oacute Aacute -40 -KPX Oacute Abreve -40 -KPX Oacute Acircumflex -40 -KPX Oacute Adieresis -40 -KPX Oacute Agrave -40 -KPX Oacute Amacron -40 -KPX Oacute Aogonek -40 -KPX Oacute Aring -40 -KPX Oacute Atilde -40 -KPX Oacute T -40 -KPX Oacute Tcaron -40 -KPX Oacute Tcommaaccent -40 -KPX Oacute V -50 -KPX Oacute W -50 -KPX Oacute X -40 -KPX Oacute Y -50 -KPX Oacute Yacute -50 -KPX Oacute Ydieresis -50 -KPX Ocircumflex A -40 -KPX Ocircumflex Aacute -40 -KPX Ocircumflex Abreve -40 -KPX Ocircumflex Acircumflex -40 -KPX Ocircumflex Adieresis -40 -KPX Ocircumflex Agrave -40 -KPX Ocircumflex Amacron -40 -KPX Ocircumflex Aogonek -40 -KPX Ocircumflex Aring -40 -KPX Ocircumflex Atilde -40 -KPX Ocircumflex T -40 -KPX Ocircumflex Tcaron -40 -KPX Ocircumflex Tcommaaccent -40 -KPX Ocircumflex V -50 -KPX Ocircumflex W -50 -KPX Ocircumflex X -40 -KPX Ocircumflex Y -50 -KPX Ocircumflex Yacute -50 -KPX Ocircumflex Ydieresis -50 -KPX Odieresis A -40 -KPX Odieresis Aacute -40 -KPX Odieresis Abreve -40 -KPX Odieresis Acircumflex -40 -KPX Odieresis Adieresis -40 -KPX Odieresis Agrave -40 -KPX Odieresis Amacron -40 -KPX Odieresis Aogonek -40 -KPX Odieresis Aring -40 -KPX Odieresis Atilde -40 -KPX Odieresis T -40 -KPX Odieresis Tcaron -40 -KPX Odieresis Tcommaaccent -40 -KPX Odieresis V -50 -KPX Odieresis W -50 -KPX Odieresis X -40 -KPX Odieresis Y -50 -KPX Odieresis Yacute -50 -KPX Odieresis Ydieresis -50 -KPX Ograve A -40 -KPX Ograve Aacute -40 -KPX Ograve Abreve -40 -KPX Ograve Acircumflex -40 -KPX Ograve Adieresis -40 -KPX Ograve Agrave -40 -KPX Ograve Amacron -40 -KPX Ograve Aogonek -40 -KPX Ograve Aring -40 -KPX Ograve Atilde -40 -KPX Ograve T -40 -KPX Ograve Tcaron -40 -KPX Ograve Tcommaaccent -40 -KPX Ograve V -50 -KPX Ograve W -50 -KPX Ograve X -40 -KPX Ograve Y -50 -KPX Ograve Yacute -50 -KPX Ograve Ydieresis -50 -KPX Ohungarumlaut A -40 -KPX Ohungarumlaut Aacute -40 -KPX Ohungarumlaut Abreve -40 -KPX Ohungarumlaut Acircumflex -40 -KPX Ohungarumlaut Adieresis -40 -KPX Ohungarumlaut Agrave -40 -KPX Ohungarumlaut Amacron -40 -KPX Ohungarumlaut Aogonek -40 -KPX Ohungarumlaut Aring -40 -KPX Ohungarumlaut Atilde -40 -KPX Ohungarumlaut T -40 -KPX Ohungarumlaut Tcaron -40 -KPX Ohungarumlaut Tcommaaccent -40 -KPX Ohungarumlaut V -50 -KPX Ohungarumlaut W -50 -KPX Ohungarumlaut X -40 -KPX Ohungarumlaut Y -50 -KPX Ohungarumlaut Yacute -50 -KPX Ohungarumlaut Ydieresis -50 -KPX Omacron A -40 -KPX Omacron Aacute -40 -KPX Omacron Abreve -40 -KPX Omacron Acircumflex -40 -KPX Omacron Adieresis -40 -KPX Omacron Agrave -40 -KPX Omacron Amacron -40 -KPX Omacron Aogonek -40 -KPX Omacron Aring -40 -KPX Omacron Atilde -40 -KPX Omacron T -40 -KPX Omacron Tcaron -40 -KPX Omacron Tcommaaccent -40 -KPX Omacron V -50 -KPX Omacron W -50 -KPX Omacron X -40 -KPX Omacron Y -50 -KPX Omacron Yacute -50 -KPX Omacron Ydieresis -50 -KPX Oslash A -40 -KPX Oslash Aacute -40 -KPX Oslash Abreve -40 -KPX Oslash Acircumflex -40 -KPX Oslash Adieresis -40 -KPX Oslash Agrave -40 -KPX Oslash Amacron -40 -KPX Oslash Aogonek -40 -KPX Oslash Aring -40 -KPX Oslash Atilde -40 -KPX Oslash T -40 -KPX Oslash Tcaron -40 -KPX Oslash Tcommaaccent -40 -KPX Oslash V -50 -KPX Oslash W -50 -KPX Oslash X -40 -KPX Oslash Y -50 -KPX Oslash Yacute -50 -KPX Oslash Ydieresis -50 -KPX Otilde A -40 -KPX Otilde Aacute -40 -KPX Otilde Abreve -40 -KPX Otilde Acircumflex -40 -KPX Otilde Adieresis -40 -KPX Otilde Agrave -40 -KPX Otilde Amacron -40 -KPX Otilde Aogonek -40 -KPX Otilde Aring -40 -KPX Otilde Atilde -40 -KPX Otilde T -40 -KPX Otilde Tcaron -40 -KPX Otilde Tcommaaccent -40 -KPX Otilde V -50 -KPX Otilde W -50 -KPX Otilde X -40 -KPX Otilde Y -50 -KPX Otilde Yacute -50 -KPX Otilde Ydieresis -50 -KPX P A -85 -KPX P Aacute -85 -KPX P Abreve -85 -KPX P Acircumflex -85 -KPX P Adieresis -85 -KPX P Agrave -85 -KPX P Amacron -85 -KPX P Aogonek -85 -KPX P Aring -85 -KPX P Atilde -85 -KPX P a -40 -KPX P aacute -40 -KPX P abreve -40 -KPX P acircumflex -40 -KPX P adieresis -40 -KPX P agrave -40 -KPX P amacron -40 -KPX P aogonek -40 -KPX P aring -40 -KPX P atilde -40 -KPX P comma -129 -KPX P e -50 -KPX P eacute -50 -KPX P ecaron -50 -KPX P ecircumflex -50 -KPX P edieresis -50 -KPX P edotaccent -50 -KPX P egrave -50 -KPX P emacron -50 -KPX P eogonek -50 -KPX P o -55 -KPX P oacute -55 -KPX P ocircumflex -55 -KPX P odieresis -55 -KPX P ograve -55 -KPX P ohungarumlaut -55 -KPX P omacron -55 -KPX P oslash -55 -KPX P otilde -55 -KPX P period -129 -KPX Q U -10 -KPX Q Uacute -10 -KPX Q Ucircumflex -10 -KPX Q Udieresis -10 -KPX Q Ugrave -10 -KPX Q Uhungarumlaut -10 -KPX Q Umacron -10 -KPX Q Uogonek -10 -KPX Q Uring -10 -KPX R O -40 -KPX R Oacute -40 -KPX R Ocircumflex -40 -KPX R Odieresis -40 -KPX R Ograve -40 -KPX R Ohungarumlaut -40 -KPX R Omacron -40 -KPX R Oslash -40 -KPX R Otilde -40 -KPX R T -30 -KPX R Tcaron -30 -KPX R Tcommaaccent -30 -KPX R U -40 -KPX R Uacute -40 -KPX R Ucircumflex -40 -KPX R Udieresis -40 -KPX R Ugrave -40 -KPX R Uhungarumlaut -40 -KPX R Umacron -40 -KPX R Uogonek -40 -KPX R Uring -40 -KPX R V -18 -KPX R W -18 -KPX R Y -18 -KPX R Yacute -18 -KPX R Ydieresis -18 -KPX Racute O -40 -KPX Racute Oacute -40 -KPX Racute Ocircumflex -40 -KPX Racute Odieresis -40 -KPX Racute Ograve -40 -KPX Racute Ohungarumlaut -40 -KPX Racute Omacron -40 -KPX Racute Oslash -40 -KPX Racute Otilde -40 -KPX Racute T -30 -KPX Racute Tcaron -30 -KPX Racute Tcommaaccent -30 -KPX Racute U -40 -KPX Racute Uacute -40 -KPX Racute Ucircumflex -40 -KPX Racute Udieresis -40 -KPX Racute Ugrave -40 -KPX Racute Uhungarumlaut -40 -KPX Racute Umacron -40 -KPX Racute Uogonek -40 -KPX Racute Uring -40 -KPX Racute V -18 -KPX Racute W -18 -KPX Racute Y -18 -KPX Racute Yacute -18 -KPX Racute Ydieresis -18 -KPX Rcaron O -40 -KPX Rcaron Oacute -40 -KPX Rcaron Ocircumflex -40 -KPX Rcaron Odieresis -40 -KPX Rcaron Ograve -40 -KPX Rcaron Ohungarumlaut -40 -KPX Rcaron Omacron -40 -KPX Rcaron Oslash -40 -KPX Rcaron Otilde -40 -KPX Rcaron T -30 -KPX Rcaron Tcaron -30 -KPX Rcaron Tcommaaccent -30 -KPX Rcaron U -40 -KPX Rcaron Uacute -40 -KPX Rcaron Ucircumflex -40 -KPX Rcaron Udieresis -40 -KPX Rcaron Ugrave -40 -KPX Rcaron Uhungarumlaut -40 -KPX Rcaron Umacron -40 -KPX Rcaron Uogonek -40 -KPX Rcaron Uring -40 -KPX Rcaron V -18 -KPX Rcaron W -18 -KPX Rcaron Y -18 -KPX Rcaron Yacute -18 -KPX Rcaron Ydieresis -18 -KPX Rcommaaccent O -40 -KPX Rcommaaccent Oacute -40 -KPX Rcommaaccent Ocircumflex -40 -KPX Rcommaaccent Odieresis -40 -KPX Rcommaaccent Ograve -40 -KPX Rcommaaccent Ohungarumlaut -40 -KPX Rcommaaccent Omacron -40 -KPX Rcommaaccent Oslash -40 -KPX Rcommaaccent Otilde -40 -KPX Rcommaaccent T -30 -KPX Rcommaaccent Tcaron -30 -KPX Rcommaaccent Tcommaaccent -30 -KPX Rcommaaccent U -40 -KPX Rcommaaccent Uacute -40 -KPX Rcommaaccent Ucircumflex -40 -KPX Rcommaaccent Udieresis -40 -KPX Rcommaaccent Ugrave -40 -KPX Rcommaaccent Uhungarumlaut -40 -KPX Rcommaaccent Umacron -40 -KPX Rcommaaccent Uogonek -40 -KPX Rcommaaccent Uring -40 -KPX Rcommaaccent V -18 -KPX Rcommaaccent W -18 -KPX Rcommaaccent Y -18 -KPX Rcommaaccent Yacute -18 -KPX Rcommaaccent Ydieresis -18 -KPX T A -55 -KPX T Aacute -55 -KPX T Abreve -55 -KPX T Acircumflex -55 -KPX T Adieresis -55 -KPX T Agrave -55 -KPX T Amacron -55 -KPX T Aogonek -55 -KPX T Aring -55 -KPX T Atilde -55 -KPX T O -18 -KPX T Oacute -18 -KPX T Ocircumflex -18 -KPX T Odieresis -18 -KPX T Ograve -18 -KPX T Ohungarumlaut -18 -KPX T Omacron -18 -KPX T Oslash -18 -KPX T Otilde -18 -KPX T a -92 -KPX T aacute -92 -KPX T abreve -92 -KPX T acircumflex -92 -KPX T adieresis -92 -KPX T agrave -92 -KPX T amacron -92 -KPX T aogonek -92 -KPX T aring -92 -KPX T atilde -92 -KPX T colon -74 -KPX T comma -92 -KPX T e -92 -KPX T eacute -92 -KPX T ecaron -92 -KPX T ecircumflex -92 -KPX T edieresis -52 -KPX T edotaccent -92 -KPX T egrave -52 -KPX T emacron -52 -KPX T eogonek -92 -KPX T hyphen -92 -KPX T i -37 -KPX T iacute -37 -KPX T iogonek -37 -KPX T o -95 -KPX T oacute -95 -KPX T ocircumflex -95 -KPX T odieresis -95 -KPX T ograve -95 -KPX T ohungarumlaut -95 -KPX T omacron -95 -KPX T oslash -95 -KPX T otilde -95 -KPX T period -92 -KPX T r -37 -KPX T racute -37 -KPX T rcaron -37 -KPX T rcommaaccent -37 -KPX T semicolon -74 -KPX T u -37 -KPX T uacute -37 -KPX T ucircumflex -37 -KPX T udieresis -37 -KPX T ugrave -37 -KPX T uhungarumlaut -37 -KPX T umacron -37 -KPX T uogonek -37 -KPX T uring -37 -KPX T w -37 -KPX T y -37 -KPX T yacute -37 -KPX T ydieresis -37 -KPX Tcaron A -55 -KPX Tcaron Aacute -55 -KPX Tcaron Abreve -55 -KPX Tcaron Acircumflex -55 -KPX Tcaron Adieresis -55 -KPX Tcaron Agrave -55 -KPX Tcaron Amacron -55 -KPX Tcaron Aogonek -55 -KPX Tcaron Aring -55 -KPX Tcaron Atilde -55 -KPX Tcaron O -18 -KPX Tcaron Oacute -18 -KPX Tcaron Ocircumflex -18 -KPX Tcaron Odieresis -18 -KPX Tcaron Ograve -18 -KPX Tcaron Ohungarumlaut -18 -KPX Tcaron Omacron -18 -KPX Tcaron Oslash -18 -KPX Tcaron Otilde -18 -KPX Tcaron a -92 -KPX Tcaron aacute -92 -KPX Tcaron abreve -92 -KPX Tcaron acircumflex -92 -KPX Tcaron adieresis -92 -KPX Tcaron agrave -92 -KPX Tcaron amacron -92 -KPX Tcaron aogonek -92 -KPX Tcaron aring -92 -KPX Tcaron atilde -92 -KPX Tcaron colon -74 -KPX Tcaron comma -92 -KPX Tcaron e -92 -KPX Tcaron eacute -92 -KPX Tcaron ecaron -92 -KPX Tcaron ecircumflex -92 -KPX Tcaron edieresis -52 -KPX Tcaron edotaccent -92 -KPX Tcaron egrave -52 -KPX Tcaron emacron -52 -KPX Tcaron eogonek -92 -KPX Tcaron hyphen -92 -KPX Tcaron i -37 -KPX Tcaron iacute -37 -KPX Tcaron iogonek -37 -KPX Tcaron o -95 -KPX Tcaron oacute -95 -KPX Tcaron ocircumflex -95 -KPX Tcaron odieresis -95 -KPX Tcaron ograve -95 -KPX Tcaron ohungarumlaut -95 -KPX Tcaron omacron -95 -KPX Tcaron oslash -95 -KPX Tcaron otilde -95 -KPX Tcaron period -92 -KPX Tcaron r -37 -KPX Tcaron racute -37 -KPX Tcaron rcaron -37 -KPX Tcaron rcommaaccent -37 -KPX Tcaron semicolon -74 -KPX Tcaron u -37 -KPX Tcaron uacute -37 -KPX Tcaron ucircumflex -37 -KPX Tcaron udieresis -37 -KPX Tcaron ugrave -37 -KPX Tcaron uhungarumlaut -37 -KPX Tcaron umacron -37 -KPX Tcaron uogonek -37 -KPX Tcaron uring -37 -KPX Tcaron w -37 -KPX Tcaron y -37 -KPX Tcaron yacute -37 -KPX Tcaron ydieresis -37 -KPX Tcommaaccent A -55 -KPX Tcommaaccent Aacute -55 -KPX Tcommaaccent Abreve -55 -KPX Tcommaaccent Acircumflex -55 -KPX Tcommaaccent Adieresis -55 -KPX Tcommaaccent Agrave -55 -KPX Tcommaaccent Amacron -55 -KPX Tcommaaccent Aogonek -55 -KPX Tcommaaccent Aring -55 -KPX Tcommaaccent Atilde -55 -KPX Tcommaaccent O -18 -KPX Tcommaaccent Oacute -18 -KPX Tcommaaccent Ocircumflex -18 -KPX Tcommaaccent Odieresis -18 -KPX Tcommaaccent Ograve -18 -KPX Tcommaaccent Ohungarumlaut -18 -KPX Tcommaaccent Omacron -18 -KPX Tcommaaccent Oslash -18 -KPX Tcommaaccent Otilde -18 -KPX Tcommaaccent a -92 -KPX Tcommaaccent aacute -92 -KPX Tcommaaccent abreve -92 -KPX Tcommaaccent acircumflex -92 -KPX Tcommaaccent adieresis -92 -KPX Tcommaaccent agrave -92 -KPX Tcommaaccent amacron -92 -KPX Tcommaaccent aogonek -92 -KPX Tcommaaccent aring -92 -KPX Tcommaaccent atilde -92 -KPX Tcommaaccent colon -74 -KPX Tcommaaccent comma -92 -KPX Tcommaaccent e -92 -KPX Tcommaaccent eacute -92 -KPX Tcommaaccent ecaron -92 -KPX Tcommaaccent ecircumflex -92 -KPX Tcommaaccent edieresis -52 -KPX Tcommaaccent edotaccent -92 -KPX Tcommaaccent egrave -52 -KPX Tcommaaccent emacron -52 -KPX Tcommaaccent eogonek -92 -KPX Tcommaaccent hyphen -92 -KPX Tcommaaccent i -37 -KPX Tcommaaccent iacute -37 -KPX Tcommaaccent iogonek -37 -KPX Tcommaaccent o -95 -KPX Tcommaaccent oacute -95 -KPX Tcommaaccent ocircumflex -95 -KPX Tcommaaccent odieresis -95 -KPX Tcommaaccent ograve -95 -KPX Tcommaaccent ohungarumlaut -95 -KPX Tcommaaccent omacron -95 -KPX Tcommaaccent oslash -95 -KPX Tcommaaccent otilde -95 -KPX Tcommaaccent period -92 -KPX Tcommaaccent r -37 -KPX Tcommaaccent racute -37 -KPX Tcommaaccent rcaron -37 -KPX Tcommaaccent rcommaaccent -37 -KPX Tcommaaccent semicolon -74 -KPX Tcommaaccent u -37 -KPX Tcommaaccent uacute -37 -KPX Tcommaaccent ucircumflex -37 -KPX Tcommaaccent udieresis -37 -KPX Tcommaaccent ugrave -37 -KPX Tcommaaccent uhungarumlaut -37 -KPX Tcommaaccent umacron -37 -KPX Tcommaaccent uogonek -37 -KPX Tcommaaccent uring -37 -KPX Tcommaaccent w -37 -KPX Tcommaaccent y -37 -KPX Tcommaaccent yacute -37 -KPX Tcommaaccent ydieresis -37 -KPX U A -45 -KPX U Aacute -45 -KPX U Abreve -45 -KPX U Acircumflex -45 -KPX U Adieresis -45 -KPX U Agrave -45 -KPX U Amacron -45 -KPX U Aogonek -45 -KPX U Aring -45 -KPX U Atilde -45 -KPX Uacute A -45 -KPX Uacute Aacute -45 -KPX Uacute Abreve -45 -KPX Uacute Acircumflex -45 -KPX Uacute Adieresis -45 -KPX Uacute Agrave -45 -KPX Uacute Amacron -45 -KPX Uacute Aogonek -45 -KPX Uacute Aring -45 -KPX Uacute Atilde -45 -KPX Ucircumflex A -45 -KPX Ucircumflex Aacute -45 -KPX Ucircumflex Abreve -45 -KPX Ucircumflex Acircumflex -45 -KPX Ucircumflex Adieresis -45 -KPX Ucircumflex Agrave -45 -KPX Ucircumflex Amacron -45 -KPX Ucircumflex Aogonek -45 -KPX Ucircumflex Aring -45 -KPX Ucircumflex Atilde -45 -KPX Udieresis A -45 -KPX Udieresis Aacute -45 -KPX Udieresis Abreve -45 -KPX Udieresis Acircumflex -45 -KPX Udieresis Adieresis -45 -KPX Udieresis Agrave -45 -KPX Udieresis Amacron -45 -KPX Udieresis Aogonek -45 -KPX Udieresis Aring -45 -KPX Udieresis Atilde -45 -KPX Ugrave A -45 -KPX Ugrave Aacute -45 -KPX Ugrave Abreve -45 -KPX Ugrave Acircumflex -45 -KPX Ugrave Adieresis -45 -KPX Ugrave Agrave -45 -KPX Ugrave Amacron -45 -KPX Ugrave Aogonek -45 -KPX Ugrave Aring -45 -KPX Ugrave Atilde -45 -KPX Uhungarumlaut A -45 -KPX Uhungarumlaut Aacute -45 -KPX Uhungarumlaut Abreve -45 -KPX Uhungarumlaut Acircumflex -45 -KPX Uhungarumlaut Adieresis -45 -KPX Uhungarumlaut Agrave -45 -KPX Uhungarumlaut Amacron -45 -KPX Uhungarumlaut Aogonek -45 -KPX Uhungarumlaut Aring -45 -KPX Uhungarumlaut Atilde -45 -KPX Umacron A -45 -KPX Umacron Aacute -45 -KPX Umacron Abreve -45 -KPX Umacron Acircumflex -45 -KPX Umacron Adieresis -45 -KPX Umacron Agrave -45 -KPX Umacron Amacron -45 -KPX Umacron Aogonek -45 -KPX Umacron Aring -45 -KPX Umacron Atilde -45 -KPX Uogonek A -45 -KPX Uogonek Aacute -45 -KPX Uogonek Abreve -45 -KPX Uogonek Acircumflex -45 -KPX Uogonek Adieresis -45 -KPX Uogonek Agrave -45 -KPX Uogonek Amacron -45 -KPX Uogonek Aogonek -45 -KPX Uogonek Aring -45 -KPX Uogonek Atilde -45 -KPX Uring A -45 -KPX Uring Aacute -45 -KPX Uring Abreve -45 -KPX Uring Acircumflex -45 -KPX Uring Adieresis -45 -KPX Uring Agrave -45 -KPX Uring Amacron -45 -KPX Uring Aogonek -45 -KPX Uring Aring -45 -KPX Uring Atilde -45 -KPX V A -85 -KPX V Aacute -85 -KPX V Abreve -85 -KPX V Acircumflex -85 -KPX V Adieresis -85 -KPX V Agrave -85 -KPX V Amacron -85 -KPX V Aogonek -85 -KPX V Aring -85 -KPX V Atilde -85 -KPX V G -10 -KPX V Gbreve -10 -KPX V Gcommaaccent -10 -KPX V O -30 -KPX V Oacute -30 -KPX V Ocircumflex -30 -KPX V Odieresis -30 -KPX V Ograve -30 -KPX V Ohungarumlaut -30 -KPX V Omacron -30 -KPX V Oslash -30 -KPX V Otilde -30 -KPX V a -111 -KPX V aacute -111 -KPX V abreve -111 -KPX V acircumflex -111 -KPX V adieresis -111 -KPX V agrave -111 -KPX V amacron -111 -KPX V aogonek -111 -KPX V aring -111 -KPX V atilde -111 -KPX V colon -74 -KPX V comma -129 -KPX V e -111 -KPX V eacute -111 -KPX V ecaron -111 -KPX V ecircumflex -111 -KPX V edieresis -71 -KPX V edotaccent -111 -KPX V egrave -71 -KPX V emacron -71 -KPX V eogonek -111 -KPX V hyphen -70 -KPX V i -55 -KPX V iacute -55 -KPX V iogonek -55 -KPX V o -111 -KPX V oacute -111 -KPX V ocircumflex -111 -KPX V odieresis -111 -KPX V ograve -111 -KPX V ohungarumlaut -111 -KPX V omacron -111 -KPX V oslash -111 -KPX V otilde -111 -KPX V period -129 -KPX V semicolon -74 -KPX V u -55 -KPX V uacute -55 -KPX V ucircumflex -55 -KPX V udieresis -55 -KPX V ugrave -55 -KPX V uhungarumlaut -55 -KPX V umacron -55 -KPX V uogonek -55 -KPX V uring -55 -KPX W A -74 -KPX W Aacute -74 -KPX W Abreve -74 -KPX W Acircumflex -74 -KPX W Adieresis -74 -KPX W Agrave -74 -KPX W Amacron -74 -KPX W Aogonek -74 -KPX W Aring -74 -KPX W Atilde -74 -KPX W O -15 -KPX W Oacute -15 -KPX W Ocircumflex -15 -KPX W Odieresis -15 -KPX W Ograve -15 -KPX W Ohungarumlaut -15 -KPX W Omacron -15 -KPX W Oslash -15 -KPX W Otilde -15 -KPX W a -85 -KPX W aacute -85 -KPX W abreve -85 -KPX W acircumflex -85 -KPX W adieresis -85 -KPX W agrave -85 -KPX W amacron -85 -KPX W aogonek -85 -KPX W aring -85 -KPX W atilde -85 -KPX W colon -55 -KPX W comma -74 -KPX W e -90 -KPX W eacute -90 -KPX W ecaron -90 -KPX W ecircumflex -90 -KPX W edieresis -50 -KPX W edotaccent -90 -KPX W egrave -50 -KPX W emacron -50 -KPX W eogonek -90 -KPX W hyphen -50 -KPX W i -37 -KPX W iacute -37 -KPX W iogonek -37 -KPX W o -80 -KPX W oacute -80 -KPX W ocircumflex -80 -KPX W odieresis -80 -KPX W ograve -80 -KPX W ohungarumlaut -80 -KPX W omacron -80 -KPX W oslash -80 -KPX W otilde -80 -KPX W period -74 -KPX W semicolon -55 -KPX W u -55 -KPX W uacute -55 -KPX W ucircumflex -55 -KPX W udieresis -55 -KPX W ugrave -55 -KPX W uhungarumlaut -55 -KPX W umacron -55 -KPX W uogonek -55 -KPX W uring -55 -KPX W y -55 -KPX W yacute -55 -KPX W ydieresis -55 -KPX Y A -74 -KPX Y Aacute -74 -KPX Y Abreve -74 -KPX Y Acircumflex -74 -KPX Y Adieresis -74 -KPX Y Agrave -74 -KPX Y Amacron -74 -KPX Y Aogonek -74 -KPX Y Aring -74 -KPX Y Atilde -74 -KPX Y O -25 -KPX Y Oacute -25 -KPX Y Ocircumflex -25 -KPX Y Odieresis -25 -KPX Y Ograve -25 -KPX Y Ohungarumlaut -25 -KPX Y Omacron -25 -KPX Y Oslash -25 -KPX Y Otilde -25 -KPX Y a -92 -KPX Y aacute -92 -KPX Y abreve -92 -KPX Y acircumflex -92 -KPX Y adieresis -92 -KPX Y agrave -92 -KPX Y amacron -92 -KPX Y aogonek -92 -KPX Y aring -92 -KPX Y atilde -92 -KPX Y colon -92 -KPX Y comma -92 -KPX Y e -111 -KPX Y eacute -111 -KPX Y ecaron -111 -KPX Y ecircumflex -71 -KPX Y edieresis -71 -KPX Y edotaccent -111 -KPX Y egrave -71 -KPX Y emacron -71 -KPX Y eogonek -111 -KPX Y hyphen -92 -KPX Y i -55 -KPX Y iacute -55 -KPX Y iogonek -55 -KPX Y o -111 -KPX Y oacute -111 -KPX Y ocircumflex -111 -KPX Y odieresis -111 -KPX Y ograve -111 -KPX Y ohungarumlaut -111 -KPX Y omacron -111 -KPX Y oslash -111 -KPX Y otilde -111 -KPX Y period -74 -KPX Y semicolon -92 -KPX Y u -92 -KPX Y uacute -92 -KPX Y ucircumflex -92 -KPX Y udieresis -92 -KPX Y ugrave -92 -KPX Y uhungarumlaut -92 -KPX Y umacron -92 -KPX Y uogonek -92 -KPX Y uring -92 -KPX Yacute A -74 -KPX Yacute Aacute -74 -KPX Yacute Abreve -74 -KPX Yacute Acircumflex -74 -KPX Yacute Adieresis -74 -KPX Yacute Agrave -74 -KPX Yacute Amacron -74 -KPX Yacute Aogonek -74 -KPX Yacute Aring -74 -KPX Yacute Atilde -74 -KPX Yacute O -25 -KPX Yacute Oacute -25 -KPX Yacute Ocircumflex -25 -KPX Yacute Odieresis -25 -KPX Yacute Ograve -25 -KPX Yacute Ohungarumlaut -25 -KPX Yacute Omacron -25 -KPX Yacute Oslash -25 -KPX Yacute Otilde -25 -KPX Yacute a -92 -KPX Yacute aacute -92 -KPX Yacute abreve -92 -KPX Yacute acircumflex -92 -KPX Yacute adieresis -92 -KPX Yacute agrave -92 -KPX Yacute amacron -92 -KPX Yacute aogonek -92 -KPX Yacute aring -92 -KPX Yacute atilde -92 -KPX Yacute colon -92 -KPX Yacute comma -92 -KPX Yacute e -111 -KPX Yacute eacute -111 -KPX Yacute ecaron -111 -KPX Yacute ecircumflex -71 -KPX Yacute edieresis -71 -KPX Yacute edotaccent -111 -KPX Yacute egrave -71 -KPX Yacute emacron -71 -KPX Yacute eogonek -111 -KPX Yacute hyphen -92 -KPX Yacute i -55 -KPX Yacute iacute -55 -KPX Yacute iogonek -55 -KPX Yacute o -111 -KPX Yacute oacute -111 -KPX Yacute ocircumflex -111 -KPX Yacute odieresis -111 -KPX Yacute ograve -111 -KPX Yacute ohungarumlaut -111 -KPX Yacute omacron -111 -KPX Yacute oslash -111 -KPX Yacute otilde -111 -KPX Yacute period -74 -KPX Yacute semicolon -92 -KPX Yacute u -92 -KPX Yacute uacute -92 -KPX Yacute ucircumflex -92 -KPX Yacute udieresis -92 -KPX Yacute ugrave -92 -KPX Yacute uhungarumlaut -92 -KPX Yacute umacron -92 -KPX Yacute uogonek -92 -KPX Yacute uring -92 -KPX Ydieresis A -74 -KPX Ydieresis Aacute -74 -KPX Ydieresis Abreve -74 -KPX Ydieresis Acircumflex -74 -KPX Ydieresis Adieresis -74 -KPX Ydieresis Agrave -74 -KPX Ydieresis Amacron -74 -KPX Ydieresis Aogonek -74 -KPX Ydieresis Aring -74 -KPX Ydieresis Atilde -74 -KPX Ydieresis O -25 -KPX Ydieresis Oacute -25 -KPX Ydieresis Ocircumflex -25 -KPX Ydieresis Odieresis -25 -KPX Ydieresis Ograve -25 -KPX Ydieresis Ohungarumlaut -25 -KPX Ydieresis Omacron -25 -KPX Ydieresis Oslash -25 -KPX Ydieresis Otilde -25 -KPX Ydieresis a -92 -KPX Ydieresis aacute -92 -KPX Ydieresis abreve -92 -KPX Ydieresis acircumflex -92 -KPX Ydieresis adieresis -92 -KPX Ydieresis agrave -92 -KPX Ydieresis amacron -92 -KPX Ydieresis aogonek -92 -KPX Ydieresis aring -92 -KPX Ydieresis atilde -92 -KPX Ydieresis colon -92 -KPX Ydieresis comma -92 -KPX Ydieresis e -111 -KPX Ydieresis eacute -111 -KPX Ydieresis ecaron -111 -KPX Ydieresis ecircumflex -71 -KPX Ydieresis edieresis -71 -KPX Ydieresis edotaccent -111 -KPX Ydieresis egrave -71 -KPX Ydieresis emacron -71 -KPX Ydieresis eogonek -111 -KPX Ydieresis hyphen -92 -KPX Ydieresis i -55 -KPX Ydieresis iacute -55 -KPX Ydieresis iogonek -55 -KPX Ydieresis o -111 -KPX Ydieresis oacute -111 -KPX Ydieresis ocircumflex -111 -KPX Ydieresis odieresis -111 -KPX Ydieresis ograve -111 -KPX Ydieresis ohungarumlaut -111 -KPX Ydieresis omacron -111 -KPX Ydieresis oslash -111 -KPX Ydieresis otilde -111 -KPX Ydieresis period -74 -KPX Ydieresis semicolon -92 -KPX Ydieresis u -92 -KPX Ydieresis uacute -92 -KPX Ydieresis ucircumflex -92 -KPX Ydieresis udieresis -92 -KPX Ydieresis ugrave -92 -KPX Ydieresis uhungarumlaut -92 -KPX Ydieresis umacron -92 -KPX Ydieresis uogonek -92 -KPX Ydieresis uring -92 -KPX b b -10 -KPX b period -40 -KPX b u -20 -KPX b uacute -20 -KPX b ucircumflex -20 -KPX b udieresis -20 -KPX b ugrave -20 -KPX b uhungarumlaut -20 -KPX b umacron -20 -KPX b uogonek -20 -KPX b uring -20 -KPX c h -10 -KPX c k -10 -KPX c kcommaaccent -10 -KPX cacute h -10 -KPX cacute k -10 -KPX cacute kcommaaccent -10 -KPX ccaron h -10 -KPX ccaron k -10 -KPX ccaron kcommaaccent -10 -KPX ccedilla h -10 -KPX ccedilla k -10 -KPX ccedilla kcommaaccent -10 -KPX comma quotedblright -95 -KPX comma quoteright -95 -KPX e b -10 -KPX eacute b -10 -KPX ecaron b -10 -KPX ecircumflex b -10 -KPX edieresis b -10 -KPX edotaccent b -10 -KPX egrave b -10 -KPX emacron b -10 -KPX eogonek b -10 -KPX f comma -10 -KPX f dotlessi -30 -KPX f e -10 -KPX f eacute -10 -KPX f edotaccent -10 -KPX f eogonek -10 -KPX f f -18 -KPX f o -10 -KPX f oacute -10 -KPX f ocircumflex -10 -KPX f ograve -10 -KPX f ohungarumlaut -10 -KPX f oslash -10 -KPX f otilde -10 -KPX f period -10 -KPX f quoteright 55 -KPX k e -30 -KPX k eacute -30 -KPX k ecaron -30 -KPX k ecircumflex -30 -KPX k edieresis -30 -KPX k edotaccent -30 -KPX k egrave -30 -KPX k emacron -30 -KPX k eogonek -30 -KPX k o -10 -KPX k oacute -10 -KPX k ocircumflex -10 -KPX k odieresis -10 -KPX k ograve -10 -KPX k ohungarumlaut -10 -KPX k omacron -10 -KPX k oslash -10 -KPX k otilde -10 -KPX kcommaaccent e -30 -KPX kcommaaccent eacute -30 -KPX kcommaaccent ecaron -30 -KPX kcommaaccent ecircumflex -30 -KPX kcommaaccent edieresis -30 -KPX kcommaaccent edotaccent -30 -KPX kcommaaccent egrave -30 -KPX kcommaaccent emacron -30 -KPX kcommaaccent eogonek -30 -KPX kcommaaccent o -10 -KPX kcommaaccent oacute -10 -KPX kcommaaccent ocircumflex -10 -KPX kcommaaccent odieresis -10 -KPX kcommaaccent ograve -10 -KPX kcommaaccent ohungarumlaut -10 -KPX kcommaaccent omacron -10 -KPX kcommaaccent oslash -10 -KPX kcommaaccent otilde -10 -KPX n v -40 -KPX nacute v -40 -KPX ncaron v -40 -KPX ncommaaccent v -40 -KPX ntilde v -40 -KPX o v -15 -KPX o w -25 -KPX o x -10 -KPX o y -10 -KPX o yacute -10 -KPX o ydieresis -10 -KPX oacute v -15 -KPX oacute w -25 -KPX oacute x -10 -KPX oacute y -10 -KPX oacute yacute -10 -KPX oacute ydieresis -10 -KPX ocircumflex v -15 -KPX ocircumflex w -25 -KPX ocircumflex x -10 -KPX ocircumflex y -10 -KPX ocircumflex yacute -10 -KPX ocircumflex ydieresis -10 -KPX odieresis v -15 -KPX odieresis w -25 -KPX odieresis x -10 -KPX odieresis y -10 -KPX odieresis yacute -10 -KPX odieresis ydieresis -10 -KPX ograve v -15 -KPX ograve w -25 -KPX ograve x -10 -KPX ograve y -10 -KPX ograve yacute -10 -KPX ograve ydieresis -10 -KPX ohungarumlaut v -15 -KPX ohungarumlaut w -25 -KPX ohungarumlaut x -10 -KPX ohungarumlaut y -10 -KPX ohungarumlaut yacute -10 -KPX ohungarumlaut ydieresis -10 -KPX omacron v -15 -KPX omacron w -25 -KPX omacron x -10 -KPX omacron y -10 -KPX omacron yacute -10 -KPX omacron ydieresis -10 -KPX oslash v -15 -KPX oslash w -25 -KPX oslash x -10 -KPX oslash y -10 -KPX oslash yacute -10 -KPX oslash ydieresis -10 -KPX otilde v -15 -KPX otilde w -25 -KPX otilde x -10 -KPX otilde y -10 -KPX otilde yacute -10 -KPX otilde ydieresis -10 -KPX period quotedblright -95 -KPX period quoteright -95 -KPX quoteleft quoteleft -74 -KPX quoteright d -15 -KPX quoteright dcroat -15 -KPX quoteright quoteright -74 -KPX quoteright r -15 -KPX quoteright racute -15 -KPX quoteright rcaron -15 -KPX quoteright rcommaaccent -15 -KPX quoteright s -74 -KPX quoteright sacute -74 -KPX quoteright scaron -74 -KPX quoteright scedilla -74 -KPX quoteright scommaaccent -74 -KPX quoteright space -74 -KPX quoteright t -37 -KPX quoteright tcommaaccent -37 -KPX quoteright v -15 -KPX r comma -65 -KPX r period -65 -KPX racute comma -65 -KPX racute period -65 -KPX rcaron comma -65 -KPX rcaron period -65 -KPX rcommaaccent comma -65 -KPX rcommaaccent period -65 -KPX space A -37 -KPX space Aacute -37 -KPX space Abreve -37 -KPX space Acircumflex -37 -KPX space Adieresis -37 -KPX space Agrave -37 -KPX space Amacron -37 -KPX space Aogonek -37 -KPX space Aring -37 -KPX space Atilde -37 -KPX space V -70 -KPX space W -70 -KPX space Y -70 -KPX space Yacute -70 -KPX space Ydieresis -70 -KPX v comma -37 -KPX v e -15 -KPX v eacute -15 -KPX v ecaron -15 -KPX v ecircumflex -15 -KPX v edieresis -15 -KPX v edotaccent -15 -KPX v egrave -15 -KPX v emacron -15 -KPX v eogonek -15 -KPX v o -15 -KPX v oacute -15 -KPX v ocircumflex -15 -KPX v odieresis -15 -KPX v ograve -15 -KPX v ohungarumlaut -15 -KPX v omacron -15 -KPX v oslash -15 -KPX v otilde -15 -KPX v period -37 -KPX w a -10 -KPX w aacute -10 -KPX w abreve -10 -KPX w acircumflex -10 -KPX w adieresis -10 -KPX w agrave -10 -KPX w amacron -10 -KPX w aogonek -10 -KPX w aring -10 -KPX w atilde -10 -KPX w comma -37 -KPX w e -10 -KPX w eacute -10 -KPX w ecaron -10 -KPX w ecircumflex -10 -KPX w edieresis -10 -KPX w edotaccent -10 -KPX w egrave -10 -KPX w emacron -10 -KPX w eogonek -10 -KPX w o -15 -KPX w oacute -15 -KPX w ocircumflex -15 -KPX w odieresis -15 -KPX w ograve -15 -KPX w ohungarumlaut -15 -KPX w omacron -15 -KPX w oslash -15 -KPX w otilde -15 -KPX w period -37 -KPX x e -10 -KPX x eacute -10 -KPX x ecaron -10 -KPX x ecircumflex -10 -KPX x edieresis -10 -KPX x edotaccent -10 -KPX x egrave -10 -KPX x emacron -10 -KPX x eogonek -10 -KPX y comma -37 -KPX y period -37 -KPX yacute comma -37 -KPX yacute period -37 -KPX ydieresis comma -37 -KPX ydieresis period -37 -EndKernPairs -EndKernData -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Times-Italic.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Times-Italic.afm deleted file mode 100644 index b0eaee4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Times-Italic.afm +++ /dev/null @@ -1,2667 +0,0 @@ -StartFontMetrics 4.1 -Comment Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Thu May 1 12:56:55 1997 -Comment UniqueID 43067 -Comment VMusage 47727 58752 -FontName Times-Italic -FullName Times Italic -FamilyName Times -Weight Medium -ItalicAngle -15.5 -IsFixedPitch false -CharacterSet ExtendedRoman -FontBBox -169 -217 1010 883 -UnderlinePosition -100 -UnderlineThickness 50 -Version 002.000 -Notice Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.Times is a trademark of Linotype-Hell AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 653 -XHeight 441 -Ascender 683 -Descender -217 -StdHW 32 -StdVW 76 -StartCharMetrics 315 -C 32 ; WX 250 ; N space ; B 0 0 0 0 ; -C 33 ; WX 333 ; N exclam ; B 39 -11 302 667 ; -C 34 ; WX 420 ; N quotedbl ; B 144 421 432 666 ; -C 35 ; WX 500 ; N numbersign ; B 2 0 540 676 ; -C 36 ; WX 500 ; N dollar ; B 31 -89 497 731 ; -C 37 ; WX 833 ; N percent ; B 79 -13 790 676 ; -C 38 ; WX 778 ; N ampersand ; B 76 -18 723 666 ; -C 39 ; WX 333 ; N quoteright ; B 151 436 290 666 ; -C 40 ; WX 333 ; N parenleft ; B 42 -181 315 669 ; -C 41 ; WX 333 ; N parenright ; B 16 -180 289 669 ; -C 42 ; WX 500 ; N asterisk ; B 128 255 492 666 ; -C 43 ; WX 675 ; N plus ; B 86 0 590 506 ; -C 44 ; WX 250 ; N comma ; B -4 -129 135 101 ; -C 45 ; WX 333 ; N hyphen ; B 49 192 282 255 ; -C 46 ; WX 250 ; N period ; B 27 -11 138 100 ; -C 47 ; WX 278 ; N slash ; B -65 -18 386 666 ; -C 48 ; WX 500 ; N zero ; B 32 -7 497 676 ; -C 49 ; WX 500 ; N one ; B 49 0 409 676 ; -C 50 ; WX 500 ; N two ; B 12 0 452 676 ; -C 51 ; WX 500 ; N three ; B 15 -7 465 676 ; -C 52 ; WX 500 ; N four ; B 1 0 479 676 ; -C 53 ; WX 500 ; N five ; B 15 -7 491 666 ; -C 54 ; WX 500 ; N six ; B 30 -7 521 686 ; -C 55 ; WX 500 ; N seven ; B 75 -8 537 666 ; -C 56 ; WX 500 ; N eight ; B 30 -7 493 676 ; -C 57 ; WX 500 ; N nine ; B 23 -17 492 676 ; -C 58 ; WX 333 ; N colon ; B 50 -11 261 441 ; -C 59 ; WX 333 ; N semicolon ; B 27 -129 261 441 ; -C 60 ; WX 675 ; N less ; B 84 -8 592 514 ; -C 61 ; WX 675 ; N equal ; B 86 120 590 386 ; -C 62 ; WX 675 ; N greater ; B 84 -8 592 514 ; -C 63 ; WX 500 ; N question ; B 132 -12 472 664 ; -C 64 ; WX 920 ; N at ; B 118 -18 806 666 ; -C 65 ; WX 611 ; N A ; B -51 0 564 668 ; -C 66 ; WX 611 ; N B ; B -8 0 588 653 ; -C 67 ; WX 667 ; N C ; B 66 -18 689 666 ; -C 68 ; WX 722 ; N D ; B -8 0 700 653 ; -C 69 ; WX 611 ; N E ; B -1 0 634 653 ; -C 70 ; WX 611 ; N F ; B 8 0 645 653 ; -C 71 ; WX 722 ; N G ; B 52 -18 722 666 ; -C 72 ; WX 722 ; N H ; B -8 0 767 653 ; -C 73 ; WX 333 ; N I ; B -8 0 384 653 ; -C 74 ; WX 444 ; N J ; B -6 -18 491 653 ; -C 75 ; WX 667 ; N K ; B 7 0 722 653 ; -C 76 ; WX 556 ; N L ; B -8 0 559 653 ; -C 77 ; WX 833 ; N M ; B -18 0 873 653 ; -C 78 ; WX 667 ; N N ; B -20 -15 727 653 ; -C 79 ; WX 722 ; N O ; B 60 -18 699 666 ; -C 80 ; WX 611 ; N P ; B 0 0 605 653 ; -C 81 ; WX 722 ; N Q ; B 59 -182 699 666 ; -C 82 ; WX 611 ; N R ; B -13 0 588 653 ; -C 83 ; WX 500 ; N S ; B 17 -18 508 667 ; -C 84 ; WX 556 ; N T ; B 59 0 633 653 ; -C 85 ; WX 722 ; N U ; B 102 -18 765 653 ; -C 86 ; WX 611 ; N V ; B 76 -18 688 653 ; -C 87 ; WX 833 ; N W ; B 71 -18 906 653 ; -C 88 ; WX 611 ; N X ; B -29 0 655 653 ; -C 89 ; WX 556 ; N Y ; B 78 0 633 653 ; -C 90 ; WX 556 ; N Z ; B -6 0 606 653 ; -C 91 ; WX 389 ; N bracketleft ; B 21 -153 391 663 ; -C 92 ; WX 278 ; N backslash ; B -41 -18 319 666 ; -C 93 ; WX 389 ; N bracketright ; B 12 -153 382 663 ; -C 94 ; WX 422 ; N asciicircum ; B 0 301 422 666 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 333 ; N quoteleft ; B 171 436 310 666 ; -C 97 ; WX 500 ; N a ; B 17 -11 476 441 ; -C 98 ; WX 500 ; N b ; B 23 -11 473 683 ; -C 99 ; WX 444 ; N c ; B 30 -11 425 441 ; -C 100 ; WX 500 ; N d ; B 15 -13 527 683 ; -C 101 ; WX 444 ; N e ; B 31 -11 412 441 ; -C 102 ; WX 278 ; N f ; B -147 -207 424 678 ; L i fi ; L l fl ; -C 103 ; WX 500 ; N g ; B 8 -206 472 441 ; -C 104 ; WX 500 ; N h ; B 19 -9 478 683 ; -C 105 ; WX 278 ; N i ; B 49 -11 264 654 ; -C 106 ; WX 278 ; N j ; B -124 -207 276 654 ; -C 107 ; WX 444 ; N k ; B 14 -11 461 683 ; -C 108 ; WX 278 ; N l ; B 41 -11 279 683 ; -C 109 ; WX 722 ; N m ; B 12 -9 704 441 ; -C 110 ; WX 500 ; N n ; B 14 -9 474 441 ; -C 111 ; WX 500 ; N o ; B 27 -11 468 441 ; -C 112 ; WX 500 ; N p ; B -75 -205 469 441 ; -C 113 ; WX 500 ; N q ; B 25 -209 483 441 ; -C 114 ; WX 389 ; N r ; B 45 0 412 441 ; -C 115 ; WX 389 ; N s ; B 16 -13 366 442 ; -C 116 ; WX 278 ; N t ; B 37 -11 296 546 ; -C 117 ; WX 500 ; N u ; B 42 -11 475 441 ; -C 118 ; WX 444 ; N v ; B 21 -18 426 441 ; -C 119 ; WX 667 ; N w ; B 16 -18 648 441 ; -C 120 ; WX 444 ; N x ; B -27 -11 447 441 ; -C 121 ; WX 444 ; N y ; B -24 -206 426 441 ; -C 122 ; WX 389 ; N z ; B -2 -81 380 428 ; -C 123 ; WX 400 ; N braceleft ; B 51 -177 407 687 ; -C 124 ; WX 275 ; N bar ; B 105 -217 171 783 ; -C 125 ; WX 400 ; N braceright ; B -7 -177 349 687 ; -C 126 ; WX 541 ; N asciitilde ; B 40 183 502 323 ; -C 161 ; WX 389 ; N exclamdown ; B 59 -205 322 473 ; -C 162 ; WX 500 ; N cent ; B 77 -143 472 560 ; -C 163 ; WX 500 ; N sterling ; B 10 -6 517 670 ; -C 164 ; WX 167 ; N fraction ; B -169 -10 337 676 ; -C 165 ; WX 500 ; N yen ; B 27 0 603 653 ; -C 166 ; WX 500 ; N florin ; B 25 -182 507 682 ; -C 167 ; WX 500 ; N section ; B 53 -162 461 666 ; -C 168 ; WX 500 ; N currency ; B -22 53 522 597 ; -C 169 ; WX 214 ; N quotesingle ; B 132 421 241 666 ; -C 170 ; WX 556 ; N quotedblleft ; B 166 436 514 666 ; -C 171 ; WX 500 ; N guillemotleft ; B 53 37 445 403 ; -C 172 ; WX 333 ; N guilsinglleft ; B 51 37 281 403 ; -C 173 ; WX 333 ; N guilsinglright ; B 52 37 282 403 ; -C 174 ; WX 500 ; N fi ; B -141 -207 481 681 ; -C 175 ; WX 500 ; N fl ; B -141 -204 518 682 ; -C 177 ; WX 500 ; N endash ; B -6 197 505 243 ; -C 178 ; WX 500 ; N dagger ; B 101 -159 488 666 ; -C 179 ; WX 500 ; N daggerdbl ; B 22 -143 491 666 ; -C 180 ; WX 250 ; N periodcentered ; B 70 199 181 310 ; -C 182 ; WX 523 ; N paragraph ; B 55 -123 616 653 ; -C 183 ; WX 350 ; N bullet ; B 40 191 310 461 ; -C 184 ; WX 333 ; N quotesinglbase ; B 44 -129 183 101 ; -C 185 ; WX 556 ; N quotedblbase ; B 57 -129 405 101 ; -C 186 ; WX 556 ; N quotedblright ; B 151 436 499 666 ; -C 187 ; WX 500 ; N guillemotright ; B 55 37 447 403 ; -C 188 ; WX 889 ; N ellipsis ; B 57 -11 762 100 ; -C 189 ; WX 1000 ; N perthousand ; B 25 -19 1010 706 ; -C 191 ; WX 500 ; N questiondown ; B 28 -205 368 471 ; -C 193 ; WX 333 ; N grave ; B 121 492 311 664 ; -C 194 ; WX 333 ; N acute ; B 180 494 403 664 ; -C 195 ; WX 333 ; N circumflex ; B 91 492 385 661 ; -C 196 ; WX 333 ; N tilde ; B 100 517 427 624 ; -C 197 ; WX 333 ; N macron ; B 99 532 411 583 ; -C 198 ; WX 333 ; N breve ; B 117 492 418 650 ; -C 199 ; WX 333 ; N dotaccent ; B 207 548 305 646 ; -C 200 ; WX 333 ; N dieresis ; B 107 548 405 646 ; -C 202 ; WX 333 ; N ring ; B 155 492 355 691 ; -C 203 ; WX 333 ; N cedilla ; B -30 -217 182 0 ; -C 205 ; WX 333 ; N hungarumlaut ; B 93 494 486 664 ; -C 206 ; WX 333 ; N ogonek ; B 20 -169 203 40 ; -C 207 ; WX 333 ; N caron ; B 121 492 426 661 ; -C 208 ; WX 889 ; N emdash ; B -6 197 894 243 ; -C 225 ; WX 889 ; N AE ; B -27 0 911 653 ; -C 227 ; WX 276 ; N ordfeminine ; B 42 406 352 676 ; -C 232 ; WX 556 ; N Lslash ; B -8 0 559 653 ; -C 233 ; WX 722 ; N Oslash ; B 60 -105 699 722 ; -C 234 ; WX 944 ; N OE ; B 49 -8 964 666 ; -C 235 ; WX 310 ; N ordmasculine ; B 67 406 362 676 ; -C 241 ; WX 667 ; N ae ; B 23 -11 640 441 ; -C 245 ; WX 278 ; N dotlessi ; B 49 -11 235 441 ; -C 248 ; WX 278 ; N lslash ; B 41 -11 312 683 ; -C 249 ; WX 500 ; N oslash ; B 28 -135 469 554 ; -C 250 ; WX 667 ; N oe ; B 20 -12 646 441 ; -C 251 ; WX 500 ; N germandbls ; B -168 -207 493 679 ; -C -1 ; WX 333 ; N Idieresis ; B -8 0 435 818 ; -C -1 ; WX 444 ; N eacute ; B 31 -11 459 664 ; -C -1 ; WX 500 ; N abreve ; B 17 -11 502 650 ; -C -1 ; WX 500 ; N uhungarumlaut ; B 42 -11 580 664 ; -C -1 ; WX 444 ; N ecaron ; B 31 -11 482 661 ; -C -1 ; WX 556 ; N Ydieresis ; B 78 0 633 818 ; -C -1 ; WX 675 ; N divide ; B 86 -11 590 517 ; -C -1 ; WX 556 ; N Yacute ; B 78 0 633 876 ; -C -1 ; WX 611 ; N Acircumflex ; B -51 0 564 873 ; -C -1 ; WX 500 ; N aacute ; B 17 -11 487 664 ; -C -1 ; WX 722 ; N Ucircumflex ; B 102 -18 765 873 ; -C -1 ; WX 444 ; N yacute ; B -24 -206 459 664 ; -C -1 ; WX 389 ; N scommaaccent ; B 16 -217 366 442 ; -C -1 ; WX 444 ; N ecircumflex ; B 31 -11 441 661 ; -C -1 ; WX 722 ; N Uring ; B 102 -18 765 883 ; -C -1 ; WX 722 ; N Udieresis ; B 102 -18 765 818 ; -C -1 ; WX 500 ; N aogonek ; B 17 -169 476 441 ; -C -1 ; WX 722 ; N Uacute ; B 102 -18 765 876 ; -C -1 ; WX 500 ; N uogonek ; B 42 -169 477 441 ; -C -1 ; WX 611 ; N Edieresis ; B -1 0 634 818 ; -C -1 ; WX 722 ; N Dcroat ; B -8 0 700 653 ; -C -1 ; WX 250 ; N commaaccent ; B 8 -217 133 -50 ; -C -1 ; WX 760 ; N copyright ; B 41 -18 719 666 ; -C -1 ; WX 611 ; N Emacron ; B -1 0 634 795 ; -C -1 ; WX 444 ; N ccaron ; B 30 -11 482 661 ; -C -1 ; WX 500 ; N aring ; B 17 -11 476 691 ; -C -1 ; WX 667 ; N Ncommaaccent ; B -20 -187 727 653 ; -C -1 ; WX 278 ; N lacute ; B 41 -11 395 876 ; -C -1 ; WX 500 ; N agrave ; B 17 -11 476 664 ; -C -1 ; WX 556 ; N Tcommaaccent ; B 59 -217 633 653 ; -C -1 ; WX 667 ; N Cacute ; B 66 -18 690 876 ; -C -1 ; WX 500 ; N atilde ; B 17 -11 511 624 ; -C -1 ; WX 611 ; N Edotaccent ; B -1 0 634 818 ; -C -1 ; WX 389 ; N scaron ; B 16 -13 454 661 ; -C -1 ; WX 389 ; N scedilla ; B 16 -217 366 442 ; -C -1 ; WX 278 ; N iacute ; B 49 -11 355 664 ; -C -1 ; WX 471 ; N lozenge ; B 13 0 459 724 ; -C -1 ; WX 611 ; N Rcaron ; B -13 0 588 873 ; -C -1 ; WX 722 ; N Gcommaaccent ; B 52 -217 722 666 ; -C -1 ; WX 500 ; N ucircumflex ; B 42 -11 475 661 ; -C -1 ; WX 500 ; N acircumflex ; B 17 -11 476 661 ; -C -1 ; WX 611 ; N Amacron ; B -51 0 564 795 ; -C -1 ; WX 389 ; N rcaron ; B 45 0 434 661 ; -C -1 ; WX 444 ; N ccedilla ; B 30 -217 425 441 ; -C -1 ; WX 556 ; N Zdotaccent ; B -6 0 606 818 ; -C -1 ; WX 611 ; N Thorn ; B 0 0 569 653 ; -C -1 ; WX 722 ; N Omacron ; B 60 -18 699 795 ; -C -1 ; WX 611 ; N Racute ; B -13 0 588 876 ; -C -1 ; WX 500 ; N Sacute ; B 17 -18 508 876 ; -C -1 ; WX 544 ; N dcaron ; B 15 -13 658 683 ; -C -1 ; WX 722 ; N Umacron ; B 102 -18 765 795 ; -C -1 ; WX 500 ; N uring ; B 42 -11 475 691 ; -C -1 ; WX 300 ; N threesuperior ; B 43 268 339 676 ; -C -1 ; WX 722 ; N Ograve ; B 60 -18 699 876 ; -C -1 ; WX 611 ; N Agrave ; B -51 0 564 876 ; -C -1 ; WX 611 ; N Abreve ; B -51 0 564 862 ; -C -1 ; WX 675 ; N multiply ; B 93 8 582 497 ; -C -1 ; WX 500 ; N uacute ; B 42 -11 477 664 ; -C -1 ; WX 556 ; N Tcaron ; B 59 0 633 873 ; -C -1 ; WX 476 ; N partialdiff ; B 17 -38 459 710 ; -C -1 ; WX 444 ; N ydieresis ; B -24 -206 441 606 ; -C -1 ; WX 667 ; N Nacute ; B -20 -15 727 876 ; -C -1 ; WX 278 ; N icircumflex ; B 33 -11 327 661 ; -C -1 ; WX 611 ; N Ecircumflex ; B -1 0 634 873 ; -C -1 ; WX 500 ; N adieresis ; B 17 -11 489 606 ; -C -1 ; WX 444 ; N edieresis ; B 31 -11 451 606 ; -C -1 ; WX 444 ; N cacute ; B 30 -11 459 664 ; -C -1 ; WX 500 ; N nacute ; B 14 -9 477 664 ; -C -1 ; WX 500 ; N umacron ; B 42 -11 485 583 ; -C -1 ; WX 667 ; N Ncaron ; B -20 -15 727 873 ; -C -1 ; WX 333 ; N Iacute ; B -8 0 433 876 ; -C -1 ; WX 675 ; N plusminus ; B 86 0 590 506 ; -C -1 ; WX 275 ; N brokenbar ; B 105 -142 171 708 ; -C -1 ; WX 760 ; N registered ; B 41 -18 719 666 ; -C -1 ; WX 722 ; N Gbreve ; B 52 -18 722 862 ; -C -1 ; WX 333 ; N Idotaccent ; B -8 0 384 818 ; -C -1 ; WX 600 ; N summation ; B 15 -10 585 706 ; -C -1 ; WX 611 ; N Egrave ; B -1 0 634 876 ; -C -1 ; WX 389 ; N racute ; B 45 0 431 664 ; -C -1 ; WX 500 ; N omacron ; B 27 -11 495 583 ; -C -1 ; WX 556 ; N Zacute ; B -6 0 606 876 ; -C -1 ; WX 556 ; N Zcaron ; B -6 0 606 873 ; -C -1 ; WX 549 ; N greaterequal ; B 26 0 523 658 ; -C -1 ; WX 722 ; N Eth ; B -8 0 700 653 ; -C -1 ; WX 667 ; N Ccedilla ; B 66 -217 689 666 ; -C -1 ; WX 278 ; N lcommaaccent ; B 22 -217 279 683 ; -C -1 ; WX 300 ; N tcaron ; B 37 -11 407 681 ; -C -1 ; WX 444 ; N eogonek ; B 31 -169 412 441 ; -C -1 ; WX 722 ; N Uogonek ; B 102 -184 765 653 ; -C -1 ; WX 611 ; N Aacute ; B -51 0 564 876 ; -C -1 ; WX 611 ; N Adieresis ; B -51 0 564 818 ; -C -1 ; WX 444 ; N egrave ; B 31 -11 412 664 ; -C -1 ; WX 389 ; N zacute ; B -2 -81 431 664 ; -C -1 ; WX 278 ; N iogonek ; B 49 -169 264 654 ; -C -1 ; WX 722 ; N Oacute ; B 60 -18 699 876 ; -C -1 ; WX 500 ; N oacute ; B 27 -11 487 664 ; -C -1 ; WX 500 ; N amacron ; B 17 -11 495 583 ; -C -1 ; WX 389 ; N sacute ; B 16 -13 431 664 ; -C -1 ; WX 278 ; N idieresis ; B 49 -11 352 606 ; -C -1 ; WX 722 ; N Ocircumflex ; B 60 -18 699 873 ; -C -1 ; WX 722 ; N Ugrave ; B 102 -18 765 876 ; -C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ; -C -1 ; WX 500 ; N thorn ; B -75 -205 469 683 ; -C -1 ; WX 300 ; N twosuperior ; B 33 271 324 676 ; -C -1 ; WX 722 ; N Odieresis ; B 60 -18 699 818 ; -C -1 ; WX 500 ; N mu ; B -30 -209 497 428 ; -C -1 ; WX 278 ; N igrave ; B 49 -11 284 664 ; -C -1 ; WX 500 ; N ohungarumlaut ; B 27 -11 590 664 ; -C -1 ; WX 611 ; N Eogonek ; B -1 -169 634 653 ; -C -1 ; WX 500 ; N dcroat ; B 15 -13 572 683 ; -C -1 ; WX 750 ; N threequarters ; B 23 -10 736 676 ; -C -1 ; WX 500 ; N Scedilla ; B 17 -217 508 667 ; -C -1 ; WX 300 ; N lcaron ; B 41 -11 407 683 ; -C -1 ; WX 667 ; N Kcommaaccent ; B 7 -217 722 653 ; -C -1 ; WX 556 ; N Lacute ; B -8 0 559 876 ; -C -1 ; WX 980 ; N trademark ; B 30 247 957 653 ; -C -1 ; WX 444 ; N edotaccent ; B 31 -11 412 606 ; -C -1 ; WX 333 ; N Igrave ; B -8 0 384 876 ; -C -1 ; WX 333 ; N Imacron ; B -8 0 441 795 ; -C -1 ; WX 611 ; N Lcaron ; B -8 0 586 653 ; -C -1 ; WX 750 ; N onehalf ; B 34 -10 749 676 ; -C -1 ; WX 549 ; N lessequal ; B 26 0 523 658 ; -C -1 ; WX 500 ; N ocircumflex ; B 27 -11 468 661 ; -C -1 ; WX 500 ; N ntilde ; B 14 -9 476 624 ; -C -1 ; WX 722 ; N Uhungarumlaut ; B 102 -18 765 876 ; -C -1 ; WX 611 ; N Eacute ; B -1 0 634 876 ; -C -1 ; WX 444 ; N emacron ; B 31 -11 457 583 ; -C -1 ; WX 500 ; N gbreve ; B 8 -206 487 650 ; -C -1 ; WX 750 ; N onequarter ; B 33 -10 736 676 ; -C -1 ; WX 500 ; N Scaron ; B 17 -18 520 873 ; -C -1 ; WX 500 ; N Scommaaccent ; B 17 -217 508 667 ; -C -1 ; WX 722 ; N Ohungarumlaut ; B 60 -18 699 876 ; -C -1 ; WX 400 ; N degree ; B 101 390 387 676 ; -C -1 ; WX 500 ; N ograve ; B 27 -11 468 664 ; -C -1 ; WX 667 ; N Ccaron ; B 66 -18 689 873 ; -C -1 ; WX 500 ; N ugrave ; B 42 -11 475 664 ; -C -1 ; WX 453 ; N radical ; B 2 -60 452 768 ; -C -1 ; WX 722 ; N Dcaron ; B -8 0 700 873 ; -C -1 ; WX 389 ; N rcommaaccent ; B -3 -217 412 441 ; -C -1 ; WX 667 ; N Ntilde ; B -20 -15 727 836 ; -C -1 ; WX 500 ; N otilde ; B 27 -11 496 624 ; -C -1 ; WX 611 ; N Rcommaaccent ; B -13 -187 588 653 ; -C -1 ; WX 556 ; N Lcommaaccent ; B -8 -217 559 653 ; -C -1 ; WX 611 ; N Atilde ; B -51 0 566 836 ; -C -1 ; WX 611 ; N Aogonek ; B -51 -169 566 668 ; -C -1 ; WX 611 ; N Aring ; B -51 0 564 883 ; -C -1 ; WX 722 ; N Otilde ; B 60 -18 699 836 ; -C -1 ; WX 389 ; N zdotaccent ; B -2 -81 380 606 ; -C -1 ; WX 611 ; N Ecaron ; B -1 0 634 873 ; -C -1 ; WX 333 ; N Iogonek ; B -8 -169 384 653 ; -C -1 ; WX 444 ; N kcommaaccent ; B 14 -187 461 683 ; -C -1 ; WX 675 ; N minus ; B 86 220 590 286 ; -C -1 ; WX 333 ; N Icircumflex ; B -8 0 425 873 ; -C -1 ; WX 500 ; N ncaron ; B 14 -9 510 661 ; -C -1 ; WX 278 ; N tcommaaccent ; B 2 -217 296 546 ; -C -1 ; WX 675 ; N logicalnot ; B 86 108 590 386 ; -C -1 ; WX 500 ; N odieresis ; B 27 -11 489 606 ; -C -1 ; WX 500 ; N udieresis ; B 42 -11 479 606 ; -C -1 ; WX 549 ; N notequal ; B 12 -29 537 541 ; -C -1 ; WX 500 ; N gcommaaccent ; B 8 -206 472 706 ; -C -1 ; WX 500 ; N eth ; B 27 -11 482 683 ; -C -1 ; WX 389 ; N zcaron ; B -2 -81 434 661 ; -C -1 ; WX 500 ; N ncommaaccent ; B 14 -187 474 441 ; -C -1 ; WX 300 ; N onesuperior ; B 43 271 284 676 ; -C -1 ; WX 278 ; N imacron ; B 46 -11 311 583 ; -C -1 ; WX 500 ; N Euro ; B 0 0 0 0 ; -EndCharMetrics -StartKernData -StartKernPairs 2321 -KPX A C -30 -KPX A Cacute -30 -KPX A Ccaron -30 -KPX A Ccedilla -30 -KPX A G -35 -KPX A Gbreve -35 -KPX A Gcommaaccent -35 -KPX A O -40 -KPX A Oacute -40 -KPX A Ocircumflex -40 -KPX A Odieresis -40 -KPX A Ograve -40 -KPX A Ohungarumlaut -40 -KPX A Omacron -40 -KPX A Oslash -40 -KPX A Otilde -40 -KPX A Q -40 -KPX A T -37 -KPX A Tcaron -37 -KPX A Tcommaaccent -37 -KPX A U -50 -KPX A Uacute -50 -KPX A Ucircumflex -50 -KPX A Udieresis -50 -KPX A Ugrave -50 -KPX A Uhungarumlaut -50 -KPX A Umacron -50 -KPX A Uogonek -50 -KPX A Uring -50 -KPX A V -105 -KPX A W -95 -KPX A Y -55 -KPX A Yacute -55 -KPX A Ydieresis -55 -KPX A quoteright -37 -KPX A u -20 -KPX A uacute -20 -KPX A ucircumflex -20 -KPX A udieresis -20 -KPX A ugrave -20 -KPX A uhungarumlaut -20 -KPX A umacron -20 -KPX A uogonek -20 -KPX A uring -20 -KPX A v -55 -KPX A w -55 -KPX A y -55 -KPX A yacute -55 -KPX A ydieresis -55 -KPX Aacute C -30 -KPX Aacute Cacute -30 -KPX Aacute Ccaron -30 -KPX Aacute Ccedilla -30 -KPX Aacute G -35 -KPX Aacute Gbreve -35 -KPX Aacute Gcommaaccent -35 -KPX Aacute O -40 -KPX Aacute Oacute -40 -KPX Aacute Ocircumflex -40 -KPX Aacute Odieresis -40 -KPX Aacute Ograve -40 -KPX Aacute Ohungarumlaut -40 -KPX Aacute Omacron -40 -KPX Aacute Oslash -40 -KPX Aacute Otilde -40 -KPX Aacute Q -40 -KPX Aacute T -37 -KPX Aacute Tcaron -37 -KPX Aacute Tcommaaccent -37 -KPX Aacute U -50 -KPX Aacute Uacute -50 -KPX Aacute Ucircumflex -50 -KPX Aacute Udieresis -50 -KPX Aacute Ugrave -50 -KPX Aacute Uhungarumlaut -50 -KPX Aacute Umacron -50 -KPX Aacute Uogonek -50 -KPX Aacute Uring -50 -KPX Aacute V -105 -KPX Aacute W -95 -KPX Aacute Y -55 -KPX Aacute Yacute -55 -KPX Aacute Ydieresis -55 -KPX Aacute quoteright -37 -KPX Aacute u -20 -KPX Aacute uacute -20 -KPX Aacute ucircumflex -20 -KPX Aacute udieresis -20 -KPX Aacute ugrave -20 -KPX Aacute uhungarumlaut -20 -KPX Aacute umacron -20 -KPX Aacute uogonek -20 -KPX Aacute uring -20 -KPX Aacute v -55 -KPX Aacute w -55 -KPX Aacute y -55 -KPX Aacute yacute -55 -KPX Aacute ydieresis -55 -KPX Abreve C -30 -KPX Abreve Cacute -30 -KPX Abreve Ccaron -30 -KPX Abreve Ccedilla -30 -KPX Abreve G -35 -KPX Abreve Gbreve -35 -KPX Abreve Gcommaaccent -35 -KPX Abreve O -40 -KPX Abreve Oacute -40 -KPX Abreve Ocircumflex -40 -KPX Abreve Odieresis -40 -KPX Abreve Ograve -40 -KPX Abreve Ohungarumlaut -40 -KPX Abreve Omacron -40 -KPX Abreve Oslash -40 -KPX Abreve Otilde -40 -KPX Abreve Q -40 -KPX Abreve T -37 -KPX Abreve Tcaron -37 -KPX Abreve Tcommaaccent -37 -KPX Abreve U -50 -KPX Abreve Uacute -50 -KPX Abreve Ucircumflex -50 -KPX Abreve Udieresis -50 -KPX Abreve Ugrave -50 -KPX Abreve Uhungarumlaut -50 -KPX Abreve Umacron -50 -KPX Abreve Uogonek -50 -KPX Abreve Uring -50 -KPX Abreve V -105 -KPX Abreve W -95 -KPX Abreve Y -55 -KPX Abreve Yacute -55 -KPX Abreve Ydieresis -55 -KPX Abreve quoteright -37 -KPX Abreve u -20 -KPX Abreve uacute -20 -KPX Abreve ucircumflex -20 -KPX Abreve udieresis -20 -KPX Abreve ugrave -20 -KPX Abreve uhungarumlaut -20 -KPX Abreve umacron -20 -KPX Abreve uogonek -20 -KPX Abreve uring -20 -KPX Abreve v -55 -KPX Abreve w -55 -KPX Abreve y -55 -KPX Abreve yacute -55 -KPX Abreve ydieresis -55 -KPX Acircumflex C -30 -KPX Acircumflex Cacute -30 -KPX Acircumflex Ccaron -30 -KPX Acircumflex Ccedilla -30 -KPX Acircumflex G -35 -KPX Acircumflex Gbreve -35 -KPX Acircumflex Gcommaaccent -35 -KPX Acircumflex O -40 -KPX Acircumflex Oacute -40 -KPX Acircumflex Ocircumflex -40 -KPX Acircumflex Odieresis -40 -KPX Acircumflex Ograve -40 -KPX Acircumflex Ohungarumlaut -40 -KPX Acircumflex Omacron -40 -KPX Acircumflex Oslash -40 -KPX Acircumflex Otilde -40 -KPX Acircumflex Q -40 -KPX Acircumflex T -37 -KPX Acircumflex Tcaron -37 -KPX Acircumflex Tcommaaccent -37 -KPX Acircumflex U -50 -KPX Acircumflex Uacute -50 -KPX Acircumflex Ucircumflex -50 -KPX Acircumflex Udieresis -50 -KPX Acircumflex Ugrave -50 -KPX Acircumflex Uhungarumlaut -50 -KPX Acircumflex Umacron -50 -KPX Acircumflex Uogonek -50 -KPX Acircumflex Uring -50 -KPX Acircumflex V -105 -KPX Acircumflex W -95 -KPX Acircumflex Y -55 -KPX Acircumflex Yacute -55 -KPX Acircumflex Ydieresis -55 -KPX Acircumflex quoteright -37 -KPX Acircumflex u -20 -KPX Acircumflex uacute -20 -KPX Acircumflex ucircumflex -20 -KPX Acircumflex udieresis -20 -KPX Acircumflex ugrave -20 -KPX Acircumflex uhungarumlaut -20 -KPX Acircumflex umacron -20 -KPX Acircumflex uogonek -20 -KPX Acircumflex uring -20 -KPX Acircumflex v -55 -KPX Acircumflex w -55 -KPX Acircumflex y -55 -KPX Acircumflex yacute -55 -KPX Acircumflex ydieresis -55 -KPX Adieresis C -30 -KPX Adieresis Cacute -30 -KPX Adieresis Ccaron -30 -KPX Adieresis Ccedilla -30 -KPX Adieresis G -35 -KPX Adieresis Gbreve -35 -KPX Adieresis Gcommaaccent -35 -KPX Adieresis O -40 -KPX Adieresis Oacute -40 -KPX Adieresis Ocircumflex -40 -KPX Adieresis Odieresis -40 -KPX Adieresis Ograve -40 -KPX Adieresis Ohungarumlaut -40 -KPX Adieresis Omacron -40 -KPX Adieresis Oslash -40 -KPX Adieresis Otilde -40 -KPX Adieresis Q -40 -KPX Adieresis T -37 -KPX Adieresis Tcaron -37 -KPX Adieresis Tcommaaccent -37 -KPX Adieresis U -50 -KPX Adieresis Uacute -50 -KPX Adieresis Ucircumflex -50 -KPX Adieresis Udieresis -50 -KPX Adieresis Ugrave -50 -KPX Adieresis Uhungarumlaut -50 -KPX Adieresis Umacron -50 -KPX Adieresis Uogonek -50 -KPX Adieresis Uring -50 -KPX Adieresis V -105 -KPX Adieresis W -95 -KPX Adieresis Y -55 -KPX Adieresis Yacute -55 -KPX Adieresis Ydieresis -55 -KPX Adieresis quoteright -37 -KPX Adieresis u -20 -KPX Adieresis uacute -20 -KPX Adieresis ucircumflex -20 -KPX Adieresis udieresis -20 -KPX Adieresis ugrave -20 -KPX Adieresis uhungarumlaut -20 -KPX Adieresis umacron -20 -KPX Adieresis uogonek -20 -KPX Adieresis uring -20 -KPX Adieresis v -55 -KPX Adieresis w -55 -KPX Adieresis y -55 -KPX Adieresis yacute -55 -KPX Adieresis ydieresis -55 -KPX Agrave C -30 -KPX Agrave Cacute -30 -KPX Agrave Ccaron -30 -KPX Agrave Ccedilla -30 -KPX Agrave G -35 -KPX Agrave Gbreve -35 -KPX Agrave Gcommaaccent -35 -KPX Agrave O -40 -KPX Agrave Oacute -40 -KPX Agrave Ocircumflex -40 -KPX Agrave Odieresis -40 -KPX Agrave Ograve -40 -KPX Agrave Ohungarumlaut -40 -KPX Agrave Omacron -40 -KPX Agrave Oslash -40 -KPX Agrave Otilde -40 -KPX Agrave Q -40 -KPX Agrave T -37 -KPX Agrave Tcaron -37 -KPX Agrave Tcommaaccent -37 -KPX Agrave U -50 -KPX Agrave Uacute -50 -KPX Agrave Ucircumflex -50 -KPX Agrave Udieresis -50 -KPX Agrave Ugrave -50 -KPX Agrave Uhungarumlaut -50 -KPX Agrave Umacron -50 -KPX Agrave Uogonek -50 -KPX Agrave Uring -50 -KPX Agrave V -105 -KPX Agrave W -95 -KPX Agrave Y -55 -KPX Agrave Yacute -55 -KPX Agrave Ydieresis -55 -KPX Agrave quoteright -37 -KPX Agrave u -20 -KPX Agrave uacute -20 -KPX Agrave ucircumflex -20 -KPX Agrave udieresis -20 -KPX Agrave ugrave -20 -KPX Agrave uhungarumlaut -20 -KPX Agrave umacron -20 -KPX Agrave uogonek -20 -KPX Agrave uring -20 -KPX Agrave v -55 -KPX Agrave w -55 -KPX Agrave y -55 -KPX Agrave yacute -55 -KPX Agrave ydieresis -55 -KPX Amacron C -30 -KPX Amacron Cacute -30 -KPX Amacron Ccaron -30 -KPX Amacron Ccedilla -30 -KPX Amacron G -35 -KPX Amacron Gbreve -35 -KPX Amacron Gcommaaccent -35 -KPX Amacron O -40 -KPX Amacron Oacute -40 -KPX Amacron Ocircumflex -40 -KPX Amacron Odieresis -40 -KPX Amacron Ograve -40 -KPX Amacron Ohungarumlaut -40 -KPX Amacron Omacron -40 -KPX Amacron Oslash -40 -KPX Amacron Otilde -40 -KPX Amacron Q -40 -KPX Amacron T -37 -KPX Amacron Tcaron -37 -KPX Amacron Tcommaaccent -37 -KPX Amacron U -50 -KPX Amacron Uacute -50 -KPX Amacron Ucircumflex -50 -KPX Amacron Udieresis -50 -KPX Amacron Ugrave -50 -KPX Amacron Uhungarumlaut -50 -KPX Amacron Umacron -50 -KPX Amacron Uogonek -50 -KPX Amacron Uring -50 -KPX Amacron V -105 -KPX Amacron W -95 -KPX Amacron Y -55 -KPX Amacron Yacute -55 -KPX Amacron Ydieresis -55 -KPX Amacron quoteright -37 -KPX Amacron u -20 -KPX Amacron uacute -20 -KPX Amacron ucircumflex -20 -KPX Amacron udieresis -20 -KPX Amacron ugrave -20 -KPX Amacron uhungarumlaut -20 -KPX Amacron umacron -20 -KPX Amacron uogonek -20 -KPX Amacron uring -20 -KPX Amacron v -55 -KPX Amacron w -55 -KPX Amacron y -55 -KPX Amacron yacute -55 -KPX Amacron ydieresis -55 -KPX Aogonek C -30 -KPX Aogonek Cacute -30 -KPX Aogonek Ccaron -30 -KPX Aogonek Ccedilla -30 -KPX Aogonek G -35 -KPX Aogonek Gbreve -35 -KPX Aogonek Gcommaaccent -35 -KPX Aogonek O -40 -KPX Aogonek Oacute -40 -KPX Aogonek Ocircumflex -40 -KPX Aogonek Odieresis -40 -KPX Aogonek Ograve -40 -KPX Aogonek Ohungarumlaut -40 -KPX Aogonek Omacron -40 -KPX Aogonek Oslash -40 -KPX Aogonek Otilde -40 -KPX Aogonek Q -40 -KPX Aogonek T -37 -KPX Aogonek Tcaron -37 -KPX Aogonek Tcommaaccent -37 -KPX Aogonek U -50 -KPX Aogonek Uacute -50 -KPX Aogonek Ucircumflex -50 -KPX Aogonek Udieresis -50 -KPX Aogonek Ugrave -50 -KPX Aogonek Uhungarumlaut -50 -KPX Aogonek Umacron -50 -KPX Aogonek Uogonek -50 -KPX Aogonek Uring -50 -KPX Aogonek V -105 -KPX Aogonek W -95 -KPX Aogonek Y -55 -KPX Aogonek Yacute -55 -KPX Aogonek Ydieresis -55 -KPX Aogonek quoteright -37 -KPX Aogonek u -20 -KPX Aogonek uacute -20 -KPX Aogonek ucircumflex -20 -KPX Aogonek udieresis -20 -KPX Aogonek ugrave -20 -KPX Aogonek uhungarumlaut -20 -KPX Aogonek umacron -20 -KPX Aogonek uogonek -20 -KPX Aogonek uring -20 -KPX Aogonek v -55 -KPX Aogonek w -55 -KPX Aogonek y -55 -KPX Aogonek yacute -55 -KPX Aogonek ydieresis -55 -KPX Aring C -30 -KPX Aring Cacute -30 -KPX Aring Ccaron -30 -KPX Aring Ccedilla -30 -KPX Aring G -35 -KPX Aring Gbreve -35 -KPX Aring Gcommaaccent -35 -KPX Aring O -40 -KPX Aring Oacute -40 -KPX Aring Ocircumflex -40 -KPX Aring Odieresis -40 -KPX Aring Ograve -40 -KPX Aring Ohungarumlaut -40 -KPX Aring Omacron -40 -KPX Aring Oslash -40 -KPX Aring Otilde -40 -KPX Aring Q -40 -KPX Aring T -37 -KPX Aring Tcaron -37 -KPX Aring Tcommaaccent -37 -KPX Aring U -50 -KPX Aring Uacute -50 -KPX Aring Ucircumflex -50 -KPX Aring Udieresis -50 -KPX Aring Ugrave -50 -KPX Aring Uhungarumlaut -50 -KPX Aring Umacron -50 -KPX Aring Uogonek -50 -KPX Aring Uring -50 -KPX Aring V -105 -KPX Aring W -95 -KPX Aring Y -55 -KPX Aring Yacute -55 -KPX Aring Ydieresis -55 -KPX Aring quoteright -37 -KPX Aring u -20 -KPX Aring uacute -20 -KPX Aring ucircumflex -20 -KPX Aring udieresis -20 -KPX Aring ugrave -20 -KPX Aring uhungarumlaut -20 -KPX Aring umacron -20 -KPX Aring uogonek -20 -KPX Aring uring -20 -KPX Aring v -55 -KPX Aring w -55 -KPX Aring y -55 -KPX Aring yacute -55 -KPX Aring ydieresis -55 -KPX Atilde C -30 -KPX Atilde Cacute -30 -KPX Atilde Ccaron -30 -KPX Atilde Ccedilla -30 -KPX Atilde G -35 -KPX Atilde Gbreve -35 -KPX Atilde Gcommaaccent -35 -KPX Atilde O -40 -KPX Atilde Oacute -40 -KPX Atilde Ocircumflex -40 -KPX Atilde Odieresis -40 -KPX Atilde Ograve -40 -KPX Atilde Ohungarumlaut -40 -KPX Atilde Omacron -40 -KPX Atilde Oslash -40 -KPX Atilde Otilde -40 -KPX Atilde Q -40 -KPX Atilde T -37 -KPX Atilde Tcaron -37 -KPX Atilde Tcommaaccent -37 -KPX Atilde U -50 -KPX Atilde Uacute -50 -KPX Atilde Ucircumflex -50 -KPX Atilde Udieresis -50 -KPX Atilde Ugrave -50 -KPX Atilde Uhungarumlaut -50 -KPX Atilde Umacron -50 -KPX Atilde Uogonek -50 -KPX Atilde Uring -50 -KPX Atilde V -105 -KPX Atilde W -95 -KPX Atilde Y -55 -KPX Atilde Yacute -55 -KPX Atilde Ydieresis -55 -KPX Atilde quoteright -37 -KPX Atilde u -20 -KPX Atilde uacute -20 -KPX Atilde ucircumflex -20 -KPX Atilde udieresis -20 -KPX Atilde ugrave -20 -KPX Atilde uhungarumlaut -20 -KPX Atilde umacron -20 -KPX Atilde uogonek -20 -KPX Atilde uring -20 -KPX Atilde v -55 -KPX Atilde w -55 -KPX Atilde y -55 -KPX Atilde yacute -55 -KPX Atilde ydieresis -55 -KPX B A -25 -KPX B Aacute -25 -KPX B Abreve -25 -KPX B Acircumflex -25 -KPX B Adieresis -25 -KPX B Agrave -25 -KPX B Amacron -25 -KPX B Aogonek -25 -KPX B Aring -25 -KPX B Atilde -25 -KPX B U -10 -KPX B Uacute -10 -KPX B Ucircumflex -10 -KPX B Udieresis -10 -KPX B Ugrave -10 -KPX B Uhungarumlaut -10 -KPX B Umacron -10 -KPX B Uogonek -10 -KPX B Uring -10 -KPX D A -35 -KPX D Aacute -35 -KPX D Abreve -35 -KPX D Acircumflex -35 -KPX D Adieresis -35 -KPX D Agrave -35 -KPX D Amacron -35 -KPX D Aogonek -35 -KPX D Aring -35 -KPX D Atilde -35 -KPX D V -40 -KPX D W -40 -KPX D Y -40 -KPX D Yacute -40 -KPX D Ydieresis -40 -KPX Dcaron A -35 -KPX Dcaron Aacute -35 -KPX Dcaron Abreve -35 -KPX Dcaron Acircumflex -35 -KPX Dcaron Adieresis -35 -KPX Dcaron Agrave -35 -KPX Dcaron Amacron -35 -KPX Dcaron Aogonek -35 -KPX Dcaron Aring -35 -KPX Dcaron Atilde -35 -KPX Dcaron V -40 -KPX Dcaron W -40 -KPX Dcaron Y -40 -KPX Dcaron Yacute -40 -KPX Dcaron Ydieresis -40 -KPX Dcroat A -35 -KPX Dcroat Aacute -35 -KPX Dcroat Abreve -35 -KPX Dcroat Acircumflex -35 -KPX Dcroat Adieresis -35 -KPX Dcroat Agrave -35 -KPX Dcroat Amacron -35 -KPX Dcroat Aogonek -35 -KPX Dcroat Aring -35 -KPX Dcroat Atilde -35 -KPX Dcroat V -40 -KPX Dcroat W -40 -KPX Dcroat Y -40 -KPX Dcroat Yacute -40 -KPX Dcroat Ydieresis -40 -KPX F A -115 -KPX F Aacute -115 -KPX F Abreve -115 -KPX F Acircumflex -115 -KPX F Adieresis -115 -KPX F Agrave -115 -KPX F Amacron -115 -KPX F Aogonek -115 -KPX F Aring -115 -KPX F Atilde -115 -KPX F a -75 -KPX F aacute -75 -KPX F abreve -75 -KPX F acircumflex -75 -KPX F adieresis -75 -KPX F agrave -75 -KPX F amacron -75 -KPX F aogonek -75 -KPX F aring -75 -KPX F atilde -75 -KPX F comma -135 -KPX F e -75 -KPX F eacute -75 -KPX F ecaron -75 -KPX F ecircumflex -75 -KPX F edieresis -75 -KPX F edotaccent -75 -KPX F egrave -75 -KPX F emacron -75 -KPX F eogonek -75 -KPX F i -45 -KPX F iacute -45 -KPX F icircumflex -45 -KPX F idieresis -45 -KPX F igrave -45 -KPX F imacron -45 -KPX F iogonek -45 -KPX F o -105 -KPX F oacute -105 -KPX F ocircumflex -105 -KPX F odieresis -105 -KPX F ograve -105 -KPX F ohungarumlaut -105 -KPX F omacron -105 -KPX F oslash -105 -KPX F otilde -105 -KPX F period -135 -KPX F r -55 -KPX F racute -55 -KPX F rcaron -55 -KPX F rcommaaccent -55 -KPX J A -40 -KPX J Aacute -40 -KPX J Abreve -40 -KPX J Acircumflex -40 -KPX J Adieresis -40 -KPX J Agrave -40 -KPX J Amacron -40 -KPX J Aogonek -40 -KPX J Aring -40 -KPX J Atilde -40 -KPX J a -35 -KPX J aacute -35 -KPX J abreve -35 -KPX J acircumflex -35 -KPX J adieresis -35 -KPX J agrave -35 -KPX J amacron -35 -KPX J aogonek -35 -KPX J aring -35 -KPX J atilde -35 -KPX J comma -25 -KPX J e -25 -KPX J eacute -25 -KPX J ecaron -25 -KPX J ecircumflex -25 -KPX J edieresis -25 -KPX J edotaccent -25 -KPX J egrave -25 -KPX J emacron -25 -KPX J eogonek -25 -KPX J o -25 -KPX J oacute -25 -KPX J ocircumflex -25 -KPX J odieresis -25 -KPX J ograve -25 -KPX J ohungarumlaut -25 -KPX J omacron -25 -KPX J oslash -25 -KPX J otilde -25 -KPX J period -25 -KPX J u -35 -KPX J uacute -35 -KPX J ucircumflex -35 -KPX J udieresis -35 -KPX J ugrave -35 -KPX J uhungarumlaut -35 -KPX J umacron -35 -KPX J uogonek -35 -KPX J uring -35 -KPX K O -50 -KPX K Oacute -50 -KPX K Ocircumflex -50 -KPX K Odieresis -50 -KPX K Ograve -50 -KPX K Ohungarumlaut -50 -KPX K Omacron -50 -KPX K Oslash -50 -KPX K Otilde -50 -KPX K e -35 -KPX K eacute -35 -KPX K ecaron -35 -KPX K ecircumflex -35 -KPX K edieresis -35 -KPX K edotaccent -35 -KPX K egrave -35 -KPX K emacron -35 -KPX K eogonek -35 -KPX K o -40 -KPX K oacute -40 -KPX K ocircumflex -40 -KPX K odieresis -40 -KPX K ograve -40 -KPX K ohungarumlaut -40 -KPX K omacron -40 -KPX K oslash -40 -KPX K otilde -40 -KPX K u -40 -KPX K uacute -40 -KPX K ucircumflex -40 -KPX K udieresis -40 -KPX K ugrave -40 -KPX K uhungarumlaut -40 -KPX K umacron -40 -KPX K uogonek -40 -KPX K uring -40 -KPX K y -40 -KPX K yacute -40 -KPX K ydieresis -40 -KPX Kcommaaccent O -50 -KPX Kcommaaccent Oacute -50 -KPX Kcommaaccent Ocircumflex -50 -KPX Kcommaaccent Odieresis -50 -KPX Kcommaaccent Ograve -50 -KPX Kcommaaccent Ohungarumlaut -50 -KPX Kcommaaccent Omacron -50 -KPX Kcommaaccent Oslash -50 -KPX Kcommaaccent Otilde -50 -KPX Kcommaaccent e -35 -KPX Kcommaaccent eacute -35 -KPX Kcommaaccent ecaron -35 -KPX Kcommaaccent ecircumflex -35 -KPX Kcommaaccent edieresis -35 -KPX Kcommaaccent edotaccent -35 -KPX Kcommaaccent egrave -35 -KPX Kcommaaccent emacron -35 -KPX Kcommaaccent eogonek -35 -KPX Kcommaaccent o -40 -KPX Kcommaaccent oacute -40 -KPX Kcommaaccent ocircumflex -40 -KPX Kcommaaccent odieresis -40 -KPX Kcommaaccent ograve -40 -KPX Kcommaaccent ohungarumlaut -40 -KPX Kcommaaccent omacron -40 -KPX Kcommaaccent oslash -40 -KPX Kcommaaccent otilde -40 -KPX Kcommaaccent u -40 -KPX Kcommaaccent uacute -40 -KPX Kcommaaccent ucircumflex -40 -KPX Kcommaaccent udieresis -40 -KPX Kcommaaccent ugrave -40 -KPX Kcommaaccent uhungarumlaut -40 -KPX Kcommaaccent umacron -40 -KPX Kcommaaccent uogonek -40 -KPX Kcommaaccent uring -40 -KPX Kcommaaccent y -40 -KPX Kcommaaccent yacute -40 -KPX Kcommaaccent ydieresis -40 -KPX L T -20 -KPX L Tcaron -20 -KPX L Tcommaaccent -20 -KPX L V -55 -KPX L W -55 -KPX L Y -20 -KPX L Yacute -20 -KPX L Ydieresis -20 -KPX L quoteright -37 -KPX L y -30 -KPX L yacute -30 -KPX L ydieresis -30 -KPX Lacute T -20 -KPX Lacute Tcaron -20 -KPX Lacute Tcommaaccent -20 -KPX Lacute V -55 -KPX Lacute W -55 -KPX Lacute Y -20 -KPX Lacute Yacute -20 -KPX Lacute Ydieresis -20 -KPX Lacute quoteright -37 -KPX Lacute y -30 -KPX Lacute yacute -30 -KPX Lacute ydieresis -30 -KPX Lcommaaccent T -20 -KPX Lcommaaccent Tcaron -20 -KPX Lcommaaccent Tcommaaccent -20 -KPX Lcommaaccent V -55 -KPX Lcommaaccent W -55 -KPX Lcommaaccent Y -20 -KPX Lcommaaccent Yacute -20 -KPX Lcommaaccent Ydieresis -20 -KPX Lcommaaccent quoteright -37 -KPX Lcommaaccent y -30 -KPX Lcommaaccent yacute -30 -KPX Lcommaaccent ydieresis -30 -KPX Lslash T -20 -KPX Lslash Tcaron -20 -KPX Lslash Tcommaaccent -20 -KPX Lslash V -55 -KPX Lslash W -55 -KPX Lslash Y -20 -KPX Lslash Yacute -20 -KPX Lslash Ydieresis -20 -KPX Lslash quoteright -37 -KPX Lslash y -30 -KPX Lslash yacute -30 -KPX Lslash ydieresis -30 -KPX N A -27 -KPX N Aacute -27 -KPX N Abreve -27 -KPX N Acircumflex -27 -KPX N Adieresis -27 -KPX N Agrave -27 -KPX N Amacron -27 -KPX N Aogonek -27 -KPX N Aring -27 -KPX N Atilde -27 -KPX Nacute A -27 -KPX Nacute Aacute -27 -KPX Nacute Abreve -27 -KPX Nacute Acircumflex -27 -KPX Nacute Adieresis -27 -KPX Nacute Agrave -27 -KPX Nacute Amacron -27 -KPX Nacute Aogonek -27 -KPX Nacute Aring -27 -KPX Nacute Atilde -27 -KPX Ncaron A -27 -KPX Ncaron Aacute -27 -KPX Ncaron Abreve -27 -KPX Ncaron Acircumflex -27 -KPX Ncaron Adieresis -27 -KPX Ncaron Agrave -27 -KPX Ncaron Amacron -27 -KPX Ncaron Aogonek -27 -KPX Ncaron Aring -27 -KPX Ncaron Atilde -27 -KPX Ncommaaccent A -27 -KPX Ncommaaccent Aacute -27 -KPX Ncommaaccent Abreve -27 -KPX Ncommaaccent Acircumflex -27 -KPX Ncommaaccent Adieresis -27 -KPX Ncommaaccent Agrave -27 -KPX Ncommaaccent Amacron -27 -KPX Ncommaaccent Aogonek -27 -KPX Ncommaaccent Aring -27 -KPX Ncommaaccent Atilde -27 -KPX Ntilde A -27 -KPX Ntilde Aacute -27 -KPX Ntilde Abreve -27 -KPX Ntilde Acircumflex -27 -KPX Ntilde Adieresis -27 -KPX Ntilde Agrave -27 -KPX Ntilde Amacron -27 -KPX Ntilde Aogonek -27 -KPX Ntilde Aring -27 -KPX Ntilde Atilde -27 -KPX O A -55 -KPX O Aacute -55 -KPX O Abreve -55 -KPX O Acircumflex -55 -KPX O Adieresis -55 -KPX O Agrave -55 -KPX O Amacron -55 -KPX O Aogonek -55 -KPX O Aring -55 -KPX O Atilde -55 -KPX O T -40 -KPX O Tcaron -40 -KPX O Tcommaaccent -40 -KPX O V -50 -KPX O W -50 -KPX O X -40 -KPX O Y -50 -KPX O Yacute -50 -KPX O Ydieresis -50 -KPX Oacute A -55 -KPX Oacute Aacute -55 -KPX Oacute Abreve -55 -KPX Oacute Acircumflex -55 -KPX Oacute Adieresis -55 -KPX Oacute Agrave -55 -KPX Oacute Amacron -55 -KPX Oacute Aogonek -55 -KPX Oacute Aring -55 -KPX Oacute Atilde -55 -KPX Oacute T -40 -KPX Oacute Tcaron -40 -KPX Oacute Tcommaaccent -40 -KPX Oacute V -50 -KPX Oacute W -50 -KPX Oacute X -40 -KPX Oacute Y -50 -KPX Oacute Yacute -50 -KPX Oacute Ydieresis -50 -KPX Ocircumflex A -55 -KPX Ocircumflex Aacute -55 -KPX Ocircumflex Abreve -55 -KPX Ocircumflex Acircumflex -55 -KPX Ocircumflex Adieresis -55 -KPX Ocircumflex Agrave -55 -KPX Ocircumflex Amacron -55 -KPX Ocircumflex Aogonek -55 -KPX Ocircumflex Aring -55 -KPX Ocircumflex Atilde -55 -KPX Ocircumflex T -40 -KPX Ocircumflex Tcaron -40 -KPX Ocircumflex Tcommaaccent -40 -KPX Ocircumflex V -50 -KPX Ocircumflex W -50 -KPX Ocircumflex X -40 -KPX Ocircumflex Y -50 -KPX Ocircumflex Yacute -50 -KPX Ocircumflex Ydieresis -50 -KPX Odieresis A -55 -KPX Odieresis Aacute -55 -KPX Odieresis Abreve -55 -KPX Odieresis Acircumflex -55 -KPX Odieresis Adieresis -55 -KPX Odieresis Agrave -55 -KPX Odieresis Amacron -55 -KPX Odieresis Aogonek -55 -KPX Odieresis Aring -55 -KPX Odieresis Atilde -55 -KPX Odieresis T -40 -KPX Odieresis Tcaron -40 -KPX Odieresis Tcommaaccent -40 -KPX Odieresis V -50 -KPX Odieresis W -50 -KPX Odieresis X -40 -KPX Odieresis Y -50 -KPX Odieresis Yacute -50 -KPX Odieresis Ydieresis -50 -KPX Ograve A -55 -KPX Ograve Aacute -55 -KPX Ograve Abreve -55 -KPX Ograve Acircumflex -55 -KPX Ograve Adieresis -55 -KPX Ograve Agrave -55 -KPX Ograve Amacron -55 -KPX Ograve Aogonek -55 -KPX Ograve Aring -55 -KPX Ograve Atilde -55 -KPX Ograve T -40 -KPX Ograve Tcaron -40 -KPX Ograve Tcommaaccent -40 -KPX Ograve V -50 -KPX Ograve W -50 -KPX Ograve X -40 -KPX Ograve Y -50 -KPX Ograve Yacute -50 -KPX Ograve Ydieresis -50 -KPX Ohungarumlaut A -55 -KPX Ohungarumlaut Aacute -55 -KPX Ohungarumlaut Abreve -55 -KPX Ohungarumlaut Acircumflex -55 -KPX Ohungarumlaut Adieresis -55 -KPX Ohungarumlaut Agrave -55 -KPX Ohungarumlaut Amacron -55 -KPX Ohungarumlaut Aogonek -55 -KPX Ohungarumlaut Aring -55 -KPX Ohungarumlaut Atilde -55 -KPX Ohungarumlaut T -40 -KPX Ohungarumlaut Tcaron -40 -KPX Ohungarumlaut Tcommaaccent -40 -KPX Ohungarumlaut V -50 -KPX Ohungarumlaut W -50 -KPX Ohungarumlaut X -40 -KPX Ohungarumlaut Y -50 -KPX Ohungarumlaut Yacute -50 -KPX Ohungarumlaut Ydieresis -50 -KPX Omacron A -55 -KPX Omacron Aacute -55 -KPX Omacron Abreve -55 -KPX Omacron Acircumflex -55 -KPX Omacron Adieresis -55 -KPX Omacron Agrave -55 -KPX Omacron Amacron -55 -KPX Omacron Aogonek -55 -KPX Omacron Aring -55 -KPX Omacron Atilde -55 -KPX Omacron T -40 -KPX Omacron Tcaron -40 -KPX Omacron Tcommaaccent -40 -KPX Omacron V -50 -KPX Omacron W -50 -KPX Omacron X -40 -KPX Omacron Y -50 -KPX Omacron Yacute -50 -KPX Omacron Ydieresis -50 -KPX Oslash A -55 -KPX Oslash Aacute -55 -KPX Oslash Abreve -55 -KPX Oslash Acircumflex -55 -KPX Oslash Adieresis -55 -KPX Oslash Agrave -55 -KPX Oslash Amacron -55 -KPX Oslash Aogonek -55 -KPX Oslash Aring -55 -KPX Oslash Atilde -55 -KPX Oslash T -40 -KPX Oslash Tcaron -40 -KPX Oslash Tcommaaccent -40 -KPX Oslash V -50 -KPX Oslash W -50 -KPX Oslash X -40 -KPX Oslash Y -50 -KPX Oslash Yacute -50 -KPX Oslash Ydieresis -50 -KPX Otilde A -55 -KPX Otilde Aacute -55 -KPX Otilde Abreve -55 -KPX Otilde Acircumflex -55 -KPX Otilde Adieresis -55 -KPX Otilde Agrave -55 -KPX Otilde Amacron -55 -KPX Otilde Aogonek -55 -KPX Otilde Aring -55 -KPX Otilde Atilde -55 -KPX Otilde T -40 -KPX Otilde Tcaron -40 -KPX Otilde Tcommaaccent -40 -KPX Otilde V -50 -KPX Otilde W -50 -KPX Otilde X -40 -KPX Otilde Y -50 -KPX Otilde Yacute -50 -KPX Otilde Ydieresis -50 -KPX P A -90 -KPX P Aacute -90 -KPX P Abreve -90 -KPX P Acircumflex -90 -KPX P Adieresis -90 -KPX P Agrave -90 -KPX P Amacron -90 -KPX P Aogonek -90 -KPX P Aring -90 -KPX P Atilde -90 -KPX P a -80 -KPX P aacute -80 -KPX P abreve -80 -KPX P acircumflex -80 -KPX P adieresis -80 -KPX P agrave -80 -KPX P amacron -80 -KPX P aogonek -80 -KPX P aring -80 -KPX P atilde -80 -KPX P comma -135 -KPX P e -80 -KPX P eacute -80 -KPX P ecaron -80 -KPX P ecircumflex -80 -KPX P edieresis -80 -KPX P edotaccent -80 -KPX P egrave -80 -KPX P emacron -80 -KPX P eogonek -80 -KPX P o -80 -KPX P oacute -80 -KPX P ocircumflex -80 -KPX P odieresis -80 -KPX P ograve -80 -KPX P ohungarumlaut -80 -KPX P omacron -80 -KPX P oslash -80 -KPX P otilde -80 -KPX P period -135 -KPX Q U -10 -KPX Q Uacute -10 -KPX Q Ucircumflex -10 -KPX Q Udieresis -10 -KPX Q Ugrave -10 -KPX Q Uhungarumlaut -10 -KPX Q Umacron -10 -KPX Q Uogonek -10 -KPX Q Uring -10 -KPX R O -40 -KPX R Oacute -40 -KPX R Ocircumflex -40 -KPX R Odieresis -40 -KPX R Ograve -40 -KPX R Ohungarumlaut -40 -KPX R Omacron -40 -KPX R Oslash -40 -KPX R Otilde -40 -KPX R U -40 -KPX R Uacute -40 -KPX R Ucircumflex -40 -KPX R Udieresis -40 -KPX R Ugrave -40 -KPX R Uhungarumlaut -40 -KPX R Umacron -40 -KPX R Uogonek -40 -KPX R Uring -40 -KPX R V -18 -KPX R W -18 -KPX R Y -18 -KPX R Yacute -18 -KPX R Ydieresis -18 -KPX Racute O -40 -KPX Racute Oacute -40 -KPX Racute Ocircumflex -40 -KPX Racute Odieresis -40 -KPX Racute Ograve -40 -KPX Racute Ohungarumlaut -40 -KPX Racute Omacron -40 -KPX Racute Oslash -40 -KPX Racute Otilde -40 -KPX Racute U -40 -KPX Racute Uacute -40 -KPX Racute Ucircumflex -40 -KPX Racute Udieresis -40 -KPX Racute Ugrave -40 -KPX Racute Uhungarumlaut -40 -KPX Racute Umacron -40 -KPX Racute Uogonek -40 -KPX Racute Uring -40 -KPX Racute V -18 -KPX Racute W -18 -KPX Racute Y -18 -KPX Racute Yacute -18 -KPX Racute Ydieresis -18 -KPX Rcaron O -40 -KPX Rcaron Oacute -40 -KPX Rcaron Ocircumflex -40 -KPX Rcaron Odieresis -40 -KPX Rcaron Ograve -40 -KPX Rcaron Ohungarumlaut -40 -KPX Rcaron Omacron -40 -KPX Rcaron Oslash -40 -KPX Rcaron Otilde -40 -KPX Rcaron U -40 -KPX Rcaron Uacute -40 -KPX Rcaron Ucircumflex -40 -KPX Rcaron Udieresis -40 -KPX Rcaron Ugrave -40 -KPX Rcaron Uhungarumlaut -40 -KPX Rcaron Umacron -40 -KPX Rcaron Uogonek -40 -KPX Rcaron Uring -40 -KPX Rcaron V -18 -KPX Rcaron W -18 -KPX Rcaron Y -18 -KPX Rcaron Yacute -18 -KPX Rcaron Ydieresis -18 -KPX Rcommaaccent O -40 -KPX Rcommaaccent Oacute -40 -KPX Rcommaaccent Ocircumflex -40 -KPX Rcommaaccent Odieresis -40 -KPX Rcommaaccent Ograve -40 -KPX Rcommaaccent Ohungarumlaut -40 -KPX Rcommaaccent Omacron -40 -KPX Rcommaaccent Oslash -40 -KPX Rcommaaccent Otilde -40 -KPX Rcommaaccent U -40 -KPX Rcommaaccent Uacute -40 -KPX Rcommaaccent Ucircumflex -40 -KPX Rcommaaccent Udieresis -40 -KPX Rcommaaccent Ugrave -40 -KPX Rcommaaccent Uhungarumlaut -40 -KPX Rcommaaccent Umacron -40 -KPX Rcommaaccent Uogonek -40 -KPX Rcommaaccent Uring -40 -KPX Rcommaaccent V -18 -KPX Rcommaaccent W -18 -KPX Rcommaaccent Y -18 -KPX Rcommaaccent Yacute -18 -KPX Rcommaaccent Ydieresis -18 -KPX T A -50 -KPX T Aacute -50 -KPX T Abreve -50 -KPX T Acircumflex -50 -KPX T Adieresis -50 -KPX T Agrave -50 -KPX T Amacron -50 -KPX T Aogonek -50 -KPX T Aring -50 -KPX T Atilde -50 -KPX T O -18 -KPX T Oacute -18 -KPX T Ocircumflex -18 -KPX T Odieresis -18 -KPX T Ograve -18 -KPX T Ohungarumlaut -18 -KPX T Omacron -18 -KPX T Oslash -18 -KPX T Otilde -18 -KPX T a -92 -KPX T aacute -92 -KPX T abreve -92 -KPX T acircumflex -92 -KPX T adieresis -92 -KPX T agrave -92 -KPX T amacron -92 -KPX T aogonek -92 -KPX T aring -92 -KPX T atilde -92 -KPX T colon -55 -KPX T comma -74 -KPX T e -92 -KPX T eacute -92 -KPX T ecaron -92 -KPX T ecircumflex -52 -KPX T edieresis -52 -KPX T edotaccent -92 -KPX T egrave -52 -KPX T emacron -52 -KPX T eogonek -92 -KPX T hyphen -74 -KPX T i -55 -KPX T iacute -55 -KPX T iogonek -55 -KPX T o -92 -KPX T oacute -92 -KPX T ocircumflex -92 -KPX T odieresis -92 -KPX T ograve -92 -KPX T ohungarumlaut -92 -KPX T omacron -92 -KPX T oslash -92 -KPX T otilde -92 -KPX T period -74 -KPX T r -55 -KPX T racute -55 -KPX T rcaron -55 -KPX T rcommaaccent -55 -KPX T semicolon -65 -KPX T u -55 -KPX T uacute -55 -KPX T ucircumflex -55 -KPX T udieresis -55 -KPX T ugrave -55 -KPX T uhungarumlaut -55 -KPX T umacron -55 -KPX T uogonek -55 -KPX T uring -55 -KPX T w -74 -KPX T y -74 -KPX T yacute -74 -KPX T ydieresis -34 -KPX Tcaron A -50 -KPX Tcaron Aacute -50 -KPX Tcaron Abreve -50 -KPX Tcaron Acircumflex -50 -KPX Tcaron Adieresis -50 -KPX Tcaron Agrave -50 -KPX Tcaron Amacron -50 -KPX Tcaron Aogonek -50 -KPX Tcaron Aring -50 -KPX Tcaron Atilde -50 -KPX Tcaron O -18 -KPX Tcaron Oacute -18 -KPX Tcaron Ocircumflex -18 -KPX Tcaron Odieresis -18 -KPX Tcaron Ograve -18 -KPX Tcaron Ohungarumlaut -18 -KPX Tcaron Omacron -18 -KPX Tcaron Oslash -18 -KPX Tcaron Otilde -18 -KPX Tcaron a -92 -KPX Tcaron aacute -92 -KPX Tcaron abreve -92 -KPX Tcaron acircumflex -92 -KPX Tcaron adieresis -92 -KPX Tcaron agrave -92 -KPX Tcaron amacron -92 -KPX Tcaron aogonek -92 -KPX Tcaron aring -92 -KPX Tcaron atilde -92 -KPX Tcaron colon -55 -KPX Tcaron comma -74 -KPX Tcaron e -92 -KPX Tcaron eacute -92 -KPX Tcaron ecaron -92 -KPX Tcaron ecircumflex -52 -KPX Tcaron edieresis -52 -KPX Tcaron edotaccent -92 -KPX Tcaron egrave -52 -KPX Tcaron emacron -52 -KPX Tcaron eogonek -92 -KPX Tcaron hyphen -74 -KPX Tcaron i -55 -KPX Tcaron iacute -55 -KPX Tcaron iogonek -55 -KPX Tcaron o -92 -KPX Tcaron oacute -92 -KPX Tcaron ocircumflex -92 -KPX Tcaron odieresis -92 -KPX Tcaron ograve -92 -KPX Tcaron ohungarumlaut -92 -KPX Tcaron omacron -92 -KPX Tcaron oslash -92 -KPX Tcaron otilde -92 -KPX Tcaron period -74 -KPX Tcaron r -55 -KPX Tcaron racute -55 -KPX Tcaron rcaron -55 -KPX Tcaron rcommaaccent -55 -KPX Tcaron semicolon -65 -KPX Tcaron u -55 -KPX Tcaron uacute -55 -KPX Tcaron ucircumflex -55 -KPX Tcaron udieresis -55 -KPX Tcaron ugrave -55 -KPX Tcaron uhungarumlaut -55 -KPX Tcaron umacron -55 -KPX Tcaron uogonek -55 -KPX Tcaron uring -55 -KPX Tcaron w -74 -KPX Tcaron y -74 -KPX Tcaron yacute -74 -KPX Tcaron ydieresis -34 -KPX Tcommaaccent A -50 -KPX Tcommaaccent Aacute -50 -KPX Tcommaaccent Abreve -50 -KPX Tcommaaccent Acircumflex -50 -KPX Tcommaaccent Adieresis -50 -KPX Tcommaaccent Agrave -50 -KPX Tcommaaccent Amacron -50 -KPX Tcommaaccent Aogonek -50 -KPX Tcommaaccent Aring -50 -KPX Tcommaaccent Atilde -50 -KPX Tcommaaccent O -18 -KPX Tcommaaccent Oacute -18 -KPX Tcommaaccent Ocircumflex -18 -KPX Tcommaaccent Odieresis -18 -KPX Tcommaaccent Ograve -18 -KPX Tcommaaccent Ohungarumlaut -18 -KPX Tcommaaccent Omacron -18 -KPX Tcommaaccent Oslash -18 -KPX Tcommaaccent Otilde -18 -KPX Tcommaaccent a -92 -KPX Tcommaaccent aacute -92 -KPX Tcommaaccent abreve -92 -KPX Tcommaaccent acircumflex -92 -KPX Tcommaaccent adieresis -92 -KPX Tcommaaccent agrave -92 -KPX Tcommaaccent amacron -92 -KPX Tcommaaccent aogonek -92 -KPX Tcommaaccent aring -92 -KPX Tcommaaccent atilde -92 -KPX Tcommaaccent colon -55 -KPX Tcommaaccent comma -74 -KPX Tcommaaccent e -92 -KPX Tcommaaccent eacute -92 -KPX Tcommaaccent ecaron -92 -KPX Tcommaaccent ecircumflex -52 -KPX Tcommaaccent edieresis -52 -KPX Tcommaaccent edotaccent -92 -KPX Tcommaaccent egrave -52 -KPX Tcommaaccent emacron -52 -KPX Tcommaaccent eogonek -92 -KPX Tcommaaccent hyphen -74 -KPX Tcommaaccent i -55 -KPX Tcommaaccent iacute -55 -KPX Tcommaaccent iogonek -55 -KPX Tcommaaccent o -92 -KPX Tcommaaccent oacute -92 -KPX Tcommaaccent ocircumflex -92 -KPX Tcommaaccent odieresis -92 -KPX Tcommaaccent ograve -92 -KPX Tcommaaccent ohungarumlaut -92 -KPX Tcommaaccent omacron -92 -KPX Tcommaaccent oslash -92 -KPX Tcommaaccent otilde -92 -KPX Tcommaaccent period -74 -KPX Tcommaaccent r -55 -KPX Tcommaaccent racute -55 -KPX Tcommaaccent rcaron -55 -KPX Tcommaaccent rcommaaccent -55 -KPX Tcommaaccent semicolon -65 -KPX Tcommaaccent u -55 -KPX Tcommaaccent uacute -55 -KPX Tcommaaccent ucircumflex -55 -KPX Tcommaaccent udieresis -55 -KPX Tcommaaccent ugrave -55 -KPX Tcommaaccent uhungarumlaut -55 -KPX Tcommaaccent umacron -55 -KPX Tcommaaccent uogonek -55 -KPX Tcommaaccent uring -55 -KPX Tcommaaccent w -74 -KPX Tcommaaccent y -74 -KPX Tcommaaccent yacute -74 -KPX Tcommaaccent ydieresis -34 -KPX U A -40 -KPX U Aacute -40 -KPX U Abreve -40 -KPX U Acircumflex -40 -KPX U Adieresis -40 -KPX U Agrave -40 -KPX U Amacron -40 -KPX U Aogonek -40 -KPX U Aring -40 -KPX U Atilde -40 -KPX U comma -25 -KPX U period -25 -KPX Uacute A -40 -KPX Uacute Aacute -40 -KPX Uacute Abreve -40 -KPX Uacute Acircumflex -40 -KPX Uacute Adieresis -40 -KPX Uacute Agrave -40 -KPX Uacute Amacron -40 -KPX Uacute Aogonek -40 -KPX Uacute Aring -40 -KPX Uacute Atilde -40 -KPX Uacute comma -25 -KPX Uacute period -25 -KPX Ucircumflex A -40 -KPX Ucircumflex Aacute -40 -KPX Ucircumflex Abreve -40 -KPX Ucircumflex Acircumflex -40 -KPX Ucircumflex Adieresis -40 -KPX Ucircumflex Agrave -40 -KPX Ucircumflex Amacron -40 -KPX Ucircumflex Aogonek -40 -KPX Ucircumflex Aring -40 -KPX Ucircumflex Atilde -40 -KPX Ucircumflex comma -25 -KPX Ucircumflex period -25 -KPX Udieresis A -40 -KPX Udieresis Aacute -40 -KPX Udieresis Abreve -40 -KPX Udieresis Acircumflex -40 -KPX Udieresis Adieresis -40 -KPX Udieresis Agrave -40 -KPX Udieresis Amacron -40 -KPX Udieresis Aogonek -40 -KPX Udieresis Aring -40 -KPX Udieresis Atilde -40 -KPX Udieresis comma -25 -KPX Udieresis period -25 -KPX Ugrave A -40 -KPX Ugrave Aacute -40 -KPX Ugrave Abreve -40 -KPX Ugrave Acircumflex -40 -KPX Ugrave Adieresis -40 -KPX Ugrave Agrave -40 -KPX Ugrave Amacron -40 -KPX Ugrave Aogonek -40 -KPX Ugrave Aring -40 -KPX Ugrave Atilde -40 -KPX Ugrave comma -25 -KPX Ugrave period -25 -KPX Uhungarumlaut A -40 -KPX Uhungarumlaut Aacute -40 -KPX Uhungarumlaut Abreve -40 -KPX Uhungarumlaut Acircumflex -40 -KPX Uhungarumlaut Adieresis -40 -KPX Uhungarumlaut Agrave -40 -KPX Uhungarumlaut Amacron -40 -KPX Uhungarumlaut Aogonek -40 -KPX Uhungarumlaut Aring -40 -KPX Uhungarumlaut Atilde -40 -KPX Uhungarumlaut comma -25 -KPX Uhungarumlaut period -25 -KPX Umacron A -40 -KPX Umacron Aacute -40 -KPX Umacron Abreve -40 -KPX Umacron Acircumflex -40 -KPX Umacron Adieresis -40 -KPX Umacron Agrave -40 -KPX Umacron Amacron -40 -KPX Umacron Aogonek -40 -KPX Umacron Aring -40 -KPX Umacron Atilde -40 -KPX Umacron comma -25 -KPX Umacron period -25 -KPX Uogonek A -40 -KPX Uogonek Aacute -40 -KPX Uogonek Abreve -40 -KPX Uogonek Acircumflex -40 -KPX Uogonek Adieresis -40 -KPX Uogonek Agrave -40 -KPX Uogonek Amacron -40 -KPX Uogonek Aogonek -40 -KPX Uogonek Aring -40 -KPX Uogonek Atilde -40 -KPX Uogonek comma -25 -KPX Uogonek period -25 -KPX Uring A -40 -KPX Uring Aacute -40 -KPX Uring Abreve -40 -KPX Uring Acircumflex -40 -KPX Uring Adieresis -40 -KPX Uring Agrave -40 -KPX Uring Amacron -40 -KPX Uring Aogonek -40 -KPX Uring Aring -40 -KPX Uring Atilde -40 -KPX Uring comma -25 -KPX Uring period -25 -KPX V A -60 -KPX V Aacute -60 -KPX V Abreve -60 -KPX V Acircumflex -60 -KPX V Adieresis -60 -KPX V Agrave -60 -KPX V Amacron -60 -KPX V Aogonek -60 -KPX V Aring -60 -KPX V Atilde -60 -KPX V O -30 -KPX V Oacute -30 -KPX V Ocircumflex -30 -KPX V Odieresis -30 -KPX V Ograve -30 -KPX V Ohungarumlaut -30 -KPX V Omacron -30 -KPX V Oslash -30 -KPX V Otilde -30 -KPX V a -111 -KPX V aacute -111 -KPX V abreve -111 -KPX V acircumflex -111 -KPX V adieresis -111 -KPX V agrave -111 -KPX V amacron -111 -KPX V aogonek -111 -KPX V aring -111 -KPX V atilde -111 -KPX V colon -65 -KPX V comma -129 -KPX V e -111 -KPX V eacute -111 -KPX V ecaron -111 -KPX V ecircumflex -111 -KPX V edieresis -71 -KPX V edotaccent -111 -KPX V egrave -71 -KPX V emacron -71 -KPX V eogonek -111 -KPX V hyphen -55 -KPX V i -74 -KPX V iacute -74 -KPX V icircumflex -34 -KPX V idieresis -34 -KPX V igrave -34 -KPX V imacron -34 -KPX V iogonek -74 -KPX V o -111 -KPX V oacute -111 -KPX V ocircumflex -111 -KPX V odieresis -111 -KPX V ograve -111 -KPX V ohungarumlaut -111 -KPX V omacron -111 -KPX V oslash -111 -KPX V otilde -111 -KPX V period -129 -KPX V semicolon -74 -KPX V u -74 -KPX V uacute -74 -KPX V ucircumflex -74 -KPX V udieresis -74 -KPX V ugrave -74 -KPX V uhungarumlaut -74 -KPX V umacron -74 -KPX V uogonek -74 -KPX V uring -74 -KPX W A -60 -KPX W Aacute -60 -KPX W Abreve -60 -KPX W Acircumflex -60 -KPX W Adieresis -60 -KPX W Agrave -60 -KPX W Amacron -60 -KPX W Aogonek -60 -KPX W Aring -60 -KPX W Atilde -60 -KPX W O -25 -KPX W Oacute -25 -KPX W Ocircumflex -25 -KPX W Odieresis -25 -KPX W Ograve -25 -KPX W Ohungarumlaut -25 -KPX W Omacron -25 -KPX W Oslash -25 -KPX W Otilde -25 -KPX W a -92 -KPX W aacute -92 -KPX W abreve -92 -KPX W acircumflex -92 -KPX W adieresis -92 -KPX W agrave -92 -KPX W amacron -92 -KPX W aogonek -92 -KPX W aring -92 -KPX W atilde -92 -KPX W colon -65 -KPX W comma -92 -KPX W e -92 -KPX W eacute -92 -KPX W ecaron -92 -KPX W ecircumflex -92 -KPX W edieresis -52 -KPX W edotaccent -92 -KPX W egrave -52 -KPX W emacron -52 -KPX W eogonek -92 -KPX W hyphen -37 -KPX W i -55 -KPX W iacute -55 -KPX W iogonek -55 -KPX W o -92 -KPX W oacute -92 -KPX W ocircumflex -92 -KPX W odieresis -92 -KPX W ograve -92 -KPX W ohungarumlaut -92 -KPX W omacron -92 -KPX W oslash -92 -KPX W otilde -92 -KPX W period -92 -KPX W semicolon -65 -KPX W u -55 -KPX W uacute -55 -KPX W ucircumflex -55 -KPX W udieresis -55 -KPX W ugrave -55 -KPX W uhungarumlaut -55 -KPX W umacron -55 -KPX W uogonek -55 -KPX W uring -55 -KPX W y -70 -KPX W yacute -70 -KPX W ydieresis -70 -KPX Y A -50 -KPX Y Aacute -50 -KPX Y Abreve -50 -KPX Y Acircumflex -50 -KPX Y Adieresis -50 -KPX Y Agrave -50 -KPX Y Amacron -50 -KPX Y Aogonek -50 -KPX Y Aring -50 -KPX Y Atilde -50 -KPX Y O -15 -KPX Y Oacute -15 -KPX Y Ocircumflex -15 -KPX Y Odieresis -15 -KPX Y Ograve -15 -KPX Y Ohungarumlaut -15 -KPX Y Omacron -15 -KPX Y Oslash -15 -KPX Y Otilde -15 -KPX Y a -92 -KPX Y aacute -92 -KPX Y abreve -92 -KPX Y acircumflex -92 -KPX Y adieresis -92 -KPX Y agrave -92 -KPX Y amacron -92 -KPX Y aogonek -92 -KPX Y aring -92 -KPX Y atilde -92 -KPX Y colon -65 -KPX Y comma -92 -KPX Y e -92 -KPX Y eacute -92 -KPX Y ecaron -92 -KPX Y ecircumflex -92 -KPX Y edieresis -52 -KPX Y edotaccent -92 -KPX Y egrave -52 -KPX Y emacron -52 -KPX Y eogonek -92 -KPX Y hyphen -74 -KPX Y i -74 -KPX Y iacute -74 -KPX Y icircumflex -34 -KPX Y idieresis -34 -KPX Y igrave -34 -KPX Y imacron -34 -KPX Y iogonek -74 -KPX Y o -92 -KPX Y oacute -92 -KPX Y ocircumflex -92 -KPX Y odieresis -92 -KPX Y ograve -92 -KPX Y ohungarumlaut -92 -KPX Y omacron -92 -KPX Y oslash -92 -KPX Y otilde -92 -KPX Y period -92 -KPX Y semicolon -65 -KPX Y u -92 -KPX Y uacute -92 -KPX Y ucircumflex -92 -KPX Y udieresis -92 -KPX Y ugrave -92 -KPX Y uhungarumlaut -92 -KPX Y umacron -92 -KPX Y uogonek -92 -KPX Y uring -92 -KPX Yacute A -50 -KPX Yacute Aacute -50 -KPX Yacute Abreve -50 -KPX Yacute Acircumflex -50 -KPX Yacute Adieresis -50 -KPX Yacute Agrave -50 -KPX Yacute Amacron -50 -KPX Yacute Aogonek -50 -KPX Yacute Aring -50 -KPX Yacute Atilde -50 -KPX Yacute O -15 -KPX Yacute Oacute -15 -KPX Yacute Ocircumflex -15 -KPX Yacute Odieresis -15 -KPX Yacute Ograve -15 -KPX Yacute Ohungarumlaut -15 -KPX Yacute Omacron -15 -KPX Yacute Oslash -15 -KPX Yacute Otilde -15 -KPX Yacute a -92 -KPX Yacute aacute -92 -KPX Yacute abreve -92 -KPX Yacute acircumflex -92 -KPX Yacute adieresis -92 -KPX Yacute agrave -92 -KPX Yacute amacron -92 -KPX Yacute aogonek -92 -KPX Yacute aring -92 -KPX Yacute atilde -92 -KPX Yacute colon -65 -KPX Yacute comma -92 -KPX Yacute e -92 -KPX Yacute eacute -92 -KPX Yacute ecaron -92 -KPX Yacute ecircumflex -92 -KPX Yacute edieresis -52 -KPX Yacute edotaccent -92 -KPX Yacute egrave -52 -KPX Yacute emacron -52 -KPX Yacute eogonek -92 -KPX Yacute hyphen -74 -KPX Yacute i -74 -KPX Yacute iacute -74 -KPX Yacute icircumflex -34 -KPX Yacute idieresis -34 -KPX Yacute igrave -34 -KPX Yacute imacron -34 -KPX Yacute iogonek -74 -KPX Yacute o -92 -KPX Yacute oacute -92 -KPX Yacute ocircumflex -92 -KPX Yacute odieresis -92 -KPX Yacute ograve -92 -KPX Yacute ohungarumlaut -92 -KPX Yacute omacron -92 -KPX Yacute oslash -92 -KPX Yacute otilde -92 -KPX Yacute period -92 -KPX Yacute semicolon -65 -KPX Yacute u -92 -KPX Yacute uacute -92 -KPX Yacute ucircumflex -92 -KPX Yacute udieresis -92 -KPX Yacute ugrave -92 -KPX Yacute uhungarumlaut -92 -KPX Yacute umacron -92 -KPX Yacute uogonek -92 -KPX Yacute uring -92 -KPX Ydieresis A -50 -KPX Ydieresis Aacute -50 -KPX Ydieresis Abreve -50 -KPX Ydieresis Acircumflex -50 -KPX Ydieresis Adieresis -50 -KPX Ydieresis Agrave -50 -KPX Ydieresis Amacron -50 -KPX Ydieresis Aogonek -50 -KPX Ydieresis Aring -50 -KPX Ydieresis Atilde -50 -KPX Ydieresis O -15 -KPX Ydieresis Oacute -15 -KPX Ydieresis Ocircumflex -15 -KPX Ydieresis Odieresis -15 -KPX Ydieresis Ograve -15 -KPX Ydieresis Ohungarumlaut -15 -KPX Ydieresis Omacron -15 -KPX Ydieresis Oslash -15 -KPX Ydieresis Otilde -15 -KPX Ydieresis a -92 -KPX Ydieresis aacute -92 -KPX Ydieresis abreve -92 -KPX Ydieresis acircumflex -92 -KPX Ydieresis adieresis -92 -KPX Ydieresis agrave -92 -KPX Ydieresis amacron -92 -KPX Ydieresis aogonek -92 -KPX Ydieresis aring -92 -KPX Ydieresis atilde -92 -KPX Ydieresis colon -65 -KPX Ydieresis comma -92 -KPX Ydieresis e -92 -KPX Ydieresis eacute -92 -KPX Ydieresis ecaron -92 -KPX Ydieresis ecircumflex -92 -KPX Ydieresis edieresis -52 -KPX Ydieresis edotaccent -92 -KPX Ydieresis egrave -52 -KPX Ydieresis emacron -52 -KPX Ydieresis eogonek -92 -KPX Ydieresis hyphen -74 -KPX Ydieresis i -74 -KPX Ydieresis iacute -74 -KPX Ydieresis icircumflex -34 -KPX Ydieresis idieresis -34 -KPX Ydieresis igrave -34 -KPX Ydieresis imacron -34 -KPX Ydieresis iogonek -74 -KPX Ydieresis o -92 -KPX Ydieresis oacute -92 -KPX Ydieresis ocircumflex -92 -KPX Ydieresis odieresis -92 -KPX Ydieresis ograve -92 -KPX Ydieresis ohungarumlaut -92 -KPX Ydieresis omacron -92 -KPX Ydieresis oslash -92 -KPX Ydieresis otilde -92 -KPX Ydieresis period -92 -KPX Ydieresis semicolon -65 -KPX Ydieresis u -92 -KPX Ydieresis uacute -92 -KPX Ydieresis ucircumflex -92 -KPX Ydieresis udieresis -92 -KPX Ydieresis ugrave -92 -KPX Ydieresis uhungarumlaut -92 -KPX Ydieresis umacron -92 -KPX Ydieresis uogonek -92 -KPX Ydieresis uring -92 -KPX a g -10 -KPX a gbreve -10 -KPX a gcommaaccent -10 -KPX aacute g -10 -KPX aacute gbreve -10 -KPX aacute gcommaaccent -10 -KPX abreve g -10 -KPX abreve gbreve -10 -KPX abreve gcommaaccent -10 -KPX acircumflex g -10 -KPX acircumflex gbreve -10 -KPX acircumflex gcommaaccent -10 -KPX adieresis g -10 -KPX adieresis gbreve -10 -KPX adieresis gcommaaccent -10 -KPX agrave g -10 -KPX agrave gbreve -10 -KPX agrave gcommaaccent -10 -KPX amacron g -10 -KPX amacron gbreve -10 -KPX amacron gcommaaccent -10 -KPX aogonek g -10 -KPX aogonek gbreve -10 -KPX aogonek gcommaaccent -10 -KPX aring g -10 -KPX aring gbreve -10 -KPX aring gcommaaccent -10 -KPX atilde g -10 -KPX atilde gbreve -10 -KPX atilde gcommaaccent -10 -KPX b period -40 -KPX b u -20 -KPX b uacute -20 -KPX b ucircumflex -20 -KPX b udieresis -20 -KPX b ugrave -20 -KPX b uhungarumlaut -20 -KPX b umacron -20 -KPX b uogonek -20 -KPX b uring -20 -KPX c h -15 -KPX c k -20 -KPX c kcommaaccent -20 -KPX cacute h -15 -KPX cacute k -20 -KPX cacute kcommaaccent -20 -KPX ccaron h -15 -KPX ccaron k -20 -KPX ccaron kcommaaccent -20 -KPX ccedilla h -15 -KPX ccedilla k -20 -KPX ccedilla kcommaaccent -20 -KPX comma quotedblright -140 -KPX comma quoteright -140 -KPX e comma -10 -KPX e g -40 -KPX e gbreve -40 -KPX e gcommaaccent -40 -KPX e period -15 -KPX e v -15 -KPX e w -15 -KPX e x -20 -KPX e y -30 -KPX e yacute -30 -KPX e ydieresis -30 -KPX eacute comma -10 -KPX eacute g -40 -KPX eacute gbreve -40 -KPX eacute gcommaaccent -40 -KPX eacute period -15 -KPX eacute v -15 -KPX eacute w -15 -KPX eacute x -20 -KPX eacute y -30 -KPX eacute yacute -30 -KPX eacute ydieresis -30 -KPX ecaron comma -10 -KPX ecaron g -40 -KPX ecaron gbreve -40 -KPX ecaron gcommaaccent -40 -KPX ecaron period -15 -KPX ecaron v -15 -KPX ecaron w -15 -KPX ecaron x -20 -KPX ecaron y -30 -KPX ecaron yacute -30 -KPX ecaron ydieresis -30 -KPX ecircumflex comma -10 -KPX ecircumflex g -40 -KPX ecircumflex gbreve -40 -KPX ecircumflex gcommaaccent -40 -KPX ecircumflex period -15 -KPX ecircumflex v -15 -KPX ecircumflex w -15 -KPX ecircumflex x -20 -KPX ecircumflex y -30 -KPX ecircumflex yacute -30 -KPX ecircumflex ydieresis -30 -KPX edieresis comma -10 -KPX edieresis g -40 -KPX edieresis gbreve -40 -KPX edieresis gcommaaccent -40 -KPX edieresis period -15 -KPX edieresis v -15 -KPX edieresis w -15 -KPX edieresis x -20 -KPX edieresis y -30 -KPX edieresis yacute -30 -KPX edieresis ydieresis -30 -KPX edotaccent comma -10 -KPX edotaccent g -40 -KPX edotaccent gbreve -40 -KPX edotaccent gcommaaccent -40 -KPX edotaccent period -15 -KPX edotaccent v -15 -KPX edotaccent w -15 -KPX edotaccent x -20 -KPX edotaccent y -30 -KPX edotaccent yacute -30 -KPX edotaccent ydieresis -30 -KPX egrave comma -10 -KPX egrave g -40 -KPX egrave gbreve -40 -KPX egrave gcommaaccent -40 -KPX egrave period -15 -KPX egrave v -15 -KPX egrave w -15 -KPX egrave x -20 -KPX egrave y -30 -KPX egrave yacute -30 -KPX egrave ydieresis -30 -KPX emacron comma -10 -KPX emacron g -40 -KPX emacron gbreve -40 -KPX emacron gcommaaccent -40 -KPX emacron period -15 -KPX emacron v -15 -KPX emacron w -15 -KPX emacron x -20 -KPX emacron y -30 -KPX emacron yacute -30 -KPX emacron ydieresis -30 -KPX eogonek comma -10 -KPX eogonek g -40 -KPX eogonek gbreve -40 -KPX eogonek gcommaaccent -40 -KPX eogonek period -15 -KPX eogonek v -15 -KPX eogonek w -15 -KPX eogonek x -20 -KPX eogonek y -30 -KPX eogonek yacute -30 -KPX eogonek ydieresis -30 -KPX f comma -10 -KPX f dotlessi -60 -KPX f f -18 -KPX f i -20 -KPX f iogonek -20 -KPX f period -15 -KPX f quoteright 92 -KPX g comma -10 -KPX g e -10 -KPX g eacute -10 -KPX g ecaron -10 -KPX g ecircumflex -10 -KPX g edieresis -10 -KPX g edotaccent -10 -KPX g egrave -10 -KPX g emacron -10 -KPX g eogonek -10 -KPX g g -10 -KPX g gbreve -10 -KPX g gcommaaccent -10 -KPX g period -15 -KPX gbreve comma -10 -KPX gbreve e -10 -KPX gbreve eacute -10 -KPX gbreve ecaron -10 -KPX gbreve ecircumflex -10 -KPX gbreve edieresis -10 -KPX gbreve edotaccent -10 -KPX gbreve egrave -10 -KPX gbreve emacron -10 -KPX gbreve eogonek -10 -KPX gbreve g -10 -KPX gbreve gbreve -10 -KPX gbreve gcommaaccent -10 -KPX gbreve period -15 -KPX gcommaaccent comma -10 -KPX gcommaaccent e -10 -KPX gcommaaccent eacute -10 -KPX gcommaaccent ecaron -10 -KPX gcommaaccent ecircumflex -10 -KPX gcommaaccent edieresis -10 -KPX gcommaaccent edotaccent -10 -KPX gcommaaccent egrave -10 -KPX gcommaaccent emacron -10 -KPX gcommaaccent eogonek -10 -KPX gcommaaccent g -10 -KPX gcommaaccent gbreve -10 -KPX gcommaaccent gcommaaccent -10 -KPX gcommaaccent period -15 -KPX k e -10 -KPX k eacute -10 -KPX k ecaron -10 -KPX k ecircumflex -10 -KPX k edieresis -10 -KPX k edotaccent -10 -KPX k egrave -10 -KPX k emacron -10 -KPX k eogonek -10 -KPX k o -10 -KPX k oacute -10 -KPX k ocircumflex -10 -KPX k odieresis -10 -KPX k ograve -10 -KPX k ohungarumlaut -10 -KPX k omacron -10 -KPX k oslash -10 -KPX k otilde -10 -KPX k y -10 -KPX k yacute -10 -KPX k ydieresis -10 -KPX kcommaaccent e -10 -KPX kcommaaccent eacute -10 -KPX kcommaaccent ecaron -10 -KPX kcommaaccent ecircumflex -10 -KPX kcommaaccent edieresis -10 -KPX kcommaaccent edotaccent -10 -KPX kcommaaccent egrave -10 -KPX kcommaaccent emacron -10 -KPX kcommaaccent eogonek -10 -KPX kcommaaccent o -10 -KPX kcommaaccent oacute -10 -KPX kcommaaccent ocircumflex -10 -KPX kcommaaccent odieresis -10 -KPX kcommaaccent ograve -10 -KPX kcommaaccent ohungarumlaut -10 -KPX kcommaaccent omacron -10 -KPX kcommaaccent oslash -10 -KPX kcommaaccent otilde -10 -KPX kcommaaccent y -10 -KPX kcommaaccent yacute -10 -KPX kcommaaccent ydieresis -10 -KPX n v -40 -KPX nacute v -40 -KPX ncaron v -40 -KPX ncommaaccent v -40 -KPX ntilde v -40 -KPX o g -10 -KPX o gbreve -10 -KPX o gcommaaccent -10 -KPX o v -10 -KPX oacute g -10 -KPX oacute gbreve -10 -KPX oacute gcommaaccent -10 -KPX oacute v -10 -KPX ocircumflex g -10 -KPX ocircumflex gbreve -10 -KPX ocircumflex gcommaaccent -10 -KPX ocircumflex v -10 -KPX odieresis g -10 -KPX odieresis gbreve -10 -KPX odieresis gcommaaccent -10 -KPX odieresis v -10 -KPX ograve g -10 -KPX ograve gbreve -10 -KPX ograve gcommaaccent -10 -KPX ograve v -10 -KPX ohungarumlaut g -10 -KPX ohungarumlaut gbreve -10 -KPX ohungarumlaut gcommaaccent -10 -KPX ohungarumlaut v -10 -KPX omacron g -10 -KPX omacron gbreve -10 -KPX omacron gcommaaccent -10 -KPX omacron v -10 -KPX oslash g -10 -KPX oslash gbreve -10 -KPX oslash gcommaaccent -10 -KPX oslash v -10 -KPX otilde g -10 -KPX otilde gbreve -10 -KPX otilde gcommaaccent -10 -KPX otilde v -10 -KPX period quotedblright -140 -KPX period quoteright -140 -KPX quoteleft quoteleft -111 -KPX quoteright d -25 -KPX quoteright dcroat -25 -KPX quoteright quoteright -111 -KPX quoteright r -25 -KPX quoteright racute -25 -KPX quoteright rcaron -25 -KPX quoteright rcommaaccent -25 -KPX quoteright s -40 -KPX quoteright sacute -40 -KPX quoteright scaron -40 -KPX quoteright scedilla -40 -KPX quoteright scommaaccent -40 -KPX quoteright space -111 -KPX quoteright t -30 -KPX quoteright tcommaaccent -30 -KPX quoteright v -10 -KPX r a -15 -KPX r aacute -15 -KPX r abreve -15 -KPX r acircumflex -15 -KPX r adieresis -15 -KPX r agrave -15 -KPX r amacron -15 -KPX r aogonek -15 -KPX r aring -15 -KPX r atilde -15 -KPX r c -37 -KPX r cacute -37 -KPX r ccaron -37 -KPX r ccedilla -37 -KPX r comma -111 -KPX r d -37 -KPX r dcroat -37 -KPX r e -37 -KPX r eacute -37 -KPX r ecaron -37 -KPX r ecircumflex -37 -KPX r edieresis -37 -KPX r edotaccent -37 -KPX r egrave -37 -KPX r emacron -37 -KPX r eogonek -37 -KPX r g -37 -KPX r gbreve -37 -KPX r gcommaaccent -37 -KPX r hyphen -20 -KPX r o -45 -KPX r oacute -45 -KPX r ocircumflex -45 -KPX r odieresis -45 -KPX r ograve -45 -KPX r ohungarumlaut -45 -KPX r omacron -45 -KPX r oslash -45 -KPX r otilde -45 -KPX r period -111 -KPX r q -37 -KPX r s -10 -KPX r sacute -10 -KPX r scaron -10 -KPX r scedilla -10 -KPX r scommaaccent -10 -KPX racute a -15 -KPX racute aacute -15 -KPX racute abreve -15 -KPX racute acircumflex -15 -KPX racute adieresis -15 -KPX racute agrave -15 -KPX racute amacron -15 -KPX racute aogonek -15 -KPX racute aring -15 -KPX racute atilde -15 -KPX racute c -37 -KPX racute cacute -37 -KPX racute ccaron -37 -KPX racute ccedilla -37 -KPX racute comma -111 -KPX racute d -37 -KPX racute dcroat -37 -KPX racute e -37 -KPX racute eacute -37 -KPX racute ecaron -37 -KPX racute ecircumflex -37 -KPX racute edieresis -37 -KPX racute edotaccent -37 -KPX racute egrave -37 -KPX racute emacron -37 -KPX racute eogonek -37 -KPX racute g -37 -KPX racute gbreve -37 -KPX racute gcommaaccent -37 -KPX racute hyphen -20 -KPX racute o -45 -KPX racute oacute -45 -KPX racute ocircumflex -45 -KPX racute odieresis -45 -KPX racute ograve -45 -KPX racute ohungarumlaut -45 -KPX racute omacron -45 -KPX racute oslash -45 -KPX racute otilde -45 -KPX racute period -111 -KPX racute q -37 -KPX racute s -10 -KPX racute sacute -10 -KPX racute scaron -10 -KPX racute scedilla -10 -KPX racute scommaaccent -10 -KPX rcaron a -15 -KPX rcaron aacute -15 -KPX rcaron abreve -15 -KPX rcaron acircumflex -15 -KPX rcaron adieresis -15 -KPX rcaron agrave -15 -KPX rcaron amacron -15 -KPX rcaron aogonek -15 -KPX rcaron aring -15 -KPX rcaron atilde -15 -KPX rcaron c -37 -KPX rcaron cacute -37 -KPX rcaron ccaron -37 -KPX rcaron ccedilla -37 -KPX rcaron comma -111 -KPX rcaron d -37 -KPX rcaron dcroat -37 -KPX rcaron e -37 -KPX rcaron eacute -37 -KPX rcaron ecaron -37 -KPX rcaron ecircumflex -37 -KPX rcaron edieresis -37 -KPX rcaron edotaccent -37 -KPX rcaron egrave -37 -KPX rcaron emacron -37 -KPX rcaron eogonek -37 -KPX rcaron g -37 -KPX rcaron gbreve -37 -KPX rcaron gcommaaccent -37 -KPX rcaron hyphen -20 -KPX rcaron o -45 -KPX rcaron oacute -45 -KPX rcaron ocircumflex -45 -KPX rcaron odieresis -45 -KPX rcaron ograve -45 -KPX rcaron ohungarumlaut -45 -KPX rcaron omacron -45 -KPX rcaron oslash -45 -KPX rcaron otilde -45 -KPX rcaron period -111 -KPX rcaron q -37 -KPX rcaron s -10 -KPX rcaron sacute -10 -KPX rcaron scaron -10 -KPX rcaron scedilla -10 -KPX rcaron scommaaccent -10 -KPX rcommaaccent a -15 -KPX rcommaaccent aacute -15 -KPX rcommaaccent abreve -15 -KPX rcommaaccent acircumflex -15 -KPX rcommaaccent adieresis -15 -KPX rcommaaccent agrave -15 -KPX rcommaaccent amacron -15 -KPX rcommaaccent aogonek -15 -KPX rcommaaccent aring -15 -KPX rcommaaccent atilde -15 -KPX rcommaaccent c -37 -KPX rcommaaccent cacute -37 -KPX rcommaaccent ccaron -37 -KPX rcommaaccent ccedilla -37 -KPX rcommaaccent comma -111 -KPX rcommaaccent d -37 -KPX rcommaaccent dcroat -37 -KPX rcommaaccent e -37 -KPX rcommaaccent eacute -37 -KPX rcommaaccent ecaron -37 -KPX rcommaaccent ecircumflex -37 -KPX rcommaaccent edieresis -37 -KPX rcommaaccent edotaccent -37 -KPX rcommaaccent egrave -37 -KPX rcommaaccent emacron -37 -KPX rcommaaccent eogonek -37 -KPX rcommaaccent g -37 -KPX rcommaaccent gbreve -37 -KPX rcommaaccent gcommaaccent -37 -KPX rcommaaccent hyphen -20 -KPX rcommaaccent o -45 -KPX rcommaaccent oacute -45 -KPX rcommaaccent ocircumflex -45 -KPX rcommaaccent odieresis -45 -KPX rcommaaccent ograve -45 -KPX rcommaaccent ohungarumlaut -45 -KPX rcommaaccent omacron -45 -KPX rcommaaccent oslash -45 -KPX rcommaaccent otilde -45 -KPX rcommaaccent period -111 -KPX rcommaaccent q -37 -KPX rcommaaccent s -10 -KPX rcommaaccent sacute -10 -KPX rcommaaccent scaron -10 -KPX rcommaaccent scedilla -10 -KPX rcommaaccent scommaaccent -10 -KPX space A -18 -KPX space Aacute -18 -KPX space Abreve -18 -KPX space Acircumflex -18 -KPX space Adieresis -18 -KPX space Agrave -18 -KPX space Amacron -18 -KPX space Aogonek -18 -KPX space Aring -18 -KPX space Atilde -18 -KPX space T -18 -KPX space Tcaron -18 -KPX space Tcommaaccent -18 -KPX space V -35 -KPX space W -40 -KPX space Y -75 -KPX space Yacute -75 -KPX space Ydieresis -75 -KPX v comma -74 -KPX v period -74 -KPX w comma -74 -KPX w period -74 -KPX y comma -55 -KPX y period -55 -KPX yacute comma -55 -KPX yacute period -55 -KPX ydieresis comma -55 -KPX ydieresis period -55 -EndKernPairs -EndKernData -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Times-Roman.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Times-Roman.afm deleted file mode 100644 index a0953f2..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/Times-Roman.afm +++ /dev/null @@ -1,2419 +0,0 @@ -StartFontMetrics 4.1 -Comment Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Thu May 1 12:49:17 1997 -Comment UniqueID 43068 -Comment VMusage 43909 54934 -FontName Times-Roman -FullName Times Roman -FamilyName Times -Weight Roman -ItalicAngle 0 -IsFixedPitch false -CharacterSet ExtendedRoman -FontBBox -168 -218 1000 898 -UnderlinePosition -100 -UnderlineThickness 50 -Version 002.000 -Notice Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.Times is a trademark of Linotype-Hell AG and/or its subsidiaries. -EncodingScheme AdobeStandardEncoding -CapHeight 662 -XHeight 450 -Ascender 683 -Descender -217 -StdHW 28 -StdVW 84 -StartCharMetrics 315 -C 32 ; WX 250 ; N space ; B 0 0 0 0 ; -C 33 ; WX 333 ; N exclam ; B 130 -9 238 676 ; -C 34 ; WX 408 ; N quotedbl ; B 77 431 331 676 ; -C 35 ; WX 500 ; N numbersign ; B 5 0 496 662 ; -C 36 ; WX 500 ; N dollar ; B 44 -87 457 727 ; -C 37 ; WX 833 ; N percent ; B 61 -13 772 676 ; -C 38 ; WX 778 ; N ampersand ; B 42 -13 750 676 ; -C 39 ; WX 333 ; N quoteright ; B 79 433 218 676 ; -C 40 ; WX 333 ; N parenleft ; B 48 -177 304 676 ; -C 41 ; WX 333 ; N parenright ; B 29 -177 285 676 ; -C 42 ; WX 500 ; N asterisk ; B 69 265 432 676 ; -C 43 ; WX 564 ; N plus ; B 30 0 534 506 ; -C 44 ; WX 250 ; N comma ; B 56 -141 195 102 ; -C 45 ; WX 333 ; N hyphen ; B 39 194 285 257 ; -C 46 ; WX 250 ; N period ; B 70 -11 181 100 ; -C 47 ; WX 278 ; N slash ; B -9 -14 287 676 ; -C 48 ; WX 500 ; N zero ; B 24 -14 476 676 ; -C 49 ; WX 500 ; N one ; B 111 0 394 676 ; -C 50 ; WX 500 ; N two ; B 30 0 475 676 ; -C 51 ; WX 500 ; N three ; B 43 -14 431 676 ; -C 52 ; WX 500 ; N four ; B 12 0 472 676 ; -C 53 ; WX 500 ; N five ; B 32 -14 438 688 ; -C 54 ; WX 500 ; N six ; B 34 -14 468 684 ; -C 55 ; WX 500 ; N seven ; B 20 -8 449 662 ; -C 56 ; WX 500 ; N eight ; B 56 -14 445 676 ; -C 57 ; WX 500 ; N nine ; B 30 -22 459 676 ; -C 58 ; WX 278 ; N colon ; B 81 -11 192 459 ; -C 59 ; WX 278 ; N semicolon ; B 80 -141 219 459 ; -C 60 ; WX 564 ; N less ; B 28 -8 536 514 ; -C 61 ; WX 564 ; N equal ; B 30 120 534 386 ; -C 62 ; WX 564 ; N greater ; B 28 -8 536 514 ; -C 63 ; WX 444 ; N question ; B 68 -8 414 676 ; -C 64 ; WX 921 ; N at ; B 116 -14 809 676 ; -C 65 ; WX 722 ; N A ; B 15 0 706 674 ; -C 66 ; WX 667 ; N B ; B 17 0 593 662 ; -C 67 ; WX 667 ; N C ; B 28 -14 633 676 ; -C 68 ; WX 722 ; N D ; B 16 0 685 662 ; -C 69 ; WX 611 ; N E ; B 12 0 597 662 ; -C 70 ; WX 556 ; N F ; B 12 0 546 662 ; -C 71 ; WX 722 ; N G ; B 32 -14 709 676 ; -C 72 ; WX 722 ; N H ; B 19 0 702 662 ; -C 73 ; WX 333 ; N I ; B 18 0 315 662 ; -C 74 ; WX 389 ; N J ; B 10 -14 370 662 ; -C 75 ; WX 722 ; N K ; B 34 0 723 662 ; -C 76 ; WX 611 ; N L ; B 12 0 598 662 ; -C 77 ; WX 889 ; N M ; B 12 0 863 662 ; -C 78 ; WX 722 ; N N ; B 12 -11 707 662 ; -C 79 ; WX 722 ; N O ; B 34 -14 688 676 ; -C 80 ; WX 556 ; N P ; B 16 0 542 662 ; -C 81 ; WX 722 ; N Q ; B 34 -178 701 676 ; -C 82 ; WX 667 ; N R ; B 17 0 659 662 ; -C 83 ; WX 556 ; N S ; B 42 -14 491 676 ; -C 84 ; WX 611 ; N T ; B 17 0 593 662 ; -C 85 ; WX 722 ; N U ; B 14 -14 705 662 ; -C 86 ; WX 722 ; N V ; B 16 -11 697 662 ; -C 87 ; WX 944 ; N W ; B 5 -11 932 662 ; -C 88 ; WX 722 ; N X ; B 10 0 704 662 ; -C 89 ; WX 722 ; N Y ; B 22 0 703 662 ; -C 90 ; WX 611 ; N Z ; B 9 0 597 662 ; -C 91 ; WX 333 ; N bracketleft ; B 88 -156 299 662 ; -C 92 ; WX 278 ; N backslash ; B -9 -14 287 676 ; -C 93 ; WX 333 ; N bracketright ; B 34 -156 245 662 ; -C 94 ; WX 469 ; N asciicircum ; B 24 297 446 662 ; -C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ; -C 96 ; WX 333 ; N quoteleft ; B 115 433 254 676 ; -C 97 ; WX 444 ; N a ; B 37 -10 442 460 ; -C 98 ; WX 500 ; N b ; B 3 -10 468 683 ; -C 99 ; WX 444 ; N c ; B 25 -10 412 460 ; -C 100 ; WX 500 ; N d ; B 27 -10 491 683 ; -C 101 ; WX 444 ; N e ; B 25 -10 424 460 ; -C 102 ; WX 333 ; N f ; B 20 0 383 683 ; L i fi ; L l fl ; -C 103 ; WX 500 ; N g ; B 28 -218 470 460 ; -C 104 ; WX 500 ; N h ; B 9 0 487 683 ; -C 105 ; WX 278 ; N i ; B 16 0 253 683 ; -C 106 ; WX 278 ; N j ; B -70 -218 194 683 ; -C 107 ; WX 500 ; N k ; B 7 0 505 683 ; -C 108 ; WX 278 ; N l ; B 19 0 257 683 ; -C 109 ; WX 778 ; N m ; B 16 0 775 460 ; -C 110 ; WX 500 ; N n ; B 16 0 485 460 ; -C 111 ; WX 500 ; N o ; B 29 -10 470 460 ; -C 112 ; WX 500 ; N p ; B 5 -217 470 460 ; -C 113 ; WX 500 ; N q ; B 24 -217 488 460 ; -C 114 ; WX 333 ; N r ; B 5 0 335 460 ; -C 115 ; WX 389 ; N s ; B 51 -10 348 460 ; -C 116 ; WX 278 ; N t ; B 13 -10 279 579 ; -C 117 ; WX 500 ; N u ; B 9 -10 479 450 ; -C 118 ; WX 500 ; N v ; B 19 -14 477 450 ; -C 119 ; WX 722 ; N w ; B 21 -14 694 450 ; -C 120 ; WX 500 ; N x ; B 17 0 479 450 ; -C 121 ; WX 500 ; N y ; B 14 -218 475 450 ; -C 122 ; WX 444 ; N z ; B 27 0 418 450 ; -C 123 ; WX 480 ; N braceleft ; B 100 -181 350 680 ; -C 124 ; WX 200 ; N bar ; B 67 -218 133 782 ; -C 125 ; WX 480 ; N braceright ; B 130 -181 380 680 ; -C 126 ; WX 541 ; N asciitilde ; B 40 183 502 323 ; -C 161 ; WX 333 ; N exclamdown ; B 97 -218 205 467 ; -C 162 ; WX 500 ; N cent ; B 53 -138 448 579 ; -C 163 ; WX 500 ; N sterling ; B 12 -8 490 676 ; -C 164 ; WX 167 ; N fraction ; B -168 -14 331 676 ; -C 165 ; WX 500 ; N yen ; B -53 0 512 662 ; -C 166 ; WX 500 ; N florin ; B 7 -189 490 676 ; -C 167 ; WX 500 ; N section ; B 70 -148 426 676 ; -C 168 ; WX 500 ; N currency ; B -22 58 522 602 ; -C 169 ; WX 180 ; N quotesingle ; B 48 431 133 676 ; -C 170 ; WX 444 ; N quotedblleft ; B 43 433 414 676 ; -C 171 ; WX 500 ; N guillemotleft ; B 42 33 456 416 ; -C 172 ; WX 333 ; N guilsinglleft ; B 63 33 285 416 ; -C 173 ; WX 333 ; N guilsinglright ; B 48 33 270 416 ; -C 174 ; WX 556 ; N fi ; B 31 0 521 683 ; -C 175 ; WX 556 ; N fl ; B 32 0 521 683 ; -C 177 ; WX 500 ; N endash ; B 0 201 500 250 ; -C 178 ; WX 500 ; N dagger ; B 59 -149 442 676 ; -C 179 ; WX 500 ; N daggerdbl ; B 58 -153 442 676 ; -C 180 ; WX 250 ; N periodcentered ; B 70 199 181 310 ; -C 182 ; WX 453 ; N paragraph ; B -22 -154 450 662 ; -C 183 ; WX 350 ; N bullet ; B 40 196 310 466 ; -C 184 ; WX 333 ; N quotesinglbase ; B 79 -141 218 102 ; -C 185 ; WX 444 ; N quotedblbase ; B 45 -141 416 102 ; -C 186 ; WX 444 ; N quotedblright ; B 30 433 401 676 ; -C 187 ; WX 500 ; N guillemotright ; B 44 33 458 416 ; -C 188 ; WX 1000 ; N ellipsis ; B 111 -11 888 100 ; -C 189 ; WX 1000 ; N perthousand ; B 7 -19 994 706 ; -C 191 ; WX 444 ; N questiondown ; B 30 -218 376 466 ; -C 193 ; WX 333 ; N grave ; B 19 507 242 678 ; -C 194 ; WX 333 ; N acute ; B 93 507 317 678 ; -C 195 ; WX 333 ; N circumflex ; B 11 507 322 674 ; -C 196 ; WX 333 ; N tilde ; B 1 532 331 638 ; -C 197 ; WX 333 ; N macron ; B 11 547 322 601 ; -C 198 ; WX 333 ; N breve ; B 26 507 307 664 ; -C 199 ; WX 333 ; N dotaccent ; B 118 581 216 681 ; -C 200 ; WX 333 ; N dieresis ; B 18 581 315 681 ; -C 202 ; WX 333 ; N ring ; B 67 512 266 711 ; -C 203 ; WX 333 ; N cedilla ; B 52 -215 261 0 ; -C 205 ; WX 333 ; N hungarumlaut ; B -3 507 377 678 ; -C 206 ; WX 333 ; N ogonek ; B 62 -165 243 0 ; -C 207 ; WX 333 ; N caron ; B 11 507 322 674 ; -C 208 ; WX 1000 ; N emdash ; B 0 201 1000 250 ; -C 225 ; WX 889 ; N AE ; B 0 0 863 662 ; -C 227 ; WX 276 ; N ordfeminine ; B 4 394 270 676 ; -C 232 ; WX 611 ; N Lslash ; B 12 0 598 662 ; -C 233 ; WX 722 ; N Oslash ; B 34 -80 688 734 ; -C 234 ; WX 889 ; N OE ; B 30 -6 885 668 ; -C 235 ; WX 310 ; N ordmasculine ; B 6 394 304 676 ; -C 241 ; WX 667 ; N ae ; B 38 -10 632 460 ; -C 245 ; WX 278 ; N dotlessi ; B 16 0 253 460 ; -C 248 ; WX 278 ; N lslash ; B 19 0 259 683 ; -C 249 ; WX 500 ; N oslash ; B 29 -112 470 551 ; -C 250 ; WX 722 ; N oe ; B 30 -10 690 460 ; -C 251 ; WX 500 ; N germandbls ; B 12 -9 468 683 ; -C -1 ; WX 333 ; N Idieresis ; B 18 0 315 835 ; -C -1 ; WX 444 ; N eacute ; B 25 -10 424 678 ; -C -1 ; WX 444 ; N abreve ; B 37 -10 442 664 ; -C -1 ; WX 500 ; N uhungarumlaut ; B 9 -10 501 678 ; -C -1 ; WX 444 ; N ecaron ; B 25 -10 424 674 ; -C -1 ; WX 722 ; N Ydieresis ; B 22 0 703 835 ; -C -1 ; WX 564 ; N divide ; B 30 -10 534 516 ; -C -1 ; WX 722 ; N Yacute ; B 22 0 703 890 ; -C -1 ; WX 722 ; N Acircumflex ; B 15 0 706 886 ; -C -1 ; WX 444 ; N aacute ; B 37 -10 442 678 ; -C -1 ; WX 722 ; N Ucircumflex ; B 14 -14 705 886 ; -C -1 ; WX 500 ; N yacute ; B 14 -218 475 678 ; -C -1 ; WX 389 ; N scommaaccent ; B 51 -218 348 460 ; -C -1 ; WX 444 ; N ecircumflex ; B 25 -10 424 674 ; -C -1 ; WX 722 ; N Uring ; B 14 -14 705 898 ; -C -1 ; WX 722 ; N Udieresis ; B 14 -14 705 835 ; -C -1 ; WX 444 ; N aogonek ; B 37 -165 469 460 ; -C -1 ; WX 722 ; N Uacute ; B 14 -14 705 890 ; -C -1 ; WX 500 ; N uogonek ; B 9 -155 487 450 ; -C -1 ; WX 611 ; N Edieresis ; B 12 0 597 835 ; -C -1 ; WX 722 ; N Dcroat ; B 16 0 685 662 ; -C -1 ; WX 250 ; N commaaccent ; B 59 -218 184 -50 ; -C -1 ; WX 760 ; N copyright ; B 38 -14 722 676 ; -C -1 ; WX 611 ; N Emacron ; B 12 0 597 813 ; -C -1 ; WX 444 ; N ccaron ; B 25 -10 412 674 ; -C -1 ; WX 444 ; N aring ; B 37 -10 442 711 ; -C -1 ; WX 722 ; N Ncommaaccent ; B 12 -198 707 662 ; -C -1 ; WX 278 ; N lacute ; B 19 0 290 890 ; -C -1 ; WX 444 ; N agrave ; B 37 -10 442 678 ; -C -1 ; WX 611 ; N Tcommaaccent ; B 17 -218 593 662 ; -C -1 ; WX 667 ; N Cacute ; B 28 -14 633 890 ; -C -1 ; WX 444 ; N atilde ; B 37 -10 442 638 ; -C -1 ; WX 611 ; N Edotaccent ; B 12 0 597 835 ; -C -1 ; WX 389 ; N scaron ; B 39 -10 350 674 ; -C -1 ; WX 389 ; N scedilla ; B 51 -215 348 460 ; -C -1 ; WX 278 ; N iacute ; B 16 0 290 678 ; -C -1 ; WX 471 ; N lozenge ; B 13 0 459 724 ; -C -1 ; WX 667 ; N Rcaron ; B 17 0 659 886 ; -C -1 ; WX 722 ; N Gcommaaccent ; B 32 -218 709 676 ; -C -1 ; WX 500 ; N ucircumflex ; B 9 -10 479 674 ; -C -1 ; WX 444 ; N acircumflex ; B 37 -10 442 674 ; -C -1 ; WX 722 ; N Amacron ; B 15 0 706 813 ; -C -1 ; WX 333 ; N rcaron ; B 5 0 335 674 ; -C -1 ; WX 444 ; N ccedilla ; B 25 -215 412 460 ; -C -1 ; WX 611 ; N Zdotaccent ; B 9 0 597 835 ; -C -1 ; WX 556 ; N Thorn ; B 16 0 542 662 ; -C -1 ; WX 722 ; N Omacron ; B 34 -14 688 813 ; -C -1 ; WX 667 ; N Racute ; B 17 0 659 890 ; -C -1 ; WX 556 ; N Sacute ; B 42 -14 491 890 ; -C -1 ; WX 588 ; N dcaron ; B 27 -10 589 695 ; -C -1 ; WX 722 ; N Umacron ; B 14 -14 705 813 ; -C -1 ; WX 500 ; N uring ; B 9 -10 479 711 ; -C -1 ; WX 300 ; N threesuperior ; B 15 262 291 676 ; -C -1 ; WX 722 ; N Ograve ; B 34 -14 688 890 ; -C -1 ; WX 722 ; N Agrave ; B 15 0 706 890 ; -C -1 ; WX 722 ; N Abreve ; B 15 0 706 876 ; -C -1 ; WX 564 ; N multiply ; B 38 8 527 497 ; -C -1 ; WX 500 ; N uacute ; B 9 -10 479 678 ; -C -1 ; WX 611 ; N Tcaron ; B 17 0 593 886 ; -C -1 ; WX 476 ; N partialdiff ; B 17 -38 459 710 ; -C -1 ; WX 500 ; N ydieresis ; B 14 -218 475 623 ; -C -1 ; WX 722 ; N Nacute ; B 12 -11 707 890 ; -C -1 ; WX 278 ; N icircumflex ; B -16 0 295 674 ; -C -1 ; WX 611 ; N Ecircumflex ; B 12 0 597 886 ; -C -1 ; WX 444 ; N adieresis ; B 37 -10 442 623 ; -C -1 ; WX 444 ; N edieresis ; B 25 -10 424 623 ; -C -1 ; WX 444 ; N cacute ; B 25 -10 413 678 ; -C -1 ; WX 500 ; N nacute ; B 16 0 485 678 ; -C -1 ; WX 500 ; N umacron ; B 9 -10 479 601 ; -C -1 ; WX 722 ; N Ncaron ; B 12 -11 707 886 ; -C -1 ; WX 333 ; N Iacute ; B 18 0 317 890 ; -C -1 ; WX 564 ; N plusminus ; B 30 0 534 506 ; -C -1 ; WX 200 ; N brokenbar ; B 67 -143 133 707 ; -C -1 ; WX 760 ; N registered ; B 38 -14 722 676 ; -C -1 ; WX 722 ; N Gbreve ; B 32 -14 709 876 ; -C -1 ; WX 333 ; N Idotaccent ; B 18 0 315 835 ; -C -1 ; WX 600 ; N summation ; B 15 -10 585 706 ; -C -1 ; WX 611 ; N Egrave ; B 12 0 597 890 ; -C -1 ; WX 333 ; N racute ; B 5 0 335 678 ; -C -1 ; WX 500 ; N omacron ; B 29 -10 470 601 ; -C -1 ; WX 611 ; N Zacute ; B 9 0 597 890 ; -C -1 ; WX 611 ; N Zcaron ; B 9 0 597 886 ; -C -1 ; WX 549 ; N greaterequal ; B 26 0 523 666 ; -C -1 ; WX 722 ; N Eth ; B 16 0 685 662 ; -C -1 ; WX 667 ; N Ccedilla ; B 28 -215 633 676 ; -C -1 ; WX 278 ; N lcommaaccent ; B 19 -218 257 683 ; -C -1 ; WX 326 ; N tcaron ; B 13 -10 318 722 ; -C -1 ; WX 444 ; N eogonek ; B 25 -165 424 460 ; -C -1 ; WX 722 ; N Uogonek ; B 14 -165 705 662 ; -C -1 ; WX 722 ; N Aacute ; B 15 0 706 890 ; -C -1 ; WX 722 ; N Adieresis ; B 15 0 706 835 ; -C -1 ; WX 444 ; N egrave ; B 25 -10 424 678 ; -C -1 ; WX 444 ; N zacute ; B 27 0 418 678 ; -C -1 ; WX 278 ; N iogonek ; B 16 -165 265 683 ; -C -1 ; WX 722 ; N Oacute ; B 34 -14 688 890 ; -C -1 ; WX 500 ; N oacute ; B 29 -10 470 678 ; -C -1 ; WX 444 ; N amacron ; B 37 -10 442 601 ; -C -1 ; WX 389 ; N sacute ; B 51 -10 348 678 ; -C -1 ; WX 278 ; N idieresis ; B -9 0 288 623 ; -C -1 ; WX 722 ; N Ocircumflex ; B 34 -14 688 886 ; -C -1 ; WX 722 ; N Ugrave ; B 14 -14 705 890 ; -C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ; -C -1 ; WX 500 ; N thorn ; B 5 -217 470 683 ; -C -1 ; WX 300 ; N twosuperior ; B 1 270 296 676 ; -C -1 ; WX 722 ; N Odieresis ; B 34 -14 688 835 ; -C -1 ; WX 500 ; N mu ; B 36 -218 512 450 ; -C -1 ; WX 278 ; N igrave ; B -8 0 253 678 ; -C -1 ; WX 500 ; N ohungarumlaut ; B 29 -10 491 678 ; -C -1 ; WX 611 ; N Eogonek ; B 12 -165 597 662 ; -C -1 ; WX 500 ; N dcroat ; B 27 -10 500 683 ; -C -1 ; WX 750 ; N threequarters ; B 15 -14 718 676 ; -C -1 ; WX 556 ; N Scedilla ; B 42 -215 491 676 ; -C -1 ; WX 344 ; N lcaron ; B 19 0 347 695 ; -C -1 ; WX 722 ; N Kcommaaccent ; B 34 -198 723 662 ; -C -1 ; WX 611 ; N Lacute ; B 12 0 598 890 ; -C -1 ; WX 980 ; N trademark ; B 30 256 957 662 ; -C -1 ; WX 444 ; N edotaccent ; B 25 -10 424 623 ; -C -1 ; WX 333 ; N Igrave ; B 18 0 315 890 ; -C -1 ; WX 333 ; N Imacron ; B 11 0 322 813 ; -C -1 ; WX 611 ; N Lcaron ; B 12 0 598 676 ; -C -1 ; WX 750 ; N onehalf ; B 31 -14 746 676 ; -C -1 ; WX 549 ; N lessequal ; B 26 0 523 666 ; -C -1 ; WX 500 ; N ocircumflex ; B 29 -10 470 674 ; -C -1 ; WX 500 ; N ntilde ; B 16 0 485 638 ; -C -1 ; WX 722 ; N Uhungarumlaut ; B 14 -14 705 890 ; -C -1 ; WX 611 ; N Eacute ; B 12 0 597 890 ; -C -1 ; WX 444 ; N emacron ; B 25 -10 424 601 ; -C -1 ; WX 500 ; N gbreve ; B 28 -218 470 664 ; -C -1 ; WX 750 ; N onequarter ; B 37 -14 718 676 ; -C -1 ; WX 556 ; N Scaron ; B 42 -14 491 886 ; -C -1 ; WX 556 ; N Scommaaccent ; B 42 -218 491 676 ; -C -1 ; WX 722 ; N Ohungarumlaut ; B 34 -14 688 890 ; -C -1 ; WX 400 ; N degree ; B 57 390 343 676 ; -C -1 ; WX 500 ; N ograve ; B 29 -10 470 678 ; -C -1 ; WX 667 ; N Ccaron ; B 28 -14 633 886 ; -C -1 ; WX 500 ; N ugrave ; B 9 -10 479 678 ; -C -1 ; WX 453 ; N radical ; B 2 -60 452 768 ; -C -1 ; WX 722 ; N Dcaron ; B 16 0 685 886 ; -C -1 ; WX 333 ; N rcommaaccent ; B 5 -218 335 460 ; -C -1 ; WX 722 ; N Ntilde ; B 12 -11 707 850 ; -C -1 ; WX 500 ; N otilde ; B 29 -10 470 638 ; -C -1 ; WX 667 ; N Rcommaaccent ; B 17 -198 659 662 ; -C -1 ; WX 611 ; N Lcommaaccent ; B 12 -218 598 662 ; -C -1 ; WX 722 ; N Atilde ; B 15 0 706 850 ; -C -1 ; WX 722 ; N Aogonek ; B 15 -165 738 674 ; -C -1 ; WX 722 ; N Aring ; B 15 0 706 898 ; -C -1 ; WX 722 ; N Otilde ; B 34 -14 688 850 ; -C -1 ; WX 444 ; N zdotaccent ; B 27 0 418 623 ; -C -1 ; WX 611 ; N Ecaron ; B 12 0 597 886 ; -C -1 ; WX 333 ; N Iogonek ; B 18 -165 315 662 ; -C -1 ; WX 500 ; N kcommaaccent ; B 7 -218 505 683 ; -C -1 ; WX 564 ; N minus ; B 30 220 534 286 ; -C -1 ; WX 333 ; N Icircumflex ; B 11 0 322 886 ; -C -1 ; WX 500 ; N ncaron ; B 16 0 485 674 ; -C -1 ; WX 278 ; N tcommaaccent ; B 13 -218 279 579 ; -C -1 ; WX 564 ; N logicalnot ; B 30 108 534 386 ; -C -1 ; WX 500 ; N odieresis ; B 29 -10 470 623 ; -C -1 ; WX 500 ; N udieresis ; B 9 -10 479 623 ; -C -1 ; WX 549 ; N notequal ; B 12 -31 537 547 ; -C -1 ; WX 500 ; N gcommaaccent ; B 28 -218 470 749 ; -C -1 ; WX 500 ; N eth ; B 29 -10 471 686 ; -C -1 ; WX 444 ; N zcaron ; B 27 0 418 674 ; -C -1 ; WX 500 ; N ncommaaccent ; B 16 -218 485 460 ; -C -1 ; WX 300 ; N onesuperior ; B 57 270 248 676 ; -C -1 ; WX 278 ; N imacron ; B 6 0 271 601 ; -C -1 ; WX 500 ; N Euro ; B 0 0 0 0 ; -EndCharMetrics -StartKernData -StartKernPairs 2073 -KPX A C -40 -KPX A Cacute -40 -KPX A Ccaron -40 -KPX A Ccedilla -40 -KPX A G -40 -KPX A Gbreve -40 -KPX A Gcommaaccent -40 -KPX A O -55 -KPX A Oacute -55 -KPX A Ocircumflex -55 -KPX A Odieresis -55 -KPX A Ograve -55 -KPX A Ohungarumlaut -55 -KPX A Omacron -55 -KPX A Oslash -55 -KPX A Otilde -55 -KPX A Q -55 -KPX A T -111 -KPX A Tcaron -111 -KPX A Tcommaaccent -111 -KPX A U -55 -KPX A Uacute -55 -KPX A Ucircumflex -55 -KPX A Udieresis -55 -KPX A Ugrave -55 -KPX A Uhungarumlaut -55 -KPX A Umacron -55 -KPX A Uogonek -55 -KPX A Uring -55 -KPX A V -135 -KPX A W -90 -KPX A Y -105 -KPX A Yacute -105 -KPX A Ydieresis -105 -KPX A quoteright -111 -KPX A v -74 -KPX A w -92 -KPX A y -92 -KPX A yacute -92 -KPX A ydieresis -92 -KPX Aacute C -40 -KPX Aacute Cacute -40 -KPX Aacute Ccaron -40 -KPX Aacute Ccedilla -40 -KPX Aacute G -40 -KPX Aacute Gbreve -40 -KPX Aacute Gcommaaccent -40 -KPX Aacute O -55 -KPX Aacute Oacute -55 -KPX Aacute Ocircumflex -55 -KPX Aacute Odieresis -55 -KPX Aacute Ograve -55 -KPX Aacute Ohungarumlaut -55 -KPX Aacute Omacron -55 -KPX Aacute Oslash -55 -KPX Aacute Otilde -55 -KPX Aacute Q -55 -KPX Aacute T -111 -KPX Aacute Tcaron -111 -KPX Aacute Tcommaaccent -111 -KPX Aacute U -55 -KPX Aacute Uacute -55 -KPX Aacute Ucircumflex -55 -KPX Aacute Udieresis -55 -KPX Aacute Ugrave -55 -KPX Aacute Uhungarumlaut -55 -KPX Aacute Umacron -55 -KPX Aacute Uogonek -55 -KPX Aacute Uring -55 -KPX Aacute V -135 -KPX Aacute W -90 -KPX Aacute Y -105 -KPX Aacute Yacute -105 -KPX Aacute Ydieresis -105 -KPX Aacute quoteright -111 -KPX Aacute v -74 -KPX Aacute w -92 -KPX Aacute y -92 -KPX Aacute yacute -92 -KPX Aacute ydieresis -92 -KPX Abreve C -40 -KPX Abreve Cacute -40 -KPX Abreve Ccaron -40 -KPX Abreve Ccedilla -40 -KPX Abreve G -40 -KPX Abreve Gbreve -40 -KPX Abreve Gcommaaccent -40 -KPX Abreve O -55 -KPX Abreve Oacute -55 -KPX Abreve Ocircumflex -55 -KPX Abreve Odieresis -55 -KPX Abreve Ograve -55 -KPX Abreve Ohungarumlaut -55 -KPX Abreve Omacron -55 -KPX Abreve Oslash -55 -KPX Abreve Otilde -55 -KPX Abreve Q -55 -KPX Abreve T -111 -KPX Abreve Tcaron -111 -KPX Abreve Tcommaaccent -111 -KPX Abreve U -55 -KPX Abreve Uacute -55 -KPX Abreve Ucircumflex -55 -KPX Abreve Udieresis -55 -KPX Abreve Ugrave -55 -KPX Abreve Uhungarumlaut -55 -KPX Abreve Umacron -55 -KPX Abreve Uogonek -55 -KPX Abreve Uring -55 -KPX Abreve V -135 -KPX Abreve W -90 -KPX Abreve Y -105 -KPX Abreve Yacute -105 -KPX Abreve Ydieresis -105 -KPX Abreve quoteright -111 -KPX Abreve v -74 -KPX Abreve w -92 -KPX Abreve y -92 -KPX Abreve yacute -92 -KPX Abreve ydieresis -92 -KPX Acircumflex C -40 -KPX Acircumflex Cacute -40 -KPX Acircumflex Ccaron -40 -KPX Acircumflex Ccedilla -40 -KPX Acircumflex G -40 -KPX Acircumflex Gbreve -40 -KPX Acircumflex Gcommaaccent -40 -KPX Acircumflex O -55 -KPX Acircumflex Oacute -55 -KPX Acircumflex Ocircumflex -55 -KPX Acircumflex Odieresis -55 -KPX Acircumflex Ograve -55 -KPX Acircumflex Ohungarumlaut -55 -KPX Acircumflex Omacron -55 -KPX Acircumflex Oslash -55 -KPX Acircumflex Otilde -55 -KPX Acircumflex Q -55 -KPX Acircumflex T -111 -KPX Acircumflex Tcaron -111 -KPX Acircumflex Tcommaaccent -111 -KPX Acircumflex U -55 -KPX Acircumflex Uacute -55 -KPX Acircumflex Ucircumflex -55 -KPX Acircumflex Udieresis -55 -KPX Acircumflex Ugrave -55 -KPX Acircumflex Uhungarumlaut -55 -KPX Acircumflex Umacron -55 -KPX Acircumflex Uogonek -55 -KPX Acircumflex Uring -55 -KPX Acircumflex V -135 -KPX Acircumflex W -90 -KPX Acircumflex Y -105 -KPX Acircumflex Yacute -105 -KPX Acircumflex Ydieresis -105 -KPX Acircumflex quoteright -111 -KPX Acircumflex v -74 -KPX Acircumflex w -92 -KPX Acircumflex y -92 -KPX Acircumflex yacute -92 -KPX Acircumflex ydieresis -92 -KPX Adieresis C -40 -KPX Adieresis Cacute -40 -KPX Adieresis Ccaron -40 -KPX Adieresis Ccedilla -40 -KPX Adieresis G -40 -KPX Adieresis Gbreve -40 -KPX Adieresis Gcommaaccent -40 -KPX Adieresis O -55 -KPX Adieresis Oacute -55 -KPX Adieresis Ocircumflex -55 -KPX Adieresis Odieresis -55 -KPX Adieresis Ograve -55 -KPX Adieresis Ohungarumlaut -55 -KPX Adieresis Omacron -55 -KPX Adieresis Oslash -55 -KPX Adieresis Otilde -55 -KPX Adieresis Q -55 -KPX Adieresis T -111 -KPX Adieresis Tcaron -111 -KPX Adieresis Tcommaaccent -111 -KPX Adieresis U -55 -KPX Adieresis Uacute -55 -KPX Adieresis Ucircumflex -55 -KPX Adieresis Udieresis -55 -KPX Adieresis Ugrave -55 -KPX Adieresis Uhungarumlaut -55 -KPX Adieresis Umacron -55 -KPX Adieresis Uogonek -55 -KPX Adieresis Uring -55 -KPX Adieresis V -135 -KPX Adieresis W -90 -KPX Adieresis Y -105 -KPX Adieresis Yacute -105 -KPX Adieresis Ydieresis -105 -KPX Adieresis quoteright -111 -KPX Adieresis v -74 -KPX Adieresis w -92 -KPX Adieresis y -92 -KPX Adieresis yacute -92 -KPX Adieresis ydieresis -92 -KPX Agrave C -40 -KPX Agrave Cacute -40 -KPX Agrave Ccaron -40 -KPX Agrave Ccedilla -40 -KPX Agrave G -40 -KPX Agrave Gbreve -40 -KPX Agrave Gcommaaccent -40 -KPX Agrave O -55 -KPX Agrave Oacute -55 -KPX Agrave Ocircumflex -55 -KPX Agrave Odieresis -55 -KPX Agrave Ograve -55 -KPX Agrave Ohungarumlaut -55 -KPX Agrave Omacron -55 -KPX Agrave Oslash -55 -KPX Agrave Otilde -55 -KPX Agrave Q -55 -KPX Agrave T -111 -KPX Agrave Tcaron -111 -KPX Agrave Tcommaaccent -111 -KPX Agrave U -55 -KPX Agrave Uacute -55 -KPX Agrave Ucircumflex -55 -KPX Agrave Udieresis -55 -KPX Agrave Ugrave -55 -KPX Agrave Uhungarumlaut -55 -KPX Agrave Umacron -55 -KPX Agrave Uogonek -55 -KPX Agrave Uring -55 -KPX Agrave V -135 -KPX Agrave W -90 -KPX Agrave Y -105 -KPX Agrave Yacute -105 -KPX Agrave Ydieresis -105 -KPX Agrave quoteright -111 -KPX Agrave v -74 -KPX Agrave w -92 -KPX Agrave y -92 -KPX Agrave yacute -92 -KPX Agrave ydieresis -92 -KPX Amacron C -40 -KPX Amacron Cacute -40 -KPX Amacron Ccaron -40 -KPX Amacron Ccedilla -40 -KPX Amacron G -40 -KPX Amacron Gbreve -40 -KPX Amacron Gcommaaccent -40 -KPX Amacron O -55 -KPX Amacron Oacute -55 -KPX Amacron Ocircumflex -55 -KPX Amacron Odieresis -55 -KPX Amacron Ograve -55 -KPX Amacron Ohungarumlaut -55 -KPX Amacron Omacron -55 -KPX Amacron Oslash -55 -KPX Amacron Otilde -55 -KPX Amacron Q -55 -KPX Amacron T -111 -KPX Amacron Tcaron -111 -KPX Amacron Tcommaaccent -111 -KPX Amacron U -55 -KPX Amacron Uacute -55 -KPX Amacron Ucircumflex -55 -KPX Amacron Udieresis -55 -KPX Amacron Ugrave -55 -KPX Amacron Uhungarumlaut -55 -KPX Amacron Umacron -55 -KPX Amacron Uogonek -55 -KPX Amacron Uring -55 -KPX Amacron V -135 -KPX Amacron W -90 -KPX Amacron Y -105 -KPX Amacron Yacute -105 -KPX Amacron Ydieresis -105 -KPX Amacron quoteright -111 -KPX Amacron v -74 -KPX Amacron w -92 -KPX Amacron y -92 -KPX Amacron yacute -92 -KPX Amacron ydieresis -92 -KPX Aogonek C -40 -KPX Aogonek Cacute -40 -KPX Aogonek Ccaron -40 -KPX Aogonek Ccedilla -40 -KPX Aogonek G -40 -KPX Aogonek Gbreve -40 -KPX Aogonek Gcommaaccent -40 -KPX Aogonek O -55 -KPX Aogonek Oacute -55 -KPX Aogonek Ocircumflex -55 -KPX Aogonek Odieresis -55 -KPX Aogonek Ograve -55 -KPX Aogonek Ohungarumlaut -55 -KPX Aogonek Omacron -55 -KPX Aogonek Oslash -55 -KPX Aogonek Otilde -55 -KPX Aogonek Q -55 -KPX Aogonek T -111 -KPX Aogonek Tcaron -111 -KPX Aogonek Tcommaaccent -111 -KPX Aogonek U -55 -KPX Aogonek Uacute -55 -KPX Aogonek Ucircumflex -55 -KPX Aogonek Udieresis -55 -KPX Aogonek Ugrave -55 -KPX Aogonek Uhungarumlaut -55 -KPX Aogonek Umacron -55 -KPX Aogonek Uogonek -55 -KPX Aogonek Uring -55 -KPX Aogonek V -135 -KPX Aogonek W -90 -KPX Aogonek Y -105 -KPX Aogonek Yacute -105 -KPX Aogonek Ydieresis -105 -KPX Aogonek quoteright -111 -KPX Aogonek v -74 -KPX Aogonek w -52 -KPX Aogonek y -52 -KPX Aogonek yacute -52 -KPX Aogonek ydieresis -52 -KPX Aring C -40 -KPX Aring Cacute -40 -KPX Aring Ccaron -40 -KPX Aring Ccedilla -40 -KPX Aring G -40 -KPX Aring Gbreve -40 -KPX Aring Gcommaaccent -40 -KPX Aring O -55 -KPX Aring Oacute -55 -KPX Aring Ocircumflex -55 -KPX Aring Odieresis -55 -KPX Aring Ograve -55 -KPX Aring Ohungarumlaut -55 -KPX Aring Omacron -55 -KPX Aring Oslash -55 -KPX Aring Otilde -55 -KPX Aring Q -55 -KPX Aring T -111 -KPX Aring Tcaron -111 -KPX Aring Tcommaaccent -111 -KPX Aring U -55 -KPX Aring Uacute -55 -KPX Aring Ucircumflex -55 -KPX Aring Udieresis -55 -KPX Aring Ugrave -55 -KPX Aring Uhungarumlaut -55 -KPX Aring Umacron -55 -KPX Aring Uogonek -55 -KPX Aring Uring -55 -KPX Aring V -135 -KPX Aring W -90 -KPX Aring Y -105 -KPX Aring Yacute -105 -KPX Aring Ydieresis -105 -KPX Aring quoteright -111 -KPX Aring v -74 -KPX Aring w -92 -KPX Aring y -92 -KPX Aring yacute -92 -KPX Aring ydieresis -92 -KPX Atilde C -40 -KPX Atilde Cacute -40 -KPX Atilde Ccaron -40 -KPX Atilde Ccedilla -40 -KPX Atilde G -40 -KPX Atilde Gbreve -40 -KPX Atilde Gcommaaccent -40 -KPX Atilde O -55 -KPX Atilde Oacute -55 -KPX Atilde Ocircumflex -55 -KPX Atilde Odieresis -55 -KPX Atilde Ograve -55 -KPX Atilde Ohungarumlaut -55 -KPX Atilde Omacron -55 -KPX Atilde Oslash -55 -KPX Atilde Otilde -55 -KPX Atilde Q -55 -KPX Atilde T -111 -KPX Atilde Tcaron -111 -KPX Atilde Tcommaaccent -111 -KPX Atilde U -55 -KPX Atilde Uacute -55 -KPX Atilde Ucircumflex -55 -KPX Atilde Udieresis -55 -KPX Atilde Ugrave -55 -KPX Atilde Uhungarumlaut -55 -KPX Atilde Umacron -55 -KPX Atilde Uogonek -55 -KPX Atilde Uring -55 -KPX Atilde V -135 -KPX Atilde W -90 -KPX Atilde Y -105 -KPX Atilde Yacute -105 -KPX Atilde Ydieresis -105 -KPX Atilde quoteright -111 -KPX Atilde v -74 -KPX Atilde w -92 -KPX Atilde y -92 -KPX Atilde yacute -92 -KPX Atilde ydieresis -92 -KPX B A -35 -KPX B Aacute -35 -KPX B Abreve -35 -KPX B Acircumflex -35 -KPX B Adieresis -35 -KPX B Agrave -35 -KPX B Amacron -35 -KPX B Aogonek -35 -KPX B Aring -35 -KPX B Atilde -35 -KPX B U -10 -KPX B Uacute -10 -KPX B Ucircumflex -10 -KPX B Udieresis -10 -KPX B Ugrave -10 -KPX B Uhungarumlaut -10 -KPX B Umacron -10 -KPX B Uogonek -10 -KPX B Uring -10 -KPX D A -40 -KPX D Aacute -40 -KPX D Abreve -40 -KPX D Acircumflex -40 -KPX D Adieresis -40 -KPX D Agrave -40 -KPX D Amacron -40 -KPX D Aogonek -40 -KPX D Aring -40 -KPX D Atilde -40 -KPX D V -40 -KPX D W -30 -KPX D Y -55 -KPX D Yacute -55 -KPX D Ydieresis -55 -KPX Dcaron A -40 -KPX Dcaron Aacute -40 -KPX Dcaron Abreve -40 -KPX Dcaron Acircumflex -40 -KPX Dcaron Adieresis -40 -KPX Dcaron Agrave -40 -KPX Dcaron Amacron -40 -KPX Dcaron Aogonek -40 -KPX Dcaron Aring -40 -KPX Dcaron Atilde -40 -KPX Dcaron V -40 -KPX Dcaron W -30 -KPX Dcaron Y -55 -KPX Dcaron Yacute -55 -KPX Dcaron Ydieresis -55 -KPX Dcroat A -40 -KPX Dcroat Aacute -40 -KPX Dcroat Abreve -40 -KPX Dcroat Acircumflex -40 -KPX Dcroat Adieresis -40 -KPX Dcroat Agrave -40 -KPX Dcroat Amacron -40 -KPX Dcroat Aogonek -40 -KPX Dcroat Aring -40 -KPX Dcroat Atilde -40 -KPX Dcroat V -40 -KPX Dcroat W -30 -KPX Dcroat Y -55 -KPX Dcroat Yacute -55 -KPX Dcroat Ydieresis -55 -KPX F A -74 -KPX F Aacute -74 -KPX F Abreve -74 -KPX F Acircumflex -74 -KPX F Adieresis -74 -KPX F Agrave -74 -KPX F Amacron -74 -KPX F Aogonek -74 -KPX F Aring -74 -KPX F Atilde -74 -KPX F a -15 -KPX F aacute -15 -KPX F abreve -15 -KPX F acircumflex -15 -KPX F adieresis -15 -KPX F agrave -15 -KPX F amacron -15 -KPX F aogonek -15 -KPX F aring -15 -KPX F atilde -15 -KPX F comma -80 -KPX F o -15 -KPX F oacute -15 -KPX F ocircumflex -15 -KPX F odieresis -15 -KPX F ograve -15 -KPX F ohungarumlaut -15 -KPX F omacron -15 -KPX F oslash -15 -KPX F otilde -15 -KPX F period -80 -KPX J A -60 -KPX J Aacute -60 -KPX J Abreve -60 -KPX J Acircumflex -60 -KPX J Adieresis -60 -KPX J Agrave -60 -KPX J Amacron -60 -KPX J Aogonek -60 -KPX J Aring -60 -KPX J Atilde -60 -KPX K O -30 -KPX K Oacute -30 -KPX K Ocircumflex -30 -KPX K Odieresis -30 -KPX K Ograve -30 -KPX K Ohungarumlaut -30 -KPX K Omacron -30 -KPX K Oslash -30 -KPX K Otilde -30 -KPX K e -25 -KPX K eacute -25 -KPX K ecaron -25 -KPX K ecircumflex -25 -KPX K edieresis -25 -KPX K edotaccent -25 -KPX K egrave -25 -KPX K emacron -25 -KPX K eogonek -25 -KPX K o -35 -KPX K oacute -35 -KPX K ocircumflex -35 -KPX K odieresis -35 -KPX K ograve -35 -KPX K ohungarumlaut -35 -KPX K omacron -35 -KPX K oslash -35 -KPX K otilde -35 -KPX K u -15 -KPX K uacute -15 -KPX K ucircumflex -15 -KPX K udieresis -15 -KPX K ugrave -15 -KPX K uhungarumlaut -15 -KPX K umacron -15 -KPX K uogonek -15 -KPX K uring -15 -KPX K y -25 -KPX K yacute -25 -KPX K ydieresis -25 -KPX Kcommaaccent O -30 -KPX Kcommaaccent Oacute -30 -KPX Kcommaaccent Ocircumflex -30 -KPX Kcommaaccent Odieresis -30 -KPX Kcommaaccent Ograve -30 -KPX Kcommaaccent Ohungarumlaut -30 -KPX Kcommaaccent Omacron -30 -KPX Kcommaaccent Oslash -30 -KPX Kcommaaccent Otilde -30 -KPX Kcommaaccent e -25 -KPX Kcommaaccent eacute -25 -KPX Kcommaaccent ecaron -25 -KPX Kcommaaccent ecircumflex -25 -KPX Kcommaaccent edieresis -25 -KPX Kcommaaccent edotaccent -25 -KPX Kcommaaccent egrave -25 -KPX Kcommaaccent emacron -25 -KPX Kcommaaccent eogonek -25 -KPX Kcommaaccent o -35 -KPX Kcommaaccent oacute -35 -KPX Kcommaaccent ocircumflex -35 -KPX Kcommaaccent odieresis -35 -KPX Kcommaaccent ograve -35 -KPX Kcommaaccent ohungarumlaut -35 -KPX Kcommaaccent omacron -35 -KPX Kcommaaccent oslash -35 -KPX Kcommaaccent otilde -35 -KPX Kcommaaccent u -15 -KPX Kcommaaccent uacute -15 -KPX Kcommaaccent ucircumflex -15 -KPX Kcommaaccent udieresis -15 -KPX Kcommaaccent ugrave -15 -KPX Kcommaaccent uhungarumlaut -15 -KPX Kcommaaccent umacron -15 -KPX Kcommaaccent uogonek -15 -KPX Kcommaaccent uring -15 -KPX Kcommaaccent y -25 -KPX Kcommaaccent yacute -25 -KPX Kcommaaccent ydieresis -25 -KPX L T -92 -KPX L Tcaron -92 -KPX L Tcommaaccent -92 -KPX L V -100 -KPX L W -74 -KPX L Y -100 -KPX L Yacute -100 -KPX L Ydieresis -100 -KPX L quoteright -92 -KPX L y -55 -KPX L yacute -55 -KPX L ydieresis -55 -KPX Lacute T -92 -KPX Lacute Tcaron -92 -KPX Lacute Tcommaaccent -92 -KPX Lacute V -100 -KPX Lacute W -74 -KPX Lacute Y -100 -KPX Lacute Yacute -100 -KPX Lacute Ydieresis -100 -KPX Lacute quoteright -92 -KPX Lacute y -55 -KPX Lacute yacute -55 -KPX Lacute ydieresis -55 -KPX Lcaron quoteright -92 -KPX Lcaron y -55 -KPX Lcaron yacute -55 -KPX Lcaron ydieresis -55 -KPX Lcommaaccent T -92 -KPX Lcommaaccent Tcaron -92 -KPX Lcommaaccent Tcommaaccent -92 -KPX Lcommaaccent V -100 -KPX Lcommaaccent W -74 -KPX Lcommaaccent Y -100 -KPX Lcommaaccent Yacute -100 -KPX Lcommaaccent Ydieresis -100 -KPX Lcommaaccent quoteright -92 -KPX Lcommaaccent y -55 -KPX Lcommaaccent yacute -55 -KPX Lcommaaccent ydieresis -55 -KPX Lslash T -92 -KPX Lslash Tcaron -92 -KPX Lslash Tcommaaccent -92 -KPX Lslash V -100 -KPX Lslash W -74 -KPX Lslash Y -100 -KPX Lslash Yacute -100 -KPX Lslash Ydieresis -100 -KPX Lslash quoteright -92 -KPX Lslash y -55 -KPX Lslash yacute -55 -KPX Lslash ydieresis -55 -KPX N A -35 -KPX N Aacute -35 -KPX N Abreve -35 -KPX N Acircumflex -35 -KPX N Adieresis -35 -KPX N Agrave -35 -KPX N Amacron -35 -KPX N Aogonek -35 -KPX N Aring -35 -KPX N Atilde -35 -KPX Nacute A -35 -KPX Nacute Aacute -35 -KPX Nacute Abreve -35 -KPX Nacute Acircumflex -35 -KPX Nacute Adieresis -35 -KPX Nacute Agrave -35 -KPX Nacute Amacron -35 -KPX Nacute Aogonek -35 -KPX Nacute Aring -35 -KPX Nacute Atilde -35 -KPX Ncaron A -35 -KPX Ncaron Aacute -35 -KPX Ncaron Abreve -35 -KPX Ncaron Acircumflex -35 -KPX Ncaron Adieresis -35 -KPX Ncaron Agrave -35 -KPX Ncaron Amacron -35 -KPX Ncaron Aogonek -35 -KPX Ncaron Aring -35 -KPX Ncaron Atilde -35 -KPX Ncommaaccent A -35 -KPX Ncommaaccent Aacute -35 -KPX Ncommaaccent Abreve -35 -KPX Ncommaaccent Acircumflex -35 -KPX Ncommaaccent Adieresis -35 -KPX Ncommaaccent Agrave -35 -KPX Ncommaaccent Amacron -35 -KPX Ncommaaccent Aogonek -35 -KPX Ncommaaccent Aring -35 -KPX Ncommaaccent Atilde -35 -KPX Ntilde A -35 -KPX Ntilde Aacute -35 -KPX Ntilde Abreve -35 -KPX Ntilde Acircumflex -35 -KPX Ntilde Adieresis -35 -KPX Ntilde Agrave -35 -KPX Ntilde Amacron -35 -KPX Ntilde Aogonek -35 -KPX Ntilde Aring -35 -KPX Ntilde Atilde -35 -KPX O A -35 -KPX O Aacute -35 -KPX O Abreve -35 -KPX O Acircumflex -35 -KPX O Adieresis -35 -KPX O Agrave -35 -KPX O Amacron -35 -KPX O Aogonek -35 -KPX O Aring -35 -KPX O Atilde -35 -KPX O T -40 -KPX O Tcaron -40 -KPX O Tcommaaccent -40 -KPX O V -50 -KPX O W -35 -KPX O X -40 -KPX O Y -50 -KPX O Yacute -50 -KPX O Ydieresis -50 -KPX Oacute A -35 -KPX Oacute Aacute -35 -KPX Oacute Abreve -35 -KPX Oacute Acircumflex -35 -KPX Oacute Adieresis -35 -KPX Oacute Agrave -35 -KPX Oacute Amacron -35 -KPX Oacute Aogonek -35 -KPX Oacute Aring -35 -KPX Oacute Atilde -35 -KPX Oacute T -40 -KPX Oacute Tcaron -40 -KPX Oacute Tcommaaccent -40 -KPX Oacute V -50 -KPX Oacute W -35 -KPX Oacute X -40 -KPX Oacute Y -50 -KPX Oacute Yacute -50 -KPX Oacute Ydieresis -50 -KPX Ocircumflex A -35 -KPX Ocircumflex Aacute -35 -KPX Ocircumflex Abreve -35 -KPX Ocircumflex Acircumflex -35 -KPX Ocircumflex Adieresis -35 -KPX Ocircumflex Agrave -35 -KPX Ocircumflex Amacron -35 -KPX Ocircumflex Aogonek -35 -KPX Ocircumflex Aring -35 -KPX Ocircumflex Atilde -35 -KPX Ocircumflex T -40 -KPX Ocircumflex Tcaron -40 -KPX Ocircumflex Tcommaaccent -40 -KPX Ocircumflex V -50 -KPX Ocircumflex W -35 -KPX Ocircumflex X -40 -KPX Ocircumflex Y -50 -KPX Ocircumflex Yacute -50 -KPX Ocircumflex Ydieresis -50 -KPX Odieresis A -35 -KPX Odieresis Aacute -35 -KPX Odieresis Abreve -35 -KPX Odieresis Acircumflex -35 -KPX Odieresis Adieresis -35 -KPX Odieresis Agrave -35 -KPX Odieresis Amacron -35 -KPX Odieresis Aogonek -35 -KPX Odieresis Aring -35 -KPX Odieresis Atilde -35 -KPX Odieresis T -40 -KPX Odieresis Tcaron -40 -KPX Odieresis Tcommaaccent -40 -KPX Odieresis V -50 -KPX Odieresis W -35 -KPX Odieresis X -40 -KPX Odieresis Y -50 -KPX Odieresis Yacute -50 -KPX Odieresis Ydieresis -50 -KPX Ograve A -35 -KPX Ograve Aacute -35 -KPX Ograve Abreve -35 -KPX Ograve Acircumflex -35 -KPX Ograve Adieresis -35 -KPX Ograve Agrave -35 -KPX Ograve Amacron -35 -KPX Ograve Aogonek -35 -KPX Ograve Aring -35 -KPX Ograve Atilde -35 -KPX Ograve T -40 -KPX Ograve Tcaron -40 -KPX Ograve Tcommaaccent -40 -KPX Ograve V -50 -KPX Ograve W -35 -KPX Ograve X -40 -KPX Ograve Y -50 -KPX Ograve Yacute -50 -KPX Ograve Ydieresis -50 -KPX Ohungarumlaut A -35 -KPX Ohungarumlaut Aacute -35 -KPX Ohungarumlaut Abreve -35 -KPX Ohungarumlaut Acircumflex -35 -KPX Ohungarumlaut Adieresis -35 -KPX Ohungarumlaut Agrave -35 -KPX Ohungarumlaut Amacron -35 -KPX Ohungarumlaut Aogonek -35 -KPX Ohungarumlaut Aring -35 -KPX Ohungarumlaut Atilde -35 -KPX Ohungarumlaut T -40 -KPX Ohungarumlaut Tcaron -40 -KPX Ohungarumlaut Tcommaaccent -40 -KPX Ohungarumlaut V -50 -KPX Ohungarumlaut W -35 -KPX Ohungarumlaut X -40 -KPX Ohungarumlaut Y -50 -KPX Ohungarumlaut Yacute -50 -KPX Ohungarumlaut Ydieresis -50 -KPX Omacron A -35 -KPX Omacron Aacute -35 -KPX Omacron Abreve -35 -KPX Omacron Acircumflex -35 -KPX Omacron Adieresis -35 -KPX Omacron Agrave -35 -KPX Omacron Amacron -35 -KPX Omacron Aogonek -35 -KPX Omacron Aring -35 -KPX Omacron Atilde -35 -KPX Omacron T -40 -KPX Omacron Tcaron -40 -KPX Omacron Tcommaaccent -40 -KPX Omacron V -50 -KPX Omacron W -35 -KPX Omacron X -40 -KPX Omacron Y -50 -KPX Omacron Yacute -50 -KPX Omacron Ydieresis -50 -KPX Oslash A -35 -KPX Oslash Aacute -35 -KPX Oslash Abreve -35 -KPX Oslash Acircumflex -35 -KPX Oslash Adieresis -35 -KPX Oslash Agrave -35 -KPX Oslash Amacron -35 -KPX Oslash Aogonek -35 -KPX Oslash Aring -35 -KPX Oslash Atilde -35 -KPX Oslash T -40 -KPX Oslash Tcaron -40 -KPX Oslash Tcommaaccent -40 -KPX Oslash V -50 -KPX Oslash W -35 -KPX Oslash X -40 -KPX Oslash Y -50 -KPX Oslash Yacute -50 -KPX Oslash Ydieresis -50 -KPX Otilde A -35 -KPX Otilde Aacute -35 -KPX Otilde Abreve -35 -KPX Otilde Acircumflex -35 -KPX Otilde Adieresis -35 -KPX Otilde Agrave -35 -KPX Otilde Amacron -35 -KPX Otilde Aogonek -35 -KPX Otilde Aring -35 -KPX Otilde Atilde -35 -KPX Otilde T -40 -KPX Otilde Tcaron -40 -KPX Otilde Tcommaaccent -40 -KPX Otilde V -50 -KPX Otilde W -35 -KPX Otilde X -40 -KPX Otilde Y -50 -KPX Otilde Yacute -50 -KPX Otilde Ydieresis -50 -KPX P A -92 -KPX P Aacute -92 -KPX P Abreve -92 -KPX P Acircumflex -92 -KPX P Adieresis -92 -KPX P Agrave -92 -KPX P Amacron -92 -KPX P Aogonek -92 -KPX P Aring -92 -KPX P Atilde -92 -KPX P a -15 -KPX P aacute -15 -KPX P abreve -15 -KPX P acircumflex -15 -KPX P adieresis -15 -KPX P agrave -15 -KPX P amacron -15 -KPX P aogonek -15 -KPX P aring -15 -KPX P atilde -15 -KPX P comma -111 -KPX P period -111 -KPX Q U -10 -KPX Q Uacute -10 -KPX Q Ucircumflex -10 -KPX Q Udieresis -10 -KPX Q Ugrave -10 -KPX Q Uhungarumlaut -10 -KPX Q Umacron -10 -KPX Q Uogonek -10 -KPX Q Uring -10 -KPX R O -40 -KPX R Oacute -40 -KPX R Ocircumflex -40 -KPX R Odieresis -40 -KPX R Ograve -40 -KPX R Ohungarumlaut -40 -KPX R Omacron -40 -KPX R Oslash -40 -KPX R Otilde -40 -KPX R T -60 -KPX R Tcaron -60 -KPX R Tcommaaccent -60 -KPX R U -40 -KPX R Uacute -40 -KPX R Ucircumflex -40 -KPX R Udieresis -40 -KPX R Ugrave -40 -KPX R Uhungarumlaut -40 -KPX R Umacron -40 -KPX R Uogonek -40 -KPX R Uring -40 -KPX R V -80 -KPX R W -55 -KPX R Y -65 -KPX R Yacute -65 -KPX R Ydieresis -65 -KPX Racute O -40 -KPX Racute Oacute -40 -KPX Racute Ocircumflex -40 -KPX Racute Odieresis -40 -KPX Racute Ograve -40 -KPX Racute Ohungarumlaut -40 -KPX Racute Omacron -40 -KPX Racute Oslash -40 -KPX Racute Otilde -40 -KPX Racute T -60 -KPX Racute Tcaron -60 -KPX Racute Tcommaaccent -60 -KPX Racute U -40 -KPX Racute Uacute -40 -KPX Racute Ucircumflex -40 -KPX Racute Udieresis -40 -KPX Racute Ugrave -40 -KPX Racute Uhungarumlaut -40 -KPX Racute Umacron -40 -KPX Racute Uogonek -40 -KPX Racute Uring -40 -KPX Racute V -80 -KPX Racute W -55 -KPX Racute Y -65 -KPX Racute Yacute -65 -KPX Racute Ydieresis -65 -KPX Rcaron O -40 -KPX Rcaron Oacute -40 -KPX Rcaron Ocircumflex -40 -KPX Rcaron Odieresis -40 -KPX Rcaron Ograve -40 -KPX Rcaron Ohungarumlaut -40 -KPX Rcaron Omacron -40 -KPX Rcaron Oslash -40 -KPX Rcaron Otilde -40 -KPX Rcaron T -60 -KPX Rcaron Tcaron -60 -KPX Rcaron Tcommaaccent -60 -KPX Rcaron U -40 -KPX Rcaron Uacute -40 -KPX Rcaron Ucircumflex -40 -KPX Rcaron Udieresis -40 -KPX Rcaron Ugrave -40 -KPX Rcaron Uhungarumlaut -40 -KPX Rcaron Umacron -40 -KPX Rcaron Uogonek -40 -KPX Rcaron Uring -40 -KPX Rcaron V -80 -KPX Rcaron W -55 -KPX Rcaron Y -65 -KPX Rcaron Yacute -65 -KPX Rcaron Ydieresis -65 -KPX Rcommaaccent O -40 -KPX Rcommaaccent Oacute -40 -KPX Rcommaaccent Ocircumflex -40 -KPX Rcommaaccent Odieresis -40 -KPX Rcommaaccent Ograve -40 -KPX Rcommaaccent Ohungarumlaut -40 -KPX Rcommaaccent Omacron -40 -KPX Rcommaaccent Oslash -40 -KPX Rcommaaccent Otilde -40 -KPX Rcommaaccent T -60 -KPX Rcommaaccent Tcaron -60 -KPX Rcommaaccent Tcommaaccent -60 -KPX Rcommaaccent U -40 -KPX Rcommaaccent Uacute -40 -KPX Rcommaaccent Ucircumflex -40 -KPX Rcommaaccent Udieresis -40 -KPX Rcommaaccent Ugrave -40 -KPX Rcommaaccent Uhungarumlaut -40 -KPX Rcommaaccent Umacron -40 -KPX Rcommaaccent Uogonek -40 -KPX Rcommaaccent Uring -40 -KPX Rcommaaccent V -80 -KPX Rcommaaccent W -55 -KPX Rcommaaccent Y -65 -KPX Rcommaaccent Yacute -65 -KPX Rcommaaccent Ydieresis -65 -KPX T A -93 -KPX T Aacute -93 -KPX T Abreve -93 -KPX T Acircumflex -93 -KPX T Adieresis -93 -KPX T Agrave -93 -KPX T Amacron -93 -KPX T Aogonek -93 -KPX T Aring -93 -KPX T Atilde -93 -KPX T O -18 -KPX T Oacute -18 -KPX T Ocircumflex -18 -KPX T Odieresis -18 -KPX T Ograve -18 -KPX T Ohungarumlaut -18 -KPX T Omacron -18 -KPX T Oslash -18 -KPX T Otilde -18 -KPX T a -80 -KPX T aacute -80 -KPX T abreve -80 -KPX T acircumflex -80 -KPX T adieresis -40 -KPX T agrave -40 -KPX T amacron -40 -KPX T aogonek -80 -KPX T aring -80 -KPX T atilde -40 -KPX T colon -50 -KPX T comma -74 -KPX T e -70 -KPX T eacute -70 -KPX T ecaron -70 -KPX T ecircumflex -70 -KPX T edieresis -30 -KPX T edotaccent -70 -KPX T egrave -70 -KPX T emacron -30 -KPX T eogonek -70 -KPX T hyphen -92 -KPX T i -35 -KPX T iacute -35 -KPX T iogonek -35 -KPX T o -80 -KPX T oacute -80 -KPX T ocircumflex -80 -KPX T odieresis -80 -KPX T ograve -80 -KPX T ohungarumlaut -80 -KPX T omacron -80 -KPX T oslash -80 -KPX T otilde -80 -KPX T period -74 -KPX T r -35 -KPX T racute -35 -KPX T rcaron -35 -KPX T rcommaaccent -35 -KPX T semicolon -55 -KPX T u -45 -KPX T uacute -45 -KPX T ucircumflex -45 -KPX T udieresis -45 -KPX T ugrave -45 -KPX T uhungarumlaut -45 -KPX T umacron -45 -KPX T uogonek -45 -KPX T uring -45 -KPX T w -80 -KPX T y -80 -KPX T yacute -80 -KPX T ydieresis -80 -KPX Tcaron A -93 -KPX Tcaron Aacute -93 -KPX Tcaron Abreve -93 -KPX Tcaron Acircumflex -93 -KPX Tcaron Adieresis -93 -KPX Tcaron Agrave -93 -KPX Tcaron Amacron -93 -KPX Tcaron Aogonek -93 -KPX Tcaron Aring -93 -KPX Tcaron Atilde -93 -KPX Tcaron O -18 -KPX Tcaron Oacute -18 -KPX Tcaron Ocircumflex -18 -KPX Tcaron Odieresis -18 -KPX Tcaron Ograve -18 -KPX Tcaron Ohungarumlaut -18 -KPX Tcaron Omacron -18 -KPX Tcaron Oslash -18 -KPX Tcaron Otilde -18 -KPX Tcaron a -80 -KPX Tcaron aacute -80 -KPX Tcaron abreve -80 -KPX Tcaron acircumflex -80 -KPX Tcaron adieresis -40 -KPX Tcaron agrave -40 -KPX Tcaron amacron -40 -KPX Tcaron aogonek -80 -KPX Tcaron aring -80 -KPX Tcaron atilde -40 -KPX Tcaron colon -50 -KPX Tcaron comma -74 -KPX Tcaron e -70 -KPX Tcaron eacute -70 -KPX Tcaron ecaron -70 -KPX Tcaron ecircumflex -30 -KPX Tcaron edieresis -30 -KPX Tcaron edotaccent -70 -KPX Tcaron egrave -70 -KPX Tcaron emacron -30 -KPX Tcaron eogonek -70 -KPX Tcaron hyphen -92 -KPX Tcaron i -35 -KPX Tcaron iacute -35 -KPX Tcaron iogonek -35 -KPX Tcaron o -80 -KPX Tcaron oacute -80 -KPX Tcaron ocircumflex -80 -KPX Tcaron odieresis -80 -KPX Tcaron ograve -80 -KPX Tcaron ohungarumlaut -80 -KPX Tcaron omacron -80 -KPX Tcaron oslash -80 -KPX Tcaron otilde -80 -KPX Tcaron period -74 -KPX Tcaron r -35 -KPX Tcaron racute -35 -KPX Tcaron rcaron -35 -KPX Tcaron rcommaaccent -35 -KPX Tcaron semicolon -55 -KPX Tcaron u -45 -KPX Tcaron uacute -45 -KPX Tcaron ucircumflex -45 -KPX Tcaron udieresis -45 -KPX Tcaron ugrave -45 -KPX Tcaron uhungarumlaut -45 -KPX Tcaron umacron -45 -KPX Tcaron uogonek -45 -KPX Tcaron uring -45 -KPX Tcaron w -80 -KPX Tcaron y -80 -KPX Tcaron yacute -80 -KPX Tcaron ydieresis -80 -KPX Tcommaaccent A -93 -KPX Tcommaaccent Aacute -93 -KPX Tcommaaccent Abreve -93 -KPX Tcommaaccent Acircumflex -93 -KPX Tcommaaccent Adieresis -93 -KPX Tcommaaccent Agrave -93 -KPX Tcommaaccent Amacron -93 -KPX Tcommaaccent Aogonek -93 -KPX Tcommaaccent Aring -93 -KPX Tcommaaccent Atilde -93 -KPX Tcommaaccent O -18 -KPX Tcommaaccent Oacute -18 -KPX Tcommaaccent Ocircumflex -18 -KPX Tcommaaccent Odieresis -18 -KPX Tcommaaccent Ograve -18 -KPX Tcommaaccent Ohungarumlaut -18 -KPX Tcommaaccent Omacron -18 -KPX Tcommaaccent Oslash -18 -KPX Tcommaaccent Otilde -18 -KPX Tcommaaccent a -80 -KPX Tcommaaccent aacute -80 -KPX Tcommaaccent abreve -80 -KPX Tcommaaccent acircumflex -80 -KPX Tcommaaccent adieresis -40 -KPX Tcommaaccent agrave -40 -KPX Tcommaaccent amacron -40 -KPX Tcommaaccent aogonek -80 -KPX Tcommaaccent aring -80 -KPX Tcommaaccent atilde -40 -KPX Tcommaaccent colon -50 -KPX Tcommaaccent comma -74 -KPX Tcommaaccent e -70 -KPX Tcommaaccent eacute -70 -KPX Tcommaaccent ecaron -70 -KPX Tcommaaccent ecircumflex -30 -KPX Tcommaaccent edieresis -30 -KPX Tcommaaccent edotaccent -70 -KPX Tcommaaccent egrave -30 -KPX Tcommaaccent emacron -70 -KPX Tcommaaccent eogonek -70 -KPX Tcommaaccent hyphen -92 -KPX Tcommaaccent i -35 -KPX Tcommaaccent iacute -35 -KPX Tcommaaccent iogonek -35 -KPX Tcommaaccent o -80 -KPX Tcommaaccent oacute -80 -KPX Tcommaaccent ocircumflex -80 -KPX Tcommaaccent odieresis -80 -KPX Tcommaaccent ograve -80 -KPX Tcommaaccent ohungarumlaut -80 -KPX Tcommaaccent omacron -80 -KPX Tcommaaccent oslash -80 -KPX Tcommaaccent otilde -80 -KPX Tcommaaccent period -74 -KPX Tcommaaccent r -35 -KPX Tcommaaccent racute -35 -KPX Tcommaaccent rcaron -35 -KPX Tcommaaccent rcommaaccent -35 -KPX Tcommaaccent semicolon -55 -KPX Tcommaaccent u -45 -KPX Tcommaaccent uacute -45 -KPX Tcommaaccent ucircumflex -45 -KPX Tcommaaccent udieresis -45 -KPX Tcommaaccent ugrave -45 -KPX Tcommaaccent uhungarumlaut -45 -KPX Tcommaaccent umacron -45 -KPX Tcommaaccent uogonek -45 -KPX Tcommaaccent uring -45 -KPX Tcommaaccent w -80 -KPX Tcommaaccent y -80 -KPX Tcommaaccent yacute -80 -KPX Tcommaaccent ydieresis -80 -KPX U A -40 -KPX U Aacute -40 -KPX U Abreve -40 -KPX U Acircumflex -40 -KPX U Adieresis -40 -KPX U Agrave -40 -KPX U Amacron -40 -KPX U Aogonek -40 -KPX U Aring -40 -KPX U Atilde -40 -KPX Uacute A -40 -KPX Uacute Aacute -40 -KPX Uacute Abreve -40 -KPX Uacute Acircumflex -40 -KPX Uacute Adieresis -40 -KPX Uacute Agrave -40 -KPX Uacute Amacron -40 -KPX Uacute Aogonek -40 -KPX Uacute Aring -40 -KPX Uacute Atilde -40 -KPX Ucircumflex A -40 -KPX Ucircumflex Aacute -40 -KPX Ucircumflex Abreve -40 -KPX Ucircumflex Acircumflex -40 -KPX Ucircumflex Adieresis -40 -KPX Ucircumflex Agrave -40 -KPX Ucircumflex Amacron -40 -KPX Ucircumflex Aogonek -40 -KPX Ucircumflex Aring -40 -KPX Ucircumflex Atilde -40 -KPX Udieresis A -40 -KPX Udieresis Aacute -40 -KPX Udieresis Abreve -40 -KPX Udieresis Acircumflex -40 -KPX Udieresis Adieresis -40 -KPX Udieresis Agrave -40 -KPX Udieresis Amacron -40 -KPX Udieresis Aogonek -40 -KPX Udieresis Aring -40 -KPX Udieresis Atilde -40 -KPX Ugrave A -40 -KPX Ugrave Aacute -40 -KPX Ugrave Abreve -40 -KPX Ugrave Acircumflex -40 -KPX Ugrave Adieresis -40 -KPX Ugrave Agrave -40 -KPX Ugrave Amacron -40 -KPX Ugrave Aogonek -40 -KPX Ugrave Aring -40 -KPX Ugrave Atilde -40 -KPX Uhungarumlaut A -40 -KPX Uhungarumlaut Aacute -40 -KPX Uhungarumlaut Abreve -40 -KPX Uhungarumlaut Acircumflex -40 -KPX Uhungarumlaut Adieresis -40 -KPX Uhungarumlaut Agrave -40 -KPX Uhungarumlaut Amacron -40 -KPX Uhungarumlaut Aogonek -40 -KPX Uhungarumlaut Aring -40 -KPX Uhungarumlaut Atilde -40 -KPX Umacron A -40 -KPX Umacron Aacute -40 -KPX Umacron Abreve -40 -KPX Umacron Acircumflex -40 -KPX Umacron Adieresis -40 -KPX Umacron Agrave -40 -KPX Umacron Amacron -40 -KPX Umacron Aogonek -40 -KPX Umacron Aring -40 -KPX Umacron Atilde -40 -KPX Uogonek A -40 -KPX Uogonek Aacute -40 -KPX Uogonek Abreve -40 -KPX Uogonek Acircumflex -40 -KPX Uogonek Adieresis -40 -KPX Uogonek Agrave -40 -KPX Uogonek Amacron -40 -KPX Uogonek Aogonek -40 -KPX Uogonek Aring -40 -KPX Uogonek Atilde -40 -KPX Uring A -40 -KPX Uring Aacute -40 -KPX Uring Abreve -40 -KPX Uring Acircumflex -40 -KPX Uring Adieresis -40 -KPX Uring Agrave -40 -KPX Uring Amacron -40 -KPX Uring Aogonek -40 -KPX Uring Aring -40 -KPX Uring Atilde -40 -KPX V A -135 -KPX V Aacute -135 -KPX V Abreve -135 -KPX V Acircumflex -135 -KPX V Adieresis -135 -KPX V Agrave -135 -KPX V Amacron -135 -KPX V Aogonek -135 -KPX V Aring -135 -KPX V Atilde -135 -KPX V G -15 -KPX V Gbreve -15 -KPX V Gcommaaccent -15 -KPX V O -40 -KPX V Oacute -40 -KPX V Ocircumflex -40 -KPX V Odieresis -40 -KPX V Ograve -40 -KPX V Ohungarumlaut -40 -KPX V Omacron -40 -KPX V Oslash -40 -KPX V Otilde -40 -KPX V a -111 -KPX V aacute -111 -KPX V abreve -111 -KPX V acircumflex -71 -KPX V adieresis -71 -KPX V agrave -71 -KPX V amacron -71 -KPX V aogonek -111 -KPX V aring -111 -KPX V atilde -71 -KPX V colon -74 -KPX V comma -129 -KPX V e -111 -KPX V eacute -111 -KPX V ecaron -71 -KPX V ecircumflex -71 -KPX V edieresis -71 -KPX V edotaccent -111 -KPX V egrave -71 -KPX V emacron -71 -KPX V eogonek -111 -KPX V hyphen -100 -KPX V i -60 -KPX V iacute -60 -KPX V icircumflex -20 -KPX V idieresis -20 -KPX V igrave -20 -KPX V imacron -20 -KPX V iogonek -60 -KPX V o -129 -KPX V oacute -129 -KPX V ocircumflex -129 -KPX V odieresis -89 -KPX V ograve -89 -KPX V ohungarumlaut -129 -KPX V omacron -89 -KPX V oslash -129 -KPX V otilde -89 -KPX V period -129 -KPX V semicolon -74 -KPX V u -75 -KPX V uacute -75 -KPX V ucircumflex -75 -KPX V udieresis -75 -KPX V ugrave -75 -KPX V uhungarumlaut -75 -KPX V umacron -75 -KPX V uogonek -75 -KPX V uring -75 -KPX W A -120 -KPX W Aacute -120 -KPX W Abreve -120 -KPX W Acircumflex -120 -KPX W Adieresis -120 -KPX W Agrave -120 -KPX W Amacron -120 -KPX W Aogonek -120 -KPX W Aring -120 -KPX W Atilde -120 -KPX W O -10 -KPX W Oacute -10 -KPX W Ocircumflex -10 -KPX W Odieresis -10 -KPX W Ograve -10 -KPX W Ohungarumlaut -10 -KPX W Omacron -10 -KPX W Oslash -10 -KPX W Otilde -10 -KPX W a -80 -KPX W aacute -80 -KPX W abreve -80 -KPX W acircumflex -80 -KPX W adieresis -80 -KPX W agrave -80 -KPX W amacron -80 -KPX W aogonek -80 -KPX W aring -80 -KPX W atilde -80 -KPX W colon -37 -KPX W comma -92 -KPX W e -80 -KPX W eacute -80 -KPX W ecaron -80 -KPX W ecircumflex -80 -KPX W edieresis -40 -KPX W edotaccent -80 -KPX W egrave -40 -KPX W emacron -40 -KPX W eogonek -80 -KPX W hyphen -65 -KPX W i -40 -KPX W iacute -40 -KPX W iogonek -40 -KPX W o -80 -KPX W oacute -80 -KPX W ocircumflex -80 -KPX W odieresis -80 -KPX W ograve -80 -KPX W ohungarumlaut -80 -KPX W omacron -80 -KPX W oslash -80 -KPX W otilde -80 -KPX W period -92 -KPX W semicolon -37 -KPX W u -50 -KPX W uacute -50 -KPX W ucircumflex -50 -KPX W udieresis -50 -KPX W ugrave -50 -KPX W uhungarumlaut -50 -KPX W umacron -50 -KPX W uogonek -50 -KPX W uring -50 -KPX W y -73 -KPX W yacute -73 -KPX W ydieresis -73 -KPX Y A -120 -KPX Y Aacute -120 -KPX Y Abreve -120 -KPX Y Acircumflex -120 -KPX Y Adieresis -120 -KPX Y Agrave -120 -KPX Y Amacron -120 -KPX Y Aogonek -120 -KPX Y Aring -120 -KPX Y Atilde -120 -KPX Y O -30 -KPX Y Oacute -30 -KPX Y Ocircumflex -30 -KPX Y Odieresis -30 -KPX Y Ograve -30 -KPX Y Ohungarumlaut -30 -KPX Y Omacron -30 -KPX Y Oslash -30 -KPX Y Otilde -30 -KPX Y a -100 -KPX Y aacute -100 -KPX Y abreve -100 -KPX Y acircumflex -100 -KPX Y adieresis -60 -KPX Y agrave -60 -KPX Y amacron -60 -KPX Y aogonek -100 -KPX Y aring -100 -KPX Y atilde -60 -KPX Y colon -92 -KPX Y comma -129 -KPX Y e -100 -KPX Y eacute -100 -KPX Y ecaron -100 -KPX Y ecircumflex -100 -KPX Y edieresis -60 -KPX Y edotaccent -100 -KPX Y egrave -60 -KPX Y emacron -60 -KPX Y eogonek -100 -KPX Y hyphen -111 -KPX Y i -55 -KPX Y iacute -55 -KPX Y iogonek -55 -KPX Y o -110 -KPX Y oacute -110 -KPX Y ocircumflex -110 -KPX Y odieresis -70 -KPX Y ograve -70 -KPX Y ohungarumlaut -110 -KPX Y omacron -70 -KPX Y oslash -110 -KPX Y otilde -70 -KPX Y period -129 -KPX Y semicolon -92 -KPX Y u -111 -KPX Y uacute -111 -KPX Y ucircumflex -111 -KPX Y udieresis -71 -KPX Y ugrave -71 -KPX Y uhungarumlaut -111 -KPX Y umacron -71 -KPX Y uogonek -111 -KPX Y uring -111 -KPX Yacute A -120 -KPX Yacute Aacute -120 -KPX Yacute Abreve -120 -KPX Yacute Acircumflex -120 -KPX Yacute Adieresis -120 -KPX Yacute Agrave -120 -KPX Yacute Amacron -120 -KPX Yacute Aogonek -120 -KPX Yacute Aring -120 -KPX Yacute Atilde -120 -KPX Yacute O -30 -KPX Yacute Oacute -30 -KPX Yacute Ocircumflex -30 -KPX Yacute Odieresis -30 -KPX Yacute Ograve -30 -KPX Yacute Ohungarumlaut -30 -KPX Yacute Omacron -30 -KPX Yacute Oslash -30 -KPX Yacute Otilde -30 -KPX Yacute a -100 -KPX Yacute aacute -100 -KPX Yacute abreve -100 -KPX Yacute acircumflex -100 -KPX Yacute adieresis -60 -KPX Yacute agrave -60 -KPX Yacute amacron -60 -KPX Yacute aogonek -100 -KPX Yacute aring -100 -KPX Yacute atilde -60 -KPX Yacute colon -92 -KPX Yacute comma -129 -KPX Yacute e -100 -KPX Yacute eacute -100 -KPX Yacute ecaron -100 -KPX Yacute ecircumflex -100 -KPX Yacute edieresis -60 -KPX Yacute edotaccent -100 -KPX Yacute egrave -60 -KPX Yacute emacron -60 -KPX Yacute eogonek -100 -KPX Yacute hyphen -111 -KPX Yacute i -55 -KPX Yacute iacute -55 -KPX Yacute iogonek -55 -KPX Yacute o -110 -KPX Yacute oacute -110 -KPX Yacute ocircumflex -110 -KPX Yacute odieresis -70 -KPX Yacute ograve -70 -KPX Yacute ohungarumlaut -110 -KPX Yacute omacron -70 -KPX Yacute oslash -110 -KPX Yacute otilde -70 -KPX Yacute period -129 -KPX Yacute semicolon -92 -KPX Yacute u -111 -KPX Yacute uacute -111 -KPX Yacute ucircumflex -111 -KPX Yacute udieresis -71 -KPX Yacute ugrave -71 -KPX Yacute uhungarumlaut -111 -KPX Yacute umacron -71 -KPX Yacute uogonek -111 -KPX Yacute uring -111 -KPX Ydieresis A -120 -KPX Ydieresis Aacute -120 -KPX Ydieresis Abreve -120 -KPX Ydieresis Acircumflex -120 -KPX Ydieresis Adieresis -120 -KPX Ydieresis Agrave -120 -KPX Ydieresis Amacron -120 -KPX Ydieresis Aogonek -120 -KPX Ydieresis Aring -120 -KPX Ydieresis Atilde -120 -KPX Ydieresis O -30 -KPX Ydieresis Oacute -30 -KPX Ydieresis Ocircumflex -30 -KPX Ydieresis Odieresis -30 -KPX Ydieresis Ograve -30 -KPX Ydieresis Ohungarumlaut -30 -KPX Ydieresis Omacron -30 -KPX Ydieresis Oslash -30 -KPX Ydieresis Otilde -30 -KPX Ydieresis a -100 -KPX Ydieresis aacute -100 -KPX Ydieresis abreve -100 -KPX Ydieresis acircumflex -100 -KPX Ydieresis adieresis -60 -KPX Ydieresis agrave -60 -KPX Ydieresis amacron -60 -KPX Ydieresis aogonek -100 -KPX Ydieresis aring -100 -KPX Ydieresis atilde -100 -KPX Ydieresis colon -92 -KPX Ydieresis comma -129 -KPX Ydieresis e -100 -KPX Ydieresis eacute -100 -KPX Ydieresis ecaron -100 -KPX Ydieresis ecircumflex -100 -KPX Ydieresis edieresis -60 -KPX Ydieresis edotaccent -100 -KPX Ydieresis egrave -60 -KPX Ydieresis emacron -60 -KPX Ydieresis eogonek -100 -KPX Ydieresis hyphen -111 -KPX Ydieresis i -55 -KPX Ydieresis iacute -55 -KPX Ydieresis iogonek -55 -KPX Ydieresis o -110 -KPX Ydieresis oacute -110 -KPX Ydieresis ocircumflex -110 -KPX Ydieresis odieresis -70 -KPX Ydieresis ograve -70 -KPX Ydieresis ohungarumlaut -110 -KPX Ydieresis omacron -70 -KPX Ydieresis oslash -110 -KPX Ydieresis otilde -70 -KPX Ydieresis period -129 -KPX Ydieresis semicolon -92 -KPX Ydieresis u -111 -KPX Ydieresis uacute -111 -KPX Ydieresis ucircumflex -111 -KPX Ydieresis udieresis -71 -KPX Ydieresis ugrave -71 -KPX Ydieresis uhungarumlaut -111 -KPX Ydieresis umacron -71 -KPX Ydieresis uogonek -111 -KPX Ydieresis uring -111 -KPX a v -20 -KPX a w -15 -KPX aacute v -20 -KPX aacute w -15 -KPX abreve v -20 -KPX abreve w -15 -KPX acircumflex v -20 -KPX acircumflex w -15 -KPX adieresis v -20 -KPX adieresis w -15 -KPX agrave v -20 -KPX agrave w -15 -KPX amacron v -20 -KPX amacron w -15 -KPX aogonek v -20 -KPX aogonek w -15 -KPX aring v -20 -KPX aring w -15 -KPX atilde v -20 -KPX atilde w -15 -KPX b period -40 -KPX b u -20 -KPX b uacute -20 -KPX b ucircumflex -20 -KPX b udieresis -20 -KPX b ugrave -20 -KPX b uhungarumlaut -20 -KPX b umacron -20 -KPX b uogonek -20 -KPX b uring -20 -KPX b v -15 -KPX c y -15 -KPX c yacute -15 -KPX c ydieresis -15 -KPX cacute y -15 -KPX cacute yacute -15 -KPX cacute ydieresis -15 -KPX ccaron y -15 -KPX ccaron yacute -15 -KPX ccaron ydieresis -15 -KPX ccedilla y -15 -KPX ccedilla yacute -15 -KPX ccedilla ydieresis -15 -KPX comma quotedblright -70 -KPX comma quoteright -70 -KPX e g -15 -KPX e gbreve -15 -KPX e gcommaaccent -15 -KPX e v -25 -KPX e w -25 -KPX e x -15 -KPX e y -15 -KPX e yacute -15 -KPX e ydieresis -15 -KPX eacute g -15 -KPX eacute gbreve -15 -KPX eacute gcommaaccent -15 -KPX eacute v -25 -KPX eacute w -25 -KPX eacute x -15 -KPX eacute y -15 -KPX eacute yacute -15 -KPX eacute ydieresis -15 -KPX ecaron g -15 -KPX ecaron gbreve -15 -KPX ecaron gcommaaccent -15 -KPX ecaron v -25 -KPX ecaron w -25 -KPX ecaron x -15 -KPX ecaron y -15 -KPX ecaron yacute -15 -KPX ecaron ydieresis -15 -KPX ecircumflex g -15 -KPX ecircumflex gbreve -15 -KPX ecircumflex gcommaaccent -15 -KPX ecircumflex v -25 -KPX ecircumflex w -25 -KPX ecircumflex x -15 -KPX ecircumflex y -15 -KPX ecircumflex yacute -15 -KPX ecircumflex ydieresis -15 -KPX edieresis g -15 -KPX edieresis gbreve -15 -KPX edieresis gcommaaccent -15 -KPX edieresis v -25 -KPX edieresis w -25 -KPX edieresis x -15 -KPX edieresis y -15 -KPX edieresis yacute -15 -KPX edieresis ydieresis -15 -KPX edotaccent g -15 -KPX edotaccent gbreve -15 -KPX edotaccent gcommaaccent -15 -KPX edotaccent v -25 -KPX edotaccent w -25 -KPX edotaccent x -15 -KPX edotaccent y -15 -KPX edotaccent yacute -15 -KPX edotaccent ydieresis -15 -KPX egrave g -15 -KPX egrave gbreve -15 -KPX egrave gcommaaccent -15 -KPX egrave v -25 -KPX egrave w -25 -KPX egrave x -15 -KPX egrave y -15 -KPX egrave yacute -15 -KPX egrave ydieresis -15 -KPX emacron g -15 -KPX emacron gbreve -15 -KPX emacron gcommaaccent -15 -KPX emacron v -25 -KPX emacron w -25 -KPX emacron x -15 -KPX emacron y -15 -KPX emacron yacute -15 -KPX emacron ydieresis -15 -KPX eogonek g -15 -KPX eogonek gbreve -15 -KPX eogonek gcommaaccent -15 -KPX eogonek v -25 -KPX eogonek w -25 -KPX eogonek x -15 -KPX eogonek y -15 -KPX eogonek yacute -15 -KPX eogonek ydieresis -15 -KPX f a -10 -KPX f aacute -10 -KPX f abreve -10 -KPX f acircumflex -10 -KPX f adieresis -10 -KPX f agrave -10 -KPX f amacron -10 -KPX f aogonek -10 -KPX f aring -10 -KPX f atilde -10 -KPX f dotlessi -50 -KPX f f -25 -KPX f i -20 -KPX f iacute -20 -KPX f quoteright 55 -KPX g a -5 -KPX g aacute -5 -KPX g abreve -5 -KPX g acircumflex -5 -KPX g adieresis -5 -KPX g agrave -5 -KPX g amacron -5 -KPX g aogonek -5 -KPX g aring -5 -KPX g atilde -5 -KPX gbreve a -5 -KPX gbreve aacute -5 -KPX gbreve abreve -5 -KPX gbreve acircumflex -5 -KPX gbreve adieresis -5 -KPX gbreve agrave -5 -KPX gbreve amacron -5 -KPX gbreve aogonek -5 -KPX gbreve aring -5 -KPX gbreve atilde -5 -KPX gcommaaccent a -5 -KPX gcommaaccent aacute -5 -KPX gcommaaccent abreve -5 -KPX gcommaaccent acircumflex -5 -KPX gcommaaccent adieresis -5 -KPX gcommaaccent agrave -5 -KPX gcommaaccent amacron -5 -KPX gcommaaccent aogonek -5 -KPX gcommaaccent aring -5 -KPX gcommaaccent atilde -5 -KPX h y -5 -KPX h yacute -5 -KPX h ydieresis -5 -KPX i v -25 -KPX iacute v -25 -KPX icircumflex v -25 -KPX idieresis v -25 -KPX igrave v -25 -KPX imacron v -25 -KPX iogonek v -25 -KPX k e -10 -KPX k eacute -10 -KPX k ecaron -10 -KPX k ecircumflex -10 -KPX k edieresis -10 -KPX k edotaccent -10 -KPX k egrave -10 -KPX k emacron -10 -KPX k eogonek -10 -KPX k o -10 -KPX k oacute -10 -KPX k ocircumflex -10 -KPX k odieresis -10 -KPX k ograve -10 -KPX k ohungarumlaut -10 -KPX k omacron -10 -KPX k oslash -10 -KPX k otilde -10 -KPX k y -15 -KPX k yacute -15 -KPX k ydieresis -15 -KPX kcommaaccent e -10 -KPX kcommaaccent eacute -10 -KPX kcommaaccent ecaron -10 -KPX kcommaaccent ecircumflex -10 -KPX kcommaaccent edieresis -10 -KPX kcommaaccent edotaccent -10 -KPX kcommaaccent egrave -10 -KPX kcommaaccent emacron -10 -KPX kcommaaccent eogonek -10 -KPX kcommaaccent o -10 -KPX kcommaaccent oacute -10 -KPX kcommaaccent ocircumflex -10 -KPX kcommaaccent odieresis -10 -KPX kcommaaccent ograve -10 -KPX kcommaaccent ohungarumlaut -10 -KPX kcommaaccent omacron -10 -KPX kcommaaccent oslash -10 -KPX kcommaaccent otilde -10 -KPX kcommaaccent y -15 -KPX kcommaaccent yacute -15 -KPX kcommaaccent ydieresis -15 -KPX l w -10 -KPX lacute w -10 -KPX lcommaaccent w -10 -KPX lslash w -10 -KPX n v -40 -KPX n y -15 -KPX n yacute -15 -KPX n ydieresis -15 -KPX nacute v -40 -KPX nacute y -15 -KPX nacute yacute -15 -KPX nacute ydieresis -15 -KPX ncaron v -40 -KPX ncaron y -15 -KPX ncaron yacute -15 -KPX ncaron ydieresis -15 -KPX ncommaaccent v -40 -KPX ncommaaccent y -15 -KPX ncommaaccent yacute -15 -KPX ncommaaccent ydieresis -15 -KPX ntilde v -40 -KPX ntilde y -15 -KPX ntilde yacute -15 -KPX ntilde ydieresis -15 -KPX o v -15 -KPX o w -25 -KPX o y -10 -KPX o yacute -10 -KPX o ydieresis -10 -KPX oacute v -15 -KPX oacute w -25 -KPX oacute y -10 -KPX oacute yacute -10 -KPX oacute ydieresis -10 -KPX ocircumflex v -15 -KPX ocircumflex w -25 -KPX ocircumflex y -10 -KPX ocircumflex yacute -10 -KPX ocircumflex ydieresis -10 -KPX odieresis v -15 -KPX odieresis w -25 -KPX odieresis y -10 -KPX odieresis yacute -10 -KPX odieresis ydieresis -10 -KPX ograve v -15 -KPX ograve w -25 -KPX ograve y -10 -KPX ograve yacute -10 -KPX ograve ydieresis -10 -KPX ohungarumlaut v -15 -KPX ohungarumlaut w -25 -KPX ohungarumlaut y -10 -KPX ohungarumlaut yacute -10 -KPX ohungarumlaut ydieresis -10 -KPX omacron v -15 -KPX omacron w -25 -KPX omacron y -10 -KPX omacron yacute -10 -KPX omacron ydieresis -10 -KPX oslash v -15 -KPX oslash w -25 -KPX oslash y -10 -KPX oslash yacute -10 -KPX oslash ydieresis -10 -KPX otilde v -15 -KPX otilde w -25 -KPX otilde y -10 -KPX otilde yacute -10 -KPX otilde ydieresis -10 -KPX p y -10 -KPX p yacute -10 -KPX p ydieresis -10 -KPX period quotedblright -70 -KPX period quoteright -70 -KPX quotedblleft A -80 -KPX quotedblleft Aacute -80 -KPX quotedblleft Abreve -80 -KPX quotedblleft Acircumflex -80 -KPX quotedblleft Adieresis -80 -KPX quotedblleft Agrave -80 -KPX quotedblleft Amacron -80 -KPX quotedblleft Aogonek -80 -KPX quotedblleft Aring -80 -KPX quotedblleft Atilde -80 -KPX quoteleft A -80 -KPX quoteleft Aacute -80 -KPX quoteleft Abreve -80 -KPX quoteleft Acircumflex -80 -KPX quoteleft Adieresis -80 -KPX quoteleft Agrave -80 -KPX quoteleft Amacron -80 -KPX quoteleft Aogonek -80 -KPX quoteleft Aring -80 -KPX quoteleft Atilde -80 -KPX quoteleft quoteleft -74 -KPX quoteright d -50 -KPX quoteright dcroat -50 -KPX quoteright l -10 -KPX quoteright lacute -10 -KPX quoteright lcommaaccent -10 -KPX quoteright lslash -10 -KPX quoteright quoteright -74 -KPX quoteright r -50 -KPX quoteright racute -50 -KPX quoteright rcaron -50 -KPX quoteright rcommaaccent -50 -KPX quoteright s -55 -KPX quoteright sacute -55 -KPX quoteright scaron -55 -KPX quoteright scedilla -55 -KPX quoteright scommaaccent -55 -KPX quoteright space -74 -KPX quoteright t -18 -KPX quoteright tcommaaccent -18 -KPX quoteright v -50 -KPX r comma -40 -KPX r g -18 -KPX r gbreve -18 -KPX r gcommaaccent -18 -KPX r hyphen -20 -KPX r period -55 -KPX racute comma -40 -KPX racute g -18 -KPX racute gbreve -18 -KPX racute gcommaaccent -18 -KPX racute hyphen -20 -KPX racute period -55 -KPX rcaron comma -40 -KPX rcaron g -18 -KPX rcaron gbreve -18 -KPX rcaron gcommaaccent -18 -KPX rcaron hyphen -20 -KPX rcaron period -55 -KPX rcommaaccent comma -40 -KPX rcommaaccent g -18 -KPX rcommaaccent gbreve -18 -KPX rcommaaccent gcommaaccent -18 -KPX rcommaaccent hyphen -20 -KPX rcommaaccent period -55 -KPX space A -55 -KPX space Aacute -55 -KPX space Abreve -55 -KPX space Acircumflex -55 -KPX space Adieresis -55 -KPX space Agrave -55 -KPX space Amacron -55 -KPX space Aogonek -55 -KPX space Aring -55 -KPX space Atilde -55 -KPX space T -18 -KPX space Tcaron -18 -KPX space Tcommaaccent -18 -KPX space V -50 -KPX space W -30 -KPX space Y -90 -KPX space Yacute -90 -KPX space Ydieresis -90 -KPX v a -25 -KPX v aacute -25 -KPX v abreve -25 -KPX v acircumflex -25 -KPX v adieresis -25 -KPX v agrave -25 -KPX v amacron -25 -KPX v aogonek -25 -KPX v aring -25 -KPX v atilde -25 -KPX v comma -65 -KPX v e -15 -KPX v eacute -15 -KPX v ecaron -15 -KPX v ecircumflex -15 -KPX v edieresis -15 -KPX v edotaccent -15 -KPX v egrave -15 -KPX v emacron -15 -KPX v eogonek -15 -KPX v o -20 -KPX v oacute -20 -KPX v ocircumflex -20 -KPX v odieresis -20 -KPX v ograve -20 -KPX v ohungarumlaut -20 -KPX v omacron -20 -KPX v oslash -20 -KPX v otilde -20 -KPX v period -65 -KPX w a -10 -KPX w aacute -10 -KPX w abreve -10 -KPX w acircumflex -10 -KPX w adieresis -10 -KPX w agrave -10 -KPX w amacron -10 -KPX w aogonek -10 -KPX w aring -10 -KPX w atilde -10 -KPX w comma -65 -KPX w o -10 -KPX w oacute -10 -KPX w ocircumflex -10 -KPX w odieresis -10 -KPX w ograve -10 -KPX w ohungarumlaut -10 -KPX w omacron -10 -KPX w oslash -10 -KPX w otilde -10 -KPX w period -65 -KPX x e -15 -KPX x eacute -15 -KPX x ecaron -15 -KPX x ecircumflex -15 -KPX x edieresis -15 -KPX x edotaccent -15 -KPX x egrave -15 -KPX x emacron -15 -KPX x eogonek -15 -KPX y comma -65 -KPX y period -65 -KPX yacute comma -65 -KPX yacute period -65 -KPX ydieresis comma -65 -KPX ydieresis period -65 -EndKernPairs -EndKernData -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/ZapfDingbats.afm b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/ZapfDingbats.afm deleted file mode 100644 index b274505..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/ZapfDingbats.afm +++ /dev/null @@ -1,225 +0,0 @@ -StartFontMetrics 4.1 -Comment Copyright (c) 1985, 1987, 1988, 1989, 1997 Adobe Systems Incorporated. All Rights Reserved. -Comment Creation Date: Thu May 1 15:14:13 1997 -Comment UniqueID 43082 -Comment VMusage 45775 55535 -FontName ZapfDingbats -FullName ITC Zapf Dingbats -FamilyName ZapfDingbats -Weight Medium -ItalicAngle 0 -IsFixedPitch false -CharacterSet Special -FontBBox -1 -143 981 820 -UnderlinePosition -100 -UnderlineThickness 50 -Version 002.000 -Notice Copyright (c) 1985, 1987, 1988, 1989, 1997 Adobe Systems Incorporated. All Rights Reserved.ITC Zapf Dingbats is a registered trademark of International Typeface Corporation. -EncodingScheme FontSpecific -StdHW 28 -StdVW 90 -StartCharMetrics 202 -C 32 ; WX 278 ; N space ; B 0 0 0 0 ; -C 33 ; WX 974 ; N a1 ; B 35 72 939 621 ; -C 34 ; WX 961 ; N a2 ; B 35 81 927 611 ; -C 35 ; WX 974 ; N a202 ; B 35 72 939 621 ; -C 36 ; WX 980 ; N a3 ; B 35 0 945 692 ; -C 37 ; WX 719 ; N a4 ; B 34 139 685 566 ; -C 38 ; WX 789 ; N a5 ; B 35 -14 755 705 ; -C 39 ; WX 790 ; N a119 ; B 35 -14 755 705 ; -C 40 ; WX 791 ; N a118 ; B 35 -13 761 705 ; -C 41 ; WX 690 ; N a117 ; B 34 138 655 553 ; -C 42 ; WX 960 ; N a11 ; B 35 123 925 568 ; -C 43 ; WX 939 ; N a12 ; B 35 134 904 559 ; -C 44 ; WX 549 ; N a13 ; B 29 -11 516 705 ; -C 45 ; WX 855 ; N a14 ; B 34 59 820 632 ; -C 46 ; WX 911 ; N a15 ; B 35 50 876 642 ; -C 47 ; WX 933 ; N a16 ; B 35 139 899 550 ; -C 48 ; WX 911 ; N a105 ; B 35 50 876 642 ; -C 49 ; WX 945 ; N a17 ; B 35 139 909 553 ; -C 50 ; WX 974 ; N a18 ; B 35 104 938 587 ; -C 51 ; WX 755 ; N a19 ; B 34 -13 721 705 ; -C 52 ; WX 846 ; N a20 ; B 36 -14 811 705 ; -C 53 ; WX 762 ; N a21 ; B 35 0 727 692 ; -C 54 ; WX 761 ; N a22 ; B 35 0 727 692 ; -C 55 ; WX 571 ; N a23 ; B -1 -68 571 661 ; -C 56 ; WX 677 ; N a24 ; B 36 -13 642 705 ; -C 57 ; WX 763 ; N a25 ; B 35 0 728 692 ; -C 58 ; WX 760 ; N a26 ; B 35 0 726 692 ; -C 59 ; WX 759 ; N a27 ; B 35 0 725 692 ; -C 60 ; WX 754 ; N a28 ; B 35 0 720 692 ; -C 61 ; WX 494 ; N a6 ; B 35 0 460 692 ; -C 62 ; WX 552 ; N a7 ; B 35 0 517 692 ; -C 63 ; WX 537 ; N a8 ; B 35 0 503 692 ; -C 64 ; WX 577 ; N a9 ; B 35 96 542 596 ; -C 65 ; WX 692 ; N a10 ; B 35 -14 657 705 ; -C 66 ; WX 786 ; N a29 ; B 35 -14 751 705 ; -C 67 ; WX 788 ; N a30 ; B 35 -14 752 705 ; -C 68 ; WX 788 ; N a31 ; B 35 -14 753 705 ; -C 69 ; WX 790 ; N a32 ; B 35 -14 756 705 ; -C 70 ; WX 793 ; N a33 ; B 35 -13 759 705 ; -C 71 ; WX 794 ; N a34 ; B 35 -13 759 705 ; -C 72 ; WX 816 ; N a35 ; B 35 -14 782 705 ; -C 73 ; WX 823 ; N a36 ; B 35 -14 787 705 ; -C 74 ; WX 789 ; N a37 ; B 35 -14 754 705 ; -C 75 ; WX 841 ; N a38 ; B 35 -14 807 705 ; -C 76 ; WX 823 ; N a39 ; B 35 -14 789 705 ; -C 77 ; WX 833 ; N a40 ; B 35 -14 798 705 ; -C 78 ; WX 816 ; N a41 ; B 35 -13 782 705 ; -C 79 ; WX 831 ; N a42 ; B 35 -14 796 705 ; -C 80 ; WX 923 ; N a43 ; B 35 -14 888 705 ; -C 81 ; WX 744 ; N a44 ; B 35 0 710 692 ; -C 82 ; WX 723 ; N a45 ; B 35 0 688 692 ; -C 83 ; WX 749 ; N a46 ; B 35 0 714 692 ; -C 84 ; WX 790 ; N a47 ; B 34 -14 756 705 ; -C 85 ; WX 792 ; N a48 ; B 35 -14 758 705 ; -C 86 ; WX 695 ; N a49 ; B 35 -14 661 706 ; -C 87 ; WX 776 ; N a50 ; B 35 -6 741 699 ; -C 88 ; WX 768 ; N a51 ; B 35 -7 734 699 ; -C 89 ; WX 792 ; N a52 ; B 35 -14 757 705 ; -C 90 ; WX 759 ; N a53 ; B 35 0 725 692 ; -C 91 ; WX 707 ; N a54 ; B 35 -13 672 704 ; -C 92 ; WX 708 ; N a55 ; B 35 -14 672 705 ; -C 93 ; WX 682 ; N a56 ; B 35 -14 647 705 ; -C 94 ; WX 701 ; N a57 ; B 35 -14 666 705 ; -C 95 ; WX 826 ; N a58 ; B 35 -14 791 705 ; -C 96 ; WX 815 ; N a59 ; B 35 -14 780 705 ; -C 97 ; WX 789 ; N a60 ; B 35 -14 754 705 ; -C 98 ; WX 789 ; N a61 ; B 35 -14 754 705 ; -C 99 ; WX 707 ; N a62 ; B 34 -14 673 705 ; -C 100 ; WX 687 ; N a63 ; B 36 0 651 692 ; -C 101 ; WX 696 ; N a64 ; B 35 0 661 691 ; -C 102 ; WX 689 ; N a65 ; B 35 0 655 692 ; -C 103 ; WX 786 ; N a66 ; B 34 -14 751 705 ; -C 104 ; WX 787 ; N a67 ; B 35 -14 752 705 ; -C 105 ; WX 713 ; N a68 ; B 35 -14 678 705 ; -C 106 ; WX 791 ; N a69 ; B 35 -14 756 705 ; -C 107 ; WX 785 ; N a70 ; B 36 -14 751 705 ; -C 108 ; WX 791 ; N a71 ; B 35 -14 757 705 ; -C 109 ; WX 873 ; N a72 ; B 35 -14 838 705 ; -C 110 ; WX 761 ; N a73 ; B 35 0 726 692 ; -C 111 ; WX 762 ; N a74 ; B 35 0 727 692 ; -C 112 ; WX 762 ; N a203 ; B 35 0 727 692 ; -C 113 ; WX 759 ; N a75 ; B 35 0 725 692 ; -C 114 ; WX 759 ; N a204 ; B 35 0 725 692 ; -C 115 ; WX 892 ; N a76 ; B 35 0 858 705 ; -C 116 ; WX 892 ; N a77 ; B 35 -14 858 692 ; -C 117 ; WX 788 ; N a78 ; B 35 -14 754 705 ; -C 118 ; WX 784 ; N a79 ; B 35 -14 749 705 ; -C 119 ; WX 438 ; N a81 ; B 35 -14 403 705 ; -C 120 ; WX 138 ; N a82 ; B 35 0 104 692 ; -C 121 ; WX 277 ; N a83 ; B 35 0 242 692 ; -C 122 ; WX 415 ; N a84 ; B 35 0 380 692 ; -C 123 ; WX 392 ; N a97 ; B 35 263 357 705 ; -C 124 ; WX 392 ; N a98 ; B 34 263 357 705 ; -C 125 ; WX 668 ; N a99 ; B 35 263 633 705 ; -C 126 ; WX 668 ; N a100 ; B 36 263 634 705 ; -C 128 ; WX 390 ; N a89 ; B 35 -14 356 705 ; -C 129 ; WX 390 ; N a90 ; B 35 -14 355 705 ; -C 130 ; WX 317 ; N a93 ; B 35 0 283 692 ; -C 131 ; WX 317 ; N a94 ; B 35 0 283 692 ; -C 132 ; WX 276 ; N a91 ; B 35 0 242 692 ; -C 133 ; WX 276 ; N a92 ; B 35 0 242 692 ; -C 134 ; WX 509 ; N a205 ; B 35 0 475 692 ; -C 135 ; WX 509 ; N a85 ; B 35 0 475 692 ; -C 136 ; WX 410 ; N a206 ; B 35 0 375 692 ; -C 137 ; WX 410 ; N a86 ; B 35 0 375 692 ; -C 138 ; WX 234 ; N a87 ; B 35 -14 199 705 ; -C 139 ; WX 234 ; N a88 ; B 35 -14 199 705 ; -C 140 ; WX 334 ; N a95 ; B 35 0 299 692 ; -C 141 ; WX 334 ; N a96 ; B 35 0 299 692 ; -C 161 ; WX 732 ; N a101 ; B 35 -143 697 806 ; -C 162 ; WX 544 ; N a102 ; B 56 -14 488 706 ; -C 163 ; WX 544 ; N a103 ; B 34 -14 508 705 ; -C 164 ; WX 910 ; N a104 ; B 35 40 875 651 ; -C 165 ; WX 667 ; N a106 ; B 35 -14 633 705 ; -C 166 ; WX 760 ; N a107 ; B 35 -14 726 705 ; -C 167 ; WX 760 ; N a108 ; B 0 121 758 569 ; -C 168 ; WX 776 ; N a112 ; B 35 0 741 705 ; -C 169 ; WX 595 ; N a111 ; B 34 -14 560 705 ; -C 170 ; WX 694 ; N a110 ; B 35 -14 659 705 ; -C 171 ; WX 626 ; N a109 ; B 34 0 591 705 ; -C 172 ; WX 788 ; N a120 ; B 35 -14 754 705 ; -C 173 ; WX 788 ; N a121 ; B 35 -14 754 705 ; -C 174 ; WX 788 ; N a122 ; B 35 -14 754 705 ; -C 175 ; WX 788 ; N a123 ; B 35 -14 754 705 ; -C 176 ; WX 788 ; N a124 ; B 35 -14 754 705 ; -C 177 ; WX 788 ; N a125 ; B 35 -14 754 705 ; -C 178 ; WX 788 ; N a126 ; B 35 -14 754 705 ; -C 179 ; WX 788 ; N a127 ; B 35 -14 754 705 ; -C 180 ; WX 788 ; N a128 ; B 35 -14 754 705 ; -C 181 ; WX 788 ; N a129 ; B 35 -14 754 705 ; -C 182 ; WX 788 ; N a130 ; B 35 -14 754 705 ; -C 183 ; WX 788 ; N a131 ; B 35 -14 754 705 ; -C 184 ; WX 788 ; N a132 ; B 35 -14 754 705 ; -C 185 ; WX 788 ; N a133 ; B 35 -14 754 705 ; -C 186 ; WX 788 ; N a134 ; B 35 -14 754 705 ; -C 187 ; WX 788 ; N a135 ; B 35 -14 754 705 ; -C 188 ; WX 788 ; N a136 ; B 35 -14 754 705 ; -C 189 ; WX 788 ; N a137 ; B 35 -14 754 705 ; -C 190 ; WX 788 ; N a138 ; B 35 -14 754 705 ; -C 191 ; WX 788 ; N a139 ; B 35 -14 754 705 ; -C 192 ; WX 788 ; N a140 ; B 35 -14 754 705 ; -C 193 ; WX 788 ; N a141 ; B 35 -14 754 705 ; -C 194 ; WX 788 ; N a142 ; B 35 -14 754 705 ; -C 195 ; WX 788 ; N a143 ; B 35 -14 754 705 ; -C 196 ; WX 788 ; N a144 ; B 35 -14 754 705 ; -C 197 ; WX 788 ; N a145 ; B 35 -14 754 705 ; -C 198 ; WX 788 ; N a146 ; B 35 -14 754 705 ; -C 199 ; WX 788 ; N a147 ; B 35 -14 754 705 ; -C 200 ; WX 788 ; N a148 ; B 35 -14 754 705 ; -C 201 ; WX 788 ; N a149 ; B 35 -14 754 705 ; -C 202 ; WX 788 ; N a150 ; B 35 -14 754 705 ; -C 203 ; WX 788 ; N a151 ; B 35 -14 754 705 ; -C 204 ; WX 788 ; N a152 ; B 35 -14 754 705 ; -C 205 ; WX 788 ; N a153 ; B 35 -14 754 705 ; -C 206 ; WX 788 ; N a154 ; B 35 -14 754 705 ; -C 207 ; WX 788 ; N a155 ; B 35 -14 754 705 ; -C 208 ; WX 788 ; N a156 ; B 35 -14 754 705 ; -C 209 ; WX 788 ; N a157 ; B 35 -14 754 705 ; -C 210 ; WX 788 ; N a158 ; B 35 -14 754 705 ; -C 211 ; WX 788 ; N a159 ; B 35 -14 754 705 ; -C 212 ; WX 894 ; N a160 ; B 35 58 860 634 ; -C 213 ; WX 838 ; N a161 ; B 35 152 803 540 ; -C 214 ; WX 1016 ; N a163 ; B 34 152 981 540 ; -C 215 ; WX 458 ; N a164 ; B 35 -127 422 820 ; -C 216 ; WX 748 ; N a196 ; B 35 94 698 597 ; -C 217 ; WX 924 ; N a165 ; B 35 140 890 552 ; -C 218 ; WX 748 ; N a192 ; B 35 94 698 597 ; -C 219 ; WX 918 ; N a166 ; B 35 166 884 526 ; -C 220 ; WX 927 ; N a167 ; B 35 32 892 660 ; -C 221 ; WX 928 ; N a168 ; B 35 129 891 562 ; -C 222 ; WX 928 ; N a169 ; B 35 128 893 563 ; -C 223 ; WX 834 ; N a170 ; B 35 155 799 537 ; -C 224 ; WX 873 ; N a171 ; B 35 93 838 599 ; -C 225 ; WX 828 ; N a172 ; B 35 104 791 588 ; -C 226 ; WX 924 ; N a173 ; B 35 98 889 594 ; -C 227 ; WX 924 ; N a162 ; B 35 98 889 594 ; -C 228 ; WX 917 ; N a174 ; B 35 0 882 692 ; -C 229 ; WX 930 ; N a175 ; B 35 84 896 608 ; -C 230 ; WX 931 ; N a176 ; B 35 84 896 608 ; -C 231 ; WX 463 ; N a177 ; B 35 -99 429 791 ; -C 232 ; WX 883 ; N a178 ; B 35 71 848 623 ; -C 233 ; WX 836 ; N a179 ; B 35 44 802 648 ; -C 234 ; WX 836 ; N a193 ; B 35 44 802 648 ; -C 235 ; WX 867 ; N a180 ; B 35 101 832 591 ; -C 236 ; WX 867 ; N a199 ; B 35 101 832 591 ; -C 237 ; WX 696 ; N a181 ; B 35 44 661 648 ; -C 238 ; WX 696 ; N a200 ; B 35 44 661 648 ; -C 239 ; WX 874 ; N a182 ; B 35 77 840 619 ; -C 241 ; WX 874 ; N a201 ; B 35 73 840 615 ; -C 242 ; WX 760 ; N a183 ; B 35 0 725 692 ; -C 243 ; WX 946 ; N a184 ; B 35 160 911 533 ; -C 244 ; WX 771 ; N a197 ; B 34 37 736 655 ; -C 245 ; WX 865 ; N a185 ; B 35 207 830 481 ; -C 246 ; WX 771 ; N a194 ; B 34 37 736 655 ; -C 247 ; WX 888 ; N a198 ; B 34 -19 853 712 ; -C 248 ; WX 967 ; N a186 ; B 35 124 932 568 ; -C 249 ; WX 888 ; N a195 ; B 34 -19 853 712 ; -C 250 ; WX 831 ; N a187 ; B 35 113 796 579 ; -C 251 ; WX 873 ; N a188 ; B 36 118 838 578 ; -C 252 ; WX 927 ; N a189 ; B 35 150 891 542 ; -C 253 ; WX 970 ; N a190 ; B 35 76 931 616 ; -C 254 ; WX 918 ; N a191 ; B 34 99 884 593 ; -EndCharMetrics -EndFontMetrics diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/readme.txt b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/readme.txt deleted file mode 100644 index 047ae70..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts/readme.txt +++ /dev/null @@ -1,15 +0,0 @@ -Font Metrics for the 14 PDF Core Fonts -====================================== - -This directory contains font metrics for the 14 PDF Core Fonts, -downloaded from Adobe. The title and this paragraph were added by -Matplotlib developers. The download URL was -. - -This file and the 14 PostScript(R) AFM files it accompanies may be used, copied, -and distributed for any purpose and without charge, with or without modification, -provided that all copyright notices are retained; that the AFM files are not -distributed without this file; that all modifications to this file or any of -the AFM files are prominently noted in the modified file(s); and that this -paragraph is not modified. Adobe Systems has no responsibility or obligation -to support the use of the AFM files. diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSans-Bold.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSans-Bold.ttf deleted file mode 100644 index 1f22f07..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSans-Bold.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSans-BoldOblique.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSans-BoldOblique.ttf deleted file mode 100644 index b8886cb..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSans-BoldOblique.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSans-Oblique.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSans-Oblique.ttf deleted file mode 100644 index 300ea68..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSans-Oblique.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSans.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSans.ttf deleted file mode 100644 index 5267218..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSans.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansDisplay.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansDisplay.ttf deleted file mode 100644 index 36758a2..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansDisplay.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono-Bold.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono-Bold.ttf deleted file mode 100644 index cbcdd31..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono-Bold.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono-BoldOblique.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono-BoldOblique.ttf deleted file mode 100644 index da51344..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono-BoldOblique.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono-Oblique.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono-Oblique.ttf deleted file mode 100644 index 0185ce9..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono-Oblique.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono.ttf deleted file mode 100644 index 278cd78..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSansMono.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerif-Bold.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerif-Bold.ttf deleted file mode 100644 index d683eb2..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerif-Bold.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerif-BoldItalic.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerif-BoldItalic.ttf deleted file mode 100644 index b4831f7..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerif-BoldItalic.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerif-Italic.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerif-Italic.ttf deleted file mode 100644 index 45b508b..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerif-Italic.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerif.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerif.ttf deleted file mode 100644 index 39dd394..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerif.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerifDisplay.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerifDisplay.ttf deleted file mode 100644 index 1623714..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/DejaVuSerifDisplay.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/LICENSE_DEJAVU b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/LICENSE_DEJAVU deleted file mode 100644 index 254e2cc..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/LICENSE_DEJAVU +++ /dev/null @@ -1,99 +0,0 @@ -Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. -Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) - -Bitstream Vera Fonts Copyright ------------------------------- - -Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is -a trademark of Bitstream, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of the fonts accompanying this license ("Fonts") and associated -documentation files (the "Font Software"), to reproduce and distribute the -Font Software, including without limitation the rights to use, copy, merge, -publish, distribute, and/or sell copies of the Font Software, and to permit -persons to whom the Font Software is furnished to do so, subject to the -following conditions: - -The above copyright and trademark notices and this permission notice shall -be included in all copies of one or more of the Font Software typefaces. - -The Font Software may be modified, altered, or added to, and in particular -the designs of glyphs or characters in the Fonts may be modified and -additional glyphs or characters may be added to the Fonts, only if the fonts -are renamed to names not containing either the words "Bitstream" or the word -"Vera". - -This License becomes null and void to the extent applicable to Fonts or Font -Software that has been modified and is distributed under the "Bitstream -Vera" names. - -The Font Software may be sold as part of a larger software package but no -copy of one or more of the Font Software typefaces may be sold by itself. - -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, -TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME -FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING -ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF -THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE -FONT SOFTWARE. - -Except as contained in this notice, the names of Gnome, the Gnome -Foundation, and Bitstream Inc., shall not be used in advertising or -otherwise to promote the sale, use or other dealings in this Font Software -without prior written authorization from the Gnome Foundation or Bitstream -Inc., respectively. For further information, contact: fonts at gnome dot -org. - -Arev Fonts Copyright ------------------------------- - -Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of the fonts accompanying this license ("Fonts") and -associated documentation files (the "Font Software"), to reproduce -and distribute the modifications to the Bitstream Vera Font Software, -including without limitation the rights to use, copy, merge, publish, -distribute, and/or sell copies of the Font Software, and to permit -persons to whom the Font Software is furnished to do so, subject to -the following conditions: - -The above copyright and trademark notices and this permission notice -shall be included in all copies of one or more of the Font Software -typefaces. - -The Font Software may be modified, altered, or added to, and in -particular the designs of glyphs or characters in the Fonts may be -modified and additional glyphs or characters may be added to the -Fonts, only if the fonts are renamed to names not containing either -the words "Tavmjong Bah" or the word "Arev". - -This License becomes null and void to the extent applicable to Fonts -or Font Software that has been modified and is distributed under the -"Tavmjong Bah Arev" names. - -The Font Software may be sold as part of a larger software package but -no copy of one or more of the Font Software typefaces may be sold by -itself. - -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL -TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. - -Except as contained in this notice, the name of Tavmjong Bah shall not -be used in advertising or otherwise to promote the sale, use or other -dealings in this Font Software without prior written authorization -from Tavmjong Bah. For further information, contact: tavmjong @ free -. fr. - -$Id: LICENSE 2133 2007-11-28 02:46:28Z lechimp $ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/LICENSE_STIX b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/LICENSE_STIX deleted file mode 100644 index 6034d94..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/LICENSE_STIX +++ /dev/null @@ -1,124 +0,0 @@ -The STIX fonts distributed with matplotlib have been modified from -their canonical form. They have been converted from OTF to TTF format -using Fontforge and this script: - - #!/usr/bin/env fontforge - i=1 - while ( i<$argc ) - Open($argv[i]) - Generate($argv[i]:r + ".ttf") - i = i+1 - endloop - -The original STIX Font License begins below. - ------------------------------------------------------------ - -STIX Font License - -24 May 2010 - -Copyright (c) 2001-2010 by the STI Pub Companies, consisting of the American -Institute of Physics, the American Chemical Society, the American Mathematical -Society, the American Physical Society, Elsevier, Inc., and The Institute of -Electrical and Electronic Engineers, Inc. (www.stixfonts.org), with Reserved -Font Name STIX Fonts, STIX Fonts (TM) is a trademark of The Institute of -Electrical and Electronics Engineers, Inc. - -Portions copyright (c) 1998-2003 by MicroPress, Inc. (www.micropress-inc.com), -with Reserved Font Name TM Math. To obtain additional mathematical fonts, please -contact MicroPress, Inc., 68-30 Harrow Street, Forest Hills, NY 11375, USA, -Phone: (718) 575-1816. - -Portions copyright (c) 1990 by Elsevier, Inc. - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -https://scripts.sil.org/OFL - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXGeneral.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXGeneral.ttf deleted file mode 100644 index 8699400..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXGeneral.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXGeneralBol.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXGeneralBol.ttf deleted file mode 100644 index ba80b53..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXGeneralBol.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXGeneralBolIta.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXGeneralBolIta.ttf deleted file mode 100644 index 5957dab..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXGeneralBolIta.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXGeneralItalic.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXGeneralItalic.ttf deleted file mode 100644 index 2830b6b..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXGeneralItalic.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXNonUni.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXNonUni.ttf deleted file mode 100644 index a70c797..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXNonUni.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXNonUniBol.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXNonUniBol.ttf deleted file mode 100644 index ccbd9a6..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXNonUniBol.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXNonUniBolIta.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXNonUniBolIta.ttf deleted file mode 100644 index e75e09e..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXNonUniBolIta.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXNonUniIta.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXNonUniIta.ttf deleted file mode 100644 index c27d42f..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXNonUniIta.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizFiveSymReg.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizFiveSymReg.ttf deleted file mode 100644 index f81717e..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizFiveSymReg.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizFourSymBol.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizFourSymBol.ttf deleted file mode 100644 index 855dec9..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizFourSymBol.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizFourSymReg.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizFourSymReg.ttf deleted file mode 100644 index f955ca2..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizFourSymReg.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizOneSymBol.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizOneSymBol.ttf deleted file mode 100644 index 6ffd357..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizOneSymBol.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizOneSymReg.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizOneSymReg.ttf deleted file mode 100644 index 01ed101..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizOneSymReg.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizThreeSymBol.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizThreeSymBol.ttf deleted file mode 100644 index 1ccf365..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizThreeSymBol.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizThreeSymReg.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizThreeSymReg.ttf deleted file mode 100644 index bf2b66a..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizThreeSymReg.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizTwoSymBol.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizTwoSymBol.ttf deleted file mode 100644 index bd5f93f..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizTwoSymBol.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizTwoSymReg.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizTwoSymReg.ttf deleted file mode 100644 index 73b9930..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/STIXSizTwoSymReg.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmb10.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmb10.ttf deleted file mode 100644 index 189bdf0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmb10.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmex10.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmex10.ttf deleted file mode 100644 index e4b468d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmex10.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmmi10.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmmi10.ttf deleted file mode 100644 index 8d32661..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmmi10.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmr10.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmr10.ttf deleted file mode 100644 index 8bc4496..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmr10.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmss10.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmss10.ttf deleted file mode 100644 index ef70532..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmss10.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmsy10.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmsy10.ttf deleted file mode 100644 index 45d8421..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmsy10.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmtt10.ttf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmtt10.ttf deleted file mode 100644 index 15bdb68..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf/cmtt10.ttf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/back-symbolic.svg b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/back-symbolic.svg deleted file mode 100644 index a933ef8..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/back-symbolic.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/back.pdf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/back.pdf deleted file mode 100644 index 79709d8..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/back.pdf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/back.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/back.png deleted file mode 100644 index e3c4b58..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/back.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/back.svg b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/back.svg deleted file mode 100644 index a933ef8..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/back.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/back_large.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/back_large.png deleted file mode 100644 index e44a70a..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/back_large.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/filesave-symbolic.svg b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/filesave-symbolic.svg deleted file mode 100644 index ad8372d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/filesave-symbolic.svg +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/filesave.pdf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/filesave.pdf deleted file mode 100644 index 794a115..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/filesave.pdf +++ /dev/null @@ -1,70 +0,0 @@ -%PDF-1.4 -%¬Ü «º -1 0 obj -<< /Pages 2 0 R /Type /Catalog >> -endobj -8 0 obj -<< /ExtGState 4 0 R /Font 3 0 R /Pattern 5 0 R -/ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /Shading 6 0 R -/XObject 7 0 R >> -endobj -10 0 obj -<< /Annots [ ] /Contents 9 0 R -/Group << /CS /DeviceRGB /S /Transparency /Type /Group >> -/MediaBox [ 0 0 72 72 ] /Parent 2 0 R /Resources 8 0 R /Type /Page >> -endobj -9 0 obj -<< /Filter /FlateDecode /Length 11 0 R >> -stream -xÚu”Kn1 D÷:…N@‹©Ï2A€Ùç - ljÑÉÆ@|ý”¤Ö§£ÛoÄb‘"‡í«yúÄöåÍ:ûŠ¿wËöaŸ¾<ÿþq<}|¶Ç›qà?MòxœõÏ Øßùß^]UõÝÏ$>zµìȧœ2“y¡,ZßN£Ž4¹ÿÓ_ªþ«:™äb;¸P&—š¤Ê®§)”˜k¬2¥r#‡ñæ†3yëû¢§ë™bc—“Ã,¶ÕÁÈËÍ«+òc*aT»1&'ÍÞ´ -Z¸šê•-pñðÞ"=¹\Â-X´œ ÆÿazÝ«D’¼IT´™ób%ãû\Õ5Qr½ÿZˆ¥uzõ(ŠT?PTÅùˆRJwåï@®–f Ûüˆ£¬­4„JÁôlãWÅA|Z¨šÓ-AðfäHêEÞ‚ðŒm 6ÒLft"†Vë¹Sô+÷È®¾šº\,Áiøv!³²íRfý#ÉF¦™½‹º2–oƒáív#æ§÷ `:lô¤±%ÅN»vÇLEÚµ¡Âз]J=ÒÎö«ÆHôÑ‹a,Ý5c `¿ªf=¿ài0W¥·`5E?Öu!7†x¢2vk’t þÔÖ±Ó‚®ÅBa1HÒëwgÀÕ½!¿H¦ð3û͘?»\ª -endstream -endobj -11 0 obj -500 -endobj -3 0 obj -<< >> -endobj -4 0 obj -<< /A1 << /CA 0 /Type /ExtGState /ca 0 >> -/A2 << /CA 1 /Type /ExtGState /ca 1 >> >> -endobj -5 0 obj -<< >> -endobj -6 0 obj -<< >> -endobj -7 0 obj -<< >> -endobj -2 0 obj -<< /Count 1 /Kids [ 10 0 R ] /Type /Pages >> -endobj -12 0 obj -<< /CreationDate (D:20160601123507-04'00') -/Creator (matplotlib 1.5.2rc2.post1933.dev0+gb775a26, http://matplotlib.org) -/Producer (matplotlib pdf backend) >> -endobj -xref -0 13 -0000000000 65535 f -0000000016 00000 n -0000001161 00000 n -0000000978 00000 n -0000000999 00000 n -0000001098 00000 n -0000001119 00000 n -0000001140 00000 n -0000000065 00000 n -0000000383 00000 n -0000000208 00000 n -0000000958 00000 n -0000001221 00000 n -trailer -<< /Info 12 0 R /Root 1 0 R /Size 13 >> -startxref -1395 -%%EOF diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/filesave.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/filesave.png deleted file mode 100644 index 919e40b..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/filesave.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/filesave.svg b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/filesave.svg deleted file mode 100644 index ad8372d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/filesave.svg +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/filesave_large.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/filesave_large.png deleted file mode 100644 index a39b55a..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/filesave_large.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/forward-symbolic.svg b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/forward-symbolic.svg deleted file mode 100644 index 1f40713..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/forward-symbolic.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/forward.pdf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/forward.pdf deleted file mode 100644 index ce4a1bc..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/forward.pdf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/forward.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/forward.png deleted file mode 100644 index 59400fe..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/forward.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/forward.svg b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/forward.svg deleted file mode 100644 index 1f40713..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/forward.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/forward_large.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/forward_large.png deleted file mode 100644 index de65815..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/forward_large.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/hand.pdf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/hand.pdf deleted file mode 100644 index 1308816..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/hand.pdf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/hand.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/hand.png deleted file mode 100644 index d956c5f..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/hand.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/hand.svg b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/hand.svg deleted file mode 100644 index f246f51..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/hand.svg +++ /dev/null @@ -1,130 +0,0 @@ - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/help-symbolic.svg b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/help-symbolic.svg deleted file mode 100644 index 484bdbc..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/help-symbolic.svg +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/help.pdf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/help.pdf deleted file mode 100644 index 38178d0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/help.pdf +++ /dev/null @@ -1,68 +0,0 @@ -%PDF-1.4 -%¬Ü «º -1 0 obj -<< /Pages 2 0 R /Type /Catalog >> -endobj -8 0 obj -<< /ExtGState 4 0 R /Font 3 0 R /Pattern 5 0 R -/ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /Shading 6 0 R -/XObject 7 0 R >> -endobj -10 0 obj -<< /Annots [ ] /Contents 9 0 R -/Group << /CS /DeviceRGB /S /Transparency /Type /Group >> -/MediaBox [ 0 0 72 72 ] /Parent 2 0 R /Resources 8 0 R /Type /Page >> -endobj -9 0 obj -<< /Filter /FlateDecode /Length 11 0 R >> -stream -xÚm“ÍŽS1 …÷yŠûžØÎ³!Íž-bU†ªÌf$x}ŽôÞ^„Ô*Õ×8>>¶y»¦§¼½¾oy»âûgû²}Åùmãíy{úôòûçååóóÇíòž2ø¯ÔÇÍ|n€qüHé /É|éÑ×ý¥Œ—ÀR*Jb\›nÜ(—¢½âÁ5sX„Z’;áGL½ ›è’÷j;Ý»%e2éÆz@ ÖÁ÷'´´\ÒA µû{;Ëc¬à•¢Rá‰v);º¤Côoé(n‡‡{ŠG4•\`r­$=´¨ò‡,(8#¶÷ˆ­JÚT¿ž‹µ¡ÚLší¥PÕ‘]\¦6œd‚9Æâ’lD X–xÍeò¼Å•vï†Ã@&Y=vÙrX:„4צ¸TOzsª¥xZE¹5æÙ"cœÞ3B\‘óœi!ruãnzͲš]²xK&©6»¨ê ™C‰º×ÁX(×0i/V+5É蜣ÁÕ©FÊ0 Ùó-Á<îU9²eYó›M©´Q©±£Î"Ñ}žBå™’mùãÂpê½¶Vγïî­àƒ!XØþÙbf9/„0†T"¸µ$÷ŒbŒÍŽbëé^ìC±Òìz¹*Œ!½¬…¸3^£?}Y–æp«ø‡WRñKbú\ÁŒðò®]Ô˜m^ñÃ~1Õ¹‰ÿÙ9_Åô=¥¿ñb« -endstream -endobj -11 0 obj -549 -endobj -3 0 obj -<< >> -endobj -4 0 obj -<< /A1 << /CA 0 /Type /ExtGState /ca 0 >> -/A2 << /CA 1 /Type /ExtGState /ca 1 >> >> -endobj -5 0 obj -<< >> -endobj -6 0 obj -<< >> -endobj -7 0 obj -<< >> -endobj -2 0 obj -<< /Count 1 /Kids [ 10 0 R ] /Type /Pages >> -endobj -12 0 obj -<< /CreationDate (D:20170812144045-04'00') -/Creator (matplotlib 2.0.2.post4623.dev0+gcb3aea0db, http://matplotlib.org) -/Producer (matplotlib pdf backend 2.0.2.post4623.dev0+gcb3aea0db) >> -endobj -xref -0 13 -0000000000 65535 f -0000000016 00000 n -0000001210 00000 n -0000001027 00000 n -0000001048 00000 n -0000001147 00000 n -0000001168 00000 n -0000001189 00000 n -0000000065 00000 n -0000000383 00000 n -0000000208 00000 n -0000001007 00000 n -0000001270 00000 n -trailer -<< /Info 12 0 R /Root 1 0 R /Size 13 >> -startxref -1474 -%%EOF diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/help.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/help.png deleted file mode 100644 index a52fbbe..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/help.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/help.svg b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/help.svg deleted file mode 100644 index 484bdbc..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/help.svg +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/help_large.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/help_large.png deleted file mode 100644 index 3f3d4df..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/help_large.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/home-symbolic.svg b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/home-symbolic.svg deleted file mode 100644 index 3c4ccce..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/home-symbolic.svg +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/home.pdf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/home.pdf deleted file mode 100644 index f9c6439..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/home.pdf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/home.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/home.png deleted file mode 100644 index 6e5fdeb..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/home.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/home.svg b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/home.svg deleted file mode 100644 index 3c4ccce..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/home.svg +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/home_large.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/home_large.png deleted file mode 100644 index 3357bfe..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/home_large.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/matplotlib.pdf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/matplotlib.pdf deleted file mode 100644 index 6c44566..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/matplotlib.pdf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/matplotlib.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/matplotlib.png deleted file mode 100644 index 8eedfa7..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/matplotlib.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/matplotlib.svg b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/matplotlib.svg deleted file mode 100644 index 95d1b61..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/matplotlib.svg +++ /dev/null @@ -1,3171 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/matplotlib_large.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/matplotlib_large.png deleted file mode 100644 index c7dcfe6..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/matplotlib_large.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/move-symbolic.svg b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/move-symbolic.svg deleted file mode 100644 index aa7198c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/move-symbolic.svg +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/move.pdf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/move.pdf deleted file mode 100644 index d883d92..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/move.pdf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/move.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/move.png deleted file mode 100644 index 4fbbaef..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/move.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/move.svg b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/move.svg deleted file mode 100644 index aa7198c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/move.svg +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/move_large.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/move_large.png deleted file mode 100644 index 96351c1..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/move_large.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/qt4_editor_options.pdf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/qt4_editor_options.pdf deleted file mode 100644 index a92f2cc..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/qt4_editor_options.pdf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/qt4_editor_options.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/qt4_editor_options.png deleted file mode 100644 index 792ec81..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/qt4_editor_options.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/qt4_editor_options.svg b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/qt4_editor_options.svg deleted file mode 100644 index 0b46bf8..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/qt4_editor_options.svg +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/qt4_editor_options_large.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/qt4_editor_options_large.png deleted file mode 100644 index 46d52c9..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/qt4_editor_options_large.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/subplots-symbolic.svg b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/subplots-symbolic.svg deleted file mode 100644 index e87d2c9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/subplots-symbolic.svg +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/subplots.pdf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/subplots.pdf deleted file mode 100644 index f404665..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/subplots.pdf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/subplots.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/subplots.png deleted file mode 100644 index bb0318c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/subplots.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/subplots.svg b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/subplots.svg deleted file mode 100644 index e87d2c9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/subplots.svg +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/subplots_large.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/subplots_large.png deleted file mode 100644 index 4440af1..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/subplots_large.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/zoom_to_rect-symbolic.svg b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/zoom_to_rect-symbolic.svg deleted file mode 100644 index f4b69b2..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/zoom_to_rect-symbolic.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/zoom_to_rect.pdf b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/zoom_to_rect.pdf deleted file mode 100644 index 22add33..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/zoom_to_rect.pdf +++ /dev/null @@ -1,68 +0,0 @@ -%PDF-1.4 -%¬Ü «º -1 0 obj -<< /Pages 2 0 R /Type /Catalog >> -endobj -8 0 obj -<< /ExtGState 4 0 R /Font 3 0 R /Pattern 5 0 R -/ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] /Shading 6 0 R -/XObject 7 0 R >> -endobj -10 0 obj -<< /Annots [ ] /Contents 9 0 R -/Group << /CS /DeviceRGB /S /Transparency /Type /Group >> -/MediaBox [ 0 0 72 72 ] /Parent 2 0 R /Resources 8 0 R /Type /Page >> -endobj -9 0 obj -<< /Filter /FlateDecode /Length 11 0 R >> -stream -xÚmRKn1 Ûë>b},ÛËÞ¾W$iƒ×n´×/m÷H€Ø $’’,éž¾Hz}O9½áÿ›$ÝÒÓ·ç??çï·¯éx§ üUÅq¾;Àyü ú ] ¯¨,·q'òÆâ¹[MÖÙ¥Gk º ™³7·–> -endobj -4 0 obj -<< /A1 << /CA 0 /Type /ExtGState /ca 0 >> -/A2 << /CA 1 /Type /ExtGState /ca 1 >> >> -endobj -5 0 obj -<< >> -endobj -6 0 obj -<< >> -endobj -7 0 obj -<< >> -endobj -2 0 obj -<< /Count 1 /Kids [ 10 0 R ] /Type /Pages >> -endobj -12 0 obj -<< /CreationDate (D:20160601123507-04'00') -/Creator (matplotlib 1.5.2rc2.post1933.dev0+gb775a26, http://matplotlib.org) -/Producer (matplotlib pdf backend) >> -endobj -xref -0 13 -0000000000 65535 f -0000000016 00000 n -0000001036 00000 n -0000000853 00000 n -0000000874 00000 n -0000000973 00000 n -0000000994 00000 n -0000001015 00000 n -0000000065 00000 n -0000000383 00000 n -0000000208 00000 n -0000000833 00000 n -0000001096 00000 n -trailer -<< /Info 12 0 R /Root 1 0 R /Size 13 >> -startxref -1270 -%%EOF diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/zoom_to_rect.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/zoom_to_rect.png deleted file mode 100644 index 12afa25..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/zoom_to_rect.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/zoom_to_rect.svg b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/zoom_to_rect.svg deleted file mode 100644 index f4b69b2..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/zoom_to_rect.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/zoom_to_rect_large.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/zoom_to_rect_large.png deleted file mode 100644 index 5963603..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/images/zoom_to_rect_large.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/kpsewhich.lua b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/kpsewhich.lua deleted file mode 100644 index 8e9172a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/kpsewhich.lua +++ /dev/null @@ -1,3 +0,0 @@ --- see dviread._LuatexKpsewhich -kpse.set_program_name("latex") -while true do print(kpse.lookup(io.read():gsub("\r", ""))); io.flush(); end diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/matplotlibrc b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/matplotlibrc deleted file mode 100644 index bf3aab7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/matplotlibrc +++ /dev/null @@ -1,788 +0,0 @@ -#### MATPLOTLIBRC FORMAT - -## NOTE FOR END USERS: DO NOT EDIT THIS FILE! -## -## This is a sample Matplotlib configuration file - you can find a copy -## of it on your system in site-packages/matplotlib/mpl-data/matplotlibrc -## (relative to your Python installation location). -## DO NOT EDIT IT! -## -## If you wish to change your default style, copy this file to one of the -## following locations: -## Unix/Linux: -## $HOME/.config/matplotlib/matplotlibrc OR -## $XDG_CONFIG_HOME/matplotlib/matplotlibrc (if $XDG_CONFIG_HOME is set) -## Other platforms: -## $HOME/.matplotlib/matplotlibrc -## and edit that copy. -## -## See https://matplotlib.org/stable/tutorials/introductory/customizing.html#customizing-with-matplotlibrc-files -## for more details on the paths which are checked for the configuration file. -## -## Blank lines, or lines starting with a comment symbol, are ignored, as are -## trailing comments. Other lines must have the format: -## key: val # optional comment -## -## Formatting: Use PEP8-like style (as enforced in the rest of the codebase). -## All lines start with an additional '#', so that removing all leading '#'s -## yields a valid style file. -## -## Colors: for the color values below, you can either use -## - a Matplotlib color string, such as r, k, or b -## - an RGB tuple, such as (1.0, 0.5, 0.0) -## - a double-quoted hex string, such as "#ff00ff". -## The unquoted string ff00ff is also supported for backward -## compatibility, but is discouraged. -## - a scalar grayscale intensity such as 0.75 -## - a legal html color name, e.g., red, blue, darkslategray -## -## String values may optionally be enclosed in double quotes, which allows -## using the comment character # in the string. -## -## This file (and other style files) must be encoded as utf-8. -## -## Matplotlib configuration are currently divided into following parts: -## - BACKENDS -## - LINES -## - PATCHES -## - HATCHES -## - BOXPLOT -## - FONT -## - TEXT -## - LaTeX -## - AXES -## - DATES -## - TICKS -## - GRIDS -## - LEGEND -## - FIGURE -## - IMAGES -## - CONTOUR PLOTS -## - ERRORBAR PLOTS -## - HISTOGRAM PLOTS -## - SCATTER PLOTS -## - AGG RENDERING -## - PATHS -## - SAVING FIGURES -## - INTERACTIVE KEYMAPS -## - ANIMATION - -##### CONFIGURATION BEGINS HERE - - -## *************************************************************************** -## * BACKENDS * -## *************************************************************************** -## The default backend. If you omit this parameter, the first working -## backend from the following list is used: -## MacOSX QtAgg Gtk4Agg Gtk3Agg TkAgg WxAgg Agg -## Other choices include: -## QtCairo GTK4Cairo GTK3Cairo TkCairo WxCairo Cairo -## Qt5Agg Qt5Cairo Wx # deprecated. -## PS PDF SVG Template -## You can also deploy your own backend outside of Matplotlib by referring to -## the module name (which must be in the PYTHONPATH) as 'module://my_backend'. -##backend: Agg - -## The port to use for the web server in the WebAgg backend. -#webagg.port: 8988 - -## The address on which the WebAgg web server should be reachable -#webagg.address: 127.0.0.1 - -## If webagg.port is unavailable, a number of other random ports will -## be tried until one that is available is found. -#webagg.port_retries: 50 - -## When True, open the web browser to the plot that is shown -#webagg.open_in_browser: True - -## If you are running pyplot inside a GUI and your backend choice -## conflicts, we will automatically try to find a compatible one for -## you if backend_fallback is True -#backend_fallback: True - -#interactive: False -#figure.hooks: # list of dotted.module.name:dotted.callable.name -#toolbar: toolbar2 # {None, toolbar2, toolmanager} -#timezone: UTC # a pytz timezone string, e.g., US/Central or Europe/Paris - - -## *************************************************************************** -## * LINES * -## *************************************************************************** -## See https://matplotlib.org/stable/api/artist_api.html#module-matplotlib.lines -## for more information on line properties. -#lines.linewidth: 1.5 # line width in points -#lines.linestyle: - # solid line -#lines.color: C0 # has no affect on plot(); see axes.prop_cycle -#lines.marker: None # the default marker -#lines.markerfacecolor: auto # the default marker face color -#lines.markeredgecolor: auto # the default marker edge color -#lines.markeredgewidth: 1.0 # the line width around the marker symbol -#lines.markersize: 6 # marker size, in points -#lines.dash_joinstyle: round # {miter, round, bevel} -#lines.dash_capstyle: butt # {butt, round, projecting} -#lines.solid_joinstyle: round # {miter, round, bevel} -#lines.solid_capstyle: projecting # {butt, round, projecting} -#lines.antialiased: True # render lines in antialiased (no jaggies) - -## The three standard dash patterns. These are scaled by the linewidth. -#lines.dashed_pattern: 3.7, 1.6 -#lines.dashdot_pattern: 6.4, 1.6, 1, 1.6 -#lines.dotted_pattern: 1, 1.65 -#lines.scale_dashes: True - -#markers.fillstyle: full # {full, left, right, bottom, top, none} - -#pcolor.shading: auto -#pcolormesh.snap: True # Whether to snap the mesh to pixel boundaries. This is - # provided solely to allow old test images to remain - # unchanged. Set to False to obtain the previous behavior. - -## *************************************************************************** -## * PATCHES * -## *************************************************************************** -## Patches are graphical objects that fill 2D space, like polygons or circles. -## See https://matplotlib.org/stable/api/artist_api.html#module-matplotlib.patches -## for more information on patch properties. -#patch.linewidth: 1.0 # edge width in points. -#patch.facecolor: C0 -#patch.edgecolor: black # if forced, or patch is not filled -#patch.force_edgecolor: False # True to always use edgecolor -#patch.antialiased: True # render patches in antialiased (no jaggies) - - -## *************************************************************************** -## * HATCHES * -## *************************************************************************** -#hatch.color: black -#hatch.linewidth: 1.0 - - -## *************************************************************************** -## * BOXPLOT * -## *************************************************************************** -#boxplot.notch: False -#boxplot.vertical: True -#boxplot.whiskers: 1.5 -#boxplot.bootstrap: None -#boxplot.patchartist: False -#boxplot.showmeans: False -#boxplot.showcaps: True -#boxplot.showbox: True -#boxplot.showfliers: True -#boxplot.meanline: False - -#boxplot.flierprops.color: black -#boxplot.flierprops.marker: o -#boxplot.flierprops.markerfacecolor: none -#boxplot.flierprops.markeredgecolor: black -#boxplot.flierprops.markeredgewidth: 1.0 -#boxplot.flierprops.markersize: 6 -#boxplot.flierprops.linestyle: none -#boxplot.flierprops.linewidth: 1.0 - -#boxplot.boxprops.color: black -#boxplot.boxprops.linewidth: 1.0 -#boxplot.boxprops.linestyle: - - -#boxplot.whiskerprops.color: black -#boxplot.whiskerprops.linewidth: 1.0 -#boxplot.whiskerprops.linestyle: - - -#boxplot.capprops.color: black -#boxplot.capprops.linewidth: 1.0 -#boxplot.capprops.linestyle: - - -#boxplot.medianprops.color: C1 -#boxplot.medianprops.linewidth: 1.0 -#boxplot.medianprops.linestyle: - - -#boxplot.meanprops.color: C2 -#boxplot.meanprops.marker: ^ -#boxplot.meanprops.markerfacecolor: C2 -#boxplot.meanprops.markeredgecolor: C2 -#boxplot.meanprops.markersize: 6 -#boxplot.meanprops.linestyle: -- -#boxplot.meanprops.linewidth: 1.0 - - -## *************************************************************************** -## * FONT * -## *************************************************************************** -## The font properties used by `text.Text`. -## See https://matplotlib.org/stable/api/font_manager_api.html for more information -## on font properties. The 6 font properties used for font matching are -## given below with their default values. -## -## The font.family property can take either a single or multiple entries of any -## combination of concrete font names (not supported when rendering text with -## usetex) or the following five generic values: -## - 'serif' (e.g., Times), -## - 'sans-serif' (e.g., Helvetica), -## - 'cursive' (e.g., Zapf-Chancery), -## - 'fantasy' (e.g., Western), and -## - 'monospace' (e.g., Courier). -## Each of these values has a corresponding default list of font names -## (font.serif, etc.); the first available font in the list is used. Note that -## for font.serif, font.sans-serif, and font.monospace, the first element of -## the list (a DejaVu font) will always be used because DejaVu is shipped with -## Matplotlib and is thus guaranteed to be available; the other entries are -## left as examples of other possible values. -## -## The font.style property has three values: normal (or roman), italic -## or oblique. The oblique style will be used for italic, if it is not -## present. -## -## The font.variant property has two values: normal or small-caps. For -## TrueType fonts, which are scalable fonts, small-caps is equivalent -## to using a font size of 'smaller', or about 83 % of the current font -## size. -## -## The font.weight property has effectively 13 values: normal, bold, -## bolder, lighter, 100, 200, 300, ..., 900. Normal is the same as -## 400, and bold is 700. bolder and lighter are relative values with -## respect to the current weight. -## -## The font.stretch property has 11 values: ultra-condensed, -## extra-condensed, condensed, semi-condensed, normal, semi-expanded, -## expanded, extra-expanded, ultra-expanded, wider, and narrower. This -## property is not currently implemented. -## -## The font.size property is the default font size for text, given in points. -## 10 pt is the standard value. -## -## Note that font.size controls default text sizes. To configure -## special text sizes tick labels, axes, labels, title, etc., see the rc -## settings for axes and ticks. Special text sizes can be defined -## relative to font.size, using the following values: xx-small, x-small, -## small, medium, large, x-large, xx-large, larger, or smaller - -#font.family: sans-serif -#font.style: normal -#font.variant: normal -#font.weight: normal -#font.stretch: normal -#font.size: 10.0 - -#font.serif: DejaVu Serif, Bitstream Vera Serif, Computer Modern Roman, New Century Schoolbook, Century Schoolbook L, Utopia, ITC Bookman, Bookman, Nimbus Roman No9 L, Times New Roman, Times, Palatino, Charter, serif -#font.sans-serif: DejaVu Sans, Bitstream Vera Sans, Computer Modern Sans Serif, Lucida Grande, Verdana, Geneva, Lucid, Arial, Helvetica, Avant Garde, sans-serif -#font.cursive: Apple Chancery, Textile, Zapf Chancery, Sand, Script MT, Felipa, Comic Neue, Comic Sans MS, cursive -#font.fantasy: Chicago, Charcoal, Impact, Western, Humor Sans, xkcd, fantasy -#font.monospace: DejaVu Sans Mono, Bitstream Vera Sans Mono, Computer Modern Typewriter, Andale Mono, Nimbus Mono L, Courier New, Courier, Fixed, Terminal, monospace - - -## *************************************************************************** -## * TEXT * -## *************************************************************************** -## The text properties used by `text.Text`. -## See https://matplotlib.org/stable/api/artist_api.html#module-matplotlib.text -## for more information on text properties -#text.color: black - -## FreeType hinting flag ("foo" corresponds to FT_LOAD_FOO); may be one of the -## following (Proprietary Matplotlib-specific synonyms are given in parentheses, -## but their use is discouraged): -## - default: Use the font's native hinter if possible, else FreeType's auto-hinter. -## ("either" is a synonym). -## - no_autohint: Use the font's native hinter if possible, else don't hint. -## ("native" is a synonym.) -## - force_autohint: Use FreeType's auto-hinter. ("auto" is a synonym.) -## - no_hinting: Disable hinting. ("none" is a synonym.) -#text.hinting: force_autohint - -#text.hinting_factor: 8 # Specifies the amount of softness for hinting in the - # horizontal direction. A value of 1 will hint to full - # pixels. A value of 2 will hint to half pixels etc. -#text.kerning_factor: 0 # Specifies the scaling factor for kerning values. This - # is provided solely to allow old test images to remain - # unchanged. Set to 6 to obtain previous behavior. - # Values other than 0 or 6 have no defined meaning. -#text.antialiased: True # If True (default), the text will be antialiased. - # This only affects raster outputs. -#text.parse_math: True # Use mathtext if there is an even number of unescaped - # dollar signs. - - -## *************************************************************************** -## * LaTeX * -## *************************************************************************** -## For more information on LaTeX properties, see -## https://matplotlib.org/stable/tutorials/text/usetex.html -#text.usetex: False # use latex for all text handling. The following fonts - # are supported through the usual rc parameter settings: - # new century schoolbook, bookman, times, palatino, - # zapf chancery, charter, serif, sans-serif, helvetica, - # avant garde, courier, monospace, computer modern roman, - # computer modern sans serif, computer modern typewriter -#text.latex.preamble: # IMPROPER USE OF THIS FEATURE WILL LEAD TO LATEX FAILURES - # AND IS THEREFORE UNSUPPORTED. PLEASE DO NOT ASK FOR HELP - # IF THIS FEATURE DOES NOT DO WHAT YOU EXPECT IT TO. - # text.latex.preamble is a single line of LaTeX code that - # will be passed on to the LaTeX system. It may contain - # any code that is valid for the LaTeX "preamble", i.e. - # between the "\documentclass" and "\begin{document}" - # statements. - # Note that it has to be put on a single line, which may - # become quite long. - # The following packages are always loaded with usetex, - # so beware of package collisions: - # geometry, inputenc, type1cm. - # PostScript (PSNFSS) font packages may also be - # loaded, depending on your font settings. - -## The following settings allow you to select the fonts in math mode. -#mathtext.fontset: dejavusans # Should be 'dejavusans' (default), - # 'dejavuserif', 'cm' (Computer Modern), 'stix', - # 'stixsans' or 'custom' -## "mathtext.fontset: custom" is defined by the mathtext.bf, .cal, .it, ... -## settings which map a TeX font name to a fontconfig font pattern. (These -## settings are not used for other font sets.) -#mathtext.bf: sans:bold -#mathtext.cal: cursive -#mathtext.it: sans:italic -#mathtext.rm: sans -#mathtext.sf: sans -#mathtext.tt: monospace -#mathtext.fallback: cm # Select fallback font from ['cm' (Computer Modern), 'stix' - # 'stixsans'] when a symbol can not be found in one of the - # custom math fonts. Select 'None' to not perform fallback - # and replace the missing character by a dummy symbol. -#mathtext.default: it # The default font to use for math. - # Can be any of the LaTeX font names, including - # the special name "regular" for the same font - # used in regular text. - - -## *************************************************************************** -## * AXES * -## *************************************************************************** -## Following are default face and edge colors, default tick sizes, -## default font sizes for tick labels, and so on. See -## https://matplotlib.org/stable/api/axes_api.html#module-matplotlib.axes -#axes.facecolor: white # axes background color -#axes.edgecolor: black # axes edge color -#axes.linewidth: 0.8 # edge line width -#axes.grid: False # display grid or not -#axes.grid.axis: both # which axis the grid should apply to -#axes.grid.which: major # grid lines at {major, minor, both} ticks -#axes.titlelocation: center # alignment of the title: {left, right, center} -#axes.titlesize: large # font size of the axes title -#axes.titleweight: normal # font weight of title -#axes.titlecolor: auto # color of the axes title, auto falls back to - # text.color as default value -#axes.titley: None # position title (axes relative units). None implies auto -#axes.titlepad: 6.0 # pad between axes and title in points -#axes.labelsize: medium # font size of the x and y labels -#axes.labelpad: 4.0 # space between label and axis -#axes.labelweight: normal # weight of the x and y labels -#axes.labelcolor: black -#axes.axisbelow: line # draw axis gridlines and ticks: - # - below patches (True) - # - above patches but below lines ('line') - # - above all (False) - -#axes.formatter.limits: -5, 6 # use scientific notation if log10 - # of the axis range is smaller than the - # first or larger than the second -#axes.formatter.use_locale: False # When True, format tick labels - # according to the user's locale. - # For example, use ',' as a decimal - # separator in the fr_FR locale. -#axes.formatter.use_mathtext: False # When True, use mathtext for scientific - # notation. -#axes.formatter.min_exponent: 0 # minimum exponent to format in scientific notation -#axes.formatter.useoffset: True # If True, the tick label formatter - # will default to labeling ticks relative - # to an offset when the data range is - # small compared to the minimum absolute - # value of the data. -#axes.formatter.offset_threshold: 4 # When useoffset is True, the offset - # will be used when it can remove - # at least this number of significant - # digits from tick labels. - -#axes.spines.left: True # display axis spines -#axes.spines.bottom: True -#axes.spines.top: True -#axes.spines.right: True - -#axes.unicode_minus: True # use Unicode for the minus symbol rather than hyphen. See - # https://en.wikipedia.org/wiki/Plus_and_minus_signs#Character_codes -#axes.prop_cycle: cycler('color', ['1f77b4', 'ff7f0e', '2ca02c', 'd62728', '9467bd', '8c564b', 'e377c2', '7f7f7f', 'bcbd22', '17becf']) - # color cycle for plot lines as list of string color specs: - # single letter, long name, or web-style hex - # As opposed to all other parameters in this file, the color - # values must be enclosed in quotes for this parameter, - # e.g. '1f77b4', instead of 1f77b4. - # See also https://matplotlib.org/stable/tutorials/intermediate/color_cycle.html - # for more details on prop_cycle usage. -#axes.xmargin: .05 # x margin. See `axes.Axes.margins` -#axes.ymargin: .05 # y margin. See `axes.Axes.margins` -#axes.zmargin: .05 # z margin. See `axes.Axes.margins` -#axes.autolimit_mode: data # If "data", use axes.xmargin and axes.ymargin as is. - # If "round_numbers", after application of margins, axis - # limits are further expanded to the nearest "round" number. -#polaraxes.grid: True # display grid on polar axes -#axes3d.grid: True # display grid on 3D axes - -#axes3d.xaxis.panecolor: (0.95, 0.95, 0.95, 0.5) # background pane on 3D axes -#axes3d.yaxis.panecolor: (0.90, 0.90, 0.90, 0.5) # background pane on 3D axes -#axes3d.zaxis.panecolor: (0.925, 0.925, 0.925, 0.5) # background pane on 3D axes - -## *************************************************************************** -## * AXIS * -## *************************************************************************** -#xaxis.labellocation: center # alignment of the xaxis label: {left, right, center} -#yaxis.labellocation: center # alignment of the yaxis label: {bottom, top, center} - - -## *************************************************************************** -## * DATES * -## *************************************************************************** -## These control the default format strings used in AutoDateFormatter. -## Any valid format datetime format string can be used (see the python -## `datetime` for details). For example, by using: -## - '%x' will use the locale date representation -## - '%X' will use the locale time representation -## - '%c' will use the full locale datetime representation -## These values map to the scales: -## {'year': 365, 'month': 30, 'day': 1, 'hour': 1/24, 'minute': 1 / (24 * 60)} - -#date.autoformatter.year: %Y -#date.autoformatter.month: %Y-%m -#date.autoformatter.day: %Y-%m-%d -#date.autoformatter.hour: %m-%d %H -#date.autoformatter.minute: %d %H:%M -#date.autoformatter.second: %H:%M:%S -#date.autoformatter.microsecond: %M:%S.%f -## The reference date for Matplotlib's internal date representation -## See https://matplotlib.org/stable/gallery/ticks/date_precision_and_epochs.html -#date.epoch: 1970-01-01T00:00:00 -## 'auto', 'concise': -#date.converter: auto -## For auto converter whether to use interval_multiples: -#date.interval_multiples: True - -## *************************************************************************** -## * TICKS * -## *************************************************************************** -## See https://matplotlib.org/stable/api/axis_api.html#matplotlib.axis.Tick -#xtick.top: False # draw ticks on the top side -#xtick.bottom: True # draw ticks on the bottom side -#xtick.labeltop: False # draw label on the top -#xtick.labelbottom: True # draw label on the bottom -#xtick.major.size: 3.5 # major tick size in points -#xtick.minor.size: 2 # minor tick size in points -#xtick.major.width: 0.8 # major tick width in points -#xtick.minor.width: 0.6 # minor tick width in points -#xtick.major.pad: 3.5 # distance to major tick label in points -#xtick.minor.pad: 3.4 # distance to the minor tick label in points -#xtick.color: black # color of the ticks -#xtick.labelcolor: inherit # color of the tick labels or inherit from xtick.color -#xtick.labelsize: medium # font size of the tick labels -#xtick.direction: out # direction: {in, out, inout} -#xtick.minor.visible: False # visibility of minor ticks on x-axis -#xtick.major.top: True # draw x axis top major ticks -#xtick.major.bottom: True # draw x axis bottom major ticks -#xtick.minor.top: True # draw x axis top minor ticks -#xtick.minor.bottom: True # draw x axis bottom minor ticks -#xtick.alignment: center # alignment of xticks - -#ytick.left: True # draw ticks on the left side -#ytick.right: False # draw ticks on the right side -#ytick.labelleft: True # draw tick labels on the left side -#ytick.labelright: False # draw tick labels on the right side -#ytick.major.size: 3.5 # major tick size in points -#ytick.minor.size: 2 # minor tick size in points -#ytick.major.width: 0.8 # major tick width in points -#ytick.minor.width: 0.6 # minor tick width in points -#ytick.major.pad: 3.5 # distance to major tick label in points -#ytick.minor.pad: 3.4 # distance to the minor tick label in points -#ytick.color: black # color of the ticks -#ytick.labelcolor: inherit # color of the tick labels or inherit from ytick.color -#ytick.labelsize: medium # font size of the tick labels -#ytick.direction: out # direction: {in, out, inout} -#ytick.minor.visible: False # visibility of minor ticks on y-axis -#ytick.major.left: True # draw y axis left major ticks -#ytick.major.right: True # draw y axis right major ticks -#ytick.minor.left: True # draw y axis left minor ticks -#ytick.minor.right: True # draw y axis right minor ticks -#ytick.alignment: center_baseline # alignment of yticks - - -## *************************************************************************** -## * GRIDS * -## *************************************************************************** -#grid.color: "#b0b0b0" # grid color -#grid.linestyle: - # solid -#grid.linewidth: 0.8 # in points -#grid.alpha: 1.0 # transparency, between 0.0 and 1.0 - - -## *************************************************************************** -## * LEGEND * -## *************************************************************************** -#legend.loc: best -#legend.frameon: True # if True, draw the legend on a background patch -#legend.framealpha: 0.8 # legend patch transparency -#legend.facecolor: inherit # inherit from axes.facecolor; or color spec -#legend.edgecolor: 0.8 # background patch boundary color -#legend.fancybox: True # if True, use a rounded box for the - # legend background, else a rectangle -#legend.shadow: False # if True, give background a shadow effect -#legend.numpoints: 1 # the number of marker points in the legend line -#legend.scatterpoints: 1 # number of scatter points -#legend.markerscale: 1.0 # the relative size of legend markers vs. original -#legend.fontsize: medium -#legend.labelcolor: None -#legend.title_fontsize: None # None sets to the same as the default axes. - -## Dimensions as fraction of font size: -#legend.borderpad: 0.4 # border whitespace -#legend.labelspacing: 0.5 # the vertical space between the legend entries -#legend.handlelength: 2.0 # the length of the legend lines -#legend.handleheight: 0.7 # the height of the legend handle -#legend.handletextpad: 0.8 # the space between the legend line and legend text -#legend.borderaxespad: 0.5 # the border between the axes and legend edge -#legend.columnspacing: 2.0 # column separation - - -## *************************************************************************** -## * FIGURE * -## *************************************************************************** -## See https://matplotlib.org/stable/api/figure_api.html#matplotlib.figure.Figure -#figure.titlesize: large # size of the figure title (``Figure.suptitle()``) -#figure.titleweight: normal # weight of the figure title -#figure.labelsize: large # size of the figure label (``Figure.sup[x|y]label()``) -#figure.labelweight: normal # weight of the figure label -#figure.figsize: 6.4, 4.8 # figure size in inches -#figure.dpi: 100 # figure dots per inch -#figure.facecolor: white # figure face color -#figure.edgecolor: white # figure edge color -#figure.frameon: True # enable figure frame -#figure.max_open_warning: 20 # The maximum number of figures to open through - # the pyplot interface before emitting a warning. - # If less than one this feature is disabled. -#figure.raise_window : True # Raise the GUI window to front when show() is called. - -## The figure subplot parameters. All dimensions are a fraction of the figure width and height. -#figure.subplot.left: 0.125 # the left side of the subplots of the figure -#figure.subplot.right: 0.9 # the right side of the subplots of the figure -#figure.subplot.bottom: 0.11 # the bottom of the subplots of the figure -#figure.subplot.top: 0.88 # the top of the subplots of the figure -#figure.subplot.wspace: 0.2 # the amount of width reserved for space between subplots, - # expressed as a fraction of the average axis width -#figure.subplot.hspace: 0.2 # the amount of height reserved for space between subplots, - # expressed as a fraction of the average axis height - -## Figure layout -#figure.autolayout: False # When True, automatically adjust subplot - # parameters to make the plot fit the figure - # using `tight_layout` -#figure.constrained_layout.use: False # When True, automatically make plot - # elements fit on the figure. (Not - # compatible with `autolayout`, above). -#figure.constrained_layout.h_pad: 0.04167 # Padding around axes objects. Float representing -#figure.constrained_layout.w_pad: 0.04167 # inches. Default is 3/72 inches (3 points) -#figure.constrained_layout.hspace: 0.02 # Space between subplot groups. Float representing -#figure.constrained_layout.wspace: 0.02 # a fraction of the subplot widths being separated. - - -## *************************************************************************** -## * IMAGES * -## *************************************************************************** -#image.aspect: equal # {equal, auto} or a number -#image.interpolation: antialiased # see help(imshow) for options -#image.cmap: viridis # A colormap name (plasma, magma, etc.) -#image.lut: 256 # the size of the colormap lookup table -#image.origin: upper # {lower, upper} -#image.resample: True -#image.composite_image: True # When True, all the images on a set of axes are - # combined into a single composite image before - # saving a figure as a vector graphics file, - # such as a PDF. - - -## *************************************************************************** -## * CONTOUR PLOTS * -## *************************************************************************** -#contour.negative_linestyle: dashed # string or on-off ink sequence -#contour.corner_mask: True # {True, False} -#contour.linewidth: None # {float, None} Size of the contour line - # widths. If set to None, it falls back to - # `line.linewidth`. -#contour.algorithm: mpl2014 # {mpl2005, mpl2014, serial, threaded} - - -## *************************************************************************** -## * ERRORBAR PLOTS * -## *************************************************************************** -#errorbar.capsize: 0 # length of end cap on error bars in pixels - - -## *************************************************************************** -## * HISTOGRAM PLOTS * -## *************************************************************************** -#hist.bins: 10 # The default number of histogram bins or 'auto'. - - -## *************************************************************************** -## * SCATTER PLOTS * -## *************************************************************************** -#scatter.marker: o # The default marker type for scatter plots. -#scatter.edgecolors: face # The default edge colors for scatter plots. - - -## *************************************************************************** -## * AGG RENDERING * -## *************************************************************************** -## Warning: experimental, 2008/10/10 -#agg.path.chunksize: 0 # 0 to disable; values in the range - # 10000 to 100000 can improve speed slightly - # and prevent an Agg rendering failure - # when plotting very large data sets, - # especially if they are very gappy. - # It may cause minor artifacts, though. - # A value of 20000 is probably a good - # starting point. - - -## *************************************************************************** -## * PATHS * -## *************************************************************************** -#path.simplify: True # When True, simplify paths by removing "invisible" - # points to reduce file size and increase rendering - # speed -#path.simplify_threshold: 0.111111111111 # The threshold of similarity below - # which vertices will be removed in - # the simplification process. -#path.snap: True # When True, rectilinear axis-aligned paths will be snapped - # to the nearest pixel when certain criteria are met. - # When False, paths will never be snapped. -#path.sketch: None # May be None, or a 3-tuple of the form: - # (scale, length, randomness). - # - *scale* is the amplitude of the wiggle - # perpendicular to the line (in pixels). - # - *length* is the length of the wiggle along the - # line (in pixels). - # - *randomness* is the factor by which the length is - # randomly scaled. -#path.effects: - - -## *************************************************************************** -## * SAVING FIGURES * -## *************************************************************************** -## The default savefig parameters can be different from the display parameters -## e.g., you may want a higher resolution, or to make the figure -## background white -#savefig.dpi: figure # figure dots per inch or 'figure' -#savefig.facecolor: auto # figure face color when saving -#savefig.edgecolor: auto # figure edge color when saving -#savefig.format: png # {png, ps, pdf, svg} -#savefig.bbox: standard # {tight, standard} - # 'tight' is incompatible with pipe-based animation - # backends (e.g. 'ffmpeg') but will work with those - # based on temporary files (e.g. 'ffmpeg_file') -#savefig.pad_inches: 0.1 # padding to be used, when bbox is set to 'tight' -#savefig.directory: ~ # default directory in savefig dialog, gets updated after - # interactive saves, unless set to the empty string (i.e. - # the current directory); use '.' to start at the current - # directory but update after interactive saves -#savefig.transparent: False # whether figures are saved with a transparent - # background by default -#savefig.orientation: portrait # orientation of saved figure, for PostScript output only - -### tk backend params -#tk.window_focus: False # Maintain shell focus for TkAgg - -### ps backend params -#ps.papersize: letter # {auto, letter, legal, ledger, A0-A10, B0-B10} -#ps.useafm: False # use of AFM fonts, results in small files -#ps.usedistiller: False # {ghostscript, xpdf, None} - # Experimental: may produce smaller files. - # xpdf intended for production of publication quality files, - # but requires ghostscript, xpdf and ps2eps -#ps.distiller.res: 6000 # dpi -#ps.fonttype: 3 # Output Type 3 (Type3) or Type 42 (TrueType) - -### PDF backend params -#pdf.compression: 6 # integer from 0 to 9 - # 0 disables compression (good for debugging) -#pdf.fonttype: 3 # Output Type 3 (Type3) or Type 42 (TrueType) -#pdf.use14corefonts: False -#pdf.inheritcolor: False - -### SVG backend params -#svg.image_inline: True # Write raster image data directly into the SVG file -#svg.fonttype: path # How to handle SVG fonts: - # path: Embed characters as paths -- supported - # by most SVG renderers - # None: Assume fonts are installed on the - # machine where the SVG will be viewed. -#svg.hashsalt: None # If not None, use this string as hash salt instead of uuid4 - -### pgf parameter -## See https://matplotlib.org/stable/tutorials/text/pgf.html for more information. -#pgf.rcfonts: True -#pgf.preamble: # See text.latex.preamble for documentation -#pgf.texsystem: xelatex - -### docstring params -#docstring.hardcopy: False # set this when you want to generate hardcopy docstring - - -## *************************************************************************** -## * INTERACTIVE KEYMAPS * -## *************************************************************************** -## Event keys to interact with figures/plots via keyboard. -## See https://matplotlib.org/stable/users/explain/interactive.html for more -## details on interactive navigation. Customize these settings according to -## your needs. Leave the field(s) empty if you don't need a key-map. (i.e., -## fullscreen : '') -#keymap.fullscreen: f, ctrl+f # toggling -#keymap.home: h, r, home # home or reset mnemonic -#keymap.back: left, c, backspace, MouseButton.BACK # forward / backward keys -#keymap.forward: right, v, MouseButton.FORWARD # for quick navigation -#keymap.pan: p # pan mnemonic -#keymap.zoom: o # zoom mnemonic -#keymap.save: s, ctrl+s # saving current figure -#keymap.help: f1 # display help about active tools -#keymap.quit: ctrl+w, cmd+w, q # close the current figure -#keymap.quit_all: # close all figures -#keymap.grid: g # switching on/off major grids in current axes -#keymap.grid_minor: G # switching on/off minor grids in current axes -#keymap.yscale: l # toggle scaling of y-axes ('log'/'linear') -#keymap.xscale: k, L # toggle scaling of x-axes ('log'/'linear') -#keymap.copy: ctrl+c, cmd+c # copy figure to clipboard - - -## *************************************************************************** -## * ANIMATION * -## *************************************************************************** -#animation.html: none # How to display the animation as HTML in - # the IPython notebook: - # - 'html5' uses HTML5 video tag - # - 'jshtml' creates a JavaScript animation -#animation.writer: ffmpeg # MovieWriter 'backend' to use -#animation.codec: h264 # Codec to use for writing movie -#animation.bitrate: -1 # Controls size/quality trade-off for movie. - # -1 implies let utility auto-determine -#animation.frame_format: png # Controls frame format used by temp files - -## Path to ffmpeg binary. Unqualified paths are resolved by subprocess.Popen. -#animation.ffmpeg_path: ffmpeg -## Additional arguments to pass to ffmpeg. -#animation.ffmpeg_args: - -## Path to ImageMagick's convert binary. Unqualified paths are resolved by -## subprocess.Popen, except that on Windows, we look up an install of -## ImageMagick in the registry (as convert is also the name of a system tool). -#animation.convert_path: convert -## Additional arguments to pass to convert. -#animation.convert_args: -layers, OptimizePlus -# -#animation.embed_limit: 20.0 # Limit, in MB, of size of base64 encoded - # animation in HTML (i.e. IPython notebook) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/plot_directive/plot_directive.css b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/plot_directive/plot_directive.css deleted file mode 100644 index d45593c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/plot_directive/plot_directive.css +++ /dev/null @@ -1,16 +0,0 @@ -/* - * plot_directive.css - * ~~~~~~~~~~~~ - * - * Stylesheet controlling images created using the `plot` directive within - * Sphinx. - * - * :copyright: Copyright 2020-* by the Matplotlib development team. - * :license: Matplotlib, see LICENSE for details. - * - */ - -img.plot-directive { - border: 0; - max-width: 100%; -} diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/Minduka_Present_Blue_Pack.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/Minduka_Present_Blue_Pack.png deleted file mode 100644 index 2b1aff4..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/Minduka_Present_Blue_Pack.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/README.txt b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/README.txt deleted file mode 100644 index 75a5349..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -This is the sample data needed for some of matplotlib's examples and -docs. See matplotlib.cbook.get_sample_data for more info. diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/Stocks.csv b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/Stocks.csv deleted file mode 100644 index 575d353..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/Stocks.csv +++ /dev/null @@ -1,526 +0,0 @@ -# Data source: https://finance.yahoo.com -Date,IBM,AAPL,MSFT,XRX,AMZN,DELL,GOOGL,ADBE,^GSPC,^IXIC -1990-01-01,10.970438003540039,0.24251236021518707,0.40375930070877075,11.202081680297852,,,,1.379060983657837,329.0799865722656,415.79998779296875 -1990-02-01,11.554415702819824,0.24251236021518707,0.43104037642478943,10.39472484588623,,,,1.7790844440460205,331.8900146484375,425.79998779296875 -1990-02-05,,,,,,,,,, -1990-03-01,11.951693534851074,0.28801724314689636,0.4834197461605072,11.394058227539062,,,,2.2348830699920654,339.94000244140625,435.5 -1990-04-01,12.275476455688477,0.2817564308643341,0.5063362717628479,10.139430046081543,,,,2.2531447410583496,330.79998779296875,420.1000061035156 -1990-05-01,13.514284133911133,0.295173317193985,0.6372847557067871,9.704153060913086,,,,2.0985164642333984,361.2300109863281,459.0 -1990-05-04,,,,,,,,,, -1990-06-01,13.380948066711426,0.3211067020893097,0.6634747385978699,9.749448776245117,,,,2.164785146713257,358.0199890136719,462.29998779296875 -1990-07-01,12.697657585144043,0.3013734817504883,0.5805402994155884,9.489465713500977,,,,2.069063901901245,356.1499938964844,438.20001220703125 -1990-08-01,11.601561546325684,0.26549553871154785,0.5368908047676086,8.553519248962402,,,,1.4750508069992065,322.55999755859375,381.20001220703125 -1990-08-06,,,,,,,,,, -1990-09-01,12.251119613647461,0.20872052013874054,0.5499854683876038,7.255113124847412,,,,1.1210384368896484,306.04998779296875,344.5 -1990-10-01,12.15034294128418,0.22131578624248505,0.5565334558486938,6.248927116394043,,,,1.416249394416809,304.0,329.79998779296875 -1990-11-01,13.08609390258789,0.26449888944625854,0.6307373642921448,7.361023426055908,,,,1.4900128841400146,322.2200012207031,359.1000061035156 -1990-11-05,,,,,,,,,, -1990-12-01,13.161062240600586,0.31051647663116455,0.6569272875785828,7.519896507263184,,,,1.7186784744262695,330.2200012207031,373.79998779296875 -1991-01-01,14.762505531311035,0.40078282356262207,0.8566243648529053,10.554410934448242,,,,2.242394208908081,343.92999267578125,414.20001220703125 -1991-02-01,14.995450973510742,0.4134202301502228,0.9057300686836243,12.286416053771973,,,,2.8402483463287354,367.07000732421875,453.1000061035156 -1991-02-04,,,,,,,,,, -1991-03-01,13.39067268371582,0.49208223819732666,0.92646324634552,12.511940002441406,,,,3.1869795322418213,375.2200012207031,482.29998779296875 -1991-04-01,12.111870765686035,0.39800751209259033,0.8642628788948059,12.511940002441406,,,,3.0589873790740967,375.3399963378906,484.7200012207031 -1991-05-01,12.479341506958008,0.34011581540107727,0.9581103920936584,12.73144817352295,,,,2.9850986003875732,389.8299865722656,506.1099853515625 -1991-05-06,,,,,,,,,, -1991-06-01,11.555957794189453,0.30108359456062317,0.89208984375,11.853414535522461,,,,2.5565452575683594,371.1600036621094,475.9200134277344 -1991-07-01,12.04675579071045,0.33554431796073914,0.9624748229980469,12.397871017456055,,,,3.1678967475891113,387.80999755859375,502.0400085449219 -1991-08-01,11.526222229003906,0.3845158815383911,1.1163398027420044,13.037229537963867,,,,3.012463331222534,395.42999267578125,525.6799926757812 -1991-08-06,,,,,,,,,, -1991-09-01,12.478833198547363,0.3599340617656708,1.1654454469680786,13.740047454833984,,,,3.0346662998199463,387.8599853515625,526.8800048828125 -1991-10-01,11.83155345916748,0.37447676062583923,1.2292828559875488,14.41578483581543,,,,3.1431360244750977,392.45001220703125,542.97998046875 -1991-11-01,11.139124870300293,0.36902356147766113,1.2734787464141846,13.965290069580078,,,,2.846613883972168,375.2200012207031,523.9000244140625 -1991-11-04,,,,,,,,,, -1991-12-01,10.851117134094238,0.4109107553958893,1.4568067789077759,15.42939281463623,,,,3.8844411373138428,417.0899963378906,586.3400268554688 -1992-01-01,10.973037719726562,0.4719553291797638,1.5746610164642334,17.584869384765625,,,,3.639812469482422,408.7799987792969,620.2100219726562 -1992-02-01,10.592026710510254,0.49199995398521423,1.61721932888031,18.04088020324707,,,,3.3547844886779785,412.70001220703125,633.469970703125 -1992-02-06,,,,,,,,,, -1992-03-01,10.317353248596191,0.4253714978694916,1.5517452955245972,16.302349090576172,,,,3.043056011199951,403.69000244140625,603.77001953125 -1992-04-01,11.213163375854492,0.43906375765800476,1.4437123537063599,17.062589645385742,,,,2.631263494491577,414.95001220703125,578.6799926757812 -1992-05-01,11.213163375854492,0.4363253116607666,1.5844820737838745,17.263996124267578,,,,2.705594062805176,415.3500061035156,585.3099975585938 -1992-05-07,,,,,,,,,, -1992-06-01,12.25231647491455,0.3505205810070038,1.3749638795852661,16.055517196655273,,,,2.705594062805176,408.1400146484375,563.5999755859375 -1992-07-01,11.861115455627441,0.34207984805107117,1.4289811849594116,17.38025665283203,,,,2.263554334640503,424.2099914550781,580.8300170898438 -1992-08-01,10.84400749206543,0.33659130334854126,1.4633547067642212,17.525571823120117,,,,1.9359338283538818,414.0299987792969,563.1199951171875 -1992-08-06,,,,,,,,,, -1992-09-01,10.243829727172852,0.3310767114162445,1.58120858669281,18.464359283447266,,,,1.6753284931182861,417.79998779296875,583.27001953125 -1992-10-01,8.483662605285645,0.38518592715263367,1.7432584762573242,17.436922073364258,,,,2.12785267829895,418.67999267578125,605.1699829101562 -1992-11-01,8.658098220825195,0.42187052965164185,1.8291932344436646,18.493711471557617,,,,2.030792474746704,431.3500061035156,652.72998046875 -1992-11-05,,,,,,,,,, -1992-12-01,6.505843639373779,0.43931084871292114,1.6769649982452393,18.788379669189453,,,,1.8814688920974731,435.7099914550781,676.9500122070312 -1993-01-01,6.651134490966797,0.43747296929359436,1.6990629434585571,20.329378128051758,,,,2.4787611961364746,438.7799987792969,696.3400268554688 -1993-02-01,7.022435188293457,0.38968151807785034,1.6376806497573853,19.618152618408203,,,,2.670894145965576,443.3800048828125,670.77001953125 -1993-02-04,,,,,,,,,, -1993-03-01,6.6405534744262695,0.37947842478752136,1.8169171810150146,19.766319274902344,,,,2.573633909225464,451.6700134277344,690.1300048828125 -1993-04-01,6.346868991851807,0.3776364326477051,1.6794201135635376,18.451818466186523,,,,3.434307336807251,440.19000244140625,661.4199829101562 -1993-05-01,6.885295867919922,0.4172421991825104,1.8193715810775757,18.12286949157715,,,,3.959200859069824,450.19000244140625,700.530029296875 -1993-05-06,,,,,,,,,, -1993-06-01,6.51603364944458,0.29166528582572937,1.7285257577896118,19.299583435058594,,,,3.7042527198791504,450.5299987792969,703.9500122070312 -1993-07-01,5.872671604156494,0.2049039751291275,1.453533411026001,17.638439178466797,,,,2.9742372035980225,448.1300048828125,704.7000122070312 -1993-08-01,6.037637233734131,0.19567380845546722,1.4756313562393188,17.759246826171875,,,,2.5536391735076904,463.55999755859375,742.8400268554688 -1993-08-05,,,,,,,,,, -1993-09-01,5.574150562286377,0.1733584851026535,1.620492935180664,17.849544525146484,,,,2.1931254863739014,458.92999267578125,762.780029296875 -1993-10-01,6.105024337768555,0.22805523872375488,1.5738422870635986,19.34462547302246,,,,2.6137242317199707,467.8299865722656,779.260009765625 -1993-11-01,7.150176048278809,0.2336171418428421,1.5713871717453003,20.107433319091797,,,,2.786593437194824,461.7900085449219,754.3900146484375 -1993-11-04,,,,,,,,,, -1993-12-01,7.535686016082764,0.21771006286144257,1.5836634635925293,22.01595687866211,,,,2.68115496635437,466.45001220703125,776.7999877929688 -1994-01-01,7.535686016082764,0.24376071989536285,1.672054648399353,24.171361923217773,,,,3.64516544342041,481.6099853515625,800.469970703125 -1994-02-01,7.052198886871338,0.27167221903800964,1.620492935180664,23.894229888916016,,,,3.5310842990875244,467.1400146484375,792.5 -1994-02-04,,,,,,,,,, -1994-03-01,7.31842041015625,0.24837146699428558,1.6646885871887207,23.706161499023438,,,,2.9274797439575195,445.7699890136719,743.4600219726562 -1994-04-01,7.703606128692627,0.22409436106681824,1.8169171810150146,24.543949127197266,,,,3.2354440689086914,450.9100036621094,733.8400268554688 -1994-05-01,8.440468788146973,0.21849241852760315,2.1115520000457764,24.947307586669922,,,,3.4773480892181396,456.5,735.1900024414062 -1994-05-05,,,,,,,,,, -1994-06-01,7.905290126800537,0.19873161613941193,2.028071165084839,24.444643020629883,,,,3.2959203720092773,444.2699890136719,705.9600219726562 -1994-07-01,8.325791358947754,0.2526327669620514,2.023160934448242,25.569963455200195,,,,3.756444215774536,458.260009765625,722.1599731445312 -1994-08-01,9.217235565185547,0.27138155698776245,2.2834222316741943,26.789077758789062,,,,3.847325563430786,475.489990234375,765.6199951171875 -1994-08-04,,,,,,,,,, -1994-09-01,9.405942916870117,0.25350791215896606,2.2048532962799072,26.88198471069336,,,,3.9382081031799316,462.7099914550781,764.2899780273438 -1994-10-01,10.064526557922363,0.324998676776886,2.474935293197632,25.811735153198242,,,,4.368794918060303,472.3500061035156,777.489990234375 -1994-11-01,9.557921409606934,0.28031668066978455,2.470024824142456,24.741479873657227,,,,4.004726409912109,453.69000244140625,750.3200073242188 -1994-11-04,,,,,,,,,, -1994-12-01,9.9633207321167,0.2943686842918396,2.401276111602783,25.12115478515625,,,,3.6103241443634033,459.2699890136719,751.9600219726562 -1995-01-01,9.77692985534668,0.3047473132610321,2.332528591156006,27.753808975219727,,,,3.5117223262786865,470.4200134277344,755.2000122070312 -1995-02-01,10.200541496276855,0.29814332723617554,2.474935293197632,28.13443946838379,,,,4.345465660095215,487.3900146484375,793.72998046875 -1995-02-06,,,,,,,,,, -1995-03-01,11.169903755187988,0.2667955756187439,2.7941226959228516,29.989917755126953,,,,6.016797065734863,500.7099914550781,817.2100219726562 -1995-04-01,12.870043754577637,0.2895018756389618,3.2115228176116943,31.52293586730957,,,,7.08742618560791,514.7100219726562,843.97998046875 -1995-05-01,12.649023056030273,0.31457316875457764,3.326920747756958,28.96788215637207,,,,6.326970100402832,533.4000244140625,864.5800170898438 -1995-05-04,,,,,,,,,, -1995-06-01,13.091790199279785,0.3524453043937683,3.5503528118133545,30.15083122253418,,,,7.057007789611816,544.75,933.4500122070312 -1995-07-01,14.847586631774902,0.3415350615978241,3.5552642345428467,30.697277069091797,,,,7.513277530670166,562.0599975585938,1001.2100219726562 -1995-08-01,14.09753131866455,0.32635587453842163,3.6338343620300293,31.08300018310547,,,,6.210629463195801,561.8800048828125,1020.1099853515625 -1995-08-08,,,,,,,,,, -1995-09-01,12.916881561279297,0.28348639607429504,3.5552642345428467,34.767181396484375,,,,6.301961898803711,584.4099731445312,1043.5400390625 -1995-10-01,13.292764663696289,0.27635207772254944,3.92846941947937,33.5381965637207,,,,6.941293239593506,581.5,1036.06005859375 -1995-11-01,13.207347869873047,0.2901458144187927,3.4226772785186768,35.478694915771484,,,,8.243063926696777,605.3699951171875,1059.199951171875 -1995-11-08,,,,,,,,,, -1995-12-01,12.52143669128418,0.24333631992340088,3.447230815887451,35.68303298950195,,,,7.557409763336182,615.9299926757812,1052.1300048828125 -1996-01-01,14.868143081665039,0.21089188754558563,3.6338343620300293,32.199371337890625,,,,4.14438533782959,636.02001953125,1059.7900390625 -1996-02-01,16.80373764038086,0.2099376916885376,3.876908779144287,33.924922943115234,,,,4.089058876037598,640.4299926757812,1100.050048828125 -1996-02-07,,,,,,,,,, -1996-03-01,15.278267860412598,0.1875123232603073,4.051232814788818,32.900360107421875,,,,3.936483860015869,645.5,1101.4000244140625 -1996-04-01,14.797598838806152,0.1860809624195099,4.448990821838379,38.40559768676758,,,,5.248644828796387,654.1699829101562,1190.52001953125 -1996-05-01,14.660264015197754,0.1994406133890152,4.6650567054748535,41.25652313232422,,,,4.538435459136963,669.1199951171875,1243.4300537109375 -1996-05-08,,,,,,,,,, -1996-06-01,13.641029357910156,0.1603158563375473,4.719073295593262,42.07575225830078,,,,4.385625839233398,670.6300048828125,1185.02001953125 -1996-07-01,14.812233924865723,0.1679503321647644,4.630680561065674,39.836021423339844,,,,3.7132644653320312,639.9500122070312,1080.5899658203125 -1996-08-01,15.759532928466797,0.18512675166130066,4.812370777130127,43.394588470458984,,,,4.269299030303955,651.989990234375,1141.5 -1996-08-07,,,,,,,,,, -1996-09-01,17.209583282470703,0.16938161849975586,5.180670261383057,42.40607833862305,,,,4.5600385665893555,687.3300170898438,1226.9200439453125 -1996-10-01,17.831613540649414,0.17558389902114868,5.391822814941406,36.86507034301758,,,,4.244296073913574,705.27001953125,1221.510009765625 -1996-11-01,22.03032875061035,0.184172585606575,6.162785530090332,38.95176315307617,,,,4.841867923736572,757.02001953125,1292.6099853515625 -1996-11-06,,,,,,,,,, -1996-12-01,20.998197555541992,0.15936164557933807,6.491794109344482,41.83340072631836,,,,4.581387996673584,740.739990234375,1291.030029296875 -1997-01-01,21.743183135986328,0.12691716849803925,8.01407527923584,46.8797492980957,,,,4.642676830291748,786.1599731445312,1379.8499755859375 -1997-02-01,19.924028396606445,0.1240537017583847,7.660515785217285,49.97840881347656,,,,4.47982120513916,790.8200073242188,1309.0 -1997-02-06,,,,,,,,,, -1997-03-01,19.067983627319336,0.13932174444198608,7.203826904296875,45.4803581237793,,,,4.924733638763428,757.1199951171875,1221.699951171875 -1997-04-01,22.298084259033203,0.12977975606918335,9.546177864074707,49.431339263916016,,,,4.807621955871582,801.3400268554688,1260.760009765625 -1997-05-01,24.034704208374023,0.12691716849803925,9.742606163024902,54.45485305786133,,,,5.483453750610352,848.280029296875,1400.3199462890625 -1997-05-07,,,,,,,,,, -1997-05-28,,,,,,,,,, -1997-06-01,25.13742446899414,0.10878564417362213,9.929201126098633,63.396705627441406,0.07708299905061722,,,4.3084282875061035,885.1400146484375,1442.0699462890625 -1997-07-01,29.45465660095215,0.1335965245962143,11.107746124267578,66.42845916748047,0.11979199945926666,,,4.592585563659668,954.3099975585938,1593.81005859375 -1997-08-01,28.23609161376953,0.1660410314798355,10.385887145996094,60.97687911987305,0.11692699790000916,,,4.851738929748535,899.469970703125,1587.3199462890625 -1997-08-07,,,,,,,,,, -1997-09-01,29.579116821289062,0.16556398570537567,10.395710945129395,67.9932632446289,0.21692700684070587,,,6.2071452140808105,947.280029296875,1685.68994140625 -1997-10-01,27.48627281188965,0.13001829385757446,10.214018821716309,64.32245635986328,0.25416699051856995,,,5.889601707458496,914.6199951171875,1593.6099853515625 -1997-11-01,30.555805206298828,0.13550494611263275,11.117568969726562,63.00460433959961,0.20624999701976776,,,5.180382251739502,955.4000244140625,1600.550048828125 -1997-11-06,,,,,,,,,, -1997-12-01,29.25237274169922,0.10019782930612564,10.155089378356934,59.91267013549805,0.2510420083999634,,,5.087874889373779,970.4299926757812,1570.3499755859375 -1998-01-01,27.609769821166992,0.1397988647222519,11.721570014953613,65.44635009765625,0.24583299458026886,,,4.750911235809326,980.280029296875,1619.3599853515625 -1998-02-01,29.19993782043457,0.1803557574748993,13.317505836486816,72.36756134033203,0.3208329975605011,,,5.452751159667969,1049.3399658203125,1770.510009765625 -1998-02-06,,,,,,,,,, -1998-03-01,29.10112953186035,0.2099376916885376,14.06391716003418,86.66805267333984,0.35637998580932617,,,5.576151371002197,1101.75,1835.6800537109375 -1998-04-01,32.4630012512207,0.20898354053497314,14.162128448486328,92.79229736328125,0.3822920024394989,,,6.177727699279785,1111.75,1868.4100341796875 -1998-05-01,32.918251037597656,0.2032574564218521,13.327329635620117,84.10579681396484,0.3671880066394806,,,4.930263519287109,1090.8199462890625,1778.8699951171875 -1998-05-06,,,,,,,,,, -1998-06-01,32.225502014160156,0.2190026193857193,17.029911041259766,83.08383178710938,0.831250011920929,,,5.238887786865234,1133.8399658203125,1894.739990234375 -1998-07-01,37.19002914428711,0.26433059573173523,17.27544403076172,86.6082992553711,0.9239580035209656,,,3.988961935043335,1120.6700439453125,1872.3900146484375 -1998-08-01,31.611520767211914,0.23808826506137848,15.075493812561035,72.04536437988281,0.6979169845581055,,,3.2419238090515137,957.280029296875,1499.25 -1998-08-06,,,,,,,,,, -1998-09-01,36.12905502319336,0.2910497486591339,17.295076370239258,69.53275299072266,0.9302080273628235,,,4.283971786499023,1017.010009765625,1693.8399658203125 -1998-10-01,41.75224685668945,0.2834153473377228,16.637067794799805,80.36154174804688,1.0536459684371948,,,4.59221076965332,1098.6700439453125,1771.3900146484375 -1998-11-01,46.4265251159668,0.2438134402036667,19.170928955078125,88.54693603515625,1.600000023841858,,,5.535391330718994,1163.6300048828125,1949.5400390625 -1998-11-06,,,,,,,,,, -1998-12-01,51.91548538208008,0.3125200569629669,21.793180465698242,97.19573974609375,2.6770830154418945,,,5.782783031463623,1229.22998046875,2192.68994140625 -1999-01-01,51.59874725341797,0.3144294023513794,27.499271392822266,102.47120666503906,2.92343807220459,,,5.912916660308838,1279.6400146484375,2505.889892578125 -1999-02-01,47.797481536865234,0.2657618522644043,23.5904541015625,91.21175384521484,3.203125,,,4.984185218811035,1238.3299560546875,2288.030029296875 -1999-02-08,,,,,,,,,, -1999-03-01,49.97552490234375,0.27435049414634705,28.16712188720703,88.21611785888672,4.304687976837158,,,7.027392387390137,1286.3699951171875,2461.39990234375 -1999-04-01,58.98025894165039,0.35116779804229736,25.554689407348633,97.44929504394531,4.301562786102295,,,7.854666709899902,1335.1800537109375,2542.860107421875 -1999-05-01,65.41220092773438,0.3363769054412842,25.35825538635254,93.19886779785156,2.96875,,,9.187017440795898,1301.8399658203125,2470.52001953125 -1999-05-06,,,,,,,,,, -1999-05-27,,,,,,,,,, -1999-06-01,72.96644592285156,0.35355332493782043,28.343910217285156,97.96764373779297,3.128124952316284,,,10.182408332824707,1372.7099609375,2686.1201171875 -1999-07-01,70.95524597167969,0.4251234233379364,26.96893310546875,81.35432434082031,2.50156307220459,,,10.634023666381836,1328.719970703125,2638.489990234375 -1999-08-01,70.32015991210938,0.49812400341033936,29.090301513671875,79.7938461303711,3.109375,,,12.354691505432129,1320.4100341796875,2739.35009765625 -1999-08-06,,,,,,,,,, -1999-09-01,68.37564849853516,0.48333317041397095,28.46175193786621,69.8066177368164,3.996875047683716,,,14.07535457611084,1282.7099609375,2746.159912109375 -1999-10-01,55.519859313964844,0.6116815209388733,29.090301513671875,47.42917251586914,3.53125,,,17.35419464111328,1362.9300537109375,2966.429931640625 -1999-11-01,58.239349365234375,0.747186541557312,28.613975524902344,45.75767135620117,4.253125190734863,,,17.044021606445312,1388.9100341796875,3336.159912109375 -1999-11-08,,,,,,,,,, -1999-12-01,61.04003143310547,0.7848799824714661,36.6919059753418,37.92245101928711,3.8062500953674316,,,16.687326431274414,1469.25,4069.31005859375 -2000-01-01,63.515560150146484,0.7920366525650024,30.75990104675293,35.14961242675781,3.2281250953674316,,,13.668244361877441,1394.4599609375,3940.35009765625 -2000-02-01,58.14006042480469,0.8750578165054321,28.088550567626953,36.62297058105469,3.4437499046325684,,,25.31960105895996,1366.4200439453125,4696.68994140625 -2000-02-08,,,,,,,,,, -2000-03-01,67.05181884765625,1.0368047952651978,33.39197540283203,43.779178619384766,3.3499999046325684,,,27.631258010864258,1498.5799560546875,4572.830078125 -2000-04-01,63.157623291015625,0.947104275226593,21.920852661132812,45.03520965576172,2.7593750953674316,,,30.027847290039062,1452.4300537109375,3860.659912109375 -2000-05-01,60.785640716552734,0.6412634253501892,19.661985397338867,46.097347259521484,2.4156250953674316,,,27.948402404785156,1420.5999755859375,3400.909912109375 -2000-05-08,,,,,,,,,, -2000-06-01,62.13500213623047,0.7996708750724792,25.142194747924805,35.529056549072266,1.8156249523162842,,,32.277984619140625,1454.5999755859375,3966.110107421875 -2000-07-01,63.65913009643555,0.7758141756057739,21.940492630004883,25.79067039489746,1.506250023841858,,,28.43518829345703,1430.8299560546875,3766.989990234375 -2000-08-01,74.86860656738281,0.9304049015045166,21.940492630004883,27.82395362854004,2.075000047683716,,,32.28449630737305,1517.6800537109375,4206.35009765625 -2000-08-08,,,,,,,,,, -2000-09-01,63.94328689575195,0.3931553065776825,18.95485496520996,25.980575561523438,1.921875,,,38.555137634277344,1436.510009765625,3672.820068359375 -2000-10-01,55.92375183105469,0.29868337512016296,21.645862579345703,14.6140718460083,1.8312499523162842,,,37.785430908203125,1429.4000244140625,3369.6298828125 -2000-11-01,53.08496856689453,0.2519250810146332,18.03167152404785,12.016016960144043,1.234375,,,31.482683181762695,1314.949951171875,2597.929931640625 -2000-11-08,,,,,,,,,, -2000-12-01,48.32046127319336,0.22711420059204102,13.631781578063965,8.067792892456055,0.778124988079071,,,28.90570068359375,1320.280029296875,2470.52001953125 -2001-01-01,63.6693229675293,0.33017459511756897,19.190568923950195,14.251648902893066,0.8656250238418579,,,21.702556610107422,1366.010009765625,2772.72998046875 -2001-02-01,56.790767669677734,0.2786443829536438,18.54237174987793,10.536102294921875,0.5093749761581421,,,14.440549850463867,1239.93994140625,2151.830078125 -2001-02-07,,,,,,,,,, -2001-03-01,54.73835754394531,0.3369686007499695,17.18704605102539,10.535667419433594,0.5115000009536743,,,17.375865936279297,1160.3299560546875,1840.260009765625 -2001-04-01,65.52893829345703,0.38918623328208923,21.292295455932617,15.900238990783691,0.7889999747276306,,,22.32872772216797,1249.4599609375,2116.239990234375 -2001-05-01,63.62804412841797,0.3046000301837921,21.741714477539062,17.430465698242188,0.8345000147819519,,,19.768779754638672,1255.8199462890625,2110.489990234375 -2001-05-08,,,,,,,,,, -2001-06-01,64.6737060546875,0.35498547554016113,22.942251205444336,16.83243751525879,0.7074999809265137,,,23.362648010253906,1224.3800048828125,2160.5400390625 -2001-07-01,59.949947357177734,0.28688937425613403,20.80202293395996,14.035829544067383,0.6244999766349792,,,18.640790939331055,1211.22998046875,2027.1300048828125 -2001-08-01,56.952762603759766,0.2832247018814087,17.929533004760742,16.18165397644043,0.44699999690055847,,,16.711578369140625,1133.5799560546875,1805.4300537109375 -2001-08-08,,,,,,,,,, -2001-09-01,52.33213424682617,0.23680917918682098,16.081575393676758,13.6312894821167,0.2985000014305115,,,11.92334270477295,1040.93994140625,1498.800048828125 -2001-10-01,61.660858154296875,0.26810887455940247,18.275238037109375,12.312134742736816,0.3490000069141388,,,13.133195877075195,1059.780029296875,1690.199951171875 -2001-11-01,65.95150756835938,0.3252120614051819,20.17975616455078,14.774558067321777,0.5659999847412109,,,15.958827018737793,1139.449951171875,1930.5799560546875 -2001-11-07,,,,,,,,,, -2001-12-01,69.1006088256836,0.3343726694583893,20.820878982543945,18.32748794555664,0.5410000085830688,,,15.446427345275879,1148.0799560546875,1950.4000244140625 -2002-01-01,61.63407897949219,0.37742963433265686,20.022619247436523,19.928070068359375,0.7095000147819519,,,16.771486282348633,1130.199951171875,1934.030029296875 -2002-02-01,56.052799224853516,0.3313194811344147,18.334945678710938,17.07868194580078,0.7049999833106995,,,18.105239868164062,1106.72998046875,1731.489990234375 -2002-02-06,,,,,,,,,, -2002-03-01,59.49020767211914,0.3613981306552887,18.95407485961914,18.907917022705078,0.7149999737739563,,,20.051130294799805,1147.3900146484375,1845.3499755859375 -2002-04-01,47.912540435791016,0.3705587685108185,16.424144744873047,15.56605339050293,0.8345000147819519,,,19.893611907958984,1076.9200439453125,1688.22998046875 -2002-05-01,46.01911926269531,0.35574817657470703,15.999865531921387,15.777122497558594,0.9114999771118164,,,17.971961975097656,1067.1400146484375,1615.72998046875 -2002-05-08,,,,,,,,,, -2002-06-01,41.26642990112305,0.2705524265766144,17.190977096557617,10.55325984954834,0.8125,,,14.188389778137207,989.8200073242188,1463.2099609375 -2002-07-01,40.349422454833984,0.23299239575862885,15.079033851623535,12.224191665649414,0.7225000262260437,,,11.933782577514648,911.6199951171875,1328.260009765625 -2002-08-01,43.203697204589844,0.22520573437213898,15.424735069274902,12.329721450805664,0.746999979019165,,,10.01123046875,916.0700073242188,1314.8499755859375 -2002-08-07,,,,,,,,,, -2002-09-01,33.49409484863281,0.22138899564743042,13.746496200561523,8.706437110900879,0.796500027179718,,,9.513158798217773,815.280029296875,1172.06005859375 -2002-10-01,45.344242095947266,0.24535933136940002,16.804426193237305,11.678934097290039,0.9679999947547913,,,11.782204627990723,885.760009765625,1329.75 -2002-11-01,49.92806625366211,0.23665708303451538,18.127525329589844,15.337401390075684,1.1675000190734863,,,14.71778678894043,936.3099975585938,1478.780029296875 -2002-11-06,,,,,,,,,, -2002-12-01,44.5990104675293,0.2187930941581726,16.248149871826172,14.15894889831543,0.9445000290870667,,,12.360349655151367,879.8200073242188,1335.510009765625 -2003-01-01,45.00184631347656,0.21925139427185059,14.91561222076416,15.56605339050293,1.0924999713897705,,,13.167760848999023,855.7000122070312,1320.9100341796875 -2003-02-01,44.85797119140625,0.22917553782463074,14.896756172180176,15.829883575439453,1.1004999876022339,,,13.712512969970703,841.1500244140625,1337.52001953125 -2003-02-06,,,,,,,,,, -2003-03-01,45.22197723388672,0.2158920168876648,15.266251564025879,15.302220344543457,1.3014999628067017,,,15.372973442077637,848.1799926757812,1341.1700439453125 -2003-04-01,48.95253372192383,0.21711383759975433,16.123830795288086,17.342517852783203,1.434499979019165,,,17.22471046447754,916.9199829101562,1464.31005859375 -2003-05-01,50.76301956176758,0.2740640342235565,15.518482208251953,19.224523544311523,1.7944999933242798,,,17.618791580200195,963.5900268554688,1595.9100341796875 -2003-05-07,,,,,,,,,, -2003-06-01,47.655845642089844,0.29101142287254333,16.16796875,18.626508712768555,1.815999984741211,,,15.997578620910645,974.5,1622.800048828125 -2003-07-01,46.933773040771484,0.32185351848602295,16.653514862060547,18.995861053466797,2.0820000171661377,,,16.333431243896484,990.3099975585938,1735.02001953125 -2003-08-01,47.37279510498047,0.34521347284317017,16.722881317138672,18.960683822631836,2.315999984741211,,,19.37755012512207,1008.010009765625,1810.449951171875 -2003-08-06,,,,,,,,,, -2003-09-01,51.1259651184082,0.3163566291332245,17.530014038085938,18.046072006225586,2.4214999675750732,,,19.657007217407227,995.969970703125,1786.93994140625 -2003-10-01,51.79159927368164,0.3494885563850403,16.48325538635254,18.468202590942383,2.7214999198913574,,,21.84463119506836,1050.7099609375,1932.2099609375 -2003-11-01,52.405128479003906,0.3192576766014099,16.303056716918945,21.423110961914062,2.698499917984009,,,20.621614456176758,1058.199951171875,1960.260009765625 -2003-11-06,,,,,,,,,, -2003-12-01,53.74093246459961,0.32628077268600464,17.35569190979004,24.272485733032227,2.63100004196167,,,19.5084171295166,1111.9200439453125,2003.3699951171875 -2004-01-01,57.53900146484375,0.3444499373435974,17.533241271972656,25.74994659423828,2.5199999809265137,,,19.119047164916992,1131.1300048828125,2066.14990234375 -2004-02-01,55.95598602294922,0.36521488428115845,16.82303810119629,24.87051010131836,2.1505000591278076,,,18.600963592529297,1144.93994140625,2029.8199462890625 -2004-02-06,,,,,,,,,, -2004-03-01,53.3401985168457,0.4128514230251312,15.808448791503906,25.6268310546875,2.1640000343322754,,,19.62464141845703,1126.2099609375,1994.219970703125 -2004-04-01,51.208683013916016,0.39361342787742615,16.56939125061035,23.6217041015625,2.180000066757202,,,20.729951858520508,1107.300048828125,1920.1500244140625 -2004-05-01,51.45262145996094,0.4284247159957886,16.632802963256836,23.815183639526367,2.424999952316284,,,22.293439865112305,1120.6800537109375,1986.739990234375 -2004-05-06,,,,,,,,,, -2004-06-01,51.300846099853516,0.4968261420726776,18.110280990600586,25.503700256347656,2.7200000286102295,,,23.227535247802734,1140.8399658203125,2047.7900390625 -2004-07-01,50.672325134277344,0.4937727451324463,18.065902709960938,24.378023147583008,1.9459999799728394,,,21.075881958007812,1101.719970703125,1887.3599853515625 -2004-08-01,49.28722381591797,0.5265995860099792,17.31130027770996,23.6217041015625,1.906999945640564,,,22.91964340209961,1104.239990234375,1838.0999755859375 -2004-08-06,,,,,,,,,, -2004-09-01,50.00397872924805,0.5916416049003601,17.5849609375,24.764976501464844,2.0429999828338623,,64.8648681640625,24.718441009521484,1114.5799560546875,1896.8399658203125 -2004-10-01,52.34260559082031,0.800052285194397,17.788476943969727,25.978591918945312,1.7065000534057617,,95.41541290283203,28.003637313842773,1130.199951171875,1974.989990234375 -2004-11-01,54.96117401123047,1.0237311124801636,17.050731658935547,26.945974349975586,1.9839999675750732,,91.0810775756836,30.26772117614746,1173.8199462890625,2096.81005859375 -2004-11-08,,,,,,,,,, -2004-12-01,57.60344696044922,0.9832708239555359,18.939943313598633,29.918479919433594,2.2144999504089355,,96.49149322509766,31.357276916503906,1211.9200439453125,2175.43994140625 -2005-01-01,54.58831024169922,1.1741228103637695,18.628063201904297,27.930959701538086,2.1610000133514404,,97.90790557861328,28.444419860839844,1181.27001953125,2062.409912109375 -2005-02-01,54.09749221801758,1.3698610067367554,17.83417510986328,27.438472747802734,1.7589999437332153,,94.0890884399414,30.86894416809082,1203.5999755859375,2051.719970703125 -2005-02-08,,,,,,,,,, -2005-03-01,53.49815368652344,1.2724499702453613,17.18527603149414,26.6469669342041,1.7135000228881836,,90.34534454345703,33.57841110229492,1180.5899658203125,1999.22998046875 -2005-04-01,44.7164421081543,1.1011403799057007,17.988739013671875,23.305103302001953,1.6180000305175781,,110.110107421875,29.735000610351562,1156.8499755859375,1921.6500244140625 -2005-05-01,44.23051071166992,1.2141252756118774,18.3442440032959,23.867952346801758,1.7755000591278076,,138.77377319335938,33.119998931884766,1191.5,2068.219970703125 -2005-05-06,,,,,,,,,, -2005-06-01,43.555538177490234,1.124043345451355,17.71768569946289,24.254899978637695,1.6545000076293945,,147.22222900390625,28.610000610351562,1191.3299560546875,2056.9599609375 -2005-07-01,48.991188049316406,1.3023751974105835,18.26690673828125,23.234752655029297,2.257499933242798,,144.02401733398438,29.639999389648438,1234.1800537109375,2184.830078125 -2005-08-01,47.3240966796875,1.431849479675293,19.529403686523438,23.58652687072754,2.134999990463257,,143.1431427001953,27.040000915527344,1220.3299560546875,2152.090087890625 -2005-08-08,,,,,,,,,, -2005-09-01,47.202552795410156,1.6370543241500854,18.40694236755371,24.00865936279297,2.265000104904175,,158.3883819580078,29.850000381469727,1228.81005859375,2151.68994140625 -2005-10-01,48.1793098449707,1.7585887908935547,18.385473251342773,23.867952346801758,1.9930000305175781,,186.25625610351562,32.25,1207.010009765625,2120.300048828125 -2005-11-01,52.309974670410156,2.0709757804870605,19.80194664001465,24.976051330566406,2.4230000972747803,,202.65765380859375,32.61000061035156,1249.47998046875,2232.820068359375 -2005-11-08,,,,,,,,,, -2005-12-01,48.483585357666016,2.1952593326568604,18.762245178222656,25.76755142211914,2.3575000762939453,,207.63763427734375,36.959999084472656,1248.2900390625,2205.320068359375 -2006-01-01,47.95273208618164,2.305800437927246,20.19721794128418,25.169517517089844,2.240999937057495,,216.5465545654297,39.72999954223633,1280.0799560546875,2305.820068359375 -2006-02-01,47.32752227783203,2.0914342403411865,19.27882957458496,26.207246780395508,1.871999979019165,,181.49148559570312,38.54999923706055,1280.6600341796875,2281.389892578125 -2006-02-08,,,,,,,,,, -2006-03-01,48.76497268676758,1.9152405261993408,19.588937759399414,26.734920501708984,1.8265000581741333,,195.1951904296875,34.95000076293945,1294.8699951171875,2339.7900390625 -2006-04-01,48.6880989074707,2.149454355239868,17.385986328125,24.694625854492188,1.7604999542236328,,209.17918395996094,39.20000076293945,1310.6099853515625,2322.570068359375 -2006-05-01,47.24531936645508,1.8251585960388184,16.306108474731445,24.149375915527344,1.7304999828338623,,186.09609985351562,28.6299991607666,1270.0899658203125,2178.8798828125 -2006-05-08,,,,,,,,,, -2006-06-01,45.58832931518555,1.7488168478012085,16.83946990966797,24.465961456298828,1.934000015258789,,209.8748779296875,30.360000610351562,1270.199951171875,2172.090087890625 -2006-07-01,45.938453674316406,2.075251340866089,17.388734817504883,24.78256607055664,1.344499945640564,,193.49349975585938,28.510000228881836,1276.6600341796875,2091.469970703125 -2006-08-01,48.051090240478516,2.0718915462493896,18.574007034301758,26.048952102661133,1.5414999723434448,,189.45445251464844,32.439998626708984,1303.8199462890625,2183.75 -2006-08-08,,,,,,,,,, -2006-09-01,48.820682525634766,2.350689172744751,19.839282989501953,27.368104934692383,1.6059999465942383,,201.15115356445312,37.459999084472656,1335.8499755859375,2258.429931640625 -2006-10-01,55.01115798950195,2.475886821746826,20.82581329345703,29.90088653564453,1.9045000076293945,,238.4334259033203,38.25,1377.93994140625,2366.7099609375 -2006-11-01,54.766883850097656,2.798962354660034,21.29731559753418,29.021446228027344,2.0169999599456787,,242.64764404296875,40.15999984741211,1400.6300048828125,2431.77001953125 -2006-11-08,,,,,,,,,, -2006-12-01,58.07079315185547,2.5907039642333984,21.73406219482422,29.812957763671875,1.9730000495910645,,230.47047424316406,41.119998931884766,1418.300048828125,2415.2900390625 -2007-01-01,59.266300201416016,2.617882013320923,22.461923599243164,30.252676010131836,1.8834999799728394,,251.00100708007812,38.869998931884766,1438.239990234375,2463.929931640625 -2007-02-01,55.55428695678711,2.583681344985962,20.50397491455078,30.37578582763672,1.9570000171661377,,224.949951171875,39.25,1406.8199462890625,2416.14990234375 -2007-02-07,,,,,,,,,, -2007-03-01,56.51309585571289,2.8371317386627197,20.3559513092041,29.707414627075195,1.9895000457763672,,229.30931091308594,41.70000076293945,1420.8599853515625,2421.639892578125 -2007-04-01,61.27952575683594,3.0475287437438965,21.867843627929688,32.539215087890625,3.066499948501587,,235.92591857910156,41.560001373291016,1482.3699951171875,2525.090087890625 -2007-05-01,63.91150665283203,3.700700044631958,22.415639877319336,33.18999099731445,3.4570000171661377,,249.20420837402344,44.060001373291016,1530.6199951171875,2604.52001953125 -2007-05-08,,,,,,,,,, -2007-06-01,63.347747802734375,3.7266552448272705,21.59428596496582,32.5040397644043,3.4205000400543213,,261.6116027832031,40.150001525878906,1503.3499755859375,2603.22998046875 -2007-07-01,66.59786987304688,4.023471355438232,21.242568969726562,30.70998764038086,3.927000045776367,,255.2552490234375,40.290000915527344,1455.27001953125,2546.27001953125 -2007-08-01,70.23324584960938,4.228675365447998,21.052053451538086,30.12954330444336,3.995500087738037,,257.88287353515625,42.75,1473.989990234375,2596.360107421875 -2007-08-08,,,,,,,,,, -2007-09-01,71.1520004272461,4.68641471862793,21.662628173828125,30.49891471862793,4.65749979019165,,283.9189147949219,43.65999984741211,1526.75,2701.5 -2007-10-01,70.13726806640625,5.800380706787109,27.067256927490234,30.674802780151367,4.457499980926514,,353.8538513183594,47.900001525878906,1549.3800048828125,2859.1201171875 -2007-11-01,63.529457092285156,5.564334392547607,24.70686912536621,29.6898193359375,4.5279998779296875,,346.8468322753906,42.13999938964844,1481.1400146484375,2660.9599609375 -2007-11-07,,,,,,,,,, -2007-12-01,65.52474212646484,6.048642158508301,26.26405906677246,28.4762020111084,4.631999969482422,,346.0860900878906,42.72999954223633,1468.3599853515625,2652.280029296875 -2008-01-01,64.92465209960938,4.133399963378906,24.050800323486328,27.21040916442871,3.884999990463257,,282.43243408203125,34.93000030517578,1378.550048828125,2389.860107421875 -2008-02-01,69.0161361694336,3.8176541328430176,20.066930770874023,25.923078536987305,3.2235000133514404,,235.82582092285156,33.650001525878906,1330.6300048828125,2271.47998046875 -2008-02-06,,,,,,,,,, -2008-03-01,70.05885314941406,4.381966590881348,21.01883316040039,26.399211883544922,3.565000057220459,,220.45545959472656,35.59000015258789,1322.699951171875,2279.10009765625 -2008-04-01,73.44194030761719,5.311798572540283,21.122520446777344,24.706567764282227,3.93149995803833,,287.43243408203125,37.290000915527344,1385.5899658203125,2412.800048828125 -2008-05-01,78.75386810302734,5.763737201690674,20.974388122558594,24.016834259033203,4.080999851226807,,293.1932067871094,44.060001373291016,1400.3800048828125,2522.659912109375 -2008-05-07,,,,,,,,,, -2008-06-01,72.41636657714844,5.11300802230835,20.449499130249023,23.981456756591797,3.6665000915527344,,263.4734802246094,39.38999938964844,1280.0,2292.97998046875 -2008-07-01,78.18988800048828,4.853753566741943,19.118900299072266,24.197914123535156,3.816999912261963,,237.1121063232422,41.349998474121094,1267.3800048828125,2325.550048828125 -2008-08-01,74.37141418457031,5.176827907562256,20.285959243774414,24.712379455566406,4.040500164031982,,231.8768768310547,42.83000183105469,1282.8299560546875,2367.52001953125 -2008-08-06,,,,,,,,,, -2008-09-01,71.73551177978516,3.470762014389038,19.91908073425293,20.454687118530273,3.638000011444092,,200.46046447753906,39.470001220703125,1166.3599853515625,2091.8798828125 -2008-10-01,57.02163314819336,3.2854056358337402,16.66515350341797,14.2785062789917,2.861999988555908,,179.85986328125,26.639999389648438,968.75,1720.949951171875 -2008-11-01,50.04803466796875,2.8298041820526123,15.090431213378906,12.444729804992676,2.134999990463257,,146.6266326904297,23.15999984741211,896.239990234375,1535.5699462890625 -2008-11-06,,,,,,,,,, -2008-12-01,51.90673828125,2.6062774658203125,14.606595039367676,14.189484596252441,2.563999891281128,,153.97897338867188,21.290000915527344,903.25,1577.030029296875 -2009-01-01,56.52627944946289,2.7522425651550293,12.848398208618164,11.888165473937988,2.940999984741211,,169.43443298339844,19.309999465942383,825.8800048828125,1476.4200439453125 -2009-02-01,56.76066589355469,2.7272019386291504,12.13459587097168,9.274202346801758,3.239500045776367,,169.16416931152344,16.700000762939453,735.0900268554688,1377.8399658203125 -2009-02-06,,,,,,,,,, -2009-03-01,60.08320999145508,3.209982395172119,13.897279739379883,8.146258354187012,3.671999931335449,,174.20420837402344,21.389999389648438,797.8699951171875,1528.5899658203125 -2009-04-01,64.00231170654297,3.8423893451690674,15.3270902633667,11.028571128845215,4.026000022888184,,198.1831817626953,27.350000381469727,872.8099975585938,1717.300048828125 -2009-05-01,65.90607452392578,4.147140979766846,15.803704261779785,12.274019241333008,3.8994998931884766,,208.82382202148438,28.18000030517578,919.1400146484375,1774.3299560546875 -2009-05-06,,,,,,,,,, -2009-06-01,65.09091186523438,4.349293231964111,18.0966796875,11.69642448425293,4.183000087738037,,211.00601196289062,28.299999237060547,919.3200073242188,1835.0400390625 -2009-07-01,73.51245880126953,4.989336013793945,17.906352996826172,14.879191398620605,4.288000106811523,,221.7467498779297,32.41999816894531,987.47998046875,1978.5 -2009-08-01,73.58724975585938,5.1365203857421875,18.766645431518555,15.714898109436035,4.059500217437744,,231.06607055664062,31.420000076293945,1020.6199951171875,2009.06005859375 -2009-08-06,,,,,,,,,, -2009-09-01,74.90744018554688,5.659914016723633,19.69136619567871,14.061647415161133,4.668000221252441,,248.1731719970703,33.040000915527344,1057.0799560546875,2122.419921875 -2009-10-01,75.53370666503906,5.756102561950684,21.230228424072266,13.72740650177002,5.940499782562256,,268.3283386230469,32.939998626708984,1036.18994140625,2045.1099853515625 -2009-11-01,79.12847900390625,6.1045241355896,22.51645278930664,14.055986404418945,6.795499801635742,,291.7917785644531,35.08000183105469,1095.6300048828125,2144.60009765625 -2009-11-06,,,,,,,,,, -2009-12-01,82.34589385986328,6.434925556182861,23.438796997070312,15.443329811096191,6.72599983215332,,310.30029296875,36.779998779296875,1115.0999755859375,2269.14990234375 -2010-01-01,76.99246215820312,5.86481237411499,21.670120239257812,15.999075889587402,6.270500183105469,,265.2352294921875,32.29999923706055,1073.8699951171875,2147.35009765625 -2010-02-01,79.99315643310547,6.248349666595459,22.04693031311035,17.19167137145996,5.920000076293945,,263.6636657714844,34.650001525878906,1104.489990234375,2238.260009765625 -2010-02-08,,,,,,,,,, -2010-03-01,81.0396728515625,7.176041126251221,22.629026412963867,17.88887596130371,6.78849983215332,,283.8438415527344,35.369998931884766,1169.4300537109375,2397.9599609375 -2010-04-01,81.51359558105469,7.972740650177002,23.594758987426758,20.0878963470459,6.855000019073486,,263.11309814453125,33.599998474121094,1186.68994140625,2461.18994140625 -2010-05-01,79.1503677368164,7.84417724609375,19.932706832885742,17.157638549804688,6.2729997634887695,,243.0580596923828,32.08000183105469,1089.4100341796875,2257.0400390625 -2010-05-06,,,,,,,,,, -2010-06-01,78.42550659179688,7.680809020996094,17.857412338256836,14.817129135131836,5.4629998207092285,,222.69769287109375,26.43000030517578,1030.7099609375,2109.239990234375 -2010-07-01,81.55033874511719,7.855478763580322,20.03040885925293,18.038850784301758,5.894499778747559,,242.66766357421875,28.719999313354492,1101.5999755859375,2254.699951171875 -2010-08-01,78.20323944091797,7.4233856201171875,18.214401245117188,15.64971923828125,6.241499900817871,,225.2352294921875,27.700000762939453,1049.3299560546875,2114.030029296875 -2010-08-06,,,,,,,,,, -2010-09-01,85.6181411743164,8.664690971374512,19.10737419128418,19.168588638305664,7.853000164031982,,263.1581726074219,26.149999618530273,1141.199951171875,2368.6201171875 -2010-10-01,91.65619659423828,9.190831184387207,20.808244705200195,21.757949829101562,8.261500358581543,,307.15716552734375,28.149999618530273,1183.260009765625,2507.409912109375 -2010-11-01,90.290283203125,9.501388549804688,19.70814323425293,21.311634063720703,8.770000457763672,,278.1331481933594,27.799999237060547,1180.550048828125,2498.22998046875 -2010-11-08,,,,,,,,,, -2010-12-01,94.08943176269531,9.84980583190918,21.909496307373047,21.423206329345703,9.0,,297.28228759765625,30.780000686645508,1257.6400146484375,2652.8701171875 -2011-01-01,103.8599853515625,10.361597061157227,21.76820182800293,19.823131561279297,8.482000350952148,,300.48046875,33.04999923706055,1286.1199951171875,2700.080078125 -2011-02-01,103.78302001953125,10.785745620727539,20.865453720092773,20.065792083740234,8.66450023651123,,307.00701904296875,34.5,1327.219970703125,2782.27001953125 -2011-02-08,,,,,,,,,, -2011-03-01,104.95984649658203,10.64222526550293,20.04909324645996,19.879127502441406,9.006500244140625,,293.6736755371094,33.15999984741211,1325.8299560546875,2781.070068359375 -2011-04-01,109.79368591308594,10.691693305969238,20.467607498168945,18.910266876220703,9.790499687194824,,272.32232666015625,33.54999923706055,1363.6099853515625,2873.5400390625 -2011-05-01,108.73165130615234,10.621461868286133,19.7490291595459,19.135168075561523,9.834500312805176,,264.7747802734375,34.630001068115234,1345.199951171875,2835.300048828125 -2011-05-06,,,,,,,,,, -2011-06-01,110.91179656982422,10.25013542175293,20.665348052978516,19.510000228881836,10.224499702453613,,253.4434356689453,31.450000762939453,1320.6400146484375,2773.52001953125 -2011-07-01,117.571044921875,11.923834800720215,21.778095245361328,17.562105178833008,11.12600040435791,,302.14715576171875,27.709999084472656,1292.280029296875,2756.3798828125 -2011-08-01,111.14454650878906,11.75130844116211,21.142244338989258,15.623312950134277,10.761500358581543,,270.7507629394531,25.239999771118164,1218.8900146484375,2579.4599609375 -2011-08-08,,,,,,,,,, -2011-09-01,113.55061340332031,11.644124984741211,19.90796661376953,13.119815826416016,10.81149959564209,,257.77777099609375,24.170000076293945,1131.4200439453125,2415.39990234375 -2011-10-01,119.88819122314453,12.360504150390625,21.299680709838867,15.485393524169922,10.67549991607666,,296.6166076660156,29.40999984741211,1253.300048828125,2684.409912109375 -2011-11-01,122.07645416259766,11.670994758605957,20.459848403930664,15.42860221862793,9.614500045776367,,299.9949951171875,27.420000076293945,1246.9599609375,2620.340087890625 -2011-11-08,,,,,,,,,, -2011-12-01,119.88117218017578,12.367225646972656,20.92014503479004,15.068914413452148,8.654999732971191,,323.2732849121094,28.270000457763672,1257.5999755859375,2605.14990234375 -2012-01-01,125.56623077392578,13.939234733581543,23.79706382751465,14.749091148376465,9.722000122070312,,290.3453369140625,30.950000762939453,1312.4100341796875,2813.840087890625 -2012-02-01,128.25875854492188,16.564136505126953,25.57801628112793,15.662582397460938,8.98449993133545,,309.4344482421875,32.88999938964844,1365.6800537109375,2966.889892578125 -2012-02-08,,,,,,,,,, -2012-03-01,136.5597381591797,18.308074951171875,26.1682071685791,15.377117156982422,10.125499725341797,,320.9409484863281,34.310001373291016,1408.469970703125,3091.570068359375 -2012-04-01,135.53221130371094,17.83262062072754,25.97353172302246,14.882647514343262,11.595000267028809,,302.72772216796875,33.54999923706055,1397.9100341796875,3046.360107421875 -2012-05-01,126.25150299072266,17.64176368713379,23.677928924560547,13.811394691467285,10.645500183105469,,290.7207336425781,31.049999237060547,1310.3299560546875,2827.340087890625 -2012-05-08,,,,,,,,,, -2012-06-01,128.5418243408203,17.83323097229004,24.976381301879883,15.054808616638184,11.417499542236328,,290.3253173828125,32.369998931884766,1362.1600341796875,2935.050048828125 -2012-07-01,128.80467224121094,18.650381088256836,24.06191635131836,13.332178115844727,11.664999961853027,,316.8017883300781,30.8799991607666,1379.3199462890625,2939.52001953125 -2012-08-01,128.06199645996094,20.314008712768555,25.1641845703125,14.178669929504395,12.41349983215332,,342.88787841796875,31.270000457763672,1406.5799560546875,3066.9599609375 -2012-08-08,,,,,,,,,, -2012-09-01,136.92532348632812,20.458269119262695,24.459667205810547,14.120949745178223,12.715999603271484,,377.62762451171875,32.439998626708984,1440.6700439453125,3116.22998046875 -2012-10-01,128.39756774902344,18.256948471069336,23.456958770751953,12.4627103805542,11.644499778747559,,340.490478515625,34.029998779296875,1412.1600341796875,2977.22998046875 -2012-11-01,125.45381927490234,17.94904899597168,21.878910064697266,13.178736686706543,12.602499961853027,,349.5345458984375,34.61000061035156,1416.1800537109375,3010.239990234375 -2012-11-07,,,,,,,,,, -2012-12-01,126.98394775390625,16.39484405517578,22.13326644897461,13.19808578491211,12.543499946594238,,354.0440368652344,37.68000030517578,1426.18994140625,3019.510009765625 -2013-01-01,134.6208953857422,14.03252124786377,22.746475219726562,15.598185539245605,13.274999618530273,,378.2232360839844,37.83000183105469,1498.1099853515625,3142.1298828125 -2013-02-01,133.1359405517578,13.598445892333984,23.036500930786133,15.79291820526123,13.213500022888184,,401.0010070800781,39.310001373291016,1514.6800537109375,3160.18994140625 -2013-02-06,,,,,,,,,, -2013-03-01,141.99786376953125,13.716739654541016,23.903995513916016,16.74711036682129,13.32450008392334,,397.49249267578125,43.52000045776367,1569.18994140625,3267.52001953125 -2013-04-01,134.8347625732422,13.720457077026367,27.655447006225586,16.822824478149414,12.690500259399414,,412.69769287109375,45.08000183105469,1597.5699462890625,3328.7900390625 -2013-05-01,138.48284912109375,13.935823440551758,29.15936851501465,17.234567642211914,13.460000038146973,,436.0460510253906,42.90999984741211,1630.739990234375,3455.909912109375 -2013-05-08,,,,,,,,,, -2013-06-01,127.82191467285156,12.368638038635254,29.060949325561523,17.783567428588867,13.884499549865723,,440.6256408691406,45.560001373291016,1606.280029296875,3403.25 -2013-07-01,130.45040893554688,14.115401268005371,26.789243698120117,19.142587661743164,15.060999870300293,,444.3193054199219,47.279998779296875,1685.72998046875,3626.3701171875 -2013-08-01,121.90938568115234,15.19745922088623,28.101774215698242,19.695158004760742,14.048999786376953,,423.8738708496094,45.75,1632.969970703125,3589.8701171875 -2013-08-07,,,,,,,,,, -2013-09-01,124.47478485107422,14.96906566619873,28.19812774658203,20.306934356689453,15.631999969482422,,438.3934020996094,51.939998626708984,1681.550048828125,3771.47998046875 -2013-10-01,120.46192169189453,16.41180992126465,30.002870559692383,19.72601890563965,18.201499938964844,,515.8057861328125,54.220001220703125,1756.5400390625,3919.7099609375 -2013-11-01,120.77778625488281,17.45956802368164,32.30753707885742,22.58371353149414,19.680999755859375,,530.3253173828125,56.779998779296875,1805.81005859375,4059.889892578125 -2013-11-06,,,,,,,,,, -2013-12-01,126.7584228515625,17.71782684326172,31.937856674194336,24.151473999023438,19.93950080871582,,560.9158935546875,59.880001068115234,1848.3599853515625,4176.58984375 -2014-01-01,119.39906311035156,15.80967903137207,32.304969787597656,21.634523391723633,17.934499740600586,,591.0760498046875,59.189998626708984,1782.5899658203125,4103.8798828125 -2014-02-01,125.13655090332031,16.61942481994629,32.70622634887695,21.913677215576172,18.104999542236328,,608.4334106445312,68.62999725341797,1859.449951171875,4308.1201171875 -2014-02-06,,,,,,,,,, -2014-03-01,130.79644775390625,17.052499771118164,35.256614685058594,22.53180694580078,16.818500518798828,,557.8128051757812,65.73999786376953,1872.3399658203125,4198.990234375 -2014-04-01,133.50091552734375,18.747455596923828,34.7491340637207,24.246538162231445,15.206500053405762,,534.8800048828125,61.689998626708984,1883.949951171875,4114.56005859375 -2014-05-01,125.27215576171875,20.11072540283203,35.21359634399414,24.767969131469727,15.6274995803833,,571.6500244140625,64.54000091552734,1923.5699462890625,4242.6201171875 -2014-05-07,,,,,,,,,, -2014-06-01,123.88963317871094,20.782461166381836,36.12032699584961,24.94846534729004,16.23900032043457,,584.6699829101562,72.36000061035156,1960.22998046875,4408.18017578125 -2014-07-01,130.99761962890625,21.37957000732422,37.38497543334961,26.72607421875,15.649499893188477,,579.5499877929688,69.25,1930.6700439453125,4369.77001953125 -2014-08-01,131.4281463623047,22.922651290893555,39.35124206542969,27.834640502929688,16.95199966430664,,582.3599853515625,71.9000015258789,2003.3699951171875,4580.27001953125 -2014-08-06,,,,,,,,,, -2014-09-01,130.50729370117188,22.643360137939453,40.40761947631836,26.66560935974121,16.121999740600586,,588.4099731445312,69.19000244140625,1972.2900390625,4493.39013671875 -2014-10-01,113.0242691040039,24.27278709411621,40.92185974121094,26.89417266845703,15.27299976348877,,567.8699951171875,70.12000274658203,2018.050048828125,4630.740234375 -2014-11-01,111.49117279052734,26.729284286499023,41.67144012451172,28.27128028869629,16.93199920654297,,549.0800170898438,73.68000030517578,2067.56005859375,4791.6298828125 -2014-11-06,,,,,,,,,, -2014-12-01,111.05672454833984,24.915250778198242,40.74142074584961,28.068769454956055,15.517499923706055,,530.6599731445312,72.69999694824219,2058.89990234375,4736.0498046875 -2015-01-01,106.12132263183594,26.445661544799805,35.434940338134766,26.79075813293457,17.726499557495117,,537.5499877929688,70.12999725341797,1994.989990234375,4635.240234375 -2015-02-01,112.09505462646484,28.996322631835938,38.460933685302734,27.767194747924805,19.007999420166016,,562.6300048828125,79.0999984741211,2104.5,4963.52978515625 -2015-02-06,,,,,,,,,, -2015-03-01,111.87759399414062,28.197509765625,35.91679000854492,26.139816284179688,18.604999542236328,,554.7000122070312,73.94000244140625,2067.889892578125,4900.8798828125 -2015-04-01,119.3988265991211,28.360668182373047,42.96587371826172,23.521541595458984,21.089000701904297,,548.77001953125,76.05999755859375,2085.510009765625,4941.419921875 -2015-05-01,118.25563049316406,29.523195266723633,41.39352035522461,23.3579158782959,21.46150016784668,,545.3200073242188,79.08999633789062,2107.389892578125,5070.02978515625 -2015-05-06,,,,,,,,,, -2015-06-01,114.24130249023438,28.542850494384766,39.253108978271484,21.762542724609375,21.704500198364258,,540.0399780273438,81.01000213623047,2063.110107421875,4986.8701171875 -2015-07-01,113.77072143554688,27.60302734375,41.520286560058594,22.683992385864258,26.8075008392334,,657.5,81.98999786376953,2103.840087890625,5128.27978515625 -2015-08-01,103.86784362792969,25.65966796875,38.692989349365234,20.93431854248047,25.644500732421875,,647.8200073242188,78.56999969482422,1972.1800537109375,4776.509765625 -2015-08-06,,,,,,,,,, -2015-09-01,102.66226959228516,25.21347999572754,39.61040496826172,20.028608322143555,25.594499588012695,,638.3699951171875,82.22000122070312,1920.030029296875,4620.16015625 -2015-10-01,99.1993637084961,27.31651496887207,47.11008071899414,19.46390151977539,31.295000076293945,,737.3900146484375,88.66000366210938,2079.360107421875,5053.75 -2015-11-01,98.7319564819336,27.042200088500977,48.64043045043945,21.868392944335938,33.2400016784668,,762.8499755859375,91.45999908447266,2080.409912109375,5108.669921875 -2015-11-06,,,,,,,,,, -2015-12-01,98.37144470214844,24.16438102722168,49.98638916015625,22.03421974182129,33.794498443603516,,778.010009765625,93.94000244140625,2043.93994140625,5007.41015625 -2016-01-01,89.20050811767578,22.3461971282959,49.63501739501953,20.343839645385742,29.350000381469727,,761.3499755859375,89.12999725341797,1940.239990234375,4613.9501953125 -2016-02-01,93.6608657836914,22.196985244750977,45.841888427734375,20.051719665527344,27.625999450683594,,717.219970703125,85.1500015258789,1932.22998046875,4557.9501953125 -2016-02-08,,,,,,,,,, -2016-03-01,109.36297607421875,25.15644073486328,50.11842727661133,23.285871505737305,29.68199920654297,,762.9000244140625,93.80000305175781,2059.739990234375,4869.85009765625 -2016-04-01,105.3841552734375,21.636524200439453,45.25450134277344,20.178363800048828,32.97949981689453,,707.8800048828125,94.22000122070312,2065.300048828125,4775.35986328125 -2016-05-01,111.01663208007812,23.049110412597656,48.094825744628906,20.956079483032227,36.13949966430664,,748.8499755859375,99.47000122070312,2096.949951171875,4948.0498046875 -2016-05-06,,,,,,,,,, -2016-06-01,110.65898895263672,22.20018196105957,46.75897216796875,19.947153091430664,35.78099822998047,,703.530029296875,95.79000091552734,2098.860107421875,4842.669921875 -2016-07-01,117.10401916503906,24.199600219726562,51.79398727416992,21.839828491210938,37.94049835205078,,791.3400268554688,97.86000061035156,2173.60009765625,5162.1298828125 -2016-08-01,115.83541107177734,24.638490676879883,52.506752014160156,20.885658264160156,38.45800018310547,,789.8499755859375,102.30999755859375,2170.949951171875,5213.22021484375 -2016-08-08,,,,,,,,,, -2016-09-01,116.81381225585938,26.394628524780273,52.96271896362305,21.479366302490234,41.865501403808594,13.321450233459473,804.0599975585938,108.54000091552734,2168.27001953125,5312.0 -2016-10-01,113.019287109375,26.509033203125,55.095947265625,20.878395080566406,39.49100112915039,13.680961608886719,809.9000244140625,107.51000213623047,2126.14990234375,5189.14013671875 -2016-11-01,119.29200744628906,25.803936004638672,55.40858840942383,19.980859756469727,37.528499603271484,14.926712036132812,775.8800048828125,102.80999755859375,2198.81005859375,5323.68017578125 -2016-11-08,,,,,,,,,, -2016-12-01,123.1717300415039,27.180198669433594,57.52321243286133,18.655929565429688,37.493499755859375,15.31966781616211,792.4500122070312,102.94999694824219,2238.830078125,5383.1201171875 -2017-01-01,129.5013427734375,28.47795867919922,59.84672927856445,22.670724868774414,41.17399978637695,17.554771423339844,820.1900024414062,113.37999725341797,2278.8701171875,5614.7900390625 -2017-02-01,133.43417358398438,32.148292541503906,59.22650909423828,24.339130401611328,42.25199890136719,17.69411849975586,844.9299926757812,118.33999633789062,2363.639892578125,5825.43994140625 -2017-02-08,,,,,,,,,, -2017-03-01,130.24111938476562,33.85976028442383,61.33644485473633,24.011991500854492,44.32699966430664,17.858545303344727,847.7999877929688,130.1300048828125,2362.719970703125,5911.740234375 -2017-04-01,119.88253784179688,33.85739517211914,63.75787353515625,23.72492027282715,46.2495002746582,18.70298194885254,924.52001953125,133.74000549316406,2384.199951171875,6047.60986328125 -2017-05-01,114.1535415649414,36.00456237792969,65.0430908203125,23.328956604003906,49.73099899291992,19.338397979736328,987.0900268554688,141.86000061035156,2411.800048828125,6198.52001953125 -2017-05-08,,,,,,,,,, -2017-06-01,116.17494201660156,34.084716796875,64.56354522705078,23.700172424316406,48.400001525878906,17.030832290649414,929.6799926757812,141.44000244140625,2423.409912109375,6140.419921875 -2017-07-01,109.25714874267578,35.19940948486328,68.0947265625,25.520694732666016,49.388999938964844,17.911497116088867,945.5,146.49000549316406,2470.300048828125,6348.1201171875 -2017-08-01,108.01860809326172,38.81330490112305,70.03360748291016,26.852062225341797,49.029998779296875,20.882347106933594,955.239990234375,155.16000366210938,2471.64990234375,6428.66015625 -2017-08-08,,,,,,,,,, -2017-09-01,110.72441864013672,36.6182861328125,70.14307403564453,27.700807571411133,48.067501068115234,21.517763137817383,973.719970703125,149.17999267578125,2519.360107421875,6495.9599609375 -2017-10-01,117.57792663574219,40.163211822509766,78.32596588134766,25.408233642578125,55.263999938964844,23.067289352416992,1033.0400390625,175.16000366210938,2575.260009765625,6727.669921875 -2017-11-01,117.50923919677734,40.83085632324219,79.2582015991211,24.86334991455078,58.837501525878906,21.80481719970703,1036.1700439453125,181.47000122070312,2647.580078125,6873.97021484375 -2017-11-09,,,,,,,,,, -2017-12-01,118.25981140136719,40.3528938293457,80.95279693603516,24.43583106994629,58.4734992980957,22.65203857421875,1053.4000244140625,175.24000549316406,2673.610107421875,6903.39013671875 -2018-01-01,126.18390655517578,39.9236946105957,89.91493225097656,28.855159759521484,72.54450225830078,19.982173919677734,1182.219970703125,199.75999450683594,2823.81005859375,7411.47998046875 -2018-02-01,120.11751556396484,42.472713470458984,88.74141693115234,25.634004592895508,75.62249755859375,20.7039852142334,1103.9200439453125,209.1300048828125,2713.830078125,7273.009765625 -2018-02-08,,,,,,,,,, -2018-03-01,119.43197631835938,40.170265197753906,86.7812271118164,24.33201026916504,72.36699676513672,20.402997970581055,1037.1400146484375,216.0800018310547,2640.8701171875,7063.4501953125 -2018-04-01,112.83880615234375,39.566917419433594,88.92057037353516,26.82056999206543,78.30650329589844,20.00168228149414,1018.5800170898438,221.60000610351562,2648.050048828125,7066.27001953125 -2018-05-01,109.99760437011719,44.7408332824707,93.97891998291016,23.179109573364258,81.48100280761719,22.479249954223633,1100.0,249.27999877929688,2705.27001953125,7442.1201171875 -2018-05-09,,,,,,,,,, -2018-06-01,109.95153045654297,44.4903450012207,94.16664123535156,20.467208862304688,84.98999786376953,23.571720123291016,1129.18994140625,243.80999755859375,2718.3701171875,7510.2998046875 -2018-07-01,114.06781768798828,45.73533630371094,101.30004119873047,22.375450134277344,88.87200164794922,25.784528732299805,1227.219970703125,244.67999267578125,2816.2900390625,7671.7900390625 -2018-08-01,115.2877197265625,54.709842681884766,107.2684097290039,24.00385284423828,100.635498046875,26.8017520904541,1231.800048828125,263.510009765625,2901.52001953125,8109.5400390625 -2018-08-09,,,,,,,,,, -2018-09-01,120.2962646484375,54.445865631103516,109.63678741455078,23.24565315246582,100.1500015258789,27.066509246826172,1207.0799560546875,269.95001220703125,2913.97998046875,8046.35009765625 -2018-10-01,91.83122253417969,52.78649139404297,102.38966369628906,24.236047744750977,79.90049743652344,25.190916061401367,1090.5799560546875,245.75999450683594,2711.739990234375,7305.89990234375 -2018-11-01,98.86396026611328,43.07142639160156,106.3008041381836,23.4099178314209,84.50849914550781,29.396371841430664,1109.6500244140625,250.88999938964844,2760.169921875,7330.5400390625 -2018-11-08,,,,,,,,,, -2018-12-01,91.58279418945312,38.17780685424805,97.78714752197266,17.18350601196289,75.09850311279297,24.59708595275879,1044.9599609375,226.24000549316406,2506.85009765625,6635.27978515625 -2019-01-01,108.30086517333984,40.28346252441406,100.5406265258789,24.84735679626465,85.9365005493164,24.456159591674805,1125.8900146484375,247.82000732421875,2704.10009765625,7281.740234375 -2019-02-01,111.28997039794922,41.90748596191406,107.85758972167969,27.216707229614258,81.99150085449219,28.095136642456055,1126.550048828125,262.5,2784.489990234375,7532.52978515625 -2019-02-07,,,,,,,,,, -2019-03-01,115.00740814208984,46.17075729370117,114.03240966796875,28.167972564697266,89.0374984741211,29.539655685424805,1176.8900146484375,266.489990234375,2834.39990234375,7729.31982421875 -2019-04-01,114.33090209960938,48.77644348144531,126.27296447753906,29.61660385131836,96.32599639892578,33.9285774230957,1198.9599609375,289.25,2945.830078125,8095.39013671875 -2019-05-01,103.50668334960938,42.55390930175781,119.58222198486328,27.175188064575195,88.75350189208984,29.972509384155273,1106.5,270.8999938964844,2752.06005859375,7453.14990234375 -2019-05-09,,,,,,,,,, -2019-06-01,113.73433685302734,48.293270111083984,130.00108337402344,31.43656349182129,94.68150329589844,25.56848907470703,1082.800048828125,294.6499938964844,2941.760009765625,8006.240234375 -2019-07-01,122.26231384277344,51.98261260986328,132.24281311035156,28.701255798339844,93.33899688720703,29.061506271362305,1218.199951171875,298.8599853515625,2980.3798828125,8175.419921875 -2019-08-01,111.7796401977539,50.93339920043945,133.78579711914062,25.92054557800293,88.81449890136719,25.9359073638916,1190.530029296875,284.510009765625,2926.4599609375,7962.8798828125 -2019-08-08,,,,,,,,,, -2019-09-01,121.34967803955078,54.85721969604492,135.37051391601562,26.743131637573242,86.79550170898438,26.10200309753418,1221.1400146484375,276.25,2976.739990234375,7999.33984375 -2019-10-01,111.59463500976562,60.929054260253906,139.59625244140625,30.58913803100586,88.83300018310547,26.620418548583984,1258.800048828125,277.92999267578125,3037.56005859375,8292.3603515625 -2019-11-01,112.19547271728516,65.45783996582031,147.39544677734375,35.09682083129883,90.04000091552734,24.40582847595215,1304.0899658203125,309.5299987792969,3140.97998046875,8665.4697265625 -2019-11-07,,,,,,,,,, -2019-12-01,113.17443084716797,72.13994598388672,154.07154846191406,33.23965072631836,92.39199829101562,25.86544418334961,1339.3900146484375,329.80999755859375,3230.780029296875,8972.599609375 -2020-01-01,121.35601806640625,76.03622436523438,166.31326293945312,32.28398132324219,100.43599700927734,24.546754837036133,1432.780029296875,351.1400146484375,3225.52001953125,9150.9404296875 -2020-02-01,109.88997650146484,67.1553726196289,158.28240966796875,29.225305557250977,94.1875,20.364192962646484,1339.25,345.1199951171875,2954.219970703125,8567.3701171875 -2020-02-07,,,,,,,,,, -2020-03-01,94.6399154663086,62.61878204345703,154.502197265625,17.190288543701172,97.48600006103516,19.90617561340332,1161.949951171875,318.239990234375,2584.590087890625,7700.10009765625 -2020-04-01,107.12150573730469,72.34808349609375,175.5648956298828,16.6003360748291,123.69999694824219,21.486589431762695,1346.699951171875,353.6400146484375,2912.429931640625,8889.5498046875 -2020-05-01,106.55841827392578,78.29256439208984,179.52272033691406,14.412978172302246,122.11849975585938,24.98464012145996,1433.52001953125,386.6000061035156,3044.31005859375,9489.8701171875 -2020-05-07,,,,,,,,,, -2020-06-01,104.41674041748047,90.0749740600586,199.92588806152344,13.877481460571289,137.9409942626953,27.652219772338867,1418.050048828125,435.30999755859375,3100.2900390625,10058.76953125 -2020-07-01,106.29290008544922,104.9491958618164,201.3994598388672,15.366937637329102,158.23399353027344,30.11343765258789,1487.949951171875,444.32000732421875,3271.1201171875,10745.26953125 -2020-08-01,106.61280059814453,127.44819641113281,221.55807495117188,17.406635284423828,172.54800415039062,33.25917053222656,1629.530029296875,513.3900146484375,3500.31005859375,11775.4599609375 -2020-08-07,,,,,,,,,, -2020-09-01,106.57222747802734,114.5876235961914,207.12527465820312,17.323570251464844,157.43649291992188,34.06950759887695,1465.5999755859375,490.42999267578125,3363.0,11167.509765625 -2020-10-01,97.80435180664062,107.71099090576172,199.38504028320312,16.259458541870117,151.8074951171875,30.329862594604492,1616.1099853515625,447.1000061035156,3269.9599609375,10911.58984375 -2020-11-01,108.19266510009766,117.79344177246094,210.8082733154297,20.478687286376953,158.40199279785156,34.74394989013672,1754.4000244140625,478.4700012207031,3621.6298828125,12198.740234375 -2020-11-09,,,,,,,,,, -2020-12-01,111.85865020751953,131.51597595214844,219.60447692871094,21.694869995117188,162.84649658203125,36.88808059692383,1752.6400146484375,500.1199951171875,3756.070068359375,12888.2802734375 -2021-01-01,105.84272766113281,130.7924346923828,229.0237274169922,19.891191482543945,160.30999755859375,36.6867561340332,1827.3599853515625,458.7699890136719,3714.239990234375,13070.6904296875 -2021-02-01,105.68277740478516,120.18710327148438,229.4384002685547,24.100215911865234,154.64649963378906,40.80388259887695,2021.9100341796875,459.6700134277344,3811.14990234375,13192.349609375 -2021-02-09,,,,,,,,,, -2021-03-01,119.9990005493164,121.25013732910156,233.32164001464844,22.955739974975586,154.70399475097656,44.367366790771484,2062.52001953125,475.3699951171875,3972.889892578125,13246.8701171875 -2021-04-01,127.76121520996094,130.49156188964844,249.56121826171875,23.070621490478516,173.37100219726562,49.49113082885742,2353.5,508.3399963378906,4181.169921875,13962.6796875 -2021-05-01,129.4361114501953,123.6920166015625,247.08718872070312,22.41118812561035,161.15350341796875,49.64715576171875,2356.85009765625,504.5799865722656,4204.10986328125,13748.740234375 -2021-05-07,,,,,,,,,, -2021-06-01,133.47738647460938,136.1819610595703,268.70587158203125,22.44941520690918,172.00799560546875,50.16557312011719,2441.7900390625,585.6400146484375,4297.5,14503.9501953125 -2021-07-01,128.35101318359375,145.03138732910156,282.6023864746094,23.305561065673828,166.37950134277344,48.63045883178711,2694.530029296875,621.6300048828125,4395.259765625,14672.6796875 -2021-08-01,127.78646087646484,150.96749877929688,299.4349365234375,21.74091148376465,173.5395050048828,49.053245544433594,2893.949951171875,663.7000122070312,4522.68017578125,15259.240234375 -2021-08-09,,,,,,,,,, -2021-09-01,127.95899963378906,140.906982421875,280.1719665527344,19.48086166381836,164.2519989013672,52.36507034301758,2673.52001953125,575.719970703125,4307.5400390625,14448.580078125 -2021-10-01,115.22111511230469,149.1721954345703,329.56378173828125,17.397972106933594,168.6215057373047,55.35980224609375,2960.919921875,650.3599853515625,4605.3798828125,15498.3896484375 -2021-11-01,112.8140869140625,164.60723876953125,328.5401611328125,18.003969192504883,175.35350036621094,56.077186584472656,2837.949951171875,669.8499755859375,4567.0,15537.6904296875 -2021-11-04,,,,,,,,,, -2021-11-09,,,,,,,,,, -2021-12-01,130.48629760742188,177.08387756347656,334.8461608886719,22.1286563873291,166.7169952392578,55.77927017211914,2897.0400390625,567.0599975585938,4766.18017578125,15644.9697265625 -2022-01-01,130.3984375,174.30149841308594,309.6171875,20.856517791748047,149.57350158691406,56.41482162475586,2706.070068359375,534.2999877929688,4515.5498046875,14239.8798828125 -2022-02-01,119.60104370117188,164.66795349121094,297.4805908203125,19.47332763671875,153.56300354003906,50.60551452636719,2701.139892578125,467.67999267578125,4373.93994140625,13751.400390625 -2022-02-10,,,,,,,,,, -2022-03-01,128.46168518066406,174.3538360595703,307.59356689453125,19.927804946899414,162.99749755859375,49.84086990356445,2781.35009765625,455.6199951171875,4530.41015625,14220.51953125 -2022-04-01,130.6254425048828,157.418701171875,276.8751220703125,17.399999618530273,124.28150177001953,46.68299102783203,2282.18994140625,395.95001220703125,4131.93017578125,12334.6396484375 -2022-05-01,137.1759796142578,148.6216278076172,271.2382507324219,18.81999969482422,120.20950317382812,49.939998626708984,2275.239990234375,416.4800109863281,4132.14990234375,12081.3896484375 -2022-05-09,,,,,,,,,, -2022-06-01,141.86000061035156,137.44000244140625,256.4800109863281,15.819999694824219,107.4000015258789,48.939998626708984,2240.14990234375,365.6300048828125,3821.550048828125,11181.5400390625 -2022-06-28,141.86000061035156,137.44000244140625,256.4800109863281,15.819999694824219,107.4000015258789,48.939998626708984,2240.14990234375,365.6300048828125,3821.550048828125,11181.5400390625 diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/axes_grid/bivariate_normal.npy b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/axes_grid/bivariate_normal.npy deleted file mode 100644 index b6b8dac..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/axes_grid/bivariate_normal.npy and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/data_x_x2_x3.csv b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/data_x_x2_x3.csv deleted file mode 100644 index 521da14..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/data_x_x2_x3.csv +++ /dev/null @@ -1,11 +0,0 @@ - 0 0 0 - 1 1 1 - 2 4 8 - 3 9 27 - 4 16 64 - 5 25 125 - 6 36 216 - 7 49 343 - 8 64 512 - 9 81 729 -10 100 1000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/eeg.dat b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/eeg.dat deleted file mode 100644 index c666c65..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/eeg.dat and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/embedding_in_wx3.xrc b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/embedding_in_wx3.xrc deleted file mode 100644 index 220656d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/embedding_in_wx3.xrc +++ /dev/null @@ -1,65 +0,0 @@ - - - - embedding_in_wx3 - - - wxVERTICAL - - - - - - - wxALL|wxEXPAND - 5 - - - - wxHORIZONTAL - - - - - - wxALL|wxEXPAND - 2 - - - - - - - wxALL|wxEXPAND - 2 - - - - - - - - wxALL|wxEXPAND - 2 - - - - 0 - - - wxEXPAND - - - - wxLEFT|wxRIGHT|wxEXPAND - 5 - - - - - wxEXPAND - - - - - \ No newline at end of file diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/goog.npz b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/goog.npz deleted file mode 100644 index 6cbfd68..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/goog.npz and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/grace_hopper.jpg b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/grace_hopper.jpg deleted file mode 100644 index 478720d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/grace_hopper.jpg and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/jacksboro_fault_dem.npz b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/jacksboro_fault_dem.npz deleted file mode 100644 index d250286..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/jacksboro_fault_dem.npz and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/logo2.png b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/logo2.png deleted file mode 100644 index a1adda4..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/logo2.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/membrane.dat b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/membrane.dat deleted file mode 100644 index 68f5e6b..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/membrane.dat and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/msft.csv b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/msft.csv deleted file mode 100644 index 727b1be..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/msft.csv +++ /dev/null @@ -1,66 +0,0 @@ -Date,Open,High,Low,Close,Volume,Adj. Close* -19-Sep-03,29.76,29.97,29.52,29.96,92433800,29.79 -18-Sep-03,28.49,29.51,28.42,29.50,67268096,29.34 -17-Sep-03,28.76,28.95,28.47,28.50,47221600,28.34 -16-Sep-03,28.41,28.95,28.32,28.90,52060600,28.74 -15-Sep-03,28.37,28.61,28.33,28.36,41432300,28.20 -12-Sep-03,27.48,28.40,27.45,28.34,55777200,28.18 -11-Sep-03,27.66,28.11,27.59,27.84,37813300,27.68 -10-Sep-03,28.03,28.18,27.48,27.55,54763500,27.40 -9-Sep-03,28.65,28.71,28.31,28.37,44315200,28.21 -8-Sep-03,28.39,28.92,28.34,28.84,46105300,28.68 -5-Sep-03,28.23,28.75,28.17,28.38,64024500,28.22 -4-Sep-03,28.10,28.47,27.99,28.43,59840800,28.27 -3-Sep-03,27.42,28.40,27.38,28.30,109437800,28.14 -2-Sep-03,26.70,27.30,26.47,27.26,74168896,27.11 -29-Aug-03,26.46,26.55,26.35,26.52,34503000,26.37 -28-Aug-03,26.50,26.58,26.24,26.51,46211200,26.36 -27-Aug-03,26.51,26.58,26.30,26.42,30633900,26.27 -26-Aug-03,26.31,26.67,25.96,26.57,47546000,26.42 -25-Aug-03,26.31,26.54,26.23,26.50,36132900,26.35 -22-Aug-03,26.78,26.95,26.21,26.22,65846300,26.07 -21-Aug-03,26.65,26.73,26.13,26.24,63802700,26.09 -20-Aug-03,26.30,26.53,26.00,26.45,56739300,26.30 -19-Aug-03,25.85,26.65,25.77,26.62,72952896,26.47 -18-Aug-03,25.56,25.83,25.46,25.70,45817400,25.56 -15-Aug-03,25.61,25.66,25.43,25.54,27607900,25.40 -14-Aug-03,25.66,25.71,25.52,25.63,37338300,25.49 -13-Aug-03,25.79,25.89,25.50,25.60,39636900,25.46 -12-Aug-03,25.71,25.77,25.45,25.73,38208400,25.59 -11-Aug-03,25.61,25.99,25.54,25.61,36433900,25.47 -8-Aug-03,25.88,25.98,25.50,25.58,33241400,25.44 -7-Aug-03,25.72,25.81,25.45,25.71,44258500,25.57 -6-Aug-03,25.54,26.19,25.43,25.65,56294900,25.51 -5-Aug-03,26.31,26.54,25.60,25.66,58825800,25.52 -4-Aug-03,26.15,26.41,25.75,26.18,51825600,26.03 -1-Aug-03,26.33,26.51,26.12,26.17,42649700,26.02 -31-Jul-03,26.60,26.99,26.31,26.41,64504800,26.26 -30-Jul-03,26.46,26.57,26.17,26.23,41240300,26.08 -29-Jul-03,26.88,26.90,26.24,26.47,62391100,26.32 -28-Jul-03,26.94,27.00,26.49,26.61,52658300,26.46 -25-Jul-03,26.28,26.95,26.07,26.89,54173000,26.74 -24-Jul-03,26.78,26.92,25.98,26.00,53556600,25.85 -23-Jul-03,26.42,26.65,26.14,26.45,49828200,26.30 -22-Jul-03,26.28,26.56,26.13,26.38,51791000,26.23 -21-Jul-03,26.87,26.91,26.00,26.04,48480800,25.89 -18-Jul-03,27.11,27.23,26.75,26.89,63388400,26.74 -17-Jul-03,27.14,27.27,26.54,26.69,72805000,26.54 -16-Jul-03,27.56,27.62,27.20,27.52,49838900,27.37 -15-Jul-03,27.47,27.53,27.10,27.27,53567600,27.12 -14-Jul-03,27.63,27.81,27.05,27.40,60464400,27.25 -11-Jul-03,26.95,27.45,26.89,27.31,50377300,27.16 -10-Jul-03,27.25,27.42,26.59,26.91,55350800,26.76 -9-Jul-03,27.56,27.70,27.25,27.47,62300700,27.32 -8-Jul-03,27.26,27.80,27.25,27.70,61896800,27.55 -7-Jul-03,27.02,27.55,26.95,27.42,88960800,27.27 -3-Jul-03,26.69,26.95,26.41,26.50,39440900,26.35 -2-Jul-03,26.50,26.93,26.45,26.88,94069296,26.73 -1-Jul-03,25.59,26.20,25.39,26.15,60926000,26.00 -30-Jun-03,25.94,26.12,25.50,25.64,48073100,25.50 -27-Jun-03,25.95,26.34,25.53,25.63,76040304,25.49 -26-Jun-03,25.39,26.51,25.21,25.75,51758100,25.61 -25-Jun-03,25.64,25.99,25.14,25.26,60483500,25.12 -24-Jun-03,25.65,26.04,25.52,25.70,51820300,25.56 -23-Jun-03,26.14,26.24,25.49,25.78,52584500,25.64 -20-Jun-03,26.34,26.38,26.01,26.33,86048896,26.18 -19-Jun-03,26.09,26.39,26.01,26.07,63626900,25.92 \ No newline at end of file diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/percent_bachelors_degrees_women_usa.csv b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/percent_bachelors_degrees_women_usa.csv deleted file mode 100644 index 1e488d0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/percent_bachelors_degrees_women_usa.csv +++ /dev/null @@ -1,43 +0,0 @@ -Year,Agriculture,Architecture,Art and Performance,Biology,Business,Communications and Journalism,Computer Science,Education,Engineering,English,Foreign Languages,Health Professions,Math and Statistics,Physical Sciences,Psychology,Public Administration,Social Sciences and History -1970,4.22979798,11.92100539,59.7,29.08836297,9.064438975,35.3,13.6,74.53532758,0.8,65.57092343,73.8,77.1,38,13.8,44.4,68.4,36.8 -1971,5.452796685,12.00310559,59.9,29.39440285,9.503186594,35.5,13.6,74.14920369,1,64.55648516,73.9,75.5,39,14.9,46.2,65.5,36.2 -1972,7.42071022,13.21459351,60.4,29.81022105,10.5589621,36.6,14.9,73.55451996,1.2,63.6642632,74.6,76.9,40.2,14.8,47.6,62.6,36.1 -1973,9.653602412,14.7916134,60.2,31.14791477,12.80460152,38.4,16.4,73.50181443,1.6,62.94150212,74.9,77.4,40.9,16.5,50.4,64.3,36.4 -1974,14.07462346,17.44468758,61.9,32.99618284,16.20485038,40.5,18.9,73.33681143,2.2,62.41341209,75.3,77.9,41.8,18.2,52.6,66.1,37.3 -1975,18.33316153,19.13404767,60.9,34.44990213,19.68624931,41.5,19.8,72.80185448,3.2,61.64720641,75,78.9,40.7,19.1,54.5,63,37.7 -1976,22.25276005,21.39449143,61.3,36.07287146,23.4300375,44.3,23.9,72.16652471,4.5,62.14819377,74.4,79.2,41.5,20,56.9,65.6,39.2 -1977,24.6401766,23.74054054,62,38.33138629,27.16342715,46.9,25.7,72.45639481,6.8,62.72306675,74.3,80.5,41.1,21.3,59,69.3,40.5 -1978,27.14619175,25.84923973,62.5,40.11249564,30.52751868,49.9,28.1,73.19282134,8.4,63.61912216,74.3,81.9,41.6,22.5,61.3,71.5,41.8 -1979,29.63336549,27.77047744,63.2,42.06555109,33.62163381,52.3,30.2,73.82114234,9.4,65.08838972,74.2,82.3,42.3,23.7,63.3,73.3,43.6 -1980,30.75938956,28.08038075,63.4,43.99925716,36.76572529,54.7,32.5,74.98103152,10.3,65.28413007,74.1,83.5,42.8,24.6,65.1,74.6,44.2 -1981,31.31865519,29.84169408,63.3,45.24951206,39.26622984,56.4,34.8,75.84512345,11.6,65.83832154,73.9,84.1,43.2,25.7,66.9,74.7,44.6 -1982,32.63666364,34.81624758,63.1,45.96733794,41.94937335,58,36.3,75.84364914,12.4,65.84735212,72.7,84.4,44,27.3,67.5,76.8,44.6 -1983,31.6353471,35.82625735,62.4,46.71313451,43.54206966,58.6,37.1,75.95060123,13.1,65.91837999,71.8,84.6,44.3,27.6,67.9,76.1,44.1 -1984,31.09294748,35.45308311,62.1,47.66908276,45.12403027,59.1,36.8,75.86911601,13.5,65.74986233,72.1,85.1,46.2,28,68.2,75.9,44.1 -1985,31.3796588,36.13334795,61.8,47.9098841,45.747782,59,35.7,75.92343971,13.5,65.79819852,70.8,85.3,46.5,27.5,69,75,43.8 -1986,31.19871923,37.24022346,62.1,48.30067763,46.53291505,60,34.7,76.14301516,13.9,65.98256091,71.2,85.7,46.7,28.4,69,75.7,44 -1987,31.48642948,38.73067535,61.7,50.20987789,46.69046648,60.2,32.4,76.96309168,14,66.70603055,72,85.5,46.5,30.4,70.1,76.4,43.9 -1988,31.08508746,39.3989071,61.7,50.09981147,46.7648277,60.4,30.8,77.62766177,13.9,67.14449816,72.3,85.2,46.2,29.7,70.9,75.6,44.4 -1989,31.6124031,39.09653994,62,50.77471585,46.7815648,60.5,29.9,78.11191872,14.1,67.01707156,72.4,84.6,46.2,31.3,71.6,76,44.2 -1990,32.70344407,40.82404662,62.6,50.81809432,47.20085084,60.8,29.4,78.86685859,14.1,66.92190193,71.2,83.9,47.3,31.6,72.6,77.6,45.1 -1991,34.71183749,33.67988118,62.1,51.46880537,47.22432481,60.8,28.7,78.99124597,14,66.24147465,71.1,83.5,47,32.6,73.2,78.2,45.5 -1992,33.93165961,35.20235628,61,51.34974154,47.21939541,59.7,28.2,78.43518191,14.5,65.62245655,71,83,47.4,32.6,73.2,77.3,45.8 -1993,34.94683208,35.77715877,60.2,51.12484404,47.63933161,58.7,28.5,77.26731199,14.9,65.73095014,70,82.4,46.4,33.6,73.1,78,46.1 -1994,36.03267447,34.43353129,59.4,52.2462176,47.98392441,58.1,28.5,75.81493264,15.7,65.64197772,69.1,81.8,47,34.8,72.9,78.8,46.8 -1995,36.84480747,36.06321839,59.2,52.59940342,48.57318101,58.8,27.5,75.12525621,16.2,65.93694921,69.6,81.5,46.1,35.9,73,78.8,47.9 -1996,38.96977475,35.9264854,58.6,53.78988011,48.6473926,58.7,27.1,75.03519921,16.7,66.43777883,69.7,81.3,46.4,37.3,73.9,79.8,48.7 -1997,40.68568483,35.10193413,58.7,54.99946903,48.56105033,60,26.8,75.1637013,17,66.78635548,70,81.9,47,38.3,74.4,81,49.2 -1998,41.91240333,37.59854457,59.1,56.35124789,49.2585152,60,27,75.48616027,17.8,67.2554484,70.1,82.1,48.3,39.7,75.1,81.3,50.5 -1999,42.88720191,38.63152919,59.2,58.22882288,49.81020815,61.2,28.1,75.83816206,18.6,67.82022113,70.9,83.5,47.8,40.2,76.5,81.1,51.2 -2000,45.05776637,40.02358491,59.2,59.38985737,49.80361649,61.9,27.7,76.69214284,18.4,68.36599498,70.9,83.5,48.2,41,77.5,81.1,51.8 -2001,45.86601517,40.69028156,59.4,60.71233149,50.27514494,63,27.6,77.37522931,19,68.57852029,71.2,85.1,47,42.2,77.5,80.9,51.7 -2002,47.13465821,41.13295053,60.9,61.8951284,50.5523346,63.7,27,78.64424394,18.7,68.82995959,70.5,85.8,45.7,41.1,77.7,81.3,51.5 -2003,47.93518721,42.75854266,61.1,62.1694558,50.34559774,64.6,25.1,78.54494815,18.8,68.89448726,70.6,86.5,46,41.7,77.8,81.5,50.9 -2004,47.88714025,43.46649345,61.3,61.91458697,49.95089449,64.2,22.2,78.65074774,18.2,68.45473436,70.8,86.5,44.7,42.1,77.8,80.7,50.5 -2005,47.67275409,43.10036784,61.4,61.50098432,49.79185139,63.4,20.6,79.06712173,17.9,68.57122114,69.9,86,45.1,41.6,77.5,81.2,50 -2006,46.79029957,44.49933107,61.6,60.17284465,49.21091439,63,18.6,78.68630551,16.8,68.29759443,69.6,85.9,44.1,40.8,77.4,81.2,49.8 -2007,47.60502633,43.10045895,61.4,59.41199314,49.00045935,62.5,17.6,78.72141311,16.8,67.87492278,70.2,85.4,44.1,40.7,77.1,82.1,49.3 -2008,47.570834,42.71173041,60.7,59.30576517,48.88802678,62.4,17.8,79.19632674,16.5,67.59402834,70.2,85.2,43.3,40.7,77.2,81.7,49.4 -2009,48.66722357,43.34892051,61,58.48958333,48.84047414,62.8,18.1,79.5329087,16.8,67.96979204,69.3,85.1,43.3,40.7,77.1,82,49.4 -2010,48.73004227,42.06672091,61.3,59.01025521,48.75798769,62.5,17.6,79.61862451,17.2,67.92810557,69,85,43.1,40.2,77,81.7,49.3 -2011,50.03718193,42.7734375,61.2,58.7423969,48.18041792,62.2,18.2,79.43281184,17.5,68.42673015,69.5,84.8,43.1,40.1,76.7,81.9,49.2 \ No newline at end of file diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/s1045.ima.gz b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/s1045.ima.gz deleted file mode 100644 index 347db4c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/s1045.ima.gz and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/topobathy.npz b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/topobathy.npz deleted file mode 100644 index 9f9b085..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/sample_data/topobathy.npz and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/Solarize_Light2.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/Solarize_Light2.mplstyle deleted file mode 100644 index 4187213..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/Solarize_Light2.mplstyle +++ /dev/null @@ -1,53 +0,0 @@ -# Solarized color palette taken from https://ethanschoonover.com/solarized/ -# Inspired by, and copied from ggthemes https://github.com/jrnold/ggthemes - -#TODO: -# 1. Padding to title from face -# 2. Remove top & right ticks -# 3. Give Title a Magenta Color(?) - -#base00 ='#657b83' -#base01 ='#93a1a1' -#base2 ='#eee8d5' -#base3 ='#fdf6e3' -#base01 ='#586e75' -#Magenta ='#d33682' -#Blue ='#268bd2' -#cyan ='#2aa198' -#violet ='#6c71c4' -#green ='#859900' -#orange ='#cb4b16' - -figure.facecolor : FDF6E3 - -patch.antialiased : True - -lines.linewidth : 2.0 -lines.solid_capstyle: butt - -axes.titlesize : 16 -axes.labelsize : 12 -axes.labelcolor : 657b83 -axes.facecolor : eee8d5 -axes.edgecolor : eee8d5 -axes.axisbelow : True -axes.prop_cycle : cycler('color', ['268BD2', '2AA198', '859900', 'B58900', 'CB4B16', 'DC322F', 'D33682', '6C71C4']) -# Blue -# Cyan -# Green -# Yellow -# Orange -# Red -# Magenta -# Violet -axes.grid : True -grid.color : fdf6e3 # grid color -grid.linestyle : - # line -grid.linewidth : 1 # in points - -### TICKS -xtick.color : 657b83 -xtick.direction : out - -ytick.color : 657b83 -ytick.direction : out diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/_classic_test_patch.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/_classic_test_patch.mplstyle deleted file mode 100644 index 96f62f4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/_classic_test_patch.mplstyle +++ /dev/null @@ -1,6 +0,0 @@ -# This patch should go on top of the "classic" style and exists solely to avoid -# changing baseline images. - -text.kerning_factor : 6 - -ytick.alignment: center_baseline diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/_mpl-gallery-nogrid.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/_mpl-gallery-nogrid.mplstyle deleted file mode 100644 index 911658f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/_mpl-gallery-nogrid.mplstyle +++ /dev/null @@ -1,19 +0,0 @@ -# This style is used for the plot_types gallery. It is considered private. - -axes.grid: False -axes.axisbelow: True - -figure.figsize: 2, 2 -# make it so the axes labels don't show up. Obviously -# not good style for any quantitative analysis: -figure.subplot.left: 0.01 -figure.subplot.right: 0.99 -figure.subplot.bottom: 0.01 -figure.subplot.top: 0.99 - -xtick.major.size: 0.0 -ytick.major.size: 0.0 - -# colors: -image.cmap : Blues -axes.prop_cycle: cycler('color', ['1f77b4', '82bbdb', 'ccdff1']) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/_mpl-gallery.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/_mpl-gallery.mplstyle deleted file mode 100644 index 75c95bf..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/_mpl-gallery.mplstyle +++ /dev/null @@ -1,19 +0,0 @@ -# This style is used for the plot_types gallery. It is considered part of the private API. - -axes.grid: True -axes.axisbelow: True - -figure.figsize: 2, 2 -# make it so the axes labels don't show up. Obviously -# not good style for any quantitative analysis: -figure.subplot.left: 0.01 -figure.subplot.right: 0.99 -figure.subplot.bottom: 0.01 -figure.subplot.top: 0.99 - -xtick.major.size: 0.0 -ytick.major.size: 0.0 - -# colors: -image.cmap : Blues -axes.prop_cycle: cycler('color', ['1f77b4', '58a1cf', 'abd0e6']) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/bmh.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/bmh.mplstyle deleted file mode 100644 index 1b449cc..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/bmh.mplstyle +++ /dev/null @@ -1,29 +0,0 @@ -#Author: Cameron Davidson-Pilon, original styles from Bayesian Methods for Hackers -# https://github.com/CamDavidsonPilon/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers/ - -lines.linewidth : 2.0 - -patch.linewidth: 0.5 -patch.facecolor: blue -patch.edgecolor: eeeeee -patch.antialiased: True - -text.hinting_factor : 8 - -mathtext.fontset : cm - -axes.facecolor: eeeeee -axes.edgecolor: bcbcbc -axes.grid : True -axes.titlesize: x-large -axes.labelsize: large -axes.prop_cycle: cycler('color', ['348ABD', 'A60628', '7A68A6', '467821', 'D55E00', 'CC79A7', '56B4E9', '009E73', 'F0E442', '0072B2']) - -grid.color: b2b2b2 -grid.linestyle: -- -grid.linewidth: 0.5 - -legend.fancybox: True - -xtick.direction: in -ytick.direction: in diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/classic.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/classic.mplstyle deleted file mode 100644 index 6f65e8b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/classic.mplstyle +++ /dev/null @@ -1,492 +0,0 @@ -### Classic matplotlib plotting style as of v1.5 - - -### LINES -# See https://matplotlib.org/api/artist_api.html#module-matplotlib.lines for more -# information on line properties. -lines.linewidth : 1.0 # line width in points -lines.linestyle : - # solid line -lines.color : b # has no affect on plot(); see axes.prop_cycle -lines.marker : None # the default marker -lines.markerfacecolor : auto # the default markerfacecolor -lines.markeredgecolor : auto # the default markeredgecolor -lines.markeredgewidth : 0.5 # the line width around the marker symbol -lines.markersize : 6 # markersize, in points -lines.dash_joinstyle : round # miter|round|bevel -lines.dash_capstyle : butt # butt|round|projecting -lines.solid_joinstyle : round # miter|round|bevel -lines.solid_capstyle : projecting # butt|round|projecting -lines.antialiased : True # render lines in antialiased (no jaggies) -lines.dashed_pattern : 6, 6 -lines.dashdot_pattern : 3, 5, 1, 5 -lines.dotted_pattern : 1, 3 -lines.scale_dashes: False - -### Marker props -markers.fillstyle: full - -### PATCHES -# Patches are graphical objects that fill 2D space, like polygons or -# circles. See -# https://matplotlib.org/api/artist_api.html#module-matplotlib.patches -# information on patch properties -patch.linewidth : 1.0 # edge width in points -patch.facecolor : b -patch.force_edgecolor : True -patch.edgecolor : k -patch.antialiased : True # render patches in antialiased (no jaggies) - -hatch.color : k -hatch.linewidth : 1.0 - -hist.bins : 10 - -### FONT -# -# font properties used by text.Text. See -# https://matplotlib.org/api/font_manager_api.html for more -# information on font properties. The 6 font properties used for font -# matching are given below with their default values. -# -# The font.family property has five values: 'serif' (e.g., Times), -# 'sans-serif' (e.g., Helvetica), 'cursive' (e.g., Zapf-Chancery), -# 'fantasy' (e.g., Western), and 'monospace' (e.g., Courier). Each of -# these font families has a default list of font names in decreasing -# order of priority associated with them. When text.usetex is False, -# font.family may also be one or more concrete font names. -# -# The font.style property has three values: normal (or roman), italic -# or oblique. The oblique style will be used for italic, if it is not -# present. -# -# The font.variant property has two values: normal or small-caps. For -# TrueType fonts, which are scalable fonts, small-caps is equivalent -# to using a font size of 'smaller', or about 83% of the current font -# size. -# -# The font.weight property has effectively 13 values: normal, bold, -# bolder, lighter, 100, 200, 300, ..., 900. Normal is the same as -# 400, and bold is 700. bolder and lighter are relative values with -# respect to the current weight. -# -# The font.stretch property has 11 values: ultra-condensed, -# extra-condensed, condensed, semi-condensed, normal, semi-expanded, -# expanded, extra-expanded, ultra-expanded, wider, and narrower. This -# property is not currently implemented. -# -# The font.size property is the default font size for text, given in pts. -# 12pt is the standard value. -# -font.family : sans-serif -font.style : normal -font.variant : normal -font.weight : normal -font.stretch : normal -# note that font.size controls default text sizes. To configure -# special text sizes tick labels, axes, labels, title, etc, see the rc -# settings for axes and ticks. Special text sizes can be defined -# relative to font.size, using the following values: xx-small, x-small, -# small, medium, large, x-large, xx-large, larger, or smaller -font.size : 12.0 -font.serif : DejaVu Serif, New Century Schoolbook, Century Schoolbook L, Utopia, ITC Bookman, Bookman, Nimbus Roman No9 L, Times New Roman, Times, Palatino, Charter, serif -font.sans-serif: DejaVu Sans, Lucida Grande, Verdana, Geneva, Lucid, Arial, Helvetica, Avant Garde, sans-serif -font.cursive : Apple Chancery, Textile, Zapf Chancery, Sand, Script MT, Felipa, cursive -font.fantasy : Comic Sans MS, Chicago, Charcoal, ImpactWestern, Humor Sans, fantasy -font.monospace : DejaVu Sans Mono, Andale Mono, Nimbus Mono L, Courier New, Courier, Fixed, Terminal, monospace - -### TEXT -# text properties used by text.Text. See -# https://matplotlib.org/api/artist_api.html#module-matplotlib.text for more -# information on text properties - -text.color : k - -### LaTeX customizations. See http://www.scipy.org/Wiki/Cookbook/Matplotlib/UsingTex -text.usetex : False # use latex for all text handling. The following fonts - # are supported through the usual rc parameter settings: - # new century schoolbook, bookman, times, palatino, - # zapf chancery, charter, serif, sans-serif, helvetica, - # avant garde, courier, monospace, computer modern roman, - # computer modern sans serif, computer modern typewriter - # If another font is desired which can loaded using the - # LaTeX \usepackage command, please inquire at the - # matplotlib mailing list -text.latex.preamble : # IMPROPER USE OF THIS FEATURE WILL LEAD TO LATEX FAILURES - # AND IS THEREFORE UNSUPPORTED. PLEASE DO NOT ASK FOR HELP - # IF THIS FEATURE DOES NOT DO WHAT YOU EXPECT IT TO. - # text.latex.preamble is a single line of LaTeX code that - # will be passed on to the LaTeX system. It may contain - # any code that is valid for the LaTeX "preamble", i.e. - # between the "\documentclass" and "\begin{document}" - # statements. - # Note that it has to be put on a single line, which may - # become quite long. - # The following packages are always loaded with usetex, so - # beware of package collisions: color, geometry, graphicx, - # type1cm, textcomp. - # Adobe Postscript (PSSNFS) font packages may also be - # loaded, depending on your font settings. - -text.hinting : auto # May be one of the following: - # 'none': Perform no hinting - # 'auto': Use freetype's autohinter - # 'native': Use the hinting information in the - # font file, if available, and if your - # freetype library supports it - # 'either': Use the native hinting information, - # or the autohinter if none is available. - # For backward compatibility, this value may also be - # True === 'auto' or False === 'none'. -text.hinting_factor : 8 # Specifies the amount of softness for hinting in the - # horizontal direction. A value of 1 will hint to full - # pixels. A value of 2 will hint to half pixels etc. - -text.antialiased : True # If True (default), the text will be antialiased. - # This only affects the Agg backend. - -# The following settings allow you to select the fonts in math mode. -# They map from a TeX font name to a fontconfig font pattern. -# These settings are only used if mathtext.fontset is 'custom'. -# Note that this "custom" mode is unsupported and may go away in the -# future. -mathtext.cal : cursive -mathtext.rm : serif -mathtext.tt : monospace -mathtext.it : serif:italic -mathtext.bf : serif:bold -mathtext.sf : sans\-serif -mathtext.fontset : cm # Should be 'cm' (Computer Modern), 'stix', - # 'stixsans' or 'custom' -mathtext.fallback: cm # Select fallback font from ['cm' (Computer Modern), 'stix' - # 'stixsans'] when a symbol can not be found in one of the - # custom math fonts. Select 'None' to not perform fallback - # and replace the missing character by a dummy. - -mathtext.default : it # The default font to use for math. - # Can be any of the LaTeX font names, including - # the special name "regular" for the same font - # used in regular text. - -### AXES -# default face and edge color, default tick sizes, -# default fontsizes for ticklabels, and so on. See -# https://matplotlib.org/api/axes_api.html#module-matplotlib.axes -axes.facecolor : w # axes background color -axes.edgecolor : k # axes edge color -axes.linewidth : 1.0 # edge linewidth -axes.grid : False # display grid or not -axes.grid.which : major -axes.grid.axis : both -axes.titlesize : large # fontsize of the axes title -axes.titley : 1.0 # at the top, no autopositioning. -axes.titlepad : 5.0 # pad between axes and title in points -axes.titleweight : normal # font weight for axes title -axes.labelsize : medium # fontsize of the x any y labels -axes.labelpad : 5.0 # space between label and axis -axes.labelweight : normal # weight of the x and y labels -axes.labelcolor : k -axes.axisbelow : False # whether axis gridlines and ticks are below - # the axes elements (lines, text, etc) - -axes.formatter.limits : -7, 7 # use scientific notation if log10 - # of the axis range is smaller than the - # first or larger than the second -axes.formatter.use_locale : False # When True, format tick labels - # according to the user's locale. - # For example, use ',' as a decimal - # separator in the fr_FR locale. -axes.formatter.use_mathtext : False # When True, use mathtext for scientific - # notation. -axes.formatter.useoffset : True # If True, the tick label formatter - # will default to labeling ticks relative - # to an offset when the data range is very - # small compared to the minimum absolute - # value of the data. -axes.formatter.offset_threshold : 2 # When useoffset is True, the offset - # will be used when it can remove - # at least this number of significant - # digits from tick labels. - -axes.unicode_minus : True # use Unicode for the minus symbol - # rather than hyphen. See - # https://en.wikipedia.org/wiki/Plus_and_minus_signs#Character_codes -axes.prop_cycle : cycler('color', 'bgrcmyk') - # color cycle for plot lines - # as list of string colorspecs: - # single letter, long name, or - # web-style hex -axes.autolimit_mode : round_numbers -axes.xmargin : 0 # x margin. See `axes.Axes.margins` -axes.ymargin : 0 # y margin See `axes.Axes.margins` -axes.spines.bottom : True -axes.spines.left : True -axes.spines.right : True -axes.spines.top : True -polaraxes.grid : True # display grid on polar axes -axes3d.grid : True # display grid on 3d axes - -date.autoformatter.year : %Y -date.autoformatter.month : %b %Y -date.autoformatter.day : %b %d %Y -date.autoformatter.hour : %H:%M:%S -date.autoformatter.minute : %H:%M:%S.%f -date.autoformatter.second : %H:%M:%S.%f -date.autoformatter.microsecond : %H:%M:%S.%f -date.converter: auto # 'auto', 'concise' - -### TICKS -# see https://matplotlib.org/api/axis_api.html#matplotlib.axis.Tick - -xtick.top : True # draw ticks on the top side -xtick.bottom : True # draw ticks on the bottom side -xtick.major.size : 4 # major tick size in points -xtick.minor.size : 2 # minor tick size in points -xtick.minor.visible : False -xtick.major.width : 0.5 # major tick width in points -xtick.minor.width : 0.5 # minor tick width in points -xtick.major.pad : 4 # distance to major tick label in points -xtick.minor.pad : 4 # distance to the minor tick label in points -xtick.color : k # color of the tick labels -xtick.labelsize : medium # fontsize of the tick labels -xtick.direction : in # direction: in, out, or inout -xtick.major.top : True # draw x axis top major ticks -xtick.major.bottom : True # draw x axis bottom major ticks -xtick.minor.top : True # draw x axis top minor ticks -xtick.minor.bottom : True # draw x axis bottom minor ticks -xtick.alignment : center - -ytick.left : True # draw ticks on the left side -ytick.right : True # draw ticks on the right side -ytick.major.size : 4 # major tick size in points -ytick.minor.size : 2 # minor tick size in points -ytick.minor.visible : False -ytick.major.width : 0.5 # major tick width in points -ytick.minor.width : 0.5 # minor tick width in points -ytick.major.pad : 4 # distance to major tick label in points -ytick.minor.pad : 4 # distance to the minor tick label in points -ytick.color : k # color of the tick labels -ytick.labelsize : medium # fontsize of the tick labels -ytick.direction : in # direction: in, out, or inout -ytick.major.left : True # draw y axis left major ticks -ytick.major.right : True # draw y axis right major ticks -ytick.minor.left : True # draw y axis left minor ticks -ytick.minor.right : True # draw y axis right minor ticks -ytick.alignment : center - -### GRIDS -grid.color : k # grid color -grid.linestyle : : # dotted -grid.linewidth : 0.5 # in points -grid.alpha : 1.0 # transparency, between 0.0 and 1.0 - -### Legend -legend.fancybox : False # if True, use a rounded box for the - # legend, else a rectangle -legend.loc : upper right -legend.numpoints : 2 # the number of points in the legend line -legend.fontsize : large -legend.borderpad : 0.4 # border whitespace in fontsize units -legend.markerscale : 1.0 # the relative size of legend markers vs. original -# the following dimensions are in axes coords -legend.labelspacing : 0.5 # the vertical space between the legend entries in fraction of fontsize -legend.handlelength : 2. # the length of the legend lines in fraction of fontsize -legend.handleheight : 0.7 # the height of the legend handle in fraction of fontsize -legend.handletextpad : 0.8 # the space between the legend line and legend text in fraction of fontsize -legend.borderaxespad : 0.5 # the border between the axes and legend edge in fraction of fontsize -legend.columnspacing : 2. # the border between the axes and legend edge in fraction of fontsize -legend.shadow : False -legend.frameon : True # whether or not to draw a frame around legend -legend.framealpha : None # opacity of legend frame -legend.scatterpoints : 3 # number of scatter points -legend.facecolor : inherit # legend background color (when 'inherit' uses axes.facecolor) -legend.edgecolor : inherit # legend edge color (when 'inherit' uses axes.edgecolor) - - - -### FIGURE -# See https://matplotlib.org/api/figure_api.html#matplotlib.figure.Figure -figure.titlesize : medium # size of the figure title -figure.titleweight : normal # weight of the figure title -figure.labelsize: medium # size of the figure label -figure.labelweight: normal # weight of the figure label -figure.figsize : 8, 6 # figure size in inches -figure.dpi : 80 # figure dots per inch -figure.facecolor : 0.75 # figure facecolor; 0.75 is scalar gray -figure.edgecolor : w # figure edgecolor -figure.autolayout : False # When True, automatically adjust subplot - # parameters to make the plot fit the figure -figure.frameon : True - -# The figure subplot parameters. All dimensions are a fraction of the -# figure width or height -figure.subplot.left : 0.125 # the left side of the subplots of the figure -figure.subplot.right : 0.9 # the right side of the subplots of the figure -figure.subplot.bottom : 0.1 # the bottom of the subplots of the figure -figure.subplot.top : 0.9 # the top of the subplots of the figure -figure.subplot.wspace : 0.2 # the amount of width reserved for space between subplots, - # expressed as a fraction of the average axis width -figure.subplot.hspace : 0.2 # the amount of height reserved for space between subplots, - # expressed as a fraction of the average axis height - -### IMAGES -image.aspect : equal # equal | auto | a number -image.interpolation : bilinear # see help(imshow) for options -image.cmap : jet # gray | jet | ... -image.lut : 256 # the size of the colormap lookup table -image.origin : upper # lower | upper -image.resample : False -image.composite_image : True - -### CONTOUR PLOTS -contour.negative_linestyle : dashed # dashed | solid -contour.corner_mask : True - -# errorbar props -errorbar.capsize: 3 - -# scatter props -scatter.marker: o - -### Boxplots -boxplot.bootstrap: None -boxplot.boxprops.color: b -boxplot.boxprops.linestyle: - -boxplot.boxprops.linewidth: 1.0 -boxplot.capprops.color: k -boxplot.capprops.linestyle: - -boxplot.capprops.linewidth: 1.0 -boxplot.flierprops.color: b -boxplot.flierprops.linestyle: none -boxplot.flierprops.linewidth: 1.0 -boxplot.flierprops.marker: + -boxplot.flierprops.markeredgecolor: k -boxplot.flierprops.markerfacecolor: auto -boxplot.flierprops.markersize: 6.0 -boxplot.meanline: False -boxplot.meanprops.color: r -boxplot.meanprops.linestyle: - -boxplot.meanprops.linewidth: 1.0 -boxplot.medianprops.color: r -boxplot.meanprops.marker: s -boxplot.meanprops.markerfacecolor: r -boxplot.meanprops.markeredgecolor: k -boxplot.meanprops.markersize: 6.0 -boxplot.medianprops.linestyle: - -boxplot.medianprops.linewidth: 1.0 -boxplot.notch: False -boxplot.patchartist: False -boxplot.showbox: True -boxplot.showcaps: True -boxplot.showfliers: True -boxplot.showmeans: False -boxplot.vertical: True -boxplot.whiskerprops.color: b -boxplot.whiskerprops.linestyle: -- -boxplot.whiskerprops.linewidth: 1.0 -boxplot.whiskers: 1.5 - -### Agg rendering -### Warning: experimental, 2008/10/10 -agg.path.chunksize : 0 # 0 to disable; values in the range - # 10000 to 100000 can improve speed slightly - # and prevent an Agg rendering failure - # when plotting very large data sets, - # especially if they are very gappy. - # It may cause minor artifacts, though. - # A value of 20000 is probably a good - # starting point. -### SAVING FIGURES -path.simplify : True # When True, simplify paths by removing "invisible" - # points to reduce file size and increase rendering - # speed -path.simplify_threshold : 0.1111111111111111 - # The threshold of similarity below which - # vertices will be removed in the simplification - # process -path.snap : True # When True, rectilinear axis-aligned paths will be snapped to - # the nearest pixel when certain criteria are met. When False, - # paths will never be snapped. -path.sketch : None # May be none, or a 3-tuple of the form (scale, length, - # randomness). - # *scale* is the amplitude of the wiggle - # perpendicular to the line (in pixels). *length* - # is the length of the wiggle along the line (in - # pixels). *randomness* is the factor by which - # the length is randomly scaled. - -# the default savefig params can be different from the display params -# e.g., you may want a higher resolution, or to make the figure -# background white -savefig.dpi : 100 # figure dots per inch -savefig.facecolor : w # figure facecolor when saving -savefig.edgecolor : w # figure edgecolor when saving -savefig.format : png # png, ps, pdf, svg -savefig.bbox : standard # 'tight' or 'standard'. - # 'tight' is incompatible with pipe-based animation - # backends (e.g. 'ffmpeg') but will work with those - # based on temporary files (e.g. 'ffmpeg_file') -savefig.pad_inches : 0.1 # Padding to be used when bbox is set to 'tight' -savefig.transparent : False # setting that controls whether figures are saved with a - # transparent background by default -savefig.orientation : portrait - -# ps backend params -ps.papersize : letter # auto, letter, legal, ledger, A0-A10, B0-B10 -ps.useafm : False # use of afm fonts, results in small files -ps.usedistiller : False # can be: None, ghostscript or xpdf - # Experimental: may produce smaller files. - # xpdf intended for production of publication quality files, - # but requires ghostscript, xpdf and ps2eps -ps.distiller.res : 6000 # dpi -ps.fonttype : 3 # Output Type 3 (Type3) or Type 42 (TrueType) - -# pdf backend params -pdf.compression : 6 # integer from 0 to 9 - # 0 disables compression (good for debugging) -pdf.fonttype : 3 # Output Type 3 (Type3) or Type 42 (TrueType) -pdf.inheritcolor : False -pdf.use14corefonts : False - -# pgf backend params -pgf.texsystem : xelatex -pgf.rcfonts : True -pgf.preamble : - -# svg backend params -svg.image_inline : True # write raster image data directly into the svg file -svg.fonttype : path # How to handle SVG fonts: -# 'none': Assume fonts are installed on the machine where the SVG will be viewed. -# 'path': Embed characters as paths -- supported by most SVG renderers - -# Event keys to interact with figures/plots via keyboard. -# Customize these settings according to your needs. -# Leave the field(s) empty if you don't need a key-map. (i.e., fullscreen : '') - -keymap.fullscreen : f, ctrl+f # toggling -keymap.home : h, r, home # home or reset mnemonic -keymap.back : left, c, backspace # forward / backward keys to enable -keymap.forward : right, v # left handed quick navigation -keymap.pan : p # pan mnemonic -keymap.zoom : o # zoom mnemonic -keymap.save : s, ctrl+s # saving current figure -keymap.quit : ctrl+w, cmd+w # close the current figure -keymap.grid : g # switching on/off a grid in current axes -keymap.yscale : l # toggle scaling of y-axes ('log'/'linear') -keymap.xscale : k, L # toggle scaling of x-axes ('log'/'linear') - -###ANIMATION settings -animation.writer : ffmpeg # MovieWriter 'backend' to use -animation.codec : mpeg4 # Codec to use for writing movie -animation.bitrate: -1 # Controls size/quality tradeoff for movie. - # -1 implies let utility auto-determine -animation.frame_format: png # Controls frame format used by temp files -animation.ffmpeg_path: ffmpeg # Path to ffmpeg binary. Without full path - # $PATH is searched -animation.ffmpeg_args: # Additional arguments to pass to ffmpeg -animation.convert_path: convert # Path to ImageMagick's convert binary. - # On Windows use the full path since convert - # is also the name of a system tool. -animation.convert_args: -animation.html: none - -_internal.classic_mode: True diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/dark_background.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/dark_background.mplstyle deleted file mode 100644 index c4b7741..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/dark_background.mplstyle +++ /dev/null @@ -1,29 +0,0 @@ -# Set black background default line colors to white. - -lines.color: white -patch.edgecolor: white - -text.color: white - -axes.facecolor: black -axes.edgecolor: white -axes.labelcolor: white -axes.prop_cycle: cycler('color', ['8dd3c7', 'feffb3', 'bfbbd9', 'fa8174', '81b1d2', 'fdb462', 'b3de69', 'bc82bd', 'ccebc4', 'ffed6f']) - -xtick.color: white -ytick.color: white - -grid.color: white - -figure.facecolor: black -figure.edgecolor: black - -savefig.facecolor: black -savefig.edgecolor: black - -### Boxplots -boxplot.boxprops.color: white -boxplot.capprops.color: white -boxplot.flierprops.color: white -boxplot.flierprops.markeredgecolor: white -boxplot.whiskerprops.color: white diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/fast.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/fast.mplstyle deleted file mode 100644 index 1f7be7d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/fast.mplstyle +++ /dev/null @@ -1,11 +0,0 @@ -# a small set of changes that will make your plotting FAST (1). -# -# (1) in some cases - -# Maximally simplify lines. -path.simplify: True -path.simplify_threshold: 1.0 - -# chunk up large lines into smaller lines! -# simple trick to avoid those pesky O(>n) algorithms! -agg.path.chunksize: 10000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/fivethirtyeight.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/fivethirtyeight.mplstyle deleted file mode 100644 index 738db39..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/fivethirtyeight.mplstyle +++ /dev/null @@ -1,40 +0,0 @@ -#Author: Cameron Davidson-Pilon, replicated styles from FiveThirtyEight.com -# See https://www.dataorigami.net/blogs/fivethirtyeight-mpl - -lines.linewidth: 4 -lines.solid_capstyle: butt - -legend.fancybox: true - -axes.prop_cycle: cycler('color', ['008fd5', 'fc4f30', 'e5ae38', '6d904f', '8b8b8b', '810f7c']) -axes.facecolor: f0f0f0 -axes.labelsize: large -axes.axisbelow: true -axes.grid: true -axes.edgecolor: f0f0f0 -axes.linewidth: 3.0 -axes.titlesize: x-large - -patch.edgecolor: f0f0f0 -patch.linewidth: 0.5 - -svg.fonttype: path - -grid.linestyle: - -grid.linewidth: 1.0 -grid.color: cbcbcb - -xtick.major.size: 0 -xtick.minor.size: 0 -ytick.major.size: 0 -ytick.minor.size: 0 - -font.size:14.0 - -savefig.edgecolor: f0f0f0 -savefig.facecolor: f0f0f0 - -figure.subplot.left: 0.08 -figure.subplot.right: 0.95 -figure.subplot.bottom: 0.07 -figure.facecolor: f0f0f0 diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/ggplot.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/ggplot.mplstyle deleted file mode 100644 index d1b3ba2..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/ggplot.mplstyle +++ /dev/null @@ -1,39 +0,0 @@ -# from https://everyhue.me/posts/sane-color-scheme-for-matplotlib/ - -patch.linewidth: 0.5 -patch.facecolor: 348ABD # blue -patch.edgecolor: EEEEEE -patch.antialiased: True - -font.size: 10.0 - -axes.facecolor: E5E5E5 -axes.edgecolor: white -axes.linewidth: 1 -axes.grid: True -axes.titlesize: x-large -axes.labelsize: large -axes.labelcolor: 555555 -axes.axisbelow: True # grid/ticks are below elements (e.g., lines, text) - -axes.prop_cycle: cycler('color', ['E24A33', '348ABD', '988ED5', '777777', 'FBC15E', '8EBA42', 'FFB5B8']) - # E24A33 : red - # 348ABD : blue - # 988ED5 : purple - # 777777 : gray - # FBC15E : yellow - # 8EBA42 : green - # FFB5B8 : pink - -xtick.color: 555555 -xtick.direction: out - -ytick.color: 555555 -ytick.direction: out - -grid.color: white -grid.linestyle: - # solid line - -figure.facecolor: white -figure.edgecolor: 0.50 - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/grayscale.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/grayscale.mplstyle deleted file mode 100644 index 6a1114e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/grayscale.mplstyle +++ /dev/null @@ -1,29 +0,0 @@ -# Set all colors to grayscale -# Note: strings of float values are interpreted by matplotlib as gray values. - - -lines.color: black -patch.facecolor: gray -patch.edgecolor: black - -text.color: black - -axes.facecolor: white -axes.edgecolor: black -axes.labelcolor: black -# black to light gray -axes.prop_cycle: cycler('color', ['0.00', '0.40', '0.60', '0.70']) - -xtick.color: black -ytick.color: black - -grid.color: black - -figure.facecolor: 0.75 -figure.edgecolor: white - -image.cmap: gray - -savefig.facecolor: white -savefig.edgecolor: white - diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-bright.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-bright.mplstyle deleted file mode 100644 index 5e9e949..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-bright.mplstyle +++ /dev/null @@ -1,3 +0,0 @@ -# Seaborn bright palette -axes.prop_cycle: cycler('color', ['003FFF', '03ED3A', 'E8000B', '8A2BE2', 'FFC400', '00D7FF']) -patch.facecolor: 003FFF diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-colorblind.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-colorblind.mplstyle deleted file mode 100644 index e13b7aa..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-colorblind.mplstyle +++ /dev/null @@ -1,3 +0,0 @@ -# Seaborn colorblind palette -axes.prop_cycle: cycler('color', ['0072B2', '009E73', 'D55E00', 'CC79A7', 'F0E442', '56B4E9']) -patch.facecolor: 0072B2 diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-dark-palette.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-dark-palette.mplstyle deleted file mode 100644 index 30160ae..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-dark-palette.mplstyle +++ /dev/null @@ -1,3 +0,0 @@ -# Seaborn dark palette -axes.prop_cycle: cycler('color', ['001C7F', '017517', '8C0900', '7600A1', 'B8860B', '006374']) -patch.facecolor: 001C7F diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-dark.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-dark.mplstyle deleted file mode 100644 index 55b50b5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-dark.mplstyle +++ /dev/null @@ -1,30 +0,0 @@ -# Seaborn common parameters -# .15 = dark_gray -# .8 = light_gray -figure.facecolor: white -text.color: .15 -axes.labelcolor: .15 -legend.frameon: False -legend.numpoints: 1 -legend.scatterpoints: 1 -xtick.direction: out -ytick.direction: out -xtick.color: .15 -ytick.color: .15 -axes.axisbelow: True -image.cmap: Greys -font.family: sans-serif -font.sans-serif: Arial, Liberation Sans, DejaVu Sans, Bitstream Vera Sans, sans-serif -grid.linestyle: - -lines.solid_capstyle: round - -# Seaborn dark parameters -axes.grid: False -axes.facecolor: EAEAF2 -axes.edgecolor: white -axes.linewidth: 0 -grid.color: white -xtick.major.size: 0 -ytick.major.size: 0 -xtick.minor.size: 0 -ytick.minor.size: 0 diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-darkgrid.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-darkgrid.mplstyle deleted file mode 100644 index 0f5d955..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-darkgrid.mplstyle +++ /dev/null @@ -1,30 +0,0 @@ -# Seaborn common parameters -# .15 = dark_gray -# .8 = light_gray -figure.facecolor: white -text.color: .15 -axes.labelcolor: .15 -legend.frameon: False -legend.numpoints: 1 -legend.scatterpoints: 1 -xtick.direction: out -ytick.direction: out -xtick.color: .15 -ytick.color: .15 -axes.axisbelow: True -image.cmap: Greys -font.family: sans-serif -font.sans-serif: Arial, Liberation Sans, DejaVu Sans, Bitstream Vera Sans, sans-serif -grid.linestyle: - -lines.solid_capstyle: round - -# Seaborn darkgrid parameters -axes.grid: True -axes.facecolor: EAEAF2 -axes.edgecolor: white -axes.linewidth: 0 -grid.color: white -xtick.major.size: 0 -ytick.major.size: 0 -xtick.minor.size: 0 -ytick.minor.size: 0 diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-deep.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-deep.mplstyle deleted file mode 100644 index 5d6b7c5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-deep.mplstyle +++ /dev/null @@ -1,3 +0,0 @@ -# Seaborn deep palette -axes.prop_cycle: cycler('color', ['4C72B0', '55A868', 'C44E52', '8172B2', 'CCB974', '64B5CD']) -patch.facecolor: 4C72B0 diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-muted.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-muted.mplstyle deleted file mode 100644 index 4a71646..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-muted.mplstyle +++ /dev/null @@ -1,3 +0,0 @@ -# Seaborn muted palette -axes.prop_cycle: cycler('color', ['4878CF', '6ACC65', 'D65F5F', 'B47CC7', 'C4AD66', '77BEDB']) -patch.facecolor: 4878CF diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-notebook.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-notebook.mplstyle deleted file mode 100644 index 18bcf3e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-notebook.mplstyle +++ /dev/null @@ -1,21 +0,0 @@ -# Seaborn notebook context -figure.figsize: 8.0, 5.5 -axes.labelsize: 11 -axes.titlesize: 12 -xtick.labelsize: 10 -ytick.labelsize: 10 -legend.fontsize: 10 - -grid.linewidth: 1 -lines.linewidth: 1.75 -patch.linewidth: .3 -lines.markersize: 7 -lines.markeredgewidth: 0 - -xtick.major.width: 1 -ytick.major.width: 1 -xtick.minor.width: .5 -ytick.minor.width: .5 - -xtick.major.pad: 7 -ytick.major.pad: 7 diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-paper.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-paper.mplstyle deleted file mode 100644 index 3326be4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-paper.mplstyle +++ /dev/null @@ -1,21 +0,0 @@ -# Seaborn paper context -figure.figsize: 6.4, 4.4 -axes.labelsize: 8.8 -axes.titlesize: 9.6 -xtick.labelsize: 8 -ytick.labelsize: 8 -legend.fontsize: 8 - -grid.linewidth: 0.8 -lines.linewidth: 1.4 -patch.linewidth: 0.24 -lines.markersize: 5.6 -lines.markeredgewidth: 0 - -xtick.major.width: 0.8 -ytick.major.width: 0.8 -xtick.minor.width: 0.4 -ytick.minor.width: 0.4 - -xtick.major.pad: 5.6 -ytick.major.pad: 5.6 diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-pastel.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-pastel.mplstyle deleted file mode 100644 index dff6748..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-pastel.mplstyle +++ /dev/null @@ -1,3 +0,0 @@ -# Seaborn pastel palette -axes.prop_cycle: cycler('color', ['92C6FF', '97F0AA', 'FF9F9A', 'D0BBFF', 'FFFEA3', 'B0E0E6']) -patch.facecolor: 92C6FF diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-poster.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-poster.mplstyle deleted file mode 100644 index 47f2370..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-poster.mplstyle +++ /dev/null @@ -1,21 +0,0 @@ -# Seaborn poster context -figure.figsize: 12.8, 8.8 -axes.labelsize: 17.6 -axes.titlesize: 19.2 -xtick.labelsize: 16 -ytick.labelsize: 16 -legend.fontsize: 16 - -grid.linewidth: 1.6 -lines.linewidth: 2.8 -patch.linewidth: 0.48 -lines.markersize: 11.2 -lines.markeredgewidth: 0 - -xtick.major.width: 1.6 -ytick.major.width: 1.6 -xtick.minor.width: 0.8 -ytick.minor.width: 0.8 - -xtick.major.pad: 11.2 -ytick.major.pad: 11.2 diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-talk.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-talk.mplstyle deleted file mode 100644 index 29a77c5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-talk.mplstyle +++ /dev/null @@ -1,21 +0,0 @@ -# Seaborn talk context -figure.figsize: 10.4, 7.15 -axes.labelsize: 14.3 -axes.titlesize: 15.6 -xtick.labelsize: 13 -ytick.labelsize: 13 -legend.fontsize: 13 - -grid.linewidth: 1.3 -lines.linewidth: 2.275 -patch.linewidth: 0.39 -lines.markersize: 9.1 -lines.markeredgewidth: 0 - -xtick.major.width: 1.3 -ytick.major.width: 1.3 -xtick.minor.width: 0.65 -ytick.minor.width: 0.65 - -xtick.major.pad: 9.1 -ytick.major.pad: 9.1 diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-ticks.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-ticks.mplstyle deleted file mode 100644 index c2a1cab..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-ticks.mplstyle +++ /dev/null @@ -1,30 +0,0 @@ -# Seaborn common parameters -# .15 = dark_gray -# .8 = light_gray -figure.facecolor: white -text.color: .15 -axes.labelcolor: .15 -legend.frameon: False -legend.numpoints: 1 -legend.scatterpoints: 1 -xtick.direction: out -ytick.direction: out -xtick.color: .15 -ytick.color: .15 -axes.axisbelow: True -image.cmap: Greys -font.family: sans-serif -font.sans-serif: Arial, Liberation Sans, DejaVu Sans, Bitstream Vera Sans, sans-serif -grid.linestyle: - -lines.solid_capstyle: round - -# Seaborn white parameters -axes.grid: False -axes.facecolor: white -axes.edgecolor: .15 -axes.linewidth: 1.25 -grid.color: .8 -xtick.major.size: 6 -ytick.major.size: 6 -xtick.minor.size: 3 -ytick.minor.size: 3 diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-white.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-white.mplstyle deleted file mode 100644 index dcbe3ac..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-white.mplstyle +++ /dev/null @@ -1,30 +0,0 @@ -# Seaborn common parameters -# .15 = dark_gray -# .8 = light_gray -figure.facecolor: white -text.color: .15 -axes.labelcolor: .15 -legend.frameon: False -legend.numpoints: 1 -legend.scatterpoints: 1 -xtick.direction: out -ytick.direction: out -xtick.color: .15 -ytick.color: .15 -axes.axisbelow: True -image.cmap: Greys -font.family: sans-serif -font.sans-serif: Arial, Liberation Sans, DejaVu Sans, Bitstream Vera Sans, sans-serif -grid.linestyle: - -lines.solid_capstyle: round - -# Seaborn white parameters -axes.grid: False -axes.facecolor: white -axes.edgecolor: .15 -axes.linewidth: 1.25 -grid.color: .8 -xtick.major.size: 0 -ytick.major.size: 0 -xtick.minor.size: 0 -ytick.minor.size: 0 diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-whitegrid.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-whitegrid.mplstyle deleted file mode 100644 index 612e218..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8-whitegrid.mplstyle +++ /dev/null @@ -1,30 +0,0 @@ -# Seaborn common parameters -# .15 = dark_gray -# .8 = light_gray -figure.facecolor: white -text.color: .15 -axes.labelcolor: .15 -legend.frameon: False -legend.numpoints: 1 -legend.scatterpoints: 1 -xtick.direction: out -ytick.direction: out -xtick.color: .15 -ytick.color: .15 -axes.axisbelow: True -image.cmap: Greys -font.family: sans-serif -font.sans-serif: Arial, Liberation Sans, DejaVu Sans, Bitstream Vera Sans, sans-serif -grid.linestyle: - -lines.solid_capstyle: round - -# Seaborn whitegrid parameters -axes.grid: True -axes.facecolor: white -axes.edgecolor: .8 -axes.linewidth: 1 -grid.color: .8 -xtick.major.size: 0 -ytick.major.size: 0 -xtick.minor.size: 0 -ytick.minor.size: 0 diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8.mplstyle deleted file mode 100644 index 94b1bc8..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/seaborn-v0_8.mplstyle +++ /dev/null @@ -1,57 +0,0 @@ -# default seaborn aesthetic -# darkgrid + deep palette + notebook context - -axes.axisbelow: True -axes.edgecolor: white -axes.facecolor: EAEAF2 -axes.grid: True -axes.labelcolor: .15 -axes.labelsize: 11 -axes.linewidth: 0 -axes.prop_cycle: cycler('color', ['4C72B0', '55A868', 'C44E52', '8172B2', 'CCB974', '64B5CD']) -axes.titlesize: 12 - -figure.facecolor: white -figure.figsize: 8.0, 5.5 - -font.family: sans-serif -font.sans-serif: Arial, Liberation Sans, DejaVu Sans, Bitstream Vera Sans, sans-serif - -grid.color: white -grid.linestyle: - -grid.linewidth: 1 - -image.cmap: Greys - -legend.fontsize: 10 -legend.frameon: False -legend.numpoints: 1 -legend.scatterpoints: 1 - -lines.linewidth: 1.75 -lines.markeredgewidth: 0 -lines.markersize: 7 -lines.solid_capstyle: round - -patch.facecolor: 4C72B0 -patch.linewidth: .3 - -text.color: .15 - -xtick.color: .15 -xtick.direction: out -xtick.labelsize: 10 -xtick.major.pad: 7 -xtick.major.size: 0 -xtick.major.width: 1 -xtick.minor.size: 0 -xtick.minor.width: .5 - -ytick.color: .15 -ytick.direction: out -ytick.labelsize: 10 -ytick.major.pad: 7 -ytick.major.size: 0 -ytick.major.width: 1 -ytick.minor.size: 0 -ytick.minor.width: .5 diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/tableau-colorblind10.mplstyle b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/tableau-colorblind10.mplstyle deleted file mode 100644 index 2d8cb02..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/mpl-data/stylelib/tableau-colorblind10.mplstyle +++ /dev/null @@ -1,3 +0,0 @@ -# Tableau colorblind 10 palette -axes.prop_cycle: cycler('color', ['006BA4', 'FF800E', 'ABABAB', '595959', '5F9ED1', 'C85200', '898989', 'A2C8EC', 'FFBC79', 'CFCFCF']) -patch.facecolor: 006BA4 \ No newline at end of file diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/offsetbox.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/offsetbox.py deleted file mode 100644 index fd4ddef..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/offsetbox.py +++ /dev/null @@ -1,1624 +0,0 @@ -r""" -Container classes for `.Artist`\s. - -`OffsetBox` - The base of all container artists defined in this module. - -`AnchoredOffsetbox`, `AnchoredText` - Anchor and align an arbitrary `.Artist` or a text relative to the parent - axes or a specific anchor point. - -`DrawingArea` - A container with fixed width and height. Children have a fixed position - inside the container and may be clipped. - -`HPacker`, `VPacker` - Containers for layouting their children vertically or horizontally. - -`PaddedBox` - A container to add a padding around an `.Artist`. - -`TextArea` - Contains a single `.Text` instance. -""" - -import functools - -import numpy as np - -import matplotlib as mpl -from matplotlib import _api, _docstring -import matplotlib.artist as martist -import matplotlib.path as mpath -import matplotlib.text as mtext -import matplotlib.transforms as mtransforms -from matplotlib.font_manager import FontProperties -from matplotlib.image import BboxImage -from matplotlib.patches import ( - FancyBboxPatch, FancyArrowPatch, bbox_artist as mbbox_artist) -from matplotlib.transforms import Bbox, BboxBase, TransformedBbox - - -DEBUG = False - - -def _compat_get_offset(meth): - """ - Decorator for the get_offset method of OffsetBox and subclasses, that - allows supporting both the new signature (self, bbox, renderer) and the old - signature (self, width, height, xdescent, ydescent, renderer). - """ - sigs = [lambda self, width, height, xdescent, ydescent, renderer: locals(), - lambda self, bbox, renderer: locals()] - - @functools.wraps(meth) - def get_offset(self, *args, **kwargs): - params = _api.select_matching_signature(sigs, self, *args, **kwargs) - bbox = (params["bbox"] if "bbox" in params else - Bbox.from_bounds(-params["xdescent"], -params["ydescent"], - params["width"], params["height"])) - return meth(params["self"], bbox, params["renderer"]) - return get_offset - - -@_api.deprecated("3.7", alternative='patches.bbox_artist') -def bbox_artist(*args, **kwargs): - if DEBUG: - mbbox_artist(*args, **kwargs) - - -# for debugging use -def _bbox_artist(*args, **kwargs): - if DEBUG: - mbbox_artist(*args, **kwargs) - - -def _get_packed_offsets(widths, total, sep, mode="fixed"): - r""" - Pack boxes specified by their *widths*. - - For simplicity of the description, the terminology used here assumes a - horizontal layout, but the function works equally for a vertical layout. - - There are three packing *mode*\s: - - - 'fixed': The elements are packed tight to the left with a spacing of - *sep* in between. If *total* is *None* the returned total will be the - right edge of the last box. A non-*None* total will be passed unchecked - to the output. In particular this means that right edge of the last - box may be further to the right than the returned total. - - - 'expand': Distribute the boxes with equal spacing so that the left edge - of the first box is at 0, and the right edge of the last box is at - *total*. The parameter *sep* is ignored in this mode. A total of *None* - is accepted and considered equal to 1. The total is returned unchanged - (except for the conversion *None* to 1). If the total is smaller than - the sum of the widths, the laid out boxes will overlap. - - - 'equal': If *total* is given, the total space is divided in N equal - ranges and each box is left-aligned within its subspace. - Otherwise (*total* is *None*), *sep* must be provided and each box is - left-aligned in its subspace of width ``(max(widths) + sep)``. The - total width is then calculated to be ``N * (max(widths) + sep)``. - - Parameters - ---------- - widths : list of float - Widths of boxes to be packed. - total : float or None - Intended total length. *None* if not used. - sep : float - Spacing between boxes. - mode : {'fixed', 'expand', 'equal'} - The packing mode. - - Returns - ------- - total : float - The total width needed to accommodate the laid out boxes. - offsets : array of float - The left offsets of the boxes. - """ - _api.check_in_list(["fixed", "expand", "equal"], mode=mode) - - if mode == "fixed": - offsets_ = np.cumsum([0] + [w + sep for w in widths]) - offsets = offsets_[:-1] - if total is None: - total = offsets_[-1] - sep - return total, offsets - - elif mode == "expand": - # This is a bit of a hack to avoid a TypeError when *total* - # is None and used in conjugation with tight layout. - if total is None: - total = 1 - if len(widths) > 1: - sep = (total - sum(widths)) / (len(widths) - 1) - else: - sep = 0 - offsets_ = np.cumsum([0] + [w + sep for w in widths]) - offsets = offsets_[:-1] - return total, offsets - - elif mode == "equal": - maxh = max(widths) - if total is None: - if sep is None: - raise ValueError("total and sep cannot both be None when " - "using layout mode 'equal'") - total = (maxh + sep) * len(widths) - else: - sep = total / len(widths) - maxh - offsets = (maxh + sep) * np.arange(len(widths)) - return total, offsets - - -def _get_aligned_offsets(yspans, height, align="baseline"): - """ - Align boxes each specified by their ``(y0, y1)`` spans. - - For simplicity of the description, the terminology used here assumes a - horizontal layout (i.e., vertical alignment), but the function works - equally for a vertical layout. - - Parameters - ---------- - yspans - List of (y0, y1) spans of boxes to be aligned. - height : float or None - Intended total height. If None, the maximum of the heights - (``y1 - y0``) in *yspans* is used. - align : {'baseline', 'left', 'top', 'right', 'bottom', 'center'} - The alignment anchor of the boxes. - - Returns - ------- - (y0, y1) - y range spanned by the packing. If a *height* was originally passed - in, then for all alignments other than "baseline", a span of ``(0, - height)`` is used without checking that it is actually large enough). - descent - The descent of the packing. - offsets - The bottom offsets of the boxes. - """ - - _api.check_in_list( - ["baseline", "left", "top", "right", "bottom", "center"], align=align) - if height is None: - height = max(y1 - y0 for y0, y1 in yspans) - - if align == "baseline": - yspan = (min(y0 for y0, y1 in yspans), max(y1 for y0, y1 in yspans)) - offsets = [0] * len(yspans) - elif align in ["left", "bottom"]: - yspan = (0, height) - offsets = [-y0 for y0, y1 in yspans] - elif align in ["right", "top"]: - yspan = (0, height) - offsets = [height - y1 for y0, y1 in yspans] - elif align == "center": - yspan = (0, height) - offsets = [(height - (y1 - y0)) * .5 - y0 for y0, y1 in yspans] - - return yspan, offsets - - -class OffsetBox(martist.Artist): - """ - The OffsetBox is a simple container artist. - - The child artists are meant to be drawn at a relative position to its - parent. - - Being an artist itself, all parameters are passed on to `.Artist`. - """ - def __init__(self, *args, **kwargs): - super().__init__(*args) - self._internal_update(kwargs) - # Clipping has not been implemented in the OffsetBox family, so - # disable the clip flag for consistency. It can always be turned back - # on to zero effect. - self.set_clip_on(False) - self._children = [] - self._offset = (0, 0) - - def set_figure(self, fig): - """ - Set the `.Figure` for the `.OffsetBox` and all its children. - - Parameters - ---------- - fig : `~matplotlib.figure.Figure` - """ - super().set_figure(fig) - for c in self.get_children(): - c.set_figure(fig) - - @martist.Artist.axes.setter - def axes(self, ax): - # TODO deal with this better - martist.Artist.axes.fset(self, ax) - for c in self.get_children(): - if c is not None: - c.axes = ax - - def contains(self, mouseevent): - """ - Delegate the mouse event contains-check to the children. - - As a container, the `.OffsetBox` does not respond itself to - mouseevents. - - Parameters - ---------- - mouseevent : `~matplotlib.backend_bases.MouseEvent` - - Returns - ------- - contains : bool - Whether any values are within the radius. - details : dict - An artist-specific dictionary of details of the event context, - such as which points are contained in the pick radius. See the - individual Artist subclasses for details. - - See Also - -------- - .Artist.contains - """ - inside, info = self._default_contains(mouseevent) - if inside is not None: - return inside, info - for c in self.get_children(): - a, b = c.contains(mouseevent) - if a: - return a, b - return False, {} - - def set_offset(self, xy): - """ - Set the offset. - - Parameters - ---------- - xy : (float, float) or callable - The (x, y) coordinates of the offset in display units. These can - either be given explicitly as a tuple (x, y), or by providing a - function that converts the extent into the offset. This function - must have the signature:: - - def offset(width, height, xdescent, ydescent, renderer) \ --> (float, float) - """ - self._offset = xy - self.stale = True - - @_compat_get_offset - def get_offset(self, bbox, renderer): - """ - Return the offset as a tuple (x, y). - - The extent parameters have to be provided to handle the case where the - offset is dynamically determined by a callable (see - `~.OffsetBox.set_offset`). - - Parameters - ---------- - bbox : `.Bbox` - renderer : `.RendererBase` subclass - """ - return ( - self._offset(bbox.width, bbox.height, -bbox.x0, -bbox.y0, renderer) - if callable(self._offset) - else self._offset) - - def set_width(self, width): - """ - Set the width of the box. - - Parameters - ---------- - width : float - """ - self.width = width - self.stale = True - - def set_height(self, height): - """ - Set the height of the box. - - Parameters - ---------- - height : float - """ - self.height = height - self.stale = True - - def get_visible_children(self): - r"""Return a list of the visible child `.Artist`\s.""" - return [c for c in self._children if c.get_visible()] - - def get_children(self): - r"""Return a list of the child `.Artist`\s.""" - return self._children - - def _get_bbox_and_child_offsets(self, renderer): - """ - Return the bbox of the offsetbox and the child offsets. - - The bbox should satisfy ``x0 <= x1 and y0 <= y1``. - - Parameters - ---------- - renderer : `.RendererBase` subclass - - Returns - ------- - bbox - list of (xoffset, yoffset) pairs - """ - raise NotImplementedError( - "get_bbox_and_offsets must be overridden in derived classes") - - def get_bbox(self, renderer): - """Return the bbox of the offsetbox, ignoring parent offsets.""" - bbox, offsets = self._get_bbox_and_child_offsets(renderer) - return bbox - - @_api.deprecated("3.7", alternative="get_bbox and child.get_offset") - def get_extent_offsets(self, renderer): - """ - Update offset of the children and return the extent of the box. - - Parameters - ---------- - renderer : `.RendererBase` subclass - - Returns - ------- - width - height - xdescent - ydescent - list of (xoffset, yoffset) pairs - """ - bbox, offsets = self._get_bbox_and_child_offsets(renderer) - return bbox.width, bbox.height, -bbox.x0, -bbox.y0, offsets - - @_api.deprecated("3.7", alternative="get_bbox") - def get_extent(self, renderer): - """Return a tuple ``width, height, xdescent, ydescent`` of the box.""" - bbox = self.get_bbox(renderer) - return bbox.width, bbox.height, -bbox.x0, -bbox.y0 - - def get_window_extent(self, renderer=None): - # docstring inherited - if renderer is None: - renderer = self.figure._get_renderer() - bbox = self.get_bbox(renderer) - try: # Some subclasses redefine get_offset to take no args. - px, py = self.get_offset(bbox, renderer) - except TypeError: - px, py = self.get_offset() - return bbox.translated(px, py) - - def draw(self, renderer): - """ - Update the location of children if necessary and draw them - to the given *renderer*. - """ - bbox, offsets = self._get_bbox_and_child_offsets(renderer) - px, py = self.get_offset(bbox, renderer) - for c, (ox, oy) in zip(self.get_visible_children(), offsets): - c.set_offset((px + ox, py + oy)) - c.draw(renderer) - _bbox_artist(self, renderer, fill=False, props=dict(pad=0.)) - self.stale = False - - -class PackerBase(OffsetBox): - def __init__(self, pad=0., sep=0., width=None, height=None, - align="baseline", mode="fixed", children=None): - """ - Parameters - ---------- - pad : float, default: 0.0 - The boundary padding in points. - - sep : float, default: 0.0 - The spacing between items in points. - - width, height : float, optional - Width and height of the container box in pixels, calculated if - *None*. - - align : {'top', 'bottom', 'left', 'right', 'center', 'baseline'}, \ -default: 'baseline' - Alignment of boxes. - - mode : {'fixed', 'expand', 'equal'}, default: 'fixed' - The packing mode. - - - 'fixed' packs the given `.Artist`\\s tight with *sep* spacing. - - 'expand' uses the maximal available space to distribute the - artists with equal spacing in between. - - 'equal': Each artist an equal fraction of the available space - and is left-aligned (or top-aligned) therein. - - children : list of `.Artist` - The artists to pack. - - Notes - ----- - *pad* and *sep* are in points and will be scaled with the renderer - dpi, while *width* and *height* are in pixels. - """ - super().__init__() - self.height = height - self.width = width - self.sep = sep - self.pad = pad - self.mode = mode - self.align = align - self._children = children - - -class VPacker(PackerBase): - """ - VPacker packs its children vertically, automatically adjusting their - relative positions at draw time. - """ - - def _get_bbox_and_child_offsets(self, renderer): - # docstring inherited - dpicor = renderer.points_to_pixels(1.) - pad = self.pad * dpicor - sep = self.sep * dpicor - - if self.width is not None: - for c in self.get_visible_children(): - if isinstance(c, PackerBase) and c.mode == "expand": - c.set_width(self.width) - - bboxes = [c.get_bbox(renderer) for c in self.get_visible_children()] - (x0, x1), xoffsets = _get_aligned_offsets( - [bbox.intervalx for bbox in bboxes], self.width, self.align) - height, yoffsets = _get_packed_offsets( - [bbox.height for bbox in bboxes], self.height, sep, self.mode) - - yoffsets = height - (yoffsets + [bbox.y1 for bbox in bboxes]) - ydescent = yoffsets[0] - yoffsets = yoffsets - ydescent - - return ( - Bbox.from_bounds(x0, -ydescent, x1 - x0, height).padded(pad), - [*zip(xoffsets, yoffsets)]) - - -class HPacker(PackerBase): - """ - HPacker packs its children horizontally, automatically adjusting their - relative positions at draw time. - """ - - def _get_bbox_and_child_offsets(self, renderer): - # docstring inherited - dpicor = renderer.points_to_pixels(1.) - pad = self.pad * dpicor - sep = self.sep * dpicor - - bboxes = [c.get_bbox(renderer) for c in self.get_visible_children()] - if not bboxes: - return Bbox.from_bounds(0, 0, 0, 0).padded(pad), [] - - (y0, y1), yoffsets = _get_aligned_offsets( - [bbox.intervaly for bbox in bboxes], self.height, self.align) - width, xoffsets = _get_packed_offsets( - [bbox.width for bbox in bboxes], self.width, sep, self.mode) - - x0 = bboxes[0].x0 - xoffsets -= ([bbox.x0 for bbox in bboxes] - x0) - - return (Bbox.from_bounds(x0, y0, width, y1 - y0).padded(pad), - [*zip(xoffsets, yoffsets)]) - - -class PaddedBox(OffsetBox): - """ - A container to add a padding around an `.Artist`. - - The `.PaddedBox` contains a `.FancyBboxPatch` that is used to visualize - it when rendering. - """ - - @_api.make_keyword_only("3.6", name="draw_frame") - def __init__(self, child, pad=0., draw_frame=False, patch_attrs=None): - """ - Parameters - ---------- - child : `~matplotlib.artist.Artist` - The contained `.Artist`. - pad : float, default: 0.0 - The padding in points. This will be scaled with the renderer dpi. - In contrast, *width* and *height* are in *pixels* and thus not - scaled. - draw_frame : bool - Whether to draw the contained `.FancyBboxPatch`. - patch_attrs : dict or None - Additional parameters passed to the contained `.FancyBboxPatch`. - """ - super().__init__() - self.pad = pad - self._children = [child] - self.patch = FancyBboxPatch( - xy=(0.0, 0.0), width=1., height=1., - facecolor='w', edgecolor='k', - mutation_scale=1, # self.prop.get_size_in_points(), - snap=True, - visible=draw_frame, - boxstyle="square,pad=0", - ) - if patch_attrs is not None: - self.patch.update(patch_attrs) - - def _get_bbox_and_child_offsets(self, renderer): - # docstring inherited. - pad = self.pad * renderer.points_to_pixels(1.) - return (self._children[0].get_bbox(renderer).padded(pad), [(0, 0)]) - - def draw(self, renderer): - # docstring inherited - bbox, offsets = self._get_bbox_and_child_offsets(renderer) - px, py = self.get_offset(bbox, renderer) - for c, (ox, oy) in zip(self.get_visible_children(), offsets): - c.set_offset((px + ox, py + oy)) - - self.draw_frame(renderer) - - for c in self.get_visible_children(): - c.draw(renderer) - - self.stale = False - - def update_frame(self, bbox, fontsize=None): - self.patch.set_bounds(bbox.bounds) - if fontsize: - self.patch.set_mutation_scale(fontsize) - self.stale = True - - def draw_frame(self, renderer): - # update the location and size of the legend - self.update_frame(self.get_window_extent(renderer)) - self.patch.draw(renderer) - - -class DrawingArea(OffsetBox): - """ - The DrawingArea can contain any Artist as a child. The DrawingArea - has a fixed width and height. The position of children relative to - the parent is fixed. The children can be clipped at the - boundaries of the parent. - """ - - def __init__(self, width, height, xdescent=0., ydescent=0., clip=False): - """ - Parameters - ---------- - width, height : float - Width and height of the container box. - xdescent, ydescent : float - Descent of the box in x- and y-direction. - clip : bool - Whether to clip the children to the box. - """ - super().__init__() - self.width = width - self.height = height - self.xdescent = xdescent - self.ydescent = ydescent - self._clip_children = clip - self.offset_transform = mtransforms.Affine2D() - self.dpi_transform = mtransforms.Affine2D() - - @property - def clip_children(self): - """ - If the children of this DrawingArea should be clipped - by DrawingArea bounding box. - """ - return self._clip_children - - @clip_children.setter - def clip_children(self, val): - self._clip_children = bool(val) - self.stale = True - - def get_transform(self): - """ - Return the `~matplotlib.transforms.Transform` applied to the children. - """ - return self.dpi_transform + self.offset_transform - - def set_transform(self, t): - """ - set_transform is ignored. - """ - - def set_offset(self, xy): - """ - Set the offset of the container. - - Parameters - ---------- - xy : (float, float) - The (x, y) coordinates of the offset in display units. - """ - self._offset = xy - self.offset_transform.clear() - self.offset_transform.translate(xy[0], xy[1]) - self.stale = True - - def get_offset(self): - """Return offset of the container.""" - return self._offset - - def get_bbox(self, renderer): - # docstring inherited - dpi_cor = renderer.points_to_pixels(1.) - return Bbox.from_bounds( - -self.xdescent * dpi_cor, -self.ydescent * dpi_cor, - self.width * dpi_cor, self.height * dpi_cor) - - def add_artist(self, a): - """Add an `.Artist` to the container box.""" - self._children.append(a) - if not a.is_transform_set(): - a.set_transform(self.get_transform()) - if self.axes is not None: - a.axes = self.axes - fig = self.figure - if fig is not None: - a.set_figure(fig) - - def draw(self, renderer): - # docstring inherited - - dpi_cor = renderer.points_to_pixels(1.) - self.dpi_transform.clear() - self.dpi_transform.scale(dpi_cor) - - # At this point the DrawingArea has a transform - # to the display space so the path created is - # good for clipping children - tpath = mtransforms.TransformedPath( - mpath.Path([[0, 0], [0, self.height], - [self.width, self.height], - [self.width, 0]]), - self.get_transform()) - for c in self._children: - if self._clip_children and not (c.clipbox or c._clippath): - c.set_clip_path(tpath) - c.draw(renderer) - - _bbox_artist(self, renderer, fill=False, props=dict(pad=0.)) - self.stale = False - - -class TextArea(OffsetBox): - """ - The TextArea is a container artist for a single Text instance. - - The text is placed at (0, 0) with baseline+left alignment, by default. The - width and height of the TextArea instance is the width and height of its - child text. - """ - - @_api.make_keyword_only("3.6", name="textprops") - def __init__(self, s, - textprops=None, - multilinebaseline=False, - ): - """ - Parameters - ---------- - s : str - The text to be displayed. - textprops : dict, default: {} - Dictionary of keyword parameters to be passed to the `.Text` - instance in the TextArea. - multilinebaseline : bool, default: False - Whether the baseline for multiline text is adjusted so that it - is (approximately) center-aligned with single-line text. - """ - if textprops is None: - textprops = {} - self._text = mtext.Text(0, 0, s, **textprops) - super().__init__() - self._children = [self._text] - self.offset_transform = mtransforms.Affine2D() - self._baseline_transform = mtransforms.Affine2D() - self._text.set_transform(self.offset_transform + - self._baseline_transform) - self._multilinebaseline = multilinebaseline - - def set_text(self, s): - """Set the text of this area as a string.""" - self._text.set_text(s) - self.stale = True - - def get_text(self): - """Return the string representation of this area's text.""" - return self._text.get_text() - - def set_multilinebaseline(self, t): - """ - Set multilinebaseline. - - If True, the baseline for multiline text is adjusted so that it is - (approximately) center-aligned with single-line text. This is used - e.g. by the legend implementation so that single-line labels are - baseline-aligned, but multiline labels are "center"-aligned with them. - """ - self._multilinebaseline = t - self.stale = True - - def get_multilinebaseline(self): - """ - Get multilinebaseline. - """ - return self._multilinebaseline - - def set_transform(self, t): - """ - set_transform is ignored. - """ - - def set_offset(self, xy): - """ - Set the offset of the container. - - Parameters - ---------- - xy : (float, float) - The (x, y) coordinates of the offset in display units. - """ - self._offset = xy - self.offset_transform.clear() - self.offset_transform.translate(xy[0], xy[1]) - self.stale = True - - def get_offset(self): - """Return offset of the container.""" - return self._offset - - def get_bbox(self, renderer): - _, h_, d_ = renderer.get_text_width_height_descent( - "lp", self._text._fontproperties, - ismath="TeX" if self._text.get_usetex() else False) - - bbox, info, yd = self._text._get_layout(renderer) - w, h = bbox.size - - self._baseline_transform.clear() - - if len(info) > 1 and self._multilinebaseline: - yd_new = 0.5 * h - 0.5 * (h_ - d_) - self._baseline_transform.translate(0, yd - yd_new) - yd = yd_new - else: # single line - h_d = max(h_ - d_, h - yd) - h = h_d + yd - - ha = self._text.get_horizontalalignment() - x0 = {"left": 0, "center": -w / 2, "right": -w}[ha] - - return Bbox.from_bounds(x0, -yd, w, h) - - def draw(self, renderer): - # docstring inherited - self._text.draw(renderer) - _bbox_artist(self, renderer, fill=False, props=dict(pad=0.)) - self.stale = False - - -class AuxTransformBox(OffsetBox): - """ - Offset Box with the aux_transform. Its children will be - transformed with the aux_transform first then will be - offsetted. The absolute coordinate of the aux_transform is meaning - as it will be automatically adjust so that the left-lower corner - of the bounding box of children will be set to (0, 0) before the - offset transform. - - It is similar to drawing area, except that the extent of the box - is not predetermined but calculated from the window extent of its - children. Furthermore, the extent of the children will be - calculated in the transformed coordinate. - """ - def __init__(self, aux_transform): - self.aux_transform = aux_transform - super().__init__() - self.offset_transform = mtransforms.Affine2D() - # ref_offset_transform makes offset_transform always relative to the - # lower-left corner of the bbox of its children. - self.ref_offset_transform = mtransforms.Affine2D() - - def add_artist(self, a): - """Add an `.Artist` to the container box.""" - self._children.append(a) - a.set_transform(self.get_transform()) - self.stale = True - - def get_transform(self): - """ - Return the :class:`~matplotlib.transforms.Transform` applied - to the children - """ - return (self.aux_transform - + self.ref_offset_transform - + self.offset_transform) - - def set_transform(self, t): - """ - set_transform is ignored. - """ - - def set_offset(self, xy): - """ - Set the offset of the container. - - Parameters - ---------- - xy : (float, float) - The (x, y) coordinates of the offset in display units. - """ - self._offset = xy - self.offset_transform.clear() - self.offset_transform.translate(xy[0], xy[1]) - self.stale = True - - def get_offset(self): - """Return offset of the container.""" - return self._offset - - def get_bbox(self, renderer): - # clear the offset transforms - _off = self.offset_transform.get_matrix() # to be restored later - self.ref_offset_transform.clear() - self.offset_transform.clear() - # calculate the extent - bboxes = [c.get_window_extent(renderer) for c in self._children] - ub = Bbox.union(bboxes) - # adjust ref_offset_transform - self.ref_offset_transform.translate(-ub.x0, -ub.y0) - # restore offset transform - self.offset_transform.set_matrix(_off) - return Bbox.from_bounds(0, 0, ub.width, ub.height) - - def draw(self, renderer): - # docstring inherited - for c in self._children: - c.draw(renderer) - _bbox_artist(self, renderer, fill=False, props=dict(pad=0.)) - self.stale = False - - -class AnchoredOffsetbox(OffsetBox): - """ - An offset box placed according to location *loc*. - - AnchoredOffsetbox has a single child. When multiple children are needed, - use an extra OffsetBox to enclose them. By default, the offset box is - anchored against its parent axes. You may explicitly specify the - *bbox_to_anchor*. - """ - zorder = 5 # zorder of the legend - - # Location codes - codes = {'upper right': 1, - 'upper left': 2, - 'lower left': 3, - 'lower right': 4, - 'right': 5, - 'center left': 6, - 'center right': 7, - 'lower center': 8, - 'upper center': 9, - 'center': 10, - } - - @_api.make_keyword_only("3.6", name="pad") - def __init__(self, loc, - pad=0.4, borderpad=0.5, - child=None, prop=None, frameon=True, - bbox_to_anchor=None, - bbox_transform=None, - **kwargs): - """ - Parameters - ---------- - loc : str - The box location. Valid locations are - 'upper left', 'upper center', 'upper right', - 'center left', 'center', 'center right', - 'lower left', 'lower center', 'lower right'. - For backward compatibility, numeric values are accepted as well. - See the parameter *loc* of `.Legend` for details. - pad : float, default: 0.4 - Padding around the child as fraction of the fontsize. - borderpad : float, default: 0.5 - Padding between the offsetbox frame and the *bbox_to_anchor*. - child : `.OffsetBox` - The box that will be anchored. - prop : `.FontProperties` - This is only used as a reference for paddings. If not given, - :rc:`legend.fontsize` is used. - frameon : bool - Whether to draw a frame around the box. - bbox_to_anchor : `.BboxBase`, 2-tuple, or 4-tuple of floats - Box that is used to position the legend in conjunction with *loc*. - bbox_transform : None or :class:`matplotlib.transforms.Transform` - The transform for the bounding box (*bbox_to_anchor*). - **kwargs - All other parameters are passed on to `.OffsetBox`. - - Notes - ----- - See `.Legend` for a detailed description of the anchoring mechanism. - """ - super().__init__(**kwargs) - - self.set_bbox_to_anchor(bbox_to_anchor, bbox_transform) - self.set_child(child) - - if isinstance(loc, str): - loc = _api.check_getitem(self.codes, loc=loc) - - self.loc = loc - self.borderpad = borderpad - self.pad = pad - - if prop is None: - self.prop = FontProperties(size=mpl.rcParams["legend.fontsize"]) - else: - self.prop = FontProperties._from_any(prop) - if isinstance(prop, dict) and "size" not in prop: - self.prop.set_size(mpl.rcParams["legend.fontsize"]) - - self.patch = FancyBboxPatch( - xy=(0.0, 0.0), width=1., height=1., - facecolor='w', edgecolor='k', - mutation_scale=self.prop.get_size_in_points(), - snap=True, - visible=frameon, - boxstyle="square,pad=0", - ) - - def set_child(self, child): - """Set the child to be anchored.""" - self._child = child - if child is not None: - child.axes = self.axes - self.stale = True - - def get_child(self): - """Return the child.""" - return self._child - - def get_children(self): - """Return the list of children.""" - return [self._child] - - def get_bbox(self, renderer): - # docstring inherited - fontsize = renderer.points_to_pixels(self.prop.get_size_in_points()) - pad = self.pad * fontsize - return self.get_child().get_bbox(renderer).padded(pad) - - def get_bbox_to_anchor(self): - """Return the bbox that the box is anchored to.""" - if self._bbox_to_anchor is None: - return self.axes.bbox - else: - transform = self._bbox_to_anchor_transform - if transform is None: - return self._bbox_to_anchor - else: - return TransformedBbox(self._bbox_to_anchor, transform) - - def set_bbox_to_anchor(self, bbox, transform=None): - """ - Set the bbox that the box is anchored to. - - *bbox* can be a Bbox instance, a list of [left, bottom, width, - height], or a list of [left, bottom] where the width and - height will be assumed to be zero. The bbox will be - transformed to display coordinate by the given transform. - """ - if bbox is None or isinstance(bbox, BboxBase): - self._bbox_to_anchor = bbox - else: - try: - l = len(bbox) - except TypeError as err: - raise ValueError(f"Invalid bbox: {bbox}") from err - - if l == 2: - bbox = [bbox[0], bbox[1], 0, 0] - - self._bbox_to_anchor = Bbox.from_bounds(*bbox) - - self._bbox_to_anchor_transform = transform - self.stale = True - - @_compat_get_offset - def get_offset(self, bbox, renderer): - # docstring inherited - pad = (self.borderpad - * renderer.points_to_pixels(self.prop.get_size_in_points())) - bbox_to_anchor = self.get_bbox_to_anchor() - x0, y0 = _get_anchored_bbox( - self.loc, Bbox.from_bounds(0, 0, bbox.width, bbox.height), - bbox_to_anchor, pad) - return x0 - bbox.x0, y0 - bbox.y0 - - def update_frame(self, bbox, fontsize=None): - self.patch.set_bounds(bbox.bounds) - if fontsize: - self.patch.set_mutation_scale(fontsize) - - def draw(self, renderer): - # docstring inherited - if not self.get_visible(): - return - - # update the location and size of the legend - bbox = self.get_window_extent(renderer) - fontsize = renderer.points_to_pixels(self.prop.get_size_in_points()) - self.update_frame(bbox, fontsize) - self.patch.draw(renderer) - - px, py = self.get_offset(self.get_bbox(renderer), renderer) - self.get_child().set_offset((px, py)) - self.get_child().draw(renderer) - self.stale = False - - -def _get_anchored_bbox(loc, bbox, parentbbox, borderpad): - """ - Return the (x, y) position of the *bbox* anchored at the *parentbbox* with - the *loc* code with the *borderpad*. - """ - # This is only called internally and *loc* should already have been - # validated. If 0 (None), we just let ``bbox.anchored`` raise. - c = [None, "NE", "NW", "SW", "SE", "E", "W", "E", "S", "N", "C"][loc] - container = parentbbox.padded(-borderpad) - return bbox.anchored(c, container=container).p0 - - -class AnchoredText(AnchoredOffsetbox): - """ - AnchoredOffsetbox with Text. - """ - - @_api.make_keyword_only("3.6", name="pad") - def __init__(self, s, loc, pad=0.4, borderpad=0.5, prop=None, **kwargs): - """ - Parameters - ---------- - s : str - Text. - - loc : str - Location code. See `AnchoredOffsetbox`. - - pad : float, default: 0.4 - Padding around the text as fraction of the fontsize. - - borderpad : float, default: 0.5 - Spacing between the offsetbox frame and the *bbox_to_anchor*. - - prop : dict, optional - Dictionary of keyword parameters to be passed to the - `~matplotlib.text.Text` instance contained inside AnchoredText. - - **kwargs - All other parameters are passed to `AnchoredOffsetbox`. - """ - - if prop is None: - prop = {} - badkwargs = {'va', 'verticalalignment'} - if badkwargs & set(prop): - raise ValueError( - 'Mixing verticalalignment with AnchoredText is not supported.') - - self.txt = TextArea(s, textprops=prop) - fp = self.txt._text.get_fontproperties() - super().__init__( - loc, pad=pad, borderpad=borderpad, child=self.txt, prop=fp, - **kwargs) - - -class OffsetImage(OffsetBox): - - @_api.make_keyword_only("3.6", name="zoom") - def __init__(self, arr, - zoom=1, - cmap=None, - norm=None, - interpolation=None, - origin=None, - filternorm=True, - filterrad=4.0, - resample=False, - dpi_cor=True, - **kwargs - ): - - super().__init__() - self._dpi_cor = dpi_cor - - self.image = BboxImage(bbox=self.get_window_extent, - cmap=cmap, - norm=norm, - interpolation=interpolation, - origin=origin, - filternorm=filternorm, - filterrad=filterrad, - resample=resample, - **kwargs - ) - - self._children = [self.image] - - self.set_zoom(zoom) - self.set_data(arr) - - def set_data(self, arr): - self._data = np.asarray(arr) - self.image.set_data(self._data) - self.stale = True - - def get_data(self): - return self._data - - def set_zoom(self, zoom): - self._zoom = zoom - self.stale = True - - def get_zoom(self): - return self._zoom - - def get_offset(self): - """Return offset of the container.""" - return self._offset - - def get_children(self): - return [self.image] - - def get_bbox(self, renderer): - dpi_cor = renderer.points_to_pixels(1.) if self._dpi_cor else 1. - zoom = self.get_zoom() - data = self.get_data() - ny, nx = data.shape[:2] - w, h = dpi_cor * nx * zoom, dpi_cor * ny * zoom - return Bbox.from_bounds(0, 0, w, h) - - def draw(self, renderer): - # docstring inherited - self.image.draw(renderer) - # bbox_artist(self, renderer, fill=False, props=dict(pad=0.)) - self.stale = False - - -class AnnotationBbox(martist.Artist, mtext._AnnotationBase): - """ - Container for an `OffsetBox` referring to a specific position *xy*. - - Optionally an arrow pointing from the offsetbox to *xy* can be drawn. - - This is like `.Annotation`, but with `OffsetBox` instead of `.Text`. - """ - - zorder = 3 - - def __str__(self): - return "AnnotationBbox(%g,%g)" % (self.xy[0], self.xy[1]) - - @_docstring.dedent_interpd - @_api.make_keyword_only("3.6", name="xycoords") - def __init__(self, offsetbox, xy, - xybox=None, - xycoords='data', - boxcoords=None, - frameon=True, pad=0.4, # FancyBboxPatch boxstyle. - annotation_clip=None, - box_alignment=(0.5, 0.5), - bboxprops=None, - arrowprops=None, - fontsize=None, - **kwargs): - """ - Parameters - ---------- - offsetbox : `OffsetBox` - - xy : (float, float) - The point *(x, y)* to annotate. The coordinate system is determined - by *xycoords*. - - xybox : (float, float), default: *xy* - The position *(x, y)* to place the text at. The coordinate system - is determined by *boxcoords*. - - xycoords : single or two-tuple of str or `.Artist` or `.Transform` or \ -callable, default: 'data' - The coordinate system that *xy* is given in. See the parameter - *xycoords* in `.Annotation` for a detailed description. - - boxcoords : single or two-tuple of str or `.Artist` or `.Transform` \ -or callable, default: value of *xycoords* - The coordinate system that *xybox* is given in. See the parameter - *textcoords* in `.Annotation` for a detailed description. - - frameon : bool, default: True - By default, the text is surrounded by a white `.FancyBboxPatch` - (accessible as the ``patch`` attribute of the `.AnnotationBbox`). - If *frameon* is set to False, this patch is made invisible. - - annotation_clip: bool or None, default: None - Whether to clip (i.e. not draw) the annotation when the annotation - point *xy* is outside the axes area. - - - If *True*, the annotation will be clipped when *xy* is outside - the axes. - - If *False*, the annotation will always be drawn. - - If *None*, the annotation will be clipped when *xy* is outside - the axes and *xycoords* is 'data'. - - pad : float, default: 0.4 - Padding around the offsetbox. - - box_alignment : (float, float) - A tuple of two floats for a vertical and horizontal alignment of - the offset box w.r.t. the *boxcoords*. - The lower-left corner is (0, 0) and upper-right corner is (1, 1). - - bboxprops : dict, optional - A dictionary of properties to set for the annotation bounding box, - for example *boxstyle* and *alpha*. See `.FancyBboxPatch` for - details. - - arrowprops: dict, optional - Arrow properties, see `.Annotation` for description. - - fontsize: float or str, optional - Translated to points and passed as *mutation_scale* into - `.FancyBboxPatch` to scale attributes of the box style (e.g. pad - or rounding_size). The name is chosen in analogy to `.Text` where - *fontsize* defines the mutation scale as well. If not given, - :rc:`legend.fontsize` is used. See `.Text.set_fontsize` for valid - values. - - **kwargs - Other `AnnotationBbox` properties. See `.AnnotationBbox.set` for - a list. - """ - - martist.Artist.__init__(self) - mtext._AnnotationBase.__init__( - self, xy, xycoords=xycoords, annotation_clip=annotation_clip) - - self.offsetbox = offsetbox - self.arrowprops = arrowprops.copy() if arrowprops is not None else None - self.set_fontsize(fontsize) - self.xybox = xybox if xybox is not None else xy - self.boxcoords = boxcoords if boxcoords is not None else xycoords - self._box_alignment = box_alignment - - if arrowprops is not None: - self._arrow_relpos = self.arrowprops.pop("relpos", (0.5, 0.5)) - self.arrow_patch = FancyArrowPatch((0, 0), (1, 1), - **self.arrowprops) - else: - self._arrow_relpos = None - self.arrow_patch = None - - self.patch = FancyBboxPatch( # frame - xy=(0.0, 0.0), width=1., height=1., - facecolor='w', edgecolor='k', - mutation_scale=self.prop.get_size_in_points(), - snap=True, - visible=frameon, - ) - self.patch.set_boxstyle("square", pad=pad) - if bboxprops: - self.patch.set(**bboxprops) - - self._internal_update(kwargs) - - @property - def xyann(self): - return self.xybox - - @xyann.setter - def xyann(self, xyann): - self.xybox = xyann - self.stale = True - - @property - def anncoords(self): - return self.boxcoords - - @anncoords.setter - def anncoords(self, coords): - self.boxcoords = coords - self.stale = True - - def contains(self, mouseevent): - inside, info = self._default_contains(mouseevent) - if inside is not None: - return inside, info - if not self._check_xy(None): - return False, {} - return self.offsetbox.contains(mouseevent) - # self.arrow_patch is currently not checked as this can be a line - JJ - - def get_children(self): - children = [self.offsetbox, self.patch] - if self.arrow_patch: - children.append(self.arrow_patch) - return children - - def set_figure(self, fig): - if self.arrow_patch is not None: - self.arrow_patch.set_figure(fig) - self.offsetbox.set_figure(fig) - martist.Artist.set_figure(self, fig) - - def set_fontsize(self, s=None): - """ - Set the fontsize in points. - - If *s* is not given, reset to :rc:`legend.fontsize`. - """ - if s is None: - s = mpl.rcParams["legend.fontsize"] - - self.prop = FontProperties(size=s) - self.stale = True - - def get_fontsize(self): - """Return the fontsize in points.""" - return self.prop.get_size_in_points() - - def get_window_extent(self, renderer=None): - # docstring inherited - if renderer is None: - renderer = self.figure._get_renderer() - self.update_positions(renderer) - return Bbox.union([child.get_window_extent(renderer) - for child in self.get_children()]) - - def get_tightbbox(self, renderer=None): - # docstring inherited - if renderer is None: - renderer = self.figure._get_renderer() - self.update_positions(renderer) - return Bbox.union([child.get_tightbbox(renderer) - for child in self.get_children()]) - - def update_positions(self, renderer): - """ - Update pixel positions for the annotated point, the text and the arrow. - """ - - x, y = self.xybox - if isinstance(self.boxcoords, tuple): - xcoord, ycoord = self.boxcoords - x1, y1 = self._get_xy(renderer, x, y, xcoord) - x2, y2 = self._get_xy(renderer, x, y, ycoord) - ox0, oy0 = x1, y2 - else: - ox0, oy0 = self._get_xy(renderer, x, y, self.boxcoords) - - bbox = self.offsetbox.get_bbox(renderer) - fw, fh = self._box_alignment - self.offsetbox.set_offset( - (ox0 - fw*bbox.width - bbox.x0, oy0 - fh*bbox.height - bbox.y0)) - - bbox = self.offsetbox.get_window_extent(renderer) - self.patch.set_bounds(bbox.bounds) - - mutation_scale = renderer.points_to_pixels(self.get_fontsize()) - self.patch.set_mutation_scale(mutation_scale) - - if self.arrowprops: - # Use FancyArrowPatch if self.arrowprops has "arrowstyle" key. - - # Adjust the starting point of the arrow relative to the textbox. - # TODO: Rotation needs to be accounted. - arrow_begin = bbox.p0 + bbox.size * self._arrow_relpos - arrow_end = self._get_position_xy(renderer) - # The arrow (from arrow_begin to arrow_end) will be first clipped - # by patchA and patchB, then shrunk by shrinkA and shrinkB (in - # points). If patch A is not set, self.bbox_patch is used. - self.arrow_patch.set_positions(arrow_begin, arrow_end) - - if "mutation_scale" in self.arrowprops: - mutation_scale = renderer.points_to_pixels( - self.arrowprops["mutation_scale"]) - # Else, use fontsize-based mutation_scale defined above. - self.arrow_patch.set_mutation_scale(mutation_scale) - - patchA = self.arrowprops.get("patchA", self.patch) - self.arrow_patch.set_patchA(patchA) - - def draw(self, renderer): - # docstring inherited - if renderer is not None: - self._renderer = renderer - if not self.get_visible() or not self._check_xy(renderer): - return - renderer.open_group(self.__class__.__name__, gid=self.get_gid()) - self.update_positions(renderer) - if self.arrow_patch is not None: - if self.arrow_patch.figure is None and self.figure is not None: - self.arrow_patch.figure = self.figure - self.arrow_patch.draw(renderer) - self.patch.draw(renderer) - self.offsetbox.draw(renderer) - renderer.close_group(self.__class__.__name__) - self.stale = False - - -class DraggableBase: - """ - Helper base class for a draggable artist (legend, offsetbox). - - Derived classes must override the following methods:: - - def save_offset(self): - ''' - Called when the object is picked for dragging; should save the - reference position of the artist. - ''' - - def update_offset(self, dx, dy): - ''' - Called during the dragging; (*dx*, *dy*) is the pixel offset from - the point where the mouse drag started. - ''' - - Optionally, you may override the following method:: - - def finalize_offset(self): - '''Called when the mouse is released.''' - - In the current implementation of `.DraggableLegend` and - `DraggableAnnotation`, `update_offset` places the artists in display - coordinates, and `finalize_offset` recalculates their position in axes - coordinate and set a relevant attribute. - """ - - def __init__(self, ref_artist, use_blit=False): - self.ref_artist = ref_artist - if not ref_artist.pickable(): - ref_artist.set_picker(True) - self.got_artist = False - self._use_blit = use_blit and self.canvas.supports_blit - callbacks = ref_artist.figure._canvas_callbacks - self._disconnectors = [ - functools.partial( - callbacks.disconnect, callbacks._connect_picklable(name, func)) - for name, func in [ - ("pick_event", self.on_pick), - ("button_release_event", self.on_release), - ("motion_notify_event", self.on_motion), - ] - ] - - # A property, not an attribute, to maintain picklability. - canvas = property(lambda self: self.ref_artist.figure.canvas) - - cids = property(lambda self: [ - disconnect.args[0] for disconnect in self._disconnectors[:2]]) - - def on_motion(self, evt): - if self._check_still_parented() and self.got_artist: - dx = evt.x - self.mouse_x - dy = evt.y - self.mouse_y - self.update_offset(dx, dy) - if self._use_blit: - self.canvas.restore_region(self.background) - self.ref_artist.draw( - self.ref_artist.figure._get_renderer()) - self.canvas.blit() - else: - self.canvas.draw() - - def on_pick(self, evt): - if self._check_still_parented() and evt.artist == self.ref_artist: - self.mouse_x = evt.mouseevent.x - self.mouse_y = evt.mouseevent.y - self.got_artist = True - if self._use_blit: - self.ref_artist.set_animated(True) - self.canvas.draw() - self.background = \ - self.canvas.copy_from_bbox(self.ref_artist.figure.bbox) - self.ref_artist.draw( - self.ref_artist.figure._get_renderer()) - self.canvas.blit() - self.save_offset() - - def on_release(self, event): - if self._check_still_parented() and self.got_artist: - self.finalize_offset() - self.got_artist = False - if self._use_blit: - self.ref_artist.set_animated(False) - - def _check_still_parented(self): - if self.ref_artist.figure is None: - self.disconnect() - return False - else: - return True - - def disconnect(self): - """Disconnect the callbacks.""" - for disconnector in self._disconnectors: - disconnector() - - def save_offset(self): - pass - - def update_offset(self, dx, dy): - pass - - def finalize_offset(self): - pass - - -class DraggableOffsetBox(DraggableBase): - def __init__(self, ref_artist, offsetbox, use_blit=False): - super().__init__(ref_artist, use_blit=use_blit) - self.offsetbox = offsetbox - - def save_offset(self): - offsetbox = self.offsetbox - renderer = offsetbox.figure._get_renderer() - offset = offsetbox.get_offset(offsetbox.get_bbox(renderer), renderer) - self.offsetbox_x, self.offsetbox_y = offset - self.offsetbox.set_offset(offset) - - def update_offset(self, dx, dy): - loc_in_canvas = self.offsetbox_x + dx, self.offsetbox_y + dy - self.offsetbox.set_offset(loc_in_canvas) - - def get_loc_in_canvas(self): - offsetbox = self.offsetbox - renderer = offsetbox.figure._get_renderer() - bbox = offsetbox.get_bbox(renderer) - ox, oy = offsetbox._offset - loc_in_canvas = (ox + bbox.x0, oy + bbox.y0) - return loc_in_canvas - - -class DraggableAnnotation(DraggableBase): - def __init__(self, annotation, use_blit=False): - super().__init__(annotation, use_blit=use_blit) - self.annotation = annotation - - def save_offset(self): - ann = self.annotation - self.ox, self.oy = ann.get_transform().transform(ann.xyann) - - def update_offset(self, dx, dy): - ann = self.annotation - ann.xyann = ann.get_transform().inverted().transform( - (self.ox + dx, self.oy + dy)) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/patches.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/patches.py deleted file mode 100644 index 98e96e3..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/patches.py +++ /dev/null @@ -1,4614 +0,0 @@ -r""" -Patches are `.Artist`\s with a face color and an edge color. -""" - -import functools -import inspect -import math -from numbers import Number -import textwrap -from types import SimpleNamespace -from collections import namedtuple -from matplotlib.transforms import Affine2D - -import numpy as np - -import matplotlib as mpl -from . import (_api, artist, cbook, colors, _docstring, hatch as mhatch, - lines as mlines, transforms) -from .bezier import ( - NonIntersectingPathException, get_cos_sin, get_intersection, - get_parallels, inside_circle, make_wedged_bezier2, - split_bezier_intersecting_with_closedpath, split_path_inout) -from .path import Path -from ._enums import JoinStyle, CapStyle - - -@_docstring.interpd -@_api.define_aliases({ - "antialiased": ["aa"], - "edgecolor": ["ec"], - "facecolor": ["fc"], - "linestyle": ["ls"], - "linewidth": ["lw"], -}) -class Patch(artist.Artist): - """ - A patch is a 2D artist with a face color and an edge color. - - If any of *edgecolor*, *facecolor*, *linewidth*, or *antialiased* - are *None*, they default to their rc params setting. - """ - zorder = 1 - - # Whether to draw an edge by default. Set on a - # subclass-by-subclass basis. - _edge_default = False - - @_api.make_keyword_only("3.6", name="edgecolor") - def __init__(self, - edgecolor=None, - facecolor=None, - color=None, - linewidth=None, - linestyle=None, - antialiased=None, - hatch=None, - fill=True, - capstyle=None, - joinstyle=None, - **kwargs): - """ - The following kwarg properties are supported - - %(Patch:kwdoc)s - """ - super().__init__() - - if linestyle is None: - linestyle = "solid" - if capstyle is None: - capstyle = CapStyle.butt - if joinstyle is None: - joinstyle = JoinStyle.miter - - self._hatch_color = colors.to_rgba(mpl.rcParams['hatch.color']) - self._fill = True # needed for set_facecolor call - if color is not None: - if edgecolor is not None or facecolor is not None: - _api.warn_external( - "Setting the 'color' property will override " - "the edgecolor or facecolor properties.") - self.set_color(color) - else: - self.set_edgecolor(edgecolor) - self.set_facecolor(facecolor) - - self._linewidth = 0 - self._unscaled_dash_pattern = (0, None) # offset, dash - self._dash_pattern = (0, None) # offset, dash (scaled by linewidth) - - self.set_fill(fill) - self.set_linestyle(linestyle) - self.set_linewidth(linewidth) - self.set_antialiased(antialiased) - self.set_hatch(hatch) - self.set_capstyle(capstyle) - self.set_joinstyle(joinstyle) - - if len(kwargs): - self._internal_update(kwargs) - - def get_verts(self): - """ - Return a copy of the vertices used in this patch. - - If the patch contains Bézier curves, the curves will be interpolated by - line segments. To access the curves as curves, use `get_path`. - """ - trans = self.get_transform() - path = self.get_path() - polygons = path.to_polygons(trans) - if len(polygons): - return polygons[0] - return [] - - def _process_radius(self, radius): - if radius is not None: - return radius - if isinstance(self._picker, Number): - _radius = self._picker - else: - if self.get_edgecolor()[3] == 0: - _radius = 0 - else: - _radius = self.get_linewidth() - return _radius - - def contains(self, mouseevent, radius=None): - """ - Test whether the mouse event occurred in the patch. - - Returns - ------- - (bool, empty dict) - """ - inside, info = self._default_contains(mouseevent) - if inside is not None: - return inside, info - radius = self._process_radius(radius) - codes = self.get_path().codes - if codes is not None: - vertices = self.get_path().vertices - # if the current path is concatenated by multiple sub paths. - # get the indexes of the starting code(MOVETO) of all sub paths - idxs, = np.where(codes == Path.MOVETO) - # Don't split before the first MOVETO. - idxs = idxs[1:] - subpaths = map( - Path, np.split(vertices, idxs), np.split(codes, idxs)) - else: - subpaths = [self.get_path()] - inside = any( - subpath.contains_point( - (mouseevent.x, mouseevent.y), self.get_transform(), radius) - for subpath in subpaths) - return inside, {} - - def contains_point(self, point, radius=None): - """ - Return whether the given point is inside the patch. - - Parameters - ---------- - point : (float, float) - The point (x, y) to check, in target coordinates of - ``self.get_transform()``. These are display coordinates for patches - that are added to a figure or axes. - radius : float, optional - Additional margin on the patch in target coordinates of - ``self.get_transform()``. See `.Path.contains_point` for further - details. - - Returns - ------- - bool - - Notes - ----- - The proper use of this method depends on the transform of the patch. - Isolated patches do not have a transform. In this case, the patch - creation coordinates and the point coordinates match. The following - example checks that the center of a circle is within the circle - - >>> center = 0, 0 - >>> c = Circle(center, radius=1) - >>> c.contains_point(center) - True - - The convention of checking against the transformed patch stems from - the fact that this method is predominantly used to check if display - coordinates (e.g. from mouse events) are within the patch. If you want - to do the above check with data coordinates, you have to properly - transform them first: - - >>> center = 0, 0 - >>> c = Circle(center, radius=1) - >>> plt.gca().add_patch(c) - >>> transformed_center = c.get_transform().transform(center) - >>> c.contains_point(transformed_center) - True - - """ - radius = self._process_radius(radius) - return self.get_path().contains_point(point, - self.get_transform(), - radius) - - def contains_points(self, points, radius=None): - """ - Return whether the given points are inside the patch. - - Parameters - ---------- - points : (N, 2) array - The points to check, in target coordinates of - ``self.get_transform()``. These are display coordinates for patches - that are added to a figure or axes. Columns contain x and y values. - radius : float, optional - Additional margin on the patch in target coordinates of - ``self.get_transform()``. See `.Path.contains_point` for further - details. - - Returns - ------- - length-N bool array - - Notes - ----- - The proper use of this method depends on the transform of the patch. - See the notes on `.Patch.contains_point`. - """ - radius = self._process_radius(radius) - return self.get_path().contains_points(points, - self.get_transform(), - radius) - - def update_from(self, other): - # docstring inherited. - super().update_from(other) - # For some properties we don't need or don't want to go through the - # getters/setters, so we just copy them directly. - self._edgecolor = other._edgecolor - self._facecolor = other._facecolor - self._original_edgecolor = other._original_edgecolor - self._original_facecolor = other._original_facecolor - self._fill = other._fill - self._hatch = other._hatch - self._hatch_color = other._hatch_color - self._unscaled_dash_pattern = other._unscaled_dash_pattern - self.set_linewidth(other._linewidth) # also sets scaled dashes - self.set_transform(other.get_data_transform()) - # If the transform of other needs further initialization, then it will - # be the case for this artist too. - self._transformSet = other.is_transform_set() - - def get_extents(self): - """ - Return the `Patch`'s axis-aligned extents as a `~.transforms.Bbox`. - """ - return self.get_path().get_extents(self.get_transform()) - - def get_transform(self): - """Return the `~.transforms.Transform` applied to the `Patch`.""" - return self.get_patch_transform() + artist.Artist.get_transform(self) - - def get_data_transform(self): - """ - Return the `~.transforms.Transform` mapping data coordinates to - physical coordinates. - """ - return artist.Artist.get_transform(self) - - def get_patch_transform(self): - """ - Return the `~.transforms.Transform` instance mapping patch coordinates - to data coordinates. - - For example, one may define a patch of a circle which represents a - radius of 5 by providing coordinates for a unit circle, and a - transform which scales the coordinates (the patch coordinate) by 5. - """ - return transforms.IdentityTransform() - - def get_antialiased(self): - """Return whether antialiasing is used for drawing.""" - return self._antialiased - - def get_edgecolor(self): - """Return the edge color.""" - return self._edgecolor - - def get_facecolor(self): - """Return the face color.""" - return self._facecolor - - def get_linewidth(self): - """Return the line width in points.""" - return self._linewidth - - def get_linestyle(self): - """Return the linestyle.""" - return self._linestyle - - def set_antialiased(self, aa): - """ - Set whether to use antialiased rendering. - - Parameters - ---------- - aa : bool or None - """ - if aa is None: - aa = mpl.rcParams['patch.antialiased'] - self._antialiased = aa - self.stale = True - - def _set_edgecolor(self, color): - set_hatch_color = True - if color is None: - if (mpl.rcParams['patch.force_edgecolor'] or - not self._fill or self._edge_default): - color = mpl.rcParams['patch.edgecolor'] - else: - color = 'none' - set_hatch_color = False - - self._edgecolor = colors.to_rgba(color, self._alpha) - if set_hatch_color: - self._hatch_color = self._edgecolor - self.stale = True - - def set_edgecolor(self, color): - """ - Set the patch edge color. - - Parameters - ---------- - color : color or None - """ - self._original_edgecolor = color - self._set_edgecolor(color) - - def _set_facecolor(self, color): - if color is None: - color = mpl.rcParams['patch.facecolor'] - alpha = self._alpha if self._fill else 0 - self._facecolor = colors.to_rgba(color, alpha) - self.stale = True - - def set_facecolor(self, color): - """ - Set the patch face color. - - Parameters - ---------- - color : color or None - """ - self._original_facecolor = color - self._set_facecolor(color) - - def set_color(self, c): - """ - Set both the edgecolor and the facecolor. - - Parameters - ---------- - c : color - - See Also - -------- - Patch.set_facecolor, Patch.set_edgecolor - For setting the edge or face color individually. - """ - self.set_facecolor(c) - self.set_edgecolor(c) - - def set_alpha(self, alpha): - # docstring inherited - super().set_alpha(alpha) - self._set_facecolor(self._original_facecolor) - self._set_edgecolor(self._original_edgecolor) - # stale is already True - - def set_linewidth(self, w): - """ - Set the patch linewidth in points. - - Parameters - ---------- - w : float or None - """ - if w is None: - w = mpl.rcParams['patch.linewidth'] - self._linewidth = float(w) - self._dash_pattern = mlines._scale_dashes( - *self._unscaled_dash_pattern, w) - self.stale = True - - def set_linestyle(self, ls): - """ - Set the patch linestyle. - - ========================================== ================= - linestyle description - ========================================== ================= - ``'-'`` or ``'solid'`` solid line - ``'--'`` or ``'dashed'`` dashed line - ``'-.'`` or ``'dashdot'`` dash-dotted line - ``':'`` or ``'dotted'`` dotted line - ``'none'``, ``'None'``, ``' '``, or ``''`` draw nothing - ========================================== ================= - - Alternatively a dash tuple of the following form can be provided:: - - (offset, onoffseq) - - where ``onoffseq`` is an even length tuple of on and off ink in points. - - Parameters - ---------- - ls : {'-', '--', '-.', ':', '', (offset, on-off-seq), ...} - The line style. - """ - if ls is None: - ls = "solid" - if ls in [' ', '', 'none']: - ls = 'None' - self._linestyle = ls - self._unscaled_dash_pattern = mlines._get_dash_pattern(ls) - self._dash_pattern = mlines._scale_dashes( - *self._unscaled_dash_pattern, self._linewidth) - self.stale = True - - def set_fill(self, b): - """ - Set whether to fill the patch. - - Parameters - ---------- - b : bool - """ - self._fill = bool(b) - self._set_facecolor(self._original_facecolor) - self._set_edgecolor(self._original_edgecolor) - self.stale = True - - def get_fill(self): - """Return whether the patch is filled.""" - return self._fill - - # Make fill a property so as to preserve the long-standing - # but somewhat inconsistent behavior in which fill was an - # attribute. - fill = property(get_fill, set_fill) - - @_docstring.interpd - def set_capstyle(self, s): - """ - Set the `.CapStyle`. - - The default capstyle is 'round' for `.FancyArrowPatch` and 'butt' for - all other patches. - - Parameters - ---------- - s : `.CapStyle` or %(CapStyle)s - """ - cs = CapStyle(s) - self._capstyle = cs - self.stale = True - - def get_capstyle(self): - """Return the capstyle.""" - return self._capstyle.name - - @_docstring.interpd - def set_joinstyle(self, s): - """ - Set the `.JoinStyle`. - - The default joinstyle is 'round' for `.FancyArrowPatch` and 'miter' for - all other patches. - - Parameters - ---------- - s : `.JoinStyle` or %(JoinStyle)s - """ - js = JoinStyle(s) - self._joinstyle = js - self.stale = True - - def get_joinstyle(self): - """Return the joinstyle.""" - return self._joinstyle.name - - def set_hatch(self, hatch): - r""" - Set the hatching pattern. - - *hatch* can be one of:: - - / - diagonal hatching - \ - back diagonal - | - vertical - - - horizontal - + - crossed - x - crossed diagonal - o - small circle - O - large circle - . - dots - * - stars - - Letters can be combined, in which case all the specified - hatchings are done. If same letter repeats, it increases the - density of hatching of that pattern. - - Hatching is supported in the PostScript, PDF, SVG and Agg - backends only. - - Parameters - ---------- - hatch : {'/', '\\', '|', '-', '+', 'x', 'o', 'O', '.', '*'} - """ - # Use validate_hatch(list) after deprecation. - mhatch._validate_hatch_pattern(hatch) - self._hatch = hatch - self.stale = True - - def get_hatch(self): - """Return the hatching pattern.""" - return self._hatch - - def _draw_paths_with_artist_properties( - self, renderer, draw_path_args_list): - """ - ``draw()`` helper factored out for sharing with `FancyArrowPatch`. - - Configure *renderer* and the associated graphics context *gc* - from the artist properties, then repeatedly call - ``renderer.draw_path(gc, *draw_path_args)`` for each tuple - *draw_path_args* in *draw_path_args_list*. - """ - - renderer.open_group('patch', self.get_gid()) - gc = renderer.new_gc() - - gc.set_foreground(self._edgecolor, isRGBA=True) - - lw = self._linewidth - if self._edgecolor[3] == 0 or self._linestyle == 'None': - lw = 0 - gc.set_linewidth(lw) - gc.set_dashes(*self._dash_pattern) - gc.set_capstyle(self._capstyle) - gc.set_joinstyle(self._joinstyle) - - gc.set_antialiased(self._antialiased) - self._set_gc_clip(gc) - gc.set_url(self._url) - gc.set_snap(self.get_snap()) - - gc.set_alpha(self._alpha) - - if self._hatch: - gc.set_hatch(self._hatch) - gc.set_hatch_color(self._hatch_color) - - if self.get_sketch_params() is not None: - gc.set_sketch_params(*self.get_sketch_params()) - - if self.get_path_effects(): - from matplotlib.patheffects import PathEffectRenderer - renderer = PathEffectRenderer(self.get_path_effects(), renderer) - - for draw_path_args in draw_path_args_list: - renderer.draw_path(gc, *draw_path_args) - - gc.restore() - renderer.close_group('patch') - self.stale = False - - @artist.allow_rasterization - def draw(self, renderer): - # docstring inherited - if not self.get_visible(): - return - path = self.get_path() - transform = self.get_transform() - tpath = transform.transform_path_non_affine(path) - affine = transform.get_affine() - self._draw_paths_with_artist_properties( - renderer, - [(tpath, affine, - # Work around a bug in the PDF and SVG renderers, which - # do not draw the hatches if the facecolor is fully - # transparent, but do if it is None. - self._facecolor if self._facecolor[3] else None)]) - - def get_path(self): - """Return the path of this patch.""" - raise NotImplementedError('Derived must override') - - def get_window_extent(self, renderer=None): - return self.get_path().get_extents(self.get_transform()) - - def _convert_xy_units(self, xy): - """Convert x and y units for a tuple (x, y).""" - x = self.convert_xunits(xy[0]) - y = self.convert_yunits(xy[1]) - return x, y - - -class Shadow(Patch): - def __str__(self): - return f"Shadow({self.patch})" - - @_docstring.dedent_interpd - def __init__(self, patch, ox, oy, **kwargs): - """ - Create a shadow of the given *patch*. - - By default, the shadow will have the same face color as the *patch*, - but darkened. - - Parameters - ---------- - patch : `~matplotlib.patches.Patch` - The patch to create the shadow for. - ox, oy : float - The shift of the shadow in data coordinates, scaled by a factor - of dpi/72. - **kwargs - Properties of the shadow patch. Supported keys are: - - %(Patch:kwdoc)s - """ - super().__init__() - self.patch = patch - self._ox, self._oy = ox, oy - self._shadow_transform = transforms.Affine2D() - - self.update_from(self.patch) - color = .3 * np.asarray(colors.to_rgb(self.patch.get_facecolor())) - self.update({'facecolor': color, 'edgecolor': color, 'alpha': 0.5, - # Place shadow patch directly behind the inherited patch. - 'zorder': np.nextafter(self.patch.zorder, -np.inf), - **kwargs}) - - def _update_transform(self, renderer): - ox = renderer.points_to_pixels(self._ox) - oy = renderer.points_to_pixels(self._oy) - self._shadow_transform.clear().translate(ox, oy) - - def get_path(self): - return self.patch.get_path() - - def get_patch_transform(self): - return self.patch.get_patch_transform() + self._shadow_transform - - def draw(self, renderer): - self._update_transform(renderer) - super().draw(renderer) - - -class Rectangle(Patch): - """ - A rectangle defined via an anchor point *xy* and its *width* and *height*. - - The rectangle extends from ``xy[0]`` to ``xy[0] + width`` in x-direction - and from ``xy[1]`` to ``xy[1] + height`` in y-direction. :: - - : +------------------+ - : | | - : height | - : | | - : (xy)---- width -----+ - - One may picture *xy* as the bottom left corner, but which corner *xy* is - actually depends on the direction of the axis and the sign of *width* - and *height*; e.g. *xy* would be the bottom right corner if the x-axis - was inverted or if *width* was negative. - """ - - def __str__(self): - pars = self._x0, self._y0, self._width, self._height, self.angle - fmt = "Rectangle(xy=(%g, %g), width=%g, height=%g, angle=%g)" - return fmt % pars - - @_docstring.dedent_interpd - @_api.make_keyword_only("3.6", name="angle") - def __init__(self, xy, width, height, angle=0.0, *, - rotation_point='xy', **kwargs): - """ - Parameters - ---------- - xy : (float, float) - The anchor point. - width : float - Rectangle width. - height : float - Rectangle height. - angle : float, default: 0 - Rotation in degrees anti-clockwise about the rotation point. - rotation_point : {'xy', 'center', (number, number)}, default: 'xy' - If ``'xy'``, rotate around the anchor point. If ``'center'`` rotate - around the center. If 2-tuple of number, rotate around this - coordinate. - - Other Parameters - ---------------- - **kwargs : `~matplotlib.patches.Patch` properties - %(Patch:kwdoc)s - """ - super().__init__(**kwargs) - self._x0 = xy[0] - self._y0 = xy[1] - self._width = width - self._height = height - self.angle = float(angle) - self.rotation_point = rotation_point - # Required for RectangleSelector with axes aspect ratio != 1 - # The patch is defined in data coordinates and when changing the - # selector with square modifier and not in data coordinates, we need - # to correct for the aspect ratio difference between the data and - # display coordinate systems. Its value is typically provide by - # Axes._get_aspect_ratio() - self._aspect_ratio_correction = 1.0 - self._convert_units() # Validate the inputs. - - def get_path(self): - """Return the vertices of the rectangle.""" - return Path.unit_rectangle() - - def _convert_units(self): - """Convert bounds of the rectangle.""" - x0 = self.convert_xunits(self._x0) - y0 = self.convert_yunits(self._y0) - x1 = self.convert_xunits(self._x0 + self._width) - y1 = self.convert_yunits(self._y0 + self._height) - return x0, y0, x1, y1 - - def get_patch_transform(self): - # Note: This cannot be called until after this has been added to - # an Axes, otherwise unit conversion will fail. This makes it very - # important to call the accessor method and not directly access the - # transformation member variable. - bbox = self.get_bbox() - if self.rotation_point == 'center': - width, height = bbox.x1 - bbox.x0, bbox.y1 - bbox.y0 - rotation_point = bbox.x0 + width / 2., bbox.y0 + height / 2. - elif self.rotation_point == 'xy': - rotation_point = bbox.x0, bbox.y0 - else: - rotation_point = self.rotation_point - return transforms.BboxTransformTo(bbox) \ - + transforms.Affine2D() \ - .translate(-rotation_point[0], -rotation_point[1]) \ - .scale(1, self._aspect_ratio_correction) \ - .rotate_deg(self.angle) \ - .scale(1, 1 / self._aspect_ratio_correction) \ - .translate(*rotation_point) - - @property - def rotation_point(self): - """The rotation point of the patch.""" - return self._rotation_point - - @rotation_point.setter - def rotation_point(self, value): - if value in ['center', 'xy'] or ( - isinstance(value, tuple) and len(value) == 2 and - isinstance(value[0], Number) and isinstance(value[1], Number) - ): - self._rotation_point = value - else: - raise ValueError("`rotation_point` must be one of " - "{'xy', 'center', (number, number)}.") - - def get_x(self): - """Return the left coordinate of the rectangle.""" - return self._x0 - - def get_y(self): - """Return the bottom coordinate of the rectangle.""" - return self._y0 - - def get_xy(self): - """Return the left and bottom coords of the rectangle as a tuple.""" - return self._x0, self._y0 - - def get_corners(self): - """ - Return the corners of the rectangle, moving anti-clockwise from - (x0, y0). - """ - return self.get_patch_transform().transform( - [(0, 0), (1, 0), (1, 1), (0, 1)]) - - def get_center(self): - """Return the centre of the rectangle.""" - return self.get_patch_transform().transform((0.5, 0.5)) - - def get_width(self): - """Return the width of the rectangle.""" - return self._width - - def get_height(self): - """Return the height of the rectangle.""" - return self._height - - def get_angle(self): - """Get the rotation angle in degrees.""" - return self.angle - - def set_x(self, x): - """Set the left coordinate of the rectangle.""" - self._x0 = x - self.stale = True - - def set_y(self, y): - """Set the bottom coordinate of the rectangle.""" - self._y0 = y - self.stale = True - - def set_angle(self, angle): - """ - Set the rotation angle in degrees. - - The rotation is performed anti-clockwise around *xy*. - """ - self.angle = angle - self.stale = True - - def set_xy(self, xy): - """ - Set the left and bottom coordinates of the rectangle. - - Parameters - ---------- - xy : (float, float) - """ - self._x0, self._y0 = xy - self.stale = True - - def set_width(self, w): - """Set the width of the rectangle.""" - self._width = w - self.stale = True - - def set_height(self, h): - """Set the height of the rectangle.""" - self._height = h - self.stale = True - - def set_bounds(self, *args): - """ - Set the bounds of the rectangle as *left*, *bottom*, *width*, *height*. - - The values may be passed as separate parameters or as a tuple:: - - set_bounds(left, bottom, width, height) - set_bounds((left, bottom, width, height)) - - .. ACCEPTS: (left, bottom, width, height) - """ - if len(args) == 1: - l, b, w, h = args[0] - else: - l, b, w, h = args - self._x0 = l - self._y0 = b - self._width = w - self._height = h - self.stale = True - - def get_bbox(self): - """Return the `.Bbox`.""" - x0, y0, x1, y1 = self._convert_units() - return transforms.Bbox.from_extents(x0, y0, x1, y1) - - xy = property(get_xy, set_xy) - - -class RegularPolygon(Patch): - """A regular polygon patch.""" - - def __str__(self): - s = "RegularPolygon((%g, %g), %d, radius=%g, orientation=%g)" - return s % (self.xy[0], self.xy[1], self.numvertices, self.radius, - self.orientation) - - @_docstring.dedent_interpd - @_api.make_keyword_only("3.6", name="radius") - def __init__(self, xy, numVertices, radius=5, orientation=0, - **kwargs): - """ - Parameters - ---------- - xy : (float, float) - The center position. - - numVertices : int - The number of vertices. - - radius : float - The distance from the center to each of the vertices. - - orientation : float - The polygon rotation angle (in radians). - - **kwargs - `Patch` properties: - - %(Patch:kwdoc)s - """ - self.xy = xy - self.numvertices = numVertices - self.orientation = orientation - self.radius = radius - self._path = Path.unit_regular_polygon(numVertices) - self._patch_transform = transforms.Affine2D() - super().__init__(**kwargs) - - def get_path(self): - return self._path - - def get_patch_transform(self): - return self._patch_transform.clear() \ - .scale(self.radius) \ - .rotate(self.orientation) \ - .translate(*self.xy) - - -class PathPatch(Patch): - """A general polycurve path patch.""" - - _edge_default = True - - def __str__(self): - s = "PathPatch%d((%g, %g) ...)" - return s % (len(self._path.vertices), *tuple(self._path.vertices[0])) - - @_docstring.dedent_interpd - def __init__(self, path, **kwargs): - """ - *path* is a `.Path` object. - - Valid keyword arguments are: - - %(Patch:kwdoc)s - """ - super().__init__(**kwargs) - self._path = path - - def get_path(self): - return self._path - - def set_path(self, path): - self._path = path - - -class StepPatch(PathPatch): - """ - A path patch describing a stepwise constant function. - - By default, the path is not closed and starts and stops at - baseline value. - """ - - _edge_default = False - - @_docstring.dedent_interpd - def __init__(self, values, edges, *, - orientation='vertical', baseline=0, **kwargs): - """ - Parameters - ---------- - values : array-like - The step heights. - - edges : array-like - The edge positions, with ``len(edges) == len(vals) + 1``, - between which the curve takes on vals values. - - orientation : {'vertical', 'horizontal'}, default: 'vertical' - The direction of the steps. Vertical means that *values* are - along the y-axis, and edges are along the x-axis. - - baseline : float, array-like or None, default: 0 - The bottom value of the bounding edges or when - ``fill=True``, position of lower edge. If *fill* is - True or an array is passed to *baseline*, a closed - path is drawn. - - Other valid keyword arguments are: - - %(Patch:kwdoc)s - """ - self.orientation = orientation - self._edges = np.asarray(edges) - self._values = np.asarray(values) - self._baseline = np.asarray(baseline) if baseline is not None else None - self._update_path() - super().__init__(self._path, **kwargs) - - def _update_path(self): - if np.isnan(np.sum(self._edges)): - raise ValueError('Nan values in "edges" are disallowed') - if self._edges.size - 1 != self._values.size: - raise ValueError('Size mismatch between "values" and "edges". ' - "Expected `len(values) + 1 == len(edges)`, but " - f"`len(values) = {self._values.size}` and " - f"`len(edges) = {self._edges.size}`.") - # Initializing with empty arrays allows supporting empty stairs. - verts, codes = [np.empty((0, 2))], [np.empty(0, dtype=Path.code_type)] - - _nan_mask = np.isnan(self._values) - if self._baseline is not None: - _nan_mask |= np.isnan(self._baseline) - for idx0, idx1 in cbook.contiguous_regions(~_nan_mask): - x = np.repeat(self._edges[idx0:idx1+1], 2) - y = np.repeat(self._values[idx0:idx1], 2) - if self._baseline is None: - y = np.concatenate([y[:1], y, y[-1:]]) - elif self._baseline.ndim == 0: # single baseline value - y = np.concatenate([[self._baseline], y, [self._baseline]]) - elif self._baseline.ndim == 1: # baseline array - base = np.repeat(self._baseline[idx0:idx1], 2)[::-1] - x = np.concatenate([x, x[::-1]]) - y = np.concatenate([base[-1:], y, base[:1], - base[:1], base, base[-1:]]) - else: # no baseline - raise ValueError('Invalid `baseline` specified') - if self.orientation == 'vertical': - xy = np.column_stack([x, y]) - else: - xy = np.column_stack([y, x]) - verts.append(xy) - codes.append([Path.MOVETO] + [Path.LINETO]*(len(xy)-1)) - self._path = Path(np.concatenate(verts), np.concatenate(codes)) - - def get_data(self): - """Get `.StepPatch` values, edges and baseline as namedtuple.""" - StairData = namedtuple('StairData', 'values edges baseline') - return StairData(self._values, self._edges, self._baseline) - - def set_data(self, values=None, edges=None, baseline=None): - """ - Set `.StepPatch` values, edges and baseline. - - Parameters - ---------- - values : 1D array-like or None - Will not update values, if passing None - edges : 1D array-like, optional - baseline : float, 1D array-like or None - """ - if values is None and edges is None and baseline is None: - raise ValueError("Must set *values*, *edges* or *baseline*.") - if values is not None: - self._values = np.asarray(values) - if edges is not None: - self._edges = np.asarray(edges) - if baseline is not None: - self._baseline = np.asarray(baseline) - self._update_path() - self.stale = True - - -class Polygon(Patch): - """A general polygon patch.""" - - def __str__(self): - if len(self._path.vertices): - s = "Polygon%d((%g, %g) ...)" - return s % (len(self._path.vertices), *self._path.vertices[0]) - else: - return "Polygon0()" - - @_docstring.dedent_interpd - @_api.make_keyword_only("3.6", name="closed") - def __init__(self, xy, closed=True, **kwargs): - """ - *xy* is a numpy array with shape Nx2. - - If *closed* is *True*, the polygon will be closed so the - starting and ending points are the same. - - Valid keyword arguments are: - - %(Patch:kwdoc)s - """ - super().__init__(**kwargs) - self._closed = closed - self.set_xy(xy) - - def get_path(self): - """Get the `.Path` of the polygon.""" - return self._path - - def get_closed(self): - """Return whether the polygon is closed.""" - return self._closed - - def set_closed(self, closed): - """ - Set whether the polygon is closed. - - Parameters - ---------- - closed : bool - True if the polygon is closed - """ - if self._closed == bool(closed): - return - self._closed = bool(closed) - self.set_xy(self.get_xy()) - self.stale = True - - def get_xy(self): - """ - Get the vertices of the path. - - Returns - ------- - (N, 2) numpy array - The coordinates of the vertices. - """ - return self._path.vertices - - def set_xy(self, xy): - """ - Set the vertices of the polygon. - - Parameters - ---------- - xy : (N, 2) array-like - The coordinates of the vertices. - - Notes - ----- - Unlike `.Path`, we do not ignore the last input vertex. If the - polygon is meant to be closed, and the last point of the polygon is not - equal to the first, we assume that the user has not explicitly passed a - ``CLOSEPOLY`` vertex, and add it ourselves. - """ - xy = np.asarray(xy) - nverts, _ = xy.shape - if self._closed: - # if the first and last vertex are the "same", then we assume that - # the user explicitly passed the CLOSEPOLY vertex. Otherwise, we - # have to append one since the last vertex will be "ignored" by - # Path - if nverts == 1 or nverts > 1 and (xy[0] != xy[-1]).any(): - xy = np.concatenate([xy, [xy[0]]]) - else: - # if we aren't closed, and the last vertex matches the first, then - # we assume we have an unnecessary CLOSEPOLY vertex and remove it - if nverts > 2 and (xy[0] == xy[-1]).all(): - xy = xy[:-1] - self._path = Path(xy, closed=self._closed) - self.stale = True - - xy = property(get_xy, set_xy, - doc='The vertices of the path as (N, 2) numpy array.') - - -class Wedge(Patch): - """Wedge shaped patch.""" - - def __str__(self): - pars = (self.center[0], self.center[1], self.r, - self.theta1, self.theta2, self.width) - fmt = "Wedge(center=(%g, %g), r=%g, theta1=%g, theta2=%g, width=%s)" - return fmt % pars - - @_docstring.dedent_interpd - @_api.make_keyword_only("3.6", name="width") - def __init__(self, center, r, theta1, theta2, width=None, **kwargs): - """ - A wedge centered at *x*, *y* center with radius *r* that - sweeps *theta1* to *theta2* (in degrees). If *width* is given, - then a partial wedge is drawn from inner radius *r* - *width* - to outer radius *r*. - - Valid keyword arguments are: - - %(Patch:kwdoc)s - """ - super().__init__(**kwargs) - self.center = center - self.r, self.width = r, width - self.theta1, self.theta2 = theta1, theta2 - self._patch_transform = transforms.IdentityTransform() - self._recompute_path() - - def _recompute_path(self): - # Inner and outer rings are connected unless the annulus is complete - if abs((self.theta2 - self.theta1) - 360) <= 1e-12: - theta1, theta2 = 0, 360 - connector = Path.MOVETO - else: - theta1, theta2 = self.theta1, self.theta2 - connector = Path.LINETO - - # Form the outer ring - arc = Path.arc(theta1, theta2) - - if self.width is not None: - # Partial annulus needs to draw the outer ring - # followed by a reversed and scaled inner ring - v1 = arc.vertices - v2 = arc.vertices[::-1] * (self.r - self.width) / self.r - v = np.concatenate([v1, v2, [(0, 0)]]) - c = [*arc.codes, connector, *arc.codes[1:], Path.CLOSEPOLY] - else: - # Wedge doesn't need an inner ring - v = np.concatenate([arc.vertices, [(0, 0), (0, 0)]]) - c = [*arc.codes, connector, Path.CLOSEPOLY] - - # Shift and scale the wedge to the final location. - self._path = Path(v * self.r + self.center, c) - - def set_center(self, center): - self._path = None - self.center = center - self.stale = True - - def set_radius(self, radius): - self._path = None - self.r = radius - self.stale = True - - def set_theta1(self, theta1): - self._path = None - self.theta1 = theta1 - self.stale = True - - def set_theta2(self, theta2): - self._path = None - self.theta2 = theta2 - self.stale = True - - def set_width(self, width): - self._path = None - self.width = width - self.stale = True - - def get_path(self): - if self._path is None: - self._recompute_path() - return self._path - - -# COVERAGE NOTE: Not used internally or from examples -class Arrow(Patch): - """An arrow patch.""" - - def __str__(self): - return "Arrow()" - - _path = Path._create_closed([ - [0.0, 0.1], [0.0, -0.1], [0.8, -0.1], [0.8, -0.3], [1.0, 0.0], - [0.8, 0.3], [0.8, 0.1]]) - - @_docstring.dedent_interpd - @_api.make_keyword_only("3.6", name="width") - def __init__(self, x, y, dx, dy, width=1.0, **kwargs): - """ - Draws an arrow from (*x*, *y*) to (*x* + *dx*, *y* + *dy*). - The width of the arrow is scaled by *width*. - - Parameters - ---------- - x : float - x coordinate of the arrow tail. - y : float - y coordinate of the arrow tail. - dx : float - Arrow length in the x direction. - dy : float - Arrow length in the y direction. - width : float, default: 1 - Scale factor for the width of the arrow. With a default value of 1, - the tail width is 0.2 and head width is 0.6. - **kwargs - Keyword arguments control the `Patch` properties: - - %(Patch:kwdoc)s - - See Also - -------- - FancyArrow - Patch that allows independent control of the head and tail - properties. - """ - super().__init__(**kwargs) - self._patch_transform = ( - transforms.Affine2D() - .scale(np.hypot(dx, dy), width) - .rotate(np.arctan2(dy, dx)) - .translate(x, y) - .frozen()) - - def get_path(self): - return self._path - - def get_patch_transform(self): - return self._patch_transform - - -class FancyArrow(Polygon): - """ - Like Arrow, but lets you set head width and head height independently. - """ - - _edge_default = True - - def __str__(self): - return "FancyArrow()" - - @_docstring.dedent_interpd - @_api.make_keyword_only("3.6", name="width") - def __init__(self, x, y, dx, dy, width=0.001, length_includes_head=False, - head_width=None, head_length=None, shape='full', overhang=0, - head_starts_at_zero=False, **kwargs): - """ - Parameters - ---------- - x, y : float - The x and y coordinates of the arrow base. - - dx, dy : float - The length of the arrow along x and y direction. - - width : float, default: 0.001 - Width of full arrow tail. - - length_includes_head : bool, default: False - True if head is to be counted in calculating the length. - - head_width : float or None, default: 3*width - Total width of the full arrow head. - - head_length : float or None, default: 1.5*head_width - Length of arrow head. - - shape : {'full', 'left', 'right'}, default: 'full' - Draw the left-half, right-half, or full arrow. - - overhang : float, default: 0 - Fraction that the arrow is swept back (0 overhang means - triangular shape). Can be negative or greater than one. - - head_starts_at_zero : bool, default: False - If True, the head starts being drawn at coordinate 0 - instead of ending at coordinate 0. - - **kwargs - `.Patch` properties: - - %(Patch:kwdoc)s - """ - self._x = x - self._y = y - self._dx = dx - self._dy = dy - self._width = width - self._length_includes_head = length_includes_head - self._head_width = head_width - self._head_length = head_length - self._shape = shape - self._overhang = overhang - self._head_starts_at_zero = head_starts_at_zero - self._make_verts() - super().__init__(self.verts, closed=True, **kwargs) - - def set_data(self, *, x=None, y=None, dx=None, dy=None, width=None, - head_width=None, head_length=None): - """ - Set `.FancyArrow` x, y, dx, dy, width, head_with, and head_length. - Values left as None will not be updated. - - Parameters - ---------- - x, y : float or None, default: None - The x and y coordinates of the arrow base. - - dx, dy : float or None, default: None - The length of the arrow along x and y direction. - - width : float or None, default: None - Width of full arrow tail. - - head_width : float or None, default: None - Total width of the full arrow head. - - head_length : float or None, default: None - Length of arrow head. - """ - if x is not None: - self._x = x - if y is not None: - self._y = y - if dx is not None: - self._dx = dx - if dy is not None: - self._dy = dy - if width is not None: - self._width = width - if head_width is not None: - self._head_width = head_width - if head_length is not None: - self._head_length = head_length - self._make_verts() - self.set_xy(self.verts) - - def _make_verts(self): - if self._head_width is None: - head_width = 3 * self._width - else: - head_width = self._head_width - if self._head_length is None: - head_length = 1.5 * head_width - else: - head_length = self._head_length - - distance = np.hypot(self._dx, self._dy) - - if self._length_includes_head: - length = distance - else: - length = distance + head_length - if not length: - self.verts = np.empty([0, 2]) # display nothing if empty - else: - # start by drawing horizontal arrow, point at (0, 0) - hw, hl = head_width, head_length - hs, lw = self._overhang, self._width - left_half_arrow = np.array([ - [0.0, 0.0], # tip - [-hl, -hw / 2], # leftmost - [-hl * (1 - hs), -lw / 2], # meets stem - [-length, -lw / 2], # bottom left - [-length, 0], - ]) - # if we're not including the head, shift up by head length - if not self._length_includes_head: - left_half_arrow += [head_length, 0] - # if the head starts at 0, shift up by another head length - if self._head_starts_at_zero: - left_half_arrow += [head_length / 2, 0] - # figure out the shape, and complete accordingly - if self._shape == 'left': - coords = left_half_arrow - else: - right_half_arrow = left_half_arrow * [1, -1] - if self._shape == 'right': - coords = right_half_arrow - elif self._shape == 'full': - # The half-arrows contain the midpoint of the stem, - # which we can omit from the full arrow. Including it - # twice caused a problem with xpdf. - coords = np.concatenate([left_half_arrow[:-1], - right_half_arrow[-2::-1]]) - else: - raise ValueError(f"Got unknown shape: {self._shape!r}") - if distance != 0: - cx = self._dx / distance - sx = self._dy / distance - else: - # Account for division by zero - cx, sx = 0, 1 - M = [[cx, sx], [-sx, cx]] - self.verts = np.dot(coords, M) + [ - self._x + self._dx, - self._y + self._dy, - ] - - -_docstring.interpd.update( - FancyArrow="\n".join( - (inspect.getdoc(FancyArrow.__init__) or "").splitlines()[2:])) - - -class CirclePolygon(RegularPolygon): - """A polygon-approximation of a circle patch.""" - - def __str__(self): - s = "CirclePolygon((%g, %g), radius=%g, resolution=%d)" - return s % (self.xy[0], self.xy[1], self.radius, self.numvertices) - - @_docstring.dedent_interpd - @_api.make_keyword_only("3.6", name="resolution") - def __init__(self, xy, radius=5, - resolution=20, # the number of vertices - ** kwargs): - """ - Create a circle at *xy* = (*x*, *y*) with given *radius*. - - This circle is approximated by a regular polygon with *resolution* - sides. For a smoother circle drawn with splines, see `Circle`. - - Valid keyword arguments are: - - %(Patch:kwdoc)s - """ - super().__init__( - xy, resolution, radius=radius, orientation=0, **kwargs) - - -class Ellipse(Patch): - """A scale-free ellipse.""" - - def __str__(self): - pars = (self._center[0], self._center[1], - self.width, self.height, self.angle) - fmt = "Ellipse(xy=(%s, %s), width=%s, height=%s, angle=%s)" - return fmt % pars - - @_docstring.dedent_interpd - @_api.make_keyword_only("3.6", name="angle") - def __init__(self, xy, width, height, angle=0, **kwargs): - """ - Parameters - ---------- - xy : (float, float) - xy coordinates of ellipse centre. - width : float - Total length (diameter) of horizontal axis. - height : float - Total length (diameter) of vertical axis. - angle : float, default: 0 - Rotation in degrees anti-clockwise. - - Notes - ----- - Valid keyword arguments are: - - %(Patch:kwdoc)s - """ - super().__init__(**kwargs) - - self._center = xy - self._width, self._height = width, height - self._angle = angle - self._path = Path.unit_circle() - # Required for EllipseSelector with axes aspect ratio != 1 - # The patch is defined in data coordinates and when changing the - # selector with square modifier and not in data coordinates, we need - # to correct for the aspect ratio difference between the data and - # display coordinate systems. - self._aspect_ratio_correction = 1.0 - # Note: This cannot be calculated until this is added to an Axes - self._patch_transform = transforms.IdentityTransform() - - def _recompute_transform(self): - """ - Notes - ----- - This cannot be called until after this has been added to an Axes, - otherwise unit conversion will fail. This makes it very important to - call the accessor method and not directly access the transformation - member variable. - """ - center = (self.convert_xunits(self._center[0]), - self.convert_yunits(self._center[1])) - width = self.convert_xunits(self._width) - height = self.convert_yunits(self._height) - self._patch_transform = transforms.Affine2D() \ - .scale(width * 0.5, height * 0.5 * self._aspect_ratio_correction) \ - .rotate_deg(self.angle) \ - .scale(1, 1 / self._aspect_ratio_correction) \ - .translate(*center) - - def get_path(self): - """Return the path of the ellipse.""" - return self._path - - def get_patch_transform(self): - self._recompute_transform() - return self._patch_transform - - def set_center(self, xy): - """ - Set the center of the ellipse. - - Parameters - ---------- - xy : (float, float) - """ - self._center = xy - self.stale = True - - def get_center(self): - """Return the center of the ellipse.""" - return self._center - - center = property(get_center, set_center) - - def set_width(self, width): - """ - Set the width of the ellipse. - - Parameters - ---------- - width : float - """ - self._width = width - self.stale = True - - def get_width(self): - """ - Return the width of the ellipse. - """ - return self._width - - width = property(get_width, set_width) - - def set_height(self, height): - """ - Set the height of the ellipse. - - Parameters - ---------- - height : float - """ - self._height = height - self.stale = True - - def get_height(self): - """Return the height of the ellipse.""" - return self._height - - height = property(get_height, set_height) - - def set_angle(self, angle): - """ - Set the angle of the ellipse. - - Parameters - ---------- - angle : float - """ - self._angle = angle - self.stale = True - - def get_angle(self): - """Return the angle of the ellipse.""" - return self._angle - - angle = property(get_angle, set_angle) - - def get_corners(self): - """ - Return the corners of the ellipse bounding box. - - The bounding box orientation is moving anti-clockwise from the - lower left corner defined before rotation. - """ - return self.get_patch_transform().transform( - [(-1, -1), (1, -1), (1, 1), (-1, 1)]) - - -class Annulus(Patch): - """ - An elliptical annulus. - """ - - @_docstring.dedent_interpd - def __init__(self, xy, r, width, angle=0.0, **kwargs): - """ - Parameters - ---------- - xy : (float, float) - xy coordinates of annulus centre. - r : float or (float, float) - The radius, or semi-axes: - - - If float: radius of the outer circle. - - If two floats: semi-major and -minor axes of outer ellipse. - width : float - Width (thickness) of the annular ring. The width is measured inward - from the outer ellipse so that for the inner ellipse the semi-axes - are given by ``r - width``. *width* must be less than or equal to - the semi-minor axis. - angle : float, default: 0 - Rotation angle in degrees (anti-clockwise from the positive - x-axis). Ignored for circular annuli (i.e., if *r* is a scalar). - **kwargs - Keyword arguments control the `Patch` properties: - - %(Patch:kwdoc)s - """ - super().__init__(**kwargs) - - self.set_radii(r) - self.center = xy - self.width = width - self.angle = angle - self._path = None - - def __str__(self): - if self.a == self.b: - r = self.a - else: - r = (self.a, self.b) - - return "Annulus(xy=(%s, %s), r=%s, width=%s, angle=%s)" % \ - (*self.center, r, self.width, self.angle) - - def set_center(self, xy): - """ - Set the center of the annulus. - - Parameters - ---------- - xy : (float, float) - """ - self._center = xy - self._path = None - self.stale = True - - def get_center(self): - """Return the center of the annulus.""" - return self._center - - center = property(get_center, set_center) - - def set_width(self, width): - """ - Set the width (thickness) of the annulus ring. - - The width is measured inwards from the outer ellipse. - - Parameters - ---------- - width : float - """ - if min(self.a, self.b) <= width: - raise ValueError( - 'Width of annulus must be less than or equal semi-minor axis') - - self._width = width - self._path = None - self.stale = True - - def get_width(self): - """Return the width (thickness) of the annulus ring.""" - return self._width - - width = property(get_width, set_width) - - def set_angle(self, angle): - """ - Set the tilt angle of the annulus. - - Parameters - ---------- - angle : float - """ - self._angle = angle - self._path = None - self.stale = True - - def get_angle(self): - """Return the angle of the annulus.""" - return self._angle - - angle = property(get_angle, set_angle) - - def set_semimajor(self, a): - """ - Set the semi-major axis *a* of the annulus. - - Parameters - ---------- - a : float - """ - self.a = float(a) - self._path = None - self.stale = True - - def set_semiminor(self, b): - """ - Set the semi-minor axis *b* of the annulus. - - Parameters - ---------- - b : float - """ - self.b = float(b) - self._path = None - self.stale = True - - def set_radii(self, r): - """ - Set the semi-major (*a*) and semi-minor radii (*b*) of the annulus. - - Parameters - ---------- - r : float or (float, float) - The radius, or semi-axes: - - - If float: radius of the outer circle. - - If two floats: semi-major and -minor axes of outer ellipse. - """ - if np.shape(r) == (2,): - self.a, self.b = r - elif np.shape(r) == (): - self.a = self.b = float(r) - else: - raise ValueError("Parameter 'r' must be one or two floats.") - - self._path = None - self.stale = True - - def get_radii(self): - """Return the semi-major and semi-minor radii of the annulus.""" - return self.a, self.b - - radii = property(get_radii, set_radii) - - def _transform_verts(self, verts, a, b): - return transforms.Affine2D() \ - .scale(*self._convert_xy_units((a, b))) \ - .rotate_deg(self.angle) \ - .translate(*self._convert_xy_units(self.center)) \ - .transform(verts) - - def _recompute_path(self): - # circular arc - arc = Path.arc(0, 360) - - # annulus needs to draw an outer ring - # followed by a reversed and scaled inner ring - a, b, w = self.a, self.b, self.width - v1 = self._transform_verts(arc.vertices, a, b) - v2 = self._transform_verts(arc.vertices[::-1], a - w, b - w) - v = np.vstack([v1, v2, v1[0, :], (0, 0)]) - c = np.hstack([arc.codes, Path.MOVETO, - arc.codes[1:], Path.MOVETO, - Path.CLOSEPOLY]) - self._path = Path(v, c) - - def get_path(self): - if self._path is None: - self._recompute_path() - return self._path - - -class Circle(Ellipse): - """ - A circle patch. - """ - def __str__(self): - pars = self.center[0], self.center[1], self.radius - fmt = "Circle(xy=(%g, %g), radius=%g)" - return fmt % pars - - @_docstring.dedent_interpd - def __init__(self, xy, radius=5, **kwargs): - """ - Create a true circle at center *xy* = (*x*, *y*) with given *radius*. - - Unlike `CirclePolygon` which is a polygonal approximation, this uses - Bezier splines and is much closer to a scale-free circle. - - Valid keyword arguments are: - - %(Patch:kwdoc)s - """ - super().__init__(xy, radius * 2, radius * 2, **kwargs) - self.radius = radius - - def set_radius(self, radius): - """ - Set the radius of the circle. - - Parameters - ---------- - radius : float - """ - self.width = self.height = 2 * radius - self.stale = True - - def get_radius(self): - """Return the radius of the circle.""" - return self.width / 2. - - radius = property(get_radius, set_radius) - - -class Arc(Ellipse): - """ - An elliptical arc, i.e. a segment of an ellipse. - - Due to internal optimizations, the arc cannot be filled. - """ - - def __str__(self): - pars = (self.center[0], self.center[1], self.width, - self.height, self.angle, self.theta1, self.theta2) - fmt = ("Arc(xy=(%g, %g), width=%g, " - "height=%g, angle=%g, theta1=%g, theta2=%g)") - return fmt % pars - - @_docstring.dedent_interpd - @_api.make_keyword_only("3.6", name="angle") - def __init__(self, xy, width, height, angle=0.0, - theta1=0.0, theta2=360.0, **kwargs): - """ - Parameters - ---------- - xy : (float, float) - The center of the ellipse. - - width : float - The length of the horizontal axis. - - height : float - The length of the vertical axis. - - angle : float - Rotation of the ellipse in degrees (counterclockwise). - - theta1, theta2 : float, default: 0, 360 - Starting and ending angles of the arc in degrees. These values - are relative to *angle*, e.g. if *angle* = 45 and *theta1* = 90 - the absolute starting angle is 135. - Default *theta1* = 0, *theta2* = 360, i.e. a complete ellipse. - The arc is drawn in the counterclockwise direction. - Angles greater than or equal to 360, or smaller than 0, are - represented by an equivalent angle in the range [0, 360), by - taking the input value mod 360. - - Other Parameters - ---------------- - **kwargs : `~matplotlib.patches.Patch` properties - Most `.Patch` properties are supported as keyword arguments, - except *fill* and *facecolor* because filling is not supported. - - %(Patch:kwdoc)s - """ - fill = kwargs.setdefault('fill', False) - if fill: - raise ValueError("Arc objects can not be filled") - - super().__init__(xy, width, height, angle=angle, **kwargs) - - self.theta1 = theta1 - self.theta2 = theta2 - (self._theta1, self._theta2, self._stretched_width, - self._stretched_height) = self._theta_stretch() - self._path = Path.arc(self._theta1, self._theta2) - - @artist.allow_rasterization - def draw(self, renderer): - """ - Draw the arc to the given *renderer*. - - Notes - ----- - Ellipses are normally drawn using an approximation that uses - eight cubic Bezier splines. The error of this approximation - is 1.89818e-6, according to this unverified source: - - Lancaster, Don. *Approximating a Circle or an Ellipse Using - Four Bezier Cubic Splines.* - - https://www.tinaja.com/glib/ellipse4.pdf - - There is a use case where very large ellipses must be drawn - with very high accuracy, and it is too expensive to render the - entire ellipse with enough segments (either splines or line - segments). Therefore, in the case where either radius of the - ellipse is large enough that the error of the spline - approximation will be visible (greater than one pixel offset - from the ideal), a different technique is used. - - In that case, only the visible parts of the ellipse are drawn, - with each visible arc using a fixed number of spline segments - (8). The algorithm proceeds as follows: - - 1. The points where the ellipse intersects the axes (or figure) - bounding box are located. (This is done by performing an inverse - transformation on the bbox such that it is relative to the unit - circle -- this makes the intersection calculation much easier than - doing rotated ellipse intersection directly.) - - This uses the "line intersecting a circle" algorithm from: - - Vince, John. *Geometry for Computer Graphics: Formulae, - Examples & Proofs.* London: Springer-Verlag, 2005. - - 2. The angles of each of the intersection points are calculated. - - 3. Proceeding counterclockwise starting in the positive - x-direction, each of the visible arc-segments between the - pairs of vertices are drawn using the Bezier arc - approximation technique implemented in `.Path.arc`. - """ - if not self.get_visible(): - return - - self._recompute_transform() - - self._update_path() - # Get width and height in pixels we need to use - # `self.get_data_transform` rather than `self.get_transform` - # because we want the transform from dataspace to the - # screen space to estimate how big the arc will be in physical - # units when rendered (the transform that we get via - # `self.get_transform()` goes from an idealized unit-radius - # space to screen space). - data_to_screen_trans = self.get_data_transform() - pwidth, pheight = ( - data_to_screen_trans.transform((self._stretched_width, - self._stretched_height)) - - data_to_screen_trans.transform((0, 0))) - inv_error = (1.0 / 1.89818e-6) * 0.5 - - if pwidth < inv_error and pheight < inv_error: - return Patch.draw(self, renderer) - - def line_circle_intersect(x0, y0, x1, y1): - dx = x1 - x0 - dy = y1 - y0 - dr2 = dx * dx + dy * dy - D = x0 * y1 - x1 * y0 - D2 = D * D - discrim = dr2 - D2 - if discrim >= 0.0: - sign_dy = np.copysign(1, dy) # +/-1, never 0. - sqrt_discrim = np.sqrt(discrim) - return np.array( - [[(D * dy + sign_dy * dx * sqrt_discrim) / dr2, - (-D * dx + abs(dy) * sqrt_discrim) / dr2], - [(D * dy - sign_dy * dx * sqrt_discrim) / dr2, - (-D * dx - abs(dy) * sqrt_discrim) / dr2]]) - else: - return np.empty((0, 2)) - - def segment_circle_intersect(x0, y0, x1, y1): - epsilon = 1e-9 - if x1 < x0: - x0e, x1e = x1, x0 - else: - x0e, x1e = x0, x1 - if y1 < y0: - y0e, y1e = y1, y0 - else: - y0e, y1e = y0, y1 - xys = line_circle_intersect(x0, y0, x1, y1) - xs, ys = xys.T - return xys[ - (x0e - epsilon < xs) & (xs < x1e + epsilon) - & (y0e - epsilon < ys) & (ys < y1e + epsilon) - ] - - # Transform the axes (or figure) box_path so that it is relative to - # the unit circle in the same way that it is relative to the desired - # ellipse. - box_path_transform = ( - transforms.BboxTransformTo((self.axes or self.figure).bbox) - - self.get_transform()) - box_path = Path.unit_rectangle().transformed(box_path_transform) - - thetas = set() - # For each of the point pairs, there is a line segment - for p0, p1 in zip(box_path.vertices[:-1], box_path.vertices[1:]): - xy = segment_circle_intersect(*p0, *p1) - x, y = xy.T - # arctan2 return [-pi, pi), the rest of our angles are in - # [0, 360], adjust as needed. - theta = (np.rad2deg(np.arctan2(y, x)) + 360) % 360 - thetas.update( - theta[(self._theta1 < theta) & (theta < self._theta2)]) - thetas = sorted(thetas) + [self._theta2] - last_theta = self._theta1 - theta1_rad = np.deg2rad(self._theta1) - inside = box_path.contains_point( - (np.cos(theta1_rad), np.sin(theta1_rad)) - ) - - # save original path - path_original = self._path - for theta in thetas: - if inside: - self._path = Path.arc(last_theta, theta, 8) - Patch.draw(self, renderer) - inside = False - else: - inside = True - last_theta = theta - - # restore original path - self._path = path_original - - def _update_path(self): - # Compute new values and update and set new _path if any value changed - stretched = self._theta_stretch() - if any(a != b for a, b in zip( - stretched, (self._theta1, self._theta2, self._stretched_width, - self._stretched_height))): - (self._theta1, self._theta2, self._stretched_width, - self._stretched_height) = stretched - self._path = Path.arc(self._theta1, self._theta2) - - def _theta_stretch(self): - # If the width and height of ellipse are not equal, take into account - # stretching when calculating angles to draw between - def theta_stretch(theta, scale): - theta = np.deg2rad(theta) - x = np.cos(theta) - y = np.sin(theta) - stheta = np.rad2deg(np.arctan2(scale * y, x)) - # arctan2 has the range [-pi, pi], we expect [0, 2*pi] - return (stheta + 360) % 360 - - width = self.convert_xunits(self.width) - height = self.convert_yunits(self.height) - if ( - # if we need to stretch the angles because we are distorted - width != height - # and we are not doing a full circle. - # - # 0 and 360 do not exactly round-trip through the angle - # stretching (due to both float precision limitations and - # the difference between the range of arctan2 [-pi, pi] and - # this method [0, 360]) so avoid doing it if we don't have to. - and not (self.theta1 != self.theta2 and - self.theta1 % 360 == self.theta2 % 360) - ): - theta1 = theta_stretch(self.theta1, width / height) - theta2 = theta_stretch(self.theta2, width / height) - return theta1, theta2, width, height - return self.theta1, self.theta2, width, height - - -def bbox_artist(artist, renderer, props=None, fill=True): - """ - A debug function to draw a rectangle around the bounding - box returned by an artist's `.Artist.get_window_extent` - to test whether the artist is returning the correct bbox. - - *props* is a dict of rectangle props with the additional property - 'pad' that sets the padding around the bbox in points. - """ - if props is None: - props = {} - props = props.copy() # don't want to alter the pad externally - pad = props.pop('pad', 4) - pad = renderer.points_to_pixels(pad) - bbox = artist.get_window_extent(renderer) - r = Rectangle( - xy=(bbox.x0 - pad / 2, bbox.y0 - pad / 2), - width=bbox.width + pad, height=bbox.height + pad, - fill=fill, transform=transforms.IdentityTransform(), clip_on=False) - r.update(props) - r.draw(renderer) - - -def draw_bbox(bbox, renderer, color='k', trans=None): - """ - A debug function to draw a rectangle around the bounding - box returned by an artist's `.Artist.get_window_extent` - to test whether the artist is returning the correct bbox. - """ - r = Rectangle(xy=bbox.p0, width=bbox.width, height=bbox.height, - edgecolor=color, fill=False, clip_on=False) - if trans is not None: - r.set_transform(trans) - r.draw(renderer) - - -class _Style: - """ - A base class for the Styles. It is meant to be a container class, - where actual styles are declared as subclass of it, and it - provides some helper functions. - """ - - def __init_subclass__(cls): - # Automatically perform docstring interpolation on the subclasses: - # This allows listing the supported styles via - # - %(BoxStyle:table)s - # - %(ConnectionStyle:table)s - # - %(ArrowStyle:table)s - # and additionally adding .. ACCEPTS: blocks via - # - %(BoxStyle:table_and_accepts)s - # - %(ConnectionStyle:table_and_accepts)s - # - %(ArrowStyle:table_and_accepts)s - _docstring.interpd.update({ - f"{cls.__name__}:table": cls.pprint_styles(), - f"{cls.__name__}:table_and_accepts": ( - cls.pprint_styles() - + "\n\n .. ACCEPTS: [" - + "|".join(map(" '{}' ".format, cls._style_list)) - + "]") - }) - - def __new__(cls, stylename, **kwargs): - """Return the instance of the subclass with the given style name.""" - # The "class" should have the _style_list attribute, which is a mapping - # of style names to style classes. - _list = stylename.replace(" ", "").split(",") - _name = _list[0].lower() - try: - _cls = cls._style_list[_name] - except KeyError as err: - raise ValueError(f"Unknown style: {stylename!r}") from err - try: - _args_pair = [cs.split("=") for cs in _list[1:]] - _args = {k: float(v) for k, v in _args_pair} - except ValueError as err: - raise ValueError( - f"Incorrect style argument: {stylename!r}") from err - return _cls(**{**_args, **kwargs}) - - @classmethod - def get_styles(cls): - """Return a dictionary of available styles.""" - return cls._style_list - - @classmethod - def pprint_styles(cls): - """Return the available styles as pretty-printed string.""" - table = [('Class', 'Name', 'Attrs'), - *[(cls.__name__, - # Add backquotes, as - and | have special meaning in reST. - f'``{name}``', - # [1:-1] drops the surrounding parentheses. - str(inspect.signature(cls))[1:-1] or 'None') - for name, cls in cls._style_list.items()]] - # Convert to rst table. - col_len = [max(len(cell) for cell in column) for column in zip(*table)] - table_formatstr = ' '.join('=' * cl for cl in col_len) - rst_table = '\n'.join([ - '', - table_formatstr, - ' '.join(cell.ljust(cl) for cell, cl in zip(table[0], col_len)), - table_formatstr, - *[' '.join(cell.ljust(cl) for cell, cl in zip(row, col_len)) - for row in table[1:]], - table_formatstr, - ]) - return textwrap.indent(rst_table, prefix=' ' * 4) - - @classmethod - def register(cls, name, style): - """Register a new style.""" - if not issubclass(style, cls._Base): - raise ValueError("%s must be a subclass of %s" % (style, - cls._Base)) - cls._style_list[name] = style - - -def _register_style(style_list, cls=None, *, name=None): - """Class decorator that stashes a class in a (style) dictionary.""" - if cls is None: - return functools.partial(_register_style, style_list, name=name) - style_list[name or cls.__name__.lower()] = cls - return cls - - -@_docstring.dedent_interpd -class BoxStyle(_Style): - """ - `BoxStyle` is a container class which defines several - boxstyle classes, which are used for `FancyBboxPatch`. - - A style object can be created as:: - - BoxStyle.Round(pad=0.2) - - or:: - - BoxStyle("Round", pad=0.2) - - or:: - - BoxStyle("Round, pad=0.2") - - The following boxstyle classes are defined. - - %(BoxStyle:table)s - - An instance of a boxstyle class is a callable object, with the signature :: - - __call__(self, x0, y0, width, height, mutation_size) -> Path - - *x0*, *y0*, *width* and *height* specify the location and size of the box - to be drawn; *mutation_size* scales the outline properties such as padding. - """ - - _style_list = {} - - @_register_style(_style_list) - class Square: - """A square box.""" - - def __init__(self, pad=0.3): - """ - Parameters - ---------- - pad : float, default: 0.3 - The amount of padding around the original box. - """ - self.pad = pad - - def __call__(self, x0, y0, width, height, mutation_size): - pad = mutation_size * self.pad - # width and height with padding added. - width, height = width + 2 * pad, height + 2 * pad - # boundary of the padded box - x0, y0 = x0 - pad, y0 - pad - x1, y1 = x0 + width, y0 + height - return Path._create_closed( - [(x0, y0), (x1, y0), (x1, y1), (x0, y1)]) - - @_register_style(_style_list) - class Circle: - """A circular box.""" - - def __init__(self, pad=0.3): - """ - Parameters - ---------- - pad : float, default: 0.3 - The amount of padding around the original box. - """ - self.pad = pad - - def __call__(self, x0, y0, width, height, mutation_size): - pad = mutation_size * self.pad - width, height = width + 2 * pad, height + 2 * pad - # boundary of the padded box - x0, y0 = x0 - pad, y0 - pad - return Path.circle((x0 + width / 2, y0 + height / 2), - max(width, height) / 2) - - @_register_style(_style_list) - class Ellipse: - """ - An elliptical box. - - .. versionadded:: 3.7 - """ - - def __init__(self, pad=0.3): - """ - Parameters - ---------- - pad : float, default: 0.3 - The amount of padding around the original box. - """ - self.pad = pad - - def __call__(self, x0, y0, width, height, mutation_size): - pad = mutation_size * self.pad - width, height = width + 2 * pad, height + 2 * pad - # boundary of the padded box - x0, y0 = x0 - pad, y0 - pad - a = width / math.sqrt(2) - b = height / math.sqrt(2) - trans = Affine2D().scale(a, b).translate(x0 + width / 2, - y0 + height / 2) - return trans.transform_path(Path.unit_circle()) - - @_register_style(_style_list) - class LArrow: - """A box in the shape of a left-pointing arrow.""" - - def __init__(self, pad=0.3): - """ - Parameters - ---------- - pad : float, default: 0.3 - The amount of padding around the original box. - """ - self.pad = pad - - def __call__(self, x0, y0, width, height, mutation_size): - # padding - pad = mutation_size * self.pad - # width and height with padding added. - width, height = width + 2 * pad, height + 2 * pad - # boundary of the padded box - x0, y0 = x0 - pad, y0 - pad, - x1, y1 = x0 + width, y0 + height - - dx = (y1 - y0) / 2 - dxx = dx / 2 - x0 = x0 + pad / 1.4 # adjust by ~sqrt(2) - - return Path._create_closed( - [(x0 + dxx, y0), (x1, y0), (x1, y1), (x0 + dxx, y1), - (x0 + dxx, y1 + dxx), (x0 - dx, y0 + dx), - (x0 + dxx, y0 - dxx), # arrow - (x0 + dxx, y0)]) - - @_register_style(_style_list) - class RArrow(LArrow): - """A box in the shape of a right-pointing arrow.""" - - def __call__(self, x0, y0, width, height, mutation_size): - p = BoxStyle.LArrow.__call__( - self, x0, y0, width, height, mutation_size) - p.vertices[:, 0] = 2 * x0 + width - p.vertices[:, 0] - return p - - @_register_style(_style_list) - class DArrow: - """A box in the shape of a two-way arrow.""" - # Modified from LArrow to add a right arrow to the bbox. - - def __init__(self, pad=0.3): - """ - Parameters - ---------- - pad : float, default: 0.3 - The amount of padding around the original box. - """ - self.pad = pad - - def __call__(self, x0, y0, width, height, mutation_size): - # padding - pad = mutation_size * self.pad - # width and height with padding added. - # The width is padded by the arrows, so we don't need to pad it. - height = height + 2 * pad - # boundary of the padded box - x0, y0 = x0 - pad, y0 - pad - x1, y1 = x0 + width, y0 + height - - dx = (y1 - y0) / 2 - dxx = dx / 2 - x0 = x0 + pad / 1.4 # adjust by ~sqrt(2) - - return Path._create_closed([ - (x0 + dxx, y0), (x1, y0), # bot-segment - (x1, y0 - dxx), (x1 + dx + dxx, y0 + dx), - (x1, y1 + dxx), # right-arrow - (x1, y1), (x0 + dxx, y1), # top-segment - (x0 + dxx, y1 + dxx), (x0 - dx, y0 + dx), - (x0 + dxx, y0 - dxx), # left-arrow - (x0 + dxx, y0)]) - - @_register_style(_style_list) - class Round: - """A box with round corners.""" - - def __init__(self, pad=0.3, rounding_size=None): - """ - Parameters - ---------- - pad : float, default: 0.3 - The amount of padding around the original box. - rounding_size : float, default: *pad* - Radius of the corners. - """ - self.pad = pad - self.rounding_size = rounding_size - - def __call__(self, x0, y0, width, height, mutation_size): - - # padding - pad = mutation_size * self.pad - - # size of the rounding corner - if self.rounding_size: - dr = mutation_size * self.rounding_size - else: - dr = pad - - width, height = width + 2 * pad, height + 2 * pad - - x0, y0 = x0 - pad, y0 - pad, - x1, y1 = x0 + width, y0 + height - - # Round corners are implemented as quadratic Bezier, e.g., - # [(x0, y0-dr), (x0, y0), (x0+dr, y0)] for lower left corner. - cp = [(x0 + dr, y0), - (x1 - dr, y0), - (x1, y0), (x1, y0 + dr), - (x1, y1 - dr), - (x1, y1), (x1 - dr, y1), - (x0 + dr, y1), - (x0, y1), (x0, y1 - dr), - (x0, y0 + dr), - (x0, y0), (x0 + dr, y0), - (x0 + dr, y0)] - - com = [Path.MOVETO, - Path.LINETO, - Path.CURVE3, Path.CURVE3, - Path.LINETO, - Path.CURVE3, Path.CURVE3, - Path.LINETO, - Path.CURVE3, Path.CURVE3, - Path.LINETO, - Path.CURVE3, Path.CURVE3, - Path.CLOSEPOLY] - - return Path(cp, com) - - @_register_style(_style_list) - class Round4: - """A box with rounded edges.""" - - def __init__(self, pad=0.3, rounding_size=None): - """ - Parameters - ---------- - pad : float, default: 0.3 - The amount of padding around the original box. - rounding_size : float, default: *pad*/2 - Rounding of edges. - """ - self.pad = pad - self.rounding_size = rounding_size - - def __call__(self, x0, y0, width, height, mutation_size): - - # padding - pad = mutation_size * self.pad - - # Rounding size; defaults to half of the padding. - if self.rounding_size: - dr = mutation_size * self.rounding_size - else: - dr = pad / 2. - - width = width + 2 * pad - 2 * dr - height = height + 2 * pad - 2 * dr - - x0, y0 = x0 - pad + dr, y0 - pad + dr, - x1, y1 = x0 + width, y0 + height - - cp = [(x0, y0), - (x0 + dr, y0 - dr), (x1 - dr, y0 - dr), (x1, y0), - (x1 + dr, y0 + dr), (x1 + dr, y1 - dr), (x1, y1), - (x1 - dr, y1 + dr), (x0 + dr, y1 + dr), (x0, y1), - (x0 - dr, y1 - dr), (x0 - dr, y0 + dr), (x0, y0), - (x0, y0)] - - com = [Path.MOVETO, - Path.CURVE4, Path.CURVE4, Path.CURVE4, - Path.CURVE4, Path.CURVE4, Path.CURVE4, - Path.CURVE4, Path.CURVE4, Path.CURVE4, - Path.CURVE4, Path.CURVE4, Path.CURVE4, - Path.CLOSEPOLY] - - return Path(cp, com) - - @_register_style(_style_list) - class Sawtooth: - """A box with a sawtooth outline.""" - - def __init__(self, pad=0.3, tooth_size=None): - """ - Parameters - ---------- - pad : float, default: 0.3 - The amount of padding around the original box. - tooth_size : float, default: *pad*/2 - Size of the sawtooth. - """ - self.pad = pad - self.tooth_size = tooth_size - - def _get_sawtooth_vertices(self, x0, y0, width, height, mutation_size): - - # padding - pad = mutation_size * self.pad - - # size of sawtooth - if self.tooth_size is None: - tooth_size = self.pad * .5 * mutation_size - else: - tooth_size = self.tooth_size * mutation_size - - hsz = tooth_size / 2 - width = width + 2 * pad - tooth_size - height = height + 2 * pad - tooth_size - - # the sizes of the vertical and horizontal sawtooth are - # separately adjusted to fit the given box size. - dsx_n = round((width - tooth_size) / (tooth_size * 2)) * 2 - dsx = (width - tooth_size) / dsx_n - dsy_n = round((height - tooth_size) / (tooth_size * 2)) * 2 - dsy = (height - tooth_size) / dsy_n - - x0, y0 = x0 - pad + hsz, y0 - pad + hsz - x1, y1 = x0 + width, y0 + height - - xs = [ - x0, *np.linspace(x0 + hsz, x1 - hsz, 2 * dsx_n + 1), # bottom - *([x1, x1 + hsz, x1, x1 - hsz] * dsy_n)[:2*dsy_n+2], # right - x1, *np.linspace(x1 - hsz, x0 + hsz, 2 * dsx_n + 1), # top - *([x0, x0 - hsz, x0, x0 + hsz] * dsy_n)[:2*dsy_n+2], # left - ] - ys = [ - *([y0, y0 - hsz, y0, y0 + hsz] * dsx_n)[:2*dsx_n+2], # bottom - y0, *np.linspace(y0 + hsz, y1 - hsz, 2 * dsy_n + 1), # right - *([y1, y1 + hsz, y1, y1 - hsz] * dsx_n)[:2*dsx_n+2], # top - y1, *np.linspace(y1 - hsz, y0 + hsz, 2 * dsy_n + 1), # left - ] - - return [*zip(xs, ys), (xs[0], ys[0])] - - def __call__(self, x0, y0, width, height, mutation_size): - saw_vertices = self._get_sawtooth_vertices(x0, y0, width, - height, mutation_size) - return Path(saw_vertices, closed=True) - - @_register_style(_style_list) - class Roundtooth(Sawtooth): - """A box with a rounded sawtooth outline.""" - - def __call__(self, x0, y0, width, height, mutation_size): - saw_vertices = self._get_sawtooth_vertices(x0, y0, - width, height, - mutation_size) - # Add a trailing vertex to allow us to close the polygon correctly - saw_vertices = np.concatenate([saw_vertices, [saw_vertices[0]]]) - codes = ([Path.MOVETO] + - [Path.CURVE3, Path.CURVE3] * ((len(saw_vertices)-1)//2) + - [Path.CLOSEPOLY]) - return Path(saw_vertices, codes) - - -@_docstring.dedent_interpd -class ConnectionStyle(_Style): - """ - `ConnectionStyle` is a container class which defines - several connectionstyle classes, which is used to create a path - between two points. These are mainly used with `FancyArrowPatch`. - - A connectionstyle object can be either created as:: - - ConnectionStyle.Arc3(rad=0.2) - - or:: - - ConnectionStyle("Arc3", rad=0.2) - - or:: - - ConnectionStyle("Arc3, rad=0.2") - - The following classes are defined - - %(ConnectionStyle:table)s - - An instance of any connection style class is a callable object, - whose call signature is:: - - __call__(self, posA, posB, - patchA=None, patchB=None, - shrinkA=2., shrinkB=2.) - - and it returns a `.Path` instance. *posA* and *posB* are - tuples of (x, y) coordinates of the two points to be - connected. *patchA* (or *patchB*) is given, the returned path is - clipped so that it start (or end) from the boundary of the - patch. The path is further shrunk by *shrinkA* (or *shrinkB*) - which is given in points. - """ - - _style_list = {} - - class _Base: - """ - A base class for connectionstyle classes. The subclass needs - to implement a *connect* method whose call signature is:: - - connect(posA, posB) - - where posA and posB are tuples of x, y coordinates to be - connected. The method needs to return a path connecting two - points. This base class defines a __call__ method, and a few - helper methods. - """ - - @_api.deprecated("3.7") - class SimpleEvent: - def __init__(self, xy): - self.x, self.y = xy - - def _in_patch(self, patch): - """ - Return a predicate function testing whether a point *xy* is - contained in *patch*. - """ - return lambda xy: patch.contains( - SimpleNamespace(x=xy[0], y=xy[1]))[0] - - def _clip(self, path, in_start, in_stop): - """ - Clip *path* at its start by the region where *in_start* returns - True, and at its stop by the region where *in_stop* returns True. - - The original path is assumed to start in the *in_start* region and - to stop in the *in_stop* region. - """ - if in_start: - try: - _, path = split_path_inout(path, in_start) - except ValueError: - pass - if in_stop: - try: - path, _ = split_path_inout(path, in_stop) - except ValueError: - pass - return path - - def __call__(self, posA, posB, - shrinkA=2., shrinkB=2., patchA=None, patchB=None): - """ - Call the *connect* method to create a path between *posA* and - *posB*; then clip and shrink the path. - """ - path = self.connect(posA, posB) - path = self._clip( - path, - self._in_patch(patchA) if patchA else None, - self._in_patch(patchB) if patchB else None, - ) - path = self._clip( - path, - inside_circle(*path.vertices[0], shrinkA) if shrinkA else None, - inside_circle(*path.vertices[-1], shrinkB) if shrinkB else None - ) - return path - - @_register_style(_style_list) - class Arc3(_Base): - """ - Creates a simple quadratic Bézier curve between two - points. The curve is created so that the middle control point - (C1) is located at the same distance from the start (C0) and - end points(C2) and the distance of the C1 to the line - connecting C0-C2 is *rad* times the distance of C0-C2. - """ - - def __init__(self, rad=0.): - """ - Parameters - ---------- - rad : float - Curvature of the curve. - """ - self.rad = rad - - def connect(self, posA, posB): - x1, y1 = posA - x2, y2 = posB - x12, y12 = (x1 + x2) / 2., (y1 + y2) / 2. - dx, dy = x2 - x1, y2 - y1 - - f = self.rad - - cx, cy = x12 + f * dy, y12 - f * dx - - vertices = [(x1, y1), - (cx, cy), - (x2, y2)] - codes = [Path.MOVETO, - Path.CURVE3, - Path.CURVE3] - - return Path(vertices, codes) - - @_register_style(_style_list) - class Angle3(_Base): - """ - Creates a simple quadratic Bézier curve between two points. The middle - control point is placed at the intersecting point of two lines which - cross the start and end point, and have a slope of *angleA* and - *angleB*, respectively. - """ - - def __init__(self, angleA=90, angleB=0): - """ - Parameters - ---------- - angleA : float - Starting angle of the path. - - angleB : float - Ending angle of the path. - """ - - self.angleA = angleA - self.angleB = angleB - - def connect(self, posA, posB): - x1, y1 = posA - x2, y2 = posB - - cosA = math.cos(math.radians(self.angleA)) - sinA = math.sin(math.radians(self.angleA)) - cosB = math.cos(math.radians(self.angleB)) - sinB = math.sin(math.radians(self.angleB)) - - cx, cy = get_intersection(x1, y1, cosA, sinA, - x2, y2, cosB, sinB) - - vertices = [(x1, y1), (cx, cy), (x2, y2)] - codes = [Path.MOVETO, Path.CURVE3, Path.CURVE3] - - return Path(vertices, codes) - - @_register_style(_style_list) - class Angle(_Base): - """ - Creates a piecewise continuous quadratic Bézier path between two - points. The path has a one passing-through point placed at the - intersecting point of two lines which cross the start and end point, - and have a slope of *angleA* and *angleB*, respectively. - The connecting edges are rounded with *rad*. - """ - - def __init__(self, angleA=90, angleB=0, rad=0.): - """ - Parameters - ---------- - angleA : float - Starting angle of the path. - - angleB : float - Ending angle of the path. - - rad : float - Rounding radius of the edge. - """ - - self.angleA = angleA - self.angleB = angleB - - self.rad = rad - - def connect(self, posA, posB): - x1, y1 = posA - x2, y2 = posB - - cosA = math.cos(math.radians(self.angleA)) - sinA = math.sin(math.radians(self.angleA)) - cosB = math.cos(math.radians(self.angleB)) - sinB = math.sin(math.radians(self.angleB)) - - cx, cy = get_intersection(x1, y1, cosA, sinA, - x2, y2, cosB, sinB) - - vertices = [(x1, y1)] - codes = [Path.MOVETO] - - if self.rad == 0.: - vertices.append((cx, cy)) - codes.append(Path.LINETO) - else: - dx1, dy1 = x1 - cx, y1 - cy - d1 = np.hypot(dx1, dy1) - f1 = self.rad / d1 - dx2, dy2 = x2 - cx, y2 - cy - d2 = np.hypot(dx2, dy2) - f2 = self.rad / d2 - vertices.extend([(cx + dx1 * f1, cy + dy1 * f1), - (cx, cy), - (cx + dx2 * f2, cy + dy2 * f2)]) - codes.extend([Path.LINETO, Path.CURVE3, Path.CURVE3]) - - vertices.append((x2, y2)) - codes.append(Path.LINETO) - - return Path(vertices, codes) - - @_register_style(_style_list) - class Arc(_Base): - """ - Creates a piecewise continuous quadratic Bézier path between two - points. The path can have two passing-through points, a - point placed at the distance of *armA* and angle of *angleA* from - point A, another point with respect to point B. The edges are - rounded with *rad*. - """ - - def __init__(self, angleA=0, angleB=0, armA=None, armB=None, rad=0.): - """ - Parameters - ---------- - angleA : float - Starting angle of the path. - - angleB : float - Ending angle of the path. - - armA : float or None - Length of the starting arm. - - armB : float or None - Length of the ending arm. - - rad : float - Rounding radius of the edges. - """ - - self.angleA = angleA - self.angleB = angleB - self.armA = armA - self.armB = armB - - self.rad = rad - - def connect(self, posA, posB): - x1, y1 = posA - x2, y2 = posB - - vertices = [(x1, y1)] - rounded = [] - codes = [Path.MOVETO] - - if self.armA: - cosA = math.cos(math.radians(self.angleA)) - sinA = math.sin(math.radians(self.angleA)) - # x_armA, y_armB - d = self.armA - self.rad - rounded.append((x1 + d * cosA, y1 + d * sinA)) - d = self.armA - rounded.append((x1 + d * cosA, y1 + d * sinA)) - - if self.armB: - cosB = math.cos(math.radians(self.angleB)) - sinB = math.sin(math.radians(self.angleB)) - x_armB, y_armB = x2 + self.armB * cosB, y2 + self.armB * sinB - - if rounded: - xp, yp = rounded[-1] - dx, dy = x_armB - xp, y_armB - yp - dd = (dx * dx + dy * dy) ** .5 - - rounded.append((xp + self.rad * dx / dd, - yp + self.rad * dy / dd)) - vertices.extend(rounded) - codes.extend([Path.LINETO, - Path.CURVE3, - Path.CURVE3]) - else: - xp, yp = vertices[-1] - dx, dy = x_armB - xp, y_armB - yp - dd = (dx * dx + dy * dy) ** .5 - - d = dd - self.rad - rounded = [(xp + d * dx / dd, yp + d * dy / dd), - (x_armB, y_armB)] - - if rounded: - xp, yp = rounded[-1] - dx, dy = x2 - xp, y2 - yp - dd = (dx * dx + dy * dy) ** .5 - - rounded.append((xp + self.rad * dx / dd, - yp + self.rad * dy / dd)) - vertices.extend(rounded) - codes.extend([Path.LINETO, - Path.CURVE3, - Path.CURVE3]) - - vertices.append((x2, y2)) - codes.append(Path.LINETO) - - return Path(vertices, codes) - - @_register_style(_style_list) - class Bar(_Base): - """ - A line with *angle* between A and B with *armA* and *armB*. One of the - arms is extended so that they are connected in a right angle. The - length of *armA* is determined by (*armA* + *fraction* x AB distance). - Same for *armB*. - """ - - def __init__(self, armA=0., armB=0., fraction=0.3, angle=None): - """ - Parameters - ---------- - armA : float - Minimum length of armA. - - armB : float - Minimum length of armB. - - fraction : float - A fraction of the distance between two points that will be - added to armA and armB. - - angle : float or None - Angle of the connecting line (if None, parallel to A and B). - """ - self.armA = armA - self.armB = armB - self.fraction = fraction - self.angle = angle - - def connect(self, posA, posB): - x1, y1 = posA - x20, y20 = x2, y2 = posB - - theta1 = math.atan2(y2 - y1, x2 - x1) - dx, dy = x2 - x1, y2 - y1 - dd = (dx * dx + dy * dy) ** .5 - ddx, ddy = dx / dd, dy / dd - - armA, armB = self.armA, self.armB - - if self.angle is not None: - theta0 = np.deg2rad(self.angle) - dtheta = theta1 - theta0 - dl = dd * math.sin(dtheta) - dL = dd * math.cos(dtheta) - x2, y2 = x1 + dL * math.cos(theta0), y1 + dL * math.sin(theta0) - armB = armB - dl - - # update - dx, dy = x2 - x1, y2 - y1 - dd2 = (dx * dx + dy * dy) ** .5 - ddx, ddy = dx / dd2, dy / dd2 - - arm = max(armA, armB) - f = self.fraction * dd + arm - - cx1, cy1 = x1 + f * ddy, y1 - f * ddx - cx2, cy2 = x2 + f * ddy, y2 - f * ddx - - vertices = [(x1, y1), - (cx1, cy1), - (cx2, cy2), - (x20, y20)] - codes = [Path.MOVETO, - Path.LINETO, - Path.LINETO, - Path.LINETO] - - return Path(vertices, codes) - - -def _point_along_a_line(x0, y0, x1, y1, d): - """ - Return the point on the line connecting (*x0*, *y0*) -- (*x1*, *y1*) whose - distance from (*x0*, *y0*) is *d*. - """ - dx, dy = x0 - x1, y0 - y1 - ff = d / (dx * dx + dy * dy) ** .5 - x2, y2 = x0 - ff * dx, y0 - ff * dy - - return x2, y2 - - -@_docstring.dedent_interpd -class ArrowStyle(_Style): - """ - `ArrowStyle` is a container class which defines several - arrowstyle classes, which is used to create an arrow path along a - given path. These are mainly used with `FancyArrowPatch`. - - An arrowstyle object can be either created as:: - - ArrowStyle.Fancy(head_length=.4, head_width=.4, tail_width=.4) - - or:: - - ArrowStyle("Fancy", head_length=.4, head_width=.4, tail_width=.4) - - or:: - - ArrowStyle("Fancy, head_length=.4, head_width=.4, tail_width=.4") - - The following classes are defined - - %(ArrowStyle:table)s - - For an overview of the visual appearance, see - :doc:`/gallery/text_labels_and_annotations/fancyarrow_demo`. - - An instance of any arrow style class is a callable object, - whose call signature is:: - - __call__(self, path, mutation_size, linewidth, aspect_ratio=1.) - - and it returns a tuple of a `.Path` instance and a boolean - value. *path* is a `.Path` instance along which the arrow - will be drawn. *mutation_size* and *aspect_ratio* have the same - meaning as in `BoxStyle`. *linewidth* is a line width to be - stroked. This is meant to be used to correct the location of the - head so that it does not overshoot the destination point, but not all - classes support it. - - Notes - ----- - *angleA* and *angleB* specify the orientation of the bracket, as either a - clockwise or counterclockwise angle depending on the arrow type. 0 degrees - means perpendicular to the line connecting the arrow's head and tail. - - .. plot:: gallery/text_labels_and_annotations/angles_on_bracket_arrows.py - """ - - _style_list = {} - - class _Base: - """ - Arrow Transmuter Base class - - ArrowTransmuterBase and its derivatives are used to make a fancy - arrow around a given path. The __call__ method returns a path - (which will be used to create a PathPatch instance) and a boolean - value indicating the path is open therefore is not fillable. This - class is not an artist and actual drawing of the fancy arrow is - done by the FancyArrowPatch class. - """ - - # The derived classes are required to be able to be initialized - # w/o arguments, i.e., all its argument (except self) must have - # the default values. - - @staticmethod - def ensure_quadratic_bezier(path): - """ - Some ArrowStyle classes only works with a simple quadratic - Bézier curve (created with `.ConnectionStyle.Arc3` or - `.ConnectionStyle.Angle3`). This static method checks if the - provided path is a simple quadratic Bézier curve and returns its - control points if true. - """ - segments = list(path.iter_segments()) - if (len(segments) != 2 or segments[0][1] != Path.MOVETO or - segments[1][1] != Path.CURVE3): - raise ValueError( - "'path' is not a valid quadratic Bezier curve") - return [*segments[0][0], *segments[1][0]] - - def transmute(self, path, mutation_size, linewidth): - """ - The transmute method is the very core of the ArrowStyle class and - must be overridden in the subclasses. It receives the *path* - object along which the arrow will be drawn, and the - *mutation_size*, with which the arrow head etc. will be scaled. - The *linewidth* may be used to adjust the path so that it does not - pass beyond the given points. It returns a tuple of a `.Path` - instance and a boolean. The boolean value indicate whether the - path can be filled or not. The return value can also be a list of - paths and list of booleans of the same length. - """ - raise NotImplementedError('Derived must override') - - def __call__(self, path, mutation_size, linewidth, - aspect_ratio=1.): - """ - The __call__ method is a thin wrapper around the transmute method - and takes care of the aspect ratio. - """ - - if aspect_ratio is not None: - # Squeeze the given height by the aspect_ratio - vertices = path.vertices / [1, aspect_ratio] - path_shrunk = Path(vertices, path.codes) - # call transmute method with squeezed height. - path_mutated, fillable = self.transmute(path_shrunk, - mutation_size, - linewidth) - if np.iterable(fillable): - # Restore the height - path_list = [Path(p.vertices * [1, aspect_ratio], p.codes) - for p in path_mutated] - return path_list, fillable - else: - return path_mutated, fillable - else: - return self.transmute(path, mutation_size, linewidth) - - class _Curve(_Base): - """ - A simple arrow which will work with any path instance. The - returned path is the concatenation of the original path, and at - most two paths representing the arrow head or bracket at the start - point and at the end point. The arrow heads can be either open - or closed. - """ - - arrow = "-" - fillbegin = fillend = False # Whether arrows are filled. - - def __init__(self, head_length=.4, head_width=.2, widthA=1., widthB=1., - lengthA=0.2, lengthB=0.2, angleA=0, angleB=0, scaleA=None, - scaleB=None): - """ - Parameters - ---------- - head_length : float, default: 0.4 - Length of the arrow head, relative to *mutation_size*. - head_width : float, default: 0.2 - Width of the arrow head, relative to *mutation_size*. - widthA, widthB : float, default: 1.0 - Width of the bracket. - lengthA, lengthB : float, default: 0.2 - Length of the bracket. - angleA, angleB : float, default: 0 - Orientation of the bracket, as a counterclockwise angle. - 0 degrees means perpendicular to the line. - scaleA, scaleB : float, default: *mutation_size* - The scale of the brackets. - """ - - self.head_length, self.head_width = head_length, head_width - self.widthA, self.widthB = widthA, widthB - self.lengthA, self.lengthB = lengthA, lengthB - self.angleA, self.angleB = angleA, angleB - self.scaleA, self.scaleB = scaleA, scaleB - - self._beginarrow_head = False - self._beginarrow_bracket = False - self._endarrow_head = False - self._endarrow_bracket = False - - if "-" not in self.arrow: - raise ValueError("arrow must have the '-' between " - "the two heads") - - beginarrow, endarrow = self.arrow.split("-", 1) - - if beginarrow == "<": - self._beginarrow_head = True - self._beginarrow_bracket = False - elif beginarrow == "<|": - self._beginarrow_head = True - self._beginarrow_bracket = False - self.fillbegin = True - elif beginarrow in ("]", "|"): - self._beginarrow_head = False - self._beginarrow_bracket = True - - if endarrow == ">": - self._endarrow_head = True - self._endarrow_bracket = False - elif endarrow == "|>": - self._endarrow_head = True - self._endarrow_bracket = False - self.fillend = True - elif endarrow in ("[", "|"): - self._endarrow_head = False - self._endarrow_bracket = True - - super().__init__() - - def _get_arrow_wedge(self, x0, y0, x1, y1, - head_dist, cos_t, sin_t, linewidth): - """ - Return the paths for arrow heads. Since arrow lines are - drawn with capstyle=projected, The arrow goes beyond the - desired point. This method also returns the amount of the path - to be shrunken so that it does not overshoot. - """ - - # arrow from x0, y0 to x1, y1 - dx, dy = x0 - x1, y0 - y1 - - cp_distance = np.hypot(dx, dy) - - # pad_projected : amount of pad to account the - # overshooting of the projection of the wedge - pad_projected = (.5 * linewidth / sin_t) - - # Account for division by zero - if cp_distance == 0: - cp_distance = 1 - - # apply pad for projected edge - ddx = pad_projected * dx / cp_distance - ddy = pad_projected * dy / cp_distance - - # offset for arrow wedge - dx = dx / cp_distance * head_dist - dy = dy / cp_distance * head_dist - - dx1, dy1 = cos_t * dx + sin_t * dy, -sin_t * dx + cos_t * dy - dx2, dy2 = cos_t * dx - sin_t * dy, sin_t * dx + cos_t * dy - - vertices_arrow = [(x1 + ddx + dx1, y1 + ddy + dy1), - (x1 + ddx, y1 + ddy), - (x1 + ddx + dx2, y1 + ddy + dy2)] - codes_arrow = [Path.MOVETO, - Path.LINETO, - Path.LINETO] - - return vertices_arrow, codes_arrow, ddx, ddy - - def _get_bracket(self, x0, y0, - x1, y1, width, length, angle): - - cos_t, sin_t = get_cos_sin(x1, y1, x0, y0) - - # arrow from x0, y0 to x1, y1 - from matplotlib.bezier import get_normal_points - x1, y1, x2, y2 = get_normal_points(x0, y0, cos_t, sin_t, width) - - dx, dy = length * cos_t, length * sin_t - - vertices_arrow = [(x1 + dx, y1 + dy), - (x1, y1), - (x2, y2), - (x2 + dx, y2 + dy)] - codes_arrow = [Path.MOVETO, - Path.LINETO, - Path.LINETO, - Path.LINETO] - - if angle: - trans = transforms.Affine2D().rotate_deg_around(x0, y0, angle) - vertices_arrow = trans.transform(vertices_arrow) - - return vertices_arrow, codes_arrow - - def transmute(self, path, mutation_size, linewidth): - # docstring inherited - if self._beginarrow_head or self._endarrow_head: - head_length = self.head_length * mutation_size - head_width = self.head_width * mutation_size - head_dist = np.hypot(head_length, head_width) - cos_t, sin_t = head_length / head_dist, head_width / head_dist - - scaleA = mutation_size if self.scaleA is None else self.scaleA - scaleB = mutation_size if self.scaleB is None else self.scaleB - - # begin arrow - x0, y0 = path.vertices[0] - x1, y1 = path.vertices[1] - - # If there is no room for an arrow and a line, then skip the arrow - has_begin_arrow = self._beginarrow_head and (x0, y0) != (x1, y1) - verticesA, codesA, ddxA, ddyA = ( - self._get_arrow_wedge(x1, y1, x0, y0, - head_dist, cos_t, sin_t, linewidth) - if has_begin_arrow - else ([], [], 0, 0) - ) - - # end arrow - x2, y2 = path.vertices[-2] - x3, y3 = path.vertices[-1] - - # If there is no room for an arrow and a line, then skip the arrow - has_end_arrow = self._endarrow_head and (x2, y2) != (x3, y3) - verticesB, codesB, ddxB, ddyB = ( - self._get_arrow_wedge(x2, y2, x3, y3, - head_dist, cos_t, sin_t, linewidth) - if has_end_arrow - else ([], [], 0, 0) - ) - - # This simple code will not work if ddx, ddy is greater than the - # separation between vertices. - paths = [Path(np.concatenate([[(x0 + ddxA, y0 + ddyA)], - path.vertices[1:-1], - [(x3 + ddxB, y3 + ddyB)]]), - path.codes)] - fills = [False] - - if has_begin_arrow: - if self.fillbegin: - paths.append( - Path([*verticesA, (0, 0)], [*codesA, Path.CLOSEPOLY])) - fills.append(True) - else: - paths.append(Path(verticesA, codesA)) - fills.append(False) - elif self._beginarrow_bracket: - x0, y0 = path.vertices[0] - x1, y1 = path.vertices[1] - verticesA, codesA = self._get_bracket(x0, y0, x1, y1, - self.widthA * scaleA, - self.lengthA * scaleA, - self.angleA) - - paths.append(Path(verticesA, codesA)) - fills.append(False) - - if has_end_arrow: - if self.fillend: - fills.append(True) - paths.append( - Path([*verticesB, (0, 0)], [*codesB, Path.CLOSEPOLY])) - else: - fills.append(False) - paths.append(Path(verticesB, codesB)) - elif self._endarrow_bracket: - x0, y0 = path.vertices[-1] - x1, y1 = path.vertices[-2] - verticesB, codesB = self._get_bracket(x0, y0, x1, y1, - self.widthB * scaleB, - self.lengthB * scaleB, - self.angleB) - - paths.append(Path(verticesB, codesB)) - fills.append(False) - - return paths, fills - - @_register_style(_style_list, name="-") - class Curve(_Curve): - """A simple curve without any arrow head.""" - - def __init__(self): # hide head_length, head_width - # These attributes (whose values come from backcompat) only matter - # if someone modifies beginarrow/etc. on an ArrowStyle instance. - super().__init__(head_length=.2, head_width=.1) - - @_register_style(_style_list, name="<-") - class CurveA(_Curve): - """An arrow with a head at its start point.""" - arrow = "<-" - - @_register_style(_style_list, name="->") - class CurveB(_Curve): - """An arrow with a head at its end point.""" - arrow = "->" - - @_register_style(_style_list, name="<->") - class CurveAB(_Curve): - """An arrow with heads both at the start and the end point.""" - arrow = "<->" - - @_register_style(_style_list, name="<|-") - class CurveFilledA(_Curve): - """An arrow with filled triangle head at the start.""" - arrow = "<|-" - - @_register_style(_style_list, name="-|>") - class CurveFilledB(_Curve): - """An arrow with filled triangle head at the end.""" - arrow = "-|>" - - @_register_style(_style_list, name="<|-|>") - class CurveFilledAB(_Curve): - """An arrow with filled triangle heads at both ends.""" - arrow = "<|-|>" - - @_register_style(_style_list, name="]-") - class BracketA(_Curve): - """An arrow with an outward square bracket at its start.""" - arrow = "]-" - - def __init__(self, widthA=1., lengthA=0.2, angleA=0): - """ - Parameters - ---------- - widthA : float, default: 1.0 - Width of the bracket. - lengthA : float, default: 0.2 - Length of the bracket. - angleA : float, default: 0 degrees - Orientation of the bracket, as a counterclockwise angle. - 0 degrees means perpendicular to the line. - """ - super().__init__(widthA=widthA, lengthA=lengthA, angleA=angleA) - - @_register_style(_style_list, name="-[") - class BracketB(_Curve): - """An arrow with an outward square bracket at its end.""" - arrow = "-[" - - def __init__(self, widthB=1., lengthB=0.2, angleB=0): - """ - Parameters - ---------- - widthB : float, default: 1.0 - Width of the bracket. - lengthB : float, default: 0.2 - Length of the bracket. - angleB : float, default: 0 degrees - Orientation of the bracket, as a counterclockwise angle. - 0 degrees means perpendicular to the line. - """ - super().__init__(widthB=widthB, lengthB=lengthB, angleB=angleB) - - @_register_style(_style_list, name="]-[") - class BracketAB(_Curve): - """An arrow with outward square brackets at both ends.""" - arrow = "]-[" - - def __init__(self, - widthA=1., lengthA=0.2, angleA=0, - widthB=1., lengthB=0.2, angleB=0): - """ - Parameters - ---------- - widthA, widthB : float, default: 1.0 - Width of the bracket. - lengthA, lengthB : float, default: 0.2 - Length of the bracket. - angleA, angleB : float, default: 0 degrees - Orientation of the bracket, as a counterclockwise angle. - 0 degrees means perpendicular to the line. - """ - super().__init__(widthA=widthA, lengthA=lengthA, angleA=angleA, - widthB=widthB, lengthB=lengthB, angleB=angleB) - - @_register_style(_style_list, name="|-|") - class BarAB(_Curve): - """An arrow with vertical bars ``|`` at both ends.""" - arrow = "|-|" - - def __init__(self, widthA=1., angleA=0, widthB=1., angleB=0): - """ - Parameters - ---------- - widthA, widthB : float, default: 1.0 - Width of the bracket. - angleA, angleB : float, default: 0 degrees - Orientation of the bracket, as a counterclockwise angle. - 0 degrees means perpendicular to the line. - """ - super().__init__(widthA=widthA, lengthA=0, angleA=angleA, - widthB=widthB, lengthB=0, angleB=angleB) - - @_register_style(_style_list, name=']->') - class BracketCurve(_Curve): - """ - An arrow with an outward square bracket at its start and a head at - the end. - """ - arrow = "]->" - - def __init__(self, widthA=1., lengthA=0.2, angleA=None): - """ - Parameters - ---------- - widthA : float, default: 1.0 - Width of the bracket. - lengthA : float, default: 0.2 - Length of the bracket. - angleA : float, default: 0 degrees - Orientation of the bracket, as a counterclockwise angle. - 0 degrees means perpendicular to the line. - """ - super().__init__(widthA=widthA, lengthA=lengthA, angleA=angleA) - - @_register_style(_style_list, name='<-[') - class CurveBracket(_Curve): - """ - An arrow with an outward square bracket at its end and a head at - the start. - """ - arrow = "<-[" - - def __init__(self, widthB=1., lengthB=0.2, angleB=None): - """ - Parameters - ---------- - widthB : float, default: 1.0 - Width of the bracket. - lengthB : float, default: 0.2 - Length of the bracket. - angleB : float, default: 0 degrees - Orientation of the bracket, as a counterclockwise angle. - 0 degrees means perpendicular to the line. - """ - super().__init__(widthB=widthB, lengthB=lengthB, angleB=angleB) - - @_register_style(_style_list) - class Simple(_Base): - """A simple arrow. Only works with a quadratic Bézier curve.""" - - def __init__(self, head_length=.5, head_width=.5, tail_width=.2): - """ - Parameters - ---------- - head_length : float, default: 0.5 - Length of the arrow head. - - head_width : float, default: 0.5 - Width of the arrow head. - - tail_width : float, default: 0.2 - Width of the arrow tail. - """ - self.head_length, self.head_width, self.tail_width = \ - head_length, head_width, tail_width - super().__init__() - - def transmute(self, path, mutation_size, linewidth): - # docstring inherited - x0, y0, x1, y1, x2, y2 = self.ensure_quadratic_bezier(path) - - # divide the path into a head and a tail - head_length = self.head_length * mutation_size - in_f = inside_circle(x2, y2, head_length) - arrow_path = [(x0, y0), (x1, y1), (x2, y2)] - - try: - arrow_out, arrow_in = \ - split_bezier_intersecting_with_closedpath(arrow_path, in_f) - except NonIntersectingPathException: - # if this happens, make a straight line of the head_length - # long. - x0, y0 = _point_along_a_line(x2, y2, x1, y1, head_length) - x1n, y1n = 0.5 * (x0 + x2), 0.5 * (y0 + y2) - arrow_in = [(x0, y0), (x1n, y1n), (x2, y2)] - arrow_out = None - - # head - head_width = self.head_width * mutation_size - head_left, head_right = make_wedged_bezier2(arrow_in, - head_width / 2., wm=.5) - - # tail - if arrow_out is not None: - tail_width = self.tail_width * mutation_size - tail_left, tail_right = get_parallels(arrow_out, - tail_width / 2.) - - patch_path = [(Path.MOVETO, tail_right[0]), - (Path.CURVE3, tail_right[1]), - (Path.CURVE3, tail_right[2]), - (Path.LINETO, head_right[0]), - (Path.CURVE3, head_right[1]), - (Path.CURVE3, head_right[2]), - (Path.CURVE3, head_left[1]), - (Path.CURVE3, head_left[0]), - (Path.LINETO, tail_left[2]), - (Path.CURVE3, tail_left[1]), - (Path.CURVE3, tail_left[0]), - (Path.LINETO, tail_right[0]), - (Path.CLOSEPOLY, tail_right[0]), - ] - else: - patch_path = [(Path.MOVETO, head_right[0]), - (Path.CURVE3, head_right[1]), - (Path.CURVE3, head_right[2]), - (Path.CURVE3, head_left[1]), - (Path.CURVE3, head_left[0]), - (Path.CLOSEPOLY, head_left[0]), - ] - - path = Path([p for c, p in patch_path], [c for c, p in patch_path]) - - return path, True - - @_register_style(_style_list) - class Fancy(_Base): - """A fancy arrow. Only works with a quadratic Bézier curve.""" - - def __init__(self, head_length=.4, head_width=.4, tail_width=.4): - """ - Parameters - ---------- - head_length : float, default: 0.4 - Length of the arrow head. - - head_width : float, default: 0.4 - Width of the arrow head. - - tail_width : float, default: 0.4 - Width of the arrow tail. - """ - self.head_length, self.head_width, self.tail_width = \ - head_length, head_width, tail_width - super().__init__() - - def transmute(self, path, mutation_size, linewidth): - # docstring inherited - x0, y0, x1, y1, x2, y2 = self.ensure_quadratic_bezier(path) - - # divide the path into a head and a tail - head_length = self.head_length * mutation_size - arrow_path = [(x0, y0), (x1, y1), (x2, y2)] - - # path for head - in_f = inside_circle(x2, y2, head_length) - try: - path_out, path_in = split_bezier_intersecting_with_closedpath( - arrow_path, in_f) - except NonIntersectingPathException: - # if this happens, make a straight line of the head_length - # long. - x0, y0 = _point_along_a_line(x2, y2, x1, y1, head_length) - x1n, y1n = 0.5 * (x0 + x2), 0.5 * (y0 + y2) - arrow_path = [(x0, y0), (x1n, y1n), (x2, y2)] - path_head = arrow_path - else: - path_head = path_in - - # path for head - in_f = inside_circle(x2, y2, head_length * .8) - path_out, path_in = split_bezier_intersecting_with_closedpath( - arrow_path, in_f) - path_tail = path_out - - # head - head_width = self.head_width * mutation_size - head_l, head_r = make_wedged_bezier2(path_head, - head_width / 2., - wm=.6) - - # tail - tail_width = self.tail_width * mutation_size - tail_left, tail_right = make_wedged_bezier2(path_tail, - tail_width * .5, - w1=1., wm=0.6, w2=0.3) - - # path for head - in_f = inside_circle(x0, y0, tail_width * .3) - path_in, path_out = split_bezier_intersecting_with_closedpath( - arrow_path, in_f) - tail_start = path_in[-1] - - head_right, head_left = head_r, head_l - patch_path = [(Path.MOVETO, tail_start), - (Path.LINETO, tail_right[0]), - (Path.CURVE3, tail_right[1]), - (Path.CURVE3, tail_right[2]), - (Path.LINETO, head_right[0]), - (Path.CURVE3, head_right[1]), - (Path.CURVE3, head_right[2]), - (Path.CURVE3, head_left[1]), - (Path.CURVE3, head_left[0]), - (Path.LINETO, tail_left[2]), - (Path.CURVE3, tail_left[1]), - (Path.CURVE3, tail_left[0]), - (Path.LINETO, tail_start), - (Path.CLOSEPOLY, tail_start), - ] - path = Path([p for c, p in patch_path], [c for c, p in patch_path]) - - return path, True - - @_register_style(_style_list) - class Wedge(_Base): - """ - Wedge(?) shape. Only works with a quadratic Bézier curve. The - start point has a width of the *tail_width* and the end point has a - width of 0. At the middle, the width is *shrink_factor*x*tail_width*. - """ - - def __init__(self, tail_width=.3, shrink_factor=0.5): - """ - Parameters - ---------- - tail_width : float, default: 0.3 - Width of the tail. - - shrink_factor : float, default: 0.5 - Fraction of the arrow width at the middle point. - """ - self.tail_width = tail_width - self.shrink_factor = shrink_factor - super().__init__() - - def transmute(self, path, mutation_size, linewidth): - # docstring inherited - x0, y0, x1, y1, x2, y2 = self.ensure_quadratic_bezier(path) - - arrow_path = [(x0, y0), (x1, y1), (x2, y2)] - b_plus, b_minus = make_wedged_bezier2( - arrow_path, - self.tail_width * mutation_size / 2., - wm=self.shrink_factor) - - patch_path = [(Path.MOVETO, b_plus[0]), - (Path.CURVE3, b_plus[1]), - (Path.CURVE3, b_plus[2]), - (Path.LINETO, b_minus[2]), - (Path.CURVE3, b_minus[1]), - (Path.CURVE3, b_minus[0]), - (Path.CLOSEPOLY, b_minus[0]), - ] - path = Path([p for c, p in patch_path], [c for c, p in patch_path]) - - return path, True - - -class FancyBboxPatch(Patch): - """ - A fancy box around a rectangle with lower left at *xy* = (*x*, *y*) - with specified width and height. - - `.FancyBboxPatch` is similar to `.Rectangle`, but it draws a fancy box - around the rectangle. The transformation of the rectangle box to the - fancy box is delegated to the style classes defined in `.BoxStyle`. - """ - - _edge_default = True - - def __str__(self): - s = self.__class__.__name__ + "((%g, %g), width=%g, height=%g)" - return s % (self._x, self._y, self._width, self._height) - - @_docstring.dedent_interpd - def __init__(self, xy, width, height, boxstyle="round", *, - mutation_scale=1, mutation_aspect=1, **kwargs): - """ - Parameters - ---------- - xy : (float, float) - The lower left corner of the box. - - width : float - The width of the box. - - height : float - The height of the box. - - boxstyle : str or `~matplotlib.patches.BoxStyle` - The style of the fancy box. This can either be a `.BoxStyle` - instance or a string of the style name and optionally comma - separated attributes (e.g. "Round, pad=0.2"). This string is - passed to `.BoxStyle` to construct a `.BoxStyle` object. See - there for a full documentation. - - The following box styles are available: - - %(BoxStyle:table)s - - mutation_scale : float, default: 1 - Scaling factor applied to the attributes of the box style - (e.g. pad or rounding_size). - - mutation_aspect : float, default: 1 - The height of the rectangle will be squeezed by this value before - the mutation and the mutated box will be stretched by the inverse - of it. For example, this allows different horizontal and vertical - padding. - - Other Parameters - ---------------- - **kwargs : `~matplotlib.patches.Patch` properties - - %(Patch:kwdoc)s - """ - - super().__init__(**kwargs) - self._x, self._y = xy - self._width = width - self._height = height - self.set_boxstyle(boxstyle) - self._mutation_scale = mutation_scale - self._mutation_aspect = mutation_aspect - self.stale = True - - @_docstring.dedent_interpd - def set_boxstyle(self, boxstyle=None, **kwargs): - """ - Set the box style, possibly with further attributes. - - Attributes from the previous box style are not reused. - - Without argument (or with ``boxstyle=None``), the available box styles - are returned as a human-readable string. - - Parameters - ---------- - boxstyle : str or `~matplotlib.patches.BoxStyle` - The style of the box: either a `.BoxStyle` instance, or a string, - which is the style name and optionally comma separated attributes - (e.g. "Round,pad=0.2"). Such a string is used to construct a - `.BoxStyle` object, as documented in that class. - - The following box styles are available: - - %(BoxStyle:table_and_accepts)s - - **kwargs - Additional attributes for the box style. See the table above for - supported parameters. - - Examples - -------- - :: - - set_boxstyle("Round,pad=0.2") - set_boxstyle("round", pad=0.2) - """ - if boxstyle is None: - return BoxStyle.pprint_styles() - self._bbox_transmuter = ( - BoxStyle(boxstyle, **kwargs) - if isinstance(boxstyle, str) else boxstyle) - self.stale = True - - def get_boxstyle(self): - """Return the boxstyle object.""" - return self._bbox_transmuter - - def set_mutation_scale(self, scale): - """ - Set the mutation scale. - - Parameters - ---------- - scale : float - """ - self._mutation_scale = scale - self.stale = True - - def get_mutation_scale(self): - """Return the mutation scale.""" - return self._mutation_scale - - def set_mutation_aspect(self, aspect): - """ - Set the aspect ratio of the bbox mutation. - - Parameters - ---------- - aspect : float - """ - self._mutation_aspect = aspect - self.stale = True - - def get_mutation_aspect(self): - """Return the aspect ratio of the bbox mutation.""" - return (self._mutation_aspect if self._mutation_aspect is not None - else 1) # backcompat. - - def get_path(self): - """Return the mutated path of the rectangle.""" - boxstyle = self.get_boxstyle() - m_aspect = self.get_mutation_aspect() - # Call boxstyle with y, height squeezed by aspect_ratio. - path = boxstyle(self._x, self._y / m_aspect, - self._width, self._height / m_aspect, - self.get_mutation_scale()) - return Path(path.vertices * [1, m_aspect], path.codes) # Unsqueeze y. - - # Following methods are borrowed from the Rectangle class. - - def get_x(self): - """Return the left coord of the rectangle.""" - return self._x - - def get_y(self): - """Return the bottom coord of the rectangle.""" - return self._y - - def get_width(self): - """Return the width of the rectangle.""" - return self._width - - def get_height(self): - """Return the height of the rectangle.""" - return self._height - - def set_x(self, x): - """ - Set the left coord of the rectangle. - - Parameters - ---------- - x : float - """ - self._x = x - self.stale = True - - def set_y(self, y): - """ - Set the bottom coord of the rectangle. - - Parameters - ---------- - y : float - """ - self._y = y - self.stale = True - - def set_width(self, w): - """ - Set the rectangle width. - - Parameters - ---------- - w : float - """ - self._width = w - self.stale = True - - def set_height(self, h): - """ - Set the rectangle height. - - Parameters - ---------- - h : float - """ - self._height = h - self.stale = True - - def set_bounds(self, *args): - """ - Set the bounds of the rectangle. - - Call signatures:: - - set_bounds(left, bottom, width, height) - set_bounds((left, bottom, width, height)) - - Parameters - ---------- - left, bottom : float - The coordinates of the bottom left corner of the rectangle. - width, height : float - The width/height of the rectangle. - """ - if len(args) == 1: - l, b, w, h = args[0] - else: - l, b, w, h = args - self._x = l - self._y = b - self._width = w - self._height = h - self.stale = True - - def get_bbox(self): - """Return the `.Bbox`.""" - return transforms.Bbox.from_bounds(self._x, self._y, - self._width, self._height) - - -class FancyArrowPatch(Patch): - """ - A fancy arrow patch. - - It draws an arrow using the `ArrowStyle`. It is primarily used by the - `~.axes.Axes.annotate` method. For most purposes, use the annotate method for - drawing arrows. - - The head and tail positions are fixed at the specified start and end points - of the arrow, but the size and shape (in display coordinates) of the arrow - does not change when the axis is moved or zoomed. - """ - _edge_default = True - - def __str__(self): - if self._posA_posB is not None: - (x1, y1), (x2, y2) = self._posA_posB - return f"{type(self).__name__}(({x1:g}, {y1:g})->({x2:g}, {y2:g}))" - else: - return f"{type(self).__name__}({self._path_original})" - - @_docstring.dedent_interpd - @_api.make_keyword_only("3.6", name="path") - def __init__(self, posA=None, posB=None, path=None, - arrowstyle="simple", connectionstyle="arc3", - patchA=None, patchB=None, - shrinkA=2, shrinkB=2, - mutation_scale=1, mutation_aspect=1, - **kwargs): - """ - There are two ways for defining an arrow: - - - If *posA* and *posB* are given, a path connecting two points is - created according to *connectionstyle*. The path will be - clipped with *patchA* and *patchB* and further shrunken by - *shrinkA* and *shrinkB*. An arrow is drawn along this - resulting path using the *arrowstyle* parameter. - - - Alternatively if *path* is provided, an arrow is drawn along this - path and *patchA*, *patchB*, *shrinkA*, and *shrinkB* are ignored. - - Parameters - ---------- - posA, posB : (float, float), default: None - (x, y) coordinates of arrow tail and arrow head respectively. - - path : `~matplotlib.path.Path`, default: None - If provided, an arrow is drawn along this path and *patchA*, - *patchB*, *shrinkA*, and *shrinkB* are ignored. - - arrowstyle : str or `.ArrowStyle`, default: 'simple' - The `.ArrowStyle` with which the fancy arrow is drawn. If a - string, it should be one of the available arrowstyle names, with - optional comma-separated attributes. The optional attributes are - meant to be scaled with the *mutation_scale*. The following arrow - styles are available: - - %(ArrowStyle:table)s - - connectionstyle : str or `.ConnectionStyle` or None, optional, \ -default: 'arc3' - The `.ConnectionStyle` with which *posA* and *posB* are connected. - If a string, it should be one of the available connectionstyle - names, with optional comma-separated attributes. The following - connection styles are available: - - %(ConnectionStyle:table)s - - patchA, patchB : `~matplotlib.patches.Patch`, default: None - Head and tail patches, respectively. - - shrinkA, shrinkB : float, default: 2 - Shrinking factor of the tail and head of the arrow respectively. - - mutation_scale : float, default: 1 - Value with which attributes of *arrowstyle* (e.g., *head_length*) - will be scaled. - - mutation_aspect : None or float, default: None - The height of the rectangle will be squeezed by this value before - the mutation and the mutated box will be stretched by the inverse - of it. - - Other Parameters - ---------------- - **kwargs : `~matplotlib.patches.Patch` properties, optional - Here is a list of available `.Patch` properties: - - %(Patch:kwdoc)s - - In contrast to other patches, the default ``capstyle`` and - ``joinstyle`` for `FancyArrowPatch` are set to ``"round"``. - """ - # Traditionally, the cap- and joinstyle for FancyArrowPatch are round - kwargs.setdefault("joinstyle", JoinStyle.round) - kwargs.setdefault("capstyle", CapStyle.round) - - super().__init__(**kwargs) - - if posA is not None and posB is not None and path is None: - self._posA_posB = [posA, posB] - - if connectionstyle is None: - connectionstyle = "arc3" - self.set_connectionstyle(connectionstyle) - - elif posA is None and posB is None and path is not None: - self._posA_posB = None - else: - raise ValueError("Either posA and posB, or path need to provided") - - self.patchA = patchA - self.patchB = patchB - self.shrinkA = shrinkA - self.shrinkB = shrinkB - - self._path_original = path - - self.set_arrowstyle(arrowstyle) - - self._mutation_scale = mutation_scale - self._mutation_aspect = mutation_aspect - - self._dpi_cor = 1.0 - - def set_positions(self, posA, posB): - """ - Set the start and end positions of the connecting path. - - Parameters - ---------- - posA, posB : None, tuple - (x, y) coordinates of arrow tail and arrow head respectively. If - `None` use current value. - """ - if posA is not None: - self._posA_posB[0] = posA - if posB is not None: - self._posA_posB[1] = posB - self.stale = True - - def set_patchA(self, patchA): - """ - Set the tail patch. - - Parameters - ---------- - patchA : `.patches.Patch` - """ - self.patchA = patchA - self.stale = True - - def set_patchB(self, patchB): - """ - Set the head patch. - - Parameters - ---------- - patchB : `.patches.Patch` - """ - self.patchB = patchB - self.stale = True - - @_docstring.dedent_interpd - def set_connectionstyle(self, connectionstyle=None, **kwargs): - """ - Set the connection style, possibly with further attributes. - - Attributes from the previous connection style are not reused. - - Without argument (or with ``connectionstyle=None``), the available box - styles are returned as a human-readable string. - - Parameters - ---------- - connectionstyle : str or `~matplotlib.patches.ConnectionStyle` - The style of the connection: either a `.ConnectionStyle` instance, - or a string, which is the style name and optionally comma separated - attributes (e.g. "Arc,armA=30,rad=10"). Such a string is used to - construct a `.ConnectionStyle` object, as documented in that class. - - The following connection styles are available: - - %(ConnectionStyle:table_and_accepts)s - - **kwargs - Additional attributes for the connection style. See the table above - for supported parameters. - - Examples - -------- - :: - - set_connectionstyle("Arc,armA=30,rad=10") - set_connectionstyle("arc", armA=30, rad=10) - """ - if connectionstyle is None: - return ConnectionStyle.pprint_styles() - self._connector = ( - ConnectionStyle(connectionstyle, **kwargs) - if isinstance(connectionstyle, str) else connectionstyle) - self.stale = True - - def get_connectionstyle(self): - """Return the `ConnectionStyle` used.""" - return self._connector - - def set_arrowstyle(self, arrowstyle=None, **kwargs): - """ - Set the arrow style, possibly with further attributes. - - Attributes from the previous arrow style are not reused. - - Without argument (or with ``arrowstyle=None``), the available box - styles are returned as a human-readable string. - - Parameters - ---------- - arrowstyle : str or `~matplotlib.patches.ArrowStyle` - The style of the arrow: either a `.ArrowStyle` instance, or a - string, which is the style name and optionally comma separated - attributes (e.g. "Fancy,head_length=0.2"). Such a string is used to - construct a `.ArrowStyle` object, as documented in that class. - - The following arrow styles are available: - - %(ArrowStyle:table_and_accepts)s - - **kwargs - Additional attributes for the arrow style. See the table above for - supported parameters. - - Examples - -------- - :: - - set_arrowstyle("Fancy,head_length=0.2") - set_arrowstyle("fancy", head_length=0.2) - """ - if arrowstyle is None: - return ArrowStyle.pprint_styles() - self._arrow_transmuter = ( - ArrowStyle(arrowstyle, **kwargs) - if isinstance(arrowstyle, str) else arrowstyle) - self.stale = True - - def get_arrowstyle(self): - """Return the arrowstyle object.""" - return self._arrow_transmuter - - def set_mutation_scale(self, scale): - """ - Set the mutation scale. - - Parameters - ---------- - scale : float - """ - self._mutation_scale = scale - self.stale = True - - def get_mutation_scale(self): - """ - Return the mutation scale. - - Returns - ------- - scalar - """ - return self._mutation_scale - - def set_mutation_aspect(self, aspect): - """ - Set the aspect ratio of the bbox mutation. - - Parameters - ---------- - aspect : float - """ - self._mutation_aspect = aspect - self.stale = True - - def get_mutation_aspect(self): - """Return the aspect ratio of the bbox mutation.""" - return (self._mutation_aspect if self._mutation_aspect is not None - else 1) # backcompat. - - def get_path(self): - """Return the path of the arrow in the data coordinates.""" - # The path is generated in display coordinates, then converted back to - # data coordinates. - _path, fillable = self._get_path_in_displaycoord() - if np.iterable(fillable): - _path = Path.make_compound_path(*_path) - return self.get_transform().inverted().transform_path(_path) - - def _get_path_in_displaycoord(self): - """Return the mutated path of the arrow in display coordinates.""" - dpi_cor = self._dpi_cor - - if self._posA_posB is not None: - posA = self._convert_xy_units(self._posA_posB[0]) - posB = self._convert_xy_units(self._posA_posB[1]) - (posA, posB) = self.get_transform().transform((posA, posB)) - _path = self.get_connectionstyle()(posA, posB, - patchA=self.patchA, - patchB=self.patchB, - shrinkA=self.shrinkA * dpi_cor, - shrinkB=self.shrinkB * dpi_cor - ) - else: - _path = self.get_transform().transform_path(self._path_original) - - _path, fillable = self.get_arrowstyle()( - _path, - self.get_mutation_scale() * dpi_cor, - self.get_linewidth() * dpi_cor, - self.get_mutation_aspect()) - - return _path, fillable - - def draw(self, renderer): - if not self.get_visible(): - return - - # FIXME: dpi_cor is for the dpi-dependency of the linewidth. There - # could be room for improvement. Maybe _get_path_in_displaycoord could - # take a renderer argument, but get_path should be adapted too. - self._dpi_cor = renderer.points_to_pixels(1.) - path, fillable = self._get_path_in_displaycoord() - - if not np.iterable(fillable): - path = [path] - fillable = [fillable] - - affine = transforms.IdentityTransform() - - self._draw_paths_with_artist_properties( - renderer, - [(p, affine, self._facecolor if f and self._facecolor[3] else None) - for p, f in zip(path, fillable)]) - - -class ConnectionPatch(FancyArrowPatch): - """A patch that connects two points (possibly in different axes).""" - - def __str__(self): - return "ConnectionPatch((%g, %g), (%g, %g))" % \ - (self.xy1[0], self.xy1[1], self.xy2[0], self.xy2[1]) - - @_docstring.dedent_interpd - @_api.make_keyword_only("3.6", name="axesA") - def __init__(self, xyA, xyB, coordsA, coordsB=None, - axesA=None, axesB=None, - arrowstyle="-", - connectionstyle="arc3", - patchA=None, - patchB=None, - shrinkA=0., - shrinkB=0., - mutation_scale=10., - mutation_aspect=None, - clip_on=False, - **kwargs): - """ - Connect point *xyA* in *coordsA* with point *xyB* in *coordsB*. - - Valid keys are - - =============== ====================================================== - Key Description - =============== ====================================================== - arrowstyle the arrow style - connectionstyle the connection style - relpos default is (0.5, 0.5) - patchA default is bounding box of the text - patchB default is None - shrinkA default is 2 points - shrinkB default is 2 points - mutation_scale default is text size (in points) - mutation_aspect default is 1. - ? any key for `matplotlib.patches.PathPatch` - =============== ====================================================== - - *coordsA* and *coordsB* are strings that indicate the - coordinates of *xyA* and *xyB*. - - ==================== ================================================== - Property Description - ==================== ================================================== - 'figure points' points from the lower left corner of the figure - 'figure pixels' pixels from the lower left corner of the figure - 'figure fraction' 0, 0 is lower left of figure and 1, 1 is upper - right - 'subfigure points' points from the lower left corner of the subfigure - 'subfigure pixels' pixels from the lower left corner of the subfigure - 'subfigure fraction' fraction of the subfigure, 0, 0 is lower left. - 'axes points' points from lower left corner of axes - 'axes pixels' pixels from lower left corner of axes - 'axes fraction' 0, 0 is lower left of axes and 1, 1 is upper right - 'data' use the coordinate system of the object being - annotated (default) - 'offset points' offset (in points) from the *xy* value - 'polar' you can specify *theta*, *r* for the annotation, - even in cartesian plots. Note that if you are - using a polar axes, you do not need to specify - polar for the coordinate system since that is the - native "data" coordinate system. - ==================== ================================================== - - Alternatively they can be set to any valid - `~matplotlib.transforms.Transform`. - - Note that 'subfigure pixels' and 'figure pixels' are the same - for the parent figure, so users who want code that is usable in - a subfigure can use 'subfigure pixels'. - - .. note:: - - Using `ConnectionPatch` across two `~.axes.Axes` instances - is not directly compatible with :doc:`constrained layout - `. Add the artist - directly to the `.Figure` instead of adding it to a specific Axes, - or exclude it from the layout using ``con.set_in_layout(False)``. - - .. code-block:: default - - fig, ax = plt.subplots(1, 2, constrained_layout=True) - con = ConnectionPatch(..., axesA=ax[0], axesB=ax[1]) - fig.add_artist(con) - - """ - if coordsB is None: - coordsB = coordsA - # we'll draw ourself after the artist we annotate by default - self.xy1 = xyA - self.xy2 = xyB - self.coords1 = coordsA - self.coords2 = coordsB - - self.axesA = axesA - self.axesB = axesB - - super().__init__(posA=(0, 0), posB=(1, 1), - arrowstyle=arrowstyle, - connectionstyle=connectionstyle, - patchA=patchA, patchB=patchB, - shrinkA=shrinkA, shrinkB=shrinkB, - mutation_scale=mutation_scale, - mutation_aspect=mutation_aspect, - clip_on=clip_on, - **kwargs) - # if True, draw annotation only if self.xy is inside the axes - self._annotation_clip = None - - def _get_xy(self, xy, s, axes=None): - """Calculate the pixel position of given point.""" - s0 = s # For the error message, if needed. - if axes is None: - axes = self.axes - xy = np.array(xy) - if s in ["figure points", "axes points"]: - xy *= self.figure.dpi / 72 - s = s.replace("points", "pixels") - elif s == "figure fraction": - s = self.figure.transFigure - elif s == "subfigure fraction": - s = self.figure.transSubfigure - elif s == "axes fraction": - s = axes.transAxes - x, y = xy - - if s == 'data': - trans = axes.transData - x = float(self.convert_xunits(x)) - y = float(self.convert_yunits(y)) - return trans.transform((x, y)) - elif s == 'offset points': - if self.xycoords == 'offset points': # prevent recursion - return self._get_xy(self.xy, 'data') - return ( - self._get_xy(self.xy, self.xycoords) # converted data point - + xy * self.figure.dpi / 72) # converted offset - elif s == 'polar': - theta, r = x, y - x = r * np.cos(theta) - y = r * np.sin(theta) - trans = axes.transData - return trans.transform((x, y)) - elif s == 'figure pixels': - # pixels from the lower left corner of the figure - bb = self.figure.figbbox - x = bb.x0 + x if x >= 0 else bb.x1 + x - y = bb.y0 + y if y >= 0 else bb.y1 + y - return x, y - elif s == 'subfigure pixels': - # pixels from the lower left corner of the figure - bb = self.figure.bbox - x = bb.x0 + x if x >= 0 else bb.x1 + x - y = bb.y0 + y if y >= 0 else bb.y1 + y - return x, y - elif s == 'axes pixels': - # pixels from the lower left corner of the axes - bb = axes.bbox - x = bb.x0 + x if x >= 0 else bb.x1 + x - y = bb.y0 + y if y >= 0 else bb.y1 + y - return x, y - elif isinstance(s, transforms.Transform): - return s.transform(xy) - else: - raise ValueError(f"{s0} is not a valid coordinate transformation") - - def set_annotation_clip(self, b): - """ - Set the annotation's clipping behavior. - - Parameters - ---------- - b : bool or None - - True: The annotation will be clipped when ``self.xy`` is - outside the axes. - - False: The annotation will always be drawn. - - None: The annotation will be clipped when ``self.xy`` is - outside the axes and ``self.xycoords == "data"``. - """ - self._annotation_clip = b - self.stale = True - - def get_annotation_clip(self): - """ - Return the clipping behavior. - - See `.set_annotation_clip` for the meaning of the return value. - """ - return self._annotation_clip - - def _get_path_in_displaycoord(self): - """Return the mutated path of the arrow in display coordinates.""" - dpi_cor = self._dpi_cor - posA = self._get_xy(self.xy1, self.coords1, self.axesA) - posB = self._get_xy(self.xy2, self.coords2, self.axesB) - path = self.get_connectionstyle()( - posA, posB, - patchA=self.patchA, patchB=self.patchB, - shrinkA=self.shrinkA * dpi_cor, shrinkB=self.shrinkB * dpi_cor, - ) - path, fillable = self.get_arrowstyle()( - path, - self.get_mutation_scale() * dpi_cor, - self.get_linewidth() * dpi_cor, - self.get_mutation_aspect() - ) - return path, fillable - - def _check_xy(self, renderer): - """Check whether the annotation needs to be drawn.""" - - b = self.get_annotation_clip() - - if b or (b is None and self.coords1 == "data"): - xy_pixel = self._get_xy(self.xy1, self.coords1, self.axesA) - if self.axesA is None: - axes = self.axes - else: - axes = self.axesA - if not axes.contains_point(xy_pixel): - return False - - if b or (b is None and self.coords2 == "data"): - xy_pixel = self._get_xy(self.xy2, self.coords2, self.axesB) - if self.axesB is None: - axes = self.axes - else: - axes = self.axesB - if not axes.contains_point(xy_pixel): - return False - - return True - - def draw(self, renderer): - if renderer is not None: - self._renderer = renderer - if not self.get_visible() or not self._check_xy(renderer): - return - super().draw(renderer) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/path.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/path.py deleted file mode 100644 index 4856c74..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/path.py +++ /dev/null @@ -1,1083 +0,0 @@ -r""" -A module for dealing with the polylines used throughout Matplotlib. - -The primary class for polyline handling in Matplotlib is `Path`. Almost all -vector drawing makes use of `Path`\s somewhere in the drawing pipeline. - -Whilst a `Path` instance itself cannot be drawn, some `.Artist` subclasses, -such as `.PathPatch` and `.PathCollection`, can be used for convenient `Path` -visualisation. -""" - -import copy -from functools import lru_cache -from weakref import WeakValueDictionary - -import numpy as np - -import matplotlib as mpl -from . import _api, _path -from .cbook import _to_unmasked_float_array, simple_linear_interpolation -from .bezier import BezierSegment - - -class Path: - """ - A series of possibly disconnected, possibly closed, line and curve - segments. - - The underlying storage is made up of two parallel numpy arrays: - - - *vertices*: an Nx2 float array of vertices - - *codes*: an N-length uint8 array of path codes, or None - - These two arrays always have the same length in the first - dimension. For example, to represent a cubic curve, you must - provide three vertices and three ``CURVE4`` codes. - - The code types are: - - - ``STOP`` : 1 vertex (ignored) - A marker for the end of the entire path (currently not required and - ignored) - - - ``MOVETO`` : 1 vertex - Pick up the pen and move to the given vertex. - - - ``LINETO`` : 1 vertex - Draw a line from the current position to the given vertex. - - - ``CURVE3`` : 1 control point, 1 endpoint - Draw a quadratic Bézier curve from the current position, with the given - control point, to the given end point. - - - ``CURVE4`` : 2 control points, 1 endpoint - Draw a cubic Bézier curve from the current position, with the given - control points, to the given end point. - - - ``CLOSEPOLY`` : 1 vertex (ignored) - Draw a line segment to the start point of the current polyline. - - If *codes* is None, it is interpreted as a ``MOVETO`` followed by a series - of ``LINETO``. - - Users of Path objects should not access the vertices and codes arrays - directly. Instead, they should use `iter_segments` or `cleaned` to get the - vertex/code pairs. This helps, in particular, to consistently handle the - case of *codes* being None. - - Some behavior of Path objects can be controlled by rcParams. See the - rcParams whose keys start with 'path.'. - - .. note:: - - The vertices and codes arrays should be treated as - immutable -- there are a number of optimizations and assumptions - made up front in the constructor that will not change when the - data changes. - """ - - code_type = np.uint8 - - # Path codes - STOP = code_type(0) # 1 vertex - MOVETO = code_type(1) # 1 vertex - LINETO = code_type(2) # 1 vertex - CURVE3 = code_type(3) # 2 vertices - CURVE4 = code_type(4) # 3 vertices - CLOSEPOLY = code_type(79) # 1 vertex - - #: A dictionary mapping Path codes to the number of vertices that the - #: code expects. - NUM_VERTICES_FOR_CODE = {STOP: 1, - MOVETO: 1, - LINETO: 1, - CURVE3: 2, - CURVE4: 3, - CLOSEPOLY: 1} - - def __init__(self, vertices, codes=None, _interpolation_steps=1, - closed=False, readonly=False): - """ - Create a new path with the given vertices and codes. - - Parameters - ---------- - vertices : (N, 2) array-like - The path vertices, as an array, masked array or sequence of pairs. - Masked values, if any, will be converted to NaNs, which are then - handled correctly by the Agg PathIterator and other consumers of - path data, such as :meth:`iter_segments`. - codes : array-like or None, optional - N-length array of integers representing the codes of the path. - If not None, codes must be the same length as vertices. - If None, *vertices* will be treated as a series of line segments. - _interpolation_steps : int, optional - Used as a hint to certain projections, such as Polar, that this - path should be linearly interpolated immediately before drawing. - This attribute is primarily an implementation detail and is not - intended for public use. - closed : bool, optional - If *codes* is None and closed is True, vertices will be treated as - line segments of a closed polygon. Note that the last vertex will - then be ignored (as the corresponding code will be set to - CLOSEPOLY). - readonly : bool, optional - Makes the path behave in an immutable way and sets the vertices - and codes as read-only arrays. - """ - vertices = _to_unmasked_float_array(vertices) - _api.check_shape((None, 2), vertices=vertices) - - if codes is not None: - codes = np.asarray(codes, self.code_type) - if codes.ndim != 1 or len(codes) != len(vertices): - raise ValueError("'codes' must be a 1D list or array with the " - "same length of 'vertices'. " - f"Your vertices have shape {vertices.shape} " - f"but your codes have shape {codes.shape}") - if len(codes) and codes[0] != self.MOVETO: - raise ValueError("The first element of 'code' must be equal " - f"to 'MOVETO' ({self.MOVETO}). " - f"Your first code is {codes[0]}") - elif closed and len(vertices): - codes = np.empty(len(vertices), dtype=self.code_type) - codes[0] = self.MOVETO - codes[1:-1] = self.LINETO - codes[-1] = self.CLOSEPOLY - - self._vertices = vertices - self._codes = codes - self._interpolation_steps = _interpolation_steps - self._update_values() - - if readonly: - self._vertices.flags.writeable = False - if self._codes is not None: - self._codes.flags.writeable = False - self._readonly = True - else: - self._readonly = False - - @classmethod - def _fast_from_codes_and_verts(cls, verts, codes, internals_from=None): - """ - Create a Path instance without the expense of calling the constructor. - - Parameters - ---------- - verts : numpy array - codes : numpy array - internals_from : Path or None - If not None, another `Path` from which the attributes - ``should_simplify``, ``simplify_threshold``, and - ``interpolation_steps`` will be copied. Note that ``readonly`` is - never copied, and always set to ``False`` by this constructor. - """ - pth = cls.__new__(cls) - pth._vertices = _to_unmasked_float_array(verts) - pth._codes = codes - pth._readonly = False - if internals_from is not None: - pth._should_simplify = internals_from._should_simplify - pth._simplify_threshold = internals_from._simplify_threshold - pth._interpolation_steps = internals_from._interpolation_steps - else: - pth._should_simplify = True - pth._simplify_threshold = mpl.rcParams['path.simplify_threshold'] - pth._interpolation_steps = 1 - return pth - - @classmethod - def _create_closed(cls, vertices): - """ - Create a closed polygonal path going through *vertices*. - - Unlike ``Path(..., closed=True)``, *vertices* should **not** end with - an entry for the CLOSEPATH; this entry is added by `._create_closed`. - """ - v = _to_unmasked_float_array(vertices) - return cls(np.concatenate([v, v[:1]]), closed=True) - - def _update_values(self): - self._simplify_threshold = mpl.rcParams['path.simplify_threshold'] - self._should_simplify = ( - self._simplify_threshold > 0 and - mpl.rcParams['path.simplify'] and - len(self._vertices) >= 128 and - (self._codes is None or np.all(self._codes <= Path.LINETO)) - ) - - @property - def vertices(self): - """ - The list of vertices in the `Path` as an Nx2 numpy array. - """ - return self._vertices - - @vertices.setter - def vertices(self, vertices): - if self._readonly: - raise AttributeError("Can't set vertices on a readonly Path") - self._vertices = vertices - self._update_values() - - @property - def codes(self): - """ - The list of codes in the `Path` as a 1D numpy array. Each - code is one of `STOP`, `MOVETO`, `LINETO`, `CURVE3`, `CURVE4` - or `CLOSEPOLY`. For codes that correspond to more than one - vertex (`CURVE3` and `CURVE4`), that code will be repeated so - that the length of `vertices` and `codes` is always - the same. - """ - return self._codes - - @codes.setter - def codes(self, codes): - if self._readonly: - raise AttributeError("Can't set codes on a readonly Path") - self._codes = codes - self._update_values() - - @property - def simplify_threshold(self): - """ - The fraction of a pixel difference below which vertices will - be simplified out. - """ - return self._simplify_threshold - - @simplify_threshold.setter - def simplify_threshold(self, threshold): - self._simplify_threshold = threshold - - @property - def should_simplify(self): - """ - `True` if the vertices array should be simplified. - """ - return self._should_simplify - - @should_simplify.setter - def should_simplify(self, should_simplify): - self._should_simplify = should_simplify - - @property - def readonly(self): - """ - `True` if the `Path` is read-only. - """ - return self._readonly - - def copy(self): - """ - Return a shallow copy of the `Path`, which will share the - vertices and codes with the source `Path`. - """ - return copy.copy(self) - - def __deepcopy__(self, memo=None): - """ - Return a deepcopy of the `Path`. The `Path` will not be - readonly, even if the source `Path` is. - """ - # Deepcopying arrays (vertices, codes) strips the writeable=False flag. - p = copy.deepcopy(super(), memo) - p._readonly = False - return p - - deepcopy = __deepcopy__ - - @classmethod - def make_compound_path_from_polys(cls, XY): - """ - Make a compound `Path` object to draw a number of polygons with equal - numbers of sides. - - .. plot:: gallery/misc/histogram_path.py - - Parameters - ---------- - XY : (numpolys, numsides, 2) array - """ - # for each poly: 1 for the MOVETO, (numsides-1) for the LINETO, 1 for - # the CLOSEPOLY; the vert for the closepoly is ignored but we still - # need it to keep the codes aligned with the vertices - numpolys, numsides, two = XY.shape - if two != 2: - raise ValueError("The third dimension of 'XY' must be 2") - stride = numsides + 1 - nverts = numpolys * stride - verts = np.zeros((nverts, 2)) - codes = np.full(nverts, cls.LINETO, dtype=cls.code_type) - codes[0::stride] = cls.MOVETO - codes[numsides::stride] = cls.CLOSEPOLY - for i in range(numsides): - verts[i::stride] = XY[:, i] - return cls(verts, codes) - - @classmethod - def make_compound_path(cls, *args): - """ - Make a compound path from a list of `Path` objects. Blindly removes - all `Path.STOP` control points. - """ - # Handle an empty list in args (i.e. no args). - if not args: - return Path(np.empty([0, 2], dtype=np.float32)) - vertices = np.concatenate([x.vertices for x in args]) - codes = np.empty(len(vertices), dtype=cls.code_type) - i = 0 - for path in args: - if path.codes is None: - codes[i] = cls.MOVETO - codes[i + 1:i + len(path.vertices)] = cls.LINETO - else: - codes[i:i + len(path.codes)] = path.codes - i += len(path.vertices) - # remove STOP's, since internal STOPs are a bug - not_stop_mask = codes != cls.STOP - vertices = vertices[not_stop_mask, :] - codes = codes[not_stop_mask] - - return cls(vertices, codes) - - def __repr__(self): - return "Path(%r, %r)" % (self.vertices, self.codes) - - def __len__(self): - return len(self.vertices) - - def iter_segments(self, transform=None, remove_nans=True, clip=None, - snap=False, stroke_width=1.0, simplify=None, - curves=True, sketch=None): - """ - Iterate over all curve segments in the path. - - Each iteration returns a pair ``(vertices, code)``, where ``vertices`` - is a sequence of 1-3 coordinate pairs, and ``code`` is a `Path` code. - - Additionally, this method can provide a number of standard cleanups and - conversions to the path. - - Parameters - ---------- - transform : None or :class:`~matplotlib.transforms.Transform` - If not None, the given affine transformation will be applied to the - path. - remove_nans : bool, optional - Whether to remove all NaNs from the path and skip over them using - MOVETO commands. - clip : None or (float, float, float, float), optional - If not None, must be a four-tuple (x1, y1, x2, y2) - defining a rectangle in which to clip the path. - snap : None or bool, optional - If True, snap all nodes to pixels; if False, don't snap them. - If None, snap if the path contains only segments - parallel to the x or y axes, and no more than 1024 of them. - stroke_width : float, optional - The width of the stroke being drawn (used for path snapping). - simplify : None or bool, optional - Whether to simplify the path by removing vertices - that do not affect its appearance. If None, use the - :attr:`should_simplify` attribute. See also :rc:`path.simplify` - and :rc:`path.simplify_threshold`. - curves : bool, optional - If True, curve segments will be returned as curve segments. - If False, all curves will be converted to line segments. - sketch : None or sequence, optional - If not None, must be a 3-tuple of the form - (scale, length, randomness), representing the sketch parameters. - """ - if not len(self): - return - - cleaned = self.cleaned(transform=transform, - remove_nans=remove_nans, clip=clip, - snap=snap, stroke_width=stroke_width, - simplify=simplify, curves=curves, - sketch=sketch) - - # Cache these object lookups for performance in the loop. - NUM_VERTICES_FOR_CODE = self.NUM_VERTICES_FOR_CODE - STOP = self.STOP - - vertices = iter(cleaned.vertices) - codes = iter(cleaned.codes) - for curr_vertices, code in zip(vertices, codes): - if code == STOP: - break - extra_vertices = NUM_VERTICES_FOR_CODE[code] - 1 - if extra_vertices: - for i in range(extra_vertices): - next(codes) - curr_vertices = np.append(curr_vertices, next(vertices)) - yield curr_vertices, code - - def iter_bezier(self, **kwargs): - """ - Iterate over each Bézier curve (lines included) in a Path. - - Parameters - ---------- - **kwargs - Forwarded to `.iter_segments`. - - Yields - ------ - B : matplotlib.bezier.BezierSegment - The Bézier curves that make up the current path. Note in particular - that freestanding points are Bézier curves of order 0, and lines - are Bézier curves of order 1 (with two control points). - code : Path.code_type - The code describing what kind of curve is being returned. - Path.MOVETO, Path.LINETO, Path.CURVE3, Path.CURVE4 correspond to - Bézier curves with 1, 2, 3, and 4 control points (respectively). - Path.CLOSEPOLY is a Path.LINETO with the control points correctly - chosen based on the start/end points of the current stroke. - """ - first_vert = None - prev_vert = None - for verts, code in self.iter_segments(**kwargs): - if first_vert is None: - if code != Path.MOVETO: - raise ValueError("Malformed path, must start with MOVETO.") - if code == Path.MOVETO: # a point is like "CURVE1" - first_vert = verts - yield BezierSegment(np.array([first_vert])), code - elif code == Path.LINETO: # "CURVE2" - yield BezierSegment(np.array([prev_vert, verts])), code - elif code == Path.CURVE3: - yield BezierSegment(np.array([prev_vert, verts[:2], - verts[2:]])), code - elif code == Path.CURVE4: - yield BezierSegment(np.array([prev_vert, verts[:2], - verts[2:4], verts[4:]])), code - elif code == Path.CLOSEPOLY: - yield BezierSegment(np.array([prev_vert, first_vert])), code - elif code == Path.STOP: - return - else: - raise ValueError(f"Invalid Path.code_type: {code}") - prev_vert = verts[-2:] - - def cleaned(self, transform=None, remove_nans=False, clip=None, - *, simplify=False, curves=False, - stroke_width=1.0, snap=False, sketch=None): - """ - Return a new Path with vertices and codes cleaned according to the - parameters. - - See Also - -------- - Path.iter_segments : for details of the keyword arguments. - """ - vertices, codes = _path.cleanup_path( - self, transform, remove_nans, clip, snap, stroke_width, simplify, - curves, sketch) - pth = Path._fast_from_codes_and_verts(vertices, codes, self) - if not simplify: - pth._should_simplify = False - return pth - - def transformed(self, transform): - """ - Return a transformed copy of the path. - - See Also - -------- - matplotlib.transforms.TransformedPath - A specialized path class that will cache the transformed result and - automatically update when the transform changes. - """ - return Path(transform.transform(self.vertices), self.codes, - self._interpolation_steps) - - def contains_point(self, point, transform=None, radius=0.0): - """ - Return whether the area enclosed by the path contains the given point. - - The path is always treated as closed; i.e. if the last code is not - CLOSEPOLY an implicit segment connecting the last vertex to the first - vertex is assumed. - - Parameters - ---------- - point : (float, float) - The point (x, y) to check. - transform : `matplotlib.transforms.Transform`, optional - If not ``None``, *point* will be compared to ``self`` transformed - by *transform*; i.e. for a correct check, *transform* should - transform the path into the coordinate system of *point*. - radius : float, default: 0 - Additional margin on the path in coordinates of *point*. - The path is extended tangentially by *radius/2*; i.e. if you would - draw the path with a linewidth of *radius*, all points on the line - would still be considered to be contained in the area. Conversely, - negative values shrink the area: Points on the imaginary line - will be considered outside the area. - - Returns - ------- - bool - - Notes - ----- - The current algorithm has some limitations: - - - The result is undefined for points exactly at the boundary - (i.e. at the path shifted by *radius/2*). - - The result is undefined if there is no enclosed area, i.e. all - vertices are on a straight line. - - If bounding lines start to cross each other due to *radius* shift, - the result is not guaranteed to be correct. - """ - if transform is not None: - transform = transform.frozen() - # `point_in_path` does not handle nonlinear transforms, so we - # transform the path ourselves. If *transform* is affine, letting - # `point_in_path` handle the transform avoids allocating an extra - # buffer. - if transform and not transform.is_affine: - self = transform.transform_path(self) - transform = None - return _path.point_in_path(point[0], point[1], radius, self, transform) - - def contains_points(self, points, transform=None, radius=0.0): - """ - Return whether the area enclosed by the path contains the given points. - - The path is always treated as closed; i.e. if the last code is not - CLOSEPOLY an implicit segment connecting the last vertex to the first - vertex is assumed. - - Parameters - ---------- - points : (N, 2) array - The points to check. Columns contain x and y values. - transform : `matplotlib.transforms.Transform`, optional - If not ``None``, *points* will be compared to ``self`` transformed - by *transform*; i.e. for a correct check, *transform* should - transform the path into the coordinate system of *points*. - radius : float, default: 0 - Additional margin on the path in coordinates of *points*. - The path is extended tangentially by *radius/2*; i.e. if you would - draw the path with a linewidth of *radius*, all points on the line - would still be considered to be contained in the area. Conversely, - negative values shrink the area: Points on the imaginary line - will be considered outside the area. - - Returns - ------- - length-N bool array - - Notes - ----- - The current algorithm has some limitations: - - - The result is undefined for points exactly at the boundary - (i.e. at the path shifted by *radius/2*). - - The result is undefined if there is no enclosed area, i.e. all - vertices are on a straight line. - - If bounding lines start to cross each other due to *radius* shift, - the result is not guaranteed to be correct. - """ - if transform is not None: - transform = transform.frozen() - result = _path.points_in_path(points, radius, self, transform) - return result.astype('bool') - - def contains_path(self, path, transform=None): - """ - Return whether this (closed) path completely contains the given path. - - If *transform* is not ``None``, the path will be transformed before - checking for containment. - """ - if transform is not None: - transform = transform.frozen() - return _path.path_in_path(self, None, path, transform) - - def get_extents(self, transform=None, **kwargs): - """ - Get Bbox of the path. - - Parameters - ---------- - transform : matplotlib.transforms.Transform, optional - Transform to apply to path before computing extents, if any. - **kwargs - Forwarded to `.iter_bezier`. - - Returns - ------- - matplotlib.transforms.Bbox - The extents of the path Bbox([[xmin, ymin], [xmax, ymax]]) - """ - from .transforms import Bbox - if transform is not None: - self = transform.transform_path(self) - if self.codes is None: - xys = self.vertices - elif len(np.intersect1d(self.codes, [Path.CURVE3, Path.CURVE4])) == 0: - # Optimization for the straight line case. - # Instead of iterating through each curve, consider - # each line segment's end-points - # (recall that STOP and CLOSEPOLY vertices are ignored) - xys = self.vertices[np.isin(self.codes, - [Path.MOVETO, Path.LINETO])] - else: - xys = [] - for curve, code in self.iter_bezier(**kwargs): - # places where the derivative is zero can be extrema - _, dzeros = curve.axis_aligned_extrema() - # as can the ends of the curve - xys.append(curve([0, *dzeros, 1])) - xys = np.concatenate(xys) - if len(xys): - return Bbox([xys.min(axis=0), xys.max(axis=0)]) - else: - return Bbox.null() - - def intersects_path(self, other, filled=True): - """ - Return whether if this path intersects another given path. - - If *filled* is True, then this also returns True if one path completely - encloses the other (i.e., the paths are treated as filled). - """ - return _path.path_intersects_path(self, other, filled) - - def intersects_bbox(self, bbox, filled=True): - """ - Return whether this path intersects a given `~.transforms.Bbox`. - - If *filled* is True, then this also returns True if the path completely - encloses the `.Bbox` (i.e., the path is treated as filled). - - The bounding box is always considered filled. - """ - return _path.path_intersects_rectangle( - self, bbox.x0, bbox.y0, bbox.x1, bbox.y1, filled) - - def interpolated(self, steps): - """ - Return a new path resampled to length N x steps. - - Codes other than LINETO are not handled correctly. - """ - if steps == 1: - return self - - vertices = simple_linear_interpolation(self.vertices, steps) - codes = self.codes - if codes is not None: - new_codes = np.full((len(codes) - 1) * steps + 1, Path.LINETO, - dtype=self.code_type) - new_codes[0::steps] = codes - else: - new_codes = None - return Path(vertices, new_codes) - - def to_polygons(self, transform=None, width=0, height=0, closed_only=True): - """ - Convert this path to a list of polygons or polylines. Each - polygon/polyline is an Nx2 array of vertices. In other words, - each polygon has no ``MOVETO`` instructions or curves. This - is useful for displaying in backends that do not support - compound paths or Bézier curves. - - If *width* and *height* are both non-zero then the lines will - be simplified so that vertices outside of (0, 0), (width, - height) will be clipped. - - If *closed_only* is `True` (default), only closed polygons, - with the last point being the same as the first point, will be - returned. Any unclosed polylines in the path will be - explicitly closed. If *closed_only* is `False`, any unclosed - polygons in the path will be returned as unclosed polygons, - and the closed polygons will be returned explicitly closed by - setting the last point to the same as the first point. - """ - if len(self.vertices) == 0: - return [] - - if transform is not None: - transform = transform.frozen() - - if self.codes is None and (width == 0 or height == 0): - vertices = self.vertices - if closed_only: - if len(vertices) < 3: - return [] - elif np.any(vertices[0] != vertices[-1]): - vertices = [*vertices, vertices[0]] - - if transform is None: - return [vertices] - else: - return [transform.transform(vertices)] - - # Deal with the case where there are curves and/or multiple - # subpaths (using extension code) - return _path.convert_path_to_polygons( - self, transform, width, height, closed_only) - - _unit_rectangle = None - - @classmethod - def unit_rectangle(cls): - """ - Return a `Path` instance of the unit rectangle from (0, 0) to (1, 1). - """ - if cls._unit_rectangle is None: - cls._unit_rectangle = cls([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]], - closed=True, readonly=True) - return cls._unit_rectangle - - _unit_regular_polygons = WeakValueDictionary() - - @classmethod - def unit_regular_polygon(cls, numVertices): - """ - Return a :class:`Path` instance for a unit regular polygon with the - given *numVertices* such that the circumscribing circle has radius 1.0, - centered at (0, 0). - """ - if numVertices <= 16: - path = cls._unit_regular_polygons.get(numVertices) - else: - path = None - if path is None: - theta = ((2 * np.pi / numVertices) * np.arange(numVertices + 1) - # This initial rotation is to make sure the polygon always - # "points-up". - + np.pi / 2) - verts = np.column_stack((np.cos(theta), np.sin(theta))) - path = cls(verts, closed=True, readonly=True) - if numVertices <= 16: - cls._unit_regular_polygons[numVertices] = path - return path - - _unit_regular_stars = WeakValueDictionary() - - @classmethod - def unit_regular_star(cls, numVertices, innerCircle=0.5): - """ - Return a :class:`Path` for a unit regular star with the given - numVertices and radius of 1.0, centered at (0, 0). - """ - if numVertices <= 16: - path = cls._unit_regular_stars.get((numVertices, innerCircle)) - else: - path = None - if path is None: - ns2 = numVertices * 2 - theta = (2*np.pi/ns2 * np.arange(ns2 + 1)) - # This initial rotation is to make sure the polygon always - # "points-up" - theta += np.pi / 2.0 - r = np.ones(ns2 + 1) - r[1::2] = innerCircle - verts = (r * np.vstack((np.cos(theta), np.sin(theta)))).T - path = cls(verts, closed=True, readonly=True) - if numVertices <= 16: - cls._unit_regular_stars[(numVertices, innerCircle)] = path - return path - - @classmethod - def unit_regular_asterisk(cls, numVertices): - """ - Return a :class:`Path` for a unit regular asterisk with the given - numVertices and radius of 1.0, centered at (0, 0). - """ - return cls.unit_regular_star(numVertices, 0.0) - - _unit_circle = None - - @classmethod - def unit_circle(cls): - """ - Return the readonly :class:`Path` of the unit circle. - - For most cases, :func:`Path.circle` will be what you want. - """ - if cls._unit_circle is None: - cls._unit_circle = cls.circle(center=(0, 0), radius=1, - readonly=True) - return cls._unit_circle - - @classmethod - def circle(cls, center=(0., 0.), radius=1., readonly=False): - """ - Return a `Path` representing a circle of a given radius and center. - - Parameters - ---------- - center : (float, float), default: (0, 0) - The center of the circle. - radius : float, default: 1 - The radius of the circle. - readonly : bool - Whether the created path should have the "readonly" argument - set when creating the Path instance. - - Notes - ----- - The circle is approximated using 8 cubic Bézier curves, as described in - - Lancaster, Don. `Approximating a Circle or an Ellipse Using Four - Bezier Cubic Splines `_. - """ - MAGIC = 0.2652031 - SQRTHALF = np.sqrt(0.5) - MAGIC45 = SQRTHALF * MAGIC - - vertices = np.array([[0.0, -1.0], - - [MAGIC, -1.0], - [SQRTHALF-MAGIC45, -SQRTHALF-MAGIC45], - [SQRTHALF, -SQRTHALF], - - [SQRTHALF+MAGIC45, -SQRTHALF+MAGIC45], - [1.0, -MAGIC], - [1.0, 0.0], - - [1.0, MAGIC], - [SQRTHALF+MAGIC45, SQRTHALF-MAGIC45], - [SQRTHALF, SQRTHALF], - - [SQRTHALF-MAGIC45, SQRTHALF+MAGIC45], - [MAGIC, 1.0], - [0.0, 1.0], - - [-MAGIC, 1.0], - [-SQRTHALF+MAGIC45, SQRTHALF+MAGIC45], - [-SQRTHALF, SQRTHALF], - - [-SQRTHALF-MAGIC45, SQRTHALF-MAGIC45], - [-1.0, MAGIC], - [-1.0, 0.0], - - [-1.0, -MAGIC], - [-SQRTHALF-MAGIC45, -SQRTHALF+MAGIC45], - [-SQRTHALF, -SQRTHALF], - - [-SQRTHALF+MAGIC45, -SQRTHALF-MAGIC45], - [-MAGIC, -1.0], - [0.0, -1.0], - - [0.0, -1.0]], - dtype=float) - - codes = [cls.CURVE4] * 26 - codes[0] = cls.MOVETO - codes[-1] = cls.CLOSEPOLY - return Path(vertices * radius + center, codes, readonly=readonly) - - _unit_circle_righthalf = None - - @classmethod - def unit_circle_righthalf(cls): - """ - Return a `Path` of the right half of a unit circle. - - See `Path.circle` for the reference on the approximation used. - """ - if cls._unit_circle_righthalf is None: - MAGIC = 0.2652031 - SQRTHALF = np.sqrt(0.5) - MAGIC45 = SQRTHALF * MAGIC - - vertices = np.array( - [[0.0, -1.0], - - [MAGIC, -1.0], - [SQRTHALF-MAGIC45, -SQRTHALF-MAGIC45], - [SQRTHALF, -SQRTHALF], - - [SQRTHALF+MAGIC45, -SQRTHALF+MAGIC45], - [1.0, -MAGIC], - [1.0, 0.0], - - [1.0, MAGIC], - [SQRTHALF+MAGIC45, SQRTHALF-MAGIC45], - [SQRTHALF, SQRTHALF], - - [SQRTHALF-MAGIC45, SQRTHALF+MAGIC45], - [MAGIC, 1.0], - [0.0, 1.0], - - [0.0, -1.0]], - - float) - - codes = np.full(14, cls.CURVE4, dtype=cls.code_type) - codes[0] = cls.MOVETO - codes[-1] = cls.CLOSEPOLY - - cls._unit_circle_righthalf = cls(vertices, codes, readonly=True) - return cls._unit_circle_righthalf - - @classmethod - def arc(cls, theta1, theta2, n=None, is_wedge=False): - """ - Return a `Path` for the unit circle arc from angles *theta1* to - *theta2* (in degrees). - - *theta2* is unwrapped to produce the shortest arc within 360 degrees. - That is, if *theta2* > *theta1* + 360, the arc will be from *theta1* to - *theta2* - 360 and not a full circle plus some extra overlap. - - If *n* is provided, it is the number of spline segments to make. - If *n* is not provided, the number of spline segments is - determined based on the delta between *theta1* and *theta2*. - - Masionobe, L. 2003. `Drawing an elliptical arc using - polylines, quadratic or cubic Bezier curves - `_. - """ - halfpi = np.pi * 0.5 - - eta1 = theta1 - eta2 = theta2 - 360 * np.floor((theta2 - theta1) / 360) - # Ensure 2pi range is not flattened to 0 due to floating-point errors, - # but don't try to expand existing 0 range. - if theta2 != theta1 and eta2 <= eta1: - eta2 += 360 - eta1, eta2 = np.deg2rad([eta1, eta2]) - - # number of curve segments to make - if n is None: - n = int(2 ** np.ceil((eta2 - eta1) / halfpi)) - if n < 1: - raise ValueError("n must be >= 1 or None") - - deta = (eta2 - eta1) / n - t = np.tan(0.5 * deta) - alpha = np.sin(deta) * (np.sqrt(4.0 + 3.0 * t * t) - 1) / 3.0 - - steps = np.linspace(eta1, eta2, n + 1, True) - cos_eta = np.cos(steps) - sin_eta = np.sin(steps) - - xA = cos_eta[:-1] - yA = sin_eta[:-1] - xA_dot = -yA - yA_dot = xA - - xB = cos_eta[1:] - yB = sin_eta[1:] - xB_dot = -yB - yB_dot = xB - - if is_wedge: - length = n * 3 + 4 - vertices = np.zeros((length, 2), float) - codes = np.full(length, cls.CURVE4, dtype=cls.code_type) - vertices[1] = [xA[0], yA[0]] - codes[0:2] = [cls.MOVETO, cls.LINETO] - codes[-2:] = [cls.LINETO, cls.CLOSEPOLY] - vertex_offset = 2 - end = length - 2 - else: - length = n * 3 + 1 - vertices = np.empty((length, 2), float) - codes = np.full(length, cls.CURVE4, dtype=cls.code_type) - vertices[0] = [xA[0], yA[0]] - codes[0] = cls.MOVETO - vertex_offset = 1 - end = length - - vertices[vertex_offset:end:3, 0] = xA + alpha * xA_dot - vertices[vertex_offset:end:3, 1] = yA + alpha * yA_dot - vertices[vertex_offset+1:end:3, 0] = xB - alpha * xB_dot - vertices[vertex_offset+1:end:3, 1] = yB - alpha * yB_dot - vertices[vertex_offset+2:end:3, 0] = xB - vertices[vertex_offset+2:end:3, 1] = yB - - return cls(vertices, codes, readonly=True) - - @classmethod - def wedge(cls, theta1, theta2, n=None): - """ - Return a `Path` for the unit circle wedge from angles *theta1* to - *theta2* (in degrees). - - *theta2* is unwrapped to produce the shortest wedge within 360 degrees. - That is, if *theta2* > *theta1* + 360, the wedge will be from *theta1* - to *theta2* - 360 and not a full circle plus some extra overlap. - - If *n* is provided, it is the number of spline segments to make. - If *n* is not provided, the number of spline segments is - determined based on the delta between *theta1* and *theta2*. - - See `Path.arc` for the reference on the approximation used. - """ - return cls.arc(theta1, theta2, n, True) - - @staticmethod - @lru_cache(8) - def hatch(hatchpattern, density=6): - """ - Given a hatch specifier, *hatchpattern*, generates a Path that - can be used in a repeated hatching pattern. *density* is the - number of lines per unit square. - """ - from matplotlib.hatch import get_path - return (get_path(hatchpattern, density) - if hatchpattern is not None else None) - - def clip_to_bbox(self, bbox, inside=True): - """ - Clip the path to the given bounding box. - - The path must be made up of one or more closed polygons. This - algorithm will not behave correctly for unclosed paths. - - If *inside* is `True`, clip to the inside of the box, otherwise - to the outside of the box. - """ - verts = _path.clip_path_to_rect(self, bbox, inside) - paths = [Path(poly) for poly in verts] - return self.make_compound_path(*paths) - - -def get_path_collection_extents( - master_transform, paths, transforms, offsets, offset_transform): - r""" - Get bounding box of a `.PathCollection`\s internal objects. - - That is, given a sequence of `Path`\s, `.Transform`\s objects, and offsets, as found - in a `.PathCollection`, return the bounding box that encapsulates all of them. - - Parameters - ---------- - master_transform : `.Transform` - Global transformation applied to all paths. - paths : list of `Path` - transforms : list of `.Affine2D` - offsets : (N, 2) array-like - offset_transform : `.Affine2D` - Transform applied to the offsets before offsetting the path. - - Notes - ----- - The way that *paths*, *transforms* and *offsets* are combined follows the same - method as for collections: each is iterated over independently, so if you have 3 - paths (A, B, C), 2 transforms (α, β) and 1 offset (O), their combinations are as - follows: - - - (A, α, O) - - (B, β, O) - - (C, α, O) - """ - from .transforms import Bbox - if len(paths) == 0: - raise ValueError("No paths provided") - extents, minpos = _path.get_path_collection_extents( - master_transform, paths, np.atleast_3d(transforms), - offsets, offset_transform) - return Bbox.from_extents(*extents, minpos=minpos) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/patheffects.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/patheffects.py deleted file mode 100644 index de72d7c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/patheffects.py +++ /dev/null @@ -1,513 +0,0 @@ -""" -Defines classes for path effects. The path effects are supported in `.Text`, -`.Line2D` and `.Patch`. - -.. seealso:: - :doc:`/tutorials/advanced/patheffects_guide` -""" - -from matplotlib.backend_bases import RendererBase -from matplotlib import colors as mcolors -from matplotlib import patches as mpatches -from matplotlib import transforms as mtransforms -from matplotlib.path import Path -import numpy as np - - -class AbstractPathEffect: - """ - A base class for path effects. - - Subclasses should override the ``draw_path`` method to add effect - functionality. - """ - - def __init__(self, offset=(0., 0.)): - """ - Parameters - ---------- - offset : (float, float), default: (0, 0) - The (x, y) offset to apply to the path, measured in points. - """ - self._offset = offset - - def _offset_transform(self, renderer): - """Apply the offset to the given transform.""" - return mtransforms.Affine2D().translate( - *map(renderer.points_to_pixels, self._offset)) - - def _update_gc(self, gc, new_gc_dict): - """ - Update the given GraphicsContext with the given dict of properties. - - The keys in the dictionary are used to identify the appropriate - ``set_`` method on the *gc*. - """ - new_gc_dict = new_gc_dict.copy() - - dashes = new_gc_dict.pop("dashes", None) - if dashes: - gc.set_dashes(**dashes) - - for k, v in new_gc_dict.items(): - set_method = getattr(gc, 'set_' + k, None) - if not callable(set_method): - raise AttributeError('Unknown property {0}'.format(k)) - set_method(v) - return gc - - def draw_path(self, renderer, gc, tpath, affine, rgbFace=None): - """ - Derived should override this method. The arguments are the same - as :meth:`matplotlib.backend_bases.RendererBase.draw_path` - except the first argument is a renderer. - """ - # Get the real renderer, not a PathEffectRenderer. - if isinstance(renderer, PathEffectRenderer): - renderer = renderer._renderer - return renderer.draw_path(gc, tpath, affine, rgbFace) - - -class PathEffectRenderer(RendererBase): - """ - Implements a Renderer which contains another renderer. - - This proxy then intercepts draw calls, calling the appropriate - :class:`AbstractPathEffect` draw method. - - .. note:: - Not all methods have been overridden on this RendererBase subclass. - It may be necessary to add further methods to extend the PathEffects - capabilities further. - """ - - def __init__(self, path_effects, renderer): - """ - Parameters - ---------- - path_effects : iterable of :class:`AbstractPathEffect` - The path effects which this renderer represents. - renderer : `~matplotlib.backend_bases.RendererBase` subclass - - """ - self._path_effects = path_effects - self._renderer = renderer - - def copy_with_path_effect(self, path_effects): - return self.__class__(path_effects, self._renderer) - - def draw_path(self, gc, tpath, affine, rgbFace=None): - for path_effect in self._path_effects: - path_effect.draw_path(self._renderer, gc, tpath, affine, - rgbFace) - - def draw_markers( - self, gc, marker_path, marker_trans, path, *args, **kwargs): - # We do a little shimmy so that all markers are drawn for each path - # effect in turn. Essentially, we induce recursion (depth 1) which is - # terminated once we have just a single path effect to work with. - if len(self._path_effects) == 1: - # Call the base path effect function - this uses the unoptimised - # approach of calling "draw_path" multiple times. - return super().draw_markers(gc, marker_path, marker_trans, path, - *args, **kwargs) - - for path_effect in self._path_effects: - renderer = self.copy_with_path_effect([path_effect]) - # Recursively call this method, only next time we will only have - # one path effect. - renderer.draw_markers(gc, marker_path, marker_trans, path, - *args, **kwargs) - - def draw_path_collection(self, gc, master_transform, paths, *args, - **kwargs): - # We do a little shimmy so that all paths are drawn for each path - # effect in turn. Essentially, we induce recursion (depth 1) which is - # terminated once we have just a single path effect to work with. - if len(self._path_effects) == 1: - # Call the base path effect function - this uses the unoptimised - # approach of calling "draw_path" multiple times. - return super().draw_path_collection(gc, master_transform, paths, - *args, **kwargs) - - for path_effect in self._path_effects: - renderer = self.copy_with_path_effect([path_effect]) - # Recursively call this method, only next time we will only have - # one path effect. - renderer.draw_path_collection(gc, master_transform, paths, - *args, **kwargs) - - def _draw_text_as_path(self, gc, x, y, s, prop, angle, ismath): - # Implements the naive text drawing as is found in RendererBase. - path, transform = self._get_text_path_transform(x, y, s, prop, - angle, ismath) - color = gc.get_rgb() - gc.set_linewidth(0.0) - self.draw_path(gc, path, transform, rgbFace=color) - - def __getattribute__(self, name): - if name in ['flipy', 'get_canvas_width_height', 'new_gc', - 'points_to_pixels', '_text2path', 'height', 'width']: - return getattr(self._renderer, name) - else: - return object.__getattribute__(self, name) - - -class Normal(AbstractPathEffect): - """ - The "identity" PathEffect. - - The Normal PathEffect's sole purpose is to draw the original artist with - no special path effect. - """ - - -def _subclass_with_normal(effect_class): - """ - Create a PathEffect class combining *effect_class* and a normal draw. - """ - - class withEffect(effect_class): - def draw_path(self, renderer, gc, tpath, affine, rgbFace): - super().draw_path(renderer, gc, tpath, affine, rgbFace) - renderer.draw_path(gc, tpath, affine, rgbFace) - - withEffect.__name__ = f"with{effect_class.__name__}" - withEffect.__qualname__ = f"with{effect_class.__name__}" - withEffect.__doc__ = f""" - A shortcut PathEffect for applying `.{effect_class.__name__}` and then - drawing the original Artist. - - With this class you can use :: - - artist.set_path_effects([path_effects.with{effect_class.__name__}()]) - - as a shortcut for :: - - artist.set_path_effects([path_effects.{effect_class.__name__}(), - path_effects.Normal()]) - """ - # Docstring inheritance doesn't work for locally-defined subclasses. - withEffect.draw_path.__doc__ = effect_class.draw_path.__doc__ - return withEffect - - -class Stroke(AbstractPathEffect): - """A line based PathEffect which re-draws a stroke.""" - - def __init__(self, offset=(0, 0), **kwargs): - """ - The path will be stroked with its gc updated with the given - keyword arguments, i.e., the keyword arguments should be valid - gc parameter values. - """ - super().__init__(offset) - self._gc = kwargs - - def draw_path(self, renderer, gc, tpath, affine, rgbFace): - """Draw the path with updated gc.""" - gc0 = renderer.new_gc() # Don't modify gc, but a copy! - gc0.copy_properties(gc) - gc0 = self._update_gc(gc0, self._gc) - renderer.draw_path( - gc0, tpath, affine + self._offset_transform(renderer), rgbFace) - gc0.restore() - - -withStroke = _subclass_with_normal(effect_class=Stroke) - - -class SimplePatchShadow(AbstractPathEffect): - """A simple shadow via a filled patch.""" - - def __init__(self, offset=(2, -2), - shadow_rgbFace=None, alpha=None, - rho=0.3, **kwargs): - """ - Parameters - ---------- - offset : (float, float), default: (2, -2) - The (x, y) offset of the shadow in points. - shadow_rgbFace : color - The shadow color. - alpha : float, default: 0.3 - The alpha transparency of the created shadow patch. - rho : float, default: 0.3 - A scale factor to apply to the rgbFace color if *shadow_rgbFace* - is not specified. - **kwargs - Extra keywords are stored and passed through to - :meth:`AbstractPathEffect._update_gc`. - - """ - super().__init__(offset) - - if shadow_rgbFace is None: - self._shadow_rgbFace = shadow_rgbFace - else: - self._shadow_rgbFace = mcolors.to_rgba(shadow_rgbFace) - - if alpha is None: - alpha = 0.3 - - self._alpha = alpha - self._rho = rho - - #: The dictionary of keywords to update the graphics collection with. - self._gc = kwargs - - def draw_path(self, renderer, gc, tpath, affine, rgbFace): - """ - Overrides the standard draw_path to add the shadow offset and - necessary color changes for the shadow. - """ - gc0 = renderer.new_gc() # Don't modify gc, but a copy! - gc0.copy_properties(gc) - - if self._shadow_rgbFace is None: - r, g, b = (rgbFace or (1., 1., 1.))[:3] - # Scale the colors by a factor to improve the shadow effect. - shadow_rgbFace = (r * self._rho, g * self._rho, b * self._rho) - else: - shadow_rgbFace = self._shadow_rgbFace - - gc0.set_foreground("none") - gc0.set_alpha(self._alpha) - gc0.set_linewidth(0) - - gc0 = self._update_gc(gc0, self._gc) - renderer.draw_path( - gc0, tpath, affine + self._offset_transform(renderer), - shadow_rgbFace) - gc0.restore() - - -withSimplePatchShadow = _subclass_with_normal(effect_class=SimplePatchShadow) - - -class SimpleLineShadow(AbstractPathEffect): - """A simple shadow via a line.""" - - def __init__(self, offset=(2, -2), - shadow_color='k', alpha=0.3, rho=0.3, **kwargs): - """ - Parameters - ---------- - offset : (float, float), default: (2, -2) - The (x, y) offset to apply to the path, in points. - shadow_color : color, default: 'black' - The shadow color. - A value of ``None`` takes the original artist's color - with a scale factor of *rho*. - alpha : float, default: 0.3 - The alpha transparency of the created shadow patch. - rho : float, default: 0.3 - A scale factor to apply to the rgbFace color if *shadow_color* - is ``None``. - **kwargs - Extra keywords are stored and passed through to - :meth:`AbstractPathEffect._update_gc`. - """ - super().__init__(offset) - if shadow_color is None: - self._shadow_color = shadow_color - else: - self._shadow_color = mcolors.to_rgba(shadow_color) - self._alpha = alpha - self._rho = rho - #: The dictionary of keywords to update the graphics collection with. - self._gc = kwargs - - def draw_path(self, renderer, gc, tpath, affine, rgbFace): - """ - Overrides the standard draw_path to add the shadow offset and - necessary color changes for the shadow. - """ - gc0 = renderer.new_gc() # Don't modify gc, but a copy! - gc0.copy_properties(gc) - - if self._shadow_color is None: - r, g, b = (gc0.get_foreground() or (1., 1., 1.))[:3] - # Scale the colors by a factor to improve the shadow effect. - shadow_rgbFace = (r * self._rho, g * self._rho, b * self._rho) - else: - shadow_rgbFace = self._shadow_color - - gc0.set_foreground(shadow_rgbFace) - gc0.set_alpha(self._alpha) - - gc0 = self._update_gc(gc0, self._gc) - renderer.draw_path( - gc0, tpath, affine + self._offset_transform(renderer)) - gc0.restore() - - -class PathPatchEffect(AbstractPathEffect): - """ - Draws a `.PathPatch` instance whose Path comes from the original - PathEffect artist. - """ - - def __init__(self, offset=(0, 0), **kwargs): - """ - Parameters - ---------- - offset : (float, float), default: (0, 0) - The (x, y) offset to apply to the path, in points. - **kwargs - All keyword arguments are passed through to the - :class:`~matplotlib.patches.PathPatch` constructor. The - properties which cannot be overridden are "path", "clip_box" - "transform" and "clip_path". - """ - super().__init__(offset=offset) - self.patch = mpatches.PathPatch([], **kwargs) - - def draw_path(self, renderer, gc, tpath, affine, rgbFace): - self.patch._path = tpath - self.patch.set_transform(affine + self._offset_transform(renderer)) - self.patch.set_clip_box(gc.get_clip_rectangle()) - clip_path = gc.get_clip_path() - if clip_path: - self.patch.set_clip_path(*clip_path) - self.patch.draw(renderer) - - -class TickedStroke(AbstractPathEffect): - """ - A line-based PathEffect which draws a path with a ticked style. - - This line style is frequently used to represent constraints in - optimization. The ticks may be used to indicate that one side - of the line is invalid or to represent a closed boundary of a - domain (i.e. a wall or the edge of a pipe). - - The spacing, length, and angle of ticks can be controlled. - - This line style is sometimes referred to as a hatched line. - - See also the :doc:`/gallery/misc/tickedstroke_demo` example. - """ - - def __init__(self, offset=(0, 0), - spacing=10.0, angle=45.0, length=np.sqrt(2), - **kwargs): - """ - Parameters - ---------- - offset : (float, float), default: (0, 0) - The (x, y) offset to apply to the path, in points. - spacing : float, default: 10.0 - The spacing between ticks in points. - angle : float, default: 45.0 - The angle between the path and the tick in degrees. The angle - is measured as if you were an ant walking along the curve, with - zero degrees pointing directly ahead, 90 to your left, -90 - to your right, and 180 behind you. To change side of the ticks, - change sign of the angle. - length : float, default: 1.414 - The length of the tick relative to spacing. - Recommended length = 1.414 (sqrt(2)) when angle=45, length=1.0 - when angle=90 and length=2.0 when angle=60. - **kwargs - Extra keywords are stored and passed through to - :meth:`AbstractPathEffect._update_gc`. - - Examples - -------- - See :doc:`/gallery/misc/tickedstroke_demo`. - """ - super().__init__(offset) - - self._spacing = spacing - self._angle = angle - self._length = length - self._gc = kwargs - - def draw_path(self, renderer, gc, tpath, affine, rgbFace): - """Draw the path with updated gc.""" - # Do not modify the input! Use copy instead. - gc0 = renderer.new_gc() - gc0.copy_properties(gc) - - gc0 = self._update_gc(gc0, self._gc) - trans = affine + self._offset_transform(renderer) - - theta = -np.radians(self._angle) - trans_matrix = np.array([[np.cos(theta), -np.sin(theta)], - [np.sin(theta), np.cos(theta)]]) - - # Convert spacing parameter to pixels. - spacing_px = renderer.points_to_pixels(self._spacing) - - # Transform before evaluation because to_polygons works at resolution - # of one -- assuming it is working in pixel space. - transpath = affine.transform_path(tpath) - - # Evaluate path to straight line segments that can be used to - # construct line ticks. - polys = transpath.to_polygons(closed_only=False) - - for p in polys: - x = p[:, 0] - y = p[:, 1] - - # Can not interpolate points or draw line if only one point in - # polyline. - if x.size < 2: - continue - - # Find distance between points on the line - ds = np.hypot(x[1:] - x[:-1], y[1:] - y[:-1]) - - # Build parametric coordinate along curve - s = np.concatenate(([0.0], np.cumsum(ds))) - s_total = s[-1] - - num = int(np.ceil(s_total / spacing_px)) - 1 - # Pick parameter values for ticks. - s_tick = np.linspace(spacing_px/2, s_total - spacing_px/2, num) - - # Find points along the parameterized curve - x_tick = np.interp(s_tick, s, x) - y_tick = np.interp(s_tick, s, y) - - # Find unit vectors in local direction of curve - delta_s = self._spacing * .001 - u = (np.interp(s_tick + delta_s, s, x) - x_tick) / delta_s - v = (np.interp(s_tick + delta_s, s, y) - y_tick) / delta_s - - # Normalize slope into unit slope vector. - n = np.hypot(u, v) - mask = n == 0 - n[mask] = 1.0 - - uv = np.array([u / n, v / n]).T - uv[mask] = np.array([0, 0]).T - - # Rotate and scale unit vector into tick vector - dxy = np.dot(uv, trans_matrix) * self._length * spacing_px - - # Build tick endpoints - x_end = x_tick + dxy[:, 0] - y_end = y_tick + dxy[:, 1] - - # Interleave ticks to form Path vertices - xyt = np.empty((2 * num, 2), dtype=x_tick.dtype) - xyt[0::2, 0] = x_tick - xyt[1::2, 0] = x_end - xyt[0::2, 1] = y_tick - xyt[1::2, 1] = y_end - - # Build up vector of Path codes - codes = np.tile([Path.MOVETO, Path.LINETO], num) - - # Construct and draw resulting path - h = Path(xyt, codes) - # Transform back to data space during render - renderer.draw_path(gc0, h, affine.inverted() + trans, rgbFace) - - gc0.restore() - - -withTickedStroke = _subclass_with_normal(effect_class=TickedStroke) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/projections/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/projections/__init__.py deleted file mode 100644 index 16a5651..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/projections/__init__.py +++ /dev/null @@ -1,114 +0,0 @@ -""" -Non-separable transforms that map from data space to screen space. - -Projections are defined as `~.axes.Axes` subclasses. They include the -following elements: - -- A transformation from data coordinates into display coordinates. - -- An inverse of that transformation. This is used, for example, to convert - mouse positions from screen space back into data space. - -- Transformations for the gridlines, ticks and ticklabels. Custom projections - will often need to place these elements in special locations, and Matplotlib - has a facility to help with doing so. - -- Setting up default values (overriding `~.axes.Axes.cla`), since the defaults - for a rectilinear axes may not be appropriate. - -- Defining the shape of the axes, for example, an elliptical axes, that will be - used to draw the background of the plot and for clipping any data elements. - -- Defining custom locators and formatters for the projection. For example, in - a geographic projection, it may be more convenient to display the grid in - degrees, even if the data is in radians. - -- Set up interactive panning and zooming. This is left as an "advanced" - feature left to the reader, but there is an example of this for polar plots - in `matplotlib.projections.polar`. - -- Any additional methods for additional convenience or features. - -Once the projection axes is defined, it can be used in one of two ways: - -- By defining the class attribute ``name``, the projection axes can be - registered with `matplotlib.projections.register_projection` and subsequently - simply invoked by name:: - - fig.add_subplot(projection="my_proj_name") - -- For more complex, parameterisable projections, a generic "projection" object - may be defined which includes the method ``_as_mpl_axes``. ``_as_mpl_axes`` - should take no arguments and return the projection's axes subclass and a - dictionary of additional arguments to pass to the subclass' ``__init__`` - method. Subsequently a parameterised projection can be initialised with:: - - fig.add_subplot(projection=MyProjection(param1=param1_value)) - - where MyProjection is an object which implements a ``_as_mpl_axes`` method. - -A full-fledged and heavily annotated example is in -:doc:`/gallery/misc/custom_projection`. The polar plot functionality in -`matplotlib.projections.polar` may also be of interest. -""" - -from .. import axes, _docstring -from .geo import AitoffAxes, HammerAxes, LambertAxes, MollweideAxes -from .polar import PolarAxes -from mpl_toolkits.mplot3d import Axes3D - - -class ProjectionRegistry: - """A mapping of registered projection names to projection classes.""" - - def __init__(self): - self._all_projection_types = {} - - def register(self, *projections): - """Register a new set of projections.""" - for projection in projections: - name = projection.name - self._all_projection_types[name] = projection - - def get_projection_class(self, name): - """Get a projection class from its *name*.""" - return self._all_projection_types[name] - - def get_projection_names(self): - """Return the names of all projections currently registered.""" - return sorted(self._all_projection_types) - - -projection_registry = ProjectionRegistry() -projection_registry.register( - axes.Axes, - PolarAxes, - AitoffAxes, - HammerAxes, - LambertAxes, - MollweideAxes, - Axes3D, -) - - -def register_projection(cls): - projection_registry.register(cls) - - -def get_projection_class(projection=None): - """ - Get a projection class from its name. - - If *projection* is None, a standard rectilinear projection is returned. - """ - if projection is None: - projection = 'rectilinear' - - try: - return projection_registry.get_projection_class(projection) - except KeyError as err: - raise ValueError("Unknown projection %r" % projection) from err - - -get_projection_names = projection_registry.get_projection_names -_docstring.interpd.update(projection_names=get_projection_names()) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/projections/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/projections/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 65169b7..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/projections/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/projections/__pycache__/geo.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/projections/__pycache__/geo.cpython-38.pyc deleted file mode 100644 index cfa806b..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/projections/__pycache__/geo.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/projections/__pycache__/polar.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/projections/__pycache__/polar.cpython-38.pyc deleted file mode 100644 index 95fe7d7..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/projections/__pycache__/polar.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/projections/geo.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/projections/geo.py deleted file mode 100644 index 75e582e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/projections/geo.py +++ /dev/null @@ -1,504 +0,0 @@ -import numpy as np - -import matplotlib as mpl -from matplotlib import _api -from matplotlib.axes import Axes -import matplotlib.axis as maxis -from matplotlib.patches import Circle -from matplotlib.path import Path -import matplotlib.spines as mspines -from matplotlib.ticker import ( - Formatter, NullLocator, FixedLocator, NullFormatter) -from matplotlib.transforms import Affine2D, BboxTransformTo, Transform - - -class GeoAxes(Axes): - """An abstract base class for geographic projections.""" - - class ThetaFormatter(Formatter): - """ - Used to format the theta tick labels. Converts the native - unit of radians into degrees and adds a degree symbol. - """ - def __init__(self, round_to=1.0): - self._round_to = round_to - - def __call__(self, x, pos=None): - degrees = round(np.rad2deg(x) / self._round_to) * self._round_to - return f"{degrees:0.0f}\N{DEGREE SIGN}" - - RESOLUTION = 75 - - def _init_axis(self): - self.xaxis = maxis.XAxis(self) - self.yaxis = maxis.YAxis(self) - # Do not register xaxis or yaxis with spines -- as done in - # Axes._init_axis() -- until GeoAxes.xaxis.clear() works. - # self.spines['geo'].register_axis(self.yaxis) - - def clear(self): - # docstring inherited - super().clear() - - self.set_longitude_grid(30) - self.set_latitude_grid(15) - self.set_longitude_grid_ends(75) - self.xaxis.set_minor_locator(NullLocator()) - self.yaxis.set_minor_locator(NullLocator()) - self.xaxis.set_ticks_position('none') - self.yaxis.set_ticks_position('none') - self.yaxis.set_tick_params(label1On=True) - # Why do we need to turn on yaxis tick labels, but - # xaxis tick labels are already on? - - self.grid(mpl.rcParams['axes.grid']) - - Axes.set_xlim(self, -np.pi, np.pi) - Axes.set_ylim(self, -np.pi / 2.0, np.pi / 2.0) - - def _set_lim_and_transforms(self): - # A (possibly non-linear) projection on the (already scaled) data - self.transProjection = self._get_core_transform(self.RESOLUTION) - - self.transAffine = self._get_affine_transform() - - self.transAxes = BboxTransformTo(self.bbox) - - # The complete data transformation stack -- from data all the - # way to display coordinates - self.transData = \ - self.transProjection + \ - self.transAffine + \ - self.transAxes - - # This is the transform for longitude ticks. - self._xaxis_pretransform = \ - Affine2D() \ - .scale(1, self._longitude_cap * 2) \ - .translate(0, -self._longitude_cap) - self._xaxis_transform = \ - self._xaxis_pretransform + \ - self.transData - self._xaxis_text1_transform = \ - Affine2D().scale(1, 0) + \ - self.transData + \ - Affine2D().translate(0, 4) - self._xaxis_text2_transform = \ - Affine2D().scale(1, 0) + \ - self.transData + \ - Affine2D().translate(0, -4) - - # This is the transform for latitude ticks. - yaxis_stretch = Affine2D().scale(np.pi * 2, 1).translate(-np.pi, 0) - yaxis_space = Affine2D().scale(1, 1.1) - self._yaxis_transform = \ - yaxis_stretch + \ - self.transData - yaxis_text_base = \ - yaxis_stretch + \ - self.transProjection + \ - (yaxis_space + - self.transAffine + - self.transAxes) - self._yaxis_text1_transform = \ - yaxis_text_base + \ - Affine2D().translate(-8, 0) - self._yaxis_text2_transform = \ - yaxis_text_base + \ - Affine2D().translate(8, 0) - - def _get_affine_transform(self): - transform = self._get_core_transform(1) - xscale, _ = transform.transform((np.pi, 0)) - _, yscale = transform.transform((0, np.pi/2)) - return Affine2D() \ - .scale(0.5 / xscale, 0.5 / yscale) \ - .translate(0.5, 0.5) - - def get_xaxis_transform(self, which='grid'): - _api.check_in_list(['tick1', 'tick2', 'grid'], which=which) - return self._xaxis_transform - - def get_xaxis_text1_transform(self, pad): - return self._xaxis_text1_transform, 'bottom', 'center' - - def get_xaxis_text2_transform(self, pad): - return self._xaxis_text2_transform, 'top', 'center' - - def get_yaxis_transform(self, which='grid'): - _api.check_in_list(['tick1', 'tick2', 'grid'], which=which) - return self._yaxis_transform - - def get_yaxis_text1_transform(self, pad): - return self._yaxis_text1_transform, 'center', 'right' - - def get_yaxis_text2_transform(self, pad): - return self._yaxis_text2_transform, 'center', 'left' - - def _gen_axes_patch(self): - return Circle((0.5, 0.5), 0.5) - - def _gen_axes_spines(self): - return {'geo': mspines.Spine.circular_spine(self, (0.5, 0.5), 0.5)} - - def set_yscale(self, *args, **kwargs): - if args[0] != 'linear': - raise NotImplementedError - - set_xscale = set_yscale - - def set_xlim(self, *args, **kwargs): - """Not supported. Please consider using Cartopy.""" - raise TypeError("Changing axes limits of a geographic projection is " - "not supported. Please consider using Cartopy.") - - set_ylim = set_xlim - - def format_coord(self, lon, lat): - """Return a format string formatting the coordinate.""" - lon, lat = np.rad2deg([lon, lat]) - ns = 'N' if lat >= 0.0 else 'S' - ew = 'E' if lon >= 0.0 else 'W' - return ('%f\N{DEGREE SIGN}%s, %f\N{DEGREE SIGN}%s' - % (abs(lat), ns, abs(lon), ew)) - - def set_longitude_grid(self, degrees): - """ - Set the number of degrees between each longitude grid. - """ - # Skip -180 and 180, which are the fixed limits. - grid = np.arange(-180 + degrees, 180, degrees) - self.xaxis.set_major_locator(FixedLocator(np.deg2rad(grid))) - self.xaxis.set_major_formatter(self.ThetaFormatter(degrees)) - - def set_latitude_grid(self, degrees): - """ - Set the number of degrees between each latitude grid. - """ - # Skip -90 and 90, which are the fixed limits. - grid = np.arange(-90 + degrees, 90, degrees) - self.yaxis.set_major_locator(FixedLocator(np.deg2rad(grid))) - self.yaxis.set_major_formatter(self.ThetaFormatter(degrees)) - - def set_longitude_grid_ends(self, degrees): - """ - Set the latitude(s) at which to stop drawing the longitude grids. - """ - self._longitude_cap = np.deg2rad(degrees) - self._xaxis_pretransform \ - .clear() \ - .scale(1.0, self._longitude_cap * 2.0) \ - .translate(0.0, -self._longitude_cap) - - def get_data_ratio(self): - """Return the aspect ratio of the data itself.""" - return 1.0 - - ### Interactive panning - - def can_zoom(self): - """ - Return whether this Axes supports the zoom box button functionality. - - This Axes object does not support interactive zoom box. - """ - return False - - def can_pan(self): - """ - Return whether this Axes supports the pan/zoom button functionality. - - This Axes object does not support interactive pan/zoom. - """ - return False - - def start_pan(self, x, y, button): - pass - - def end_pan(self): - pass - - def drag_pan(self, button, key, x, y): - pass - - -class _GeoTransform(Transform): - # Factoring out some common functionality. - input_dims = output_dims = 2 - - def __init__(self, resolution): - """ - Create a new geographical transform. - - Resolution is the number of steps to interpolate between each input - line segment to approximate its path in curved space. - """ - super().__init__() - self._resolution = resolution - - def __str__(self): - return "{}({})".format(type(self).__name__, self._resolution) - - def transform_path_non_affine(self, path): - # docstring inherited - ipath = path.interpolated(self._resolution) - return Path(self.transform(ipath.vertices), ipath.codes) - - -class AitoffAxes(GeoAxes): - name = 'aitoff' - - class AitoffTransform(_GeoTransform): - """The base Aitoff transform.""" - - def transform_non_affine(self, ll): - # docstring inherited - longitude, latitude = ll.T - - # Pre-compute some values - half_long = longitude / 2.0 - cos_latitude = np.cos(latitude) - - alpha = np.arccos(cos_latitude * np.cos(half_long)) - sinc_alpha = np.sinc(alpha / np.pi) # np.sinc is sin(pi*x)/(pi*x). - - x = (cos_latitude * np.sin(half_long)) / sinc_alpha - y = np.sin(latitude) / sinc_alpha - return np.column_stack([x, y]) - - def inverted(self): - # docstring inherited - return AitoffAxes.InvertedAitoffTransform(self._resolution) - - class InvertedAitoffTransform(_GeoTransform): - - def transform_non_affine(self, xy): - # docstring inherited - # MGDTODO: Math is hard ;( - return np.full_like(xy, np.nan) - - def inverted(self): - # docstring inherited - return AitoffAxes.AitoffTransform(self._resolution) - - def __init__(self, *args, **kwargs): - self._longitude_cap = np.pi / 2.0 - super().__init__(*args, **kwargs) - self.set_aspect(0.5, adjustable='box', anchor='C') - self.clear() - - def _get_core_transform(self, resolution): - return self.AitoffTransform(resolution) - - -class HammerAxes(GeoAxes): - name = 'hammer' - - class HammerTransform(_GeoTransform): - """The base Hammer transform.""" - - def transform_non_affine(self, ll): - # docstring inherited - longitude, latitude = ll.T - half_long = longitude / 2.0 - cos_latitude = np.cos(latitude) - sqrt2 = np.sqrt(2.0) - alpha = np.sqrt(1.0 + cos_latitude * np.cos(half_long)) - x = (2.0 * sqrt2) * (cos_latitude * np.sin(half_long)) / alpha - y = (sqrt2 * np.sin(latitude)) / alpha - return np.column_stack([x, y]) - - def inverted(self): - # docstring inherited - return HammerAxes.InvertedHammerTransform(self._resolution) - - class InvertedHammerTransform(_GeoTransform): - - def transform_non_affine(self, xy): - # docstring inherited - x, y = xy.T - z = np.sqrt(1 - (x / 4) ** 2 - (y / 2) ** 2) - longitude = 2 * np.arctan((z * x) / (2 * (2 * z ** 2 - 1))) - latitude = np.arcsin(y*z) - return np.column_stack([longitude, latitude]) - - def inverted(self): - # docstring inherited - return HammerAxes.HammerTransform(self._resolution) - - def __init__(self, *args, **kwargs): - self._longitude_cap = np.pi / 2.0 - super().__init__(*args, **kwargs) - self.set_aspect(0.5, adjustable='box', anchor='C') - self.clear() - - def _get_core_transform(self, resolution): - return self.HammerTransform(resolution) - - -class MollweideAxes(GeoAxes): - name = 'mollweide' - - class MollweideTransform(_GeoTransform): - """The base Mollweide transform.""" - - def transform_non_affine(self, ll): - # docstring inherited - def d(theta): - delta = (-(theta + np.sin(theta) - pi_sin_l) - / (1 + np.cos(theta))) - return delta, np.abs(delta) > 0.001 - - longitude, latitude = ll.T - - clat = np.pi/2 - np.abs(latitude) - ihigh = clat < 0.087 # within 5 degrees of the poles - ilow = ~ihigh - aux = np.empty(latitude.shape, dtype=float) - - if ilow.any(): # Newton-Raphson iteration - pi_sin_l = np.pi * np.sin(latitude[ilow]) - theta = 2.0 * latitude[ilow] - delta, large_delta = d(theta) - while np.any(large_delta): - theta[large_delta] += delta[large_delta] - delta, large_delta = d(theta) - aux[ilow] = theta / 2 - - if ihigh.any(): # Taylor series-based approx. solution - e = clat[ihigh] - d = 0.5 * (3 * np.pi * e**2) ** (1.0/3) - aux[ihigh] = (np.pi/2 - d) * np.sign(latitude[ihigh]) - - xy = np.empty(ll.shape, dtype=float) - xy[:, 0] = (2.0 * np.sqrt(2.0) / np.pi) * longitude * np.cos(aux) - xy[:, 1] = np.sqrt(2.0) * np.sin(aux) - - return xy - - def inverted(self): - # docstring inherited - return MollweideAxes.InvertedMollweideTransform(self._resolution) - - class InvertedMollweideTransform(_GeoTransform): - - def transform_non_affine(self, xy): - # docstring inherited - x, y = xy.T - # from Equations (7, 8) of - # https://mathworld.wolfram.com/MollweideProjection.html - theta = np.arcsin(y / np.sqrt(2)) - longitude = (np.pi / (2 * np.sqrt(2))) * x / np.cos(theta) - latitude = np.arcsin((2 * theta + np.sin(2 * theta)) / np.pi) - return np.column_stack([longitude, latitude]) - - def inverted(self): - # docstring inherited - return MollweideAxes.MollweideTransform(self._resolution) - - def __init__(self, *args, **kwargs): - self._longitude_cap = np.pi / 2.0 - super().__init__(*args, **kwargs) - self.set_aspect(0.5, adjustable='box', anchor='C') - self.clear() - - def _get_core_transform(self, resolution): - return self.MollweideTransform(resolution) - - -class LambertAxes(GeoAxes): - name = 'lambert' - - class LambertTransform(_GeoTransform): - """The base Lambert transform.""" - - def __init__(self, center_longitude, center_latitude, resolution): - """ - Create a new Lambert transform. Resolution is the number of steps - to interpolate between each input line segment to approximate its - path in curved Lambert space. - """ - _GeoTransform.__init__(self, resolution) - self._center_longitude = center_longitude - self._center_latitude = center_latitude - - def transform_non_affine(self, ll): - # docstring inherited - longitude, latitude = ll.T - clong = self._center_longitude - clat = self._center_latitude - cos_lat = np.cos(latitude) - sin_lat = np.sin(latitude) - diff_long = longitude - clong - cos_diff_long = np.cos(diff_long) - - inner_k = np.maximum( # Prevent divide-by-zero problems - 1 + np.sin(clat)*sin_lat + np.cos(clat)*cos_lat*cos_diff_long, - 1e-15) - k = np.sqrt(2 / inner_k) - x = k * cos_lat*np.sin(diff_long) - y = k * (np.cos(clat)*sin_lat - np.sin(clat)*cos_lat*cos_diff_long) - - return np.column_stack([x, y]) - - def inverted(self): - # docstring inherited - return LambertAxes.InvertedLambertTransform( - self._center_longitude, - self._center_latitude, - self._resolution) - - class InvertedLambertTransform(_GeoTransform): - - def __init__(self, center_longitude, center_latitude, resolution): - _GeoTransform.__init__(self, resolution) - self._center_longitude = center_longitude - self._center_latitude = center_latitude - - def transform_non_affine(self, xy): - # docstring inherited - x, y = xy.T - clong = self._center_longitude - clat = self._center_latitude - p = np.maximum(np.hypot(x, y), 1e-9) - c = 2 * np.arcsin(0.5 * p) - sin_c = np.sin(c) - cos_c = np.cos(c) - - latitude = np.arcsin(cos_c*np.sin(clat) + - ((y*sin_c*np.cos(clat)) / p)) - longitude = clong + np.arctan( - (x*sin_c) / (p*np.cos(clat)*cos_c - y*np.sin(clat)*sin_c)) - - return np.column_stack([longitude, latitude]) - - def inverted(self): - # docstring inherited - return LambertAxes.LambertTransform( - self._center_longitude, - self._center_latitude, - self._resolution) - - def __init__(self, *args, center_longitude=0, center_latitude=0, **kwargs): - self._longitude_cap = np.pi / 2 - self._center_longitude = center_longitude - self._center_latitude = center_latitude - super().__init__(*args, **kwargs) - self.set_aspect('equal', adjustable='box', anchor='C') - self.clear() - - def clear(self): - # docstring inherited - super().clear() - self.yaxis.set_major_formatter(NullFormatter()) - - def _get_core_transform(self, resolution): - return self.LambertTransform( - self._center_longitude, - self._center_latitude, - resolution) - - def _get_affine_transform(self): - return Affine2D() \ - .scale(0.25) \ - .translate(0.5, 0.5) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/projections/polar.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/projections/polar.py deleted file mode 100644 index b7e5c83..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/projections/polar.py +++ /dev/null @@ -1,1537 +0,0 @@ -import math -import types - -import numpy as np - -import matplotlib as mpl -from matplotlib import _api, cbook -from matplotlib.axes import Axes -import matplotlib.axis as maxis -import matplotlib.markers as mmarkers -import matplotlib.patches as mpatches -from matplotlib.path import Path -import matplotlib.ticker as mticker -import matplotlib.transforms as mtransforms -from matplotlib.spines import Spine - - -class PolarTransform(mtransforms.Transform): - r""" - The base polar transform. - - This transform maps polar coordinates :math:`\theta, r` into Cartesian - coordinates :math:`x, y = r \cos(\theta), r \sin(\theta)` - (but does not fully transform into Axes coordinates or - handle positioning in screen space). - - This transformation is designed to be applied to data after any scaling - along the radial axis (e.g. log-scaling) has been applied to the input - data. - - Path segments at a fixed radius are automatically transformed to circular - arcs as long as ``path._interpolation_steps > 1``. - """ - - input_dims = output_dims = 2 - - def __init__(self, axis=None, use_rmin=True, - _apply_theta_transforms=True, *, scale_transform=None): - """ - Parameters - ---------- - axis : `~matplotlib.axis.Axis`, optional - Axis associated with this transform. This is used to get the - minimum radial limit. - use_rmin : `bool`, optional - If ``True``, subtract the minimum radial axis limit before - transforming to Cartesian coordinates. *axis* must also be - specified for this to take effect. - """ - super().__init__() - self._axis = axis - self._use_rmin = use_rmin - self._apply_theta_transforms = _apply_theta_transforms - self._scale_transform = scale_transform - - __str__ = mtransforms._make_str_method( - "_axis", - use_rmin="_use_rmin", - _apply_theta_transforms="_apply_theta_transforms") - - def _get_rorigin(self): - # Get lower r limit after being scaled by the radial scale transform - return self._scale_transform.transform( - (0, self._axis.get_rorigin()))[1] - - def transform_non_affine(self, tr): - # docstring inherited - theta, r = np.transpose(tr) - # PolarAxes does not use the theta transforms here, but apply them for - # backwards-compatibility if not being used by it. - if self._apply_theta_transforms and self._axis is not None: - theta *= self._axis.get_theta_direction() - theta += self._axis.get_theta_offset() - if self._use_rmin and self._axis is not None: - r = (r - self._get_rorigin()) * self._axis.get_rsign() - r = np.where(r >= 0, r, np.nan) - return np.column_stack([r * np.cos(theta), r * np.sin(theta)]) - - def transform_path_non_affine(self, path): - # docstring inherited - if not len(path) or path._interpolation_steps == 1: - return Path(self.transform_non_affine(path.vertices), path.codes) - xys = [] - codes = [] - last_t = last_r = None - for trs, c in path.iter_segments(): - trs = trs.reshape((-1, 2)) - if c == Path.LINETO: - (t, r), = trs - if t == last_t: # Same angle: draw a straight line. - xys.extend(self.transform_non_affine(trs)) - codes.append(Path.LINETO) - elif r == last_r: # Same radius: draw an arc. - # The following is complicated by Path.arc() being - # "helpful" and unwrapping the angles, but we don't want - # that behavior here. - last_td, td = np.rad2deg([last_t, t]) - if self._use_rmin and self._axis is not None: - r = ((r - self._get_rorigin()) - * self._axis.get_rsign()) - if last_td <= td: - while td - last_td > 360: - arc = Path.arc(last_td, last_td + 360) - xys.extend(arc.vertices[1:] * r) - codes.extend(arc.codes[1:]) - last_td += 360 - arc = Path.arc(last_td, td) - xys.extend(arc.vertices[1:] * r) - codes.extend(arc.codes[1:]) - else: - # The reverse version also relies on the fact that all - # codes but the first one are the same. - while last_td - td > 360: - arc = Path.arc(last_td - 360, last_td) - xys.extend(arc.vertices[::-1][1:] * r) - codes.extend(arc.codes[1:]) - last_td -= 360 - arc = Path.arc(td, last_td) - xys.extend(arc.vertices[::-1][1:] * r) - codes.extend(arc.codes[1:]) - else: # Interpolate. - trs = cbook.simple_linear_interpolation( - np.row_stack([(last_t, last_r), trs]), - path._interpolation_steps)[1:] - xys.extend(self.transform_non_affine(trs)) - codes.extend([Path.LINETO] * len(trs)) - else: # Not a straight line. - xys.extend(self.transform_non_affine(trs)) - codes.extend([c] * len(trs)) - last_t, last_r = trs[-1] - return Path(xys, codes) - - def inverted(self): - # docstring inherited - return PolarAxes.InvertedPolarTransform(self._axis, self._use_rmin, - self._apply_theta_transforms) - - -class PolarAffine(mtransforms.Affine2DBase): - r""" - The affine part of the polar projection. - - Scales the output so that maximum radius rests on the edge of the axes - circle and the origin is mapped to (0.5, 0.5). The transform applied is - the same to x and y components and given by: - - .. math:: - - x_{1} = 0.5 \left [ \frac{x_{0}}{(r_{\max} - r_{\min})} + 1 \right ] - - :math:`r_{\min}, r_{\max}` are the minimum and maximum radial limits after - any scaling (e.g. log scaling) has been removed. - """ - def __init__(self, scale_transform, limits): - """ - Parameters - ---------- - scale_transform : `~matplotlib.transforms.Transform` - Scaling transform for the data. This is used to remove any scaling - from the radial view limits. - limits : `~matplotlib.transforms.BboxBase` - View limits of the data. The only part of its bounds that is used - is the y limits (for the radius limits). - """ - super().__init__() - self._scale_transform = scale_transform - self._limits = limits - self.set_children(scale_transform, limits) - self._mtx = None - - __str__ = mtransforms._make_str_method("_scale_transform", "_limits") - - def get_matrix(self): - # docstring inherited - if self._invalid: - limits_scaled = self._limits.transformed(self._scale_transform) - yscale = limits_scaled.ymax - limits_scaled.ymin - affine = mtransforms.Affine2D() \ - .scale(0.5 / yscale) \ - .translate(0.5, 0.5) - self._mtx = affine.get_matrix() - self._inverted = None - self._invalid = 0 - return self._mtx - - -class InvertedPolarTransform(mtransforms.Transform): - """ - The inverse of the polar transform, mapping Cartesian - coordinate space *x* and *y* back to *theta* and *r*. - """ - input_dims = output_dims = 2 - - def __init__(self, axis=None, use_rmin=True, - _apply_theta_transforms=True): - """ - Parameters - ---------- - axis : `~matplotlib.axis.Axis`, optional - Axis associated with this transform. This is used to get the - minimum radial limit. - use_rmin : `bool`, optional - If ``True`` add the minimum radial axis limit after - transforming from Cartesian coordinates. *axis* must also be - specified for this to take effect. - """ - super().__init__() - self._axis = axis - self._use_rmin = use_rmin - self._apply_theta_transforms = _apply_theta_transforms - - __str__ = mtransforms._make_str_method( - "_axis", - use_rmin="_use_rmin", - _apply_theta_transforms="_apply_theta_transforms") - - def transform_non_affine(self, xy): - # docstring inherited - x, y = xy.T - r = np.hypot(x, y) - theta = (np.arctan2(y, x) + 2 * np.pi) % (2 * np.pi) - # PolarAxes does not use the theta transforms here, but apply them for - # backwards-compatibility if not being used by it. - if self._apply_theta_transforms and self._axis is not None: - theta -= self._axis.get_theta_offset() - theta *= self._axis.get_theta_direction() - theta %= 2 * np.pi - if self._use_rmin and self._axis is not None: - r += self._axis.get_rorigin() - r *= self._axis.get_rsign() - return np.column_stack([theta, r]) - - def inverted(self): - # docstring inherited - return PolarAxes.PolarTransform(self._axis, self._use_rmin, - self._apply_theta_transforms) - - -class ThetaFormatter(mticker.Formatter): - """ - Used to format the *theta* tick labels. Converts the native - unit of radians into degrees and adds a degree symbol. - """ - - def __call__(self, x, pos=None): - vmin, vmax = self.axis.get_view_interval() - d = np.rad2deg(abs(vmax - vmin)) - digits = max(-int(np.log10(d) - 1.5), 0) - # Use Unicode rather than mathtext with \circ, so that it will work - # correctly with any arbitrary font (assuming it has a degree sign), - # whereas $5\circ$ will only work correctly with one of the supported - # math fonts (Computer Modern and STIX). - return ("{value:0.{digits:d}f}\N{DEGREE SIGN}" - .format(value=np.rad2deg(x), digits=digits)) - - -class _AxisWrapper: - def __init__(self, axis): - self._axis = axis - - def get_view_interval(self): - return np.rad2deg(self._axis.get_view_interval()) - - def set_view_interval(self, vmin, vmax): - self._axis.set_view_interval(*np.deg2rad((vmin, vmax))) - - def get_minpos(self): - return np.rad2deg(self._axis.get_minpos()) - - def get_data_interval(self): - return np.rad2deg(self._axis.get_data_interval()) - - def set_data_interval(self, vmin, vmax): - self._axis.set_data_interval(*np.deg2rad((vmin, vmax))) - - def get_tick_space(self): - return self._axis.get_tick_space() - - -class ThetaLocator(mticker.Locator): - """ - Used to locate theta ticks. - - This will work the same as the base locator except in the case that the - view spans the entire circle. In such cases, the previously used default - locations of every 45 degrees are returned. - """ - - def __init__(self, base): - self.base = base - self.axis = self.base.axis = _AxisWrapper(self.base.axis) - - def set_axis(self, axis): - self.axis = _AxisWrapper(axis) - self.base.set_axis(self.axis) - - def __call__(self): - lim = self.axis.get_view_interval() - if _is_full_circle_deg(lim[0], lim[1]): - return np.arange(8) * 2 * np.pi / 8 - else: - return np.deg2rad(self.base()) - - def view_limits(self, vmin, vmax): - vmin, vmax = np.rad2deg((vmin, vmax)) - return np.deg2rad(self.base.view_limits(vmin, vmax)) - - -class ThetaTick(maxis.XTick): - """ - A theta-axis tick. - - This subclass of `.XTick` provides angular ticks with some small - modification to their re-positioning such that ticks are rotated based on - tick location. This results in ticks that are correctly perpendicular to - the arc spine. - - When 'auto' rotation is enabled, labels are also rotated to be parallel to - the spine. The label padding is also applied here since it's not possible - to use a generic axes transform to produce tick-specific padding. - """ - - def __init__(self, axes, *args, **kwargs): - self._text1_translate = mtransforms.ScaledTranslation( - 0, 0, axes.figure.dpi_scale_trans) - self._text2_translate = mtransforms.ScaledTranslation( - 0, 0, axes.figure.dpi_scale_trans) - super().__init__(axes, *args, **kwargs) - self.label1.set( - rotation_mode='anchor', - transform=self.label1.get_transform() + self._text1_translate) - self.label2.set( - rotation_mode='anchor', - transform=self.label2.get_transform() + self._text2_translate) - - def _apply_params(self, **kwargs): - super()._apply_params(**kwargs) - # Ensure transform is correct; sometimes this gets reset. - trans = self.label1.get_transform() - if not trans.contains_branch(self._text1_translate): - self.label1.set_transform(trans + self._text1_translate) - trans = self.label2.get_transform() - if not trans.contains_branch(self._text2_translate): - self.label2.set_transform(trans + self._text2_translate) - - def _update_padding(self, pad, angle): - padx = pad * np.cos(angle) / 72 - pady = pad * np.sin(angle) / 72 - self._text1_translate._t = (padx, pady) - self._text1_translate.invalidate() - self._text2_translate._t = (-padx, -pady) - self._text2_translate.invalidate() - - def update_position(self, loc): - super().update_position(loc) - axes = self.axes - angle = loc * axes.get_theta_direction() + axes.get_theta_offset() - text_angle = np.rad2deg(angle) % 360 - 90 - angle -= np.pi / 2 - - marker = self.tick1line.get_marker() - if marker in (mmarkers.TICKUP, '|'): - trans = mtransforms.Affine2D().scale(1, 1).rotate(angle) - elif marker == mmarkers.TICKDOWN: - trans = mtransforms.Affine2D().scale(1, -1).rotate(angle) - else: - # Don't modify custom tick line markers. - trans = self.tick1line._marker._transform - self.tick1line._marker._transform = trans - - marker = self.tick2line.get_marker() - if marker in (mmarkers.TICKUP, '|'): - trans = mtransforms.Affine2D().scale(1, 1).rotate(angle) - elif marker == mmarkers.TICKDOWN: - trans = mtransforms.Affine2D().scale(1, -1).rotate(angle) - else: - # Don't modify custom tick line markers. - trans = self.tick2line._marker._transform - self.tick2line._marker._transform = trans - - mode, user_angle = self._labelrotation - if mode == 'default': - text_angle = user_angle - else: - if text_angle > 90: - text_angle -= 180 - elif text_angle < -90: - text_angle += 180 - text_angle += user_angle - self.label1.set_rotation(text_angle) - self.label2.set_rotation(text_angle) - - # This extra padding helps preserve the look from previous releases but - # is also needed because labels are anchored to their center. - pad = self._pad + 7 - self._update_padding(pad, - self._loc * axes.get_theta_direction() + - axes.get_theta_offset()) - - -class ThetaAxis(maxis.XAxis): - """ - A theta Axis. - - This overrides certain properties of an `.XAxis` to provide special-casing - for an angular axis. - """ - __name__ = 'thetaaxis' - axis_name = 'theta' #: Read-only name identifying the axis. - _tick_class = ThetaTick - - def _wrap_locator_formatter(self): - self.set_major_locator(ThetaLocator(self.get_major_locator())) - self.set_major_formatter(ThetaFormatter()) - self.isDefault_majloc = True - self.isDefault_majfmt = True - - def clear(self): - # docstring inherited - super().clear() - self.set_ticks_position('none') - self._wrap_locator_formatter() - - def _set_scale(self, value, **kwargs): - if value != 'linear': - raise NotImplementedError( - "The xscale cannot be set on a polar plot") - super()._set_scale(value, **kwargs) - # LinearScale.set_default_locators_and_formatters just set the major - # locator to be an AutoLocator, so we customize it here to have ticks - # at sensible degree multiples. - self.get_major_locator().set_params(steps=[1, 1.5, 3, 4.5, 9, 10]) - self._wrap_locator_formatter() - - def _copy_tick_props(self, src, dest): - """Copy the props from src tick to dest tick.""" - if src is None or dest is None: - return - super()._copy_tick_props(src, dest) - - # Ensure that tick transforms are independent so that padding works. - trans = dest._get_text1_transform()[0] - dest.label1.set_transform(trans + dest._text1_translate) - trans = dest._get_text2_transform()[0] - dest.label2.set_transform(trans + dest._text2_translate) - - -class RadialLocator(mticker.Locator): - """ - Used to locate radius ticks. - - Ensures that all ticks are strictly positive. For all other tasks, it - delegates to the base `.Locator` (which may be different depending on the - scale of the *r*-axis). - """ - - def __init__(self, base, axes=None): - self.base = base - self._axes = axes - - def set_axis(self, axis): - self.base.set_axis(axis) - - def __call__(self): - # Ensure previous behaviour with full circle non-annular views. - if self._axes: - if _is_full_circle_rad(*self._axes.viewLim.intervalx): - rorigin = self._axes.get_rorigin() * self._axes.get_rsign() - if self._axes.get_rmin() <= rorigin: - return [tick for tick in self.base() if tick > rorigin] - return self.base() - - def _zero_in_bounds(self): - """ - Return True if zero is within the valid values for the - scale of the radial axis. - """ - vmin, vmax = self._axes.yaxis._scale.limit_range_for_scale(0, 1, 1e-5) - return vmin == 0 - - def nonsingular(self, vmin, vmax): - # docstring inherited - if self._zero_in_bounds() and (vmin, vmax) == (-np.inf, np.inf): - # Initial view limits - return (0, 1) - else: - return self.base.nonsingular(vmin, vmax) - - def view_limits(self, vmin, vmax): - vmin, vmax = self.base.view_limits(vmin, vmax) - if self._zero_in_bounds() and vmax > vmin: - # this allows inverted r/y-lims - vmin = min(0, vmin) - return mtransforms.nonsingular(vmin, vmax) - - -class _ThetaShift(mtransforms.ScaledTranslation): - """ - Apply a padding shift based on axes theta limits. - - This is used to create padding for radial ticks. - - Parameters - ---------- - axes : `~matplotlib.axes.Axes` - The owning axes; used to determine limits. - pad : float - The padding to apply, in points. - mode : {'min', 'max', 'rlabel'} - Whether to shift away from the start (``'min'``) or the end (``'max'``) - of the axes, or using the rlabel position (``'rlabel'``). - """ - def __init__(self, axes, pad, mode): - super().__init__(pad, pad, axes.figure.dpi_scale_trans) - self.set_children(axes._realViewLim) - self.axes = axes - self.mode = mode - self.pad = pad - - __str__ = mtransforms._make_str_method("axes", "pad", "mode") - - def get_matrix(self): - if self._invalid: - if self.mode == 'rlabel': - angle = ( - np.deg2rad(self.axes.get_rlabel_position()) * - self.axes.get_theta_direction() + - self.axes.get_theta_offset() - ) - else: - if self.mode == 'min': - angle = self.axes._realViewLim.xmin - elif self.mode == 'max': - angle = self.axes._realViewLim.xmax - - if self.mode in ('rlabel', 'min'): - padx = np.cos(angle - np.pi / 2) - pady = np.sin(angle - np.pi / 2) - else: - padx = np.cos(angle + np.pi / 2) - pady = np.sin(angle + np.pi / 2) - - self._t = (self.pad * padx / 72, self.pad * pady / 72) - return super().get_matrix() - - -class RadialTick(maxis.YTick): - """ - A radial-axis tick. - - This subclass of `.YTick` provides radial ticks with some small - modification to their re-positioning such that ticks are rotated based on - axes limits. This results in ticks that are correctly perpendicular to - the spine. Labels are also rotated to be perpendicular to the spine, when - 'auto' rotation is enabled. - """ - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.label1.set_rotation_mode('anchor') - self.label2.set_rotation_mode('anchor') - - def _determine_anchor(self, mode, angle, start): - # Note: angle is the (spine angle - 90) because it's used for the tick - # & text setup, so all numbers below are -90 from (normed) spine angle. - if mode == 'auto': - if start: - if -90 <= angle <= 90: - return 'left', 'center' - else: - return 'right', 'center' - else: - if -90 <= angle <= 90: - return 'right', 'center' - else: - return 'left', 'center' - else: - if start: - if angle < -68.5: - return 'center', 'top' - elif angle < -23.5: - return 'left', 'top' - elif angle < 22.5: - return 'left', 'center' - elif angle < 67.5: - return 'left', 'bottom' - elif angle < 112.5: - return 'center', 'bottom' - elif angle < 157.5: - return 'right', 'bottom' - elif angle < 202.5: - return 'right', 'center' - elif angle < 247.5: - return 'right', 'top' - else: - return 'center', 'top' - else: - if angle < -68.5: - return 'center', 'bottom' - elif angle < -23.5: - return 'right', 'bottom' - elif angle < 22.5: - return 'right', 'center' - elif angle < 67.5: - return 'right', 'top' - elif angle < 112.5: - return 'center', 'top' - elif angle < 157.5: - return 'left', 'top' - elif angle < 202.5: - return 'left', 'center' - elif angle < 247.5: - return 'left', 'bottom' - else: - return 'center', 'bottom' - - def update_position(self, loc): - super().update_position(loc) - axes = self.axes - thetamin = axes.get_thetamin() - thetamax = axes.get_thetamax() - direction = axes.get_theta_direction() - offset_rad = axes.get_theta_offset() - offset = np.rad2deg(offset_rad) - full = _is_full_circle_deg(thetamin, thetamax) - - if full: - angle = (axes.get_rlabel_position() * direction + - offset) % 360 - 90 - tick_angle = 0 - else: - angle = (thetamin * direction + offset) % 360 - 90 - if direction > 0: - tick_angle = np.deg2rad(angle) - else: - tick_angle = np.deg2rad(angle + 180) - text_angle = (angle + 90) % 180 - 90 # between -90 and +90. - mode, user_angle = self._labelrotation - if mode == 'auto': - text_angle += user_angle - else: - text_angle = user_angle - - if full: - ha = self.label1.get_horizontalalignment() - va = self.label1.get_verticalalignment() - else: - ha, va = self._determine_anchor(mode, angle, direction > 0) - self.label1.set_horizontalalignment(ha) - self.label1.set_verticalalignment(va) - self.label1.set_rotation(text_angle) - - marker = self.tick1line.get_marker() - if marker == mmarkers.TICKLEFT: - trans = mtransforms.Affine2D().rotate(tick_angle) - elif marker == '_': - trans = mtransforms.Affine2D().rotate(tick_angle + np.pi / 2) - elif marker == mmarkers.TICKRIGHT: - trans = mtransforms.Affine2D().scale(-1, 1).rotate(tick_angle) - else: - # Don't modify custom tick line markers. - trans = self.tick1line._marker._transform - self.tick1line._marker._transform = trans - - if full: - self.label2.set_visible(False) - self.tick2line.set_visible(False) - angle = (thetamax * direction + offset) % 360 - 90 - if direction > 0: - tick_angle = np.deg2rad(angle) - else: - tick_angle = np.deg2rad(angle + 180) - text_angle = (angle + 90) % 180 - 90 # between -90 and +90. - mode, user_angle = self._labelrotation - if mode == 'auto': - text_angle += user_angle - else: - text_angle = user_angle - - ha, va = self._determine_anchor(mode, angle, direction < 0) - self.label2.set_ha(ha) - self.label2.set_va(va) - self.label2.set_rotation(text_angle) - - marker = self.tick2line.get_marker() - if marker == mmarkers.TICKLEFT: - trans = mtransforms.Affine2D().rotate(tick_angle) - elif marker == '_': - trans = mtransforms.Affine2D().rotate(tick_angle + np.pi / 2) - elif marker == mmarkers.TICKRIGHT: - trans = mtransforms.Affine2D().scale(-1, 1).rotate(tick_angle) - else: - # Don't modify custom tick line markers. - trans = self.tick2line._marker._transform - self.tick2line._marker._transform = trans - - -class RadialAxis(maxis.YAxis): - """ - A radial Axis. - - This overrides certain properties of a `.YAxis` to provide special-casing - for a radial axis. - """ - __name__ = 'radialaxis' - axis_name = 'radius' #: Read-only name identifying the axis. - _tick_class = RadialTick - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.sticky_edges.y.append(0) - - def _wrap_locator_formatter(self): - self.set_major_locator(RadialLocator(self.get_major_locator(), - self.axes)) - self.isDefault_majloc = True - - def clear(self): - # docstring inherited - super().clear() - self.set_ticks_position('none') - self._wrap_locator_formatter() - - def _set_scale(self, value, **kwargs): - super()._set_scale(value, **kwargs) - self._wrap_locator_formatter() - - -def _is_full_circle_deg(thetamin, thetamax): - """ - Determine if a wedge (in degrees) spans the full circle. - - The condition is derived from :class:`~matplotlib.patches.Wedge`. - """ - return abs(abs(thetamax - thetamin) - 360.0) < 1e-12 - - -def _is_full_circle_rad(thetamin, thetamax): - """ - Determine if a wedge (in radians) spans the full circle. - - The condition is derived from :class:`~matplotlib.patches.Wedge`. - """ - return abs(abs(thetamax - thetamin) - 2 * np.pi) < 1.74e-14 - - -class _WedgeBbox(mtransforms.Bbox): - """ - Transform (theta, r) wedge Bbox into axes bounding box. - - Parameters - ---------- - center : (float, float) - Center of the wedge - viewLim : `~matplotlib.transforms.Bbox` - Bbox determining the boundaries of the wedge - originLim : `~matplotlib.transforms.Bbox` - Bbox determining the origin for the wedge, if different from *viewLim* - """ - def __init__(self, center, viewLim, originLim, **kwargs): - super().__init__([[0, 0], [1, 1]], **kwargs) - self._center = center - self._viewLim = viewLim - self._originLim = originLim - self.set_children(viewLim, originLim) - - __str__ = mtransforms._make_str_method("_center", "_viewLim", "_originLim") - - def get_points(self): - # docstring inherited - if self._invalid: - points = self._viewLim.get_points().copy() - # Scale angular limits to work with Wedge. - points[:, 0] *= 180 / np.pi - if points[0, 0] > points[1, 0]: - points[:, 0] = points[::-1, 0] - - # Scale radial limits based on origin radius. - points[:, 1] -= self._originLim.y0 - - # Scale radial limits to match axes limits. - rscale = 0.5 / points[1, 1] - points[:, 1] *= rscale - width = min(points[1, 1] - points[0, 1], 0.5) - - # Generate bounding box for wedge. - wedge = mpatches.Wedge(self._center, points[1, 1], - points[0, 0], points[1, 0], - width=width) - self.update_from_path(wedge.get_path()) - - # Ensure equal aspect ratio. - w, h = self._points[1] - self._points[0] - deltah = max(w - h, 0) / 2 - deltaw = max(h - w, 0) / 2 - self._points += np.array([[-deltaw, -deltah], [deltaw, deltah]]) - - self._invalid = 0 - - return self._points - - -class PolarAxes(Axes): - """ - A polar graph projection, where the input dimensions are *theta*, *r*. - - Theta starts pointing east and goes anti-clockwise. - """ - name = 'polar' - - def __init__(self, *args, - theta_offset=0, theta_direction=1, rlabel_position=22.5, - **kwargs): - # docstring inherited - self._default_theta_offset = theta_offset - self._default_theta_direction = theta_direction - self._default_rlabel_position = np.deg2rad(rlabel_position) - super().__init__(*args, **kwargs) - self.use_sticky_edges = True - self.set_aspect('equal', adjustable='box', anchor='C') - self.clear() - - def clear(self): - # docstring inherited - super().clear() - - self.title.set_y(1.05) - - start = self.spines.get('start', None) - if start: - start.set_visible(False) - end = self.spines.get('end', None) - if end: - end.set_visible(False) - self.set_xlim(0.0, 2 * np.pi) - - self.grid(mpl.rcParams['polaraxes.grid']) - inner = self.spines.get('inner', None) - if inner: - inner.set_visible(False) - - self.set_rorigin(None) - self.set_theta_offset(self._default_theta_offset) - self.set_theta_direction(self._default_theta_direction) - - def _init_axis(self): - # This is moved out of __init__ because non-separable axes don't use it - self.xaxis = ThetaAxis(self) - self.yaxis = RadialAxis(self) - # Calling polar_axes.xaxis.clear() or polar_axes.yaxis.clear() - # results in weird artifacts. Therefore we disable this for now. - # self.spines['polar'].register_axis(self.yaxis) - - def _set_lim_and_transforms(self): - # A view limit where the minimum radius can be locked if the user - # specifies an alternate origin. - self._originViewLim = mtransforms.LockableBbox(self.viewLim) - - # Handle angular offset and direction. - self._direction = mtransforms.Affine2D() \ - .scale(self._default_theta_direction, 1.0) - self._theta_offset = mtransforms.Affine2D() \ - .translate(self._default_theta_offset, 0.0) - self.transShift = self._direction + self._theta_offset - # A view limit shifted to the correct location after accounting for - # orientation and offset. - self._realViewLim = mtransforms.TransformedBbox(self.viewLim, - self.transShift) - - # Transforms the x and y axis separately by a scale factor - # It is assumed that this part will have non-linear components - self.transScale = mtransforms.TransformWrapper( - mtransforms.IdentityTransform()) - - # Scale view limit into a bbox around the selected wedge. This may be - # smaller than the usual unit axes rectangle if not plotting the full - # circle. - self.axesLim = _WedgeBbox((0.5, 0.5), - self._realViewLim, self._originViewLim) - - # Scale the wedge to fill the axes. - self.transWedge = mtransforms.BboxTransformFrom(self.axesLim) - - # Scale the axes to fill the figure. - self.transAxes = mtransforms.BboxTransformTo(self.bbox) - - # A (possibly non-linear) projection on the (already scaled) - # data. This one is aware of rmin - self.transProjection = self.PolarTransform( - self, - _apply_theta_transforms=False, - scale_transform=self.transScale - ) - # Add dependency on rorigin. - self.transProjection.set_children(self._originViewLim) - - # An affine transformation on the data, generally to limit the - # range of the axes - self.transProjectionAffine = self.PolarAffine(self.transScale, - self._originViewLim) - - # The complete data transformation stack -- from data all the - # way to display coordinates - # - # 1. Remove any radial axis scaling (e.g. log scaling) - # 2. Shift data in the theta direction - # 3. Project the data from polar to cartesian values - # (with the origin in the same place) - # 4. Scale and translate the cartesian values to Axes coordinates - # (here the origin is moved to the lower left of the Axes) - # 5. Move and scale to fill the Axes - # 6. Convert from Axes coordinates to Figure coordinates - self.transData = ( - self.transScale + - self.transShift + - self.transProjection + - ( - self.transProjectionAffine + - self.transWedge + - self.transAxes - ) - ) - - # This is the transform for theta-axis ticks. It is - # equivalent to transData, except it always puts r == 0.0 and r == 1.0 - # at the edge of the axis circles. - self._xaxis_transform = ( - mtransforms.blended_transform_factory( - mtransforms.IdentityTransform(), - mtransforms.BboxTransformTo(self.viewLim)) + - self.transData) - # The theta labels are flipped along the radius, so that text 1 is on - # the outside by default. This should work the same as before. - flipr_transform = mtransforms.Affine2D() \ - .translate(0.0, -0.5) \ - .scale(1.0, -1.0) \ - .translate(0.0, 0.5) - self._xaxis_text_transform = flipr_transform + self._xaxis_transform - - # This is the transform for r-axis ticks. It scales the theta - # axis so the gridlines from 0.0 to 1.0, now go from thetamin to - # thetamax. - self._yaxis_transform = ( - mtransforms.blended_transform_factory( - mtransforms.BboxTransformTo(self.viewLim), - mtransforms.IdentityTransform()) + - self.transData) - # The r-axis labels are put at an angle and padded in the r-direction - self._r_label_position = mtransforms.Affine2D() \ - .translate(self._default_rlabel_position, 0.0) - self._yaxis_text_transform = mtransforms.TransformWrapper( - self._r_label_position + self.transData) - - def get_xaxis_transform(self, which='grid'): - _api.check_in_list(['tick1', 'tick2', 'grid'], which=which) - return self._xaxis_transform - - def get_xaxis_text1_transform(self, pad): - return self._xaxis_text_transform, 'center', 'center' - - def get_xaxis_text2_transform(self, pad): - return self._xaxis_text_transform, 'center', 'center' - - def get_yaxis_transform(self, which='grid'): - if which in ('tick1', 'tick2'): - return self._yaxis_text_transform - elif which == 'grid': - return self._yaxis_transform - else: - _api.check_in_list(['tick1', 'tick2', 'grid'], which=which) - - def get_yaxis_text1_transform(self, pad): - thetamin, thetamax = self._realViewLim.intervalx - if _is_full_circle_rad(thetamin, thetamax): - return self._yaxis_text_transform, 'bottom', 'left' - elif self.get_theta_direction() > 0: - halign = 'left' - pad_shift = _ThetaShift(self, pad, 'min') - else: - halign = 'right' - pad_shift = _ThetaShift(self, pad, 'max') - return self._yaxis_text_transform + pad_shift, 'center', halign - - def get_yaxis_text2_transform(self, pad): - if self.get_theta_direction() > 0: - halign = 'right' - pad_shift = _ThetaShift(self, pad, 'max') - else: - halign = 'left' - pad_shift = _ThetaShift(self, pad, 'min') - return self._yaxis_text_transform + pad_shift, 'center', halign - - def draw(self, renderer): - self._unstale_viewLim() - thetamin, thetamax = np.rad2deg(self._realViewLim.intervalx) - if thetamin > thetamax: - thetamin, thetamax = thetamax, thetamin - rmin, rmax = ((self._realViewLim.intervaly - self.get_rorigin()) * - self.get_rsign()) - if isinstance(self.patch, mpatches.Wedge): - # Backwards-compatibility: Any subclassed Axes might override the - # patch to not be the Wedge that PolarAxes uses. - center = self.transWedge.transform((0.5, 0.5)) - self.patch.set_center(center) - self.patch.set_theta1(thetamin) - self.patch.set_theta2(thetamax) - - edge, _ = self.transWedge.transform((1, 0)) - radius = edge - center[0] - width = min(radius * (rmax - rmin) / rmax, radius) - self.patch.set_radius(radius) - self.patch.set_width(width) - - inner_width = radius - width - inner = self.spines.get('inner', None) - if inner: - inner.set_visible(inner_width != 0.0) - - visible = not _is_full_circle_deg(thetamin, thetamax) - # For backwards compatibility, any subclassed Axes might override the - # spines to not include start/end that PolarAxes uses. - start = self.spines.get('start', None) - end = self.spines.get('end', None) - if start: - start.set_visible(visible) - if end: - end.set_visible(visible) - if visible: - yaxis_text_transform = self._yaxis_transform - else: - yaxis_text_transform = self._r_label_position + self.transData - if self._yaxis_text_transform != yaxis_text_transform: - self._yaxis_text_transform.set(yaxis_text_transform) - self.yaxis.reset_ticks() - self.yaxis.set_clip_path(self.patch) - - super().draw(renderer) - - def _gen_axes_patch(self): - return mpatches.Wedge((0.5, 0.5), 0.5, 0.0, 360.0) - - def _gen_axes_spines(self): - spines = { - 'polar': Spine.arc_spine(self, 'top', (0.5, 0.5), 0.5, 0, 360), - 'start': Spine.linear_spine(self, 'left'), - 'end': Spine.linear_spine(self, 'right'), - 'inner': Spine.arc_spine(self, 'bottom', (0.5, 0.5), 0.0, 0, 360), - } - spines['polar'].set_transform(self.transWedge + self.transAxes) - spines['inner'].set_transform(self.transWedge + self.transAxes) - spines['start'].set_transform(self._yaxis_transform) - spines['end'].set_transform(self._yaxis_transform) - return spines - - def set_thetamax(self, thetamax): - """Set the maximum theta limit in degrees.""" - self.viewLim.x1 = np.deg2rad(thetamax) - - def get_thetamax(self): - """Return the maximum theta limit in degrees.""" - return np.rad2deg(self.viewLim.xmax) - - def set_thetamin(self, thetamin): - """Set the minimum theta limit in degrees.""" - self.viewLim.x0 = np.deg2rad(thetamin) - - def get_thetamin(self): - """Get the minimum theta limit in degrees.""" - return np.rad2deg(self.viewLim.xmin) - - def set_thetalim(self, *args, **kwargs): - r""" - Set the minimum and maximum theta values. - - Can take the following signatures: - - - ``set_thetalim(minval, maxval)``: Set the limits in radians. - - ``set_thetalim(thetamin=minval, thetamax=maxval)``: Set the limits - in degrees. - - where minval and maxval are the minimum and maximum limits. Values are - wrapped in to the range :math:`[0, 2\pi]` (in radians), so for example - it is possible to do ``set_thetalim(-np.pi / 2, np.pi / 2)`` to have - an axis symmetric around 0. A ValueError is raised if the absolute - angle difference is larger than a full circle. - """ - orig_lim = self.get_xlim() # in radians - if 'thetamin' in kwargs: - kwargs['xmin'] = np.deg2rad(kwargs.pop('thetamin')) - if 'thetamax' in kwargs: - kwargs['xmax'] = np.deg2rad(kwargs.pop('thetamax')) - new_min, new_max = self.set_xlim(*args, **kwargs) - # Parsing all permutations of *args, **kwargs is tricky; it is simpler - # to let set_xlim() do it and then validate the limits. - if abs(new_max - new_min) > 2 * np.pi: - self.set_xlim(orig_lim) # un-accept the change - raise ValueError("The angle range must be less than a full circle") - return tuple(np.rad2deg((new_min, new_max))) - - def set_theta_offset(self, offset): - """ - Set the offset for the location of 0 in radians. - """ - mtx = self._theta_offset.get_matrix() - mtx[0, 2] = offset - self._theta_offset.invalidate() - - def get_theta_offset(self): - """ - Get the offset for the location of 0 in radians. - """ - return self._theta_offset.get_matrix()[0, 2] - - def set_theta_zero_location(self, loc, offset=0.0): - """ - Set the location of theta's zero. - - This simply calls `set_theta_offset` with the correct value in radians. - - Parameters - ---------- - loc : str - May be one of "N", "NW", "W", "SW", "S", "SE", "E", or "NE". - offset : float, default: 0 - An offset in degrees to apply from the specified *loc*. **Note:** - this offset is *always* applied counter-clockwise regardless of - the direction setting. - """ - mapping = { - 'N': np.pi * 0.5, - 'NW': np.pi * 0.75, - 'W': np.pi, - 'SW': np.pi * 1.25, - 'S': np.pi * 1.5, - 'SE': np.pi * 1.75, - 'E': 0, - 'NE': np.pi * 0.25} - return self.set_theta_offset(mapping[loc] + np.deg2rad(offset)) - - def set_theta_direction(self, direction): - """ - Set the direction in which theta increases. - - clockwise, -1: - Theta increases in the clockwise direction - - counterclockwise, anticlockwise, 1: - Theta increases in the counterclockwise direction - """ - mtx = self._direction.get_matrix() - if direction in ('clockwise', -1): - mtx[0, 0] = -1 - elif direction in ('counterclockwise', 'anticlockwise', 1): - mtx[0, 0] = 1 - else: - _api.check_in_list( - [-1, 1, 'clockwise', 'counterclockwise', 'anticlockwise'], - direction=direction) - self._direction.invalidate() - - def get_theta_direction(self): - """ - Get the direction in which theta increases. - - -1: - Theta increases in the clockwise direction - - 1: - Theta increases in the counterclockwise direction - """ - return self._direction.get_matrix()[0, 0] - - def set_rmax(self, rmax): - """ - Set the outer radial limit. - - Parameters - ---------- - rmax : float - """ - self.viewLim.y1 = rmax - - def get_rmax(self): - """ - Returns - ------- - float - Outer radial limit. - """ - return self.viewLim.ymax - - def set_rmin(self, rmin): - """ - Set the inner radial limit. - - Parameters - ---------- - rmin : float - """ - self.viewLim.y0 = rmin - - def get_rmin(self): - """ - Returns - ------- - float - The inner radial limit. - """ - return self.viewLim.ymin - - def set_rorigin(self, rorigin): - """ - Update the radial origin. - - Parameters - ---------- - rorigin : float - """ - self._originViewLim.locked_y0 = rorigin - - def get_rorigin(self): - """ - Returns - ------- - float - """ - return self._originViewLim.y0 - - def get_rsign(self): - return np.sign(self._originViewLim.y1 - self._originViewLim.y0) - - @_api.make_keyword_only("3.6", "emit") - def set_rlim(self, bottom=None, top=None, emit=True, auto=False, **kwargs): - """ - Set the radial axis view limits. - - This function behaves like `.Axes.set_ylim`, but additionally supports - *rmin* and *rmax* as aliases for *bottom* and *top*. - - See Also - -------- - .Axes.set_ylim - """ - if 'rmin' in kwargs: - if bottom is None: - bottom = kwargs.pop('rmin') - else: - raise ValueError('Cannot supply both positional "bottom"' - 'argument and kwarg "rmin"') - if 'rmax' in kwargs: - if top is None: - top = kwargs.pop('rmax') - else: - raise ValueError('Cannot supply both positional "top"' - 'argument and kwarg "rmax"') - return self.set_ylim(bottom=bottom, top=top, emit=emit, auto=auto, - **kwargs) - - def get_rlabel_position(self): - """ - Returns - ------- - float - The theta position of the radius labels in degrees. - """ - return np.rad2deg(self._r_label_position.get_matrix()[0, 2]) - - def set_rlabel_position(self, value): - """ - Update the theta position of the radius labels. - - Parameters - ---------- - value : number - The angular position of the radius labels in degrees. - """ - self._r_label_position.clear().translate(np.deg2rad(value), 0.0) - - def set_yscale(self, *args, **kwargs): - super().set_yscale(*args, **kwargs) - self.yaxis.set_major_locator( - self.RadialLocator(self.yaxis.get_major_locator(), self)) - - def set_rscale(self, *args, **kwargs): - return Axes.set_yscale(self, *args, **kwargs) - - def set_rticks(self, *args, **kwargs): - return Axes.set_yticks(self, *args, **kwargs) - - def set_thetagrids(self, angles, labels=None, fmt=None, **kwargs): - """ - Set the theta gridlines in a polar plot. - - Parameters - ---------- - angles : tuple with floats, degrees - The angles of the theta gridlines. - - labels : tuple with strings or None - The labels to use at each theta gridline. The - `.projections.polar.ThetaFormatter` will be used if None. - - fmt : str or None - Format string used in `matplotlib.ticker.FormatStrFormatter`. - For example '%f'. Note that the angle that is used is in - radians. - - Returns - ------- - lines : list of `.lines.Line2D` - The theta gridlines. - - labels : list of `.text.Text` - The tick labels. - - Other Parameters - ---------------- - **kwargs - *kwargs* are optional `.Text` properties for the labels. - - .. warning:: - - This only sets the properties of the current ticks. - Ticks are not guaranteed to be persistent. Various operations - can create, delete and modify the Tick instances. There is an - imminent risk that these settings can get lost if you work on - the figure further (including also panning/zooming on a - displayed figure). - - Use `.set_tick_params` instead if possible. - - See Also - -------- - .PolarAxes.set_rgrids - .Axis.get_gridlines - .Axis.get_ticklabels - """ - - # Make sure we take into account unitized data - angles = self.convert_yunits(angles) - angles = np.deg2rad(angles) - self.set_xticks(angles) - if labels is not None: - self.set_xticklabels(labels) - elif fmt is not None: - self.xaxis.set_major_formatter(mticker.FormatStrFormatter(fmt)) - for t in self.xaxis.get_ticklabels(): - t._internal_update(kwargs) - return self.xaxis.get_ticklines(), self.xaxis.get_ticklabels() - - def set_rgrids(self, radii, labels=None, angle=None, fmt=None, **kwargs): - """ - Set the radial gridlines on a polar plot. - - Parameters - ---------- - radii : tuple with floats - The radii for the radial gridlines - - labels : tuple with strings or None - The labels to use at each radial gridline. The - `matplotlib.ticker.ScalarFormatter` will be used if None. - - angle : float - The angular position of the radius labels in degrees. - - fmt : str or None - Format string used in `matplotlib.ticker.FormatStrFormatter`. - For example '%f'. - - Returns - ------- - lines : list of `.lines.Line2D` - The radial gridlines. - - labels : list of `.text.Text` - The tick labels. - - Other Parameters - ---------------- - **kwargs - *kwargs* are optional `.Text` properties for the labels. - - .. warning:: - - This only sets the properties of the current ticks. - Ticks are not guaranteed to be persistent. Various operations - can create, delete and modify the Tick instances. There is an - imminent risk that these settings can get lost if you work on - the figure further (including also panning/zooming on a - displayed figure). - - Use `.set_tick_params` instead if possible. - - See Also - -------- - .PolarAxes.set_thetagrids - .Axis.get_gridlines - .Axis.get_ticklabels - """ - # Make sure we take into account unitized data - radii = self.convert_xunits(radii) - radii = np.asarray(radii) - - self.set_yticks(radii) - if labels is not None: - self.set_yticklabels(labels) - elif fmt is not None: - self.yaxis.set_major_formatter(mticker.FormatStrFormatter(fmt)) - if angle is None: - angle = self.get_rlabel_position() - self.set_rlabel_position(angle) - for t in self.yaxis.get_ticklabels(): - t._internal_update(kwargs) - return self.yaxis.get_gridlines(), self.yaxis.get_ticklabels() - - def format_coord(self, theta, r): - # docstring inherited - screen_xy = self.transData.transform((theta, r)) - screen_xys = screen_xy + np.stack( - np.meshgrid([-1, 0, 1], [-1, 0, 1])).reshape((2, -1)).T - ts, rs = self.transData.inverted().transform(screen_xys).T - delta_t = abs((ts - theta + np.pi) % (2 * np.pi) - np.pi).max() - delta_t_halfturns = delta_t / np.pi - delta_t_degrees = delta_t_halfturns * 180 - delta_r = abs(rs - r).max() - if theta < 0: - theta += 2 * np.pi - theta_halfturns = theta / np.pi - theta_degrees = theta_halfturns * 180 - - # See ScalarFormatter.format_data_short. For r, use #g-formatting - # (as for linear axes), but for theta, use f-formatting as scientific - # notation doesn't make sense and the trailing dot is ugly. - def format_sig(value, delta, opt, fmt): - # For "f", only count digits after decimal point. - prec = (max(0, -math.floor(math.log10(delta))) if fmt == "f" else - cbook._g_sig_digits(value, delta)) - return f"{value:-{opt}.{prec}{fmt}}" - - return ('\N{GREEK SMALL LETTER THETA}={}\N{GREEK SMALL LETTER PI} ' - '({}\N{DEGREE SIGN}), r={}').format( - format_sig(theta_halfturns, delta_t_halfturns, "", "f"), - format_sig(theta_degrees, delta_t_degrees, "", "f"), - format_sig(r, delta_r, "#", "g"), - ) - - def get_data_ratio(self): - """ - Return the aspect ratio of the data itself. For a polar plot, - this should always be 1.0 - """ - return 1.0 - - # # # Interactive panning - - def can_zoom(self): - """ - Return whether this Axes supports the zoom box button functionality. - - A polar Axes does not support zoom boxes. - """ - return False - - def can_pan(self): - """ - Return whether this Axes supports the pan/zoom button functionality. - - For a polar Axes, this is slightly misleading. Both panning and - zooming are performed by the same button. Panning is performed - in azimuth while zooming is done along the radial. - """ - return True - - def start_pan(self, x, y, button): - angle = np.deg2rad(self.get_rlabel_position()) - mode = '' - if button == 1: - epsilon = np.pi / 45.0 - t, r = self.transData.inverted().transform((x, y)) - if angle - epsilon <= t <= angle + epsilon: - mode = 'drag_r_labels' - elif button == 3: - mode = 'zoom' - - self._pan_start = types.SimpleNamespace( - rmax=self.get_rmax(), - trans=self.transData.frozen(), - trans_inverse=self.transData.inverted().frozen(), - r_label_angle=self.get_rlabel_position(), - x=x, - y=y, - mode=mode) - - def end_pan(self): - del self._pan_start - - def drag_pan(self, button, key, x, y): - p = self._pan_start - - if p.mode == 'drag_r_labels': - (startt, startr), (t, r) = p.trans_inverse.transform( - [(p.x, p.y), (x, y)]) - - # Deal with theta - dt = np.rad2deg(startt - t) - self.set_rlabel_position(p.r_label_angle - dt) - - trans, vert1, horiz1 = self.get_yaxis_text1_transform(0.0) - trans, vert2, horiz2 = self.get_yaxis_text2_transform(0.0) - for t in self.yaxis.majorTicks + self.yaxis.minorTicks: - t.label1.set_va(vert1) - t.label1.set_ha(horiz1) - t.label2.set_va(vert2) - t.label2.set_ha(horiz2) - - elif p.mode == 'zoom': - (startt, startr), (t, r) = p.trans_inverse.transform( - [(p.x, p.y), (x, y)]) - - # Deal with r - scale = r / startr - self.set_rmax(p.rmax / scale) - - -# To keep things all self-contained, we can put aliases to the Polar classes -# defined above. This isn't strictly necessary, but it makes some of the -# code more readable, and provides a backwards compatible Polar API. In -# particular, this is used by the :doc:`/gallery/specialty_plots/radar_chart` -# example to override PolarTransform on a PolarAxes subclass, so make sure that -# that example is unaffected before changing this. -PolarAxes.PolarTransform = PolarTransform -PolarAxes.PolarAffine = PolarAffine -PolarAxes.InvertedPolarTransform = InvertedPolarTransform -PolarAxes.ThetaFormatter = ThetaFormatter -PolarAxes.RadialLocator = RadialLocator -PolarAxes.ThetaLocator = ThetaLocator diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/pylab.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/pylab.py deleted file mode 100644 index 62fced0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/pylab.py +++ /dev/null @@ -1,55 +0,0 @@ -""" -.. warning:: - Since heavily importing into the global namespace may result in unexpected - behavior, the use of pylab is strongly discouraged. Use `matplotlib.pyplot` - instead. - -`pylab` is a module that includes `matplotlib.pyplot`, `numpy`, `numpy.fft`, -`numpy.linalg`, `numpy.random`, and some additional functions, all within -a single namespace. Its original purpose was to mimic a MATLAB-like way -of working by importing all functions into the global namespace. This is -considered bad style nowadays. -""" - -from matplotlib.cbook import flatten, silent_list - -import matplotlib as mpl - -from matplotlib.dates import ( - date2num, num2date, datestr2num, drange, DateFormatter, DateLocator, - RRuleLocator, YearLocator, MonthLocator, WeekdayLocator, DayLocator, - HourLocator, MinuteLocator, SecondLocator, rrule, MO, TU, WE, TH, FR, - SA, SU, YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, SECONDLY, - relativedelta) - -# bring all the symbols in so folks can import them from -# pylab in one fell swoop - -## We are still importing too many things from mlab; more cleanup is needed. - -from matplotlib.mlab import ( - detrend, detrend_linear, detrend_mean, detrend_none, window_hanning, - window_none) - -from matplotlib import cbook, mlab, pyplot as plt -from matplotlib.pyplot import * - -from numpy import * -from numpy.fft import * -from numpy.random import * -from numpy.linalg import * - -import numpy as np -import numpy.ma as ma - -# don't let numpy's datetime hide stdlib -import datetime - -# This is needed, or bytes will be numpy.random.bytes from -# "from numpy.random import *" above -bytes = __import__("builtins").bytes -# We also don't want the numpy version of these functions -abs = __import__("builtins").abs -max = __import__("builtins").max -min = __import__("builtins").min -round = __import__("builtins").round diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/pyplot.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/pyplot.py deleted file mode 100644 index 80a75ea..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/pyplot.py +++ /dev/null @@ -1,3322 +0,0 @@ -# Note: The first part of this file can be modified in place, but the latter -# part is autogenerated by the boilerplate.py script. - -""" -`matplotlib.pyplot` is a state-based interface to matplotlib. It provides -an implicit, MATLAB-like, way of plotting. It also opens figures on your -screen, and acts as the figure GUI manager. - -pyplot is mainly intended for interactive plots and simple cases of -programmatic plot generation:: - - import numpy as np - import matplotlib.pyplot as plt - - x = np.arange(0, 5, 0.1) - y = np.sin(x) - plt.plot(x, y) - -The explicit object-oriented API is recommended for complex plots, though -pyplot is still usually used to create the figure and often the axes in the -figure. See `.pyplot.figure`, `.pyplot.subplots`, and -`.pyplot.subplot_mosaic` to create figures, and -:doc:`Axes API ` for the plotting methods on an Axes:: - - import numpy as np - import matplotlib.pyplot as plt - - x = np.arange(0, 5, 0.1) - y = np.sin(x) - fig, ax = plt.subplots() - ax.plot(x, y) - - -See :ref:`api_interfaces` for an explanation of the tradeoffs between the -implicit and explicit interfaces. -""" - -from contextlib import ExitStack -from enum import Enum -import functools -import importlib -import inspect -import logging -from numbers import Number -import re -import sys -import threading -import time - -from cycler import cycler -import matplotlib -import matplotlib.colorbar -import matplotlib.image -from matplotlib import _api -from matplotlib import rcsetup, style -from matplotlib import _pylab_helpers, interactive -from matplotlib import cbook -from matplotlib import _docstring -from matplotlib.backend_bases import ( - FigureCanvasBase, FigureManagerBase, MouseButton) -from matplotlib.figure import Figure, FigureBase, figaspect -from matplotlib.gridspec import GridSpec, SubplotSpec -from matplotlib import rcParams, rcParamsDefault, get_backend, rcParamsOrig -from matplotlib.rcsetup import interactive_bk as _interactive_bk -from matplotlib.artist import Artist -from matplotlib.axes import Axes, Subplot -from matplotlib.projections import PolarAxes -from matplotlib import mlab # for detrend_none, window_hanning -from matplotlib.scale import get_scale_names - -from matplotlib import cm -from matplotlib.cm import _colormaps as colormaps, register_cmap -from matplotlib.colors import _color_sequences as color_sequences - -import numpy as np - -# We may not need the following imports here: -from matplotlib.colors import Normalize -from matplotlib.lines import Line2D -from matplotlib.text import Text, Annotation -from matplotlib.patches import Polygon, Rectangle, Circle, Arrow -from matplotlib.widgets import Button, Slider, Widget - -from .ticker import ( - TickHelper, Formatter, FixedFormatter, NullFormatter, FuncFormatter, - FormatStrFormatter, ScalarFormatter, LogFormatter, LogFormatterExponent, - LogFormatterMathtext, Locator, IndexLocator, FixedLocator, NullLocator, - LinearLocator, LogLocator, AutoLocator, MultipleLocator, MaxNLocator) - -_log = logging.getLogger(__name__) - - -def _copy_docstring_and_deprecators(method, func=None): - if func is None: - return functools.partial(_copy_docstring_and_deprecators, method) - decorators = [_docstring.copy(method)] - # Check whether the definition of *method* includes @_api.rename_parameter - # or @_api.make_keyword_only decorators; if so, propagate them to the - # pyplot wrapper as well. - while getattr(method, "__wrapped__", None) is not None: - decorator = _api.deprecation.DECORATORS.get(method) - if decorator: - decorators.append(decorator) - method = method.__wrapped__ - for decorator in decorators[::-1]: - func = decorator(func) - return func - - -## Global ## - - -# The state controlled by {,un}install_repl_displayhook(). -_ReplDisplayHook = Enum("_ReplDisplayHook", ["NONE", "PLAIN", "IPYTHON"]) -_REPL_DISPLAYHOOK = _ReplDisplayHook.NONE - - -def _draw_all_if_interactive(): - if matplotlib.is_interactive(): - draw_all() - - -def install_repl_displayhook(): - """ - Connect to the display hook of the current shell. - - The display hook gets called when the read-evaluate-print-loop (REPL) of - the shell has finished the execution of a command. We use this callback - to be able to automatically update a figure in interactive mode. - - This works both with IPython and with vanilla python shells. - """ - global _REPL_DISPLAYHOOK - - if _REPL_DISPLAYHOOK is _ReplDisplayHook.IPYTHON: - return - - # See if we have IPython hooks around, if so use them. - # Use ``sys.modules.get(name)`` rather than ``name in sys.modules`` as - # entries can also have been explicitly set to None. - mod_ipython = sys.modules.get("IPython") - if not mod_ipython: - _REPL_DISPLAYHOOK = _ReplDisplayHook.PLAIN - return - ip = mod_ipython.get_ipython() - if not ip: - _REPL_DISPLAYHOOK = _ReplDisplayHook.PLAIN - return - - ip.events.register("post_execute", _draw_all_if_interactive) - _REPL_DISPLAYHOOK = _ReplDisplayHook.IPYTHON - - from IPython.core.pylabtools import backend2gui - # trigger IPython's eventloop integration, if available - ipython_gui_name = backend2gui.get(get_backend()) - if ipython_gui_name: - ip.enable_gui(ipython_gui_name) - - -def uninstall_repl_displayhook(): - """Disconnect from the display hook of the current shell.""" - global _REPL_DISPLAYHOOK - if _REPL_DISPLAYHOOK is _ReplDisplayHook.IPYTHON: - from IPython import get_ipython - ip = get_ipython() - ip.events.unregister("post_execute", _draw_all_if_interactive) - _REPL_DISPLAYHOOK = _ReplDisplayHook.NONE - - -draw_all = _pylab_helpers.Gcf.draw_all - - -@_copy_docstring_and_deprecators(matplotlib.set_loglevel) -def set_loglevel(*args, **kwargs): # Ensure this appears in the pyplot docs. - return matplotlib.set_loglevel(*args, **kwargs) - - -@_copy_docstring_and_deprecators(Artist.findobj) -def findobj(o=None, match=None, include_self=True): - if o is None: - o = gcf() - return o.findobj(match, include_self=include_self) - - -def _get_required_interactive_framework(backend_mod): - if not hasattr(getattr(backend_mod, "FigureCanvas", None), - "required_interactive_framework"): - _api.warn_deprecated( - "3.6", name="Support for FigureCanvases without a " - "required_interactive_framework attribute") - return None - # Inline this once the deprecation elapses. - return backend_mod.FigureCanvas.required_interactive_framework - -_backend_mod = None - - -def _get_backend_mod(): - """ - Ensure that a backend is selected and return it. - - This is currently private, but may be made public in the future. - """ - if _backend_mod is None: - # Use rcParams._get("backend") to avoid going through the fallback - # logic (which will (re)import pyplot and then call switch_backend if - # we need to resolve the auto sentinel) - switch_backend(rcParams._get("backend")) - return _backend_mod - - -def switch_backend(newbackend): - """ - Set the pyplot backend. - - Switching to an interactive backend is possible only if no event loop for - another interactive backend has started. Switching to and from - non-interactive backends is always possible. - - If the new backend is different than the current backend then all open - Figures will be closed via ``plt.close('all')``. - - Parameters - ---------- - newbackend : str - The case-insensitive name of the backend to use. - - """ - global _backend_mod - # make sure the init is pulled up so we can assign to it later - import matplotlib.backends - - if newbackend is rcsetup._auto_backend_sentinel: - current_framework = cbook._get_running_interactive_framework() - mapping = {'qt': 'qtagg', - 'gtk3': 'gtk3agg', - 'gtk4': 'gtk4agg', - 'wx': 'wxagg', - 'tk': 'tkagg', - 'macosx': 'macosx', - 'headless': 'agg'} - - best_guess = mapping.get(current_framework, None) - if best_guess is not None: - candidates = [best_guess] - else: - candidates = [] - candidates += [ - "macosx", "qtagg", "gtk4agg", "gtk3agg", "tkagg", "wxagg"] - - # Don't try to fallback on the cairo-based backends as they each have - # an additional dependency (pycairo) over the agg-based backend, and - # are of worse quality. - for candidate in candidates: - try: - switch_backend(candidate) - except ImportError: - continue - else: - rcParamsOrig['backend'] = candidate - return - else: - # Switching to Agg should always succeed; if it doesn't, let the - # exception propagate out. - switch_backend("agg") - rcParamsOrig["backend"] = "agg" - return - # have to escape the switch on access logic - old_backend = dict.__getitem__(rcParams, 'backend') - - backend_mod = importlib.import_module( - cbook._backend_module_name(newbackend)) - - required_framework = _get_required_interactive_framework(backend_mod) - if required_framework is not None: - current_framework = cbook._get_running_interactive_framework() - if (current_framework and required_framework - and current_framework != required_framework): - raise ImportError( - "Cannot load backend {!r} which requires the {!r} interactive " - "framework, as {!r} is currently running".format( - newbackend, required_framework, current_framework)) - - # Load the new_figure_manager() and show() functions from the backend. - - # Classically, backends can directly export these functions. This should - # keep working for backcompat. - new_figure_manager = getattr(backend_mod, "new_figure_manager", None) - show = getattr(backend_mod, "show", None) - - # In that classical approach, backends are implemented as modules, but - # "inherit" default method implementations from backend_bases._Backend. - # This is achieved by creating a "class" that inherits from - # backend_bases._Backend and whose body is filled with the module globals. - class backend_mod(matplotlib.backend_bases._Backend): - locals().update(vars(backend_mod)) - - # However, the newer approach for defining new_figure_manager and - # show is to derive them from canvas methods. In that case, also - # update backend_mod accordingly; also, per-backend customization of - # draw_if_interactive is disabled. - if new_figure_manager is None: - # Only try to get the canvas class if have opted into the new scheme. - canvas_class = backend_mod.FigureCanvas - - def new_figure_manager_given_figure(num, figure): - return canvas_class.new_manager(figure, num) - - def new_figure_manager(num, *args, FigureClass=Figure, **kwargs): - fig = FigureClass(*args, **kwargs) - return new_figure_manager_given_figure(num, fig) - - def draw_if_interactive(): - if matplotlib.is_interactive(): - manager = _pylab_helpers.Gcf.get_active() - if manager: - manager.canvas.draw_idle() - - backend_mod.new_figure_manager_given_figure = \ - new_figure_manager_given_figure - backend_mod.new_figure_manager = new_figure_manager - backend_mod.draw_if_interactive = draw_if_interactive - - # If the manager explicitly overrides pyplot_show, use it even if a global - # show is already present, as the latter may be here for backcompat. - manager_class = getattr(getattr(backend_mod, "FigureCanvas", None), - "manager_class", None) - # We can't compare directly manager_class.pyplot_show and FMB.pyplot_show because - # pyplot_show is a classmethod so the above constructs are bound classmethods, and - # thus always different (being bound to different classes). We also have to use - # getattr_static instead of vars as manager_class could have no __dict__. - manager_pyplot_show = inspect.getattr_static(manager_class, "pyplot_show", None) - base_pyplot_show = inspect.getattr_static(FigureManagerBase, "pyplot_show", None) - if (show is None - or (manager_pyplot_show is not None - and manager_pyplot_show != base_pyplot_show)): - backend_mod.show = manager_class.pyplot_show - - _log.debug("Loaded backend %s version %s.", - newbackend, backend_mod.backend_version) - - rcParams['backend'] = rcParamsDefault['backend'] = newbackend - _backend_mod = backend_mod - for func_name in ["new_figure_manager", "draw_if_interactive", "show"]: - globals()[func_name].__signature__ = inspect.signature( - getattr(backend_mod, func_name)) - - # Need to keep a global reference to the backend for compatibility reasons. - # See https://github.com/matplotlib/matplotlib/issues/6092 - matplotlib.backends.backend = newbackend - if not cbook._str_equal(old_backend, newbackend): - close("all") - - # make sure the repl display hook is installed in case we become - # interactive - install_repl_displayhook() - - -def _warn_if_gui_out_of_main_thread(): - warn = False - if _get_required_interactive_framework(_get_backend_mod()): - if hasattr(threading, 'get_native_id'): - # This compares native thread ids because even if Python-level - # Thread objects match, the underlying OS thread (which is what - # really matters) may be different on Python implementations with - # green threads. - if threading.get_native_id() != threading.main_thread().native_id: - warn = True - else: - # Fall back to Python-level Thread if native IDs are unavailable, - # mainly for PyPy. - if threading.current_thread() is not threading.main_thread(): - warn = True - if warn: - _api.warn_external( - "Starting a Matplotlib GUI outside of the main thread will likely " - "fail.") - - -# This function's signature is rewritten upon backend-load by switch_backend. -def new_figure_manager(*args, **kwargs): - """Create a new figure manager instance.""" - _warn_if_gui_out_of_main_thread() - return _get_backend_mod().new_figure_manager(*args, **kwargs) - - -# This function's signature is rewritten upon backend-load by switch_backend. -def draw_if_interactive(*args, **kwargs): - """ - Redraw the current figure if in interactive mode. - - .. warning:: - - End users will typically not have to call this function because the - the interactive mode takes care of this. - """ - return _get_backend_mod().draw_if_interactive(*args, **kwargs) - - -# This function's signature is rewritten upon backend-load by switch_backend. -def show(*args, **kwargs): - """ - Display all open figures. - - Parameters - ---------- - block : bool, optional - Whether to wait for all figures to be closed before returning. - - If `True` block and run the GUI main loop until all figure windows - are closed. - - If `False` ensure that all figure windows are displayed and return - immediately. In this case, you are responsible for ensuring - that the event loop is running to have responsive figures. - - Defaults to True in non-interactive mode and to False in interactive - mode (see `.pyplot.isinteractive`). - - See Also - -------- - ion : Enable interactive mode, which shows / updates the figure after - every plotting command, so that calling ``show()`` is not necessary. - ioff : Disable interactive mode. - savefig : Save the figure to an image file instead of showing it on screen. - - Notes - ----- - **Saving figures to file and showing a window at the same time** - - If you want an image file as well as a user interface window, use - `.pyplot.savefig` before `.pyplot.show`. At the end of (a blocking) - ``show()`` the figure is closed and thus unregistered from pyplot. Calling - `.pyplot.savefig` afterwards would save a new and thus empty figure. This - limitation of command order does not apply if the show is non-blocking or - if you keep a reference to the figure and use `.Figure.savefig`. - - **Auto-show in jupyter notebooks** - - The jupyter backends (activated via ``%matplotlib inline``, - ``%matplotlib notebook``, or ``%matplotlib widget``), call ``show()`` at - the end of every cell by default. Thus, you usually don't have to call it - explicitly there. - """ - _warn_if_gui_out_of_main_thread() - return _get_backend_mod().show(*args, **kwargs) - - -def isinteractive(): - """ - Return whether plots are updated after every plotting command. - - The interactive mode is mainly useful if you build plots from the command - line and want to see the effect of each command while you are building the - figure. - - In interactive mode: - - - newly created figures will be shown immediately; - - figures will automatically redraw on change; - - `.pyplot.show` will not block by default. - - In non-interactive mode: - - - newly created figures and changes to figures will not be reflected until - explicitly asked to be; - - `.pyplot.show` will block by default. - - See Also - -------- - ion : Enable interactive mode. - ioff : Disable interactive mode. - show : Show all figures (and maybe block). - pause : Show all figures, and block for a time. - """ - return matplotlib.is_interactive() - - -def ioff(): - """ - Disable interactive mode. - - See `.pyplot.isinteractive` for more details. - - See Also - -------- - ion : Enable interactive mode. - isinteractive : Whether interactive mode is enabled. - show : Show all figures (and maybe block). - pause : Show all figures, and block for a time. - - Notes - ----- - For a temporary change, this can be used as a context manager:: - - # if interactive mode is on - # then figures will be shown on creation - plt.ion() - # This figure will be shown immediately - fig = plt.figure() - - with plt.ioff(): - # interactive mode will be off - # figures will not automatically be shown - fig2 = plt.figure() - # ... - - To enable optional usage as a context manager, this function returns a - `~contextlib.ExitStack` object, which is not intended to be stored or - accessed by the user. - """ - stack = ExitStack() - stack.callback(ion if isinteractive() else ioff) - matplotlib.interactive(False) - uninstall_repl_displayhook() - return stack - - -def ion(): - """ - Enable interactive mode. - - See `.pyplot.isinteractive` for more details. - - See Also - -------- - ioff : Disable interactive mode. - isinteractive : Whether interactive mode is enabled. - show : Show all figures (and maybe block). - pause : Show all figures, and block for a time. - - Notes - ----- - For a temporary change, this can be used as a context manager:: - - # if interactive mode is off - # then figures will not be shown on creation - plt.ioff() - # This figure will not be shown immediately - fig = plt.figure() - - with plt.ion(): - # interactive mode will be on - # figures will automatically be shown - fig2 = plt.figure() - # ... - - To enable optional usage as a context manager, this function returns a - `~contextlib.ExitStack` object, which is not intended to be stored or - accessed by the user. - """ - stack = ExitStack() - stack.callback(ion if isinteractive() else ioff) - matplotlib.interactive(True) - install_repl_displayhook() - return stack - - -def pause(interval): - """ - Run the GUI event loop for *interval* seconds. - - If there is an active figure, it will be updated and displayed before the - pause, and the GUI event loop (if any) will run during the pause. - - This can be used for crude animation. For more complex animation use - :mod:`matplotlib.animation`. - - If there is no active figure, sleep for *interval* seconds instead. - - See Also - -------- - matplotlib.animation : Proper animations - show : Show all figures and optional block until all figures are closed. - """ - manager = _pylab_helpers.Gcf.get_active() - if manager is not None: - canvas = manager.canvas - if canvas.figure.stale: - canvas.draw_idle() - show(block=False) - canvas.start_event_loop(interval) - else: - time.sleep(interval) - - -@_copy_docstring_and_deprecators(matplotlib.rc) -def rc(group, **kwargs): - matplotlib.rc(group, **kwargs) - - -@_copy_docstring_and_deprecators(matplotlib.rc_context) -def rc_context(rc=None, fname=None): - return matplotlib.rc_context(rc, fname) - - -@_copy_docstring_and_deprecators(matplotlib.rcdefaults) -def rcdefaults(): - matplotlib.rcdefaults() - if matplotlib.is_interactive(): - draw_all() - - -# getp/get/setp are explicitly reexported so that they show up in pyplot docs. - - -@_copy_docstring_and_deprecators(matplotlib.artist.getp) -def getp(obj, *args, **kwargs): - return matplotlib.artist.getp(obj, *args, **kwargs) - - -@_copy_docstring_and_deprecators(matplotlib.artist.get) -def get(obj, *args, **kwargs): - return matplotlib.artist.get(obj, *args, **kwargs) - - -@_copy_docstring_and_deprecators(matplotlib.artist.setp) -def setp(obj, *args, **kwargs): - return matplotlib.artist.setp(obj, *args, **kwargs) - - -def xkcd(scale=1, length=100, randomness=2): - """ - Turn on `xkcd `_ sketch-style drawing mode. This will - only have effect on things drawn after this function is called. - - For best results, the "Humor Sans" font should be installed: it is - not included with Matplotlib. - - Parameters - ---------- - scale : float, optional - The amplitude of the wiggle perpendicular to the source line. - length : float, optional - The length of the wiggle along the line. - randomness : float, optional - The scale factor by which the length is shrunken or expanded. - - Notes - ----- - This function works by a number of rcParams, so it will probably - override others you have set before. - - If you want the effects of this function to be temporary, it can - be used as a context manager, for example:: - - with plt.xkcd(): - # This figure will be in XKCD-style - fig1 = plt.figure() - # ... - - # This figure will be in regular style - fig2 = plt.figure() - """ - # This cannot be implemented in terms of contextmanager() or rc_context() - # because this needs to work as a non-contextmanager too. - - if rcParams['text.usetex']: - raise RuntimeError( - "xkcd mode is not compatible with text.usetex = True") - - stack = ExitStack() - stack.callback(dict.update, rcParams, rcParams.copy()) - - from matplotlib import patheffects - rcParams.update({ - 'font.family': ['xkcd', 'xkcd Script', 'Humor Sans', 'Comic Neue', - 'Comic Sans MS'], - 'font.size': 14.0, - 'path.sketch': (scale, length, randomness), - 'path.effects': [ - patheffects.withStroke(linewidth=4, foreground="w")], - 'axes.linewidth': 1.5, - 'lines.linewidth': 2.0, - 'figure.facecolor': 'white', - 'grid.linewidth': 0.0, - 'axes.grid': False, - 'axes.unicode_minus': False, - 'axes.edgecolor': 'black', - 'xtick.major.size': 8, - 'xtick.major.width': 3, - 'ytick.major.size': 8, - 'ytick.major.width': 3, - }) - - return stack - - -## Figures ## - -@_api.make_keyword_only("3.6", "facecolor") -def figure(num=None, # autoincrement if None, else integer from 1-N - figsize=None, # defaults to rc figure.figsize - dpi=None, # defaults to rc figure.dpi - facecolor=None, # defaults to rc figure.facecolor - edgecolor=None, # defaults to rc figure.edgecolor - frameon=True, - FigureClass=Figure, - clear=False, - **kwargs - ): - """ - Create a new figure, or activate an existing figure. - - Parameters - ---------- - num : int or str or `.Figure` or `.SubFigure`, optional - A unique identifier for the figure. - - If a figure with that identifier already exists, this figure is made - active and returned. An integer refers to the ``Figure.number`` - attribute, a string refers to the figure label. - - If there is no figure with the identifier or *num* is not given, a new - figure is created, made active and returned. If *num* is an int, it - will be used for the ``Figure.number`` attribute, otherwise, an - auto-generated integer value is used (starting at 1 and incremented - for each new figure). If *num* is a string, the figure label and the - window title is set to this value. If num is a ``SubFigure``, its - parent ``Figure`` is activated. - - figsize : (float, float), default: :rc:`figure.figsize` - Width, height in inches. - - dpi : float, default: :rc:`figure.dpi` - The resolution of the figure in dots-per-inch. - - facecolor : color, default: :rc:`figure.facecolor` - The background color. - - edgecolor : color, default: :rc:`figure.edgecolor` - The border color. - - frameon : bool, default: True - If False, suppress drawing the figure frame. - - FigureClass : subclass of `~matplotlib.figure.Figure` - If set, an instance of this subclass will be created, rather than a - plain `.Figure`. - - clear : bool, default: False - If True and the figure already exists, then it is cleared. - - layout : {'constrained', 'compressed', 'tight', 'none', `.LayoutEngine`, None}, \ -default: None - The layout mechanism for positioning of plot elements to avoid - overlapping Axes decorations (labels, ticks, etc). Note that layout - managers can measurably slow down figure display. - - - 'constrained': The constrained layout solver adjusts axes sizes - to avoid overlapping axes decorations. Can handle complex plot - layouts and colorbars, and is thus recommended. - - See :doc:`/tutorials/intermediate/constrainedlayout_guide` - for examples. - - - 'compressed': uses the same algorithm as 'constrained', but - removes extra space between fixed-aspect-ratio Axes. Best for - simple grids of axes. - - - 'tight': Use the tight layout mechanism. This is a relatively - simple algorithm that adjusts the subplot parameters so that - decorations do not overlap. See `.Figure.set_tight_layout` for - further details. - - - 'none': Do not use a layout engine. - - - A `.LayoutEngine` instance. Builtin layout classes are - `.ConstrainedLayoutEngine` and `.TightLayoutEngine`, more easily - accessible by 'constrained' and 'tight'. Passing an instance - allows third parties to provide their own layout engine. - - If not given, fall back to using the parameters *tight_layout* and - *constrained_layout*, including their config defaults - :rc:`figure.autolayout` and :rc:`figure.constrained_layout.use`. - - **kwargs - Additional keyword arguments are passed to the `.Figure` constructor. - - Returns - ------- - `~matplotlib.figure.Figure` - - Notes - ----- - A newly created figure is passed to the `~.FigureCanvasBase.new_manager` - method or the `new_figure_manager` function provided by the current - backend, which install a canvas and a manager on the figure. - - Once this is done, :rc:`figure.hooks` are called, one at a time, on the - figure; these hooks allow arbitrary customization of the figure (e.g., - attaching callbacks) or of associated elements (e.g., modifying the - toolbar). See :doc:`/gallery/user_interfaces/mplcvd` for an example of - toolbar customization. - - If you are creating many figures, make sure you explicitly call - `.pyplot.close` on the figures you are not using, because this will - enable pyplot to properly clean up the memory. - - `~matplotlib.rcParams` defines the default values, which can be modified - in the matplotlibrc file. - """ - if isinstance(num, FigureBase): - if num.canvas.manager is None: - raise ValueError("The passed figure is not managed by pyplot") - _pylab_helpers.Gcf.set_active(num.canvas.manager) - return num.figure - - allnums = get_fignums() - next_num = max(allnums) + 1 if allnums else 1 - fig_label = '' - if num is None: - num = next_num - elif isinstance(num, str): - fig_label = num - all_labels = get_figlabels() - if fig_label not in all_labels: - if fig_label == 'all': - _api.warn_external("close('all') closes all existing figures.") - num = next_num - else: - inum = all_labels.index(fig_label) - num = allnums[inum] - else: - num = int(num) # crude validation of num argument - - manager = _pylab_helpers.Gcf.get_fig_manager(num) - if manager is None: - max_open_warning = rcParams['figure.max_open_warning'] - if len(allnums) == max_open_warning >= 1: - _api.warn_external( - f"More than {max_open_warning} figures have been opened. " - f"Figures created through the pyplot interface " - f"(`matplotlib.pyplot.figure`) are retained until explicitly " - f"closed and may consume too much memory. (To control this " - f"warning, see the rcParam `figure.max_open_warning`). " - f"Consider using `matplotlib.pyplot.close()`.", - RuntimeWarning) - - manager = new_figure_manager( - num, figsize=figsize, dpi=dpi, - facecolor=facecolor, edgecolor=edgecolor, frameon=frameon, - FigureClass=FigureClass, **kwargs) - fig = manager.canvas.figure - if fig_label: - fig.set_label(fig_label) - - for hookspecs in rcParams["figure.hooks"]: - module_name, dotted_name = hookspecs.split(":") - obj = importlib.import_module(module_name) - for part in dotted_name.split("."): - obj = getattr(obj, part) - obj(fig) - - _pylab_helpers.Gcf._set_new_active_manager(manager) - - # make sure backends (inline) that we don't ship that expect this - # to be called in plotting commands to make the figure call show - # still work. There is probably a better way to do this in the - # FigureManager base class. - draw_if_interactive() - - if _REPL_DISPLAYHOOK is _ReplDisplayHook.PLAIN: - fig.stale_callback = _auto_draw_if_interactive - - if clear: - manager.canvas.figure.clear() - - return manager.canvas.figure - - -def _auto_draw_if_interactive(fig, val): - """ - An internal helper function for making sure that auto-redrawing - works as intended in the plain python repl. - - Parameters - ---------- - fig : Figure - A figure object which is assumed to be associated with a canvas - """ - if (val and matplotlib.is_interactive() - and not fig.canvas.is_saving() - and not fig.canvas._is_idle_drawing): - # Some artists can mark themselves as stale in the middle of drawing - # (e.g. axes position & tick labels being computed at draw time), but - # this shouldn't trigger a redraw because the current redraw will - # already take them into account. - with fig.canvas._idle_draw_cntx(): - fig.canvas.draw_idle() - - -def gcf(): - """ - Get the current figure. - - If there is currently no figure on the pyplot figure stack, a new one is - created using `~.pyplot.figure()`. (To test whether there is currently a - figure on the pyplot figure stack, check whether `~.pyplot.get_fignums()` - is empty.) - """ - manager = _pylab_helpers.Gcf.get_active() - if manager is not None: - return manager.canvas.figure - else: - return figure() - - -def fignum_exists(num): - """Return whether the figure with the given id exists.""" - return _pylab_helpers.Gcf.has_fignum(num) or num in get_figlabels() - - -def get_fignums(): - """Return a list of existing figure numbers.""" - return sorted(_pylab_helpers.Gcf.figs) - - -def get_figlabels(): - """Return a list of existing figure labels.""" - managers = _pylab_helpers.Gcf.get_all_fig_managers() - managers.sort(key=lambda m: m.num) - return [m.canvas.figure.get_label() for m in managers] - - -def get_current_fig_manager(): - """ - Return the figure manager of the current figure. - - The figure manager is a container for the actual backend-depended window - that displays the figure on screen. - - If no current figure exists, a new one is created, and its figure - manager is returned. - - Returns - ------- - `.FigureManagerBase` or backend-dependent subclass thereof - """ - return gcf().canvas.manager - - -@_copy_docstring_and_deprecators(FigureCanvasBase.mpl_connect) -def connect(s, func): - return gcf().canvas.mpl_connect(s, func) - - -@_copy_docstring_and_deprecators(FigureCanvasBase.mpl_disconnect) -def disconnect(cid): - return gcf().canvas.mpl_disconnect(cid) - - -def close(fig=None): - """ - Close a figure window. - - Parameters - ---------- - fig : None or int or str or `.Figure` - The figure to close. There are a number of ways to specify this: - - - *None*: the current figure - - `.Figure`: the given `.Figure` instance - - ``int``: a figure number - - ``str``: a figure name - - 'all': all figures - - """ - if fig is None: - manager = _pylab_helpers.Gcf.get_active() - if manager is None: - return - else: - _pylab_helpers.Gcf.destroy(manager) - elif fig == 'all': - _pylab_helpers.Gcf.destroy_all() - elif isinstance(fig, int): - _pylab_helpers.Gcf.destroy(fig) - elif hasattr(fig, 'int'): - # if we are dealing with a type UUID, we - # can use its integer representation - _pylab_helpers.Gcf.destroy(fig.int) - elif isinstance(fig, str): - all_labels = get_figlabels() - if fig in all_labels: - num = get_fignums()[all_labels.index(fig)] - _pylab_helpers.Gcf.destroy(num) - elif isinstance(fig, Figure): - _pylab_helpers.Gcf.destroy_fig(fig) - else: - raise TypeError("close() argument must be a Figure, an int, a string, " - "or None, not %s" % type(fig)) - - -def clf(): - """Clear the current figure.""" - gcf().clear() - - -def draw(): - """ - Redraw the current figure. - - This is used to update a figure that has been altered, but not - automatically re-drawn. If interactive mode is on (via `.ion()`), this - should be only rarely needed, but there may be ways to modify the state of - a figure without marking it as "stale". Please report these cases as bugs. - - This is equivalent to calling ``fig.canvas.draw_idle()``, where ``fig`` is - the current figure. - - See Also - -------- - .FigureCanvasBase.draw_idle - .FigureCanvasBase.draw - """ - gcf().canvas.draw_idle() - - -@_copy_docstring_and_deprecators(Figure.savefig) -def savefig(*args, **kwargs): - fig = gcf() - res = fig.savefig(*args, **kwargs) - fig.canvas.draw_idle() # Need this if 'transparent=True', to reset colors. - return res - - -## Putting things in figures ## - - -def figlegend(*args, **kwargs): - return gcf().legend(*args, **kwargs) -if Figure.legend.__doc__: - figlegend.__doc__ = Figure.legend.__doc__ \ - .replace(" legend(", " figlegend(") \ - .replace("fig.legend(", "plt.figlegend(") \ - .replace("ax.plot(", "plt.plot(") - - -## Axes ## - -@_docstring.dedent_interpd -def axes(arg=None, **kwargs): - """ - Add an Axes to the current figure and make it the current Axes. - - Call signatures:: - - plt.axes() - plt.axes(rect, projection=None, polar=False, **kwargs) - plt.axes(ax) - - Parameters - ---------- - arg : None or 4-tuple - The exact behavior of this function depends on the type: - - - *None*: A new full window Axes is added using - ``subplot(**kwargs)``. - - 4-tuple of floats *rect* = ``[left, bottom, width, height]``. - A new Axes is added with dimensions *rect* in normalized - (0, 1) units using `~.Figure.add_axes` on the current figure. - - projection : {None, 'aitoff', 'hammer', 'lambert', 'mollweide', \ -'polar', 'rectilinear', str}, optional - The projection type of the `~.axes.Axes`. *str* is the name of - a custom projection, see `~matplotlib.projections`. The default - None results in a 'rectilinear' projection. - - polar : bool, default: False - If True, equivalent to projection='polar'. - - sharex, sharey : `~matplotlib.axes.Axes`, optional - Share the x or y `~matplotlib.axis` with sharex and/or sharey. - The axis will have the same limits, ticks, and scale as the axis - of the shared Axes. - - label : str - A label for the returned Axes. - - Returns - ------- - `~.axes.Axes`, or a subclass of `~.axes.Axes` - The returned axes class depends on the projection used. It is - `~.axes.Axes` if rectilinear projection is used and - `.projections.polar.PolarAxes` if polar projection is used. - - Other Parameters - ---------------- - **kwargs - This method also takes the keyword arguments for - the returned Axes class. The keyword arguments for the - rectilinear Axes class `~.axes.Axes` can be found in - the following table but there might also be other keyword - arguments if another projection is used, see the actual Axes - class. - - %(Axes:kwdoc)s - - See Also - -------- - .Figure.add_axes - .pyplot.subplot - .Figure.add_subplot - .Figure.subplots - .pyplot.subplots - - Examples - -------- - :: - - # Creating a new full window Axes - plt.axes() - - # Creating a new Axes with specified dimensions and a grey background - plt.axes((left, bottom, width, height), facecolor='grey') - """ - fig = gcf() - pos = kwargs.pop('position', None) - if arg is None: - if pos is None: - return fig.add_subplot(**kwargs) - else: - return fig.add_axes(pos, **kwargs) - else: - return fig.add_axes(arg, **kwargs) - - -def delaxes(ax=None): - """ - Remove an `~.axes.Axes` (defaulting to the current axes) from its figure. - """ - if ax is None: - ax = gca() - ax.remove() - - -def sca(ax): - """ - Set the current Axes to *ax* and the current Figure to the parent of *ax*. - """ - figure(ax.figure) - ax.figure.sca(ax) - - -def cla(): - """Clear the current axes.""" - # Not generated via boilerplate.py to allow a different docstring. - return gca().cla() - - -## More ways of creating axes ## - -@_docstring.dedent_interpd -def subplot(*args, **kwargs): - """ - Add an Axes to the current figure or retrieve an existing Axes. - - This is a wrapper of `.Figure.add_subplot` which provides additional - behavior when working with the implicit API (see the notes section). - - Call signatures:: - - subplot(nrows, ncols, index, **kwargs) - subplot(pos, **kwargs) - subplot(**kwargs) - subplot(ax) - - Parameters - ---------- - *args : int, (int, int, *index*), or `.SubplotSpec`, default: (1, 1, 1) - The position of the subplot described by one of - - - Three integers (*nrows*, *ncols*, *index*). The subplot will take the - *index* position on a grid with *nrows* rows and *ncols* columns. - *index* starts at 1 in the upper left corner and increases to the - right. *index* can also be a two-tuple specifying the (*first*, - *last*) indices (1-based, and including *last*) of the subplot, e.g., - ``fig.add_subplot(3, 1, (1, 2))`` makes a subplot that spans the - upper 2/3 of the figure. - - A 3-digit integer. The digits are interpreted as if given separately - as three single-digit integers, i.e. ``fig.add_subplot(235)`` is the - same as ``fig.add_subplot(2, 3, 5)``. Note that this can only be used - if there are no more than 9 subplots. - - A `.SubplotSpec`. - - projection : {None, 'aitoff', 'hammer', 'lambert', 'mollweide', \ -'polar', 'rectilinear', str}, optional - The projection type of the subplot (`~.axes.Axes`). *str* is the name - of a custom projection, see `~matplotlib.projections`. The default - None results in a 'rectilinear' projection. - - polar : bool, default: False - If True, equivalent to projection='polar'. - - sharex, sharey : `~matplotlib.axes.Axes`, optional - Share the x or y `~matplotlib.axis` with sharex and/or sharey. The - axis will have the same limits, ticks, and scale as the axis of the - shared axes. - - label : str - A label for the returned axes. - - Returns - ------- - `~.axes.Axes` - - The Axes of the subplot. The returned Axes can actually be an instance - of a subclass, such as `.projections.polar.PolarAxes` for polar - projections. - - Other Parameters - ---------------- - **kwargs - This method also takes the keyword arguments for the returned axes - base class; except for the *figure* argument. The keyword arguments - for the rectilinear base class `~.axes.Axes` can be found in - the following table but there might also be other keyword - arguments if another projection is used. - - %(Axes:kwdoc)s - - Notes - ----- - Creating a new Axes will delete any preexisting Axes that - overlaps with it beyond sharing a boundary:: - - import matplotlib.pyplot as plt - # plot a line, implicitly creating a subplot(111) - plt.plot([1, 2, 3]) - # now create a subplot which represents the top plot of a grid - # with 2 rows and 1 column. Since this subplot will overlap the - # first, the plot (and its axes) previously created, will be removed - plt.subplot(211) - - If you do not want this behavior, use the `.Figure.add_subplot` method - or the `.pyplot.axes` function instead. - - If no *kwargs* are passed and there exists an Axes in the location - specified by *args* then that Axes will be returned rather than a new - Axes being created. - - If *kwargs* are passed and there exists an Axes in the location - specified by *args*, the projection type is the same, and the - *kwargs* match with the existing Axes, then the existing Axes is - returned. Otherwise a new Axes is created with the specified - parameters. We save a reference to the *kwargs* which we use - for this comparison. If any of the values in *kwargs* are - mutable we will not detect the case where they are mutated. - In these cases we suggest using `.Figure.add_subplot` and the - explicit Axes API rather than the implicit pyplot API. - - See Also - -------- - .Figure.add_subplot - .pyplot.subplots - .pyplot.axes - .Figure.subplots - - Examples - -------- - :: - - plt.subplot(221) - - # equivalent but more general - ax1 = plt.subplot(2, 2, 1) - - # add a subplot with no frame - ax2 = plt.subplot(222, frameon=False) - - # add a polar subplot - plt.subplot(223, projection='polar') - - # add a red subplot that shares the x-axis with ax1 - plt.subplot(224, sharex=ax1, facecolor='red') - - # delete ax2 from the figure - plt.delaxes(ax2) - - # add ax2 to the figure again - plt.subplot(ax2) - - # make the first axes "current" again - plt.subplot(221) - - """ - # Here we will only normalize `polar=True` vs `projection='polar'` and let - # downstream code deal with the rest. - unset = object() - projection = kwargs.get('projection', unset) - polar = kwargs.pop('polar', unset) - if polar is not unset and polar: - # if we got mixed messages from the user, raise - if projection is not unset and projection != 'polar': - raise ValueError( - f"polar={polar}, yet projection={projection!r}. " - "Only one of these arguments should be supplied." - ) - kwargs['projection'] = projection = 'polar' - - # if subplot called without arguments, create subplot(1, 1, 1) - if len(args) == 0: - args = (1, 1, 1) - - # This check was added because it is very easy to type subplot(1, 2, False) - # when subplots(1, 2, False) was intended (sharex=False, that is). In most - # cases, no error will ever occur, but mysterious behavior can result - # because what was intended to be the sharex argument is instead treated as - # a subplot index for subplot() - if len(args) >= 3 and isinstance(args[2], bool): - _api.warn_external("The subplot index argument to subplot() appears " - "to be a boolean. Did you intend to use " - "subplots()?") - # Check for nrows and ncols, which are not valid subplot args: - if 'nrows' in kwargs or 'ncols' in kwargs: - raise TypeError("subplot() got an unexpected keyword argument 'ncols' " - "and/or 'nrows'. Did you intend to call subplots()?") - - fig = gcf() - - # First, search for an existing subplot with a matching spec. - key = SubplotSpec._from_subplot_args(fig, args) - - for ax in fig.axes: - # if we found an Axes at the position sort out if we can re-use it - if ax.get_subplotspec() == key: - # if the user passed no kwargs, re-use - if kwargs == {}: - break - # if the axes class and kwargs are identical, reuse - elif ax._projection_init == fig._process_projection_requirements( - *args, **kwargs - ): - break - else: - # we have exhausted the known Axes and none match, make a new one! - ax = fig.add_subplot(*args, **kwargs) - - fig.sca(ax) - - axes_to_delete = [other for other in fig.axes - if other != ax and ax.bbox.fully_overlaps(other.bbox)] - if axes_to_delete: - _api.warn_deprecated( - "3.6", message="Auto-removal of overlapping axes is deprecated " - "since %(since)s and will be removed %(removal)s; explicitly call " - "ax.remove() as needed.") - for ax_to_del in axes_to_delete: - delaxes(ax_to_del) - - return ax - - -def subplots(nrows=1, ncols=1, *, sharex=False, sharey=False, squeeze=True, - width_ratios=None, height_ratios=None, - subplot_kw=None, gridspec_kw=None, **fig_kw): - """ - Create a figure and a set of subplots. - - This utility wrapper makes it convenient to create common layouts of - subplots, including the enclosing figure object, in a single call. - - Parameters - ---------- - nrows, ncols : int, default: 1 - Number of rows/columns of the subplot grid. - - sharex, sharey : bool or {'none', 'all', 'row', 'col'}, default: False - Controls sharing of properties among x (*sharex*) or y (*sharey*) - axes: - - - True or 'all': x- or y-axis will be shared among all subplots. - - False or 'none': each subplot x- or y-axis will be independent. - - 'row': each subplot row will share an x- or y-axis. - - 'col': each subplot column will share an x- or y-axis. - - When subplots have a shared x-axis along a column, only the x tick - labels of the bottom subplot are created. Similarly, when subplots - have a shared y-axis along a row, only the y tick labels of the first - column subplot are created. To later turn other subplots' ticklabels - on, use `~matplotlib.axes.Axes.tick_params`. - - When subplots have a shared axis that has units, calling - `~matplotlib.axis.Axis.set_units` will update each axis with the - new units. - - squeeze : bool, default: True - - If True, extra dimensions are squeezed out from the returned - array of `~matplotlib.axes.Axes`: - - - if only one subplot is constructed (nrows=ncols=1), the - resulting single Axes object is returned as a scalar. - - for Nx1 or 1xM subplots, the returned object is a 1D numpy - object array of Axes objects. - - for NxM, subplots with N>1 and M>1 are returned as a 2D array. - - - If False, no squeezing at all is done: the returned Axes object is - always a 2D array containing Axes instances, even if it ends up - being 1x1. - - width_ratios : array-like of length *ncols*, optional - Defines the relative widths of the columns. Each column gets a - relative width of ``width_ratios[i] / sum(width_ratios)``. - If not given, all columns will have the same width. Equivalent - to ``gridspec_kw={'width_ratios': [...]}``. - - height_ratios : array-like of length *nrows*, optional - Defines the relative heights of the rows. Each row gets a - relative height of ``height_ratios[i] / sum(height_ratios)``. - If not given, all rows will have the same height. Convenience - for ``gridspec_kw={'height_ratios': [...]}``. - - subplot_kw : dict, optional - Dict with keywords passed to the - `~matplotlib.figure.Figure.add_subplot` call used to create each - subplot. - - gridspec_kw : dict, optional - Dict with keywords passed to the `~matplotlib.gridspec.GridSpec` - constructor used to create the grid the subplots are placed on. - - **fig_kw - All additional keyword arguments are passed to the - `.pyplot.figure` call. - - Returns - ------- - fig : `.Figure` - - ax : `~matplotlib.axes.Axes` or array of Axes - *ax* can be either a single `~.axes.Axes` object, or an array of Axes - objects if more than one subplot was created. The dimensions of the - resulting array can be controlled with the squeeze keyword, see above. - - Typical idioms for handling the return value are:: - - # using the variable ax for single a Axes - fig, ax = plt.subplots() - - # using the variable axs for multiple Axes - fig, axs = plt.subplots(2, 2) - - # using tuple unpacking for multiple Axes - fig, (ax1, ax2) = plt.subplots(1, 2) - fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2) - - The names ``ax`` and pluralized ``axs`` are preferred over ``axes`` - because for the latter it's not clear if it refers to a single - `~.axes.Axes` instance or a collection of these. - - See Also - -------- - .pyplot.figure - .pyplot.subplot - .pyplot.axes - .Figure.subplots - .Figure.add_subplot - - Examples - -------- - :: - - # First create some toy data: - x = np.linspace(0, 2*np.pi, 400) - y = np.sin(x**2) - - # Create just a figure and only one subplot - fig, ax = plt.subplots() - ax.plot(x, y) - ax.set_title('Simple plot') - - # Create two subplots and unpack the output array immediately - f, (ax1, ax2) = plt.subplots(1, 2, sharey=True) - ax1.plot(x, y) - ax1.set_title('Sharing Y axis') - ax2.scatter(x, y) - - # Create four polar axes and access them through the returned array - fig, axs = plt.subplots(2, 2, subplot_kw=dict(projection="polar")) - axs[0, 0].plot(x, y) - axs[1, 1].scatter(x, y) - - # Share a X axis with each column of subplots - plt.subplots(2, 2, sharex='col') - - # Share a Y axis with each row of subplots - plt.subplots(2, 2, sharey='row') - - # Share both X and Y axes with all subplots - plt.subplots(2, 2, sharex='all', sharey='all') - - # Note that this is the same as - plt.subplots(2, 2, sharex=True, sharey=True) - - # Create figure number 10 with a single subplot - # and clears it if it already exists. - fig, ax = plt.subplots(num=10, clear=True) - - """ - fig = figure(**fig_kw) - axs = fig.subplots(nrows=nrows, ncols=ncols, sharex=sharex, sharey=sharey, - squeeze=squeeze, subplot_kw=subplot_kw, - gridspec_kw=gridspec_kw, height_ratios=height_ratios, - width_ratios=width_ratios) - return fig, axs - - -def subplot_mosaic(mosaic, *, sharex=False, sharey=False, - width_ratios=None, height_ratios=None, empty_sentinel='.', - subplot_kw=None, gridspec_kw=None, - per_subplot_kw=None, **fig_kw): - """ - Build a layout of Axes based on ASCII art or nested lists. - - This is a helper function to build complex GridSpec layouts visually. - - See :doc:`/gallery/subplots_axes_and_figures/mosaic` - for an example and full API documentation - - Parameters - ---------- - mosaic : list of list of {hashable or nested} or str - - A visual layout of how you want your Axes to be arranged - labeled as strings. For example :: - - x = [['A panel', 'A panel', 'edge'], - ['C panel', '.', 'edge']] - - produces 4 axes: - - - 'A panel' which is 1 row high and spans the first two columns - - 'edge' which is 2 rows high and is on the right edge - - 'C panel' which in 1 row and 1 column wide in the bottom left - - a blank space 1 row and 1 column wide in the bottom center - - Any of the entries in the layout can be a list of lists - of the same form to create nested layouts. - - If input is a str, then it must be of the form :: - - ''' - AAE - C.E - ''' - - where each character is a column and each line is a row. - This only allows only single character Axes labels and does - not allow nesting but is very terse. - - sharex, sharey : bool, default: False - If True, the x-axis (*sharex*) or y-axis (*sharey*) will be shared - among all subplots. In that case, tick label visibility and axis units - behave as for `subplots`. If False, each subplot's x- or y-axis will - be independent. - - width_ratios : array-like of length *ncols*, optional - Defines the relative widths of the columns. Each column gets a - relative width of ``width_ratios[i] / sum(width_ratios)``. - If not given, all columns will have the same width. Convenience - for ``gridspec_kw={'width_ratios': [...]}``. - - height_ratios : array-like of length *nrows*, optional - Defines the relative heights of the rows. Each row gets a - relative height of ``height_ratios[i] / sum(height_ratios)``. - If not given, all rows will have the same height. Convenience - for ``gridspec_kw={'height_ratios': [...]}``. - - empty_sentinel : object, optional - Entry in the layout to mean "leave this space empty". Defaults - to ``'.'``. Note, if *layout* is a string, it is processed via - `inspect.cleandoc` to remove leading white space, which may - interfere with using white-space as the empty sentinel. - - subplot_kw : dict, optional - Dictionary with keywords passed to the `.Figure.add_subplot` call - used to create each subplot. These values may be overridden by - values in *per_subplot_kw*. - - per_subplot_kw : dict, optional - A dictionary mapping the Axes identifiers or tuples of identifiers - to a dictionary of keyword arguments to be passed to the - `.Figure.add_subplot` call used to create each subplot. The values - in these dictionaries have precedence over the values in - *subplot_kw*. - - If *mosaic* is a string, and thus all keys are single characters, - it is possible to use a single string instead of a tuple as keys; - i.e. ``"AB"`` is equivalent to ``("A", "B")``. - - .. versionadded:: 3.7 - - gridspec_kw : dict, optional - Dictionary with keywords passed to the `.GridSpec` constructor used - to create the grid the subplots are placed on. - - **fig_kw - All additional keyword arguments are passed to the - `.pyplot.figure` call. - - Returns - ------- - fig : `.Figure` - The new figure - - dict[label, Axes] - A dictionary mapping the labels to the Axes objects. The order of - the axes is left-to-right and top-to-bottom of their position in the - total layout. - - """ - fig = figure(**fig_kw) - ax_dict = fig.subplot_mosaic( - mosaic, sharex=sharex, sharey=sharey, - height_ratios=height_ratios, width_ratios=width_ratios, - subplot_kw=subplot_kw, gridspec_kw=gridspec_kw, - empty_sentinel=empty_sentinel, - per_subplot_kw=per_subplot_kw, - ) - return fig, ax_dict - - -def subplot2grid(shape, loc, rowspan=1, colspan=1, fig=None, **kwargs): - """ - Create a subplot at a specific location inside a regular grid. - - Parameters - ---------- - shape : (int, int) - Number of rows and of columns of the grid in which to place axis. - loc : (int, int) - Row number and column number of the axis location within the grid. - rowspan : int, default: 1 - Number of rows for the axis to span downwards. - colspan : int, default: 1 - Number of columns for the axis to span to the right. - fig : `.Figure`, optional - Figure to place the subplot in. Defaults to the current figure. - **kwargs - Additional keyword arguments are handed to `~.Figure.add_subplot`. - - Returns - ------- - `~.axes.Axes` - - The Axes of the subplot. The returned Axes can actually be an instance - of a subclass, such as `.projections.polar.PolarAxes` for polar - projections. - - Notes - ----- - The following call :: - - ax = subplot2grid((nrows, ncols), (row, col), rowspan, colspan) - - is identical to :: - - fig = gcf() - gs = fig.add_gridspec(nrows, ncols) - ax = fig.add_subplot(gs[row:row+rowspan, col:col+colspan]) - """ - - if fig is None: - fig = gcf() - - rows, cols = shape - gs = GridSpec._check_gridspec_exists(fig, rows, cols) - - subplotspec = gs.new_subplotspec(loc, rowspan=rowspan, colspan=colspan) - ax = fig.add_subplot(subplotspec, **kwargs) - - axes_to_delete = [other for other in fig.axes - if other != ax and ax.bbox.fully_overlaps(other.bbox)] - if axes_to_delete: - _api.warn_deprecated( - "3.6", message="Auto-removal of overlapping axes is deprecated " - "since %(since)s and will be removed %(removal)s; explicitly call " - "ax.remove() as needed.") - for ax_to_del in axes_to_delete: - delaxes(ax_to_del) - - return ax - - -def twinx(ax=None): - """ - Make and return a second axes that shares the *x*-axis. The new axes will - overlay *ax* (or the current axes if *ax* is *None*), and its ticks will be - on the right. - - Examples - -------- - :doc:`/gallery/subplots_axes_and_figures/two_scales` - """ - if ax is None: - ax = gca() - ax1 = ax.twinx() - return ax1 - - -def twiny(ax=None): - """ - Make and return a second axes that shares the *y*-axis. The new axes will - overlay *ax* (or the current axes if *ax* is *None*), and its ticks will be - on the top. - - Examples - -------- - :doc:`/gallery/subplots_axes_and_figures/two_scales` - """ - if ax is None: - ax = gca() - ax1 = ax.twiny() - return ax1 - - -def subplot_tool(targetfig=None): - """ - Launch a subplot tool window for a figure. - - Returns - ------- - `matplotlib.widgets.SubplotTool` - """ - if targetfig is None: - targetfig = gcf() - tb = targetfig.canvas.manager.toolbar - if hasattr(tb, "configure_subplots"): # toolbar2 - return tb.configure_subplots() - elif hasattr(tb, "trigger_tool"): # toolmanager - return tb.trigger_tool("subplots") - else: - raise ValueError("subplot_tool can only be launched for figures with " - "an associated toolbar") - - -def box(on=None): - """ - Turn the axes box on or off on the current axes. - - Parameters - ---------- - on : bool or None - The new `~matplotlib.axes.Axes` box state. If ``None``, toggle - the state. - - See Also - -------- - :meth:`matplotlib.axes.Axes.set_frame_on` - :meth:`matplotlib.axes.Axes.get_frame_on` - """ - ax = gca() - if on is None: - on = not ax.get_frame_on() - ax.set_frame_on(on) - -## Axis ## - - -def xlim(*args, **kwargs): - """ - Get or set the x limits of the current axes. - - Call signatures:: - - left, right = xlim() # return the current xlim - xlim((left, right)) # set the xlim to left, right - xlim(left, right) # set the xlim to left, right - - If you do not specify args, you can pass *left* or *right* as kwargs, - i.e.:: - - xlim(right=3) # adjust the right leaving left unchanged - xlim(left=1) # adjust the left leaving right unchanged - - Setting limits turns autoscaling off for the x-axis. - - Returns - ------- - left, right - A tuple of the new x-axis limits. - - Notes - ----- - Calling this function with no arguments (e.g. ``xlim()``) is the pyplot - equivalent of calling `~.Axes.get_xlim` on the current axes. - Calling this function with arguments is the pyplot equivalent of calling - `~.Axes.set_xlim` on the current axes. All arguments are passed though. - """ - ax = gca() - if not args and not kwargs: - return ax.get_xlim() - ret = ax.set_xlim(*args, **kwargs) - return ret - - -def ylim(*args, **kwargs): - """ - Get or set the y-limits of the current axes. - - Call signatures:: - - bottom, top = ylim() # return the current ylim - ylim((bottom, top)) # set the ylim to bottom, top - ylim(bottom, top) # set the ylim to bottom, top - - If you do not specify args, you can alternatively pass *bottom* or - *top* as kwargs, i.e.:: - - ylim(top=3) # adjust the top leaving bottom unchanged - ylim(bottom=1) # adjust the bottom leaving top unchanged - - Setting limits turns autoscaling off for the y-axis. - - Returns - ------- - bottom, top - A tuple of the new y-axis limits. - - Notes - ----- - Calling this function with no arguments (e.g. ``ylim()``) is the pyplot - equivalent of calling `~.Axes.get_ylim` on the current axes. - Calling this function with arguments is the pyplot equivalent of calling - `~.Axes.set_ylim` on the current axes. All arguments are passed though. - """ - ax = gca() - if not args and not kwargs: - return ax.get_ylim() - ret = ax.set_ylim(*args, **kwargs) - return ret - - -def xticks(ticks=None, labels=None, *, minor=False, **kwargs): - """ - Get or set the current tick locations and labels of the x-axis. - - Pass no arguments to return the current values without modifying them. - - Parameters - ---------- - ticks : array-like, optional - The list of xtick locations. Passing an empty list removes all xticks. - labels : array-like, optional - The labels to place at the given *ticks* locations. This argument can - only be passed if *ticks* is passed as well. - minor : bool, default: False - If ``False``, get/set the major ticks/labels; if ``True``, the minor - ticks/labels. - **kwargs - `.Text` properties can be used to control the appearance of the labels. - - Returns - ------- - locs - The list of xtick locations. - labels - The list of xlabel `.Text` objects. - - Notes - ----- - Calling this function with no arguments (e.g. ``xticks()``) is the pyplot - equivalent of calling `~.Axes.get_xticks` and `~.Axes.get_xticklabels` on - the current axes. - Calling this function with arguments is the pyplot equivalent of calling - `~.Axes.set_xticks` and `~.Axes.set_xticklabels` on the current axes. - - Examples - -------- - >>> locs, labels = xticks() # Get the current locations and labels. - >>> xticks(np.arange(0, 1, step=0.2)) # Set label locations. - >>> xticks(np.arange(3), ['Tom', 'Dick', 'Sue']) # Set text labels. - >>> xticks([0, 1, 2], ['January', 'February', 'March'], - ... rotation=20) # Set text labels and properties. - >>> xticks([]) # Disable xticks. - """ - ax = gca() - - if ticks is None: - locs = ax.get_xticks(minor=minor) - if labels is not None: - raise TypeError("xticks(): Parameter 'labels' can't be set " - "without setting 'ticks'") - else: - locs = ax.set_xticks(ticks, minor=minor) - - if labels is None: - labels = ax.get_xticklabels(minor=minor) - for l in labels: - l._internal_update(kwargs) - else: - labels = ax.set_xticklabels(labels, minor=minor, **kwargs) - - return locs, labels - - -def yticks(ticks=None, labels=None, *, minor=False, **kwargs): - """ - Get or set the current tick locations and labels of the y-axis. - - Pass no arguments to return the current values without modifying them. - - Parameters - ---------- - ticks : array-like, optional - The list of ytick locations. Passing an empty list removes all yticks. - labels : array-like, optional - The labels to place at the given *ticks* locations. This argument can - only be passed if *ticks* is passed as well. - minor : bool, default: False - If ``False``, get/set the major ticks/labels; if ``True``, the minor - ticks/labels. - **kwargs - `.Text` properties can be used to control the appearance of the labels. - - Returns - ------- - locs - The list of ytick locations. - labels - The list of ylabel `.Text` objects. - - Notes - ----- - Calling this function with no arguments (e.g. ``yticks()``) is the pyplot - equivalent of calling `~.Axes.get_yticks` and `~.Axes.get_yticklabels` on - the current axes. - Calling this function with arguments is the pyplot equivalent of calling - `~.Axes.set_yticks` and `~.Axes.set_yticklabels` on the current axes. - - Examples - -------- - >>> locs, labels = yticks() # Get the current locations and labels. - >>> yticks(np.arange(0, 1, step=0.2)) # Set label locations. - >>> yticks(np.arange(3), ['Tom', 'Dick', 'Sue']) # Set text labels. - >>> yticks([0, 1, 2], ['January', 'February', 'March'], - ... rotation=45) # Set text labels and properties. - >>> yticks([]) # Disable yticks. - """ - ax = gca() - - if ticks is None: - locs = ax.get_yticks(minor=minor) - if labels is not None: - raise TypeError("yticks(): Parameter 'labels' can't be set " - "without setting 'ticks'") - else: - locs = ax.set_yticks(ticks, minor=minor) - - if labels is None: - labels = ax.get_yticklabels(minor=minor) - for l in labels: - l._internal_update(kwargs) - else: - labels = ax.set_yticklabels(labels, minor=minor, **kwargs) - - return locs, labels - - -def rgrids(radii=None, labels=None, angle=None, fmt=None, **kwargs): - """ - Get or set the radial gridlines on the current polar plot. - - Call signatures:: - - lines, labels = rgrids() - lines, labels = rgrids(radii, labels=None, angle=22.5, fmt=None, **kwargs) - - When called with no arguments, `.rgrids` simply returns the tuple - (*lines*, *labels*). When called with arguments, the labels will - appear at the specified radial distances and angle. - - Parameters - ---------- - radii : tuple with floats - The radii for the radial gridlines - - labels : tuple with strings or None - The labels to use at each radial gridline. The - `matplotlib.ticker.ScalarFormatter` will be used if None. - - angle : float - The angular position of the radius labels in degrees. - - fmt : str or None - Format string used in `matplotlib.ticker.FormatStrFormatter`. - For example '%f'. - - Returns - ------- - lines : list of `.lines.Line2D` - The radial gridlines. - - labels : list of `.text.Text` - The tick labels. - - Other Parameters - ---------------- - **kwargs - *kwargs* are optional `.Text` properties for the labels. - - See Also - -------- - .pyplot.thetagrids - .projections.polar.PolarAxes.set_rgrids - .Axis.get_gridlines - .Axis.get_ticklabels - - Examples - -------- - :: - - # set the locations of the radial gridlines - lines, labels = rgrids( (0.25, 0.5, 1.0) ) - - # set the locations and labels of the radial gridlines - lines, labels = rgrids( (0.25, 0.5, 1.0), ('Tom', 'Dick', 'Harry' )) - """ - ax = gca() - if not isinstance(ax, PolarAxes): - raise RuntimeError('rgrids only defined for polar axes') - if all(p is None for p in [radii, labels, angle, fmt]) and not kwargs: - lines = ax.yaxis.get_gridlines() - labels = ax.yaxis.get_ticklabels() - else: - lines, labels = ax.set_rgrids( - radii, labels=labels, angle=angle, fmt=fmt, **kwargs) - return lines, labels - - -def thetagrids(angles=None, labels=None, fmt=None, **kwargs): - """ - Get or set the theta gridlines on the current polar plot. - - Call signatures:: - - lines, labels = thetagrids() - lines, labels = thetagrids(angles, labels=None, fmt=None, **kwargs) - - When called with no arguments, `.thetagrids` simply returns the tuple - (*lines*, *labels*). When called with arguments, the labels will - appear at the specified angles. - - Parameters - ---------- - angles : tuple with floats, degrees - The angles of the theta gridlines. - - labels : tuple with strings or None - The labels to use at each radial gridline. The - `.projections.polar.ThetaFormatter` will be used if None. - - fmt : str or None - Format string used in `matplotlib.ticker.FormatStrFormatter`. - For example '%f'. Note that the angle in radians will be used. - - Returns - ------- - lines : list of `.lines.Line2D` - The theta gridlines. - - labels : list of `.text.Text` - The tick labels. - - Other Parameters - ---------------- - **kwargs - *kwargs* are optional `.Text` properties for the labels. - - See Also - -------- - .pyplot.rgrids - .projections.polar.PolarAxes.set_thetagrids - .Axis.get_gridlines - .Axis.get_ticklabels - - Examples - -------- - :: - - # set the locations of the angular gridlines - lines, labels = thetagrids(range(45, 360, 90)) - - # set the locations and labels of the angular gridlines - lines, labels = thetagrids(range(45, 360, 90), ('NE', 'NW', 'SW', 'SE')) - """ - ax = gca() - if not isinstance(ax, PolarAxes): - raise RuntimeError('thetagrids only defined for polar axes') - if all(param is None for param in [angles, labels, fmt]) and not kwargs: - lines = ax.xaxis.get_ticklines() - labels = ax.xaxis.get_ticklabels() - else: - lines, labels = ax.set_thetagrids(angles, - labels=labels, fmt=fmt, **kwargs) - return lines, labels - - -@_api.deprecated("3.7", pending=True) -def get_plot_commands(): - """ - Get a sorted list of all of the plotting commands. - """ - NON_PLOT_COMMANDS = { - 'connect', 'disconnect', 'get_current_fig_manager', 'ginput', - 'new_figure_manager', 'waitforbuttonpress'} - return [name for name in _get_pyplot_commands() - if name not in NON_PLOT_COMMANDS] - - -def _get_pyplot_commands(): - # This works by searching for all functions in this module and removing - # a few hard-coded exclusions, as well as all of the colormap-setting - # functions, and anything marked as private with a preceding underscore. - exclude = {'colormaps', 'colors', 'get_plot_commands', *colormaps} - this_module = inspect.getmodule(get_plot_commands) - return sorted( - name for name, obj in globals().items() - if not name.startswith('_') and name not in exclude - and inspect.isfunction(obj) - and inspect.getmodule(obj) is this_module) - - -## Plotting part 1: manually generated functions and wrappers ## - - -@_copy_docstring_and_deprecators(Figure.colorbar) -def colorbar(mappable=None, cax=None, ax=None, **kwargs): - if mappable is None: - mappable = gci() - if mappable is None: - raise RuntimeError('No mappable was found to use for colorbar ' - 'creation. First define a mappable such as ' - 'an image (with imshow) or a contour set (' - 'with contourf).') - ret = gcf().colorbar(mappable, cax=cax, ax=ax, **kwargs) - return ret - - -def clim(vmin=None, vmax=None): - """ - Set the color limits of the current image. - - If either *vmin* or *vmax* is None, the image min/max respectively - will be used for color scaling. - - If you want to set the clim of multiple images, use - `~.ScalarMappable.set_clim` on every image, for example:: - - for im in gca().get_images(): - im.set_clim(0, 0.5) - - """ - im = gci() - if im is None: - raise RuntimeError('You must first define an image, e.g., with imshow') - - im.set_clim(vmin, vmax) - - -# eventually this implementation should move here, use indirection for now to -# avoid having two copies of the code floating around. -def get_cmap(name=None, lut=None): - return cm._get_cmap(name=name, lut=lut) -get_cmap.__doc__ = cm._get_cmap.__doc__ - - -def set_cmap(cmap): - """ - Set the default colormap, and applies it to the current image if any. - - Parameters - ---------- - cmap : `~matplotlib.colors.Colormap` or str - A colormap instance or the name of a registered colormap. - - See Also - -------- - colormaps - matplotlib.cm.register_cmap - matplotlib.cm.get_cmap - """ - cmap = get_cmap(cmap) - - rc('image', cmap=cmap.name) - im = gci() - - if im is not None: - im.set_cmap(cmap) - - -@_copy_docstring_and_deprecators(matplotlib.image.imread) -def imread(fname, format=None): - return matplotlib.image.imread(fname, format) - - -@_copy_docstring_and_deprecators(matplotlib.image.imsave) -def imsave(fname, arr, **kwargs): - return matplotlib.image.imsave(fname, arr, **kwargs) - - -def matshow(A, fignum=None, **kwargs): - """ - Display an array as a matrix in a new figure window. - - The origin is set at the upper left hand corner and rows (first - dimension of the array) are displayed horizontally. The aspect - ratio of the figure window is that of the array, unless this would - make an excessively short or narrow figure. - - Tick labels for the xaxis are placed on top. - - Parameters - ---------- - A : 2D array-like - The matrix to be displayed. - - fignum : None or int or False - If *None*, create a new figure window with automatic numbering. - - If a nonzero integer, draw into the figure with the given number - (create it if it does not exist). - - If 0, use the current axes (or create one if it does not exist). - - .. note:: - - Because of how `.Axes.matshow` tries to set the figure aspect - ratio to be the one of the array, strange things may happen if you - reuse an existing figure. - - Returns - ------- - `~matplotlib.image.AxesImage` - - Other Parameters - ---------------- - **kwargs : `~matplotlib.axes.Axes.imshow` arguments - - """ - A = np.asanyarray(A) - if fignum == 0: - ax = gca() - else: - # Extract actual aspect ratio of array and make appropriately sized - # figure. - fig = figure(fignum, figsize=figaspect(A)) - ax = fig.add_axes([0.15, 0.09, 0.775, 0.775]) - im = ax.matshow(A, **kwargs) - sci(im) - return im - - -def polar(*args, **kwargs): - """ - Make a polar plot. - - call signature:: - - polar(theta, r, **kwargs) - - Multiple *theta*, *r* arguments are supported, with format strings, as in - `plot`. - """ - # If an axis already exists, check if it has a polar projection - if gcf().get_axes(): - ax = gca() - if not isinstance(ax, PolarAxes): - _api.warn_external('Trying to create polar plot on an Axes ' - 'that does not have a polar projection.') - else: - ax = axes(projection="polar") - return ax.plot(*args, **kwargs) - - -# If rcParams['backend_fallback'] is true, and an interactive backend is -# requested, ignore rcParams['backend'] and force selection of a backend that -# is compatible with the current running interactive framework. -if (rcParams["backend_fallback"] - and rcParams._get_backend_or_none() in ( - set(_interactive_bk) - {'WebAgg', 'nbAgg'}) - and cbook._get_running_interactive_framework()): - rcParams._set("backend", rcsetup._auto_backend_sentinel) - - -################# REMAINING CONTENT GENERATED BY boilerplate.py ############## - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Figure.figimage) -def figimage( - X, xo=0, yo=0, alpha=None, norm=None, cmap=None, vmin=None, - vmax=None, origin=None, resize=False, **kwargs): - return gcf().figimage( - X, xo=xo, yo=yo, alpha=alpha, norm=norm, cmap=cmap, vmin=vmin, - vmax=vmax, origin=origin, resize=resize, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Figure.text) -def figtext(x, y, s, fontdict=None, **kwargs): - return gcf().text(x, y, s, fontdict=fontdict, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Figure.gca) -def gca(): - return gcf().gca() - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Figure._gci) -def gci(): - return gcf()._gci() - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Figure.ginput) -def ginput( - n=1, timeout=30, show_clicks=True, - mouse_add=MouseButton.LEFT, mouse_pop=MouseButton.RIGHT, - mouse_stop=MouseButton.MIDDLE): - return gcf().ginput( - n=n, timeout=timeout, show_clicks=show_clicks, - mouse_add=mouse_add, mouse_pop=mouse_pop, - mouse_stop=mouse_stop) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Figure.subplots_adjust) -def subplots_adjust( - left=None, bottom=None, right=None, top=None, wspace=None, - hspace=None): - return gcf().subplots_adjust( - left=left, bottom=bottom, right=right, top=top, wspace=wspace, - hspace=hspace) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Figure.suptitle) -def suptitle(t, **kwargs): - return gcf().suptitle(t, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Figure.tight_layout) -def tight_layout(*, pad=1.08, h_pad=None, w_pad=None, rect=None): - return gcf().tight_layout(pad=pad, h_pad=h_pad, w_pad=w_pad, rect=rect) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Figure.waitforbuttonpress) -def waitforbuttonpress(timeout=-1): - return gcf().waitforbuttonpress(timeout=timeout) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.acorr) -def acorr(x, *, data=None, **kwargs): - return gca().acorr( - x, **({"data": data} if data is not None else {}), **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.angle_spectrum) -def angle_spectrum( - x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, *, - data=None, **kwargs): - return gca().angle_spectrum( - x, Fs=Fs, Fc=Fc, window=window, pad_to=pad_to, sides=sides, - **({"data": data} if data is not None else {}), **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.annotate) -def annotate( - text, xy, xytext=None, xycoords='data', textcoords=None, - arrowprops=None, annotation_clip=None, **kwargs): - return gca().annotate( - text, xy, xytext=xytext, xycoords=xycoords, - textcoords=textcoords, arrowprops=arrowprops, - annotation_clip=annotation_clip, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.arrow) -def arrow(x, y, dx, dy, **kwargs): - return gca().arrow(x, y, dx, dy, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.autoscale) -def autoscale(enable=True, axis='both', tight=None): - return gca().autoscale(enable=enable, axis=axis, tight=tight) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.axhline) -def axhline(y=0, xmin=0, xmax=1, **kwargs): - return gca().axhline(y=y, xmin=xmin, xmax=xmax, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.axhspan) -def axhspan(ymin, ymax, xmin=0, xmax=1, **kwargs): - return gca().axhspan(ymin, ymax, xmin=xmin, xmax=xmax, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.axis) -def axis(arg=None, /, *, emit=True, **kwargs): - return gca().axis(arg, emit=emit, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.axline) -def axline(xy1, xy2=None, *, slope=None, **kwargs): - return gca().axline(xy1, xy2=xy2, slope=slope, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.axvline) -def axvline(x=0, ymin=0, ymax=1, **kwargs): - return gca().axvline(x=x, ymin=ymin, ymax=ymax, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.axvspan) -def axvspan(xmin, xmax, ymin=0, ymax=1, **kwargs): - return gca().axvspan(xmin, xmax, ymin=ymin, ymax=ymax, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.bar) -def bar( - x, height, width=0.8, bottom=None, *, align='center', - data=None, **kwargs): - return gca().bar( - x, height, width=width, bottom=bottom, align=align, - **({"data": data} if data is not None else {}), **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.barbs) -def barbs(*args, data=None, **kwargs): - return gca().barbs( - *args, **({"data": data} if data is not None else {}), - **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.barh) -def barh( - y, width, height=0.8, left=None, *, align='center', - data=None, **kwargs): - return gca().barh( - y, width, height=height, left=left, align=align, - **({"data": data} if data is not None else {}), **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.bar_label) -def bar_label( - container, labels=None, *, fmt='%g', label_type='edge', - padding=0, **kwargs): - return gca().bar_label( - container, labels=labels, fmt=fmt, label_type=label_type, - padding=padding, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.boxplot) -def boxplot( - x, notch=None, sym=None, vert=None, whis=None, - positions=None, widths=None, patch_artist=None, - bootstrap=None, usermedians=None, conf_intervals=None, - meanline=None, showmeans=None, showcaps=None, showbox=None, - showfliers=None, boxprops=None, labels=None, flierprops=None, - medianprops=None, meanprops=None, capprops=None, - whiskerprops=None, manage_ticks=True, autorange=False, - zorder=None, capwidths=None, *, data=None): - return gca().boxplot( - x, notch=notch, sym=sym, vert=vert, whis=whis, - positions=positions, widths=widths, patch_artist=patch_artist, - bootstrap=bootstrap, usermedians=usermedians, - conf_intervals=conf_intervals, meanline=meanline, - showmeans=showmeans, showcaps=showcaps, showbox=showbox, - showfliers=showfliers, boxprops=boxprops, labels=labels, - flierprops=flierprops, medianprops=medianprops, - meanprops=meanprops, capprops=capprops, - whiskerprops=whiskerprops, manage_ticks=manage_ticks, - autorange=autorange, zorder=zorder, capwidths=capwidths, - **({"data": data} if data is not None else {})) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.broken_barh) -def broken_barh(xranges, yrange, *, data=None, **kwargs): - return gca().broken_barh( - xranges, yrange, - **({"data": data} if data is not None else {}), **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.clabel) -def clabel(CS, levels=None, **kwargs): - return gca().clabel(CS, levels=levels, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.cohere) -def cohere( - x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, - window=mlab.window_hanning, noverlap=0, pad_to=None, - sides='default', scale_by_freq=None, *, data=None, **kwargs): - return gca().cohere( - x, y, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, window=window, - noverlap=noverlap, pad_to=pad_to, sides=sides, - scale_by_freq=scale_by_freq, - **({"data": data} if data is not None else {}), **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.contour) -def contour(*args, data=None, **kwargs): - __ret = gca().contour( - *args, **({"data": data} if data is not None else {}), - **kwargs) - if __ret._A is not None: sci(__ret) # noqa - return __ret - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.contourf) -def contourf(*args, data=None, **kwargs): - __ret = gca().contourf( - *args, **({"data": data} if data is not None else {}), - **kwargs) - if __ret._A is not None: sci(__ret) # noqa - return __ret - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.csd) -def csd( - x, y, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, - noverlap=None, pad_to=None, sides=None, scale_by_freq=None, - return_line=None, *, data=None, **kwargs): - return gca().csd( - x, y, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, window=window, - noverlap=noverlap, pad_to=pad_to, sides=sides, - scale_by_freq=scale_by_freq, return_line=return_line, - **({"data": data} if data is not None else {}), **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.errorbar) -def errorbar( - x, y, yerr=None, xerr=None, fmt='', ecolor=None, - elinewidth=None, capsize=None, barsabove=False, lolims=False, - uplims=False, xlolims=False, xuplims=False, errorevery=1, - capthick=None, *, data=None, **kwargs): - return gca().errorbar( - x, y, yerr=yerr, xerr=xerr, fmt=fmt, ecolor=ecolor, - elinewidth=elinewidth, capsize=capsize, barsabove=barsabove, - lolims=lolims, uplims=uplims, xlolims=xlolims, - xuplims=xuplims, errorevery=errorevery, capthick=capthick, - **({"data": data} if data is not None else {}), **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.eventplot) -def eventplot( - positions, orientation='horizontal', lineoffsets=1, - linelengths=1, linewidths=None, colors=None, alpha=None, - linestyles='solid', *, data=None, **kwargs): - return gca().eventplot( - positions, orientation=orientation, lineoffsets=lineoffsets, - linelengths=linelengths, linewidths=linewidths, colors=colors, - alpha=alpha, linestyles=linestyles, - **({"data": data} if data is not None else {}), **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.fill) -def fill(*args, data=None, **kwargs): - return gca().fill( - *args, **({"data": data} if data is not None else {}), - **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.fill_between) -def fill_between( - x, y1, y2=0, where=None, interpolate=False, step=None, *, - data=None, **kwargs): - return gca().fill_between( - x, y1, y2=y2, where=where, interpolate=interpolate, step=step, - **({"data": data} if data is not None else {}), **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.fill_betweenx) -def fill_betweenx( - y, x1, x2=0, where=None, step=None, interpolate=False, *, - data=None, **kwargs): - return gca().fill_betweenx( - y, x1, x2=x2, where=where, step=step, interpolate=interpolate, - **({"data": data} if data is not None else {}), **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.grid) -def grid(visible=None, which='major', axis='both', **kwargs): - return gca().grid(visible=visible, which=which, axis=axis, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.hexbin) -def hexbin( - x, y, C=None, gridsize=100, bins=None, xscale='linear', - yscale='linear', extent=None, cmap=None, norm=None, vmin=None, - vmax=None, alpha=None, linewidths=None, edgecolors='face', - reduce_C_function=np.mean, mincnt=None, marginals=False, *, - data=None, **kwargs): - __ret = gca().hexbin( - x, y, C=C, gridsize=gridsize, bins=bins, xscale=xscale, - yscale=yscale, extent=extent, cmap=cmap, norm=norm, vmin=vmin, - vmax=vmax, alpha=alpha, linewidths=linewidths, - edgecolors=edgecolors, reduce_C_function=reduce_C_function, - mincnt=mincnt, marginals=marginals, - **({"data": data} if data is not None else {}), **kwargs) - sci(__ret) - return __ret - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.hist) -def hist( - x, bins=None, range=None, density=False, weights=None, - cumulative=False, bottom=None, histtype='bar', align='mid', - orientation='vertical', rwidth=None, log=False, color=None, - label=None, stacked=False, *, data=None, **kwargs): - return gca().hist( - x, bins=bins, range=range, density=density, weights=weights, - cumulative=cumulative, bottom=bottom, histtype=histtype, - align=align, orientation=orientation, rwidth=rwidth, log=log, - color=color, label=label, stacked=stacked, - **({"data": data} if data is not None else {}), **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.stairs) -def stairs( - values, edges=None, *, orientation='vertical', baseline=0, - fill=False, data=None, **kwargs): - return gca().stairs( - values, edges=edges, orientation=orientation, - baseline=baseline, fill=fill, - **({"data": data} if data is not None else {}), **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.hist2d) -def hist2d( - x, y, bins=10, range=None, density=False, weights=None, - cmin=None, cmax=None, *, data=None, **kwargs): - __ret = gca().hist2d( - x, y, bins=bins, range=range, density=density, - weights=weights, cmin=cmin, cmax=cmax, - **({"data": data} if data is not None else {}), **kwargs) - sci(__ret[-1]) - return __ret - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.hlines) -def hlines( - y, xmin, xmax, colors=None, linestyles='solid', label='', *, - data=None, **kwargs): - return gca().hlines( - y, xmin, xmax, colors=colors, linestyles=linestyles, - label=label, **({"data": data} if data is not None else {}), - **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.imshow) -def imshow( - X, cmap=None, norm=None, *, aspect=None, interpolation=None, - alpha=None, vmin=None, vmax=None, origin=None, extent=None, - interpolation_stage=None, filternorm=True, filterrad=4.0, - resample=None, url=None, data=None, **kwargs): - __ret = gca().imshow( - X, cmap=cmap, norm=norm, aspect=aspect, - interpolation=interpolation, alpha=alpha, vmin=vmin, - vmax=vmax, origin=origin, extent=extent, - interpolation_stage=interpolation_stage, - filternorm=filternorm, filterrad=filterrad, resample=resample, - url=url, **({"data": data} if data is not None else {}), - **kwargs) - sci(__ret) - return __ret - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.legend) -def legend(*args, **kwargs): - return gca().legend(*args, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.locator_params) -def locator_params(axis='both', tight=None, **kwargs): - return gca().locator_params(axis=axis, tight=tight, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.loglog) -def loglog(*args, **kwargs): - return gca().loglog(*args, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.magnitude_spectrum) -def magnitude_spectrum( - x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, - scale=None, *, data=None, **kwargs): - return gca().magnitude_spectrum( - x, Fs=Fs, Fc=Fc, window=window, pad_to=pad_to, sides=sides, - scale=scale, **({"data": data} if data is not None else {}), - **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.margins) -def margins(*margins, x=None, y=None, tight=True): - return gca().margins(*margins, x=x, y=y, tight=tight) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.minorticks_off) -def minorticks_off(): - return gca().minorticks_off() - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.minorticks_on) -def minorticks_on(): - return gca().minorticks_on() - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.pcolor) -def pcolor( - *args, shading=None, alpha=None, norm=None, cmap=None, - vmin=None, vmax=None, data=None, **kwargs): - __ret = gca().pcolor( - *args, shading=shading, alpha=alpha, norm=norm, cmap=cmap, - vmin=vmin, vmax=vmax, - **({"data": data} if data is not None else {}), **kwargs) - sci(__ret) - return __ret - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.pcolormesh) -def pcolormesh( - *args, alpha=None, norm=None, cmap=None, vmin=None, - vmax=None, shading=None, antialiased=False, data=None, - **kwargs): - __ret = gca().pcolormesh( - *args, alpha=alpha, norm=norm, cmap=cmap, vmin=vmin, - vmax=vmax, shading=shading, antialiased=antialiased, - **({"data": data} if data is not None else {}), **kwargs) - sci(__ret) - return __ret - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.phase_spectrum) -def phase_spectrum( - x, Fs=None, Fc=None, window=None, pad_to=None, sides=None, *, - data=None, **kwargs): - return gca().phase_spectrum( - x, Fs=Fs, Fc=Fc, window=window, pad_to=pad_to, sides=sides, - **({"data": data} if data is not None else {}), **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.pie) -def pie( - x, explode=None, labels=None, colors=None, autopct=None, - pctdistance=0.6, shadow=False, labeldistance=1.1, - startangle=0, radius=1, counterclock=True, wedgeprops=None, - textprops=None, center=(0, 0), frame=False, - rotatelabels=False, *, normalize=True, hatch=None, data=None): - return gca().pie( - x, explode=explode, labels=labels, colors=colors, - autopct=autopct, pctdistance=pctdistance, shadow=shadow, - labeldistance=labeldistance, startangle=startangle, - radius=radius, counterclock=counterclock, - wedgeprops=wedgeprops, textprops=textprops, center=center, - frame=frame, rotatelabels=rotatelabels, normalize=normalize, - hatch=hatch, **({"data": data} if data is not None else {})) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.plot) -def plot(*args, scalex=True, scaley=True, data=None, **kwargs): - return gca().plot( - *args, scalex=scalex, scaley=scaley, - **({"data": data} if data is not None else {}), **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.plot_date) -def plot_date( - x, y, fmt='o', tz=None, xdate=True, ydate=False, *, - data=None, **kwargs): - return gca().plot_date( - x, y, fmt=fmt, tz=tz, xdate=xdate, ydate=ydate, - **({"data": data} if data is not None else {}), **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.psd) -def psd( - x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, - noverlap=None, pad_to=None, sides=None, scale_by_freq=None, - return_line=None, *, data=None, **kwargs): - return gca().psd( - x, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, window=window, - noverlap=noverlap, pad_to=pad_to, sides=sides, - scale_by_freq=scale_by_freq, return_line=return_line, - **({"data": data} if data is not None else {}), **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.quiver) -def quiver(*args, data=None, **kwargs): - __ret = gca().quiver( - *args, **({"data": data} if data is not None else {}), - **kwargs) - sci(__ret) - return __ret - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.quiverkey) -def quiverkey(Q, X, Y, U, label, **kwargs): - return gca().quiverkey(Q, X, Y, U, label, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.scatter) -def scatter( - x, y, s=None, c=None, marker=None, cmap=None, norm=None, - vmin=None, vmax=None, alpha=None, linewidths=None, *, - edgecolors=None, plotnonfinite=False, data=None, **kwargs): - __ret = gca().scatter( - x, y, s=s, c=c, marker=marker, cmap=cmap, norm=norm, - vmin=vmin, vmax=vmax, alpha=alpha, linewidths=linewidths, - edgecolors=edgecolors, plotnonfinite=plotnonfinite, - **({"data": data} if data is not None else {}), **kwargs) - sci(__ret) - return __ret - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.semilogx) -def semilogx(*args, **kwargs): - return gca().semilogx(*args, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.semilogy) -def semilogy(*args, **kwargs): - return gca().semilogy(*args, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.specgram) -def specgram( - x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, - noverlap=None, cmap=None, xextent=None, pad_to=None, - sides=None, scale_by_freq=None, mode=None, scale=None, - vmin=None, vmax=None, *, data=None, **kwargs): - __ret = gca().specgram( - x, NFFT=NFFT, Fs=Fs, Fc=Fc, detrend=detrend, window=window, - noverlap=noverlap, cmap=cmap, xextent=xextent, pad_to=pad_to, - sides=sides, scale_by_freq=scale_by_freq, mode=mode, - scale=scale, vmin=vmin, vmax=vmax, - **({"data": data} if data is not None else {}), **kwargs) - sci(__ret[-1]) - return __ret - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.spy) -def spy( - Z, precision=0, marker=None, markersize=None, aspect='equal', - origin='upper', **kwargs): - __ret = gca().spy( - Z, precision=precision, marker=marker, markersize=markersize, - aspect=aspect, origin=origin, **kwargs) - if isinstance(__ret, cm.ScalarMappable): sci(__ret) # noqa - return __ret - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.stackplot) -def stackplot( - x, *args, labels=(), colors=None, baseline='zero', data=None, - **kwargs): - return gca().stackplot( - x, *args, labels=labels, colors=colors, baseline=baseline, - **({"data": data} if data is not None else {}), **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.stem) -def stem( - *args, linefmt=None, markerfmt=None, basefmt=None, bottom=0, - label=None, - use_line_collection=_api.deprecation._deprecated_parameter, - orientation='vertical', data=None): - return gca().stem( - *args, linefmt=linefmt, markerfmt=markerfmt, basefmt=basefmt, - bottom=bottom, label=label, - use_line_collection=use_line_collection, - orientation=orientation, - **({"data": data} if data is not None else {})) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.step) -def step(x, y, *args, where='pre', data=None, **kwargs): - return gca().step( - x, y, *args, where=where, - **({"data": data} if data is not None else {}), **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.streamplot) -def streamplot( - x, y, u, v, density=1, linewidth=None, color=None, cmap=None, - norm=None, arrowsize=1, arrowstyle='-|>', minlength=0.1, - transform=None, zorder=None, start_points=None, maxlength=4.0, - integration_direction='both', broken_streamlines=True, *, - data=None): - __ret = gca().streamplot( - x, y, u, v, density=density, linewidth=linewidth, color=color, - cmap=cmap, norm=norm, arrowsize=arrowsize, - arrowstyle=arrowstyle, minlength=minlength, - transform=transform, zorder=zorder, start_points=start_points, - maxlength=maxlength, - integration_direction=integration_direction, - broken_streamlines=broken_streamlines, - **({"data": data} if data is not None else {})) - sci(__ret.lines) - return __ret - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.table) -def table( - cellText=None, cellColours=None, cellLoc='right', - colWidths=None, rowLabels=None, rowColours=None, - rowLoc='left', colLabels=None, colColours=None, - colLoc='center', loc='bottom', bbox=None, edges='closed', - **kwargs): - return gca().table( - cellText=cellText, cellColours=cellColours, cellLoc=cellLoc, - colWidths=colWidths, rowLabels=rowLabels, - rowColours=rowColours, rowLoc=rowLoc, colLabels=colLabels, - colColours=colColours, colLoc=colLoc, loc=loc, bbox=bbox, - edges=edges, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.text) -def text(x, y, s, fontdict=None, **kwargs): - return gca().text(x, y, s, fontdict=fontdict, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.tick_params) -def tick_params(axis='both', **kwargs): - return gca().tick_params(axis=axis, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.ticklabel_format) -def ticklabel_format( - *, axis='both', style='', scilimits=None, useOffset=None, - useLocale=None, useMathText=None): - return gca().ticklabel_format( - axis=axis, style=style, scilimits=scilimits, - useOffset=useOffset, useLocale=useLocale, - useMathText=useMathText) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.tricontour) -def tricontour(*args, **kwargs): - __ret = gca().tricontour(*args, **kwargs) - if __ret._A is not None: sci(__ret) # noqa - return __ret - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.tricontourf) -def tricontourf(*args, **kwargs): - __ret = gca().tricontourf(*args, **kwargs) - if __ret._A is not None: sci(__ret) # noqa - return __ret - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.tripcolor) -def tripcolor( - *args, alpha=1.0, norm=None, cmap=None, vmin=None, vmax=None, - shading='flat', facecolors=None, **kwargs): - __ret = gca().tripcolor( - *args, alpha=alpha, norm=norm, cmap=cmap, vmin=vmin, - vmax=vmax, shading=shading, facecolors=facecolors, **kwargs) - sci(__ret) - return __ret - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.triplot) -def triplot(*args, **kwargs): - return gca().triplot(*args, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.violinplot) -def violinplot( - dataset, positions=None, vert=True, widths=0.5, - showmeans=False, showextrema=True, showmedians=False, - quantiles=None, points=100, bw_method=None, *, data=None): - return gca().violinplot( - dataset, positions=positions, vert=vert, widths=widths, - showmeans=showmeans, showextrema=showextrema, - showmedians=showmedians, quantiles=quantiles, points=points, - bw_method=bw_method, - **({"data": data} if data is not None else {})) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.vlines) -def vlines( - x, ymin, ymax, colors=None, linestyles='solid', label='', *, - data=None, **kwargs): - return gca().vlines( - x, ymin, ymax, colors=colors, linestyles=linestyles, - label=label, **({"data": data} if data is not None else {}), - **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.xcorr) -def xcorr( - x, y, normed=True, detrend=mlab.detrend_none, usevlines=True, - maxlags=10, *, data=None, **kwargs): - return gca().xcorr( - x, y, normed=normed, detrend=detrend, usevlines=usevlines, - maxlags=maxlags, - **({"data": data} if data is not None else {}), **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes._sci) -def sci(im): - return gca()._sci(im) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.set_title) -def title(label, fontdict=None, loc=None, pad=None, *, y=None, **kwargs): - return gca().set_title( - label, fontdict=fontdict, loc=loc, pad=pad, y=y, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.set_xlabel) -def xlabel(xlabel, fontdict=None, labelpad=None, *, loc=None, **kwargs): - return gca().set_xlabel( - xlabel, fontdict=fontdict, labelpad=labelpad, loc=loc, - **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.set_ylabel) -def ylabel(ylabel, fontdict=None, labelpad=None, *, loc=None, **kwargs): - return gca().set_ylabel( - ylabel, fontdict=fontdict, labelpad=labelpad, loc=loc, - **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.set_xscale) -def xscale(value, **kwargs): - return gca().set_xscale(value, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -@_copy_docstring_and_deprecators(Axes.set_yscale) -def yscale(value, **kwargs): - return gca().set_yscale(value, **kwargs) - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def autumn(): - """ - Set the colormap to 'autumn'. - - This changes the default colormap as well as the colormap of the current - image if there is one. See ``help(colormaps)`` for more information. - """ - set_cmap('autumn') - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def bone(): - """ - Set the colormap to 'bone'. - - This changes the default colormap as well as the colormap of the current - image if there is one. See ``help(colormaps)`` for more information. - """ - set_cmap('bone') - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def cool(): - """ - Set the colormap to 'cool'. - - This changes the default colormap as well as the colormap of the current - image if there is one. See ``help(colormaps)`` for more information. - """ - set_cmap('cool') - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def copper(): - """ - Set the colormap to 'copper'. - - This changes the default colormap as well as the colormap of the current - image if there is one. See ``help(colormaps)`` for more information. - """ - set_cmap('copper') - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def flag(): - """ - Set the colormap to 'flag'. - - This changes the default colormap as well as the colormap of the current - image if there is one. See ``help(colormaps)`` for more information. - """ - set_cmap('flag') - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def gray(): - """ - Set the colormap to 'gray'. - - This changes the default colormap as well as the colormap of the current - image if there is one. See ``help(colormaps)`` for more information. - """ - set_cmap('gray') - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def hot(): - """ - Set the colormap to 'hot'. - - This changes the default colormap as well as the colormap of the current - image if there is one. See ``help(colormaps)`` for more information. - """ - set_cmap('hot') - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def hsv(): - """ - Set the colormap to 'hsv'. - - This changes the default colormap as well as the colormap of the current - image if there is one. See ``help(colormaps)`` for more information. - """ - set_cmap('hsv') - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def jet(): - """ - Set the colormap to 'jet'. - - This changes the default colormap as well as the colormap of the current - image if there is one. See ``help(colormaps)`` for more information. - """ - set_cmap('jet') - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def pink(): - """ - Set the colormap to 'pink'. - - This changes the default colormap as well as the colormap of the current - image if there is one. See ``help(colormaps)`` for more information. - """ - set_cmap('pink') - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def prism(): - """ - Set the colormap to 'prism'. - - This changes the default colormap as well as the colormap of the current - image if there is one. See ``help(colormaps)`` for more information. - """ - set_cmap('prism') - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def spring(): - """ - Set the colormap to 'spring'. - - This changes the default colormap as well as the colormap of the current - image if there is one. See ``help(colormaps)`` for more information. - """ - set_cmap('spring') - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def summer(): - """ - Set the colormap to 'summer'. - - This changes the default colormap as well as the colormap of the current - image if there is one. See ``help(colormaps)`` for more information. - """ - set_cmap('summer') - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def winter(): - """ - Set the colormap to 'winter'. - - This changes the default colormap as well as the colormap of the current - image if there is one. See ``help(colormaps)`` for more information. - """ - set_cmap('winter') - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def magma(): - """ - Set the colormap to 'magma'. - - This changes the default colormap as well as the colormap of the current - image if there is one. See ``help(colormaps)`` for more information. - """ - set_cmap('magma') - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def inferno(): - """ - Set the colormap to 'inferno'. - - This changes the default colormap as well as the colormap of the current - image if there is one. See ``help(colormaps)`` for more information. - """ - set_cmap('inferno') - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def plasma(): - """ - Set the colormap to 'plasma'. - - This changes the default colormap as well as the colormap of the current - image if there is one. See ``help(colormaps)`` for more information. - """ - set_cmap('plasma') - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def viridis(): - """ - Set the colormap to 'viridis'. - - This changes the default colormap as well as the colormap of the current - image if there is one. See ``help(colormaps)`` for more information. - """ - set_cmap('viridis') - - -# Autogenerated by boilerplate.py. Do not edit as changes will be lost. -def nipy_spectral(): - """ - Set the colormap to 'nipy_spectral'. - - This changes the default colormap as well as the colormap of the current - image if there is one. See ``help(colormaps)`` for more information. - """ - set_cmap('nipy_spectral') diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/quiver.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/quiver.py deleted file mode 100644 index 46f2942..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/quiver.py +++ /dev/null @@ -1,1182 +0,0 @@ -""" -Support for plotting vector fields. - -Presently this contains Quiver and Barb. Quiver plots an arrow in the -direction of the vector, with the size of the arrow related to the -magnitude of the vector. - -Barbs are like quiver in that they point along a vector, but -the magnitude of the vector is given schematically by the presence of barbs -or flags on the barb. - -This will also become a home for things such as standard -deviation ellipses, which can and will be derived very easily from -the Quiver code. -""" - -import math - -import numpy as np -from numpy import ma - -from matplotlib import _api, cbook, _docstring -import matplotlib.artist as martist -import matplotlib.collections as mcollections -from matplotlib.patches import CirclePolygon -import matplotlib.text as mtext -import matplotlib.transforms as transforms - - -_quiver_doc = """ -Plot a 2D field of arrows. - -Call signature:: - - quiver([X, Y], U, V, [C], **kwargs) - -*X*, *Y* define the arrow locations, *U*, *V* define the arrow directions, and -*C* optionally sets the color. - -**Arrow length** - -The default settings auto-scales the length of the arrows to a reasonable size. -To change this behavior see the *scale* and *scale_units* parameters. - -**Arrow shape** - -The arrow shape is determined by *width*, *headwidth*, *headlength* and -*headaxislength*. See the notes below. - -**Arrow styling** - -Each arrow is internally represented by a filled polygon with a default edge -linewidth of 0. As a result, an arrow is rather a filled area, not a line with -a head, and `.PolyCollection` properties like *linewidth*, *edgecolor*, -*facecolor*, etc. act accordingly. - - -Parameters ----------- -X, Y : 1D or 2D array-like, optional - The x and y coordinates of the arrow locations. - - If not given, they will be generated as a uniform integer meshgrid based - on the dimensions of *U* and *V*. - - If *X* and *Y* are 1D but *U*, *V* are 2D, *X*, *Y* are expanded to 2D - using ``X, Y = np.meshgrid(X, Y)``. In this case ``len(X)`` and ``len(Y)`` - must match the column and row dimensions of *U* and *V*. - -U, V : 1D or 2D array-like - The x and y direction components of the arrow vectors. The interpretation - of these components (in data or in screen space) depends on *angles*. - - *U* and *V* must have the same number of elements, matching the number of - arrow locations in *X*, *Y*. *U* and *V* may be masked. Locations masked - in any of *U*, *V*, and *C* will not be drawn. - -C : 1D or 2D array-like, optional - Numeric data that defines the arrow colors by colormapping via *norm* and - *cmap*. - - This does not support explicit colors. If you want to set colors directly, - use *color* instead. The size of *C* must match the number of arrow - locations. - -angles : {'uv', 'xy'} or array-like, default: 'uv' - Method for determining the angle of the arrows. - - - 'uv': Arrow direction in screen coordinates. Use this if the arrows - symbolize a quantity that is not based on *X*, *Y* data coordinates. - - If *U* == *V* the orientation of the arrow on the plot is 45 degrees - counter-clockwise from the horizontal axis (positive to the right). - - - 'xy': Arrow direction in data coordinates, i.e. the arrows point from - (x, y) to (x+u, y+v). Use this e.g. for plotting a gradient field. - - - Arbitrary angles may be specified explicitly as an array of values - in degrees, counter-clockwise from the horizontal axis. - - In this case *U*, *V* is only used to determine the length of the - arrows. - - Note: inverting a data axis will correspondingly invert the - arrows only with ``angles='xy'``. - -pivot : {'tail', 'mid', 'middle', 'tip'}, default: 'tail' - The part of the arrow that is anchored to the *X*, *Y* grid. The arrow - rotates about this point. - - 'mid' is a synonym for 'middle'. - -scale : float, optional - Scales the length of the arrow inversely. - - Number of data units per arrow length unit, e.g., m/s per plot width; a - smaller scale parameter makes the arrow longer. Default is *None*. - - If *None*, a simple autoscaling algorithm is used, based on the average - vector length and the number of vectors. The arrow length unit is given by - the *scale_units* parameter. - -scale_units : {'width', 'height', 'dots', 'inches', 'x', 'y', 'xy'}, optional - If the *scale* kwarg is *None*, the arrow length unit. Default is *None*. - - e.g. *scale_units* is 'inches', *scale* is 2.0, and ``(u, v) = (1, 0)``, - then the vector will be 0.5 inches long. - - If *scale_units* is 'width' or 'height', then the vector will be half the - width/height of the axes. - - If *scale_units* is 'x' then the vector will be 0.5 x-axis - units. To plot vectors in the x-y plane, with u and v having - the same units as x and y, use - ``angles='xy', scale_units='xy', scale=1``. - -units : {'width', 'height', 'dots', 'inches', 'x', 'y', 'xy'}, default: 'width' - Affects the arrow size (except for the length). In particular, the shaft - *width* is measured in multiples of this unit. - - Supported values are: - - - 'width', 'height': The width or height of the Axes. - - 'dots', 'inches': Pixels or inches based on the figure dpi. - - 'x', 'y', 'xy': *X*, *Y* or :math:`\\sqrt{X^2 + Y^2}` in data units. - - The following table summarizes how these values affect the visible arrow - size under zooming and figure size changes: - - ================= ================= ================== - units zoom figure size change - ================= ================= ================== - 'x', 'y', 'xy' arrow size scales — - 'width', 'height' — arrow size scales - 'dots', 'inches' — — - ================= ================= ================== - -width : float, optional - Shaft width in arrow units. All head parameters are relative to *width*. - - The default depends on choice of *units* above, and number of vectors; - a typical starting value is about 0.005 times the width of the plot. - -headwidth : float, default: 3 - Head width as multiple of shaft *width*. See the notes below. - -headlength : float, default: 5 - Head length as multiple of shaft *width*. See the notes below. - -headaxislength : float, default: 4.5 - Head length at shaft intersection as multiple of shaft *width*. - See the notes below. - -minshaft : float, default: 1 - Length below which arrow scales, in units of head length. Do not - set this to less than 1, or small arrows will look terrible! - -minlength : float, default: 1 - Minimum length as a multiple of shaft width; if an arrow length - is less than this, plot a dot (hexagon) of this diameter instead. - -color : color or color sequence, optional - Explicit color(s) for the arrows. If *C* has been set, *color* has no - effect. - - This is a synonym for the `.PolyCollection` *facecolor* parameter. - -Other Parameters ----------------- -data : indexable object, optional - DATA_PARAMETER_PLACEHOLDER - -**kwargs : `~matplotlib.collections.PolyCollection` properties, optional - All other keyword arguments are passed on to `.PolyCollection`: - - %(PolyCollection:kwdoc)s - -Returns -------- -`~matplotlib.quiver.Quiver` - -See Also --------- -.Axes.quiverkey : Add a key to a quiver plot. - -Notes ------ - -**Arrow shape** - -The arrow is drawn as a polygon using the nodes as shown below. The values -*headwidth*, *headlength*, and *headaxislength* are in units of *width*. - -.. image:: /_static/quiver_sizes.svg - :width: 500px - -The defaults give a slightly swept-back arrow. Here are some guidelines how to -get other head shapes: - -- To make the head a triangle, make *headaxislength* the same as *headlength*. -- To make the arrow more pointed, reduce *headwidth* or increase *headlength* - and *headaxislength*. -- To make the head smaller relative to the shaft, scale down all the head - parameters proportionally. -- To remove the head completely, set all *head* parameters to 0. -- To get a diamond-shaped head, make *headaxislength* larger than *headlength*. -- Warning: For *headaxislength* < (*headlength* / *headwidth*), the "headaxis" - nodes (i.e. the ones connecting the head with the shaft) will protrude out - of the head in forward direction so that the arrow head looks broken. -""" % _docstring.interpd.params - -_docstring.interpd.update(quiver_doc=_quiver_doc) - - -class QuiverKey(martist.Artist): - """Labelled arrow for use as a quiver plot scale key.""" - halign = {'N': 'center', 'S': 'center', 'E': 'left', 'W': 'right'} - valign = {'N': 'bottom', 'S': 'top', 'E': 'center', 'W': 'center'} - pivot = {'N': 'middle', 'S': 'middle', 'E': 'tip', 'W': 'tail'} - - def __init__(self, Q, X, Y, U, label, - *, angle=0, coordinates='axes', color=None, labelsep=0.1, - labelpos='N', labelcolor=None, fontproperties=None, **kwargs): - """ - Add a key to a quiver plot. - - The positioning of the key depends on *X*, *Y*, *coordinates*, and - *labelpos*. If *labelpos* is 'N' or 'S', *X*, *Y* give the position of - the middle of the key arrow. If *labelpos* is 'E', *X*, *Y* positions - the head, and if *labelpos* is 'W', *X*, *Y* positions the tail; in - either of these two cases, *X*, *Y* is somewhere in the middle of the - arrow+label key object. - - Parameters - ---------- - Q : `~matplotlib.quiver.Quiver` - A `.Quiver` object as returned by a call to `~.Axes.quiver()`. - X, Y : float - The location of the key. - U : float - The length of the key. - label : str - The key label (e.g., length and units of the key). - angle : float, default: 0 - The angle of the key arrow, in degrees anti-clockwise from the - x-axis. - coordinates : {'axes', 'figure', 'data', 'inches'}, default: 'axes' - Coordinate system and units for *X*, *Y*: 'axes' and 'figure' are - normalized coordinate systems with (0, 0) in the lower left and - (1, 1) in the upper right; 'data' are the axes data coordinates - (used for the locations of the vectors in the quiver plot itself); - 'inches' is position in the figure in inches, with (0, 0) at the - lower left corner. - color : color - Overrides face and edge colors from *Q*. - labelpos : {'N', 'S', 'E', 'W'} - Position the label above, below, to the right, to the left of the - arrow, respectively. - labelsep : float, default: 0.1 - Distance in inches between the arrow and the label. - labelcolor : color, default: :rc:`text.color` - Label color. - fontproperties : dict, optional - A dictionary with keyword arguments accepted by the - `~matplotlib.font_manager.FontProperties` initializer: - *family*, *style*, *variant*, *size*, *weight*. - **kwargs - Any additional keyword arguments are used to override vector - properties taken from *Q*. - """ - super().__init__() - self.Q = Q - self.X = X - self.Y = Y - self.U = U - self.angle = angle - self.coord = coordinates - self.color = color - self.label = label - self._labelsep_inches = labelsep - - self.labelpos = labelpos - self.labelcolor = labelcolor - self.fontproperties = fontproperties or dict() - self.kw = kwargs - self.text = mtext.Text( - text=label, - horizontalalignment=self.halign[self.labelpos], - verticalalignment=self.valign[self.labelpos], - fontproperties=self.fontproperties) - if self.labelcolor is not None: - self.text.set_color(self.labelcolor) - self._dpi_at_last_init = None - self.zorder = Q.zorder + 0.1 - - @property - def labelsep(self): - return self._labelsep_inches * self.Q.axes.figure.dpi - - def _init(self): - if True: # self._dpi_at_last_init != self.axes.figure.dpi - if self.Q._dpi_at_last_init != self.Q.axes.figure.dpi: - self.Q._init() - self._set_transform() - with cbook._setattr_cm(self.Q, pivot=self.pivot[self.labelpos], - # Hack: save and restore the Umask - Umask=ma.nomask): - u = self.U * np.cos(np.radians(self.angle)) - v = self.U * np.sin(np.radians(self.angle)) - angle = (self.Q.angles if isinstance(self.Q.angles, str) - else 'uv') - self.verts = self.Q._make_verts( - np.array([u]), np.array([v]), angle) - kwargs = self.Q.polykw - kwargs.update(self.kw) - self.vector = mcollections.PolyCollection( - self.verts, - offsets=[(self.X, self.Y)], - offset_transform=self.get_transform(), - **kwargs) - if self.color is not None: - self.vector.set_color(self.color) - self.vector.set_transform(self.Q.get_transform()) - self.vector.set_figure(self.get_figure()) - self._dpi_at_last_init = self.Q.axes.figure.dpi - - def _text_shift(self): - return { - "N": (0, +self.labelsep), - "S": (0, -self.labelsep), - "E": (+self.labelsep, 0), - "W": (-self.labelsep, 0), - }[self.labelpos] - - @martist.allow_rasterization - def draw(self, renderer): - self._init() - self.vector.draw(renderer) - pos = self.get_transform().transform((self.X, self.Y)) - self.text.set_position(pos + self._text_shift()) - self.text.draw(renderer) - self.stale = False - - def _set_transform(self): - self.set_transform(_api.check_getitem({ - "data": self.Q.axes.transData, - "axes": self.Q.axes.transAxes, - "figure": self.Q.axes.figure.transFigure, - "inches": self.Q.axes.figure.dpi_scale_trans, - }, coordinates=self.coord)) - - def set_figure(self, fig): - super().set_figure(fig) - self.text.set_figure(fig) - - def contains(self, mouseevent): - inside, info = self._default_contains(mouseevent) - if inside is not None: - return inside, info - # Maybe the dictionary should allow one to - # distinguish between a text hit and a vector hit. - if (self.text.contains(mouseevent)[0] or - self.vector.contains(mouseevent)[0]): - return True, {} - return False, {} - - -def _parse_args(*args, caller_name='function'): - """ - Helper function to parse positional parameters for colored vector plots. - - This is currently used for Quiver and Barbs. - - Parameters - ---------- - *args : list - list of 2-5 arguments. Depending on their number they are parsed to:: - - U, V - U, V, C - X, Y, U, V - X, Y, U, V, C - - caller_name : str - Name of the calling method (used in error messages). - """ - X = Y = C = None - - nargs = len(args) - if nargs == 2: - # The use of atleast_1d allows for handling scalar arguments while also - # keeping masked arrays - U, V = np.atleast_1d(*args) - elif nargs == 3: - U, V, C = np.atleast_1d(*args) - elif nargs == 4: - X, Y, U, V = np.atleast_1d(*args) - elif nargs == 5: - X, Y, U, V, C = np.atleast_1d(*args) - else: - raise _api.nargs_error(caller_name, takes="from 2 to 5", given=nargs) - - nr, nc = (1, U.shape[0]) if U.ndim == 1 else U.shape - - if X is not None: - X = X.ravel() - Y = Y.ravel() - if len(X) == nc and len(Y) == nr: - X, Y = [a.ravel() for a in np.meshgrid(X, Y)] - elif len(X) != len(Y): - raise ValueError('X and Y must be the same size, but ' - f'X.size is {X.size} and Y.size is {Y.size}.') - else: - indexgrid = np.meshgrid(np.arange(nc), np.arange(nr)) - X, Y = [np.ravel(a) for a in indexgrid] - # Size validation for U, V, C is left to the set_UVC method. - return X, Y, U, V, C - - -def _check_consistent_shapes(*arrays): - all_shapes = {a.shape for a in arrays} - if len(all_shapes) != 1: - raise ValueError('The shapes of the passed in arrays do not match') - - -class Quiver(mcollections.PolyCollection): - """ - Specialized PolyCollection for arrows. - - The only API method is set_UVC(), which can be used - to change the size, orientation, and color of the - arrows; their locations are fixed when the class is - instantiated. Possibly this method will be useful - in animations. - - Much of the work in this class is done in the draw() - method so that as much information as possible is available - about the plot. In subsequent draw() calls, recalculation - is limited to things that might have changed, so there - should be no performance penalty from putting the calculations - in the draw() method. - """ - - _PIVOT_VALS = ('tail', 'middle', 'tip') - - @_docstring.Substitution(_quiver_doc) - def __init__(self, ax, *args, - scale=None, headwidth=3, headlength=5, headaxislength=4.5, - minshaft=1, minlength=1, units='width', scale_units=None, - angles='uv', width=None, color='k', pivot='tail', **kwargs): - """ - The constructor takes one required argument, an Axes - instance, followed by the args and kwargs described - by the following pyplot interface documentation: - %s - """ - self._axes = ax # The attr actually set by the Artist.axes property. - X, Y, U, V, C = _parse_args(*args, caller_name='quiver') - self.X = X - self.Y = Y - self.XY = np.column_stack((X, Y)) - self.N = len(X) - self.scale = scale - self.headwidth = headwidth - self.headlength = float(headlength) - self.headaxislength = headaxislength - self.minshaft = minshaft - self.minlength = minlength - self.units = units - self.scale_units = scale_units - self.angles = angles - self.width = width - - if pivot.lower() == 'mid': - pivot = 'middle' - self.pivot = pivot.lower() - _api.check_in_list(self._PIVOT_VALS, pivot=self.pivot) - - self.transform = kwargs.pop('transform', ax.transData) - kwargs.setdefault('facecolors', color) - kwargs.setdefault('linewidths', (0,)) - super().__init__([], offsets=self.XY, offset_transform=self.transform, - closed=False, **kwargs) - self.polykw = kwargs - self.set_UVC(U, V, C) - self._dpi_at_last_init = None - - def _init(self): - """ - Initialization delayed until first draw; - allow time for axes setup. - """ - # It seems that there are not enough event notifications - # available to have this work on an as-needed basis at present. - if True: # self._dpi_at_last_init != self.axes.figure.dpi - trans = self._set_transform() - self.span = trans.inverted().transform_bbox(self.axes.bbox).width - if self.width is None: - sn = np.clip(math.sqrt(self.N), 8, 25) - self.width = 0.06 * self.span / sn - - # _make_verts sets self.scale if not already specified - if (self._dpi_at_last_init != self.axes.figure.dpi - and self.scale is None): - self._make_verts(self.U, self.V, self.angles) - - self._dpi_at_last_init = self.axes.figure.dpi - - def get_datalim(self, transData): - trans = self.get_transform() - offset_trf = self.get_offset_transform() - full_transform = (trans - transData) + (offset_trf - transData) - XY = full_transform.transform(self.XY) - bbox = transforms.Bbox.null() - bbox.update_from_data_xy(XY, ignore=True) - return bbox - - @martist.allow_rasterization - def draw(self, renderer): - self._init() - verts = self._make_verts(self.U, self.V, self.angles) - self.set_verts(verts, closed=False) - super().draw(renderer) - self.stale = False - - def set_UVC(self, U, V, C=None): - # We need to ensure we have a copy, not a reference - # to an array that might change before draw(). - U = ma.masked_invalid(U, copy=True).ravel() - V = ma.masked_invalid(V, copy=True).ravel() - if C is not None: - C = ma.masked_invalid(C, copy=True).ravel() - for name, var in zip(('U', 'V', 'C'), (U, V, C)): - if not (var is None or var.size == self.N or var.size == 1): - raise ValueError(f'Argument {name} has a size {var.size}' - f' which does not match {self.N},' - ' the number of arrow positions') - - mask = ma.mask_or(U.mask, V.mask, copy=False, shrink=True) - if C is not None: - mask = ma.mask_or(mask, C.mask, copy=False, shrink=True) - if mask is ma.nomask: - C = C.filled() - else: - C = ma.array(C, mask=mask, copy=False) - self.U = U.filled(1) - self.V = V.filled(1) - self.Umask = mask - if C is not None: - self.set_array(C) - self.stale = True - - def _dots_per_unit(self, units): - """Return a scale factor for converting from units to pixels.""" - bb = self.axes.bbox - vl = self.axes.viewLim - return _api.check_getitem({ - 'x': bb.width / vl.width, - 'y': bb.height / vl.height, - 'xy': np.hypot(*bb.size) / np.hypot(*vl.size), - 'width': bb.width, - 'height': bb.height, - 'dots': 1., - 'inches': self.axes.figure.dpi, - }, units=units) - - def _set_transform(self): - """ - Set the PolyCollection transform to go - from arrow width units to pixels. - """ - dx = self._dots_per_unit(self.units) - self._trans_scale = dx # pixels per arrow width unit - trans = transforms.Affine2D().scale(dx) - self.set_transform(trans) - return trans - - def _angles_lengths(self, U, V, eps=1): - xy = self.axes.transData.transform(self.XY) - uv = np.column_stack((U, V)) - xyp = self.axes.transData.transform(self.XY + eps * uv) - dxy = xyp - xy - angles = np.arctan2(dxy[:, 1], dxy[:, 0]) - lengths = np.hypot(*dxy.T) / eps - return angles, lengths - - def _make_verts(self, U, V, angles): - uv = (U + V * 1j) - str_angles = angles if isinstance(angles, str) else '' - if str_angles == 'xy' and self.scale_units == 'xy': - # Here eps is 1 so that if we get U, V by diffing - # the X, Y arrays, the vectors will connect the - # points, regardless of the axis scaling (including log). - angles, lengths = self._angles_lengths(U, V, eps=1) - elif str_angles == 'xy' or self.scale_units == 'xy': - # Calculate eps based on the extents of the plot - # so that we don't end up with roundoff error from - # adding a small number to a large. - eps = np.abs(self.axes.dataLim.extents).max() * 0.001 - angles, lengths = self._angles_lengths(U, V, eps=eps) - if str_angles and self.scale_units == 'xy': - a = lengths - else: - a = np.abs(uv) - if self.scale is None: - sn = max(10, math.sqrt(self.N)) - if self.Umask is not ma.nomask: - amean = a[~self.Umask].mean() - else: - amean = a.mean() - # crude auto-scaling - # scale is typical arrow length as a multiple of the arrow width - scale = 1.8 * amean * sn / self.span - if self.scale_units is None: - if self.scale is None: - self.scale = scale - widthu_per_lenu = 1.0 - else: - if self.scale_units == 'xy': - dx = 1 - else: - dx = self._dots_per_unit(self.scale_units) - widthu_per_lenu = dx / self._trans_scale - if self.scale is None: - self.scale = scale * widthu_per_lenu - length = a * (widthu_per_lenu / (self.scale * self.width)) - X, Y = self._h_arrows(length) - if str_angles == 'xy': - theta = angles - elif str_angles == 'uv': - theta = np.angle(uv) - else: - theta = ma.masked_invalid(np.deg2rad(angles)).filled(0) - theta = theta.reshape((-1, 1)) # for broadcasting - xy = (X + Y * 1j) * np.exp(1j * theta) * self.width - XY = np.stack((xy.real, xy.imag), axis=2) - if self.Umask is not ma.nomask: - XY = ma.array(XY) - XY[self.Umask] = ma.masked - # This might be handled more efficiently with nans, given - # that nans will end up in the paths anyway. - - return XY - - def _h_arrows(self, length): - """Length is in arrow width units.""" - # It might be possible to streamline the code - # and speed it up a bit by using complex (x, y) - # instead of separate arrays; but any gain would be slight. - minsh = self.minshaft * self.headlength - N = len(length) - length = length.reshape(N, 1) - # This number is chosen based on when pixel values overflow in Agg - # causing rendering errors - # length = np.minimum(length, 2 ** 16) - np.clip(length, 0, 2 ** 16, out=length) - # x, y: normal horizontal arrow - x = np.array([0, -self.headaxislength, - -self.headlength, 0], - np.float64) - x = x + np.array([0, 1, 1, 1]) * length - y = 0.5 * np.array([1, 1, self.headwidth, 0], np.float64) - y = np.repeat(y[np.newaxis, :], N, axis=0) - # x0, y0: arrow without shaft, for short vectors - x0 = np.array([0, minsh - self.headaxislength, - minsh - self.headlength, minsh], np.float64) - y0 = 0.5 * np.array([1, 1, self.headwidth, 0], np.float64) - ii = [0, 1, 2, 3, 2, 1, 0, 0] - X = x[:, ii] - Y = y[:, ii] - Y[:, 3:-1] *= -1 - X0 = x0[ii] - Y0 = y0[ii] - Y0[3:-1] *= -1 - shrink = length / minsh if minsh != 0. else 0. - X0 = shrink * X0[np.newaxis, :] - Y0 = shrink * Y0[np.newaxis, :] - short = np.repeat(length < minsh, 8, axis=1) - # Now select X0, Y0 if short, otherwise X, Y - np.copyto(X, X0, where=short) - np.copyto(Y, Y0, where=short) - if self.pivot == 'middle': - X -= 0.5 * X[:, 3, np.newaxis] - elif self.pivot == 'tip': - # numpy bug? using -= does not work here unless we multiply by a - # float first, as with 'mid'. - X = X - X[:, 3, np.newaxis] - elif self.pivot != 'tail': - _api.check_in_list(["middle", "tip", "tail"], pivot=self.pivot) - - tooshort = length < self.minlength - if tooshort.any(): - # Use a heptagonal dot: - th = np.arange(0, 8, 1, np.float64) * (np.pi / 3.0) - x1 = np.cos(th) * self.minlength * 0.5 - y1 = np.sin(th) * self.minlength * 0.5 - X1 = np.repeat(x1[np.newaxis, :], N, axis=0) - Y1 = np.repeat(y1[np.newaxis, :], N, axis=0) - tooshort = np.repeat(tooshort, 8, 1) - np.copyto(X, X1, where=tooshort) - np.copyto(Y, Y1, where=tooshort) - # Mask handling is deferred to the caller, _make_verts. - return X, Y - - quiver_doc = _api.deprecated("3.7")(property(lambda self: _quiver_doc)) - - -_barbs_doc = r""" -Plot a 2D field of barbs. - -Call signature:: - - barbs([X, Y], U, V, [C], **kwargs) - -Where *X*, *Y* define the barb locations, *U*, *V* define the barb -directions, and *C* optionally sets the color. - -All arguments may be 1D or 2D. *U*, *V*, *C* may be masked arrays, but masked -*X*, *Y* are not supported at present. - -Barbs are traditionally used in meteorology as a way to plot the speed -and direction of wind observations, but can technically be used to -plot any two dimensional vector quantity. As opposed to arrows, which -give vector magnitude by the length of the arrow, the barbs give more -quantitative information about the vector magnitude by putting slanted -lines or a triangle for various increments in magnitude, as show -schematically below:: - - : /\ \ - : / \ \ - : / \ \ \ - : / \ \ \ - : ------------------------------ - -The largest increment is given by a triangle (or "flag"). After those -come full lines (barbs). The smallest increment is a half line. There -is only, of course, ever at most 1 half line. If the magnitude is -small and only needs a single half-line and no full lines or -triangles, the half-line is offset from the end of the barb so that it -can be easily distinguished from barbs with a single full line. The -magnitude for the barb shown above would nominally be 65, using the -standard increments of 50, 10, and 5. - -See also https://en.wikipedia.org/wiki/Wind_barb. - -Parameters ----------- -X, Y : 1D or 2D array-like, optional - The x and y coordinates of the barb locations. See *pivot* for how the - barbs are drawn to the x, y positions. - - If not given, they will be generated as a uniform integer meshgrid based - on the dimensions of *U* and *V*. - - If *X* and *Y* are 1D but *U*, *V* are 2D, *X*, *Y* are expanded to 2D - using ``X, Y = np.meshgrid(X, Y)``. In this case ``len(X)`` and ``len(Y)`` - must match the column and row dimensions of *U* and *V*. - -U, V : 1D or 2D array-like - The x and y components of the barb shaft. - -C : 1D or 2D array-like, optional - Numeric data that defines the barb colors by colormapping via *norm* and - *cmap*. - - This does not support explicit colors. If you want to set colors directly, - use *barbcolor* instead. - -length : float, default: 7 - Length of the barb in points; the other parts of the barb - are scaled against this. - -pivot : {'tip', 'middle'} or float, default: 'tip' - The part of the arrow that is anchored to the *X*, *Y* grid. The barb - rotates about this point. This can also be a number, which shifts the - start of the barb that many points away from grid point. - -barbcolor : color or color sequence - The color of all parts of the barb except for the flags. This parameter - is analogous to the *edgecolor* parameter for polygons, which can be used - instead. However this parameter will override facecolor. - -flagcolor : color or color sequence - The color of any flags on the barb. This parameter is analogous to the - *facecolor* parameter for polygons, which can be used instead. However, - this parameter will override facecolor. If this is not set (and *C* has - not either) then *flagcolor* will be set to match *barbcolor* so that the - barb has a uniform color. If *C* has been set, *flagcolor* has no effect. - -sizes : dict, optional - A dictionary of coefficients specifying the ratio of a given - feature to the length of the barb. Only those values one wishes to - override need to be included. These features include: - - - 'spacing' - space between features (flags, full/half barbs) - - 'height' - height (distance from shaft to top) of a flag or full barb - - 'width' - width of a flag, twice the width of a full barb - - 'emptybarb' - radius of the circle used for low magnitudes - -fill_empty : bool, default: False - Whether the empty barbs (circles) that are drawn should be filled with - the flag color. If they are not filled, the center is transparent. - -rounding : bool, default: True - Whether the vector magnitude should be rounded when allocating barb - components. If True, the magnitude is rounded to the nearest multiple - of the half-barb increment. If False, the magnitude is simply truncated - to the next lowest multiple. - -barb_increments : dict, optional - A dictionary of increments specifying values to associate with - different parts of the barb. Only those values one wishes to - override need to be included. - - - 'half' - half barbs (Default is 5) - - 'full' - full barbs (Default is 10) - - 'flag' - flags (default is 50) - -flip_barb : bool or array-like of bool, default: False - Whether the lines and flags should point opposite to normal. - Normal behavior is for the barbs and lines to point right (comes from wind - barbs having these features point towards low pressure in the Northern - Hemisphere). - - A single value is applied to all barbs. Individual barbs can be flipped by - passing a bool array of the same size as *U* and *V*. - -Returns -------- -barbs : `~matplotlib.quiver.Barbs` - -Other Parameters ----------------- -data : indexable object, optional - DATA_PARAMETER_PLACEHOLDER - -**kwargs - The barbs can further be customized using `.PolyCollection` keyword - arguments: - - %(PolyCollection:kwdoc)s -""" % _docstring.interpd.params - -_docstring.interpd.update(barbs_doc=_barbs_doc) - - -class Barbs(mcollections.PolyCollection): - """ - Specialized PolyCollection for barbs. - - The only API method is :meth:`set_UVC`, which can be used to - change the size, orientation, and color of the arrows. Locations - are changed using the :meth:`set_offsets` collection method. - Possibly this method will be useful in animations. - - There is one internal function :meth:`_find_tails` which finds - exactly what should be put on the barb given the vector magnitude. - From there :meth:`_make_barbs` is used to find the vertices of the - polygon to represent the barb based on this information. - """ - - # This may be an abuse of polygons here to render what is essentially maybe - # 1 triangle and a series of lines. It works fine as far as I can tell - # however. - - @_docstring.interpd - def __init__(self, ax, *args, - pivot='tip', length=7, barbcolor=None, flagcolor=None, - sizes=None, fill_empty=False, barb_increments=None, - rounding=True, flip_barb=False, **kwargs): - """ - The constructor takes one required argument, an Axes - instance, followed by the args and kwargs described - by the following pyplot interface documentation: - %(barbs_doc)s - """ - self.sizes = sizes or dict() - self.fill_empty = fill_empty - self.barb_increments = barb_increments or dict() - self.rounding = rounding - self.flip = np.atleast_1d(flip_barb) - transform = kwargs.pop('transform', ax.transData) - self._pivot = pivot - self._length = length - - # Flagcolor and barbcolor provide convenience parameters for - # setting the facecolor and edgecolor, respectively, of the barb - # polygon. We also work here to make the flag the same color as the - # rest of the barb by default - - if None in (barbcolor, flagcolor): - kwargs['edgecolors'] = 'face' - if flagcolor: - kwargs['facecolors'] = flagcolor - elif barbcolor: - kwargs['facecolors'] = barbcolor - else: - # Set to facecolor passed in or default to black - kwargs.setdefault('facecolors', 'k') - else: - kwargs['edgecolors'] = barbcolor - kwargs['facecolors'] = flagcolor - - # Explicitly set a line width if we're not given one, otherwise - # polygons are not outlined and we get no barbs - if 'linewidth' not in kwargs and 'lw' not in kwargs: - kwargs['linewidth'] = 1 - - # Parse out the data arrays from the various configurations supported - x, y, u, v, c = _parse_args(*args, caller_name='barbs') - self.x = x - self.y = y - xy = np.column_stack((x, y)) - - # Make a collection - barb_size = self._length ** 2 / 4 # Empirically determined - super().__init__( - [], (barb_size,), offsets=xy, offset_transform=transform, **kwargs) - self.set_transform(transforms.IdentityTransform()) - - self.set_UVC(u, v, c) - - def _find_tails(self, mag, rounding=True, half=5, full=10, flag=50): - """ - Find how many of each of the tail pieces is necessary. - - Parameters - ---------- - mag : `~numpy.ndarray` - Vector magnitudes; must be non-negative (and an actual ndarray). - rounding : bool, default: True - Whether to round or to truncate to the nearest half-barb. - half, full, flag : float, defaults: 5, 10, 50 - Increments for a half-barb, a barb, and a flag. - - Returns - ------- - n_flags, n_barbs : int array - For each entry in *mag*, the number of flags and barbs. - half_flag : bool array - For each entry in *mag*, whether a half-barb is needed. - empty_flag : bool array - For each entry in *mag*, whether nothing is drawn. - """ - # If rounding, round to the nearest multiple of half, the smallest - # increment - if rounding: - mag = half * np.around(mag / half) - n_flags, mag = divmod(mag, flag) - n_barb, mag = divmod(mag, full) - half_flag = mag >= half - empty_flag = ~(half_flag | (n_flags > 0) | (n_barb > 0)) - return n_flags.astype(int), n_barb.astype(int), half_flag, empty_flag - - def _make_barbs(self, u, v, nflags, nbarbs, half_barb, empty_flag, length, - pivot, sizes, fill_empty, flip): - """ - Create the wind barbs. - - Parameters - ---------- - u, v - Components of the vector in the x and y directions, respectively. - - nflags, nbarbs, half_barb, empty_flag - Respectively, the number of flags, number of barbs, flag for - half a barb, and flag for empty barb, ostensibly obtained from - :meth:`_find_tails`. - - length - The length of the barb staff in points. - - pivot : {"tip", "middle"} or number - The point on the barb around which the entire barb should be - rotated. If a number, the start of the barb is shifted by that - many points from the origin. - - sizes : dict - Coefficients specifying the ratio of a given feature to the length - of the barb. These features include: - - - *spacing*: space between features (flags, full/half barbs). - - *height*: distance from shaft of top of a flag or full barb. - - *width*: width of a flag, twice the width of a full barb. - - *emptybarb*: radius of the circle used for low magnitudes. - - fill_empty : bool - Whether the circle representing an empty barb should be filled or - not (this changes the drawing of the polygon). - - flip : list of bool - Whether the features should be flipped to the other side of the - barb (useful for winds in the southern hemisphere). - - Returns - ------- - list of arrays of vertices - Polygon vertices for each of the wind barbs. These polygons have - been rotated to properly align with the vector direction. - """ - - # These control the spacing and size of barb elements relative to the - # length of the shaft - spacing = length * sizes.get('spacing', 0.125) - full_height = length * sizes.get('height', 0.4) - full_width = length * sizes.get('width', 0.25) - empty_rad = length * sizes.get('emptybarb', 0.15) - - # Controls y point where to pivot the barb. - pivot_points = dict(tip=0.0, middle=-length / 2.) - - endx = 0.0 - try: - endy = float(pivot) - except ValueError: - endy = pivot_points[pivot.lower()] - - # Get the appropriate angle for the vector components. The offset is - # due to the way the barb is initially drawn, going down the y-axis. - # This makes sense in a meteorological mode of thinking since there 0 - # degrees corresponds to north (the y-axis traditionally) - angles = -(ma.arctan2(v, u) + np.pi / 2) - - # Used for low magnitude. We just get the vertices, so if we make it - # out here, it can be reused. The center set here should put the - # center of the circle at the location(offset), rather than at the - # same point as the barb pivot; this seems more sensible. - circ = CirclePolygon((0, 0), radius=empty_rad).get_verts() - if fill_empty: - empty_barb = circ - else: - # If we don't want the empty one filled, we make a degenerate - # polygon that wraps back over itself - empty_barb = np.concatenate((circ, circ[::-1])) - - barb_list = [] - for index, angle in np.ndenumerate(angles): - # If the vector magnitude is too weak to draw anything, plot an - # empty circle instead - if empty_flag[index]: - # We can skip the transform since the circle has no preferred - # orientation - barb_list.append(empty_barb) - continue - - poly_verts = [(endx, endy)] - offset = length - - # Handle if this barb should be flipped - barb_height = -full_height if flip[index] else full_height - - # Add vertices for each flag - for i in range(nflags[index]): - # The spacing that works for the barbs is a little to much for - # the flags, but this only occurs when we have more than 1 - # flag. - if offset != length: - offset += spacing / 2. - poly_verts.extend( - [[endx, endy + offset], - [endx + barb_height, endy - full_width / 2 + offset], - [endx, endy - full_width + offset]]) - - offset -= full_width + spacing - - # Add vertices for each barb. These really are lines, but works - # great adding 3 vertices that basically pull the polygon out and - # back down the line - for i in range(nbarbs[index]): - poly_verts.extend( - [(endx, endy + offset), - (endx + barb_height, endy + offset + full_width / 2), - (endx, endy + offset)]) - - offset -= spacing - - # Add the vertices for half a barb, if needed - if half_barb[index]: - # If the half barb is the first on the staff, traditionally it - # is offset from the end to make it easy to distinguish from a - # barb with a full one - if offset == length: - poly_verts.append((endx, endy + offset)) - offset -= 1.5 * spacing - poly_verts.extend( - [(endx, endy + offset), - (endx + barb_height / 2, endy + offset + full_width / 4), - (endx, endy + offset)]) - - # Rotate the barb according the angle. Making the barb first and - # then rotating it made the math for drawing the barb really easy. - # Also, the transform framework makes doing the rotation simple. - poly_verts = transforms.Affine2D().rotate(-angle).transform( - poly_verts) - barb_list.append(poly_verts) - - return barb_list - - def set_UVC(self, U, V, C=None): - # We need to ensure we have a copy, not a reference to an array that - # might change before draw(). - self.u = ma.masked_invalid(U, copy=True).ravel() - self.v = ma.masked_invalid(V, copy=True).ravel() - - # Flip needs to have the same number of entries as everything else. - # Use broadcast_to to avoid a bloated array of identical values. - # (can't rely on actual broadcasting) - if len(self.flip) == 1: - flip = np.broadcast_to(self.flip, self.u.shape) - else: - flip = self.flip - - if C is not None: - c = ma.masked_invalid(C, copy=True).ravel() - x, y, u, v, c, flip = cbook.delete_masked_points( - self.x.ravel(), self.y.ravel(), self.u, self.v, c, - flip.ravel()) - _check_consistent_shapes(x, y, u, v, c, flip) - else: - x, y, u, v, flip = cbook.delete_masked_points( - self.x.ravel(), self.y.ravel(), self.u, self.v, flip.ravel()) - _check_consistent_shapes(x, y, u, v, flip) - - magnitude = np.hypot(u, v) - flags, barbs, halves, empty = self._find_tails( - magnitude, self.rounding, **self.barb_increments) - - # Get the vertices for each of the barbs - - plot_barbs = self._make_barbs(u, v, flags, barbs, halves, empty, - self._length, self._pivot, self.sizes, - self.fill_empty, flip) - self.set_verts(plot_barbs) - - # Set the color array - if C is not None: - self.set_array(c) - - # Update the offsets in case the masked data changed - xy = np.column_stack((x, y)) - self._offsets = xy - self.stale = True - - def set_offsets(self, xy): - """ - Set the offsets for the barb polygons. This saves the offsets passed - in and masks them as appropriate for the existing U/V data. - - Parameters - ---------- - xy : sequence of pairs of floats - """ - self.x = xy[:, 0] - self.y = xy[:, 1] - x, y, u, v = cbook.delete_masked_points( - self.x.ravel(), self.y.ravel(), self.u, self.v) - _check_consistent_shapes(x, y, u, v) - xy = np.column_stack((x, y)) - super().set_offsets(xy) - self.stale = True - - barbs_doc = _api.deprecated("3.7")(property(lambda self: _barbs_doc)) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/rcsetup.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/rcsetup.py deleted file mode 100644 index 4fafd35..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/rcsetup.py +++ /dev/null @@ -1,1267 +0,0 @@ -""" -The rcsetup module contains the validation code for customization using -Matplotlib's rc settings. - -Each rc setting is assigned a function used to validate any attempted changes -to that setting. The validation functions are defined in the rcsetup module, -and are used to construct the rcParams global object which stores the settings -and is referenced throughout Matplotlib. - -The default values of the rc settings are set in the default matplotlibrc file. -Any additions or deletions to the parameter set listed here should also be -propagated to the :file:`lib/matplotlib/mpl-data/matplotlibrc` in Matplotlib's -root source directory. -""" - -import ast -from functools import lru_cache, reduce -from numbers import Number -import operator -import os -import re - -import numpy as np - -from matplotlib import _api, cbook -from matplotlib.cbook import ls_mapper -from matplotlib.colors import Colormap, is_color_like -from matplotlib._fontconfig_pattern import parse_fontconfig_pattern -from matplotlib._enums import JoinStyle, CapStyle - -# Don't let the original cycler collide with our validating cycler -from cycler import Cycler, cycler as ccycler - - -# The capitalized forms are needed for ipython at present; this may -# change for later versions. -interactive_bk = [ - 'GTK3Agg', 'GTK3Cairo', 'GTK4Agg', 'GTK4Cairo', - 'MacOSX', - 'nbAgg', - 'QtAgg', 'QtCairo', 'Qt5Agg', 'Qt5Cairo', - 'TkAgg', 'TkCairo', - 'WebAgg', - 'WX', 'WXAgg', 'WXCairo', -] -non_interactive_bk = ['agg', 'cairo', - 'pdf', 'pgf', 'ps', 'svg', 'template'] -all_backends = interactive_bk + non_interactive_bk - - -class ValidateInStrings: - def __init__(self, key, valid, ignorecase=False, *, - _deprecated_since=None): - """*valid* is a list of legal strings.""" - self.key = key - self.ignorecase = ignorecase - self._deprecated_since = _deprecated_since - - def func(s): - if ignorecase: - return s.lower() - else: - return s - self.valid = {func(k): k for k in valid} - - def __call__(self, s): - if self._deprecated_since: - name, = (k for k, v in globals().items() if v is self) - _api.warn_deprecated( - self._deprecated_since, name=name, obj_type="function") - if self.ignorecase and isinstance(s, str): - s = s.lower() - if s in self.valid: - return self.valid[s] - msg = (f"{s!r} is not a valid value for {self.key}; supported values " - f"are {[*self.valid.values()]}") - if (isinstance(s, str) - and (s.startswith('"') and s.endswith('"') - or s.startswith("'") and s.endswith("'")) - and s[1:-1] in self.valid): - msg += "; remove quotes surrounding your string" - raise ValueError(msg) - - -@lru_cache() -def _listify_validator(scalar_validator, allow_stringlist=False, *, - n=None, doc=None): - def f(s): - if isinstance(s, str): - try: - val = [scalar_validator(v.strip()) for v in s.split(',') - if v.strip()] - except Exception: - if allow_stringlist: - # Sometimes, a list of colors might be a single string - # of single-letter colornames. So give that a shot. - val = [scalar_validator(v.strip()) for v in s if v.strip()] - else: - raise - # Allow any ordered sequence type -- generators, np.ndarray, pd.Series - # -- but not sets, whose iteration order is non-deterministic. - elif np.iterable(s) and not isinstance(s, (set, frozenset)): - # The condition on this list comprehension will preserve the - # behavior of filtering out any empty strings (behavior was - # from the original validate_stringlist()), while allowing - # any non-string/text scalar values such as numbers and arrays. - val = [scalar_validator(v) for v in s - if not isinstance(v, str) or v] - else: - raise ValueError( - f"Expected str or other non-set iterable, but got {s}") - if n is not None and len(val) != n: - raise ValueError( - f"Expected {n} values, but there are {len(val)} values in {s}") - return val - - try: - f.__name__ = "{}list".format(scalar_validator.__name__) - except AttributeError: # class instance. - f.__name__ = "{}List".format(type(scalar_validator).__name__) - f.__qualname__ = f.__qualname__.rsplit(".", 1)[0] + "." + f.__name__ - f.__doc__ = doc if doc is not None else scalar_validator.__doc__ - return f - - -def validate_any(s): - return s -validate_anylist = _listify_validator(validate_any) - - -def _validate_date(s): - try: - np.datetime64(s) - return s - except ValueError: - raise ValueError( - f'{s!r} should be a string that can be parsed by numpy.datetime64') - - -def validate_bool(b): - """Convert b to ``bool`` or raise.""" - if isinstance(b, str): - b = b.lower() - if b in ('t', 'y', 'yes', 'on', 'true', '1', 1, True): - return True - elif b in ('f', 'n', 'no', 'off', 'false', '0', 0, False): - return False - else: - raise ValueError(f'Cannot convert {b!r} to bool') - - -def validate_axisbelow(s): - try: - return validate_bool(s) - except ValueError: - if isinstance(s, str): - if s == 'line': - return 'line' - raise ValueError(f'{s!r} cannot be interpreted as' - ' True, False, or "line"') - - -def validate_dpi(s): - """Confirm s is string 'figure' or convert s to float or raise.""" - if s == 'figure': - return s - try: - return float(s) - except ValueError as e: - raise ValueError(f'{s!r} is not string "figure" and ' - f'could not convert {s!r} to float') from e - - -def _make_type_validator(cls, *, allow_none=False): - """ - Return a validator that converts inputs to *cls* or raises (and possibly - allows ``None`` as well). - """ - - def validator(s): - if (allow_none and - (s is None or isinstance(s, str) and s.lower() == "none")): - return None - if cls is str and not isinstance(s, str): - raise ValueError(f'Could not convert {s!r} to str') - try: - return cls(s) - except (TypeError, ValueError) as e: - raise ValueError( - f'Could not convert {s!r} to {cls.__name__}') from e - - validator.__name__ = f"validate_{cls.__name__}" - if allow_none: - validator.__name__ += "_or_None" - validator.__qualname__ = ( - validator.__qualname__.rsplit(".", 1)[0] + "." + validator.__name__) - return validator - - -validate_string = _make_type_validator(str) -validate_string_or_None = _make_type_validator(str, allow_none=True) -validate_stringlist = _listify_validator( - validate_string, doc='return a list of strings') -validate_int = _make_type_validator(int) -validate_int_or_None = _make_type_validator(int, allow_none=True) -validate_float = _make_type_validator(float) -validate_float_or_None = _make_type_validator(float, allow_none=True) -validate_floatlist = _listify_validator( - validate_float, doc='return a list of floats') - - -def _validate_pathlike(s): - if isinstance(s, (str, os.PathLike)): - # Store value as str because savefig.directory needs to distinguish - # between "" (cwd) and "." (cwd, but gets updated by user selections). - return os.fsdecode(s) - else: - return validate_string(s) - - -def validate_fonttype(s): - """ - Confirm that this is a Postscript or PDF font type that we know how to - convert to. - """ - fonttypes = {'type3': 3, - 'truetype': 42} - try: - fonttype = validate_int(s) - except ValueError: - try: - return fonttypes[s.lower()] - except KeyError as e: - raise ValueError('Supported Postscript/PDF font types are %s' - % list(fonttypes)) from e - else: - if fonttype not in fonttypes.values(): - raise ValueError( - 'Supported Postscript/PDF font types are %s' % - list(fonttypes.values())) - return fonttype - - -_validate_standard_backends = ValidateInStrings( - 'backend', all_backends, ignorecase=True) -_auto_backend_sentinel = object() - - -def validate_backend(s): - backend = ( - s if s is _auto_backend_sentinel or s.startswith("module://") - else _validate_standard_backends(s)) - return backend - - -def _validate_toolbar(s): - s = ValidateInStrings( - 'toolbar', ['None', 'toolbar2', 'toolmanager'], ignorecase=True)(s) - if s == 'toolmanager': - _api.warn_external( - "Treat the new Tool classes introduced in v1.5 as experimental " - "for now; the API and rcParam may change in future versions.") - return s - - -def validate_color_or_inherit(s): - """Return a valid color arg.""" - if cbook._str_equal(s, 'inherit'): - return s - return validate_color(s) - - -def validate_color_or_auto(s): - if cbook._str_equal(s, 'auto'): - return s - return validate_color(s) - - -def validate_color_for_prop_cycle(s): - # N-th color cycle syntax can't go into the color cycle. - if isinstance(s, str) and re.match("^C[0-9]$", s): - raise ValueError(f"Cannot put cycle reference ({s!r}) in prop_cycler") - return validate_color(s) - - -def _validate_color_or_linecolor(s): - if cbook._str_equal(s, 'linecolor'): - return s - elif cbook._str_equal(s, 'mfc') or cbook._str_equal(s, 'markerfacecolor'): - return 'markerfacecolor' - elif cbook._str_equal(s, 'mec') or cbook._str_equal(s, 'markeredgecolor'): - return 'markeredgecolor' - elif s is None: - return None - elif isinstance(s, str) and len(s) == 6 or len(s) == 8: - stmp = '#' + s - if is_color_like(stmp): - return stmp - if s.lower() == 'none': - return None - elif is_color_like(s): - return s - - raise ValueError(f'{s!r} does not look like a color arg') - - -def validate_color(s): - """Return a valid color arg.""" - if isinstance(s, str): - if s.lower() == 'none': - return 'none' - if len(s) == 6 or len(s) == 8: - stmp = '#' + s - if is_color_like(stmp): - return stmp - - if is_color_like(s): - return s - - # If it is still valid, it must be a tuple (as a string from matplotlibrc). - try: - color = ast.literal_eval(s) - except (SyntaxError, ValueError): - pass - else: - if is_color_like(color): - return color - - raise ValueError(f'{s!r} does not look like a color arg') - - -validate_colorlist = _listify_validator( - validate_color, allow_stringlist=True, doc='return a list of colorspecs') - - -def _validate_cmap(s): - _api.check_isinstance((str, Colormap), cmap=s) - return s - - -def validate_aspect(s): - if s in ('auto', 'equal'): - return s - try: - return float(s) - except ValueError as e: - raise ValueError('not a valid aspect specification') from e - - -def validate_fontsize_None(s): - if s is None or s == 'None': - return None - else: - return validate_fontsize(s) - - -def validate_fontsize(s): - fontsizes = ['xx-small', 'x-small', 'small', 'medium', 'large', - 'x-large', 'xx-large', 'smaller', 'larger'] - if isinstance(s, str): - s = s.lower() - if s in fontsizes: - return s - try: - return float(s) - except ValueError as e: - raise ValueError("%s is not a valid font size. Valid font sizes " - "are %s." % (s, ", ".join(fontsizes))) from e - - -validate_fontsizelist = _listify_validator(validate_fontsize) - - -def validate_fontweight(s): - weights = [ - 'ultralight', 'light', 'normal', 'regular', 'book', 'medium', 'roman', - 'semibold', 'demibold', 'demi', 'bold', 'heavy', 'extra bold', 'black'] - # Note: Historically, weights have been case-sensitive in Matplotlib - if s in weights: - return s - try: - return int(s) - except (ValueError, TypeError) as e: - raise ValueError(f'{s} is not a valid font weight.') from e - - -def validate_fontstretch(s): - stretchvalues = [ - 'ultra-condensed', 'extra-condensed', 'condensed', 'semi-condensed', - 'normal', 'semi-expanded', 'expanded', 'extra-expanded', - 'ultra-expanded'] - # Note: Historically, stretchvalues have been case-sensitive in Matplotlib - if s in stretchvalues: - return s - try: - return int(s) - except (ValueError, TypeError) as e: - raise ValueError(f'{s} is not a valid font stretch.') from e - - -def validate_font_properties(s): - parse_fontconfig_pattern(s) - return s - - -def _validate_mathtext_fallback(s): - _fallback_fonts = ['cm', 'stix', 'stixsans'] - if isinstance(s, str): - s = s.lower() - if s is None or s == 'none': - return None - elif s.lower() in _fallback_fonts: - return s - else: - raise ValueError( - f"{s} is not a valid fallback font name. Valid fallback font " - f"names are {','.join(_fallback_fonts)}. Passing 'None' will turn " - "fallback off.") - - -def validate_whiskers(s): - try: - return _listify_validator(validate_float, n=2)(s) - except (TypeError, ValueError): - try: - return float(s) - except ValueError as e: - raise ValueError("Not a valid whisker value [float, " - "(float, float)]") from e - - -def validate_ps_distiller(s): - if isinstance(s, str): - s = s.lower() - if s in ('none', None, 'false', False): - return None - else: - return ValidateInStrings('ps.usedistiller', ['ghostscript', 'xpdf'])(s) - - -# A validator dedicated to the named line styles, based on the items in -# ls_mapper, and a list of possible strings read from Line2D.set_linestyle -_validate_named_linestyle = ValidateInStrings( - 'linestyle', - [*ls_mapper.keys(), *ls_mapper.values(), 'None', 'none', ' ', ''], - ignorecase=True) - - -def _validate_linestyle(ls): - """ - A validator for all possible line styles, the named ones *and* - the on-off ink sequences. - """ - if isinstance(ls, str): - try: # Look first for a valid named line style, like '--' or 'solid'. - return _validate_named_linestyle(ls) - except ValueError: - pass - try: - ls = ast.literal_eval(ls) # Parsing matplotlibrc. - except (SyntaxError, ValueError): - pass # Will error with the ValueError at the end. - - def _is_iterable_not_string_like(x): - # Explicitly exclude bytes/bytearrays so that they are not - # nonsensically interpreted as sequences of numbers (codepoints). - return np.iterable(x) and not isinstance(x, (str, bytes, bytearray)) - - if _is_iterable_not_string_like(ls): - if len(ls) == 2 and _is_iterable_not_string_like(ls[1]): - # (offset, (on, off, on, off, ...)) - offset, onoff = ls - else: - # For backcompat: (on, off, on, off, ...); the offset is implicit. - offset = 0 - onoff = ls - - if (isinstance(offset, Number) - and len(onoff) % 2 == 0 - and all(isinstance(elem, Number) for elem in onoff)): - return (offset, onoff) - - raise ValueError(f"linestyle {ls!r} is not a valid on-off ink sequence.") - - -validate_fillstyle = ValidateInStrings( - 'markers.fillstyle', ['full', 'left', 'right', 'bottom', 'top', 'none']) - - -validate_fillstylelist = _listify_validator(validate_fillstyle) - - -def validate_markevery(s): - """ - Validate the markevery property of a Line2D object. - - Parameters - ---------- - s : None, int, (int, int), slice, float, (float, float), or list[int] - - Returns - ------- - None, int, (int, int), slice, float, (float, float), or list[int] - """ - # Validate s against type slice float int and None - if isinstance(s, (slice, float, int, type(None))): - return s - # Validate s against type tuple - if isinstance(s, tuple): - if (len(s) == 2 - and (all(isinstance(e, int) for e in s) - or all(isinstance(e, float) for e in s))): - return s - else: - raise TypeError( - "'markevery' tuple must be pair of ints or of floats") - # Validate s against type list - if isinstance(s, list): - if all(isinstance(e, int) for e in s): - return s - else: - raise TypeError( - "'markevery' list must have all elements of type int") - raise TypeError("'markevery' is of an invalid type") - - -validate_markeverylist = _listify_validator(validate_markevery) - - -def validate_bbox(s): - if isinstance(s, str): - s = s.lower() - if s == 'tight': - return s - if s == 'standard': - return None - raise ValueError("bbox should be 'tight' or 'standard'") - elif s is not None: - # Backwards compatibility. None is equivalent to 'standard'. - raise ValueError("bbox should be 'tight' or 'standard'") - return s - - -def validate_sketch(s): - if isinstance(s, str): - s = s.lower() - if s == 'none' or s is None: - return None - try: - return tuple(_listify_validator(validate_float, n=3)(s)) - except ValueError: - raise ValueError("Expected a (scale, length, randomness) triplet") - - -def _validate_greaterequal0_lessthan1(s): - s = validate_float(s) - if 0 <= s < 1: - return s - else: - raise RuntimeError(f'Value must be >=0 and <1; got {s}') - - -def _validate_greaterequal0_lessequal1(s): - s = validate_float(s) - if 0 <= s <= 1: - return s - else: - raise RuntimeError(f'Value must be >=0 and <=1; got {s}') - - -_range_validators = { # Slightly nicer (internal) API. - "0 <= x < 1": _validate_greaterequal0_lessthan1, - "0 <= x <= 1": _validate_greaterequal0_lessequal1, -} - - -def validate_hatch(s): - r""" - Validate a hatch pattern. - A hatch pattern string can have any sequence of the following - characters: ``\ / | - + * . x o O``. - """ - if not isinstance(s, str): - raise ValueError("Hatch pattern must be a string") - _api.check_isinstance(str, hatch_pattern=s) - unknown = set(s) - {'\\', '/', '|', '-', '+', '*', '.', 'x', 'o', 'O'} - if unknown: - raise ValueError("Unknown hatch symbol(s): %s" % list(unknown)) - return s - - -validate_hatchlist = _listify_validator(validate_hatch) -validate_dashlist = _listify_validator(validate_floatlist) - - -_prop_validators = { - 'color': _listify_validator(validate_color_for_prop_cycle, - allow_stringlist=True), - 'linewidth': validate_floatlist, - 'linestyle': _listify_validator(_validate_linestyle), - 'facecolor': validate_colorlist, - 'edgecolor': validate_colorlist, - 'joinstyle': _listify_validator(JoinStyle), - 'capstyle': _listify_validator(CapStyle), - 'fillstyle': validate_fillstylelist, - 'markerfacecolor': validate_colorlist, - 'markersize': validate_floatlist, - 'markeredgewidth': validate_floatlist, - 'markeredgecolor': validate_colorlist, - 'markevery': validate_markeverylist, - 'alpha': validate_floatlist, - 'marker': validate_stringlist, - 'hatch': validate_hatchlist, - 'dashes': validate_dashlist, - } -_prop_aliases = { - 'c': 'color', - 'lw': 'linewidth', - 'ls': 'linestyle', - 'fc': 'facecolor', - 'ec': 'edgecolor', - 'mfc': 'markerfacecolor', - 'mec': 'markeredgecolor', - 'mew': 'markeredgewidth', - 'ms': 'markersize', - } - - -def cycler(*args, **kwargs): - """ - Create a `~cycler.Cycler` object much like :func:`cycler.cycler`, - but includes input validation. - - Call signatures:: - - cycler(cycler) - cycler(label=values[, label2=values2[, ...]]) - cycler(label, values) - - Form 1 copies a given `~cycler.Cycler` object. - - Form 2 creates a `~cycler.Cycler` which cycles over one or more - properties simultaneously. If multiple properties are given, their - value lists must have the same length. - - Form 3 creates a `~cycler.Cycler` for a single property. This form - exists for compatibility with the original cycler. Its use is - discouraged in favor of the kwarg form, i.e. ``cycler(label=values)``. - - Parameters - ---------- - cycler : Cycler - Copy constructor for Cycler. - - label : str - The property key. Must be a valid `.Artist` property. - For example, 'color' or 'linestyle'. Aliases are allowed, - such as 'c' for 'color' and 'lw' for 'linewidth'. - - values : iterable - Finite-length iterable of the property values. These values - are validated and will raise a ValueError if invalid. - - Returns - ------- - Cycler - A new :class:`~cycler.Cycler` for the given properties. - - Examples - -------- - Creating a cycler for a single property: - - >>> c = cycler(color=['red', 'green', 'blue']) - - Creating a cycler for simultaneously cycling over multiple properties - (e.g. red circle, green plus, blue cross): - - >>> c = cycler(color=['red', 'green', 'blue'], - ... marker=['o', '+', 'x']) - - """ - if args and kwargs: - raise TypeError("cycler() can only accept positional OR keyword " - "arguments -- not both.") - elif not args and not kwargs: - raise TypeError("cycler() must have positional OR keyword arguments") - - if len(args) == 1: - if not isinstance(args[0], Cycler): - raise TypeError("If only one positional argument given, it must " - "be a Cycler instance.") - return validate_cycler(args[0]) - elif len(args) == 2: - pairs = [(args[0], args[1])] - elif len(args) > 2: - raise TypeError("No more than 2 positional arguments allowed") - else: - pairs = kwargs.items() - - validated = [] - for prop, vals in pairs: - norm_prop = _prop_aliases.get(prop, prop) - validator = _prop_validators.get(norm_prop, None) - if validator is None: - raise TypeError("Unknown artist property: %s" % prop) - vals = validator(vals) - # We will normalize the property names as well to reduce - # the amount of alias handling code elsewhere. - validated.append((norm_prop, vals)) - - return reduce(operator.add, (ccycler(k, v) for k, v in validated)) - - -class _DunderChecker(ast.NodeVisitor): - def visit_Attribute(self, node): - if node.attr.startswith("__") and node.attr.endswith("__"): - raise ValueError("cycler strings with dunders are forbidden") - self.generic_visit(node) - - -def validate_cycler(s): - """Return a Cycler object from a string repr or the object itself.""" - if isinstance(s, str): - # TODO: We might want to rethink this... - # While I think I have it quite locked down, it is execution of - # arbitrary code without sanitation. - # Combine this with the possibility that rcparams might come from the - # internet (future plans), this could be downright dangerous. - # I locked it down by only having the 'cycler()' function available. - # UPDATE: Partly plugging a security hole. - # I really should have read this: - # https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html - # We should replace this eval with a combo of PyParsing and - # ast.literal_eval() - try: - _DunderChecker().visit(ast.parse(s)) - s = eval(s, {'cycler': cycler, '__builtins__': {}}) - except BaseException as e: - raise ValueError(f"{s!r} is not a valid cycler construction: {e}" - ) from e - # Should make sure what comes from the above eval() - # is a Cycler object. - if isinstance(s, Cycler): - cycler_inst = s - else: - raise ValueError(f"Object is not a string or Cycler instance: {s!r}") - - unknowns = cycler_inst.keys - (set(_prop_validators) | set(_prop_aliases)) - if unknowns: - raise ValueError("Unknown artist properties: %s" % unknowns) - - # Not a full validation, but it'll at least normalize property names - # A fuller validation would require v0.10 of cycler. - checker = set() - for prop in cycler_inst.keys: - norm_prop = _prop_aliases.get(prop, prop) - if norm_prop != prop and norm_prop in cycler_inst.keys: - raise ValueError(f"Cannot specify both {norm_prop!r} and alias " - f"{prop!r} in the same prop_cycle") - if norm_prop in checker: - raise ValueError(f"Another property was already aliased to " - f"{norm_prop!r}. Collision normalizing {prop!r}.") - checker.update([norm_prop]) - - # This is just an extra-careful check, just in case there is some - # edge-case I haven't thought of. - assert len(checker) == len(cycler_inst.keys) - - # Now, it should be safe to mutate this cycler - for prop in cycler_inst.keys: - norm_prop = _prop_aliases.get(prop, prop) - cycler_inst.change_key(prop, norm_prop) - - for key, vals in cycler_inst.by_key().items(): - _prop_validators[key](vals) - - return cycler_inst - - -def validate_hist_bins(s): - valid_strs = ["auto", "sturges", "fd", "doane", "scott", "rice", "sqrt"] - if isinstance(s, str) and s in valid_strs: - return s - try: - return int(s) - except (TypeError, ValueError): - pass - try: - return validate_floatlist(s) - except ValueError: - pass - raise ValueError("'hist.bins' must be one of {}, an int or" - " a sequence of floats".format(valid_strs)) - - -class _ignorecase(list): - """A marker class indicating that a list-of-str is case-insensitive.""" - - -def _convert_validator_spec(key, conv): - if isinstance(conv, list): - ignorecase = isinstance(conv, _ignorecase) - return ValidateInStrings(key, conv, ignorecase=ignorecase) - else: - return conv - - -# Mapping of rcParams to validators. -# Converters given as lists or _ignorecase are converted to ValidateInStrings -# immediately below. -# The rcParams defaults are defined in lib/matplotlib/mpl-data/matplotlibrc, which -# gets copied to matplotlib/mpl-data/matplotlibrc by the setup script. -_validators = { - "backend": validate_backend, - "backend_fallback": validate_bool, - "figure.hooks": validate_stringlist, - "toolbar": _validate_toolbar, - "interactive": validate_bool, - "timezone": validate_string, - - "webagg.port": validate_int, - "webagg.address": validate_string, - "webagg.open_in_browser": validate_bool, - "webagg.port_retries": validate_int, - - # line props - "lines.linewidth": validate_float, # line width in points - "lines.linestyle": _validate_linestyle, # solid line - "lines.color": validate_color, # first color in color cycle - "lines.marker": validate_string, # marker name - "lines.markerfacecolor": validate_color_or_auto, # default color - "lines.markeredgecolor": validate_color_or_auto, # default color - "lines.markeredgewidth": validate_float, - "lines.markersize": validate_float, # markersize, in points - "lines.antialiased": validate_bool, # antialiased (no jaggies) - "lines.dash_joinstyle": JoinStyle, - "lines.solid_joinstyle": JoinStyle, - "lines.dash_capstyle": CapStyle, - "lines.solid_capstyle": CapStyle, - "lines.dashed_pattern": validate_floatlist, - "lines.dashdot_pattern": validate_floatlist, - "lines.dotted_pattern": validate_floatlist, - "lines.scale_dashes": validate_bool, - - # marker props - "markers.fillstyle": validate_fillstyle, - - ## pcolor(mesh) props: - "pcolor.shading": ["auto", "flat", "nearest", "gouraud"], - "pcolormesh.snap": validate_bool, - - ## patch props - "patch.linewidth": validate_float, # line width in points - "patch.edgecolor": validate_color, - "patch.force_edgecolor": validate_bool, - "patch.facecolor": validate_color, # first color in cycle - "patch.antialiased": validate_bool, # antialiased (no jaggies) - - ## hatch props - "hatch.color": validate_color, - "hatch.linewidth": validate_float, - - ## Histogram properties - "hist.bins": validate_hist_bins, - - ## Boxplot properties - "boxplot.notch": validate_bool, - "boxplot.vertical": validate_bool, - "boxplot.whiskers": validate_whiskers, - "boxplot.bootstrap": validate_int_or_None, - "boxplot.patchartist": validate_bool, - "boxplot.showmeans": validate_bool, - "boxplot.showcaps": validate_bool, - "boxplot.showbox": validate_bool, - "boxplot.showfliers": validate_bool, - "boxplot.meanline": validate_bool, - - "boxplot.flierprops.color": validate_color, - "boxplot.flierprops.marker": validate_string, - "boxplot.flierprops.markerfacecolor": validate_color_or_auto, - "boxplot.flierprops.markeredgecolor": validate_color, - "boxplot.flierprops.markeredgewidth": validate_float, - "boxplot.flierprops.markersize": validate_float, - "boxplot.flierprops.linestyle": _validate_linestyle, - "boxplot.flierprops.linewidth": validate_float, - - "boxplot.boxprops.color": validate_color, - "boxplot.boxprops.linewidth": validate_float, - "boxplot.boxprops.linestyle": _validate_linestyle, - - "boxplot.whiskerprops.color": validate_color, - "boxplot.whiskerprops.linewidth": validate_float, - "boxplot.whiskerprops.linestyle": _validate_linestyle, - - "boxplot.capprops.color": validate_color, - "boxplot.capprops.linewidth": validate_float, - "boxplot.capprops.linestyle": _validate_linestyle, - - "boxplot.medianprops.color": validate_color, - "boxplot.medianprops.linewidth": validate_float, - "boxplot.medianprops.linestyle": _validate_linestyle, - - "boxplot.meanprops.color": validate_color, - "boxplot.meanprops.marker": validate_string, - "boxplot.meanprops.markerfacecolor": validate_color, - "boxplot.meanprops.markeredgecolor": validate_color, - "boxplot.meanprops.markersize": validate_float, - "boxplot.meanprops.linestyle": _validate_linestyle, - "boxplot.meanprops.linewidth": validate_float, - - ## font props - "font.family": validate_stringlist, # used by text object - "font.style": validate_string, - "font.variant": validate_string, - "font.stretch": validate_fontstretch, - "font.weight": validate_fontweight, - "font.size": validate_float, # Base font size in points - "font.serif": validate_stringlist, - "font.sans-serif": validate_stringlist, - "font.cursive": validate_stringlist, - "font.fantasy": validate_stringlist, - "font.monospace": validate_stringlist, - - # text props - "text.color": validate_color, - "text.usetex": validate_bool, - "text.latex.preamble": validate_string, - "text.hinting": ["default", "no_autohint", "force_autohint", - "no_hinting", "auto", "native", "either", "none"], - "text.hinting_factor": validate_int, - "text.kerning_factor": validate_int, - "text.antialiased": validate_bool, - "text.parse_math": validate_bool, - - "mathtext.cal": validate_font_properties, - "mathtext.rm": validate_font_properties, - "mathtext.tt": validate_font_properties, - "mathtext.it": validate_font_properties, - "mathtext.bf": validate_font_properties, - "mathtext.sf": validate_font_properties, - "mathtext.fontset": ["dejavusans", "dejavuserif", "cm", "stix", - "stixsans", "custom"], - "mathtext.default": ["rm", "cal", "it", "tt", "sf", "bf", "default", - "bb", "frak", "scr", "regular"], - "mathtext.fallback": _validate_mathtext_fallback, - - "image.aspect": validate_aspect, # equal, auto, a number - "image.interpolation": validate_string, - "image.cmap": _validate_cmap, # gray, jet, etc. - "image.lut": validate_int, # lookup table - "image.origin": ["upper", "lower"], - "image.resample": validate_bool, - # Specify whether vector graphics backends will combine all images on a - # set of axes into a single composite image - "image.composite_image": validate_bool, - - # contour props - "contour.negative_linestyle": _validate_linestyle, - "contour.corner_mask": validate_bool, - "contour.linewidth": validate_float_or_None, - "contour.algorithm": ["mpl2005", "mpl2014", "serial", "threaded"], - - # errorbar props - "errorbar.capsize": validate_float, - - # axis props - # alignment of x/y axis title - "xaxis.labellocation": ["left", "center", "right"], - "yaxis.labellocation": ["bottom", "center", "top"], - - # axes props - "axes.axisbelow": validate_axisbelow, - "axes.facecolor": validate_color, # background color - "axes.edgecolor": validate_color, # edge color - "axes.linewidth": validate_float, # edge linewidth - - "axes.spines.left": validate_bool, # Set visibility of axes spines, - "axes.spines.right": validate_bool, # i.e., the lines around the chart - "axes.spines.bottom": validate_bool, # denoting data boundary. - "axes.spines.top": validate_bool, - - "axes.titlesize": validate_fontsize, # axes title fontsize - "axes.titlelocation": ["left", "center", "right"], # axes title alignment - "axes.titleweight": validate_fontweight, # axes title font weight - "axes.titlecolor": validate_color_or_auto, # axes title font color - # title location, axes units, None means auto - "axes.titley": validate_float_or_None, - # pad from axes top decoration to title in points - "axes.titlepad": validate_float, - "axes.grid": validate_bool, # display grid or not - "axes.grid.which": ["minor", "both", "major"], # which grids are drawn - "axes.grid.axis": ["x", "y", "both"], # grid type - "axes.labelsize": validate_fontsize, # fontsize of x & y labels - "axes.labelpad": validate_float, # space between label and axis - "axes.labelweight": validate_fontweight, # fontsize of x & y labels - "axes.labelcolor": validate_color, # color of axis label - # use scientific notation if log10 of the axis range is smaller than the - # first or larger than the second - "axes.formatter.limits": _listify_validator(validate_int, n=2), - # use current locale to format ticks - "axes.formatter.use_locale": validate_bool, - "axes.formatter.use_mathtext": validate_bool, - # minimum exponent to format in scientific notation - "axes.formatter.min_exponent": validate_int, - "axes.formatter.useoffset": validate_bool, - "axes.formatter.offset_threshold": validate_int, - "axes.unicode_minus": validate_bool, - # This entry can be either a cycler object or a string repr of a - # cycler-object, which gets eval()'ed to create the object. - "axes.prop_cycle": validate_cycler, - # If "data", axes limits are set close to the data. - # If "round_numbers" axes limits are set to the nearest round numbers. - "axes.autolimit_mode": ["data", "round_numbers"], - "axes.xmargin": _range_validators["0 <= x <= 1"], # margin added to xaxis - "axes.ymargin": _range_validators["0 <= x <= 1"], # margin added to yaxis - 'axes.zmargin': _range_validators["0 <= x <= 1"], # margin added to zaxis - - "polaraxes.grid": validate_bool, # display polar grid or not - "axes3d.grid": validate_bool, # display 3d grid - - "axes3d.xaxis.panecolor": validate_color, # 3d background pane - "axes3d.yaxis.panecolor": validate_color, # 3d background pane - "axes3d.zaxis.panecolor": validate_color, # 3d background pane - - # scatter props - "scatter.marker": validate_string, - "scatter.edgecolors": validate_string, - - "date.epoch": _validate_date, - "date.autoformatter.year": validate_string, - "date.autoformatter.month": validate_string, - "date.autoformatter.day": validate_string, - "date.autoformatter.hour": validate_string, - "date.autoformatter.minute": validate_string, - "date.autoformatter.second": validate_string, - "date.autoformatter.microsecond": validate_string, - - 'date.converter': ['auto', 'concise'], - # for auto date locator, choose interval_multiples - 'date.interval_multiples': validate_bool, - - # legend properties - "legend.fancybox": validate_bool, - "legend.loc": _ignorecase([ - "best", - "upper right", "upper left", "lower left", "lower right", "right", - "center left", "center right", "lower center", "upper center", - "center"]), - - # the number of points in the legend line - "legend.numpoints": validate_int, - # the number of points in the legend line for scatter - "legend.scatterpoints": validate_int, - "legend.fontsize": validate_fontsize, - "legend.title_fontsize": validate_fontsize_None, - # color of the legend - "legend.labelcolor": _validate_color_or_linecolor, - # the relative size of legend markers vs. original - "legend.markerscale": validate_float, - "legend.shadow": validate_bool, - # whether or not to draw a frame around legend - "legend.frameon": validate_bool, - # alpha value of the legend frame - "legend.framealpha": validate_float_or_None, - - ## the following dimensions are in fraction of the font size - "legend.borderpad": validate_float, # units are fontsize - # the vertical space between the legend entries - "legend.labelspacing": validate_float, - # the length of the legend lines - "legend.handlelength": validate_float, - # the length of the legend lines - "legend.handleheight": validate_float, - # the space between the legend line and legend text - "legend.handletextpad": validate_float, - # the border between the axes and legend edge - "legend.borderaxespad": validate_float, - # the border between the axes and legend edge - "legend.columnspacing": validate_float, - "legend.facecolor": validate_color_or_inherit, - "legend.edgecolor": validate_color_or_inherit, - - # tick properties - "xtick.top": validate_bool, # draw ticks on top side - "xtick.bottom": validate_bool, # draw ticks on bottom side - "xtick.labeltop": validate_bool, # draw label on top - "xtick.labelbottom": validate_bool, # draw label on bottom - "xtick.major.size": validate_float, # major xtick size in points - "xtick.minor.size": validate_float, # minor xtick size in points - "xtick.major.width": validate_float, # major xtick width in points - "xtick.minor.width": validate_float, # minor xtick width in points - "xtick.major.pad": validate_float, # distance to label in points - "xtick.minor.pad": validate_float, # distance to label in points - "xtick.color": validate_color, # color of xticks - "xtick.labelcolor": validate_color_or_inherit, # color of xtick labels - "xtick.minor.visible": validate_bool, # visibility of minor xticks - "xtick.minor.top": validate_bool, # draw top minor xticks - "xtick.minor.bottom": validate_bool, # draw bottom minor xticks - "xtick.major.top": validate_bool, # draw top major xticks - "xtick.major.bottom": validate_bool, # draw bottom major xticks - "xtick.labelsize": validate_fontsize, # fontsize of xtick labels - "xtick.direction": ["out", "in", "inout"], # direction of xticks - "xtick.alignment": ["center", "right", "left"], - - "ytick.left": validate_bool, # draw ticks on left side - "ytick.right": validate_bool, # draw ticks on right side - "ytick.labelleft": validate_bool, # draw tick labels on left side - "ytick.labelright": validate_bool, # draw tick labels on right side - "ytick.major.size": validate_float, # major ytick size in points - "ytick.minor.size": validate_float, # minor ytick size in points - "ytick.major.width": validate_float, # major ytick width in points - "ytick.minor.width": validate_float, # minor ytick width in points - "ytick.major.pad": validate_float, # distance to label in points - "ytick.minor.pad": validate_float, # distance to label in points - "ytick.color": validate_color, # color of yticks - "ytick.labelcolor": validate_color_or_inherit, # color of ytick labels - "ytick.minor.visible": validate_bool, # visibility of minor yticks - "ytick.minor.left": validate_bool, # draw left minor yticks - "ytick.minor.right": validate_bool, # draw right minor yticks - "ytick.major.left": validate_bool, # draw left major yticks - "ytick.major.right": validate_bool, # draw right major yticks - "ytick.labelsize": validate_fontsize, # fontsize of ytick labels - "ytick.direction": ["out", "in", "inout"], # direction of yticks - "ytick.alignment": [ - "center", "top", "bottom", "baseline", "center_baseline"], - - "grid.color": validate_color, # grid color - "grid.linestyle": _validate_linestyle, # solid - "grid.linewidth": validate_float, # in points - "grid.alpha": validate_float, - - ## figure props - # figure title - "figure.titlesize": validate_fontsize, - "figure.titleweight": validate_fontweight, - - # figure labels - "figure.labelsize": validate_fontsize, - "figure.labelweight": validate_fontweight, - - # figure size in inches: width by height - "figure.figsize": _listify_validator(validate_float, n=2), - "figure.dpi": validate_float, - "figure.facecolor": validate_color, - "figure.edgecolor": validate_color, - "figure.frameon": validate_bool, - "figure.autolayout": validate_bool, - "figure.max_open_warning": validate_int, - "figure.raise_window": validate_bool, - - "figure.subplot.left": _range_validators["0 <= x <= 1"], - "figure.subplot.right": _range_validators["0 <= x <= 1"], - "figure.subplot.bottom": _range_validators["0 <= x <= 1"], - "figure.subplot.top": _range_validators["0 <= x <= 1"], - "figure.subplot.wspace": _range_validators["0 <= x < 1"], - "figure.subplot.hspace": _range_validators["0 <= x < 1"], - - "figure.constrained_layout.use": validate_bool, # run constrained_layout? - # wspace and hspace are fraction of adjacent subplots to use for space. - # Much smaller than above because we don't need room for the text. - "figure.constrained_layout.hspace": _range_validators["0 <= x < 1"], - "figure.constrained_layout.wspace": _range_validators["0 <= x < 1"], - # buffer around the axes, in inches. - 'figure.constrained_layout.h_pad': validate_float, - 'figure.constrained_layout.w_pad': validate_float, - - ## Saving figure's properties - 'savefig.dpi': validate_dpi, - 'savefig.facecolor': validate_color_or_auto, - 'savefig.edgecolor': validate_color_or_auto, - 'savefig.orientation': ['landscape', 'portrait'], - "savefig.format": validate_string, - "savefig.bbox": validate_bbox, # "tight", or "standard" (= None) - "savefig.pad_inches": validate_float, - # default directory in savefig dialog box - "savefig.directory": _validate_pathlike, - "savefig.transparent": validate_bool, - - "tk.window_focus": validate_bool, # Maintain shell focus for TkAgg - - # Set the papersize/type - "ps.papersize": _ignorecase(["auto", "letter", "legal", "ledger", - *[f"{ab}{i}" - for ab in "ab" for i in range(11)]]), - "ps.useafm": validate_bool, - # use ghostscript or xpdf to distill ps output - "ps.usedistiller": validate_ps_distiller, - "ps.distiller.res": validate_int, # dpi - "ps.fonttype": validate_fonttype, # 3 (Type3) or 42 (Truetype) - "pdf.compression": validate_int, # 0-9 compression level; 0 to disable - "pdf.inheritcolor": validate_bool, # skip color setting commands - # use only the 14 PDF core fonts embedded in every PDF viewing application - "pdf.use14corefonts": validate_bool, - "pdf.fonttype": validate_fonttype, # 3 (Type3) or 42 (Truetype) - - "pgf.texsystem": ["xelatex", "lualatex", "pdflatex"], # latex variant used - "pgf.rcfonts": validate_bool, # use mpl's rc settings for font config - "pgf.preamble": validate_string, # custom LaTeX preamble - - # write raster image data into the svg file - "svg.image_inline": validate_bool, - "svg.fonttype": ["none", "path"], # save text as text ("none") or "paths" - "svg.hashsalt": validate_string_or_None, - - # set this when you want to generate hardcopy docstring - "docstring.hardcopy": validate_bool, - - "path.simplify": validate_bool, - "path.simplify_threshold": _range_validators["0 <= x <= 1"], - "path.snap": validate_bool, - "path.sketch": validate_sketch, - "path.effects": validate_anylist, - "agg.path.chunksize": validate_int, # 0 to disable chunking - - # key-mappings (multi-character mappings should be a list/tuple) - "keymap.fullscreen": validate_stringlist, - "keymap.home": validate_stringlist, - "keymap.back": validate_stringlist, - "keymap.forward": validate_stringlist, - "keymap.pan": validate_stringlist, - "keymap.zoom": validate_stringlist, - "keymap.save": validate_stringlist, - "keymap.quit": validate_stringlist, - "keymap.quit_all": validate_stringlist, # e.g.: "W", "cmd+W", "Q" - "keymap.grid": validate_stringlist, - "keymap.grid_minor": validate_stringlist, - "keymap.yscale": validate_stringlist, - "keymap.xscale": validate_stringlist, - "keymap.help": validate_stringlist, - "keymap.copy": validate_stringlist, - - # Animation settings - "animation.html": ["html5", "jshtml", "none"], - # Limit, in MB, of size of base64 encoded animation in HTML - # (i.e. IPython notebook) - "animation.embed_limit": validate_float, - "animation.writer": validate_string, - "animation.codec": validate_string, - "animation.bitrate": validate_int, - # Controls image format when frames are written to disk - "animation.frame_format": ["png", "jpeg", "tiff", "raw", "rgba", "ppm", - "sgi", "bmp", "pbm", "svg"], - # Path to ffmpeg binary. If just binary name, subprocess uses $PATH. - "animation.ffmpeg_path": _validate_pathlike, - # Additional arguments for ffmpeg movie writer (using pipes) - "animation.ffmpeg_args": validate_stringlist, - # Path to convert binary. If just binary name, subprocess uses $PATH. - "animation.convert_path": _validate_pathlike, - # Additional arguments for convert movie writer (using pipes) - "animation.convert_args": validate_stringlist, - - # Classic (pre 2.0) compatibility mode - # This is used for things that are hard to make backward compatible - # with a sane rcParam alone. This does *not* turn on classic mode - # altogether. For that use `matplotlib.style.use("classic")`. - "_internal.classic_mode": validate_bool -} -_hardcoded_defaults = { # Defaults not inferred from - # lib/matplotlib/mpl-data/matplotlibrc... - # ... because they are private: - "_internal.classic_mode": False, - # ... because they are deprecated: - # No current deprecations. - # backend is handled separately when constructing rcParamsDefault. -} -_validators = {k: _convert_validator_spec(k, conv) - for k, conv in _validators.items()} diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/sankey.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/sankey.py deleted file mode 100644 index 9ebae57..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/sankey.py +++ /dev/null @@ -1,822 +0,0 @@ -""" -Module for creating Sankey diagrams using Matplotlib. -""" - -import logging -from types import SimpleNamespace - -import numpy as np - -import matplotlib as mpl -from matplotlib.path import Path -from matplotlib.patches import PathPatch -from matplotlib.transforms import Affine2D -from matplotlib import _docstring - -_log = logging.getLogger(__name__) - -__author__ = "Kevin L. Davies" -__credits__ = ["Yannick Copin"] -__license__ = "BSD" -__version__ = "2011/09/16" - -# Angles [deg/90] -RIGHT = 0 -UP = 1 -# LEFT = 2 -DOWN = 3 - - -class Sankey: - """ - Sankey diagram. - - Sankey diagrams are a specific type of flow diagram, in which - the width of the arrows is shown proportionally to the flow - quantity. They are typically used to visualize energy or - material or cost transfers between processes. - `Wikipedia (6/1/2011) `_ - - """ - - def __init__(self, ax=None, scale=1.0, unit='', format='%G', gap=0.25, - radius=0.1, shoulder=0.03, offset=0.15, head_angle=100, - margin=0.4, tolerance=1e-6, **kwargs): - """ - Create a new Sankey instance. - - The optional arguments listed below are applied to all subdiagrams so - that there is consistent alignment and formatting. - - In order to draw a complex Sankey diagram, create an instance of - :class:`Sankey` by calling it without any kwargs:: - - sankey = Sankey() - - Then add simple Sankey sub-diagrams:: - - sankey.add() # 1 - sankey.add() # 2 - #... - sankey.add() # n - - Finally, create the full diagram:: - - sankey.finish() - - Or, instead, simply daisy-chain those calls:: - - Sankey().add().add... .add().finish() - - Other Parameters - ---------------- - ax : `~matplotlib.axes.Axes` - Axes onto which the data should be plotted. If *ax* isn't - provided, new Axes will be created. - scale : float - Scaling factor for the flows. *scale* sizes the width of the paths - in order to maintain proper layout. The same scale is applied to - all subdiagrams. The value should be chosen such that the product - of the scale and the sum of the inputs is approximately 1.0 (and - the product of the scale and the sum of the outputs is - approximately -1.0). - unit : str - The physical unit associated with the flow quantities. If *unit* - is None, then none of the quantities are labeled. - format : str or callable - A Python number formatting string or callable used to label the - flows with their quantities (i.e., a number times a unit, where the - unit is given). If a format string is given, the label will be - ``format % quantity``. If a callable is given, it will be called - with ``quantity`` as an argument. - gap : float - Space between paths that break in/break away to/from the top or - bottom. - radius : float - Inner radius of the vertical paths. - shoulder : float - Size of the shoulders of output arrows. - offset : float - Text offset (from the dip or tip of the arrow). - head_angle : float - Angle, in degrees, of the arrow heads (and negative of the angle of - the tails). - margin : float - Minimum space between Sankey outlines and the edge of the plot - area. - tolerance : float - Acceptable maximum of the magnitude of the sum of flows. The - magnitude of the sum of connected flows cannot be greater than - *tolerance*. - **kwargs - Any additional keyword arguments will be passed to :meth:`add`, - which will create the first subdiagram. - - See Also - -------- - Sankey.add - Sankey.finish - - Examples - -------- - .. plot:: gallery/specialty_plots/sankey_basics.py - """ - # Check the arguments. - if gap < 0: - raise ValueError( - "'gap' is negative, which is not allowed because it would " - "cause the paths to overlap") - if radius > gap: - raise ValueError( - "'radius' is greater than 'gap', which is not allowed because " - "it would cause the paths to overlap") - if head_angle < 0: - raise ValueError( - "'head_angle' is negative, which is not allowed because it " - "would cause inputs to look like outputs and vice versa") - if tolerance < 0: - raise ValueError( - "'tolerance' is negative, but it must be a magnitude") - - # Create axes if necessary. - if ax is None: - import matplotlib.pyplot as plt - fig = plt.figure() - ax = fig.add_subplot(1, 1, 1, xticks=[], yticks=[]) - - self.diagrams = [] - - # Store the inputs. - self.ax = ax - self.unit = unit - self.format = format - self.scale = scale - self.gap = gap - self.radius = radius - self.shoulder = shoulder - self.offset = offset - self.margin = margin - self.pitch = np.tan(np.pi * (1 - head_angle / 180.0) / 2.0) - self.tolerance = tolerance - - # Initialize the vertices of tight box around the diagram(s). - self.extent = np.array((np.inf, -np.inf, np.inf, -np.inf)) - - # If there are any kwargs, create the first subdiagram. - if len(kwargs): - self.add(**kwargs) - - def _arc(self, quadrant=0, cw=True, radius=1, center=(0, 0)): - """ - Return the codes and vertices for a rotated, scaled, and translated - 90 degree arc. - - Other Parameters - ---------------- - quadrant : {0, 1, 2, 3}, default: 0 - Uses 0-based indexing (0, 1, 2, or 3). - cw : bool, default: True - If True, the arc vertices are produced clockwise; counter-clockwise - otherwise. - radius : float, default: 1 - The radius of the arc. - center : (float, float), default: (0, 0) - (x, y) tuple of the arc's center. - """ - # Note: It would be possible to use matplotlib's transforms to rotate, - # scale, and translate the arc, but since the angles are discrete, - # it's just as easy and maybe more efficient to do it here. - ARC_CODES = [Path.LINETO, - Path.CURVE4, - Path.CURVE4, - Path.CURVE4, - Path.CURVE4, - Path.CURVE4, - Path.CURVE4] - # Vertices of a cubic Bezier curve approximating a 90 deg arc - # These can be determined by Path.arc(0, 90). - ARC_VERTICES = np.array([[1.00000000e+00, 0.00000000e+00], - [1.00000000e+00, 2.65114773e-01], - [8.94571235e-01, 5.19642327e-01], - [7.07106781e-01, 7.07106781e-01], - [5.19642327e-01, 8.94571235e-01], - [2.65114773e-01, 1.00000000e+00], - # Insignificant - # [6.12303177e-17, 1.00000000e+00]]) - [0.00000000e+00, 1.00000000e+00]]) - if quadrant in (0, 2): - if cw: - vertices = ARC_VERTICES - else: - vertices = ARC_VERTICES[:, ::-1] # Swap x and y. - else: # 1, 3 - # Negate x. - if cw: - # Swap x and y. - vertices = np.column_stack((-ARC_VERTICES[:, 1], - ARC_VERTICES[:, 0])) - else: - vertices = np.column_stack((-ARC_VERTICES[:, 0], - ARC_VERTICES[:, 1])) - if quadrant > 1: - radius = -radius # Rotate 180 deg. - return list(zip(ARC_CODES, radius * vertices + - np.tile(center, (ARC_VERTICES.shape[0], 1)))) - - def _add_input(self, path, angle, flow, length): - """ - Add an input to a path and return its tip and label locations. - """ - if angle is None: - return [0, 0], [0, 0] - else: - x, y = path[-1][1] # Use the last point as a reference. - dipdepth = (flow / 2) * self.pitch - if angle == RIGHT: - x -= length - dip = [x + dipdepth, y + flow / 2.0] - path.extend([(Path.LINETO, [x, y]), - (Path.LINETO, dip), - (Path.LINETO, [x, y + flow]), - (Path.LINETO, [x + self.gap, y + flow])]) - label_location = [dip[0] - self.offset, dip[1]] - else: # Vertical - x -= self.gap - if angle == UP: - sign = 1 - else: - sign = -1 - - dip = [x - flow / 2, y - sign * (length - dipdepth)] - if angle == DOWN: - quadrant = 2 - else: - quadrant = 1 - - # Inner arc isn't needed if inner radius is zero - if self.radius: - path.extend(self._arc(quadrant=quadrant, - cw=angle == UP, - radius=self.radius, - center=(x + self.radius, - y - sign * self.radius))) - else: - path.append((Path.LINETO, [x, y])) - path.extend([(Path.LINETO, [x, y - sign * length]), - (Path.LINETO, dip), - (Path.LINETO, [x - flow, y - sign * length])]) - path.extend(self._arc(quadrant=quadrant, - cw=angle == DOWN, - radius=flow + self.radius, - center=(x + self.radius, - y - sign * self.radius))) - path.append((Path.LINETO, [x - flow, y + sign * flow])) - label_location = [dip[0], dip[1] - sign * self.offset] - - return dip, label_location - - def _add_output(self, path, angle, flow, length): - """ - Append an output to a path and return its tip and label locations. - - .. note:: *flow* is negative for an output. - """ - if angle is None: - return [0, 0], [0, 0] - else: - x, y = path[-1][1] # Use the last point as a reference. - tipheight = (self.shoulder - flow / 2) * self.pitch - if angle == RIGHT: - x += length - tip = [x + tipheight, y + flow / 2.0] - path.extend([(Path.LINETO, [x, y]), - (Path.LINETO, [x, y + self.shoulder]), - (Path.LINETO, tip), - (Path.LINETO, [x, y - self.shoulder + flow]), - (Path.LINETO, [x, y + flow]), - (Path.LINETO, [x - self.gap, y + flow])]) - label_location = [tip[0] + self.offset, tip[1]] - else: # Vertical - x += self.gap - if angle == UP: - sign, quadrant = 1, 3 - else: - sign, quadrant = -1, 0 - - tip = [x - flow / 2.0, y + sign * (length + tipheight)] - # Inner arc isn't needed if inner radius is zero - if self.radius: - path.extend(self._arc(quadrant=quadrant, - cw=angle == UP, - radius=self.radius, - center=(x - self.radius, - y + sign * self.radius))) - else: - path.append((Path.LINETO, [x, y])) - path.extend([(Path.LINETO, [x, y + sign * length]), - (Path.LINETO, [x - self.shoulder, - y + sign * length]), - (Path.LINETO, tip), - (Path.LINETO, [x + self.shoulder - flow, - y + sign * length]), - (Path.LINETO, [x - flow, y + sign * length])]) - path.extend(self._arc(quadrant=quadrant, - cw=angle == DOWN, - radius=self.radius - flow, - center=(x - self.radius, - y + sign * self.radius))) - path.append((Path.LINETO, [x - flow, y + sign * flow])) - label_location = [tip[0], tip[1] + sign * self.offset] - return tip, label_location - - def _revert(self, path, first_action=Path.LINETO): - """ - A path is not simply reversible by path[::-1] since the code - specifies an action to take from the **previous** point. - """ - reverse_path = [] - next_code = first_action - for code, position in path[::-1]: - reverse_path.append((next_code, position)) - next_code = code - return reverse_path - # This might be more efficient, but it fails because 'tuple' object - # doesn't support item assignment: - # path[1] = path[1][-1:0:-1] - # path[1][0] = first_action - # path[2] = path[2][::-1] - # return path - - @_docstring.dedent_interpd - def add(self, patchlabel='', flows=None, orientations=None, labels='', - trunklength=1.0, pathlengths=0.25, prior=None, connect=(0, 0), - rotation=0, **kwargs): - """ - Add a simple Sankey diagram with flows at the same hierarchical level. - - Parameters - ---------- - patchlabel : str - Label to be placed at the center of the diagram. - Note that *label* (not *patchlabel*) can be passed as keyword - argument to create an entry in the legend. - - flows : list of float - Array of flow values. By convention, inputs are positive and - outputs are negative. - - Flows are placed along the top of the diagram from the inside out - in order of their index within *flows*. They are placed along the - sides of the diagram from the top down and along the bottom from - the outside in. - - If the sum of the inputs and outputs is - nonzero, the discrepancy will appear as a cubic Bézier curve along - the top and bottom edges of the trunk. - - orientations : list of {-1, 0, 1} - List of orientations of the flows (or a single orientation to be - used for all flows). Valid values are 0 (inputs from - the left, outputs to the right), 1 (from and to the top) or -1 - (from and to the bottom). - - labels : list of (str or None) - List of labels for the flows (or a single label to be used for all - flows). Each label may be *None* (no label), or a labeling string. - If an entry is a (possibly empty) string, then the quantity for the - corresponding flow will be shown below the string. However, if - the *unit* of the main diagram is None, then quantities are never - shown, regardless of the value of this argument. - - trunklength : float - Length between the bases of the input and output groups (in - data-space units). - - pathlengths : list of float - List of lengths of the vertical arrows before break-in or after - break-away. If a single value is given, then it will be applied to - the first (inside) paths on the top and bottom, and the length of - all other arrows will be justified accordingly. The *pathlengths* - are not applied to the horizontal inputs and outputs. - - prior : int - Index of the prior diagram to which this diagram should be - connected. - - connect : (int, int) - A (prior, this) tuple indexing the flow of the prior diagram and - the flow of this diagram which should be connected. If this is the - first diagram or *prior* is *None*, *connect* will be ignored. - - rotation : float - Angle of rotation of the diagram in degrees. The interpretation of - the *orientations* argument will be rotated accordingly (e.g., if - *rotation* == 90, an *orientations* entry of 1 means to/from the - left). *rotation* is ignored if this diagram is connected to an - existing one (using *prior* and *connect*). - - Returns - ------- - Sankey - The current `.Sankey` instance. - - Other Parameters - ---------------- - **kwargs - Additional keyword arguments set `matplotlib.patches.PathPatch` - properties, listed below. For example, one may want to use - ``fill=False`` or ``label="A legend entry"``. - - %(Patch:kwdoc)s - - See Also - -------- - Sankey.finish - """ - # Check and preprocess the arguments. - flows = np.array([1.0, -1.0]) if flows is None else np.array(flows) - n = flows.shape[0] # Number of flows - if rotation is None: - rotation = 0 - else: - # In the code below, angles are expressed in deg/90. - rotation /= 90.0 - if orientations is None: - orientations = 0 - try: - orientations = np.broadcast_to(orientations, n) - except ValueError: - raise ValueError( - f"The shapes of 'flows' {np.shape(flows)} and 'orientations' " - f"{np.shape(orientations)} are incompatible" - ) from None - try: - labels = np.broadcast_to(labels, n) - except ValueError: - raise ValueError( - f"The shapes of 'flows' {np.shape(flows)} and 'labels' " - f"{np.shape(labels)} are incompatible" - ) from None - if trunklength < 0: - raise ValueError( - "'trunklength' is negative, which is not allowed because it " - "would cause poor layout") - if abs(np.sum(flows)) > self.tolerance: - _log.info("The sum of the flows is nonzero (%f; patchlabel=%r); " - "is the system not at steady state?", - np.sum(flows), patchlabel) - scaled_flows = self.scale * flows - gain = sum(max(flow, 0) for flow in scaled_flows) - loss = sum(min(flow, 0) for flow in scaled_flows) - if prior is not None: - if prior < 0: - raise ValueError("The index of the prior diagram is negative") - if min(connect) < 0: - raise ValueError( - "At least one of the connection indices is negative") - if prior >= len(self.diagrams): - raise ValueError( - f"The index of the prior diagram is {prior}, but there " - f"are only {len(self.diagrams)} other diagrams") - if connect[0] >= len(self.diagrams[prior].flows): - raise ValueError( - "The connection index to the source diagram is {}, but " - "that diagram has only {} flows".format( - connect[0], len(self.diagrams[prior].flows))) - if connect[1] >= n: - raise ValueError( - f"The connection index to this diagram is {connect[1]}, " - f"but this diagram has only {n} flows") - if self.diagrams[prior].angles[connect[0]] is None: - raise ValueError( - f"The connection cannot be made, which may occur if the " - f"magnitude of flow {connect[0]} of diagram {prior} is " - f"less than the specified tolerance") - flow_error = (self.diagrams[prior].flows[connect[0]] + - flows[connect[1]]) - if abs(flow_error) >= self.tolerance: - raise ValueError( - f"The scaled sum of the connected flows is {flow_error}, " - f"which is not within the tolerance ({self.tolerance})") - - # Determine if the flows are inputs. - are_inputs = [None] * n - for i, flow in enumerate(flows): - if flow >= self.tolerance: - are_inputs[i] = True - elif flow <= -self.tolerance: - are_inputs[i] = False - else: - _log.info( - "The magnitude of flow %d (%f) is below the tolerance " - "(%f).\nIt will not be shown, and it cannot be used in a " - "connection.", i, flow, self.tolerance) - - # Determine the angles of the arrows (before rotation). - angles = [None] * n - for i, (orient, is_input) in enumerate(zip(orientations, are_inputs)): - if orient == 1: - if is_input: - angles[i] = DOWN - elif is_input is False: - # Be specific since is_input can be None. - angles[i] = UP - elif orient == 0: - if is_input is not None: - angles[i] = RIGHT - else: - if orient != -1: - raise ValueError( - f"The value of orientations[{i}] is {orient}, " - f"but it must be -1, 0, or 1") - if is_input: - angles[i] = UP - elif is_input is False: - angles[i] = DOWN - - # Justify the lengths of the paths. - if np.iterable(pathlengths): - if len(pathlengths) != n: - raise ValueError( - f"The lengths of 'flows' ({n}) and 'pathlengths' " - f"({len(pathlengths)}) are incompatible") - else: # Make pathlengths into a list. - urlength = pathlengths - ullength = pathlengths - lrlength = pathlengths - lllength = pathlengths - d = dict(RIGHT=pathlengths) - pathlengths = [d.get(angle, 0) for angle in angles] - # Determine the lengths of the top-side arrows - # from the middle outwards. - for i, (angle, is_input, flow) in enumerate(zip(angles, are_inputs, - scaled_flows)): - if angle == DOWN and is_input: - pathlengths[i] = ullength - ullength += flow - elif angle == UP and is_input is False: - pathlengths[i] = urlength - urlength -= flow # Flow is negative for outputs. - # Determine the lengths of the bottom-side arrows - # from the middle outwards. - for i, (angle, is_input, flow) in enumerate(reversed(list(zip( - angles, are_inputs, scaled_flows)))): - if angle == UP and is_input: - pathlengths[n - i - 1] = lllength - lllength += flow - elif angle == DOWN and is_input is False: - pathlengths[n - i - 1] = lrlength - lrlength -= flow - # Determine the lengths of the left-side arrows - # from the bottom upwards. - has_left_input = False - for i, (angle, is_input, spec) in enumerate(reversed(list(zip( - angles, are_inputs, zip(scaled_flows, pathlengths))))): - if angle == RIGHT: - if is_input: - if has_left_input: - pathlengths[n - i - 1] = 0 - else: - has_left_input = True - # Determine the lengths of the right-side arrows - # from the top downwards. - has_right_output = False - for i, (angle, is_input, spec) in enumerate(zip( - angles, are_inputs, list(zip(scaled_flows, pathlengths)))): - if angle == RIGHT: - if is_input is False: - if has_right_output: - pathlengths[i] = 0 - else: - has_right_output = True - - # Begin the subpaths, and smooth the transition if the sum of the flows - # is nonzero. - urpath = [(Path.MOVETO, [(self.gap - trunklength / 2.0), # Upper right - gain / 2.0]), - (Path.LINETO, [(self.gap - trunklength / 2.0) / 2.0, - gain / 2.0]), - (Path.CURVE4, [(self.gap - trunklength / 2.0) / 8.0, - gain / 2.0]), - (Path.CURVE4, [(trunklength / 2.0 - self.gap) / 8.0, - -loss / 2.0]), - (Path.LINETO, [(trunklength / 2.0 - self.gap) / 2.0, - -loss / 2.0]), - (Path.LINETO, [(trunklength / 2.0 - self.gap), - -loss / 2.0])] - llpath = [(Path.LINETO, [(trunklength / 2.0 - self.gap), # Lower left - loss / 2.0]), - (Path.LINETO, [(trunklength / 2.0 - self.gap) / 2.0, - loss / 2.0]), - (Path.CURVE4, [(trunklength / 2.0 - self.gap) / 8.0, - loss / 2.0]), - (Path.CURVE4, [(self.gap - trunklength / 2.0) / 8.0, - -gain / 2.0]), - (Path.LINETO, [(self.gap - trunklength / 2.0) / 2.0, - -gain / 2.0]), - (Path.LINETO, [(self.gap - trunklength / 2.0), - -gain / 2.0])] - lrpath = [(Path.LINETO, [(trunklength / 2.0 - self.gap), # Lower right - loss / 2.0])] - ulpath = [(Path.LINETO, [self.gap - trunklength / 2.0, # Upper left - gain / 2.0])] - - # Add the subpaths and assign the locations of the tips and labels. - tips = np.zeros((n, 2)) - label_locations = np.zeros((n, 2)) - # Add the top-side inputs and outputs from the middle outwards. - for i, (angle, is_input, spec) in enumerate(zip( - angles, are_inputs, list(zip(scaled_flows, pathlengths)))): - if angle == DOWN and is_input: - tips[i, :], label_locations[i, :] = self._add_input( - ulpath, angle, *spec) - elif angle == UP and is_input is False: - tips[i, :], label_locations[i, :] = self._add_output( - urpath, angle, *spec) - # Add the bottom-side inputs and outputs from the middle outwards. - for i, (angle, is_input, spec) in enumerate(reversed(list(zip( - angles, are_inputs, list(zip(scaled_flows, pathlengths)))))): - if angle == UP and is_input: - tip, label_location = self._add_input(llpath, angle, *spec) - tips[n - i - 1, :] = tip - label_locations[n - i - 1, :] = label_location - elif angle == DOWN and is_input is False: - tip, label_location = self._add_output(lrpath, angle, *spec) - tips[n - i - 1, :] = tip - label_locations[n - i - 1, :] = label_location - # Add the left-side inputs from the bottom upwards. - has_left_input = False - for i, (angle, is_input, spec) in enumerate(reversed(list(zip( - angles, are_inputs, list(zip(scaled_flows, pathlengths)))))): - if angle == RIGHT and is_input: - if not has_left_input: - # Make sure the lower path extends - # at least as far as the upper one. - if llpath[-1][1][0] > ulpath[-1][1][0]: - llpath.append((Path.LINETO, [ulpath[-1][1][0], - llpath[-1][1][1]])) - has_left_input = True - tip, label_location = self._add_input(llpath, angle, *spec) - tips[n - i - 1, :] = tip - label_locations[n - i - 1, :] = label_location - # Add the right-side outputs from the top downwards. - has_right_output = False - for i, (angle, is_input, spec) in enumerate(zip( - angles, are_inputs, list(zip(scaled_flows, pathlengths)))): - if angle == RIGHT and is_input is False: - if not has_right_output: - # Make sure the upper path extends - # at least as far as the lower one. - if urpath[-1][1][0] < lrpath[-1][1][0]: - urpath.append((Path.LINETO, [lrpath[-1][1][0], - urpath[-1][1][1]])) - has_right_output = True - tips[i, :], label_locations[i, :] = self._add_output( - urpath, angle, *spec) - # Trim any hanging vertices. - if not has_left_input: - ulpath.pop() - llpath.pop() - if not has_right_output: - lrpath.pop() - urpath.pop() - - # Concatenate the subpaths in the correct order (clockwise from top). - path = (urpath + self._revert(lrpath) + llpath + self._revert(ulpath) + - [(Path.CLOSEPOLY, urpath[0][1])]) - - # Create a patch with the Sankey outline. - codes, vertices = zip(*path) - vertices = np.array(vertices) - - def _get_angle(a, r): - if a is None: - return None - else: - return a + r - - if prior is None: - if rotation != 0: # By default, none of this is needed. - angles = [_get_angle(angle, rotation) for angle in angles] - rotate = Affine2D().rotate_deg(rotation * 90).transform_affine - tips = rotate(tips) - label_locations = rotate(label_locations) - vertices = rotate(vertices) - text = self.ax.text(0, 0, s=patchlabel, ha='center', va='center') - else: - rotation = (self.diagrams[prior].angles[connect[0]] - - angles[connect[1]]) - angles = [_get_angle(angle, rotation) for angle in angles] - rotate = Affine2D().rotate_deg(rotation * 90).transform_affine - tips = rotate(tips) - offset = self.diagrams[prior].tips[connect[0]] - tips[connect[1]] - translate = Affine2D().translate(*offset).transform_affine - tips = translate(tips) - label_locations = translate(rotate(label_locations)) - vertices = translate(rotate(vertices)) - kwds = dict(s=patchlabel, ha='center', va='center') - text = self.ax.text(*offset, **kwds) - if mpl.rcParams['_internal.classic_mode']: - fc = kwargs.pop('fc', kwargs.pop('facecolor', '#bfd1d4')) - lw = kwargs.pop('lw', kwargs.pop('linewidth', 0.5)) - else: - fc = kwargs.pop('fc', kwargs.pop('facecolor', None)) - lw = kwargs.pop('lw', kwargs.pop('linewidth', None)) - if fc is None: - fc = next(self.ax._get_patches_for_fill.prop_cycler)['color'] - patch = PathPatch(Path(vertices, codes), fc=fc, lw=lw, **kwargs) - self.ax.add_patch(patch) - - # Add the path labels. - texts = [] - for number, angle, label, location in zip(flows, angles, labels, - label_locations): - if label is None or angle is None: - label = '' - elif self.unit is not None: - if isinstance(self.format, str): - quantity = self.format % abs(number) + self.unit - elif callable(self.format): - quantity = self.format(number) - else: - raise TypeError( - 'format must be callable or a format string') - if label != '': - label += "\n" - label += quantity - texts.append(self.ax.text(x=location[0], y=location[1], - s=label, - ha='center', va='center')) - # Text objects are placed even they are empty (as long as the magnitude - # of the corresponding flow is larger than the tolerance) in case the - # user wants to provide labels later. - - # Expand the size of the diagram if necessary. - self.extent = (min(np.min(vertices[:, 0]), - np.min(label_locations[:, 0]), - self.extent[0]), - max(np.max(vertices[:, 0]), - np.max(label_locations[:, 0]), - self.extent[1]), - min(np.min(vertices[:, 1]), - np.min(label_locations[:, 1]), - self.extent[2]), - max(np.max(vertices[:, 1]), - np.max(label_locations[:, 1]), - self.extent[3])) - # Include both vertices _and_ label locations in the extents; there are - # where either could determine the margins (e.g., arrow shoulders). - - # Add this diagram as a subdiagram. - self.diagrams.append( - SimpleNamespace(patch=patch, flows=flows, angles=angles, tips=tips, - text=text, texts=texts)) - - # Allow a daisy-chained call structure (see docstring for the class). - return self - - def finish(self): - """ - Adjust the axes and return a list of information about the Sankey - subdiagram(s). - - Return value is a list of subdiagrams represented with the following - fields: - - =============== =================================================== - Field Description - =============== =================================================== - *patch* Sankey outline (an instance of - :class:`~matplotlib.patches.PathPatch`) - *flows* values of the flows (positive for input, negative - for output) - *angles* list of angles of the arrows [deg/90] - For example, if the diagram has not been rotated, - an input to the top side will have an angle of 3 - (DOWN), and an output from the top side will have - an angle of 1 (UP). If a flow has been skipped - (because its magnitude is less than *tolerance*), - then its angle will be *None*. - *tips* array in which each row is an [x, y] pair - indicating the positions of the tips (or "dips") of - the flow paths - If the magnitude of a flow is less the *tolerance* - for the instance of :class:`Sankey`, the flow is - skipped and its tip will be at the center of the - diagram. - *text* :class:`~matplotlib.text.Text` instance for the - label of the diagram - *texts* list of :class:`~matplotlib.text.Text` instances - for the labels of flows - =============== =================================================== - - See Also - -------- - Sankey.add - """ - self.ax.axis([self.extent[0] - self.margin, - self.extent[1] + self.margin, - self.extent[2] - self.margin, - self.extent[3] + self.margin]) - self.ax.set_aspect('equal', adjustable='datalim') - return self.diagrams diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/scale.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/scale.py deleted file mode 100644 index c1a7a91..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/scale.py +++ /dev/null @@ -1,748 +0,0 @@ -""" -Scales define the distribution of data values on an axis, e.g. a log scaling. -They are defined as subclasses of `ScaleBase`. - -See also `.axes.Axes.set_xscale` and the scales examples in the documentation. - -See :doc:`/gallery/scales/custom_scale` for a full example of defining a custom -scale. - -Matplotlib also supports non-separable transformations that operate on both -`~.axis.Axis` at the same time. They are known as projections, and defined in -`matplotlib.projections`. -""" - -import inspect -import textwrap - -import numpy as np - -import matplotlib as mpl -from matplotlib import _api, _docstring -from matplotlib.ticker import ( - NullFormatter, ScalarFormatter, LogFormatterSciNotation, LogitFormatter, - NullLocator, LogLocator, AutoLocator, AutoMinorLocator, - SymmetricalLogLocator, AsinhLocator, LogitLocator) -from matplotlib.transforms import Transform, IdentityTransform - - -class ScaleBase: - """ - The base class for all scales. - - Scales are separable transformations, working on a single dimension. - - Subclasses should override - - :attr:`name` - The scale's name. - :meth:`get_transform` - A method returning a `.Transform`, which converts data coordinates to - scaled coordinates. This transform should be invertible, so that e.g. - mouse positions can be converted back to data coordinates. - :meth:`set_default_locators_and_formatters` - A method that sets default locators and formatters for an `~.axis.Axis` - that uses this scale. - :meth:`limit_range_for_scale` - An optional method that "fixes" the axis range to acceptable values, - e.g. restricting log-scaled axes to positive values. - """ - - def __init__(self, axis): - r""" - Construct a new scale. - - Notes - ----- - The following note is for scale implementors. - - For back-compatibility reasons, scales take an `~matplotlib.axis.Axis` - object as first argument. However, this argument should not - be used: a single scale object should be usable by multiple - `~matplotlib.axis.Axis`\es at the same time. - """ - - def get_transform(self): - """ - Return the `.Transform` object associated with this scale. - """ - raise NotImplementedError() - - def set_default_locators_and_formatters(self, axis): - """ - Set the locators and formatters of *axis* to instances suitable for - this scale. - """ - raise NotImplementedError() - - def limit_range_for_scale(self, vmin, vmax, minpos): - """ - Return the range *vmin*, *vmax*, restricted to the - domain supported by this scale (if any). - - *minpos* should be the minimum positive value in the data. - This is used by log scales to determine a minimum value. - """ - return vmin, vmax - - -class LinearScale(ScaleBase): - """ - The default linear scale. - """ - - name = 'linear' - - def __init__(self, axis): - # This method is present only to prevent inheritance of the base class' - # constructor docstring, which would otherwise end up interpolated into - # the docstring of Axis.set_scale. - """ - """ - - def set_default_locators_and_formatters(self, axis): - # docstring inherited - axis.set_major_locator(AutoLocator()) - axis.set_major_formatter(ScalarFormatter()) - axis.set_minor_formatter(NullFormatter()) - # update the minor locator for x and y axis based on rcParams - if (axis.axis_name == 'x' and mpl.rcParams['xtick.minor.visible'] or - axis.axis_name == 'y' and mpl.rcParams['ytick.minor.visible']): - axis.set_minor_locator(AutoMinorLocator()) - else: - axis.set_minor_locator(NullLocator()) - - def get_transform(self): - """ - Return the transform for linear scaling, which is just the - `~matplotlib.transforms.IdentityTransform`. - """ - return IdentityTransform() - - -class FuncTransform(Transform): - """ - A simple transform that takes and arbitrary function for the - forward and inverse transform. - """ - - input_dims = output_dims = 1 - - def __init__(self, forward, inverse): - """ - Parameters - ---------- - forward : callable - The forward function for the transform. This function must have - an inverse and, for best behavior, be monotonic. - It must have the signature:: - - def forward(values: array-like) -> array-like - - inverse : callable - The inverse of the forward function. Signature as ``forward``. - """ - super().__init__() - if callable(forward) and callable(inverse): - self._forward = forward - self._inverse = inverse - else: - raise ValueError('arguments to FuncTransform must be functions') - - def transform_non_affine(self, values): - return self._forward(values) - - def inverted(self): - return FuncTransform(self._inverse, self._forward) - - -class FuncScale(ScaleBase): - """ - Provide an arbitrary scale with user-supplied function for the axis. - """ - - name = 'function' - - def __init__(self, axis, functions): - """ - Parameters - ---------- - axis : `~matplotlib.axis.Axis` - The axis for the scale. - functions : (callable, callable) - two-tuple of the forward and inverse functions for the scale. - The forward function must be monotonic. - - Both functions must have the signature:: - - def forward(values: array-like) -> array-like - """ - forward, inverse = functions - transform = FuncTransform(forward, inverse) - self._transform = transform - - def get_transform(self): - """Return the `.FuncTransform` associated with this scale.""" - return self._transform - - def set_default_locators_and_formatters(self, axis): - # docstring inherited - axis.set_major_locator(AutoLocator()) - axis.set_major_formatter(ScalarFormatter()) - axis.set_minor_formatter(NullFormatter()) - # update the minor locator for x and y axis based on rcParams - if (axis.axis_name == 'x' and mpl.rcParams['xtick.minor.visible'] or - axis.axis_name == 'y' and mpl.rcParams['ytick.minor.visible']): - axis.set_minor_locator(AutoMinorLocator()) - else: - axis.set_minor_locator(NullLocator()) - - -class LogTransform(Transform): - input_dims = output_dims = 1 - - def __init__(self, base, nonpositive='clip'): - super().__init__() - if base <= 0 or base == 1: - raise ValueError('The log base cannot be <= 0 or == 1') - self.base = base - self._clip = _api.check_getitem( - {"clip": True, "mask": False}, nonpositive=nonpositive) - - def __str__(self): - return "{}(base={}, nonpositive={!r})".format( - type(self).__name__, self.base, "clip" if self._clip else "mask") - - def transform_non_affine(self, a): - # Ignore invalid values due to nans being passed to the transform. - with np.errstate(divide="ignore", invalid="ignore"): - log = {np.e: np.log, 2: np.log2, 10: np.log10}.get(self.base) - if log: # If possible, do everything in a single call to NumPy. - out = log(a) - else: - out = np.log(a) - out /= np.log(self.base) - if self._clip: - # SVG spec says that conforming viewers must support values up - # to 3.4e38 (C float); however experiments suggest that - # Inkscape (which uses cairo for rendering) runs into cairo's - # 24-bit limit (which is apparently shared by Agg). - # Ghostscript (used for pdf rendering appears to overflow even - # earlier, with the max value around 2 ** 15 for the tests to - # pass. On the other hand, in practice, we want to clip beyond - # np.log10(np.nextafter(0, 1)) ~ -323 - # so 1000 seems safe. - out[a <= 0] = -1000 - return out - - def inverted(self): - return InvertedLogTransform(self.base) - - -class InvertedLogTransform(Transform): - input_dims = output_dims = 1 - - def __init__(self, base): - super().__init__() - self.base = base - - def __str__(self): - return "{}(base={})".format(type(self).__name__, self.base) - - def transform_non_affine(self, a): - return np.power(self.base, a) - - def inverted(self): - return LogTransform(self.base) - - -class LogScale(ScaleBase): - """ - A standard logarithmic scale. Care is taken to only plot positive values. - """ - name = 'log' - - def __init__(self, axis, *, base=10, subs=None, nonpositive="clip"): - """ - Parameters - ---------- - axis : `~matplotlib.axis.Axis` - The axis for the scale. - base : float, default: 10 - The base of the logarithm. - nonpositive : {'clip', 'mask'}, default: 'clip' - Determines the behavior for non-positive values. They can either - be masked as invalid, or clipped to a very small positive number. - subs : sequence of int, default: None - Where to place the subticks between each major tick. For example, - in a log10 scale, ``[2, 3, 4, 5, 6, 7, 8, 9]`` will place 8 - logarithmically spaced minor ticks between each major tick. - """ - self._transform = LogTransform(base, nonpositive) - self.subs = subs - - base = property(lambda self: self._transform.base) - - def set_default_locators_and_formatters(self, axis): - # docstring inherited - axis.set_major_locator(LogLocator(self.base)) - axis.set_major_formatter(LogFormatterSciNotation(self.base)) - axis.set_minor_locator(LogLocator(self.base, self.subs)) - axis.set_minor_formatter( - LogFormatterSciNotation(self.base, - labelOnlyBase=(self.subs is not None))) - - def get_transform(self): - """Return the `.LogTransform` associated with this scale.""" - return self._transform - - def limit_range_for_scale(self, vmin, vmax, minpos): - """Limit the domain to positive values.""" - if not np.isfinite(minpos): - minpos = 1e-300 # Should rarely (if ever) have a visible effect. - - return (minpos if vmin <= 0 else vmin, - minpos if vmax <= 0 else vmax) - - -class FuncScaleLog(LogScale): - """ - Provide an arbitrary scale with user-supplied function for the axis and - then put on a logarithmic axes. - """ - - name = 'functionlog' - - def __init__(self, axis, functions, base=10): - """ - Parameters - ---------- - axis : `~matplotlib.axis.Axis` - The axis for the scale. - functions : (callable, callable) - two-tuple of the forward and inverse functions for the scale. - The forward function must be monotonic. - - Both functions must have the signature:: - - def forward(values: array-like) -> array-like - - base : float, default: 10 - Logarithmic base of the scale. - """ - forward, inverse = functions - self.subs = None - self._transform = FuncTransform(forward, inverse) + LogTransform(base) - - @property - def base(self): - return self._transform._b.base # Base of the LogTransform. - - def get_transform(self): - """Return the `.Transform` associated with this scale.""" - return self._transform - - -class SymmetricalLogTransform(Transform): - input_dims = output_dims = 1 - - def __init__(self, base, linthresh, linscale): - super().__init__() - if base <= 1.0: - raise ValueError("'base' must be larger than 1") - if linthresh <= 0.0: - raise ValueError("'linthresh' must be positive") - if linscale <= 0.0: - raise ValueError("'linscale' must be positive") - self.base = base - self.linthresh = linthresh - self.linscale = linscale - self._linscale_adj = (linscale / (1.0 - self.base ** -1)) - self._log_base = np.log(base) - - def transform_non_affine(self, a): - abs_a = np.abs(a) - with np.errstate(divide="ignore", invalid="ignore"): - out = np.sign(a) * self.linthresh * ( - self._linscale_adj + - np.log(abs_a / self.linthresh) / self._log_base) - inside = abs_a <= self.linthresh - out[inside] = a[inside] * self._linscale_adj - return out - - def inverted(self): - return InvertedSymmetricalLogTransform(self.base, self.linthresh, - self.linscale) - - -class InvertedSymmetricalLogTransform(Transform): - input_dims = output_dims = 1 - - def __init__(self, base, linthresh, linscale): - super().__init__() - symlog = SymmetricalLogTransform(base, linthresh, linscale) - self.base = base - self.linthresh = linthresh - self.invlinthresh = symlog.transform(linthresh) - self.linscale = linscale - self._linscale_adj = (linscale / (1.0 - self.base ** -1)) - - def transform_non_affine(self, a): - abs_a = np.abs(a) - with np.errstate(divide="ignore", invalid="ignore"): - out = np.sign(a) * self.linthresh * ( - np.power(self.base, - abs_a / self.linthresh - self._linscale_adj)) - inside = abs_a <= self.invlinthresh - out[inside] = a[inside] / self._linscale_adj - return out - - def inverted(self): - return SymmetricalLogTransform(self.base, - self.linthresh, self.linscale) - - -class SymmetricalLogScale(ScaleBase): - """ - The symmetrical logarithmic scale is logarithmic in both the - positive and negative directions from the origin. - - Since the values close to zero tend toward infinity, there is a - need to have a range around zero that is linear. The parameter - *linthresh* allows the user to specify the size of this range - (-*linthresh*, *linthresh*). - - Parameters - ---------- - base : float, default: 10 - The base of the logarithm. - - linthresh : float, default: 2 - Defines the range ``(-x, x)``, within which the plot is linear. - This avoids having the plot go to infinity around zero. - - subs : sequence of int - Where to place the subticks between each major tick. - For example, in a log10 scale: ``[2, 3, 4, 5, 6, 7, 8, 9]`` will place - 8 logarithmically spaced minor ticks between each major tick. - - linscale : float, optional - This allows the linear range ``(-linthresh, linthresh)`` to be - stretched relative to the logarithmic range. Its value is the number of - decades to use for each half of the linear range. For example, when - *linscale* == 1.0 (the default), the space used for the positive and - negative halves of the linear range will be equal to one decade in - the logarithmic range. - """ - name = 'symlog' - - def __init__(self, axis, *, base=10, linthresh=2, subs=None, linscale=1): - self._transform = SymmetricalLogTransform(base, linthresh, linscale) - self.subs = subs - - base = property(lambda self: self._transform.base) - linthresh = property(lambda self: self._transform.linthresh) - linscale = property(lambda self: self._transform.linscale) - - def set_default_locators_and_formatters(self, axis): - # docstring inherited - axis.set_major_locator(SymmetricalLogLocator(self.get_transform())) - axis.set_major_formatter(LogFormatterSciNotation(self.base)) - axis.set_minor_locator(SymmetricalLogLocator(self.get_transform(), - self.subs)) - axis.set_minor_formatter(NullFormatter()) - - def get_transform(self): - """Return the `.SymmetricalLogTransform` associated with this scale.""" - return self._transform - - -class AsinhTransform(Transform): - """Inverse hyperbolic-sine transformation used by `.AsinhScale`""" - input_dims = output_dims = 1 - - def __init__(self, linear_width): - super().__init__() - if linear_width <= 0.0: - raise ValueError("Scale parameter 'linear_width' " + - "must be strictly positive") - self.linear_width = linear_width - - def transform_non_affine(self, a): - return self.linear_width * np.arcsinh(a / self.linear_width) - - def inverted(self): - return InvertedAsinhTransform(self.linear_width) - - -class InvertedAsinhTransform(Transform): - """Hyperbolic sine transformation used by `.AsinhScale`""" - input_dims = output_dims = 1 - - def __init__(self, linear_width): - super().__init__() - self.linear_width = linear_width - - def transform_non_affine(self, a): - return self.linear_width * np.sinh(a / self.linear_width) - - def inverted(self): - return AsinhTransform(self.linear_width) - - -class AsinhScale(ScaleBase): - """ - A quasi-logarithmic scale based on the inverse hyperbolic sine (asinh) - - For values close to zero, this is essentially a linear scale, - but for large magnitude values (either positive or negative) - it is asymptotically logarithmic. The transition between these - linear and logarithmic regimes is smooth, and has no discontinuities - in the function gradient in contrast to - the `.SymmetricalLogScale` ("symlog") scale. - - Specifically, the transformation of an axis coordinate :math:`a` is - :math:`a \\rightarrow a_0 \\sinh^{-1} (a / a_0)` where :math:`a_0` - is the effective width of the linear region of the transformation. - In that region, the transformation is - :math:`a \\rightarrow a + \\mathcal{O}(a^3)`. - For large values of :math:`a` the transformation behaves as - :math:`a \\rightarrow a_0 \\, \\mathrm{sgn}(a) \\ln |a| + \\mathcal{O}(1)`. - - .. note:: - - This API is provisional and may be revised in the future - based on early user feedback. - """ - - name = 'asinh' - - auto_tick_multipliers = { - 3: (2, ), - 4: (2, ), - 5: (2, ), - 8: (2, 4), - 10: (2, 5), - 16: (2, 4, 8), - 64: (4, 16), - 1024: (256, 512) - } - - def __init__(self, axis, *, linear_width=1.0, - base=10, subs='auto', **kwargs): - """ - Parameters - ---------- - linear_width : float, default: 1 - The scale parameter (elsewhere referred to as :math:`a_0`) - defining the extent of the quasi-linear region, - and the coordinate values beyond which the transformation - becomes asymptotically logarithmic. - base : int, default: 10 - The number base used for rounding tick locations - on a logarithmic scale. If this is less than one, - then rounding is to the nearest integer multiple - of powers of ten. - subs : sequence of int - Multiples of the number base used for minor ticks. - If set to 'auto', this will use built-in defaults, - e.g. (2, 5) for base=10. - """ - super().__init__(axis) - self._transform = AsinhTransform(linear_width) - self._base = int(base) - if subs == 'auto': - self._subs = self.auto_tick_multipliers.get(self._base) - else: - self._subs = subs - - linear_width = property(lambda self: self._transform.linear_width) - - def get_transform(self): - return self._transform - - def set_default_locators_and_formatters(self, axis): - axis.set(major_locator=AsinhLocator(self.linear_width, - base=self._base), - minor_locator=AsinhLocator(self.linear_width, - base=self._base, - subs=self._subs), - minor_formatter=NullFormatter()) - if self._base > 1: - axis.set_major_formatter(LogFormatterSciNotation(self._base)) - else: - axis.set_major_formatter('{x:.3g}'), - - -class LogitTransform(Transform): - input_dims = output_dims = 1 - - def __init__(self, nonpositive='mask'): - super().__init__() - _api.check_in_list(['mask', 'clip'], nonpositive=nonpositive) - self._nonpositive = nonpositive - self._clip = {"clip": True, "mask": False}[nonpositive] - - def transform_non_affine(self, a): - """logit transform (base 10), masked or clipped""" - with np.errstate(divide="ignore", invalid="ignore"): - out = np.log10(a / (1 - a)) - if self._clip: # See LogTransform for choice of clip value. - out[a <= 0] = -1000 - out[1 <= a] = 1000 - return out - - def inverted(self): - return LogisticTransform(self._nonpositive) - - def __str__(self): - return "{}({!r})".format(type(self).__name__, self._nonpositive) - - -class LogisticTransform(Transform): - input_dims = output_dims = 1 - - def __init__(self, nonpositive='mask'): - super().__init__() - self._nonpositive = nonpositive - - def transform_non_affine(self, a): - """logistic transform (base 10)""" - return 1.0 / (1 + 10**(-a)) - - def inverted(self): - return LogitTransform(self._nonpositive) - - def __str__(self): - return "{}({!r})".format(type(self).__name__, self._nonpositive) - - -class LogitScale(ScaleBase): - """ - Logit scale for data between zero and one, both excluded. - - This scale is similar to a log scale close to zero and to one, and almost - linear around 0.5. It maps the interval ]0, 1[ onto ]-infty, +infty[. - """ - name = 'logit' - - def __init__(self, axis, nonpositive='mask', *, - one_half=r"\frac{1}{2}", use_overline=False): - r""" - Parameters - ---------- - axis : `~matplotlib.axis.Axis` - Currently unused. - nonpositive : {'mask', 'clip'} - Determines the behavior for values beyond the open interval ]0, 1[. - They can either be masked as invalid, or clipped to a number very - close to 0 or 1. - use_overline : bool, default: False - Indicate the usage of survival notation (\overline{x}) in place of - standard notation (1-x) for probability close to one. - one_half : str, default: r"\frac{1}{2}" - The string used for ticks formatter to represent 1/2. - """ - self._transform = LogitTransform(nonpositive) - self._use_overline = use_overline - self._one_half = one_half - - def get_transform(self): - """Return the `.LogitTransform` associated with this scale.""" - return self._transform - - def set_default_locators_and_formatters(self, axis): - # docstring inherited - # ..., 0.01, 0.1, 0.5, 0.9, 0.99, ... - axis.set_major_locator(LogitLocator()) - axis.set_major_formatter( - LogitFormatter( - one_half=self._one_half, - use_overline=self._use_overline - ) - ) - axis.set_minor_locator(LogitLocator(minor=True)) - axis.set_minor_formatter( - LogitFormatter( - minor=True, - one_half=self._one_half, - use_overline=self._use_overline - ) - ) - - def limit_range_for_scale(self, vmin, vmax, minpos): - """ - Limit the domain to values between 0 and 1 (excluded). - """ - if not np.isfinite(minpos): - minpos = 1e-7 # Should rarely (if ever) have a visible effect. - return (minpos if vmin <= 0 else vmin, - 1 - minpos if vmax >= 1 else vmax) - - -_scale_mapping = { - 'linear': LinearScale, - 'log': LogScale, - 'symlog': SymmetricalLogScale, - 'asinh': AsinhScale, - 'logit': LogitScale, - 'function': FuncScale, - 'functionlog': FuncScaleLog, - } - - -def get_scale_names(): - """Return the names of the available scales.""" - return sorted(_scale_mapping) - - -def scale_factory(scale, axis, **kwargs): - """ - Return a scale class by name. - - Parameters - ---------- - scale : {%(names)s} - axis : `~matplotlib.axis.Axis` - """ - scale_cls = _api.check_getitem(_scale_mapping, scale=scale) - return scale_cls(axis, **kwargs) - - -if scale_factory.__doc__: - scale_factory.__doc__ = scale_factory.__doc__ % { - "names": ", ".join(map(repr, get_scale_names()))} - - -def register_scale(scale_class): - """ - Register a new kind of scale. - - Parameters - ---------- - scale_class : subclass of `ScaleBase` - The scale to register. - """ - _scale_mapping[scale_class.name] = scale_class - - -def _get_scale_docs(): - """ - Helper function for generating docstrings related to scales. - """ - docs = [] - for name, scale_class in _scale_mapping.items(): - docstring = inspect.getdoc(scale_class.__init__) or "" - docs.extend([ - f" {name!r}", - "", - textwrap.indent(docstring, " " * 8), - "" - ]) - return "\n".join(docs) - - -_docstring.interpd.update( - scale_type='{%s}' % ', '.join([repr(x) for x in get_scale_names()]), - scale_docs=_get_scale_docs().rstrip(), - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/sphinxext/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/sphinxext/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/sphinxext/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/sphinxext/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index a7990b8..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/sphinxext/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/sphinxext/__pycache__/mathmpl.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/sphinxext/__pycache__/mathmpl.cpython-38.pyc deleted file mode 100644 index c208daa..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/sphinxext/__pycache__/mathmpl.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/sphinxext/__pycache__/plot_directive.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/sphinxext/__pycache__/plot_directive.cpython-38.pyc deleted file mode 100644 index 3b12785..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/sphinxext/__pycache__/plot_directive.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/sphinxext/mathmpl.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/sphinxext/mathmpl.py deleted file mode 100644 index dd30f34..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/sphinxext/mathmpl.py +++ /dev/null @@ -1,232 +0,0 @@ -r""" -A role and directive to display mathtext in Sphinx -================================================== - -.. warning:: - In most cases, you will likely want to use one of `Sphinx's builtin Math - extensions - `__ - instead of this one. - -Mathtext may be included in two ways: - -1. Inline, using the role:: - - This text uses inline math: :mathmpl:`\alpha > \beta`. - - which produces: - - This text uses inline math: :mathmpl:`\alpha > \beta`. - -2. Standalone, using the directive:: - - Here is some standalone math: - - .. mathmpl:: - - \alpha > \beta - - which produces: - - Here is some standalone math: - - .. mathmpl:: - - \alpha > \beta - -Options -------- - -The ``mathmpl`` role and directive both support the following options: - - fontset : str, default: 'cm' - The font set to use when displaying math. See :rc:`mathtext.fontset`. - - fontsize : float - The font size, in points. Defaults to the value from the extension - configuration option defined below. - -Configuration options ---------------------- - -The mathtext extension has the following configuration options: - - mathmpl_fontsize : float, default: 10.0 - Default font size, in points. - - mathmpl_srcset : list of str, default: [] - Additional image sizes to generate when embedding in HTML, to support - `responsive resolution images - `__. - The list should contain additional x-descriptors (``'1.5x'``, ``'2x'``, - etc.) to generate (1x is the default and always included.) - -""" - -import hashlib -from pathlib import Path - -from docutils import nodes -from docutils.parsers.rst import Directive, directives -import sphinx -from sphinx.errors import ConfigError, ExtensionError - -import matplotlib as mpl -from matplotlib import _api, mathtext -from matplotlib.rcsetup import validate_float_or_None - - -# Define LaTeX math node: -class latex_math(nodes.General, nodes.Element): - pass - - -def fontset_choice(arg): - return directives.choice(arg, mathtext.MathTextParser._font_type_mapping) - - -def math_role(role, rawtext, text, lineno, inliner, - options={}, content=[]): - i = rawtext.find('`') - latex = rawtext[i+1:-1] - node = latex_math(rawtext) - node['latex'] = latex - node['fontset'] = options.get('fontset', 'cm') - node['fontsize'] = options.get('fontsize', - setup.app.config.mathmpl_fontsize) - return [node], [] -math_role.options = {'fontset': fontset_choice, - 'fontsize': validate_float_or_None} - - -class MathDirective(Directive): - """ - The ``.. mathmpl::`` directive, as documented in the module's docstring. - """ - has_content = True - required_arguments = 0 - optional_arguments = 0 - final_argument_whitespace = False - option_spec = {'fontset': fontset_choice, - 'fontsize': validate_float_or_None} - - def run(self): - latex = ''.join(self.content) - node = latex_math(self.block_text) - node['latex'] = latex - node['fontset'] = self.options.get('fontset', 'cm') - node['fontsize'] = self.options.get('fontsize', - setup.app.config.mathmpl_fontsize) - return [node] - - -# This uses mathtext to render the expression -def latex2png(latex, filename, fontset='cm', fontsize=10, dpi=100): - with mpl.rc_context({'mathtext.fontset': fontset, 'font.size': fontsize}): - try: - depth = mathtext.math_to_image( - f"${latex}$", filename, dpi=dpi, format="png") - except Exception: - _api.warn_external(f"Could not render math expression {latex}") - depth = 0 - return depth - - -# LaTeX to HTML translation stuff: -def latex2html(node, source): - inline = isinstance(node.parent, nodes.TextElement) - latex = node['latex'] - fontset = node['fontset'] - fontsize = node['fontsize'] - name = 'math-{}'.format( - hashlib.md5(f'{latex}{fontset}{fontsize}'.encode()).hexdigest()[-10:]) - - destdir = Path(setup.app.builder.outdir, '_images', 'mathmpl') - destdir.mkdir(parents=True, exist_ok=True) - - dest = destdir / f'{name}.png' - depth = latex2png(latex, dest, fontset, fontsize=fontsize) - - srcset = [] - for size in setup.app.config.mathmpl_srcset: - filename = f'{name}-{size.replace(".", "_")}.png' - latex2png(latex, destdir / filename, fontset, fontsize=fontsize, - dpi=100 * float(size[:-1])) - srcset.append( - f'{setup.app.builder.imgpath}/mathmpl/{filename} {size}') - if srcset: - srcset = (f'srcset="{setup.app.builder.imgpath}/mathmpl/{name}.png, ' + - ', '.join(srcset) + '" ') - - if inline: - cls = '' - else: - cls = 'class="center" ' - if inline and depth != 0: - style = 'style="position: relative; bottom: -%dpx"' % (depth + 1) - else: - style = '' - - return (f'') - - -def _config_inited(app, config): - # Check for srcset hidpi images - for i, size in enumerate(app.config.mathmpl_srcset): - if size[-1] == 'x': # "2x" = "2.0" - try: - float(size[:-1]) - except ValueError: - raise ConfigError( - f'Invalid value for mathmpl_srcset parameter: {size!r}. ' - 'Must be a list of strings with the multiplicative ' - 'factor followed by an "x". e.g. ["2.0x", "1.5x"]') - else: - raise ConfigError( - f'Invalid value for mathmpl_srcset parameter: {size!r}. ' - 'Must be a list of strings with the multiplicative ' - 'factor followed by an "x". e.g. ["2.0x", "1.5x"]') - - -def setup(app): - setup.app = app - app.add_config_value('mathmpl_fontsize', 10.0, True) - app.add_config_value('mathmpl_srcset', [], True) - try: - app.connect('config-inited', _config_inited) # Sphinx 1.8+ - except ExtensionError: - app.connect('env-updated', lambda app, env: _config_inited(app, None)) - - # Add visit/depart methods to HTML-Translator: - def visit_latex_math_html(self, node): - source = self.document.attributes['source'] - self.body.append(latex2html(node, source)) - - def depart_latex_math_html(self, node): - pass - - # Add visit/depart methods to LaTeX-Translator: - def visit_latex_math_latex(self, node): - inline = isinstance(node.parent, nodes.TextElement) - if inline: - self.body.append('$%s$' % node['latex']) - else: - self.body.extend(['\\begin{equation}', - node['latex'], - '\\end{equation}']) - - def depart_latex_math_latex(self, node): - pass - - app.add_node(latex_math, - html=(visit_latex_math_html, depart_latex_math_html), - latex=(visit_latex_math_latex, depart_latex_math_latex)) - app.add_role('mathmpl', math_role) - app.add_directive('mathmpl', MathDirective) - if sphinx.version_info < (1, 8): - app.add_role('math', math_role) - app.add_directive('math', MathDirective) - - metadata = {'parallel_read_safe': True, 'parallel_write_safe': True} - return metadata diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/sphinxext/plot_directive.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/sphinxext/plot_directive.py deleted file mode 100644 index c942085..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/sphinxext/plot_directive.py +++ /dev/null @@ -1,826 +0,0 @@ -""" -A directive for including a Matplotlib plot in a Sphinx document -================================================================ - -This is a Sphinx extension providing a reStructuredText directive -``.. plot::`` for including a plot in a Sphinx document. - -In HTML output, ``.. plot::`` will include a .png file with a link -to a high-res .png and .pdf. In LaTeX output, it will include a .pdf. - -The plot content may be defined in one of three ways: - -1. **A path to a source file** as the argument to the directive:: - - .. plot:: path/to/plot.py - - When a path to a source file is given, the content of the - directive may optionally contain a caption for the plot:: - - .. plot:: path/to/plot.py - - The plot caption. - - Additionally, one may specify the name of a function to call (with - no arguments) immediately after importing the module:: - - .. plot:: path/to/plot.py plot_function1 - -2. Included as **inline content** to the directive:: - - .. plot:: - - import matplotlib.pyplot as plt - plt.plot([1, 2, 3], [4, 5, 6]) - plt.title("A plotting exammple") - -3. Using **doctest** syntax:: - - .. plot:: - - A plotting example: - >>> import matplotlib.pyplot as plt - >>> plt.plot([1, 2, 3], [4, 5, 6]) - -Options -------- - -The ``.. plot::`` directive supports the following options: - - ``:format:`` : {'python', 'doctest'} - The format of the input. If unset, the format is auto-detected. - - ``:include-source:`` : bool - Whether to display the source code. The default can be changed using - the ``plot_include_source`` variable in :file:`conf.py` (which itself - defaults to False). - - ``:show-source-link:`` : bool - Whether to show a link to the source in HTML. The default can be - changed using the ``plot_html_show_source_link`` variable in - :file:`conf.py` (which itself defaults to True). - - ``:context:`` : bool or str - If provided, the code will be run in the context of all previous plot - directives for which the ``:context:`` option was specified. This only - applies to inline code plot directives, not those run from files. If - the ``:context: reset`` option is specified, the context is reset - for this and future plots, and previous figures are closed prior to - running the code. ``:context: close-figs`` keeps the context but closes - previous figures before running the code. - - ``:nofigs:`` : bool - If specified, the code block will be run, but no figures will be - inserted. This is usually useful with the ``:context:`` option. - - ``:caption:`` : str - If specified, the option's argument will be used as a caption for the - figure. This overwrites the caption given in the content, when the plot - is generated from a file. - -Additionally, this directive supports all the options of the `image directive -`_, -except for ``:target:`` (since plot will add its own target). These include -``:alt:``, ``:height:``, ``:width:``, ``:scale:``, ``:align:`` and ``:class:``. - -Configuration options ---------------------- - -The plot directive has the following configuration options: - - plot_include_source - Default value for the include-source option (default: False). - - plot_html_show_source_link - Whether to show a link to the source in HTML (default: True). - - plot_pre_code - Code that should be executed before each plot. If None (the default), - it will default to a string containing:: - - import numpy as np - from matplotlib import pyplot as plt - - plot_basedir - Base directory, to which ``plot::`` file names are relative to. - If None or empty (the default), file names are relative to the - directory where the file containing the directive is. - - plot_formats - File formats to generate (default: ['png', 'hires.png', 'pdf']). - List of tuples or strings:: - - [(suffix, dpi), suffix, ...] - - that determine the file format and the DPI. For entries whose - DPI was omitted, sensible defaults are chosen. When passing from - the command line through sphinx_build the list should be passed as - suffix:dpi,suffix:dpi, ... - - plot_html_show_formats - Whether to show links to the files in HTML (default: True). - - plot_rcparams - A dictionary containing any non-standard rcParams that should - be applied before each plot (default: {}). - - plot_apply_rcparams - By default, rcParams are applied when ``:context:`` option is not used - in a plot directive. If set, this configuration option overrides this - behavior and applies rcParams before each plot. - - plot_working_directory - By default, the working directory will be changed to the directory of - the example, so the code can get at its data files, if any. Also its - path will be added to `sys.path` so it can import any helper modules - sitting beside it. This configuration option can be used to specify - a central directory (also added to `sys.path`) where data files and - helper modules for all code are located. - - plot_template - Provide a customized template for preparing restructured text. -""" - -import contextlib -import doctest -from io import StringIO -import itertools -import os -from os.path import relpath -from pathlib import Path -import re -import shutil -import sys -import textwrap -import traceback - -from docutils.parsers.rst import directives, Directive -from docutils.parsers.rst.directives.images import Image -import jinja2 # Sphinx dependency. - -import matplotlib -from matplotlib.backend_bases import FigureManagerBase -import matplotlib.pyplot as plt -from matplotlib import _pylab_helpers, cbook - -matplotlib.use("agg") - -__version__ = 2 - - -# ----------------------------------------------------------------------------- -# Registration hook -# ----------------------------------------------------------------------------- - - -def _option_boolean(arg): - if not arg or not arg.strip(): - # no argument given, assume used as a flag - return True - elif arg.strip().lower() in ('no', '0', 'false'): - return False - elif arg.strip().lower() in ('yes', '1', 'true'): - return True - else: - raise ValueError(f'{arg!r} unknown boolean') - - -def _option_context(arg): - if arg in [None, 'reset', 'close-figs']: - return arg - raise ValueError("Argument should be None or 'reset' or 'close-figs'") - - -def _option_format(arg): - return directives.choice(arg, ('python', 'doctest')) - - -def mark_plot_labels(app, document): - """ - To make plots referenceable, we need to move the reference from the - "htmlonly" (or "latexonly") node to the actual figure node itself. - """ - for name, explicit in document.nametypes.items(): - if not explicit: - continue - labelid = document.nameids[name] - if labelid is None: - continue - node = document.ids[labelid] - if node.tagname in ('html_only', 'latex_only'): - for n in node: - if n.tagname == 'figure': - sectname = name - for c in n: - if c.tagname == 'caption': - sectname = c.astext() - break - - node['ids'].remove(labelid) - node['names'].remove(name) - n['ids'].append(labelid) - n['names'].append(name) - document.settings.env.labels[name] = \ - document.settings.env.docname, labelid, sectname - break - - -class PlotDirective(Directive): - """The ``.. plot::`` directive, as documented in the module's docstring.""" - - has_content = True - required_arguments = 0 - optional_arguments = 2 - final_argument_whitespace = False - option_spec = { - 'alt': directives.unchanged, - 'height': directives.length_or_unitless, - 'width': directives.length_or_percentage_or_unitless, - 'scale': directives.nonnegative_int, - 'align': Image.align, - 'class': directives.class_option, - 'include-source': _option_boolean, - 'show-source-link': _option_boolean, - 'format': _option_format, - 'context': _option_context, - 'nofigs': directives.flag, - 'caption': directives.unchanged, - } - - def run(self): - """Run the plot directive.""" - try: - return run(self.arguments, self.content, self.options, - self.state_machine, self.state, self.lineno) - except Exception as e: - raise self.error(str(e)) - - -def _copy_css_file(app, exc): - if exc is None and app.builder.format == 'html': - src = cbook._get_data_path('plot_directive/plot_directive.css') - dst = app.outdir / Path('_static') - dst.mkdir(exist_ok=True) - # Use copyfile because we do not want to copy src's permissions. - shutil.copyfile(src, dst / Path('plot_directive.css')) - - -def setup(app): - setup.app = app - setup.config = app.config - setup.confdir = app.confdir - app.add_directive('plot', PlotDirective) - app.add_config_value('plot_pre_code', None, True) - app.add_config_value('plot_include_source', False, True) - app.add_config_value('plot_html_show_source_link', True, True) - app.add_config_value('plot_formats', ['png', 'hires.png', 'pdf'], True) - app.add_config_value('plot_basedir', None, True) - app.add_config_value('plot_html_show_formats', True, True) - app.add_config_value('plot_rcparams', {}, True) - app.add_config_value('plot_apply_rcparams', False, True) - app.add_config_value('plot_working_directory', None, True) - app.add_config_value('plot_template', None, True) - app.connect('doctree-read', mark_plot_labels) - app.add_css_file('plot_directive.css') - app.connect('build-finished', _copy_css_file) - metadata = {'parallel_read_safe': True, 'parallel_write_safe': True, - 'version': matplotlib.__version__} - return metadata - - -# ----------------------------------------------------------------------------- -# Doctest handling -# ----------------------------------------------------------------------------- - - -def contains_doctest(text): - try: - # check if it's valid Python as-is - compile(text, '', 'exec') - return False - except SyntaxError: - pass - r = re.compile(r'^\s*>>>', re.M) - m = r.search(text) - return bool(m) - - -def _split_code_at_show(text, function_name): - """Split code at plt.show().""" - - is_doctest = contains_doctest(text) - if function_name is None: - parts = [] - part = [] - for line in text.split("\n"): - if ((not is_doctest and line.startswith('plt.show(')) or - (is_doctest and line.strip() == '>>> plt.show()')): - part.append(line) - parts.append("\n".join(part)) - part = [] - else: - part.append(line) - if "\n".join(part).strip(): - parts.append("\n".join(part)) - else: - parts = [text] - return is_doctest, parts - - -# ----------------------------------------------------------------------------- -# Template -# ----------------------------------------------------------------------------- - -TEMPLATE = """ -{{ source_code }} - -.. only:: html - - {% if src_name or (html_show_formats and not multi_image) %} - ( - {%- if src_name -%} - :download:`Source code <{{ build_dir }}/{{ src_name }}>` - {%- endif -%} - {%- if html_show_formats and not multi_image -%} - {%- for img in images -%} - {%- for fmt in img.formats -%} - {%- if src_name or not loop.first -%}, {% endif -%} - :download:`{{ fmt }} <{{ build_dir }}/{{ img.basename }}.{{ fmt }}>` - {%- endfor -%} - {%- endfor -%} - {%- endif -%} - ) - {% endif %} - - {% for img in images %} - .. figure:: {{ build_dir }}/{{ img.basename }}.{{ default_fmt }} - {% for option in options -%} - {{ option }} - {% endfor %} - - {% if html_show_formats and multi_image -%} - ( - {%- for fmt in img.formats -%} - {%- if not loop.first -%}, {% endif -%} - :download:`{{ fmt }} <{{ build_dir }}/{{ img.basename }}.{{ fmt }}>` - {%- endfor -%} - ) - {%- endif -%} - - {{ caption }} {# appropriate leading whitespace added beforehand #} - {% endfor %} - -.. only:: not html - - {% for img in images %} - .. figure:: {{ build_dir }}/{{ img.basename }}.* - {% for option in options -%} - {{ option }} - {% endfor -%} - - {{ caption }} {# appropriate leading whitespace added beforehand #} - {% endfor %} - -""" - -exception_template = """ -.. only:: html - - [`source code <%(linkdir)s/%(basename)s.py>`__] - -Exception occurred rendering plot. - -""" - -# the context of the plot for all directives specified with the -# :context: option -plot_context = dict() - - -class ImageFile: - def __init__(self, basename, dirname): - self.basename = basename - self.dirname = dirname - self.formats = [] - - def filename(self, format): - return os.path.join(self.dirname, "%s.%s" % (self.basename, format)) - - def filenames(self): - return [self.filename(fmt) for fmt in self.formats] - - -def out_of_date(original, derived, includes=None): - """ - Return whether *derived* is out-of-date relative to *original* or any of - the RST files included in it using the RST include directive (*includes*). - *derived* and *original* are full paths, and *includes* is optionally a - list of full paths which may have been included in the *original*. - """ - if not os.path.exists(derived): - return True - - if includes is None: - includes = [] - files_to_check = [original, *includes] - - def out_of_date_one(original, derived_mtime): - return (os.path.exists(original) and - derived_mtime < os.stat(original).st_mtime) - - derived_mtime = os.stat(derived).st_mtime - return any(out_of_date_one(f, derived_mtime) for f in files_to_check) - - -class PlotError(RuntimeError): - pass - - -def _run_code(code, code_path, ns=None, function_name=None): - """ - Import a Python module from a path, and run the function given by - name, if function_name is not None. - """ - - # Change the working directory to the directory of the example, so - # it can get at its data files, if any. Add its path to sys.path - # so it can import any helper modules sitting beside it. - pwd = os.getcwd() - if setup.config.plot_working_directory is not None: - try: - os.chdir(setup.config.plot_working_directory) - except OSError as err: - raise OSError(f'{err}\n`plot_working_directory` option in ' - f'Sphinx configuration file must be a valid ' - f'directory path') from err - except TypeError as err: - raise TypeError(f'{err}\n`plot_working_directory` option in ' - f'Sphinx configuration file must be a string or ' - f'None') from err - elif code_path is not None: - dirname = os.path.abspath(os.path.dirname(code_path)) - os.chdir(dirname) - - with cbook._setattr_cm( - sys, argv=[code_path], path=[os.getcwd(), *sys.path]), \ - contextlib.redirect_stdout(StringIO()): - try: - if ns is None: - ns = {} - if not ns: - if setup.config.plot_pre_code is None: - exec('import numpy as np\n' - 'from matplotlib import pyplot as plt\n', ns) - else: - exec(str(setup.config.plot_pre_code), ns) - if "__main__" in code: - ns['__name__'] = '__main__' - - # Patch out non-interactive show() to avoid triggering a warning. - with cbook._setattr_cm(FigureManagerBase, show=lambda self: None): - exec(code, ns) - if function_name is not None: - exec(function_name + "()", ns) - - except (Exception, SystemExit) as err: - raise PlotError(traceback.format_exc()) from err - finally: - os.chdir(pwd) - return ns - - -def clear_state(plot_rcparams, close=True): - if close: - plt.close('all') - matplotlib.rc_file_defaults() - matplotlib.rcParams.update(plot_rcparams) - - -def get_plot_formats(config): - default_dpi = {'png': 80, 'hires.png': 200, 'pdf': 200} - formats = [] - plot_formats = config.plot_formats - for fmt in plot_formats: - if isinstance(fmt, str): - if ':' in fmt: - suffix, dpi = fmt.split(':') - formats.append((str(suffix), int(dpi))) - else: - formats.append((fmt, default_dpi.get(fmt, 80))) - elif isinstance(fmt, (tuple, list)) and len(fmt) == 2: - formats.append((str(fmt[0]), int(fmt[1]))) - else: - raise PlotError('invalid image format "%r" in plot_formats' % fmt) - return formats - - -def render_figures(code, code_path, output_dir, output_base, context, - function_name, config, context_reset=False, - close_figs=False, - code_includes=None): - """ - Run a pyplot script and save the images in *output_dir*. - - Save the images under *output_dir* with file names derived from - *output_base* - """ - if function_name is not None: - output_base = f'{output_base}_{function_name}' - formats = get_plot_formats(config) - - # Try to determine if all images already exist - - is_doctest, code_pieces = _split_code_at_show(code, function_name) - - # Look for single-figure output files first - img = ImageFile(output_base, output_dir) - for format, dpi in formats: - if context or out_of_date(code_path, img.filename(format), - includes=code_includes): - all_exists = False - break - img.formats.append(format) - else: - all_exists = True - - if all_exists: - return [(code, [img])] - - # Then look for multi-figure output files - results = [] - for i, code_piece in enumerate(code_pieces): - images = [] - for j in itertools.count(): - if len(code_pieces) > 1: - img = ImageFile('%s_%02d_%02d' % (output_base, i, j), - output_dir) - else: - img = ImageFile('%s_%02d' % (output_base, j), output_dir) - for fmt, dpi in formats: - if context or out_of_date(code_path, img.filename(fmt), - includes=code_includes): - all_exists = False - break - img.formats.append(fmt) - - # assume that if we have one, we have them all - if not all_exists: - all_exists = (j > 0) - break - images.append(img) - if not all_exists: - break - results.append((code_piece, images)) - else: - all_exists = True - - if all_exists: - return results - - # We didn't find the files, so build them - - results = [] - ns = plot_context if context else {} - - if context_reset: - clear_state(config.plot_rcparams) - plot_context.clear() - - close_figs = not context or close_figs - - for i, code_piece in enumerate(code_pieces): - - if not context or config.plot_apply_rcparams: - clear_state(config.plot_rcparams, close_figs) - elif close_figs: - plt.close('all') - - _run_code(doctest.script_from_examples(code_piece) if is_doctest - else code_piece, - code_path, ns, function_name) - - images = [] - fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() - for j, figman in enumerate(fig_managers): - if len(fig_managers) == 1 and len(code_pieces) == 1: - img = ImageFile(output_base, output_dir) - elif len(code_pieces) == 1: - img = ImageFile("%s_%02d" % (output_base, j), output_dir) - else: - img = ImageFile("%s_%02d_%02d" % (output_base, i, j), - output_dir) - images.append(img) - for fmt, dpi in formats: - try: - figman.canvas.figure.savefig(img.filename(fmt), dpi=dpi) - except Exception as err: - raise PlotError(traceback.format_exc()) from err - img.formats.append(fmt) - - results.append((code_piece, images)) - - if not context or config.plot_apply_rcparams: - clear_state(config.plot_rcparams, close=not context) - - return results - - -def run(arguments, content, options, state_machine, state, lineno): - document = state_machine.document - config = document.settings.env.config - nofigs = 'nofigs' in options - - formats = get_plot_formats(config) - default_fmt = formats[0][0] - - options.setdefault('include-source', config.plot_include_source) - options.setdefault('show-source-link', config.plot_html_show_source_link) - if 'class' in options: - # classes are parsed into a list of string, and output by simply - # printing the list, abusing the fact that RST guarantees to strip - # non-conforming characters - options['class'] = ['plot-directive'] + options['class'] - else: - options.setdefault('class', ['plot-directive']) - keep_context = 'context' in options - context_opt = None if not keep_context else options['context'] - - rst_file = document.attributes['source'] - rst_dir = os.path.dirname(rst_file) - - if len(arguments): - if not config.plot_basedir: - source_file_name = os.path.join(setup.app.builder.srcdir, - directives.uri(arguments[0])) - else: - source_file_name = os.path.join(setup.confdir, config.plot_basedir, - directives.uri(arguments[0])) - - # If there is content, it will be passed as a caption. - caption = '\n'.join(content) - - # Enforce unambiguous use of captions. - if "caption" in options: - if caption: - raise ValueError( - 'Caption specified in both content and options.' - ' Please remove ambiguity.' - ) - # Use caption option - caption = options["caption"] - - # If the optional function name is provided, use it - if len(arguments) == 2: - function_name = arguments[1] - else: - function_name = None - - code = Path(source_file_name).read_text(encoding='utf-8') - output_base = os.path.basename(source_file_name) - else: - source_file_name = rst_file - code = textwrap.dedent("\n".join(map(str, content))) - counter = document.attributes.get('_plot_counter', 0) + 1 - document.attributes['_plot_counter'] = counter - base, ext = os.path.splitext(os.path.basename(source_file_name)) - output_base = '%s-%d.py' % (base, counter) - function_name = None - caption = options.get('caption', '') - - base, source_ext = os.path.splitext(output_base) - if source_ext in ('.py', '.rst', '.txt'): - output_base = base - else: - source_ext = '' - - # ensure that LaTeX includegraphics doesn't choke in foo.bar.pdf filenames - output_base = output_base.replace('.', '-') - - # is it in doctest format? - is_doctest = contains_doctest(code) - if 'format' in options: - if options['format'] == 'python': - is_doctest = False - else: - is_doctest = True - - # determine output directory name fragment - source_rel_name = relpath(source_file_name, setup.confdir) - source_rel_dir = os.path.dirname(source_rel_name).lstrip(os.path.sep) - - # build_dir: where to place output files (temporarily) - build_dir = os.path.join(os.path.dirname(setup.app.doctreedir), - 'plot_directive', - source_rel_dir) - # get rid of .. in paths, also changes pathsep - # see note in Python docs for warning about symbolic links on Windows. - # need to compare source and dest paths at end - build_dir = os.path.normpath(build_dir) - os.makedirs(build_dir, exist_ok=True) - - # how to link to files from the RST file - try: - build_dir_link = relpath(build_dir, rst_dir).replace(os.path.sep, '/') - except ValueError: - # on Windows, relpath raises ValueError when path and start are on - # different mounts/drives - build_dir_link = build_dir - - # get list of included rst files so that the output is updated when any - # plots in the included files change. These attributes are modified by the - # include directive (see the docutils.parsers.rst.directives.misc module). - try: - source_file_includes = [os.path.join(os.getcwd(), t[0]) - for t in state.document.include_log] - except AttributeError: - # the document.include_log attribute only exists in docutils >=0.17, - # before that we need to inspect the state machine - possible_sources = {os.path.join(setup.confdir, t[0]) - for t in state_machine.input_lines.items} - source_file_includes = [f for f in possible_sources - if os.path.isfile(f)] - # remove the source file itself from the includes - try: - source_file_includes.remove(source_file_name) - except ValueError: - pass - - # save script (if necessary) - if options['show-source-link']: - Path(build_dir, output_base + source_ext).write_text( - doctest.script_from_examples(code) - if source_file_name == rst_file and is_doctest - else code, - encoding='utf-8') - - # make figures - try: - results = render_figures(code=code, - code_path=source_file_name, - output_dir=build_dir, - output_base=output_base, - context=keep_context, - function_name=function_name, - config=config, - context_reset=context_opt == 'reset', - close_figs=context_opt == 'close-figs', - code_includes=source_file_includes) - errors = [] - except PlotError as err: - reporter = state.memo.reporter - sm = reporter.system_message( - 2, "Exception occurred in plotting {}\n from {}:\n{}".format( - output_base, source_file_name, err), - line=lineno) - results = [(code, [])] - errors = [sm] - - # Properly indent the caption - caption = '\n' + '\n'.join(' ' + line.strip() - for line in caption.split('\n')) - - # generate output restructuredtext - total_lines = [] - for j, (code_piece, images) in enumerate(results): - if options['include-source']: - if is_doctest: - lines = ['', *code_piece.splitlines()] - else: - lines = ['.. code-block:: python', '', - *textwrap.indent(code_piece, ' ').splitlines()] - source_code = "\n".join(lines) - else: - source_code = "" - - if nofigs: - images = [] - - opts = [ - ':%s: %s' % (key, val) for key, val in options.items() - if key in ('alt', 'height', 'width', 'scale', 'align', 'class')] - - # Not-None src_name signals the need for a source download in the - # generated html - if j == 0 and options['show-source-link']: - src_name = output_base + source_ext - else: - src_name = None - - result = jinja2.Template(config.plot_template or TEMPLATE).render( - default_fmt=default_fmt, - build_dir=build_dir_link, - src_name=src_name, - multi_image=len(images) > 1, - options=opts, - images=images, - source_code=source_code, - html_show_formats=config.plot_html_show_formats and len(images), - caption=caption) - - total_lines.extend(result.split("\n")) - total_lines.extend("\n") - - if total_lines: - state_machine.insert_input(total_lines, source=source_file_name) - - return errors diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/spines.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/spines.py deleted file mode 100644 index 674ae3e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/spines.py +++ /dev/null @@ -1,588 +0,0 @@ -from collections.abc import MutableMapping -import functools - -import numpy as np - -import matplotlib as mpl -from matplotlib import _api, _docstring -from matplotlib.artist import allow_rasterization -import matplotlib.transforms as mtransforms -import matplotlib.patches as mpatches -import matplotlib.path as mpath - - -class Spine(mpatches.Patch): - """ - An axis spine -- the line noting the data area boundaries. - - Spines are the lines connecting the axis tick marks and noting the - boundaries of the data area. They can be placed at arbitrary - positions. See `~.Spine.set_position` for more information. - - The default position is ``('outward', 0)``. - - Spines are subclasses of `.Patch`, and inherit much of their behavior. - - Spines draw a line, a circle, or an arc depending on if - `~.Spine.set_patch_line`, `~.Spine.set_patch_circle`, or - `~.Spine.set_patch_arc` has been called. Line-like is the default. - - For examples see :ref:`spines_examples`. - """ - def __str__(self): - return "Spine" - - @_docstring.dedent_interpd - def __init__(self, axes, spine_type, path, **kwargs): - """ - Parameters - ---------- - axes : `~matplotlib.axes.Axes` - The `~.axes.Axes` instance containing the spine. - spine_type : str - The spine type. - path : `~matplotlib.path.Path` - The `.Path` instance used to draw the spine. - - Other Parameters - ---------------- - **kwargs - Valid keyword arguments are: - - %(Patch:kwdoc)s - """ - super().__init__(**kwargs) - self.axes = axes - self.set_figure(self.axes.figure) - self.spine_type = spine_type - self.set_facecolor('none') - self.set_edgecolor(mpl.rcParams['axes.edgecolor']) - self.set_linewidth(mpl.rcParams['axes.linewidth']) - self.set_capstyle('projecting') - self.axis = None - - self.set_zorder(2.5) - self.set_transform(self.axes.transData) # default transform - - self._bounds = None # default bounds - - # Defer initial position determination. (Not much support for - # non-rectangular axes is currently implemented, and this lets - # them pass through the spines machinery without errors.) - self._position = None - _api.check_isinstance(mpath.Path, path=path) - self._path = path - - # To support drawing both linear and circular spines, this - # class implements Patch behavior three ways. If - # self._patch_type == 'line', behave like a mpatches.PathPatch - # instance. If self._patch_type == 'circle', behave like a - # mpatches.Ellipse instance. If self._patch_type == 'arc', behave like - # a mpatches.Arc instance. - self._patch_type = 'line' - - # Behavior copied from mpatches.Ellipse: - # Note: This cannot be calculated until this is added to an Axes - self._patch_transform = mtransforms.IdentityTransform() - - def set_patch_arc(self, center, radius, theta1, theta2): - """Set the spine to be arc-like.""" - self._patch_type = 'arc' - self._center = center - self._width = radius * 2 - self._height = radius * 2 - self._theta1 = theta1 - self._theta2 = theta2 - self._path = mpath.Path.arc(theta1, theta2) - # arc drawn on axes transform - self.set_transform(self.axes.transAxes) - self.stale = True - - def set_patch_circle(self, center, radius): - """Set the spine to be circular.""" - self._patch_type = 'circle' - self._center = center - self._width = radius * 2 - self._height = radius * 2 - # circle drawn on axes transform - self.set_transform(self.axes.transAxes) - self.stale = True - - def set_patch_line(self): - """Set the spine to be linear.""" - self._patch_type = 'line' - self.stale = True - - # Behavior copied from mpatches.Ellipse: - def _recompute_transform(self): - """ - Notes - ----- - This cannot be called until after this has been added to an Axes, - otherwise unit conversion will fail. This makes it very important to - call the accessor method and not directly access the transformation - member variable. - """ - assert self._patch_type in ('arc', 'circle') - center = (self.convert_xunits(self._center[0]), - self.convert_yunits(self._center[1])) - width = self.convert_xunits(self._width) - height = self.convert_yunits(self._height) - self._patch_transform = mtransforms.Affine2D() \ - .scale(width * 0.5, height * 0.5) \ - .translate(*center) - - def get_patch_transform(self): - if self._patch_type in ('arc', 'circle'): - self._recompute_transform() - return self._patch_transform - else: - return super().get_patch_transform() - - def get_window_extent(self, renderer=None): - """ - Return the window extent of the spines in display space, including - padding for ticks (but not their labels) - - See Also - -------- - matplotlib.axes.Axes.get_tightbbox - matplotlib.axes.Axes.get_window_extent - """ - # make sure the location is updated so that transforms etc are correct: - self._adjust_location() - bb = super().get_window_extent(renderer=renderer) - if self.axis is None or not self.axis.get_visible(): - return bb - bboxes = [bb] - drawn_ticks = self.axis._update_ticks() - - major_tick = next(iter({*drawn_ticks} & {*self.axis.majorTicks}), None) - minor_tick = next(iter({*drawn_ticks} & {*self.axis.minorTicks}), None) - for tick in [major_tick, minor_tick]: - if tick is None: - continue - bb0 = bb.frozen() - tickl = tick._size - tickdir = tick._tickdir - if tickdir == 'out': - padout = 1 - padin = 0 - elif tickdir == 'in': - padout = 0 - padin = 1 - else: - padout = 0.5 - padin = 0.5 - padout = padout * tickl / 72 * self.figure.dpi - padin = padin * tickl / 72 * self.figure.dpi - - if tick.tick1line.get_visible(): - if self.spine_type == 'left': - bb0.x0 = bb0.x0 - padout - bb0.x1 = bb0.x1 + padin - elif self.spine_type == 'bottom': - bb0.y0 = bb0.y0 - padout - bb0.y1 = bb0.y1 + padin - - if tick.tick2line.get_visible(): - if self.spine_type == 'right': - bb0.x1 = bb0.x1 + padout - bb0.x0 = bb0.x0 - padin - elif self.spine_type == 'top': - bb0.y1 = bb0.y1 + padout - bb0.y0 = bb0.y0 - padout - bboxes.append(bb0) - - return mtransforms.Bbox.union(bboxes) - - def get_path(self): - return self._path - - def _ensure_position_is_set(self): - if self._position is None: - # default position - self._position = ('outward', 0.0) # in points - self.set_position(self._position) - - def register_axis(self, axis): - """ - Register an axis. - - An axis should be registered with its corresponding spine from - the Axes instance. This allows the spine to clear any axis - properties when needed. - """ - self.axis = axis - if self.axis is not None: - self.axis.clear() - self.stale = True - - def clear(self): - """Clear the current spine.""" - self._position = None # clear position - if self.axis is not None: - self.axis.clear() - - def _adjust_location(self): - """Automatically set spine bounds to the view interval.""" - - if self.spine_type == 'circle': - return - - if self._bounds is not None: - low, high = self._bounds - elif self.spine_type in ('left', 'right'): - low, high = self.axes.viewLim.intervaly - elif self.spine_type in ('top', 'bottom'): - low, high = self.axes.viewLim.intervalx - else: - raise ValueError(f'unknown spine spine_type: {self.spine_type}') - - if self._patch_type == 'arc': - if self.spine_type in ('bottom', 'top'): - try: - direction = self.axes.get_theta_direction() - except AttributeError: - direction = 1 - try: - offset = self.axes.get_theta_offset() - except AttributeError: - offset = 0 - low = low * direction + offset - high = high * direction + offset - if low > high: - low, high = high, low - - self._path = mpath.Path.arc(np.rad2deg(low), np.rad2deg(high)) - - if self.spine_type == 'bottom': - rmin, rmax = self.axes.viewLim.intervaly - try: - rorigin = self.axes.get_rorigin() - except AttributeError: - rorigin = rmin - scaled_diameter = (rmin - rorigin) / (rmax - rorigin) - self._height = scaled_diameter - self._width = scaled_diameter - - else: - raise ValueError('unable to set bounds for spine "%s"' % - self.spine_type) - else: - v1 = self._path.vertices - assert v1.shape == (2, 2), 'unexpected vertices shape' - if self.spine_type in ['left', 'right']: - v1[0, 1] = low - v1[1, 1] = high - elif self.spine_type in ['bottom', 'top']: - v1[0, 0] = low - v1[1, 0] = high - else: - raise ValueError('unable to set bounds for spine "%s"' % - self.spine_type) - - @allow_rasterization - def draw(self, renderer): - self._adjust_location() - ret = super().draw(renderer) - self.stale = False - return ret - - def set_position(self, position): - """ - Set the position of the spine. - - Spine position is specified by a 2 tuple of (position type, - amount). The position types are: - - * 'outward': place the spine out from the data area by the specified - number of points. (Negative values place the spine inwards.) - * 'axes': place the spine at the specified Axes coordinate (0 to 1). - * 'data': place the spine at the specified data coordinate. - - Additionally, shorthand notations define a special positions: - - * 'center' -> ``('axes', 0.5)`` - * 'zero' -> ``('data', 0.0)`` - - Examples - -------- - :doc:`/gallery/spines/spine_placement_demo` - """ - if position in ('center', 'zero'): # special positions - pass - else: - if len(position) != 2: - raise ValueError("position should be 'center' or 2-tuple") - if position[0] not in ['outward', 'axes', 'data']: - raise ValueError("position[0] should be one of 'outward', " - "'axes', or 'data' ") - self._position = position - self.set_transform(self.get_spine_transform()) - if self.axis is not None: - self.axis.reset_ticks() - self.stale = True - - def get_position(self): - """Return the spine position.""" - self._ensure_position_is_set() - return self._position - - def get_spine_transform(self): - """Return the spine transform.""" - self._ensure_position_is_set() - - position = self._position - if isinstance(position, str): - if position == 'center': - position = ('axes', 0.5) - elif position == 'zero': - position = ('data', 0) - assert len(position) == 2, 'position should be 2-tuple' - position_type, amount = position - _api.check_in_list(['axes', 'outward', 'data'], - position_type=position_type) - if self.spine_type in ['left', 'right']: - base_transform = self.axes.get_yaxis_transform(which='grid') - elif self.spine_type in ['top', 'bottom']: - base_transform = self.axes.get_xaxis_transform(which='grid') - else: - raise ValueError(f'unknown spine spine_type: {self.spine_type!r}') - - if position_type == 'outward': - if amount == 0: # short circuit commonest case - return base_transform - else: - offset_vec = {'left': (-1, 0), 'right': (1, 0), - 'bottom': (0, -1), 'top': (0, 1), - }[self.spine_type] - # calculate x and y offset in dots - offset_dots = amount * np.array(offset_vec) / 72 - return (base_transform - + mtransforms.ScaledTranslation( - *offset_dots, self.figure.dpi_scale_trans)) - elif position_type == 'axes': - if self.spine_type in ['left', 'right']: - # keep y unchanged, fix x at amount - return (mtransforms.Affine2D.from_values(0, 0, 0, 1, amount, 0) - + base_transform) - elif self.spine_type in ['bottom', 'top']: - # keep x unchanged, fix y at amount - return (mtransforms.Affine2D.from_values(1, 0, 0, 0, 0, amount) - + base_transform) - elif position_type == 'data': - if self.spine_type in ('right', 'top'): - # The right and top spines have a default position of 1 in - # axes coordinates. When specifying the position in data - # coordinates, we need to calculate the position relative to 0. - amount -= 1 - if self.spine_type in ('left', 'right'): - return mtransforms.blended_transform_factory( - mtransforms.Affine2D().translate(amount, 0) - + self.axes.transData, - self.axes.transData) - elif self.spine_type in ('bottom', 'top'): - return mtransforms.blended_transform_factory( - self.axes.transData, - mtransforms.Affine2D().translate(0, amount) - + self.axes.transData) - - def set_bounds(self, low=None, high=None): - """ - Set the spine bounds. - - Parameters - ---------- - low : float or None, optional - The lower spine bound. Passing *None* leaves the limit unchanged. - - The bounds may also be passed as the tuple (*low*, *high*) as the - first positional argument. - - .. ACCEPTS: (low: float, high: float) - - high : float or None, optional - The higher spine bound. Passing *None* leaves the limit unchanged. - """ - if self.spine_type == 'circle': - raise ValueError( - 'set_bounds() method incompatible with circular spines') - if high is None and np.iterable(low): - low, high = low - old_low, old_high = self.get_bounds() or (None, None) - if low is None: - low = old_low - if high is None: - high = old_high - self._bounds = (low, high) - self.stale = True - - def get_bounds(self): - """Get the bounds of the spine.""" - return self._bounds - - @classmethod - def linear_spine(cls, axes, spine_type, **kwargs): - """Create and return a linear `Spine`.""" - # all values of 0.999 get replaced upon call to set_bounds() - if spine_type == 'left': - path = mpath.Path([(0.0, 0.999), (0.0, 0.999)]) - elif spine_type == 'right': - path = mpath.Path([(1.0, 0.999), (1.0, 0.999)]) - elif spine_type == 'bottom': - path = mpath.Path([(0.999, 0.0), (0.999, 0.0)]) - elif spine_type == 'top': - path = mpath.Path([(0.999, 1.0), (0.999, 1.0)]) - else: - raise ValueError('unable to make path for spine "%s"' % spine_type) - result = cls(axes, spine_type, path, **kwargs) - result.set_visible(mpl.rcParams['axes.spines.{0}'.format(spine_type)]) - - return result - - @classmethod - def arc_spine(cls, axes, spine_type, center, radius, theta1, theta2, - **kwargs): - """Create and return an arc `Spine`.""" - path = mpath.Path.arc(theta1, theta2) - result = cls(axes, spine_type, path, **kwargs) - result.set_patch_arc(center, radius, theta1, theta2) - return result - - @classmethod - def circular_spine(cls, axes, center, radius, **kwargs): - """Create and return a circular `Spine`.""" - path = mpath.Path.unit_circle() - spine_type = 'circle' - result = cls(axes, spine_type, path, **kwargs) - result.set_patch_circle(center, radius) - return result - - def set_color(self, c): - """ - Set the edgecolor. - - Parameters - ---------- - c : color - - Notes - ----- - This method does not modify the facecolor (which defaults to "none"), - unlike the `.Patch.set_color` method defined in the parent class. Use - `.Patch.set_facecolor` to set the facecolor. - """ - self.set_edgecolor(c) - self.stale = True - - -class SpinesProxy: - """ - A proxy to broadcast ``set_*`` method calls to all contained `.Spines`. - - The proxy cannot be used for any other operations on its members. - - The supported methods are determined dynamically based on the contained - spines. If not all spines support a given method, it's executed only on - the subset of spines that support it. - """ - def __init__(self, spine_dict): - self._spine_dict = spine_dict - - def __getattr__(self, name): - broadcast_targets = [spine for spine in self._spine_dict.values() - if hasattr(spine, name)] - if not name.startswith('set_') or not broadcast_targets: - raise AttributeError( - f"'SpinesProxy' object has no attribute '{name}'") - - def x(_targets, _funcname, *args, **kwargs): - for spine in _targets: - getattr(spine, _funcname)(*args, **kwargs) - x = functools.partial(x, broadcast_targets, name) - x.__doc__ = broadcast_targets[0].__doc__ - return x - - def __dir__(self): - names = [] - for spine in self._spine_dict.values(): - names.extend(name - for name in dir(spine) if name.startswith('set_')) - return list(sorted(set(names))) - - -class Spines(MutableMapping): - r""" - The container of all `.Spine`\s in an Axes. - - The interface is dict-like mapping names (e.g. 'left') to `.Spine` objects. - Additionally, it implements some pandas.Series-like features like accessing - elements by attribute:: - - spines['top'].set_visible(False) - spines.top.set_visible(False) - - Multiple spines can be addressed simultaneously by passing a list:: - - spines[['top', 'right']].set_visible(False) - - Use an open slice to address all spines:: - - spines[:].set_visible(False) - - The latter two indexing methods will return a `SpinesProxy` that broadcasts - all ``set_*`` calls to its members, but cannot be used for any other - operation. - """ - def __init__(self, **kwargs): - self._dict = kwargs - - @classmethod - def from_dict(cls, d): - return cls(**d) - - def __getstate__(self): - return self._dict - - def __setstate__(self, state): - self.__init__(**state) - - def __getattr__(self, name): - try: - return self._dict[name] - except KeyError: - raise AttributeError( - f"'Spines' object does not contain a '{name}' spine") - - def __getitem__(self, key): - if isinstance(key, list): - unknown_keys = [k for k in key if k not in self._dict] - if unknown_keys: - raise KeyError(', '.join(unknown_keys)) - return SpinesProxy({k: v for k, v in self._dict.items() - if k in key}) - if isinstance(key, tuple): - raise ValueError('Multiple spines must be passed as a single list') - if isinstance(key, slice): - if key.start is None and key.stop is None and key.step is None: - return SpinesProxy(self._dict) - else: - raise ValueError( - 'Spines does not support slicing except for the fully ' - 'open slice [:] to access all spines.') - return self._dict[key] - - def __setitem__(self, key, value): - # TODO: Do we want to deprecate adding spines? - self._dict[key] = value - - def __delitem__(self, key): - # TODO: Do we want to deprecate deleting spines? - del self._dict[key] - - def __iter__(self): - return iter(self._dict) - - def __len__(self): - return len(self._dict) diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/stackplot.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/stackplot.py deleted file mode 100644 index c97a21e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/stackplot.py +++ /dev/null @@ -1,127 +0,0 @@ -""" -Stacked area plot for 1D arrays inspired by Douglas Y'barbo's stackoverflow -answer: -https://stackoverflow.com/q/2225995/ - -(https://stackoverflow.com/users/66549/doug) -""" - -import itertools - -import numpy as np - -from matplotlib import _api - -__all__ = ['stackplot'] - - -def stackplot(axes, x, *args, - labels=(), colors=None, baseline='zero', - **kwargs): - """ - Draw a stacked area plot. - - Parameters - ---------- - x : (N,) array-like - - y : (M, N) array-like - The data is assumed to be unstacked. Each of the following - calls is legal:: - - stackplot(x, y) # where y has shape (M, N) - stackplot(x, y1, y2, y3) # where y1, y2, y3, y4 have length N - - baseline : {'zero', 'sym', 'wiggle', 'weighted_wiggle'} - Method used to calculate the baseline: - - - ``'zero'``: Constant zero baseline, i.e. a simple stacked plot. - - ``'sym'``: Symmetric around zero and is sometimes called - 'ThemeRiver'. - - ``'wiggle'``: Minimizes the sum of the squared slopes. - - ``'weighted_wiggle'``: Does the same but weights to account for - size of each layer. It is also called 'Streamgraph'-layout. More - details can be found at http://leebyron.com/streamgraph/. - - labels : list of str, optional - A sequence of labels to assign to each data series. If unspecified, - then no labels will be applied to artists. - - colors : list of color, optional - A sequence of colors to be cycled through and used to color the stacked - areas. The sequence need not be exactly the same length as the number - of provided *y*, in which case the colors will repeat from the - beginning. - - If not specified, the colors from the Axes property cycle will be used. - - data : indexable object, optional - DATA_PARAMETER_PLACEHOLDER - - **kwargs - All other keyword arguments are passed to `.Axes.fill_between`. - - Returns - ------- - list of `.PolyCollection` - A list of `.PolyCollection` instances, one for each element in the - stacked area plot. - """ - - y = np.row_stack(args) - - labels = iter(labels) - if colors is not None: - colors = itertools.cycle(colors) - else: - colors = (axes._get_lines.get_next_color() for _ in y) - - # Assume data passed has not been 'stacked', so stack it here. - # We'll need a float buffer for the upcoming calculations. - stack = np.cumsum(y, axis=0, dtype=np.promote_types(y.dtype, np.float32)) - - _api.check_in_list(['zero', 'sym', 'wiggle', 'weighted_wiggle'], - baseline=baseline) - if baseline == 'zero': - first_line = 0. - - elif baseline == 'sym': - first_line = -np.sum(y, 0) * 0.5 - stack += first_line[None, :] - - elif baseline == 'wiggle': - m = y.shape[0] - first_line = (y * (m - 0.5 - np.arange(m)[:, None])).sum(0) - first_line /= -m - stack += first_line - - elif baseline == 'weighted_wiggle': - total = np.sum(y, 0) - # multiply by 1/total (or zero) to avoid infinities in the division: - inv_total = np.zeros_like(total) - mask = total > 0 - inv_total[mask] = 1.0 / total[mask] - increase = np.hstack((y[:, 0:1], np.diff(y))) - below_size = total - stack - below_size += 0.5 * y - move_up = below_size * inv_total - move_up[:, 0] = 0.5 - center = (move_up - 0.5) * increase - center = np.cumsum(center.sum(0)) - first_line = center - 0.5 * total - stack += first_line - - # Color between x = 0 and the first array. - coll = axes.fill_between(x, first_line, stack[0, :], - facecolor=next(colors), label=next(labels, None), - **kwargs) - coll.sticky_edges.y[:] = [0] - r = [coll] - - # Color between array i-1 and array i - for i in range(len(y) - 1): - r.append(axes.fill_between(x, stack[i, :], stack[i + 1, :], - facecolor=next(colors), - label=next(labels, None), - **kwargs)) - return r diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/streamplot.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/streamplot.py deleted file mode 100644 index 293fd67..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/streamplot.py +++ /dev/null @@ -1,707 +0,0 @@ -""" -Streamline plotting for 2D vector fields. - -""" - -import numpy as np - -import matplotlib as mpl -from matplotlib import _api, cm, patches -import matplotlib.colors as mcolors -import matplotlib.collections as mcollections -import matplotlib.lines as mlines - - -__all__ = ['streamplot'] - - -def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None, - cmap=None, norm=None, arrowsize=1, arrowstyle='-|>', - minlength=0.1, transform=None, zorder=None, start_points=None, - maxlength=4.0, integration_direction='both', - broken_streamlines=True): - """ - Draw streamlines of a vector flow. - - Parameters - ---------- - x, y : 1D/2D arrays - Evenly spaced strictly increasing arrays to make a grid. If 2D, all - rows of *x* must be equal and all columns of *y* must be equal; i.e., - they must be as if generated by ``np.meshgrid(x_1d, y_1d)``. - u, v : 2D arrays - *x* and *y*-velocities. The number of rows and columns must match - the length of *y* and *x*, respectively. - density : float or (float, float) - Controls the closeness of streamlines. When ``density = 1``, the domain - is divided into a 30x30 grid. *density* linearly scales this grid. - Each cell in the grid can have, at most, one traversing streamline. - For different densities in each direction, use a tuple - (density_x, density_y). - linewidth : float or 2D array - The width of the streamlines. With a 2D array the line width can be - varied across the grid. The array must have the same shape as *u* - and *v*. - color : color or 2D array - The streamline color. If given an array, its values are converted to - colors using *cmap* and *norm*. The array must have the same shape - as *u* and *v*. - cmap, norm - Data normalization and colormapping parameters for *color*; only used - if *color* is an array of floats. See `~.Axes.imshow` for a detailed - description. - arrowsize : float - Scaling factor for the arrow size. - arrowstyle : str - Arrow style specification. - See `~matplotlib.patches.FancyArrowPatch`. - minlength : float - Minimum length of streamline in axes coordinates. - start_points : Nx2 array - Coordinates of starting points for the streamlines in data coordinates - (the same coordinates as the *x* and *y* arrays). - zorder : float - The zorder of the streamlines and arrows. - Artists with lower zorder values are drawn first. - maxlength : float - Maximum length of streamline in axes coordinates. - integration_direction : {'forward', 'backward', 'both'}, default: 'both' - Integrate the streamline in forward, backward or both directions. - data : indexable object, optional - DATA_PARAMETER_PLACEHOLDER - broken_streamlines : boolean, default: True - If False, forces streamlines to continue until they - leave the plot domain. If True, they may be terminated if they - come too close to another streamline. - - Returns - ------- - StreamplotSet - Container object with attributes - - - ``lines``: `.LineCollection` of streamlines - - - ``arrows``: `.PatchCollection` containing `.FancyArrowPatch` - objects representing the arrows half-way along streamlines. - - This container will probably change in the future to allow changes - to the colormap, alpha, etc. for both lines and arrows, but these - changes should be backward compatible. - """ - grid = Grid(x, y) - mask = StreamMask(density) - dmap = DomainMap(grid, mask) - - if zorder is None: - zorder = mlines.Line2D.zorder - - # default to data coordinates - if transform is None: - transform = axes.transData - - if color is None: - color = axes._get_lines.get_next_color() - - if linewidth is None: - linewidth = mpl.rcParams['lines.linewidth'] - - line_kw = {} - arrow_kw = dict(arrowstyle=arrowstyle, mutation_scale=10 * arrowsize) - - _api.check_in_list(['both', 'forward', 'backward'], - integration_direction=integration_direction) - - if integration_direction == 'both': - maxlength /= 2. - - use_multicolor_lines = isinstance(color, np.ndarray) - if use_multicolor_lines: - if color.shape != grid.shape: - raise ValueError("If 'color' is given, it must match the shape of " - "the (x, y) grid") - line_colors = [[]] # Empty entry allows concatenation of zero arrays. - color = np.ma.masked_invalid(color) - else: - line_kw['color'] = color - arrow_kw['color'] = color - - if isinstance(linewidth, np.ndarray): - if linewidth.shape != grid.shape: - raise ValueError("If 'linewidth' is given, it must match the " - "shape of the (x, y) grid") - line_kw['linewidth'] = [] - else: - line_kw['linewidth'] = linewidth - arrow_kw['linewidth'] = linewidth - - line_kw['zorder'] = zorder - arrow_kw['zorder'] = zorder - - # Sanity checks. - if u.shape != grid.shape or v.shape != grid.shape: - raise ValueError("'u' and 'v' must match the shape of the (x, y) grid") - - u = np.ma.masked_invalid(u) - v = np.ma.masked_invalid(v) - - integrate = _get_integrator(u, v, dmap, minlength, maxlength, - integration_direction) - - trajectories = [] - if start_points is None: - for xm, ym in _gen_starting_points(mask.shape): - if mask[ym, xm] == 0: - xg, yg = dmap.mask2grid(xm, ym) - t = integrate(xg, yg, broken_streamlines) - if t is not None: - trajectories.append(t) - else: - sp2 = np.asanyarray(start_points, dtype=float).copy() - - # Check if start_points are outside the data boundaries - for xs, ys in sp2: - if not (grid.x_origin <= xs <= grid.x_origin + grid.width and - grid.y_origin <= ys <= grid.y_origin + grid.height): - raise ValueError("Starting point ({}, {}) outside of data " - "boundaries".format(xs, ys)) - - # Convert start_points from data to array coords - # Shift the seed points from the bottom left of the data so that - # data2grid works properly. - sp2[:, 0] -= grid.x_origin - sp2[:, 1] -= grid.y_origin - - for xs, ys in sp2: - xg, yg = dmap.data2grid(xs, ys) - # Floating point issues can cause xg, yg to be slightly out of - # bounds for xs, ys on the upper boundaries. Because we have - # already checked that the starting points are within the original - # grid, clip the xg, yg to the grid to work around this issue - xg = np.clip(xg, 0, grid.nx - 1) - yg = np.clip(yg, 0, grid.ny - 1) - - t = integrate(xg, yg, broken_streamlines) - if t is not None: - trajectories.append(t) - - if use_multicolor_lines: - if norm is None: - norm = mcolors.Normalize(color.min(), color.max()) - cmap = cm._ensure_cmap(cmap) - - streamlines = [] - arrows = [] - for t in trajectories: - tgx, tgy = t.T - # Rescale from grid-coordinates to data-coordinates. - tx, ty = dmap.grid2data(tgx, tgy) - tx += grid.x_origin - ty += grid.y_origin - - points = np.transpose([tx, ty]).reshape(-1, 1, 2) - streamlines.extend(np.hstack([points[:-1], points[1:]])) - - # Add arrows halfway along each trajectory. - s = np.cumsum(np.hypot(np.diff(tx), np.diff(ty))) - n = np.searchsorted(s, s[-1] / 2.) - arrow_tail = (tx[n], ty[n]) - arrow_head = (np.mean(tx[n:n + 2]), np.mean(ty[n:n + 2])) - - if isinstance(linewidth, np.ndarray): - line_widths = interpgrid(linewidth, tgx, tgy)[:-1] - line_kw['linewidth'].extend(line_widths) - arrow_kw['linewidth'] = line_widths[n] - - if use_multicolor_lines: - color_values = interpgrid(color, tgx, tgy)[:-1] - line_colors.append(color_values) - arrow_kw['color'] = cmap(norm(color_values[n])) - - p = patches.FancyArrowPatch( - arrow_tail, arrow_head, transform=transform, **arrow_kw) - arrows.append(p) - - lc = mcollections.LineCollection( - streamlines, transform=transform, **line_kw) - lc.sticky_edges.x[:] = [grid.x_origin, grid.x_origin + grid.width] - lc.sticky_edges.y[:] = [grid.y_origin, grid.y_origin + grid.height] - if use_multicolor_lines: - lc.set_array(np.ma.hstack(line_colors)) - lc.set_cmap(cmap) - lc.set_norm(norm) - axes.add_collection(lc) - - ac = mcollections.PatchCollection(arrows) - # Adding the collection itself is broken; see #2341. - for p in arrows: - axes.add_patch(p) - - axes.autoscale_view() - stream_container = StreamplotSet(lc, ac) - return stream_container - - -class StreamplotSet: - - def __init__(self, lines, arrows): - self.lines = lines - self.arrows = arrows - - -# Coordinate definitions -# ======================== - -class DomainMap: - """ - Map representing different coordinate systems. - - Coordinate definitions: - - * axes-coordinates goes from 0 to 1 in the domain. - * data-coordinates are specified by the input x-y coordinates. - * grid-coordinates goes from 0 to N and 0 to M for an N x M grid, - where N and M match the shape of the input data. - * mask-coordinates goes from 0 to N and 0 to M for an N x M mask, - where N and M are user-specified to control the density of streamlines. - - This class also has methods for adding trajectories to the StreamMask. - Before adding a trajectory, run `start_trajectory` to keep track of regions - crossed by a given trajectory. Later, if you decide the trajectory is bad - (e.g., if the trajectory is very short) just call `undo_trajectory`. - """ - - def __init__(self, grid, mask): - self.grid = grid - self.mask = mask - # Constants for conversion between grid- and mask-coordinates - self.x_grid2mask = (mask.nx - 1) / (grid.nx - 1) - self.y_grid2mask = (mask.ny - 1) / (grid.ny - 1) - - self.x_mask2grid = 1. / self.x_grid2mask - self.y_mask2grid = 1. / self.y_grid2mask - - self.x_data2grid = 1. / grid.dx - self.y_data2grid = 1. / grid.dy - - def grid2mask(self, xi, yi): - """Return nearest space in mask-coords from given grid-coords.""" - return round(xi * self.x_grid2mask), round(yi * self.y_grid2mask) - - def mask2grid(self, xm, ym): - return xm * self.x_mask2grid, ym * self.y_mask2grid - - def data2grid(self, xd, yd): - return xd * self.x_data2grid, yd * self.y_data2grid - - def grid2data(self, xg, yg): - return xg / self.x_data2grid, yg / self.y_data2grid - - def start_trajectory(self, xg, yg, broken_streamlines=True): - xm, ym = self.grid2mask(xg, yg) - self.mask._start_trajectory(xm, ym, broken_streamlines) - - def reset_start_point(self, xg, yg): - xm, ym = self.grid2mask(xg, yg) - self.mask._current_xy = (xm, ym) - - def update_trajectory(self, xg, yg, broken_streamlines=True): - if not self.grid.within_grid(xg, yg): - raise InvalidIndexError - xm, ym = self.grid2mask(xg, yg) - self.mask._update_trajectory(xm, ym, broken_streamlines) - - def undo_trajectory(self): - self.mask._undo_trajectory() - - -class Grid: - """Grid of data.""" - def __init__(self, x, y): - - if np.ndim(x) == 1: - pass - elif np.ndim(x) == 2: - x_row = x[0] - if not np.allclose(x_row, x): - raise ValueError("The rows of 'x' must be equal") - x = x_row - else: - raise ValueError("'x' can have at maximum 2 dimensions") - - if np.ndim(y) == 1: - pass - elif np.ndim(y) == 2: - yt = np.transpose(y) # Also works for nested lists. - y_col = yt[0] - if not np.allclose(y_col, yt): - raise ValueError("The columns of 'y' must be equal") - y = y_col - else: - raise ValueError("'y' can have at maximum 2 dimensions") - - if not (np.diff(x) > 0).all(): - raise ValueError("'x' must be strictly increasing") - if not (np.diff(y) > 0).all(): - raise ValueError("'y' must be strictly increasing") - - self.nx = len(x) - self.ny = len(y) - - self.dx = x[1] - x[0] - self.dy = y[1] - y[0] - - self.x_origin = x[0] - self.y_origin = y[0] - - self.width = x[-1] - x[0] - self.height = y[-1] - y[0] - - if not np.allclose(np.diff(x), self.width / (self.nx - 1)): - raise ValueError("'x' values must be equally spaced") - if not np.allclose(np.diff(y), self.height / (self.ny - 1)): - raise ValueError("'y' values must be equally spaced") - - @property - def shape(self): - return self.ny, self.nx - - def within_grid(self, xi, yi): - """Return whether (*xi*, *yi*) is a valid index of the grid.""" - # Note that xi/yi can be floats; so, for example, we can't simply check - # `xi < self.nx` since *xi* can be `self.nx - 1 < xi < self.nx` - return 0 <= xi <= self.nx - 1 and 0 <= yi <= self.ny - 1 - - -class StreamMask: - """ - Mask to keep track of discrete regions crossed by streamlines. - - The resolution of this grid determines the approximate spacing between - trajectories. Streamlines are only allowed to pass through zeroed cells: - When a streamline enters a cell, that cell is set to 1, and no new - streamlines are allowed to enter. - """ - - def __init__(self, density): - try: - self.nx, self.ny = (30 * np.broadcast_to(density, 2)).astype(int) - except ValueError as err: - raise ValueError("'density' must be a scalar or be of length " - "2") from err - if self.nx < 0 or self.ny < 0: - raise ValueError("'density' must be positive") - self._mask = np.zeros((self.ny, self.nx)) - self.shape = self._mask.shape - - self._current_xy = None - - def __getitem__(self, args): - return self._mask[args] - - def _start_trajectory(self, xm, ym, broken_streamlines=True): - """Start recording streamline trajectory""" - self._traj = [] - self._update_trajectory(xm, ym, broken_streamlines) - - def _undo_trajectory(self): - """Remove current trajectory from mask""" - for t in self._traj: - self._mask[t] = 0 - - def _update_trajectory(self, xm, ym, broken_streamlines=True): - """ - Update current trajectory position in mask. - - If the new position has already been filled, raise `InvalidIndexError`. - """ - if self._current_xy != (xm, ym): - if self[ym, xm] == 0: - self._traj.append((ym, xm)) - self._mask[ym, xm] = 1 - self._current_xy = (xm, ym) - else: - if broken_streamlines: - raise InvalidIndexError - else: - pass - - -class InvalidIndexError(Exception): - pass - - -class TerminateTrajectory(Exception): - pass - - -# Integrator definitions -# ======================= - -def _get_integrator(u, v, dmap, minlength, maxlength, integration_direction): - - # rescale velocity onto grid-coordinates for integrations. - u, v = dmap.data2grid(u, v) - - # speed (path length) will be in axes-coordinates - u_ax = u / (dmap.grid.nx - 1) - v_ax = v / (dmap.grid.ny - 1) - speed = np.ma.sqrt(u_ax ** 2 + v_ax ** 2) - - def forward_time(xi, yi): - if not dmap.grid.within_grid(xi, yi): - raise OutOfBounds - ds_dt = interpgrid(speed, xi, yi) - if ds_dt == 0: - raise TerminateTrajectory() - dt_ds = 1. / ds_dt - ui = interpgrid(u, xi, yi) - vi = interpgrid(v, xi, yi) - return ui * dt_ds, vi * dt_ds - - def backward_time(xi, yi): - dxi, dyi = forward_time(xi, yi) - return -dxi, -dyi - - def integrate(x0, y0, broken_streamlines=True): - """ - Return x, y grid-coordinates of trajectory based on starting point. - - Integrate both forward and backward in time from starting point in - grid coordinates. - - Integration is terminated when a trajectory reaches a domain boundary - or when it crosses into an already occupied cell in the StreamMask. The - resulting trajectory is None if it is shorter than `minlength`. - """ - - stotal, xy_traj = 0., [] - - try: - dmap.start_trajectory(x0, y0, broken_streamlines) - except InvalidIndexError: - return None - if integration_direction in ['both', 'backward']: - s, xyt = _integrate_rk12(x0, y0, dmap, backward_time, maxlength, - broken_streamlines) - stotal += s - xy_traj += xyt[::-1] - - if integration_direction in ['both', 'forward']: - dmap.reset_start_point(x0, y0) - s, xyt = _integrate_rk12(x0, y0, dmap, forward_time, maxlength, - broken_streamlines) - stotal += s - xy_traj += xyt[1:] - - if stotal > minlength: - return np.broadcast_arrays(xy_traj, np.empty((1, 2)))[0] - else: # reject short trajectories - dmap.undo_trajectory() - return None - - return integrate - - -class OutOfBounds(IndexError): - pass - - -def _integrate_rk12(x0, y0, dmap, f, maxlength, broken_streamlines=True): - """ - 2nd-order Runge-Kutta algorithm with adaptive step size. - - This method is also referred to as the improved Euler's method, or Heun's - method. This method is favored over higher-order methods because: - - 1. To get decent looking trajectories and to sample every mask cell - on the trajectory we need a small timestep, so a lower order - solver doesn't hurt us unless the data is *very* high resolution. - In fact, for cases where the user inputs - data smaller or of similar grid size to the mask grid, the higher - order corrections are negligible because of the very fast linear - interpolation used in `interpgrid`. - - 2. For high resolution input data (i.e. beyond the mask - resolution), we must reduce the timestep. Therefore, an adaptive - timestep is more suited to the problem as this would be very hard - to judge automatically otherwise. - - This integrator is about 1.5 - 2x as fast as RK4 and RK45 solvers (using - similar Python implementations) in most setups. - """ - # This error is below that needed to match the RK4 integrator. It - # is set for visual reasons -- too low and corners start - # appearing ugly and jagged. Can be tuned. - maxerror = 0.003 - - # This limit is important (for all integrators) to avoid the - # trajectory skipping some mask cells. We could relax this - # condition if we use the code which is commented out below to - # increment the location gradually. However, due to the efficient - # nature of the interpolation, this doesn't boost speed by much - # for quite a bit of complexity. - maxds = min(1. / dmap.mask.nx, 1. / dmap.mask.ny, 0.1) - - ds = maxds - stotal = 0 - xi = x0 - yi = y0 - xyf_traj = [] - - while True: - try: - if dmap.grid.within_grid(xi, yi): - xyf_traj.append((xi, yi)) - else: - raise OutOfBounds - - # Compute the two intermediate gradients. - # f should raise OutOfBounds if the locations given are - # outside the grid. - k1x, k1y = f(xi, yi) - k2x, k2y = f(xi + ds * k1x, yi + ds * k1y) - - except OutOfBounds: - # Out of the domain during this step. - # Take an Euler step to the boundary to improve neatness - # unless the trajectory is currently empty. - if xyf_traj: - ds, xyf_traj = _euler_step(xyf_traj, dmap, f) - stotal += ds - break - except TerminateTrajectory: - break - - dx1 = ds * k1x - dy1 = ds * k1y - dx2 = ds * 0.5 * (k1x + k2x) - dy2 = ds * 0.5 * (k1y + k2y) - - ny, nx = dmap.grid.shape - # Error is normalized to the axes coordinates - error = np.hypot((dx2 - dx1) / (nx - 1), (dy2 - dy1) / (ny - 1)) - - # Only save step if within error tolerance - if error < maxerror: - xi += dx2 - yi += dy2 - try: - dmap.update_trajectory(xi, yi, broken_streamlines) - except InvalidIndexError: - break - if stotal + ds > maxlength: - break - stotal += ds - - # recalculate stepsize based on step error - if error == 0: - ds = maxds - else: - ds = min(maxds, 0.85 * ds * (maxerror / error) ** 0.5) - - return stotal, xyf_traj - - -def _euler_step(xyf_traj, dmap, f): - """Simple Euler integration step that extends streamline to boundary.""" - ny, nx = dmap.grid.shape - xi, yi = xyf_traj[-1] - cx, cy = f(xi, yi) - if cx == 0: - dsx = np.inf - elif cx < 0: - dsx = xi / -cx - else: - dsx = (nx - 1 - xi) / cx - if cy == 0: - dsy = np.inf - elif cy < 0: - dsy = yi / -cy - else: - dsy = (ny - 1 - yi) / cy - ds = min(dsx, dsy) - xyf_traj.append((xi + cx * ds, yi + cy * ds)) - return ds, xyf_traj - - -# Utility functions -# ======================== - -def interpgrid(a, xi, yi): - """Fast 2D, linear interpolation on an integer grid""" - - Ny, Nx = np.shape(a) - if isinstance(xi, np.ndarray): - x = xi.astype(int) - y = yi.astype(int) - # Check that xn, yn don't exceed max index - xn = np.clip(x + 1, 0, Nx - 1) - yn = np.clip(y + 1, 0, Ny - 1) - else: - x = int(xi) - y = int(yi) - # conditional is faster than clipping for integers - if x == (Nx - 1): - xn = x - else: - xn = x + 1 - if y == (Ny - 1): - yn = y - else: - yn = y + 1 - - a00 = a[y, x] - a01 = a[y, xn] - a10 = a[yn, x] - a11 = a[yn, xn] - xt = xi - x - yt = yi - y - a0 = a00 * (1 - xt) + a01 * xt - a1 = a10 * (1 - xt) + a11 * xt - ai = a0 * (1 - yt) + a1 * yt - - if not isinstance(xi, np.ndarray): - if np.ma.is_masked(ai): - raise TerminateTrajectory - - return ai - - -def _gen_starting_points(shape): - """ - Yield starting points for streamlines. - - Trying points on the boundary first gives higher quality streamlines. - This algorithm starts with a point on the mask corner and spirals inward. - This algorithm is inefficient, but fast compared to rest of streamplot. - """ - ny, nx = shape - xfirst = 0 - yfirst = 1 - xlast = nx - 1 - ylast = ny - 1 - x, y = 0, 0 - direction = 'right' - for i in range(nx * ny): - yield x, y - - if direction == 'right': - x += 1 - if x >= xlast: - xlast -= 1 - direction = 'up' - elif direction == 'up': - y += 1 - if y >= ylast: - ylast -= 1 - direction = 'left' - elif direction == 'left': - x -= 1 - if x <= xfirst: - xfirst += 1 - direction = 'down' - elif direction == 'down': - y -= 1 - if y <= yfirst: - yfirst += 1 - direction = 'right' diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/style/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/style/__init__.py deleted file mode 100644 index 488c6d6..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/style/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .core import available, context, library, reload_library, use - - -__all__ = ["available", "context", "library", "reload_library", "use"] diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/style/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/style/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index b89fe8e..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/style/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/style/__pycache__/core.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/style/__pycache__/core.cpython-38.pyc deleted file mode 100644 index e725c02..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/style/__pycache__/core.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/style/core.py b/plotter-app/venv/lib/python3.8/site-packages/matplotlib/style/core.py deleted file mode 100644 index 4ff4618..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/matplotlib/style/core.py +++ /dev/null @@ -1,283 +0,0 @@ -""" -Core functions and attributes for the matplotlib style library: - -``use`` - Select style sheet to override the current matplotlib settings. -``context`` - Context manager to use a style sheet temporarily. -``available`` - List available style sheets. -``library`` - A dictionary of style names and matplotlib settings. -""" - -import contextlib -import logging -import os -from pathlib import Path -import sys -import warnings - -if sys.version_info >= (3, 10): - import importlib.resources as importlib_resources -else: - # Even though Py3.9 has importlib.resources, it doesn't properly handle - # modules added in sys.path. - import importlib_resources - -import matplotlib as mpl -from matplotlib import _api, _docstring, _rc_params_in_file, rcParamsDefault - -_log = logging.getLogger(__name__) - -__all__ = ['use', 'context', 'available', 'library', 'reload_library'] - - -BASE_LIBRARY_PATH = os.path.join(mpl.get_data_path(), 'stylelib') -# Users may want multiple library paths, so store a list of paths. -USER_LIBRARY_PATHS = [os.path.join(mpl.get_configdir(), 'stylelib')] -STYLE_EXTENSION = 'mplstyle' -# A list of rcParams that should not be applied from styles -STYLE_BLACKLIST = { - 'interactive', 'backend', 'webagg.port', 'webagg.address', - 'webagg.port_retries', 'webagg.open_in_browser', 'backend_fallback', - 'toolbar', 'timezone', 'figure.max_open_warning', - 'figure.raise_window', 'savefig.directory', 'tk.window_focus', - 'docstring.hardcopy', 'date.epoch'} -_DEPRECATED_SEABORN_STYLES = { - s: s.replace("seaborn", "seaborn-v0_8") - for s in [ - "seaborn", - "seaborn-bright", - "seaborn-colorblind", - "seaborn-dark", - "seaborn-darkgrid", - "seaborn-dark-palette", - "seaborn-deep", - "seaborn-muted", - "seaborn-notebook", - "seaborn-paper", - "seaborn-pastel", - "seaborn-poster", - "seaborn-talk", - "seaborn-ticks", - "seaborn-white", - "seaborn-whitegrid", - ] -} -_DEPRECATED_SEABORN_MSG = ( - "The seaborn styles shipped by Matplotlib are deprecated since %(since)s, " - "as they no longer correspond to the styles shipped by seaborn. However, " - "they will remain available as 'seaborn-v0_8- - {% else %} - {{ head | safe }} - {% endif %} -{% if not embed %} - - -{% endif %} - -{{ body | safe }} -{% for diagram in diagrams %} -
-

{{ diagram.title }}

-
{{ diagram.text }}
-
- {{ diagram.svg }} -
-
-{% endfor %} -{% if not embed %} - - -{% endif %} -""" - -template = Template(jinja2_template_source) - -# Note: ideally this would be a dataclass, but we're supporting Python 3.5+ so we can't do this yet -NamedDiagram = NamedTuple( - "NamedDiagram", - [("name", str), ("diagram", typing.Optional[railroad.DiagramItem]), ("index", int)], -) -""" -A simple structure for associating a name with a railroad diagram -""" - -T = TypeVar("T") - - -class EachItem(railroad.Group): - """ - Custom railroad item to compose a: - - Group containing a - - OneOrMore containing a - - Choice of the elements in the Each - with the group label indicating that all must be matched - """ - - all_label = "[ALL]" - - def __init__(self, *items): - choice_item = railroad.Choice(len(items) - 1, *items) - one_or_more_item = railroad.OneOrMore(item=choice_item) - super().__init__(one_or_more_item, label=self.all_label) - - -class AnnotatedItem(railroad.Group): - """ - Simple subclass of Group that creates an annotation label - """ - - def __init__(self, label: str, item): - super().__init__(item=item, label=f"[{label}]") - - -class EditablePartial(Generic[T]): - """ - Acts like a functools.partial, but can be edited. In other words, it represents a type that hasn't yet been - constructed. - """ - - # We need this here because the railroad constructors actually transform the data, so can't be called until the - # entire tree is assembled - - def __init__(self, func: Callable[..., T], args: list, kwargs: dict): - self.func = func - self.args = args - self.kwargs = kwargs - - @classmethod - def from_call(cls, func: Callable[..., T], *args, **kwargs) -> "EditablePartial[T]": - """ - If you call this function in the same way that you would call the constructor, it will store the arguments - as you expect. For example EditablePartial.from_call(Fraction, 1, 3)() == Fraction(1, 3) - """ - return EditablePartial(func=func, args=list(args), kwargs=kwargs) - - @property - def name(self): - return self.kwargs["name"] - - def __call__(self) -> T: - """ - Evaluate the partial and return the result - """ - args = self.args.copy() - kwargs = self.kwargs.copy() - - # This is a helpful hack to allow you to specify varargs parameters (e.g. *args) as keyword args (e.g. - # args=['list', 'of', 'things']) - arg_spec = inspect.getfullargspec(self.func) - if arg_spec.varargs in self.kwargs: - args += kwargs.pop(arg_spec.varargs) - - return self.func(*args, **kwargs) - - -def railroad_to_html(diagrams: List[NamedDiagram], embed=False, **kwargs) -> str: - """ - Given a list of NamedDiagram, produce a single HTML string that visualises those diagrams - :params kwargs: kwargs to be passed in to the template - """ - data = [] - for diagram in diagrams: - if diagram.diagram is None: - continue - io = StringIO() - try: - css = kwargs.get("css") - diagram.diagram.writeStandalone(io.write, css=css) - except AttributeError: - diagram.diagram.writeSvg(io.write) - title = diagram.name - if diagram.index == 0: - title += " (root)" - data.append({"title": title, "text": "", "svg": io.getvalue()}) - - return template.render(diagrams=data, embed=embed, **kwargs) - - -def resolve_partial(partial: "EditablePartial[T]") -> T: - """ - Recursively resolves a collection of Partials into whatever type they are - """ - if isinstance(partial, EditablePartial): - partial.args = resolve_partial(partial.args) - partial.kwargs = resolve_partial(partial.kwargs) - return partial() - elif isinstance(partial, list): - return [resolve_partial(x) for x in partial] - elif isinstance(partial, dict): - return {key: resolve_partial(x) for key, x in partial.items()} - else: - return partial - - -def to_railroad( - element: pyparsing.ParserElement, - diagram_kwargs: typing.Optional[dict] = None, - vertical: int = 3, - show_results_names: bool = False, - show_groups: bool = False, -) -> List[NamedDiagram]: - """ - Convert a pyparsing element tree into a list of diagrams. This is the recommended entrypoint to diagram - creation if you want to access the Railroad tree before it is converted to HTML - :param element: base element of the parser being diagrammed - :param diagram_kwargs: kwargs to pass to the Diagram() constructor - :param vertical: (optional) - int - limit at which number of alternatives should be - shown vertically instead of horizontally - :param show_results_names - bool to indicate whether results name annotations should be - included in the diagram - :param show_groups - bool to indicate whether groups should be highlighted with an unlabeled - surrounding box - """ - # Convert the whole tree underneath the root - lookup = ConverterState(diagram_kwargs=diagram_kwargs or {}) - _to_diagram_element( - element, - lookup=lookup, - parent=None, - vertical=vertical, - show_results_names=show_results_names, - show_groups=show_groups, - ) - - root_id = id(element) - # Convert the root if it hasn't been already - if root_id in lookup: - if not element.customName: - lookup[root_id].name = "" - lookup[root_id].mark_for_extraction(root_id, lookup, force=True) - - # Now that we're finished, we can convert from intermediate structures into Railroad elements - diags = list(lookup.diagrams.values()) - if len(diags) > 1: - # collapse out duplicate diags with the same name - seen = set() - deduped_diags = [] - for d in diags: - # don't extract SkipTo elements, they are uninformative as subdiagrams - if d.name == "...": - continue - if d.name is not None and d.name not in seen: - seen.add(d.name) - deduped_diags.append(d) - resolved = [resolve_partial(partial) for partial in deduped_diags] - else: - # special case - if just one diagram, always display it, even if - # it has no name - resolved = [resolve_partial(partial) for partial in diags] - return sorted(resolved, key=lambda diag: diag.index) - - -def _should_vertical( - specification: int, exprs: Iterable[pyparsing.ParserElement] -) -> bool: - """ - Returns true if we should return a vertical list of elements - """ - if specification is None: - return False - else: - return len(_visible_exprs(exprs)) >= specification - - -class ElementState: - """ - State recorded for an individual pyparsing Element - """ - - # Note: this should be a dataclass, but we have to support Python 3.5 - def __init__( - self, - element: pyparsing.ParserElement, - converted: EditablePartial, - parent: EditablePartial, - number: int, - name: str = None, - parent_index: typing.Optional[int] = None, - ): - #: The pyparsing element that this represents - self.element: pyparsing.ParserElement = element - #: The name of the element - self.name: typing.Optional[str] = name - #: The output Railroad element in an unconverted state - self.converted: EditablePartial = converted - #: The parent Railroad element, which we store so that we can extract this if it's duplicated - self.parent: EditablePartial = parent - #: The order in which we found this element, used for sorting diagrams if this is extracted into a diagram - self.number: int = number - #: The index of this inside its parent - self.parent_index: typing.Optional[int] = parent_index - #: If true, we should extract this out into a subdiagram - self.extract: bool = False - #: If true, all of this element's children have been filled out - self.complete: bool = False - - def mark_for_extraction( - self, el_id: int, state: "ConverterState", name: str = None, force: bool = False - ): - """ - Called when this instance has been seen twice, and thus should eventually be extracted into a sub-diagram - :param el_id: id of the element - :param state: element/diagram state tracker - :param name: name to use for this element's text - :param force: If true, force extraction now, regardless of the state of this. Only useful for extracting the - root element when we know we're finished - """ - self.extract = True - - # Set the name - if not self.name: - if name: - # Allow forcing a custom name - self.name = name - elif self.element.customName: - self.name = self.element.customName - else: - self.name = "" - - # Just because this is marked for extraction doesn't mean we can do it yet. We may have to wait for children - # to be added - # Also, if this is just a string literal etc, don't bother extracting it - if force or (self.complete and _worth_extracting(self.element)): - state.extract_into_diagram(el_id) - - -class ConverterState: - """ - Stores some state that persists between recursions into the element tree - """ - - def __init__(self, diagram_kwargs: typing.Optional[dict] = None): - #: A dictionary mapping ParserElements to state relating to them - self._element_diagram_states: Dict[int, ElementState] = {} - #: A dictionary mapping ParserElement IDs to subdiagrams generated from them - self.diagrams: Dict[int, EditablePartial[NamedDiagram]] = {} - #: The index of the next unnamed element - self.unnamed_index: int = 1 - #: The index of the next element. This is used for sorting - self.index: int = 0 - #: Shared kwargs that are used to customize the construction of diagrams - self.diagram_kwargs: dict = diagram_kwargs or {} - self.extracted_diagram_names: Set[str] = set() - - def __setitem__(self, key: int, value: ElementState): - self._element_diagram_states[key] = value - - def __getitem__(self, key: int) -> ElementState: - return self._element_diagram_states[key] - - def __delitem__(self, key: int): - del self._element_diagram_states[key] - - def __contains__(self, key: int): - return key in self._element_diagram_states - - def generate_unnamed(self) -> int: - """ - Generate a number used in the name of an otherwise unnamed diagram - """ - self.unnamed_index += 1 - return self.unnamed_index - - def generate_index(self) -> int: - """ - Generate a number used to index a diagram - """ - self.index += 1 - return self.index - - def extract_into_diagram(self, el_id: int): - """ - Used when we encounter the same token twice in the same tree. When this - happens, we replace all instances of that token with a terminal, and - create a new subdiagram for the token - """ - position = self[el_id] - - # Replace the original definition of this element with a regular block - if position.parent: - ret = EditablePartial.from_call(railroad.NonTerminal, text=position.name) - if "item" in position.parent.kwargs: - position.parent.kwargs["item"] = ret - elif "items" in position.parent.kwargs: - position.parent.kwargs["items"][position.parent_index] = ret - - # If the element we're extracting is a group, skip to its content but keep the title - if position.converted.func == railroad.Group: - content = position.converted.kwargs["item"] - else: - content = position.converted - - self.diagrams[el_id] = EditablePartial.from_call( - NamedDiagram, - name=position.name, - diagram=EditablePartial.from_call( - railroad.Diagram, content, **self.diagram_kwargs - ), - index=position.number, - ) - - del self[el_id] - - -def _worth_extracting(element: pyparsing.ParserElement) -> bool: - """ - Returns true if this element is worth having its own sub-diagram. Simply, if any of its children - themselves have children, then its complex enough to extract - """ - children = element.recurse() - return any(child.recurse() for child in children) - - -def _apply_diagram_item_enhancements(fn): - """ - decorator to ensure enhancements to a diagram item (such as results name annotations) - get applied on return from _to_diagram_element (we do this since there are several - returns in _to_diagram_element) - """ - - def _inner( - element: pyparsing.ParserElement, - parent: typing.Optional[EditablePartial], - lookup: ConverterState = None, - vertical: int = None, - index: int = 0, - name_hint: str = None, - show_results_names: bool = False, - show_groups: bool = False, - ) -> typing.Optional[EditablePartial]: - ret = fn( - element, - parent, - lookup, - vertical, - index, - name_hint, - show_results_names, - show_groups, - ) - - # apply annotation for results name, if present - if show_results_names and ret is not None: - element_results_name = element.resultsName - if element_results_name: - # add "*" to indicate if this is a "list all results" name - modal_tag = "" if element.modalResults else "*" - ret = EditablePartial.from_call( - railroad.Group, - item=ret, - label=f"{repr(element_results_name)}{modal_tag}", - ) - - return ret - - return _inner - - -def _visible_exprs(exprs: Iterable[pyparsing.ParserElement]): - non_diagramming_exprs = ( - pyparsing.ParseElementEnhance, - pyparsing.PositionToken, - pyparsing.And._ErrorStop, - ) - return [ - e - for e in exprs - if not (e.customName or e.resultsName or isinstance(e, non_diagramming_exprs)) - ] - - -@_apply_diagram_item_enhancements -def _to_diagram_element( - element: pyparsing.ParserElement, - parent: typing.Optional[EditablePartial], - lookup: ConverterState = None, - vertical: int = None, - index: int = 0, - name_hint: str = None, - show_results_names: bool = False, - show_groups: bool = False, -) -> typing.Optional[EditablePartial]: - """ - Recursively converts a PyParsing Element to a railroad Element - :param lookup: The shared converter state that keeps track of useful things - :param index: The index of this element within the parent - :param parent: The parent of this element in the output tree - :param vertical: Controls at what point we make a list of elements vertical. If this is an integer (the default), - it sets the threshold of the number of items before we go vertical. If True, always go vertical, if False, never - do so - :param name_hint: If provided, this will override the generated name - :param show_results_names: bool flag indicating whether to add annotations for results names - :returns: The converted version of the input element, but as a Partial that hasn't yet been constructed - :param show_groups: bool flag indicating whether to show groups using bounding box - """ - exprs = element.recurse() - name = name_hint or element.customName or type(element).__name__ - - # Python's id() is used to provide a unique identifier for elements - el_id = id(element) - - element_results_name = element.resultsName - - # Here we basically bypass processing certain wrapper elements if they contribute nothing to the diagram - if not element.customName: - if isinstance( - element, - ( - # pyparsing.TokenConverter, - # pyparsing.Forward, - pyparsing.Located, - ), - ): - # However, if this element has a useful custom name, and its child does not, we can pass it on to the child - if exprs: - if not exprs[0].customName: - propagated_name = name - else: - propagated_name = None - - return _to_diagram_element( - element.expr, - parent=parent, - lookup=lookup, - vertical=vertical, - index=index, - name_hint=propagated_name, - show_results_names=show_results_names, - show_groups=show_groups, - ) - - # If the element isn't worth extracting, we always treat it as the first time we say it - if _worth_extracting(element): - if el_id in lookup: - # If we've seen this element exactly once before, we are only just now finding out that it's a duplicate, - # so we have to extract it into a new diagram. - looked_up = lookup[el_id] - looked_up.mark_for_extraction(el_id, lookup, name=name_hint) - ret = EditablePartial.from_call(railroad.NonTerminal, text=looked_up.name) - return ret - - elif el_id in lookup.diagrams: - # If we have seen the element at least twice before, and have already extracted it into a subdiagram, we - # just put in a marker element that refers to the sub-diagram - ret = EditablePartial.from_call( - railroad.NonTerminal, text=lookup.diagrams[el_id].kwargs["name"] - ) - return ret - - # Recursively convert child elements - # Here we find the most relevant Railroad element for matching pyparsing Element - # We use ``items=[]`` here to hold the place for where the child elements will go once created - if isinstance(element, pyparsing.And): - # detect And's created with ``expr*N`` notation - for these use a OneOrMore with a repeat - # (all will have the same name, and resultsName) - if not exprs: - return None - if len(set((e.name, e.resultsName) for e in exprs)) == 1 and len(exprs) > 2: - ret = EditablePartial.from_call( - railroad.OneOrMore, item="", repeat=str(len(exprs)) - ) - elif _should_vertical(vertical, exprs): - ret = EditablePartial.from_call(railroad.Stack, items=[]) - else: - ret = EditablePartial.from_call(railroad.Sequence, items=[]) - elif isinstance(element, (pyparsing.Or, pyparsing.MatchFirst)): - if not exprs: - return None - if _should_vertical(vertical, exprs): - ret = EditablePartial.from_call(railroad.Choice, 0, items=[]) - else: - ret = EditablePartial.from_call(railroad.HorizontalChoice, items=[]) - elif isinstance(element, pyparsing.Each): - if not exprs: - return None - ret = EditablePartial.from_call(EachItem, items=[]) - elif isinstance(element, pyparsing.NotAny): - ret = EditablePartial.from_call(AnnotatedItem, label="NOT", item="") - elif isinstance(element, pyparsing.FollowedBy): - ret = EditablePartial.from_call(AnnotatedItem, label="LOOKAHEAD", item="") - elif isinstance(element, pyparsing.PrecededBy): - ret = EditablePartial.from_call(AnnotatedItem, label="LOOKBEHIND", item="") - elif isinstance(element, pyparsing.Group): - if show_groups: - ret = EditablePartial.from_call(AnnotatedItem, label="", item="") - else: - ret = EditablePartial.from_call(railroad.Sequence, items=[]) - elif isinstance(element, pyparsing.TokenConverter): - label = type(element).__name__.lower() - if label == "tokenconverter": - ret = EditablePartial.from_call(railroad.Sequence, items=[]) - else: - ret = EditablePartial.from_call(AnnotatedItem, label=label, item="") - elif isinstance(element, pyparsing.Opt): - ret = EditablePartial.from_call(railroad.Optional, item="") - elif isinstance(element, pyparsing.OneOrMore): - if element.not_ender is not None: - args = [ - parent, - lookup, - vertical, - index, - name_hint, - show_results_names, - show_groups, - ] - return _to_diagram_element( - (~element.not_ender.expr + element.expr)[1, ...].set_name(element.name), - *args, - ) - ret = EditablePartial.from_call(railroad.OneOrMore, item=None) - elif isinstance(element, pyparsing.ZeroOrMore): - if element.not_ender is not None: - args = [ - parent, - lookup, - vertical, - index, - name_hint, - show_results_names, - show_groups, - ] - return _to_diagram_element( - (~element.not_ender.expr + element.expr)[...].set_name(element.name), - *args, - ) - ret = EditablePartial.from_call(railroad.ZeroOrMore, item="") - elif isinstance(element, pyparsing.Group): - ret = EditablePartial.from_call( - railroad.Group, item=None, label=element_results_name - ) - elif isinstance(element, pyparsing.Empty) and not element.customName: - # Skip unnamed "Empty" elements - ret = None - elif isinstance(element, pyparsing.ParseElementEnhance): - ret = EditablePartial.from_call(railroad.Sequence, items=[]) - elif len(exprs) > 0 and not element_results_name: - ret = EditablePartial.from_call(railroad.Group, item="", label=name) - elif len(exprs) > 0: - ret = EditablePartial.from_call(railroad.Sequence, items=[]) - else: - terminal = EditablePartial.from_call(railroad.Terminal, element.defaultName) - ret = terminal - - if ret is None: - return - - # Indicate this element's position in the tree so we can extract it if necessary - lookup[el_id] = ElementState( - element=element, - converted=ret, - parent=parent, - parent_index=index, - number=lookup.generate_index(), - ) - if element.customName: - lookup[el_id].mark_for_extraction(el_id, lookup, element.customName) - - i = 0 - for expr in exprs: - # Add a placeholder index in case we have to extract the child before we even add it to the parent - if "items" in ret.kwargs: - ret.kwargs["items"].insert(i, None) - - item = _to_diagram_element( - expr, - parent=ret, - lookup=lookup, - vertical=vertical, - index=i, - show_results_names=show_results_names, - show_groups=show_groups, - ) - - # Some elements don't need to be shown in the diagram - if item is not None: - if "item" in ret.kwargs: - ret.kwargs["item"] = item - elif "items" in ret.kwargs: - # If we've already extracted the child, don't touch this index, since it's occupied by a nonterminal - ret.kwargs["items"][i] = item - i += 1 - elif "items" in ret.kwargs: - # If we're supposed to skip this element, remove it from the parent - del ret.kwargs["items"][i] - - # If all this items children are none, skip this item - if ret and ( - ("items" in ret.kwargs and len(ret.kwargs["items"]) == 0) - or ("item" in ret.kwargs and ret.kwargs["item"] is None) - ): - ret = EditablePartial.from_call(railroad.Terminal, name) - - # Mark this element as "complete", ie it has all of its children - if el_id in lookup: - lookup[el_id].complete = True - - if el_id in lookup and lookup[el_id].extract and lookup[el_id].complete: - lookup.extract_into_diagram(el_id) - if ret is not None: - ret = EditablePartial.from_call( - railroad.NonTerminal, text=lookup.diagrams[el_id].kwargs["name"] - ) - - return ret diff --git a/plotter-app/venv/lib/python3.8/site-packages/pyparsing/diagram/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/pyparsing/diagram/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 68b5908..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/pyparsing/diagram/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/pyparsing/exceptions.py b/plotter-app/venv/lib/python3.8/site-packages/pyparsing/exceptions.py deleted file mode 100644 index 8db34f1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/pyparsing/exceptions.py +++ /dev/null @@ -1,301 +0,0 @@ -# exceptions.py - -import re -import sys -import typing - -from .util import ( - col, - line, - lineno, - _collapse_string_to_ranges, - replaced_by_pep8, -) -from .unicode import pyparsing_unicode as ppu - - -class _ExceptionWordUnicodeSet( - ppu.Latin1, ppu.LatinA, ppu.LatinB, ppu.Greek, ppu.Cyrillic -): - pass - - -_extract_alphanums = _collapse_string_to_ranges(_ExceptionWordUnicodeSet.alphanums) -_exception_word_extractor = re.compile("([" + _extract_alphanums + "]{1,16})|.") - - -class ParseBaseException(Exception): - """base exception class for all parsing runtime exceptions""" - - loc: int - msg: str - pstr: str - parser_element: typing.Any # "ParserElement" - args: typing.Tuple[str, int, typing.Optional[str]] - - __slots__ = ( - "loc", - "msg", - "pstr", - "parser_element", - "args", - ) - - # Performance tuning: we construct a *lot* of these, so keep this - # constructor as small and fast as possible - def __init__( - self, - pstr: str, - loc: int = 0, - msg: typing.Optional[str] = None, - elem=None, - ): - self.loc = loc - if msg is None: - self.msg = pstr - self.pstr = "" - else: - self.msg = msg - self.pstr = pstr - self.parser_element = elem - self.args = (pstr, loc, msg) - - @staticmethod - def explain_exception(exc, depth=16): - """ - Method to take an exception and translate the Python internal traceback into a list - of the pyparsing expressions that caused the exception to be raised. - - Parameters: - - - exc - exception raised during parsing (need not be a ParseException, in support - of Python exceptions that might be raised in a parse action) - - depth (default=16) - number of levels back in the stack trace to list expression - and function names; if None, the full stack trace names will be listed; if 0, only - the failing input line, marker, and exception string will be shown - - Returns a multi-line string listing the ParserElements and/or function names in the - exception's stack trace. - """ - import inspect - from .core import ParserElement - - if depth is None: - depth = sys.getrecursionlimit() - ret = [] - if isinstance(exc, ParseBaseException): - ret.append(exc.line) - ret.append(f"{' ' * (exc.column - 1)}^") - ret.append(f"{type(exc).__name__}: {exc}") - - if depth <= 0: - return "\n".join(ret) - - callers = inspect.getinnerframes(exc.__traceback__, context=depth) - seen = set() - for ff in callers[-depth:]: - frm = ff[0] - - f_self = frm.f_locals.get("self", None) - if isinstance(f_self, ParserElement): - if not frm.f_code.co_name.startswith(("parseImpl", "_parseNoCache")): - continue - if id(f_self) in seen: - continue - seen.add(id(f_self)) - - self_type = type(f_self) - ret.append(f"{self_type.__module__}.{self_type.__name__} - {f_self}") - - elif f_self is not None: - self_type = type(f_self) - ret.append(f"{self_type.__module__}.{self_type.__name__}") - - else: - code = frm.f_code - if code.co_name in ("wrapper", ""): - continue - - ret.append(code.co_name) - - depth -= 1 - if not depth: - break - - return "\n".join(ret) - - @classmethod - def _from_exception(cls, pe): - """ - internal factory method to simplify creating one type of ParseException - from another - avoids having __init__ signature conflicts among subclasses - """ - return cls(pe.pstr, pe.loc, pe.msg, pe.parser_element) - - @property - def line(self) -> str: - """ - Return the line of text where the exception occurred. - """ - return line(self.loc, self.pstr) - - @property - def lineno(self) -> int: - """ - Return the 1-based line number of text where the exception occurred. - """ - return lineno(self.loc, self.pstr) - - @property - def col(self) -> int: - """ - Return the 1-based column on the line of text where the exception occurred. - """ - return col(self.loc, self.pstr) - - @property - def column(self) -> int: - """ - Return the 1-based column on the line of text where the exception occurred. - """ - return col(self.loc, self.pstr) - - # pre-PEP8 compatibility - @property - def parserElement(self): - return self.parser_element - - @parserElement.setter - def parserElement(self, elem): - self.parser_element = elem - - def __str__(self) -> str: - if self.pstr: - if self.loc >= len(self.pstr): - foundstr = ", found end of text" - else: - # pull out next word at error location - found_match = _exception_word_extractor.match(self.pstr, self.loc) - if found_match is not None: - found = found_match.group(0) - else: - found = self.pstr[self.loc : self.loc + 1] - foundstr = (", found %r" % found).replace(r"\\", "\\") - else: - foundstr = "" - return f"{self.msg}{foundstr} (at char {self.loc}), (line:{self.lineno}, col:{self.column})" - - def __repr__(self): - return str(self) - - def mark_input_line( - self, marker_string: typing.Optional[str] = None, *, markerString: str = ">!<" - ) -> str: - """ - Extracts the exception line from the input string, and marks - the location of the exception with a special symbol. - """ - markerString = marker_string if marker_string is not None else markerString - line_str = self.line - line_column = self.column - 1 - if markerString: - line_str = "".join( - (line_str[:line_column], markerString, line_str[line_column:]) - ) - return line_str.strip() - - def explain(self, depth=16) -> str: - """ - Method to translate the Python internal traceback into a list - of the pyparsing expressions that caused the exception to be raised. - - Parameters: - - - depth (default=16) - number of levels back in the stack trace to list expression - and function names; if None, the full stack trace names will be listed; if 0, only - the failing input line, marker, and exception string will be shown - - Returns a multi-line string listing the ParserElements and/or function names in the - exception's stack trace. - - Example:: - - # an expression to parse 3 integers - expr = pp.Word(pp.nums) * 3 - try: - # a failing parse - the third integer is prefixed with "A" - expr.parse_string("123 456 A789") - except pp.ParseException as pe: - print(pe.explain(depth=0)) - - prints:: - - 123 456 A789 - ^ - ParseException: Expected W:(0-9), found 'A' (at char 8), (line:1, col:9) - - Note: the diagnostic output will include string representations of the expressions - that failed to parse. These representations will be more helpful if you use `set_name` to - give identifiable names to your expressions. Otherwise they will use the default string - forms, which may be cryptic to read. - - Note: pyparsing's default truncation of exception tracebacks may also truncate the - stack of expressions that are displayed in the ``explain`` output. To get the full listing - of parser expressions, you may have to set ``ParserElement.verbose_stacktrace = True`` - """ - return self.explain_exception(self, depth) - - # Compatibility synonyms - # fmt: off - markInputline = replaced_by_pep8("markInputline", mark_input_line) - # fmt: on - - -class ParseException(ParseBaseException): - """ - Exception thrown when a parse expression doesn't match the input string - - Example:: - - integer = Word(nums).set_name("integer") - try: - integer.parse_string("ABC") - except ParseException as pe: - print(pe) - print(f"column: {pe.column}") - - prints:: - - Expected integer (at char 0), (line:1, col:1) column: 1 - - """ - - -class ParseFatalException(ParseBaseException): - """ - User-throwable exception thrown when inconsistent parse content - is found; stops all parsing immediately - """ - - -class ParseSyntaxException(ParseFatalException): - """ - Just like :class:`ParseFatalException`, but thrown internally - when an :class:`ErrorStop` ('-' operator) indicates - that parsing is to stop immediately because an unbacktrackable - syntax error has been found. - """ - - -class RecursiveGrammarException(Exception): - """ - Exception thrown by :class:`ParserElement.validate` if the - grammar could be left-recursive; parser may need to enable - left recursion using :class:`ParserElement.enable_left_recursion` - """ - - def __init__(self, parseElementList): - self.parseElementTrace = parseElementList - - def __str__(self) -> str: - return f"RecursiveGrammarException: {self.parseElementTrace}" diff --git a/plotter-app/venv/lib/python3.8/site-packages/pyparsing/helpers.py b/plotter-app/venv/lib/python3.8/site-packages/pyparsing/helpers.py deleted file mode 100644 index d5d14a0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/pyparsing/helpers.py +++ /dev/null @@ -1,1082 +0,0 @@ -# helpers.py -import html.entities -import re -import sys -import typing - -from . import __diag__ -from .core import * -from .util import ( - _bslash, - _flatten, - _escape_regex_range_chars, - replaced_by_pep8, -) - - -# -# global helpers -# -def counted_array( - expr: ParserElement, - int_expr: typing.Optional[ParserElement] = None, - *, - intExpr: typing.Optional[ParserElement] = None, -) -> ParserElement: - """Helper to define a counted list of expressions. - - This helper defines a pattern of the form:: - - integer expr expr expr... - - where the leading integer tells how many expr expressions follow. - The matched tokens returns the array of expr tokens as a list - the - leading count token is suppressed. - - If ``int_expr`` is specified, it should be a pyparsing expression - that produces an integer value. - - Example:: - - counted_array(Word(alphas)).parse_string('2 ab cd ef') # -> ['ab', 'cd'] - - # in this parser, the leading integer value is given in binary, - # '10' indicating that 2 values are in the array - binary_constant = Word('01').set_parse_action(lambda t: int(t[0], 2)) - counted_array(Word(alphas), int_expr=binary_constant).parse_string('10 ab cd ef') # -> ['ab', 'cd'] - - # if other fields must be parsed after the count but before the - # list items, give the fields results names and they will - # be preserved in the returned ParseResults: - count_with_metadata = integer + Word(alphas)("type") - typed_array = counted_array(Word(alphanums), int_expr=count_with_metadata)("items") - result = typed_array.parse_string("3 bool True True False") - print(result.dump()) - - # prints - # ['True', 'True', 'False'] - # - items: ['True', 'True', 'False'] - # - type: 'bool' - """ - intExpr = intExpr or int_expr - array_expr = Forward() - - def count_field_parse_action(s, l, t): - nonlocal array_expr - n = t[0] - array_expr <<= (expr * n) if n else Empty() - # clear list contents, but keep any named results - del t[:] - - if intExpr is None: - intExpr = Word(nums).set_parse_action(lambda t: int(t[0])) - else: - intExpr = intExpr.copy() - intExpr.set_name("arrayLen") - intExpr.add_parse_action(count_field_parse_action, call_during_try=True) - return (intExpr + array_expr).set_name(f"(len) {expr}...") - - -def match_previous_literal(expr: ParserElement) -> ParserElement: - """Helper to define an expression that is indirectly defined from - the tokens matched in a previous expression, that is, it looks for - a 'repeat' of a previous expression. For example:: - - first = Word(nums) - second = match_previous_literal(first) - match_expr = first + ":" + second - - will match ``"1:1"``, but not ``"1:2"``. Because this - matches a previous literal, will also match the leading - ``"1:1"`` in ``"1:10"``. If this is not desired, use - :class:`match_previous_expr`. Do *not* use with packrat parsing - enabled. - """ - rep = Forward() - - def copy_token_to_repeater(s, l, t): - if not t: - rep << Empty() - return - - if len(t) == 1: - rep << t[0] - return - - # flatten t tokens - tflat = _flatten(t.as_list()) - rep << And(Literal(tt) for tt in tflat) - - expr.add_parse_action(copy_token_to_repeater, callDuringTry=True) - rep.set_name("(prev) " + str(expr)) - return rep - - -def match_previous_expr(expr: ParserElement) -> ParserElement: - """Helper to define an expression that is indirectly defined from - the tokens matched in a previous expression, that is, it looks for - a 'repeat' of a previous expression. For example:: - - first = Word(nums) - second = match_previous_expr(first) - match_expr = first + ":" + second - - will match ``"1:1"``, but not ``"1:2"``. Because this - matches by expressions, will *not* match the leading ``"1:1"`` - in ``"1:10"``; the expressions are evaluated first, and then - compared, so ``"1"`` is compared with ``"10"``. Do *not* use - with packrat parsing enabled. - """ - rep = Forward() - e2 = expr.copy() - rep <<= e2 - - def copy_token_to_repeater(s, l, t): - matchTokens = _flatten(t.as_list()) - - def must_match_these_tokens(s, l, t): - theseTokens = _flatten(t.as_list()) - if theseTokens != matchTokens: - raise ParseException( - s, l, f"Expected {matchTokens}, found{theseTokens}" - ) - - rep.set_parse_action(must_match_these_tokens, callDuringTry=True) - - expr.add_parse_action(copy_token_to_repeater, callDuringTry=True) - rep.set_name("(prev) " + str(expr)) - return rep - - -def one_of( - strs: Union[typing.Iterable[str], str], - caseless: bool = False, - use_regex: bool = True, - as_keyword: bool = False, - *, - useRegex: bool = True, - asKeyword: bool = False, -) -> ParserElement: - """Helper to quickly define a set of alternative :class:`Literal` s, - and makes sure to do longest-first testing when there is a conflict, - regardless of the input order, but returns - a :class:`MatchFirst` for best performance. - - Parameters: - - - ``strs`` - a string of space-delimited literals, or a collection of - string literals - - ``caseless`` - treat all literals as caseless - (default= ``False``) - - ``use_regex`` - as an optimization, will - generate a :class:`Regex` object; otherwise, will generate - a :class:`MatchFirst` object (if ``caseless=True`` or ``as_keyword=True``, or if - creating a :class:`Regex` raises an exception) - (default= ``True``) - - ``as_keyword`` - enforce :class:`Keyword`-style matching on the - generated expressions - (default= ``False``) - - ``asKeyword`` and ``useRegex`` are retained for pre-PEP8 compatibility, - but will be removed in a future release - - Example:: - - comp_oper = one_of("< = > <= >= !=") - var = Word(alphas) - number = Word(nums) - term = var | number - comparison_expr = term + comp_oper + term - print(comparison_expr.search_string("B = 12 AA=23 B<=AA AA>12")) - - prints:: - - [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']] - """ - asKeyword = asKeyword or as_keyword - useRegex = useRegex and use_regex - - if ( - isinstance(caseless, str_type) - and __diag__.warn_on_multiple_string_args_to_oneof - ): - warnings.warn( - "More than one string argument passed to one_of, pass" - " choices as a list or space-delimited string", - stacklevel=2, - ) - - if caseless: - isequal = lambda a, b: a.upper() == b.upper() - masks = lambda a, b: b.upper().startswith(a.upper()) - parseElementClass = CaselessKeyword if asKeyword else CaselessLiteral - else: - isequal = lambda a, b: a == b - masks = lambda a, b: b.startswith(a) - parseElementClass = Keyword if asKeyword else Literal - - symbols: List[str] = [] - if isinstance(strs, str_type): - strs = typing.cast(str, strs) - symbols = strs.split() - elif isinstance(strs, Iterable): - symbols = list(strs) - else: - raise TypeError("Invalid argument to one_of, expected string or iterable") - if not symbols: - return NoMatch() - - # reorder given symbols to take care to avoid masking longer choices with shorter ones - # (but only if the given symbols are not just single characters) - if any(len(sym) > 1 for sym in symbols): - i = 0 - while i < len(symbols) - 1: - cur = symbols[i] - for j, other in enumerate(symbols[i + 1 :]): - if isequal(other, cur): - del symbols[i + j + 1] - break - if masks(cur, other): - del symbols[i + j + 1] - symbols.insert(i, other) - break - else: - i += 1 - - if useRegex: - re_flags: int = re.IGNORECASE if caseless else 0 - - try: - if all(len(sym) == 1 for sym in symbols): - # symbols are just single characters, create range regex pattern - patt = f"[{''.join(_escape_regex_range_chars(sym) for sym in symbols)}]" - else: - patt = "|".join(re.escape(sym) for sym in symbols) - - # wrap with \b word break markers if defining as keywords - if asKeyword: - patt = rf"\b(?:{patt})\b" - - ret = Regex(patt, flags=re_flags).set_name(" | ".join(symbols)) - - if caseless: - # add parse action to return symbols as specified, not in random - # casing as found in input string - symbol_map = {sym.lower(): sym for sym in symbols} - ret.add_parse_action(lambda s, l, t: symbol_map[t[0].lower()]) - - return ret - - except re.error: - warnings.warn( - "Exception creating Regex for one_of, building MatchFirst", stacklevel=2 - ) - - # last resort, just use MatchFirst - return MatchFirst(parseElementClass(sym) for sym in symbols).set_name( - " | ".join(symbols) - ) - - -def dict_of(key: ParserElement, value: ParserElement) -> ParserElement: - """Helper to easily and clearly define a dictionary by specifying - the respective patterns for the key and value. Takes care of - defining the :class:`Dict`, :class:`ZeroOrMore`, and - :class:`Group` tokens in the proper order. The key pattern - can include delimiting markers or punctuation, as long as they are - suppressed, thereby leaving the significant key text. The value - pattern can include named results, so that the :class:`Dict` results - can include named token fields. - - Example:: - - text = "shape: SQUARE posn: upper left color: light blue texture: burlap" - attr_expr = (label + Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join)) - print(attr_expr[1, ...].parse_string(text).dump()) - - attr_label = label - attr_value = Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join) - - # similar to Dict, but simpler call format - result = dict_of(attr_label, attr_value).parse_string(text) - print(result.dump()) - print(result['shape']) - print(result.shape) # object attribute access works too - print(result.as_dict()) - - prints:: - - [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] - - color: 'light blue' - - posn: 'upper left' - - shape: 'SQUARE' - - texture: 'burlap' - SQUARE - SQUARE - {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'} - """ - return Dict(OneOrMore(Group(key + value))) - - -def original_text_for( - expr: ParserElement, as_string: bool = True, *, asString: bool = True -) -> ParserElement: - """Helper to return the original, untokenized text for a given - expression. Useful to restore the parsed fields of an HTML start - tag into the raw tag text itself, or to revert separate tokens with - intervening whitespace back to the original matching input text. By - default, returns a string containing the original parsed text. - - If the optional ``as_string`` argument is passed as - ``False``, then the return value is - a :class:`ParseResults` containing any results names that - were originally matched, and a single token containing the original - matched text from the input string. So if the expression passed to - :class:`original_text_for` contains expressions with defined - results names, you must set ``as_string`` to ``False`` if you - want to preserve those results name values. - - The ``asString`` pre-PEP8 argument is retained for compatibility, - but will be removed in a future release. - - Example:: - - src = "this is test bold text normal text " - for tag in ("b", "i"): - opener, closer = make_html_tags(tag) - patt = original_text_for(opener + ... + closer) - print(patt.search_string(src)[0]) - - prints:: - - [' bold text '] - ['text'] - """ - asString = asString and as_string - - locMarker = Empty().set_parse_action(lambda s, loc, t: loc) - endlocMarker = locMarker.copy() - endlocMarker.callPreparse = False - matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end") - if asString: - extractText = lambda s, l, t: s[t._original_start : t._original_end] - else: - - def extractText(s, l, t): - t[:] = [s[t.pop("_original_start") : t.pop("_original_end")]] - - matchExpr.set_parse_action(extractText) - matchExpr.ignoreExprs = expr.ignoreExprs - matchExpr.suppress_warning(Diagnostics.warn_ungrouped_named_tokens_in_collection) - return matchExpr - - -def ungroup(expr: ParserElement) -> ParserElement: - """Helper to undo pyparsing's default grouping of And expressions, - even if all but one are non-empty. - """ - return TokenConverter(expr).add_parse_action(lambda t: t[0]) - - -def locatedExpr(expr: ParserElement) -> ParserElement: - """ - (DEPRECATED - future code should use the :class:`Located` class) - Helper to decorate a returned token with its starting and ending - locations in the input string. - - This helper adds the following results names: - - - ``locn_start`` - location where matched expression begins - - ``locn_end`` - location where matched expression ends - - ``value`` - the actual parsed results - - Be careful if the input text contains ```` characters, you - may want to call :class:`ParserElement.parse_with_tabs` - - Example:: - - wd = Word(alphas) - for match in locatedExpr(wd).search_string("ljsdf123lksdjjf123lkkjj1222"): - print(match) - - prints:: - - [[0, 'ljsdf', 5]] - [[8, 'lksdjjf', 15]] - [[18, 'lkkjj', 23]] - """ - locator = Empty().set_parse_action(lambda ss, ll, tt: ll) - return Group( - locator("locn_start") - + expr("value") - + locator.copy().leaveWhitespace()("locn_end") - ) - - -def nested_expr( - opener: Union[str, ParserElement] = "(", - closer: Union[str, ParserElement] = ")", - content: typing.Optional[ParserElement] = None, - ignore_expr: ParserElement = quoted_string(), - *, - ignoreExpr: ParserElement = quoted_string(), -) -> ParserElement: - """Helper method for defining nested lists enclosed in opening and - closing delimiters (``"("`` and ``")"`` are the default). - - Parameters: - - - ``opener`` - opening character for a nested list - (default= ``"("``); can also be a pyparsing expression - - ``closer`` - closing character for a nested list - (default= ``")"``); can also be a pyparsing expression - - ``content`` - expression for items within the nested lists - (default= ``None``) - - ``ignore_expr`` - expression for ignoring opening and closing delimiters - (default= :class:`quoted_string`) - - ``ignoreExpr`` - this pre-PEP8 argument is retained for compatibility - but will be removed in a future release - - If an expression is not provided for the content argument, the - nested expression will capture all whitespace-delimited content - between delimiters as a list of separate values. - - Use the ``ignore_expr`` argument to define expressions that may - contain opening or closing characters that should not be treated as - opening or closing characters for nesting, such as quoted_string or - a comment expression. Specify multiple expressions using an - :class:`Or` or :class:`MatchFirst`. The default is - :class:`quoted_string`, but if no expressions are to be ignored, then - pass ``None`` for this argument. - - Example:: - - data_type = one_of("void int short long char float double") - decl_data_type = Combine(data_type + Opt(Word('*'))) - ident = Word(alphas+'_', alphanums+'_') - number = pyparsing_common.number - arg = Group(decl_data_type + ident) - LPAR, RPAR = map(Suppress, "()") - - code_body = nested_expr('{', '}', ignore_expr=(quoted_string | c_style_comment)) - - c_function = (decl_data_type("type") - + ident("name") - + LPAR + Opt(DelimitedList(arg), [])("args") + RPAR - + code_body("body")) - c_function.ignore(c_style_comment) - - source_code = ''' - int is_odd(int x) { - return (x%2); - } - - int dec_to_hex(char hchar) { - if (hchar >= '0' && hchar <= '9') { - return (ord(hchar)-ord('0')); - } else { - return (10+ord(hchar)-ord('A')); - } - } - ''' - for func in c_function.search_string(source_code): - print("%(name)s (%(type)s) args: %(args)s" % func) - - - prints:: - - is_odd (int) args: [['int', 'x']] - dec_to_hex (int) args: [['char', 'hchar']] - """ - if ignoreExpr != ignore_expr: - ignoreExpr = ignore_expr if ignoreExpr == quoted_string() else ignoreExpr - if opener == closer: - raise ValueError("opening and closing strings cannot be the same") - if content is None: - if isinstance(opener, str_type) and isinstance(closer, str_type): - opener = typing.cast(str, opener) - closer = typing.cast(str, closer) - if len(opener) == 1 and len(closer) == 1: - if ignoreExpr is not None: - content = Combine( - OneOrMore( - ~ignoreExpr - + CharsNotIn( - opener + closer + ParserElement.DEFAULT_WHITE_CHARS, - exact=1, - ) - ) - ).set_parse_action(lambda t: t[0].strip()) - else: - content = empty.copy() + CharsNotIn( - opener + closer + ParserElement.DEFAULT_WHITE_CHARS - ).set_parse_action(lambda t: t[0].strip()) - else: - if ignoreExpr is not None: - content = Combine( - OneOrMore( - ~ignoreExpr - + ~Literal(opener) - + ~Literal(closer) - + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS, exact=1) - ) - ).set_parse_action(lambda t: t[0].strip()) - else: - content = Combine( - OneOrMore( - ~Literal(opener) - + ~Literal(closer) - + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS, exact=1) - ) - ).set_parse_action(lambda t: t[0].strip()) - else: - raise ValueError( - "opening and closing arguments must be strings if no content expression is given" - ) - ret = Forward() - if ignoreExpr is not None: - ret <<= Group( - Suppress(opener) + ZeroOrMore(ignoreExpr | ret | content) + Suppress(closer) - ) - else: - ret <<= Group(Suppress(opener) + ZeroOrMore(ret | content) + Suppress(closer)) - ret.set_name(f"nested {opener}{closer} expression") - # don't override error message from content expressions - ret.errmsg = None - return ret - - -def _makeTags(tagStr, xml, suppress_LT=Suppress("<"), suppress_GT=Suppress(">")): - """Internal helper to construct opening and closing tag expressions, given a tag name""" - if isinstance(tagStr, str_type): - resname = tagStr - tagStr = Keyword(tagStr, caseless=not xml) - else: - resname = tagStr.name - - tagAttrName = Word(alphas, alphanums + "_-:") - if xml: - tagAttrValue = dbl_quoted_string.copy().set_parse_action(remove_quotes) - openTag = ( - suppress_LT - + tagStr("tag") - + Dict(ZeroOrMore(Group(tagAttrName + Suppress("=") + tagAttrValue))) - + Opt("/", default=[False])("empty").set_parse_action( - lambda s, l, t: t[0] == "/" - ) - + suppress_GT - ) - else: - tagAttrValue = quoted_string.copy().set_parse_action(remove_quotes) | Word( - printables, exclude_chars=">" - ) - openTag = ( - suppress_LT - + tagStr("tag") - + Dict( - ZeroOrMore( - Group( - tagAttrName.set_parse_action(lambda t: t[0].lower()) - + Opt(Suppress("=") + tagAttrValue) - ) - ) - ) - + Opt("/", default=[False])("empty").set_parse_action( - lambda s, l, t: t[0] == "/" - ) - + suppress_GT - ) - closeTag = Combine(Literal("", adjacent=False) - - openTag.set_name(f"<{resname}>") - # add start results name in parse action now that ungrouped names are not reported at two levels - openTag.add_parse_action( - lambda t: t.__setitem__( - "start" + "".join(resname.replace(":", " ").title().split()), t.copy() - ) - ) - closeTag = closeTag( - "end" + "".join(resname.replace(":", " ").title().split()) - ).set_name(f"") - openTag.tag = resname - closeTag.tag = resname - openTag.tag_body = SkipTo(closeTag()) - return openTag, closeTag - - -def make_html_tags( - tag_str: Union[str, ParserElement] -) -> Tuple[ParserElement, ParserElement]: - """Helper to construct opening and closing tag expressions for HTML, - given a tag name. Matches tags in either upper or lower case, - attributes with namespaces and with quoted or unquoted values. - - Example:: - - text = 'More info at the pyparsing wiki page' - # make_html_tags returns pyparsing expressions for the opening and - # closing tags as a 2-tuple - a, a_end = make_html_tags("A") - link_expr = a + SkipTo(a_end)("link_text") + a_end - - for link in link_expr.search_string(text): - # attributes in the tag (like "href" shown here) are - # also accessible as named results - print(link.link_text, '->', link.href) - - prints:: - - pyparsing -> https://github.com/pyparsing/pyparsing/wiki - """ - return _makeTags(tag_str, False) - - -def make_xml_tags( - tag_str: Union[str, ParserElement] -) -> Tuple[ParserElement, ParserElement]: - """Helper to construct opening and closing tag expressions for XML, - given a tag name. Matches tags only in the given upper/lower case. - - Example: similar to :class:`make_html_tags` - """ - return _makeTags(tag_str, True) - - -any_open_tag: ParserElement -any_close_tag: ParserElement -any_open_tag, any_close_tag = make_html_tags( - Word(alphas, alphanums + "_:").set_name("any tag") -) - -_htmlEntityMap = {k.rstrip(";"): v for k, v in html.entities.html5.items()} -common_html_entity = Regex("&(?P" + "|".join(_htmlEntityMap) + ");").set_name( - "common HTML entity" -) - - -def replace_html_entity(s, l, t): - """Helper parser action to replace common HTML entities with their special characters""" - return _htmlEntityMap.get(t.entity) - - -class OpAssoc(Enum): - """Enumeration of operator associativity - - used in constructing InfixNotationOperatorSpec for :class:`infix_notation`""" - - LEFT = 1 - RIGHT = 2 - - -InfixNotationOperatorArgType = Union[ - ParserElement, str, Tuple[Union[ParserElement, str], Union[ParserElement, str]] -] -InfixNotationOperatorSpec = Union[ - Tuple[ - InfixNotationOperatorArgType, - int, - OpAssoc, - typing.Optional[ParseAction], - ], - Tuple[ - InfixNotationOperatorArgType, - int, - OpAssoc, - ], -] - - -def infix_notation( - base_expr: ParserElement, - op_list: List[InfixNotationOperatorSpec], - lpar: Union[str, ParserElement] = Suppress("("), - rpar: Union[str, ParserElement] = Suppress(")"), -) -> ParserElement: - """Helper method for constructing grammars of expressions made up of - operators working in a precedence hierarchy. Operators may be unary - or binary, left- or right-associative. Parse actions can also be - attached to operator expressions. The generated parser will also - recognize the use of parentheses to override operator precedences - (see example below). - - Note: if you define a deep operator list, you may see performance - issues when using infix_notation. See - :class:`ParserElement.enable_packrat` for a mechanism to potentially - improve your parser performance. - - Parameters: - - - ``base_expr`` - expression representing the most basic operand to - be used in the expression - - ``op_list`` - list of tuples, one for each operator precedence level - in the expression grammar; each tuple is of the form ``(op_expr, - num_operands, right_left_assoc, (optional)parse_action)``, where: - - - ``op_expr`` is the pyparsing expression for the operator; may also - be a string, which will be converted to a Literal; if ``num_operands`` - is 3, ``op_expr`` is a tuple of two expressions, for the two - operators separating the 3 terms - - ``num_operands`` is the number of terms for this operator (must be 1, - 2, or 3) - - ``right_left_assoc`` is the indicator whether the operator is right - or left associative, using the pyparsing-defined constants - ``OpAssoc.RIGHT`` and ``OpAssoc.LEFT``. - - ``parse_action`` is the parse action to be associated with - expressions matching this operator expression (the parse action - tuple member may be omitted); if the parse action is passed - a tuple or list of functions, this is equivalent to calling - ``set_parse_action(*fn)`` - (:class:`ParserElement.set_parse_action`) - - ``lpar`` - expression for matching left-parentheses; if passed as a - str, then will be parsed as ``Suppress(lpar)``. If lpar is passed as - an expression (such as ``Literal('(')``), then it will be kept in - the parsed results, and grouped with them. (default= ``Suppress('(')``) - - ``rpar`` - expression for matching right-parentheses; if passed as a - str, then will be parsed as ``Suppress(rpar)``. If rpar is passed as - an expression (such as ``Literal(')')``), then it will be kept in - the parsed results, and grouped with them. (default= ``Suppress(')')``) - - Example:: - - # simple example of four-function arithmetic with ints and - # variable names - integer = pyparsing_common.signed_integer - varname = pyparsing_common.identifier - - arith_expr = infix_notation(integer | varname, - [ - ('-', 1, OpAssoc.RIGHT), - (one_of('* /'), 2, OpAssoc.LEFT), - (one_of('+ -'), 2, OpAssoc.LEFT), - ]) - - arith_expr.run_tests(''' - 5+3*6 - (5+3)*6 - -2--11 - ''', full_dump=False) - - prints:: - - 5+3*6 - [[5, '+', [3, '*', 6]]] - - (5+3)*6 - [[[5, '+', 3], '*', 6]] - - (5+x)*y - [[[5, '+', 'x'], '*', 'y']] - - -2--11 - [[['-', 2], '-', ['-', 11]]] - """ - - # captive version of FollowedBy that does not do parse actions or capture results names - class _FB(FollowedBy): - def parseImpl(self, instring, loc, doActions=True): - self.expr.try_parse(instring, loc) - return loc, [] - - _FB.__name__ = "FollowedBy>" - - ret = Forward() - if isinstance(lpar, str): - lpar = Suppress(lpar) - if isinstance(rpar, str): - rpar = Suppress(rpar) - - # if lpar and rpar are not suppressed, wrap in group - if not (isinstance(lpar, Suppress) and isinstance(rpar, Suppress)): - lastExpr = base_expr | Group(lpar + ret + rpar).set_name( - f"nested_{base_expr.name}" - ) - else: - lastExpr = base_expr | (lpar + ret + rpar).set_name(f"nested_{base_expr.name}") - root_expr = lastExpr - - arity: int - rightLeftAssoc: opAssoc - pa: typing.Optional[ParseAction] - opExpr1: ParserElement - opExpr2: ParserElement - for operDef in op_list: - opExpr, arity, rightLeftAssoc, pa = (operDef + (None,))[:4] # type: ignore[assignment] - if isinstance(opExpr, str_type): - opExpr = ParserElement._literalStringClass(opExpr) - opExpr = typing.cast(ParserElement, opExpr) - if arity == 3: - if not isinstance(opExpr, (tuple, list)) or len(opExpr) != 2: - raise ValueError( - "if numterms=3, opExpr must be a tuple or list of two expressions" - ) - opExpr1, opExpr2 = opExpr - term_name = f"{opExpr1}{opExpr2} term" - else: - term_name = f"{opExpr} term" - - if not 1 <= arity <= 3: - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - - if rightLeftAssoc not in (OpAssoc.LEFT, OpAssoc.RIGHT): - raise ValueError("operator must indicate right or left associativity") - - thisExpr: ParserElement = Forward().set_name(term_name) - thisExpr = typing.cast(Forward, thisExpr) - if rightLeftAssoc is OpAssoc.LEFT: - if arity == 1: - matchExpr = _FB(lastExpr + opExpr) + Group(lastExpr + opExpr[1, ...]) - elif arity == 2: - if opExpr is not None: - matchExpr = _FB(lastExpr + opExpr + lastExpr) + Group( - lastExpr + (opExpr + lastExpr)[1, ...] - ) - else: - matchExpr = _FB(lastExpr + lastExpr) + Group(lastExpr[2, ...]) - elif arity == 3: - matchExpr = _FB( - lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr - ) + Group(lastExpr + OneOrMore(opExpr1 + lastExpr + opExpr2 + lastExpr)) - elif rightLeftAssoc is OpAssoc.RIGHT: - if arity == 1: - # try to avoid LR with this extra test - if not isinstance(opExpr, Opt): - opExpr = Opt(opExpr) - matchExpr = _FB(opExpr.expr + thisExpr) + Group(opExpr + thisExpr) - elif arity == 2: - if opExpr is not None: - matchExpr = _FB(lastExpr + opExpr + thisExpr) + Group( - lastExpr + (opExpr + thisExpr)[1, ...] - ) - else: - matchExpr = _FB(lastExpr + thisExpr) + Group( - lastExpr + thisExpr[1, ...] - ) - elif arity == 3: - matchExpr = _FB( - lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr - ) + Group(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) - if pa: - if isinstance(pa, (tuple, list)): - matchExpr.set_parse_action(*pa) - else: - matchExpr.set_parse_action(pa) - thisExpr <<= (matchExpr | lastExpr).setName(term_name) - lastExpr = thisExpr - ret <<= lastExpr - root_expr.set_name("base_expr") - return ret - - -def indentedBlock(blockStatementExpr, indentStack, indent=True, backup_stacks=[]): - """ - (DEPRECATED - use :class:`IndentedBlock` class instead) - Helper method for defining space-delimited indentation blocks, - such as those used to define block statements in Python source code. - - Parameters: - - - ``blockStatementExpr`` - expression defining syntax of statement that - is repeated within the indented block - - ``indentStack`` - list created by caller to manage indentation stack - (multiple ``statementWithIndentedBlock`` expressions within a single - grammar should share a common ``indentStack``) - - ``indent`` - boolean indicating whether block must be indented beyond - the current level; set to ``False`` for block of left-most statements - (default= ``True``) - - A valid block must contain at least one ``blockStatement``. - - (Note that indentedBlock uses internal parse actions which make it - incompatible with packrat parsing.) - - Example:: - - data = ''' - def A(z): - A1 - B = 100 - G = A2 - A2 - A3 - B - def BB(a,b,c): - BB1 - def BBA(): - bba1 - bba2 - bba3 - C - D - def spam(x,y): - def eggs(z): - pass - ''' - - - indentStack = [1] - stmt = Forward() - - identifier = Word(alphas, alphanums) - funcDecl = ("def" + identifier + Group("(" + Opt(delimitedList(identifier)) + ")") + ":") - func_body = indentedBlock(stmt, indentStack) - funcDef = Group(funcDecl + func_body) - - rvalue = Forward() - funcCall = Group(identifier + "(" + Opt(delimitedList(rvalue)) + ")") - rvalue << (funcCall | identifier | Word(nums)) - assignment = Group(identifier + "=" + rvalue) - stmt << (funcDef | assignment | identifier) - - module_body = stmt[1, ...] - - parseTree = module_body.parseString(data) - parseTree.pprint() - - prints:: - - [['def', - 'A', - ['(', 'z', ')'], - ':', - [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], - 'B', - ['def', - 'BB', - ['(', 'a', 'b', 'c', ')'], - ':', - [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], - 'C', - 'D', - ['def', - 'spam', - ['(', 'x', 'y', ')'], - ':', - [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] - """ - backup_stacks.append(indentStack[:]) - - def reset_stack(): - indentStack[:] = backup_stacks[-1] - - def checkPeerIndent(s, l, t): - if l >= len(s): - return - curCol = col(l, s) - if curCol != indentStack[-1]: - if curCol > indentStack[-1]: - raise ParseException(s, l, "illegal nesting") - raise ParseException(s, l, "not a peer entry") - - def checkSubIndent(s, l, t): - curCol = col(l, s) - if curCol > indentStack[-1]: - indentStack.append(curCol) - else: - raise ParseException(s, l, "not a subentry") - - def checkUnindent(s, l, t): - if l >= len(s): - return - curCol = col(l, s) - if not (indentStack and curCol in indentStack): - raise ParseException(s, l, "not an unindent") - if curCol < indentStack[-1]: - indentStack.pop() - - NL = OneOrMore(LineEnd().set_whitespace_chars("\t ").suppress()) - INDENT = (Empty() + Empty().set_parse_action(checkSubIndent)).set_name("INDENT") - PEER = Empty().set_parse_action(checkPeerIndent).set_name("") - UNDENT = Empty().set_parse_action(checkUnindent).set_name("UNINDENT") - if indent: - smExpr = Group( - Opt(NL) - + INDENT - + OneOrMore(PEER + Group(blockStatementExpr) + Opt(NL)) - + UNDENT - ) - else: - smExpr = Group( - Opt(NL) - + OneOrMore(PEER + Group(blockStatementExpr) + Opt(NL)) - + Opt(UNDENT) - ) - - # add a parse action to remove backup_stack from list of backups - smExpr.add_parse_action( - lambda: backup_stacks.pop(-1) and None if backup_stacks else None - ) - smExpr.set_fail_action(lambda a, b, c, d: reset_stack()) - blockStatementExpr.ignore(_bslash + LineEnd()) - return smExpr.set_name("indented block") - - -# it's easy to get these comment structures wrong - they're very common, so may as well make them available -c_style_comment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + "*/").set_name( - "C style comment" -) -"Comment of the form ``/* ... */``" - -html_comment = Regex(r"").set_name("HTML comment") -"Comment of the form ````" - -rest_of_line = Regex(r".*").leave_whitespace().set_name("rest of line") -dbl_slash_comment = Regex(r"//(?:\\\n|[^\n])*").set_name("// comment") -"Comment of the form ``// ... (to end of line)``" - -cpp_style_comment = Combine( - Regex(r"/\*(?:[^*]|\*(?!/))*") + "*/" | dbl_slash_comment -).set_name("C++ style comment") -"Comment of either form :class:`c_style_comment` or :class:`dbl_slash_comment`" - -java_style_comment = cpp_style_comment -"Same as :class:`cpp_style_comment`" - -python_style_comment = Regex(r"#.*").set_name("Python style comment") -"Comment of the form ``# ... (to end of line)``" - - -# build list of built-in expressions, for future reference if a global default value -# gets updated -_builtin_exprs: List[ParserElement] = [ - v for v in vars().values() if isinstance(v, ParserElement) -] - - -# compatibility function, superseded by DelimitedList class -def delimited_list( - expr: Union[str, ParserElement], - delim: Union[str, ParserElement] = ",", - combine: bool = False, - min: typing.Optional[int] = None, - max: typing.Optional[int] = None, - *, - allow_trailing_delim: bool = False, -) -> ParserElement: - """(DEPRECATED - use :class:`DelimitedList` class)""" - return DelimitedList( - expr, delim, combine, min, max, allow_trailing_delim=allow_trailing_delim - ) - - -# Compatibility synonyms -# fmt: off -opAssoc = OpAssoc -anyOpenTag = any_open_tag -anyCloseTag = any_close_tag -commonHTMLEntity = common_html_entity -cStyleComment = c_style_comment -htmlComment = html_comment -restOfLine = rest_of_line -dblSlashComment = dbl_slash_comment -cppStyleComment = cpp_style_comment -javaStyleComment = java_style_comment -pythonStyleComment = python_style_comment -delimitedList = replaced_by_pep8("delimitedList", DelimitedList) -delimited_list = replaced_by_pep8("delimited_list", DelimitedList) -countedArray = replaced_by_pep8("countedArray", counted_array) -matchPreviousLiteral = replaced_by_pep8("matchPreviousLiteral", match_previous_literal) -matchPreviousExpr = replaced_by_pep8("matchPreviousExpr", match_previous_expr) -oneOf = replaced_by_pep8("oneOf", one_of) -dictOf = replaced_by_pep8("dictOf", dict_of) -originalTextFor = replaced_by_pep8("originalTextFor", original_text_for) -nestedExpr = replaced_by_pep8("nestedExpr", nested_expr) -makeHTMLTags = replaced_by_pep8("makeHTMLTags", make_html_tags) -makeXMLTags = replaced_by_pep8("makeXMLTags", make_xml_tags) -replaceHTMLEntity = replaced_by_pep8("replaceHTMLEntity", replace_html_entity) -infixNotation = replaced_by_pep8("infixNotation", infix_notation) -# fmt: on diff --git a/plotter-app/venv/lib/python3.8/site-packages/pyparsing/py.typed b/plotter-app/venv/lib/python3.8/site-packages/pyparsing/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/pyparsing/results.py b/plotter-app/venv/lib/python3.8/site-packages/pyparsing/results.py deleted file mode 100644 index 3bb7c94..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/pyparsing/results.py +++ /dev/null @@ -1,799 +0,0 @@ -# results.py -from collections.abc import ( - MutableMapping, - Mapping, - MutableSequence, - Iterator, - Iterable, -) -import pprint -from typing import Tuple, Any, Dict, Set, List - -from .util import replaced_by_pep8 - - -str_type: Tuple[type, ...] = (str, bytes) -_generator_type = type((_ for _ in ())) - - -class _ParseResultsWithOffset: - tup: Tuple["ParseResults", int] - __slots__ = ["tup"] - - def __init__(self, p1: "ParseResults", p2: int): - self.tup: Tuple[ParseResults, int] = (p1, p2) - - def __getitem__(self, i): - return self.tup[i] - - def __getstate__(self): - return self.tup - - def __setstate__(self, *args): - self.tup = args[0] - - -class ParseResults: - """Structured parse results, to provide multiple means of access to - the parsed data: - - - as a list (``len(results)``) - - by list index (``results[0], results[1]``, etc.) - - by attribute (``results.`` - see :class:`ParserElement.set_results_name`) - - Example:: - - integer = Word(nums) - date_str = (integer.set_results_name("year") + '/' - + integer.set_results_name("month") + '/' - + integer.set_results_name("day")) - # equivalent form: - # date_str = (integer("year") + '/' - # + integer("month") + '/' - # + integer("day")) - - # parse_string returns a ParseResults object - result = date_str.parse_string("1999/12/31") - - def test(s, fn=repr): - print(f"{s} -> {fn(eval(s))}") - test("list(result)") - test("result[0]") - test("result['month']") - test("result.day") - test("'month' in result") - test("'minutes' in result") - test("result.dump()", str) - - prints:: - - list(result) -> ['1999', '/', '12', '/', '31'] - result[0] -> '1999' - result['month'] -> '12' - result.day -> '31' - 'month' in result -> True - 'minutes' in result -> False - result.dump() -> ['1999', '/', '12', '/', '31'] - - day: '31' - - month: '12' - - year: '1999' - """ - - _null_values: Tuple[Any, ...] = (None, [], ()) - - _name: str - _parent: "ParseResults" - _all_names: Set[str] - _modal: bool - _toklist: List[Any] - _tokdict: Dict[str, Any] - - __slots__ = ( - "_name", - "_parent", - "_all_names", - "_modal", - "_toklist", - "_tokdict", - ) - - class List(list): - """ - Simple wrapper class to distinguish parsed list results that should be preserved - as actual Python lists, instead of being converted to :class:`ParseResults`:: - - LBRACK, RBRACK = map(pp.Suppress, "[]") - element = pp.Forward() - item = ppc.integer - element_list = LBRACK + pp.DelimitedList(element) + RBRACK - - # add parse actions to convert from ParseResults to actual Python collection types - def as_python_list(t): - return pp.ParseResults.List(t.as_list()) - element_list.add_parse_action(as_python_list) - - element <<= item | element_list - - element.run_tests(''' - 100 - [2,3,4] - [[2, 1],3,4] - [(2, 1),3,4] - (2,3,4) - ''', post_parse=lambda s, r: (r[0], type(r[0]))) - - prints:: - - 100 - (100, ) - - [2,3,4] - ([2, 3, 4], ) - - [[2, 1],3,4] - ([[2, 1], 3, 4], ) - - (Used internally by :class:`Group` when `aslist=True`.) - """ - - def __new__(cls, contained=None): - if contained is None: - contained = [] - - if not isinstance(contained, list): - raise TypeError( - f"{cls.__name__} may only be constructed with a list, not {type(contained).__name__}" - ) - - return list.__new__(cls) - - def __new__(cls, toklist=None, name=None, **kwargs): - if isinstance(toklist, ParseResults): - return toklist - self = object.__new__(cls) - self._name = None - self._parent = None - self._all_names = set() - - if toklist is None: - self._toklist = [] - elif isinstance(toklist, (list, _generator_type)): - self._toklist = ( - [toklist[:]] - if isinstance(toklist, ParseResults.List) - else list(toklist) - ) - else: - self._toklist = [toklist] - self._tokdict = dict() - return self - - # Performance tuning: we construct a *lot* of these, so keep this - # constructor as small and fast as possible - def __init__( - self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance - ): - self._tokdict: Dict[str, _ParseResultsWithOffset] - self._modal = modal - - if name is None or name == "": - return - - if isinstance(name, int): - name = str(name) - - if not modal: - self._all_names = {name} - - self._name = name - - if toklist in self._null_values: - return - - if isinstance(toklist, (str_type, type)): - toklist = [toklist] - - if asList: - if isinstance(toklist, ParseResults): - self[name] = _ParseResultsWithOffset(ParseResults(toklist._toklist), 0) - else: - self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]), 0) - self[name]._name = name - return - - try: - self[name] = toklist[0] - except (KeyError, TypeError, IndexError): - if toklist is not self: - self[name] = toklist - else: - self._name = name - - def __getitem__(self, i): - if isinstance(i, (int, slice)): - return self._toklist[i] - - if i not in self._all_names: - return self._tokdict[i][-1][0] - - return ParseResults([v[0] for v in self._tokdict[i]]) - - def __setitem__(self, k, v, isinstance=isinstance): - if isinstance(v, _ParseResultsWithOffset): - self._tokdict[k] = self._tokdict.get(k, list()) + [v] - sub = v[0] - elif isinstance(k, (int, slice)): - self._toklist[k] = v - sub = v - else: - self._tokdict[k] = self._tokdict.get(k, list()) + [ - _ParseResultsWithOffset(v, 0) - ] - sub = v - if isinstance(sub, ParseResults): - sub._parent = self - - def __delitem__(self, i): - if not isinstance(i, (int, slice)): - del self._tokdict[i] - return - - mylen = len(self._toklist) - del self._toklist[i] - - # convert int to slice - if isinstance(i, int): - if i < 0: - i += mylen - i = slice(i, i + 1) - # get removed indices - removed = list(range(*i.indices(mylen))) - removed.reverse() - # fixup indices in token dictionary - for occurrences in self._tokdict.values(): - for j in removed: - for k, (value, position) in enumerate(occurrences): - occurrences[k] = _ParseResultsWithOffset( - value, position - (position > j) - ) - - def __contains__(self, k) -> bool: - return k in self._tokdict - - def __len__(self) -> int: - return len(self._toklist) - - def __bool__(self) -> bool: - return not not (self._toklist or self._tokdict) - - def __iter__(self) -> Iterator: - return iter(self._toklist) - - def __reversed__(self) -> Iterator: - return iter(self._toklist[::-1]) - - def keys(self): - return iter(self._tokdict) - - def values(self): - return (self[k] for k in self.keys()) - - def items(self): - return ((k, self[k]) for k in self.keys()) - - def haskeys(self) -> bool: - """ - Since ``keys()`` returns an iterator, this method is helpful in bypassing - code that looks for the existence of any defined results names.""" - return not not self._tokdict - - def pop(self, *args, **kwargs): - """ - Removes and returns item at specified index (default= ``last``). - Supports both ``list`` and ``dict`` semantics for ``pop()``. If - passed no argument or an integer argument, it will use ``list`` - semantics and pop tokens from the list of parsed tokens. If passed - a non-integer argument (most likely a string), it will use ``dict`` - semantics and pop the corresponding value from any defined results - names. A second default return value argument is supported, just as in - ``dict.pop()``. - - Example:: - - numlist = Word(nums)[...] - print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321'] - - def remove_first(tokens): - tokens.pop(0) - numlist.add_parse_action(remove_first) - print(numlist.parse_string("0 123 321")) # -> ['123', '321'] - - label = Word(alphas) - patt = label("LABEL") + Word(nums)[1, ...] - print(patt.parse_string("AAB 123 321").dump()) - - # Use pop() in a parse action to remove named result (note that corresponding value is not - # removed from list form of results) - def remove_LABEL(tokens): - tokens.pop("LABEL") - return tokens - patt.add_parse_action(remove_LABEL) - print(patt.parse_string("AAB 123 321").dump()) - - prints:: - - ['AAB', '123', '321'] - - LABEL: 'AAB' - - ['AAB', '123', '321'] - """ - if not args: - args = [-1] - for k, v in kwargs.items(): - if k == "default": - args = (args[0], v) - else: - raise TypeError(f"pop() got an unexpected keyword argument {k!r}") - if isinstance(args[0], int) or len(args) == 1 or args[0] in self: - index = args[0] - ret = self[index] - del self[index] - return ret - else: - defaultvalue = args[1] - return defaultvalue - - def get(self, key, default_value=None): - """ - Returns named result matching the given key, or if there is no - such name, then returns the given ``default_value`` or ``None`` if no - ``default_value`` is specified. - - Similar to ``dict.get()``. - - Example:: - - integer = Word(nums) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - - result = date_str.parse_string("1999/12/31") - print(result.get("year")) # -> '1999' - print(result.get("hour", "not specified")) # -> 'not specified' - print(result.get("hour")) # -> None - """ - if key in self: - return self[key] - else: - return default_value - - def insert(self, index, ins_string): - """ - Inserts new element at location index in the list of parsed tokens. - - Similar to ``list.insert()``. - - Example:: - - numlist = Word(nums)[...] - print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321'] - - # use a parse action to insert the parse location in the front of the parsed results - def insert_locn(locn, tokens): - tokens.insert(0, locn) - numlist.add_parse_action(insert_locn) - print(numlist.parse_string("0 123 321")) # -> [0, '0', '123', '321'] - """ - self._toklist.insert(index, ins_string) - # fixup indices in token dictionary - for occurrences in self._tokdict.values(): - for k, (value, position) in enumerate(occurrences): - occurrences[k] = _ParseResultsWithOffset( - value, position + (position > index) - ) - - def append(self, item): - """ - Add single element to end of ``ParseResults`` list of elements. - - Example:: - - numlist = Word(nums)[...] - print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321'] - - # use a parse action to compute the sum of the parsed integers, and add it to the end - def append_sum(tokens): - tokens.append(sum(map(int, tokens))) - numlist.add_parse_action(append_sum) - print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321', 444] - """ - self._toklist.append(item) - - def extend(self, itemseq): - """ - Add sequence of elements to end of ``ParseResults`` list of elements. - - Example:: - - patt = Word(alphas)[1, ...] - - # use a parse action to append the reverse of the matched strings, to make a palindrome - def make_palindrome(tokens): - tokens.extend(reversed([t[::-1] for t in tokens])) - return ''.join(tokens) - patt.add_parse_action(make_palindrome) - print(patt.parse_string("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl' - """ - if isinstance(itemseq, ParseResults): - self.__iadd__(itemseq) - else: - self._toklist.extend(itemseq) - - def clear(self): - """ - Clear all elements and results names. - """ - del self._toklist[:] - self._tokdict.clear() - - def __getattr__(self, name): - try: - return self[name] - except KeyError: - if name.startswith("__"): - raise AttributeError(name) - return "" - - def __add__(self, other: "ParseResults") -> "ParseResults": - ret = self.copy() - ret += other - return ret - - def __iadd__(self, other: "ParseResults") -> "ParseResults": - if not other: - return self - - if other._tokdict: - offset = len(self._toklist) - addoffset = lambda a: offset if a < 0 else a + offset - otheritems = other._tokdict.items() - otherdictitems = [ - (k, _ParseResultsWithOffset(v[0], addoffset(v[1]))) - for k, vlist in otheritems - for v in vlist - ] - for k, v in otherdictitems: - self[k] = v - if isinstance(v[0], ParseResults): - v[0]._parent = self - - self._toklist += other._toklist - self._all_names |= other._all_names - return self - - def __radd__(self, other) -> "ParseResults": - if isinstance(other, int) and other == 0: - # useful for merging many ParseResults using sum() builtin - return self.copy() - else: - # this may raise a TypeError - so be it - return other + self - - def __repr__(self) -> str: - return f"{type(self).__name__}({self._toklist!r}, {self.as_dict()})" - - def __str__(self) -> str: - return ( - "[" - + ", ".join( - [ - str(i) if isinstance(i, ParseResults) else repr(i) - for i in self._toklist - ] - ) - + "]" - ) - - def _asStringList(self, sep=""): - out = [] - for item in self._toklist: - if out and sep: - out.append(sep) - if isinstance(item, ParseResults): - out += item._asStringList() - else: - out.append(str(item)) - return out - - def as_list(self) -> list: - """ - Returns the parse results as a nested list of matching tokens, all converted to strings. - - Example:: - - patt = Word(alphas)[1, ...] - result = patt.parse_string("sldkj lsdkj sldkj") - # even though the result prints in string-like form, it is actually a pyparsing ParseResults - print(type(result), result) # -> ['sldkj', 'lsdkj', 'sldkj'] - - # Use as_list() to create an actual list - result_list = result.as_list() - print(type(result_list), result_list) # -> ['sldkj', 'lsdkj', 'sldkj'] - """ - return [ - res.as_list() if isinstance(res, ParseResults) else res - for res in self._toklist - ] - - def as_dict(self) -> dict: - """ - Returns the named parse results as a nested dictionary. - - Example:: - - integer = Word(nums) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - - result = date_str.parse_string('12/31/1999') - print(type(result), repr(result)) # -> (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]}) - - result_dict = result.as_dict() - print(type(result_dict), repr(result_dict)) # -> {'day': '1999', 'year': '12', 'month': '31'} - - # even though a ParseResults supports dict-like access, sometime you just need to have a dict - import json - print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable - print(json.dumps(result.as_dict())) # -> {"month": "31", "day": "1999", "year": "12"} - """ - - def to_item(obj): - if isinstance(obj, ParseResults): - return obj.as_dict() if obj.haskeys() else [to_item(v) for v in obj] - else: - return obj - - return dict((k, to_item(v)) for k, v in self.items()) - - def copy(self) -> "ParseResults": - """ - Returns a new shallow copy of a :class:`ParseResults` object. `ParseResults` - items contained within the source are shared with the copy. Use - :class:`ParseResults.deepcopy()` to create a copy with its own separate - content values. - """ - ret = ParseResults(self._toklist) - ret._tokdict = self._tokdict.copy() - ret._parent = self._parent - ret._all_names |= self._all_names - ret._name = self._name - return ret - - def deepcopy(self) -> "ParseResults": - """ - Returns a new deep copy of a :class:`ParseResults` object. - """ - ret = self.copy() - # replace values with copies if they are of known mutable types - for i, obj in enumerate(self._toklist): - if isinstance(obj, ParseResults): - ret._toklist[i] = obj.deepcopy() - elif isinstance(obj, (str, bytes)): - pass - elif isinstance(obj, MutableMapping): - ret._toklist[i] = dest = type(obj)() - for k, v in obj.items(): - dest[k] = v.deepcopy() if isinstance(v, ParseResults) else v - elif isinstance(obj, Iterable): - ret._toklist[i] = type(obj)( - v.deepcopy() if isinstance(v, ParseResults) else v for v in obj - ) - return ret - - def get_name(self) -> str: - r""" - Returns the results name for this token expression. Useful when several - different expressions might match at a particular location. - - Example:: - - integer = Word(nums) - ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d") - house_number_expr = Suppress('#') + Word(nums, alphanums) - user_data = (Group(house_number_expr)("house_number") - | Group(ssn_expr)("ssn") - | Group(integer)("age")) - user_info = user_data[1, ...] - - result = user_info.parse_string("22 111-22-3333 #221B") - for item in result: - print(item.get_name(), ':', item[0]) - - prints:: - - age : 22 - ssn : 111-22-3333 - house_number : 221B - """ - if self._name: - return self._name - elif self._parent: - par: "ParseResults" = self._parent - parent_tokdict_items = par._tokdict.items() - return next( - ( - k - for k, vlist in parent_tokdict_items - for v, loc in vlist - if v is self - ), - None, - ) - elif ( - len(self) == 1 - and len(self._tokdict) == 1 - and next(iter(self._tokdict.values()))[0][1] in (0, -1) - ): - return next(iter(self._tokdict.keys())) - else: - return None - - def dump(self, indent="", full=True, include_list=True, _depth=0) -> str: - """ - Diagnostic method for listing out the contents of - a :class:`ParseResults`. Accepts an optional ``indent`` argument so - that this string can be embedded in a nested display of other data. - - Example:: - - integer = Word(nums) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - - result = date_str.parse_string('1999/12/31') - print(result.dump()) - - prints:: - - ['1999', '/', '12', '/', '31'] - - day: '31' - - month: '12' - - year: '1999' - """ - out = [] - NL = "\n" - out.append(indent + str(self.as_list()) if include_list else "") - - if not full: - return "".join(out) - - if self.haskeys(): - items = sorted((str(k), v) for k, v in self.items()) - for k, v in items: - if out: - out.append(NL) - out.append(f"{indent}{(' ' * _depth)}- {k}: ") - if not isinstance(v, ParseResults): - out.append(repr(v)) - continue - - if not v: - out.append(str(v)) - continue - - out.append( - v.dump( - indent=indent, - full=full, - include_list=include_list, - _depth=_depth + 1, - ) - ) - if not any(isinstance(vv, ParseResults) for vv in self): - return "".join(out) - - v = self - incr = " " - nl = "\n" - for i, vv in enumerate(v): - if isinstance(vv, ParseResults): - vv_dump = vv.dump( - indent=indent, - full=full, - include_list=include_list, - _depth=_depth + 1, - ) - out.append( - f"{nl}{indent}{incr * _depth}[{i}]:{nl}{indent}{incr * (_depth + 1)}{vv_dump}" - ) - else: - out.append( - f"{nl}{indent}{incr * _depth}[{i}]:{nl}{indent}{incr * (_depth + 1)}{vv}" - ) - - return "".join(out) - - def pprint(self, *args, **kwargs): - """ - Pretty-printer for parsed results as a list, using the - `pprint `_ module. - Accepts additional positional or keyword args as defined for - `pprint.pprint `_ . - - Example:: - - ident = Word(alphas, alphanums) - num = Word(nums) - func = Forward() - term = ident | num | Group('(' + func + ')') - func <<= ident + Group(Optional(DelimitedList(term))) - result = func.parse_string("fna a,b,(fnb c,d,200),100") - result.pprint(width=40) - - prints:: - - ['fna', - ['a', - 'b', - ['(', 'fnb', ['c', 'd', '200'], ')'], - '100']] - """ - pprint.pprint(self.as_list(), *args, **kwargs) - - # add support for pickle protocol - def __getstate__(self): - return ( - self._toklist, - ( - self._tokdict.copy(), - None, - self._all_names, - self._name, - ), - ) - - def __setstate__(self, state): - self._toklist, (self._tokdict, par, inAccumNames, self._name) = state - self._all_names = set(inAccumNames) - self._parent = None - - def __getnewargs__(self): - return self._toklist, self._name - - def __dir__(self): - return dir(type(self)) + list(self.keys()) - - @classmethod - def from_dict(cls, other, name=None) -> "ParseResults": - """ - Helper classmethod to construct a ``ParseResults`` from a ``dict``, preserving the - name-value relations as results names. If an optional ``name`` argument is - given, a nested ``ParseResults`` will be returned. - """ - - def is_iterable(obj): - try: - iter(obj) - except Exception: - return False - # str's are iterable, but in pyparsing, we don't want to iterate over them - else: - return not isinstance(obj, str_type) - - ret = cls([]) - for k, v in other.items(): - if isinstance(v, Mapping): - ret += cls.from_dict(v, name=k) - else: - ret += cls([v], name=k, asList=is_iterable(v)) - if name is not None: - ret = cls([ret], name=name) - return ret - - asList = as_list - """Deprecated - use :class:`as_list`""" - asDict = as_dict - """Deprecated - use :class:`as_dict`""" - getName = get_name - """Deprecated - use :class:`get_name`""" - - -MutableMapping.register(ParseResults) -MutableSequence.register(ParseResults) diff --git a/plotter-app/venv/lib/python3.8/site-packages/pyparsing/testing.py b/plotter-app/venv/lib/python3.8/site-packages/pyparsing/testing.py deleted file mode 100644 index 5654d47..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/pyparsing/testing.py +++ /dev/null @@ -1,345 +0,0 @@ -# testing.py - -from contextlib import contextmanager -import re -import typing - - -from .core import ( - ParserElement, - ParseException, - Keyword, - __diag__, - __compat__, -) - - -class pyparsing_test: - """ - namespace class for classes useful in writing unit tests - """ - - class reset_pyparsing_context: - """ - Context manager to be used when writing unit tests that modify pyparsing config values: - - packrat parsing - - bounded recursion parsing - - default whitespace characters. - - default keyword characters - - literal string auto-conversion class - - __diag__ settings - - Example:: - - with reset_pyparsing_context(): - # test that literals used to construct a grammar are automatically suppressed - ParserElement.inlineLiteralsUsing(Suppress) - - term = Word(alphas) | Word(nums) - group = Group('(' + term[...] + ')') - - # assert that the '()' characters are not included in the parsed tokens - self.assertParseAndCheckList(group, "(abc 123 def)", ['abc', '123', 'def']) - - # after exiting context manager, literals are converted to Literal expressions again - """ - - def __init__(self): - self._save_context = {} - - def save(self): - self._save_context["default_whitespace"] = ParserElement.DEFAULT_WHITE_CHARS - self._save_context["default_keyword_chars"] = Keyword.DEFAULT_KEYWORD_CHARS - - self._save_context["literal_string_class"] = ( - ParserElement._literalStringClass - ) - - self._save_context["verbose_stacktrace"] = ParserElement.verbose_stacktrace - - self._save_context["packrat_enabled"] = ParserElement._packratEnabled - if ParserElement._packratEnabled: - self._save_context["packrat_cache_size"] = ( - ParserElement.packrat_cache.size - ) - else: - self._save_context["packrat_cache_size"] = None - self._save_context["packrat_parse"] = ParserElement._parse - self._save_context["recursion_enabled"] = ( - ParserElement._left_recursion_enabled - ) - - self._save_context["__diag__"] = { - name: getattr(__diag__, name) for name in __diag__._all_names - } - - self._save_context["__compat__"] = { - "collect_all_And_tokens": __compat__.collect_all_And_tokens - } - - return self - - def restore(self): - # reset pyparsing global state - if ( - ParserElement.DEFAULT_WHITE_CHARS - != self._save_context["default_whitespace"] - ): - ParserElement.set_default_whitespace_chars( - self._save_context["default_whitespace"] - ) - - ParserElement.verbose_stacktrace = self._save_context["verbose_stacktrace"] - - Keyword.DEFAULT_KEYWORD_CHARS = self._save_context["default_keyword_chars"] - ParserElement.inlineLiteralsUsing( - self._save_context["literal_string_class"] - ) - - for name, value in self._save_context["__diag__"].items(): - (__diag__.enable if value else __diag__.disable)(name) - - ParserElement._packratEnabled = False - if self._save_context["packrat_enabled"]: - ParserElement.enable_packrat(self._save_context["packrat_cache_size"]) - else: - ParserElement._parse = self._save_context["packrat_parse"] - ParserElement._left_recursion_enabled = self._save_context[ - "recursion_enabled" - ] - - __compat__.collect_all_And_tokens = self._save_context["__compat__"] - - return self - - def copy(self): - ret = type(self)() - ret._save_context.update(self._save_context) - return ret - - def __enter__(self): - return self.save() - - def __exit__(self, *args): - self.restore() - - class TestParseResultsAsserts: - """ - A mixin class to add parse results assertion methods to normal unittest.TestCase classes. - """ - - def assertParseResultsEquals( - self, result, expected_list=None, expected_dict=None, msg=None - ): - """ - Unit test assertion to compare a :class:`ParseResults` object with an optional ``expected_list``, - and compare any defined results names with an optional ``expected_dict``. - """ - if expected_list is not None: - self.assertEqual(expected_list, result.as_list(), msg=msg) - if expected_dict is not None: - self.assertEqual(expected_dict, result.as_dict(), msg=msg) - - def assertParseAndCheckList( - self, expr, test_string, expected_list, msg=None, verbose=True - ): - """ - Convenience wrapper assert to test a parser element and input string, and assert that - the resulting ``ParseResults.asList()`` is equal to the ``expected_list``. - """ - result = expr.parse_string(test_string, parse_all=True) - if verbose: - print(result.dump()) - else: - print(result.as_list()) - self.assertParseResultsEquals(result, expected_list=expected_list, msg=msg) - - def assertParseAndCheckDict( - self, expr, test_string, expected_dict, msg=None, verbose=True - ): - """ - Convenience wrapper assert to test a parser element and input string, and assert that - the resulting ``ParseResults.asDict()`` is equal to the ``expected_dict``. - """ - result = expr.parse_string(test_string, parseAll=True) - if verbose: - print(result.dump()) - else: - print(result.as_list()) - self.assertParseResultsEquals(result, expected_dict=expected_dict, msg=msg) - - def assertRunTestResults( - self, run_tests_report, expected_parse_results=None, msg=None - ): - """ - Unit test assertion to evaluate output of ``ParserElement.runTests()``. If a list of - list-dict tuples is given as the ``expected_parse_results`` argument, then these are zipped - with the report tuples returned by ``runTests`` and evaluated using ``assertParseResultsEquals``. - Finally, asserts that the overall ``runTests()`` success value is ``True``. - - :param run_tests_report: tuple(bool, [tuple(str, ParseResults or Exception)]) returned from runTests - :param expected_parse_results (optional): [tuple(str, list, dict, Exception)] - """ - run_test_success, run_test_results = run_tests_report - - if expected_parse_results is None: - self.assertTrue( - run_test_success, msg=msg if msg is not None else "failed runTests" - ) - return - - merged = [ - (*rpt, expected) - for rpt, expected in zip(run_test_results, expected_parse_results) - ] - for test_string, result, expected in merged: - # expected should be a tuple containing a list and/or a dict or an exception, - # and optional failure message string - # an empty tuple will skip any result validation - fail_msg = next((exp for exp in expected if isinstance(exp, str)), None) - expected_exception = next( - ( - exp - for exp in expected - if isinstance(exp, type) and issubclass(exp, Exception) - ), - None, - ) - if expected_exception is not None: - with self.assertRaises( - expected_exception=expected_exception, msg=fail_msg or msg - ): - if isinstance(result, Exception): - raise result - else: - expected_list = next( - (exp for exp in expected if isinstance(exp, list)), None - ) - expected_dict = next( - (exp for exp in expected if isinstance(exp, dict)), None - ) - if (expected_list, expected_dict) != (None, None): - self.assertParseResultsEquals( - result, - expected_list=expected_list, - expected_dict=expected_dict, - msg=fail_msg or msg, - ) - else: - # warning here maybe? - print(f"no validation for {test_string!r}") - - # do this last, in case some specific test results can be reported instead - self.assertTrue( - run_test_success, msg=msg if msg is not None else "failed runTests" - ) - - @contextmanager - def assertRaisesParseException( - self, exc_type=ParseException, expected_msg=None, msg=None - ): - if expected_msg is not None: - if isinstance(expected_msg, str): - expected_msg = re.escape(expected_msg) - with self.assertRaisesRegex(exc_type, expected_msg, msg=msg) as ctx: - yield ctx - - else: - with self.assertRaises(exc_type, msg=msg) as ctx: - yield ctx - - @staticmethod - def with_line_numbers( - s: str, - start_line: typing.Optional[int] = None, - end_line: typing.Optional[int] = None, - expand_tabs: bool = True, - eol_mark: str = "|", - mark_spaces: typing.Optional[str] = None, - mark_control: typing.Optional[str] = None, - ) -> str: - """ - Helpful method for debugging a parser - prints a string with line and column numbers. - (Line and column numbers are 1-based.) - - :param s: tuple(bool, str - string to be printed with line and column numbers - :param start_line: int - (optional) starting line number in s to print (default=1) - :param end_line: int - (optional) ending line number in s to print (default=len(s)) - :param expand_tabs: bool - (optional) expand tabs to spaces, to match the pyparsing default - :param eol_mark: str - (optional) string to mark the end of lines, helps visualize trailing spaces (default="|") - :param mark_spaces: str - (optional) special character to display in place of spaces - :param mark_control: str - (optional) convert non-printing control characters to a placeholding - character; valid values: - - "unicode" - replaces control chars with Unicode symbols, such as "â" and "âŠ" - - any single character string - replace control characters with given string - - None (default) - string is displayed as-is - - :return: str - input string with leading line numbers and column number headers - """ - if expand_tabs: - s = s.expandtabs() - if mark_control is not None: - mark_control = typing.cast(str, mark_control) - if mark_control == "unicode": - transtable_map = { - c: u for c, u in zip(range(0, 33), range(0x2400, 0x2433)) - } - transtable_map[127] = 0x2421 - tbl = str.maketrans(transtable_map) - eol_mark = "" - else: - ord_mark_control = ord(mark_control) - tbl = str.maketrans( - {c: ord_mark_control for c in list(range(0, 32)) + [127]} - ) - s = s.translate(tbl) - if mark_spaces is not None and mark_spaces != " ": - if mark_spaces == "unicode": - tbl = str.maketrans({9: 0x2409, 32: 0x2423}) - s = s.translate(tbl) - else: - s = s.replace(" ", mark_spaces) - if start_line is None: - start_line = 1 - if end_line is None: - end_line = len(s) - end_line = min(end_line, len(s)) - start_line = min(max(1, start_line), end_line) - - if mark_control != "unicode": - s_lines = s.splitlines()[start_line - 1 : end_line] - else: - s_lines = [line + "âŠ" for line in s.split("âŠ")[start_line - 1 : end_line]] - if not s_lines: - return "" - - lineno_width = len(str(end_line)) - max_line_len = max(len(line) for line in s_lines) - lead = " " * (lineno_width + 1) - if max_line_len >= 99: - header0 = ( - lead - + "".join( - f"{' ' * 99}{(i + 1) % 100}" - for i in range(max(max_line_len // 100, 1)) - ) - + "\n" - ) - else: - header0 = "" - header1 = ( - header0 - + lead - + "".join(f" {(i + 1) % 10}" for i in range(-(-max_line_len // 10))) - + "\n" - ) - header2 = lead + "1234567890" * (-(-max_line_len // 10)) + "\n" - return ( - header1 - + header2 - + "\n".join( - f"{i:{lineno_width}d}:{line}{eol_mark}" - for i, line in enumerate(s_lines, start=start_line) - ) - + "\n" - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/pyparsing/unicode.py b/plotter-app/venv/lib/python3.8/site-packages/pyparsing/unicode.py deleted file mode 100644 index 0e3e065..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/pyparsing/unicode.py +++ /dev/null @@ -1,356 +0,0 @@ -# unicode.py - -import sys -from itertools import filterfalse -from typing import List, Tuple, Union - - -class _lazyclassproperty: - def __init__(self, fn): - self.fn = fn - self.__doc__ = fn.__doc__ - self.__name__ = fn.__name__ - - def __get__(self, obj, cls): - if cls is None: - cls = type(obj) - if not hasattr(cls, "_intern") or any( - cls._intern is getattr(superclass, "_intern", []) - for superclass in cls.__mro__[1:] - ): - cls._intern = {} - attrname = self.fn.__name__ - if attrname not in cls._intern: - cls._intern[attrname] = self.fn(cls) - return cls._intern[attrname] - - -UnicodeRangeList = List[Union[Tuple[int, int], Tuple[int]]] - - -class unicode_set: - """ - A set of Unicode characters, for language-specific strings for - ``alphas``, ``nums``, ``alphanums``, and ``printables``. - A unicode_set is defined by a list of ranges in the Unicode character - set, in a class attribute ``_ranges``. Ranges can be specified using - 2-tuples or a 1-tuple, such as:: - - _ranges = [ - (0x0020, 0x007e), - (0x00a0, 0x00ff), - (0x0100,), - ] - - Ranges are left- and right-inclusive. A 1-tuple of (x,) is treated as (x, x). - - A unicode set can also be defined using multiple inheritance of other unicode sets:: - - class CJK(Chinese, Japanese, Korean): - pass - """ - - _ranges: UnicodeRangeList = [] - - @_lazyclassproperty - def _chars_for_ranges(cls) -> List[str]: - ret: List[int] = [] - for cc in cls.__mro__: - if cc is unicode_set: - break - for rr in getattr(cc, "_ranges", ()): - ret.extend(range(rr[0], rr[-1] + 1)) - return sorted(chr(c) for c in set(ret)) - - @_lazyclassproperty - def printables(cls) -> str: - """all non-whitespace characters in this range""" - return "".join(filterfalse(str.isspace, cls._chars_for_ranges)) - - @_lazyclassproperty - def alphas(cls) -> str: - """all alphabetic characters in this range""" - return "".join(filter(str.isalpha, cls._chars_for_ranges)) - - @_lazyclassproperty - def nums(cls) -> str: - """all numeric digit characters in this range""" - return "".join(filter(str.isdigit, cls._chars_for_ranges)) - - @_lazyclassproperty - def alphanums(cls) -> str: - """all alphanumeric characters in this range""" - return cls.alphas + cls.nums - - @_lazyclassproperty - def identchars(cls) -> str: - """all characters in this range that are valid identifier characters, plus underscore '_'""" - return "".join( - sorted( - set(filter(str.isidentifier, cls._chars_for_ranges)) - | set( - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµº" - "ÀÃÂÃÄÅÆÇÈÉÊËÌÃÃŽÃÃÑÒÓÔÕÖØÙÚÛÜÃÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ" - "_" - ) - ) - ) - - @_lazyclassproperty - def identbodychars(cls) -> str: - """ - all characters in this range that are valid identifier body characters, - plus the digits 0-9, and · (Unicode MIDDLE DOT) - """ - identifier_chars = set( - c for c in cls._chars_for_ranges if ("_" + c).isidentifier() - ) - return "".join( - sorted(identifier_chars | set(cls.identchars) | set("0123456789·")) - ) - - @_lazyclassproperty - def identifier(cls): - """ - a pyparsing Word expression for an identifier using this range's definitions for - identchars and identbodychars - """ - from pyparsing import Word - - return Word(cls.identchars, cls.identbodychars) - - -class pyparsing_unicode(unicode_set): - """ - A namespace class for defining common language unicode_sets. - """ - - # fmt: off - - # define ranges in language character sets - _ranges: UnicodeRangeList = [ - (0x0020, sys.maxunicode), - ] - - class BasicMultilingualPlane(unicode_set): - """Unicode set for the Basic Multilingual Plane""" - _ranges: UnicodeRangeList = [ - (0x0020, 0xFFFF), - ] - - class Latin1(unicode_set): - """Unicode set for Latin-1 Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x0020, 0x007E), - (0x00A0, 0x00FF), - ] - - class LatinA(unicode_set): - """Unicode set for Latin-A Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x0100, 0x017F), - ] - - class LatinB(unicode_set): - """Unicode set for Latin-B Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x0180, 0x024F), - ] - - class Greek(unicode_set): - """Unicode set for Greek Unicode Character Ranges""" - _ranges: UnicodeRangeList = [ - (0x0342, 0x0345), - (0x0370, 0x0377), - (0x037A, 0x037F), - (0x0384, 0x038A), - (0x038C,), - (0x038E, 0x03A1), - (0x03A3, 0x03E1), - (0x03F0, 0x03FF), - (0x1D26, 0x1D2A), - (0x1D5E,), - (0x1D60,), - (0x1D66, 0x1D6A), - (0x1F00, 0x1F15), - (0x1F18, 0x1F1D), - (0x1F20, 0x1F45), - (0x1F48, 0x1F4D), - (0x1F50, 0x1F57), - (0x1F59,), - (0x1F5B,), - (0x1F5D,), - (0x1F5F, 0x1F7D), - (0x1F80, 0x1FB4), - (0x1FB6, 0x1FC4), - (0x1FC6, 0x1FD3), - (0x1FD6, 0x1FDB), - (0x1FDD, 0x1FEF), - (0x1FF2, 0x1FF4), - (0x1FF6, 0x1FFE), - (0x2129,), - (0x2719, 0x271A), - (0xAB65,), - (0x10140, 0x1018D), - (0x101A0,), - (0x1D200, 0x1D245), - (0x1F7A1, 0x1F7A7), - ] - - class Cyrillic(unicode_set): - """Unicode set for Cyrillic Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x0400, 0x052F), - (0x1C80, 0x1C88), - (0x1D2B,), - (0x1D78,), - (0x2DE0, 0x2DFF), - (0xA640, 0xA672), - (0xA674, 0xA69F), - (0xFE2E, 0xFE2F), - ] - - class Chinese(unicode_set): - """Unicode set for Chinese Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x2E80, 0x2E99), - (0x2E9B, 0x2EF3), - (0x31C0, 0x31E3), - (0x3400, 0x4DB5), - (0x4E00, 0x9FEF), - (0xA700, 0xA707), - (0xF900, 0xFA6D), - (0xFA70, 0xFAD9), - (0x16FE2, 0x16FE3), - (0x1F210, 0x1F212), - (0x1F214, 0x1F23B), - (0x1F240, 0x1F248), - (0x20000, 0x2A6D6), - (0x2A700, 0x2B734), - (0x2B740, 0x2B81D), - (0x2B820, 0x2CEA1), - (0x2CEB0, 0x2EBE0), - (0x2F800, 0x2FA1D), - ] - - class Japanese(unicode_set): - """Unicode set for Japanese Unicode Character Range, combining Kanji, Hiragana, and Katakana ranges""" - - class Kanji(unicode_set): - "Unicode set for Kanji Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x4E00, 0x9FBF), - (0x3000, 0x303F), - ] - - class Hiragana(unicode_set): - """Unicode set for Hiragana Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x3041, 0x3096), - (0x3099, 0x30A0), - (0x30FC,), - (0xFF70,), - (0x1B001,), - (0x1B150, 0x1B152), - (0x1F200,), - ] - - class Katakana(unicode_set): - """Unicode set for Katakana Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x3099, 0x309C), - (0x30A0, 0x30FF), - (0x31F0, 0x31FF), - (0x32D0, 0x32FE), - (0xFF65, 0xFF9F), - (0x1B000,), - (0x1B164, 0x1B167), - (0x1F201, 0x1F202), - (0x1F213,), - ] - - 漢字 = Kanji - カタカナ = Katakana - ã²ã‚‰ãŒãª = Hiragana - - _ranges = ( - Kanji._ranges - + Hiragana._ranges - + Katakana._ranges - ) - - class Hangul(unicode_set): - """Unicode set for Hangul (Korean) Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x1100, 0x11FF), - (0x302E, 0x302F), - (0x3131, 0x318E), - (0x3200, 0x321C), - (0x3260, 0x327B), - (0x327E,), - (0xA960, 0xA97C), - (0xAC00, 0xD7A3), - (0xD7B0, 0xD7C6), - (0xD7CB, 0xD7FB), - (0xFFA0, 0xFFBE), - (0xFFC2, 0xFFC7), - (0xFFCA, 0xFFCF), - (0xFFD2, 0xFFD7), - (0xFFDA, 0xFFDC), - ] - - Korean = Hangul - - class CJK(Chinese, Japanese, Hangul): - """Unicode set for combined Chinese, Japanese, and Korean (CJK) Unicode Character Range""" - - class Thai(unicode_set): - """Unicode set for Thai Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x0E01, 0x0E3A), - (0x0E3F, 0x0E5B) - ] - - class Arabic(unicode_set): - """Unicode set for Arabic Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x0600, 0x061B), - (0x061E, 0x06FF), - (0x0700, 0x077F), - ] - - class Hebrew(unicode_set): - """Unicode set for Hebrew Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x0591, 0x05C7), - (0x05D0, 0x05EA), - (0x05EF, 0x05F4), - (0xFB1D, 0xFB36), - (0xFB38, 0xFB3C), - (0xFB3E,), - (0xFB40, 0xFB41), - (0xFB43, 0xFB44), - (0xFB46, 0xFB4F), - ] - - class Devanagari(unicode_set): - """Unicode set for Devanagari Unicode Character Range""" - _ranges: UnicodeRangeList = [ - (0x0900, 0x097F), - (0xA8E0, 0xA8FF) - ] - - BMP = BasicMultilingualPlane - - # add language identifiers using language Unicode - العربية = Arabic - 中文 = Chinese - кириллица = Cyrillic - Ελληνικά = Greek - עִברִית = Hebrew - 日本語 = Japanese - 한국어 = Korean - ไทย = Thai - देवनागरी = Devanagari - - # fmt: on diff --git a/plotter-app/venv/lib/python3.8/site-packages/pyparsing/util.py b/plotter-app/venv/lib/python3.8/site-packages/pyparsing/util.py deleted file mode 100644 index 94837fe..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/pyparsing/util.py +++ /dev/null @@ -1,277 +0,0 @@ -# util.py -import inspect -import warnings -import types -import collections -import itertools -from functools import lru_cache, wraps -from typing import Callable, List, Union, Iterable, TypeVar, cast - -_bslash = chr(92) -C = TypeVar("C", bound=Callable) - - -class __config_flags: - """Internal class for defining compatibility and debugging flags""" - - _all_names: List[str] = [] - _fixed_names: List[str] = [] - _type_desc = "configuration" - - @classmethod - def _set(cls, dname, value): - if dname in cls._fixed_names: - warnings.warn( - f"{cls.__name__}.{dname} {cls._type_desc} is {str(getattr(cls, dname)).upper()}" - f" and cannot be overridden", - stacklevel=3, - ) - return - if dname in cls._all_names: - setattr(cls, dname, value) - else: - raise ValueError(f"no such {cls._type_desc} {dname!r}") - - enable = classmethod(lambda cls, name: cls._set(name, True)) - disable = classmethod(lambda cls, name: cls._set(name, False)) - - -@lru_cache(maxsize=128) -def col(loc: int, strg: str) -> int: - """ - Returns current column within a string, counting newlines as line separators. - The first column is number 1. - - Note: the default parsing behavior is to expand tabs in the input string - before starting the parsing process. See - :class:`ParserElement.parse_string` for more - information on parsing strings containing ```` s, and suggested - methods to maintain a consistent view of the parsed string, the parse - location, and line and column positions within the parsed string. - """ - s = strg - return 1 if 0 < loc < len(s) and s[loc - 1] == "\n" else loc - s.rfind("\n", 0, loc) - - -@lru_cache(maxsize=128) -def lineno(loc: int, strg: str) -> int: - """Returns current line number within a string, counting newlines as line separators. - The first line is number 1. - - Note - the default parsing behavior is to expand tabs in the input string - before starting the parsing process. See :class:`ParserElement.parse_string` - for more information on parsing strings containing ```` s, and - suggested methods to maintain a consistent view of the parsed string, the - parse location, and line and column positions within the parsed string. - """ - return strg.count("\n", 0, loc) + 1 - - -@lru_cache(maxsize=128) -def line(loc: int, strg: str) -> str: - """ - Returns the line of text containing loc within a string, counting newlines as line separators. - """ - last_cr = strg.rfind("\n", 0, loc) - next_cr = strg.find("\n", loc) - return strg[last_cr + 1 : next_cr] if next_cr >= 0 else strg[last_cr + 1 :] - - -class _UnboundedCache: - def __init__(self): - cache = {} - cache_get = cache.get - self.not_in_cache = not_in_cache = object() - - def get(_, key): - return cache_get(key, not_in_cache) - - def set_(_, key, value): - cache[key] = value - - def clear(_): - cache.clear() - - self.size = None - self.get = types.MethodType(get, self) - self.set = types.MethodType(set_, self) - self.clear = types.MethodType(clear, self) - - -class _FifoCache: - def __init__(self, size): - self.not_in_cache = not_in_cache = object() - cache = {} - keyring = [object()] * size - cache_get = cache.get - cache_pop = cache.pop - keyiter = itertools.cycle(range(size)) - - def get(_, key): - return cache_get(key, not_in_cache) - - def set_(_, key, value): - cache[key] = value - i = next(keyiter) - cache_pop(keyring[i], None) - keyring[i] = key - - def clear(_): - cache.clear() - keyring[:] = [object()] * size - - self.size = size - self.get = types.MethodType(get, self) - self.set = types.MethodType(set_, self) - self.clear = types.MethodType(clear, self) - - -class LRUMemo: - """ - A memoizing mapping that retains `capacity` deleted items - - The memo tracks retained items by their access order; once `capacity` items - are retained, the least recently used item is discarded. - """ - - def __init__(self, capacity): - self._capacity = capacity - self._active = {} - self._memory = collections.OrderedDict() - - def __getitem__(self, key): - try: - return self._active[key] - except KeyError: - self._memory.move_to_end(key) - return self._memory[key] - - def __setitem__(self, key, value): - self._memory.pop(key, None) - self._active[key] = value - - def __delitem__(self, key): - try: - value = self._active.pop(key) - except KeyError: - pass - else: - while len(self._memory) >= self._capacity: - self._memory.popitem(last=False) - self._memory[key] = value - - def clear(self): - self._active.clear() - self._memory.clear() - - -class UnboundedMemo(dict): - """ - A memoizing mapping that retains all deleted items - """ - - def __delitem__(self, key): - pass - - -def _escape_regex_range_chars(s: str) -> str: - # escape these chars: ^-[] - for c in r"\^-[]": - s = s.replace(c, _bslash + c) - s = s.replace("\n", r"\n") - s = s.replace("\t", r"\t") - return str(s) - - -def _collapse_string_to_ranges( - s: Union[str, Iterable[str]], re_escape: bool = True -) -> str: - def is_consecutive(c): - c_int = ord(c) - is_consecutive.prev, prev = c_int, is_consecutive.prev - if c_int - prev > 1: - is_consecutive.value = next(is_consecutive.counter) - return is_consecutive.value - - is_consecutive.prev = 0 # type: ignore [attr-defined] - is_consecutive.counter = itertools.count() # type: ignore [attr-defined] - is_consecutive.value = -1 # type: ignore [attr-defined] - - def escape_re_range_char(c): - return "\\" + c if c in r"\^-][" else c - - def no_escape_re_range_char(c): - return c - - if not re_escape: - escape_re_range_char = no_escape_re_range_char - - ret = [] - s = "".join(sorted(set(s))) - if len(s) > 3: - for _, chars in itertools.groupby(s, key=is_consecutive): - first = last = next(chars) - last = collections.deque( - itertools.chain(iter([last]), chars), maxlen=1 - ).pop() - if first == last: - ret.append(escape_re_range_char(first)) - else: - sep = "" if ord(last) == ord(first) + 1 else "-" - ret.append( - f"{escape_re_range_char(first)}{sep}{escape_re_range_char(last)}" - ) - else: - ret = [escape_re_range_char(c) for c in s] - - return "".join(ret) - - -def _flatten(ll: list) -> list: - ret = [] - for i in ll: - if isinstance(i, list): - ret.extend(_flatten(i)) - else: - ret.append(i) - return ret - - -def replaced_by_pep8(compat_name: str, fn: C) -> C: - # In a future version, uncomment the code in the internal _inner() functions - # to begin emitting DeprecationWarnings. - - # Unwrap staticmethod/classmethod - fn = getattr(fn, "__func__", fn) - - # (Presence of 'self' arg in signature is used by explain_exception() methods, so we take - # some extra steps to add it if present in decorated function.) - if ["self"] == list(inspect.signature(fn).parameters)[:1]: - - @wraps(fn) - def _inner(self, *args, **kwargs): - # warnings.warn( - # f"Deprecated - use {fn.__name__}", DeprecationWarning, stacklevel=2 - # ) - return fn(self, *args, **kwargs) - - else: - - @wraps(fn) - def _inner(*args, **kwargs): - # warnings.warn( - # f"Deprecated - use {fn.__name__}", DeprecationWarning, stacklevel=2 - # ) - return fn(*args, **kwargs) - - _inner.__doc__ = f"""Deprecated - use :class:`{fn.__name__}`""" - _inner.__name__ = compat_name - _inner.__annotations__ = fn.__annotations__ - if isinstance(fn, types.FunctionType): - _inner.__kwdefaults__ = fn.__kwdefaults__ - elif isinstance(fn, type) and hasattr(fn, "__init__"): - _inner.__kwdefaults__ = fn.__init__.__kwdefaults__ - else: - _inner.__kwdefaults__ = None - _inner.__qualname__ = fn.__qualname__ - return cast(C, _inner) diff --git a/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/DESCRIPTION.rst b/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/DESCRIPTION.rst deleted file mode 100644 index 606dcaa..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/DESCRIPTION.rst +++ /dev/null @@ -1,13 +0,0 @@ -Python Serial Port Extension for Win32, OSX, Linux, BSD, Jython, IronPython - -Stable: - -- Documentation: http://pythonhosted.org/pyserial/ -- Download Page: https://pypi.python.org/pypi/pyserial - -Latest: - -- Documentation: http://pyserial.readthedocs.io/en/latest/ -- Project Homepage: https://github.com/pyserial/pyserial - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/INSTALLER b/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/METADATA b/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/METADATA deleted file mode 100644 index 363d8e1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/METADATA +++ /dev/null @@ -1,47 +0,0 @@ -Metadata-Version: 2.0 -Name: pyserial -Version: 3.5 -Summary: Python Serial Port Extension -Home-page: https://github.com/pyserial/pyserial -Author: Chris Liechti -Author-email: cliechti@gmx.net -License: BSD -Platform: any -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: Intended Audience :: End Users/Desktop -Classifier: License :: OSI Approved :: BSD License -Classifier: Natural Language :: English -Classifier: Operating System :: POSIX -Classifier: Operating System :: Microsoft :: Windows -Classifier: Operating System :: MacOS :: MacOS X -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.4 -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: Topic :: Communications -Classifier: Topic :: Software Development :: Libraries -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: Terminals :: Serial -Provides-Extra: cp2110 -Provides-Extra: cp2110 -Requires-Dist: hidapi; extra == 'cp2110' - -Python Serial Port Extension for Win32, OSX, Linux, BSD, Jython, IronPython - -Stable: - -- Documentation: http://pythonhosted.org/pyserial/ -- Download Page: https://pypi.python.org/pypi/pyserial - -Latest: - -- Documentation: http://pyserial.readthedocs.io/en/latest/ -- Project Homepage: https://github.com/pyserial/pyserial - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/RECORD b/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/RECORD deleted file mode 100644 index 52d0c65..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/RECORD +++ /dev/null @@ -1,66 +0,0 @@ -../../../bin/pyserial-miniterm,sha256=ChaNSy4-eyBrxvbcxT3O2iJKFWZItOV_frhgPQGIdMk,276 -../../../bin/pyserial-ports,sha256=B1ztFJMeKK1AGRXPBbKJehwMavIuUyKCcm6WBXBTor4,278 -pyserial-3.5.dist-info/DESCRIPTION.rst,sha256=rXXIUFeAsfXq2YS7DGkztGmXez-G7gAwbwdBL8t9KME,320 -pyserial-3.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -pyserial-3.5.dist-info/METADATA,sha256=QqirfpTvC3uqfpTNrGXWuSVMYIR29jASDJkAB79HKUM,1650 -pyserial-3.5.dist-info/RECORD,, -pyserial-3.5.dist-info/WHEEL,sha256=kdsN-5OJAZIiHN-iO4Rhl82KyS0bDWf4uBwMbkNafr8,110 -pyserial-3.5.dist-info/entry_points.txt,sha256=-AQ3oVmIn7rtW5Dh0Oup90Hq0qkIlMj79qGmdDIXk9U,112 -pyserial-3.5.dist-info/metadata.json,sha256=s5rFXxQKL9QXO3UMXRmYoMXGRQt2lol67rf_64S1v10,1647 -pyserial-3.5.dist-info/top_level.txt,sha256=FSjfWHWw-VjPiEqOhttbiP-F8OHn-liixq1wKL2fWOA,7 -serial/__init__.py,sha256=XeyJf970Wg6vY-rNoeAdYuHnVJAwJYSuAjj3U3ZZI0Q,3212 -serial/__main__.py,sha256=oSpVknDS2Yqn2JdXlDs5Fk0E8ccdiLIJaXvPWUizQj0,45 -serial/__pycache__/__init__.cpython-38.pyc,, -serial/__pycache__/__main__.cpython-38.pyc,, -serial/__pycache__/rfc2217.cpython-38.pyc,, -serial/__pycache__/rs485.cpython-38.pyc,, -serial/__pycache__/serialcli.cpython-38.pyc,, -serial/__pycache__/serialjava.cpython-38.pyc,, -serial/__pycache__/serialposix.cpython-38.pyc,, -serial/__pycache__/serialutil.cpython-38.pyc,, -serial/__pycache__/serialwin32.cpython-38.pyc,, -serial/__pycache__/win32.cpython-38.pyc,, -serial/rfc2217.py,sha256=ncG_5Ts42M_Tm_7XN3Q7iE24y-lGcwu2jC3MFSEv6Bc,59700 -serial/rs485.py,sha256=9t6yuGcte36gk8G1U6NgboKVGtJUFqtbpAOXj7vYxM0,3305 -serial/serialcli.py,sha256=u5QnG90UxttqsGG9nYgkj0GUyb0wIOxzlUgxJ4gCczg,9190 -serial/serialjava.py,sha256=AcHLp2D_sAihu7L_wCcg8mtk7etf6zAyB4L_tuthVo8,8480 -serial/serialposix.py,sha256=XVb5hRM5HhdmoYR6BOLhICztQXKhUoA7ocgjoUmptvk,35127 -serial/serialutil.py,sha256=PIT4x8MZ8WGoXW-Ntb7cT2UlVxRYm9y-7m8tr2WhfAo,21797 -serial/serialwin32.py,sha256=F2geqaZQEgxx2xqum4iBMBpA04xHs0uw2gUOP1v7vgA,20284 -serial/threaded/__init__.py,sha256=ikXlKYejRlzzCze9kwxR_uABKa1YfuTyqcPjw3VRV1I,9319 -serial/threaded/__pycache__/__init__.cpython-38.pyc,, -serial/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -serial/tools/__pycache__/__init__.cpython-38.pyc,, -serial/tools/__pycache__/hexlify_codec.cpython-38.pyc,, -serial/tools/__pycache__/list_ports.cpython-38.pyc,, -serial/tools/__pycache__/list_ports_common.cpython-38.pyc,, -serial/tools/__pycache__/list_ports_linux.cpython-38.pyc,, -serial/tools/__pycache__/list_ports_osx.cpython-38.pyc,, -serial/tools/__pycache__/list_ports_posix.cpython-38.pyc,, -serial/tools/__pycache__/list_ports_windows.cpython-38.pyc,, -serial/tools/__pycache__/miniterm.cpython-38.pyc,, -serial/tools/hexlify_codec.py,sha256=FRJSO8pfjM6AR9_SBqL34e50LVkvlzfFKdmCScGn408,3677 -serial/tools/list_ports.py,sha256=eDDoyIhoS3f9D3CVpthqlQUqiR2l-X0VTGGOBjuM4ew,3389 -serial/tools/list_ports_common.py,sha256=x5HIghG4NIz-Xf5iX6Gk7xZfdeads2tqCsfyJhh3Ifs,3736 -serial/tools/list_ports_linux.py,sha256=UnU1VYP1NJI7J8Zn7gY-A2mbi1lugbFZSVztfX8P1pU,4503 -serial/tools/list_ports_osx.py,sha256=eoefMGiuJqC-OCu9aAWqqJX75wGlBzoqZ6kdmMA82LM,11178 -serial/tools/list_ports_posix.py,sha256=EYqD5kRbk0f2a5scaRS4tgWGBynkpVH77ja_G6S3UhE,4535 -serial/tools/list_ports_windows.py,sha256=U4EzcOAiU66LWoPdMXI2oOM4LCd5vKwO3DgeTT6M3qc,16021 -serial/tools/miniterm.py,sha256=fXvkEU9FEyU7HPSNE8bdX2OCgiGYgCee066yF58nots,37840 -serial/urlhandler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -serial/urlhandler/__pycache__/__init__.cpython-38.pyc,, -serial/urlhandler/__pycache__/protocol_alt.cpython-38.pyc,, -serial/urlhandler/__pycache__/protocol_cp2110.cpython-38.pyc,, -serial/urlhandler/__pycache__/protocol_hwgrep.cpython-38.pyc,, -serial/urlhandler/__pycache__/protocol_loop.cpython-38.pyc,, -serial/urlhandler/__pycache__/protocol_rfc2217.cpython-38.pyc,, -serial/urlhandler/__pycache__/protocol_socket.cpython-38.pyc,, -serial/urlhandler/__pycache__/protocol_spy.cpython-38.pyc,, -serial/urlhandler/protocol_alt.py,sha256=-kYoCgy9GyMWN6wC8Oew8FL04LjL4Ntx3HHVqSTKGcQ,2033 -serial/urlhandler/protocol_cp2110.py,sha256=iULOT4Vdw20P_w2jfSWdt0roUY1Ku8xJVHHYc6d3ImY,8540 -serial/urlhandler/protocol_hwgrep.py,sha256=GdKdQ9tExKRHJzsiPcQ9ExmaLa6-A71q52i4jQxmoBk,3159 -serial/urlhandler/protocol_loop.py,sha256=5barru_hfwNkayjjBz4w3snBJn0G7C-fG7-QmHaeTWo,10623 -serial/urlhandler/protocol_rfc2217.py,sha256=IPO8r3pFN6yEDl1Zv2jgUnfIa0tQ0iY0ZsD8_xhAUeQ,317 -serial/urlhandler/protocol_socket.py,sha256=QotaHCPd6t903W_9fa2Lv_5uElq0noir2Ci10a987XM,14299 -serial/urlhandler/protocol_spy.py,sha256=FdUaU43-bl1KXTy_S4HhzyGV5hpUsopm8IqTrX2VX-4,9130 -serial/win32.py,sha256=lk6rod9mHkqzgchmaqB3ygiTkmAWTNQ00IJ985ZjvTI,11138 diff --git a/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/WHEEL b/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/WHEEL deleted file mode 100644 index 7332a41..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.30.0) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/entry_points.txt b/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/entry_points.txt deleted file mode 100644 index b69a613..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/entry_points.txt +++ /dev/null @@ -1,4 +0,0 @@ -[console_scripts] -pyserial-miniterm = serial.tools.miniterm:main -pyserial-ports = serial.tools.list_ports:main - diff --git a/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/metadata.json b/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/metadata.json deleted file mode 100644 index 5064763..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: End Users/Desktop", "License :: OSI Approved :: BSD License", "Natural Language :: English", "Operating System :: POSIX", "Operating System :: Microsoft :: Windows", "Operating System :: MacOS :: MacOS X", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Topic :: Communications", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Terminals :: Serial"], "extensions": {"python.commands": {"wrap_console": {"pyserial-miniterm": "serial.tools.miniterm:main", "pyserial-ports": "serial.tools.list_ports:main"}}, "python.details": {"contacts": [{"email": "cliechti@gmx.net", "name": "Chris Liechti", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/pyserial/pyserial"}}, "python.exports": {"console_scripts": {"pyserial-miniterm": "serial.tools.miniterm:main", "pyserial-ports": "serial.tools.list_ports:main"}}}, "extras": ["cp2110"], "generator": "bdist_wheel (0.30.0)", "license": "BSD", "metadata_version": "2.0", "name": "pyserial", "platform": "any", "run_requires": [{"extra": "cp2110", "requires": ["hidapi"]}], "summary": "Python Serial Port Extension", "version": "3.5"} \ No newline at end of file diff --git a/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/top_level.txt b/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/top_level.txt deleted file mode 100644 index b6be06a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/pyserial-3.5.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -serial diff --git a/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/INSTALLER b/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/LICENSE b/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/LICENSE deleted file mode 100644 index 1e65815..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/LICENSE +++ /dev/null @@ -1,54 +0,0 @@ -Copyright 2017- Paul Ganssle -Copyright 2017- dateutil contributors (see AUTHORS file) - - 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. - -The above license applies to all contributions after 2017-12-01, as well as -all contributions that have been re-licensed (see AUTHORS file for the list of -contributors who have re-licensed their code). --------------------------------------------------------------------------------- -dateutil - Extensions to the standard Python datetime module. - -Copyright (c) 2003-2011 - Gustavo Niemeyer -Copyright (c) 2012-2014 - Tomi Pieviläinen -Copyright (c) 2014-2016 - Yaron de Leeuw -Copyright (c) 2015- - Paul Ganssle -Copyright (c) 2015- - dateutil contributors (see AUTHORS file) - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 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 above BSD License Applies to all code, even that also covered by Apache 2.0. \ No newline at end of file diff --git a/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/METADATA b/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/METADATA deleted file mode 100644 index 577f2bf..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/METADATA +++ /dev/null @@ -1,204 +0,0 @@ -Metadata-Version: 2.1 -Name: python-dateutil -Version: 2.9.0.post0 -Summary: Extensions to the standard Python datetime module -Home-page: https://github.com/dateutil/dateutil -Author: Gustavo Niemeyer -Author-email: gustavo@niemeyer.net -Maintainer: Paul Ganssle -Maintainer-email: dateutil@python.org -License: Dual License -Project-URL: Documentation, https://dateutil.readthedocs.io/en/stable/ -Project-URL: Source, https://github.com/dateutil/dateutil -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: BSD License -Classifier: License :: OSI Approved :: Apache Software License -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 -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.12 -Classifier: Topic :: Software Development :: Libraries -Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,>=2.7 -Description-Content-Type: text/x-rst -License-File: LICENSE -Requires-Dist: six >=1.5 - -dateutil - powerful extensions to datetime -========================================== - -|pypi| |support| |licence| - -|gitter| |readthedocs| - -|travis| |appveyor| |pipelines| |coverage| - -.. |pypi| image:: https://img.shields.io/pypi/v/python-dateutil.svg?style=flat-square - :target: https://pypi.org/project/python-dateutil/ - :alt: pypi version - -.. |support| image:: https://img.shields.io/pypi/pyversions/python-dateutil.svg?style=flat-square - :target: https://pypi.org/project/python-dateutil/ - :alt: supported Python version - -.. |travis| image:: https://img.shields.io/travis/dateutil/dateutil/master.svg?style=flat-square&label=Travis%20Build - :target: https://travis-ci.org/dateutil/dateutil - :alt: travis build status - -.. |appveyor| image:: https://img.shields.io/appveyor/ci/dateutil/dateutil/master.svg?style=flat-square&logo=appveyor - :target: https://ci.appveyor.com/project/dateutil/dateutil - :alt: appveyor build status - -.. |pipelines| image:: https://dev.azure.com/pythondateutilazure/dateutil/_apis/build/status/dateutil.dateutil?branchName=master - :target: https://dev.azure.com/pythondateutilazure/dateutil/_build/latest?definitionId=1&branchName=master - :alt: azure pipelines build status - -.. |coverage| image:: https://codecov.io/gh/dateutil/dateutil/branch/master/graphs/badge.svg?branch=master - :target: https://codecov.io/gh/dateutil/dateutil?branch=master - :alt: Code coverage - -.. |gitter| image:: https://badges.gitter.im/dateutil/dateutil.svg - :alt: Join the chat at https://gitter.im/dateutil/dateutil - :target: https://gitter.im/dateutil/dateutil - -.. |licence| image:: https://img.shields.io/pypi/l/python-dateutil.svg?style=flat-square - :target: https://pypi.org/project/python-dateutil/ - :alt: licence - -.. |readthedocs| image:: https://img.shields.io/readthedocs/dateutil/latest.svg?style=flat-square&label=Read%20the%20Docs - :alt: Read the documentation at https://dateutil.readthedocs.io/en/latest/ - :target: https://dateutil.readthedocs.io/en/latest/ - -The `dateutil` module provides powerful extensions to -the standard `datetime` module, available in Python. - -Installation -============ -`dateutil` can be installed from PyPI using `pip` (note that the package name is -different from the importable name):: - - pip install python-dateutil - -Download -======== -dateutil is available on PyPI -https://pypi.org/project/python-dateutil/ - -The documentation is hosted at: -https://dateutil.readthedocs.io/en/stable/ - -Code -==== -The code and issue tracker are hosted on GitHub: -https://github.com/dateutil/dateutil/ - -Features -======== - -* Computing of relative deltas (next month, next year, - next Monday, last week of month, etc); -* Computing of relative deltas between two given - date and/or datetime objects; -* Computing of dates based on very flexible recurrence rules, - using a superset of the `iCalendar `_ - specification. Parsing of RFC strings is supported as well. -* Generic parsing of dates in almost any string format; -* Timezone (tzinfo) implementations for tzfile(5) format - files (/etc/localtime, /usr/share/zoneinfo, etc), TZ - environment string (in all known formats), iCalendar - format files, given ranges (with help from relative deltas), - local machine timezone, fixed offset timezone, UTC timezone, - and Windows registry-based time zones. -* Internal up-to-date world timezone information based on - Olson's database. -* Computing of Easter Sunday dates for any given year, - using Western, Orthodox or Julian algorithms; -* A comprehensive test suite. - -Quick example -============= -Here's a snapshot, just to give an idea about the power of the -package. For more examples, look at the documentation. - -Suppose you want to know how much time is left, in -years/months/days/etc, before the next easter happening on a -year with a Friday 13th in August, and you want to get today's -date out of the "date" unix system command. Here is the code: - -.. code-block:: python3 - - >>> from dateutil.relativedelta import * - >>> from dateutil.easter import * - >>> from dateutil.rrule import * - >>> from dateutil.parser import * - >>> from datetime import * - >>> now = parse("Sat Oct 11 17:13:46 UTC 2003") - >>> today = now.date() - >>> year = rrule(YEARLY,dtstart=now,bymonth=8,bymonthday=13,byweekday=FR)[0].year - >>> rdelta = relativedelta(easter(year), today) - >>> print("Today is: %s" % today) - Today is: 2003-10-11 - >>> print("Year with next Aug 13th on a Friday is: %s" % year) - Year with next Aug 13th on a Friday is: 2004 - >>> print("How far is the Easter of that year: %s" % rdelta) - How far is the Easter of that year: relativedelta(months=+6) - >>> print("And the Easter of that year is: %s" % (today+rdelta)) - And the Easter of that year is: 2004-04-11 - -Being exactly 6 months ahead was **really** a coincidence :) - -Contributing -============ - -We welcome many types of contributions - bug reports, pull requests (code, infrastructure or documentation fixes). For more information about how to contribute to the project, see the ``CONTRIBUTING.md`` file in the repository. - - -Author -====== -The dateutil module was written by Gustavo Niemeyer -in 2003. - -It is maintained by: - -* Gustavo Niemeyer 2003-2011 -* Tomi Pieviläinen 2012-2014 -* Yaron de Leeuw 2014-2016 -* Paul Ganssle 2015- - -Starting with version 2.4.1 and running until 2.8.2, all source and binary -distributions will be signed by a PGP key that has, at the very least, been -signed by the key which made the previous release. A table of release signing -keys can be found below: - -=========== ============================ -Releases Signing key fingerprint -=========== ============================ -2.4.1-2.8.2 `6B49 ACBA DCF6 BD1C A206 67AB CD54 FCE3 D964 BEFB`_ -=========== ============================ - -New releases *may* have signed tags, but binary and source distributions -uploaded to PyPI will no longer have GPG signatures attached. - -Contact -======= -Our mailing list is available at `dateutil@python.org `_. As it is hosted by the PSF, it is subject to the `PSF code of -conduct `_. - -License -======= - -All contributions after December 1, 2017 released under dual license - either `Apache 2.0 License `_ or the `BSD 3-Clause License `_. Contributions before December 1, 2017 - except those those explicitly relicensed - are released only under the BSD 3-Clause License. - - -.. _6B49 ACBA DCF6 BD1C A206 67AB CD54 FCE3 D964 BEFB: - https://pgp.mit.edu/pks/lookup?op=vindex&search=0xCD54FCE3D964BEFB diff --git a/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/RECORD b/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/RECORD deleted file mode 100644 index 63e5b75..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/RECORD +++ /dev/null @@ -1,44 +0,0 @@ -dateutil/__init__.py,sha256=Mqam67WO9IkTmUFyI66vS6IoSXTp9G388DadH2LCMLY,620 -dateutil/__pycache__/__init__.cpython-38.pyc,, -dateutil/__pycache__/_common.cpython-38.pyc,, -dateutil/__pycache__/_version.cpython-38.pyc,, -dateutil/__pycache__/easter.cpython-38.pyc,, -dateutil/__pycache__/relativedelta.cpython-38.pyc,, -dateutil/__pycache__/rrule.cpython-38.pyc,, -dateutil/__pycache__/tzwin.cpython-38.pyc,, -dateutil/__pycache__/utils.cpython-38.pyc,, -dateutil/_common.py,sha256=77w0yytkrxlYbSn--lDVPUMabUXRR9I3lBv_vQRUqUY,932 -dateutil/_version.py,sha256=BV031OxDDAmy58neUg5yyqLkLaqIw7ibK9As3jiMib0,166 -dateutil/easter.py,sha256=dyBi-lKvimH1u_k6p7Z0JJK72QhqVtVBsqByvpEPKvc,2678 -dateutil/parser/__init__.py,sha256=wWk6GFuxTpjoggCGtgkceJoti4pVjl4_fHQXpNOaSYg,1766 -dateutil/parser/__pycache__/__init__.cpython-38.pyc,, -dateutil/parser/__pycache__/_parser.cpython-38.pyc,, -dateutil/parser/__pycache__/isoparser.cpython-38.pyc,, -dateutil/parser/_parser.py,sha256=7klDdyicksQB_Xgl-3UAmBwzCYor1AIZqklIcT6dH_8,58796 -dateutil/parser/isoparser.py,sha256=8Fy999bnCd1frSdOYuOraWfJTtd5W7qQ51NwNuH_hXM,13233 -dateutil/relativedelta.py,sha256=IY_mglMjoZbYfrvloTY2ce02aiVjPIkiZfqgNTZRfuA,24903 -dateutil/rrule.py,sha256=KJzKlaCd1jEbu4A38ZltslaoAUh9nSbdbOFdjp70Kew,66557 -dateutil/tz/__init__.py,sha256=F-Mz13v6jYseklQf9Te9J6nzcLDmq47gORa61K35_FA,444 -dateutil/tz/__pycache__/__init__.cpython-38.pyc,, -dateutil/tz/__pycache__/_common.cpython-38.pyc,, -dateutil/tz/__pycache__/_factories.cpython-38.pyc,, -dateutil/tz/__pycache__/tz.cpython-38.pyc,, -dateutil/tz/__pycache__/win.cpython-38.pyc,, -dateutil/tz/_common.py,sha256=cgzDTANsOXvEc86cYF77EsliuSab8Puwpsl5-bX3_S4,12977 -dateutil/tz/_factories.py,sha256=unb6XQNXrPMveksTCU-Ag8jmVZs4SojoPUcAHpWnrvU,2569 -dateutil/tz/tz.py,sha256=EUnEdMfeThXiY6l4sh9yBabZ63_POzy01zSsh9thn1o,62855 -dateutil/tz/win.py,sha256=xJszWgSwE1xPx_HJj4ZkepyukC_hNy016WMcXhbRaB8,12935 -dateutil/tzwin.py,sha256=7Ar4vdQCnnM0mKR3MUjbIKsZrBVfHgdwsJZc_mGYRew,59 -dateutil/utils.py,sha256=dKCchEw8eObi0loGTx91unBxm_7UGlU3v_FjFMdqwYM,1965 -dateutil/zoneinfo/__init__.py,sha256=KYg0pthCMjcp5MXSEiBJn3nMjZeNZav7rlJw5-tz1S4,5889 -dateutil/zoneinfo/__pycache__/__init__.cpython-38.pyc,, -dateutil/zoneinfo/__pycache__/rebuild.cpython-38.pyc,, -dateutil/zoneinfo/dateutil-zoneinfo.tar.gz,sha256=0-pS57bpaN4NiE3xKIGTWW-pW4A9tPkqGCeac5gARHU,156400 -dateutil/zoneinfo/rebuild.py,sha256=MiqYzCIHvNbMH-LdRYLv-4T0EIA7hDKt5GLR0IRTLdI,2392 -python_dateutil-2.9.0.post0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -python_dateutil-2.9.0.post0.dist-info/LICENSE,sha256=ugD1Gg2SgjtaHN4n2LW50jIeZ-2NqbwWPv-W1eF-V34,2889 -python_dateutil-2.9.0.post0.dist-info/METADATA,sha256=qdQ22jIr6AgzL5jYgyWZjofLaTpniplp_rTPrXKabpM,8354 -python_dateutil-2.9.0.post0.dist-info/RECORD,, -python_dateutil-2.9.0.post0.dist-info/WHEEL,sha256=-G_t0oGuE7UD0DrSpVZnq1hHMBV9DD2XkS5v7XpmTnk,110 -python_dateutil-2.9.0.post0.dist-info/top_level.txt,sha256=4tjdWkhRZvF7LA_BYe_L9gB2w_p2a-z5y6ArjaRkot8,9 -python_dateutil-2.9.0.post0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 diff --git a/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/WHEEL b/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/WHEEL deleted file mode 100644 index 4724c45..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.42.0) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/top_level.txt b/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/top_level.txt deleted file mode 100644 index 6650148..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -dateutil diff --git a/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/zip-safe b/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/zip-safe deleted file mode 100644 index 8b13789..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/python_dateutil-2.9.0.post0.dist-info/zip-safe +++ /dev/null @@ -1 +0,0 @@ - diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/serial/__init__.py deleted file mode 100644 index caa4de1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/__init__.py +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env python -# -# This is a wrapper module for different platform implementations -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# (C) 2001-2020 Chris Liechti -# -# SPDX-License-Identifier: BSD-3-Clause - -from __future__ import absolute_import - -import sys -import importlib - -from serial.serialutil import * -#~ SerialBase, SerialException, to_bytes, iterbytes - -__version__ = '3.5' - -VERSION = __version__ - -# pylint: disable=wrong-import-position -if sys.platform == 'cli': - from serial.serialcli import Serial -else: - import os - # chose an implementation, depending on os - if os.name == 'nt': # sys.platform == 'win32': - from serial.serialwin32 import Serial - elif os.name == 'posix': - from serial.serialposix import Serial, PosixPollSerial, VTIMESerial # noqa - elif os.name == 'java': - from serial.serialjava import Serial - else: - raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name)) - - -protocol_handler_packages = [ - 'serial.urlhandler', -] - - -def serial_for_url(url, *args, **kwargs): - """\ - Get an instance of the Serial class, depending on port/url. The port is not - opened when the keyword parameter 'do_not_open' is true, by default it - is. All other parameters are directly passed to the __init__ method when - the port is instantiated. - - The list of package names that is searched for protocol handlers is kept in - ``protocol_handler_packages``. - - e.g. we want to support a URL ``foobar://``. A module - ``my_handlers.protocol_foobar`` is provided by the user. Then - ``protocol_handler_packages.append("my_handlers")`` would extend the search - path so that ``serial_for_url("foobar://"))`` would work. - """ - # check and remove extra parameter to not confuse the Serial class - do_open = not kwargs.pop('do_not_open', False) - # the default is to use the native implementation - klass = Serial - try: - url_lowercase = url.lower() - except AttributeError: - # it's not a string, use default - pass - else: - # if it is an URL, try to import the handler module from the list of possible packages - if '://' in url_lowercase: - protocol = url_lowercase.split('://', 1)[0] - module_name = '.protocol_{}'.format(protocol) - for package_name in protocol_handler_packages: - try: - importlib.import_module(package_name) - handler_module = importlib.import_module(module_name, package_name) - except ImportError: - continue - else: - if hasattr(handler_module, 'serial_class_for_url'): - url, klass = handler_module.serial_class_for_url(url) - else: - klass = handler_module.Serial - break - else: - raise ValueError('invalid URL, protocol {!r} not known'.format(protocol)) - # instantiate and open when desired - instance = klass(None, *args, **kwargs) - instance.port = url - if do_open: - instance.open() - return instance diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/__main__.py b/plotter-app/venv/lib/python3.8/site-packages/serial/__main__.py deleted file mode 100644 index bd0a2e6..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/__main__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .tools import miniterm - -miniterm.main() diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index d1821c2..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/__main__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/__main__.cpython-38.pyc deleted file mode 100644 index 39c0220..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/__main__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/rfc2217.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/rfc2217.cpython-38.pyc deleted file mode 100644 index de33145..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/rfc2217.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/rs485.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/rs485.cpython-38.pyc deleted file mode 100644 index 8fcd35f..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/rs485.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/serialcli.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/serialcli.cpython-38.pyc deleted file mode 100644 index e11521d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/serialcli.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/serialjava.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/serialjava.cpython-38.pyc deleted file mode 100644 index 4bd20ce..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/serialjava.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/serialposix.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/serialposix.cpython-38.pyc deleted file mode 100644 index e578a6d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/serialposix.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/serialutil.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/serialutil.cpython-38.pyc deleted file mode 100644 index a08c80a..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/serialutil.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/serialwin32.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/serialwin32.cpython-38.pyc deleted file mode 100644 index 393e1fb..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/serialwin32.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/win32.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/win32.cpython-38.pyc deleted file mode 100644 index 6f53e52..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/__pycache__/win32.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/rfc2217.py b/plotter-app/venv/lib/python3.8/site-packages/serial/rfc2217.py deleted file mode 100644 index 2ae188e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/rfc2217.py +++ /dev/null @@ -1,1351 +0,0 @@ -#! python -# -# This module implements a RFC2217 compatible client. RF2217 descibes a -# protocol to access serial ports over TCP/IP and allows setting the baud rate, -# modem control lines etc. -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# (C) 2001-2015 Chris Liechti -# -# SPDX-License-Identifier: BSD-3-Clause - -# TODO: -# - setting control line -> answer is not checked (had problems with one of the -# severs). consider implementing a compatibility mode flag to make check -# conditional -# - write timeout not implemented at all - -# ########################################################################### -# observations and issues with servers -# =========================================================================== -# sredird V2.2.1 -# - http://www.ibiblio.org/pub/Linux/system/serial/ sredird-2.2.2.tar.gz -# - does not acknowledge SET_CONTROL (RTS/DTR) correctly, always responding -# [105 1] instead of the actual value. -# - SET_BAUDRATE answer contains 4 extra null bytes -> probably for larger -# numbers than 2**32? -# - To get the signature [COM_PORT_OPTION 0] has to be sent. -# - run a server: while true; do nc -l -p 7000 -c "sredird debug /dev/ttyUSB0 /var/lock/sredir"; done -# =========================================================================== -# telnetcpcd (untested) -# - http://ftp.wayne.edu/kermit/sredird/telnetcpcd-1.09.tar.gz -# - To get the signature [COM_PORT_OPTION] w/o data has to be sent. -# =========================================================================== -# ser2net -# - does not negotiate BINARY or COM_PORT_OPTION for his side but at least -# acknowledges that the client activates these options -# - The configuration may be that the server prints a banner. As this client -# implementation does a flushInput on connect, this banner is hidden from -# the user application. -# - NOTIFY_MODEMSTATE: the poll interval of the server seems to be one -# second. -# - To get the signature [COM_PORT_OPTION 0] has to be sent. -# - run a server: run ser2net daemon, in /etc/ser2net.conf: -# 2000:telnet:0:/dev/ttyS0:9600 remctl banner -# ########################################################################### - -# How to identify ports? pySerial might want to support other protocols in the -# future, so lets use an URL scheme. -# for RFC2217 compliant servers we will use this: -# rfc2217://:[?option[&option...]] -# -# options: -# - "logging" set log level print diagnostic messages (e.g. "logging=debug") -# - "ign_set_control": do not look at the answers to SET_CONTROL -# - "poll_modem": issue NOTIFY_MODEMSTATE requests when CTS/DTR/RI/CD is read. -# Without this option it expects that the server sends notifications -# automatically on change (which most servers do and is according to the -# RFC). -# the order of the options is not relevant - -from __future__ import absolute_import - -import logging -import socket -import struct -import threading -import time -try: - import urlparse -except ImportError: - import urllib.parse as urlparse -try: - import Queue -except ImportError: - import queue as Queue - -import serial -from serial.serialutil import SerialBase, SerialException, to_bytes, \ - iterbytes, PortNotOpenError, Timeout - -# port string is expected to be something like this: -# rfc2217://host:port -# host may be an IP or including domain, whatever. -# port is 0...65535 - -# map log level names to constants. used in from_url() -LOGGER_LEVELS = { - 'debug': logging.DEBUG, - 'info': logging.INFO, - 'warning': logging.WARNING, - 'error': logging.ERROR, -} - - -# telnet protocol characters -SE = b'\xf0' # Subnegotiation End -NOP = b'\xf1' # No Operation -DM = b'\xf2' # Data Mark -BRK = b'\xf3' # Break -IP = b'\xf4' # Interrupt process -AO = b'\xf5' # Abort output -AYT = b'\xf6' # Are You There -EC = b'\xf7' # Erase Character -EL = b'\xf8' # Erase Line -GA = b'\xf9' # Go Ahead -SB = b'\xfa' # Subnegotiation Begin -WILL = b'\xfb' -WONT = b'\xfc' -DO = b'\xfd' -DONT = b'\xfe' -IAC = b'\xff' # Interpret As Command -IAC_DOUBLED = b'\xff\xff' - -# selected telnet options -BINARY = b'\x00' # 8-bit data path -ECHO = b'\x01' # echo -SGA = b'\x03' # suppress go ahead - -# RFC2217 -COM_PORT_OPTION = b'\x2c' - -# Client to Access Server -SET_BAUDRATE = b'\x01' -SET_DATASIZE = b'\x02' -SET_PARITY = b'\x03' -SET_STOPSIZE = b'\x04' -SET_CONTROL = b'\x05' -NOTIFY_LINESTATE = b'\x06' -NOTIFY_MODEMSTATE = b'\x07' -FLOWCONTROL_SUSPEND = b'\x08' -FLOWCONTROL_RESUME = b'\x09' -SET_LINESTATE_MASK = b'\x0a' -SET_MODEMSTATE_MASK = b'\x0b' -PURGE_DATA = b'\x0c' - -SERVER_SET_BAUDRATE = b'\x65' -SERVER_SET_DATASIZE = b'\x66' -SERVER_SET_PARITY = b'\x67' -SERVER_SET_STOPSIZE = b'\x68' -SERVER_SET_CONTROL = b'\x69' -SERVER_NOTIFY_LINESTATE = b'\x6a' -SERVER_NOTIFY_MODEMSTATE = b'\x6b' -SERVER_FLOWCONTROL_SUSPEND = b'\x6c' -SERVER_FLOWCONTROL_RESUME = b'\x6d' -SERVER_SET_LINESTATE_MASK = b'\x6e' -SERVER_SET_MODEMSTATE_MASK = b'\x6f' -SERVER_PURGE_DATA = b'\x70' - -RFC2217_ANSWER_MAP = { - SET_BAUDRATE: SERVER_SET_BAUDRATE, - SET_DATASIZE: SERVER_SET_DATASIZE, - SET_PARITY: SERVER_SET_PARITY, - SET_STOPSIZE: SERVER_SET_STOPSIZE, - SET_CONTROL: SERVER_SET_CONTROL, - NOTIFY_LINESTATE: SERVER_NOTIFY_LINESTATE, - NOTIFY_MODEMSTATE: SERVER_NOTIFY_MODEMSTATE, - FLOWCONTROL_SUSPEND: SERVER_FLOWCONTROL_SUSPEND, - FLOWCONTROL_RESUME: SERVER_FLOWCONTROL_RESUME, - SET_LINESTATE_MASK: SERVER_SET_LINESTATE_MASK, - SET_MODEMSTATE_MASK: SERVER_SET_MODEMSTATE_MASK, - PURGE_DATA: SERVER_PURGE_DATA, -} - -SET_CONTROL_REQ_FLOW_SETTING = b'\x00' # Request Com Port Flow Control Setting (outbound/both) -SET_CONTROL_USE_NO_FLOW_CONTROL = b'\x01' # Use No Flow Control (outbound/both) -SET_CONTROL_USE_SW_FLOW_CONTROL = b'\x02' # Use XON/XOFF Flow Control (outbound/both) -SET_CONTROL_USE_HW_FLOW_CONTROL = b'\x03' # Use HARDWARE Flow Control (outbound/both) -SET_CONTROL_REQ_BREAK_STATE = b'\x04' # Request BREAK State -SET_CONTROL_BREAK_ON = b'\x05' # Set BREAK State ON -SET_CONTROL_BREAK_OFF = b'\x06' # Set BREAK State OFF -SET_CONTROL_REQ_DTR = b'\x07' # Request DTR Signal State -SET_CONTROL_DTR_ON = b'\x08' # Set DTR Signal State ON -SET_CONTROL_DTR_OFF = b'\x09' # Set DTR Signal State OFF -SET_CONTROL_REQ_RTS = b'\x0a' # Request RTS Signal State -SET_CONTROL_RTS_ON = b'\x0b' # Set RTS Signal State ON -SET_CONTROL_RTS_OFF = b'\x0c' # Set RTS Signal State OFF -SET_CONTROL_REQ_FLOW_SETTING_IN = b'\x0d' # Request Com Port Flow Control Setting (inbound) -SET_CONTROL_USE_NO_FLOW_CONTROL_IN = b'\x0e' # Use No Flow Control (inbound) -SET_CONTROL_USE_SW_FLOW_CONTOL_IN = b'\x0f' # Use XON/XOFF Flow Control (inbound) -SET_CONTROL_USE_HW_FLOW_CONTOL_IN = b'\x10' # Use HARDWARE Flow Control (inbound) -SET_CONTROL_USE_DCD_FLOW_CONTROL = b'\x11' # Use DCD Flow Control (outbound/both) -SET_CONTROL_USE_DTR_FLOW_CONTROL = b'\x12' # Use DTR Flow Control (inbound) -SET_CONTROL_USE_DSR_FLOW_CONTROL = b'\x13' # Use DSR Flow Control (outbound/both) - -LINESTATE_MASK_TIMEOUT = 128 # Time-out Error -LINESTATE_MASK_SHIFTREG_EMPTY = 64 # Transfer Shift Register Empty -LINESTATE_MASK_TRANSREG_EMPTY = 32 # Transfer Holding Register Empty -LINESTATE_MASK_BREAK_DETECT = 16 # Break-detect Error -LINESTATE_MASK_FRAMING_ERROR = 8 # Framing Error -LINESTATE_MASK_PARTIY_ERROR = 4 # Parity Error -LINESTATE_MASK_OVERRUN_ERROR = 2 # Overrun Error -LINESTATE_MASK_DATA_READY = 1 # Data Ready - -MODEMSTATE_MASK_CD = 128 # Receive Line Signal Detect (also known as Carrier Detect) -MODEMSTATE_MASK_RI = 64 # Ring Indicator -MODEMSTATE_MASK_DSR = 32 # Data-Set-Ready Signal State -MODEMSTATE_MASK_CTS = 16 # Clear-To-Send Signal State -MODEMSTATE_MASK_CD_CHANGE = 8 # Delta Receive Line Signal Detect -MODEMSTATE_MASK_RI_CHANGE = 4 # Trailing-edge Ring Detector -MODEMSTATE_MASK_DSR_CHANGE = 2 # Delta Data-Set-Ready -MODEMSTATE_MASK_CTS_CHANGE = 1 # Delta Clear-To-Send - -PURGE_RECEIVE_BUFFER = b'\x01' # Purge access server receive data buffer -PURGE_TRANSMIT_BUFFER = b'\x02' # Purge access server transmit data buffer -PURGE_BOTH_BUFFERS = b'\x03' # Purge both the access server receive data - # buffer and the access server transmit data buffer - - -RFC2217_PARITY_MAP = { - serial.PARITY_NONE: 1, - serial.PARITY_ODD: 2, - serial.PARITY_EVEN: 3, - serial.PARITY_MARK: 4, - serial.PARITY_SPACE: 5, -} -RFC2217_REVERSE_PARITY_MAP = dict((v, k) for k, v in RFC2217_PARITY_MAP.items()) - -RFC2217_STOPBIT_MAP = { - serial.STOPBITS_ONE: 1, - serial.STOPBITS_ONE_POINT_FIVE: 3, - serial.STOPBITS_TWO: 2, -} -RFC2217_REVERSE_STOPBIT_MAP = dict((v, k) for k, v in RFC2217_STOPBIT_MAP.items()) - -# Telnet filter states -M_NORMAL = 0 -M_IAC_SEEN = 1 -M_NEGOTIATE = 2 - -# TelnetOption and TelnetSubnegotiation states -REQUESTED = 'REQUESTED' -ACTIVE = 'ACTIVE' -INACTIVE = 'INACTIVE' -REALLY_INACTIVE = 'REALLY_INACTIVE' - - -class TelnetOption(object): - """Manage a single telnet option, keeps track of DO/DONT WILL/WONT.""" - - def __init__(self, connection, name, option, send_yes, send_no, ack_yes, - ack_no, initial_state, activation_callback=None): - """\ - Initialize option. - :param connection: connection used to transmit answers - :param name: a readable name for debug outputs - :param send_yes: what to send when option is to be enabled. - :param send_no: what to send when option is to be disabled. - :param ack_yes: what to expect when remote agrees on option. - :param ack_no: what to expect when remote disagrees on option. - :param initial_state: options initialized with REQUESTED are tried to - be enabled on startup. use INACTIVE for all others. - """ - self.connection = connection - self.name = name - self.option = option - self.send_yes = send_yes - self.send_no = send_no - self.ack_yes = ack_yes - self.ack_no = ack_no - self.state = initial_state - self.active = False - self.activation_callback = activation_callback - - def __repr__(self): - """String for debug outputs""" - return "{o.name}:{o.active}({o.state})".format(o=self) - - def process_incoming(self, command): - """\ - A DO/DONT/WILL/WONT was received for this option, update state and - answer when needed. - """ - if command == self.ack_yes: - if self.state is REQUESTED: - self.state = ACTIVE - self.active = True - if self.activation_callback is not None: - self.activation_callback() - elif self.state is ACTIVE: - pass - elif self.state is INACTIVE: - self.state = ACTIVE - self.connection.telnet_send_option(self.send_yes, self.option) - self.active = True - if self.activation_callback is not None: - self.activation_callback() - elif self.state is REALLY_INACTIVE: - self.connection.telnet_send_option(self.send_no, self.option) - else: - raise ValueError('option in illegal state {!r}'.format(self)) - elif command == self.ack_no: - if self.state is REQUESTED: - self.state = INACTIVE - self.active = False - elif self.state is ACTIVE: - self.state = INACTIVE - self.connection.telnet_send_option(self.send_no, self.option) - self.active = False - elif self.state is INACTIVE: - pass - elif self.state is REALLY_INACTIVE: - pass - else: - raise ValueError('option in illegal state {!r}'.format(self)) - - -class TelnetSubnegotiation(object): - """\ - A object to handle subnegotiation of options. In this case actually - sub-sub options for RFC 2217. It is used to track com port options. - """ - - def __init__(self, connection, name, option, ack_option=None): - if ack_option is None: - ack_option = option - self.connection = connection - self.name = name - self.option = option - self.value = None - self.ack_option = ack_option - self.state = INACTIVE - - def __repr__(self): - """String for debug outputs.""" - return "{sn.name}:{sn.state}".format(sn=self) - - def set(self, value): - """\ - Request a change of the value. a request is sent to the server. if - the client needs to know if the change is performed he has to check the - state of this object. - """ - self.value = value - self.state = REQUESTED - self.connection.rfc2217_send_subnegotiation(self.option, self.value) - if self.connection.logger: - self.connection.logger.debug("SB Requesting {} -> {!r}".format(self.name, self.value)) - - def is_ready(self): - """\ - Check if answer from server has been received. when server rejects - the change, raise a ValueError. - """ - if self.state == REALLY_INACTIVE: - raise ValueError("remote rejected value for option {!r}".format(self.name)) - return self.state == ACTIVE - # add property to have a similar interface as TelnetOption - active = property(is_ready) - - def wait(self, timeout=3): - """\ - Wait until the subnegotiation has been acknowledged or timeout. It - can also throw a value error when the answer from the server does not - match the value sent. - """ - timeout_timer = Timeout(timeout) - while not timeout_timer.expired(): - time.sleep(0.05) # prevent 100% CPU load - if self.is_ready(): - break - else: - raise SerialException("timeout while waiting for option {!r}".format(self.name)) - - def check_answer(self, suboption): - """\ - Check an incoming subnegotiation block. The parameter already has - cut off the header like sub option number and com port option value. - """ - if self.value == suboption[:len(self.value)]: - self.state = ACTIVE - else: - # error propagation done in is_ready - self.state = REALLY_INACTIVE - if self.connection.logger: - self.connection.logger.debug("SB Answer {} -> {!r} -> {}".format(self.name, suboption, self.state)) - - -class Serial(SerialBase): - """Serial port implementation for RFC 2217 remote serial ports.""" - - BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200) - - def __init__(self, *args, **kwargs): - self._thread = None - self._socket = None - self._linestate = 0 - self._modemstate = None - self._modemstate_timeout = Timeout(-1) - self._remote_suspend_flow = False - self._write_lock = None - self.logger = None - self._ignore_set_control_answer = False - self._poll_modem_state = False - self._network_timeout = 3 - self._telnet_options = None - self._rfc2217_port_settings = None - self._rfc2217_options = None - self._read_buffer = None - super(Serial, self).__init__(*args, **kwargs) # must be last call in case of auto-open - - def open(self): - """\ - Open port with current settings. This may throw a SerialException - if the port cannot be opened. - """ - self.logger = None - self._ignore_set_control_answer = False - self._poll_modem_state = False - self._network_timeout = 3 - if self._port is None: - raise SerialException("Port must be configured before it can be used.") - if self.is_open: - raise SerialException("Port is already open.") - try: - self._socket = socket.create_connection(self.from_url(self.portstr), timeout=5) # XXX good value? - self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - except Exception as msg: - self._socket = None - raise SerialException("Could not open port {}: {}".format(self.portstr, msg)) - - # use a thread save queue as buffer. it also simplifies implementing - # the read timeout - self._read_buffer = Queue.Queue() - # to ensure that user writes does not interfere with internal - # telnet/rfc2217 options establish a lock - self._write_lock = threading.Lock() - # name the following separately so that, below, a check can be easily done - mandadory_options = [ - TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE), - TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED), - ] - # all supported telnet options - self._telnet_options = [ - TelnetOption(self, 'ECHO', ECHO, DO, DONT, WILL, WONT, REQUESTED), - TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED), - TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, REQUESTED), - TelnetOption(self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, INACTIVE), - TelnetOption(self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, REQUESTED), - ] + mandadory_options - # RFC 2217 specific states - # COM port settings - self._rfc2217_port_settings = { - 'baudrate': TelnetSubnegotiation(self, 'baudrate', SET_BAUDRATE, SERVER_SET_BAUDRATE), - 'datasize': TelnetSubnegotiation(self, 'datasize', SET_DATASIZE, SERVER_SET_DATASIZE), - 'parity': TelnetSubnegotiation(self, 'parity', SET_PARITY, SERVER_SET_PARITY), - 'stopsize': TelnetSubnegotiation(self, 'stopsize', SET_STOPSIZE, SERVER_SET_STOPSIZE), - } - # There are more subnegotiation objects, combine all in one dictionary - # for easy access - self._rfc2217_options = { - 'purge': TelnetSubnegotiation(self, 'purge', PURGE_DATA, SERVER_PURGE_DATA), - 'control': TelnetSubnegotiation(self, 'control', SET_CONTROL, SERVER_SET_CONTROL), - } - self._rfc2217_options.update(self._rfc2217_port_settings) - # cache for line and modem states that the server sends to us - self._linestate = 0 - self._modemstate = None - self._modemstate_timeout = Timeout(-1) - # RFC 2217 flow control between server and client - self._remote_suspend_flow = False - - self.is_open = True - self._thread = threading.Thread(target=self._telnet_read_loop) - self._thread.setDaemon(True) - self._thread.setName('pySerial RFC 2217 reader thread for {}'.format(self._port)) - self._thread.start() - - try: # must clean-up if open fails - # negotiate Telnet/RFC 2217 -> send initial requests - for option in self._telnet_options: - if option.state is REQUESTED: - self.telnet_send_option(option.send_yes, option.option) - # now wait until important options are negotiated - timeout = Timeout(self._network_timeout) - while not timeout.expired(): - time.sleep(0.05) # prevent 100% CPU load - if sum(o.active for o in mandadory_options) == sum(o.state != INACTIVE for o in mandadory_options): - break - else: - raise SerialException( - "Remote does not seem to support RFC2217 or BINARY mode {!r}".format(mandadory_options)) - if self.logger: - self.logger.info("Negotiated options: {}".format(self._telnet_options)) - - # fine, go on, set RFC 2217 specific things - self._reconfigure_port() - # all things set up get, now a clean start - if not self._dsrdtr: - self._update_dtr_state() - if not self._rtscts: - self._update_rts_state() - self.reset_input_buffer() - self.reset_output_buffer() - except: - self.close() - raise - - def _reconfigure_port(self): - """Set communication parameters on opened port.""" - if self._socket is None: - raise SerialException("Can only operate on open ports") - - # if self._timeout != 0 and self._interCharTimeout is not None: - # XXX - - if self._write_timeout is not None: - raise NotImplementedError('write_timeout is currently not supported') - # XXX - - # Setup the connection - # to get good performance, all parameter changes are sent first... - if not 0 < self._baudrate < 2 ** 32: - raise ValueError("invalid baudrate: {!r}".format(self._baudrate)) - self._rfc2217_port_settings['baudrate'].set(struct.pack(b'!I', self._baudrate)) - self._rfc2217_port_settings['datasize'].set(struct.pack(b'!B', self._bytesize)) - self._rfc2217_port_settings['parity'].set(struct.pack(b'!B', RFC2217_PARITY_MAP[self._parity])) - self._rfc2217_port_settings['stopsize'].set(struct.pack(b'!B', RFC2217_STOPBIT_MAP[self._stopbits])) - - # and now wait until parameters are active - items = self._rfc2217_port_settings.values() - if self.logger: - self.logger.debug("Negotiating settings: {}".format(items)) - timeout = Timeout(self._network_timeout) - while not timeout.expired(): - time.sleep(0.05) # prevent 100% CPU load - if sum(o.active for o in items) == len(items): - break - else: - raise SerialException("Remote does not accept parameter change (RFC2217): {!r}".format(items)) - if self.logger: - self.logger.info("Negotiated settings: {}".format(items)) - - if self._rtscts and self._xonxoff: - raise ValueError('xonxoff and rtscts together are not supported') - elif self._rtscts: - self.rfc2217_set_control(SET_CONTROL_USE_HW_FLOW_CONTROL) - elif self._xonxoff: - self.rfc2217_set_control(SET_CONTROL_USE_SW_FLOW_CONTROL) - else: - self.rfc2217_set_control(SET_CONTROL_USE_NO_FLOW_CONTROL) - - def close(self): - """Close port""" - self.is_open = False - if self._socket: - try: - self._socket.shutdown(socket.SHUT_RDWR) - self._socket.close() - except: - # ignore errors. - pass - if self._thread: - self._thread.join(7) # XXX more than socket timeout - self._thread = None - # in case of quick reconnects, give the server some time - time.sleep(0.3) - self._socket = None - - def from_url(self, url): - """\ - extract host and port from an URL string, other settings are extracted - an stored in instance - """ - parts = urlparse.urlsplit(url) - if parts.scheme != "rfc2217": - raise SerialException( - 'expected a string in the form ' - '"rfc2217://:[?option[&option...]]": ' - 'not starting with rfc2217:// ({!r})'.format(parts.scheme)) - try: - # process options now, directly altering self - for option, values in urlparse.parse_qs(parts.query, True).items(): - if option == 'logging': - logging.basicConfig() # XXX is that good to call it here? - self.logger = logging.getLogger('pySerial.rfc2217') - self.logger.setLevel(LOGGER_LEVELS[values[0]]) - self.logger.debug('enabled logging') - elif option == 'ign_set_control': - self._ignore_set_control_answer = True - elif option == 'poll_modem': - self._poll_modem_state = True - elif option == 'timeout': - self._network_timeout = float(values[0]) - else: - raise ValueError('unknown option: {!r}'.format(option)) - if not 0 <= parts.port < 65536: - raise ValueError("port not in range 0...65535") - except ValueError as e: - raise SerialException( - 'expected a string in the form ' - '"rfc2217://:[?option[&option...]]": {}'.format(e)) - return (parts.hostname, parts.port) - - # - - - - - - - - - - - - - - - - - - - - - - - - - - @property - def in_waiting(self): - """Return the number of bytes currently in the input buffer.""" - if not self.is_open: - raise PortNotOpenError() - return self._read_buffer.qsize() - - def read(self, size=1): - """\ - Read size bytes from the serial port. If a timeout is set it may - return less characters as requested. With no timeout it will block - until the requested number of bytes is read. - """ - if not self.is_open: - raise PortNotOpenError() - data = bytearray() - try: - timeout = Timeout(self._timeout) - while len(data) < size: - if self._thread is None or not self._thread.is_alive(): - raise SerialException('connection failed (reader thread died)') - buf = self._read_buffer.get(True, timeout.time_left()) - if buf is None: - return bytes(data) - data += buf - if timeout.expired(): - break - except Queue.Empty: # -> timeout - pass - return bytes(data) - - def write(self, data): - """\ - Output the given byte string over the serial port. Can block if the - connection is blocked. May raise SerialException if the connection is - closed. - """ - if not self.is_open: - raise PortNotOpenError() - with self._write_lock: - try: - self._socket.sendall(to_bytes(data).replace(IAC, IAC_DOUBLED)) - except socket.error as e: - raise SerialException("connection failed (socket error): {}".format(e)) - return len(data) - - def reset_input_buffer(self): - """Clear input buffer, discarding all that is in the buffer.""" - if not self.is_open: - raise PortNotOpenError() - self.rfc2217_send_purge(PURGE_RECEIVE_BUFFER) - # empty read buffer - while self._read_buffer.qsize(): - self._read_buffer.get(False) - - def reset_output_buffer(self): - """\ - Clear output buffer, aborting the current output and - discarding all that is in the buffer. - """ - if not self.is_open: - raise PortNotOpenError() - self.rfc2217_send_purge(PURGE_TRANSMIT_BUFFER) - - def _update_break_state(self): - """\ - Set break: Controls TXD. When active, to transmitting is - possible. - """ - if not self.is_open: - raise PortNotOpenError() - if self.logger: - self.logger.info('set BREAK to {}'.format('active' if self._break_state else 'inactive')) - if self._break_state: - self.rfc2217_set_control(SET_CONTROL_BREAK_ON) - else: - self.rfc2217_set_control(SET_CONTROL_BREAK_OFF) - - def _update_rts_state(self): - """Set terminal status line: Request To Send.""" - if not self.is_open: - raise PortNotOpenError() - if self.logger: - self.logger.info('set RTS to {}'.format('active' if self._rts_state else 'inactive')) - if self._rts_state: - self.rfc2217_set_control(SET_CONTROL_RTS_ON) - else: - self.rfc2217_set_control(SET_CONTROL_RTS_OFF) - - def _update_dtr_state(self): - """Set terminal status line: Data Terminal Ready.""" - if not self.is_open: - raise PortNotOpenError() - if self.logger: - self.logger.info('set DTR to {}'.format('active' if self._dtr_state else 'inactive')) - if self._dtr_state: - self.rfc2217_set_control(SET_CONTROL_DTR_ON) - else: - self.rfc2217_set_control(SET_CONTROL_DTR_OFF) - - @property - def cts(self): - """Read terminal status line: Clear To Send.""" - if not self.is_open: - raise PortNotOpenError() - return bool(self.get_modem_state() & MODEMSTATE_MASK_CTS) - - @property - def dsr(self): - """Read terminal status line: Data Set Ready.""" - if not self.is_open: - raise PortNotOpenError() - return bool(self.get_modem_state() & MODEMSTATE_MASK_DSR) - - @property - def ri(self): - """Read terminal status line: Ring Indicator.""" - if not self.is_open: - raise PortNotOpenError() - return bool(self.get_modem_state() & MODEMSTATE_MASK_RI) - - @property - def cd(self): - """Read terminal status line: Carrier Detect.""" - if not self.is_open: - raise PortNotOpenError() - return bool(self.get_modem_state() & MODEMSTATE_MASK_CD) - - # - - - platform specific - - - - # None so far - - # - - - RFC2217 specific - - - - - def _telnet_read_loop(self): - """Read loop for the socket.""" - mode = M_NORMAL - suboption = None - try: - while self.is_open: - try: - data = self._socket.recv(1024) - except socket.timeout: - # just need to get out of recv form time to time to check if - # still alive - continue - except socket.error as e: - # connection fails -> terminate loop - if self.logger: - self.logger.debug("socket error in reader thread: {}".format(e)) - self._read_buffer.put(None) - break - if not data: - self._read_buffer.put(None) - break # lost connection - for byte in iterbytes(data): - if mode == M_NORMAL: - # interpret as command or as data - if byte == IAC: - mode = M_IAC_SEEN - else: - # store data in read buffer or sub option buffer - # depending on state - if suboption is not None: - suboption += byte - else: - self._read_buffer.put(byte) - elif mode == M_IAC_SEEN: - if byte == IAC: - # interpret as command doubled -> insert character - # itself - if suboption is not None: - suboption += IAC - else: - self._read_buffer.put(IAC) - mode = M_NORMAL - elif byte == SB: - # sub option start - suboption = bytearray() - mode = M_NORMAL - elif byte == SE: - # sub option end -> process it now - self._telnet_process_subnegotiation(bytes(suboption)) - suboption = None - mode = M_NORMAL - elif byte in (DO, DONT, WILL, WONT): - # negotiation - telnet_command = byte - mode = M_NEGOTIATE - else: - # other telnet commands - self._telnet_process_command(byte) - mode = M_NORMAL - elif mode == M_NEGOTIATE: # DO, DONT, WILL, WONT was received, option now following - self._telnet_negotiate_option(telnet_command, byte) - mode = M_NORMAL - finally: - if self.logger: - self.logger.debug("read thread terminated") - - # - incoming telnet commands and options - - def _telnet_process_command(self, command): - """Process commands other than DO, DONT, WILL, WONT.""" - # Currently none. RFC2217 only uses negotiation and subnegotiation. - if self.logger: - self.logger.warning("ignoring Telnet command: {!r}".format(command)) - - def _telnet_negotiate_option(self, command, option): - """Process incoming DO, DONT, WILL, WONT.""" - # check our registered telnet options and forward command to them - # they know themselves if they have to answer or not - known = False - for item in self._telnet_options: - # can have more than one match! as some options are duplicated for - # 'us' and 'them' - if item.option == option: - item.process_incoming(command) - known = True - if not known: - # handle unknown options - # only answer to positive requests and deny them - if command == WILL or command == DO: - self.telnet_send_option((DONT if command == WILL else WONT), option) - if self.logger: - self.logger.warning("rejected Telnet option: {!r}".format(option)) - - def _telnet_process_subnegotiation(self, suboption): - """Process subnegotiation, the data between IAC SB and IAC SE.""" - if suboption[0:1] == COM_PORT_OPTION: - if suboption[1:2] == SERVER_NOTIFY_LINESTATE and len(suboption) >= 3: - self._linestate = ord(suboption[2:3]) # ensure it is a number - if self.logger: - self.logger.info("NOTIFY_LINESTATE: {}".format(self._linestate)) - elif suboption[1:2] == SERVER_NOTIFY_MODEMSTATE and len(suboption) >= 3: - self._modemstate = ord(suboption[2:3]) # ensure it is a number - if self.logger: - self.logger.info("NOTIFY_MODEMSTATE: {}".format(self._modemstate)) - # update time when we think that a poll would make sense - self._modemstate_timeout.restart(0.3) - elif suboption[1:2] == FLOWCONTROL_SUSPEND: - self._remote_suspend_flow = True - elif suboption[1:2] == FLOWCONTROL_RESUME: - self._remote_suspend_flow = False - else: - for item in self._rfc2217_options.values(): - if item.ack_option == suboption[1:2]: - #~ print "processing COM_PORT_OPTION: %r" % list(suboption[1:]) - item.check_answer(bytes(suboption[2:])) - break - else: - if self.logger: - self.logger.warning("ignoring COM_PORT_OPTION: {!r}".format(suboption)) - else: - if self.logger: - self.logger.warning("ignoring subnegotiation: {!r}".format(suboption)) - - # - outgoing telnet commands and options - - def _internal_raw_write(self, data): - """internal socket write with no data escaping. used to send telnet stuff.""" - with self._write_lock: - self._socket.sendall(data) - - def telnet_send_option(self, action, option): - """Send DO, DONT, WILL, WONT.""" - self._internal_raw_write(IAC + action + option) - - def rfc2217_send_subnegotiation(self, option, value=b''): - """Subnegotiation of RFC2217 parameters.""" - value = value.replace(IAC, IAC_DOUBLED) - self._internal_raw_write(IAC + SB + COM_PORT_OPTION + option + value + IAC + SE) - - def rfc2217_send_purge(self, value): - """\ - Send purge request to the remote. - (PURGE_RECEIVE_BUFFER / PURGE_TRANSMIT_BUFFER / PURGE_BOTH_BUFFERS) - """ - item = self._rfc2217_options['purge'] - item.set(value) # transmit desired purge type - item.wait(self._network_timeout) # wait for acknowledge from the server - - def rfc2217_set_control(self, value): - """transmit change of control line to remote""" - item = self._rfc2217_options['control'] - item.set(value) # transmit desired control type - if self._ignore_set_control_answer: - # answers are ignored when option is set. compatibility mode for - # servers that answer, but not the expected one... (or no answer - # at all) i.e. sredird - time.sleep(0.1) # this helps getting the unit tests passed - else: - item.wait(self._network_timeout) # wait for acknowledge from the server - - def rfc2217_flow_server_ready(self): - """\ - check if server is ready to receive data. block for some time when - not. - """ - #~ if self._remote_suspend_flow: - #~ wait--- - - def get_modem_state(self): - """\ - get last modem state (cached value. If value is "old", request a new - one. This cache helps that we don't issue to many requests when e.g. all - status lines, one after the other is queried by the user (CTS, DSR - etc.) - """ - # active modem state polling enabled? is the value fresh enough? - if self._poll_modem_state and self._modemstate_timeout.expired(): - if self.logger: - self.logger.debug('polling modem state') - # when it is older, request an update - self.rfc2217_send_subnegotiation(NOTIFY_MODEMSTATE) - timeout = Timeout(self._network_timeout) - while not timeout.expired(): - time.sleep(0.05) # prevent 100% CPU load - # when expiration time is updated, it means that there is a new - # value - if not self._modemstate_timeout.expired(): - break - else: - if self.logger: - self.logger.warning('poll for modem state failed') - # even when there is a timeout, do not generate an error just - # return the last known value. this way we can support buggy - # servers that do not respond to polls, but send automatic - # updates. - if self._modemstate is not None: - if self.logger: - self.logger.debug('using cached modem state') - return self._modemstate - else: - # never received a notification from the server - raise SerialException("remote sends no NOTIFY_MODEMSTATE") - - -############################################################################# -# The following is code that helps implementing an RFC 2217 server. - -class PortManager(object): - """\ - This class manages the state of Telnet and RFC 2217. It needs a serial - instance and a connection to work with. Connection is expected to implement - a (thread safe) write function, that writes the string to the network. - """ - - def __init__(self, serial_port, connection, logger=None): - self.serial = serial_port - self.connection = connection - self.logger = logger - self._client_is_rfc2217 = False - - # filter state machine - self.mode = M_NORMAL - self.suboption = None - self.telnet_command = None - - # states for modem/line control events - self.modemstate_mask = 255 - self.last_modemstate = None - self.linstate_mask = 0 - - # all supported telnet options - self._telnet_options = [ - TelnetOption(self, 'ECHO', ECHO, WILL, WONT, DO, DONT, REQUESTED), - TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED), - TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, INACTIVE), - TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE), - TelnetOption(self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, REQUESTED), - TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED, self._client_ok), - TelnetOption(self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, INACTIVE, self._client_ok), - ] - - # negotiate Telnet/RFC2217 -> send initial requests - if self.logger: - self.logger.debug("requesting initial Telnet/RFC 2217 options") - for option in self._telnet_options: - if option.state is REQUESTED: - self.telnet_send_option(option.send_yes, option.option) - # issue 1st modem state notification - - def _client_ok(self): - """\ - callback of telnet option. It gets called when option is activated. - This one here is used to detect when the client agrees on RFC 2217. A - flag is set so that other functions like check_modem_lines know if the - client is OK. - """ - # The callback is used for we and they so if one party agrees, we're - # already happy. it seems not all servers do the negotiation correctly - # and i guess there are incorrect clients too.. so be happy if client - # answers one or the other positively. - self._client_is_rfc2217 = True - if self.logger: - self.logger.info("client accepts RFC 2217") - # this is to ensure that the client gets a notification, even if there - # was no change - self.check_modem_lines(force_notification=True) - - # - outgoing telnet commands and options - - def telnet_send_option(self, action, option): - """Send DO, DONT, WILL, WONT.""" - self.connection.write(IAC + action + option) - - def rfc2217_send_subnegotiation(self, option, value=b''): - """Subnegotiation of RFC 2217 parameters.""" - value = value.replace(IAC, IAC_DOUBLED) - self.connection.write(IAC + SB + COM_PORT_OPTION + option + value + IAC + SE) - - # - check modem lines, needs to be called periodically from user to - # establish polling - - def check_modem_lines(self, force_notification=False): - """\ - read control lines from serial port and compare the last value sent to remote. - send updates on changes. - """ - modemstate = ( - (self.serial.cts and MODEMSTATE_MASK_CTS) | - (self.serial.dsr and MODEMSTATE_MASK_DSR) | - (self.serial.ri and MODEMSTATE_MASK_RI) | - (self.serial.cd and MODEMSTATE_MASK_CD)) - # check what has changed - deltas = modemstate ^ (self.last_modemstate or 0) # when last is None -> 0 - if deltas & MODEMSTATE_MASK_CTS: - modemstate |= MODEMSTATE_MASK_CTS_CHANGE - if deltas & MODEMSTATE_MASK_DSR: - modemstate |= MODEMSTATE_MASK_DSR_CHANGE - if deltas & MODEMSTATE_MASK_RI: - modemstate |= MODEMSTATE_MASK_RI_CHANGE - if deltas & MODEMSTATE_MASK_CD: - modemstate |= MODEMSTATE_MASK_CD_CHANGE - # if new state is different and the mask allows this change, send - # notification. suppress notifications when client is not rfc2217 - if modemstate != self.last_modemstate or force_notification: - if (self._client_is_rfc2217 and (modemstate & self.modemstate_mask)) or force_notification: - self.rfc2217_send_subnegotiation( - SERVER_NOTIFY_MODEMSTATE, - to_bytes([modemstate & self.modemstate_mask])) - if self.logger: - self.logger.info("NOTIFY_MODEMSTATE: {}".format(modemstate)) - # save last state, but forget about deltas. - # otherwise it would also notify about changing deltas which is - # probably not very useful - self.last_modemstate = modemstate & 0xf0 - - # - outgoing data escaping - - def escape(self, data): - """\ - This generator function is for the user. All outgoing data has to be - properly escaped, so that no IAC character in the data stream messes up - the Telnet state machine in the server. - - socket.sendall(escape(data)) - """ - for byte in iterbytes(data): - if byte == IAC: - yield IAC - yield IAC - else: - yield byte - - # - incoming data filter - - def filter(self, data): - """\ - Handle a bunch of incoming bytes. This is a generator. It will yield - all characters not of interest for Telnet/RFC 2217. - - The idea is that the reader thread pushes data from the socket through - this filter: - - for byte in filter(socket.recv(1024)): - # do things like CR/LF conversion/whatever - # and write data to the serial port - serial.write(byte) - - (socket error handling code left as exercise for the reader) - """ - for byte in iterbytes(data): - if self.mode == M_NORMAL: - # interpret as command or as data - if byte == IAC: - self.mode = M_IAC_SEEN - else: - # store data in sub option buffer or pass it to our - # consumer depending on state - if self.suboption is not None: - self.suboption += byte - else: - yield byte - elif self.mode == M_IAC_SEEN: - if byte == IAC: - # interpret as command doubled -> insert character - # itself - if self.suboption is not None: - self.suboption += byte - else: - yield byte - self.mode = M_NORMAL - elif byte == SB: - # sub option start - self.suboption = bytearray() - self.mode = M_NORMAL - elif byte == SE: - # sub option end -> process it now - self._telnet_process_subnegotiation(bytes(self.suboption)) - self.suboption = None - self.mode = M_NORMAL - elif byte in (DO, DONT, WILL, WONT): - # negotiation - self.telnet_command = byte - self.mode = M_NEGOTIATE - else: - # other telnet commands - self._telnet_process_command(byte) - self.mode = M_NORMAL - elif self.mode == M_NEGOTIATE: # DO, DONT, WILL, WONT was received, option now following - self._telnet_negotiate_option(self.telnet_command, byte) - self.mode = M_NORMAL - - # - incoming telnet commands and options - - def _telnet_process_command(self, command): - """Process commands other than DO, DONT, WILL, WONT.""" - # Currently none. RFC2217 only uses negotiation and subnegotiation. - if self.logger: - self.logger.warning("ignoring Telnet command: {!r}".format(command)) - - def _telnet_negotiate_option(self, command, option): - """Process incoming DO, DONT, WILL, WONT.""" - # check our registered telnet options and forward command to them - # they know themselves if they have to answer or not - known = False - for item in self._telnet_options: - # can have more than one match! as some options are duplicated for - # 'us' and 'them' - if item.option == option: - item.process_incoming(command) - known = True - if not known: - # handle unknown options - # only answer to positive requests and deny them - if command == WILL or command == DO: - self.telnet_send_option((DONT if command == WILL else WONT), option) - if self.logger: - self.logger.warning("rejected Telnet option: {!r}".format(option)) - - def _telnet_process_subnegotiation(self, suboption): - """Process subnegotiation, the data between IAC SB and IAC SE.""" - if suboption[0:1] == COM_PORT_OPTION: - if self.logger: - self.logger.debug('received COM_PORT_OPTION: {!r}'.format(suboption)) - if suboption[1:2] == SET_BAUDRATE: - backup = self.serial.baudrate - try: - (baudrate,) = struct.unpack(b"!I", suboption[2:6]) - if baudrate != 0: - self.serial.baudrate = baudrate - except ValueError as e: - if self.logger: - self.logger.error("failed to set baud rate: {}".format(e)) - self.serial.baudrate = backup - else: - if self.logger: - self.logger.info("{} baud rate: {}".format('set' if baudrate else 'get', self.serial.baudrate)) - self.rfc2217_send_subnegotiation(SERVER_SET_BAUDRATE, struct.pack(b"!I", self.serial.baudrate)) - elif suboption[1:2] == SET_DATASIZE: - backup = self.serial.bytesize - try: - (datasize,) = struct.unpack(b"!B", suboption[2:3]) - if datasize != 0: - self.serial.bytesize = datasize - except ValueError as e: - if self.logger: - self.logger.error("failed to set data size: {}".format(e)) - self.serial.bytesize = backup - else: - if self.logger: - self.logger.info("{} data size: {}".format('set' if datasize else 'get', self.serial.bytesize)) - self.rfc2217_send_subnegotiation(SERVER_SET_DATASIZE, struct.pack(b"!B", self.serial.bytesize)) - elif suboption[1:2] == SET_PARITY: - backup = self.serial.parity - try: - parity = struct.unpack(b"!B", suboption[2:3])[0] - if parity != 0: - self.serial.parity = RFC2217_REVERSE_PARITY_MAP[parity] - except ValueError as e: - if self.logger: - self.logger.error("failed to set parity: {}".format(e)) - self.serial.parity = backup - else: - if self.logger: - self.logger.info("{} parity: {}".format('set' if parity else 'get', self.serial.parity)) - self.rfc2217_send_subnegotiation( - SERVER_SET_PARITY, - struct.pack(b"!B", RFC2217_PARITY_MAP[self.serial.parity])) - elif suboption[1:2] == SET_STOPSIZE: - backup = self.serial.stopbits - try: - stopbits = struct.unpack(b"!B", suboption[2:3])[0] - if stopbits != 0: - self.serial.stopbits = RFC2217_REVERSE_STOPBIT_MAP[stopbits] - except ValueError as e: - if self.logger: - self.logger.error("failed to set stop bits: {}".format(e)) - self.serial.stopbits = backup - else: - if self.logger: - self.logger.info("{} stop bits: {}".format('set' if stopbits else 'get', self.serial.stopbits)) - self.rfc2217_send_subnegotiation( - SERVER_SET_STOPSIZE, - struct.pack(b"!B", RFC2217_STOPBIT_MAP[self.serial.stopbits])) - elif suboption[1:2] == SET_CONTROL: - if suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING: - if self.serial.xonxoff: - self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL) - elif self.serial.rtscts: - self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL) - else: - self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_NO_FLOW_CONTROL) - elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL: - self.serial.xonxoff = False - self.serial.rtscts = False - if self.logger: - self.logger.info("changed flow control to None") - self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_NO_FLOW_CONTROL) - elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTROL: - self.serial.xonxoff = True - if self.logger: - self.logger.info("changed flow control to XON/XOFF") - self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL) - elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTROL: - self.serial.rtscts = True - if self.logger: - self.logger.info("changed flow control to RTS/CTS") - self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL) - elif suboption[2:3] == SET_CONTROL_REQ_BREAK_STATE: - if self.logger: - self.logger.warning("requested break state - not implemented") - pass # XXX needs cached value - elif suboption[2:3] == SET_CONTROL_BREAK_ON: - self.serial.break_condition = True - if self.logger: - self.logger.info("changed BREAK to active") - self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_ON) - elif suboption[2:3] == SET_CONTROL_BREAK_OFF: - self.serial.break_condition = False - if self.logger: - self.logger.info("changed BREAK to inactive") - self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_OFF) - elif suboption[2:3] == SET_CONTROL_REQ_DTR: - if self.logger: - self.logger.warning("requested DTR state - not implemented") - pass # XXX needs cached value - elif suboption[2:3] == SET_CONTROL_DTR_ON: - self.serial.dtr = True - if self.logger: - self.logger.info("changed DTR to active") - self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_ON) - elif suboption[2:3] == SET_CONTROL_DTR_OFF: - self.serial.dtr = False - if self.logger: - self.logger.info("changed DTR to inactive") - self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_OFF) - elif suboption[2:3] == SET_CONTROL_REQ_RTS: - if self.logger: - self.logger.warning("requested RTS state - not implemented") - pass # XXX needs cached value - #~ self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON) - elif suboption[2:3] == SET_CONTROL_RTS_ON: - self.serial.rts = True - if self.logger: - self.logger.info("changed RTS to active") - self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON) - elif suboption[2:3] == SET_CONTROL_RTS_OFF: - self.serial.rts = False - if self.logger: - self.logger.info("changed RTS to inactive") - self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_OFF) - #~ elif suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING_IN: - #~ elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL_IN: - #~ elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTOL_IN: - #~ elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTOL_IN: - #~ elif suboption[2:3] == SET_CONTROL_USE_DCD_FLOW_CONTROL: - #~ elif suboption[2:3] == SET_CONTROL_USE_DTR_FLOW_CONTROL: - #~ elif suboption[2:3] == SET_CONTROL_USE_DSR_FLOW_CONTROL: - elif suboption[1:2] == NOTIFY_LINESTATE: - # client polls for current state - self.rfc2217_send_subnegotiation( - SERVER_NOTIFY_LINESTATE, - to_bytes([0])) # sorry, nothing like that implemented - elif suboption[1:2] == NOTIFY_MODEMSTATE: - if self.logger: - self.logger.info("request for modem state") - # client polls for current state - self.check_modem_lines(force_notification=True) - elif suboption[1:2] == FLOWCONTROL_SUSPEND: - if self.logger: - self.logger.info("suspend") - self._remote_suspend_flow = True - elif suboption[1:2] == FLOWCONTROL_RESUME: - if self.logger: - self.logger.info("resume") - self._remote_suspend_flow = False - elif suboption[1:2] == SET_LINESTATE_MASK: - self.linstate_mask = ord(suboption[2:3]) # ensure it is a number - if self.logger: - self.logger.info("line state mask: 0x{:02x}".format(self.linstate_mask)) - elif suboption[1:2] == SET_MODEMSTATE_MASK: - self.modemstate_mask = ord(suboption[2:3]) # ensure it is a number - if self.logger: - self.logger.info("modem state mask: 0x{:02x}".format(self.modemstate_mask)) - elif suboption[1:2] == PURGE_DATA: - if suboption[2:3] == PURGE_RECEIVE_BUFFER: - self.serial.reset_input_buffer() - if self.logger: - self.logger.info("purge in") - self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_RECEIVE_BUFFER) - elif suboption[2:3] == PURGE_TRANSMIT_BUFFER: - self.serial.reset_output_buffer() - if self.logger: - self.logger.info("purge out") - self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_TRANSMIT_BUFFER) - elif suboption[2:3] == PURGE_BOTH_BUFFERS: - self.serial.reset_input_buffer() - self.serial.reset_output_buffer() - if self.logger: - self.logger.info("purge both") - self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_BOTH_BUFFERS) - else: - if self.logger: - self.logger.error("undefined PURGE_DATA: {!r}".format(list(suboption[2:]))) - else: - if self.logger: - self.logger.error("undefined COM_PORT_OPTION: {!r}".format(list(suboption[1:]))) - else: - if self.logger: - self.logger.warning("unknown subnegotiation: {!r}".format(suboption)) - - -# simple client test -if __name__ == '__main__': - import sys - s = Serial('rfc2217://localhost:7000', 115200) - sys.stdout.write('{}\n'.format(s)) - - sys.stdout.write("write...\n") - s.write(b"hello\n") - s.flush() - sys.stdout.write("read: {}\n".format(s.read(5))) - s.close() diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/rs485.py b/plotter-app/venv/lib/python3.8/site-packages/serial/rs485.py deleted file mode 100644 index d7aff6f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/rs485.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python - -# RS485 support -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# (C) 2015 Chris Liechti -# -# SPDX-License-Identifier: BSD-3-Clause - -"""\ -The settings for RS485 are stored in a dedicated object that can be applied to -serial ports (where supported). -NOTE: Some implementations may only support a subset of the settings. -""" - -from __future__ import absolute_import - -import time -import serial - - -class RS485Settings(object): - def __init__( - self, - rts_level_for_tx=True, - rts_level_for_rx=False, - loopback=False, - delay_before_tx=None, - delay_before_rx=None): - self.rts_level_for_tx = rts_level_for_tx - self.rts_level_for_rx = rts_level_for_rx - self.loopback = loopback - self.delay_before_tx = delay_before_tx - self.delay_before_rx = delay_before_rx - - -class RS485(serial.Serial): - """\ - A subclass that replaces the write method with one that toggles RTS - according to the RS485 settings. - - NOTE: This may work unreliably on some serial ports (control signals not - synchronized or delayed compared to data). Using delays may be - unreliable (varying times, larger than expected) as the OS may not - support very fine grained delays (no smaller than in the order of - tens of milliseconds). - - NOTE: Some implementations support this natively. Better performance - can be expected when the native version is used. - - NOTE: The loopback property is ignored by this implementation. The actual - behavior depends on the used hardware. - - Usage: - - ser = RS485(...) - ser.rs485_mode = RS485Settings(...) - ser.write(b'hello') - """ - - def __init__(self, *args, **kwargs): - super(RS485, self).__init__(*args, **kwargs) - self._alternate_rs485_settings = None - - def write(self, b): - """Write to port, controlling RTS before and after transmitting.""" - if self._alternate_rs485_settings is not None: - # apply level for TX and optional delay - self.setRTS(self._alternate_rs485_settings.rts_level_for_tx) - if self._alternate_rs485_settings.delay_before_tx is not None: - time.sleep(self._alternate_rs485_settings.delay_before_tx) - # write and wait for data to be written - super(RS485, self).write(b) - super(RS485, self).flush() - # optional delay and apply level for RX - if self._alternate_rs485_settings.delay_before_rx is not None: - time.sleep(self._alternate_rs485_settings.delay_before_rx) - self.setRTS(self._alternate_rs485_settings.rts_level_for_rx) - else: - super(RS485, self).write(b) - - # redirect where the property stores the settings so that underlying Serial - # instance does not see them - @property - def rs485_mode(self): - """\ - Enable RS485 mode and apply new settings, set to None to disable. - See serial.rs485.RS485Settings for more info about the value. - """ - return self._alternate_rs485_settings - - @rs485_mode.setter - def rs485_mode(self, rs485_settings): - self._alternate_rs485_settings = rs485_settings diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/serialcli.py b/plotter-app/venv/lib/python3.8/site-packages/serial/serialcli.py deleted file mode 100644 index 4614736..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/serialcli.py +++ /dev/null @@ -1,253 +0,0 @@ -#! python -# -# Backend for .NET/Mono (IronPython), .NET >= 2 -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# (C) 2008-2015 Chris Liechti -# -# SPDX-License-Identifier: BSD-3-Clause - -from __future__ import absolute_import - -import System -import System.IO.Ports -from serial.serialutil import * - -# must invoke function with byte array, make a helper to convert strings -# to byte arrays -sab = System.Array[System.Byte] - - -def as_byte_array(string): - return sab([ord(x) for x in string]) # XXX will require adaption when run with a 3.x compatible IronPython - - -class Serial(SerialBase): - """Serial port implementation for .NET/Mono.""" - - BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200) - - def open(self): - """\ - Open port with current settings. This may throw a SerialException - if the port cannot be opened. - """ - if self._port is None: - raise SerialException("Port must be configured before it can be used.") - if self.is_open: - raise SerialException("Port is already open.") - try: - self._port_handle = System.IO.Ports.SerialPort(self.portstr) - except Exception as msg: - self._port_handle = None - raise SerialException("could not open port %s: %s" % (self.portstr, msg)) - - # if RTS and/or DTR are not set before open, they default to True - if self._rts_state is None: - self._rts_state = True - if self._dtr_state is None: - self._dtr_state = True - - self._reconfigure_port() - self._port_handle.Open() - self.is_open = True - if not self._dsrdtr: - self._update_dtr_state() - if not self._rtscts: - self._update_rts_state() - self.reset_input_buffer() - - def _reconfigure_port(self): - """Set communication parameters on opened port.""" - if not self._port_handle: - raise SerialException("Can only operate on a valid port handle") - - #~ self._port_handle.ReceivedBytesThreshold = 1 - - if self._timeout is None: - self._port_handle.ReadTimeout = System.IO.Ports.SerialPort.InfiniteTimeout - else: - self._port_handle.ReadTimeout = int(self._timeout * 1000) - - # if self._timeout != 0 and self._interCharTimeout is not None: - # timeouts = (int(self._interCharTimeout * 1000),) + timeouts[1:] - - if self._write_timeout is None: - self._port_handle.WriteTimeout = System.IO.Ports.SerialPort.InfiniteTimeout - else: - self._port_handle.WriteTimeout = int(self._write_timeout * 1000) - - # Setup the connection info. - try: - self._port_handle.BaudRate = self._baudrate - except IOError as e: - # catch errors from illegal baudrate settings - raise ValueError(str(e)) - - if self._bytesize == FIVEBITS: - self._port_handle.DataBits = 5 - elif self._bytesize == SIXBITS: - self._port_handle.DataBits = 6 - elif self._bytesize == SEVENBITS: - self._port_handle.DataBits = 7 - elif self._bytesize == EIGHTBITS: - self._port_handle.DataBits = 8 - else: - raise ValueError("Unsupported number of data bits: %r" % self._bytesize) - - if self._parity == PARITY_NONE: - self._port_handle.Parity = getattr(System.IO.Ports.Parity, 'None') # reserved keyword in Py3k - elif self._parity == PARITY_EVEN: - self._port_handle.Parity = System.IO.Ports.Parity.Even - elif self._parity == PARITY_ODD: - self._port_handle.Parity = System.IO.Ports.Parity.Odd - elif self._parity == PARITY_MARK: - self._port_handle.Parity = System.IO.Ports.Parity.Mark - elif self._parity == PARITY_SPACE: - self._port_handle.Parity = System.IO.Ports.Parity.Space - else: - raise ValueError("Unsupported parity mode: %r" % self._parity) - - if self._stopbits == STOPBITS_ONE: - self._port_handle.StopBits = System.IO.Ports.StopBits.One - elif self._stopbits == STOPBITS_ONE_POINT_FIVE: - self._port_handle.StopBits = System.IO.Ports.StopBits.OnePointFive - elif self._stopbits == STOPBITS_TWO: - self._port_handle.StopBits = System.IO.Ports.StopBits.Two - else: - raise ValueError("Unsupported number of stop bits: %r" % self._stopbits) - - if self._rtscts and self._xonxoff: - self._port_handle.Handshake = System.IO.Ports.Handshake.RequestToSendXOnXOff - elif self._rtscts: - self._port_handle.Handshake = System.IO.Ports.Handshake.RequestToSend - elif self._xonxoff: - self._port_handle.Handshake = System.IO.Ports.Handshake.XOnXOff - else: - self._port_handle.Handshake = getattr(System.IO.Ports.Handshake, 'None') # reserved keyword in Py3k - - #~ def __del__(self): - #~ self.close() - - def close(self): - """Close port""" - if self.is_open: - if self._port_handle: - try: - self._port_handle.Close() - except System.IO.Ports.InvalidOperationException: - # ignore errors. can happen for unplugged USB serial devices - pass - self._port_handle = None - self.is_open = False - - # - - - - - - - - - - - - - - - - - - - - - - - - - - @property - def in_waiting(self): - """Return the number of characters currently in the input buffer.""" - if not self.is_open: - raise PortNotOpenError() - return self._port_handle.BytesToRead - - def read(self, size=1): - """\ - Read size bytes from the serial port. If a timeout is set it may - return less characters as requested. With no timeout it will block - until the requested number of bytes is read. - """ - if not self.is_open: - raise PortNotOpenError() - # must use single byte reads as this is the only way to read - # without applying encodings - data = bytearray() - while size: - try: - data.append(self._port_handle.ReadByte()) - except System.TimeoutException: - break - else: - size -= 1 - return bytes(data) - - def write(self, data): - """Output the given string over the serial port.""" - if not self.is_open: - raise PortNotOpenError() - #~ if not isinstance(data, (bytes, bytearray)): - #~ raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data))) - try: - # must call overloaded method with byte array argument - # as this is the only one not applying encodings - self._port_handle.Write(as_byte_array(data), 0, len(data)) - except System.TimeoutException: - raise SerialTimeoutException('Write timeout') - return len(data) - - def reset_input_buffer(self): - """Clear input buffer, discarding all that is in the buffer.""" - if not self.is_open: - raise PortNotOpenError() - self._port_handle.DiscardInBuffer() - - def reset_output_buffer(self): - """\ - Clear output buffer, aborting the current output and - discarding all that is in the buffer. - """ - if not self.is_open: - raise PortNotOpenError() - self._port_handle.DiscardOutBuffer() - - def _update_break_state(self): - """ - Set break: Controls TXD. When active, to transmitting is possible. - """ - if not self.is_open: - raise PortNotOpenError() - self._port_handle.BreakState = bool(self._break_state) - - def _update_rts_state(self): - """Set terminal status line: Request To Send""" - if not self.is_open: - raise PortNotOpenError() - self._port_handle.RtsEnable = bool(self._rts_state) - - def _update_dtr_state(self): - """Set terminal status line: Data Terminal Ready""" - if not self.is_open: - raise PortNotOpenError() - self._port_handle.DtrEnable = bool(self._dtr_state) - - @property - def cts(self): - """Read terminal status line: Clear To Send""" - if not self.is_open: - raise PortNotOpenError() - return self._port_handle.CtsHolding - - @property - def dsr(self): - """Read terminal status line: Data Set Ready""" - if not self.is_open: - raise PortNotOpenError() - return self._port_handle.DsrHolding - - @property - def ri(self): - """Read terminal status line: Ring Indicator""" - if not self.is_open: - raise PortNotOpenError() - #~ return self._port_handle.XXX - return False # XXX an error would be better - - @property - def cd(self): - """Read terminal status line: Carrier Detect""" - if not self.is_open: - raise PortNotOpenError() - return self._port_handle.CDHolding - - # - - platform specific - - - - - # none diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/serialjava.py b/plotter-app/venv/lib/python3.8/site-packages/serial/serialjava.py deleted file mode 100644 index 0789a78..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/serialjava.py +++ /dev/null @@ -1,251 +0,0 @@ -#!jython -# -# Backend Jython with JavaComm -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# (C) 2002-2015 Chris Liechti -# -# SPDX-License-Identifier: BSD-3-Clause - -from __future__ import absolute_import - -from serial.serialutil import * - - -def my_import(name): - mod = __import__(name) - components = name.split('.') - for comp in components[1:]: - mod = getattr(mod, comp) - return mod - - -def detect_java_comm(names): - """try given list of modules and return that imports""" - for name in names: - try: - mod = my_import(name) - mod.SerialPort - return mod - except (ImportError, AttributeError): - pass - raise ImportError("No Java Communications API implementation found") - - -# Java Communications API implementations -# http://mho.republika.pl/java/comm/ - -comm = detect_java_comm([ - 'javax.comm', # Sun/IBM - 'gnu.io', # RXTX -]) - - -def device(portnumber): - """Turn a port number into a device name""" - enum = comm.CommPortIdentifier.getPortIdentifiers() - ports = [] - while enum.hasMoreElements(): - el = enum.nextElement() - if el.getPortType() == comm.CommPortIdentifier.PORT_SERIAL: - ports.append(el) - return ports[portnumber].getName() - - -class Serial(SerialBase): - """\ - Serial port class, implemented with Java Communications API and - thus usable with jython and the appropriate java extension. - """ - - def open(self): - """\ - Open port with current settings. This may throw a SerialException - if the port cannot be opened. - """ - if self._port is None: - raise SerialException("Port must be configured before it can be used.") - if self.is_open: - raise SerialException("Port is already open.") - if type(self._port) == type(''): # strings are taken directly - portId = comm.CommPortIdentifier.getPortIdentifier(self._port) - else: - portId = comm.CommPortIdentifier.getPortIdentifier(device(self._port)) # numbers are transformed to a comport id obj - try: - self.sPort = portId.open("python serial module", 10) - except Exception as msg: - self.sPort = None - raise SerialException("Could not open port: %s" % msg) - self._reconfigurePort() - self._instream = self.sPort.getInputStream() - self._outstream = self.sPort.getOutputStream() - self.is_open = True - - def _reconfigurePort(self): - """Set communication parameters on opened port.""" - if not self.sPort: - raise SerialException("Can only operate on a valid port handle") - - self.sPort.enableReceiveTimeout(30) - if self._bytesize == FIVEBITS: - jdatabits = comm.SerialPort.DATABITS_5 - elif self._bytesize == SIXBITS: - jdatabits = comm.SerialPort.DATABITS_6 - elif self._bytesize == SEVENBITS: - jdatabits = comm.SerialPort.DATABITS_7 - elif self._bytesize == EIGHTBITS: - jdatabits = comm.SerialPort.DATABITS_8 - else: - raise ValueError("unsupported bytesize: %r" % self._bytesize) - - if self._stopbits == STOPBITS_ONE: - jstopbits = comm.SerialPort.STOPBITS_1 - elif self._stopbits == STOPBITS_ONE_POINT_FIVE: - jstopbits = comm.SerialPort.STOPBITS_1_5 - elif self._stopbits == STOPBITS_TWO: - jstopbits = comm.SerialPort.STOPBITS_2 - else: - raise ValueError("unsupported number of stopbits: %r" % self._stopbits) - - if self._parity == PARITY_NONE: - jparity = comm.SerialPort.PARITY_NONE - elif self._parity == PARITY_EVEN: - jparity = comm.SerialPort.PARITY_EVEN - elif self._parity == PARITY_ODD: - jparity = comm.SerialPort.PARITY_ODD - elif self._parity == PARITY_MARK: - jparity = comm.SerialPort.PARITY_MARK - elif self._parity == PARITY_SPACE: - jparity = comm.SerialPort.PARITY_SPACE - else: - raise ValueError("unsupported parity type: %r" % self._parity) - - jflowin = jflowout = 0 - if self._rtscts: - jflowin |= comm.SerialPort.FLOWCONTROL_RTSCTS_IN - jflowout |= comm.SerialPort.FLOWCONTROL_RTSCTS_OUT - if self._xonxoff: - jflowin |= comm.SerialPort.FLOWCONTROL_XONXOFF_IN - jflowout |= comm.SerialPort.FLOWCONTROL_XONXOFF_OUT - - self.sPort.setSerialPortParams(self._baudrate, jdatabits, jstopbits, jparity) - self.sPort.setFlowControlMode(jflowin | jflowout) - - if self._timeout >= 0: - self.sPort.enableReceiveTimeout(int(self._timeout*1000)) - else: - self.sPort.disableReceiveTimeout() - - def close(self): - """Close port""" - if self.is_open: - if self.sPort: - self._instream.close() - self._outstream.close() - self.sPort.close() - self.sPort = None - self.is_open = False - - # - - - - - - - - - - - - - - - - - - - - - - - - - - @property - def in_waiting(self): - """Return the number of characters currently in the input buffer.""" - if not self.sPort: - raise PortNotOpenError() - return self._instream.available() - - def read(self, size=1): - """\ - Read size bytes from the serial port. If a timeout is set it may - return less characters as requested. With no timeout it will block - until the requested number of bytes is read. - """ - if not self.sPort: - raise PortNotOpenError() - read = bytearray() - if size > 0: - while len(read) < size: - x = self._instream.read() - if x == -1: - if self.timeout >= 0: - break - else: - read.append(x) - return bytes(read) - - def write(self, data): - """Output the given string over the serial port.""" - if not self.sPort: - raise PortNotOpenError() - if not isinstance(data, (bytes, bytearray)): - raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data))) - self._outstream.write(data) - return len(data) - - def reset_input_buffer(self): - """Clear input buffer, discarding all that is in the buffer.""" - if not self.sPort: - raise PortNotOpenError() - self._instream.skip(self._instream.available()) - - def reset_output_buffer(self): - """\ - Clear output buffer, aborting the current output and - discarding all that is in the buffer. - """ - if not self.sPort: - raise PortNotOpenError() - self._outstream.flush() - - def send_break(self, duration=0.25): - """Send break condition. Timed, returns to idle state after given duration.""" - if not self.sPort: - raise PortNotOpenError() - self.sPort.sendBreak(duration*1000.0) - - def _update_break_state(self): - """Set break: Controls TXD. When active, to transmitting is possible.""" - if self.fd is None: - raise PortNotOpenError() - raise SerialException("The _update_break_state function is not implemented in java.") - - def _update_rts_state(self): - """Set terminal status line: Request To Send""" - if not self.sPort: - raise PortNotOpenError() - self.sPort.setRTS(self._rts_state) - - def _update_dtr_state(self): - """Set terminal status line: Data Terminal Ready""" - if not self.sPort: - raise PortNotOpenError() - self.sPort.setDTR(self._dtr_state) - - @property - def cts(self): - """Read terminal status line: Clear To Send""" - if not self.sPort: - raise PortNotOpenError() - self.sPort.isCTS() - - @property - def dsr(self): - """Read terminal status line: Data Set Ready""" - if not self.sPort: - raise PortNotOpenError() - self.sPort.isDSR() - - @property - def ri(self): - """Read terminal status line: Ring Indicator""" - if not self.sPort: - raise PortNotOpenError() - self.sPort.isRI() - - @property - def cd(self): - """Read terminal status line: Carrier Detect""" - if not self.sPort: - raise PortNotOpenError() - self.sPort.isCD() diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/serialposix.py b/plotter-app/venv/lib/python3.8/site-packages/serial/serialposix.py deleted file mode 100644 index 7aceb76..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/serialposix.py +++ /dev/null @@ -1,900 +0,0 @@ -#!/usr/bin/env python -# -# backend for serial IO for POSIX compatible systems, like Linux, OSX -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# (C) 2001-2020 Chris Liechti -# -# SPDX-License-Identifier: BSD-3-Clause -# -# parts based on code from Grant B. Edwards : -# ftp://ftp.visi.com/users/grante/python/PosixSerial.py -# -# references: http://www.easysw.com/~mike/serial/serial.html - -# Collection of port names (was previously used by number_to_device which was -# removed. -# - Linux /dev/ttyS%d (confirmed) -# - cygwin/win32 /dev/com%d (confirmed) -# - openbsd (OpenBSD) /dev/cua%02d -# - bsd*, freebsd* /dev/cuad%d -# - darwin (OS X) /dev/cuad%d -# - netbsd /dev/dty%02d (NetBSD 1.6 testing by Erk) -# - irix (IRIX) /dev/ttyf%d (partially tested) names depending on flow control -# - hp (HP-UX) /dev/tty%dp0 (not tested) -# - sunos (Solaris/SunOS) /dev/tty%c (letters, 'a'..'z') (confirmed) -# - aix (AIX) /dev/tty%d - - -from __future__ import absolute_import - -# pylint: disable=abstract-method -import errno -import fcntl -import os -import select -import struct -import sys -import termios - -import serial -from serial.serialutil import SerialBase, SerialException, to_bytes, \ - PortNotOpenError, SerialTimeoutException, Timeout - - -class PlatformSpecificBase(object): - BAUDRATE_CONSTANTS = {} - - def _set_special_baudrate(self, baudrate): - raise NotImplementedError('non-standard baudrates are not supported on this platform') - - def _set_rs485_mode(self, rs485_settings): - raise NotImplementedError('RS485 not supported on this platform') - - def set_low_latency_mode(self, low_latency_settings): - raise NotImplementedError('Low latency not supported on this platform') - - def _update_break_state(self): - """\ - Set break: Controls TXD. When active, no transmitting is possible. - """ - if self._break_state: - fcntl.ioctl(self.fd, TIOCSBRK) - else: - fcntl.ioctl(self.fd, TIOCCBRK) - - -# some systems support an extra flag to enable the two in POSIX unsupported -# paritiy settings for MARK and SPACE -CMSPAR = 0 # default, for unsupported platforms, override below - -# try to detect the OS so that a device can be selected... -# this code block should supply a device() and set_special_baudrate() function -# for the platform -plat = sys.platform.lower() - -if plat[:5] == 'linux': # Linux (confirmed) # noqa - import array - - # extra termios flags - CMSPAR = 0o10000000000 # Use "stick" (mark/space) parity - - # baudrate ioctls - TCGETS2 = 0x802C542A - TCSETS2 = 0x402C542B - BOTHER = 0o010000 - - # RS485 ioctls - TIOCGRS485 = 0x542E - TIOCSRS485 = 0x542F - SER_RS485_ENABLED = 0b00000001 - SER_RS485_RTS_ON_SEND = 0b00000010 - SER_RS485_RTS_AFTER_SEND = 0b00000100 - SER_RS485_RX_DURING_TX = 0b00010000 - - class PlatformSpecific(PlatformSpecificBase): - BAUDRATE_CONSTANTS = { - 0: 0o000000, # hang up - 50: 0o000001, - 75: 0o000002, - 110: 0o000003, - 134: 0o000004, - 150: 0o000005, - 200: 0o000006, - 300: 0o000007, - 600: 0o000010, - 1200: 0o000011, - 1800: 0o000012, - 2400: 0o000013, - 4800: 0o000014, - 9600: 0o000015, - 19200: 0o000016, - 38400: 0o000017, - 57600: 0o010001, - 115200: 0o010002, - 230400: 0o010003, - 460800: 0o010004, - 500000: 0o010005, - 576000: 0o010006, - 921600: 0o010007, - 1000000: 0o010010, - 1152000: 0o010011, - 1500000: 0o010012, - 2000000: 0o010013, - 2500000: 0o010014, - 3000000: 0o010015, - 3500000: 0o010016, - 4000000: 0o010017 - } - - def set_low_latency_mode(self, low_latency_settings): - buf = array.array('i', [0] * 32) - - try: - # get serial_struct - fcntl.ioctl(self.fd, termios.TIOCGSERIAL, buf) - - # set or unset ASYNC_LOW_LATENCY flag - if low_latency_settings: - buf[4] |= 0x2000 - else: - buf[4] &= ~0x2000 - - # set serial_struct - fcntl.ioctl(self.fd, termios.TIOCSSERIAL, buf) - except IOError as e: - raise ValueError('Failed to update ASYNC_LOW_LATENCY flag to {}: {}'.format(low_latency_settings, e)) - - def _set_special_baudrate(self, baudrate): - # right size is 44 on x86_64, allow for some growth - buf = array.array('i', [0] * 64) - try: - # get serial_struct - fcntl.ioctl(self.fd, TCGETS2, buf) - # set custom speed - buf[2] &= ~termios.CBAUD - buf[2] |= BOTHER - buf[9] = buf[10] = baudrate - - # set serial_struct - fcntl.ioctl(self.fd, TCSETS2, buf) - except IOError as e: - raise ValueError('Failed to set custom baud rate ({}): {}'.format(baudrate, e)) - - def _set_rs485_mode(self, rs485_settings): - buf = array.array('i', [0] * 8) # flags, delaytx, delayrx, padding - try: - fcntl.ioctl(self.fd, TIOCGRS485, buf) - buf[0] |= SER_RS485_ENABLED - if rs485_settings is not None: - if rs485_settings.loopback: - buf[0] |= SER_RS485_RX_DURING_TX - else: - buf[0] &= ~SER_RS485_RX_DURING_TX - if rs485_settings.rts_level_for_tx: - buf[0] |= SER_RS485_RTS_ON_SEND - else: - buf[0] &= ~SER_RS485_RTS_ON_SEND - if rs485_settings.rts_level_for_rx: - buf[0] |= SER_RS485_RTS_AFTER_SEND - else: - buf[0] &= ~SER_RS485_RTS_AFTER_SEND - if rs485_settings.delay_before_tx is not None: - buf[1] = int(rs485_settings.delay_before_tx * 1000) - if rs485_settings.delay_before_rx is not None: - buf[2] = int(rs485_settings.delay_before_rx * 1000) - else: - buf[0] = 0 # clear SER_RS485_ENABLED - fcntl.ioctl(self.fd, TIOCSRS485, buf) - except IOError as e: - raise ValueError('Failed to set RS485 mode: {}'.format(e)) - - -elif plat == 'cygwin': # cygwin/win32 (confirmed) - - class PlatformSpecific(PlatformSpecificBase): - BAUDRATE_CONSTANTS = { - 128000: 0x01003, - 256000: 0x01005, - 500000: 0x01007, - 576000: 0x01008, - 921600: 0x01009, - 1000000: 0x0100a, - 1152000: 0x0100b, - 1500000: 0x0100c, - 2000000: 0x0100d, - 2500000: 0x0100e, - 3000000: 0x0100f - } - - -elif plat[:6] == 'darwin': # OS X - import array - IOSSIOSPEED = 0x80045402 # _IOW('T', 2, speed_t) - - class PlatformSpecific(PlatformSpecificBase): - osx_version = os.uname()[2].split('.') - TIOCSBRK = 0x2000747B # _IO('t', 123) - TIOCCBRK = 0x2000747A # _IO('t', 122) - - # Tiger or above can support arbitrary serial speeds - if int(osx_version[0]) >= 8: - def _set_special_baudrate(self, baudrate): - # use IOKit-specific call to set up high speeds - buf = array.array('i', [baudrate]) - fcntl.ioctl(self.fd, IOSSIOSPEED, buf, 1) - - def _update_break_state(self): - """\ - Set break: Controls TXD. When active, no transmitting is possible. - """ - if self._break_state: - fcntl.ioctl(self.fd, PlatformSpecific.TIOCSBRK) - else: - fcntl.ioctl(self.fd, PlatformSpecific.TIOCCBRK) - -elif plat[:3] == 'bsd' or \ - plat[:7] == 'freebsd' or \ - plat[:6] == 'netbsd' or \ - plat[:7] == 'openbsd': - - class ReturnBaudrate(object): - def __getitem__(self, key): - return key - - class PlatformSpecific(PlatformSpecificBase): - # Only tested on FreeBSD: - # The baud rate may be passed in as - # a literal value. - BAUDRATE_CONSTANTS = ReturnBaudrate() - - TIOCSBRK = 0x2000747B # _IO('t', 123) - TIOCCBRK = 0x2000747A # _IO('t', 122) - - - def _update_break_state(self): - """\ - Set break: Controls TXD. When active, no transmitting is possible. - """ - if self._break_state: - fcntl.ioctl(self.fd, PlatformSpecific.TIOCSBRK) - else: - fcntl.ioctl(self.fd, PlatformSpecific.TIOCCBRK) - -else: - class PlatformSpecific(PlatformSpecificBase): - pass - - -# load some constants for later use. -# try to use values from termios, use defaults from linux otherwise -TIOCMGET = getattr(termios, 'TIOCMGET', 0x5415) -TIOCMBIS = getattr(termios, 'TIOCMBIS', 0x5416) -TIOCMBIC = getattr(termios, 'TIOCMBIC', 0x5417) -TIOCMSET = getattr(termios, 'TIOCMSET', 0x5418) - -# TIOCM_LE = getattr(termios, 'TIOCM_LE', 0x001) -TIOCM_DTR = getattr(termios, 'TIOCM_DTR', 0x002) -TIOCM_RTS = getattr(termios, 'TIOCM_RTS', 0x004) -# TIOCM_ST = getattr(termios, 'TIOCM_ST', 0x008) -# TIOCM_SR = getattr(termios, 'TIOCM_SR', 0x010) - -TIOCM_CTS = getattr(termios, 'TIOCM_CTS', 0x020) -TIOCM_CAR = getattr(termios, 'TIOCM_CAR', 0x040) -TIOCM_RNG = getattr(termios, 'TIOCM_RNG', 0x080) -TIOCM_DSR = getattr(termios, 'TIOCM_DSR', 0x100) -TIOCM_CD = getattr(termios, 'TIOCM_CD', TIOCM_CAR) -TIOCM_RI = getattr(termios, 'TIOCM_RI', TIOCM_RNG) -# TIOCM_OUT1 = getattr(termios, 'TIOCM_OUT1', 0x2000) -# TIOCM_OUT2 = getattr(termios, 'TIOCM_OUT2', 0x4000) -if hasattr(termios, 'TIOCINQ'): - TIOCINQ = termios.TIOCINQ -else: - TIOCINQ = getattr(termios, 'FIONREAD', 0x541B) -TIOCOUTQ = getattr(termios, 'TIOCOUTQ', 0x5411) - -TIOCM_zero_str = struct.pack('I', 0) -TIOCM_RTS_str = struct.pack('I', TIOCM_RTS) -TIOCM_DTR_str = struct.pack('I', TIOCM_DTR) - -TIOCSBRK = getattr(termios, 'TIOCSBRK', 0x5427) -TIOCCBRK = getattr(termios, 'TIOCCBRK', 0x5428) - - -class Serial(SerialBase, PlatformSpecific): - """\ - Serial port class POSIX implementation. Serial port configuration is - done with termios and fcntl. Runs on Linux and many other Un*x like - systems. - """ - - def open(self): - """\ - Open port with current settings. This may throw a SerialException - if the port cannot be opened.""" - if self._port is None: - raise SerialException("Port must be configured before it can be used.") - if self.is_open: - raise SerialException("Port is already open.") - self.fd = None - # open - try: - self.fd = os.open(self.portstr, os.O_RDWR | os.O_NOCTTY | os.O_NONBLOCK) - except OSError as msg: - self.fd = None - raise SerialException(msg.errno, "could not open port {}: {}".format(self._port, msg)) - #~ fcntl.fcntl(self.fd, fcntl.F_SETFL, 0) # set blocking - - self.pipe_abort_read_r, self.pipe_abort_read_w = None, None - self.pipe_abort_write_r, self.pipe_abort_write_w = None, None - - try: - self._reconfigure_port(force_update=True) - - try: - if not self._dsrdtr: - self._update_dtr_state() - if not self._rtscts: - self._update_rts_state() - except IOError as e: - # ignore Invalid argument and Inappropriate ioctl - if e.errno not in (errno.EINVAL, errno.ENOTTY): - raise - - self._reset_input_buffer() - - self.pipe_abort_read_r, self.pipe_abort_read_w = os.pipe() - self.pipe_abort_write_r, self.pipe_abort_write_w = os.pipe() - fcntl.fcntl(self.pipe_abort_read_r, fcntl.F_SETFL, os.O_NONBLOCK) - fcntl.fcntl(self.pipe_abort_write_r, fcntl.F_SETFL, os.O_NONBLOCK) - except BaseException: - try: - os.close(self.fd) - except Exception: - # ignore any exception when closing the port - # also to keep original exception that happened when setting up - pass - self.fd = None - - if self.pipe_abort_read_w is not None: - os.close(self.pipe_abort_read_w) - self.pipe_abort_read_w = None - if self.pipe_abort_read_r is not None: - os.close(self.pipe_abort_read_r) - self.pipe_abort_read_r = None - if self.pipe_abort_write_w is not None: - os.close(self.pipe_abort_write_w) - self.pipe_abort_write_w = None - if self.pipe_abort_write_r is not None: - os.close(self.pipe_abort_write_r) - self.pipe_abort_write_r = None - - raise - - self.is_open = True - - def _reconfigure_port(self, force_update=False): - """Set communication parameters on opened port.""" - if self.fd is None: - raise SerialException("Can only operate on a valid file descriptor") - - # if exclusive lock is requested, create it before we modify anything else - if self._exclusive is not None: - if self._exclusive: - try: - fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB) - except IOError as msg: - raise SerialException(msg.errno, "Could not exclusively lock port {}: {}".format(self._port, msg)) - else: - fcntl.flock(self.fd, fcntl.LOCK_UN) - - custom_baud = None - - vmin = vtime = 0 # timeout is done via select - if self._inter_byte_timeout is not None: - vmin = 1 - vtime = int(self._inter_byte_timeout * 10) - try: - orig_attr = termios.tcgetattr(self.fd) - iflag, oflag, cflag, lflag, ispeed, ospeed, cc = orig_attr - except termios.error as msg: # if a port is nonexistent but has a /dev file, it'll fail here - raise SerialException("Could not configure port: {}".format(msg)) - # set up raw mode / no echo / binary - cflag |= (termios.CLOCAL | termios.CREAD) - lflag &= ~(termios.ICANON | termios.ECHO | termios.ECHOE | - termios.ECHOK | termios.ECHONL | - termios.ISIG | termios.IEXTEN) # |termios.ECHOPRT - for flag in ('ECHOCTL', 'ECHOKE'): # netbsd workaround for Erk - if hasattr(termios, flag): - lflag &= ~getattr(termios, flag) - - oflag &= ~(termios.OPOST | termios.ONLCR | termios.OCRNL) - iflag &= ~(termios.INLCR | termios.IGNCR | termios.ICRNL | termios.IGNBRK) - if hasattr(termios, 'IUCLC'): - iflag &= ~termios.IUCLC - if hasattr(termios, 'PARMRK'): - iflag &= ~termios.PARMRK - - # setup baud rate - try: - ispeed = ospeed = getattr(termios, 'B{}'.format(self._baudrate)) - except AttributeError: - try: - ispeed = ospeed = self.BAUDRATE_CONSTANTS[self._baudrate] - except KeyError: - #~ raise ValueError('Invalid baud rate: %r' % self._baudrate) - - # See if BOTHER is defined for this platform; if it is, use - # this for a speed not defined in the baudrate constants list. - try: - ispeed = ospeed = BOTHER - except NameError: - # may need custom baud rate, it isn't in our list. - ispeed = ospeed = getattr(termios, 'B38400') - - try: - custom_baud = int(self._baudrate) # store for later - except ValueError: - raise ValueError('Invalid baud rate: {!r}'.format(self._baudrate)) - else: - if custom_baud < 0: - raise ValueError('Invalid baud rate: {!r}'.format(self._baudrate)) - - # setup char len - cflag &= ~termios.CSIZE - if self._bytesize == 8: - cflag |= termios.CS8 - elif self._bytesize == 7: - cflag |= termios.CS7 - elif self._bytesize == 6: - cflag |= termios.CS6 - elif self._bytesize == 5: - cflag |= termios.CS5 - else: - raise ValueError('Invalid char len: {!r}'.format(self._bytesize)) - # setup stop bits - if self._stopbits == serial.STOPBITS_ONE: - cflag &= ~(termios.CSTOPB) - elif self._stopbits == serial.STOPBITS_ONE_POINT_FIVE: - cflag |= (termios.CSTOPB) # XXX same as TWO.. there is no POSIX support for 1.5 - elif self._stopbits == serial.STOPBITS_TWO: - cflag |= (termios.CSTOPB) - else: - raise ValueError('Invalid stop bit specification: {!r}'.format(self._stopbits)) - # setup parity - iflag &= ~(termios.INPCK | termios.ISTRIP) - if self._parity == serial.PARITY_NONE: - cflag &= ~(termios.PARENB | termios.PARODD | CMSPAR) - elif self._parity == serial.PARITY_EVEN: - cflag &= ~(termios.PARODD | CMSPAR) - cflag |= (termios.PARENB) - elif self._parity == serial.PARITY_ODD: - cflag &= ~CMSPAR - cflag |= (termios.PARENB | termios.PARODD) - elif self._parity == serial.PARITY_MARK and CMSPAR: - cflag |= (termios.PARENB | CMSPAR | termios.PARODD) - elif self._parity == serial.PARITY_SPACE and CMSPAR: - cflag |= (termios.PARENB | CMSPAR) - cflag &= ~(termios.PARODD) - else: - raise ValueError('Invalid parity: {!r}'.format(self._parity)) - # setup flow control - # xonxoff - if hasattr(termios, 'IXANY'): - if self._xonxoff: - iflag |= (termios.IXON | termios.IXOFF) # |termios.IXANY) - else: - iflag &= ~(termios.IXON | termios.IXOFF | termios.IXANY) - else: - if self._xonxoff: - iflag |= (termios.IXON | termios.IXOFF) - else: - iflag &= ~(termios.IXON | termios.IXOFF) - # rtscts - if hasattr(termios, 'CRTSCTS'): - if self._rtscts: - cflag |= (termios.CRTSCTS) - else: - cflag &= ~(termios.CRTSCTS) - elif hasattr(termios, 'CNEW_RTSCTS'): # try it with alternate constant name - if self._rtscts: - cflag |= (termios.CNEW_RTSCTS) - else: - cflag &= ~(termios.CNEW_RTSCTS) - # XXX should there be a warning if setting up rtscts (and xonxoff etc) fails?? - - # buffer - # vmin "minimal number of characters to be read. 0 for non blocking" - if vmin < 0 or vmin > 255: - raise ValueError('Invalid vmin: {!r}'.format(vmin)) - cc[termios.VMIN] = vmin - # vtime - if vtime < 0 or vtime > 255: - raise ValueError('Invalid vtime: {!r}'.format(vtime)) - cc[termios.VTIME] = vtime - # activate settings - if force_update or [iflag, oflag, cflag, lflag, ispeed, ospeed, cc] != orig_attr: - termios.tcsetattr( - self.fd, - termios.TCSANOW, - [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]) - - # apply custom baud rate, if any - if custom_baud is not None: - self._set_special_baudrate(custom_baud) - - if self._rs485_mode is not None: - self._set_rs485_mode(self._rs485_mode) - - def close(self): - """Close port""" - if self.is_open: - if self.fd is not None: - os.close(self.fd) - self.fd = None - os.close(self.pipe_abort_read_w) - os.close(self.pipe_abort_read_r) - os.close(self.pipe_abort_write_w) - os.close(self.pipe_abort_write_r) - self.pipe_abort_read_r, self.pipe_abort_read_w = None, None - self.pipe_abort_write_r, self.pipe_abort_write_w = None, None - self.is_open = False - - # - - - - - - - - - - - - - - - - - - - - - - - - - - @property - def in_waiting(self): - """Return the number of bytes currently in the input buffer.""" - #~ s = fcntl.ioctl(self.fd, termios.FIONREAD, TIOCM_zero_str) - s = fcntl.ioctl(self.fd, TIOCINQ, TIOCM_zero_str) - return struct.unpack('I', s)[0] - - # select based implementation, proved to work on many systems - def read(self, size=1): - """\ - Read size bytes from the serial port. If a timeout is set it may - return less characters as requested. With no timeout it will block - until the requested number of bytes is read. - """ - if not self.is_open: - raise PortNotOpenError() - read = bytearray() - timeout = Timeout(self._timeout) - while len(read) < size: - try: - ready, _, _ = select.select([self.fd, self.pipe_abort_read_r], [], [], timeout.time_left()) - if self.pipe_abort_read_r in ready: - os.read(self.pipe_abort_read_r, 1000) - break - # If select was used with a timeout, and the timeout occurs, it - # returns with empty lists -> thus abort read operation. - # For timeout == 0 (non-blocking operation) also abort when - # there is nothing to read. - if not ready: - break # timeout - buf = os.read(self.fd, size - len(read)) - except OSError as e: - # this is for Python 3.x where select.error is a subclass of - # OSError ignore BlockingIOErrors and EINTR. other errors are shown - # https://www.python.org/dev/peps/pep-0475. - if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): - raise SerialException('read failed: {}'.format(e)) - except select.error as e: - # this is for Python 2.x - # ignore BlockingIOErrors and EINTR. all errors are shown - # see also http://www.python.org/dev/peps/pep-3151/#select - if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): - raise SerialException('read failed: {}'.format(e)) - else: - # read should always return some data as select reported it was - # ready to read when we get to this point. - if not buf: - # Disconnected devices, at least on Linux, show the - # behavior that they are always ready to read immediately - # but reading returns nothing. - raise SerialException( - 'device reports readiness to read but returned no data ' - '(device disconnected or multiple access on port?)') - read.extend(buf) - - if timeout.expired(): - break - return bytes(read) - - def cancel_read(self): - if self.is_open: - os.write(self.pipe_abort_read_w, b"x") - - def cancel_write(self): - if self.is_open: - os.write(self.pipe_abort_write_w, b"x") - - def write(self, data): - """Output the given byte string over the serial port.""" - if not self.is_open: - raise PortNotOpenError() - d = to_bytes(data) - tx_len = length = len(d) - timeout = Timeout(self._write_timeout) - while tx_len > 0: - try: - n = os.write(self.fd, d) - if timeout.is_non_blocking: - # Zero timeout indicates non-blocking - simply return the - # number of bytes of data actually written - return n - elif not timeout.is_infinite: - # when timeout is set, use select to wait for being ready - # with the time left as timeout - if timeout.expired(): - raise SerialTimeoutException('Write timeout') - abort, ready, _ = select.select([self.pipe_abort_write_r], [self.fd], [], timeout.time_left()) - if abort: - os.read(self.pipe_abort_write_r, 1000) - break - if not ready: - raise SerialTimeoutException('Write timeout') - else: - assert timeout.time_left() is None - # wait for write operation - abort, ready, _ = select.select([self.pipe_abort_write_r], [self.fd], [], None) - if abort: - os.read(self.pipe_abort_write_r, 1) - break - if not ready: - raise SerialException('write failed (select)') - d = d[n:] - tx_len -= n - except SerialException: - raise - except OSError as e: - # this is for Python 3.x where select.error is a subclass of - # OSError ignore BlockingIOErrors and EINTR. other errors are shown - # https://www.python.org/dev/peps/pep-0475. - if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): - raise SerialException('write failed: {}'.format(e)) - except select.error as e: - # this is for Python 2.x - # ignore BlockingIOErrors and EINTR. all errors are shown - # see also http://www.python.org/dev/peps/pep-3151/#select - if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR): - raise SerialException('write failed: {}'.format(e)) - if not timeout.is_non_blocking and timeout.expired(): - raise SerialTimeoutException('Write timeout') - return length - len(d) - - def flush(self): - """\ - Flush of file like objects. In this case, wait until all data - is written. - """ - if not self.is_open: - raise PortNotOpenError() - termios.tcdrain(self.fd) - - def _reset_input_buffer(self): - """Clear input buffer, discarding all that is in the buffer.""" - termios.tcflush(self.fd, termios.TCIFLUSH) - - def reset_input_buffer(self): - """Clear input buffer, discarding all that is in the buffer.""" - if not self.is_open: - raise PortNotOpenError() - self._reset_input_buffer() - - def reset_output_buffer(self): - """\ - Clear output buffer, aborting the current output and discarding all - that is in the buffer. - """ - if not self.is_open: - raise PortNotOpenError() - termios.tcflush(self.fd, termios.TCOFLUSH) - - def send_break(self, duration=0.25): - """\ - Send break condition. Timed, returns to idle state after given - duration. - """ - if not self.is_open: - raise PortNotOpenError() - termios.tcsendbreak(self.fd, int(duration / 0.25)) - - def _update_rts_state(self): - """Set terminal status line: Request To Send""" - if self._rts_state: - fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_RTS_str) - else: - fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_RTS_str) - - def _update_dtr_state(self): - """Set terminal status line: Data Terminal Ready""" - if self._dtr_state: - fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_DTR_str) - else: - fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_DTR_str) - - @property - def cts(self): - """Read terminal status line: Clear To Send""" - if not self.is_open: - raise PortNotOpenError() - s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) - return struct.unpack('I', s)[0] & TIOCM_CTS != 0 - - @property - def dsr(self): - """Read terminal status line: Data Set Ready""" - if not self.is_open: - raise PortNotOpenError() - s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) - return struct.unpack('I', s)[0] & TIOCM_DSR != 0 - - @property - def ri(self): - """Read terminal status line: Ring Indicator""" - if not self.is_open: - raise PortNotOpenError() - s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) - return struct.unpack('I', s)[0] & TIOCM_RI != 0 - - @property - def cd(self): - """Read terminal status line: Carrier Detect""" - if not self.is_open: - raise PortNotOpenError() - s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str) - return struct.unpack('I', s)[0] & TIOCM_CD != 0 - - # - - platform specific - - - - - - @property - def out_waiting(self): - """Return the number of bytes currently in the output buffer.""" - #~ s = fcntl.ioctl(self.fd, termios.FIONREAD, TIOCM_zero_str) - s = fcntl.ioctl(self.fd, TIOCOUTQ, TIOCM_zero_str) - return struct.unpack('I', s)[0] - - def fileno(self): - """\ - For easier use of the serial port instance with select. - WARNING: this function is not portable to different platforms! - """ - if not self.is_open: - raise PortNotOpenError() - return self.fd - - def set_input_flow_control(self, enable=True): - """\ - Manually control flow - when software flow control is enabled. - This will send XON (true) or XOFF (false) to the other device. - WARNING: this function is not portable to different platforms! - """ - if not self.is_open: - raise PortNotOpenError() - if enable: - termios.tcflow(self.fd, termios.TCION) - else: - termios.tcflow(self.fd, termios.TCIOFF) - - def set_output_flow_control(self, enable=True): - """\ - Manually control flow of outgoing data - when hardware or software flow - control is enabled. - WARNING: this function is not portable to different platforms! - """ - if not self.is_open: - raise PortNotOpenError() - if enable: - termios.tcflow(self.fd, termios.TCOON) - else: - termios.tcflow(self.fd, termios.TCOOFF) - - def nonblocking(self): - """DEPRECATED - has no use""" - import warnings - warnings.warn("nonblocking() has no effect, already nonblocking", DeprecationWarning) - - -class PosixPollSerial(Serial): - """\ - Poll based read implementation. Not all systems support poll properly. - However this one has better handling of errors, such as a device - disconnecting while it's in use (e.g. USB-serial unplugged). - """ - - def read(self, size=1): - """\ - Read size bytes from the serial port. If a timeout is set it may - return less characters as requested. With no timeout it will block - until the requested number of bytes is read. - """ - if not self.is_open: - raise PortNotOpenError() - read = bytearray() - timeout = Timeout(self._timeout) - poll = select.poll() - poll.register(self.fd, select.POLLIN | select.POLLERR | select.POLLHUP | select.POLLNVAL) - poll.register(self.pipe_abort_read_r, select.POLLIN | select.POLLERR | select.POLLHUP | select.POLLNVAL) - if size > 0: - while len(read) < size: - # print "\tread(): size",size, "have", len(read) #debug - # wait until device becomes ready to read (or something fails) - for fd, event in poll.poll(None if timeout.is_infinite else (timeout.time_left() * 1000)): - if fd == self.pipe_abort_read_r: - break - if event & (select.POLLERR | select.POLLHUP | select.POLLNVAL): - raise SerialException('device reports error (poll)') - # we don't care if it is select.POLLIN or timeout, that's - # handled below - if fd == self.pipe_abort_read_r: - os.read(self.pipe_abort_read_r, 1000) - break - buf = os.read(self.fd, size - len(read)) - read.extend(buf) - if timeout.expired() \ - or (self._inter_byte_timeout is not None and self._inter_byte_timeout > 0) and not buf: - break # early abort on timeout - return bytes(read) - - -class VTIMESerial(Serial): - """\ - Implement timeout using vtime of tty device instead of using select. - This means that no inter character timeout can be specified and that - the error handling is degraded. - - Overall timeout is disabled when inter-character timeout is used. - - Note that this implementation does NOT support cancel_read(), it will - just ignore that. - """ - - def _reconfigure_port(self, force_update=True): - """Set communication parameters on opened port.""" - super(VTIMESerial, self)._reconfigure_port() - fcntl.fcntl(self.fd, fcntl.F_SETFL, 0) # clear O_NONBLOCK - - if self._inter_byte_timeout is not None: - vmin = 1 - vtime = int(self._inter_byte_timeout * 10) - elif self._timeout is None: - vmin = 1 - vtime = 0 - else: - vmin = 0 - vtime = int(self._timeout * 10) - try: - orig_attr = termios.tcgetattr(self.fd) - iflag, oflag, cflag, lflag, ispeed, ospeed, cc = orig_attr - except termios.error as msg: # if a port is nonexistent but has a /dev file, it'll fail here - raise serial.SerialException("Could not configure port: {}".format(msg)) - - if vtime < 0 or vtime > 255: - raise ValueError('Invalid vtime: {!r}'.format(vtime)) - cc[termios.VTIME] = vtime - cc[termios.VMIN] = vmin - - termios.tcsetattr( - self.fd, - termios.TCSANOW, - [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]) - - def read(self, size=1): - """\ - Read size bytes from the serial port. If a timeout is set it may - return less characters as requested. With no timeout it will block - until the requested number of bytes is read. - """ - if not self.is_open: - raise PortNotOpenError() - read = bytearray() - while len(read) < size: - buf = os.read(self.fd, size - len(read)) - if not buf: - break - read.extend(buf) - return bytes(read) - - # hack to make hasattr return false - cancel_read = property() diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/serialutil.py b/plotter-app/venv/lib/python3.8/site-packages/serial/serialutil.py deleted file mode 100644 index 789219e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/serialutil.py +++ /dev/null @@ -1,697 +0,0 @@ -#! python -# -# Base class and support functions used by various backends. -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# (C) 2001-2020 Chris Liechti -# -# SPDX-License-Identifier: BSD-3-Clause - -from __future__ import absolute_import - -import io -import time - -# ``memoryview`` was introduced in Python 2.7 and ``bytes(some_memoryview)`` -# isn't returning the contents (very unfortunate). Therefore we need special -# cases and test for it. Ensure that there is a ``memoryview`` object for older -# Python versions. This is easier than making every test dependent on its -# existence. -try: - memoryview -except (NameError, AttributeError): - # implementation does not matter as we do not really use it. - # it just must not inherit from something else we might care for. - class memoryview(object): # pylint: disable=redefined-builtin,invalid-name - pass - -try: - unicode -except (NameError, AttributeError): - unicode = str # for Python 3, pylint: disable=redefined-builtin,invalid-name - -try: - basestring -except (NameError, AttributeError): - basestring = (str,) # for Python 3, pylint: disable=redefined-builtin,invalid-name - - -# "for byte in data" fails for python3 as it returns ints instead of bytes -def iterbytes(b): - """Iterate over bytes, returning bytes instead of ints (python3)""" - if isinstance(b, memoryview): - b = b.tobytes() - i = 0 - while True: - a = b[i:i + 1] - i += 1 - if a: - yield a - else: - break - - -# all Python versions prior 3.x convert ``str([17])`` to '[17]' instead of '\x11' -# so a simple ``bytes(sequence)`` doesn't work for all versions -def to_bytes(seq): - """convert a sequence to a bytes type""" - if isinstance(seq, bytes): - return seq - elif isinstance(seq, bytearray): - return bytes(seq) - elif isinstance(seq, memoryview): - return seq.tobytes() - elif isinstance(seq, unicode): - raise TypeError('unicode strings are not supported, please encode to bytes: {!r}'.format(seq)) - else: - # handle list of integers and bytes (one or more items) for Python 2 and 3 - return bytes(bytearray(seq)) - - -# create control bytes -XON = to_bytes([17]) -XOFF = to_bytes([19]) - -CR = to_bytes([13]) -LF = to_bytes([10]) - - -PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE = 'N', 'E', 'O', 'M', 'S' -STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO = (1, 1.5, 2) -FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5, 6, 7, 8) - -PARITY_NAMES = { - PARITY_NONE: 'None', - PARITY_EVEN: 'Even', - PARITY_ODD: 'Odd', - PARITY_MARK: 'Mark', - PARITY_SPACE: 'Space', -} - - -class SerialException(IOError): - """Base class for serial port related exceptions.""" - - -class SerialTimeoutException(SerialException): - """Write timeouts give an exception""" - - -class PortNotOpenError(SerialException): - """Port is not open""" - def __init__(self): - super(PortNotOpenError, self).__init__('Attempting to use a port that is not open') - - -class Timeout(object): - """\ - Abstraction for timeout operations. Using time.monotonic() if available - or time.time() in all other cases. - - The class can also be initialized with 0 or None, in order to support - non-blocking and fully blocking I/O operations. The attributes - is_non_blocking and is_infinite are set accordingly. - """ - if hasattr(time, 'monotonic'): - # Timeout implementation with time.monotonic(). This function is only - # supported by Python 3.3 and above. It returns a time in seconds - # (float) just as time.time(), but is not affected by system clock - # adjustments. - TIME = time.monotonic - else: - # Timeout implementation with time.time(). This is compatible with all - # Python versions but has issues if the clock is adjusted while the - # timeout is running. - TIME = time.time - - def __init__(self, duration): - """Initialize a timeout with given duration""" - self.is_infinite = (duration is None) - self.is_non_blocking = (duration == 0) - self.duration = duration - if duration is not None: - self.target_time = self.TIME() + duration - else: - self.target_time = None - - def expired(self): - """Return a boolean, telling if the timeout has expired""" - return self.target_time is not None and self.time_left() <= 0 - - def time_left(self): - """Return how many seconds are left until the timeout expires""" - if self.is_non_blocking: - return 0 - elif self.is_infinite: - return None - else: - delta = self.target_time - self.TIME() - if delta > self.duration: - # clock jumped, recalculate - self.target_time = self.TIME() + self.duration - return self.duration - else: - return max(0, delta) - - def restart(self, duration): - """\ - Restart a timeout, only supported if a timeout was already set up - before. - """ - self.duration = duration - self.target_time = self.TIME() + duration - - -class SerialBase(io.RawIOBase): - """\ - Serial port base class. Provides __init__ function and properties to - get/set port settings. - """ - - # default values, may be overridden in subclasses that do not support all values - BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000, - 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000, - 3000000, 3500000, 4000000) - BYTESIZES = (FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS) - PARITIES = (PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE) - STOPBITS = (STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO) - - def __init__(self, - port=None, - baudrate=9600, - bytesize=EIGHTBITS, - parity=PARITY_NONE, - stopbits=STOPBITS_ONE, - timeout=None, - xonxoff=False, - rtscts=False, - write_timeout=None, - dsrdtr=False, - inter_byte_timeout=None, - exclusive=None, - **kwargs): - """\ - Initialize comm port object. If a "port" is given, then the port will be - opened immediately. Otherwise a Serial port object in closed state - is returned. - """ - - self.is_open = False - self.portstr = None - self.name = None - # correct values are assigned below through properties - self._port = None - self._baudrate = None - self._bytesize = None - self._parity = None - self._stopbits = None - self._timeout = None - self._write_timeout = None - self._xonxoff = None - self._rtscts = None - self._dsrdtr = None - self._inter_byte_timeout = None - self._rs485_mode = None # disabled by default - self._rts_state = True - self._dtr_state = True - self._break_state = False - self._exclusive = None - - # assign values using get/set methods using the properties feature - self.port = port - self.baudrate = baudrate - self.bytesize = bytesize - self.parity = parity - self.stopbits = stopbits - self.timeout = timeout - self.write_timeout = write_timeout - self.xonxoff = xonxoff - self.rtscts = rtscts - self.dsrdtr = dsrdtr - self.inter_byte_timeout = inter_byte_timeout - self.exclusive = exclusive - - # watch for backward compatible kwargs - if 'writeTimeout' in kwargs: - self.write_timeout = kwargs.pop('writeTimeout') - if 'interCharTimeout' in kwargs: - self.inter_byte_timeout = kwargs.pop('interCharTimeout') - if kwargs: - raise ValueError('unexpected keyword arguments: {!r}'.format(kwargs)) - - if port is not None: - self.open() - - # - - - - - - - - - - - - - - - - - - - - - - - - - - # to be implemented by subclasses: - # def open(self): - # def close(self): - - # - - - - - - - - - - - - - - - - - - - - - - - - - - @property - def port(self): - """\ - Get the current port setting. The value that was passed on init or using - setPort() is passed back. - """ - return self._port - - @port.setter - def port(self, port): - """\ - Change the port. - """ - if port is not None and not isinstance(port, basestring): - raise ValueError('"port" must be None or a string, not {}'.format(type(port))) - was_open = self.is_open - if was_open: - self.close() - self.portstr = port - self._port = port - self.name = self.portstr - if was_open: - self.open() - - @property - def baudrate(self): - """Get the current baud rate setting.""" - return self._baudrate - - @baudrate.setter - def baudrate(self, baudrate): - """\ - Change baud rate. It raises a ValueError if the port is open and the - baud rate is not possible. If the port is closed, then the value is - accepted and the exception is raised when the port is opened. - """ - try: - b = int(baudrate) - except TypeError: - raise ValueError("Not a valid baudrate: {!r}".format(baudrate)) - else: - if b < 0: - raise ValueError("Not a valid baudrate: {!r}".format(baudrate)) - self._baudrate = b - if self.is_open: - self._reconfigure_port() - - @property - def bytesize(self): - """Get the current byte size setting.""" - return self._bytesize - - @bytesize.setter - def bytesize(self, bytesize): - """Change byte size.""" - if bytesize not in self.BYTESIZES: - raise ValueError("Not a valid byte size: {!r}".format(bytesize)) - self._bytesize = bytesize - if self.is_open: - self._reconfigure_port() - - @property - def exclusive(self): - """Get the current exclusive access setting.""" - return self._exclusive - - @exclusive.setter - def exclusive(self, exclusive): - """Change the exclusive access setting.""" - self._exclusive = exclusive - if self.is_open: - self._reconfigure_port() - - @property - def parity(self): - """Get the current parity setting.""" - return self._parity - - @parity.setter - def parity(self, parity): - """Change parity setting.""" - if parity not in self.PARITIES: - raise ValueError("Not a valid parity: {!r}".format(parity)) - self._parity = parity - if self.is_open: - self._reconfigure_port() - - @property - def stopbits(self): - """Get the current stop bits setting.""" - return self._stopbits - - @stopbits.setter - def stopbits(self, stopbits): - """Change stop bits size.""" - if stopbits not in self.STOPBITS: - raise ValueError("Not a valid stop bit size: {!r}".format(stopbits)) - self._stopbits = stopbits - if self.is_open: - self._reconfigure_port() - - @property - def timeout(self): - """Get the current timeout setting.""" - return self._timeout - - @timeout.setter - def timeout(self, timeout): - """Change timeout setting.""" - if timeout is not None: - try: - timeout + 1 # test if it's a number, will throw a TypeError if not... - except TypeError: - raise ValueError("Not a valid timeout: {!r}".format(timeout)) - if timeout < 0: - raise ValueError("Not a valid timeout: {!r}".format(timeout)) - self._timeout = timeout - if self.is_open: - self._reconfigure_port() - - @property - def write_timeout(self): - """Get the current timeout setting.""" - return self._write_timeout - - @write_timeout.setter - def write_timeout(self, timeout): - """Change timeout setting.""" - if timeout is not None: - if timeout < 0: - raise ValueError("Not a valid timeout: {!r}".format(timeout)) - try: - timeout + 1 # test if it's a number, will throw a TypeError if not... - except TypeError: - raise ValueError("Not a valid timeout: {!r}".format(timeout)) - - self._write_timeout = timeout - if self.is_open: - self._reconfigure_port() - - @property - def inter_byte_timeout(self): - """Get the current inter-character timeout setting.""" - return self._inter_byte_timeout - - @inter_byte_timeout.setter - def inter_byte_timeout(self, ic_timeout): - """Change inter-byte timeout setting.""" - if ic_timeout is not None: - if ic_timeout < 0: - raise ValueError("Not a valid timeout: {!r}".format(ic_timeout)) - try: - ic_timeout + 1 # test if it's a number, will throw a TypeError if not... - except TypeError: - raise ValueError("Not a valid timeout: {!r}".format(ic_timeout)) - - self._inter_byte_timeout = ic_timeout - if self.is_open: - self._reconfigure_port() - - @property - def xonxoff(self): - """Get the current XON/XOFF setting.""" - return self._xonxoff - - @xonxoff.setter - def xonxoff(self, xonxoff): - """Change XON/XOFF setting.""" - self._xonxoff = xonxoff - if self.is_open: - self._reconfigure_port() - - @property - def rtscts(self): - """Get the current RTS/CTS flow control setting.""" - return self._rtscts - - @rtscts.setter - def rtscts(self, rtscts): - """Change RTS/CTS flow control setting.""" - self._rtscts = rtscts - if self.is_open: - self._reconfigure_port() - - @property - def dsrdtr(self): - """Get the current DSR/DTR flow control setting.""" - return self._dsrdtr - - @dsrdtr.setter - def dsrdtr(self, dsrdtr=None): - """Change DsrDtr flow control setting.""" - if dsrdtr is None: - # if not set, keep backwards compatibility and follow rtscts setting - self._dsrdtr = self._rtscts - else: - # if defined independently, follow its value - self._dsrdtr = dsrdtr - if self.is_open: - self._reconfigure_port() - - @property - def rts(self): - return self._rts_state - - @rts.setter - def rts(self, value): - self._rts_state = value - if self.is_open: - self._update_rts_state() - - @property - def dtr(self): - return self._dtr_state - - @dtr.setter - def dtr(self, value): - self._dtr_state = value - if self.is_open: - self._update_dtr_state() - - @property - def break_condition(self): - return self._break_state - - @break_condition.setter - def break_condition(self, value): - self._break_state = value - if self.is_open: - self._update_break_state() - - # - - - - - - - - - - - - - - - - - - - - - - - - - # functions useful for RS-485 adapters - - @property - def rs485_mode(self): - """\ - Enable RS485 mode and apply new settings, set to None to disable. - See serial.rs485.RS485Settings for more info about the value. - """ - return self._rs485_mode - - @rs485_mode.setter - def rs485_mode(self, rs485_settings): - self._rs485_mode = rs485_settings - if self.is_open: - self._reconfigure_port() - - # - - - - - - - - - - - - - - - - - - - - - - - - - - _SAVED_SETTINGS = ('baudrate', 'bytesize', 'parity', 'stopbits', 'xonxoff', - 'dsrdtr', 'rtscts', 'timeout', 'write_timeout', - 'inter_byte_timeout') - - def get_settings(self): - """\ - Get current port settings as a dictionary. For use with - apply_settings(). - """ - return dict([(key, getattr(self, '_' + key)) for key in self._SAVED_SETTINGS]) - - def apply_settings(self, d): - """\ - Apply stored settings from a dictionary returned from - get_settings(). It's allowed to delete keys from the dictionary. These - values will simply left unchanged. - """ - for key in self._SAVED_SETTINGS: - if key in d and d[key] != getattr(self, '_' + key): # check against internal "_" value - setattr(self, key, d[key]) # set non "_" value to use properties write function - - # - - - - - - - - - - - - - - - - - - - - - - - - - - def __repr__(self): - """String representation of the current port settings and its state.""" - return '{name}(port={p.portstr!r}, ' \ - 'baudrate={p.baudrate!r}, bytesize={p.bytesize!r}, parity={p.parity!r}, ' \ - 'stopbits={p.stopbits!r}, timeout={p.timeout!r}, xonxoff={p.xonxoff!r}, ' \ - 'rtscts={p.rtscts!r}, dsrdtr={p.dsrdtr!r})'.format( - name=self.__class__.__name__, id=id(self), p=self) - - # - - - - - - - - - - - - - - - - - - - - - - - - - # compatibility with io library - # pylint: disable=invalid-name,missing-docstring - - def readable(self): - return True - - def writable(self): - return True - - def seekable(self): - return False - - def readinto(self, b): - data = self.read(len(b)) - n = len(data) - try: - b[:n] = data - except TypeError as err: - import array - if not isinstance(b, array.array): - raise err - b[:n] = array.array('b', data) - return n - - # - - - - - - - - - - - - - - - - - - - - - - - - - # context manager - - def __enter__(self): - if self._port is not None and not self.is_open: - self.open() - return self - - def __exit__(self, *args, **kwargs): - self.close() - - # - - - - - - - - - - - - - - - - - - - - - - - - - - def send_break(self, duration=0.25): - """\ - Send break condition. Timed, returns to idle state after given - duration. - """ - if not self.is_open: - raise PortNotOpenError() - self.break_condition = True - time.sleep(duration) - self.break_condition = False - - # - - - - - - - - - - - - - - - - - - - - - - - - - # backwards compatibility / deprecated functions - - def flushInput(self): - self.reset_input_buffer() - - def flushOutput(self): - self.reset_output_buffer() - - def inWaiting(self): - return self.in_waiting - - def sendBreak(self, duration=0.25): - self.send_break(duration) - - def setRTS(self, value=1): - self.rts = value - - def setDTR(self, value=1): - self.dtr = value - - def getCTS(self): - return self.cts - - def getDSR(self): - return self.dsr - - def getRI(self): - return self.ri - - def getCD(self): - return self.cd - - def setPort(self, port): - self.port = port - - @property - def writeTimeout(self): - return self.write_timeout - - @writeTimeout.setter - def writeTimeout(self, timeout): - self.write_timeout = timeout - - @property - def interCharTimeout(self): - return self.inter_byte_timeout - - @interCharTimeout.setter - def interCharTimeout(self, interCharTimeout): - self.inter_byte_timeout = interCharTimeout - - def getSettingsDict(self): - return self.get_settings() - - def applySettingsDict(self, d): - self.apply_settings(d) - - def isOpen(self): - return self.is_open - - # - - - - - - - - - - - - - - - - - - - - - - - - - # additional functionality - - def read_all(self): - """\ - Read all bytes currently available in the buffer of the OS. - """ - return self.read(self.in_waiting) - - def read_until(self, expected=LF, size=None): - """\ - Read until an expected sequence is found ('\n' by default), the size - is exceeded or until timeout occurs. - """ - lenterm = len(expected) - line = bytearray() - timeout = Timeout(self._timeout) - while True: - c = self.read(1) - if c: - line += c - if line[-lenterm:] == expected: - break - if size is not None and len(line) >= size: - break - else: - break - if timeout.expired(): - break - return bytes(line) - - def iread_until(self, *args, **kwargs): - """\ - Read lines, implemented as generator. It will raise StopIteration on - timeout (empty read). - """ - while True: - line = self.read_until(*args, **kwargs) - if not line: - break - yield line - - -# - - - - - - - - - - - - - - - - - - - - - - - - - -if __name__ == '__main__': - import sys - s = SerialBase() - sys.stdout.write('port name: {}\n'.format(s.name)) - sys.stdout.write('baud rates: {}\n'.format(s.BAUDRATES)) - sys.stdout.write('byte sizes: {}\n'.format(s.BYTESIZES)) - sys.stdout.write('parities: {}\n'.format(s.PARITIES)) - sys.stdout.write('stop bits: {}\n'.format(s.STOPBITS)) - sys.stdout.write('{}\n'.format(s)) diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/serialwin32.py b/plotter-app/venv/lib/python3.8/site-packages/serial/serialwin32.py deleted file mode 100644 index e7da929..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/serialwin32.py +++ /dev/null @@ -1,477 +0,0 @@ -#! python -# -# backend for Windows ("win32" incl. 32/64 bit support) -# -# (C) 2001-2020 Chris Liechti -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# SPDX-License-Identifier: BSD-3-Clause -# -# Initial patch to use ctypes by Giovanni Bajo - -from __future__ import absolute_import - -# pylint: disable=invalid-name,too-few-public-methods -import ctypes -import time -from serial import win32 - -import serial -from serial.serialutil import SerialBase, SerialException, to_bytes, PortNotOpenError, SerialTimeoutException - - -class Serial(SerialBase): - """Serial port implementation for Win32 based on ctypes.""" - - BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200) - - def __init__(self, *args, **kwargs): - self._port_handle = None - self._overlapped_read = None - self._overlapped_write = None - super(Serial, self).__init__(*args, **kwargs) - - def open(self): - """\ - Open port with current settings. This may throw a SerialException - if the port cannot be opened. - """ - if self._port is None: - raise SerialException("Port must be configured before it can be used.") - if self.is_open: - raise SerialException("Port is already open.") - # the "\\.\COMx" format is required for devices other than COM1-COM8 - # not all versions of windows seem to support this properly - # so that the first few ports are used with the DOS device name - port = self.name - try: - if port.upper().startswith('COM') and int(port[3:]) > 8: - port = '\\\\.\\' + port - except ValueError: - # for like COMnotanumber - pass - self._port_handle = win32.CreateFile( - port, - win32.GENERIC_READ | win32.GENERIC_WRITE, - 0, # exclusive access - None, # no security - win32.OPEN_EXISTING, - win32.FILE_ATTRIBUTE_NORMAL | win32.FILE_FLAG_OVERLAPPED, - 0) - if self._port_handle == win32.INVALID_HANDLE_VALUE: - self._port_handle = None # 'cause __del__ is called anyway - raise SerialException("could not open port {!r}: {!r}".format(self.portstr, ctypes.WinError())) - - try: - self._overlapped_read = win32.OVERLAPPED() - self._overlapped_read.hEvent = win32.CreateEvent(None, 1, 0, None) - self._overlapped_write = win32.OVERLAPPED() - #~ self._overlapped_write.hEvent = win32.CreateEvent(None, 1, 0, None) - self._overlapped_write.hEvent = win32.CreateEvent(None, 0, 0, None) - - # Setup a 4k buffer - win32.SetupComm(self._port_handle, 4096, 4096) - - # Save original timeout values: - self._orgTimeouts = win32.COMMTIMEOUTS() - win32.GetCommTimeouts(self._port_handle, ctypes.byref(self._orgTimeouts)) - - self._reconfigure_port() - - # Clear buffers: - # Remove anything that was there - win32.PurgeComm( - self._port_handle, - win32.PURGE_TXCLEAR | win32.PURGE_TXABORT | - win32.PURGE_RXCLEAR | win32.PURGE_RXABORT) - except: - try: - self._close() - except: - # ignore any exception when closing the port - # also to keep original exception that happened when setting up - pass - self._port_handle = None - raise - else: - self.is_open = True - - def _reconfigure_port(self): - """Set communication parameters on opened port.""" - if not self._port_handle: - raise SerialException("Can only operate on a valid port handle") - - # Set Windows timeout values - # timeouts is a tuple with the following items: - # (ReadIntervalTimeout,ReadTotalTimeoutMultiplier, - # ReadTotalTimeoutConstant,WriteTotalTimeoutMultiplier, - # WriteTotalTimeoutConstant) - timeouts = win32.COMMTIMEOUTS() - if self._timeout is None: - pass # default of all zeros is OK - elif self._timeout == 0: - timeouts.ReadIntervalTimeout = win32.MAXDWORD - else: - timeouts.ReadTotalTimeoutConstant = max(int(self._timeout * 1000), 1) - if self._timeout != 0 and self._inter_byte_timeout is not None: - timeouts.ReadIntervalTimeout = max(int(self._inter_byte_timeout * 1000), 1) - - if self._write_timeout is None: - pass - elif self._write_timeout == 0: - timeouts.WriteTotalTimeoutConstant = win32.MAXDWORD - else: - timeouts.WriteTotalTimeoutConstant = max(int(self._write_timeout * 1000), 1) - win32.SetCommTimeouts(self._port_handle, ctypes.byref(timeouts)) - - win32.SetCommMask(self._port_handle, win32.EV_ERR) - - # Setup the connection info. - # Get state and modify it: - comDCB = win32.DCB() - win32.GetCommState(self._port_handle, ctypes.byref(comDCB)) - comDCB.BaudRate = self._baudrate - - if self._bytesize == serial.FIVEBITS: - comDCB.ByteSize = 5 - elif self._bytesize == serial.SIXBITS: - comDCB.ByteSize = 6 - elif self._bytesize == serial.SEVENBITS: - comDCB.ByteSize = 7 - elif self._bytesize == serial.EIGHTBITS: - comDCB.ByteSize = 8 - else: - raise ValueError("Unsupported number of data bits: {!r}".format(self._bytesize)) - - if self._parity == serial.PARITY_NONE: - comDCB.Parity = win32.NOPARITY - comDCB.fParity = 0 # Disable Parity Check - elif self._parity == serial.PARITY_EVEN: - comDCB.Parity = win32.EVENPARITY - comDCB.fParity = 1 # Enable Parity Check - elif self._parity == serial.PARITY_ODD: - comDCB.Parity = win32.ODDPARITY - comDCB.fParity = 1 # Enable Parity Check - elif self._parity == serial.PARITY_MARK: - comDCB.Parity = win32.MARKPARITY - comDCB.fParity = 1 # Enable Parity Check - elif self._parity == serial.PARITY_SPACE: - comDCB.Parity = win32.SPACEPARITY - comDCB.fParity = 1 # Enable Parity Check - else: - raise ValueError("Unsupported parity mode: {!r}".format(self._parity)) - - if self._stopbits == serial.STOPBITS_ONE: - comDCB.StopBits = win32.ONESTOPBIT - elif self._stopbits == serial.STOPBITS_ONE_POINT_FIVE: - comDCB.StopBits = win32.ONE5STOPBITS - elif self._stopbits == serial.STOPBITS_TWO: - comDCB.StopBits = win32.TWOSTOPBITS - else: - raise ValueError("Unsupported number of stop bits: {!r}".format(self._stopbits)) - - comDCB.fBinary = 1 # Enable Binary Transmission - # Char. w/ Parity-Err are replaced with 0xff (if fErrorChar is set to TRUE) - if self._rs485_mode is None: - if self._rtscts: - comDCB.fRtsControl = win32.RTS_CONTROL_HANDSHAKE - else: - comDCB.fRtsControl = win32.RTS_CONTROL_ENABLE if self._rts_state else win32.RTS_CONTROL_DISABLE - comDCB.fOutxCtsFlow = self._rtscts - else: - # checks for unsupported settings - # XXX verify if platform really does not have a setting for those - if not self._rs485_mode.rts_level_for_tx: - raise ValueError( - 'Unsupported value for RS485Settings.rts_level_for_tx: {!r} (only True is allowed)'.format( - self._rs485_mode.rts_level_for_tx,)) - if self._rs485_mode.rts_level_for_rx: - raise ValueError( - 'Unsupported value for RS485Settings.rts_level_for_rx: {!r} (only False is allowed)'.format( - self._rs485_mode.rts_level_for_rx,)) - if self._rs485_mode.delay_before_tx is not None: - raise ValueError( - 'Unsupported value for RS485Settings.delay_before_tx: {!r} (only None is allowed)'.format( - self._rs485_mode.delay_before_tx,)) - if self._rs485_mode.delay_before_rx is not None: - raise ValueError( - 'Unsupported value for RS485Settings.delay_before_rx: {!r} (only None is allowed)'.format( - self._rs485_mode.delay_before_rx,)) - if self._rs485_mode.loopback: - raise ValueError( - 'Unsupported value for RS485Settings.loopback: {!r} (only False is allowed)'.format( - self._rs485_mode.loopback,)) - comDCB.fRtsControl = win32.RTS_CONTROL_TOGGLE - comDCB.fOutxCtsFlow = 0 - - if self._dsrdtr: - comDCB.fDtrControl = win32.DTR_CONTROL_HANDSHAKE - else: - comDCB.fDtrControl = win32.DTR_CONTROL_ENABLE if self._dtr_state else win32.DTR_CONTROL_DISABLE - comDCB.fOutxDsrFlow = self._dsrdtr - comDCB.fOutX = self._xonxoff - comDCB.fInX = self._xonxoff - comDCB.fNull = 0 - comDCB.fErrorChar = 0 - comDCB.fAbortOnError = 0 - comDCB.XonChar = serial.XON - comDCB.XoffChar = serial.XOFF - - if not win32.SetCommState(self._port_handle, ctypes.byref(comDCB)): - raise SerialException( - 'Cannot configure port, something went wrong. ' - 'Original message: {!r}'.format(ctypes.WinError())) - - #~ def __del__(self): - #~ self.close() - - def _close(self): - """internal close port helper""" - if self._port_handle is not None: - # Restore original timeout values: - win32.SetCommTimeouts(self._port_handle, self._orgTimeouts) - if self._overlapped_read is not None: - self.cancel_read() - win32.CloseHandle(self._overlapped_read.hEvent) - self._overlapped_read = None - if self._overlapped_write is not None: - self.cancel_write() - win32.CloseHandle(self._overlapped_write.hEvent) - self._overlapped_write = None - win32.CloseHandle(self._port_handle) - self._port_handle = None - - def close(self): - """Close port""" - if self.is_open: - self._close() - self.is_open = False - - # - - - - - - - - - - - - - - - - - - - - - - - - - - @property - def in_waiting(self): - """Return the number of bytes currently in the input buffer.""" - flags = win32.DWORD() - comstat = win32.COMSTAT() - if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)): - raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError())) - return comstat.cbInQue - - def read(self, size=1): - """\ - Read size bytes from the serial port. If a timeout is set it may - return less characters as requested. With no timeout it will block - until the requested number of bytes is read. - """ - if not self.is_open: - raise PortNotOpenError() - if size > 0: - win32.ResetEvent(self._overlapped_read.hEvent) - flags = win32.DWORD() - comstat = win32.COMSTAT() - if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)): - raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError())) - n = min(comstat.cbInQue, size) if self.timeout == 0 else size - if n > 0: - buf = ctypes.create_string_buffer(n) - rc = win32.DWORD() - read_ok = win32.ReadFile( - self._port_handle, - buf, - n, - ctypes.byref(rc), - ctypes.byref(self._overlapped_read)) - if not read_ok and win32.GetLastError() not in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING): - raise SerialException("ReadFile failed ({!r})".format(ctypes.WinError())) - result_ok = win32.GetOverlappedResult( - self._port_handle, - ctypes.byref(self._overlapped_read), - ctypes.byref(rc), - True) - if not result_ok: - if win32.GetLastError() != win32.ERROR_OPERATION_ABORTED: - raise SerialException("GetOverlappedResult failed ({!r})".format(ctypes.WinError())) - read = buf.raw[:rc.value] - else: - read = bytes() - else: - read = bytes() - return bytes(read) - - def write(self, data): - """Output the given byte string over the serial port.""" - if not self.is_open: - raise PortNotOpenError() - #~ if not isinstance(data, (bytes, bytearray)): - #~ raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data))) - # convert data (needed in case of memoryview instance: Py 3.1 io lib), ctypes doesn't like memoryview - data = to_bytes(data) - if data: - #~ win32event.ResetEvent(self._overlapped_write.hEvent) - n = win32.DWORD() - success = win32.WriteFile(self._port_handle, data, len(data), ctypes.byref(n), self._overlapped_write) - if self._write_timeout != 0: # if blocking (None) or w/ write timeout (>0) - if not success and win32.GetLastError() not in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING): - raise SerialException("WriteFile failed ({!r})".format(ctypes.WinError())) - - # Wait for the write to complete. - #~ win32.WaitForSingleObject(self._overlapped_write.hEvent, win32.INFINITE) - win32.GetOverlappedResult(self._port_handle, self._overlapped_write, ctypes.byref(n), True) - if win32.GetLastError() == win32.ERROR_OPERATION_ABORTED: - return n.value # canceled IO is no error - if n.value != len(data): - raise SerialTimeoutException('Write timeout') - return n.value - else: - errorcode = win32.ERROR_SUCCESS if success else win32.GetLastError() - if errorcode in (win32.ERROR_INVALID_USER_BUFFER, win32.ERROR_NOT_ENOUGH_MEMORY, - win32.ERROR_OPERATION_ABORTED): - return 0 - elif errorcode in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING): - # no info on true length provided by OS function in async mode - return len(data) - else: - raise SerialException("WriteFile failed ({!r})".format(ctypes.WinError())) - else: - return 0 - - def flush(self): - """\ - Flush of file like objects. In this case, wait until all data - is written. - """ - while self.out_waiting: - time.sleep(0.05) - # XXX could also use WaitCommEvent with mask EV_TXEMPTY, but it would - # require overlapped IO and it's also only possible to set a single mask - # on the port--- - - def reset_input_buffer(self): - """Clear input buffer, discarding all that is in the buffer.""" - if not self.is_open: - raise PortNotOpenError() - win32.PurgeComm(self._port_handle, win32.PURGE_RXCLEAR | win32.PURGE_RXABORT) - - def reset_output_buffer(self): - """\ - Clear output buffer, aborting the current output and discarding all - that is in the buffer. - """ - if not self.is_open: - raise PortNotOpenError() - win32.PurgeComm(self._port_handle, win32.PURGE_TXCLEAR | win32.PURGE_TXABORT) - - def _update_break_state(self): - """Set break: Controls TXD. When active, to transmitting is possible.""" - if not self.is_open: - raise PortNotOpenError() - if self._break_state: - win32.SetCommBreak(self._port_handle) - else: - win32.ClearCommBreak(self._port_handle) - - def _update_rts_state(self): - """Set terminal status line: Request To Send""" - if self._rts_state: - win32.EscapeCommFunction(self._port_handle, win32.SETRTS) - else: - win32.EscapeCommFunction(self._port_handle, win32.CLRRTS) - - def _update_dtr_state(self): - """Set terminal status line: Data Terminal Ready""" - if self._dtr_state: - win32.EscapeCommFunction(self._port_handle, win32.SETDTR) - else: - win32.EscapeCommFunction(self._port_handle, win32.CLRDTR) - - def _GetCommModemStatus(self): - if not self.is_open: - raise PortNotOpenError() - stat = win32.DWORD() - win32.GetCommModemStatus(self._port_handle, ctypes.byref(stat)) - return stat.value - - @property - def cts(self): - """Read terminal status line: Clear To Send""" - return win32.MS_CTS_ON & self._GetCommModemStatus() != 0 - - @property - def dsr(self): - """Read terminal status line: Data Set Ready""" - return win32.MS_DSR_ON & self._GetCommModemStatus() != 0 - - @property - def ri(self): - """Read terminal status line: Ring Indicator""" - return win32.MS_RING_ON & self._GetCommModemStatus() != 0 - - @property - def cd(self): - """Read terminal status line: Carrier Detect""" - return win32.MS_RLSD_ON & self._GetCommModemStatus() != 0 - - # - - platform specific - - - - - - def set_buffer_size(self, rx_size=4096, tx_size=None): - """\ - Recommend a buffer size to the driver (device driver can ignore this - value). Must be called after the port is opened. - """ - if tx_size is None: - tx_size = rx_size - win32.SetupComm(self._port_handle, rx_size, tx_size) - - def set_output_flow_control(self, enable=True): - """\ - Manually control flow - when software flow control is enabled. - This will do the same as if XON (true) or XOFF (false) are received - from the other device and control the transmission accordingly. - WARNING: this function is not portable to different platforms! - """ - if not self.is_open: - raise PortNotOpenError() - if enable: - win32.EscapeCommFunction(self._port_handle, win32.SETXON) - else: - win32.EscapeCommFunction(self._port_handle, win32.SETXOFF) - - @property - def out_waiting(self): - """Return how many bytes the in the outgoing buffer""" - flags = win32.DWORD() - comstat = win32.COMSTAT() - if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)): - raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError())) - return comstat.cbOutQue - - def _cancel_overlapped_io(self, overlapped): - """Cancel a blocking read operation, may be called from other thread""" - # check if read operation is pending - rc = win32.DWORD() - err = win32.GetOverlappedResult( - self._port_handle, - ctypes.byref(overlapped), - ctypes.byref(rc), - False) - if not err and win32.GetLastError() in (win32.ERROR_IO_PENDING, win32.ERROR_IO_INCOMPLETE): - # cancel, ignoring any errors (e.g. it may just have finished on its own) - win32.CancelIoEx(self._port_handle, overlapped) - - def cancel_read(self): - """Cancel a blocking read operation, may be called from other thread""" - self._cancel_overlapped_io(self._overlapped_read) - - def cancel_write(self): - """Cancel a blocking write operation, may be called from other thread""" - self._cancel_overlapped_io(self._overlapped_write) - - @SerialBase.exclusive.setter - def exclusive(self, exclusive): - """Change the exclusive access setting.""" - if exclusive is not None and not exclusive: - raise ValueError('win32 only supports exclusive access (not: {})'.format(exclusive)) - else: - serial.SerialBase.exclusive.__set__(self, exclusive) diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/threaded/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/serial/threaded/__init__.py deleted file mode 100644 index b8940b6..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/threaded/__init__.py +++ /dev/null @@ -1,297 +0,0 @@ -#!/usr/bin/env python3 -# -# Working with threading and pySerial -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# (C) 2015-2016 Chris Liechti -# -# SPDX-License-Identifier: BSD-3-Clause -"""\ -Support threading with serial ports. -""" -from __future__ import absolute_import - -import serial -import threading - - -class Protocol(object): - """\ - Protocol as used by the ReaderThread. This base class provides empty - implementations of all methods. - """ - - def connection_made(self, transport): - """Called when reader thread is started""" - - def data_received(self, data): - """Called with snippets received from the serial port""" - - def connection_lost(self, exc): - """\ - Called when the serial port is closed or the reader loop terminated - otherwise. - """ - if isinstance(exc, Exception): - raise exc - - -class Packetizer(Protocol): - """ - Read binary packets from serial port. Packets are expected to be terminated - with a TERMINATOR byte (null byte by default). - - The class also keeps track of the transport. - """ - - TERMINATOR = b'\0' - - def __init__(self): - self.buffer = bytearray() - self.transport = None - - def connection_made(self, transport): - """Store transport""" - self.transport = transport - - def connection_lost(self, exc): - """Forget transport""" - self.transport = None - super(Packetizer, self).connection_lost(exc) - - def data_received(self, data): - """Buffer received data, find TERMINATOR, call handle_packet""" - self.buffer.extend(data) - while self.TERMINATOR in self.buffer: - packet, self.buffer = self.buffer.split(self.TERMINATOR, 1) - self.handle_packet(packet) - - def handle_packet(self, packet): - """Process packets - to be overridden by subclassing""" - raise NotImplementedError('please implement functionality in handle_packet') - - -class FramedPacket(Protocol): - """ - Read binary packets. Packets are expected to have a start and stop marker. - - The class also keeps track of the transport. - """ - - START = b'(' - STOP = b')' - - def __init__(self): - self.packet = bytearray() - self.in_packet = False - self.transport = None - - def connection_made(self, transport): - """Store transport""" - self.transport = transport - - def connection_lost(self, exc): - """Forget transport""" - self.transport = None - self.in_packet = False - del self.packet[:] - super(FramedPacket, self).connection_lost(exc) - - def data_received(self, data): - """Find data enclosed in START/STOP, call handle_packet""" - for byte in serial.iterbytes(data): - if byte == self.START: - self.in_packet = True - elif byte == self.STOP: - self.in_packet = False - self.handle_packet(bytes(self.packet)) # make read-only copy - del self.packet[:] - elif self.in_packet: - self.packet.extend(byte) - else: - self.handle_out_of_packet_data(byte) - - def handle_packet(self, packet): - """Process packets - to be overridden by subclassing""" - raise NotImplementedError('please implement functionality in handle_packet') - - def handle_out_of_packet_data(self, data): - """Process data that is received outside of packets""" - pass - - -class LineReader(Packetizer): - """ - Read and write (Unicode) lines from/to serial port. - The encoding is applied. - """ - - TERMINATOR = b'\r\n' - ENCODING = 'utf-8' - UNICODE_HANDLING = 'replace' - - def handle_packet(self, packet): - self.handle_line(packet.decode(self.ENCODING, self.UNICODE_HANDLING)) - - def handle_line(self, line): - """Process one line - to be overridden by subclassing""" - raise NotImplementedError('please implement functionality in handle_line') - - def write_line(self, text): - """ - Write text to the transport. ``text`` is a Unicode string and the encoding - is applied before sending ans also the newline is append. - """ - # + is not the best choice but bytes does not support % or .format in py3 and we want a single write call - self.transport.write(text.encode(self.ENCODING, self.UNICODE_HANDLING) + self.TERMINATOR) - - -class ReaderThread(threading.Thread): - """\ - Implement a serial port read loop and dispatch to a Protocol instance (like - the asyncio.Protocol) but do it with threads. - - Calls to close() will close the serial port but it is also possible to just - stop() this thread and continue the serial port instance otherwise. - """ - - def __init__(self, serial_instance, protocol_factory): - """\ - Initialize thread. - - Note that the serial_instance' timeout is set to one second! - Other settings are not changed. - """ - super(ReaderThread, self).__init__() - self.daemon = True - self.serial = serial_instance - self.protocol_factory = protocol_factory - self.alive = True - self._lock = threading.Lock() - self._connection_made = threading.Event() - self.protocol = None - - def stop(self): - """Stop the reader thread""" - self.alive = False - if hasattr(self.serial, 'cancel_read'): - self.serial.cancel_read() - self.join(2) - - def run(self): - """Reader loop""" - if not hasattr(self.serial, 'cancel_read'): - self.serial.timeout = 1 - self.protocol = self.protocol_factory() - try: - self.protocol.connection_made(self) - except Exception as e: - self.alive = False - self.protocol.connection_lost(e) - self._connection_made.set() - return - error = None - self._connection_made.set() - while self.alive and self.serial.is_open: - try: - # read all that is there or wait for one byte (blocking) - data = self.serial.read(self.serial.in_waiting or 1) - except serial.SerialException as e: - # probably some I/O problem such as disconnected USB serial - # adapters -> exit - error = e - break - else: - if data: - # make a separated try-except for called user code - try: - self.protocol.data_received(data) - except Exception as e: - error = e - break - self.alive = False - self.protocol.connection_lost(error) - self.protocol = None - - def write(self, data): - """Thread safe writing (uses lock)""" - with self._lock: - return self.serial.write(data) - - def close(self): - """Close the serial port and exit reader thread (uses lock)""" - # use the lock to let other threads finish writing - with self._lock: - # first stop reading, so that closing can be done on idle port - self.stop() - self.serial.close() - - def connect(self): - """ - Wait until connection is set up and return the transport and protocol - instances. - """ - if self.alive: - self._connection_made.wait() - if not self.alive: - raise RuntimeError('connection_lost already called') - return (self, self.protocol) - else: - raise RuntimeError('already stopped') - - # - - context manager, returns protocol - - def __enter__(self): - """\ - Enter context handler. May raise RuntimeError in case the connection - could not be created. - """ - self.start() - self._connection_made.wait() - if not self.alive: - raise RuntimeError('connection_lost already called') - return self.protocol - - def __exit__(self, exc_type, exc_val, exc_tb): - """Leave context: close port""" - self.close() - - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# test -if __name__ == '__main__': - # pylint: disable=wrong-import-position - import sys - import time - import traceback - - #~ PORT = 'spy:///dev/ttyUSB0' - PORT = 'loop://' - - class PrintLines(LineReader): - def connection_made(self, transport): - super(PrintLines, self).connection_made(transport) - sys.stdout.write('port opened\n') - self.write_line('hello world') - - def handle_line(self, data): - sys.stdout.write('line received: {!r}\n'.format(data)) - - def connection_lost(self, exc): - if exc: - traceback.print_exc(exc) - sys.stdout.write('port closed\n') - - ser = serial.serial_for_url(PORT, baudrate=115200, timeout=1) - with ReaderThread(ser, PrintLines) as protocol: - protocol.write_line('hello') - time.sleep(2) - - # alternative usage - ser = serial.serial_for_url(PORT, baudrate=115200, timeout=1) - t = ReaderThread(ser, PrintLines) - t.start() - transport, protocol = t.connect() - protocol.write_line('hello') - time.sleep(2) - t.close() diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/threaded/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/threaded/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 3bd85ea..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/threaded/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 67083a9..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/hexlify_codec.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/hexlify_codec.cpython-38.pyc deleted file mode 100644 index acf78ef..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/hexlify_codec.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/list_ports.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/list_ports.cpython-38.pyc deleted file mode 100644 index d272df0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/list_ports.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/list_ports_common.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/list_ports_common.cpython-38.pyc deleted file mode 100644 index f94a2c5..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/list_ports_common.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/list_ports_linux.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/list_ports_linux.cpython-38.pyc deleted file mode 100644 index 52ef583..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/list_ports_linux.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/list_ports_osx.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/list_ports_osx.cpython-38.pyc deleted file mode 100644 index e236eb7..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/list_ports_osx.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/list_ports_posix.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/list_ports_posix.cpython-38.pyc deleted file mode 100644 index 7050c0c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/list_ports_posix.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/list_ports_windows.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/list_ports_windows.cpython-38.pyc deleted file mode 100644 index 5923c2d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/list_ports_windows.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/miniterm.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/miniterm.cpython-38.pyc deleted file mode 100644 index 007ebde..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/__pycache__/miniterm.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/hexlify_codec.py b/plotter-app/venv/lib/python3.8/site-packages/serial/tools/hexlify_codec.py deleted file mode 100644 index bd8f6b0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/hexlify_codec.py +++ /dev/null @@ -1,126 +0,0 @@ -#! python -# -# This is a codec to create and decode hexdumps with spaces between characters. used by miniterm. -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# (C) 2015-2016 Chris Liechti -# -# SPDX-License-Identifier: BSD-3-Clause -"""\ -Python 'hex' Codec - 2-digit hex with spaces content transfer encoding. - -Encode and decode may be a bit missleading at first sight... - -The textual representation is a hex dump: e.g. "40 41" -The "encoded" data of this is the binary form, e.g. b"@A" - -Therefore decoding is binary to text and thus converting binary data to hex dump. - -""" - -from __future__ import absolute_import - -import codecs -import serial - - -try: - unicode -except (NameError, AttributeError): - unicode = str # for Python 3, pylint: disable=redefined-builtin,invalid-name - - -HEXDIGITS = '0123456789ABCDEF' - - -# Codec APIs - -def hex_encode(data, errors='strict'): - """'40 41 42' -> b'@ab'""" - return (serial.to_bytes([int(h, 16) for h in data.split()]), len(data)) - - -def hex_decode(data, errors='strict'): - """b'@ab' -> '40 41 42'""" - return (unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data))), len(data)) - - -class Codec(codecs.Codec): - def encode(self, data, errors='strict'): - """'40 41 42' -> b'@ab'""" - return serial.to_bytes([int(h, 16) for h in data.split()]) - - def decode(self, data, errors='strict'): - """b'@ab' -> '40 41 42'""" - return unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data))) - - -class IncrementalEncoder(codecs.IncrementalEncoder): - """Incremental hex encoder""" - - def __init__(self, errors='strict'): - self.errors = errors - self.state = 0 - - def reset(self): - self.state = 0 - - def getstate(self): - return self.state - - def setstate(self, state): - self.state = state - - def encode(self, data, final=False): - """\ - Incremental encode, keep track of digits and emit a byte when a pair - of hex digits is found. The space is optional unless the error - handling is defined to be 'strict'. - """ - state = self.state - encoded = [] - for c in data.upper(): - if c in HEXDIGITS: - z = HEXDIGITS.index(c) - if state: - encoded.append(z + (state & 0xf0)) - state = 0 - else: - state = 0x100 + (z << 4) - elif c == ' ': # allow spaces to separate values - if state and self.errors == 'strict': - raise UnicodeError('odd number of hex digits') - state = 0 - else: - if self.errors == 'strict': - raise UnicodeError('non-hex digit found: {!r}'.format(c)) - self.state = state - return serial.to_bytes(encoded) - - -class IncrementalDecoder(codecs.IncrementalDecoder): - """Incremental decoder""" - def decode(self, data, final=False): - return unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data))) - - -class StreamWriter(Codec, codecs.StreamWriter): - """Combination of hexlify codec and StreamWriter""" - - -class StreamReader(Codec, codecs.StreamReader): - """Combination of hexlify codec and StreamReader""" - - -def getregentry(): - """encodings module API""" - return codecs.CodecInfo( - name='hexlify', - encode=hex_encode, - decode=hex_decode, - incrementalencoder=IncrementalEncoder, - incrementaldecoder=IncrementalDecoder, - streamwriter=StreamWriter, - streamreader=StreamReader, - #~ _is_text_encoding=True, - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/list_ports.py b/plotter-app/venv/lib/python3.8/site-packages/serial/tools/list_ports.py deleted file mode 100644 index 0d7e3d4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/list_ports.py +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env python -# -# Serial port enumeration. Console tool and backend selection. -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# (C) 2011-2015 Chris Liechti -# -# SPDX-License-Identifier: BSD-3-Clause - -"""\ -This module will provide a function called comports that returns an -iterable (generator or list) that will enumerate available com ports. Note that -on some systems non-existent ports may be listed. - -Additionally a grep function is supplied that can be used to search for ports -based on their descriptions or hardware ID. -""" - -from __future__ import absolute_import - -import sys -import os -import re - -# chose an implementation, depending on os -#~ if sys.platform == 'cli': -#~ else: -if os.name == 'nt': # sys.platform == 'win32': - from serial.tools.list_ports_windows import comports -elif os.name == 'posix': - from serial.tools.list_ports_posix import comports -#~ elif os.name == 'java': -else: - raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name)) - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -def grep(regexp, include_links=False): - """\ - Search for ports using a regular expression. Port name, description and - hardware ID are searched. The function returns an iterable that returns the - same tuples as comport() would do. - """ - r = re.compile(regexp, re.I) - for info in comports(include_links): - port, desc, hwid = info - if r.search(port) or r.search(desc) or r.search(hwid): - yield info - - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -def main(): - import argparse - - parser = argparse.ArgumentParser(description='Serial port enumeration') - - parser.add_argument( - 'regexp', - nargs='?', - help='only show ports that match this regex') - - parser.add_argument( - '-v', '--verbose', - action='store_true', - help='show more messages') - - parser.add_argument( - '-q', '--quiet', - action='store_true', - help='suppress all messages') - - parser.add_argument( - '-n', - type=int, - help='only output the N-th entry') - - parser.add_argument( - '-s', '--include-links', - action='store_true', - help='include entries that are symlinks to real devices') - - args = parser.parse_args() - - hits = 0 - # get iteraror w/ or w/o filter - if args.regexp: - if not args.quiet: - sys.stderr.write("Filtered list with regexp: {!r}\n".format(args.regexp)) - iterator = sorted(grep(args.regexp, include_links=args.include_links)) - else: - iterator = sorted(comports(include_links=args.include_links)) - # list them - for n, (port, desc, hwid) in enumerate(iterator, 1): - if args.n is None or args.n == n: - sys.stdout.write("{:20}\n".format(port)) - if args.verbose: - sys.stdout.write(" desc: {}\n".format(desc)) - sys.stdout.write(" hwid: {}\n".format(hwid)) - hits += 1 - if not args.quiet: - if hits: - sys.stderr.write("{} ports found\n".format(hits)) - else: - sys.stderr.write("no ports found\n") - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# test -if __name__ == '__main__': - main() diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/list_ports_common.py b/plotter-app/venv/lib/python3.8/site-packages/serial/tools/list_ports_common.py deleted file mode 100644 index 617f3dc..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/list_ports_common.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/env python -# -# This is a helper module for the various platform dependent list_port -# implementations. -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# (C) 2015 Chris Liechti -# -# SPDX-License-Identifier: BSD-3-Clause - -from __future__ import absolute_import - -import re -import glob -import os -import os.path - - -def numsplit(text): - """\ - Convert string into a list of texts and numbers in order to support a - natural sorting. - """ - result = [] - for group in re.split(r'(\d+)', text): - if group: - try: - group = int(group) - except ValueError: - pass - result.append(group) - return result - - -class ListPortInfo(object): - """Info collection base class for serial ports""" - - def __init__(self, device, skip_link_detection=False): - self.device = device - self.name = os.path.basename(device) - self.description = 'n/a' - self.hwid = 'n/a' - # USB specific data - self.vid = None - self.pid = None - self.serial_number = None - self.location = None - self.manufacturer = None - self.product = None - self.interface = None - # special handling for links - if not skip_link_detection and device is not None and os.path.islink(device): - self.hwid = 'LINK={}'.format(os.path.realpath(device)) - - def usb_description(self): - """return a short string to name the port based on USB info""" - if self.interface is not None: - return '{} - {}'.format(self.product, self.interface) - elif self.product is not None: - return self.product - else: - return self.name - - def usb_info(self): - """return a string with USB related information about device""" - return 'USB VID:PID={:04X}:{:04X}{}{}'.format( - self.vid or 0, - self.pid or 0, - ' SER={}'.format(self.serial_number) if self.serial_number is not None else '', - ' LOCATION={}'.format(self.location) if self.location is not None else '') - - def apply_usb_info(self): - """update description and hwid from USB data""" - self.description = self.usb_description() - self.hwid = self.usb_info() - - def __eq__(self, other): - return isinstance(other, ListPortInfo) and self.device == other.device - - def __hash__(self): - return hash(self.device) - - def __lt__(self, other): - if not isinstance(other, ListPortInfo): - raise TypeError('unorderable types: {}() and {}()'.format( - type(self).__name__, - type(other).__name__)) - return numsplit(self.device) < numsplit(other.device) - - def __str__(self): - return '{} - {}'.format(self.device, self.description) - - def __getitem__(self, index): - """Item access: backwards compatible -> (port, desc, hwid)""" - if index == 0: - return self.device - elif index == 1: - return self.description - elif index == 2: - return self.hwid - else: - raise IndexError('{} > 2'.format(index)) - - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -def list_links(devices): - """\ - search all /dev devices and look for symlinks to known ports already - listed in devices. - """ - links = [] - for device in glob.glob('/dev/*'): - if os.path.islink(device) and os.path.realpath(device) in devices: - links.append(device) - return links - - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# test -if __name__ == '__main__': - print(ListPortInfo('dummy')) diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/list_ports_linux.py b/plotter-app/venv/lib/python3.8/site-packages/serial/tools/list_ports_linux.py deleted file mode 100644 index c8c1cfc..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/list_ports_linux.py +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env python -# -# This is a module that gathers a list of serial ports including details on -# GNU/Linux systems. -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# (C) 2011-2015 Chris Liechti -# -# SPDX-License-Identifier: BSD-3-Clause - -from __future__ import absolute_import - -import glob -import os -from serial.tools import list_ports_common - - -class SysFS(list_ports_common.ListPortInfo): - """Wrapper for easy sysfs access and device info""" - - def __init__(self, device): - super(SysFS, self).__init__(device) - # special handling for links - if device is not None and os.path.islink(device): - device = os.path.realpath(device) - is_link = True - else: - is_link = False - self.usb_device_path = None - if os.path.exists('/sys/class/tty/{}/device'.format(self.name)): - self.device_path = os.path.realpath('/sys/class/tty/{}/device'.format(self.name)) - self.subsystem = os.path.basename(os.path.realpath(os.path.join(self.device_path, 'subsystem'))) - else: - self.device_path = None - self.subsystem = None - # check device type - if self.subsystem == 'usb-serial': - self.usb_interface_path = os.path.dirname(self.device_path) - elif self.subsystem == 'usb': - self.usb_interface_path = self.device_path - else: - self.usb_interface_path = None - # fill-in info for USB devices - if self.usb_interface_path is not None: - self.usb_device_path = os.path.dirname(self.usb_interface_path) - - try: - num_if = int(self.read_line(self.usb_device_path, 'bNumInterfaces')) - except ValueError: - num_if = 1 - - self.vid = int(self.read_line(self.usb_device_path, 'idVendor'), 16) - self.pid = int(self.read_line(self.usb_device_path, 'idProduct'), 16) - self.serial_number = self.read_line(self.usb_device_path, 'serial') - if num_if > 1: # multi interface devices like FT4232 - self.location = os.path.basename(self.usb_interface_path) - else: - self.location = os.path.basename(self.usb_device_path) - - self.manufacturer = self.read_line(self.usb_device_path, 'manufacturer') - self.product = self.read_line(self.usb_device_path, 'product') - self.interface = self.read_line(self.usb_interface_path, 'interface') - - if self.subsystem in ('usb', 'usb-serial'): - self.apply_usb_info() - #~ elif self.subsystem in ('pnp', 'amba'): # PCI based devices, raspi - elif self.subsystem == 'pnp': # PCI based devices - self.description = self.name - self.hwid = self.read_line(self.device_path, 'id') - elif self.subsystem == 'amba': # raspi - self.description = self.name - self.hwid = os.path.basename(self.device_path) - - if is_link: - self.hwid += ' LINK={}'.format(device) - - def read_line(self, *args): - """\ - Helper function to read a single line from a file. - One or more parameters are allowed, they are joined with os.path.join. - Returns None on errors.. - """ - try: - with open(os.path.join(*args)) as f: - line = f.readline().strip() - return line - except IOError: - return None - - -def comports(include_links=False): - devices = glob.glob('/dev/ttyS*') # built-in serial ports - devices.extend(glob.glob('/dev/ttyUSB*')) # usb-serial with own driver - devices.extend(glob.glob('/dev/ttyXRUSB*')) # xr-usb-serial port exar (DELL Edge 3001) - devices.extend(glob.glob('/dev/ttyACM*')) # usb-serial with CDC-ACM profile - devices.extend(glob.glob('/dev/ttyAMA*')) # ARM internal port (raspi) - devices.extend(glob.glob('/dev/rfcomm*')) # BT serial devices - devices.extend(glob.glob('/dev/ttyAP*')) # Advantech multi-port serial controllers - if include_links: - devices.extend(list_ports_common.list_links(devices)) - return [info - for info in [SysFS(d) for d in devices] - if info.subsystem != "platform"] # hide non-present internal serial ports - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# test -if __name__ == '__main__': - for info in sorted(comports()): - print("{0}: {0.subsystem}".format(info)) diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/list_ports_osx.py b/plotter-app/venv/lib/python3.8/site-packages/serial/tools/list_ports_osx.py deleted file mode 100644 index 51b4e8c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/list_ports_osx.py +++ /dev/null @@ -1,299 +0,0 @@ -#!/usr/bin/env python -# -# This is a module that gathers a list of serial ports including details on OSX -# -# code originally from https://github.com/makerbot/pyserial/tree/master/serial/tools -# with contributions from cibomahto, dgs3, FarMcKon, tedbrandston -# and modifications by cliechti, hoihu, hardkrash -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# (C) 2013-2020 -# -# SPDX-License-Identifier: BSD-3-Clause - - -# List all of the callout devices in OS/X by querying IOKit. - -# See the following for a reference of how to do this: -# http://developer.apple.com/library/mac/#documentation/DeviceDrivers/Conceptual/WorkingWSerial/WWSerial_SerialDevs/SerialDevices.html#//apple_ref/doc/uid/TP30000384-CIHGEAFD - -# More help from darwin_hid.py - -# Also see the 'IORegistryExplorer' for an idea of what we are actually searching - -from __future__ import absolute_import - -import ctypes - -from serial.tools import list_ports_common - -iokit = ctypes.cdll.LoadLibrary('/System/Library/Frameworks/IOKit.framework/IOKit') -cf = ctypes.cdll.LoadLibrary('/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation') - -# kIOMasterPortDefault is no longer exported in BigSur but no biggie, using NULL works just the same -kIOMasterPortDefault = 0 # WAS: ctypes.c_void_p.in_dll(iokit, "kIOMasterPortDefault") -kCFAllocatorDefault = ctypes.c_void_p.in_dll(cf, "kCFAllocatorDefault") - -kCFStringEncodingMacRoman = 0 -kCFStringEncodingUTF8 = 0x08000100 - -# defined in `IOKit/usb/USBSpec.h` -kUSBVendorString = 'USB Vendor Name' -kUSBSerialNumberString = 'USB Serial Number' - -# `io_name_t` defined as `typedef char io_name_t[128];` -# in `device/device_types.h` -io_name_size = 128 - -# defined in `mach/kern_return.h` -KERN_SUCCESS = 0 -# kern_return_t defined as `typedef int kern_return_t;` in `mach/i386/kern_return.h` -kern_return_t = ctypes.c_int - -iokit.IOServiceMatching.restype = ctypes.c_void_p - -iokit.IOServiceGetMatchingServices.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] -iokit.IOServiceGetMatchingServices.restype = kern_return_t - -iokit.IORegistryEntryGetParentEntry.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] -iokit.IOServiceGetMatchingServices.restype = kern_return_t - -iokit.IORegistryEntryCreateCFProperty.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint32] -iokit.IORegistryEntryCreateCFProperty.restype = ctypes.c_void_p - -iokit.IORegistryEntryGetPath.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p] -iokit.IORegistryEntryGetPath.restype = kern_return_t - -iokit.IORegistryEntryGetName.argtypes = [ctypes.c_void_p, ctypes.c_void_p] -iokit.IORegistryEntryGetName.restype = kern_return_t - -iokit.IOObjectGetClass.argtypes = [ctypes.c_void_p, ctypes.c_void_p] -iokit.IOObjectGetClass.restype = kern_return_t - -iokit.IOObjectRelease.argtypes = [ctypes.c_void_p] - - -cf.CFStringCreateWithCString.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int32] -cf.CFStringCreateWithCString.restype = ctypes.c_void_p - -cf.CFStringGetCStringPtr.argtypes = [ctypes.c_void_p, ctypes.c_uint32] -cf.CFStringGetCStringPtr.restype = ctypes.c_char_p - -cf.CFStringGetCString.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_long, ctypes.c_uint32] -cf.CFStringGetCString.restype = ctypes.c_bool - -cf.CFNumberGetValue.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.c_void_p] -cf.CFNumberGetValue.restype = ctypes.c_void_p - -# void CFRelease ( CFTypeRef cf ); -cf.CFRelease.argtypes = [ctypes.c_void_p] -cf.CFRelease.restype = None - -# CFNumber type defines -kCFNumberSInt8Type = 1 -kCFNumberSInt16Type = 2 -kCFNumberSInt32Type = 3 -kCFNumberSInt64Type = 4 - - -def get_string_property(device_type, property): - """ - Search the given device for the specified string property - - @param device_type Type of Device - @param property String to search for - @return Python string containing the value, or None if not found. - """ - key = cf.CFStringCreateWithCString( - kCFAllocatorDefault, - property.encode("utf-8"), - kCFStringEncodingUTF8) - - CFContainer = iokit.IORegistryEntryCreateCFProperty( - device_type, - key, - kCFAllocatorDefault, - 0) - output = None - - if CFContainer: - output = cf.CFStringGetCStringPtr(CFContainer, 0) - if output is not None: - output = output.decode('utf-8') - else: - buffer = ctypes.create_string_buffer(io_name_size); - success = cf.CFStringGetCString(CFContainer, ctypes.byref(buffer), io_name_size, kCFStringEncodingUTF8) - if success: - output = buffer.value.decode('utf-8') - cf.CFRelease(CFContainer) - return output - - -def get_int_property(device_type, property, cf_number_type): - """ - Search the given device for the specified string property - - @param device_type Device to search - @param property String to search for - @param cf_number_type CFType number - - @return Python string containing the value, or None if not found. - """ - key = cf.CFStringCreateWithCString( - kCFAllocatorDefault, - property.encode("utf-8"), - kCFStringEncodingUTF8) - - CFContainer = iokit.IORegistryEntryCreateCFProperty( - device_type, - key, - kCFAllocatorDefault, - 0) - - if CFContainer: - if (cf_number_type == kCFNumberSInt32Type): - number = ctypes.c_uint32() - elif (cf_number_type == kCFNumberSInt16Type): - number = ctypes.c_uint16() - cf.CFNumberGetValue(CFContainer, cf_number_type, ctypes.byref(number)) - cf.CFRelease(CFContainer) - return number.value - return None - -def IORegistryEntryGetName(device): - devicename = ctypes.create_string_buffer(io_name_size); - res = iokit.IORegistryEntryGetName(device, ctypes.byref(devicename)) - if res != KERN_SUCCESS: - return None - # this works in python2 but may not be valid. Also I don't know if - # this encoding is guaranteed. It may be dependent on system locale. - return devicename.value.decode('utf-8') - -def IOObjectGetClass(device): - classname = ctypes.create_string_buffer(io_name_size) - iokit.IOObjectGetClass(device, ctypes.byref(classname)) - return classname.value - -def GetParentDeviceByType(device, parent_type): - """ Find the first parent of a device that implements the parent_type - @param IOService Service to inspect - @return Pointer to the parent type, or None if it was not found. - """ - # First, try to walk up the IOService tree to find a parent of this device that is a IOUSBDevice. - parent_type = parent_type.encode('utf-8') - while IOObjectGetClass(device) != parent_type: - parent = ctypes.c_void_p() - response = iokit.IORegistryEntryGetParentEntry( - device, - "IOService".encode("utf-8"), - ctypes.byref(parent)) - # If we weren't able to find a parent for the device, we're done. - if response != KERN_SUCCESS: - return None - device = parent - return device - - -def GetIOServicesByType(service_type): - """ - returns iterator over specified service_type - """ - serial_port_iterator = ctypes.c_void_p() - - iokit.IOServiceGetMatchingServices( - kIOMasterPortDefault, - iokit.IOServiceMatching(service_type.encode('utf-8')), - ctypes.byref(serial_port_iterator)) - - services = [] - while iokit.IOIteratorIsValid(serial_port_iterator): - service = iokit.IOIteratorNext(serial_port_iterator) - if not service: - break - services.append(service) - iokit.IOObjectRelease(serial_port_iterator) - return services - - -def location_to_string(locationID): - """ - helper to calculate port and bus number from locationID - """ - loc = ['{}-'.format(locationID >> 24)] - while locationID & 0xf00000: - if len(loc) > 1: - loc.append('.') - loc.append('{}'.format((locationID >> 20) & 0xf)) - locationID <<= 4 - return ''.join(loc) - - -class SuitableSerialInterface(object): - pass - - -def scan_interfaces(): - """ - helper function to scan USB interfaces - returns a list of SuitableSerialInterface objects with name and id attributes - """ - interfaces = [] - for service in GetIOServicesByType('IOSerialBSDClient'): - device = get_string_property(service, "IOCalloutDevice") - if device: - usb_device = GetParentDeviceByType(service, "IOUSBInterface") - if usb_device: - name = get_string_property(usb_device, "USB Interface Name") or None - locationID = get_int_property(usb_device, "locationID", kCFNumberSInt32Type) or '' - i = SuitableSerialInterface() - i.id = locationID - i.name = name - interfaces.append(i) - return interfaces - - -def search_for_locationID_in_interfaces(serial_interfaces, locationID): - for interface in serial_interfaces: - if (interface.id == locationID): - return interface.name - return None - - -def comports(include_links=False): - # XXX include_links is currently ignored. are links in /dev even supported here? - # Scan for all iokit serial ports - services = GetIOServicesByType('IOSerialBSDClient') - ports = [] - serial_interfaces = scan_interfaces() - for service in services: - # First, add the callout device file. - device = get_string_property(service, "IOCalloutDevice") - if device: - info = list_ports_common.ListPortInfo(device) - # If the serial port is implemented by IOUSBDevice - # NOTE IOUSBDevice was deprecated as of 10.11 and finally on Apple Silicon - # devices has been completely removed. Thanks to @oskay for this patch. - usb_device = GetParentDeviceByType(service, "IOUSBHostDevice") - if not usb_device: - usb_device = GetParentDeviceByType(service, "IOUSBDevice") - if usb_device: - # fetch some useful informations from properties - info.vid = get_int_property(usb_device, "idVendor", kCFNumberSInt16Type) - info.pid = get_int_property(usb_device, "idProduct", kCFNumberSInt16Type) - info.serial_number = get_string_property(usb_device, kUSBSerialNumberString) - # We know this is a usb device, so the - # IORegistryEntryName should always be aliased to the - # usb product name string descriptor. - info.product = IORegistryEntryGetName(usb_device) or 'n/a' - info.manufacturer = get_string_property(usb_device, kUSBVendorString) - locationID = get_int_property(usb_device, "locationID", kCFNumberSInt32Type) - info.location = location_to_string(locationID) - info.interface = search_for_locationID_in_interfaces(serial_interfaces, locationID) - info.apply_usb_info() - ports.append(info) - return ports - -# test -if __name__ == '__main__': - for port, desc, hwid in sorted(comports()): - print("{}: {} [{}]".format(port, desc, hwid)) diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/list_ports_posix.py b/plotter-app/venv/lib/python3.8/site-packages/serial/tools/list_ports_posix.py deleted file mode 100644 index 79bc8ed..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/list_ports_posix.py +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env python -# -# This is a module that gathers a list of serial ports on POSIXy systems. -# For some specific implementations, see also list_ports_linux, list_ports_osx -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# (C) 2011-2015 Chris Liechti -# -# SPDX-License-Identifier: BSD-3-Clause - -"""\ -The ``comports`` function is expected to return an iterable that yields tuples -of 3 strings: port name, human readable description and a hardware ID. - -As currently no method is known to get the second two strings easily, they are -currently just identical to the port name. -""" - -from __future__ import absolute_import - -import glob -import sys -import os -from serial.tools import list_ports_common - -# try to detect the OS so that a device can be selected... -plat = sys.platform.lower() - -if plat[:5] == 'linux': # Linux (confirmed) # noqa - from serial.tools.list_ports_linux import comports - -elif plat[:6] == 'darwin': # OS X (confirmed) - from serial.tools.list_ports_osx import comports - -elif plat == 'cygwin': # cygwin/win32 - # cygwin accepts /dev/com* in many contexts - # (such as 'open' call, explicit 'ls'), but 'glob.glob' - # and bare 'ls' do not; so use /dev/ttyS* instead - def comports(include_links=False): - devices = glob.glob('/dev/ttyS*') - if include_links: - devices.extend(list_ports_common.list_links(devices)) - return [list_ports_common.ListPortInfo(d) for d in devices] - -elif plat[:7] == 'openbsd': # OpenBSD - def comports(include_links=False): - devices = glob.glob('/dev/cua*') - if include_links: - devices.extend(list_ports_common.list_links(devices)) - return [list_ports_common.ListPortInfo(d) for d in devices] - -elif plat[:3] == 'bsd' or plat[:7] == 'freebsd': - def comports(include_links=False): - devices = glob.glob('/dev/cua*[!.init][!.lock]') - if include_links: - devices.extend(list_ports_common.list_links(devices)) - return [list_ports_common.ListPortInfo(d) for d in devices] - -elif plat[:6] == 'netbsd': # NetBSD - def comports(include_links=False): - """scan for available ports. return a list of device names.""" - devices = glob.glob('/dev/dty*') - if include_links: - devices.extend(list_ports_common.list_links(devices)) - return [list_ports_common.ListPortInfo(d) for d in devices] - -elif plat[:4] == 'irix': # IRIX - def comports(include_links=False): - """scan for available ports. return a list of device names.""" - devices = glob.glob('/dev/ttyf*') - if include_links: - devices.extend(list_ports_common.list_links(devices)) - return [list_ports_common.ListPortInfo(d) for d in devices] - -elif plat[:2] == 'hp': # HP-UX (not tested) - def comports(include_links=False): - """scan for available ports. return a list of device names.""" - devices = glob.glob('/dev/tty*p0') - if include_links: - devices.extend(list_ports_common.list_links(devices)) - return [list_ports_common.ListPortInfo(d) for d in devices] - -elif plat[:5] == 'sunos': # Solaris/SunOS - def comports(include_links=False): - """scan for available ports. return a list of device names.""" - devices = glob.glob('/dev/tty*c') - if include_links: - devices.extend(list_ports_common.list_links(devices)) - return [list_ports_common.ListPortInfo(d) for d in devices] - -elif plat[:3] == 'aix': # AIX - def comports(include_links=False): - """scan for available ports. return a list of device names.""" - devices = glob.glob('/dev/tty*') - if include_links: - devices.extend(list_ports_common.list_links(devices)) - return [list_ports_common.ListPortInfo(d) for d in devices] - -else: - # platform detection has failed... - import serial - sys.stderr.write("""\ -don't know how to enumerate ttys on this system. -! I you know how the serial ports are named send this information to -! the author of this module: - -sys.platform = {!r} -os.name = {!r} -pySerial version = {} - -also add the naming scheme of the serial ports and with a bit luck you can get -this module running... -""".format(sys.platform, os.name, serial.VERSION)) - raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name)) - -# test -if __name__ == '__main__': - for port, desc, hwid in sorted(comports()): - print("{}: {} [{}]".format(port, desc, hwid)) diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/list_ports_windows.py b/plotter-app/venv/lib/python3.8/site-packages/serial/tools/list_ports_windows.py deleted file mode 100644 index 0b4a5b1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/list_ports_windows.py +++ /dev/null @@ -1,427 +0,0 @@ -#! python -# -# Enumerate serial ports on Windows including a human readable description -# and hardware information. -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# (C) 2001-2016 Chris Liechti -# -# SPDX-License-Identifier: BSD-3-Clause - -from __future__ import absolute_import - -# pylint: disable=invalid-name,too-few-public-methods -import re -import ctypes -from ctypes.wintypes import BOOL -from ctypes.wintypes import HWND -from ctypes.wintypes import DWORD -from ctypes.wintypes import WORD -from ctypes.wintypes import LONG -from ctypes.wintypes import ULONG -from ctypes.wintypes import HKEY -from ctypes.wintypes import BYTE -import serial -from serial.win32 import ULONG_PTR -from serial.tools import list_ports_common - - -def ValidHandle(value, func, arguments): - if value == 0: - raise ctypes.WinError() - return value - - -NULL = 0 -HDEVINFO = ctypes.c_void_p -LPCTSTR = ctypes.c_wchar_p -PCTSTR = ctypes.c_wchar_p -PTSTR = ctypes.c_wchar_p -LPDWORD = PDWORD = ctypes.POINTER(DWORD) -#~ LPBYTE = PBYTE = ctypes.POINTER(BYTE) -LPBYTE = PBYTE = ctypes.c_void_p # XXX avoids error about types - -ACCESS_MASK = DWORD -REGSAM = ACCESS_MASK - - -class GUID(ctypes.Structure): - _fields_ = [ - ('Data1', DWORD), - ('Data2', WORD), - ('Data3', WORD), - ('Data4', BYTE * 8), - ] - - def __str__(self): - return "{{{:08x}-{:04x}-{:04x}-{}-{}}}".format( - self.Data1, - self.Data2, - self.Data3, - ''.join(["{:02x}".format(d) for d in self.Data4[:2]]), - ''.join(["{:02x}".format(d) for d in self.Data4[2:]]), - ) - - -class SP_DEVINFO_DATA(ctypes.Structure): - _fields_ = [ - ('cbSize', DWORD), - ('ClassGuid', GUID), - ('DevInst', DWORD), - ('Reserved', ULONG_PTR), - ] - - def __str__(self): - return "ClassGuid:{} DevInst:{}".format(self.ClassGuid, self.DevInst) - - -PSP_DEVINFO_DATA = ctypes.POINTER(SP_DEVINFO_DATA) - -PSP_DEVICE_INTERFACE_DETAIL_DATA = ctypes.c_void_p - -setupapi = ctypes.windll.LoadLibrary("setupapi") -SetupDiDestroyDeviceInfoList = setupapi.SetupDiDestroyDeviceInfoList -SetupDiDestroyDeviceInfoList.argtypes = [HDEVINFO] -SetupDiDestroyDeviceInfoList.restype = BOOL - -SetupDiClassGuidsFromName = setupapi.SetupDiClassGuidsFromNameW -SetupDiClassGuidsFromName.argtypes = [PCTSTR, ctypes.POINTER(GUID), DWORD, PDWORD] -SetupDiClassGuidsFromName.restype = BOOL - -SetupDiEnumDeviceInfo = setupapi.SetupDiEnumDeviceInfo -SetupDiEnumDeviceInfo.argtypes = [HDEVINFO, DWORD, PSP_DEVINFO_DATA] -SetupDiEnumDeviceInfo.restype = BOOL - -SetupDiGetClassDevs = setupapi.SetupDiGetClassDevsW -SetupDiGetClassDevs.argtypes = [ctypes.POINTER(GUID), PCTSTR, HWND, DWORD] -SetupDiGetClassDevs.restype = HDEVINFO -SetupDiGetClassDevs.errcheck = ValidHandle - -SetupDiGetDeviceRegistryProperty = setupapi.SetupDiGetDeviceRegistryPropertyW -SetupDiGetDeviceRegistryProperty.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD] -SetupDiGetDeviceRegistryProperty.restype = BOOL - -SetupDiGetDeviceInstanceId = setupapi.SetupDiGetDeviceInstanceIdW -SetupDiGetDeviceInstanceId.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, PTSTR, DWORD, PDWORD] -SetupDiGetDeviceInstanceId.restype = BOOL - -SetupDiOpenDevRegKey = setupapi.SetupDiOpenDevRegKey -SetupDiOpenDevRegKey.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM] -SetupDiOpenDevRegKey.restype = HKEY - -advapi32 = ctypes.windll.LoadLibrary("Advapi32") -RegCloseKey = advapi32.RegCloseKey -RegCloseKey.argtypes = [HKEY] -RegCloseKey.restype = LONG - -RegQueryValueEx = advapi32.RegQueryValueExW -RegQueryValueEx.argtypes = [HKEY, LPCTSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD] -RegQueryValueEx.restype = LONG - -cfgmgr32 = ctypes.windll.LoadLibrary("Cfgmgr32") -CM_Get_Parent = cfgmgr32.CM_Get_Parent -CM_Get_Parent.argtypes = [PDWORD, DWORD, ULONG] -CM_Get_Parent.restype = LONG - -CM_Get_Device_IDW = cfgmgr32.CM_Get_Device_IDW -CM_Get_Device_IDW.argtypes = [DWORD, PTSTR, ULONG, ULONG] -CM_Get_Device_IDW.restype = LONG - -CM_MapCrToWin32Err = cfgmgr32.CM_MapCrToWin32Err -CM_MapCrToWin32Err.argtypes = [DWORD, DWORD] -CM_MapCrToWin32Err.restype = DWORD - - -DIGCF_PRESENT = 2 -DIGCF_DEVICEINTERFACE = 16 -INVALID_HANDLE_VALUE = 0 -ERROR_INSUFFICIENT_BUFFER = 122 -ERROR_NOT_FOUND = 1168 -SPDRP_HARDWAREID = 1 -SPDRP_FRIENDLYNAME = 12 -SPDRP_LOCATION_PATHS = 35 -SPDRP_MFG = 11 -DICS_FLAG_GLOBAL = 1 -DIREG_DEV = 0x00000001 -KEY_READ = 0x20019 - - -MAX_USB_DEVICE_TREE_TRAVERSAL_DEPTH = 5 - - -def get_parent_serial_number(child_devinst, child_vid, child_pid, depth=0, last_serial_number=None): - """ Get the serial number of the parent of a device. - - Args: - child_devinst: The device instance handle to get the parent serial number of. - child_vid: The vendor ID of the child device. - child_pid: The product ID of the child device. - depth: The current iteration depth of the USB device tree. - """ - - # If the traversal depth is beyond the max, abandon attempting to find the serial number. - if depth > MAX_USB_DEVICE_TREE_TRAVERSAL_DEPTH: - return '' if not last_serial_number else last_serial_number - - # Get the parent device instance. - devinst = DWORD() - ret = CM_Get_Parent(ctypes.byref(devinst), child_devinst, 0) - - if ret: - win_error = CM_MapCrToWin32Err(DWORD(ret), DWORD(0)) - - # If there is no parent available, the child was the root device. We cannot traverse - # further. - if win_error == ERROR_NOT_FOUND: - return '' if not last_serial_number else last_serial_number - - raise ctypes.WinError(win_error) - - # Get the ID of the parent device and parse it for vendor ID, product ID, and serial number. - parentHardwareID = ctypes.create_unicode_buffer(250) - - ret = CM_Get_Device_IDW( - devinst, - parentHardwareID, - ctypes.sizeof(parentHardwareID) - 1, - 0) - - if ret: - raise ctypes.WinError(CM_MapCrToWin32Err(DWORD(ret), DWORD(0))) - - parentHardwareID_str = parentHardwareID.value - m = re.search(r'VID_([0-9a-f]{4})(&PID_([0-9a-f]{4}))?(&MI_(\d{2}))?(\\(.*))?', - parentHardwareID_str, - re.I) - - # return early if we have no matches (likely malformed serial, traversed too far) - if not m: - return '' if not last_serial_number else last_serial_number - - vid = None - pid = None - serial_number = None - if m.group(1): - vid = int(m.group(1), 16) - if m.group(3): - pid = int(m.group(3), 16) - if m.group(7): - serial_number = m.group(7) - - # store what we found as a fallback for malformed serial values up the chain - found_serial_number = serial_number - - # Check that the USB serial number only contains alpha-numeric characters. It may be a windows - # device ID (ephemeral ID). - if serial_number and not re.match(r'^\w+$', serial_number): - serial_number = None - - if not vid or not pid: - # If pid and vid are not available at this device level, continue to the parent. - return get_parent_serial_number(devinst, child_vid, child_pid, depth + 1, found_serial_number) - - if pid != child_pid or vid != child_vid: - # If the VID or PID has changed, we are no longer looking at the same physical device. The - # serial number is unknown. - return '' if not last_serial_number else last_serial_number - - # In this case, the vid and pid of the parent device are identical to the child. However, if - # there still isn't a serial number available, continue to the next parent. - if not serial_number: - return get_parent_serial_number(devinst, child_vid, child_pid, depth + 1, found_serial_number) - - # Finally, the VID and PID are identical to the child and a serial number is present, so return - # it. - return serial_number - - -def iterate_comports(): - """Return a generator that yields descriptions for serial ports""" - PortsGUIDs = (GUID * 8)() # so far only seen one used, so hope 8 are enough... - ports_guids_size = DWORD() - if not SetupDiClassGuidsFromName( - "Ports", - PortsGUIDs, - ctypes.sizeof(PortsGUIDs), - ctypes.byref(ports_guids_size)): - raise ctypes.WinError() - - ModemsGUIDs = (GUID * 8)() # so far only seen one used, so hope 8 are enough... - modems_guids_size = DWORD() - if not SetupDiClassGuidsFromName( - "Modem", - ModemsGUIDs, - ctypes.sizeof(ModemsGUIDs), - ctypes.byref(modems_guids_size)): - raise ctypes.WinError() - - GUIDs = PortsGUIDs[:ports_guids_size.value] + ModemsGUIDs[:modems_guids_size.value] - - # repeat for all possible GUIDs - for index in range(len(GUIDs)): - bInterfaceNumber = None - g_hdi = SetupDiGetClassDevs( - ctypes.byref(GUIDs[index]), - None, - NULL, - DIGCF_PRESENT) # was DIGCF_PRESENT|DIGCF_DEVICEINTERFACE which misses CDC ports - - devinfo = SP_DEVINFO_DATA() - devinfo.cbSize = ctypes.sizeof(devinfo) - index = 0 - while SetupDiEnumDeviceInfo(g_hdi, index, ctypes.byref(devinfo)): - index += 1 - - # get the real com port name - hkey = SetupDiOpenDevRegKey( - g_hdi, - ctypes.byref(devinfo), - DICS_FLAG_GLOBAL, - 0, - DIREG_DEV, # DIREG_DRV for SW info - KEY_READ) - port_name_buffer = ctypes.create_unicode_buffer(250) - port_name_length = ULONG(ctypes.sizeof(port_name_buffer)) - RegQueryValueEx( - hkey, - "PortName", - None, - None, - ctypes.byref(port_name_buffer), - ctypes.byref(port_name_length)) - RegCloseKey(hkey) - - # unfortunately does this method also include parallel ports. - # we could check for names starting with COM or just exclude LPT - # and hope that other "unknown" names are serial ports... - if port_name_buffer.value.startswith('LPT'): - continue - - # hardware ID - szHardwareID = ctypes.create_unicode_buffer(250) - # try to get ID that includes serial number - if not SetupDiGetDeviceInstanceId( - g_hdi, - ctypes.byref(devinfo), - #~ ctypes.byref(szHardwareID), - szHardwareID, - ctypes.sizeof(szHardwareID) - 1, - None): - # fall back to more generic hardware ID if that would fail - if not SetupDiGetDeviceRegistryProperty( - g_hdi, - ctypes.byref(devinfo), - SPDRP_HARDWAREID, - None, - ctypes.byref(szHardwareID), - ctypes.sizeof(szHardwareID) - 1, - None): - # Ignore ERROR_INSUFFICIENT_BUFFER - if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER: - raise ctypes.WinError() - # stringify - szHardwareID_str = szHardwareID.value - - info = list_ports_common.ListPortInfo(port_name_buffer.value, skip_link_detection=True) - - # in case of USB, make a more readable string, similar to that form - # that we also generate on other platforms - if szHardwareID_str.startswith('USB'): - m = re.search(r'VID_([0-9a-f]{4})(&PID_([0-9a-f]{4}))?(&MI_(\d{2}))?(\\(.*))?', szHardwareID_str, re.I) - if m: - info.vid = int(m.group(1), 16) - if m.group(3): - info.pid = int(m.group(3), 16) - if m.group(5): - bInterfaceNumber = int(m.group(5)) - - # Check that the USB serial number only contains alpha-numeric characters. It - # may be a windows device ID (ephemeral ID) for composite devices. - if m.group(7) and re.match(r'^\w+$', m.group(7)): - info.serial_number = m.group(7) - else: - info.serial_number = get_parent_serial_number(devinfo.DevInst, info.vid, info.pid) - - # calculate a location string - loc_path_str = ctypes.create_unicode_buffer(250) - if SetupDiGetDeviceRegistryProperty( - g_hdi, - ctypes.byref(devinfo), - SPDRP_LOCATION_PATHS, - None, - ctypes.byref(loc_path_str), - ctypes.sizeof(loc_path_str) - 1, - None): - m = re.finditer(r'USBROOT\((\w+)\)|#USB\((\w+)\)', loc_path_str.value) - location = [] - for g in m: - if g.group(1): - location.append('{:d}'.format(int(g.group(1)) + 1)) - else: - if len(location) > 1: - location.append('.') - else: - location.append('-') - location.append(g.group(2)) - if bInterfaceNumber is not None: - location.append(':{}.{}'.format( - 'x', # XXX how to determine correct bConfigurationValue? - bInterfaceNumber)) - if location: - info.location = ''.join(location) - info.hwid = info.usb_info() - elif szHardwareID_str.startswith('FTDIBUS'): - m = re.search(r'VID_([0-9a-f]{4})\+PID_([0-9a-f]{4})(\+(\w+))?', szHardwareID_str, re.I) - if m: - info.vid = int(m.group(1), 16) - info.pid = int(m.group(2), 16) - if m.group(4): - info.serial_number = m.group(4) - # USB location is hidden by FDTI driver :( - info.hwid = info.usb_info() - else: - info.hwid = szHardwareID_str - - # friendly name - szFriendlyName = ctypes.create_unicode_buffer(250) - if SetupDiGetDeviceRegistryProperty( - g_hdi, - ctypes.byref(devinfo), - SPDRP_FRIENDLYNAME, - #~ SPDRP_DEVICEDESC, - None, - ctypes.byref(szFriendlyName), - ctypes.sizeof(szFriendlyName) - 1, - None): - info.description = szFriendlyName.value - #~ else: - # Ignore ERROR_INSUFFICIENT_BUFFER - #~ if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER: - #~ raise IOError("failed to get details for %s (%s)" % (devinfo, szHardwareID.value)) - # ignore errors and still include the port in the list, friendly name will be same as port name - - # manufacturer - szManufacturer = ctypes.create_unicode_buffer(250) - if SetupDiGetDeviceRegistryProperty( - g_hdi, - ctypes.byref(devinfo), - SPDRP_MFG, - #~ SPDRP_DEVICEDESC, - None, - ctypes.byref(szManufacturer), - ctypes.sizeof(szManufacturer) - 1, - None): - info.manufacturer = szManufacturer.value - yield info - SetupDiDestroyDeviceInfoList(g_hdi) - - -def comports(include_links=False): - """Return a list of info objects about serial ports""" - return list(iterate_comports()) - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# test -if __name__ == '__main__': - for port, desc, hwid in sorted(comports()): - print("{}: {} [{}]".format(port, desc, hwid)) diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/miniterm.py b/plotter-app/venv/lib/python3.8/site-packages/serial/tools/miniterm.py deleted file mode 100644 index 2cceff6..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/tools/miniterm.py +++ /dev/null @@ -1,1042 +0,0 @@ -#!/usr/bin/env python -# -# Very simple serial terminal -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# (C)2002-2020 Chris Liechti -# -# SPDX-License-Identifier: BSD-3-Clause - -from __future__ import absolute_import - -import codecs -import os -import sys -import threading - -import serial -from serial.tools.list_ports import comports -from serial.tools import hexlify_codec - -# pylint: disable=wrong-import-order,wrong-import-position - -codecs.register(lambda c: hexlify_codec.getregentry() if c == 'hexlify' else None) - -try: - raw_input -except NameError: - # pylint: disable=redefined-builtin,invalid-name - raw_input = input # in python3 it's "raw" - unichr = chr - - -def key_description(character): - """generate a readable description for a key""" - ascii_code = ord(character) - if ascii_code < 32: - return 'Ctrl+{:c}'.format(ord('@') + ascii_code) - else: - return repr(character) - - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -class ConsoleBase(object): - """OS abstraction for console (input/output codec, no echo)""" - - def __init__(self): - if sys.version_info >= (3, 0): - self.byte_output = sys.stdout.buffer - else: - self.byte_output = sys.stdout - self.output = sys.stdout - - def setup(self): - """Set console to read single characters, no echo""" - - def cleanup(self): - """Restore default console settings""" - - def getkey(self): - """Read a single key from the console""" - return None - - def write_bytes(self, byte_string): - """Write bytes (already encoded)""" - self.byte_output.write(byte_string) - self.byte_output.flush() - - def write(self, text): - """Write string""" - self.output.write(text) - self.output.flush() - - def cancel(self): - """Cancel getkey operation""" - - # - - - - - - - - - - - - - - - - - - - - - - - - - # context manager: - # switch terminal temporary to normal mode (e.g. to get user input) - - def __enter__(self): - self.cleanup() - return self - - def __exit__(self, *args, **kwargs): - self.setup() - - -if os.name == 'nt': # noqa - import msvcrt - import ctypes - import platform - - class Out(object): - """file-like wrapper that uses os.write""" - - def __init__(self, fd): - self.fd = fd - - def flush(self): - pass - - def write(self, s): - os.write(self.fd, s) - - class Console(ConsoleBase): - fncodes = { - ';': '\1bOP', # F1 - '<': '\1bOQ', # F2 - '=': '\1bOR', # F3 - '>': '\1bOS', # F4 - '?': '\1b[15~', # F5 - '@': '\1b[17~', # F6 - 'A': '\1b[18~', # F7 - 'B': '\1b[19~', # F8 - 'C': '\1b[20~', # F9 - 'D': '\1b[21~', # F10 - } - navcodes = { - 'H': '\x1b[A', # UP - 'P': '\x1b[B', # DOWN - 'K': '\x1b[D', # LEFT - 'M': '\x1b[C', # RIGHT - 'G': '\x1b[H', # HOME - 'O': '\x1b[F', # END - 'R': '\x1b[2~', # INSERT - 'S': '\x1b[3~', # DELETE - 'I': '\x1b[5~', # PGUP - 'Q': '\x1b[6~', # PGDN - } - - def __init__(self): - super(Console, self).__init__() - self._saved_ocp = ctypes.windll.kernel32.GetConsoleOutputCP() - self._saved_icp = ctypes.windll.kernel32.GetConsoleCP() - ctypes.windll.kernel32.SetConsoleOutputCP(65001) - ctypes.windll.kernel32.SetConsoleCP(65001) - # ANSI handling available through SetConsoleMode since Windows 10 v1511 - # https://en.wikipedia.org/wiki/ANSI_escape_code#cite_note-win10th2-1 - if platform.release() == '10' and int(platform.version().split('.')[2]) > 10586: - ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 - import ctypes.wintypes as wintypes - if not hasattr(wintypes, 'LPDWORD'): # PY2 - wintypes.LPDWORD = ctypes.POINTER(wintypes.DWORD) - SetConsoleMode = ctypes.windll.kernel32.SetConsoleMode - GetConsoleMode = ctypes.windll.kernel32.GetConsoleMode - GetStdHandle = ctypes.windll.kernel32.GetStdHandle - mode = wintypes.DWORD() - GetConsoleMode(GetStdHandle(-11), ctypes.byref(mode)) - if (mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0: - SetConsoleMode(GetStdHandle(-11), mode.value | ENABLE_VIRTUAL_TERMINAL_PROCESSING) - self._saved_cm = mode - self.output = codecs.getwriter('UTF-8')(Out(sys.stdout.fileno()), 'replace') - # the change of the code page is not propagated to Python, manually fix it - sys.stderr = codecs.getwriter('UTF-8')(Out(sys.stderr.fileno()), 'replace') - sys.stdout = self.output - self.output.encoding = 'UTF-8' # needed for input - - def __del__(self): - ctypes.windll.kernel32.SetConsoleOutputCP(self._saved_ocp) - ctypes.windll.kernel32.SetConsoleCP(self._saved_icp) - try: - ctypes.windll.kernel32.SetConsoleMode(ctypes.windll.kernel32.GetStdHandle(-11), self._saved_cm) - except AttributeError: # in case no _saved_cm - pass - - def getkey(self): - while True: - z = msvcrt.getwch() - if z == unichr(13): - return unichr(10) - elif z is unichr(0) or z is unichr(0xe0): - try: - code = msvcrt.getwch() - if z is unichr(0): - return self.fncodes[code] - else: - return self.navcodes[code] - except KeyError: - pass - else: - return z - - def cancel(self): - # CancelIo, CancelSynchronousIo do not seem to work when using - # getwch, so instead, send a key to the window with the console - hwnd = ctypes.windll.kernel32.GetConsoleWindow() - ctypes.windll.user32.PostMessageA(hwnd, 0x100, 0x0d, 0) - -elif os.name == 'posix': - import atexit - import termios - import fcntl - - class Console(ConsoleBase): - def __init__(self): - super(Console, self).__init__() - self.fd = sys.stdin.fileno() - self.old = termios.tcgetattr(self.fd) - atexit.register(self.cleanup) - if sys.version_info < (3, 0): - self.enc_stdin = codecs.getreader(sys.stdin.encoding)(sys.stdin) - else: - self.enc_stdin = sys.stdin - - def setup(self): - new = termios.tcgetattr(self.fd) - new[3] = new[3] & ~termios.ICANON & ~termios.ECHO & ~termios.ISIG - new[6][termios.VMIN] = 1 - new[6][termios.VTIME] = 0 - termios.tcsetattr(self.fd, termios.TCSANOW, new) - - def getkey(self): - c = self.enc_stdin.read(1) - if c == unichr(0x7f): - c = unichr(8) # map the BS key (which yields DEL) to backspace - return c - - def cancel(self): - fcntl.ioctl(self.fd, termios.TIOCSTI, b'\0') - - def cleanup(self): - termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old) - -else: - raise NotImplementedError( - 'Sorry no implementation for your platform ({}) available.'.format(sys.platform)) - - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -class Transform(object): - """do-nothing: forward all data unchanged""" - def rx(self, text): - """text received from serial port""" - return text - - def tx(self, text): - """text to be sent to serial port""" - return text - - def echo(self, text): - """text to be sent but displayed on console""" - return text - - -class CRLF(Transform): - """ENTER sends CR+LF""" - - def tx(self, text): - return text.replace('\n', '\r\n') - - -class CR(Transform): - """ENTER sends CR""" - - def rx(self, text): - return text.replace('\r', '\n') - - def tx(self, text): - return text.replace('\n', '\r') - - -class LF(Transform): - """ENTER sends LF""" - - -class NoTerminal(Transform): - """remove typical terminal control codes from input""" - - REPLACEMENT_MAP = dict((x, 0x2400 + x) for x in range(32) if unichr(x) not in '\r\n\b\t') - REPLACEMENT_MAP.update( - { - 0x7F: 0x2421, # DEL - 0x9B: 0x2425, # CSI - }) - - def rx(self, text): - return text.translate(self.REPLACEMENT_MAP) - - echo = rx - - -class NoControls(NoTerminal): - """Remove all control codes, incl. CR+LF""" - - REPLACEMENT_MAP = dict((x, 0x2400 + x) for x in range(32)) - REPLACEMENT_MAP.update( - { - 0x20: 0x2423, # visual space - 0x7F: 0x2421, # DEL - 0x9B: 0x2425, # CSI - }) - - -class Printable(Transform): - """Show decimal code for all non-ASCII characters and replace most control codes""" - - def rx(self, text): - r = [] - for c in text: - if ' ' <= c < '\x7f' or c in '\r\n\b\t': - r.append(c) - elif c < ' ': - r.append(unichr(0x2400 + ord(c))) - else: - r.extend(unichr(0x2080 + ord(d) - 48) for d in '{:d}'.format(ord(c))) - r.append(' ') - return ''.join(r) - - echo = rx - - -class Colorize(Transform): - """Apply different colors for received and echo""" - - def __init__(self): - # XXX make it configurable, use colorama? - self.input_color = '\x1b[37m' - self.echo_color = '\x1b[31m' - - def rx(self, text): - return self.input_color + text - - def echo(self, text): - return self.echo_color + text - - -class DebugIO(Transform): - """Print what is sent and received""" - - def rx(self, text): - sys.stderr.write(' [RX:{!r}] '.format(text)) - sys.stderr.flush() - return text - - def tx(self, text): - sys.stderr.write(' [TX:{!r}] '.format(text)) - sys.stderr.flush() - return text - - -# other ideas: -# - add date/time for each newline -# - insert newline after: a) timeout b) packet end character - -EOL_TRANSFORMATIONS = { - 'crlf': CRLF, - 'cr': CR, - 'lf': LF, -} - -TRANSFORMATIONS = { - 'direct': Transform, # no transformation - 'default': NoTerminal, - 'nocontrol': NoControls, - 'printable': Printable, - 'colorize': Colorize, - 'debug': DebugIO, -} - - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -def ask_for_port(): - """\ - Show a list of ports and ask the user for a choice. To make selection - easier on systems with long device names, also allow the input of an - index. - """ - sys.stderr.write('\n--- Available ports:\n') - ports = [] - for n, (port, desc, hwid) in enumerate(sorted(comports()), 1): - sys.stderr.write('--- {:2}: {:20} {!r}\n'.format(n, port, desc)) - ports.append(port) - while True: - port = raw_input('--- Enter port index or full name: ') - try: - index = int(port) - 1 - if not 0 <= index < len(ports): - sys.stderr.write('--- Invalid index!\n') - continue - except ValueError: - pass - else: - port = ports[index] - return port - - -class Miniterm(object): - """\ - Terminal application. Copy data from serial port to console and vice versa. - Handle special keys from the console to show menu etc. - """ - - def __init__(self, serial_instance, echo=False, eol='crlf', filters=()): - self.console = Console() - self.serial = serial_instance - self.echo = echo - self.raw = False - self.input_encoding = 'UTF-8' - self.output_encoding = 'UTF-8' - self.eol = eol - self.filters = filters - self.update_transformations() - self.exit_character = unichr(0x1d) # GS/CTRL+] - self.menu_character = unichr(0x14) # Menu: CTRL+T - self.alive = None - self._reader_alive = None - self.receiver_thread = None - self.rx_decoder = None - self.tx_decoder = None - - def _start_reader(self): - """Start reader thread""" - self._reader_alive = True - # start serial->console thread - self.receiver_thread = threading.Thread(target=self.reader, name='rx') - self.receiver_thread.daemon = True - self.receiver_thread.start() - - def _stop_reader(self): - """Stop reader thread only, wait for clean exit of thread""" - self._reader_alive = False - if hasattr(self.serial, 'cancel_read'): - self.serial.cancel_read() - self.receiver_thread.join() - - def start(self): - """start worker threads""" - self.alive = True - self._start_reader() - # enter console->serial loop - self.transmitter_thread = threading.Thread(target=self.writer, name='tx') - self.transmitter_thread.daemon = True - self.transmitter_thread.start() - self.console.setup() - - def stop(self): - """set flag to stop worker threads""" - self.alive = False - - def join(self, transmit_only=False): - """wait for worker threads to terminate""" - self.transmitter_thread.join() - if not transmit_only: - if hasattr(self.serial, 'cancel_read'): - self.serial.cancel_read() - self.receiver_thread.join() - - def close(self): - self.serial.close() - - def update_transformations(self): - """take list of transformation classes and instantiate them for rx and tx""" - transformations = [EOL_TRANSFORMATIONS[self.eol]] + [TRANSFORMATIONS[f] - for f in self.filters] - self.tx_transformations = [t() for t in transformations] - self.rx_transformations = list(reversed(self.tx_transformations)) - - def set_rx_encoding(self, encoding, errors='replace'): - """set encoding for received data""" - self.input_encoding = encoding - self.rx_decoder = codecs.getincrementaldecoder(encoding)(errors) - - def set_tx_encoding(self, encoding, errors='replace'): - """set encoding for transmitted data""" - self.output_encoding = encoding - self.tx_encoder = codecs.getincrementalencoder(encoding)(errors) - - def dump_port_settings(self): - """Write current settings to sys.stderr""" - sys.stderr.write("\n--- Settings: {p.name} {p.baudrate},{p.bytesize},{p.parity},{p.stopbits}\n".format( - p=self.serial)) - sys.stderr.write('--- RTS: {:8} DTR: {:8} BREAK: {:8}\n'.format( - ('active' if self.serial.rts else 'inactive'), - ('active' if self.serial.dtr else 'inactive'), - ('active' if self.serial.break_condition else 'inactive'))) - try: - sys.stderr.write('--- CTS: {:8} DSR: {:8} RI: {:8} CD: {:8}\n'.format( - ('active' if self.serial.cts else 'inactive'), - ('active' if self.serial.dsr else 'inactive'), - ('active' if self.serial.ri else 'inactive'), - ('active' if self.serial.cd else 'inactive'))) - except serial.SerialException: - # on RFC 2217 ports, it can happen if no modem state notification was - # yet received. ignore this error. - pass - sys.stderr.write('--- software flow control: {}\n'.format('active' if self.serial.xonxoff else 'inactive')) - sys.stderr.write('--- hardware flow control: {}\n'.format('active' if self.serial.rtscts else 'inactive')) - sys.stderr.write('--- serial input encoding: {}\n'.format(self.input_encoding)) - sys.stderr.write('--- serial output encoding: {}\n'.format(self.output_encoding)) - sys.stderr.write('--- EOL: {}\n'.format(self.eol.upper())) - sys.stderr.write('--- filters: {}\n'.format(' '.join(self.filters))) - - def reader(self): - """loop and copy serial->console""" - try: - while self.alive and self._reader_alive: - # read all that is there or wait for one byte - data = self.serial.read(self.serial.in_waiting or 1) - if data: - if self.raw: - self.console.write_bytes(data) - else: - text = self.rx_decoder.decode(data) - for transformation in self.rx_transformations: - text = transformation.rx(text) - self.console.write(text) - except serial.SerialException: - self.alive = False - self.console.cancel() - raise # XXX handle instead of re-raise? - - def writer(self): - """\ - Loop and copy console->serial until self.exit_character character is - found. When self.menu_character is found, interpret the next key - locally. - """ - menu_active = False - try: - while self.alive: - try: - c = self.console.getkey() - except KeyboardInterrupt: - c = '\x03' - if not self.alive: - break - if menu_active: - self.handle_menu_key(c) - menu_active = False - elif c == self.menu_character: - menu_active = True # next char will be for menu - elif c == self.exit_character: - self.stop() # exit app - break - else: - #~ if self.raw: - text = c - for transformation in self.tx_transformations: - text = transformation.tx(text) - self.serial.write(self.tx_encoder.encode(text)) - if self.echo: - echo_text = c - for transformation in self.tx_transformations: - echo_text = transformation.echo(echo_text) - self.console.write(echo_text) - except: - self.alive = False - raise - - def handle_menu_key(self, c): - """Implement a simple menu / settings""" - if c == self.menu_character or c == self.exit_character: - # Menu/exit character again -> send itself - self.serial.write(self.tx_encoder.encode(c)) - if self.echo: - self.console.write(c) - elif c == '\x15': # CTRL+U -> upload file - self.upload_file() - elif c in '\x08hH?': # CTRL+H, h, H, ? -> Show help - sys.stderr.write(self.get_help_text()) - elif c == '\x12': # CTRL+R -> Toggle RTS - self.serial.rts = not self.serial.rts - sys.stderr.write('--- RTS {} ---\n'.format('active' if self.serial.rts else 'inactive')) - elif c == '\x04': # CTRL+D -> Toggle DTR - self.serial.dtr = not self.serial.dtr - sys.stderr.write('--- DTR {} ---\n'.format('active' if self.serial.dtr else 'inactive')) - elif c == '\x02': # CTRL+B -> toggle BREAK condition - self.serial.break_condition = not self.serial.break_condition - sys.stderr.write('--- BREAK {} ---\n'.format('active' if self.serial.break_condition else 'inactive')) - elif c == '\x05': # CTRL+E -> toggle local echo - self.echo = not self.echo - sys.stderr.write('--- local echo {} ---\n'.format('active' if self.echo else 'inactive')) - elif c == '\x06': # CTRL+F -> edit filters - self.change_filter() - elif c == '\x0c': # CTRL+L -> EOL mode - modes = list(EOL_TRANSFORMATIONS) # keys - eol = modes.index(self.eol) + 1 - if eol >= len(modes): - eol = 0 - self.eol = modes[eol] - sys.stderr.write('--- EOL: {} ---\n'.format(self.eol.upper())) - self.update_transformations() - elif c == '\x01': # CTRL+A -> set encoding - self.change_encoding() - elif c == '\x09': # CTRL+I -> info - self.dump_port_settings() - #~ elif c == '\x01': # CTRL+A -> cycle escape mode - #~ elif c == '\x0c': # CTRL+L -> cycle linefeed mode - elif c in 'pP': # P -> change port - self.change_port() - elif c in 'zZ': # S -> suspend / open port temporarily - self.suspend_port() - elif c in 'bB': # B -> change baudrate - self.change_baudrate() - elif c == '8': # 8 -> change to 8 bits - self.serial.bytesize = serial.EIGHTBITS - self.dump_port_settings() - elif c == '7': # 7 -> change to 8 bits - self.serial.bytesize = serial.SEVENBITS - self.dump_port_settings() - elif c in 'eE': # E -> change to even parity - self.serial.parity = serial.PARITY_EVEN - self.dump_port_settings() - elif c in 'oO': # O -> change to odd parity - self.serial.parity = serial.PARITY_ODD - self.dump_port_settings() - elif c in 'mM': # M -> change to mark parity - self.serial.parity = serial.PARITY_MARK - self.dump_port_settings() - elif c in 'sS': # S -> change to space parity - self.serial.parity = serial.PARITY_SPACE - self.dump_port_settings() - elif c in 'nN': # N -> change to no parity - self.serial.parity = serial.PARITY_NONE - self.dump_port_settings() - elif c == '1': # 1 -> change to 1 stop bits - self.serial.stopbits = serial.STOPBITS_ONE - self.dump_port_settings() - elif c == '2': # 2 -> change to 2 stop bits - self.serial.stopbits = serial.STOPBITS_TWO - self.dump_port_settings() - elif c == '3': # 3 -> change to 1.5 stop bits - self.serial.stopbits = serial.STOPBITS_ONE_POINT_FIVE - self.dump_port_settings() - elif c in 'xX': # X -> change software flow control - self.serial.xonxoff = (c == 'X') - self.dump_port_settings() - elif c in 'rR': # R -> change hardware flow control - self.serial.rtscts = (c == 'R') - self.dump_port_settings() - elif c in 'qQ': - self.stop() # Q -> exit app - else: - sys.stderr.write('--- unknown menu character {} --\n'.format(key_description(c))) - - def upload_file(self): - """Ask user for filenname and send its contents""" - sys.stderr.write('\n--- File to upload: ') - sys.stderr.flush() - with self.console: - filename = sys.stdin.readline().rstrip('\r\n') - if filename: - try: - with open(filename, 'rb') as f: - sys.stderr.write('--- Sending file {} ---\n'.format(filename)) - while True: - block = f.read(1024) - if not block: - break - self.serial.write(block) - # Wait for output buffer to drain. - self.serial.flush() - sys.stderr.write('.') # Progress indicator. - sys.stderr.write('\n--- File {} sent ---\n'.format(filename)) - except IOError as e: - sys.stderr.write('--- ERROR opening file {}: {} ---\n'.format(filename, e)) - - def change_filter(self): - """change the i/o transformations""" - sys.stderr.write('\n--- Available Filters:\n') - sys.stderr.write('\n'.join( - '--- {:<10} = {.__doc__}'.format(k, v) - for k, v in sorted(TRANSFORMATIONS.items()))) - sys.stderr.write('\n--- Enter new filter name(s) [{}]: '.format(' '.join(self.filters))) - with self.console: - new_filters = sys.stdin.readline().lower().split() - if new_filters: - for f in new_filters: - if f not in TRANSFORMATIONS: - sys.stderr.write('--- unknown filter: {!r}\n'.format(f)) - break - else: - self.filters = new_filters - self.update_transformations() - sys.stderr.write('--- filters: {}\n'.format(' '.join(self.filters))) - - def change_encoding(self): - """change encoding on the serial port""" - sys.stderr.write('\n--- Enter new encoding name [{}]: '.format(self.input_encoding)) - with self.console: - new_encoding = sys.stdin.readline().strip() - if new_encoding: - try: - codecs.lookup(new_encoding) - except LookupError: - sys.stderr.write('--- invalid encoding name: {}\n'.format(new_encoding)) - else: - self.set_rx_encoding(new_encoding) - self.set_tx_encoding(new_encoding) - sys.stderr.write('--- serial input encoding: {}\n'.format(self.input_encoding)) - sys.stderr.write('--- serial output encoding: {}\n'.format(self.output_encoding)) - - def change_baudrate(self): - """change the baudrate""" - sys.stderr.write('\n--- Baudrate: ') - sys.stderr.flush() - with self.console: - backup = self.serial.baudrate - try: - self.serial.baudrate = int(sys.stdin.readline().strip()) - except ValueError as e: - sys.stderr.write('--- ERROR setting baudrate: {} ---\n'.format(e)) - self.serial.baudrate = backup - else: - self.dump_port_settings() - - def change_port(self): - """Have a conversation with the user to change the serial port""" - with self.console: - try: - port = ask_for_port() - except KeyboardInterrupt: - port = None - if port and port != self.serial.port: - # reader thread needs to be shut down - self._stop_reader() - # save settings - settings = self.serial.getSettingsDict() - try: - new_serial = serial.serial_for_url(port, do_not_open=True) - # restore settings and open - new_serial.applySettingsDict(settings) - new_serial.rts = self.serial.rts - new_serial.dtr = self.serial.dtr - new_serial.open() - new_serial.break_condition = self.serial.break_condition - except Exception as e: - sys.stderr.write('--- ERROR opening new port: {} ---\n'.format(e)) - new_serial.close() - else: - self.serial.close() - self.serial = new_serial - sys.stderr.write('--- Port changed to: {} ---\n'.format(self.serial.port)) - # and restart the reader thread - self._start_reader() - - def suspend_port(self): - """\ - open port temporarily, allow reconnect, exit and port change to get - out of the loop - """ - # reader thread needs to be shut down - self._stop_reader() - self.serial.close() - sys.stderr.write('\n--- Port closed: {} ---\n'.format(self.serial.port)) - do_change_port = False - while not self.serial.is_open: - sys.stderr.write('--- Quit: {exit} | p: port change | any other key to reconnect ---\n'.format( - exit=key_description(self.exit_character))) - k = self.console.getkey() - if k == self.exit_character: - self.stop() # exit app - break - elif k in 'pP': - do_change_port = True - break - try: - self.serial.open() - except Exception as e: - sys.stderr.write('--- ERROR opening port: {} ---\n'.format(e)) - if do_change_port: - self.change_port() - else: - # and restart the reader thread - self._start_reader() - sys.stderr.write('--- Port opened: {} ---\n'.format(self.serial.port)) - - def get_help_text(self): - """return the help text""" - # help text, starts with blank line! - return """ ---- pySerial ({version}) - miniterm - help ---- ---- {exit:8} Exit program (alias {menu} Q) ---- {menu:8} Menu escape key, followed by: ---- Menu keys: ---- {menu:7} Send the menu character itself to remote ---- {exit:7} Send the exit character itself to remote ---- {info:7} Show info ---- {upload:7} Upload file (prompt will be shown) ---- {repr:7} encoding ---- {filter:7} edit filters ---- Toggles: ---- {rts:7} RTS {dtr:7} DTR {brk:7} BREAK ---- {echo:7} echo {eol:7} EOL ---- ---- Port settings ({menu} followed by the following): ---- p change port ---- 7 8 set data bits ---- N E O S M change parity (None, Even, Odd, Space, Mark) ---- 1 2 3 set stop bits (1, 2, 1.5) ---- b change baud rate ---- x X disable/enable software flow control ---- r R disable/enable hardware flow control -""".format(version=getattr(serial, 'VERSION', 'unknown version'), - exit=key_description(self.exit_character), - menu=key_description(self.menu_character), - rts=key_description('\x12'), - dtr=key_description('\x04'), - brk=key_description('\x02'), - echo=key_description('\x05'), - info=key_description('\x09'), - upload=key_description('\x15'), - repr=key_description('\x01'), - filter=key_description('\x06'), - eol=key_description('\x0c')) - - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# default args can be used to override when calling main() from an other script -# e.g to create a miniterm-my-device.py -def main(default_port=None, default_baudrate=9600, default_rts=None, default_dtr=None): - """Command line tool, entry point""" - - import argparse - - parser = argparse.ArgumentParser( - description='Miniterm - A simple terminal program for the serial port.') - - parser.add_argument( - 'port', - nargs='?', - help='serial port name ("-" to show port list)', - default=default_port) - - parser.add_argument( - 'baudrate', - nargs='?', - type=int, - help='set baud rate, default: %(default)s', - default=default_baudrate) - - group = parser.add_argument_group('port settings') - - group.add_argument( - '--parity', - choices=['N', 'E', 'O', 'S', 'M'], - type=lambda c: c.upper(), - help='set parity, one of {N E O S M}, default: N', - default='N') - - group.add_argument( - '--rtscts', - action='store_true', - help='enable RTS/CTS flow control (default off)', - default=False) - - group.add_argument( - '--xonxoff', - action='store_true', - help='enable software flow control (default off)', - default=False) - - group.add_argument( - '--rts', - type=int, - help='set initial RTS line state (possible values: 0, 1)', - default=default_rts) - - group.add_argument( - '--dtr', - type=int, - help='set initial DTR line state (possible values: 0, 1)', - default=default_dtr) - - group.add_argument( - '--non-exclusive', - dest='exclusive', - action='store_false', - help='disable locking for native ports', - default=True) - - group.add_argument( - '--ask', - action='store_true', - help='ask again for port when open fails', - default=False) - - group = parser.add_argument_group('data handling') - - group.add_argument( - '-e', '--echo', - action='store_true', - help='enable local echo (default off)', - default=False) - - group.add_argument( - '--encoding', - dest='serial_port_encoding', - metavar='CODEC', - help='set the encoding for the serial port (e.g. hexlify, Latin1, UTF-8), default: %(default)s', - default='UTF-8') - - group.add_argument( - '-f', '--filter', - action='append', - metavar='NAME', - help='add text transformation', - default=[]) - - group.add_argument( - '--eol', - choices=['CR', 'LF', 'CRLF'], - type=lambda c: c.upper(), - help='end of line mode', - default='CRLF') - - group.add_argument( - '--raw', - action='store_true', - help='Do no apply any encodings/transformations', - default=False) - - group = parser.add_argument_group('hotkeys') - - group.add_argument( - '--exit-char', - type=int, - metavar='NUM', - help='Unicode of special character that is used to exit the application, default: %(default)s', - default=0x1d) # GS/CTRL+] - - group.add_argument( - '--menu-char', - type=int, - metavar='NUM', - help='Unicode code of special character that is used to control miniterm (menu), default: %(default)s', - default=0x14) # Menu: CTRL+T - - group = parser.add_argument_group('diagnostics') - - group.add_argument( - '-q', '--quiet', - action='store_true', - help='suppress non-error messages', - default=False) - - group.add_argument( - '--develop', - action='store_true', - help='show Python traceback on error', - default=False) - - args = parser.parse_args() - - if args.menu_char == args.exit_char: - parser.error('--exit-char can not be the same as --menu-char') - - if args.filter: - if 'help' in args.filter: - sys.stderr.write('Available filters:\n') - sys.stderr.write('\n'.join( - '{:<10} = {.__doc__}'.format(k, v) - for k, v in sorted(TRANSFORMATIONS.items()))) - sys.stderr.write('\n') - sys.exit(1) - filters = args.filter - else: - filters = ['default'] - - while True: - # no port given on command line -> ask user now - if args.port is None or args.port == '-': - try: - args.port = ask_for_port() - except KeyboardInterrupt: - sys.stderr.write('\n') - parser.error('user aborted and port is not given') - else: - if not args.port: - parser.error('port is not given') - try: - serial_instance = serial.serial_for_url( - args.port, - args.baudrate, - parity=args.parity, - rtscts=args.rtscts, - xonxoff=args.xonxoff, - do_not_open=True) - - if not hasattr(serial_instance, 'cancel_read'): - # enable timeout for alive flag polling if cancel_read is not available - serial_instance.timeout = 1 - - if args.dtr is not None: - if not args.quiet: - sys.stderr.write('--- forcing DTR {}\n'.format('active' if args.dtr else 'inactive')) - serial_instance.dtr = args.dtr - if args.rts is not None: - if not args.quiet: - sys.stderr.write('--- forcing RTS {}\n'.format('active' if args.rts else 'inactive')) - serial_instance.rts = args.rts - - if isinstance(serial_instance, serial.Serial): - serial_instance.exclusive = args.exclusive - - serial_instance.open() - except serial.SerialException as e: - sys.stderr.write('could not open port {!r}: {}\n'.format(args.port, e)) - if args.develop: - raise - if not args.ask: - sys.exit(1) - else: - args.port = '-' - else: - break - - miniterm = Miniterm( - serial_instance, - echo=args.echo, - eol=args.eol.lower(), - filters=filters) - miniterm.exit_character = unichr(args.exit_char) - miniterm.menu_character = unichr(args.menu_char) - miniterm.raw = args.raw - miniterm.set_rx_encoding(args.serial_port_encoding) - miniterm.set_tx_encoding(args.serial_port_encoding) - - if not args.quiet: - sys.stderr.write('--- Miniterm on {p.name} {p.baudrate},{p.bytesize},{p.parity},{p.stopbits} ---\n'.format( - p=miniterm.serial)) - sys.stderr.write('--- Quit: {} | Menu: {} | Help: {} followed by {} ---\n'.format( - key_description(miniterm.exit_character), - key_description(miniterm.menu_character), - key_description(miniterm.menu_character), - key_description('\x08'))) - - miniterm.start() - try: - miniterm.join(True) - except KeyboardInterrupt: - pass - if not args.quiet: - sys.stderr.write('\n--- exit ---\n') - miniterm.join() - miniterm.close() - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -if __name__ == '__main__': - main() diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index efc4133..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_alt.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_alt.cpython-38.pyc deleted file mode 100644 index 28d91a1..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_alt.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_cp2110.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_cp2110.cpython-38.pyc deleted file mode 100644 index b5cc72d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_cp2110.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_hwgrep.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_hwgrep.cpython-38.pyc deleted file mode 100644 index a5d4793..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_hwgrep.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_loop.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_loop.cpython-38.pyc deleted file mode 100644 index 9d3d722..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_loop.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_rfc2217.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_rfc2217.cpython-38.pyc deleted file mode 100644 index 5bfb86c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_rfc2217.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_socket.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_socket.cpython-38.pyc deleted file mode 100644 index 621b7fe..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_socket.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_spy.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_spy.cpython-38.pyc deleted file mode 100644 index 0a94816..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/__pycache__/protocol_spy.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/protocol_alt.py b/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/protocol_alt.py deleted file mode 100644 index 2e666ca..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/protocol_alt.py +++ /dev/null @@ -1,57 +0,0 @@ -#! python -# -# This module implements a special URL handler that allows selecting an -# alternate implementation provided by some backends. -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# (C) 2015 Chris Liechti -# -# SPDX-License-Identifier: BSD-3-Clause -# -# URL format: alt://port[?option[=value][&option[=value]]] -# options: -# - class=X used class named X instead of Serial -# -# example: -# use poll based implementation on Posix (Linux): -# python -m serial.tools.miniterm alt:///dev/ttyUSB0?class=PosixPollSerial - -from __future__ import absolute_import - -try: - import urlparse -except ImportError: - import urllib.parse as urlparse - -import serial - - -def serial_class_for_url(url): - """extract host and port from an URL string""" - parts = urlparse.urlsplit(url) - if parts.scheme != 'alt': - raise serial.SerialException( - 'expected a string in the form "alt://port[?option[=value][&option[=value]]]": ' - 'not starting with alt:// ({!r})'.format(parts.scheme)) - class_name = 'Serial' - try: - for option, values in urlparse.parse_qs(parts.query, True).items(): - if option == 'class': - class_name = values[0] - else: - raise ValueError('unknown option: {!r}'.format(option)) - except ValueError as e: - raise serial.SerialException( - 'expected a string in the form ' - '"alt://port[?option[=value][&option[=value]]]": {!r}'.format(e)) - if not hasattr(serial, class_name): - raise ValueError('unknown class: {!r}'.format(class_name)) - cls = getattr(serial, class_name) - if not issubclass(cls, serial.Serial): - raise ValueError('class {!r} is not an instance of Serial'.format(class_name)) - return (''.join([parts.netloc, parts.path]), cls) - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -if __name__ == '__main__': - s = serial.serial_for_url('alt:///dev/ttyS0?class=PosixPollSerial') - print(s) diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/protocol_cp2110.py b/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/protocol_cp2110.py deleted file mode 100644 index 44ad4eb..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/protocol_cp2110.py +++ /dev/null @@ -1,258 +0,0 @@ -#! python -# -# Backend for Silicon Labs CP2110/4 HID-to-UART devices. -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# (C) 2001-2015 Chris Liechti -# (C) 2019 Google LLC -# -# SPDX-License-Identifier: BSD-3-Clause - -# This backend implements support for HID-to-UART devices manufactured -# by Silicon Labs and marketed as CP2110 and CP2114. The -# implementation is (mostly) OS-independent and in userland. It relies -# on cython-hidapi (https://github.com/trezor/cython-hidapi). - -# The HID-to-UART protocol implemented by CP2110/4 is described in the -# AN434 document from Silicon Labs: -# https://www.silabs.com/documents/public/application-notes/AN434-CP2110-4-Interface-Specification.pdf - -# TODO items: - -# - rtscts support is configured for hardware flow control, but the -# signaling is missing (AN434 suggests this is done through GPIO). -# - Cancelling reads and writes is not supported. -# - Baudrate validation is not implemented, as it depends on model and configuration. - -import struct -import threading - -try: - import urlparse -except ImportError: - import urllib.parse as urlparse - -try: - import Queue -except ImportError: - import queue as Queue - -import hid # hidapi - -import serial -from serial.serialutil import SerialBase, SerialException, PortNotOpenError, to_bytes, Timeout - - -# Report IDs and related constant -_REPORT_GETSET_UART_ENABLE = 0x41 -_DISABLE_UART = 0x00 -_ENABLE_UART = 0x01 - -_REPORT_SET_PURGE_FIFOS = 0x43 -_PURGE_TX_FIFO = 0x01 -_PURGE_RX_FIFO = 0x02 - -_REPORT_GETSET_UART_CONFIG = 0x50 - -_REPORT_SET_TRANSMIT_LINE_BREAK = 0x51 -_REPORT_SET_STOP_LINE_BREAK = 0x52 - - -class Serial(SerialBase): - # This is not quite correct. AN343 specifies that the minimum - # baudrate is different between CP2110 and CP2114, and it's halved - # when using non-8-bit symbols. - BAUDRATES = (300, 375, 600, 1200, 1800, 2400, 4800, 9600, 19200, - 38400, 57600, 115200, 230400, 460800, 500000, 576000, - 921600, 1000000) - - def __init__(self, *args, **kwargs): - self._hid_handle = None - self._read_buffer = None - self._thread = None - super(Serial, self).__init__(*args, **kwargs) - - def open(self): - if self._port is None: - raise SerialException("Port must be configured before it can be used.") - if self.is_open: - raise SerialException("Port is already open.") - - self._read_buffer = Queue.Queue() - - self._hid_handle = hid.device() - try: - portpath = self.from_url(self.portstr) - self._hid_handle.open_path(portpath) - except OSError as msg: - raise SerialException(msg.errno, "could not open port {}: {}".format(self._port, msg)) - - try: - self._reconfigure_port() - except: - try: - self._hid_handle.close() - except: - pass - self._hid_handle = None - raise - else: - self.is_open = True - self._thread = threading.Thread(target=self._hid_read_loop) - self._thread.setDaemon(True) - self._thread.setName('pySerial CP2110 reader thread for {}'.format(self._port)) - self._thread.start() - - def from_url(self, url): - parts = urlparse.urlsplit(url) - if parts.scheme != "cp2110": - raise SerialException( - 'expected a string in the forms ' - '"cp2110:///dev/hidraw9" or "cp2110://0001:0023:00": ' - 'not starting with cp2110:// {{!r}}'.format(parts.scheme)) - if parts.netloc: # cp2100://BUS:DEVICE:ENDPOINT, for libusb - return parts.netloc.encode('utf-8') - return parts.path.encode('utf-8') - - def close(self): - self.is_open = False - if self._thread: - self._thread.join(1) # read timeout is 0.1 - self._thread = None - self._hid_handle.close() - self._hid_handle = None - - def _reconfigure_port(self): - parity_value = None - if self._parity == serial.PARITY_NONE: - parity_value = 0x00 - elif self._parity == serial.PARITY_ODD: - parity_value = 0x01 - elif self._parity == serial.PARITY_EVEN: - parity_value = 0x02 - elif self._parity == serial.PARITY_MARK: - parity_value = 0x03 - elif self._parity == serial.PARITY_SPACE: - parity_value = 0x04 - else: - raise ValueError('Invalid parity: {!r}'.format(self._parity)) - - if self.rtscts: - flow_control_value = 0x01 - else: - flow_control_value = 0x00 - - data_bits_value = None - if self._bytesize == 5: - data_bits_value = 0x00 - elif self._bytesize == 6: - data_bits_value = 0x01 - elif self._bytesize == 7: - data_bits_value = 0x02 - elif self._bytesize == 8: - data_bits_value = 0x03 - else: - raise ValueError('Invalid char len: {!r}'.format(self._bytesize)) - - stop_bits_value = None - if self._stopbits == serial.STOPBITS_ONE: - stop_bits_value = 0x00 - elif self._stopbits == serial.STOPBITS_ONE_POINT_FIVE: - stop_bits_value = 0x01 - elif self._stopbits == serial.STOPBITS_TWO: - stop_bits_value = 0x01 - else: - raise ValueError('Invalid stop bit specification: {!r}'.format(self._stopbits)) - - configuration_report = struct.pack( - '>BLBBBB', - _REPORT_GETSET_UART_CONFIG, - self._baudrate, - parity_value, - flow_control_value, - data_bits_value, - stop_bits_value) - - self._hid_handle.send_feature_report(configuration_report) - - self._hid_handle.send_feature_report( - bytes((_REPORT_GETSET_UART_ENABLE, _ENABLE_UART))) - self._update_break_state() - - @property - def in_waiting(self): - return self._read_buffer.qsize() - - def reset_input_buffer(self): - if not self.is_open: - raise PortNotOpenError() - self._hid_handle.send_feature_report( - bytes((_REPORT_SET_PURGE_FIFOS, _PURGE_RX_FIFO))) - # empty read buffer - while self._read_buffer.qsize(): - self._read_buffer.get(False) - - def reset_output_buffer(self): - if not self.is_open: - raise PortNotOpenError() - self._hid_handle.send_feature_report( - bytes((_REPORT_SET_PURGE_FIFOS, _PURGE_TX_FIFO))) - - def _update_break_state(self): - if not self._hid_handle: - raise PortNotOpenError() - - if self._break_state: - self._hid_handle.send_feature_report( - bytes((_REPORT_SET_TRANSMIT_LINE_BREAK, 0))) - else: - # Note that while AN434 states "There are no data bytes in - # the payload other than the Report ID", either hidapi or - # Linux does not seem to send the report otherwise. - self._hid_handle.send_feature_report( - bytes((_REPORT_SET_STOP_LINE_BREAK, 0))) - - def read(self, size=1): - if not self.is_open: - raise PortNotOpenError() - - data = bytearray() - try: - timeout = Timeout(self._timeout) - while len(data) < size: - if self._thread is None: - raise SerialException('connection failed (reader thread died)') - buf = self._read_buffer.get(True, timeout.time_left()) - if buf is None: - return bytes(data) - data += buf - if timeout.expired(): - break - except Queue.Empty: # -> timeout - pass - return bytes(data) - - def write(self, data): - if not self.is_open: - raise PortNotOpenError() - data = to_bytes(data) - tx_len = len(data) - while tx_len > 0: - to_be_sent = min(tx_len, 0x3F) - report = to_bytes([to_be_sent]) + data[:to_be_sent] - self._hid_handle.write(report) - - data = data[to_be_sent:] - tx_len = len(data) - - def _hid_read_loop(self): - try: - while self.is_open: - data = self._hid_handle.read(64, timeout_ms=100) - if not data: - continue - data_len = data.pop(0) - assert data_len == len(data) - self._read_buffer.put(bytearray(data)) - finally: - self._thread = None diff --git a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/protocol_hwgrep.py b/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/protocol_hwgrep.py deleted file mode 100644 index 1a288c9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/serial/urlhandler/protocol_hwgrep.py +++ /dev/null @@ -1,91 +0,0 @@ -#! python -# -# This module implements a special URL handler that uses the port listing to -# find ports by searching the string descriptions. -# -# This file is part of pySerial. https://github.com/pyserial/pyserial -# (C) 2011-2015 Chris Liechti -# -# SPDX-License-Identifier: BSD-3-Clause -# -# URL format: hwgrep://&
                                          # 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
-"""
-
-
-class Version(_BaseVersion):
-
-    _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
-
-    def __init__(self, version):
-        # Validate the version and parse it into pieces
-        match = self._regex.search(version)
-        if not match:
-            raise InvalidVersion("Invalid version: '{0}'".format(version))
-
-        # Store the parsed out pieces of the version
-        self._version = _Version(
-            epoch=int(match.group("epoch")) if match.group("epoch") else 0,
-            release=tuple(int(i) for i in match.group("release").split(".")),
-            pre=_parse_letter_version(match.group("pre_l"), match.group("pre_n")),
-            post=_parse_letter_version(
-                match.group("post_l"), match.group("post_n1") or match.group("post_n2")
-            ),
-            dev=_parse_letter_version(match.group("dev_l"), match.group("dev_n")),
-            local=_parse_local_version(match.group("local")),
-        )
-
-        # Generate a key which will be used for sorting
-        self._key = _cmpkey(
-            self._version.epoch,
-            self._version.release,
-            self._version.pre,
-            self._version.post,
-            self._version.dev,
-            self._version.local,
-        )
-
-    def __repr__(self):
-        return "".format(repr(str(self)))
-
-    def __str__(self):
-        parts = []
-
-        # Epoch
-        if self.epoch != 0:
-            parts.append("{0}!".format(self.epoch))
-
-        # Release segment
-        parts.append(".".join(str(x) for x in self.release))
-
-        # Pre-release
-        if self.pre is not None:
-            parts.append("".join(str(x) for x in self.pre))
-
-        # Post-release
-        if self.post is not None:
-            parts.append(".post{0}".format(self.post))
-
-        # Development release
-        if self.dev is not None:
-            parts.append(".dev{0}".format(self.dev))
-
-        # Local version segment
-        if self.local is not None:
-            parts.append("+{0}".format(self.local))
-
-        return "".join(parts)
-
-    @property
-    def epoch(self):
-        return self._version.epoch
-
-    @property
-    def release(self):
-        return self._version.release
-
-    @property
-    def pre(self):
-        return self._version.pre
-
-    @property
-    def post(self):
-        return self._version.post[1] if self._version.post else None
-
-    @property
-    def dev(self):
-        return self._version.dev[1] if self._version.dev else None
-
-    @property
-    def local(self):
-        if self._version.local:
-            return ".".join(str(x) for x in self._version.local)
-        else:
-            return None
-
-    @property
-    def public(self):
-        return str(self).split("+", 1)[0]
-
-    @property
-    def base_version(self):
-        parts = []
-
-        # Epoch
-        if self.epoch != 0:
-            parts.append("{0}!".format(self.epoch))
-
-        # Release segment
-        parts.append(".".join(str(x) for x in self.release))
-
-        return "".join(parts)
-
-    @property
-    def is_prerelease(self):
-        return self.dev is not None or self.pre is not None
-
-    @property
-    def is_postrelease(self):
-        return self.post is not None
-
-    @property
-    def is_devrelease(self):
-        return self.dev is not None
-
-
-def _parse_letter_version(letter, number):
-    if letter:
-        # We consider there to be an implicit 0 in a pre-release if there is
-        # not a numeral associated with it.
-        if number is None:
-            number = 0
-
-        # We normalize any letters to their lower case form
-        letter = letter.lower()
-
-        # We consider some words to be alternate spellings of other words and
-        # in those cases we want to normalize the spellings to our preferred
-        # spelling.
-        if letter == "alpha":
-            letter = "a"
-        elif letter == "beta":
-            letter = "b"
-        elif letter in ["c", "pre", "preview"]:
-            letter = "rc"
-        elif letter in ["rev", "r"]:
-            letter = "post"
-
-        return letter, int(number)
-    if not letter and number:
-        # We assume if we are given a number, but we are not given a letter
-        # then this is using the implicit post release syntax (e.g. 1.0-1)
-        letter = "post"
-
-        return letter, int(number)
-
-
-_local_version_separators = re.compile(r"[\._-]")
-
-
-def _parse_local_version(local):
-    """
-    Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
-    """
-    if local is not None:
-        return tuple(
-            part.lower() if not part.isdigit() else int(part)
-            for part in _local_version_separators.split(local)
-        )
-
-
-def _cmpkey(epoch, release, pre, post, dev, local):
-    # When we compare a release version, we want to compare it with all of the
-    # trailing zeros removed. So we'll use a reverse the list, drop all the now
-    # leading zeros until we come to something non zero, then take the rest
-    # re-reverse it back into the correct order and make it a tuple and use
-    # that for our sorting key.
-    release = tuple(
-        reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release))))
-    )
-
-    # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
-    # We'll do this by abusing the pre segment, but we _only_ want to do this
-    # if there is not a pre or a post segment. If we have one of those then
-    # the normal sorting rules will handle this case correctly.
-    if pre is None and post is None and dev is not None:
-        pre = -Infinity
-    # Versions without a pre-release (except as noted above) should sort after
-    # those with one.
-    elif pre is None:
-        pre = Infinity
-
-    # Versions without a post segment should sort before those with one.
-    if post is None:
-        post = -Infinity
-
-    # Versions without a development segment should sort after those with one.
-    if dev is None:
-        dev = Infinity
-
-    if local is None:
-        # Versions without a local segment should sort before those with one.
-        local = -Infinity
-    else:
-        # Versions with a local segment need that segment parsed to implement
-        # the sorting rules in PEP440.
-        # - Alpha numeric segments sort before numeric segments
-        # - Alpha numeric segments sort lexicographically
-        # - Numeric segments sort numerically
-        # - Shorter versions sort before longer versions when the prefixes
-        #   match exactly
-        local = tuple((i, "") if isinstance(i, int) else (-Infinity, i) for i in local)
-
-    return epoch, release, pre, post, dev, local
diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/_vendor/pyparsing.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/_vendor/pyparsing.py
deleted file mode 100644
index cf75e1e..0000000
--- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/_vendor/pyparsing.py
+++ /dev/null
@@ -1,5742 +0,0 @@
-# module pyparsing.py
-#
-# Copyright (c) 2003-2018  Paul T. McGuire
-#
-# 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.
-#
-
-__doc__ = \
-"""
-pyparsing module - Classes and methods to define and execute parsing grammars
-=============================================================================
-
-The pyparsing module is an alternative approach to creating and executing simple grammars,
-vs. the traditional lex/yacc approach, or the use of regular expressions.  With pyparsing, you
-don't need to learn a new syntax for defining grammars or matching expressions - the parsing module
-provides a library of classes that you use to construct the grammar directly in Python.
-
-Here is a program to parse "Hello, World!" (or any greeting of the form 
-C{", !"}), built up using L{Word}, L{Literal}, and L{And} elements 
-(L{'+'} operator gives L{And} expressions, strings are auto-converted to
-L{Literal} expressions)::
-
-    from pyparsing import Word, alphas
-
-    # define grammar of a greeting
-    greet = Word(alphas) + "," + Word(alphas) + "!"
-
-    hello = "Hello, World!"
-    print (hello, "->", greet.parseString(hello))
-
-The program outputs the following::
-
-    Hello, World! -> ['Hello', ',', 'World', '!']
-
-The Python representation of the grammar is quite readable, owing to the self-explanatory
-class names, and the use of '+', '|' and '^' operators.
-
-The L{ParseResults} object returned from L{ParserElement.parseString} can be accessed as a nested list, a dictionary, or an
-object with named attributes.
-
-The pyparsing module handles some of the problems that are typically vexing when writing text parsers:
- - extra or missing whitespace (the above program will also handle "Hello,World!", "Hello  ,  World  !", etc.)
- - quoted strings
- - embedded comments
-
-
-Getting Started -
------------------
-Visit the classes L{ParserElement} and L{ParseResults} to see the base classes that most other pyparsing
-classes inherit from. Use the docstrings for examples of how to:
- - construct literal match expressions from L{Literal} and L{CaselessLiteral} classes
- - construct character word-group expressions using the L{Word} class
- - see how to create repetitive expressions using L{ZeroOrMore} and L{OneOrMore} classes
- - use L{'+'}, L{'|'}, L{'^'}, and L{'&'} operators to combine simple expressions into more complex ones
- - associate names with your parsed results using L{ParserElement.setResultsName}
- - find some helpful expression short-cuts like L{delimitedList} and L{oneOf}
- - find more useful common expressions in the L{pyparsing_common} namespace class
-"""
-
-__version__ = "2.2.1"
-__versionTime__ = "18 Sep 2018 00:49 UTC"
-__author__ = "Paul McGuire "
-
-import string
-from weakref import ref as wkref
-import copy
-import sys
-import warnings
-import re
-import sre_constants
-import collections
-import pprint
-import traceback
-import types
-from datetime import datetime
-
-try:
-    from _thread import RLock
-except ImportError:
-    from threading import RLock
-
-try:
-    # Python 3
-    from collections.abc import Iterable
-    from collections.abc import MutableMapping
-except ImportError:
-    # Python 2.7
-    from collections import Iterable
-    from collections import MutableMapping
-
-try:
-    from collections import OrderedDict as _OrderedDict
-except ImportError:
-    try:
-        from ordereddict import OrderedDict as _OrderedDict
-    except ImportError:
-        _OrderedDict = None
-
-#~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) )
-
-__all__ = [
-'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty',
-'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal',
-'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or',
-'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException',
-'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException',
-'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', 
-'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore',
-'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col',
-'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString',
-'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'hexnums',
-'htmlComment', 'javaStyleComment', 'line', 'lineEnd', 'lineStart', 'lineno',
-'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral',
-'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables',
-'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity', 
-'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd',
-'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute',
-'indentedBlock', 'originalTextFor', 'ungroup', 'infixNotation','locatedExpr', 'withClass',
-'CloseMatch', 'tokenMap', 'pyparsing_common',
-]
-
-system_version = tuple(sys.version_info)[:3]
-PY_3 = system_version[0] == 3
-if PY_3:
-    _MAX_INT = sys.maxsize
-    basestring = str
-    unichr = chr
-    _ustr = str
-
-    # build list of single arg builtins, that can be used as parse actions
-    singleArgBuiltins = [sum, len, sorted, reversed, list, tuple, set, any, all, min, max]
-
-else:
-    _MAX_INT = sys.maxint
-    range = xrange
-
-    def _ustr(obj):
-        """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries
-           str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It
-           then < returns the unicode object | encodes it with the default encoding | ... >.
-        """
-        if isinstance(obj,unicode):
-            return obj
-
-        try:
-            # If this works, then _ustr(obj) has the same behaviour as str(obj), so
-            # it won't break any existing code.
-            return str(obj)
-
-        except UnicodeEncodeError:
-            # Else encode it
-            ret = unicode(obj).encode(sys.getdefaultencoding(), 'xmlcharrefreplace')
-            xmlcharref = Regex(r'&#\d+;')
-            xmlcharref.setParseAction(lambda t: '\\u' + hex(int(t[0][2:-1]))[2:])
-            return xmlcharref.transformString(ret)
-
-    # build list of single arg builtins, tolerant of Python version, that can be used as parse actions
-    singleArgBuiltins = []
-    import __builtin__
-    for fname in "sum len sorted reversed list tuple set any all min max".split():
-        try:
-            singleArgBuiltins.append(getattr(__builtin__,fname))
-        except AttributeError:
-            continue
-            
-_generatorType = type((y for y in range(1)))
- 
-def _xml_escape(data):
-    """Escape &, <, >, ", ', etc. in a string of data."""
-
-    # ampersand must be replaced first
-    from_symbols = '&><"\''
-    to_symbols = ('&'+s+';' for s in "amp gt lt quot apos".split())
-    for from_,to_ in zip(from_symbols, to_symbols):
-        data = data.replace(from_, to_)
-    return data
-
-class _Constants(object):
-    pass
-
-alphas     = string.ascii_uppercase + string.ascii_lowercase
-nums       = "0123456789"
-hexnums    = nums + "ABCDEFabcdef"
-alphanums  = alphas + nums
-_bslash    = chr(92)
-printables = "".join(c for c in string.printable if c not in string.whitespace)
-
-class ParseBaseException(Exception):
-    """base exception class for all parsing runtime exceptions"""
-    # Performance tuning: we construct a *lot* of these, so keep this
-    # constructor as small and fast as possible
-    def __init__( self, pstr, loc=0, msg=None, elem=None ):
-        self.loc = loc
-        if msg is None:
-            self.msg = pstr
-            self.pstr = ""
-        else:
-            self.msg = msg
-            self.pstr = pstr
-        self.parserElement = elem
-        self.args = (pstr, loc, msg)
-
-    @classmethod
-    def _from_exception(cls, pe):
-        """
-        internal factory method to simplify creating one type of ParseException 
-        from another - avoids having __init__ signature conflicts among subclasses
-        """
-        return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement)
-
-    def __getattr__( self, aname ):
-        """supported attributes by name are:
-            - lineno - returns the line number of the exception text
-            - col - returns the column number of the exception text
-            - line - returns the line containing the exception text
-        """
-        if( aname == "lineno" ):
-            return lineno( self.loc, self.pstr )
-        elif( aname in ("col", "column") ):
-            return col( self.loc, self.pstr )
-        elif( aname == "line" ):
-            return line( self.loc, self.pstr )
-        else:
-            raise AttributeError(aname)
-
-    def __str__( self ):
-        return "%s (at char %d), (line:%d, col:%d)" % \
-                ( self.msg, self.loc, self.lineno, self.column )
-    def __repr__( self ):
-        return _ustr(self)
-    def markInputline( self, markerString = ">!<" ):
-        """Extracts the exception line from the input string, and marks
-           the location of the exception with a special symbol.
-        """
-        line_str = self.line
-        line_column = self.column - 1
-        if markerString:
-            line_str = "".join((line_str[:line_column],
-                                markerString, line_str[line_column:]))
-        return line_str.strip()
-    def __dir__(self):
-        return "lineno col line".split() + dir(type(self))
-
-class ParseException(ParseBaseException):
-    """
-    Exception thrown when parse expressions don't match class;
-    supported attributes by name are:
-     - lineno - returns the line number of the exception text
-     - col - returns the column number of the exception text
-     - line - returns the line containing the exception text
-        
-    Example::
-        try:
-            Word(nums).setName("integer").parseString("ABC")
-        except ParseException as pe:
-            print(pe)
-            print("column: {}".format(pe.col))
-            
-    prints::
-       Expected integer (at char 0), (line:1, col:1)
-        column: 1
-    """
-    pass
-
-class ParseFatalException(ParseBaseException):
-    """user-throwable exception thrown when inconsistent parse content
-       is found; stops all parsing immediately"""
-    pass
-
-class ParseSyntaxException(ParseFatalException):
-    """just like L{ParseFatalException}, but thrown internally when an
-       L{ErrorStop} ('-' operator) indicates that parsing is to stop 
-       immediately because an unbacktrackable syntax error has been found"""
-    pass
-
-#~ class ReparseException(ParseBaseException):
-    #~ """Experimental class - parse actions can raise this exception to cause
-       #~ pyparsing to reparse the input string:
-        #~ - with a modified input string, and/or
-        #~ - with a modified start location
-       #~ Set the values of the ReparseException in the constructor, and raise the
-       #~ exception in a parse action to cause pyparsing to use the new string/location.
-       #~ Setting the values as None causes no change to be made.
-       #~ """
-    #~ def __init_( self, newstring, restartLoc ):
-        #~ self.newParseText = newstring
-        #~ self.reparseLoc = restartLoc
-
-class RecursiveGrammarException(Exception):
-    """exception thrown by L{ParserElement.validate} if the grammar could be improperly recursive"""
-    def __init__( self, parseElementList ):
-        self.parseElementTrace = parseElementList
-
-    def __str__( self ):
-        return "RecursiveGrammarException: %s" % self.parseElementTrace
-
-class _ParseResultsWithOffset(object):
-    def __init__(self,p1,p2):
-        self.tup = (p1,p2)
-    def __getitem__(self,i):
-        return self.tup[i]
-    def __repr__(self):
-        return repr(self.tup[0])
-    def setOffset(self,i):
-        self.tup = (self.tup[0],i)
-
-class ParseResults(object):
-    """
-    Structured parse results, to provide multiple means of access to the parsed data:
-       - as a list (C{len(results)})
-       - by list index (C{results[0], results[1]}, etc.)
-       - by attribute (C{results.} - see L{ParserElement.setResultsName})
-
-    Example::
-        integer = Word(nums)
-        date_str = (integer.setResultsName("year") + '/' 
-                        + integer.setResultsName("month") + '/' 
-                        + integer.setResultsName("day"))
-        # equivalent form:
-        # date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-
-        # parseString returns a ParseResults object
-        result = date_str.parseString("1999/12/31")
-
-        def test(s, fn=repr):
-            print("%s -> %s" % (s, fn(eval(s))))
-        test("list(result)")
-        test("result[0]")
-        test("result['month']")
-        test("result.day")
-        test("'month' in result")
-        test("'minutes' in result")
-        test("result.dump()", str)
-    prints::
-        list(result) -> ['1999', '/', '12', '/', '31']
-        result[0] -> '1999'
-        result['month'] -> '12'
-        result.day -> '31'
-        'month' in result -> True
-        'minutes' in result -> False
-        result.dump() -> ['1999', '/', '12', '/', '31']
-        - day: 31
-        - month: 12
-        - year: 1999
-    """
-    def __new__(cls, toklist=None, name=None, asList=True, modal=True ):
-        if isinstance(toklist, cls):
-            return toklist
-        retobj = object.__new__(cls)
-        retobj.__doinit = True
-        return retobj
-
-    # Performance tuning: we construct a *lot* of these, so keep this
-    # constructor as small and fast as possible
-    def __init__( self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance ):
-        if self.__doinit:
-            self.__doinit = False
-            self.__name = None
-            self.__parent = None
-            self.__accumNames = {}
-            self.__asList = asList
-            self.__modal = modal
-            if toklist is None:
-                toklist = []
-            if isinstance(toklist, list):
-                self.__toklist = toklist[:]
-            elif isinstance(toklist, _generatorType):
-                self.__toklist = list(toklist)
-            else:
-                self.__toklist = [toklist]
-            self.__tokdict = dict()
-
-        if name is not None and name:
-            if not modal:
-                self.__accumNames[name] = 0
-            if isinstance(name,int):
-                name = _ustr(name) # will always return a str, but use _ustr for consistency
-            self.__name = name
-            if not (isinstance(toklist, (type(None), basestring, list)) and toklist in (None,'',[])):
-                if isinstance(toklist,basestring):
-                    toklist = [ toklist ]
-                if asList:
-                    if isinstance(toklist,ParseResults):
-                        self[name] = _ParseResultsWithOffset(toklist.copy(),0)
-                    else:
-                        self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]),0)
-                    self[name].__name = name
-                else:
-                    try:
-                        self[name] = toklist[0]
-                    except (KeyError,TypeError,IndexError):
-                        self[name] = toklist
-
-    def __getitem__( self, i ):
-        if isinstance( i, (int,slice) ):
-            return self.__toklist[i]
-        else:
-            if i not in self.__accumNames:
-                return self.__tokdict[i][-1][0]
-            else:
-                return ParseResults([ v[0] for v in self.__tokdict[i] ])
-
-    def __setitem__( self, k, v, isinstance=isinstance ):
-        if isinstance(v,_ParseResultsWithOffset):
-            self.__tokdict[k] = self.__tokdict.get(k,list()) + [v]
-            sub = v[0]
-        elif isinstance(k,(int,slice)):
-            self.__toklist[k] = v
-            sub = v
-        else:
-            self.__tokdict[k] = self.__tokdict.get(k,list()) + [_ParseResultsWithOffset(v,0)]
-            sub = v
-        if isinstance(sub,ParseResults):
-            sub.__parent = wkref(self)
-
-    def __delitem__( self, i ):
-        if isinstance(i,(int,slice)):
-            mylen = len( self.__toklist )
-            del self.__toklist[i]
-
-            # convert int to slice
-            if isinstance(i, int):
-                if i < 0:
-                    i += mylen
-                i = slice(i, i+1)
-            # get removed indices
-            removed = list(range(*i.indices(mylen)))
-            removed.reverse()
-            # fixup indices in token dictionary
-            for name,occurrences in self.__tokdict.items():
-                for j in removed:
-                    for k, (value, position) in enumerate(occurrences):
-                        occurrences[k] = _ParseResultsWithOffset(value, position - (position > j))
-        else:
-            del self.__tokdict[i]
-
-    def __contains__( self, k ):
-        return k in self.__tokdict
-
-    def __len__( self ): return len( self.__toklist )
-    def __bool__(self): return ( not not self.__toklist )
-    __nonzero__ = __bool__
-    def __iter__( self ): return iter( self.__toklist )
-    def __reversed__( self ): return iter( self.__toklist[::-1] )
-    def _iterkeys( self ):
-        if hasattr(self.__tokdict, "iterkeys"):
-            return self.__tokdict.iterkeys()
-        else:
-            return iter(self.__tokdict)
-
-    def _itervalues( self ):
-        return (self[k] for k in self._iterkeys())
-            
-    def _iteritems( self ):
-        return ((k, self[k]) for k in self._iterkeys())
-
-    if PY_3:
-        keys = _iterkeys       
-        """Returns an iterator of all named result keys (Python 3.x only)."""
-
-        values = _itervalues
-        """Returns an iterator of all named result values (Python 3.x only)."""
-
-        items = _iteritems
-        """Returns an iterator of all named result key-value tuples (Python 3.x only)."""
-
-    else:
-        iterkeys = _iterkeys
-        """Returns an iterator of all named result keys (Python 2.x only)."""
-
-        itervalues = _itervalues
-        """Returns an iterator of all named result values (Python 2.x only)."""
-
-        iteritems = _iteritems
-        """Returns an iterator of all named result key-value tuples (Python 2.x only)."""
-
-        def keys( self ):
-            """Returns all named result keys (as a list in Python 2.x, as an iterator in Python 3.x)."""
-            return list(self.iterkeys())
-
-        def values( self ):
-            """Returns all named result values (as a list in Python 2.x, as an iterator in Python 3.x)."""
-            return list(self.itervalues())
-                
-        def items( self ):
-            """Returns all named result key-values (as a list of tuples in Python 2.x, as an iterator in Python 3.x)."""
-            return list(self.iteritems())
-
-    def haskeys( self ):
-        """Since keys() returns an iterator, this method is helpful in bypassing
-           code that looks for the existence of any defined results names."""
-        return bool(self.__tokdict)
-        
-    def pop( self, *args, **kwargs):
-        """
-        Removes and returns item at specified index (default=C{last}).
-        Supports both C{list} and C{dict} semantics for C{pop()}. If passed no
-        argument or an integer argument, it will use C{list} semantics
-        and pop tokens from the list of parsed tokens. If passed a 
-        non-integer argument (most likely a string), it will use C{dict}
-        semantics and pop the corresponding value from any defined 
-        results names. A second default return value argument is 
-        supported, just as in C{dict.pop()}.
-
-        Example::
-            def remove_first(tokens):
-                tokens.pop(0)
-            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
-            print(OneOrMore(Word(nums)).addParseAction(remove_first).parseString("0 123 321")) # -> ['123', '321']
-
-            label = Word(alphas)
-            patt = label("LABEL") + OneOrMore(Word(nums))
-            print(patt.parseString("AAB 123 321").dump())
-
-            # Use pop() in a parse action to remove named result (note that corresponding value is not
-            # removed from list form of results)
-            def remove_LABEL(tokens):
-                tokens.pop("LABEL")
-                return tokens
-            patt.addParseAction(remove_LABEL)
-            print(patt.parseString("AAB 123 321").dump())
-        prints::
-            ['AAB', '123', '321']
-            - LABEL: AAB
-
-            ['AAB', '123', '321']
-        """
-        if not args:
-            args = [-1]
-        for k,v in kwargs.items():
-            if k == 'default':
-                args = (args[0], v)
-            else:
-                raise TypeError("pop() got an unexpected keyword argument '%s'" % k)
-        if (isinstance(args[0], int) or 
-                        len(args) == 1 or 
-                        args[0] in self):
-            index = args[0]
-            ret = self[index]
-            del self[index]
-            return ret
-        else:
-            defaultvalue = args[1]
-            return defaultvalue
-
-    def get(self, key, defaultValue=None):
-        """
-        Returns named result matching the given key, or if there is no
-        such name, then returns the given C{defaultValue} or C{None} if no
-        C{defaultValue} is specified.
-
-        Similar to C{dict.get()}.
-        
-        Example::
-            integer = Word(nums)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
-
-            result = date_str.parseString("1999/12/31")
-            print(result.get("year")) # -> '1999'
-            print(result.get("hour", "not specified")) # -> 'not specified'
-            print(result.get("hour")) # -> None
-        """
-        if key in self:
-            return self[key]
-        else:
-            return defaultValue
-
-    def insert( self, index, insStr ):
-        """
-        Inserts new element at location index in the list of parsed tokens.
-        
-        Similar to C{list.insert()}.
-
-        Example::
-            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
-
-            # use a parse action to insert the parse location in the front of the parsed results
-            def insert_locn(locn, tokens):
-                tokens.insert(0, locn)
-            print(OneOrMore(Word(nums)).addParseAction(insert_locn).parseString("0 123 321")) # -> [0, '0', '123', '321']
-        """
-        self.__toklist.insert(index, insStr)
-        # fixup indices in token dictionary
-        for name,occurrences in self.__tokdict.items():
-            for k, (value, position) in enumerate(occurrences):
-                occurrences[k] = _ParseResultsWithOffset(value, position + (position > index))
-
-    def append( self, item ):
-        """
-        Add single element to end of ParseResults list of elements.
-
-        Example::
-            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
-            
-            # use a parse action to compute the sum of the parsed integers, and add it to the end
-            def append_sum(tokens):
-                tokens.append(sum(map(int, tokens)))
-            print(OneOrMore(Word(nums)).addParseAction(append_sum).parseString("0 123 321")) # -> ['0', '123', '321', 444]
-        """
-        self.__toklist.append(item)
-
-    def extend( self, itemseq ):
-        """
-        Add sequence of elements to end of ParseResults list of elements.
-
-        Example::
-            patt = OneOrMore(Word(alphas))
-            
-            # use a parse action to append the reverse of the matched strings, to make a palindrome
-            def make_palindrome(tokens):
-                tokens.extend(reversed([t[::-1] for t in tokens]))
-                return ''.join(tokens)
-            print(patt.addParseAction(make_palindrome).parseString("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl'
-        """
-        if isinstance(itemseq, ParseResults):
-            self += itemseq
-        else:
-            self.__toklist.extend(itemseq)
-
-    def clear( self ):
-        """
-        Clear all elements and results names.
-        """
-        del self.__toklist[:]
-        self.__tokdict.clear()
-
-    def __getattr__( self, name ):
-        try:
-            return self[name]
-        except KeyError:
-            return ""
-            
-        if name in self.__tokdict:
-            if name not in self.__accumNames:
-                return self.__tokdict[name][-1][0]
-            else:
-                return ParseResults([ v[0] for v in self.__tokdict[name] ])
-        else:
-            return ""
-
-    def __add__( self, other ):
-        ret = self.copy()
-        ret += other
-        return ret
-
-    def __iadd__( self, other ):
-        if other.__tokdict:
-            offset = len(self.__toklist)
-            addoffset = lambda a: offset if a<0 else a+offset
-            otheritems = other.__tokdict.items()
-            otherdictitems = [(k, _ParseResultsWithOffset(v[0],addoffset(v[1])) )
-                                for (k,vlist) in otheritems for v in vlist]
-            for k,v in otherdictitems:
-                self[k] = v
-                if isinstance(v[0],ParseResults):
-                    v[0].__parent = wkref(self)
-            
-        self.__toklist += other.__toklist
-        self.__accumNames.update( other.__accumNames )
-        return self
-
-    def __radd__(self, other):
-        if isinstance(other,int) and other == 0:
-            # useful for merging many ParseResults using sum() builtin
-            return self.copy()
-        else:
-            # this may raise a TypeError - so be it
-            return other + self
-        
-    def __repr__( self ):
-        return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) )
-
-    def __str__( self ):
-        return '[' + ', '.join(_ustr(i) if isinstance(i, ParseResults) else repr(i) for i in self.__toklist) + ']'
-
-    def _asStringList( self, sep='' ):
-        out = []
-        for item in self.__toklist:
-            if out and sep:
-                out.append(sep)
-            if isinstance( item, ParseResults ):
-                out += item._asStringList()
-            else:
-                out.append( _ustr(item) )
-        return out
-
-    def asList( self ):
-        """
-        Returns the parse results as a nested list of matching tokens, all converted to strings.
-
-        Example::
-            patt = OneOrMore(Word(alphas))
-            result = patt.parseString("sldkj lsdkj sldkj")
-            # even though the result prints in string-like form, it is actually a pyparsing ParseResults
-            print(type(result), result) # ->  ['sldkj', 'lsdkj', 'sldkj']
-            
-            # Use asList() to create an actual list
-            result_list = result.asList()
-            print(type(result_list), result_list) # ->  ['sldkj', 'lsdkj', 'sldkj']
-        """
-        return [res.asList() if isinstance(res,ParseResults) else res for res in self.__toklist]
-
-    def asDict( self ):
-        """
-        Returns the named parse results as a nested dictionary.
-
-        Example::
-            integer = Word(nums)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-            
-            result = date_str.parseString('12/31/1999')
-            print(type(result), repr(result)) # ->  (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]})
-            
-            result_dict = result.asDict()
-            print(type(result_dict), repr(result_dict)) # ->  {'day': '1999', 'year': '12', 'month': '31'}
-
-            # even though a ParseResults supports dict-like access, sometime you just need to have a dict
-            import json
-            print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable
-            print(json.dumps(result.asDict())) # -> {"month": "31", "day": "1999", "year": "12"}
-        """
-        if PY_3:
-            item_fn = self.items
-        else:
-            item_fn = self.iteritems
-            
-        def toItem(obj):
-            if isinstance(obj, ParseResults):
-                if obj.haskeys():
-                    return obj.asDict()
-                else:
-                    return [toItem(v) for v in obj]
-            else:
-                return obj
-                
-        return dict((k,toItem(v)) for k,v in item_fn())
-
-    def copy( self ):
-        """
-        Returns a new copy of a C{ParseResults} object.
-        """
-        ret = ParseResults( self.__toklist )
-        ret.__tokdict = self.__tokdict.copy()
-        ret.__parent = self.__parent
-        ret.__accumNames.update( self.__accumNames )
-        ret.__name = self.__name
-        return ret
-
-    def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ):
-        """
-        (Deprecated) Returns the parse results as XML. Tags are created for tokens and lists that have defined results names.
-        """
-        nl = "\n"
-        out = []
-        namedItems = dict((v[1],k) for (k,vlist) in self.__tokdict.items()
-                                                            for v in vlist)
-        nextLevelIndent = indent + "  "
-
-        # collapse out indents if formatting is not desired
-        if not formatted:
-            indent = ""
-            nextLevelIndent = ""
-            nl = ""
-
-        selfTag = None
-        if doctag is not None:
-            selfTag = doctag
-        else:
-            if self.__name:
-                selfTag = self.__name
-
-        if not selfTag:
-            if namedItemsOnly:
-                return ""
-            else:
-                selfTag = "ITEM"
-
-        out += [ nl, indent, "<", selfTag, ">" ]
-
-        for i,res in enumerate(self.__toklist):
-            if isinstance(res,ParseResults):
-                if i in namedItems:
-                    out += [ res.asXML(namedItems[i],
-                                        namedItemsOnly and doctag is None,
-                                        nextLevelIndent,
-                                        formatted)]
-                else:
-                    out += [ res.asXML(None,
-                                        namedItemsOnly and doctag is None,
-                                        nextLevelIndent,
-                                        formatted)]
-            else:
-                # individual token, see if there is a name for it
-                resTag = None
-                if i in namedItems:
-                    resTag = namedItems[i]
-                if not resTag:
-                    if namedItemsOnly:
-                        continue
-                    else:
-                        resTag = "ITEM"
-                xmlBodyText = _xml_escape(_ustr(res))
-                out += [ nl, nextLevelIndent, "<", resTag, ">",
-                                                xmlBodyText,
-                                                "" ]
-
-        out += [ nl, indent, "" ]
-        return "".join(out)
-
-    def __lookup(self,sub):
-        for k,vlist in self.__tokdict.items():
-            for v,loc in vlist:
-                if sub is v:
-                    return k
-        return None
-
-    def getName(self):
-        r"""
-        Returns the results name for this token expression. Useful when several 
-        different expressions might match at a particular location.
-
-        Example::
-            integer = Word(nums)
-            ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d")
-            house_number_expr = Suppress('#') + Word(nums, alphanums)
-            user_data = (Group(house_number_expr)("house_number") 
-                        | Group(ssn_expr)("ssn")
-                        | Group(integer)("age"))
-            user_info = OneOrMore(user_data)
-            
-            result = user_info.parseString("22 111-22-3333 #221B")
-            for item in result:
-                print(item.getName(), ':', item[0])
-        prints::
-            age : 22
-            ssn : 111-22-3333
-            house_number : 221B
-        """
-        if self.__name:
-            return self.__name
-        elif self.__parent:
-            par = self.__parent()
-            if par:
-                return par.__lookup(self)
-            else:
-                return None
-        elif (len(self) == 1 and
-               len(self.__tokdict) == 1 and
-               next(iter(self.__tokdict.values()))[0][1] in (0,-1)):
-            return next(iter(self.__tokdict.keys()))
-        else:
-            return None
-
-    def dump(self, indent='', depth=0, full=True):
-        """
-        Diagnostic method for listing out the contents of a C{ParseResults}.
-        Accepts an optional C{indent} argument so that this string can be embedded
-        in a nested display of other data.
-
-        Example::
-            integer = Word(nums)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-            
-            result = date_str.parseString('12/31/1999')
-            print(result.dump())
-        prints::
-            ['12', '/', '31', '/', '1999']
-            - day: 1999
-            - month: 31
-            - year: 12
-        """
-        out = []
-        NL = '\n'
-        out.append( indent+_ustr(self.asList()) )
-        if full:
-            if self.haskeys():
-                items = sorted((str(k), v) for k,v in self.items())
-                for k,v in items:
-                    if out:
-                        out.append(NL)
-                    out.append( "%s%s- %s: " % (indent,('  '*depth), k) )
-                    if isinstance(v,ParseResults):
-                        if v:
-                            out.append( v.dump(indent,depth+1) )
-                        else:
-                            out.append(_ustr(v))
-                    else:
-                        out.append(repr(v))
-            elif any(isinstance(vv,ParseResults) for vv in self):
-                v = self
-                for i,vv in enumerate(v):
-                    if isinstance(vv,ParseResults):
-                        out.append("\n%s%s[%d]:\n%s%s%s" % (indent,('  '*(depth)),i,indent,('  '*(depth+1)),vv.dump(indent,depth+1) ))
-                    else:
-                        out.append("\n%s%s[%d]:\n%s%s%s" % (indent,('  '*(depth)),i,indent,('  '*(depth+1)),_ustr(vv)))
-            
-        return "".join(out)
-
-    def pprint(self, *args, **kwargs):
-        """
-        Pretty-printer for parsed results as a list, using the C{pprint} module.
-        Accepts additional positional or keyword args as defined for the 
-        C{pprint.pprint} method. (U{http://docs.python.org/3/library/pprint.html#pprint.pprint})
-
-        Example::
-            ident = Word(alphas, alphanums)
-            num = Word(nums)
-            func = Forward()
-            term = ident | num | Group('(' + func + ')')
-            func <<= ident + Group(Optional(delimitedList(term)))
-            result = func.parseString("fna a,b,(fnb c,d,200),100")
-            result.pprint(width=40)
-        prints::
-            ['fna',
-             ['a',
-              'b',
-              ['(', 'fnb', ['c', 'd', '200'], ')'],
-              '100']]
-        """
-        pprint.pprint(self.asList(), *args, **kwargs)
-
-    # add support for pickle protocol
-    def __getstate__(self):
-        return ( self.__toklist,
-                 ( self.__tokdict.copy(),
-                   self.__parent is not None and self.__parent() or None,
-                   self.__accumNames,
-                   self.__name ) )
-
-    def __setstate__(self,state):
-        self.__toklist = state[0]
-        (self.__tokdict,
-         par,
-         inAccumNames,
-         self.__name) = state[1]
-        self.__accumNames = {}
-        self.__accumNames.update(inAccumNames)
-        if par is not None:
-            self.__parent = wkref(par)
-        else:
-            self.__parent = None
-
-    def __getnewargs__(self):
-        return self.__toklist, self.__name, self.__asList, self.__modal
-
-    def __dir__(self):
-        return (dir(type(self)) + list(self.keys()))
-
-MutableMapping.register(ParseResults)
-
-def col (loc,strg):
-    """Returns current column within a string, counting newlines as line separators.
-   The first column is number 1.
-
-   Note: the default parsing behavior is to expand tabs in the input string
-   before starting the parsing process.  See L{I{ParserElement.parseString}} for more information
-   on parsing strings containing C{}s, and suggested methods to maintain a
-   consistent view of the parsed string, the parse location, and line and column
-   positions within the parsed string.
-   """
-    s = strg
-    return 1 if 0} for more information
-   on parsing strings containing C{}s, and suggested methods to maintain a
-   consistent view of the parsed string, the parse location, and line and column
-   positions within the parsed string.
-   """
-    return strg.count("\n",0,loc) + 1
-
-def line( loc, strg ):
-    """Returns the line of text containing loc within a string, counting newlines as line separators.
-       """
-    lastCR = strg.rfind("\n", 0, loc)
-    nextCR = strg.find("\n", loc)
-    if nextCR >= 0:
-        return strg[lastCR+1:nextCR]
-    else:
-        return strg[lastCR+1:]
-
-def _defaultStartDebugAction( instring, loc, expr ):
-    print (("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )))
-
-def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ):
-    print ("Matched " + _ustr(expr) + " -> " + str(toks.asList()))
-
-def _defaultExceptionDebugAction( instring, loc, expr, exc ):
-    print ("Exception raised:" + _ustr(exc))
-
-def nullDebugAction(*args):
-    """'Do-nothing' debug action, to suppress debugging output during parsing."""
-    pass
-
-# Only works on Python 3.x - nonlocal is toxic to Python 2 installs
-#~ 'decorator to trim function calls to match the arity of the target'
-#~ def _trim_arity(func, maxargs=3):
-    #~ if func in singleArgBuiltins:
-        #~ return lambda s,l,t: func(t)
-    #~ limit = 0
-    #~ foundArity = False
-    #~ def wrapper(*args):
-        #~ nonlocal limit,foundArity
-        #~ while 1:
-            #~ try:
-                #~ ret = func(*args[limit:])
-                #~ foundArity = True
-                #~ return ret
-            #~ except TypeError:
-                #~ if limit == maxargs or foundArity:
-                    #~ raise
-                #~ limit += 1
-                #~ continue
-    #~ return wrapper
-
-# this version is Python 2.x-3.x cross-compatible
-'decorator to trim function calls to match the arity of the target'
-def _trim_arity(func, maxargs=2):
-    if func in singleArgBuiltins:
-        return lambda s,l,t: func(t)
-    limit = [0]
-    foundArity = [False]
-    
-    # traceback return data structure changed in Py3.5 - normalize back to plain tuples
-    if system_version[:2] >= (3,5):
-        def extract_stack(limit=0):
-            # special handling for Python 3.5.0 - extra deep call stack by 1
-            offset = -3 if system_version == (3,5,0) else -2
-            frame_summary = traceback.extract_stack(limit=-offset+limit-1)[offset]
-            return [frame_summary[:2]]
-        def extract_tb(tb, limit=0):
-            frames = traceback.extract_tb(tb, limit=limit)
-            frame_summary = frames[-1]
-            return [frame_summary[:2]]
-    else:
-        extract_stack = traceback.extract_stack
-        extract_tb = traceback.extract_tb
-    
-    # synthesize what would be returned by traceback.extract_stack at the call to 
-    # user's parse action 'func', so that we don't incur call penalty at parse time
-    
-    LINE_DIFF = 6
-    # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND 
-    # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!!
-    this_line = extract_stack(limit=2)[-1]
-    pa_call_line_synth = (this_line[0], this_line[1]+LINE_DIFF)
-
-    def wrapper(*args):
-        while 1:
-            try:
-                ret = func(*args[limit[0]:])
-                foundArity[0] = True
-                return ret
-            except TypeError:
-                # re-raise TypeErrors if they did not come from our arity testing
-                if foundArity[0]:
-                    raise
-                else:
-                    try:
-                        tb = sys.exc_info()[-1]
-                        if not extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth:
-                            raise
-                    finally:
-                        del tb
-
-                if limit[0] <= maxargs:
-                    limit[0] += 1
-                    continue
-                raise
-
-    # copy func name to wrapper for sensible debug output
-    func_name = ""
-    try:
-        func_name = getattr(func, '__name__', 
-                            getattr(func, '__class__').__name__)
-    except Exception:
-        func_name = str(func)
-    wrapper.__name__ = func_name
-
-    return wrapper
-
-class ParserElement(object):
-    """Abstract base level parser element class."""
-    DEFAULT_WHITE_CHARS = " \n\t\r"
-    verbose_stacktrace = False
-
-    @staticmethod
-    def setDefaultWhitespaceChars( chars ):
-        r"""
-        Overrides the default whitespace chars
-
-        Example::
-            # default whitespace chars are space,  and newline
-            OneOrMore(Word(alphas)).parseString("abc def\nghi jkl")  # -> ['abc', 'def', 'ghi', 'jkl']
-            
-            # change to just treat newline as significant
-            ParserElement.setDefaultWhitespaceChars(" \t")
-            OneOrMore(Word(alphas)).parseString("abc def\nghi jkl")  # -> ['abc', 'def']
-        """
-        ParserElement.DEFAULT_WHITE_CHARS = chars
-
-    @staticmethod
-    def inlineLiteralsUsing(cls):
-        """
-        Set class to be used for inclusion of string literals into a parser.
-        
-        Example::
-            # default literal class used is Literal
-            integer = Word(nums)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
-
-            date_str.parseString("1999/12/31")  # -> ['1999', '/', '12', '/', '31']
-
-
-            # change to Suppress
-            ParserElement.inlineLiteralsUsing(Suppress)
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
-
-            date_str.parseString("1999/12/31")  # -> ['1999', '12', '31']
-        """
-        ParserElement._literalStringClass = cls
-
-    def __init__( self, savelist=False ):
-        self.parseAction = list()
-        self.failAction = None
-        #~ self.name = ""  # don't define self.name, let subclasses try/except upcall
-        self.strRepr = None
-        self.resultsName = None
-        self.saveAsList = savelist
-        self.skipWhitespace = True
-        self.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
-        self.copyDefaultWhiteChars = True
-        self.mayReturnEmpty = False # used when checking for left-recursion
-        self.keepTabs = False
-        self.ignoreExprs = list()
-        self.debug = False
-        self.streamlined = False
-        self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index
-        self.errmsg = ""
-        self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all)
-        self.debugActions = ( None, None, None ) #custom debug actions
-        self.re = None
-        self.callPreparse = True # used to avoid redundant calls to preParse
-        self.callDuringTry = False
-
-    def copy( self ):
-        """
-        Make a copy of this C{ParserElement}.  Useful for defining different parse actions
-        for the same parsing pattern, using copies of the original parse element.
-        
-        Example::
-            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
-            integerK = integer.copy().addParseAction(lambda toks: toks[0]*1024) + Suppress("K")
-            integerM = integer.copy().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M")
-            
-            print(OneOrMore(integerK | integerM | integer).parseString("5K 100 640K 256M"))
-        prints::
-            [5120, 100, 655360, 268435456]
-        Equivalent form of C{expr.copy()} is just C{expr()}::
-            integerM = integer().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M")
-        """
-        cpy = copy.copy( self )
-        cpy.parseAction = self.parseAction[:]
-        cpy.ignoreExprs = self.ignoreExprs[:]
-        if self.copyDefaultWhiteChars:
-            cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
-        return cpy
-
-    def setName( self, name ):
-        """
-        Define name for this expression, makes debugging and exception messages clearer.
-        
-        Example::
-            Word(nums).parseString("ABC")  # -> Exception: Expected W:(0123...) (at char 0), (line:1, col:1)
-            Word(nums).setName("integer").parseString("ABC")  # -> Exception: Expected integer (at char 0), (line:1, col:1)
-        """
-        self.name = name
-        self.errmsg = "Expected " + self.name
-        if hasattr(self,"exception"):
-            self.exception.msg = self.errmsg
-        return self
-
-    def setResultsName( self, name, listAllMatches=False ):
-        """
-        Define name for referencing matching tokens as a nested attribute
-        of the returned parse results.
-        NOTE: this returns a *copy* of the original C{ParserElement} object;
-        this is so that the client can define a basic element, such as an
-        integer, and reference it in multiple places with different names.
-
-        You can also set results names using the abbreviated syntax,
-        C{expr("name")} in place of C{expr.setResultsName("name")} - 
-        see L{I{__call__}<__call__>}.
-
-        Example::
-            date_str = (integer.setResultsName("year") + '/' 
-                        + integer.setResultsName("month") + '/' 
-                        + integer.setResultsName("day"))
-
-            # equivalent form:
-            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
-        """
-        newself = self.copy()
-        if name.endswith("*"):
-            name = name[:-1]
-            listAllMatches=True
-        newself.resultsName = name
-        newself.modalResults = not listAllMatches
-        return newself
-
-    def setBreak(self,breakFlag = True):
-        """Method to invoke the Python pdb debugger when this element is
-           about to be parsed. Set C{breakFlag} to True to enable, False to
-           disable.
-        """
-        if breakFlag:
-            _parseMethod = self._parse
-            def breaker(instring, loc, doActions=True, callPreParse=True):
-                import pdb
-                pdb.set_trace()
-                return _parseMethod( instring, loc, doActions, callPreParse )
-            breaker._originalParseMethod = _parseMethod
-            self._parse = breaker
-        else:
-            if hasattr(self._parse,"_originalParseMethod"):
-                self._parse = self._parse._originalParseMethod
-        return self
-
-    def setParseAction( self, *fns, **kwargs ):
-        """
-        Define one or more actions to perform when successfully matching parse element definition.
-        Parse action fn is a callable method with 0-3 arguments, called as C{fn(s,loc,toks)},
-        C{fn(loc,toks)}, C{fn(toks)}, or just C{fn()}, where:
-         - s   = the original string being parsed (see note below)
-         - loc = the location of the matching substring
-         - toks = a list of the matched tokens, packaged as a C{L{ParseResults}} object
-        If the functions in fns modify the tokens, they can return them as the return
-        value from fn, and the modified list of tokens will replace the original.
-        Otherwise, fn does not need to return any value.
-
-        Optional keyword arguments:
-         - callDuringTry = (default=C{False}) indicate if parse action should be run during lookaheads and alternate testing
-
-        Note: the default parsing behavior is to expand tabs in the input string
-        before starting the parsing process.  See L{I{parseString}} for more information
-        on parsing strings containing C{}s, and suggested methods to maintain a
-        consistent view of the parsed string, the parse location, and line and column
-        positions within the parsed string.
-        
-        Example::
-            integer = Word(nums)
-            date_str = integer + '/' + integer + '/' + integer
-
-            date_str.parseString("1999/12/31")  # -> ['1999', '/', '12', '/', '31']
-
-            # use parse action to convert to ints at parse time
-            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
-            date_str = integer + '/' + integer + '/' + integer
-
-            # note that integer fields are now ints, not strings
-            date_str.parseString("1999/12/31")  # -> [1999, '/', 12, '/', 31]
-        """
-        self.parseAction = list(map(_trim_arity, list(fns)))
-        self.callDuringTry = kwargs.get("callDuringTry", False)
-        return self
-
-    def addParseAction( self, *fns, **kwargs ):
-        """
-        Add one or more parse actions to expression's list of parse actions. See L{I{setParseAction}}.
-        
-        See examples in L{I{copy}}.
-        """
-        self.parseAction += list(map(_trim_arity, list(fns)))
-        self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False)
-        return self
-
-    def addCondition(self, *fns, **kwargs):
-        """Add a boolean predicate function to expression's list of parse actions. See 
-        L{I{setParseAction}} for function call signatures. Unlike C{setParseAction}, 
-        functions passed to C{addCondition} need to return boolean success/fail of the condition.
-
-        Optional keyword arguments:
-         - message = define a custom message to be used in the raised exception
-         - fatal   = if True, will raise ParseFatalException to stop parsing immediately; otherwise will raise ParseException
-         
-        Example::
-            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
-            year_int = integer.copy()
-            year_int.addCondition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later")
-            date_str = year_int + '/' + integer + '/' + integer
-
-            result = date_str.parseString("1999/12/31")  # -> Exception: Only support years 2000 and later (at char 0), (line:1, col:1)
-        """
-        msg = kwargs.get("message", "failed user-defined condition")
-        exc_type = ParseFatalException if kwargs.get("fatal", False) else ParseException
-        for fn in fns:
-            def pa(s,l,t):
-                if not bool(_trim_arity(fn)(s,l,t)):
-                    raise exc_type(s,l,msg)
-            self.parseAction.append(pa)
-        self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False)
-        return self
-
-    def setFailAction( self, fn ):
-        """Define action to perform if parsing fails at this expression.
-           Fail acton fn is a callable function that takes the arguments
-           C{fn(s,loc,expr,err)} where:
-            - s = string being parsed
-            - loc = location where expression match was attempted and failed
-            - expr = the parse expression that failed
-            - err = the exception thrown
-           The function returns no value.  It may throw C{L{ParseFatalException}}
-           if it is desired to stop parsing immediately."""
-        self.failAction = fn
-        return self
-
-    def _skipIgnorables( self, instring, loc ):
-        exprsFound = True
-        while exprsFound:
-            exprsFound = False
-            for e in self.ignoreExprs:
-                try:
-                    while 1:
-                        loc,dummy = e._parse( instring, loc )
-                        exprsFound = True
-                except ParseException:
-                    pass
-        return loc
-
-    def preParse( self, instring, loc ):
-        if self.ignoreExprs:
-            loc = self._skipIgnorables( instring, loc )
-
-        if self.skipWhitespace:
-            wt = self.whiteChars
-            instrlen = len(instring)
-            while loc < instrlen and instring[loc] in wt:
-                loc += 1
-
-        return loc
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        return loc, []
-
-    def postParse( self, instring, loc, tokenlist ):
-        return tokenlist
-
-    #~ @profile
-    def _parseNoCache( self, instring, loc, doActions=True, callPreParse=True ):
-        debugging = ( self.debug ) #and doActions )
-
-        if debugging or self.failAction:
-            #~ print ("Match",self,"at loc",loc,"(%d,%d)" % ( lineno(loc,instring), col(loc,instring) ))
-            if (self.debugActions[0] ):
-                self.debugActions[0]( instring, loc, self )
-            if callPreParse and self.callPreparse:
-                preloc = self.preParse( instring, loc )
-            else:
-                preloc = loc
-            tokensStart = preloc
-            try:
-                try:
-                    loc,tokens = self.parseImpl( instring, preloc, doActions )
-                except IndexError:
-                    raise ParseException( instring, len(instring), self.errmsg, self )
-            except ParseBaseException as err:
-                #~ print ("Exception raised:", err)
-                if self.debugActions[2]:
-                    self.debugActions[2]( instring, tokensStart, self, err )
-                if self.failAction:
-                    self.failAction( instring, tokensStart, self, err )
-                raise
-        else:
-            if callPreParse and self.callPreparse:
-                preloc = self.preParse( instring, loc )
-            else:
-                preloc = loc
-            tokensStart = preloc
-            if self.mayIndexError or preloc >= len(instring):
-                try:
-                    loc,tokens = self.parseImpl( instring, preloc, doActions )
-                except IndexError:
-                    raise ParseException( instring, len(instring), self.errmsg, self )
-            else:
-                loc,tokens = self.parseImpl( instring, preloc, doActions )
-
-        tokens = self.postParse( instring, loc, tokens )
-
-        retTokens = ParseResults( tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults )
-        if self.parseAction and (doActions or self.callDuringTry):
-            if debugging:
-                try:
-                    for fn in self.parseAction:
-                        tokens = fn( instring, tokensStart, retTokens )
-                        if tokens is not None:
-                            retTokens = ParseResults( tokens,
-                                                      self.resultsName,
-                                                      asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
-                                                      modal=self.modalResults )
-                except ParseBaseException as err:
-                    #~ print "Exception raised in user parse action:", err
-                    if (self.debugActions[2] ):
-                        self.debugActions[2]( instring, tokensStart, self, err )
-                    raise
-            else:
-                for fn in self.parseAction:
-                    tokens = fn( instring, tokensStart, retTokens )
-                    if tokens is not None:
-                        retTokens = ParseResults( tokens,
-                                                  self.resultsName,
-                                                  asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
-                                                  modal=self.modalResults )
-        if debugging:
-            #~ print ("Matched",self,"->",retTokens.asList())
-            if (self.debugActions[1] ):
-                self.debugActions[1]( instring, tokensStart, loc, self, retTokens )
-
-        return loc, retTokens
-
-    def tryParse( self, instring, loc ):
-        try:
-            return self._parse( instring, loc, doActions=False )[0]
-        except ParseFatalException:
-            raise ParseException( instring, loc, self.errmsg, self)
-    
-    def canParseNext(self, instring, loc):
-        try:
-            self.tryParse(instring, loc)
-        except (ParseException, IndexError):
-            return False
-        else:
-            return True
-
-    class _UnboundedCache(object):
-        def __init__(self):
-            cache = {}
-            self.not_in_cache = not_in_cache = object()
-
-            def get(self, key):
-                return cache.get(key, not_in_cache)
-
-            def set(self, key, value):
-                cache[key] = value
-
-            def clear(self):
-                cache.clear()
-                
-            def cache_len(self):
-                return len(cache)
-
-            self.get = types.MethodType(get, self)
-            self.set = types.MethodType(set, self)
-            self.clear = types.MethodType(clear, self)
-            self.__len__ = types.MethodType(cache_len, self)
-
-    if _OrderedDict is not None:
-        class _FifoCache(object):
-            def __init__(self, size):
-                self.not_in_cache = not_in_cache = object()
-
-                cache = _OrderedDict()
-
-                def get(self, key):
-                    return cache.get(key, not_in_cache)
-
-                def set(self, key, value):
-                    cache[key] = value
-                    while len(cache) > size:
-                        try:
-                            cache.popitem(False)
-                        except KeyError:
-                            pass
-
-                def clear(self):
-                    cache.clear()
-
-                def cache_len(self):
-                    return len(cache)
-
-                self.get = types.MethodType(get, self)
-                self.set = types.MethodType(set, self)
-                self.clear = types.MethodType(clear, self)
-                self.__len__ = types.MethodType(cache_len, self)
-
-    else:
-        class _FifoCache(object):
-            def __init__(self, size):
-                self.not_in_cache = not_in_cache = object()
-
-                cache = {}
-                key_fifo = collections.deque([], size)
-
-                def get(self, key):
-                    return cache.get(key, not_in_cache)
-
-                def set(self, key, value):
-                    cache[key] = value
-                    while len(key_fifo) > size:
-                        cache.pop(key_fifo.popleft(), None)
-                    key_fifo.append(key)
-
-                def clear(self):
-                    cache.clear()
-                    key_fifo.clear()
-
-                def cache_len(self):
-                    return len(cache)
-
-                self.get = types.MethodType(get, self)
-                self.set = types.MethodType(set, self)
-                self.clear = types.MethodType(clear, self)
-                self.__len__ = types.MethodType(cache_len, self)
-
-    # argument cache for optimizing repeated calls when backtracking through recursive expressions
-    packrat_cache = {} # this is set later by enabledPackrat(); this is here so that resetCache() doesn't fail
-    packrat_cache_lock = RLock()
-    packrat_cache_stats = [0, 0]
-
-    # this method gets repeatedly called during backtracking with the same arguments -
-    # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression
-    def _parseCache( self, instring, loc, doActions=True, callPreParse=True ):
-        HIT, MISS = 0, 1
-        lookup = (self, instring, loc, callPreParse, doActions)
-        with ParserElement.packrat_cache_lock:
-            cache = ParserElement.packrat_cache
-            value = cache.get(lookup)
-            if value is cache.not_in_cache:
-                ParserElement.packrat_cache_stats[MISS] += 1
-                try:
-                    value = self._parseNoCache(instring, loc, doActions, callPreParse)
-                except ParseBaseException as pe:
-                    # cache a copy of the exception, without the traceback
-                    cache.set(lookup, pe.__class__(*pe.args))
-                    raise
-                else:
-                    cache.set(lookup, (value[0], value[1].copy()))
-                    return value
-            else:
-                ParserElement.packrat_cache_stats[HIT] += 1
-                if isinstance(value, Exception):
-                    raise value
-                return (value[0], value[1].copy())
-
-    _parse = _parseNoCache
-
-    @staticmethod
-    def resetCache():
-        ParserElement.packrat_cache.clear()
-        ParserElement.packrat_cache_stats[:] = [0] * len(ParserElement.packrat_cache_stats)
-
-    _packratEnabled = False
-    @staticmethod
-    def enablePackrat(cache_size_limit=128):
-        """Enables "packrat" parsing, which adds memoizing to the parsing logic.
-           Repeated parse attempts at the same string location (which happens
-           often in many complex grammars) can immediately return a cached value,
-           instead of re-executing parsing/validating code.  Memoizing is done of
-           both valid results and parsing exceptions.
-           
-           Parameters:
-            - cache_size_limit - (default=C{128}) - if an integer value is provided
-              will limit the size of the packrat cache; if None is passed, then
-              the cache size will be unbounded; if 0 is passed, the cache will
-              be effectively disabled.
-            
-           This speedup may break existing programs that use parse actions that
-           have side-effects.  For this reason, packrat parsing is disabled when
-           you first import pyparsing.  To activate the packrat feature, your
-           program must call the class method C{ParserElement.enablePackrat()}.  If
-           your program uses C{psyco} to "compile as you go", you must call
-           C{enablePackrat} before calling C{psyco.full()}.  If you do not do this,
-           Python will crash.  For best results, call C{enablePackrat()} immediately
-           after importing pyparsing.
-           
-           Example::
-               import pyparsing
-               pyparsing.ParserElement.enablePackrat()
-        """
-        if not ParserElement._packratEnabled:
-            ParserElement._packratEnabled = True
-            if cache_size_limit is None:
-                ParserElement.packrat_cache = ParserElement._UnboundedCache()
-            else:
-                ParserElement.packrat_cache = ParserElement._FifoCache(cache_size_limit)
-            ParserElement._parse = ParserElement._parseCache
-
-    def parseString( self, instring, parseAll=False ):
-        """
-        Execute the parse expression with the given string.
-        This is the main interface to the client code, once the complete
-        expression has been built.
-
-        If you want the grammar to require that the entire input string be
-        successfully parsed, then set C{parseAll} to True (equivalent to ending
-        the grammar with C{L{StringEnd()}}).
-
-        Note: C{parseString} implicitly calls C{expandtabs()} on the input string,
-        in order to report proper column numbers in parse actions.
-        If the input string contains tabs and
-        the grammar uses parse actions that use the C{loc} argument to index into the
-        string being parsed, you can ensure you have a consistent view of the input
-        string by:
-         - calling C{parseWithTabs} on your grammar before calling C{parseString}
-           (see L{I{parseWithTabs}})
-         - define your parse action using the full C{(s,loc,toks)} signature, and
-           reference the input string using the parse action's C{s} argument
-         - explictly expand the tabs in your input string before calling
-           C{parseString}
-        
-        Example::
-            Word('a').parseString('aaaaabaaa')  # -> ['aaaaa']
-            Word('a').parseString('aaaaabaaa', parseAll=True)  # -> Exception: Expected end of text
-        """
-        ParserElement.resetCache()
-        if not self.streamlined:
-            self.streamline()
-            #~ self.saveAsList = True
-        for e in self.ignoreExprs:
-            e.streamline()
-        if not self.keepTabs:
-            instring = instring.expandtabs()
-        try:
-            loc, tokens = self._parse( instring, 0 )
-            if parseAll:
-                loc = self.preParse( instring, loc )
-                se = Empty() + StringEnd()
-                se._parse( instring, loc )
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clears out pyparsing internal stack trace
-                raise exc
-        else:
-            return tokens
-
-    def scanString( self, instring, maxMatches=_MAX_INT, overlap=False ):
-        """
-        Scan the input string for expression matches.  Each match will return the
-        matching tokens, start location, and end location.  May be called with optional
-        C{maxMatches} argument, to clip scanning after 'n' matches are found.  If
-        C{overlap} is specified, then overlapping matches will be reported.
-
-        Note that the start and end locations are reported relative to the string
-        being parsed.  See L{I{parseString}} for more information on parsing
-        strings with embedded tabs.
-
-        Example::
-            source = "sldjf123lsdjjkf345sldkjf879lkjsfd987"
-            print(source)
-            for tokens,start,end in Word(alphas).scanString(source):
-                print(' '*start + '^'*(end-start))
-                print(' '*start + tokens[0])
-        
-        prints::
-        
-            sldjf123lsdjjkf345sldkjf879lkjsfd987
-            ^^^^^
-            sldjf
-                    ^^^^^^^
-                    lsdjjkf
-                              ^^^^^^
-                              sldkjf
-                                       ^^^^^^
-                                       lkjsfd
-        """
-        if not self.streamlined:
-            self.streamline()
-        for e in self.ignoreExprs:
-            e.streamline()
-
-        if not self.keepTabs:
-            instring = _ustr(instring).expandtabs()
-        instrlen = len(instring)
-        loc = 0
-        preparseFn = self.preParse
-        parseFn = self._parse
-        ParserElement.resetCache()
-        matches = 0
-        try:
-            while loc <= instrlen and matches < maxMatches:
-                try:
-                    preloc = preparseFn( instring, loc )
-                    nextLoc,tokens = parseFn( instring, preloc, callPreParse=False )
-                except ParseException:
-                    loc = preloc+1
-                else:
-                    if nextLoc > loc:
-                        matches += 1
-                        yield tokens, preloc, nextLoc
-                        if overlap:
-                            nextloc = preparseFn( instring, loc )
-                            if nextloc > loc:
-                                loc = nextLoc
-                            else:
-                                loc += 1
-                        else:
-                            loc = nextLoc
-                    else:
-                        loc = preloc+1
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clears out pyparsing internal stack trace
-                raise exc
-
-    def transformString( self, instring ):
-        """
-        Extension to C{L{scanString}}, to modify matching text with modified tokens that may
-        be returned from a parse action.  To use C{transformString}, define a grammar and
-        attach a parse action to it that modifies the returned token list.
-        Invoking C{transformString()} on a target string will then scan for matches,
-        and replace the matched text patterns according to the logic in the parse
-        action.  C{transformString()} returns the resulting transformed string.
-        
-        Example::
-            wd = Word(alphas)
-            wd.setParseAction(lambda toks: toks[0].title())
-            
-            print(wd.transformString("now is the winter of our discontent made glorious summer by this sun of york."))
-        Prints::
-            Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York.
-        """
-        out = []
-        lastE = 0
-        # force preservation of s, to minimize unwanted transformation of string, and to
-        # keep string locs straight between transformString and scanString
-        self.keepTabs = True
-        try:
-            for t,s,e in self.scanString( instring ):
-                out.append( instring[lastE:s] )
-                if t:
-                    if isinstance(t,ParseResults):
-                        out += t.asList()
-                    elif isinstance(t,list):
-                        out += t
-                    else:
-                        out.append(t)
-                lastE = e
-            out.append(instring[lastE:])
-            out = [o for o in out if o]
-            return "".join(map(_ustr,_flatten(out)))
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clears out pyparsing internal stack trace
-                raise exc
-
-    def searchString( self, instring, maxMatches=_MAX_INT ):
-        """
-        Another extension to C{L{scanString}}, simplifying the access to the tokens found
-        to match the given parse expression.  May be called with optional
-        C{maxMatches} argument, to clip searching after 'n' matches are found.
-        
-        Example::
-            # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters
-            cap_word = Word(alphas.upper(), alphas.lower())
-            
-            print(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity"))
-
-            # the sum() builtin can be used to merge results into a single ParseResults object
-            print(sum(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity")))
-        prints::
-            [['More'], ['Iron'], ['Lead'], ['Gold'], ['I'], ['Electricity']]
-            ['More', 'Iron', 'Lead', 'Gold', 'I', 'Electricity']
-        """
-        try:
-            return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ])
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clears out pyparsing internal stack trace
-                raise exc
-
-    def split(self, instring, maxsplit=_MAX_INT, includeSeparators=False):
-        """
-        Generator method to split a string using the given expression as a separator.
-        May be called with optional C{maxsplit} argument, to limit the number of splits;
-        and the optional C{includeSeparators} argument (default=C{False}), if the separating
-        matching text should be included in the split results.
-        
-        Example::        
-            punc = oneOf(list(".,;:/-!?"))
-            print(list(punc.split("This, this?, this sentence, is badly punctuated!")))
-        prints::
-            ['This', ' this', '', ' this sentence', ' is badly punctuated', '']
-        """
-        splits = 0
-        last = 0
-        for t,s,e in self.scanString(instring, maxMatches=maxsplit):
-            yield instring[last:s]
-            if includeSeparators:
-                yield t[0]
-            last = e
-        yield instring[last:]
-
-    def __add__(self, other ):
-        """
-        Implementation of + operator - returns C{L{And}}. Adding strings to a ParserElement
-        converts them to L{Literal}s by default.
-        
-        Example::
-            greet = Word(alphas) + "," + Word(alphas) + "!"
-            hello = "Hello, World!"
-            print (hello, "->", greet.parseString(hello))
-        Prints::
-            Hello, World! -> ['Hello', ',', 'World', '!']
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return And( [ self, other ] )
-
-    def __radd__(self, other ):
-        """
-        Implementation of + operator when left operand is not a C{L{ParserElement}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return other + self
-
-    def __sub__(self, other):
-        """
-        Implementation of - operator, returns C{L{And}} with error stop
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return self + And._ErrorStop() + other
-
-    def __rsub__(self, other ):
-        """
-        Implementation of - operator when left operand is not a C{L{ParserElement}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return other - self
-
-    def __mul__(self,other):
-        """
-        Implementation of * operator, allows use of C{expr * 3} in place of
-        C{expr + expr + expr}.  Expressions may also me multiplied by a 2-integer
-        tuple, similar to C{{min,max}} multipliers in regular expressions.  Tuples
-        may also include C{None} as in:
-         - C{expr*(n,None)} or C{expr*(n,)} is equivalent
-              to C{expr*n + L{ZeroOrMore}(expr)}
-              (read as "at least n instances of C{expr}")
-         - C{expr*(None,n)} is equivalent to C{expr*(0,n)}
-              (read as "0 to n instances of C{expr}")
-         - C{expr*(None,None)} is equivalent to C{L{ZeroOrMore}(expr)}
-         - C{expr*(1,None)} is equivalent to C{L{OneOrMore}(expr)}
-
-        Note that C{expr*(None,n)} does not raise an exception if
-        more than n exprs exist in the input stream; that is,
-        C{expr*(None,n)} does not enforce a maximum number of expr
-        occurrences.  If this behavior is desired, then write
-        C{expr*(None,n) + ~expr}
-        """
-        if isinstance(other,int):
-            minElements, optElements = other,0
-        elif isinstance(other,tuple):
-            other = (other + (None, None))[:2]
-            if other[0] is None:
-                other = (0, other[1])
-            if isinstance(other[0],int) and other[1] is None:
-                if other[0] == 0:
-                    return ZeroOrMore(self)
-                if other[0] == 1:
-                    return OneOrMore(self)
-                else:
-                    return self*other[0] + ZeroOrMore(self)
-            elif isinstance(other[0],int) and isinstance(other[1],int):
-                minElements, optElements = other
-                optElements -= minElements
-            else:
-                raise TypeError("cannot multiply 'ParserElement' and ('%s','%s') objects", type(other[0]),type(other[1]))
-        else:
-            raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other))
-
-        if minElements < 0:
-            raise ValueError("cannot multiply ParserElement by negative value")
-        if optElements < 0:
-            raise ValueError("second tuple value must be greater or equal to first tuple value")
-        if minElements == optElements == 0:
-            raise ValueError("cannot multiply ParserElement by 0 or (0,0)")
-
-        if (optElements):
-            def makeOptionalList(n):
-                if n>1:
-                    return Optional(self + makeOptionalList(n-1))
-                else:
-                    return Optional(self)
-            if minElements:
-                if minElements == 1:
-                    ret = self + makeOptionalList(optElements)
-                else:
-                    ret = And([self]*minElements) + makeOptionalList(optElements)
-            else:
-                ret = makeOptionalList(optElements)
-        else:
-            if minElements == 1:
-                ret = self
-            else:
-                ret = And([self]*minElements)
-        return ret
-
-    def __rmul__(self, other):
-        return self.__mul__(other)
-
-    def __or__(self, other ):
-        """
-        Implementation of | operator - returns C{L{MatchFirst}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return MatchFirst( [ self, other ] )
-
-    def __ror__(self, other ):
-        """
-        Implementation of | operator when left operand is not a C{L{ParserElement}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return other | self
-
-    def __xor__(self, other ):
-        """
-        Implementation of ^ operator - returns C{L{Or}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return Or( [ self, other ] )
-
-    def __rxor__(self, other ):
-        """
-        Implementation of ^ operator when left operand is not a C{L{ParserElement}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return other ^ self
-
-    def __and__(self, other ):
-        """
-        Implementation of & operator - returns C{L{Each}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return Each( [ self, other ] )
-
-    def __rand__(self, other ):
-        """
-        Implementation of & operator when left operand is not a C{L{ParserElement}}
-        """
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        if not isinstance( other, ParserElement ):
-            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
-                    SyntaxWarning, stacklevel=2)
-            return None
-        return other & self
-
-    def __invert__( self ):
-        """
-        Implementation of ~ operator - returns C{L{NotAny}}
-        """
-        return NotAny( self )
-
-    def __call__(self, name=None):
-        """
-        Shortcut for C{L{setResultsName}}, with C{listAllMatches=False}.
-        
-        If C{name} is given with a trailing C{'*'} character, then C{listAllMatches} will be
-        passed as C{True}.
-           
-        If C{name} is omitted, same as calling C{L{copy}}.
-
-        Example::
-            # these are equivalent
-            userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno")
-            userdata = Word(alphas)("name") + Word(nums+"-")("socsecno")             
-        """
-        if name is not None:
-            return self.setResultsName(name)
-        else:
-            return self.copy()
-
-    def suppress( self ):
-        """
-        Suppresses the output of this C{ParserElement}; useful to keep punctuation from
-        cluttering up returned output.
-        """
-        return Suppress( self )
-
-    def leaveWhitespace( self ):
-        """
-        Disables the skipping of whitespace before matching the characters in the
-        C{ParserElement}'s defined pattern.  This is normally only used internally by
-        the pyparsing module, but may be needed in some whitespace-sensitive grammars.
-        """
-        self.skipWhitespace = False
-        return self
-
-    def setWhitespaceChars( self, chars ):
-        """
-        Overrides the default whitespace chars
-        """
-        self.skipWhitespace = True
-        self.whiteChars = chars
-        self.copyDefaultWhiteChars = False
-        return self
-
-    def parseWithTabs( self ):
-        """
-        Overrides default behavior to expand C{}s to spaces before parsing the input string.
-        Must be called before C{parseString} when the input grammar contains elements that
-        match C{} characters.
-        """
-        self.keepTabs = True
-        return self
-
-    def ignore( self, other ):
-        """
-        Define expression to be ignored (e.g., comments) while doing pattern
-        matching; may be called repeatedly, to define multiple comment or other
-        ignorable patterns.
-        
-        Example::
-            patt = OneOrMore(Word(alphas))
-            patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj']
-            
-            patt.ignore(cStyleComment)
-            patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj', 'lskjd']
-        """
-        if isinstance(other, basestring):
-            other = Suppress(other)
-
-        if isinstance( other, Suppress ):
-            if other not in self.ignoreExprs:
-                self.ignoreExprs.append(other)
-        else:
-            self.ignoreExprs.append( Suppress( other.copy() ) )
-        return self
-
-    def setDebugActions( self, startAction, successAction, exceptionAction ):
-        """
-        Enable display of debugging messages while doing pattern matching.
-        """
-        self.debugActions = (startAction or _defaultStartDebugAction,
-                             successAction or _defaultSuccessDebugAction,
-                             exceptionAction or _defaultExceptionDebugAction)
-        self.debug = True
-        return self
-
-    def setDebug( self, flag=True ):
-        """
-        Enable display of debugging messages while doing pattern matching.
-        Set C{flag} to True to enable, False to disable.
-
-        Example::
-            wd = Word(alphas).setName("alphaword")
-            integer = Word(nums).setName("numword")
-            term = wd | integer
-            
-            # turn on debugging for wd
-            wd.setDebug()
-
-            OneOrMore(term).parseString("abc 123 xyz 890")
-        
-        prints::
-            Match alphaword at loc 0(1,1)
-            Matched alphaword -> ['abc']
-            Match alphaword at loc 3(1,4)
-            Exception raised:Expected alphaword (at char 4), (line:1, col:5)
-            Match alphaword at loc 7(1,8)
-            Matched alphaword -> ['xyz']
-            Match alphaword at loc 11(1,12)
-            Exception raised:Expected alphaword (at char 12), (line:1, col:13)
-            Match alphaword at loc 15(1,16)
-            Exception raised:Expected alphaword (at char 15), (line:1, col:16)
-
-        The output shown is that produced by the default debug actions - custom debug actions can be
-        specified using L{setDebugActions}. Prior to attempting
-        to match the C{wd} expression, the debugging message C{"Match  at loc (,)"}
-        is shown. Then if the parse succeeds, a C{"Matched"} message is shown, or an C{"Exception raised"}
-        message is shown. Also note the use of L{setName} to assign a human-readable name to the expression,
-        which makes debugging and exception messages easier to understand - for instance, the default
-        name created for the C{Word} expression without calling C{setName} is C{"W:(ABCD...)"}.
-        """
-        if flag:
-            self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction )
-        else:
-            self.debug = False
-        return self
-
-    def __str__( self ):
-        return self.name
-
-    def __repr__( self ):
-        return _ustr(self)
-
-    def streamline( self ):
-        self.streamlined = True
-        self.strRepr = None
-        return self
-
-    def checkRecursion( self, parseElementList ):
-        pass
-
-    def validate( self, validateTrace=[] ):
-        """
-        Check defined expressions for valid structure, check for infinite recursive definitions.
-        """
-        self.checkRecursion( [] )
-
-    def parseFile( self, file_or_filename, parseAll=False ):
-        """
-        Execute the parse expression on the given file or filename.
-        If a filename is specified (instead of a file object),
-        the entire file is opened, read, and closed before parsing.
-        """
-        try:
-            file_contents = file_or_filename.read()
-        except AttributeError:
-            with open(file_or_filename, "r") as f:
-                file_contents = f.read()
-        try:
-            return self.parseString(file_contents, parseAll)
-        except ParseBaseException as exc:
-            if ParserElement.verbose_stacktrace:
-                raise
-            else:
-                # catch and re-raise exception from here, clears out pyparsing internal stack trace
-                raise exc
-
-    def __eq__(self,other):
-        if isinstance(other, ParserElement):
-            return self is other or vars(self) == vars(other)
-        elif isinstance(other, basestring):
-            return self.matches(other)
-        else:
-            return super(ParserElement,self)==other
-
-    def __ne__(self,other):
-        return not (self == other)
-
-    def __hash__(self):
-        return hash(id(self))
-
-    def __req__(self,other):
-        return self == other
-
-    def __rne__(self,other):
-        return not (self == other)
-
-    def matches(self, testString, parseAll=True):
-        """
-        Method for quick testing of a parser against a test string. Good for simple 
-        inline microtests of sub expressions while building up larger parser.
-           
-        Parameters:
-         - testString - to test against this expression for a match
-         - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests
-            
-        Example::
-            expr = Word(nums)
-            assert expr.matches("100")
-        """
-        try:
-            self.parseString(_ustr(testString), parseAll=parseAll)
-            return True
-        except ParseBaseException:
-            return False
-                
-    def runTests(self, tests, parseAll=True, comment='#', fullDump=True, printResults=True, failureTests=False):
-        """
-        Execute the parse expression on a series of test strings, showing each
-        test, the parsed results or where the parse failed. Quick and easy way to
-        run a parse expression against a list of sample strings.
-           
-        Parameters:
-         - tests - a list of separate test strings, or a multiline string of test strings
-         - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests           
-         - comment - (default=C{'#'}) - expression for indicating embedded comments in the test 
-              string; pass None to disable comment filtering
-         - fullDump - (default=C{True}) - dump results as list followed by results names in nested outline;
-              if False, only dump nested list
-         - printResults - (default=C{True}) prints test output to stdout
-         - failureTests - (default=C{False}) indicates if these tests are expected to fail parsing
-
-        Returns: a (success, results) tuple, where success indicates that all tests succeeded
-        (or failed if C{failureTests} is True), and the results contain a list of lines of each 
-        test's output
-        
-        Example::
-            number_expr = pyparsing_common.number.copy()
-
-            result = number_expr.runTests('''
-                # unsigned integer
-                100
-                # negative integer
-                -100
-                # float with scientific notation
-                6.02e23
-                # integer with scientific notation
-                1e-12
-                ''')
-            print("Success" if result[0] else "Failed!")
-
-            result = number_expr.runTests('''
-                # stray character
-                100Z
-                # missing leading digit before '.'
-                -.100
-                # too many '.'
-                3.14.159
-                ''', failureTests=True)
-            print("Success" if result[0] else "Failed!")
-        prints::
-            # unsigned integer
-            100
-            [100]
-
-            # negative integer
-            -100
-            [-100]
-
-            # float with scientific notation
-            6.02e23
-            [6.02e+23]
-
-            # integer with scientific notation
-            1e-12
-            [1e-12]
-
-            Success
-            
-            # stray character
-            100Z
-               ^
-            FAIL: Expected end of text (at char 3), (line:1, col:4)
-
-            # missing leading digit before '.'
-            -.100
-            ^
-            FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1)
-
-            # too many '.'
-            3.14.159
-                ^
-            FAIL: Expected end of text (at char 4), (line:1, col:5)
-
-            Success
-
-        Each test string must be on a single line. If you want to test a string that spans multiple
-        lines, create a test like this::
-
-            expr.runTest(r"this is a test\\n of strings that spans \\n 3 lines")
-        
-        (Note that this is a raw string literal, you must include the leading 'r'.)
-        """
-        if isinstance(tests, basestring):
-            tests = list(map(str.strip, tests.rstrip().splitlines()))
-        if isinstance(comment, basestring):
-            comment = Literal(comment)
-        allResults = []
-        comments = []
-        success = True
-        for t in tests:
-            if comment is not None and comment.matches(t, False) or comments and not t:
-                comments.append(t)
-                continue
-            if not t:
-                continue
-            out = ['\n'.join(comments), t]
-            comments = []
-            try:
-                t = t.replace(r'\n','\n')
-                result = self.parseString(t, parseAll=parseAll)
-                out.append(result.dump(full=fullDump))
-                success = success and not failureTests
-            except ParseBaseException as pe:
-                fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else ""
-                if '\n' in t:
-                    out.append(line(pe.loc, t))
-                    out.append(' '*(col(pe.loc,t)-1) + '^' + fatal)
-                else:
-                    out.append(' '*pe.loc + '^' + fatal)
-                out.append("FAIL: " + str(pe))
-                success = success and failureTests
-                result = pe
-            except Exception as exc:
-                out.append("FAIL-EXCEPTION: " + str(exc))
-                success = success and failureTests
-                result = exc
-
-            if printResults:
-                if fullDump:
-                    out.append('')
-                print('\n'.join(out))
-
-            allResults.append((t, result))
-        
-        return success, allResults
-
-        
-class Token(ParserElement):
-    """
-    Abstract C{ParserElement} subclass, for defining atomic matching patterns.
-    """
-    def __init__( self ):
-        super(Token,self).__init__( savelist=False )
-
-
-class Empty(Token):
-    """
-    An empty token, will always match.
-    """
-    def __init__( self ):
-        super(Empty,self).__init__()
-        self.name = "Empty"
-        self.mayReturnEmpty = True
-        self.mayIndexError = False
-
-
-class NoMatch(Token):
-    """
-    A token that will never match.
-    """
-    def __init__( self ):
-        super(NoMatch,self).__init__()
-        self.name = "NoMatch"
-        self.mayReturnEmpty = True
-        self.mayIndexError = False
-        self.errmsg = "Unmatchable token"
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        raise ParseException(instring, loc, self.errmsg, self)
-
-
-class Literal(Token):
-    """
-    Token to exactly match a specified string.
-    
-    Example::
-        Literal('blah').parseString('blah')  # -> ['blah']
-        Literal('blah').parseString('blahfooblah')  # -> ['blah']
-        Literal('blah').parseString('bla')  # -> Exception: Expected "blah"
-    
-    For case-insensitive matching, use L{CaselessLiteral}.
-    
-    For keyword matching (force word break before and after the matched string),
-    use L{Keyword} or L{CaselessKeyword}.
-    """
-    def __init__( self, matchString ):
-        super(Literal,self).__init__()
-        self.match = matchString
-        self.matchLen = len(matchString)
-        try:
-            self.firstMatchChar = matchString[0]
-        except IndexError:
-            warnings.warn("null string passed to Literal; use Empty() instead",
-                            SyntaxWarning, stacklevel=2)
-            self.__class__ = Empty
-        self.name = '"%s"' % _ustr(self.match)
-        self.errmsg = "Expected " + self.name
-        self.mayReturnEmpty = False
-        self.mayIndexError = False
-
-    # Performance tuning: this routine gets called a *lot*
-    # if this is a single character match string  and the first character matches,
-    # short-circuit as quickly as possible, and avoid calling startswith
-    #~ @profile
-    def parseImpl( self, instring, loc, doActions=True ):
-        if (instring[loc] == self.firstMatchChar and
-            (self.matchLen==1 or instring.startswith(self.match,loc)) ):
-            return loc+self.matchLen, self.match
-        raise ParseException(instring, loc, self.errmsg, self)
-_L = Literal
-ParserElement._literalStringClass = Literal
-
-class Keyword(Token):
-    """
-    Token to exactly match a specified string as a keyword, that is, it must be
-    immediately followed by a non-keyword character.  Compare with C{L{Literal}}:
-     - C{Literal("if")} will match the leading C{'if'} in C{'ifAndOnlyIf'}.
-     - C{Keyword("if")} will not; it will only match the leading C{'if'} in C{'if x=1'}, or C{'if(y==2)'}
-    Accepts two optional constructor arguments in addition to the keyword string:
-     - C{identChars} is a string of characters that would be valid identifier characters,
-          defaulting to all alphanumerics + "_" and "$"
-     - C{caseless} allows case-insensitive matching, default is C{False}.
-       
-    Example::
-        Keyword("start").parseString("start")  # -> ['start']
-        Keyword("start").parseString("starting")  # -> Exception
-
-    For case-insensitive matching, use L{CaselessKeyword}.
-    """
-    DEFAULT_KEYWORD_CHARS = alphanums+"_$"
-
-    def __init__( self, matchString, identChars=None, caseless=False ):
-        super(Keyword,self).__init__()
-        if identChars is None:
-            identChars = Keyword.DEFAULT_KEYWORD_CHARS
-        self.match = matchString
-        self.matchLen = len(matchString)
-        try:
-            self.firstMatchChar = matchString[0]
-        except IndexError:
-            warnings.warn("null string passed to Keyword; use Empty() instead",
-                            SyntaxWarning, stacklevel=2)
-        self.name = '"%s"' % self.match
-        self.errmsg = "Expected " + self.name
-        self.mayReturnEmpty = False
-        self.mayIndexError = False
-        self.caseless = caseless
-        if caseless:
-            self.caselessmatch = matchString.upper()
-            identChars = identChars.upper()
-        self.identChars = set(identChars)
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if self.caseless:
-            if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and
-                 (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) and
-                 (loc == 0 or instring[loc-1].upper() not in self.identChars) ):
-                return loc+self.matchLen, self.match
-        else:
-            if (instring[loc] == self.firstMatchChar and
-                (self.matchLen==1 or instring.startswith(self.match,loc)) and
-                (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen] not in self.identChars) and
-                (loc == 0 or instring[loc-1] not in self.identChars) ):
-                return loc+self.matchLen, self.match
-        raise ParseException(instring, loc, self.errmsg, self)
-
-    def copy(self):
-        c = super(Keyword,self).copy()
-        c.identChars = Keyword.DEFAULT_KEYWORD_CHARS
-        return c
-
-    @staticmethod
-    def setDefaultKeywordChars( chars ):
-        """Overrides the default Keyword chars
-        """
-        Keyword.DEFAULT_KEYWORD_CHARS = chars
-
-class CaselessLiteral(Literal):
-    """
-    Token to match a specified string, ignoring case of letters.
-    Note: the matched results will always be in the case of the given
-    match string, NOT the case of the input text.
-
-    Example::
-        OneOrMore(CaselessLiteral("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD', 'CMD']
-        
-    (Contrast with example for L{CaselessKeyword}.)
-    """
-    def __init__( self, matchString ):
-        super(CaselessLiteral,self).__init__( matchString.upper() )
-        # Preserve the defining literal.
-        self.returnString = matchString
-        self.name = "'%s'" % self.returnString
-        self.errmsg = "Expected " + self.name
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if instring[ loc:loc+self.matchLen ].upper() == self.match:
-            return loc+self.matchLen, self.returnString
-        raise ParseException(instring, loc, self.errmsg, self)
-
-class CaselessKeyword(Keyword):
-    """
-    Caseless version of L{Keyword}.
-
-    Example::
-        OneOrMore(CaselessKeyword("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD']
-        
-    (Contrast with example for L{CaselessLiteral}.)
-    """
-    def __init__( self, matchString, identChars=None ):
-        super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True )
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and
-             (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) ):
-            return loc+self.matchLen, self.match
-        raise ParseException(instring, loc, self.errmsg, self)
-
-class CloseMatch(Token):
-    """
-    A variation on L{Literal} which matches "close" matches, that is, 
-    strings with at most 'n' mismatching characters. C{CloseMatch} takes parameters:
-     - C{match_string} - string to be matched
-     - C{maxMismatches} - (C{default=1}) maximum number of mismatches allowed to count as a match
-    
-    The results from a successful parse will contain the matched text from the input string and the following named results:
-     - C{mismatches} - a list of the positions within the match_string where mismatches were found
-     - C{original} - the original match_string used to compare against the input string
-    
-    If C{mismatches} is an empty list, then the match was an exact match.
-    
-    Example::
-        patt = CloseMatch("ATCATCGAATGGA")
-        patt.parseString("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']})
-        patt.parseString("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1)
-
-        # exact match
-        patt.parseString("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']})
-
-        # close match allowing up to 2 mismatches
-        patt = CloseMatch("ATCATCGAATGGA", maxMismatches=2)
-        patt.parseString("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']})
-    """
-    def __init__(self, match_string, maxMismatches=1):
-        super(CloseMatch,self).__init__()
-        self.name = match_string
-        self.match_string = match_string
-        self.maxMismatches = maxMismatches
-        self.errmsg = "Expected %r (with up to %d mismatches)" % (self.match_string, self.maxMismatches)
-        self.mayIndexError = False
-        self.mayReturnEmpty = False
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        start = loc
-        instrlen = len(instring)
-        maxloc = start + len(self.match_string)
-
-        if maxloc <= instrlen:
-            match_string = self.match_string
-            match_stringloc = 0
-            mismatches = []
-            maxMismatches = self.maxMismatches
-
-            for match_stringloc,s_m in enumerate(zip(instring[loc:maxloc], self.match_string)):
-                src,mat = s_m
-                if src != mat:
-                    mismatches.append(match_stringloc)
-                    if len(mismatches) > maxMismatches:
-                        break
-            else:
-                loc = match_stringloc + 1
-                results = ParseResults([instring[start:loc]])
-                results['original'] = self.match_string
-                results['mismatches'] = mismatches
-                return loc, results
-
-        raise ParseException(instring, loc, self.errmsg, self)
-
-
-class Word(Token):
-    """
-    Token for matching words composed of allowed character sets.
-    Defined with string containing all allowed initial characters,
-    an optional string containing allowed body characters (if omitted,
-    defaults to the initial character set), and an optional minimum,
-    maximum, and/or exact length.  The default value for C{min} is 1 (a
-    minimum value < 1 is not valid); the default values for C{max} and C{exact}
-    are 0, meaning no maximum or exact length restriction. An optional
-    C{excludeChars} parameter can list characters that might be found in 
-    the input C{bodyChars} string; useful to define a word of all printables
-    except for one or two characters, for instance.
-    
-    L{srange} is useful for defining custom character set strings for defining 
-    C{Word} expressions, using range notation from regular expression character sets.
-    
-    A common mistake is to use C{Word} to match a specific literal string, as in 
-    C{Word("Address")}. Remember that C{Word} uses the string argument to define
-    I{sets} of matchable characters. This expression would match "Add", "AAA",
-    "dAred", or any other word made up of the characters 'A', 'd', 'r', 'e', and 's'.
-    To match an exact literal string, use L{Literal} or L{Keyword}.
-
-    pyparsing includes helper strings for building Words:
-     - L{alphas}
-     - L{nums}
-     - L{alphanums}
-     - L{hexnums}
-     - L{alphas8bit} (alphabetic characters in ASCII range 128-255 - accented, tilded, umlauted, etc.)
-     - L{punc8bit} (non-alphabetic characters in ASCII range 128-255 - currency, symbols, superscripts, diacriticals, etc.)
-     - L{printables} (any non-whitespace character)
-
-    Example::
-        # a word composed of digits
-        integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9"))
-        
-        # a word with a leading capital, and zero or more lowercase
-        capital_word = Word(alphas.upper(), alphas.lower())
-
-        # hostnames are alphanumeric, with leading alpha, and '-'
-        hostname = Word(alphas, alphanums+'-')
-        
-        # roman numeral (not a strict parser, accepts invalid mix of characters)
-        roman = Word("IVXLCDM")
-        
-        # any string of non-whitespace characters, except for ','
-        csv_value = Word(printables, excludeChars=",")
-    """
-    def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False, excludeChars=None ):
-        super(Word,self).__init__()
-        if excludeChars:
-            initChars = ''.join(c for c in initChars if c not in excludeChars)
-            if bodyChars:
-                bodyChars = ''.join(c for c in bodyChars if c not in excludeChars)
-        self.initCharsOrig = initChars
-        self.initChars = set(initChars)
-        if bodyChars :
-            self.bodyCharsOrig = bodyChars
-            self.bodyChars = set(bodyChars)
-        else:
-            self.bodyCharsOrig = initChars
-            self.bodyChars = set(initChars)
-
-        self.maxSpecified = max > 0
-
-        if min < 1:
-            raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted")
-
-        self.minLen = min
-
-        if max > 0:
-            self.maxLen = max
-        else:
-            self.maxLen = _MAX_INT
-
-        if exact > 0:
-            self.maxLen = exact
-            self.minLen = exact
-
-        self.name = _ustr(self)
-        self.errmsg = "Expected " + self.name
-        self.mayIndexError = False
-        self.asKeyword = asKeyword
-
-        if ' ' not in self.initCharsOrig+self.bodyCharsOrig and (min==1 and max==0 and exact==0):
-            if self.bodyCharsOrig == self.initCharsOrig:
-                self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig)
-            elif len(self.initCharsOrig) == 1:
-                self.reString = "%s[%s]*" % \
-                                      (re.escape(self.initCharsOrig),
-                                      _escapeRegexRangeChars(self.bodyCharsOrig),)
-            else:
-                self.reString = "[%s][%s]*" % \
-                                      (_escapeRegexRangeChars(self.initCharsOrig),
-                                      _escapeRegexRangeChars(self.bodyCharsOrig),)
-            if self.asKeyword:
-                self.reString = r"\b"+self.reString+r"\b"
-            try:
-                self.re = re.compile( self.reString )
-            except Exception:
-                self.re = None
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if self.re:
-            result = self.re.match(instring,loc)
-            if not result:
-                raise ParseException(instring, loc, self.errmsg, self)
-
-            loc = result.end()
-            return loc, result.group()
-
-        if not(instring[ loc ] in self.initChars):
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        start = loc
-        loc += 1
-        instrlen = len(instring)
-        bodychars = self.bodyChars
-        maxloc = start + self.maxLen
-        maxloc = min( maxloc, instrlen )
-        while loc < maxloc and instring[loc] in bodychars:
-            loc += 1
-
-        throwException = False
-        if loc - start < self.minLen:
-            throwException = True
-        if self.maxSpecified and loc < instrlen and instring[loc] in bodychars:
-            throwException = True
-        if self.asKeyword:
-            if (start>0 and instring[start-1] in bodychars) or (loc4:
-                    return s[:4]+"..."
-                else:
-                    return s
-
-            if ( self.initCharsOrig != self.bodyCharsOrig ):
-                self.strRepr = "W:(%s,%s)" % ( charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig) )
-            else:
-                self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig)
-
-        return self.strRepr
-
-
-class Regex(Token):
-    r"""
-    Token for matching strings that match a given regular expression.
-    Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module.
-    If the given regex contains named groups (defined using C{(?P...)}), these will be preserved as 
-    named parse results.
-
-    Example::
-        realnum = Regex(r"[+-]?\d+\.\d*")
-        date = Regex(r'(?P\d{4})-(?P\d\d?)-(?P\d\d?)')
-        # ref: http://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression
-        roman = Regex(r"M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})")
-    """
-    compiledREtype = type(re.compile("[A-Z]"))
-    def __init__( self, pattern, flags=0):
-        """The parameters C{pattern} and C{flags} are passed to the C{re.compile()} function as-is. See the Python C{re} module for an explanation of the acceptable patterns and flags."""
-        super(Regex,self).__init__()
-
-        if isinstance(pattern, basestring):
-            if not pattern:
-                warnings.warn("null string passed to Regex; use Empty() instead",
-                        SyntaxWarning, stacklevel=2)
-
-            self.pattern = pattern
-            self.flags = flags
-
-            try:
-                self.re = re.compile(self.pattern, self.flags)
-                self.reString = self.pattern
-            except sre_constants.error:
-                warnings.warn("invalid pattern (%s) passed to Regex" % pattern,
-                    SyntaxWarning, stacklevel=2)
-                raise
-
-        elif isinstance(pattern, Regex.compiledREtype):
-            self.re = pattern
-            self.pattern = \
-            self.reString = str(pattern)
-            self.flags = flags
-            
-        else:
-            raise ValueError("Regex may only be constructed with a string or a compiled RE object")
-
-        self.name = _ustr(self)
-        self.errmsg = "Expected " + self.name
-        self.mayIndexError = False
-        self.mayReturnEmpty = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        result = self.re.match(instring,loc)
-        if not result:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        loc = result.end()
-        d = result.groupdict()
-        ret = ParseResults(result.group())
-        if d:
-            for k in d:
-                ret[k] = d[k]
-        return loc,ret
-
-    def __str__( self ):
-        try:
-            return super(Regex,self).__str__()
-        except Exception:
-            pass
-
-        if self.strRepr is None:
-            self.strRepr = "Re:(%s)" % repr(self.pattern)
-
-        return self.strRepr
-
-
-class QuotedString(Token):
-    r"""
-    Token for matching strings that are delimited by quoting characters.
-    
-    Defined with the following parameters:
-        - quoteChar - string of one or more characters defining the quote delimiting string
-        - escChar - character to escape quotes, typically backslash (default=C{None})
-        - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=C{None})
-        - multiline - boolean indicating whether quotes can span multiple lines (default=C{False})
-        - unquoteResults - boolean indicating whether the matched text should be unquoted (default=C{True})
-        - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=C{None} => same as quoteChar)
-        - convertWhitespaceEscapes - convert escaped whitespace (C{'\t'}, C{'\n'}, etc.) to actual whitespace (default=C{True})
-
-    Example::
-        qs = QuotedString('"')
-        print(qs.searchString('lsjdf "This is the quote" sldjf'))
-        complex_qs = QuotedString('{{', endQuoteChar='}}')
-        print(complex_qs.searchString('lsjdf {{This is the "quote"}} sldjf'))
-        sql_qs = QuotedString('"', escQuote='""')
-        print(sql_qs.searchString('lsjdf "This is the quote with ""embedded"" quotes" sldjf'))
-    prints::
-        [['This is the quote']]
-        [['This is the "quote"']]
-        [['This is the quote with "embedded" quotes']]
-    """
-    def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None, convertWhitespaceEscapes=True):
-        super(QuotedString,self).__init__()
-
-        # remove white space from quote chars - wont work anyway
-        quoteChar = quoteChar.strip()
-        if not quoteChar:
-            warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2)
-            raise SyntaxError()
-
-        if endQuoteChar is None:
-            endQuoteChar = quoteChar
-        else:
-            endQuoteChar = endQuoteChar.strip()
-            if not endQuoteChar:
-                warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2)
-                raise SyntaxError()
-
-        self.quoteChar = quoteChar
-        self.quoteCharLen = len(quoteChar)
-        self.firstQuoteChar = quoteChar[0]
-        self.endQuoteChar = endQuoteChar
-        self.endQuoteCharLen = len(endQuoteChar)
-        self.escChar = escChar
-        self.escQuote = escQuote
-        self.unquoteResults = unquoteResults
-        self.convertWhitespaceEscapes = convertWhitespaceEscapes
-
-        if multiline:
-            self.flags = re.MULTILINE | re.DOTALL
-            self.pattern = r'%s(?:[^%s%s]' % \
-                ( re.escape(self.quoteChar),
-                  _escapeRegexRangeChars(self.endQuoteChar[0]),
-                  (escChar is not None and _escapeRegexRangeChars(escChar) or '') )
-        else:
-            self.flags = 0
-            self.pattern = r'%s(?:[^%s\n\r%s]' % \
-                ( re.escape(self.quoteChar),
-                  _escapeRegexRangeChars(self.endQuoteChar[0]),
-                  (escChar is not None and _escapeRegexRangeChars(escChar) or '') )
-        if len(self.endQuoteChar) > 1:
-            self.pattern += (
-                '|(?:' + ')|(?:'.join("%s[^%s]" % (re.escape(self.endQuoteChar[:i]),
-                                               _escapeRegexRangeChars(self.endQuoteChar[i]))
-                                    for i in range(len(self.endQuoteChar)-1,0,-1)) + ')'
-                )
-        if escQuote:
-            self.pattern += (r'|(?:%s)' % re.escape(escQuote))
-        if escChar:
-            self.pattern += (r'|(?:%s.)' % re.escape(escChar))
-            self.escCharReplacePattern = re.escape(self.escChar)+"(.)"
-        self.pattern += (r')*%s' % re.escape(self.endQuoteChar))
-
-        try:
-            self.re = re.compile(self.pattern, self.flags)
-            self.reString = self.pattern
-        except sre_constants.error:
-            warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern,
-                SyntaxWarning, stacklevel=2)
-            raise
-
-        self.name = _ustr(self)
-        self.errmsg = "Expected " + self.name
-        self.mayIndexError = False
-        self.mayReturnEmpty = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None
-        if not result:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        loc = result.end()
-        ret = result.group()
-
-        if self.unquoteResults:
-
-            # strip off quotes
-            ret = ret[self.quoteCharLen:-self.endQuoteCharLen]
-
-            if isinstance(ret,basestring):
-                # replace escaped whitespace
-                if '\\' in ret and self.convertWhitespaceEscapes:
-                    ws_map = {
-                        r'\t' : '\t',
-                        r'\n' : '\n',
-                        r'\f' : '\f',
-                        r'\r' : '\r',
-                    }
-                    for wslit,wschar in ws_map.items():
-                        ret = ret.replace(wslit, wschar)
-
-                # replace escaped characters
-                if self.escChar:
-                    ret = re.sub(self.escCharReplacePattern, r"\g<1>", ret)
-
-                # replace escaped quotes
-                if self.escQuote:
-                    ret = ret.replace(self.escQuote, self.endQuoteChar)
-
-        return loc, ret
-
-    def __str__( self ):
-        try:
-            return super(QuotedString,self).__str__()
-        except Exception:
-            pass
-
-        if self.strRepr is None:
-            self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar)
-
-        return self.strRepr
-
-
-class CharsNotIn(Token):
-    """
-    Token for matching words composed of characters I{not} in a given set (will
-    include whitespace in matched characters if not listed in the provided exclusion set - see example).
-    Defined with string containing all disallowed characters, and an optional
-    minimum, maximum, and/or exact length.  The default value for C{min} is 1 (a
-    minimum value < 1 is not valid); the default values for C{max} and C{exact}
-    are 0, meaning no maximum or exact length restriction.
-
-    Example::
-        # define a comma-separated-value as anything that is not a ','
-        csv_value = CharsNotIn(',')
-        print(delimitedList(csv_value).parseString("dkls,lsdkjf,s12 34,@!#,213"))
-    prints::
-        ['dkls', 'lsdkjf', 's12 34', '@!#', '213']
-    """
-    def __init__( self, notChars, min=1, max=0, exact=0 ):
-        super(CharsNotIn,self).__init__()
-        self.skipWhitespace = False
-        self.notChars = notChars
-
-        if min < 1:
-            raise ValueError("cannot specify a minimum length < 1; use Optional(CharsNotIn()) if zero-length char group is permitted")
-
-        self.minLen = min
-
-        if max > 0:
-            self.maxLen = max
-        else:
-            self.maxLen = _MAX_INT
-
-        if exact > 0:
-            self.maxLen = exact
-            self.minLen = exact
-
-        self.name = _ustr(self)
-        self.errmsg = "Expected " + self.name
-        self.mayReturnEmpty = ( self.minLen == 0 )
-        self.mayIndexError = False
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if instring[loc] in self.notChars:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        start = loc
-        loc += 1
-        notchars = self.notChars
-        maxlen = min( start+self.maxLen, len(instring) )
-        while loc < maxlen and \
-              (instring[loc] not in notchars):
-            loc += 1
-
-        if loc - start < self.minLen:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        return loc, instring[start:loc]
-
-    def __str__( self ):
-        try:
-            return super(CharsNotIn, self).__str__()
-        except Exception:
-            pass
-
-        if self.strRepr is None:
-            if len(self.notChars) > 4:
-                self.strRepr = "!W:(%s...)" % self.notChars[:4]
-            else:
-                self.strRepr = "!W:(%s)" % self.notChars
-
-        return self.strRepr
-
-class White(Token):
-    """
-    Special matching class for matching whitespace.  Normally, whitespace is ignored
-    by pyparsing grammars.  This class is included when some whitespace structures
-    are significant.  Define with a string containing the whitespace characters to be
-    matched; default is C{" \\t\\r\\n"}.  Also takes optional C{min}, C{max}, and C{exact} arguments,
-    as defined for the C{L{Word}} class.
-    """
-    whiteStrs = {
-        " " : "",
-        "\t": "",
-        "\n": "",
-        "\r": "",
-        "\f": "",
-        }
-    def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0):
-        super(White,self).__init__()
-        self.matchWhite = ws
-        self.setWhitespaceChars( "".join(c for c in self.whiteChars if c not in self.matchWhite) )
-        #~ self.leaveWhitespace()
-        self.name = ("".join(White.whiteStrs[c] for c in self.matchWhite))
-        self.mayReturnEmpty = True
-        self.errmsg = "Expected " + self.name
-
-        self.minLen = min
-
-        if max > 0:
-            self.maxLen = max
-        else:
-            self.maxLen = _MAX_INT
-
-        if exact > 0:
-            self.maxLen = exact
-            self.minLen = exact
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if not(instring[ loc ] in self.matchWhite):
-            raise ParseException(instring, loc, self.errmsg, self)
-        start = loc
-        loc += 1
-        maxloc = start + self.maxLen
-        maxloc = min( maxloc, len(instring) )
-        while loc < maxloc and instring[loc] in self.matchWhite:
-            loc += 1
-
-        if loc - start < self.minLen:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        return loc, instring[start:loc]
-
-
-class _PositionToken(Token):
-    def __init__( self ):
-        super(_PositionToken,self).__init__()
-        self.name=self.__class__.__name__
-        self.mayReturnEmpty = True
-        self.mayIndexError = False
-
-class GoToColumn(_PositionToken):
-    """
-    Token to advance to a specific column of input text; useful for tabular report scraping.
-    """
-    def __init__( self, colno ):
-        super(GoToColumn,self).__init__()
-        self.col = colno
-
-    def preParse( self, instring, loc ):
-        if col(loc,instring) != self.col:
-            instrlen = len(instring)
-            if self.ignoreExprs:
-                loc = self._skipIgnorables( instring, loc )
-            while loc < instrlen and instring[loc].isspace() and col( loc, instring ) != self.col :
-                loc += 1
-        return loc
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        thiscol = col( loc, instring )
-        if thiscol > self.col:
-            raise ParseException( instring, loc, "Text not in expected column", self )
-        newloc = loc + self.col - thiscol
-        ret = instring[ loc: newloc ]
-        return newloc, ret
-
-
-class LineStart(_PositionToken):
-    """
-    Matches if current position is at the beginning of a line within the parse string
-    
-    Example::
-    
-        test = '''\
-        AAA this line
-        AAA and this line
-          AAA but not this one
-        B AAA and definitely not this one
-        '''
-
-        for t in (LineStart() + 'AAA' + restOfLine).searchString(test):
-            print(t)
-    
-    Prints::
-        ['AAA', ' this line']
-        ['AAA', ' and this line']    
-
-    """
-    def __init__( self ):
-        super(LineStart,self).__init__()
-        self.errmsg = "Expected start of line"
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if col(loc, instring) == 1:
-            return loc, []
-        raise ParseException(instring, loc, self.errmsg, self)
-
-class LineEnd(_PositionToken):
-    """
-    Matches if current position is at the end of a line within the parse string
-    """
-    def __init__( self ):
-        super(LineEnd,self).__init__()
-        self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") )
-        self.errmsg = "Expected end of line"
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if loc len(instring):
-            return loc, []
-        else:
-            raise ParseException(instring, loc, self.errmsg, self)
-
-class WordStart(_PositionToken):
-    """
-    Matches if the current position is at the beginning of a Word, and
-    is not preceded by any character in a given set of C{wordChars}
-    (default=C{printables}). To emulate the C{\b} behavior of regular expressions,
-    use C{WordStart(alphanums)}. C{WordStart} will also match at the beginning of
-    the string being parsed, or at the beginning of a line.
-    """
-    def __init__(self, wordChars = printables):
-        super(WordStart,self).__init__()
-        self.wordChars = set(wordChars)
-        self.errmsg = "Not at the start of a word"
-
-    def parseImpl(self, instring, loc, doActions=True ):
-        if loc != 0:
-            if (instring[loc-1] in self.wordChars or
-                instring[loc] not in self.wordChars):
-                raise ParseException(instring, loc, self.errmsg, self)
-        return loc, []
-
-class WordEnd(_PositionToken):
-    """
-    Matches if the current position is at the end of a Word, and
-    is not followed by any character in a given set of C{wordChars}
-    (default=C{printables}). To emulate the C{\b} behavior of regular expressions,
-    use C{WordEnd(alphanums)}. C{WordEnd} will also match at the end of
-    the string being parsed, or at the end of a line.
-    """
-    def __init__(self, wordChars = printables):
-        super(WordEnd,self).__init__()
-        self.wordChars = set(wordChars)
-        self.skipWhitespace = False
-        self.errmsg = "Not at the end of a word"
-
-    def parseImpl(self, instring, loc, doActions=True ):
-        instrlen = len(instring)
-        if instrlen>0 and loc maxExcLoc:
-                    maxException = err
-                    maxExcLoc = err.loc
-            except IndexError:
-                if len(instring) > maxExcLoc:
-                    maxException = ParseException(instring,len(instring),e.errmsg,self)
-                    maxExcLoc = len(instring)
-            else:
-                # save match among all matches, to retry longest to shortest
-                matches.append((loc2, e))
-
-        if matches:
-            matches.sort(key=lambda x: -x[0])
-            for _,e in matches:
-                try:
-                    return e._parse( instring, loc, doActions )
-                except ParseException as err:
-                    err.__traceback__ = None
-                    if err.loc > maxExcLoc:
-                        maxException = err
-                        maxExcLoc = err.loc
-
-        if maxException is not None:
-            maxException.msg = self.errmsg
-            raise maxException
-        else:
-            raise ParseException(instring, loc, "no defined alternatives to match", self)
-
-
-    def __ixor__(self, other ):
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        return self.append( other ) #Or( [ self, other ] )
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "{" + " ^ ".join(_ustr(e) for e in self.exprs) + "}"
-
-        return self.strRepr
-
-    def checkRecursion( self, parseElementList ):
-        subRecCheckList = parseElementList[:] + [ self ]
-        for e in self.exprs:
-            e.checkRecursion( subRecCheckList )
-
-
-class MatchFirst(ParseExpression):
-    """
-    Requires that at least one C{ParseExpression} is found.
-    If two expressions match, the first one listed is the one that will match.
-    May be constructed using the C{'|'} operator.
-
-    Example::
-        # construct MatchFirst using '|' operator
-        
-        # watch the order of expressions to match
-        number = Word(nums) | Combine(Word(nums) + '.' + Word(nums))
-        print(number.searchString("123 3.1416 789")) #  Fail! -> [['123'], ['3'], ['1416'], ['789']]
-
-        # put more selective expression first
-        number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums)
-        print(number.searchString("123 3.1416 789")) #  Better -> [['123'], ['3.1416'], ['789']]
-    """
-    def __init__( self, exprs, savelist = False ):
-        super(MatchFirst,self).__init__(exprs, savelist)
-        if self.exprs:
-            self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs)
-        else:
-            self.mayReturnEmpty = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        maxExcLoc = -1
-        maxException = None
-        for e in self.exprs:
-            try:
-                ret = e._parse( instring, loc, doActions )
-                return ret
-            except ParseException as err:
-                if err.loc > maxExcLoc:
-                    maxException = err
-                    maxExcLoc = err.loc
-            except IndexError:
-                if len(instring) > maxExcLoc:
-                    maxException = ParseException(instring,len(instring),e.errmsg,self)
-                    maxExcLoc = len(instring)
-
-        # only got here if no expression matched, raise exception for match that made it the furthest
-        else:
-            if maxException is not None:
-                maxException.msg = self.errmsg
-                raise maxException
-            else:
-                raise ParseException(instring, loc, "no defined alternatives to match", self)
-
-    def __ior__(self, other ):
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass( other )
-        return self.append( other ) #MatchFirst( [ self, other ] )
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "{" + " | ".join(_ustr(e) for e in self.exprs) + "}"
-
-        return self.strRepr
-
-    def checkRecursion( self, parseElementList ):
-        subRecCheckList = parseElementList[:] + [ self ]
-        for e in self.exprs:
-            e.checkRecursion( subRecCheckList )
-
-
-class Each(ParseExpression):
-    """
-    Requires all given C{ParseExpression}s to be found, but in any order.
-    Expressions may be separated by whitespace.
-    May be constructed using the C{'&'} operator.
-
-    Example::
-        color = oneOf("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN")
-        shape_type = oneOf("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON")
-        integer = Word(nums)
-        shape_attr = "shape:" + shape_type("shape")
-        posn_attr = "posn:" + Group(integer("x") + ',' + integer("y"))("posn")
-        color_attr = "color:" + color("color")
-        size_attr = "size:" + integer("size")
-
-        # use Each (using operator '&') to accept attributes in any order 
-        # (shape and posn are required, color and size are optional)
-        shape_spec = shape_attr & posn_attr & Optional(color_attr) & Optional(size_attr)
-
-        shape_spec.runTests('''
-            shape: SQUARE color: BLACK posn: 100, 120
-            shape: CIRCLE size: 50 color: BLUE posn: 50,80
-            color:GREEN size:20 shape:TRIANGLE posn:20,40
-            '''
-            )
-    prints::
-        shape: SQUARE color: BLACK posn: 100, 120
-        ['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']]
-        - color: BLACK
-        - posn: ['100', ',', '120']
-          - x: 100
-          - y: 120
-        - shape: SQUARE
-
-
-        shape: CIRCLE size: 50 color: BLUE posn: 50,80
-        ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']]
-        - color: BLUE
-        - posn: ['50', ',', '80']
-          - x: 50
-          - y: 80
-        - shape: CIRCLE
-        - size: 50
-
-
-        color: GREEN size: 20 shape: TRIANGLE posn: 20,40
-        ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']]
-        - color: GREEN
-        - posn: ['20', ',', '40']
-          - x: 20
-          - y: 40
-        - shape: TRIANGLE
-        - size: 20
-    """
-    def __init__( self, exprs, savelist = True ):
-        super(Each,self).__init__(exprs, savelist)
-        self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs)
-        self.skipWhitespace = True
-        self.initExprGroups = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if self.initExprGroups:
-            self.opt1map = dict((id(e.expr),e) for e in self.exprs if isinstance(e,Optional))
-            opt1 = [ e.expr for e in self.exprs if isinstance(e,Optional) ]
-            opt2 = [ e for e in self.exprs if e.mayReturnEmpty and not isinstance(e,Optional)]
-            self.optionals = opt1 + opt2
-            self.multioptionals = [ e.expr for e in self.exprs if isinstance(e,ZeroOrMore) ]
-            self.multirequired = [ e.expr for e in self.exprs if isinstance(e,OneOrMore) ]
-            self.required = [ e for e in self.exprs if not isinstance(e,(Optional,ZeroOrMore,OneOrMore)) ]
-            self.required += self.multirequired
-            self.initExprGroups = False
-        tmpLoc = loc
-        tmpReqd = self.required[:]
-        tmpOpt  = self.optionals[:]
-        matchOrder = []
-
-        keepMatching = True
-        while keepMatching:
-            tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired
-            failed = []
-            for e in tmpExprs:
-                try:
-                    tmpLoc = e.tryParse( instring, tmpLoc )
-                except ParseException:
-                    failed.append(e)
-                else:
-                    matchOrder.append(self.opt1map.get(id(e),e))
-                    if e in tmpReqd:
-                        tmpReqd.remove(e)
-                    elif e in tmpOpt:
-                        tmpOpt.remove(e)
-            if len(failed) == len(tmpExprs):
-                keepMatching = False
-
-        if tmpReqd:
-            missing = ", ".join(_ustr(e) for e in tmpReqd)
-            raise ParseException(instring,loc,"Missing one or more required elements (%s)" % missing )
-
-        # add any unmatched Optionals, in case they have default values defined
-        matchOrder += [e for e in self.exprs if isinstance(e,Optional) and e.expr in tmpOpt]
-
-        resultlist = []
-        for e in matchOrder:
-            loc,results = e._parse(instring,loc,doActions)
-            resultlist.append(results)
-
-        finalResults = sum(resultlist, ParseResults([]))
-        return loc, finalResults
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "{" + " & ".join(_ustr(e) for e in self.exprs) + "}"
-
-        return self.strRepr
-
-    def checkRecursion( self, parseElementList ):
-        subRecCheckList = parseElementList[:] + [ self ]
-        for e in self.exprs:
-            e.checkRecursion( subRecCheckList )
-
-
-class ParseElementEnhance(ParserElement):
-    """
-    Abstract subclass of C{ParserElement}, for combining and post-processing parsed tokens.
-    """
-    def __init__( self, expr, savelist=False ):
-        super(ParseElementEnhance,self).__init__(savelist)
-        if isinstance( expr, basestring ):
-            if issubclass(ParserElement._literalStringClass, Token):
-                expr = ParserElement._literalStringClass(expr)
-            else:
-                expr = ParserElement._literalStringClass(Literal(expr))
-        self.expr = expr
-        self.strRepr = None
-        if expr is not None:
-            self.mayIndexError = expr.mayIndexError
-            self.mayReturnEmpty = expr.mayReturnEmpty
-            self.setWhitespaceChars( expr.whiteChars )
-            self.skipWhitespace = expr.skipWhitespace
-            self.saveAsList = expr.saveAsList
-            self.callPreparse = expr.callPreparse
-            self.ignoreExprs.extend(expr.ignoreExprs)
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if self.expr is not None:
-            return self.expr._parse( instring, loc, doActions, callPreParse=False )
-        else:
-            raise ParseException("",loc,self.errmsg,self)
-
-    def leaveWhitespace( self ):
-        self.skipWhitespace = False
-        self.expr = self.expr.copy()
-        if self.expr is not None:
-            self.expr.leaveWhitespace()
-        return self
-
-    def ignore( self, other ):
-        if isinstance( other, Suppress ):
-            if other not in self.ignoreExprs:
-                super( ParseElementEnhance, self).ignore( other )
-                if self.expr is not None:
-                    self.expr.ignore( self.ignoreExprs[-1] )
-        else:
-            super( ParseElementEnhance, self).ignore( other )
-            if self.expr is not None:
-                self.expr.ignore( self.ignoreExprs[-1] )
-        return self
-
-    def streamline( self ):
-        super(ParseElementEnhance,self).streamline()
-        if self.expr is not None:
-            self.expr.streamline()
-        return self
-
-    def checkRecursion( self, parseElementList ):
-        if self in parseElementList:
-            raise RecursiveGrammarException( parseElementList+[self] )
-        subRecCheckList = parseElementList[:] + [ self ]
-        if self.expr is not None:
-            self.expr.checkRecursion( subRecCheckList )
-
-    def validate( self, validateTrace=[] ):
-        tmp = validateTrace[:]+[self]
-        if self.expr is not None:
-            self.expr.validate(tmp)
-        self.checkRecursion( [] )
-
-    def __str__( self ):
-        try:
-            return super(ParseElementEnhance,self).__str__()
-        except Exception:
-            pass
-
-        if self.strRepr is None and self.expr is not None:
-            self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.expr) )
-        return self.strRepr
-
-
-class FollowedBy(ParseElementEnhance):
-    """
-    Lookahead matching of the given parse expression.  C{FollowedBy}
-    does I{not} advance the parsing position within the input string, it only
-    verifies that the specified parse expression matches at the current
-    position.  C{FollowedBy} always returns a null token list.
-
-    Example::
-        # use FollowedBy to match a label only if it is followed by a ':'
-        data_word = Word(alphas)
-        label = data_word + FollowedBy(':')
-        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
-        
-        OneOrMore(attr_expr).parseString("shape: SQUARE color: BLACK posn: upper left").pprint()
-    prints::
-        [['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']]
-    """
-    def __init__( self, expr ):
-        super(FollowedBy,self).__init__(expr)
-        self.mayReturnEmpty = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        self.expr.tryParse( instring, loc )
-        return loc, []
-
-
-class NotAny(ParseElementEnhance):
-    """
-    Lookahead to disallow matching with the given parse expression.  C{NotAny}
-    does I{not} advance the parsing position within the input string, it only
-    verifies that the specified parse expression does I{not} match at the current
-    position.  Also, C{NotAny} does I{not} skip over leading whitespace. C{NotAny}
-    always returns a null token list.  May be constructed using the '~' operator.
-
-    Example::
-        
-    """
-    def __init__( self, expr ):
-        super(NotAny,self).__init__(expr)
-        #~ self.leaveWhitespace()
-        self.skipWhitespace = False  # do NOT use self.leaveWhitespace(), don't want to propagate to exprs
-        self.mayReturnEmpty = True
-        self.errmsg = "Found unwanted token, "+_ustr(self.expr)
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        if self.expr.canParseNext(instring, loc):
-            raise ParseException(instring, loc, self.errmsg, self)
-        return loc, []
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "~{" + _ustr(self.expr) + "}"
-
-        return self.strRepr
-
-class _MultipleMatch(ParseElementEnhance):
-    def __init__( self, expr, stopOn=None):
-        super(_MultipleMatch, self).__init__(expr)
-        self.saveAsList = True
-        ender = stopOn
-        if isinstance(ender, basestring):
-            ender = ParserElement._literalStringClass(ender)
-        self.not_ender = ~ender if ender is not None else None
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        self_expr_parse = self.expr._parse
-        self_skip_ignorables = self._skipIgnorables
-        check_ender = self.not_ender is not None
-        if check_ender:
-            try_not_ender = self.not_ender.tryParse
-        
-        # must be at least one (but first see if we are the stopOn sentinel;
-        # if so, fail)
-        if check_ender:
-            try_not_ender(instring, loc)
-        loc, tokens = self_expr_parse( instring, loc, doActions, callPreParse=False )
-        try:
-            hasIgnoreExprs = (not not self.ignoreExprs)
-            while 1:
-                if check_ender:
-                    try_not_ender(instring, loc)
-                if hasIgnoreExprs:
-                    preloc = self_skip_ignorables( instring, loc )
-                else:
-                    preloc = loc
-                loc, tmptokens = self_expr_parse( instring, preloc, doActions )
-                if tmptokens or tmptokens.haskeys():
-                    tokens += tmptokens
-        except (ParseException,IndexError):
-            pass
-
-        return loc, tokens
-        
-class OneOrMore(_MultipleMatch):
-    """
-    Repetition of one or more of the given expression.
-    
-    Parameters:
-     - expr - expression that must match one or more times
-     - stopOn - (default=C{None}) - expression for a terminating sentinel
-          (only required if the sentinel would ordinarily match the repetition 
-          expression)          
-
-    Example::
-        data_word = Word(alphas)
-        label = data_word + FollowedBy(':')
-        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join))
-
-        text = "shape: SQUARE posn: upper left color: BLACK"
-        OneOrMore(attr_expr).parseString(text).pprint()  # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']]
-
-        # use stopOn attribute for OneOrMore to avoid reading label string as part of the data
-        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
-        OneOrMore(attr_expr).parseString(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']]
-        
-        # could also be written as
-        (attr_expr * (1,)).parseString(text).pprint()
-    """
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "{" + _ustr(self.expr) + "}..."
-
-        return self.strRepr
-
-class ZeroOrMore(_MultipleMatch):
-    """
-    Optional repetition of zero or more of the given expression.
-    
-    Parameters:
-     - expr - expression that must match zero or more times
-     - stopOn - (default=C{None}) - expression for a terminating sentinel
-          (only required if the sentinel would ordinarily match the repetition 
-          expression)          
-
-    Example: similar to L{OneOrMore}
-    """
-    def __init__( self, expr, stopOn=None):
-        super(ZeroOrMore,self).__init__(expr, stopOn=stopOn)
-        self.mayReturnEmpty = True
-        
-    def parseImpl( self, instring, loc, doActions=True ):
-        try:
-            return super(ZeroOrMore, self).parseImpl(instring, loc, doActions)
-        except (ParseException,IndexError):
-            return loc, []
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "[" + _ustr(self.expr) + "]..."
-
-        return self.strRepr
-
-class _NullToken(object):
-    def __bool__(self):
-        return False
-    __nonzero__ = __bool__
-    def __str__(self):
-        return ""
-
-_optionalNotMatched = _NullToken()
-class Optional(ParseElementEnhance):
-    """
-    Optional matching of the given expression.
-
-    Parameters:
-     - expr - expression that must match zero or more times
-     - default (optional) - value to be returned if the optional expression is not found.
-
-    Example::
-        # US postal code can be a 5-digit zip, plus optional 4-digit qualifier
-        zip = Combine(Word(nums, exact=5) + Optional('-' + Word(nums, exact=4)))
-        zip.runTests('''
-            # traditional ZIP code
-            12345
-            
-            # ZIP+4 form
-            12101-0001
-            
-            # invalid ZIP
-            98765-
-            ''')
-    prints::
-        # traditional ZIP code
-        12345
-        ['12345']
-
-        # ZIP+4 form
-        12101-0001
-        ['12101-0001']
-
-        # invalid ZIP
-        98765-
-             ^
-        FAIL: Expected end of text (at char 5), (line:1, col:6)
-    """
-    def __init__( self, expr, default=_optionalNotMatched ):
-        super(Optional,self).__init__( expr, savelist=False )
-        self.saveAsList = self.expr.saveAsList
-        self.defaultValue = default
-        self.mayReturnEmpty = True
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        try:
-            loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False )
-        except (ParseException,IndexError):
-            if self.defaultValue is not _optionalNotMatched:
-                if self.expr.resultsName:
-                    tokens = ParseResults([ self.defaultValue ])
-                    tokens[self.expr.resultsName] = self.defaultValue
-                else:
-                    tokens = [ self.defaultValue ]
-            else:
-                tokens = []
-        return loc, tokens
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-
-        if self.strRepr is None:
-            self.strRepr = "[" + _ustr(self.expr) + "]"
-
-        return self.strRepr
-
-class SkipTo(ParseElementEnhance):
-    """
-    Token for skipping over all undefined text until the matched expression is found.
-
-    Parameters:
-     - expr - target expression marking the end of the data to be skipped
-     - include - (default=C{False}) if True, the target expression is also parsed 
-          (the skipped text and target expression are returned as a 2-element list).
-     - ignore - (default=C{None}) used to define grammars (typically quoted strings and 
-          comments) that might contain false matches to the target expression
-     - failOn - (default=C{None}) define expressions that are not allowed to be 
-          included in the skipped test; if found before the target expression is found, 
-          the SkipTo is not a match
-
-    Example::
-        report = '''
-            Outstanding Issues Report - 1 Jan 2000
-
-               # | Severity | Description                               |  Days Open
-            -----+----------+-------------------------------------------+-----------
-             101 | Critical | Intermittent system crash                 |          6
-              94 | Cosmetic | Spelling error on Login ('log|n')         |         14
-              79 | Minor    | System slow when running too many reports |         47
-            '''
-        integer = Word(nums)
-        SEP = Suppress('|')
-        # use SkipTo to simply match everything up until the next SEP
-        # - ignore quoted strings, so that a '|' character inside a quoted string does not match
-        # - parse action will call token.strip() for each matched token, i.e., the description body
-        string_data = SkipTo(SEP, ignore=quotedString)
-        string_data.setParseAction(tokenMap(str.strip))
-        ticket_expr = (integer("issue_num") + SEP 
-                      + string_data("sev") + SEP 
-                      + string_data("desc") + SEP 
-                      + integer("days_open"))
-        
-        for tkt in ticket_expr.searchString(report):
-            print tkt.dump()
-    prints::
-        ['101', 'Critical', 'Intermittent system crash', '6']
-        - days_open: 6
-        - desc: Intermittent system crash
-        - issue_num: 101
-        - sev: Critical
-        ['94', 'Cosmetic', "Spelling error on Login ('log|n')", '14']
-        - days_open: 14
-        - desc: Spelling error on Login ('log|n')
-        - issue_num: 94
-        - sev: Cosmetic
-        ['79', 'Minor', 'System slow when running too many reports', '47']
-        - days_open: 47
-        - desc: System slow when running too many reports
-        - issue_num: 79
-        - sev: Minor
-    """
-    def __init__( self, other, include=False, ignore=None, failOn=None ):
-        super( SkipTo, self ).__init__( other )
-        self.ignoreExpr = ignore
-        self.mayReturnEmpty = True
-        self.mayIndexError = False
-        self.includeMatch = include
-        self.asList = False
-        if isinstance(failOn, basestring):
-            self.failOn = ParserElement._literalStringClass(failOn)
-        else:
-            self.failOn = failOn
-        self.errmsg = "No match found for "+_ustr(self.expr)
-
-    def parseImpl( self, instring, loc, doActions=True ):
-        startloc = loc
-        instrlen = len(instring)
-        expr = self.expr
-        expr_parse = self.expr._parse
-        self_failOn_canParseNext = self.failOn.canParseNext if self.failOn is not None else None
-        self_ignoreExpr_tryParse = self.ignoreExpr.tryParse if self.ignoreExpr is not None else None
-        
-        tmploc = loc
-        while tmploc <= instrlen:
-            if self_failOn_canParseNext is not None:
-                # break if failOn expression matches
-                if self_failOn_canParseNext(instring, tmploc):
-                    break
-                    
-            if self_ignoreExpr_tryParse is not None:
-                # advance past ignore expressions
-                while 1:
-                    try:
-                        tmploc = self_ignoreExpr_tryParse(instring, tmploc)
-                    except ParseBaseException:
-                        break
-            
-            try:
-                expr_parse(instring, tmploc, doActions=False, callPreParse=False)
-            except (ParseException, IndexError):
-                # no match, advance loc in string
-                tmploc += 1
-            else:
-                # matched skipto expr, done
-                break
-
-        else:
-            # ran off the end of the input string without matching skipto expr, fail
-            raise ParseException(instring, loc, self.errmsg, self)
-
-        # build up return values
-        loc = tmploc
-        skiptext = instring[startloc:loc]
-        skipresult = ParseResults(skiptext)
-        
-        if self.includeMatch:
-            loc, mat = expr_parse(instring,loc,doActions,callPreParse=False)
-            skipresult += mat
-
-        return loc, skipresult
-
-class Forward(ParseElementEnhance):
-    """
-    Forward declaration of an expression to be defined later -
-    used for recursive grammars, such as algebraic infix notation.
-    When the expression is known, it is assigned to the C{Forward} variable using the '<<' operator.
-
-    Note: take care when assigning to C{Forward} not to overlook precedence of operators.
-    Specifically, '|' has a lower precedence than '<<', so that::
-        fwdExpr << a | b | c
-    will actually be evaluated as::
-        (fwdExpr << a) | b | c
-    thereby leaving b and c out as parseable alternatives.  It is recommended that you
-    explicitly group the values inserted into the C{Forward}::
-        fwdExpr << (a | b | c)
-    Converting to use the '<<=' operator instead will avoid this problem.
-
-    See L{ParseResults.pprint} for an example of a recursive parser created using
-    C{Forward}.
-    """
-    def __init__( self, other=None ):
-        super(Forward,self).__init__( other, savelist=False )
-
-    def __lshift__( self, other ):
-        if isinstance( other, basestring ):
-            other = ParserElement._literalStringClass(other)
-        self.expr = other
-        self.strRepr = None
-        self.mayIndexError = self.expr.mayIndexError
-        self.mayReturnEmpty = self.expr.mayReturnEmpty
-        self.setWhitespaceChars( self.expr.whiteChars )
-        self.skipWhitespace = self.expr.skipWhitespace
-        self.saveAsList = self.expr.saveAsList
-        self.ignoreExprs.extend(self.expr.ignoreExprs)
-        return self
-        
-    def __ilshift__(self, other):
-        return self << other
-    
-    def leaveWhitespace( self ):
-        self.skipWhitespace = False
-        return self
-
-    def streamline( self ):
-        if not self.streamlined:
-            self.streamlined = True
-            if self.expr is not None:
-                self.expr.streamline()
-        return self
-
-    def validate( self, validateTrace=[] ):
-        if self not in validateTrace:
-            tmp = validateTrace[:]+[self]
-            if self.expr is not None:
-                self.expr.validate(tmp)
-        self.checkRecursion([])
-
-    def __str__( self ):
-        if hasattr(self,"name"):
-            return self.name
-        return self.__class__.__name__ + ": ..."
-
-        # stubbed out for now - creates awful memory and perf issues
-        self._revertClass = self.__class__
-        self.__class__ = _ForwardNoRecurse
-        try:
-            if self.expr is not None:
-                retString = _ustr(self.expr)
-            else:
-                retString = "None"
-        finally:
-            self.__class__ = self._revertClass
-        return self.__class__.__name__ + ": " + retString
-
-    def copy(self):
-        if self.expr is not None:
-            return super(Forward,self).copy()
-        else:
-            ret = Forward()
-            ret <<= self
-            return ret
-
-class _ForwardNoRecurse(Forward):
-    def __str__( self ):
-        return "..."
-
-class TokenConverter(ParseElementEnhance):
-    """
-    Abstract subclass of C{ParseExpression}, for converting parsed results.
-    """
-    def __init__( self, expr, savelist=False ):
-        super(TokenConverter,self).__init__( expr )#, savelist )
-        self.saveAsList = False
-
-class Combine(TokenConverter):
-    """
-    Converter to concatenate all matching tokens to a single string.
-    By default, the matching patterns must also be contiguous in the input string;
-    this can be disabled by specifying C{'adjacent=False'} in the constructor.
-
-    Example::
-        real = Word(nums) + '.' + Word(nums)
-        print(real.parseString('3.1416')) # -> ['3', '.', '1416']
-        # will also erroneously match the following
-        print(real.parseString('3. 1416')) # -> ['3', '.', '1416']
-
-        real = Combine(Word(nums) + '.' + Word(nums))
-        print(real.parseString('3.1416')) # -> ['3.1416']
-        # no match when there are internal spaces
-        print(real.parseString('3. 1416')) # -> Exception: Expected W:(0123...)
-    """
-    def __init__( self, expr, joinString="", adjacent=True ):
-        super(Combine,self).__init__( expr )
-        # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself
-        if adjacent:
-            self.leaveWhitespace()
-        self.adjacent = adjacent
-        self.skipWhitespace = True
-        self.joinString = joinString
-        self.callPreparse = True
-
-    def ignore( self, other ):
-        if self.adjacent:
-            ParserElement.ignore(self, other)
-        else:
-            super( Combine, self).ignore( other )
-        return self
-
-    def postParse( self, instring, loc, tokenlist ):
-        retToks = tokenlist.copy()
-        del retToks[:]
-        retToks += ParseResults([ "".join(tokenlist._asStringList(self.joinString)) ], modal=self.modalResults)
-
-        if self.resultsName and retToks.haskeys():
-            return [ retToks ]
-        else:
-            return retToks
-
-class Group(TokenConverter):
-    """
-    Converter to return the matched tokens as a list - useful for returning tokens of C{L{ZeroOrMore}} and C{L{OneOrMore}} expressions.
-
-    Example::
-        ident = Word(alphas)
-        num = Word(nums)
-        term = ident | num
-        func = ident + Optional(delimitedList(term))
-        print(func.parseString("fn a,b,100"))  # -> ['fn', 'a', 'b', '100']
-
-        func = ident + Group(Optional(delimitedList(term)))
-        print(func.parseString("fn a,b,100"))  # -> ['fn', ['a', 'b', '100']]
-    """
-    def __init__( self, expr ):
-        super(Group,self).__init__( expr )
-        self.saveAsList = True
-
-    def postParse( self, instring, loc, tokenlist ):
-        return [ tokenlist ]
-
-class Dict(TokenConverter):
-    """
-    Converter to return a repetitive expression as a list, but also as a dictionary.
-    Each element can also be referenced using the first token in the expression as its key.
-    Useful for tabular report scraping when the first column can be used as a item key.
-
-    Example::
-        data_word = Word(alphas)
-        label = data_word + FollowedBy(':')
-        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join))
-
-        text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
-        attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
-        
-        # print attributes as plain groups
-        print(OneOrMore(attr_expr).parseString(text).dump())
-        
-        # instead of OneOrMore(expr), parse using Dict(OneOrMore(Group(expr))) - Dict will auto-assign names
-        result = Dict(OneOrMore(Group(attr_expr))).parseString(text)
-        print(result.dump())
-        
-        # access named fields as dict entries, or output as dict
-        print(result['shape'])        
-        print(result.asDict())
-    prints::
-        ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap']
-
-        [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']]
-        - color: light blue
-        - posn: upper left
-        - shape: SQUARE
-        - texture: burlap
-        SQUARE
-        {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'}
-    See more examples at L{ParseResults} of accessing fields by results name.
-    """
-    def __init__( self, expr ):
-        super(Dict,self).__init__( expr )
-        self.saveAsList = True
-
-    def postParse( self, instring, loc, tokenlist ):
-        for i,tok in enumerate(tokenlist):
-            if len(tok) == 0:
-                continue
-            ikey = tok[0]
-            if isinstance(ikey,int):
-                ikey = _ustr(tok[0]).strip()
-            if len(tok)==1:
-                tokenlist[ikey] = _ParseResultsWithOffset("",i)
-            elif len(tok)==2 and not isinstance(tok[1],ParseResults):
-                tokenlist[ikey] = _ParseResultsWithOffset(tok[1],i)
-            else:
-                dictvalue = tok.copy() #ParseResults(i)
-                del dictvalue[0]
-                if len(dictvalue)!= 1 or (isinstance(dictvalue,ParseResults) and dictvalue.haskeys()):
-                    tokenlist[ikey] = _ParseResultsWithOffset(dictvalue,i)
-                else:
-                    tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0],i)
-
-        if self.resultsName:
-            return [ tokenlist ]
-        else:
-            return tokenlist
-
-
-class Suppress(TokenConverter):
-    """
-    Converter for ignoring the results of a parsed expression.
-
-    Example::
-        source = "a, b, c,d"
-        wd = Word(alphas)
-        wd_list1 = wd + ZeroOrMore(',' + wd)
-        print(wd_list1.parseString(source))
-
-        # often, delimiters that are useful during parsing are just in the
-        # way afterward - use Suppress to keep them out of the parsed output
-        wd_list2 = wd + ZeroOrMore(Suppress(',') + wd)
-        print(wd_list2.parseString(source))
-    prints::
-        ['a', ',', 'b', ',', 'c', ',', 'd']
-        ['a', 'b', 'c', 'd']
-    (See also L{delimitedList}.)
-    """
-    def postParse( self, instring, loc, tokenlist ):
-        return []
-
-    def suppress( self ):
-        return self
-
-
-class OnlyOnce(object):
-    """
-    Wrapper for parse actions, to ensure they are only called once.
-    """
-    def __init__(self, methodCall):
-        self.callable = _trim_arity(methodCall)
-        self.called = False
-    def __call__(self,s,l,t):
-        if not self.called:
-            results = self.callable(s,l,t)
-            self.called = True
-            return results
-        raise ParseException(s,l,"")
-    def reset(self):
-        self.called = False
-
-def traceParseAction(f):
-    """
-    Decorator for debugging parse actions. 
-    
-    When the parse action is called, this decorator will print C{">> entering I{method-name}(line:I{current_source_line}, I{parse_location}, I{matched_tokens})".}
-    When the parse action completes, the decorator will print C{"<<"} followed by the returned value, or any exception that the parse action raised.
-
-    Example::
-        wd = Word(alphas)
-
-        @traceParseAction
-        def remove_duplicate_chars(tokens):
-            return ''.join(sorted(set(''.join(tokens))))
-
-        wds = OneOrMore(wd).setParseAction(remove_duplicate_chars)
-        print(wds.parseString("slkdjs sld sldd sdlf sdljf"))
-    prints::
-        >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {}))
-        <3:
-            thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc
-        sys.stderr.write( ">>entering %s(line: '%s', %d, %r)\n" % (thisFunc,line(l,s),l,t) )
-        try:
-            ret = f(*paArgs)
-        except Exception as exc:
-            sys.stderr.write( "< ['aa', 'bb', 'cc']
-        delimitedList(Word(hexnums), delim=':', combine=True).parseString("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE']
-    """
-    dlName = _ustr(expr)+" ["+_ustr(delim)+" "+_ustr(expr)+"]..."
-    if combine:
-        return Combine( expr + ZeroOrMore( delim + expr ) ).setName(dlName)
-    else:
-        return ( expr + ZeroOrMore( Suppress( delim ) + expr ) ).setName(dlName)
-
-def countedArray( expr, intExpr=None ):
-    """
-    Helper to define a counted list of expressions.
-    This helper defines a pattern of the form::
-        integer expr expr expr...
-    where the leading integer tells how many expr expressions follow.
-    The matched tokens returns the array of expr tokens as a list - the leading count token is suppressed.
-    
-    If C{intExpr} is specified, it should be a pyparsing expression that produces an integer value.
-
-    Example::
-        countedArray(Word(alphas)).parseString('2 ab cd ef')  # -> ['ab', 'cd']
-
-        # in this parser, the leading integer value is given in binary,
-        # '10' indicating that 2 values are in the array
-        binaryConstant = Word('01').setParseAction(lambda t: int(t[0], 2))
-        countedArray(Word(alphas), intExpr=binaryConstant).parseString('10 ab cd ef')  # -> ['ab', 'cd']
-    """
-    arrayExpr = Forward()
-    def countFieldParseAction(s,l,t):
-        n = t[0]
-        arrayExpr << (n and Group(And([expr]*n)) or Group(empty))
-        return []
-    if intExpr is None:
-        intExpr = Word(nums).setParseAction(lambda t:int(t[0]))
-    else:
-        intExpr = intExpr.copy()
-    intExpr.setName("arrayLen")
-    intExpr.addParseAction(countFieldParseAction, callDuringTry=True)
-    return ( intExpr + arrayExpr ).setName('(len) ' + _ustr(expr) + '...')
-
-def _flatten(L):
-    ret = []
-    for i in L:
-        if isinstance(i,list):
-            ret.extend(_flatten(i))
-        else:
-            ret.append(i)
-    return ret
-
-def matchPreviousLiteral(expr):
-    """
-    Helper to define an expression that is indirectly defined from
-    the tokens matched in a previous expression, that is, it looks
-    for a 'repeat' of a previous expression.  For example::
-        first = Word(nums)
-        second = matchPreviousLiteral(first)
-        matchExpr = first + ":" + second
-    will match C{"1:1"}, but not C{"1:2"}.  Because this matches a
-    previous literal, will also match the leading C{"1:1"} in C{"1:10"}.
-    If this is not desired, use C{matchPreviousExpr}.
-    Do I{not} use with packrat parsing enabled.
-    """
-    rep = Forward()
-    def copyTokenToRepeater(s,l,t):
-        if t:
-            if len(t) == 1:
-                rep << t[0]
-            else:
-                # flatten t tokens
-                tflat = _flatten(t.asList())
-                rep << And(Literal(tt) for tt in tflat)
-        else:
-            rep << Empty()
-    expr.addParseAction(copyTokenToRepeater, callDuringTry=True)
-    rep.setName('(prev) ' + _ustr(expr))
-    return rep
-
-def matchPreviousExpr(expr):
-    """
-    Helper to define an expression that is indirectly defined from
-    the tokens matched in a previous expression, that is, it looks
-    for a 'repeat' of a previous expression.  For example::
-        first = Word(nums)
-        second = matchPreviousExpr(first)
-        matchExpr = first + ":" + second
-    will match C{"1:1"}, but not C{"1:2"}.  Because this matches by
-    expressions, will I{not} match the leading C{"1:1"} in C{"1:10"};
-    the expressions are evaluated first, and then compared, so
-    C{"1"} is compared with C{"10"}.
-    Do I{not} use with packrat parsing enabled.
-    """
-    rep = Forward()
-    e2 = expr.copy()
-    rep <<= e2
-    def copyTokenToRepeater(s,l,t):
-        matchTokens = _flatten(t.asList())
-        def mustMatchTheseTokens(s,l,t):
-            theseTokens = _flatten(t.asList())
-            if  theseTokens != matchTokens:
-                raise ParseException("",0,"")
-        rep.setParseAction( mustMatchTheseTokens, callDuringTry=True )
-    expr.addParseAction(copyTokenToRepeater, callDuringTry=True)
-    rep.setName('(prev) ' + _ustr(expr))
-    return rep
-
-def _escapeRegexRangeChars(s):
-    #~  escape these chars: ^-]
-    for c in r"\^-]":
-        s = s.replace(c,_bslash+c)
-    s = s.replace("\n",r"\n")
-    s = s.replace("\t",r"\t")
-    return _ustr(s)
-
-def oneOf( strs, caseless=False, useRegex=True ):
-    """
-    Helper to quickly define a set of alternative Literals, and makes sure to do
-    longest-first testing when there is a conflict, regardless of the input order,
-    but returns a C{L{MatchFirst}} for best performance.
-
-    Parameters:
-     - strs - a string of space-delimited literals, or a collection of string literals
-     - caseless - (default=C{False}) - treat all literals as caseless
-     - useRegex - (default=C{True}) - as an optimization, will generate a Regex
-          object; otherwise, will generate a C{MatchFirst} object (if C{caseless=True}, or
-          if creating a C{Regex} raises an exception)
-
-    Example::
-        comp_oper = oneOf("< = > <= >= !=")
-        var = Word(alphas)
-        number = Word(nums)
-        term = var | number
-        comparison_expr = term + comp_oper + term
-        print(comparison_expr.searchString("B = 12  AA=23 B<=AA AA>12"))
-    prints::
-        [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']]
-    """
-    if caseless:
-        isequal = ( lambda a,b: a.upper() == b.upper() )
-        masks = ( lambda a,b: b.upper().startswith(a.upper()) )
-        parseElementClass = CaselessLiteral
-    else:
-        isequal = ( lambda a,b: a == b )
-        masks = ( lambda a,b: b.startswith(a) )
-        parseElementClass = Literal
-
-    symbols = []
-    if isinstance(strs,basestring):
-        symbols = strs.split()
-    elif isinstance(strs, Iterable):
-        symbols = list(strs)
-    else:
-        warnings.warn("Invalid argument to oneOf, expected string or iterable",
-                SyntaxWarning, stacklevel=2)
-    if not symbols:
-        return NoMatch()
-
-    i = 0
-    while i < len(symbols)-1:
-        cur = symbols[i]
-        for j,other in enumerate(symbols[i+1:]):
-            if ( isequal(other, cur) ):
-                del symbols[i+j+1]
-                break
-            elif ( masks(cur, other) ):
-                del symbols[i+j+1]
-                symbols.insert(i,other)
-                cur = other
-                break
-        else:
-            i += 1
-
-    if not caseless and useRegex:
-        #~ print (strs,"->", "|".join( [ _escapeRegexChars(sym) for sym in symbols] ))
-        try:
-            if len(symbols)==len("".join(symbols)):
-                return Regex( "[%s]" % "".join(_escapeRegexRangeChars(sym) for sym in symbols) ).setName(' | '.join(symbols))
-            else:
-                return Regex( "|".join(re.escape(sym) for sym in symbols) ).setName(' | '.join(symbols))
-        except Exception:
-            warnings.warn("Exception creating Regex for oneOf, building MatchFirst",
-                    SyntaxWarning, stacklevel=2)
-
-
-    # last resort, just use MatchFirst
-    return MatchFirst(parseElementClass(sym) for sym in symbols).setName(' | '.join(symbols))
-
-def dictOf( key, value ):
-    """
-    Helper to easily and clearly define a dictionary by specifying the respective patterns
-    for the key and value.  Takes care of defining the C{L{Dict}}, C{L{ZeroOrMore}}, and C{L{Group}} tokens
-    in the proper order.  The key pattern can include delimiting markers or punctuation,
-    as long as they are suppressed, thereby leaving the significant key text.  The value
-    pattern can include named results, so that the C{Dict} results can include named token
-    fields.
-
-    Example::
-        text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
-        attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
-        print(OneOrMore(attr_expr).parseString(text).dump())
-        
-        attr_label = label
-        attr_value = Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)
-
-        # similar to Dict, but simpler call format
-        result = dictOf(attr_label, attr_value).parseString(text)
-        print(result.dump())
-        print(result['shape'])
-        print(result.shape)  # object attribute access works too
-        print(result.asDict())
-    prints::
-        [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']]
-        - color: light blue
-        - posn: upper left
-        - shape: SQUARE
-        - texture: burlap
-        SQUARE
-        SQUARE
-        {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'}
-    """
-    return Dict( ZeroOrMore( Group ( key + value ) ) )
-
-def originalTextFor(expr, asString=True):
-    """
-    Helper to return the original, untokenized text for a given expression.  Useful to
-    restore the parsed fields of an HTML start tag into the raw tag text itself, or to
-    revert separate tokens with intervening whitespace back to the original matching
-    input text. By default, returns astring containing the original parsed text.  
-       
-    If the optional C{asString} argument is passed as C{False}, then the return value is a 
-    C{L{ParseResults}} containing any results names that were originally matched, and a 
-    single token containing the original matched text from the input string.  So if 
-    the expression passed to C{L{originalTextFor}} contains expressions with defined
-    results names, you must set C{asString} to C{False} if you want to preserve those
-    results name values.
-
-    Example::
-        src = "this is test  bold text  normal text "
-        for tag in ("b","i"):
-            opener,closer = makeHTMLTags(tag)
-            patt = originalTextFor(opener + SkipTo(closer) + closer)
-            print(patt.searchString(src)[0])
-    prints::
-        [' bold text ']
-        ['text']
-    """
-    locMarker = Empty().setParseAction(lambda s,loc,t: loc)
-    endlocMarker = locMarker.copy()
-    endlocMarker.callPreparse = False
-    matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end")
-    if asString:
-        extractText = lambda s,l,t: s[t._original_start:t._original_end]
-    else:
-        def extractText(s,l,t):
-            t[:] = [s[t.pop('_original_start'):t.pop('_original_end')]]
-    matchExpr.setParseAction(extractText)
-    matchExpr.ignoreExprs = expr.ignoreExprs
-    return matchExpr
-
-def ungroup(expr): 
-    """
-    Helper to undo pyparsing's default grouping of And expressions, even
-    if all but one are non-empty.
-    """
-    return TokenConverter(expr).setParseAction(lambda t:t[0])
-
-def locatedExpr(expr):
-    """
-    Helper to decorate a returned token with its starting and ending locations in the input string.
-    This helper adds the following results names:
-     - locn_start = location where matched expression begins
-     - locn_end = location where matched expression ends
-     - value = the actual parsed results
-
-    Be careful if the input text contains C{} characters, you may want to call
-    C{L{ParserElement.parseWithTabs}}
-
-    Example::
-        wd = Word(alphas)
-        for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"):
-            print(match)
-    prints::
-        [[0, 'ljsdf', 5]]
-        [[8, 'lksdjjf', 15]]
-        [[18, 'lkkjj', 23]]
-    """
-    locator = Empty().setParseAction(lambda s,l,t: l)
-    return Group(locator("locn_start") + expr("value") + locator.copy().leaveWhitespace()("locn_end"))
-
-
-# convenience constants for positional expressions
-empty       = Empty().setName("empty")
-lineStart   = LineStart().setName("lineStart")
-lineEnd     = LineEnd().setName("lineEnd")
-stringStart = StringStart().setName("stringStart")
-stringEnd   = StringEnd().setName("stringEnd")
-
-_escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1])
-_escapedHexChar = Regex(r"\\0?[xX][0-9a-fA-F]+").setParseAction(lambda s,l,t:unichr(int(t[0].lstrip(r'\0x'),16)))
-_escapedOctChar = Regex(r"\\0[0-7]+").setParseAction(lambda s,l,t:unichr(int(t[0][1:],8)))
-_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | CharsNotIn(r'\]', exact=1)
-_charRange = Group(_singleChar + Suppress("-") + _singleChar)
-_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]"
-
-def srange(s):
-    r"""
-    Helper to easily define string ranges for use in Word construction.  Borrows
-    syntax from regexp '[]' string range definitions::
-        srange("[0-9]")   -> "0123456789"
-        srange("[a-z]")   -> "abcdefghijklmnopqrstuvwxyz"
-        srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_"
-    The input string must be enclosed in []'s, and the returned string is the expanded
-    character set joined into a single string.
-    The values enclosed in the []'s may be:
-     - a single character
-     - an escaped character with a leading backslash (such as C{\-} or C{\]})
-     - an escaped hex character with a leading C{'\x'} (C{\x21}, which is a C{'!'} character) 
-         (C{\0x##} is also supported for backwards compatibility) 
-     - an escaped octal character with a leading C{'\0'} (C{\041}, which is a C{'!'} character)
-     - a range of any of the above, separated by a dash (C{'a-z'}, etc.)
-     - any combination of the above (C{'aeiouy'}, C{'a-zA-Z0-9_$'}, etc.)
-    """
-    _expanded = lambda p: p if not isinstance(p,ParseResults) else ''.join(unichr(c) for c in range(ord(p[0]),ord(p[1])+1))
-    try:
-        return "".join(_expanded(part) for part in _reBracketExpr.parseString(s).body)
-    except Exception:
-        return ""
-
-def matchOnlyAtCol(n):
-    """
-    Helper method for defining parse actions that require matching at a specific
-    column in the input text.
-    """
-    def verifyCol(strg,locn,toks):
-        if col(locn,strg) != n:
-            raise ParseException(strg,locn,"matched token not at column %d" % n)
-    return verifyCol
-
-def replaceWith(replStr):
-    """
-    Helper method for common parse actions that simply return a literal value.  Especially
-    useful when used with C{L{transformString}()}.
-
-    Example::
-        num = Word(nums).setParseAction(lambda toks: int(toks[0]))
-        na = oneOf("N/A NA").setParseAction(replaceWith(math.nan))
-        term = na | num
-        
-        OneOrMore(term).parseString("324 234 N/A 234") # -> [324, 234, nan, 234]
-    """
-    return lambda s,l,t: [replStr]
-
-def removeQuotes(s,l,t):
-    """
-    Helper parse action for removing quotation marks from parsed quoted strings.
-
-    Example::
-        # by default, quotation marks are included in parsed results
-        quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"]
-
-        # use removeQuotes to strip quotation marks from parsed results
-        quotedString.setParseAction(removeQuotes)
-        quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"]
-    """
-    return t[0][1:-1]
-
-def tokenMap(func, *args):
-    """
-    Helper to define a parse action by mapping a function to all elements of a ParseResults list.If any additional 
-    args are passed, they are forwarded to the given function as additional arguments after
-    the token, as in C{hex_integer = Word(hexnums).setParseAction(tokenMap(int, 16))}, which will convert the
-    parsed data to an integer using base 16.
-
-    Example (compare the last to example in L{ParserElement.transformString}::
-        hex_ints = OneOrMore(Word(hexnums)).setParseAction(tokenMap(int, 16))
-        hex_ints.runTests('''
-            00 11 22 aa FF 0a 0d 1a
-            ''')
-        
-        upperword = Word(alphas).setParseAction(tokenMap(str.upper))
-        OneOrMore(upperword).runTests('''
-            my kingdom for a horse
-            ''')
-
-        wd = Word(alphas).setParseAction(tokenMap(str.title))
-        OneOrMore(wd).setParseAction(' '.join).runTests('''
-            now is the winter of our discontent made glorious summer by this sun of york
-            ''')
-    prints::
-        00 11 22 aa FF 0a 0d 1a
-        [0, 17, 34, 170, 255, 10, 13, 26]
-
-        my kingdom for a horse
-        ['MY', 'KINGDOM', 'FOR', 'A', 'HORSE']
-
-        now is the winter of our discontent made glorious summer by this sun of york
-        ['Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York']
-    """
-    def pa(s,l,t):
-        return [func(tokn, *args) for tokn in t]
-
-    try:
-        func_name = getattr(func, '__name__', 
-                            getattr(func, '__class__').__name__)
-    except Exception:
-        func_name = str(func)
-    pa.__name__ = func_name
-
-    return pa
-
-upcaseTokens = tokenMap(lambda t: _ustr(t).upper())
-"""(Deprecated) Helper parse action to convert tokens to upper case. Deprecated in favor of L{pyparsing_common.upcaseTokens}"""
-
-downcaseTokens = tokenMap(lambda t: _ustr(t).lower())
-"""(Deprecated) Helper parse action to convert tokens to lower case. Deprecated in favor of L{pyparsing_common.downcaseTokens}"""
-    
-def _makeTags(tagStr, xml):
-    """Internal helper to construct opening and closing tag expressions, given a tag name"""
-    if isinstance(tagStr,basestring):
-        resname = tagStr
-        tagStr = Keyword(tagStr, caseless=not xml)
-    else:
-        resname = tagStr.name
-
-    tagAttrName = Word(alphas,alphanums+"_-:")
-    if (xml):
-        tagAttrValue = dblQuotedString.copy().setParseAction( removeQuotes )
-        openTag = Suppress("<") + tagStr("tag") + \
-                Dict(ZeroOrMore(Group( tagAttrName + Suppress("=") + tagAttrValue ))) + \
-                Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">")
-    else:
-        printablesLessRAbrack = "".join(c for c in printables if c not in ">")
-        tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printablesLessRAbrack)
-        openTag = Suppress("<") + tagStr("tag") + \
-                Dict(ZeroOrMore(Group( tagAttrName.setParseAction(downcaseTokens) + \
-                Optional( Suppress("=") + tagAttrValue ) ))) + \
-                Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">")
-    closeTag = Combine(_L("")
-
-    openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % resname)
-    closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("" % resname)
-    openTag.tag = resname
-    closeTag.tag = resname
-    return openTag, closeTag
-
-def makeHTMLTags(tagStr):
-    """
-    Helper to construct opening and closing tag expressions for HTML, given a tag name. Matches
-    tags in either upper or lower case, attributes with namespaces and with quoted or unquoted values.
-
-    Example::
-        text = 'More info at the pyparsing wiki page'
-        # makeHTMLTags returns pyparsing expressions for the opening and closing tags as a 2-tuple
-        a,a_end = makeHTMLTags("A")
-        link_expr = a + SkipTo(a_end)("link_text") + a_end
-        
-        for link in link_expr.searchString(text):
-            # attributes in the  tag (like "href" shown here) are also accessible as named results
-            print(link.link_text, '->', link.href)
-    prints::
-        pyparsing -> http://pyparsing.wikispaces.com
-    """
-    return _makeTags( tagStr, False )
-
-def makeXMLTags(tagStr):
-    """
-    Helper to construct opening and closing tag expressions for XML, given a tag name. Matches
-    tags only in the given upper/lower case.
-
-    Example: similar to L{makeHTMLTags}
-    """
-    return _makeTags( tagStr, True )
-
-def withAttribute(*args,**attrDict):
-    """
-    Helper to create a validating parse action to be used with start tags created
-    with C{L{makeXMLTags}} or C{L{makeHTMLTags}}. Use C{withAttribute} to qualify a starting tag
-    with a required attribute value, to avoid false matches on common tags such as
-    C{} or C{
}. - - Call C{withAttribute} with a series of attribute names and values. Specify the list - of filter attributes names and values as: - - keyword arguments, as in C{(align="right")}, or - - as an explicit dict with C{**} operator, when an attribute name is also a Python - reserved word, as in C{**{"class":"Customer", "align":"right"}} - - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") ) - For attribute names with a namespace prefix, you must use the second form. Attribute - names are matched insensitive to upper/lower case. - - If just testing for C{class} (with or without a namespace), use C{L{withClass}}. - - To verify that the attribute exists, but without specifying a value, pass - C{withAttribute.ANY_VALUE} as the value. - - Example:: - html = ''' -
- Some text -
1 4 0 1 0
-
1,3 2,3 1,1
-
this has no type
-
- - ''' - div,div_end = makeHTMLTags("div") - - # only match div tag having a type attribute with value "grid" - div_grid = div().setParseAction(withAttribute(type="grid")) - grid_expr = div_grid + SkipTo(div | div_end)("body") - for grid_header in grid_expr.searchString(html): - print(grid_header.body) - - # construct a match with any div tag having a type attribute, regardless of the value - div_any_type = div().setParseAction(withAttribute(type=withAttribute.ANY_VALUE)) - div_expr = div_any_type + SkipTo(div | div_end)("body") - for div_header in div_expr.searchString(html): - print(div_header.body) - prints:: - 1 4 0 1 0 - - 1 4 0 1 0 - 1,3 2,3 1,1 - """ - if args: - attrs = args[:] - else: - attrs = attrDict.items() - attrs = [(k,v) for k,v in attrs] - def pa(s,l,tokens): - for attrName,attrValue in attrs: - if attrName not in tokens: - raise ParseException(s,l,"no matching attribute " + attrName) - if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue: - raise ParseException(s,l,"attribute '%s' has value '%s', must be '%s'" % - (attrName, tokens[attrName], attrValue)) - return pa -withAttribute.ANY_VALUE = object() - -def withClass(classname, namespace=''): - """ - Simplified version of C{L{withAttribute}} when matching on a div class - made - difficult because C{class} is a reserved word in Python. - - Example:: - html = ''' -
- Some text -
1 4 0 1 0
-
1,3 2,3 1,1
-
this <div> has no class
-
- - ''' - div,div_end = makeHTMLTags("div") - div_grid = div().setParseAction(withClass("grid")) - - grid_expr = div_grid + SkipTo(div | div_end)("body") - for grid_header in grid_expr.searchString(html): - print(grid_header.body) - - div_any_type = div().setParseAction(withClass(withAttribute.ANY_VALUE)) - div_expr = div_any_type + SkipTo(div | div_end)("body") - for div_header in div_expr.searchString(html): - print(div_header.body) - prints:: - 1 4 0 1 0 - - 1 4 0 1 0 - 1,3 2,3 1,1 - """ - classattr = "%s:class" % namespace if namespace else "class" - return withAttribute(**{classattr : classname}) - -opAssoc = _Constants() -opAssoc.LEFT = object() -opAssoc.RIGHT = object() - -def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ): - """ - Helper method for constructing grammars of expressions made up of - operators working in a precedence hierarchy. Operators may be unary or - binary, left- or right-associative. Parse actions can also be attached - to operator expressions. The generated parser will also recognize the use - of parentheses to override operator precedences (see example below). - - Note: if you define a deep operator list, you may see performance issues - when using infixNotation. See L{ParserElement.enablePackrat} for a - mechanism to potentially improve your parser performance. - - Parameters: - - baseExpr - expression representing the most basic element for the nested - - opList - list of tuples, one for each operator precedence level in the - expression grammar; each tuple is of the form - (opExpr, numTerms, rightLeftAssoc, parseAction), where: - - opExpr is the pyparsing expression for the operator; - may also be a string, which will be converted to a Literal; - if numTerms is 3, opExpr is a tuple of two expressions, for the - two operators separating the 3 terms - - numTerms is the number of terms for this operator (must - be 1, 2, or 3) - - rightLeftAssoc is the indicator whether the operator is - right or left associative, using the pyparsing-defined - constants C{opAssoc.RIGHT} and C{opAssoc.LEFT}. - - parseAction is the parse action to be associated with - expressions matching this operator expression (the - parse action tuple member may be omitted); if the parse action - is passed a tuple or list of functions, this is equivalent to - calling C{setParseAction(*fn)} (L{ParserElement.setParseAction}) - - lpar - expression for matching left-parentheses (default=C{Suppress('(')}) - - rpar - expression for matching right-parentheses (default=C{Suppress(')')}) - - Example:: - # simple example of four-function arithmetic with ints and variable names - integer = pyparsing_common.signed_integer - varname = pyparsing_common.identifier - - arith_expr = infixNotation(integer | varname, - [ - ('-', 1, opAssoc.RIGHT), - (oneOf('* /'), 2, opAssoc.LEFT), - (oneOf('+ -'), 2, opAssoc.LEFT), - ]) - - arith_expr.runTests(''' - 5+3*6 - (5+3)*6 - -2--11 - ''', fullDump=False) - prints:: - 5+3*6 - [[5, '+', [3, '*', 6]]] - - (5+3)*6 - [[[5, '+', 3], '*', 6]] - - -2--11 - [[['-', 2], '-', ['-', 11]]] - """ - ret = Forward() - lastExpr = baseExpr | ( lpar + ret + rpar ) - for i,operDef in enumerate(opList): - opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] - termName = "%s term" % opExpr if arity < 3 else "%s%s term" % opExpr - if arity == 3: - if opExpr is None or len(opExpr) != 2: - raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions") - opExpr1, opExpr2 = opExpr - thisExpr = Forward().setName(termName) - if rightLeftAssoc == opAssoc.LEFT: - if arity == 1: - matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) ) - elif arity == 2: - if opExpr is not None: - matchExpr = FollowedBy(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) ) - else: - matchExpr = FollowedBy(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) ) - elif arity == 3: - matchExpr = FollowedBy(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \ - Group( lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr ) - else: - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - elif rightLeftAssoc == opAssoc.RIGHT: - if arity == 1: - # try to avoid LR with this extra test - if not isinstance(opExpr, Optional): - opExpr = Optional(opExpr) - matchExpr = FollowedBy(opExpr.expr + thisExpr) + Group( opExpr + thisExpr ) - elif arity == 2: - if opExpr is not None: - matchExpr = FollowedBy(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) ) - else: - matchExpr = FollowedBy(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) ) - elif arity == 3: - matchExpr = FollowedBy(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \ - Group( lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr ) - else: - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - else: - raise ValueError("operator must indicate right or left associativity") - if pa: - if isinstance(pa, (tuple, list)): - matchExpr.setParseAction(*pa) - else: - matchExpr.setParseAction(pa) - thisExpr <<= ( matchExpr.setName(termName) | lastExpr ) - lastExpr = thisExpr - ret <<= lastExpr - return ret - -operatorPrecedence = infixNotation -"""(Deprecated) Former name of C{L{infixNotation}}, will be dropped in a future release.""" - -dblQuotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"').setName("string enclosed in double quotes") -sglQuotedString = Combine(Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("string enclosed in single quotes") -quotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"'| - Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("quotedString using single or double quotes") -unicodeString = Combine(_L('u') + quotedString.copy()).setName("unicode string literal") - -def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString.copy()): - """ - Helper method for defining nested lists enclosed in opening and closing - delimiters ("(" and ")" are the default). - - Parameters: - - opener - opening character for a nested list (default=C{"("}); can also be a pyparsing expression - - closer - closing character for a nested list (default=C{")"}); can also be a pyparsing expression - - content - expression for items within the nested lists (default=C{None}) - - ignoreExpr - expression for ignoring opening and closing delimiters (default=C{quotedString}) - - If an expression is not provided for the content argument, the nested - expression will capture all whitespace-delimited content between delimiters - as a list of separate values. - - Use the C{ignoreExpr} argument to define expressions that may contain - opening or closing characters that should not be treated as opening - or closing characters for nesting, such as quotedString or a comment - expression. Specify multiple expressions using an C{L{Or}} or C{L{MatchFirst}}. - The default is L{quotedString}, but if no expressions are to be ignored, - then pass C{None} for this argument. - - Example:: - data_type = oneOf("void int short long char float double") - decl_data_type = Combine(data_type + Optional(Word('*'))) - ident = Word(alphas+'_', alphanums+'_') - number = pyparsing_common.number - arg = Group(decl_data_type + ident) - LPAR,RPAR = map(Suppress, "()") - - code_body = nestedExpr('{', '}', ignoreExpr=(quotedString | cStyleComment)) - - c_function = (decl_data_type("type") - + ident("name") - + LPAR + Optional(delimitedList(arg), [])("args") + RPAR - + code_body("body")) - c_function.ignore(cStyleComment) - - source_code = ''' - int is_odd(int x) { - return (x%2); - } - - int dec_to_hex(char hchar) { - if (hchar >= '0' && hchar <= '9') { - return (ord(hchar)-ord('0')); - } else { - return (10+ord(hchar)-ord('A')); - } - } - ''' - for func in c_function.searchString(source_code): - print("%(name)s (%(type)s) args: %(args)s" % func) - - prints:: - is_odd (int) args: [['int', 'x']] - dec_to_hex (int) args: [['char', 'hchar']] - """ - if opener == closer: - raise ValueError("opening and closing strings cannot be the same") - if content is None: - if isinstance(opener,basestring) and isinstance(closer,basestring): - if len(opener) == 1 and len(closer)==1: - if ignoreExpr is not None: - content = (Combine(OneOrMore(~ignoreExpr + - CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - ).setParseAction(lambda t:t[0].strip())) - else: - content = (empty.copy()+CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS - ).setParseAction(lambda t:t[0].strip())) - else: - if ignoreExpr is not None: - content = (Combine(OneOrMore(~ignoreExpr + - ~Literal(opener) + ~Literal(closer) + - CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - ).setParseAction(lambda t:t[0].strip())) - else: - content = (Combine(OneOrMore(~Literal(opener) + ~Literal(closer) + - CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) - ).setParseAction(lambda t:t[0].strip())) - else: - raise ValueError("opening and closing arguments must be strings if no content expression is given") - ret = Forward() - if ignoreExpr is not None: - ret <<= Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) - else: - ret <<= Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) - ret.setName('nested %s%s expression' % (opener,closer)) - return ret - -def indentedBlock(blockStatementExpr, indentStack, indent=True): - """ - Helper method for defining space-delimited indentation blocks, such as - those used to define block statements in Python source code. - - Parameters: - - blockStatementExpr - expression defining syntax of statement that - is repeated within the indented block - - indentStack - list created by caller to manage indentation stack - (multiple statementWithIndentedBlock expressions within a single grammar - should share a common indentStack) - - indent - boolean indicating whether block must be indented beyond the - the current level; set to False for block of left-most statements - (default=C{True}) - - A valid block must contain at least one C{blockStatement}. - - Example:: - data = ''' - def A(z): - A1 - B = 100 - G = A2 - A2 - A3 - B - def BB(a,b,c): - BB1 - def BBA(): - bba1 - bba2 - bba3 - C - D - def spam(x,y): - def eggs(z): - pass - ''' - - - indentStack = [1] - stmt = Forward() - - identifier = Word(alphas, alphanums) - funcDecl = ("def" + identifier + Group( "(" + Optional( delimitedList(identifier) ) + ")" ) + ":") - func_body = indentedBlock(stmt, indentStack) - funcDef = Group( funcDecl + func_body ) - - rvalue = Forward() - funcCall = Group(identifier + "(" + Optional(delimitedList(rvalue)) + ")") - rvalue << (funcCall | identifier | Word(nums)) - assignment = Group(identifier + "=" + rvalue) - stmt << ( funcDef | assignment | identifier ) - - module_body = OneOrMore(stmt) - - parseTree = module_body.parseString(data) - parseTree.pprint() - prints:: - [['def', - 'A', - ['(', 'z', ')'], - ':', - [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], - 'B', - ['def', - 'BB', - ['(', 'a', 'b', 'c', ')'], - ':', - [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], - 'C', - 'D', - ['def', - 'spam', - ['(', 'x', 'y', ')'], - ':', - [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] - """ - def checkPeerIndent(s,l,t): - if l >= len(s): return - curCol = col(l,s) - if curCol != indentStack[-1]: - if curCol > indentStack[-1]: - raise ParseFatalException(s,l,"illegal nesting") - raise ParseException(s,l,"not a peer entry") - - def checkSubIndent(s,l,t): - curCol = col(l,s) - if curCol > indentStack[-1]: - indentStack.append( curCol ) - else: - raise ParseException(s,l,"not a subentry") - - def checkUnindent(s,l,t): - if l >= len(s): return - curCol = col(l,s) - if not(indentStack and curCol < indentStack[-1] and curCol <= indentStack[-2]): - raise ParseException(s,l,"not an unindent") - indentStack.pop() - - NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress()) - INDENT = (Empty() + Empty().setParseAction(checkSubIndent)).setName('INDENT') - PEER = Empty().setParseAction(checkPeerIndent).setName('') - UNDENT = Empty().setParseAction(checkUnindent).setName('UNINDENT') - if indent: - smExpr = Group( Optional(NL) + - #~ FollowedBy(blockStatementExpr) + - INDENT + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) + UNDENT) - else: - smExpr = Group( Optional(NL) + - (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) ) - blockStatementExpr.ignore(_bslash + LineEnd()) - return smExpr.setName('indented block') - -alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") -punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") - -anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:").setName('any tag')) -_htmlEntityMap = dict(zip("gt lt amp nbsp quot apos".split(),'><& "\'')) -commonHTMLEntity = Regex('&(?P' + '|'.join(_htmlEntityMap.keys()) +");").setName("common HTML entity") -def replaceHTMLEntity(t): - """Helper parser action to replace common HTML entities with their special characters""" - return _htmlEntityMap.get(t.entity) - -# it's easy to get these comment structures wrong - they're very common, so may as well make them available -cStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/').setName("C style comment") -"Comment of the form C{/* ... */}" - -htmlComment = Regex(r"").setName("HTML comment") -"Comment of the form C{}" - -restOfLine = Regex(r".*").leaveWhitespace().setName("rest of line") -dblSlashComment = Regex(r"//(?:\\\n|[^\n])*").setName("// comment") -"Comment of the form C{// ... (to end of line)}" - -cppStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/'| dblSlashComment).setName("C++ style comment") -"Comment of either form C{L{cStyleComment}} or C{L{dblSlashComment}}" - -javaStyleComment = cppStyleComment -"Same as C{L{cppStyleComment}}" - -pythonStyleComment = Regex(r"#.*").setName("Python style comment") -"Comment of the form C{# ... (to end of line)}" - -_commasepitem = Combine(OneOrMore(Word(printables, excludeChars=',') + - Optional( Word(" \t") + - ~Literal(",") + ~LineEnd() ) ) ).streamline().setName("commaItem") -commaSeparatedList = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("commaSeparatedList") -"""(Deprecated) Predefined expression of 1 or more printable words or quoted strings, separated by commas. - This expression is deprecated in favor of L{pyparsing_common.comma_separated_list}.""" - -# some other useful expressions - using lower-case class name since we are really using this as a namespace -class pyparsing_common: - """ - Here are some common low-level expressions that may be useful in jump-starting parser development: - - numeric forms (L{integers}, L{reals}, L{scientific notation}) - - common L{programming identifiers} - - network addresses (L{MAC}, L{IPv4}, L{IPv6}) - - ISO8601 L{dates} and L{datetime} - - L{UUID} - - L{comma-separated list} - Parse actions: - - C{L{convertToInteger}} - - C{L{convertToFloat}} - - C{L{convertToDate}} - - C{L{convertToDatetime}} - - C{L{stripHTMLTags}} - - C{L{upcaseTokens}} - - C{L{downcaseTokens}} - - Example:: - pyparsing_common.number.runTests(''' - # any int or real number, returned as the appropriate type - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - ''') - - pyparsing_common.fnumber.runTests(''' - # any int or real number, returned as float - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - ''') - - pyparsing_common.hex_integer.runTests(''' - # hex numbers - 100 - FF - ''') - - pyparsing_common.fraction.runTests(''' - # fractions - 1/2 - -3/4 - ''') - - pyparsing_common.mixed_integer.runTests(''' - # mixed fractions - 1 - 1/2 - -3/4 - 1-3/4 - ''') - - import uuid - pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) - pyparsing_common.uuid.runTests(''' - # uuid - 12345678-1234-5678-1234-567812345678 - ''') - prints:: - # any int or real number, returned as the appropriate type - 100 - [100] - - -100 - [-100] - - +100 - [100] - - 3.14159 - [3.14159] - - 6.02e23 - [6.02e+23] - - 1e-12 - [1e-12] - - # any int or real number, returned as float - 100 - [100.0] - - -100 - [-100.0] - - +100 - [100.0] - - 3.14159 - [3.14159] - - 6.02e23 - [6.02e+23] - - 1e-12 - [1e-12] - - # hex numbers - 100 - [256] - - FF - [255] - - # fractions - 1/2 - [0.5] - - -3/4 - [-0.75] - - # mixed fractions - 1 - [1] - - 1/2 - [0.5] - - -3/4 - [-0.75] - - 1-3/4 - [1.75] - - # uuid - 12345678-1234-5678-1234-567812345678 - [UUID('12345678-1234-5678-1234-567812345678')] - """ - - convertToInteger = tokenMap(int) - """ - Parse action for converting parsed integers to Python int - """ - - convertToFloat = tokenMap(float) - """ - Parse action for converting parsed numbers to Python float - """ - - integer = Word(nums).setName("integer").setParseAction(convertToInteger) - """expression that parses an unsigned integer, returns an int""" - - hex_integer = Word(hexnums).setName("hex integer").setParseAction(tokenMap(int,16)) - """expression that parses a hexadecimal integer, returns an int""" - - signed_integer = Regex(r'[+-]?\d+').setName("signed integer").setParseAction(convertToInteger) - """expression that parses an integer with optional leading sign, returns an int""" - - fraction = (signed_integer().setParseAction(convertToFloat) + '/' + signed_integer().setParseAction(convertToFloat)).setName("fraction") - """fractional expression of an integer divided by an integer, returns a float""" - fraction.addParseAction(lambda t: t[0]/t[-1]) - - mixed_integer = (fraction | signed_integer + Optional(Optional('-').suppress() + fraction)).setName("fraction or mixed integer-fraction") - """mixed integer of the form 'integer - fraction', with optional leading integer, returns float""" - mixed_integer.addParseAction(sum) - - real = Regex(r'[+-]?\d+\.\d*').setName("real number").setParseAction(convertToFloat) - """expression that parses a floating point number and returns a float""" - - sci_real = Regex(r'[+-]?\d+([eE][+-]?\d+|\.\d*([eE][+-]?\d+)?)').setName("real number with scientific notation").setParseAction(convertToFloat) - """expression that parses a floating point number with optional scientific notation and returns a float""" - - # streamlining this expression makes the docs nicer-looking - number = (sci_real | real | signed_integer).streamline() - """any numeric expression, returns the corresponding Python type""" - - fnumber = Regex(r'[+-]?\d+\.?\d*([eE][+-]?\d+)?').setName("fnumber").setParseAction(convertToFloat) - """any int or real number, returned as float""" - - identifier = Word(alphas+'_', alphanums+'_').setName("identifier") - """typical code identifier (leading alpha or '_', followed by 0 or more alphas, nums, or '_')""" - - ipv4_address = Regex(r'(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})){3}').setName("IPv4 address") - "IPv4 address (C{0.0.0.0 - 255.255.255.255})" - - _ipv6_part = Regex(r'[0-9a-fA-F]{1,4}').setName("hex_integer") - _full_ipv6_address = (_ipv6_part + (':' + _ipv6_part)*7).setName("full IPv6 address") - _short_ipv6_address = (Optional(_ipv6_part + (':' + _ipv6_part)*(0,6)) + "::" + Optional(_ipv6_part + (':' + _ipv6_part)*(0,6))).setName("short IPv6 address") - _short_ipv6_address.addCondition(lambda t: sum(1 for tt in t if pyparsing_common._ipv6_part.matches(tt)) < 8) - _mixed_ipv6_address = ("::ffff:" + ipv4_address).setName("mixed IPv6 address") - ipv6_address = Combine((_full_ipv6_address | _mixed_ipv6_address | _short_ipv6_address).setName("IPv6 address")).setName("IPv6 address") - "IPv6 address (long, short, or mixed form)" - - mac_address = Regex(r'[0-9a-fA-F]{2}([:.-])[0-9a-fA-F]{2}(?:\1[0-9a-fA-F]{2}){4}').setName("MAC address") - "MAC address xx:xx:xx:xx:xx (may also have '-' or '.' delimiters)" - - @staticmethod - def convertToDate(fmt="%Y-%m-%d"): - """ - Helper to create a parse action for converting parsed date string to Python datetime.date - - Params - - - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%d"}) - - Example:: - date_expr = pyparsing_common.iso8601_date.copy() - date_expr.setParseAction(pyparsing_common.convertToDate()) - print(date_expr.parseString("1999-12-31")) - prints:: - [datetime.date(1999, 12, 31)] - """ - def cvt_fn(s,l,t): - try: - return datetime.strptime(t[0], fmt).date() - except ValueError as ve: - raise ParseException(s, l, str(ve)) - return cvt_fn - - @staticmethod - def convertToDatetime(fmt="%Y-%m-%dT%H:%M:%S.%f"): - """ - Helper to create a parse action for converting parsed datetime string to Python datetime.datetime - - Params - - - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%dT%H:%M:%S.%f"}) - - Example:: - dt_expr = pyparsing_common.iso8601_datetime.copy() - dt_expr.setParseAction(pyparsing_common.convertToDatetime()) - print(dt_expr.parseString("1999-12-31T23:59:59.999")) - prints:: - [datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)] - """ - def cvt_fn(s,l,t): - try: - return datetime.strptime(t[0], fmt) - except ValueError as ve: - raise ParseException(s, l, str(ve)) - return cvt_fn - - iso8601_date = Regex(r'(?P\d{4})(?:-(?P\d\d)(?:-(?P\d\d))?)?').setName("ISO8601 date") - "ISO8601 date (C{yyyy-mm-dd})" - - iso8601_datetime = Regex(r'(?P\d{4})-(?P\d\d)-(?P\d\d)[T ](?P\d\d):(?P\d\d)(:(?P\d\d(\.\d*)?)?)?(?PZ|[+-]\d\d:?\d\d)?').setName("ISO8601 datetime") - "ISO8601 datetime (C{yyyy-mm-ddThh:mm:ss.s(Z|+-00:00)}) - trailing seconds, milliseconds, and timezone optional; accepts separating C{'T'} or C{' '}" - - uuid = Regex(r'[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}').setName("UUID") - "UUID (C{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx})" - - _html_stripper = anyOpenTag.suppress() | anyCloseTag.suppress() - @staticmethod - def stripHTMLTags(s, l, tokens): - """ - Parse action to remove HTML tags from web page HTML source - - Example:: - # strip HTML links from normal text - text = 'More info at the
pyparsing wiki page' - td,td_end = makeHTMLTags("TD") - table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end - - print(table_text.parseString(text).body) # -> 'More info at the pyparsing wiki page' - """ - return pyparsing_common._html_stripper.transformString(tokens[0]) - - _commasepitem = Combine(OneOrMore(~Literal(",") + ~LineEnd() + Word(printables, excludeChars=',') - + Optional( White(" \t") ) ) ).streamline().setName("commaItem") - comma_separated_list = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("comma separated list") - """Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" - - upcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).upper())) - """Parse action to convert tokens to upper case.""" - - downcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).lower())) - """Parse action to convert tokens to lower case.""" - - -if __name__ == "__main__": - - selectToken = CaselessLiteral("select") - fromToken = CaselessLiteral("from") - - ident = Word(alphas, alphanums + "_$") - - columnName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) - columnNameList = Group(delimitedList(columnName)).setName("columns") - columnSpec = ('*' | columnNameList) - - tableName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) - tableNameList = Group(delimitedList(tableName)).setName("tables") - - simpleSQL = selectToken("command") + columnSpec("columns") + fromToken + tableNameList("tables") - - # demo runTests method, including embedded comments in test string - simpleSQL.runTests(""" - # '*' as column list and dotted table name - select * from SYS.XYZZY - - # caseless match on "SELECT", and casts back to "select" - SELECT * from XYZZY, ABC - - # list of column names, and mixed case SELECT keyword - Select AA,BB,CC from Sys.dual - - # multiple tables - Select A, B, C from Sys.dual, Table2 - - # invalid SELECT keyword - should fail - Xelect A, B, C from Sys.dual - - # incomplete command - should fail - Select - - # invalid column name - should fail - Select ^^^ frox Sys.dual - - """) - - pyparsing_common.number.runTests(""" - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - """) - - # any int or real number, returned as float - pyparsing_common.fnumber.runTests(""" - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - """) - - pyparsing_common.hex_integer.runTests(""" - 100 - FF - """) - - import uuid - pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) - pyparsing_common.uuid.runTests(""" - 12345678-1234-5678-1234-567812345678 - """) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/_vendor/six.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/_vendor/six.py deleted file mode 100644 index 190c023..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/_vendor/six.py +++ /dev/null @@ -1,868 +0,0 @@ -"""Utilities for writing code that runs on Python 2 and 3""" - -# Copyright (c) 2010-2015 Benjamin Peterson -# -# 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. - -from __future__ import absolute_import - -import functools -import itertools -import operator -import sys -import types - -__author__ = "Benjamin Peterson " -__version__ = "1.10.0" - - -# Useful for very coarse version differentiation. -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -PY34 = sys.version_info[0:2] >= (3, 4) - -if PY3: - string_types = str, - integer_types = int, - class_types = type, - text_type = str - binary_type = bytes - - MAXSIZE = sys.maxsize -else: - string_types = basestring, - integer_types = (int, long) - class_types = (type, types.ClassType) - text_type = unicode - binary_type = str - - if sys.platform.startswith("java"): - # Jython always uses 32 bits. - MAXSIZE = int((1 << 31) - 1) - else: - # It's possible to have sizeof(long) != sizeof(Py_ssize_t). - class X(object): - - def __len__(self): - return 1 << 31 - try: - len(X()) - except OverflowError: - # 32-bit - MAXSIZE = int((1 << 31) - 1) - else: - # 64-bit - MAXSIZE = int((1 << 63) - 1) - del X - - -def _add_doc(func, doc): - """Add documentation to a function.""" - func.__doc__ = doc - - -def _import_module(name): - """Import module, returning the module after the last dot.""" - __import__(name) - return sys.modules[name] - - -class _LazyDescr(object): - - def __init__(self, name): - self.name = name - - def __get__(self, obj, tp): - result = self._resolve() - setattr(obj, self.name, result) # Invokes __set__. - try: - # This is a bit ugly, but it avoids running this again by - # removing this descriptor. - delattr(obj.__class__, self.name) - except AttributeError: - pass - return result - - -class MovedModule(_LazyDescr): - - def __init__(self, name, old, new=None): - super(MovedModule, self).__init__(name) - if PY3: - if new is None: - new = name - self.mod = new - else: - self.mod = old - - def _resolve(self): - return _import_module(self.mod) - - def __getattr__(self, attr): - _module = self._resolve() - value = getattr(_module, attr) - setattr(self, attr, value) - return value - - -class _LazyModule(types.ModuleType): - - def __init__(self, name): - super(_LazyModule, self).__init__(name) - self.__doc__ = self.__class__.__doc__ - - def __dir__(self): - attrs = ["__doc__", "__name__"] - attrs += [attr.name for attr in self._moved_attributes] - return attrs - - # Subclasses should override this - _moved_attributes = [] - - -class MovedAttribute(_LazyDescr): - - def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): - super(MovedAttribute, self).__init__(name) - if PY3: - if new_mod is None: - new_mod = name - self.mod = new_mod - if new_attr is None: - if old_attr is None: - new_attr = name - else: - new_attr = old_attr - self.attr = new_attr - else: - self.mod = old_mod - if old_attr is None: - old_attr = name - self.attr = old_attr - - def _resolve(self): - module = _import_module(self.mod) - return getattr(module, self.attr) - - -class _SixMetaPathImporter(object): - - """ - A meta path importer to import six.moves and its submodules. - - This class implements a PEP302 finder and loader. It should be compatible - with Python 2.5 and all existing versions of Python3 - """ - - def __init__(self, six_module_name): - self.name = six_module_name - self.known_modules = {} - - def _add_module(self, mod, *fullnames): - for fullname in fullnames: - self.known_modules[self.name + "." + fullname] = mod - - def _get_module(self, fullname): - return self.known_modules[self.name + "." + fullname] - - def find_module(self, fullname, path=None): - if fullname in self.known_modules: - return self - return None - - def __get_module(self, fullname): - try: - return self.known_modules[fullname] - except KeyError: - raise ImportError("This loader does not know module " + fullname) - - def load_module(self, fullname): - try: - # in case of a reload - return sys.modules[fullname] - except KeyError: - pass - mod = self.__get_module(fullname) - if isinstance(mod, MovedModule): - mod = mod._resolve() - else: - mod.__loader__ = self - sys.modules[fullname] = mod - return mod - - def is_package(self, fullname): - """ - Return true, if the named module is a package. - - We need this method to get correct spec objects with - Python 3.4 (see PEP451) - """ - return hasattr(self.__get_module(fullname), "__path__") - - def get_code(self, fullname): - """Return None - - Required, if is_package is implemented""" - self.__get_module(fullname) # eventually raises ImportError - return None - get_source = get_code # same as get_code - -_importer = _SixMetaPathImporter(__name__) - - -class _MovedItems(_LazyModule): - - """Lazy loading of moved objects""" - __path__ = [] # mark as package - - -_moved_attributes = [ - MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), - MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), - MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), - MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), - MovedAttribute("intern", "__builtin__", "sys"), - MovedAttribute("map", "itertools", "builtins", "imap", "map"), - MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), - MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), - MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), - MovedAttribute("reduce", "__builtin__", "functools"), - MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), - MovedAttribute("StringIO", "StringIO", "io"), - MovedAttribute("UserDict", "UserDict", "collections"), - MovedAttribute("UserList", "UserList", "collections"), - MovedAttribute("UserString", "UserString", "collections"), - MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), - MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - MovedModule("builtins", "__builtin__"), - MovedModule("configparser", "ConfigParser"), - MovedModule("copyreg", "copy_reg"), - MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), - MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), - MovedModule("http_cookies", "Cookie", "http.cookies"), - MovedModule("html_entities", "htmlentitydefs", "html.entities"), - MovedModule("html_parser", "HTMLParser", "html.parser"), - MovedModule("http_client", "httplib", "http.client"), - MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), - MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), - MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), - MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), - MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), - MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), - MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), - MovedModule("cPickle", "cPickle", "pickle"), - MovedModule("queue", "Queue"), - MovedModule("reprlib", "repr"), - MovedModule("socketserver", "SocketServer"), - MovedModule("_thread", "thread", "_thread"), - MovedModule("tkinter", "Tkinter"), - MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), - MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), - MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), - MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), - MovedModule("tkinter_tix", "Tix", "tkinter.tix"), - MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), - MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), - MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), - MovedModule("tkinter_colorchooser", "tkColorChooser", - "tkinter.colorchooser"), - MovedModule("tkinter_commondialog", "tkCommonDialog", - "tkinter.commondialog"), - MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), - MovedModule("tkinter_font", "tkFont", "tkinter.font"), - MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), - MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", - "tkinter.simpledialog"), - MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), - MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), - MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), - MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), - MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), - MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), -] -# Add windows specific modules. -if sys.platform == "win32": - _moved_attributes += [ - MovedModule("winreg", "_winreg"), - ] - -for attr in _moved_attributes: - setattr(_MovedItems, attr.name, attr) - if isinstance(attr, MovedModule): - _importer._add_module(attr, "moves." + attr.name) -del attr - -_MovedItems._moved_attributes = _moved_attributes - -moves = _MovedItems(__name__ + ".moves") -_importer._add_module(moves, "moves") - - -class Module_six_moves_urllib_parse(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_parse""" - - -_urllib_parse_moved_attributes = [ - MovedAttribute("ParseResult", "urlparse", "urllib.parse"), - MovedAttribute("SplitResult", "urlparse", "urllib.parse"), - MovedAttribute("parse_qs", "urlparse", "urllib.parse"), - MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), - MovedAttribute("urldefrag", "urlparse", "urllib.parse"), - MovedAttribute("urljoin", "urlparse", "urllib.parse"), - MovedAttribute("urlparse", "urlparse", "urllib.parse"), - MovedAttribute("urlsplit", "urlparse", "urllib.parse"), - MovedAttribute("urlunparse", "urlparse", "urllib.parse"), - MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), - MovedAttribute("quote", "urllib", "urllib.parse"), - MovedAttribute("quote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote", "urllib", "urllib.parse"), - MovedAttribute("unquote_plus", "urllib", "urllib.parse"), - MovedAttribute("urlencode", "urllib", "urllib.parse"), - MovedAttribute("splitquery", "urllib", "urllib.parse"), - MovedAttribute("splittag", "urllib", "urllib.parse"), - MovedAttribute("splituser", "urllib", "urllib.parse"), - MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), - MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), - MovedAttribute("uses_params", "urlparse", "urllib.parse"), - MovedAttribute("uses_query", "urlparse", "urllib.parse"), - MovedAttribute("uses_relative", "urlparse", "urllib.parse"), -] -for attr in _urllib_parse_moved_attributes: - setattr(Module_six_moves_urllib_parse, attr.name, attr) -del attr - -Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes - -_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), - "moves.urllib_parse", "moves.urllib.parse") - - -class Module_six_moves_urllib_error(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_error""" - - -_urllib_error_moved_attributes = [ - MovedAttribute("URLError", "urllib2", "urllib.error"), - MovedAttribute("HTTPError", "urllib2", "urllib.error"), - MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), -] -for attr in _urllib_error_moved_attributes: - setattr(Module_six_moves_urllib_error, attr.name, attr) -del attr - -Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes - -_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), - "moves.urllib_error", "moves.urllib.error") - - -class Module_six_moves_urllib_request(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_request""" - - -_urllib_request_moved_attributes = [ - MovedAttribute("urlopen", "urllib2", "urllib.request"), - MovedAttribute("install_opener", "urllib2", "urllib.request"), - MovedAttribute("build_opener", "urllib2", "urllib.request"), - MovedAttribute("pathname2url", "urllib", "urllib.request"), - MovedAttribute("url2pathname", "urllib", "urllib.request"), - MovedAttribute("getproxies", "urllib", "urllib.request"), - MovedAttribute("Request", "urllib2", "urllib.request"), - MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), - MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), - MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), - MovedAttribute("BaseHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), - MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), - MovedAttribute("FileHandler", "urllib2", "urllib.request"), - MovedAttribute("FTPHandler", "urllib2", "urllib.request"), - MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), - MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), - MovedAttribute("urlretrieve", "urllib", "urllib.request"), - MovedAttribute("urlcleanup", "urllib", "urllib.request"), - MovedAttribute("URLopener", "urllib", "urllib.request"), - MovedAttribute("FancyURLopener", "urllib", "urllib.request"), - MovedAttribute("proxy_bypass", "urllib", "urllib.request"), -] -for attr in _urllib_request_moved_attributes: - setattr(Module_six_moves_urllib_request, attr.name, attr) -del attr - -Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes - -_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), - "moves.urllib_request", "moves.urllib.request") - - -class Module_six_moves_urllib_response(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_response""" - - -_urllib_response_moved_attributes = [ - MovedAttribute("addbase", "urllib", "urllib.response"), - MovedAttribute("addclosehook", "urllib", "urllib.response"), - MovedAttribute("addinfo", "urllib", "urllib.response"), - MovedAttribute("addinfourl", "urllib", "urllib.response"), -] -for attr in _urllib_response_moved_attributes: - setattr(Module_six_moves_urllib_response, attr.name, attr) -del attr - -Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes - -_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), - "moves.urllib_response", "moves.urllib.response") - - -class Module_six_moves_urllib_robotparser(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_robotparser""" - - -_urllib_robotparser_moved_attributes = [ - MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), -] -for attr in _urllib_robotparser_moved_attributes: - setattr(Module_six_moves_urllib_robotparser, attr.name, attr) -del attr - -Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes - -_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), - "moves.urllib_robotparser", "moves.urllib.robotparser") - - -class Module_six_moves_urllib(types.ModuleType): - - """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - __path__ = [] # mark as package - parse = _importer._get_module("moves.urllib_parse") - error = _importer._get_module("moves.urllib_error") - request = _importer._get_module("moves.urllib_request") - response = _importer._get_module("moves.urllib_response") - robotparser = _importer._get_module("moves.urllib_robotparser") - - def __dir__(self): - return ['parse', 'error', 'request', 'response', 'robotparser'] - -_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), - "moves.urllib") - - -def add_move(move): - """Add an item to six.moves.""" - setattr(_MovedItems, move.name, move) - - -def remove_move(name): - """Remove item from six.moves.""" - try: - delattr(_MovedItems, name) - except AttributeError: - try: - del moves.__dict__[name] - except KeyError: - raise AttributeError("no such move, %r" % (name,)) - - -if PY3: - _meth_func = "__func__" - _meth_self = "__self__" - - _func_closure = "__closure__" - _func_code = "__code__" - _func_defaults = "__defaults__" - _func_globals = "__globals__" -else: - _meth_func = "im_func" - _meth_self = "im_self" - - _func_closure = "func_closure" - _func_code = "func_code" - _func_defaults = "func_defaults" - _func_globals = "func_globals" - - -try: - advance_iterator = next -except NameError: - def advance_iterator(it): - return it.next() -next = advance_iterator - - -try: - callable = callable -except NameError: - def callable(obj): - return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - - -if PY3: - def get_unbound_function(unbound): - return unbound - - create_bound_method = types.MethodType - - def create_unbound_method(func, cls): - return func - - Iterator = object -else: - def get_unbound_function(unbound): - return unbound.im_func - - def create_bound_method(func, obj): - return types.MethodType(func, obj, obj.__class__) - - def create_unbound_method(func, cls): - return types.MethodType(func, None, cls) - - class Iterator(object): - - def next(self): - return type(self).__next__(self) - - callable = callable -_add_doc(get_unbound_function, - """Get the function out of a possibly unbound function""") - - -get_method_function = operator.attrgetter(_meth_func) -get_method_self = operator.attrgetter(_meth_self) -get_function_closure = operator.attrgetter(_func_closure) -get_function_code = operator.attrgetter(_func_code) -get_function_defaults = operator.attrgetter(_func_defaults) -get_function_globals = operator.attrgetter(_func_globals) - - -if PY3: - def iterkeys(d, **kw): - return iter(d.keys(**kw)) - - def itervalues(d, **kw): - return iter(d.values(**kw)) - - def iteritems(d, **kw): - return iter(d.items(**kw)) - - def iterlists(d, **kw): - return iter(d.lists(**kw)) - - viewkeys = operator.methodcaller("keys") - - viewvalues = operator.methodcaller("values") - - viewitems = operator.methodcaller("items") -else: - def iterkeys(d, **kw): - return d.iterkeys(**kw) - - def itervalues(d, **kw): - return d.itervalues(**kw) - - def iteritems(d, **kw): - return d.iteritems(**kw) - - def iterlists(d, **kw): - return d.iterlists(**kw) - - viewkeys = operator.methodcaller("viewkeys") - - viewvalues = operator.methodcaller("viewvalues") - - viewitems = operator.methodcaller("viewitems") - -_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") -_add_doc(itervalues, "Return an iterator over the values of a dictionary.") -_add_doc(iteritems, - "Return an iterator over the (key, value) pairs of a dictionary.") -_add_doc(iterlists, - "Return an iterator over the (key, [values]) pairs of a dictionary.") - - -if PY3: - def b(s): - return s.encode("latin-1") - - def u(s): - return s - unichr = chr - import struct - int2byte = struct.Struct(">B").pack - del struct - byte2int = operator.itemgetter(0) - indexbytes = operator.getitem - iterbytes = iter - import io - StringIO = io.StringIO - BytesIO = io.BytesIO - _assertCountEqual = "assertCountEqual" - if sys.version_info[1] <= 1: - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - else: - _assertRaisesRegex = "assertRaisesRegex" - _assertRegex = "assertRegex" -else: - def b(s): - return s - # Workaround for standalone backslash - - def u(s): - return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") - unichr = unichr - int2byte = chr - - def byte2int(bs): - return ord(bs[0]) - - def indexbytes(buf, i): - return ord(buf[i]) - iterbytes = functools.partial(itertools.imap, ord) - import StringIO - StringIO = BytesIO = StringIO.StringIO - _assertCountEqual = "assertItemsEqual" - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" -_add_doc(b, """Byte literal""") -_add_doc(u, """Text literal""") - - -def assertCountEqual(self, *args, **kwargs): - return getattr(self, _assertCountEqual)(*args, **kwargs) - - -def assertRaisesRegex(self, *args, **kwargs): - return getattr(self, _assertRaisesRegex)(*args, **kwargs) - - -def assertRegex(self, *args, **kwargs): - return getattr(self, _assertRegex)(*args, **kwargs) - - -if PY3: - exec_ = getattr(moves.builtins, "exec") - - def reraise(tp, value, tb=None): - if value is None: - value = tp() - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - -else: - def exec_(_code_, _globs_=None, _locs_=None): - """Execute code in a namespace.""" - if _globs_ is None: - frame = sys._getframe(1) - _globs_ = frame.f_globals - if _locs_ is None: - _locs_ = frame.f_locals - del frame - elif _locs_ is None: - _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") - - exec_("""def reraise(tp, value, tb=None): - raise tp, value, tb -""") - - -if sys.version_info[:2] == (3, 2): - exec_("""def raise_from(value, from_value): - if from_value is None: - raise value - raise value from from_value -""") -elif sys.version_info[:2] > (3, 2): - exec_("""def raise_from(value, from_value): - raise value from from_value -""") -else: - def raise_from(value, from_value): - raise value - - -print_ = getattr(moves.builtins, "print", None) -if print_ is None: - def print_(*args, **kwargs): - """The new-style print function for Python 2.4 and 2.5.""" - fp = kwargs.pop("file", sys.stdout) - if fp is None: - return - - def write(data): - if not isinstance(data, basestring): - data = str(data) - # If the file has an encoding, encode unicode with it. - if (isinstance(fp, file) and - isinstance(data, unicode) and - fp.encoding is not None): - errors = getattr(fp, "errors", None) - if errors is None: - errors = "strict" - data = data.encode(fp.encoding, errors) - fp.write(data) - want_unicode = False - sep = kwargs.pop("sep", None) - if sep is not None: - if isinstance(sep, unicode): - want_unicode = True - elif not isinstance(sep, str): - raise TypeError("sep must be None or a string") - end = kwargs.pop("end", None) - if end is not None: - if isinstance(end, unicode): - want_unicode = True - elif not isinstance(end, str): - raise TypeError("end must be None or a string") - if kwargs: - raise TypeError("invalid keyword arguments to print()") - if not want_unicode: - for arg in args: - if isinstance(arg, unicode): - want_unicode = True - break - if want_unicode: - newline = unicode("\n") - space = unicode(" ") - else: - newline = "\n" - space = " " - if sep is None: - sep = space - if end is None: - end = newline - for i, arg in enumerate(args): - if i: - write(sep) - write(arg) - write(end) -if sys.version_info[:2] < (3, 3): - _print = print_ - - def print_(*args, **kwargs): - fp = kwargs.get("file", sys.stdout) - flush = kwargs.pop("flush", False) - _print(*args, **kwargs) - if flush and fp is not None: - fp.flush() - -_add_doc(reraise, """Reraise an exception.""") - -if sys.version_info[0:2] < (3, 4): - def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - def wrapper(f): - f = functools.wraps(wrapped, assigned, updated)(f) - f.__wrapped__ = wrapped - return f - return wrapper -else: - wraps = functools.wraps - - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(meta): - - def __new__(cls, name, this_bases, d): - return meta(name, bases, d) - return type.__new__(metaclass, 'temporary_class', (), {}) - - -def add_metaclass(metaclass): - """Class decorator for creating a class with a metaclass.""" - def wrapper(cls): - orig_vars = cls.__dict__.copy() - slots = orig_vars.get('__slots__') - if slots is not None: - if isinstance(slots, str): - slots = [slots] - for slots_var in slots: - orig_vars.pop(slots_var) - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) - return metaclass(cls.__name__, cls.__bases__, orig_vars) - return wrapper - - -def python_2_unicode_compatible(klass): - """ - A decorator that defines __unicode__ and __str__ methods under Python 2. - Under Python 3 it does nothing. - - To support Python 2 and 3 with a single code base, define a __str__ method - returning text and apply this decorator to the class. - """ - if PY2: - if '__str__' not in klass.__dict__: - raise ValueError("@python_2_unicode_compatible cannot be applied " - "to %s because it doesn't define __str__()." % - klass.__name__) - klass.__unicode__ = klass.__str__ - klass.__str__ = lambda self: self.__unicode__().encode('utf-8') - return klass - - -# Complete the moves implementation. -# This code is at the end of this module to speed up module loading. -# Turn this module into a package. -__path__ = [] # required for PEP 302 and PEP 451 -__package__ = __name__ # see PEP 366 @ReservedAssignment -if globals().get("__spec__") is not None: - __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable -# Remove other six meta path importers, since they cause problems. This can -# happen if six is removed from sys.modules and then reloaded. (Setuptools does -# this for some reason.) -if sys.meta_path: - for i, importer in enumerate(sys.meta_path): - # Here's some real nastiness: Another "instance" of the six module might - # be floating around. Therefore, we can't use isinstance() to check for - # the six meta path importer, since the other six instance will have - # inserted an importer with different class. - if (type(importer).__name__ == "_SixMetaPathImporter" and - importer.name == __name__): - del sys.meta_path[i] - break - del i, importer -# Finally, add the importer to the meta path import hook. -sys.meta_path.append(_importer) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/archive_util.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/archive_util.py deleted file mode 100644 index 8143604..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/archive_util.py +++ /dev/null @@ -1,173 +0,0 @@ -"""Utilities for extracting common archive formats""" - -import zipfile -import tarfile -import os -import shutil -import posixpath -import contextlib -from distutils.errors import DistutilsError - -from pkg_resources import ensure_directory - -__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): - """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( - "Not a recognized archive type: %s" % filename - ) - - -def unpack_directory(filename, extract_dir, progress_filter=default_filter): - """"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("%s is not a directory" % filename) - - 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): - """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("%s is not a zip file" % (filename,)) - - with zipfile.ZipFile(filename) as z: - for info in z.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 = z.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 unpack_tarfile(filename, extract_dir, progress_filter=default_filter): - """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: - raise UnrecognizedFormat( - "%s is not a compressed or uncompressed tar file" % (filename,) - ) - with contextlib.closing(tarobj): - # don't do any chowning! - tarobj.chown = lambda *args: None - for member in tarobj: - name = member.name - # don't extract absolute paths or ones with .. in them - if not name.startswith('/') and '..' not in name.split('/'): - prelim_dst = os.path.join(extract_dir, *name.split('/')) - - # resolve any links and to extract the link targets as normal - # files - while member is not None and (member.islnk() or member.issym()): - linkpath = member.linkname - if member.issym(): - base = posixpath.dirname(member.name) - linkpath = posixpath.join(base, linkpath) - linkpath = posixpath.normpath(linkpath) - member = tarobj._getmember(linkpath) - - if member is not None and (member.isfile() or member.isdir()): - final_dst = progress_filter(name, prelim_dst) - if final_dst: - if final_dst.endswith(os.sep): - final_dst = final_dst[:-1] - 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/plotter-app/venv/lib/python3.8/site-packages/setuptools/build_meta.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/build_meta.py deleted file mode 100644 index 10c4b52..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/build_meta.py +++ /dev/null @@ -1,257 +0,0 @@ -"""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 -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. -""" - -import io -import os -import sys -import tokenize -import shutil -import contextlib - -import setuptools -import distutils -from setuptools.py31compat import TemporaryDirectory - -from pkg_resources import parse_requirements -from pkg_resources.py31compat import makedirs - -__all__ = ['get_requires_for_build_sdist', - 'get_requires_for_build_wheel', - 'prepare_metadata_for_build_wheel', - 'build_wheel', - 'build_sdist', - '__legacy__', - 'SetupRequirementsError'] - -class SetupRequirementsError(BaseException): - def __init__(self, specifiers): - self.specifiers = specifiers - - -class Distribution(setuptools.dist.Distribution): - def fetch_build_eggs(self, specifiers): - specifier_list = list(map(str, parse_requirements(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 - try: - yield - finally: - distutils.core.Distribution = orig - - -def _to_str(s): - """ - Convert a filename to a string (on Python 2, explicitly - a byte string, not Unicode) as distutils checks for the - exact type str. - """ - if sys.version_info[0] == 2 and not isinstance(s, str): - # Assume it's Unicode, as that's what the PEP says - # should be provided. - return s.encode(sys.getfilesystemencoding()) - return s - - -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, extension): - matching = ( - f for f in os.listdir(directory) - if f.endswith(extension) - ) - file, = matching - return file - - -def _open_setup_script(setup_script): - if not os.path.exists(setup_script): - # Supply a default setup.py - return io.StringIO(u"from setuptools import setup; setup()") - - return getattr(tokenize, 'open', open)(setup_script) - - -class _BuildMetaBackend(object): - - def _fix_config(self, config_settings): - config_settings = config_settings or {} - config_settings.setdefault('--global-option', []) - return config_settings - - def _get_build_requires(self, config_settings, requirements): - config_settings = self._fix_config(config_settings) - - sys.argv = sys.argv[:1] + ['egg_info'] + \ - config_settings["--global-option"] - try: - with Distribution.patch(): - self.run_setup() - except SetupRequirementsError as e: - requirements += e.specifiers - - return requirements - - def run_setup(self, setup_script='setup.py'): - # Note that we can reuse our build directory between calls - # Correctness comes first, then optimization later - __file__ = setup_script - __name__ = '__main__' - - with _open_setup_script(__file__) as f: - code = f.read().replace(r'\r\n', r'\n') - - exec(compile(code, __file__, 'exec'), locals()) - - def get_requires_for_build_wheel(self, config_settings=None): - config_settings = self._fix_config(config_settings) - return self._get_build_requires(config_settings, requirements=['wheel']) - - def get_requires_for_build_sdist(self, config_settings=None): - config_settings = self._fix_config(config_settings) - return self._get_build_requires(config_settings, requirements=[]) - - def prepare_metadata_for_build_wheel(self, metadata_directory, - config_settings=None): - sys.argv = sys.argv[:1] + ['dist_info', '--egg-base', - _to_str(metadata_directory)] - self.run_setup() - - dist_info_directory = metadata_directory - while True: - dist_infos = [f for f in os.listdir(dist_info_directory) - if f.endswith('.dist-info')] - - if (len(dist_infos) == 0 and - len(_get_immediate_subdirectories(dist_info_directory)) == 1): - - dist_info_directory = os.path.join( - dist_info_directory, os.listdir(dist_info_directory)[0]) - continue - - assert len(dist_infos) == 1 - break - - # PEP 517 requires that the .dist-info directory be placed in the - # metadata_directory. To comply, we MUST copy the directory to the root - if dist_info_directory != metadata_directory: - shutil.move( - os.path.join(dist_info_directory, dist_infos[0]), - metadata_directory) - shutil.rmtree(dist_info_directory, ignore_errors=True) - - return dist_infos[0] - - def _build_with_temp_dir(self, setup_command, result_extension, - result_directory, config_settings): - config_settings = self._fix_config(config_settings) - result_directory = os.path.abspath(result_directory) - - # Build in a temporary directory, then copy to the target. - makedirs(result_directory, exist_ok=True) - with TemporaryDirectory(dir=result_directory) as tmp_dist_dir: - sys.argv = (sys.argv[:1] + setup_command + - ['--dist-dir', tmp_dist_dir] + - config_settings["--global-option"]) - 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, config_settings=None, - metadata_directory=None): - return self._build_with_temp_dir(['bdist_wheel'], '.whl', - wheel_directory, config_settings) - - def build_sdist(self, sdist_directory, config_settings=None): - return self._build_with_temp_dir(['sdist', '--formats', 'gztar'], - '.tar.gz', sdist_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='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) - - try: - super(_BuildMetaLegacyBackend, - self).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 - -# 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 - - -# The legacy backend -__legacy__ = _BuildMetaLegacyBackend() diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/cli-32.exe b/plotter-app/venv/lib/python3.8/site-packages/setuptools/cli-32.exe deleted file mode 100644 index b1487b7..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/cli-32.exe and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/cli-64.exe b/plotter-app/venv/lib/python3.8/site-packages/setuptools/cli-64.exe deleted file mode 100644 index 675e6bf..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/cli-64.exe and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/cli.exe b/plotter-app/venv/lib/python3.8/site-packages/setuptools/cli.exe deleted file mode 100644 index b1487b7..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/cli.exe and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__init__.py deleted file mode 100644 index 743f558..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -__all__ = [ - 'alias', 'bdist_egg', 'bdist_rpm', 'build_ext', 'build_py', 'develop', - 'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts', - 'sdist', 'setopt', 'test', 'install_egg_info', 'install_scripts', - 'bdist_wininst', 'upload_docs', 'build_clib', 'dist_info', -] - -from distutils.command.bdist import bdist -import sys - -from setuptools.command import install_scripts - -if 'egg' not in bdist.format_commands: - bdist.format_command['egg'] = ('bdist_egg', "Python .egg file") - bdist.format_commands.append('egg') - -del bdist, sys diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index c622cb2..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/alias.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/alias.cpython-38.pyc deleted file mode 100644 index a33e8b5..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/alias.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_egg.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_egg.cpython-38.pyc deleted file mode 100644 index 2933066..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_egg.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_rpm.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_rpm.cpython-38.pyc deleted file mode 100644 index 3544061..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_rpm.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_wininst.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_wininst.cpython-38.pyc deleted file mode 100644 index a916f61..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_wininst.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/build_clib.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/build_clib.cpython-38.pyc deleted file mode 100644 index 967214c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/build_clib.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/build_ext.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/build_ext.cpython-38.pyc deleted file mode 100644 index 6bfbb03..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/build_ext.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/build_py.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/build_py.cpython-38.pyc deleted file mode 100644 index 17880db..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/build_py.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/develop.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/develop.cpython-38.pyc deleted file mode 100644 index 7c005e6..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/develop.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/dist_info.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/dist_info.cpython-38.pyc deleted file mode 100644 index 2d400d3..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/dist_info.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/easy_install.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/easy_install.cpython-38.pyc deleted file mode 100644 index 445e6cd..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/easy_install.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/egg_info.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/egg_info.cpython-38.pyc deleted file mode 100644 index 28ab9d8..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/egg_info.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install.cpython-38.pyc deleted file mode 100644 index 7af94ee..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install_egg_info.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install_egg_info.cpython-38.pyc deleted file mode 100644 index a71e0f6..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install_egg_info.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install_lib.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install_lib.cpython-38.pyc deleted file mode 100644 index 36f569d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install_lib.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install_scripts.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install_scripts.cpython-38.pyc deleted file mode 100644 index 98933b8..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install_scripts.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/py36compat.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/py36compat.cpython-38.pyc deleted file mode 100644 index ac878cf..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/py36compat.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/register.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/register.cpython-38.pyc deleted file mode 100644 index 8962620..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/register.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/rotate.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/rotate.cpython-38.pyc deleted file mode 100644 index 9471866..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/rotate.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/saveopts.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/saveopts.cpython-38.pyc deleted file mode 100644 index d893d97..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/saveopts.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/sdist.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/sdist.cpython-38.pyc deleted file mode 100644 index af88d33..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/sdist.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/setopt.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/setopt.cpython-38.pyc deleted file mode 100644 index 3bf634b..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/setopt.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/test.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/test.cpython-38.pyc deleted file mode 100644 index c7b28cf..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/test.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/upload.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/upload.cpython-38.pyc deleted file mode 100644 index 6fa3db3..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/upload.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/upload_docs.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/upload_docs.cpython-38.pyc deleted file mode 100644 index afbd250..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/upload_docs.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/alias.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/alias.py deleted file mode 100644 index 4532b1c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/alias.py +++ /dev/null @@ -1,80 +0,0 @@ -from distutils.errors import DistutilsOptionError - -from setuptools.extern.six.moves import map - -from setuptools.command.setopt import edit_config, option_base, config_file - - -def shquote(arg): - """Quote an argument for later parsing by shlex.split()""" - for c in '"', "'", "\\", "#": - if c in arg: - return repr(arg) - if arg.split() != [arg]: - return repr(arg) - return arg - - -class alias(option_base): - """Define a shortcut that invokes one or more commands""" - - description = "define a shortcut to invoke one or more commands" - command_consumes_arguments = True - - user_options = [ - ('remove', 'r', 'remove (unset) the alias'), - ] + option_base.user_options - - boolean_options = option_base.boolean_options + ['remove'] - - def initialize_options(self): - option_base.initialize_options(self) - self.args = None - self.remove = None - - def finalize_options(self): - option_base.finalize_options(self) - if self.remove and len(self.args) != 1: - raise DistutilsOptionError( - "Must specify exactly one argument (the alias name) when " - "using --remove" - ) - - def run(self): - aliases = self.distribution.get_option_dict('aliases') - - if not self.args: - print("Command Aliases") - print("---------------") - for alias in aliases: - print("setup.py alias", format_alias(alias, aliases)) - return - - elif len(self.args) == 1: - alias, = self.args - if self.remove: - command = None - elif alias in aliases: - print("setup.py alias", format_alias(alias, aliases)) - return - else: - print("No alias definition found for %r" % alias) - return - else: - alias = self.args[0] - command = ' '.join(map(shquote, self.args[1:])) - - edit_config(self.filename, {'aliases': {alias: command}}, self.dry_run) - - -def format_alias(name, aliases): - source, command = aliases[name] - if source == config_file('global'): - source = '--global-config ' - elif source == config_file('user'): - source = '--user-config ' - elif source == config_file('local'): - source = '' - else: - source = '--filename=%r' % source - return source + name + ' ' + command diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/bdist_egg.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/bdist_egg.py deleted file mode 100644 index 98470f1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/bdist_egg.py +++ /dev/null @@ -1,502 +0,0 @@ -"""setuptools.command.bdist_egg - -Build .egg distributions""" - -from distutils.errors import DistutilsSetupError -from distutils.dir_util import remove_tree, mkpath -from distutils import log -from types import CodeType -import sys -import os -import re -import textwrap -import marshal - -from setuptools.extern import six - -from pkg_resources import get_build_platform, Distribution, ensure_directory -from pkg_resources import EntryPoint -from setuptools.extension import Library -from setuptools import Command - -try: - # Python 2.7 or >=3.2 - from sysconfig import get_path, get_python_version - - def _get_purelib(): - return get_path("purelib") -except ImportError: - from distutils.sysconfig import get_python_lib, get_python_version - - def _get_purelib(): - return get_python_lib(False) - - -def strip_module(filename): - if '.' in filename: - filename = os.path.splitext(filename)[0] - if filename.endswith('module'): - filename = filename[:-6] - return filename - - -def sorted_walk(dir): - """Do os.walk in a reproducible way, - independent of indeterministic filesystem readdir order - """ - for base, dirs, files in os.walk(dir): - dirs.sort() - files.sort() - yield base, dirs, files - - -def write_stub(resource, pyfile): - _stub_template = textwrap.dedent(""" - def __bootstrap__(): - global __bootstrap__, __loader__, __file__ - import sys, pkg_resources, imp - __file__ = pkg_resources.resource_filename(__name__, %r) - __loader__ = None; del __bootstrap__, __loader__ - imp.load_dynamic(__name__,__file__) - __bootstrap__() - """).lstrip() - with open(pyfile, 'w') as f: - f.write(_stub_template % resource) - - -class bdist_egg(Command): - description = "create an \"egg\" distribution" - - user_options = [ - ('bdist-dir=', 'b', - "temporary directory for creating the distribution"), - ('plat-name=', 'p', "platform name to embed in generated filenames " - "(default: %s)" % get_build_platform()), - ('exclude-source-files', None, - "remove all .py files from the generated egg"), - ('keep-temp', 'k', - "keep the pseudo-installation tree around after " + - "creating the distribution archive"), - ('dist-dir=', 'd', - "directory to put final built distributions in"), - ('skip-build', None, - "skip rebuilding everything (for testing/debugging)"), - ] - - boolean_options = [ - 'keep-temp', 'skip-build', 'exclude-source-files' - ] - - def initialize_options(self): - self.bdist_dir = None - self.plat_name = None - self.keep_temp = 0 - self.dist_dir = None - self.skip_build = 0 - self.egg_output = None - self.exclude_source_files = None - - def finalize_options(self): - ei_cmd = self.ei_cmd = self.get_finalized_command("egg_info") - self.egg_info = ei_cmd.egg_info - - if self.bdist_dir is None: - bdist_base = self.get_finalized_command('bdist').bdist_base - self.bdist_dir = os.path.join(bdist_base, 'egg') - - if self.plat_name is None: - self.plat_name = get_build_platform() - - self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) - - if self.egg_output is None: - - # Compute filename of the output egg - basename = Distribution( - None, None, ei_cmd.egg_name, ei_cmd.egg_version, - get_python_version(), - self.distribution.has_ext_modules() and self.plat_name - ).egg_name() - - self.egg_output = os.path.join(self.dist_dir, basename + '.egg') - - def do_install_data(self): - # Hack for packages that install data to install's --install-lib - self.get_finalized_command('install').install_lib = self.bdist_dir - - site_packages = os.path.normcase(os.path.realpath(_get_purelib())) - old, self.distribution.data_files = self.distribution.data_files, [] - - for item in old: - if isinstance(item, tuple) and len(item) == 2: - if os.path.isabs(item[0]): - realpath = os.path.realpath(item[0]) - normalized = os.path.normcase(realpath) - if normalized == site_packages or normalized.startswith( - site_packages + os.sep - ): - item = realpath[len(site_packages) + 1:], item[1] - # XXX else: raise ??? - self.distribution.data_files.append(item) - - try: - log.info("installing package data to %s", self.bdist_dir) - self.call_command('install_data', force=0, root=None) - finally: - self.distribution.data_files = old - - def get_outputs(self): - return [self.egg_output] - - def call_command(self, cmdname, **kw): - """Invoke reinitialized command `cmdname` with keyword args""" - for dirname in INSTALL_DIRECTORY_ATTRS: - kw.setdefault(dirname, self.bdist_dir) - kw.setdefault('skip_build', self.skip_build) - kw.setdefault('dry_run', self.dry_run) - cmd = self.reinitialize_command(cmdname, **kw) - self.run_command(cmdname) - return cmd - - def run(self): - # Generate metadata first - self.run_command("egg_info") - # We run install_lib before install_data, because some data hacks - # pull their data path from the install_lib command. - log.info("installing library code to %s", self.bdist_dir) - instcmd = self.get_finalized_command('install') - old_root = instcmd.root - instcmd.root = None - if self.distribution.has_c_libraries() and not self.skip_build: - self.run_command('build_clib') - cmd = self.call_command('install_lib', warn_dir=0) - instcmd.root = old_root - - all_outputs, ext_outputs = self.get_ext_outputs() - self.stubs = [] - to_compile = [] - for (p, ext_name) in enumerate(ext_outputs): - filename, ext = os.path.splitext(ext_name) - pyfile = os.path.join(self.bdist_dir, strip_module(filename) + - '.py') - self.stubs.append(pyfile) - log.info("creating stub loader for %s", ext_name) - if not self.dry_run: - write_stub(os.path.basename(ext_name), pyfile) - to_compile.append(pyfile) - ext_outputs[p] = ext_name.replace(os.sep, '/') - - if to_compile: - cmd.byte_compile(to_compile) - if self.distribution.data_files: - self.do_install_data() - - # Make the EGG-INFO directory - archive_root = self.bdist_dir - egg_info = os.path.join(archive_root, 'EGG-INFO') - self.mkpath(egg_info) - if self.distribution.scripts: - script_dir = os.path.join(egg_info, 'scripts') - log.info("installing scripts to %s", script_dir) - self.call_command('install_scripts', install_dir=script_dir, - no_ep=1) - - self.copy_metadata_to(egg_info) - native_libs = os.path.join(egg_info, "native_libs.txt") - if all_outputs: - log.info("writing %s", native_libs) - if not self.dry_run: - ensure_directory(native_libs) - libs_file = open(native_libs, 'wt') - libs_file.write('\n'.join(all_outputs)) - libs_file.write('\n') - libs_file.close() - elif os.path.isfile(native_libs): - log.info("removing %s", native_libs) - if not self.dry_run: - os.unlink(native_libs) - - write_safety_flag( - os.path.join(archive_root, 'EGG-INFO'), self.zip_safe() - ) - - if os.path.exists(os.path.join(self.egg_info, 'depends.txt')): - log.warn( - "WARNING: 'depends.txt' will not be used by setuptools 0.6!\n" - "Use the install_requires/extras_require setup() args instead." - ) - - if self.exclude_source_files: - self.zap_pyfiles() - - # Make the archive - make_zipfile(self.egg_output, archive_root, verbose=self.verbose, - dry_run=self.dry_run, mode=self.gen_header()) - if not self.keep_temp: - remove_tree(self.bdist_dir, dry_run=self.dry_run) - - # Add to 'Distribution.dist_files' so that the "upload" command works - getattr(self.distribution, 'dist_files', []).append( - ('bdist_egg', get_python_version(), self.egg_output)) - - def zap_pyfiles(self): - log.info("Removing .py files from temporary directory") - for base, dirs, files in walk_egg(self.bdist_dir): - for name in files: - path = os.path.join(base, name) - - if name.endswith('.py'): - log.debug("Deleting %s", path) - os.unlink(path) - - if base.endswith('__pycache__'): - path_old = path - - pattern = r'(?P.+)\.(?P[^.]+)\.pyc' - m = re.match(pattern, name) - path_new = os.path.join( - base, os.pardir, m.group('name') + '.pyc') - log.info( - "Renaming file from [%s] to [%s]" - % (path_old, path_new)) - try: - os.remove(path_new) - except OSError: - pass - os.rename(path_old, path_new) - - def zip_safe(self): - safe = getattr(self.distribution, 'zip_safe', None) - if safe is not None: - return safe - log.warn("zip_safe flag not set; analyzing archive contents...") - return analyze_egg(self.bdist_dir, self.stubs) - - def gen_header(self): - epm = EntryPoint.parse_map(self.distribution.entry_points or '') - ep = epm.get('setuptools.installation', {}).get('eggsecutable') - if ep is None: - return 'w' # not an eggsecutable, do it the usual way. - - if not ep.attrs or ep.extras: - raise DistutilsSetupError( - "eggsecutable entry point (%r) cannot have 'extras' " - "or refer to a module" % (ep,) - ) - - pyver = '{}.{}'.format(*sys.version_info) - pkg = ep.module_name - full = '.'.join(ep.attrs) - base = ep.attrs[0] - basename = os.path.basename(self.egg_output) - - header = ( - "#!/bin/sh\n" - 'if [ `basename $0` = "%(basename)s" ]\n' - 'then exec python%(pyver)s -c "' - "import sys, os; sys.path.insert(0, os.path.abspath('$0')); " - "from %(pkg)s import %(base)s; sys.exit(%(full)s())" - '" "$@"\n' - 'else\n' - ' echo $0 is not the correct name for this egg file.\n' - ' echo Please rename it back to %(basename)s and try again.\n' - ' exec false\n' - 'fi\n' - ) % locals() - - if not self.dry_run: - mkpath(os.path.dirname(self.egg_output), dry_run=self.dry_run) - f = open(self.egg_output, 'w') - f.write(header) - f.close() - return 'a' - - def copy_metadata_to(self, target_dir): - "Copy metadata (egg info) to the target_dir" - # normalize the path (so that a forward-slash in egg_info will - # match using startswith below) - norm_egg_info = os.path.normpath(self.egg_info) - prefix = os.path.join(norm_egg_info, '') - for path in self.ei_cmd.filelist.files: - if path.startswith(prefix): - target = os.path.join(target_dir, path[len(prefix):]) - ensure_directory(target) - self.copy_file(path, target) - - def get_ext_outputs(self): - """Get a list of relative paths to C extensions in the output distro""" - - all_outputs = [] - ext_outputs = [] - - paths = {self.bdist_dir: ''} - for base, dirs, files in sorted_walk(self.bdist_dir): - for filename in files: - if os.path.splitext(filename)[1].lower() in NATIVE_EXTENSIONS: - all_outputs.append(paths[base] + filename) - for filename in dirs: - paths[os.path.join(base, filename)] = (paths[base] + - filename + '/') - - if self.distribution.has_ext_modules(): - build_cmd = self.get_finalized_command('build_ext') - for ext in build_cmd.extensions: - if isinstance(ext, Library): - continue - fullname = build_cmd.get_ext_fullname(ext.name) - filename = build_cmd.get_ext_filename(fullname) - if not os.path.basename(filename).startswith('dl-'): - if os.path.exists(os.path.join(self.bdist_dir, filename)): - ext_outputs.append(filename) - - return all_outputs, ext_outputs - - -NATIVE_EXTENSIONS = dict.fromkeys('.dll .so .dylib .pyd'.split()) - - -def walk_egg(egg_dir): - """Walk an unpacked egg's contents, skipping the metadata directory""" - walker = sorted_walk(egg_dir) - base, dirs, files = next(walker) - if 'EGG-INFO' in dirs: - dirs.remove('EGG-INFO') - yield base, dirs, files - for bdf in walker: - yield bdf - - -def analyze_egg(egg_dir, stubs): - # check for existing flag in EGG-INFO - for flag, fn in safety_flags.items(): - if os.path.exists(os.path.join(egg_dir, 'EGG-INFO', fn)): - return flag - if not can_scan(): - return False - safe = True - for base, dirs, files in walk_egg(egg_dir): - for name in files: - if name.endswith('.py') or name.endswith('.pyw'): - continue - elif name.endswith('.pyc') or name.endswith('.pyo'): - # always scan, even if we already know we're not safe - safe = scan_module(egg_dir, base, name, stubs) and safe - return safe - - -def write_safety_flag(egg_dir, safe): - # Write or remove zip safety flag file(s) - for flag, fn in safety_flags.items(): - fn = os.path.join(egg_dir, fn) - if os.path.exists(fn): - if safe is None or bool(safe) != flag: - os.unlink(fn) - elif safe is not None and bool(safe) == flag: - f = open(fn, 'wt') - f.write('\n') - f.close() - - -safety_flags = { - True: 'zip-safe', - False: 'not-zip-safe', -} - - -def scan_module(egg_dir, base, name, stubs): - """Check whether module possibly uses unsafe-for-zipfile stuff""" - - filename = os.path.join(base, name) - if filename[:-1] in stubs: - return True # Extension module - pkg = base[len(egg_dir) + 1:].replace(os.sep, '.') - module = pkg + (pkg and '.' or '') + os.path.splitext(name)[0] - if six.PY2: - skip = 8 # skip magic & date - elif sys.version_info < (3, 7): - skip = 12 # skip magic & date & file size - else: - skip = 16 # skip magic & reserved? & date & file size - f = open(filename, 'rb') - f.read(skip) - code = marshal.load(f) - f.close() - safe = True - symbols = dict.fromkeys(iter_symbols(code)) - for bad in ['__file__', '__path__']: - if bad in symbols: - log.warn("%s: module references %s", module, bad) - safe = False - if 'inspect' in symbols: - for bad in [ - 'getsource', 'getabsfile', 'getsourcefile', 'getfile' - 'getsourcelines', 'findsource', 'getcomments', 'getframeinfo', - 'getinnerframes', 'getouterframes', 'stack', 'trace' - ]: - if bad in symbols: - log.warn("%s: module MAY be using inspect.%s", module, bad) - safe = False - return safe - - -def iter_symbols(code): - """Yield names and strings used by `code` and its nested code objects""" - for name in code.co_names: - yield name - for const in code.co_consts: - if isinstance(const, six.string_types): - yield const - elif isinstance(const, CodeType): - for name in iter_symbols(const): - yield name - - -def can_scan(): - if not sys.platform.startswith('java') and sys.platform != 'cli': - # CPython, PyPy, etc. - return True - log.warn("Unable to analyze compiled code on this platform.") - log.warn("Please ask the author to include a 'zip_safe'" - " setting (either True or False) in the package's setup.py") - - -# Attribute names of options for commands that might need to be convinced to -# install to the egg build directory - -INSTALL_DIRECTORY_ATTRS = [ - 'install_lib', 'install_dir', 'install_data', 'install_base' -] - - -def make_zipfile(zip_filename, base_dir, verbose=0, dry_run=0, compress=True, - mode='w'): - """Create a zip file from all the files under 'base_dir'. The output - zip file will be named 'base_dir' + ".zip". Uses either the "zipfile" - Python module (if available) or the InfoZIP "zip" utility (if installed - and found on the default search path). If neither tool is available, - raises DistutilsExecError. Returns the name of the output zip file. - """ - import zipfile - - mkpath(os.path.dirname(zip_filename), dry_run=dry_run) - log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir) - - def visit(z, dirname, names): - for name in names: - path = os.path.normpath(os.path.join(dirname, name)) - if os.path.isfile(path): - p = path[len(base_dir) + 1:] - if not dry_run: - z.write(path, p) - log.debug("adding '%s'", p) - - compression = zipfile.ZIP_DEFLATED if compress else zipfile.ZIP_STORED - if not dry_run: - z = zipfile.ZipFile(zip_filename, mode, compression=compression) - for dirname, dirs, files in sorted_walk(base_dir): - visit(z, dirname, files) - z.close() - else: - for dirname, dirs, files in sorted_walk(base_dir): - visit(None, dirname, files) - return zip_filename diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/bdist_rpm.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/bdist_rpm.py deleted file mode 100644 index 7073092..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/bdist_rpm.py +++ /dev/null @@ -1,43 +0,0 @@ -import distutils.command.bdist_rpm as orig - - -class bdist_rpm(orig.bdist_rpm): - """ - Override the default bdist_rpm behavior to do the following: - - 1. Run egg_info to ensure the name and version are properly calculated. - 2. Always run 'install' using --single-version-externally-managed to - disable eggs in RPM distributions. - 3. Replace dash with underscore in the version numbers for better RPM - compatibility. - """ - - def run(self): - # ensure distro name is up-to-date - self.run_command('egg_info') - - orig.bdist_rpm.run(self) - - def _make_spec_file(self): - version = self.distribution.get_version() - rpmversion = version.replace('-', '_') - spec = orig.bdist_rpm._make_spec_file(self) - line23 = '%define version ' + version - line24 = '%define version ' + rpmversion - spec = [ - line.replace( - "Source0: %{name}-%{version}.tar", - "Source0: %{name}-%{unmangled_version}.tar" - ).replace( - "setup.py install ", - "setup.py install --single-version-externally-managed " - ).replace( - "%setup", - "%setup -n %{name}-%{unmangled_version}" - ).replace(line23, line24) - for line in spec - ] - insert_loc = spec.index(line24) + 1 - unmangled_version = "%define unmangled_version " + version - spec.insert(insert_loc, unmangled_version) - return spec diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/bdist_wininst.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/bdist_wininst.py deleted file mode 100644 index 073de97..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/bdist_wininst.py +++ /dev/null @@ -1,21 +0,0 @@ -import distutils.command.bdist_wininst as orig - - -class bdist_wininst(orig.bdist_wininst): - def reinitialize_command(self, command, reinit_subcommands=0): - """ - Supplement reinitialize_command to work around - http://bugs.python.org/issue20819 - """ - cmd = self.distribution.reinitialize_command( - command, reinit_subcommands) - if command in ('install', 'install_lib'): - cmd.install_lib = None - return cmd - - def run(self): - self._is_running = True - try: - orig.bdist_wininst.run(self) - finally: - self._is_running = False diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/build_clib.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/build_clib.py deleted file mode 100644 index 09caff6..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/build_clib.py +++ /dev/null @@ -1,98 +0,0 @@ -import distutils.command.build_clib as orig -from distutils.errors import DistutilsSetupError -from distutils import log -from setuptools.dep_util import newer_pairwise_group - - -class build_clib(orig.build_clib): - """ - Override the default build_clib behaviour to do the following: - - 1. Implement a rudimentary timestamp-based dependency system - so 'compile()' doesn't run every time. - 2. Add more keys to the 'build_info' dictionary: - * obj_deps - specify dependencies for each object compiled. - this should be a dictionary mapping a key - with the source filename to a list of - dependencies. Use an empty string for global - dependencies. - * cflags - specify a list of additional flags to pass to - the compiler. - """ - - def build_libraries(self, libraries): - for (lib_name, build_info) in libraries: - sources = build_info.get('sources') - if sources is None or not isinstance(sources, (list, tuple)): - raise DistutilsSetupError( - "in 'libraries' option (library '%s'), " - "'sources' must be present and must be " - "a list of source filenames" % lib_name) - sources = list(sources) - - log.info("building '%s' library", lib_name) - - # Make sure everything is the correct type. - # obj_deps should be a dictionary of keys as sources - # and a list/tuple of files that are its dependencies. - obj_deps = build_info.get('obj_deps', dict()) - if not isinstance(obj_deps, dict): - raise DistutilsSetupError( - "in 'libraries' option (library '%s'), " - "'obj_deps' must be a dictionary of " - "type 'source: list'" % lib_name) - dependencies = [] - - # Get the global dependencies that are specified by the '' key. - # These will go into every source's dependency list. - global_deps = obj_deps.get('', list()) - if not isinstance(global_deps, (list, tuple)): - raise DistutilsSetupError( - "in 'libraries' option (library '%s'), " - "'obj_deps' must be a dictionary of " - "type 'source: list'" % lib_name) - - # Build the list to be used by newer_pairwise_group - # each source will be auto-added to its dependencies. - for source in sources: - src_deps = [source] - src_deps.extend(global_deps) - extra_deps = obj_deps.get(source, list()) - if not isinstance(extra_deps, (list, tuple)): - raise DistutilsSetupError( - "in 'libraries' option (library '%s'), " - "'obj_deps' must be a dictionary of " - "type 'source: list'" % lib_name) - src_deps.extend(extra_deps) - dependencies.append(src_deps) - - expected_objects = self.compiler.object_filenames( - sources, - output_dir=self.build_temp - ) - - if newer_pairwise_group(dependencies, expected_objects) != ([], []): - # First, compile the source code to object files in the library - # directory. (This should probably change to putting object - # files in a temporary build directory.) - macros = build_info.get('macros') - include_dirs = build_info.get('include_dirs') - cflags = build_info.get('cflags') - objects = self.compiler.compile( - sources, - output_dir=self.build_temp, - macros=macros, - include_dirs=include_dirs, - extra_postargs=cflags, - debug=self.debug - ) - - # Now "link" the object files together into a static library. - # (On Unix at least, this isn't really linking -- it just - # builds an archive. Whatever.) - self.compiler.create_static_lib( - expected_objects, - lib_name, - output_dir=self.build_clib, - debug=self.debug - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/build_ext.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/build_ext.py deleted file mode 100644 index daa8e4f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/build_ext.py +++ /dev/null @@ -1,327 +0,0 @@ -import os -import sys -import itertools -from distutils.command.build_ext import build_ext as _du_build_ext -from distutils.file_util import copy_file -from distutils.ccompiler import new_compiler -from distutils.sysconfig import customize_compiler, get_config_var -from distutils.errors import DistutilsError -from distutils import log - -from setuptools.extension import Library -from setuptools.extern import six - -if six.PY2: - import imp - - EXTENSION_SUFFIXES = [s for s, _, tp in imp.get_suffixes() if tp == imp.C_EXTENSION] -else: - from importlib.machinery import EXTENSION_SUFFIXES - -try: - # Attempt to use Cython for building extensions, if available - from Cython.Distutils.build_ext import build_ext as _build_ext - # Additionally, assert that the compiler module will load - # also. Ref #1229. - __import__('Cython.Compiler.Main') -except ImportError: - _build_ext = _du_build_ext - -# make sure _config_vars is initialized -get_config_var("LDSHARED") -from distutils.sysconfig import _config_vars as _CONFIG_VARS - - -def _customize_compiler_for_shlib(compiler): - if sys.platform == "darwin": - # building .dylib requires additional compiler flags on OSX; here we - # temporarily substitute the pyconfig.h variables so that distutils' - # 'customize_compiler' uses them before we build the shared libraries. - tmp = _CONFIG_VARS.copy() - try: - # XXX Help! I don't have any idea whether these are right... - _CONFIG_VARS['LDSHARED'] = ( - "gcc -Wl,-x -dynamiclib -undefined dynamic_lookup") - _CONFIG_VARS['CCSHARED'] = " -dynamiclib" - _CONFIG_VARS['SO'] = ".dylib" - customize_compiler(compiler) - finally: - _CONFIG_VARS.clear() - _CONFIG_VARS.update(tmp) - else: - customize_compiler(compiler) - - -have_rtld = False -use_stubs = False -libtype = 'shared' - -if sys.platform == "darwin": - use_stubs = True -elif os.name != 'nt': - try: - import dl - use_stubs = have_rtld = hasattr(dl, 'RTLD_NOW') - except ImportError: - pass - -if_dl = lambda s: s if have_rtld else '' - - -def get_abi3_suffix(): - """Return the file extension for an abi3-compliant Extension()""" - for suffix in EXTENSION_SUFFIXES: - if '.abi3' in suffix: # Unix - return suffix - elif suffix == '.pyd': # Windows - return suffix - - -class build_ext(_build_ext): - def run(self): - """Build extensions in build directory, then copy if --inplace""" - old_inplace, self.inplace = self.inplace, 0 - _build_ext.run(self) - self.inplace = old_inplace - if old_inplace: - self.copy_extensions_to_source() - - def copy_extensions_to_source(self): - build_py = self.get_finalized_command('build_py') - for ext in self.extensions: - fullname = self.get_ext_fullname(ext.name) - filename = self.get_ext_filename(fullname) - modpath = fullname.split('.') - package = '.'.join(modpath[:-1]) - package_dir = build_py.get_package_dir(package) - dest_filename = os.path.join(package_dir, - os.path.basename(filename)) - src_filename = os.path.join(self.build_lib, filename) - - # Always copy, even if source is older than destination, to ensure - # that the right extensions for the current Python/platform are - # used. - copy_file( - src_filename, dest_filename, verbose=self.verbose, - dry_run=self.dry_run - ) - if ext._needs_stub: - self.write_stub(package_dir or os.curdir, ext, True) - - def get_ext_filename(self, fullname): - filename = _build_ext.get_ext_filename(self, fullname) - if fullname in self.ext_map: - ext = self.ext_map[fullname] - use_abi3 = ( - six.PY3 - and getattr(ext, 'py_limited_api') - and get_abi3_suffix() - ) - if use_abi3: - so_ext = get_config_var('EXT_SUFFIX') - filename = filename[:-len(so_ext)] - filename = filename + get_abi3_suffix() - if isinstance(ext, Library): - fn, ext = os.path.splitext(filename) - return self.shlib_compiler.library_filename(fn, libtype) - elif use_stubs and ext._links_to_dynamic: - d, fn = os.path.split(filename) - return os.path.join(d, 'dl-' + fn) - return filename - - def initialize_options(self): - _build_ext.initialize_options(self) - self.shlib_compiler = None - self.shlibs = [] - self.ext_map = {} - - def finalize_options(self): - _build_ext.finalize_options(self) - self.extensions = self.extensions or [] - self.check_extensions_list(self.extensions) - self.shlibs = [ext for ext in self.extensions - if isinstance(ext, Library)] - if self.shlibs: - self.setup_shlib_compiler() - for ext in self.extensions: - ext._full_name = self.get_ext_fullname(ext.name) - for ext in self.extensions: - fullname = ext._full_name - self.ext_map[fullname] = ext - - # distutils 3.1 will also ask for module names - # XXX what to do with conflicts? - self.ext_map[fullname.split('.')[-1]] = ext - - ltd = self.shlibs and self.links_to_dynamic(ext) or False - ns = ltd and use_stubs and not isinstance(ext, Library) - ext._links_to_dynamic = ltd - ext._needs_stub = ns - filename = ext._file_name = self.get_ext_filename(fullname) - libdir = os.path.dirname(os.path.join(self.build_lib, filename)) - if ltd and libdir not in ext.library_dirs: - ext.library_dirs.append(libdir) - if ltd and use_stubs and os.curdir not in ext.runtime_library_dirs: - ext.runtime_library_dirs.append(os.curdir) - - def setup_shlib_compiler(self): - compiler = self.shlib_compiler = new_compiler( - compiler=self.compiler, dry_run=self.dry_run, force=self.force - ) - _customize_compiler_for_shlib(compiler) - - if self.include_dirs is not None: - compiler.set_include_dirs(self.include_dirs) - if self.define is not None: - # 'define' option is a list of (name,value) tuples - for (name, value) in self.define: - compiler.define_macro(name, value) - if self.undef is not None: - for macro in self.undef: - compiler.undefine_macro(macro) - if self.libraries is not None: - compiler.set_libraries(self.libraries) - if self.library_dirs is not None: - compiler.set_library_dirs(self.library_dirs) - if self.rpath is not None: - compiler.set_runtime_library_dirs(self.rpath) - if self.link_objects is not None: - compiler.set_link_objects(self.link_objects) - - # hack so distutils' build_extension() builds a library instead - compiler.link_shared_object = link_shared_object.__get__(compiler) - - def get_export_symbols(self, ext): - if isinstance(ext, Library): - return ext.export_symbols - return _build_ext.get_export_symbols(self, ext) - - def build_extension(self, ext): - ext._convert_pyx_sources_to_lang() - _compiler = self.compiler - try: - if isinstance(ext, Library): - self.compiler = self.shlib_compiler - _build_ext.build_extension(self, ext) - if ext._needs_stub: - cmd = self.get_finalized_command('build_py').build_lib - self.write_stub(cmd, ext) - finally: - self.compiler = _compiler - - def links_to_dynamic(self, ext): - """Return true if 'ext' links to a dynamic lib in the same package""" - # XXX this should check to ensure the lib is actually being built - # XXX as dynamic, and not just using a locally-found version or a - # XXX static-compiled version - libnames = dict.fromkeys([lib._full_name for lib in self.shlibs]) - pkg = '.'.join(ext._full_name.split('.')[:-1] + ['']) - return any(pkg + libname in libnames for libname in ext.libraries) - - def get_outputs(self): - return _build_ext.get_outputs(self) + self.__get_stubs_outputs() - - def __get_stubs_outputs(self): - # assemble the base name for each extension that needs a stub - ns_ext_bases = ( - os.path.join(self.build_lib, *ext._full_name.split('.')) - for ext in self.extensions - if ext._needs_stub - ) - # pair each base with the extension - pairs = itertools.product(ns_ext_bases, self.__get_output_extensions()) - return list(base + fnext for base, fnext in pairs) - - def __get_output_extensions(self): - yield '.py' - yield '.pyc' - if self.get_finalized_command('build_py').optimize: - yield '.pyo' - - def write_stub(self, output_dir, ext, compile=False): - log.info("writing stub loader for %s to %s", ext._full_name, - output_dir) - stub_file = (os.path.join(output_dir, *ext._full_name.split('.')) + - '.py') - if compile and os.path.exists(stub_file): - raise DistutilsError(stub_file + " already exists! Please delete.") - if not self.dry_run: - f = open(stub_file, 'w') - f.write( - '\n'.join([ - "def __bootstrap__():", - " global __bootstrap__, __file__, __loader__", - " import sys, os, pkg_resources, imp" + if_dl(", dl"), - " __file__ = pkg_resources.resource_filename" - "(__name__,%r)" - % os.path.basename(ext._file_name), - " del __bootstrap__", - " if '__loader__' in globals():", - " del __loader__", - if_dl(" old_flags = sys.getdlopenflags()"), - " old_dir = os.getcwd()", - " try:", - " os.chdir(os.path.dirname(__file__))", - if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"), - " imp.load_dynamic(__name__,__file__)", - " finally:", - if_dl(" sys.setdlopenflags(old_flags)"), - " os.chdir(old_dir)", - "__bootstrap__()", - "" # terminal \n - ]) - ) - f.close() - if compile: - from distutils.util import byte_compile - - byte_compile([stub_file], optimize=0, - force=True, dry_run=self.dry_run) - optimize = self.get_finalized_command('install_lib').optimize - if optimize > 0: - byte_compile([stub_file], optimize=optimize, - force=True, dry_run=self.dry_run) - if os.path.exists(stub_file) and not self.dry_run: - os.unlink(stub_file) - - -if use_stubs or os.name == 'nt': - # Build shared libraries - # - def link_shared_object( - self, objects, output_libname, output_dir=None, libraries=None, - library_dirs=None, runtime_library_dirs=None, export_symbols=None, - debug=0, extra_preargs=None, extra_postargs=None, build_temp=None, - target_lang=None): - self.link( - self.SHARED_LIBRARY, objects, output_libname, - output_dir, libraries, library_dirs, runtime_library_dirs, - export_symbols, debug, extra_preargs, extra_postargs, - build_temp, target_lang - ) -else: - # Build static libraries everywhere else - libtype = 'static' - - def link_shared_object( - self, objects, output_libname, output_dir=None, libraries=None, - library_dirs=None, runtime_library_dirs=None, export_symbols=None, - debug=0, extra_preargs=None, extra_postargs=None, build_temp=None, - target_lang=None): - # XXX we need to either disallow these attrs on Library instances, - # or warn/abort here if set, or something... - # libraries=None, library_dirs=None, runtime_library_dirs=None, - # export_symbols=None, extra_preargs=None, extra_postargs=None, - # build_temp=None - - assert output_dir is None # distutils build_ext doesn't pass this - output_dir, filename = os.path.split(output_libname) - basename, ext = os.path.splitext(filename) - if self.library_filename("x").startswith('lib'): - # strip 'lib' prefix; this is kludgy if some platform uses - # a different prefix - basename = basename[3:] - - self.create_static_lib( - objects, basename, output_dir, debug, target_lang - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/build_py.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/build_py.py deleted file mode 100644 index b0314fd..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/build_py.py +++ /dev/null @@ -1,270 +0,0 @@ -from glob import glob -from distutils.util import convert_path -import distutils.command.build_py as orig -import os -import fnmatch -import textwrap -import io -import distutils.errors -import itertools - -from setuptools.extern import six -from setuptools.extern.six.moves import map, filter, filterfalse - -try: - from setuptools.lib2to3_ex import Mixin2to3 -except ImportError: - - class Mixin2to3: - def run_2to3(self, files, doctests=True): - "do nothing" - - -class build_py(orig.build_py, Mixin2to3): - """Enhanced 'build_py' command that includes data files with packages - - The data files are specified via a 'package_data' argument to 'setup()'. - See 'setuptools.dist.Distribution' for more details. - - Also, this version of the 'build_py' command allows you to specify both - 'py_modules' and 'packages' in the same setup operation. - """ - - def finalize_options(self): - orig.build_py.finalize_options(self) - self.package_data = self.distribution.package_data - self.exclude_package_data = (self.distribution.exclude_package_data or - {}) - if 'data_files' in self.__dict__: - del self.__dict__['data_files'] - self.__updated_files = [] - self.__doctests_2to3 = [] - - def run(self): - """Build modules, packages, and copy data files to build directory""" - if not self.py_modules and not self.packages: - return - - if self.py_modules: - self.build_modules() - - if self.packages: - self.build_packages() - self.build_package_data() - - self.run_2to3(self.__updated_files, False) - self.run_2to3(self.__updated_files, True) - self.run_2to3(self.__doctests_2to3, True) - - # Only compile actual .py files, using our base class' idea of what our - # output files are. - self.byte_compile(orig.build_py.get_outputs(self, include_bytecode=0)) - - def __getattr__(self, attr): - "lazily compute data files" - if attr == 'data_files': - self.data_files = self._get_data_files() - return self.data_files - return orig.build_py.__getattr__(self, attr) - - def build_module(self, module, module_file, package): - if six.PY2 and isinstance(package, six.string_types): - # avoid errors on Python 2 when unicode is passed (#190) - package = package.split('.') - outfile, copied = orig.build_py.build_module(self, module, module_file, - package) - if copied: - self.__updated_files.append(outfile) - return outfile, copied - - def _get_data_files(self): - """Generate list of '(package,src_dir,build_dir,filenames)' tuples""" - self.analyze_manifest() - return list(map(self._get_pkg_data_files, self.packages or ())) - - def _get_pkg_data_files(self, package): - # Locate package source directory - src_dir = self.get_package_dir(package) - - # Compute package build directory - build_dir = os.path.join(*([self.build_lib] + package.split('.'))) - - # Strip directory from globbed filenames - filenames = [ - os.path.relpath(file, src_dir) - for file in self.find_data_files(package, src_dir) - ] - return package, src_dir, build_dir, filenames - - def find_data_files(self, package, src_dir): - """Return filenames for package's data files in 'src_dir'""" - patterns = self._get_platform_patterns( - self.package_data, - package, - src_dir, - ) - globs_expanded = map(glob, patterns) - # flatten the expanded globs into an iterable of matches - globs_matches = itertools.chain.from_iterable(globs_expanded) - glob_files = filter(os.path.isfile, globs_matches) - files = itertools.chain( - self.manifest_files.get(package, []), - glob_files, - ) - return self.exclude_data_files(package, src_dir, files) - - def build_package_data(self): - """Copy data files into build directory""" - for package, src_dir, build_dir, filenames in self.data_files: - for filename in filenames: - target = os.path.join(build_dir, filename) - self.mkpath(os.path.dirname(target)) - srcfile = os.path.join(src_dir, filename) - outf, copied = self.copy_file(srcfile, target) - srcfile = os.path.abspath(srcfile) - if (copied and - srcfile in self.distribution.convert_2to3_doctests): - self.__doctests_2to3.append(outf) - - def analyze_manifest(self): - self.manifest_files = mf = {} - if not self.distribution.include_package_data: - return - src_dirs = {} - for package in self.packages or (): - # Locate package source directory - src_dirs[assert_relative(self.get_package_dir(package))] = package - - self.run_command('egg_info') - ei_cmd = self.get_finalized_command('egg_info') - for path in ei_cmd.filelist.files: - d, f = os.path.split(assert_relative(path)) - prev = None - oldf = f - while d and d != prev and d not in src_dirs: - prev = d - d, df = os.path.split(d) - f = os.path.join(df, f) - if d in src_dirs: - if path.endswith('.py') and f == oldf: - continue # it's a module, not data - mf.setdefault(src_dirs[d], []).append(path) - - def get_data_files(self): - pass # Lazily compute data files in _get_data_files() function. - - def check_package(self, package, package_dir): - """Check namespace packages' __init__ for declare_namespace""" - try: - return self.packages_checked[package] - except KeyError: - pass - - init_py = orig.build_py.check_package(self, package, package_dir) - self.packages_checked[package] = init_py - - if not init_py or not self.distribution.namespace_packages: - return init_py - - for pkg in self.distribution.namespace_packages: - if pkg == package or pkg.startswith(package + '.'): - break - else: - return init_py - - with io.open(init_py, 'rb') as f: - contents = f.read() - if b'declare_namespace' not in contents: - raise distutils.errors.DistutilsError( - "Namespace package problem: %s is a namespace package, but " - "its\n__init__.py does not call declare_namespace()! Please " - 'fix it.\n(See the setuptools manual under ' - '"Namespace Packages" for details.)\n"' % (package,) - ) - return init_py - - def initialize_options(self): - self.packages_checked = {} - orig.build_py.initialize_options(self) - - def get_package_dir(self, package): - res = orig.build_py.get_package_dir(self, package) - if self.distribution.src_root is not None: - return os.path.join(self.distribution.src_root, res) - return res - - def exclude_data_files(self, package, src_dir, files): - """Filter filenames for package's data files in 'src_dir'""" - files = list(files) - patterns = self._get_platform_patterns( - self.exclude_package_data, - package, - src_dir, - ) - match_groups = ( - fnmatch.filter(files, pattern) - for pattern in patterns - ) - # flatten the groups of matches into an iterable of matches - matches = itertools.chain.from_iterable(match_groups) - bad = set(matches) - keepers = ( - fn - for fn in files - if fn not in bad - ) - # ditch dupes - return list(_unique_everseen(keepers)) - - @staticmethod - def _get_platform_patterns(spec, package, src_dir): - """ - yield platform-specific path patterns (suitable for glob - or fn_match) from a glob-based spec (such as - self.package_data or self.exclude_package_data) - matching package in src_dir. - """ - raw_patterns = itertools.chain( - spec.get('', []), - spec.get(package, []), - ) - return ( - # Each pattern has to be converted to a platform-specific path - os.path.join(src_dir, convert_path(pattern)) - for pattern in raw_patterns - ) - - -# from Python docs -def _unique_everseen(iterable, key=None): - "List unique elements, preserving order. Remember all elements ever seen." - # unique_everseen('AAAABBBCCDAABBB') --> A B C D - # unique_everseen('ABBCcAD', str.lower) --> A B C D - seen = set() - seen_add = seen.add - if key is None: - for element in filterfalse(seen.__contains__, iterable): - seen_add(element) - yield element - else: - for element in iterable: - k = key(element) - if k not in seen: - seen_add(k) - yield element - - -def assert_relative(path): - if not os.path.isabs(path): - return path - from distutils.errors import DistutilsSetupError - - msg = textwrap.dedent(""" - Error: setup script specifies an absolute path: - - %s - - setup() arguments must *always* be /-separated paths relative to the - setup.py directory, *never* absolute paths. - """).lstrip() % path - raise DistutilsSetupError(msg) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/develop.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/develop.py deleted file mode 100644 index 009e4f9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/develop.py +++ /dev/null @@ -1,221 +0,0 @@ -from distutils.util import convert_path -from distutils import log -from distutils.errors import DistutilsError, DistutilsOptionError -import os -import glob -import io - -from setuptools.extern import six - -import pkg_resources -from setuptools.command.easy_install import easy_install -from setuptools import namespaces -import setuptools - -__metaclass__ = type - - -class develop(namespaces.DevelopInstaller, easy_install): - """Set up package for development""" - - description = "install package in 'development mode'" - - user_options = easy_install.user_options + [ - ("uninstall", "u", "Uninstall this source package"), - ("egg-path=", None, "Set the path to be used in the .egg-link file"), - ] - - boolean_options = easy_install.boolean_options + ['uninstall'] - - command_consumes_arguments = False # override base - - def run(self): - if self.uninstall: - self.multi_version = True - self.uninstall_link() - self.uninstall_namespaces() - else: - self.install_for_development() - self.warn_deprecated_options() - - def initialize_options(self): - self.uninstall = None - self.egg_path = None - easy_install.initialize_options(self) - self.setup_path = None - self.always_copy_from = '.' # always copy eggs installed in curdir - - def finalize_options(self): - ei = self.get_finalized_command("egg_info") - if ei.broken_egg_info: - template = "Please rename %r to %r before using 'develop'" - args = ei.egg_info, ei.broken_egg_info - raise DistutilsError(template % args) - self.args = [ei.egg_name] - - easy_install.finalize_options(self) - self.expand_basedirs() - self.expand_dirs() - # pick up setup-dir .egg files only: no .egg-info - self.package_index.scan(glob.glob('*.egg')) - - egg_link_fn = ei.egg_name + '.egg-link' - self.egg_link = os.path.join(self.install_dir, egg_link_fn) - self.egg_base = ei.egg_base - if self.egg_path is None: - self.egg_path = os.path.abspath(ei.egg_base) - - target = pkg_resources.normalize_path(self.egg_base) - egg_path = pkg_resources.normalize_path( - os.path.join(self.install_dir, self.egg_path)) - if egg_path != target: - raise DistutilsOptionError( - "--egg-path must be a relative path from the install" - " directory to " + target - ) - - # Make a distribution for the package's source - self.dist = pkg_resources.Distribution( - target, - pkg_resources.PathMetadata(target, os.path.abspath(ei.egg_info)), - project_name=ei.egg_name - ) - - self.setup_path = self._resolve_setup_path( - self.egg_base, - self.install_dir, - self.egg_path, - ) - - @staticmethod - def _resolve_setup_path(egg_base, install_dir, egg_path): - """ - Generate a path from egg_base back to '.' where the - setup script resides and ensure that path points to the - setup path from $install_dir/$egg_path. - """ - path_to_setup = egg_base.replace(os.sep, '/').rstrip('/') - if path_to_setup != os.curdir: - path_to_setup = '../' * (path_to_setup.count('/') + 1) - resolved = pkg_resources.normalize_path( - os.path.join(install_dir, egg_path, path_to_setup) - ) - if resolved != pkg_resources.normalize_path(os.curdir): - raise DistutilsOptionError( - "Can't get a consistent path to setup script from" - " installation directory", resolved, - pkg_resources.normalize_path(os.curdir)) - return path_to_setup - - def install_for_development(self): - if six.PY3 and getattr(self.distribution, 'use_2to3', False): - # If we run 2to3 we can not do this inplace: - - # Ensure metadata is up-to-date - self.reinitialize_command('build_py', inplace=0) - self.run_command('build_py') - bpy_cmd = self.get_finalized_command("build_py") - build_path = pkg_resources.normalize_path(bpy_cmd.build_lib) - - # Build extensions - self.reinitialize_command('egg_info', egg_base=build_path) - self.run_command('egg_info') - - self.reinitialize_command('build_ext', inplace=0) - self.run_command('build_ext') - - # Fixup egg-link and easy-install.pth - ei_cmd = self.get_finalized_command("egg_info") - self.egg_path = build_path - self.dist.location = build_path - # XXX - self.dist._provider = pkg_resources.PathMetadata( - build_path, ei_cmd.egg_info) - else: - # Without 2to3 inplace works fine: - self.run_command('egg_info') - - # Build extensions in-place - self.reinitialize_command('build_ext', inplace=1) - self.run_command('build_ext') - - self.install_site_py() # ensure that target dir is site-safe - if setuptools.bootstrap_install_from: - self.easy_install(setuptools.bootstrap_install_from) - setuptools.bootstrap_install_from = None - - self.install_namespaces() - - # create an .egg-link in the installation dir, pointing to our egg - log.info("Creating %s (link to %s)", self.egg_link, self.egg_base) - if not self.dry_run: - with open(self.egg_link, "w") as f: - f.write(self.egg_path + "\n" + self.setup_path) - # postprocess the installed distro, fixing up .pth, installing scripts, - # and handling requirements - self.process_distribution(None, self.dist, not self.no_deps) - - def uninstall_link(self): - if os.path.exists(self.egg_link): - log.info("Removing %s (link to %s)", self.egg_link, self.egg_base) - egg_link_file = open(self.egg_link) - contents = [line.rstrip() for line in egg_link_file] - egg_link_file.close() - if contents not in ([self.egg_path], - [self.egg_path, self.setup_path]): - log.warn("Link points to %s: uninstall aborted", contents) - return - if not self.dry_run: - os.unlink(self.egg_link) - if not self.dry_run: - self.update_pth(self.dist) # remove any .pth link to us - if self.distribution.scripts: - # XXX should also check for entry point scripts! - log.warn("Note: you must uninstall or replace scripts manually!") - - def install_egg_scripts(self, dist): - if dist is not self.dist: - # Installing a dependency, so fall back to normal behavior - return easy_install.install_egg_scripts(self, dist) - - # create wrapper scripts in the script dir, pointing to dist.scripts - - # new-style... - self.install_wrapper_scripts(dist) - - # ...and old-style - for script_name in self.distribution.scripts or []: - script_path = os.path.abspath(convert_path(script_name)) - script_name = os.path.basename(script_path) - with io.open(script_path) as strm: - script_text = strm.read() - self.install_script(dist, script_name, script_text, script_path) - - def install_wrapper_scripts(self, dist): - dist = VersionlessRequirement(dist) - return easy_install.install_wrapper_scripts(self, dist) - - -class VersionlessRequirement: - """ - Adapt a pkg_resources.Distribution to simply return the project - name as the 'requirement' so that scripts will work across - multiple versions. - - >>> from pkg_resources import Distribution - >>> dist = Distribution(project_name='foo', version='1.0') - >>> str(dist.as_requirement()) - 'foo==1.0' - >>> adapted_dist = VersionlessRequirement(dist) - >>> str(adapted_dist.as_requirement()) - 'foo' - """ - - def __init__(self, dist): - self.__dist = dist - - def __getattr__(self, name): - return getattr(self.__dist, name) - - def as_requirement(self): - return self.project_name diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/dist_info.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/dist_info.py deleted file mode 100644 index c45258f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/dist_info.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -Create a dist_info directory -As defined in the wheel specification -""" - -import os - -from distutils.core import Command -from distutils import log - - -class dist_info(Command): - - description = 'create a .dist-info directory' - - user_options = [ - ('egg-base=', 'e', "directory containing .egg-info directories" - " (default: top of the source tree)"), - ] - - def initialize_options(self): - self.egg_base = None - - def finalize_options(self): - pass - - def run(self): - egg_info = self.get_finalized_command('egg_info') - egg_info.egg_base = self.egg_base - egg_info.finalize_options() - egg_info.run() - dist_info_dir = egg_info.egg_info[:-len('.egg-info')] + '.dist-info' - log.info("creating '{}'".format(os.path.abspath(dist_info_dir))) - - bdist_wheel = self.get_finalized_command('bdist_wheel') - bdist_wheel.egg2dist(egg_info.egg_info, dist_info_dir) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/easy_install.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/easy_install.py deleted file mode 100644 index 1f6839c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/easy_install.py +++ /dev/null @@ -1,2402 +0,0 @@ -#!/usr/bin/env python -""" -Easy Install ------------- - -A tool for doing automatic download/extract/build of distutils-based Python -packages. For detailed documentation, see the accompanying EasyInstall.txt -file, or visit the `EasyInstall home page`__. - -__ https://setuptools.readthedocs.io/en/latest/easy_install.html - -""" - -from glob import glob -from distutils.util import get_platform -from distutils.util import convert_path, subst_vars -from distutils.errors import ( - DistutilsArgError, DistutilsOptionError, - DistutilsError, DistutilsPlatformError, -) -from distutils.command.install import INSTALL_SCHEMES, SCHEME_KEYS -from distutils import log, dir_util -from distutils.command.build_scripts import first_line_re -from distutils.spawn import find_executable -import sys -import os -import zipimport -import shutil -import tempfile -import zipfile -import re -import stat -import random -import textwrap -import warnings -import site -import struct -import contextlib -import subprocess -import shlex -import io - - -from sysconfig import get_config_vars, get_path - -from setuptools import SetuptoolsDeprecationWarning - -from setuptools.extern import six -from setuptools.extern.six.moves import configparser, map - -from setuptools import Command -from setuptools.sandbox import run_setup -from setuptools.py27compat import rmtree_safe -from setuptools.command import setopt -from setuptools.archive_util import unpack_archive -from setuptools.package_index import ( - PackageIndex, parse_requirement_arg, URL_SCHEME, -) -from setuptools.command import bdist_egg, egg_info -from setuptools.wheel import Wheel -from pkg_resources import ( - yield_lines, normalize_path, resource_string, ensure_directory, - get_distribution, find_distributions, Environment, Requirement, - Distribution, PathMetadata, EggMetadata, WorkingSet, DistributionNotFound, - VersionConflict, DEVELOP_DIST, -) -import pkg_resources.py31compat - -__metaclass__ = type - -# Turn on PEP440Warnings -warnings.filterwarnings("default", category=pkg_resources.PEP440Warning) - -__all__ = [ - 'samefile', 'easy_install', 'PthDistributions', 'extract_wininst_cfg', - 'main', 'get_exe_prefixes', -] - - -def is_64bit(): - return struct.calcsize("P") == 8 - - -def samefile(p1, p2): - """ - Determine if two paths reference the same file. - - Augments os.path.samefile to work on Windows and - suppresses errors if the path doesn't exist. - """ - both_exist = os.path.exists(p1) and os.path.exists(p2) - use_samefile = hasattr(os.path, 'samefile') and both_exist - if use_samefile: - return os.path.samefile(p1, p2) - norm_p1 = os.path.normpath(os.path.normcase(p1)) - norm_p2 = os.path.normpath(os.path.normcase(p2)) - return norm_p1 == norm_p2 - - -if six.PY2: - - def _to_bytes(s): - return s - - def isascii(s): - try: - six.text_type(s, 'ascii') - return True - except UnicodeError: - return False -else: - - def _to_bytes(s): - return s.encode('utf8') - - def isascii(s): - try: - s.encode('ascii') - return True - except UnicodeError: - return False - - -_one_liner = lambda text: textwrap.dedent(text).strip().replace('\n', '; ') - - -class easy_install(Command): - """Manage a download/build/install process""" - description = "Find/get/install Python packages" - command_consumes_arguments = True - - user_options = [ - ('prefix=', None, "installation prefix"), - ("zip-ok", "z", "install package as a zipfile"), - ("multi-version", "m", "make apps have to require() a version"), - ("upgrade", "U", "force upgrade (searches PyPI for latest versions)"), - ("install-dir=", "d", "install package to DIR"), - ("script-dir=", "s", "install scripts to DIR"), - ("exclude-scripts", "x", "Don't install scripts"), - ("always-copy", "a", "Copy all needed packages to install dir"), - ("index-url=", "i", "base URL of Python Package Index"), - ("find-links=", "f", "additional URL(s) to search for packages"), - ("build-directory=", "b", - "download/extract/build in DIR; keep the results"), - ('optimize=', 'O', - "also compile with optimization: -O1 for \"python -O\", " - "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), - ('record=', None, - "filename in which to record list of installed files"), - ('always-unzip', 'Z', "don't install as a zipfile, no matter what"), - ('site-dirs=', 'S', "list of directories where .pth files work"), - ('editable', 'e', "Install specified packages in editable form"), - ('no-deps', 'N', "don't install dependencies"), - ('allow-hosts=', 'H', "pattern(s) that hostnames must match"), - ('local-snapshots-ok', 'l', - "allow building eggs from local checkouts"), - ('version', None, "print version information and exit"), - ('install-layout=', None, "installation layout to choose (known values: deb)"), - ('force-installation-into-system-dir', '0', "force installation into /usr"), - ('no-find-links', None, - "Don't load find-links defined in packages being installed") - ] - boolean_options = [ - 'zip-ok', 'multi-version', 'exclude-scripts', 'upgrade', 'always-copy', - 'editable', - 'no-deps', 'local-snapshots-ok', 'version', 'force-installation-into-system-dir' - ] - - if site.ENABLE_USER_SITE: - help_msg = "install in user site-package '%s'" % site.USER_SITE - user_options.append(('user', None, help_msg)) - boolean_options.append('user') - - negative_opt = {'always-unzip': 'zip-ok'} - create_index = PackageIndex - - def initialize_options(self): - # the --user option seems to be an opt-in one, - # so the default should be False. - self.user = 0 - self.zip_ok = self.local_snapshots_ok = None - self.install_dir = self.script_dir = self.exclude_scripts = None - self.index_url = None - self.find_links = None - self.build_directory = None - self.args = None - self.optimize = self.record = None - self.upgrade = self.always_copy = self.multi_version = None - self.editable = self.no_deps = self.allow_hosts = None - self.root = self.prefix = self.no_report = None - self.version = None - self.install_purelib = None # for pure module distributions - self.install_platlib = None # non-pure (dists w/ extensions) - self.install_headers = None # for C/C++ headers - self.install_lib = None # set to either purelib or platlib - self.install_scripts = None - self.install_data = None - self.install_base = None - self.install_platbase = None - if site.ENABLE_USER_SITE: - self.install_userbase = site.USER_BASE - self.install_usersite = site.USER_SITE - else: - self.install_userbase = None - self.install_usersite = None - self.no_find_links = None - - # Options not specifiable via command line - self.package_index = None - self.pth_file = self.always_copy_from = None - self.site_dirs = None - self.installed_projects = {} - self.sitepy_installed = False - # enable custom installation, known values: deb - self.install_layout = None - self.force_installation_into_system_dir = None - self.multiarch = None - - # Always read easy_install options, even if we are subclassed, or have - # an independent instance created. This ensures that defaults will - # always come from the standard configuration file(s)' "easy_install" - # section, even if this is a "develop" or "install" command, or some - # other embedding. - self._dry_run = None - self.verbose = self.distribution.verbose - self.distribution._set_command_options( - self, self.distribution.get_option_dict('easy_install') - ) - - def delete_blockers(self, blockers): - extant_blockers = ( - filename for filename in blockers - if os.path.exists(filename) or os.path.islink(filename) - ) - list(map(self._delete_path, extant_blockers)) - - def _delete_path(self, path): - log.info("Deleting %s", path) - if self.dry_run: - return - - is_tree = os.path.isdir(path) and not os.path.islink(path) - remover = rmtree if is_tree else os.unlink - remover(path) - - @staticmethod - def _render_version(): - """ - Render the Setuptools version and installation details, then exit. - """ - ver = '{}.{}'.format(*sys.version_info) - dist = get_distribution('setuptools') - tmpl = 'setuptools {dist.version} from {dist.location} (Python {ver})' - print(tmpl.format(**locals())) - raise SystemExit() - - def finalize_options(self): - self.version and self._render_version() - - py_version = sys.version.split()[0] - prefix, exec_prefix = get_config_vars('prefix', 'exec_prefix') - - self.config_vars = { - 'dist_name': self.distribution.get_name(), - 'dist_version': self.distribution.get_version(), - 'dist_fullname': self.distribution.get_fullname(), - 'py_version': py_version, - 'py_version_short': py_version[0:3], - 'py_version_nodot': py_version[0] + py_version[2], - 'sys_prefix': prefix, - 'prefix': prefix, - 'sys_exec_prefix': exec_prefix, - 'exec_prefix': exec_prefix, - # Only python 3.2+ has abiflags - 'abiflags': getattr(sys, 'abiflags', ''), - } - - if site.ENABLE_USER_SITE: - self.config_vars['userbase'] = self.install_userbase - self.config_vars['usersite'] = self.install_usersite - - self._fix_install_dir_for_user_site() - - self.expand_basedirs() - self.expand_dirs() - - if self.install_layout: - if not self.install_layout.lower() in ['deb']: - raise DistutilsOptionError("unknown value for --install-layout") - self.install_layout = self.install_layout.lower() - - import sysconfig - if sys.version_info[:2] >= (3, 3): - self.multiarch = sysconfig.get_config_var('MULTIARCH') - - self._expand( - 'install_dir', 'script_dir', 'build_directory', - 'site_dirs', - ) - # If a non-default installation directory was specified, default the - # script directory to match it. - if self.script_dir is None: - self.script_dir = self.install_dir - - if self.no_find_links is None: - self.no_find_links = False - - # Let install_dir get set by install_lib command, which in turn - # gets its info from the install command, and takes into account - # --prefix and --home and all that other crud. - self.set_undefined_options( - 'install_lib', ('install_dir', 'install_dir') - ) - # Likewise, set default script_dir from 'install_scripts.install_dir' - self.set_undefined_options( - 'install_scripts', ('install_dir', 'script_dir') - ) - - if self.user and self.install_purelib: - self.install_dir = self.install_purelib - self.script_dir = self.install_scripts - - if self.prefix == '/usr' and not self.force_installation_into_system_dir: - raise DistutilsOptionError("""installation into /usr - -Trying to install into the system managed parts of the file system. Please -consider to install to another location, or use the option ---force-installation-into-system-dir to overwrite this warning. -""") - - # default --record from the install command - self.set_undefined_options('install', ('record', 'record')) - # Should this be moved to the if statement below? It's not used - # elsewhere - normpath = map(normalize_path, sys.path) - self.all_site_dirs = get_site_dirs() - if self.site_dirs is not None: - site_dirs = [ - os.path.expanduser(s.strip()) for s in - self.site_dirs.split(',') - ] - for d in site_dirs: - if not os.path.isdir(d): - log.warn("%s (in --site-dirs) does not exist", d) - elif normalize_path(d) not in normpath: - raise DistutilsOptionError( - d + " (in --site-dirs) is not on sys.path" - ) - else: - self.all_site_dirs.append(normalize_path(d)) - if not self.editable: - self.check_site_dir() - self.index_url = self.index_url or "https://pypi.org/simple/" - self.shadow_path = self.all_site_dirs[:] - for path_item in self.install_dir, normalize_path(self.script_dir): - if path_item not in self.shadow_path: - self.shadow_path.insert(0, path_item) - - if self.allow_hosts is not None: - hosts = [s.strip() for s in self.allow_hosts.split(',')] - else: - hosts = ['*'] - if self.package_index is None: - self.package_index = self.create_index( - self.index_url, search_path=self.shadow_path, hosts=hosts, - ) - self.local_index = Environment(self.shadow_path + sys.path) - - if self.find_links is not None: - if isinstance(self.find_links, six.string_types): - self.find_links = self.find_links.split() - else: - self.find_links = [] - if self.local_snapshots_ok: - self.package_index.scan_egg_links(self.shadow_path + sys.path) - if not self.no_find_links: - self.package_index.add_find_links(self.find_links) - self.set_undefined_options('install_lib', ('optimize', 'optimize')) - if not isinstance(self.optimize, int): - try: - self.optimize = int(self.optimize) - if not (0 <= self.optimize <= 2): - raise ValueError - except ValueError: - raise DistutilsOptionError("--optimize must be 0, 1, or 2") - - if self.editable and not self.build_directory: - raise DistutilsArgError( - "Must specify a build directory (-b) when using --editable" - ) - if not self.args: - raise DistutilsArgError( - "No urls, filenames, or requirements specified (see --help)") - - self.outputs = [] - - def _fix_install_dir_for_user_site(self): - """ - Fix the install_dir if "--user" was used. - """ - if not self.user or not site.ENABLE_USER_SITE: - return - - self.create_home_path() - if self.install_userbase is None: - msg = "User base directory is not specified" - raise DistutilsPlatformError(msg) - self.install_base = self.install_platbase = self.install_userbase - scheme_name = os.name.replace('posix', 'unix') + '_user' - self.select_scheme(scheme_name) - - def _expand_attrs(self, attrs): - for attr in attrs: - val = getattr(self, attr) - if val is not None: - if os.name == 'posix' or os.name == 'nt': - val = os.path.expanduser(val) - val = subst_vars(val, self.config_vars) - setattr(self, attr, val) - - def expand_basedirs(self): - """Calls `os.path.expanduser` on install_base, install_platbase and - root.""" - self._expand_attrs(['install_base', 'install_platbase', 'root']) - - def expand_dirs(self): - """Calls `os.path.expanduser` on install dirs.""" - dirs = [ - 'install_purelib', - 'install_platlib', - 'install_lib', - 'install_headers', - 'install_scripts', - 'install_data', - ] - self._expand_attrs(dirs) - - def run(self, show_deprecation=True): - if show_deprecation: - self.announce( - "WARNING: The easy_install command is deprecated " - "and will be removed in a future version." - , log.WARN, - ) - if self.verbose != self.distribution.verbose: - log.set_verbosity(self.verbose) - try: - for spec in self.args: - self.easy_install(spec, not self.no_deps) - if self.record: - outputs = list(sorted(self.outputs)) - if self.root: # strip any package prefix - root_len = len(self.root) - for counter in range(len(outputs)): - outputs[counter] = outputs[counter][root_len:] - from distutils import file_util - - self.execute( - file_util.write_file, (self.record, outputs), - "writing list of installed files to '%s'" % - self.record - ) - self.warn_deprecated_options() - finally: - log.set_verbosity(self.distribution.verbose) - - def pseudo_tempname(self): - """Return a pseudo-tempname base in the install directory. - This code is intentionally naive; if a malicious party can write to - the target directory you're already in deep doodoo. - """ - try: - pid = os.getpid() - except Exception: - pid = random.randint(0, sys.maxsize) - return os.path.join(self.install_dir, "test-easy-install-%s" % pid) - - def warn_deprecated_options(self): - pass - - def check_site_dir(self): - """Verify that self.install_dir is .pth-capable dir, if needed""" - - instdir = normalize_path(self.install_dir) - pth_file = os.path.join(instdir, 'easy-install.pth') - - # Is it a configured, PYTHONPATH, implicit, or explicit site dir? - is_site_dir = instdir in self.all_site_dirs - - if not is_site_dir and not self.multi_version: - # No? Then directly test whether it does .pth file processing - is_site_dir = self.check_pth_processing() - else: - # make sure we can write to target dir - testfile = self.pseudo_tempname() + '.write-test' - test_exists = os.path.exists(testfile) - try: - if test_exists: - os.unlink(testfile) - open(testfile, 'w').close() - os.unlink(testfile) - except (OSError, IOError): - self.cant_write_to_target() - - if not is_site_dir and not self.multi_version: - # Can't install non-multi to non-site dir - raise DistutilsError(self.no_default_version_msg()) - - if is_site_dir: - if self.pth_file is None: - self.pth_file = PthDistributions(pth_file, self.all_site_dirs) - else: - self.pth_file = None - - if instdir not in map(normalize_path, _pythonpath()): - # only PYTHONPATH dirs need a site.py, so pretend it's there - self.sitepy_installed = True - elif self.multi_version and not os.path.exists(pth_file): - self.sitepy_installed = True # don't need site.py in this case - self.pth_file = None # and don't create a .pth file - self.install_dir = instdir - - __cant_write_msg = textwrap.dedent(""" - can't create or remove files in install directory - - The following error occurred while trying to add or remove files in the - installation directory: - - %s - - The installation directory you specified (via --install-dir, --prefix, or - the distutils default setting) was: - - %s - """).lstrip() - - __not_exists_id = textwrap.dedent(""" - This directory does not currently exist. Please create it and try again, or - choose a different installation directory (using the -d or --install-dir - option). - """).lstrip() - - __access_msg = textwrap.dedent(""" - Perhaps your account does not have write access to this directory? If the - installation directory is a system-owned directory, you may need to sign in - as the administrator or "root" account. If you do not have administrative - access to this machine, you may wish to choose a different installation - directory, preferably one that is listed in your PYTHONPATH environment - variable. - - For information on other options, you may wish to consult the - documentation at: - - https://setuptools.readthedocs.io/en/latest/easy_install.html - - Please make the appropriate changes for your system and try again. - """).lstrip() - - def cant_write_to_target(self): - msg = self.__cant_write_msg % (sys.exc_info()[1], self.install_dir,) - - if not os.path.exists(self.install_dir): - msg += '\n' + self.__not_exists_id - else: - msg += '\n' + self.__access_msg - raise DistutilsError(msg) - - def check_pth_processing(self): - """Empirically verify whether .pth files are supported in inst. dir""" - instdir = self.install_dir - log.info("Checking .pth file support in %s", instdir) - pth_file = self.pseudo_tempname() + ".pth" - ok_file = pth_file + '.ok' - ok_exists = os.path.exists(ok_file) - tmpl = _one_liner(""" - import os - f = open({ok_file!r}, 'w') - f.write('OK') - f.close() - """) + '\n' - try: - if ok_exists: - os.unlink(ok_file) - dirname = os.path.dirname(ok_file) - pkg_resources.py31compat.makedirs(dirname, exist_ok=True) - f = open(pth_file, 'w') - except (OSError, IOError): - self.cant_write_to_target() - else: - try: - f.write(tmpl.format(**locals())) - f.close() - f = None - executable = sys.executable - if os.name == 'nt': - dirname, basename = os.path.split(executable) - alt = os.path.join(dirname, 'pythonw.exe') - use_alt = ( - basename.lower() == 'python.exe' and - os.path.exists(alt) - ) - if use_alt: - # use pythonw.exe to avoid opening a console window - executable = alt - - from distutils.spawn import spawn - - spawn([executable, '-E', '-c', 'pass'], 0) - - if os.path.exists(ok_file): - log.info( - "TEST PASSED: %s appears to support .pth files", - instdir - ) - return True - finally: - if f: - f.close() - if os.path.exists(ok_file): - os.unlink(ok_file) - if os.path.exists(pth_file): - os.unlink(pth_file) - if not self.multi_version: - log.warn("TEST FAILED: %s does NOT support .pth files", instdir) - return False - - def install_egg_scripts(self, dist): - """Write all the scripts for `dist`, unless scripts are excluded""" - if not self.exclude_scripts and dist.metadata_isdir('scripts'): - for script_name in dist.metadata_listdir('scripts'): - if dist.metadata_isdir('scripts/' + script_name): - # The "script" is a directory, likely a Python 3 - # __pycache__ directory, so skip it. - continue - self.install_script( - dist, script_name, - dist.get_metadata('scripts/' + script_name) - ) - self.install_wrapper_scripts(dist) - - def add_output(self, path): - if os.path.isdir(path): - for base, dirs, files in os.walk(path): - for filename in files: - self.outputs.append(os.path.join(base, filename)) - else: - self.outputs.append(path) - - def not_editable(self, spec): - if self.editable: - raise DistutilsArgError( - "Invalid argument %r: you can't use filenames or URLs " - "with --editable (except via the --find-links option)." - % (spec,) - ) - - def check_editable(self, spec): - if not self.editable: - return - - if os.path.exists(os.path.join(self.build_directory, spec.key)): - raise DistutilsArgError( - "%r already exists in %s; can't do a checkout there" % - (spec.key, self.build_directory) - ) - - @contextlib.contextmanager - def _tmpdir(self): - tmpdir = tempfile.mkdtemp(prefix=u"easy_install-") - try: - # cast to str as workaround for #709 and #710 and #712 - yield str(tmpdir) - finally: - os.path.exists(tmpdir) and rmtree(rmtree_safe(tmpdir)) - - def easy_install(self, spec, deps=False): - if not self.editable: - self.install_site_py() - - with self._tmpdir() as tmpdir: - if not isinstance(spec, Requirement): - if URL_SCHEME(spec): - # It's a url, download it to tmpdir and process - self.not_editable(spec) - dl = self.package_index.download(spec, tmpdir) - return self.install_item(None, dl, tmpdir, deps, True) - - elif os.path.exists(spec): - # Existing file or directory, just process it directly - self.not_editable(spec) - return self.install_item(None, spec, tmpdir, deps, True) - else: - spec = parse_requirement_arg(spec) - - self.check_editable(spec) - dist = self.package_index.fetch_distribution( - spec, tmpdir, self.upgrade, self.editable, - not self.always_copy, self.local_index - ) - if dist is None: - msg = "Could not find suitable distribution for %r" % spec - if self.always_copy: - msg += " (--always-copy skips system and development eggs)" - raise DistutilsError(msg) - elif dist.precedence == DEVELOP_DIST: - # .egg-info dists don't need installing, just process deps - self.process_distribution(spec, dist, deps, "Using") - return dist - else: - return self.install_item(spec, dist.location, tmpdir, deps) - - def install_item(self, spec, download, tmpdir, deps, install_needed=False): - - # Installation is also needed if file in tmpdir or is not an egg - install_needed = install_needed or self.always_copy - install_needed = install_needed or os.path.dirname(download) == tmpdir - install_needed = install_needed or not download.endswith('.egg') - install_needed = install_needed or ( - self.always_copy_from is not None and - os.path.dirname(normalize_path(download)) == - normalize_path(self.always_copy_from) - ) - - if spec and not install_needed: - # at this point, we know it's a local .egg, we just don't know if - # it's already installed. - for dist in self.local_index[spec.project_name]: - if dist.location == download: - break - else: - install_needed = True # it's not in the local index - - log.info("Processing %s", os.path.basename(download)) - - if install_needed: - dists = self.install_eggs(spec, download, tmpdir) - for dist in dists: - self.process_distribution(spec, dist, deps) - else: - dists = [self.egg_distribution(download)] - self.process_distribution(spec, dists[0], deps, "Using") - - if spec is not None: - for dist in dists: - if dist in spec: - return dist - - def select_scheme(self, name): - """Sets the install directories by applying the install schemes.""" - # it's the caller's problem if they supply a bad name! - scheme = INSTALL_SCHEMES[name] - for key in SCHEME_KEYS: - attrname = 'install_' + key - if getattr(self, attrname) is None: - setattr(self, attrname, scheme[key]) - - def process_distribution(self, requirement, dist, deps=True, *info): - self.update_pth(dist) - self.package_index.add(dist) - if dist in self.local_index[dist.key]: - self.local_index.remove(dist) - self.local_index.add(dist) - self.install_egg_scripts(dist) - self.installed_projects[dist.key] = dist - log.info(self.installation_report(requirement, dist, *info)) - if (dist.has_metadata('dependency_links.txt') and - not self.no_find_links): - self.package_index.add_find_links( - dist.get_metadata_lines('dependency_links.txt') - ) - if not deps and not self.always_copy: - return - elif requirement is not None and dist.key != requirement.key: - log.warn("Skipping dependencies for %s", dist) - return # XXX this is not the distribution we were looking for - elif requirement is None or dist not in requirement: - # if we wound up with a different version, resolve what we've got - distreq = dist.as_requirement() - requirement = Requirement(str(distreq)) - log.info("Processing dependencies for %s", requirement) - try: - distros = WorkingSet([]).resolve( - [requirement], self.local_index, self.easy_install - ) - except DistributionNotFound as e: - raise DistutilsError(str(e)) - except VersionConflict as e: - raise DistutilsError(e.report()) - if self.always_copy or self.always_copy_from: - # Force all the relevant distros to be copied or activated - for dist in distros: - if dist.key not in self.installed_projects: - self.easy_install(dist.as_requirement()) - log.info("Finished processing dependencies for %s", requirement) - - def should_unzip(self, dist): - if self.zip_ok is not None: - return not self.zip_ok - if dist.has_metadata('not-zip-safe'): - return True - if not dist.has_metadata('zip-safe'): - return True - return False - - def maybe_move(self, spec, dist_filename, setup_base): - dst = os.path.join(self.build_directory, spec.key) - if os.path.exists(dst): - msg = ( - "%r already exists in %s; build directory %s will not be kept" - ) - log.warn(msg, spec.key, self.build_directory, setup_base) - return setup_base - if os.path.isdir(dist_filename): - setup_base = dist_filename - else: - if os.path.dirname(dist_filename) == setup_base: - os.unlink(dist_filename) # get it out of the tmp dir - contents = os.listdir(setup_base) - if len(contents) == 1: - dist_filename = os.path.join(setup_base, contents[0]) - if os.path.isdir(dist_filename): - # if the only thing there is a directory, move it instead - setup_base = dist_filename - ensure_directory(dst) - shutil.move(setup_base, dst) - return dst - - def install_wrapper_scripts(self, dist): - if self.exclude_scripts: - return - for args in ScriptWriter.best().get_args(dist): - self.write_script(*args) - - def install_script(self, dist, script_name, script_text, dev_path=None): - """Generate a legacy script wrapper and install it""" - spec = str(dist.as_requirement()) - is_script = is_python_script(script_text, script_name) - - if is_script: - body = self._load_template(dev_path) % locals() - script_text = ScriptWriter.get_header(script_text) + body - self.write_script(script_name, _to_bytes(script_text), 'b') - - @staticmethod - def _load_template(dev_path): - """ - There are a couple of template scripts in the package. This - function loads one of them and prepares it for use. - """ - # See https://github.com/pypa/setuptools/issues/134 for info - # on script file naming and downstream issues with SVR4 - name = 'script.tmpl' - if dev_path: - name = name.replace('.tmpl', ' (dev).tmpl') - - raw_bytes = resource_string('setuptools', name) - return raw_bytes.decode('utf-8') - - def write_script(self, script_name, contents, mode="t", blockers=()): - """Write an executable file to the scripts directory""" - self.delete_blockers( # clean up old .py/.pyw w/o a script - [os.path.join(self.script_dir, x) for x in blockers] - ) - log.info("Installing %s script to %s", script_name, self.script_dir) - target = os.path.join(self.script_dir, script_name) - self.add_output(target) - - if self.dry_run: - return - - mask = current_umask() - ensure_directory(target) - if os.path.exists(target): - os.unlink(target) - with open(target, "w" + mode) as f: - f.write(contents) - chmod(target, 0o777 - mask) - - def install_eggs(self, spec, dist_filename, tmpdir): - # .egg dirs or files are already built, so just return them - if dist_filename.lower().endswith('.egg'): - return [self.install_egg(dist_filename, tmpdir)] - elif dist_filename.lower().endswith('.exe'): - return [self.install_exe(dist_filename, tmpdir)] - elif dist_filename.lower().endswith('.whl'): - return [self.install_wheel(dist_filename, tmpdir)] - - # Anything else, try to extract and build - setup_base = tmpdir - if os.path.isfile(dist_filename) and not dist_filename.endswith('.py'): - unpack_archive(dist_filename, tmpdir, self.unpack_progress) - elif os.path.isdir(dist_filename): - setup_base = os.path.abspath(dist_filename) - - if (setup_base.startswith(tmpdir) # something we downloaded - and self.build_directory and spec is not None): - setup_base = self.maybe_move(spec, dist_filename, setup_base) - - # Find the setup.py file - setup_script = os.path.join(setup_base, 'setup.py') - - if not os.path.exists(setup_script): - setups = glob(os.path.join(setup_base, '*', 'setup.py')) - if not setups: - raise DistutilsError( - "Couldn't find a setup script in %s" % - os.path.abspath(dist_filename) - ) - if len(setups) > 1: - raise DistutilsError( - "Multiple setup scripts in %s" % - os.path.abspath(dist_filename) - ) - setup_script = setups[0] - - # Now run it, and return the result - if self.editable: - log.info(self.report_editable(spec, setup_script)) - return [] - else: - return self.build_and_install(setup_script, setup_base) - - def egg_distribution(self, egg_path): - if os.path.isdir(egg_path): - metadata = PathMetadata(egg_path, os.path.join(egg_path, - 'EGG-INFO')) - else: - metadata = EggMetadata(zipimport.zipimporter(egg_path)) - return Distribution.from_filename(egg_path, metadata=metadata) - - def install_egg(self, egg_path, tmpdir): - destination = os.path.join( - self.install_dir, - os.path.basename(egg_path), - ) - destination = os.path.abspath(destination) - if not self.dry_run: - ensure_directory(destination) - - dist = self.egg_distribution(egg_path) - if not samefile(egg_path, destination): - if os.path.isdir(destination) and not os.path.islink(destination): - dir_util.remove_tree(destination, dry_run=self.dry_run) - elif os.path.exists(destination): - self.execute( - os.unlink, - (destination,), - "Removing " + destination, - ) - try: - new_dist_is_zipped = False - if os.path.isdir(egg_path): - if egg_path.startswith(tmpdir): - f, m = shutil.move, "Moving" - else: - f, m = shutil.copytree, "Copying" - elif self.should_unzip(dist): - self.mkpath(destination) - f, m = self.unpack_and_compile, "Extracting" - else: - new_dist_is_zipped = True - if egg_path.startswith(tmpdir): - f, m = shutil.move, "Moving" - else: - f, m = shutil.copy2, "Copying" - self.execute( - f, - (egg_path, destination), - (m + " %s to %s") % ( - os.path.basename(egg_path), - os.path.dirname(destination) - ), - ) - update_dist_caches( - destination, - fix_zipimporter_caches=new_dist_is_zipped, - ) - except Exception: - update_dist_caches(destination, fix_zipimporter_caches=False) - raise - - self.add_output(destination) - return self.egg_distribution(destination) - - def install_exe(self, dist_filename, tmpdir): - # See if it's valid, get data - cfg = extract_wininst_cfg(dist_filename) - if cfg is None: - raise DistutilsError( - "%s is not a valid distutils Windows .exe" % dist_filename - ) - # Create a dummy distribution object until we build the real distro - dist = Distribution( - None, - project_name=cfg.get('metadata', 'name'), - version=cfg.get('metadata', 'version'), platform=get_platform(), - ) - - # Convert the .exe to an unpacked egg - egg_path = os.path.join(tmpdir, dist.egg_name() + '.egg') - dist.location = egg_path - egg_tmp = egg_path + '.tmp' - _egg_info = os.path.join(egg_tmp, 'EGG-INFO') - pkg_inf = os.path.join(_egg_info, 'PKG-INFO') - ensure_directory(pkg_inf) # make sure EGG-INFO dir exists - dist._provider = PathMetadata(egg_tmp, _egg_info) # XXX - self.exe_to_egg(dist_filename, egg_tmp) - - # Write EGG-INFO/PKG-INFO - if not os.path.exists(pkg_inf): - f = open(pkg_inf, 'w') - f.write('Metadata-Version: 1.0\n') - for k, v in cfg.items('metadata'): - if k != 'target_version': - f.write('%s: %s\n' % (k.replace('_', '-').title(), v)) - f.close() - script_dir = os.path.join(_egg_info, 'scripts') - # delete entry-point scripts to avoid duping - self.delete_blockers([ - os.path.join(script_dir, args[0]) - for args in ScriptWriter.get_args(dist) - ]) - # Build .egg file from tmpdir - bdist_egg.make_zipfile( - egg_path, egg_tmp, verbose=self.verbose, dry_run=self.dry_run, - ) - # install the .egg - return self.install_egg(egg_path, tmpdir) - - def exe_to_egg(self, dist_filename, egg_tmp): - """Extract a bdist_wininst to the directories an egg would use""" - # Check for .pth file and set up prefix translations - prefixes = get_exe_prefixes(dist_filename) - to_compile = [] - native_libs = [] - top_level = {} - - def process(src, dst): - s = src.lower() - for old, new in prefixes: - if s.startswith(old): - src = new + src[len(old):] - parts = src.split('/') - dst = os.path.join(egg_tmp, *parts) - dl = dst.lower() - if dl.endswith('.pyd') or dl.endswith('.dll'): - parts[-1] = bdist_egg.strip_module(parts[-1]) - top_level[os.path.splitext(parts[0])[0]] = 1 - native_libs.append(src) - elif dl.endswith('.py') and old != 'SCRIPTS/': - top_level[os.path.splitext(parts[0])[0]] = 1 - to_compile.append(dst) - return dst - if not src.endswith('.pth'): - log.warn("WARNING: can't process %s", src) - return None - - # extract, tracking .pyd/.dll->native_libs and .py -> to_compile - unpack_archive(dist_filename, egg_tmp, process) - stubs = [] - for res in native_libs: - if res.lower().endswith('.pyd'): # create stubs for .pyd's - parts = res.split('/') - resource = parts[-1] - parts[-1] = bdist_egg.strip_module(parts[-1]) + '.py' - pyfile = os.path.join(egg_tmp, *parts) - to_compile.append(pyfile) - stubs.append(pyfile) - bdist_egg.write_stub(resource, pyfile) - self.byte_compile(to_compile) # compile .py's - bdist_egg.write_safety_flag( - os.path.join(egg_tmp, 'EGG-INFO'), - bdist_egg.analyze_egg(egg_tmp, stubs)) # write zip-safety flag - - for name in 'top_level', 'native_libs': - if locals()[name]: - txt = os.path.join(egg_tmp, 'EGG-INFO', name + '.txt') - if not os.path.exists(txt): - f = open(txt, 'w') - f.write('\n'.join(locals()[name]) + '\n') - f.close() - - def install_wheel(self, wheel_path, tmpdir): - wheel = Wheel(wheel_path) - assert wheel.is_compatible() - destination = os.path.join(self.install_dir, wheel.egg_name()) - destination = os.path.abspath(destination) - if not self.dry_run: - ensure_directory(destination) - if os.path.isdir(destination) and not os.path.islink(destination): - dir_util.remove_tree(destination, dry_run=self.dry_run) - elif os.path.exists(destination): - self.execute( - os.unlink, - (destination,), - "Removing " + destination, - ) - try: - self.execute( - wheel.install_as_egg, - (destination,), - ("Installing %s to %s") % ( - os.path.basename(wheel_path), - os.path.dirname(destination) - ), - ) - finally: - update_dist_caches(destination, fix_zipimporter_caches=False) - self.add_output(destination) - return self.egg_distribution(destination) - - __mv_warning = textwrap.dedent(""" - Because this distribution was installed --multi-version, before you can - import modules from this package in an application, you will need to - 'import pkg_resources' and then use a 'require()' call similar to one of - these examples, in order to select the desired version: - - pkg_resources.require("%(name)s") # latest installed version - pkg_resources.require("%(name)s==%(version)s") # this exact version - pkg_resources.require("%(name)s>=%(version)s") # this version or higher - """).lstrip() - - __id_warning = textwrap.dedent(""" - Note also that the installation directory must be on sys.path at runtime for - this to work. (e.g. by being the application's script directory, by being on - PYTHONPATH, or by being added to sys.path by your code.) - """) - - def installation_report(self, req, dist, what="Installed"): - """Helpful installation message for display to package users""" - msg = "\n%(what)s %(eggloc)s%(extras)s" - if self.multi_version and not self.no_report: - msg += '\n' + self.__mv_warning - if self.install_dir not in map(normalize_path, sys.path): - msg += '\n' + self.__id_warning - - eggloc = dist.location - name = dist.project_name - version = dist.version - extras = '' # TODO: self.report_extras(req, dist) - return msg % locals() - - __editable_msg = textwrap.dedent(""" - Extracted editable version of %(spec)s to %(dirname)s - - If it uses setuptools in its setup script, you can activate it in - "development" mode by going to that directory and running:: - - %(python)s setup.py develop - - See the setuptools documentation for the "develop" command for more info. - """).lstrip() - - def report_editable(self, spec, setup_script): - dirname = os.path.dirname(setup_script) - python = sys.executable - return '\n' + self.__editable_msg % locals() - - def run_setup(self, setup_script, setup_base, args): - sys.modules.setdefault('distutils.command.bdist_egg', bdist_egg) - sys.modules.setdefault('distutils.command.egg_info', egg_info) - - args = list(args) - if self.verbose > 2: - v = 'v' * (self.verbose - 1) - args.insert(0, '-' + v) - elif self.verbose < 2: - args.insert(0, '-q') - if self.dry_run: - args.insert(0, '-n') - log.info( - "Running %s %s", setup_script[len(setup_base) + 1:], ' '.join(args) - ) - try: - run_setup(setup_script, args) - except SystemExit as v: - raise DistutilsError("Setup script exited with %s" % (v.args[0],)) - - def build_and_install(self, setup_script, setup_base): - args = ['bdist_egg', '--dist-dir'] - - dist_dir = tempfile.mkdtemp( - prefix='egg-dist-tmp-', dir=os.path.dirname(setup_script) - ) - try: - self._set_fetcher_options(os.path.dirname(setup_script)) - args.append(dist_dir) - - self.run_setup(setup_script, setup_base, args) - all_eggs = Environment([dist_dir]) - eggs = [] - for key in all_eggs: - for dist in all_eggs[key]: - eggs.append(self.install_egg(dist.location, setup_base)) - if not eggs and not self.dry_run: - log.warn("No eggs found in %s (setup script problem?)", - dist_dir) - return eggs - finally: - rmtree(dist_dir) - log.set_verbosity(self.verbose) # restore our log verbosity - - def _set_fetcher_options(self, base): - """ - When easy_install is about to run bdist_egg on a source dist, that - source dist might have 'setup_requires' directives, requiring - additional fetching. Ensure the fetcher options given to easy_install - are available to that command as well. - """ - # find the fetch options from easy_install and write them out - # to the setup.cfg file. - ei_opts = self.distribution.get_option_dict('easy_install').copy() - fetch_directives = ( - 'find_links', 'site_dirs', 'index_url', 'optimize', 'allow_hosts', - ) - fetch_options = {} - for key, val in ei_opts.items(): - if key not in fetch_directives: - continue - fetch_options[key.replace('_', '-')] = val[1] - # create a settings dictionary suitable for `edit_config` - settings = dict(easy_install=fetch_options) - cfg_filename = os.path.join(base, 'setup.cfg') - setopt.edit_config(cfg_filename, settings) - - def update_pth(self, dist): - if self.pth_file is None: - return - - for d in self.pth_file[dist.key]: # drop old entries - if self.multi_version or d.location != dist.location: - log.info("Removing %s from easy-install.pth file", d) - self.pth_file.remove(d) - if d.location in self.shadow_path: - self.shadow_path.remove(d.location) - - if not self.multi_version: - if dist.location in self.pth_file.paths: - log.info( - "%s is already the active version in easy-install.pth", - dist, - ) - else: - log.info("Adding %s to easy-install.pth file", dist) - self.pth_file.add(dist) # add new entry - if dist.location not in self.shadow_path: - self.shadow_path.append(dist.location) - - if not self.dry_run: - - self.pth_file.save() - - if dist.key == 'setuptools': - # Ensure that setuptools itself never becomes unavailable! - # XXX should this check for latest version? - filename = os.path.join(self.install_dir, 'setuptools.pth') - if os.path.islink(filename): - os.unlink(filename) - f = open(filename, 'wt') - f.write(self.pth_file.make_relative(dist.location) + '\n') - f.close() - - def unpack_progress(self, src, dst): - # Progress filter for unpacking - log.debug("Unpacking %s to %s", src, dst) - return dst # only unpack-and-compile skips files for dry run - - def unpack_and_compile(self, egg_path, destination): - to_compile = [] - to_chmod = [] - - def pf(src, dst): - if dst.endswith('.py') and not src.startswith('EGG-INFO/'): - to_compile.append(dst) - elif dst.endswith('.dll') or dst.endswith('.so'): - to_chmod.append(dst) - self.unpack_progress(src, dst) - return not self.dry_run and dst or None - - unpack_archive(egg_path, destination, pf) - self.byte_compile(to_compile) - if not self.dry_run: - for f in to_chmod: - mode = ((os.stat(f)[stat.ST_MODE]) | 0o555) & 0o7755 - chmod(f, mode) - - def byte_compile(self, to_compile): - if sys.dont_write_bytecode: - return - - from distutils.util import byte_compile - - try: - # try to make the byte compile messages quieter - log.set_verbosity(self.verbose - 1) - - byte_compile(to_compile, optimize=0, force=1, dry_run=self.dry_run) - if self.optimize: - byte_compile( - to_compile, optimize=self.optimize, force=1, - dry_run=self.dry_run, - ) - finally: - log.set_verbosity(self.verbose) # restore original verbosity - - __no_default_msg = textwrap.dedent(""" - bad install directory or PYTHONPATH - - You are attempting to install a package to a directory that is not - on PYTHONPATH and which Python does not read ".pth" files from. The - installation directory you specified (via --install-dir, --prefix, or - the distutils default setting) was: - - %s - - and your PYTHONPATH environment variable currently contains: - - %r - - Here are some of your options for correcting the problem: - - * You can choose a different installation directory, i.e., one that is - on PYTHONPATH or supports .pth files - - * You can add the installation directory to the PYTHONPATH environment - variable. (It must then also be on PYTHONPATH whenever you run - Python and want to use the package(s) you are installing.) - - * You can set up the installation directory to support ".pth" files by - using one of the approaches described here: - - https://setuptools.readthedocs.io/en/latest/easy_install.html#custom-installation-locations - - - Please make the appropriate changes for your system and try again.""").lstrip() - - def no_default_version_msg(self): - template = self.__no_default_msg - return template % (self.install_dir, os.environ.get('PYTHONPATH', '')) - - def install_site_py(self): - """Make sure there's a site.py in the target dir, if needed""" - - if self.sitepy_installed: - return # already did it, or don't need to - - sitepy = os.path.join(self.install_dir, "site.py") - source = resource_string("setuptools", "site-patch.py") - source = source.decode('utf-8') - current = "" - - if os.path.exists(sitepy): - log.debug("Checking existing site.py in %s", self.install_dir) - with io.open(sitepy) as strm: - current = strm.read() - - if not current.startswith('def __boot():'): - raise DistutilsError( - "%s is not a setuptools-generated site.py; please" - " remove it." % sitepy - ) - - if current != source: - log.info("Creating %s", sitepy) - if not self.dry_run: - ensure_directory(sitepy) - with io.open(sitepy, 'w', encoding='utf-8') as strm: - strm.write(source) - self.byte_compile([sitepy]) - - self.sitepy_installed = True - - def create_home_path(self): - """Create directories under ~.""" - if not self.user: - return - home = convert_path(os.path.expanduser("~")) - for name, path in six.iteritems(self.config_vars): - if path.startswith(home) and not os.path.isdir(path): - self.debug_print("os.makedirs('%s', 0o700)" % path) - os.makedirs(path, 0o700) - - if sys.version[:3] in ('2.3', '2.4', '2.5') or 'real_prefix' in sys.__dict__: - sitedir_name = 'site-packages' - else: - sitedir_name = 'dist-packages' - - INSTALL_SCHEMES = dict( - posix=dict( - install_dir='$base/lib/python$py_version_short/site-packages', - script_dir='$base/bin', - ), - unix_local = dict( - install_dir = '$base/local/lib/python$py_version_short/%s' % sitedir_name, - script_dir = '$base/local/bin', - ), - posix_local = dict( - install_dir = '$base/local/lib/python$py_version_short/%s' % sitedir_name, - script_dir = '$base/local/bin', - ), - deb_system = dict( - install_dir = '$base/lib/python3/%s' % sitedir_name, - script_dir = '$base/bin', - ), - ) - - DEFAULT_SCHEME = dict( - install_dir='$base/Lib/site-packages', - script_dir='$base/Scripts', - ) - - def _expand(self, *attrs): - config_vars = self.get_finalized_command('install').config_vars - - if self.prefix or self.install_layout: - if self.install_layout and self.install_layout in ['deb']: - scheme_name = "deb_system" - self.prefix = '/usr' - elif self.prefix or 'real_prefix' in sys.__dict__: - scheme_name = os.name - else: - scheme_name = "posix_local" - # Set default install_dir/scripts from --prefix - config_vars = config_vars.copy() - config_vars['base'] = self.prefix - scheme = self.INSTALL_SCHEMES.get(scheme_name,self.DEFAULT_SCHEME) - for attr, val in scheme.items(): - if getattr(self, attr, None) is None: - setattr(self, attr, val) - - from distutils.util import subst_vars - - for attr in attrs: - val = getattr(self, attr) - if val is not None: - val = subst_vars(val, config_vars) - if os.name == 'posix': - val = os.path.expanduser(val) - setattr(self, attr, val) - - -def _pythonpath(): - items = os.environ.get('PYTHONPATH', '').split(os.pathsep) - return filter(None, items) - - -def get_site_dirs(): - """ - Return a list of 'site' dirs - """ - - sitedirs = [] - - # start with PYTHONPATH - sitedirs.extend(_pythonpath()) - - prefixes = [sys.prefix] - if sys.exec_prefix != sys.prefix: - prefixes.append(sys.exec_prefix) - for prefix in prefixes: - if prefix: - if sys.platform in ('os2emx', 'riscos'): - sitedirs.append(os.path.join(prefix, "Lib", "site-packages")) - elif os.sep == '/': - sitedirs.extend([ - os.path.join( - prefix, - "local/lib", - "python" + sys.version[:3], - "dist-packages", - ), - os.path.join( - prefix, - "lib", - "python{}.{}".format(*sys.version_info), - "dist-packages", - ), - os.path.join(prefix, "lib", "site-python"), - ]) - else: - sitedirs.extend([ - prefix, - os.path.join(prefix, "lib", "site-packages"), - ]) - if sys.platform == 'darwin': - # for framework builds *only* we add the standard Apple - # locations. Currently only per-user, but /Library and - # /Network/Library could be added too - if 'Python.framework' in prefix: - home = os.environ.get('HOME') - if home: - home_sp = os.path.join( - home, - 'Library', - 'Python', - '{}.{}'.format(*sys.version_info), - 'site-packages', - ) - sitedirs.append(home_sp) - lib_paths = get_path('purelib'), get_path('platlib') - for site_lib in lib_paths: - if site_lib not in sitedirs: - sitedirs.append(site_lib) - - if site.ENABLE_USER_SITE: - sitedirs.append(site.USER_SITE) - - try: - sitedirs.extend(site.getsitepackages()) - except AttributeError: - pass - - sitedirs = list(map(normalize_path, sitedirs)) - - return sitedirs - - -def expand_paths(inputs): - """Yield sys.path directories that might contain "old-style" packages""" - - seen = {} - - for dirname in inputs: - dirname = normalize_path(dirname) - if dirname in seen: - continue - - seen[dirname] = 1 - if not os.path.isdir(dirname): - continue - - files = os.listdir(dirname) - yield dirname, files - - for name in files: - if not name.endswith('.pth'): - # We only care about the .pth files - continue - if name in ('easy-install.pth', 'setuptools.pth'): - # Ignore .pth files that we control - continue - - # Read the .pth file - f = open(os.path.join(dirname, name)) - lines = list(yield_lines(f)) - f.close() - - # Yield existing non-dupe, non-import directory lines from it - for line in lines: - if not line.startswith("import"): - line = normalize_path(line.rstrip()) - if line not in seen: - seen[line] = 1 - if not os.path.isdir(line): - continue - yield line, os.listdir(line) - - -def extract_wininst_cfg(dist_filename): - """Extract configuration data from a bdist_wininst .exe - - Returns a configparser.RawConfigParser, or None - """ - f = open(dist_filename, 'rb') - try: - endrec = zipfile._EndRecData(f) - if endrec is None: - return None - - prepended = (endrec[9] - endrec[5]) - endrec[6] - if prepended < 12: # no wininst data here - return None - f.seek(prepended - 12) - - tag, cfglen, bmlen = struct.unpack("egg path translations for a given .exe file""" - - prefixes = [ - ('PURELIB/', ''), - ('PLATLIB/pywin32_system32', ''), - ('PLATLIB/', ''), - ('SCRIPTS/', 'EGG-INFO/scripts/'), - ('DATA/lib/site-packages', ''), - ] - z = zipfile.ZipFile(exe_filename) - try: - for info in z.infolist(): - name = info.filename - parts = name.split('/') - if len(parts) == 3 and parts[2] == 'PKG-INFO': - if parts[1].endswith('.egg-info'): - prefixes.insert(0, ('/'.join(parts[:2]), 'EGG-INFO/')) - break - if len(parts) != 2 or not name.endswith('.pth'): - continue - if name.endswith('-nspkg.pth'): - continue - if parts[0].upper() in ('PURELIB', 'PLATLIB'): - contents = z.read(name) - if six.PY3: - contents = contents.decode() - for pth in yield_lines(contents): - pth = pth.strip().replace('\\', '/') - if not pth.startswith('import'): - prefixes.append((('%s/%s/' % (parts[0], pth)), '')) - finally: - z.close() - prefixes = [(x.lower(), y) for x, y in prefixes] - prefixes.sort() - prefixes.reverse() - return prefixes - - -class PthDistributions(Environment): - """A .pth file with Distribution paths in it""" - - dirty = False - - def __init__(self, filename, sitedirs=()): - self.filename = filename - self.sitedirs = list(map(normalize_path, sitedirs)) - self.basedir = normalize_path(os.path.dirname(self.filename)) - self._load() - Environment.__init__(self, [], None, None) - for path in yield_lines(self.paths): - list(map(self.add, find_distributions(path, True))) - - def _load(self): - self.paths = [] - saw_import = False - seen = dict.fromkeys(self.sitedirs) - if os.path.isfile(self.filename): - f = open(self.filename, 'rt') - for line in f: - if line.startswith('import'): - saw_import = True - continue - path = line.rstrip() - self.paths.append(path) - if not path.strip() or path.strip().startswith('#'): - continue - # skip non-existent paths, in case somebody deleted a package - # manually, and duplicate paths as well - path = self.paths[-1] = normalize_path( - os.path.join(self.basedir, path) - ) - if not os.path.exists(path) or path in seen: - self.paths.pop() # skip it - self.dirty = True # we cleaned up, so we're dirty now :) - continue - seen[path] = 1 - f.close() - - if self.paths and not saw_import: - self.dirty = True # ensure anything we touch has import wrappers - while self.paths and not self.paths[-1].strip(): - self.paths.pop() - - def save(self): - """Write changed .pth file back to disk""" - if not self.dirty: - return - - rel_paths = list(map(self.make_relative, self.paths)) - if rel_paths: - log.debug("Saving %s", self.filename) - lines = self._wrap_lines(rel_paths) - data = '\n'.join(lines) + '\n' - - if os.path.islink(self.filename): - os.unlink(self.filename) - with open(self.filename, 'wt') as f: - f.write(data) - - elif os.path.exists(self.filename): - log.debug("Deleting empty %s", self.filename) - os.unlink(self.filename) - - self.dirty = False - - @staticmethod - def _wrap_lines(lines): - return lines - - def add(self, dist): - """Add `dist` to the distribution map""" - new_path = ( - dist.location not in self.paths and ( - dist.location not in self.sitedirs or - # account for '.' being in PYTHONPATH - dist.location == os.getcwd() - ) - ) - if new_path: - self.paths.append(dist.location) - self.dirty = True - Environment.add(self, dist) - - def remove(self, dist): - """Remove `dist` from the distribution map""" - while dist.location in self.paths: - self.paths.remove(dist.location) - self.dirty = True - Environment.remove(self, dist) - - def make_relative(self, path): - npath, last = os.path.split(normalize_path(path)) - baselen = len(self.basedir) - parts = [last] - sep = os.altsep == '/' and '/' or os.sep - while len(npath) >= baselen: - if npath == self.basedir: - parts.append(os.curdir) - parts.reverse() - return sep.join(parts) - npath, last = os.path.split(npath) - parts.append(last) - else: - return path - - -class RewritePthDistributions(PthDistributions): - @classmethod - def _wrap_lines(cls, lines): - yield cls.prelude - for line in lines: - yield line - yield cls.postlude - - prelude = _one_liner(""" - import sys - sys.__plen = len(sys.path) - """) - postlude = _one_liner(""" - import sys - new = sys.path[sys.__plen:] - del sys.path[sys.__plen:] - p = getattr(sys, '__egginsert', 0) - sys.path[p:p] = new - sys.__egginsert = p + len(new) - """) - - -if os.environ.get('SETUPTOOLS_SYS_PATH_TECHNIQUE', 'raw') == 'rewrite': - PthDistributions = RewritePthDistributions - - -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 auto_chmod(func, arg, exc): - if func in [os.unlink, os.remove] and os.name == 'nt': - chmod(arg, stat.S_IWRITE) - return func(arg) - et, ev, _ = sys.exc_info() - six.reraise(et, (ev[0], ev[1] + (" %s %s" % (func, arg)))) - - -def update_dist_caches(dist_path, fix_zipimporter_caches): - """ - Fix any globally cached `dist_path` related data - - `dist_path` should be a path of a newly installed egg distribution (zipped - or unzipped). - - sys.path_importer_cache contains finder objects that have been cached when - importing data from the original distribution. Any such finders need to be - cleared since the replacement distribution might be packaged differently, - e.g. a zipped egg distribution might get replaced with an unzipped egg - folder or vice versa. Having the old finders cached may then cause Python - to attempt loading modules from the replacement distribution using an - incorrect loader. - - zipimport.zipimporter objects are Python loaders charged with importing - data packaged inside zip archives. If stale loaders referencing the - original distribution, are left behind, they can fail to load modules from - the replacement distribution. E.g. if an old zipimport.zipimporter instance - is used to load data from a new zipped egg archive, it may cause the - operation to attempt to locate the requested data in the wrong location - - one indicated by the original distribution's zip archive directory - information. Such an operation may then fail outright, e.g. report having - read a 'bad local file header', or even worse, it may fail silently & - return invalid data. - - zipimport._zip_directory_cache contains cached zip archive directory - information for all existing zipimport.zipimporter instances and all such - instances connected to the same archive share the same cached directory - information. - - If asked, and the underlying Python implementation allows it, we can fix - all existing zipimport.zipimporter instances instead of having to track - them down and remove them one by one, by updating their shared cached zip - archive directory information. This, of course, assumes that the - replacement distribution is packaged as a zipped egg. - - If not asked to fix existing zipimport.zipimporter instances, we still do - our best to clear any remaining zipimport.zipimporter related cached data - that might somehow later get used when attempting to load data from the new - distribution and thus cause such load operations to fail. Note that when - tracking down such remaining stale data, we can not catch every conceivable - usage from here, and we clear only those that we know of and have found to - cause problems if left alive. Any remaining caches should be updated by - whomever is in charge of maintaining them, i.e. they should be ready to - handle us replacing their zip archives with new distributions at runtime. - - """ - # There are several other known sources of stale zipimport.zipimporter - # instances that we do not clear here, but might if ever given a reason to - # do so: - # * Global setuptools pkg_resources.working_set (a.k.a. 'master working - # set') may contain distributions which may in turn contain their - # zipimport.zipimporter loaders. - # * Several zipimport.zipimporter loaders held by local variables further - # up the function call stack when running the setuptools installation. - # * Already loaded modules may have their __loader__ attribute set to the - # exact loader instance used when importing them. Python 3.4 docs state - # that this information is intended mostly for introspection and so is - # not expected to cause us problems. - normalized_path = normalize_path(dist_path) - _uncache(normalized_path, sys.path_importer_cache) - if fix_zipimporter_caches: - _replace_zip_directory_cache_data(normalized_path) - else: - # Here, even though we do not want to fix existing and now stale - # zipimporter cache information, we still want to remove it. Related to - # Python's zip archive directory information cache, we clear each of - # its stale entries in two phases: - # 1. Clear the entry so attempting to access zip archive information - # via any existing stale zipimport.zipimporter instances fails. - # 2. Remove the entry from the cache so any newly constructed - # zipimport.zipimporter instances do not end up using old stale - # zip archive directory information. - # This whole stale data removal step does not seem strictly necessary, - # but has been left in because it was done before we started replacing - # the zip archive directory information cache content if possible, and - # there are no relevant unit tests that we can depend on to tell us if - # this is really needed. - _remove_and_clear_zip_directory_cache_data(normalized_path) - - -def _collect_zipimporter_cache_entries(normalized_path, cache): - """ - Return zipimporter cache entry keys related to a given normalized path. - - Alternative path spellings (e.g. those using different character case or - those using alternative path separators) related to the same path are - included. Any sub-path entries are included as well, i.e. those - corresponding to zip archives embedded in other zip archives. - - """ - result = [] - prefix_len = len(normalized_path) - for p in cache: - np = normalize_path(p) - if (np.startswith(normalized_path) and - np[prefix_len:prefix_len + 1] in (os.sep, '')): - result.append(p) - return result - - -def _update_zipimporter_cache(normalized_path, cache, updater=None): - """ - Update zipimporter cache data for a given normalized path. - - Any sub-path entries are processed as well, i.e. those corresponding to zip - archives embedded in other zip archives. - - Given updater is a callable taking a cache entry key and the original entry - (after already removing the entry from the cache), and expected to update - the entry and possibly return a new one to be inserted in its place. - Returning None indicates that the entry should not be replaced with a new - one. If no updater is given, the cache entries are simply removed without - any additional processing, the same as if the updater simply returned None. - - """ - for p in _collect_zipimporter_cache_entries(normalized_path, cache): - # N.B. pypy's custom zipimport._zip_directory_cache implementation does - # not support the complete dict interface: - # * Does not support item assignment, thus not allowing this function - # to be used only for removing existing cache entries. - # * Does not support the dict.pop() method, forcing us to use the - # get/del patterns instead. For more detailed information see the - # following links: - # https://github.com/pypa/setuptools/issues/202#issuecomment-202913420 - # http://bit.ly/2h9itJX - old_entry = cache[p] - del cache[p] - new_entry = updater and updater(p, old_entry) - if new_entry is not None: - cache[p] = new_entry - - -def _uncache(normalized_path, cache): - _update_zipimporter_cache(normalized_path, cache) - - -def _remove_and_clear_zip_directory_cache_data(normalized_path): - def clear_and_remove_cached_zip_archive_directory_data(path, old_entry): - old_entry.clear() - - _update_zipimporter_cache( - normalized_path, zipimport._zip_directory_cache, - updater=clear_and_remove_cached_zip_archive_directory_data) - - -# PyPy Python implementation does not allow directly writing to the -# zipimport._zip_directory_cache and so prevents us from attempting to correct -# its content. The best we can do there is clear the problematic cache content -# and have PyPy repopulate it as needed. The downside is that if there are any -# stale zipimport.zipimporter instances laying around, attempting to use them -# will fail due to not having its zip archive directory information available -# instead of being automatically corrected to use the new correct zip archive -# directory information. -if '__pypy__' in sys.builtin_module_names: - _replace_zip_directory_cache_data = \ - _remove_and_clear_zip_directory_cache_data -else: - - def _replace_zip_directory_cache_data(normalized_path): - def replace_cached_zip_archive_directory_data(path, old_entry): - # N.B. In theory, we could load the zip directory information just - # once for all updated path spellings, and then copy it locally and - # update its contained path strings to contain the correct - # spelling, but that seems like a way too invasive move (this cache - # structure is not officially documented anywhere and could in - # theory change with new Python releases) for no significant - # benefit. - old_entry.clear() - zipimport.zipimporter(path) - old_entry.update(zipimport._zip_directory_cache[path]) - return old_entry - - _update_zipimporter_cache( - normalized_path, zipimport._zip_directory_cache, - updater=replace_cached_zip_archive_directory_data) - - -def is_python(text, filename=''): - "Is this string a valid Python script?" - try: - compile(text, filename, 'exec') - except (SyntaxError, TypeError): - return False - else: - return True - - -def is_sh(executable): - """Determine if the specified executable is a .sh (contains a #! line)""" - try: - with io.open(executable, encoding='latin-1') as fp: - magic = fp.read(2) - except (OSError, IOError): - return executable - return magic == '#!' - - -def nt_quote_arg(arg): - """Quote a command line argument according to Windows parsing rules""" - return subprocess.list2cmdline([arg]) - - -def is_python_script(script_text, filename): - """Is this text, as a whole, a Python script? (as opposed to shell/bat/etc. - """ - if filename.endswith('.py') or filename.endswith('.pyw'): - return True # extension says it's Python - if is_python(script_text, filename): - return True # it's syntactically valid Python - if script_text.startswith('#!'): - # It begins with a '#!' line, so check if 'python' is in it somewhere - return 'python' in script_text.splitlines()[0].lower() - - return False # Not any Python I can recognize - - -try: - from os import chmod as _chmod -except ImportError: - # Jython compatibility - def _chmod(*args): - pass - - -def chmod(path, mode): - log.debug("changing mode of %s to %o", path, mode) - try: - _chmod(path, mode) - except os.error as e: - log.debug("chmod failed: %s", e) - - -class CommandSpec(list): - """ - A command spec for a #! header, specified as a list of arguments akin to - those passed to Popen. - """ - - options = [] - split_args = dict() - - @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): - """ - Construct a CommandSpec from a parameter to build_scripts, which may - be None. - """ - if isinstance(param, cls): - return param - if isinstance(param, list): - return cls(param) - if param is None: - return cls.from_environment() - # otherwise, assume it's a string. - return cls.from_string(param) - - @classmethod - def from_environment(cls): - return cls([cls._sys_executable()]) - - @classmethod - def from_string(cls, string): - """ - 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): - 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' - - -# For pbr compat; will be removed in a future version. -sys_executable = CommandSpec._sys_executable() - - -class WindowsCommandSpec(CommandSpec): - split_args = dict(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 - __requires__ = %(spec)r - import re - import sys - from pkg_resources import 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_script_args(cls, dist, executable=None, wininst=False): - # for backward compatibility - warnings.warn("Use get_args", EasyInstallDeprecationWarning) - writer = (WindowsScriptWriter if wininst else ScriptWriter).best() - header = cls.get_script_header("", executable, wininst) - return writer.get_args(dist, header) - - @classmethod - def get_script_header(cls, script_text, executable=None, wininst=False): - # for backward compatibility - warnings.warn("Use get_header", EasyInstallDeprecationWarning, stacklevel=2) - if wininst: - executable = "python.exe" - return cls.get_header(script_text, executable) - - @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 header is None: - header = cls.get_header() - spec = str(dist.as_requirement()) - for type_ in 'console', 'gui': - group = type_ + '_scripts' - for name, ep in dist.get_entry_map(group).items(): - cls._ensure_safe_name(name) - script_text = cls.template % locals() - args = cls._get_script_args(type_, name, header, script_text) - for res in args: - yield res - - @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 get_writer(cls, force_windows): - # for backward compatibility - warnings.warn("Use best", EasyInstallDeprecationWarning) - return WindowsScriptWriter.best() if force_windows else cls.best() - - @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="", executable=None): - """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 get_writer(cls): - # for backward compatibility - warnings.warn("Use best", EasyInstallDeprecationWarning) - return cls.best() - - @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()) - warnings.warn(msg, UserWarning) - 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 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 find_executable(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') - - -# for backward-compatibility -get_script_args = ScriptWriter.get_script_args -get_script_header = ScriptWriter.get_script_header - - -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 = '%s.exe' % type - if is_64bit(): - launcher_fn = launcher_fn.replace(".", "-64.") - else: - launcher_fn = launcher_fn.replace(".", "-32.") - return resource_string('setuptools', launcher_fn) - - -def load_launcher_manifest(name): - manifest = pkg_resources.resource_string(__name__, 'launcher manifest.xml') - if six.PY2: - return manifest % vars() - else: - return manifest.decode('utf-8') % vars() - - -def rmtree(path, ignore_errors=False, onerror=auto_chmod): - return shutil.rmtree(path, ignore_errors, onerror) - - -def current_umask(): - tmp = os.umask(0o022) - os.umask(tmp) - return tmp - - -def bootstrap(): - # This function is called when setuptools*.egg is run using /bin/sh - import setuptools - - argv0 = os.path.dirname(setuptools.__path__[0]) - sys.argv[0] = argv0 - sys.argv.append(argv0) - main() - - -def main(argv=None, **kw): - from setuptools import setup - from setuptools.dist import Distribution - - class DistributionWithoutHelpCommands(Distribution): - common_usage = "" - - def _show_help(self, *args, **kw): - with _patch_usage(): - Distribution._show_help(self, *args, **kw) - - if argv is None: - argv = sys.argv[1:] - - with _patch_usage(): - setup( - script_args=['-q', 'easy_install', '-v'] + argv, - script_name=sys.argv[0] or 'easy_install', - distclass=DistributionWithoutHelpCommands, - **kw - ) - - -@contextlib.contextmanager -def _patch_usage(): - import distutils.core - USAGE = textwrap.dedent(""" - usage: %(script)s [options] requirement_or_url ... - or: %(script)s --help - """).lstrip() - - def gen_usage(script_name): - return USAGE % dict( - script=os.path.basename(script_name), - ) - - saved = distutils.core.gen_usage - distutils.core.gen_usage = gen_usage - try: - yield - finally: - distutils.core.gen_usage = saved - -class EasyInstallDeprecationWarning(SetuptoolsDeprecationWarning): - """Class for warning about deprecations in EasyInstall in SetupTools. Not ignored by default, unlike DeprecationWarning.""" - diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/egg_info.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/egg_info.py deleted file mode 100644 index b767ef3..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/egg_info.py +++ /dev/null @@ -1,717 +0,0 @@ -"""setuptools.command.egg_info - -Create a distribution's .egg-info directory and contents""" - -from distutils.filelist import FileList as _FileList -from distutils.errors import DistutilsInternalError -from distutils.util import convert_path -from distutils import log -import distutils.errors -import distutils.filelist -import os -import re -import sys -import io -import warnings -import time -import collections - -from setuptools.extern import six -from setuptools.extern.six.moves import map - -from setuptools import Command -from setuptools.command.sdist import sdist -from setuptools.command.sdist import walk_revctrl -from setuptools.command.setopt import edit_config -from setuptools.command import bdist_egg -from pkg_resources import ( - parse_requirements, safe_name, parse_version, - safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename) -import setuptools.unicode_utils as unicode_utils -from setuptools.glob import glob - -from setuptools.extern import packaging -from setuptools import SetuptoolsDeprecationWarning - -def translate_pattern(glob): - """ - Translate a file path glob like '*.txt' in to a regular expression. - This differs from fnmatch.translate which allows wildcards to match - directory separators. It also knows about '**/' which matches any number of - directories. - """ - pat = '' - - # This will split on '/' within [character classes]. This is deliberate. - chunks = glob.split(os.path.sep) - - sep = re.escape(os.sep) - valid_char = '[^%s]' % (sep,) - - for c, chunk in enumerate(chunks): - last_chunk = c == len(chunks) - 1 - - # Chunks that are a literal ** are globstars. They match anything. - if chunk == '**': - if last_chunk: - # Match anything if this is the last component - pat += '.*' - else: - # Match '(name/)*' - pat += '(?:%s+%s)*' % (valid_char, sep) - continue # Break here as the whole path component has been handled - - # Find any special characters in the remainder - i = 0 - chunk_len = len(chunk) - while i < chunk_len: - char = chunk[i] - if char == '*': - # Match any number of name characters - pat += valid_char + '*' - elif char == '?': - # Match a name character - pat += valid_char - elif char == '[': - # Character class - inner_i = i + 1 - # Skip initial !/] chars - if inner_i < chunk_len and chunk[inner_i] == '!': - inner_i = inner_i + 1 - if inner_i < chunk_len and chunk[inner_i] == ']': - inner_i = inner_i + 1 - - # Loop till the closing ] is found - while inner_i < chunk_len and chunk[inner_i] != ']': - inner_i = inner_i + 1 - - if inner_i >= chunk_len: - # Got to the end of the string without finding a closing ] - # Do not treat this as a matching group, but as a literal [ - pat += re.escape(char) - else: - # Grab the insides of the [brackets] - inner = chunk[i + 1:inner_i] - char_class = '' - - # Class negation - if inner[0] == '!': - char_class = '^' - inner = inner[1:] - - char_class += re.escape(inner) - pat += '[%s]' % (char_class,) - - # Skip to the end ] - i = inner_i - else: - pat += re.escape(char) - i += 1 - - # Join each chunk with the dir separator - if not last_chunk: - pat += sep - - pat += r'\Z' - return re.compile(pat, flags=re.MULTILINE|re.DOTALL) - - -class InfoCommon: - tag_build = None - tag_date = None - - @property - def name(self): - return safe_name(self.distribution.get_name()) - - def tagged_version(self): - version = self.distribution.get_version() - # egg_info may be called more than once for a distribution, - # in which case the version string already contains all tags. - if self.vtags and version.endswith(self.vtags): - return safe_version(version) - return safe_version(version + self.vtags) - - def tags(self): - version = '' - if self.tag_build: - version += self.tag_build - if self.tag_date: - version += time.strftime("-%Y%m%d") - return version - vtags = property(tags) - - -class egg_info(InfoCommon, Command): - description = "create a distribution's .egg-info directory" - - user_options = [ - ('egg-base=', 'e', "directory containing .egg-info directories" - " (default: top of the source tree)"), - ('tag-date', 'd', "Add date stamp (e.g. 20050528) to version number"), - ('tag-build=', 'b', "Specify explicit tag to add to version number"), - ('no-date', 'D', "Don't include date stamp [default]"), - ] - - boolean_options = ['tag-date'] - negative_opt = { - 'no-date': 'tag-date', - } - - def initialize_options(self): - self.egg_base = None - self.egg_name = None - self.egg_info = None - self.egg_version = None - self.broken_egg_info = False - - #################################### - # allow the 'tag_svn_revision' to be detected and - # set, supporting sdists built on older Setuptools. - @property - def tag_svn_revision(self): - pass - - @tag_svn_revision.setter - def tag_svn_revision(self, value): - pass - #################################### - - def save_version_info(self, filename): - """ - Materialize the value of date into the - build tag. Install build keys in a deterministic order - to avoid arbitrary reordering on subsequent builds. - """ - egg_info = collections.OrderedDict() - # follow the order these keys would have been added - # when PYTHONHASHSEED=0 - egg_info['tag_build'] = self.tags() - egg_info['tag_date'] = 0 - edit_config(filename, dict(egg_info=egg_info)) - - def finalize_options(self): - # Note: we need to capture the current value returned - # by `self.tagged_version()`, so we can later update - # `self.distribution.metadata.version` without - # repercussions. - self.egg_name = self.name - self.egg_version = self.tagged_version() - parsed_version = parse_version(self.egg_version) - - try: - is_version = isinstance(parsed_version, packaging.version.Version) - spec = ( - "%s==%s" if is_version else "%s===%s" - ) - list( - parse_requirements(spec % (self.egg_name, self.egg_version)) - ) - except ValueError: - raise distutils.errors.DistutilsOptionError( - "Invalid distribution name or version syntax: %s-%s" % - (self.egg_name, self.egg_version) - ) - - if self.egg_base is None: - dirs = self.distribution.package_dir - self.egg_base = (dirs or {}).get('', os.curdir) - - self.ensure_dirname('egg_base') - self.egg_info = to_filename(self.egg_name) + '.egg-info' - if self.egg_base != os.curdir: - self.egg_info = os.path.join(self.egg_base, self.egg_info) - if '-' in self.egg_name: - self.check_broken_egg_info() - - # Set package version for the benefit of dumber commands - # (e.g. sdist, bdist_wininst, etc.) - # - self.distribution.metadata.version = self.egg_version - - # If we bootstrapped around the lack of a PKG-INFO, as might be the - # case in a fresh checkout, make sure that any special tags get added - # to the version info - # - pd = self.distribution._patched_dist - if pd is not None and pd.key == self.egg_name.lower(): - pd._version = self.egg_version - pd._parsed_version = parse_version(self.egg_version) - self.distribution._patched_dist = None - - def write_or_delete_file(self, what, filename, data, force=False): - """Write `data` to `filename` or delete if empty - - If `data` is non-empty, this routine is the same as ``write_file()``. - If `data` is empty but not ``None``, this is the same as calling - ``delete_file(filename)`. If `data` is ``None``, then this is a no-op - unless `filename` exists, in which case a warning is issued about the - orphaned file (if `force` is false), or deleted (if `force` is true). - """ - if data: - self.write_file(what, filename, data) - elif os.path.exists(filename): - if data is None and not force: - log.warn( - "%s not set in setup(), but %s exists", what, filename - ) - return - else: - self.delete_file(filename) - - def write_file(self, what, filename, data): - """Write `data` to `filename` (if not a dry run) after announcing it - - `what` is used in a log message to identify what is being written - to the file. - """ - log.info("writing %s to %s", what, filename) - if six.PY3: - data = data.encode("utf-8") - if not self.dry_run: - f = open(filename, 'wb') - f.write(data) - f.close() - - def delete_file(self, filename): - """Delete `filename` (if not a dry run) after announcing it""" - log.info("deleting %s", filename) - if not self.dry_run: - os.unlink(filename) - - def run(self): - self.mkpath(self.egg_info) - os.utime(self.egg_info, None) - installer = self.distribution.fetch_build_egg - for ep in iter_entry_points('egg_info.writers'): - ep.require(installer=installer) - writer = ep.resolve() - writer(self, ep.name, os.path.join(self.egg_info, ep.name)) - - # Get rid of native_libs.txt if it was put there by older bdist_egg - nl = os.path.join(self.egg_info, "native_libs.txt") - if os.path.exists(nl): - self.delete_file(nl) - - self.find_sources() - - def find_sources(self): - """Generate SOURCES.txt manifest file""" - manifest_filename = os.path.join(self.egg_info, "SOURCES.txt") - mm = manifest_maker(self.distribution) - mm.manifest = manifest_filename - mm.run() - self.filelist = mm.filelist - - def check_broken_egg_info(self): - bei = self.egg_name + '.egg-info' - if self.egg_base != os.curdir: - bei = os.path.join(self.egg_base, bei) - if os.path.exists(bei): - log.warn( - "-" * 78 + '\n' - "Note: Your current .egg-info directory has a '-' in its name;" - '\nthis will not work correctly with "setup.py develop".\n\n' - 'Please rename %s to %s to correct this problem.\n' + '-' * 78, - bei, self.egg_info - ) - self.broken_egg_info = self.egg_info - self.egg_info = bei # make it work for now - - -class FileList(_FileList): - # Implementations of the various MANIFEST.in commands - - def process_template_line(self, line): - # Parse the line: split it up, make sure the right number of words - # is there, and return the relevant words. 'action' is always - # defined: it's the first word of the line. Which of the other - # three are defined depends on the action; it'll be either - # patterns, (dir and patterns), or (dir_pattern). - (action, patterns, dir, dir_pattern) = self._parse_template_line(line) - - # OK, now we know that the action is valid and we have the - # right number of words on the line for that action -- so we - # can proceed with minimal error-checking. - if action == 'include': - self.debug_print("include " + ' '.join(patterns)) - for pattern in patterns: - if not self.include(pattern): - log.warn("warning: no files found matching '%s'", pattern) - - elif action == 'exclude': - self.debug_print("exclude " + ' '.join(patterns)) - for pattern in patterns: - if not self.exclude(pattern): - log.warn(("warning: no previously-included files " - "found matching '%s'"), pattern) - - elif action == 'global-include': - self.debug_print("global-include " + ' '.join(patterns)) - for pattern in patterns: - if not self.global_include(pattern): - log.warn(("warning: no files found matching '%s' " - "anywhere in distribution"), pattern) - - elif action == 'global-exclude': - self.debug_print("global-exclude " + ' '.join(patterns)) - for pattern in patterns: - if not self.global_exclude(pattern): - log.warn(("warning: no previously-included files matching " - "'%s' found anywhere in distribution"), - pattern) - - elif action == 'recursive-include': - self.debug_print("recursive-include %s %s" % - (dir, ' '.join(patterns))) - for pattern in patterns: - if not self.recursive_include(dir, pattern): - log.warn(("warning: no files found matching '%s' " - "under directory '%s'"), - pattern, dir) - - elif action == 'recursive-exclude': - self.debug_print("recursive-exclude %s %s" % - (dir, ' '.join(patterns))) - for pattern in patterns: - if not self.recursive_exclude(dir, pattern): - log.warn(("warning: no previously-included files matching " - "'%s' found under directory '%s'"), - pattern, dir) - - elif action == 'graft': - self.debug_print("graft " + dir_pattern) - if not self.graft(dir_pattern): - log.warn("warning: no directories found matching '%s'", - dir_pattern) - - elif action == 'prune': - self.debug_print("prune " + dir_pattern) - if not self.prune(dir_pattern): - log.warn(("no previously-included directories found " - "matching '%s'"), dir_pattern) - - else: - raise DistutilsInternalError( - "this cannot happen: invalid action '%s'" % action) - - def _remove_files(self, predicate): - """ - Remove all files from the file list that match the predicate. - Return True if any matching files were removed - """ - found = False - for i in range(len(self.files) - 1, -1, -1): - if predicate(self.files[i]): - self.debug_print(" removing " + self.files[i]) - del self.files[i] - found = True - return found - - def include(self, pattern): - """Include files that match 'pattern'.""" - found = [f for f in glob(pattern) if not os.path.isdir(f)] - self.extend(found) - return bool(found) - - def exclude(self, pattern): - """Exclude files that match 'pattern'.""" - match = translate_pattern(pattern) - return self._remove_files(match.match) - - def recursive_include(self, dir, pattern): - """ - Include all files anywhere in 'dir/' that match the pattern. - """ - full_pattern = os.path.join(dir, '**', pattern) - found = [f for f in glob(full_pattern, recursive=True) - if not os.path.isdir(f)] - self.extend(found) - return bool(found) - - def recursive_exclude(self, dir, pattern): - """ - Exclude any file anywhere in 'dir/' that match the pattern. - """ - match = translate_pattern(os.path.join(dir, '**', pattern)) - return self._remove_files(match.match) - - def graft(self, dir): - """Include all files from 'dir/'.""" - found = [ - item - for match_dir in glob(dir) - for item in distutils.filelist.findall(match_dir) - ] - self.extend(found) - return bool(found) - - def prune(self, dir): - """Filter out files from 'dir/'.""" - match = translate_pattern(os.path.join(dir, '**')) - return self._remove_files(match.match) - - def global_include(self, pattern): - """ - Include all files anywhere in the current directory that match the - pattern. This is very inefficient on large file trees. - """ - if self.allfiles is None: - self.findall() - match = translate_pattern(os.path.join('**', pattern)) - found = [f for f in self.allfiles if match.match(f)] - self.extend(found) - return bool(found) - - def global_exclude(self, pattern): - """ - Exclude all files anywhere that match the pattern. - """ - match = translate_pattern(os.path.join('**', pattern)) - return self._remove_files(match.match) - - def append(self, item): - if item.endswith('\r'): # Fix older sdists built on Windows - item = item[:-1] - path = convert_path(item) - - if self._safe_path(path): - self.files.append(path) - - def extend(self, paths): - self.files.extend(filter(self._safe_path, paths)) - - def _repair(self): - """ - Replace self.files with only safe paths - - Because some owners of FileList manipulate the underlying - ``files`` attribute directly, this method must be called to - repair those paths. - """ - self.files = list(filter(self._safe_path, self.files)) - - def _safe_path(self, path): - enc_warn = "'%s' not %s encodable -- skipping" - - # To avoid accidental trans-codings errors, first to unicode - u_path = unicode_utils.filesys_decode(path) - if u_path is None: - log.warn("'%s' in unexpected encoding -- skipping" % path) - return False - - # Must ensure utf-8 encodability - utf8_path = unicode_utils.try_encode(u_path, "utf-8") - if utf8_path is None: - log.warn(enc_warn, path, 'utf-8') - return False - - try: - # accept is either way checks out - if os.path.exists(u_path) or os.path.exists(utf8_path): - return True - # this will catch any encode errors decoding u_path - except UnicodeEncodeError: - log.warn(enc_warn, path, sys.getfilesystemencoding()) - - -class manifest_maker(sdist): - template = "MANIFEST.in" - - def initialize_options(self): - self.use_defaults = 1 - self.prune = 1 - self.manifest_only = 1 - self.force_manifest = 1 - - def finalize_options(self): - pass - - def run(self): - self.filelist = FileList() - if not os.path.exists(self.manifest): - self.write_manifest() # it must exist so it'll get in the list - self.add_defaults() - if os.path.exists(self.template): - self.read_template() - self.prune_file_list() - self.filelist.sort() - self.filelist.remove_duplicates() - self.write_manifest() - - def _manifest_normalize(self, path): - path = unicode_utils.filesys_decode(path) - return path.replace(os.sep, '/') - - def write_manifest(self): - """ - Write the file list in 'self.filelist' to the manifest file - named by 'self.manifest'. - """ - self.filelist._repair() - - # Now _repairs should encodability, but not unicode - files = [self._manifest_normalize(f) for f in self.filelist.files] - msg = "writing manifest file '%s'" % self.manifest - self.execute(write_file, (self.manifest, files), msg) - - def warn(self, msg): - if not self._should_suppress_warning(msg): - sdist.warn(self, msg) - - @staticmethod - def _should_suppress_warning(msg): - """ - suppress missing-file warnings from sdist - """ - return re.match(r"standard file .*not found", msg) - - def add_defaults(self): - sdist.add_defaults(self) - self.check_license() - self.filelist.append(self.template) - self.filelist.append(self.manifest) - rcfiles = list(walk_revctrl()) - if rcfiles: - self.filelist.extend(rcfiles) - elif os.path.exists(self.manifest): - self.read_manifest() - - if os.path.exists("setup.py"): - # setup.py should be included by default, even if it's not - # the script called to create the sdist - self.filelist.append("setup.py") - - ei_cmd = self.get_finalized_command('egg_info') - self.filelist.graft(ei_cmd.egg_info) - - def prune_file_list(self): - build = self.get_finalized_command('build') - base_dir = self.distribution.get_fullname() - self.filelist.prune(build.build_base) - self.filelist.prune(base_dir) - sep = re.escape(os.sep) - self.filelist.exclude_pattern(r'(^|' + sep + r')(RCS|CVS|\.svn)' + sep, - is_regex=1) - - -def write_file(filename, contents): - """Create a file with the specified name and write 'contents' (a - sequence of strings without line terminators) to it. - """ - contents = "\n".join(contents) - - # assuming the contents has been vetted for utf-8 encoding - contents = contents.encode("utf-8") - - with open(filename, "wb") as f: # always write POSIX-style manifest - f.write(contents) - - -def write_pkg_info(cmd, basename, filename): - log.info("writing %s", filename) - if not cmd.dry_run: - metadata = cmd.distribution.metadata - metadata.version, oldver = cmd.egg_version, metadata.version - metadata.name, oldname = cmd.egg_name, metadata.name - - try: - # write unescaped data to PKG-INFO, so older pkg_resources - # can still parse it - metadata.write_pkg_info(cmd.egg_info) - finally: - metadata.name, metadata.version = oldname, oldver - - safe = getattr(cmd.distribution, 'zip_safe', None) - - bdist_egg.write_safety_flag(cmd.egg_info, safe) - - -def warn_depends_obsolete(cmd, basename, filename): - if os.path.exists(filename): - log.warn( - "WARNING: 'depends.txt' is not used by setuptools 0.6!\n" - "Use the install_requires/extras_require setup() args instead." - ) - - -def _write_requirements(stream, reqs): - lines = yield_lines(reqs or ()) - append_cr = lambda line: line + '\n' - lines = map(append_cr, sorted(lines)) - stream.writelines(lines) - - -def write_requirements(cmd, basename, filename): - dist = cmd.distribution - data = six.StringIO() - _write_requirements(data, dist.install_requires) - extras_require = dist.extras_require or {} - for extra in sorted(extras_require): - data.write('\n[{extra}]\n'.format(**vars())) - _write_requirements(data, extras_require[extra]) - cmd.write_or_delete_file("requirements", filename, data.getvalue()) - - -def write_setup_requirements(cmd, basename, filename): - data = io.StringIO() - _write_requirements(data, cmd.distribution.setup_requires) - cmd.write_or_delete_file("setup-requirements", filename, data.getvalue()) - - -def write_toplevel_names(cmd, basename, filename): - pkgs = dict.fromkeys( - [ - k.split('.', 1)[0] - for k in cmd.distribution.iter_distribution_names() - ] - ) - cmd.write_file("top-level names", filename, '\n'.join(sorted(pkgs)) + '\n') - - -def overwrite_arg(cmd, basename, filename): - write_arg(cmd, basename, filename, True) - - -def write_arg(cmd, basename, filename, force=False): - argname = os.path.splitext(basename)[0] - value = getattr(cmd.distribution, argname, None) - if value is not None: - value = '\n'.join(value) + '\n' - cmd.write_or_delete_file(argname, filename, value, force) - - -def write_entries(cmd, basename, filename): - ep = cmd.distribution.entry_points - - if isinstance(ep, six.string_types) or ep is None: - data = ep - elif ep is not None: - data = [] - for section, contents in sorted(ep.items()): - if not isinstance(contents, six.string_types): - contents = EntryPoint.parse_group(section, contents) - contents = '\n'.join(sorted(map(str, contents.values()))) - data.append('[%s]\n%s\n\n' % (section, contents)) - data = ''.join(data) - - cmd.write_or_delete_file('entry points', filename, data, True) - - -def get_pkg_info_revision(): - """ - Get a -r### off of PKG-INFO Version in case this is an sdist of - a subversion revision. - """ - warnings.warn("get_pkg_info_revision is deprecated.", EggInfoDeprecationWarning) - if os.path.exists('PKG-INFO'): - with io.open('PKG-INFO') as f: - for line in f: - match = re.match(r"Version:.*-r(\d+)\s*$", line) - if match: - return int(match.group(1)) - return 0 - - -class EggInfoDeprecationWarning(SetuptoolsDeprecationWarning): - """Class for warning about deprecations in eggInfo in setupTools. Not ignored by default, unlike DeprecationWarning.""" diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/install.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/install.py deleted file mode 100644 index 72b9a3e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/install.py +++ /dev/null @@ -1,125 +0,0 @@ -from distutils.errors import DistutilsArgError -import inspect -import glob -import warnings -import platform -import distutils.command.install as orig - -import setuptools - -# Prior to numpy 1.9, NumPy relies on the '_install' name, so provide it for -# now. See https://github.com/pypa/setuptools/issues/199/ -_install = orig.install - - -class install(orig.install): - """Use easy_install to install the package, w/dependencies""" - - user_options = orig.install.user_options + [ - ('old-and-unmanageable', None, "Try not to use this!"), - ('single-version-externally-managed', None, - "used by system package builders to create 'flat' eggs"), - ] - boolean_options = orig.install.boolean_options + [ - 'old-and-unmanageable', 'single-version-externally-managed', - ] - new_commands = [ - ('install_egg_info', lambda self: True), - ('install_scripts', lambda self: True), - ] - _nc = dict(new_commands) - - def initialize_options(self): - orig.install.initialize_options(self) - self.old_and_unmanageable = None - self.single_version_externally_managed = None - - def finalize_options(self): - orig.install.finalize_options(self) - if self.root: - self.single_version_externally_managed = True - elif self.single_version_externally_managed: - if not self.root and not self.record: - raise DistutilsArgError( - "You must specify --record or --root when building system" - " packages" - ) - - def handle_extra_path(self): - if self.root or self.single_version_externally_managed: - # explicit backward-compatibility mode, allow extra_path to work - return orig.install.handle_extra_path(self) - - # Ignore extra_path when installing an egg (or being run by another - # command without --root or --single-version-externally-managed - self.path_file = None - self.extra_dirs = '' - - def run(self): - # Explicit request for old-style install? Just do it - if self.old_and_unmanageable or self.single_version_externally_managed: - return orig.install.run(self) - - if not self._called_from_setup(inspect.currentframe()): - # Run in backward-compatibility mode to support bdist_* commands. - orig.install.run(self) - else: - self.do_egg_install() - - @staticmethod - def _called_from_setup(run_frame): - """ - Attempt to detect whether run() was called from setup() or by another - command. If called by setup(), the parent caller will be the - 'run_command' method in 'distutils.dist', and *its* caller will be - the 'run_commands' method. If called any other way, the - immediate caller *might* be 'run_command', but it won't have been - called by 'run_commands'. Return True in that case or if a call stack - is unavailable. Return False otherwise. - """ - if run_frame is None: - msg = "Call stack not available. bdist_* commands may fail." - warnings.warn(msg) - if platform.python_implementation() == 'IronPython': - msg = "For best results, pass -X:Frames to enable call stack." - warnings.warn(msg) - return True - res = inspect.getouterframes(run_frame)[2] - caller, = res[:1] - info = inspect.getframeinfo(caller) - caller_module = caller.f_globals.get('__name__', '') - return ( - caller_module == 'distutils.dist' - and info.function == 'run_commands' - ) - - def do_egg_install(self): - - easy_install = self.distribution.get_command_class('easy_install') - - cmd = easy_install( - self.distribution, args="x", root=self.root, record=self.record, - ) - cmd.ensure_finalized() # finalize before bdist_egg munges install cmd - cmd.always_copy_from = '.' # make sure local-dir eggs get installed - - # pick up setup-dir .egg files only: no .egg-info - cmd.package_index.scan(glob.glob('*.egg')) - - self.run_command('bdist_egg') - args = [self.distribution.get_command_obj('bdist_egg').egg_output] - - if setuptools.bootstrap_install_from: - # Bootstrap self-installation of setuptools - args.insert(0, setuptools.bootstrap_install_from) - - cmd.args = args - cmd.run(show_deprecation=False) - setuptools.bootstrap_install_from = None - - -# XXX Python 3.1 doesn't see _nc if this is inside the class -install.sub_commands = ( - [cmd for cmd in orig.install.sub_commands if cmd[0] not in install._nc] + - install.new_commands -) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/install_egg_info.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/install_egg_info.py deleted file mode 100644 index 5f405bc..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/install_egg_info.py +++ /dev/null @@ -1,82 +0,0 @@ -from distutils import log, dir_util -import os, sys - -from setuptools import Command -from setuptools import namespaces -from setuptools.archive_util import unpack_archive -import pkg_resources - - -class install_egg_info(namespaces.Installer, Command): - """Install an .egg-info directory for the package""" - - description = "Install an .egg-info directory for the package" - - user_options = [ - ('install-dir=', 'd', "directory to install to"), - ] - - def initialize_options(self): - self.install_dir = None - self.install_layout = None - self.prefix_option = None - - def finalize_options(self): - self.set_undefined_options('install_lib', - ('install_dir', 'install_dir')) - self.set_undefined_options('install',('install_layout','install_layout')) - if sys.hexversion > 0x2060000: - self.set_undefined_options('install',('prefix_option','prefix_option')) - ei_cmd = self.get_finalized_command("egg_info") - basename = pkg_resources.Distribution( - None, None, ei_cmd.egg_name, ei_cmd.egg_version - ).egg_name() + '.egg-info' - - if self.install_layout: - if not self.install_layout.lower() in ['deb']: - raise DistutilsOptionError("unknown value for --install-layout") - self.install_layout = self.install_layout.lower() - basename = basename.replace('-py%s' % pkg_resources.PY_MAJOR, '') - elif self.prefix_option or 'real_prefix' in sys.__dict__: - # don't modify for virtualenv - pass - else: - basename = basename.replace('-py%s' % pkg_resources.PY_MAJOR, '') - - self.source = ei_cmd.egg_info - self.target = os.path.join(self.install_dir, basename) - self.outputs = [] - - def run(self): - self.run_command('egg_info') - if os.path.isdir(self.target) and not os.path.islink(self.target): - dir_util.remove_tree(self.target, dry_run=self.dry_run) - elif os.path.exists(self.target): - self.execute(os.unlink, (self.target,), "Removing " + self.target) - if not self.dry_run: - pkg_resources.ensure_directory(self.target) - self.execute( - self.copytree, (), "Copying %s to %s" % (self.source, self.target) - ) - self.install_namespaces() - - def get_outputs(self): - return self.outputs - - def copytree(self): - # Copy the .egg-info tree to site-packages - def skimmer(src, dst): - # filter out source-control directories; note that 'src' is always - # a '/'-separated path, regardless of platform. 'dst' is a - # platform-specific path. - for skip in '.svn/', 'CVS/': - if src.startswith(skip) or '/' + skip in src: - return None - if self.install_layout and self.install_layout in ['deb'] and src.startswith('SOURCES.txt'): - log.info("Skipping SOURCES.txt") - return None - self.outputs.append(dst) - log.debug("Copying %s to %s", src, dst) - return dst - - unpack_archive(self.source, self.target, skimmer) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/install_lib.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/install_lib.py deleted file mode 100644 index bf81519..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/install_lib.py +++ /dev/null @@ -1,147 +0,0 @@ -import os -import sys -from itertools import product, starmap -import distutils.command.install_lib as orig - - -class install_lib(orig.install_lib): - """Don't add compiled flags to filenames of non-Python files""" - - def initialize_options(self): - orig.install_lib.initialize_options(self) - self.multiarch = None - self.install_layout = None - - def finalize_options(self): - orig.install_lib.finalize_options(self) - self.set_undefined_options('install',('install_layout','install_layout')) - if self.install_layout == 'deb' and sys.version_info[:2] >= (3, 3): - import sysconfig - self.multiarch = sysconfig.get_config_var('MULTIARCH') - - def run(self): - self.build() - outfiles = self.install() - if outfiles is not None: - # always compile, in case we have any extension stubs to deal with - self.byte_compile(outfiles) - - def get_exclusions(self): - """ - Return a collections.Sized collections.Container of paths to be - excluded for single_version_externally_managed installations. - """ - all_packages = ( - pkg - for ns_pkg in self._get_SVEM_NSPs() - for pkg in self._all_packages(ns_pkg) - ) - - excl_specs = product(all_packages, self._gen_exclusion_paths()) - return set(starmap(self._exclude_pkg_path, excl_specs)) - - def _exclude_pkg_path(self, pkg, exclusion_path): - """ - Given a package name and exclusion path within that package, - compute the full exclusion path. - """ - parts = pkg.split('.') + [exclusion_path] - return os.path.join(self.install_dir, *parts) - - @staticmethod - def _all_packages(pkg_name): - """ - >>> list(install_lib._all_packages('foo.bar.baz')) - ['foo.bar.baz', 'foo.bar', 'foo'] - """ - while pkg_name: - yield pkg_name - pkg_name, sep, child = pkg_name.rpartition('.') - - def _get_SVEM_NSPs(self): - """ - Get namespace packages (list) but only for - single_version_externally_managed installations and empty otherwise. - """ - # TODO: is it necessary to short-circuit here? i.e. what's the cost - # if get_finalized_command is called even when namespace_packages is - # False? - if not self.distribution.namespace_packages: - return [] - - install_cmd = self.get_finalized_command('install') - svem = install_cmd.single_version_externally_managed - - return self.distribution.namespace_packages if svem else [] - - @staticmethod - def _gen_exclusion_paths(): - """ - Generate file paths to be excluded for namespace packages (bytecode - cache files). - """ - # always exclude the package module itself - yield '__init__.py' - - yield '__init__.pyc' - yield '__init__.pyo' - - if not hasattr(sys, 'implementation'): - return - - base = os.path.join('__pycache__', '__init__.' + sys.implementation.cache_tag) - yield base + '.pyc' - yield base + '.pyo' - yield base + '.opt-1.pyc' - yield base + '.opt-2.pyc' - - def copy_tree( - self, infile, outfile, - preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1 - ): - assert preserve_mode and preserve_times and not preserve_symlinks - exclude = self.get_exclusions() - - if not exclude: - import distutils.dir_util - distutils.dir_util._multiarch = self.multiarch - return orig.install_lib.copy_tree(self, infile, outfile) - - # Exclude namespace package __init__.py* files from the output - - from setuptools.archive_util import unpack_directory - from distutils import log - - outfiles = [] - - if self.multiarch: - import sysconfig - ext_suffix = sysconfig.get_config_var ('EXT_SUFFIX') - if ext_suffix.endswith(self.multiarch + ext_suffix[-3:]): - new_suffix = None - else: - new_suffix = "%s-%s%s" % (ext_suffix[:-3], self.multiarch, ext_suffix[-3:]) - - def pf(src, dst): - if dst in exclude: - log.warn("Skipping installation of %s (namespace package)", - dst) - return False - - if self.multiarch and new_suffix and dst.endswith(ext_suffix) and not dst.endswith(new_suffix): - dst = dst.replace(ext_suffix, new_suffix) - log.info("renaming extension to %s", os.path.basename(dst)) - - log.info("copying %s -> %s", src, os.path.dirname(dst)) - outfiles.append(dst) - return dst - - unpack_directory(infile, outfile, pf) - return outfiles - - def get_outputs(self): - outputs = orig.install_lib.get_outputs(self) - exclude = self.get_exclusions() - if exclude: - return [f for f in outputs if f not in exclude] - return outputs diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/install_scripts.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/install_scripts.py deleted file mode 100644 index 1623427..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/install_scripts.py +++ /dev/null @@ -1,65 +0,0 @@ -from distutils import log -import distutils.command.install_scripts as orig -import os -import sys - -from pkg_resources import Distribution, PathMetadata, ensure_directory - - -class install_scripts(orig.install_scripts): - """Do normal script install, plus any egg_info wrapper scripts""" - - def initialize_options(self): - orig.install_scripts.initialize_options(self) - self.no_ep = False - - def run(self): - import setuptools.command.easy_install as ei - - self.run_command("egg_info") - if self.distribution.scripts: - orig.install_scripts.run(self) # run first to set up self.outfiles - else: - self.outfiles = [] - if self.no_ep: - # don't install entry point scripts into .egg file! - return - - ei_cmd = self.get_finalized_command("egg_info") - dist = Distribution( - ei_cmd.egg_base, PathMetadata(ei_cmd.egg_base, ei_cmd.egg_info), - ei_cmd.egg_name, ei_cmd.egg_version, - ) - bs_cmd = self.get_finalized_command('build_scripts') - exec_param = getattr(bs_cmd, 'executable', None) - bw_cmd = self.get_finalized_command("bdist_wininst") - is_wininst = getattr(bw_cmd, '_is_running', False) - writer = ei.ScriptWriter - if is_wininst: - exec_param = "python.exe" - writer = ei.WindowsScriptWriter - if exec_param == sys.executable: - # In case the path to the Python executable contains a space, wrap - # it so it's not split up. - exec_param = [exec_param] - # resolve the writer to the environment - writer = writer.best() - cmd = writer.command_spec_class.best().from_param(exec_param) - for args in writer.get_args(dist, cmd.as_header()): - self.write_script(*args) - - def write_script(self, script_name, contents, mode="t", *ignored): - """Write an executable file to the scripts directory""" - from setuptools.command.easy_install import chmod, current_umask - - log.info("Installing %s script to %s", script_name, self.install_dir) - target = os.path.join(self.install_dir, script_name) - self.outfiles.append(target) - - mask = current_umask() - if not self.dry_run: - ensure_directory(target) - f = open(target, "w" + mode) - f.write(contents) - f.close() - chmod(target, 0o777 - mask) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/launcher manifest.xml b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/launcher manifest.xml deleted file mode 100644 index 5972a96..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/launcher manifest.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/py36compat.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/py36compat.py deleted file mode 100644 index 61063e7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/py36compat.py +++ /dev/null @@ -1,136 +0,0 @@ -import os -from glob import glob -from distutils.util import convert_path -from distutils.command import sdist - -from setuptools.extern.six.moves import filter - - -class sdist_add_defaults: - """ - Mix-in providing forward-compatibility for functionality as found in - distutils on Python 3.7. - - Do not edit the code in this class except to update functionality - as implemented in distutils. Instead, override in the subclass. - """ - - def add_defaults(self): - """Add all the default files to self.filelist: - - README or README.txt - - setup.py - - test/test*.py - - all pure Python modules mentioned in setup script - - all files pointed by package_data (build_py) - - all files defined in data_files. - - all files defined as scripts. - - all C sources listed as part of extensions or C libraries - in the setup script (doesn't catch C headers!) - Warns if (README or README.txt) or setup.py are missing; everything - else is optional. - """ - self._add_defaults_standards() - self._add_defaults_optional() - self._add_defaults_python() - self._add_defaults_data_files() - self._add_defaults_ext() - self._add_defaults_c_libs() - self._add_defaults_scripts() - - @staticmethod - def _cs_path_exists(fspath): - """ - Case-sensitive path existence check - - >>> sdist_add_defaults._cs_path_exists(__file__) - True - >>> sdist_add_defaults._cs_path_exists(__file__.upper()) - False - """ - if not os.path.exists(fspath): - return False - # make absolute so we always have a directory - abspath = os.path.abspath(fspath) - directory, filename = os.path.split(abspath) - return filename in os.listdir(directory) - - def _add_defaults_standards(self): - standards = [self.READMES, self.distribution.script_name] - for fn in standards: - if isinstance(fn, tuple): - alts = fn - got_it = False - for fn in alts: - if self._cs_path_exists(fn): - got_it = True - self.filelist.append(fn) - break - - if not got_it: - self.warn("standard file not found: should have one of " + - ', '.join(alts)) - else: - if self._cs_path_exists(fn): - self.filelist.append(fn) - else: - self.warn("standard file '%s' not found" % fn) - - def _add_defaults_optional(self): - optional = ['test/test*.py', 'setup.cfg'] - for pattern in optional: - files = filter(os.path.isfile, glob(pattern)) - self.filelist.extend(files) - - def _add_defaults_python(self): - # build_py is used to get: - # - python modules - # - files defined in package_data - build_py = self.get_finalized_command('build_py') - - # getting python files - if self.distribution.has_pure_modules(): - self.filelist.extend(build_py.get_source_files()) - - # getting package_data files - # (computed in build_py.data_files by build_py.finalize_options) - for pkg, src_dir, build_dir, filenames in build_py.data_files: - for filename in filenames: - self.filelist.append(os.path.join(src_dir, filename)) - - def _add_defaults_data_files(self): - # getting distribution.data_files - if self.distribution.has_data_files(): - for item in self.distribution.data_files: - if isinstance(item, str): - # plain file - item = convert_path(item) - if os.path.isfile(item): - self.filelist.append(item) - else: - # a (dirname, filenames) tuple - dirname, filenames = item - for f in filenames: - f = convert_path(f) - if os.path.isfile(f): - self.filelist.append(f) - - def _add_defaults_ext(self): - if self.distribution.has_ext_modules(): - build_ext = self.get_finalized_command('build_ext') - self.filelist.extend(build_ext.get_source_files()) - - def _add_defaults_c_libs(self): - if self.distribution.has_c_libraries(): - build_clib = self.get_finalized_command('build_clib') - self.filelist.extend(build_clib.get_source_files()) - - def _add_defaults_scripts(self): - if self.distribution.has_scripts(): - build_scripts = self.get_finalized_command('build_scripts') - self.filelist.extend(build_scripts.get_source_files()) - - -if hasattr(sdist.sdist, '_add_defaults_standards'): - # disable the functionality already available upstream - class sdist_add_defaults: - pass diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/register.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/register.py deleted file mode 100644 index b8266b9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/register.py +++ /dev/null @@ -1,18 +0,0 @@ -from distutils import log -import distutils.command.register as orig - -from setuptools.errors import RemovedCommandError - - -class register(orig.register): - """Formerly used to register packages on PyPI.""" - - def run(self): - msg = ( - "The register command has been removed, use twine to upload " - + "instead (https://pypi.org/p/twine)" - ) - - self.announce("ERROR: " + msg, log.ERROR) - - raise RemovedCommandError(msg) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/rotate.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/rotate.py deleted file mode 100644 index b89353f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/rotate.py +++ /dev/null @@ -1,66 +0,0 @@ -from distutils.util import convert_path -from distutils import log -from distutils.errors import DistutilsOptionError -import os -import shutil - -from setuptools.extern import six - -from setuptools import Command - - -class rotate(Command): - """Delete older distributions""" - - description = "delete older distributions, keeping N newest files" - user_options = [ - ('match=', 'm', "patterns to match (required)"), - ('dist-dir=', 'd', "directory where the distributions are"), - ('keep=', 'k', "number of matching distributions to keep"), - ] - - boolean_options = [] - - def initialize_options(self): - self.match = None - self.dist_dir = None - self.keep = None - - def finalize_options(self): - if self.match is None: - raise DistutilsOptionError( - "Must specify one or more (comma-separated) match patterns " - "(e.g. '.zip' or '.egg')" - ) - if self.keep is None: - raise DistutilsOptionError("Must specify number of files to keep") - try: - self.keep = int(self.keep) - except ValueError: - raise DistutilsOptionError("--keep must be an integer") - if isinstance(self.match, six.string_types): - self.match = [ - convert_path(p.strip()) for p in self.match.split(',') - ] - self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) - - def run(self): - self.run_command("egg_info") - from glob import glob - - for pattern in self.match: - pattern = self.distribution.get_name() + '*' + pattern - files = glob(os.path.join(self.dist_dir, pattern)) - files = [(os.path.getmtime(f), f) for f in files] - files.sort() - files.reverse() - - log.info("%d file(s) matching %s", len(files), pattern) - files = files[self.keep:] - for (t, f) in files: - log.info("Deleting %s", f) - if not self.dry_run: - if os.path.isdir(f): - shutil.rmtree(f) - else: - os.unlink(f) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/saveopts.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/saveopts.py deleted file mode 100644 index 611cec5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/saveopts.py +++ /dev/null @@ -1,22 +0,0 @@ -from setuptools.command.setopt import edit_config, option_base - - -class saveopts(option_base): - """Save command-line options to a file""" - - description = "save supplied options to setup.cfg or other config file" - - def run(self): - dist = self.distribution - settings = {} - - for cmd in dist.command_options: - - if cmd == 'saveopts': - continue # don't save our own options! - - for opt, (src, val) in dist.get_option_dict(cmd).items(): - if src == "command line": - settings.setdefault(cmd, {})[opt] = val - - edit_config(self.filename, settings, self.dry_run) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/sdist.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/sdist.py deleted file mode 100644 index a851453..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/sdist.py +++ /dev/null @@ -1,252 +0,0 @@ -from distutils import log -import distutils.command.sdist as orig -import os -import sys -import io -import contextlib - -from setuptools.extern import six, ordered_set - -from .py36compat import sdist_add_defaults - -import pkg_resources - -_default_revctrl = list - - -def walk_revctrl(dirname=''): - """Find all files under revision control""" - for ep in pkg_resources.iter_entry_points('setuptools.file_finders'): - for item in ep.load()(dirname): - yield item - - -class sdist(sdist_add_defaults, orig.sdist): - """Smart sdist that finds anything supported by revision control""" - - user_options = [ - ('formats=', None, - "formats for source distribution (comma-separated list)"), - ('keep-temp', 'k', - "keep the distribution tree around after creating " + - "archive file(s)"), - ('dist-dir=', 'd', - "directory to put the source distribution archive(s) in " - "[default: dist]"), - ] - - negative_opt = {} - - README_EXTENSIONS = ['', '.rst', '.txt', '.md'] - READMES = tuple('README{0}'.format(ext) for ext in README_EXTENSIONS) - - def run(self): - self.run_command('egg_info') - ei_cmd = self.get_finalized_command('egg_info') - self.filelist = ei_cmd.filelist - self.filelist.append(os.path.join(ei_cmd.egg_info, 'SOURCES.txt')) - self.check_readme() - - # Run sub commands - for cmd_name in self.get_sub_commands(): - self.run_command(cmd_name) - - self.make_distribution() - - dist_files = getattr(self.distribution, 'dist_files', []) - for file in self.archive_files: - data = ('sdist', '', file) - if data not in dist_files: - dist_files.append(data) - - def initialize_options(self): - orig.sdist.initialize_options(self) - - self._default_to_gztar() - - def _default_to_gztar(self): - # only needed on Python prior to 3.6. - if sys.version_info >= (3, 6, 0, 'beta', 1): - return - self.formats = ['gztar'] - - def make_distribution(self): - """ - Workaround for #516 - """ - with self._remove_os_link(): - orig.sdist.make_distribution(self) - - @staticmethod - @contextlib.contextmanager - def _remove_os_link(): - """ - In a context, remove and restore os.link if it exists - """ - - class NoValue: - pass - - orig_val = getattr(os, 'link', NoValue) - try: - del os.link - except Exception: - pass - try: - yield - finally: - if orig_val is not NoValue: - setattr(os, 'link', orig_val) - - def __read_template_hack(self): - # This grody hack closes the template file (MANIFEST.in) if an - # exception occurs during read_template. - # Doing so prevents an error when easy_install attempts to delete the - # file. - try: - orig.sdist.read_template(self) - except Exception: - _, _, tb = sys.exc_info() - tb.tb_next.tb_frame.f_locals['template'].close() - raise - - # Beginning with Python 2.7.2, 3.1.4, and 3.2.1, this leaky file handle - # has been fixed, so only override the method if we're using an earlier - # Python. - has_leaky_handle = ( - sys.version_info < (2, 7, 2) - or (3, 0) <= sys.version_info < (3, 1, 4) - or (3, 2) <= sys.version_info < (3, 2, 1) - ) - if has_leaky_handle: - read_template = __read_template_hack - - def _add_defaults_optional(self): - if six.PY2: - sdist_add_defaults._add_defaults_optional(self) - else: - super()._add_defaults_optional() - if os.path.isfile('pyproject.toml'): - self.filelist.append('pyproject.toml') - - def _add_defaults_python(self): - """getting python files""" - if self.distribution.has_pure_modules(): - build_py = self.get_finalized_command('build_py') - self.filelist.extend(build_py.get_source_files()) - self._add_data_files(self._safe_data_files(build_py)) - - def _safe_data_files(self, build_py): - """ - Extracting data_files from build_py is known to cause - infinite recursion errors when `include_package_data` - is enabled, so suppress it in that case. - """ - if self.distribution.include_package_data: - return () - return build_py.data_files - - def _add_data_files(self, data_files): - """ - Add data files as found in build_py.data_files. - """ - self.filelist.extend( - os.path.join(src_dir, name) - for _, src_dir, _, filenames in data_files - for name in filenames - ) - - def _add_defaults_data_files(self): - try: - if six.PY2: - sdist_add_defaults._add_defaults_data_files(self) - else: - super()._add_defaults_data_files() - except TypeError: - log.warn("data_files contains unexpected objects") - - def check_readme(self): - for f in self.READMES: - if os.path.exists(f): - return - else: - self.warn( - "standard file not found: should have one of " + - ', '.join(self.READMES) - ) - - def make_release_tree(self, base_dir, files): - orig.sdist.make_release_tree(self, base_dir, files) - - # Save any egg_info command line options used to create this sdist - dest = os.path.join(base_dir, 'setup.cfg') - if hasattr(os, 'link') and os.path.exists(dest): - # unlink and re-copy, since it might be hard-linked, and - # we don't want to change the source version - os.unlink(dest) - self.copy_file('setup.cfg', dest) - - self.get_finalized_command('egg_info').save_version_info(dest) - - def _manifest_is_not_generated(self): - # check for special comment used in 2.7.1 and higher - if not os.path.isfile(self.manifest): - return False - - with io.open(self.manifest, 'rb') as fp: - first_line = fp.readline() - return (first_line != - '# file GENERATED by distutils, do NOT edit\n'.encode()) - - def read_manifest(self): - """Read the manifest file (named by 'self.manifest') and use it to - fill in 'self.filelist', the list of files to include in the source - distribution. - """ - log.info("reading manifest file '%s'", self.manifest) - manifest = open(self.manifest, 'rb') - for line in manifest: - # The manifest must contain UTF-8. See #303. - if six.PY3: - try: - line = line.decode('UTF-8') - except UnicodeDecodeError: - log.warn("%r not UTF-8 decodable -- skipping" % line) - continue - # ignore comments and blank lines - line = line.strip() - if line.startswith('#') or not line: - continue - self.filelist.append(line) - manifest.close() - - def check_license(self): - """Checks if license_file' or 'license_files' is configured and adds any - valid paths to 'self.filelist'. - """ - - files = ordered_set.OrderedSet() - - opts = self.distribution.get_option_dict('metadata') - - # ignore the source of the value - _, license_file = opts.get('license_file', (None, None)) - - if license_file is None: - log.debug("'license_file' option was not specified") - else: - files.add(license_file) - - try: - files.update(self.distribution.metadata.license_files) - except TypeError: - log.warn("warning: 'license_files' option is malformed") - - for f in files: - if not os.path.exists(f): - log.warn( - "warning: Failed to find the configured license file '%s'", - f) - files.remove(f) - - self.filelist.extend(files) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/setopt.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/setopt.py deleted file mode 100644 index 7e57cc0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/setopt.py +++ /dev/null @@ -1,149 +0,0 @@ -from distutils.util import convert_path -from distutils import log -from distutils.errors import DistutilsOptionError -import distutils -import os - -from setuptools.extern.six.moves import configparser - -from setuptools import Command - -__all__ = ['config_file', 'edit_config', 'option_base', 'setopt'] - - -def config_file(kind="local"): - """Get the filename of the distutils, local, global, or per-user config - - `kind` must be one of "local", "global", or "user" - """ - if kind == 'local': - return 'setup.cfg' - if kind == 'global': - return os.path.join( - os.path.dirname(distutils.__file__), 'distutils.cfg' - ) - if kind == 'user': - dot = os.name == 'posix' and '.' or '' - return os.path.expanduser(convert_path("~/%spydistutils.cfg" % dot)) - raise ValueError( - "config_file() type must be 'local', 'global', or 'user'", kind - ) - - -def edit_config(filename, settings, dry_run=False): - """Edit a configuration file to include `settings` - - `settings` is a dictionary of dictionaries or ``None`` values, keyed by - command/section name. A ``None`` value means to delete the entire section, - while a dictionary lists settings to be changed or deleted in that section. - A setting of ``None`` means to delete that setting. - """ - log.debug("Reading configuration from %s", filename) - opts = configparser.RawConfigParser() - opts.read([filename]) - for section, options in settings.items(): - if options is None: - log.info("Deleting section [%s] from %s", section, filename) - opts.remove_section(section) - else: - if not opts.has_section(section): - log.debug("Adding new section [%s] to %s", section, filename) - opts.add_section(section) - for option, value in options.items(): - if value is None: - log.debug( - "Deleting %s.%s from %s", - section, option, filename - ) - opts.remove_option(section, option) - if not opts.options(section): - log.info("Deleting empty [%s] section from %s", - section, filename) - opts.remove_section(section) - else: - log.debug( - "Setting %s.%s to %r in %s", - section, option, value, filename - ) - opts.set(section, option, value) - - log.info("Writing %s", filename) - if not dry_run: - with open(filename, 'w') as f: - opts.write(f) - - -class option_base(Command): - """Abstract base class for commands that mess with config files""" - - user_options = [ - ('global-config', 'g', - "save options to the site-wide distutils.cfg file"), - ('user-config', 'u', - "save options to the current user's pydistutils.cfg file"), - ('filename=', 'f', - "configuration file to use (default=setup.cfg)"), - ] - - boolean_options = [ - 'global-config', 'user-config', - ] - - def initialize_options(self): - self.global_config = None - self.user_config = None - self.filename = None - - def finalize_options(self): - filenames = [] - if self.global_config: - filenames.append(config_file('global')) - if self.user_config: - filenames.append(config_file('user')) - if self.filename is not None: - filenames.append(self.filename) - if not filenames: - filenames.append(config_file('local')) - if len(filenames) > 1: - raise DistutilsOptionError( - "Must specify only one configuration file option", - filenames - ) - self.filename, = filenames - - -class setopt(option_base): - """Save command-line options to a file""" - - description = "set an option in setup.cfg or another config file" - - user_options = [ - ('command=', 'c', 'command to set an option for'), - ('option=', 'o', 'option to set'), - ('set-value=', 's', 'value of the option'), - ('remove', 'r', 'remove (unset) the value'), - ] + option_base.user_options - - boolean_options = option_base.boolean_options + ['remove'] - - def initialize_options(self): - option_base.initialize_options(self) - self.command = None - self.option = None - self.set_value = None - self.remove = None - - def finalize_options(self): - option_base.finalize_options(self) - if self.command is None or self.option is None: - raise DistutilsOptionError("Must specify --command *and* --option") - if self.set_value is None and not self.remove: - raise DistutilsOptionError("Must specify --set-value or --remove") - - def run(self): - edit_config( - self.filename, { - self.command: {self.option.replace('-', '_'): self.set_value} - }, - self.dry_run - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/test.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/test.py deleted file mode 100644 index c148b38..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/test.py +++ /dev/null @@ -1,279 +0,0 @@ -import os -import operator -import sys -import contextlib -import itertools -import unittest -from distutils.errors import DistutilsError, DistutilsOptionError -from distutils import log -from unittest import TestLoader - -from setuptools.extern import six -from setuptools.extern.six.moves import map, filter - -from pkg_resources import (resource_listdir, resource_exists, normalize_path, - working_set, _namespace_packages, evaluate_marker, - add_activation_listener, require, EntryPoint) -from setuptools import Command -from .build_py import _unique_everseen - -__metaclass__ = type - - -class ScanningLoader(TestLoader): - - def __init__(self): - TestLoader.__init__(self) - self._visited = set() - - def loadTestsFromModule(self, module, pattern=None): - """Return a suite of all tests cases contained in the given module - - If the module is a package, load tests from all the modules in it. - If the module has an ``additional_tests`` function, call it and add - the return value to the tests. - """ - if module in self._visited: - return None - self._visited.add(module) - - tests = [] - tests.append(TestLoader.loadTestsFromModule(self, module)) - - if hasattr(module, "additional_tests"): - tests.append(module.additional_tests()) - - if hasattr(module, '__path__'): - for file in resource_listdir(module.__name__, ''): - if file.endswith('.py') and file != '__init__.py': - submodule = module.__name__ + '.' + file[:-3] - else: - if resource_exists(module.__name__, file + '/__init__.py'): - submodule = module.__name__ + '.' + file - else: - continue - tests.append(self.loadTestsFromName(submodule)) - - if len(tests) != 1: - return self.suiteClass(tests) - else: - return tests[0] # don't create a nested suite for only one return - - -# adapted from jaraco.classes.properties:NonDataProperty -class NonDataProperty: - def __init__(self, fget): - self.fget = fget - - def __get__(self, obj, objtype=None): - if obj is None: - return self - return self.fget(obj) - - -class test(Command): - """Command to run unit tests after in-place build""" - - description = "run unit tests after in-place build (deprecated)" - - user_options = [ - ('test-module=', 'm', "Run 'test_suite' in specified module"), - ('test-suite=', 's', - "Run single test, case or suite (e.g. 'module.test_suite')"), - ('test-runner=', 'r', "Test runner to use"), - ] - - def initialize_options(self): - self.test_suite = None - self.test_module = None - self.test_loader = None - self.test_runner = None - - def finalize_options(self): - - if self.test_suite and self.test_module: - msg = "You may specify a module or a suite, but not both" - raise DistutilsOptionError(msg) - - if self.test_suite is None: - if self.test_module is None: - self.test_suite = self.distribution.test_suite - else: - self.test_suite = self.test_module + ".test_suite" - - if self.test_loader is None: - self.test_loader = getattr(self.distribution, 'test_loader', None) - if self.test_loader is None: - self.test_loader = "setuptools.command.test:ScanningLoader" - if self.test_runner is None: - self.test_runner = getattr(self.distribution, 'test_runner', None) - - @NonDataProperty - def test_args(self): - return list(self._test_args()) - - def _test_args(self): - if not self.test_suite and sys.version_info >= (2, 7): - yield 'discover' - if self.verbose: - yield '--verbose' - if self.test_suite: - yield self.test_suite - - def with_project_on_sys_path(self, func): - """ - Backward compatibility for project_on_sys_path context. - """ - with self.project_on_sys_path(): - func() - - @contextlib.contextmanager - def project_on_sys_path(self, include_dists=[]): - with_2to3 = six.PY3 and getattr(self.distribution, 'use_2to3', False) - - if with_2to3: - # If we run 2to3 we can not do this inplace: - - # Ensure metadata is up-to-date - self.reinitialize_command('build_py', inplace=0) - self.run_command('build_py') - bpy_cmd = self.get_finalized_command("build_py") - build_path = normalize_path(bpy_cmd.build_lib) - - # Build extensions - self.reinitialize_command('egg_info', egg_base=build_path) - self.run_command('egg_info') - - self.reinitialize_command('build_ext', inplace=0) - self.run_command('build_ext') - else: - # Without 2to3 inplace works fine: - self.run_command('egg_info') - - # Build extensions in-place - self.reinitialize_command('build_ext', inplace=1) - self.run_command('build_ext') - - ei_cmd = self.get_finalized_command("egg_info") - - old_path = sys.path[:] - old_modules = sys.modules.copy() - - try: - project_path = normalize_path(ei_cmd.egg_base) - sys.path.insert(0, project_path) - working_set.__init__() - add_activation_listener(lambda dist: dist.activate()) - require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version)) - with self.paths_on_pythonpath([project_path]): - yield - finally: - sys.path[:] = old_path - sys.modules.clear() - sys.modules.update(old_modules) - working_set.__init__() - - @staticmethod - @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. - """ - 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 - - @staticmethod - def install_dists(dist): - """ - Install the requirements indicated by self.distribution and - return an iterable of the dists that were built. - """ - ir_d = dist.fetch_build_eggs(dist.install_requires) - tr_d = dist.fetch_build_eggs(dist.tests_require or []) - er_d = dist.fetch_build_eggs( - v for k, v in dist.extras_require.items() - if k.startswith(':') and evaluate_marker(k[1:]) - ) - return itertools.chain(ir_d, tr_d, er_d) - - def run(self): - self.announce( - "WARNING: Testing via this command is deprecated and will be " - "removed in a future version. Users looking for a generic test " - "entry point independent of test runner are encouraged to use " - "tox.", - log.WARN, - ) - - installed_dists = self.install_dists(self.distribution) - - cmd = ' '.join(self._argv) - if self.dry_run: - self.announce('skipping "%s" (dry run)' % cmd) - return - - self.announce('running "%s"' % cmd) - - paths = map(operator.attrgetter('location'), installed_dists) - with self.paths_on_pythonpath(paths): - with self.project_on_sys_path(): - self.run_tests() - - def run_tests(self): - # Purge modules under test from sys.modules. The test loader will - # re-import them from the build location. Required when 2to3 is used - # with namespace packages. - if six.PY3 and getattr(self.distribution, 'use_2to3', False): - module = self.test_suite.split('.')[0] - if module in _namespace_packages: - del_modules = [] - if module in sys.modules: - del_modules.append(module) - module += '.' - for name in sys.modules: - if name.startswith(module): - del_modules.append(name) - list(map(sys.modules.__delitem__, del_modules)) - - test = unittest.main( - None, None, self._argv, - testLoader=self._resolve_as_ep(self.test_loader), - testRunner=self._resolve_as_ep(self.test_runner), - exit=False, - ) - if not test.result.wasSuccessful(): - msg = 'Test failed: %s' % test.result - self.announce(msg, log.ERROR) - raise DistutilsError(msg) - - @property - def _argv(self): - return ['unittest'] + self.test_args - - @staticmethod - def _resolve_as_ep(val): - """ - Load the indicated attribute value, called, as a as if it were - specified as an entry point. - """ - if val is None: - return - parsed = EntryPoint.parse("x=" + val) - return parsed.resolve()() diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/upload.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/upload.py deleted file mode 100644 index ec7f81e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/upload.py +++ /dev/null @@ -1,17 +0,0 @@ -from distutils import log -from distutils.command import upload as orig - -from setuptools.errors import RemovedCommandError - - -class upload(orig.upload): - """Formerly used to upload packages to PyPI.""" - - def run(self): - msg = ( - "The upload command has been removed, use twine to upload " - + "instead (https://pypi.org/p/twine)" - ) - - self.announce("ERROR: " + msg, log.ERROR) - raise RemovedCommandError(msg) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/upload_docs.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/upload_docs.py deleted file mode 100644 index 07aa564..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/command/upload_docs.py +++ /dev/null @@ -1,206 +0,0 @@ -# -*- coding: utf-8 -*- -"""upload_docs - -Implements a Distutils 'upload_docs' subcommand (upload documentation to -PyPI's pythonhosted.org). -""" - -from base64 import standard_b64encode -from distutils import log -from distutils.errors import DistutilsOptionError -import os -import socket -import zipfile -import tempfile -import shutil -import itertools -import functools - -from setuptools.extern import six -from setuptools.extern.six.moves import http_client, urllib - -from pkg_resources import iter_entry_points -from .upload import upload - - -def _encode(s): - errors = 'surrogateescape' if six.PY3 else 'strict' - return s.encode('utf-8', errors) - - -class upload_docs(upload): - # override the default repository as upload_docs isn't - # supported by Warehouse (and won't be). - DEFAULT_REPOSITORY = 'https://pypi.python.org/pypi/' - - description = 'Upload documentation to PyPI' - - user_options = [ - ('repository=', 'r', - "url of repository [default: %s]" % upload.DEFAULT_REPOSITORY), - ('show-response', None, - 'display full response text from server'), - ('upload-dir=', None, 'directory to upload'), - ] - boolean_options = upload.boolean_options - - def has_sphinx(self): - if self.upload_dir is None: - for ep in iter_entry_points('distutils.commands', 'build_sphinx'): - return True - - sub_commands = [('build_sphinx', has_sphinx)] - - def initialize_options(self): - upload.initialize_options(self) - self.upload_dir = None - self.target_dir = None - - def finalize_options(self): - upload.finalize_options(self) - if self.upload_dir is None: - if self.has_sphinx(): - build_sphinx = self.get_finalized_command('build_sphinx') - self.target_dir = build_sphinx.builder_target_dir - else: - build = self.get_finalized_command('build') - self.target_dir = os.path.join(build.build_base, 'docs') - else: - self.ensure_dirname('upload_dir') - self.target_dir = self.upload_dir - if 'pypi.python.org' in self.repository: - log.warn("Upload_docs command is deprecated. Use RTD instead.") - self.announce('Using upload directory %s' % self.target_dir) - - def create_zipfile(self, filename): - zip_file = zipfile.ZipFile(filename, "w") - try: - self.mkpath(self.target_dir) # just in case - for root, dirs, files in os.walk(self.target_dir): - if root == self.target_dir and not files: - tmpl = "no files found in upload directory '%s'" - raise DistutilsOptionError(tmpl % self.target_dir) - for name in files: - full = os.path.join(root, name) - relative = root[len(self.target_dir):].lstrip(os.path.sep) - dest = os.path.join(relative, name) - zip_file.write(full, dest) - finally: - zip_file.close() - - def run(self): - # Run sub commands - for cmd_name in self.get_sub_commands(): - self.run_command(cmd_name) - - tmp_dir = tempfile.mkdtemp() - name = self.distribution.metadata.get_name() - zip_file = os.path.join(tmp_dir, "%s.zip" % name) - try: - self.create_zipfile(zip_file) - self.upload_file(zip_file) - finally: - shutil.rmtree(tmp_dir) - - @staticmethod - def _build_part(item, sep_boundary): - key, values = item - title = '\nContent-Disposition: form-data; name="%s"' % key - # handle multiple entries for the same name - if not isinstance(values, list): - values = [values] - for value in values: - if isinstance(value, tuple): - title += '; filename="%s"' % value[0] - value = value[1] - else: - value = _encode(value) - yield sep_boundary - yield _encode(title) - yield b"\n\n" - yield value - if value and value[-1:] == b'\r': - yield b'\n' # write an extra newline (lurve Macs) - - @classmethod - def _build_multipart(cls, data): - """ - Build up the MIME payload for the POST data - """ - boundary = b'--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' - sep_boundary = b'\n--' + boundary - end_boundary = sep_boundary + b'--' - end_items = end_boundary, b"\n", - builder = functools.partial( - cls._build_part, - sep_boundary=sep_boundary, - ) - part_groups = map(builder, data.items()) - parts = itertools.chain.from_iterable(part_groups) - body_items = itertools.chain(parts, end_items) - content_type = 'multipart/form-data; boundary=%s' % boundary.decode('ascii') - return b''.join(body_items), content_type - - def upload_file(self, filename): - with open(filename, 'rb') as f: - content = f.read() - meta = self.distribution.metadata - data = { - ':action': 'doc_upload', - 'name': meta.get_name(), - 'content': (os.path.basename(filename), content), - } - # set up the authentication - credentials = _encode(self.username + ':' + self.password) - credentials = standard_b64encode(credentials) - if six.PY3: - credentials = credentials.decode('ascii') - auth = "Basic " + credentials - - body, ct = self._build_multipart(data) - - msg = "Submitting documentation to %s" % (self.repository) - self.announce(msg, log.INFO) - - # build the Request - # We can't use urllib2 since we need to send the Basic - # auth right with the first request - schema, netloc, url, params, query, fragments = \ - urllib.parse.urlparse(self.repository) - assert not params and not query and not fragments - if schema == 'http': - conn = http_client.HTTPConnection(netloc) - elif schema == 'https': - conn = http_client.HTTPSConnection(netloc) - else: - raise AssertionError("unsupported schema " + schema) - - data = '' - try: - conn.connect() - conn.putrequest("POST", url) - content_type = ct - conn.putheader('Content-type', content_type) - conn.putheader('Content-length', str(len(body))) - conn.putheader('Authorization', auth) - conn.endheaders() - conn.send(body) - except socket.error as e: - self.announce(str(e), log.ERROR) - return - - r = conn.getresponse() - if r.status == 200: - msg = 'Server response (%s): %s' % (r.status, r.reason) - self.announce(msg, log.INFO) - elif r.status == 301: - location = r.getheader('Location') - if location is None: - location = 'https://pythonhosted.org/%s/' % meta.get_name() - msg = 'Upload successful. Visit %s' % location - self.announce(msg, log.INFO) - else: - msg = 'Upload failed (%s): %s' % (r.status, r.reason) - self.announce(msg, log.ERROR) - if self.show_response: - print('-' * 75, r.read(), '-' * 75) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/config.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/config.py deleted file mode 100644 index 9b9a0c4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/config.py +++ /dev/null @@ -1,659 +0,0 @@ -from __future__ import absolute_import, unicode_literals -import io -import os -import sys - -import warnings -import functools -from collections import defaultdict -from functools import partial -from functools import wraps -from importlib import import_module - -from distutils.errors import DistutilsOptionError, DistutilsFileError -from setuptools.extern.packaging.version import LegacyVersion, parse -from setuptools.extern.packaging.specifiers import SpecifierSet -from setuptools.extern.six import string_types, PY3 - - -__metaclass__ = type - - -def read_configuration( - filepath, find_others=False, ignore_option_errors=False): - """Read given configuration file and returns options from it as a dict. - - :param str|unicode filepath: Path to configuration file - to get options from. - - :param bool find_others: Whether to search for other configuration files - which could be on in various places. - - :param bool ignore_option_errors: Whether to silently ignore - options, values of which could not be resolved (e.g. due to exceptions - in directives such as file:, attr:, etc.). - If False exceptions are propagated as expected. - - :rtype: dict - """ - from setuptools.dist import Distribution, _Distribution - - filepath = os.path.abspath(filepath) - - if not os.path.isfile(filepath): - raise DistutilsFileError( - 'Configuration file %s does not exist.' % filepath) - - current_directory = os.getcwd() - os.chdir(os.path.dirname(filepath)) - - try: - dist = Distribution() - - filenames = dist.find_config_files() if find_others else [] - if filepath not in filenames: - filenames.append(filepath) - - _Distribution.parse_config_files(dist, filenames=filenames) - - handlers = parse_configuration( - dist, dist.command_options, - ignore_option_errors=ignore_option_errors) - - finally: - os.chdir(current_directory) - - return configuration_to_dict(handlers) - - -def _get_option(target_obj, key): - """ - Given a target object and option key, get that option from - the target object, either through a get_{key} method or - from an attribute directly. - """ - getter_name = 'get_{key}'.format(**locals()) - by_attribute = functools.partial(getattr, target_obj, key) - getter = getattr(target_obj, getter_name, by_attribute) - return getter() - - -def configuration_to_dict(handlers): - """Returns configuration data gathered by given handlers as a dict. - - :param list[ConfigHandler] handlers: Handlers list, - usually from parse_configuration() - - :rtype: dict - """ - config_dict = defaultdict(dict) - - for handler in handlers: - for option in handler.set_options: - value = _get_option(handler.target_obj, option) - config_dict[handler.section_prefix][option] = value - - return config_dict - - -def parse_configuration( - distribution, command_options, ignore_option_errors=False): - """Performs additional parsing of configuration options - for a distribution. - - Returns a list of used option handlers. - - :param Distribution distribution: - :param dict command_options: - :param bool ignore_option_errors: Whether to silently ignore - options, values of which could not be resolved (e.g. due to exceptions - in directives such as file:, attr:, etc.). - If False exceptions are propagated as expected. - :rtype: list - """ - options = ConfigOptionsHandler( - distribution, command_options, ignore_option_errors) - options.parse() - - meta = ConfigMetadataHandler( - distribution.metadata, command_options, ignore_option_errors, - distribution.package_dir) - meta.parse() - - return meta, options - - -class ConfigHandler: - """Handles metadata supplied in configuration files.""" - - section_prefix = None - """Prefix for config sections handled by this handler. - Must be provided by class heirs. - - """ - - aliases = {} - """Options aliases. - For compatibility with various packages. E.g.: d2to1 and pbr. - Note: `-` in keys is replaced with `_` by config parser. - - """ - - def __init__(self, target_obj, options, ignore_option_errors=False): - sections = {} - - section_prefix = self.section_prefix - for section_name, section_options in options.items(): - if not section_name.startswith(section_prefix): - continue - - section_name = section_name.replace(section_prefix, '').strip('.') - sections[section_name] = section_options - - self.ignore_option_errors = ignore_option_errors - self.target_obj = target_obj - self.sections = sections - self.set_options = [] - - @property - def parsers(self): - """Metadata item name to parser function mapping.""" - raise NotImplementedError( - '%s must provide .parsers property' % self.__class__.__name__) - - def __setitem__(self, option_name, value): - unknown = tuple() - target_obj = self.target_obj - - # Translate alias into real name. - option_name = self.aliases.get(option_name, option_name) - - current_value = getattr(target_obj, option_name, unknown) - - if current_value is unknown: - raise KeyError(option_name) - - if current_value: - # Already inhabited. Skipping. - return - - skip_option = False - parser = self.parsers.get(option_name) - if parser: - try: - value = parser(value) - - except Exception: - skip_option = True - if not self.ignore_option_errors: - raise - - if skip_option: - return - - setter = getattr(target_obj, 'set_%s' % option_name, None) - if setter is None: - setattr(target_obj, option_name, value) - else: - setter(value) - - self.set_options.append(option_name) - - @classmethod - def _parse_list(cls, value, separator=','): - """Represents value as a list. - - Value is split either by separator (defaults to comma) or by lines. - - :param value: - :param separator: List items separator character. - :rtype: list - """ - if isinstance(value, list): # _get_parser_compound case - return value - - if '\n' in value: - value = value.splitlines() - else: - value = value.split(separator) - - return [chunk.strip() for chunk in value if chunk.strip()] - - @classmethod - def _parse_dict(cls, value): - """Represents value as a dict. - - :param value: - :rtype: dict - """ - separator = '=' - result = {} - for line in cls._parse_list(value): - key, sep, val = line.partition(separator) - if sep != separator: - raise DistutilsOptionError( - 'Unable to parse option value to dict: %s' % value) - result[key.strip()] = val.strip() - - return result - - @classmethod - def _parse_bool(cls, value): - """Represents value as boolean. - - :param value: - :rtype: bool - """ - value = value.lower() - return value in ('1', 'true', 'yes') - - @classmethod - def _exclude_files_parser(cls, key): - """Returns a parser function to make sure field inputs - are not files. - - Parses a value after getting the key so error messages are - more informative. - - :param key: - :rtype: callable - """ - def parser(value): - exclude_directive = 'file:' - if value.startswith(exclude_directive): - raise ValueError( - 'Only strings are accepted for the {0} field, ' - 'files are not accepted'.format(key)) - return value - return parser - - @classmethod - def _parse_file(cls, value): - """Represents value as a string, allowing including text - from nearest files using `file:` directive. - - Directive is sandboxed and won't reach anything outside - directory with setup.py. - - Examples: - file: README.rst, CHANGELOG.md, src/file.txt - - :param str value: - :rtype: str - """ - include_directive = 'file:' - - if not isinstance(value, string_types): - return value - - if not value.startswith(include_directive): - return value - - spec = value[len(include_directive):] - filepaths = (os.path.abspath(path.strip()) for path in spec.split(',')) - return '\n'.join( - cls._read_file(path) - for path in filepaths - if (cls._assert_local(path) or True) - and os.path.isfile(path) - ) - - @staticmethod - def _assert_local(filepath): - if not filepath.startswith(os.getcwd()): - raise DistutilsOptionError( - '`file:` directive can not access %s' % filepath) - - @staticmethod - def _read_file(filepath): - with io.open(filepath, encoding='utf-8') as f: - return f.read() - - @classmethod - def _parse_attr(cls, value, package_dir=None): - """Represents value as a module attribute. - - Examples: - attr: package.attr - attr: package.module.attr - - :param str value: - :rtype: str - """ - attr_directive = 'attr:' - if not value.startswith(attr_directive): - return value - - attrs_path = value.replace(attr_directive, '').strip().split('.') - attr_name = attrs_path.pop() - - module_name = '.'.join(attrs_path) - module_name = module_name or '__init__' - - parent_path = os.getcwd() - if package_dir: - if attrs_path[0] in package_dir: - # A custom path was specified for the module we want to import - custom_path = package_dir[attrs_path[0]] - parts = custom_path.rsplit('/', 1) - if len(parts) > 1: - parent_path = os.path.join(os.getcwd(), parts[0]) - module_name = parts[1] - else: - module_name = custom_path - elif '' in package_dir: - # A custom parent directory was specified for all root modules - parent_path = os.path.join(os.getcwd(), package_dir['']) - sys.path.insert(0, parent_path) - try: - module = import_module(module_name) - value = getattr(module, attr_name) - - finally: - sys.path = sys.path[1:] - - return value - - @classmethod - def _get_parser_compound(cls, *parse_methods): - """Returns parser function to represents value as a list. - - Parses a value applying given methods one after another. - - :param parse_methods: - :rtype: callable - """ - def parse(value): - parsed = value - - for method in parse_methods: - parsed = method(parsed) - - return parsed - - return parse - - @classmethod - def _parse_section_to_dict(cls, section_options, values_parser=None): - """Parses section options into a dictionary. - - Optionally applies a given parser to values. - - :param dict section_options: - :param callable values_parser: - :rtype: dict - """ - value = {} - values_parser = values_parser or (lambda val: val) - for key, (_, val) in section_options.items(): - value[key] = values_parser(val) - return value - - def parse_section(self, section_options): - """Parses configuration file section. - - :param dict section_options: - """ - for (name, (_, value)) in section_options.items(): - try: - self[name] = value - - except KeyError: - pass # Keep silent for a new option may appear anytime. - - def parse(self): - """Parses configuration file items from one - or more related sections. - - """ - for section_name, section_options in self.sections.items(): - - method_postfix = '' - if section_name: # [section.option] variant - method_postfix = '_%s' % section_name - - section_parser_method = getattr( - self, - # Dots in section names are translated into dunderscores. - ('parse_section%s' % method_postfix).replace('.', '__'), - None) - - if section_parser_method is None: - raise DistutilsOptionError( - 'Unsupported distribution option section: [%s.%s]' % ( - self.section_prefix, section_name)) - - section_parser_method(section_options) - - def _deprecated_config_handler(self, func, msg, warning_class): - """ this function will wrap around parameters that are deprecated - - :param msg: deprecation message - :param warning_class: class of warning exception to be raised - :param func: function to be wrapped around - """ - @wraps(func) - def config_handler(*args, **kwargs): - warnings.warn(msg, warning_class) - return func(*args, **kwargs) - - return config_handler - - -class ConfigMetadataHandler(ConfigHandler): - - section_prefix = 'metadata' - - aliases = { - 'home_page': 'url', - 'summary': 'description', - 'classifier': 'classifiers', - 'platform': 'platforms', - } - - strict_mode = False - """We need to keep it loose, to be partially compatible with - `pbr` and `d2to1` packages which also uses `metadata` section. - - """ - - def __init__(self, target_obj, options, ignore_option_errors=False, - package_dir=None): - super(ConfigMetadataHandler, self).__init__(target_obj, options, - ignore_option_errors) - self.package_dir = package_dir - - @property - def parsers(self): - """Metadata item name to parser function mapping.""" - parse_list = self._parse_list - parse_file = self._parse_file - parse_dict = self._parse_dict - exclude_files_parser = self._exclude_files_parser - - return { - 'platforms': parse_list, - 'keywords': parse_list, - 'provides': parse_list, - 'requires': self._deprecated_config_handler( - parse_list, - "The requires parameter is deprecated, please use " - "install_requires for runtime dependencies.", - DeprecationWarning), - 'obsoletes': parse_list, - 'classifiers': self._get_parser_compound(parse_file, parse_list), - 'license': exclude_files_parser('license'), - 'license_files': parse_list, - 'description': parse_file, - 'long_description': parse_file, - 'version': self._parse_version, - 'project_urls': parse_dict, - } - - def _parse_version(self, value): - """Parses `version` option value. - - :param value: - :rtype: str - - """ - version = self._parse_file(value) - - if version != value: - version = version.strip() - # Be strict about versions loaded from file because it's easy to - # accidentally include newlines and other unintended content - if isinstance(parse(version), LegacyVersion): - tmpl = ( - 'Version loaded from {value} does not ' - 'comply with PEP 440: {version}' - ) - raise DistutilsOptionError(tmpl.format(**locals())) - - return version - - version = self._parse_attr(value, self.package_dir) - - if callable(version): - version = version() - - if not isinstance(version, string_types): - if hasattr(version, '__iter__'): - version = '.'.join(map(str, version)) - else: - version = '%s' % version - - return version - - -class ConfigOptionsHandler(ConfigHandler): - - section_prefix = 'options' - - @property - def parsers(self): - """Metadata item name to parser function mapping.""" - parse_list = self._parse_list - parse_list_semicolon = partial(self._parse_list, separator=';') - parse_bool = self._parse_bool - parse_dict = self._parse_dict - - return { - 'zip_safe': parse_bool, - 'use_2to3': parse_bool, - 'include_package_data': parse_bool, - 'package_dir': parse_dict, - 'use_2to3_fixers': parse_list, - 'use_2to3_exclude_fixers': parse_list, - 'convert_2to3_doctests': parse_list, - 'scripts': parse_list, - 'eager_resources': parse_list, - 'dependency_links': parse_list, - 'namespace_packages': parse_list, - 'install_requires': parse_list_semicolon, - 'setup_requires': parse_list_semicolon, - 'tests_require': parse_list_semicolon, - 'packages': self._parse_packages, - 'entry_points': self._parse_file, - 'py_modules': parse_list, - 'python_requires': SpecifierSet, - } - - def _parse_packages(self, value): - """Parses `packages` option value. - - :param value: - :rtype: list - """ - find_directives = ['find:', 'find_namespace:'] - trimmed_value = value.strip() - - if trimmed_value not in find_directives: - return self._parse_list(value) - - findns = trimmed_value == find_directives[1] - if findns and not PY3: - raise DistutilsOptionError( - 'find_namespace: directive is unsupported on Python < 3.3') - - # Read function arguments from a dedicated section. - find_kwargs = self.parse_section_packages__find( - self.sections.get('packages.find', {})) - - if findns: - from setuptools import find_namespace_packages as find_packages - else: - from setuptools import find_packages - - return find_packages(**find_kwargs) - - def parse_section_packages__find(self, section_options): - """Parses `packages.find` configuration file section. - - To be used in conjunction with _parse_packages(). - - :param dict section_options: - """ - section_data = self._parse_section_to_dict( - section_options, self._parse_list) - - valid_keys = ['where', 'include', 'exclude'] - - find_kwargs = dict( - [(k, v) for k, v in section_data.items() if k in valid_keys and v]) - - where = find_kwargs.get('where') - if where is not None: - find_kwargs['where'] = where[0] # cast list to single val - - return find_kwargs - - def parse_section_entry_points(self, section_options): - """Parses `entry_points` configuration file section. - - :param dict section_options: - """ - parsed = self._parse_section_to_dict(section_options, self._parse_list) - self['entry_points'] = parsed - - def _parse_package_data(self, section_options): - parsed = self._parse_section_to_dict(section_options, self._parse_list) - - root = parsed.get('*') - if root: - parsed[''] = root - del parsed['*'] - - return parsed - - def parse_section_package_data(self, section_options): - """Parses `package_data` configuration file section. - - :param dict section_options: - """ - self['package_data'] = self._parse_package_data(section_options) - - def parse_section_exclude_package_data(self, section_options): - """Parses `exclude_package_data` configuration file section. - - :param dict section_options: - """ - self['exclude_package_data'] = self._parse_package_data( - section_options) - - def parse_section_extras_require(self, section_options): - """Parses `extras_require` configuration file section. - - :param dict section_options: - """ - parse_list = partial(self._parse_list, separator=';') - self['extras_require'] = self._parse_section_to_dict( - section_options, parse_list) - - def parse_section_data_files(self, section_options): - """Parses `data_files` configuration file section. - - :param dict section_options: - """ - parsed = self._parse_section_to_dict(section_options, self._parse_list) - self['data_files'] = [(k, v) for k, v in parsed.items()] diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/dep_util.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/dep_util.py deleted file mode 100644 index 2931c13..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/dep_util.py +++ /dev/null @@ -1,23 +0,0 @@ -from distutils.dep_util import newer_group - -# yes, this is was almost entirely copy-pasted from -# 'newer_pairwise()', this is just another convenience -# function. -def newer_pairwise_group(sources_groups, targets): - """Walk both arguments in parallel, testing if each source group is newer - than its corresponding target. Returns a pair of lists (sources_groups, - targets) where sources is newer than target, according to the semantics - of 'newer_group()'. - """ - if len(sources_groups) != len(targets): - raise ValueError("'sources_group' and 'targets' must be the same length") - - # build a pair of lists (sources_groups, targets) where source is newer - n_sources = [] - n_targets = [] - for i in range(len(sources_groups)): - if newer_group(sources_groups[i], targets[i]): - n_sources.append(sources_groups[i]) - n_targets.append(targets[i]) - - return n_sources, n_targets diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/depends.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/depends.py deleted file mode 100644 index a37675c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/depends.py +++ /dev/null @@ -1,176 +0,0 @@ -import sys -import marshal -import contextlib -from distutils.version import StrictVersion - -from .py33compat import Bytecode - -from .py27compat import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE -from . import py27compat - - -__all__ = [ - 'Require', 'find_module', 'get_module_constant', 'extract_constant' -] - - -class Require: - """A prerequisite to building or installing a distribution""" - - def __init__( - self, name, requested_version, module, homepage='', - attribute=None, format=None): - - if format is None and requested_version is not None: - format = StrictVersion - - 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 '%s-%s' % (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 version >= self.requested_version - - def get_version(self, paths=None, default="unknown"): - """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) - if f: - f.close() - return default - except ImportError: - return None - - 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(version) - - -def maybe_close(f): - @contextlib.contextmanager - def empty(): - yield - return - if not f: - return empty() - - return contextlib.closing(f) - - -def get_module_constant(module, symbol, default=-1, paths=None): - """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 = py27compat.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 = py27compat.get_module(module, paths, info) - return getattr(imported, symbol, None) - - return extract_constant(code, symbol, default) - - -def extract_constant(code, symbol, default=-1): - """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 = 90 - STORE_GLOBAL = 97 - LOAD_CONST = 100 - - const = default - - for byte_code in Bytecode(code): - op = byte_code.opcode - arg = byte_code.arg - - if op == LOAD_CONST: - const = code.co_consts[arg] - elif arg == name_idx and (op == STORE_NAME or op == STORE_GLOBAL): - return const - else: - const = default - - -def _update_globals(): - """ - Patch the globals to remove the objects 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': - return - incompatible = 'extract_constant', 'get_module_constant' - for name in incompatible: - del globals()[name] - __all__.remove(name) - - -_update_globals() diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/dist.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/dist.py deleted file mode 100644 index f22429e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/dist.py +++ /dev/null @@ -1,1274 +0,0 @@ -# -*- coding: utf-8 -*- -__all__ = ['Distribution'] - -import io -import sys -import re -import os -import warnings -import numbers -import distutils.log -import distutils.core -import distutils.cmd -import distutils.dist -from distutils.util import strtobool -from distutils.debug import DEBUG -from distutils.fancy_getopt import translate_longopt -import itertools - -from collections import defaultdict -from email import message_from_file - -from distutils.errors import ( - DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError, -) -from distutils.util import rfc822_escape -from distutils.version import StrictVersion - -from setuptools.extern import six -from setuptools.extern import packaging -from setuptools.extern import ordered_set -from setuptools.extern.six.moves import map, filter, filterfalse - -from . import SetuptoolsDeprecationWarning - -from setuptools.depends import Require -from setuptools import windows_support -from setuptools.monkey import get_unpatched -from setuptools.config import parse_configuration -import pkg_resources - -__import__('setuptools.extern.packaging.specifiers') -__import__('setuptools.extern.packaging.version') - - -def _get_unpatched(cls): - warnings.warn("Do not call this function", DistDeprecationWarning) - return get_unpatched(cls) - - -def get_metadata_version(self): - mv = getattr(self, 'metadata_version', None) - - if mv is None: - if self.long_description_content_type or self.provides_extras: - mv = StrictVersion('2.1') - elif (self.maintainer is not None or - self.maintainer_email is not None or - getattr(self, 'python_requires', None) is not None or - self.project_urls): - mv = StrictVersion('1.2') - elif (self.provides or self.requires or self.obsoletes or - self.classifiers or self.download_url): - mv = StrictVersion('1.1') - else: - mv = StrictVersion('1.0') - - self.metadata_version = mv - - return mv - - -def read_pkg_file(self, file): - """Reads the metadata values from a file object.""" - msg = message_from_file(file) - - def _read_field(name): - value = msg[name] - if value == 'UNKNOWN': - return None - return value - - def _read_list(name): - values = msg.get_all(name, None) - if values == []: - return None - return values - - self.metadata_version = StrictVersion(msg['metadata-version']) - self.name = _read_field('name') - self.version = _read_field('version') - self.description = _read_field('summary') - # we are filling author only. - self.author = _read_field('author') - self.maintainer = None - self.author_email = _read_field('author-email') - self.maintainer_email = None - self.url = _read_field('home-page') - self.license = _read_field('license') - - if 'download-url' in msg: - self.download_url = _read_field('download-url') - else: - self.download_url = None - - self.long_description = _read_field('description') - self.description = _read_field('summary') - - if 'keywords' in msg: - self.keywords = _read_field('keywords').split(',') - - self.platforms = _read_list('platform') - self.classifiers = _read_list('classifier') - - # PEP 314 - these fields only exist in 1.1 - if self.metadata_version == StrictVersion('1.1'): - self.requires = _read_list('requires') - self.provides = _read_list('provides') - self.obsoletes = _read_list('obsoletes') - else: - self.requires = None - self.provides = None - self.obsoletes = None - - -# Based on Python 3.5 version -def write_pkg_file(self, file): - """Write the PKG-INFO format data to a file object. - """ - version = self.get_metadata_version() - - if six.PY2: - def write_field(key, value): - file.write("%s: %s\n" % (key, self._encode_field(value))) - else: - def write_field(key, value): - file.write("%s: %s\n" % (key, value)) - - write_field('Metadata-Version', str(version)) - write_field('Name', self.get_name()) - write_field('Version', self.get_version()) - write_field('Summary', self.get_description()) - write_field('Home-page', self.get_url()) - - if version < StrictVersion('1.2'): - write_field('Author', self.get_contact()) - write_field('Author-email', self.get_contact_email()) - else: - optional_fields = ( - ('Author', 'author'), - ('Author-email', 'author_email'), - ('Maintainer', 'maintainer'), - ('Maintainer-email', 'maintainer_email'), - ) - - for field, attr in optional_fields: - attr_val = getattr(self, attr) - - if attr_val is not None: - write_field(field, attr_val) - - write_field('License', self.get_license()) - if self.download_url: - write_field('Download-URL', self.download_url) - for project_url in self.project_urls.items(): - write_field('Project-URL', '%s, %s' % project_url) - - long_desc = rfc822_escape(self.get_long_description()) - write_field('Description', long_desc) - - keywords = ','.join(self.get_keywords()) - if keywords: - write_field('Keywords', keywords) - - if version >= StrictVersion('1.2'): - for platform in self.get_platforms(): - write_field('Platform', platform) - else: - self._write_list(file, 'Platform', self.get_platforms()) - - 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 - ) - if self.provides_extras: - for extra in sorted(self.provides_extras): - write_field('Provides-Extra', extra) - - -sequence = tuple, list - - -def check_importable(dist, attr, value): - try: - ep = pkg_resources.EntryPoint.parse('x=' + value) - assert not ep.extras - except (TypeError, ValueError, AttributeError, AssertionError): - raise DistutilsSetupError( - "%r must be importable 'module:attrs' string (got %r)" - % (attr, value) - ) - - -def assert_string_list(dist, attr, value): - """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, (list, tuple)) - # verify that elements of value are strings - assert ''.join(value) != value - except (TypeError, ValueError, AttributeError, AssertionError): - raise DistutilsSetupError( - "%r must be a list of strings (got %r)" % (attr, value) - ) - - -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( - "Distribution contains no modules or packages for " + - "namespace package %r" % nsp - ) - 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 - ) - - -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): - raise DistutilsSetupError( - "'extras_require' must be a dictionary whose values are " - "strings or lists of strings containing valid project/version " - "requirement specifiers." - ) - - -def _check_extra(extra, reqs): - name, sep, marker = extra.partition(':') - if marker and pkg_resources.invalid_marker(marker): - raise DistutilsSetupError("Invalid environment marker: " + marker) - list(pkg_resources.parse_requirements(reqs)) - - -def assert_bool(dist, attr, value): - """Verify that value is True, False, 0, or 1""" - if bool(value) != value: - tmpl = "{attr!r} must be a boolean value (got {value!r})" - raise DistutilsSetupError(tmpl.format(attr=attr, value=value)) - - -def check_requirements(dist, attr, value): - """Verify that install_requires is a valid requirements list""" - try: - list(pkg_resources.parse_requirements(value)) - if isinstance(value, (dict, set)): - raise TypeError("Unordered types are not allowed") - except (TypeError, ValueError) as error: - tmpl = ( - "{attr!r} must be a string or list of strings " - "containing valid project/version requirement specifiers; {error}" - ) - raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) - - -def check_specifier(dist, attr, value): - """Verify that value is a valid version specifier""" - try: - packaging.specifiers.SpecifierSet(value) - except packaging.specifiers.InvalidSpecifier as error: - tmpl = ( - "{attr!r} must be a string " - "containing valid version specifiers; {error}" - ) - raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) - - -def check_entry_points(dist, attr, value): - """Verify that entry_points map is parseable""" - try: - pkg_resources.EntryPoint.parse_map(value) - except ValueError as e: - raise DistutilsSetupError(e) - - -def check_test_suite(dist, attr, value): - if not isinstance(value, six.string_types): - raise DistutilsSetupError("test_suite must be a string") - - -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( - "{!r} must be a dictionary mapping package names to lists of " - "string wildcard patterns".format(attr)) - for k, v in value.items(): - if not isinstance(k, six.string_types): - raise DistutilsSetupError( - "keys of {!r} dict must be strings (got {!r})" - .format(attr, k) - ) - assert_string_list(dist, 'values of {!r} dict'.format(attr), 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 - ) - - -_Distribution = get_unpatched(distutils.core.Distribution) - - -class Distribution(_Distribution): - """Distribution with support for features, 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. - - 'features' **deprecated** -- a dictionary mapping option names to - 'setuptools.Feature' - objects. Features are a portion of the distribution that can be - included or excluded based on user options, inter-feature dependencies, - and availability on the current system. Excluded features are omitted - from all setup commands, including source and binary distributions, so - you can create multiple distributions from the same source tree. - Feature names should be valid Python identifiers, except that they may - contain the '-' (minus) sign. Features can be included or excluded - via the command line options '--with-X' and '--without-X', where 'X' is - the name of the feature. Whether a feature is included by default, and - whether you are allowed to control this from the command line, is - determined by the Feature object. See the 'Feature' class for more - information. - - 'test_suite' -- the name of a test suite to run for the 'test' command. - If the user runs 'python setup.py test', the package will be installed, - and the named test suite will be run. The format is the same as - would be used on a 'unittest.py' command line. That is, it is the - dotted name of an object to import and call to generate a test suite. - - '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. They are used by the feature subsystem to configure the - distribution for the included and excluded features. - """ - - _DISTUTILS_UNSUPPORTED_METADATA = { - 'long_description_content_type': None, - 'project_urls': dict, - 'provides_extras': ordered_set.OrderedSet, - 'license_files': ordered_set.OrderedSet, - } - - _patched_dist = None - - def patch_missing_pkg_info(self, attrs): - # Fake up a replacement for the data that would normally come from - # PKG-INFO, but which might not yet be built if this is a fresh - # checkout. - # - if not attrs or 'name' not in attrs or 'version' not in attrs: - return - key = pkg_resources.safe_name(str(attrs['name'])).lower() - dist = pkg_resources.working_set.by_key.get(key) - if dist is not None and not dist.has_metadata('PKG-INFO'): - dist._version = pkg_resources.safe_version(str(attrs['version'])) - self._patched_dist = dist - - def __init__(self, attrs=None): - have_package_data = hasattr(self, "package_data") - if not have_package_data: - self.package_data = {} - attrs = attrs or {} - if 'features' in attrs or 'require_features' in attrs: - Feature.warn_deprecated() - self.require_features = [] - self.features = {} - self.dist_files = [] - # Filter-out setuptools' specific options. - self.src_root = attrs.pop("src_root", None) - self.patch_missing_pkg_info(attrs) - self.dependency_links = attrs.pop('dependency_links', []) - self.setup_requires = attrs.pop('setup_requires', []) - for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): - vars(self).setdefault(ep.name, None) - _Distribution.__init__(self, { - k: v for k, v in attrs.items() - if k not in self._DISTUTILS_UNSUPPORTED_METADATA - }) - - # Fill-in missing metadata fields not supported by distutils. - # Note some fields may have been set by other tools (e.g. pbr) - # above; they are taken preferrentially to setup() arguments - for option, default in self._DISTUTILS_UNSUPPORTED_METADATA.items(): - for source in self.metadata.__dict__, attrs: - if option in source: - value = source[option] - break - else: - value = default() if default else None - setattr(self.metadata, option, value) - - if isinstance(self.metadata.version, numbers.Number): - # Some people apparently take "version number" too literally :) - self.metadata.version = str(self.metadata.version) - - if self.metadata.version is not None: - try: - ver = packaging.version.Version(self.metadata.version) - normalized_version = str(ver) - if self.metadata.version != normalized_version: - warnings.warn( - "Normalizing '%s' to '%s'" % ( - self.metadata.version, - normalized_version, - ) - ) - self.metadata.version = normalized_version - except (packaging.version.InvalidVersion, TypeError): - warnings.warn( - "The version specified (%r) is an invalid version, this " - "may not work as expected with newer versions of " - "setuptools, pip, and PyPI. Please see PEP 440 for more " - "details." % self.metadata.version - ) - self._finalize_requires() - - 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 - - if getattr(self, 'extras_require', None): - for extra in self.extras_require.keys(): - # Since this gets called multiple times at points where the - # keys have become 'converted' extras, ensure that we are only - # truly adding extras we haven't seen before here. - extra = extra.split(':')[0] - if extra: - self.metadata.provides_extras.add(extra) - - self._convert_extras_requirements() - self._move_install_requirements_markers() - - def _convert_extras_requirements(self): - """ - Convert requirements in `extras_require` of the form - `"extra": ["barbazquux; {marker}"]` to - `"extra:{marker}": ["barbazquux"]`. - """ - spec_ext_reqs = getattr(self, 'extras_require', None) or {} - self._tmp_extras_require = defaultdict(list) - for section, v in spec_ext_reqs.items(): - # Do not strip empty sections. - self._tmp_extras_require[section] - for r in pkg_resources.parse_requirements(v): - suffix = self._suffix_for(r) - self._tmp_extras_require[section + suffix].append(r) - - @staticmethod - def _suffix_for(req): - """ - For a requirement, return the 'extras_require' suffix for - that requirement. - """ - return ':' + str(req.marker) if req.marker else '' - - def _move_install_requirements_markers(self): - """ - Move requirements in `install_requires` that are using environment - markers `extras_require`. - """ - - # divide the install_requires into two sets, simple ones still - # handled by install_requires and more complex ones handled - # by extras_require. - - def is_simple_req(req): - return not req.marker - - spec_inst_reqs = getattr(self, 'install_requires', None) or () - inst_reqs = list(pkg_resources.parse_requirements(spec_inst_reqs)) - simple_reqs = filter(is_simple_req, inst_reqs) - complex_reqs = filterfalse(is_simple_req, inst_reqs) - self.install_requires = list(map(str, simple_reqs)) - - for r in complex_reqs: - self._tmp_extras_require[':' + str(r.marker)].append(r) - self.extras_require = dict( - (k, [str(r) for r in map(self._clean_req, v)]) - for k, v in self._tmp_extras_require.items() - ) - - def _clean_req(self, req): - """ - Given a Requirement, remove environment markers and return it. - """ - req.marker = None - return req - - def _parse_config_files(self, filenames=None): - """ - Adapted from distutils.dist.Distribution.parse_config_files, - this method provides the same functionality in subtly-improved - ways. - """ - from setuptools.extern.six.moves.configparser import ConfigParser - - # Ignore install directory options if we have a venv - if six.PY3 and sys.prefix != sys.base_prefix: - ignore_options = [ - 'install-base', 'install-platbase', 'install-lib', - 'install-platlib', 'install-purelib', 'install-headers', - 'install-scripts', 'install-data', 'prefix', 'exec-prefix', - 'home', 'user', 'root'] - else: - ignore_options = [] - - ignore_options = frozenset(ignore_options) - - if filenames is None: - filenames = self.find_config_files() - - if DEBUG: - self.announce("Distribution.parse_config_files():") - - parser = ConfigParser() - for filename in filenames: - with io.open(filename, encoding='utf-8') as reader: - if DEBUG: - self.announce(" reading {filename}".format(**locals())) - (parser.read_file if six.PY3 else parser.readfp)(reader) - for section in parser.sections(): - options = parser.options(section) - opt_dict = self.get_option_dict(section) - - for opt in options: - if opt != '__name__' and opt not in ignore_options: - val = self._try_str(parser.get(section, opt)) - opt = opt.replace('-', '_') - opt_dict[opt] = (filename, val) - - # Make the ConfigParser forget everything (so we retain - # the original filenames that options come from) - parser.__init__() - - # If there was a "global" section in the config file, use it - # to set Distribution options. - - if 'global' in self.command_options: - for (opt, (src, val)) in self.command_options['global'].items(): - alias = self.negative_opt.get(opt) - try: - if alias: - setattr(self, alias, not strtobool(val)) - elif opt in ('verbose', 'dry_run'): # ugh! - setattr(self, opt, strtobool(val)) - else: - setattr(self, opt, val) - except ValueError as msg: - raise DistutilsOptionError(msg) - - @staticmethod - def _try_str(val): - """ - On Python 2, much of distutils relies on string values being of - type 'str' (bytes) and not unicode text. If the value can be safely - encoded to bytes using the default encoding, prefer that. - - Why the default encoding? Because that value can be implicitly - decoded back to text if needed. - - Ref #1653 - """ - if six.PY3: - return val - try: - return val.encode() - except UnicodeEncodeError: - pass - return val - - def _set_command_options(self, command_obj, option_dict=None): - """ - 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(" setting options for '%s' command:" % command_name) - for (option, (source, value)) in option_dict.items(): - if DEBUG: - self.announce(" %s = %s (from %s)" % (option, value, - 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, six.string_types) - 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( - "error in %s: command '%s' has no such option '%s'" - % (source, command_name, option)) - except ValueError as msg: - raise DistutilsOptionError(msg) - - def parse_config_files(self, filenames=None, ignore_option_errors=False): - """Parses configuration files from various levels - and loads configuration. - - """ - self._parse_config_files(filenames=filenames) - - parse_configuration(self, self.command_options, - ignore_option_errors=ignore_option_errors) - self._finalize_requires() - - def parse_command_line(self): - """Process features after parsing command line options""" - result = _Distribution.parse_command_line(self) - if self.features: - self._finalize_features() - return result - - def _feature_attrname(self, name): - """Convert feature name to corresponding option attribute name""" - return 'with_' + name.replace('-', '_') - - def fetch_build_eggs(self, requires): - """Resolve pre-setup requirements""" - resolved_dists = pkg_resources.working_set.resolve( - pkg_resources.parse_requirements(requires), - installer=self.fetch_build_egg, - replace_conflicting=True, - ) - for dist in resolved_dists: - pkg_resources.working_set.add(dist, replace=True) - return resolved_dists - - def finalize_options(self): - """ - 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. - """ - hook_key = 'setuptools.finalize_distribution_options' - - def by_order(hook): - return getattr(hook, 'order', 0) - eps = pkg_resources.iter_entry_points(hook_key) - for ep in sorted(eps, key=by_order): - ep.load()(self) - - def _finalize_setup_keywords(self): - for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): - value = getattr(self, ep.name, None) - if value is not None: - ep.require(installer=self.fetch_build_egg) - ep.load()(self, ep.name, value) - - def _finalize_2to3_doctests(self): - if getattr(self, 'convert_2to3_doctests', None): - # XXX may convert to set here when we can rely on set being builtin - self.convert_2to3_doctests = [ - os.path.abspath(p) - for p in self.convert_2to3_doctests - ] - else: - self.convert_2to3_doctests = [] - - def get_egg_cache_dir(self): - 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') 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 setuptools.installer import fetch_build_egg - return fetch_build_egg(self, req) - - def _finalize_feature_opts(self): - """Add --with-X/--without-X options based on optional features""" - - if not self.features: - return - - go = [] - no = self.negative_opt.copy() - - for name, feature in self.features.items(): - self._set_feature(name, None) - feature.validate(self) - - if feature.optional: - descr = feature.description - incdef = ' (default)' - excdef = '' - if not feature.include_by_default(): - excdef, incdef = incdef, excdef - - new = ( - ('with-' + name, None, 'include ' + descr + incdef), - ('without-' + name, None, 'exclude ' + descr + excdef), - ) - go.extend(new) - no['without-' + name] = 'with-' + name - - self.global_options = self.feature_options = go + self.global_options - self.negative_opt = self.feature_negopt = no - - def _finalize_features(self): - """Add/remove features and resolve dependencies between them""" - - # First, flag all the enabled items (and thus their dependencies) - for name, feature in self.features.items(): - enabled = self.feature_is_included(name) - if enabled or (enabled is None and feature.include_by_default()): - feature.include_in(self) - self._set_feature(name, 1) - - # Then disable the rest, so that off-by-default features don't - # get flagged as errors when they're required by an enabled feature - for name, feature in self.features.items(): - if not self.feature_is_included(name): - feature.exclude_from(self) - self._set_feature(name, 0) - - def get_command_class(self, command): - """Pluggable version of get_command_class()""" - if command in self.cmdclass: - return self.cmdclass[command] - - eps = pkg_resources.iter_entry_points('distutils.commands', command) - for ep in eps: - ep.require(installer=self.fetch_build_egg) - self.cmdclass[command] = cmdclass = ep.load() - return cmdclass - else: - return _Distribution.get_command_class(self, command) - - def print_commands(self): - for ep in pkg_resources.iter_entry_points('distutils.commands'): - if ep.name not in self.cmdclass: - # don't require extras as the commands won't be invoked - cmdclass = ep.resolve() - self.cmdclass[ep.name] = cmdclass - return _Distribution.print_commands(self) - - def get_command_list(self): - for ep in pkg_resources.iter_entry_points('distutils.commands'): - if ep.name not in self.cmdclass: - # don't require extras as the commands won't be invoked - cmdclass = ep.resolve() - self.cmdclass[ep.name] = cmdclass - return _Distribution.get_command_list(self) - - def _set_feature(self, name, status): - """Set feature's inclusion status""" - setattr(self, self._feature_attrname(name), status) - - def feature_is_included(self, name): - """Return 1 if feature is included, 0 if excluded, 'None' if unknown""" - return getattr(self, self._feature_attrname(name)) - - def include_feature(self, name): - """Request inclusion of feature named 'name'""" - - if self.feature_is_included(name) == 0: - descr = self.features[name].description - raise DistutilsOptionError( - descr + " is required, but was excluded or is not available" - ) - self.features[name].include_in(self) - self._set_feature(name, 1) - - def include(self, **attrs): - """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): - """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): - """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 - - def _exclude_misc(self, name, value): - """Handle 'exclude()' for list/tuple attrs without a special handler""" - if not isinstance(value, sequence): - raise DistutilsSetupError( - "%s: setting must be a list or tuple (%r)" % (name, value) - ) - try: - old = getattr(self, name) - except AttributeError: - raise DistutilsSetupError( - "%s: No such distribution setting" % name - ) - 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, value): - """Handle 'include()' for list/tuple attrs without a special handler""" - - if not isinstance(value, sequence): - raise DistutilsSetupError( - "%s: setting must be a list (%r)" % (name, value) - ) - try: - old = getattr(self, name) - except AttributeError: - raise DistutilsSetupError( - "%s: No such distribution setting" % name - ) - 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, old + new) - - def exclude(self, **attrs): - """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): - if not isinstance(packages, sequence): - raise DistutilsSetupError( - "packages: setting must be a list or tuple (%r)" % (packages,) - ) - 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): - """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 = {} - - for cmd, opts in self.command_options.items(): - - 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""" - - for pkg in self.packages or (): - yield pkg - - for module in self.py_modules or (): - yield module - - 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 six.PY2 or 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 - errors = sys.stdout.errors - newline = sys.platform != 'win32' and '\n' or None - line_buffering = sys.stdout.line_buffering - - sys.stdout = io.TextIOWrapper( - sys.stdout.detach(), 'utf-8', errors, newline, line_buffering) - try: - return _Distribution.handle_display_options(self, option_order) - finally: - sys.stdout = io.TextIOWrapper( - sys.stdout.detach(), encoding, errors, newline, line_buffering) - - -class Feature: - """ - **deprecated** -- The `Feature` facility was never completely implemented - or supported, `has reported issues - `_ and will be removed in - a future version. - - A subset of the distribution that can be excluded if unneeded/wanted - - Features are created using these keyword arguments: - - 'description' -- a short, human readable description of the feature, to - be used in error messages, and option help messages. - - 'standard' -- if true, the feature is included by default if it is - available on the current system. Otherwise, the feature is only - included if requested via a command line '--with-X' option, or if - another included feature requires it. The default setting is 'False'. - - 'available' -- if true, the feature is available for installation on the - current system. The default setting is 'True'. - - 'optional' -- if true, the feature's inclusion can be controlled from the - command line, using the '--with-X' or '--without-X' options. If - false, the feature's inclusion status is determined automatically, - based on 'availabile', 'standard', and whether any other feature - requires it. The default setting is 'True'. - - 'require_features' -- a string or sequence of strings naming features - that should also be included if this feature is included. Defaults to - empty list. May also contain 'Require' objects that should be - added/removed from the distribution. - - 'remove' -- a string or list of strings naming packages to be removed - from the distribution if this feature is *not* included. If the - feature *is* included, this argument is ignored. This argument exists - to support removing features that "crosscut" a distribution, such as - defining a 'tests' feature that removes all the 'tests' subpackages - provided by other features. The default for this argument is an empty - list. (Note: the named package(s) or modules must exist in the base - distribution when the 'setup()' function is initially called.) - - other keywords -- any other keyword arguments are saved, and passed to - the distribution's 'include()' and 'exclude()' methods when the - feature is included or excluded, respectively. So, for example, you - could pass 'packages=["a","b"]' to cause packages 'a' and 'b' to be - added or removed from the distribution as appropriate. - - A feature must include at least one 'requires', 'remove', or other - keyword argument. Otherwise, it can't affect the distribution in any way. - Note also that you can subclass 'Feature' to create your own specialized - feature types that modify the distribution in other ways when included or - excluded. See the docstrings for the various methods here for more detail. - Aside from the methods, the only feature attributes that distributions look - at are 'description' and 'optional'. - """ - - @staticmethod - def warn_deprecated(): - msg = ( - "Features are deprecated and will be removed in a future " - "version. See https://github.com/pypa/setuptools/issues/65." - ) - warnings.warn(msg, DistDeprecationWarning, stacklevel=3) - - def __init__( - self, description, standard=False, available=True, - optional=True, require_features=(), remove=(), **extras): - self.warn_deprecated() - - self.description = description - self.standard = standard - self.available = available - self.optional = optional - if isinstance(require_features, (str, Require)): - require_features = require_features, - - self.require_features = [ - r for r in require_features if isinstance(r, str) - ] - er = [r for r in require_features if not isinstance(r, str)] - if er: - extras['require_features'] = er - - if isinstance(remove, str): - remove = remove, - self.remove = remove - self.extras = extras - - if not remove and not require_features and not extras: - raise DistutilsSetupError( - "Feature %s: must define 'require_features', 'remove', or " - "at least one of 'packages', 'py_modules', etc." - ) - - def include_by_default(self): - """Should this feature be included by default?""" - return self.available and self.standard - - def include_in(self, dist): - """Ensure feature and its requirements are included in distribution - - You may override this in a subclass to perform additional operations on - the distribution. Note that this method may be called more than once - per feature, and so should be idempotent. - - """ - - if not self.available: - raise DistutilsPlatformError( - self.description + " is required, " - "but is not available on this platform" - ) - - dist.include(**self.extras) - - for f in self.require_features: - dist.include_feature(f) - - def exclude_from(self, dist): - """Ensure feature is excluded from distribution - - You may override this in a subclass to perform additional operations on - the distribution. This method will be called at most once per - feature, and only after all included features have been asked to - include themselves. - """ - - dist.exclude(**self.extras) - - if self.remove: - for item in self.remove: - dist.exclude_package(item) - - def validate(self, dist): - """Verify that feature makes sense in context of distribution - - This method is called by the distribution just before it parses its - command line. It checks to ensure that the 'remove' attribute, if any, - contains only valid package/module names that are present in the base - distribution when 'setup()' is called. You may override it in a - subclass to perform any other required validation of the feature - against a target distribution. - """ - - for item in self.remove: - if not dist.has_contents_for(item): - raise DistutilsSetupError( - "%s wants to be able to remove %s, but the distribution" - " doesn't contain any packages or modules under %s" - % (self.description, item, item) - ) - - -class DistDeprecationWarning(SetuptoolsDeprecationWarning): - """Class for warning about deprecations in dist in - setuptools. Not ignored by default, unlike DeprecationWarning.""" diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/errors.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/errors.py deleted file mode 100644 index 2701747..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/errors.py +++ /dev/null @@ -1,16 +0,0 @@ -"""setuptools.errors - -Provides exceptions used by setuptools modules. -""" - -from distutils.errors import DistutilsError - - -class RemovedCommandError(DistutilsError, RuntimeError): - """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``. - """ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/extension.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/extension.py deleted file mode 100644 index 2946889..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/extension.py +++ /dev/null @@ -1,57 +0,0 @@ -import re -import functools -import distutils.core -import distutils.errors -import distutils.extension - -from setuptools.extern.six.moves import map - -from .monkey import get_unpatched - - -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 - return True - except Exception: - pass - return False - - -# for compatibility -have_pyrex = _have_cython - -_Extension = get_unpatched(distutils.core.Extension) - - -class Extension(_Extension): - """Extension that uses '.c' files in place of '.pyx' files""" - - def __init__(self, name, sources, *args, **kw): - # 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 = kw.pop("py_limited_api", False) - _Extension.__init__(self, name, sources, *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/plotter-app/venv/lib/python3.8/site-packages/setuptools/extern/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/extern/__init__.py deleted file mode 100644 index e8c616f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/extern/__init__.py +++ /dev/null @@ -1,73 +0,0 @@ -import sys - - -class VendorImporter: - """ - A PEP 302 meta path importer for finding optionally-vendored - or otherwise naturally-installed packages from root_name. - """ - - def __init__(self, root_name, vendored_names=(), vendor_pkg=None): - self.root_name = root_name - self.vendored_names = set(vendored_names) - self.vendor_pkg = vendor_pkg or root_name.replace('extern', '_vendor') - - @property - def search_path(self): - """ - Search first the vendor package then as a natural package. - """ - yield self.vendor_pkg + '.' - yield '' - - def find_module(self, fullname, path=None): - """ - Return self when fullname starts with root_name and the - target module is one vendored through this importer. - """ - root, base, target = fullname.partition(self.root_name + '.') - if root: - return - if not any(map(target.startswith, self.vendored_names)): - return - return self - - def load_module(self, fullname): - """ - Iterate over the search path to locate and load fullname. - """ - root, base, target = fullname.partition(self.root_name + '.') - for prefix in self.search_path: - try: - extant = prefix + target - __import__(extant) - mod = sys.modules[extant] - sys.modules[fullname] = mod - # mysterious hack: - # Remove the reference to the extant package/module - # on later Python versions to cause relative imports - # in the vendor package to resolve the same modules - # as those going through this importer. - if sys.version_info >= (3, ): - del sys.modules[extant] - return mod - except ImportError: - pass - else: - raise ImportError( - "The '{target}' package is required; " - "normally this is bundled with this package so if you get " - "this warning, consult the packager of your " - "distribution.".format(**locals()) - ) - - def install(self): - """ - Install this importer into sys.meta_path if not already present. - """ - if self not in sys.meta_path: - sys.meta_path.append(self) - - -names = 'six', 'packaging', 'pyparsing', 'ordered_set', -VendorImporter(__name__, names, 'setuptools._vendor').install() diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/extern/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/setuptools/extern/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 84d8f59..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/extern/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/glob.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/glob.py deleted file mode 100644 index 9d7cbc5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/glob.py +++ /dev/null @@ -1,174 +0,0 @@ -""" -Filename globbing utility. Mostly a copy of `glob` from Python 3.5. - -Changes include: - * `yield from` and PEP3102 `*` removed. - * Hidden files are not ignored. -""" - -import os -import re -import fnmatch - -__all__ = ["glob", "iglob", "escape"] - - -def glob(pathname, recursive=False): - """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, recursive=False): - """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, recursive): - dirname, basename = os.path.split(pathname) - 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: - if recursive and _isrecursive(basename): - for x in glob2(dirname, basename): - yield x - else: - for x in glob1(dirname, basename): - yield x - 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 = _iglob(dirname, recursive) - else: - dirs = [dirname] - if has_magic(basename): - if recursive and _isrecursive(basename): - glob_in_dir = glob2 - else: - glob_in_dir = glob1 - else: - 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). - - -def glob1(dirname, pattern): - if not dirname: - if isinstance(pattern, bytes): - dirname = os.curdir.encode('ASCII') - else: - dirname = os.curdir - try: - names = os.listdir(dirname) - except OSError: - return [] - return fnmatch.filter(names, pattern) - - -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. - - -def glob2(dirname, pattern): - assert _isrecursive(pattern) - yield pattern[:0] - for x in _rlistdir(dirname): - yield x - - -# Recursively yields relative pathnames inside a literal directory. -def _rlistdir(dirname): - if not dirname: - if isinstance(dirname, bytes): - dirname = os.curdir.encode('ASCII') - else: - dirname = os.curdir - try: - names = os.listdir(dirname) - except os.error: - return - for x in names: - yield x - path = os.path.join(dirname, x) if dirname else x - for y in _rlistdir(path): - yield os.path.join(x, y) - - -magic_check = re.compile('([*?[])') -magic_check_bytes = re.compile(b'([*?[])') - - -def has_magic(s): - if isinstance(s, bytes): - match = magic_check_bytes.search(s) - else: - match = magic_check.search(s) - return match is not None - - -def _isrecursive(pattern): - 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(br'[\1]', pathname) - else: - pathname = magic_check.sub(r'[\1]', pathname) - return drive + pathname diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/gui-32.exe b/plotter-app/venv/lib/python3.8/site-packages/setuptools/gui-32.exe deleted file mode 100644 index f8d3509..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/gui-32.exe and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/gui-64.exe b/plotter-app/venv/lib/python3.8/site-packages/setuptools/gui-64.exe deleted file mode 100644 index 330c51a..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/gui-64.exe and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/gui.exe b/plotter-app/venv/lib/python3.8/site-packages/setuptools/gui.exe deleted file mode 100644 index f8d3509..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/setuptools/gui.exe and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/installer.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/installer.py deleted file mode 100644 index 9f8be2e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/installer.py +++ /dev/null @@ -1,150 +0,0 @@ -import glob -import os -import subprocess -import sys -from distutils import log -from distutils.errors import DistutilsError - -import pkg_resources -from setuptools.command.easy_install import easy_install -from setuptools.extern import six -from setuptools.wheel import Wheel - -from .py31compat import TemporaryDirectory - - -def _fixup_find_links(find_links): - """Ensure find-links option end-up being a list of strings.""" - if isinstance(find_links, six.string_types): - return find_links.split() - assert isinstance(find_links, (tuple, list)) - return find_links - - -def _legacy_fetch_build_egg(dist, req): - """Fetch an egg needed for building. - - Legacy path using EasyInstall. - """ - tmp_dist = dist.__class__({'script_args': ['easy_install']}) - opts = tmp_dist.get_option_dict('easy_install') - opts.clear() - opts.update( - (k, v) - for k, v in dist.get_option_dict('easy_install').items() - if k in ( - # don't use any other settings - 'find_links', 'site_dirs', 'index_url', - 'optimize', 'site_dirs', 'allow_hosts', - )) - if dist.dependency_links: - links = dist.dependency_links[:] - if 'find_links' in opts: - links = _fixup_find_links(opts['find_links'][1]) + links - opts['find_links'] = ('setup', links) - install_dir = dist.get_egg_cache_dir() - cmd = easy_install( - tmp_dist, args=["x"], install_dir=install_dir, - exclude_scripts=True, - always_copy=False, build_directory=None, editable=False, - upgrade=False, multi_version=True, no_report=True, user=False - ) - cmd.ensure_finalized() - return cmd.easy_install(req) - - -def fetch_build_egg(dist, req): - """Fetch an egg needed for building. - - Use pip/wheel to fetch/build a wheel.""" - # Check pip is available. - try: - pkg_resources.get_distribution('pip') - except pkg_resources.DistributionNotFound: - dist.announce( - 'WARNING: The pip package is not available, falling back ' - 'to EasyInstall for handling setup_requires/test_requires; ' - 'this is deprecated and will be removed in a future version.' - , log.WARN - ) - return _legacy_fetch_build_egg(dist, req) - # Warn if wheel is not. - try: - pkg_resources.get_distribution('wheel') - except pkg_resources.DistributionNotFound: - dist.announce('WARNING: The wheel package is not available.', log.WARN) - # 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.') - if 'PIP_QUIET' in os.environ or 'PIP_VERBOSE' in os.environ: - quiet = False - else: - quiet = True - 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 - if 'find_links' in opts: - find_links = _fixup_find_links(opts['find_links'][1])[:] - else: - find_links = [] - if dist.dependency_links: - find_links.extend(dist.dependency_links) - eggs_dir = os.path.realpath(dist.get_egg_cache_dir()) - environment = pkg_resources.Environment() - for egg_dist in pkg_resources.find_distributions(eggs_dir): - if egg_dist in req and environment.can_add(egg_dist): - return egg_dist - with 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)) - if find_links is not None: - for link in find_links: - 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. - if req.url: - cmd.append(req.url) - else: - cmd.append(str(req)) - try: - subprocess.check_call(cmd) - except subprocess.CalledProcessError as e: - raise DistutilsError(str(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) - dist_metadata = pkg_resources.PathMetadata( - dist_location, os.path.join(dist_location, 'EGG-INFO')) - dist = pkg_resources.Distribution.from_filename( - dist_location, metadata=dist_metadata) - return dist - - -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 = pkg_resources.Requirement.parse(str(req)) - req.marker = None - return req diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/launch.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/launch.py deleted file mode 100644 index 308283e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/launch.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -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 tokenize -import sys - - -def run(): - """ - 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) - script = open_(script_name).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/plotter-app/venv/lib/python3.8/site-packages/setuptools/lib2to3_ex.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/lib2to3_ex.py deleted file mode 100644 index 4b1a73f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/lib2to3_ex.py +++ /dev/null @@ -1,62 +0,0 @@ -""" -Customized Mixin2to3 support: - - - adds support for converting doctests - - -This module raises an ImportError on Python 2. -""" - -from distutils.util import Mixin2to3 as _Mixin2to3 -from distutils import log -from lib2to3.refactor import RefactoringTool, get_fixers_from_package - -import setuptools - - -class DistutilsRefactoringTool(RefactoringTool): - def log_error(self, msg, *args, **kw): - log.error(msg, *args) - - def log_message(self, msg, *args): - log.info(msg, *args) - - def log_debug(self, msg, *args): - log.debug(msg, *args) - - -class Mixin2to3(_Mixin2to3): - def run_2to3(self, files, doctests=False): - # See of the distribution option has been set, otherwise check the - # setuptools default. - if self.distribution.use_2to3 is not True: - return - if not files: - return - log.info("Fixing " + " ".join(files)) - self.__build_fixer_names() - self.__exclude_fixers() - if doctests: - if setuptools.run_2to3_on_doctests: - r = DistutilsRefactoringTool(self.fixer_names) - r.refactor(files, write=True, doctests_only=True) - else: - _Mixin2to3.run_2to3(self, files) - - def __build_fixer_names(self): - if self.fixer_names: - return - self.fixer_names = [] - for p in setuptools.lib2to3_fixer_packages: - self.fixer_names.extend(get_fixers_from_package(p)) - if self.distribution.use_2to3_fixers is not None: - for p in self.distribution.use_2to3_fixers: - self.fixer_names.extend(get_fixers_from_package(p)) - - def __exclude_fixers(self): - excluded_fixers = getattr(self, 'exclude_fixers', []) - if self.distribution.use_2to3_exclude_fixers is not None: - excluded_fixers.extend(self.distribution.use_2to3_exclude_fixers) - for fixer_name in excluded_fixers: - if fixer_name in self.fixer_names: - self.fixer_names.remove(fixer_name) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/monkey.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/monkey.py deleted file mode 100644 index 3c77f8c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/monkey.py +++ /dev/null @@ -1,179 +0,0 @@ -""" -Monkey patching of distutils. -""" - -import sys -import distutils.filelist -import platform -import types -import functools -from importlib import import_module -import inspect - -from setuptools.extern import six - -import setuptools - -__all__ = [] -""" -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) - - -def get_unpatched(item): - lookup = ( - get_unpatched_class if isinstance(item, six.class_types) else - get_unpatched_function if isinstance(item, types.FunctionType) else - lambda item: None - ) - return lookup(item) - - -def get_unpatched_class(cls): - """Protect against re-patching the distutils if reloaded - - Also ensures that no other distutils extension monkeypatched the distutils - first. - """ - external_bases = ( - cls - for cls in _get_mro(cls) - if not cls.__module__.startswith('setuptools') - ) - base = next(external_bases) - if not base.__module__.startswith('distutils'): - msg = "distutils has already been patched by %r" % cls - raise AssertionError(msg) - return base - - -def patch_all(): - # we can't patch distutils.cmd, alas - distutils.core.Command = setuptools.Command - - has_issue_12885 = sys.version_info <= (3, 5, 3) - - if has_issue_12885: - # fix findall bug in distutils (http://bugs.python.org/issue12885) - distutils.filelist.findall = setuptools.findall - - needs_warehouse = ( - sys.version_info < (2, 7, 13) - or - (3, 4) < sys.version_info < (3, 4, 6) - or - (3, 5) < sys.version_info <= (3, 5, 3) - ) - - if needs_warehouse: - warehouse = 'https://upload.pypi.org/legacy/' - distutils.config.PyPIRCCommand.DEFAULT_REPOSITORY = warehouse - - _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 - distutils.extension.Extension = setuptools.extension.Extension - if 'distutils.command.build_ext' in sys.modules: - sys.modules['distutils.command.build_ext'].Extension = ( - setuptools.extension.Extension - ) - - patch_for_msvc_specialized_compiler() - - -def _patch_distribution_metadata(): - """Patch write_pkg_file and read_pkg_file for higher metadata standards""" - for attr in ('write_pkg_file', 'read_pkg_file', 'get_metadata_version'): - new_val = getattr(setuptools.dist, 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 getattr(candidate, 'unpatched') - - -def patch_for_msvc_specialized_compiler(): - """ - Patch functions in distutils to use standalone Microsoft Visual C++ - compilers. - """ - # import late to avoid circular imports on Python < 3.5 - msvc = import_module('setuptools.msvc') - - if platform.system() != 'Windows': - # Compilers only availables on Microsoft Windows - return - - def patch_params(mod_name, func_name): - """ - Prepare the parameters for patch_func to patch indicated function. - """ - repl_prefix = 'msvc9_' if 'msvc9' in mod_name else 'msvc14_' - repl_name = repl_prefix + func_name.lstrip('_') - repl = getattr(msvc, repl_name) - mod = import_module(mod_name) - if not hasattr(mod, func_name): - raise ImportError(func_name) - return repl, mod, func_name - - # Python 2.7 to 3.4 - msvc9 = functools.partial(patch_params, 'distutils.msvc9compiler') - - # Python 3.5+ - msvc14 = functools.partial(patch_params, 'distutils._msvccompiler') - - try: - # Patch distutils.msvc9compiler - patch_func(*msvc9('find_vcvarsall')) - patch_func(*msvc9('query_vcvarsall')) - except ImportError: - pass - - try: - # Patch distutils._msvccompiler._get_vc_env - patch_func(*msvc14('_get_vc_env')) - except ImportError: - pass - - try: - # Patch distutils._msvccompiler.gen_lib_options for Numpy - patch_func(*msvc14('gen_lib_options')) - except ImportError: - pass diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/msvc.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/msvc.py deleted file mode 100644 index 2ffe1c8..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/msvc.py +++ /dev/null @@ -1,1679 +0,0 @@ -""" -Improved support for Microsoft Visual C++ compilers. - -Known supported compilers: --------------------------- -Microsoft Visual C++ 9.0: - Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64) - Microsoft Windows SDK 6.1 (x86, x64, ia64) - Microsoft Windows SDK 7.0 (x86, x64, ia64) - -Microsoft Visual C++ 10.0: - Microsoft Windows SDK 7.1 (x86, x64, ia64) - -Microsoft Visual C++ 14.X: - Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) - Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64) - Microsoft Visual Studio Build Tools 2019 (x86, x64, arm, arm64) - -This may also support compilers shipped with compatible Visual Studio versions. -""" - -import json -from io import open -from os import listdir, pathsep -from os.path import join, isfile, isdir, dirname -import sys -import platform -import itertools -import distutils.errors -from setuptools.extern.packaging.version import LegacyVersion - -from setuptools.extern.six.moves import filterfalse - -from .monkey import get_unpatched - -if platform.system() == 'Windows': - from setuptools.extern.six.moves 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() - -_msvc9_suppress_errors = ( - # msvc9compiler isn't available on some platforms - ImportError, - - # msvc9compiler raises DistutilsPlatformError in some - # environments. See #1118. - distutils.errors.DistutilsPlatformError, -) - -try: - from distutils.msvc9compiler import Reg -except _msvc9_suppress_errors: - pass - - -def msvc9_find_vcvarsall(version): - """ - Patched "distutils.msvc9compiler.find_vcvarsall" to use the standalone - compiler build for Python - (VCForPython / Microsoft Visual C++ Compiler for Python 2.7). - - Fall back to original behavior when the standalone compiler is not - available. - - Redirect the path of "vcvarsall.bat". - - Parameters - ---------- - version: float - Required Microsoft Visual C++ version. - - Return - ------ - str - vcvarsall.bat path - """ - vc_base = r'Software\%sMicrosoft\DevDiv\VCForPython\%0.1f' - key = vc_base % ('', version) - try: - # Per-user installs register the compiler path here - productdir = Reg.get_value(key, "installdir") - except KeyError: - try: - # All-user installs on a 64-bit system register here - key = vc_base % ('Wow6432Node\\', version) - productdir = Reg.get_value(key, "installdir") - except KeyError: - productdir = None - - if productdir: - vcvarsall = join(productdir, "vcvarsall.bat") - if isfile(vcvarsall): - return vcvarsall - - return get_unpatched(msvc9_find_vcvarsall)(version) - - -def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): - """ - Patched "distutils.msvc9compiler.query_vcvarsall" for support extra - Microsoft Visual C++ 9.0 and 10.0 compilers. - - Set environment without use of "vcvarsall.bat". - - Parameters - ---------- - ver: float - Required Microsoft Visual C++ version. - arch: str - Target architecture. - - Return - ------ - dict - environment - """ - # Try to get environment from vcvarsall.bat (Classical way) - try: - orig = get_unpatched(msvc9_query_vcvarsall) - return orig(ver, arch, *args, **kwargs) - except distutils.errors.DistutilsPlatformError: - # Pass error if Vcvarsall.bat is missing - pass - except ValueError: - # Pass error if environment not set after executing vcvarsall.bat - pass - - # If error, try to set environment directly - try: - return EnvironmentInfo(arch, ver).return_env() - except distutils.errors.DistutilsPlatformError as exc: - _augment_exception(exc, ver, arch) - raise - - -def msvc14_get_vc_env(plat_spec): - """ - Patched "distutils._msvccompiler._get_vc_env" for support extra - Microsoft Visual C++ 14.X compilers. - - Set environment without use of "vcvarsall.bat". - - Parameters - ---------- - plat_spec: str - Target architecture. - - Return - ------ - dict - environment - """ - # Try to get environment from vcvarsall.bat (Classical way) - try: - return get_unpatched(msvc14_get_vc_env)(plat_spec) - except distutils.errors.DistutilsPlatformError: - # Pass error Vcvarsall.bat is missing - pass - - # If error, try to set environment directly - try: - return EnvironmentInfo(plat_spec, vc_min_ver=14.0).return_env() - except distutils.errors.DistutilsPlatformError as exc: - _augment_exception(exc, 14.0) - raise - - -def msvc14_gen_lib_options(*args, **kwargs): - """ - Patched "distutils._msvccompiler.gen_lib_options" for fix - compatibility between "numpy.distutils" and "distutils._msvccompiler" - (for Numpy < 1.11.2) - """ - if "numpy.distutils" in sys.modules: - import numpy as np - if LegacyVersion(np.__version__) < LegacyVersion('1.11.2'): - return np.distutils.ccompiler.gen_lib_options(*args, **kwargs) - return get_unpatched(msvc14_gen_lib_options)(*args, **kwargs) - - -def _augment_exception(exc, version, arch=''): - """ - Add details to the exception message to help guide the user - as to what action will resolve it. - """ - # Error if MSVC++ directory not found or environment not set - message = exc.args[0] - - if "vcvarsall" in message.lower() or "visual c" in message.lower(): - # Special error message if MSVC++ not installed - tmpl = 'Microsoft Visual C++ {version:0.1f} is required.' - message = tmpl.format(**locals()) - msdownload = 'www.microsoft.com/download/details.aspx?id=%d' - if version == 9.0: - if arch.lower().find('ia64') > -1: - # For VC++ 9.0, if IA64 support is needed, redirect user - # to Windows SDK 7.0. - # Note: No download link available from Microsoft. - message += ' Get it with "Microsoft Windows SDK 7.0"' - else: - # For VC++ 9.0 redirect user to Vc++ for Python 2.7 : - # This redirection link is maintained by Microsoft. - # Contact vspython@microsoft.com if it needs updating. - message += ' Get it from http://aka.ms/vcpython27' - elif version == 10.0: - # For VC++ 10.0 Redirect user to Windows SDK 7.1 - message += ' Get it with "Microsoft Windows SDK 7.1": ' - message += msdownload % 8279 - elif version >= 14.0: - # For VC++ 14.X Redirect user to latest Visual C++ Build Tools - message += (' Get it with "Build Tools for Visual Studio": ' - r'https://visualstudio.microsoft.com/downloads/') - - exc.args = (message, ) - - -class PlatformInfo: - """ - Current and Target Architectures information. - - Parameters - ---------- - arch: str - Target architecture. - """ - current_cpu = environ.get('processor_architecture', '').lower() - - def __init__(self, arch): - 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): - """ - 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 - r'\%s' % self.current_cpu - ) - - def target_dir(self, hidex86=False, x64=False): - 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 - r'\%s' % 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('\\', '\\%s_' % 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): - self.pi = platform_info - - @property - def visualstudio(self): - """ - 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 join(self.visualstudio, 'SxS') - - @property - def vc(self): - """ - Microsoft Visual C++ VC7 registry key. - - Return - ------ - str - Registry key - """ - return join(self.sxs, 'VC7') - - @property - def vs(self): - """ - Microsoft Visual Studio VS7 registry key. - - Return - ------ - str - Registry key - """ - return join(self.sxs, 'VS7') - - @property - def vc_for_python(self): - """ - Microsoft Visual C++ for Python registry key. - - Return - ------ - str - Registry key - """ - return r'DevDiv\VCForPython' - - @property - def microsoft_sdk(self): - """ - 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 join(self.microsoft_sdk, 'Windows') - - @property - def netfx_sdk(self): - """ - Microsoft .NET Framework SDK registry key. - - Return - ------ - str - Registry key - """ - return join(self.microsoft_sdk, 'NETFXSDK') - - @property - def windows_kits_roots(self): - """ - 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 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 - ms = self.microsoft - for hkey in self.HKEYS: - try: - bkey = openkey(hkey, ms(key), 0, key_read) - except (OSError, IOError): - if not self.pi.current_is_x86(): - try: - bkey = openkey(hkey, ms(key, True), 0, key_read) - except (OSError, IOError): - continue - else: - continue - try: - return winreg.QueryValueEx(bkey, name)[0] - except (OSError, IOError): - pass - - -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): - 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 in self.ri.HKEYS: - for key in vckeys: - try: - bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ) - except (OSError, IOError): - continue - subkeys, values, _ = winreg.QueryInfoKey(bkey) - for i in range(values): - try: - ver = float(winreg.EnumValue(bkey, i)[0]) - if ver not in vs_vers: - vs_vers.append(ver) - except ValueError: - pass - for i in range(subkeys): - try: - ver = float(winreg.EnumKey(bkey, i)) - if ver not in vs_vers: - vs_vers.append(ver) - except ValueError: - pass - return sorted(vs_vers) - - def find_programdata_vs_vers(self): - 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 = {} - instances_dir = \ - r'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances' - - try: - hashed_names = listdir(instances_dir) - - except (OSError, IOError): - # 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 = 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 - listdir(join(vs_path, r'VC\Tools\MSVC')) - - # Store version and path - vs_versions[self._as_float_version( - state['installationVersion'])] = vs_path - - except (OSError, IOError, 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 = join(self.ProgramFilesx86, - 'Microsoft Visual Studio %0.1f' % self.vs_ver) - - # Try to get path from registry, if fail use default path - return self.ri.lookup(self.ri.vs, '%0.1f' % self.vs_ver) or default - - @property - def VCInstallDir(self): - """ - Microsoft Visual C++ directory. - - Return - ------ - str - path - """ - path = self._guess_vc() or self._guess_vc_legacy() - - if not 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 = 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 = listdir(guess_vc)[-1] - self.vc_ver = self._as_float_version(vc_ver) - return join(guess_vc, vc_ver) - except (OSError, IOError, IndexError): - return '' - - def _guess_vc_legacy(self): - """ - Locate Visual C++ for versions prior to 2017. - - Return - ------ - str - path - """ - default = join(self.ProgramFilesx86, - r'Microsoft Visual Studio %0.1f\VC' % self.vs_ver) - - # Try to get "VC++ for Python" path from registry as default path - reg_path = join(self.ri.vc_for_python, '%0.1f' % self.vs_ver) - python_vc = self.ri.lookup(reg_path, 'installdir') - default_vc = 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, '%0.1f' % self.vs_ver) or default_vc - - @property - def WindowsSdkVersion(self): - """ - 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' - - @property - def WindowsSdkLastVersion(self): - """ - Microsoft Windows SDK last version. - - Return - ------ - str - version - """ - return self._use_last_dir_name(join(self.WindowsSdkDir, 'lib')) - - @property - def WindowsSdkDir(self): - """ - Microsoft Windows SDK directory. - - Return - ------ - str - path - """ - sdkdir = '' - for ver in self.WindowsSdkVersion: - # Try to get it from registry - loc = join(self.ri.windows_sdk, 'v%s' % ver) - sdkdir = self.ri.lookup(loc, 'installationfolder') - if sdkdir: - break - if not sdkdir or not isdir(sdkdir): - # Try to get "VC++ for Python" version from registry - path = join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) - install_base = self.ri.lookup(path, 'installdir') - if install_base: - sdkdir = join(install_base, 'WinSDK') - if not sdkdir or not isdir(sdkdir): - # If fail, use default new path - for ver in self.WindowsSdkVersion: - intver = ver[:ver.rfind('.')] - path = r'Microsoft SDKs\Windows Kits\%s' % intver - d = join(self.ProgramFiles, path) - if isdir(d): - sdkdir = d - if not sdkdir or not isdir(sdkdir): - # If fail, use default old path - for ver in self.WindowsSdkVersion: - path = r'Microsoft SDKs\Windows\v%s' % ver - d = join(self.ProgramFiles, path) - if isdir(d): - sdkdir = d - if not sdkdir: - # If fail, use Platform SDK - sdkdir = 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) - fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-')) - - # list all possibles registry paths - regpaths = [] - if self.vs_ver >= 14.0: - for ver in self.NetFxSdkVersion: - regpaths += [join(self.ri.netfx_sdk, ver, fx)] - - for ver in self.WindowsSdkVersion: - regpaths += [join(self.ri.windows_sdk, 'v%sA' % ver, fx)] - - # Return installation folder from the more recent path - for path in regpaths: - execpath = self.ri.lookup(path, 'installationfolder') - if execpath: - return execpath - - @property - def FSharpInstallDir(self): - """ - Microsoft Visual F# directory. - - Return - ------ - str - path - """ - path = join(self.ri.visualstudio, r'%0.1f\Setup\F#' % self.vs_ver) - 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, - 'kitsroot%s' % ver) - if sdkdir: - return sdkdir or '' - - @property - def UniversalCRTSdkLastVersion(self): - """ - Microsoft Universal C Runtime SDK last version. - - Return - ------ - str - version - """ - return self._use_last_dir_name(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 = 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 = 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 = 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): - """ - Microsoft .NET Framework 32bit versions. - - Return - ------ - tuple of str - versions - """ - return self._find_dot_net_versions(32) - - @property - def FrameworkVersion64(self): - """ - Microsoft .NET Framework 64bit versions. - - Return - ------ - tuple of str - versions - """ - return self._find_dot_net_versions(64) - - def _find_dot_net_versions(self, bits): - """ - 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, 'frameworkver%d' % bits) - dot_net_dir = getattr(self, 'FrameworkDir%d' % 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' - - @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(listdir(path)) - if isdir(join(path, dir_name)) and - dir_name.startswith(prefix) - ) - return next(matching_dirs, None) or '' - - -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): - 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 += [r'Team Tools\Performance Tools%s' % arch_subdir] - - return [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 [join(self.si.VCInstallDir, 'Include'), - 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 = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir] - - if self.vs_ver >= 14.0: - paths += [r'Lib\store%s' % arch_subdir] - - return [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 [join(self.si.VCInstallDir, r'Lib\store\references')] - - @property - def VCTools(self): - """ - Microsoft Visual C++ Tools. - - Return - ------ - list of str - paths - """ - si = self.si - tools = [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 += [join(si.VCInstallDir, 'Bin%s' % arch_subdir)] - - if self.vs_ver == 14.0: - path = 'Bin%s' % self.pi.current_dir(hidex86=True) - tools += [join(si.VCInstallDir, path)] - - elif self.vs_ver >= 15.0: - host_dir = (r'bin\HostX86%s' if self.pi.current_is_x86() else - r'bin\HostX64%s') - tools += [join( - si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))] - - if self.pi.current_cpu != self.pi.target_cpu: - tools += [join( - si.VCInstallDir, host_dir % self.pi.current_dir(x64=True))] - - else: - tools += [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 [join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)] - - else: - arch_subdir = self.pi.target_dir(x64=True) - lib = join(self.si.WindowsSdkDir, 'lib') - libver = self._sdk_subdir - return [join(lib, '%sum%s' % (libver , arch_subdir))] - - @property - def OSIncludes(self): - """ - Microsoft Windows SDK Include. - - Return - ------ - list of str - paths - """ - include = join(self.si.WindowsSdkDir, 'include') - - if self.vs_ver <= 10.0: - return [include, join(include, 'gl')] - - else: - if self.vs_ver >= 14.0: - sdkver = self._sdk_subdir - else: - sdkver = '' - return [join(include, '%sshared' % sdkver), - join(include, '%sum' % sdkver), - join(include, '%swinrt' % sdkver)] - - @property - def OSLibpath(self): - """ - Microsoft Windows SDK Libraries Paths. - - Return - ------ - list of str - paths - """ - ref = join(self.si.WindowsSdkDir, 'References') - libpath = [] - - if self.vs_ver <= 9.0: - libpath += self.OSLibraries - - if self.vs_ver >= 11.0: - libpath += [join(ref, r'CommonConfiguration\Neutral')] - - if self.vs_ver >= 14.0: - libpath += [ - ref, - join(self.si.WindowsSdkDir, 'UnionMetadata'), - join(ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'), - join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'), - join(ref,'Windows.Networking.Connectivity.WwanContract', - '1.0.0.0'), - join(self.si.WindowsSdkDir, 'ExtensionSDKs', 'Microsoft.VCLibs', - '%0.1f' % self.vs_ver, '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 join(self.si.WindowsSdkDir, bin_dir) - - if not self.pi.current_is_x86(): - arch_subdir = self.pi.current_dir(x64=True) - path = 'Bin%s' % arch_subdir - yield 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 = r'Bin\NETFX 4.0 Tools%s' % arch_subdir - yield join(self.si.WindowsSdkDir, path) - - elif self.vs_ver >= 15.0: - path = join(self.si.WindowsSdkDir, 'Bin') - arch_subdir = self.pi.current_dir(x64=True) - sdkver = self.si.WindowsSdkLastVersion - yield join(path, '%s%s' % (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 ('%s\\' % 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 [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 += [join(si.FrameworkDir32, ver) - for ver in si.FrameworkVersion32] - if include64: - tools += [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 [join(self.si.NetFxSdkDir, r'lib\um%s' % 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 [join(self.si.NetFxSdkDir, r'include\um')] - - @property - def VsTDb(self): - """ - Microsoft Visual Studio Team System Database. - - Return - ------ - list of str - paths - """ - return [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 = r'MSBuild\%0.1f\bin%s' % (self.vs_ver, arch_subdir) - build = [join(base_path, path)] - - if self.vs_ver >= 15.0: - # Add Roslyn C# & Visual Basic Compiler - build += [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 [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 = join(self.si.UniversalCRTSdkDir, 'lib') - ucrtver = self._ucrt_subdir - return [join(lib, '%sucrt%s' % (ucrtver, 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 = join(self.si.UniversalCRTSdkDir, 'include') - return [join(include, '%sucrt' % self._ucrt_subdir)] - - @property - def _ucrt_subdir(self): - """ - Microsoft Universal C Runtime SDK version subdir. - - Return - ------ - str - subdir - """ - ucrtver = self.si.UniversalCRTSdkLastVersion - return ('%s\\' % 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): - """ - Microsoft Visual C++ runtime redistributable dll. - - Return - ------ - str - path - """ - vcruntime = 'vcruntime%d0.dll' % self.vc_ver - arch_subdir = self.pi.target_dir(x64=True).strip('\\') - - # Installation prefixes candidates - prefixes = [] - tools_path = self.si.VCInstallDir - redist_path = dirname(tools_path.replace(r'\Tools', r'\Redist')) - if isdir(redist_path): - # Redist version may not be exactly the same as tools - redist_path = join(redist_path, listdir(redist_path)[-1]) - prefixes += [redist_path, join(redist_path, 'onecore')] - - prefixes += [join(tools_path, 'redist')] # VS14 legacy path - - # CRT directory - crt_dirs = ('Microsoft.VC%d.CRT' % (self.vc_ver * 10), - # Sometime store in directory with VS version instead of VC - 'Microsoft.VC%d.CRT' % (int(self.vs_ver) * 10)) - - # vcruntime path - for prefix, crt_dir in itertools.product(prefixes, crt_dirs): - path = join(prefix, arch_subdir, crt_dir, vcruntime) - if isfile(path): - return path - - def return_env(self, exists=True): - """ - Return environment dict. - - Parameters - ---------- - exists: bool - It True, only return existing paths. - - Return - ------ - dict - environment - """ - env = dict( - 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 isfile(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(pathsep) - paths = itertools.chain(spec_paths, env_paths) - extant_paths = list(filter(isdir, paths)) if exists else paths - if not extant_paths: - msg = "%s environment variable is empty" % name.upper() - raise distutils.errors.DistutilsPlatformError(msg) - unique_paths = self._unique_everseen(extant_paths) - return pathsep.join(unique_paths) - - # from Python docs - @staticmethod - def _unique_everseen(iterable, key=None): - """ - List unique elements, preserving order. - Remember all elements ever seen. - - _unique_everseen('AAAABBBCCDAABBB') --> A B C D - - _unique_everseen('ABBCcAD', str.lower) --> A B C D - """ - seen = set() - seen_add = seen.add - if key is None: - for element in filterfalse(seen.__contains__, iterable): - seen_add(element) - yield element - else: - for element in iterable: - k = key(element) - if k not in seen: - seen_add(k) - yield element diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/namespaces.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/namespaces.py deleted file mode 100644 index dc16106..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/namespaces.py +++ /dev/null @@ -1,107 +0,0 @@ -import os -from distutils import log -import itertools - -from setuptools.extern.six.moves import map - - -flatten = itertools.chain.from_iterable - - -class Installer: - - nspkg_ext = '-nspkg.pth' - - def install_namespaces(self): - nsp = self._get_all_ns_packages() - if not nsp: - return - filename, ext = os.path.splitext(self._get_target()) - filename += self.nspkg_ext - 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') as f: - f.writelines(lines) - - def uninstall_namespaces(self): - filename, ext = os.path.splitext(self._get_target()) - filename += self.nspkg_ext - if not os.path.exists(filename): - return - log.info("Removing %s", filename) - os.remove(filename) - - def _get_target(self): - return self.target - - _nspkg_tmpl = ( - "import sys, types, os", - "has_mfs = sys.version_info > (3, 5)", - "p = os.path.join(%(root)s, *%(pth)r)", - "importlib = has_mfs and __import__('importlib.util')", - "has_mfs and __import__('importlib.machinery')", - "m = has_mfs and " - "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): - # ensure pkg is not a unicode string under Python 2.7 - pkg = str(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(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/plotter-app/venv/lib/python3.8/site-packages/setuptools/package_index.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/package_index.py deleted file mode 100644 index f419d47..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/package_index.py +++ /dev/null @@ -1,1136 +0,0 @@ -"""PyPI and direct package downloading""" -import sys -import os -import re -import shutil -import socket -import base64 -import hashlib -import itertools -import warnings -from functools import wraps - -from setuptools.extern import six -from setuptools.extern.six.moves import urllib, http_client, configparser, map - -import setuptools -from pkg_resources import ( - CHECKOUT_DIST, Distribution, BINARY_DIST, normalize_path, SOURCE_DIST, - Environment, find_distributions, safe_name, safe_version, - to_filename, Requirement, DEVELOP_DIST, EGG_DIST, -) -from setuptools import ssl_support -from distutils import log -from distutils.errors import DistutilsError -from fnmatch import translate -from setuptools.py27compat import get_all_headers -from setuptools.py33compat import unescape -from setuptools.wheel import Wheel - -__metaclass__ = type - -EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.+!]+)$') -HREF = re.compile(r"""href\s*=\s*['"]?([^'"> ]+)""", re.I) -PYPI_MD5 = re.compile( - r'([^<]+)\n\s+\(md5\)' -) -URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):', re.I).match -EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz".split() - -__all__ = [ - 'PackageIndex', 'distros_for_url', 'parse_bdist_wininst', - 'interpret_distro_name', -] - -_SOCKET_TIMEOUT = 15 - -_tmpl = "setuptools/{setuptools.__version__} Python-urllib/{py_major}" -user_agent = _tmpl.format(py_major='{}.{}'.format(*sys.version_info), setuptools=setuptools) - - -def parse_requirement_arg(spec): - try: - return Requirement.parse(spec) - except ValueError: - raise DistutilsError( - "Not a URL, existing file, or requirement spec: %r" % (spec,) - ) - - -def parse_bdist_wininst(name): - """Return (base,pyversion) or (None,None) for possible .exe name""" - - lower = name.lower() - base, py_ver, plat = None, None, None - - if lower.endswith('.exe'): - if lower.endswith('.win32.exe'): - base = name[:-10] - plat = 'win32' - elif lower.startswith('.win32-py', -16): - py_ver = name[-7:-4] - base = name[:-16] - plat = 'win32' - elif lower.endswith('.win-amd64.exe'): - base = name[:-14] - plat = 'win-amd64' - elif lower.startswith('.win-amd64-py', -20): - py_ver = name[-7:-4] - base = name[:-20] - plat = 'win-amd64' - return base, py_ver, plat - - -def egg_info_for_url(url): - parts = urllib.parse.urlparse(url) - scheme, server, path, parameters, query, fragment = parts - base = urllib.parse.unquote(path.split('/')[-1]) - if server == 'sourceforge.net' and base == 'download': # XXX Yuck - base = urllib.parse.unquote(path.split('/')[-2]) - if '#' in base: - base, fragment = base.split('#', 1) - return base, fragment - - -def distros_for_url(url, metadata=None): - """Yield egg or source distribution objects that might be found at a URL""" - base, fragment = egg_info_for_url(url) - for dist in distros_for_location(url, base, metadata): - yield dist - if fragment: - match = EGG_FRAGMENT.match(fragment) - if match: - for dist in interpret_distro_name( - url, match.group(1), metadata, precedence=CHECKOUT_DIST - ): - yield dist - - -def distros_for_location(location, basename, metadata=None): - """Yield egg or source distribution objects based on basename""" - if basename.endswith('.egg.zip'): - basename = basename[:-4] # strip the .zip - if basename.endswith('.egg') and '-' in basename: - # only one, unambiguous interpretation - return [Distribution.from_location(location, basename, metadata)] - if basename.endswith('.whl') and '-' in basename: - wheel = Wheel(basename) - if not wheel.is_compatible(): - return [] - return [Distribution( - location=location, - project_name=wheel.project_name, - version=wheel.version, - # Increase priority over eggs. - precedence=EGG_DIST + 1, - )] - if basename.endswith('.exe'): - win_base, py_ver, platform = parse_bdist_wininst(basename) - if win_base is not None: - return interpret_distro_name( - location, win_base, metadata, py_ver, BINARY_DIST, platform - ) - # Try source distro extensions (.zip, .tgz, etc.) - # - for ext in EXTENSIONS: - if basename.endswith(ext): - basename = basename[:-len(ext)] - return interpret_distro_name(location, basename, metadata) - return [] # no extension matched - - -def distros_for_filename(filename, metadata=None): - """Yield possible egg or source distribution objects based on a filename""" - return distros_for_location( - normalize_path(filename), os.path.basename(filename), metadata - ) - - -def interpret_distro_name( - location, basename, metadata, py_version=None, precedence=SOURCE_DIST, - platform=None -): - """Generate alternative interpretations of a source distro name - - Note: if `location` is a filesystem filename, you should call - ``pkg_resources.normalize_path()`` on it before passing it to this - routine! - """ - # Generate alternative interpretations of a source distro name - # Because some packages are ambiguous as to name/versions split - # e.g. "adns-python-1.1.0", "egenix-mx-commercial", etc. - # So, we generate each possible interepretation (e.g. "adns, python-1.1.0" - # "adns-python, 1.1.0", and "adns-python-1.1.0, no version"). In practice, - # the spurious interpretations should be ignored, because in the event - # there's also an "adns" package, the spurious "python-1.1.0" version will - # compare lower than any numeric version number, and is therefore unlikely - # to match a request for it. It's still a potential problem, though, and - # in the long run PyPI and the distutils should go for "safe" names and - # versions in distribution archive names (sdist and bdist). - - parts = basename.split('-') - if not py_version and any(re.match(r'py\d\.\d$', p) for p in parts[2:]): - # it is a bdist_dumb, not an sdist -- bail out - return - - for p in range(1, len(parts) + 1): - yield Distribution( - location, metadata, '-'.join(parts[:p]), '-'.join(parts[p:]), - py_version=py_version, precedence=precedence, - platform=platform - ) - - -# From Python 2.7 docs -def unique_everseen(iterable, key=None): - "List unique elements, preserving order. Remember all elements ever seen." - # unique_everseen('AAAABBBCCDAABBB') --> A B C D - # unique_everseen('ABBCcAD', str.lower) --> A B C D - seen = set() - seen_add = seen.add - if key is None: - for element in six.moves.filterfalse(seen.__contains__, iterable): - seen_add(element) - yield element - else: - for element in iterable: - k = key(element) - if k not in seen: - seen_add(k) - yield element - - -def unique_values(func): - """ - Wrap a function returning an iterable such that the resulting iterable - only ever yields unique items. - """ - - @wraps(func) - def wrapper(*args, **kwargs): - return unique_everseen(func(*args, **kwargs)) - - return wrapper - - -REL = re.compile(r"""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I) -# this line is here to fix emacs' cruddy broken syntax highlighting - - -@unique_values -def find_external_links(url, page): - """Find rel="homepage" and rel="download" links in `page`, yielding URLs""" - - for match in REL.finditer(page): - tag, rel = match.groups() - rels = set(map(str.strip, rel.lower().split(','))) - if 'homepage' in rels or 'download' in rels: - for match in HREF.finditer(tag): - yield urllib.parse.urljoin(url, htmldecode(match.group(1))) - - for tag in ("Home Page", "Download URL"): - pos = page.find(tag) - if pos != -1: - match = HREF.search(page, pos) - if match: - yield urllib.parse.urljoin(url, htmldecode(match.group(1))) - - -class ContentChecker: - """ - A null content checker that defines the interface for checking content - """ - - def feed(self, block): - """ - Feed a block of data to the hash. - """ - return - - def is_valid(self): - """ - Check the hash. Return False if validation fails. - """ - return True - - def report(self, reporter, template): - """ - Call reporter with information about the checker (hash name) - substituted into the template. - """ - return - - -class HashChecker(ContentChecker): - pattern = re.compile( - r'(?Psha1|sha224|sha384|sha256|sha512|md5)=' - r'(?P[a-f0-9]+)' - ) - - def __init__(self, hash_name, expected): - self.hash_name = hash_name - self.hash = hashlib.new(hash_name) - self.expected = expected - - @classmethod - def from_url(cls, url): - "Construct a (possibly null) ContentChecker from a URL" - fragment = urllib.parse.urlparse(url)[-1] - if not fragment: - return ContentChecker() - match = cls.pattern.search(fragment) - if not match: - return ContentChecker() - return cls(**match.groupdict()) - - def feed(self, block): - self.hash.update(block) - - def is_valid(self): - return self.hash.hexdigest() == self.expected - - def report(self, reporter, template): - msg = template % self.hash_name - return reporter(msg) - - -class PackageIndex(Environment): - """A distribution index that scans web pages for download URLs""" - - def __init__( - self, index_url="https://pypi.org/simple/", hosts=('*',), - ca_bundle=None, verify_ssl=True, *args, **kw - ): - Environment.__init__(self, *args, **kw) - self.index_url = index_url + "/" [:not index_url.endswith('/')] - self.scanned_urls = {} - self.fetched_urls = {} - self.package_pages = {} - self.allows = re.compile('|'.join(map(translate, hosts))).match - self.to_scan = [] - use_ssl = ( - verify_ssl - and ssl_support.is_available - and (ca_bundle or ssl_support.find_ca_bundle()) - ) - if use_ssl: - self.opener = ssl_support.opener_for(ca_bundle) - else: - self.opener = urllib.request.urlopen - - def process_url(self, url, retrieve=False): - """Evaluate a URL as a possible download, and maybe retrieve it""" - if url in self.scanned_urls and not retrieve: - return - self.scanned_urls[url] = True - if not URL_SCHEME(url): - self.process_filename(url) - return - else: - dists = list(distros_for_url(url)) - if dists: - if not self.url_ok(url): - return - self.debug("Found link: %s", url) - - if dists or not retrieve or url in self.fetched_urls: - list(map(self.add, dists)) - return # don't need the actual page - - if not self.url_ok(url): - self.fetched_urls[url] = True - return - - self.info("Reading %s", url) - self.fetched_urls[url] = True # prevent multiple fetch attempts - tmpl = "Download error on %s: %%s -- Some packages may not be found!" - f = self.open_url(url, tmpl % url) - if f is None: - return - self.fetched_urls[f.url] = True - if 'html' not in f.headers.get('content-type', '').lower(): - f.close() # not html, we can't process it - return - - base = f.url # handle redirects - page = f.read() - if not isinstance(page, str): - # In Python 3 and got bytes but want str. - if isinstance(f, urllib.error.HTTPError): - # Errors have no charset, assume latin1: - charset = 'latin-1' - else: - charset = f.headers.get_param('charset') or 'latin-1' - page = page.decode(charset, "ignore") - f.close() - for match in HREF.finditer(page): - link = urllib.parse.urljoin(base, htmldecode(match.group(1))) - self.process_url(link) - if url.startswith(self.index_url) and getattr(f, 'code', None) != 404: - page = self.process_index(url, page) - - def process_filename(self, fn, nested=False): - # process filenames or directories - if not os.path.exists(fn): - self.warn("Not found: %s", fn) - return - - if os.path.isdir(fn) and not nested: - path = os.path.realpath(fn) - for item in os.listdir(path): - self.process_filename(os.path.join(path, item), True) - - dists = distros_for_filename(fn) - if dists: - self.debug("Found: %s", fn) - list(map(self.add, dists)) - - def url_ok(self, url, fatal=False): - s = URL_SCHEME(url) - is_file = s and s.group(1).lower() == 'file' - if is_file or self.allows(urllib.parse.urlparse(url)[1]): - return True - msg = ( - "\nNote: Bypassing %s (disallowed host; see " - "http://bit.ly/2hrImnY for details).\n") - if fatal: - raise DistutilsError(msg % url) - else: - self.warn(msg, url) - - def scan_egg_links(self, search_path): - dirs = filter(os.path.isdir, search_path) - egg_links = ( - (path, entry) - for path in dirs - for entry in os.listdir(path) - if entry.endswith('.egg-link') - ) - list(itertools.starmap(self.scan_egg_link, egg_links)) - - def scan_egg_link(self, path, entry): - with open(os.path.join(path, entry)) as raw_lines: - # filter non-empty lines - lines = list(filter(None, map(str.strip, raw_lines))) - - if len(lines) != 2: - # format is not recognized; punt - return - - egg_path, setup_path = lines - - for dist in find_distributions(os.path.join(path, egg_path)): - dist.location = os.path.join(path, *lines) - dist.precedence = SOURCE_DIST - self.add(dist) - - def process_index(self, url, page): - """Process the contents of a PyPI page""" - - def scan(link): - # Process a URL to see if it's for a package page - if link.startswith(self.index_url): - parts = list(map( - urllib.parse.unquote, link[len(self.index_url):].split('/') - )) - if len(parts) == 2 and '#' not in parts[1]: - # it's a package page, sanitize and index it - pkg = safe_name(parts[0]) - ver = safe_version(parts[1]) - self.package_pages.setdefault(pkg.lower(), {})[link] = True - return to_filename(pkg), to_filename(ver) - return None, None - - # process an index page into the package-page index - for match in HREF.finditer(page): - try: - scan(urllib.parse.urljoin(url, htmldecode(match.group(1)))) - except ValueError: - pass - - pkg, ver = scan(url) # ensure this page is in the page index - if pkg: - # process individual package page - for new_url in find_external_links(url, page): - # Process the found URL - base, frag = egg_info_for_url(new_url) - if base.endswith('.py') and not frag: - if ver: - new_url += '#egg=%s-%s' % (pkg, ver) - else: - self.need_version_info(url) - self.scan_url(new_url) - - return PYPI_MD5.sub( - lambda m: '%s' % m.group(1, 3, 2), page - ) - else: - return "" # no sense double-scanning non-package pages - - def need_version_info(self, url): - self.scan_all( - "Page at %s links to .py file(s) without version info; an index " - "scan is required.", url - ) - - def scan_all(self, msg=None, *args): - if self.index_url not in self.fetched_urls: - if msg: - self.warn(msg, *args) - self.info( - "Scanning index of all packages (this may take a while)" - ) - self.scan_url(self.index_url) - - def find_packages(self, requirement): - self.scan_url(self.index_url + requirement.unsafe_name + '/') - - if not self.package_pages.get(requirement.key): - # Fall back to safe version of the name - self.scan_url(self.index_url + requirement.project_name + '/') - - if not self.package_pages.get(requirement.key): - # We couldn't find the target package, so search the index page too - self.not_found_in_index(requirement) - - for url in list(self.package_pages.get(requirement.key, ())): - # scan each page that might be related to the desired package - self.scan_url(url) - - def obtain(self, requirement, installer=None): - self.prescan() - self.find_packages(requirement) - for dist in self[requirement.key]: - if dist in requirement: - return dist - self.debug("%s does not match %s", requirement, dist) - return super(PackageIndex, self).obtain(requirement, installer) - - def check_hash(self, checker, filename, tfp): - """ - checker is a ContentChecker - """ - checker.report( - self.debug, - "Validating %%s checksum for %s" % filename) - if not checker.is_valid(): - tfp.close() - os.unlink(filename) - raise DistutilsError( - "%s validation failed for %s; " - "possible download problem?" - % (checker.hash.name, os.path.basename(filename)) - ) - - def add_find_links(self, urls): - """Add `urls` to the list that will be prescanned for searches""" - for url in urls: - if ( - self.to_scan is None # if we have already "gone online" - or not URL_SCHEME(url) # or it's a local file/directory - or url.startswith('file:') - or list(distros_for_url(url)) # or a direct package link - ): - # then go ahead and process it now - self.scan_url(url) - else: - # otherwise, defer retrieval till later - self.to_scan.append(url) - - def prescan(self): - """Scan urls scheduled for prescanning (e.g. --find-links)""" - if self.to_scan: - list(map(self.scan_url, self.to_scan)) - self.to_scan = None # from now on, go ahead and process immediately - - def not_found_in_index(self, requirement): - if self[requirement.key]: # we've seen at least one distro - meth, msg = self.info, "Couldn't retrieve index page for %r" - else: # no distros seen for this name, might be misspelled - meth, msg = ( - self.warn, - "Couldn't find index page for %r (maybe misspelled?)") - meth(msg, requirement.unsafe_name) - self.scan_all() - - def download(self, spec, tmpdir): - """Locate and/or download `spec` to `tmpdir`, returning a local path - - `spec` may be a ``Requirement`` object, or a string containing a URL, - an existing local filename, or a project/version requirement spec - (i.e. the string form of a ``Requirement`` object). If it is the URL - of a .py file with an unambiguous ``#egg=name-version`` tag (i.e., one - that escapes ``-`` as ``_`` throughout), a trivial ``setup.py`` is - automatically created alongside the downloaded file. - - If `spec` is a ``Requirement`` object or a string containing a - project/version requirement spec, this method returns the location of - a matching distribution (possibly after downloading it to `tmpdir`). - If `spec` is a locally existing file or directory name, it is simply - returned unchanged. If `spec` is a URL, it is downloaded to a subpath - of `tmpdir`, and the local filename is returned. Various errors may be - raised if a problem occurs during downloading. - """ - if not isinstance(spec, Requirement): - scheme = URL_SCHEME(spec) - if scheme: - # It's a url, download it to tmpdir - found = self._download_url(scheme.group(1), spec, tmpdir) - base, fragment = egg_info_for_url(spec) - if base.endswith('.py'): - found = self.gen_setup(found, fragment, tmpdir) - return found - elif os.path.exists(spec): - # Existing file or directory, just return it - return spec - else: - spec = parse_requirement_arg(spec) - return getattr(self.fetch_distribution(spec, tmpdir), 'location', None) - - def fetch_distribution( - self, requirement, tmpdir, force_scan=False, source=False, - develop_ok=False, local_index=None): - """Obtain a distribution suitable for fulfilling `requirement` - - `requirement` must be a ``pkg_resources.Requirement`` instance. - If necessary, or if the `force_scan` flag is set, the requirement is - searched for in the (online) package index as well as the locally - installed packages. If a distribution matching `requirement` is found, - the returned distribution's ``location`` is the value you would have - gotten from calling the ``download()`` method with the matching - distribution's URL or filename. If no matching distribution is found, - ``None`` is returned. - - If the `source` flag is set, only source distributions and source - checkout links will be considered. Unless the `develop_ok` flag is - set, development and system eggs (i.e., those using the ``.egg-info`` - format) will be ignored. - """ - # process a Requirement - self.info("Searching for %s", requirement) - skipped = {} - dist = None - - def find(req, env=None): - if env is None: - env = self - # Find a matching distribution; may be called more than once - - for dist in env[req.key]: - - if dist.precedence == DEVELOP_DIST and not develop_ok: - if dist not in skipped: - self.warn( - "Skipping development or system egg: %s", dist, - ) - skipped[dist] = 1 - continue - - test = ( - dist in req - and (dist.precedence <= SOURCE_DIST or not source) - ) - if test: - loc = self.download(dist.location, tmpdir) - dist.download_location = loc - if os.path.exists(dist.download_location): - return dist - - if force_scan: - self.prescan() - self.find_packages(requirement) - dist = find(requirement) - - if not dist and local_index is not None: - dist = find(requirement, local_index) - - if dist is None: - if self.to_scan is not None: - self.prescan() - dist = find(requirement) - - if dist is None and not force_scan: - self.find_packages(requirement) - dist = find(requirement) - - if dist is None: - self.warn( - "No local packages or working download links found for %s%s", - (source and "a source distribution of " or ""), - requirement, - ) - else: - self.info("Best match: %s", dist) - return dist.clone(location=dist.download_location) - - def fetch(self, requirement, tmpdir, force_scan=False, source=False): - """Obtain a file suitable for fulfilling `requirement` - - DEPRECATED; use the ``fetch_distribution()`` method now instead. For - backward compatibility, this routine is identical but returns the - ``location`` of the downloaded distribution instead of a distribution - object. - """ - dist = self.fetch_distribution(requirement, tmpdir, force_scan, source) - if dist is not None: - return dist.location - return None - - def gen_setup(self, filename, fragment, tmpdir): - match = EGG_FRAGMENT.match(fragment) - dists = match and [ - d for d in - interpret_distro_name(filename, match.group(1), None) if d.version - ] or [] - - if len(dists) == 1: # unambiguous ``#egg`` fragment - basename = os.path.basename(filename) - - # Make sure the file has been downloaded to the temp dir. - if os.path.dirname(filename) != tmpdir: - dst = os.path.join(tmpdir, basename) - from setuptools.command.easy_install import samefile - if not samefile(filename, dst): - shutil.copy2(filename, dst) - filename = dst - - with open(os.path.join(tmpdir, 'setup.py'), 'w') as file: - file.write( - "from setuptools import setup\n" - "setup(name=%r, version=%r, py_modules=[%r])\n" - % ( - dists[0].project_name, dists[0].version, - os.path.splitext(basename)[0] - ) - ) - return filename - - elif match: - raise DistutilsError( - "Can't unambiguously interpret project/version identifier %r; " - "any dashes in the name or version should be escaped using " - "underscores. %r" % (fragment, dists) - ) - else: - raise DistutilsError( - "Can't process plain .py files without an '#egg=name-version'" - " suffix to enable automatic setup script generation." - ) - - dl_blocksize = 8192 - - def _download_to(self, url, filename): - self.info("Downloading %s", url) - # Download the file - fp = None - try: - checker = HashChecker.from_url(url) - fp = self.open_url(url) - if isinstance(fp, urllib.error.HTTPError): - raise DistutilsError( - "Can't download %s: %s %s" % (url, fp.code, fp.msg) - ) - headers = fp.info() - blocknum = 0 - bs = self.dl_blocksize - size = -1 - if "content-length" in headers: - # Some servers return multiple Content-Length headers :( - sizes = get_all_headers(headers, 'Content-Length') - size = max(map(int, sizes)) - self.reporthook(url, filename, blocknum, bs, size) - with open(filename, 'wb') as tfp: - while True: - block = fp.read(bs) - if block: - checker.feed(block) - tfp.write(block) - blocknum += 1 - self.reporthook(url, filename, blocknum, bs, size) - else: - break - self.check_hash(checker, filename, tfp) - return headers - finally: - if fp: - fp.close() - - def reporthook(self, url, filename, blocknum, blksize, size): - pass # no-op - - def open_url(self, url, warning=None): - if url.startswith('file:'): - return local_open(url) - try: - return open_with_auth(url, self.opener) - except (ValueError, http_client.InvalidURL) as v: - msg = ' '.join([str(arg) for arg in v.args]) - if warning: - self.warn(warning, msg) - else: - raise DistutilsError('%s %s' % (url, msg)) - except urllib.error.HTTPError as v: - return v - except urllib.error.URLError as v: - if warning: - self.warn(warning, v.reason) - else: - raise DistutilsError("Download error for %s: %s" - % (url, v.reason)) - except http_client.BadStatusLine as v: - if warning: - self.warn(warning, v.line) - else: - raise DistutilsError( - '%s returned a bad status line. The server might be ' - 'down, %s' % - (url, v.line) - ) - except (http_client.HTTPException, socket.error) as v: - if warning: - self.warn(warning, v) - else: - raise DistutilsError("Download error for %s: %s" - % (url, v)) - - def _download_url(self, scheme, url, tmpdir): - # Determine download filename - # - name, fragment = egg_info_for_url(url) - if name: - while '..' in name: - name = name.replace('..', '.').replace('\\', '_') - else: - name = "__downloaded__" # default if URL has no path contents - - if name.endswith('.egg.zip'): - name = name[:-4] # strip the extra .zip before download - - filename = os.path.join(tmpdir, name) - - # Download the file - # - if scheme == 'svn' or scheme.startswith('svn+'): - return self._download_svn(url, filename) - elif scheme == 'git' or scheme.startswith('git+'): - return self._download_git(url, filename) - elif scheme.startswith('hg+'): - return self._download_hg(url, filename) - elif scheme == 'file': - return urllib.request.url2pathname(urllib.parse.urlparse(url)[2]) - else: - self.url_ok(url, True) # raises error if not allowed - return self._attempt_download(url, filename) - - def scan_url(self, url): - self.process_url(url, True) - - def _attempt_download(self, url, filename): - headers = self._download_to(url, filename) - if 'html' in headers.get('content-type', '').lower(): - return self._download_html(url, headers, filename) - else: - return filename - - def _download_html(self, url, headers, filename): - file = open(filename) - for line in file: - if line.strip(): - # Check for a subversion index page - if re.search(r'([^- ]+ - )?Revision \d+:', line): - # it's a subversion index page: - file.close() - os.unlink(filename) - return self._download_svn(url, filename) - break # not an index page - file.close() - os.unlink(filename) - raise DistutilsError("Unexpected HTML page found at " + url) - - def _download_svn(self, url, filename): - warnings.warn("SVN download support is deprecated", UserWarning) - url = url.split('#', 1)[0] # remove any fragment for svn's sake - creds = '' - if url.lower().startswith('svn:') and '@' in url: - scheme, netloc, path, p, q, f = urllib.parse.urlparse(url) - if not netloc and path.startswith('//') and '/' in path[2:]: - netloc, path = path[2:].split('/', 1) - auth, host = _splituser(netloc) - if auth: - if ':' in auth: - user, pw = auth.split(':', 1) - creds = " --username=%s --password=%s" % (user, pw) - else: - creds = " --username=" + auth - netloc = host - parts = scheme, netloc, url, p, q, f - url = urllib.parse.urlunparse(parts) - self.info("Doing subversion checkout from %s to %s", url, filename) - os.system("svn checkout%s -q %s %s" % (creds, url, filename)) - return filename - - @staticmethod - def _vcs_split_rev_from_url(url, pop_prefix=False): - scheme, netloc, path, query, frag = urllib.parse.urlsplit(url) - - scheme = scheme.split('+', 1)[-1] - - # Some fragment identification fails - path = path.split('#', 1)[0] - - rev = None - if '@' in path: - path, rev = path.rsplit('@', 1) - - # Also, discard fragment - url = urllib.parse.urlunsplit((scheme, netloc, path, query, '')) - - return url, rev - - def _download_git(self, url, filename): - filename = filename.split('#', 1)[0] - url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True) - - self.info("Doing git clone from %s to %s", url, filename) - os.system("git clone --quiet %s %s" % (url, filename)) - - if rev is not None: - self.info("Checking out %s", rev) - os.system("git -C %s checkout --quiet %s" % ( - filename, - rev, - )) - - return filename - - def _download_hg(self, url, filename): - filename = filename.split('#', 1)[0] - url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True) - - self.info("Doing hg clone from %s to %s", url, filename) - os.system("hg clone --quiet %s %s" % (url, filename)) - - if rev is not None: - self.info("Updating to %s", rev) - os.system("hg --cwd %s up -C -r %s -q" % ( - filename, - rev, - )) - - return filename - - def debug(self, msg, *args): - log.debug(msg, *args) - - def info(self, msg, *args): - log.info(msg, *args) - - def warn(self, msg, *args): - log.warn(msg, *args) - - -# This pattern matches a character entity reference (a decimal numeric -# references, a hexadecimal numeric reference, or a named reference). -entity_sub = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub - - -def decode_entity(match): - what = match.group(0) - return unescape(what) - - -def htmldecode(text): - """ - Decode HTML entities in the given text. - - >>> htmldecode( - ... 'https://../package_name-0.1.2.tar.gz' - ... '?tokena=A&tokenb=B">package_name-0.1.2.tar.gz') - 'https://../package_name-0.1.2.tar.gz?tokena=A&tokenb=B">package_name-0.1.2.tar.gz' - """ - return entity_sub(decode_entity, text) - - -def socket_timeout(timeout=15): - def _socket_timeout(func): - def _socket_timeout(*args, **kwargs): - old_timeout = socket.getdefaulttimeout() - socket.setdefaulttimeout(timeout) - try: - return func(*args, **kwargs) - finally: - socket.setdefaulttimeout(old_timeout) - - return _socket_timeout - - return _socket_timeout - - -def _encode_auth(auth): - """ - A function compatible with Python 2.3-3.3 that will encode - auth from a URL suitable for an HTTP header. - >>> str(_encode_auth('username%3Apassword')) - 'dXNlcm5hbWU6cGFzc3dvcmQ=' - - Long auth strings should not cause a newline to be inserted. - >>> long_auth = 'username:' + 'password'*10 - >>> chr(10) in str(_encode_auth(long_auth)) - False - """ - auth_s = urllib.parse.unquote(auth) - # convert to bytes - auth_bytes = auth_s.encode() - encoded_bytes = base64.b64encode(auth_bytes) - # convert back to a string - encoded = encoded_bytes.decode() - # strip the trailing carriage return - return encoded.replace('\n', '') - - -class Credential: - """ - A username/password pair. Use like a namedtuple. - """ - - def __init__(self, username, password): - self.username = username - self.password = password - - def __iter__(self): - yield self.username - yield self.password - - def __str__(self): - return '%(username)s:%(password)s' % vars(self) - - -class PyPIConfig(configparser.RawConfigParser): - def __init__(self): - """ - Load from ~/.pypirc - """ - defaults = dict.fromkeys(['username', 'password', 'repository'], '') - configparser.RawConfigParser.__init__(self, defaults) - - rc = os.path.join(os.path.expanduser('~'), '.pypirc') - if os.path.exists(rc): - self.read(rc) - - @property - def creds_by_repository(self): - sections_with_repositories = [ - section for section in self.sections() - if self.get(section, 'repository').strip() - ] - - return dict(map(self._get_repo_cred, sections_with_repositories)) - - def _get_repo_cred(self, section): - repo = self.get(section, 'repository').strip() - return repo, Credential( - self.get(section, 'username').strip(), - self.get(section, 'password').strip(), - ) - - def find_credential(self, url): - """ - If the URL indicated appears to be a repository defined in this - config, return the credential for that repository. - """ - for repository, cred in self.creds_by_repository.items(): - if url.startswith(repository): - return cred - - -def open_with_auth(url, opener=urllib.request.urlopen): - """Open a urllib2 request, handling HTTP authentication""" - - parsed = urllib.parse.urlparse(url) - scheme, netloc, path, params, query, frag = parsed - - # Double scheme does not raise on Mac OS X as revealed by a - # failing test. We would expect "nonnumeric port". Refs #20. - if netloc.endswith(':'): - raise http_client.InvalidURL("nonnumeric port: ''") - - if scheme in ('http', 'https'): - auth, address = _splituser(netloc) - else: - auth = None - - if not auth: - cred = PyPIConfig().find_credential(url) - if cred: - auth = str(cred) - info = cred.username, url - log.info('Authenticating as %s for %s (from .pypirc)', *info) - - if auth: - auth = "Basic " + _encode_auth(auth) - parts = scheme, address, path, params, query, frag - new_url = urllib.parse.urlunparse(parts) - request = urllib.request.Request(new_url) - request.add_header("Authorization", auth) - else: - request = urllib.request.Request(url) - - request.add_header('User-Agent', user_agent) - fp = opener(request) - - if auth: - # Put authentication info back into request URL if same host, - # so that links found on the page will work - s2, h2, path2, param2, query2, frag2 = urllib.parse.urlparse(fp.url) - if s2 == scheme and h2 == address: - parts = s2, netloc, path2, param2, query2, frag2 - fp.url = urllib.parse.urlunparse(parts) - - return fp - - -# copy of urllib.parse._splituser from Python 3.8 -def _splituser(host): - """splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'.""" - user, delim, host = host.rpartition('@') - return (user if delim else None), host - - -# adding a timeout to avoid freezing package_index -open_with_auth = socket_timeout(_SOCKET_TIMEOUT)(open_with_auth) - - -def fix_sf_url(url): - return url # backward compatibility - - -def local_open(url): - """Read a local path, with special support for directories""" - scheme, server, path, param, query, frag = urllib.parse.urlparse(url) - filename = urllib.request.url2pathname(path) - if os.path.isfile(filename): - return urllib.request.urlopen(url) - elif path.endswith('/') and os.path.isdir(filename): - files = [] - for f in os.listdir(filename): - filepath = os.path.join(filename, f) - if f == 'index.html': - with open(filepath, 'r') as fp: - body = fp.read() - break - elif os.path.isdir(filepath): - f += '/' - files.append('<a href="{name}">{name}</a>'.format(name=f)) - else: - tmpl = ( - "<html><head><title>{url}" - "{files}") - body = tmpl.format(url=url, files='\n'.join(files)) - status, message = 200, "OK" - else: - status, message, body = 404, "Path not found", "Not found" - - headers = {'content-type': 'text/html'} - body_stream = six.StringIO(body) - return urllib.error.HTTPError(url, status, message, headers, body_stream) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/py27compat.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/py27compat.py deleted file mode 100644 index 1d57360..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/py27compat.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -Compatibility Support for Python 2.7 and earlier -""" - -import sys -import platform - -from setuptools.extern import six - - -def get_all_headers(message, key): - """ - Given an HTTPMessage, return all headers matching a given key. - """ - return message.get_all(key) - - -if six.PY2: - def get_all_headers(message, key): - return message.getheaders(key) - - -linux_py2_ascii = ( - platform.system() == 'Linux' and - six.PY2 -) - -rmtree_safe = str if linux_py2_ascii else lambda x: x -"""Workaround for http://bugs.python.org/issue24672""" - - -try: - from ._imp import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE - from ._imp import get_frozen_object, get_module -except ImportError: - import imp - from imp import PY_COMPILED, PY_FROZEN, PY_SOURCE # noqa - - def find_module(module, paths=None): - """Just like 'imp.find_module()', but with package support""" - parts = module.split('.') - while parts: - part = parts.pop(0) - f, path, (suffix, mode, kind) = info = imp.find_module(part, paths) - - if kind == imp.PKG_DIRECTORY: - parts = parts or ['__init__'] - paths = [path] - - elif parts: - raise ImportError("Can't find %r in %s" % (parts, module)) - - return info - - def get_frozen_object(module, paths): - return imp.get_frozen_object(module) - - def get_module(module, paths, info): - imp.load_module(module, *info) - return sys.modules[module] diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/py31compat.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/py31compat.py deleted file mode 100644 index e1da7ee..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/py31compat.py +++ /dev/null @@ -1,32 +0,0 @@ -__all__ = [] - -__metaclass__ = type - - -try: - # Python >=3.2 - from tempfile import TemporaryDirectory -except ImportError: - import shutil - import tempfile - - class TemporaryDirectory: - """ - Very simple temporary directory context manager. - Will try to delete afterward, but will also ignore OS and similar - errors on deletion. - """ - - def __init__(self, **kwargs): - self.name = None # Handle mkdtemp raising an exception - self.name = tempfile.mkdtemp(**kwargs) - - def __enter__(self): - return self.name - - def __exit__(self, exctype, excvalue, exctrace): - try: - shutil.rmtree(self.name, True) - except OSError: # removal errors are not the only possible - pass - self.name = None diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/py33compat.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/py33compat.py deleted file mode 100644 index cb69443..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/py33compat.py +++ /dev/null @@ -1,59 +0,0 @@ -import dis -import array -import collections - -try: - import html -except ImportError: - html = None - -from setuptools.extern import six -from setuptools.extern.six.moves import html_parser - -__metaclass__ = type - -OpArg = collections.namedtuple('OpArg', 'opcode arg') - - -class Bytecode_compat: - def __init__(self, code): - self.code = code - - def __iter__(self): - """Yield '(op,arg)' pair for each operation in code object 'code'""" - - bytes = array.array('b', self.code.co_code) - eof = len(self.code.co_code) - - ptr = 0 - extended_arg = 0 - - while ptr < eof: - - op = bytes[ptr] - - if op >= dis.HAVE_ARGUMENT: - - arg = bytes[ptr + 1] + bytes[ptr + 2] * 256 + extended_arg - ptr += 3 - - if op == dis.EXTENDED_ARG: - long_type = six.integer_types[-1] - extended_arg = arg * long_type(65536) - continue - - else: - arg = None - ptr += 1 - - yield OpArg(op, arg) - - -Bytecode = getattr(dis, 'Bytecode', Bytecode_compat) - - -unescape = getattr(html, 'unescape', None) -if unescape is None: - # HTMLParser.unescape is deprecated since Python 3.4, and will be removed - # from 3.9. - unescape = html_parser.HTMLParser().unescape diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/py34compat.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/py34compat.py deleted file mode 100644 index 3ad9172..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/py34compat.py +++ /dev/null @@ -1,13 +0,0 @@ -import importlib - -try: - import importlib.util -except ImportError: - pass - - -try: - module_from_spec = importlib.util.module_from_spec -except AttributeError: - def module_from_spec(spec): - return spec.loader.load_module(spec.name) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/sandbox.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/sandbox.py deleted file mode 100644 index 685f3f7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/sandbox.py +++ /dev/null @@ -1,491 +0,0 @@ -import os -import sys -import tempfile -import operator -import functools -import itertools -import re -import contextlib -import pickle -import textwrap - -from setuptools.extern import six -from setuptools.extern.six.moves import builtins, map - -import pkg_resources.py31compat - -if sys.platform.startswith('java'): - import org.python.modules.posix.PosixModule as _os -else: - _os = sys.modules[os.name] -try: - _file = file -except NameError: - _file = None -_open = open -from distutils.errors import DistutilsError -from pkg_resources import working_set - - -__all__ = [ - "AbstractSandbox", "DirectorySandbox", "SandboxViolation", "run_setup", -] - - -def _execfile(filename, globals, locals=None): - """ - Python 3 implementation of execfile. - """ - mode = 'rb' - with open(filename, mode) as stream: - script = stream.read() - if locals is None: - locals = globals - code = compile(script, filename, 'exec') - exec(code, globals, locals) - - -@contextlib.contextmanager -def save_argv(repl=None): - saved = sys.argv[:] - if repl is not None: - sys.argv[:] = repl - try: - yield saved - finally: - sys.argv[:] = saved - - -@contextlib.contextmanager -def save_path(): - saved = sys.path[:] - try: - yield saved - finally: - sys.path[:] = saved - - -@contextlib.contextmanager -def override_temp(replacement): - """ - Monkey-patch tempfile.tempdir with replacement, ensuring it exists - """ - pkg_resources.py31compat.makedirs(replacement, exist_ok=True) - - saved = tempfile.tempdir - - tempfile.tempdir = replacement - - try: - yield - finally: - tempfile.tempdir = saved - - -@contextlib.contextmanager -def pushd(target): - saved = os.getcwd() - os.chdir(target) - try: - yield saved - finally: - os.chdir(saved) - - -class UnpickleableException(Exception): - """ - An exception representing another Exception that could not be pickled. - """ - - @staticmethod - def dump(type, exc): - """ - Always return a dumped (pickled) type and exc. If exc can't be pickled, - wrap it in UnpickleableException first. - """ - try: - return pickle.dumps(type), pickle.dumps(exc) - except Exception: - # get UnpickleableException inside the sandbox - from setuptools.sandbox import UnpickleableException as cls - return cls.dump(cls, cls(repr(exc))) - - -class ExceptionSaver: - """ - A Context Manager that will save an exception, serialized, and restore it - later. - """ - - def __enter__(self): - return self - - def __exit__(self, type, exc, tb): - if not exc: - return - - # dump the exception - self._saved = UnpickleableException.dump(type, exc) - self._tb = tb - - # suppress the exception - return True - - def resume(self): - "restore and re-raise any exception" - - if '_saved' not in vars(self): - return - - type, exc = map(pickle.loads, self._saved) - six.reraise(type, exc, self._tb) - - -@contextlib.contextmanager -def save_modules(): - """ - Context in which imported modules are saved. - - Translates exceptions internal to the context into the equivalent exception - outside the context. - """ - saved = sys.modules.copy() - with ExceptionSaver() as saved_exc: - yield saved - - sys.modules.update(saved) - # remove any modules imported since - del_modules = ( - mod_name for mod_name in sys.modules - if mod_name not in saved - # exclude any encodings modules. See #285 - and not mod_name.startswith('encodings.') - ) - _clear_modules(del_modules) - - saved_exc.resume() - - -def _clear_modules(module_names): - for mod_name in list(module_names): - del sys.modules[mod_name] - - -@contextlib.contextmanager -def save_pkg_resources_state(): - saved = pkg_resources.__getstate__() - try: - yield saved - finally: - pkg_resources.__setstate__(saved) - - -@contextlib.contextmanager -def setup_context(setup_dir): - temp_dir = os.path.join(setup_dir, 'temp') - with save_pkg_resources_state(): - with save_modules(): - hide_setuptools() - with save_path(): - with save_argv(): - with override_temp(temp_dir): - with pushd(setup_dir): - # ensure setuptools commands are available - __import__('setuptools') - yield - - -def _needs_hiding(mod_name): - """ - >>> _needs_hiding('setuptools') - True - >>> _needs_hiding('pkg_resources') - True - >>> _needs_hiding('setuptools_plugin') - False - >>> _needs_hiding('setuptools.__init__') - True - >>> _needs_hiding('distutils') - True - >>> _needs_hiding('os') - False - >>> _needs_hiding('Cython') - True - """ - pattern = re.compile(r'(setuptools|pkg_resources|distutils|Cython)(\.|$)') - return bool(pattern.match(mod_name)) - - -def hide_setuptools(): - """ - Remove references to setuptools' modules from sys.modules to allow the - invocation to import the most appropriate setuptools. This technique is - necessary to avoid issues such as #315 where setuptools upgrading itself - would fail to find a function declared in the metadata. - """ - modules = filter(_needs_hiding, sys.modules) - _clear_modules(modules) - - -def run_setup(setup_script, args): - """Run a distutils setup script, sandboxed in its directory""" - setup_dir = os.path.abspath(os.path.dirname(setup_script)) - with setup_context(setup_dir): - try: - sys.argv[:] = [setup_script] + list(args) - sys.path.insert(0, setup_dir) - # reset to include setup dir, w/clean callback list - working_set.__init__() - working_set.callbacks.append(lambda dist: dist.activate()) - - # __file__ should be a byte string on Python 2 (#712) - dunder_file = ( - setup_script - if isinstance(setup_script, str) else - setup_script.encode(sys.getfilesystemencoding()) - ) - - with DirectorySandbox(setup_dir): - ns = dict(__file__=dunder_file, __name__='__main__') - _execfile(setup_script, ns) - except SystemExit as v: - if v.args and v.args[0]: - raise - # Normal exit, just return - - -class AbstractSandbox: - """Wrap 'os' module and 'open()' builtin for virtualizing setup scripts""" - - _active = False - - def __init__(self): - self._attrs = [ - name for name in dir(_os) - if not name.startswith('_') and hasattr(self, name) - ] - - def _copy(self, source): - for name in self._attrs: - setattr(os, name, getattr(source, name)) - - def __enter__(self): - self._copy(self) - if _file: - builtins.file = self._file - builtins.open = self._open - self._active = True - - def __exit__(self, exc_type, exc_value, traceback): - self._active = False - if _file: - builtins.file = _file - builtins.open = _open - self._copy(_os) - - def run(self, func): - """Run 'func' under os sandboxing""" - with self: - return func() - - def _mk_dual_path_wrapper(name): - original = getattr(_os, name) - - def wrap(self, src, dst, *args, **kw): - if self._active: - src, dst = self._remap_pair(name, src, dst, *args, **kw) - return original(src, dst, *args, **kw) - - return wrap - - for name in ["rename", "link", "symlink"]: - if hasattr(_os, name): - locals()[name] = _mk_dual_path_wrapper(name) - - def _mk_single_path_wrapper(name, original=None): - original = original or getattr(_os, name) - - def wrap(self, path, *args, **kw): - if self._active: - path = self._remap_input(name, path, *args, **kw) - return original(path, *args, **kw) - - return wrap - - if _file: - _file = _mk_single_path_wrapper('file', _file) - _open = _mk_single_path_wrapper('open', _open) - for name in [ - "stat", "listdir", "chdir", "open", "chmod", "chown", "mkdir", - "remove", "unlink", "rmdir", "utime", "lchown", "chroot", "lstat", - "startfile", "mkfifo", "mknod", "pathconf", "access" - ]: - if hasattr(_os, name): - locals()[name] = _mk_single_path_wrapper(name) - - def _mk_single_with_return(name): - original = getattr(_os, name) - - def wrap(self, path, *args, **kw): - if self._active: - path = self._remap_input(name, path, *args, **kw) - return self._remap_output(name, original(path, *args, **kw)) - return original(path, *args, **kw) - - return wrap - - for name in ['readlink', 'tempnam']: - if hasattr(_os, name): - locals()[name] = _mk_single_with_return(name) - - def _mk_query(name): - original = getattr(_os, name) - - def wrap(self, *args, **kw): - retval = original(*args, **kw) - if self._active: - return self._remap_output(name, retval) - return retval - - return wrap - - for name in ['getcwd', 'tmpnam']: - if hasattr(_os, name): - locals()[name] = _mk_query(name) - - def _validate_path(self, path): - """Called to remap or validate any path, whether input or output""" - return path - - def _remap_input(self, operation, path, *args, **kw): - """Called for path inputs""" - return self._validate_path(path) - - def _remap_output(self, operation, path): - """Called for path outputs""" - return self._validate_path(path) - - def _remap_pair(self, operation, src, dst, *args, **kw): - """Called for path pairs like rename, link, and symlink operations""" - return ( - self._remap_input(operation + '-from', src, *args, **kw), - self._remap_input(operation + '-to', dst, *args, **kw) - ) - - -if hasattr(os, 'devnull'): - _EXCEPTIONS = [os.devnull,] -else: - _EXCEPTIONS = [] - - -class DirectorySandbox(AbstractSandbox): - """Restrict operations to a single subdirectory - pseudo-chroot""" - - write_ops = dict.fromkeys([ - "open", "chmod", "chown", "mkdir", "remove", "unlink", "rmdir", - "utime", "lchown", "chroot", "mkfifo", "mknod", "tempnam", - ]) - - _exception_patterns = [ - # Allow lib2to3 to attempt to save a pickled grammar object (#121) - r'.*lib2to3.*\.pickle$', - ] - "exempt writing to paths that match the pattern" - - def __init__(self, sandbox, exceptions=_EXCEPTIONS): - self._sandbox = os.path.normcase(os.path.realpath(sandbox)) - self._prefix = os.path.join(self._sandbox, '') - self._exceptions = [ - os.path.normcase(os.path.realpath(path)) - for path in exceptions - ] - AbstractSandbox.__init__(self) - - def _violation(self, operation, *args, **kw): - from setuptools.sandbox import SandboxViolation - raise SandboxViolation(operation, args, kw) - - if _file: - - def _file(self, path, mode='r', *args, **kw): - if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path): - self._violation("file", path, mode, *args, **kw) - return _file(path, mode, *args, **kw) - - def _open(self, path, mode='r', *args, **kw): - if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path): - self._violation("open", path, mode, *args, **kw) - return _open(path, mode, *args, **kw) - - def tmpnam(self): - self._violation("tmpnam") - - def _ok(self, path): - active = self._active - try: - self._active = False - realpath = os.path.normcase(os.path.realpath(path)) - return ( - self._exempted(realpath) - or realpath == self._sandbox - or realpath.startswith(self._prefix) - ) - finally: - self._active = active - - def _exempted(self, filepath): - start_matches = ( - filepath.startswith(exception) - for exception in self._exceptions - ) - pattern_matches = ( - re.match(pattern, filepath) - for pattern in self._exception_patterns - ) - candidates = itertools.chain(start_matches, pattern_matches) - return any(candidates) - - def _remap_input(self, operation, path, *args, **kw): - """Called for path inputs""" - if operation in self.write_ops and not self._ok(path): - self._violation(operation, os.path.realpath(path), *args, **kw) - return path - - def _remap_pair(self, operation, src, dst, *args, **kw): - """Called for path pairs like rename, link, and symlink operations""" - if not self._ok(src) or not self._ok(dst): - self._violation(operation, src, dst, *args, **kw) - return (src, dst) - - def open(self, file, flags, mode=0o777, *args, **kw): - """Called for low-level os.open()""" - if flags & WRITE_FLAGS and not self._ok(file): - self._violation("os.open", file, flags, mode, *args, **kw) - return _os.open(file, flags, mode, *args, **kw) - - -WRITE_FLAGS = functools.reduce( - operator.or_, [getattr(_os, a, 0) for a in - "O_WRONLY O_RDWR O_APPEND O_CREAT O_TRUNC O_TEMPORARY".split()] -) - - -class SandboxViolation(DistutilsError): - """A setup script attempted to modify the filesystem outside the sandbox""" - - tmpl = textwrap.dedent(""" - SandboxViolation: {cmd}{args!r} {kwargs} - - The package setup script has attempted to modify files on your system - that are not within the EasyInstall build area, and has been aborted. - - This package cannot be safely installed by EasyInstall, and may not - support alternate installation locations even if you run its setup - script by hand. Please inform the package's author and the EasyInstall - maintainers to find out if a fix or workaround is available. - """).lstrip() - - def __str__(self): - cmd, args, kwargs = self.args - return self.tmpl.format(**locals()) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/script (dev).tmpl b/plotter-app/venv/lib/python3.8/site-packages/setuptools/script (dev).tmpl deleted file mode 100644 index 39a24b0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/script (dev).tmpl +++ /dev/null @@ -1,6 +0,0 @@ -# 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/plotter-app/venv/lib/python3.8/site-packages/setuptools/script.tmpl b/plotter-app/venv/lib/python3.8/site-packages/setuptools/script.tmpl deleted file mode 100644 index ff5efbc..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/script.tmpl +++ /dev/null @@ -1,3 +0,0 @@ -# EASY-INSTALL-SCRIPT: %(spec)r,%(script_name)r -__requires__ = %(spec)r -__import__('pkg_resources').run_script(%(spec)r, %(script_name)r) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/site-patch.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/site-patch.py deleted file mode 100644 index 40b00de..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/site-patch.py +++ /dev/null @@ -1,74 +0,0 @@ -def __boot(): - import sys - import os - PYTHONPATH = os.environ.get('PYTHONPATH') - if PYTHONPATH is None or (sys.platform == 'win32' and not PYTHONPATH): - PYTHONPATH = [] - else: - PYTHONPATH = PYTHONPATH.split(os.pathsep) - - pic = getattr(sys, 'path_importer_cache', {}) - stdpath = sys.path[len(PYTHONPATH):] - mydir = os.path.dirname(__file__) - - for item in stdpath: - if item == mydir or not item: - continue # skip if current dir. on Windows, or my own directory - importer = pic.get(item) - if importer is not None: - loader = importer.find_module('site') - if loader is not None: - # This should actually reload the current module - loader.load_module('site') - break - else: - try: - import imp # Avoid import loop in Python 3 - stream, path, descr = imp.find_module('site', [item]) - except ImportError: - continue - if stream is None: - continue - try: - # This should actually reload the current module - imp.load_module('site', stream, path, descr) - finally: - stream.close() - break - else: - raise ImportError("Couldn't find the real 'site' module") - - known_paths = dict([(makepath(item)[1], 1) for item in sys.path]) # 2.2 comp - - oldpos = getattr(sys, '__egginsert', 0) # save old insertion position - sys.__egginsert = 0 # and reset the current one - - for item in PYTHONPATH: - addsitedir(item) - - sys.__egginsert += oldpos # restore effective old position - - d, nd = makepath(stdpath[0]) - insert_at = None - new_path = [] - - for item in sys.path: - p, np = makepath(item) - - if np == nd and insert_at is None: - # We've hit the first 'system' path entry, so added entries go here - insert_at = len(new_path) - - if np in known_paths or insert_at is None: - new_path.append(item) - else: - # new path after the insert point, back-insert it - new_path.insert(insert_at, item) - insert_at += 1 - - sys.path[:] = new_path - - -if __name__ == 'site': - __boot() - del __boot diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/ssl_support.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/ssl_support.py deleted file mode 100644 index 226db69..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/ssl_support.py +++ /dev/null @@ -1,260 +0,0 @@ -import os -import socket -import atexit -import re -import functools - -from setuptools.extern.six.moves import urllib, http_client, map, filter - -from pkg_resources import ResolutionError, ExtractionError - -try: - import ssl -except ImportError: - ssl = None - -__all__ = [ - 'VerifyingHTTPSHandler', 'find_ca_bundle', 'is_available', 'cert_paths', - 'opener_for' -] - -cert_paths = """ -/etc/pki/tls/certs/ca-bundle.crt -/etc/ssl/certs/ca-certificates.crt -/usr/share/ssl/certs/ca-bundle.crt -/usr/local/share/certs/ca-root.crt -/etc/ssl/cert.pem -/System/Library/OpenSSL/certs/cert.pem -/usr/local/share/certs/ca-root-nss.crt -/etc/ssl/ca-bundle.pem -""".strip().split() - -try: - HTTPSHandler = urllib.request.HTTPSHandler - HTTPSConnection = http_client.HTTPSConnection -except AttributeError: - HTTPSHandler = HTTPSConnection = object - -is_available = ssl is not None and object not in (HTTPSHandler, HTTPSConnection) - - -try: - from ssl import CertificateError, match_hostname -except ImportError: - try: - from backports.ssl_match_hostname import CertificateError - from backports.ssl_match_hostname import match_hostname - except ImportError: - CertificateError = None - match_hostname = None - -if not CertificateError: - - class CertificateError(ValueError): - pass - - -if not match_hostname: - - def _dnsname_match(dn, hostname, max_wildcards=1): - """Matching according to RFC 6125, section 6.4.3 - - https://tools.ietf.org/html/rfc6125#section-6.4.3 - """ - pats = [] - if not dn: - return False - - # Ported from python3-syntax: - # leftmost, *remainder = dn.split(r'.') - parts = dn.split(r'.') - leftmost = parts[0] - remainder = parts[1:] - - wildcards = leftmost.count('*') - if wildcards > max_wildcards: - # Issue #17980: avoid denials of service by refusing more - # than one wildcard per fragment. A survey of established - # policy among SSL implementations showed it to be a - # reasonable choice. - raise CertificateError( - "too many wildcards in certificate DNS name: " + repr(dn)) - - # speed up common case w/o wildcards - if not wildcards: - return dn.lower() == hostname.lower() - - # RFC 6125, section 6.4.3, subitem 1. - # The client SHOULD NOT attempt to match a presented identifier in which - # the wildcard character comprises a label other than the left-most label. - if leftmost == '*': - # When '*' is a fragment by itself, it matches a non-empty dotless - # fragment. - pats.append('[^.]+') - elif leftmost.startswith('xn--') or hostname.startswith('xn--'): - # RFC 6125, section 6.4.3, subitem 3. - # The client SHOULD NOT attempt to match a presented identifier - # where the wildcard character is embedded within an A-label or - # U-label of an internationalized domain name. - pats.append(re.escape(leftmost)) - else: - # Otherwise, '*' matches any dotless string, e.g. www* - pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) - - # add the remaining fragments, ignore any wildcards - for frag in remainder: - pats.append(re.escape(frag)) - - pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) - return pat.match(hostname) - - def match_hostname(cert, hostname): - """Verify that *cert* (in decoded format as returned by - SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 - rules are followed, but IP addresses are not accepted for *hostname*. - - CertificateError is raised on failure. On success, the function - returns nothing. - """ - if not cert: - raise ValueError("empty or no certificate") - dnsnames = [] - san = cert.get('subjectAltName', ()) - for key, value in san: - if key == 'DNS': - if _dnsname_match(value, hostname): - return - dnsnames.append(value) - if not dnsnames: - # The subject is only checked when there is no dNSName entry - # in subjectAltName - for sub in cert.get('subject', ()): - for key, value in sub: - # XXX according to RFC 2818, the most specific Common Name - # must be used. - if key == 'commonName': - if _dnsname_match(value, hostname): - return - dnsnames.append(value) - if len(dnsnames) > 1: - raise CertificateError("hostname %r " - "doesn't match either of %s" - % (hostname, ', '.join(map(repr, dnsnames)))) - elif len(dnsnames) == 1: - raise CertificateError("hostname %r " - "doesn't match %r" - % (hostname, dnsnames[0])) - else: - raise CertificateError("no appropriate commonName or " - "subjectAltName fields were found") - - -class VerifyingHTTPSHandler(HTTPSHandler): - """Simple verifying handler: no auth, subclasses, timeouts, etc.""" - - def __init__(self, ca_bundle): - self.ca_bundle = ca_bundle - HTTPSHandler.__init__(self) - - def https_open(self, req): - return self.do_open( - lambda host, **kw: VerifyingHTTPSConn(host, self.ca_bundle, **kw), req - ) - - -class VerifyingHTTPSConn(HTTPSConnection): - """Simple verifying connection: no auth, subclasses, timeouts, etc.""" - - def __init__(self, host, ca_bundle, **kw): - HTTPSConnection.__init__(self, host, **kw) - self.ca_bundle = ca_bundle - - def connect(self): - sock = socket.create_connection( - (self.host, self.port), getattr(self, 'source_address', None) - ) - - # Handle the socket if a (proxy) tunnel is present - if hasattr(self, '_tunnel') and getattr(self, '_tunnel_host', None): - self.sock = sock - self._tunnel() - # http://bugs.python.org/issue7776: Python>=3.4.1 and >=2.7.7 - # change self.host to mean the proxy server host when tunneling is - # being used. Adapt, since we are interested in the destination - # host for the match_hostname() comparison. - actual_host = self._tunnel_host - else: - actual_host = self.host - - if hasattr(ssl, 'create_default_context'): - ctx = ssl.create_default_context(cafile=self.ca_bundle) - self.sock = ctx.wrap_socket(sock, server_hostname=actual_host) - else: - # This is for python < 2.7.9 and < 3.4? - self.sock = ssl.wrap_socket( - sock, cert_reqs=ssl.CERT_REQUIRED, ca_certs=self.ca_bundle - ) - try: - match_hostname(self.sock.getpeercert(), actual_host) - except CertificateError: - self.sock.shutdown(socket.SHUT_RDWR) - self.sock.close() - raise - - -def opener_for(ca_bundle=None): - """Get a urlopen() replacement that uses ca_bundle for verification""" - return urllib.request.build_opener( - VerifyingHTTPSHandler(ca_bundle or find_ca_bundle()) - ).open - - -# from jaraco.functools -def once(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - if not hasattr(func, 'always_returns'): - func.always_returns = func(*args, **kwargs) - return func.always_returns - return wrapper - - -@once -def get_win_certfile(): - try: - import wincertstore - except ImportError: - return None - - class CertFile(wincertstore.CertFile): - def __init__(self): - super(CertFile, self).__init__() - atexit.register(self.close) - - def close(self): - try: - super(CertFile, self).close() - except OSError: - pass - - _wincerts = CertFile() - _wincerts.addstore('CA') - _wincerts.addstore('ROOT') - return _wincerts.name - - -def find_ca_bundle(): - """Return an existing CA bundle path, or None""" - extant_cert_paths = filter(os.path.isfile, cert_paths) - return ( - get_win_certfile() - or next(extant_cert_paths, None) - or _certifi_where() - ) - - -def _certifi_where(): - try: - return __import__('certifi').where() - except (ImportError, ResolutionError, ExtractionError): - pass diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/unicode_utils.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/unicode_utils.py deleted file mode 100644 index 7c63efd..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/unicode_utils.py +++ /dev/null @@ -1,44 +0,0 @@ -import unicodedata -import sys - -from setuptools.extern import six - - -# HFS Plus uses decomposed UTF-8 -def decompose(path): - if isinstance(path, six.text_type): - 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, six.text_type): - 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 - - -def try_encode(string, enc): - "turn unicode encoding into a functional routine" - try: - return string.encode(enc) - except UnicodeEncodeError: - return None diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/version.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/version.py deleted file mode 100644 index 95e1869..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/version.py +++ /dev/null @@ -1,6 +0,0 @@ -import pkg_resources - -try: - __version__ = pkg_resources.get_distribution('setuptools').version -except Exception: - __version__ = 'unknown' diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/wheel.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/wheel.py deleted file mode 100644 index 025aaa8..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/wheel.py +++ /dev/null @@ -1,220 +0,0 @@ -"""Wheels support.""" - -from distutils.util import get_platform -from distutils import log -import email -import itertools -import os -import posixpath -import re -import zipfile - -import pkg_resources -import setuptools -from pkg_resources import parse_version -from setuptools.extern.packaging.tags import sys_tags -from setuptools.extern.packaging.utils import canonicalize_name -from setuptools.extern.six import PY3 -from setuptools.command.egg_info import write_requirements - - -__metaclass__ = type - - -WHEEL_NAME = re.compile( - r"""^(?P.+?)-(?P\d.*?) - ((-(?P\d.*?))?-(?P.+?)-(?P.+?)-(?P.+?) - )\.whl$""", - re.VERBOSE).match - -NAMESPACE_PACKAGE_INIT = '''\ -try: - __import__('pkg_resources').declare_namespace(__name__) -except ImportError: - __path__ = __import__('pkgutil').extend_path(__path__, __name__) -''' - - -def unpack(src_dir, dst_dir): - '''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) - - -class Wheel: - - def __init__(self, filename): - match = WHEEL_NAME(os.path.basename(filename)) - if match is None: - raise ValueError('invalid wheel name: %r' % filename) - 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 is compatible with the current platform?''' - supported_tags = set((t.interpreter, t.abi, t.platform) for t in sys_tags()) - return next((True for t in self.tags() if t in supported_tags), False) - - def egg_name(self): - return pkg_resources.Distribution( - project_name=self.project_name, version=self.version, - platform=(None if self.platform == 'any' else get_platform()), - ).egg_name() + '.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): - '''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 = '%s-%s' % (self.project_name, self.version) - dist_info = self.get_dist_info(zf) - dist_data = '%s.data' % dist_basename - 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') if PY3 else fp.read() - 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( - 'unsupported wheel format version: %s' % wheel_version) - # Extract to target directory. - os.mkdir(destination_eggdir) - zf.extractall(destination_eggdir) - # Convert metadata. - dist_info = os.path.join(destination_eggdir, dist_info) - dist = pkg_resources.Distribution.from_location( - destination_eggdir, dist_info, - metadata=pkg_resources.PathMetadata(destination_eggdir, dist_info), - ) - - # 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.marker = None - return str(req) - install_requires = list(sorted(map(raw_req, dist.requires()))) - extras_require = { - extra: sorted( - req - for req in map(raw_req, dist.requires((extra,))) - if req not in install_requires - ) - for extra in dist.extras - } - 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, - ), - ) - # Temporarily disable info traces. - log_threshold = log._global_log.threshold - log.set_threshold(log.WARN) - try: - write_requirements( - setup_dist.get_command_obj('egg_info'), - None, - os.path.join(egg_info, 'requires.txt'), - ) - finally: - log.set_threshold(log_threshold) - - @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): - with open(namespace_packages) as fp: - namespace_packages = fp.read().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') as fp: - fp.write(NAMESPACE_PACKAGE_INIT) diff --git a/plotter-app/venv/lib/python3.8/site-packages/setuptools/windows_support.py b/plotter-app/venv/lib/python3.8/site-packages/setuptools/windows_support.py deleted file mode 100644 index cb977cf..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/setuptools/windows_support.py +++ /dev/null @@ -1,29 +0,0 @@ -import platform -import ctypes - - -def windows_only(func): - if platform.system() != 'Windows': - return lambda *args, **kwargs: None - return func - - -@windows_only -def hide_file(path): - """ - Set the hidden attribute on a file or directory. - - From http://stackoverflow.com/questions/19622133/ - - `path` must be text. - """ - __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/plotter-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/INSTALLER b/plotter-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/plotter-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/LICENSE b/plotter-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/LICENSE deleted file mode 100644 index de66331..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -Copyright (c) 2010-2020 Benjamin Peterson - -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/plotter-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/METADATA b/plotter-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/METADATA deleted file mode 100644 index 6d7525c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/METADATA +++ /dev/null @@ -1,49 +0,0 @@ -Metadata-Version: 2.1 -Name: six -Version: 1.16.0 -Summary: Python 2 and 3 compatibility utilities -Home-page: https://github.com/benjaminp/six -Author: Benjamin Peterson -Author-email: benjamin@python.org -License: MIT -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 3 -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Topic :: Software Development :: Libraries -Classifier: Topic :: Utilities -Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.* - -.. image:: https://img.shields.io/pypi/v/six.svg - :target: https://pypi.org/project/six/ - :alt: six on PyPI - -.. image:: https://travis-ci.org/benjaminp/six.svg?branch=master - :target: https://travis-ci.org/benjaminp/six - :alt: six on TravisCI - -.. image:: https://readthedocs.org/projects/six/badge/?version=latest - :target: https://six.readthedocs.io/ - :alt: six's documentation on Read the Docs - -.. image:: https://img.shields.io/badge/license-MIT-green.svg - :target: https://github.com/benjaminp/six/blob/master/LICENSE - :alt: MIT License badge - -Six is a Python 2 and 3 compatibility library. It provides utility functions -for smoothing over the differences between the Python versions with the goal of -writing Python code that is compatible on both Python versions. See the -documentation for more information on what is provided. - -Six supports Python 2.7 and 3.3+. It is contained in only one Python -file, so it can be easily copied into your project. (The copyright and license -notice must be retained.) - -Online documentation is at https://six.readthedocs.io/. - -Bugs can be reported to https://github.com/benjaminp/six. The code can also -be found there. - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/RECORD b/plotter-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/RECORD deleted file mode 100644 index 4de46ba..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/RECORD +++ /dev/null @@ -1,8 +0,0 @@ -__pycache__/six.cpython-38.pyc,, -six-1.16.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -six-1.16.0.dist-info/LICENSE,sha256=i7hQxWWqOJ_cFvOkaWWtI9gq3_YPI5P8J2K2MYXo5sk,1066 -six-1.16.0.dist-info/METADATA,sha256=VQcGIFCAEmfZcl77E5riPCN4v2TIsc_qtacnjxKHJoI,1795 -six-1.16.0.dist-info/RECORD,, -six-1.16.0.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110 -six-1.16.0.dist-info/top_level.txt,sha256=_iVH_iYEtEXnD8nYGQYpYFUvkUW9sEO1GYbkeKSAais,4 -six.py,sha256=TOOfQi7nFGfMrIvtdr6wX4wyHH8M7aknmuLfo2cBBrM,34549 diff --git a/plotter-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/WHEEL b/plotter-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/WHEEL deleted file mode 100644 index 01b8fc7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.36.2) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/plotter-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/top_level.txt b/plotter-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/top_level.txt deleted file mode 100644 index ffe2fce..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -six diff --git a/plotter-app/venv/lib/python3.8/site-packages/six.py b/plotter-app/venv/lib/python3.8/site-packages/six.py deleted file mode 100644 index 4e15675..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/six.py +++ /dev/null @@ -1,998 +0,0 @@ -# Copyright (c) 2010-2020 Benjamin Peterson -# -# 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. - -"""Utilities for writing code that runs on Python 2 and 3""" - -from __future__ import absolute_import - -import functools -import itertools -import operator -import sys -import types - -__author__ = "Benjamin Peterson " -__version__ = "1.16.0" - - -# Useful for very coarse version differentiation. -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -PY34 = sys.version_info[0:2] >= (3, 4) - -if PY3: - string_types = str, - integer_types = int, - class_types = type, - text_type = str - binary_type = bytes - - MAXSIZE = sys.maxsize -else: - string_types = basestring, - integer_types = (int, long) - class_types = (type, types.ClassType) - text_type = unicode - binary_type = str - - if sys.platform.startswith("java"): - # Jython always uses 32 bits. - MAXSIZE = int((1 << 31) - 1) - else: - # It's possible to have sizeof(long) != sizeof(Py_ssize_t). - class X(object): - - def __len__(self): - return 1 << 31 - try: - len(X()) - except OverflowError: - # 32-bit - MAXSIZE = int((1 << 31) - 1) - else: - # 64-bit - MAXSIZE = int((1 << 63) - 1) - del X - -if PY34: - from importlib.util import spec_from_loader -else: - spec_from_loader = None - - -def _add_doc(func, doc): - """Add documentation to a function.""" - func.__doc__ = doc - - -def _import_module(name): - """Import module, returning the module after the last dot.""" - __import__(name) - return sys.modules[name] - - -class _LazyDescr(object): - - def __init__(self, name): - self.name = name - - def __get__(self, obj, tp): - result = self._resolve() - setattr(obj, self.name, result) # Invokes __set__. - try: - # This is a bit ugly, but it avoids running this again by - # removing this descriptor. - delattr(obj.__class__, self.name) - except AttributeError: - pass - return result - - -class MovedModule(_LazyDescr): - - def __init__(self, name, old, new=None): - super(MovedModule, self).__init__(name) - if PY3: - if new is None: - new = name - self.mod = new - else: - self.mod = old - - def _resolve(self): - return _import_module(self.mod) - - def __getattr__(self, attr): - _module = self._resolve() - value = getattr(_module, attr) - setattr(self, attr, value) - return value - - -class _LazyModule(types.ModuleType): - - def __init__(self, name): - super(_LazyModule, self).__init__(name) - self.__doc__ = self.__class__.__doc__ - - def __dir__(self): - attrs = ["__doc__", "__name__"] - attrs += [attr.name for attr in self._moved_attributes] - return attrs - - # Subclasses should override this - _moved_attributes = [] - - -class MovedAttribute(_LazyDescr): - - def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): - super(MovedAttribute, self).__init__(name) - if PY3: - if new_mod is None: - new_mod = name - self.mod = new_mod - if new_attr is None: - if old_attr is None: - new_attr = name - else: - new_attr = old_attr - self.attr = new_attr - else: - self.mod = old_mod - if old_attr is None: - old_attr = name - self.attr = old_attr - - def _resolve(self): - module = _import_module(self.mod) - return getattr(module, self.attr) - - -class _SixMetaPathImporter(object): - - """ - A meta path importer to import six.moves and its submodules. - - This class implements a PEP302 finder and loader. It should be compatible - with Python 2.5 and all existing versions of Python3 - """ - - def __init__(self, six_module_name): - self.name = six_module_name - self.known_modules = {} - - def _add_module(self, mod, *fullnames): - for fullname in fullnames: - self.known_modules[self.name + "." + fullname] = mod - - def _get_module(self, fullname): - return self.known_modules[self.name + "." + fullname] - - def find_module(self, fullname, path=None): - if fullname in self.known_modules: - return self - return None - - def find_spec(self, fullname, path, target=None): - if fullname in self.known_modules: - return spec_from_loader(fullname, self) - return None - - def __get_module(self, fullname): - try: - return self.known_modules[fullname] - except KeyError: - raise ImportError("This loader does not know module " + fullname) - - def load_module(self, fullname): - try: - # in case of a reload - return sys.modules[fullname] - except KeyError: - pass - mod = self.__get_module(fullname) - if isinstance(mod, MovedModule): - mod = mod._resolve() - else: - mod.__loader__ = self - sys.modules[fullname] = mod - return mod - - def is_package(self, fullname): - """ - Return true, if the named module is a package. - - We need this method to get correct spec objects with - Python 3.4 (see PEP451) - """ - return hasattr(self.__get_module(fullname), "__path__") - - def get_code(self, fullname): - """Return None - - Required, if is_package is implemented""" - self.__get_module(fullname) # eventually raises ImportError - return None - get_source = get_code # same as get_code - - def create_module(self, spec): - return self.load_module(spec.name) - - def exec_module(self, module): - pass - -_importer = _SixMetaPathImporter(__name__) - - -class _MovedItems(_LazyModule): - - """Lazy loading of moved objects""" - __path__ = [] # mark as package - - -_moved_attributes = [ - MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), - MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), - MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), - MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), - MovedAttribute("intern", "__builtin__", "sys"), - MovedAttribute("map", "itertools", "builtins", "imap", "map"), - MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), - MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), - MovedAttribute("getoutput", "commands", "subprocess"), - MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), - MovedAttribute("reduce", "__builtin__", "functools"), - MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), - MovedAttribute("StringIO", "StringIO", "io"), - MovedAttribute("UserDict", "UserDict", "collections"), - MovedAttribute("UserList", "UserList", "collections"), - MovedAttribute("UserString", "UserString", "collections"), - MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), - MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - MovedModule("builtins", "__builtin__"), - MovedModule("configparser", "ConfigParser"), - MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"), - MovedModule("copyreg", "copy_reg"), - MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"), - MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), - MovedModule("http_cookies", "Cookie", "http.cookies"), - MovedModule("html_entities", "htmlentitydefs", "html.entities"), - MovedModule("html_parser", "HTMLParser", "html.parser"), - MovedModule("http_client", "httplib", "http.client"), - MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), - MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), - MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), - MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), - MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), - MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), - MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), - MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), - MovedModule("cPickle", "cPickle", "pickle"), - MovedModule("queue", "Queue"), - MovedModule("reprlib", "repr"), - MovedModule("socketserver", "SocketServer"), - MovedModule("_thread", "thread", "_thread"), - MovedModule("tkinter", "Tkinter"), - MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), - MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), - MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), - MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), - MovedModule("tkinter_tix", "Tix", "tkinter.tix"), - MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), - MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), - MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), - MovedModule("tkinter_colorchooser", "tkColorChooser", - "tkinter.colorchooser"), - MovedModule("tkinter_commondialog", "tkCommonDialog", - "tkinter.commondialog"), - MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), - MovedModule("tkinter_font", "tkFont", "tkinter.font"), - MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), - MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", - "tkinter.simpledialog"), - MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), - MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), - MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), - MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), - MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), - MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), -] -# Add windows specific modules. -if sys.platform == "win32": - _moved_attributes += [ - MovedModule("winreg", "_winreg"), - ] - -for attr in _moved_attributes: - setattr(_MovedItems, attr.name, attr) - if isinstance(attr, MovedModule): - _importer._add_module(attr, "moves." + attr.name) -del attr - -_MovedItems._moved_attributes = _moved_attributes - -moves = _MovedItems(__name__ + ".moves") -_importer._add_module(moves, "moves") - - -class Module_six_moves_urllib_parse(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_parse""" - - -_urllib_parse_moved_attributes = [ - MovedAttribute("ParseResult", "urlparse", "urllib.parse"), - MovedAttribute("SplitResult", "urlparse", "urllib.parse"), - MovedAttribute("parse_qs", "urlparse", "urllib.parse"), - MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), - MovedAttribute("urldefrag", "urlparse", "urllib.parse"), - MovedAttribute("urljoin", "urlparse", "urllib.parse"), - MovedAttribute("urlparse", "urlparse", "urllib.parse"), - MovedAttribute("urlsplit", "urlparse", "urllib.parse"), - MovedAttribute("urlunparse", "urlparse", "urllib.parse"), - MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), - MovedAttribute("quote", "urllib", "urllib.parse"), - MovedAttribute("quote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote", "urllib", "urllib.parse"), - MovedAttribute("unquote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), - MovedAttribute("urlencode", "urllib", "urllib.parse"), - MovedAttribute("splitquery", "urllib", "urllib.parse"), - MovedAttribute("splittag", "urllib", "urllib.parse"), - MovedAttribute("splituser", "urllib", "urllib.parse"), - MovedAttribute("splitvalue", "urllib", "urllib.parse"), - MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), - MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), - MovedAttribute("uses_params", "urlparse", "urllib.parse"), - MovedAttribute("uses_query", "urlparse", "urllib.parse"), - MovedAttribute("uses_relative", "urlparse", "urllib.parse"), -] -for attr in _urllib_parse_moved_attributes: - setattr(Module_six_moves_urllib_parse, attr.name, attr) -del attr - -Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes - -_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), - "moves.urllib_parse", "moves.urllib.parse") - - -class Module_six_moves_urllib_error(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_error""" - - -_urllib_error_moved_attributes = [ - MovedAttribute("URLError", "urllib2", "urllib.error"), - MovedAttribute("HTTPError", "urllib2", "urllib.error"), - MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), -] -for attr in _urllib_error_moved_attributes: - setattr(Module_six_moves_urllib_error, attr.name, attr) -del attr - -Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes - -_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), - "moves.urllib_error", "moves.urllib.error") - - -class Module_six_moves_urllib_request(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_request""" - - -_urllib_request_moved_attributes = [ - MovedAttribute("urlopen", "urllib2", "urllib.request"), - MovedAttribute("install_opener", "urllib2", "urllib.request"), - MovedAttribute("build_opener", "urllib2", "urllib.request"), - MovedAttribute("pathname2url", "urllib", "urllib.request"), - MovedAttribute("url2pathname", "urllib", "urllib.request"), - MovedAttribute("getproxies", "urllib", "urllib.request"), - MovedAttribute("Request", "urllib2", "urllib.request"), - MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), - MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), - MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), - MovedAttribute("BaseHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), - MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), - MovedAttribute("FileHandler", "urllib2", "urllib.request"), - MovedAttribute("FTPHandler", "urllib2", "urllib.request"), - MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), - MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), - MovedAttribute("urlretrieve", "urllib", "urllib.request"), - MovedAttribute("urlcleanup", "urllib", "urllib.request"), - MovedAttribute("URLopener", "urllib", "urllib.request"), - MovedAttribute("FancyURLopener", "urllib", "urllib.request"), - MovedAttribute("proxy_bypass", "urllib", "urllib.request"), - MovedAttribute("parse_http_list", "urllib2", "urllib.request"), - MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"), -] -for attr in _urllib_request_moved_attributes: - setattr(Module_six_moves_urllib_request, attr.name, attr) -del attr - -Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes - -_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), - "moves.urllib_request", "moves.urllib.request") - - -class Module_six_moves_urllib_response(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_response""" - - -_urllib_response_moved_attributes = [ - MovedAttribute("addbase", "urllib", "urllib.response"), - MovedAttribute("addclosehook", "urllib", "urllib.response"), - MovedAttribute("addinfo", "urllib", "urllib.response"), - MovedAttribute("addinfourl", "urllib", "urllib.response"), -] -for attr in _urllib_response_moved_attributes: - setattr(Module_six_moves_urllib_response, attr.name, attr) -del attr - -Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes - -_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), - "moves.urllib_response", "moves.urllib.response") - - -class Module_six_moves_urllib_robotparser(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_robotparser""" - - -_urllib_robotparser_moved_attributes = [ - MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), -] -for attr in _urllib_robotparser_moved_attributes: - setattr(Module_six_moves_urllib_robotparser, attr.name, attr) -del attr - -Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes - -_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), - "moves.urllib_robotparser", "moves.urllib.robotparser") - - -class Module_six_moves_urllib(types.ModuleType): - - """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - __path__ = [] # mark as package - parse = _importer._get_module("moves.urllib_parse") - error = _importer._get_module("moves.urllib_error") - request = _importer._get_module("moves.urllib_request") - response = _importer._get_module("moves.urllib_response") - robotparser = _importer._get_module("moves.urllib_robotparser") - - def __dir__(self): - return ['parse', 'error', 'request', 'response', 'robotparser'] - -_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), - "moves.urllib") - - -def add_move(move): - """Add an item to six.moves.""" - setattr(_MovedItems, move.name, move) - - -def remove_move(name): - """Remove item from six.moves.""" - try: - delattr(_MovedItems, name) - except AttributeError: - try: - del moves.__dict__[name] - except KeyError: - raise AttributeError("no such move, %r" % (name,)) - - -if PY3: - _meth_func = "__func__" - _meth_self = "__self__" - - _func_closure = "__closure__" - _func_code = "__code__" - _func_defaults = "__defaults__" - _func_globals = "__globals__" -else: - _meth_func = "im_func" - _meth_self = "im_self" - - _func_closure = "func_closure" - _func_code = "func_code" - _func_defaults = "func_defaults" - _func_globals = "func_globals" - - -try: - advance_iterator = next -except NameError: - def advance_iterator(it): - return it.next() -next = advance_iterator - - -try: - callable = callable -except NameError: - def callable(obj): - return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - - -if PY3: - def get_unbound_function(unbound): - return unbound - - create_bound_method = types.MethodType - - def create_unbound_method(func, cls): - return func - - Iterator = object -else: - def get_unbound_function(unbound): - return unbound.im_func - - def create_bound_method(func, obj): - return types.MethodType(func, obj, obj.__class__) - - def create_unbound_method(func, cls): - return types.MethodType(func, None, cls) - - class Iterator(object): - - def next(self): - return type(self).__next__(self) - - callable = callable -_add_doc(get_unbound_function, - """Get the function out of a possibly unbound function""") - - -get_method_function = operator.attrgetter(_meth_func) -get_method_self = operator.attrgetter(_meth_self) -get_function_closure = operator.attrgetter(_func_closure) -get_function_code = operator.attrgetter(_func_code) -get_function_defaults = operator.attrgetter(_func_defaults) -get_function_globals = operator.attrgetter(_func_globals) - - -if PY3: - def iterkeys(d, **kw): - return iter(d.keys(**kw)) - - def itervalues(d, **kw): - return iter(d.values(**kw)) - - def iteritems(d, **kw): - return iter(d.items(**kw)) - - def iterlists(d, **kw): - return iter(d.lists(**kw)) - - viewkeys = operator.methodcaller("keys") - - viewvalues = operator.methodcaller("values") - - viewitems = operator.methodcaller("items") -else: - def iterkeys(d, **kw): - return d.iterkeys(**kw) - - def itervalues(d, **kw): - return d.itervalues(**kw) - - def iteritems(d, **kw): - return d.iteritems(**kw) - - def iterlists(d, **kw): - return d.iterlists(**kw) - - viewkeys = operator.methodcaller("viewkeys") - - viewvalues = operator.methodcaller("viewvalues") - - viewitems = operator.methodcaller("viewitems") - -_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") -_add_doc(itervalues, "Return an iterator over the values of a dictionary.") -_add_doc(iteritems, - "Return an iterator over the (key, value) pairs of a dictionary.") -_add_doc(iterlists, - "Return an iterator over the (key, [values]) pairs of a dictionary.") - - -if PY3: - def b(s): - return s.encode("latin-1") - - def u(s): - return s - unichr = chr - import struct - int2byte = struct.Struct(">B").pack - del struct - byte2int = operator.itemgetter(0) - indexbytes = operator.getitem - iterbytes = iter - import io - StringIO = io.StringIO - BytesIO = io.BytesIO - del io - _assertCountEqual = "assertCountEqual" - if sys.version_info[1] <= 1: - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - _assertNotRegex = "assertNotRegexpMatches" - else: - _assertRaisesRegex = "assertRaisesRegex" - _assertRegex = "assertRegex" - _assertNotRegex = "assertNotRegex" -else: - def b(s): - return s - # Workaround for standalone backslash - - def u(s): - return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") - unichr = unichr - int2byte = chr - - def byte2int(bs): - return ord(bs[0]) - - def indexbytes(buf, i): - return ord(buf[i]) - iterbytes = functools.partial(itertools.imap, ord) - import StringIO - StringIO = BytesIO = StringIO.StringIO - _assertCountEqual = "assertItemsEqual" - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - _assertNotRegex = "assertNotRegexpMatches" -_add_doc(b, """Byte literal""") -_add_doc(u, """Text literal""") - - -def assertCountEqual(self, *args, **kwargs): - return getattr(self, _assertCountEqual)(*args, **kwargs) - - -def assertRaisesRegex(self, *args, **kwargs): - return getattr(self, _assertRaisesRegex)(*args, **kwargs) - - -def assertRegex(self, *args, **kwargs): - return getattr(self, _assertRegex)(*args, **kwargs) - - -def assertNotRegex(self, *args, **kwargs): - return getattr(self, _assertNotRegex)(*args, **kwargs) - - -if PY3: - exec_ = getattr(moves.builtins, "exec") - - def reraise(tp, value, tb=None): - try: - if value is None: - value = tp() - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - finally: - value = None - tb = None - -else: - def exec_(_code_, _globs_=None, _locs_=None): - """Execute code in a namespace.""" - if _globs_ is None: - frame = sys._getframe(1) - _globs_ = frame.f_globals - if _locs_ is None: - _locs_ = frame.f_locals - del frame - elif _locs_ is None: - _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") - - exec_("""def reraise(tp, value, tb=None): - try: - raise tp, value, tb - finally: - tb = None -""") - - -if sys.version_info[:2] > (3,): - exec_("""def raise_from(value, from_value): - try: - raise value from from_value - finally: - value = None -""") -else: - def raise_from(value, from_value): - raise value - - -print_ = getattr(moves.builtins, "print", None) -if print_ is None: - def print_(*args, **kwargs): - """The new-style print function for Python 2.4 and 2.5.""" - fp = kwargs.pop("file", sys.stdout) - if fp is None: - return - - def write(data): - if not isinstance(data, basestring): - data = str(data) - # If the file has an encoding, encode unicode with it. - if (isinstance(fp, file) and - isinstance(data, unicode) and - fp.encoding is not None): - errors = getattr(fp, "errors", None) - if errors is None: - errors = "strict" - data = data.encode(fp.encoding, errors) - fp.write(data) - want_unicode = False - sep = kwargs.pop("sep", None) - if sep is not None: - if isinstance(sep, unicode): - want_unicode = True - elif not isinstance(sep, str): - raise TypeError("sep must be None or a string") - end = kwargs.pop("end", None) - if end is not None: - if isinstance(end, unicode): - want_unicode = True - elif not isinstance(end, str): - raise TypeError("end must be None or a string") - if kwargs: - raise TypeError("invalid keyword arguments to print()") - if not want_unicode: - for arg in args: - if isinstance(arg, unicode): - want_unicode = True - break - if want_unicode: - newline = unicode("\n") - space = unicode(" ") - else: - newline = "\n" - space = " " - if sep is None: - sep = space - if end is None: - end = newline - for i, arg in enumerate(args): - if i: - write(sep) - write(arg) - write(end) -if sys.version_info[:2] < (3, 3): - _print = print_ - - def print_(*args, **kwargs): - fp = kwargs.get("file", sys.stdout) - flush = kwargs.pop("flush", False) - _print(*args, **kwargs) - if flush and fp is not None: - fp.flush() - -_add_doc(reraise, """Reraise an exception.""") - -if sys.version_info[0:2] < (3, 4): - # This does exactly the same what the :func:`py3:functools.update_wrapper` - # function does on Python versions after 3.2. It sets the ``__wrapped__`` - # attribute on ``wrapper`` object and it doesn't raise an error if any of - # the attributes mentioned in ``assigned`` and ``updated`` are missing on - # ``wrapped`` object. - def _update_wrapper(wrapper, wrapped, - assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - for attr in assigned: - try: - value = getattr(wrapped, attr) - except AttributeError: - continue - else: - setattr(wrapper, attr, value) - for attr in updated: - getattr(wrapper, attr).update(getattr(wrapped, attr, {})) - wrapper.__wrapped__ = wrapped - return wrapper - _update_wrapper.__doc__ = functools.update_wrapper.__doc__ - - def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - return functools.partial(_update_wrapper, wrapped=wrapped, - assigned=assigned, updated=updated) - wraps.__doc__ = functools.wraps.__doc__ - -else: - wraps = functools.wraps - - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(type): - - def __new__(cls, name, this_bases, d): - if sys.version_info[:2] >= (3, 7): - # This version introduced PEP 560 that requires a bit - # of extra care (we mimic what is done by __build_class__). - resolved_bases = types.resolve_bases(bases) - if resolved_bases is not bases: - d['__orig_bases__'] = bases - else: - resolved_bases = bases - return meta(name, resolved_bases, d) - - @classmethod - def __prepare__(cls, name, this_bases): - return meta.__prepare__(name, bases) - return type.__new__(metaclass, 'temporary_class', (), {}) - - -def add_metaclass(metaclass): - """Class decorator for creating a class with a metaclass.""" - def wrapper(cls): - orig_vars = cls.__dict__.copy() - slots = orig_vars.get('__slots__') - if slots is not None: - if isinstance(slots, str): - slots = [slots] - for slots_var in slots: - orig_vars.pop(slots_var) - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) - if hasattr(cls, '__qualname__'): - orig_vars['__qualname__'] = cls.__qualname__ - return metaclass(cls.__name__, cls.__bases__, orig_vars) - return wrapper - - -def ensure_binary(s, encoding='utf-8', errors='strict'): - """Coerce **s** to six.binary_type. - - For Python 2: - - `unicode` -> encoded to `str` - - `str` -> `str` - - For Python 3: - - `str` -> encoded to `bytes` - - `bytes` -> `bytes` - """ - if isinstance(s, binary_type): - return s - if isinstance(s, text_type): - return s.encode(encoding, errors) - raise TypeError("not expecting type '%s'" % type(s)) - - -def ensure_str(s, encoding='utf-8', errors='strict'): - """Coerce *s* to `str`. - - For Python 2: - - `unicode` -> encoded to `str` - - `str` -> `str` - - For Python 3: - - `str` -> `str` - - `bytes` -> decoded to `str` - """ - # Optimization: Fast return for the common case. - if type(s) is str: - return s - if PY2 and isinstance(s, text_type): - return s.encode(encoding, errors) - elif PY3 and isinstance(s, binary_type): - return s.decode(encoding, errors) - elif not isinstance(s, (text_type, binary_type)): - raise TypeError("not expecting type '%s'" % type(s)) - return s - - -def ensure_text(s, encoding='utf-8', errors='strict'): - """Coerce *s* to six.text_type. - - For Python 2: - - `unicode` -> `unicode` - - `str` -> `unicode` - - For Python 3: - - `str` -> `str` - - `bytes` -> decoded to `str` - """ - if isinstance(s, binary_type): - return s.decode(encoding, errors) - elif isinstance(s, text_type): - return s - else: - raise TypeError("not expecting type '%s'" % type(s)) - - -def python_2_unicode_compatible(klass): - """ - A class decorator that defines __unicode__ and __str__ methods under Python 2. - Under Python 3 it does nothing. - - To support Python 2 and 3 with a single code base, define a __str__ method - returning text and apply this decorator to the class. - """ - if PY2: - if '__str__' not in klass.__dict__: - raise ValueError("@python_2_unicode_compatible cannot be applied " - "to %s because it doesn't define __str__()." % - klass.__name__) - klass.__unicode__ = klass.__str__ - klass.__str__ = lambda self: self.__unicode__().encode('utf-8') - return klass - - -# Complete the moves implementation. -# This code is at the end of this module to speed up module loading. -# Turn this module into a package. -__path__ = [] # required for PEP 302 and PEP 451 -__package__ = __name__ # see PEP 366 @ReservedAssignment -if globals().get("__spec__") is not None: - __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable -# Remove other six meta path importers, since they cause problems. This can -# happen if six is removed from sys.modules and then reloaded. (Setuptools does -# this for some reason.) -if sys.meta_path: - for i, importer in enumerate(sys.meta_path): - # Here's some real nastiness: Another "instance" of the six module might - # be floating around. Therefore, we can't use isinstance() to check for - # the six meta path importer, since the other six instance will have - # inserted an importer with different class. - if (type(importer).__name__ == "_SixMetaPathImporter" and - importer.name == __name__): - del sys.meta_path[i] - break - del i, importer -# Finally, add the importer to the meta path import hook. -sys.meta_path.append(_importer) diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode-1.5.4.dist-info/INSTALLER b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode-1.5.4.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode-1.5.4.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode-1.5.4.dist-info/LICENSE b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode-1.5.4.dist-info/LICENSE deleted file mode 100644 index 871ce8e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode-1.5.4.dist-info/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - 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 -. \ No newline at end of file diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode-1.5.4.dist-info/METADATA b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode-1.5.4.dist-info/METADATA deleted file mode 100644 index 1b539a5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode-1.5.4.dist-info/METADATA +++ /dev/null @@ -1,140 +0,0 @@ -Metadata-Version: 2.1 -Name: svg-to-gcode -Version: 1.5.4 -Summary: The definitive NPM module to construct gcode from svg files. -Home-page: https://github.com/PadLex/SvgToGcode -Author: Padlex -Author-email: -License: UNKNOWN -Platform: UNKNOWN -Classifier: Programming Language :: Python :: 3 -Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3) -Classifier: Operating System :: OS Independent -Requires-Python: >=3.6 -Description-Content-Type: text/markdown - - -# Svg to Gcode - Flamma project -### The definitive NPM module to construct gcode from svg files. -Don't feel like coding? Use the [Inkscape extension](https://github.com/JTechPhotonics/J-Tech-Photonics-Laser-Tool). - -This library's intended purpose is to laser-cut svg images. However, it is structured such that it can be easily -expanded to parse other image formats or compile to different numerical control languages. - -* [Installation](#Installation) -* [Documentation](#Documentation) - * [Basic Usage](#Basic-Usage) - * [Custom interfaces](#Custom-interfaces) - * [Insert or Modify Geometry](#Insert-or-Modify-Geometry) - * [Approximation tolerance](#Approximation-tolerance) - * [Support for additional formats](#Support-for-additional-formats) -* [Contribution guidelines](CONTRIBUTING.md) - - -## Installation -Svg to Gcode is available on pip. To install it, execute: -> pip install svg-to-gcode - -Of course, you could also just download the sourcecode. - -## Documentation -The module is divided in three sub-modules: -* svg_to_gcode.**geometry** offers a general representation of geometric curves. -* svg_to_gcode.**parser** parses svg files, converting them to geometric curves. -* svg_to_gcode.**compiler** transforms geometric curves into gcode. - -### Basic Usage -If all you need is to compile an svg image to gcode, for a standard cnc machine, this is all the code you need. Just -remember to select your own cutting and movement speeds. - -```python -from svg_to_gcode.svg_parser import parse_file -from svg_to_gcode.compiler import Compiler, interfaces - -# Instantiate a compiler, specifying the interface type and the speed at which the tool should move. pass_depth controls -# how far down the tool moves after every pass. Set it to 0 if your machine does not support Z axis movement. -gcode_compiler = Compiler(interfaces.Gcode, movement_speed=1000, cutting_speed=300, pass_depth=5) - -curves = parse_file("drawing.svg") # Parse an svg file into geometric curves - -gcode_compiler.append_curves(curves) -gcode_compiler.compile_to_file("drawing.gcode", passes=2) -``` - -### Custom interfaces -Interfaces exist to abstract commands used by the compiler. In this way, you can compile for a non-standard printer or -to a completely new numerical control language without modifying the compiler. You can easily write custom interfaces to - perform additional operations (like powering a fan) or to modify the gcode commands used to perform existing operations - (some DIY laser cutters, for example, control the laser diode from the fan output). - -The code bellow implements a custom interface which powers on a fan every time the laser is powered on. -```python -from svg_to_gcode.svg_parser import parse_file -from svg_to_gcode.compiler import Compiler, interfaces -from svg_to_gcode.formulas import linear_map - -class CustomInterface(interfaces.Gcode): - def __init__(self): - super().__init__() - self.fan_speed = 1 - - # Override the laser_off method such that it also powers off the fan. - def laser_off(self): - return "M107;\n" + "M5;" # Turn off the fan + turn off the laser - - # Override the set_laser_power method - def set_laser_power(self, power): - if power < 0 or power > 1: - raise ValueError(f"{power} is out of bounds. Laser power must be given between 0 and 1. " - f"The interface will scale it correctly.") - - return f"M106 S255\n" + f"M3 S{linear_map(0, 255, power)};" # Turn on the fan + change laser power - -# Instantiate a compiler, specifying the custom interface and the speed at which the tool should move. -gcode_compiler = Compiler(CustomInterface, movement_speed=1000, cutting_speed=300, pass_depth=5) - -curves = parse_file("drawing.svg") # Parse an svg file into geometric curves - -gcode_compiler.append_curves(curves) -gcode_compiler.compile_to_file("drawing.gcode") -``` - -### Insert or Modify Geometry - -Before compiling, you could append or modify geometric curves. I'm not sure why you would want to, but you can. -The code below draws a fractal and compiles it to gcode. - -```python -from svg_to_gcode.svg_parser import parse_file -from svg_to_gcode.compiler import Compiler, interfaces -from svg_to_gcode.formulas import linear_map - -# Instantiate a compiler, specifying the custom interface and the speed at which the tool should move. -gcode_compiler = Compiler(interfaces.Gcode, movement_speed=1000, cutting_speed=300, pass_depth=5) - -curves = parse_file("drawing.svg") # Parse an svg file into geometric curves - -gcode_compiler.append_curves(curves) -gcode_compiler.compile_to_file("drawing.gcode") -``` - -### Approximation tolerance -Gcode only supports liner and circular arcs. Currently I've only implemented a line segment approximation. As such, -geometric curves are compiled to a chain of line-segments. The exact length of the segments is adjusted dynamically such -that it never diverges from the original curve by more then the value of the TOLERANCES['approximation'] key. - -The default value is 0.1. Smaller values improve accuracy, larger ones result in shorter gcode files. - -```python -from svg_to_gcode import TOLERANCES - -TOLERANCES['approximation'] = 0.01 -``` - - -### Support for additional formats -For now, this library only converts svgs to gcode files. However, its modular design makes it simple to -support other formats. If you're looking to support a specific format, pull requests are always welcome. Just make sure -to read [CONTRIBUTING.md](CONTRIBUTING.md) to get a feeling for the internal structure and best practices. - - diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode-1.5.4.dist-info/RECORD b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode-1.5.4.dist-info/RECORD deleted file mode 100644 index 41b78da..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode-1.5.4.dist-info/RECORD +++ /dev/null @@ -1,58 +0,0 @@ -svg_to_gcode-1.5.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -svg_to_gcode-1.5.4.dist-info/LICENSE,sha256=gcuuhKKc5-dwvyvHsXjlC9oM6N5gZ6umYbC8ewW1Yvg,35821 -svg_to_gcode-1.5.4.dist-info/METADATA,sha256=E0cVIgmZlRkYwE1sAydgP0SH7_MRdxPL6EJVDSH3u8g,6025 -svg_to_gcode-1.5.4.dist-info/RECORD,, -svg_to_gcode-1.5.4.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92 -svg_to_gcode-1.5.4.dist-info/top_level.txt,sha256=p5exl6OoSTU-ghCGjzStRoVfL-id71_GF0RbaIWP3PA,13 -svg_to_gcode/__init__.py,sha256=5Ubk06hdshM-9tX_OdwpsCZxWJ4Xb_d-6WMH3btU7Is,104 -svg_to_gcode/__pycache__/__init__.cpython-38.pyc,, -svg_to_gcode/__pycache__/formulas.cpython-38.pyc,, -svg_to_gcode/compiler/__init__.py,sha256=Kc_Ujh0LNjLWeaaorzSEHXJNnAHRUyt8gHYx_tp2MM0,138 -svg_to_gcode/compiler/__pycache__/__init__.cpython-38.pyc,, -svg_to_gcode/compiler/__pycache__/_compiler.cpython-38.pyc,, -svg_to_gcode/compiler/_compiler.py,sha256=x24qCJgYe6tyorU3hmUoPQ2qgS1SqPN1Xx7DxFexRcs,5802 -svg_to_gcode/compiler/interfaces/__init__.py,sha256=pZwYA5OWeCTnEj15hHkMh_0bMwNLgixjTuVfe-OhiE8,222 -svg_to_gcode/compiler/interfaces/__pycache__/__init__.cpython-38.pyc,, -svg_to_gcode/compiler/interfaces/__pycache__/_abstract_interface.cpython-38.pyc,, -svg_to_gcode/compiler/interfaces/__pycache__/_fan_controlled_gcode.cpython-38.pyc,, -svg_to_gcode/compiler/interfaces/__pycache__/_gcode.cpython-38.pyc,, -svg_to_gcode/compiler/interfaces/_abstract_interface.py,sha256=-g3AGYF223DALpG3SgpyV8EvAIAnxYGlG0_kBhiaDV8,4158 -svg_to_gcode/compiler/interfaces/_fan_controlled_gcode.py,sha256=uVJhzDodT8xQR4c9IfcBZXHxGswDeMjH7gfAmNGT5lU,665 -svg_to_gcode/compiler/interfaces/_gcode.py,sha256=q1opm3VqYicuXtnYxXP-2gtJgyQDCMasjB2S7UqpPL0,2898 -svg_to_gcode/formulas.py,sha256=dz0gNUQjTarykzcEJi3LCUpABFRuc6l0rRNZ00UGZoY,5148 -svg_to_gcode/geometry/__init__.py,sha256=s_Hy7hlMM2n-9x-hCf1f_JMUABlKzpiy-LyCBXYPcck,1200 -svg_to_gcode/geometry/__pycache__/__init__.cpython-38.pyc,, -svg_to_gcode/geometry/__pycache__/_abstract_chain.cpython-38.pyc,, -svg_to_gcode/geometry/__pycache__/_abstract_curve.cpython-38.pyc,, -svg_to_gcode/geometry/__pycache__/_circular_arc.cpython-38.pyc,, -svg_to_gcode/geometry/__pycache__/_cubic_bazier.cpython-38.pyc,, -svg_to_gcode/geometry/__pycache__/_elliptical_arc.cpython-38.pyc,, -svg_to_gcode/geometry/__pycache__/_line.cpython-38.pyc,, -svg_to_gcode/geometry/__pycache__/_line_segment_chain.cpython-38.pyc,, -svg_to_gcode/geometry/__pycache__/_matrix.cpython-38.pyc,, -svg_to_gcode/geometry/__pycache__/_quadratic_bazier.cpython-38.pyc,, -svg_to_gcode/geometry/__pycache__/_smooth_arc_chain.cpython-38.pyc,, -svg_to_gcode/geometry/__pycache__/_vector.cpython-38.pyc,, -svg_to_gcode/geometry/_abstract_chain.py,sha256=tTfv4jAi9cFd8zTfutOqW7WDpp9zUpuMNPvjZOnp4YA,3398 -svg_to_gcode/geometry/_abstract_curve.py,sha256=htPybGLOfIKMl29VOWM2Xno36ZDtfgRWNy8ddefZNXU,3171 -svg_to_gcode/geometry/_circular_arc.py,sha256=rPfPpoUMw-xHsuEfVOepnJYkY_ADOMZjrolDUAWcYnM,3098 -svg_to_gcode/geometry/_cubic_bazier.py,sha256=H7vnEeyzjH8eqVOrFwmIJqLOiwOZwgsB6YlPdoW_HHo,1086 -svg_to_gcode/geometry/_elliptical_arc.py,sha256=a-Kb0Vt1TAoQ9vZXfU0_HlhoCQdJ9WbhN5SCS1-sH_I,2340 -svg_to_gcode/geometry/_line.py,sha256=K1iMVnq_H47jw2kF54JHb7uxrevvvcbsGNerTWq4Maw,920 -svg_to_gcode/geometry/_line_segment_chain.py,sha256=J4WXkKhWK2tB8c-DjE5DGPRhNGArRAa76VJXK5zixP8,3330 -svg_to_gcode/geometry/_matrix.py,sha256=q8g0kSnzO9mLZJxr1xLPqx755s1ILyYVrm9dGXhIMuc,3243 -svg_to_gcode/geometry/_quadratic_bazier.py,sha256=XrO0Qni8s1YTi6sU3M4xrkqHU2VNuT-Z9M7EtH0Vle8,972 -svg_to_gcode/geometry/_smooth_arc_chain.py,sha256=tR1bmma05SnY13QOf-j-UN94fbFpIKblUy4A3AUyRhI,2524 -svg_to_gcode/geometry/_vector.py,sha256=hz9-amWYMltBD_aKIAogW2m6ixDboI9d6349vRB7vto,1550 -svg_to_gcode/svg_parser/__init__.py,sha256=adRMUB648xTtqpOzY37GPz_NKKX114yVzWwJl8pU-4g,582 -svg_to_gcode/svg_parser/__pycache__/__init__.cpython-38.pyc,, -svg_to_gcode/svg_parser/__pycache__/_helper_functions.cpython-38.pyc,, -svg_to_gcode/svg_parser/__pycache__/_parser_methods.cpython-38.pyc,, -svg_to_gcode/svg_parser/__pycache__/_path.cpython-38.pyc,, -svg_to_gcode/svg_parser/__pycache__/_transformation.cpython-38.pyc,, -svg_to_gcode/svg_parser/__pycache__/debug_methods.cpython-38.pyc,, -svg_to_gcode/svg_parser/_helper_functions.py,sha256=V4cPkF7p9WA34P2kBgjWFWYBQa-zNF9_RS2g63elkpI,5579 -svg_to_gcode/svg_parser/_parser_methods.py,sha256=cRCvhEhlyIU42qeYFYtWS2mjVQOhXyrAU_FAeIoMhLc,5859 -svg_to_gcode/svg_parser/_path.py,sha256=0JVqWcAxDu2zlUJ2noylYf9qrKCGDlCV5hI8l_j5bDY,14183 -svg_to_gcode/svg_parser/_transformation.py,sha256=MlsJbU7CCPSj3OneOq23Md5wOpP_CEsF1zJZ5jVWAHQ,5540 -svg_to_gcode/svg_parser/debug_methods.py,sha256=awLrHQZWBVnIZI2YB-sPk2P_RdnhU7wjsE79vsjFMJ0,2501 diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode-1.5.4.dist-info/WHEEL b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode-1.5.4.dist-info/WHEEL deleted file mode 100644 index 385faab..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode-1.5.4.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.36.2) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode-1.5.4.dist-info/top_level.txt b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode-1.5.4.dist-info/top_level.txt deleted file mode 100644 index 8433f65..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode-1.5.4.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -svg_to_gcode diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/__init__.py deleted file mode 100644 index 473d7bb..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -TOLERANCES = {"approximation": 10 ** -2, "input": 10 ** -3, "operation": 10**-6} -UNITS = {"mm", "in"} diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 1ce4bf2..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/__pycache__/formulas.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/__pycache__/formulas.cpython-38.pyc deleted file mode 100644 index 7bdaf3c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/__pycache__/formulas.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/__init__.py deleted file mode 100644 index 5af4de7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""The compiler sub-module transforms geometric Curves into CAM machine code.""" - -from svg_to_gcode.compiler._compiler import Compiler diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 75ebc7d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/__pycache__/_compiler.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/__pycache__/_compiler.cpython-38.pyc deleted file mode 100644 index e0094c8..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/__pycache__/_compiler.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/_compiler.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/_compiler.py deleted file mode 100644 index 21f55a2..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/_compiler.py +++ /dev/null @@ -1,140 +0,0 @@ -import typing -import warnings - -from svg_to_gcode.compiler.interfaces import Interface -from svg_to_gcode.geometry import Curve, Line -from svg_to_gcode.geometry import LineSegmentChain -from svg_to_gcode import UNITS, TOLERANCES - - -class Compiler: - """ - The Compiler class handles the process of drawing geometric objects using interface commands and assembling - the resulting numerical control code. - """ - - def __init__(self, interface_class: typing.Type[Interface], movement_speed, cutting_speed, pass_depth, - dwell_time=0, unit=None, custom_header=None, custom_footer=None): - """ - - :param interface_class: Specify which interface to use. The ost common is the gcode interface. - :param movement_speed: the speed at which to move the tool when moving. (units are determined by the printer) - :param cutting_speed: the speed at which to move the tool when cutting. (units are determined by the printer) - :param pass_depth: . AKA, the depth your laser cuts in a pass. - :param dwell_time: the number of ms the tool should wait before moving to another cut. Useful for pen plotters. - :param unit: specify a unit to the machine - :param custom_header: A list of commands to be executed before all generated commands. Default is [laser_off,] - :param custom_footer: A list of commands to be executed after all generated commands. Default is [laser_off,] - """ - self.interface = interface_class() - self.movement_speed = movement_speed - self.cutting_speed = cutting_speed - self.pass_depth = abs(pass_depth) - self.dwell_time = dwell_time - - if (unit is not None) and (unit not in UNITS): - raise ValueError(f"Unknown unit {unit}. Please specify one of the following: {UNITS}") - - self.unit = unit - - if custom_header is None: - custom_header = [self.interface.laser_off()] - - if custom_footer is None: - custom_footer = [self.interface.laser_off()] - - self.header = [self.interface.set_absolute_coordinates(), - self.interface.set_movement_speed(self.movement_speed)] + custom_header - self.footer = custom_footer - self.body = [] - - def compile(self, passes=1): - - """ - Assembles the code in the header, body and footer, saving it to a file. - - - :param passes: the number of passes that should be made. Every pass the machine moves_down (z-axis) by - self.pass_depth and self.body is repeated. - :return returns the assembled code. self.header + [self.body, -self.pass_depth] * passes + self.footer - """ - - if len(self.body) == 0: - warnings.warn("Compile with an empty body (no curves). Is this intentional?") - - gcode = [] - - gcode.extend(self.header) - gcode.append(self.interface.set_unit(self.unit)) - for i in range(passes): - gcode.extend(self.body) - - if i < passes - 1: # If it isn't the last pass, turn off the laser and move down - gcode.append(self.interface.laser_off()) - - if self.pass_depth > 0: - gcode.append(self.interface.set_relative_coordinates()) - gcode.append(self.interface.linear_move(z=-self.pass_depth)) - gcode.append(self.interface.set_absolute_coordinates()) - - gcode.extend(self.footer) - - gcode = filter(lambda command: len(command) > 0, gcode) - - return '\n'.join(gcode) - - def compile_to_file(self, file_name: str, passes=1): - """ - A wrapper for the self.compile method. Assembles the code in the header, body and footer, saving it to a file. - - :param file_name: the path to save the file. - :param passes: the number of passes that should be made. Every pass the machine moves_down (z-axis) by - self.pass_depth and self.body is repeated. - """ - - with open(file_name, 'w') as file: - file.write(self.compile(passes=passes)) - - def append_line_chain(self, line_chain: LineSegmentChain): - """ - Draws a LineSegmentChain by calling interface.linear_move() for each segment. The resulting code is appended to - self.body - """ - - if line_chain.chain_size() == 0: - warnings.warn("Attempted to parse empty LineChain") - return [] - - code = [] - - start = line_chain.get(0).start - - # Don't dwell and turn off laser if the new start is at the current position - if self.interface.position is None or abs(self.interface.position - start) > TOLERANCES["operation"]: - - code = [self.interface.laser_off(), self.interface.set_movement_speed(self.movement_speed), - self.interface.linear_move(start.x, start.y), self.interface.set_movement_speed(self.cutting_speed), - self.interface.set_laser_power(1)] - - if self.dwell_time > 0: - code = [self.interface.dwell(self.dwell_time)] + code - - for line in line_chain: - code.append(self.interface.linear_move(line.end.x, line.end.y)) - - self.body.extend(code) - - def append_curves(self, curves: [typing.Type[Curve]]): - """ - Draws curves by approximating them as line segments and calling self.append_line_chain(). The resulting code is - appended to self.body - """ - - for curve in curves: - line_chain = LineSegmentChain() - - approximation = LineSegmentChain.line_segment_approximation(curve) - - line_chain.extend(approximation) - - self.append_line_chain(line_chain) diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/__init__.py deleted file mode 100644 index 75d42b1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from svg_to_gcode.compiler.interfaces._abstract_interface import Interface -from svg_to_gcode.compiler.interfaces._gcode import Gcode -from svg_to_gcode.compiler.interfaces._fan_controlled_gcode import FanControlledGcode diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 489245c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/__pycache__/_abstract_interface.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/__pycache__/_abstract_interface.cpython-38.pyc deleted file mode 100644 index 27bbdbb..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/__pycache__/_abstract_interface.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/__pycache__/_fan_controlled_gcode.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/__pycache__/_fan_controlled_gcode.cpython-38.pyc deleted file mode 100644 index c4b8956..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/__pycache__/_fan_controlled_gcode.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/__pycache__/_gcode.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/__pycache__/_gcode.cpython-38.pyc deleted file mode 100644 index b6c4d40..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/__pycache__/_gcode.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/_abstract_interface.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/_abstract_interface.py deleted file mode 100644 index 62eaf2c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/_abstract_interface.py +++ /dev/null @@ -1,106 +0,0 @@ -class Interface: - - """ - Classes which inherit from the abstract Interface class provide a consistent interface_class for the gcode parser. - - The abstract methods below are necessary for the gcode parser to function. Some child classes may choose to also - implement additional methods like specify_unit and home_axis to provide additional functionality to the parser. - - :param self.position stores the current tool position in 2d - """ - - # Todo convert to abc class - # Todo add requirement self.position - - def set_movement_speed(self, speed) -> str: - """ - Changes the speed at which the tool moves. - - :return: Appropriate command. - """ - raise NotImplementedError("Interface class must implement the set_speed command") - - def linear_move(self, x=None, y=None, z=None) -> str: - """ - Moves the tool in a straight line. - - :return: Appropriate command. - """ - raise NotImplementedError("Interface class must implement the linear_move command") - - def laser_off(self) -> str: - """ - Powers off the laser beam. - - :return: Appropriate command. - """ - raise NotImplementedError("Interface class must implement the laser_off command") - - def set_laser_power(self, power) -> str: - """ - If the target machine supports pwm, change the laser power. Regardless of pwm support, powers on the laser beam - for values of power > 0. - - :param power: Defines the power level of the laser. Valid values range between 0 and 1. - :return: Appropriate command. - """ - raise NotImplementedError("Interface class must implement the laser_power command") - - def set_absolute_coordinates(self) -> str: - """ - Make the coordinate space absolute. ie. move relative to origin not current position. - - return '' if the target of the interface only supports absolute space. If the target only supports - relative coordinate space, this command should return '' and the child class must transform all future inputs from - absolute positions to relative positions until set_relative_coordinates is called. - - :return: Appropriate command. - """ - raise NotImplementedError("Interface class must implement the set_absolute_coordinates command") - - def set_relative_coordinates(self) -> str: - """ - Make the coordinate space relative. ie. move relative to current position not origin. - - return '' if the target of the interface only supports relative space. If the target only supports - absolute coordinate space, this command should return '' and the child class must transform all future inputs from - relative positions to absolute positions until set_absolute_coordinates is called. - - :return: Appropriate command. - """ - raise NotImplementedError("Interface class must implement the set_relative_coordinates command") - - # Optional commands # - def dwell(self, milliseconds) -> str: - """ - Optional method, if implemented dwells for a determined number of milliseconds before moving to the next command. - - :return: Appropriate command. - """ - pass - - def set_origin_at_position(self) -> str: - """ - Optional method, if implemented translates coordinate space such that the current position is the new origin. - If the target of the interface does not implement this command, return '' and the child class must translate all - input positions to the new coordinate space. - - :return: Appropriate command. - """ - pass - - def set_unit(self, unit): - """ - Optional method, if implemented Specifies the unit of measurement. - - :return: Appropriate command. If not implemented return ''. - """ - pass - - def home_axes(self): - """ - Optional method, if implemented homes all axes. - - :return: Appropriate command. If not implemented return ''. - """ - pass diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/_fan_controlled_gcode.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/_fan_controlled_gcode.py deleted file mode 100644 index 1d03fe8..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/_fan_controlled_gcode.py +++ /dev/null @@ -1,21 +0,0 @@ -from svg_to_gcode.compiler.interfaces import Gcode -from svg_to_gcode import formulas - - -class FanControlledGcode(Gcode): - - def laser_off(self): - if self._current_power is None or self._current_power > 0: - self._current_power = 0 - return f"M107;" - - return '' - - def set_laser_power(self, power): - self._current_power = power - - if power < 0 or power > 1: - raise ValueError(f"{power} is out of bounds. Laser power must be given between 0 and 1. " - f"The interface will scale it correctly.") - - return f"M106 S{formulas.linear_map(0, 255, power)};" diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/_gcode.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/_gcode.py deleted file mode 100644 index 061d6d2..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/compiler/interfaces/_gcode.py +++ /dev/null @@ -1,95 +0,0 @@ -import warnings -import math - -from svg_to_gcode import formulas -from svg_to_gcode.compiler.interfaces import Interface -from svg_to_gcode.geometry import Vector -from svg_to_gcode import TOLERANCES - -verbose = False - - -class Gcode(Interface): - - def __init__(self): - self.position = None - self._next_speed = None - self._current_speed = None - - # Round outputs to the same number of significant figures as the operational tolerance. - self.precision = abs(round(math.log(TOLERANCES["operation"], 10))) - - def set_movement_speed(self, speed): - self._next_speed = speed - return '' - - def linear_move(self, x=None, y=None, z=None): - - if self._next_speed is None: - raise ValueError("Undefined movement speed. Call set_movement_speed before executing movement commands.") - - # Don't do anything if linear move was called without passing a value. - if x is None and y is None and z is None: - warnings.warn("linear_move command invoked without arguments.") - return '' - - # Todo, investigate G0 command and replace movement speeds with G1 (normal speed) and G0 (fast move) - command = "G1" - - if self._current_speed != self._next_speed: - self._current_speed = self._next_speed - command += f" F{self._current_speed}" - - # Move if not 0 and not None - command += f" X{x:.{self.precision}f}" if x is not None else '' - command += f" Y{y:.{self.precision}f}" if y is not None else '' - command += f" Z{z:.{self.precision}f}" if z is not None else '' - - if self.position is not None or (x is not None and y is not None): - if x is None: - x = self.position.x - - if y is None: - y = self.position.y - - self.position = Vector(x, y) - - if verbose: - print(f"Move to {x}, {y}, {z}") - - return command + ';' - - def laser_off(self): - return f"M5;" - - def set_laser_power(self, power): - if power < 0 or power > 1: - raise ValueError(f"{power} is out of bounds. Laser power must be given between 0 and 1. " - f"The interface will scale it correctly.") - - return f"M3 S{formulas.linear_map(0, 255, power)};" - - def set_absolute_coordinates(self): - return "G90;" - - def set_relative_coordinates(self): - return "G91;" - - def dwell(self, milliseconds): - return f"G4 P{milliseconds}" - - def set_origin_at_position(self): - self.position = Vector(0, 0) - return "G92 X0 Y0 Z0;" - - def set_unit(self, unit): - if unit == "mm": - return "G21;" - - if unit == "in": - return "G20;" - - return '' - - def home_axes(self): - return "G28;" diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/formulas.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/formulas.py deleted file mode 100644 index 9af726e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/formulas.py +++ /dev/null @@ -1,148 +0,0 @@ -""" -This script contains handy mathematical equations. -It's used to limit code repetition and abstract complicated math functions. -""" - -import math -from svg_to_gcode.geometry import Vector, RotationMatrix -from svg_to_gcode import TOLERANCES - - -def tolerance_constrain(value, maximum, minimum, tolerance=TOLERANCES["operation"]): - """ - Constrain a value between if it surpasses a limit and is within operational tolerance of the limit. Else return the - value. Useful if you want to correct for flatting point errors but still want to raise an exception if the value is - out-of-bounds for a different reason. - """ - - if value > maximum and value-maximum < tolerance: - return maximum - - if value < minimum and minimum-value < tolerance: - return minimum - - return value - - -def line_slope(p1, p2): - """Calculate the slope of the line p1p2""" - x1, y1 = p1.x, p1.y - x2, y2 = p2.x, p2.y - - if x1 == x2: - return 1 - - return (y1 - y2) / (x1 - x2) - - -def line_offset(p1, p2): - """Calculate the offset of the line p1p2 from the origin""" - x1, y1 = p1.x, p1.y - - return y1 - line_slope(p1, p2) * x1 - - -def line_intersect(p1, c1, p2, c2): - """Find point of intersection between line p1c2 and p2c2""" - p1_c, c1_c, p2_c, c2_c = p1.conjugate(), c1.conjugate(), p2.conjugate(), c2.conjugate() - - return (((c1_c - p1_c) * p1 - (c1 - p1) * p1_c) * (c2 - p2) - ((c2_c - p2_c) * p2 - (c2 - p2) * p2_c) * ( - c1 - p1)) / ((c2 - p2) * (c1_c - p1_c) - (c1 - p1) * (c2_c - p2_c)) - - -def is_on_mid_perpendicular(z, a, b): - """Check if a point z is on the line which is perpendicular to ab and passes through the segment's midpoint""" - return ((2 * z - (a + b)) / (a - b)).x == 0 - - -def tangent_arc_center(c, p, g): - """Find center of circular arc which passes through p and g, and is tangent to the line pc""" - c_c, p_c, g_c = c.conjugate(), p.conjugate(), g.conjugate() - - return (c * g * (p_c - g_c) + p * (g * (-2 * p_c + c_c + g_c) + (p_c - c_c) * p) - ) / (g * (-p_c + c_c) + c * (p_c - g_c) + (-c_c + g_c) * p) - - -def linear_map(min, max, t): - """Linear map from t∈[0, 1] --> t'∈[min, max]""" - return (max - min) * t + min - - -def inv_linear_map(min, max, t_p): - """Linear map from t'∈[min, max] --> t∈[0, 1]""" - return (t_p - min)/(max - min) - - -def angle_between_vectors(v1, v2): - """Compute angle between two vectors v1, v2""" - cos_angle = Vector.dot_product(v1, v2) / (abs(v1) * abs(v2)) - cos_angle = tolerance_constrain(cos_angle, 1, -1) - - angle = math.acos(cos_angle) - - angle *= 1 if v1.x * v2.y - v1.y * v2.x > 0 else -1 - - return angle - - -def center_to_endpoint_parameterization(center, radii, rotation, start_angle, sweep_angle): - rotation_matrix = RotationMatrix(rotation) - - start = rotation_matrix * Vector(radii.x * math.cos(start_angle), radii.y * math.sin(start_angle)) + center - - end_angle = start_angle + sweep_angle - end = rotation_matrix * Vector(radii.x * math.cos(end_angle), radii.y * math.sin(end_angle)) + center - - large_arc_flag = 1 if abs(sweep_angle) > math.pi else 0 - sweep_flag = 1 if sweep_angle > 0 else 0 - - return start, end, large_arc_flag, sweep_flag - - -def endpoint_to_center_parameterization(start, end, radii, rotation_rad, large_arc_flag, sweep_flag): - # Find and select one of the two possible eclipse centers by undoing the rotation (to simplify the math) and - # then re-applying it. - rotated_primed_values = (start - end) / 2 # Find the primed_values of the start and the end points. - primed_values = RotationMatrix(rotation_rad, True) * rotated_primed_values - px, py = primed_values.x, primed_values.y - - # Correct out-of-range radii - rx = abs(radii.x) - ry = abs(radii.y) - - delta = px ** 2 / rx ** 2 + py ** 2 / ry ** 2 - - if delta > 1: - rx *= math.sqrt(delta) - ry *= math.sqrt(delta) - - if math.sqrt(delta) > 1: - center = Vector(0, 0) - else: - radicant = ((rx * ry) ** 2 - (rx * py) ** 2 - (ry * px) ** 2) / ((rx * py) ** 2 + (ry * px) ** 2) - radicant = max(0, radicant) - - # Find center using w3.org's formula - center = math.sqrt(radicant) * Vector((rx * py) / ry, - (ry * px) / rx) - - center *= -1 if large_arc_flag == sweep_flag else 1 # Select one of the two solutions based on flags - - rotated_center = RotationMatrix(rotation_rad) * center + (start + end) / 2 # re-apply the rotation - - cx, cy = center.x, center.y - u = Vector((px - cx) / rx, (py - cy) / ry) - v = Vector((-px - cx) / rx, (-py - cy) / ry) - - max_angle = 2 * math.pi - - start_angle = angle_between_vectors(Vector(1, 0), u) - sweep_angle_unbounded = angle_between_vectors(u, v) - sweep_angle = sweep_angle_unbounded % max_angle - - if not sweep_flag and sweep_angle > 0: - sweep_angle -= max_angle - - if sweep_flag and sweep_angle < 0: - sweep_angle += max_angle - - return Vector(rx, ry), rotated_center, start_angle, sweep_angle diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__init__.py deleted file mode 100644 index 6219e10..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -The geometry sub-module offers a geometric representation of curves. - -specific maintenance notes: - - Explicit variable declaration for geometric objects. ** You can't dynamically declare instance variables. ** - Refer to the docs for more info https://docs.python.org/3/reference/datamodel.html?highlight=__slots__#slots - - The origin is at the bottom-left. As such, all svg_curves must be transformed from the top-left coordinate system - before generating geometric objects. -""" - -from svg_to_gcode.geometry._vector import Vector -from svg_to_gcode.geometry._matrix import Matrix, IdentityMatrix, RotationMatrix - -from svg_to_gcode.geometry._abstract_curve import Curve -from svg_to_gcode.geometry._line import Line -from svg_to_gcode.geometry._circular_arc import CircularArc -from svg_to_gcode.geometry._elliptical_arc import EllipticalArc -from svg_to_gcode.geometry._quadratic_bazier import QuadraticBezier -from svg_to_gcode.geometry._cubic_bazier import CubicBazier - -from svg_to_gcode.geometry._abstract_chain import Chain -from svg_to_gcode.geometry._line_segment_chain import LineSegmentChain -from svg_to_gcode.geometry._smooth_arc_chain import SmoothArcChain diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 80c8326..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_abstract_chain.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_abstract_chain.cpython-38.pyc deleted file mode 100644 index 95875b7..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_abstract_chain.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_abstract_curve.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_abstract_curve.cpython-38.pyc deleted file mode 100644 index 805c81f..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_abstract_curve.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_circular_arc.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_circular_arc.cpython-38.pyc deleted file mode 100644 index 3a1c9c6..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_circular_arc.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_cubic_bazier.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_cubic_bazier.cpython-38.pyc deleted file mode 100644 index 222d03e..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_cubic_bazier.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_elliptical_arc.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_elliptical_arc.cpython-38.pyc deleted file mode 100644 index d6e1363..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_elliptical_arc.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_line.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_line.cpython-38.pyc deleted file mode 100644 index 86f56ec..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_line.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_line_segment_chain.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_line_segment_chain.cpython-38.pyc deleted file mode 100644 index 834c372..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_line_segment_chain.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_matrix.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_matrix.cpython-38.pyc deleted file mode 100644 index 4ba31a8..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_matrix.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_quadratic_bazier.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_quadratic_bazier.cpython-38.pyc deleted file mode 100644 index ae6492c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_quadratic_bazier.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_smooth_arc_chain.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_smooth_arc_chain.cpython-38.pyc deleted file mode 100644 index 36cdabb..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_smooth_arc_chain.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_vector.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_vector.cpython-38.pyc deleted file mode 100644 index ddf7417..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/__pycache__/_vector.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_abstract_chain.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_abstract_chain.py deleted file mode 100644 index ec03681..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_abstract_chain.py +++ /dev/null @@ -1,104 +0,0 @@ -from collections.abc import Iterable - -from svg_to_gcode.geometry import Curve -from svg_to_gcode import formulas - - -class Chain(Curve): - """ - The Chain class is used to store a sequence of consecutive curves. When considered as a whole, Chains can also be - viewed as a single, continuous curve. They inherit from the Curve class and are equipped with the subsequent point() - and derivative() methods. - """ - - __slots__ = '_curves' - - def __init__(self, curves=None): - self._curves = [] - - if curves is not None: - self.extend(curves) - - def __iter__(self): - yield from self._curves - - def length(self): - """ - Return the geometric length of the chain. - The __len__ magic method wasn't overridden to avoid ambiguity between total length and chain size. - """ - return sum([curve.length() for curve in self._curves]) - - def chain_size(self): - """ - Return the number of curves in the chain. - The __len__ magic method wasn't overridden to avoid ambiguity between total length and chain size. - """ - return len(self._curves) - - def get(self, index: int) -> Curve: - """Return a curve at a given index""" - return self._curves[index] - - def append(self, new_curve: Curve): - """Append a new curve to the chain""" - raise NotImplementedError("All chains must implement an append command") - - def extend(self, new_curves: Iterable): - """Extend the Chain with an iterable""" - for new_curve in new_curves: - self.append(new_curve) - - def merge(self, chain: "Chain"): - """ - Merge the Chain instance with another Chain. - Equivalent to self.extend() but includes additional checks. - """ - if not chain._curves: - return - - if self._curves: - assert self.append(chain._curves[0]) - - self._curves.extend(chain._curves[1:]) - - def remove_from_first(self, number_of_curves: int): - """Remove n curves starting from the first""" - for i in range(number_of_curves): - self._curves.pop(0) - - def remove_from_last(self, number_of_curves: int): - """Remove n curves starting from the last""" - for i in range(len(self._curves) - 1, number_of_curves + 1, -1): - self._curves.pop(-1) - - def _get_curve_t(self, t): - lengths = [curve.length() for curve in self._curves] - t_position = t * sum(lengths) - - position = 0 - for i, length in enumerate(lengths): - position += length - if position > t_position: - break - - curve_t = formulas.inv_linear_map(position - length, position, t_position) - - return self._curves[i], curve_t - - def point(self, t): - if self.chain_size() == 0: - raise ValueError("Chain.point was called before adding any curves to the chain.") - - curve, curve_t = self._get_curve_t(t) - return curve.point(curve_t) - - def derivative(self, t): - if self.chain_size() == 0: - raise ValueError("Chain.derivative was called before adding any curves to the chain.") - - curve, curve_t = self._get_curve_t(t) - return curve.derivative(curve_t) - - def sanity_check(self): - pass diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_abstract_curve.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_abstract_curve.py deleted file mode 100644 index 03c7086..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_abstract_curve.py +++ /dev/null @@ -1,74 +0,0 @@ -from svg_to_gcode import formulas -from svg_to_gcode.geometry import Vector - - -class Curve: - """ - The Curve abstract class is the cornerstone of the geometry sub-module. Child classes inherit from it to represent - different types of curves. - - :param self.start: the first point of the curve - :type self.start: Vector - - :param self.end: the last point of the curve - :type self.end: Vector - """ - - __slots__ = 'start', 'end' - - def point(self, t: float) -> Vector: - """ - The point method returns a point along the curve. - - :param t: t is a number between 0 and 1. - :return: the point at distance t*self.length from self.start. For t=0.5, the point half-way across the curve - would be returned. - """ - raise NotImplementedError("point(self, t) must be implemented") - - def derivative(self, t): - """ - The derivative method returns a derivative at a point along the curve. - - :param t: t is a number between 0 and 1. - :return: the derivative at self.point(t) - """ - raise NotImplementedError("derivative(self, t) must be implemented") - - def sanity_check(self): - """Verify if that the curve is valid.""" - raise NotImplementedError("sanity_check(self) must be implemented") - - # A custom print representation is trivial to implement and useful for debugging - def __repr__(self): - raise NotImplementedError("__repr__(self) must be implemented") - - @staticmethod - def max_distance(curve1: "Curve", curve2: "Curve", t_range1=(0, 1), t_range2=(0, 1), samples=9): - - """ - Return the approximate maximum distance between two Curves for different values of t. - - WARNING: should only be used when comparing relatively flat curve segments. If one of the two - curves has very eccentric humps, the tip of the hump may not be sampled. - - :param curve1: the first of two curves to be compared. - :param curve2: the second of two curves to be compared. - :param t_range1: (min_t, max_t) the range of t values which should be sampled in the first curve. The default - value of (0, 1) samples the whole curve. A value of (0, 5) would only sample from the first half of the curve. - :param t_range2: (min_t, max_t) the range of t values which should be sampled in the second curve. The default - value of (0, 1) samples the whole curve. A value of (0, 5) would only sample from the first half of the curve. - :param samples: the number of samples which should be taken. Higher values are slower but more accurate. - :return: the approximate maximum distance - """ - - maximum_distance = 0 - for i in range(samples): - t = (i + 1) / (samples + 1) - t1 = formulas.linear_map(t_range1[0], t_range1[1], t) - t2 = formulas.linear_map(t_range2[0], t_range2[1], t) - - distance = abs(curve1.point(t1) - curve2.point(t2)) - maximum_distance = distance if distance > maximum_distance else maximum_distance - - return maximum_distance diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_circular_arc.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_circular_arc.py deleted file mode 100644 index f2b8636..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_circular_arc.py +++ /dev/null @@ -1,72 +0,0 @@ -import math - -from svg_to_gcode.geometry import Vector -from svg_to_gcode.geometry import Curve -from svg_to_gcode import formulas -from svg_to_gcode import TOLERANCES - - -class CircularArc(Curve): - """The CircularArc class inherits from the abstract Curve class and describes a circular arc.""" - - __slots__ = 'center', 'radius', 'start_angle', 'end_angle' - - # ToDo use different instantiation parameters to be consistent with elliptical arcs - def __init__(self, start: Vector, end: Vector, center: Vector): - self.start = start - self.end = end - self.center = center - - self.radius = abs(self.start - self.center) - self.start_angle = self.point_to_angle(self.start) - self.end_angle = self.point_to_angle(self.end) - - def __repr__(self): - return f"Arc(start: {self.start}, end: {self.end}, center: {self.center})" - - def length(self): - return abs(self.start_angle - self.end_angle) * self.radius - - def angle_to_point(self, rad): - at_origin = self.radius * Vector(math.cos(rad), math.sin(rad)) - translated = at_origin + self.center - return translated - - def point_to_angle(self, point: Vector): - translated = (point - self.center)/self.radius # translate the point onto the unit circle - return math.acos(translated.x) - - def point(self, t): - angle = formulas.linear_map(self.start_angle, self.end_angle, t) - return self.angle_to_point(angle) - - def derivative(self, t): - position = self.point(t) - return (self.center.x - position.x) / (position.y - self.center.y) - - def sanity_check(self): - # Assert that the Arc is not a point or a line - try: - assert abs(self.start - self.end) > TOLERANCES["input"] - except AssertionError: - raise ValueError(f"Arc is a point. The start and the end points are equivalent: " - f"|{self.start} - {self.end}| <= {TOLERANCES['input']}") - - try: - assert abs(self.start - self.center) > TOLERANCES["input"] - except AssertionError: - raise ValueError(f"Arc is a line. The start and the center points are equivalent, " - f"|{self.start} - {self.center}| <= {TOLERANCES['input']}") - - try: - assert abs(self.end - self.center) > TOLERANCES["input"] - except AssertionError: - raise ValueError(f"Arc is a line. The end and the center points are equivalent, " - f"|{self.end} - {self.center}| <= {TOLERANCES['input']}") - - # Assert that the center is equidistant from the start and the end - try: - assert abs(abs(self.start - self.center) - abs(self.end - self.center)) < TOLERANCES['input'] - except AssertionError: - raise ValueError(f"Center is not equidistant to the start and end points within tolerance, " - f"|{abs(self.start - self.center)} - {abs(self.end - self.center)}| >= {TOLERANCES['input']}") diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_cubic_bazier.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_cubic_bazier.py deleted file mode 100644 index 86d1ab3..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_cubic_bazier.py +++ /dev/null @@ -1,32 +0,0 @@ -from svg_to_gcode.geometry import Vector -from svg_to_gcode.geometry import Curve - - -class CubicBazier(Curve): - """The CubicBazier class inherits from the abstract Curve class and describes a cubic bazier.""" - - __slots__ = 'control1', 'control2' - - def __init__(self, start: Vector, end: Vector, control1: Vector, control2: Vector): - - self.start = start - self.end = end - self.control1 = control1 - self.control2 = control2 - - def __repr__(self): - return f"CubicBazier(start: {self.start}, end: {self.end}, control1: {self.control1}, control2: {self.control2})" - - def point(self, t): - return (1-t)**3 * self.start +\ - 3 * (1-t)**2 * t * self.control1 +\ - 3 * (1-t) * t**2 * self.control2 +\ - t**3 * self.end - - def derivative(self, t): - return 3 * (1-t)**2 * (self.control1 - self.start) +\ - 6 * (1-t) * t * (self.control2 - self.control1) +\ - 3 * t**2 * (self.end - self.control2) - - def sanity_check(self): - pass diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_elliptical_arc.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_elliptical_arc.py deleted file mode 100644 index a8d8d85..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_elliptical_arc.py +++ /dev/null @@ -1,58 +0,0 @@ -import math - -from svg_to_gcode import formulas -from svg_to_gcode.geometry import Vector, RotationMatrix -from svg_to_gcode.geometry import Curve - - -class EllipticalArc(Curve): - """The EllipticalArc class inherits from the abstract Curve class and describes an elliptical arc.""" - - __slots__ = 'center', 'radii', 'rotation', 'start_angle', 'sweep_angle', 'end_angle', 'transformation' - - # ToDo apply transformation beforehand (in Path) for consistency with other geometric objects. If you (the reader) - # know how to easily apply an affine transformation to an ellipse feel free to make a pull request. - def __init__(self, center: Vector, radii: Vector, rotation: float, start_angle: float, sweep_angle: float, - transformation: None): - - # Assign and verify arguments - self.center = center - self.radii = radii - self.rotation = rotation - self.start_angle = start_angle - self.sweep_angle = sweep_angle - self.transformation = transformation - - # Calculate missing data - self.end_angle = start_angle + sweep_angle - self.start = self.angle_to_point(self.start_angle) - self.end = self.angle_to_point(self.end_angle) - - self.sanity_check() - - def __repr__(self): - return f"EllipticalArc(start: {self.start}, end: {self.end}, center: {self.center}, radii: {self.radii}," \ - f" rotation: {self.rotation}, start_angle: {self.start_angle}, sweep_angle: {self.sweep_angle})" - - def point(self, t): - angle = formulas.linear_map(self.start_angle, self.end_angle, t) - return self.angle_to_point(angle) - - def angle_to_point(self, angle): - transformed_radii = Vector(self.radii.x * math.cos(angle), self.radii.y * math.sin(angle)) - point = RotationMatrix(self.rotation) * transformed_radii + self.center - - if self.transformation: - point = self.transformation.apply_affine_transformation(point) - - return point - - def derivative(self, t): - angle = formulas.linear_map(self.start_angle, self.end_angle, t) - return self.angle_to_derivative(angle) - - def angle_to_derivative(self, rad): - return -(self.radii.y / self.radii.x) * math.tan(rad)**-1 - - def sanity_check(self): - pass diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_line.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_line.py deleted file mode 100644 index 947d765..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_line.py +++ /dev/null @@ -1,32 +0,0 @@ -from svg_to_gcode.geometry import Vector -from svg_to_gcode.geometry import Curve -from svg_to_gcode import formulas - - -# A line segment -class Line(Curve): - """The Line class inherits from the abstract Curve class and describes a straight line segment.""" - - __slots__ = 'slope', 'offset' - - def __init__(self, start, end): - self.start = start - self.end = end - - self.slope = formulas.line_slope(start, end) - self.offset = formulas.line_offset(start, end) - - def __repr__(self): - return f"Line(start:{self.start}, end:{self.end}, slope:{self.slope}, offset:{self.offset})" - - def length(self): - return abs(self.start - self.end) - - def point(self, t): - x = self.start.x + t * (self.end.x - self.start.x) - y = self.slope * x + self.offset - - return Vector(x, y) - - def derivative(self, t): - return self.slope diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_line_segment_chain.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_line_segment_chain.py deleted file mode 100644 index f741dc3..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_line_segment_chain.py +++ /dev/null @@ -1,88 +0,0 @@ -from svg_to_gcode.geometry import Chain -from svg_to_gcode.geometry import Curve, Line, Vector -from svg_to_gcode import TOLERANCES - - -class LineSegmentChain(Chain): - """ - The LineSegmentChain class inherits form the abstract Chain class. It represents a series of continuous straight - line-segments. - - LineSegmentChains can be instantiated either conventionally or through the static method line_segment_approximation(), - which approximates any Curve with a series of line-segments contained in a new LineSegmentChain instance. - """ - def __repr__(self): - return f"{type(self)}({len(self._curves)} curves: {[line.__repr__() for line in self._curves[:2]]}...)" - - def append(self, line2: Line): - if self._curves: - line1 = self._curves[-1] - - # Assert continuity - if abs(line1.end - line2.start) > TOLERANCES['input']: - raise ValueError(f"The end of the last line is different from the start of the new line" - f"|{line1.end} - {line2.start}| >= {TOLERANCES['input']}") - - # Join lines - line2.start = line1.end - - self._curves.append(line2) - - @staticmethod - def line_segment_approximation(shape, increment_growth=11 / 10, error_cap=None, error_floor=None)\ - -> "LineSegmentChain": - """ - This method approximates any shape using straight line segments. - - :param shape: The shape to be approximated. - :param increment_growth: the scale by which line_segments grow and shrink. Must be > 1. - :param error_cap: the maximum acceptable deviation from the curve. - :param error_floor: the maximum minimum deviation from the curve before segment length starts growing again. - :return: A LineSegmentChain which approximates the given shape. - """ - - error_cap = TOLERANCES['approximation'] if error_cap is None else error_cap - error_floor = (increment_growth - 1) * error_cap if error_floor is None else error_floor - - if error_cap <= 0: - raise ValueError(f"This algorithm is approximate. error_cap must be a non-zero positive float. Not {error_cap}") - - if increment_growth <= 1: - raise ValueError(f"increment_growth must be > 1. Not {increment_growth}") - - lines = LineSegmentChain() - - if isinstance(shape, Line): - lines.append(shape) - return lines - - t = 0 - line_start = shape.start - increment = 5 - - while t < 1: - new_t = t + increment - - if new_t > 1: - new_t = 1 - - line_end = shape.point(new_t) - line = Line(line_start, line_end) - - distance = Curve.max_distance(shape, line, t_range1=(t, new_t)) - - # If the error is too high, reduce increment and restart cycle - if distance > error_cap: - increment /= increment_growth - continue - - # If the error is very low, increase increment but DO NOT restart cycle. - if distance < error_floor: - increment *= increment_growth - - lines.append(line) - - line_start = line_end - t = new_t - - return lines diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_matrix.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_matrix.py deleted file mode 100644 index 3524044..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_matrix.py +++ /dev/null @@ -1,82 +0,0 @@ -import math - -from svg_to_gcode.geometry import Vector - - -class Matrix: - """The Matrix class represents matrices. It's mostly used for applying linear transformations to vectors.""" - __slots__ = 'number_of_rows', 'number_of_columns', 'matrix_list' - - def __init__(self, matrix_list): - """ - :param matrix_list: the matrix represented as a list of rows. - :type matrix_list: list[list] - """ - self.number_of_rows = len(matrix_list) - self.number_of_columns = len(matrix_list[0]) - - if not all([len(row) == self.number_of_columns for row in matrix_list]): - raise ValueError("Not a matrix. Rows in matrix_list have different lengths.") - - if not all([all([isinstance(value, float) or isinstance(value, int) for value in row]) for row in matrix_list]): - raise ValueError("Not a matrix. matrix_list contains non numeric values.") - - self.matrix_list = matrix_list - - def __repr__(self): - matrix_str = "\n ".join([str(row) for row in self]) - return f"Matrix({matrix_str})" - - def __iter__(self): - yield from self.matrix_list - - def __getitem__(self, index: int): - return self.matrix_list[index] - - def __mul__(self, other): - if isinstance(other, Vector): - return self.multiply_vector(other) - - if isinstance(other, Matrix): - return self.multiply_matrix(other) - - raise TypeError(f"can't multiply matrix by type '{type(other)}'") - - def multiply_vector(self, other_vector: Vector): - if self.number_of_columns != 2: - raise ValueError(f"can't multiply matrix with 2D vector. The matrix must have 2 columns, not " - f"{self.number_of_columns}") - - x = sum([self[0][k] * other_vector[k] for k in range(self.number_of_columns)]) - y = sum([self[1][k] * other_vector[k] for k in range(self.number_of_columns)]) - - return Vector(x, y) - - def multiply_matrix(self, other_matrix: "Matrix"): - if self.number_of_columns != other_matrix.number_of_rows: - raise ValueError(f"can't multiply matrices. The first matrix must have the same number of columns as the " - f"second has rows. {self.number_of_columns}!={other_matrix.number_of_rows}") - - matrix_list = [[ - sum([self[i][k] * other_matrix[k][j] for k in range(self.number_of_columns)]) - for j in range(other_matrix.number_of_columns)] - for i in range(self.number_of_rows)] - - return Matrix(matrix_list) - - -class IdentityMatrix(Matrix): - def __init__(self, size): - matrix_list = [[int(i == j) for j in range(size)] for i in range(size)] - super().__init__(matrix_list) - - -class RotationMatrix(Matrix): - def __init__(self, angle, inverse=False): - if not inverse: - matrix_list = [[math.cos(angle), -math.sin(angle)], - [math.sin(angle), math.cos(angle)]] - else: - matrix_list = [[math.cos(angle), math.sin(angle)], - [-math.sin(angle), math.cos(angle)]] - super().__init__(matrix_list) diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_quadratic_bazier.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_quadratic_bazier.py deleted file mode 100644 index cc55e75..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_quadratic_bazier.py +++ /dev/null @@ -1,29 +0,0 @@ -from svg_to_gcode.geometry import Vector -from svg_to_gcode.geometry import Curve - - -class QuadraticBezier(Curve): - """The QuadraticBezier class inherits from the abstract Curve class and describes a quadratic bezier.""" - - __slots__ = 'control' - - def __init__(self, start: Vector, end: Vector, control: Vector): - - self.start = start - self.end = end - self.control = control - - self.sanity_check() - - def __repr__(self): - return f"QuadraticBezier(start: {self.start}, end: {self.end}, control: {self.control})" - - def point(self, t): - return self.control + ((1 - t)**2) * (self.start - self.control) + (t**2) * (self.end - self.control) - - def derivative(self, t): - return 2 * (1 - t) * (self.control - self.start) + 2 * t * (self.end - self.control) - - def sanity_check(self): - # ToDo verify if self.start == self.end forms a valid curve under the svg standard - pass diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_smooth_arc_chain.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_smooth_arc_chain.py deleted file mode 100644 index 7bea78d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_smooth_arc_chain.py +++ /dev/null @@ -1,67 +0,0 @@ -""" -This is an unfinished class which should not be committed yet. -""" - -from svg_to_gcode.geometry import Chain -from svg_to_gcode.geometry import CircularArc -from svg_to_gcode import TOLERANCES, formulas - - -class SmoothArcChain(Chain): - - def __repr__(self): - return f"SmoothArcs({[arc.__repr__() for arc in self._curves]})" - - def append(self, arc2: CircularArc): - - if self._curves: - arc1 = self._curves[-1] - - # Assert continuity - try: - assert abs(arc1.end - arc2.start) < TOLERANCES['input'] - except AssertionError: - raise ValueError(f"The end of the last arc is different from the start of the new arc, " - f"|{arc1.end} - {arc2.start}| >= {TOLERANCES['input']}") - - try: - assert abs(arc1.derivative(arc1.end) - arc2.derivative(arc2.start)) < TOLERANCES['input'] - except AssertionError: - raise ValueError(f"The last arc and the new arc form a discontinues curve, " - f"|{arc1.derivative(1)} - {arc2.derivative(0)}| >= {TOLERANCES['input']}") - - # Join arcs - arc2.start = arc1.end - - self._curves.append(arc2) - - @staticmethod - def cubic_bazier_to_arcs(bazier, _arcs=None): - - smooth_arcs = _arcs if _arcs else SmoothArcChain() - - start, control1, control2, end = bazier.start, bazier.control1, bazier.control2, bazier.end - tangent_intersect = formulas.line_intersect(start, control1, end, control2) - - # print("tangent_intersect:", tangent_intersect) - - start_length = abs(start - tangent_intersect) - end_length = abs(end - tangent_intersect) - base_length = abs(start - end) - - # print("start_length:", start_length, "end_length:", end_length, "base_length:", base_length) - - incenter_point = (start_length * end + end_length * start + base_length * tangent_intersect) / \ - (start_length + end_length + base_length) - - # print("incenter:", incenter_point) - - center1 = formulas.tangent_arc_center(control1, start, incenter_point) - center2 = formulas.tangent_arc_center(control2, end, incenter_point) - - # print("centers:", center1, center2) - - smooth_arcs.append(CircularArc(start, incenter_point, center1)) - smooth_arcs.append(CircularArc(incenter_point, end, center2)) - - return smooth_arcs diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_vector.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_vector.py deleted file mode 100644 index fc53a9a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/geometry/_vector.py +++ /dev/null @@ -1,52 +0,0 @@ -class Vector: - """The Vector class is a simple representation of a 2D vector.""" - - __slots__ = 'x', 'y' - - def __init__(self, x, y): - self.x = x - self.y = y - - def __repr__(self): - return f"Vector({self.x}, {self.y})" - - def __add__(self, other): - return Vector(self.x + other.x, self.y + other.y) - - def __sub__(self, other): - return Vector(self.x - other.x, self.y - other.y) - - def __mul__(self, other): - if isinstance(other, Vector): - return Vector.dot_product(self, other) - - return Vector.scalar_product(self, other) - - __rmul__ = __mul__ - - def __truediv__(self, other): - if not (isinstance(other, int) or isinstance(other, float)): - raise TypeError(f"""unsupported operand type(s) for /: 'Vector' and {type(other)}""") - - return Vector.scalar_product(self, 1/other) - - def __abs__(self): - return (self.x ** 2 + self.y ** 2) ** 0.5 - - def __iter__(self): - yield from (self.x, self.y) # ignore your editor, these parentheses are not redundant - - def __getitem__(self, index: int): - return (self.x, self.y)[index] - - @staticmethod - def scalar_product(v1: "Vector", n: int): - return Vector(v1.x * n, v1.y * n) - - @staticmethod - def dot_product(v1: "Vector", v2: "Vector"): - return v1.x*v2.x + v1.y*v2.y - - @staticmethod - def cross_product(v1: "Vector", v2: "Vector"): - return Vector(v1.x * (v2.x + v2.y), v1.y * (v2.x + v2.y)) diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__init__.py deleted file mode 100644 index f667a54..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -""" -The svg_parser sub-module is used to parse svg_files into the geometric form supplied by the geometry sub-module. - -specific maintenance notes: - - The svg origin is at the top-left, while the geometry sub-module has it's origin a the bottom-left. As such, all - parser classes must transform_origin input coordinates to the bottom-left coordinate system. -""" - -from svg_to_gcode.svg_parser._transformation import Transformation -from svg_to_gcode.svg_parser._path import Path -from svg_to_gcode.svg_parser._parser_methods import parse_file, parse_string, parse_root diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index d1190a5..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__pycache__/_helper_functions.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__pycache__/_helper_functions.cpython-38.pyc deleted file mode 100644 index 7ca7f08..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__pycache__/_helper_functions.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__pycache__/_parser_methods.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__pycache__/_parser_methods.cpython-38.pyc deleted file mode 100644 index 6efdbe2..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__pycache__/_parser_methods.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__pycache__/_path.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__pycache__/_path.cpython-38.pyc deleted file mode 100644 index f94657b..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__pycache__/_path.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__pycache__/_transformation.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__pycache__/_transformation.cpython-38.pyc deleted file mode 100644 index 4db0026..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__pycache__/_transformation.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__pycache__/debug_methods.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__pycache__/debug_methods.cpython-38.pyc deleted file mode 100644 index e19e880..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/__pycache__/debug_methods.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/_helper_functions.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/_helper_functions.py deleted file mode 100644 index e35d2f5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/_helper_functions.py +++ /dev/null @@ -1,102 +0,0 @@ -from xml.etree import ElementTree -from typing import List -from svg_to_gcode.svg_parser import Path -from svg_to_gcode.geometry import Curve - -NAMESPACES = {'svg': 'http://www.w3.org/2000/svg'} - - -def _has_style(element: ElementTree.Element, key: str, value: str) -> bool: - """ - Check if an element contains a specific key and value either as an independent attribute or in the style attribute. - """ - return element.get(key) == value or (element.get("style") and f"{key}:{value}" in element.get("style")) - - -def parse_root(root: ElementTree.Element, canvas_height=None, transform=True, draw_hidden=False, _visible_root=True) \ - -> List[Curve]: - - """ - Recursively parse an etree root's children into geometric curves. - - :param root: The etree element who's children should be recursively parsed. The root will not be drawn. - :param canvas_height: The height of the canvas. By default the height attribute of the root is used. If the root - does not contain the height attribute, it must be either manually specified or transform must be False. - :param transform: Whether or not to transform input coordinates from the svg coordinate system to standard cartesian - system. Depends on canvas_height for calculations. - :param draw_hidden: Whether or not to draw hidden elements based on their display, visibility and opacity attributes. - :param _visible_root: Internally used to specify whether or the root is visible. (Inheritance can be overridden) - :return: A list of geometric curves describing the svg. Use the Compiler sub-module to compile them to gcode. - """ - - if canvas_height is None: - height_str = root.get("height") - canvas_height = float(height_str) if height_str.isnumeric() else float(height_str[:-2]) - - curves = [] - - if draw_hidden: - # Parse paths - for element in root.iter("{%s}path" % NAMESPACES["svg"]): - path = Path(element.attrib['d'], canvas_height, transform, transform) - curves.extend(path.curves) - else: - # Draw visible elements (Depth-first search) - for element in list(root): - - # display cannot be overridden by inheritance. Just skip the element - if _has_style(element, "display", "none"): - continue - - # Is the element and it's root not hidden? - visible = _visible_root and not (_has_style(element, "visibility", "hidden") - or _has_style(element, "visibility", "collapse")) - # Override inherited visibility - visible = visible or (_has_style(element, "visibility", "visible")) - - transparent = _has_style(element, "opacity", "0") - - # If the current element is opaque and visible, draw it - if not transparent and visible: - if element.tag == "{%s}path" % NAMESPACES["svg"]: - path = Path(element.attrib['d'], canvas_height, transform, transform) - curves.extend(path.curves) - - # Continue the recursion - curves.extend(parse_root(element, canvas_height, transform, False, visible)) - - # ToDo implement shapes class - return curves - - -# Todo deal with viewBoxes -def parse_string(svg_string: str, canvas_height=None, transform=True, draw_hidden=False) -> List[Curve]: - """ - Recursively parse an svg string into geometric curves. (Wrapper for parse_root) - - :param svg_string: The etree element who's children should be recursively parsed. The root will not be drawn. - :param canvas_height: The height of the canvas. By default the height attribute of the root is used. If the root - does not contain the height attribute, it must be either manually specified or transform must be False. - :param transform: Whether or not to transform input coordinates from the svg coordinate system to standard cartesian - system. Depends on canvas_height for calculations. - :param draw_hidden: Whether or not to draw hidden elements based on their display, visibility and opacity attributes. - :return: A list of geometric curves describing the svg. Use the Compiler sub-module to compile them to gcode. - """ - root = ElementTree.fromstring(svg_string) - return parse_root(root, canvas_height, transform, draw_hidden) - - -def parse_file(file_path: str, canvas_height=None, transform=True, draw_hidden=False) -> List[Curve]: - """ - Recursively parse an svg file into geometric curves. (Wrapper for parse_root) - - :param file_path: The etree element who's children should be recursively parsed. The root will not be drawn. - :param canvas_height: The height of the canvas. By default the height attribute of the root is used. If the root - does not contain the height attribute, it must be either manually specified or transform must be False. - :param transform: Whether or not to transform input coordinates from the svg coordinate system to standard cartesian - system. Depends on canvas_height for calculations. - :param draw_hidden: Whether or not to draw hidden elements based on their display, visibility and opacity attributes. - :return: A list of geometric curves describing the svg. Use the Compiler sub-module to compile them to gcode. - """ - root = ElementTree.parse(file_path).getroot() - return parse_root(root, canvas_height, transform, draw_hidden) diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/_parser_methods.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/_parser_methods.py deleted file mode 100644 index cfe62d4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/_parser_methods.py +++ /dev/null @@ -1,106 +0,0 @@ -from xml.etree import ElementTree -from typing import List -from copy import deepcopy - -from svg_to_gcode.svg_parser import Path, Transformation -from svg_to_gcode.geometry import Curve - -NAMESPACES = {'svg': 'http://www.w3.org/2000/svg'} - - -def _has_style(element: ElementTree.Element, key: str, value: str) -> bool: - """ - Check if an element contains a specific key and value either as an independent attribute or in the style attribute. - """ - return element.get(key) == value or (element.get("style") and f"{key}:{value}" in element.get("style")) - - -# Todo deal with viewBoxes -def parse_root(root: ElementTree.Element, transform_origin=True, canvas_height=None, draw_hidden=False, - visible_root=True, root_transformation=None) -> List[Curve]: - - """ - Recursively parse an etree root's children into geometric curves. - - :param root: The etree element who's children should be recursively parsed. The root will not be drawn. - :param canvas_height: The height of the canvas. By default the height attribute of the root is used. If the root - does not contain the height attribute, it must be either manually specified or transform must be False. - :param transform_origin: Whether or not to transform input coordinates from the svg coordinate system to standard - cartesian system. Depends on canvas_height for calculations. - :param draw_hidden: Whether or not to draw hidden elements based on their display, visibility and opacity attributes. - :param visible_root: Specifies whether or the root is visible. (Inheritance can be overridden) - :param root_transformation: Specifies whether the root's transformation. (Transformations are inheritable) - :return: A list of geometric curves describing the svg. Use the Compiler sub-module to compile them to gcode. - """ - - if canvas_height is None: - height_str = root.get("height") - canvas_height = float(height_str) if height_str.isnumeric() else float(height_str[:-2]) - - curves = [] - - # Draw visible elements (Depth-first search) - for element in list(root): - - # display cannot be overridden by inheritance. Just skip the element - display = _has_style(element, "display", "none") - - if display or element.tag == "{%s}defs" % NAMESPACES["svg"]: - continue - - transformation = deepcopy(root_transformation) if root_transformation else None - - transform = element.get('transform') - if transform: - transformation = Transformation() if transformation is None else transformation - transformation.add_transform(transform) - - # Is the element and it's root not hidden? - visible = visible_root and not (_has_style(element, "visibility", "hidden") - or _has_style(element, "visibility", "collapse")) - # Override inherited visibility - visible = visible or (_has_style(element, "visibility", "visible")) - - # If the current element is opaque and visible, draw it - if draw_hidden or visible: - if element.tag == "{%s}path" % NAMESPACES["svg"]: - path = Path(element.attrib['d'], canvas_height, transform_origin, transformation) - curves.extend(path.curves) - - # Continue the recursion - curves.extend(parse_root(element, transform_origin, canvas_height, draw_hidden, visible, transformation)) - - # ToDo implement shapes class - return curves - - -def parse_string(svg_string: str, transform_origin=True, canvas_height=None, draw_hidden=False) -> List[Curve]: - """ - Recursively parse an svg string into geometric curves. (Wrapper for parse_root) - - :param svg_string: The etree element who's children should be recursively parsed. The root will not be drawn. - :param canvas_height: The height of the canvas. By default the height attribute of the root is used. If the root - does not contain the height attribute, it must be either manually specified or transform_origin must be False. - :param transform_origin: Whether or not to transform input coordinates from the svg coordinate system to standard cartesian - system. Depends on canvas_height for calculations. - :param draw_hidden: Whether or not to draw hidden elements based on their display, visibility and opacity attributes. - :return: A list of geometric curves describing the svg. Use the Compiler sub-module to compile them to gcode. - """ - root = ElementTree.fromstring(svg_string) - return parse_root(root, transform_origin, canvas_height, draw_hidden) - - -def parse_file(file_path: str, transform_origin=True, canvas_height=None, draw_hidden=False) -> List[Curve]: - """ - Recursively parse an svg file into geometric curves. (Wrapper for parse_root) - - :param file_path: The etree element who's children should be recursively parsed. The root will not be drawn. - :param canvas_height: The height of the canvas. By default the height attribute of the root is used. If the root - does not contain the height attribute, it must be either manually specified or transform_origin must be False. - :param transform_origin: Whether or not to transform input coordinates from the svg coordinate system to standard cartesian - system. Depends on canvas_height for calculations. - :param draw_hidden: Whether or not to draw hidden elements based on their display, visibility and opacity attributes. - :return: A list of geometric curves describing the svg. Use the Compiler sub-module to compile them to gcode. - """ - root = ElementTree.parse(file_path).getroot() - return parse_root(root, transform_origin, canvas_height, draw_hidden) diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/_path.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/_path.py deleted file mode 100644 index 92fa19d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/_path.py +++ /dev/null @@ -1,322 +0,0 @@ -import math -import warnings - -from typing import List - -from svg_to_gcode.geometry import Vector -from svg_to_gcode.geometry import Line, EllipticalArc, CubicBazier, QuadraticBezier -from svg_to_gcode.svg_parser import Transformation -from svg_to_gcode import formulas - -verbose = False - - -class Path: - """The Path class represents a generic svg path.""" - - command_lengths = {'M': 2, 'm': 2, 'L': 2, 'l': 2, 'H': 1, 'h': 1, 'V': 1, 'v': 1, 'Z': 0, 'z': 0, 'C': 6, 'c': 6, - 'Q': 4, 'q': 4, 'S': 4, 's': 4, 'T': 2, 't': 2, 'A': 7, 'a': 7} - - __slots__ = "curves", "initial_point", "current_point", "last_control", "canvas_height", "draw_move", \ - "transform_origin", "transformation" - - def __init__(self, d: str, canvas_height: float, transform_origin=True, transformation=None): - self.canvas_height = canvas_height - self.transform_origin = transform_origin - - self.curves = [] - self.initial_point = Vector(0, 0) # type: Vector - self.current_point = Vector(0, 0) - self.last_control = None # type: Vector - - self.transformation = Transformation() - - if self.transform_origin: - self.transformation.add_translation(0, canvas_height) - self.transformation.add_scale(1, -1) - - if transformation is not None: - self.transformation.extend(transformation) - - try: - self._parse_commands(d) - except Exception as generic_exception: - warnings.warn(f"Terminating path. The following unforeseen exception occurred: {generic_exception}") - - def __repr__(self): - return f"Path({self.curves})" - - def _parse_commands(self, d: str): - """Parse svg commands (stored in value of the d key) into geometric curves.""" - - command_key = '' # A character representing a specific command based on the svg standard - command_arguments = [] # A list containing the arguments for the current command_key - - number_str = '' # A buffer used to store numeric characters before conferring them to a number - - # Parse each character in d - i = 0 - while i < len(d): - character = d[i] - - is_numeric = character.isnumeric() or character in ['-', '.', 'e'] # Yes, "-6.2e-4" is a valid float. - is_delimiter = character.isspace() or character in [','] - is_command_key = character in self.command_lengths.keys() - is_final = i == len(d) - 1 - - # If the current command is complete, however the next command does not specify a new key, assume the next - # command has the same key. This is implemented by inserting the current key before the next command and - # restarting the loop without incrementing i - try: - if command_key and len(command_arguments) == self.command_lengths[command_key] and is_numeric: - duplicate = command_key - # If a moveto is followed by multiple pairs of coordinates, the subsequent pairs are treated as - # implicit lineto commands. https://www.w3.org/TR/SVG2/paths.html#PathDataMovetoCommands - if command_key == 'm': - duplicate = 'l' - - if command_key == 'M': - duplicate = 'L' - - d = d[:i] + duplicate + d[i:] - continue - except KeyError as key_error: - warnings.warn(f"Unknown command key {command_key}. Skipping curve.") - - # If the character is part of a number, keep on composing it - if is_numeric: - number_str += character - - # if a negative number follows another number, no delimiter is required. - # implicitly stated decimals like .6 don't require a delimiter. In either case we add a delimiter. - negatives = not is_final and character != 'e' and d[i + 1] == '-' - implicit_decimals = not is_final and d[i + 1] == '.' and '.' in number_str - if negatives or implicit_decimals: - d = d[:i+1] + ',' + d[i+1:] - - # If the character is a delimiter or a command key or the last character, complete the number and save it - # as an argument - if is_delimiter or is_command_key or is_final: - if number_str: - # In svg form '-.5' can be written as '-.5'. Python doesn't like that notation. - if number_str[0] == '.': - number_str = '0' + number_str - if number_str[0] == '-' and number_str[1] == '.': - number_str = '-0' + number_str[1:] - - command_arguments.append(float(number_str)) - number_str = '' - - # If it's a command key or the last character, parse the previous (now complete) command and save the letter - # as the new command key - if is_command_key or is_final: - if command_key: - self._add_svg_curve(command_key, command_arguments) - - command_key = character - command_arguments.clear() - - # If the last character is a command key (only useful for Z), save - if is_command_key and is_final: - self._add_svg_curve(command_key, command_arguments) - - i += 1 - - def _add_svg_curve(self, command_key: str, command_arguments: List[float]): - """ - Offer a representation of a curve using the geometry sub-module. - Based on Mozilla Docs: https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths - - Each sub-method must be implemented with the following structure: - def descriptive_name(*command_arguments): - execute calculations and transformations, **do not modify or create any instance variables** - generate curve - modify instance variables - return curve - - Alternatively a sub-method may simply call a base command. - - :param command_key: a character representing a specific command based on the svg standard - :param command_arguments: A list containing the arguments for the current command_key - """ - - # Establish a new initial point and a new current point. (multiple coordinates are parsed as lineto commands) - def absolute_move(x, y): - self.initial_point = Vector(x, y) - self.current_point = Vector(x, y) - return None - - def relative_move(dx, dy): - return absolute_move(*(self.current_point + Vector(dx, dy))) - - # Draw straight line - def absolute_line(x, y): - start = self.current_point - end = Vector(x, y) - - line = Line(self.transformation.apply_affine_transformation(start), - self.transformation.apply_affine_transformation(end)) - - self.current_point = end - - return line - - def relative_line(dx, dy): - return absolute_line(*(self.current_point + Vector(dx, dy))) - - def absolute_horizontal_line(x): - return absolute_line(x, self.current_point.y) - - def relative_horizontal_line(dx): - return absolute_horizontal_line(self.current_point.x + dx) - - def absolute_vertical_line(y): - return absolute_line(self.current_point.x, y) - - def relative_vertical_line(dy): - return absolute_vertical_line(self.current_point.y + dy) - - def close_path(): - return absolute_line(*self.initial_point) - - # Draw curvy curves - def absolute_cubic_bazier(control1_x, control1_y, control2_x, control2_y, x, y): - - trans_start = self.transformation.apply_affine_transformation(self.current_point) - trans_end = self.transformation.apply_affine_transformation(Vector(x, y)) - trans_control1 = self.transformation.apply_affine_transformation(Vector(control1_x, control1_y)) - trans_control2 = self.transformation.apply_affine_transformation(Vector(control2_x, control2_y)) - - cubic_bezier = CubicBazier(trans_start, trans_end, trans_control1, trans_control2) - - self.last_control = Vector(control2_x, control2_y) - self.current_point = Vector(x, y) - - return cubic_bezier - - def relative_cubic_bazier(dx1, dy1, dx2, dy2, dx, dy): - return absolute_cubic_bazier(self.current_point.x + dx1, self.current_point.y + dy1, - self.current_point.x + dx2, self.current_point.y + dy2, - self.current_point.x + dx, self.current_point.y + dy) - - def absolute_cubic_bezier_extension(x2, y2, x, y): - start = self.current_point - control2 = Vector(x2, y2) - end = Vector(x, y) - - if self.last_control: - control1 = 2 * start - self.last_control - bazier = absolute_cubic_bazier(*control1, *control2, *end) - else: - bazier = absolute_quadratic_bazier(*control2, *end) - - self.current_point = start - - return bazier - - def relative_cubic_bazier_extension(dx2, dy2, dx, dy): - return absolute_cubic_bezier_extension(self.current_point.x + dx2, self.current_point.y + dy2, - self.current_point.x + dx, self.current_point.y + dy) - - def absolute_quadratic_bazier(control1_x, control1_y, x, y): - - trans_end = self.transformation.apply_affine_transformation(self.current_point) - trans_new_end = self.transformation.apply_affine_transformation(Vector(x, y)) - trans_control1 = self.transformation.apply_affine_transformation(Vector(control1_x, control1_y)) - - quadratic_bezier = QuadraticBezier(trans_end, trans_new_end, trans_control1) - - self.last_control = Vector(control1_x, control1_y) - self.current_point = Vector(x, y) - - return quadratic_bezier - - def relative_quadratic_bazier(dx1, dy1, dx, dy): - return absolute_quadratic_bazier(self.current_point.x + dx1, self.current_point.y + dy1, - self.current_point.x + dx, self.current_point.y + dy) - - def absolute_quadratic_bazier_extension(x, y): - start = self.current_point - end = Vector(x, y) - - if self.last_control: - control = 2 * start - self.last_control - bazier = absolute_quadratic_bazier(*control, *end) - else: - bazier = absolute_quadratic_bazier(*start, *end) - - self.current_point = end - return bazier - - def relative_quadratic_bazier_extension(dx, dy): - return absolute_quadratic_bazier_extension(self.current_point.x + dx, self.current_point.y + dy) - - # Generate EllipticalArc with center notation from svg endpoint notation. - # Based on w3.org implementation notes. https://www.w3.org/TR/SVG2/implnote.html - # Todo transformations aren't applied correctly to elliptical arcs - def absolute_arc(rx, ry, deg_from_horizontal, large_arc_flag, sweep_flag, x, y): - end = Vector(x, y) - start = self.current_point - - radii = Vector(rx, ry) - - rotation_rad = math.radians(deg_from_horizontal) - - if abs(start-end) == 0: - raise ValueError("start and end points can't be equal") - - radii, center, start_angle, sweep_angle = formulas.endpoint_to_center_parameterization( - start, end, radii, rotation_rad, large_arc_flag, sweep_flag) - - arc = EllipticalArc(center, radii, rotation_rad, start_angle, sweep_angle, transformation=self.transformation) - - self.current_point = end - return arc - - def relative_arc(rx, ry, deg_from_horizontal, large_arc_flag, sweep_flag, dx, dy): - return absolute_arc(rx, ry, deg_from_horizontal, large_arc_flag, sweep_flag, self.current_point.x + dx, self.current_point.y + dy) - - command_methods = { - # Only move end point - 'M': absolute_move, - 'm': relative_move, - - # Draw straight line - 'L': absolute_line, - 'l': relative_line, - 'H': absolute_horizontal_line, - 'h': relative_horizontal_line, - 'V': absolute_vertical_line, - 'v': relative_vertical_line, - 'Z': close_path, - 'z': close_path, - - # Draw bazier curves - 'C': absolute_cubic_bazier, - 'c': relative_cubic_bazier, - 'S': absolute_cubic_bezier_extension, - 's': relative_cubic_bazier_extension, - 'Q': absolute_quadratic_bazier, - 'q': relative_quadratic_bazier, - 'T': absolute_quadratic_bazier_extension, - 't': relative_quadratic_bazier_extension, - - # Draw elliptical arcs - 'A': absolute_arc, - 'a': relative_arc - } - - try: - curve = command_methods[command_key](*command_arguments) - except TypeError as type_error: - warnings.warn(f"Mis-formed input. Skipping command {command_key, command_arguments} because it caused the " - f"following error: \n{type_error}") - except ValueError as value_error: - warnings.warn(f"Impossible geometry. Skipping curve {command_key, command_arguments} because it caused the " - f"following value error:\n{value_error}") - else: - if curve is not None: - self.curves.append(curve) - - if verbose: - print(f"{command_key}{tuple(command_arguments)} -> {curve}") diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/_transformation.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/_transformation.py deleted file mode 100644 index c345edd..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/_transformation.py +++ /dev/null @@ -1,155 +0,0 @@ -import math -from copy import deepcopy - -from svg_to_gcode.geometry import Vector, Matrix, IdentityMatrix - - -class Transformation: - """ - The Transformation class handles the parsing and computation behind svg transform attributes. - """ - __slots__ = "translation_matrix", "transformation_record", "command_methods" - - def __init__(self): - # Fancy matrix used for affine transformations (translations and linear transformations) - self.translation_matrix = IdentityMatrix(4) - - self.transformation_record = [] - - self.command_methods = { - "matrix": self.add_matrix, - "translate": self.add_translation, - "scale": self.add_scale, - "rotate": self.add_rotation, - "skewX": self.add_skew_x, - "skewY": self.add_skew_y - } - - def __repr__(self): - transformations = ", ".join( - [f"{transformation[0]}("f"{', '.join(map(lambda x: str(x), self.transformation_record[0][1]))})" - for transformation in self.transformation_record]) - return f"Transformation({transformations})" - - def __deepcopy__(self, memodict={}): - copy = Transformation() - copy.translation_matrix = deepcopy(self.translation_matrix) - - return copy - - def add_transform(self, transform_string: str): - transformations = transform_string.split(')') - - for transformation in transformations: - transformation = transformation.strip() - if not transformation or '(' not in transformation: - continue - - command, arguments = transformation.split('(') - command = command.replace(',', '') - command = command.strip() - arguments = [float(argument.strip()) for argument in arguments.replace(',', ' ').split()] - - command_method = self.command_methods[command] - - command_method(*arguments) - - # SVG transforms are equivalent to CSS transforms https://www.w3.org/TR/css-transforms-1/#MatrixDefined - def add_matrix(self, a, b, c, d, e, f): - self.transformation_record.append(("matrix", [a, b, c, d, e, f])) - matrix = Matrix([ - [a, c, 0, e], - [b, d, 0, f], - [0, 0, 1, 0], - [0, 0, 0, 1] - ]) - - self.translation_matrix *= matrix - - def add_translation(self, x: float, y=0.0): - self.transformation_record.append(("translate", [x, y])) - translation_matrix = Matrix([ - [1, 0, 0, x], - [0, 1, 0, y], - [0, 0, 1, 0], - [0, 0, 0, 1] - ]) - - self.translation_matrix *= translation_matrix - - def add_scale(self, factor: float, factor_y=None): - factor_x = factor - factor_y = factor if factor_y is None else factor_y - - self.transformation_record.append(("scale", [factor_x, factor_y])) - - scale_matrix = Matrix([ - [factor_x, 0, 0, 0], - [0, factor_y, 0, 0], - [0, 0, 1, 0], - [0, 0, 0, 1] - ]) - - self.translation_matrix *= scale_matrix - - def add_rotation(self, angle: float): - self.transformation_record.append(("rotate", [angle])) - - angle = math.radians(angle) - rotation_matrix = Matrix([ - [math.cos(angle), -math.sin(angle), 0, 0], - [math.sin(angle), math.cos(angle), 0, 0], - [0, 0, 1, 0], - [0, 0, 0, 1] - ]) - - self.translation_matrix *= rotation_matrix - - def add_skew_x(self, angle): - self.transformation_record.append(("skewX", [angle])) - - angle = math.radians(angle) - skew_matrix = IdentityMatrix(4) - skew_matrix.matrix_list[0][1] = math.tan(angle) - - self.translation_matrix *= skew_matrix - - def add_skew_y(self, angle): - self.transformation_record.append(("skewY", [angle])) - - angle = math.radians(angle) - skew_matrix = IdentityMatrix(4) - skew_matrix.matrix_list[1][0] = math.tan(angle) - - self.translation_matrix *= skew_matrix - - def extend(self, other: "Transformation"): - self.translation_matrix *= other.translation_matrix - self.transformation_record.extend(other.transformation_record) - - def apply_affine_transformation(self, vector: Vector) -> Vector: - """ - Apply the full affine transformation (linear + translation) to a vector. Generally used to transform points. - Eg the center of an ellipse. - """ - vector_4d = Matrix([[vector.x], [vector.y], [1], [1]]) - vector_4d = self.translation_matrix * vector_4d - - return Vector(vector_4d.matrix_list[0][0], vector_4d.matrix_list[1][0]) - - def apply_linear_transformation(self, vector: Vector) -> Vector: - """ - Apply the linear component of the affine transformation (no translation) to a vector. - Generally used to transform vector properties. Eg the radii of an ellipse. - """ - a = self.translation_matrix[0][0] - b = self.translation_matrix[1][0] - c = self.translation_matrix[0][1] - d = self.translation_matrix[1][1] - - linear_transformation = Matrix([ - [a, c], - [b, d] - ]) - - return linear_transformation * vector diff --git a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/debug_methods.py b/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/debug_methods.py deleted file mode 100644 index f0d685a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/svg_to_gcode/svg_parser/debug_methods.py +++ /dev/null @@ -1,67 +0,0 @@ -from xml.etree import ElementTree - -from svg_to_gcode.geometry import LineSegmentChain, Vector - - -svg_namespace = 'http://www.w3.org/2000/svg' - - -def to_svg_path(line_segment_chain: LineSegmentChain, transformation=None, color="black", opacity="1", - stroke_width="0.864583px", draw_arrows=False, arrow_id="arrow-346") -> ElementTree.Element: - """ - A handy debugging function which converts the current line-chain to svg form - - :param line_segment_chain: The LineSegmentChain to the converted. - :param transformation: A transformation to apply to every line before converting it. - :param color: The path's color. - :param opacity: The path's opacity. - :param stroke_width: The path's stroke width. - :param draw_arrows: Whether or not to draw arrows at the end of each segment. Requires placing the output of - arrow_defs() in the document. - :param arrow_id: The id of the arrow def. default = arrow-346 - """ - - start = Vector(line_segment_chain.get(0).start.x, line_segment_chain.get(0).start.y) - if transformation: - start = transformation.apply_affine_transformation(start) - - d = f"M{start.x} {start.y}" - - for line in line_segment_chain: - end = Vector(line.end.x, line.end.y) - if transformation: - end = transformation.apply_affine_transformation(end) - d += f" L {end.x} {end.y}" - - style = f"fill:none;stroke:{color};stroke-opacity:{opacity};stroke-width:{stroke_width};stroke-linecap:butt;stroke-linejoin:miter;" - - path = ElementTree.Element("{%s}path" % svg_namespace) - path.set("d", d) - path.set("style", style) - if draw_arrows: - path.set("marker-mid", f"url(#{arrow_id})") - - return path - - -def arrow_defs(arrow_scale=1, arrow_id="arrow-346"): - defs = ElementTree.Element("{%s}defs" % svg_namespace) - - marker = ElementTree.Element("{%s}marker" % svg_namespace) - marker.set("id", arrow_id) - marker.set("viewBox", "0 0 10 10") - marker.set("refX", "5") - marker.set("refY", "5") - marker.set("markerWidth", str(arrow_scale)) - marker.set("markerHeight", str(arrow_scale)) - marker.set("orient", "auto-start-reverse") - defs.append(marker) - - arrow = ElementTree.Element("{%s}path" % svg_namespace) - arrow.set("d", "M 5 0 l 10 5 l -10 5 z") - arrow.set("fill", "yellow") - arrow.set("stroke", "black") - arrow.set("stroke-width", "0.1") - marker.append(arrow) - - return defs diff --git a/plotter-app/venv/lib/python3.8/site-packages/tests/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/tests/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/tests/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index ce3f5ea..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/tests/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/tests/__pycache__/test_HersheyFonts.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/tests/__pycache__/test_HersheyFonts.cpython-38.pyc deleted file mode 100644 index 5603fb9..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/tests/__pycache__/test_HersheyFonts.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/tests/test_HersheyFonts.py b/plotter-app/venv/lib/python3.8/site-packages/tests/test_HersheyFonts.py deleted file mode 100644 index cffc1ee..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/tests/test_HersheyFonts.py +++ /dev/null @@ -1,87 +0,0 @@ -import binascii -import tarfile -from io import BytesIO -from unittest import TestCase - -from HersheyFonts import HersheyFonts - - -class TestHersheyFonts(TestCase): - def setUp(self) -> None: - self.font = HersheyFonts() - - def get_compressed_data(self): - return self.font._HersheyFonts__get_compressed_font_bytes() - - -class TestDefaultFonts(TestHersheyFonts): - def test_return_font_name(self): - def_load_font_name = self.font.load_default_font() - self.assertIsNotNone(def_load_font_name, msg='load_default_font did not return loaded font name') - - def test_invalid_font_raises_exception(self): - nonexisting_font_name = '|*|' * 1000 - with self.assertRaises(ValueError, msg='Loading invalid default font did not raise proper exception'): - self.font.load_default_font(nonexisting_font_name) - - def test_is_default_font_first(self): - def_font_name = self.font.default_font_names[0] - def_load_font_name = self.font.load_default_font() - self.assertEqual(def_font_name, def_load_font_name, msg='Default font is not reported as first font') - - def test_load_all(self): - all_font_names = self.font.default_font_names - failed_fonts = {} - for font_name in all_font_names: - try: - self.assertEqual(font_name, self.font.load_default_font(font_name), msg='Did not load the requested font') - except Exception as e: - failed_fonts[font_name] = e - self.assertFalse(failed_fonts, msg=f'Failed loading font{["", "s in list"][len(failed_fonts) > 1]}') - - def test_coding(self): - compressed = self.get_compressed_data() - self.assertIsInstance(compressed, bytes, msg='Decoded stream should be binary string') - - def test_invalid_font_resource(self): - font_resource_data_name = None - for enc in ('64', '85', '32', '16'): - font_resource_data_name_test = '_HersheyFonts__compressed_fonts_base' + enc - if hasattr(self.font, font_resource_data_name_test): - font_resource_data_name = font_resource_data_name_test - break - self.assertIsNotNone(font_resource_data_name, msg='Couldn\'t find font data resource in class') - original_data = getattr(self.font, font_resource_data_name) - - # Ruin data - ruined_data = [] - setattr(self.font, font_resource_data_name, bytes(ruined_data)) - self.test_coding() - with self.assertRaises(tarfile.ReadError, msg='tarfile corruption handled incorrectly'): - self.test_compression() - - # Ruin encoding - ruined_data = list(original_data) - ruined_data[0] = 254 - setattr(self.font, font_resource_data_name, bytes(ruined_data)) - with self.assertRaises(binascii.Error, msg='font resource encoding handeled incorrectly'): - self.test_coding() - with self.assertRaises((binascii.Error, tarfile.ReadError), msg='tarfile corruption handled incorrectly'): - self.test_compression() - - # Ruin compression - ruined_data = list(original_data) - ruined_data[0] = 0x32 if original_data[0] == 0x33 else 0x33 - setattr(self.font, font_resource_data_name, bytes(ruined_data)) - self.test_coding() - with self.assertRaises(tarfile.ReadError, msg='tarfile corruption handled incorrectly'): - self.test_compression() - - def test_compression(self): - with BytesIO(self.get_compressed_data()) as compressed_file_stream: - with tarfile.open(fileobj=compressed_file_stream, mode='r', ) as ftar: - for tar_member in ftar.getmembers(): - self.assertIsNotNone(tar_member, 'tar_member does not exist') - font_file = ftar.extractfile(tar_member) - data = font_file.read() - self.assertGreater(len(data), 0, 'Empty font file wasting space in module') diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/PKG-INFO b/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/PKG-INFO deleted file mode 100644 index 9efc2c8..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/PKG-INFO +++ /dev/null @@ -1,19 +0,0 @@ -Metadata-Version: 1.1 -Name: websocket -Version: 0.2.1 -Summary: Websocket implementation for gevent -Home-page: http://pypi.python.org/pypi/websocket -Author: UNKNOWN -Author-email: UNKNOWN -License: UNKNOWN -Description: UNKNOWN -Platform: UNKNOWN -Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python -Classifier: Operating System :: MacOS :: MacOS X -Classifier: Operating System :: POSIX -Classifier: Operating System :: Microsoft :: Windows -Classifier: Topic :: Internet -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Intended Audience :: Developers -Classifier: Development Status :: 4 - Beta diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/SOURCES.txt b/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/SOURCES.txt deleted file mode 100644 index 5402f31..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/SOURCES.txt +++ /dev/null @@ -1,22 +0,0 @@ -LICENSE -MANIFEST.in -flash.py -setup.cfg -setup.py -examples/chat.html -examples/chat.py -examples/follower.html -examples/follower.py -examples/json.js -websocket/__init__.py -websocket/policyserver.py -websocket/server.py -websocket.egg-info/PKG-INFO -websocket.egg-info/SOURCES.txt -websocket.egg-info/dependency_links.txt -websocket.egg-info/not-zip-safe -websocket.egg-info/requires.txt -websocket.egg-info/top_level.txt -websocket/data/WebSocketMain.swf -websocket/data/__init__.py -websocket/data/flashsocket.js \ No newline at end of file diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/dependency_links.txt b/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/installed-files.txt b/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/installed-files.txt deleted file mode 100644 index 4383a4f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/installed-files.txt +++ /dev/null @@ -1,16 +0,0 @@ -../websocket/__init__.py -../websocket/__pycache__/__init__.cpython-38.pyc -../websocket/__pycache__/policyserver.cpython-38.pyc -../websocket/__pycache__/server.cpython-38.pyc -../websocket/data/WebSocketMain.swf -../websocket/data/__init__.py -../websocket/data/__pycache__/__init__.cpython-38.pyc -../websocket/data/flashsocket.js -../websocket/policyserver.py -../websocket/server.py -PKG-INFO -SOURCES.txt -dependency_links.txt -not-zip-safe -requires.txt -top_level.txt diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/not-zip-safe b/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/not-zip-safe deleted file mode 100644 index 8b13789..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/not-zip-safe +++ /dev/null @@ -1 +0,0 @@ - diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/requires.txt b/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/requires.txt deleted file mode 100644 index 6cebbc5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/requires.txt +++ /dev/null @@ -1,2 +0,0 @@ -gevent -greenlet diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/top_level.txt b/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/top_level.txt deleted file mode 100644 index ca4cb0c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket-0.2.1.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -websocket diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/__init__.py deleted file mode 100644 index 559b38a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -__init__.py -websocket - WebSocket client library for Python - -Copyright 2024 engn33r - -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 ._abnf import * -from ._app import WebSocketApp as WebSocketApp, setReconnect as setReconnect -from ._core import * -from ._exceptions import * -from ._logging import * -from ._socket import * - -__version__ = "1.8.0" diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 6442648..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_abnf.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_abnf.cpython-38.pyc deleted file mode 100644 index d0c582d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_abnf.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_app.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_app.cpython-38.pyc deleted file mode 100644 index de527e8..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_app.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_cookiejar.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_cookiejar.cpython-38.pyc deleted file mode 100644 index 63b24fa..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_cookiejar.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_core.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_core.cpython-38.pyc deleted file mode 100644 index 73c8d55..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_core.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_exceptions.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_exceptions.cpython-38.pyc deleted file mode 100644 index 8a36d17..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_exceptions.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_handshake.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_handshake.cpython-38.pyc deleted file mode 100644 index a7087b4..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_handshake.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_http.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_http.cpython-38.pyc deleted file mode 100644 index 79905b3..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_http.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_logging.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_logging.cpython-38.pyc deleted file mode 100644 index 25c0bb1..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_logging.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_socket.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_socket.cpython-38.pyc deleted file mode 100644 index bc070f8..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_socket.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_ssl_compat.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_ssl_compat.cpython-38.pyc deleted file mode 100644 index 4de684f..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_ssl_compat.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_url.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_url.cpython-38.pyc deleted file mode 100644 index b3a2a28..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_url.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_utils.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_utils.cpython-38.pyc deleted file mode 100644 index af12e54..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_utils.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_wsdump.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_wsdump.cpython-38.pyc deleted file mode 100644 index f62a2d8..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/__pycache__/_wsdump.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/_abnf.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/_abnf.py deleted file mode 100644 index d7754e0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/_abnf.py +++ /dev/null @@ -1,453 +0,0 @@ -import array -import os -import struct -import sys -from threading import Lock -from typing import Callable, Optional, Union - -from ._exceptions import WebSocketPayloadException, WebSocketProtocolException -from ._utils import validate_utf8 - -""" -_abnf.py -websocket - WebSocket client library for Python - -Copyright 2024 engn33r - -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. -""" - -try: - # If wsaccel is available, use compiled routines to mask data. - # wsaccel only provides around a 10% speed boost compared - # to the websocket-client _mask() implementation. - # Note that wsaccel is unmaintained. - from wsaccel.xormask import XorMaskerSimple - - def _mask(mask_value: array.array, data_value: array.array) -> bytes: - mask_result: bytes = XorMaskerSimple(mask_value).process(data_value) - return mask_result - -except ImportError: - # wsaccel is not available, use websocket-client _mask() - native_byteorder = sys.byteorder - - def _mask(mask_value: array.array, data_value: array.array) -> bytes: - datalen = len(data_value) - int_data_value = int.from_bytes(data_value, native_byteorder) - int_mask_value = int.from_bytes( - mask_value * (datalen // 4) + mask_value[: datalen % 4], native_byteorder - ) - return (int_data_value ^ int_mask_value).to_bytes(datalen, native_byteorder) - - -__all__ = [ - "ABNF", - "continuous_frame", - "frame_buffer", - "STATUS_NORMAL", - "STATUS_GOING_AWAY", - "STATUS_PROTOCOL_ERROR", - "STATUS_UNSUPPORTED_DATA_TYPE", - "STATUS_STATUS_NOT_AVAILABLE", - "STATUS_ABNORMAL_CLOSED", - "STATUS_INVALID_PAYLOAD", - "STATUS_POLICY_VIOLATION", - "STATUS_MESSAGE_TOO_BIG", - "STATUS_INVALID_EXTENSION", - "STATUS_UNEXPECTED_CONDITION", - "STATUS_BAD_GATEWAY", - "STATUS_TLS_HANDSHAKE_ERROR", -] - -# closing frame status codes. -STATUS_NORMAL = 1000 -STATUS_GOING_AWAY = 1001 -STATUS_PROTOCOL_ERROR = 1002 -STATUS_UNSUPPORTED_DATA_TYPE = 1003 -STATUS_STATUS_NOT_AVAILABLE = 1005 -STATUS_ABNORMAL_CLOSED = 1006 -STATUS_INVALID_PAYLOAD = 1007 -STATUS_POLICY_VIOLATION = 1008 -STATUS_MESSAGE_TOO_BIG = 1009 -STATUS_INVALID_EXTENSION = 1010 -STATUS_UNEXPECTED_CONDITION = 1011 -STATUS_SERVICE_RESTART = 1012 -STATUS_TRY_AGAIN_LATER = 1013 -STATUS_BAD_GATEWAY = 1014 -STATUS_TLS_HANDSHAKE_ERROR = 1015 - -VALID_CLOSE_STATUS = ( - STATUS_NORMAL, - STATUS_GOING_AWAY, - STATUS_PROTOCOL_ERROR, - STATUS_UNSUPPORTED_DATA_TYPE, - STATUS_INVALID_PAYLOAD, - STATUS_POLICY_VIOLATION, - STATUS_MESSAGE_TOO_BIG, - STATUS_INVALID_EXTENSION, - STATUS_UNEXPECTED_CONDITION, - STATUS_SERVICE_RESTART, - STATUS_TRY_AGAIN_LATER, - STATUS_BAD_GATEWAY, -) - - -class ABNF: - """ - ABNF frame class. - See http://tools.ietf.org/html/rfc5234 - and http://tools.ietf.org/html/rfc6455#section-5.2 - """ - - # operation code values. - OPCODE_CONT = 0x0 - OPCODE_TEXT = 0x1 - OPCODE_BINARY = 0x2 - OPCODE_CLOSE = 0x8 - OPCODE_PING = 0x9 - OPCODE_PONG = 0xA - - # available operation code value tuple - OPCODES = ( - OPCODE_CONT, - OPCODE_TEXT, - OPCODE_BINARY, - OPCODE_CLOSE, - OPCODE_PING, - OPCODE_PONG, - ) - - # opcode human readable string - OPCODE_MAP = { - OPCODE_CONT: "cont", - OPCODE_TEXT: "text", - OPCODE_BINARY: "binary", - OPCODE_CLOSE: "close", - OPCODE_PING: "ping", - OPCODE_PONG: "pong", - } - - # data length threshold. - LENGTH_7 = 0x7E - LENGTH_16 = 1 << 16 - LENGTH_63 = 1 << 63 - - def __init__( - self, - fin: int = 0, - rsv1: int = 0, - rsv2: int = 0, - rsv3: int = 0, - opcode: int = OPCODE_TEXT, - mask_value: int = 1, - data: Union[str, bytes, None] = "", - ) -> None: - """ - Constructor for ABNF. Please check RFC for arguments. - """ - self.fin = fin - self.rsv1 = rsv1 - self.rsv2 = rsv2 - self.rsv3 = rsv3 - self.opcode = opcode - self.mask_value = mask_value - if data is None: - data = "" - self.data = data - self.get_mask_key = os.urandom - - def validate(self, skip_utf8_validation: bool = False) -> None: - """ - Validate the ABNF frame. - - Parameters - ---------- - skip_utf8_validation: skip utf8 validation. - """ - if self.rsv1 or self.rsv2 or self.rsv3: - raise WebSocketProtocolException("rsv is not implemented, yet") - - if self.opcode not in ABNF.OPCODES: - raise WebSocketProtocolException("Invalid opcode %r", self.opcode) - - if self.opcode == ABNF.OPCODE_PING and not self.fin: - raise WebSocketProtocolException("Invalid ping frame.") - - if self.opcode == ABNF.OPCODE_CLOSE: - l = len(self.data) - if not l: - return - if l == 1 or l >= 126: - raise WebSocketProtocolException("Invalid close frame.") - if l > 2 and not skip_utf8_validation and not validate_utf8(self.data[2:]): - raise WebSocketProtocolException("Invalid close frame.") - - code = 256 * int(self.data[0]) + int(self.data[1]) - if not self._is_valid_close_status(code): - raise WebSocketProtocolException("Invalid close opcode %r", code) - - @staticmethod - def _is_valid_close_status(code: int) -> bool: - return code in VALID_CLOSE_STATUS or (3000 <= code < 5000) - - def __str__(self) -> str: - return f"fin={self.fin} opcode={self.opcode} data={self.data}" - - @staticmethod - def create_frame(data: Union[bytes, str], opcode: int, fin: int = 1) -> "ABNF": - """ - Create frame to send text, binary and other data. - - Parameters - ---------- - data: str - data to send. This is string value(byte array). - If opcode is OPCODE_TEXT and this value is unicode, - data value is converted into unicode string, automatically. - opcode: int - operation code. please see OPCODE_MAP. - fin: int - fin flag. if set to 0, create continue fragmentation. - """ - if opcode == ABNF.OPCODE_TEXT and isinstance(data, str): - data = data.encode("utf-8") - # mask must be set if send data from client - return ABNF(fin, 0, 0, 0, opcode, 1, data) - - def format(self) -> bytes: - """ - Format this object to string(byte array) to send data to server. - """ - if any(x not in (0, 1) for x in [self.fin, self.rsv1, self.rsv2, self.rsv3]): - raise ValueError("not 0 or 1") - if self.opcode not in ABNF.OPCODES: - raise ValueError("Invalid OPCODE") - length = len(self.data) - if length >= ABNF.LENGTH_63: - raise ValueError("data is too long") - - frame_header = chr( - self.fin << 7 - | self.rsv1 << 6 - | self.rsv2 << 5 - | self.rsv3 << 4 - | self.opcode - ).encode("latin-1") - if length < ABNF.LENGTH_7: - frame_header += chr(self.mask_value << 7 | length).encode("latin-1") - elif length < ABNF.LENGTH_16: - frame_header += chr(self.mask_value << 7 | 0x7E).encode("latin-1") - frame_header += struct.pack("!H", length) - else: - frame_header += chr(self.mask_value << 7 | 0x7F).encode("latin-1") - frame_header += struct.pack("!Q", length) - - if not self.mask_value: - if isinstance(self.data, str): - self.data = self.data.encode("utf-8") - return frame_header + self.data - mask_key = self.get_mask_key(4) - return frame_header + self._get_masked(mask_key) - - def _get_masked(self, mask_key: Union[str, bytes]) -> bytes: - s = ABNF.mask(mask_key, self.data) - - if isinstance(mask_key, str): - mask_key = mask_key.encode("utf-8") - - return mask_key + s - - @staticmethod - def mask(mask_key: Union[str, bytes], data: Union[str, bytes]) -> bytes: - """ - Mask or unmask data. Just do xor for each byte - - Parameters - ---------- - mask_key: bytes or str - 4 byte mask. - data: bytes or str - data to mask/unmask. - """ - if data is None: - data = "" - - if isinstance(mask_key, str): - mask_key = mask_key.encode("latin-1") - - if isinstance(data, str): - data = data.encode("latin-1") - - return _mask(array.array("B", mask_key), array.array("B", data)) - - -class frame_buffer: - _HEADER_MASK_INDEX = 5 - _HEADER_LENGTH_INDEX = 6 - - def __init__( - self, recv_fn: Callable[[int], int], skip_utf8_validation: bool - ) -> None: - self.recv = recv_fn - self.skip_utf8_validation = skip_utf8_validation - # Buffers over the packets from the layer beneath until desired amount - # bytes of bytes are received. - self.recv_buffer: list = [] - self.clear() - self.lock = Lock() - - def clear(self) -> None: - self.header: Optional[tuple] = None - self.length: Optional[int] = None - self.mask_value: Union[bytes, str, None] = None - - def has_received_header(self) -> bool: - return self.header is None - - def recv_header(self) -> None: - header = self.recv_strict(2) - b1 = header[0] - fin = b1 >> 7 & 1 - rsv1 = b1 >> 6 & 1 - rsv2 = b1 >> 5 & 1 - rsv3 = b1 >> 4 & 1 - opcode = b1 & 0xF - b2 = header[1] - has_mask = b2 >> 7 & 1 - length_bits = b2 & 0x7F - - self.header = (fin, rsv1, rsv2, rsv3, opcode, has_mask, length_bits) - - def has_mask(self) -> Union[bool, int]: - if not self.header: - return False - header_val: int = self.header[frame_buffer._HEADER_MASK_INDEX] - return header_val - - def has_received_length(self) -> bool: - return self.length is None - - def recv_length(self) -> None: - bits = self.header[frame_buffer._HEADER_LENGTH_INDEX] - length_bits = bits & 0x7F - if length_bits == 0x7E: - v = self.recv_strict(2) - self.length = struct.unpack("!H", v)[0] - elif length_bits == 0x7F: - v = self.recv_strict(8) - self.length = struct.unpack("!Q", v)[0] - else: - self.length = length_bits - - def has_received_mask(self) -> bool: - return self.mask_value is None - - def recv_mask(self) -> None: - self.mask_value = self.recv_strict(4) if self.has_mask() else "" - - def recv_frame(self) -> ABNF: - with self.lock: - # Header - if self.has_received_header(): - self.recv_header() - (fin, rsv1, rsv2, rsv3, opcode, has_mask, _) = self.header - - # Frame length - if self.has_received_length(): - self.recv_length() - length = self.length - - # Mask - if self.has_received_mask(): - self.recv_mask() - mask_value = self.mask_value - - # Payload - payload = self.recv_strict(length) - if has_mask: - payload = ABNF.mask(mask_value, payload) - - # Reset for next frame - self.clear() - - frame = ABNF(fin, rsv1, rsv2, rsv3, opcode, has_mask, payload) - frame.validate(self.skip_utf8_validation) - - return frame - - def recv_strict(self, bufsize: int) -> bytes: - shortage = bufsize - sum(map(len, self.recv_buffer)) - while shortage > 0: - # Limit buffer size that we pass to socket.recv() to avoid - # fragmenting the heap -- the number of bytes recv() actually - # reads is limited by socket buffer and is relatively small, - # yet passing large numbers repeatedly causes lots of large - # buffers allocated and then shrunk, which results in - # fragmentation. - bytes_ = self.recv(min(16384, shortage)) - self.recv_buffer.append(bytes_) - shortage -= len(bytes_) - - unified = b"".join(self.recv_buffer) - - if shortage == 0: - self.recv_buffer = [] - return unified - else: - self.recv_buffer = [unified[bufsize:]] - return unified[:bufsize] - - -class continuous_frame: - def __init__(self, fire_cont_frame: bool, skip_utf8_validation: bool) -> None: - self.fire_cont_frame = fire_cont_frame - self.skip_utf8_validation = skip_utf8_validation - self.cont_data: Optional[list] = None - self.recving_frames: Optional[int] = None - - def validate(self, frame: ABNF) -> None: - if not self.recving_frames and frame.opcode == ABNF.OPCODE_CONT: - raise WebSocketProtocolException("Illegal frame") - if self.recving_frames and frame.opcode in ( - ABNF.OPCODE_TEXT, - ABNF.OPCODE_BINARY, - ): - raise WebSocketProtocolException("Illegal frame") - - def add(self, frame: ABNF) -> None: - if self.cont_data: - self.cont_data[1] += frame.data - else: - if frame.opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY): - self.recving_frames = frame.opcode - self.cont_data = [frame.opcode, frame.data] - - if frame.fin: - self.recving_frames = None - - def is_fire(self, frame: ABNF) -> Union[bool, int]: - return frame.fin or self.fire_cont_frame - - def extract(self, frame: ABNF) -> tuple: - data = self.cont_data - self.cont_data = None - frame.data = data[1] - if ( - not self.fire_cont_frame - and data[0] == ABNF.OPCODE_TEXT - and not self.skip_utf8_validation - and not validate_utf8(frame.data) - ): - raise WebSocketPayloadException(f"cannot decode: {repr(frame.data)}") - return data[0], frame diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/_app.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/_app.py deleted file mode 100644 index 9fee765..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/_app.py +++ /dev/null @@ -1,677 +0,0 @@ -import inspect -import selectors -import socket -import threading -import time -from typing import Any, Callable, Optional, Union - -from . import _logging -from ._abnf import ABNF -from ._core import WebSocket, getdefaulttimeout -from ._exceptions import ( - WebSocketConnectionClosedException, - WebSocketException, - WebSocketTimeoutException, -) -from ._ssl_compat import SSLEOFError -from ._url import parse_url - -""" -_app.py -websocket - WebSocket client library for Python - -Copyright 2024 engn33r - -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. -""" - -__all__ = ["WebSocketApp"] - -RECONNECT = 0 - - -def setReconnect(reconnectInterval: int) -> None: - global RECONNECT - RECONNECT = reconnectInterval - - -class DispatcherBase: - """ - DispatcherBase - """ - - def __init__(self, app: Any, ping_timeout: Union[float, int, None]) -> None: - self.app = app - self.ping_timeout = ping_timeout - - def timeout(self, seconds: Union[float, int, None], callback: Callable) -> None: - time.sleep(seconds) - callback() - - def reconnect(self, seconds: int, reconnector: Callable) -> None: - try: - _logging.info( - f"reconnect() - retrying in {seconds} seconds [{len(inspect.stack())} frames in stack]" - ) - time.sleep(seconds) - reconnector(reconnecting=True) - except KeyboardInterrupt as e: - _logging.info(f"User exited {e}") - raise e - - -class Dispatcher(DispatcherBase): - """ - Dispatcher - """ - - def read( - self, - sock: socket.socket, - read_callback: Callable, - check_callback: Callable, - ) -> None: - sel = selectors.DefaultSelector() - sel.register(self.app.sock.sock, selectors.EVENT_READ) - try: - while self.app.keep_running: - if sel.select(self.ping_timeout): - if not read_callback(): - break - check_callback() - finally: - sel.close() - - -class SSLDispatcher(DispatcherBase): - """ - SSLDispatcher - """ - - def read( - self, - sock: socket.socket, - read_callback: Callable, - check_callback: Callable, - ) -> None: - sock = self.app.sock.sock - sel = selectors.DefaultSelector() - sel.register(sock, selectors.EVENT_READ) - try: - while self.app.keep_running: - if self.select(sock, sel): - if not read_callback(): - break - check_callback() - finally: - sel.close() - - def select(self, sock, sel: selectors.DefaultSelector): - sock = self.app.sock.sock - if sock.pending(): - return [ - sock, - ] - - r = sel.select(self.ping_timeout) - - if len(r) > 0: - return r[0][0] - - -class WrappedDispatcher: - """ - WrappedDispatcher - """ - - def __init__(self, app, ping_timeout: Union[float, int, None], dispatcher) -> None: - self.app = app - self.ping_timeout = ping_timeout - self.dispatcher = dispatcher - dispatcher.signal(2, dispatcher.abort) # keyboard interrupt - - def read( - self, - sock: socket.socket, - read_callback: Callable, - check_callback: Callable, - ) -> None: - self.dispatcher.read(sock, read_callback) - self.ping_timeout and self.timeout(self.ping_timeout, check_callback) - - def timeout(self, seconds: float, callback: Callable) -> None: - self.dispatcher.timeout(seconds, callback) - - def reconnect(self, seconds: int, reconnector: Callable) -> None: - self.timeout(seconds, reconnector) - - -class WebSocketApp: - """ - Higher level of APIs are provided. The interface is like JavaScript WebSocket object. - """ - - def __init__( - self, - url: str, - header: Union[list, dict, Callable, None] = None, - on_open: Optional[Callable[[WebSocket], None]] = None, - on_reconnect: Optional[Callable[[WebSocket], None]] = None, - on_message: Optional[Callable[[WebSocket, Any], None]] = None, - on_error: Optional[Callable[[WebSocket, Any], None]] = None, - on_close: Optional[Callable[[WebSocket, Any, Any], None]] = None, - on_ping: Optional[Callable] = None, - on_pong: Optional[Callable] = None, - on_cont_message: Optional[Callable] = None, - keep_running: bool = True, - get_mask_key: Optional[Callable] = None, - cookie: Optional[str] = None, - subprotocols: Optional[list] = None, - on_data: Optional[Callable] = None, - socket: Optional[socket.socket] = None, - ) -> None: - """ - WebSocketApp initialization - - Parameters - ---------- - url: str - Websocket url. - header: list or dict or Callable - Custom header for websocket handshake. - If the parameter is a callable object, it is called just before the connection attempt. - The returned dict or list is used as custom header value. - This could be useful in order to properly setup timestamp dependent headers. - on_open: function - Callback object which is called at opening websocket. - on_open has one argument. - The 1st argument is this class object. - on_reconnect: function - Callback object which is called at reconnecting websocket. - on_reconnect has one argument. - The 1st argument is this class object. - on_message: function - Callback object which is called when received data. - on_message has 2 arguments. - The 1st argument is this class object. - The 2nd argument is utf-8 data received from the server. - on_error: function - Callback object which is called when we get error. - on_error has 2 arguments. - The 1st argument is this class object. - The 2nd argument is exception object. - on_close: function - Callback object which is called when connection is closed. - on_close has 3 arguments. - The 1st argument is this class object. - The 2nd argument is close_status_code. - The 3rd argument is close_msg. - on_cont_message: function - Callback object which is called when a continuation - frame is received. - on_cont_message has 3 arguments. - The 1st argument is this class object. - The 2nd argument is utf-8 string which we get from the server. - The 3rd argument is continue flag. if 0, the data continue - to next frame data - on_data: function - Callback object which is called when a message received. - This is called before on_message or on_cont_message, - and then on_message or on_cont_message is called. - on_data has 4 argument. - The 1st argument is this class object. - The 2nd argument is utf-8 string which we get from the server. - The 3rd argument is data type. ABNF.OPCODE_TEXT or ABNF.OPCODE_BINARY will be came. - The 4th argument is continue flag. If 0, the data continue - keep_running: bool - This parameter is obsolete and ignored. - get_mask_key: function - A callable function to get new mask keys, see the - WebSocket.set_mask_key's docstring for more information. - cookie: str - Cookie value. - subprotocols: list - List of available sub protocols. Default is None. - socket: socket - Pre-initialized stream socket. - """ - self.url = url - self.header = header if header is not None else [] - self.cookie = cookie - - self.on_open = on_open - self.on_reconnect = on_reconnect - self.on_message = on_message - self.on_data = on_data - self.on_error = on_error - self.on_close = on_close - self.on_ping = on_ping - self.on_pong = on_pong - self.on_cont_message = on_cont_message - self.keep_running = False - self.get_mask_key = get_mask_key - self.sock: Optional[WebSocket] = None - self.last_ping_tm = float(0) - self.last_pong_tm = float(0) - self.ping_thread: Optional[threading.Thread] = None - self.stop_ping: Optional[threading.Event] = None - self.ping_interval = float(0) - self.ping_timeout: Union[float, int, None] = None - self.ping_payload = "" - self.subprotocols = subprotocols - self.prepared_socket = socket - self.has_errored = False - self.has_done_teardown = False - self.has_done_teardown_lock = threading.Lock() - - def send(self, data: Union[bytes, str], opcode: int = ABNF.OPCODE_TEXT) -> None: - """ - send message - - Parameters - ---------- - data: str - Message to send. If you set opcode to OPCODE_TEXT, - data must be utf-8 string or unicode. - opcode: int - Operation code of data. Default is OPCODE_TEXT. - """ - - if not self.sock or self.sock.send(data, opcode) == 0: - raise WebSocketConnectionClosedException("Connection is already closed.") - - def send_text(self, text_data: str) -> None: - """ - Sends UTF-8 encoded text. - """ - if not self.sock or self.sock.send(text_data, ABNF.OPCODE_TEXT) == 0: - raise WebSocketConnectionClosedException("Connection is already closed.") - - def send_bytes(self, data: Union[bytes, bytearray]) -> None: - """ - Sends a sequence of bytes. - """ - if not self.sock or self.sock.send(data, ABNF.OPCODE_BINARY) == 0: - raise WebSocketConnectionClosedException("Connection is already closed.") - - def close(self, **kwargs) -> None: - """ - Close websocket connection. - """ - self.keep_running = False - if self.sock: - self.sock.close(**kwargs) - self.sock = None - - def _start_ping_thread(self) -> None: - self.last_ping_tm = self.last_pong_tm = float(0) - self.stop_ping = threading.Event() - self.ping_thread = threading.Thread(target=self._send_ping) - self.ping_thread.daemon = True - self.ping_thread.start() - - def _stop_ping_thread(self) -> None: - if self.stop_ping: - self.stop_ping.set() - if self.ping_thread and self.ping_thread.is_alive(): - self.ping_thread.join(3) - self.last_ping_tm = self.last_pong_tm = float(0) - - def _send_ping(self) -> None: - if self.stop_ping.wait(self.ping_interval) or self.keep_running is False: - return - while not self.stop_ping.wait(self.ping_interval) and self.keep_running is True: - if self.sock: - self.last_ping_tm = time.time() - try: - _logging.debug("Sending ping") - self.sock.ping(self.ping_payload) - except Exception as e: - _logging.debug(f"Failed to send ping: {e}") - - def run_forever( - self, - sockopt: tuple = None, - sslopt: dict = None, - ping_interval: Union[float, int] = 0, - ping_timeout: Union[float, int, None] = None, - ping_payload: str = "", - http_proxy_host: str = None, - http_proxy_port: Union[int, str] = None, - http_no_proxy: list = None, - http_proxy_auth: tuple = None, - http_proxy_timeout: Optional[float] = None, - skip_utf8_validation: bool = False, - host: str = None, - origin: str = None, - dispatcher=None, - suppress_origin: bool = False, - proxy_type: str = None, - reconnect: int = None, - ) -> bool: - """ - Run event loop for WebSocket framework. - - This loop is an infinite loop and is alive while websocket is available. - - Parameters - ---------- - sockopt: tuple - Values for socket.setsockopt. - sockopt must be tuple - and each element is argument of sock.setsockopt. - sslopt: dict - Optional dict object for ssl socket option. - ping_interval: int or float - Automatically send "ping" command - every specified period (in seconds). - If set to 0, no ping is sent periodically. - ping_timeout: int or float - Timeout (in seconds) if the pong message is not received. - ping_payload: str - Payload message to send with each ping. - http_proxy_host: str - HTTP proxy host name. - http_proxy_port: int or str - HTTP proxy port. If not set, set to 80. - http_no_proxy: list - Whitelisted host names that don't use the proxy. - http_proxy_timeout: int or float - HTTP proxy timeout, default is 60 sec as per python-socks. - http_proxy_auth: tuple - HTTP proxy auth information. tuple of username and password. Default is None. - skip_utf8_validation: bool - skip utf8 validation. - host: str - update host header. - origin: str - update origin header. - dispatcher: Dispatcher object - customize reading data from socket. - suppress_origin: bool - suppress outputting origin header. - proxy_type: str - type of proxy from: http, socks4, socks4a, socks5, socks5h - reconnect: int - delay interval when reconnecting - - Returns - ------- - teardown: bool - False if the `WebSocketApp` is closed or caught KeyboardInterrupt, - True if any other exception was raised during a loop. - """ - - if reconnect is None: - reconnect = RECONNECT - - if ping_timeout is not None and ping_timeout <= 0: - raise WebSocketException("Ensure ping_timeout > 0") - if ping_interval is not None and ping_interval < 0: - raise WebSocketException("Ensure ping_interval >= 0") - if ping_timeout and ping_interval and ping_interval <= ping_timeout: - raise WebSocketException("Ensure ping_interval > ping_timeout") - if not sockopt: - sockopt = () - if not sslopt: - sslopt = {} - if self.sock: - raise WebSocketException("socket is already opened") - - self.ping_interval = ping_interval - self.ping_timeout = ping_timeout - self.ping_payload = ping_payload - self.has_done_teardown = False - self.keep_running = True - - def teardown(close_frame: ABNF = None): - """ - Tears down the connection. - - Parameters - ---------- - close_frame: ABNF frame - If close_frame is set, the on_close handler is invoked - with the statusCode and reason from the provided frame. - """ - - # teardown() is called in many code paths to ensure resources are cleaned up and on_close is fired. - # To ensure the work is only done once, we use this bool and lock. - with self.has_done_teardown_lock: - if self.has_done_teardown: - return - self.has_done_teardown = True - - self._stop_ping_thread() - self.keep_running = False - if self.sock: - self.sock.close() - close_status_code, close_reason = self._get_close_args( - close_frame if close_frame else None - ) - self.sock = None - - # Finally call the callback AFTER all teardown is complete - self._callback(self.on_close, close_status_code, close_reason) - - def setSock(reconnecting: bool = False) -> None: - if reconnecting and self.sock: - self.sock.shutdown() - - self.sock = WebSocket( - self.get_mask_key, - sockopt=sockopt, - sslopt=sslopt, - fire_cont_frame=self.on_cont_message is not None, - skip_utf8_validation=skip_utf8_validation, - enable_multithread=True, - ) - - self.sock.settimeout(getdefaulttimeout()) - try: - header = self.header() if callable(self.header) else self.header - - self.sock.connect( - self.url, - header=header, - cookie=self.cookie, - http_proxy_host=http_proxy_host, - http_proxy_port=http_proxy_port, - http_no_proxy=http_no_proxy, - http_proxy_auth=http_proxy_auth, - http_proxy_timeout=http_proxy_timeout, - subprotocols=self.subprotocols, - host=host, - origin=origin, - suppress_origin=suppress_origin, - proxy_type=proxy_type, - socket=self.prepared_socket, - ) - - _logging.info("Websocket connected") - - if self.ping_interval: - self._start_ping_thread() - - if reconnecting and self.on_reconnect: - self._callback(self.on_reconnect) - else: - self._callback(self.on_open) - - dispatcher.read(self.sock.sock, read, check) - except ( - WebSocketConnectionClosedException, - ConnectionRefusedError, - KeyboardInterrupt, - SystemExit, - Exception, - ) as e: - handleDisconnect(e, reconnecting) - - def read() -> bool: - if not self.keep_running: - return teardown() - - try: - op_code, frame = self.sock.recv_data_frame(True) - except ( - WebSocketConnectionClosedException, - KeyboardInterrupt, - SSLEOFError, - ) as e: - if custom_dispatcher: - return handleDisconnect(e, bool(reconnect)) - else: - raise e - - if op_code == ABNF.OPCODE_CLOSE: - return teardown(frame) - elif op_code == ABNF.OPCODE_PING: - self._callback(self.on_ping, frame.data) - elif op_code == ABNF.OPCODE_PONG: - self.last_pong_tm = time.time() - self._callback(self.on_pong, frame.data) - elif op_code == ABNF.OPCODE_CONT and self.on_cont_message: - self._callback(self.on_data, frame.data, frame.opcode, frame.fin) - self._callback(self.on_cont_message, frame.data, frame.fin) - else: - data = frame.data - if op_code == ABNF.OPCODE_TEXT and not skip_utf8_validation: - data = data.decode("utf-8") - self._callback(self.on_data, data, frame.opcode, True) - self._callback(self.on_message, data) - - return True - - def check() -> bool: - if self.ping_timeout: - has_timeout_expired = ( - time.time() - self.last_ping_tm > self.ping_timeout - ) - has_pong_not_arrived_after_last_ping = ( - self.last_pong_tm - self.last_ping_tm < 0 - ) - has_pong_arrived_too_late = ( - self.last_pong_tm - self.last_ping_tm > self.ping_timeout - ) - - if ( - self.last_ping_tm - and has_timeout_expired - and ( - has_pong_not_arrived_after_last_ping - or has_pong_arrived_too_late - ) - ): - raise WebSocketTimeoutException("ping/pong timed out") - return True - - def handleDisconnect( - e: Union[ - WebSocketConnectionClosedException, - ConnectionRefusedError, - KeyboardInterrupt, - SystemExit, - Exception, - ], - reconnecting: bool = False, - ) -> bool: - self.has_errored = True - self._stop_ping_thread() - if not reconnecting: - self._callback(self.on_error, e) - - if isinstance(e, (KeyboardInterrupt, SystemExit)): - teardown() - # Propagate further - raise - - if reconnect: - _logging.info(f"{e} - reconnect") - if custom_dispatcher: - _logging.debug( - f"Calling custom dispatcher reconnect [{len(inspect.stack())} frames in stack]" - ) - dispatcher.reconnect(reconnect, setSock) - else: - _logging.error(f"{e} - goodbye") - teardown() - - custom_dispatcher = bool(dispatcher) - dispatcher = self.create_dispatcher( - ping_timeout, dispatcher, parse_url(self.url)[3] - ) - - try: - setSock() - if not custom_dispatcher and reconnect: - while self.keep_running: - _logging.debug( - f"Calling dispatcher reconnect [{len(inspect.stack())} frames in stack]" - ) - dispatcher.reconnect(reconnect, setSock) - except (KeyboardInterrupt, Exception) as e: - _logging.info(f"tearing down on exception {e}") - teardown() - finally: - if not custom_dispatcher: - # Ensure teardown was called before returning from run_forever - teardown() - - return self.has_errored - - def create_dispatcher( - self, - ping_timeout: Union[float, int, None], - dispatcher: Optional[DispatcherBase] = None, - is_ssl: bool = False, - ) -> Union[Dispatcher, SSLDispatcher, WrappedDispatcher]: - if dispatcher: # If custom dispatcher is set, use WrappedDispatcher - return WrappedDispatcher(self, ping_timeout, dispatcher) - timeout = ping_timeout or 10 - if is_ssl: - return SSLDispatcher(self, timeout) - return Dispatcher(self, timeout) - - def _get_close_args(self, close_frame: ABNF) -> list: - """ - _get_close_args extracts the close code and reason from the close body - if it exists (RFC6455 says WebSocket Connection Close Code is optional) - """ - # Need to catch the case where close_frame is None - # Otherwise the following if statement causes an error - if not self.on_close or not close_frame: - return [None, None] - - # Extract close frame status code - if close_frame.data and len(close_frame.data) >= 2: - close_status_code = 256 * int(close_frame.data[0]) + int( - close_frame.data[1] - ) - reason = close_frame.data[2:] - if isinstance(reason, bytes): - reason = reason.decode("utf-8") - return [close_status_code, reason] - else: - # Most likely reached this because len(close_frame_data.data) < 2 - return [None, None] - - def _callback(self, callback, *args) -> None: - if callback: - try: - callback(self, *args) - - except Exception as e: - _logging.error(f"error from callback {callback}: {e}") - if self.on_error: - self.on_error(self, e) diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/_cookiejar.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/_cookiejar.py deleted file mode 100644 index 7480e5f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/_cookiejar.py +++ /dev/null @@ -1,75 +0,0 @@ -import http.cookies -from typing import Optional - -""" -_cookiejar.py -websocket - WebSocket client library for Python - -Copyright 2024 engn33r - -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. -""" - - -class SimpleCookieJar: - def __init__(self) -> None: - self.jar: dict = {} - - def add(self, set_cookie: Optional[str]) -> None: - if set_cookie: - simple_cookie = http.cookies.SimpleCookie(set_cookie) - - for v in simple_cookie.values(): - if domain := v.get("domain"): - if not domain.startswith("."): - domain = f".{domain}" - cookie = ( - self.jar.get(domain) - if self.jar.get(domain) - else http.cookies.SimpleCookie() - ) - cookie.update(simple_cookie) - self.jar[domain.lower()] = cookie - - def set(self, set_cookie: str) -> None: - if set_cookie: - simple_cookie = http.cookies.SimpleCookie(set_cookie) - - for v in simple_cookie.values(): - if domain := v.get("domain"): - if not domain.startswith("."): - domain = f".{domain}" - self.jar[domain.lower()] = simple_cookie - - def get(self, host: str) -> str: - if not host: - return "" - - cookies = [] - for domain, _ in self.jar.items(): - host = host.lower() - if host.endswith(domain) or host == domain[1:]: - cookies.append(self.jar.get(domain)) - - return "; ".join( - filter( - None, - sorted( - [ - f"{k}={v.value}" - for cookie in filter(None, cookies) - for k, v in cookie.items() - ] - ), - ) - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/_core.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/_core.py deleted file mode 100644 index f940ed0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/_core.py +++ /dev/null @@ -1,647 +0,0 @@ -import socket -import struct -import threading -import time -from typing import Optional, Union - -# websocket modules -from ._abnf import ABNF, STATUS_NORMAL, continuous_frame, frame_buffer -from ._exceptions import WebSocketProtocolException, WebSocketConnectionClosedException -from ._handshake import SUPPORTED_REDIRECT_STATUSES, handshake -from ._http import connect, proxy_info -from ._logging import debug, error, trace, isEnabledForError, isEnabledForTrace -from ._socket import getdefaulttimeout, recv, send, sock_opt -from ._ssl_compat import ssl -from ._utils import NoLock - -""" -_core.py -websocket - WebSocket client library for Python - -Copyright 2024 engn33r - -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. -""" - -__all__ = ["WebSocket", "create_connection"] - - -class WebSocket: - """ - Low level WebSocket interface. - - This class is based on the WebSocket protocol `draft-hixie-thewebsocketprotocol-76 `_ - - We can connect to the websocket server and send/receive data. - The following example is an echo client. - - >>> import websocket - >>> ws = websocket.WebSocket() - >>> ws.connect("ws://echo.websocket.events") - >>> ws.recv() - 'echo.websocket.events sponsored by Lob.com' - >>> ws.send("Hello, Server") - 19 - >>> ws.recv() - 'Hello, Server' - >>> ws.close() - - Parameters - ---------- - get_mask_key: func - A callable function to get new mask keys, see the - WebSocket.set_mask_key's docstring for more information. - sockopt: tuple - Values for socket.setsockopt. - sockopt must be tuple and each element is argument of sock.setsockopt. - sslopt: dict - Optional dict object for ssl socket options. See FAQ for details. - fire_cont_frame: bool - Fire recv event for each cont frame. Default is False. - enable_multithread: bool - If set to True, lock send method. - skip_utf8_validation: bool - Skip utf8 validation. - """ - - def __init__( - self, - get_mask_key=None, - sockopt=None, - sslopt=None, - fire_cont_frame: bool = False, - enable_multithread: bool = True, - skip_utf8_validation: bool = False, - **_, - ): - """ - Initialize WebSocket object. - - Parameters - ---------- - sslopt: dict - Optional dict object for ssl socket options. See FAQ for details. - """ - self.sock_opt = sock_opt(sockopt, sslopt) - self.handshake_response = None - self.sock: Optional[socket.socket] = None - - self.connected = False - self.get_mask_key = get_mask_key - # These buffer over the build-up of a single frame. - self.frame_buffer = frame_buffer(self._recv, skip_utf8_validation) - self.cont_frame = continuous_frame(fire_cont_frame, skip_utf8_validation) - - if enable_multithread: - self.lock = threading.Lock() - self.readlock = threading.Lock() - else: - self.lock = NoLock() - self.readlock = NoLock() - - def __iter__(self): - """ - Allow iteration over websocket, implying sequential `recv` executions. - """ - while True: - yield self.recv() - - def __next__(self): - return self.recv() - - def next(self): - return self.__next__() - - def fileno(self): - return self.sock.fileno() - - def set_mask_key(self, func): - """ - Set function to create mask key. You can customize mask key generator. - Mainly, this is for testing purpose. - - Parameters - ---------- - func: func - callable object. the func takes 1 argument as integer. - The argument means length of mask key. - This func must return string(byte array), - which length is argument specified. - """ - self.get_mask_key = func - - def gettimeout(self) -> Union[float, int, None]: - """ - Get the websocket timeout (in seconds) as an int or float - - Returns - ---------- - timeout: int or float - returns timeout value (in seconds). This value could be either float/integer. - """ - return self.sock_opt.timeout - - def settimeout(self, timeout: Union[float, int, None]): - """ - Set the timeout to the websocket. - - Parameters - ---------- - timeout: int or float - timeout time (in seconds). This value could be either float/integer. - """ - self.sock_opt.timeout = timeout - if self.sock: - self.sock.settimeout(timeout) - - timeout = property(gettimeout, settimeout) - - def getsubprotocol(self): - """ - Get subprotocol - """ - if self.handshake_response: - return self.handshake_response.subprotocol - else: - return None - - subprotocol = property(getsubprotocol) - - def getstatus(self): - """ - Get handshake status - """ - if self.handshake_response: - return self.handshake_response.status - else: - return None - - status = property(getstatus) - - def getheaders(self): - """ - Get handshake response header - """ - if self.handshake_response: - return self.handshake_response.headers - else: - return None - - def is_ssl(self): - try: - return isinstance(self.sock, ssl.SSLSocket) - except: - return False - - headers = property(getheaders) - - def connect(self, url, **options): - """ - Connect to url. url is websocket url scheme. - ie. ws://host:port/resource - You can customize using 'options'. - If you set "header" list object, you can set your own custom header. - - >>> ws = WebSocket() - >>> ws.connect("ws://echo.websocket.events", - ... header=["User-Agent: MyProgram", - ... "x-custom: header"]) - - Parameters - ---------- - header: list or dict - Custom http header list or dict. - cookie: str - Cookie value. - origin: str - Custom origin url. - connection: str - Custom connection header value. - Default value "Upgrade" set in _handshake.py - suppress_origin: bool - Suppress outputting origin header. - host: str - Custom host header string. - timeout: int or float - Socket timeout time. This value is an integer or float. - If you set None for this value, it means "use default_timeout value" - http_proxy_host: str - HTTP proxy host name. - http_proxy_port: str or int - HTTP proxy port. Default is 80. - http_no_proxy: list - Whitelisted host names that don't use the proxy. - http_proxy_auth: tuple - HTTP proxy auth information. Tuple of username and password. Default is None. - http_proxy_timeout: int or float - HTTP proxy timeout, default is 60 sec as per python-socks. - redirect_limit: int - Number of redirects to follow. - subprotocols: list - List of available subprotocols. Default is None. - socket: socket - Pre-initialized stream socket. - """ - self.sock_opt.timeout = options.get("timeout", self.sock_opt.timeout) - self.sock, addrs = connect( - url, self.sock_opt, proxy_info(**options), options.pop("socket", None) - ) - - try: - self.handshake_response = handshake(self.sock, url, *addrs, **options) - for _ in range(options.pop("redirect_limit", 3)): - if self.handshake_response.status in SUPPORTED_REDIRECT_STATUSES: - url = self.handshake_response.headers["location"] - self.sock.close() - self.sock, addrs = connect( - url, - self.sock_opt, - proxy_info(**options), - options.pop("socket", None), - ) - self.handshake_response = handshake( - self.sock, url, *addrs, **options - ) - self.connected = True - except: - if self.sock: - self.sock.close() - self.sock = None - raise - - def send(self, payload: Union[bytes, str], opcode: int = ABNF.OPCODE_TEXT) -> int: - """ - Send the data as string. - - Parameters - ---------- - payload: str - Payload must be utf-8 string or unicode, - If the opcode is OPCODE_TEXT. - Otherwise, it must be string(byte array). - opcode: int - Operation code (opcode) to send. - """ - - frame = ABNF.create_frame(payload, opcode) - return self.send_frame(frame) - - def send_text(self, text_data: str) -> int: - """ - Sends UTF-8 encoded text. - """ - return self.send(text_data, ABNF.OPCODE_TEXT) - - def send_bytes(self, data: Union[bytes, bytearray]) -> int: - """ - Sends a sequence of bytes. - """ - return self.send(data, ABNF.OPCODE_BINARY) - - def send_frame(self, frame) -> int: - """ - Send the data frame. - - >>> ws = create_connection("ws://echo.websocket.events") - >>> frame = ABNF.create_frame("Hello", ABNF.OPCODE_TEXT) - >>> ws.send_frame(frame) - >>> cont_frame = ABNF.create_frame("My name is ", ABNF.OPCODE_CONT, 0) - >>> ws.send_frame(frame) - >>> cont_frame = ABNF.create_frame("Foo Bar", ABNF.OPCODE_CONT, 1) - >>> ws.send_frame(frame) - - Parameters - ---------- - frame: ABNF frame - frame data created by ABNF.create_frame - """ - if self.get_mask_key: - frame.get_mask_key = self.get_mask_key - data = frame.format() - length = len(data) - if isEnabledForTrace(): - trace(f"++Sent raw: {repr(data)}") - trace(f"++Sent decoded: {frame.__str__()}") - with self.lock: - while data: - l = self._send(data) - data = data[l:] - - return length - - def send_binary(self, payload: bytes) -> int: - """ - Send a binary message (OPCODE_BINARY). - - Parameters - ---------- - payload: bytes - payload of message to send. - """ - return self.send(payload, ABNF.OPCODE_BINARY) - - def ping(self, payload: Union[str, bytes] = ""): - """ - Send ping data. - - Parameters - ---------- - payload: str - data payload to send server. - """ - if isinstance(payload, str): - payload = payload.encode("utf-8") - self.send(payload, ABNF.OPCODE_PING) - - def pong(self, payload: Union[str, bytes] = ""): - """ - Send pong data. - - Parameters - ---------- - payload: str - data payload to send server. - """ - if isinstance(payload, str): - payload = payload.encode("utf-8") - self.send(payload, ABNF.OPCODE_PONG) - - def recv(self) -> Union[str, bytes]: - """ - Receive string data(byte array) from the server. - - Returns - ---------- - data: string (byte array) value. - """ - with self.readlock: - opcode, data = self.recv_data() - if opcode == ABNF.OPCODE_TEXT: - data_received: Union[bytes, str] = data - if isinstance(data_received, bytes): - return data_received.decode("utf-8") - elif isinstance(data_received, str): - return data_received - elif opcode == ABNF.OPCODE_BINARY: - data_binary: bytes = data - return data_binary - else: - return "" - - def recv_data(self, control_frame: bool = False) -> tuple: - """ - Receive data with operation code. - - Parameters - ---------- - control_frame: bool - a boolean flag indicating whether to return control frame - data, defaults to False - - Returns - ------- - opcode, frame.data: tuple - tuple of operation code and string(byte array) value. - """ - opcode, frame = self.recv_data_frame(control_frame) - return opcode, frame.data - - def recv_data_frame(self, control_frame: bool = False) -> tuple: - """ - Receive data with operation code. - - If a valid ping message is received, a pong response is sent. - - Parameters - ---------- - control_frame: bool - a boolean flag indicating whether to return control frame - data, defaults to False - - Returns - ------- - frame.opcode, frame: tuple - tuple of operation code and string(byte array) value. - """ - while True: - frame = self.recv_frame() - if isEnabledForTrace(): - trace(f"++Rcv raw: {repr(frame.format())}") - trace(f"++Rcv decoded: {frame.__str__()}") - if not frame: - # handle error: - # 'NoneType' object has no attribute 'opcode' - raise WebSocketProtocolException(f"Not a valid frame {frame}") - elif frame.opcode in ( - ABNF.OPCODE_TEXT, - ABNF.OPCODE_BINARY, - ABNF.OPCODE_CONT, - ): - self.cont_frame.validate(frame) - self.cont_frame.add(frame) - - if self.cont_frame.is_fire(frame): - return self.cont_frame.extract(frame) - - elif frame.opcode == ABNF.OPCODE_CLOSE: - self.send_close() - return frame.opcode, frame - elif frame.opcode == ABNF.OPCODE_PING: - if len(frame.data) < 126: - self.pong(frame.data) - else: - raise WebSocketProtocolException("Ping message is too long") - if control_frame: - return frame.opcode, frame - elif frame.opcode == ABNF.OPCODE_PONG: - if control_frame: - return frame.opcode, frame - - def recv_frame(self): - """ - Receive data as frame from server. - - Returns - ------- - self.frame_buffer.recv_frame(): ABNF frame object - """ - return self.frame_buffer.recv_frame() - - def send_close(self, status: int = STATUS_NORMAL, reason: bytes = b""): - """ - Send close data to the server. - - Parameters - ---------- - status: int - Status code to send. See STATUS_XXX. - reason: str or bytes - The reason to close. This must be string or UTF-8 bytes. - """ - if status < 0 or status >= ABNF.LENGTH_16: - raise ValueError("code is invalid range") - self.connected = False - self.send(struct.pack("!H", status) + reason, ABNF.OPCODE_CLOSE) - - def close(self, status: int = STATUS_NORMAL, reason: bytes = b"", timeout: int = 3): - """ - Close Websocket object - - Parameters - ---------- - status: int - Status code to send. See VALID_CLOSE_STATUS in ABNF. - reason: bytes - The reason to close in UTF-8. - timeout: int or float - Timeout until receive a close frame. - If None, it will wait forever until receive a close frame. - """ - if not self.connected: - return - if status < 0 or status >= ABNF.LENGTH_16: - raise ValueError("code is invalid range") - - try: - self.connected = False - self.send(struct.pack("!H", status) + reason, ABNF.OPCODE_CLOSE) - sock_timeout = self.sock.gettimeout() - self.sock.settimeout(timeout) - start_time = time.time() - while timeout is None or time.time() - start_time < timeout: - try: - frame = self.recv_frame() - if frame.opcode != ABNF.OPCODE_CLOSE: - continue - if isEnabledForError(): - recv_status = struct.unpack("!H", frame.data[0:2])[0] - if recv_status >= 3000 and recv_status <= 4999: - debug(f"close status: {repr(recv_status)}") - elif recv_status != STATUS_NORMAL: - error(f"close status: {repr(recv_status)}") - break - except: - break - self.sock.settimeout(sock_timeout) - self.sock.shutdown(socket.SHUT_RDWR) - except: - pass - - self.shutdown() - - def abort(self): - """ - Low-level asynchronous abort, wakes up other threads that are waiting in recv_* - """ - if self.connected: - self.sock.shutdown(socket.SHUT_RDWR) - - def shutdown(self): - """ - close socket, immediately. - """ - if self.sock: - self.sock.close() - self.sock = None - self.connected = False - - def _send(self, data: Union[str, bytes]): - return send(self.sock, data) - - def _recv(self, bufsize): - try: - return recv(self.sock, bufsize) - except WebSocketConnectionClosedException: - if self.sock: - self.sock.close() - self.sock = None - self.connected = False - raise - - -def create_connection(url: str, timeout=None, class_=WebSocket, **options): - """ - Connect to url and return websocket object. - - Connect to url and return the WebSocket object. - Passing optional timeout parameter will set the timeout on the socket. - If no timeout is supplied, - the global default timeout setting returned by getdefaulttimeout() is used. - You can customize using 'options'. - If you set "header" list object, you can set your own custom header. - - >>> conn = create_connection("ws://echo.websocket.events", - ... header=["User-Agent: MyProgram", - ... "x-custom: header"]) - - Parameters - ---------- - class_: class - class to instantiate when creating the connection. It has to implement - settimeout and connect. It's __init__ should be compatible with - WebSocket.__init__, i.e. accept all of it's kwargs. - header: list or dict - custom http header list or dict. - cookie: str - Cookie value. - origin: str - custom origin url. - suppress_origin: bool - suppress outputting origin header. - host: str - custom host header string. - timeout: int or float - socket timeout time. This value could be either float/integer. - If set to None, it uses the default_timeout value. - http_proxy_host: str - HTTP proxy host name. - http_proxy_port: str or int - HTTP proxy port. If not set, set to 80. - http_no_proxy: list - Whitelisted host names that don't use the proxy. - http_proxy_auth: tuple - HTTP proxy auth information. tuple of username and password. Default is None. - http_proxy_timeout: int or float - HTTP proxy timeout, default is 60 sec as per python-socks. - enable_multithread: bool - Enable lock for multithread. - redirect_limit: int - Number of redirects to follow. - sockopt: tuple - Values for socket.setsockopt. - sockopt must be a tuple and each element is an argument of sock.setsockopt. - sslopt: dict - Optional dict object for ssl socket options. See FAQ for details. - subprotocols: list - List of available subprotocols. Default is None. - skip_utf8_validation: bool - Skip utf8 validation. - socket: socket - Pre-initialized stream socket. - """ - sockopt = options.pop("sockopt", []) - sslopt = options.pop("sslopt", {}) - fire_cont_frame = options.pop("fire_cont_frame", False) - enable_multithread = options.pop("enable_multithread", True) - skip_utf8_validation = options.pop("skip_utf8_validation", False) - websock = class_( - sockopt=sockopt, - sslopt=sslopt, - fire_cont_frame=fire_cont_frame, - enable_multithread=enable_multithread, - skip_utf8_validation=skip_utf8_validation, - **options, - ) - websock.settimeout(timeout if timeout is not None else getdefaulttimeout()) - websock.connect(url, **options) - return websock diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/_exceptions.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/_exceptions.py deleted file mode 100644 index cd196e4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/_exceptions.py +++ /dev/null @@ -1,94 +0,0 @@ -""" -_exceptions.py -websocket - WebSocket client library for Python - -Copyright 2024 engn33r - -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. -""" - - -class WebSocketException(Exception): - """ - WebSocket exception class. - """ - - pass - - -class WebSocketProtocolException(WebSocketException): - """ - If the WebSocket protocol is invalid, this exception will be raised. - """ - - pass - - -class WebSocketPayloadException(WebSocketException): - """ - If the WebSocket payload is invalid, this exception will be raised. - """ - - pass - - -class WebSocketConnectionClosedException(WebSocketException): - """ - If remote host closed the connection or some network error happened, - this exception will be raised. - """ - - pass - - -class WebSocketTimeoutException(WebSocketException): - """ - WebSocketTimeoutException will be raised at socket timeout during read/write data. - """ - - pass - - -class WebSocketProxyException(WebSocketException): - """ - WebSocketProxyException will be raised when proxy error occurred. - """ - - pass - - -class WebSocketBadStatusException(WebSocketException): - """ - WebSocketBadStatusException will be raised when we get bad handshake status code. - """ - - def __init__( - self, - message: str, - status_code: int, - status_message=None, - resp_headers=None, - resp_body=None, - ): - super().__init__(message) - self.status_code = status_code - self.resp_headers = resp_headers - self.resp_body = resp_body - - -class WebSocketAddressException(WebSocketException): - """ - If the websocket address info cannot be found, this exception will be raised. - """ - - pass diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/_handshake.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/_handshake.py deleted file mode 100644 index 7bd61b8..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/_handshake.py +++ /dev/null @@ -1,202 +0,0 @@ -""" -_handshake.py -websocket - WebSocket client library for Python - -Copyright 2024 engn33r - -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 hashlib -import hmac -import os -from base64 import encodebytes as base64encode -from http import HTTPStatus - -from ._cookiejar import SimpleCookieJar -from ._exceptions import WebSocketException, WebSocketBadStatusException -from ._http import read_headers -from ._logging import dump, error -from ._socket import send - -__all__ = ["handshake_response", "handshake", "SUPPORTED_REDIRECT_STATUSES"] - -# websocket supported version. -VERSION = 13 - -SUPPORTED_REDIRECT_STATUSES = ( - HTTPStatus.MOVED_PERMANENTLY, - HTTPStatus.FOUND, - HTTPStatus.SEE_OTHER, - HTTPStatus.TEMPORARY_REDIRECT, - HTTPStatus.PERMANENT_REDIRECT, -) -SUCCESS_STATUSES = SUPPORTED_REDIRECT_STATUSES + (HTTPStatus.SWITCHING_PROTOCOLS,) - -CookieJar = SimpleCookieJar() - - -class handshake_response: - def __init__(self, status: int, headers: dict, subprotocol): - self.status = status - self.headers = headers - self.subprotocol = subprotocol - CookieJar.add(headers.get("set-cookie")) - - -def handshake( - sock, url: str, hostname: str, port: int, resource: str, **options -) -> handshake_response: - headers, key = _get_handshake_headers(resource, url, hostname, port, options) - - header_str = "\r\n".join(headers) - send(sock, header_str) - dump("request header", header_str) - - status, resp = _get_resp_headers(sock) - if status in SUPPORTED_REDIRECT_STATUSES: - return handshake_response(status, resp, None) - success, subproto = _validate(resp, key, options.get("subprotocols")) - if not success: - raise WebSocketException("Invalid WebSocket Header") - - return handshake_response(status, resp, subproto) - - -def _pack_hostname(hostname: str) -> str: - # IPv6 address - if ":" in hostname: - return f"[{hostname}]" - return hostname - - -def _get_handshake_headers( - resource: str, url: str, host: str, port: int, options: dict -) -> tuple: - headers = [f"GET {resource} HTTP/1.1", "Upgrade: websocket"] - if port in [80, 443]: - hostport = _pack_hostname(host) - else: - hostport = f"{_pack_hostname(host)}:{port}" - if options.get("host"): - headers.append(f'Host: {options["host"]}') - else: - headers.append(f"Host: {hostport}") - - # scheme indicates whether http or https is used in Origin - # The same approach is used in parse_url of _url.py to set default port - scheme, url = url.split(":", 1) - if not options.get("suppress_origin"): - if "origin" in options and options["origin"] is not None: - headers.append(f'Origin: {options["origin"]}') - elif scheme == "wss": - headers.append(f"Origin: https://{hostport}") - else: - headers.append(f"Origin: http://{hostport}") - - key = _create_sec_websocket_key() - - # Append Sec-WebSocket-Key & Sec-WebSocket-Version if not manually specified - if not options.get("header") or "Sec-WebSocket-Key" not in options["header"]: - headers.append(f"Sec-WebSocket-Key: {key}") - else: - key = options["header"]["Sec-WebSocket-Key"] - - if not options.get("header") or "Sec-WebSocket-Version" not in options["header"]: - headers.append(f"Sec-WebSocket-Version: {VERSION}") - - if not options.get("connection"): - headers.append("Connection: Upgrade") - else: - headers.append(options["connection"]) - - if subprotocols := options.get("subprotocols"): - headers.append(f'Sec-WebSocket-Protocol: {",".join(subprotocols)}') - - if header := options.get("header"): - if isinstance(header, dict): - header = [": ".join([k, v]) for k, v in header.items() if v is not None] - headers.extend(header) - - server_cookie = CookieJar.get(host) - client_cookie = options.get("cookie", None) - - if cookie := "; ".join(filter(None, [server_cookie, client_cookie])): - headers.append(f"Cookie: {cookie}") - - headers.extend(("", "")) - return headers, key - - -def _get_resp_headers(sock, success_statuses: tuple = SUCCESS_STATUSES) -> tuple: - status, resp_headers, status_message = read_headers(sock) - if status not in success_statuses: - content_len = resp_headers.get("content-length") - if content_len: - response_body = sock.recv( - int(content_len) - ) # read the body of the HTTP error message response and include it in the exception - else: - response_body = None - raise WebSocketBadStatusException( - f"Handshake status {status} {status_message} -+-+- {resp_headers} -+-+- {response_body}", - status, - status_message, - resp_headers, - response_body, - ) - return status, resp_headers - - -_HEADERS_TO_CHECK = { - "upgrade": "websocket", - "connection": "upgrade", -} - - -def _validate(headers, key: str, subprotocols) -> tuple: - subproto = None - for k, v in _HEADERS_TO_CHECK.items(): - r = headers.get(k, None) - if not r: - return False, None - r = [x.strip().lower() for x in r.split(",")] - if v not in r: - return False, None - - if subprotocols: - subproto = headers.get("sec-websocket-protocol", None) - if not subproto or subproto.lower() not in [s.lower() for s in subprotocols]: - error(f"Invalid subprotocol: {subprotocols}") - return False, None - subproto = subproto.lower() - - result = headers.get("sec-websocket-accept", None) - if not result: - return False, None - result = result.lower() - - if isinstance(result, str): - result = result.encode("utf-8") - - value = f"{key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11".encode("utf-8") - hashed = base64encode(hashlib.sha1(value).digest()).strip().lower() - - if hmac.compare_digest(hashed, result): - return True, subproto - else: - return False, None - - -def _create_sec_websocket_key() -> str: - randomness = os.urandom(16) - return base64encode(randomness).decode("utf-8").strip() diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/_http.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/_http.py deleted file mode 100644 index 9b1bf85..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/_http.py +++ /dev/null @@ -1,373 +0,0 @@ -""" -_http.py -websocket - WebSocket client library for Python - -Copyright 2024 engn33r - -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 errno -import os -import socket -from base64 import encodebytes as base64encode - -from ._exceptions import ( - WebSocketAddressException, - WebSocketException, - WebSocketProxyException, -) -from ._logging import debug, dump, trace -from ._socket import DEFAULT_SOCKET_OPTION, recv_line, send -from ._ssl_compat import HAVE_SSL, ssl -from ._url import get_proxy_info, parse_url - -__all__ = ["proxy_info", "connect", "read_headers"] - -try: - from python_socks._errors import * - from python_socks._types import ProxyType - from python_socks.sync import Proxy - - HAVE_PYTHON_SOCKS = True -except: - HAVE_PYTHON_SOCKS = False - - class ProxyError(Exception): - pass - - class ProxyTimeoutError(Exception): - pass - - class ProxyConnectionError(Exception): - pass - - -class proxy_info: - def __init__(self, **options): - self.proxy_host = options.get("http_proxy_host", None) - if self.proxy_host: - self.proxy_port = options.get("http_proxy_port", 0) - self.auth = options.get("http_proxy_auth", None) - self.no_proxy = options.get("http_no_proxy", None) - self.proxy_protocol = options.get("proxy_type", "http") - # Note: If timeout not specified, default python-socks timeout is 60 seconds - self.proxy_timeout = options.get("http_proxy_timeout", None) - if self.proxy_protocol not in [ - "http", - "socks4", - "socks4a", - "socks5", - "socks5h", - ]: - raise ProxyError( - "Only http, socks4, socks5 proxy protocols are supported" - ) - else: - self.proxy_port = 0 - self.auth = None - self.no_proxy = None - self.proxy_protocol = "http" - - -def _start_proxied_socket(url: str, options, proxy) -> tuple: - if not HAVE_PYTHON_SOCKS: - raise WebSocketException( - "Python Socks is needed for SOCKS proxying but is not available" - ) - - hostname, port, resource, is_secure = parse_url(url) - - if proxy.proxy_protocol == "socks4": - rdns = False - proxy_type = ProxyType.SOCKS4 - # socks4a sends DNS through proxy - elif proxy.proxy_protocol == "socks4a": - rdns = True - proxy_type = ProxyType.SOCKS4 - elif proxy.proxy_protocol == "socks5": - rdns = False - proxy_type = ProxyType.SOCKS5 - # socks5h sends DNS through proxy - elif proxy.proxy_protocol == "socks5h": - rdns = True - proxy_type = ProxyType.SOCKS5 - - ws_proxy = Proxy.create( - proxy_type=proxy_type, - host=proxy.proxy_host, - port=int(proxy.proxy_port), - username=proxy.auth[0] if proxy.auth else None, - password=proxy.auth[1] if proxy.auth else None, - rdns=rdns, - ) - - sock = ws_proxy.connect(hostname, port, timeout=proxy.proxy_timeout) - - if is_secure: - if HAVE_SSL: - sock = _ssl_socket(sock, options.sslopt, hostname) - else: - raise WebSocketException("SSL not available.") - - return sock, (hostname, port, resource) - - -def connect(url: str, options, proxy, socket): - # Use _start_proxied_socket() only for socks4 or socks5 proxy - # Use _tunnel() for http proxy - # TODO: Use python-socks for http protocol also, to standardize flow - if proxy.proxy_host and not socket and proxy.proxy_protocol != "http": - return _start_proxied_socket(url, options, proxy) - - hostname, port_from_url, resource, is_secure = parse_url(url) - - if socket: - return socket, (hostname, port_from_url, resource) - - addrinfo_list, need_tunnel, auth = _get_addrinfo_list( - hostname, port_from_url, is_secure, proxy - ) - if not addrinfo_list: - raise WebSocketException(f"Host not found.: {hostname}:{port_from_url}") - - sock = None - try: - sock = _open_socket(addrinfo_list, options.sockopt, options.timeout) - if need_tunnel: - sock = _tunnel(sock, hostname, port_from_url, auth) - - if is_secure: - if HAVE_SSL: - sock = _ssl_socket(sock, options.sslopt, hostname) - else: - raise WebSocketException("SSL not available.") - - return sock, (hostname, port_from_url, resource) - except: - if sock: - sock.close() - raise - - -def _get_addrinfo_list(hostname, port: int, is_secure: bool, proxy) -> tuple: - phost, pport, pauth = get_proxy_info( - hostname, - is_secure, - proxy.proxy_host, - proxy.proxy_port, - proxy.auth, - proxy.no_proxy, - ) - try: - # when running on windows 10, getaddrinfo without socktype returns a socktype 0. - # This generates an error exception: `_on_error: exception Socket type must be stream or datagram, not 0` - # or `OSError: [Errno 22] Invalid argument` when creating socket. Force the socket type to SOCK_STREAM. - if not phost: - addrinfo_list = socket.getaddrinfo( - hostname, port, 0, socket.SOCK_STREAM, socket.SOL_TCP - ) - return addrinfo_list, False, None - else: - pport = pport and pport or 80 - # when running on windows 10, the getaddrinfo used above - # returns a socktype 0. This generates an error exception: - # _on_error: exception Socket type must be stream or datagram, not 0 - # Force the socket type to SOCK_STREAM - addrinfo_list = socket.getaddrinfo( - phost, pport, 0, socket.SOCK_STREAM, socket.SOL_TCP - ) - return addrinfo_list, True, pauth - except socket.gaierror as e: - raise WebSocketAddressException(e) - - -def _open_socket(addrinfo_list, sockopt, timeout): - err = None - for addrinfo in addrinfo_list: - family, socktype, proto = addrinfo[:3] - sock = socket.socket(family, socktype, proto) - sock.settimeout(timeout) - for opts in DEFAULT_SOCKET_OPTION: - sock.setsockopt(*opts) - for opts in sockopt: - sock.setsockopt(*opts) - - address = addrinfo[4] - err = None - while not err: - try: - sock.connect(address) - except socket.error as error: - sock.close() - error.remote_ip = str(address[0]) - try: - eConnRefused = ( - errno.ECONNREFUSED, - errno.WSAECONNREFUSED, - errno.ENETUNREACH, - ) - except AttributeError: - eConnRefused = (errno.ECONNREFUSED, errno.ENETUNREACH) - if error.errno not in eConnRefused: - raise error - err = error - continue - else: - break - else: - continue - break - else: - if err: - raise err - - return sock - - -def _wrap_sni_socket(sock: socket.socket, sslopt: dict, hostname, check_hostname): - context = sslopt.get("context", None) - if not context: - context = ssl.SSLContext(sslopt.get("ssl_version", ssl.PROTOCOL_TLS_CLIENT)) - # Non default context need to manually enable SSLKEYLOGFILE support by setting the keylog_filename attribute. - # For more details see also: - # * https://docs.python.org/3.8/library/ssl.html?highlight=sslkeylogfile#context-creation - # * https://docs.python.org/3.8/library/ssl.html?highlight=sslkeylogfile#ssl.SSLContext.keylog_filename - context.keylog_filename = os.environ.get("SSLKEYLOGFILE", None) - - if sslopt.get("cert_reqs", ssl.CERT_NONE) != ssl.CERT_NONE: - cafile = sslopt.get("ca_certs", None) - capath = sslopt.get("ca_cert_path", None) - if cafile or capath: - context.load_verify_locations(cafile=cafile, capath=capath) - elif hasattr(context, "load_default_certs"): - context.load_default_certs(ssl.Purpose.SERVER_AUTH) - if sslopt.get("certfile", None): - context.load_cert_chain( - sslopt["certfile"], - sslopt.get("keyfile", None), - sslopt.get("password", None), - ) - - # Python 3.10 switch to PROTOCOL_TLS_CLIENT defaults to "cert_reqs = ssl.CERT_REQUIRED" and "check_hostname = True" - # If both disabled, set check_hostname before verify_mode - # see https://github.com/liris/websocket-client/commit/b96a2e8fa765753e82eea531adb19716b52ca3ca#commitcomment-10803153 - if sslopt.get("cert_reqs", ssl.CERT_NONE) == ssl.CERT_NONE and not sslopt.get( - "check_hostname", False - ): - context.check_hostname = False - context.verify_mode = ssl.CERT_NONE - else: - context.check_hostname = sslopt.get("check_hostname", True) - context.verify_mode = sslopt.get("cert_reqs", ssl.CERT_REQUIRED) - - if "ciphers" in sslopt: - context.set_ciphers(sslopt["ciphers"]) - if "cert_chain" in sslopt: - certfile, keyfile, password = sslopt["cert_chain"] - context.load_cert_chain(certfile, keyfile, password) - if "ecdh_curve" in sslopt: - context.set_ecdh_curve(sslopt["ecdh_curve"]) - - return context.wrap_socket( - sock, - do_handshake_on_connect=sslopt.get("do_handshake_on_connect", True), - suppress_ragged_eofs=sslopt.get("suppress_ragged_eofs", True), - server_hostname=hostname, - ) - - -def _ssl_socket(sock: socket.socket, user_sslopt: dict, hostname): - sslopt: dict = {"cert_reqs": ssl.CERT_REQUIRED} - sslopt.update(user_sslopt) - - cert_path = os.environ.get("WEBSOCKET_CLIENT_CA_BUNDLE") - if ( - cert_path - and os.path.isfile(cert_path) - and user_sslopt.get("ca_certs", None) is None - ): - sslopt["ca_certs"] = cert_path - elif ( - cert_path - and os.path.isdir(cert_path) - and user_sslopt.get("ca_cert_path", None) is None - ): - sslopt["ca_cert_path"] = cert_path - - if sslopt.get("server_hostname", None): - hostname = sslopt["server_hostname"] - - check_hostname = sslopt.get("check_hostname", True) - sock = _wrap_sni_socket(sock, sslopt, hostname, check_hostname) - - return sock - - -def _tunnel(sock: socket.socket, host, port: int, auth) -> socket.socket: - debug("Connecting proxy...") - connect_header = f"CONNECT {host}:{port} HTTP/1.1\r\n" - connect_header += f"Host: {host}:{port}\r\n" - - # TODO: support digest auth. - if auth and auth[0]: - auth_str = auth[0] - if auth[1]: - auth_str += f":{auth[1]}" - encoded_str = base64encode(auth_str.encode()).strip().decode().replace("\n", "") - connect_header += f"Proxy-Authorization: Basic {encoded_str}\r\n" - connect_header += "\r\n" - dump("request header", connect_header) - - send(sock, connect_header) - - try: - status, _, _ = read_headers(sock) - except Exception as e: - raise WebSocketProxyException(str(e)) - - if status != 200: - raise WebSocketProxyException(f"failed CONNECT via proxy status: {status}") - - return sock - - -def read_headers(sock: socket.socket) -> tuple: - status = None - status_message = None - headers: dict = {} - trace("--- response header ---") - - while True: - line = recv_line(sock) - line = line.decode("utf-8").strip() - if not line: - break - trace(line) - if not status: - status_info = line.split(" ", 2) - status = int(status_info[1]) - if len(status_info) > 2: - status_message = status_info[2] - else: - kv = line.split(":", 1) - if len(kv) != 2: - raise WebSocketException("Invalid header") - key, value = kv - if key.lower() == "set-cookie" and headers.get("set-cookie"): - headers["set-cookie"] = headers.get("set-cookie") + "; " + value.strip() - else: - headers[key.lower()] = value.strip() - - trace("-----------------------") - - return status, headers, status_message diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/_logging.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/_logging.py deleted file mode 100644 index 0f673d3..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/_logging.py +++ /dev/null @@ -1,106 +0,0 @@ -import logging - -""" -_logging.py -websocket - WebSocket client library for Python - -Copyright 2024 engn33r - -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. -""" - -_logger = logging.getLogger("websocket") -try: - from logging import NullHandler -except ImportError: - - class NullHandler(logging.Handler): - def emit(self, record) -> None: - pass - - -_logger.addHandler(NullHandler()) - -_traceEnabled = False - -__all__ = [ - "enableTrace", - "dump", - "error", - "warning", - "debug", - "trace", - "isEnabledForError", - "isEnabledForDebug", - "isEnabledForTrace", -] - - -def enableTrace( - traceable: bool, - handler: logging.StreamHandler = logging.StreamHandler(), - level: str = "DEBUG", -) -> None: - """ - Turn on/off the traceability. - - Parameters - ---------- - traceable: bool - If set to True, traceability is enabled. - """ - global _traceEnabled - _traceEnabled = traceable - if traceable: - _logger.addHandler(handler) - _logger.setLevel(getattr(logging, level)) - - -def dump(title: str, message: str) -> None: - if _traceEnabled: - _logger.debug(f"--- {title} ---") - _logger.debug(message) - _logger.debug("-----------------------") - - -def error(msg: str) -> None: - _logger.error(msg) - - -def warning(msg: str) -> None: - _logger.warning(msg) - - -def debug(msg: str) -> None: - _logger.debug(msg) - - -def info(msg: str) -> None: - _logger.info(msg) - - -def trace(msg: str) -> None: - if _traceEnabled: - _logger.debug(msg) - - -def isEnabledForError() -> bool: - return _logger.isEnabledFor(logging.ERROR) - - -def isEnabledForDebug() -> bool: - return _logger.isEnabledFor(logging.DEBUG) - - -def isEnabledForTrace() -> bool: - return _traceEnabled diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/_socket.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/_socket.py deleted file mode 100644 index 81094ff..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/_socket.py +++ /dev/null @@ -1,188 +0,0 @@ -import errno -import selectors -import socket -from typing import Union - -from ._exceptions import ( - WebSocketConnectionClosedException, - WebSocketTimeoutException, -) -from ._ssl_compat import SSLError, SSLWantReadError, SSLWantWriteError -from ._utils import extract_error_code, extract_err_message - -""" -_socket.py -websocket - WebSocket client library for Python - -Copyright 2024 engn33r - -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. -""" - -DEFAULT_SOCKET_OPTION = [(socket.SOL_TCP, socket.TCP_NODELAY, 1)] -if hasattr(socket, "SO_KEEPALIVE"): - DEFAULT_SOCKET_OPTION.append((socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)) -if hasattr(socket, "TCP_KEEPIDLE"): - DEFAULT_SOCKET_OPTION.append((socket.SOL_TCP, socket.TCP_KEEPIDLE, 30)) -if hasattr(socket, "TCP_KEEPINTVL"): - DEFAULT_SOCKET_OPTION.append((socket.SOL_TCP, socket.TCP_KEEPINTVL, 10)) -if hasattr(socket, "TCP_KEEPCNT"): - DEFAULT_SOCKET_OPTION.append((socket.SOL_TCP, socket.TCP_KEEPCNT, 3)) - -_default_timeout = None - -__all__ = [ - "DEFAULT_SOCKET_OPTION", - "sock_opt", - "setdefaulttimeout", - "getdefaulttimeout", - "recv", - "recv_line", - "send", -] - - -class sock_opt: - def __init__(self, sockopt: list, sslopt: dict) -> None: - if sockopt is None: - sockopt = [] - if sslopt is None: - sslopt = {} - self.sockopt = sockopt - self.sslopt = sslopt - self.timeout = None - - -def setdefaulttimeout(timeout: Union[int, float, None]) -> None: - """ - Set the global timeout setting to connect. - - Parameters - ---------- - timeout: int or float - default socket timeout time (in seconds) - """ - global _default_timeout - _default_timeout = timeout - - -def getdefaulttimeout() -> Union[int, float, None]: - """ - Get default timeout - - Returns - ---------- - _default_timeout: int or float - Return the global timeout setting (in seconds) to connect. - """ - return _default_timeout - - -def recv(sock: socket.socket, bufsize: int) -> bytes: - if not sock: - raise WebSocketConnectionClosedException("socket is already closed.") - - def _recv(): - try: - return sock.recv(bufsize) - except SSLWantReadError: - pass - except socket.error as exc: - error_code = extract_error_code(exc) - if error_code not in [errno.EAGAIN, errno.EWOULDBLOCK]: - raise - - sel = selectors.DefaultSelector() - sel.register(sock, selectors.EVENT_READ) - - r = sel.select(sock.gettimeout()) - sel.close() - - if r: - return sock.recv(bufsize) - - try: - if sock.gettimeout() == 0: - bytes_ = sock.recv(bufsize) - else: - bytes_ = _recv() - except TimeoutError: - raise WebSocketTimeoutException("Connection timed out") - except socket.timeout as e: - message = extract_err_message(e) - raise WebSocketTimeoutException(message) - except SSLError as e: - message = extract_err_message(e) - if isinstance(message, str) and "timed out" in message: - raise WebSocketTimeoutException(message) - else: - raise - - if not bytes_: - raise WebSocketConnectionClosedException("Connection to remote host was lost.") - - return bytes_ - - -def recv_line(sock: socket.socket) -> bytes: - line = [] - while True: - c = recv(sock, 1) - line.append(c) - if c == b"\n": - break - return b"".join(line) - - -def send(sock: socket.socket, data: Union[bytes, str]) -> int: - if isinstance(data, str): - data = data.encode("utf-8") - - if not sock: - raise WebSocketConnectionClosedException("socket is already closed.") - - def _send(): - try: - return sock.send(data) - except SSLWantWriteError: - pass - except socket.error as exc: - error_code = extract_error_code(exc) - if error_code is None: - raise - if error_code not in [errno.EAGAIN, errno.EWOULDBLOCK]: - raise - - sel = selectors.DefaultSelector() - sel.register(sock, selectors.EVENT_WRITE) - - w = sel.select(sock.gettimeout()) - sel.close() - - if w: - return sock.send(data) - - try: - if sock.gettimeout() == 0: - return sock.send(data) - else: - return _send() - except socket.timeout as e: - message = extract_err_message(e) - raise WebSocketTimeoutException(message) - except Exception as e: - message = extract_err_message(e) - if isinstance(message, str) and "timed out" in message: - raise WebSocketTimeoutException(message) - else: - raise diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/_ssl_compat.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/_ssl_compat.py deleted file mode 100644 index 0a8a32b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/_ssl_compat.py +++ /dev/null @@ -1,48 +0,0 @@ -""" -_ssl_compat.py -websocket - WebSocket client library for Python - -Copyright 2024 engn33r - -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. -""" -__all__ = [ - "HAVE_SSL", - "ssl", - "SSLError", - "SSLEOFError", - "SSLWantReadError", - "SSLWantWriteError", -] - -try: - import ssl - from ssl import SSLError, SSLEOFError, SSLWantReadError, SSLWantWriteError - - HAVE_SSL = True -except ImportError: - # dummy class of SSLError for environment without ssl support - class SSLError(Exception): - pass - - class SSLEOFError(Exception): - pass - - class SSLWantReadError(Exception): - pass - - class SSLWantWriteError(Exception): - pass - - ssl = None - HAVE_SSL = False diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/_url.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/_url.py deleted file mode 100644 index 9021317..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/_url.py +++ /dev/null @@ -1,190 +0,0 @@ -import os -import socket -import struct -from typing import Optional -from urllib.parse import unquote, urlparse -from ._exceptions import WebSocketProxyException - -""" -_url.py -websocket - WebSocket client library for Python - -Copyright 2024 engn33r - -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. -""" - -__all__ = ["parse_url", "get_proxy_info"] - - -def parse_url(url: str) -> tuple: - """ - parse url and the result is tuple of - (hostname, port, resource path and the flag of secure mode) - - Parameters - ---------- - url: str - url string. - """ - if ":" not in url: - raise ValueError("url is invalid") - - scheme, url = url.split(":", 1) - - parsed = urlparse(url, scheme="http") - if parsed.hostname: - hostname = parsed.hostname - else: - raise ValueError("hostname is invalid") - port = 0 - if parsed.port: - port = parsed.port - - is_secure = False - if scheme == "ws": - if not port: - port = 80 - elif scheme == "wss": - is_secure = True - if not port: - port = 443 - else: - raise ValueError("scheme %s is invalid" % scheme) - - if parsed.path: - resource = parsed.path - else: - resource = "/" - - if parsed.query: - resource += f"?{parsed.query}" - - return hostname, port, resource, is_secure - - -DEFAULT_NO_PROXY_HOST = ["localhost", "127.0.0.1"] - - -def _is_ip_address(addr: str) -> bool: - try: - socket.inet_aton(addr) - except socket.error: - return False - else: - return True - - -def _is_subnet_address(hostname: str) -> bool: - try: - addr, netmask = hostname.split("/") - return _is_ip_address(addr) and 0 <= int(netmask) < 32 - except ValueError: - return False - - -def _is_address_in_network(ip: str, net: str) -> bool: - ipaddr: int = struct.unpack("!I", socket.inet_aton(ip))[0] - netaddr, netmask = net.split("/") - netaddr: int = struct.unpack("!I", socket.inet_aton(netaddr))[0] - - netmask = (0xFFFFFFFF << (32 - int(netmask))) & 0xFFFFFFFF - return ipaddr & netmask == netaddr - - -def _is_no_proxy_host(hostname: str, no_proxy: Optional[list]) -> bool: - if not no_proxy: - if v := os.environ.get("no_proxy", os.environ.get("NO_PROXY", "")).replace( - " ", "" - ): - no_proxy = v.split(",") - if not no_proxy: - no_proxy = DEFAULT_NO_PROXY_HOST - - if "*" in no_proxy: - return True - if hostname in no_proxy: - return True - if _is_ip_address(hostname): - return any( - [ - _is_address_in_network(hostname, subnet) - for subnet in no_proxy - if _is_subnet_address(subnet) - ] - ) - for domain in [domain for domain in no_proxy if domain.startswith(".")]: - if hostname.endswith(domain): - return True - return False - - -def get_proxy_info( - hostname: str, - is_secure: bool, - proxy_host: Optional[str] = None, - proxy_port: int = 0, - proxy_auth: Optional[tuple] = None, - no_proxy: Optional[list] = None, - proxy_type: str = "http", -) -> tuple: - """ - Try to retrieve proxy host and port from environment - if not provided in options. - Result is (proxy_host, proxy_port, proxy_auth). - proxy_auth is tuple of username and password - of proxy authentication information. - - Parameters - ---------- - hostname: str - Websocket server name. - is_secure: bool - Is the connection secure? (wss) looks for "https_proxy" in env - instead of "http_proxy" - proxy_host: str - http proxy host name. - proxy_port: str or int - http proxy port. - no_proxy: list - Whitelisted host names that don't use the proxy. - proxy_auth: tuple - HTTP proxy auth information. Tuple of username and password. Default is None. - proxy_type: str - Specify the proxy protocol (http, socks4, socks4a, socks5, socks5h). Default is "http". - Use socks4a or socks5h if you want to send DNS requests through the proxy. - """ - if _is_no_proxy_host(hostname, no_proxy): - return None, 0, None - - if proxy_host: - if not proxy_port: - raise WebSocketProxyException("Cannot use port 0 when proxy_host specified") - port = proxy_port - auth = proxy_auth - return proxy_host, port, auth - - env_key = "https_proxy" if is_secure else "http_proxy" - value = os.environ.get(env_key, os.environ.get(env_key.upper(), "")).replace( - " ", "" - ) - if value: - proxy = urlparse(value) - auth = ( - (unquote(proxy.username), unquote(proxy.password)) - if proxy.username - else None - ) - return proxy.hostname, proxy.port, auth - - return None, 0, None diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/_utils.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/_utils.py deleted file mode 100644 index 65f3c0d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/_utils.py +++ /dev/null @@ -1,459 +0,0 @@ -from typing import Union - -""" -_url.py -websocket - WebSocket client library for Python - -Copyright 2024 engn33r - -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. -""" -__all__ = ["NoLock", "validate_utf8", "extract_err_message", "extract_error_code"] - - -class NoLock: - def __enter__(self) -> None: - pass - - def __exit__(self, exc_type, exc_value, traceback) -> None: - pass - - -try: - # If wsaccel is available we use compiled routines to validate UTF-8 - # strings. - from wsaccel.utf8validator import Utf8Validator - - def _validate_utf8(utfbytes: Union[str, bytes]) -> bool: - result: bool = Utf8Validator().validate(utfbytes)[0] - return result - -except ImportError: - # UTF-8 validator - # python implementation of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ - - _UTF8_ACCEPT = 0 - _UTF8_REJECT = 12 - - _UTF8D = [ - # The first part of the table maps bytes to character classes that - # to reduce the size of the transition table and create bitmasks. - 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, - 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, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 9, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 7, - 8, - 8, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 10, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 3, - 4, - 3, - 3, - 11, - 6, - 6, - 6, - 5, - 8, - 8, - 8, - 8, - 8, - 8, - 8, - 8, - 8, - 8, - 8, - # The second part is a transition table that maps a combination - # of a state of the automaton and a character class to a state. - 0, - 12, - 24, - 36, - 60, - 96, - 84, - 12, - 12, - 12, - 48, - 72, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 0, - 12, - 12, - 12, - 12, - 12, - 0, - 12, - 0, - 12, - 12, - 12, - 24, - 12, - 12, - 12, - 12, - 12, - 24, - 12, - 24, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 24, - 12, - 12, - 12, - 12, - 12, - 24, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 24, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 36, - 12, - 36, - 12, - 12, - 12, - 36, - 12, - 12, - 12, - 12, - 12, - 36, - 12, - 36, - 12, - 12, - 12, - 36, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - 12, - ] - - def _decode(state: int, codep: int, ch: int) -> tuple: - tp = _UTF8D[ch] - - codep = ( - (ch & 0x3F) | (codep << 6) if (state != _UTF8_ACCEPT) else (0xFF >> tp) & ch - ) - state = _UTF8D[256 + state + tp] - - return state, codep - - def _validate_utf8(utfbytes: Union[str, bytes]) -> bool: - state = _UTF8_ACCEPT - codep = 0 - for i in utfbytes: - state, codep = _decode(state, codep, int(i)) - if state == _UTF8_REJECT: - return False - - return True - - -def validate_utf8(utfbytes: Union[str, bytes]) -> bool: - """ - validate utf8 byte string. - utfbytes: utf byte string to check. - return value: if valid utf8 string, return true. Otherwise, return false. - """ - return _validate_utf8(utfbytes) - - -def extract_err_message(exception: Exception) -> Union[str, None]: - if exception.args: - exception_message: str = exception.args[0] - return exception_message - else: - return None - - -def extract_error_code(exception: Exception) -> Union[int, None]: - if exception.args and len(exception.args) > 1: - return exception.args[0] if isinstance(exception.args[0], int) else None diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/_wsdump.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/_wsdump.py deleted file mode 100644 index d4d76dc..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/_wsdump.py +++ /dev/null @@ -1,244 +0,0 @@ -#!/usr/bin/env python3 - -""" -wsdump.py -websocket - WebSocket client library for Python - -Copyright 2024 engn33r - -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 argparse -import code -import gzip -import ssl -import sys -import threading -import time -import zlib -from urllib.parse import urlparse - -import websocket - -try: - import readline -except ImportError: - pass - - -def get_encoding() -> str: - encoding = getattr(sys.stdin, "encoding", "") - if not encoding: - return "utf-8" - else: - return encoding.lower() - - -OPCODE_DATA = (websocket.ABNF.OPCODE_TEXT, websocket.ABNF.OPCODE_BINARY) -ENCODING = get_encoding() - - -class VAction(argparse.Action): - def __call__( - self, - parser: argparse.Namespace, - args: tuple, - values: str, - option_string: str = None, - ) -> None: - if values is None: - values = "1" - try: - values = int(values) - except ValueError: - values = values.count("v") + 1 - setattr(args, self.dest, values) - - -def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser(description="WebSocket Simple Dump Tool") - parser.add_argument( - "url", metavar="ws_url", help="websocket url. ex. ws://echo.websocket.events/" - ) - parser.add_argument("-p", "--proxy", help="proxy url. ex. http://127.0.0.1:8080") - parser.add_argument( - "-v", - "--verbose", - default=0, - nargs="?", - action=VAction, - dest="verbose", - help="set verbose mode. If set to 1, show opcode. " - "If set to 2, enable to trace websocket module", - ) - parser.add_argument( - "-n", "--nocert", action="store_true", help="Ignore invalid SSL cert" - ) - parser.add_argument("-r", "--raw", action="store_true", help="raw output") - parser.add_argument("-s", "--subprotocols", nargs="*", help="Set subprotocols") - parser.add_argument("-o", "--origin", help="Set origin") - parser.add_argument( - "--eof-wait", - default=0, - type=int, - help="wait time(second) after 'EOF' received.", - ) - parser.add_argument("-t", "--text", help="Send initial text") - parser.add_argument( - "--timings", action="store_true", help="Print timings in seconds" - ) - parser.add_argument("--headers", help="Set custom headers. Use ',' as separator") - - return parser.parse_args() - - -class RawInput: - def raw_input(self, prompt: str = "") -> str: - line = input(prompt) - - if ENCODING and ENCODING != "utf-8" and not isinstance(line, str): - line = line.decode(ENCODING).encode("utf-8") - elif isinstance(line, str): - line = line.encode("utf-8") - - return line - - -class InteractiveConsole(RawInput, code.InteractiveConsole): - def write(self, data: str) -> None: - sys.stdout.write("\033[2K\033[E") - # sys.stdout.write("\n") - sys.stdout.write("\033[34m< " + data + "\033[39m") - sys.stdout.write("\n> ") - sys.stdout.flush() - - def read(self) -> str: - return self.raw_input("> ") - - -class NonInteractive(RawInput): - def write(self, data: str) -> None: - sys.stdout.write(data) - sys.stdout.write("\n") - sys.stdout.flush() - - def read(self) -> str: - return self.raw_input("") - - -def main() -> None: - start_time = time.time() - args = parse_args() - if args.verbose > 1: - websocket.enableTrace(True) - options = {} - if args.proxy: - p = urlparse(args.proxy) - options["http_proxy_host"] = p.hostname - options["http_proxy_port"] = p.port - if args.origin: - options["origin"] = args.origin - if args.subprotocols: - options["subprotocols"] = args.subprotocols - opts = {} - if args.nocert: - opts = {"cert_reqs": ssl.CERT_NONE, "check_hostname": False} - if args.headers: - options["header"] = list(map(str.strip, args.headers.split(","))) - ws = websocket.create_connection(args.url, sslopt=opts, **options) - if args.raw: - console = NonInteractive() - else: - console = InteractiveConsole() - print("Press Ctrl+C to quit") - - def recv() -> tuple: - try: - frame = ws.recv_frame() - except websocket.WebSocketException: - return websocket.ABNF.OPCODE_CLOSE, "" - if not frame: - raise websocket.WebSocketException(f"Not a valid frame {frame}") - elif frame.opcode in OPCODE_DATA: - return frame.opcode, frame.data - elif frame.opcode == websocket.ABNF.OPCODE_CLOSE: - ws.send_close() - return frame.opcode, "" - elif frame.opcode == websocket.ABNF.OPCODE_PING: - ws.pong(frame.data) - return frame.opcode, frame.data - - return frame.opcode, frame.data - - def recv_ws() -> None: - while True: - opcode, data = recv() - msg = None - if opcode == websocket.ABNF.OPCODE_TEXT and isinstance(data, bytes): - data = str(data, "utf-8") - if ( - isinstance(data, bytes) and len(data) > 2 and data[:2] == b"\037\213" - ): # gzip magick - try: - data = "[gzip] " + str(gzip.decompress(data), "utf-8") - except: - pass - elif isinstance(data, bytes): - try: - data = "[zlib] " + str( - zlib.decompress(data, -zlib.MAX_WBITS), "utf-8" - ) - except: - pass - - if isinstance(data, bytes): - data = repr(data) - - if args.verbose: - msg = f"{websocket.ABNF.OPCODE_MAP.get(opcode)}: {data}" - else: - msg = data - - if msg is not None: - if args.timings: - console.write(f"{time.time() - start_time}: {msg}") - else: - console.write(msg) - - if opcode == websocket.ABNF.OPCODE_CLOSE: - break - - thread = threading.Thread(target=recv_ws) - thread.daemon = True - thread.start() - - if args.text: - ws.send(args.text) - - while True: - try: - message = console.read() - ws.send(message) - except KeyboardInterrupt: - return - except EOFError: - time.sleep(args.eof_wait) - return - - -if __name__ == "__main__": - try: - main() - except Exception as e: - print(e) diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/data/WebSocketMain.swf b/plotter-app/venv/lib/python3.8/site-packages/websocket/data/WebSocketMain.swf deleted file mode 100644 index 5eab452..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/data/WebSocketMain.swf and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/data/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/data/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/data/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/data/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 853a0d0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/data/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/data/flashsocket.js b/plotter-app/venv/lib/python3.8/site-packages/websocket/data/flashsocket.js deleted file mode 100644 index c7dd442..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/data/flashsocket.js +++ /dev/null @@ -1,998 +0,0 @@ -WEB_SOCKET_SWF_LOCATION = '/websocket/WebSocketMain.swf'; -/* SWFObject v2.2 - is released under the MIT License -*/ -var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='"+af+"";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab 0) { - for (var i = 0; i < ol; i++) { - if (typeof objects[i].SetVariable != "undefined") { - activeObjects[activeObjects.length] = objects[i]; - } - } - } - var embeds = document.getElementsByTagName("embed"); - var el = embeds.length; - var activeEmbeds = []; - if (el > 0) { - for (var j = 0; j < el; j++) { - if (typeof embeds[j].SetVariable != "undefined") { - activeEmbeds[activeEmbeds.length] = embeds[j]; - } - } - } - var aol = activeObjects.length; - var ael = activeEmbeds.length; - var searchStr = "bridgeName="+ bridgeName; - if ((aol == 1 && !ael) || (aol == 1 && ael == 1)) { - FABridge.attachBridge(activeObjects[0], bridgeName); - } - else if (ael == 1 && !aol) { - FABridge.attachBridge(activeEmbeds[0], bridgeName); - } - else { - var flash_found = false; - if (aol > 1) { - for (var k = 0; k < aol; k++) { - var params = activeObjects[k].childNodes; - for (var l = 0; l < params.length; l++) { - var param = params[l]; - if (param.nodeType == 1 && param.tagName.toLowerCase() == "param" && param["name"].toLowerCase() == "flashvars" && param["value"].indexOf(searchStr) >= 0) { - FABridge.attachBridge(activeObjects[k], bridgeName); - flash_found = true; - break; - } - } - if (flash_found) { - break; - } - } - } - if (!flash_found && ael > 1) { - for (var m = 0; m < ael; m++) { - var flashVars = activeEmbeds[m].attributes.getNamedItem("flashVars").nodeValue; - if (flashVars.indexOf(searchStr) >= 0) { - FABridge.attachBridge(activeEmbeds[m], bridgeName); - break; - } - } - } - } - return true; -} - -// used to track multiple bridge instances, since callbacks from AS are global across the page. - -FABridge.nextBridgeID = 0; -FABridge.instances = {}; -FABridge.idMap = {}; -FABridge.refCount = 0; - -FABridge.extractBridgeFromID = function(id) -{ - var bridgeID = (id >> 16); - return FABridge.idMap[bridgeID]; -} - -FABridge.attachBridge = function(instance, bridgeName) -{ - var newBridgeInstance = new FABridge(instance, bridgeName); - - FABridge[bridgeName] = newBridgeInstance; - -/* FABridge[bridgeName] = function() { - return newBridgeInstance.root(); - } -*/ - var callbacks = FABridge.initCallbacks[bridgeName]; - if (callbacks == null) - { - return; - } - for (var i = 0; i < callbacks.length; i++) - { - callbacks[i].call(newBridgeInstance); - } - delete FABridge.initCallbacks[bridgeName] -} - -// some methods can't be proxied. You can use the explicit get,set, and call methods if necessary. - -FABridge.blockedMethods = -{ - toString: true, - get: true, - set: true, - call: true -}; - -FABridge.prototype = -{ - - -// bootstrapping - - root: function() - { - return this.deserialize(this.target.getRoot()); - }, -//clears all of the AS objects in the cache maps - releaseASObjects: function() - { - return this.target.releaseASObjects(); - }, -//clears a specific object in AS from the type maps - releaseNamedASObject: function(value) - { - if(typeof(value) != "object") - { - return false; - } - else - { - var ret = this.target.releaseNamedASObject(value.fb_instance_id); - return ret; - } - }, -//create a new AS Object - create: function(className) - { - return this.deserialize(this.target.create(className)); - }, - - - // utilities - - makeID: function(token) - { - return (this.bridgeID << 16) + token; - }, - - - // low level access to the flash object - -//get a named property from an AS object - getPropertyFromAS: function(objRef, propName) - { - if (FABridge.refCount > 0) - { - throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround."); - } - else - { - FABridge.refCount++; - retVal = this.target.getPropFromAS(objRef, propName); - retVal = this.handleError(retVal); - FABridge.refCount--; - return retVal; - } - }, -//set a named property on an AS object - setPropertyInAS: function(objRef,propName, value) - { - if (FABridge.refCount > 0) - { - throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround."); - } - else - { - FABridge.refCount++; - retVal = this.target.setPropInAS(objRef,propName, this.serialize(value)); - retVal = this.handleError(retVal); - FABridge.refCount--; - return retVal; - } - }, - -//call an AS function - callASFunction: function(funcID, args) - { - if (FABridge.refCount > 0) - { - throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround."); - } - else - { - FABridge.refCount++; - retVal = this.target.invokeASFunction(funcID, this.serialize(args)); - retVal = this.handleError(retVal); - FABridge.refCount--; - return retVal; - } - }, -//call a method on an AS object - callASMethod: function(objID, funcName, args) - { - if (FABridge.refCount > 0) - { - throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround."); - } - else - { - FABridge.refCount++; - args = this.serialize(args); - retVal = this.target.invokeASMethod(objID, funcName, args); - retVal = this.handleError(retVal); - FABridge.refCount--; - return retVal; - } - }, - - // responders to remote calls from flash - - //callback from flash that executes a local JS function - //used mostly when setting js functions as callbacks on events - invokeLocalFunction: function(funcID, args) - { - var result; - var func = this.localFunctionCache[funcID]; - - if(func != undefined) - { - result = this.serialize(func.apply(null, this.deserialize(args))); - } - - return result; - }, - - // Object Types and Proxies - - // accepts an object reference, returns a type object matching the obj reference. - getTypeFromName: function(objTypeName) - { - return this.remoteTypeCache[objTypeName]; - }, - //create an AS proxy for the given object ID and type - createProxy: function(objID, typeName) - { - var objType = this.getTypeFromName(typeName); - instanceFactory.prototype = objType; - var instance = new instanceFactory(objID); - this.remoteInstanceCache[objID] = instance; - return instance; - }, - //return the proxy associated with the given object ID - getProxy: function(objID) - { - return this.remoteInstanceCache[objID]; - }, - - // accepts a type structure, returns a constructed type - addTypeDataToCache: function(typeData) - { - var newType = new ASProxy(this, typeData.name); - var accessors = typeData.accessors; - for (var i = 0; i < accessors.length; i++) - { - this.addPropertyToType(newType, accessors[i]); - } - - var methods = typeData.methods; - for (var i = 0; i < methods.length; i++) - { - if (FABridge.blockedMethods[methods[i]] == undefined) - { - this.addMethodToType(newType, methods[i]); - } - } - - - this.remoteTypeCache[newType.typeName] = newType; - return newType; - }, - - //add a property to a typename; used to define the properties that can be called on an AS proxied object - addPropertyToType: function(ty, propName) - { - var c = propName.charAt(0); - var setterName; - var getterName; - if(c >= "a" && c <= "z") - { - getterName = "get" + c.toUpperCase() + propName.substr(1); - setterName = "set" + c.toUpperCase() + propName.substr(1); - } - else - { - getterName = "get" + propName; - setterName = "set" + propName; - } - ty[setterName] = function(val) - { - this.bridge.setPropertyInAS(this.fb_instance_id, propName, val); - } - ty[getterName] = function() - { - return this.bridge.deserialize(this.bridge.getPropertyFromAS(this.fb_instance_id, propName)); - } - }, - - //add a method to a typename; used to define the methods that can be callefd on an AS proxied object - addMethodToType: function(ty, methodName) - { - ty[methodName] = function() - { - return this.bridge.deserialize(this.bridge.callASMethod(this.fb_instance_id, methodName, FABridge.argsToArray(arguments))); - } - }, - - // Function Proxies - - //returns the AS proxy for the specified function ID - getFunctionProxy: function(funcID) - { - var bridge = this; - if (this.remoteFunctionCache[funcID] == null) - { - this.remoteFunctionCache[funcID] = function() - { - bridge.callASFunction(funcID, FABridge.argsToArray(arguments)); - } - } - return this.remoteFunctionCache[funcID]; - }, - - //reutrns the ID of the given function; if it doesnt exist it is created and added to the local cache - getFunctionID: function(func) - { - if (func.__bridge_id__ == undefined) - { - func.__bridge_id__ = this.makeID(this.nextLocalFuncID++); - this.localFunctionCache[func.__bridge_id__] = func; - } - return func.__bridge_id__; - }, - - // serialization / deserialization - - serialize: function(value) - { - var result = {}; - - var t = typeof(value); - //primitives are kept as such - if (t == "number" || t == "string" || t == "boolean" || t == null || t == undefined) - { - result = value; - } - else if (value instanceof Array) - { - //arrays are serializesd recursively - result = []; - for (var i = 0; i < value.length; i++) - { - result[i] = this.serialize(value[i]); - } - } - else if (t == "function") - { - //js functions are assigned an ID and stored in the local cache - result.type = FABridge.TYPE_JSFUNCTION; - result.value = this.getFunctionID(value); - } - else if (value instanceof ASProxy) - { - result.type = FABridge.TYPE_ASINSTANCE; - result.value = value.fb_instance_id; - } - else - { - result.type = FABridge.TYPE_ANONYMOUS; - result.value = value; - } - - return result; - }, - - //on deserialization we always check the return for the specific error code that is used to marshall NPE's into JS errors - // the unpacking is done by returning the value on each pachet for objects/arrays - deserialize: function(packedValue) - { - - var result; - - var t = typeof(packedValue); - if (t == "number" || t == "string" || t == "boolean" || packedValue == null || packedValue == undefined) - { - result = this.handleError(packedValue); - } - else if (packedValue instanceof Array) - { - result = []; - for (var i = 0; i < packedValue.length; i++) - { - result[i] = this.deserialize(packedValue[i]); - } - } - else if (t == "object") - { - for(var i = 0; i < packedValue.newTypes.length; i++) - { - this.addTypeDataToCache(packedValue.newTypes[i]); - } - for (var aRefID in packedValue.newRefs) - { - this.createProxy(aRefID, packedValue.newRefs[aRefID]); - } - if (packedValue.type == FABridge.TYPE_PRIMITIVE) - { - result = packedValue.value; - } - else if (packedValue.type == FABridge.TYPE_ASFUNCTION) - { - result = this.getFunctionProxy(packedValue.value); - } - else if (packedValue.type == FABridge.TYPE_ASINSTANCE) - { - result = this.getProxy(packedValue.value); - } - else if (packedValue.type == FABridge.TYPE_ANONYMOUS) - { - result = packedValue.value; - } - } - return result; - }, - //increases the reference count for the given object - addRef: function(obj) - { - this.target.incRef(obj.fb_instance_id); - }, - //decrease the reference count for the given object and release it if needed - release:function(obj) - { - this.target.releaseRef(obj.fb_instance_id); - }, - - // check the given value for the components of the hard-coded error code : __FLASHERROR - // used to marshall NPE's into flash - - handleError: function(value) - { - if (typeof(value)=="string" && value.indexOf("__FLASHERROR")==0) - { - var myErrorMessage = value.split("||"); - if(FABridge.refCount > 0 ) - { - FABridge.refCount--; - } - throw new Error(myErrorMessage[1]); - return value; - } - else - { - return value; - } - } -}; - -// The root ASProxy class that facades a flash object - -ASProxy = function(bridge, typeName) -{ - this.bridge = bridge; - this.typeName = typeName; - return this; -}; -//methods available on each ASProxy object -ASProxy.prototype = -{ - get: function(propName) - { - return this.bridge.deserialize(this.bridge.getPropertyFromAS(this.fb_instance_id, propName)); - }, - - set: function(propName, value) - { - this.bridge.setPropertyInAS(this.fb_instance_id, propName, value); - }, - - call: function(funcName, args) - { - this.bridge.callASMethod(this.fb_instance_id, funcName, args); - }, - - addRef: function() { - this.bridge.addRef(this); - }, - - release: function() { - this.bridge.release(this); - } -}; - -// Copyright: Hiroshi Ichikawa -// License: New BSD License -// Reference: http://dev.w3.org/html5/websockets/ -// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol - -(function() { - - if (window.WebSocket) return; - - var console = window.console; - if (!console) console = {log: function(){ }, error: function(){ }}; - - if (!swfobject.hasFlashPlayerVersion("9.0.0")) { - console.error("Flash Player is not installed."); - return; - } - if (location.protocol == "file:") { - console.error( - "WARNING: web-socket-js doesn't work in file:///... URL " + - "unless you set Flash Security Settings properly. " + - "Open the page via Web server i.e. http://..."); - } - - WebSocket = function(url, protocol, proxyHost, proxyPort, headers) { - var self = this; - self.readyState = WebSocket.CONNECTING; - self.bufferedAmount = 0; - // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc. - // Otherwise, when onopen fires immediately, onopen is called before it is set. - setTimeout(function() { - WebSocket.__addTask(function() { - self.__createFlash(url, protocol, proxyHost, proxyPort, headers); - }); - }, 1); - } - - WebSocket.prototype.__createFlash = function(url, protocol, proxyHost, proxyPort, headers) { - var self = this; - self.__flash = - WebSocket.__flash.create(url, protocol, proxyHost || null, proxyPort || 0, headers || null); - - self.__flash.addEventListener("open", function(fe) { - try { - self.readyState = self.__flash.getReadyState(); - if (self.__timer) clearInterval(self.__timer); - if (window.opera) { - // Workaround for weird behavior of Opera which sometimes drops events. - self.__timer = setInterval(function () { - self.__handleMessages(); - }, 500); - } - if (self.onopen) self.onopen(); - } catch (e) { - console.error(e.toString()); - } - }); - - self.__flash.addEventListener("close", function(fe) { - try { - self.readyState = self.__flash.getReadyState(); - if (self.__timer) clearInterval(self.__timer); - if (self.onclose) self.onclose(); - } catch (e) { - console.error(e.toString()); - } - }); - - self.__flash.addEventListener("message", function() { - try { - self.__handleMessages(); - } catch (e) { - console.error(e.toString()); - } - }); - - self.__flash.addEventListener("error", function(fe) { - try { - if (self.__timer) clearInterval(self.__timer); - if (self.onerror) self.onerror(); - } catch (e) { - console.error(e.toString()); - } - }); - - self.__flash.addEventListener("stateChange", function(fe) { - try { - self.readyState = self.__flash.getReadyState(); - self.bufferedAmount = fe.getBufferedAmount(); - } catch (e) { - console.error(e.toString()); - } - }); - - //console.log("[WebSocket] Flash object is ready"); - }; - - WebSocket.prototype.send = function(data) { - if (this.__flash) { - this.readyState = this.__flash.getReadyState(); - } - if (!this.__flash || this.readyState == WebSocket.CONNECTING) { - throw "INVALID_STATE_ERR: Web Socket connection has not been established"; - } - // We use encodeURIComponent() here, because FABridge doesn't work if - // the argument includes some characters. We don't use escape() here - // because of this: - // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions - // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't - // preserve all Unicode characters either e.g. "\uffff" in Firefox. - var result = this.__flash.send(encodeURIComponent(data)); - if (result < 0) { // success - return true; - } else { - this.bufferedAmount = result; - return false; - } - }; - - WebSocket.prototype.close = function() { - var self = this; - if (!self.__flash) return; - self.readyState = self.__flash.getReadyState(); - if (self.readyState == WebSocket.CLOSED || self.readyState == WebSocket.CLOSING) return; - self.__flash.close(); - // Sets/calls them manually here because Flash WebSocketConnection.close cannot fire events - // which causes weird error: - // > You are trying to call recursively into the Flash Player which is not allowed. - self.readyState = WebSocket.CLOSED; - if (self.__timer) clearInterval(self.__timer); - if (self.onclose) { - // Make it asynchronous so that it looks more like an actual - // close event - setTimeout(self.onclose, 1); - } - }; - - /** - * Implementation of {@link DOM 2 EventTarget Interface} - * - * @param {string} type - * @param {function} listener - * @param {boolean} useCapture !NB Not implemented yet - * @return void - */ - WebSocket.prototype.addEventListener = function(type, listener, useCapture) { - if (!('__events' in this)) { - this.__events = {}; - } - if (!(type in this.__events)) { - this.__events[type] = []; - if ('function' == typeof this['on' + type]) { - this.__events[type].defaultHandler = this['on' + type]; - this['on' + type] = this.__createEventHandler(this, type); - } - } - this.__events[type].push(listener); - }; - - /** - * Implementation of {@link DOM 2 EventTarget Interface} - * - * @param {string} type - * @param {function} listener - * @param {boolean} useCapture NB! Not implemented yet - * @return void - */ - WebSocket.prototype.removeEventListener = function(type, listener, useCapture) { - if (!('__events' in this)) { - this.__events = {}; - } - if (!(type in this.__events)) return; - for (var i = this.__events.length; i > -1; --i) { - if (listener === this.__events[type][i]) { - this.__events[type].splice(i, 1); - break; - } - } - }; - - /** - * Implementation of {@link DOM 2 EventTarget Interface} - * - * @param {WebSocketEvent} event - * @return void - */ - WebSocket.prototype.dispatchEvent = function(event) { - if (!('__events' in this)) throw 'UNSPECIFIED_EVENT_TYPE_ERR'; - if (!(event.type in this.__events)) throw 'UNSPECIFIED_EVENT_TYPE_ERR'; - - for (var i = 0, l = this.__events[event.type].length; i < l; ++ i) { - this.__events[event.type][i](event); - if (event.cancelBubble) break; - } - - if (false !== event.returnValue && - 'function' == typeof this.__events[event.type].defaultHandler) - { - this.__events[event.type].defaultHandler(event); - } - }; - - WebSocket.prototype.__handleMessages = function() { - // Gets data using readSocketData() instead of getting it from event object - // of Flash event. This is to make sure to keep message order. - // It seems sometimes Flash events don't arrive in the same order as they are sent. - var arr = this.__flash.readSocketData(); - for (var i = 0; i < arr.length; i++) { - var data = decodeURIComponent(arr[i]); - try { - if (this.onmessage) { - var e; - if (window.MessageEvent && !window.opera) { - e = document.createEvent("MessageEvent"); - e.initMessageEvent("message", false, false, data, null, null, window, null); - } else { // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes - e = {data: data}; - } - this.onmessage(e); - } - } catch (e) { - console.error(e.toString()); - } - } - }; - - /** - * @param {object} object - * @param {string} type - */ - WebSocket.prototype.__createEventHandler = function(object, type) { - return function(data) { - var event = new WebSocketEvent(); - event.initEvent(type, true, true); - event.target = event.currentTarget = object; - for (var key in data) { - event[key] = data[key]; - } - object.dispatchEvent(event, arguments); - }; - } - - /** - * Basic implementation of {@link DOM 2 EventInterface} - * - * @class - * @constructor - */ - function WebSocketEvent(){} - - /** - * - * @type boolean - */ - WebSocketEvent.prototype.cancelable = true; - - /** - * - * @type boolean - */ - WebSocketEvent.prototype.cancelBubble = false; - - /** - * - * @return void - */ - WebSocketEvent.prototype.preventDefault = function() { - if (this.cancelable) { - this.returnValue = false; - } - }; - - /** - * - * @return void - */ - WebSocketEvent.prototype.stopPropagation = function() { - this.cancelBubble = true; - }; - - /** - * - * @param {string} eventTypeArg - * @param {boolean} canBubbleArg - * @param {boolean} cancelableArg - * @return void - */ - WebSocketEvent.prototype.initEvent = function(eventTypeArg, canBubbleArg, cancelableArg) { - this.type = eventTypeArg; - this.cancelable = cancelableArg; - this.timeStamp = new Date(); - }; - - - WebSocket.CONNECTING = 0; - WebSocket.OPEN = 1; - WebSocket.CLOSING = 2; - WebSocket.CLOSED = 3; - - WebSocket.__tasks = []; - - WebSocket.__initialize = function() { - if (WebSocket.__swfLocation) { - // For backword compatibility. - window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation; - } - if (!window.WEB_SOCKET_SWF_LOCATION) { - console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf"); - return; - } - var container = document.createElement("div"); - container.id = "webSocketContainer"; - // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents - // Flash from loading at least in IE. So we move it out of the screen at (-100, -100). - // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash - // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is - // the best we can do as far as we know now. - container.style.position = "absolute"; - if (WebSocket.__isFlashLite()) { - container.style.left = "0px"; - container.style.top = "0px"; - } else { - container.style.left = "-100px"; - container.style.top = "-100px"; - } - var holder = document.createElement("div"); - holder.id = "webSocketFlash"; - container.appendChild(holder); - document.body.appendChild(container); - // See this article for hasPriority: - // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html - swfobject.embedSWF( - WEB_SOCKET_SWF_LOCATION, "webSocketFlash", - "1" /* width */, "1" /* height */, "9.0.0" /* SWF version */, - null, {bridgeName: "webSocket"}, {hasPriority: true, allowScriptAccess: "always"}, null, - function(e) { - if (!e.success) console.error("[WebSocket] swfobject.embedSWF failed"); - } - ); - FABridge.addInitializationCallback("webSocket", function() { - try { - //console.log("[WebSocket] FABridge initializad"); - WebSocket.__flash = FABridge.webSocket.root(); - WebSocket.__flash.setCallerUrl(location.href); - WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG); - for (var i = 0; i < WebSocket.__tasks.length; ++i) { - WebSocket.__tasks[i](); - } - WebSocket.__tasks = []; - } catch (e) { - console.error("[WebSocket] " + e.toString()); - } - }); - }; - - WebSocket.__addTask = function(task) { - if (WebSocket.__flash) { - task(); - } else { - WebSocket.__tasks.push(task); - } - }; - - WebSocket.__isFlashLite = function() { - if (!window.navigator || !window.navigator.mimeTypes) return false; - var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"]; - if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) return false; - return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false; - }; - - // called from Flash - window.webSocketLog = function(message) { - console.log(decodeURIComponent(message)); - }; - - // called from Flash - window.webSocketError = function(message) { - console.error(decodeURIComponent(message)); - }; - - if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) { - if (window.addEventListener) { - window.addEventListener("load", WebSocket.__initialize, false); - } else { - window.attachEvent("onload", WebSocket.__initialize); - } - } - -})(); diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/policyserver.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/policyserver.py deleted file mode 100644 index 6da9e0b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/policyserver.py +++ /dev/null @@ -1,22 +0,0 @@ -from gevent.server import StreamServer - -__all__ = ['FlashPolicyServer'] - - -class FlashPolicyServer(StreamServer): - policy = """ -""" - - noisy = False - - def __init__(self, listener=None, backlog=None, noisy=None): - if listener is None: - listener = ('0.0.0.0', 843) - if noisy is not None: - self.noisy = noisy - StreamServer.__init__(self, listener=listener, backlog=backlog) - - def handle(self, socket, address): - if self.noisy: - print 'Accepted connection from %s:%s' % address - socket.sendall(self.policy) diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/py.typed b/plotter-app/venv/lib/python3.8/site-packages/websocket/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/server.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/server.py deleted file mode 100644 index 1c42249..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/server.py +++ /dev/null @@ -1,134 +0,0 @@ -import sys -import traceback -from os.path import abspath, dirname, join, basename -from socket import error -from hashlib import md5 -from datetime import datetime -from gevent.pywsgi import WSGIHandler, WSGIServer - -from websocket.policyserver import FlashPolicyServer -from websocket import WebSocket - -import gevent -assert gevent.version_info >= (0, 13, 2), 'Newer version of gevent is required to run websocket.server' - -__all__ = ['WebsocketHandler', 'WebsocketServer'] - - -class WebsocketHandler(WSGIHandler): - - def run_application(self): - path = self.environ.get('PATH_INFO') - content_type = self.server.data_handlers.get(path) - if content_type is not None: - self.serve_file(basename(path), content_type) - return - - websocket_mode = False - - if WebSocket.is_socket(self.environ): - self.status = 'websocket' - self.log_request() - self.environ['websocket'] = WebSocket(self.environ, self.socket, self.rfile) - websocket_mode = True - try: - self.result = self.application(self.environ, self.start_response) - if self.result is not None: - self.process_result() - except: - websocket = self.environ.get('websocket') - if websocket is not None: - websocket.close() - raise - finally: - if websocket_mode: - # we own the socket now, make sure pywsgi does not try to read from it: - self.socket = None - - def serve_file(self, filename, content_type): - from websocket import data - path = join(dirname(abspath(data.__file__)), filename) - if self.server.etags.get(path) == (self.environ.get('HTTP_IF_NONE_MATCH') or 'x'): - self.start_response('304 Not Modifed', []) - self.write('') - return - try: - body = open(path).read() - except IOError, ex: - sys.stderr.write('Cannot open %s: %s\n' % (path, ex)) - self.start_response('404 Not Found', []) - self.write('') - return - etag = md5(body).hexdigest() - self.server.etags[path] = etag - self.start_response('200 OK', [('Content-Type', content_type), - ('Content-Length', str(len(body))), - ('Etag', etag)]) - self.write(body) - - -class WebsocketServer(WSGIServer): - - handler_class = WebsocketHandler - data_handlers = { - '/websocket/WebSocketMain.swf': 'application/x-shockwave-flash', - '/websocket/flashsocket.js': 'text/javascript' - } - etags = {} - - def __init__(self, listener, application=None, policy_server=True, backlog=None, - spawn='default', log='default', handler_class=None, environ=None, **ssl_args): - if policy_server is True: - self.policy_server = FlashPolicyServer() - elif isinstance(policy_server, tuple): - self.policy_server = FlashPolicyServer(policy_server) - elif policy_server: - raise TypeError('Expected tuple or boolean: %r' % (policy_server, )) - else: - self.policy_server = None - super(WebsocketServer, self).__init__(listener, application, backlog=backlog, spawn=spawn, log=log, - handler_class=handler_class, environ=environ, **ssl_args) - - def start_accepting(self): - self._start_policy_server() - super(WebsocketServer, self).start_accepting() - self.log_message('%s accepting connections on %s', self.__class__.__name__, _format_address(self)) - - def _start_policy_server(self): - server = self.policy_server - if server is not None: - try: - server.start() - self.log_message('%s accepting connections on %s', server.__class__.__name__, _format_address(server)) - except error, ex: - sys.stderr.write('FAILED to start %s on %s: %s\n' % (server.__class__.__name__, _format_address(server), ex)) - except Exception: - traceback.print_exc() - sys.stderr.write('FAILED to start %s on %s\n' % (server.__class__.__name__, _format_address(server))) - - def kill(self): - if self.policy_server is not None: - self.policy_server.kill() - super(WebsocketServer, self).kill() - - def log_message(self, message, *args): - log = self.log - if log is not None: - try: - message = message % args - except Exception: - traceback.print_exc() - try: - message = '%r %r' % (message, args) - except Exception: - traceback.print_exc() - log.write('%s %s\n' % (datetime.now().replace(microsecond=0), message)) - - -def _format_address(server): - try: - if server.server_host == '0.0.0.0': - return ':%s' % server.server_port - return '%s:%s' % (server.server_host, server.server_port) - except Exception: - traceback.print_exc() diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 16e8242..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/echo-server.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/echo-server.cpython-38.pyc deleted file mode 100644 index 433b1b8..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/echo-server.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/test_abnf.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/test_abnf.cpython-38.pyc deleted file mode 100644 index 98de696..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/test_abnf.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/test_app.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/test_app.cpython-38.pyc deleted file mode 100644 index 9539000..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/test_app.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/test_cookiejar.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/test_cookiejar.cpython-38.pyc deleted file mode 100644 index 34aceeb..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/test_cookiejar.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/test_http.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/test_http.cpython-38.pyc deleted file mode 100644 index 386a4c3..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/test_http.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/test_url.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/test_url.cpython-38.pyc deleted file mode 100644 index add565d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/test_url.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/test_websocket.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/test_websocket.cpython-38.pyc deleted file mode 100644 index 6504b30..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/__pycache__/test_websocket.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/data/header01.txt b/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/data/header01.txt deleted file mode 100644 index 3142b43..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/data/header01.txt +++ /dev/null @@ -1,6 +0,0 @@ -HTTP/1.1 101 WebSocket Protocol Handshake -Connection: Upgrade -Upgrade: WebSocket -Sec-WebSocket-Accept: Kxep+hNu9n51529fGidYu7a3wO0= -some_header: something - diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/data/header02.txt b/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/data/header02.txt deleted file mode 100644 index a9dd2ce..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/data/header02.txt +++ /dev/null @@ -1,6 +0,0 @@ -HTTP/1.1 101 WebSocket Protocol Handshake -Connection: Upgrade -Upgrade WebSocket -Sec-WebSocket-Accept: Kxep+hNu9n51529fGidYu7a3wO0= -some_header: something - diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/data/header03.txt b/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/data/header03.txt deleted file mode 100644 index 1a81dc7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/data/header03.txt +++ /dev/null @@ -1,8 +0,0 @@ -HTTP/1.1 101 WebSocket Protocol Handshake -Connection: Upgrade, Keep-Alive -Upgrade: WebSocket -Sec-WebSocket-Accept: Kxep+hNu9n51529fGidYu7a3wO0= -Set-Cookie: Token=ABCDE -Set-Cookie: Token=FGHIJ -some_header: something - diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/echo-server.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/echo-server.py deleted file mode 100644 index 5d1e870..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/echo-server.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -# From https://github.com/aaugustin/websockets/blob/main/example/echo.py - -import asyncio -import os - -import websockets - -LOCAL_WS_SERVER_PORT = int(os.environ.get("LOCAL_WS_SERVER_PORT", "8765")) - - -async def echo(websocket): - async for message in websocket: - await websocket.send(message) - - -async def main(): - async with websockets.serve(echo, "localhost", LOCAL_WS_SERVER_PORT): - await asyncio.Future() # run forever - - -asyncio.run(main()) diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/test_abnf.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/test_abnf.py deleted file mode 100644 index a749f13..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/test_abnf.py +++ /dev/null @@ -1,125 +0,0 @@ -# -*- coding: utf-8 -*- -# -import unittest - -from websocket._abnf import ABNF, frame_buffer -from websocket._exceptions import WebSocketProtocolException - -""" -test_abnf.py -websocket - WebSocket client library for Python - -Copyright 2024 engn33r - -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. -""" - - -class ABNFTest(unittest.TestCase): - def test_init(self): - a = ABNF(0, 0, 0, 0, opcode=ABNF.OPCODE_PING) - self.assertEqual(a.fin, 0) - self.assertEqual(a.rsv1, 0) - self.assertEqual(a.rsv2, 0) - self.assertEqual(a.rsv3, 0) - self.assertEqual(a.opcode, 9) - self.assertEqual(a.data, "") - a_bad = ABNF(0, 1, 0, 0, opcode=77) - self.assertEqual(a_bad.rsv1, 1) - self.assertEqual(a_bad.opcode, 77) - - def test_validate(self): - a_invalid_ping = ABNF(0, 0, 0, 0, opcode=ABNF.OPCODE_PING) - self.assertRaises( - WebSocketProtocolException, - a_invalid_ping.validate, - skip_utf8_validation=False, - ) - a_bad_rsv_value = ABNF(0, 1, 0, 0, opcode=ABNF.OPCODE_TEXT) - self.assertRaises( - WebSocketProtocolException, - a_bad_rsv_value.validate, - skip_utf8_validation=False, - ) - a_bad_opcode = ABNF(0, 0, 0, 0, opcode=77) - self.assertRaises( - WebSocketProtocolException, - a_bad_opcode.validate, - skip_utf8_validation=False, - ) - a_bad_close_frame = ABNF(0, 0, 0, 0, opcode=ABNF.OPCODE_CLOSE, data=b"\x01") - self.assertRaises( - WebSocketProtocolException, - a_bad_close_frame.validate, - skip_utf8_validation=False, - ) - a_bad_close_frame_2 = ABNF( - 0, 0, 0, 0, opcode=ABNF.OPCODE_CLOSE, data=b"\x01\x8a\xaa\xff\xdd" - ) - self.assertRaises( - WebSocketProtocolException, - a_bad_close_frame_2.validate, - skip_utf8_validation=False, - ) - a_bad_close_frame_3 = ABNF( - 0, 0, 0, 0, opcode=ABNF.OPCODE_CLOSE, data=b"\x03\xe7" - ) - self.assertRaises( - WebSocketProtocolException, - a_bad_close_frame_3.validate, - skip_utf8_validation=True, - ) - - def test_mask(self): - abnf_none_data = ABNF( - 0, 0, 0, 0, opcode=ABNF.OPCODE_PING, mask_value=1, data=None - ) - bytes_val = b"aaaa" - self.assertEqual(abnf_none_data._get_masked(bytes_val), bytes_val) - abnf_str_data = ABNF( - 0, 0, 0, 0, opcode=ABNF.OPCODE_PING, mask_value=1, data="a" - ) - self.assertEqual(abnf_str_data._get_masked(bytes_val), b"aaaa\x00") - - def test_format(self): - abnf_bad_rsv_bits = ABNF(2, 0, 0, 0, opcode=ABNF.OPCODE_TEXT) - self.assertRaises(ValueError, abnf_bad_rsv_bits.format) - abnf_bad_opcode = ABNF(0, 0, 0, 0, opcode=5) - self.assertRaises(ValueError, abnf_bad_opcode.format) - abnf_length_10 = ABNF(0, 0, 0, 0, opcode=ABNF.OPCODE_TEXT, data="abcdefghij") - self.assertEqual(b"\x01", abnf_length_10.format()[0].to_bytes(1, "big")) - self.assertEqual(b"\x8a", abnf_length_10.format()[1].to_bytes(1, "big")) - self.assertEqual("fin=0 opcode=1 data=abcdefghij", abnf_length_10.__str__()) - abnf_length_20 = ABNF( - 0, 0, 0, 0, opcode=ABNF.OPCODE_BINARY, data="abcdefghijabcdefghij" - ) - self.assertEqual(b"\x02", abnf_length_20.format()[0].to_bytes(1, "big")) - self.assertEqual(b"\x94", abnf_length_20.format()[1].to_bytes(1, "big")) - abnf_no_mask = ABNF( - 0, 0, 0, 0, opcode=ABNF.OPCODE_TEXT, mask_value=0, data=b"\x01\x8a\xcc" - ) - self.assertEqual(b"\x01\x03\x01\x8a\xcc", abnf_no_mask.format()) - - def test_frame_buffer(self): - fb = frame_buffer(0, True) - self.assertEqual(fb.recv, 0) - self.assertEqual(fb.skip_utf8_validation, True) - fb.clear - self.assertEqual(fb.header, None) - self.assertEqual(fb.length, None) - self.assertEqual(fb.mask_value, None) - self.assertEqual(fb.has_mask(), False) - - -if __name__ == "__main__": - unittest.main() diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/test_app.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/test_app.py deleted file mode 100644 index 18eace5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/test_app.py +++ /dev/null @@ -1,352 +0,0 @@ -# -*- coding: utf-8 -*- -# -import os -import os.path -import ssl -import threading -import unittest - -import websocket as ws - -""" -test_app.py -websocket - WebSocket client library for Python - -Copyright 2024 engn33r - -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. -""" - -# Skip test to access the internet unless TEST_WITH_INTERNET == 1 -TEST_WITH_INTERNET = os.environ.get("TEST_WITH_INTERNET", "0") == "1" -# Skip tests relying on local websockets server unless LOCAL_WS_SERVER_PORT != -1 -LOCAL_WS_SERVER_PORT = os.environ.get("LOCAL_WS_SERVER_PORT", "-1") -TEST_WITH_LOCAL_SERVER = LOCAL_WS_SERVER_PORT != "-1" -TRACEABLE = True - - -class WebSocketAppTest(unittest.TestCase): - class NotSetYet: - """A marker class for signalling that a value hasn't been set yet.""" - - def setUp(self): - ws.enableTrace(TRACEABLE) - - WebSocketAppTest.keep_running_open = WebSocketAppTest.NotSetYet() - WebSocketAppTest.keep_running_close = WebSocketAppTest.NotSetYet() - WebSocketAppTest.get_mask_key_id = WebSocketAppTest.NotSetYet() - WebSocketAppTest.on_error_data = WebSocketAppTest.NotSetYet() - - def tearDown(self): - WebSocketAppTest.keep_running_open = WebSocketAppTest.NotSetYet() - WebSocketAppTest.keep_running_close = WebSocketAppTest.NotSetYet() - WebSocketAppTest.get_mask_key_id = WebSocketAppTest.NotSetYet() - WebSocketAppTest.on_error_data = WebSocketAppTest.NotSetYet() - - def close(self): - pass - - @unittest.skipUnless( - TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" - ) - def test_keep_running(self): - """A WebSocketApp should keep running as long as its self.keep_running - is not False (in the boolean context). - """ - - def on_open(self, *args, **kwargs): - """Set the keep_running flag for later inspection and immediately - close the connection. - """ - self.send("hello!") - WebSocketAppTest.keep_running_open = self.keep_running - self.keep_running = False - - def on_message(_, message): - print(message) - self.close() - - def on_close(self, *args, **kwargs): - """Set the keep_running flag for the test to use.""" - WebSocketAppTest.keep_running_close = self.keep_running - - app = ws.WebSocketApp( - f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}", - on_open=on_open, - on_close=on_close, - on_message=on_message, - ) - app.run_forever() - - # @unittest.skipUnless(TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled") - @unittest.skipUnless(False, "Test disabled for now (requires rel)") - def test_run_forever_dispatcher(self): - """A WebSocketApp should keep running as long as its self.keep_running - is not False (in the boolean context). - """ - - def on_open(self, *args, **kwargs): - """Send a message, receive, and send one more""" - self.send("hello!") - self.recv() - self.send("goodbye!") - - def on_message(_, message): - print(message) - self.close() - - app = ws.WebSocketApp( - f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}", - on_open=on_open, - on_message=on_message, - ) - app.run_forever(dispatcher="Dispatcher") # doesn't work - - # app.run_forever(dispatcher=rel) # would work - # rel.dispatch() - - @unittest.skipUnless( - TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" - ) - def test_run_forever_teardown_clean_exit(self): - """The WebSocketApp.run_forever() method should return `False` when the application ends gracefully.""" - app = ws.WebSocketApp(f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}") - threading.Timer(interval=0.2, function=app.close).start() - teardown = app.run_forever() - self.assertEqual(teardown, False) - - @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def test_sock_mask_key(self): - """A WebSocketApp should forward the received mask_key function down - to the actual socket. - """ - - def my_mask_key_func(): - return "\x00\x00\x00\x00" - - app = ws.WebSocketApp( - "wss://api-pub.bitfinex.com/ws/1", get_mask_key=my_mask_key_func - ) - - # if numpy is installed, this assertion fail - # Note: We can't use 'is' for comparing the functions directly, need to use 'id'. - self.assertEqual(id(app.get_mask_key), id(my_mask_key_func)) - - @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def test_invalid_ping_interval_ping_timeout(self): - """Test exception handling if ping_interval < ping_timeout""" - - def on_ping(app, _): - print("Got a ping!") - app.close() - - def on_pong(app, _): - print("Got a pong! No need to respond") - app.close() - - app = ws.WebSocketApp( - "wss://api-pub.bitfinex.com/ws/1", on_ping=on_ping, on_pong=on_pong - ) - self.assertRaises( - ws.WebSocketException, - app.run_forever, - ping_interval=1, - ping_timeout=2, - sslopt={"cert_reqs": ssl.CERT_NONE}, - ) - - @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def test_ping_interval(self): - """Test WebSocketApp proper ping functionality""" - - def on_ping(app, _): - print("Got a ping!") - app.close() - - def on_pong(app, _): - print("Got a pong! No need to respond") - app.close() - - app = ws.WebSocketApp( - "wss://api-pub.bitfinex.com/ws/1", on_ping=on_ping, on_pong=on_pong - ) - app.run_forever( - ping_interval=2, ping_timeout=1, sslopt={"cert_reqs": ssl.CERT_NONE} - ) - - @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def test_opcode_close(self): - """Test WebSocketApp close opcode""" - - app = ws.WebSocketApp("wss://tsock.us1.twilio.com/v3/wsconnect") - app.run_forever(ping_interval=2, ping_timeout=1, ping_payload="Ping payload") - - # This is commented out because the URL no longer responds in the expected way - # @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - # def testOpcodeBinary(self): - # """ Test WebSocketApp binary opcode - # """ - # app = ws.WebSocketApp('wss://streaming.vn.teslamotors.com/streaming/') - # app.run_forever(ping_interval=2, ping_timeout=1, ping_payload="Ping payload") - - @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def test_bad_ping_interval(self): - """A WebSocketApp handling of negative ping_interval""" - app = ws.WebSocketApp("wss://api-pub.bitfinex.com/ws/1") - self.assertRaises( - ws.WebSocketException, - app.run_forever, - ping_interval=-5, - sslopt={"cert_reqs": ssl.CERT_NONE}, - ) - - @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def test_bad_ping_timeout(self): - """A WebSocketApp handling of negative ping_timeout""" - app = ws.WebSocketApp("wss://api-pub.bitfinex.com/ws/1") - self.assertRaises( - ws.WebSocketException, - app.run_forever, - ping_timeout=-3, - sslopt={"cert_reqs": ssl.CERT_NONE}, - ) - - @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def test_close_status_code(self): - """Test extraction of close frame status code and close reason in WebSocketApp""" - - def on_close(wsapp, close_status_code, close_msg): - print("on_close reached") - - app = ws.WebSocketApp( - "wss://tsock.us1.twilio.com/v3/wsconnect", on_close=on_close - ) - closeframe = ws.ABNF( - opcode=ws.ABNF.OPCODE_CLOSE, data=b"\x03\xe8no-init-from-client" - ) - self.assertEqual([1000, "no-init-from-client"], app._get_close_args(closeframe)) - - closeframe = ws.ABNF(opcode=ws.ABNF.OPCODE_CLOSE, data=b"") - self.assertEqual([None, None], app._get_close_args(closeframe)) - - app2 = ws.WebSocketApp("wss://tsock.us1.twilio.com/v3/wsconnect") - closeframe = ws.ABNF(opcode=ws.ABNF.OPCODE_CLOSE, data=b"") - self.assertEqual([None, None], app2._get_close_args(closeframe)) - - self.assertRaises( - ws.WebSocketConnectionClosedException, - app.send, - data="test if connection is closed", - ) - - @unittest.skipUnless( - TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" - ) - def test_callback_function_exception(self): - """Test callback function exception handling""" - - exc = None - passed_app = None - - def on_open(app): - raise RuntimeError("Callback failed") - - def on_error(app, err): - nonlocal passed_app - passed_app = app - nonlocal exc - exc = err - - def on_pong(app, _): - app.close() - - app = ws.WebSocketApp( - f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}", - on_open=on_open, - on_error=on_error, - on_pong=on_pong, - ) - app.run_forever(ping_interval=2, ping_timeout=1) - - self.assertEqual(passed_app, app) - self.assertIsInstance(exc, RuntimeError) - self.assertEqual(str(exc), "Callback failed") - - @unittest.skipUnless( - TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" - ) - def test_callback_method_exception(self): - """Test callback method exception handling""" - - class Callbacks: - def __init__(self): - self.exc = None - self.passed_app = None - self.app = ws.WebSocketApp( - f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}", - on_open=self.on_open, - on_error=self.on_error, - on_pong=self.on_pong, - ) - self.app.run_forever(ping_interval=2, ping_timeout=1) - - def on_open(self, _): - raise RuntimeError("Callback failed") - - def on_error(self, app, err): - self.passed_app = app - self.exc = err - - def on_pong(self, app, _): - app.close() - - callbacks = Callbacks() - - self.assertEqual(callbacks.passed_app, callbacks.app) - self.assertIsInstance(callbacks.exc, RuntimeError) - self.assertEqual(str(callbacks.exc), "Callback failed") - - @unittest.skipUnless( - TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" - ) - def test_reconnect(self): - """Test reconnect""" - pong_count = 0 - exc = None - - def on_error(_, err): - nonlocal exc - exc = err - - def on_pong(app, _): - nonlocal pong_count - pong_count += 1 - if pong_count == 1: - # First pong, shutdown socket, enforce read error - app.sock.shutdown() - if pong_count >= 2: - # Got second pong after reconnect - app.close() - - app = ws.WebSocketApp( - f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}", on_pong=on_pong, on_error=on_error - ) - app.run_forever(ping_interval=2, ping_timeout=1, reconnect=3) - - self.assertEqual(pong_count, 2) - self.assertIsInstance(exc, ws.WebSocketTimeoutException) - self.assertEqual(str(exc), "ping/pong timed out") - - -if __name__ == "__main__": - unittest.main() diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/test_cookiejar.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/test_cookiejar.py deleted file mode 100644 index 67eddb6..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/test_cookiejar.py +++ /dev/null @@ -1,123 +0,0 @@ -import unittest - -from websocket._cookiejar import SimpleCookieJar - -""" -test_cookiejar.py -websocket - WebSocket client library for Python - -Copyright 2024 engn33r - -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. -""" - - -class CookieJarTest(unittest.TestCase): - def test_add(self): - cookie_jar = SimpleCookieJar() - cookie_jar.add("") - self.assertFalse( - cookie_jar.jar, "Cookie with no domain should not be added to the jar" - ) - - cookie_jar = SimpleCookieJar() - cookie_jar.add("a=b") - self.assertFalse( - cookie_jar.jar, "Cookie with no domain should not be added to the jar" - ) - - cookie_jar = SimpleCookieJar() - cookie_jar.add("a=b; domain=.abc") - self.assertTrue(".abc" in cookie_jar.jar) - - cookie_jar = SimpleCookieJar() - cookie_jar.add("a=b; domain=abc") - self.assertTrue(".abc" in cookie_jar.jar) - self.assertTrue("abc" not in cookie_jar.jar) - - cookie_jar = SimpleCookieJar() - cookie_jar.add("a=b; c=d; domain=abc") - self.assertEqual(cookie_jar.get("abc"), "a=b; c=d") - self.assertEqual(cookie_jar.get(None), "") - - cookie_jar = SimpleCookieJar() - cookie_jar.add("a=b; c=d; domain=abc") - cookie_jar.add("e=f; domain=abc") - self.assertEqual(cookie_jar.get("abc"), "a=b; c=d; e=f") - - cookie_jar = SimpleCookieJar() - cookie_jar.add("a=b; c=d; domain=abc") - cookie_jar.add("e=f; domain=.abc") - self.assertEqual(cookie_jar.get("abc"), "a=b; c=d; e=f") - - cookie_jar = SimpleCookieJar() - cookie_jar.add("a=b; c=d; domain=abc") - cookie_jar.add("e=f; domain=xyz") - self.assertEqual(cookie_jar.get("abc"), "a=b; c=d") - self.assertEqual(cookie_jar.get("xyz"), "e=f") - self.assertEqual(cookie_jar.get("something"), "") - - def test_set(self): - cookie_jar = SimpleCookieJar() - cookie_jar.set("a=b") - self.assertFalse( - cookie_jar.jar, "Cookie with no domain should not be added to the jar" - ) - - cookie_jar = SimpleCookieJar() - cookie_jar.set("a=b; domain=.abc") - self.assertTrue(".abc" in cookie_jar.jar) - - cookie_jar = SimpleCookieJar() - cookie_jar.set("a=b; domain=abc") - self.assertTrue(".abc" in cookie_jar.jar) - self.assertTrue("abc" not in cookie_jar.jar) - - cookie_jar = SimpleCookieJar() - cookie_jar.set("a=b; c=d; domain=abc") - self.assertEqual(cookie_jar.get("abc"), "a=b; c=d") - - cookie_jar = SimpleCookieJar() - cookie_jar.set("a=b; c=d; domain=abc") - cookie_jar.set("e=f; domain=abc") - self.assertEqual(cookie_jar.get("abc"), "e=f") - - cookie_jar = SimpleCookieJar() - cookie_jar.set("a=b; c=d; domain=abc") - cookie_jar.set("e=f; domain=.abc") - self.assertEqual(cookie_jar.get("abc"), "e=f") - - cookie_jar = SimpleCookieJar() - cookie_jar.set("a=b; c=d; domain=abc") - cookie_jar.set("e=f; domain=xyz") - self.assertEqual(cookie_jar.get("abc"), "a=b; c=d") - self.assertEqual(cookie_jar.get("xyz"), "e=f") - self.assertEqual(cookie_jar.get("something"), "") - - def test_get(self): - cookie_jar = SimpleCookieJar() - cookie_jar.set("a=b; c=d; domain=abc.com") - self.assertEqual(cookie_jar.get("abc.com"), "a=b; c=d") - self.assertEqual(cookie_jar.get("x.abc.com"), "a=b; c=d") - self.assertEqual(cookie_jar.get("abc.com.es"), "") - self.assertEqual(cookie_jar.get("xabc.com"), "") - - cookie_jar.set("a=b; c=d; domain=.abc.com") - self.assertEqual(cookie_jar.get("abc.com"), "a=b; c=d") - self.assertEqual(cookie_jar.get("x.abc.com"), "a=b; c=d") - self.assertEqual(cookie_jar.get("abc.com.es"), "") - self.assertEqual(cookie_jar.get("xabc.com"), "") - - -if __name__ == "__main__": - unittest.main() diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/test_http.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/test_http.py deleted file mode 100644 index f495e63..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/test_http.py +++ /dev/null @@ -1,370 +0,0 @@ -# -*- coding: utf-8 -*- -# -import os -import os.path -import socket -import ssl -import unittest - -import websocket -from websocket._exceptions import WebSocketProxyException, WebSocketException -from websocket._http import ( - _get_addrinfo_list, - _start_proxied_socket, - _tunnel, - connect, - proxy_info, - read_headers, - HAVE_PYTHON_SOCKS, -) - -""" -test_http.py -websocket - WebSocket client library for Python - -Copyright 2024 engn33r - -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. -""" - -try: - from python_socks._errors import ProxyConnectionError, ProxyError, ProxyTimeoutError -except: - from websocket._http import ProxyConnectionError, ProxyError, ProxyTimeoutError - -# Skip test to access the internet unless TEST_WITH_INTERNET == 1 -TEST_WITH_INTERNET = os.environ.get("TEST_WITH_INTERNET", "0") == "1" -TEST_WITH_PROXY = os.environ.get("TEST_WITH_PROXY", "0") == "1" -# Skip tests relying on local websockets server unless LOCAL_WS_SERVER_PORT != -1 -LOCAL_WS_SERVER_PORT = os.environ.get("LOCAL_WS_SERVER_PORT", "-1") -TEST_WITH_LOCAL_SERVER = LOCAL_WS_SERVER_PORT != "-1" - - -class SockMock: - def __init__(self): - self.data = [] - self.sent = [] - - def add_packet(self, data): - self.data.append(data) - - def gettimeout(self): - return None - - def recv(self, bufsize): - if self.data: - e = self.data.pop(0) - if isinstance(e, Exception): - raise e - if len(e) > bufsize: - self.data.insert(0, e[bufsize:]) - return e[:bufsize] - - def send(self, data): - self.sent.append(data) - return len(data) - - def close(self): - pass - - -class HeaderSockMock(SockMock): - def __init__(self, fname): - SockMock.__init__(self) - path = os.path.join(os.path.dirname(__file__), fname) - with open(path, "rb") as f: - self.add_packet(f.read()) - - -class OptsList: - def __init__(self): - self.timeout = 1 - self.sockopt = [] - self.sslopt = {"cert_reqs": ssl.CERT_NONE} - - -class HttpTest(unittest.TestCase): - def test_read_header(self): - status, header, _ = read_headers(HeaderSockMock("data/header01.txt")) - self.assertEqual(status, 101) - self.assertEqual(header["connection"], "Upgrade") - # header02.txt is intentionally malformed - self.assertRaises( - WebSocketException, read_headers, HeaderSockMock("data/header02.txt") - ) - - def test_tunnel(self): - self.assertRaises( - WebSocketProxyException, - _tunnel, - HeaderSockMock("data/header01.txt"), - "example.com", - 80, - ("username", "password"), - ) - self.assertRaises( - WebSocketProxyException, - _tunnel, - HeaderSockMock("data/header02.txt"), - "example.com", - 80, - ("username", "password"), - ) - - @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def test_connect(self): - # Not currently testing an actual proxy connection, so just check whether proxy errors are raised. This requires internet for a DNS lookup - if HAVE_PYTHON_SOCKS: - # Need this check, otherwise case where python_socks is not installed triggers - # websocket._exceptions.WebSocketException: Python Socks is needed for SOCKS proxying but is not available - self.assertRaises( - (ProxyTimeoutError, OSError), - _start_proxied_socket, - "wss://example.com", - OptsList(), - proxy_info( - http_proxy_host="example.com", - http_proxy_port="8080", - proxy_type="socks4", - http_proxy_timeout=1, - ), - ) - self.assertRaises( - (ProxyTimeoutError, OSError), - _start_proxied_socket, - "wss://example.com", - OptsList(), - proxy_info( - http_proxy_host="example.com", - http_proxy_port="8080", - proxy_type="socks4a", - http_proxy_timeout=1, - ), - ) - self.assertRaises( - (ProxyTimeoutError, OSError), - _start_proxied_socket, - "wss://example.com", - OptsList(), - proxy_info( - http_proxy_host="example.com", - http_proxy_port="8080", - proxy_type="socks5", - http_proxy_timeout=1, - ), - ) - self.assertRaises( - (ProxyTimeoutError, OSError), - _start_proxied_socket, - "wss://example.com", - OptsList(), - proxy_info( - http_proxy_host="example.com", - http_proxy_port="8080", - proxy_type="socks5h", - http_proxy_timeout=1, - ), - ) - self.assertRaises( - ProxyConnectionError, - connect, - "wss://example.com", - OptsList(), - proxy_info( - http_proxy_host="127.0.0.1", - http_proxy_port=9999, - proxy_type="socks4", - http_proxy_timeout=1, - ), - None, - ) - - self.assertRaises( - TypeError, - _get_addrinfo_list, - None, - 80, - True, - proxy_info( - http_proxy_host="127.0.0.1", http_proxy_port="9999", proxy_type="http" - ), - ) - self.assertRaises( - TypeError, - _get_addrinfo_list, - None, - 80, - True, - proxy_info( - http_proxy_host="127.0.0.1", http_proxy_port="9999", proxy_type="http" - ), - ) - self.assertRaises( - socket.timeout, - connect, - "wss://google.com", - OptsList(), - proxy_info( - http_proxy_host="8.8.8.8", - http_proxy_port=9999, - proxy_type="http", - http_proxy_timeout=1, - ), - None, - ) - self.assertEqual( - connect( - "wss://google.com", - OptsList(), - proxy_info( - http_proxy_host="8.8.8.8", http_proxy_port=8080, proxy_type="http" - ), - True, - ), - (True, ("google.com", 443, "/")), - ) - # The following test fails on Mac OS with a gaierror, not an OverflowError - # self.assertRaises(OverflowError, connect, "wss://example.com", OptsList(), proxy_info(http_proxy_host="127.0.0.1", http_proxy_port=99999, proxy_type="socks4", timeout=2), False) - - @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - @unittest.skipUnless( - TEST_WITH_PROXY, "This test requires a HTTP proxy to be running on port 8899" - ) - @unittest.skipUnless( - TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" - ) - def test_proxy_connect(self): - ws = websocket.WebSocket() - ws.connect( - f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}", - http_proxy_host="127.0.0.1", - http_proxy_port="8899", - proxy_type="http", - ) - ws.send("Hello, Server") - server_response = ws.recv() - self.assertEqual(server_response, "Hello, Server") - # self.assertEqual(_start_proxied_socket("wss://api.bitfinex.com/ws/2", OptsList(), proxy_info(http_proxy_host="127.0.0.1", http_proxy_port="8899", proxy_type="http"))[1], ("api.bitfinex.com", 443, '/ws/2')) - self.assertEqual( - _get_addrinfo_list( - "api.bitfinex.com", - 443, - True, - proxy_info( - http_proxy_host="127.0.0.1", - http_proxy_port="8899", - proxy_type="http", - ), - ), - ( - socket.getaddrinfo( - "127.0.0.1", 8899, 0, socket.SOCK_STREAM, socket.SOL_TCP - ), - True, - None, - ), - ) - self.assertEqual( - connect( - "wss://api.bitfinex.com/ws/2", - OptsList(), - proxy_info( - http_proxy_host="127.0.0.1", http_proxy_port=8899, proxy_type="http" - ), - None, - )[1], - ("api.bitfinex.com", 443, "/ws/2"), - ) - # TODO: Test SOCKS4 and SOCK5 proxies with unit tests - - @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def test_sslopt(self): - ssloptions = { - "check_hostname": False, - "server_hostname": "ServerName", - "ssl_version": ssl.PROTOCOL_TLS_CLIENT, - "ciphers": "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:\ - TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:\ - ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:\ - ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\ - DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:\ - ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:\ - ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:\ - DHE-RSA-AES256-SHA256:ECDHE-ECDSA-AES128-SHA256:\ - ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:\ - ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA", - "ecdh_curve": "prime256v1", - } - ws_ssl1 = websocket.WebSocket(sslopt=ssloptions) - ws_ssl1.connect("wss://api.bitfinex.com/ws/2") - ws_ssl1.send("Hello") - ws_ssl1.close() - - ws_ssl2 = websocket.WebSocket(sslopt={"check_hostname": True}) - ws_ssl2.connect("wss://api.bitfinex.com/ws/2") - ws_ssl2.close - - def test_proxy_info(self): - self.assertEqual( - proxy_info( - http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http" - ).proxy_protocol, - "http", - ) - self.assertRaises( - ProxyError, - proxy_info, - http_proxy_host="127.0.0.1", - http_proxy_port="8080", - proxy_type="badval", - ) - self.assertEqual( - proxy_info( - http_proxy_host="example.com", http_proxy_port="8080", proxy_type="http" - ).proxy_host, - "example.com", - ) - self.assertEqual( - proxy_info( - http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http" - ).proxy_port, - "8080", - ) - self.assertEqual( - proxy_info( - http_proxy_host="127.0.0.1", http_proxy_port="8080", proxy_type="http" - ).auth, - None, - ) - self.assertEqual( - proxy_info( - http_proxy_host="127.0.0.1", - http_proxy_port="8080", - proxy_type="http", - http_proxy_auth=("my_username123", "my_pass321"), - ).auth[0], - "my_username123", - ) - self.assertEqual( - proxy_info( - http_proxy_host="127.0.0.1", - http_proxy_port="8080", - proxy_type="http", - http_proxy_auth=("my_username123", "my_pass321"), - ).auth[1], - "my_pass321", - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/test_url.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/test_url.py deleted file mode 100644 index 110fdfa..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/test_url.py +++ /dev/null @@ -1,464 +0,0 @@ -# -*- coding: utf-8 -*- -# -import os -import unittest - -from websocket._url import ( - _is_address_in_network, - _is_no_proxy_host, - get_proxy_info, - parse_url, -) -from websocket._exceptions import WebSocketProxyException - -""" -test_url.py -websocket - WebSocket client library for Python - -Copyright 2024 engn33r - -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. -""" - - -class UrlTest(unittest.TestCase): - def test_address_in_network(self): - self.assertTrue(_is_address_in_network("127.0.0.1", "127.0.0.0/8")) - self.assertTrue(_is_address_in_network("127.1.0.1", "127.0.0.0/8")) - self.assertFalse(_is_address_in_network("127.1.0.1", "127.0.0.0/24")) - - def test_parse_url(self): - p = parse_url("ws://www.example.com/r") - self.assertEqual(p[0], "www.example.com") - self.assertEqual(p[1], 80) - self.assertEqual(p[2], "/r") - self.assertEqual(p[3], False) - - p = parse_url("ws://www.example.com/r/") - self.assertEqual(p[0], "www.example.com") - self.assertEqual(p[1], 80) - self.assertEqual(p[2], "/r/") - self.assertEqual(p[3], False) - - p = parse_url("ws://www.example.com/") - self.assertEqual(p[0], "www.example.com") - self.assertEqual(p[1], 80) - self.assertEqual(p[2], "/") - self.assertEqual(p[3], False) - - p = parse_url("ws://www.example.com") - self.assertEqual(p[0], "www.example.com") - self.assertEqual(p[1], 80) - self.assertEqual(p[2], "/") - self.assertEqual(p[3], False) - - p = parse_url("ws://www.example.com:8080/r") - self.assertEqual(p[0], "www.example.com") - self.assertEqual(p[1], 8080) - self.assertEqual(p[2], "/r") - self.assertEqual(p[3], False) - - p = parse_url("ws://www.example.com:8080/") - self.assertEqual(p[0], "www.example.com") - self.assertEqual(p[1], 8080) - self.assertEqual(p[2], "/") - self.assertEqual(p[3], False) - - p = parse_url("ws://www.example.com:8080") - self.assertEqual(p[0], "www.example.com") - self.assertEqual(p[1], 8080) - self.assertEqual(p[2], "/") - self.assertEqual(p[3], False) - - p = parse_url("wss://www.example.com:8080/r") - self.assertEqual(p[0], "www.example.com") - self.assertEqual(p[1], 8080) - self.assertEqual(p[2], "/r") - self.assertEqual(p[3], True) - - p = parse_url("wss://www.example.com:8080/r?key=value") - self.assertEqual(p[0], "www.example.com") - self.assertEqual(p[1], 8080) - self.assertEqual(p[2], "/r?key=value") - self.assertEqual(p[3], True) - - self.assertRaises(ValueError, parse_url, "http://www.example.com/r") - - p = parse_url("ws://[2a03:4000:123:83::3]/r") - self.assertEqual(p[0], "2a03:4000:123:83::3") - self.assertEqual(p[1], 80) - self.assertEqual(p[2], "/r") - self.assertEqual(p[3], False) - - p = parse_url("ws://[2a03:4000:123:83::3]:8080/r") - self.assertEqual(p[0], "2a03:4000:123:83::3") - self.assertEqual(p[1], 8080) - self.assertEqual(p[2], "/r") - self.assertEqual(p[3], False) - - p = parse_url("wss://[2a03:4000:123:83::3]/r") - self.assertEqual(p[0], "2a03:4000:123:83::3") - self.assertEqual(p[1], 443) - self.assertEqual(p[2], "/r") - self.assertEqual(p[3], True) - - p = parse_url("wss://[2a03:4000:123:83::3]:8080/r") - self.assertEqual(p[0], "2a03:4000:123:83::3") - self.assertEqual(p[1], 8080) - self.assertEqual(p[2], "/r") - self.assertEqual(p[3], True) - - -class IsNoProxyHostTest(unittest.TestCase): - def setUp(self): - self.no_proxy = os.environ.get("no_proxy", None) - if "no_proxy" in os.environ: - del os.environ["no_proxy"] - - def tearDown(self): - if self.no_proxy: - os.environ["no_proxy"] = self.no_proxy - elif "no_proxy" in os.environ: - del os.environ["no_proxy"] - - def test_match_all(self): - self.assertTrue(_is_no_proxy_host("any.websocket.org", ["*"])) - self.assertTrue(_is_no_proxy_host("192.168.0.1", ["*"])) - self.assertFalse(_is_no_proxy_host("192.168.0.1", ["192.168.1.1"])) - self.assertFalse( - _is_no_proxy_host("any.websocket.org", ["other.websocket.org"]) - ) - self.assertTrue( - _is_no_proxy_host("any.websocket.org", ["other.websocket.org", "*"]) - ) - os.environ["no_proxy"] = "*" - self.assertTrue(_is_no_proxy_host("any.websocket.org", None)) - self.assertTrue(_is_no_proxy_host("192.168.0.1", None)) - os.environ["no_proxy"] = "other.websocket.org, *" - self.assertTrue(_is_no_proxy_host("any.websocket.org", None)) - - def test_ip_address(self): - self.assertTrue(_is_no_proxy_host("127.0.0.1", ["127.0.0.1"])) - self.assertFalse(_is_no_proxy_host("127.0.0.2", ["127.0.0.1"])) - self.assertTrue( - _is_no_proxy_host("127.0.0.1", ["other.websocket.org", "127.0.0.1"]) - ) - self.assertFalse( - _is_no_proxy_host("127.0.0.2", ["other.websocket.org", "127.0.0.1"]) - ) - os.environ["no_proxy"] = "127.0.0.1" - self.assertTrue(_is_no_proxy_host("127.0.0.1", None)) - self.assertFalse(_is_no_proxy_host("127.0.0.2", None)) - os.environ["no_proxy"] = "other.websocket.org, 127.0.0.1" - self.assertTrue(_is_no_proxy_host("127.0.0.1", None)) - self.assertFalse(_is_no_proxy_host("127.0.0.2", None)) - - def test_ip_address_in_range(self): - self.assertTrue(_is_no_proxy_host("127.0.0.1", ["127.0.0.0/8"])) - self.assertTrue(_is_no_proxy_host("127.0.0.2", ["127.0.0.0/8"])) - self.assertFalse(_is_no_proxy_host("127.1.0.1", ["127.0.0.0/24"])) - os.environ["no_proxy"] = "127.0.0.0/8" - self.assertTrue(_is_no_proxy_host("127.0.0.1", None)) - self.assertTrue(_is_no_proxy_host("127.0.0.2", None)) - os.environ["no_proxy"] = "127.0.0.0/24" - self.assertFalse(_is_no_proxy_host("127.1.0.1", None)) - - def test_hostname_match(self): - self.assertTrue(_is_no_proxy_host("my.websocket.org", ["my.websocket.org"])) - self.assertTrue( - _is_no_proxy_host( - "my.websocket.org", ["other.websocket.org", "my.websocket.org"] - ) - ) - self.assertFalse(_is_no_proxy_host("my.websocket.org", ["other.websocket.org"])) - os.environ["no_proxy"] = "my.websocket.org" - self.assertTrue(_is_no_proxy_host("my.websocket.org", None)) - self.assertFalse(_is_no_proxy_host("other.websocket.org", None)) - os.environ["no_proxy"] = "other.websocket.org, my.websocket.org" - self.assertTrue(_is_no_proxy_host("my.websocket.org", None)) - - def test_hostname_match_domain(self): - self.assertTrue(_is_no_proxy_host("any.websocket.org", [".websocket.org"])) - self.assertTrue(_is_no_proxy_host("my.other.websocket.org", [".websocket.org"])) - self.assertTrue( - _is_no_proxy_host( - "any.websocket.org", ["my.websocket.org", ".websocket.org"] - ) - ) - self.assertFalse(_is_no_proxy_host("any.websocket.com", [".websocket.org"])) - os.environ["no_proxy"] = ".websocket.org" - self.assertTrue(_is_no_proxy_host("any.websocket.org", None)) - self.assertTrue(_is_no_proxy_host("my.other.websocket.org", None)) - self.assertFalse(_is_no_proxy_host("any.websocket.com", None)) - os.environ["no_proxy"] = "my.websocket.org, .websocket.org" - self.assertTrue(_is_no_proxy_host("any.websocket.org", None)) - - -class ProxyInfoTest(unittest.TestCase): - def setUp(self): - self.http_proxy = os.environ.get("http_proxy", None) - self.https_proxy = os.environ.get("https_proxy", None) - self.no_proxy = os.environ.get("no_proxy", None) - if "http_proxy" in os.environ: - del os.environ["http_proxy"] - if "https_proxy" in os.environ: - del os.environ["https_proxy"] - if "no_proxy" in os.environ: - del os.environ["no_proxy"] - - def tearDown(self): - if self.http_proxy: - os.environ["http_proxy"] = self.http_proxy - elif "http_proxy" in os.environ: - del os.environ["http_proxy"] - - if self.https_proxy: - os.environ["https_proxy"] = self.https_proxy - elif "https_proxy" in os.environ: - del os.environ["https_proxy"] - - if self.no_proxy: - os.environ["no_proxy"] = self.no_proxy - elif "no_proxy" in os.environ: - del os.environ["no_proxy"] - - def test_proxy_from_args(self): - self.assertRaises( - WebSocketProxyException, - get_proxy_info, - "echo.websocket.events", - False, - proxy_host="localhost", - ) - self.assertEqual( - get_proxy_info( - "echo.websocket.events", False, proxy_host="localhost", proxy_port=3128 - ), - ("localhost", 3128, None), - ) - self.assertEqual( - get_proxy_info( - "echo.websocket.events", True, proxy_host="localhost", proxy_port=3128 - ), - ("localhost", 3128, None), - ) - - self.assertEqual( - get_proxy_info( - "echo.websocket.events", - False, - proxy_host="localhost", - proxy_port=9001, - proxy_auth=("a", "b"), - ), - ("localhost", 9001, ("a", "b")), - ) - self.assertEqual( - get_proxy_info( - "echo.websocket.events", - False, - proxy_host="localhost", - proxy_port=3128, - proxy_auth=("a", "b"), - ), - ("localhost", 3128, ("a", "b")), - ) - self.assertEqual( - get_proxy_info( - "echo.websocket.events", - True, - proxy_host="localhost", - proxy_port=8765, - proxy_auth=("a", "b"), - ), - ("localhost", 8765, ("a", "b")), - ) - self.assertEqual( - get_proxy_info( - "echo.websocket.events", - True, - proxy_host="localhost", - proxy_port=3128, - proxy_auth=("a", "b"), - ), - ("localhost", 3128, ("a", "b")), - ) - - self.assertEqual( - get_proxy_info( - "echo.websocket.events", - True, - proxy_host="localhost", - proxy_port=3128, - no_proxy=["example.com"], - proxy_auth=("a", "b"), - ), - ("localhost", 3128, ("a", "b")), - ) - self.assertEqual( - get_proxy_info( - "echo.websocket.events", - True, - proxy_host="localhost", - proxy_port=3128, - no_proxy=["echo.websocket.events"], - proxy_auth=("a", "b"), - ), - (None, 0, None), - ) - - self.assertEqual( - get_proxy_info( - "echo.websocket.events", - True, - proxy_host="localhost", - proxy_port=3128, - no_proxy=[".websocket.events"], - ), - (None, 0, None), - ) - - def test_proxy_from_env(self): - os.environ["http_proxy"] = "http://localhost/" - self.assertEqual( - get_proxy_info("echo.websocket.events", False), ("localhost", None, None) - ) - os.environ["http_proxy"] = "http://localhost:3128/" - self.assertEqual( - get_proxy_info("echo.websocket.events", False), ("localhost", 3128, None) - ) - - os.environ["http_proxy"] = "http://localhost/" - os.environ["https_proxy"] = "http://localhost2/" - self.assertEqual( - get_proxy_info("echo.websocket.events", False), ("localhost", None, None) - ) - os.environ["http_proxy"] = "http://localhost:3128/" - os.environ["https_proxy"] = "http://localhost2:3128/" - self.assertEqual( - get_proxy_info("echo.websocket.events", False), ("localhost", 3128, None) - ) - - os.environ["http_proxy"] = "http://localhost/" - os.environ["https_proxy"] = "http://localhost2/" - self.assertEqual( - get_proxy_info("echo.websocket.events", True), ("localhost2", None, None) - ) - os.environ["http_proxy"] = "http://localhost:3128/" - os.environ["https_proxy"] = "http://localhost2:3128/" - self.assertEqual( - get_proxy_info("echo.websocket.events", True), ("localhost2", 3128, None) - ) - - os.environ["http_proxy"] = "" - os.environ["https_proxy"] = "http://localhost2/" - self.assertEqual( - get_proxy_info("echo.websocket.events", True), ("localhost2", None, None) - ) - self.assertEqual( - get_proxy_info("echo.websocket.events", False), (None, 0, None) - ) - os.environ["http_proxy"] = "" - os.environ["https_proxy"] = "http://localhost2:3128/" - self.assertEqual( - get_proxy_info("echo.websocket.events", True), ("localhost2", 3128, None) - ) - self.assertEqual( - get_proxy_info("echo.websocket.events", False), (None, 0, None) - ) - - os.environ["http_proxy"] = "http://localhost/" - os.environ["https_proxy"] = "" - self.assertEqual(get_proxy_info("echo.websocket.events", True), (None, 0, None)) - self.assertEqual( - get_proxy_info("echo.websocket.events", False), ("localhost", None, None) - ) - os.environ["http_proxy"] = "http://localhost:3128/" - os.environ["https_proxy"] = "" - self.assertEqual(get_proxy_info("echo.websocket.events", True), (None, 0, None)) - self.assertEqual( - get_proxy_info("echo.websocket.events", False), ("localhost", 3128, None) - ) - - os.environ["http_proxy"] = "http://a:b@localhost/" - self.assertEqual( - get_proxy_info("echo.websocket.events", False), - ("localhost", None, ("a", "b")), - ) - os.environ["http_proxy"] = "http://a:b@localhost:3128/" - self.assertEqual( - get_proxy_info("echo.websocket.events", False), - ("localhost", 3128, ("a", "b")), - ) - - os.environ["http_proxy"] = "http://a:b@localhost/" - os.environ["https_proxy"] = "http://a:b@localhost2/" - self.assertEqual( - get_proxy_info("echo.websocket.events", False), - ("localhost", None, ("a", "b")), - ) - os.environ["http_proxy"] = "http://a:b@localhost:3128/" - os.environ["https_proxy"] = "http://a:b@localhost2:3128/" - self.assertEqual( - get_proxy_info("echo.websocket.events", False), - ("localhost", 3128, ("a", "b")), - ) - - os.environ["http_proxy"] = "http://a:b@localhost/" - os.environ["https_proxy"] = "http://a:b@localhost2/" - self.assertEqual( - get_proxy_info("echo.websocket.events", True), - ("localhost2", None, ("a", "b")), - ) - os.environ["http_proxy"] = "http://a:b@localhost:3128/" - os.environ["https_proxy"] = "http://a:b@localhost2:3128/" - self.assertEqual( - get_proxy_info("echo.websocket.events", True), - ("localhost2", 3128, ("a", "b")), - ) - - os.environ[ - "http_proxy" - ] = "http://john%40example.com:P%40SSWORD@localhost:3128/" - os.environ[ - "https_proxy" - ] = "http://john%40example.com:P%40SSWORD@localhost2:3128/" - self.assertEqual( - get_proxy_info("echo.websocket.events", True), - ("localhost2", 3128, ("john@example.com", "P@SSWORD")), - ) - - os.environ["http_proxy"] = "http://a:b@localhost/" - os.environ["https_proxy"] = "http://a:b@localhost2/" - os.environ["no_proxy"] = "example1.com,example2.com" - self.assertEqual( - get_proxy_info("example.1.com", True), ("localhost2", None, ("a", "b")) - ) - os.environ["http_proxy"] = "http://a:b@localhost:3128/" - os.environ["https_proxy"] = "http://a:b@localhost2:3128/" - os.environ["no_proxy"] = "example1.com,example2.com, echo.websocket.events" - self.assertEqual(get_proxy_info("echo.websocket.events", True), (None, 0, None)) - os.environ["http_proxy"] = "http://a:b@localhost:3128/" - os.environ["https_proxy"] = "http://a:b@localhost2:3128/" - os.environ["no_proxy"] = "example1.com,example2.com, .websocket.events" - self.assertEqual(get_proxy_info("echo.websocket.events", True), (None, 0, None)) - - os.environ["http_proxy"] = "http://a:b@localhost:3128/" - os.environ["https_proxy"] = "http://a:b@localhost2:3128/" - os.environ["no_proxy"] = "127.0.0.0/8, 192.168.0.0/16" - self.assertEqual(get_proxy_info("127.0.0.1", False), (None, 0, None)) - self.assertEqual(get_proxy_info("192.168.1.1", False), (None, 0, None)) - - -if __name__ == "__main__": - unittest.main() diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/test_websocket.py b/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/test_websocket.py deleted file mode 100644 index a1d7ad5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket/tests/test_websocket.py +++ /dev/null @@ -1,497 +0,0 @@ -# -*- coding: utf-8 -*- -# -import os -import os.path -import socket -import unittest -from base64 import decodebytes as base64decode - -import websocket as ws -from websocket._exceptions import WebSocketBadStatusException, WebSocketAddressException -from websocket._handshake import _create_sec_websocket_key -from websocket._handshake import _validate as _validate_header -from websocket._http import read_headers -from websocket._utils import validate_utf8 - -""" -test_websocket.py -websocket - WebSocket client library for Python - -Copyright 2024 engn33r - -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. -""" - -try: - import ssl -except ImportError: - # dummy class of SSLError for ssl none-support environment. - class SSLError(Exception): - pass - - -# Skip test to access the internet unless TEST_WITH_INTERNET == 1 -TEST_WITH_INTERNET = os.environ.get("TEST_WITH_INTERNET", "0") == "1" -# Skip tests relying on local websockets server unless LOCAL_WS_SERVER_PORT != -1 -LOCAL_WS_SERVER_PORT = os.environ.get("LOCAL_WS_SERVER_PORT", "-1") -TEST_WITH_LOCAL_SERVER = LOCAL_WS_SERVER_PORT != "-1" -TRACEABLE = True - - -def create_mask_key(_): - return "abcd" - - -class SockMock: - def __init__(self): - self.data = [] - self.sent = [] - - def add_packet(self, data): - self.data.append(data) - - def gettimeout(self): - return None - - def recv(self, bufsize): - if self.data: - e = self.data.pop(0) - if isinstance(e, Exception): - raise e - if len(e) > bufsize: - self.data.insert(0, e[bufsize:]) - return e[:bufsize] - - def send(self, data): - self.sent.append(data) - return len(data) - - def close(self): - pass - - -class HeaderSockMock(SockMock): - def __init__(self, fname): - SockMock.__init__(self) - path = os.path.join(os.path.dirname(__file__), fname) - with open(path, "rb") as f: - self.add_packet(f.read()) - - -class WebSocketTest(unittest.TestCase): - def setUp(self): - ws.enableTrace(TRACEABLE) - - def tearDown(self): - pass - - def test_default_timeout(self): - self.assertEqual(ws.getdefaulttimeout(), None) - ws.setdefaulttimeout(10) - self.assertEqual(ws.getdefaulttimeout(), 10) - ws.setdefaulttimeout(None) - - def test_ws_key(self): - key = _create_sec_websocket_key() - self.assertTrue(key != 24) - self.assertTrue("Â¥n" not in key) - - def test_nonce(self): - """WebSocket key should be a random 16-byte nonce.""" - key = _create_sec_websocket_key() - nonce = base64decode(key.encode("utf-8")) - self.assertEqual(16, len(nonce)) - - def test_ws_utils(self): - key = "c6b8hTg4EeGb2gQMztV1/g==" - required_header = { - "upgrade": "websocket", - "connection": "upgrade", - "sec-websocket-accept": "Kxep+hNu9n51529fGidYu7a3wO0=", - } - self.assertEqual(_validate_header(required_header, key, None), (True, None)) - - header = required_header.copy() - header["upgrade"] = "http" - self.assertEqual(_validate_header(header, key, None), (False, None)) - del header["upgrade"] - self.assertEqual(_validate_header(header, key, None), (False, None)) - - header = required_header.copy() - header["connection"] = "something" - self.assertEqual(_validate_header(header, key, None), (False, None)) - del header["connection"] - self.assertEqual(_validate_header(header, key, None), (False, None)) - - header = required_header.copy() - header["sec-websocket-accept"] = "something" - self.assertEqual(_validate_header(header, key, None), (False, None)) - del header["sec-websocket-accept"] - self.assertEqual(_validate_header(header, key, None), (False, None)) - - header = required_header.copy() - header["sec-websocket-protocol"] = "sub1" - self.assertEqual( - _validate_header(header, key, ["sub1", "sub2"]), (True, "sub1") - ) - # This case will print out a logging error using the error() function, but that is expected - self.assertEqual(_validate_header(header, key, ["sub2", "sub3"]), (False, None)) - - header = required_header.copy() - header["sec-websocket-protocol"] = "sUb1" - self.assertEqual( - _validate_header(header, key, ["Sub1", "suB2"]), (True, "sub1") - ) - - header = required_header.copy() - # This case will print out a logging error using the error() function, but that is expected - self.assertEqual(_validate_header(header, key, ["Sub1", "suB2"]), (False, None)) - - def test_read_header(self): - status, header, _ = read_headers(HeaderSockMock("data/header01.txt")) - self.assertEqual(status, 101) - self.assertEqual(header["connection"], "Upgrade") - - status, header, _ = read_headers(HeaderSockMock("data/header03.txt")) - self.assertEqual(status, 101) - self.assertEqual(header["connection"], "Upgrade, Keep-Alive") - - HeaderSockMock("data/header02.txt") - self.assertRaises( - ws.WebSocketException, read_headers, HeaderSockMock("data/header02.txt") - ) - - def test_send(self): - # TODO: add longer frame data - sock = ws.WebSocket() - sock.set_mask_key(create_mask_key) - s = sock.sock = HeaderSockMock("data/header01.txt") - sock.send("Hello") - self.assertEqual(s.sent[0], b"\x81\x85abcd)\x07\x0f\x08\x0e") - - sock.send("ã“ã‚“ã«ã¡ã¯") - self.assertEqual( - s.sent[1], - b"\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc", - ) - - # sock.send("x" * 5000) - # self.assertEqual(s.sent[1], b'\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc") - - self.assertEqual(sock.send_binary(b"1111111111101"), 19) - - def test_recv(self): - # TODO: add longer frame data - sock = ws.WebSocket() - s = sock.sock = SockMock() - something = ( - b"\x81\x8fabcd\x82\xe3\xf0\x87\xe3\xf1\x80\xe5\xca\x81\xe2\xc5\x82\xe3\xcc" - ) - s.add_packet(something) - data = sock.recv() - self.assertEqual(data, "ã“ã‚“ã«ã¡ã¯") - - s.add_packet(b"\x81\x85abcd)\x07\x0f\x08\x0e") - data = sock.recv() - self.assertEqual(data, "Hello") - - @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def test_iter(self): - count = 2 - s = ws.create_connection("wss://api.bitfinex.com/ws/2") - s.send('{"event": "subscribe", "channel": "ticker"}') - for _ in s: - count -= 1 - if count == 0: - break - - @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def test_next(self): - sock = ws.create_connection("wss://api.bitfinex.com/ws/2") - self.assertEqual(str, type(next(sock))) - - def test_internal_recv_strict(self): - sock = ws.WebSocket() - s = sock.sock = SockMock() - s.add_packet(b"foo") - s.add_packet(socket.timeout()) - s.add_packet(b"bar") - # s.add_packet(SSLError("The read operation timed out")) - s.add_packet(b"baz") - with self.assertRaises(ws.WebSocketTimeoutException): - sock.frame_buffer.recv_strict(9) - # with self.assertRaises(SSLError): - # data = sock._recv_strict(9) - data = sock.frame_buffer.recv_strict(9) - self.assertEqual(data, b"foobarbaz") - with self.assertRaises(ws.WebSocketConnectionClosedException): - sock.frame_buffer.recv_strict(1) - - def test_recv_timeout(self): - sock = ws.WebSocket() - s = sock.sock = SockMock() - s.add_packet(b"\x81") - s.add_packet(socket.timeout()) - s.add_packet(b"\x8dabcd\x29\x07\x0f\x08\x0e") - s.add_packet(socket.timeout()) - s.add_packet(b"\x4e\x43\x33\x0e\x10\x0f\x00\x40") - with self.assertRaises(ws.WebSocketTimeoutException): - sock.recv() - with self.assertRaises(ws.WebSocketTimeoutException): - sock.recv() - data = sock.recv() - self.assertEqual(data, "Hello, World!") - with self.assertRaises(ws.WebSocketConnectionClosedException): - sock.recv() - - def test_recv_with_simple_fragmentation(self): - sock = ws.WebSocket() - s = sock.sock = SockMock() - # OPCODE=TEXT, FIN=0, MSG="Brevity is " - s.add_packet(b"\x01\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C") - # OPCODE=CONT, FIN=1, MSG="the soul of wit" - s.add_packet(b"\x80\x8fabcd\x15\n\x06D\x12\r\x16\x08A\r\x05D\x16\x0b\x17") - data = sock.recv() - self.assertEqual(data, "Brevity is the soul of wit") - with self.assertRaises(ws.WebSocketConnectionClosedException): - sock.recv() - - def test_recv_with_fire_event_of_fragmentation(self): - sock = ws.WebSocket(fire_cont_frame=True) - s = sock.sock = SockMock() - # OPCODE=TEXT, FIN=0, MSG="Brevity is " - s.add_packet(b"\x01\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C") - # OPCODE=CONT, FIN=0, MSG="Brevity is " - s.add_packet(b"\x00\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C") - # OPCODE=CONT, FIN=1, MSG="the soul of wit" - s.add_packet(b"\x80\x8fabcd\x15\n\x06D\x12\r\x16\x08A\r\x05D\x16\x0b\x17") - - _, data = sock.recv_data() - self.assertEqual(data, b"Brevity is ") - _, data = sock.recv_data() - self.assertEqual(data, b"Brevity is ") - _, data = sock.recv_data() - self.assertEqual(data, b"the soul of wit") - - # OPCODE=CONT, FIN=0, MSG="Brevity is " - s.add_packet(b"\x80\x8babcd#\x10\x06\x12\x08\x16\x1aD\x08\x11C") - - with self.assertRaises(ws.WebSocketException): - sock.recv_data() - - with self.assertRaises(ws.WebSocketConnectionClosedException): - sock.recv() - - def test_close(self): - sock = ws.WebSocket() - sock.connected = True - sock.close - - sock = ws.WebSocket() - s = sock.sock = SockMock() - sock.connected = True - s.add_packet(b"\x88\x80\x17\x98p\x84") - sock.recv() - self.assertEqual(sock.connected, False) - - def test_recv_cont_fragmentation(self): - sock = ws.WebSocket() - s = sock.sock = SockMock() - # OPCODE=CONT, FIN=1, MSG="the soul of wit" - s.add_packet(b"\x80\x8fabcd\x15\n\x06D\x12\r\x16\x08A\r\x05D\x16\x0b\x17") - self.assertRaises(ws.WebSocketException, sock.recv) - - def test_recv_with_prolonged_fragmentation(self): - sock = ws.WebSocket() - s = sock.sock = SockMock() - # OPCODE=TEXT, FIN=0, MSG="Once more unto the breach, " - s.add_packet( - b"\x01\x9babcd.\x0c\x00\x01A\x0f\x0c\x16\x04B\x16\n\x15\rC\x10\t\x07C\x06\x13\x07\x02\x07\tNC" - ) - # OPCODE=CONT, FIN=0, MSG="dear friends, " - s.add_packet(b"\x00\x8eabcd\x05\x07\x02\x16A\x04\x11\r\x04\x0c\x07\x17MB") - # OPCODE=CONT, FIN=1, MSG="once more" - s.add_packet(b"\x80\x89abcd\x0e\x0c\x00\x01A\x0f\x0c\x16\x04") - data = sock.recv() - self.assertEqual(data, "Once more unto the breach, dear friends, once more") - with self.assertRaises(ws.WebSocketConnectionClosedException): - sock.recv() - - def test_recv_with_fragmentation_and_control_frame(self): - sock = ws.WebSocket() - sock.set_mask_key(create_mask_key) - s = sock.sock = SockMock() - # OPCODE=TEXT, FIN=0, MSG="Too much " - s.add_packet(b"\x01\x89abcd5\r\x0cD\x0c\x17\x00\x0cA") - # OPCODE=PING, FIN=1, MSG="Please PONG this" - s.add_packet(b"\x89\x90abcd1\x0e\x06\x05\x12\x07C4.,$D\x15\n\n\x17") - # OPCODE=CONT, FIN=1, MSG="of a good thing" - s.add_packet(b"\x80\x8fabcd\x0e\x04C\x05A\x05\x0c\x0b\x05B\x17\x0c\x08\x0c\x04") - data = sock.recv() - self.assertEqual(data, "Too much of a good thing") - with self.assertRaises(ws.WebSocketConnectionClosedException): - sock.recv() - self.assertEqual( - s.sent[0], b"\x8a\x90abcd1\x0e\x06\x05\x12\x07C4.,$D\x15\n\n\x17" - ) - - @unittest.skipUnless( - TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" - ) - def test_websocket(self): - s = ws.create_connection(f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}") - self.assertNotEqual(s, None) - s.send("Hello, World") - result = s.next() - s.fileno() - self.assertEqual(result, "Hello, World") - - s.send("ã“ã«ã‚ƒã«ã‚ƒã¡ã¯ã€ä¸–界") - result = s.recv() - self.assertEqual(result, "ã“ã«ã‚ƒã«ã‚ƒã¡ã¯ã€ä¸–界") - self.assertRaises(ValueError, s.send_close, -1, "") - s.close() - - @unittest.skipUnless( - TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" - ) - def test_ping_pong(self): - s = ws.create_connection(f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}") - self.assertNotEqual(s, None) - s.ping("Hello") - s.pong("Hi") - s.close() - - @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def test_support_redirect(self): - s = ws.WebSocket() - self.assertRaises(WebSocketBadStatusException, s.connect, "ws://google.com/") - # Need to find a URL that has a redirect code leading to a websocket - - @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def test_secure_websocket(self): - s = ws.create_connection("wss://api.bitfinex.com/ws/2") - self.assertNotEqual(s, None) - self.assertTrue(isinstance(s.sock, ssl.SSLSocket)) - self.assertEqual(s.getstatus(), 101) - self.assertNotEqual(s.getheaders(), None) - s.settimeout(10) - self.assertEqual(s.gettimeout(), 10) - self.assertEqual(s.getsubprotocol(), None) - s.abort() - - @unittest.skipUnless( - TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" - ) - def test_websocket_with_custom_header(self): - s = ws.create_connection( - f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}", - headers={"User-Agent": "PythonWebsocketClient"}, - ) - self.assertNotEqual(s, None) - self.assertEqual(s.getsubprotocol(), None) - s.send("Hello, World") - result = s.recv() - self.assertEqual(result, "Hello, World") - self.assertRaises(ValueError, s.close, -1, "") - s.close() - - @unittest.skipUnless( - TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" - ) - def test_after_close(self): - s = ws.create_connection(f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}") - self.assertNotEqual(s, None) - s.close() - self.assertRaises(ws.WebSocketConnectionClosedException, s.send, "Hello") - self.assertRaises(ws.WebSocketConnectionClosedException, s.recv) - - -class SockOptTest(unittest.TestCase): - @unittest.skipUnless( - TEST_WITH_LOCAL_SERVER, "Tests using local websocket server are disabled" - ) - def test_sockopt(self): - sockopt = ((socket.IPPROTO_TCP, socket.TCP_NODELAY, 1),) - s = ws.create_connection( - f"ws://127.0.0.1:{LOCAL_WS_SERVER_PORT}", sockopt=sockopt - ) - self.assertNotEqual( - s.sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY), 0 - ) - s.close() - - -class UtilsTest(unittest.TestCase): - def test_utf8_validator(self): - state = validate_utf8(b"\xf0\x90\x80\x80") - self.assertEqual(state, True) - state = validate_utf8( - b"\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5\xed\xa0\x80edited" - ) - self.assertEqual(state, False) - state = validate_utf8(b"") - self.assertEqual(state, True) - - -class HandshakeTest(unittest.TestCase): - @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def test_http_ssl(self): - websock1 = ws.WebSocket( - sslopt={"cert_chain": ssl.get_default_verify_paths().capath}, - enable_multithread=False, - ) - self.assertRaises(ValueError, websock1.connect, "wss://api.bitfinex.com/ws/2") - websock2 = ws.WebSocket(sslopt={"certfile": "myNonexistentCertFile"}) - self.assertRaises( - FileNotFoundError, websock2.connect, "wss://api.bitfinex.com/ws/2" - ) - - @unittest.skipUnless(TEST_WITH_INTERNET, "Internet-requiring tests are disabled") - def test_manual_headers(self): - websock3 = ws.WebSocket( - sslopt={ - "ca_certs": ssl.get_default_verify_paths().cafile, - "ca_cert_path": ssl.get_default_verify_paths().capath, - } - ) - self.assertRaises( - WebSocketBadStatusException, - websock3.connect, - "wss://api.bitfinex.com/ws/2", - cookie="chocolate", - origin="testing_websockets.com", - host="echo.websocket.events/websocket-client-test", - subprotocols=["testproto"], - connection="Upgrade", - header={ - "CustomHeader1": "123", - "Cookie": "TestValue", - "Sec-WebSocket-Key": "k9kFAUWNAMmf5OEMfTlOEA==", - "Sec-WebSocket-Protocol": "newprotocol", - }, - ) - - def test_ipv6(self): - websock2 = ws.WebSocket() - self.assertRaises(ValueError, websock2.connect, "2001:4860:4860::8888") - - def test_bad_urls(self): - websock3 = ws.WebSocket() - self.assertRaises(ValueError, websock3.connect, "ws//example.com") - self.assertRaises(WebSocketAddressException, websock3.connect, "ws://example") - self.assertRaises(ValueError, websock3.connect, "example.com") - - -if __name__ == "__main__": - unittest.main() diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/INSTALLER b/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/LICENSE b/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/LICENSE deleted file mode 100644 index 62a54ca..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/LICENSE +++ /dev/null @@ -1,203 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2024 engn33r - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/METADATA b/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/METADATA deleted file mode 100644 index 563e5c0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/METADATA +++ /dev/null @@ -1,184 +0,0 @@ -Metadata-Version: 2.1 -Name: websocket-client -Version: 1.8.0 -Summary: WebSocket client for Python with low level API options -Home-page: https://github.com/websocket-client/websocket-client.git -Download-URL: https://github.com/websocket-client/websocket-client/releases -Author: liris -Author-email: liris.pp@gmail.com -Maintainer: engn33r -Maintainer-email: websocket.client@proton.me -License: Apache-2.0 -Project-URL: Documentation, https://websocket-client.readthedocs.io/ -Project-URL: Source, https://github.com/websocket-client/websocket-client/ -Keywords: websockets client -Classifier: Development Status :: 4 - Beta -Classifier: License :: OSI Approved :: Apache Software License -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: Operating System :: MacOS :: MacOS X -Classifier: Operating System :: POSIX -Classifier: Operating System :: Microsoft :: Windows -Classifier: Topic :: Internet -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Intended Audience :: Developers -Requires-Python: >=3.8 -Description-Content-Type: text/markdown -License-File: LICENSE -Provides-Extra: docs -Requires-Dist: Sphinx >=6.0 ; extra == 'docs' -Requires-Dist: sphinx-rtd-theme >=1.1.0 ; extra == 'docs' -Requires-Dist: myst-parser >=2.0.0 ; extra == 'docs' -Provides-Extra: optional -Requires-Dist: python-socks ; extra == 'optional' -Requires-Dist: wsaccel ; extra == 'optional' -Provides-Extra: test -Requires-Dist: websockets ; extra == 'test' - -[![docs](https://readthedocs.org/projects/websocket-client/badge/?style=flat)](https://websocket-client.readthedocs.io/) -[![Build Status](https://github.com/websocket-client/websocket-client/actions/workflows/build.yml/badge.svg)](https://github.com/websocket-client/websocket-client/actions/workflows/build.yml) -[![codecov](https://codecov.io/gh/websocket-client/websocket-client/branch/master/graph/badge.svg?token=pcXhUQwiL3)](https://codecov.io/gh/websocket-client/websocket-client) -[![PyPI Downloads](https://pepy.tech/badge/websocket-client)](https://pepy.tech/project/websocket-client) -[![PyPI version](https://img.shields.io/pypi/v/websocket_client)](https://pypi.org/project/websocket_client/) -[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) -[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) - -# websocket-client - -websocket-client is a WebSocket client for Python. It provides access -to low level APIs for WebSockets. websocket-client implements version -[hybi-13](https://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-13) -of the WebSocket protocol. This client does not currently support the -permessage-deflate extension from -[RFC 7692](https://tools.ietf.org/html/rfc7692). - -## Documentation - -This project's documentation can be found at -[https://websocket-client.readthedocs.io/](https://websocket-client.readthedocs.io/) - -## Contributing - -Please see the [contribution guidelines](https://github.com/websocket-client/websocket-client/blob/master/CONTRIBUTING.md) - -## Installation - -You can use `pip install websocket-client` to install, or `pip install -e .` -to install from a local copy of the code. This module is tested on Python 3.8+. - -There are several optional dependencies that can be installed to enable -specific websocket-client features. -- To install `python-socks` for proxy usage and `wsaccel` for a minor performance boost, use: - `pip install websocket-client[optional]` -- To install `websockets` to run unit tests using the local echo server, use: - `pip install websocket-client[test]` -- To install `Sphinx` and `sphinx_rtd_theme` to build project documentation, use: - `pip install websocket-client[docs]` - -While not a strict dependency, [rel](https://github.com/bubbleboy14/registeredeventlistener) -is useful when using `run_forever` with automatic reconnect. Install rel with `pip install rel`. - -Footnote: Some shells, such as zsh, require you to escape the `[` and `]` characters with a `\`. - -## Usage Tips - -Check out the documentation's FAQ for additional guidelines: -[https://websocket-client.readthedocs.io/en/latest/faq.html](https://websocket-client.readthedocs.io/en/latest/faq.html) - -Known issues with this library include lack of WebSocket Compression -support (RFC 7692) and [minimal threading documentation/support](https://websocket-client.readthedocs.io/en/latest/threading.html). - -## Performance - -The `send` and `validate_utf8` methods can sometimes be bottleneck. -You can disable UTF8 validation in this library (and receive a -performance enhancement) with the `skip_utf8_validation` parameter. -If you want to get better performance, install wsaccel. While -websocket-client does not depend on wsaccel, it will be used if -available. wsaccel doubles the speed of UTF8 validation and -offers a very minor 10% performance boost when masking the -payload data as part of the `send` process. Numpy used to -be a suggested performance enhancement alternative, but -[issue #687](https://github.com/websocket-client/websocket-client/issues/687) -found it didn't help. - -## Examples - -Many more examples are found in the -[examples documentation](https://websocket-client.readthedocs.io/en/latest/examples.html). - -### Long-lived Connection - -Most real-world WebSockets situations involve longer-lived connections. -The WebSocketApp `run_forever` loop will automatically try to reconnect -to an open WebSocket connection when a network -connection is lost if it is provided with: - -- a `dispatcher` argument (async dispatcher like rel or pyevent) -- a non-zero `reconnect` argument (delay between disconnection and attempted reconnection) - -`run_forever` provides a variety of event-based connection controls -using callbacks like `on_message` and `on_error`. -`run_forever` **does not automatically reconnect** if the server -closes the WebSocket gracefully (returning -[a standard websocket close code](https://www.rfc-editor.org/rfc/rfc6455.html#section-7.4.1)). -[This is the logic](https://github.com/websocket-client/websocket-client/pull/838#issuecomment-1228454826) behind the decision. -Customizing behavior when the server closes -the WebSocket should be handled in the `on_close` callback. -This example uses [rel](https://github.com/bubbleboy14/registeredeventlistener) -for the dispatcher to provide automatic reconnection. - -```python -import websocket -import _thread -import time -import rel - -def on_message(ws, message): - print(message) - -def on_error(ws, error): - print(error) - -def on_close(ws, close_status_code, close_msg): - print("### closed ###") - -def on_open(ws): - print("Opened connection") - -if __name__ == "__main__": - websocket.enableTrace(True) - ws = websocket.WebSocketApp("wss://api.gemini.com/v1/marketdata/BTCUSD", - on_open=on_open, - on_message=on_message, - on_error=on_error, - on_close=on_close) - - ws.run_forever(dispatcher=rel, reconnect=5) # Set dispatcher to automatic reconnection, 5 second reconnect delay if connection closed unexpectedly - rel.signal(2, rel.abort) # Keyboard Interrupt - rel.dispatch() -``` - -### Short-lived Connection - -This is if you want to communicate a short message and disconnect -immediately when done. For example, if you want to confirm that a WebSocket -server is running and responds properly to a specific request. - -```python -from websocket import create_connection - -ws = create_connection("ws://echo.websocket.events/") -print(ws.recv()) -print("Sending 'Hello, World'...") -ws.send("Hello, World") -print("Sent") -print("Receiving...") -result = ws.recv() -print("Received '%s'" % result) -ws.close() -``` diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/RECORD b/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/RECORD deleted file mode 100644 index 847affb..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/RECORD +++ /dev/null @@ -1,56 +0,0 @@ -../../../bin/wsdump,sha256=7K_jKtsMGvGt7rLZNncLACHHc220QXB-DB9gof1LzR0,272 -websocket/__init__.py,sha256=GvVpTPkjvhYEGZcjXszSfrs2FaVHudUdGgGSqf_Rbw4,833 -websocket/__pycache__/__init__.cpython-38.pyc,, -websocket/__pycache__/_abnf.cpython-38.pyc,, -websocket/__pycache__/_app.cpython-38.pyc,, -websocket/__pycache__/_cookiejar.cpython-38.pyc,, -websocket/__pycache__/_core.cpython-38.pyc,, -websocket/__pycache__/_exceptions.cpython-38.pyc,, -websocket/__pycache__/_handshake.cpython-38.pyc,, -websocket/__pycache__/_http.cpython-38.pyc,, -websocket/__pycache__/_logging.cpython-38.pyc,, -websocket/__pycache__/_socket.cpython-38.pyc,, -websocket/__pycache__/_ssl_compat.cpython-38.pyc,, -websocket/__pycache__/_url.cpython-38.pyc,, -websocket/__pycache__/_utils.cpython-38.pyc,, -websocket/__pycache__/_wsdump.cpython-38.pyc,, -websocket/_abnf.py,sha256=WesUJSSGSRpzuTp0VeK12O4SYYVMN6CyN6bqXS4lk2w,14385 -websocket/_app.py,sha256=xCAlaM2FfsPLsaEsBGXhdg4olHDEEzif6llSWefHcm8,24176 -websocket/_cookiejar.py,sha256=893SWoHmk_JysiXj8lkyLy95co5zvuC62XGMrvmgI7E,2399 -websocket/_core.py,sha256=P-lYcwk-LKJUKDqBleJsmRSIJjX2v_o4FongdJYbhn8,21080 -websocket/_exceptions.py,sha256=r7lGaC8Y2brBnaK_YJJRDdYY6UCGWxOXoQsMcgFFeJ4,2178 -websocket/_handshake.py,sha256=h_88S6vhStOZBj5zMGJtIKFV1RVMVuEskLybjJCnaj4,6578 -websocket/_http.py,sha256=33GsroWgLhOsE8pMC2Xka-RjonPuUypjeFRABFAtQJo,12818 -websocket/_logging.py,sha256=DHRUl4sEaSkolvMo4A6upn7UIYF0kJT5NlAL0vWCHRI,2228 -websocket/_socket.py,sha256=NpsUBO1ihnC-xPA0U2o1-hMXK8eiplNcU9R2VZvZ3qU,5198 -websocket/_ssl_compat.py,sha256=SRPtw1rT3LPSl9q70mCi5hW9h2xS-nIfdcXbjyGi8sE,1188 -websocket/_url.py,sha256=kbEdbdZ-BMMoYQ3sMfcp9QEY1IYrDppIKCpIIHdGVMc,5251 -websocket/_utils.py,sha256=R36FnFTxYVJyKlh-yEaapRxpoK8Xwn9JFXCy2q2haY0,6961 -websocket/_wsdump.py,sha256=kV82LWLlD6d3vzOKOVAEqkfoCA_Qkpd0hc9WN2Tq2kM,7010 -websocket/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -websocket/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -websocket/tests/__pycache__/__init__.cpython-38.pyc,, -websocket/tests/__pycache__/echo-server.cpython-38.pyc,, -websocket/tests/__pycache__/test_abnf.cpython-38.pyc,, -websocket/tests/__pycache__/test_app.cpython-38.pyc,, -websocket/tests/__pycache__/test_cookiejar.cpython-38.pyc,, -websocket/tests/__pycache__/test_http.cpython-38.pyc,, -websocket/tests/__pycache__/test_url.cpython-38.pyc,, -websocket/tests/__pycache__/test_websocket.cpython-38.pyc,, -websocket/tests/data/header01.txt,sha256=eR9UDpnf7mREys9MttKytzB5OXA5IwOGWJZKmaF4II8,163 -websocket/tests/data/header02.txt,sha256=1HzQGIMG0OGwfnboRkUqwbTEg2nTevOX2Waj0gQARaw,161 -websocket/tests/data/header03.txt,sha256=l_soTbfEWjZTLC5Ydx2U8uj8NJ30IwAc8yo5IUG5fyQ,216 -websocket/tests/echo-server.py,sha256=yYwKXimgqo6gHFjGgqhkADb6fRncSrF-OewX6UinZVg,482 -websocket/tests/test_abnf.py,sha256=fGZ335DReScT4zSNayl5H8z7MxzJ9NXbiSvt97SJGDI,4629 -websocket/tests/test_app.py,sha256=0YoTjRjcZ2MqG3Hd8-4rfvkPkNA9BUAt1UODkjXu3Y0,12364 -websocket/tests/test_cookiejar.py,sha256=FNm9Hjxu0YBwA8GLRi1GSrAp8jkKzdND6Icm7iIIu_Y,4395 -websocket/tests/test_http.py,sha256=VLqGlXOovB6DGZDnq25c7cOjAWWTJAhhjeKSFYXnLnc,12461 -websocket/tests/test_url.py,sha256=5oONcPf4rU8KSQjDYjNevcnUpOnqUNFmontSiXB5Qd4,17718 -websocket/tests/test_websocket.py,sha256=F5XtOwa_v2JHUE-xOpCWdhUgUYfNbHGErHJKQ0bEsTI,18390 -websocket_client-1.8.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -websocket_client-1.8.0.dist-info/LICENSE,sha256=RT5qaUJ-2f-SoD7y17Ie8dtB_fVtYjB6SbqFBZ96DGI,11339 -websocket_client-1.8.0.dist-info/METADATA,sha256=mUuXo30suA6pEGGHkoT30gxksNLloIXGTTH1JPE5zjc,7962 -websocket_client-1.8.0.dist-info/RECORD,, -websocket_client-1.8.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92 -websocket_client-1.8.0.dist-info/entry_points.txt,sha256=IoCGCuANLuLxE3m2QXEd2Ip57qScBjf7RfQnkjn6DNE,50 -websocket_client-1.8.0.dist-info/top_level.txt,sha256=8m_tTpcUlzWGl8v-pj5Wi7XhAFaN1_bLKRHQKCyz5_I,10 diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/WHEEL b/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/WHEEL deleted file mode 100644 index bab98d6..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.43.0) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/entry_points.txt b/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/entry_points.txt deleted file mode 100644 index 45c854e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/entry_points.txt +++ /dev/null @@ -1,2 +0,0 @@ -[console_scripts] -wsdump = websocket._wsdump:main diff --git a/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/top_level.txt b/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/top_level.txt deleted file mode 100644 index ca4cb0c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websocket_client-1.8.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -websocket diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets-13.1.dist-info/INSTALLER b/plotter-app/venv/lib/python3.8/site-packages/websockets-13.1.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets-13.1.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets-13.1.dist-info/LICENSE b/plotter-app/venv/lib/python3.8/site-packages/websockets-13.1.dist-info/LICENSE deleted file mode 100644 index 5d61ece..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets-13.1.dist-info/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) Aymeric Augustin and contributors - -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 IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets-13.1.dist-info/METADATA b/plotter-app/venv/lib/python3.8/site-packages/websockets-13.1.dist-info/METADATA deleted file mode 100644 index be70919..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets-13.1.dist-info/METADATA +++ /dev/null @@ -1,177 +0,0 @@ -Metadata-Version: 2.1 -Name: websockets -Version: 13.1 -Summary: An implementation of the WebSocket Protocol (RFC 6455 & 7692) -Author-email: Aymeric Augustin -License: BSD-3-Clause -Project-URL: Homepage, https://github.com/python-websockets/websockets -Project-URL: Changelog, https://websockets.readthedocs.io/en/stable/project/changelog.html -Project-URL: Documentation, https://websockets.readthedocs.io/ -Project-URL: Funding, https://tidelift.com/subscription/pkg/pypi-websockets?utm_source=pypi-websockets&utm_medium=referral&utm_campaign=readme -Project-URL: Tracker, https://github.com/python-websockets/websockets/issues -Keywords: WebSocket -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.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 -Requires-Python: >=3.8 -Description-Content-Type: text/x-rst -License-File: LICENSE - -.. image:: logo/horizontal.svg - :width: 480px - :alt: websockets - -|licence| |version| |pyversions| |tests| |docs| |openssf| - -.. |licence| image:: https://img.shields.io/pypi/l/websockets.svg - :target: https://pypi.python.org/pypi/websockets - -.. |version| image:: https://img.shields.io/pypi/v/websockets.svg - :target: https://pypi.python.org/pypi/websockets - -.. |pyversions| image:: https://img.shields.io/pypi/pyversions/websockets.svg - :target: https://pypi.python.org/pypi/websockets - -.. |tests| image:: https://img.shields.io/github/checks-status/python-websockets/websockets/main?label=tests - :target: https://github.com/python-websockets/websockets/actions/workflows/tests.yml - -.. |docs| image:: https://img.shields.io/readthedocs/websockets.svg - :target: https://websockets.readthedocs.io/ - -.. |openssf| image:: https://bestpractices.coreinfrastructure.org/projects/6475/badge - :target: https://bestpractices.coreinfrastructure.org/projects/6475 - -What is ``websockets``? ------------------------ - -websockets is a library for building WebSocket_ servers and clients in Python -with a focus on correctness, simplicity, robustness, and performance. - -.. _WebSocket: https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API - -Built on top of ``asyncio``, Python's standard asynchronous I/O framework, the -default implementation provides an elegant coroutine-based API. - -An implementation on top of ``threading`` and a Sans-I/O implementation are also -available. - -`Documentation is available on Read the Docs. `_ - -.. copy-pasted because GitHub doesn't support the include directive - -Here's an echo server with the ``asyncio`` API: - -.. code:: python - - #!/usr/bin/env python - - import asyncio - from websockets.server import serve - - async def echo(websocket): - async for message in websocket: - await websocket.send(message) - - async def main(): - async with serve(echo, "localhost", 8765): - await asyncio.get_running_loop().create_future() # run forever - - asyncio.run(main()) - -Here's how a client sends and receives messages with the ``threading`` API: - -.. code:: python - - #!/usr/bin/env python - - from websockets.sync.client import connect - - def hello(): - with connect("ws://localhost:8765") as websocket: - websocket.send("Hello world!") - message = websocket.recv() - print(f"Received: {message}") - - hello() - - -Does that look good? - -`Get started with the tutorial! `_ - -Why should I use ``websockets``? --------------------------------- - -The development of ``websockets`` is shaped by four principles: - -1. **Correctness**: ``websockets`` is heavily tested for compliance with - :rfc:`6455`. Continuous integration fails under 100% branch coverage. - -2. **Simplicity**: all you need to understand is ``msg = await ws.recv()`` and - ``await ws.send(msg)``. ``websockets`` takes care of managing connections - so you can focus on your application. - -3. **Robustness**: ``websockets`` is built for production. For example, it was - the only library to `handle backpressure correctly`_ before the issue - became widely known in the Python community. - -4. **Performance**: memory usage is optimized and configurable. A C extension - accelerates expensive operations. It's pre-compiled for Linux, macOS and - Windows and packaged in the wheel format for each system and Python version. - -Documentation is a first class concern in the project. Head over to `Read the -Docs`_ and see for yourself. - -.. _Read the Docs: https://websockets.readthedocs.io/ -.. _handle backpressure correctly: https://vorpus.org/blog/some-thoughts-on-asynchronous-api-design-in-a-post-asyncawait-world/#websocket-servers - -Why shouldn't I use ``websockets``? ------------------------------------ - -* If you prefer callbacks over coroutines: ``websockets`` was created to - provide the best coroutine-based API to manage WebSocket connections in - Python. Pick another library for a callback-based API. - -* If you're looking for a mixed HTTP / WebSocket library: ``websockets`` aims - at being an excellent implementation of :rfc:`6455`: The WebSocket Protocol - and :rfc:`7692`: Compression Extensions for WebSocket. Its support for HTTP - is minimal — just enough for an HTTP health check. - - If you want to do both in the same server, look at HTTP frameworks that - build on top of ``websockets`` to support WebSocket connections, like - Sanic_. - -.. _Sanic: https://sanicframework.org/en/ - -What else? ----------- - -Bug reports, patches and suggestions are welcome! - -To report a security vulnerability, please use the `Tidelift security -contact`_. Tidelift will coordinate the fix and disclosure. - -.. _Tidelift security contact: https://tidelift.com/security - -For anything else, please open an issue_ or send a `pull request`_. - -.. _issue: https://github.com/python-websockets/websockets/issues/new -.. _pull request: https://github.com/python-websockets/websockets/compare/ - -Participants must uphold the `Contributor Covenant code of conduct`_. - -.. _Contributor Covenant code of conduct: https://github.com/python-websockets/websockets/blob/main/CODE_OF_CONDUCT.md - -``websockets`` is released under the `BSD license`_. - -.. _BSD license: https://github.com/python-websockets/websockets/blob/main/LICENSE diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets-13.1.dist-info/RECORD b/plotter-app/venv/lib/python3.8/site-packages/websockets-13.1.dist-info/RECORD deleted file mode 100644 index 7a4b25a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets-13.1.dist-info/RECORD +++ /dev/null @@ -1,98 +0,0 @@ -websockets-13.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -websockets-13.1.dist-info/LICENSE,sha256=PWoMBQ2L7FL6utUC5F-yW9ArytvXDeo01Ee2oP9Obag,1514 -websockets-13.1.dist-info/METADATA,sha256=dYWGin2sQircUMT-_HkCtQkc5_LTzdQ7BLwIGDTHbbI,6777 -websockets-13.1.dist-info/RECORD,, -websockets-13.1.dist-info/WHEEL,sha256=kEv-8RuW3g3tDaovfFYwwvLeK_2lL8t8SExFktY7GL0,216 -websockets-13.1.dist-info/top_level.txt,sha256=CMpdKklxKsvZgCgyltxUWOHibZXZ1uYIVpca9xsQ8Hk,11 -websockets/__init__.py,sha256=UlYOZWjPPdgEtBFq4CP5t7Kd1Jjq-iMJT62Ya9ImDSo,5936 -websockets/__main__.py,sha256=q6tBA72COhz7NUkuP_VG9IVypJjOexx2Oi7qkKNxneg,4756 -websockets/__pycache__/__init__.cpython-38.pyc,, -websockets/__pycache__/__main__.cpython-38.pyc,, -websockets/__pycache__/auth.cpython-38.pyc,, -websockets/__pycache__/client.cpython-38.pyc,, -websockets/__pycache__/connection.cpython-38.pyc,, -websockets/__pycache__/datastructures.cpython-38.pyc,, -websockets/__pycache__/exceptions.cpython-38.pyc,, -websockets/__pycache__/frames.cpython-38.pyc,, -websockets/__pycache__/headers.cpython-38.pyc,, -websockets/__pycache__/http.cpython-38.pyc,, -websockets/__pycache__/http11.cpython-38.pyc,, -websockets/__pycache__/imports.cpython-38.pyc,, -websockets/__pycache__/protocol.cpython-38.pyc,, -websockets/__pycache__/server.cpython-38.pyc,, -websockets/__pycache__/streams.cpython-38.pyc,, -websockets/__pycache__/typing.cpython-38.pyc,, -websockets/__pycache__/uri.cpython-38.pyc,, -websockets/__pycache__/utils.cpython-38.pyc,, -websockets/__pycache__/version.cpython-38.pyc,, -websockets/asyncio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -websockets/asyncio/__pycache__/__init__.cpython-38.pyc,, -websockets/asyncio/__pycache__/async_timeout.cpython-38.pyc,, -websockets/asyncio/__pycache__/client.cpython-38.pyc,, -websockets/asyncio/__pycache__/compatibility.cpython-38.pyc,, -websockets/asyncio/__pycache__/connection.cpython-38.pyc,, -websockets/asyncio/__pycache__/messages.cpython-38.pyc,, -websockets/asyncio/__pycache__/server.cpython-38.pyc,, -websockets/asyncio/async_timeout.py,sha256=N-6Mubyiaoh66PAXGvCzhgxCM-7V2XiRnH32Xi6J6TE,8971 -websockets/asyncio/client.py,sha256=Kx9L-AYQUlMRAyo0d2DjuebggcM-rogx3JB26rEebY4,21700 -websockets/asyncio/compatibility.py,sha256=gkenDDhzNbm6_iXV5Edvbvp6uHZYdrTvGNjt8P_JtyQ,786 -websockets/asyncio/connection.py,sha256=sxX1WTz2iVxCsUvLJUoogJT9SdHsHU4ut2PuSIbxVs4,44475 -websockets/asyncio/messages.py,sha256=-sS9JCa4-aFVSv0sPJd_VtGcoADj8mE0sMxfsqW-rQw,9854 -websockets/asyncio/server.py,sha256=In45P1Ng2gznGMbnwuz3brlIAsZkSel0ScshrJZSMw8,36548 -websockets/auth.py,sha256=pCeunT3V2AdwRt_Tpq9TrkdGY7qUlDHIEqeggj5yQFk,262 -websockets/client.py,sha256=cc8y1I2Firs1JRXCfgD4j2JWnneYAuQSpGWNjrhkqFY,13541 -websockets/connection.py,sha256=OLiMVkNd25_86sB8Q7CrCwBoXy9nA0OCgdgLRA8WUR8,323 -websockets/datastructures.py,sha256=s5Rkipz4n15HSZsOrs64CoCs-_3oSBCgpe9uPvztDkY,5677 -websockets/exceptions.py,sha256=b2-QiL1pszljREQQCzbPE1Fv7-Xb-uwso2Zt6LLD10A,10594 -websockets/extensions/__init__.py,sha256=QkZsxaJVllVSp1uhdD5uPGibdbx_091GrVVfS5LXcpw,98 -websockets/extensions/__pycache__/__init__.cpython-38.pyc,, -websockets/extensions/__pycache__/base.cpython-38.pyc,, -websockets/extensions/__pycache__/permessage_deflate.cpython-38.pyc,, -websockets/extensions/base.py,sha256=jsSJnO47L2VxYzx0cZ_LLQcAyUudSDgJEtKN247H-38,2890 -websockets/extensions/permessage_deflate.py,sha256=JR9s7pvAJv2zWRUfOLysOtAiO-ovgRMqnSUpb92gohI,24661 -websockets/frames.py,sha256=H-4ULsevYdna_CjalVASRPlh2Z54NoQat_vq8P4cVfc,12765 -websockets/headers.py,sha256=9OHHZvaj4hXrofi0HuJFNYJaE0yRoPmmrBYxMaDuCTs,15931 -websockets/http.py,sha256=eWitbqWAmHeqYK4OF3JLRC4lWwI1OIeft7oY3OobXvc,481 -websockets/http11.py,sha256=-TNxOVVLr0050-0Ac3jOlWt5G9HAfkHZrt8dqoto9bs,13376 -websockets/imports.py,sha256=TNONfYXO1UPExiwCVMgmg77fH5b4nyNAKcqtTg0gO2I,2768 -websockets/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -websockets/legacy/__pycache__/__init__.cpython-38.pyc,, -websockets/legacy/__pycache__/auth.cpython-38.pyc,, -websockets/legacy/__pycache__/client.cpython-38.pyc,, -websockets/legacy/__pycache__/exceptions.cpython-38.pyc,, -websockets/legacy/__pycache__/framing.cpython-38.pyc,, -websockets/legacy/__pycache__/handshake.cpython-38.pyc,, -websockets/legacy/__pycache__/http.cpython-38.pyc,, -websockets/legacy/__pycache__/protocol.cpython-38.pyc,, -websockets/legacy/__pycache__/server.cpython-38.pyc,, -websockets/legacy/auth.py,sha256=UdK0eZg1TjMGY6iEVRbBn51M9AjpSyRv2lJbvuuI6aA,6567 -websockets/legacy/client.py,sha256=iuyFib2kX5ybK9vLVpqJRNJHa4BuA0u5MLyoNnartY4,26706 -websockets/legacy/exceptions.py,sha256=DbSHBKcDEoYoUeCxURo0cnH8PyCCKYzkTboP_tOtsxw,1967 -websockets/legacy/framing.py,sha256=ALEDiBNq17FUqNEe5LHxkPxWoY6tPwffgGFiHMdnnIs,6371 -websockets/legacy/handshake.py,sha256=2Nzr5AN2xvDC5EdNP-kB3lOcrAaUNlYuj_-hr_jv7pM,5285 -websockets/legacy/http.py,sha256=cOCQmDWhIKQmm8UWGXPW7CDZg03wjogCsb0LP9oetNQ,7061 -websockets/legacy/protocol.py,sha256=Rbk88lnbghWpcEBT-TuTtAGqDy9OA7VsUFEMUcv95RM,63681 -websockets/legacy/server.py,sha256=lb26Vm_y7biVVYLyVzG9R1BiaLmuS3TrQh-LesjO4Ss,45318 -websockets/protocol.py,sha256=yl1j9ecLShF0iTRALOTzFfq0KmW5XO74Mtk0baVkvo0,25512 -websockets/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -websockets/server.py,sha256=BVoC433LZUgKVndtaYPrndB7uf_FTuG7MXrM9QHJEzk,21275 -websockets/speedups.c,sha256=j-damnT02MKRoYw8MtTT45qLGX6z6TnriqhTkyfcNZE,5767 -websockets/speedups.cpython-38-x86_64-linux-gnu.so,sha256=FRW0JugiQU4471Sd8Yergmr8u39ELoI5T9SIrQJ2Uqs,34072 -websockets/speedups.pyi,sha256=NikZ3sAxs9Z2uWH_ZvctvMJUBbsHeC2D1L954EVSwJc,55 -websockets/streams.py,sha256=3K3FcgTcXon-51P0sVyz0G4J-H51L82SVMS--W-gl6g,4038 -websockets/sync/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -websockets/sync/__pycache__/__init__.cpython-38.pyc,, -websockets/sync/__pycache__/client.cpython-38.pyc,, -websockets/sync/__pycache__/connection.cpython-38.pyc,, -websockets/sync/__pycache__/messages.cpython-38.pyc,, -websockets/sync/__pycache__/server.cpython-38.pyc,, -websockets/sync/__pycache__/utils.cpython-38.pyc,, -websockets/sync/client.py,sha256=QWs2wYU7S8--CZgFYWUliWjSYX5zrJEFQ6_gEcrW1sA,11372 -websockets/sync/connection.py,sha256=Ve2aW760xPz8nXU56TuL7M3qV1THYmZZcfoS_0Wwh0c,30684 -websockets/sync/messages.py,sha256=K-VHhUERUsS6bOaLgTox4kShnUKt8aPmWgOdqj_4E-Y,9809 -websockets/sync/server.py,sha256=WutnccxDQWJNfPsX2WthvDr0QeVn36fUpf0MKmbeXY0,25608 -websockets/sync/utils.py,sha256=TtW-ncYFvJmiSW2gO86ngE2BVsnnBdL-4H88kWNDYbg,1107 -websockets/typing.py,sha256=b9F78aYY-sDNnIgSbvV_ApVBicVJdduLGv5wU0PVB5c,2157 -websockets/uri.py,sha256=1r8dXNEiLcdMrCrzXmsy7DwSHiF3gaOWlmAdoFexOOM,3125 -websockets/utils.py,sha256=ZpH3WJLsQS29Jf5R6lTacxf_hPd8E4zS2JmGyNpg4bA,1150 -websockets/version.py,sha256=M0HSppy6IqnAdAr0McbPGkyCuBlue4Uzigc78cOWHxs,3202 diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets-13.1.dist-info/WHEEL b/plotter-app/venv/lib/python3.8/site-packages/websockets-13.1.dist-info/WHEEL deleted file mode 100644 index 24f5fa6..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets-13.1.dist-info/WHEEL +++ /dev/null @@ -1,8 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (75.1.0) -Root-Is-Purelib: false -Tag: cp38-cp38-manylinux_2_5_x86_64 -Tag: cp38-cp38-manylinux1_x86_64 -Tag: cp38-cp38-manylinux_2_17_x86_64 -Tag: cp38-cp38-manylinux2014_x86_64 - diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets-13.1.dist-info/top_level.txt b/plotter-app/venv/lib/python3.8/site-packages/websockets-13.1.dist-info/top_level.txt deleted file mode 100644 index 14774b4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets-13.1.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -websockets diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/__init__.py deleted file mode 100644 index 54591e9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/__init__.py +++ /dev/null @@ -1,199 +0,0 @@ -from __future__ import annotations - -import typing - -from .imports import lazy_import -from .version import version as __version__ # noqa: F401 - - -__all__ = [ - # .client - "ClientProtocol", - # .datastructures - "Headers", - "HeadersLike", - "MultipleValuesError", - # .exceptions - "ConcurrencyError", - "ConnectionClosed", - "ConnectionClosedError", - "ConnectionClosedOK", - "DuplicateParameter", - "InvalidHandshake", - "InvalidHeader", - "InvalidHeaderFormat", - "InvalidHeaderValue", - "InvalidOrigin", - "InvalidParameterName", - "InvalidParameterValue", - "InvalidState", - "InvalidStatus", - "InvalidUpgrade", - "InvalidURI", - "NegotiationError", - "PayloadTooBig", - "ProtocolError", - "SecurityError", - "WebSocketException", - "WebSocketProtocolError", - # .legacy.auth - "BasicAuthWebSocketServerProtocol", - "basic_auth_protocol_factory", - # .legacy.client - "WebSocketClientProtocol", - "connect", - "unix_connect", - # .legacy.exceptions - "AbortHandshake", - "InvalidMessage", - "InvalidStatusCode", - "RedirectHandshake", - # .legacy.protocol - "WebSocketCommonProtocol", - # .legacy.server - "WebSocketServer", - "WebSocketServerProtocol", - "broadcast", - "serve", - "unix_serve", - # .server - "ServerProtocol", - # .typing - "Data", - "ExtensionName", - "ExtensionParameter", - "LoggerLike", - "StatusLike", - "Origin", - "Subprotocol", -] - -# When type checking, import non-deprecated aliases eagerly. Else, import on demand. -if typing.TYPE_CHECKING: - from .client import ClientProtocol - from .datastructures import Headers, HeadersLike, MultipleValuesError - from .exceptions import ( - ConcurrencyError, - ConnectionClosed, - ConnectionClosedError, - ConnectionClosedOK, - DuplicateParameter, - InvalidHandshake, - InvalidHeader, - InvalidHeaderFormat, - InvalidHeaderValue, - InvalidOrigin, - InvalidParameterName, - InvalidParameterValue, - InvalidState, - InvalidStatus, - InvalidUpgrade, - InvalidURI, - NegotiationError, - PayloadTooBig, - ProtocolError, - SecurityError, - WebSocketException, - WebSocketProtocolError, - ) - from .legacy.auth import ( - BasicAuthWebSocketServerProtocol, - basic_auth_protocol_factory, - ) - from .legacy.client import WebSocketClientProtocol, connect, unix_connect - from .legacy.exceptions import ( - AbortHandshake, - InvalidMessage, - InvalidStatusCode, - RedirectHandshake, - ) - from .legacy.protocol import WebSocketCommonProtocol - from .legacy.server import ( - WebSocketServer, - WebSocketServerProtocol, - broadcast, - serve, - unix_serve, - ) - from .server import ServerProtocol - from .typing import ( - Data, - ExtensionName, - ExtensionParameter, - LoggerLike, - Origin, - StatusLike, - Subprotocol, - ) -else: - lazy_import( - globals(), - aliases={ - # .client - "ClientProtocol": ".client", - # .datastructures - "Headers": ".datastructures", - "HeadersLike": ".datastructures", - "MultipleValuesError": ".datastructures", - # .exceptions - "ConcurrencyError": ".exceptions", - "ConnectionClosed": ".exceptions", - "ConnectionClosedError": ".exceptions", - "ConnectionClosedOK": ".exceptions", - "DuplicateParameter": ".exceptions", - "InvalidHandshake": ".exceptions", - "InvalidHeader": ".exceptions", - "InvalidHeaderFormat": ".exceptions", - "InvalidHeaderValue": ".exceptions", - "InvalidOrigin": ".exceptions", - "InvalidParameterName": ".exceptions", - "InvalidParameterValue": ".exceptions", - "InvalidState": ".exceptions", - "InvalidStatus": ".exceptions", - "InvalidUpgrade": ".exceptions", - "InvalidURI": ".exceptions", - "NegotiationError": ".exceptions", - "PayloadTooBig": ".exceptions", - "ProtocolError": ".exceptions", - "SecurityError": ".exceptions", - "WebSocketException": ".exceptions", - "WebSocketProtocolError": ".exceptions", - # .legacy.auth - "BasicAuthWebSocketServerProtocol": ".legacy.auth", - "basic_auth_protocol_factory": ".legacy.auth", - # .legacy.client - "WebSocketClientProtocol": ".legacy.client", - "connect": ".legacy.client", - "unix_connect": ".legacy.client", - # .legacy.exceptions - "AbortHandshake": ".legacy.exceptions", - "InvalidMessage": ".legacy.exceptions", - "InvalidStatusCode": ".legacy.exceptions", - "RedirectHandshake": ".legacy.exceptions", - # .legacy.protocol - "WebSocketCommonProtocol": ".legacy.protocol", - # .legacy.server - "WebSocketServer": ".legacy.server", - "WebSocketServerProtocol": ".legacy.server", - "broadcast": ".legacy.server", - "serve": ".legacy.server", - "unix_serve": ".legacy.server", - # .server - "ServerProtocol": ".server", - # .typing - "Data": ".typing", - "ExtensionName": ".typing", - "ExtensionParameter": ".typing", - "LoggerLike": ".typing", - "Origin": ".typing", - "StatusLike": ".typing", - "Subprotocol": ".typing", - }, - deprecated_aliases={ - # deprecated in 9.0 - 2021-09-01 - "framing": ".legacy", - "handshake": ".legacy", - "parse_uri": ".uri", - "WebSocketURI": ".uri", - }, - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__main__.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/__main__.py deleted file mode 100644 index 8647481..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/__main__.py +++ /dev/null @@ -1,159 +0,0 @@ -from __future__ import annotations - -import argparse -import os -import signal -import sys -import threading - - -try: - import readline # noqa: F401 -except ImportError: # Windows has no `readline` normally - pass - -from .sync.client import ClientConnection, connect -from .version import version as websockets_version - - -if sys.platform == "win32": - - def win_enable_vt100() -> None: - """ - Enable VT-100 for console output on Windows. - - See also https://github.com/python/cpython/issues/73245. - - """ - import ctypes - - STD_OUTPUT_HANDLE = ctypes.c_uint(-11) - INVALID_HANDLE_VALUE = ctypes.c_uint(-1) - ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x004 - - handle = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE) - if handle == INVALID_HANDLE_VALUE: - raise RuntimeError("unable to obtain stdout handle") - - cur_mode = ctypes.c_uint() - if ctypes.windll.kernel32.GetConsoleMode(handle, ctypes.byref(cur_mode)) == 0: - raise RuntimeError("unable to query current console mode") - - # ctypes ints lack support for the required bit-OR operation. - # Temporarily convert to Py int, do the OR and convert back. - py_int_mode = int.from_bytes(cur_mode, sys.byteorder) - new_mode = ctypes.c_uint(py_int_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING) - - if ctypes.windll.kernel32.SetConsoleMode(handle, new_mode) == 0: - raise RuntimeError("unable to set console mode") - - -def print_during_input(string: str) -> None: - sys.stdout.write( - # Save cursor position - "\N{ESC}7" - # Add a new line - "\N{LINE FEED}" - # Move cursor up - "\N{ESC}[A" - # Insert blank line, scroll last line down - "\N{ESC}[L" - # Print string in the inserted blank line - f"{string}\N{LINE FEED}" - # Restore cursor position - "\N{ESC}8" - # Move cursor down - "\N{ESC}[B" - ) - sys.stdout.flush() - - -def print_over_input(string: str) -> None: - sys.stdout.write( - # Move cursor to beginning of line - "\N{CARRIAGE RETURN}" - # Delete current line - "\N{ESC}[K" - # Print string - f"{string}\N{LINE FEED}" - ) - sys.stdout.flush() - - -def print_incoming_messages(websocket: ClientConnection, stop: threading.Event) -> None: - for message in websocket: - if isinstance(message, str): - print_during_input("< " + message) - else: - print_during_input("< (binary) " + message.hex()) - if not stop.is_set(): - # When the server closes the connection, raise KeyboardInterrupt - # in the main thread to exit the program. - if sys.platform == "win32": - ctrl_c = signal.CTRL_C_EVENT - else: - ctrl_c = signal.SIGINT - os.kill(os.getpid(), ctrl_c) - - -def main() -> None: - # Parse command line arguments. - parser = argparse.ArgumentParser( - prog="python -m websockets", - description="Interactive WebSocket client.", - add_help=False, - ) - group = parser.add_mutually_exclusive_group() - group.add_argument("--version", action="store_true") - group.add_argument("uri", metavar="", nargs="?") - args = parser.parse_args() - - if args.version: - print(f"websockets {websockets_version}") - return - - if args.uri is None: - parser.error("the following arguments are required: ") - - # If we're on Windows, enable VT100 terminal support. - if sys.platform == "win32": - try: - win_enable_vt100() - except RuntimeError as exc: - sys.stderr.write( - f"Unable to set terminal to VT100 mode. This is only " - f"supported since Win10 anniversary update. Expect " - f"weird symbols on the terminal.\nError: {exc}\n" - ) - sys.stderr.flush() - - try: - websocket = connect(args.uri) - except Exception as exc: - print(f"Failed to connect to {args.uri}: {exc}.") - sys.exit(1) - else: - print(f"Connected to {args.uri}.") - - stop = threading.Event() - - # Start the thread that reads messages from the connection. - thread = threading.Thread(target=print_incoming_messages, args=(websocket, stop)) - thread.start() - - # Read from stdin in the main thread in order to receive signals. - try: - while True: - # Since there's no size limit, put_nowait is identical to put. - message = input("> ") - websocket.send(message) - except (KeyboardInterrupt, EOFError): # ^C, ^D - stop.set() - websocket.close() - print_over_input("Connection closed.") - - thread.join() - - -if __name__ == "__main__": - main() diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index b82d24b..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/__main__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/__main__.cpython-38.pyc deleted file mode 100644 index 1707e3a..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/__main__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/auth.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/auth.cpython-38.pyc deleted file mode 100644 index e06724b..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/auth.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/client.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/client.cpython-38.pyc deleted file mode 100644 index cfb1924..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/client.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/connection.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/connection.cpython-38.pyc deleted file mode 100644 index 1b500e6..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/connection.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/datastructures.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/datastructures.cpython-38.pyc deleted file mode 100644 index 9f72169..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/datastructures.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/exceptions.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/exceptions.cpython-38.pyc deleted file mode 100644 index 538fd9c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/exceptions.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/frames.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/frames.cpython-38.pyc deleted file mode 100644 index 639dea1..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/frames.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/headers.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/headers.cpython-38.pyc deleted file mode 100644 index 8176700..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/headers.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/http.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/http.cpython-38.pyc deleted file mode 100644 index b2f3314..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/http.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/http11.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/http11.cpython-38.pyc deleted file mode 100644 index d5fd2a5..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/http11.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/imports.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/imports.cpython-38.pyc deleted file mode 100644 index 813fbfb..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/imports.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/protocol.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/protocol.cpython-38.pyc deleted file mode 100644 index 48cf503..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/protocol.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/server.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/server.cpython-38.pyc deleted file mode 100644 index dde7be2..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/server.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/streams.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/streams.cpython-38.pyc deleted file mode 100644 index 7450a7a..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/streams.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/typing.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/typing.cpython-38.pyc deleted file mode 100644 index 9a204af..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/typing.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/uri.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/uri.cpython-38.pyc deleted file mode 100644 index d294f7b..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/uri.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/utils.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/utils.cpython-38.pyc deleted file mode 100644 index 1fd0fb9..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/utils.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/version.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/version.cpython-38.pyc deleted file mode 100644 index e70087c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/__pycache__/version.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index c7de6ea..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/async_timeout.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/async_timeout.cpython-38.pyc deleted file mode 100644 index 9e89c84..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/async_timeout.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/client.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/client.cpython-38.pyc deleted file mode 100644 index 157959d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/client.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/compatibility.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/compatibility.cpython-38.pyc deleted file mode 100644 index 2d97fc2..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/compatibility.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/connection.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/connection.cpython-38.pyc deleted file mode 100644 index 5237652..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/connection.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/messages.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/messages.cpython-38.pyc deleted file mode 100644 index 6893fa0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/messages.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/server.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/server.cpython-38.pyc deleted file mode 100644 index bc852bd..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/__pycache__/server.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/async_timeout.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/async_timeout.py deleted file mode 100644 index 6ffa899..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/async_timeout.py +++ /dev/null @@ -1,282 +0,0 @@ -# From https://github.com/aio-libs/async-timeout/blob/master/async_timeout/__init__.py -# Licensed under the Apache License (Apache-2.0) - -import asyncio -import enum -import sys -import warnings -from types import TracebackType -from typing import Optional, Type - - -if sys.version_info >= (3, 11): - from typing import final -else: - # From https://github.com/python/typing_extensions/blob/main/src/typing_extensions.py - # Licensed under the Python Software Foundation License (PSF-2.0) - - # @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 - - # End https://github.com/python/typing_extensions/blob/main/src/typing_extensions.py - - -if sys.version_info >= (3, 11): - - def _uncancel_task(task: "asyncio.Task[object]") -> None: - task.uncancel() - -else: - - def _uncancel_task(task: "asyncio.Task[object]") -> None: - pass - - -__version__ = "4.0.3" - - -__all__ = ("timeout", "timeout_at", "Timeout") - - -def timeout(delay: Optional[float]) -> "Timeout": - """timeout context manager. - - Useful in cases when you want to apply timeout logic around block - of code or in cases when asyncio.wait_for is not suitable. For example: - - >>> async with timeout(0.001): - ... async with aiohttp.get('https://github.com') as r: - ... await r.text() - - - delay - value in seconds or None to disable timeout logic - """ - loop = asyncio.get_running_loop() - if delay is not None: - deadline = loop.time() + delay # type: Optional[float] - else: - deadline = None - return Timeout(deadline, loop) - - -def timeout_at(deadline: Optional[float]) -> "Timeout": - """Schedule the timeout at absolute time. - - deadline argument points on the time in the same clock system - as loop.time(). - - Please note: it is not POSIX time but a time with - undefined starting base, e.g. the time of the system power on. - - >>> async with timeout_at(loop.time() + 10): - ... async with aiohttp.get('https://github.com') as r: - ... await r.text() - - - """ - loop = asyncio.get_running_loop() - return Timeout(deadline, loop) - - -class _State(enum.Enum): - INIT = "INIT" - ENTER = "ENTER" - TIMEOUT = "TIMEOUT" - EXIT = "EXIT" - - -@final -class Timeout: - # Internal class, please don't instantiate it directly - # Use timeout() and timeout_at() public factories instead. - # - # Implementation note: `async with timeout()` is preferred - # over `with timeout()`. - # While technically the Timeout class implementation - # doesn't need to be async at all, - # the `async with` statement explicitly points that - # the context manager should be used from async function context. - # - # This design allows to avoid many silly misusages. - # - # TimeoutError is raised immediately when scheduled - # if the deadline is passed. - # The purpose is to time out as soon as possible - # without waiting for the next await expression. - - __slots__ = ("_deadline", "_loop", "_state", "_timeout_handler", "_task") - - def __init__( - self, deadline: Optional[float], loop: asyncio.AbstractEventLoop - ) -> None: - self._loop = loop - self._state = _State.INIT - - self._task: Optional["asyncio.Task[object]"] = None - self._timeout_handler = None # type: Optional[asyncio.Handle] - if deadline is None: - self._deadline = None # type: Optional[float] - else: - self.update(deadline) - - def __enter__(self) -> "Timeout": - warnings.warn( - "with timeout() is deprecated, use async with timeout() instead", - DeprecationWarning, - stacklevel=2, - ) - self._do_enter() - return self - - def __exit__( - self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], - ) -> Optional[bool]: - self._do_exit(exc_type) - return None - - async def __aenter__(self) -> "Timeout": - self._do_enter() - return self - - async def __aexit__( - self, - exc_type: Optional[Type[BaseException]], - exc_val: Optional[BaseException], - exc_tb: Optional[TracebackType], - ) -> Optional[bool]: - self._do_exit(exc_type) - return None - - @property - def expired(self) -> bool: - """Is timeout expired during execution?""" - return self._state == _State.TIMEOUT - - @property - def deadline(self) -> Optional[float]: - return self._deadline - - def reject(self) -> None: - """Reject scheduled timeout if any.""" - # cancel is maybe better name but - # task.cancel() raises CancelledError in asyncio world. - if self._state not in (_State.INIT, _State.ENTER): - raise RuntimeError(f"invalid state {self._state.value}") - self._reject() - - def _reject(self) -> None: - self._task = None - if self._timeout_handler is not None: - self._timeout_handler.cancel() - self._timeout_handler = None - - def shift(self, delay: float) -> None: - """Advance timeout on delay seconds. - - The delay can be negative. - - Raise RuntimeError if shift is called when deadline is not scheduled - """ - deadline = self._deadline - if deadline is None: - raise RuntimeError("cannot shift timeout if deadline is not scheduled") - self.update(deadline + delay) - - def update(self, deadline: float) -> None: - """Set deadline to absolute value. - - deadline argument points on the time in the same clock system - as loop.time(). - - If new deadline is in the past the timeout is raised immediately. - - Please note: it is not POSIX time but a time with - undefined starting base, e.g. the time of the system power on. - """ - if self._state == _State.EXIT: - raise RuntimeError("cannot reschedule after exit from context manager") - if self._state == _State.TIMEOUT: - raise RuntimeError("cannot reschedule expired timeout") - if self._timeout_handler is not None: - self._timeout_handler.cancel() - self._deadline = deadline - if self._state != _State.INIT: - self._reschedule() - - def _reschedule(self) -> None: - assert self._state == _State.ENTER - deadline = self._deadline - if deadline is None: - return - - now = self._loop.time() - if self._timeout_handler is not None: - self._timeout_handler.cancel() - - self._task = asyncio.current_task() - if deadline <= now: - self._timeout_handler = self._loop.call_soon(self._on_timeout) - else: - self._timeout_handler = self._loop.call_at(deadline, self._on_timeout) - - def _do_enter(self) -> None: - if self._state != _State.INIT: - raise RuntimeError(f"invalid state {self._state.value}") - self._state = _State.ENTER - self._reschedule() - - def _do_exit(self, exc_type: Optional[Type[BaseException]]) -> None: - if exc_type is asyncio.CancelledError and self._state == _State.TIMEOUT: - assert self._task is not None - _uncancel_task(self._task) - self._timeout_handler = None - self._task = None - raise asyncio.TimeoutError - # timeout has not expired - self._state = _State.EXIT - self._reject() - return None - - def _on_timeout(self) -> None: - assert self._task is not None - self._task.cancel() - self._state = _State.TIMEOUT - # drop the reference early - self._timeout_handler = None - - -# End https://github.com/aio-libs/async-timeout/blob/master/async_timeout/__init__.py diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/client.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/client.py deleted file mode 100644 index b1beb3e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/client.py +++ /dev/null @@ -1,561 +0,0 @@ -from __future__ import annotations - -import asyncio -import logging -import os -import urllib.parse -from types import TracebackType -from typing import Any, AsyncIterator, Callable, Generator, Sequence - -from ..client import ClientProtocol, backoff -from ..datastructures import HeadersLike -from ..exceptions import InvalidStatus, SecurityError -from ..extensions.base import ClientExtensionFactory -from ..extensions.permessage_deflate import enable_client_permessage_deflate -from ..headers import validate_subprotocols -from ..http11 import USER_AGENT, Response -from ..protocol import CONNECTING, Event -from ..typing import LoggerLike, Origin, Subprotocol -from ..uri import WebSocketURI, parse_uri -from .compatibility import TimeoutError, asyncio_timeout -from .connection import Connection - - -__all__ = ["connect", "unix_connect", "ClientConnection"] - -MAX_REDIRECTS = int(os.environ.get("WEBSOCKETS_MAX_REDIRECTS", "10")) - - -class ClientConnection(Connection): - """ - :mod:`asyncio` implementation of a WebSocket client connection. - - :class:`ClientConnection` provides :meth:`recv` and :meth:`send` coroutines - for receiving and sending messages. - - It supports asynchronous iteration to receive messages:: - - async for message in websocket: - await process(message) - - The iterator exits normally when the connection is closed with close code - 1000 (OK) or 1001 (going away) or without a close code. It raises a - :exc:`~websockets.exceptions.ConnectionClosedError` when the connection is - closed with any other code. - - The ``ping_interval``, ``ping_timeout``, ``close_timeout``, ``max_queue``, - and ``write_limit`` arguments the same meaning as in :func:`connect`. - - Args: - protocol: Sans-I/O connection. - - """ - - def __init__( - self, - protocol: ClientProtocol, - *, - ping_interval: float | None = 20, - ping_timeout: float | None = 20, - close_timeout: float | None = 10, - max_queue: int | tuple[int, int | None] = 16, - write_limit: int | tuple[int, int | None] = 2**15, - ) -> None: - self.protocol: ClientProtocol - super().__init__( - protocol, - ping_interval=ping_interval, - ping_timeout=ping_timeout, - close_timeout=close_timeout, - max_queue=max_queue, - write_limit=write_limit, - ) - self.response_rcvd: asyncio.Future[None] = self.loop.create_future() - - async def handshake( - self, - additional_headers: HeadersLike | None = None, - user_agent_header: str | None = USER_AGENT, - ) -> None: - """ - Perform the opening handshake. - - """ - async with self.send_context(expected_state=CONNECTING): - self.request = self.protocol.connect() - if additional_headers is not None: - self.request.headers.update(additional_headers) - if user_agent_header: - self.request.headers["User-Agent"] = user_agent_header - self.protocol.send_request(self.request) - - await asyncio.wait( - [self.response_rcvd, self.connection_lost_waiter], - return_when=asyncio.FIRST_COMPLETED, - ) - - # self.protocol.handshake_exc is always set when the connection is lost - # before receiving a response, when the response cannot be parsed, or - # when the response fails the handshake. - - if self.protocol.handshake_exc is not None: - raise self.protocol.handshake_exc - - def process_event(self, event: Event) -> None: - """ - Process one incoming event. - - """ - # First event - handshake response. - if self.response is None: - assert isinstance(event, Response) - self.response = event - self.response_rcvd.set_result(None) - # Later events - frames. - else: - super().process_event(event) - - -def process_exception(exc: Exception) -> Exception | None: - """ - Determine whether a connection error is retryable or fatal. - - When reconnecting automatically with ``async for ... in connect(...)``, if a - connection attempt fails, :func:`process_exception` is called to determine - whether to retry connecting or to raise the exception. - - This function defines the default behavior, which is to retry on: - - * :exc:`EOFError`, :exc:`OSError`, :exc:`asyncio.TimeoutError`: network - errors; - * :exc:`~websockets.exceptions.InvalidStatus` when the status code is 500, - 502, 503, or 504: server or proxy errors. - - All other exceptions are considered fatal. - - You can change this behavior with the ``process_exception`` argument of - :func:`connect`. - - Return :obj:`None` if the exception is retryable i.e. when the error could - be transient and trying to reconnect with the same parameters could succeed. - The exception will be logged at the ``INFO`` level. - - Return an exception, either ``exc`` or a new exception, if the exception is - fatal i.e. when trying to reconnect will most likely produce the same error. - That exception will be raised, breaking out of the retry loop. - - """ - if isinstance(exc, (EOFError, OSError, asyncio.TimeoutError)): - return None - if isinstance(exc, InvalidStatus) and exc.response.status_code in [ - 500, # Internal Server Error - 502, # Bad Gateway - 503, # Service Unavailable - 504, # Gateway Timeout - ]: - return None - return exc - - -# This is spelled in lower case because it's exposed as a callable in the API. -class connect: - """ - Connect to the WebSocket server at ``uri``. - - This coroutine returns a :class:`ClientConnection` instance, which you can - use to send and receive messages. - - :func:`connect` may be used as an asynchronous context manager:: - - from websockets.asyncio.client import connect - - async with connect(...) as websocket: - ... - - The connection is closed automatically when exiting the context. - - :func:`connect` can be used as an infinite asynchronous iterator to - reconnect automatically on errors:: - - async for websocket in connect(...): - try: - ... - except websockets.ConnectionClosed: - continue - - If the connection fails with a transient error, it is retried with - exponential backoff. If it fails with a fatal error, the exception is - raised, breaking out of the loop. - - The connection is closed automatically after each iteration of the loop. - - Args: - uri: URI of the WebSocket server. - origin: Value of the ``Origin`` header, for servers that require it. - extensions: List of supported extensions, in order in which they - should be negotiated and run. - subprotocols: List of supported subprotocols, in order of decreasing - preference. - additional_headers (HeadersLike | None): Arbitrary HTTP headers to add - to the handshake request. - user_agent_header: Value of the ``User-Agent`` request header. - It defaults to ``"Python/x.y.z websockets/X.Y"``. - Setting it to :obj:`None` removes the header. - compression: The "permessage-deflate" extension is enabled by default. - Set ``compression`` to :obj:`None` to disable it. See the - :doc:`compression guide <../../topics/compression>` for details. - process_exception: When reconnecting automatically, tell whether an - error is transient or fatal. The default behavior is defined by - :func:`process_exception`. Refer to its documentation for details. - open_timeout: Timeout for opening the connection in seconds. - :obj:`None` disables the timeout. - ping_interval: Interval between keepalive pings in seconds. - :obj:`None` disables keepalive. - ping_timeout: Timeout for keepalive pings in seconds. - :obj:`None` disables timeouts. - close_timeout: Timeout for closing the connection in seconds. - :obj:`None` disables the timeout. - max_size: Maximum size of incoming messages in bytes. - :obj:`None` disables the limit. - max_queue: High-water mark of the buffer where frames are received. - It defaults to 16 frames. The low-water mark defaults to ``max_queue - // 4``. You may pass a ``(high, low)`` tuple to set the high-water - and low-water marks. - write_limit: High-water mark of write buffer in bytes. It is passed to - :meth:`~asyncio.WriteTransport.set_write_buffer_limits`. It defaults - to 32 KiB. You may pass a ``(high, low)`` tuple to set the - high-water and low-water marks. - logger: Logger for this client. - It defaults to ``logging.getLogger("websockets.client")``. - See the :doc:`logging guide <../../topics/logging>` for details. - create_connection: Factory for the :class:`ClientConnection` managing - the connection. Set it to a wrapper or a subclass to customize - connection handling. - - Any other keyword arguments are passed to the event loop's - :meth:`~asyncio.loop.create_connection` method. - - For example: - - * You can set ``ssl`` to a :class:`~ssl.SSLContext` to enforce TLS settings. - When connecting to a ``wss://`` URI, if ``ssl`` isn't provided, a TLS - context is created with :func:`~ssl.create_default_context`. - - * You can set ``server_hostname`` to override the host name from ``uri`` in - the TLS handshake. - - * You can set ``host`` and ``port`` to connect to a different host and port - from those found in ``uri``. This only changes the destination of the TCP - connection. The host name from ``uri`` is still used in the TLS handshake - for secure connections and in the ``Host`` header. - - * You can set ``sock`` to provide a preexisting TCP socket. You may call - :func:`socket.create_connection` (not to be confused with the event loop's - :meth:`~asyncio.loop.create_connection` method) to create a suitable - client socket and customize it. - - Raises: - InvalidURI: If ``uri`` isn't a valid WebSocket URI. - OSError: If the TCP connection fails. - InvalidHandshake: If the opening handshake fails. - TimeoutError: If the opening handshake times out. - - """ - - def __init__( - self, - uri: str, - *, - # WebSocket - origin: Origin | None = None, - extensions: Sequence[ClientExtensionFactory] | None = None, - subprotocols: Sequence[Subprotocol] | None = None, - additional_headers: HeadersLike | None = None, - user_agent_header: str | None = USER_AGENT, - compression: str | None = "deflate", - process_exception: Callable[[Exception], Exception | None] = process_exception, - # Timeouts - open_timeout: float | None = 10, - ping_interval: float | None = 20, - ping_timeout: float | None = 20, - close_timeout: float | None = 10, - # Limits - max_size: int | None = 2**20, - max_queue: int | tuple[int, int | None] = 16, - write_limit: int | tuple[int, int | None] = 2**15, - # Logging - logger: LoggerLike | None = None, - # Escape hatch for advanced customization - create_connection: type[ClientConnection] | None = None, - # Other keyword arguments are passed to loop.create_connection - **kwargs: Any, - ) -> None: - self.uri = uri - - if subprotocols is not None: - validate_subprotocols(subprotocols) - - if compression == "deflate": - extensions = enable_client_permessage_deflate(extensions) - elif compression is not None: - raise ValueError(f"unsupported compression: {compression}") - - if logger is None: - logger = logging.getLogger("websockets.client") - - if create_connection is None: - create_connection = ClientConnection - - def protocol_factory(wsuri: WebSocketURI) -> ClientConnection: - # This is a protocol in the Sans-I/O implementation of websockets. - protocol = ClientProtocol( - wsuri, - origin=origin, - extensions=extensions, - subprotocols=subprotocols, - max_size=max_size, - logger=logger, - ) - # This is a connection in websockets and a protocol in asyncio. - connection = create_connection( - protocol, - ping_interval=ping_interval, - ping_timeout=ping_timeout, - close_timeout=close_timeout, - max_queue=max_queue, - write_limit=write_limit, - ) - return connection - - self.protocol_factory = protocol_factory - self.handshake_args = ( - additional_headers, - user_agent_header, - ) - self.process_exception = process_exception - self.open_timeout = open_timeout - self.logger = logger - self.connection_kwargs = kwargs - - async def create_connection(self) -> ClientConnection: - """Create TCP or Unix connection.""" - loop = asyncio.get_running_loop() - - wsuri = parse_uri(self.uri) - kwargs = self.connection_kwargs.copy() - - def factory() -> ClientConnection: - return self.protocol_factory(wsuri) - - if wsuri.secure: - kwargs.setdefault("ssl", True) - kwargs.setdefault("server_hostname", wsuri.host) - if kwargs.get("ssl") is None: - raise TypeError("ssl=None is incompatible with a wss:// URI") - else: - if kwargs.get("ssl") is not None: - raise TypeError("ssl argument is incompatible with a ws:// URI") - - if kwargs.pop("unix", False): - _, connection = await loop.create_unix_connection(factory, **kwargs) - else: - if kwargs.get("sock") is None: - kwargs.setdefault("host", wsuri.host) - kwargs.setdefault("port", wsuri.port) - _, connection = await loop.create_connection(factory, **kwargs) - return connection - - def process_redirect(self, exc: Exception) -> Exception | str: - """ - Determine whether a connection error is a redirect that can be followed. - - Return the new URI if it's a valid redirect. Else, return an exception. - - """ - if not ( - isinstance(exc, InvalidStatus) - and exc.response.status_code - in [ - 300, # Multiple Choices - 301, # Moved Permanently - 302, # Found - 303, # See Other - 307, # Temporary Redirect - 308, # Permanent Redirect - ] - and "Location" in exc.response.headers - ): - return exc - - old_wsuri = parse_uri(self.uri) - new_uri = urllib.parse.urljoin(self.uri, exc.response.headers["Location"]) - new_wsuri = parse_uri(new_uri) - - # If connect() received a socket, it is closed and cannot be reused. - if self.connection_kwargs.get("sock") is not None: - return ValueError( - f"cannot follow redirect to {new_uri} with a preexisting socket" - ) - - # TLS downgrade is forbidden. - if old_wsuri.secure and not new_wsuri.secure: - return SecurityError(f"cannot follow redirect to non-secure URI {new_uri}") - - # Apply restrictions to cross-origin redirects. - if ( - old_wsuri.secure != new_wsuri.secure - or old_wsuri.host != new_wsuri.host - or old_wsuri.port != new_wsuri.port - ): - # Cross-origin redirects on Unix sockets don't quite make sense. - if self.connection_kwargs.get("unix", False): - return ValueError( - f"cannot follow cross-origin redirect to {new_uri} " - f"with a Unix socket" - ) - - # Cross-origin redirects when host and port are overridden are ill-defined. - if ( - self.connection_kwargs.get("host") is not None - or self.connection_kwargs.get("port") is not None - ): - return ValueError( - f"cannot follow cross-origin redirect to {new_uri} " - f"with an explicit host or port" - ) - - return new_uri - - # ... = await connect(...) - - def __await__(self) -> Generator[Any, None, ClientConnection]: - # Create a suitable iterator by calling __await__ on a coroutine. - return self.__await_impl__().__await__() - - async def __await_impl__(self) -> ClientConnection: - try: - async with asyncio_timeout(self.open_timeout): - for _ in range(MAX_REDIRECTS): - self.connection = await self.create_connection() - try: - await self.connection.handshake(*self.handshake_args) - except asyncio.CancelledError: - self.connection.close_transport() - raise - except Exception as exc: - # Always close the connection even though keep-alive is - # the default in HTTP/1.1 because create_connection ties - # opening the network connection with initializing the - # protocol. In the current design of connect(), there is - # no easy way to reuse the network connection that works - # in every case nor to reinitialize the protocol. - self.connection.close_transport() - - uri_or_exc = self.process_redirect(exc) - # Response is a valid redirect; follow it. - if isinstance(uri_or_exc, str): - self.uri = uri_or_exc - continue - # Response isn't a valid redirect; raise the exception. - if uri_or_exc is exc: - raise - else: - raise uri_or_exc from exc - - else: - self.connection.start_keepalive() - return self.connection - else: - raise SecurityError(f"more than {MAX_REDIRECTS} redirects") - - except TimeoutError: - # Re-raise exception with an informative error message. - raise TimeoutError("timed out during handshake") from None - - # ... = yield from connect(...) - remove when dropping Python < 3.10 - - __iter__ = __await__ - - # async with connect(...) as ...: ... - - async def __aenter__(self) -> ClientConnection: - return await self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - traceback: TracebackType | None, - ) -> None: - await self.connection.close() - - # async for ... in connect(...): - - async def __aiter__(self) -> AsyncIterator[ClientConnection]: - delays: Generator[float, None, None] | None = None - while True: - try: - async with self as protocol: - yield protocol - except Exception as exc: - # Determine whether the exception is retryable or fatal. - # The API of process_exception is "return an exception or None"; - # "raise an exception" is also supported because it's a frequent - # mistake. It isn't documented in order to keep the API simple. - try: - new_exc = self.process_exception(exc) - except Exception as raised_exc: - new_exc = raised_exc - - # The connection failed with a fatal error. - # Raise the exception and exit the loop. - if new_exc is exc: - raise - if new_exc is not None: - raise new_exc from exc - - # The connection failed with a retryable error. - # Start or continue backoff and reconnect. - if delays is None: - delays = backoff() - delay = next(delays) - self.logger.info( - "! connect failed; reconnecting in %.1f seconds", - delay, - exc_info=True, - ) - await asyncio.sleep(delay) - continue - - else: - # The connection succeeded. Reset backoff. - delays = None - - -def unix_connect( - path: str | None = None, - uri: str | None = None, - **kwargs: Any, -) -> connect: - """ - Connect to a WebSocket server listening on a Unix socket. - - This function accepts the same keyword arguments as :func:`connect`. - - It's only available on Unix. - - It's mainly useful for debugging servers listening on Unix sockets. - - Args: - path: File system path to the Unix socket. - uri: URI of the WebSocket server. ``uri`` defaults to - ``ws://localhost/`` or, when a ``ssl`` argument is provided, to - ``wss://localhost/``. - - """ - if uri is None: - if kwargs.get("ssl") is None: - uri = "ws://localhost/" - else: - uri = "wss://localhost/" - return connect(uri=uri, unix=True, path=path, **kwargs) diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/compatibility.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/compatibility.py deleted file mode 100644 index e170000..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/compatibility.py +++ /dev/null @@ -1,30 +0,0 @@ -from __future__ import annotations - -import sys - - -__all__ = ["TimeoutError", "aiter", "anext", "asyncio_timeout", "asyncio_timeout_at"] - - -if sys.version_info[:2] >= (3, 11): - TimeoutError = TimeoutError - aiter = aiter - anext = anext - from asyncio import ( - timeout as asyncio_timeout, # noqa: F401 - timeout_at as asyncio_timeout_at, # noqa: F401 - ) - -else: # Python < 3.11 - from asyncio import TimeoutError - - def aiter(async_iterable): - return type(async_iterable).__aiter__(async_iterable) - - async def anext(async_iterator): - return await type(async_iterator).__anext__(async_iterator) - - from .async_timeout import ( - timeout as asyncio_timeout, # noqa: F401 - timeout_at as asyncio_timeout_at, # noqa: F401 - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/connection.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/connection.py deleted file mode 100644 index 6af61a4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/connection.py +++ /dev/null @@ -1,1148 +0,0 @@ -from __future__ import annotations - -import asyncio -import collections -import contextlib -import logging -import random -import struct -import sys -import uuid -from types import TracebackType -from typing import ( - Any, - AsyncIterable, - AsyncIterator, - Awaitable, - Iterable, - Mapping, - cast, -) - -from ..exceptions import ( - ConcurrencyError, - ConnectionClosed, - ConnectionClosedOK, - ProtocolError, -) -from ..frames import DATA_OPCODES, BytesLike, CloseCode, Frame, Opcode -from ..http11 import Request, Response -from ..protocol import CLOSED, OPEN, Event, Protocol, State -from ..typing import Data, LoggerLike, Subprotocol -from .compatibility import ( - TimeoutError, - aiter, - anext, - asyncio_timeout, - asyncio_timeout_at, -) -from .messages import Assembler - - -__all__ = ["Connection"] - - -class Connection(asyncio.Protocol): - """ - :mod:`asyncio` implementation of a WebSocket connection. - - :class:`Connection` provides APIs shared between WebSocket servers and - clients. - - You shouldn't use it directly. Instead, use - :class:`~websockets.asyncio.client.ClientConnection` or - :class:`~websockets.asyncio.server.ServerConnection`. - - """ - - def __init__( - self, - protocol: Protocol, - *, - ping_interval: float | None = 20, - ping_timeout: float | None = 20, - close_timeout: float | None = 10, - max_queue: int | tuple[int, int | None] = 16, - write_limit: int | tuple[int, int | None] = 2**15, - ) -> None: - self.protocol = protocol - self.ping_interval = ping_interval - self.ping_timeout = ping_timeout - self.close_timeout = close_timeout - if isinstance(max_queue, int): - max_queue = (max_queue, None) - self.max_queue = max_queue - if isinstance(write_limit, int): - write_limit = (write_limit, None) - self.write_limit = write_limit - - # Inject reference to this instance in the protocol's logger. - self.protocol.logger = logging.LoggerAdapter( - self.protocol.logger, - {"websocket": self}, - ) - - # Copy attributes from the protocol for convenience. - self.id: uuid.UUID = self.protocol.id - """Unique identifier of the connection. Useful in logs.""" - self.logger: LoggerLike = self.protocol.logger - """Logger for this connection.""" - self.debug = self.protocol.debug - - # HTTP handshake request and response. - self.request: Request | None = None - """Opening handshake request.""" - self.response: Response | None = None - """Opening handshake response.""" - - # Event loop running this connection. - self.loop = asyncio.get_running_loop() - - # Assembler turning frames into messages and serializing reads. - self.recv_messages: Assembler # initialized in connection_made - - # Deadline for the closing handshake. - self.close_deadline: float | None = None - - # Protect sending fragmented messages. - self.fragmented_send_waiter: asyncio.Future[None] | None = None - - # Mapping of ping IDs to pong waiters, in chronological order. - self.pong_waiters: dict[bytes, tuple[asyncio.Future[float], float]] = {} - - self.latency: float = 0 - """ - Latency of the connection, in seconds. - - Latency is defined as the round-trip time of the connection. It is - measured by sending a Ping frame and waiting for a matching Pong frame. - Before the first measurement, :attr:`latency` is ``0``. - - By default, websockets enables a :ref:`keepalive ` mechanism - that sends Ping frames automatically at regular intervals. You can also - send Ping frames and measure latency with :meth:`ping`. - """ - - # Task that sends keepalive pings. None when ping_interval is None. - self.keepalive_task: asyncio.Task[None] | None = None - - # Exception raised while reading from the connection, to be chained to - # ConnectionClosed in order to show why the TCP connection dropped. - self.recv_exc: BaseException | None = None - - # Completed when the TCP connection is closed and the WebSocket - # connection state becomes CLOSED. - self.connection_lost_waiter: asyncio.Future[None] = self.loop.create_future() - - # Adapted from asyncio.FlowControlMixin - self.paused: bool = False - self.drain_waiters: collections.deque[asyncio.Future[None]] = ( - collections.deque() - ) - - # Public attributes - - @property - def local_address(self) -> Any: - """ - Local address of the connection. - - For IPv4 connections, this is a ``(host, port)`` tuple. - - The format of the address depends on the address family. - See :meth:`~socket.socket.getsockname`. - - """ - return self.transport.get_extra_info("sockname") - - @property - def remote_address(self) -> Any: - """ - Remote address of the connection. - - For IPv4 connections, this is a ``(host, port)`` tuple. - - The format of the address depends on the address family. - See :meth:`~socket.socket.getpeername`. - - """ - return self.transport.get_extra_info("peername") - - @property - def state(self) -> State: - """ - State of the WebSocket connection, defined in :rfc:`6455`. - - This attribute is provided for completeness. Typical applications - shouldn't check its value. Instead, they should call :meth:`~recv` or - :meth:`send` and handle :exc:`~websockets.exceptions.ConnectionClosed` - exceptions. - - """ - return self.protocol.state - - @property - def subprotocol(self) -> Subprotocol | None: - """ - Subprotocol negotiated during the opening handshake. - - :obj:`None` if no subprotocol was negotiated. - - """ - return self.protocol.subprotocol - - # Public methods - - async def __aenter__(self) -> Connection: - return self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - traceback: TracebackType | None, - ) -> None: - if exc_type is None: - await self.close() - else: - await self.close(CloseCode.INTERNAL_ERROR) - - async def __aiter__(self) -> AsyncIterator[Data]: - """ - Iterate on incoming messages. - - The iterator calls :meth:`recv` and yields messages asynchronously in an - infinite loop. - - It exits when the connection is closed normally. It raises a - :exc:`~websockets.exceptions.ConnectionClosedError` exception after a - protocol error or a network failure. - - """ - try: - while True: - yield await self.recv() - except ConnectionClosedOK: - return - - async def recv(self, decode: bool | None = None) -> Data: - """ - Receive the next message. - - When the connection is closed, :meth:`recv` raises - :exc:`~websockets.exceptions.ConnectionClosed`. Specifically, it raises - :exc:`~websockets.exceptions.ConnectionClosedOK` after a normal closure - and :exc:`~websockets.exceptions.ConnectionClosedError` after a protocol - error or a network failure. This is how you detect the end of the - message stream. - - Canceling :meth:`recv` is safe. There's no risk of losing data. The next - invocation of :meth:`recv` will return the next message. - - This makes it possible to enforce a timeout by wrapping :meth:`recv` in - :func:`~asyncio.timeout` or :func:`~asyncio.wait_for`. - - When the message is fragmented, :meth:`recv` waits until all fragments - are received, reassembles them, and returns the whole message. - - Args: - decode: Set this flag to override the default behavior of returning - :class:`str` or :class:`bytes`. See below for details. - - Returns: - A string (:class:`str`) for a Text_ frame or a bytestring - (:class:`bytes`) for a Binary_ frame. - - .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - - You may override this behavior with the ``decode`` argument: - - * Set ``decode=False`` to disable UTF-8 decoding of Text_ frames - and return a bytestring (:class:`bytes`). This may be useful to - optimize performance when decoding isn't needed. - * Set ``decode=True`` to force UTF-8 decoding of Binary_ frames - and return a string (:class:`str`). This is useful for servers - that send binary frames instead of text frames. - - Raises: - ConnectionClosed: When the connection is closed. - ConcurrencyError: If two coroutines call :meth:`recv` or - :meth:`recv_streaming` concurrently. - - """ - try: - return await self.recv_messages.get(decode) - except EOFError: - # Wait for the protocol state to be CLOSED before accessing close_exc. - await asyncio.shield(self.connection_lost_waiter) - raise self.protocol.close_exc from self.recv_exc - except ConcurrencyError: - raise ConcurrencyError( - "cannot call recv while another coroutine " - "is already running recv or recv_streaming" - ) from None - - async def recv_streaming(self, decode: bool | None = None) -> AsyncIterator[Data]: - """ - Receive the next message frame by frame. - - This method is designed for receiving fragmented messages. It returns an - asynchronous iterator that yields each fragment as it is received. This - iterator must be fully consumed. Else, future calls to :meth:`recv` or - :meth:`recv_streaming` will raise - :exc:`~websockets.exceptions.ConcurrencyError`, making the connection - unusable. - - :meth:`recv_streaming` raises the same exceptions as :meth:`recv`. - - Canceling :meth:`recv_streaming` before receiving the first frame is - safe. Canceling it after receiving one or more frames leaves the - iterator in a partially consumed state, making the connection unusable. - Instead, you should close the connection with :meth:`close`. - - Args: - decode: Set this flag to override the default behavior of returning - :class:`str` or :class:`bytes`. See below for details. - - Returns: - An iterator of strings (:class:`str`) for a Text_ frame or - bytestrings (:class:`bytes`) for a Binary_ frame. - - .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - - You may override this behavior with the ``decode`` argument: - - * Set ``decode=False`` to disable UTF-8 decoding of Text_ frames - and return bytestrings (:class:`bytes`). This may be useful to - optimize performance when decoding isn't needed. - * Set ``decode=True`` to force UTF-8 decoding of Binary_ frames - and return strings (:class:`str`). This is useful for servers - that send binary frames instead of text frames. - - Raises: - ConnectionClosed: When the connection is closed. - ConcurrencyError: If two coroutines call :meth:`recv` or - :meth:`recv_streaming` concurrently. - - """ - try: - async for frame in self.recv_messages.get_iter(decode): - yield frame - except EOFError: - # Wait for the protocol state to be CLOSED before accessing close_exc. - await asyncio.shield(self.connection_lost_waiter) - raise self.protocol.close_exc from self.recv_exc - except ConcurrencyError: - raise ConcurrencyError( - "cannot call recv_streaming while another coroutine " - "is already running recv or recv_streaming" - ) from None - - async def send(self, message: Data | Iterable[Data] | AsyncIterable[Data]) -> None: - """ - Send a message. - - A string (:class:`str`) is sent as a Text_ frame. A bytestring or - bytes-like object (:class:`bytes`, :class:`bytearray`, or - :class:`memoryview`) is sent as a Binary_ frame. - - .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - - :meth:`send` also accepts an iterable or an asynchronous iterable of - strings, bytestrings, or bytes-like objects to enable fragmentation_. - Each item is treated as a message fragment and sent in its own frame. - All items must be of the same type, or else :meth:`send` will raise a - :exc:`TypeError` and the connection will be closed. - - .. _fragmentation: https://datatracker.ietf.org/doc/html/rfc6455#section-5.4 - - :meth:`send` rejects dict-like objects because this is often an error. - (If you really want to send the keys of a dict-like object as fragments, - call its :meth:`~dict.keys` method and pass the result to :meth:`send`.) - - Canceling :meth:`send` is discouraged. Instead, you should close the - connection with :meth:`close`. Indeed, there are only two situations - where :meth:`send` may yield control to the event loop and then get - canceled; in both cases, :meth:`close` has the same effect and is - more clear: - - 1. The write buffer is full. If you don't want to wait until enough - data is sent, your only alternative is to close the connection. - :meth:`close` will likely time out then abort the TCP connection. - 2. ``message`` is an asynchronous iterator that yields control. - Stopping in the middle of a fragmented message will cause a - protocol error and the connection will be closed. - - When the connection is closed, :meth:`send` raises - :exc:`~websockets.exceptions.ConnectionClosed`. Specifically, it - raises :exc:`~websockets.exceptions.ConnectionClosedOK` after a normal - connection closure and - :exc:`~websockets.exceptions.ConnectionClosedError` after a protocol - error or a network failure. - - Args: - message: Message to send. - - Raises: - ConnectionClosed: When the connection is closed. - TypeError: If ``message`` doesn't have a supported type. - - """ - # While sending a fragmented message, prevent sending other messages - # until all fragments are sent. - while self.fragmented_send_waiter is not None: - await asyncio.shield(self.fragmented_send_waiter) - - # Unfragmented message -- this case must be handled first because - # strings and bytes-like objects are iterable. - - if isinstance(message, str): - async with self.send_context(): - self.protocol.send_text(message.encode()) - - elif isinstance(message, BytesLike): - async with self.send_context(): - self.protocol.send_binary(message) - - # Catch a common mistake -- passing a dict to send(). - - elif isinstance(message, Mapping): - raise TypeError("data is a dict-like object") - - # Fragmented message -- regular iterator. - - elif isinstance(message, Iterable): - chunks = iter(message) - try: - chunk = next(chunks) - except StopIteration: - return - - assert self.fragmented_send_waiter is None - self.fragmented_send_waiter = self.loop.create_future() - try: - # First fragment. - if isinstance(chunk, str): - text = True - async with self.send_context(): - self.protocol.send_text( - chunk.encode(), - fin=False, - ) - elif isinstance(chunk, BytesLike): - text = False - async with self.send_context(): - self.protocol.send_binary( - chunk, - fin=False, - ) - else: - raise TypeError("iterable must contain bytes or str") - - # Other fragments - for chunk in chunks: - if isinstance(chunk, str) and text: - async with self.send_context(): - self.protocol.send_continuation( - chunk.encode(), - fin=False, - ) - elif isinstance(chunk, BytesLike) and not text: - async with self.send_context(): - self.protocol.send_continuation( - chunk, - fin=False, - ) - else: - raise TypeError("iterable must contain uniform types") - - # Final fragment. - async with self.send_context(): - self.protocol.send_continuation(b"", fin=True) - - except Exception: - # We're half-way through a fragmented message and we can't - # complete it. This makes the connection unusable. - async with self.send_context(): - self.protocol.fail(1011, "error in fragmented message") - raise - - finally: - self.fragmented_send_waiter.set_result(None) - self.fragmented_send_waiter = None - - # Fragmented message -- async iterator. - - elif isinstance(message, AsyncIterable): - achunks = aiter(message) - try: - chunk = await anext(achunks) - except StopAsyncIteration: - return - - assert self.fragmented_send_waiter is None - self.fragmented_send_waiter = self.loop.create_future() - try: - # First fragment. - if isinstance(chunk, str): - text = True - async with self.send_context(): - self.protocol.send_text( - chunk.encode(), - fin=False, - ) - elif isinstance(chunk, BytesLike): - text = False - async with self.send_context(): - self.protocol.send_binary( - chunk, - fin=False, - ) - else: - raise TypeError("async iterable must contain bytes or str") - - # Other fragments - async for chunk in achunks: - if isinstance(chunk, str) and text: - async with self.send_context(): - self.protocol.send_continuation( - chunk.encode(), - fin=False, - ) - elif isinstance(chunk, BytesLike) and not text: - async with self.send_context(): - self.protocol.send_continuation( - chunk, - fin=False, - ) - else: - raise TypeError("async iterable must contain uniform types") - - # Final fragment. - async with self.send_context(): - self.protocol.send_continuation(b"", fin=True) - - except Exception: - # We're half-way through a fragmented message and we can't - # complete it. This makes the connection unusable. - async with self.send_context(): - self.protocol.fail(1011, "error in fragmented message") - raise - - finally: - self.fragmented_send_waiter.set_result(None) - self.fragmented_send_waiter = None - - else: - raise TypeError("data must be str, bytes, iterable, or async iterable") - - async def close(self, code: int = 1000, reason: str = "") -> None: - """ - Perform the closing handshake. - - :meth:`close` waits for the other end to complete the handshake and - for the TCP connection to terminate. - - :meth:`close` is idempotent: it doesn't do anything once the - connection is closed. - - Args: - code: WebSocket close code. - reason: WebSocket close reason. - - """ - try: - # The context manager takes care of waiting for the TCP connection - # to terminate after calling a method that sends a close frame. - async with self.send_context(): - if self.fragmented_send_waiter is not None: - self.protocol.fail(1011, "close during fragmented message") - else: - self.protocol.send_close(code, reason) - except ConnectionClosed: - # Ignore ConnectionClosed exceptions raised from send_context(). - # They mean that the connection is closed, which was the goal. - pass - - async def wait_closed(self) -> None: - """ - Wait until the connection is closed. - - :meth:`wait_closed` waits for the closing handshake to complete and for - the TCP connection to terminate. - - """ - await asyncio.shield(self.connection_lost_waiter) - - async def ping(self, data: Data | None = None) -> Awaitable[float]: - """ - Send a Ping_. - - .. _Ping: https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.2 - - A ping may serve as a keepalive or as a check that the remote endpoint - received all messages up to this point - - Args: - data: Payload of the ping. A :class:`str` will be encoded to UTF-8. - If ``data`` is :obj:`None`, the payload is four random bytes. - - Returns: - A future that will be completed when the corresponding pong is - received. You can ignore it if you don't intend to wait. The result - of the future is the latency of the connection in seconds. - - :: - - pong_waiter = await ws.ping() - # only if you want to wait for the corresponding pong - latency = await pong_waiter - - Raises: - ConnectionClosed: When the connection is closed. - ConcurrencyError: If another ping was sent with the same data and - the corresponding pong wasn't received yet. - - """ - if isinstance(data, BytesLike): - data = bytes(data) - elif isinstance(data, str): - data = data.encode() - elif data is not None: - raise TypeError("data must be str or bytes-like") - - async with self.send_context(): - # Protect against duplicates if a payload is explicitly set. - if data in self.pong_waiters: - raise ConcurrencyError("already waiting for a pong with the same data") - - # Generate a unique random payload otherwise. - while data is None or data in self.pong_waiters: - data = struct.pack("!I", random.getrandbits(32)) - - pong_waiter = self.loop.create_future() - # The event loop's default clock is time.monotonic(). Its resolution - # is a bit low on Windows (~16ms). This is improved in Python 3.13. - ping_timestamp = self.loop.time() - self.pong_waiters[data] = (pong_waiter, ping_timestamp) - self.protocol.send_ping(data) - return pong_waiter - - async def pong(self, data: Data = b"") -> None: - """ - Send a Pong_. - - .. _Pong: https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.3 - - An unsolicited pong may serve as a unidirectional heartbeat. - - Args: - data: Payload of the pong. A :class:`str` will be encoded to UTF-8. - - Raises: - ConnectionClosed: When the connection is closed. - - """ - if isinstance(data, BytesLike): - data = bytes(data) - elif isinstance(data, str): - data = data.encode() - else: - raise TypeError("data must be str or bytes-like") - - async with self.send_context(): - self.protocol.send_pong(data) - - # Private methods - - def process_event(self, event: Event) -> None: - """ - Process one incoming event. - - This method is overridden in subclasses to handle the handshake. - - """ - assert isinstance(event, Frame) - if event.opcode in DATA_OPCODES: - self.recv_messages.put(event) - - if event.opcode is Opcode.PONG: - self.acknowledge_pings(bytes(event.data)) - - def acknowledge_pings(self, data: bytes) -> None: - """ - Acknowledge pings when receiving a pong. - - """ - # Ignore unsolicited pong. - if data not in self.pong_waiters: - return - - pong_timestamp = self.loop.time() - - # Sending a pong for only the most recent ping is legal. - # Acknowledge all previous pings too in that case. - ping_id = None - ping_ids = [] - for ping_id, (pong_waiter, ping_timestamp) in self.pong_waiters.items(): - ping_ids.append(ping_id) - latency = pong_timestamp - ping_timestamp - pong_waiter.set_result(latency) - if ping_id == data: - self.latency = latency - break - else: - raise AssertionError("solicited pong not found in pings") - - # Remove acknowledged pings from self.pong_waiters. - for ping_id in ping_ids: - del self.pong_waiters[ping_id] - - def abort_pings(self) -> None: - """ - Raise ConnectionClosed in pending pings. - - They'll never receive a pong once the connection is closed. - - """ - assert self.protocol.state is CLOSED - exc = self.protocol.close_exc - - for pong_waiter, _ping_timestamp in self.pong_waiters.values(): - if not pong_waiter.done(): - pong_waiter.set_exception(exc) - # If the exception is never retrieved, it will be logged when ping - # is garbage-collected. This is confusing for users. - # Given that ping is done (with an exception), canceling it does - # nothing, but it prevents logging the exception. - pong_waiter.cancel() - - self.pong_waiters.clear() - - async def keepalive(self) -> None: - """ - Send a Ping frame and wait for a Pong frame at regular intervals. - - """ - assert self.ping_interval is not None - latency = 0.0 - try: - while True: - # If self.ping_timeout > latency > self.ping_interval, pings - # will be sent immediately after receiving pongs. The period - # will be longer than self.ping_interval. - await asyncio.sleep(self.ping_interval - latency) - - self.logger.debug("% sending keepalive ping") - pong_waiter = await self.ping() - - if self.ping_timeout is not None: - try: - async with asyncio_timeout(self.ping_timeout): - # connection_lost cancels keepalive immediately - # after setting a ConnectionClosed exception on - # pong_waiter. A CancelledError is raised here, - # not a ConnectionClosed exception. - latency = await pong_waiter - self.logger.debug("% received keepalive pong") - except asyncio.TimeoutError: - if self.debug: - self.logger.debug("! timed out waiting for keepalive pong") - async with self.send_context(): - self.protocol.fail( - CloseCode.INTERNAL_ERROR, - "keepalive ping timeout", - ) - raise AssertionError( - "send_context() should wait for connection_lost(), " - "which cancels keepalive()" - ) - except Exception: - self.logger.error("keepalive ping failed", exc_info=True) - - def start_keepalive(self) -> None: - """ - Run :meth:`keepalive` in a task, unless keepalive is disabled. - - """ - if self.ping_interval is not None: - self.keepalive_task = self.loop.create_task(self.keepalive()) - - @contextlib.asynccontextmanager - async def send_context( - self, - *, - expected_state: State = OPEN, # CONNECTING during the opening handshake - ) -> AsyncIterator[None]: - """ - Create a context for writing to the connection from user code. - - On entry, :meth:`send_context` checks that the connection is open; on - exit, it writes outgoing data to the socket:: - - async with self.send_context(): - self.protocol.send_text(message.encode()) - - When the connection isn't open on entry, when the connection is expected - to close on exit, or when an unexpected error happens, terminating the - connection, :meth:`send_context` waits until the connection is closed - then raises :exc:`~websockets.exceptions.ConnectionClosed`. - - """ - # Should we wait until the connection is closed? - wait_for_close = False - # Should we close the transport and raise ConnectionClosed? - raise_close_exc = False - # What exception should we chain ConnectionClosed to? - original_exc: BaseException | None = None - - if self.protocol.state is expected_state: - # Let the caller interact with the protocol. - try: - yield - except (ProtocolError, ConcurrencyError): - # The protocol state wasn't changed. Exit immediately. - raise - except Exception as exc: - self.logger.error("unexpected internal error", exc_info=True) - # This branch should never run. It's a safety net in case of - # bugs. Since we don't know what happened, we will close the - # connection and raise the exception to the caller. - wait_for_close = False - raise_close_exc = True - original_exc = exc - else: - # Check if the connection is expected to close soon. - if self.protocol.close_expected(): - wait_for_close = True - # If the connection is expected to close soon, set the - # close deadline based on the close timeout. - # Since we tested earlier that protocol.state was OPEN - # (or CONNECTING), self.close_deadline is still None. - if self.close_timeout is not None: - assert self.close_deadline is None - self.close_deadline = self.loop.time() + self.close_timeout - # Write outgoing data to the socket and enforce flow control. - try: - self.send_data() - await self.drain() - except Exception as exc: - if self.debug: - self.logger.debug("error while sending data", exc_info=True) - # While the only expected exception here is OSError, - # other exceptions would be treated identically. - wait_for_close = False - raise_close_exc = True - original_exc = exc - - else: # self.protocol.state is not expected_state - # Minor layering violation: we assume that the connection - # will be closing soon if it isn't in the expected state. - wait_for_close = True - # Calculate close_deadline if it wasn't set yet. - if self.close_timeout is not None: - if self.close_deadline is None: - self.close_deadline = self.loop.time() + self.close_timeout - raise_close_exc = True - - # If the connection is expected to close soon and the close timeout - # elapses, close the socket to terminate the connection. - if wait_for_close: - try: - async with asyncio_timeout_at(self.close_deadline): - await asyncio.shield(self.connection_lost_waiter) - except TimeoutError: - # There's no risk to overwrite another error because - # original_exc is never set when wait_for_close is True. - assert original_exc is None - original_exc = TimeoutError("timed out while closing connection") - # Set recv_exc before closing the transport in order to get - # proper exception reporting. - raise_close_exc = True - self.set_recv_exc(original_exc) - - # If an error occurred, close the transport to terminate the connection and - # raise an exception. - if raise_close_exc: - self.close_transport() - # Wait for the protocol state to be CLOSED before accessing close_exc. - await asyncio.shield(self.connection_lost_waiter) - raise self.protocol.close_exc from original_exc - - def send_data(self) -> None: - """ - Send outgoing data. - - Raises: - OSError: When a socket operations fails. - - """ - for data in self.protocol.data_to_send(): - if data: - self.transport.write(data) - else: - # Half-close the TCP connection when possible i.e. no TLS. - if self.transport.can_write_eof(): - if self.debug: - self.logger.debug("x half-closing TCP connection") - # write_eof() doesn't document which exceptions it raises. - # OSError is plausible. uvloop can raise RuntimeError here. - try: - self.transport.write_eof() - except (OSError, RuntimeError): # pragma: no cover - pass - # Else, close the TCP connection. - else: # pragma: no cover - if self.debug: - self.logger.debug("x closing TCP connection") - self.transport.close() - - def set_recv_exc(self, exc: BaseException | None) -> None: - """ - Set recv_exc, if not set yet. - - """ - if self.recv_exc is None: - self.recv_exc = exc - - def close_transport(self) -> None: - """ - Close transport and message assembler. - - """ - self.transport.close() - self.recv_messages.close() - - # asyncio.Protocol methods - - # Connection callbacks - - def connection_made(self, transport: asyncio.BaseTransport) -> None: - transport = cast(asyncio.Transport, transport) - self.recv_messages = Assembler( - *self.max_queue, - pause=transport.pause_reading, - resume=transport.resume_reading, - ) - transport.set_write_buffer_limits(*self.write_limit) - self.transport = transport - - def connection_lost(self, exc: Exception | None) -> None: - # Calling protocol.receive_eof() is safe because it's idempotent. - # This guarantees that the protocol state becomes CLOSED. - self.protocol.receive_eof() - assert self.protocol.state is CLOSED - - self.set_recv_exc(exc) - - # Abort recv() and pending pings with a ConnectionClosed exception. - self.recv_messages.close() - self.abort_pings() - - if self.keepalive_task is not None: - self.keepalive_task.cancel() - - # If self.connection_lost_waiter isn't pending, that's a bug, because: - # - it's set only here in connection_lost() which is called only once; - # - it must never be canceled. - self.connection_lost_waiter.set_result(None) - - # Adapted from asyncio.streams.FlowControlMixin - if self.paused: # pragma: no cover - self.paused = False - for waiter in self.drain_waiters: - if not waiter.done(): - if exc is None: - waiter.set_result(None) - else: - waiter.set_exception(exc) - - # Flow control callbacks - - def pause_writing(self) -> None: # pragma: no cover - # Adapted from asyncio.streams.FlowControlMixin - assert not self.paused - self.paused = True - - def resume_writing(self) -> None: # pragma: no cover - # Adapted from asyncio.streams.FlowControlMixin - assert self.paused - self.paused = False - for waiter in self.drain_waiters: - if not waiter.done(): - waiter.set_result(None) - - async def drain(self) -> None: # pragma: no cover - # We don't check if the connection is closed because we call drain() - # immediately after write() and write() would fail in that case. - - # Adapted from asyncio.streams.StreamWriter - # Yield to the event loop so that connection_lost() may be called. - if self.transport.is_closing(): - await asyncio.sleep(0) - - # Adapted from asyncio.streams.FlowControlMixin - if self.paused: - waiter = self.loop.create_future() - self.drain_waiters.append(waiter) - try: - await waiter - finally: - self.drain_waiters.remove(waiter) - - # Streaming protocol callbacks - - def data_received(self, data: bytes) -> None: - # Feed incoming data to the protocol. - self.protocol.receive_data(data) - - # This isn't expected to raise an exception. - events = self.protocol.events_received() - - # Write outgoing data to the transport. - try: - self.send_data() - except Exception as exc: - if self.debug: - self.logger.debug("error while sending data", exc_info=True) - self.set_recv_exc(exc) - - if self.protocol.close_expected(): - # If the connection is expected to close soon, set the - # close deadline based on the close timeout. - if self.close_timeout is not None: - if self.close_deadline is None: - self.close_deadline = self.loop.time() + self.close_timeout - - for event in events: - # This isn't expected to raise an exception. - self.process_event(event) - - def eof_received(self) -> None: - # Feed the end of the data stream to the connection. - self.protocol.receive_eof() - - # This isn't expected to generate events. - assert not self.protocol.events_received() - - # There is no error handling because send_data() can only write - # the end of the data stream here and it shouldn't raise errors. - self.send_data() - - # The WebSocket protocol has its own closing handshake: endpoints close - # the TCP or TLS connection after sending and receiving a close frame. - # As a consequence, they never need to write after receiving EOF, so - # there's no reason to keep the transport open by returning True. - # Besides, that doesn't work on TLS connections. - - -# broadcast() is defined in the connection module even though it's primarily -# used by servers and documented in the server module because it works with -# client connections too and because it's easier to test together with the -# Connection class. - - -def broadcast( - connections: Iterable[Connection], - message: Data, - raise_exceptions: bool = False, -) -> None: - """ - Broadcast a message to several WebSocket connections. - - A string (:class:`str`) is sent as a Text_ frame. A bytestring or bytes-like - object (:class:`bytes`, :class:`bytearray`, or :class:`memoryview`) is sent - as a Binary_ frame. - - .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - - :func:`broadcast` pushes the message synchronously to all connections even - if their write buffers are overflowing. There's no backpressure. - - If you broadcast messages faster than a connection can handle them, messages - will pile up in its write buffer until the connection times out. Keep - ``ping_interval`` and ``ping_timeout`` low to prevent excessive memory usage - from slow connections. - - Unlike :meth:`~websockets.asyncio.connection.Connection.send`, - :func:`broadcast` doesn't support sending fragmented messages. Indeed, - fragmentation is useful for sending large messages without buffering them in - memory, while :func:`broadcast` buffers one copy per connection as fast as - possible. - - :func:`broadcast` skips connections that aren't open in order to avoid - errors on connections where the closing handshake is in progress. - - :func:`broadcast` ignores failures to write the message on some connections. - It continues writing to other connections. On Python 3.11 and above, you may - set ``raise_exceptions`` to :obj:`True` to record failures and raise all - exceptions in a :pep:`654` :exc:`ExceptionGroup`. - - While :func:`broadcast` makes more sense for servers, it works identically - with clients, if you have a use case for opening connections to many servers - and broadcasting a message to them. - - Args: - websockets: WebSocket connections to which the message will be sent. - message: Message to send. - raise_exceptions: Whether to raise an exception in case of failures. - - Raises: - TypeError: If ``message`` doesn't have a supported type. - - """ - if isinstance(message, str): - send_method = "send_text" - message = message.encode() - elif isinstance(message, BytesLike): - send_method = "send_binary" - else: - raise TypeError("data must be str or bytes") - - if raise_exceptions: - if sys.version_info[:2] < (3, 11): # pragma: no cover - raise ValueError("raise_exceptions requires at least Python 3.11") - exceptions: list[Exception] = [] - - for connection in connections: - exception: Exception - - if connection.protocol.state is not OPEN: - continue - - if connection.fragmented_send_waiter is not None: - if raise_exceptions: - exception = ConcurrencyError("sending a fragmented message") - exceptions.append(exception) - else: - connection.logger.warning( - "skipped broadcast: sending a fragmented message", - ) - continue - - try: - # Call connection.protocol.send_text or send_binary. - # Either way, message is already converted to bytes. - getattr(connection.protocol, send_method)(message) - connection.send_data() - except Exception as write_exception: - if raise_exceptions: - exception = RuntimeError("failed to write message") - exception.__cause__ = write_exception - exceptions.append(exception) - else: - connection.logger.warning( - "skipped broadcast: failed to write message", - exc_info=True, - ) - - if raise_exceptions and exceptions: - raise ExceptionGroup("skipped broadcast", exceptions) - - -# Pretend that broadcast is actually defined in the server module. -broadcast.__module__ = "websockets.asyncio.server" diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/messages.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/messages.py deleted file mode 100644 index c2b4afd..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/messages.py +++ /dev/null @@ -1,293 +0,0 @@ -from __future__ import annotations - -import asyncio -import codecs -import collections -from typing import ( - Any, - AsyncIterator, - Callable, - Generic, - Iterable, - TypeVar, -) - -from ..exceptions import ConcurrencyError -from ..frames import OP_BINARY, OP_CONT, OP_TEXT, Frame -from ..typing import Data - - -__all__ = ["Assembler"] - -UTF8Decoder = codecs.getincrementaldecoder("utf-8") - -T = TypeVar("T") - - -class SimpleQueue(Generic[T]): - """ - Simplified version of :class:`asyncio.Queue`. - - Provides only the subset of functionality needed by :class:`Assembler`. - - """ - - def __init__(self) -> None: - self.loop = asyncio.get_running_loop() - self.get_waiter: asyncio.Future[None] | None = None - self.queue: collections.deque[T] = collections.deque() - - def __len__(self) -> int: - return len(self.queue) - - def put(self, item: T) -> None: - """Put an item into the queue without waiting.""" - self.queue.append(item) - if self.get_waiter is not None and not self.get_waiter.done(): - self.get_waiter.set_result(None) - - async def get(self) -> T: - """Remove and return an item from the queue, waiting if necessary.""" - if not self.queue: - if self.get_waiter is not None: - raise ConcurrencyError("get is already running") - self.get_waiter = self.loop.create_future() - try: - await self.get_waiter - finally: - self.get_waiter.cancel() - self.get_waiter = None - return self.queue.popleft() - - def reset(self, items: Iterable[T]) -> None: - """Put back items into an empty, idle queue.""" - assert self.get_waiter is None, "cannot reset() while get() is running" - assert not self.queue, "cannot reset() while queue isn't empty" - self.queue.extend(items) - - def abort(self) -> None: - if self.get_waiter is not None and not self.get_waiter.done(): - self.get_waiter.set_exception(EOFError("stream of frames ended")) - # Clear the queue to avoid storing unnecessary data in memory. - self.queue.clear() - - -class Assembler: - """ - Assemble messages from frames. - - :class:`Assembler` expects only data frames. The stream of frames must - respect the protocol; if it doesn't, the behavior is undefined. - - Args: - pause: Called when the buffer of frames goes above the high water mark; - should pause reading from the network. - resume: Called when the buffer of frames goes below the low water mark; - should resume reading from the network. - - """ - - # coverage reports incorrectly: "line NN didn't jump to the function exit" - def __init__( # pragma: no cover - self, - high: int = 16, - low: int | None = None, - pause: Callable[[], Any] = lambda: None, - resume: Callable[[], Any] = lambda: None, - ) -> None: - # Queue of incoming messages. Each item is a queue of frames. - self.frames: SimpleQueue[Frame] = SimpleQueue() - - # We cannot put a hard limit on the size of the queue because a single - # call to Protocol.data_received() could produce thousands of frames, - # which must be buffered. Instead, we pause reading when the buffer goes - # above the high limit and we resume when it goes under the low limit. - if low is None: - low = high // 4 - if low < 0: - raise ValueError("low must be positive or equal to zero") - if high < low: - raise ValueError("high must be greater than or equal to low") - self.high, self.low = high, low - self.pause = pause - self.resume = resume - self.paused = False - - # This flag prevents concurrent calls to get() by user code. - self.get_in_progress = False - - # This flag marks the end of the connection. - self.closed = False - - async def get(self, decode: bool | None = None) -> Data: - """ - Read the next message. - - :meth:`get` returns a single :class:`str` or :class:`bytes`. - - If the message is fragmented, :meth:`get` waits until the last frame is - received, then it reassembles the message and returns it. To receive - messages frame by frame, use :meth:`get_iter` instead. - - Args: - decode: :obj:`False` disables UTF-8 decoding of text frames and - returns :class:`bytes`. :obj:`True` forces UTF-8 decoding of - binary frames and returns :class:`str`. - - Raises: - EOFError: If the stream of frames has ended. - ConcurrencyError: If two coroutines run :meth:`get` or - :meth:`get_iter` concurrently. - - """ - if self.closed: - raise EOFError("stream of frames ended") - - if self.get_in_progress: - raise ConcurrencyError("get() or get_iter() is already running") - - # Locking with get_in_progress ensures only one coroutine can get here. - self.get_in_progress = True - - # First frame - try: - frame = await self.frames.get() - except asyncio.CancelledError: - self.get_in_progress = False - raise - self.maybe_resume() - assert frame.opcode is OP_TEXT or frame.opcode is OP_BINARY - if decode is None: - decode = frame.opcode is OP_TEXT - frames = [frame] - - # Following frames, for fragmented messages - while not frame.fin: - try: - frame = await self.frames.get() - except asyncio.CancelledError: - # Put frames already received back into the queue - # so that future calls to get() can return them. - self.frames.reset(frames) - self.get_in_progress = False - raise - self.maybe_resume() - assert frame.opcode is OP_CONT - frames.append(frame) - - self.get_in_progress = False - - data = b"".join(frame.data for frame in frames) - if decode: - return data.decode() - else: - return data - - async def get_iter(self, decode: bool | None = None) -> AsyncIterator[Data]: - """ - Stream the next message. - - Iterating the return value of :meth:`get_iter` asynchronously yields a - :class:`str` or :class:`bytes` for each frame in the message. - - The iterator must be fully consumed before calling :meth:`get_iter` or - :meth:`get` again. Else, :exc:`ConcurrencyError` is raised. - - This method only makes sense for fragmented messages. If messages aren't - fragmented, use :meth:`get` instead. - - Args: - decode: :obj:`False` disables UTF-8 decoding of text frames and - returns :class:`bytes`. :obj:`True` forces UTF-8 decoding of - binary frames and returns :class:`str`. - - Raises: - EOFError: If the stream of frames has ended. - ConcurrencyError: If two coroutines run :meth:`get` or - :meth:`get_iter` concurrently. - - """ - if self.closed: - raise EOFError("stream of frames ended") - - if self.get_in_progress: - raise ConcurrencyError("get() or get_iter() is already running") - - # Locking with get_in_progress ensures only one coroutine can get here. - self.get_in_progress = True - - # First frame - try: - frame = await self.frames.get() - except asyncio.CancelledError: - self.get_in_progress = False - raise - self.maybe_resume() - assert frame.opcode is OP_TEXT or frame.opcode is OP_BINARY - if decode is None: - decode = frame.opcode is OP_TEXT - if decode: - decoder = UTF8Decoder() - yield decoder.decode(frame.data, frame.fin) - else: - yield frame.data - - # Following frames, for fragmented messages - while not frame.fin: - # We cannot handle asyncio.CancelledError because we don't buffer - # previous fragments — we're streaming them. Canceling get_iter() - # here will leave the assembler in a stuck state. Future calls to - # get() or get_iter() will raise ConcurrencyError. - frame = await self.frames.get() - self.maybe_resume() - assert frame.opcode is OP_CONT - if decode: - yield decoder.decode(frame.data, frame.fin) - else: - yield frame.data - - self.get_in_progress = False - - def put(self, frame: Frame) -> None: - """ - Add ``frame`` to the next message. - - Raises: - EOFError: If the stream of frames has ended. - - """ - if self.closed: - raise EOFError("stream of frames ended") - - self.frames.put(frame) - self.maybe_pause() - - def maybe_pause(self) -> None: - """Pause the writer if queue is above the high water mark.""" - # Check for "> high" to support high = 0 - if len(self.frames) > self.high and not self.paused: - self.paused = True - self.pause() - - def maybe_resume(self) -> None: - """Resume the writer if queue is below the low water mark.""" - # Check for "<= low" to support low = 0 - if len(self.frames) <= self.low and self.paused: - self.paused = False - self.resume() - - def close(self) -> None: - """ - End the stream of frames. - - Callling :meth:`close` concurrently with :meth:`get`, :meth:`get_iter`, - or :meth:`put` is safe. They will raise :exc:`EOFError`. - - """ - if self.closed: - return - - self.closed = True - - # Unblock get() or get_iter(). - self.frames.abort() diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/server.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/server.py deleted file mode 100644 index 19dae44..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/asyncio/server.py +++ /dev/null @@ -1,973 +0,0 @@ -from __future__ import annotations - -import asyncio -import hmac -import http -import logging -import socket -import sys -from types import TracebackType -from typing import ( - Any, - Awaitable, - Callable, - Generator, - Iterable, - Sequence, - Tuple, - cast, -) - -from ..exceptions import InvalidHeader -from ..extensions.base import ServerExtensionFactory -from ..extensions.permessage_deflate import enable_server_permessage_deflate -from ..frames import CloseCode -from ..headers import ( - build_www_authenticate_basic, - parse_authorization_basic, - validate_subprotocols, -) -from ..http11 import SERVER, Request, Response -from ..protocol import CONNECTING, OPEN, Event -from ..server import ServerProtocol -from ..typing import LoggerLike, Origin, StatusLike, Subprotocol -from .compatibility import asyncio_timeout -from .connection import Connection, broadcast - - -__all__ = [ - "broadcast", - "serve", - "unix_serve", - "ServerConnection", - "Server", - "basic_auth", -] - - -class ServerConnection(Connection): - """ - :mod:`asyncio` implementation of a WebSocket server connection. - - :class:`ServerConnection` provides :meth:`recv` and :meth:`send` methods for - receiving and sending messages. - - It supports asynchronous iteration to receive messages:: - - async for message in websocket: - await process(message) - - The iterator exits normally when the connection is closed with close code - 1000 (OK) or 1001 (going away) or without a close code. It raises a - :exc:`~websockets.exceptions.ConnectionClosedError` when the connection is - closed with any other code. - - The ``ping_interval``, ``ping_timeout``, ``close_timeout``, ``max_queue``, - and ``write_limit`` arguments the same meaning as in :func:`serve`. - - Args: - protocol: Sans-I/O connection. - server: Server that manages this connection. - - """ - - def __init__( - self, - protocol: ServerProtocol, - server: Server, - *, - ping_interval: float | None = 20, - ping_timeout: float | None = 20, - close_timeout: float | None = 10, - max_queue: int | tuple[int, int | None] = 16, - write_limit: int | tuple[int, int | None] = 2**15, - ) -> None: - self.protocol: ServerProtocol - super().__init__( - protocol, - ping_interval=ping_interval, - ping_timeout=ping_timeout, - close_timeout=close_timeout, - max_queue=max_queue, - write_limit=write_limit, - ) - self.server = server - self.request_rcvd: asyncio.Future[None] = self.loop.create_future() - self.username: str # see basic_auth() - - def respond(self, status: StatusLike, text: str) -> Response: - """ - Create a plain text HTTP response. - - ``process_request`` and ``process_response`` may call this method to - return an HTTP response instead of performing the WebSocket opening - handshake. - - You can modify the response before returning it, for example by changing - HTTP headers. - - Args: - status: HTTP status code. - text: HTTP response body; it will be encoded to UTF-8. - - Returns: - HTTP response to send to the client. - - """ - return self.protocol.reject(status, text) - - async def handshake( - self, - process_request: ( - Callable[ - [ServerConnection, Request], - Awaitable[Response | None] | Response | None, - ] - | None - ) = None, - process_response: ( - Callable[ - [ServerConnection, Request, Response], - Awaitable[Response | None] | Response | None, - ] - | None - ) = None, - server_header: str | None = SERVER, - ) -> None: - """ - Perform the opening handshake. - - """ - await asyncio.wait( - [self.request_rcvd, self.connection_lost_waiter], - return_when=asyncio.FIRST_COMPLETED, - ) - - if self.request is not None: - async with self.send_context(expected_state=CONNECTING): - response = None - - if process_request is not None: - try: - response = process_request(self, self.request) - if isinstance(response, Awaitable): - response = await response - except Exception as exc: - self.protocol.handshake_exc = exc - response = self.protocol.reject( - http.HTTPStatus.INTERNAL_SERVER_ERROR, - ( - "Failed to open a WebSocket connection.\n" - "See server log for more information.\n" - ), - ) - - if response is None: - if self.server.is_serving(): - self.response = self.protocol.accept(self.request) - else: - self.response = self.protocol.reject( - http.HTTPStatus.SERVICE_UNAVAILABLE, - "Server is shutting down.\n", - ) - else: - assert isinstance(response, Response) # help mypy - self.response = response - - if server_header: - self.response.headers["Server"] = server_header - - response = None - - if process_response is not None: - try: - response = process_response(self, self.request, self.response) - if isinstance(response, Awaitable): - response = await response - except Exception as exc: - self.protocol.handshake_exc = exc - response = self.protocol.reject( - http.HTTPStatus.INTERNAL_SERVER_ERROR, - ( - "Failed to open a WebSocket connection.\n" - "See server log for more information.\n" - ), - ) - - if response is not None: - assert isinstance(response, Response) # help mypy - self.response = response - - self.protocol.send_response(self.response) - - # self.protocol.handshake_exc is always set when the connection is lost - # before receiving a request, when the request cannot be parsed, when - # the handshake encounters an error, or when process_request or - # process_response sends an HTTP response that rejects the handshake. - - if self.protocol.handshake_exc is not None: - raise self.protocol.handshake_exc - - def process_event(self, event: Event) -> None: - """ - Process one incoming event. - - """ - # First event - handshake request. - if self.request is None: - assert isinstance(event, Request) - self.request = event - self.request_rcvd.set_result(None) - # Later events - frames. - else: - super().process_event(event) - - def connection_made(self, transport: asyncio.BaseTransport) -> None: - super().connection_made(transport) - self.server.start_connection_handler(self) - - -class Server: - """ - WebSocket server returned by :func:`serve`. - - This class mirrors the API of :class:`asyncio.Server`. - - It keeps track of WebSocket connections in order to close them properly - when shutting down. - - Args: - handler: Connection handler. It receives the WebSocket connection, - which is a :class:`ServerConnection`, in argument. - process_request: Intercept the request during the opening handshake. - Return an HTTP response to force the response. Return :obj:`None` to - continue normally. When you force an HTTP 101 Continue response, the - handshake is successful. Else, the connection is aborted. - ``process_request`` may be a function or a coroutine. - process_response: Intercept the response during the opening handshake. - Modify the response or return a new HTTP response to force the - response. Return :obj:`None` to continue normally. When you force an - HTTP 101 Continue response, the handshake is successful. Else, the - connection is aborted. ``process_response`` may be a function or a - coroutine. - server_header: Value of the ``Server`` response header. - It defaults to ``"Python/x.y.z websockets/X.Y"``. Setting it to - :obj:`None` removes the header. - open_timeout: Timeout for opening connections in seconds. - :obj:`None` disables the timeout. - logger: Logger for this server. - It defaults to ``logging.getLogger("websockets.server")``. - See the :doc:`logging guide <../../topics/logging>` for details. - - """ - - def __init__( - self, - handler: Callable[[ServerConnection], Awaitable[None]], - *, - process_request: ( - Callable[ - [ServerConnection, Request], - Awaitable[Response | None] | Response | None, - ] - | None - ) = None, - process_response: ( - Callable[ - [ServerConnection, Request, Response], - Awaitable[Response | None] | Response | None, - ] - | None - ) = None, - server_header: str | None = SERVER, - open_timeout: float | None = 10, - logger: LoggerLike | None = None, - ) -> None: - self.loop = asyncio.get_running_loop() - self.handler = handler - self.process_request = process_request - self.process_response = process_response - self.server_header = server_header - self.open_timeout = open_timeout - if logger is None: - logger = logging.getLogger("websockets.server") - self.logger = logger - - # Keep track of active connections. - self.handlers: dict[ServerConnection, asyncio.Task[None]] = {} - - # Task responsible for closing the server and terminating connections. - self.close_task: asyncio.Task[None] | None = None - - # Completed when the server is closed and connections are terminated. - self.closed_waiter: asyncio.Future[None] = self.loop.create_future() - - @property - def connections(self) -> set[ServerConnection]: - """ - Set of active connections. - - This property contains all connections that completed the opening - handshake successfully and didn't start the closing handshake yet. - It can be useful in combination with :func:`~broadcast`. - - """ - return {connection for connection in self.handlers if connection.state is OPEN} - - def wrap(self, server: asyncio.Server) -> None: - """ - Attach to a given :class:`asyncio.Server`. - - Since :meth:`~asyncio.loop.create_server` doesn't support injecting a - custom ``Server`` class, the easiest solution that doesn't rely on - private :mod:`asyncio` APIs is to: - - - instantiate a :class:`Server` - - give the protocol factory a reference to that instance - - call :meth:`~asyncio.loop.create_server` with the factory - - attach the resulting :class:`asyncio.Server` with this method - - """ - self.server = server - for sock in server.sockets: - if sock.family == socket.AF_INET: - name = "%s:%d" % sock.getsockname() - elif sock.family == socket.AF_INET6: - name = "[%s]:%d" % sock.getsockname()[:2] - elif sock.family == socket.AF_UNIX: - name = sock.getsockname() - # In the unlikely event that someone runs websockets over a - # protocol other than IP or Unix sockets, avoid crashing. - else: # pragma: no cover - name = str(sock.getsockname()) - self.logger.info("server listening on %s", name) - - async def conn_handler(self, connection: ServerConnection) -> None: - """ - Handle the lifecycle of a WebSocket connection. - - Since this method doesn't have a caller that can handle exceptions, - it attempts to log relevant ones. - - It guarantees that the TCP connection is closed before exiting. - - """ - try: - async with asyncio_timeout(self.open_timeout): - try: - await connection.handshake( - self.process_request, - self.process_response, - self.server_header, - ) - except asyncio.CancelledError: - connection.close_transport() - raise - except Exception: - connection.logger.error("opening handshake failed", exc_info=True) - connection.close_transport() - return - - assert connection.protocol.state is OPEN - try: - connection.start_keepalive() - await self.handler(connection) - except Exception: - connection.logger.error("connection handler failed", exc_info=True) - await connection.close(CloseCode.INTERNAL_ERROR) - else: - await connection.close() - - except TimeoutError: - # When the opening handshake times out, there's nothing to log. - pass - - except Exception: # pragma: no cover - # Don't leak connections on unexpected errors. - connection.transport.abort() - - finally: - # Registration is tied to the lifecycle of conn_handler() because - # the server waits for connection handlers to terminate, even if - # all connections are already closed. - del self.handlers[connection] - - def start_connection_handler(self, connection: ServerConnection) -> None: - """ - Register a connection with this server. - - """ - # The connection must be registered in self.handlers immediately. - # If it was registered in conn_handler(), a race condition could - # happen when closing the server after scheduling conn_handler() - # but before it starts executing. - self.handlers[connection] = self.loop.create_task(self.conn_handler(connection)) - - def close(self, close_connections: bool = True) -> None: - """ - Close the server. - - * Close the underlying :class:`asyncio.Server`. - * When ``close_connections`` is :obj:`True`, which is the default, - close existing connections. Specifically: - - * Reject opening WebSocket connections with an HTTP 503 (service - unavailable) error. This happens when the server accepted the TCP - connection but didn't complete the opening handshake before closing. - * Close open WebSocket connections with close code 1001 (going away). - - * Wait until all connection handlers terminate. - - :meth:`close` is idempotent. - - """ - if self.close_task is None: - self.close_task = self.get_loop().create_task( - self._close(close_connections) - ) - - async def _close(self, close_connections: bool) -> None: - """ - Implementation of :meth:`close`. - - This calls :meth:`~asyncio.Server.close` on the underlying - :class:`asyncio.Server` object to stop accepting new connections and - then closes open connections with close code 1001. - - """ - self.logger.info("server closing") - - # Stop accepting new connections. - self.server.close() - - # Wait until all accepted connections reach connection_made() and call - # register(). See https://github.com/python/cpython/issues/79033 for - # details. This workaround can be removed when dropping Python < 3.11. - await asyncio.sleep(0) - - if close_connections: - # Close OPEN connections with close code 1001. After server.close(), - # handshake() closes OPENING connections with an HTTP 503 error. - close_tasks = [ - asyncio.create_task(connection.close(1001)) - for connection in self.handlers - if connection.protocol.state is not CONNECTING - ] - # asyncio.wait doesn't accept an empty first argument. - if close_tasks: - await asyncio.wait(close_tasks) - - # Wait until all TCP connections are closed. - await self.server.wait_closed() - - # Wait until all connection handlers terminate. - # asyncio.wait doesn't accept an empty first argument. - if self.handlers: - await asyncio.wait(self.handlers.values()) - - # Tell wait_closed() to return. - self.closed_waiter.set_result(None) - - self.logger.info("server closed") - - async def wait_closed(self) -> None: - """ - Wait until the server is closed. - - When :meth:`wait_closed` returns, all TCP connections are closed and - all connection handlers have returned. - - To ensure a fast shutdown, a connection handler should always be - awaiting at least one of: - - * :meth:`~ServerConnection.recv`: when the connection is closed, - it raises :exc:`~websockets.exceptions.ConnectionClosedOK`; - * :meth:`~ServerConnection.wait_closed`: when the connection is - closed, it returns. - - Then the connection handler is immediately notified of the shutdown; - it can clean up and exit. - - """ - await asyncio.shield(self.closed_waiter) - - def get_loop(self) -> asyncio.AbstractEventLoop: - """ - See :meth:`asyncio.Server.get_loop`. - - """ - return self.server.get_loop() - - def is_serving(self) -> bool: # pragma: no cover - """ - See :meth:`asyncio.Server.is_serving`. - - """ - return self.server.is_serving() - - async def start_serving(self) -> None: # pragma: no cover - """ - See :meth:`asyncio.Server.start_serving`. - - Typical use:: - - server = await serve(..., start_serving=False) - # perform additional setup here... - # ... then start the server - await server.start_serving() - - """ - await self.server.start_serving() - - async def serve_forever(self) -> None: # pragma: no cover - """ - See :meth:`asyncio.Server.serve_forever`. - - Typical use:: - - server = await serve(...) - # this coroutine doesn't return - # canceling it stops the server - await server.serve_forever() - - This is an alternative to using :func:`serve` as an asynchronous context - manager. Shutdown is triggered by canceling :meth:`serve_forever` - instead of exiting a :func:`serve` context. - - """ - await self.server.serve_forever() - - @property - def sockets(self) -> Iterable[socket.socket]: - """ - See :attr:`asyncio.Server.sockets`. - - """ - return self.server.sockets - - async def __aenter__(self) -> Server: # pragma: no cover - return self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - traceback: TracebackType | None, - ) -> None: # pragma: no cover - self.close() - await self.wait_closed() - - -# This is spelled in lower case because it's exposed as a callable in the API. -class serve: - """ - Create a WebSocket server listening on ``host`` and ``port``. - - Whenever a client connects, the server creates a :class:`ServerConnection`, - performs the opening handshake, and delegates to the ``handler`` coroutine. - - The handler receives the :class:`ServerConnection` instance, which you can - use to send and receive messages. - - Once the handler completes, either normally or with an exception, the server - performs the closing handshake and closes the connection. - - This coroutine returns a :class:`Server` whose API mirrors - :class:`asyncio.Server`. Treat it as an asynchronous context manager to - ensure that the server will be closed:: - - from websockets.asyncio.server import serve - - def handler(websocket): - ... - - # set this future to exit the server - stop = asyncio.get_running_loop().create_future() - - async with serve(handler, host, port): - await stop - - Alternatively, call :meth:`~Server.serve_forever` to serve requests and - cancel it to stop the server:: - - server = await serve(handler, host, port) - await server.serve_forever() - - Args: - handler: Connection handler. It receives the WebSocket connection, - which is a :class:`ServerConnection`, in argument. - host: Network interfaces the server binds to. - See :meth:`~asyncio.loop.create_server` for details. - port: TCP port the server listens on. - See :meth:`~asyncio.loop.create_server` for details. - origins: Acceptable values of the ``Origin`` header, for defending - against Cross-Site WebSocket Hijacking attacks. Include :obj:`None` - in the list if the lack of an origin is acceptable. - extensions: List of supported extensions, in order in which they - should be negotiated and run. - subprotocols: List of supported subprotocols, in order of decreasing - preference. - select_subprotocol: Callback for selecting a subprotocol among - those supported by the client and the server. It receives a - :class:`ServerConnection` (not a - :class:`~websockets.server.ServerProtocol`!) instance and a list of - subprotocols offered by the client. Other than the first argument, - it has the same behavior as the - :meth:`ServerProtocol.select_subprotocol - ` method. - process_request: Intercept the request during the opening handshake. - Return an HTTP response to force the response or :obj:`None` to - continue normally. When you force an HTTP 101 Continue response, the - handshake is successful. Else, the connection is aborted. - ``process_request`` may be a function or a coroutine. - process_response: Intercept the response during the opening handshake. - Return an HTTP response to force the response or :obj:`None` to - continue normally. When you force an HTTP 101 Continue response, the - handshake is successful. Else, the connection is aborted. - ``process_response`` may be a function or a coroutine. - server_header: Value of the ``Server`` response header. - It defaults to ``"Python/x.y.z websockets/X.Y"``. Setting it to - :obj:`None` removes the header. - compression: The "permessage-deflate" extension is enabled by default. - Set ``compression`` to :obj:`None` to disable it. See the - :doc:`compression guide <../../topics/compression>` for details. - open_timeout: Timeout for opening connections in seconds. - :obj:`None` disables the timeout. - ping_interval: Interval between keepalive pings in seconds. - :obj:`None` disables keepalive. - ping_timeout: Timeout for keepalive pings in seconds. - :obj:`None` disables timeouts. - close_timeout: Timeout for closing connections in seconds. - :obj:`None` disables the timeout. - max_size: Maximum size of incoming messages in bytes. - :obj:`None` disables the limit. - max_queue: High-water mark of the buffer where frames are received. - It defaults to 16 frames. The low-water mark defaults to ``max_queue - // 4``. You may pass a ``(high, low)`` tuple to set the high-water - and low-water marks. - write_limit: High-water mark of write buffer in bytes. It is passed to - :meth:`~asyncio.WriteTransport.set_write_buffer_limits`. It defaults - to 32 KiB. You may pass a ``(high, low)`` tuple to set the - high-water and low-water marks. - logger: Logger for this server. - It defaults to ``logging.getLogger("websockets.server")``. See the - :doc:`logging guide <../../topics/logging>` for details. - create_connection: Factory for the :class:`ServerConnection` managing - the connection. Set it to a wrapper or a subclass to customize - connection handling. - - Any other keyword arguments are passed to the event loop's - :meth:`~asyncio.loop.create_server` method. - - For example: - - * You can set ``ssl`` to a :class:`~ssl.SSLContext` to enable TLS. - - * You can set ``sock`` to provide a preexisting TCP socket. You may call - :func:`socket.create_server` (not to be confused with the event loop's - :meth:`~asyncio.loop.create_server` method) to create a suitable server - socket and customize it. - - * You can set ``start_serving`` to ``False`` to start accepting connections - only after you call :meth:`~Server.start_serving()` or - :meth:`~Server.serve_forever()`. - - """ - - def __init__( - self, - handler: Callable[[ServerConnection], Awaitable[None]], - host: str | None = None, - port: int | None = None, - *, - # WebSocket - origins: Sequence[Origin | None] | None = None, - extensions: Sequence[ServerExtensionFactory] | None = None, - subprotocols: Sequence[Subprotocol] | None = None, - select_subprotocol: ( - Callable[ - [ServerConnection, Sequence[Subprotocol]], - Subprotocol | None, - ] - | None - ) = None, - process_request: ( - Callable[ - [ServerConnection, Request], - Awaitable[Response | None] | Response | None, - ] - | None - ) = None, - process_response: ( - Callable[ - [ServerConnection, Request, Response], - Awaitable[Response | None] | Response | None, - ] - | None - ) = None, - server_header: str | None = SERVER, - compression: str | None = "deflate", - # Timeouts - open_timeout: float | None = 10, - ping_interval: float | None = 20, - ping_timeout: float | None = 20, - close_timeout: float | None = 10, - # Limits - max_size: int | None = 2**20, - max_queue: int | tuple[int, int | None] = 16, - write_limit: int | tuple[int, int | None] = 2**15, - # Logging - logger: LoggerLike | None = None, - # Escape hatch for advanced customization - create_connection: type[ServerConnection] | None = None, - # Other keyword arguments are passed to loop.create_server - **kwargs: Any, - ) -> None: - if subprotocols is not None: - validate_subprotocols(subprotocols) - - if compression == "deflate": - extensions = enable_server_permessage_deflate(extensions) - elif compression is not None: - raise ValueError(f"unsupported compression: {compression}") - - if create_connection is None: - create_connection = ServerConnection - - self.server = Server( - handler, - process_request=process_request, - process_response=process_response, - server_header=server_header, - open_timeout=open_timeout, - logger=logger, - ) - - if kwargs.get("ssl") is not None: - kwargs.setdefault("ssl_handshake_timeout", open_timeout) - if sys.version_info[:2] >= (3, 11): # pragma: no branch - kwargs.setdefault("ssl_shutdown_timeout", close_timeout) - - def factory() -> ServerConnection: - """ - Create an asyncio protocol for managing a WebSocket connection. - - """ - # Create a closure to give select_subprotocol access to connection. - protocol_select_subprotocol: ( - Callable[ - [ServerProtocol, Sequence[Subprotocol]], - Subprotocol | None, - ] - | None - ) = None - if select_subprotocol is not None: - - def protocol_select_subprotocol( - protocol: ServerProtocol, - subprotocols: Sequence[Subprotocol], - ) -> Subprotocol | None: - # mypy doesn't know that select_subprotocol is immutable. - assert select_subprotocol is not None - # Ensure this function is only used in the intended context. - assert protocol is connection.protocol - return select_subprotocol(connection, subprotocols) - - # This is a protocol in the Sans-I/O implementation of websockets. - protocol = ServerProtocol( - origins=origins, - extensions=extensions, - subprotocols=subprotocols, - select_subprotocol=protocol_select_subprotocol, - max_size=max_size, - logger=logger, - ) - # This is a connection in websockets and a protocol in asyncio. - connection = create_connection( - protocol, - self.server, - ping_interval=ping_interval, - ping_timeout=ping_timeout, - close_timeout=close_timeout, - max_queue=max_queue, - write_limit=write_limit, - ) - return connection - - loop = asyncio.get_running_loop() - if kwargs.pop("unix", False): - self.create_server = loop.create_unix_server(factory, **kwargs) - else: - # mypy cannot tell that kwargs must provide sock when port is None. - self.create_server = loop.create_server(factory, host, port, **kwargs) # type: ignore[arg-type] - - # async with serve(...) as ...: ... - - async def __aenter__(self) -> Server: - return await self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - traceback: TracebackType | None, - ) -> None: - self.server.close() - await self.server.wait_closed() - - # ... = await serve(...) - - def __await__(self) -> Generator[Any, None, Server]: - # Create a suitable iterator by calling __await__ on a coroutine. - return self.__await_impl__().__await__() - - async def __await_impl__(self) -> Server: - server = await self.create_server - self.server.wrap(server) - return self.server - - # ... = yield from serve(...) - remove when dropping Python < 3.10 - - __iter__ = __await__ - - -def unix_serve( - handler: Callable[[ServerConnection], Awaitable[None]], - path: str | None = None, - **kwargs: Any, -) -> Awaitable[Server]: - """ - Create a WebSocket server listening on a Unix socket. - - This function is identical to :func:`serve`, except the ``host`` and - ``port`` arguments are replaced by ``path``. It's only available on Unix. - - It's useful for deploying a server behind a reverse proxy such as nginx. - - Args: - handler: Connection handler. It receives the WebSocket connection, - which is a :class:`ServerConnection`, in argument. - path: File system path to the Unix socket. - - """ - return serve(handler, unix=True, path=path, **kwargs) - - -def is_credentials(credentials: Any) -> bool: - try: - username, password = credentials - except (TypeError, ValueError): - return False - else: - return isinstance(username, str) and isinstance(password, str) - - -def basic_auth( - realm: str = "", - credentials: tuple[str, str] | Iterable[tuple[str, str]] | None = None, - check_credentials: Callable[[str, str], Awaitable[bool] | bool] | None = None, -) -> Callable[[ServerConnection, Request], Awaitable[Response | None]]: - """ - Factory for ``process_request`` to enforce HTTP Basic Authentication. - - :func:`basic_auth` is designed to integrate with :func:`serve` as follows:: - - from websockets.asyncio.server import basic_auth, serve - - async with serve( - ..., - process_request=basic_auth( - realm="my dev server", - credentials=("hello", "iloveyou"), - ), - ): - - If authentication succeeds, the connection's ``username`` attribute is set. - If it fails, the server responds with an HTTP 401 Unauthorized status. - - One of ``credentials`` or ``check_credentials`` must be provided; not both. - - Args: - realm: Scope of protection. It should contain only ASCII characters - because the encoding of non-ASCII characters is undefined. Refer to - section 2.2 of :rfc:`7235` for details. - credentials: Hard coded authorized credentials. It can be a - ``(username, password)`` pair or a list of such pairs. - check_credentials: Function or coroutine that verifies credentials. - It receives ``username`` and ``password`` arguments and returns - whether they're valid. - Raises: - TypeError: If ``credentials`` or ``check_credentials`` is wrong. - - """ - if (credentials is None) == (check_credentials is None): - raise TypeError("provide either credentials or check_credentials") - - if credentials is not None: - if is_credentials(credentials): - credentials_list = [cast(Tuple[str, str], credentials)] - elif isinstance(credentials, Iterable): - credentials_list = list(cast(Iterable[Tuple[str, str]], credentials)) - if not all(is_credentials(item) for item in credentials_list): - raise TypeError(f"invalid credentials argument: {credentials}") - else: - raise TypeError(f"invalid credentials argument: {credentials}") - - credentials_dict = dict(credentials_list) - - def check_credentials(username: str, password: str) -> bool: - try: - expected_password = credentials_dict[username] - except KeyError: - return False - return hmac.compare_digest(expected_password, password) - - assert check_credentials is not None # help mypy - - async def process_request( - connection: ServerConnection, - request: Request, - ) -> Response | None: - """ - Perform HTTP Basic Authentication. - - If it succeeds, set the connection's ``username`` attribute and return - :obj:`None`. If it fails, return an HTTP 401 Unauthorized responss. - - """ - try: - authorization = request.headers["Authorization"] - except KeyError: - response = connection.respond( - http.HTTPStatus.UNAUTHORIZED, - "Missing credentials\n", - ) - response.headers["WWW-Authenticate"] = build_www_authenticate_basic(realm) - return response - - try: - username, password = parse_authorization_basic(authorization) - except InvalidHeader: - response = connection.respond( - http.HTTPStatus.UNAUTHORIZED, - "Unsupported credentials\n", - ) - response.headers["WWW-Authenticate"] = build_www_authenticate_basic(realm) - return response - - valid_credentials = check_credentials(username, password) - if isinstance(valid_credentials, Awaitable): - valid_credentials = await valid_credentials - - if not valid_credentials: - response = connection.respond( - http.HTTPStatus.UNAUTHORIZED, - "Invalid credentials\n", - ) - response.headers["WWW-Authenticate"] = build_www_authenticate_basic(realm) - return response - - connection.username = username - return None - - return process_request diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/auth.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/auth.py deleted file mode 100644 index b792e02..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/auth.py +++ /dev/null @@ -1,6 +0,0 @@ -from __future__ import annotations - -# See #940 for why lazy_import isn't used here for backwards compatibility. -# See #1400 for why listing compatibility imports in __all__ helps PyCharm. -from .legacy.auth import * -from .legacy.auth import __all__ # noqa: F401 diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/client.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/client.py deleted file mode 100644 index e5f2949..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/client.py +++ /dev/null @@ -1,393 +0,0 @@ -from __future__ import annotations - -import os -import random -import warnings -from typing import Any, Generator, Sequence - -from .datastructures import Headers, MultipleValuesError -from .exceptions import ( - InvalidHandshake, - InvalidHeader, - InvalidHeaderValue, - InvalidStatus, - InvalidUpgrade, - NegotiationError, -) -from .extensions import ClientExtensionFactory, Extension -from .headers import ( - build_authorization_basic, - build_extension, - build_host, - build_subprotocol, - parse_connection, - parse_extension, - parse_subprotocol, - parse_upgrade, -) -from .http11 import Request, Response -from .protocol import CLIENT, CONNECTING, OPEN, Protocol, State -from .typing import ( - ConnectionOption, - ExtensionHeader, - LoggerLike, - Origin, - Subprotocol, - UpgradeProtocol, -) -from .uri import WebSocketURI -from .utils import accept_key, generate_key - - -# See #940 for why lazy_import isn't used here for backwards compatibility. -# See #1400 for why listing compatibility imports in __all__ helps PyCharm. -from .legacy.client import * # isort:skip # noqa: I001 -from .legacy.client import __all__ as legacy__all__ - - -__all__ = ["ClientProtocol"] + legacy__all__ - - -class ClientProtocol(Protocol): - """ - Sans-I/O implementation of a WebSocket client connection. - - Args: - wsuri: URI of the WebSocket server, parsed - with :func:`~websockets.uri.parse_uri`. - origin: Value of the ``Origin`` header. This is useful when connecting - to a server that validates the ``Origin`` header to defend against - Cross-Site WebSocket Hijacking attacks. - extensions: List of supported extensions, in order in which they - should be tried. - subprotocols: List of supported subprotocols, in order of decreasing - preference. - state: Initial state of the WebSocket connection. - max_size: Maximum size of incoming messages in bytes; - :obj:`None` disables the limit. - logger: Logger for this connection; - defaults to ``logging.getLogger("websockets.client")``; - see the :doc:`logging guide <../../topics/logging>` for details. - - """ - - def __init__( - self, - wsuri: WebSocketURI, - *, - origin: Origin | None = None, - extensions: Sequence[ClientExtensionFactory] | None = None, - subprotocols: Sequence[Subprotocol] | None = None, - state: State = CONNECTING, - max_size: int | None = 2**20, - logger: LoggerLike | None = None, - ) -> None: - super().__init__( - side=CLIENT, - state=state, - max_size=max_size, - logger=logger, - ) - self.wsuri = wsuri - self.origin = origin - self.available_extensions = extensions - self.available_subprotocols = subprotocols - self.key = generate_key() - - def connect(self) -> Request: - """ - Create a handshake request to open a connection. - - You must send the handshake request with :meth:`send_request`. - - You can modify it before sending it, for example to add HTTP headers. - - Returns: - WebSocket handshake request event to send to the server. - - """ - headers = Headers() - - headers["Host"] = build_host( - self.wsuri.host, self.wsuri.port, self.wsuri.secure - ) - - if self.wsuri.user_info: - headers["Authorization"] = build_authorization_basic(*self.wsuri.user_info) - - if self.origin is not None: - headers["Origin"] = self.origin - - headers["Upgrade"] = "websocket" - headers["Connection"] = "Upgrade" - headers["Sec-WebSocket-Key"] = self.key - headers["Sec-WebSocket-Version"] = "13" - - if self.available_extensions is not None: - extensions_header = build_extension( - [ - (extension_factory.name, extension_factory.get_request_params()) - for extension_factory in self.available_extensions - ] - ) - headers["Sec-WebSocket-Extensions"] = extensions_header - - if self.available_subprotocols is not None: - protocol_header = build_subprotocol(self.available_subprotocols) - headers["Sec-WebSocket-Protocol"] = protocol_header - - return Request(self.wsuri.resource_name, headers) - - def process_response(self, response: Response) -> None: - """ - Check a handshake response. - - Args: - request: WebSocket handshake response received from the server. - - Raises: - InvalidHandshake: If the handshake response is invalid. - - """ - - if response.status_code != 101: - raise InvalidStatus(response) - - headers = response.headers - - connection: list[ConnectionOption] = sum( - [parse_connection(value) for value in headers.get_all("Connection")], [] - ) - - if not any(value.lower() == "upgrade" for value in connection): - raise InvalidUpgrade( - "Connection", ", ".join(connection) if connection else None - ) - - upgrade: list[UpgradeProtocol] = sum( - [parse_upgrade(value) for value in headers.get_all("Upgrade")], [] - ) - - # For compatibility with non-strict implementations, ignore case when - # checking the Upgrade header. It's supposed to be 'WebSocket'. - if not (len(upgrade) == 1 and upgrade[0].lower() == "websocket"): - raise InvalidUpgrade("Upgrade", ", ".join(upgrade) if upgrade else None) - - try: - s_w_accept = headers["Sec-WebSocket-Accept"] - except KeyError: - raise InvalidHeader("Sec-WebSocket-Accept") from None - except MultipleValuesError: - raise InvalidHeader("Sec-WebSocket-Accept", "multiple values") from None - - if s_w_accept != accept_key(self.key): - raise InvalidHeaderValue("Sec-WebSocket-Accept", s_w_accept) - - self.extensions = self.process_extensions(headers) - - self.subprotocol = self.process_subprotocol(headers) - - def process_extensions(self, headers: Headers) -> list[Extension]: - """ - Handle the Sec-WebSocket-Extensions HTTP response header. - - Check that each extension is supported, as well as its parameters. - - :rfc:`6455` leaves the rules up to the specification of each - extension. - - To provide this level of flexibility, for each extension accepted by - the server, we check for a match with each extension available in the - client configuration. If no match is found, an exception is raised. - - If several variants of the same extension are accepted by the server, - it may be configured several times, which won't make sense in general. - Extensions must implement their own requirements. For this purpose, - the list of previously accepted extensions is provided. - - Other requirements, for example related to mandatory extensions or the - order of extensions, may be implemented by overriding this method. - - Args: - headers: WebSocket handshake response headers. - - Returns: - List of accepted extensions. - - Raises: - InvalidHandshake: To abort the handshake. - - """ - accepted_extensions: list[Extension] = [] - - extensions = headers.get_all("Sec-WebSocket-Extensions") - - if extensions: - if self.available_extensions is None: - raise NegotiationError("no extensions supported") - - parsed_extensions: list[ExtensionHeader] = sum( - [parse_extension(header_value) for header_value in extensions], [] - ) - - for name, response_params in parsed_extensions: - for extension_factory in self.available_extensions: - # Skip non-matching extensions based on their name. - if extension_factory.name != name: - continue - - # Skip non-matching extensions based on their params. - try: - extension = extension_factory.process_response_params( - response_params, accepted_extensions - ) - except NegotiationError: - continue - - # Add matching extension to the final list. - accepted_extensions.append(extension) - - # Break out of the loop once we have a match. - break - - # If we didn't break from the loop, no extension in our list - # matched what the server sent. Fail the connection. - else: - raise NegotiationError( - f"Unsupported extension: " - f"name = {name}, params = {response_params}" - ) - - return accepted_extensions - - def process_subprotocol(self, headers: Headers) -> Subprotocol | None: - """ - Handle the Sec-WebSocket-Protocol HTTP response header. - - If provided, check that it contains exactly one supported subprotocol. - - Args: - headers: WebSocket handshake response headers. - - Returns: - Subprotocol, if one was selected. - - """ - subprotocol: Subprotocol | None = None - - subprotocols = headers.get_all("Sec-WebSocket-Protocol") - - if subprotocols: - if self.available_subprotocols is None: - raise NegotiationError("no subprotocols supported") - - parsed_subprotocols: Sequence[Subprotocol] = sum( - [parse_subprotocol(header_value) for header_value in subprotocols], [] - ) - - if len(parsed_subprotocols) > 1: - raise InvalidHeader( - "Sec-WebSocket-Protocol", - f"multiple values: {', '.join(parsed_subprotocols)}", - ) - - subprotocol = parsed_subprotocols[0] - - if subprotocol not in self.available_subprotocols: - raise NegotiationError(f"unsupported subprotocol: {subprotocol}") - - return subprotocol - - def send_request(self, request: Request) -> None: - """ - Send a handshake request to the server. - - Args: - request: WebSocket handshake request event. - - """ - if self.debug: - self.logger.debug("> GET %s HTTP/1.1", request.path) - for key, value in request.headers.raw_items(): - self.logger.debug("> %s: %s", key, value) - - self.writes.append(request.serialize()) - - def parse(self) -> Generator[None, None, None]: - if self.state is CONNECTING: - try: - response = yield from Response.parse( - self.reader.read_line, - self.reader.read_exact, - self.reader.read_to_eof, - ) - except Exception as exc: - self.handshake_exc = exc - self.send_eof() - self.parser = self.discard() - next(self.parser) # start coroutine - yield - - if self.debug: - code, phrase = response.status_code, response.reason_phrase - self.logger.debug("< HTTP/1.1 %d %s", code, phrase) - for key, value in response.headers.raw_items(): - self.logger.debug("< %s: %s", key, value) - if response.body is not None: - self.logger.debug("< [body] (%d bytes)", len(response.body)) - - try: - self.process_response(response) - except InvalidHandshake as exc: - response._exception = exc - self.events.append(response) - self.handshake_exc = exc - self.send_eof() - self.parser = self.discard() - next(self.parser) # start coroutine - yield - - assert self.state is CONNECTING - self.state = OPEN - self.events.append(response) - - yield from super().parse() - - -class ClientConnection(ClientProtocol): - def __init__(self, *args: Any, **kwargs: Any) -> None: - warnings.warn( # deprecated in 11.0 - 2023-04-02 - "ClientConnection was renamed to ClientProtocol", - DeprecationWarning, - ) - super().__init__(*args, **kwargs) - - -BACKOFF_INITIAL_DELAY = float(os.environ.get("WEBSOCKETS_BACKOFF_INITIAL_DELAY", "5")) -BACKOFF_MIN_DELAY = float(os.environ.get("WEBSOCKETS_BACKOFF_MIN_DELAY", "3.1")) -BACKOFF_MAX_DELAY = float(os.environ.get("WEBSOCKETS_BACKOFF_MAX_DELAY", "90.0")) -BACKOFF_FACTOR = float(os.environ.get("WEBSOCKETS_BACKOFF_FACTOR", "1.618")) - - -def backoff( - initial_delay: float = BACKOFF_INITIAL_DELAY, - min_delay: float = BACKOFF_MIN_DELAY, - max_delay: float = BACKOFF_MAX_DELAY, - factor: float = BACKOFF_FACTOR, -) -> Generator[float, None, None]: - """ - Generate a series of backoff delays between reconnection attempts. - - Yields: - How many seconds to wait before retrying to connect. - - """ - # Add a random initial delay between 0 and 5 seconds. - # See 7.2.3. Recovering from Abnormal Closure in RFC 6455. - yield random.random() * initial_delay - delay = min_delay - while delay < max_delay: - yield delay - delay *= factor - while True: - yield max_delay diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/connection.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/connection.py deleted file mode 100644 index 5e78e34..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/connection.py +++ /dev/null @@ -1,12 +0,0 @@ -from __future__ import annotations - -import warnings - -from .protocol import SEND_EOF, Protocol as Connection, Side, State # noqa: F401 - - -warnings.warn( # deprecated in 11.0 - 2023-04-02 - "websockets.connection was renamed to websockets.protocol " - "and Connection was renamed to Protocol", - DeprecationWarning, -) diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/datastructures.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/datastructures.py deleted file mode 100644 index 106d6f3..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/datastructures.py +++ /dev/null @@ -1,192 +0,0 @@ -from __future__ import annotations - -from typing import ( - Any, - Iterable, - Iterator, - Mapping, - MutableMapping, - Protocol, - Tuple, - Union, -) - - -__all__ = ["Headers", "HeadersLike", "MultipleValuesError"] - - -class MultipleValuesError(LookupError): - """ - Exception raised when :class:`Headers` has multiple values for a key. - - """ - - def __str__(self) -> str: - # Implement the same logic as KeyError_str in Objects/exceptions.c. - if len(self.args) == 1: - return repr(self.args[0]) - return super().__str__() - - -class Headers(MutableMapping[str, str]): - """ - Efficient data structure for manipulating HTTP headers. - - A :class:`list` of ``(name, values)`` is inefficient for lookups. - - A :class:`dict` doesn't suffice because header names are case-insensitive - and multiple occurrences of headers with the same name are possible. - - :class:`Headers` stores HTTP headers in a hybrid data structure to provide - efficient insertions and lookups while preserving the original data. - - In order to account for multiple values with minimal hassle, - :class:`Headers` follows this logic: - - - When getting a header with ``headers[name]``: - - if there's no value, :exc:`KeyError` is raised; - - if there's exactly one value, it's returned; - - if there's more than one value, :exc:`MultipleValuesError` is raised. - - - When setting a header with ``headers[name] = value``, the value is - appended to the list of values for that header. - - - When deleting a header with ``del headers[name]``, all values for that - header are removed (this is slow). - - Other methods for manipulating headers are consistent with this logic. - - As long as no header occurs multiple times, :class:`Headers` behaves like - :class:`dict`, except keys are lower-cased to provide case-insensitivity. - - Two methods support manipulating multiple values explicitly: - - - :meth:`get_all` returns a list of all values for a header; - - :meth:`raw_items` returns an iterator of ``(name, values)`` pairs. - - """ - - __slots__ = ["_dict", "_list"] - - # Like dict, Headers accepts an optional "mapping or iterable" argument. - def __init__(self, *args: HeadersLike, **kwargs: str) -> None: - self._dict: dict[str, list[str]] = {} - self._list: list[tuple[str, str]] = [] - self.update(*args, **kwargs) - - def __str__(self) -> str: - return "".join(f"{key}: {value}\r\n" for key, value in self._list) + "\r\n" - - def __repr__(self) -> str: - return f"{self.__class__.__name__}({self._list!r})" - - def copy(self) -> Headers: - copy = self.__class__() - copy._dict = self._dict.copy() - copy._list = self._list.copy() - return copy - - def serialize(self) -> bytes: - # Since headers only contain ASCII characters, we can keep this simple. - return str(self).encode() - - # Collection methods - - def __contains__(self, key: object) -> bool: - return isinstance(key, str) and key.lower() in self._dict - - def __iter__(self) -> Iterator[str]: - return iter(self._dict) - - def __len__(self) -> int: - return len(self._dict) - - # MutableMapping methods - - def __getitem__(self, key: str) -> str: - value = self._dict[key.lower()] - if len(value) == 1: - return value[0] - else: - raise MultipleValuesError(key) - - def __setitem__(self, key: str, value: str) -> None: - self._dict.setdefault(key.lower(), []).append(value) - self._list.append((key, value)) - - def __delitem__(self, key: str) -> None: - key_lower = key.lower() - self._dict.__delitem__(key_lower) - # This is inefficient. Fortunately deleting HTTP headers is uncommon. - self._list = [(k, v) for k, v in self._list if k.lower() != key_lower] - - def __eq__(self, other: Any) -> bool: - if not isinstance(other, Headers): - return NotImplemented - return self._dict == other._dict - - def clear(self) -> None: - """ - Remove all headers. - - """ - self._dict = {} - self._list = [] - - def update(self, *args: HeadersLike, **kwargs: str) -> None: - """ - Update from a :class:`Headers` instance and/or keyword arguments. - - """ - args = tuple( - arg.raw_items() if isinstance(arg, Headers) else arg for arg in args - ) - super().update(*args, **kwargs) - - # Methods for handling multiple values - - def get_all(self, key: str) -> list[str]: - """ - Return the (possibly empty) list of all values for a header. - - Args: - key: Header name. - - """ - return self._dict.get(key.lower(), []) - - def raw_items(self) -> Iterator[tuple[str, str]]: - """ - Return an iterator of all values as ``(name, value)`` pairs. - - """ - return iter(self._list) - - -# copy of _typeshed.SupportsKeysAndGetItem. -class SupportsKeysAndGetItem(Protocol): # pragma: no cover - """ - Dict-like types with ``keys() -> str`` and ``__getitem__(key: str) -> str`` methods. - - """ - - def keys(self) -> Iterable[str]: ... - - def __getitem__(self, key: str) -> str: ... - - -# Change to Headers | Mapping[str, str] | ... when dropping Python < 3.10. -HeadersLike = Union[ - Headers, - Mapping[str, str], - # Change to tuple[str, str] when dropping Python < 3.9. - Iterable[Tuple[str, str]], - SupportsKeysAndGetItem, -] -""" -Types accepted where :class:`Headers` is expected. - -In addition to :class:`Headers` itself, this includes dict-like types where both -keys and values are :class:`str`. - -""" diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/exceptions.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/exceptions.py deleted file mode 100644 index d723f2f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/exceptions.py +++ /dev/null @@ -1,392 +0,0 @@ -""" -:mod:`websockets.exceptions` defines the following hierarchy of exceptions. - -* :exc:`WebSocketException` - * :exc:`ConnectionClosed` - * :exc:`ConnectionClosedOK` - * :exc:`ConnectionClosedError` - * :exc:`InvalidURI` - * :exc:`InvalidHandshake` - * :exc:`SecurityError` - * :exc:`InvalidMessage` (legacy) - * :exc:`InvalidStatus` - * :exc:`InvalidStatusCode` (legacy) - * :exc:`InvalidHeader` - * :exc:`InvalidHeaderFormat` - * :exc:`InvalidHeaderValue` - * :exc:`InvalidOrigin` - * :exc:`InvalidUpgrade` - * :exc:`NegotiationError` - * :exc:`DuplicateParameter` - * :exc:`InvalidParameterName` - * :exc:`InvalidParameterValue` - * :exc:`AbortHandshake` (legacy) - * :exc:`RedirectHandshake` (legacy) - * :exc:`ProtocolError` (Sans-I/O) - * :exc:`PayloadTooBig` (Sans-I/O) - * :exc:`InvalidState` (Sans-I/O) - * :exc:`ConcurrencyError` - -""" - -from __future__ import annotations - -import typing -import warnings - -from .imports import lazy_import - - -__all__ = [ - "WebSocketException", - "ConnectionClosed", - "ConnectionClosedOK", - "ConnectionClosedError", - "InvalidURI", - "InvalidHandshake", - "SecurityError", - "InvalidMessage", - "InvalidStatus", - "InvalidStatusCode", - "InvalidHeader", - "InvalidHeaderFormat", - "InvalidHeaderValue", - "InvalidOrigin", - "InvalidUpgrade", - "NegotiationError", - "DuplicateParameter", - "InvalidParameterName", - "InvalidParameterValue", - "AbortHandshake", - "RedirectHandshake", - "ProtocolError", - "WebSocketProtocolError", - "PayloadTooBig", - "InvalidState", - "ConcurrencyError", -] - - -class WebSocketException(Exception): - """ - Base class for all exceptions defined by websockets. - - """ - - -class ConnectionClosed(WebSocketException): - """ - Raised when trying to interact with a closed connection. - - Attributes: - rcvd: If a close frame was received, its code and reason are available - in ``rcvd.code`` and ``rcvd.reason``. - sent: If a close frame was sent, its code and reason are available - in ``sent.code`` and ``sent.reason``. - rcvd_then_sent: If close frames were received and sent, this attribute - tells in which order this happened, from the perspective of this - side of the connection. - - """ - - def __init__( - self, - rcvd: frames.Close | None, - sent: frames.Close | None, - rcvd_then_sent: bool | None = None, - ) -> None: - self.rcvd = rcvd - self.sent = sent - self.rcvd_then_sent = rcvd_then_sent - assert (self.rcvd_then_sent is None) == (self.rcvd is None or self.sent is None) - - def __str__(self) -> str: - if self.rcvd is None: - if self.sent is None: - return "no close frame received or sent" - else: - return f"sent {self.sent}; no close frame received" - else: - if self.sent is None: - return f"received {self.rcvd}; no close frame sent" - else: - if self.rcvd_then_sent: - return f"received {self.rcvd}; then sent {self.sent}" - else: - return f"sent {self.sent}; then received {self.rcvd}" - - # code and reason attributes are provided for backwards-compatibility - - @property - def code(self) -> int: - warnings.warn( # deprecated in 13.1 - "ConnectionClosed.code is deprecated; " - "use Protocol.close_code or ConnectionClosed.rcvd.code", - DeprecationWarning, - ) - if self.rcvd is None: - return frames.CloseCode.ABNORMAL_CLOSURE - return self.rcvd.code - - @property - def reason(self) -> str: - warnings.warn( # deprecated in 13.1 - "ConnectionClosed.reason is deprecated; " - "use Protocol.close_reason or ConnectionClosed.rcvd.reason", - DeprecationWarning, - ) - if self.rcvd is None: - return "" - return self.rcvd.reason - - -class ConnectionClosedOK(ConnectionClosed): - """ - Like :exc:`ConnectionClosed`, when the connection terminated properly. - - A close code with code 1000 (OK) or 1001 (going away) or without a code was - received and sent. - - """ - - -class ConnectionClosedError(ConnectionClosed): - """ - Like :exc:`ConnectionClosed`, when the connection terminated with an error. - - A close frame with a code other than 1000 (OK) or 1001 (going away) was - received or sent, or the closing handshake didn't complete properly. - - """ - - -class InvalidURI(WebSocketException): - """ - Raised when connecting to a URI that isn't a valid WebSocket URI. - - """ - - def __init__(self, uri: str, msg: str) -> None: - self.uri = uri - self.msg = msg - - def __str__(self) -> str: - return f"{self.uri} isn't a valid URI: {self.msg}" - - -class InvalidHandshake(WebSocketException): - """ - Base class for exceptions raised when the opening handshake fails. - - """ - - -class SecurityError(InvalidHandshake): - """ - Raised when a handshake request or response breaks a security rule. - - Security limits can be configured with :doc:`environment variables - <../reference/variables>`. - - """ - - -class InvalidStatus(InvalidHandshake): - """ - Raised when a handshake response rejects the WebSocket upgrade. - - """ - - def __init__(self, response: http11.Response) -> None: - self.response = response - - def __str__(self) -> str: - return ( - "server rejected WebSocket connection: " - f"HTTP {self.response.status_code:d}" - ) - - -class InvalidHeader(InvalidHandshake): - """ - Raised when an HTTP header doesn't have a valid format or value. - - """ - - def __init__(self, name: str, value: str | None = None) -> None: - self.name = name - self.value = value - - def __str__(self) -> str: - if self.value is None: - return f"missing {self.name} header" - elif self.value == "": - return f"empty {self.name} header" - else: - return f"invalid {self.name} header: {self.value}" - - -class InvalidHeaderFormat(InvalidHeader): - """ - Raised when an HTTP header cannot be parsed. - - The format of the header doesn't match the grammar for that header. - - """ - - def __init__(self, name: str, error: str, header: str, pos: int) -> None: - super().__init__(name, f"{error} at {pos} in {header}") - - -class InvalidHeaderValue(InvalidHeader): - """ - Raised when an HTTP header has a wrong value. - - The format of the header is correct but the value isn't acceptable. - - """ - - -class InvalidOrigin(InvalidHeader): - """ - Raised when the Origin header in a request isn't allowed. - - """ - - def __init__(self, origin: str | None) -> None: - super().__init__("Origin", origin) - - -class InvalidUpgrade(InvalidHeader): - """ - Raised when the Upgrade or Connection header isn't correct. - - """ - - -class NegotiationError(InvalidHandshake): - """ - Raised when negotiating an extension or a subprotocol fails. - - """ - - -class DuplicateParameter(NegotiationError): - """ - Raised when a parameter name is repeated in an extension header. - - """ - - def __init__(self, name: str) -> None: - self.name = name - - def __str__(self) -> str: - return f"duplicate parameter: {self.name}" - - -class InvalidParameterName(NegotiationError): - """ - Raised when a parameter name in an extension header is invalid. - - """ - - def __init__(self, name: str) -> None: - self.name = name - - def __str__(self) -> str: - return f"invalid parameter name: {self.name}" - - -class InvalidParameterValue(NegotiationError): - """ - Raised when a parameter value in an extension header is invalid. - - """ - - def __init__(self, name: str, value: str | None) -> None: - self.name = name - self.value = value - - def __str__(self) -> str: - if self.value is None: - return f"missing value for parameter {self.name}" - elif self.value == "": - return f"empty value for parameter {self.name}" - else: - return f"invalid value for parameter {self.name}: {self.value}" - - -class ProtocolError(WebSocketException): - """ - Raised when receiving or sending a frame that breaks the protocol. - - The Sans-I/O implementation raises this exception when: - - * receiving or sending a frame that contains invalid data; - * receiving or sending an invalid sequence of frames. - - """ - - -class PayloadTooBig(WebSocketException): - """ - Raised when parsing a frame with a payload that exceeds the maximum size. - - The Sans-I/O layer uses this exception internally. It doesn't bubble up to - the I/O layer. - - The :meth:`~websockets.extensions.Extension.decode` method of extensions - must raise :exc:`PayloadTooBig` if decoding a frame would exceed the limit. - - """ - - -class InvalidState(WebSocketException, AssertionError): - """ - Raised when sending a frame is forbidden in the current state. - - Specifically, the Sans-I/O layer raises this exception when: - - * sending a data frame to a connection in a state other - :attr:`~websockets.protocol.State.OPEN`; - * sending a control frame to a connection in a state other than - :attr:`~websockets.protocol.State.OPEN` or - :attr:`~websockets.protocol.State.CLOSING`. - - """ - - -class ConcurrencyError(WebSocketException, RuntimeError): - """ - Raised when receiving or sending messages concurrently. - - WebSocket is a connection-oriented protocol. Reads must be serialized; so - must be writes. However, reading and writing concurrently is possible. - - """ - - -# When type checking, import non-deprecated aliases eagerly. Else, import on demand. -if typing.TYPE_CHECKING: - from .legacy.exceptions import ( - AbortHandshake, - InvalidMessage, - InvalidStatusCode, - RedirectHandshake, - ) - - WebSocketProtocolError = ProtocolError -else: - lazy_import( - globals(), - aliases={ - "AbortHandshake": ".legacy.exceptions", - "InvalidMessage": ".legacy.exceptions", - "InvalidStatusCode": ".legacy.exceptions", - "RedirectHandshake": ".legacy.exceptions", - "WebSocketProtocolError": ".legacy.exceptions", - }, - ) - -# At the bottom to break import cycles created by type annotations. -from . import frames, http11 # noqa: E402 diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/extensions/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/extensions/__init__.py deleted file mode 100644 index 02838b9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/extensions/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .base import * - - -__all__ = ["Extension", "ClientExtensionFactory", "ServerExtensionFactory"] diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/extensions/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/extensions/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 9af294c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/extensions/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/extensions/__pycache__/base.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/extensions/__pycache__/base.cpython-38.pyc deleted file mode 100644 index 1659bef..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/extensions/__pycache__/base.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/extensions/__pycache__/permessage_deflate.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/extensions/__pycache__/permessage_deflate.cpython-38.pyc deleted file mode 100644 index d0da03d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/extensions/__pycache__/permessage_deflate.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/extensions/base.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/extensions/base.py deleted file mode 100644 index 75bae6b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/extensions/base.py +++ /dev/null @@ -1,123 +0,0 @@ -from __future__ import annotations - -from typing import Sequence - -from ..frames import Frame -from ..typing import ExtensionName, ExtensionParameter - - -__all__ = ["Extension", "ClientExtensionFactory", "ServerExtensionFactory"] - - -class Extension: - """ - Base class for extensions. - - """ - - name: ExtensionName - """Extension identifier.""" - - def decode(self, frame: Frame, *, max_size: int | None = None) -> Frame: - """ - Decode an incoming frame. - - Args: - frame: Incoming frame. - max_size: Maximum payload size in bytes. - - Returns: - Decoded frame. - - Raises: - PayloadTooBig: If decoding the payload exceeds ``max_size``. - - """ - raise NotImplementedError - - def encode(self, frame: Frame) -> Frame: - """ - Encode an outgoing frame. - - Args: - frame: Outgoing frame. - - Returns: - Encoded frame. - - """ - raise NotImplementedError - - -class ClientExtensionFactory: - """ - Base class for client-side extension factories. - - """ - - name: ExtensionName - """Extension identifier.""" - - def get_request_params(self) -> list[ExtensionParameter]: - """ - Build parameters to send to the server for this extension. - - Returns: - Parameters to send to the server. - - """ - raise NotImplementedError - - def process_response_params( - self, - params: Sequence[ExtensionParameter], - accepted_extensions: Sequence[Extension], - ) -> Extension: - """ - Process parameters received from the server. - - Args: - params: Parameters received from the server for this extension. - accepted_extensions: List of previously accepted extensions. - - Returns: - An extension instance. - - Raises: - NegotiationError: If parameters aren't acceptable. - - """ - raise NotImplementedError - - -class ServerExtensionFactory: - """ - Base class for server-side extension factories. - - """ - - name: ExtensionName - """Extension identifier.""" - - def process_request_params( - self, - params: Sequence[ExtensionParameter], - accepted_extensions: Sequence[Extension], - ) -> tuple[list[ExtensionParameter], Extension]: - """ - Process parameters received from the client. - - Args: - params: Parameters received from the client for this extension. - accepted_extensions: List of previously accepted extensions. - - Returns: - To accept the offer, parameters to send to the client for this - extension and an extension instance. - - Raises: - NegotiationError: To reject the offer, if parameters received from - the client aren't acceptable. - - """ - raise NotImplementedError diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/extensions/permessage_deflate.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/extensions/permessage_deflate.py deleted file mode 100644 index 25d2c1c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/extensions/permessage_deflate.py +++ /dev/null @@ -1,670 +0,0 @@ -from __future__ import annotations - -import dataclasses -import zlib -from typing import Any, Sequence - -from .. import frames -from ..exceptions import ( - DuplicateParameter, - InvalidParameterName, - InvalidParameterValue, - NegotiationError, - PayloadTooBig, - ProtocolError, -) -from ..typing import ExtensionName, ExtensionParameter -from .base import ClientExtensionFactory, Extension, ServerExtensionFactory - - -__all__ = [ - "PerMessageDeflate", - "ClientPerMessageDeflateFactory", - "enable_client_permessage_deflate", - "ServerPerMessageDeflateFactory", - "enable_server_permessage_deflate", -] - -_EMPTY_UNCOMPRESSED_BLOCK = b"\x00\x00\xff\xff" - -_MAX_WINDOW_BITS_VALUES = [str(bits) for bits in range(8, 16)] - - -class PerMessageDeflate(Extension): - """ - Per-Message Deflate extension. - - """ - - name = ExtensionName("permessage-deflate") - - def __init__( - self, - remote_no_context_takeover: bool, - local_no_context_takeover: bool, - remote_max_window_bits: int, - local_max_window_bits: int, - compress_settings: dict[Any, Any] | None = None, - ) -> None: - """ - Configure the Per-Message Deflate extension. - - """ - if compress_settings is None: - compress_settings = {} - - assert remote_no_context_takeover in [False, True] - assert local_no_context_takeover in [False, True] - assert 8 <= remote_max_window_bits <= 15 - assert 8 <= local_max_window_bits <= 15 - assert "wbits" not in compress_settings - - self.remote_no_context_takeover = remote_no_context_takeover - self.local_no_context_takeover = local_no_context_takeover - self.remote_max_window_bits = remote_max_window_bits - self.local_max_window_bits = local_max_window_bits - self.compress_settings = compress_settings - - if not self.remote_no_context_takeover: - self.decoder = zlib.decompressobj(wbits=-self.remote_max_window_bits) - - if not self.local_no_context_takeover: - self.encoder = zlib.compressobj( - wbits=-self.local_max_window_bits, - **self.compress_settings, - ) - - # To handle continuation frames properly, we must keep track of - # whether that initial frame was encoded. - self.decode_cont_data = False - # There's no need for self.encode_cont_data because we always encode - # outgoing frames, so it would always be True. - - def __repr__(self) -> str: - return ( - f"PerMessageDeflate(" - f"remote_no_context_takeover={self.remote_no_context_takeover}, " - f"local_no_context_takeover={self.local_no_context_takeover}, " - f"remote_max_window_bits={self.remote_max_window_bits}, " - f"local_max_window_bits={self.local_max_window_bits})" - ) - - def decode( - self, - frame: frames.Frame, - *, - max_size: int | None = None, - ) -> frames.Frame: - """ - Decode an incoming frame. - - """ - # Skip control frames. - if frame.opcode in frames.CTRL_OPCODES: - return frame - - # Handle continuation data frames: - # - skip if the message isn't encoded - # - reset "decode continuation data" flag if it's a final frame - if frame.opcode is frames.OP_CONT: - if not self.decode_cont_data: - return frame - if frame.fin: - self.decode_cont_data = False - - # Handle text and binary data frames: - # - skip if the message isn't encoded - # - unset the rsv1 flag on the first frame of a compressed message - # - set "decode continuation data" flag if it's a non-final frame - else: - if not frame.rsv1: - return frame - frame = dataclasses.replace(frame, rsv1=False) - if not frame.fin: - self.decode_cont_data = True - - # Re-initialize per-message decoder. - if self.remote_no_context_takeover: - self.decoder = zlib.decompressobj(wbits=-self.remote_max_window_bits) - - # Uncompress data. Protect against zip bombs by preventing zlib from - # decompressing more than max_length bytes (except when the limit is - # disabled with max_size = None). - data = frame.data - if frame.fin: - data += _EMPTY_UNCOMPRESSED_BLOCK - max_length = 0 if max_size is None else max_size - try: - data = self.decoder.decompress(data, max_length) - except zlib.error as exc: - raise ProtocolError("decompression failed") from exc - if self.decoder.unconsumed_tail: - raise PayloadTooBig(f"over size limit (? > {max_size} bytes)") - - # Allow garbage collection of the decoder if it won't be reused. - if frame.fin and self.remote_no_context_takeover: - del self.decoder - - return dataclasses.replace(frame, data=data) - - def encode(self, frame: frames.Frame) -> frames.Frame: - """ - Encode an outgoing frame. - - """ - # Skip control frames. - if frame.opcode in frames.CTRL_OPCODES: - return frame - - # Since we always encode messages, there's no "encode continuation - # data" flag similar to "decode continuation data" at this time. - - if frame.opcode is not frames.OP_CONT: - # Set the rsv1 flag on the first frame of a compressed message. - frame = dataclasses.replace(frame, rsv1=True) - # Re-initialize per-message decoder. - if self.local_no_context_takeover: - self.encoder = zlib.compressobj( - wbits=-self.local_max_window_bits, - **self.compress_settings, - ) - - # Compress data. - data = self.encoder.compress(frame.data) + self.encoder.flush(zlib.Z_SYNC_FLUSH) - if frame.fin and data.endswith(_EMPTY_UNCOMPRESSED_BLOCK): - data = data[:-4] - - # Allow garbage collection of the encoder if it won't be reused. - if frame.fin and self.local_no_context_takeover: - del self.encoder - - return dataclasses.replace(frame, data=data) - - -def _build_parameters( - server_no_context_takeover: bool, - client_no_context_takeover: bool, - server_max_window_bits: int | None, - client_max_window_bits: int | bool | None, -) -> list[ExtensionParameter]: - """ - Build a list of ``(name, value)`` pairs for some compression parameters. - - """ - params: list[ExtensionParameter] = [] - if server_no_context_takeover: - params.append(("server_no_context_takeover", None)) - if client_no_context_takeover: - params.append(("client_no_context_takeover", None)) - if server_max_window_bits: - params.append(("server_max_window_bits", str(server_max_window_bits))) - if client_max_window_bits is True: # only in handshake requests - params.append(("client_max_window_bits", None)) - elif client_max_window_bits: - params.append(("client_max_window_bits", str(client_max_window_bits))) - return params - - -def _extract_parameters( - params: Sequence[ExtensionParameter], *, is_server: bool -) -> tuple[bool, bool, int | None, int | bool | None]: - """ - Extract compression parameters from a list of ``(name, value)`` pairs. - - If ``is_server`` is :obj:`True`, ``client_max_window_bits`` may be - provided without a value. This is only allowed in handshake requests. - - """ - server_no_context_takeover: bool = False - client_no_context_takeover: bool = False - server_max_window_bits: int | None = None - client_max_window_bits: int | bool | None = None - - for name, value in params: - if name == "server_no_context_takeover": - if server_no_context_takeover: - raise DuplicateParameter(name) - if value is None: - server_no_context_takeover = True - else: - raise InvalidParameterValue(name, value) - - elif name == "client_no_context_takeover": - if client_no_context_takeover: - raise DuplicateParameter(name) - if value is None: - client_no_context_takeover = True - else: - raise InvalidParameterValue(name, value) - - elif name == "server_max_window_bits": - if server_max_window_bits is not None: - raise DuplicateParameter(name) - if value in _MAX_WINDOW_BITS_VALUES: - server_max_window_bits = int(value) - else: - raise InvalidParameterValue(name, value) - - elif name == "client_max_window_bits": - if client_max_window_bits is not None: - raise DuplicateParameter(name) - if is_server and value is None: # only in handshake requests - client_max_window_bits = True - elif value in _MAX_WINDOW_BITS_VALUES: - client_max_window_bits = int(value) - else: - raise InvalidParameterValue(name, value) - - else: - raise InvalidParameterName(name) - - return ( - server_no_context_takeover, - client_no_context_takeover, - server_max_window_bits, - client_max_window_bits, - ) - - -class ClientPerMessageDeflateFactory(ClientExtensionFactory): - """ - Client-side extension factory for the Per-Message Deflate extension. - - Parameters behave as described in `section 7.1 of RFC 7692`_. - - .. _section 7.1 of RFC 7692: https://datatracker.ietf.org/doc/html/rfc7692#section-7.1 - - Set them to :obj:`True` to include them in the negotiation offer without a - value or to an integer value to include them with this value. - - Args: - server_no_context_takeover: Prevent server from using context takeover. - client_no_context_takeover: Prevent client from using context takeover. - server_max_window_bits: Maximum size of the server's LZ77 sliding window - in bits, between 8 and 15. - client_max_window_bits: Maximum size of the client's LZ77 sliding window - in bits, between 8 and 15, or :obj:`True` to indicate support without - setting a limit. - compress_settings: Additional keyword arguments for :func:`zlib.compressobj`, - excluding ``wbits``. - - """ - - name = ExtensionName("permessage-deflate") - - def __init__( - self, - server_no_context_takeover: bool = False, - client_no_context_takeover: bool = False, - server_max_window_bits: int | None = None, - client_max_window_bits: int | bool | None = True, - compress_settings: dict[str, Any] | None = None, - ) -> None: - """ - Configure the Per-Message Deflate extension factory. - - """ - if not (server_max_window_bits is None or 8 <= server_max_window_bits <= 15): - raise ValueError("server_max_window_bits must be between 8 and 15") - if not ( - client_max_window_bits is None - or client_max_window_bits is True - or 8 <= client_max_window_bits <= 15 - ): - raise ValueError("client_max_window_bits must be between 8 and 15") - if compress_settings is not None and "wbits" in compress_settings: - raise ValueError( - "compress_settings must not include wbits, " - "set client_max_window_bits instead" - ) - - self.server_no_context_takeover = server_no_context_takeover - self.client_no_context_takeover = client_no_context_takeover - self.server_max_window_bits = server_max_window_bits - self.client_max_window_bits = client_max_window_bits - self.compress_settings = compress_settings - - def get_request_params(self) -> list[ExtensionParameter]: - """ - Build request parameters. - - """ - return _build_parameters( - self.server_no_context_takeover, - self.client_no_context_takeover, - self.server_max_window_bits, - self.client_max_window_bits, - ) - - def process_response_params( - self, - params: Sequence[ExtensionParameter], - accepted_extensions: Sequence[Extension], - ) -> PerMessageDeflate: - """ - Process response parameters. - - Return an extension instance. - - """ - if any(other.name == self.name for other in accepted_extensions): - raise NegotiationError(f"received duplicate {self.name}") - - # Request parameters are available in instance variables. - - # Load response parameters in local variables. - ( - server_no_context_takeover, - client_no_context_takeover, - server_max_window_bits, - client_max_window_bits, - ) = _extract_parameters(params, is_server=False) - - # After comparing the request and the response, the final - # configuration must be available in the local variables. - - # server_no_context_takeover - # - # Req. Resp. Result - # ------ ------ -------------------------------------------------- - # False False False - # False True True - # True False Error! - # True True True - - if self.server_no_context_takeover: - if not server_no_context_takeover: - raise NegotiationError("expected server_no_context_takeover") - - # client_no_context_takeover - # - # Req. Resp. Result - # ------ ------ -------------------------------------------------- - # False False False - # False True True - # True False True - must change value - # True True True - - if self.client_no_context_takeover: - if not client_no_context_takeover: - client_no_context_takeover = True - - # server_max_window_bits - - # Req. Resp. Result - # ------ ------ -------------------------------------------------- - # None None None - # None 8≤M≤15 M - # 8≤N≤15 None Error! - # 8≤N≤15 8≤M≤N M - # 8≤N≤15 N self.server_max_window_bits: - raise NegotiationError("unsupported server_max_window_bits") - - # client_max_window_bits - - # Req. Resp. Result - # ------ ------ -------------------------------------------------- - # None None None - # None 8≤M≤15 Error! - # True None None - # True 8≤M≤15 M - # 8≤N≤15 None N - must change value - # 8≤N≤15 8≤M≤N M - # 8≤N≤15 N self.client_max_window_bits: - raise NegotiationError("unsupported client_max_window_bits") - - return PerMessageDeflate( - server_no_context_takeover, # remote_no_context_takeover - client_no_context_takeover, # local_no_context_takeover - server_max_window_bits or 15, # remote_max_window_bits - client_max_window_bits or 15, # local_max_window_bits - self.compress_settings, - ) - - -def enable_client_permessage_deflate( - extensions: Sequence[ClientExtensionFactory] | None, -) -> Sequence[ClientExtensionFactory]: - """ - Enable Per-Message Deflate with default settings in client extensions. - - If the extension is already present, perhaps with non-default settings, - the configuration isn't changed. - - """ - if extensions is None: - extensions = [] - if not any( - extension_factory.name == ClientPerMessageDeflateFactory.name - for extension_factory in extensions - ): - extensions = list(extensions) + [ - ClientPerMessageDeflateFactory( - compress_settings={"memLevel": 5}, - ) - ] - return extensions - - -class ServerPerMessageDeflateFactory(ServerExtensionFactory): - """ - Server-side extension factory for the Per-Message Deflate extension. - - Parameters behave as described in `section 7.1 of RFC 7692`_. - - .. _section 7.1 of RFC 7692: https://datatracker.ietf.org/doc/html/rfc7692#section-7.1 - - Set them to :obj:`True` to include them in the negotiation offer without a - value or to an integer value to include them with this value. - - Args: - server_no_context_takeover: Prevent server from using context takeover. - client_no_context_takeover: Prevent client from using context takeover. - server_max_window_bits: Maximum size of the server's LZ77 sliding window - in bits, between 8 and 15. - client_max_window_bits: Maximum size of the client's LZ77 sliding window - in bits, between 8 and 15. - compress_settings: Additional keyword arguments for :func:`zlib.compressobj`, - excluding ``wbits``. - require_client_max_window_bits: Do not enable compression at all if - client doesn't advertise support for ``client_max_window_bits``; - the default behavior is to enable compression without enforcing - ``client_max_window_bits``. - - """ - - name = ExtensionName("permessage-deflate") - - def __init__( - self, - server_no_context_takeover: bool = False, - client_no_context_takeover: bool = False, - server_max_window_bits: int | None = None, - client_max_window_bits: int | None = None, - compress_settings: dict[str, Any] | None = None, - require_client_max_window_bits: bool = False, - ) -> None: - """ - Configure the Per-Message Deflate extension factory. - - """ - if not (server_max_window_bits is None or 8 <= server_max_window_bits <= 15): - raise ValueError("server_max_window_bits must be between 8 and 15") - if not (client_max_window_bits is None or 8 <= client_max_window_bits <= 15): - raise ValueError("client_max_window_bits must be between 8 and 15") - if compress_settings is not None and "wbits" in compress_settings: - raise ValueError( - "compress_settings must not include wbits, " - "set server_max_window_bits instead" - ) - if client_max_window_bits is None and require_client_max_window_bits: - raise ValueError( - "require_client_max_window_bits is enabled, " - "but client_max_window_bits isn't configured" - ) - - self.server_no_context_takeover = server_no_context_takeover - self.client_no_context_takeover = client_no_context_takeover - self.server_max_window_bits = server_max_window_bits - self.client_max_window_bits = client_max_window_bits - self.compress_settings = compress_settings - self.require_client_max_window_bits = require_client_max_window_bits - - def process_request_params( - self, - params: Sequence[ExtensionParameter], - accepted_extensions: Sequence[Extension], - ) -> tuple[list[ExtensionParameter], PerMessageDeflate]: - """ - Process request parameters. - - Return response params and an extension instance. - - """ - if any(other.name == self.name for other in accepted_extensions): - raise NegotiationError(f"skipped duplicate {self.name}") - - # Load request parameters in local variables. - ( - server_no_context_takeover, - client_no_context_takeover, - server_max_window_bits, - client_max_window_bits, - ) = _extract_parameters(params, is_server=True) - - # Configuration parameters are available in instance variables. - - # After comparing the request and the configuration, the response must - # be available in the local variables. - - # server_no_context_takeover - # - # Config Req. Resp. - # ------ ------ -------------------------------------------------- - # False False False - # False True True - # True False True - must change value to True - # True True True - - if self.server_no_context_takeover: - if not server_no_context_takeover: - server_no_context_takeover = True - - # client_no_context_takeover - # - # Config Req. Resp. - # ------ ------ -------------------------------------------------- - # False False False - # False True True (or False) - # True False True - must change value to True - # True True True (or False) - - if self.client_no_context_takeover: - if not client_no_context_takeover: - client_no_context_takeover = True - - # server_max_window_bits - - # Config Req. Resp. - # ------ ------ -------------------------------------------------- - # None None None - # None 8≤M≤15 M - # 8≤N≤15 None N - must change value - # 8≤N≤15 8≤M≤N M - # 8≤N≤15 N self.server_max_window_bits: - server_max_window_bits = self.server_max_window_bits - - # client_max_window_bits - - # Config Req. Resp. - # ------ ------ -------------------------------------------------- - # None None None - # None True None - must change value - # None 8≤M≤15 M (or None) - # 8≤N≤15 None None or Error! - # 8≤N≤15 True N - must change value - # 8≤N≤15 8≤M≤N M (or None) - # 8≤N≤15 N Sequence[ServerExtensionFactory]: - """ - Enable Per-Message Deflate with default settings in server extensions. - - If the extension is already present, perhaps with non-default settings, - the configuration isn't changed. - - """ - if extensions is None: - extensions = [] - if not any( - ext_factory.name == ServerPerMessageDeflateFactory.name - for ext_factory in extensions - ): - extensions = list(extensions) + [ - ServerPerMessageDeflateFactory( - server_max_window_bits=12, - client_max_window_bits=12, - compress_settings={"memLevel": 5}, - ) - ] - return extensions diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/frames.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/frames.py deleted file mode 100644 index a63bdc3..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/frames.py +++ /dev/null @@ -1,429 +0,0 @@ -from __future__ import annotations - -import dataclasses -import enum -import io -import os -import secrets -import struct -from typing import Callable, Generator, Sequence - -from .exceptions import PayloadTooBig, ProtocolError - - -try: - from .speedups import apply_mask -except ImportError: - from .utils import apply_mask - - -__all__ = [ - "Opcode", - "OP_CONT", - "OP_TEXT", - "OP_BINARY", - "OP_CLOSE", - "OP_PING", - "OP_PONG", - "DATA_OPCODES", - "CTRL_OPCODES", - "Frame", - "Close", -] - - -class Opcode(enum.IntEnum): - """Opcode values for WebSocket frames.""" - - CONT, TEXT, BINARY = 0x00, 0x01, 0x02 - CLOSE, PING, PONG = 0x08, 0x09, 0x0A - - -OP_CONT = Opcode.CONT -OP_TEXT = Opcode.TEXT -OP_BINARY = Opcode.BINARY -OP_CLOSE = Opcode.CLOSE -OP_PING = Opcode.PING -OP_PONG = Opcode.PONG - -DATA_OPCODES = OP_CONT, OP_TEXT, OP_BINARY -CTRL_OPCODES = OP_CLOSE, OP_PING, OP_PONG - - -class CloseCode(enum.IntEnum): - """Close code values for WebSocket close frames.""" - - NORMAL_CLOSURE = 1000 - GOING_AWAY = 1001 - PROTOCOL_ERROR = 1002 - UNSUPPORTED_DATA = 1003 - # 1004 is reserved - NO_STATUS_RCVD = 1005 - ABNORMAL_CLOSURE = 1006 - INVALID_DATA = 1007 - POLICY_VIOLATION = 1008 - MESSAGE_TOO_BIG = 1009 - MANDATORY_EXTENSION = 1010 - INTERNAL_ERROR = 1011 - SERVICE_RESTART = 1012 - TRY_AGAIN_LATER = 1013 - BAD_GATEWAY = 1014 - TLS_HANDSHAKE = 1015 - - -# See https://www.iana.org/assignments/websocket/websocket.xhtml -CLOSE_CODE_EXPLANATIONS: dict[int, str] = { - CloseCode.NORMAL_CLOSURE: "OK", - CloseCode.GOING_AWAY: "going away", - CloseCode.PROTOCOL_ERROR: "protocol error", - CloseCode.UNSUPPORTED_DATA: "unsupported data", - CloseCode.NO_STATUS_RCVD: "no status received [internal]", - CloseCode.ABNORMAL_CLOSURE: "abnormal closure [internal]", - CloseCode.INVALID_DATA: "invalid frame payload data", - CloseCode.POLICY_VIOLATION: "policy violation", - CloseCode.MESSAGE_TOO_BIG: "message too big", - CloseCode.MANDATORY_EXTENSION: "mandatory extension", - CloseCode.INTERNAL_ERROR: "internal error", - CloseCode.SERVICE_RESTART: "service restart", - CloseCode.TRY_AGAIN_LATER: "try again later", - CloseCode.BAD_GATEWAY: "bad gateway", - CloseCode.TLS_HANDSHAKE: "TLS handshake failure [internal]", -} - - -# Close code that are allowed in a close frame. -# Using a set optimizes `code in EXTERNAL_CLOSE_CODES`. -EXTERNAL_CLOSE_CODES = { - CloseCode.NORMAL_CLOSURE, - CloseCode.GOING_AWAY, - CloseCode.PROTOCOL_ERROR, - CloseCode.UNSUPPORTED_DATA, - CloseCode.INVALID_DATA, - CloseCode.POLICY_VIOLATION, - CloseCode.MESSAGE_TOO_BIG, - CloseCode.MANDATORY_EXTENSION, - CloseCode.INTERNAL_ERROR, - CloseCode.SERVICE_RESTART, - CloseCode.TRY_AGAIN_LATER, - CloseCode.BAD_GATEWAY, -} - - -OK_CLOSE_CODES = { - CloseCode.NORMAL_CLOSURE, - CloseCode.GOING_AWAY, - CloseCode.NO_STATUS_RCVD, -} - - -BytesLike = bytes, bytearray, memoryview - - -@dataclasses.dataclass -class Frame: - """ - WebSocket frame. - - Attributes: - opcode: Opcode. - data: Payload data. - fin: FIN bit. - rsv1: RSV1 bit. - rsv2: RSV2 bit. - rsv3: RSV3 bit. - - Only these fields are needed. The MASK bit, payload length and masking-key - are handled on the fly when parsing and serializing frames. - - """ - - opcode: Opcode - data: bytes - fin: bool = True - rsv1: bool = False - rsv2: bool = False - rsv3: bool = False - - # Configure if you want to see more in logs. Should be a multiple of 3. - MAX_LOG_SIZE = int(os.environ.get("WEBSOCKETS_MAX_LOG_SIZE", "75")) - - def __str__(self) -> str: - """ - Return a human-readable representation of a frame. - - """ - coding = None - length = f"{len(self.data)} byte{'' if len(self.data) == 1 else 's'}" - non_final = "" if self.fin else "continued" - - if self.opcode is OP_TEXT: - # Decoding only the beginning and the end is needlessly hard. - # Decode the entire payload then elide later if necessary. - data = repr(self.data.decode()) - elif self.opcode is OP_BINARY: - # We'll show at most the first 16 bytes and the last 8 bytes. - # Encode just what we need, plus two dummy bytes to elide later. - binary = self.data - if len(binary) > self.MAX_LOG_SIZE // 3: - cut = (self.MAX_LOG_SIZE // 3 - 1) // 3 # by default cut = 8 - binary = b"".join([binary[: 2 * cut], b"\x00\x00", binary[-cut:]]) - data = " ".join(f"{byte:02x}" for byte in binary) - elif self.opcode is OP_CLOSE: - data = str(Close.parse(self.data)) - elif self.data: - # We don't know if a Continuation frame contains text or binary. - # Ping and Pong frames could contain UTF-8. - # Attempt to decode as UTF-8 and display it as text; fallback to - # binary. If self.data is a memoryview, it has no decode() method, - # which raises AttributeError. - try: - data = repr(self.data.decode()) - coding = "text" - except (UnicodeDecodeError, AttributeError): - binary = self.data - if len(binary) > self.MAX_LOG_SIZE // 3: - cut = (self.MAX_LOG_SIZE // 3 - 1) // 3 # by default cut = 8 - binary = b"".join([binary[: 2 * cut], b"\x00\x00", binary[-cut:]]) - data = " ".join(f"{byte:02x}" for byte in binary) - coding = "binary" - else: - data = "''" - - if len(data) > self.MAX_LOG_SIZE: - cut = self.MAX_LOG_SIZE // 3 - 1 # by default cut = 24 - data = data[: 2 * cut] + "..." + data[-cut:] - - metadata = ", ".join(filter(None, [coding, length, non_final])) - - return f"{self.opcode.name} {data} [{metadata}]" - - @classmethod - def parse( - cls, - read_exact: Callable[[int], Generator[None, None, bytes]], - *, - mask: bool, - max_size: int | None = None, - extensions: Sequence[extensions.Extension] | None = None, - ) -> Generator[None, None, Frame]: - """ - Parse a WebSocket frame. - - This is a generator-based coroutine. - - Args: - read_exact: Generator-based coroutine that reads the requested - bytes or raises an exception if there isn't enough data. - mask: Whether the frame should be masked i.e. whether the read - happens on the server side. - max_size: Maximum payload size in bytes. - extensions: List of extensions, applied in reverse order. - - Raises: - EOFError: If the connection is closed without a full WebSocket frame. - UnicodeDecodeError: If the frame contains invalid UTF-8. - PayloadTooBig: If the frame's payload size exceeds ``max_size``. - ProtocolError: If the frame contains incorrect values. - - """ - # Read the header. - data = yield from read_exact(2) - head1, head2 = struct.unpack("!BB", data) - - # While not Pythonic, this is marginally faster than calling bool(). - fin = True if head1 & 0b10000000 else False - rsv1 = True if head1 & 0b01000000 else False - rsv2 = True if head1 & 0b00100000 else False - rsv3 = True if head1 & 0b00010000 else False - - try: - opcode = Opcode(head1 & 0b00001111) - except ValueError as exc: - raise ProtocolError("invalid opcode") from exc - - if (True if head2 & 0b10000000 else False) != mask: - raise ProtocolError("incorrect masking") - - length = head2 & 0b01111111 - if length == 126: - data = yield from read_exact(2) - (length,) = struct.unpack("!H", data) - elif length == 127: - data = yield from read_exact(8) - (length,) = struct.unpack("!Q", data) - if max_size is not None and length > max_size: - raise PayloadTooBig(f"over size limit ({length} > {max_size} bytes)") - if mask: - mask_bytes = yield from read_exact(4) - - # Read the data. - data = yield from read_exact(length) - if mask: - data = apply_mask(data, mask_bytes) - - frame = cls(opcode, data, fin, rsv1, rsv2, rsv3) - - if extensions is None: - extensions = [] - for extension in reversed(extensions): - frame = extension.decode(frame, max_size=max_size) - - frame.check() - - return frame - - def serialize( - self, - *, - mask: bool, - extensions: Sequence[extensions.Extension] | None = None, - ) -> bytes: - """ - Serialize a WebSocket frame. - - Args: - mask: Whether the frame should be masked i.e. whether the write - happens on the client side. - extensions: List of extensions, applied in order. - - Raises: - ProtocolError: If the frame contains incorrect values. - - """ - self.check() - - if extensions is None: - extensions = [] - for extension in extensions: - self = extension.encode(self) - - output = io.BytesIO() - - # Prepare the header. - head1 = ( - (0b10000000 if self.fin else 0) - | (0b01000000 if self.rsv1 else 0) - | (0b00100000 if self.rsv2 else 0) - | (0b00010000 if self.rsv3 else 0) - | self.opcode - ) - - head2 = 0b10000000 if mask else 0 - - length = len(self.data) - if length < 126: - output.write(struct.pack("!BB", head1, head2 | length)) - elif length < 65536: - output.write(struct.pack("!BBH", head1, head2 | 126, length)) - else: - output.write(struct.pack("!BBQ", head1, head2 | 127, length)) - - if mask: - mask_bytes = secrets.token_bytes(4) - output.write(mask_bytes) - - # Prepare the data. - if mask: - data = apply_mask(self.data, mask_bytes) - else: - data = self.data - output.write(data) - - return output.getvalue() - - def check(self) -> None: - """ - Check that reserved bits and opcode have acceptable values. - - Raises: - ProtocolError: If a reserved bit or the opcode is invalid. - - """ - if self.rsv1 or self.rsv2 or self.rsv3: - raise ProtocolError("reserved bits must be 0") - - if self.opcode in CTRL_OPCODES: - if len(self.data) > 125: - raise ProtocolError("control frame too long") - if not self.fin: - raise ProtocolError("fragmented control frame") - - -@dataclasses.dataclass -class Close: - """ - Code and reason for WebSocket close frames. - - Attributes: - code: Close code. - reason: Close reason. - - """ - - code: int - reason: str - - def __str__(self) -> str: - """ - Return a human-readable representation of a close code and reason. - - """ - if 3000 <= self.code < 4000: - explanation = "registered" - elif 4000 <= self.code < 5000: - explanation = "private use" - else: - explanation = CLOSE_CODE_EXPLANATIONS.get(self.code, "unknown") - result = f"{self.code} ({explanation})" - - if self.reason: - result = f"{result} {self.reason}" - - return result - - @classmethod - def parse(cls, data: bytes) -> Close: - """ - Parse the payload of a close frame. - - Args: - data: Payload of the close frame. - - Raises: - ProtocolError: If data is ill-formed. - UnicodeDecodeError: If the reason isn't valid UTF-8. - - """ - if len(data) >= 2: - (code,) = struct.unpack("!H", data[:2]) - reason = data[2:].decode() - close = cls(code, reason) - close.check() - return close - elif len(data) == 0: - return cls(CloseCode.NO_STATUS_RCVD, "") - else: - raise ProtocolError("close frame too short") - - def serialize(self) -> bytes: - """ - Serialize the payload of a close frame. - - """ - self.check() - return struct.pack("!H", self.code) + self.reason.encode() - - def check(self) -> None: - """ - Check that the close code has a valid value for a close frame. - - Raises: - ProtocolError: If the close code is invalid. - - """ - if not (self.code in EXTERNAL_CLOSE_CODES or 3000 <= self.code < 5000): - raise ProtocolError("invalid status code") - - -# At the bottom to break import cycles created by type annotations. -from . import extensions # noqa: E402 diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/headers.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/headers.py deleted file mode 100644 index 9103018..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/headers.py +++ /dev/null @@ -1,579 +0,0 @@ -from __future__ import annotations - -import base64 -import binascii -import ipaddress -import re -from typing import Callable, Sequence, TypeVar, cast - -from .exceptions import InvalidHeaderFormat, InvalidHeaderValue -from .typing import ( - ConnectionOption, - ExtensionHeader, - ExtensionName, - ExtensionParameter, - Subprotocol, - UpgradeProtocol, -) - - -__all__ = [ - "build_host", - "parse_connection", - "parse_upgrade", - "parse_extension", - "build_extension", - "parse_subprotocol", - "build_subprotocol", - "validate_subprotocols", - "build_www_authenticate_basic", - "parse_authorization_basic", - "build_authorization_basic", -] - - -T = TypeVar("T") - - -def build_host(host: str, port: int, secure: bool) -> str: - """ - Build a ``Host`` header. - - """ - # https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2 - # IPv6 addresses must be enclosed in brackets. - try: - address = ipaddress.ip_address(host) - except ValueError: - # host is a hostname - pass - else: - # host is an IP address - if address.version == 6: - host = f"[{host}]" - - if port != (443 if secure else 80): - host = f"{host}:{port}" - - return host - - -# To avoid a dependency on a parsing library, we implement manually the ABNF -# described in https://datatracker.ietf.org/doc/html/rfc6455#section-9.1 and -# https://datatracker.ietf.org/doc/html/rfc7230#appendix-B. - - -def peek_ahead(header: str, pos: int) -> str | None: - """ - Return the next character from ``header`` at the given position. - - Return :obj:`None` at the end of ``header``. - - We never need to peek more than one character ahead. - - """ - return None if pos == len(header) else header[pos] - - -_OWS_re = re.compile(r"[\t ]*") - - -def parse_OWS(header: str, pos: int) -> int: - """ - Parse optional whitespace from ``header`` at the given position. - - Return the new position. - - The whitespace itself isn't returned because it isn't significant. - - """ - # There's always a match, possibly empty, whose content doesn't matter. - match = _OWS_re.match(header, pos) - assert match is not None - return match.end() - - -_token_re = re.compile(r"[-!#$%&\'*+.^_`|~0-9a-zA-Z]+") - - -def parse_token(header: str, pos: int, header_name: str) -> tuple[str, int]: - """ - Parse a token from ``header`` at the given position. - - Return the token value and the new position. - - Raises: - InvalidHeaderFormat: On invalid inputs. - - """ - match = _token_re.match(header, pos) - if match is None: - raise InvalidHeaderFormat(header_name, "expected token", header, pos) - return match.group(), match.end() - - -_quoted_string_re = re.compile( - r'"(?:[\x09\x20-\x21\x23-\x5b\x5d-\x7e]|\\[\x09\x20-\x7e\x80-\xff])*"' -) - - -_unquote_re = re.compile(r"\\([\x09\x20-\x7e\x80-\xff])") - - -def parse_quoted_string(header: str, pos: int, header_name: str) -> tuple[str, int]: - """ - Parse a quoted string from ``header`` at the given position. - - Return the unquoted value and the new position. - - Raises: - InvalidHeaderFormat: On invalid inputs. - - """ - match = _quoted_string_re.match(header, pos) - if match is None: - raise InvalidHeaderFormat(header_name, "expected quoted string", header, pos) - return _unquote_re.sub(r"\1", match.group()[1:-1]), match.end() - - -_quotable_re = re.compile(r"[\x09\x20-\x7e\x80-\xff]*") - - -_quote_re = re.compile(r"([\x22\x5c])") - - -def build_quoted_string(value: str) -> str: - """ - Format ``value`` as a quoted string. - - This is the reverse of :func:`parse_quoted_string`. - - """ - match = _quotable_re.fullmatch(value) - if match is None: - raise ValueError("invalid characters for quoted-string encoding") - return '"' + _quote_re.sub(r"\\\1", value) + '"' - - -def parse_list( - parse_item: Callable[[str, int, str], tuple[T, int]], - header: str, - pos: int, - header_name: str, -) -> list[T]: - """ - Parse a comma-separated list from ``header`` at the given position. - - This is appropriate for parsing values with the following grammar: - - 1#item - - ``parse_item`` parses one item. - - ``header`` is assumed not to start or end with whitespace. - - (This function is designed for parsing an entire header value and - :func:`~websockets.http.read_headers` strips whitespace from values.) - - Return a list of items. - - Raises: - InvalidHeaderFormat: On invalid inputs. - - """ - # Per https://datatracker.ietf.org/doc/html/rfc7230#section-7, "a recipient - # MUST parse and ignore a reasonable number of empty list elements"; - # hence while loops that remove extra delimiters. - - # Remove extra delimiters before the first item. - while peek_ahead(header, pos) == ",": - pos = parse_OWS(header, pos + 1) - - items = [] - while True: - # Loop invariant: a item starts at pos in header. - item, pos = parse_item(header, pos, header_name) - items.append(item) - pos = parse_OWS(header, pos) - - # We may have reached the end of the header. - if pos == len(header): - break - - # There must be a delimiter after each element except the last one. - if peek_ahead(header, pos) == ",": - pos = parse_OWS(header, pos + 1) - else: - raise InvalidHeaderFormat(header_name, "expected comma", header, pos) - - # Remove extra delimiters before the next item. - while peek_ahead(header, pos) == ",": - pos = parse_OWS(header, pos + 1) - - # We may have reached the end of the header. - if pos == len(header): - break - - # Since we only advance in the header by one character with peek_ahead() - # or with the end position of a regex match, we can't overshoot the end. - assert pos == len(header) - - return items - - -def parse_connection_option( - header: str, pos: int, header_name: str -) -> tuple[ConnectionOption, int]: - """ - Parse a Connection option from ``header`` at the given position. - - Return the protocol value and the new position. - - Raises: - InvalidHeaderFormat: On invalid inputs. - - """ - item, pos = parse_token(header, pos, header_name) - return cast(ConnectionOption, item), pos - - -def parse_connection(header: str) -> list[ConnectionOption]: - """ - Parse a ``Connection`` header. - - Return a list of HTTP connection options. - - Args - header: value of the ``Connection`` header. - - Raises: - InvalidHeaderFormat: On invalid inputs. - - """ - return parse_list(parse_connection_option, header, 0, "Connection") - - -_protocol_re = re.compile( - r"[-!#$%&\'*+.^_`|~0-9a-zA-Z]+(?:/[-!#$%&\'*+.^_`|~0-9a-zA-Z]+)?" -) - - -def parse_upgrade_protocol( - header: str, pos: int, header_name: str -) -> tuple[UpgradeProtocol, int]: - """ - Parse an Upgrade protocol from ``header`` at the given position. - - Return the protocol value and the new position. - - Raises: - InvalidHeaderFormat: On invalid inputs. - - """ - match = _protocol_re.match(header, pos) - if match is None: - raise InvalidHeaderFormat(header_name, "expected protocol", header, pos) - return cast(UpgradeProtocol, match.group()), match.end() - - -def parse_upgrade(header: str) -> list[UpgradeProtocol]: - """ - Parse an ``Upgrade`` header. - - Return a list of HTTP protocols. - - Args: - header: Value of the ``Upgrade`` header. - - Raises: - InvalidHeaderFormat: On invalid inputs. - - """ - return parse_list(parse_upgrade_protocol, header, 0, "Upgrade") - - -def parse_extension_item_param( - header: str, pos: int, header_name: str -) -> tuple[ExtensionParameter, int]: - """ - Parse a single extension parameter from ``header`` at the given position. - - Return a ``(name, value)`` pair and the new position. - - Raises: - InvalidHeaderFormat: On invalid inputs. - - """ - # Extract parameter name. - name, pos = parse_token(header, pos, header_name) - pos = parse_OWS(header, pos) - # Extract parameter value, if there is one. - value: str | None = None - if peek_ahead(header, pos) == "=": - pos = parse_OWS(header, pos + 1) - if peek_ahead(header, pos) == '"': - pos_before = pos # for proper error reporting below - value, pos = parse_quoted_string(header, pos, header_name) - # https://datatracker.ietf.org/doc/html/rfc6455#section-9.1 says: - # the value after quoted-string unescaping MUST conform to - # the 'token' ABNF. - if _token_re.fullmatch(value) is None: - raise InvalidHeaderFormat( - header_name, "invalid quoted header content", header, pos_before - ) - else: - value, pos = parse_token(header, pos, header_name) - pos = parse_OWS(header, pos) - - return (name, value), pos - - -def parse_extension_item( - header: str, pos: int, header_name: str -) -> tuple[ExtensionHeader, int]: - """ - Parse an extension definition from ``header`` at the given position. - - Return an ``(extension name, parameters)`` pair, where ``parameters`` is a - list of ``(name, value)`` pairs, and the new position. - - Raises: - InvalidHeaderFormat: On invalid inputs. - - """ - # Extract extension name. - name, pos = parse_token(header, pos, header_name) - pos = parse_OWS(header, pos) - # Extract all parameters. - parameters = [] - while peek_ahead(header, pos) == ";": - pos = parse_OWS(header, pos + 1) - parameter, pos = parse_extension_item_param(header, pos, header_name) - parameters.append(parameter) - return (cast(ExtensionName, name), parameters), pos - - -def parse_extension(header: str) -> list[ExtensionHeader]: - """ - Parse a ``Sec-WebSocket-Extensions`` header. - - Return a list of WebSocket extensions and their parameters in this format:: - - [ - ( - 'extension name', - [ - ('parameter name', 'parameter value'), - .... - ] - ), - ... - ] - - Parameter values are :obj:`None` when no value is provided. - - Raises: - InvalidHeaderFormat: On invalid inputs. - - """ - return parse_list(parse_extension_item, header, 0, "Sec-WebSocket-Extensions") - - -parse_extension_list = parse_extension # alias for backwards compatibility - - -def build_extension_item( - name: ExtensionName, parameters: list[ExtensionParameter] -) -> str: - """ - Build an extension definition. - - This is the reverse of :func:`parse_extension_item`. - - """ - return "; ".join( - [cast(str, name)] - + [ - # Quoted strings aren't necessary because values are always tokens. - name if value is None else f"{name}={value}" - for name, value in parameters - ] - ) - - -def build_extension(extensions: Sequence[ExtensionHeader]) -> str: - """ - Build a ``Sec-WebSocket-Extensions`` header. - - This is the reverse of :func:`parse_extension`. - - """ - return ", ".join( - build_extension_item(name, parameters) for name, parameters in extensions - ) - - -build_extension_list = build_extension # alias for backwards compatibility - - -def parse_subprotocol_item( - header: str, pos: int, header_name: str -) -> tuple[Subprotocol, int]: - """ - Parse a subprotocol from ``header`` at the given position. - - Return the subprotocol value and the new position. - - Raises: - InvalidHeaderFormat: On invalid inputs. - - """ - item, pos = parse_token(header, pos, header_name) - return cast(Subprotocol, item), pos - - -def parse_subprotocol(header: str) -> list[Subprotocol]: - """ - Parse a ``Sec-WebSocket-Protocol`` header. - - Return a list of WebSocket subprotocols. - - Raises: - InvalidHeaderFormat: On invalid inputs. - - """ - return parse_list(parse_subprotocol_item, header, 0, "Sec-WebSocket-Protocol") - - -parse_subprotocol_list = parse_subprotocol # alias for backwards compatibility - - -def build_subprotocol(subprotocols: Sequence[Subprotocol]) -> str: - """ - Build a ``Sec-WebSocket-Protocol`` header. - - This is the reverse of :func:`parse_subprotocol`. - - """ - return ", ".join(subprotocols) - - -build_subprotocol_list = build_subprotocol # alias for backwards compatibility - - -def validate_subprotocols(subprotocols: Sequence[Subprotocol]) -> None: - """ - Validate that ``subprotocols`` is suitable for :func:`build_subprotocol`. - - """ - if not isinstance(subprotocols, Sequence): - raise TypeError("subprotocols must be a list") - if isinstance(subprotocols, str): - raise TypeError("subprotocols must be a list, not a str") - for subprotocol in subprotocols: - if not _token_re.fullmatch(subprotocol): - raise ValueError(f"invalid subprotocol: {subprotocol}") - - -def build_www_authenticate_basic(realm: str) -> str: - """ - Build a ``WWW-Authenticate`` header for HTTP Basic Auth. - - Args: - realm: Identifier of the protection space. - - """ - # https://datatracker.ietf.org/doc/html/rfc7617#section-2 - realm = build_quoted_string(realm) - charset = build_quoted_string("UTF-8") - return f"Basic realm={realm}, charset={charset}" - - -_token68_re = re.compile(r"[A-Za-z0-9-._~+/]+=*") - - -def parse_token68(header: str, pos: int, header_name: str) -> tuple[str, int]: - """ - Parse a token68 from ``header`` at the given position. - - Return the token value and the new position. - - Raises: - InvalidHeaderFormat: On invalid inputs. - - """ - match = _token68_re.match(header, pos) - if match is None: - raise InvalidHeaderFormat(header_name, "expected token68", header, pos) - return match.group(), match.end() - - -def parse_end(header: str, pos: int, header_name: str) -> None: - """ - Check that parsing reached the end of header. - - """ - if pos < len(header): - raise InvalidHeaderFormat(header_name, "trailing data", header, pos) - - -def parse_authorization_basic(header: str) -> tuple[str, str]: - """ - Parse an ``Authorization`` header for HTTP Basic Auth. - - Return a ``(username, password)`` tuple. - - Args: - header: Value of the ``Authorization`` header. - - Raises: - InvalidHeaderFormat: On invalid inputs. - InvalidHeaderValue: On unsupported inputs. - - """ - # https://datatracker.ietf.org/doc/html/rfc7235#section-2.1 - # https://datatracker.ietf.org/doc/html/rfc7617#section-2 - scheme, pos = parse_token(header, 0, "Authorization") - if scheme.lower() != "basic": - raise InvalidHeaderValue( - "Authorization", - f"unsupported scheme: {scheme}", - ) - if peek_ahead(header, pos) != " ": - raise InvalidHeaderFormat( - "Authorization", "expected space after scheme", header, pos - ) - pos += 1 - basic_credentials, pos = parse_token68(header, pos, "Authorization") - parse_end(header, pos, "Authorization") - - try: - user_pass = base64.b64decode(basic_credentials.encode()).decode() - except binascii.Error: - raise InvalidHeaderValue( - "Authorization", - "expected base64-encoded credentials", - ) from None - try: - username, password = user_pass.split(":", 1) - except ValueError: - raise InvalidHeaderValue( - "Authorization", - "expected username:password credentials", - ) from None - - return username, password - - -def build_authorization_basic(username: str, password: str) -> str: - """ - Build an ``Authorization`` header for HTTP Basic Auth. - - This is the reverse of :func:`parse_authorization_basic`. - - """ - # https://datatracker.ietf.org/doc/html/rfc7617#section-2 - assert ":" not in username - user_pass = f"{username}:{password}" - basic_credentials = base64.b64encode(user_pass.encode()).decode() - return "Basic " + basic_credentials diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/http.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/http.py deleted file mode 100644 index 0ff5598..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/http.py +++ /dev/null @@ -1,15 +0,0 @@ -from __future__ import annotations - -import warnings - -from .datastructures import Headers, MultipleValuesError # noqa: F401 -from .legacy.http import read_request, read_response # noqa: F401 - - -warnings.warn( # deprecated in 9.0 - 2021-09-01 - "Headers and MultipleValuesError were moved " - "from websockets.http to websockets.datastructures" - "and read_request and read_response were moved " - "from websockets.http to websockets.legacy.http", - DeprecationWarning, -) diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/http11.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/http11.py deleted file mode 100644 index 47cef7a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/http11.py +++ /dev/null @@ -1,385 +0,0 @@ -from __future__ import annotations - -import dataclasses -import os -import re -import sys -import warnings -from typing import Callable, Generator - -from .datastructures import Headers -from .exceptions import SecurityError -from .version import version as websockets_version - - -__all__ = ["SERVER", "USER_AGENT", "Request", "Response"] - - -PYTHON_VERSION = "{}.{}".format(*sys.version_info) - -# User-Agent header for HTTP requests. -USER_AGENT = os.environ.get( - "WEBSOCKETS_USER_AGENT", - f"Python/{PYTHON_VERSION} websockets/{websockets_version}", -) - -# Server header for HTTP responses. -SERVER = os.environ.get( - "WEBSOCKETS_SERVER", - f"Python/{PYTHON_VERSION} websockets/{websockets_version}", -) - -# Maximum total size of headers is around 128 * 8 KiB = 1 MiB. -MAX_NUM_HEADERS = int(os.environ.get("WEBSOCKETS_MAX_NUM_HEADERS", "128")) - -# Limit request line and header lines. 8KiB is the most common default -# configuration of popular HTTP servers. -MAX_LINE_LENGTH = int(os.environ.get("WEBSOCKETS_MAX_LINE_LENGTH", "8192")) - -# Support for HTTP response bodies is intended to read an error message -# returned by a server. It isn't designed to perform large file transfers. -MAX_BODY_SIZE = int(os.environ.get("WEBSOCKETS_MAX_BODY_SIZE", "1_048_576")) # 1 MiB - - -def d(value: bytes) -> str: - """ - Decode a bytestring for interpolating into an error message. - - """ - return value.decode(errors="backslashreplace") - - -# See https://datatracker.ietf.org/doc/html/rfc7230#appendix-B. - -# Regex for validating header names. - -_token_re = re.compile(rb"[-!#$%&\'*+.^_`|~0-9a-zA-Z]+") - -# Regex for validating header values. - -# We don't attempt to support obsolete line folding. - -# Include HTAB (\x09), SP (\x20), VCHAR (\x21-\x7e), obs-text (\x80-\xff). - -# The ABNF is complicated because it attempts to express that optional -# whitespace is ignored. We strip whitespace and don't revalidate that. - -# See also https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4189 - -_value_re = re.compile(rb"[\x09\x20-\x7e\x80-\xff]*") - - -@dataclasses.dataclass -class Request: - """ - WebSocket handshake request. - - Attributes: - path: Request path, including optional query. - headers: Request headers. - """ - - path: str - headers: Headers - # body isn't useful is the context of this library. - - _exception: Exception | None = None - - @property - def exception(self) -> Exception | None: # pragma: no cover - warnings.warn( # deprecated in 10.3 - 2022-04-17 - "Request.exception is deprecated; " - "use ServerProtocol.handshake_exc instead", - DeprecationWarning, - ) - return self._exception - - @classmethod - def parse( - cls, - read_line: Callable[[int], Generator[None, None, bytes]], - ) -> Generator[None, None, Request]: - """ - Parse a WebSocket handshake request. - - This is a generator-based coroutine. - - The request path isn't URL-decoded or validated in any way. - - The request path and headers are expected to contain only ASCII - characters. Other characters are represented with surrogate escapes. - - :meth:`parse` doesn't attempt to read the request body because - WebSocket handshake requests don't have one. If the request contains a - body, it may be read from the data stream after :meth:`parse` returns. - - Args: - read_line: Generator-based coroutine that reads a LF-terminated - line or raises an exception if there isn't enough data - - Raises: - EOFError: If the connection is closed without a full HTTP request. - SecurityError: If the request exceeds a security limit. - ValueError: If the request isn't well formatted. - - """ - # https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.1 - - # Parsing is simple because fixed values are expected for method and - # version and because path isn't checked. Since WebSocket software tends - # to implement HTTP/1.1 strictly, there's little need for lenient parsing. - - try: - request_line = yield from parse_line(read_line) - except EOFError as exc: - raise EOFError("connection closed while reading HTTP request line") from exc - - try: - method, raw_path, protocol = request_line.split(b" ", 2) - except ValueError: # not enough values to unpack (expected 3, got 1-2) - raise ValueError(f"invalid HTTP request line: {d(request_line)}") from None - if protocol != b"HTTP/1.1": - raise ValueError( - f"unsupported protocol; expected HTTP/1.1: {d(request_line)}" - ) - if method != b"GET": - raise ValueError(f"unsupported HTTP method; expected GET; got {d(method)}") - path = raw_path.decode("ascii", "surrogateescape") - - headers = yield from parse_headers(read_line) - - # https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3 - - if "Transfer-Encoding" in headers: - raise NotImplementedError("transfer codings aren't supported") - - if "Content-Length" in headers: - raise ValueError("unsupported request body") - - return cls(path, headers) - - def serialize(self) -> bytes: - """ - Serialize a WebSocket handshake request. - - """ - # Since the request line and headers only contain ASCII characters, - # we can keep this simple. - request = f"GET {self.path} HTTP/1.1\r\n".encode() - request += self.headers.serialize() - return request - - -@dataclasses.dataclass -class Response: - """ - WebSocket handshake response. - - Attributes: - status_code: Response code. - reason_phrase: Response reason. - headers: Response headers. - body: Response body, if any. - - """ - - status_code: int - reason_phrase: str - headers: Headers - body: bytes | None = None - - _exception: Exception | None = None - - @property - def exception(self) -> Exception | None: # pragma: no cover - warnings.warn( # deprecated in 10.3 - 2022-04-17 - "Response.exception is deprecated; " - "use ClientProtocol.handshake_exc instead", - DeprecationWarning, - ) - return self._exception - - @classmethod - def parse( - cls, - read_line: Callable[[int], Generator[None, None, bytes]], - read_exact: Callable[[int], Generator[None, None, bytes]], - read_to_eof: Callable[[int], Generator[None, None, bytes]], - ) -> Generator[None, None, Response]: - """ - Parse a WebSocket handshake response. - - This is a generator-based coroutine. - - The reason phrase and headers are expected to contain only ASCII - characters. Other characters are represented with surrogate escapes. - - Args: - read_line: Generator-based coroutine that reads a LF-terminated - line or raises an exception if there isn't enough data. - read_exact: Generator-based coroutine that reads the requested - bytes or raises an exception if there isn't enough data. - read_to_eof: Generator-based coroutine that reads until the end - of the stream. - - Raises: - EOFError: If the connection is closed without a full HTTP response. - SecurityError: If the response exceeds a security limit. - LookupError: If the response isn't well formatted. - ValueError: If the response isn't well formatted. - - """ - # https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2 - - try: - status_line = yield from parse_line(read_line) - except EOFError as exc: - raise EOFError("connection closed while reading HTTP status line") from exc - - try: - protocol, raw_status_code, raw_reason = status_line.split(b" ", 2) - except ValueError: # not enough values to unpack (expected 3, got 1-2) - raise ValueError(f"invalid HTTP status line: {d(status_line)}") from None - if protocol != b"HTTP/1.1": - raise ValueError( - f"unsupported protocol; expected HTTP/1.1: {d(status_line)}" - ) - try: - status_code = int(raw_status_code) - except ValueError: # invalid literal for int() with base 10 - raise ValueError( - f"invalid status code; expected integer; got {d(raw_status_code)}" - ) from None - if not 100 <= status_code < 600: - raise ValueError( - f"invalid status code; expected 100–599; got {d(raw_status_code)}" - ) - if not _value_re.fullmatch(raw_reason): - raise ValueError(f"invalid HTTP reason phrase: {d(raw_reason)}") - reason = raw_reason.decode("ascii", "surrogateescape") - - headers = yield from parse_headers(read_line) - - # https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3 - - if "Transfer-Encoding" in headers: - raise NotImplementedError("transfer codings aren't supported") - - # Since websockets only does GET requests (no HEAD, no CONNECT), all - # responses except 1xx, 204, and 304 include a message body. - if 100 <= status_code < 200 or status_code == 204 or status_code == 304: - body = None - else: - content_length: int | None - try: - # MultipleValuesError is sufficiently unlikely that we don't - # attempt to handle it. Instead we document that its parent - # class, LookupError, may be raised. - raw_content_length = headers["Content-Length"] - except KeyError: - content_length = None - else: - content_length = int(raw_content_length) - - if content_length is None: - try: - body = yield from read_to_eof(MAX_BODY_SIZE) - except RuntimeError: - raise SecurityError(f"body too large: over {MAX_BODY_SIZE} bytes") - elif content_length > MAX_BODY_SIZE: - raise SecurityError(f"body too large: {content_length} bytes") - else: - body = yield from read_exact(content_length) - - return cls(status_code, reason, headers, body) - - def serialize(self) -> bytes: - """ - Serialize a WebSocket handshake response. - - """ - # Since the status line and headers only contain ASCII characters, - # we can keep this simple. - response = f"HTTP/1.1 {self.status_code} {self.reason_phrase}\r\n".encode() - response += self.headers.serialize() - if self.body is not None: - response += self.body - return response - - -def parse_headers( - read_line: Callable[[int], Generator[None, None, bytes]], -) -> Generator[None, None, Headers]: - """ - Parse HTTP headers. - - Non-ASCII characters are represented with surrogate escapes. - - Args: - read_line: Generator-based coroutine that reads a LF-terminated line - or raises an exception if there isn't enough data. - - Raises: - EOFError: If the connection is closed without complete headers. - SecurityError: If the request exceeds a security limit. - ValueError: If the request isn't well formatted. - - """ - # https://datatracker.ietf.org/doc/html/rfc7230#section-3.2 - - # We don't attempt to support obsolete line folding. - - headers = Headers() - for _ in range(MAX_NUM_HEADERS + 1): - try: - line = yield from parse_line(read_line) - except EOFError as exc: - raise EOFError("connection closed while reading HTTP headers") from exc - if line == b"": - break - - try: - raw_name, raw_value = line.split(b":", 1) - except ValueError: # not enough values to unpack (expected 2, got 1) - raise ValueError(f"invalid HTTP header line: {d(line)}") from None - if not _token_re.fullmatch(raw_name): - raise ValueError(f"invalid HTTP header name: {d(raw_name)}") - raw_value = raw_value.strip(b" \t") - if not _value_re.fullmatch(raw_value): - raise ValueError(f"invalid HTTP header value: {d(raw_value)}") - - name = raw_name.decode("ascii") # guaranteed to be ASCII at this point - value = raw_value.decode("ascii", "surrogateescape") - headers[name] = value - - else: - raise SecurityError("too many HTTP headers") - - return headers - - -def parse_line( - read_line: Callable[[int], Generator[None, None, bytes]], -) -> Generator[None, None, bytes]: - """ - Parse a single line. - - CRLF is stripped from the return value. - - Args: - read_line: Generator-based coroutine that reads a LF-terminated line - or raises an exception if there isn't enough data. - - Raises: - EOFError: If the connection is closed without a CRLF. - SecurityError: If the response exceeds a security limit. - - """ - try: - line = yield from read_line(MAX_LINE_LENGTH) - except RuntimeError: - raise SecurityError("line too long") - # Not mandatory but safe - https://datatracker.ietf.org/doc/html/rfc7230#section-3.5 - if not line.endswith(b"\r\n"): - raise EOFError("line without CRLF") - return line[:-2] diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/imports.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/imports.py deleted file mode 100644 index bb80e4e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/imports.py +++ /dev/null @@ -1,99 +0,0 @@ -from __future__ import annotations - -import warnings -from typing import Any, Iterable - - -__all__ = ["lazy_import"] - - -def import_name(name: str, source: str, namespace: dict[str, Any]) -> Any: - """ - Import ``name`` from ``source`` in ``namespace``. - - There are two use cases: - - - ``name`` is an object defined in ``source``; - - ``name`` is a submodule of ``source``. - - Neither :func:`__import__` nor :func:`~importlib.import_module` does - exactly this. :func:`__import__` is closer to the intended behavior. - - """ - level = 0 - while source[level] == ".": - level += 1 - assert level < len(source), "importing from parent isn't supported" - module = __import__(source[level:], namespace, None, [name], level) - return getattr(module, name) - - -def lazy_import( - namespace: dict[str, Any], - aliases: dict[str, str] | None = None, - deprecated_aliases: dict[str, str] | None = None, -) -> None: - """ - Provide lazy, module-level imports. - - Typical use:: - - __getattr__, __dir__ = lazy_import( - globals(), - aliases={ - "": "", - ... - }, - deprecated_aliases={ - ..., - } - ) - - This function defines ``__getattr__`` and ``__dir__`` per :pep:`562`. - - """ - if aliases is None: - aliases = {} - if deprecated_aliases is None: - deprecated_aliases = {} - - namespace_set = set(namespace) - aliases_set = set(aliases) - deprecated_aliases_set = set(deprecated_aliases) - - assert not namespace_set & aliases_set, "namespace conflict" - assert not namespace_set & deprecated_aliases_set, "namespace conflict" - assert not aliases_set & deprecated_aliases_set, "namespace conflict" - - package = namespace["__name__"] - - def __getattr__(name: str) -> Any: - assert aliases is not None # mypy cannot figure this out - try: - source = aliases[name] - except KeyError: - pass - else: - return import_name(name, source, namespace) - - assert deprecated_aliases is not None # mypy cannot figure this out - try: - source = deprecated_aliases[name] - except KeyError: - pass - else: - warnings.warn( - f"{package}.{name} is deprecated", - DeprecationWarning, - stacklevel=2, - ) - return import_name(name, source, namespace) - - raise AttributeError(f"module {package!r} has no attribute {name!r}") - - namespace["__getattr__"] = __getattr__ - - def __dir__() -> Iterable[str]: - return sorted(namespace_set | aliases_set | deprecated_aliases_set) - - namespace["__dir__"] = __dir__ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 6e57bd1..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/auth.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/auth.cpython-38.pyc deleted file mode 100644 index d667104..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/auth.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/client.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/client.cpython-38.pyc deleted file mode 100644 index 49bd5fb..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/client.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/exceptions.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/exceptions.cpython-38.pyc deleted file mode 100644 index a11bce0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/exceptions.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/framing.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/framing.cpython-38.pyc deleted file mode 100644 index 291e2a3..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/framing.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/handshake.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/handshake.cpython-38.pyc deleted file mode 100644 index 5604c7e..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/handshake.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/http.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/http.cpython-38.pyc deleted file mode 100644 index a4f58b0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/http.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/protocol.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/protocol.cpython-38.pyc deleted file mode 100644 index 4746af5..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/protocol.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/server.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/server.cpython-38.pyc deleted file mode 100644 index 250dc9e..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/__pycache__/server.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/auth.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/auth.py deleted file mode 100644 index 4d030e5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/auth.py +++ /dev/null @@ -1,190 +0,0 @@ -from __future__ import annotations - -import functools -import hmac -import http -from typing import Any, Awaitable, Callable, Iterable, Tuple, cast - -from ..datastructures import Headers -from ..exceptions import InvalidHeader -from ..headers import build_www_authenticate_basic, parse_authorization_basic -from .server import HTTPResponse, WebSocketServerProtocol - - -__all__ = ["BasicAuthWebSocketServerProtocol", "basic_auth_protocol_factory"] - -# Change to tuple[str, str] when dropping Python < 3.9. -Credentials = Tuple[str, str] - - -def is_credentials(value: Any) -> bool: - try: - username, password = value - except (TypeError, ValueError): - return False - else: - return isinstance(username, str) and isinstance(password, str) - - -class BasicAuthWebSocketServerProtocol(WebSocketServerProtocol): - """ - WebSocket server protocol that enforces HTTP Basic Auth. - - """ - - realm: str = "" - """ - Scope of protection. - - If provided, it should contain only ASCII characters because the - encoding of non-ASCII characters is undefined. - """ - - username: str | None = None - """Username of the authenticated user.""" - - def __init__( - self, - *args: Any, - realm: str | None = None, - check_credentials: Callable[[str, str], Awaitable[bool]] | None = None, - **kwargs: Any, - ) -> None: - if realm is not None: - self.realm = realm # shadow class attribute - self._check_credentials = check_credentials - super().__init__(*args, **kwargs) - - async def check_credentials(self, username: str, password: str) -> bool: - """ - Check whether credentials are authorized. - - This coroutine may be overridden in a subclass, for example to - authenticate against a database or an external service. - - Args: - username: HTTP Basic Auth username. - password: HTTP Basic Auth password. - - Returns: - :obj:`True` if the handshake should continue; - :obj:`False` if it should fail with an HTTP 401 error. - - """ - if self._check_credentials is not None: - return await self._check_credentials(username, password) - - return False - - async def process_request( - self, - path: str, - request_headers: Headers, - ) -> HTTPResponse | None: - """ - Check HTTP Basic Auth and return an HTTP 401 response if needed. - - """ - try: - authorization = request_headers["Authorization"] - except KeyError: - return ( - http.HTTPStatus.UNAUTHORIZED, - [("WWW-Authenticate", build_www_authenticate_basic(self.realm))], - b"Missing credentials\n", - ) - - try: - username, password = parse_authorization_basic(authorization) - except InvalidHeader: - return ( - http.HTTPStatus.UNAUTHORIZED, - [("WWW-Authenticate", build_www_authenticate_basic(self.realm))], - b"Unsupported credentials\n", - ) - - if not await self.check_credentials(username, password): - return ( - http.HTTPStatus.UNAUTHORIZED, - [("WWW-Authenticate", build_www_authenticate_basic(self.realm))], - b"Invalid credentials\n", - ) - - self.username = username - - return await super().process_request(path, request_headers) - - -def basic_auth_protocol_factory( - realm: str | None = None, - credentials: Credentials | Iterable[Credentials] | None = None, - check_credentials: Callable[[str, str], Awaitable[bool]] | None = None, - create_protocol: Callable[..., BasicAuthWebSocketServerProtocol] | None = None, -) -> Callable[..., BasicAuthWebSocketServerProtocol]: - """ - Protocol factory that enforces HTTP Basic Auth. - - :func:`basic_auth_protocol_factory` is designed to integrate with - :func:`~websockets.legacy.server.serve` like this:: - - serve( - ..., - create_protocol=basic_auth_protocol_factory( - realm="my dev server", - credentials=("hello", "iloveyou"), - ) - ) - - Args: - realm: Scope of protection. It should contain only ASCII characters - because the encoding of non-ASCII characters is undefined. - Refer to section 2.2 of :rfc:`7235` for details. - credentials: Hard coded authorized credentials. It can be a - ``(username, password)`` pair or a list of such pairs. - check_credentials: Coroutine that verifies credentials. - It receives ``username`` and ``password`` arguments - and returns a :class:`bool`. One of ``credentials`` or - ``check_credentials`` must be provided but not both. - create_protocol: Factory that creates the protocol. By default, this - is :class:`BasicAuthWebSocketServerProtocol`. It can be replaced - by a subclass. - Raises: - TypeError: If the ``credentials`` or ``check_credentials`` argument is - wrong. - - """ - if (credentials is None) == (check_credentials is None): - raise TypeError("provide either credentials or check_credentials") - - if credentials is not None: - if is_credentials(credentials): - credentials_list = [cast(Credentials, credentials)] - elif isinstance(credentials, Iterable): - credentials_list = list(cast(Iterable[Credentials], credentials)) - if not all(is_credentials(item) for item in credentials_list): - raise TypeError(f"invalid credentials argument: {credentials}") - else: - raise TypeError(f"invalid credentials argument: {credentials}") - - credentials_dict = dict(credentials_list) - - async def check_credentials(username: str, password: str) -> bool: - try: - expected_password = credentials_dict[username] - except KeyError: - return False - return hmac.compare_digest(expected_password, password) - - if create_protocol is None: - create_protocol = BasicAuthWebSocketServerProtocol - - # Help mypy and avoid this error: "type[BasicAuthWebSocketServerProtocol] | - # Callable[..., BasicAuthWebSocketServerProtocol]" not callable [misc] - create_protocol = cast( - Callable[..., BasicAuthWebSocketServerProtocol], create_protocol - ) - return functools.partial( - create_protocol, - realm=realm, - check_credentials=check_credentials, - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/client.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/client.py deleted file mode 100644 index ec4c2ff..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/client.py +++ /dev/null @@ -1,707 +0,0 @@ -from __future__ import annotations - -import asyncio -import functools -import logging -import os -import random -import urllib.parse -import warnings -from types import TracebackType -from typing import ( - Any, - AsyncIterator, - Callable, - Generator, - Sequence, - cast, -) - -from ..asyncio.compatibility import asyncio_timeout -from ..datastructures import Headers, HeadersLike -from ..exceptions import ( - InvalidHeader, - InvalidHeaderValue, - NegotiationError, - SecurityError, -) -from ..extensions import ClientExtensionFactory, Extension -from ..extensions.permessage_deflate import enable_client_permessage_deflate -from ..headers import ( - build_authorization_basic, - build_extension, - build_host, - build_subprotocol, - parse_extension, - parse_subprotocol, - validate_subprotocols, -) -from ..http11 import USER_AGENT -from ..typing import ExtensionHeader, LoggerLike, Origin, Subprotocol -from ..uri import WebSocketURI, parse_uri -from .exceptions import InvalidMessage, InvalidStatusCode, RedirectHandshake -from .handshake import build_request, check_response -from .http import read_response -from .protocol import WebSocketCommonProtocol - - -__all__ = ["connect", "unix_connect", "WebSocketClientProtocol"] - - -class WebSocketClientProtocol(WebSocketCommonProtocol): - """ - WebSocket client connection. - - :class:`WebSocketClientProtocol` provides :meth:`recv` and :meth:`send` - coroutines for receiving and sending messages. - - It supports asynchronous iteration to receive messages:: - - async for message in websocket: - await process(message) - - The iterator exits normally when the connection is closed with close code - 1000 (OK) or 1001 (going away) or without a close code. It raises - a :exc:`~websockets.exceptions.ConnectionClosedError` when the connection - is closed with any other code. - - See :func:`connect` for the documentation of ``logger``, ``origin``, - ``extensions``, ``subprotocols``, ``extra_headers``, and - ``user_agent_header``. - - See :class:`~websockets.legacy.protocol.WebSocketCommonProtocol` for the - documentation of ``ping_interval``, ``ping_timeout``, ``close_timeout``, - ``max_size``, ``max_queue``, ``read_limit``, and ``write_limit``. - - """ - - is_client = True - side = "client" - - def __init__( - self, - *, - logger: LoggerLike | None = None, - origin: Origin | None = None, - extensions: Sequence[ClientExtensionFactory] | None = None, - subprotocols: Sequence[Subprotocol] | None = None, - extra_headers: HeadersLike | None = None, - user_agent_header: str | None = USER_AGENT, - **kwargs: Any, - ) -> None: - if logger is None: - logger = logging.getLogger("websockets.client") - super().__init__(logger=logger, **kwargs) - self.origin = origin - self.available_extensions = extensions - self.available_subprotocols = subprotocols - self.extra_headers = extra_headers - self.user_agent_header = user_agent_header - - def write_http_request(self, path: str, headers: Headers) -> None: - """ - Write request line and headers to the HTTP request. - - """ - self.path = path - self.request_headers = headers - - if self.debug: - self.logger.debug("> GET %s HTTP/1.1", path) - for key, value in headers.raw_items(): - self.logger.debug("> %s: %s", key, value) - - # Since the path and headers only contain ASCII characters, - # we can keep this simple. - request = f"GET {path} HTTP/1.1\r\n" - request += str(headers) - - self.transport.write(request.encode()) - - async def read_http_response(self) -> tuple[int, Headers]: - """ - Read status line and headers from the HTTP response. - - If the response contains a body, it may be read from ``self.reader`` - after this coroutine returns. - - Raises: - InvalidMessage: If the HTTP message is malformed or isn't an - HTTP/1.1 GET response. - - """ - try: - status_code, reason, headers = await read_response(self.reader) - except Exception as exc: - raise InvalidMessage("did not receive a valid HTTP response") from exc - - if self.debug: - self.logger.debug("< HTTP/1.1 %d %s", status_code, reason) - for key, value in headers.raw_items(): - self.logger.debug("< %s: %s", key, value) - - self.response_headers = headers - - return status_code, self.response_headers - - @staticmethod - def process_extensions( - headers: Headers, - available_extensions: Sequence[ClientExtensionFactory] | None, - ) -> list[Extension]: - """ - Handle the Sec-WebSocket-Extensions HTTP response header. - - Check that each extension is supported, as well as its parameters. - - Return the list of accepted extensions. - - Raise :exc:`~websockets.exceptions.InvalidHandshake` to abort the - connection. - - :rfc:`6455` leaves the rules up to the specification of each - :extension. - - To provide this level of flexibility, for each extension accepted by - the server, we check for a match with each extension available in the - client configuration. If no match is found, an exception is raised. - - If several variants of the same extension are accepted by the server, - it may be configured several times, which won't make sense in general. - Extensions must implement their own requirements. For this purpose, - the list of previously accepted extensions is provided. - - Other requirements, for example related to mandatory extensions or the - order of extensions, may be implemented by overriding this method. - - """ - accepted_extensions: list[Extension] = [] - - header_values = headers.get_all("Sec-WebSocket-Extensions") - - if header_values: - if available_extensions is None: - raise NegotiationError("no extensions supported") - - parsed_header_values: list[ExtensionHeader] = sum( - [parse_extension(header_value) for header_value in header_values], [] - ) - - for name, response_params in parsed_header_values: - for extension_factory in available_extensions: - # Skip non-matching extensions based on their name. - if extension_factory.name != name: - continue - - # Skip non-matching extensions based on their params. - try: - extension = extension_factory.process_response_params( - response_params, accepted_extensions - ) - except NegotiationError: - continue - - # Add matching extension to the final list. - accepted_extensions.append(extension) - - # Break out of the loop once we have a match. - break - - # If we didn't break from the loop, no extension in our list - # matched what the server sent. Fail the connection. - else: - raise NegotiationError( - f"Unsupported extension: " - f"name = {name}, params = {response_params}" - ) - - return accepted_extensions - - @staticmethod - def process_subprotocol( - headers: Headers, available_subprotocols: Sequence[Subprotocol] | None - ) -> Subprotocol | None: - """ - Handle the Sec-WebSocket-Protocol HTTP response header. - - Check that it contains exactly one supported subprotocol. - - Return the selected subprotocol. - - """ - subprotocol: Subprotocol | None = None - - header_values = headers.get_all("Sec-WebSocket-Protocol") - - if header_values: - if available_subprotocols is None: - raise NegotiationError("no subprotocols supported") - - parsed_header_values: Sequence[Subprotocol] = sum( - [parse_subprotocol(header_value) for header_value in header_values], [] - ) - - if len(parsed_header_values) > 1: - raise InvalidHeaderValue( - "Sec-WebSocket-Protocol", - f"multiple values: {', '.join(parsed_header_values)}", - ) - - subprotocol = parsed_header_values[0] - - if subprotocol not in available_subprotocols: - raise NegotiationError(f"unsupported subprotocol: {subprotocol}") - - return subprotocol - - async def handshake( - self, - wsuri: WebSocketURI, - origin: Origin | None = None, - available_extensions: Sequence[ClientExtensionFactory] | None = None, - available_subprotocols: Sequence[Subprotocol] | None = None, - extra_headers: HeadersLike | None = None, - ) -> None: - """ - Perform the client side of the opening handshake. - - Args: - wsuri: URI of the WebSocket server. - origin: Value of the ``Origin`` header. - extensions: List of supported extensions, in order in which they - should be negotiated and run. - subprotocols: List of supported subprotocols, in order of decreasing - preference. - extra_headers: Arbitrary HTTP headers to add to the handshake request. - - Raises: - InvalidHandshake: If the handshake fails. - - """ - request_headers = Headers() - - request_headers["Host"] = build_host(wsuri.host, wsuri.port, wsuri.secure) - - if wsuri.user_info: - request_headers["Authorization"] = build_authorization_basic( - *wsuri.user_info - ) - - if origin is not None: - request_headers["Origin"] = origin - - key = build_request(request_headers) - - if available_extensions is not None: - extensions_header = build_extension( - [ - (extension_factory.name, extension_factory.get_request_params()) - for extension_factory in available_extensions - ] - ) - request_headers["Sec-WebSocket-Extensions"] = extensions_header - - if available_subprotocols is not None: - protocol_header = build_subprotocol(available_subprotocols) - request_headers["Sec-WebSocket-Protocol"] = protocol_header - - if self.extra_headers is not None: - request_headers.update(self.extra_headers) - - if self.user_agent_header: - request_headers.setdefault("User-Agent", self.user_agent_header) - - self.write_http_request(wsuri.resource_name, request_headers) - - status_code, response_headers = await self.read_http_response() - if status_code in (301, 302, 303, 307, 308): - if "Location" not in response_headers: - raise InvalidHeader("Location") - raise RedirectHandshake(response_headers["Location"]) - elif status_code != 101: - raise InvalidStatusCode(status_code, response_headers) - - check_response(response_headers, key) - - self.extensions = self.process_extensions( - response_headers, available_extensions - ) - - self.subprotocol = self.process_subprotocol( - response_headers, available_subprotocols - ) - - self.connection_open() - - -class Connect: - """ - Connect to the WebSocket server at ``uri``. - - Awaiting :func:`connect` yields a :class:`WebSocketClientProtocol` which - can then be used to send and receive messages. - - :func:`connect` can be used as a asynchronous context manager:: - - async with connect(...) as websocket: - ... - - The connection is closed automatically when exiting the context. - - :func:`connect` can be used as an infinite asynchronous iterator to - reconnect automatically on errors:: - - async for websocket in connect(...): - try: - ... - except websockets.ConnectionClosed: - continue - - The connection is closed automatically after each iteration of the loop. - - If an error occurs while establishing the connection, :func:`connect` - retries with exponential backoff. The backoff delay starts at three - seconds and increases up to one minute. - - If an error occurs in the body of the loop, you can handle the exception - and :func:`connect` will reconnect with the next iteration; or you can - let the exception bubble up and break out of the loop. This lets you - decide which errors trigger a reconnection and which errors are fatal. - - Args: - uri: URI of the WebSocket server. - create_protocol: Factory for the :class:`asyncio.Protocol` managing - the connection. It defaults to :class:`WebSocketClientProtocol`. - Set it to a wrapper or a subclass to customize connection handling. - logger: Logger for this client. - It defaults to ``logging.getLogger("websockets.client")``. - See the :doc:`logging guide <../../topics/logging>` for details. - compression: The "permessage-deflate" extension is enabled by default. - Set ``compression`` to :obj:`None` to disable it. See the - :doc:`compression guide <../../topics/compression>` for details. - origin: Value of the ``Origin`` header, for servers that require it. - extensions: List of supported extensions, in order in which they - should be negotiated and run. - subprotocols: List of supported subprotocols, in order of decreasing - preference. - extra_headers: Arbitrary HTTP headers to add to the handshake request. - user_agent_header: Value of the ``User-Agent`` request header. - It defaults to ``"Python/x.y.z websockets/X.Y"``. - Setting it to :obj:`None` removes the header. - open_timeout: Timeout for opening the connection in seconds. - :obj:`None` disables the timeout. - - See :class:`~websockets.legacy.protocol.WebSocketCommonProtocol` for the - documentation of ``ping_interval``, ``ping_timeout``, ``close_timeout``, - ``max_size``, ``max_queue``, ``read_limit``, and ``write_limit``. - - Any other keyword arguments are passed the event loop's - :meth:`~asyncio.loop.create_connection` method. - - For example: - - * You can set ``ssl`` to a :class:`~ssl.SSLContext` to enforce TLS - settings. When connecting to a ``wss://`` URI, if ``ssl`` isn't - provided, a TLS context is created - with :func:`~ssl.create_default_context`. - - * You can set ``host`` and ``port`` to connect to a different host and - port from those found in ``uri``. This only changes the destination of - the TCP connection. The host name from ``uri`` is still used in the TLS - handshake for secure connections and in the ``Host`` header. - - Raises: - InvalidURI: If ``uri`` isn't a valid WebSocket URI. - OSError: If the TCP connection fails. - InvalidHandshake: If the opening handshake fails. - ~asyncio.TimeoutError: If the opening handshake times out. - - """ - - MAX_REDIRECTS_ALLOWED = int(os.environ.get("WEBSOCKETS_MAX_REDIRECTS", "10")) - - def __init__( - self, - uri: str, - *, - create_protocol: Callable[..., WebSocketClientProtocol] | None = None, - logger: LoggerLike | None = None, - compression: str | None = "deflate", - origin: Origin | None = None, - extensions: Sequence[ClientExtensionFactory] | None = None, - subprotocols: Sequence[Subprotocol] | None = None, - extra_headers: HeadersLike | None = None, - user_agent_header: str | None = USER_AGENT, - open_timeout: float | None = 10, - ping_interval: float | None = 20, - ping_timeout: float | None = 20, - close_timeout: float | None = None, - max_size: int | None = 2**20, - max_queue: int | None = 2**5, - read_limit: int = 2**16, - write_limit: int = 2**16, - **kwargs: Any, - ) -> None: - # Backwards compatibility: close_timeout used to be called timeout. - timeout: float | None = kwargs.pop("timeout", None) - if timeout is None: - timeout = 10 - else: - warnings.warn("rename timeout to close_timeout", DeprecationWarning) - # If both are specified, timeout is ignored. - if close_timeout is None: - close_timeout = timeout - - # Backwards compatibility: create_protocol used to be called klass. - klass: type[WebSocketClientProtocol] | None = kwargs.pop("klass", None) - if klass is None: - klass = WebSocketClientProtocol - else: - warnings.warn("rename klass to create_protocol", DeprecationWarning) - # If both are specified, klass is ignored. - if create_protocol is None: - create_protocol = klass - - # Backwards compatibility: recv() used to return None on closed connections - legacy_recv: bool = kwargs.pop("legacy_recv", False) - - # Backwards compatibility: the loop parameter used to be supported. - _loop: asyncio.AbstractEventLoop | None = kwargs.pop("loop", None) - if _loop is None: - loop = asyncio.get_event_loop() - else: - loop = _loop - warnings.warn("remove loop argument", DeprecationWarning) - - wsuri = parse_uri(uri) - if wsuri.secure: - kwargs.setdefault("ssl", True) - elif kwargs.get("ssl") is not None: - raise ValueError( - "connect() received a ssl argument for a ws:// URI, " - "use a wss:// URI to enable TLS" - ) - - if compression == "deflate": - extensions = enable_client_permessage_deflate(extensions) - elif compression is not None: - raise ValueError(f"unsupported compression: {compression}") - - if subprotocols is not None: - validate_subprotocols(subprotocols) - - # Help mypy and avoid this error: "type[WebSocketClientProtocol] | - # Callable[..., WebSocketClientProtocol]" not callable [misc] - create_protocol = cast(Callable[..., WebSocketClientProtocol], create_protocol) - factory = functools.partial( - create_protocol, - logger=logger, - origin=origin, - extensions=extensions, - subprotocols=subprotocols, - extra_headers=extra_headers, - user_agent_header=user_agent_header, - ping_interval=ping_interval, - ping_timeout=ping_timeout, - close_timeout=close_timeout, - max_size=max_size, - max_queue=max_queue, - read_limit=read_limit, - write_limit=write_limit, - host=wsuri.host, - port=wsuri.port, - secure=wsuri.secure, - legacy_recv=legacy_recv, - loop=_loop, - ) - - if kwargs.pop("unix", False): - path: str | None = kwargs.pop("path", None) - create_connection = functools.partial( - loop.create_unix_connection, factory, path, **kwargs - ) - else: - host: str | None - port: int | None - if kwargs.get("sock") is None: - host, port = wsuri.host, wsuri.port - else: - # If sock is given, host and port shouldn't be specified. - host, port = None, None - if kwargs.get("ssl"): - kwargs.setdefault("server_hostname", wsuri.host) - # If host and port are given, override values from the URI. - host = kwargs.pop("host", host) - port = kwargs.pop("port", port) - create_connection = functools.partial( - loop.create_connection, factory, host, port, **kwargs - ) - - self.open_timeout = open_timeout - if logger is None: - logger = logging.getLogger("websockets.client") - self.logger = logger - - # This is a coroutine function. - self._create_connection = create_connection - self._uri = uri - self._wsuri = wsuri - - def handle_redirect(self, uri: str) -> None: - # Update the state of this instance to connect to a new URI. - old_uri = self._uri - old_wsuri = self._wsuri - new_uri = urllib.parse.urljoin(old_uri, uri) - new_wsuri = parse_uri(new_uri) - - # Forbid TLS downgrade. - if old_wsuri.secure and not new_wsuri.secure: - raise SecurityError("redirect from WSS to WS") - - same_origin = ( - old_wsuri.secure == new_wsuri.secure - and old_wsuri.host == new_wsuri.host - and old_wsuri.port == new_wsuri.port - ) - - # Rewrite secure, host, and port for cross-origin redirects. - # This preserves connection overrides with the host and port - # arguments if the redirect points to the same host and port. - if not same_origin: - factory = self._create_connection.args[0] - # Support TLS upgrade. - if not old_wsuri.secure and new_wsuri.secure: - factory.keywords["secure"] = True - self._create_connection.keywords.setdefault("ssl", True) - # Replace secure, host, and port arguments of the protocol factory. - factory = functools.partial( - factory.func, - *factory.args, - **dict(factory.keywords, host=new_wsuri.host, port=new_wsuri.port), - ) - # Replace secure, host, and port arguments of create_connection. - self._create_connection = functools.partial( - self._create_connection.func, - *(factory, new_wsuri.host, new_wsuri.port), - **self._create_connection.keywords, - ) - - # Set the new WebSocket URI. This suffices for same-origin redirects. - self._uri = new_uri - self._wsuri = new_wsuri - - # async for ... in connect(...): - - BACKOFF_INITIAL = float(os.environ.get("WEBSOCKETS_BACKOFF_INITIAL_DELAY", "5")) - BACKOFF_MIN = float(os.environ.get("WEBSOCKETS_BACKOFF_MIN_DELAY", "3.1")) - BACKOFF_MAX = float(os.environ.get("WEBSOCKETS_BACKOFF_MAX_DELAY", "90.0")) - BACKOFF_FACTOR = float(os.environ.get("WEBSOCKETS_BACKOFF_FACTOR", "1.618")) - - async def __aiter__(self) -> AsyncIterator[WebSocketClientProtocol]: - backoff_delay = self.BACKOFF_MIN / self.BACKOFF_FACTOR - while True: - try: - async with self as protocol: - yield protocol - except Exception: - # Add a random initial delay between 0 and 5 seconds. - # See 7.2.3. Recovering from Abnormal Closure in RFC 6455. - if backoff_delay == self.BACKOFF_MIN: - initial_delay = random.random() * self.BACKOFF_INITIAL - self.logger.info( - "! connect failed; reconnecting in %.1f seconds", - initial_delay, - exc_info=True, - ) - await asyncio.sleep(initial_delay) - else: - self.logger.info( - "! connect failed again; retrying in %d seconds", - int(backoff_delay), - exc_info=True, - ) - await asyncio.sleep(int(backoff_delay)) - # Increase delay with truncated exponential backoff. - backoff_delay = backoff_delay * self.BACKOFF_FACTOR - backoff_delay = min(backoff_delay, self.BACKOFF_MAX) - continue - else: - # Connection succeeded - reset backoff delay - backoff_delay = self.BACKOFF_MIN - - # async with connect(...) as ...: - - async def __aenter__(self) -> WebSocketClientProtocol: - return await self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - traceback: TracebackType | None, - ) -> None: - await self.protocol.close() - - # ... = await connect(...) - - def __await__(self) -> Generator[Any, None, WebSocketClientProtocol]: - # Create a suitable iterator by calling __await__ on a coroutine. - return self.__await_impl__().__await__() - - async def __await_impl__(self) -> WebSocketClientProtocol: - async with asyncio_timeout(self.open_timeout): - for _redirects in range(self.MAX_REDIRECTS_ALLOWED): - _transport, protocol = await self._create_connection() - try: - await protocol.handshake( - self._wsuri, - origin=protocol.origin, - available_extensions=protocol.available_extensions, - available_subprotocols=protocol.available_subprotocols, - extra_headers=protocol.extra_headers, - ) - except RedirectHandshake as exc: - protocol.fail_connection() - await protocol.wait_closed() - self.handle_redirect(exc.uri) - # Avoid leaking a connected socket when the handshake fails. - except (Exception, asyncio.CancelledError): - protocol.fail_connection() - await protocol.wait_closed() - raise - else: - self.protocol = protocol - return protocol - else: - raise SecurityError("too many redirects") - - # ... = yield from connect(...) - remove when dropping Python < 3.10 - - __iter__ = __await__ - - -connect = Connect - - -def unix_connect( - path: str | None = None, - uri: str = "ws://localhost/", - **kwargs: Any, -) -> Connect: - """ - Similar to :func:`connect`, but for connecting to a Unix socket. - - This function builds upon the event loop's - :meth:`~asyncio.loop.create_unix_connection` method. - - It is only available on Unix. - - It's mainly useful for debugging servers listening on Unix sockets. - - Args: - path: File system path to the Unix socket. - uri: URI of the WebSocket server; the host is used in the TLS - handshake for secure connections and in the ``Host`` header. - - """ - return connect(uri=uri, path=path, unix=True, **kwargs) diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/exceptions.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/exceptions.py deleted file mode 100644 index 9ca9b7a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/exceptions.py +++ /dev/null @@ -1,78 +0,0 @@ -import http - -from .. import datastructures -from ..exceptions import ( - InvalidHandshake, - ProtocolError as WebSocketProtocolError, # noqa: F401 -) -from ..typing import StatusLike - - -class InvalidMessage(InvalidHandshake): - """ - Raised when a handshake request or response is malformed. - - """ - - -class InvalidStatusCode(InvalidHandshake): - """ - Raised when a handshake response status code is invalid. - - """ - - def __init__(self, status_code: int, headers: datastructures.Headers) -> None: - self.status_code = status_code - self.headers = headers - - def __str__(self) -> str: - return f"server rejected WebSocket connection: HTTP {self.status_code}" - - -class AbortHandshake(InvalidHandshake): - """ - Raised to abort the handshake on purpose and return an HTTP response. - - This exception is an implementation detail. - - The public API is - :meth:`~websockets.legacy.server.WebSocketServerProtocol.process_request`. - - Attributes: - status (~http.HTTPStatus): HTTP status code. - headers (Headers): HTTP response headers. - body (bytes): HTTP response body. - """ - - def __init__( - self, - status: StatusLike, - headers: datastructures.HeadersLike, - body: bytes = b"", - ) -> None: - # If a user passes an int instead of a HTTPStatus, fix it automatically. - self.status = http.HTTPStatus(status) - self.headers = datastructures.Headers(headers) - self.body = body - - def __str__(self) -> str: - return ( - f"HTTP {self.status:d}, " - f"{len(self.headers)} headers, " - f"{len(self.body)} bytes" - ) - - -class RedirectHandshake(InvalidHandshake): - """ - Raised when a handshake gets redirected. - - This exception is an implementation detail. - - """ - - def __init__(self, uri: str) -> None: - self.uri = uri - - def __str__(self) -> str: - return f"redirect to {self.uri}" diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/framing.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/framing.py deleted file mode 100644 index 4c2f8c2..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/framing.py +++ /dev/null @@ -1,224 +0,0 @@ -from __future__ import annotations - -import struct -from typing import Any, Awaitable, Callable, NamedTuple, Sequence - -from .. import extensions, frames -from ..exceptions import PayloadTooBig, ProtocolError -from ..frames import BytesLike -from ..typing import Data - - -try: - from ..speedups import apply_mask -except ImportError: - from ..utils import apply_mask - - -class Frame(NamedTuple): - fin: bool - opcode: frames.Opcode - data: bytes - rsv1: bool = False - rsv2: bool = False - rsv3: bool = False - - @property - def new_frame(self) -> frames.Frame: - return frames.Frame( - self.opcode, - self.data, - self.fin, - self.rsv1, - self.rsv2, - self.rsv3, - ) - - def __str__(self) -> str: - return str(self.new_frame) - - def check(self) -> None: - return self.new_frame.check() - - @classmethod - async def read( - cls, - reader: Callable[[int], Awaitable[bytes]], - *, - mask: bool, - max_size: int | None = None, - extensions: Sequence[extensions.Extension] | None = None, - ) -> Frame: - """ - Read a WebSocket frame. - - Args: - reader: Coroutine that reads exactly the requested number of - bytes, unless the end of file is reached. - mask: Whether the frame should be masked i.e. whether the read - happens on the server side. - max_size: Maximum payload size in bytes. - extensions: List of extensions, applied in reverse order. - - Raises: - PayloadTooBig: If the frame exceeds ``max_size``. - ProtocolError: If the frame contains incorrect values. - - """ - - # Read the header. - data = await reader(2) - head1, head2 = struct.unpack("!BB", data) - - # While not Pythonic, this is marginally faster than calling bool(). - fin = True if head1 & 0b10000000 else False - rsv1 = True if head1 & 0b01000000 else False - rsv2 = True if head1 & 0b00100000 else False - rsv3 = True if head1 & 0b00010000 else False - - try: - opcode = frames.Opcode(head1 & 0b00001111) - except ValueError as exc: - raise ProtocolError("invalid opcode") from exc - - if (True if head2 & 0b10000000 else False) != mask: - raise ProtocolError("incorrect masking") - - length = head2 & 0b01111111 - if length == 126: - data = await reader(2) - (length,) = struct.unpack("!H", data) - elif length == 127: - data = await reader(8) - (length,) = struct.unpack("!Q", data) - if max_size is not None and length > max_size: - raise PayloadTooBig(f"over size limit ({length} > {max_size} bytes)") - if mask: - mask_bits = await reader(4) - - # Read the data. - data = await reader(length) - if mask: - data = apply_mask(data, mask_bits) - - new_frame = frames.Frame(opcode, data, fin, rsv1, rsv2, rsv3) - - if extensions is None: - extensions = [] - for extension in reversed(extensions): - new_frame = extension.decode(new_frame, max_size=max_size) - - new_frame.check() - - return cls( - new_frame.fin, - new_frame.opcode, - new_frame.data, - new_frame.rsv1, - new_frame.rsv2, - new_frame.rsv3, - ) - - def write( - self, - write: Callable[[bytes], Any], - *, - mask: bool, - extensions: Sequence[extensions.Extension] | None = None, - ) -> None: - """ - Write a WebSocket frame. - - Args: - frame: Frame to write. - write: Function that writes bytes. - mask: Whether the frame should be masked i.e. whether the write - happens on the client side. - extensions: List of extensions, applied in order. - - Raises: - ProtocolError: If the frame contains incorrect values. - - """ - # The frame is written in a single call to write in order to prevent - # TCP fragmentation. See #68 for details. This also makes it safe to - # send frames concurrently from multiple coroutines. - write(self.new_frame.serialize(mask=mask, extensions=extensions)) - - -def prepare_data(data: Data) -> tuple[int, bytes]: - """ - Convert a string or byte-like object to an opcode and a bytes-like object. - - This function is designed for data frames. - - If ``data`` is a :class:`str`, return ``OP_TEXT`` and a :class:`bytes` - object encoding ``data`` in UTF-8. - - If ``data`` is a bytes-like object, return ``OP_BINARY`` and a bytes-like - object. - - Raises: - TypeError: If ``data`` doesn't have a supported type. - - """ - if isinstance(data, str): - return frames.Opcode.TEXT, data.encode() - elif isinstance(data, BytesLike): - return frames.Opcode.BINARY, data - else: - raise TypeError("data must be str or bytes-like") - - -def prepare_ctrl(data: Data) -> bytes: - """ - Convert a string or byte-like object to bytes. - - This function is designed for ping and pong frames. - - If ``data`` is a :class:`str`, return a :class:`bytes` object encoding - ``data`` in UTF-8. - - If ``data`` is a bytes-like object, return a :class:`bytes` object. - - Raises: - TypeError: If ``data`` doesn't have a supported type. - - """ - if isinstance(data, str): - return data.encode() - elif isinstance(data, BytesLike): - return bytes(data) - else: - raise TypeError("data must be str or bytes-like") - - -# Backwards compatibility with previously documented public APIs -encode_data = prepare_ctrl - -# Backwards compatibility with previously documented public APIs -from ..frames import Close # noqa: E402 F401, I001 - - -def parse_close(data: bytes) -> tuple[int, str]: - """ - Parse the payload from a close frame. - - Returns: - Close code and reason. - - Raises: - ProtocolError: If data is ill-formed. - UnicodeDecodeError: If the reason isn't valid UTF-8. - - """ - close = Close.parse(data) - return close.code, close.reason - - -def serialize_close(code: int, reason: str) -> bytes: - """ - Serialize the payload for a close frame. - - """ - return Close(code, reason).serialize() diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/handshake.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/handshake.py deleted file mode 100644 index 6a7157c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/handshake.py +++ /dev/null @@ -1,158 +0,0 @@ -from __future__ import annotations - -import base64 -import binascii - -from ..datastructures import Headers, MultipleValuesError -from ..exceptions import InvalidHeader, InvalidHeaderValue, InvalidUpgrade -from ..headers import parse_connection, parse_upgrade -from ..typing import ConnectionOption, UpgradeProtocol -from ..utils import accept_key as accept, generate_key - - -__all__ = ["build_request", "check_request", "build_response", "check_response"] - - -def build_request(headers: Headers) -> str: - """ - Build a handshake request to send to the server. - - Update request headers passed in argument. - - Args: - headers: Handshake request headers. - - Returns: - ``key`` that must be passed to :func:`check_response`. - - """ - key = generate_key() - headers["Upgrade"] = "websocket" - headers["Connection"] = "Upgrade" - headers["Sec-WebSocket-Key"] = key - headers["Sec-WebSocket-Version"] = "13" - return key - - -def check_request(headers: Headers) -> str: - """ - Check a handshake request received from the client. - - This function doesn't verify that the request is an HTTP/1.1 or higher GET - request and doesn't perform ``Host`` and ``Origin`` checks. These controls - are usually performed earlier in the HTTP request handling code. They're - the responsibility of the caller. - - Args: - headers: Handshake request headers. - - Returns: - ``key`` that must be passed to :func:`build_response`. - - Raises: - InvalidHandshake: If the handshake request is invalid. - Then, the server must return a 400 Bad Request error. - - """ - connection: list[ConnectionOption] = sum( - [parse_connection(value) for value in headers.get_all("Connection")], [] - ) - - if not any(value.lower() == "upgrade" for value in connection): - raise InvalidUpgrade("Connection", ", ".join(connection)) - - upgrade: list[UpgradeProtocol] = sum( - [parse_upgrade(value) for value in headers.get_all("Upgrade")], [] - ) - - # For compatibility with non-strict implementations, ignore case when - # checking the Upgrade header. The RFC always uses "websocket", except - # in section 11.2. (IANA registration) where it uses "WebSocket". - if not (len(upgrade) == 1 and upgrade[0].lower() == "websocket"): - raise InvalidUpgrade("Upgrade", ", ".join(upgrade)) - - try: - s_w_key = headers["Sec-WebSocket-Key"] - except KeyError as exc: - raise InvalidHeader("Sec-WebSocket-Key") from exc - except MultipleValuesError as exc: - raise InvalidHeader("Sec-WebSocket-Key", "multiple values") from exc - - try: - raw_key = base64.b64decode(s_w_key.encode(), validate=True) - except binascii.Error as exc: - raise InvalidHeaderValue("Sec-WebSocket-Key", s_w_key) from exc - if len(raw_key) != 16: - raise InvalidHeaderValue("Sec-WebSocket-Key", s_w_key) - - try: - s_w_version = headers["Sec-WebSocket-Version"] - except KeyError as exc: - raise InvalidHeader("Sec-WebSocket-Version") from exc - except MultipleValuesError as exc: - raise InvalidHeader("Sec-WebSocket-Version", "multiple values") from exc - - if s_w_version != "13": - raise InvalidHeaderValue("Sec-WebSocket-Version", s_w_version) - - return s_w_key - - -def build_response(headers: Headers, key: str) -> None: - """ - Build a handshake response to send to the client. - - Update response headers passed in argument. - - Args: - headers: Handshake response headers. - key: Returned by :func:`check_request`. - - """ - headers["Upgrade"] = "websocket" - headers["Connection"] = "Upgrade" - headers["Sec-WebSocket-Accept"] = accept(key) - - -def check_response(headers: Headers, key: str) -> None: - """ - Check a handshake response received from the server. - - This function doesn't verify that the response is an HTTP/1.1 or higher - response with a 101 status code. These controls are the responsibility of - the caller. - - Args: - headers: Handshake response headers. - key: Returned by :func:`build_request`. - - Raises: - InvalidHandshake: If the handshake response is invalid. - - """ - connection: list[ConnectionOption] = sum( - [parse_connection(value) for value in headers.get_all("Connection")], [] - ) - - if not any(value.lower() == "upgrade" for value in connection): - raise InvalidUpgrade("Connection", " ".join(connection)) - - upgrade: list[UpgradeProtocol] = sum( - [parse_upgrade(value) for value in headers.get_all("Upgrade")], [] - ) - - # For compatibility with non-strict implementations, ignore case when - # checking the Upgrade header. The RFC always uses "websocket", except - # in section 11.2. (IANA registration) where it uses "WebSocket". - if not (len(upgrade) == 1 and upgrade[0].lower() == "websocket"): - raise InvalidUpgrade("Upgrade", ", ".join(upgrade)) - - try: - s_w_accept = headers["Sec-WebSocket-Accept"] - except KeyError as exc: - raise InvalidHeader("Sec-WebSocket-Accept") from exc - except MultipleValuesError as exc: - raise InvalidHeader("Sec-WebSocket-Accept", "multiple values") from exc - - if s_w_accept != accept(key): - raise InvalidHeaderValue("Sec-WebSocket-Accept", s_w_accept) diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/http.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/http.py deleted file mode 100644 index a7c8a92..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/http.py +++ /dev/null @@ -1,201 +0,0 @@ -from __future__ import annotations - -import asyncio -import os -import re - -from ..datastructures import Headers -from ..exceptions import SecurityError - - -__all__ = ["read_request", "read_response"] - -MAX_NUM_HEADERS = int(os.environ.get("WEBSOCKETS_MAX_NUM_HEADERS", "128")) -MAX_LINE_LENGTH = int(os.environ.get("WEBSOCKETS_MAX_LINE_LENGTH", "8192")) - - -def d(value: bytes) -> str: - """ - Decode a bytestring for interpolating into an error message. - - """ - return value.decode(errors="backslashreplace") - - -# See https://datatracker.ietf.org/doc/html/rfc7230#appendix-B. - -# Regex for validating header names. - -_token_re = re.compile(rb"[-!#$%&\'*+.^_`|~0-9a-zA-Z]+") - -# Regex for validating header values. - -# We don't attempt to support obsolete line folding. - -# Include HTAB (\x09), SP (\x20), VCHAR (\x21-\x7e), obs-text (\x80-\xff). - -# The ABNF is complicated because it attempts to express that optional -# whitespace is ignored. We strip whitespace and don't revalidate that. - -# See also https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4189 - -_value_re = re.compile(rb"[\x09\x20-\x7e\x80-\xff]*") - - -async def read_request(stream: asyncio.StreamReader) -> tuple[str, Headers]: - """ - Read an HTTP/1.1 GET request and return ``(path, headers)``. - - ``path`` isn't URL-decoded or validated in any way. - - ``path`` and ``headers`` are expected to contain only ASCII characters. - Other characters are represented with surrogate escapes. - - :func:`read_request` doesn't attempt to read the request body because - WebSocket handshake requests don't have one. If the request contains a - body, it may be read from ``stream`` after this coroutine returns. - - Args: - stream: Input to read the request from. - - Raises: - EOFError: If the connection is closed without a full HTTP request. - SecurityError: If the request exceeds a security limit. - ValueError: If the request isn't well formatted. - - """ - # https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.1 - - # Parsing is simple because fixed values are expected for method and - # version and because path isn't checked. Since WebSocket software tends - # to implement HTTP/1.1 strictly, there's little need for lenient parsing. - - try: - request_line = await read_line(stream) - except EOFError as exc: - raise EOFError("connection closed while reading HTTP request line") from exc - - try: - method, raw_path, version = request_line.split(b" ", 2) - except ValueError: # not enough values to unpack (expected 3, got 1-2) - raise ValueError(f"invalid HTTP request line: {d(request_line)}") from None - - if method != b"GET": - raise ValueError(f"unsupported HTTP method: {d(method)}") - if version != b"HTTP/1.1": - raise ValueError(f"unsupported HTTP version: {d(version)}") - path = raw_path.decode("ascii", "surrogateescape") - - headers = await read_headers(stream) - - return path, headers - - -async def read_response(stream: asyncio.StreamReader) -> tuple[int, str, Headers]: - """ - Read an HTTP/1.1 response and return ``(status_code, reason, headers)``. - - ``reason`` and ``headers`` are expected to contain only ASCII characters. - Other characters are represented with surrogate escapes. - - :func:`read_request` doesn't attempt to read the response body because - WebSocket handshake responses don't have one. If the response contains a - body, it may be read from ``stream`` after this coroutine returns. - - Args: - stream: Input to read the response from. - - Raises: - EOFError: If the connection is closed without a full HTTP response. - SecurityError: If the response exceeds a security limit. - ValueError: If the response isn't well formatted. - - """ - # https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2 - - # As in read_request, parsing is simple because a fixed value is expected - # for version, status_code is a 3-digit number, and reason can be ignored. - - try: - status_line = await read_line(stream) - except EOFError as exc: - raise EOFError("connection closed while reading HTTP status line") from exc - - try: - version, raw_status_code, raw_reason = status_line.split(b" ", 2) - except ValueError: # not enough values to unpack (expected 3, got 1-2) - raise ValueError(f"invalid HTTP status line: {d(status_line)}") from None - - if version != b"HTTP/1.1": - raise ValueError(f"unsupported HTTP version: {d(version)}") - try: - status_code = int(raw_status_code) - except ValueError: # invalid literal for int() with base 10 - raise ValueError(f"invalid HTTP status code: {d(raw_status_code)}") from None - if not 100 <= status_code < 1000: - raise ValueError(f"unsupported HTTP status code: {d(raw_status_code)}") - if not _value_re.fullmatch(raw_reason): - raise ValueError(f"invalid HTTP reason phrase: {d(raw_reason)}") - reason = raw_reason.decode() - - headers = await read_headers(stream) - - return status_code, reason, headers - - -async def read_headers(stream: asyncio.StreamReader) -> Headers: - """ - Read HTTP headers from ``stream``. - - Non-ASCII characters are represented with surrogate escapes. - - """ - # https://datatracker.ietf.org/doc/html/rfc7230#section-3.2 - - # We don't attempt to support obsolete line folding. - - headers = Headers() - for _ in range(MAX_NUM_HEADERS + 1): - try: - line = await read_line(stream) - except EOFError as exc: - raise EOFError("connection closed while reading HTTP headers") from exc - if line == b"": - break - - try: - raw_name, raw_value = line.split(b":", 1) - except ValueError: # not enough values to unpack (expected 2, got 1) - raise ValueError(f"invalid HTTP header line: {d(line)}") from None - if not _token_re.fullmatch(raw_name): - raise ValueError(f"invalid HTTP header name: {d(raw_name)}") - raw_value = raw_value.strip(b" \t") - if not _value_re.fullmatch(raw_value): - raise ValueError(f"invalid HTTP header value: {d(raw_value)}") - - name = raw_name.decode("ascii") # guaranteed to be ASCII at this point - value = raw_value.decode("ascii", "surrogateescape") - headers[name] = value - - else: - raise SecurityError("too many HTTP headers") - - return headers - - -async def read_line(stream: asyncio.StreamReader) -> bytes: - """ - Read a single line from ``stream``. - - CRLF is stripped from the return value. - - """ - # Security: this is bounded by the StreamReader's limit (default = 32 KiB). - line = await stream.readline() - # Security: this guarantees header values are small (hard-coded = 8 KiB) - if len(line) > MAX_LINE_LENGTH: - raise SecurityError("line too long") - # Not mandatory but safe - https://datatracker.ietf.org/doc/html/rfc7230#section-3.5 - if not line.endswith(b"\r\n"): - raise EOFError("line without CRLF") - return line[:-2] diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/protocol.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/protocol.py deleted file mode 100644 index 998e390..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/protocol.py +++ /dev/null @@ -1,1645 +0,0 @@ -from __future__ import annotations - -import asyncio -import codecs -import collections -import logging -import random -import ssl -import struct -import sys -import time -import uuid -import warnings -from typing import ( - Any, - AsyncIterable, - AsyncIterator, - Awaitable, - Callable, - Deque, - Iterable, - Mapping, - cast, -) - -from ..asyncio.compatibility import asyncio_timeout -from ..datastructures import Headers -from ..exceptions import ( - ConnectionClosed, - ConnectionClosedError, - ConnectionClosedOK, - InvalidState, - PayloadTooBig, - ProtocolError, -) -from ..extensions import Extension -from ..frames import ( - OK_CLOSE_CODES, - OP_BINARY, - OP_CLOSE, - OP_CONT, - OP_PING, - OP_PONG, - OP_TEXT, - Close, - CloseCode, - Opcode, -) -from ..protocol import State -from ..typing import Data, LoggerLike, Subprotocol -from .framing import Frame, prepare_ctrl, prepare_data - - -__all__ = ["WebSocketCommonProtocol"] - - -# In order to ensure consistency, the code always checks the current value of -# WebSocketCommonProtocol.state before assigning a new value and never yields -# between the check and the assignment. - - -class WebSocketCommonProtocol(asyncio.Protocol): - """ - WebSocket connection. - - :class:`WebSocketCommonProtocol` provides APIs shared between WebSocket - servers and clients. You shouldn't use it directly. Instead, use - :class:`~websockets.legacy.client.WebSocketClientProtocol` or - :class:`~websockets.legacy.server.WebSocketServerProtocol`. - - This documentation focuses on low-level details that aren't covered in the - documentation of :class:`~websockets.legacy.client.WebSocketClientProtocol` - and :class:`~websockets.legacy.server.WebSocketServerProtocol` for the sake - of simplicity. - - Once the connection is open, a Ping_ frame is sent every ``ping_interval`` - seconds. This serves as a keepalive. It helps keeping the connection open, - especially in the presence of proxies with short timeouts on inactive - connections. Set ``ping_interval`` to :obj:`None` to disable this behavior. - - .. _Ping: https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.2 - - If the corresponding Pong_ frame isn't received within ``ping_timeout`` - seconds, the connection is considered unusable and is closed with code 1011. - This ensures that the remote endpoint remains responsive. Set - ``ping_timeout`` to :obj:`None` to disable this behavior. - - .. _Pong: https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.3 - - See the discussion of :doc:`keepalive <../../topics/keepalive>` for details. - - The ``close_timeout`` parameter defines a maximum wait time for completing - the closing handshake and terminating the TCP connection. For legacy - reasons, :meth:`close` completes in at most ``5 * close_timeout`` seconds - for clients and ``4 * close_timeout`` for servers. - - ``close_timeout`` is a parameter of the protocol because websockets usually - calls :meth:`close` implicitly upon exit: - - * on the client side, when using :func:`~websockets.legacy.client.connect` - as a context manager; - * on the server side, when the connection handler terminates. - - To apply a timeout to any other API, wrap it in :func:`~asyncio.timeout` or - :func:`~asyncio.wait_for`. - - The ``max_size`` parameter enforces the maximum size for incoming messages - in bytes. The default value is 1 MiB. If a larger message is received, - :meth:`recv` will raise :exc:`~websockets.exceptions.ConnectionClosedError` - and the connection will be closed with code 1009. - - The ``max_queue`` parameter sets the maximum length of the queue that - holds incoming messages. The default value is ``32``. Messages are added - to an in-memory queue when they're received; then :meth:`recv` pops from - that queue. In order to prevent excessive memory consumption when - messages are received faster than they can be processed, the queue must - be bounded. If the queue fills up, the protocol stops processing incoming - data until :meth:`recv` is called. In this situation, various receive - buffers (at least in :mod:`asyncio` and in the OS) will fill up, then the - TCP receive window will shrink, slowing down transmission to avoid packet - loss. - - Since Python can use up to 4 bytes of memory to represent a single - character, each connection may use up to ``4 * max_size * max_queue`` - bytes of memory to store incoming messages. By default, this is 128 MiB. - You may want to lower the limits, depending on your application's - requirements. - - The ``read_limit`` argument sets the high-water limit of the buffer for - incoming bytes. The low-water limit is half the high-water limit. The - default value is 64 KiB, half of asyncio's default (based on the current - implementation of :class:`~asyncio.StreamReader`). - - The ``write_limit`` argument sets the high-water limit of the buffer for - outgoing bytes. The low-water limit is a quarter of the high-water limit. - The default value is 64 KiB, equal to asyncio's default (based on the - current implementation of ``FlowControlMixin``). - - See the discussion of :doc:`memory usage <../../topics/memory>` for details. - - Args: - logger: Logger for this server. - It defaults to ``logging.getLogger("websockets.protocol")``. - See the :doc:`logging guide <../../topics/logging>` for details. - ping_interval: Interval between keepalive pings in seconds. - :obj:`None` disables keepalive. - ping_timeout: Timeout for keepalive pings in seconds. - :obj:`None` disables timeouts. - close_timeout: Timeout for closing the connection in seconds. - For legacy reasons, the actual timeout is 4 or 5 times larger. - max_size: Maximum size of incoming messages in bytes. - :obj:`None` disables the limit. - max_queue: Maximum number of incoming messages in receive buffer. - :obj:`None` disables the limit. - read_limit: High-water mark of read buffer in bytes. - write_limit: High-water mark of write buffer in bytes. - - """ - - # There are only two differences between the client-side and server-side - # behavior: masking the payload and closing the underlying TCP connection. - # Set is_client = True/False and side = "client"/"server" to pick a side. - is_client: bool - side: str = "undefined" - - def __init__( - self, - *, - logger: LoggerLike | None = None, - ping_interval: float | None = 20, - ping_timeout: float | None = 20, - close_timeout: float | None = None, - max_size: int | None = 2**20, - max_queue: int | None = 2**5, - read_limit: int = 2**16, - write_limit: int = 2**16, - # The following arguments are kept only for backwards compatibility. - host: str | None = None, - port: int | None = None, - secure: bool | None = None, - legacy_recv: bool = False, - loop: asyncio.AbstractEventLoop | None = None, - timeout: float | None = None, - ) -> None: - if legacy_recv: # pragma: no cover - warnings.warn("legacy_recv is deprecated", DeprecationWarning) - - # Backwards compatibility: close_timeout used to be called timeout. - if timeout is None: - timeout = 10 - else: - warnings.warn("rename timeout to close_timeout", DeprecationWarning) - # If both are specified, timeout is ignored. - if close_timeout is None: - close_timeout = timeout - - # Backwards compatibility: the loop parameter used to be supported. - if loop is None: - loop = asyncio.get_event_loop() - else: - warnings.warn("remove loop argument", DeprecationWarning) - - self.ping_interval = ping_interval - self.ping_timeout = ping_timeout - self.close_timeout = close_timeout - self.max_size = max_size - self.max_queue = max_queue - self.read_limit = read_limit - self.write_limit = write_limit - - # Unique identifier. For logs. - self.id: uuid.UUID = uuid.uuid4() - """Unique identifier of the connection. Useful in logs.""" - - # Logger or LoggerAdapter for this connection. - if logger is None: - logger = logging.getLogger("websockets.protocol") - self.logger: LoggerLike = logging.LoggerAdapter(logger, {"websocket": self}) - """Logger for this connection.""" - - # Track if DEBUG is enabled. Shortcut logging calls if it isn't. - self.debug = logger.isEnabledFor(logging.DEBUG) - - self.loop = loop - - self._host = host - self._port = port - self._secure = secure - self.legacy_recv = legacy_recv - - # Configure read buffer limits. The high-water limit is defined by - # ``self.read_limit``. The ``limit`` argument controls the line length - # limit and half the buffer limit of :class:`~asyncio.StreamReader`. - # That's why it must be set to half of ``self.read_limit``. - self.reader = asyncio.StreamReader(limit=read_limit // 2, loop=loop) - - # Copied from asyncio.FlowControlMixin - self._paused = False - self._drain_waiter: asyncio.Future[None] | None = None - - self._drain_lock = asyncio.Lock() - - # This class implements the data transfer and closing handshake, which - # are shared between the client-side and the server-side. - # Subclasses implement the opening handshake and, on success, execute - # :meth:`connection_open` to change the state to OPEN. - self.state = State.CONNECTING - if self.debug: - self.logger.debug("= connection is CONNECTING") - - # HTTP protocol parameters. - self.path: str - """Path of the opening handshake request.""" - self.request_headers: Headers - """Opening handshake request headers.""" - self.response_headers: Headers - """Opening handshake response headers.""" - - # WebSocket protocol parameters. - self.extensions: list[Extension] = [] - self.subprotocol: Subprotocol | None = None - """Subprotocol, if one was negotiated.""" - - # Close code and reason, set when a close frame is sent or received. - self.close_rcvd: Close | None = None - self.close_sent: Close | None = None - self.close_rcvd_then_sent: bool | None = None - - # Completed when the connection state becomes CLOSED. Translates the - # :meth:`connection_lost` callback to a :class:`~asyncio.Future` - # that can be awaited. (Other :class:`~asyncio.Protocol` callbacks are - # translated by ``self.stream_reader``). - self.connection_lost_waiter: asyncio.Future[None] = loop.create_future() - - # Queue of received messages. - self.messages: Deque[Data] = collections.deque() - self._pop_message_waiter: asyncio.Future[None] | None = None - self._put_message_waiter: asyncio.Future[None] | None = None - - # Protect sending fragmented messages. - self._fragmented_message_waiter: asyncio.Future[None] | None = None - - # Mapping of ping IDs to pong waiters, in chronological order. - self.pings: dict[bytes, tuple[asyncio.Future[float], float]] = {} - - self.latency: float = 0 - """ - Latency of the connection, in seconds. - - Latency is defined as the round-trip time of the connection. It is - measured by sending a Ping frame and waiting for a matching Pong frame. - Before the first measurement, :attr:`latency` is ``0``. - - By default, websockets enables a :ref:`keepalive ` mechanism - that sends Ping frames automatically at regular intervals. You can also - send Ping frames and measure latency with :meth:`ping`. - """ - - # Task running the data transfer. - self.transfer_data_task: asyncio.Task[None] - - # Exception that occurred during data transfer, if any. - self.transfer_data_exc: BaseException | None = None - - # Task sending keepalive pings. - self.keepalive_ping_task: asyncio.Task[None] - - # Task closing the TCP connection. - self.close_connection_task: asyncio.Task[None] - - # Copied from asyncio.FlowControlMixin - async def _drain_helper(self) -> None: # pragma: no cover - if self.connection_lost_waiter.done(): - raise ConnectionResetError("Connection lost") - if not self._paused: - return - waiter = self._drain_waiter - assert waiter is None or waiter.cancelled() - waiter = self.loop.create_future() - self._drain_waiter = waiter - await waiter - - # Copied from asyncio.StreamWriter - async def _drain(self) -> None: # pragma: no cover - if self.reader is not None: - exc = self.reader.exception() - if exc is not None: - raise exc - if self.transport is not None: - if self.transport.is_closing(): - # Yield to the event loop so connection_lost() may be - # called. Without this, _drain_helper() would return - # immediately, and code that calls - # write(...); yield from drain() - # in a loop would never call connection_lost(), so it - # would not see an error when the socket is closed. - await asyncio.sleep(0) - await self._drain_helper() - - def connection_open(self) -> None: - """ - Callback when the WebSocket opening handshake completes. - - Enter the OPEN state and start the data transfer phase. - - """ - # 4.1. The WebSocket Connection is Established. - assert self.state is State.CONNECTING - self.state = State.OPEN - if self.debug: - self.logger.debug("= connection is OPEN") - # Start the task that receives incoming WebSocket messages. - self.transfer_data_task = self.loop.create_task(self.transfer_data()) - # Start the task that sends pings at regular intervals. - self.keepalive_ping_task = self.loop.create_task(self.keepalive_ping()) - # Start the task that eventually closes the TCP connection. - self.close_connection_task = self.loop.create_task(self.close_connection()) - - @property - def host(self) -> str | None: - alternative = "remote_address" if self.is_client else "local_address" - warnings.warn(f"use {alternative}[0] instead of host", DeprecationWarning) - return self._host - - @property - def port(self) -> int | None: - alternative = "remote_address" if self.is_client else "local_address" - warnings.warn(f"use {alternative}[1] instead of port", DeprecationWarning) - return self._port - - @property - def secure(self) -> bool | None: - warnings.warn("don't use secure", DeprecationWarning) - return self._secure - - # Public API - - @property - def local_address(self) -> Any: - """ - Local address of the connection. - - For IPv4 connections, this is a ``(host, port)`` tuple. - - The format of the address depends on the address family; - see :meth:`~socket.socket.getsockname`. - - :obj:`None` if the TCP connection isn't established yet. - - """ - try: - transport = self.transport - except AttributeError: - return None - else: - return transport.get_extra_info("sockname") - - @property - def remote_address(self) -> Any: - """ - Remote address of the connection. - - For IPv4 connections, this is a ``(host, port)`` tuple. - - The format of the address depends on the address family; - see :meth:`~socket.socket.getpeername`. - - :obj:`None` if the TCP connection isn't established yet. - - """ - try: - transport = self.transport - except AttributeError: - return None - else: - return transport.get_extra_info("peername") - - @property - def open(self) -> bool: - """ - :obj:`True` when the connection is open; :obj:`False` otherwise. - - This attribute may be used to detect disconnections. However, this - approach is discouraged per the EAFP_ principle. Instead, you should - handle :exc:`~websockets.exceptions.ConnectionClosed` exceptions. - - .. _EAFP: https://docs.python.org/3/glossary.html#term-eafp - - """ - return self.state is State.OPEN and not self.transfer_data_task.done() - - @property - def closed(self) -> bool: - """ - :obj:`True` when the connection is closed; :obj:`False` otherwise. - - Be aware that both :attr:`open` and :attr:`closed` are :obj:`False` - during the opening and closing sequences. - - """ - return self.state is State.CLOSED - - @property - def close_code(self) -> int | None: - """ - WebSocket close code, defined in `section 7.1.5 of RFC 6455`_. - - .. _section 7.1.5 of RFC 6455: - https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.5 - - :obj:`None` if the connection isn't closed yet. - - """ - if self.state is not State.CLOSED: - return None - elif self.close_rcvd is None: - return CloseCode.ABNORMAL_CLOSURE - else: - return self.close_rcvd.code - - @property - def close_reason(self) -> str | None: - """ - WebSocket close reason, defined in `section 7.1.6 of RFC 6455`_. - - .. _section 7.1.6 of RFC 6455: - https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.6 - - :obj:`None` if the connection isn't closed yet. - - """ - if self.state is not State.CLOSED: - return None - elif self.close_rcvd is None: - return "" - else: - return self.close_rcvd.reason - - async def __aiter__(self) -> AsyncIterator[Data]: - """ - Iterate on incoming messages. - - The iterator exits normally when the connection is closed with the close - code 1000 (OK) or 1001 (going away) or without a close code. - - It raises a :exc:`~websockets.exceptions.ConnectionClosedError` - exception when the connection is closed with any other code. - - """ - try: - while True: - yield await self.recv() - except ConnectionClosedOK: - return - - async def recv(self) -> Data: - """ - Receive the next message. - - When the connection is closed, :meth:`recv` raises - :exc:`~websockets.exceptions.ConnectionClosed`. Specifically, it raises - :exc:`~websockets.exceptions.ConnectionClosedOK` after a normal - connection closure and - :exc:`~websockets.exceptions.ConnectionClosedError` after a protocol - error or a network failure. This is how you detect the end of the - message stream. - - Canceling :meth:`recv` is safe. There's no risk of losing the next - message. The next invocation of :meth:`recv` will return it. - - This makes it possible to enforce a timeout by wrapping :meth:`recv` in - :func:`~asyncio.timeout` or :func:`~asyncio.wait_for`. - - Returns: - A string (:class:`str`) for a Text_ frame. A bytestring - (:class:`bytes`) for a Binary_ frame. - - .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - - Raises: - ConnectionClosed: When the connection is closed. - RuntimeError: If two coroutines call :meth:`recv` concurrently. - - """ - if self._pop_message_waiter is not None: - raise RuntimeError( - "cannot call recv while another coroutine " - "is already waiting for the next message" - ) - - # Don't await self.ensure_open() here: - # - messages could be available in the queue even if the connection - # is closed; - # - messages could be received before the closing frame even if the - # connection is closing. - - # Wait until there's a message in the queue (if necessary) or the - # connection is closed. - while len(self.messages) <= 0: - pop_message_waiter: asyncio.Future[None] = self.loop.create_future() - self._pop_message_waiter = pop_message_waiter - try: - # If asyncio.wait() is canceled, it doesn't cancel - # pop_message_waiter and self.transfer_data_task. - await asyncio.wait( - [pop_message_waiter, self.transfer_data_task], - return_when=asyncio.FIRST_COMPLETED, - ) - finally: - self._pop_message_waiter = None - - # If asyncio.wait(...) exited because self.transfer_data_task - # completed before receiving a new message, raise a suitable - # exception (or return None if legacy_recv is enabled). - if not pop_message_waiter.done(): - if self.legacy_recv: - return None # type: ignore - else: - # Wait until the connection is closed to raise - # ConnectionClosed with the correct code and reason. - await self.ensure_open() - - # Pop a message from the queue. - message = self.messages.popleft() - - # Notify transfer_data(). - if self._put_message_waiter is not None: - self._put_message_waiter.set_result(None) - self._put_message_waiter = None - - return message - - async def send( - self, - message: Data | Iterable[Data] | AsyncIterable[Data], - ) -> None: - """ - Send a message. - - A string (:class:`str`) is sent as a Text_ frame. A bytestring or - bytes-like object (:class:`bytes`, :class:`bytearray`, or - :class:`memoryview`) is sent as a Binary_ frame. - - .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - - :meth:`send` also accepts an iterable or an asynchronous iterable of - strings, bytestrings, or bytes-like objects to enable fragmentation_. - Each item is treated as a message fragment and sent in its own frame. - All items must be of the same type, or else :meth:`send` will raise a - :exc:`TypeError` and the connection will be closed. - - .. _fragmentation: https://datatracker.ietf.org/doc/html/rfc6455#section-5.4 - - :meth:`send` rejects dict-like objects because this is often an error. - (If you want to send the keys of a dict-like object as fragments, call - its :meth:`~dict.keys` method and pass the result to :meth:`send`.) - - Canceling :meth:`send` is discouraged. Instead, you should close the - connection with :meth:`close`. Indeed, there are only two situations - where :meth:`send` may yield control to the event loop and then get - canceled; in both cases, :meth:`close` has the same effect and is - more clear: - - 1. The write buffer is full. If you don't want to wait until enough - data is sent, your only alternative is to close the connection. - :meth:`close` will likely time out then abort the TCP connection. - 2. ``message`` is an asynchronous iterator that yields control. - Stopping in the middle of a fragmented message will cause a - protocol error and the connection will be closed. - - When the connection is closed, :meth:`send` raises - :exc:`~websockets.exceptions.ConnectionClosed`. Specifically, it - raises :exc:`~websockets.exceptions.ConnectionClosedOK` after a normal - connection closure and - :exc:`~websockets.exceptions.ConnectionClosedError` after a protocol - error or a network failure. - - Args: - message: Message to send. - - Raises: - ConnectionClosed: When the connection is closed. - TypeError: If ``message`` doesn't have a supported type. - - """ - await self.ensure_open() - - # While sending a fragmented message, prevent sending other messages - # until all fragments are sent. - while self._fragmented_message_waiter is not None: - await asyncio.shield(self._fragmented_message_waiter) - - # Unfragmented message -- this case must be handled first because - # strings and bytes-like objects are iterable. - - if isinstance(message, (str, bytes, bytearray, memoryview)): - opcode, data = prepare_data(message) - await self.write_frame(True, opcode, data) - - # Catch a common mistake -- passing a dict to send(). - - elif isinstance(message, Mapping): - raise TypeError("data is a dict-like object") - - # Fragmented message -- regular iterator. - - elif isinstance(message, Iterable): - # Work around https://github.com/python/mypy/issues/6227 - message = cast(Iterable[Data], message) - - iter_message = iter(message) - try: - fragment = next(iter_message) - except StopIteration: - return - opcode, data = prepare_data(fragment) - - self._fragmented_message_waiter = self.loop.create_future() - try: - # First fragment. - await self.write_frame(False, opcode, data) - - # Other fragments. - for fragment in iter_message: - confirm_opcode, data = prepare_data(fragment) - if confirm_opcode != opcode: - raise TypeError("data contains inconsistent types") - await self.write_frame(False, OP_CONT, data) - - # Final fragment. - await self.write_frame(True, OP_CONT, b"") - - except (Exception, asyncio.CancelledError): - # We're half-way through a fragmented message and we can't - # complete it. This makes the connection unusable. - self.fail_connection(CloseCode.INTERNAL_ERROR) - raise - - finally: - self._fragmented_message_waiter.set_result(None) - self._fragmented_message_waiter = None - - # Fragmented message -- asynchronous iterator - - elif isinstance(message, AsyncIterable): - # Implement aiter_message = aiter(message) without aiter - # Work around https://github.com/python/mypy/issues/5738 - aiter_message = cast( - Callable[[AsyncIterable[Data]], AsyncIterator[Data]], - type(message).__aiter__, - )(message) - try: - # Implement fragment = anext(aiter_message) without anext - # Work around https://github.com/python/mypy/issues/5738 - fragment = await cast( - Callable[[AsyncIterator[Data]], Awaitable[Data]], - type(aiter_message).__anext__, - )(aiter_message) - except StopAsyncIteration: - return - opcode, data = prepare_data(fragment) - - self._fragmented_message_waiter = self.loop.create_future() - try: - # First fragment. - await self.write_frame(False, opcode, data) - - # Other fragments. - async for fragment in aiter_message: - confirm_opcode, data = prepare_data(fragment) - if confirm_opcode != opcode: - raise TypeError("data contains inconsistent types") - await self.write_frame(False, OP_CONT, data) - - # Final fragment. - await self.write_frame(True, OP_CONT, b"") - - except (Exception, asyncio.CancelledError): - # We're half-way through a fragmented message and we can't - # complete it. This makes the connection unusable. - self.fail_connection(CloseCode.INTERNAL_ERROR) - raise - - finally: - self._fragmented_message_waiter.set_result(None) - self._fragmented_message_waiter = None - - else: - raise TypeError("data must be str, bytes-like, or iterable") - - async def close( - self, - code: int = CloseCode.NORMAL_CLOSURE, - reason: str = "", - ) -> None: - """ - Perform the closing handshake. - - :meth:`close` waits for the other end to complete the handshake and - for the TCP connection to terminate. As a consequence, there's no need - to await :meth:`wait_closed` after :meth:`close`. - - :meth:`close` is idempotent: it doesn't do anything once the - connection is closed. - - Wrapping :func:`close` in :func:`~asyncio.create_task` is safe, given - that errors during connection termination aren't particularly useful. - - Canceling :meth:`close` is discouraged. If it takes too long, you can - set a shorter ``close_timeout``. If you don't want to wait, let the - Python process exit, then the OS will take care of closing the TCP - connection. - - Args: - code: WebSocket close code. - reason: WebSocket close reason. - - """ - try: - async with asyncio_timeout(self.close_timeout): - await self.write_close_frame(Close(code, reason)) - except asyncio.TimeoutError: - # If the close frame cannot be sent because the send buffers - # are full, the closing handshake won't complete anyway. - # Fail the connection to shut down faster. - self.fail_connection() - - # If no close frame is received within the timeout, asyncio_timeout() - # cancels the data transfer task and raises TimeoutError. - - # If close() is called multiple times concurrently and one of these - # calls hits the timeout, the data transfer task will be canceled. - # Other calls will receive a CancelledError here. - - try: - # If close() is canceled during the wait, self.transfer_data_task - # is canceled before the timeout elapses. - async with asyncio_timeout(self.close_timeout): - await self.transfer_data_task - except (asyncio.TimeoutError, asyncio.CancelledError): - pass - - # Wait for the close connection task to close the TCP connection. - await asyncio.shield(self.close_connection_task) - - async def wait_closed(self) -> None: - """ - Wait until the connection is closed. - - This coroutine is identical to the :attr:`closed` attribute, except it - can be awaited. - - This can make it easier to detect connection termination, regardless - of its cause, in tasks that interact with the WebSocket connection. - - """ - await asyncio.shield(self.connection_lost_waiter) - - async def ping(self, data: Data | None = None) -> Awaitable[float]: - """ - Send a Ping_. - - .. _Ping: https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.2 - - A ping may serve as a keepalive, as a check that the remote endpoint - received all messages up to this point, or to measure :attr:`latency`. - - Canceling :meth:`ping` is discouraged. If :meth:`ping` doesn't return - immediately, it means the write buffer is full. If you don't want to - wait, you should close the connection. - - Canceling the :class:`~asyncio.Future` returned by :meth:`ping` has no - effect. - - Args: - data: Payload of the ping. A string will be encoded to UTF-8. - If ``data`` is :obj:`None`, the payload is four random bytes. - - Returns: - A future that will be completed when the corresponding pong is - received. You can ignore it if you don't intend to wait. The result - of the future is the latency of the connection in seconds. - - :: - - pong_waiter = await ws.ping() - # only if you want to wait for the corresponding pong - latency = await pong_waiter - - Raises: - ConnectionClosed: When the connection is closed. - RuntimeError: If another ping was sent with the same data and - the corresponding pong wasn't received yet. - - """ - await self.ensure_open() - - if data is not None: - data = prepare_ctrl(data) - - # Protect against duplicates if a payload is explicitly set. - if data in self.pings: - raise RuntimeError("already waiting for a pong with the same data") - - # Generate a unique random payload otherwise. - while data is None or data in self.pings: - data = struct.pack("!I", random.getrandbits(32)) - - pong_waiter = self.loop.create_future() - # Resolution of time.monotonic() may be too low on Windows. - ping_timestamp = time.perf_counter() - self.pings[data] = (pong_waiter, ping_timestamp) - - await self.write_frame(True, OP_PING, data) - - return asyncio.shield(pong_waiter) - - async def pong(self, data: Data = b"") -> None: - """ - Send a Pong_. - - .. _Pong: https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.3 - - An unsolicited pong may serve as a unidirectional heartbeat. - - Canceling :meth:`pong` is discouraged. If :meth:`pong` doesn't return - immediately, it means the write buffer is full. If you don't want to - wait, you should close the connection. - - Args: - data: Payload of the pong. A string will be encoded to UTF-8. - - Raises: - ConnectionClosed: When the connection is closed. - - """ - await self.ensure_open() - - data = prepare_ctrl(data) - - await self.write_frame(True, OP_PONG, data) - - # Private methods - no guarantees. - - def connection_closed_exc(self) -> ConnectionClosed: - exc: ConnectionClosed - if ( - self.close_rcvd is not None - and self.close_rcvd.code in OK_CLOSE_CODES - and self.close_sent is not None - and self.close_sent.code in OK_CLOSE_CODES - ): - exc = ConnectionClosedOK( - self.close_rcvd, - self.close_sent, - self.close_rcvd_then_sent, - ) - else: - exc = ConnectionClosedError( - self.close_rcvd, - self.close_sent, - self.close_rcvd_then_sent, - ) - # Chain to the exception that terminated data transfer, if any. - exc.__cause__ = self.transfer_data_exc - return exc - - async def ensure_open(self) -> None: - """ - Check that the WebSocket connection is open. - - Raise :exc:`~websockets.exceptions.ConnectionClosed` if it isn't. - - """ - # Handle cases from most common to least common for performance. - if self.state is State.OPEN: - # If self.transfer_data_task exited without a closing handshake, - # self.close_connection_task may be closing the connection, going - # straight from OPEN to CLOSED. - if self.transfer_data_task.done(): - await asyncio.shield(self.close_connection_task) - raise self.connection_closed_exc() - else: - return - - if self.state is State.CLOSED: - raise self.connection_closed_exc() - - if self.state is State.CLOSING: - # If we started the closing handshake, wait for its completion to - # get the proper close code and reason. self.close_connection_task - # will complete within 4 or 5 * close_timeout after close(). The - # CLOSING state also occurs when failing the connection. In that - # case self.close_connection_task will complete even faster. - await asyncio.shield(self.close_connection_task) - raise self.connection_closed_exc() - - # Control may only reach this point in buggy third-party subclasses. - assert self.state is State.CONNECTING - raise InvalidState("WebSocket connection isn't established yet") - - async def transfer_data(self) -> None: - """ - Read incoming messages and put them in a queue. - - This coroutine runs in a task until the closing handshake is started. - - """ - try: - while True: - message = await self.read_message() - - # Exit the loop when receiving a close frame. - if message is None: - break - - # Wait until there's room in the queue (if necessary). - if self.max_queue is not None: - while len(self.messages) >= self.max_queue: - self._put_message_waiter = self.loop.create_future() - try: - await asyncio.shield(self._put_message_waiter) - finally: - self._put_message_waiter = None - - # Put the message in the queue. - self.messages.append(message) - - # Notify recv(). - if self._pop_message_waiter is not None: - self._pop_message_waiter.set_result(None) - self._pop_message_waiter = None - - except asyncio.CancelledError as exc: - self.transfer_data_exc = exc - # If fail_connection() cancels this task, avoid logging the error - # twice and failing the connection again. - raise - - except ProtocolError as exc: - self.transfer_data_exc = exc - self.fail_connection(CloseCode.PROTOCOL_ERROR) - - except (ConnectionError, TimeoutError, EOFError, ssl.SSLError) as exc: - # Reading data with self.reader.readexactly may raise: - # - most subclasses of ConnectionError if the TCP connection - # breaks, is reset, or is aborted; - # - TimeoutError if the TCP connection times out; - # - IncompleteReadError, a subclass of EOFError, if fewer - # bytes are available than requested; - # - ssl.SSLError if the other side infringes the TLS protocol. - self.transfer_data_exc = exc - self.fail_connection(CloseCode.ABNORMAL_CLOSURE) - - except UnicodeDecodeError as exc: - self.transfer_data_exc = exc - self.fail_connection(CloseCode.INVALID_DATA) - - except PayloadTooBig as exc: - self.transfer_data_exc = exc - self.fail_connection(CloseCode.MESSAGE_TOO_BIG) - - except Exception as exc: - # This shouldn't happen often because exceptions expected under - # regular circumstances are handled above. If it does, consider - # catching and handling more exceptions. - self.logger.error("data transfer failed", exc_info=True) - - self.transfer_data_exc = exc - self.fail_connection(CloseCode.INTERNAL_ERROR) - - async def read_message(self) -> Data | None: - """ - Read a single message from the connection. - - Re-assemble data frames if the message is fragmented. - - Return :obj:`None` when the closing handshake is started. - - """ - frame = await self.read_data_frame(max_size=self.max_size) - - # A close frame was received. - if frame is None: - return None - - if frame.opcode == OP_TEXT: - text = True - elif frame.opcode == OP_BINARY: - text = False - else: # frame.opcode == OP_CONT - raise ProtocolError("unexpected opcode") - - # Shortcut for the common case - no fragmentation - if frame.fin: - return frame.data.decode() if text else frame.data - - # 5.4. Fragmentation - fragments: list[Data] = [] - max_size = self.max_size - if text: - decoder_factory = codecs.getincrementaldecoder("utf-8") - decoder = decoder_factory(errors="strict") - if max_size is None: - - def append(frame: Frame) -> None: - nonlocal fragments - fragments.append(decoder.decode(frame.data, frame.fin)) - - else: - - def append(frame: Frame) -> None: - nonlocal fragments, max_size - fragments.append(decoder.decode(frame.data, frame.fin)) - assert isinstance(max_size, int) - max_size -= len(frame.data) - - else: - if max_size is None: - - def append(frame: Frame) -> None: - nonlocal fragments - fragments.append(frame.data) - - else: - - def append(frame: Frame) -> None: - nonlocal fragments, max_size - fragments.append(frame.data) - assert isinstance(max_size, int) - max_size -= len(frame.data) - - append(frame) - - while not frame.fin: - frame = await self.read_data_frame(max_size=max_size) - if frame is None: - raise ProtocolError("incomplete fragmented message") - if frame.opcode != OP_CONT: - raise ProtocolError("unexpected opcode") - append(frame) - - return ("" if text else b"").join(fragments) - - async def read_data_frame(self, max_size: int | None) -> Frame | None: - """ - Read a single data frame from the connection. - - Process control frames received before the next data frame. - - Return :obj:`None` if a close frame is encountered before any data frame. - - """ - # 6.2. Receiving Data - while True: - frame = await self.read_frame(max_size) - - # 5.5. Control Frames - if frame.opcode == OP_CLOSE: - # 7.1.5. The WebSocket Connection Close Code - # 7.1.6. The WebSocket Connection Close Reason - self.close_rcvd = Close.parse(frame.data) - if self.close_sent is not None: - self.close_rcvd_then_sent = False - try: - # Echo the original data instead of re-serializing it with - # Close.serialize() because that fails when the close frame - # is empty and Close.parse() synthesizes a 1005 close code. - await self.write_close_frame(self.close_rcvd, frame.data) - except ConnectionClosed: - # Connection closed before we could echo the close frame. - pass - return None - - elif frame.opcode == OP_PING: - # Answer pings, unless connection is CLOSING. - if self.state is State.OPEN: - try: - await self.pong(frame.data) - except ConnectionClosed: - # Connection closed while draining write buffer. - pass - - elif frame.opcode == OP_PONG: - if frame.data in self.pings: - pong_timestamp = time.perf_counter() - # Sending a pong for only the most recent ping is legal. - # Acknowledge all previous pings too in that case. - ping_id = None - ping_ids = [] - for ping_id, (pong_waiter, ping_timestamp) in self.pings.items(): - ping_ids.append(ping_id) - if not pong_waiter.done(): - pong_waiter.set_result(pong_timestamp - ping_timestamp) - if ping_id == frame.data: - self.latency = pong_timestamp - ping_timestamp - break - else: - raise AssertionError("solicited pong not found in pings") - # Remove acknowledged pings from self.pings. - for ping_id in ping_ids: - del self.pings[ping_id] - - # 5.6. Data Frames - else: - return frame - - async def read_frame(self, max_size: int | None) -> Frame: - """ - Read a single frame from the connection. - - """ - frame = await Frame.read( - self.reader.readexactly, - mask=not self.is_client, - max_size=max_size, - extensions=self.extensions, - ) - if self.debug: - self.logger.debug("< %s", frame) - return frame - - def write_frame_sync(self, fin: bool, opcode: int, data: bytes) -> None: - frame = Frame(fin, Opcode(opcode), data) - if self.debug: - self.logger.debug("> %s", frame) - frame.write( - self.transport.write, - mask=self.is_client, - extensions=self.extensions, - ) - - async def drain(self) -> None: - try: - # drain() cannot be called concurrently by multiple coroutines. - # See https://github.com/python/cpython/issues/74116 for details. - # This workaround can be removed when dropping Python < 3.10. - async with self._drain_lock: - # Handle flow control automatically. - await self._drain() - except ConnectionError: - # Terminate the connection if the socket died. - self.fail_connection() - # Wait until the connection is closed to raise ConnectionClosed - # with the correct code and reason. - await self.ensure_open() - - async def write_frame( - self, fin: bool, opcode: int, data: bytes, *, _state: int = State.OPEN - ) -> None: - # Defensive assertion for protocol compliance. - if self.state is not _state: # pragma: no cover - raise InvalidState( - f"Cannot write to a WebSocket in the {self.state.name} state" - ) - self.write_frame_sync(fin, opcode, data) - await self.drain() - - async def write_close_frame(self, close: Close, data: bytes | None = None) -> None: - """ - Write a close frame if and only if the connection state is OPEN. - - This dedicated coroutine must be used for writing close frames to - ensure that at most one close frame is sent on a given connection. - - """ - # Test and set the connection state before sending the close frame to - # avoid sending two frames in case of concurrent calls. - if self.state is State.OPEN: - # 7.1.3. The WebSocket Closing Handshake is Started - self.state = State.CLOSING - if self.debug: - self.logger.debug("= connection is CLOSING") - - self.close_sent = close - if self.close_rcvd is not None: - self.close_rcvd_then_sent = True - if data is None: - data = close.serialize() - - # 7.1.2. Start the WebSocket Closing Handshake - await self.write_frame(True, OP_CLOSE, data, _state=State.CLOSING) - - async def keepalive_ping(self) -> None: - """ - Send a Ping frame and wait for a Pong frame at regular intervals. - - This coroutine exits when the connection terminates and one of the - following happens: - - - :meth:`ping` raises :exc:`ConnectionClosed`, or - - :meth:`close_connection` cancels :attr:`keepalive_ping_task`. - - """ - if self.ping_interval is None: - return - - try: - while True: - await asyncio.sleep(self.ping_interval) - - self.logger.debug("% sending keepalive ping") - pong_waiter = await self.ping() - - if self.ping_timeout is not None: - try: - async with asyncio_timeout(self.ping_timeout): - # Raises CancelledError if the connection is closed, - # when close_connection() cancels keepalive_ping(). - # Raises ConnectionClosed if the connection is lost, - # when connection_lost() calls abort_pings(). - await pong_waiter - self.logger.debug("% received keepalive pong") - except asyncio.TimeoutError: - if self.debug: - self.logger.debug("! timed out waiting for keepalive pong") - self.fail_connection( - CloseCode.INTERNAL_ERROR, - "keepalive ping timeout", - ) - break - - except ConnectionClosed: - pass - - except Exception: - self.logger.error("keepalive ping failed", exc_info=True) - - async def close_connection(self) -> None: - """ - 7.1.1. Close the WebSocket Connection - - When the opening handshake succeeds, :meth:`connection_open` starts - this coroutine in a task. It waits for the data transfer phase to - complete then it closes the TCP connection cleanly. - - When the opening handshake fails, :meth:`fail_connection` does the - same. There's no data transfer phase in that case. - - """ - try: - # Wait for the data transfer phase to complete. - if hasattr(self, "transfer_data_task"): - try: - await self.transfer_data_task - except asyncio.CancelledError: - pass - - # Cancel the keepalive ping task. - if hasattr(self, "keepalive_ping_task"): - self.keepalive_ping_task.cancel() - - # A client should wait for a TCP close from the server. - if self.is_client and hasattr(self, "transfer_data_task"): - if await self.wait_for_connection_lost(): - return - if self.debug: - self.logger.debug("! timed out waiting for TCP close") - - # Half-close the TCP connection if possible (when there's no TLS). - if self.transport.can_write_eof(): - if self.debug: - self.logger.debug("x half-closing TCP connection") - # write_eof() doesn't document which exceptions it raises. - # "[Errno 107] Transport endpoint is not connected" happens - # but it isn't completely clear under which circumstances. - # uvloop can raise RuntimeError here. - try: - self.transport.write_eof() - except (OSError, RuntimeError): # pragma: no cover - pass - - if await self.wait_for_connection_lost(): - return - if self.debug: - self.logger.debug("! timed out waiting for TCP close") - - finally: - # The try/finally ensures that the transport never remains open, - # even if this coroutine is canceled (for example). - await self.close_transport() - - async def close_transport(self) -> None: - """ - Close the TCP connection. - - """ - # If connection_lost() was called, the TCP connection is closed. - # However, if TLS is enabled, the transport still needs closing. - # Else asyncio complains: ResourceWarning: unclosed transport. - if self.connection_lost_waiter.done() and self.transport.is_closing(): - return - - # Close the TCP connection. Buffers are flushed asynchronously. - if self.debug: - self.logger.debug("x closing TCP connection") - self.transport.close() - - if await self.wait_for_connection_lost(): - return - if self.debug: - self.logger.debug("! timed out waiting for TCP close") - - # Abort the TCP connection. Buffers are discarded. - if self.debug: - self.logger.debug("x aborting TCP connection") - self.transport.abort() - - # connection_lost() is called quickly after aborting. - await self.wait_for_connection_lost() - - async def wait_for_connection_lost(self) -> bool: - """ - Wait until the TCP connection is closed or ``self.close_timeout`` elapses. - - Return :obj:`True` if the connection is closed and :obj:`False` - otherwise. - - """ - if not self.connection_lost_waiter.done(): - try: - async with asyncio_timeout(self.close_timeout): - await asyncio.shield(self.connection_lost_waiter) - except asyncio.TimeoutError: - pass - # Re-check self.connection_lost_waiter.done() synchronously because - # connection_lost() could run between the moment the timeout occurs - # and the moment this coroutine resumes running. - return self.connection_lost_waiter.done() - - def fail_connection( - self, - code: int = CloseCode.ABNORMAL_CLOSURE, - reason: str = "", - ) -> None: - """ - 7.1.7. Fail the WebSocket Connection - - This requires: - - 1. Stopping all processing of incoming data, which means cancelling - :attr:`transfer_data_task`. The close code will be 1006 unless a - close frame was received earlier. - - 2. Sending a close frame with an appropriate code if the opening - handshake succeeded and the other side is likely to process it. - - 3. Closing the connection. :meth:`close_connection` takes care of - this once :attr:`transfer_data_task` exits after being canceled. - - (The specification describes these steps in the opposite order.) - - """ - if self.debug: - self.logger.debug("! failing connection with code %d", code) - - # Cancel transfer_data_task if the opening handshake succeeded. - # cancel() is idempotent and ignored if the task is done already. - if hasattr(self, "transfer_data_task"): - self.transfer_data_task.cancel() - - # Send a close frame when the state is OPEN (a close frame was already - # sent if it's CLOSING), except when failing the connection because of - # an error reading from or writing to the network. - # Don't send a close frame if the connection is broken. - if code != CloseCode.ABNORMAL_CLOSURE and self.state is State.OPEN: - close = Close(code, reason) - - # Write the close frame without draining the write buffer. - - # Keeping fail_connection() synchronous guarantees it can't - # get stuck and simplifies the implementation of the callers. - # Not drainig the write buffer is acceptable in this context. - - # This duplicates a few lines of code from write_close_frame(). - - self.state = State.CLOSING - if self.debug: - self.logger.debug("= connection is CLOSING") - - # If self.close_rcvd was set, the connection state would be - # CLOSING. Therefore self.close_rcvd isn't set and we don't - # have to set self.close_rcvd_then_sent. - assert self.close_rcvd is None - self.close_sent = close - - self.write_frame_sync(True, OP_CLOSE, close.serialize()) - - # Start close_connection_task if the opening handshake didn't succeed. - if not hasattr(self, "close_connection_task"): - self.close_connection_task = self.loop.create_task(self.close_connection()) - - def abort_pings(self) -> None: - """ - Raise ConnectionClosed in pending keepalive pings. - - They'll never receive a pong once the connection is closed. - - """ - assert self.state is State.CLOSED - exc = self.connection_closed_exc() - - for pong_waiter, _ping_timestamp in self.pings.values(): - pong_waiter.set_exception(exc) - # If the exception is never retrieved, it will be logged when ping - # is garbage-collected. This is confusing for users. - # Given that ping is done (with an exception), canceling it does - # nothing, but it prevents logging the exception. - pong_waiter.cancel() - - # asyncio.Protocol methods - - def connection_made(self, transport: asyncio.BaseTransport) -> None: - """ - Configure write buffer limits. - - The high-water limit is defined by ``self.write_limit``. - - The low-water limit currently defaults to ``self.write_limit // 4`` in - :meth:`~asyncio.WriteTransport.set_write_buffer_limits`, which should - be all right for reasonable use cases of this library. - - This is the earliest point where we can get hold of the transport, - which means it's the best point for configuring it. - - """ - transport = cast(asyncio.Transport, transport) - transport.set_write_buffer_limits(self.write_limit) - self.transport = transport - - # Copied from asyncio.StreamReaderProtocol - self.reader.set_transport(transport) - - def connection_lost(self, exc: Exception | None) -> None: - """ - 7.1.4. The WebSocket Connection is Closed. - - """ - self.state = State.CLOSED - self.logger.debug("= connection is CLOSED") - - self.abort_pings() - - # If self.connection_lost_waiter isn't pending, that's a bug, because: - # - it's set only here in connection_lost() which is called only once; - # - it must never be canceled. - self.connection_lost_waiter.set_result(None) - - if True: # pragma: no cover - # Copied from asyncio.StreamReaderProtocol - if self.reader is not None: - if exc is None: - self.reader.feed_eof() - else: - self.reader.set_exception(exc) - - # Copied from asyncio.FlowControlMixin - # Wake up the writer if currently paused. - if not self._paused: - return - waiter = self._drain_waiter - if waiter is None: - return - self._drain_waiter = None - if waiter.done(): - return - if exc is None: - waiter.set_result(None) - else: - waiter.set_exception(exc) - - def pause_writing(self) -> None: # pragma: no cover - assert not self._paused - self._paused = True - - def resume_writing(self) -> None: # pragma: no cover - assert self._paused - self._paused = False - - waiter = self._drain_waiter - if waiter is not None: - self._drain_waiter = None - if not waiter.done(): - waiter.set_result(None) - - def data_received(self, data: bytes) -> None: - self.reader.feed_data(data) - - def eof_received(self) -> None: - """ - Close the transport after receiving EOF. - - The WebSocket protocol has its own closing handshake: endpoints close - the TCP or TLS connection after sending and receiving a close frame. - - As a consequence, they never need to write after receiving EOF, so - there's no reason to keep the transport open by returning :obj:`True`. - - Besides, that doesn't work on TLS connections. - - """ - self.reader.feed_eof() - - -# broadcast() is defined in the protocol module even though it's primarily -# used by servers and documented in the server module because it works with -# client connections too and because it's easier to test together with the -# WebSocketCommonProtocol class. - - -def broadcast( - websockets: Iterable[WebSocketCommonProtocol], - message: Data, - raise_exceptions: bool = False, -) -> None: - """ - Broadcast a message to several WebSocket connections. - - A string (:class:`str`) is sent as a Text_ frame. A bytestring or bytes-like - object (:class:`bytes`, :class:`bytearray`, or :class:`memoryview`) is sent - as a Binary_ frame. - - .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - - :func:`broadcast` pushes the message synchronously to all connections even - if their write buffers are overflowing. There's no backpressure. - - If you broadcast messages faster than a connection can handle them, messages - will pile up in its write buffer until the connection times out. Keep - ``ping_interval`` and ``ping_timeout`` low to prevent excessive memory usage - from slow connections. - - Unlike :meth:`~websockets.legacy.protocol.WebSocketCommonProtocol.send`, - :func:`broadcast` doesn't support sending fragmented messages. Indeed, - fragmentation is useful for sending large messages without buffering them in - memory, while :func:`broadcast` buffers one copy per connection as fast as - possible. - - :func:`broadcast` skips connections that aren't open in order to avoid - errors on connections where the closing handshake is in progress. - - :func:`broadcast` ignores failures to write the message on some connections. - It continues writing to other connections. On Python 3.11 and above, you may - set ``raise_exceptions`` to :obj:`True` to record failures and raise all - exceptions in a :pep:`654` :exc:`ExceptionGroup`. - - While :func:`broadcast` makes more sense for servers, it works identically - with clients, if you have a use case for opening connections to many servers - and broadcasting a message to them. - - Args: - websockets: WebSocket connections to which the message will be sent. - message: Message to send. - raise_exceptions: Whether to raise an exception in case of failures. - - Raises: - TypeError: If ``message`` doesn't have a supported type. - - """ - if not isinstance(message, (str, bytes, bytearray, memoryview)): - raise TypeError("data must be str or bytes-like") - - if raise_exceptions: - if sys.version_info[:2] < (3, 11): # pragma: no cover - raise ValueError("raise_exceptions requires at least Python 3.11") - exceptions = [] - - opcode, data = prepare_data(message) - - for websocket in websockets: - if websocket.state is not State.OPEN: - continue - - if websocket._fragmented_message_waiter is not None: - if raise_exceptions: - exception = RuntimeError("sending a fragmented message") - exceptions.append(exception) - else: - websocket.logger.warning( - "skipped broadcast: sending a fragmented message", - ) - continue - - try: - websocket.write_frame_sync(True, opcode, data) - except Exception as write_exception: - if raise_exceptions: - exception = RuntimeError("failed to write message") - exception.__cause__ = write_exception - exceptions.append(exception) - else: - websocket.logger.warning( - "skipped broadcast: failed to write message", - exc_info=True, - ) - - if raise_exceptions and exceptions: - raise ExceptionGroup("skipped broadcast", exceptions) - - -# Pretend that broadcast is actually defined in the server module. -broadcast.__module__ = "websockets.legacy.server" diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/server.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/server.py deleted file mode 100644 index 2cb9b1a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/legacy/server.py +++ /dev/null @@ -1,1200 +0,0 @@ -from __future__ import annotations - -import asyncio -import email.utils -import functools -import http -import inspect -import logging -import socket -import warnings -from types import TracebackType -from typing import ( - Any, - Awaitable, - Callable, - Generator, - Iterable, - Sequence, - Tuple, - Union, - cast, -) - -from ..asyncio.compatibility import asyncio_timeout -from ..datastructures import Headers, HeadersLike, MultipleValuesError -from ..exceptions import ( - InvalidHandshake, - InvalidHeader, - InvalidOrigin, - InvalidUpgrade, - NegotiationError, -) -from ..extensions import Extension, ServerExtensionFactory -from ..extensions.permessage_deflate import enable_server_permessage_deflate -from ..headers import ( - build_extension, - parse_extension, - parse_subprotocol, - validate_subprotocols, -) -from ..http11 import SERVER -from ..protocol import State -from ..typing import ExtensionHeader, LoggerLike, Origin, StatusLike, Subprotocol -from .exceptions import AbortHandshake, InvalidMessage -from .handshake import build_response, check_request -from .http import read_request -from .protocol import WebSocketCommonProtocol, broadcast - - -__all__ = [ - "broadcast", - "serve", - "unix_serve", - "WebSocketServerProtocol", - "WebSocketServer", -] - - -# Change to HeadersLike | ... when dropping Python < 3.10. -HeadersLikeOrCallable = Union[HeadersLike, Callable[[str, Headers], HeadersLike]] - -# Change to tuple[...] when dropping Python < 3.9. -HTTPResponse = Tuple[StatusLike, HeadersLike, bytes] - - -class WebSocketServerProtocol(WebSocketCommonProtocol): - """ - WebSocket server connection. - - :class:`WebSocketServerProtocol` provides :meth:`recv` and :meth:`send` - coroutines for receiving and sending messages. - - It supports asynchronous iteration to receive messages:: - - async for message in websocket: - await process(message) - - The iterator exits normally when the connection is closed with close code - 1000 (OK) or 1001 (going away) or without a close code. It raises - a :exc:`~websockets.exceptions.ConnectionClosedError` when the connection - is closed with any other code. - - You may customize the opening handshake in a subclass by - overriding :meth:`process_request` or :meth:`select_subprotocol`. - - Args: - ws_server: WebSocket server that created this connection. - - See :func:`serve` for the documentation of ``ws_handler``, ``logger``, ``origins``, - ``extensions``, ``subprotocols``, ``extra_headers``, and ``server_header``. - - See :class:`~websockets.legacy.protocol.WebSocketCommonProtocol` for the - documentation of ``ping_interval``, ``ping_timeout``, ``close_timeout``, - ``max_size``, ``max_queue``, ``read_limit``, and ``write_limit``. - - """ - - is_client = False - side = "server" - - def __init__( - self, - # The version that accepts the path in the second argument is deprecated. - ws_handler: ( - Callable[[WebSocketServerProtocol], Awaitable[Any]] - | Callable[[WebSocketServerProtocol, str], Awaitable[Any]] - ), - ws_server: WebSocketServer, - *, - logger: LoggerLike | None = None, - origins: Sequence[Origin | None] | None = None, - extensions: Sequence[ServerExtensionFactory] | None = None, - subprotocols: Sequence[Subprotocol] | None = None, - extra_headers: HeadersLikeOrCallable | None = None, - server_header: str | None = SERVER, - process_request: ( - Callable[[str, Headers], Awaitable[HTTPResponse | None]] | None - ) = None, - select_subprotocol: ( - Callable[[Sequence[Subprotocol], Sequence[Subprotocol]], Subprotocol] | None - ) = None, - open_timeout: float | None = 10, - **kwargs: Any, - ) -> None: - if logger is None: - logger = logging.getLogger("websockets.server") - super().__init__(logger=logger, **kwargs) - # For backwards compatibility with 6.0 or earlier. - if origins is not None and "" in origins: - warnings.warn("use None instead of '' in origins", DeprecationWarning) - origins = [None if origin == "" else origin for origin in origins] - # For backwards compatibility with 10.0 or earlier. Done here in - # addition to serve to trigger the deprecation warning on direct - # use of WebSocketServerProtocol. - self.ws_handler = remove_path_argument(ws_handler) - self.ws_server = ws_server - self.origins = origins - self.available_extensions = extensions - self.available_subprotocols = subprotocols - self.extra_headers = extra_headers - self.server_header = server_header - self._process_request = process_request - self._select_subprotocol = select_subprotocol - self.open_timeout = open_timeout - - def connection_made(self, transport: asyncio.BaseTransport) -> None: - """ - Register connection and initialize a task to handle it. - - """ - super().connection_made(transport) - # Register the connection with the server before creating the handler - # task. Registering at the beginning of the handler coroutine would - # create a race condition between the creation of the task, which - # schedules its execution, and the moment the handler starts running. - self.ws_server.register(self) - self.handler_task = self.loop.create_task(self.handler()) - - async def handler(self) -> None: - """ - Handle the lifecycle of a WebSocket connection. - - Since this method doesn't have a caller able to handle exceptions, it - attempts to log relevant ones and guarantees that the TCP connection is - closed before exiting. - - """ - try: - try: - async with asyncio_timeout(self.open_timeout): - await self.handshake( - origins=self.origins, - available_extensions=self.available_extensions, - available_subprotocols=self.available_subprotocols, - extra_headers=self.extra_headers, - ) - except asyncio.TimeoutError: # pragma: no cover - raise - except ConnectionError: - raise - except Exception as exc: - if isinstance(exc, AbortHandshake): - status, headers, body = exc.status, exc.headers, exc.body - elif isinstance(exc, InvalidOrigin): - if self.debug: - self.logger.debug("! invalid origin", exc_info=True) - status, headers, body = ( - http.HTTPStatus.FORBIDDEN, - Headers(), - f"Failed to open a WebSocket connection: {exc}.\n".encode(), - ) - elif isinstance(exc, InvalidUpgrade): - if self.debug: - self.logger.debug("! invalid upgrade", exc_info=True) - status, headers, body = ( - http.HTTPStatus.UPGRADE_REQUIRED, - Headers([("Upgrade", "websocket")]), - ( - f"Failed to open a WebSocket connection: {exc}.\n" - f"\n" - f"You cannot access a WebSocket server directly " - f"with a browser. You need a WebSocket client.\n" - ).encode(), - ) - elif isinstance(exc, InvalidHandshake): - if self.debug: - self.logger.debug("! invalid handshake", exc_info=True) - exc_chain = cast(BaseException, exc) - exc_str = f"{exc_chain}" - while exc_chain.__cause__ is not None: - exc_chain = exc_chain.__cause__ - exc_str += f"; {exc_chain}" - status, headers, body = ( - http.HTTPStatus.BAD_REQUEST, - Headers(), - f"Failed to open a WebSocket connection: {exc_str}.\n".encode(), - ) - else: - self.logger.error("opening handshake failed", exc_info=True) - status, headers, body = ( - http.HTTPStatus.INTERNAL_SERVER_ERROR, - Headers(), - ( - b"Failed to open a WebSocket connection.\n" - b"See server log for more information.\n" - ), - ) - - headers.setdefault("Date", email.utils.formatdate(usegmt=True)) - if self.server_header: - headers.setdefault("Server", self.server_header) - - headers.setdefault("Content-Length", str(len(body))) - headers.setdefault("Content-Type", "text/plain") - headers.setdefault("Connection", "close") - - self.write_http_response(status, headers, body) - self.logger.info( - "connection rejected (%d %s)", status.value, status.phrase - ) - await self.close_transport() - return - - try: - await self.ws_handler(self) - except Exception: - self.logger.error("connection handler failed", exc_info=True) - if not self.closed: - self.fail_connection(1011) - raise - - try: - await self.close() - except ConnectionError: - raise - except Exception: - self.logger.error("closing handshake failed", exc_info=True) - raise - - except Exception: - # Last-ditch attempt to avoid leaking connections on errors. - try: - self.transport.close() - except Exception: # pragma: no cover - pass - - finally: - # Unregister the connection with the server when the handler task - # terminates. Registration is tied to the lifecycle of the handler - # task because the server waits for tasks attached to registered - # connections before terminating. - self.ws_server.unregister(self) - self.logger.info("connection closed") - - async def read_http_request(self) -> tuple[str, Headers]: - """ - Read request line and headers from the HTTP request. - - If the request contains a body, it may be read from ``self.reader`` - after this coroutine returns. - - Raises: - InvalidMessage: If the HTTP message is malformed or isn't an - HTTP/1.1 GET request. - - """ - try: - path, headers = await read_request(self.reader) - except asyncio.CancelledError: # pragma: no cover - raise - except Exception as exc: - raise InvalidMessage("did not receive a valid HTTP request") from exc - - if self.debug: - self.logger.debug("< GET %s HTTP/1.1", path) - for key, value in headers.raw_items(): - self.logger.debug("< %s: %s", key, value) - - self.path = path - self.request_headers = headers - - return path, headers - - def write_http_response( - self, status: http.HTTPStatus, headers: Headers, body: bytes | None = None - ) -> None: - """ - Write status line and headers to the HTTP response. - - This coroutine is also able to write a response body. - - """ - self.response_headers = headers - - if self.debug: - self.logger.debug("> HTTP/1.1 %d %s", status.value, status.phrase) - for key, value in headers.raw_items(): - self.logger.debug("> %s: %s", key, value) - if body is not None: - self.logger.debug("> [body] (%d bytes)", len(body)) - - # Since the status line and headers only contain ASCII characters, - # we can keep this simple. - response = f"HTTP/1.1 {status.value} {status.phrase}\r\n" - response += str(headers) - - self.transport.write(response.encode()) - - if body is not None: - self.transport.write(body) - - async def process_request( - self, path: str, request_headers: Headers - ) -> HTTPResponse | None: - """ - Intercept the HTTP request and return an HTTP response if appropriate. - - You may override this method in a :class:`WebSocketServerProtocol` - subclass, for example: - - * to return an HTTP 200 OK response on a given path; then a load - balancer can use this path for a health check; - * to authenticate the request and return an HTTP 401 Unauthorized or an - HTTP 403 Forbidden when authentication fails. - - You may also override this method with the ``process_request`` - argument of :func:`serve` and :class:`WebSocketServerProtocol`. This - is equivalent, except ``process_request`` won't have access to the - protocol instance, so it can't store information for later use. - - :meth:`process_request` is expected to complete quickly. If it may run - for a long time, then it should await :meth:`wait_closed` and exit if - :meth:`wait_closed` completes, or else it could prevent the server - from shutting down. - - Args: - path: Request path, including optional query string. - request_headers: Request headers. - - Returns: - tuple[StatusLike, HeadersLike, bytes] | None: :obj:`None` to - continue the WebSocket handshake normally. - - An HTTP response, represented by a 3-uple of the response status, - headers, and body, to abort the WebSocket handshake and return - that HTTP response instead. - - """ - if self._process_request is not None: - response = self._process_request(path, request_headers) - if isinstance(response, Awaitable): - return await response - else: - # For backwards compatibility with 7.0. - warnings.warn( - "declare process_request as a coroutine", DeprecationWarning - ) - return response - return None - - @staticmethod - def process_origin( - headers: Headers, origins: Sequence[Origin | None] | None = None - ) -> Origin | None: - """ - Handle the Origin HTTP request header. - - Args: - headers: Request headers. - origins: Optional list of acceptable origins. - - Raises: - InvalidOrigin: If the origin isn't acceptable. - - """ - # "The user agent MUST NOT include more than one Origin header field" - # per https://datatracker.ietf.org/doc/html/rfc6454#section-7.3. - try: - origin = headers.get("Origin") - except MultipleValuesError as exc: - raise InvalidHeader("Origin", "multiple values") from exc - if origin is not None: - origin = cast(Origin, origin) - if origins is not None: - if origin not in origins: - raise InvalidOrigin(origin) - return origin - - @staticmethod - def process_extensions( - headers: Headers, - available_extensions: Sequence[ServerExtensionFactory] | None, - ) -> tuple[str | None, list[Extension]]: - """ - Handle the Sec-WebSocket-Extensions HTTP request header. - - Accept or reject each extension proposed in the client request. - Negotiate parameters for accepted extensions. - - Return the Sec-WebSocket-Extensions HTTP response header and the list - of accepted extensions. - - :rfc:`6455` leaves the rules up to the specification of each - :extension. - - To provide this level of flexibility, for each extension proposed by - the client, we check for a match with each extension available in the - server configuration. If no match is found, the extension is ignored. - - If several variants of the same extension are proposed by the client, - it may be accepted several times, which won't make sense in general. - Extensions must implement their own requirements. For this purpose, - the list of previously accepted extensions is provided. - - This process doesn't allow the server to reorder extensions. It can - only select a subset of the extensions proposed by the client. - - Other requirements, for example related to mandatory extensions or the - order of extensions, may be implemented by overriding this method. - - Args: - headers: Request headers. - extensions: Optional list of supported extensions. - - Raises: - InvalidHandshake: To abort the handshake with an HTTP 400 error. - - """ - response_header_value: str | None = None - - extension_headers: list[ExtensionHeader] = [] - accepted_extensions: list[Extension] = [] - - header_values = headers.get_all("Sec-WebSocket-Extensions") - - if header_values and available_extensions: - parsed_header_values: list[ExtensionHeader] = sum( - [parse_extension(header_value) for header_value in header_values], [] - ) - - for name, request_params in parsed_header_values: - for ext_factory in available_extensions: - # Skip non-matching extensions based on their name. - if ext_factory.name != name: - continue - - # Skip non-matching extensions based on their params. - try: - response_params, extension = ext_factory.process_request_params( - request_params, accepted_extensions - ) - except NegotiationError: - continue - - # Add matching extension to the final list. - extension_headers.append((name, response_params)) - accepted_extensions.append(extension) - - # Break out of the loop once we have a match. - break - - # If we didn't break from the loop, no extension in our list - # matched what the client sent. The extension is declined. - - # Serialize extension header. - if extension_headers: - response_header_value = build_extension(extension_headers) - - return response_header_value, accepted_extensions - - # Not @staticmethod because it calls self.select_subprotocol() - def process_subprotocol( - self, headers: Headers, available_subprotocols: Sequence[Subprotocol] | None - ) -> Subprotocol | None: - """ - Handle the Sec-WebSocket-Protocol HTTP request header. - - Return Sec-WebSocket-Protocol HTTP response header, which is the same - as the selected subprotocol. - - Args: - headers: Request headers. - available_subprotocols: Optional list of supported subprotocols. - - Raises: - InvalidHandshake: To abort the handshake with an HTTP 400 error. - - """ - subprotocol: Subprotocol | None = None - - header_values = headers.get_all("Sec-WebSocket-Protocol") - - if header_values and available_subprotocols: - parsed_header_values: list[Subprotocol] = sum( - [parse_subprotocol(header_value) for header_value in header_values], [] - ) - - subprotocol = self.select_subprotocol( - parsed_header_values, available_subprotocols - ) - - return subprotocol - - def select_subprotocol( - self, - client_subprotocols: Sequence[Subprotocol], - server_subprotocols: Sequence[Subprotocol], - ) -> Subprotocol | None: - """ - Pick a subprotocol among those supported by the client and the server. - - If several subprotocols are available, select the preferred subprotocol - by giving equal weight to the preferences of the client and the server. - - If no subprotocol is available, proceed without a subprotocol. - - You may provide a ``select_subprotocol`` argument to :func:`serve` or - :class:`WebSocketServerProtocol` to override this logic. For example, - you could reject the handshake if the client doesn't support a - particular subprotocol, rather than accept the handshake without that - subprotocol. - - Args: - client_subprotocols: List of subprotocols offered by the client. - server_subprotocols: List of subprotocols available on the server. - - Returns: - Selected subprotocol, if a common subprotocol was found. - - :obj:`None` to continue without a subprotocol. - - """ - if self._select_subprotocol is not None: - return self._select_subprotocol(client_subprotocols, server_subprotocols) - - subprotocols = set(client_subprotocols) & set(server_subprotocols) - if not subprotocols: - return None - return sorted( - subprotocols, - key=lambda p: client_subprotocols.index(p) + server_subprotocols.index(p), - )[0] - - async def handshake( - self, - origins: Sequence[Origin | None] | None = None, - available_extensions: Sequence[ServerExtensionFactory] | None = None, - available_subprotocols: Sequence[Subprotocol] | None = None, - extra_headers: HeadersLikeOrCallable | None = None, - ) -> str: - """ - Perform the server side of the opening handshake. - - Args: - origins: List of acceptable values of the Origin HTTP header; - include :obj:`None` if the lack of an origin is acceptable. - extensions: List of supported extensions, in order in which they - should be tried. - subprotocols: List of supported subprotocols, in order of - decreasing preference. - extra_headers: Arbitrary HTTP headers to add to the response when - the handshake succeeds. - - Returns: - path of the URI of the request. - - Raises: - InvalidHandshake: If the handshake fails. - - """ - path, request_headers = await self.read_http_request() - - # Hook for customizing request handling, for example checking - # authentication or treating some paths as plain HTTP endpoints. - early_response_awaitable = self.process_request(path, request_headers) - if isinstance(early_response_awaitable, Awaitable): - early_response = await early_response_awaitable - else: - # For backwards compatibility with 7.0. - warnings.warn("declare process_request as a coroutine", DeprecationWarning) - early_response = early_response_awaitable - - # The connection may drop while process_request is running. - if self.state is State.CLOSED: - # This subclass of ConnectionError is silently ignored in handler(). - raise BrokenPipeError("connection closed during opening handshake") - - # Change the response to a 503 error if the server is shutting down. - if not self.ws_server.is_serving(): - early_response = ( - http.HTTPStatus.SERVICE_UNAVAILABLE, - [], - b"Server is shutting down.\n", - ) - - if early_response is not None: - raise AbortHandshake(*early_response) - - key = check_request(request_headers) - - self.origin = self.process_origin(request_headers, origins) - - extensions_header, self.extensions = self.process_extensions( - request_headers, available_extensions - ) - - protocol_header = self.subprotocol = self.process_subprotocol( - request_headers, available_subprotocols - ) - - response_headers = Headers() - - build_response(response_headers, key) - - if extensions_header is not None: - response_headers["Sec-WebSocket-Extensions"] = extensions_header - - if protocol_header is not None: - response_headers["Sec-WebSocket-Protocol"] = protocol_header - - if callable(extra_headers): - extra_headers = extra_headers(path, self.request_headers) - if extra_headers is not None: - response_headers.update(extra_headers) - - response_headers.setdefault("Date", email.utils.formatdate(usegmt=True)) - if self.server_header is not None: - response_headers.setdefault("Server", self.server_header) - - self.write_http_response(http.HTTPStatus.SWITCHING_PROTOCOLS, response_headers) - - self.logger.info("connection open") - - self.connection_open() - - return path - - -class WebSocketServer: - """ - WebSocket server returned by :func:`serve`. - - This class mirrors the API of :class:`~asyncio.Server`. - - It keeps track of WebSocket connections in order to close them properly - when shutting down. - - Args: - logger: Logger for this server. - It defaults to ``logging.getLogger("websockets.server")``. - See the :doc:`logging guide <../../topics/logging>` for details. - - """ - - def __init__(self, logger: LoggerLike | None = None) -> None: - if logger is None: - logger = logging.getLogger("websockets.server") - self.logger = logger - - # Keep track of active connections. - self.websockets: set[WebSocketServerProtocol] = set() - - # Task responsible for closing the server and terminating connections. - self.close_task: asyncio.Task[None] | None = None - - # Completed when the server is closed and connections are terminated. - self.closed_waiter: asyncio.Future[None] - - def wrap(self, server: asyncio.base_events.Server) -> None: - """ - Attach to a given :class:`~asyncio.Server`. - - Since :meth:`~asyncio.loop.create_server` doesn't support injecting a - custom ``Server`` class, the easiest solution that doesn't rely on - private :mod:`asyncio` APIs is to: - - - instantiate a :class:`WebSocketServer` - - give the protocol factory a reference to that instance - - call :meth:`~asyncio.loop.create_server` with the factory - - attach the resulting :class:`~asyncio.Server` with this method - - """ - self.server = server - for sock in server.sockets: - if sock.family == socket.AF_INET: - name = "%s:%d" % sock.getsockname() - elif sock.family == socket.AF_INET6: - name = "[%s]:%d" % sock.getsockname()[:2] - elif sock.family == socket.AF_UNIX: - name = sock.getsockname() - # In the unlikely event that someone runs websockets over a - # protocol other than IP or Unix sockets, avoid crashing. - else: # pragma: no cover - name = str(sock.getsockname()) - self.logger.info("server listening on %s", name) - - # Initialized here because we need a reference to the event loop. - # This should be moved back to __init__ when dropping Python < 3.10. - self.closed_waiter = server.get_loop().create_future() - - def register(self, protocol: WebSocketServerProtocol) -> None: - """ - Register a connection with this server. - - """ - self.websockets.add(protocol) - - def unregister(self, protocol: WebSocketServerProtocol) -> None: - """ - Unregister a connection with this server. - - """ - self.websockets.remove(protocol) - - def close(self, close_connections: bool = True) -> None: - """ - Close the server. - - * Close the underlying :class:`~asyncio.Server`. - * When ``close_connections`` is :obj:`True`, which is the default, - close existing connections. Specifically: - - * Reject opening WebSocket connections with an HTTP 503 (service - unavailable) error. This happens when the server accepted the TCP - connection but didn't complete the opening handshake before closing. - * Close open WebSocket connections with close code 1001 (going away). - - * Wait until all connection handlers terminate. - - :meth:`close` is idempotent. - - """ - if self.close_task is None: - self.close_task = self.get_loop().create_task( - self._close(close_connections) - ) - - async def _close(self, close_connections: bool) -> None: - """ - Implementation of :meth:`close`. - - This calls :meth:`~asyncio.Server.close` on the underlying - :class:`~asyncio.Server` object to stop accepting new connections and - then closes open connections with close code 1001. - - """ - self.logger.info("server closing") - - # Stop accepting new connections. - self.server.close() - - # Wait until all accepted connections reach connection_made() and call - # register(). See https://github.com/python/cpython/issues/79033 for - # details. This workaround can be removed when dropping Python < 3.11. - await asyncio.sleep(0) - - if close_connections: - # Close OPEN connections with close code 1001. After server.close(), - # handshake() closes OPENING connections with an HTTP 503 error. - close_tasks = [ - asyncio.create_task(websocket.close(1001)) - for websocket in self.websockets - if websocket.state is not State.CONNECTING - ] - # asyncio.wait doesn't accept an empty first argument. - if close_tasks: - await asyncio.wait(close_tasks) - - # Wait until all TCP connections are closed. - await self.server.wait_closed() - - # Wait until all connection handlers terminate. - # asyncio.wait doesn't accept an empty first argument. - if self.websockets: - await asyncio.wait( - [websocket.handler_task for websocket in self.websockets] - ) - - # Tell wait_closed() to return. - self.closed_waiter.set_result(None) - - self.logger.info("server closed") - - async def wait_closed(self) -> None: - """ - Wait until the server is closed. - - When :meth:`wait_closed` returns, all TCP connections are closed and - all connection handlers have returned. - - To ensure a fast shutdown, a connection handler should always be - awaiting at least one of: - - * :meth:`~WebSocketServerProtocol.recv`: when the connection is closed, - it raises :exc:`~websockets.exceptions.ConnectionClosedOK`; - * :meth:`~WebSocketServerProtocol.wait_closed`: when the connection is - closed, it returns. - - Then the connection handler is immediately notified of the shutdown; - it can clean up and exit. - - """ - await asyncio.shield(self.closed_waiter) - - def get_loop(self) -> asyncio.AbstractEventLoop: - """ - See :meth:`asyncio.Server.get_loop`. - - """ - return self.server.get_loop() - - def is_serving(self) -> bool: - """ - See :meth:`asyncio.Server.is_serving`. - - """ - return self.server.is_serving() - - async def start_serving(self) -> None: # pragma: no cover - """ - See :meth:`asyncio.Server.start_serving`. - - Typical use:: - - server = await serve(..., start_serving=False) - # perform additional setup here... - # ... then start the server - await server.start_serving() - - """ - await self.server.start_serving() - - async def serve_forever(self) -> None: # pragma: no cover - """ - See :meth:`asyncio.Server.serve_forever`. - - Typical use:: - - server = await serve(...) - # this coroutine doesn't return - # canceling it stops the server - await server.serve_forever() - - This is an alternative to using :func:`serve` as an asynchronous context - manager. Shutdown is triggered by canceling :meth:`serve_forever` - instead of exiting a :func:`serve` context. - - """ - await self.server.serve_forever() - - @property - def sockets(self) -> Iterable[socket.socket]: - """ - See :attr:`asyncio.Server.sockets`. - - """ - return self.server.sockets - - async def __aenter__(self) -> WebSocketServer: # pragma: no cover - return self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - traceback: TracebackType | None, - ) -> None: # pragma: no cover - self.close() - await self.wait_closed() - - -class Serve: - """ - Start a WebSocket server listening on ``host`` and ``port``. - - Whenever a client connects, the server creates a - :class:`WebSocketServerProtocol`, performs the opening handshake, and - delegates to the connection handler, ``ws_handler``. - - The handler receives the :class:`WebSocketServerProtocol` and uses it to - send and receive messages. - - Once the handler completes, either normally or with an exception, the - server performs the closing handshake and closes the connection. - - Awaiting :func:`serve` yields a :class:`WebSocketServer`. This object - provides a :meth:`~WebSocketServer.close` method to shut down the server:: - - # set this future to exit the server - stop = asyncio.get_running_loop().create_future() - - server = await serve(...) - await stop - server.close() - await server.wait_closed() - - :func:`serve` can be used as an asynchronous context manager. Then, the - server is shut down automatically when exiting the context:: - - # set this future to exit the server - stop = asyncio.get_running_loop().create_future() - - async with serve(...): - await stop - - Args: - ws_handler: Connection handler. It receives the WebSocket connection, - which is a :class:`WebSocketServerProtocol`, in argument. - host: Network interfaces the server binds to. - See :meth:`~asyncio.loop.create_server` for details. - port: TCP port the server listens on. - See :meth:`~asyncio.loop.create_server` for details. - create_protocol: Factory for the :class:`asyncio.Protocol` managing - the connection. It defaults to :class:`WebSocketServerProtocol`. - Set it to a wrapper or a subclass to customize connection handling. - logger: Logger for this server. - It defaults to ``logging.getLogger("websockets.server")``. - See the :doc:`logging guide <../../topics/logging>` for details. - compression: The "permessage-deflate" extension is enabled by default. - Set ``compression`` to :obj:`None` to disable it. See the - :doc:`compression guide <../../topics/compression>` for details. - origins: Acceptable values of the ``Origin`` header, for defending - against Cross-Site WebSocket Hijacking attacks. Include :obj:`None` - in the list if the lack of an origin is acceptable. - extensions: List of supported extensions, in order in which they - should be negotiated and run. - subprotocols: List of supported subprotocols, in order of decreasing - preference. - extra_headers (HeadersLike | Callable[[str, Headers] | HeadersLike]): - Arbitrary HTTP headers to add to the response. This can be - a :data:`~websockets.datastructures.HeadersLike` or a callable - taking the request path and headers in arguments and returning - a :data:`~websockets.datastructures.HeadersLike`. - server_header: Value of the ``Server`` response header. - It defaults to ``"Python/x.y.z websockets/X.Y"``. - Setting it to :obj:`None` removes the header. - process_request (Callable[[str, Headers], \ - Awaitable[tuple[StatusLike, HeadersLike, bytes] | None]] | None): - Intercept HTTP request before the opening handshake. - See :meth:`~WebSocketServerProtocol.process_request` for details. - select_subprotocol: Select a subprotocol supported by the client. - See :meth:`~WebSocketServerProtocol.select_subprotocol` for details. - open_timeout: Timeout for opening connections in seconds. - :obj:`None` disables the timeout. - - See :class:`~websockets.legacy.protocol.WebSocketCommonProtocol` for the - documentation of ``ping_interval``, ``ping_timeout``, ``close_timeout``, - ``max_size``, ``max_queue``, ``read_limit``, and ``write_limit``. - - Any other keyword arguments are passed the event loop's - :meth:`~asyncio.loop.create_server` method. - - For example: - - * You can set ``ssl`` to a :class:`~ssl.SSLContext` to enable TLS. - - * You can set ``sock`` to a :obj:`~socket.socket` that you created - outside of websockets. - - Returns: - WebSocket server. - - """ - - def __init__( - self, - # The version that accepts the path in the second argument is deprecated. - ws_handler: ( - Callable[[WebSocketServerProtocol], Awaitable[Any]] - | Callable[[WebSocketServerProtocol, str], Awaitable[Any]] - ), - host: str | Sequence[str] | None = None, - port: int | None = None, - *, - create_protocol: Callable[..., WebSocketServerProtocol] | None = None, - logger: LoggerLike | None = None, - compression: str | None = "deflate", - origins: Sequence[Origin | None] | None = None, - extensions: Sequence[ServerExtensionFactory] | None = None, - subprotocols: Sequence[Subprotocol] | None = None, - extra_headers: HeadersLikeOrCallable | None = None, - server_header: str | None = SERVER, - process_request: ( - Callable[[str, Headers], Awaitable[HTTPResponse | None]] | None - ) = None, - select_subprotocol: ( - Callable[[Sequence[Subprotocol], Sequence[Subprotocol]], Subprotocol] | None - ) = None, - open_timeout: float | None = 10, - ping_interval: float | None = 20, - ping_timeout: float | None = 20, - close_timeout: float | None = None, - max_size: int | None = 2**20, - max_queue: int | None = 2**5, - read_limit: int = 2**16, - write_limit: int = 2**16, - **kwargs: Any, - ) -> None: - # Backwards compatibility: close_timeout used to be called timeout. - timeout: float | None = kwargs.pop("timeout", None) - if timeout is None: - timeout = 10 - else: - warnings.warn("rename timeout to close_timeout", DeprecationWarning) - # If both are specified, timeout is ignored. - if close_timeout is None: - close_timeout = timeout - - # Backwards compatibility: create_protocol used to be called klass. - klass: type[WebSocketServerProtocol] | None = kwargs.pop("klass", None) - if klass is None: - klass = WebSocketServerProtocol - else: - warnings.warn("rename klass to create_protocol", DeprecationWarning) - # If both are specified, klass is ignored. - if create_protocol is None: - create_protocol = klass - - # Backwards compatibility: recv() used to return None on closed connections - legacy_recv: bool = kwargs.pop("legacy_recv", False) - - # Backwards compatibility: the loop parameter used to be supported. - _loop: asyncio.AbstractEventLoop | None = kwargs.pop("loop", None) - if _loop is None: - loop = asyncio.get_event_loop() - else: - loop = _loop - warnings.warn("remove loop argument", DeprecationWarning) - - ws_server = WebSocketServer(logger=logger) - - secure = kwargs.get("ssl") is not None - - if compression == "deflate": - extensions = enable_server_permessage_deflate(extensions) - elif compression is not None: - raise ValueError(f"unsupported compression: {compression}") - - if subprotocols is not None: - validate_subprotocols(subprotocols) - - # Help mypy and avoid this error: "type[WebSocketServerProtocol] | - # Callable[..., WebSocketServerProtocol]" not callable [misc] - create_protocol = cast(Callable[..., WebSocketServerProtocol], create_protocol) - factory = functools.partial( - create_protocol, - # For backwards compatibility with 10.0 or earlier. Done here in - # addition to WebSocketServerProtocol to trigger the deprecation - # warning once per serve() call rather than once per connection. - remove_path_argument(ws_handler), - ws_server, - host=host, - port=port, - secure=secure, - open_timeout=open_timeout, - ping_interval=ping_interval, - ping_timeout=ping_timeout, - close_timeout=close_timeout, - max_size=max_size, - max_queue=max_queue, - read_limit=read_limit, - write_limit=write_limit, - loop=_loop, - legacy_recv=legacy_recv, - origins=origins, - extensions=extensions, - subprotocols=subprotocols, - extra_headers=extra_headers, - server_header=server_header, - process_request=process_request, - select_subprotocol=select_subprotocol, - logger=logger, - ) - - if kwargs.pop("unix", False): - path: str | None = kwargs.pop("path", None) - # unix_serve(path) must not specify host and port parameters. - assert host is None and port is None - create_server = functools.partial( - loop.create_unix_server, factory, path, **kwargs - ) - else: - create_server = functools.partial( - loop.create_server, factory, host, port, **kwargs - ) - - # This is a coroutine function. - self._create_server = create_server - self.ws_server = ws_server - - # async with serve(...) - - async def __aenter__(self) -> WebSocketServer: - return await self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - traceback: TracebackType | None, - ) -> None: - self.ws_server.close() - await self.ws_server.wait_closed() - - # await serve(...) - - def __await__(self) -> Generator[Any, None, WebSocketServer]: - # Create a suitable iterator by calling __await__ on a coroutine. - return self.__await_impl__().__await__() - - async def __await_impl__(self) -> WebSocketServer: - server = await self._create_server() - self.ws_server.wrap(server) - return self.ws_server - - # yield from serve(...) - remove when dropping Python < 3.10 - - __iter__ = __await__ - - -serve = Serve - - -def unix_serve( - # The version that accepts the path in the second argument is deprecated. - ws_handler: ( - Callable[[WebSocketServerProtocol], Awaitable[Any]] - | Callable[[WebSocketServerProtocol, str], Awaitable[Any]] - ), - path: str | None = None, - **kwargs: Any, -) -> Serve: - """ - Start a WebSocket server listening on a Unix socket. - - This function is identical to :func:`serve`, except the ``host`` and - ``port`` arguments are replaced by ``path``. It is only available on Unix. - - Unrecognized keyword arguments are passed the event loop's - :meth:`~asyncio.loop.create_unix_server` method. - - It's useful for deploying a server behind a reverse proxy such as nginx. - - Args: - path: File system path to the Unix socket. - - """ - return serve(ws_handler, path=path, unix=True, **kwargs) - - -def remove_path_argument( - ws_handler: ( - Callable[[WebSocketServerProtocol], Awaitable[Any]] - | Callable[[WebSocketServerProtocol, str], Awaitable[Any]] - ), -) -> Callable[[WebSocketServerProtocol], Awaitable[Any]]: - try: - inspect.signature(ws_handler).bind(None) - except TypeError: - try: - inspect.signature(ws_handler).bind(None, "") - except TypeError: # pragma: no cover - # ws_handler accepts neither one nor two arguments; leave it alone. - pass - else: - # ws_handler accepts two arguments; activate backwards compatibility. - warnings.warn("remove second argument of ws_handler", DeprecationWarning) - - async def _ws_handler(websocket: WebSocketServerProtocol) -> Any: - return await cast( - Callable[[WebSocketServerProtocol, str], Awaitable[Any]], - ws_handler, - )(websocket, websocket.path) - - return _ws_handler - - return cast( - Callable[[WebSocketServerProtocol], Awaitable[Any]], - ws_handler, - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/protocol.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/protocol.py deleted file mode 100644 index 8751ebd..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/protocol.py +++ /dev/null @@ -1,732 +0,0 @@ -from __future__ import annotations - -import enum -import logging -import uuid -from typing import Generator, Union - -from .exceptions import ( - ConnectionClosed, - ConnectionClosedError, - ConnectionClosedOK, - InvalidState, - PayloadTooBig, - ProtocolError, -) -from .extensions import Extension -from .frames import ( - OK_CLOSE_CODES, - OP_BINARY, - OP_CLOSE, - OP_CONT, - OP_PING, - OP_PONG, - OP_TEXT, - Close, - CloseCode, - Frame, -) -from .http11 import Request, Response -from .streams import StreamReader -from .typing import LoggerLike, Origin, Subprotocol - - -__all__ = [ - "Protocol", - "Side", - "State", - "SEND_EOF", -] - -# Change to Request | Response | Frame when dropping Python < 3.10. -Event = Union[Request, Response, Frame] -"""Events that :meth:`~Protocol.events_received` may return.""" - - -class Side(enum.IntEnum): - """A WebSocket connection is either a server or a client.""" - - SERVER, CLIENT = range(2) - - -SERVER = Side.SERVER -CLIENT = Side.CLIENT - - -class State(enum.IntEnum): - """A WebSocket connection is in one of these four states.""" - - CONNECTING, OPEN, CLOSING, CLOSED = range(4) - - -CONNECTING = State.CONNECTING -OPEN = State.OPEN -CLOSING = State.CLOSING -CLOSED = State.CLOSED - - -SEND_EOF = b"" -"""Sentinel signaling that the TCP connection must be half-closed.""" - - -class Protocol: - """ - Sans-I/O implementation of a WebSocket connection. - - Args: - side: :attr:`~Side.CLIENT` or :attr:`~Side.SERVER`. - state: Initial state of the WebSocket connection. - max_size: Maximum size of incoming messages in bytes; - :obj:`None` disables the limit. - logger: Logger for this connection; depending on ``side``, - defaults to ``logging.getLogger("websockets.client")`` - or ``logging.getLogger("websockets.server")``; - see the :doc:`logging guide <../../topics/logging>` for details. - - """ - - def __init__( - self, - side: Side, - *, - state: State = OPEN, - max_size: int | None = 2**20, - logger: LoggerLike | None = None, - ) -> None: - # Unique identifier. For logs. - self.id: uuid.UUID = uuid.uuid4() - """Unique identifier of the connection. Useful in logs.""" - - # Logger or LoggerAdapter for this connection. - if logger is None: - logger = logging.getLogger(f"websockets.{side.name.lower()}") - self.logger: LoggerLike = logger - """Logger for this connection.""" - - # Track if DEBUG is enabled. Shortcut logging calls if it isn't. - self.debug = logger.isEnabledFor(logging.DEBUG) - - # Connection side. CLIENT or SERVER. - self.side = side - - # Connection state. Initially OPEN because subclasses handle CONNECTING. - self.state = state - - # Maximum size of incoming messages in bytes. - self.max_size = max_size - - # Current size of incoming message in bytes. Only set while reading a - # fragmented message i.e. a data frames with the FIN bit not set. - self.cur_size: int | None = None - - # True while sending a fragmented message i.e. a data frames with the - # FIN bit not set. - self.expect_continuation_frame = False - - # WebSocket protocol parameters. - self.origin: Origin | None = None - self.extensions: list[Extension] = [] - self.subprotocol: Subprotocol | None = None - - # Close code and reason, set when a close frame is sent or received. - self.close_rcvd: Close | None = None - self.close_sent: Close | None = None - self.close_rcvd_then_sent: bool | None = None - - # Track if an exception happened during the handshake. - self.handshake_exc: Exception | None = None - """ - Exception to raise if the opening handshake failed. - - :obj:`None` if the opening handshake succeeded. - - """ - - # Track if send_eof() was called. - self.eof_sent = False - - # Parser state. - self.reader = StreamReader() - self.events: list[Event] = [] - self.writes: list[bytes] = [] - self.parser = self.parse() - next(self.parser) # start coroutine - self.parser_exc: Exception | None = None - - @property - def state(self) -> State: - """ - State of the WebSocket connection. - - Defined in 4.1, 4.2, 7.1.3, and 7.1.4 of :rfc:`6455`. - - """ - return self._state - - @state.setter - def state(self, state: State) -> None: - if self.debug: - self.logger.debug("= connection is %s", state.name) - self._state = state - - @property - def close_code(self) -> int | None: - """ - `WebSocket close code`_. - - .. _WebSocket close code: - https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.5 - - :obj:`None` if the connection isn't closed yet. - - """ - if self.state is not CLOSED: - return None - elif self.close_rcvd is None: - return CloseCode.ABNORMAL_CLOSURE - else: - return self.close_rcvd.code - - @property - def close_reason(self) -> str | None: - """ - `WebSocket close reason`_. - - .. _WebSocket close reason: - https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.6 - - :obj:`None` if the connection isn't closed yet. - - """ - if self.state is not CLOSED: - return None - elif self.close_rcvd is None: - return "" - else: - return self.close_rcvd.reason - - @property - def close_exc(self) -> ConnectionClosed: - """ - Exception to raise when trying to interact with a closed connection. - - Don't raise this exception while the connection :attr:`state` - is :attr:`~websockets.protocol.State.CLOSING`; wait until - it's :attr:`~websockets.protocol.State.CLOSED`. - - Indeed, the exception includes the close code and reason, which are - known only once the connection is closed. - - Raises: - AssertionError: If the connection isn't closed yet. - - """ - assert self.state is CLOSED, "connection isn't closed yet" - exc_type: type[ConnectionClosed] - if ( - self.close_rcvd is not None - and self.close_sent is not None - and self.close_rcvd.code in OK_CLOSE_CODES - and self.close_sent.code in OK_CLOSE_CODES - ): - exc_type = ConnectionClosedOK - else: - exc_type = ConnectionClosedError - exc: ConnectionClosed = exc_type( - self.close_rcvd, - self.close_sent, - self.close_rcvd_then_sent, - ) - # Chain to the exception raised in the parser, if any. - exc.__cause__ = self.parser_exc - return exc - - # Public methods for receiving data. - - def receive_data(self, data: bytes) -> None: - """ - Receive data from the network. - - After calling this method: - - - You must call :meth:`data_to_send` and send this data to the network. - - You should call :meth:`events_received` and process resulting events. - - Raises: - EOFError: If :meth:`receive_eof` was called earlier. - - """ - self.reader.feed_data(data) - next(self.parser) - - def receive_eof(self) -> None: - """ - Receive the end of the data stream from the network. - - After calling this method: - - - You must call :meth:`data_to_send` and send this data to the network; - it will return ``[b""]``, signaling the end of the stream, or ``[]``. - - You aren't expected to call :meth:`events_received`; it won't return - any new events. - - :meth:`receive_eof` is idempotent. - - """ - if self.reader.eof: - return - self.reader.feed_eof() - next(self.parser) - - # Public methods for sending events. - - def send_continuation(self, data: bytes, fin: bool) -> None: - """ - Send a `Continuation frame`_. - - .. _Continuation frame: - https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - - Parameters: - data: payload containing the same kind of data - as the initial frame. - fin: FIN bit; set it to :obj:`True` if this is the last frame - of a fragmented message and to :obj:`False` otherwise. - - Raises: - ProtocolError: If a fragmented message isn't in progress. - - """ - if not self.expect_continuation_frame: - raise ProtocolError("unexpected continuation frame") - if self._state is not OPEN: - raise InvalidState(f"connection is {self.state.name.lower()}") - self.expect_continuation_frame = not fin - self.send_frame(Frame(OP_CONT, data, fin)) - - def send_text(self, data: bytes, fin: bool = True) -> None: - """ - Send a `Text frame`_. - - .. _Text frame: - https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - - Parameters: - data: payload containing text encoded with UTF-8. - fin: FIN bit; set it to :obj:`False` if this is the first frame of - a fragmented message. - - Raises: - ProtocolError: If a fragmented message is in progress. - - """ - if self.expect_continuation_frame: - raise ProtocolError("expected a continuation frame") - if self._state is not OPEN: - raise InvalidState(f"connection is {self.state.name.lower()}") - self.expect_continuation_frame = not fin - self.send_frame(Frame(OP_TEXT, data, fin)) - - def send_binary(self, data: bytes, fin: bool = True) -> None: - """ - Send a `Binary frame`_. - - .. _Binary frame: - https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - - Parameters: - data: payload containing arbitrary binary data. - fin: FIN bit; set it to :obj:`False` if this is the first frame of - a fragmented message. - - Raises: - ProtocolError: If a fragmented message is in progress. - - """ - if self.expect_continuation_frame: - raise ProtocolError("expected a continuation frame") - if self._state is not OPEN: - raise InvalidState(f"connection is {self.state.name.lower()}") - self.expect_continuation_frame = not fin - self.send_frame(Frame(OP_BINARY, data, fin)) - - def send_close(self, code: int | None = None, reason: str = "") -> None: - """ - Send a `Close frame`_. - - .. _Close frame: - https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.1 - - Parameters: - code: close code. - reason: close reason. - - Raises: - ProtocolError: If the code isn't valid or if a reason is provided - without a code. - - """ - # While RFC 6455 doesn't rule out sending more than one close Frame, - # websockets is conservative in what it sends and doesn't allow that. - if self._state is not OPEN: - raise InvalidState(f"connection is {self.state.name.lower()}") - if code is None: - if reason != "": - raise ProtocolError("cannot send a reason without a code") - close = Close(CloseCode.NO_STATUS_RCVD, "") - data = b"" - else: - close = Close(code, reason) - data = close.serialize() - # 7.1.3. The WebSocket Closing Handshake is Started - self.send_frame(Frame(OP_CLOSE, data)) - # Since the state is OPEN, no close frame was received yet. - # As a consequence, self.close_rcvd_then_sent remains None. - assert self.close_rcvd is None - self.close_sent = close - self.state = CLOSING - - def send_ping(self, data: bytes) -> None: - """ - Send a `Ping frame`_. - - .. _Ping frame: - https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.2 - - Parameters: - data: payload containing arbitrary binary data. - - """ - # RFC 6455 allows control frames after starting the closing handshake. - if self._state is not OPEN and self._state is not CLOSING: - raise InvalidState(f"connection is {self.state.name.lower()}") - self.send_frame(Frame(OP_PING, data)) - - def send_pong(self, data: bytes) -> None: - """ - Send a `Pong frame`_. - - .. _Pong frame: - https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.3 - - Parameters: - data: payload containing arbitrary binary data. - - """ - # RFC 6455 allows control frames after starting the closing handshake. - if self._state is not OPEN and self._state is not CLOSING: - raise InvalidState(f"connection is {self.state.name.lower()}") - self.send_frame(Frame(OP_PONG, data)) - - def fail(self, code: int, reason: str = "") -> None: - """ - `Fail the WebSocket connection`_. - - .. _Fail the WebSocket connection: - https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.7 - - Parameters: - code: close code - reason: close reason - - Raises: - ProtocolError: If the code isn't valid. - """ - # 7.1.7. Fail the WebSocket Connection - - # Send a close frame when the state is OPEN (a close frame was already - # sent if it's CLOSING), except when failing the connection because - # of an error reading from or writing to the network. - if self.state is OPEN: - if code != CloseCode.ABNORMAL_CLOSURE: - close = Close(code, reason) - data = close.serialize() - self.send_frame(Frame(OP_CLOSE, data)) - self.close_sent = close - # If recv_messages() raised an exception upon receiving a close - # frame but before echoing it, then close_rcvd is not None even - # though the state is OPEN. This happens when the connection is - # closed while receiving a fragmented message. - if self.close_rcvd is not None: - self.close_rcvd_then_sent = True - self.state = CLOSING - - # When failing the connection, a server closes the TCP connection - # without waiting for the client to complete the handshake, while a - # client waits for the server to close the TCP connection, possibly - # after sending a close frame that the client will ignore. - if self.side is SERVER and not self.eof_sent: - self.send_eof() - - # 7.1.7. Fail the WebSocket Connection "An endpoint MUST NOT continue - # to attempt to process data(including a responding Close frame) from - # the remote endpoint after being instructed to _Fail the WebSocket - # Connection_." - self.parser = self.discard() - next(self.parser) # start coroutine - - # Public method for getting incoming events after receiving data. - - def events_received(self) -> list[Event]: - """ - Fetch events generated from data received from the network. - - Call this method immediately after any of the ``receive_*()`` methods. - - Process resulting events, likely by passing them to the application. - - Returns: - Events read from the connection. - """ - events, self.events = self.events, [] - return events - - # Public method for getting outgoing data after receiving data or sending events. - - def data_to_send(self) -> list[bytes]: - """ - Obtain data to send to the network. - - Call this method immediately after any of the ``receive_*()``, - ``send_*()``, or :meth:`fail` methods. - - Write resulting data to the connection. - - The empty bytestring :data:`~websockets.protocol.SEND_EOF` signals - the end of the data stream. When you receive it, half-close the TCP - connection. - - Returns: - Data to write to the connection. - - """ - writes, self.writes = self.writes, [] - return writes - - def close_expected(self) -> bool: - """ - Tell if the TCP connection is expected to close soon. - - Call this method immediately after any of the ``receive_*()``, - ``send_close()``, or :meth:`fail` methods. - - If it returns :obj:`True`, schedule closing the TCP connection after a - short timeout if the other side hasn't already closed it. - - Returns: - Whether the TCP connection is expected to close soon. - - """ - # We expect a TCP close if and only if we sent a close frame: - # * Normal closure: once we send a close frame, we expect a TCP close: - # server waits for client to complete the TCP closing handshake; - # client waits for server to initiate the TCP closing handshake. - # * Abnormal closure: we always send a close frame and the same logic - # applies, except on EOFError where we don't send a close frame - # because we already received the TCP close, so we don't expect it. - # We already got a TCP Close if and only if the state is CLOSED. - return self.state is CLOSING or self.handshake_exc is not None - - # Private methods for receiving data. - - def parse(self) -> Generator[None, None, None]: - """ - Parse incoming data into frames. - - :meth:`receive_data` and :meth:`receive_eof` run this generator - coroutine until it needs more data or reaches EOF. - - :meth:`parse` never raises an exception. Instead, it sets the - :attr:`parser_exc` and yields control. - - """ - try: - while True: - if (yield from self.reader.at_eof()): - if self.debug: - self.logger.debug("< EOF") - # If the WebSocket connection is closed cleanly, with a - # closing handhshake, recv_frame() substitutes parse() - # with discard(). This branch is reached only when the - # connection isn't closed cleanly. - raise EOFError("unexpected end of stream") - - if self.max_size is None: - max_size = None - elif self.cur_size is None: - max_size = self.max_size - else: - max_size = self.max_size - self.cur_size - - # During a normal closure, execution ends here on the next - # iteration of the loop after receiving a close frame. At - # this point, recv_frame() replaced parse() by discard(). - frame = yield from Frame.parse( - self.reader.read_exact, - mask=self.side is SERVER, - max_size=max_size, - extensions=self.extensions, - ) - - if self.debug: - self.logger.debug("< %s", frame) - - self.recv_frame(frame) - - except ProtocolError as exc: - self.fail(CloseCode.PROTOCOL_ERROR, str(exc)) - self.parser_exc = exc - - except EOFError as exc: - self.fail(CloseCode.ABNORMAL_CLOSURE, str(exc)) - self.parser_exc = exc - - except UnicodeDecodeError as exc: - self.fail(CloseCode.INVALID_DATA, f"{exc.reason} at position {exc.start}") - self.parser_exc = exc - - except PayloadTooBig as exc: - self.fail(CloseCode.MESSAGE_TOO_BIG, str(exc)) - self.parser_exc = exc - - except Exception as exc: - self.logger.error("parser failed", exc_info=True) - # Don't include exception details, which may be security-sensitive. - self.fail(CloseCode.INTERNAL_ERROR) - self.parser_exc = exc - - # During an abnormal closure, execution ends here after catching an - # exception. At this point, fail() replaced parse() by discard(). - yield - raise AssertionError("parse() shouldn't step after error") - - def discard(self) -> Generator[None, None, None]: - """ - Discard incoming data. - - This coroutine replaces :meth:`parse`: - - - after receiving a close frame, during a normal closure (1.4); - - after sending a close frame, during an abnormal closure (7.1.7). - - """ - # After the opening handshake completes, the server closes the TCP - # connection in the same circumstances where discard() replaces parse(). - # The client closes it when it receives EOF from the server or times - # out. (The latter case cannot be handled in this Sans-I/O layer.) - assert (self.state == CONNECTING or self.side is SERVER) == (self.eof_sent) - while not (yield from self.reader.at_eof()): - self.reader.discard() - if self.debug: - self.logger.debug("< EOF") - # A server closes the TCP connection immediately, while a client - # waits for the server to close the TCP connection. - if self.state != CONNECTING and self.side is CLIENT: - self.send_eof() - self.state = CLOSED - # If discard() completes normally, execution ends here. - yield - # Once the reader reaches EOF, its feed_data/eof() methods raise an - # error, so our receive_data/eof() methods don't step the generator. - raise AssertionError("discard() shouldn't step after EOF") - - def recv_frame(self, frame: Frame) -> None: - """ - Process an incoming frame. - - """ - if frame.opcode is OP_TEXT or frame.opcode is OP_BINARY: - if self.cur_size is not None: - raise ProtocolError("expected a continuation frame") - if frame.fin: - self.cur_size = None - else: - self.cur_size = len(frame.data) - - elif frame.opcode is OP_CONT: - if self.cur_size is None: - raise ProtocolError("unexpected continuation frame") - if frame.fin: - self.cur_size = None - else: - self.cur_size += len(frame.data) - - elif frame.opcode is OP_PING: - # 5.5.2. Ping: "Upon receipt of a Ping frame, an endpoint MUST - # send a Pong frame in response" - pong_frame = Frame(OP_PONG, frame.data) - self.send_frame(pong_frame) - - elif frame.opcode is OP_PONG: - # 5.5.3 Pong: "A response to an unsolicited Pong frame is not - # expected." - pass - - elif frame.opcode is OP_CLOSE: - # 7.1.5. The WebSocket Connection Close Code - # 7.1.6. The WebSocket Connection Close Reason - self.close_rcvd = Close.parse(frame.data) - if self.state is CLOSING: - assert self.close_sent is not None - self.close_rcvd_then_sent = False - - if self.cur_size is not None: - raise ProtocolError("incomplete fragmented message") - - # 5.5.1 Close: "If an endpoint receives a Close frame and did - # not previously send a Close frame, the endpoint MUST send a - # Close frame in response. (When sending a Close frame in - # response, the endpoint typically echos the status code it - # received.)" - - if self.state is OPEN: - # Echo the original data instead of re-serializing it with - # Close.serialize() because that fails when the close frame - # is empty and Close.parse() synthesizes a 1005 close code. - # The rest is identical to send_close(). - self.send_frame(Frame(OP_CLOSE, frame.data)) - self.close_sent = self.close_rcvd - self.close_rcvd_then_sent = True - self.state = CLOSING - - # 7.1.2. Start the WebSocket Closing Handshake: "Once an - # endpoint has both sent and received a Close control frame, - # that endpoint SHOULD _Close the WebSocket Connection_" - - # A server closes the TCP connection immediately, while a client - # waits for the server to close the TCP connection. - if self.side is SERVER: - self.send_eof() - - # 1.4. Closing Handshake: "after receiving a control frame - # indicating the connection should be closed, a peer discards - # any further data received." - # RFC 6455 allows reading Ping and Pong frames after a Close frame. - # However, that doesn't seem useful; websockets doesn't support it. - self.parser = self.discard() - next(self.parser) # start coroutine - - else: - # This can't happen because Frame.parse() validates opcodes. - raise AssertionError(f"unexpected opcode: {frame.opcode:02x}") - - self.events.append(frame) - - # Private methods for sending events. - - def send_frame(self, frame: Frame) -> None: - if self.debug: - self.logger.debug("> %s", frame) - self.writes.append( - frame.serialize( - mask=self.side is CLIENT, - extensions=self.extensions, - ) - ) - - def send_eof(self) -> None: - assert not self.eof_sent - self.eof_sent = True - if self.debug: - self.logger.debug("> EOF") - self.writes.append(SEND_EOF) diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/py.typed b/plotter-app/venv/lib/python3.8/site-packages/websockets/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/server.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/server.py deleted file mode 100644 index 006d5bd..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/server.py +++ /dev/null @@ -1,587 +0,0 @@ -from __future__ import annotations - -import base64 -import binascii -import email.utils -import http -import warnings -from typing import Any, Callable, Generator, Sequence, cast - -from .datastructures import Headers, MultipleValuesError -from .exceptions import ( - InvalidHandshake, - InvalidHeader, - InvalidHeaderValue, - InvalidOrigin, - InvalidStatus, - InvalidUpgrade, - NegotiationError, -) -from .extensions import Extension, ServerExtensionFactory -from .headers import ( - build_extension, - parse_connection, - parse_extension, - parse_subprotocol, - parse_upgrade, -) -from .http11 import Request, Response -from .protocol import CONNECTING, OPEN, SERVER, Protocol, State -from .typing import ( - ConnectionOption, - ExtensionHeader, - LoggerLike, - Origin, - StatusLike, - Subprotocol, - UpgradeProtocol, -) -from .utils import accept_key - - -# See #940 for why lazy_import isn't used here for backwards compatibility. -# See #1400 for why listing compatibility imports in __all__ helps PyCharm. -from .legacy.server import * # isort:skip # noqa: I001 -from .legacy.server import __all__ as legacy__all__ - - -__all__ = ["ServerProtocol"] + legacy__all__ - - -class ServerProtocol(Protocol): - """ - Sans-I/O implementation of a WebSocket server connection. - - Args: - origins: Acceptable values of the ``Origin`` header; include - :obj:`None` in the list if the lack of an origin is acceptable. - This is useful for defending against Cross-Site WebSocket - Hijacking attacks. - extensions: List of supported extensions, in order in which they - should be tried. - subprotocols: List of supported subprotocols, in order of decreasing - preference. - select_subprotocol: Callback for selecting a subprotocol among - those supported by the client and the server. It has the same - signature as the :meth:`select_subprotocol` method, including a - :class:`ServerProtocol` instance as first argument. - state: Initial state of the WebSocket connection. - max_size: Maximum size of incoming messages in bytes; - :obj:`None` disables the limit. - logger: Logger for this connection; - defaults to ``logging.getLogger("websockets.server")``; - see the :doc:`logging guide <../../topics/logging>` for details. - - """ - - def __init__( - self, - *, - origins: Sequence[Origin | None] | None = None, - extensions: Sequence[ServerExtensionFactory] | None = None, - subprotocols: Sequence[Subprotocol] | None = None, - select_subprotocol: ( - Callable[ - [ServerProtocol, Sequence[Subprotocol]], - Subprotocol | None, - ] - | None - ) = None, - state: State = CONNECTING, - max_size: int | None = 2**20, - logger: LoggerLike | None = None, - ) -> None: - super().__init__( - side=SERVER, - state=state, - max_size=max_size, - logger=logger, - ) - self.origins = origins - self.available_extensions = extensions - self.available_subprotocols = subprotocols - if select_subprotocol is not None: - # Bind select_subprotocol then shadow self.select_subprotocol. - # Use setattr to work around https://github.com/python/mypy/issues/2427. - setattr( - self, - "select_subprotocol", - select_subprotocol.__get__(self, self.__class__), - ) - - def accept(self, request: Request) -> Response: - """ - Create a handshake response to accept the connection. - - If the handshake request is valid and the handshake successful, - :meth:`accept` returns an HTTP response with status code 101. - - Else, it returns an HTTP response with another status code. This rejects - the connection, like :meth:`reject` would. - - You must send the handshake response with :meth:`send_response`. - - You may modify the response before sending it, typically by adding HTTP - headers. - - Args: - request: WebSocket handshake request received from the client. - - Returns: - WebSocket handshake response or HTTP response to send to the client. - - """ - try: - ( - accept_header, - extensions_header, - protocol_header, - ) = self.process_request(request) - except InvalidOrigin as exc: - request._exception = exc - self.handshake_exc = exc - if self.debug: - self.logger.debug("! invalid origin", exc_info=True) - return self.reject( - http.HTTPStatus.FORBIDDEN, - f"Failed to open a WebSocket connection: {exc}.\n", - ) - except InvalidUpgrade as exc: - request._exception = exc - self.handshake_exc = exc - if self.debug: - self.logger.debug("! invalid upgrade", exc_info=True) - response = self.reject( - http.HTTPStatus.UPGRADE_REQUIRED, - ( - f"Failed to open a WebSocket connection: {exc}.\n" - f"\n" - f"You cannot access a WebSocket server directly " - f"with a browser. You need a WebSocket client.\n" - ), - ) - response.headers["Upgrade"] = "websocket" - return response - except InvalidHandshake as exc: - request._exception = exc - self.handshake_exc = exc - if self.debug: - self.logger.debug("! invalid handshake", exc_info=True) - exc_chain = cast(BaseException, exc) - exc_str = f"{exc_chain}" - while exc_chain.__cause__ is not None: - exc_chain = exc_chain.__cause__ - exc_str += f"; {exc_chain}" - return self.reject( - http.HTTPStatus.BAD_REQUEST, - f"Failed to open a WebSocket connection: {exc_str}.\n", - ) - except Exception as exc: - # Handle exceptions raised by user-provided select_subprotocol and - # unexpected errors. - request._exception = exc - self.handshake_exc = exc - self.logger.error("opening handshake failed", exc_info=True) - return self.reject( - http.HTTPStatus.INTERNAL_SERVER_ERROR, - ( - "Failed to open a WebSocket connection.\n" - "See server log for more information.\n" - ), - ) - - headers = Headers() - - headers["Date"] = email.utils.formatdate(usegmt=True) - - headers["Upgrade"] = "websocket" - headers["Connection"] = "Upgrade" - headers["Sec-WebSocket-Accept"] = accept_header - - if extensions_header is not None: - headers["Sec-WebSocket-Extensions"] = extensions_header - - if protocol_header is not None: - headers["Sec-WebSocket-Protocol"] = protocol_header - - return Response(101, "Switching Protocols", headers) - - def process_request( - self, - request: Request, - ) -> tuple[str, str | None, str | None]: - """ - Check a handshake request and negotiate extensions and subprotocol. - - This function doesn't verify that the request is an HTTP/1.1 or higher - GET request and doesn't check the ``Host`` header. These controls are - usually performed earlier in the HTTP request handling code. They're - the responsibility of the caller. - - Args: - request: WebSocket handshake request received from the client. - - Returns: - ``Sec-WebSocket-Accept``, ``Sec-WebSocket-Extensions``, and - ``Sec-WebSocket-Protocol`` headers for the handshake response. - - Raises: - InvalidHandshake: If the handshake request is invalid; - then the server must return 400 Bad Request error. - - """ - headers = request.headers - - connection: list[ConnectionOption] = sum( - [parse_connection(value) for value in headers.get_all("Connection")], [] - ) - - if not any(value.lower() == "upgrade" for value in connection): - raise InvalidUpgrade( - "Connection", ", ".join(connection) if connection else None - ) - - upgrade: list[UpgradeProtocol] = sum( - [parse_upgrade(value) for value in headers.get_all("Upgrade")], [] - ) - - # For compatibility with non-strict implementations, ignore case when - # checking the Upgrade header. The RFC always uses "websocket", except - # in section 11.2. (IANA registration) where it uses "WebSocket". - if not (len(upgrade) == 1 and upgrade[0].lower() == "websocket"): - raise InvalidUpgrade("Upgrade", ", ".join(upgrade) if upgrade else None) - - try: - key = headers["Sec-WebSocket-Key"] - except KeyError: - raise InvalidHeader("Sec-WebSocket-Key") from None - except MultipleValuesError: - raise InvalidHeader("Sec-WebSocket-Key", "multiple values") from None - - try: - raw_key = base64.b64decode(key.encode(), validate=True) - except binascii.Error as exc: - raise InvalidHeaderValue("Sec-WebSocket-Key", key) from exc - if len(raw_key) != 16: - raise InvalidHeaderValue("Sec-WebSocket-Key", key) - - try: - version = headers["Sec-WebSocket-Version"] - except KeyError: - raise InvalidHeader("Sec-WebSocket-Version") from None - except MultipleValuesError: - raise InvalidHeader("Sec-WebSocket-Version", "multiple values") from None - - if version != "13": - raise InvalidHeaderValue("Sec-WebSocket-Version", version) - - accept_header = accept_key(key) - - self.origin = self.process_origin(headers) - - extensions_header, self.extensions = self.process_extensions(headers) - - protocol_header = self.subprotocol = self.process_subprotocol(headers) - - return ( - accept_header, - extensions_header, - protocol_header, - ) - - def process_origin(self, headers: Headers) -> Origin | None: - """ - Handle the Origin HTTP request header. - - Args: - headers: WebSocket handshake request headers. - - Returns: - origin, if it is acceptable. - - Raises: - InvalidHandshake: If the Origin header is invalid. - InvalidOrigin: If the origin isn't acceptable. - - """ - # "The user agent MUST NOT include more than one Origin header field" - # per https://datatracker.ietf.org/doc/html/rfc6454#section-7.3. - try: - origin = headers.get("Origin") - except MultipleValuesError: - raise InvalidHeader("Origin", "multiple values") from None - if origin is not None: - origin = cast(Origin, origin) - if self.origins is not None: - if origin not in self.origins: - raise InvalidOrigin(origin) - return origin - - def process_extensions( - self, - headers: Headers, - ) -> tuple[str | None, list[Extension]]: - """ - Handle the Sec-WebSocket-Extensions HTTP request header. - - Accept or reject each extension proposed in the client request. - Negotiate parameters for accepted extensions. - - Per :rfc:`6455`, negotiation rules are defined by the specification of - each extension. - - To provide this level of flexibility, for each extension proposed by - the client, we check for a match with each extension available in the - server configuration. If no match is found, the extension is ignored. - - If several variants of the same extension are proposed by the client, - it may be accepted several times, which won't make sense in general. - Extensions must implement their own requirements. For this purpose, - the list of previously accepted extensions is provided. - - This process doesn't allow the server to reorder extensions. It can - only select a subset of the extensions proposed by the client. - - Other requirements, for example related to mandatory extensions or the - order of extensions, may be implemented by overriding this method. - - Args: - headers: WebSocket handshake request headers. - - Returns: - ``Sec-WebSocket-Extensions`` HTTP response header and list of - accepted extensions. - - Raises: - InvalidHandshake: If the Sec-WebSocket-Extensions header is invalid. - - """ - response_header_value: str | None = None - - extension_headers: list[ExtensionHeader] = [] - accepted_extensions: list[Extension] = [] - - header_values = headers.get_all("Sec-WebSocket-Extensions") - - if header_values and self.available_extensions: - parsed_header_values: list[ExtensionHeader] = sum( - [parse_extension(header_value) for header_value in header_values], [] - ) - - for name, request_params in parsed_header_values: - for ext_factory in self.available_extensions: - # Skip non-matching extensions based on their name. - if ext_factory.name != name: - continue - - # Skip non-matching extensions based on their params. - try: - response_params, extension = ext_factory.process_request_params( - request_params, accepted_extensions - ) - except NegotiationError: - continue - - # Add matching extension to the final list. - extension_headers.append((name, response_params)) - accepted_extensions.append(extension) - - # Break out of the loop once we have a match. - break - - # If we didn't break from the loop, no extension in our list - # matched what the client sent. The extension is declined. - - # Serialize extension header. - if extension_headers: - response_header_value = build_extension(extension_headers) - - return response_header_value, accepted_extensions - - def process_subprotocol(self, headers: Headers) -> Subprotocol | None: - """ - Handle the Sec-WebSocket-Protocol HTTP request header. - - Args: - headers: WebSocket handshake request headers. - - Returns: - Subprotocol, if one was selected; this is also the value of the - ``Sec-WebSocket-Protocol`` response header. - - Raises: - InvalidHandshake: If the Sec-WebSocket-Subprotocol header is invalid. - - """ - subprotocols: Sequence[Subprotocol] = sum( - [ - parse_subprotocol(header_value) - for header_value in headers.get_all("Sec-WebSocket-Protocol") - ], - [], - ) - - return self.select_subprotocol(subprotocols) - - def select_subprotocol( - self, - subprotocols: Sequence[Subprotocol], - ) -> Subprotocol | None: - """ - Pick a subprotocol among those offered by the client. - - If several subprotocols are supported by both the client and the server, - pick the first one in the list declared the server. - - If the server doesn't support any subprotocols, continue without a - subprotocol, regardless of what the client offers. - - If the server supports at least one subprotocol and the client doesn't - offer any, abort the handshake with an HTTP 400 error. - - You provide a ``select_subprotocol`` argument to :class:`ServerProtocol` - to override this logic. For example, you could accept the connection - even if client doesn't offer a subprotocol, rather than reject it. - - Here's how to negotiate the ``chat`` subprotocol if the client supports - it and continue without a subprotocol otherwise:: - - def select_subprotocol(protocol, subprotocols): - if "chat" in subprotocols: - return "chat" - - Args: - subprotocols: List of subprotocols offered by the client. - - Returns: - Selected subprotocol, if a common subprotocol was found. - - :obj:`None` to continue without a subprotocol. - - Raises: - NegotiationError: Custom implementations may raise this exception - to abort the handshake with an HTTP 400 error. - - """ - # Server doesn't offer any subprotocols. - if not self.available_subprotocols: # None or empty list - return None - - # Server offers at least one subprotocol but client doesn't offer any. - if not subprotocols: - raise NegotiationError("missing subprotocol") - - # Server and client both offer subprotocols. Look for a shared one. - proposed_subprotocols = set(subprotocols) - for subprotocol in self.available_subprotocols: - if subprotocol in proposed_subprotocols: - return subprotocol - - # No common subprotocol was found. - raise NegotiationError( - "invalid subprotocol; expected one of " - + ", ".join(self.available_subprotocols) - ) - - def reject(self, status: StatusLike, text: str) -> Response: - """ - Create a handshake response to reject the connection. - - A short plain text response is the best fallback when failing to - establish a WebSocket connection. - - You must send the handshake response with :meth:`send_response`. - - You may modify the response before sending it, for example by changing - HTTP headers. - - Args: - status: HTTP status code. - text: HTTP response body; it will be encoded to UTF-8. - - Returns: - HTTP response to send to the client. - - """ - # If status is an int instead of an HTTPStatus, fix it automatically. - status = http.HTTPStatus(status) - body = text.encode() - headers = Headers( - [ - ("Date", email.utils.formatdate(usegmt=True)), - ("Connection", "close"), - ("Content-Length", str(len(body))), - ("Content-Type", "text/plain; charset=utf-8"), - ] - ) - return Response(status.value, status.phrase, headers, body) - - def send_response(self, response: Response) -> None: - """ - Send a handshake response to the client. - - Args: - response: WebSocket handshake response event to send. - - """ - if self.debug: - code, phrase = response.status_code, response.reason_phrase - self.logger.debug("> HTTP/1.1 %d %s", code, phrase) - for key, value in response.headers.raw_items(): - self.logger.debug("> %s: %s", key, value) - if response.body is not None: - self.logger.debug("> [body] (%d bytes)", len(response.body)) - - self.writes.append(response.serialize()) - - if response.status_code == 101: - assert self.state is CONNECTING - self.state = OPEN - self.logger.info("connection open") - - else: - # handshake_exc may be already set if accept() encountered an error. - # If the connection isn't open, set handshake_exc to guarantee that - # handshake_exc is None if and only if opening handshake succeeded. - if self.handshake_exc is None: - self.handshake_exc = InvalidStatus(response) - self.logger.info( - "connection rejected (%d %s)", - response.status_code, - response.reason_phrase, - ) - - self.send_eof() - self.parser = self.discard() - next(self.parser) # start coroutine - - def parse(self) -> Generator[None, None, None]: - if self.state is CONNECTING: - try: - request = yield from Request.parse( - self.reader.read_line, - ) - except Exception as exc: - self.handshake_exc = exc - self.send_eof() - self.parser = self.discard() - next(self.parser) # start coroutine - yield - - if self.debug: - self.logger.debug("< GET %s HTTP/1.1", request.path) - for key, value in request.headers.raw_items(): - self.logger.debug("< %s: %s", key, value) - - self.events.append(request) - - yield from super().parse() - - -class ServerConnection(ServerProtocol): - def __init__(self, *args: Any, **kwargs: Any) -> None: - warnings.warn( # deprecated in 11.0 - 2023-04-02 - "ServerConnection was renamed to ServerProtocol", - DeprecationWarning, - ) - super().__init__(*args, **kwargs) diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/speedups.c b/plotter-app/venv/lib/python3.8/site-packages/websockets/speedups.c deleted file mode 100644 index cb10ded..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/speedups.c +++ /dev/null @@ -1,222 +0,0 @@ -/* C implementation of performance sensitive functions. */ - -#define PY_SSIZE_T_CLEAN -#include -#include /* uint8_t, uint32_t, uint64_t */ - -#if __ARM_NEON -#include -#elif __SSE2__ -#include -#endif - -static const Py_ssize_t MASK_LEN = 4; - -/* Similar to PyBytes_AsStringAndSize, but accepts more types */ - -static int -_PyBytesLike_AsStringAndSize(PyObject *obj, PyObject **tmp, char **buffer, Py_ssize_t *length) -{ - // This supports bytes, bytearrays, and memoryview objects, - // which are common data structures for handling byte streams. - // If *tmp isn't NULL, the caller gets a new reference. - if (PyBytes_Check(obj)) - { - *tmp = NULL; - *buffer = PyBytes_AS_STRING(obj); - *length = PyBytes_GET_SIZE(obj); - } - else if (PyByteArray_Check(obj)) - { - *tmp = NULL; - *buffer = PyByteArray_AS_STRING(obj); - *length = PyByteArray_GET_SIZE(obj); - } - else if (PyMemoryView_Check(obj)) - { - *tmp = PyMemoryView_GetContiguous(obj, PyBUF_READ, 'C'); - if (*tmp == NULL) - { - return -1; - } - Py_buffer *mv_buf; - mv_buf = PyMemoryView_GET_BUFFER(*tmp); - *buffer = mv_buf->buf; - *length = mv_buf->len; - } - else - { - PyErr_Format( - PyExc_TypeError, - "expected a bytes-like object, %.200s found", - Py_TYPE(obj)->tp_name); - return -1; - } - - return 0; -} - -/* C implementation of websockets.utils.apply_mask */ - -static PyObject * -apply_mask(PyObject *self, PyObject *args, PyObject *kwds) -{ - - // In order to support various bytes-like types, accept any Python object. - - static char *kwlist[] = {"data", "mask", NULL}; - PyObject *input_obj; - PyObject *mask_obj; - - // A pointer to a char * + length will be extracted from the data and mask - // arguments, possibly via a Py_buffer. - - PyObject *input_tmp = NULL; - char *input; - Py_ssize_t input_len; - PyObject *mask_tmp = NULL; - char *mask; - Py_ssize_t mask_len; - - // Initialize a PyBytesObject then get a pointer to the underlying char * - // in order to avoid an extra memory copy in PyBytes_FromStringAndSize. - - PyObject *result = NULL; - char *output; - - // Other variables. - - Py_ssize_t i = 0; - - // Parse inputs. - - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "OO", kwlist, &input_obj, &mask_obj)) - { - goto exit; - } - - if (_PyBytesLike_AsStringAndSize(input_obj, &input_tmp, &input, &input_len) == -1) - { - goto exit; - } - - if (_PyBytesLike_AsStringAndSize(mask_obj, &mask_tmp, &mask, &mask_len) == -1) - { - goto exit; - } - - if (mask_len != MASK_LEN) - { - PyErr_SetString(PyExc_ValueError, "mask must contain 4 bytes"); - goto exit; - } - - // Create output. - - result = PyBytes_FromStringAndSize(NULL, input_len); - if (result == NULL) - { - goto exit; - } - - // Since we just created result, we don't need error checks. - output = PyBytes_AS_STRING(result); - - // Perform the masking operation. - - // Apparently GCC cannot figure out the following optimizations by itself. - - // We need a new scope for MSVC 2010 (non C99 friendly) - { -#if __ARM_NEON - - // With NEON support, XOR by blocks of 16 bytes = 128 bits. - - Py_ssize_t input_len_128 = input_len & ~15; - uint8x16_t mask_128 = vreinterpretq_u8_u32(vdupq_n_u32(*(uint32_t *)mask)); - - for (; i < input_len_128; i += 16) - { - uint8x16_t in_128 = vld1q_u8((uint8_t *)(input + i)); - uint8x16_t out_128 = veorq_u8(in_128, mask_128); - vst1q_u8((uint8_t *)(output + i), out_128); - } - -#elif __SSE2__ - - // With SSE2 support, XOR by blocks of 16 bytes = 128 bits. - - // Since we cannot control the 16-bytes alignment of input and output - // buffers, we rely on loadu/storeu rather than load/store. - - Py_ssize_t input_len_128 = input_len & ~15; - __m128i mask_128 = _mm_set1_epi32(*(uint32_t *)mask); - - for (; i < input_len_128; i += 16) - { - __m128i in_128 = _mm_loadu_si128((__m128i *)(input + i)); - __m128i out_128 = _mm_xor_si128(in_128, mask_128); - _mm_storeu_si128((__m128i *)(output + i), out_128); - } - -#else - - // Without SSE2 support, XOR by blocks of 8 bytes = 64 bits. - - // We assume the memory allocator aligns everything on 8 bytes boundaries. - - Py_ssize_t input_len_64 = input_len & ~7; - uint32_t mask_32 = *(uint32_t *)mask; - uint64_t mask_64 = ((uint64_t)mask_32 << 32) | (uint64_t)mask_32; - - for (; i < input_len_64; i += 8) - { - *(uint64_t *)(output + i) = *(uint64_t *)(input + i) ^ mask_64; - } - -#endif - } - - // XOR the remainder of the input byte by byte. - - for (; i < input_len; i++) - { - output[i] = input[i] ^ mask[i & (MASK_LEN - 1)]; - } - -exit: - Py_XDECREF(input_tmp); - Py_XDECREF(mask_tmp); - return result; - -} - -static PyMethodDef speedups_methods[] = { - { - "apply_mask", - (PyCFunction)apply_mask, - METH_VARARGS | METH_KEYWORDS, - "Apply masking to the data of a WebSocket message.", - }, - {NULL, NULL, 0, NULL}, /* Sentinel */ -}; - -static struct PyModuleDef speedups_module = { - PyModuleDef_HEAD_INIT, - "websocket.speedups", /* m_name */ - "C implementation of performance sensitive functions.", - /* m_doc */ - -1, /* m_size */ - speedups_methods, /* m_methods */ - NULL, - NULL, - NULL, - NULL -}; - -PyMODINIT_FUNC -PyInit_speedups(void) -{ - return PyModule_Create(&speedups_module); -} diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/speedups.cpython-38-x86_64-linux-gnu.so b/plotter-app/venv/lib/python3.8/site-packages/websockets/speedups.cpython-38-x86_64-linux-gnu.so deleted file mode 100755 index e1e4b38..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/speedups.cpython-38-x86_64-linux-gnu.so and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/speedups.pyi b/plotter-app/venv/lib/python3.8/site-packages/websockets/speedups.pyi deleted file mode 100644 index 821438a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/speedups.pyi +++ /dev/null @@ -1 +0,0 @@ -def apply_mask(data: bytes, mask: bytes) -> bytes: ... diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/streams.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/streams.py deleted file mode 100644 index 956f139..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/streams.py +++ /dev/null @@ -1,151 +0,0 @@ -from __future__ import annotations - -from typing import Generator - - -class StreamReader: - """ - Generator-based stream reader. - - This class doesn't support concurrent calls to :meth:`read_line`, - :meth:`read_exact`, or :meth:`read_to_eof`. Make sure calls are - serialized. - - """ - - def __init__(self) -> None: - self.buffer = bytearray() - self.eof = False - - def read_line(self, m: int) -> Generator[None, None, bytes]: - """ - Read a LF-terminated line from the stream. - - This is a generator-based coroutine. - - The return value includes the LF character. - - Args: - m: Maximum number bytes to read; this is a security limit. - - Raises: - EOFError: If the stream ends without a LF. - RuntimeError: If the stream ends in more than ``m`` bytes. - - """ - n = 0 # number of bytes to read - p = 0 # number of bytes without a newline - while True: - n = self.buffer.find(b"\n", p) + 1 - if n > 0: - break - p = len(self.buffer) - if p > m: - raise RuntimeError(f"read {p} bytes, expected no more than {m} bytes") - if self.eof: - raise EOFError(f"stream ends after {p} bytes, before end of line") - yield - if n > m: - raise RuntimeError(f"read {n} bytes, expected no more than {m} bytes") - r = self.buffer[:n] - del self.buffer[:n] - return r - - def read_exact(self, n: int) -> Generator[None, None, bytes]: - """ - Read a given number of bytes from the stream. - - This is a generator-based coroutine. - - Args: - n: How many bytes to read. - - Raises: - EOFError: If the stream ends in less than ``n`` bytes. - - """ - assert n >= 0 - while len(self.buffer) < n: - if self.eof: - p = len(self.buffer) - raise EOFError(f"stream ends after {p} bytes, expected {n} bytes") - yield - r = self.buffer[:n] - del self.buffer[:n] - return r - - def read_to_eof(self, m: int) -> Generator[None, None, bytes]: - """ - Read all bytes from the stream. - - This is a generator-based coroutine. - - Args: - m: Maximum number bytes to read; this is a security limit. - - Raises: - RuntimeError: If the stream ends in more than ``m`` bytes. - - """ - while not self.eof: - p = len(self.buffer) - if p > m: - raise RuntimeError(f"read {p} bytes, expected no more than {m} bytes") - yield - r = self.buffer[:] - del self.buffer[:] - return r - - def at_eof(self) -> Generator[None, None, bool]: - """ - Tell whether the stream has ended and all data was read. - - This is a generator-based coroutine. - - """ - while True: - if self.buffer: - return False - if self.eof: - return True - # When all data was read but the stream hasn't ended, we can't - # tell if until either feed_data() or feed_eof() is called. - yield - - def feed_data(self, data: bytes) -> None: - """ - Write data to the stream. - - :meth:`feed_data` cannot be called after :meth:`feed_eof`. - - Args: - data: Data to write. - - Raises: - EOFError: If the stream has ended. - - """ - if self.eof: - raise EOFError("stream ended") - self.buffer += data - - def feed_eof(self) -> None: - """ - End the stream. - - :meth:`feed_eof` cannot be called more than once. - - Raises: - EOFError: If the stream has ended. - - """ - if self.eof: - raise EOFError("stream ended") - self.eof = True - - def discard(self) -> None: - """ - Discard all buffered data, but don't end the stream. - - """ - del self.buffer[:] diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index d8ba756..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/__pycache__/client.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/__pycache__/client.cpython-38.pyc deleted file mode 100644 index e0a8941..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/__pycache__/client.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/__pycache__/connection.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/__pycache__/connection.cpython-38.pyc deleted file mode 100644 index 672b6a3..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/__pycache__/connection.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/__pycache__/messages.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/__pycache__/messages.cpython-38.pyc deleted file mode 100644 index 37ed6ba..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/__pycache__/messages.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/__pycache__/server.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/__pycache__/server.cpython-38.pyc deleted file mode 100644 index 8f46d31..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/__pycache__/server.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/__pycache__/utils.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/__pycache__/utils.cpython-38.pyc deleted file mode 100644 index 74116f6..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/__pycache__/utils.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/client.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/client.py deleted file mode 100644 index d1e20a7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/client.py +++ /dev/null @@ -1,336 +0,0 @@ -from __future__ import annotations - -import socket -import ssl as ssl_module -import threading -import warnings -from typing import Any, Sequence - -from ..client import ClientProtocol -from ..datastructures import HeadersLike -from ..extensions.base import ClientExtensionFactory -from ..extensions.permessage_deflate import enable_client_permessage_deflate -from ..headers import validate_subprotocols -from ..http11 import USER_AGENT, Response -from ..protocol import CONNECTING, Event -from ..typing import LoggerLike, Origin, Subprotocol -from ..uri import parse_uri -from .connection import Connection -from .utils import Deadline - - -__all__ = ["connect", "unix_connect", "ClientConnection"] - - -class ClientConnection(Connection): - """ - :mod:`threading` implementation of a WebSocket client connection. - - :class:`ClientConnection` provides :meth:`recv` and :meth:`send` methods for - receiving and sending messages. - - It supports iteration to receive messages:: - - for message in websocket: - process(message) - - The iterator exits normally when the connection is closed with close code - 1000 (OK) or 1001 (going away) or without a close code. It raises a - :exc:`~websockets.exceptions.ConnectionClosedError` when the connection is - closed with any other code. - - Args: - socket: Socket connected to a WebSocket server. - protocol: Sans-I/O connection. - close_timeout: Timeout for closing the connection in seconds. - - """ - - def __init__( - self, - socket: socket.socket, - protocol: ClientProtocol, - *, - close_timeout: float | None = 10, - ) -> None: - self.protocol: ClientProtocol - self.response_rcvd = threading.Event() - super().__init__( - socket, - protocol, - close_timeout=close_timeout, - ) - - def handshake( - self, - additional_headers: HeadersLike | None = None, - user_agent_header: str | None = USER_AGENT, - timeout: float | None = None, - ) -> None: - """ - Perform the opening handshake. - - """ - with self.send_context(expected_state=CONNECTING): - self.request = self.protocol.connect() - if additional_headers is not None: - self.request.headers.update(additional_headers) - if user_agent_header is not None: - self.request.headers["User-Agent"] = user_agent_header - self.protocol.send_request(self.request) - - if not self.response_rcvd.wait(timeout): - raise TimeoutError("timed out during handshake") - - # self.protocol.handshake_exc is always set when the connection is lost - # before receiving a response, when the response cannot be parsed, or - # when the response fails the handshake. - - if self.protocol.handshake_exc is not None: - raise self.protocol.handshake_exc - - def process_event(self, event: Event) -> None: - """ - Process one incoming event. - - """ - # First event - handshake response. - if self.response is None: - assert isinstance(event, Response) - self.response = event - self.response_rcvd.set() - # Later events - frames. - else: - super().process_event(event) - - def recv_events(self) -> None: - """ - Read incoming data from the socket and process events. - - """ - try: - super().recv_events() - finally: - # If the connection is closed during the handshake, unblock it. - self.response_rcvd.set() - - -def connect( - uri: str, - *, - # TCP/TLS - sock: socket.socket | None = None, - ssl: ssl_module.SSLContext | None = None, - server_hostname: str | None = None, - # WebSocket - origin: Origin | None = None, - extensions: Sequence[ClientExtensionFactory] | None = None, - subprotocols: Sequence[Subprotocol] | None = None, - additional_headers: HeadersLike | None = None, - user_agent_header: str | None = USER_AGENT, - compression: str | None = "deflate", - # Timeouts - open_timeout: float | None = 10, - close_timeout: float | None = 10, - # Limits - max_size: int | None = 2**20, - # Logging - logger: LoggerLike | None = None, - # Escape hatch for advanced customization - create_connection: type[ClientConnection] | None = None, - **kwargs: Any, -) -> ClientConnection: - """ - Connect to the WebSocket server at ``uri``. - - This function returns a :class:`ClientConnection` instance, which you can - use to send and receive messages. - - :func:`connect` may be used as a context manager:: - - from websockets.sync.client import connect - - with connect(...) as websocket: - ... - - The connection is closed automatically when exiting the context. - - Args: - uri: URI of the WebSocket server. - sock: Preexisting TCP socket. ``sock`` overrides the host and port - from ``uri``. You may call :func:`socket.create_connection` to - create a suitable TCP socket. - ssl: Configuration for enabling TLS on the connection. - server_hostname: Host name for the TLS handshake. ``server_hostname`` - overrides the host name from ``uri``. - origin: Value of the ``Origin`` header, for servers that require it. - extensions: List of supported extensions, in order in which they - should be negotiated and run. - subprotocols: List of supported subprotocols, in order of decreasing - preference. - additional_headers (HeadersLike | None): Arbitrary HTTP headers to add - to the handshake request. - user_agent_header: Value of the ``User-Agent`` request header. - It defaults to ``"Python/x.y.z websockets/X.Y"``. - Setting it to :obj:`None` removes the header. - compression: The "permessage-deflate" extension is enabled by default. - Set ``compression`` to :obj:`None` to disable it. See the - :doc:`compression guide <../../topics/compression>` for details. - open_timeout: Timeout for opening the connection in seconds. - :obj:`None` disables the timeout. - close_timeout: Timeout for closing the connection in seconds. - :obj:`None` disables the timeout. - max_size: Maximum size of incoming messages in bytes. - :obj:`None` disables the limit. - logger: Logger for this client. - It defaults to ``logging.getLogger("websockets.client")``. - See the :doc:`logging guide <../../topics/logging>` for details. - create_connection: Factory for the :class:`ClientConnection` managing - the connection. Set it to a wrapper or a subclass to customize - connection handling. - - Any other keyword arguments are passed to :func:`~socket.create_connection`. - - Raises: - InvalidURI: If ``uri`` isn't a valid WebSocket URI. - OSError: If the TCP connection fails. - InvalidHandshake: If the opening handshake fails. - TimeoutError: If the opening handshake times out. - - """ - - # Process parameters - - # Backwards compatibility: ssl used to be called ssl_context. - if ssl is None and "ssl_context" in kwargs: - ssl = kwargs.pop("ssl_context") - warnings.warn( # deprecated in 13.0 - 2024-08-20 - "ssl_context was renamed to ssl", - DeprecationWarning, - ) - - wsuri = parse_uri(uri) - if not wsuri.secure and ssl is not None: - raise TypeError("ssl argument is incompatible with a ws:// URI") - - # Private APIs for unix_connect() - unix: bool = kwargs.pop("unix", False) - path: str | None = kwargs.pop("path", None) - - if unix: - if path is None and sock is None: - raise TypeError("missing path argument") - elif path is not None and sock is not None: - raise TypeError("path and sock arguments are incompatible") - - if subprotocols is not None: - validate_subprotocols(subprotocols) - - if compression == "deflate": - extensions = enable_client_permessage_deflate(extensions) - elif compression is not None: - raise ValueError(f"unsupported compression: {compression}") - - # Calculate timeouts on the TCP, TLS, and WebSocket handshakes. - # The TCP and TLS timeouts must be set on the socket, then removed - # to avoid conflicting with the WebSocket timeout in handshake(). - deadline = Deadline(open_timeout) - - if create_connection is None: - create_connection = ClientConnection - - try: - # Connect socket - - if sock is None: - if unix: - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.settimeout(deadline.timeout()) - assert path is not None # mypy cannot figure this out - sock.connect(path) - else: - kwargs.setdefault("timeout", deadline.timeout()) - sock = socket.create_connection((wsuri.host, wsuri.port), **kwargs) - sock.settimeout(None) - - # Disable Nagle algorithm - - if not unix: - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True) - - # Initialize TLS wrapper and perform TLS handshake - - if wsuri.secure: - if ssl is None: - ssl = ssl_module.create_default_context() - if server_hostname is None: - server_hostname = wsuri.host - sock.settimeout(deadline.timeout()) - sock = ssl.wrap_socket(sock, server_hostname=server_hostname) - sock.settimeout(None) - - # Initialize WebSocket protocol - - protocol = ClientProtocol( - wsuri, - origin=origin, - extensions=extensions, - subprotocols=subprotocols, - max_size=max_size, - logger=logger, - ) - - # Initialize WebSocket connection - - connection = create_connection( - sock, - protocol, - close_timeout=close_timeout, - ) - except Exception: - if sock is not None: - sock.close() - raise - - try: - connection.handshake( - additional_headers, - user_agent_header, - deadline.timeout(), - ) - except Exception: - connection.close_socket() - connection.recv_events_thread.join() - raise - - return connection - - -def unix_connect( - path: str | None = None, - uri: str | None = None, - **kwargs: Any, -) -> ClientConnection: - """ - Connect to a WebSocket server listening on a Unix socket. - - This function accepts the same keyword arguments as :func:`connect`. - - It's only available on Unix. - - It's mainly useful for debugging servers listening on Unix sockets. - - Args: - path: File system path to the Unix socket. - uri: URI of the WebSocket server. ``uri`` defaults to - ``ws://localhost/`` or, when a ``ssl`` is provided, to - ``wss://localhost/``. - - """ - if uri is None: - # Backwards compatibility: ssl used to be called ssl_context. - if kwargs.get("ssl") is None and kwargs.get("ssl_context") is None: - uri = "ws://localhost/" - else: - uri = "wss://localhost/" - return connect(uri=uri, unix=True, path=path, **kwargs) diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/connection.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/connection.py deleted file mode 100644 index 9758887..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/connection.py +++ /dev/null @@ -1,801 +0,0 @@ -from __future__ import annotations - -import contextlib -import logging -import random -import socket -import struct -import threading -import uuid -from types import TracebackType -from typing import Any, Iterable, Iterator, Mapping - -from ..exceptions import ( - ConcurrencyError, - ConnectionClosed, - ConnectionClosedOK, - ProtocolError, -) -from ..frames import DATA_OPCODES, BytesLike, CloseCode, Frame, Opcode -from ..http11 import Request, Response -from ..protocol import CLOSED, OPEN, Event, Protocol, State -from ..typing import Data, LoggerLike, Subprotocol -from .messages import Assembler -from .utils import Deadline - - -__all__ = ["Connection"] - - -class Connection: - """ - :mod:`threading` implementation of a WebSocket connection. - - :class:`Connection` provides APIs shared between WebSocket servers and - clients. - - You shouldn't use it directly. Instead, use - :class:`~websockets.sync.client.ClientConnection` or - :class:`~websockets.sync.server.ServerConnection`. - - """ - - recv_bufsize = 65536 - - def __init__( - self, - socket: socket.socket, - protocol: Protocol, - *, - close_timeout: float | None = 10, - ) -> None: - self.socket = socket - self.protocol = protocol - self.close_timeout = close_timeout - - # Inject reference to this instance in the protocol's logger. - self.protocol.logger = logging.LoggerAdapter( - self.protocol.logger, - {"websocket": self}, - ) - - # Copy attributes from the protocol for convenience. - self.id: uuid.UUID = self.protocol.id - """Unique identifier of the connection. Useful in logs.""" - self.logger: LoggerLike = self.protocol.logger - """Logger for this connection.""" - self.debug = self.protocol.debug - - # HTTP handshake request and response. - self.request: Request | None = None - """Opening handshake request.""" - self.response: Response | None = None - """Opening handshake response.""" - - # Mutex serializing interactions with the protocol. - self.protocol_mutex = threading.Lock() - - # Assembler turning frames into messages and serializing reads. - self.recv_messages = Assembler() - - # Whether we are busy sending a fragmented message. - self.send_in_progress = False - - # Deadline for the closing handshake. - self.close_deadline: Deadline | None = None - - # Mapping of ping IDs to pong waiters, in chronological order. - self.ping_waiters: dict[bytes, threading.Event] = {} - - # Receiving events from the socket. This thread is marked as daemon to - # allow creating a connection in a non-daemon thread and using it in a - # daemon thread. This mustn't prevent the interpreter from exiting. - self.recv_events_thread = threading.Thread( - target=self.recv_events, - daemon=True, - ) - self.recv_events_thread.start() - - # Exception raised in recv_events, to be chained to ConnectionClosed - # in the user thread in order to show why the TCP connection dropped. - self.recv_exc: BaseException | None = None - - # Public attributes - - @property - def local_address(self) -> Any: - """ - Local address of the connection. - - For IPv4 connections, this is a ``(host, port)`` tuple. - - The format of the address depends on the address family. - See :meth:`~socket.socket.getsockname`. - - """ - return self.socket.getsockname() - - @property - def remote_address(self) -> Any: - """ - Remote address of the connection. - - For IPv4 connections, this is a ``(host, port)`` tuple. - - The format of the address depends on the address family. - See :meth:`~socket.socket.getpeername`. - - """ - return self.socket.getpeername() - - @property - def subprotocol(self) -> Subprotocol | None: - """ - Subprotocol negotiated during the opening handshake. - - :obj:`None` if no subprotocol was negotiated. - - """ - return self.protocol.subprotocol - - # Public methods - - def __enter__(self) -> Connection: - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - traceback: TracebackType | None, - ) -> None: - if exc_type is None: - self.close() - else: - self.close(CloseCode.INTERNAL_ERROR) - - def __iter__(self) -> Iterator[Data]: - """ - Iterate on incoming messages. - - The iterator calls :meth:`recv` and yields messages in an infinite loop. - - It exits when the connection is closed normally. It raises a - :exc:`~websockets.exceptions.ConnectionClosedError` exception after a - protocol error or a network failure. - - """ - try: - while True: - yield self.recv() - except ConnectionClosedOK: - return - - def recv(self, timeout: float | None = None) -> Data: - """ - Receive the next message. - - When the connection is closed, :meth:`recv` raises - :exc:`~websockets.exceptions.ConnectionClosed`. Specifically, it raises - :exc:`~websockets.exceptions.ConnectionClosedOK` after a normal closure - and :exc:`~websockets.exceptions.ConnectionClosedError` after a protocol - error or a network failure. This is how you detect the end of the - message stream. - - If ``timeout`` is :obj:`None`, block until a message is received. If - ``timeout`` is set and no message is received within ``timeout`` - seconds, raise :exc:`TimeoutError`. Set ``timeout`` to ``0`` to check if - a message was already received. - - If the message is fragmented, wait until all fragments are received, - reassemble them, and return the whole message. - - Returns: - A string (:class:`str`) for a Text_ frame or a bytestring - (:class:`bytes`) for a Binary_ frame. - - .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - - Raises: - ConnectionClosed: When the connection is closed. - ConcurrencyError: If two threads call :meth:`recv` or - :meth:`recv_streaming` concurrently. - - """ - try: - return self.recv_messages.get(timeout) - except EOFError: - # Wait for the protocol state to be CLOSED before accessing close_exc. - self.recv_events_thread.join() - raise self.protocol.close_exc from self.recv_exc - except ConcurrencyError: - raise ConcurrencyError( - "cannot call recv while another thread " - "is already running recv or recv_streaming" - ) from None - - def recv_streaming(self) -> Iterator[Data]: - """ - Receive the next message frame by frame. - - If the message is fragmented, yield each fragment as it is received. - The iterator must be fully consumed, or else the connection will become - unusable. - - :meth:`recv_streaming` raises the same exceptions as :meth:`recv`. - - Returns: - An iterator of strings (:class:`str`) for a Text_ frame or - bytestrings (:class:`bytes`) for a Binary_ frame. - - .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - - Raises: - ConnectionClosed: When the connection is closed. - ConcurrencyError: If two threads call :meth:`recv` or - :meth:`recv_streaming` concurrently. - - """ - try: - for frame in self.recv_messages.get_iter(): - yield frame - except EOFError: - # Wait for the protocol state to be CLOSED before accessing close_exc. - self.recv_events_thread.join() - raise self.protocol.close_exc from self.recv_exc - except ConcurrencyError: - raise ConcurrencyError( - "cannot call recv_streaming while another thread " - "is already running recv or recv_streaming" - ) from None - - def send(self, message: Data | Iterable[Data]) -> None: - """ - Send a message. - - A string (:class:`str`) is sent as a Text_ frame. A bytestring or - bytes-like object (:class:`bytes`, :class:`bytearray`, or - :class:`memoryview`) is sent as a Binary_ frame. - - .. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - .. _Binary: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - - :meth:`send` also accepts an iterable of strings, bytestrings, or - bytes-like objects to enable fragmentation_. Each item is treated as a - message fragment and sent in its own frame. All items must be of the - same type, or else :meth:`send` will raise a :exc:`TypeError` and the - connection will be closed. - - .. _fragmentation: https://datatracker.ietf.org/doc/html/rfc6455#section-5.4 - - :meth:`send` rejects dict-like objects because this is often an error. - (If you really want to send the keys of a dict-like object as fragments, - call its :meth:`~dict.keys` method and pass the result to :meth:`send`.) - - When the connection is closed, :meth:`send` raises - :exc:`~websockets.exceptions.ConnectionClosed`. Specifically, it - raises :exc:`~websockets.exceptions.ConnectionClosedOK` after a normal - connection closure and - :exc:`~websockets.exceptions.ConnectionClosedError` after a protocol - error or a network failure. - - Args: - message: Message to send. - - Raises: - ConnectionClosed: When the connection is closed. - ConcurrencyError: If the connection is sending a fragmented message. - TypeError: If ``message`` doesn't have a supported type. - - """ - # Unfragmented message -- this case must be handled first because - # strings and bytes-like objects are iterable. - - if isinstance(message, str): - with self.send_context(): - if self.send_in_progress: - raise ConcurrencyError( - "cannot call send while another thread " - "is already running send" - ) - self.protocol.send_text(message.encode()) - - elif isinstance(message, BytesLike): - with self.send_context(): - if self.send_in_progress: - raise ConcurrencyError( - "cannot call send while another thread " - "is already running send" - ) - self.protocol.send_binary(message) - - # Catch a common mistake -- passing a dict to send(). - - elif isinstance(message, Mapping): - raise TypeError("data is a dict-like object") - - # Fragmented message -- regular iterator. - - elif isinstance(message, Iterable): - chunks = iter(message) - try: - chunk = next(chunks) - except StopIteration: - return - - try: - # First fragment. - if isinstance(chunk, str): - text = True - with self.send_context(): - if self.send_in_progress: - raise ConcurrencyError( - "cannot call send while another thread " - "is already running send" - ) - self.send_in_progress = True - self.protocol.send_text( - chunk.encode(), - fin=False, - ) - elif isinstance(chunk, BytesLike): - text = False - with self.send_context(): - if self.send_in_progress: - raise ConcurrencyError( - "cannot call send while another thread " - "is already running send" - ) - self.send_in_progress = True - self.protocol.send_binary( - chunk, - fin=False, - ) - else: - raise TypeError("data iterable must contain bytes or str") - - # Other fragments - for chunk in chunks: - if isinstance(chunk, str) and text: - with self.send_context(): - assert self.send_in_progress - self.protocol.send_continuation( - chunk.encode(), - fin=False, - ) - elif isinstance(chunk, BytesLike) and not text: - with self.send_context(): - assert self.send_in_progress - self.protocol.send_continuation( - chunk, - fin=False, - ) - else: - raise TypeError("data iterable must contain uniform types") - - # Final fragment. - with self.send_context(): - self.protocol.send_continuation(b"", fin=True) - self.send_in_progress = False - - except ConcurrencyError: - # We didn't start sending a fragmented message. - # The connection is still usable. - raise - - except Exception: - # We're half-way through a fragmented message and we can't - # complete it. This makes the connection unusable. - with self.send_context(): - self.protocol.fail( - CloseCode.INTERNAL_ERROR, - "error in fragmented message", - ) - raise - - else: - raise TypeError("data must be str, bytes, or iterable") - - def close(self, code: int = CloseCode.NORMAL_CLOSURE, reason: str = "") -> None: - """ - Perform the closing handshake. - - :meth:`close` waits for the other end to complete the handshake, for the - TCP connection to terminate, and for all incoming messages to be read - with :meth:`recv`. - - :meth:`close` is idempotent: it doesn't do anything once the - connection is closed. - - Args: - code: WebSocket close code. - reason: WebSocket close reason. - - """ - try: - # The context manager takes care of waiting for the TCP connection - # to terminate after calling a method that sends a close frame. - with self.send_context(): - if self.send_in_progress: - self.protocol.fail( - CloseCode.INTERNAL_ERROR, - "close during fragmented message", - ) - else: - self.protocol.send_close(code, reason) - except ConnectionClosed: - # Ignore ConnectionClosed exceptions raised from send_context(). - # They mean that the connection is closed, which was the goal. - pass - - def ping(self, data: Data | None = None) -> threading.Event: - """ - Send a Ping_. - - .. _Ping: https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.2 - - A ping may serve as a keepalive or as a check that the remote endpoint - received all messages up to this point - - Args: - data: Payload of the ping. A :class:`str` will be encoded to UTF-8. - If ``data`` is :obj:`None`, the payload is four random bytes. - - Returns: - An event that will be set when the corresponding pong is received. - You can ignore it if you don't intend to wait. - - :: - - pong_event = ws.ping() - pong_event.wait() # only if you want to wait for the pong - - Raises: - ConnectionClosed: When the connection is closed. - ConcurrencyError: If another ping was sent with the same data and - the corresponding pong wasn't received yet. - - """ - if isinstance(data, BytesLike): - data = bytes(data) - elif isinstance(data, str): - data = data.encode() - elif data is not None: - raise TypeError("data must be str or bytes-like") - - with self.send_context(): - # Protect against duplicates if a payload is explicitly set. - if data in self.ping_waiters: - raise ConcurrencyError("already waiting for a pong with the same data") - - # Generate a unique random payload otherwise. - while data is None or data in self.ping_waiters: - data = struct.pack("!I", random.getrandbits(32)) - - pong_waiter = threading.Event() - self.ping_waiters[data] = pong_waiter - self.protocol.send_ping(data) - return pong_waiter - - def pong(self, data: Data = b"") -> None: - """ - Send a Pong_. - - .. _Pong: https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.3 - - An unsolicited pong may serve as a unidirectional heartbeat. - - Args: - data: Payload of the pong. A :class:`str` will be encoded to UTF-8. - - Raises: - ConnectionClosed: When the connection is closed. - - """ - if isinstance(data, BytesLike): - data = bytes(data) - elif isinstance(data, str): - data = data.encode() - else: - raise TypeError("data must be str or bytes-like") - - with self.send_context(): - self.protocol.send_pong(data) - - # Private methods - - def process_event(self, event: Event) -> None: - """ - Process one incoming event. - - This method is overridden in subclasses to handle the handshake. - - """ - assert isinstance(event, Frame) - if event.opcode in DATA_OPCODES: - self.recv_messages.put(event) - - if event.opcode is Opcode.PONG: - self.acknowledge_pings(bytes(event.data)) - - def acknowledge_pings(self, data: bytes) -> None: - """ - Acknowledge pings when receiving a pong. - - """ - with self.protocol_mutex: - # Ignore unsolicited pong. - if data not in self.ping_waiters: - return - # Sending a pong for only the most recent ping is legal. - # Acknowledge all previous pings too in that case. - ping_id = None - ping_ids = [] - for ping_id, ping in self.ping_waiters.items(): - ping_ids.append(ping_id) - ping.set() - if ping_id == data: - break - else: - raise AssertionError("solicited pong not found in pings") - # Remove acknowledged pings from self.ping_waiters. - for ping_id in ping_ids: - del self.ping_waiters[ping_id] - - def recv_events(self) -> None: - """ - Read incoming data from the socket and process events. - - Run this method in a thread as long as the connection is alive. - - ``recv_events()`` exits immediately when the ``self.socket`` is closed. - - """ - try: - while True: - try: - if self.close_deadline is not None: - self.socket.settimeout(self.close_deadline.timeout()) - data = self.socket.recv(self.recv_bufsize) - except Exception as exc: - if self.debug: - self.logger.debug("error while receiving data", exc_info=True) - # When the closing handshake is initiated by our side, - # recv() may block until send_context() closes the socket. - # In that case, send_context() already set recv_exc. - # Calling set_recv_exc() avoids overwriting it. - with self.protocol_mutex: - self.set_recv_exc(exc) - break - - if data == b"": - break - - # Acquire the connection lock. - with self.protocol_mutex: - # Feed incoming data to the protocol. - self.protocol.receive_data(data) - - # This isn't expected to raise an exception. - events = self.protocol.events_received() - - # Write outgoing data to the socket. - try: - self.send_data() - except Exception as exc: - if self.debug: - self.logger.debug("error while sending data", exc_info=True) - # Similarly to the above, avoid overriding an exception - # set by send_context(), in case of a race condition - # i.e. send_context() closes the socket after recv() - # returns above but before send_data() calls send(). - self.set_recv_exc(exc) - break - - if self.protocol.close_expected(): - # If the connection is expected to close soon, set the - # close deadline based on the close timeout. - if self.close_deadline is None: - self.close_deadline = Deadline(self.close_timeout) - - # Unlock conn_mutex before processing events. Else, the - # application can't send messages in response to events. - - # If self.send_data raised an exception, then events are lost. - # Given that automatic responses write small amounts of data, - # this should be uncommon, so we don't handle the edge case. - - try: - for event in events: - # This may raise EOFError if the closing handshake - # times out while a message is waiting to be read. - self.process_event(event) - except EOFError: - break - - # Breaking out of the while True: ... loop means that we believe - # that the socket doesn't work anymore. - with self.protocol_mutex: - # Feed the end of the data stream to the protocol. - self.protocol.receive_eof() - - # This isn't expected to generate events. - assert not self.protocol.events_received() - - # There is no error handling because send_data() can only write - # the end of the data stream here and it handles errors itself. - self.send_data() - - except Exception as exc: - # This branch should never run. It's a safety net in case of bugs. - self.logger.error("unexpected internal error", exc_info=True) - with self.protocol_mutex: - self.set_recv_exc(exc) - finally: - # This isn't expected to raise an exception. - self.close_socket() - - @contextlib.contextmanager - def send_context( - self, - *, - expected_state: State = OPEN, # CONNECTING during the opening handshake - ) -> Iterator[None]: - """ - Create a context for writing to the connection from user code. - - On entry, :meth:`send_context` acquires the connection lock and checks - that the connection is open; on exit, it writes outgoing data to the - socket:: - - with self.send_context(): - self.protocol.send_text(message.encode()) - - When the connection isn't open on entry, when the connection is expected - to close on exit, or when an unexpected error happens, terminating the - connection, :meth:`send_context` waits until the connection is closed - then raises :exc:`~websockets.exceptions.ConnectionClosed`. - - """ - # Should we wait until the connection is closed? - wait_for_close = False - # Should we close the socket and raise ConnectionClosed? - raise_close_exc = False - # What exception should we chain ConnectionClosed to? - original_exc: BaseException | None = None - - # Acquire the protocol lock. - with self.protocol_mutex: - if self.protocol.state is expected_state: - # Let the caller interact with the protocol. - try: - yield - except (ProtocolError, ConcurrencyError): - # The protocol state wasn't changed. Exit immediately. - raise - except Exception as exc: - self.logger.error("unexpected internal error", exc_info=True) - # This branch should never run. It's a safety net in case of - # bugs. Since we don't know what happened, we will close the - # connection and raise the exception to the caller. - wait_for_close = False - raise_close_exc = True - original_exc = exc - else: - # Check if the connection is expected to close soon. - if self.protocol.close_expected(): - wait_for_close = True - # If the connection is expected to close soon, set the - # close deadline based on the close timeout. - # Since we tested earlier that protocol.state was OPEN - # (or CONNECTING) and we didn't release protocol_mutex, - # it is certain that self.close_deadline is still None. - assert self.close_deadline is None - self.close_deadline = Deadline(self.close_timeout) - # Write outgoing data to the socket. - try: - self.send_data() - except Exception as exc: - if self.debug: - self.logger.debug("error while sending data", exc_info=True) - # While the only expected exception here is OSError, - # other exceptions would be treated identically. - wait_for_close = False - raise_close_exc = True - original_exc = exc - - else: # self.protocol.state is not expected_state - # Minor layering violation: we assume that the connection - # will be closing soon if it isn't in the expected state. - wait_for_close = True - raise_close_exc = True - - # To avoid a deadlock, release the connection lock by exiting the - # context manager before waiting for recv_events() to terminate. - - # If the connection is expected to close soon and the close timeout - # elapses, close the socket to terminate the connection. - if wait_for_close: - if self.close_deadline is None: - timeout = self.close_timeout - else: - # Thread.join() returns immediately if timeout is negative. - timeout = self.close_deadline.timeout(raise_if_elapsed=False) - self.recv_events_thread.join(timeout) - - if self.recv_events_thread.is_alive(): - # There's no risk to overwrite another error because - # original_exc is never set when wait_for_close is True. - assert original_exc is None - original_exc = TimeoutError("timed out while closing connection") - # Set recv_exc before closing the socket in order to get - # proper exception reporting. - raise_close_exc = True - with self.protocol_mutex: - self.set_recv_exc(original_exc) - - # If an error occurred, close the socket to terminate the connection and - # raise an exception. - if raise_close_exc: - self.close_socket() - # Wait for the protocol state to be CLOSED before accessing close_exc. - self.recv_events_thread.join() - raise self.protocol.close_exc from original_exc - - def send_data(self) -> None: - """ - Send outgoing data. - - This method requires holding protocol_mutex. - - Raises: - OSError: When a socket operations fails. - - """ - assert self.protocol_mutex.locked() - for data in self.protocol.data_to_send(): - if data: - if self.close_deadline is not None: - self.socket.settimeout(self.close_deadline.timeout()) - self.socket.sendall(data) - else: - try: - self.socket.shutdown(socket.SHUT_WR) - except OSError: # socket already closed - pass - - def set_recv_exc(self, exc: BaseException | None) -> None: - """ - Set recv_exc, if not set yet. - - This method requires holding protocol_mutex. - - """ - assert self.protocol_mutex.locked() - if self.recv_exc is None: # pragma: no branch - self.recv_exc = exc - - def close_socket(self) -> None: - """ - Shutdown and close socket. Close message assembler. - - Calling close_socket() guarantees that recv_events() terminates. Indeed, - recv_events() may block only on socket.recv() or on recv_messages.put(). - - """ - # shutdown() is required to interrupt recv() on Linux. - try: - self.socket.shutdown(socket.SHUT_RDWR) - except OSError: - pass # socket is already closed - self.socket.close() - - # Calling protocol.receive_eof() is safe because it's idempotent. - # This guarantees that the protocol state becomes CLOSED. - self.protocol.receive_eof() - assert self.protocol.state is CLOSED - - # Abort recv() with a ConnectionClosed exception. - self.recv_messages.close() diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/messages.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/messages.py deleted file mode 100644 index 8d09053..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/messages.py +++ /dev/null @@ -1,283 +0,0 @@ -from __future__ import annotations - -import codecs -import queue -import threading -from typing import Iterator, cast - -from ..exceptions import ConcurrencyError -from ..frames import OP_BINARY, OP_CONT, OP_TEXT, Frame -from ..typing import Data - - -__all__ = ["Assembler"] - -UTF8Decoder = codecs.getincrementaldecoder("utf-8") - - -class Assembler: - """ - Assemble messages from frames. - - """ - - def __init__(self) -> None: - # Serialize reads and writes -- except for reads via synchronization - # primitives provided by the threading and queue modules. - self.mutex = threading.Lock() - - # We create a latch with two events to synchronize the production of - # frames and the consumption of messages (or frames) without a buffer. - # This design requires a switch between the library thread and the user - # thread for each message; that shouldn't be a performance bottleneck. - - # put() sets this event to tell get() that a message can be fetched. - self.message_complete = threading.Event() - # get() sets this event to let put() that the message was fetched. - self.message_fetched = threading.Event() - - # This flag prevents concurrent calls to get() by user code. - self.get_in_progress = False - # This flag prevents concurrent calls to put() by library code. - self.put_in_progress = False - - # Decoder for text frames, None for binary frames. - self.decoder: codecs.IncrementalDecoder | None = None - - # Buffer of frames belonging to the same message. - self.chunks: list[Data] = [] - - # When switching from "buffering" to "streaming", we use a thread-safe - # queue for transferring frames from the writing thread (library code) - # to the reading thread (user code). We're buffering when chunks_queue - # is None and streaming when it's a SimpleQueue. None is a sentinel - # value marking the end of the message, superseding message_complete. - - # Stream data from frames belonging to the same message. - self.chunks_queue: queue.SimpleQueue[Data | None] | None = None - - # This flag marks the end of the connection. - self.closed = False - - def get(self, timeout: float | None = None) -> Data: - """ - Read the next message. - - :meth:`get` returns a single :class:`str` or :class:`bytes`. - - If the message is fragmented, :meth:`get` waits until the last frame is - received, then it reassembles the message and returns it. To receive - messages frame by frame, use :meth:`get_iter` instead. - - Args: - timeout: If a timeout is provided and elapses before a complete - message is received, :meth:`get` raises :exc:`TimeoutError`. - - Raises: - EOFError: If the stream of frames has ended. - ConcurrencyError: If two threads run :meth:`get` or :meth:`get_iter` - concurrently. - TimeoutError: If a timeout is provided and elapses before a - complete message is received. - - """ - with self.mutex: - if self.closed: - raise EOFError("stream of frames ended") - - if self.get_in_progress: - raise ConcurrencyError("get() or get_iter() is already running") - - self.get_in_progress = True - - # If the message_complete event isn't set yet, release the lock to - # allow put() to run and eventually set it. - # Locking with get_in_progress ensures only one thread can get here. - completed = self.message_complete.wait(timeout) - - with self.mutex: - self.get_in_progress = False - - # Waiting for a complete message timed out. - if not completed: - raise TimeoutError(f"timed out in {timeout:.1f}s") - - # get() was unblocked by close() rather than put(). - if self.closed: - raise EOFError("stream of frames ended") - - assert self.message_complete.is_set() - self.message_complete.clear() - - joiner: Data = b"" if self.decoder is None else "" - # mypy cannot figure out that chunks have the proper type. - message: Data = joiner.join(self.chunks) # type: ignore - - self.chunks = [] - assert self.chunks_queue is None - - assert not self.message_fetched.is_set() - self.message_fetched.set() - - return message - - def get_iter(self) -> Iterator[Data]: - """ - Stream the next message. - - Iterating the return value of :meth:`get_iter` yields a :class:`str` or - :class:`bytes` for each frame in the message. - - The iterator must be fully consumed before calling :meth:`get_iter` or - :meth:`get` again. Else, :exc:`ConcurrencyError` is raised. - - This method only makes sense for fragmented messages. If messages aren't - fragmented, use :meth:`get` instead. - - Raises: - EOFError: If the stream of frames has ended. - ConcurrencyError: If two threads run :meth:`get` or :meth:`get_iter` - concurrently. - - """ - with self.mutex: - if self.closed: - raise EOFError("stream of frames ended") - - if self.get_in_progress: - raise ConcurrencyError("get() or get_iter() is already running") - - chunks = self.chunks - self.chunks = [] - self.chunks_queue = cast( - # Remove quotes around type when dropping Python < 3.9. - "queue.SimpleQueue[Data | None]", - queue.SimpleQueue(), - ) - - # Sending None in chunk_queue supersedes setting message_complete - # when switching to "streaming". If message is already complete - # when the switch happens, put() didn't send None, so we have to. - if self.message_complete.is_set(): - self.chunks_queue.put(None) - - self.get_in_progress = True - - # Locking with get_in_progress ensures only one thread can get here. - chunk: Data | None - for chunk in chunks: - yield chunk - while (chunk := self.chunks_queue.get()) is not None: - yield chunk - - with self.mutex: - self.get_in_progress = False - - # get_iter() was unblocked by close() rather than put(). - if self.closed: - raise EOFError("stream of frames ended") - - assert self.message_complete.is_set() - self.message_complete.clear() - - assert self.chunks == [] - self.chunks_queue = None - - assert not self.message_fetched.is_set() - self.message_fetched.set() - - def put(self, frame: Frame) -> None: - """ - Add ``frame`` to the next message. - - When ``frame`` is the final frame in a message, :meth:`put` waits until - the message is fetched, which can be achieved by calling :meth:`get` or - by fully consuming the return value of :meth:`get_iter`. - - :meth:`put` assumes that the stream of frames respects the protocol. If - it doesn't, the behavior is undefined. - - Raises: - EOFError: If the stream of frames has ended. - ConcurrencyError: If two threads run :meth:`put` concurrently. - - """ - with self.mutex: - if self.closed: - raise EOFError("stream of frames ended") - - if self.put_in_progress: - raise ConcurrencyError("put is already running") - - if frame.opcode is OP_TEXT: - self.decoder = UTF8Decoder(errors="strict") - elif frame.opcode is OP_BINARY: - self.decoder = None - else: - assert frame.opcode is OP_CONT - - data: Data - if self.decoder is not None: - data = self.decoder.decode(frame.data, frame.fin) - else: - data = frame.data - - if self.chunks_queue is None: - self.chunks.append(data) - else: - self.chunks_queue.put(data) - - if not frame.fin: - return - - # Message is complete. Wait until it's fetched to return. - - assert not self.message_complete.is_set() - self.message_complete.set() - - if self.chunks_queue is not None: - self.chunks_queue.put(None) - - assert not self.message_fetched.is_set() - - self.put_in_progress = True - - # Release the lock to allow get() to run and eventually set the event. - # Locking with put_in_progress ensures only one coroutine can get here. - self.message_fetched.wait() - - with self.mutex: - self.put_in_progress = False - - # put() was unblocked by close() rather than get() or get_iter(). - if self.closed: - raise EOFError("stream of frames ended") - - assert self.message_fetched.is_set() - self.message_fetched.clear() - - self.decoder = None - - def close(self) -> None: - """ - End the stream of frames. - - Callling :meth:`close` concurrently with :meth:`get`, :meth:`get_iter`, - or :meth:`put` is safe. They will raise :exc:`EOFError`. - - """ - with self.mutex: - if self.closed: - return - - self.closed = True - - # Unblock get or get_iter. - if self.get_in_progress: - self.message_complete.set() - if self.chunks_queue is not None: - self.chunks_queue.put(None) - - # Unblock put(). - if self.put_in_progress: - self.message_fetched.set() diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/server.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/server.py deleted file mode 100644 index 1b7cbb4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/server.py +++ /dev/null @@ -1,727 +0,0 @@ -from __future__ import annotations - -import hmac -import http -import logging -import os -import selectors -import socket -import ssl as ssl_module -import sys -import threading -import warnings -from types import TracebackType -from typing import Any, Callable, Iterable, Sequence, Tuple, cast - -from ..exceptions import InvalidHeader -from ..extensions.base import ServerExtensionFactory -from ..extensions.permessage_deflate import enable_server_permessage_deflate -from ..frames import CloseCode -from ..headers import ( - build_www_authenticate_basic, - parse_authorization_basic, - validate_subprotocols, -) -from ..http11 import SERVER, Request, Response -from ..protocol import CONNECTING, OPEN, Event -from ..server import ServerProtocol -from ..typing import LoggerLike, Origin, StatusLike, Subprotocol -from .connection import Connection -from .utils import Deadline - - -__all__ = ["serve", "unix_serve", "ServerConnection", "Server", "basic_auth"] - - -class ServerConnection(Connection): - """ - :mod:`threading` implementation of a WebSocket server connection. - - :class:`ServerConnection` provides :meth:`recv` and :meth:`send` methods for - receiving and sending messages. - - It supports iteration to receive messages:: - - for message in websocket: - process(message) - - The iterator exits normally when the connection is closed with close code - 1000 (OK) or 1001 (going away) or without a close code. It raises a - :exc:`~websockets.exceptions.ConnectionClosedError` when the connection is - closed with any other code. - - Args: - socket: Socket connected to a WebSocket client. - protocol: Sans-I/O connection. - close_timeout: Timeout for closing the connection in seconds. - - """ - - def __init__( - self, - socket: socket.socket, - protocol: ServerProtocol, - *, - close_timeout: float | None = 10, - ) -> None: - self.protocol: ServerProtocol - self.request_rcvd = threading.Event() - super().__init__( - socket, - protocol, - close_timeout=close_timeout, - ) - self.username: str # see basic_auth() - - def respond(self, status: StatusLike, text: str) -> Response: - """ - Create a plain text HTTP response. - - ``process_request`` and ``process_response`` may call this method to - return an HTTP response instead of performing the WebSocket opening - handshake. - - You can modify the response before returning it, for example by changing - HTTP headers. - - Args: - status: HTTP status code. - text: HTTP response body; it will be encoded to UTF-8. - - Returns: - HTTP response to send to the client. - - """ - return self.protocol.reject(status, text) - - def handshake( - self, - process_request: ( - Callable[ - [ServerConnection, Request], - Response | None, - ] - | None - ) = None, - process_response: ( - Callable[ - [ServerConnection, Request, Response], - Response | None, - ] - | None - ) = None, - server_header: str | None = SERVER, - timeout: float | None = None, - ) -> None: - """ - Perform the opening handshake. - - """ - if not self.request_rcvd.wait(timeout): - raise TimeoutError("timed out during handshake") - - if self.request is not None: - with self.send_context(expected_state=CONNECTING): - response = None - - if process_request is not None: - try: - response = process_request(self, self.request) - except Exception as exc: - self.protocol.handshake_exc = exc - response = self.protocol.reject( - http.HTTPStatus.INTERNAL_SERVER_ERROR, - ( - "Failed to open a WebSocket connection.\n" - "See server log for more information.\n" - ), - ) - - if response is None: - self.response = self.protocol.accept(self.request) - else: - self.response = response - - if server_header: - self.response.headers["Server"] = server_header - - response = None - - if process_response is not None: - try: - response = process_response(self, self.request, self.response) - except Exception as exc: - self.protocol.handshake_exc = exc - response = self.protocol.reject( - http.HTTPStatus.INTERNAL_SERVER_ERROR, - ( - "Failed to open a WebSocket connection.\n" - "See server log for more information.\n" - ), - ) - - if response is not None: - self.response = response - - self.protocol.send_response(self.response) - - # self.protocol.handshake_exc is always set when the connection is lost - # before receiving a request, when the request cannot be parsed, when - # the handshake encounters an error, or when process_request or - # process_response sends an HTTP response that rejects the handshake. - - if self.protocol.handshake_exc is not None: - raise self.protocol.handshake_exc - - def process_event(self, event: Event) -> None: - """ - Process one incoming event. - - """ - # First event - handshake request. - if self.request is None: - assert isinstance(event, Request) - self.request = event - self.request_rcvd.set() - # Later events - frames. - else: - super().process_event(event) - - def recv_events(self) -> None: - """ - Read incoming data from the socket and process events. - - """ - try: - super().recv_events() - finally: - # If the connection is closed during the handshake, unblock it. - self.request_rcvd.set() - - -class Server: - """ - WebSocket server returned by :func:`serve`. - - This class mirrors the API of :class:`~socketserver.BaseServer`, notably the - :meth:`~socketserver.BaseServer.serve_forever` and - :meth:`~socketserver.BaseServer.shutdown` methods, as well as the context - manager protocol. - - Args: - socket: Server socket listening for new connections. - handler: Handler for one connection. Receives the socket and address - returned by :meth:`~socket.socket.accept`. - logger: Logger for this server. - It defaults to ``logging.getLogger("websockets.server")``. - See the :doc:`logging guide <../../topics/logging>` for details. - - """ - - def __init__( - self, - socket: socket.socket, - handler: Callable[[socket.socket, Any], None], - logger: LoggerLike | None = None, - ) -> None: - self.socket = socket - self.handler = handler - if logger is None: - logger = logging.getLogger("websockets.server") - self.logger = logger - if sys.platform != "win32": - self.shutdown_watcher, self.shutdown_notifier = os.pipe() - - def serve_forever(self) -> None: - """ - See :meth:`socketserver.BaseServer.serve_forever`. - - This method doesn't return. Calling :meth:`shutdown` from another thread - stops the server. - - Typical use:: - - with serve(...) as server: - server.serve_forever() - - """ - poller = selectors.DefaultSelector() - try: - poller.register(self.socket, selectors.EVENT_READ) - except ValueError: # pragma: no cover - # If shutdown() is called before poller.register(), - # the socket is closed and poller.register() raises - # ValueError: Invalid file descriptor: -1 - return - if sys.platform != "win32": - poller.register(self.shutdown_watcher, selectors.EVENT_READ) - - while True: - poller.select() - try: - # If the socket is closed, this will raise an exception and exit - # the loop. So we don't need to check the return value of select(). - sock, addr = self.socket.accept() - except OSError: - break - # Since there isn't a mechanism for tracking connections and waiting - # for them to terminate, we cannot use daemon threads, or else all - # connections would be terminate brutally when closing the server. - thread = threading.Thread(target=self.handler, args=(sock, addr)) - thread.start() - - def shutdown(self) -> None: - """ - See :meth:`socketserver.BaseServer.shutdown`. - - """ - self.socket.close() - if sys.platform != "win32": - os.write(self.shutdown_notifier, b"x") - - def fileno(self) -> int: - """ - See :meth:`socketserver.BaseServer.fileno`. - - """ - return self.socket.fileno() - - def __enter__(self) -> Server: - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_value: BaseException | None, - traceback: TracebackType | None, - ) -> None: - self.shutdown() - - -def __getattr__(name: str) -> Any: - if name == "WebSocketServer": - warnings.warn( # deprecated in 13.0 - 2024-08-20 - "WebSocketServer was renamed to Server", - DeprecationWarning, - ) - return Server - raise AttributeError(f"module {__name__!r} has no attribute {name!r}") - - -def serve( - handler: Callable[[ServerConnection], None], - host: str | None = None, - port: int | None = None, - *, - # TCP/TLS - sock: socket.socket | None = None, - ssl: ssl_module.SSLContext | None = None, - # WebSocket - origins: Sequence[Origin | None] | None = None, - extensions: Sequence[ServerExtensionFactory] | None = None, - subprotocols: Sequence[Subprotocol] | None = None, - select_subprotocol: ( - Callable[ - [ServerConnection, Sequence[Subprotocol]], - Subprotocol | None, - ] - | None - ) = None, - process_request: ( - Callable[ - [ServerConnection, Request], - Response | None, - ] - | None - ) = None, - process_response: ( - Callable[ - [ServerConnection, Request, Response], - Response | None, - ] - | None - ) = None, - server_header: str | None = SERVER, - compression: str | None = "deflate", - # Timeouts - open_timeout: float | None = 10, - close_timeout: float | None = 10, - # Limits - max_size: int | None = 2**20, - # Logging - logger: LoggerLike | None = None, - # Escape hatch for advanced customization - create_connection: type[ServerConnection] | None = None, - **kwargs: Any, -) -> Server: - """ - Create a WebSocket server listening on ``host`` and ``port``. - - Whenever a client connects, the server creates a :class:`ServerConnection`, - performs the opening handshake, and delegates to the ``handler``. - - The handler receives the :class:`ServerConnection` instance, which you can - use to send and receive messages. - - Once the handler completes, either normally or with an exception, the server - performs the closing handshake and closes the connection. - - This function returns a :class:`Server` whose API mirrors - :class:`~socketserver.BaseServer`. Treat it as a context manager to ensure - that it will be closed and call :meth:`~Server.serve_forever` to serve - requests:: - - from websockets.sync.server import serve - - def handler(websocket): - ... - - with serve(handler, ...) as server: - server.serve_forever() - - Args: - handler: Connection handler. It receives the WebSocket connection, - which is a :class:`ServerConnection`, in argument. - host: Network interfaces the server binds to. - See :func:`~socket.create_server` for details. - port: TCP port the server listens on. - See :func:`~socket.create_server` for details. - sock: Preexisting TCP socket. ``sock`` replaces ``host`` and ``port``. - You may call :func:`socket.create_server` to create a suitable TCP - socket. - ssl: Configuration for enabling TLS on the connection. - origins: Acceptable values of the ``Origin`` header, for defending - against Cross-Site WebSocket Hijacking attacks. Include :obj:`None` - in the list if the lack of an origin is acceptable. - extensions: List of supported extensions, in order in which they - should be negotiated and run. - subprotocols: List of supported subprotocols, in order of decreasing - preference. - select_subprotocol: Callback for selecting a subprotocol among - those supported by the client and the server. It receives a - :class:`ServerConnection` (not a - :class:`~websockets.server.ServerProtocol`!) instance and a list of - subprotocols offered by the client. Other than the first argument, - it has the same behavior as the - :meth:`ServerProtocol.select_subprotocol - ` method. - process_request: Intercept the request during the opening handshake. - Return an HTTP response to force the response. Return :obj:`None` to - continue normally. When you force an HTTP 101 Continue response, the - handshake is successful. Else, the connection is aborted. - process_response: Intercept the response during the opening handshake. - Modify the response or return a new HTTP response to force the - response. Return :obj:`None` to continue normally. When you force an - HTTP 101 Continue response, the handshake is successful. Else, the - connection is aborted. - server_header: Value of the ``Server`` response header. - It defaults to ``"Python/x.y.z websockets/X.Y"``. Setting it to - :obj:`None` removes the header. - compression: The "permessage-deflate" extension is enabled by default. - Set ``compression`` to :obj:`None` to disable it. See the - :doc:`compression guide <../../topics/compression>` for details. - open_timeout: Timeout for opening connections in seconds. - :obj:`None` disables the timeout. - close_timeout: Timeout for closing connections in seconds. - :obj:`None` disables the timeout. - max_size: Maximum size of incoming messages in bytes. - :obj:`None` disables the limit. - logger: Logger for this server. - It defaults to ``logging.getLogger("websockets.server")``. See the - :doc:`logging guide <../../topics/logging>` for details. - create_connection: Factory for the :class:`ServerConnection` managing - the connection. Set it to a wrapper or a subclass to customize - connection handling. - - Any other keyword arguments are passed to :func:`~socket.create_server`. - - """ - - # Process parameters - - # Backwards compatibility: ssl used to be called ssl_context. - if ssl is None and "ssl_context" in kwargs: - ssl = kwargs.pop("ssl_context") - warnings.warn( # deprecated in 13.0 - 2024-08-20 - "ssl_context was renamed to ssl", - DeprecationWarning, - ) - - if subprotocols is not None: - validate_subprotocols(subprotocols) - - if compression == "deflate": - extensions = enable_server_permessage_deflate(extensions) - elif compression is not None: - raise ValueError(f"unsupported compression: {compression}") - - if create_connection is None: - create_connection = ServerConnection - - # Bind socket and listen - - # Private APIs for unix_connect() - unix: bool = kwargs.pop("unix", False) - path: str | None = kwargs.pop("path", None) - - if sock is None: - if unix: - if path is None: - raise TypeError("missing path argument") - kwargs.setdefault("family", socket.AF_UNIX) - sock = socket.create_server(path, **kwargs) - else: - sock = socket.create_server((host, port), **kwargs) - else: - if path is not None: - raise TypeError("path and sock arguments are incompatible") - - # Initialize TLS wrapper - - if ssl is not None: - sock = ssl.wrap_socket( - sock, - server_side=True, - # Delay TLS handshake until after we set a timeout on the socket. - do_handshake_on_connect=False, - ) - - # Define request handler - - def conn_handler(sock: socket.socket, addr: Any) -> None: - # Calculate timeouts on the TLS and WebSocket handshakes. - # The TLS timeout must be set on the socket, then removed - # to avoid conflicting with the WebSocket timeout in handshake(). - deadline = Deadline(open_timeout) - - try: - # Disable Nagle algorithm - - if not unix: - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True) - - # Perform TLS handshake - - if ssl is not None: - sock.settimeout(deadline.timeout()) - # mypy cannot figure this out - assert isinstance(sock, ssl_module.SSLSocket) - sock.do_handshake() - sock.settimeout(None) - - # Create a closure to give select_subprotocol access to connection. - protocol_select_subprotocol: ( - Callable[ - [ServerProtocol, Sequence[Subprotocol]], - Subprotocol | None, - ] - | None - ) = None - if select_subprotocol is not None: - - def protocol_select_subprotocol( - protocol: ServerProtocol, - subprotocols: Sequence[Subprotocol], - ) -> Subprotocol | None: - # mypy doesn't know that select_subprotocol is immutable. - assert select_subprotocol is not None - # Ensure this function is only used in the intended context. - assert protocol is connection.protocol - return select_subprotocol(connection, subprotocols) - - # Initialize WebSocket protocol - - protocol = ServerProtocol( - origins=origins, - extensions=extensions, - subprotocols=subprotocols, - select_subprotocol=protocol_select_subprotocol, - max_size=max_size, - logger=logger, - ) - - # Initialize WebSocket connection - - assert create_connection is not None # help mypy - connection = create_connection( - sock, - protocol, - close_timeout=close_timeout, - ) - except Exception: - sock.close() - return - - try: - try: - connection.handshake( - process_request, - process_response, - server_header, - deadline.timeout(), - ) - except TimeoutError: - connection.close_socket() - connection.recv_events_thread.join() - return - except Exception: - connection.logger.error("opening handshake failed", exc_info=True) - connection.close_socket() - connection.recv_events_thread.join() - return - - assert connection.protocol.state is OPEN - try: - handler(connection) - except Exception: - connection.logger.error("connection handler failed", exc_info=True) - connection.close(CloseCode.INTERNAL_ERROR) - else: - connection.close() - - except Exception: # pragma: no cover - # Don't leak sockets on unexpected errors. - sock.close() - - # Initialize server - - return Server(sock, conn_handler, logger) - - -def unix_serve( - handler: Callable[[ServerConnection], None], - path: str | None = None, - **kwargs: Any, -) -> Server: - """ - Create a WebSocket server listening on a Unix socket. - - This function accepts the same keyword arguments as :func:`serve`. - - It's only available on Unix. - - It's useful for deploying a server behind a reverse proxy such as nginx. - - Args: - handler: Connection handler. It receives the WebSocket connection, - which is a :class:`ServerConnection`, in argument. - path: File system path to the Unix socket. - - """ - return serve(handler, unix=True, path=path, **kwargs) - - -def is_credentials(credentials: Any) -> bool: - try: - username, password = credentials - except (TypeError, ValueError): - return False - else: - return isinstance(username, str) and isinstance(password, str) - - -def basic_auth( - realm: str = "", - credentials: tuple[str, str] | Iterable[tuple[str, str]] | None = None, - check_credentials: Callable[[str, str], bool] | None = None, -) -> Callable[[ServerConnection, Request], Response | None]: - """ - Factory for ``process_request`` to enforce HTTP Basic Authentication. - - :func:`basic_auth` is designed to integrate with :func:`serve` as follows:: - - from websockets.sync.server import basic_auth, serve - - with serve( - ..., - process_request=basic_auth( - realm="my dev server", - credentials=("hello", "iloveyou"), - ), - ): - - If authentication succeeds, the connection's ``username`` attribute is set. - If it fails, the server responds with an HTTP 401 Unauthorized status. - - One of ``credentials`` or ``check_credentials`` must be provided; not both. - - Args: - realm: Scope of protection. It should contain only ASCII characters - because the encoding of non-ASCII characters is undefined. Refer to - section 2.2 of :rfc:`7235` for details. - credentials: Hard coded authorized credentials. It can be a - ``(username, password)`` pair or a list of such pairs. - check_credentials: Function that verifies credentials. - It receives ``username`` and ``password`` arguments and returns - whether they're valid. - Raises: - TypeError: If ``credentials`` or ``check_credentials`` is wrong. - - """ - if (credentials is None) == (check_credentials is None): - raise TypeError("provide either credentials or check_credentials") - - if credentials is not None: - if is_credentials(credentials): - credentials_list = [cast(Tuple[str, str], credentials)] - elif isinstance(credentials, Iterable): - credentials_list = list(cast(Iterable[Tuple[str, str]], credentials)) - if not all(is_credentials(item) for item in credentials_list): - raise TypeError(f"invalid credentials argument: {credentials}") - else: - raise TypeError(f"invalid credentials argument: {credentials}") - - credentials_dict = dict(credentials_list) - - def check_credentials(username: str, password: str) -> bool: - try: - expected_password = credentials_dict[username] - except KeyError: - return False - return hmac.compare_digest(expected_password, password) - - assert check_credentials is not None # help mypy - - def process_request( - connection: ServerConnection, - request: Request, - ) -> Response | None: - """ - Perform HTTP Basic Authentication. - - If it succeeds, set the connection's ``username`` attribute and return - :obj:`None`. If it fails, return an HTTP 401 Unauthorized responss. - - """ - try: - authorization = request.headers["Authorization"] - except KeyError: - response = connection.respond( - http.HTTPStatus.UNAUTHORIZED, - "Missing credentials\n", - ) - response.headers["WWW-Authenticate"] = build_www_authenticate_basic(realm) - return response - - try: - username, password = parse_authorization_basic(authorization) - except InvalidHeader: - response = connection.respond( - http.HTTPStatus.UNAUTHORIZED, - "Unsupported credentials\n", - ) - response.headers["WWW-Authenticate"] = build_www_authenticate_basic(realm) - return response - - if not check_credentials(username, password): - response = connection.respond( - http.HTTPStatus.UNAUTHORIZED, - "Invalid credentials\n", - ) - response.headers["WWW-Authenticate"] = build_www_authenticate_basic(realm) - return response - - connection.username = username - return None - - return process_request diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/utils.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/utils.py deleted file mode 100644 index 00bce2c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/sync/utils.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import annotations - -import time - - -__all__ = ["Deadline"] - - -class Deadline: - """ - Manage timeouts across multiple steps. - - Args: - timeout: Time available in seconds or :obj:`None` if there is no limit. - - """ - - def __init__(self, timeout: float | None) -> None: - self.deadline: float | None - if timeout is None: - self.deadline = None - else: - self.deadline = time.monotonic() + timeout - - def timeout(self, *, raise_if_elapsed: bool = True) -> float | None: - """ - Calculate a timeout from a deadline. - - Args: - raise_if_elapsed: Whether to raise :exc:`TimeoutError` - if the deadline lapsed. - - Raises: - TimeoutError: If the deadline lapsed. - - Returns: - Time left in seconds or :obj:`None` if there is no limit. - - """ - if self.deadline is None: - return None - timeout = self.deadline - time.monotonic() - if raise_if_elapsed and timeout <= 0: - raise TimeoutError("timed out") - return timeout diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/typing.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/typing.py deleted file mode 100644 index 447fe79..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/typing.py +++ /dev/null @@ -1,77 +0,0 @@ -from __future__ import annotations - -import http -import logging -import typing -from typing import Any, List, NewType, Optional, Tuple, Union - - -__all__ = [ - "Data", - "LoggerLike", - "StatusLike", - "Origin", - "Subprotocol", - "ExtensionName", - "ExtensionParameter", -] - - -# Public types used in the signature of public APIs - -# Change to str | bytes when dropping Python < 3.10. -Data = Union[str, bytes] -"""Types supported in a WebSocket message: -:class:`str` for a Text_ frame, :class:`bytes` for a Binary_. - -.. _Text: https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 -.. _Binary : https://datatracker.ietf.org/doc/html/rfc6455#section-5.6 - -""" - - -# Change to logging.Logger | ... when dropping Python < 3.10. -if typing.TYPE_CHECKING: - LoggerLike = Union[logging.Logger, logging.LoggerAdapter[Any]] - """Types accepted where a :class:`~logging.Logger` is expected.""" -else: # remove this branch when dropping support for Python < 3.11 - LoggerLike = Union[logging.Logger, logging.LoggerAdapter] - """Types accepted where a :class:`~logging.Logger` is expected.""" - - -# Change to http.HTTPStatus | int when dropping Python < 3.10. -StatusLike = Union[http.HTTPStatus, int] -""" -Types accepted where an :class:`~http.HTTPStatus` is expected.""" - - -Origin = NewType("Origin", str) -"""Value of a ``Origin`` header.""" - - -Subprotocol = NewType("Subprotocol", str) -"""Subprotocol in a ``Sec-WebSocket-Protocol`` header.""" - - -ExtensionName = NewType("ExtensionName", str) -"""Name of a WebSocket extension.""" - -# Change to tuple[str, Optional[str]] when dropping Python < 3.9. -# Change to tuple[str, str | None] when dropping Python < 3.10. -ExtensionParameter = Tuple[str, Optional[str]] -"""Parameter of a WebSocket extension.""" - - -# Private types - -# Change to tuple[.., list[...]] when dropping Python < 3.9. -ExtensionHeader = Tuple[ExtensionName, List[ExtensionParameter]] -"""Extension in a ``Sec-WebSocket-Extensions`` header.""" - - -ConnectionOption = NewType("ConnectionOption", str) -"""Connection option in a ``Connection`` header.""" - - -UpgradeProtocol = NewType("UpgradeProtocol", str) -"""Upgrade protocol in an ``Upgrade`` header.""" diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/uri.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/uri.py deleted file mode 100644 index 16bb3f1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/uri.py +++ /dev/null @@ -1,107 +0,0 @@ -from __future__ import annotations - -import dataclasses -import urllib.parse - -from .exceptions import InvalidURI - - -__all__ = ["parse_uri", "WebSocketURI"] - - -@dataclasses.dataclass -class WebSocketURI: - """ - WebSocket URI. - - Attributes: - secure: :obj:`True` for a ``wss`` URI, :obj:`False` for a ``ws`` URI. - host: Normalized to lower case. - port: Always set even if it's the default. - path: May be empty. - query: May be empty if the URI doesn't include a query component. - username: Available when the URI contains `User Information`_. - password: Available when the URI contains `User Information`_. - - .. _User Information: https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1 - - """ - - secure: bool - host: str - port: int - path: str - query: str - username: str | None = None - password: str | None = None - - @property - def resource_name(self) -> str: - if self.path: - resource_name = self.path - else: - resource_name = "/" - if self.query: - resource_name += "?" + self.query - return resource_name - - @property - def user_info(self) -> tuple[str, str] | None: - if self.username is None: - return None - assert self.password is not None - return (self.username, self.password) - - -# All characters from the gen-delims and sub-delims sets in RFC 3987. -DELIMS = ":/?#[]@!$&'()*+,;=" - - -def parse_uri(uri: str) -> WebSocketURI: - """ - Parse and validate a WebSocket URI. - - Args: - uri: WebSocket URI. - - Returns: - Parsed WebSocket URI. - - Raises: - InvalidURI: If ``uri`` isn't a valid WebSocket URI. - - """ - parsed = urllib.parse.urlparse(uri) - if parsed.scheme not in ["ws", "wss"]: - raise InvalidURI(uri, "scheme isn't ws or wss") - if parsed.hostname is None: - raise InvalidURI(uri, "hostname isn't provided") - if parsed.fragment != "": - raise InvalidURI(uri, "fragment identifier is meaningless") - - secure = parsed.scheme == "wss" - host = parsed.hostname - port = parsed.port or (443 if secure else 80) - path = parsed.path - query = parsed.query - username = parsed.username - password = parsed.password - # urllib.parse.urlparse accepts URLs with a username but without a - # password. This doesn't make sense for HTTP Basic Auth credentials. - if username is not None and password is None: - raise InvalidURI(uri, "username provided without password") - - try: - uri.encode("ascii") - except UnicodeEncodeError: - # Input contains non-ASCII characters. - # It must be an IRI. Convert it to a URI. - host = host.encode("idna").decode() - path = urllib.parse.quote(path, safe=DELIMS) - query = urllib.parse.quote(query, safe=DELIMS) - if username is not None: - assert password is not None - username = urllib.parse.quote(username, safe=DELIMS) - password = urllib.parse.quote(password, safe=DELIMS) - - return WebSocketURI(secure, host, port, path, query, username, password) diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/utils.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/utils.py deleted file mode 100644 index 62d2dc1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/utils.py +++ /dev/null @@ -1,51 +0,0 @@ -from __future__ import annotations - -import base64 -import hashlib -import secrets -import sys - - -__all__ = ["accept_key", "apply_mask"] - - -GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" - - -def generate_key() -> str: - """ - Generate a random key for the Sec-WebSocket-Key header. - - """ - key = secrets.token_bytes(16) - return base64.b64encode(key).decode() - - -def accept_key(key: str) -> str: - """ - Compute the value of the Sec-WebSocket-Accept header. - - Args: - key: Value of the Sec-WebSocket-Key header. - - """ - sha1 = hashlib.sha1((key + GUID).encode()).digest() - return base64.b64encode(sha1).decode() - - -def apply_mask(data: bytes, mask: bytes) -> bytes: - """ - Apply masking to the data of a WebSocket message. - - Args: - data: Data to mask. - mask: 4-bytes mask. - - """ - if len(mask) != 4: - raise ValueError("mask must contain 4 bytes") - - data_int = int.from_bytes(data, sys.byteorder) - mask_repeated = mask * (len(data) // 4) + mask[: len(data) % 4] - mask_int = int.from_bytes(mask_repeated, sys.byteorder) - return (data_int ^ mask_int).to_bytes(len(data), sys.byteorder) diff --git a/plotter-app/venv/lib/python3.8/site-packages/websockets/version.py b/plotter-app/venv/lib/python3.8/site-packages/websockets/version.py deleted file mode 100644 index 00b0a98..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/websockets/version.py +++ /dev/null @@ -1,92 +0,0 @@ -from __future__ import annotations - -import importlib.metadata - - -__all__ = ["tag", "version", "commit"] - - -# ========= =========== =================== -# release development -# ========= =========== =================== -# tag X.Y X.Y (upcoming) -# version X.Y X.Y.dev1+g5678cde -# commit X.Y 5678cde -# ========= =========== =================== - - -# When tagging a release, set `released = True`. -# After tagging a release, set `released = False` and increment `tag`. - -released = True - -tag = version = commit = "13.1" - - -if not released: # pragma: no cover - import pathlib - import re - import subprocess - - def get_version(tag: str) -> str: - # Since setup.py executes the contents of src/websockets/version.py, - # __file__ can point to either of these two files. - file_path = pathlib.Path(__file__) - root_dir = file_path.parents[0 if file_path.name == "setup.py" else 2] - - # Read version from package metadata if it is installed. - try: - version = importlib.metadata.version("websockets") - except ImportError: - pass - else: - # Check that this file belongs to the installed package. - files = importlib.metadata.files("websockets") - if files: - version_files = [f for f in files if f.name == file_path.name] - if version_files: - version_file = version_files[0] - if version_file.locate() == file_path: - return version - - # Read version from git if available. - try: - description = subprocess.run( - ["git", "describe", "--dirty", "--tags", "--long"], - capture_output=True, - cwd=root_dir, - timeout=1, - check=True, - text=True, - ).stdout.strip() - # subprocess.run raises FileNotFoundError if git isn't on $PATH. - except ( - FileNotFoundError, - subprocess.CalledProcessError, - subprocess.TimeoutExpired, - ): - pass - else: - description_re = r"[0-9.]+-([0-9]+)-(g[0-9a-f]{7,}(?:-dirty)?)" - match = re.fullmatch(description_re, description) - if match is None: - raise ValueError(f"Unexpected git description: {description}") - distance, remainder = match.groups() - remainder = remainder.replace("-", ".") # required by PEP 440 - return f"{tag}.dev{distance}+{remainder}" - - # Avoid crashing if the development version cannot be determined. - return f"{tag}.dev0+gunknown" - - version = get_version(tag) - - def get_commit(tag: str, version: str) -> str: - # Extract commit from version, falling back to tag if not available. - version_re = r"[0-9.]+\.dev[0-9]+\+g([0-9a-f]{7,}|unknown)(?:\.dirty)?" - match = re.fullmatch(version_re, version) - if match is None: - raise ValueError(f"Unexpected version: {version}") - (commit,) = match.groups() - return tag if commit == "unknown" else commit - - commit = get_commit(tag, version) diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug-3.0.6.dist-info/INSTALLER b/plotter-app/venv/lib/python3.8/site-packages/werkzeug-3.0.6.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug-3.0.6.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug-3.0.6.dist-info/LICENSE.txt b/plotter-app/venv/lib/python3.8/site-packages/werkzeug-3.0.6.dist-info/LICENSE.txt deleted file mode 100644 index c37cae4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug-3.0.6.dist-info/LICENSE.txt +++ /dev/null @@ -1,28 +0,0 @@ -Copyright 2007 Pallets - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug-3.0.6.dist-info/METADATA b/plotter-app/venv/lib/python3.8/site-packages/werkzeug-3.0.6.dist-info/METADATA deleted file mode 100644 index b7cfe0a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug-3.0.6.dist-info/METADATA +++ /dev/null @@ -1,99 +0,0 @@ -Metadata-Version: 2.1 -Name: Werkzeug -Version: 3.0.6 -Summary: The comprehensive WSGI web application library. -Maintainer-email: Pallets -Requires-Python: >=3.8 -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 :: Internet :: WWW/HTTP :: WSGI -Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application -Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware -Classifier: Topic :: Software Development :: Libraries :: Application Frameworks -Classifier: Typing :: Typed -Requires-Dist: MarkupSafe>=2.1.1 -Requires-Dist: watchdog>=2.3 ; extra == "watchdog" -Project-URL: Changes, https://werkzeug.palletsprojects.com/changes/ -Project-URL: Chat, https://discord.gg/pallets -Project-URL: Documentation, https://werkzeug.palletsprojects.com/ -Project-URL: Donate, https://palletsprojects.com/donate -Project-URL: Issue Tracker, https://github.com/pallets/werkzeug/issues/ -Project-URL: Source Code, https://github.com/pallets/werkzeug/ -Provides-Extra: watchdog - -# Werkzeug - -*werkzeug* German noun: "tool". Etymology: *werk* ("work"), *zeug* ("stuff") - -Werkzeug is a comprehensive [WSGI][] web application library. It began as -a simple collection of various utilities for WSGI applications and has -become one of the most advanced WSGI utility libraries. - -It includes: - -- An interactive debugger that allows inspecting stack traces and - source code in the browser with an interactive interpreter for any - frame in the stack. -- A full-featured request object with objects to interact with - headers, query args, form data, files, and cookies. -- A response object that can wrap other WSGI applications and handle - streaming data. -- A routing system for matching URLs to endpoints and generating URLs - for endpoints, with an extensible system for capturing variables - from URLs. -- HTTP utilities to handle entity tags, cache control, dates, user - agents, cookies, files, and more. -- A threaded WSGI server for use while developing applications - locally. -- A test client for simulating HTTP requests during testing without - requiring running a server. - -Werkzeug doesn't enforce any dependencies. It is up to the developer to -choose a template engine, database adapter, and even how to handle -requests. It can be used to build all sorts of end user applications -such as blogs, wikis, or bulletin boards. - -[Flask][] wraps Werkzeug, using it to handle the details of WSGI while -providing more structure and patterns for defining powerful -applications. - -[WSGI]: https://wsgi.readthedocs.io/en/latest/ -[Flask]: https://www.palletsprojects.com/p/flask/ - - -## A Simple Example - -```python -# save this as app.py -from werkzeug.wrappers import Request, Response - -@Request.application -def application(request: Request) -> Response: - return Response("Hello, World!") - -if __name__ == "__main__": - from werkzeug.serving import run_simple - run_simple("127.0.0.1", 5000, application) -``` - -``` -$ python -m app - * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) -``` - - -## Donate - -The Pallets organization develops and supports Werkzeug and other -popular packages. In order to grow the community of contributors and -users, and allow the maintainers to devote more time to the projects, -[please donate today][]. - -[please donate today]: https://palletsprojects.com/donate - diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug-3.0.6.dist-info/RECORD b/plotter-app/venv/lib/python3.8/site-packages/werkzeug-3.0.6.dist-info/RECORD deleted file mode 100644 index 1dc793c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug-3.0.6.dist-info/RECORD +++ /dev/null @@ -1,125 +0,0 @@ -werkzeug-3.0.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -werkzeug-3.0.6.dist-info/LICENSE.txt,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 -werkzeug-3.0.6.dist-info/METADATA,sha256=im31HsOnwlqXdpNMK9QeIqZNo8hRiVBl7pCusz3C8sU,3682 -werkzeug-3.0.6.dist-info/RECORD,, -werkzeug-3.0.6.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 -werkzeug/__init__.py,sha256=HX_PSY5E2vtVlD3R4YblwBRCjg7j3Tlm3LASbYqOSkU,727 -werkzeug/__pycache__/__init__.cpython-38.pyc,, -werkzeug/__pycache__/_internal.cpython-38.pyc,, -werkzeug/__pycache__/_reloader.cpython-38.pyc,, -werkzeug/__pycache__/exceptions.cpython-38.pyc,, -werkzeug/__pycache__/formparser.cpython-38.pyc,, -werkzeug/__pycache__/http.cpython-38.pyc,, -werkzeug/__pycache__/local.cpython-38.pyc,, -werkzeug/__pycache__/security.cpython-38.pyc,, -werkzeug/__pycache__/serving.cpython-38.pyc,, -werkzeug/__pycache__/test.cpython-38.pyc,, -werkzeug/__pycache__/testapp.cpython-38.pyc,, -werkzeug/__pycache__/urls.cpython-38.pyc,, -werkzeug/__pycache__/user_agent.cpython-38.pyc,, -werkzeug/__pycache__/utils.cpython-38.pyc,, -werkzeug/__pycache__/wsgi.cpython-38.pyc,, -werkzeug/_internal.py,sha256=su1olkbHMkzt0VKcEkPLCha8sdVzXNBuqW6YVpp8GHg,5545 -werkzeug/_reloader.py,sha256=QuMO-UwuD-cHsUpwSEUr7iYNbv_ziHFhgNDAiv25J80,15429 -werkzeug/datastructures/__init__.py,sha256=yzBdOT9DdK3nraNG49pA3bVsvtPPLx2-t2N8ZmuAd9w,1900 -werkzeug/datastructures/__pycache__/__init__.cpython-38.pyc,, -werkzeug/datastructures/__pycache__/accept.cpython-38.pyc,, -werkzeug/datastructures/__pycache__/auth.cpython-38.pyc,, -werkzeug/datastructures/__pycache__/cache_control.cpython-38.pyc,, -werkzeug/datastructures/__pycache__/csp.cpython-38.pyc,, -werkzeug/datastructures/__pycache__/etag.cpython-38.pyc,, -werkzeug/datastructures/__pycache__/file_storage.cpython-38.pyc,, -werkzeug/datastructures/__pycache__/headers.cpython-38.pyc,, -werkzeug/datastructures/__pycache__/mixins.cpython-38.pyc,, -werkzeug/datastructures/__pycache__/range.cpython-38.pyc,, -werkzeug/datastructures/__pycache__/structures.cpython-38.pyc,, -werkzeug/datastructures/accept.py,sha256=CuCvBAxNzbt4QUb17rH986vvOVGURFUjo0DX2PQy_yI,10670 -werkzeug/datastructures/accept.pyi,sha256=6P114gncjZoy-i_n_3OQy2nJVwjEAIe7PcBxKYqCEfc,1917 -werkzeug/datastructures/auth.py,sha256=tZz0wZ1sIpIcAQoEAVhrUvy8M3kqKvIytmvGvwkAdxo,10021 -werkzeug/datastructures/cache_control.py,sha256=RTUipZev50s-1TAn2rYGZrytm_6IOIxQd67fkR5bNF0,6043 -werkzeug/datastructures/cache_control.pyi,sha256=NI5myF8f4yzgiqOHJANgp6XtT8SGCWI_GBp5JuH3NIs,3870 -werkzeug/datastructures/csp.py,sha256=DAOAO266LK0JKbvlG80bbkAgfrNsnU9HBoz-FdIYNdo,3244 -werkzeug/datastructures/csp.pyi,sha256=AmDWiZU4rrJA4SZmyMNI1L5PLdIfJsI5Li9r5lE1q6M,5765 -werkzeug/datastructures/etag.py,sha256=JsyI-yXayF-hQu26MyFzbHFIZsaQ6odj3RZO_jF-_cc,2913 -werkzeug/datastructures/etag.pyi,sha256=N9cuUBrZnxHmsbW0BBmjKW-djNY7WKbI6t_WopB8Zo0,1047 -werkzeug/datastructures/file_storage.py,sha256=ePeMtr65s_1_sunXMv_SBOiFof5CX5BepYv5_W16fZk,6184 -werkzeug/datastructures/file_storage.pyi,sha256=PvUx7s2U3ifIf2YxMUhFtZFdkLFderInKG1U3VWwM9E,1457 -werkzeug/datastructures/headers.py,sha256=97-P-LgzterxEwxLbQsBEGiZpCOAXzZ7fExXXd4uH-o,17286 -werkzeug/datastructures/headers.pyi,sha256=66Gh9DbD8QNpLRBOuer4DMCj12csddHrcgxiJPLE5n8,4237 -werkzeug/datastructures/mixins.py,sha256=-IQSQ70UOMQlqtJEIyyhplOd4obaTOfzGvka-cunCtM,5337 -werkzeug/datastructures/mixins.pyi,sha256=Axe16elbs9zSOK9IuXIGs08ukgqSSPCxXFEjB_ACYSM,4189 -werkzeug/datastructures/range.py,sha256=JXSDPseG7iH5giJp3R1SnQC_SqQp634M8Iv6QTsbTxM,5669 -werkzeug/datastructures/range.pyi,sha256=bsM61iNp86gT2lyN0F_Dqg8xsnfPerdmElipuHppiJQ,1792 -werkzeug/datastructures/structures.py,sha256=8nRqvwHM8moZj_fEaxOqF-N7lguoXgnNJeT2l9LX7xA,31917 -werkzeug/datastructures/structures.pyi,sha256=9NeGm8NDS-x3XmE2ZP9676tKvQfo5G9GGvIlfV4v3aY,8237 -werkzeug/debug/__init__.py,sha256=80vaavVZYwV21ItrsOLQudEUi2uKDlxcjm5cCmeU1u4,20016 -werkzeug/debug/__pycache__/__init__.cpython-38.pyc,, -werkzeug/debug/__pycache__/console.cpython-38.pyc,, -werkzeug/debug/__pycache__/repr.cpython-38.pyc,, -werkzeug/debug/__pycache__/tbtools.cpython-38.pyc,, -werkzeug/debug/console.py,sha256=t4hZ0Qg1p6Uu2MWimqoMDi7S3WYZvLMjnc8v_dPaxAo,6089 -werkzeug/debug/repr.py,sha256=iHMYny8whiiMDasvUqj0nm4-1VHVvwe697KleiZVK1s,9303 -werkzeug/debug/shared/ICON_LICENSE.md,sha256=DhA6Y1gUl5Jwfg0NFN9Rj4VWITt8tUx0IvdGf0ux9-s,222 -werkzeug/debug/shared/console.png,sha256=bxax6RXXlvOij_KeqvSNX0ojJf83YbnZ7my-3Gx9w2A,507 -werkzeug/debug/shared/debugger.js,sha256=SRL9YZ9FTVngaYD-INQNincEVdZ-kBHa_-VJx0U7-rg,10068 -werkzeug/debug/shared/less.png,sha256=-4-kNRaXJSONVLahrQKUxMwXGm9R4OnZ9SxDGpHlIR4,191 -werkzeug/debug/shared/more.png,sha256=GngN7CioHQoV58rH6ojnkYi8c_qED2Aka5FO5UXrReY,200 -werkzeug/debug/shared/style.css,sha256=-xSxzUEZGw_IqlDR5iZxitNl8LQUjBM-_Y4UAvXVH8g,6078 -werkzeug/debug/tbtools.py,sha256=p25f0gP9V2GCzwxJxdxMcbTz0GkcG7_DkN8tFBLkP9k,13553 -werkzeug/exceptions.py,sha256=SX3MUTqvWVyA9SnfMPxowNPu5beR9DyrmbUJ4AD2XT0,26160 -werkzeug/formparser.py,sha256=mkrP8j4CYxthuYtNRPsSn_xxzspLLJRlcjgpPNAGBbo,15849 -werkzeug/http.py,sha256=18eD84Lys8tIQVyzlSfaxSMmkU_utOEFoND_kHhhfcM,44452 -werkzeug/local.py,sha256=KUFuAm8BAayQouzVg0MGqW_hiwY8Z_lY5l7d1Scvsx8,22492 -werkzeug/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -werkzeug/middleware/__pycache__/__init__.cpython-38.pyc,, -werkzeug/middleware/__pycache__/dispatcher.cpython-38.pyc,, -werkzeug/middleware/__pycache__/http_proxy.cpython-38.pyc,, -werkzeug/middleware/__pycache__/lint.cpython-38.pyc,, -werkzeug/middleware/__pycache__/profiler.cpython-38.pyc,, -werkzeug/middleware/__pycache__/proxy_fix.cpython-38.pyc,, -werkzeug/middleware/__pycache__/shared_data.cpython-38.pyc,, -werkzeug/middleware/dispatcher.py,sha256=zWN5_lqJr_sc9UDv-PPoSlDHN_zR33z6B74F_4Cxpo8,2602 -werkzeug/middleware/http_proxy.py,sha256=sdk-V6GoZ6aMny-D0QNKNf5MWD2OTO3AGbBg6upp4Hc,7834 -werkzeug/middleware/lint.py,sha256=lwsZhyDNTnsNr4D8dqsgG8Akp7YP9D_X49SCiZucE04,14478 -werkzeug/middleware/profiler.py,sha256=1ZAHlDeYNdhgp8THOXkV5lgmcLl307phAr2Ufy30-lY,5562 -werkzeug/middleware/proxy_fix.py,sha256=n-HW-MRWJquCIhmqiZKoGdbbEeHuWJqPRHhFpuj4pzY,6755 -werkzeug/middleware/shared_data.py,sha256=G28bG3rzrP6R0kODOsbFfWOj5Xcc_qIOVu1VNVl8iW0,9546 -werkzeug/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -werkzeug/routing/__init__.py,sha256=d8TRxsk24IWu2BdoOYUfL--deolHwiGVCBJqLoEe3YM,4820 -werkzeug/routing/__pycache__/__init__.cpython-38.pyc,, -werkzeug/routing/__pycache__/converters.cpython-38.pyc,, -werkzeug/routing/__pycache__/exceptions.cpython-38.pyc,, -werkzeug/routing/__pycache__/map.cpython-38.pyc,, -werkzeug/routing/__pycache__/matcher.cpython-38.pyc,, -werkzeug/routing/__pycache__/rules.cpython-38.pyc,, -werkzeug/routing/converters.py,sha256=iqpee_mAjr1oGbq0etujYF9PiDv5U7DgNkARHXnMId0,7297 -werkzeug/routing/exceptions.py,sha256=wNBiUmUk4OtFOpbdDSr7KKKUjH7yn84JqwBicUup8p8,4846 -werkzeug/routing/map.py,sha256=mEXlHOyinkg1Jtx5L0UDYsvoX4eVLiEuEVQzD5LVAz8,36515 -werkzeug/routing/matcher.py,sha256=nfBbl37eGAkZ1dQlumshFcPuyfggmFjPuSSQOE6GuYs,7849 -werkzeug/routing/rules.py,sha256=eGi6PD-COG2As_HY0nAw-nxYxLTH0FsuqRaSy8d9FjQ,32510 -werkzeug/sansio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -werkzeug/sansio/__pycache__/__init__.cpython-38.pyc,, -werkzeug/sansio/__pycache__/http.cpython-38.pyc,, -werkzeug/sansio/__pycache__/multipart.cpython-38.pyc,, -werkzeug/sansio/__pycache__/request.cpython-38.pyc,, -werkzeug/sansio/__pycache__/response.cpython-38.pyc,, -werkzeug/sansio/__pycache__/utils.cpython-38.pyc,, -werkzeug/sansio/http.py,sha256=_5fVKoogLUyNH5O2BnKty6dFB1p4REBZieJ4vYoOUOA,5370 -werkzeug/sansio/multipart.py,sha256=fMGgY9CvMDRgNgKozz2cBIp0iN2EME2IIOBh306_Ecs,11637 -werkzeug/sansio/request.py,sha256=MI59ROX1P_Y6F4FkCLjaV9hwPEXE7aTTqaVphiTw4UA,19983 -werkzeug/sansio/response.py,sha256=uhKYuDy5-Q5v0Mk5VIxiF-Xob9vfGdDzWiJG7J7MYYc,27585 -werkzeug/sansio/utils.py,sha256=Y7zkEmIvBLtVvgwSdtBhFpGqCclBtYx7GUhckiRSyhI,4957 -werkzeug/security.py,sha256=W56YSXPBE6H5TkyhnPmjk1PUo9XCV63CCw9I2CHgGzQ,5479 -werkzeug/serving.py,sha256=Yb4WgEtaLY2QK0X-_CknmIgrnoraGP2DbZFbQDuv_QQ,39859 -werkzeug/test.py,sha256=kMEWtC_bZ5LqvBya-Pvtq1Jvtb4RR_t7pBp27_4JpJo,52782 -werkzeug/testapp.py,sha256=5_IS5Dh_WfWfNcTLmbydj01lomgcKA_4l9PPCNZnmdI,6332 -werkzeug/urls.py,sha256=XyNKwHvK5IC37-wuIDMYWkiCJ3yLTLGv7wn2GF3ndqI,6430 -werkzeug/user_agent.py,sha256=lSlLYKCcbzCUSkbdAoO8zPk2UR-8Mdn6iu_iA2kYPBA,1416 -werkzeug/utils.py,sha256=6iV_-JdFaLXG6bCR3FMSMyUY0HCnsdzlKirANavAXkk,24699 -werkzeug/wrappers/__init__.py,sha256=b78jCM8x96kJUGLZ5FYFR3zlK-3pnFAmP9RJIGU0ses,138 -werkzeug/wrappers/__pycache__/__init__.cpython-38.pyc,, -werkzeug/wrappers/__pycache__/request.cpython-38.pyc,, -werkzeug/wrappers/__pycache__/response.cpython-38.pyc,, -werkzeug/wrappers/request.py,sha256=dvsuukldNLz5tiwyrbcethow903DA4wmQ7McR0RQgjk,24645 -werkzeug/wrappers/response.py,sha256=u6zg7VpNYrCeEjpIgf8VqgfaSi9yR_9wi9ly2uudglg,32459 -werkzeug/wsgi.py,sha256=P7jB0VpG6X6miies4uk7Zgm7NVm4Yz8Ra6Inr5q_FMs,20894 diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug-3.0.6.dist-info/WHEEL b/plotter-app/venv/lib/python3.8/site-packages/werkzeug-3.0.6.dist-info/WHEEL deleted file mode 100644 index 3b5e64b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug-3.0.6.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: flit 3.9.0 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__init__.py deleted file mode 100644 index 57cb753..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -from __future__ import annotations - -import typing as t - -from .serving import run_simple as run_simple -from .test import Client as Client -from .wrappers import Request as Request -from .wrappers import Response as Response - - -def __getattr__(name: str) -> t.Any: - if name == "__version__": - import importlib.metadata - import warnings - - warnings.warn( - "The '__version__' attribute is deprecated and will be removed in" - " Werkzeug 3.1. Use feature detection or" - " 'importlib.metadata.version(\"werkzeug\")' instead.", - DeprecationWarning, - stacklevel=2, - ) - return importlib.metadata.version("werkzeug") - - raise AttributeError(name) diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 7cc0022..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/_internal.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/_internal.cpython-38.pyc deleted file mode 100644 index 440de0c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/_internal.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/_reloader.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/_reloader.cpython-38.pyc deleted file mode 100644 index 215c598..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/_reloader.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/exceptions.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/exceptions.cpython-38.pyc deleted file mode 100644 index 0c01860..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/exceptions.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/formparser.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/formparser.cpython-38.pyc deleted file mode 100644 index 711407b..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/formparser.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/http.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/http.cpython-38.pyc deleted file mode 100644 index 3395348..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/http.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/local.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/local.cpython-38.pyc deleted file mode 100644 index 9b532b1..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/local.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/security.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/security.cpython-38.pyc deleted file mode 100644 index 8f28544..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/security.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/serving.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/serving.cpython-38.pyc deleted file mode 100644 index 702aa6f..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/serving.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/test.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/test.cpython-38.pyc deleted file mode 100644 index b6e9e8d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/test.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/testapp.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/testapp.cpython-38.pyc deleted file mode 100644 index aaf1d30..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/testapp.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/urls.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/urls.cpython-38.pyc deleted file mode 100644 index 16406be..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/urls.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/user_agent.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/user_agent.cpython-38.pyc deleted file mode 100644 index 03a6865..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/user_agent.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/utils.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/utils.cpython-38.pyc deleted file mode 100644 index 4212063..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/utils.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/wsgi.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/wsgi.cpython-38.pyc deleted file mode 100644 index 4529773..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/wsgi.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/_internal.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/_internal.py deleted file mode 100644 index 7dd2fbc..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/_internal.py +++ /dev/null @@ -1,211 +0,0 @@ -from __future__ import annotations - -import logging -import re -import sys -import typing as t -from datetime import datetime -from datetime import timezone - -if t.TYPE_CHECKING: - from _typeshed.wsgi import WSGIEnvironment - - from .wrappers.request import Request - -_logger: logging.Logger | None = None - - -class _Missing: - def __repr__(self) -> str: - return "no value" - - def __reduce__(self) -> str: - return "_missing" - - -_missing = _Missing() - - -def _wsgi_decoding_dance(s: str) -> str: - return s.encode("latin1").decode(errors="replace") - - -def _wsgi_encoding_dance(s: str) -> str: - return s.encode().decode("latin1") - - -def _get_environ(obj: WSGIEnvironment | Request) -> WSGIEnvironment: - env = getattr(obj, "environ", obj) - assert isinstance( - env, dict - ), f"{type(obj).__name__!r} is not a WSGI environment (has to be a dict)" - return env - - -def _has_level_handler(logger: logging.Logger) -> bool: - """Check if there is a handler in the logging chain that will handle - the given logger's effective level. - """ - level = logger.getEffectiveLevel() - current = logger - - while current: - if any(handler.level <= level for handler in current.handlers): - return True - - if not current.propagate: - break - - current = current.parent # type: ignore - - return False - - -class _ColorStreamHandler(logging.StreamHandler): # type: ignore[type-arg] - """On Windows, wrap stream with Colorama for ANSI style support.""" - - def __init__(self) -> None: - try: - import colorama - except ImportError: - stream = None - else: - stream = colorama.AnsiToWin32(sys.stderr) - - super().__init__(stream) - - -def _log(type: str, message: str, *args: t.Any, **kwargs: t.Any) -> None: - """Log a message to the 'werkzeug' logger. - - The logger is created the first time it is needed. If there is no - level set, it is set to :data:`logging.INFO`. If there is no handler - for the logger's effective level, a :class:`logging.StreamHandler` - is added. - """ - global _logger - - if _logger is None: - _logger = logging.getLogger("werkzeug") - - if _logger.level == logging.NOTSET: - _logger.setLevel(logging.INFO) - - if not _has_level_handler(_logger): - _logger.addHandler(_ColorStreamHandler()) - - getattr(_logger, type)(message.rstrip(), *args, **kwargs) - - -@t.overload -def _dt_as_utc(dt: None) -> None: ... - - -@t.overload -def _dt_as_utc(dt: datetime) -> datetime: ... - - -def _dt_as_utc(dt: datetime | None) -> datetime | None: - if dt is None: - return dt - - if dt.tzinfo is None: - return dt.replace(tzinfo=timezone.utc) - elif dt.tzinfo != timezone.utc: - return dt.astimezone(timezone.utc) - - return dt - - -_TAccessorValue = t.TypeVar("_TAccessorValue") - - -class _DictAccessorProperty(t.Generic[_TAccessorValue]): - """Baseclass for `environ_property` and `header_property`.""" - - read_only = False - - def __init__( - self, - name: str, - default: _TAccessorValue | None = None, - load_func: t.Callable[[str], _TAccessorValue] | None = None, - dump_func: t.Callable[[_TAccessorValue], str] | None = None, - read_only: bool | None = None, - doc: str | None = None, - ) -> None: - self.name = name - self.default = default - self.load_func = load_func - self.dump_func = dump_func - if read_only is not None: - self.read_only = read_only - self.__doc__ = doc - - def lookup(self, instance: t.Any) -> t.MutableMapping[str, t.Any]: - raise NotImplementedError - - @t.overload - def __get__( - self, instance: None, owner: type - ) -> _DictAccessorProperty[_TAccessorValue]: ... - - @t.overload - def __get__(self, instance: t.Any, owner: type) -> _TAccessorValue: ... - - def __get__( - self, instance: t.Any | None, owner: type - ) -> _TAccessorValue | _DictAccessorProperty[_TAccessorValue]: - if instance is None: - return self - - storage = self.lookup(instance) - - if self.name not in storage: - return self.default # type: ignore - - value = storage[self.name] - - if self.load_func is not None: - try: - return self.load_func(value) - except (ValueError, TypeError): - return self.default # type: ignore - - return value # type: ignore - - def __set__(self, instance: t.Any, value: _TAccessorValue) -> None: - if self.read_only: - raise AttributeError("read only property") - - if self.dump_func is not None: - self.lookup(instance)[self.name] = self.dump_func(value) - else: - self.lookup(instance)[self.name] = value - - def __delete__(self, instance: t.Any) -> None: - if self.read_only: - raise AttributeError("read only property") - - self.lookup(instance).pop(self.name, None) - - def __repr__(self) -> str: - return f"<{type(self).__name__} {self.name}>" - - -_plain_int_re = re.compile(r"-?\d+", re.ASCII) - - -def _plain_int(value: str) -> int: - """Parse an int only if it is only ASCII digits and ``-``. - - This disallows ``+``, ``_``, and non-ASCII digits, which are accepted by ``int`` but - are not allowed in HTTP header values. - - Any leading or trailing whitespace is stripped - """ - value = value.strip() - if _plain_int_re.fullmatch(value) is None: - raise ValueError - - return int(value) diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/_reloader.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/_reloader.py deleted file mode 100644 index 8fd50b9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/_reloader.py +++ /dev/null @@ -1,471 +0,0 @@ -from __future__ import annotations - -import fnmatch -import os -import subprocess -import sys -import threading -import time -import typing as t -from itertools import chain -from pathlib import PurePath - -from ._internal import _log - -# The various system prefixes where imports are found. Base values are -# different when running in a virtualenv. All reloaders will ignore the -# base paths (usually the system installation). The stat reloader won't -# scan the virtualenv paths, it will only include modules that are -# already imported. -_ignore_always = tuple({sys.base_prefix, sys.base_exec_prefix}) -prefix = {*_ignore_always, sys.prefix, sys.exec_prefix} - -if hasattr(sys, "real_prefix"): - # virtualenv < 20 - prefix.add(sys.real_prefix) - -_stat_ignore_scan = tuple(prefix) -del prefix -_ignore_common_dirs = { - "__pycache__", - ".git", - ".hg", - ".tox", - ".nox", - ".pytest_cache", - ".mypy_cache", -} - - -def _iter_module_paths() -> t.Iterator[str]: - """Find the filesystem paths associated with imported modules.""" - # List is in case the value is modified by the app while updating. - for module in list(sys.modules.values()): - name = getattr(module, "__file__", None) - - if name is None or name.startswith(_ignore_always): - continue - - while not os.path.isfile(name): - # Zip file, find the base file without the module path. - old = name - name = os.path.dirname(name) - - if name == old: # skip if it was all directories somehow - break - else: - yield name - - -def _remove_by_pattern(paths: set[str], exclude_patterns: set[str]) -> None: - for pattern in exclude_patterns: - paths.difference_update(fnmatch.filter(paths, pattern)) - - -def _find_stat_paths( - extra_files: set[str], exclude_patterns: set[str] -) -> t.Iterable[str]: - """Find paths for the stat reloader to watch. Returns imported - module files, Python files under non-system paths. Extra files and - Python files under extra directories can also be scanned. - - System paths have to be excluded for efficiency. Non-system paths, - such as a project root or ``sys.path.insert``, should be the paths - of interest to the user anyway. - """ - paths = set() - - for path in chain(list(sys.path), extra_files): - path = os.path.abspath(path) - - if os.path.isfile(path): - # zip file on sys.path, or extra file - paths.add(path) - continue - - parent_has_py = {os.path.dirname(path): True} - - for root, dirs, files in os.walk(path): - # Optimizations: ignore system prefixes, __pycache__ will - # have a py or pyc module at the import path, ignore some - # common known dirs such as version control and tool caches. - if ( - root.startswith(_stat_ignore_scan) - or os.path.basename(root) in _ignore_common_dirs - ): - dirs.clear() - continue - - has_py = False - - for name in files: - if name.endswith((".py", ".pyc")): - has_py = True - paths.add(os.path.join(root, name)) - - # Optimization: stop scanning a directory if neither it nor - # its parent contained Python files. - if not (has_py or parent_has_py[os.path.dirname(root)]): - dirs.clear() - continue - - parent_has_py[root] = has_py - - paths.update(_iter_module_paths()) - _remove_by_pattern(paths, exclude_patterns) - return paths - - -def _find_watchdog_paths( - extra_files: set[str], exclude_patterns: set[str] -) -> t.Iterable[str]: - """Find paths for the stat reloader to watch. Looks at the same - sources as the stat reloader, but watches everything under - directories instead of individual files. - """ - dirs = set() - - for name in chain(list(sys.path), extra_files): - name = os.path.abspath(name) - - if os.path.isfile(name): - name = os.path.dirname(name) - - dirs.add(name) - - for name in _iter_module_paths(): - dirs.add(os.path.dirname(name)) - - _remove_by_pattern(dirs, exclude_patterns) - return _find_common_roots(dirs) - - -def _find_common_roots(paths: t.Iterable[str]) -> t.Iterable[str]: - root: dict[str, dict[str, t.Any]] = {} - - for chunks in sorted((PurePath(x).parts for x in paths), key=len, reverse=True): - node = root - - for chunk in chunks: - node = node.setdefault(chunk, {}) - - node.clear() - - rv = set() - - def _walk(node: t.Mapping[str, dict[str, t.Any]], path: tuple[str, ...]) -> None: - for prefix, child in node.items(): - _walk(child, path + (prefix,)) - - # If there are no more nodes, and a path has been accumulated, add it. - # Path may be empty if the "" entry is in sys.path. - if not node and path: - rv.add(os.path.join(*path)) - - _walk(root, ()) - return rv - - -def _get_args_for_reloading() -> list[str]: - """Determine how the script was executed, and return the args needed - to execute it again in a new process. - """ - if sys.version_info >= (3, 10): - # sys.orig_argv, added in Python 3.10, contains the exact args used to invoke - # Python. Still replace argv[0] with sys.executable for accuracy. - return [sys.executable, *sys.orig_argv[1:]] - - rv = [sys.executable] - py_script = sys.argv[0] - args = sys.argv[1:] - # Need to look at main module to determine how it was executed. - __main__ = sys.modules["__main__"] - - # The value of __package__ indicates how Python was called. It may - # not exist if a setuptools script is installed as an egg. It may be - # set incorrectly for entry points created with pip on Windows. - if getattr(__main__, "__package__", None) is None or ( - os.name == "nt" - and __main__.__package__ == "" - and not os.path.exists(py_script) - and os.path.exists(f"{py_script}.exe") - ): - # Executed a file, like "python app.py". - py_script = os.path.abspath(py_script) - - if os.name == "nt": - # Windows entry points have ".exe" extension and should be - # called directly. - if not os.path.exists(py_script) and os.path.exists(f"{py_script}.exe"): - py_script += ".exe" - - if ( - os.path.splitext(sys.executable)[1] == ".exe" - and os.path.splitext(py_script)[1] == ".exe" - ): - rv.pop(0) - - rv.append(py_script) - else: - # Executed a module, like "python -m werkzeug.serving". - if os.path.isfile(py_script): - # Rewritten by Python from "-m script" to "/path/to/script.py". - py_module = t.cast(str, __main__.__package__) - name = os.path.splitext(os.path.basename(py_script))[0] - - if name != "__main__": - py_module += f".{name}" - else: - # Incorrectly rewritten by pydevd debugger from "-m script" to "script". - py_module = py_script - - rv.extend(("-m", py_module.lstrip("."))) - - rv.extend(args) - return rv - - -class ReloaderLoop: - name = "" - - def __init__( - self, - extra_files: t.Iterable[str] | None = None, - exclude_patterns: t.Iterable[str] | None = None, - interval: int | float = 1, - ) -> None: - self.extra_files: set[str] = {os.path.abspath(x) for x in extra_files or ()} - self.exclude_patterns: set[str] = set(exclude_patterns or ()) - self.interval = interval - - def __enter__(self) -> ReloaderLoop: - """Do any setup, then run one step of the watch to populate the - initial filesystem state. - """ - self.run_step() - return self - - def __exit__(self, exc_type, exc_val, exc_tb): # type: ignore - """Clean up any resources associated with the reloader.""" - pass - - def run(self) -> None: - """Continually run the watch step, sleeping for the configured - interval after each step. - """ - while True: - self.run_step() - time.sleep(self.interval) - - def run_step(self) -> None: - """Run one step for watching the filesystem. Called once to set - up initial state, then repeatedly to update it. - """ - pass - - def restart_with_reloader(self) -> int: - """Spawn a new Python interpreter with the same arguments as the - current one, but running the reloader thread. - """ - while True: - _log("info", f" * Restarting with {self.name}") - args = _get_args_for_reloading() - new_environ = os.environ.copy() - new_environ["WERKZEUG_RUN_MAIN"] = "true" - exit_code = subprocess.call(args, env=new_environ, close_fds=False) - - if exit_code != 3: - return exit_code - - def trigger_reload(self, filename: str) -> None: - self.log_reload(filename) - sys.exit(3) - - def log_reload(self, filename: str | bytes) -> None: - filename = os.path.abspath(filename) - _log("info", f" * Detected change in {filename!r}, reloading") - - -class StatReloaderLoop(ReloaderLoop): - name = "stat" - - def __enter__(self) -> ReloaderLoop: - self.mtimes: dict[str, float] = {} - return super().__enter__() - - def run_step(self) -> None: - for name in _find_stat_paths(self.extra_files, self.exclude_patterns): - try: - mtime = os.stat(name).st_mtime - except OSError: - continue - - old_time = self.mtimes.get(name) - - if old_time is None: - self.mtimes[name] = mtime - continue - - if mtime > old_time: - self.trigger_reload(name) - - -class WatchdogReloaderLoop(ReloaderLoop): - def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: - from watchdog.events import EVENT_TYPE_CLOSED - from watchdog.events import EVENT_TYPE_CREATED - from watchdog.events import EVENT_TYPE_DELETED - from watchdog.events import EVENT_TYPE_MODIFIED - from watchdog.events import EVENT_TYPE_MOVED - from watchdog.events import FileModifiedEvent - from watchdog.events import PatternMatchingEventHandler - from watchdog.observers import Observer - - super().__init__(*args, **kwargs) - trigger_reload = self.trigger_reload - - class EventHandler(PatternMatchingEventHandler): - def on_any_event(self, event: FileModifiedEvent): # type: ignore - if event.event_type not in { - EVENT_TYPE_CLOSED, - EVENT_TYPE_CREATED, - EVENT_TYPE_DELETED, - EVENT_TYPE_MODIFIED, - EVENT_TYPE_MOVED, - }: - # skip events that don't involve changes to the file - return - - trigger_reload(event.src_path) - - reloader_name = Observer.__name__.lower() # type: ignore[attr-defined] - - if reloader_name.endswith("observer"): - reloader_name = reloader_name[:-8] - - self.name = f"watchdog ({reloader_name})" - self.observer = Observer() - # Extra patterns can be non-Python files, match them in addition - # to all Python files in default and extra directories. Ignore - # __pycache__ since a change there will always have a change to - # the source file (or initial pyc file) as well. Ignore Git and - # Mercurial internal changes. - extra_patterns = [p for p in self.extra_files if not os.path.isdir(p)] - self.event_handler = EventHandler( - patterns=["*.py", "*.pyc", "*.zip", *extra_patterns], - ignore_patterns=[ - *[f"*/{d}/*" for d in _ignore_common_dirs], - *self.exclude_patterns, - ], - ) - self.should_reload = False - - def trigger_reload(self, filename: str | bytes) -> None: - # This is called inside an event handler, which means throwing - # SystemExit has no effect. - # https://github.com/gorakhargosh/watchdog/issues/294 - self.should_reload = True - self.log_reload(filename) - - def __enter__(self) -> ReloaderLoop: - self.watches: dict[str, t.Any] = {} - self.observer.start() - return super().__enter__() - - def __exit__(self, exc_type, exc_val, exc_tb): # type: ignore - self.observer.stop() - self.observer.join() - - def run(self) -> None: - while not self.should_reload: - self.run_step() - time.sleep(self.interval) - - sys.exit(3) - - def run_step(self) -> None: - to_delete = set(self.watches) - - for path in _find_watchdog_paths(self.extra_files, self.exclude_patterns): - if path not in self.watches: - try: - self.watches[path] = self.observer.schedule( - self.event_handler, path, recursive=True - ) - except OSError: - # Clear this path from list of watches We don't want - # the same error message showing again in the next - # iteration. - self.watches[path] = None - - to_delete.discard(path) - - for path in to_delete: - watch = self.watches.pop(path, None) - - if watch is not None: - self.observer.unschedule(watch) - - -reloader_loops: dict[str, type[ReloaderLoop]] = { - "stat": StatReloaderLoop, - "watchdog": WatchdogReloaderLoop, -} - -try: - __import__("watchdog.observers") -except ImportError: - reloader_loops["auto"] = reloader_loops["stat"] -else: - reloader_loops["auto"] = reloader_loops["watchdog"] - - -def ensure_echo_on() -> None: - """Ensure that echo mode is enabled. Some tools such as PDB disable - it which causes usability issues after a reload.""" - # tcgetattr will fail if stdin isn't a tty - if sys.stdin is None or not sys.stdin.isatty(): - return - - try: - import termios - except ImportError: - return - - attributes = termios.tcgetattr(sys.stdin) - - if not attributes[3] & termios.ECHO: - attributes[3] |= termios.ECHO - termios.tcsetattr(sys.stdin, termios.TCSANOW, attributes) - - -def run_with_reloader( - main_func: t.Callable[[], None], - extra_files: t.Iterable[str] | None = None, - exclude_patterns: t.Iterable[str] | None = None, - interval: int | float = 1, - reloader_type: str = "auto", -) -> None: - """Run the given function in an independent Python interpreter.""" - import signal - - signal.signal(signal.SIGTERM, lambda *args: sys.exit(0)) - reloader = reloader_loops[reloader_type]( - extra_files=extra_files, exclude_patterns=exclude_patterns, interval=interval - ) - - try: - if os.environ.get("WERKZEUG_RUN_MAIN") == "true": - ensure_echo_on() - t = threading.Thread(target=main_func, args=()) - t.daemon = True - - # Enter the reloader to set up initial state, then start - # the app thread and reloader update loop. - with reloader: - t.start() - reloader.run() - else: - sys.exit(reloader.restart_with_reloader()) - except KeyboardInterrupt: - pass diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__init__.py deleted file mode 100644 index 846ffce..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -from .accept import Accept as Accept -from .accept import CharsetAccept as CharsetAccept -from .accept import LanguageAccept as LanguageAccept -from .accept import MIMEAccept as MIMEAccept -from .auth import Authorization as Authorization -from .auth import WWWAuthenticate as WWWAuthenticate -from .cache_control import RequestCacheControl as RequestCacheControl -from .cache_control import ResponseCacheControl as ResponseCacheControl -from .csp import ContentSecurityPolicy as ContentSecurityPolicy -from .etag import ETags as ETags -from .file_storage import FileMultiDict as FileMultiDict -from .file_storage import FileStorage as FileStorage -from .headers import EnvironHeaders as EnvironHeaders -from .headers import Headers as Headers -from .mixins import ImmutableDictMixin as ImmutableDictMixin -from .mixins import ImmutableHeadersMixin as ImmutableHeadersMixin -from .mixins import ImmutableListMixin as ImmutableListMixin -from .mixins import ImmutableMultiDictMixin as ImmutableMultiDictMixin -from .mixins import UpdateDictMixin as UpdateDictMixin -from .range import ContentRange as ContentRange -from .range import IfRange as IfRange -from .range import Range as Range -from .structures import CallbackDict as CallbackDict -from .structures import CombinedMultiDict as CombinedMultiDict -from .structures import HeaderSet as HeaderSet -from .structures import ImmutableDict as ImmutableDict -from .structures import ImmutableList as ImmutableList -from .structures import ImmutableMultiDict as ImmutableMultiDict -from .structures import ImmutableOrderedMultiDict as ImmutableOrderedMultiDict -from .structures import ImmutableTypeConversionDict as ImmutableTypeConversionDict -from .structures import iter_multi_items as iter_multi_items -from .structures import MultiDict as MultiDict -from .structures import OrderedMultiDict as OrderedMultiDict -from .structures import TypeConversionDict as TypeConversionDict diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 10bd9c9..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/accept.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/accept.cpython-38.pyc deleted file mode 100644 index 563eed2..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/accept.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/auth.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/auth.cpython-38.pyc deleted file mode 100644 index 6632128..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/auth.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/cache_control.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/cache_control.cpython-38.pyc deleted file mode 100644 index 459ea48..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/cache_control.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/csp.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/csp.cpython-38.pyc deleted file mode 100644 index 75fbe6c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/csp.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/etag.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/etag.cpython-38.pyc deleted file mode 100644 index 81b68ec..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/etag.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/file_storage.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/file_storage.cpython-38.pyc deleted file mode 100644 index 2e7dbe2..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/file_storage.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/headers.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/headers.cpython-38.pyc deleted file mode 100644 index 8b08b5a..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/headers.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/mixins.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/mixins.cpython-38.pyc deleted file mode 100644 index f293d05..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/mixins.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/range.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/range.cpython-38.pyc deleted file mode 100644 index 50c3639..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/range.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/structures.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/structures.cpython-38.pyc deleted file mode 100644 index 1f85487..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/__pycache__/structures.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/accept.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/accept.py deleted file mode 100644 index d80f0bb..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/accept.py +++ /dev/null @@ -1,326 +0,0 @@ -from __future__ import annotations - -import codecs -import re - -from .structures import ImmutableList - - -class Accept(ImmutableList): - """An :class:`Accept` object is just a list subclass for lists of - ``(value, quality)`` tuples. It is automatically sorted by specificity - and quality. - - All :class:`Accept` objects work similar to a list but provide extra - functionality for working with the data. Containment checks are - normalized to the rules of that header: - - >>> a = CharsetAccept([('ISO-8859-1', 1), ('utf-8', 0.7)]) - >>> a.best - 'ISO-8859-1' - >>> 'iso-8859-1' in a - True - >>> 'UTF8' in a - True - >>> 'utf7' in a - False - - To get the quality for an item you can use normal item lookup: - - >>> print a['utf-8'] - 0.7 - >>> a['utf7'] - 0 - - .. versionchanged:: 0.5 - :class:`Accept` objects are forced immutable now. - - .. versionchanged:: 1.0.0 - :class:`Accept` internal values are no longer ordered - alphabetically for equal quality tags. Instead the initial - order is preserved. - - """ - - def __init__(self, values=()): - if values is None: - list.__init__(self) - self.provided = False - elif isinstance(values, Accept): - self.provided = values.provided - list.__init__(self, values) - else: - self.provided = True - values = sorted( - values, key=lambda x: (self._specificity(x[0]), x[1]), reverse=True - ) - list.__init__(self, values) - - def _specificity(self, value): - """Returns a tuple describing the value's specificity.""" - return (value != "*",) - - def _value_matches(self, value, item): - """Check if a value matches a given accept item.""" - return item == "*" or item.lower() == value.lower() - - def __getitem__(self, key): - """Besides index lookup (getting item n) you can also pass it a string - to get the quality for the item. If the item is not in the list, the - returned quality is ``0``. - """ - if isinstance(key, str): - return self.quality(key) - return list.__getitem__(self, key) - - def quality(self, key): - """Returns the quality of the key. - - .. versionadded:: 0.6 - In previous versions you had to use the item-lookup syntax - (eg: ``obj[key]`` instead of ``obj.quality(key)``) - """ - for item, quality in self: - if self._value_matches(key, item): - return quality - return 0 - - def __contains__(self, value): - for item, _quality in self: - if self._value_matches(value, item): - return True - return False - - def __repr__(self): - pairs_str = ", ".join(f"({x!r}, {y})" for x, y in self) - return f"{type(self).__name__}([{pairs_str}])" - - def index(self, key): - """Get the position of an entry or raise :exc:`ValueError`. - - :param key: The key to be looked up. - - .. versionchanged:: 0.5 - This used to raise :exc:`IndexError`, which was inconsistent - with the list API. - """ - if isinstance(key, str): - for idx, (item, _quality) in enumerate(self): - if self._value_matches(key, item): - return idx - raise ValueError(key) - return list.index(self, key) - - def find(self, key): - """Get the position of an entry or return -1. - - :param key: The key to be looked up. - """ - try: - return self.index(key) - except ValueError: - return -1 - - def values(self): - """Iterate over all values.""" - for item in self: - yield item[0] - - def to_header(self): - """Convert the header set into an HTTP header string.""" - result = [] - for value, quality in self: - if quality != 1: - value = f"{value};q={quality}" - result.append(value) - return ",".join(result) - - def __str__(self): - return self.to_header() - - def _best_single_match(self, match): - for client_item, quality in self: - if self._value_matches(match, client_item): - # self is sorted by specificity descending, we can exit - return client_item, quality - return None - - def best_match(self, matches, default=None): - """Returns the best match from a list of possible matches based - on the specificity and quality of the client. If two items have the - same quality and specificity, the one is returned that comes first. - - :param matches: a list of matches to check for - :param default: the value that is returned if none match - """ - result = default - best_quality = -1 - best_specificity = (-1,) - for server_item in matches: - match = self._best_single_match(server_item) - if not match: - continue - client_item, quality = match - specificity = self._specificity(client_item) - if quality <= 0 or quality < best_quality: - continue - # better quality or same quality but more specific => better match - if quality > best_quality or specificity > best_specificity: - result = server_item - best_quality = quality - best_specificity = specificity - return result - - @property - def best(self): - """The best match as value.""" - if self: - return self[0][0] - - -_mime_split_re = re.compile(r"/|(?:\s*;\s*)") - - -def _normalize_mime(value): - return _mime_split_re.split(value.lower()) - - -class MIMEAccept(Accept): - """Like :class:`Accept` but with special methods and behavior for - mimetypes. - """ - - def _specificity(self, value): - return tuple(x != "*" for x in _mime_split_re.split(value)) - - def _value_matches(self, value, item): - # item comes from the client, can't match if it's invalid. - if "/" not in item: - return False - - # value comes from the application, tell the developer when it - # doesn't look valid. - if "/" not in value: - raise ValueError(f"invalid mimetype {value!r}") - - # Split the match value into type, subtype, and a sorted list of parameters. - normalized_value = _normalize_mime(value) - value_type, value_subtype = normalized_value[:2] - value_params = sorted(normalized_value[2:]) - - # "*/*" is the only valid value that can start with "*". - if value_type == "*" and value_subtype != "*": - raise ValueError(f"invalid mimetype {value!r}") - - # Split the accept item into type, subtype, and parameters. - normalized_item = _normalize_mime(item) - item_type, item_subtype = normalized_item[:2] - item_params = sorted(normalized_item[2:]) - - # "*/not-*" from the client is invalid, can't match. - if item_type == "*" and item_subtype != "*": - return False - - return ( - (item_type == "*" and item_subtype == "*") - or (value_type == "*" and value_subtype == "*") - ) or ( - item_type == value_type - and ( - item_subtype == "*" - or value_subtype == "*" - or (item_subtype == value_subtype and item_params == value_params) - ) - ) - - @property - def accept_html(self): - """True if this object accepts HTML.""" - return ( - "text/html" in self or "application/xhtml+xml" in self or self.accept_xhtml - ) - - @property - def accept_xhtml(self): - """True if this object accepts XHTML.""" - return "application/xhtml+xml" in self or "application/xml" in self - - @property - def accept_json(self): - """True if this object accepts JSON.""" - return "application/json" in self - - -_locale_delim_re = re.compile(r"[_-]") - - -def _normalize_lang(value): - """Process a language tag for matching.""" - return _locale_delim_re.split(value.lower()) - - -class LanguageAccept(Accept): - """Like :class:`Accept` but with normalization for language tags.""" - - def _value_matches(self, value, item): - return item == "*" or _normalize_lang(value) == _normalize_lang(item) - - def best_match(self, matches, default=None): - """Given a list of supported values, finds the best match from - the list of accepted values. - - Language tags are normalized for the purpose of matching, but - are returned unchanged. - - If no exact match is found, this will fall back to matching - the first subtag (primary language only), first with the - accepted values then with the match values. This partial is not - applied to any other language subtags. - - The default is returned if no exact or fallback match is found. - - :param matches: A list of supported languages to find a match. - :param default: The value that is returned if none match. - """ - # Look for an exact match first. If a client accepts "en-US", - # "en-US" is a valid match at this point. - result = super().best_match(matches) - - if result is not None: - return result - - # Fall back to accepting primary tags. If a client accepts - # "en-US", "en" is a valid match at this point. Need to use - # re.split to account for 2 or 3 letter codes. - fallback = Accept( - [(_locale_delim_re.split(item[0], 1)[0], item[1]) for item in self] - ) - result = fallback.best_match(matches) - - if result is not None: - return result - - # Fall back to matching primary tags. If the client accepts - # "en", "en-US" is a valid match at this point. - fallback_matches = [_locale_delim_re.split(item, 1)[0] for item in matches] - result = super().best_match(fallback_matches) - - # Return a value from the original match list. Find the first - # original value that starts with the matched primary tag. - if result is not None: - return next(item for item in matches if item.startswith(result)) - - return default - - -class CharsetAccept(Accept): - """Like :class:`Accept` but with normalization for charsets.""" - - def _value_matches(self, value, item): - def _normalize(name): - try: - return codecs.lookup(name).name - except LookupError: - return name.lower() - - return item == "*" or _normalize(value) == _normalize(item) diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/accept.pyi b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/accept.pyi deleted file mode 100644 index 4b74dd9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/accept.pyi +++ /dev/null @@ -1,54 +0,0 @@ -from collections.abc import Iterable -from collections.abc import Iterator -from typing import overload - -from .structures import ImmutableList - -class Accept(ImmutableList[tuple[str, int]]): - provided: bool - def __init__( - self, values: Accept | Iterable[tuple[str, float]] | None = None - ) -> None: ... - def _specificity(self, value: str) -> tuple[bool, ...]: ... - def _value_matches(self, value: str, item: str) -> bool: ... - @overload # type: ignore - def __getitem__(self, key: str) -> int: ... - @overload - def __getitem__(self, key: int) -> tuple[str, int]: ... - @overload - def __getitem__(self, key: slice) -> Iterable[tuple[str, int]]: ... - def quality(self, key: str) -> int: ... - def __contains__(self, value: str) -> bool: ... # type: ignore - def index(self, key: str) -> int: ... # type: ignore - def find(self, key: str) -> int: ... - def values(self) -> Iterator[str]: ... - def to_header(self) -> str: ... - def _best_single_match(self, match: str) -> tuple[str, int] | None: ... - @overload - def best_match(self, matches: Iterable[str], default: str) -> str: ... - @overload - def best_match( - self, matches: Iterable[str], default: str | None = None - ) -> str | None: ... - @property - def best(self) -> str: ... - -def _normalize_mime(value: str) -> list[str]: ... - -class MIMEAccept(Accept): - def _specificity(self, value: str) -> tuple[bool, ...]: ... - def _value_matches(self, value: str, item: str) -> bool: ... - @property - def accept_html(self) -> bool: ... - @property - def accept_xhtml(self) -> bool: ... - @property - def accept_json(self) -> bool: ... - -def _normalize_lang(value: str) -> list[str]: ... - -class LanguageAccept(Accept): - def _value_matches(self, value: str, item: str) -> bool: ... - -class CharsetAccept(Accept): - def _value_matches(self, value: str, item: str) -> bool: ... diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/auth.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/auth.py deleted file mode 100644 index a3ca0de..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/auth.py +++ /dev/null @@ -1,316 +0,0 @@ -from __future__ import annotations - -import base64 -import binascii -import typing as t - -from ..http import dump_header -from ..http import parse_dict_header -from ..http import quote_header_value -from .structures import CallbackDict - -if t.TYPE_CHECKING: - import typing_extensions as te - - -class Authorization: - """Represents the parts of an ``Authorization`` request header. - - :attr:`.Request.authorization` returns an instance if the header is set. - - An instance can be used with the test :class:`.Client` request methods' ``auth`` - parameter to send the header in test requests. - - Depending on the auth scheme, either :attr:`parameters` or :attr:`token` will be - set. The ``Basic`` scheme's token is decoded into the ``username`` and ``password`` - parameters. - - For convenience, ``auth["key"]`` and ``auth.key`` both access the key in the - :attr:`parameters` dict, along with ``auth.get("key")`` and ``"key" in auth``. - - .. versionchanged:: 2.3 - The ``token`` parameter and attribute was added to support auth schemes that use - a token instead of parameters, such as ``Bearer``. - - .. versionchanged:: 2.3 - The object is no longer a ``dict``. - - .. versionchanged:: 0.5 - The object is an immutable dict. - """ - - def __init__( - self, - auth_type: str, - data: dict[str, str | None] | None = None, - token: str | None = None, - ) -> None: - self.type = auth_type - """The authorization scheme, like ``basic``, ``digest``, or ``bearer``.""" - - if data is None: - data = {} - - self.parameters = data - """A dict of parameters parsed from the header. Either this or :attr:`token` - will have a value for a given scheme. - """ - - self.token = token - """A token parsed from the header. Either this or :attr:`parameters` will have a - value for a given scheme. - - .. versionadded:: 2.3 - """ - - def __getattr__(self, name: str) -> str | None: - return self.parameters.get(name) - - def __getitem__(self, name: str) -> str | None: - return self.parameters.get(name) - - def get(self, key: str, default: str | None = None) -> str | None: - return self.parameters.get(key, default) - - def __contains__(self, key: str) -> bool: - return key in self.parameters - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Authorization): - return NotImplemented - - return ( - other.type == self.type - and other.token == self.token - and other.parameters == self.parameters - ) - - @classmethod - def from_header(cls, value: str | None) -> te.Self | None: - """Parse an ``Authorization`` header value and return an instance, or ``None`` - if the value is empty. - - :param value: The header value to parse. - - .. versionadded:: 2.3 - """ - if not value: - return None - - scheme, _, rest = value.partition(" ") - scheme = scheme.lower() - rest = rest.strip() - - if scheme == "basic": - try: - username, _, password = base64.b64decode(rest).decode().partition(":") - except (binascii.Error, UnicodeError): - return None - - return cls(scheme, {"username": username, "password": password}) - - if "=" in rest.rstrip("="): - # = that is not trailing, this is parameters. - return cls(scheme, parse_dict_header(rest), None) - - # No = or only trailing =, this is a token. - return cls(scheme, None, rest) - - def to_header(self) -> str: - """Produce an ``Authorization`` header value representing this data. - - .. versionadded:: 2.0 - """ - if self.type == "basic": - value = base64.b64encode( - f"{self.username}:{self.password}".encode() - ).decode("ascii") - return f"Basic {value}" - - if self.token is not None: - return f"{self.type.title()} {self.token}" - - return f"{self.type.title()} {dump_header(self.parameters)}" - - def __str__(self) -> str: - return self.to_header() - - def __repr__(self) -> str: - return f"<{type(self).__name__} {self.to_header()}>" - - -class WWWAuthenticate: - """Represents the parts of a ``WWW-Authenticate`` response header. - - Set :attr:`.Response.www_authenticate` to an instance of list of instances to set - values for this header in the response. Modifying this instance will modify the - header value. - - Depending on the auth scheme, either :attr:`parameters` or :attr:`token` should be - set. The ``Basic`` scheme will encode ``username`` and ``password`` parameters to a - token. - - For convenience, ``auth["key"]`` and ``auth.key`` both act on the :attr:`parameters` - dict, and can be used to get, set, or delete parameters. ``auth.get("key")`` and - ``"key" in auth`` are also provided. - - .. versionchanged:: 2.3 - The ``token`` parameter and attribute was added to support auth schemes that use - a token instead of parameters, such as ``Bearer``. - - .. versionchanged:: 2.3 - The object is no longer a ``dict``. - - .. versionchanged:: 2.3 - The ``on_update`` parameter was removed. - """ - - def __init__( - self, - auth_type: str, - values: dict[str, str | None] | None = None, - token: str | None = None, - ): - self._type = auth_type.lower() - self._parameters: dict[str, str | None] = CallbackDict( - values, lambda _: self._trigger_on_update() - ) - self._token = token - self._on_update: t.Callable[[WWWAuthenticate], None] | None = None - - def _trigger_on_update(self) -> None: - if self._on_update is not None: - self._on_update(self) - - @property - def type(self) -> str: - """The authorization scheme, like ``basic``, ``digest``, or ``bearer``.""" - return self._type - - @type.setter - def type(self, value: str) -> None: - self._type = value - self._trigger_on_update() - - @property - def parameters(self) -> dict[str, str | None]: - """A dict of parameters for the header. Only one of this or :attr:`token` should - have a value for a given scheme. - """ - return self._parameters - - @parameters.setter - def parameters(self, value: dict[str, str]) -> None: - self._parameters = CallbackDict(value, lambda _: self._trigger_on_update()) - self._trigger_on_update() - - @property - def token(self) -> str | None: - """A dict of parameters for the header. Only one of this or :attr:`token` should - have a value for a given scheme. - """ - return self._token - - @token.setter - def token(self, value: str | None) -> None: - """A token for the header. Only one of this or :attr:`parameters` should have a - value for a given scheme. - - .. versionadded:: 2.3 - """ - self._token = value - self._trigger_on_update() - - def __getitem__(self, key: str) -> str | None: - return self.parameters.get(key) - - def __setitem__(self, key: str, value: str | None) -> None: - if value is None: - if key in self.parameters: - del self.parameters[key] - else: - self.parameters[key] = value - - self._trigger_on_update() - - def __delitem__(self, key: str) -> None: - if key in self.parameters: - del self.parameters[key] - self._trigger_on_update() - - def __getattr__(self, name: str) -> str | None: - return self[name] - - def __setattr__(self, name: str, value: str | None) -> None: - if name in {"_type", "_parameters", "_token", "_on_update"}: - super().__setattr__(name, value) - else: - self[name] = value - - def __delattr__(self, name: str) -> None: - del self[name] - - def __contains__(self, key: str) -> bool: - return key in self.parameters - - def __eq__(self, other: object) -> bool: - if not isinstance(other, WWWAuthenticate): - return NotImplemented - - return ( - other.type == self.type - and other.token == self.token - and other.parameters == self.parameters - ) - - def get(self, key: str, default: str | None = None) -> str | None: - return self.parameters.get(key, default) - - @classmethod - def from_header(cls, value: str | None) -> te.Self | None: - """Parse a ``WWW-Authenticate`` header value and return an instance, or ``None`` - if the value is empty. - - :param value: The header value to parse. - - .. versionadded:: 2.3 - """ - if not value: - return None - - scheme, _, rest = value.partition(" ") - scheme = scheme.lower() - rest = rest.strip() - - if "=" in rest.rstrip("="): - # = that is not trailing, this is parameters. - return cls(scheme, parse_dict_header(rest), None) - - # No = or only trailing =, this is a token. - return cls(scheme, None, rest) - - def to_header(self) -> str: - """Produce a ``WWW-Authenticate`` header value representing this data.""" - if self.token is not None: - return f"{self.type.title()} {self.token}" - - if self.type == "digest": - items = [] - - for key, value in self.parameters.items(): - if key in {"realm", "domain", "nonce", "opaque", "qop"}: - value = quote_header_value(value, allow_token=False) - else: - value = quote_header_value(value) - - items.append(f"{key}={value}") - - return f"Digest {', '.join(items)}" - - return f"{self.type.title()} {dump_header(self.parameters)}" - - def __str__(self) -> str: - return self.to_header() - - def __repr__(self) -> str: - return f"<{type(self).__name__} {self.to_header()}>" diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/cache_control.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/cache_control.py deleted file mode 100644 index bff4c18..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/cache_control.py +++ /dev/null @@ -1,175 +0,0 @@ -from __future__ import annotations - -from .mixins import ImmutableDictMixin -from .mixins import UpdateDictMixin - - -def cache_control_property(key, empty, type): - """Return a new property object for a cache header. Useful if you - want to add support for a cache extension in a subclass. - - .. versionchanged:: 2.0 - Renamed from ``cache_property``. - """ - return property( - lambda x: x._get_cache_value(key, empty, type), - lambda x, v: x._set_cache_value(key, v, type), - lambda x: x._del_cache_value(key), - f"accessor for {key!r}", - ) - - -class _CacheControl(UpdateDictMixin, dict): - """Subclass of a dict that stores values for a Cache-Control header. It - has accessors for all the cache-control directives specified in RFC 2616. - The class does not differentiate between request and response directives. - - Because the cache-control directives in the HTTP header use dashes the - python descriptors use underscores for that. - - To get a header of the :class:`CacheControl` object again you can convert - the object into a string or call the :meth:`to_header` method. If you plan - to subclass it and add your own items have a look at the sourcecode for - that class. - - .. versionchanged:: 2.1.0 - Setting int properties such as ``max_age`` will convert the - value to an int. - - .. versionchanged:: 0.4 - - Setting `no_cache` or `private` to boolean `True` will set the implicit - none-value which is ``*``: - - >>> cc = ResponseCacheControl() - >>> cc.no_cache = True - >>> cc - - >>> cc.no_cache - '*' - >>> cc.no_cache = None - >>> cc - - - In versions before 0.5 the behavior documented here affected the now - no longer existing `CacheControl` class. - """ - - no_cache = cache_control_property("no-cache", "*", None) - no_store = cache_control_property("no-store", None, bool) - max_age = cache_control_property("max-age", -1, int) - no_transform = cache_control_property("no-transform", None, None) - - def __init__(self, values=(), on_update=None): - dict.__init__(self, values or ()) - self.on_update = on_update - self.provided = values is not None - - def _get_cache_value(self, key, empty, type): - """Used internally by the accessor properties.""" - if type is bool: - return key in self - if key in self: - value = self[key] - if value is None: - return empty - elif type is not None: - try: - value = type(value) - except ValueError: - pass - return value - return None - - def _set_cache_value(self, key, value, type): - """Used internally by the accessor properties.""" - if type is bool: - if value: - self[key] = None - else: - self.pop(key, None) - else: - if value is None: - self.pop(key, None) - elif value is True: - self[key] = None - else: - if type is not None: - self[key] = type(value) - else: - self[key] = value - - def _del_cache_value(self, key): - """Used internally by the accessor properties.""" - if key in self: - del self[key] - - def to_header(self): - """Convert the stored values into a cache control header.""" - return http.dump_header(self) - - def __str__(self): - return self.to_header() - - def __repr__(self): - kv_str = " ".join(f"{k}={v!r}" for k, v in sorted(self.items())) - return f"<{type(self).__name__} {kv_str}>" - - cache_property = staticmethod(cache_control_property) - - -class RequestCacheControl(ImmutableDictMixin, _CacheControl): - """A cache control for requests. This is immutable and gives access - to all the request-relevant cache control headers. - - To get a header of the :class:`RequestCacheControl` object again you can - convert the object into a string or call the :meth:`to_header` method. If - you plan to subclass it and add your own items have a look at the sourcecode - for that class. - - .. versionchanged:: 2.1.0 - Setting int properties such as ``max_age`` will convert the - value to an int. - - .. versionadded:: 0.5 - In previous versions a `CacheControl` class existed that was used - both for request and response. - """ - - max_stale = cache_control_property("max-stale", "*", int) - min_fresh = cache_control_property("min-fresh", "*", int) - only_if_cached = cache_control_property("only-if-cached", None, bool) - - -class ResponseCacheControl(_CacheControl): - """A cache control for responses. Unlike :class:`RequestCacheControl` - this is mutable and gives access to response-relevant cache control - headers. - - To get a header of the :class:`ResponseCacheControl` object again you can - convert the object into a string or call the :meth:`to_header` method. If - you plan to subclass it and add your own items have a look at the sourcecode - for that class. - - .. versionchanged:: 2.1.1 - ``s_maxage`` converts the value to an int. - - .. versionchanged:: 2.1.0 - Setting int properties such as ``max_age`` will convert the - value to an int. - - .. versionadded:: 0.5 - In previous versions a `CacheControl` class existed that was used - both for request and response. - """ - - public = cache_control_property("public", None, bool) - private = cache_control_property("private", "*", None) - must_revalidate = cache_control_property("must-revalidate", None, bool) - proxy_revalidate = cache_control_property("proxy-revalidate", None, bool) - s_maxage = cache_control_property("s-maxage", None, int) - immutable = cache_control_property("immutable", None, bool) - - -# circular dependencies -from .. import http diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/cache_control.pyi b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/cache_control.pyi deleted file mode 100644 index 54ec020..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/cache_control.pyi +++ /dev/null @@ -1,115 +0,0 @@ -from collections.abc import Callable -from collections.abc import Iterable -from collections.abc import Mapping -from typing import TypeVar - -from .mixins import ImmutableDictMixin -from .mixins import UpdateDictMixin - -T = TypeVar("T") -_CPT = TypeVar("_CPT", str, int, bool) - -def cache_control_property( - key: str, empty: _CPT | None, type: type[_CPT] -) -> property: ... - -class _CacheControl( - UpdateDictMixin[str, str | int | bool | None], dict[str, str | int | bool | None] -): - provided: bool - def __init__( - self, - values: Mapping[str, str | int | bool | None] - | Iterable[tuple[str, str | int | bool | None]] = (), - on_update: Callable[[_CacheControl], None] | None = None, - ) -> None: ... - @property - def no_cache(self) -> bool | None: ... - @no_cache.setter - def no_cache(self, value: bool | None) -> None: ... - @no_cache.deleter - def no_cache(self) -> None: ... - @property - def no_store(self) -> bool | None: ... - @no_store.setter - def no_store(self, value: bool | None) -> None: ... - @no_store.deleter - def no_store(self) -> None: ... - @property - def max_age(self) -> int | None: ... - @max_age.setter - def max_age(self, value: int | None) -> None: ... - @max_age.deleter - def max_age(self) -> None: ... - @property - def no_transform(self) -> bool | None: ... - @no_transform.setter - def no_transform(self, value: bool | None) -> None: ... - @no_transform.deleter - def no_transform(self) -> None: ... - def _get_cache_value(self, key: str, empty: T | None, type: type[T]) -> T: ... - def _set_cache_value(self, key: str, value: T | None, type: type[T]) -> None: ... - def _del_cache_value(self, key: str) -> None: ... - def to_header(self) -> str: ... - @staticmethod - def cache_property(key: str, empty: _CPT | None, type: type[_CPT]) -> property: ... - -class RequestCacheControl( # type: ignore[misc] - ImmutableDictMixin[str, str | int | bool | None], _CacheControl -): - @property - def max_stale(self) -> int | None: ... - @max_stale.setter - def max_stale(self, value: int | None) -> None: ... - @max_stale.deleter - def max_stale(self) -> None: ... - @property - def min_fresh(self) -> int | None: ... - @min_fresh.setter - def min_fresh(self, value: int | None) -> None: ... - @min_fresh.deleter - def min_fresh(self) -> None: ... - @property - def only_if_cached(self) -> bool | None: ... - @only_if_cached.setter - def only_if_cached(self, value: bool | None) -> None: ... - @only_if_cached.deleter - def only_if_cached(self) -> None: ... - -class ResponseCacheControl(_CacheControl): - @property - def public(self) -> bool | None: ... - @public.setter - def public(self, value: bool | None) -> None: ... - @public.deleter - def public(self) -> None: ... - @property - def private(self) -> bool | None: ... - @private.setter - def private(self, value: bool | None) -> None: ... - @private.deleter - def private(self) -> None: ... - @property - def must_revalidate(self) -> bool | None: ... - @must_revalidate.setter - def must_revalidate(self, value: bool | None) -> None: ... - @must_revalidate.deleter - def must_revalidate(self) -> None: ... - @property - def proxy_revalidate(self) -> bool | None: ... - @proxy_revalidate.setter - def proxy_revalidate(self, value: bool | None) -> None: ... - @proxy_revalidate.deleter - def proxy_revalidate(self) -> None: ... - @property - def s_maxage(self) -> int | None: ... - @s_maxage.setter - def s_maxage(self, value: int | None) -> None: ... - @s_maxage.deleter - def s_maxage(self) -> None: ... - @property - def immutable(self) -> bool | None: ... - @immutable.setter - def immutable(self, value: bool | None) -> None: ... - @immutable.deleter - def immutable(self) -> None: ... diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/csp.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/csp.py deleted file mode 100644 index dde9414..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/csp.py +++ /dev/null @@ -1,94 +0,0 @@ -from __future__ import annotations - -from .mixins import UpdateDictMixin - - -def csp_property(key): - """Return a new property object for a content security policy header. - Useful if you want to add support for a csp extension in a - subclass. - """ - return property( - lambda x: x._get_value(key), - lambda x, v: x._set_value(key, v), - lambda x: x._del_value(key), - f"accessor for {key!r}", - ) - - -class ContentSecurityPolicy(UpdateDictMixin, dict): - """Subclass of a dict that stores values for a Content Security Policy - header. It has accessors for all the level 3 policies. - - Because the csp directives in the HTTP header use dashes the - python descriptors use underscores for that. - - To get a header of the :class:`ContentSecuirtyPolicy` object again - you can convert the object into a string or call the - :meth:`to_header` method. If you plan to subclass it and add your - own items have a look at the sourcecode for that class. - - .. versionadded:: 1.0.0 - Support for Content Security Policy headers was added. - - """ - - base_uri = csp_property("base-uri") - child_src = csp_property("child-src") - connect_src = csp_property("connect-src") - default_src = csp_property("default-src") - font_src = csp_property("font-src") - form_action = csp_property("form-action") - frame_ancestors = csp_property("frame-ancestors") - frame_src = csp_property("frame-src") - img_src = csp_property("img-src") - manifest_src = csp_property("manifest-src") - media_src = csp_property("media-src") - navigate_to = csp_property("navigate-to") - object_src = csp_property("object-src") - prefetch_src = csp_property("prefetch-src") - plugin_types = csp_property("plugin-types") - report_to = csp_property("report-to") - report_uri = csp_property("report-uri") - sandbox = csp_property("sandbox") - script_src = csp_property("script-src") - script_src_attr = csp_property("script-src-attr") - script_src_elem = csp_property("script-src-elem") - style_src = csp_property("style-src") - style_src_attr = csp_property("style-src-attr") - style_src_elem = csp_property("style-src-elem") - worker_src = csp_property("worker-src") - - def __init__(self, values=(), on_update=None): - dict.__init__(self, values or ()) - self.on_update = on_update - self.provided = values is not None - - def _get_value(self, key): - """Used internally by the accessor properties.""" - return self.get(key) - - def _set_value(self, key, value): - """Used internally by the accessor properties.""" - if value is None: - self.pop(key, None) - else: - self[key] = value - - def _del_value(self, key): - """Used internally by the accessor properties.""" - if key in self: - del self[key] - - def to_header(self): - """Convert the stored values into a cache control header.""" - from ..http import dump_csp_header - - return dump_csp_header(self) - - def __str__(self): - return self.to_header() - - def __repr__(self): - kv_str = " ".join(f"{k}={v!r}" for k, v in sorted(self.items())) - return f"<{type(self).__name__} {kv_str}>" diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/csp.pyi b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/csp.pyi deleted file mode 100644 index f9e2ac0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/csp.pyi +++ /dev/null @@ -1,169 +0,0 @@ -from collections.abc import Callable -from collections.abc import Iterable -from collections.abc import Mapping - -from .mixins import UpdateDictMixin - -def csp_property(key: str) -> property: ... - -class ContentSecurityPolicy(UpdateDictMixin[str, str], dict[str, str]): - @property - def base_uri(self) -> str | None: ... - @base_uri.setter - def base_uri(self, value: str | None) -> None: ... - @base_uri.deleter - def base_uri(self) -> None: ... - @property - def child_src(self) -> str | None: ... - @child_src.setter - def child_src(self, value: str | None) -> None: ... - @child_src.deleter - def child_src(self) -> None: ... - @property - def connect_src(self) -> str | None: ... - @connect_src.setter - def connect_src(self, value: str | None) -> None: ... - @connect_src.deleter - def connect_src(self) -> None: ... - @property - def default_src(self) -> str | None: ... - @default_src.setter - def default_src(self, value: str | None) -> None: ... - @default_src.deleter - def default_src(self) -> None: ... - @property - def font_src(self) -> str | None: ... - @font_src.setter - def font_src(self, value: str | None) -> None: ... - @font_src.deleter - def font_src(self) -> None: ... - @property - def form_action(self) -> str | None: ... - @form_action.setter - def form_action(self, value: str | None) -> None: ... - @form_action.deleter - def form_action(self) -> None: ... - @property - def frame_ancestors(self) -> str | None: ... - @frame_ancestors.setter - def frame_ancestors(self, value: str | None) -> None: ... - @frame_ancestors.deleter - def frame_ancestors(self) -> None: ... - @property - def frame_src(self) -> str | None: ... - @frame_src.setter - def frame_src(self, value: str | None) -> None: ... - @frame_src.deleter - def frame_src(self) -> None: ... - @property - def img_src(self) -> str | None: ... - @img_src.setter - def img_src(self, value: str | None) -> None: ... - @img_src.deleter - def img_src(self) -> None: ... - @property - def manifest_src(self) -> str | None: ... - @manifest_src.setter - def manifest_src(self, value: str | None) -> None: ... - @manifest_src.deleter - def manifest_src(self) -> None: ... - @property - def media_src(self) -> str | None: ... - @media_src.setter - def media_src(self, value: str | None) -> None: ... - @media_src.deleter - def media_src(self) -> None: ... - @property - def navigate_to(self) -> str | None: ... - @navigate_to.setter - def navigate_to(self, value: str | None) -> None: ... - @navigate_to.deleter - def navigate_to(self) -> None: ... - @property - def object_src(self) -> str | None: ... - @object_src.setter - def object_src(self, value: str | None) -> None: ... - @object_src.deleter - def object_src(self) -> None: ... - @property - def prefetch_src(self) -> str | None: ... - @prefetch_src.setter - def prefetch_src(self, value: str | None) -> None: ... - @prefetch_src.deleter - def prefetch_src(self) -> None: ... - @property - def plugin_types(self) -> str | None: ... - @plugin_types.setter - def plugin_types(self, value: str | None) -> None: ... - @plugin_types.deleter - def plugin_types(self) -> None: ... - @property - def report_to(self) -> str | None: ... - @report_to.setter - def report_to(self, value: str | None) -> None: ... - @report_to.deleter - def report_to(self) -> None: ... - @property - def report_uri(self) -> str | None: ... - @report_uri.setter - def report_uri(self, value: str | None) -> None: ... - @report_uri.deleter - def report_uri(self) -> None: ... - @property - def sandbox(self) -> str | None: ... - @sandbox.setter - def sandbox(self, value: str | None) -> None: ... - @sandbox.deleter - def sandbox(self) -> None: ... - @property - def script_src(self) -> str | None: ... - @script_src.setter - def script_src(self, value: str | None) -> None: ... - @script_src.deleter - def script_src(self) -> None: ... - @property - def script_src_attr(self) -> str | None: ... - @script_src_attr.setter - def script_src_attr(self, value: str | None) -> None: ... - @script_src_attr.deleter - def script_src_attr(self) -> None: ... - @property - def script_src_elem(self) -> str | None: ... - @script_src_elem.setter - def script_src_elem(self, value: str | None) -> None: ... - @script_src_elem.deleter - def script_src_elem(self) -> None: ... - @property - def style_src(self) -> str | None: ... - @style_src.setter - def style_src(self, value: str | None) -> None: ... - @style_src.deleter - def style_src(self) -> None: ... - @property - def style_src_attr(self) -> str | None: ... - @style_src_attr.setter - def style_src_attr(self, value: str | None) -> None: ... - @style_src_attr.deleter - def style_src_attr(self) -> None: ... - @property - def style_src_elem(self) -> str | None: ... - @style_src_elem.setter - def style_src_elem(self, value: str | None) -> None: ... - @style_src_elem.deleter - def style_src_elem(self) -> None: ... - @property - def worker_src(self) -> str | None: ... - @worker_src.setter - def worker_src(self, value: str | None) -> None: ... - @worker_src.deleter - def worker_src(self) -> None: ... - provided: bool - def __init__( - self, - values: Mapping[str, str] | Iterable[tuple[str, str]] = (), - on_update: Callable[[ContentSecurityPolicy], None] | None = None, - ) -> None: ... - def _get_value(self, key: str) -> str | None: ... - def _set_value(self, key: str, value: str) -> None: ... - def _del_value(self, key: str) -> None: ... - def to_header(self) -> str: ... diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/etag.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/etag.py deleted file mode 100644 index 747d996..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/etag.py +++ /dev/null @@ -1,95 +0,0 @@ -from __future__ import annotations - -from collections.abc import Collection - - -class ETags(Collection): - """A set that can be used to check if one etag is present in a collection - of etags. - """ - - def __init__(self, strong_etags=None, weak_etags=None, star_tag=False): - if not star_tag and strong_etags: - self._strong = frozenset(strong_etags) - else: - self._strong = frozenset() - - self._weak = frozenset(weak_etags or ()) - self.star_tag = star_tag - - def as_set(self, include_weak=False): - """Convert the `ETags` object into a python set. Per default all the - weak etags are not part of this set.""" - rv = set(self._strong) - if include_weak: - rv.update(self._weak) - return rv - - def is_weak(self, etag): - """Check if an etag is weak.""" - return etag in self._weak - - def is_strong(self, etag): - """Check if an etag is strong.""" - return etag in self._strong - - def contains_weak(self, etag): - """Check if an etag is part of the set including weak and strong tags.""" - return self.is_weak(etag) or self.contains(etag) - - def contains(self, etag): - """Check if an etag is part of the set ignoring weak tags. - It is also possible to use the ``in`` operator. - """ - if self.star_tag: - return True - return self.is_strong(etag) - - def contains_raw(self, etag): - """When passed a quoted tag it will check if this tag is part of the - set. If the tag is weak it is checked against weak and strong tags, - otherwise strong only.""" - from ..http import unquote_etag - - etag, weak = unquote_etag(etag) - if weak: - return self.contains_weak(etag) - return self.contains(etag) - - def to_header(self): - """Convert the etags set into a HTTP header string.""" - if self.star_tag: - return "*" - return ", ".join( - [f'"{x}"' for x in self._strong] + [f'W/"{x}"' for x in self._weak] - ) - - def __call__(self, etag=None, data=None, include_weak=False): - if [etag, data].count(None) != 1: - raise TypeError("either tag or data required, but at least one") - if etag is None: - from ..http import generate_etag - - etag = generate_etag(data) - if include_weak: - if etag in self._weak: - return True - return etag in self._strong - - def __bool__(self): - return bool(self.star_tag or self._strong or self._weak) - - def __str__(self): - return self.to_header() - - def __len__(self): - return len(self._strong) - - def __iter__(self): - return iter(self._strong) - - def __contains__(self, etag): - return self.contains(etag) - - def __repr__(self): - return f"<{type(self).__name__} {str(self)!r}>" diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/etag.pyi b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/etag.pyi deleted file mode 100644 index 88e54f1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/etag.pyi +++ /dev/null @@ -1,30 +0,0 @@ -from collections.abc import Collection -from collections.abc import Iterable -from collections.abc import Iterator - -class ETags(Collection[str]): - _strong: frozenset[str] - _weak: frozenset[str] - star_tag: bool - def __init__( - self, - strong_etags: Iterable[str] | None = None, - weak_etags: Iterable[str] | None = None, - star_tag: bool = False, - ) -> None: ... - def as_set(self, include_weak: bool = False) -> set[str]: ... - def is_weak(self, etag: str) -> bool: ... - def is_strong(self, etag: str) -> bool: ... - def contains_weak(self, etag: str) -> bool: ... - def contains(self, etag: str) -> bool: ... - def contains_raw(self, etag: str) -> bool: ... - def to_header(self) -> str: ... - def __call__( - self, - etag: str | None = None, - data: bytes | None = None, - include_weak: bool = False, - ) -> bool: ... - def __len__(self) -> int: ... - def __iter__(self) -> Iterator[str]: ... - def __contains__(self, item: str) -> bool: ... # type: ignore diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/file_storage.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/file_storage.py deleted file mode 100644 index e878a56..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/file_storage.py +++ /dev/null @@ -1,196 +0,0 @@ -from __future__ import annotations - -import mimetypes -from io import BytesIO -from os import fsdecode -from os import fspath - -from .._internal import _plain_int -from .structures import MultiDict - - -class FileStorage: - """The :class:`FileStorage` class is a thin wrapper over incoming files. - It is used by the request object to represent uploaded files. All the - attributes of the wrapper stream are proxied by the file storage so - it's possible to do ``storage.read()`` instead of the long form - ``storage.stream.read()``. - """ - - def __init__( - self, - stream=None, - filename=None, - name=None, - content_type=None, - content_length=None, - headers=None, - ): - self.name = name - self.stream = stream or BytesIO() - - # If no filename is provided, attempt to get the filename from - # the stream object. Python names special streams like - # ```` with angular brackets, skip these streams. - if filename is None: - filename = getattr(stream, "name", None) - - if filename is not None: - filename = fsdecode(filename) - - if filename and filename[0] == "<" and filename[-1] == ">": - filename = None - else: - filename = fsdecode(filename) - - self.filename = filename - - if headers is None: - from .headers import Headers - - headers = Headers() - self.headers = headers - if content_type is not None: - headers["Content-Type"] = content_type - if content_length is not None: - headers["Content-Length"] = str(content_length) - - def _parse_content_type(self): - if not hasattr(self, "_parsed_content_type"): - self._parsed_content_type = http.parse_options_header(self.content_type) - - @property - def content_type(self): - """The content-type sent in the header. Usually not available""" - return self.headers.get("content-type") - - @property - def content_length(self): - """The content-length sent in the header. Usually not available""" - if "content-length" in self.headers: - try: - return _plain_int(self.headers["content-length"]) - except ValueError: - pass - - return 0 - - @property - def mimetype(self): - """Like :attr:`content_type`, but without parameters (eg, without - charset, type etc.) and always lowercase. For example if the content - type is ``text/HTML; charset=utf-8`` the mimetype would be - ``'text/html'``. - - .. versionadded:: 0.7 - """ - self._parse_content_type() - return self._parsed_content_type[0].lower() - - @property - def mimetype_params(self): - """The mimetype parameters as dict. For example if the content - type is ``text/html; charset=utf-8`` the params would be - ``{'charset': 'utf-8'}``. - - .. versionadded:: 0.7 - """ - self._parse_content_type() - return self._parsed_content_type[1] - - def save(self, dst, buffer_size=16384): - """Save the file to a destination path or file object. If the - destination is a file object you have to close it yourself after the - call. The buffer size is the number of bytes held in memory during - the copy process. It defaults to 16KB. - - For secure file saving also have a look at :func:`secure_filename`. - - :param dst: a filename, :class:`os.PathLike`, or open file - object to write to. - :param buffer_size: Passed as the ``length`` parameter of - :func:`shutil.copyfileobj`. - - .. versionchanged:: 1.0 - Supports :mod:`pathlib`. - """ - from shutil import copyfileobj - - close_dst = False - - if hasattr(dst, "__fspath__"): - dst = fspath(dst) - - if isinstance(dst, str): - dst = open(dst, "wb") - close_dst = True - - try: - copyfileobj(self.stream, dst, buffer_size) - finally: - if close_dst: - dst.close() - - def close(self): - """Close the underlying file if possible.""" - try: - self.stream.close() - except Exception: - pass - - def __bool__(self): - return bool(self.filename) - - def __getattr__(self, name): - try: - return getattr(self.stream, name) - except AttributeError: - # SpooledTemporaryFile doesn't implement IOBase, get the - # attribute from its backing file instead. - # https://github.com/python/cpython/pull/3249 - if hasattr(self.stream, "_file"): - return getattr(self.stream._file, name) - raise - - def __iter__(self): - return iter(self.stream) - - def __repr__(self): - return f"<{type(self).__name__}: {self.filename!r} ({self.content_type!r})>" - - -class FileMultiDict(MultiDict): - """A special :class:`MultiDict` that has convenience methods to add - files to it. This is used for :class:`EnvironBuilder` and generally - useful for unittesting. - - .. versionadded:: 0.5 - """ - - def add_file(self, name, file, filename=None, content_type=None): - """Adds a new file to the dict. `file` can be a file name or - a :class:`file`-like or a :class:`FileStorage` object. - - :param name: the name of the field. - :param file: a filename or :class:`file`-like object - :param filename: an optional filename - :param content_type: an optional content type - """ - if isinstance(file, FileStorage): - value = file - else: - if isinstance(file, str): - if filename is None: - filename = file - file = open(file, "rb") - if filename and content_type is None: - content_type = ( - mimetypes.guess_type(filename)[0] or "application/octet-stream" - ) - value = FileStorage(file, filename, name, content_type) - - self.add(name, value) - - -# circular dependencies -from .. import http diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/file_storage.pyi b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/file_storage.pyi deleted file mode 100644 index 36a7ed9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/file_storage.pyi +++ /dev/null @@ -1,49 +0,0 @@ -from collections.abc import Iterator -from os import PathLike -from typing import Any -from typing import IO - -from .headers import Headers -from .structures import MultiDict - -class FileStorage: - name: str | None - stream: IO[bytes] - filename: str | None - headers: Headers - _parsed_content_type: tuple[str, dict[str, str]] - def __init__( - self, - stream: IO[bytes] | None = None, - filename: str | PathLike[str] | None = None, - name: str | None = None, - content_type: str | None = None, - content_length: int | None = None, - headers: Headers | None = None, - ) -> None: ... - def _parse_content_type(self) -> None: ... - @property - def content_type(self) -> str: ... - @property - def content_length(self) -> int: ... - @property - def mimetype(self) -> str: ... - @property - def mimetype_params(self) -> dict[str, str]: ... - def save( - self, dst: str | PathLike[str] | IO[bytes], buffer_size: int = ... - ) -> None: ... - def close(self) -> None: ... - def __bool__(self) -> bool: ... - def __getattr__(self, name: str) -> Any: ... - def __iter__(self) -> Iterator[bytes]: ... - def __repr__(self) -> str: ... - -class FileMultiDict(MultiDict[str, FileStorage]): - def add_file( - self, - name: str, - file: FileStorage | str | IO[bytes], - filename: str | None = None, - content_type: str | None = None, - ) -> None: ... diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/headers.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/headers.py deleted file mode 100644 index d9dd655..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/headers.py +++ /dev/null @@ -1,515 +0,0 @@ -from __future__ import annotations - -import re -import typing as t - -from .._internal import _missing -from ..exceptions import BadRequestKeyError -from .mixins import ImmutableHeadersMixin -from .structures import iter_multi_items -from .structures import MultiDict - - -class Headers: - """An object that stores some headers. It has a dict-like interface, - but is ordered, can store the same key multiple times, and iterating - yields ``(key, value)`` pairs instead of only keys. - - This data structure is useful if you want a nicer way to handle WSGI - headers which are stored as tuples in a list. - - From Werkzeug 0.3 onwards, the :exc:`KeyError` raised by this class is - also a subclass of the :class:`~exceptions.BadRequest` HTTP exception - and will render a page for a ``400 BAD REQUEST`` if caught in a - catch-all for HTTP exceptions. - - Headers is mostly compatible with the Python :class:`wsgiref.headers.Headers` - class, with the exception of `__getitem__`. :mod:`wsgiref` will return - `None` for ``headers['missing']``, whereas :class:`Headers` will raise - a :class:`KeyError`. - - To create a new ``Headers`` object, pass it a list, dict, or - other ``Headers`` object with default values. These values are - validated the same way values added later are. - - :param defaults: The list of default values for the :class:`Headers`. - - .. versionchanged:: 2.1.0 - Default values are validated the same as values added later. - - .. versionchanged:: 0.9 - This data structure now stores unicode values similar to how the - multi dicts do it. The main difference is that bytes can be set as - well which will automatically be latin1 decoded. - - .. versionchanged:: 0.9 - The :meth:`linked` function was removed without replacement as it - was an API that does not support the changes to the encoding model. - """ - - def __init__(self, defaults=None): - self._list = [] - if defaults is not None: - self.extend(defaults) - - def __getitem__(self, key, _get_mode=False): - if not _get_mode: - if isinstance(key, int): - return self._list[key] - elif isinstance(key, slice): - return self.__class__(self._list[key]) - if not isinstance(key, str): - raise BadRequestKeyError(key) - ikey = key.lower() - for k, v in self._list: - if k.lower() == ikey: - return v - # micro optimization: if we are in get mode we will catch that - # exception one stack level down so we can raise a standard - # key error instead of our special one. - if _get_mode: - raise KeyError() - raise BadRequestKeyError(key) - - def __eq__(self, other): - def lowered(item): - return (item[0].lower(),) + item[1:] - - return other.__class__ is self.__class__ and set( - map(lowered, other._list) - ) == set(map(lowered, self._list)) - - __hash__ = None - - def get(self, key, default=None, type=None): - """Return the default value if the requested data doesn't exist. - If `type` is provided and is a callable it should convert the value, - return it or raise a :exc:`ValueError` if that is not possible. In - this case the function will return the default as if the value was not - found: - - >>> d = Headers([('Content-Length', '42')]) - >>> d.get('Content-Length', type=int) - 42 - - :param key: The key to be looked up. - :param default: The default value to be returned if the key can't - be looked up. If not further specified `None` is - returned. - :param type: A callable that is used to cast the value in the - :class:`Headers`. If a :exc:`ValueError` is raised - by this callable the default value is returned. - - .. versionchanged:: 3.0 - The ``as_bytes`` parameter was removed. - - .. versionchanged:: 0.9 - The ``as_bytes`` parameter was added. - """ - try: - rv = self.__getitem__(key, _get_mode=True) - except KeyError: - return default - if type is None: - return rv - try: - return type(rv) - except ValueError: - return default - - def getlist(self, key, type=None): - """Return the list of items for a given key. If that key is not in the - :class:`Headers`, the return value will be an empty list. Just like - :meth:`get`, :meth:`getlist` accepts a `type` parameter. All items will - be converted with the callable defined there. - - :param key: The key to be looked up. - :param type: A callable that is used to cast the value in the - :class:`Headers`. If a :exc:`ValueError` is raised - by this callable the value will be removed from the list. - :return: a :class:`list` of all the values for the key. - - .. versionchanged:: 3.0 - The ``as_bytes`` parameter was removed. - - .. versionchanged:: 0.9 - The ``as_bytes`` parameter was added. - """ - ikey = key.lower() - result = [] - for k, v in self: - if k.lower() == ikey: - if type is not None: - try: - v = type(v) - except ValueError: - continue - result.append(v) - return result - - def get_all(self, name): - """Return a list of all the values for the named field. - - This method is compatible with the :mod:`wsgiref` - :meth:`~wsgiref.headers.Headers.get_all` method. - """ - return self.getlist(name) - - def items(self, lower=False): - for key, value in self: - if lower: - key = key.lower() - yield key, value - - def keys(self, lower=False): - for key, _ in self.items(lower): - yield key - - def values(self): - for _, value in self.items(): - yield value - - def extend(self, *args, **kwargs): - """Extend headers in this object with items from another object - containing header items as well as keyword arguments. - - To replace existing keys instead of extending, use - :meth:`update` instead. - - If provided, the first argument can be another :class:`Headers` - object, a :class:`MultiDict`, :class:`dict`, or iterable of - pairs. - - .. versionchanged:: 1.0 - Support :class:`MultiDict`. Allow passing ``kwargs``. - """ - if len(args) > 1: - raise TypeError(f"update expected at most 1 arguments, got {len(args)}") - - if args: - for key, value in iter_multi_items(args[0]): - self.add(key, value) - - for key, value in iter_multi_items(kwargs): - self.add(key, value) - - def __delitem__(self, key, _index_operation=True): - if _index_operation and isinstance(key, (int, slice)): - del self._list[key] - return - key = key.lower() - new = [] - for k, v in self._list: - if k.lower() != key: - new.append((k, v)) - self._list[:] = new - - def remove(self, key): - """Remove a key. - - :param key: The key to be removed. - """ - return self.__delitem__(key, _index_operation=False) - - def pop(self, key=None, default=_missing): - """Removes and returns a key or index. - - :param key: The key to be popped. If this is an integer the item at - that position is removed, if it's a string the value for - that key is. If the key is omitted or `None` the last - item is removed. - :return: an item. - """ - if key is None: - return self._list.pop() - if isinstance(key, int): - return self._list.pop(key) - try: - rv = self[key] - self.remove(key) - except KeyError: - if default is not _missing: - return default - raise - return rv - - def popitem(self): - """Removes a key or index and returns a (key, value) item.""" - return self.pop() - - def __contains__(self, key): - """Check if a key is present.""" - try: - self.__getitem__(key, _get_mode=True) - except KeyError: - return False - return True - - def __iter__(self): - """Yield ``(key, value)`` tuples.""" - return iter(self._list) - - def __len__(self): - return len(self._list) - - def add(self, _key, _value, **kw): - """Add a new header tuple to the list. - - Keyword arguments can specify additional parameters for the header - value, with underscores converted to dashes:: - - >>> d = Headers() - >>> d.add('Content-Type', 'text/plain') - >>> d.add('Content-Disposition', 'attachment', filename='foo.png') - - The keyword argument dumping uses :func:`dump_options_header` - behind the scenes. - - .. versionadded:: 0.4.1 - keyword arguments were added for :mod:`wsgiref` compatibility. - """ - if kw: - _value = _options_header_vkw(_value, kw) - _value = _str_header_value(_value) - self._list.append((_key, _value)) - - def add_header(self, _key, _value, **_kw): - """Add a new header tuple to the list. - - An alias for :meth:`add` for compatibility with the :mod:`wsgiref` - :meth:`~wsgiref.headers.Headers.add_header` method. - """ - self.add(_key, _value, **_kw) - - def clear(self): - """Clears all headers.""" - del self._list[:] - - def set(self, _key, _value, **kw): - """Remove all header tuples for `key` and add a new one. The newly - added key either appears at the end of the list if there was no - entry or replaces the first one. - - Keyword arguments can specify additional parameters for the header - value, with underscores converted to dashes. See :meth:`add` for - more information. - - .. versionchanged:: 0.6.1 - :meth:`set` now accepts the same arguments as :meth:`add`. - - :param key: The key to be inserted. - :param value: The value to be inserted. - """ - if kw: - _value = _options_header_vkw(_value, kw) - _value = _str_header_value(_value) - if not self._list: - self._list.append((_key, _value)) - return - listiter = iter(self._list) - ikey = _key.lower() - for idx, (old_key, _old_value) in enumerate(listiter): - if old_key.lower() == ikey: - # replace first occurrence - self._list[idx] = (_key, _value) - break - else: - self._list.append((_key, _value)) - return - self._list[idx + 1 :] = [t for t in listiter if t[0].lower() != ikey] - - def setlist(self, key, values): - """Remove any existing values for a header and add new ones. - - :param key: The header key to set. - :param values: An iterable of values to set for the key. - - .. versionadded:: 1.0 - """ - if values: - values_iter = iter(values) - self.set(key, next(values_iter)) - - for value in values_iter: - self.add(key, value) - else: - self.remove(key) - - def setdefault(self, key, default): - """Return the first value for the key if it is in the headers, - otherwise set the header to the value given by ``default`` and - return that. - - :param key: The header key to get. - :param default: The value to set for the key if it is not in the - headers. - """ - if key in self: - return self[key] - - self.set(key, default) - return default - - def setlistdefault(self, key, default): - """Return the list of values for the key if it is in the - headers, otherwise set the header to the list of values given - by ``default`` and return that. - - Unlike :meth:`MultiDict.setlistdefault`, modifying the returned - list will not affect the headers. - - :param key: The header key to get. - :param default: An iterable of values to set for the key if it - is not in the headers. - - .. versionadded:: 1.0 - """ - if key not in self: - self.setlist(key, default) - - return self.getlist(key) - - def __setitem__(self, key, value): - """Like :meth:`set` but also supports index/slice based setting.""" - if isinstance(key, (slice, int)): - if isinstance(key, int): - value = [value] - value = [(k, _str_header_value(v)) for (k, v) in value] - if isinstance(key, int): - self._list[key] = value[0] - else: - self._list[key] = value - else: - self.set(key, value) - - def update(self, *args, **kwargs): - """Replace headers in this object with items from another - headers object and keyword arguments. - - To extend existing keys instead of replacing, use :meth:`extend` - instead. - - If provided, the first argument can be another :class:`Headers` - object, a :class:`MultiDict`, :class:`dict`, or iterable of - pairs. - - .. versionadded:: 1.0 - """ - if len(args) > 1: - raise TypeError(f"update expected at most 1 arguments, got {len(args)}") - - if args: - mapping = args[0] - - if isinstance(mapping, (Headers, MultiDict)): - for key in mapping.keys(): - self.setlist(key, mapping.getlist(key)) - elif isinstance(mapping, dict): - for key, value in mapping.items(): - if isinstance(value, (list, tuple)): - self.setlist(key, value) - else: - self.set(key, value) - else: - for key, value in mapping: - self.set(key, value) - - for key, value in kwargs.items(): - if isinstance(value, (list, tuple)): - self.setlist(key, value) - else: - self.set(key, value) - - def to_wsgi_list(self): - """Convert the headers into a list suitable for WSGI. - - :return: list - """ - return list(self) - - def copy(self): - return self.__class__(self._list) - - def __copy__(self): - return self.copy() - - def __str__(self): - """Returns formatted headers suitable for HTTP transmission.""" - strs = [] - for key, value in self.to_wsgi_list(): - strs.append(f"{key}: {value}") - strs.append("\r\n") - return "\r\n".join(strs) - - def __repr__(self): - return f"{type(self).__name__}({list(self)!r})" - - -def _options_header_vkw(value: str, kw: dict[str, t.Any]): - return http.dump_options_header( - value, {k.replace("_", "-"): v for k, v in kw.items()} - ) - - -_newline_re = re.compile(r"[\r\n]") - - -def _str_header_value(value: t.Any) -> str: - if not isinstance(value, str): - value = str(value) - - if _newline_re.search(value) is not None: - raise ValueError("Header values must not contain newline characters.") - - return value - - -class EnvironHeaders(ImmutableHeadersMixin, Headers): - """Read only version of the headers from a WSGI environment. This - provides the same interface as `Headers` and is constructed from - a WSGI environment. - From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a - subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will - render a page for a ``400 BAD REQUEST`` if caught in a catch-all for - HTTP exceptions. - """ - - def __init__(self, environ): - self.environ = environ - - def __eq__(self, other): - return self.environ is other.environ - - __hash__ = None - - def __getitem__(self, key, _get_mode=False): - # _get_mode is a no-op for this class as there is no index but - # used because get() calls it. - if not isinstance(key, str): - raise KeyError(key) - key = key.upper().replace("-", "_") - if key in {"CONTENT_TYPE", "CONTENT_LENGTH"}: - return self.environ[key] - return self.environ[f"HTTP_{key}"] - - def __len__(self): - # the iter is necessary because otherwise list calls our - # len which would call list again and so forth. - return len(list(iter(self))) - - def __iter__(self): - for key, value in self.environ.items(): - 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"} and value: - yield key.replace("_", "-").title(), value - - def copy(self): - raise TypeError(f"cannot create {type(self).__name__!r} copies") - - -# circular dependencies -from .. import http diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/headers.pyi b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/headers.pyi deleted file mode 100644 index 8650222..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/headers.pyi +++ /dev/null @@ -1,109 +0,0 @@ -from collections.abc import Callable -from collections.abc import Iterable -from collections.abc import Iterator -from collections.abc import Mapping -from typing import Literal -from typing import NoReturn -from typing import overload -from typing import TypeVar - -from _typeshed import SupportsKeysAndGetItem -from _typeshed.wsgi import WSGIEnvironment - -from .mixins import ImmutableHeadersMixin - -D = TypeVar("D") -T = TypeVar("T") - -class Headers(dict[str, str]): - _list: list[tuple[str, str]] - def __init__( - self, - defaults: Mapping[str, str | Iterable[str]] - | Iterable[tuple[str, str]] - | None = None, - ) -> None: ... - @overload - def __getitem__(self, key: str) -> str: ... - @overload - def __getitem__(self, key: int) -> tuple[str, str]: ... - @overload - def __getitem__(self, key: slice) -> Headers: ... - @overload - def __getitem__(self, key: str, _get_mode: Literal[True] = ...) -> str: ... - def __eq__(self, other: object) -> bool: ... - @overload # type: ignore - def get(self, key: str, default: str) -> str: ... - @overload - def get(self, key: str, default: str | None = None) -> str | None: ... - @overload - def get( - self, key: str, default: T | None = None, type: Callable[[str], T] = ... - ) -> T | None: ... - @overload - def getlist(self, key: str) -> list[str]: ... - @overload - def getlist(self, key: str, type: Callable[[str], T]) -> list[T]: ... - def get_all(self, name: str) -> list[str]: ... - def items( # type: ignore - self, lower: bool = False - ) -> Iterator[tuple[str, str]]: ... - def keys(self, lower: bool = False) -> Iterator[str]: ... # type: ignore - def values(self) -> Iterator[str]: ... # type: ignore - def extend( - self, - *args: Mapping[str, str | Iterable[str]] | Iterable[tuple[str, str]], - **kwargs: str | Iterable[str], - ) -> None: ... - @overload - def __delitem__(self, key: str | int | slice) -> None: ... - @overload - def __delitem__(self, key: str, _index_operation: Literal[False]) -> None: ... - def remove(self, key: str) -> None: ... - @overload # type: ignore - def pop(self, key: str, default: str | None = None) -> str: ... - @overload - def pop( - self, key: int | None = None, default: tuple[str, str] | None = None - ) -> tuple[str, str]: ... - def popitem(self) -> tuple[str, str]: ... - def __contains__(self, key: str) -> bool: ... # type: ignore - def has_key(self, key: str) -> bool: ... - def __iter__(self) -> Iterator[tuple[str, str]]: ... # type: ignore - def add(self, _key: str, _value: str, **kw: str) -> None: ... - def _validate_value(self, value: str) -> None: ... - def add_header(self, _key: str, _value: str, **_kw: str) -> None: ... - def clear(self) -> None: ... - def set(self, _key: str, _value: str, **kw: str) -> None: ... - def setlist(self, key: str, values: Iterable[str]) -> None: ... - def setdefault(self, key: str, default: str) -> str: ... - def setlistdefault(self, key: str, default: Iterable[str]) -> None: ... - @overload - def __setitem__(self, key: str, value: str) -> None: ... - @overload - def __setitem__(self, key: int, value: tuple[str, str]) -> None: ... - @overload - def __setitem__(self, key: slice, value: Iterable[tuple[str, str]]) -> None: ... - @overload - def update( - self, __m: SupportsKeysAndGetItem[str, str], **kwargs: str | Iterable[str] - ) -> None: ... - @overload - def update( - self, __m: Iterable[tuple[str, str]], **kwargs: str | Iterable[str] - ) -> None: ... - @overload - def update(self, **kwargs: str | Iterable[str]) -> None: ... - def to_wsgi_list(self) -> list[tuple[str, str]]: ... - def copy(self) -> Headers: ... - def __copy__(self) -> Headers: ... - -class EnvironHeaders(ImmutableHeadersMixin, Headers): - environ: WSGIEnvironment - def __init__(self, environ: WSGIEnvironment) -> None: ... - def __eq__(self, other: object) -> bool: ... - def __getitem__( # type: ignore - self, key: str, _get_mode: Literal[False] = False - ) -> str: ... - def __iter__(self) -> Iterator[tuple[str, str]]: ... # type: ignore - def copy(self) -> NoReturn: ... diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/mixins.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/mixins.py deleted file mode 100644 index 2c84ca8..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/mixins.py +++ /dev/null @@ -1,242 +0,0 @@ -from __future__ import annotations - -from itertools import repeat - -from .._internal import _missing - - -def is_immutable(self): - raise TypeError(f"{type(self).__name__!r} objects are immutable") - - -class ImmutableListMixin: - """Makes a :class:`list` immutable. - - .. versionadded:: 0.5 - - :private: - """ - - _hash_cache = None - - def __hash__(self): - if self._hash_cache is not None: - return self._hash_cache - rv = self._hash_cache = hash(tuple(self)) - return rv - - def __reduce_ex__(self, protocol): - return type(self), (list(self),) - - def __delitem__(self, key): - is_immutable(self) - - def __iadd__(self, other): - is_immutable(self) - - def __imul__(self, other): - is_immutable(self) - - def __setitem__(self, key, value): - is_immutable(self) - - def append(self, item): - is_immutable(self) - - def remove(self, item): - is_immutable(self) - - def extend(self, iterable): - is_immutable(self) - - def insert(self, pos, value): - is_immutable(self) - - def pop(self, index=-1): - is_immutable(self) - - def reverse(self): - is_immutable(self) - - def sort(self, key=None, reverse=False): - is_immutable(self) - - -class ImmutableDictMixin: - """Makes a :class:`dict` immutable. - - .. versionadded:: 0.5 - - :private: - """ - - _hash_cache = None - - @classmethod - def fromkeys(cls, keys, value=None): - instance = super().__new__(cls) - instance.__init__(zip(keys, repeat(value))) - return instance - - def __reduce_ex__(self, protocol): - return type(self), (dict(self),) - - def _iter_hashitems(self): - return self.items() - - def __hash__(self): - if self._hash_cache is not None: - return self._hash_cache - rv = self._hash_cache = hash(frozenset(self._iter_hashitems())) - return rv - - def setdefault(self, key, default=None): - is_immutable(self) - - def update(self, *args, **kwargs): - is_immutable(self) - - def pop(self, key, default=None): - is_immutable(self) - - def popitem(self): - is_immutable(self) - - def __setitem__(self, key, value): - is_immutable(self) - - def __delitem__(self, key): - is_immutable(self) - - def clear(self): - is_immutable(self) - - -class ImmutableMultiDictMixin(ImmutableDictMixin): - """Makes a :class:`MultiDict` immutable. - - .. versionadded:: 0.5 - - :private: - """ - - def __reduce_ex__(self, protocol): - return type(self), (list(self.items(multi=True)),) - - def _iter_hashitems(self): - return self.items(multi=True) - - def add(self, key, value): - is_immutable(self) - - def popitemlist(self): - is_immutable(self) - - def poplist(self, key): - is_immutable(self) - - def setlist(self, key, new_list): - is_immutable(self) - - def setlistdefault(self, key, default_list=None): - is_immutable(self) - - -class ImmutableHeadersMixin: - """Makes a :class:`Headers` immutable. We do not mark them as - hashable though since the only usecase for this datastructure - in Werkzeug is a view on a mutable structure. - - .. versionadded:: 0.5 - - :private: - """ - - def __delitem__(self, key, **kwargs): - is_immutable(self) - - def __setitem__(self, key, value): - is_immutable(self) - - def set(self, _key, _value, **kwargs): - is_immutable(self) - - def setlist(self, key, values): - is_immutable(self) - - def add(self, _key, _value, **kwargs): - is_immutable(self) - - def add_header(self, _key, _value, **_kwargs): - is_immutable(self) - - def remove(self, key): - is_immutable(self) - - def extend(self, *args, **kwargs): - is_immutable(self) - - def update(self, *args, **kwargs): - is_immutable(self) - - def insert(self, pos, value): - is_immutable(self) - - def pop(self, key=None, default=_missing): - is_immutable(self) - - def popitem(self): - is_immutable(self) - - def setdefault(self, key, default): - is_immutable(self) - - def setlistdefault(self, key, default): - is_immutable(self) - - -def _calls_update(name): - def oncall(self, *args, **kw): - rv = getattr(super(UpdateDictMixin, self), name)(*args, **kw) - - if self.on_update is not None: - self.on_update(self) - - return rv - - oncall.__name__ = name - return oncall - - -class UpdateDictMixin(dict): - """Makes dicts call `self.on_update` on modifications. - - .. versionadded:: 0.5 - - :private: - """ - - on_update = None - - def setdefault(self, key, default=None): - modified = key not in self - rv = super().setdefault(key, default) - if modified and self.on_update is not None: - self.on_update(self) - return rv - - def pop(self, key, default=_missing): - modified = key in self - if default is _missing: - rv = super().pop(key) - else: - rv = super().pop(key, default) - if modified and self.on_update is not None: - self.on_update(self) - return rv - - __setitem__ = _calls_update("__setitem__") - __delitem__ = _calls_update("__delitem__") - clear = _calls_update("clear") - popitem = _calls_update("popitem") - update = _calls_update("update") diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/mixins.pyi b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/mixins.pyi deleted file mode 100644 index 40453f7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/mixins.pyi +++ /dev/null @@ -1,97 +0,0 @@ -from collections.abc import Callable -from collections.abc import Hashable -from collections.abc import Iterable -from typing import Any -from typing import NoReturn -from typing import overload -from typing import SupportsIndex -from typing import TypeVar - -from _typeshed import SupportsKeysAndGetItem - -from .headers import Headers - -K = TypeVar("K") -T = TypeVar("T") -V = TypeVar("V") - -def is_immutable(self: object) -> NoReturn: ... - -class ImmutableListMixin(list[V]): - _hash_cache: int | None - def __hash__(self) -> int: ... # type: ignore - def __delitem__(self, key: SupportsIndex | slice) -> NoReturn: ... - def __iadd__(self, other: Any) -> NoReturn: ... # type: ignore - def __imul__(self, other: SupportsIndex) -> NoReturn: ... - def __setitem__(self, key: int | slice, value: V) -> NoReturn: ... # type: ignore - def append(self, value: V) -> NoReturn: ... - def remove(self, value: V) -> NoReturn: ... - def extend(self, values: Iterable[V]) -> NoReturn: ... - def insert(self, pos: SupportsIndex, value: V) -> NoReturn: ... - def pop(self, index: SupportsIndex = -1) -> NoReturn: ... - def reverse(self) -> NoReturn: ... - def sort( - self, key: Callable[[V], Any] | None = None, reverse: bool = False - ) -> NoReturn: ... - -class ImmutableDictMixin(dict[K, V]): - _hash_cache: int | None - @classmethod - def fromkeys( # type: ignore - cls, keys: Iterable[K], value: V | None = None - ) -> ImmutableDictMixin[K, V]: ... - def _iter_hashitems(self) -> Iterable[Hashable]: ... - def __hash__(self) -> int: ... # type: ignore - def setdefault(self, key: K, default: V | None = None) -> NoReturn: ... - def update(self, *args: Any, **kwargs: V) -> NoReturn: ... - def pop(self, key: K, default: V | None = None) -> NoReturn: ... # type: ignore - def popitem(self) -> NoReturn: ... - def __setitem__(self, key: K, value: V) -> NoReturn: ... - def __delitem__(self, key: K) -> NoReturn: ... - def clear(self) -> NoReturn: ... - -class ImmutableMultiDictMixin(ImmutableDictMixin[K, V]): - def _iter_hashitems(self) -> Iterable[Hashable]: ... - def add(self, key: K, value: V) -> NoReturn: ... - def popitemlist(self) -> NoReturn: ... - def poplist(self, key: K) -> NoReturn: ... - def setlist(self, key: K, new_list: Iterable[V]) -> NoReturn: ... - def setlistdefault( - self, key: K, default_list: Iterable[V] | None = None - ) -> NoReturn: ... - -class ImmutableHeadersMixin(Headers): - def __delitem__(self, key: Any, _index_operation: bool = True) -> NoReturn: ... - def __setitem__(self, key: Any, value: Any) -> NoReturn: ... - def set(self, _key: Any, _value: Any, **kw: Any) -> NoReturn: ... - def setlist(self, key: Any, values: Any) -> NoReturn: ... - def add(self, _key: Any, _value: Any, **kw: Any) -> NoReturn: ... - def add_header(self, _key: Any, _value: Any, **_kw: Any) -> NoReturn: ... - def remove(self, key: Any) -> NoReturn: ... - def extend(self, *args: Any, **kwargs: Any) -> NoReturn: ... - def update(self, *args: Any, **kwargs: Any) -> NoReturn: ... - def insert(self, pos: Any, value: Any) -> NoReturn: ... - def pop(self, key: Any = None, default: Any = ...) -> NoReturn: ... - def popitem(self) -> NoReturn: ... - def setdefault(self, key: Any, default: Any) -> NoReturn: ... - def setlistdefault(self, key: Any, default: Any) -> NoReturn: ... - -def _calls_update(name: str) -> Callable[[UpdateDictMixin[K, V]], Any]: ... - -class UpdateDictMixin(dict[K, V]): - on_update: Callable[[UpdateDictMixin[K, V] | None, None], None] - def setdefault(self, key: K, default: V | None = None) -> V: ... - @overload - def pop(self, key: K) -> V: ... - @overload - def pop(self, key: K, default: V | T = ...) -> V | T: ... - def __setitem__(self, key: K, value: V) -> None: ... - def __delitem__(self, key: K) -> None: ... - def clear(self) -> None: ... - def popitem(self) -> tuple[K, V]: ... - @overload - def update(self, __m: SupportsKeysAndGetItem[K, V], **kwargs: V) -> None: ... - @overload - def update(self, __m: Iterable[tuple[K, V]], **kwargs: V) -> None: ... - @overload - def update(self, **kwargs: V) -> None: ... diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/range.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/range.py deleted file mode 100644 index 7011ea4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/range.py +++ /dev/null @@ -1,180 +0,0 @@ -from __future__ import annotations - - -class IfRange: - """Very simple object that represents the `If-Range` header in parsed - form. It will either have neither a etag or date or one of either but - never both. - - .. versionadded:: 0.7 - """ - - def __init__(self, etag=None, date=None): - #: The etag parsed and unquoted. Ranges always operate on strong - #: etags so the weakness information is not necessary. - self.etag = etag - #: The date in parsed format or `None`. - self.date = date - - def to_header(self): - """Converts the object back into an HTTP header.""" - if self.date is not None: - return http.http_date(self.date) - if self.etag is not None: - return http.quote_etag(self.etag) - return "" - - def __str__(self): - return self.to_header() - - def __repr__(self): - return f"<{type(self).__name__} {str(self)!r}>" - - -class Range: - """Represents a ``Range`` header. All methods only support only - bytes as the unit. Stores a list of ranges if given, but the methods - only work if only one range is provided. - - :raise ValueError: If the ranges provided are invalid. - - .. versionchanged:: 0.15 - The ranges passed in are validated. - - .. versionadded:: 0.7 - """ - - def __init__(self, units, ranges): - #: The units of this range. Usually "bytes". - self.units = units - #: A list of ``(begin, end)`` tuples for the range header provided. - #: The ranges are non-inclusive. - self.ranges = ranges - - for start, end in ranges: - if start is None or (end is not None and (start < 0 or start >= end)): - raise ValueError(f"{(start, end)} is not a valid range.") - - def range_for_length(self, length): - """If the range is for bytes, the length is not None and there is - exactly one range and it is satisfiable it returns a ``(start, stop)`` - tuple, otherwise `None`. - """ - if self.units != "bytes" or length is None or len(self.ranges) != 1: - return None - start, end = self.ranges[0] - if end is None: - end = length - if start < 0: - start += length - if http.is_byte_range_valid(start, end, length): - return start, min(end, length) - return None - - def make_content_range(self, length): - """Creates a :class:`~werkzeug.datastructures.ContentRange` object - from the current range and given content length. - """ - rng = self.range_for_length(length) - if rng is not None: - return ContentRange(self.units, rng[0], rng[1], length) - return None - - def to_header(self): - """Converts the object back into an HTTP header.""" - ranges = [] - for begin, end in self.ranges: - if end is None: - ranges.append(f"{begin}-" if begin >= 0 else str(begin)) - else: - ranges.append(f"{begin}-{end - 1}") - return f"{self.units}={','.join(ranges)}" - - def to_content_range_header(self, length): - """Converts the object into `Content-Range` HTTP header, - based on given length - """ - range = self.range_for_length(length) - if range is not None: - return f"{self.units} {range[0]}-{range[1] - 1}/{length}" - return None - - def __str__(self): - return self.to_header() - - def __repr__(self): - return f"<{type(self).__name__} {str(self)!r}>" - - -def _callback_property(name): - def fget(self): - return getattr(self, name) - - def fset(self, value): - setattr(self, name, value) - if self.on_update is not None: - self.on_update(self) - - return property(fget, fset) - - -class ContentRange: - """Represents the content range header. - - .. versionadded:: 0.7 - """ - - def __init__(self, units, start, stop, length=None, on_update=None): - assert http.is_byte_range_valid(start, stop, length), "Bad range provided" - self.on_update = on_update - self.set(start, stop, length, units) - - #: The units to use, usually "bytes" - units = _callback_property("_units") - #: The start point of the range or `None`. - start = _callback_property("_start") - #: The stop point of the range (non-inclusive) or `None`. Can only be - #: `None` if also start is `None`. - stop = _callback_property("_stop") - #: The length of the range or `None`. - length = _callback_property("_length") - - def set(self, start, stop, length=None, units="bytes"): - """Simple method to update the ranges.""" - assert http.is_byte_range_valid(start, stop, length), "Bad range provided" - self._units = units - self._start = start - self._stop = stop - self._length = length - if self.on_update is not None: - self.on_update(self) - - def unset(self): - """Sets the units to `None` which indicates that the header should - no longer be used. - """ - self.set(None, None, units=None) - - def to_header(self): - if self.units is None: - return "" - if self.length is None: - length = "*" - else: - length = self.length - if self.start is None: - return f"{self.units} */{length}" - return f"{self.units} {self.start}-{self.stop - 1}/{length}" - - def __bool__(self): - return self.units is not None - - def __str__(self): - return self.to_header() - - def __repr__(self): - return f"<{type(self).__name__} {str(self)!r}>" - - -# circular dependencies -from .. import http diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/range.pyi b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/range.pyi deleted file mode 100644 index f38ad69..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/range.pyi +++ /dev/null @@ -1,57 +0,0 @@ -from collections.abc import Callable -from datetime import datetime - -class IfRange: - etag: str | None - date: datetime | None - def __init__( - self, etag: str | None = None, date: datetime | None = None - ) -> None: ... - def to_header(self) -> str: ... - -class Range: - units: str - ranges: list[tuple[int, int | None]] - def __init__(self, units: str, ranges: list[tuple[int, int | None]]) -> None: ... - def range_for_length(self, length: int | None) -> tuple[int, int] | None: ... - def make_content_range(self, length: int | None) -> ContentRange | None: ... - def to_header(self) -> str: ... - def to_content_range_header(self, length: int | None) -> str | None: ... - -def _callback_property(name: str) -> property: ... - -class ContentRange: - on_update: Callable[[ContentRange], None] | None - def __init__( - self, - units: str | None, - start: int | None, - stop: int | None, - length: int | None = None, - on_update: Callable[[ContentRange], None] | None = None, - ) -> None: ... - @property - def units(self) -> str | None: ... - @units.setter - def units(self, value: str | None) -> None: ... - @property - def start(self) -> int | None: ... - @start.setter - def start(self, value: int | None) -> None: ... - @property - def stop(self) -> int | None: ... - @stop.setter - def stop(self, value: int | None) -> None: ... - @property - def length(self) -> int | None: ... - @length.setter - def length(self, value: int | None) -> None: ... - def set( - self, - start: int | None, - stop: int | None, - length: int | None = None, - units: str | None = "bytes", - ) -> None: ... - def unset(self) -> None: ... - def to_header(self) -> str: ... diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/structures.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/structures.py deleted file mode 100644 index 4279ceb..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/structures.py +++ /dev/null @@ -1,1010 +0,0 @@ -from __future__ import annotations - -from collections.abc import MutableSet -from copy import deepcopy - -from .. import exceptions -from .._internal import _missing -from .mixins import ImmutableDictMixin -from .mixins import ImmutableListMixin -from .mixins import ImmutableMultiDictMixin -from .mixins import UpdateDictMixin - - -def is_immutable(self): - raise TypeError(f"{type(self).__name__!r} objects are immutable") - - -def iter_multi_items(mapping): - """Iterates over the items of a mapping yielding keys and values - without dropping any from more complex structures. - """ - if isinstance(mapping, MultiDict): - yield from mapping.items(multi=True) - elif isinstance(mapping, dict): - for key, value in mapping.items(): - if isinstance(value, (tuple, list)): - for v in value: - yield key, v - else: - yield key, value - else: - yield from mapping - - -class ImmutableList(ImmutableListMixin, list): - """An immutable :class:`list`. - - .. versionadded:: 0.5 - - :private: - """ - - def __repr__(self): - return f"{type(self).__name__}({list.__repr__(self)})" - - -class TypeConversionDict(dict): - """Works like a regular dict but the :meth:`get` method can perform - type conversions. :class:`MultiDict` and :class:`CombinedMultiDict` - are subclasses of this class and provide the same feature. - - .. versionadded:: 0.5 - """ - - def get(self, key, default=None, type=None): - """Return the default value if the requested data doesn't exist. - If `type` is provided and is a callable it should convert the value, - return it or raise a :exc:`ValueError` if that is not possible. In - this case the function will return the default as if the value was not - found: - - >>> d = TypeConversionDict(foo='42', bar='blub') - >>> d.get('foo', type=int) - 42 - >>> d.get('bar', -1, type=int) - -1 - - :param key: The key to be looked up. - :param default: The default value to be returned if the key can't - be looked up. If not further specified `None` is - returned. - :param type: A callable that is used to cast the value in the - :class:`MultiDict`. If a :exc:`ValueError` or a - :exc:`TypeError` is raised by this callable the default - value is returned. - - .. versionchanged:: 3.0.2 - Returns the default value on :exc:`TypeError`, too. - """ - try: - rv = self[key] - except KeyError: - return default - if type is not None: - try: - rv = type(rv) - except (ValueError, TypeError): - rv = default - return rv - - -class ImmutableTypeConversionDict(ImmutableDictMixin, TypeConversionDict): - """Works like a :class:`TypeConversionDict` but does not support - modifications. - - .. versionadded:: 0.5 - """ - - def copy(self): - """Return a shallow mutable copy of this object. Keep in mind that - the standard library's :func:`copy` function is a no-op for this class - like for any other python immutable type (eg: :class:`tuple`). - """ - return TypeConversionDict(self) - - def __copy__(self): - return self - - -class MultiDict(TypeConversionDict): - """A :class:`MultiDict` is a dictionary subclass customized to deal with - multiple values for the same key which is for example used by the parsing - functions in the wrappers. This is necessary because some HTML form - elements pass multiple values for the same key. - - :class:`MultiDict` implements all standard dictionary methods. - Internally, it saves all values for a key as a list, but the standard dict - access methods will only return the first value for a key. If you want to - gain access to the other values, too, you have to use the `list` methods as - explained below. - - Basic Usage: - - >>> d = MultiDict([('a', 'b'), ('a', 'c')]) - >>> d - MultiDict([('a', 'b'), ('a', 'c')]) - >>> d['a'] - 'b' - >>> d.getlist('a') - ['b', 'c'] - >>> 'a' in d - True - - It behaves like a normal dict thus all dict functions will only return the - first value when multiple values for one key are found. - - From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a - subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will - render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP - exceptions. - - A :class:`MultiDict` can be constructed from an iterable of - ``(key, value)`` tuples, a dict, a :class:`MultiDict` or from Werkzeug 0.2 - onwards some keyword parameters. - - :param mapping: the initial value for the :class:`MultiDict`. Either a - regular dict, an iterable of ``(key, value)`` tuples - or `None`. - """ - - def __init__(self, mapping=None): - if isinstance(mapping, MultiDict): - dict.__init__(self, ((k, vs[:]) for k, vs in mapping.lists())) - elif isinstance(mapping, dict): - tmp = {} - for key, value in mapping.items(): - if isinstance(value, (tuple, list)): - if len(value) == 0: - continue - value = list(value) - else: - value = [value] - tmp[key] = value - dict.__init__(self, tmp) - else: - tmp = {} - for key, value in mapping or (): - tmp.setdefault(key, []).append(value) - dict.__init__(self, tmp) - - def __getstate__(self): - return dict(self.lists()) - - def __setstate__(self, value): - dict.clear(self) - dict.update(self, value) - - def __iter__(self): - # Work around https://bugs.python.org/issue43246. - # (`return super().__iter__()` also works here, which makes this look - # even more like it should be a no-op, yet it isn't.) - return dict.__iter__(self) - - def __getitem__(self, key): - """Return the first data value for this key; - raises KeyError if not found. - - :param key: The key to be looked up. - :raise KeyError: if the key does not exist. - """ - - if key in self: - lst = dict.__getitem__(self, key) - if len(lst) > 0: - return lst[0] - raise exceptions.BadRequestKeyError(key) - - def __setitem__(self, key, value): - """Like :meth:`add` but removes an existing key first. - - :param key: the key for the value. - :param value: the value to set. - """ - dict.__setitem__(self, key, [value]) - - def add(self, key, value): - """Adds a new value for the key. - - .. versionadded:: 0.6 - - :param key: the key for the value. - :param value: the value to add. - """ - dict.setdefault(self, key, []).append(value) - - def getlist(self, key, type=None): - """Return the list of items for a given key. If that key is not in the - `MultiDict`, the return value will be an empty list. Just like `get`, - `getlist` accepts a `type` parameter. All items will be converted - with the callable defined there. - - :param key: The key to be looked up. - :param type: A callable that is used to cast the value in the - :class:`MultiDict`. If a :exc:`ValueError` is raised - by this callable the value will be removed from the list. - :return: a :class:`list` of all the values for the key. - """ - try: - rv = dict.__getitem__(self, key) - except KeyError: - return [] - if type is None: - return list(rv) - result = [] - for item in rv: - try: - result.append(type(item)) - except ValueError: - pass - return result - - def setlist(self, key, new_list): - """Remove the old values for a key and add new ones. Note that the list - you pass the values in will be shallow-copied before it is inserted in - the dictionary. - - >>> d = MultiDict() - >>> d.setlist('foo', ['1', '2']) - >>> d['foo'] - '1' - >>> d.getlist('foo') - ['1', '2'] - - :param key: The key for which the values are set. - :param new_list: An iterable with the new values for the key. Old values - are removed first. - """ - dict.__setitem__(self, key, list(new_list)) - - def setdefault(self, key, default=None): - """Returns the value for the key if it is in the dict, otherwise it - returns `default` and sets that value for `key`. - - :param key: The key to be looked up. - :param default: The default value to be returned if the key is not - in the dict. If not further specified it's `None`. - """ - if key not in self: - self[key] = default - else: - default = self[key] - return default - - def setlistdefault(self, key, default_list=None): - """Like `setdefault` but sets multiple values. The list returned - is not a copy, but the list that is actually used internally. This - means that you can put new values into the dict by appending items - to the list: - - >>> d = MultiDict({"foo": 1}) - >>> d.setlistdefault("foo").extend([2, 3]) - >>> d.getlist("foo") - [1, 2, 3] - - :param key: The key to be looked up. - :param default_list: An iterable of default values. It is either copied - (in case it was a list) or converted into a list - before returned. - :return: a :class:`list` - """ - if key not in self: - default_list = list(default_list or ()) - dict.__setitem__(self, key, default_list) - else: - default_list = dict.__getitem__(self, key) - return default_list - - def items(self, multi=False): - """Return an iterator of ``(key, value)`` pairs. - - :param multi: If set to `True` the iterator returned will have a pair - for each value of each key. Otherwise it will only - contain pairs for the first value of each key. - """ - for key, values in dict.items(self): - if multi: - for value in values: - yield key, value - else: - yield key, values[0] - - def lists(self): - """Return a iterator of ``(key, values)`` pairs, where values is the list - of all values associated with the key.""" - for key, values in dict.items(self): - yield key, list(values) - - def values(self): - """Returns an iterator of the first value on every key's value list.""" - for values in dict.values(self): - yield values[0] - - def listvalues(self): - """Return an iterator of all values associated with a key. Zipping - :meth:`keys` and this is the same as calling :meth:`lists`: - - >>> d = MultiDict({"foo": [1, 2, 3]}) - >>> zip(d.keys(), d.listvalues()) == d.lists() - True - """ - return dict.values(self) - - def copy(self): - """Return a shallow copy of this object.""" - return self.__class__(self) - - def deepcopy(self, memo=None): - """Return a deep copy of this object.""" - return self.__class__(deepcopy(self.to_dict(flat=False), memo)) - - def to_dict(self, flat=True): - """Return the contents as regular dict. If `flat` is `True` the - returned dict will only have the first item present, if `flat` is - `False` all values will be returned as lists. - - :param flat: If set to `False` the dict returned will have lists - with all the values in it. Otherwise it will only - contain the first value for each key. - :return: a :class:`dict` - """ - if flat: - return dict(self.items()) - return dict(self.lists()) - - def update(self, mapping): - """update() extends rather than replaces existing key lists: - - >>> a = MultiDict({'x': 1}) - >>> b = MultiDict({'x': 2, 'y': 3}) - >>> a.update(b) - >>> a - MultiDict([('y', 3), ('x', 1), ('x', 2)]) - - If the value list for a key in ``other_dict`` is empty, no new values - will be added to the dict and the key will not be created: - - >>> x = {'empty_list': []} - >>> y = MultiDict() - >>> y.update(x) - >>> y - MultiDict([]) - """ - for key, value in iter_multi_items(mapping): - MultiDict.add(self, key, value) - - def pop(self, key, default=_missing): - """Pop the first item for a list on the dict. Afterwards the - key is removed from the dict, so additional values are discarded: - - >>> d = MultiDict({"foo": [1, 2, 3]}) - >>> d.pop("foo") - 1 - >>> "foo" in d - False - - :param key: the key to pop. - :param default: if provided the value to return if the key was - not in the dictionary. - """ - try: - lst = dict.pop(self, key) - - if len(lst) == 0: - raise exceptions.BadRequestKeyError(key) - - return lst[0] - except KeyError: - if default is not _missing: - return default - - raise exceptions.BadRequestKeyError(key) from None - - def popitem(self): - """Pop an item from the dict.""" - try: - item = dict.popitem(self) - - if len(item[1]) == 0: - raise exceptions.BadRequestKeyError(item[0]) - - return (item[0], item[1][0]) - except KeyError as e: - raise exceptions.BadRequestKeyError(e.args[0]) from None - - def poplist(self, key): - """Pop the list for a key from the dict. If the key is not in the dict - an empty list is returned. - - .. versionchanged:: 0.5 - If the key does no longer exist a list is returned instead of - raising an error. - """ - return dict.pop(self, key, []) - - def popitemlist(self): - """Pop a ``(key, list)`` tuple from the dict.""" - try: - return dict.popitem(self) - except KeyError as e: - raise exceptions.BadRequestKeyError(e.args[0]) from None - - def __copy__(self): - return self.copy() - - def __deepcopy__(self, memo): - return self.deepcopy(memo=memo) - - def __repr__(self): - return f"{type(self).__name__}({list(self.items(multi=True))!r})" - - -class _omd_bucket: - """Wraps values in the :class:`OrderedMultiDict`. This makes it - possible to keep an order over multiple different keys. It requires - a lot of extra memory and slows down access a lot, but makes it - possible to access elements in O(1) and iterate in O(n). - """ - - __slots__ = ("prev", "key", "value", "next") - - def __init__(self, omd, key, value): - self.prev = omd._last_bucket - self.key = key - self.value = value - self.next = None - - if omd._first_bucket is None: - omd._first_bucket = self - if omd._last_bucket is not None: - omd._last_bucket.next = self - omd._last_bucket = self - - def unlink(self, omd): - if self.prev: - self.prev.next = self.next - if self.next: - self.next.prev = self.prev - if omd._first_bucket is self: - omd._first_bucket = self.next - if omd._last_bucket is self: - omd._last_bucket = self.prev - - -class OrderedMultiDict(MultiDict): - """Works like a regular :class:`MultiDict` but preserves the - order of the fields. To convert the ordered multi dict into a - list you can use the :meth:`items` method and pass it ``multi=True``. - - In general an :class:`OrderedMultiDict` is an order of magnitude - slower than a :class:`MultiDict`. - - .. admonition:: note - - Due to a limitation in Python you cannot convert an ordered - multi dict into a regular dict by using ``dict(multidict)``. - Instead you have to use the :meth:`to_dict` method, otherwise - the internal bucket objects are exposed. - """ - - def __init__(self, mapping=None): - dict.__init__(self) - self._first_bucket = self._last_bucket = None - if mapping is not None: - OrderedMultiDict.update(self, mapping) - - def __eq__(self, other): - if not isinstance(other, MultiDict): - return NotImplemented - if isinstance(other, OrderedMultiDict): - iter1 = iter(self.items(multi=True)) - iter2 = iter(other.items(multi=True)) - try: - for k1, v1 in iter1: - k2, v2 = next(iter2) - if k1 != k2 or v1 != v2: - return False - except StopIteration: - return False - try: - next(iter2) - except StopIteration: - return True - return False - if len(self) != len(other): - return False - for key, values in self.lists(): - if other.getlist(key) != values: - return False - return True - - __hash__ = None - - def __reduce_ex__(self, protocol): - return type(self), (list(self.items(multi=True)),) - - def __getstate__(self): - return list(self.items(multi=True)) - - def __setstate__(self, values): - dict.clear(self) - for key, value in values: - self.add(key, value) - - def __getitem__(self, key): - if key in self: - return dict.__getitem__(self, key)[0].value - raise exceptions.BadRequestKeyError(key) - - def __setitem__(self, key, value): - self.poplist(key) - self.add(key, value) - - def __delitem__(self, key): - self.pop(key) - - def keys(self): - return (key for key, value in self.items()) - - def __iter__(self): - return iter(self.keys()) - - def values(self): - return (value for key, value in self.items()) - - def items(self, multi=False): - ptr = self._first_bucket - if multi: - while ptr is not None: - yield ptr.key, ptr.value - ptr = ptr.next - else: - returned_keys = set() - while ptr is not None: - if ptr.key not in returned_keys: - returned_keys.add(ptr.key) - yield ptr.key, ptr.value - ptr = ptr.next - - def lists(self): - returned_keys = set() - ptr = self._first_bucket - while ptr is not None: - if ptr.key not in returned_keys: - yield ptr.key, self.getlist(ptr.key) - returned_keys.add(ptr.key) - ptr = ptr.next - - def listvalues(self): - for _key, values in self.lists(): - yield values - - def add(self, key, value): - dict.setdefault(self, key, []).append(_omd_bucket(self, key, value)) - - def getlist(self, key, type=None): - try: - rv = dict.__getitem__(self, key) - except KeyError: - return [] - if type is None: - return [x.value for x in rv] - result = [] - for item in rv: - try: - result.append(type(item.value)) - except ValueError: - pass - return result - - def setlist(self, key, new_list): - self.poplist(key) - for value in new_list: - self.add(key, value) - - def setlistdefault(self, key, default_list=None): - raise TypeError("setlistdefault is unsupported for ordered multi dicts") - - def update(self, mapping): - for key, value in iter_multi_items(mapping): - OrderedMultiDict.add(self, key, value) - - def poplist(self, key): - buckets = dict.pop(self, key, ()) - for bucket in buckets: - bucket.unlink(self) - return [x.value for x in buckets] - - def pop(self, key, default=_missing): - try: - buckets = dict.pop(self, key) - except KeyError: - if default is not _missing: - return default - - raise exceptions.BadRequestKeyError(key) from None - - for bucket in buckets: - bucket.unlink(self) - - return buckets[0].value - - def popitem(self): - try: - key, buckets = dict.popitem(self) - except KeyError as e: - raise exceptions.BadRequestKeyError(e.args[0]) from None - - for bucket in buckets: - bucket.unlink(self) - - return key, buckets[0].value - - def popitemlist(self): - try: - key, buckets = dict.popitem(self) - except KeyError as e: - raise exceptions.BadRequestKeyError(e.args[0]) from None - - for bucket in buckets: - bucket.unlink(self) - - return key, [x.value for x in buckets] - - -class CombinedMultiDict(ImmutableMultiDictMixin, MultiDict): - """A read only :class:`MultiDict` that you can pass multiple :class:`MultiDict` - instances as sequence and it will combine the return values of all wrapped - dicts: - - >>> from werkzeug.datastructures import CombinedMultiDict, MultiDict - >>> post = MultiDict([('foo', 'bar')]) - >>> get = MultiDict([('blub', 'blah')]) - >>> combined = CombinedMultiDict([get, post]) - >>> combined['foo'] - 'bar' - >>> combined['blub'] - 'blah' - - This works for all read operations and will raise a `TypeError` for - methods that usually change data which isn't possible. - - From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a - subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will - render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP - exceptions. - """ - - def __reduce_ex__(self, protocol): - return type(self), (self.dicts,) - - def __init__(self, dicts=None): - self.dicts = list(dicts) or [] - - @classmethod - def fromkeys(cls, keys, value=None): - raise TypeError(f"cannot create {cls.__name__!r} instances by fromkeys") - - def __getitem__(self, key): - for d in self.dicts: - if key in d: - return d[key] - raise exceptions.BadRequestKeyError(key) - - def get(self, key, default=None, type=None): - for d in self.dicts: - if key in d: - if type is not None: - try: - return type(d[key]) - except ValueError: - continue - return d[key] - return default - - def getlist(self, key, type=None): - rv = [] - for d in self.dicts: - rv.extend(d.getlist(key, type)) - return rv - - def _keys_impl(self): - """This function exists so __len__ can be implemented more efficiently, - saving one list creation from an iterator. - """ - rv = set() - rv.update(*self.dicts) - return rv - - def keys(self): - return self._keys_impl() - - def __iter__(self): - return iter(self.keys()) - - def items(self, multi=False): - found = set() - for d in self.dicts: - for key, value in d.items(multi): - if multi: - yield key, value - elif key not in found: - found.add(key) - yield key, value - - def values(self): - for _key, value in self.items(): - yield value - - def lists(self): - rv = {} - for d in self.dicts: - for key, values in d.lists(): - rv.setdefault(key, []).extend(values) - return list(rv.items()) - - def listvalues(self): - return (x[1] for x in self.lists()) - - def copy(self): - """Return a shallow mutable copy of this object. - - This returns a :class:`MultiDict` representing the data at the - time of copying. The copy will no longer reflect changes to the - wrapped dicts. - - .. versionchanged:: 0.15 - Return a mutable :class:`MultiDict`. - """ - return MultiDict(self) - - def to_dict(self, flat=True): - """Return the contents as regular dict. If `flat` is `True` the - returned dict will only have the first item present, if `flat` is - `False` all values will be returned as lists. - - :param flat: If set to `False` the dict returned will have lists - with all the values in it. Otherwise it will only - contain the first item for each key. - :return: a :class:`dict` - """ - if flat: - return dict(self.items()) - - return dict(self.lists()) - - def __len__(self): - return len(self._keys_impl()) - - def __contains__(self, key): - for d in self.dicts: - if key in d: - return True - return False - - def __repr__(self): - return f"{type(self).__name__}({self.dicts!r})" - - -class ImmutableDict(ImmutableDictMixin, dict): - """An immutable :class:`dict`. - - .. versionadded:: 0.5 - """ - - def __repr__(self): - return f"{type(self).__name__}({dict.__repr__(self)})" - - def copy(self): - """Return a shallow mutable copy of this object. Keep in mind that - the standard library's :func:`copy` function is a no-op for this class - like for any other python immutable type (eg: :class:`tuple`). - """ - return dict(self) - - def __copy__(self): - return self - - -class ImmutableMultiDict(ImmutableMultiDictMixin, MultiDict): - """An immutable :class:`MultiDict`. - - .. versionadded:: 0.5 - """ - - def copy(self): - """Return a shallow mutable copy of this object. Keep in mind that - the standard library's :func:`copy` function is a no-op for this class - like for any other python immutable type (eg: :class:`tuple`). - """ - return MultiDict(self) - - def __copy__(self): - return self - - -class ImmutableOrderedMultiDict(ImmutableMultiDictMixin, OrderedMultiDict): - """An immutable :class:`OrderedMultiDict`. - - .. versionadded:: 0.6 - """ - - def _iter_hashitems(self): - return enumerate(self.items(multi=True)) - - def copy(self): - """Return a shallow mutable copy of this object. Keep in mind that - the standard library's :func:`copy` function is a no-op for this class - like for any other python immutable type (eg: :class:`tuple`). - """ - return OrderedMultiDict(self) - - def __copy__(self): - return self - - -class CallbackDict(UpdateDictMixin, dict): - """A dict that calls a function passed every time something is changed. - The function is passed the dict instance. - """ - - def __init__(self, initial=None, on_update=None): - dict.__init__(self, initial or ()) - self.on_update = on_update - - def __repr__(self): - return f"<{type(self).__name__} {dict.__repr__(self)}>" - - -class HeaderSet(MutableSet): - """Similar to the :class:`ETags` class this implements a set-like structure. - Unlike :class:`ETags` this is case insensitive and used for vary, allow, and - content-language headers. - - If not constructed using the :func:`parse_set_header` function the - instantiation works like this: - - >>> hs = HeaderSet(['foo', 'bar', 'baz']) - >>> hs - HeaderSet(['foo', 'bar', 'baz']) - """ - - def __init__(self, headers=None, on_update=None): - self._headers = list(headers or ()) - self._set = {x.lower() for x in self._headers} - self.on_update = on_update - - def add(self, header): - """Add a new header to the set.""" - self.update((header,)) - - def remove(self, header): - """Remove a header from the set. This raises an :exc:`KeyError` if the - header is not in the set. - - .. versionchanged:: 0.5 - In older versions a :exc:`IndexError` was raised instead of a - :exc:`KeyError` if the object was missing. - - :param header: the header to be removed. - """ - key = header.lower() - if key not in self._set: - raise KeyError(header) - self._set.remove(key) - for idx, key in enumerate(self._headers): - if key.lower() == header: - del self._headers[idx] - break - if self.on_update is not None: - self.on_update(self) - - def update(self, iterable): - """Add all the headers from the iterable to the set. - - :param iterable: updates the set with the items from the iterable. - """ - inserted_any = False - for header in iterable: - key = header.lower() - if key not in self._set: - self._headers.append(header) - self._set.add(key) - inserted_any = True - if inserted_any and self.on_update is not None: - self.on_update(self) - - def discard(self, header): - """Like :meth:`remove` but ignores errors. - - :param header: the header to be discarded. - """ - try: - self.remove(header) - except KeyError: - pass - - def find(self, header): - """Return the index of the header in the set or return -1 if not found. - - :param header: the header to be looked up. - """ - header = header.lower() - for idx, item in enumerate(self._headers): - if item.lower() == header: - return idx - return -1 - - def index(self, header): - """Return the index of the header in the set or raise an - :exc:`IndexError`. - - :param header: the header to be looked up. - """ - rv = self.find(header) - if rv < 0: - raise IndexError(header) - return rv - - def clear(self): - """Clear the set.""" - self._set.clear() - del self._headers[:] - if self.on_update is not None: - self.on_update(self) - - def as_set(self, preserve_casing=False): - """Return the set as real python set type. When calling this, all - the items are converted to lowercase and the ordering is lost. - - :param preserve_casing: if set to `True` the items in the set returned - will have the original case like in the - :class:`HeaderSet`, otherwise they will - be lowercase. - """ - if preserve_casing: - return set(self._headers) - return set(self._set) - - def to_header(self): - """Convert the header set into an HTTP header string.""" - return ", ".join(map(http.quote_header_value, self._headers)) - - def __getitem__(self, idx): - return self._headers[idx] - - def __delitem__(self, idx): - rv = self._headers.pop(idx) - self._set.remove(rv.lower()) - if self.on_update is not None: - self.on_update(self) - - def __setitem__(self, idx, value): - old = self._headers[idx] - self._set.remove(old.lower()) - self._headers[idx] = value - self._set.add(value.lower()) - if self.on_update is not None: - self.on_update(self) - - def __contains__(self, header): - return header.lower() in self._set - - def __len__(self): - return len(self._set) - - def __iter__(self): - return iter(self._headers) - - def __bool__(self): - return bool(self._set) - - def __str__(self): - return self.to_header() - - def __repr__(self): - return f"{type(self).__name__}({self._headers!r})" - - -# circular dependencies -from .. import http diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/structures.pyi b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/structures.pyi deleted file mode 100644 index 7086dda..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/datastructures/structures.pyi +++ /dev/null @@ -1,206 +0,0 @@ -from collections.abc import Callable -from collections.abc import Iterable -from collections.abc import Iterator -from collections.abc import Mapping -from typing import Any -from typing import Generic -from typing import Literal -from typing import NoReturn -from typing import overload -from typing import TypeVar - -from .mixins import ImmutableDictMixin -from .mixins import ImmutableListMixin -from .mixins import ImmutableMultiDictMixin -from .mixins import UpdateDictMixin - -D = TypeVar("D") -K = TypeVar("K") -T = TypeVar("T") -V = TypeVar("V") -_CD = TypeVar("_CD", bound="CallbackDict[Any, Any]") - -def is_immutable(self: object) -> NoReturn: ... -def iter_multi_items( - mapping: Mapping[K, V | Iterable[V]] | Iterable[tuple[K, V]], -) -> Iterator[tuple[K, V]]: ... - -class ImmutableList(ImmutableListMixin[V]): ... - -class TypeConversionDict(dict[K, V]): - @overload - def get(self, key: K, default: None = ..., type: None = ...) -> V | None: ... - @overload - def get(self, key: K, default: D, type: None = ...) -> D | V: ... - @overload - def get(self, key: K, default: D, type: Callable[[V], T]) -> D | T: ... - @overload - def get(self, key: K, type: Callable[[V], T]) -> T | None: ... - -class ImmutableTypeConversionDict(ImmutableDictMixin[K, V], TypeConversionDict[K, V]): - def copy(self) -> TypeConversionDict[K, V]: ... - def __copy__(self) -> ImmutableTypeConversionDict[K, V]: ... - -class MultiDict(TypeConversionDict[K, V]): - def __init__( - self, - mapping: Mapping[K, Iterable[V] | V] | Iterable[tuple[K, V]] | None = None, - ) -> None: ... - def __getitem__(self, item: K) -> V: ... - def __setitem__(self, key: K, value: V) -> None: ... - def add(self, key: K, value: V) -> None: ... - @overload - def getlist(self, key: K) -> list[V]: ... - @overload - def getlist(self, key: K, type: Callable[[V], T] = ...) -> list[T]: ... - def setlist(self, key: K, new_list: Iterable[V]) -> None: ... - def setdefault(self, key: K, default: V | None = None) -> V: ... - def setlistdefault( - self, key: K, default_list: Iterable[V] | None = None - ) -> list[V]: ... - def items(self, multi: bool = False) -> Iterator[tuple[K, V]]: ... # type: ignore - def lists(self) -> Iterator[tuple[K, list[V]]]: ... - def values(self) -> Iterator[V]: ... # type: ignore - def listvalues(self) -> Iterator[list[V]]: ... - def copy(self) -> MultiDict[K, V]: ... - def deepcopy(self, memo: Any = None) -> MultiDict[K, V]: ... - @overload - def to_dict(self) -> dict[K, V]: ... - @overload - def to_dict(self, flat: Literal[False]) -> dict[K, list[V]]: ... - def update( # type: ignore - self, mapping: Mapping[K, Iterable[V] | V] | Iterable[tuple[K, V]] - ) -> None: ... - @overload - def pop(self, key: K) -> V: ... - @overload - def pop(self, key: K, default: V | T = ...) -> V | T: ... - def popitem(self) -> tuple[K, V]: ... - def poplist(self, key: K) -> list[V]: ... - def popitemlist(self) -> tuple[K, list[V]]: ... - def __copy__(self) -> MultiDict[K, V]: ... - def __deepcopy__(self, memo: Any) -> MultiDict[K, V]: ... - -class _omd_bucket(Generic[K, V]): - prev: _omd_bucket[K, V] | None - next: _omd_bucket[K, V] | None - key: K - value: V - def __init__(self, omd: OrderedMultiDict[K, V], key: K, value: V) -> None: ... - def unlink(self, omd: OrderedMultiDict[K, V]) -> None: ... - -class OrderedMultiDict(MultiDict[K, V]): - _first_bucket: _omd_bucket[K, V] | None - _last_bucket: _omd_bucket[K, V] | None - def __init__(self, mapping: Mapping[K, V] | None = None) -> None: ... - def __eq__(self, other: object) -> bool: ... - def __getitem__(self, key: K) -> V: ... - def __setitem__(self, key: K, value: V) -> None: ... - def __delitem__(self, key: K) -> None: ... - def keys(self) -> Iterator[K]: ... # type: ignore - def __iter__(self) -> Iterator[K]: ... - def values(self) -> Iterator[V]: ... # type: ignore - def items(self, multi: bool = False) -> Iterator[tuple[K, V]]: ... # type: ignore - def lists(self) -> Iterator[tuple[K, list[V]]]: ... - def listvalues(self) -> Iterator[list[V]]: ... - def add(self, key: K, value: V) -> None: ... - @overload - def getlist(self, key: K) -> list[V]: ... - @overload - def getlist(self, key: K, type: Callable[[V], T] = ...) -> list[T]: ... - def setlist(self, key: K, new_list: Iterable[V]) -> None: ... - def setlistdefault( - self, key: K, default_list: Iterable[V] | None = None - ) -> list[V]: ... - def update( # type: ignore - self, mapping: Mapping[K, V] | Iterable[tuple[K, V]] - ) -> None: ... - def poplist(self, key: K) -> list[V]: ... - @overload - def pop(self, key: K) -> V: ... - @overload - def pop(self, key: K, default: V | T = ...) -> V | T: ... - def popitem(self) -> tuple[K, V]: ... - def popitemlist(self) -> tuple[K, list[V]]: ... - -class CombinedMultiDict(ImmutableMultiDictMixin[K, V], MultiDict[K, V]): # type: ignore - dicts: list[MultiDict[K, V]] - def __init__(self, dicts: Iterable[MultiDict[K, V]] | None) -> None: ... - @classmethod - def fromkeys(cls, keys: Any, value: Any = None) -> NoReturn: ... - def __getitem__(self, key: K) -> V: ... - @overload # type: ignore - def get(self, key: K) -> V | None: ... - @overload - def get(self, key: K, default: V | T = ...) -> V | T: ... - @overload - def get( - self, key: K, default: T | None = None, type: Callable[[V], T] = ... - ) -> T | None: ... - @overload - def getlist(self, key: K) -> list[V]: ... - @overload - def getlist(self, key: K, type: Callable[[V], T] = ...) -> list[T]: ... - def _keys_impl(self) -> set[K]: ... - def keys(self) -> set[K]: ... # type: ignore - def __iter__(self) -> set[K]: ... # type: ignore - def items(self, multi: bool = False) -> Iterator[tuple[K, V]]: ... # type: ignore - def values(self) -> Iterator[V]: ... # type: ignore - def lists(self) -> Iterator[tuple[K, list[V]]]: ... - def listvalues(self) -> Iterator[list[V]]: ... - def copy(self) -> MultiDict[K, V]: ... - @overload - def to_dict(self) -> dict[K, V]: ... - @overload - def to_dict(self, flat: Literal[False]) -> dict[K, list[V]]: ... - def __contains__(self, key: K) -> bool: ... # type: ignore - def has_key(self, key: K) -> bool: ... - -class ImmutableDict(ImmutableDictMixin[K, V], dict[K, V]): - def copy(self) -> dict[K, V]: ... - def __copy__(self) -> ImmutableDict[K, V]: ... - -class ImmutableMultiDict( # type: ignore - ImmutableMultiDictMixin[K, V], MultiDict[K, V] -): - def copy(self) -> MultiDict[K, V]: ... - def __copy__(self) -> ImmutableMultiDict[K, V]: ... - -class ImmutableOrderedMultiDict( # type: ignore - ImmutableMultiDictMixin[K, V], OrderedMultiDict[K, V] -): - def _iter_hashitems(self) -> Iterator[tuple[int, tuple[K, V]]]: ... - def copy(self) -> OrderedMultiDict[K, V]: ... - def __copy__(self) -> ImmutableOrderedMultiDict[K, V]: ... - -class CallbackDict(UpdateDictMixin[K, V], dict[K, V]): - def __init__( - self, - initial: Mapping[K, V] | Iterable[tuple[K, V]] | None = None, - on_update: Callable[[_CD], None] | None = None, - ) -> None: ... - -class HeaderSet(set[str]): - _headers: list[str] - _set: set[str] - on_update: Callable[[HeaderSet], None] | None - def __init__( - self, - headers: Iterable[str] | None = None, - on_update: Callable[[HeaderSet], None] | None = None, - ) -> None: ... - def add(self, header: str) -> None: ... - def remove(self, header: str) -> None: ... - def update(self, iterable: Iterable[str]) -> None: ... # type: ignore - def discard(self, header: str) -> None: ... - def find(self, header: str) -> int: ... - def index(self, header: str) -> int: ... - def clear(self) -> None: ... - def as_set(self, preserve_casing: bool = False) -> set[str]: ... - def to_header(self) -> str: ... - def __getitem__(self, idx: int) -> str: ... - def __delitem__(self, idx: int) -> None: ... - def __setitem__(self, idx: int, value: str) -> None: ... - def __contains__(self, header: str) -> bool: ... # type: ignore - def __len__(self) -> int: ... - def __iter__(self) -> Iterator[str]: ... diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/__init__.py deleted file mode 100644 index 0c4cabd..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/__init__.py +++ /dev/null @@ -1,565 +0,0 @@ -from __future__ import annotations - -import getpass -import hashlib -import json -import os -import pkgutil -import re -import sys -import time -import typing as t -import uuid -from contextlib import ExitStack -from io import BytesIO -from itertools import chain -from multiprocessing import Value -from os.path import basename -from os.path import join -from zlib import adler32 - -from .._internal import _log -from ..exceptions import NotFound -from ..exceptions import SecurityError -from ..http import parse_cookie -from ..sansio.utils import host_is_trusted -from ..security import gen_salt -from ..utils import send_file -from ..wrappers.request import Request -from ..wrappers.response import Response -from .console import Console -from .tbtools import DebugFrameSummary -from .tbtools import DebugTraceback -from .tbtools import render_console_html - -if t.TYPE_CHECKING: - from _typeshed.wsgi import StartResponse - from _typeshed.wsgi import WSGIApplication - from _typeshed.wsgi import WSGIEnvironment - -# A week -PIN_TIME = 60 * 60 * 24 * 7 - - -def hash_pin(pin: str) -> str: - return hashlib.sha1(f"{pin} added salt".encode("utf-8", "replace")).hexdigest()[:12] - - -_machine_id: str | bytes | None = None - - -def get_machine_id() -> str | bytes | None: - global _machine_id - - if _machine_id is not None: - return _machine_id - - def _generate() -> str | bytes | None: - linux = b"" - - # machine-id is stable across boots, boot_id is not. - for filename in "/etc/machine-id", "/proc/sys/kernel/random/boot_id": - try: - with open(filename, "rb") as f: - value = f.readline().strip() - except OSError: - continue - - if value: - linux += value - break - - # Containers share the same machine id, add some cgroup - # information. This is used outside containers too but should be - # relatively stable across boots. - try: - with open("/proc/self/cgroup", "rb") as f: - linux += f.readline().strip().rpartition(b"/")[2] - except OSError: - pass - - if linux: - return linux - - # On OS X, use ioreg to get the computer's serial number. - try: - # subprocess may not be available, e.g. Google App Engine - # https://github.com/pallets/werkzeug/issues/925 - from subprocess import PIPE - from subprocess import Popen - - dump = Popen( - ["ioreg", "-c", "IOPlatformExpertDevice", "-d", "2"], stdout=PIPE - ).communicate()[0] - match = re.search(b'"serial-number" = <([^>]+)', dump) - - if match is not None: - return match.group(1) - except (OSError, ImportError): - pass - - # On Windows, use winreg to get the machine guid. - if sys.platform == "win32": - import winreg - - try: - with winreg.OpenKey( - winreg.HKEY_LOCAL_MACHINE, - "SOFTWARE\\Microsoft\\Cryptography", - 0, - winreg.KEY_READ | winreg.KEY_WOW64_64KEY, - ) as rk: - guid: str | bytes - guid_type: int - guid, guid_type = winreg.QueryValueEx(rk, "MachineGuid") - - if guid_type == winreg.REG_SZ: - return guid.encode() - - return guid - except OSError: - pass - - return None - - _machine_id = _generate() - return _machine_id - - -class _ConsoleFrame: - """Helper class so that we can reuse the frame console code for the - standalone console. - """ - - def __init__(self, namespace: dict[str, t.Any]): - self.console = Console(namespace) - self.id = 0 - - def eval(self, code: str) -> t.Any: - return self.console.eval(code) - - -def get_pin_and_cookie_name( - app: WSGIApplication, -) -> tuple[str, str] | tuple[None, None]: - """Given an application object this returns a semi-stable 9 digit pin - code and a random key. The hope is that this is stable between - restarts to not make debugging particularly frustrating. If the pin - was forcefully disabled this returns `None`. - - Second item in the resulting tuple is the cookie name for remembering. - """ - pin = os.environ.get("WERKZEUG_DEBUG_PIN") - rv = None - num = None - - # Pin was explicitly disabled - if pin == "off": - return None, None - - # Pin was provided explicitly - if pin is not None and pin.replace("-", "").isdecimal(): - # If there are separators in the pin, return it directly - if "-" in pin: - rv = pin - else: - num = pin - - modname = getattr(app, "__module__", t.cast(object, app).__class__.__module__) - username: str | None - - try: - # getuser imports the pwd module, which does not exist in Google - # App Engine. It may also raise a KeyError if the UID does not - # have a username, such as in Docker. - username = getpass.getuser() - # Python >= 3.13 only raises OSError - except (ImportError, KeyError, OSError): - username = None - - mod = sys.modules.get(modname) - - # This information only exists to make the cookie unique on the - # computer, not as a security feature. - probably_public_bits = [ - username, - modname, - getattr(app, "__name__", type(app).__name__), - getattr(mod, "__file__", None), - ] - - # This information is here to make it harder for an attacker to - # guess the cookie name. They are unlikely to be contained anywhere - # within the unauthenticated debug page. - private_bits = [str(uuid.getnode()), get_machine_id()] - - h = hashlib.sha1() - for bit in chain(probably_public_bits, private_bits): - if not bit: - continue - if isinstance(bit, str): - bit = bit.encode() - h.update(bit) - h.update(b"cookiesalt") - - cookie_name = f"__wzd{h.hexdigest()[:20]}" - - # If we need to generate a pin we salt it a bit more so that we don't - # end up with the same value and generate out 9 digits - if num is None: - h.update(b"pinsalt") - num = f"{int(h.hexdigest(), 16):09d}"[:9] - - # Format the pincode in groups of digits for easier remembering if - # we don't have a result yet. - if rv is None: - for group_size in 5, 4, 3: - if len(num) % group_size == 0: - rv = "-".join( - num[x : x + group_size].rjust(group_size, "0") - for x in range(0, len(num), group_size) - ) - break - else: - rv = num - - return rv, cookie_name - - -class DebuggedApplication: - """Enables debugging support for a given application:: - - from werkzeug.debug import DebuggedApplication - from myapp import app - app = DebuggedApplication(app, evalex=True) - - The ``evalex`` argument allows evaluating expressions in any frame - of a traceback. This works by preserving each frame with its local - state. Some state, such as context globals, cannot be restored with - the frame by default. When ``evalex`` is enabled, - ``environ["werkzeug.debug.preserve_context"]`` will be a callable - that takes a context manager, and can be called multiple times. - Each context manager will be entered before evaluating code in the - frame, then exited again, so they can perform setup and cleanup for - each call. - - :param app: the WSGI application to run debugged. - :param evalex: enable exception evaluation feature (interactive - debugging). This requires a non-forking server. - :param request_key: The key that points to the request object in this - environment. This parameter is ignored in current - versions. - :param console_path: the URL for a general purpose console. - :param console_init_func: the function that is executed before starting - the general purpose console. The return value - is used as initial namespace. - :param show_hidden_frames: by default hidden traceback frames are skipped. - You can show them by setting this parameter - to `True`. - :param pin_security: can be used to disable the pin based security system. - :param pin_logging: enables the logging of the pin system. - - .. versionchanged:: 2.2 - Added the ``werkzeug.debug.preserve_context`` environ key. - """ - - _pin: str - _pin_cookie: str - - def __init__( - self, - app: WSGIApplication, - evalex: bool = False, - request_key: str = "werkzeug.request", - console_path: str = "/console", - console_init_func: t.Callable[[], dict[str, t.Any]] | None = None, - show_hidden_frames: bool = False, - pin_security: bool = True, - pin_logging: bool = True, - ) -> None: - if not console_init_func: - console_init_func = None - self.app = app - self.evalex = evalex - self.frames: dict[int, DebugFrameSummary | _ConsoleFrame] = {} - self.frame_contexts: dict[int, list[t.ContextManager[None]]] = {} - self.request_key = request_key - self.console_path = console_path - self.console_init_func = console_init_func - self.show_hidden_frames = show_hidden_frames - self.secret = gen_salt(20) - self._failed_pin_auth = Value("B") - - self.pin_logging = pin_logging - if pin_security: - # Print out the pin for the debugger on standard out. - if os.environ.get("WERKZEUG_RUN_MAIN") == "true" and pin_logging: - _log("warning", " * Debugger is active!") - if self.pin is None: - _log("warning", " * Debugger PIN disabled. DEBUGGER UNSECURED!") - else: - _log("info", " * Debugger PIN: %s", self.pin) - else: - self.pin = None - - self.trusted_hosts: list[str] = [".localhost", "127.0.0.1"] - """List of domains to allow requests to the debugger from. A leading dot - allows all subdomains. This only allows ``".localhost"`` domains by - default. - - .. versionadded:: 3.0.3 - """ - - @property - def pin(self) -> str | None: - if not hasattr(self, "_pin"): - pin_cookie = get_pin_and_cookie_name(self.app) - self._pin, self._pin_cookie = pin_cookie # type: ignore - return self._pin - - @pin.setter - def pin(self, value: str) -> None: - self._pin = value - - @property - def pin_cookie_name(self) -> str: - """The name of the pin cookie.""" - if not hasattr(self, "_pin_cookie"): - pin_cookie = get_pin_and_cookie_name(self.app) - self._pin, self._pin_cookie = pin_cookie # type: ignore - return self._pin_cookie - - def debug_application( - self, environ: WSGIEnvironment, start_response: StartResponse - ) -> t.Iterator[bytes]: - """Run the application and conserve the traceback frames.""" - contexts: list[t.ContextManager[t.Any]] = [] - - if self.evalex: - environ["werkzeug.debug.preserve_context"] = contexts.append - - app_iter = None - try: - app_iter = self.app(environ, start_response) - yield from app_iter - if hasattr(app_iter, "close"): - app_iter.close() - except Exception as e: - if hasattr(app_iter, "close"): - app_iter.close() # type: ignore - - tb = DebugTraceback(e, skip=1, hide=not self.show_hidden_frames) - - for frame in tb.all_frames: - self.frames[id(frame)] = frame - self.frame_contexts[id(frame)] = contexts - - is_trusted = bool(self.check_pin_trust(environ)) - html = tb.render_debugger_html( - evalex=self.evalex and self.check_host_trust(environ), - secret=self.secret, - evalex_trusted=is_trusted, - ) - response = Response(html, status=500, mimetype="text/html") - - try: - yield from response(environ, start_response) - except Exception: - # if we end up here there has been output but an error - # occurred. in that situation we can do nothing fancy any - # more, better log something into the error log and fall - # back gracefully. - environ["wsgi.errors"].write( - "Debugging middleware caught exception in streamed " - "response at a point where response headers were already " - "sent.\n" - ) - - environ["wsgi.errors"].write("".join(tb.render_traceback_text())) - - def execute_command( - self, - request: Request, - command: str, - frame: DebugFrameSummary | _ConsoleFrame, - ) -> Response: - """Execute a command in a console.""" - if not self.check_host_trust(request.environ): - return SecurityError() # type: ignore[return-value] - - contexts = self.frame_contexts.get(id(frame), []) - - with ExitStack() as exit_stack: - for cm in contexts: - exit_stack.enter_context(cm) - - return Response(frame.eval(command), mimetype="text/html") - - def display_console(self, request: Request) -> Response: - """Display a standalone shell.""" - if not self.check_host_trust(request.environ): - return SecurityError() # type: ignore[return-value] - - if 0 not in self.frames: - if self.console_init_func is None: - ns = {} - else: - ns = dict(self.console_init_func()) - ns.setdefault("app", self.app) - self.frames[0] = _ConsoleFrame(ns) - is_trusted = bool(self.check_pin_trust(request.environ)) - return Response( - render_console_html(secret=self.secret, evalex_trusted=is_trusted), - mimetype="text/html", - ) - - def get_resource(self, request: Request, filename: str) -> Response: - """Return a static resource from the shared folder.""" - path = join("shared", basename(filename)) - - try: - data = pkgutil.get_data(__package__, path) - except OSError: - return NotFound() # type: ignore[return-value] - else: - if data is None: - return NotFound() # type: ignore[return-value] - - etag = str(adler32(data) & 0xFFFFFFFF) - return send_file( - BytesIO(data), request.environ, download_name=filename, etag=etag - ) - - def check_pin_trust(self, environ: WSGIEnvironment) -> bool | None: - """Checks if the request passed the pin test. This returns `True` if the - request is trusted on a pin/cookie basis and returns `False` if not. - Additionally if the cookie's stored pin hash is wrong it will return - `None` so that appropriate action can be taken. - """ - if self.pin is None: - return True - val = parse_cookie(environ).get(self.pin_cookie_name) - if not val or "|" not in val: - return False - ts_str, pin_hash = val.split("|", 1) - - try: - ts = int(ts_str) - except ValueError: - return False - - if pin_hash != hash_pin(self.pin): - return None - return (time.time() - PIN_TIME) < ts - - def check_host_trust(self, environ: WSGIEnvironment) -> bool: - return host_is_trusted(environ.get("HTTP_HOST"), self.trusted_hosts) - - def _fail_pin_auth(self) -> None: - with self._failed_pin_auth.get_lock(): - count = self._failed_pin_auth.value - self._failed_pin_auth.value = count + 1 - - time.sleep(5.0 if count > 5 else 0.5) - - def pin_auth(self, request: Request) -> Response: - """Authenticates with the pin.""" - if not self.check_host_trust(request.environ): - return SecurityError() # type: ignore[return-value] - - exhausted = False - auth = False - trust = self.check_pin_trust(request.environ) - pin = t.cast(str, self.pin) - - # If the trust return value is `None` it means that the cookie is - # set but the stored pin hash value is bad. This means that the - # pin was changed. In this case we count a bad auth and unset the - # cookie. This way it becomes harder to guess the cookie name - # instead of the pin as we still count up failures. - bad_cookie = False - if trust is None: - self._fail_pin_auth() - bad_cookie = True - - # If we're trusted, we're authenticated. - elif trust: - auth = True - - # If we failed too many times, then we're locked out. - elif self._failed_pin_auth.value > 10: - exhausted = True - - # Otherwise go through pin based authentication - else: - entered_pin = request.args["pin"] - - if entered_pin.strip().replace("-", "") == pin.replace("-", ""): - self._failed_pin_auth.value = 0 - auth = True - else: - self._fail_pin_auth() - - rv = Response( - json.dumps({"auth": auth, "exhausted": exhausted}), - mimetype="application/json", - ) - if auth: - rv.set_cookie( - self.pin_cookie_name, - f"{int(time.time())}|{hash_pin(pin)}", - httponly=True, - samesite="Strict", - secure=request.is_secure, - ) - elif bad_cookie: - rv.delete_cookie(self.pin_cookie_name) - return rv - - def log_pin_request(self, request: Request) -> Response: - """Log the pin if needed.""" - if not self.check_host_trust(request.environ): - return SecurityError() # type: ignore[return-value] - - if self.pin_logging and self.pin is not None: - _log( - "info", " * To enable the debugger you need to enter the security pin:" - ) - _log("info", " * Debugger pin code: %s", self.pin) - return Response("") - - def __call__( - self, environ: WSGIEnvironment, start_response: StartResponse - ) -> t.Iterable[bytes]: - """Dispatch the requests.""" - # important: don't ever access a function here that reads the incoming - # form data! Otherwise the application won't have access to that data - # any more! - request = Request(environ) - response = self.debug_application - if request.args.get("__debugger__") == "yes": - cmd = request.args.get("cmd") - arg = request.args.get("f") - secret = request.args.get("s") - frame = self.frames.get(request.args.get("frm", type=int)) # type: ignore - if cmd == "resource" and arg: - response = self.get_resource(request, arg) # type: ignore - elif cmd == "pinauth" and secret == self.secret: - response = self.pin_auth(request) # type: ignore - elif cmd == "printpin" and secret == self.secret: - response = self.log_pin_request(request) # type: ignore - elif ( - self.evalex - and cmd is not None - and frame is not None - and self.secret == secret - and self.check_pin_trust(environ) - ): - response = self.execute_command(request, cmd, frame) # type: ignore - elif ( - self.evalex - and self.console_path is not None - and request.path == self.console_path - ): - response = self.display_console(request) # type: ignore - return response(environ, start_response) diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 0a02ae9..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/__pycache__/console.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/__pycache__/console.cpython-38.pyc deleted file mode 100644 index 55e0f2b..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/__pycache__/console.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/__pycache__/repr.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/__pycache__/repr.cpython-38.pyc deleted file mode 100644 index 7f68402..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/__pycache__/repr.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/__pycache__/tbtools.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/__pycache__/tbtools.cpython-38.pyc deleted file mode 100644 index 7254f8c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/__pycache__/tbtools.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/console.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/console.py deleted file mode 100644 index 4e40475..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/console.py +++ /dev/null @@ -1,219 +0,0 @@ -from __future__ import annotations - -import code -import sys -import typing as t -from contextvars import ContextVar -from types import CodeType - -from markupsafe import escape - -from .repr import debug_repr -from .repr import dump -from .repr import helper - -_stream: ContextVar[HTMLStringO] = ContextVar("werkzeug.debug.console.stream") -_ipy: ContextVar[_InteractiveConsole] = ContextVar("werkzeug.debug.console.ipy") - - -class HTMLStringO: - """A StringO version that HTML escapes on write.""" - - def __init__(self) -> None: - self._buffer: list[str] = [] - - def isatty(self) -> bool: - return False - - def close(self) -> None: - pass - - def flush(self) -> None: - pass - - def seek(self, n: int, mode: int = 0) -> None: - pass - - def readline(self) -> str: - if len(self._buffer) == 0: - return "" - ret = self._buffer[0] - del self._buffer[0] - return ret - - def reset(self) -> str: - val = "".join(self._buffer) - del self._buffer[:] - return val - - def _write(self, x: str) -> None: - self._buffer.append(x) - - def write(self, x: str) -> None: - self._write(escape(x)) - - def writelines(self, x: t.Iterable[str]) -> None: - self._write(escape("".join(x))) - - -class ThreadedStream: - """Thread-local wrapper for sys.stdout for the interactive console.""" - - @staticmethod - def push() -> None: - if not isinstance(sys.stdout, ThreadedStream): - sys.stdout = t.cast(t.TextIO, ThreadedStream()) - - _stream.set(HTMLStringO()) - - @staticmethod - def fetch() -> str: - try: - stream = _stream.get() - except LookupError: - return "" - - return stream.reset() - - @staticmethod - def displayhook(obj: object) -> None: - try: - stream = _stream.get() - except LookupError: - return _displayhook(obj) # type: ignore - - # stream._write bypasses escaping as debug_repr is - # already generating HTML for us. - if obj is not None: - _ipy.get().locals["_"] = obj - stream._write(debug_repr(obj)) - - def __setattr__(self, name: str, value: t.Any) -> None: - raise AttributeError(f"read only attribute {name}") - - def __dir__(self) -> list[str]: - return dir(sys.__stdout__) - - def __getattribute__(self, name: str) -> t.Any: - try: - stream = _stream.get() - except LookupError: - stream = sys.__stdout__ # type: ignore[assignment] - - return getattr(stream, name) - - def __repr__(self) -> str: - return repr(sys.__stdout__) - - -# add the threaded stream as display hook -_displayhook = sys.displayhook -sys.displayhook = ThreadedStream.displayhook - - -class _ConsoleLoader: - def __init__(self) -> None: - self._storage: dict[int, str] = {} - - def register(self, code: CodeType, source: str) -> None: - self._storage[id(code)] = source - # register code objects of wrapped functions too. - for var in code.co_consts: - if isinstance(var, CodeType): - self._storage[id(var)] = source - - def get_source_by_code(self, code: CodeType) -> str | None: - try: - return self._storage[id(code)] - except KeyError: - return None - - -class _InteractiveConsole(code.InteractiveInterpreter): - locals: dict[str, t.Any] - - def __init__(self, globals: dict[str, t.Any], locals: dict[str, t.Any]) -> None: - self.loader = _ConsoleLoader() - locals = { - **globals, - **locals, - "dump": dump, - "help": helper, - "__loader__": self.loader, - } - super().__init__(locals) - original_compile = self.compile - - def compile(source: str, filename: str, symbol: str) -> CodeType | None: - code = original_compile(source, filename, symbol) - - if code is not None: - self.loader.register(code, source) - - return code - - self.compile = compile # type: ignore[assignment] - self.more = False - self.buffer: list[str] = [] - - def runsource(self, source: str, **kwargs: t.Any) -> str: # type: ignore - source = f"{source.rstrip()}\n" - ThreadedStream.push() - prompt = "... " if self.more else ">>> " - try: - source_to_eval = "".join(self.buffer + [source]) - if super().runsource(source_to_eval, "", "single"): - self.more = True - self.buffer.append(source) - else: - self.more = False - del self.buffer[:] - finally: - output = ThreadedStream.fetch() - return f"{prompt}{escape(source)}{output}" - - def runcode(self, code: CodeType) -> None: - try: - exec(code, self.locals) - except Exception: - self.showtraceback() - - def showtraceback(self) -> None: - from .tbtools import DebugTraceback - - exc = t.cast(BaseException, sys.exc_info()[1]) - te = DebugTraceback(exc, skip=1) - sys.stdout._write(te.render_traceback_html()) # type: ignore - - def showsyntaxerror(self, filename: str | None = None) -> None: - from .tbtools import DebugTraceback - - exc = t.cast(BaseException, sys.exc_info()[1]) - te = DebugTraceback(exc, skip=4) - sys.stdout._write(te.render_traceback_html()) # type: ignore - - def write(self, data: str) -> None: - sys.stdout.write(data) - - -class Console: - """An interactive console.""" - - def __init__( - self, - globals: dict[str, t.Any] | None = None, - locals: dict[str, t.Any] | None = None, - ) -> None: - if locals is None: - locals = {} - if globals is None: - globals = {} - self._ipy = _InteractiveConsole(globals, locals) - - def eval(self, code: str) -> str: - _ipy.set(self._ipy) - old_sys_stdout = sys.stdout - try: - return self._ipy.runsource(code) - finally: - sys.stdout = old_sys_stdout diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/repr.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/repr.py deleted file mode 100644 index 2bbd9d5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/repr.py +++ /dev/null @@ -1,282 +0,0 @@ -"""Object representations for debugging purposes. Unlike the default -repr, these expose more information and produce HTML instead of ASCII. - -Together with the CSS and JavaScript of the debugger this gives a -colorful and more compact output. -""" - -from __future__ import annotations - -import codecs -import re -import sys -import typing as t -from collections import deque -from traceback import format_exception_only - -from markupsafe import escape - -missing = object() -_paragraph_re = re.compile(r"(?:\r\n|\r|\n){2,}") -RegexType = type(_paragraph_re) - -HELP_HTML = """\ -
-

%(title)s

-
%(text)s
-
\ -""" -OBJECT_DUMP_HTML = """\ -
-

%(title)s

- %(repr)s - %(items)s
-
\ -""" - - -def debug_repr(obj: object) -> str: - """Creates a debug repr of an object as HTML string.""" - return DebugReprGenerator().repr(obj) - - -def dump(obj: object = missing) -> None: - """Print the object details to stdout._write (for the interactive - console of the web debugger. - """ - gen = DebugReprGenerator() - if obj is missing: - rv = gen.dump_locals(sys._getframe(1).f_locals) - else: - rv = gen.dump_object(obj) - sys.stdout._write(rv) # type: ignore - - -class _Helper: - """Displays an HTML version of the normal help, for the interactive - debugger only because it requires a patched sys.stdout. - """ - - def __repr__(self) -> str: - return "Type help(object) for help about object." - - def __call__(self, topic: t.Any | None = None) -> None: - if topic is None: - sys.stdout._write(f"{self!r}") # type: ignore - return - import pydoc - - pydoc.help(topic) - rv = sys.stdout.reset() # type: ignore - paragraphs = _paragraph_re.split(rv) - if len(paragraphs) > 1: - title = paragraphs[0] - text = "\n\n".join(paragraphs[1:]) - else: - title = "Help" - text = paragraphs[0] - sys.stdout._write(HELP_HTML % {"title": title, "text": text}) # type: ignore - - -helper = _Helper() - - -def _add_subclass_info(inner: str, obj: object, base: type | tuple[type, ...]) -> str: - if isinstance(base, tuple): - for cls in base: - if type(obj) is cls: - return inner - elif type(obj) is base: - return inner - module = "" - if obj.__class__.__module__ not in ("__builtin__", "exceptions"): - module = f'{obj.__class__.__module__}.' - return f"{module}{type(obj).__name__}({inner})" - - -def _sequence_repr_maker( - left: str, right: str, base: type, limit: int = 8 -) -> t.Callable[[DebugReprGenerator, t.Iterable[t.Any], bool], str]: - def proxy(self: DebugReprGenerator, obj: t.Iterable[t.Any], recursive: bool) -> str: - if recursive: - return _add_subclass_info(f"{left}...{right}", obj, base) - buf = [left] - have_extended_section = False - for idx, item in enumerate(obj): - if idx: - buf.append(", ") - if idx == limit: - buf.append('') - have_extended_section = True - buf.append(self.repr(item)) - if have_extended_section: - buf.append("") - buf.append(right) - return _add_subclass_info("".join(buf), obj, base) - - return proxy - - -class DebugReprGenerator: - def __init__(self) -> None: - self._stack: list[t.Any] = [] - - list_repr = _sequence_repr_maker("[", "]", list) - tuple_repr = _sequence_repr_maker("(", ")", tuple) - set_repr = _sequence_repr_maker("set([", "])", set) - frozenset_repr = _sequence_repr_maker("frozenset([", "])", frozenset) - deque_repr = _sequence_repr_maker( - 'collections.deque([', "])", deque - ) - - def regex_repr(self, obj: t.Pattern[t.AnyStr]) -> str: - pattern = repr(obj.pattern) - pattern = codecs.decode(pattern, "unicode-escape", "ignore") - pattern = f"r{pattern}" - return f're.compile({pattern})' - - def string_repr(self, obj: str | bytes, limit: int = 70) -> str: - buf = [''] - r = repr(obj) - - # shorten the repr when the hidden part would be at least 3 chars - if len(r) - limit > 2: - buf.extend( - ( - escape(r[:limit]), - '', - escape(r[limit:]), - "", - ) - ) - else: - buf.append(escape(r)) - - buf.append("") - out = "".join(buf) - - # if the repr looks like a standard string, add subclass info if needed - if r[0] in "'\"" or (r[0] == "b" and r[1] in "'\""): - return _add_subclass_info(out, obj, (bytes, str)) - - # otherwise, assume the repr distinguishes the subclass already - return out - - def dict_repr( - self, - d: dict[int, None] | dict[str, int] | dict[str | int, int], - recursive: bool, - limit: int = 5, - ) -> str: - if recursive: - return _add_subclass_info("{...}", d, dict) - buf = ["{"] - have_extended_section = False - for idx, (key, value) in enumerate(d.items()): - if idx: - buf.append(", ") - if idx == limit - 1: - buf.append('') - have_extended_section = True - buf.append( - f'{self.repr(key)}:' - f' {self.repr(value)}' - ) - if have_extended_section: - buf.append("") - buf.append("}") - return _add_subclass_info("".join(buf), d, dict) - - def object_repr(self, obj: t.Any) -> str: - r = repr(obj) - return f'{escape(r)}' - - def dispatch_repr(self, obj: t.Any, recursive: bool) -> str: - if obj is helper: - return f'{helper!r}' - if isinstance(obj, (int, float, complex)): - return f'{obj!r}' - if isinstance(obj, str) or isinstance(obj, bytes): - return self.string_repr(obj) - if isinstance(obj, RegexType): - return self.regex_repr(obj) - if isinstance(obj, list): - return self.list_repr(obj, recursive) - if isinstance(obj, tuple): - return self.tuple_repr(obj, recursive) - if isinstance(obj, set): - return self.set_repr(obj, recursive) - if isinstance(obj, frozenset): - return self.frozenset_repr(obj, recursive) - if isinstance(obj, dict): - return self.dict_repr(obj, recursive) - if isinstance(obj, deque): - return self.deque_repr(obj, recursive) - return self.object_repr(obj) - - def fallback_repr(self) -> str: - try: - info = "".join(format_exception_only(*sys.exc_info()[:2])) - except Exception: - info = "?" - return ( - '' - f"<broken repr ({escape(info.strip())})>" - ) - - def repr(self, obj: object) -> str: - recursive = False - for item in self._stack: - if item is obj: - recursive = True - break - self._stack.append(obj) - try: - try: - return self.dispatch_repr(obj, recursive) - except Exception: - return self.fallback_repr() - finally: - self._stack.pop() - - def dump_object(self, obj: object) -> str: - repr = None - items: list[tuple[str, str]] | None = None - - if isinstance(obj, dict): - title = "Contents of" - items = [] - for key, value in obj.items(): - if not isinstance(key, str): - items = None - break - items.append((key, self.repr(value))) - if items is None: - items = [] - repr = self.repr(obj) - for key in dir(obj): - try: - items.append((key, self.repr(getattr(obj, key)))) - except Exception: - pass - title = "Details for" - title += f" {object.__repr__(obj)[1:-1]}" - return self.render_object_dump(items, title, repr) - - def dump_locals(self, d: dict[str, t.Any]) -> str: - items = [(key, self.repr(value)) for key, value in d.items()] - return self.render_object_dump(items, "Local variables in frame") - - def render_object_dump( - self, items: list[tuple[str, str]], title: str, repr: str | None = None - ) -> str: - html_items = [] - for key, value in items: - html_items.append(f"{escape(key)}
{value}
") - if not html_items: - html_items.append("Nothing") - return OBJECT_DUMP_HTML % { - "title": escape(title), - "repr": f"
{repr if repr else ''}
", - "items": "\n".join(html_items), - } diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/ICON_LICENSE.md b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/ICON_LICENSE.md deleted file mode 100644 index 3bdbfc7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/ICON_LICENSE.md +++ /dev/null @@ -1,6 +0,0 @@ -Silk icon set 1.3 by Mark James - -http://www.famfamfam.com/lab/icons/silk/ - -License: [CC-BY-2.5](https://creativecommons.org/licenses/by/2.5/) -or [CC-BY-3.0](https://creativecommons.org/licenses/by/3.0/) diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/console.png b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/console.png deleted file mode 100644 index c28dd63..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/console.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/debugger.js b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/debugger.js deleted file mode 100644 index 809b14a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/debugger.js +++ /dev/null @@ -1,344 +0,0 @@ -docReady(() => { - if (!EVALEX_TRUSTED) { - initPinBox(); - } - // if we are in console mode, show the console. - if (CONSOLE_MODE && EVALEX) { - createInteractiveConsole(); - } - - const frames = document.querySelectorAll("div.traceback div.frame"); - if (EVALEX) { - addConsoleIconToFrames(frames); - } - addEventListenersToElements(document.querySelectorAll("div.detail"), "click", () => - document.querySelector("div.traceback").scrollIntoView(false) - ); - addToggleFrameTraceback(frames); - addToggleTraceTypesOnClick(document.querySelectorAll("h2.traceback")); - addInfoPrompt(document.querySelectorAll("span.nojavascript")); - wrapPlainTraceback(); -}); - -function addToggleFrameTraceback(frames) { - frames.forEach((frame) => { - frame.addEventListener("click", () => { - frame.getElementsByTagName("pre")[0].parentElement.classList.toggle("expanded"); - }); - }) -} - - -function wrapPlainTraceback() { - const plainTraceback = document.querySelector("div.plain textarea"); - const wrapper = document.createElement("pre"); - const textNode = document.createTextNode(plainTraceback.textContent); - wrapper.appendChild(textNode); - plainTraceback.replaceWith(wrapper); -} - -function makeDebugURL(args) { - const params = new URLSearchParams(args) - params.set("s", SECRET) - return `?__debugger__=yes&${params}` -} - -function initPinBox() { - document.querySelector(".pin-prompt form").addEventListener( - "submit", - function (event) { - event.preventDefault(); - const btn = this.btn; - btn.disabled = true; - - fetch( - makeDebugURL({cmd: "pinauth", pin: this.pin.value}) - ) - .then((res) => res.json()) - .then(({auth, exhausted}) => { - if (auth) { - EVALEX_TRUSTED = true; - fadeOut(document.getElementsByClassName("pin-prompt")[0]); - } else { - alert( - `Error: ${ - exhausted - ? "too many attempts. Restart server to retry." - : "incorrect pin" - }` - ); - } - }) - .catch((err) => { - alert("Error: Could not verify PIN. Network error?"); - console.error(err); - }) - .finally(() => (btn.disabled = false)); - }, - false - ); -} - -function promptForPin() { - if (!EVALEX_TRUSTED) { - fetch(makeDebugURL({cmd: "printpin"})); - const pinPrompt = document.getElementsByClassName("pin-prompt")[0]; - fadeIn(pinPrompt); - document.querySelector('.pin-prompt input[name="pin"]').focus(); - } -} - -/** - * Helper function for shell initialization - */ -function openShell(consoleNode, target, frameID) { - promptForPin(); - if (consoleNode) { - slideToggle(consoleNode); - return consoleNode; - } - let historyPos = 0; - const history = [""]; - const consoleElement = createConsole(); - const output = createConsoleOutput(); - const form = createConsoleInputForm(); - const command = createConsoleInput(); - - target.parentNode.appendChild(consoleElement); - consoleElement.append(output); - consoleElement.append(form); - form.append(command); - command.focus(); - slideToggle(consoleElement); - - form.addEventListener("submit", (e) => { - handleConsoleSubmit(e, command, frameID).then((consoleOutput) => { - output.append(consoleOutput); - command.focus(); - consoleElement.scrollTo(0, consoleElement.scrollHeight); - const old = history.pop(); - history.push(command.value); - if (typeof old !== "undefined") { - history.push(old); - } - historyPos = history.length - 1; - command.value = ""; - }); - }); - - command.addEventListener("keydown", (e) => { - if (e.key === "l" && e.ctrlKey) { - output.innerText = "--- screen cleared ---"; - } else if (e.key === "ArrowUp" || e.key === "ArrowDown") { - // Handle up arrow and down arrow. - if (e.key === "ArrowUp" && historyPos > 0) { - e.preventDefault(); - historyPos--; - } else if (e.key === "ArrowDown" && historyPos < history.length - 1) { - historyPos++; - } - command.value = history[historyPos]; - } - return false; - }); - - return consoleElement; -} - -function addEventListenersToElements(elements, event, listener) { - elements.forEach((el) => el.addEventListener(event, listener)); -} - -/** - * Add extra info - */ -function addInfoPrompt(elements) { - for (let i = 0; i < elements.length; i++) { - elements[i].innerHTML = - "

To switch between the interactive traceback and the plaintext " + - 'one, you can click on the "Traceback" headline. From the text ' + - "traceback you can also create a paste of it. " + - (!EVALEX - ? "" - : "For code execution mouse-over the frame you want to debug and " + - "click on the console icon on the right side." + - "

You can execute arbitrary Python code in the stack frames and " + - "there are some extra helpers available for introspection:" + - "

  • dump() shows all variables in the frame" + - "
  • dump(obj) dumps all that's known about the object
"); - elements[i].classList.remove("nojavascript"); - } -} - -function addConsoleIconToFrames(frames) { - for (let i = 0; i < frames.length; i++) { - let consoleNode = null; - const target = frames[i]; - const frameID = frames[i].id.substring(6); - - for (let j = 0; j < target.getElementsByTagName("pre").length; j++) { - const img = createIconForConsole(); - img.addEventListener("click", (e) => { - e.stopPropagation(); - consoleNode = openShell(consoleNode, target, frameID); - return false; - }); - target.getElementsByTagName("pre")[j].append(img); - } - } -} - -function slideToggle(target) { - target.classList.toggle("active"); -} - -/** - * toggle traceback types on click. - */ -function addToggleTraceTypesOnClick(elements) { - for (let i = 0; i < elements.length; i++) { - elements[i].addEventListener("click", () => { - document.querySelector("div.traceback").classList.toggle("hidden"); - document.querySelector("div.plain").classList.toggle("hidden"); - }); - elements[i].style.cursor = "pointer"; - document.querySelector("div.plain").classList.toggle("hidden"); - } -} - -function createConsole() { - const consoleNode = document.createElement("pre"); - consoleNode.classList.add("console"); - consoleNode.classList.add("active"); - return consoleNode; -} - -function createConsoleOutput() { - const output = document.createElement("div"); - output.classList.add("output"); - output.innerHTML = "[console ready]"; - return output; -} - -function createConsoleInputForm() { - const form = document.createElement("form"); - form.innerHTML = ">>> "; - return form; -} - -function createConsoleInput() { - const command = document.createElement("input"); - command.type = "text"; - command.setAttribute("autocomplete", "off"); - command.setAttribute("spellcheck", false); - command.setAttribute("autocapitalize", "off"); - command.setAttribute("autocorrect", "off"); - return command; -} - -function createIconForConsole() { - const img = document.createElement("img"); - img.setAttribute("src", makeDebugURL({cmd: "resource", f: "console.png"})); - img.setAttribute("title", "Open an interactive python shell in this frame"); - return img; -} - -function createExpansionButtonForConsole() { - const expansionButton = document.createElement("a"); - expansionButton.setAttribute("href", "#"); - expansionButton.setAttribute("class", "toggle"); - expansionButton.innerHTML = "  "; - return expansionButton; -} - -function createInteractiveConsole() { - const target = document.querySelector("div.console div.inner"); - while (target.firstChild) { - target.removeChild(target.firstChild); - } - openShell(null, target, 0); -} - -function handleConsoleSubmit(e, command, frameID) { - // Prevent page from refreshing. - e.preventDefault(); - - return new Promise((resolve) => { - fetch(makeDebugURL({cmd: command.value, frm: frameID})) - .then((res) => { - return res.text(); - }) - .then((data) => { - const tmp = document.createElement("div"); - tmp.innerHTML = data; - resolve(tmp); - - // Handle expandable span for long list outputs. - // Example to test: list(range(13)) - let wrapperAdded = false; - const wrapperSpan = document.createElement("span"); - const expansionButton = createExpansionButtonForConsole(); - - tmp.querySelectorAll("span.extended").forEach((spanToWrap) => { - const parentDiv = spanToWrap.parentNode; - if (!wrapperAdded) { - parentDiv.insertBefore(wrapperSpan, spanToWrap); - wrapperAdded = true; - } - parentDiv.removeChild(spanToWrap); - wrapperSpan.append(spanToWrap); - spanToWrap.hidden = true; - - expansionButton.addEventListener("click", (event) => { - event.preventDefault(); - spanToWrap.hidden = !spanToWrap.hidden; - expansionButton.classList.toggle("open"); - return false; - }); - }); - - // Add expansion button at end of wrapper. - if (wrapperAdded) { - wrapperSpan.append(expansionButton); - } - }) - .catch((err) => { - console.error(err); - }); - return false; - }); -} - -function fadeOut(element) { - element.style.opacity = 1; - - (function fade() { - element.style.opacity -= 0.1; - if (element.style.opacity < 0) { - element.style.display = "none"; - } else { - requestAnimationFrame(fade); - } - })(); -} - -function fadeIn(element, display) { - element.style.opacity = 0; - element.style.display = display || "block"; - - (function fade() { - let val = parseFloat(element.style.opacity) + 0.1; - if (val <= 1) { - element.style.opacity = val; - requestAnimationFrame(fade); - } - })(); -} - -function docReady(fn) { - if (document.readyState === "complete" || document.readyState === "interactive") { - setTimeout(fn, 1); - } else { - document.addEventListener("DOMContentLoaded", fn); - } -} diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/less.png b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/less.png deleted file mode 100644 index 5efefd6..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/less.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/more.png b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/more.png deleted file mode 100644 index 804fa22..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/more.png and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/style.css b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/style.css deleted file mode 100644 index e9397ca..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/style.css +++ /dev/null @@ -1,150 +0,0 @@ -body, input { font-family: sans-serif; color: #000; text-align: center; - margin: 1em; padding: 0; font-size: 15px; } -h1, h2, h3 { font-weight: normal; } - -input { background-color: #fff; margin: 0; text-align: left; - outline: none !important; } -input[type="submit"] { padding: 3px 6px; } -a { color: #11557C; } -a:hover { color: #177199; } -pre, code, -textarea { font-family: monospace; font-size: 14px; } - -div.debugger { text-align: left; padding: 12px; margin: auto; - background-color: white; } -h1 { font-size: 36px; margin: 0 0 0.3em 0; } -div.detail { cursor: pointer; } -div.detail p { margin: 0 0 8px 13px; font-size: 14px; white-space: pre-wrap; - font-family: monospace; } -div.explanation { margin: 20px 13px; font-size: 15px; color: #555; } -div.footer { font-size: 13px; text-align: right; margin: 30px 0; - color: #86989B; } - -h2 { font-size: 16px; margin: 1.3em 0 0.0 0; padding: 9px; - background-color: #11557C; color: white; } -h2 em, h3 em { font-style: normal; color: #A5D6D9; font-weight: normal; } - -div.traceback, div.plain { border: 1px solid #ddd; margin: 0 0 1em 0; padding: 10px; } -div.plain p { margin: 0; } -div.plain textarea, -div.plain pre { margin: 10px 0 0 0; padding: 4px; - background-color: #E8EFF0; border: 1px solid #D3E7E9; } -div.plain textarea { width: 99%; height: 300px; } -div.traceback h3 { font-size: 1em; margin: 0 0 0.8em 0; } -div.traceback ul { list-style: none; margin: 0; padding: 0 0 0 1em; } -div.traceback h4 { font-size: 13px; font-weight: normal; margin: 0.7em 0 0.1em 0; } -div.traceback pre { margin: 0; padding: 5px 0 3px 15px; - background-color: #E8EFF0; border: 1px solid #D3E7E9; } -div.traceback .library .current { background: white; color: #555; } -div.traceback .expanded .current { background: #E8EFF0; color: black; } -div.traceback pre:hover { background-color: #DDECEE; color: black; cursor: pointer; } -div.traceback div.source.expanded pre + pre { border-top: none; } - -div.traceback span.ws { display: none; } -div.traceback pre.before, div.traceback pre.after { display: none; background: white; } -div.traceback div.source.expanded pre.before, -div.traceback div.source.expanded pre.after { - display: block; -} - -div.traceback div.source.expanded span.ws { - display: inline; -} - -div.traceback blockquote { margin: 1em 0 0 0; padding: 0; white-space: pre-line; } -div.traceback img { float: right; padding: 2px; margin: -3px 2px 0 0; display: none; } -div.traceback img:hover { background-color: #ddd; cursor: pointer; - border-color: #BFDDE0; } -div.traceback pre:hover img { display: block; } -div.traceback cite.filename { font-style: normal; color: #3B666B; } - -pre.console { border: 1px solid #ccc; background: white!important; - color: black; padding: 5px!important; - margin: 3px 0 0 0!important; cursor: default!important; - max-height: 400px; overflow: auto; } -pre.console form { color: #555; } -pre.console input { background-color: transparent; color: #555; - width: 90%; font-family: monospace; font-size: 14px; - border: none!important; } - -span.string { color: #30799B; } -span.number { color: #9C1A1C; } -span.help { color: #3A7734; } -span.object { color: #485F6E; } -span.extended { opacity: 0.5; } -span.extended:hover { opacity: 1; } -a.toggle { text-decoration: none; background-repeat: no-repeat; - background-position: center center; - background-image: url(?__debugger__=yes&cmd=resource&f=more.png); } -a.toggle:hover { background-color: #444; } -a.open { background-image: url(?__debugger__=yes&cmd=resource&f=less.png); } - -pre.console div.traceback, -pre.console div.box { margin: 5px 10px; white-space: normal; - border: 1px solid #11557C; padding: 10px; - font-family: sans-serif; } -pre.console div.box h3, -pre.console div.traceback h3 { margin: -10px -10px 10px -10px; padding: 5px; - background: #11557C; color: white; } - -pre.console div.traceback pre:hover { cursor: default; background: #E8EFF0; } -pre.console div.traceback pre.syntaxerror { background: inherit; border: none; - margin: 20px -10px -10px -10px; - padding: 10px; border-top: 1px solid #BFDDE0; - background: #E8EFF0; } -pre.console div.noframe-traceback pre.syntaxerror { margin-top: -10px; border: none; } - -pre.console div.box pre.repr { padding: 0; margin: 0; background-color: white; border: none; } -pre.console div.box table { margin-top: 6px; } -pre.console div.box pre { border: none; } -pre.console div.box pre.help { background-color: white; } -pre.console div.box pre.help:hover { cursor: default; } -pre.console table tr { vertical-align: top; } -div.console { border: 1px solid #ccc; padding: 4px; background-color: #fafafa; } - -div.traceback pre, div.console pre { - white-space: pre-wrap; /* css-3 should we be so lucky... */ - white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ - white-space: -pre-wrap; /* Opera 4-6 ?? */ - white-space: -o-pre-wrap; /* Opera 7 ?? */ - word-wrap: break-word; /* Internet Explorer 5.5+ */ - _white-space: pre; /* IE only hack to re-specify in - addition to word-wrap */ -} - - -div.pin-prompt { - position: absolute; - display: none; - top: 0; - bottom: 0; - left: 0; - right: 0; - background: rgba(255, 255, 255, 0.8); -} - -div.pin-prompt .inner { - background: #eee; - padding: 10px 50px; - width: 350px; - margin: 10% auto 0 auto; - border: 1px solid #ccc; - border-radius: 2px; -} - -div.exc-divider { - margin: 0.7em 0 0 -1em; - padding: 0.5em; - background: #11557C; - color: #ddd; - border: 1px solid #ddd; -} - -.console.active { - max-height: 0!important; - display: none; -} - -.hidden { - display: none; -} diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/tbtools.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/tbtools.py deleted file mode 100644 index e81ed6e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/debug/tbtools.py +++ /dev/null @@ -1,450 +0,0 @@ -from __future__ import annotations - -import itertools -import linecache -import os -import re -import sys -import sysconfig -import traceback -import typing as t - -from markupsafe import escape - -from ..utils import cached_property -from .console import Console - -HEADER = """\ - - - - %(title)s // Werkzeug Debugger - - - - - - -
-""" - -FOOTER = """\ - -
- -
-
-

Console Locked

-

- The console is locked and needs to be unlocked by entering the PIN. - You can find the PIN printed out on the standard output of your - shell that runs the server. -

-

PIN: - - -

-
-
- - -""" - -PAGE_HTML = ( - HEADER - + """\ -

%(exception_type)s

-
-

%(exception)s

-
-

Traceback (most recent call last)

-%(summary)s -
-

- This is the Copy/Paste friendly version of the traceback. -

- -
-
- The debugger caught an exception in your WSGI application. You can now - look at the traceback which led to the error. - If you enable JavaScript you can also use additional features such as code - execution (if the evalex feature is enabled), automatic pasting of the - exceptions and much more. -
-""" - + FOOTER - + """ - -""" -) - -CONSOLE_HTML = ( - HEADER - + """\ -

Interactive Console

-
-In this console you can execute Python expressions in the context of the -application. The initial namespace was created by the debugger automatically. -
-
The Console requires JavaScript.
-""" - + FOOTER -) - -SUMMARY_HTML = """\ -
- %(title)s -
    %(frames)s
- %(description)s -
-""" - -FRAME_HTML = """\ -
-

File "%(filename)s", - line %(lineno)s, - in %(function_name)s

-
%(lines)s
-
-""" - - -def _process_traceback( - exc: BaseException, - te: traceback.TracebackException | None = None, - *, - skip: int = 0, - hide: bool = True, -) -> traceback.TracebackException: - if te is None: - te = traceback.TracebackException.from_exception(exc, lookup_lines=False) - - # Get the frames the same way StackSummary.extract did, in order - # to match each frame with the FrameSummary to augment. - frame_gen = traceback.walk_tb(exc.__traceback__) - limit = getattr(sys, "tracebacklimit", None) - - if limit is not None: - if limit < 0: - limit = 0 - - frame_gen = itertools.islice(frame_gen, limit) - - if skip: - frame_gen = itertools.islice(frame_gen, skip, None) - del te.stack[:skip] - - new_stack: list[DebugFrameSummary] = [] - hidden = False - - # Match each frame with the FrameSummary that was generated. - # Hide frames using Paste's __traceback_hide__ rules. Replace - # all visible FrameSummary with DebugFrameSummary. - for (f, _), fs in zip(frame_gen, te.stack): - if hide: - hide_value = f.f_locals.get("__traceback_hide__", False) - - if hide_value in {"before", "before_and_this"}: - new_stack = [] - hidden = False - - if hide_value == "before_and_this": - continue - elif hide_value in {"reset", "reset_and_this"}: - hidden = False - - if hide_value == "reset_and_this": - continue - elif hide_value in {"after", "after_and_this"}: - hidden = True - - if hide_value == "after_and_this": - continue - elif hide_value or hidden: - continue - - frame_args: dict[str, t.Any] = { - "filename": fs.filename, - "lineno": fs.lineno, - "name": fs.name, - "locals": f.f_locals, - "globals": f.f_globals, - } - - if hasattr(fs, "colno"): - frame_args["colno"] = fs.colno - frame_args["end_colno"] = fs.end_colno - - new_stack.append(DebugFrameSummary(**frame_args)) - - # The codeop module is used to compile code from the interactive - # debugger. Hide any codeop frames from the bottom of the traceback. - while new_stack: - module = new_stack[0].global_ns.get("__name__") - - if module is None: - module = new_stack[0].local_ns.get("__name__") - - if module == "codeop": - del new_stack[0] - else: - break - - te.stack[:] = new_stack - - if te.__context__: - context_exc = t.cast(BaseException, exc.__context__) - te.__context__ = _process_traceback(context_exc, te.__context__, hide=hide) - - if te.__cause__: - cause_exc = t.cast(BaseException, exc.__cause__) - te.__cause__ = _process_traceback(cause_exc, te.__cause__, hide=hide) - - return te - - -class DebugTraceback: - __slots__ = ("_te", "_cache_all_tracebacks", "_cache_all_frames") - - def __init__( - self, - exc: BaseException, - te: traceback.TracebackException | None = None, - *, - skip: int = 0, - hide: bool = True, - ) -> None: - self._te = _process_traceback(exc, te, skip=skip, hide=hide) - - def __str__(self) -> str: - return f"<{type(self).__name__} {self._te}>" - - @cached_property - def all_tracebacks( - self, - ) -> list[tuple[str | None, traceback.TracebackException]]: - out = [] - current = self._te - - while current is not None: - if current.__cause__ is not None: - chained_msg = ( - "The above exception was the direct cause of the" - " following exception" - ) - chained_exc = current.__cause__ - elif current.__context__ is not None and not current.__suppress_context__: - chained_msg = ( - "During handling of the above exception, another" - " exception occurred" - ) - chained_exc = current.__context__ - else: - chained_msg = None - chained_exc = None - - out.append((chained_msg, current)) - current = chained_exc - - return out - - @cached_property - def all_frames(self) -> list[DebugFrameSummary]: - return [ - f # type: ignore[misc] - for _, te in self.all_tracebacks - for f in te.stack - ] - - def render_traceback_text(self) -> str: - return "".join(self._te.format()) - - def render_traceback_html(self, include_title: bool = True) -> str: - library_frames = [f.is_library for f in self.all_frames] - mark_library = 0 < sum(library_frames) < len(library_frames) - rows = [] - - if not library_frames: - classes = "traceback noframe-traceback" - else: - classes = "traceback" - - for msg, current in reversed(self.all_tracebacks): - row_parts = [] - - if msg is not None: - row_parts.append(f'
  • {msg}:
    ') - - for frame in current.stack: - frame = t.cast(DebugFrameSummary, frame) - info = f' title="{escape(frame.info)}"' if frame.info else "" - row_parts.append(f"{frame.render_html(mark_library)}") - - rows.append("\n".join(row_parts)) - - if sys.version_info < (3, 13): - exc_type_str = self._te.exc_type.__name__ - else: - exc_type_str = self._te.exc_type_str - - is_syntax_error = exc_type_str == "SyntaxError" - - if include_title: - if is_syntax_error: - title = "Syntax Error" - else: - title = "Traceback (most recent call last):" - else: - title = "" - - exc_full = escape("".join(self._te.format_exception_only())) - - if is_syntax_error: - description = f"
    {exc_full}
    " - else: - description = f"
    {exc_full}
    " - - return SUMMARY_HTML % { - "classes": classes, - "title": f"

    {title}

    ", - "frames": "\n".join(rows), - "description": description, - } - - def render_debugger_html( - self, evalex: bool, secret: str, evalex_trusted: bool - ) -> str: - exc_lines = list(self._te.format_exception_only()) - plaintext = "".join(self._te.format()) - - if sys.version_info < (3, 13): - exc_type_str = self._te.exc_type.__name__ - else: - exc_type_str = self._te.exc_type_str - - return PAGE_HTML % { - "evalex": "true" if evalex else "false", - "evalex_trusted": "true" if evalex_trusted else "false", - "console": "false", - "title": escape(exc_lines[0]), - "exception": escape("".join(exc_lines)), - "exception_type": escape(exc_type_str), - "summary": self.render_traceback_html(include_title=False), - "plaintext": escape(plaintext), - "plaintext_cs": re.sub("-{2,}", "-", plaintext), - "secret": secret, - } - - -class DebugFrameSummary(traceback.FrameSummary): - """A :class:`traceback.FrameSummary` that can evaluate code in the - frame's namespace. - """ - - __slots__ = ( - "local_ns", - "global_ns", - "_cache_info", - "_cache_is_library", - "_cache_console", - ) - - def __init__( - self, - *, - locals: dict[str, t.Any], - globals: dict[str, t.Any], - **kwargs: t.Any, - ) -> None: - super().__init__(locals=None, **kwargs) - self.local_ns = locals - self.global_ns = globals - - @cached_property - def info(self) -> str | None: - return self.local_ns.get("__traceback_info__") - - @cached_property - def is_library(self) -> bool: - return any( - self.filename.startswith((path, os.path.realpath(path))) - for path in sysconfig.get_paths().values() - ) - - @cached_property - def console(self) -> Console: - return Console(self.global_ns, self.local_ns) - - def eval(self, code: str) -> t.Any: - return self.console.eval(code) - - def render_html(self, mark_library: bool) -> str: - context = 5 - lines = linecache.getlines(self.filename) - line_idx = self.lineno - 1 # type: ignore[operator] - start_idx = max(0, line_idx - context) - stop_idx = min(len(lines), line_idx + context + 1) - rendered_lines = [] - - def render_line(line: str, cls: str) -> None: - line = line.expandtabs().rstrip() - stripped_line = line.strip() - prefix = len(line) - len(stripped_line) - colno = getattr(self, "colno", 0) - end_colno = getattr(self, "end_colno", 0) - - if cls == "current" and colno and end_colno: - arrow = ( - f'\n{" " * prefix}' - f'{" " * (colno - prefix)}{"^" * (end_colno - colno)}' - ) - else: - arrow = "" - - rendered_lines.append( - f'
    {" " * prefix}'
    -                f"{escape(stripped_line) if stripped_line else ' '}"
    -                f"{arrow if arrow else ''}
    " - ) - - if lines: - for line in lines[start_idx:line_idx]: - render_line(line, "before") - - render_line(lines[line_idx], "current") - - for line in lines[line_idx + 1 : stop_idx]: - render_line(line, "after") - - return FRAME_HTML % { - "id": id(self), - "filename": escape(self.filename), - "lineno": self.lineno, - "function_name": escape(self.name), - "lines": "\n".join(rendered_lines), - "library": "library" if mark_library and self.is_library else "", - } - - -def render_console_html(secret: str, evalex_trusted: bool) -> str: - return CONSOLE_HTML % { - "evalex": "true", - "evalex_trusted": "true" if evalex_trusted else "false", - "console": "true", - "title": "Console", - "secret": secret, - } diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/exceptions.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/exceptions.py deleted file mode 100644 index 6ce7ef9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/exceptions.py +++ /dev/null @@ -1,881 +0,0 @@ -"""Implements a number of Python exceptions which can be raised from within -a view to trigger a standard HTTP non-200 response. - -Usage Example -------------- - -.. code-block:: python - - from werkzeug.wrappers.request import Request - from werkzeug.exceptions import HTTPException, NotFound - - def view(request): - raise NotFound() - - @Request.application - def application(request): - try: - return view(request) - except HTTPException as e: - return e - -As you can see from this example those exceptions are callable WSGI -applications. However, they are not Werkzeug response objects. You -can get a response object by calling ``get_response()`` on a HTTP -exception. - -Keep in mind that you may have to pass an environ (WSGI) or scope -(ASGI) to ``get_response()`` because some errors fetch additional -information relating to the request. - -If you want to hook in a different exception page to say, a 404 status -code, you can add a second except for a specific subclass of an error: - -.. code-block:: python - - @Request.application - def application(request): - try: - return view(request) - except NotFound as e: - return not_found(request) - except HTTPException as e: - return e - -""" - -from __future__ import annotations - -import typing as t -from datetime import datetime - -from markupsafe import escape -from markupsafe import Markup - -from ._internal import _get_environ - -if t.TYPE_CHECKING: - from _typeshed.wsgi import StartResponse - from _typeshed.wsgi import WSGIEnvironment - - from .datastructures import WWWAuthenticate - from .sansio.response import Response - from .wrappers.request import Request as WSGIRequest - from .wrappers.response import Response as WSGIResponse - - -class HTTPException(Exception): - """The base class for all HTTP exceptions. This exception can be called as a WSGI - application to render a default error page or you can catch the subclasses - of it independently and render nicer error messages. - - .. versionchanged:: 2.1 - Removed the ``wrap`` class method. - """ - - code: int | None = None - description: str | None = None - - def __init__( - self, - description: str | None = None, - response: Response | None = None, - ) -> None: - super().__init__() - if description is not None: - self.description = description - self.response = response - - @property - def name(self) -> str: - """The status name.""" - from .http import HTTP_STATUS_CODES - - return HTTP_STATUS_CODES.get(self.code, "Unknown Error") # type: ignore - - def get_description( - self, - environ: WSGIEnvironment | None = None, - scope: dict[str, t.Any] | None = None, - ) -> str: - """Get the description.""" - if self.description is None: - description = "" - else: - description = self.description - - description = escape(description).replace("\n", Markup("
    ")) - return f"

    {description}

    " - - def get_body( - self, - environ: WSGIEnvironment | None = None, - scope: dict[str, t.Any] | None = None, - ) -> str: - """Get the HTML body.""" - return ( - "\n" - "\n" - f"{self.code} {escape(self.name)}\n" - f"

    {escape(self.name)}

    \n" - f"{self.get_description(environ)}\n" - ) - - def get_headers( - self, - environ: WSGIEnvironment | None = None, - scope: dict[str, t.Any] | None = None, - ) -> list[tuple[str, str]]: - """Get a list of headers.""" - return [("Content-Type", "text/html; charset=utf-8")] - - def get_response( - self, - environ: WSGIEnvironment | WSGIRequest | None = None, - scope: dict[str, t.Any] | None = None, - ) -> Response: - """Get a response object. If one was passed to the exception - it's returned directly. - - :param environ: the optional environ for the request. This - can be used to modify the response depending - on how the request looked like. - :return: a :class:`Response` object or a subclass thereof. - """ - from .wrappers.response import Response as WSGIResponse # noqa: F811 - - if self.response is not None: - return self.response - if environ is not None: - environ = _get_environ(environ) - headers = self.get_headers(environ, scope) - return WSGIResponse(self.get_body(environ, scope), self.code, headers) - - def __call__( - self, environ: WSGIEnvironment, start_response: StartResponse - ) -> t.Iterable[bytes]: - """Call the exception as WSGI application. - - :param environ: the WSGI environment. - :param start_response: the response callable provided by the WSGI - server. - """ - response = t.cast("WSGIResponse", self.get_response(environ)) - return response(environ, start_response) - - def __str__(self) -> str: - code = self.code if self.code is not None else "???" - return f"{code} {self.name}: {self.description}" - - def __repr__(self) -> str: - code = self.code if self.code is not None else "???" - return f"<{type(self).__name__} '{code}: {self.name}'>" - - -class BadRequest(HTTPException): - """*400* `Bad Request` - - Raise if the browser sends something to the application the application - or server cannot handle. - """ - - code = 400 - description = ( - "The browser (or proxy) sent a request that this server could " - "not understand." - ) - - -class BadRequestKeyError(BadRequest, KeyError): - """An exception that is used to signal both a :exc:`KeyError` and a - :exc:`BadRequest`. Used by many of the datastructures. - """ - - _description = BadRequest.description - #: Show the KeyError along with the HTTP error message in the - #: response. This should be disabled in production, but can be - #: useful in a debug mode. - show_exception = False - - def __init__(self, arg: str | None = None, *args: t.Any, **kwargs: t.Any): - super().__init__(*args, **kwargs) - - if arg is None: - KeyError.__init__(self) - else: - KeyError.__init__(self, arg) - - @property # type: ignore - def description(self) -> str: - if self.show_exception: - return ( - f"{self._description}\n" - f"{KeyError.__name__}: {KeyError.__str__(self)}" - ) - - return self._description - - @description.setter - def description(self, value: str) -> None: - self._description = value - - -class ClientDisconnected(BadRequest): - """Internal exception that is raised if Werkzeug detects a disconnected - client. Since the client is already gone at that point attempting to - send the error message to the client might not work and might ultimately - result in another exception in the server. Mainly this is here so that - it is silenced by default as far as Werkzeug is concerned. - - Since disconnections cannot be reliably detected and are unspecified - by WSGI to a large extent this might or might not be raised if a client - is gone. - - .. versionadded:: 0.8 - """ - - -class SecurityError(BadRequest): - """Raised if something triggers a security error. This is otherwise - exactly like a bad request error. - - .. versionadded:: 0.9 - """ - - -class BadHost(BadRequest): - """Raised if the submitted host is badly formatted. - - .. versionadded:: 0.11.2 - """ - - -class Unauthorized(HTTPException): - """*401* ``Unauthorized`` - - Raise if the user is not authorized to access a resource. - - The ``www_authenticate`` argument should be used to set the - ``WWW-Authenticate`` header. This is used for HTTP basic auth and - other schemes. Use :class:`~werkzeug.datastructures.WWWAuthenticate` - to create correctly formatted values. Strictly speaking a 401 - response is invalid if it doesn't provide at least one value for - this header, although real clients typically don't care. - - :param description: Override the default message used for the body - of the response. - :param www-authenticate: A single value, or list of values, for the - WWW-Authenticate header(s). - - .. versionchanged:: 2.0 - Serialize multiple ``www_authenticate`` items into multiple - ``WWW-Authenticate`` headers, rather than joining them - into a single value, for better interoperability. - - .. versionchanged:: 0.15.3 - If the ``www_authenticate`` argument is not set, the - ``WWW-Authenticate`` header is not set. - - .. versionchanged:: 0.15.3 - The ``response`` argument was restored. - - .. versionchanged:: 0.15.1 - ``description`` was moved back as the first argument, restoring - its previous position. - - .. versionchanged:: 0.15.0 - ``www_authenticate`` was added as the first argument, ahead of - ``description``. - """ - - code = 401 - description = ( - "The server could not verify that you are authorized to access" - " the URL requested. You either supplied the wrong credentials" - " (e.g. a bad password), or your browser doesn't understand" - " how to supply the credentials required." - ) - - def __init__( - self, - description: str | None = None, - response: Response | None = None, - www_authenticate: None | (WWWAuthenticate | t.Iterable[WWWAuthenticate]) = None, - ) -> None: - super().__init__(description, response) - - from .datastructures import WWWAuthenticate - - if isinstance(www_authenticate, WWWAuthenticate): - www_authenticate = (www_authenticate,) - - self.www_authenticate = www_authenticate - - def get_headers( - self, - environ: WSGIEnvironment | None = None, - scope: dict[str, t.Any] | None = None, - ) -> list[tuple[str, str]]: - headers = super().get_headers(environ, scope) - if self.www_authenticate: - headers.extend(("WWW-Authenticate", str(x)) for x in self.www_authenticate) - return headers - - -class Forbidden(HTTPException): - """*403* `Forbidden` - - Raise if the user doesn't have the permission for the requested resource - but was authenticated. - """ - - code = 403 - description = ( - "You don't have the permission to access the requested" - " resource. It is either read-protected or not readable by the" - " server." - ) - - -class NotFound(HTTPException): - """*404* `Not Found` - - Raise if a resource does not exist and never existed. - """ - - code = 404 - description = ( - "The requested URL was not found on the server. If you entered" - " the URL manually please check your spelling and try again." - ) - - -class MethodNotAllowed(HTTPException): - """*405* `Method Not Allowed` - - Raise if the server used a method the resource does not handle. For - example `POST` if the resource is view only. Especially useful for REST. - - The first argument for this exception should be a list of allowed methods. - Strictly speaking the response would be invalid if you don't provide valid - methods in the header which you can do with that list. - """ - - code = 405 - description = "The method is not allowed for the requested URL." - - def __init__( - self, - valid_methods: t.Iterable[str] | None = None, - description: str | None = None, - response: Response | None = None, - ) -> None: - """Takes an optional list of valid http methods - starting with werkzeug 0.3 the list will be mandatory.""" - super().__init__(description=description, response=response) - self.valid_methods = valid_methods - - def get_headers( - self, - environ: WSGIEnvironment | None = None, - scope: dict[str, t.Any] | None = None, - ) -> list[tuple[str, str]]: - headers = super().get_headers(environ, scope) - if self.valid_methods: - headers.append(("Allow", ", ".join(self.valid_methods))) - return headers - - -class NotAcceptable(HTTPException): - """*406* `Not Acceptable` - - Raise if the server can't return any content conforming to the - `Accept` headers of the client. - """ - - code = 406 - description = ( - "The resource identified by the request is only capable of" - " generating response entities which have content" - " characteristics not acceptable according to the accept" - " headers sent in the request." - ) - - -class RequestTimeout(HTTPException): - """*408* `Request Timeout` - - Raise to signalize a timeout. - """ - - code = 408 - description = ( - "The server closed the network connection because the browser" - " didn't finish the request within the specified time." - ) - - -class Conflict(HTTPException): - """*409* `Conflict` - - Raise to signal that a request cannot be completed because it conflicts - with the current state on the server. - - .. versionadded:: 0.7 - """ - - code = 409 - description = ( - "A conflict happened while processing the request. The" - " resource might have been modified while the request was being" - " processed." - ) - - -class Gone(HTTPException): - """*410* `Gone` - - Raise if a resource existed previously and went away without new location. - """ - - code = 410 - description = ( - "The requested URL is no longer available on this server and" - " there is no forwarding address. If you followed a link from a" - " foreign page, please contact the author of this page." - ) - - -class LengthRequired(HTTPException): - """*411* `Length Required` - - Raise if the browser submitted data but no ``Content-Length`` header which - is required for the kind of processing the server does. - """ - - code = 411 - description = ( - "A request with this method requires a valid Content-" - "Length header." - ) - - -class PreconditionFailed(HTTPException): - """*412* `Precondition Failed` - - Status code used in combination with ``If-Match``, ``If-None-Match``, or - ``If-Unmodified-Since``. - """ - - code = 412 - description = ( - "The precondition on the request for the URL failed positive evaluation." - ) - - -class RequestEntityTooLarge(HTTPException): - """*413* `Request Entity Too Large` - - The status code one should return if the data submitted exceeded a given - limit. - """ - - code = 413 - description = "The data value transmitted exceeds the capacity limit." - - -class RequestURITooLarge(HTTPException): - """*414* `Request URI Too Large` - - Like *413* but for too long URLs. - """ - - code = 414 - description = ( - "The length of the requested URL exceeds the capacity limit for" - " this server. The request cannot be processed." - ) - - -class UnsupportedMediaType(HTTPException): - """*415* `Unsupported Media Type` - - The status code returned if the server is unable to handle the media type - the client transmitted. - """ - - code = 415 - description = ( - "The server does not support the media type transmitted in the request." - ) - - -class RequestedRangeNotSatisfiable(HTTPException): - """*416* `Requested Range Not Satisfiable` - - The client asked for an invalid part of the file. - - .. versionadded:: 0.7 - """ - - code = 416 - description = "The server cannot provide the requested range." - - def __init__( - self, - length: int | None = None, - units: str = "bytes", - description: str | None = None, - response: Response | None = None, - ) -> None: - """Takes an optional `Content-Range` header value based on ``length`` - parameter. - """ - super().__init__(description=description, response=response) - self.length = length - self.units = units - - def get_headers( - self, - environ: WSGIEnvironment | None = None, - scope: dict[str, t.Any] | None = None, - ) -> list[tuple[str, str]]: - headers = super().get_headers(environ, scope) - if self.length is not None: - headers.append(("Content-Range", f"{self.units} */{self.length}")) - return headers - - -class ExpectationFailed(HTTPException): - """*417* `Expectation Failed` - - The server cannot meet the requirements of the Expect request-header. - - .. versionadded:: 0.7 - """ - - code = 417 - description = "The server could not meet the requirements of the Expect header" - - -class ImATeapot(HTTPException): - """*418* `I'm a teapot` - - The server should return this if it is a teapot and someone attempted - to brew coffee with it. - - .. versionadded:: 0.7 - """ - - code = 418 - description = "This server is a teapot, not a coffee machine" - - -class UnprocessableEntity(HTTPException): - """*422* `Unprocessable Entity` - - Used if the request is well formed, but the instructions are otherwise - incorrect. - """ - - code = 422 - description = ( - "The request was well-formed but was unable to be followed due" - " to semantic errors." - ) - - -class Locked(HTTPException): - """*423* `Locked` - - Used if the resource that is being accessed is locked. - """ - - code = 423 - description = "The resource that is being accessed is locked." - - -class FailedDependency(HTTPException): - """*424* `Failed Dependency` - - Used if the method could not be performed on the resource - because the requested action depended on another action and that action failed. - """ - - code = 424 - description = ( - "The method could not be performed on the resource because the" - " requested action depended on another action and that action" - " failed." - ) - - -class PreconditionRequired(HTTPException): - """*428* `Precondition Required` - - The server requires this request to be conditional, typically to prevent - the lost update problem, which is a race condition between two or more - clients attempting to update a resource through PUT or DELETE. By requiring - each client to include a conditional header ("If-Match" or "If-Unmodified- - Since") with the proper value retained from a recent GET request, the - server ensures that each client has at least seen the previous revision of - the resource. - """ - - code = 428 - description = ( - "This request is required to be conditional; try using" - ' "If-Match" or "If-Unmodified-Since".' - ) - - -class _RetryAfter(HTTPException): - """Adds an optional ``retry_after`` parameter which will set the - ``Retry-After`` header. May be an :class:`int` number of seconds or - a :class:`~datetime.datetime`. - """ - - def __init__( - self, - description: str | None = None, - response: Response | None = None, - retry_after: datetime | int | None = None, - ) -> None: - super().__init__(description, response) - self.retry_after = retry_after - - def get_headers( - self, - environ: WSGIEnvironment | None = None, - scope: dict[str, t.Any] | None = None, - ) -> list[tuple[str, str]]: - headers = super().get_headers(environ, scope) - - if self.retry_after: - if isinstance(self.retry_after, datetime): - from .http import http_date - - value = http_date(self.retry_after) - else: - value = str(self.retry_after) - - headers.append(("Retry-After", value)) - - return headers - - -class TooManyRequests(_RetryAfter): - """*429* `Too Many Requests` - - The server is limiting the rate at which this user receives - responses, and this request exceeds that rate. (The server may use - any convenient method to identify users and their request rates). - The server may include a "Retry-After" header to indicate how long - the user should wait before retrying. - - :param retry_after: If given, set the ``Retry-After`` header to this - value. May be an :class:`int` number of seconds or a - :class:`~datetime.datetime`. - - .. versionchanged:: 1.0 - Added ``retry_after`` parameter. - """ - - code = 429 - description = "This user has exceeded an allotted request count. Try again later." - - -class RequestHeaderFieldsTooLarge(HTTPException): - """*431* `Request Header Fields Too Large` - - The server refuses to process the request because the header fields are too - large. One or more individual fields may be too large, or the set of all - headers is too large. - """ - - code = 431 - description = "One or more header fields exceeds the maximum size." - - -class UnavailableForLegalReasons(HTTPException): - """*451* `Unavailable For Legal Reasons` - - This status code indicates that the server is denying access to the - resource as a consequence of a legal demand. - """ - - code = 451 - description = "Unavailable for legal reasons." - - -class InternalServerError(HTTPException): - """*500* `Internal Server Error` - - Raise if an internal server error occurred. This is a good fallback if an - unknown error occurred in the dispatcher. - - .. versionchanged:: 1.0.0 - Added the :attr:`original_exception` attribute. - """ - - code = 500 - description = ( - "The server encountered an internal error and was unable to" - " complete your request. Either the server is overloaded or" - " there is an error in the application." - ) - - def __init__( - self, - description: str | None = None, - response: Response | None = None, - original_exception: BaseException | None = None, - ) -> None: - #: The original exception that caused this 500 error. Can be - #: used by frameworks to provide context when handling - #: unexpected errors. - self.original_exception = original_exception - super().__init__(description=description, response=response) - - -class NotImplemented(HTTPException): - """*501* `Not Implemented` - - Raise if the application does not support the action requested by the - browser. - """ - - code = 501 - description = "The server does not support the action requested by the browser." - - -class BadGateway(HTTPException): - """*502* `Bad Gateway` - - If you do proxying in your application you should return this status code - if you received an invalid response from the upstream server it accessed - in attempting to fulfill the request. - """ - - code = 502 - description = ( - "The proxy server received an invalid response from an upstream server." - ) - - -class ServiceUnavailable(_RetryAfter): - """*503* `Service Unavailable` - - Status code you should return if a service is temporarily - unavailable. - - :param retry_after: If given, set the ``Retry-After`` header to this - value. May be an :class:`int` number of seconds or a - :class:`~datetime.datetime`. - - .. versionchanged:: 1.0 - Added ``retry_after`` parameter. - """ - - code = 503 - description = ( - "The server is temporarily unable to service your request due" - " to maintenance downtime or capacity problems. Please try" - " again later." - ) - - -class GatewayTimeout(HTTPException): - """*504* `Gateway Timeout` - - Status code you should return if a connection to an upstream server - times out. - """ - - code = 504 - description = "The connection to an upstream server timed out." - - -class HTTPVersionNotSupported(HTTPException): - """*505* `HTTP Version Not Supported` - - The server does not support the HTTP protocol version used in the request. - """ - - code = 505 - description = ( - "The server does not support the HTTP protocol version used in the request." - ) - - -default_exceptions: dict[int, type[HTTPException]] = {} - - -def _find_exceptions() -> None: - for obj in globals().values(): - try: - is_http_exception = issubclass(obj, HTTPException) - except TypeError: - is_http_exception = False - if not is_http_exception or obj.code is None: - continue - old_obj = default_exceptions.get(obj.code, None) - if old_obj is not None and issubclass(obj, old_obj): - continue - default_exceptions[obj.code] = obj - - -_find_exceptions() -del _find_exceptions - - -class Aborter: - """When passed a dict of code -> exception items it can be used as - callable that raises exceptions. If the first argument to the - callable is an integer it will be looked up in the mapping, if it's - a WSGI application it will be raised in a proxy exception. - - The rest of the arguments are forwarded to the exception constructor. - """ - - def __init__( - self, - mapping: dict[int, type[HTTPException]] | None = None, - extra: dict[int, type[HTTPException]] | None = None, - ) -> None: - if mapping is None: - mapping = default_exceptions - self.mapping = dict(mapping) - if extra is not None: - self.mapping.update(extra) - - def __call__( - self, code: int | Response, *args: t.Any, **kwargs: t.Any - ) -> t.NoReturn: - from .sansio.response import Response - - if isinstance(code, Response): - raise HTTPException(response=code) - - if code not in self.mapping: - raise LookupError(f"no exception for {code!r}") - - raise self.mapping[code](*args, **kwargs) - - -def abort(status: int | Response, *args: t.Any, **kwargs: t.Any) -> t.NoReturn: - """Raises an :py:exc:`HTTPException` for the given status code or WSGI - application. - - If a status code is given, it will be looked up in the list of - exceptions and will raise that exception. If passed a WSGI application, - it will wrap it in a proxy WSGI exception and raise that:: - - abort(404) # 404 Not Found - abort(Response('Hello World')) - - """ - _aborter(status, *args, **kwargs) - - -_aborter: Aborter = Aborter() diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/formparser.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/formparser.py deleted file mode 100644 index 3c6875e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/formparser.py +++ /dev/null @@ -1,430 +0,0 @@ -from __future__ import annotations - -import typing as t -from io import BytesIO -from urllib.parse import parse_qsl - -from ._internal import _plain_int -from .datastructures import FileStorage -from .datastructures import Headers -from .datastructures import MultiDict -from .exceptions import RequestEntityTooLarge -from .http import parse_options_header -from .sansio.multipart import Data -from .sansio.multipart import Epilogue -from .sansio.multipart import Field -from .sansio.multipart import File -from .sansio.multipart import MultipartDecoder -from .sansio.multipart import NeedData -from .wsgi import get_content_length -from .wsgi import get_input_stream - -# there are some platforms where SpooledTemporaryFile is not available. -# In that case we need to provide a fallback. -try: - from tempfile import SpooledTemporaryFile -except ImportError: - from tempfile import TemporaryFile - - SpooledTemporaryFile = None # type: ignore - -if t.TYPE_CHECKING: - import typing as te - - from _typeshed.wsgi import WSGIEnvironment - - t_parse_result = t.Tuple[ - t.IO[bytes], MultiDict[str, str], MultiDict[str, FileStorage] - ] - - class TStreamFactory(te.Protocol): - def __call__( - self, - total_content_length: int | None, - content_type: str | None, - filename: str | None, - content_length: int | None = None, - ) -> t.IO[bytes]: ... - - -F = t.TypeVar("F", bound=t.Callable[..., t.Any]) - - -def default_stream_factory( - total_content_length: int | None, - content_type: str | None, - filename: str | None, - content_length: int | None = None, -) -> t.IO[bytes]: - max_size = 1024 * 500 - - if SpooledTemporaryFile is not None: - return t.cast(t.IO[bytes], SpooledTemporaryFile(max_size=max_size, mode="rb+")) - elif total_content_length is None or total_content_length > max_size: - return t.cast(t.IO[bytes], TemporaryFile("rb+")) - - return BytesIO() - - -def parse_form_data( - environ: WSGIEnvironment, - stream_factory: TStreamFactory | None = None, - max_form_memory_size: int | None = None, - max_content_length: int | None = None, - cls: type[MultiDict[str, t.Any]] | None = None, - silent: bool = True, - *, - max_form_parts: int | None = None, -) -> t_parse_result: - """Parse the form data in the environ and return it as tuple in the form - ``(stream, form, files)``. You should only call this method if the - transport method is `POST`, `PUT`, or `PATCH`. - - If the mimetype of the data transmitted is `multipart/form-data` the - files multidict will be filled with `FileStorage` objects. If the - mimetype is unknown the input stream is wrapped and returned as first - argument, else the stream is empty. - - This is a shortcut for the common usage of :class:`FormDataParser`. - - :param environ: the WSGI environment to be used for parsing. - :param stream_factory: An optional callable that returns a new read and - writeable file descriptor. This callable works - the same as :meth:`Response._get_file_stream`. - :param max_form_memory_size: the maximum number of bytes to be accepted for - in-memory stored form data. If the data - exceeds the value specified an - :exc:`~exceptions.RequestEntityTooLarge` - exception is raised. - :param max_content_length: If this is provided and the transmitted data - is longer than this value an - :exc:`~exceptions.RequestEntityTooLarge` - exception is raised. - :param cls: an optional dict class to use. If this is not specified - or `None` the default :class:`MultiDict` is used. - :param silent: If set to False parsing errors will not be caught. - :param max_form_parts: The maximum number of multipart parts to be parsed. If this - is exceeded, a :exc:`~exceptions.RequestEntityTooLarge` exception is raised. - :return: A tuple in the form ``(stream, form, files)``. - - .. versionchanged:: 3.0 - The ``charset`` and ``errors`` parameters were removed. - - .. versionchanged:: 2.3 - Added the ``max_form_parts`` parameter. - - .. versionadded:: 0.5.1 - Added the ``silent`` parameter. - - .. versionadded:: 0.5 - Added the ``max_form_memory_size``, ``max_content_length``, and ``cls`` - parameters. - """ - return FormDataParser( - stream_factory=stream_factory, - max_form_memory_size=max_form_memory_size, - max_content_length=max_content_length, - max_form_parts=max_form_parts, - silent=silent, - cls=cls, - ).parse_from_environ(environ) - - -class FormDataParser: - """This class implements parsing of form data for Werkzeug. By itself - it can parse multipart and url encoded form data. It can be subclassed - and extended but for most mimetypes it is a better idea to use the - untouched stream and expose it as separate attributes on a request - object. - - :param stream_factory: An optional callable that returns a new read and - writeable file descriptor. This callable works - the same as :meth:`Response._get_file_stream`. - :param max_form_memory_size: the maximum number of bytes to be accepted for - in-memory stored form data. If the data - exceeds the value specified an - :exc:`~exceptions.RequestEntityTooLarge` - exception is raised. - :param max_content_length: If this is provided and the transmitted data - is longer than this value an - :exc:`~exceptions.RequestEntityTooLarge` - exception is raised. - :param cls: an optional dict class to use. If this is not specified - or `None` the default :class:`MultiDict` is used. - :param silent: If set to False parsing errors will not be caught. - :param max_form_parts: The maximum number of multipart parts to be parsed. If this - is exceeded, a :exc:`~exceptions.RequestEntityTooLarge` exception is raised. - - .. versionchanged:: 3.0 - The ``charset`` and ``errors`` parameters were removed. - - .. versionchanged:: 3.0 - The ``parse_functions`` attribute and ``get_parse_func`` methods were removed. - - .. versionchanged:: 2.2.3 - Added the ``max_form_parts`` parameter. - - .. versionadded:: 0.8 - """ - - def __init__( - self, - stream_factory: TStreamFactory | None = None, - max_form_memory_size: int | None = None, - max_content_length: int | None = None, - cls: type[MultiDict[str, t.Any]] | None = None, - silent: bool = True, - *, - max_form_parts: int | None = None, - ) -> None: - if stream_factory is None: - stream_factory = default_stream_factory - - self.stream_factory = stream_factory - self.max_form_memory_size = max_form_memory_size - self.max_content_length = max_content_length - self.max_form_parts = max_form_parts - - if cls is None: - cls = t.cast("type[MultiDict[str, t.Any]]", MultiDict) - - self.cls = cls - self.silent = silent - - def parse_from_environ(self, environ: WSGIEnvironment) -> t_parse_result: - """Parses the information from the environment as form data. - - :param environ: the WSGI environment to be used for parsing. - :return: A tuple in the form ``(stream, form, files)``. - """ - stream = get_input_stream(environ, max_content_length=self.max_content_length) - content_length = get_content_length(environ) - mimetype, options = parse_options_header(environ.get("CONTENT_TYPE")) - return self.parse( - stream, - content_length=content_length, - mimetype=mimetype, - options=options, - ) - - def parse( - self, - stream: t.IO[bytes], - mimetype: str, - content_length: int | None, - options: dict[str, str] | None = None, - ) -> t_parse_result: - """Parses the information from the given stream, mimetype, - content length and mimetype parameters. - - :param stream: an input stream - :param mimetype: the mimetype of the data - :param content_length: the content length of the incoming data - :param options: optional mimetype parameters (used for - the multipart boundary for instance) - :return: A tuple in the form ``(stream, form, files)``. - - .. versionchanged:: 3.0 - The invalid ``application/x-url-encoded`` content type is not - treated as ``application/x-www-form-urlencoded``. - """ - if mimetype == "multipart/form-data": - parse_func = self._parse_multipart - elif mimetype == "application/x-www-form-urlencoded": - parse_func = self._parse_urlencoded - else: - return stream, self.cls(), self.cls() - - if options is None: - options = {} - - try: - return parse_func(stream, mimetype, content_length, options) - except ValueError: - if not self.silent: - raise - - return stream, self.cls(), self.cls() - - def _parse_multipart( - self, - stream: t.IO[bytes], - mimetype: str, - content_length: int | None, - options: dict[str, str], - ) -> t_parse_result: - parser = MultiPartParser( - stream_factory=self.stream_factory, - max_form_memory_size=self.max_form_memory_size, - max_form_parts=self.max_form_parts, - cls=self.cls, - ) - boundary = options.get("boundary", "").encode("ascii") - - if not boundary: - raise ValueError("Missing boundary") - - form, files = parser.parse(stream, boundary, content_length) - return stream, form, files - - def _parse_urlencoded( - self, - stream: t.IO[bytes], - mimetype: str, - content_length: int | None, - options: dict[str, str], - ) -> t_parse_result: - if ( - self.max_form_memory_size is not None - and content_length is not None - and content_length > self.max_form_memory_size - ): - raise RequestEntityTooLarge() - - items = parse_qsl( - stream.read().decode(), - keep_blank_values=True, - errors="werkzeug.url_quote", - ) - return stream, self.cls(items), self.cls() - - -class MultiPartParser: - def __init__( - self, - stream_factory: TStreamFactory | None = None, - max_form_memory_size: int | None = None, - cls: type[MultiDict[str, t.Any]] | None = None, - buffer_size: int = 64 * 1024, - max_form_parts: int | None = None, - ) -> None: - self.max_form_memory_size = max_form_memory_size - self.max_form_parts = max_form_parts - - if stream_factory is None: - stream_factory = default_stream_factory - - self.stream_factory = stream_factory - - if cls is None: - cls = t.cast("type[MultiDict[str, t.Any]]", MultiDict) - - self.cls = cls - self.buffer_size = buffer_size - - def fail(self, message: str) -> te.NoReturn: - raise ValueError(message) - - def get_part_charset(self, headers: Headers) -> str: - # Figure out input charset for current part - content_type = headers.get("content-type") - - if content_type: - parameters = parse_options_header(content_type)[1] - ct_charset = parameters.get("charset", "").lower() - - # A safe list of encodings. Modern clients should only send ASCII or UTF-8. - # This list will not be extended further. - if ct_charset in {"ascii", "us-ascii", "utf-8", "iso-8859-1"}: - return ct_charset - - return "utf-8" - - def start_file_streaming( - self, event: File, total_content_length: int | None - ) -> t.IO[bytes]: - content_type = event.headers.get("content-type") - - try: - content_length = _plain_int(event.headers["content-length"]) - except (KeyError, ValueError): - content_length = 0 - - container = self.stream_factory( - total_content_length=total_content_length, - filename=event.filename, - content_type=content_type, - content_length=content_length, - ) - return container - - def parse( - self, stream: t.IO[bytes], boundary: bytes, content_length: int | None - ) -> tuple[MultiDict[str, str], MultiDict[str, FileStorage]]: - current_part: Field | File - field_size: int | None = None - container: t.IO[bytes] | list[bytes] - _write: t.Callable[[bytes], t.Any] - - parser = MultipartDecoder( - boundary, - max_form_memory_size=self.max_form_memory_size, - max_parts=self.max_form_parts, - ) - - fields = [] - files = [] - - for data in _chunk_iter(stream.read, self.buffer_size): - parser.receive_data(data) - event = parser.next_event() - while not isinstance(event, (Epilogue, NeedData)): - if isinstance(event, Field): - current_part = event - field_size = 0 - container = [] - _write = container.append - elif isinstance(event, File): - current_part = event - field_size = None - container = self.start_file_streaming(event, content_length) - _write = container.write - elif isinstance(event, Data): - if self.max_form_memory_size is not None and field_size is not None: - # Ensure that accumulated data events do not exceed limit. - # Also checked within single event in MultipartDecoder. - field_size += len(event.data) - - if field_size > self.max_form_memory_size: - raise RequestEntityTooLarge() - - _write(event.data) - if not event.more_data: - if isinstance(current_part, Field): - value = b"".join(container).decode( - self.get_part_charset(current_part.headers), "replace" - ) - fields.append((current_part.name, value)) - else: - container = t.cast(t.IO[bytes], container) - container.seek(0) - files.append( - ( - current_part.name, - FileStorage( - container, - current_part.filename, - current_part.name, - headers=current_part.headers, - ), - ) - ) - - event = parser.next_event() - - return self.cls(fields), self.cls(files) - - -def _chunk_iter(read: t.Callable[[int], bytes], size: int) -> t.Iterator[bytes | None]: - """Read data in chunks for multipart/form-data parsing. Stop if no data is read. - Yield ``None`` at the end to signal end of parsing. - """ - while True: - data = read(size) - - if not data: - break - - yield data - - yield None diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/http.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/http.py deleted file mode 100644 index cb8bc25..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/http.py +++ /dev/null @@ -1,1391 +0,0 @@ -from __future__ import annotations - -import email.utils -import re -import typing as t -import warnings -from datetime import date -from datetime import datetime -from datetime import time -from datetime import timedelta -from datetime import timezone -from enum import Enum -from hashlib import sha1 -from time import mktime -from time import struct_time -from urllib.parse import quote -from urllib.parse import unquote -from urllib.request import parse_http_list as _parse_list_header - -from ._internal import _dt_as_utc -from ._internal import _plain_int - -if t.TYPE_CHECKING: - from _typeshed.wsgi import WSGIEnvironment - -_token_chars = frozenset( - "!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~" -) -_etag_re = re.compile(r'([Ww]/)?(?:"(.*?)"|(.*?))(?:\s*,\s*|$)') -_entity_headers = frozenset( - [ - "allow", - "content-encoding", - "content-language", - "content-length", - "content-location", - "content-md5", - "content-range", - "content-type", - "expires", - "last-modified", - ] -) -_hop_by_hop_headers = frozenset( - [ - "connection", - "keep-alive", - "proxy-authenticate", - "proxy-authorization", - "te", - "trailer", - "transfer-encoding", - "upgrade", - ] -) -HTTP_STATUS_CODES = { - 100: "Continue", - 101: "Switching Protocols", - 102: "Processing", - 103: "Early Hints", # see RFC 8297 - 200: "OK", - 201: "Created", - 202: "Accepted", - 203: "Non Authoritative Information", - 204: "No Content", - 205: "Reset Content", - 206: "Partial Content", - 207: "Multi Status", - 208: "Already Reported", # see RFC 5842 - 226: "IM Used", # see RFC 3229 - 300: "Multiple Choices", - 301: "Moved Permanently", - 302: "Found", - 303: "See Other", - 304: "Not Modified", - 305: "Use Proxy", - 306: "Switch Proxy", # unused - 307: "Temporary Redirect", - 308: "Permanent Redirect", - 400: "Bad Request", - 401: "Unauthorized", - 402: "Payment Required", # unused - 403: "Forbidden", - 404: "Not Found", - 405: "Method Not Allowed", - 406: "Not Acceptable", - 407: "Proxy Authentication Required", - 408: "Request Timeout", - 409: "Conflict", - 410: "Gone", - 411: "Length Required", - 412: "Precondition Failed", - 413: "Request Entity Too Large", - 414: "Request URI Too Long", - 415: "Unsupported Media Type", - 416: "Requested Range Not Satisfiable", - 417: "Expectation Failed", - 418: "I'm a teapot", # see RFC 2324 - 421: "Misdirected Request", # see RFC 7540 - 422: "Unprocessable Entity", - 423: "Locked", - 424: "Failed Dependency", - 425: "Too Early", # see RFC 8470 - 426: "Upgrade Required", - 428: "Precondition Required", # see RFC 6585 - 429: "Too Many Requests", - 431: "Request Header Fields Too Large", - 449: "Retry With", # proprietary MS extension - 451: "Unavailable For Legal Reasons", - 500: "Internal Server Error", - 501: "Not Implemented", - 502: "Bad Gateway", - 503: "Service Unavailable", - 504: "Gateway Timeout", - 505: "HTTP Version Not Supported", - 506: "Variant Also Negotiates", # see RFC 2295 - 507: "Insufficient Storage", - 508: "Loop Detected", # see RFC 5842 - 510: "Not Extended", - 511: "Network Authentication Failed", -} - - -class COEP(Enum): - """Cross Origin Embedder Policies""" - - UNSAFE_NONE = "unsafe-none" - REQUIRE_CORP = "require-corp" - - -class COOP(Enum): - """Cross Origin Opener Policies""" - - UNSAFE_NONE = "unsafe-none" - SAME_ORIGIN_ALLOW_POPUPS = "same-origin-allow-popups" - SAME_ORIGIN = "same-origin" - - -def quote_header_value(value: t.Any, allow_token: bool = True) -> str: - """Add double quotes around a header value. If the header contains only ASCII token - characters, it will be returned unchanged. If the header contains ``"`` or ``\\`` - characters, they will be escaped with an additional ``\\`` character. - - This is the reverse of :func:`unquote_header_value`. - - :param value: The value to quote. Will be converted to a string. - :param allow_token: Disable to quote the value even if it only has token characters. - - .. versionchanged:: 3.0 - Passing bytes is not supported. - - .. versionchanged:: 3.0 - The ``extra_chars`` parameter is removed. - - .. versionchanged:: 2.3 - The value is quoted if it is the empty string. - - .. versionadded:: 0.5 - """ - value_str = str(value) - - if not value_str: - return '""' - - if allow_token: - token_chars = _token_chars - - if token_chars.issuperset(value_str): - return value_str - - value_str = value_str.replace("\\", "\\\\").replace('"', '\\"') - return f'"{value_str}"' - - -def unquote_header_value(value: str) -> str: - """Remove double quotes and decode slash-escaped ``"`` and ``\\`` characters in a - header value. - - This is the reverse of :func:`quote_header_value`. - - :param value: The header value to unquote. - - .. versionchanged:: 3.0 - The ``is_filename`` parameter is removed. - """ - if len(value) >= 2 and value[0] == value[-1] == '"': - value = value[1:-1] - return value.replace("\\\\", "\\").replace('\\"', '"') - - return value - - -def dump_options_header(header: str | None, options: t.Mapping[str, t.Any]) -> str: - """Produce a header value and ``key=value`` parameters separated by semicolons - ``;``. For example, the ``Content-Type`` header. - - .. code-block:: python - - dump_options_header("text/html", {"charset": "UTF-8"}) - 'text/html; charset=UTF-8' - - This is the reverse of :func:`parse_options_header`. - - If a value contains non-token characters, it will be quoted. - - If a value is ``None``, the parameter is skipped. - - In some keys for some headers, a UTF-8 value can be encoded using a special - ``key*=UTF-8''value`` form, where ``value`` is percent encoded. This function will - not produce that format automatically, but if a given key ends with an asterisk - ``*``, the value is assumed to have that form and will not be quoted further. - - :param header: The primary header value. - :param options: Parameters to encode as ``key=value`` pairs. - - .. versionchanged:: 2.3 - Keys with ``None`` values are skipped rather than treated as a bare key. - - .. versionchanged:: 2.2.3 - If a key ends with ``*``, its value will not be quoted. - """ - segments = [] - - if header is not None: - segments.append(header) - - for key, value in options.items(): - if value is None: - continue - - if key[-1] == "*": - segments.append(f"{key}={value}") - else: - segments.append(f"{key}={quote_header_value(value)}") - - return "; ".join(segments) - - -def dump_header(iterable: dict[str, t.Any] | t.Iterable[t.Any]) -> str: - """Produce a header value from a list of items or ``key=value`` pairs, separated by - commas ``,``. - - This is the reverse of :func:`parse_list_header`, :func:`parse_dict_header`, and - :func:`parse_set_header`. - - If a value contains non-token characters, it will be quoted. - - If a value is ``None``, the key is output alone. - - In some keys for some headers, a UTF-8 value can be encoded using a special - ``key*=UTF-8''value`` form, where ``value`` is percent encoded. This function will - not produce that format automatically, but if a given key ends with an asterisk - ``*``, the value is assumed to have that form and will not be quoted further. - - .. code-block:: python - - dump_header(["foo", "bar baz"]) - 'foo, "bar baz"' - - dump_header({"foo": "bar baz"}) - 'foo="bar baz"' - - :param iterable: The items to create a header from. - - .. versionchanged:: 3.0 - The ``allow_token`` parameter is removed. - - .. versionchanged:: 2.2.3 - If a key ends with ``*``, its value will not be quoted. - """ - if isinstance(iterable, dict): - items = [] - - for key, value in iterable.items(): - if value is None: - items.append(key) - elif key[-1] == "*": - items.append(f"{key}={value}") - else: - items.append(f"{key}={quote_header_value(value)}") - else: - items = [quote_header_value(x) for x in iterable] - - return ", ".join(items) - - -def dump_csp_header(header: ds.ContentSecurityPolicy) -> str: - """Dump a Content Security Policy header. - - These are structured into policies such as "default-src 'self'; - script-src 'self'". - - .. versionadded:: 1.0.0 - Support for Content Security Policy headers was added. - - """ - return "; ".join(f"{key} {value}" for key, value in header.items()) - - -def parse_list_header(value: str) -> list[str]: - """Parse a header value that consists of a list of comma separated items according - to `RFC 9110 `__. - - This extends :func:`urllib.request.parse_http_list` to remove surrounding quotes - from values. - - .. code-block:: python - - parse_list_header('token, "quoted value"') - ['token', 'quoted value'] - - This is the reverse of :func:`dump_header`. - - :param value: The header value to parse. - """ - result = [] - - for item in _parse_list_header(value): - if len(item) >= 2 and item[0] == item[-1] == '"': - item = item[1:-1] - - result.append(item) - - return result - - -def parse_dict_header(value: str) -> dict[str, str | None]: - """Parse a list header using :func:`parse_list_header`, then parse each item as a - ``key=value`` pair. - - .. code-block:: python - - parse_dict_header('a=b, c="d, e", f') - {"a": "b", "c": "d, e", "f": None} - - This is the reverse of :func:`dump_header`. - - If a key does not have a value, it is ``None``. - - This handles charsets for values as described in - `RFC 2231 `__. Only ASCII, UTF-8, - and ISO-8859-1 charsets are accepted, otherwise the value remains quoted. - - :param value: The header value to parse. - - .. versionchanged:: 3.0 - Passing bytes is not supported. - - .. versionchanged:: 3.0 - The ``cls`` argument is removed. - - .. versionchanged:: 2.3 - Added support for ``key*=charset''value`` encoded items. - - .. versionchanged:: 0.9 - The ``cls`` argument was added. - """ - result: dict[str, str | None] = {} - - for item in parse_list_header(value): - key, has_value, value = item.partition("=") - key = key.strip() - - if not key: - # =value is not valid - continue - - if not has_value: - result[key] = None - continue - - value = value.strip() - encoding: str | None = None - - if key[-1] == "*": - # key*=charset''value becomes key=value, where value is percent encoded - # adapted from parse_options_header, without the continuation handling - key = key[:-1] - match = _charset_value_re.match(value) - - if match: - # If there is a charset marker in the value, split it off. - encoding, value = match.groups() - encoding = encoding.lower() - - # A safe list of encodings. Modern clients should only send ASCII or UTF-8. - # This list will not be extended further. An invalid encoding will leave the - # value quoted. - if encoding in {"ascii", "us-ascii", "utf-8", "iso-8859-1"}: - # invalid bytes are replaced during unquoting - value = unquote(value, encoding=encoding) - - if len(value) >= 2 and value[0] == value[-1] == '"': - value = value[1:-1] - - result[key] = value - - return result - - -# https://httpwg.org/specs/rfc9110.html#parameter -_parameter_key_re = re.compile(r"([\w!#$%&'*+\-.^`|~]+)=", flags=re.ASCII) -_parameter_token_value_re = re.compile(r"[\w!#$%&'*+\-.^`|~]+", flags=re.ASCII) -# https://www.rfc-editor.org/rfc/rfc2231#section-4 -_charset_value_re = re.compile( - r""" - ([\w!#$%&*+\-.^`|~]*)' # charset part, could be empty - [\w!#$%&*+\-.^`|~]*' # don't care about language part, usually empty - ([\w!#$%&'*+\-.^`|~]+) # one or more token chars with percent encoding - """, - re.ASCII | re.VERBOSE, -) -# https://www.rfc-editor.org/rfc/rfc2231#section-3 -_continuation_re = re.compile(r"\*(\d+)$", re.ASCII) - - -def parse_options_header(value: str | None) -> tuple[str, dict[str, str]]: - """Parse a header that consists of a value with ``key=value`` parameters separated - by semicolons ``;``. For example, the ``Content-Type`` header. - - .. code-block:: python - - parse_options_header("text/html; charset=UTF-8") - ('text/html', {'charset': 'UTF-8'}) - - parse_options_header("") - ("", {}) - - This is the reverse of :func:`dump_options_header`. - - This parses valid parameter parts as described in - `RFC 9110 `__. Invalid parts are - skipped. - - This handles continuations and charsets as described in - `RFC 2231 `__, although not as - strictly as the RFC. Only ASCII, UTF-8, and ISO-8859-1 charsets are accepted, - otherwise the value remains quoted. - - Clients may not be consistent in how they handle a quote character within a quoted - value. The `HTML Standard `__ - replaces it with ``%22`` in multipart form data. - `RFC 9110 `__ uses backslash - escapes in HTTP headers. Both are decoded to the ``"`` character. - - Clients may not be consistent in how they handle non-ASCII characters. HTML - documents must declare ````, otherwise browsers may replace with - HTML character references, which can be decoded using :func:`html.unescape`. - - :param value: The header value to parse. - :return: ``(value, options)``, where ``options`` is a dict - - .. versionchanged:: 2.3 - Invalid parts, such as keys with no value, quoted keys, and incorrectly quoted - values, are discarded instead of treating as ``None``. - - .. versionchanged:: 2.3 - Only ASCII, UTF-8, and ISO-8859-1 are accepted for charset values. - - .. versionchanged:: 2.3 - Escaped quotes in quoted values, like ``%22`` and ``\\"``, are handled. - - .. versionchanged:: 2.2 - Option names are always converted to lowercase. - - .. versionchanged:: 2.2 - The ``multiple`` parameter was removed. - - .. versionchanged:: 0.15 - :rfc:`2231` parameter continuations are handled. - - .. versionadded:: 0.5 - """ - if value is None: - return "", {} - - value, _, rest = value.partition(";") - value = value.strip() - rest = rest.strip() - - if not value or not rest: - # empty (invalid) value, or value without options - return value, {} - - # Collect all valid key=value parts without processing the value. - parts: list[tuple[str, str]] = [] - - while True: - if (m := _parameter_key_re.match(rest)) is not None: - pk = m.group(1).lower() - rest = rest[m.end() :] - - # Value may be a token. - if (m := _parameter_token_value_re.match(rest)) is not None: - parts.append((pk, m.group())) - - # Value may be a quoted string, find the closing quote. - elif rest[:1] == '"': - pos = 1 - length = len(rest) - - while pos < length: - if rest[pos : pos + 2] in {"\\\\", '\\"'}: - # Consume escaped slashes and quotes. - pos += 2 - elif rest[pos] == '"': - # Stop at an unescaped quote. - parts.append((pk, rest[: pos + 1])) - rest = rest[pos + 1 :] - break - else: - # Consume any other character. - pos += 1 - - # Find the next section delimited by `;`, if any. - if (end := rest.find(";")) == -1: - break - - rest = rest[end + 1 :].lstrip() - - options: dict[str, str] = {} - encoding: str | None = None - continued_encoding: str | None = None - - # For each collected part, process optional charset and continuation, - # unquote quoted values. - for pk, pv in parts: - if pk[-1] == "*": - # key*=charset''value becomes key=value, where value is percent encoded - pk = pk[:-1] - match = _charset_value_re.match(pv) - - if match: - # If there is a valid charset marker in the value, split it off. - encoding, pv = match.groups() - # This might be the empty string, handled next. - encoding = encoding.lower() - - # No charset marker, or marker with empty charset value. - if not encoding: - encoding = continued_encoding - - # A safe list of encodings. Modern clients should only send ASCII or UTF-8. - # This list will not be extended further. An invalid encoding will leave the - # value quoted. - if encoding in {"ascii", "us-ascii", "utf-8", "iso-8859-1"}: - # Continuation parts don't require their own charset marker. This is - # looser than the RFC, it will persist across different keys and allows - # changing the charset during a continuation. But this implementation is - # much simpler than tracking the full state. - continued_encoding = encoding - # invalid bytes are replaced during unquoting - pv = unquote(pv, encoding=encoding) - - # Remove quotes. At this point the value cannot be empty or a single quote. - if pv[0] == pv[-1] == '"': - # HTTP headers use slash, multipart form data uses percent - pv = pv[1:-1].replace("\\\\", "\\").replace('\\"', '"').replace("%22", '"') - - match = _continuation_re.search(pk) - - if match: - # key*0=a; key*1=b becomes key=ab - pk = pk[: match.start()] - options[pk] = options.get(pk, "") + pv - else: - options[pk] = pv - - return value, options - - -_q_value_re = re.compile(r"-?\d+(\.\d+)?", re.ASCII) -_TAnyAccept = t.TypeVar("_TAnyAccept", bound="ds.Accept") - - -@t.overload -def parse_accept_header(value: str | None) -> ds.Accept: ... - - -@t.overload -def parse_accept_header(value: str | None, cls: type[_TAnyAccept]) -> _TAnyAccept: ... - - -def parse_accept_header( - value: str | None, cls: type[_TAnyAccept] | None = None -) -> _TAnyAccept: - """Parse an ``Accept`` header according to - `RFC 9110 `__. - - Returns an :class:`.Accept` instance, which can sort and inspect items based on - their quality parameter. When parsing ``Accept-Charset``, ``Accept-Encoding``, or - ``Accept-Language``, pass the appropriate :class:`.Accept` subclass. - - :param value: The header value to parse. - :param cls: The :class:`.Accept` class to wrap the result in. - :return: An instance of ``cls``. - - .. versionchanged:: 2.3 - Parse according to RFC 9110. Items with invalid ``q`` values are skipped. - """ - if cls is None: - cls = t.cast(t.Type[_TAnyAccept], ds.Accept) - - if not value: - return cls(None) - - result = [] - - for item in parse_list_header(value): - item, options = parse_options_header(item) - - if "q" in options: - # pop q, remaining options are reconstructed - q_str = options.pop("q").strip() - - if _q_value_re.fullmatch(q_str) is None: - # ignore an invalid q - continue - - q = float(q_str) - - if q < 0 or q > 1: - # ignore an invalid q - continue - else: - q = 1 - - if options: - # reconstruct the media type with any options - item = dump_options_header(item, options) - - result.append((item, q)) - - return cls(result) - - -_TAnyCC = t.TypeVar("_TAnyCC", bound="ds.cache_control._CacheControl") - - -@t.overload -def parse_cache_control_header( - value: str | None, - on_update: t.Callable[[ds.cache_control._CacheControl], None] | None = None, -) -> ds.RequestCacheControl: ... - - -@t.overload -def parse_cache_control_header( - value: str | None, - on_update: t.Callable[[ds.cache_control._CacheControl], None] | None = None, - cls: type[_TAnyCC] = ..., -) -> _TAnyCC: ... - - -def parse_cache_control_header( - value: str | None, - on_update: t.Callable[[ds.cache_control._CacheControl], None] | None = None, - cls: type[_TAnyCC] | None = None, -) -> _TAnyCC: - """Parse a cache control header. The RFC differs between response and - request cache control, this method does not. It's your responsibility - to not use the wrong control statements. - - .. versionadded:: 0.5 - The `cls` was added. If not specified an immutable - :class:`~werkzeug.datastructures.RequestCacheControl` is returned. - - :param value: a cache control header to be parsed. - :param on_update: an optional callable that is called every time a value - on the :class:`~werkzeug.datastructures.CacheControl` - object is changed. - :param cls: the class for the returned object. By default - :class:`~werkzeug.datastructures.RequestCacheControl` is used. - :return: a `cls` object. - """ - if cls is None: - cls = t.cast("type[_TAnyCC]", ds.RequestCacheControl) - - if not value: - return cls((), on_update) - - return cls(parse_dict_header(value), on_update) - - -_TAnyCSP = t.TypeVar("_TAnyCSP", bound="ds.ContentSecurityPolicy") - - -@t.overload -def parse_csp_header( - value: str | None, - on_update: t.Callable[[ds.ContentSecurityPolicy], None] | None = None, -) -> ds.ContentSecurityPolicy: ... - - -@t.overload -def parse_csp_header( - value: str | None, - on_update: t.Callable[[ds.ContentSecurityPolicy], None] | None = None, - cls: type[_TAnyCSP] = ..., -) -> _TAnyCSP: ... - - -def parse_csp_header( - value: str | None, - on_update: t.Callable[[ds.ContentSecurityPolicy], None] | None = None, - cls: type[_TAnyCSP] | None = None, -) -> _TAnyCSP: - """Parse a Content Security Policy header. - - .. versionadded:: 1.0.0 - Support for Content Security Policy headers was added. - - :param value: a csp header to be parsed. - :param on_update: an optional callable that is called every time a value - on the object is changed. - :param cls: the class for the returned object. By default - :class:`~werkzeug.datastructures.ContentSecurityPolicy` is used. - :return: a `cls` object. - """ - if cls is None: - cls = t.cast("type[_TAnyCSP]", ds.ContentSecurityPolicy) - - if value is None: - return cls((), on_update) - - items = [] - - for policy in value.split(";"): - policy = policy.strip() - - # Ignore badly formatted policies (no space) - if " " in policy: - directive, value = policy.strip().split(" ", 1) - items.append((directive.strip(), value.strip())) - - return cls(items, on_update) - - -def parse_set_header( - value: str | None, - on_update: t.Callable[[ds.HeaderSet], None] | None = None, -) -> ds.HeaderSet: - """Parse a set-like header and return a - :class:`~werkzeug.datastructures.HeaderSet` object: - - >>> hs = parse_set_header('token, "quoted value"') - - The return value is an object that treats the items case-insensitively - and keeps the order of the items: - - >>> 'TOKEN' in hs - True - >>> hs.index('quoted value') - 1 - >>> hs - HeaderSet(['token', 'quoted value']) - - To create a header from the :class:`HeaderSet` again, use the - :func:`dump_header` function. - - :param value: a set header to be parsed. - :param on_update: an optional callable that is called every time a - value on the :class:`~werkzeug.datastructures.HeaderSet` - object is changed. - :return: a :class:`~werkzeug.datastructures.HeaderSet` - """ - if not value: - return ds.HeaderSet(None, on_update) - return ds.HeaderSet(parse_list_header(value), on_update) - - -def parse_if_range_header(value: str | None) -> ds.IfRange: - """Parses an if-range header which can be an etag or a date. Returns - a :class:`~werkzeug.datastructures.IfRange` object. - - .. versionchanged:: 2.0 - If the value represents a datetime, it is timezone-aware. - - .. versionadded:: 0.7 - """ - if not value: - return ds.IfRange() - date = parse_date(value) - if date is not None: - return ds.IfRange(date=date) - # drop weakness information - return ds.IfRange(unquote_etag(value)[0]) - - -def parse_range_header( - value: str | None, make_inclusive: bool = True -) -> ds.Range | None: - """Parses a range header into a :class:`~werkzeug.datastructures.Range` - object. If the header is missing or malformed `None` is returned. - `ranges` is a list of ``(start, stop)`` tuples where the ranges are - non-inclusive. - - .. versionadded:: 0.7 - """ - if not value or "=" not in value: - return None - - ranges = [] - last_end = 0 - units, rng = value.split("=", 1) - units = units.strip().lower() - - for item in rng.split(","): - item = item.strip() - if "-" not in item: - return None - if item.startswith("-"): - if last_end < 0: - return None - try: - begin = _plain_int(item) - except ValueError: - return None - end = None - last_end = -1 - elif "-" in item: - begin_str, end_str = item.split("-", 1) - begin_str = begin_str.strip() - end_str = end_str.strip() - - try: - begin = _plain_int(begin_str) - except ValueError: - return None - - if begin < last_end or last_end < 0: - return None - if end_str: - try: - end = _plain_int(end_str) + 1 - except ValueError: - return None - - if begin >= end: - return None - else: - end = None - last_end = end if end is not None else -1 - ranges.append((begin, end)) - - return ds.Range(units, ranges) - - -def parse_content_range_header( - value: str | None, - on_update: t.Callable[[ds.ContentRange], None] | None = None, -) -> ds.ContentRange | None: - """Parses a range header into a - :class:`~werkzeug.datastructures.ContentRange` object or `None` if - parsing is not possible. - - .. versionadded:: 0.7 - - :param value: a content range header to be parsed. - :param on_update: an optional callable that is called every time a value - on the :class:`~werkzeug.datastructures.ContentRange` - object is changed. - """ - if value is None: - return None - try: - units, rangedef = (value or "").strip().split(None, 1) - except ValueError: - return None - - if "/" not in rangedef: - return None - rng, length_str = rangedef.split("/", 1) - if length_str == "*": - length = None - else: - try: - length = _plain_int(length_str) - except ValueError: - return None - - if rng == "*": - if not is_byte_range_valid(None, None, length): - return None - - return ds.ContentRange(units, None, None, length, on_update=on_update) - elif "-" not in rng: - return None - - start_str, stop_str = rng.split("-", 1) - try: - start = _plain_int(start_str) - stop = _plain_int(stop_str) + 1 - except ValueError: - return None - - if is_byte_range_valid(start, stop, length): - return ds.ContentRange(units, start, stop, length, on_update=on_update) - - return None - - -def quote_etag(etag: str, weak: bool = False) -> str: - """Quote an etag. - - :param etag: the etag to quote. - :param weak: set to `True` to tag it "weak". - """ - if '"' in etag: - raise ValueError("invalid etag") - etag = f'"{etag}"' - if weak: - etag = f"W/{etag}" - return etag - - -def unquote_etag( - etag: str | None, -) -> tuple[str, bool] | tuple[None, None]: - """Unquote a single etag: - - >>> unquote_etag('W/"bar"') - ('bar', True) - >>> unquote_etag('"bar"') - ('bar', False) - - :param etag: the etag identifier to unquote. - :return: a ``(etag, weak)`` tuple. - """ - if not etag: - return None, None - etag = etag.strip() - weak = False - if etag.startswith(("W/", "w/")): - weak = True - etag = etag[2:] - if etag[:1] == etag[-1:] == '"': - etag = etag[1:-1] - return etag, weak - - -def parse_etags(value: str | None) -> ds.ETags: - """Parse an etag header. - - :param value: the tag header to parse - :return: an :class:`~werkzeug.datastructures.ETags` object. - """ - if not value: - return ds.ETags() - strong = [] - weak = [] - end = len(value) - pos = 0 - while pos < end: - match = _etag_re.match(value, pos) - if match is None: - break - is_weak, quoted, raw = match.groups() - if raw == "*": - return ds.ETags(star_tag=True) - elif quoted: - raw = quoted - if is_weak: - weak.append(raw) - else: - strong.append(raw) - pos = match.end() - return ds.ETags(strong, weak) - - -def generate_etag(data: bytes) -> str: - """Generate an etag for some data. - - .. versionchanged:: 2.0 - Use SHA-1. MD5 may not be available in some environments. - """ - return sha1(data).hexdigest() - - -def parse_date(value: str | None) -> datetime | None: - """Parse an :rfc:`2822` date into a timezone-aware - :class:`datetime.datetime` object, or ``None`` if parsing fails. - - This is a wrapper for :func:`email.utils.parsedate_to_datetime`. It - returns ``None`` if parsing fails instead of raising an exception, - and always returns a timezone-aware datetime object. If the string - doesn't have timezone information, it is assumed to be UTC. - - :param value: A string with a supported date format. - - .. versionchanged:: 2.0 - Return a timezone-aware datetime object. Use - ``email.utils.parsedate_to_datetime``. - """ - if value is None: - return None - - try: - dt = email.utils.parsedate_to_datetime(value) - except (TypeError, ValueError): - return None - - if dt.tzinfo is None: - return dt.replace(tzinfo=timezone.utc) - - return dt - - -def http_date( - timestamp: datetime | date | int | float | struct_time | None = None, -) -> str: - """Format a datetime object or timestamp into an :rfc:`2822` date - string. - - This is a wrapper for :func:`email.utils.format_datetime`. It - assumes naive datetime objects are in UTC instead of raising an - exception. - - :param timestamp: The datetime or timestamp to format. Defaults to - the current time. - - .. versionchanged:: 2.0 - Use ``email.utils.format_datetime``. Accept ``date`` objects. - """ - if isinstance(timestamp, date): - if not isinstance(timestamp, datetime): - # Assume plain date is midnight UTC. - timestamp = datetime.combine(timestamp, time(), tzinfo=timezone.utc) - else: - # Ensure datetime is timezone-aware. - timestamp = _dt_as_utc(timestamp) - - return email.utils.format_datetime(timestamp, usegmt=True) - - if isinstance(timestamp, struct_time): - timestamp = mktime(timestamp) - - return email.utils.formatdate(timestamp, usegmt=True) - - -def parse_age(value: str | None = None) -> timedelta | None: - """Parses a base-10 integer count of seconds into a timedelta. - - If parsing fails, the return value is `None`. - - :param value: a string consisting of an integer represented in base-10 - :return: a :class:`datetime.timedelta` object or `None`. - """ - if not value: - return None - try: - seconds = int(value) - except ValueError: - return None - if seconds < 0: - return None - try: - return timedelta(seconds=seconds) - except OverflowError: - return None - - -def dump_age(age: timedelta | int | None = None) -> str | None: - """Formats the duration as a base-10 integer. - - :param age: should be an integer number of seconds, - a :class:`datetime.timedelta` object, or, - if the age is unknown, `None` (default). - """ - if age is None: - return None - if isinstance(age, timedelta): - age = int(age.total_seconds()) - else: - age = int(age) - - if age < 0: - raise ValueError("age cannot be negative") - - return str(age) - - -def is_resource_modified( - environ: WSGIEnvironment, - etag: str | None = None, - data: bytes | None = None, - last_modified: datetime | str | None = None, - ignore_if_range: bool = True, -) -> bool: - """Convenience method for conditional requests. - - :param environ: the WSGI environment of the request to be checked. - :param etag: the etag for the response for comparison. - :param data: or alternatively the data of the response to automatically - generate an etag using :func:`generate_etag`. - :param last_modified: an optional date of the last modification. - :param ignore_if_range: If `False`, `If-Range` header will be taken into - account. - :return: `True` if the resource was modified, otherwise `False`. - - .. versionchanged:: 2.0 - SHA-1 is used to generate an etag value for the data. MD5 may - not be available in some environments. - - .. versionchanged:: 1.0.0 - The check is run for methods other than ``GET`` and ``HEAD``. - """ - return _sansio_http.is_resource_modified( - http_range=environ.get("HTTP_RANGE"), - http_if_range=environ.get("HTTP_IF_RANGE"), - http_if_modified_since=environ.get("HTTP_IF_MODIFIED_SINCE"), - http_if_none_match=environ.get("HTTP_IF_NONE_MATCH"), - http_if_match=environ.get("HTTP_IF_MATCH"), - etag=etag, - data=data, - last_modified=last_modified, - ignore_if_range=ignore_if_range, - ) - - -def remove_entity_headers( - headers: ds.Headers | list[tuple[str, str]], - allowed: t.Iterable[str] = ("expires", "content-location"), -) -> None: - """Remove all entity headers from a list or :class:`Headers` object. This - operation works in-place. `Expires` and `Content-Location` headers are - by default not removed. The reason for this is :rfc:`2616` section - 10.3.5 which specifies some entity headers that should be sent. - - .. versionchanged:: 0.5 - added `allowed` parameter. - - :param headers: a list or :class:`Headers` object. - :param allowed: a list of headers that should still be allowed even though - they are entity headers. - """ - allowed = {x.lower() for x in allowed} - headers[:] = [ - (key, value) - for key, value in headers - if not is_entity_header(key) or key.lower() in allowed - ] - - -def remove_hop_by_hop_headers(headers: ds.Headers | list[tuple[str, str]]) -> None: - """Remove all HTTP/1.1 "Hop-by-Hop" headers from a list or - :class:`Headers` object. This operation works in-place. - - .. versionadded:: 0.5 - - :param headers: a list or :class:`Headers` object. - """ - headers[:] = [ - (key, value) for key, value in headers if not is_hop_by_hop_header(key) - ] - - -def is_entity_header(header: str) -> bool: - """Check if a header is an entity header. - - .. versionadded:: 0.5 - - :param header: the header to test. - :return: `True` if it's an entity header, `False` otherwise. - """ - return header.lower() in _entity_headers - - -def is_hop_by_hop_header(header: str) -> bool: - """Check if a header is an HTTP/1.1 "Hop-by-Hop" header. - - .. versionadded:: 0.5 - - :param header: the header to test. - :return: `True` if it's an HTTP/1.1 "Hop-by-Hop" header, `False` otherwise. - """ - return header.lower() in _hop_by_hop_headers - - -def parse_cookie( - header: WSGIEnvironment | str | None, - cls: type[ds.MultiDict[str, str]] | None = None, -) -> ds.MultiDict[str, str]: - """Parse a cookie from a string or WSGI environ. - - The same key can be provided multiple times, the values are stored - in-order. The default :class:`MultiDict` will have the first value - first, and all values can be retrieved with - :meth:`MultiDict.getlist`. - - :param header: The cookie header as a string, or a WSGI environ dict - with a ``HTTP_COOKIE`` key. - :param cls: A dict-like class to store the parsed cookies in. - Defaults to :class:`MultiDict`. - - .. versionchanged:: 3.0 - Passing bytes, and the ``charset`` and ``errors`` parameters, were removed. - - .. versionchanged:: 1.0 - Returns a :class:`MultiDict` instead of a ``TypeConversionDict``. - - .. versionchanged:: 0.5 - Returns a :class:`TypeConversionDict` instead of a regular dict. The ``cls`` - parameter was added. - """ - if isinstance(header, dict): - cookie = header.get("HTTP_COOKIE") - else: - cookie = header - - if cookie: - cookie = cookie.encode("latin1").decode() - - return _sansio_http.parse_cookie(cookie=cookie, cls=cls) - - -_cookie_no_quote_re = re.compile(r"[\w!#$%&'()*+\-./:<=>?@\[\]^`{|}~]*", re.A) -_cookie_slash_re = re.compile(rb"[\x00-\x19\",;\\\x7f-\xff]", re.A) -_cookie_slash_map = {b'"': b'\\"', b"\\": b"\\\\"} -_cookie_slash_map.update( - (v.to_bytes(1, "big"), b"\\%03o" % v) - for v in [*range(0x20), *b",;", *range(0x7F, 256)] -) - - -def dump_cookie( - key: str, - value: str = "", - max_age: timedelta | int | None = None, - expires: str | datetime | int | float | None = None, - path: str | None = "/", - domain: str | None = None, - secure: bool = False, - httponly: bool = False, - sync_expires: bool = True, - max_size: int = 4093, - samesite: str | None = None, -) -> str: - """Create a Set-Cookie header without the ``Set-Cookie`` prefix. - - The return value is usually restricted to ascii as the vast majority - of values are properly escaped, but that is no guarantee. It's - tunneled through latin1 as required by :pep:`3333`. - - The return value is not ASCII safe if the key contains unicode - characters. This is technically against the specification but - happens in the wild. It's strongly recommended to not use - non-ASCII values for the keys. - - :param max_age: should be a number of seconds, or `None` (default) if - the cookie should last only as long as the client's - browser session. Additionally `timedelta` objects - are accepted, too. - :param expires: should be a `datetime` object or unix timestamp. - :param path: limits the cookie to a given path, per default it will - span the whole domain. - :param domain: Use this if you want to set a cross-domain cookie. For - example, ``domain="example.com"`` will set a cookie - that is readable by the domain ``www.example.com``, - ``foo.example.com`` etc. Otherwise, a cookie will only - be readable by the domain that set it. - :param secure: The cookie will only be available via HTTPS - :param httponly: disallow JavaScript to access the cookie. This is an - extension to the cookie standard and probably not - supported by all browsers. - :param charset: the encoding for string values. - :param sync_expires: automatically set expires if max_age is defined - but expires not. - :param max_size: Warn if the final header value exceeds this size. The - default, 4093, should be safely `supported by most browsers - `_. Set to 0 to disable this check. - :param samesite: Limits the scope of the cookie such that it will - only be attached to requests if those requests are same-site. - - .. _`cookie`: http://browsercookielimits.squawky.net/ - - .. versionchanged:: 3.0 - Passing bytes, and the ``charset`` parameter, were removed. - - .. versionchanged:: 2.3.3 - The ``path`` parameter is ``/`` by default. - - .. versionchanged:: 2.3.1 - The value allows more characters without quoting. - - .. versionchanged:: 2.3 - ``localhost`` and other names without a dot are allowed for the domain. A - leading dot is ignored. - - .. versionchanged:: 2.3 - The ``path`` parameter is ``None`` by default. - - .. versionchanged:: 1.0.0 - The string ``'None'`` is accepted for ``samesite``. - """ - if path is not None: - # safe = https://url.spec.whatwg.org/#url-path-segment-string - # as well as percent for things that are already quoted - # excluding semicolon since it's part of the header syntax - path = quote(path, safe="%!$&'()*+,/:=@") - - if domain: - domain = domain.partition(":")[0].lstrip(".").encode("idna").decode("ascii") - - if isinstance(max_age, timedelta): - max_age = int(max_age.total_seconds()) - - if expires is not None: - if not isinstance(expires, str): - expires = http_date(expires) - elif max_age is not None and sync_expires: - expires = http_date(datetime.now(tz=timezone.utc).timestamp() + max_age) - - if samesite is not None: - samesite = samesite.title() - - if samesite not in {"Strict", "Lax", "None"}: - raise ValueError("SameSite must be 'Strict', 'Lax', or 'None'.") - - # Quote value if it contains characters not allowed by RFC 6265. Slash-escape with - # three octal digits, which matches http.cookies, although the RFC suggests base64. - if not _cookie_no_quote_re.fullmatch(value): - # Work with bytes here, since a UTF-8 character could be multiple bytes. - value = _cookie_slash_re.sub( - lambda m: _cookie_slash_map[m.group()], value.encode() - ).decode("ascii") - value = f'"{value}"' - - # Send a non-ASCII key as mojibake. Everything else should already be ASCII. - # TODO Remove encoding dance, it seems like clients accept UTF-8 keys - buf = [f"{key.encode().decode('latin1')}={value}"] - - for k, v in ( - ("Domain", domain), - ("Expires", expires), - ("Max-Age", max_age), - ("Secure", secure), - ("HttpOnly", httponly), - ("Path", path), - ("SameSite", samesite), - ): - if v is None or v is False: - continue - - if v is True: - buf.append(k) - continue - - buf.append(f"{k}={v}") - - rv = "; ".join(buf) - - # Warn if the final value of the cookie is larger than the limit. If the cookie is - # too large, then it may be silently ignored by the browser, which can be quite hard - # to debug. - cookie_size = len(rv) - - if max_size and cookie_size > max_size: - value_size = len(value) - warnings.warn( - f"The '{key}' cookie is too large: the value was {value_size} bytes but the" - f" header required {cookie_size - value_size} extra bytes. The final size" - f" was {cookie_size} bytes but the limit is {max_size} bytes. Browsers may" - " silently ignore cookies larger than this.", - stacklevel=2, - ) - - return rv - - -def is_byte_range_valid( - start: int | None, stop: int | None, length: int | None -) -> bool: - """Checks if a given byte content range is valid for the given length. - - .. versionadded:: 0.7 - """ - if (start is None) != (stop is None): - return False - elif start is None: - return length is None or length >= 0 - elif length is None: - return 0 <= start < stop # type: ignore - elif start >= stop: # type: ignore - return False - return 0 <= start < length - - -# circular dependencies -from . import datastructures as ds -from .sansio import http as _sansio_http diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/local.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/local.py deleted file mode 100644 index 302589b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/local.py +++ /dev/null @@ -1,653 +0,0 @@ -from __future__ import annotations - -import copy -import math -import operator -import typing as t -from contextvars import ContextVar -from functools import partial -from functools import update_wrapper -from operator import attrgetter - -from .wsgi import ClosingIterator - -if t.TYPE_CHECKING: - from _typeshed.wsgi import StartResponse - from _typeshed.wsgi import WSGIApplication - from _typeshed.wsgi import WSGIEnvironment - -T = t.TypeVar("T") -F = t.TypeVar("F", bound=t.Callable[..., t.Any]) - - -def release_local(local: Local | LocalStack[t.Any]) -> None: - """Release the data for the current context in a :class:`Local` or - :class:`LocalStack` without using a :class:`LocalManager`. - - This should not be needed for modern use cases, and may be removed - in the future. - - .. versionadded:: 0.6.1 - """ - local.__release_local__() - - -class Local: - """Create a namespace of context-local data. This wraps a - :class:`ContextVar` containing a :class:`dict` value. - - This may incur a performance penalty compared to using individual - context vars, as it has to copy data to avoid mutating the dict - between nested contexts. - - :param context_var: The :class:`~contextvars.ContextVar` to use as - storage for this local. If not given, one will be created. - Context vars not created at the global scope may interfere with - garbage collection. - - .. versionchanged:: 2.0 - Uses ``ContextVar`` instead of a custom storage implementation. - """ - - __slots__ = ("__storage",) - - def __init__(self, context_var: ContextVar[dict[str, t.Any]] | None = None) -> None: - if context_var is None: - # A ContextVar not created at global scope interferes with - # Python's garbage collection. However, a local only makes - # sense defined at the global scope as well, in which case - # the GC issue doesn't seem relevant. - context_var = ContextVar(f"werkzeug.Local<{id(self)}>.storage") - - object.__setattr__(self, "_Local__storage", context_var) - - def __iter__(self) -> t.Iterator[tuple[str, t.Any]]: - return iter(self.__storage.get({}).items()) - - def __call__( - self, name: str, *, unbound_message: str | None = None - ) -> LocalProxy[t.Any]: - """Create a :class:`LocalProxy` that access an attribute on this - local namespace. - - :param name: Proxy this attribute. - :param unbound_message: The error message that the proxy will - show if the attribute isn't set. - """ - return LocalProxy(self, name, unbound_message=unbound_message) - - def __release_local__(self) -> None: - self.__storage.set({}) - - def __getattr__(self, name: str) -> t.Any: - values = self.__storage.get({}) - - if name in values: - return values[name] - - raise AttributeError(name) - - def __setattr__(self, name: str, value: t.Any) -> None: - values = self.__storage.get({}).copy() - values[name] = value - self.__storage.set(values) - - def __delattr__(self, name: str) -> None: - values = self.__storage.get({}) - - if name in values: - values = values.copy() - del values[name] - self.__storage.set(values) - else: - raise AttributeError(name) - - -class LocalStack(t.Generic[T]): - """Create a stack of context-local data. This wraps a - :class:`ContextVar` containing a :class:`list` value. - - This may incur a performance penalty compared to using individual - context vars, as it has to copy data to avoid mutating the list - between nested contexts. - - :param context_var: The :class:`~contextvars.ContextVar` to use as - storage for this local. If not given, one will be created. - Context vars not created at the global scope may interfere with - garbage collection. - - .. versionchanged:: 2.0 - Uses ``ContextVar`` instead of a custom storage implementation. - - .. versionadded:: 0.6.1 - """ - - __slots__ = ("_storage",) - - def __init__(self, context_var: ContextVar[list[T]] | None = None) -> None: - if context_var is None: - # A ContextVar not created at global scope interferes with - # Python's garbage collection. However, a local only makes - # sense defined at the global scope as well, in which case - # the GC issue doesn't seem relevant. - context_var = ContextVar(f"werkzeug.LocalStack<{id(self)}>.storage") - - self._storage = context_var - - def __release_local__(self) -> None: - self._storage.set([]) - - def push(self, obj: T) -> list[T]: - """Add a new item to the top of the stack.""" - stack = self._storage.get([]).copy() - stack.append(obj) - self._storage.set(stack) - return stack - - def pop(self) -> T | None: - """Remove the top item from the stack and return it. If the - stack is empty, return ``None``. - """ - stack = self._storage.get([]) - - if len(stack) == 0: - return None - - rv = stack[-1] - self._storage.set(stack[:-1]) - return rv - - @property - def top(self) -> T | None: - """The topmost item on the stack. If the stack is empty, - `None` is returned. - """ - stack = self._storage.get([]) - - if len(stack) == 0: - return None - - return stack[-1] - - def __call__( - self, name: str | None = None, *, unbound_message: str | None = None - ) -> LocalProxy[t.Any]: - """Create a :class:`LocalProxy` that accesses the top of this - local stack. - - :param name: If given, the proxy access this attribute of the - top item, rather than the item itself. - :param unbound_message: The error message that the proxy will - show if the stack is empty. - """ - return LocalProxy(self, name, unbound_message=unbound_message) - - -class LocalManager: - """Manage releasing the data for the current context in one or more - :class:`Local` and :class:`LocalStack` objects. - - This should not be needed for modern use cases, and may be removed - in the future. - - :param locals: A local or list of locals to manage. - - .. versionchanged:: 2.1 - The ``ident_func`` was removed. - - .. versionchanged:: 0.7 - The ``ident_func`` parameter was added. - - .. versionchanged:: 0.6.1 - The :func:`release_local` function can be used instead of a - manager. - """ - - __slots__ = ("locals",) - - def __init__( - self, - locals: None - | (Local | LocalStack[t.Any] | t.Iterable[Local | LocalStack[t.Any]]) = None, - ) -> None: - if locals is None: - self.locals = [] - elif isinstance(locals, Local): - self.locals = [locals] - else: - self.locals = list(locals) # type: ignore[arg-type] - - def cleanup(self) -> None: - """Release the data in the locals for this context. Call this at - the end of each request or use :meth:`make_middleware`. - """ - for local in self.locals: - release_local(local) - - def make_middleware(self, app: WSGIApplication) -> WSGIApplication: - """Wrap a WSGI application so that local data is released - automatically after the response has been sent for a request. - """ - - def application( - environ: WSGIEnvironment, start_response: StartResponse - ) -> t.Iterable[bytes]: - return ClosingIterator(app(environ, start_response), self.cleanup) - - return application - - def middleware(self, func: WSGIApplication) -> WSGIApplication: - """Like :meth:`make_middleware` but used as a decorator on the - WSGI application function. - - .. code-block:: python - - @manager.middleware - def application(environ, start_response): - ... - """ - return update_wrapper(self.make_middleware(func), func) - - def __repr__(self) -> str: - return f"<{type(self).__name__} storages: {len(self.locals)}>" - - -class _ProxyLookup: - """Descriptor that handles proxied attribute lookup for - :class:`LocalProxy`. - - :param f: The built-in function this attribute is accessed through. - Instead of looking up the special method, the function call - is redone on the object. - :param fallback: Return this function if the proxy is unbound - instead of raising a :exc:`RuntimeError`. - :param is_attr: This proxied name is an attribute, not a function. - Call the fallback immediately to get the value. - :param class_value: Value to return when accessed from the - ``LocalProxy`` class directly. Used for ``__doc__`` so building - docs still works. - """ - - __slots__ = ("bind_f", "fallback", "is_attr", "class_value", "name") - - def __init__( - self, - f: t.Callable[..., t.Any] | None = None, - fallback: t.Callable[[LocalProxy[t.Any]], t.Any] | None = None, - class_value: t.Any | None = None, - is_attr: bool = False, - ) -> None: - bind_f: t.Callable[[LocalProxy[t.Any], t.Any], t.Callable[..., t.Any]] | None - - if hasattr(f, "__get__"): - # A Python function, can be turned into a bound method. - - def bind_f( - instance: LocalProxy[t.Any], obj: t.Any - ) -> t.Callable[..., t.Any]: - return f.__get__(obj, type(obj)) # type: ignore - - elif f is not None: - # A C function, use partial to bind the first argument. - - def bind_f( - instance: LocalProxy[t.Any], obj: t.Any - ) -> t.Callable[..., t.Any]: - return partial(f, obj) - - else: - # Use getattr, which will produce a bound method. - bind_f = None - - self.bind_f = bind_f - self.fallback = fallback - self.class_value = class_value - self.is_attr = is_attr - - def __set_name__(self, owner: LocalProxy[t.Any], name: str) -> None: - self.name = name - - def __get__(self, instance: LocalProxy[t.Any], owner: type | None = None) -> t.Any: - if instance is None: - if self.class_value is not None: - return self.class_value - - return self - - try: - obj = instance._get_current_object() - except RuntimeError: - if self.fallback is None: - raise - - fallback = self.fallback.__get__(instance, owner) - - if self.is_attr: - # __class__ and __doc__ are attributes, not methods. - # Call the fallback to get the value. - return fallback() - - return fallback - - if self.bind_f is not None: - return self.bind_f(instance, obj) - - return getattr(obj, self.name) - - def __repr__(self) -> str: - return f"proxy {self.name}" - - def __call__( - self, instance: LocalProxy[t.Any], *args: t.Any, **kwargs: t.Any - ) -> t.Any: - """Support calling unbound methods from the class. For example, - this happens with ``copy.copy``, which does - ``type(x).__copy__(x)``. ``type(x)`` can't be proxied, so it - returns the proxy type and descriptor. - """ - return self.__get__(instance, type(instance))(*args, **kwargs) - - -class _ProxyIOp(_ProxyLookup): - """Look up an augmented assignment method on a proxied object. The - method is wrapped to return the proxy instead of the object. - """ - - __slots__ = () - - def __init__( - self, - f: t.Callable[..., t.Any] | None = None, - fallback: t.Callable[[LocalProxy[t.Any]], t.Any] | None = None, - ) -> None: - super().__init__(f, fallback) - - def bind_f(instance: LocalProxy[t.Any], obj: t.Any) -> t.Callable[..., t.Any]: - def i_op(self: t.Any, other: t.Any) -> LocalProxy[t.Any]: - f(self, other) # type: ignore - return instance - - return i_op.__get__(obj, type(obj)) # type: ignore - - self.bind_f = bind_f - - -def _l_to_r_op(op: F) -> F: - """Swap the argument order to turn an l-op into an r-op.""" - - def r_op(obj: t.Any, other: t.Any) -> t.Any: - return op(other, obj) - - return t.cast(F, r_op) - - -def _identity(o: T) -> T: - return o - - -class LocalProxy(t.Generic[T]): - """A proxy to the object bound to a context-local object. All - operations on the proxy are forwarded to the bound object. If no - object is bound, a ``RuntimeError`` is raised. - - :param local: The context-local object that provides the proxied - object. - :param name: Proxy this attribute from the proxied object. - :param unbound_message: The error message to show if the - context-local object is unbound. - - Proxy a :class:`~contextvars.ContextVar` to make it easier to - access. Pass a name to proxy that attribute. - - .. code-block:: python - - _request_var = ContextVar("request") - request = LocalProxy(_request_var) - session = LocalProxy(_request_var, "session") - - Proxy an attribute on a :class:`Local` namespace by calling the - local with the attribute name: - - .. code-block:: python - - data = Local() - user = data("user") - - Proxy the top item on a :class:`LocalStack` by calling the local. - Pass a name to proxy that attribute. - - .. code-block:: - - app_stack = LocalStack() - current_app = app_stack() - g = app_stack("g") - - Pass a function to proxy the return value from that function. This - was previously used to access attributes of local objects before - that was supported directly. - - .. code-block:: python - - session = LocalProxy(lambda: request.session) - - ``__repr__`` and ``__class__`` are proxied, so ``repr(x)`` and - ``isinstance(x, cls)`` will look like the proxied object. Use - ``issubclass(type(x), LocalProxy)`` to check if an object is a - proxy. - - .. code-block:: python - - repr(user) # - isinstance(user, User) # True - issubclass(type(user), LocalProxy) # True - - .. versionchanged:: 2.2.2 - ``__wrapped__`` is set when wrapping an object, not only when - wrapping a function, to prevent doctest from failing. - - .. versionchanged:: 2.2 - Can proxy a ``ContextVar`` or ``LocalStack`` directly. - - .. versionchanged:: 2.2 - The ``name`` parameter can be used with any proxied object, not - only ``Local``. - - .. versionchanged:: 2.2 - Added the ``unbound_message`` parameter. - - .. versionchanged:: 2.0 - Updated proxied attributes and methods to reflect the current - data model. - - .. versionchanged:: 0.6.1 - The class can be instantiated with a callable. - """ - - __slots__ = ("__wrapped", "_get_current_object") - - _get_current_object: t.Callable[[], T] - """Return the current object this proxy is bound to. If the proxy is - unbound, this raises a ``RuntimeError``. - - This should be used if you need to pass the object to something that - doesn't understand the proxy. It can also be useful for performance - if you are accessing the object multiple times in a function, rather - than going through the proxy multiple times. - """ - - def __init__( - self, - local: ContextVar[T] | Local | LocalStack[T] | t.Callable[[], T], - name: str | None = None, - *, - unbound_message: str | None = None, - ) -> None: - if name is None: - get_name = _identity - else: - get_name = attrgetter(name) # type: ignore[assignment] - - if unbound_message is None: - unbound_message = "object is not bound" - - if isinstance(local, Local): - if name is None: - raise TypeError("'name' is required when proxying a 'Local' object.") - - def _get_current_object() -> T: - try: - return get_name(local) # type: ignore[return-value] - except AttributeError: - raise RuntimeError(unbound_message) from None - - elif isinstance(local, LocalStack): - - def _get_current_object() -> T: - obj = local.top - - if obj is None: - raise RuntimeError(unbound_message) - - return get_name(obj) - - elif isinstance(local, ContextVar): - - def _get_current_object() -> T: - try: - obj = local.get() - except LookupError: - raise RuntimeError(unbound_message) from None - - return get_name(obj) - - elif callable(local): - - def _get_current_object() -> T: - return get_name(local()) - - else: - raise TypeError(f"Don't know how to proxy '{type(local)}'.") - - object.__setattr__(self, "_LocalProxy__wrapped", local) - object.__setattr__(self, "_get_current_object", _get_current_object) - - __doc__ = _ProxyLookup( # type: ignore[assignment] - class_value=__doc__, fallback=lambda self: type(self).__doc__, is_attr=True - ) - __wrapped__ = _ProxyLookup( - fallback=lambda self: self._LocalProxy__wrapped, # type: ignore[attr-defined] - is_attr=True, - ) - # __del__ should only delete the proxy - __repr__ = _ProxyLookup( # type: ignore[assignment] - repr, fallback=lambda self: f"<{type(self).__name__} unbound>" - ) - __str__ = _ProxyLookup(str) # type: ignore[assignment] - __bytes__ = _ProxyLookup(bytes) - __format__ = _ProxyLookup() # type: ignore[assignment] - __lt__ = _ProxyLookup(operator.lt) - __le__ = _ProxyLookup(operator.le) - __eq__ = _ProxyLookup(operator.eq) # type: ignore[assignment] - __ne__ = _ProxyLookup(operator.ne) # type: ignore[assignment] - __gt__ = _ProxyLookup(operator.gt) - __ge__ = _ProxyLookup(operator.ge) - __hash__ = _ProxyLookup(hash) # type: ignore[assignment] - __bool__ = _ProxyLookup(bool, fallback=lambda self: False) - __getattr__ = _ProxyLookup(getattr) - # __getattribute__ triggered through __getattr__ - __setattr__ = _ProxyLookup(setattr) # type: ignore[assignment] - __delattr__ = _ProxyLookup(delattr) # type: ignore[assignment] - __dir__ = _ProxyLookup(dir, fallback=lambda self: []) # type: ignore[assignment] - # __get__ (proxying descriptor not supported) - # __set__ (descriptor) - # __delete__ (descriptor) - # __set_name__ (descriptor) - # __objclass__ (descriptor) - # __slots__ used by proxy itself - # __dict__ (__getattr__) - # __weakref__ (__getattr__) - # __init_subclass__ (proxying metaclass not supported) - # __prepare__ (metaclass) - __class__ = _ProxyLookup(fallback=lambda self: type(self), is_attr=True) # type: ignore[assignment] - __instancecheck__ = _ProxyLookup(lambda self, other: isinstance(other, self)) - __subclasscheck__ = _ProxyLookup(lambda self, other: issubclass(other, self)) - # __class_getitem__ triggered through __getitem__ - __call__ = _ProxyLookup(lambda self, *args, **kwargs: self(*args, **kwargs)) - __len__ = _ProxyLookup(len) - __length_hint__ = _ProxyLookup(operator.length_hint) - __getitem__ = _ProxyLookup(operator.getitem) - __setitem__ = _ProxyLookup(operator.setitem) - __delitem__ = _ProxyLookup(operator.delitem) - # __missing__ triggered through __getitem__ - __iter__ = _ProxyLookup(iter) - __next__ = _ProxyLookup(next) - __reversed__ = _ProxyLookup(reversed) - __contains__ = _ProxyLookup(operator.contains) - __add__ = _ProxyLookup(operator.add) - __sub__ = _ProxyLookup(operator.sub) - __mul__ = _ProxyLookup(operator.mul) - __matmul__ = _ProxyLookup(operator.matmul) - __truediv__ = _ProxyLookup(operator.truediv) - __floordiv__ = _ProxyLookup(operator.floordiv) - __mod__ = _ProxyLookup(operator.mod) - __divmod__ = _ProxyLookup(divmod) - __pow__ = _ProxyLookup(pow) - __lshift__ = _ProxyLookup(operator.lshift) - __rshift__ = _ProxyLookup(operator.rshift) - __and__ = _ProxyLookup(operator.and_) - __xor__ = _ProxyLookup(operator.xor) - __or__ = _ProxyLookup(operator.or_) - __radd__ = _ProxyLookup(_l_to_r_op(operator.add)) - __rsub__ = _ProxyLookup(_l_to_r_op(operator.sub)) - __rmul__ = _ProxyLookup(_l_to_r_op(operator.mul)) - __rmatmul__ = _ProxyLookup(_l_to_r_op(operator.matmul)) - __rtruediv__ = _ProxyLookup(_l_to_r_op(operator.truediv)) - __rfloordiv__ = _ProxyLookup(_l_to_r_op(operator.floordiv)) - __rmod__ = _ProxyLookup(_l_to_r_op(operator.mod)) - __rdivmod__ = _ProxyLookup(_l_to_r_op(divmod)) - __rpow__ = _ProxyLookup(_l_to_r_op(pow)) - __rlshift__ = _ProxyLookup(_l_to_r_op(operator.lshift)) - __rrshift__ = _ProxyLookup(_l_to_r_op(operator.rshift)) - __rand__ = _ProxyLookup(_l_to_r_op(operator.and_)) - __rxor__ = _ProxyLookup(_l_to_r_op(operator.xor)) - __ror__ = _ProxyLookup(_l_to_r_op(operator.or_)) - __iadd__ = _ProxyIOp(operator.iadd) - __isub__ = _ProxyIOp(operator.isub) - __imul__ = _ProxyIOp(operator.imul) - __imatmul__ = _ProxyIOp(operator.imatmul) - __itruediv__ = _ProxyIOp(operator.itruediv) - __ifloordiv__ = _ProxyIOp(operator.ifloordiv) - __imod__ = _ProxyIOp(operator.imod) - __ipow__ = _ProxyIOp(operator.ipow) - __ilshift__ = _ProxyIOp(operator.ilshift) - __irshift__ = _ProxyIOp(operator.irshift) - __iand__ = _ProxyIOp(operator.iand) - __ixor__ = _ProxyIOp(operator.ixor) - __ior__ = _ProxyIOp(operator.ior) - __neg__ = _ProxyLookup(operator.neg) - __pos__ = _ProxyLookup(operator.pos) - __abs__ = _ProxyLookup(abs) - __invert__ = _ProxyLookup(operator.invert) - __complex__ = _ProxyLookup(complex) - __int__ = _ProxyLookup(int) - __float__ = _ProxyLookup(float) - __index__ = _ProxyLookup(operator.index) - __round__ = _ProxyLookup(round) - __trunc__ = _ProxyLookup(math.trunc) - __floor__ = _ProxyLookup(math.floor) - __ceil__ = _ProxyLookup(math.ceil) - __enter__ = _ProxyLookup() - __exit__ = _ProxyLookup() - __await__ = _ProxyLookup() - __aiter__ = _ProxyLookup() - __anext__ = _ProxyLookup() - __aenter__ = _ProxyLookup() - __aexit__ = _ProxyLookup() - __copy__ = _ProxyLookup(copy.copy) - __deepcopy__ = _ProxyLookup(copy.deepcopy) - # __getnewargs_ex__ (pickle through proxy not supported) - # __getnewargs__ (pickle) - # __getstate__ (pickle) - # __setstate__ (pickle) - # __reduce__ (pickle) - # __reduce_ex__ (pickle) diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 23b3ac9..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/dispatcher.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/dispatcher.cpython-38.pyc deleted file mode 100644 index 719f7c7..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/dispatcher.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/http_proxy.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/http_proxy.cpython-38.pyc deleted file mode 100644 index 5ed141a..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/http_proxy.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/lint.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/lint.cpython-38.pyc deleted file mode 100644 index 8408d0e..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/lint.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/profiler.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/profiler.cpython-38.pyc deleted file mode 100644 index a5a4d9d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/profiler.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/proxy_fix.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/proxy_fix.cpython-38.pyc deleted file mode 100644 index 0dec233..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/proxy_fix.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/shared_data.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/shared_data.cpython-38.pyc deleted file mode 100644 index 382a0c4..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/shared_data.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/dispatcher.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/dispatcher.py deleted file mode 100644 index e11bacc..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/dispatcher.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -Application Dispatcher -====================== - -This middleware creates a single WSGI application that dispatches to -multiple other WSGI applications mounted at different URL paths. - -A common example is writing a Single Page Application, where you have a -backend API and a frontend written in JavaScript that does the routing -in the browser rather than requesting different pages from the server. -The frontend is a single HTML and JS file that should be served for any -path besides "/api". - -This example dispatches to an API app under "/api", an admin app -under "/admin", and an app that serves frontend files for all other -requests:: - - app = DispatcherMiddleware(serve_frontend, { - '/api': api_app, - '/admin': admin_app, - }) - -In production, you might instead handle this at the HTTP server level, -serving files or proxying to application servers based on location. The -API and admin apps would each be deployed with a separate WSGI server, -and the static files would be served directly by the HTTP server. - -.. autoclass:: DispatcherMiddleware - -:copyright: 2007 Pallets -:license: BSD-3-Clause -""" - -from __future__ import annotations - -import typing as t - -if t.TYPE_CHECKING: - from _typeshed.wsgi import StartResponse - from _typeshed.wsgi import WSGIApplication - from _typeshed.wsgi import WSGIEnvironment - - -class DispatcherMiddleware: - """Combine multiple applications as a single WSGI application. - Requests are dispatched to an application based on the path it is - mounted under. - - :param app: The WSGI application to dispatch to if the request - doesn't match a mounted path. - :param mounts: Maps path prefixes to applications for dispatching. - """ - - def __init__( - self, - app: WSGIApplication, - mounts: dict[str, WSGIApplication] | None = None, - ) -> None: - self.app = app - self.mounts = mounts or {} - - def __call__( - self, environ: WSGIEnvironment, start_response: StartResponse - ) -> t.Iterable[bytes]: - script = environ.get("PATH_INFO", "") - path_info = "" - - while "/" in script: - if script in self.mounts: - app = self.mounts[script] - break - - script, last_item = script.rsplit("/", 1) - path_info = f"/{last_item}{path_info}" - else: - app = self.mounts.get(script, self.app) - - original_script_name = environ.get("SCRIPT_NAME", "") - environ["SCRIPT_NAME"] = original_script_name + script - environ["PATH_INFO"] = path_info - return app(environ, start_response) diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/http_proxy.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/http_proxy.py deleted file mode 100644 index 5e23915..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/http_proxy.py +++ /dev/null @@ -1,236 +0,0 @@ -""" -Basic HTTP Proxy -================ - -.. autoclass:: ProxyMiddleware - -:copyright: 2007 Pallets -:license: BSD-3-Clause -""" - -from __future__ import annotations - -import typing as t -from http import client -from urllib.parse import quote -from urllib.parse import urlsplit - -from ..datastructures import EnvironHeaders -from ..http import is_hop_by_hop_header -from ..wsgi import get_input_stream - -if t.TYPE_CHECKING: - from _typeshed.wsgi import StartResponse - from _typeshed.wsgi import WSGIApplication - from _typeshed.wsgi import WSGIEnvironment - - -class ProxyMiddleware: - """Proxy requests under a path to an external server, routing other - requests to the app. - - This middleware can only proxy HTTP requests, as HTTP is the only - protocol handled by the WSGI server. Other protocols, such as - WebSocket requests, cannot be proxied at this layer. This should - only be used for development, in production a real proxy server - should be used. - - The middleware takes a dict mapping a path prefix to a dict - describing the host to be proxied to:: - - app = ProxyMiddleware(app, { - "/static/": { - "target": "http://127.0.0.1:5001/", - } - }) - - Each host has the following options: - - ``target``: - The target URL to dispatch to. This is required. - ``remove_prefix``: - Whether to remove the prefix from the URL before dispatching it - to the target. The default is ``False``. - ``host``: - ``""`` (default): - The host header is automatically rewritten to the URL of the - target. - ``None``: - The host header is unmodified from the client request. - Any other value: - The host header is overwritten with the value. - ``headers``: - A dictionary of headers to be sent with the request to the - target. The default is ``{}``. - ``ssl_context``: - A :class:`ssl.SSLContext` defining how to verify requests if the - target is HTTPS. The default is ``None``. - - In the example above, everything under ``"/static/"`` is proxied to - the server on port 5001. The host header is rewritten to the target, - and the ``"/static/"`` prefix is removed from the URLs. - - :param app: The WSGI application to wrap. - :param targets: Proxy target configurations. See description above. - :param chunk_size: Size of chunks to read from input stream and - write to target. - :param timeout: Seconds before an operation to a target fails. - - .. versionadded:: 0.14 - """ - - def __init__( - self, - app: WSGIApplication, - targets: t.Mapping[str, dict[str, t.Any]], - chunk_size: int = 2 << 13, - timeout: int = 10, - ) -> None: - def _set_defaults(opts: dict[str, t.Any]) -> dict[str, t.Any]: - opts.setdefault("remove_prefix", False) - opts.setdefault("host", "") - opts.setdefault("headers", {}) - opts.setdefault("ssl_context", None) - return opts - - self.app = app - self.targets = { - f"/{k.strip('/')}/": _set_defaults(v) for k, v in targets.items() - } - self.chunk_size = chunk_size - self.timeout = timeout - - def proxy_to( - self, opts: dict[str, t.Any], path: str, prefix: str - ) -> WSGIApplication: - target = urlsplit(opts["target"]) - # socket can handle unicode host, but header must be ascii - host = target.hostname.encode("idna").decode("ascii") - - def application( - environ: WSGIEnvironment, start_response: StartResponse - ) -> t.Iterable[bytes]: - headers = list(EnvironHeaders(environ).items()) - headers[:] = [ - (k, v) - for k, v in headers - if not is_hop_by_hop_header(k) - and k.lower() not in ("content-length", "host") - ] - headers.append(("Connection", "close")) - - if opts["host"] == "": - headers.append(("Host", host)) - elif opts["host"] is None: - headers.append(("Host", environ["HTTP_HOST"])) - else: - headers.append(("Host", opts["host"])) - - headers.extend(opts["headers"].items()) - remote_path = path - - if opts["remove_prefix"]: - remote_path = remote_path[len(prefix) :].lstrip("/") - remote_path = f"{target.path.rstrip('/')}/{remote_path}" - - content_length = environ.get("CONTENT_LENGTH") - chunked = False - - if content_length not in ("", None): - headers.append(("Content-Length", content_length)) # type: ignore - elif content_length is not None: - headers.append(("Transfer-Encoding", "chunked")) - chunked = True - - try: - if target.scheme == "http": - con = client.HTTPConnection( - host, target.port or 80, timeout=self.timeout - ) - elif target.scheme == "https": - con = client.HTTPSConnection( - host, - target.port or 443, - timeout=self.timeout, - context=opts["ssl_context"], - ) - else: - raise RuntimeError( - "Target scheme must be 'http' or 'https', got" - f" {target.scheme!r}." - ) - - con.connect() - # safe = https://url.spec.whatwg.org/#url-path-segment-string - # as well as percent for things that are already quoted - remote_url = quote(remote_path, safe="!$&'()*+,/:;=@%") - querystring = environ["QUERY_STRING"] - - if querystring: - remote_url = f"{remote_url}?{querystring}" - - con.putrequest(environ["REQUEST_METHOD"], remote_url, skip_host=True) - - for k, v in headers: - if k.lower() == "connection": - v = "close" - - con.putheader(k, v) - - con.endheaders() - stream = get_input_stream(environ) - - while True: - data = stream.read(self.chunk_size) - - if not data: - break - - if chunked: - con.send(b"%x\r\n%s\r\n" % (len(data), data)) - else: - con.send(data) - - resp = con.getresponse() - except OSError: - from ..exceptions import BadGateway - - return BadGateway()(environ, start_response) - - start_response( - f"{resp.status} {resp.reason}", - [ - (k.title(), v) - for k, v in resp.getheaders() - if not is_hop_by_hop_header(k) - ], - ) - - def read() -> t.Iterator[bytes]: - while True: - try: - data = resp.read(self.chunk_size) - except OSError: - break - - if not data: - break - - yield data - - return read() - - return application - - def __call__( - self, environ: WSGIEnvironment, start_response: StartResponse - ) -> t.Iterable[bytes]: - path = environ["PATH_INFO"] - app = self.app - - for prefix, opts in self.targets.items(): - if path.startswith(prefix): - app = self.proxy_to(opts, path, prefix) - break - - return app(environ, start_response) diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/lint.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/lint.py deleted file mode 100644 index de93b52..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/lint.py +++ /dev/null @@ -1,439 +0,0 @@ -""" -WSGI Protocol Linter -==================== - -This module provides a middleware that performs sanity checks on the -behavior of the WSGI server and application. It checks that the -:pep:`3333` WSGI spec is properly implemented. It also warns on some -common HTTP errors such as non-empty responses for 304 status codes. - -.. autoclass:: LintMiddleware - -:copyright: 2007 Pallets -:license: BSD-3-Clause -""" - -from __future__ import annotations - -import typing as t -from types import TracebackType -from urllib.parse import urlparse -from warnings import warn - -from ..datastructures import Headers -from ..http import is_entity_header -from ..wsgi import FileWrapper - -if t.TYPE_CHECKING: - from _typeshed.wsgi import StartResponse - from _typeshed.wsgi import WSGIApplication - from _typeshed.wsgi import WSGIEnvironment - - -class WSGIWarning(Warning): - """Warning class for WSGI warnings.""" - - -class HTTPWarning(Warning): - """Warning class for HTTP warnings.""" - - -def check_type(context: str, obj: object, need: type = str) -> None: - if type(obj) is not need: - warn( - f"{context!r} requires {need.__name__!r}, got {type(obj).__name__!r}.", - WSGIWarning, - stacklevel=3, - ) - - -class InputStream: - def __init__(self, stream: t.IO[bytes]) -> None: - self._stream = stream - - def read(self, *args: t.Any) -> bytes: - if len(args) == 0: - warn( - "WSGI does not guarantee an EOF marker on the input stream, thus making" - " calls to 'wsgi.input.read()' unsafe. Conforming servers may never" - " return from this call.", - WSGIWarning, - stacklevel=2, - ) - elif len(args) != 1: - warn( - "Too many parameters passed to 'wsgi.input.read()'.", - WSGIWarning, - stacklevel=2, - ) - return self._stream.read(*args) - - def readline(self, *args: t.Any) -> bytes: - if len(args) == 0: - warn( - "Calls to 'wsgi.input.readline()' without arguments are unsafe. Use" - " 'wsgi.input.read()' instead.", - WSGIWarning, - stacklevel=2, - ) - elif len(args) == 1: - warn( - "'wsgi.input.readline()' was called with a size hint. WSGI does not" - " support this, although it's available on all major servers.", - WSGIWarning, - stacklevel=2, - ) - else: - raise TypeError("Too many arguments passed to 'wsgi.input.readline()'.") - return self._stream.readline(*args) - - def __iter__(self) -> t.Iterator[bytes]: - try: - return iter(self._stream) - except TypeError: - warn("'wsgi.input' is not iterable.", WSGIWarning, stacklevel=2) - return iter(()) - - def close(self) -> None: - warn("The application closed the input stream!", WSGIWarning, stacklevel=2) - self._stream.close() - - -class ErrorStream: - def __init__(self, stream: t.IO[str]) -> None: - self._stream = stream - - def write(self, s: str) -> None: - check_type("wsgi.error.write()", s, str) - self._stream.write(s) - - def flush(self) -> None: - self._stream.flush() - - def writelines(self, seq: t.Iterable[str]) -> None: - for line in seq: - self.write(line) - - def close(self) -> None: - warn("The application closed the error stream!", WSGIWarning, stacklevel=2) - self._stream.close() - - -class GuardedWrite: - def __init__(self, write: t.Callable[[bytes], object], chunks: list[int]) -> None: - self._write = write - self._chunks = chunks - - def __call__(self, s: bytes) -> None: - check_type("write()", s, bytes) - self._write(s) - self._chunks.append(len(s)) - - -class GuardedIterator: - def __init__( - self, - iterator: t.Iterable[bytes], - headers_set: tuple[int, Headers], - chunks: list[int], - ) -> None: - self._iterator = iterator - self._next = iter(iterator).__next__ - self.closed = False - self.headers_set = headers_set - self.chunks = chunks - - def __iter__(self) -> GuardedIterator: - return self - - def __next__(self) -> bytes: - if self.closed: - warn("Iterated over closed 'app_iter'.", WSGIWarning, stacklevel=2) - - rv = self._next() - - if not self.headers_set: - warn( - "The application returned before it started the response.", - WSGIWarning, - stacklevel=2, - ) - - check_type("application iterator items", rv, bytes) - self.chunks.append(len(rv)) - return rv - - def close(self) -> None: - self.closed = True - - if hasattr(self._iterator, "close"): - self._iterator.close() - - if self.headers_set: - status_code, headers = self.headers_set - bytes_sent = sum(self.chunks) - content_length = headers.get("content-length", type=int) - - if status_code == 304: - for key, _value in headers: - key = key.lower() - if key not in ("expires", "content-location") and is_entity_header( - key - ): - warn( - f"Entity header {key!r} found in 304 response.", - HTTPWarning, - stacklevel=2, - ) - if bytes_sent: - warn( - "304 responses must not have a body.", - HTTPWarning, - stacklevel=2, - ) - elif 100 <= status_code < 200 or status_code == 204: - if content_length != 0: - warn( - f"{status_code} responses must have an empty content length.", - HTTPWarning, - stacklevel=2, - ) - if bytes_sent: - warn( - f"{status_code} responses must not have a body.", - HTTPWarning, - stacklevel=2, - ) - elif content_length is not None and content_length != bytes_sent: - warn( - "Content-Length and the number of bytes sent to the" - " client do not match.", - WSGIWarning, - stacklevel=2, - ) - - def __del__(self) -> None: - if not self.closed: - try: - warn( - "Iterator was garbage collected before it was closed.", - WSGIWarning, - stacklevel=2, - ) - except Exception: - pass - - -class LintMiddleware: - """Warns about common errors in the WSGI and HTTP behavior of the - server and wrapped application. Some of the issues it checks are: - - - invalid status codes - - non-bytes sent to the WSGI server - - strings returned from the WSGI application - - non-empty conditional responses - - unquoted etags - - relative URLs in the Location header - - unsafe calls to wsgi.input - - unclosed iterators - - Error information is emitted using the :mod:`warnings` module. - - :param app: The WSGI application to wrap. - - .. code-block:: python - - from werkzeug.middleware.lint import LintMiddleware - app = LintMiddleware(app) - """ - - def __init__(self, app: WSGIApplication) -> None: - self.app = app - - def check_environ(self, environ: WSGIEnvironment) -> None: - if type(environ) is not dict: # noqa: E721 - warn( - "WSGI environment is not a standard Python dict.", - WSGIWarning, - stacklevel=4, - ) - for key in ( - "REQUEST_METHOD", - "SERVER_NAME", - "SERVER_PORT", - "wsgi.version", - "wsgi.input", - "wsgi.errors", - "wsgi.multithread", - "wsgi.multiprocess", - "wsgi.run_once", - ): - if key not in environ: - warn( - f"Required environment key {key!r} not found", - WSGIWarning, - stacklevel=3, - ) - if environ["wsgi.version"] != (1, 0): - warn("Environ is not a WSGI 1.0 environ.", WSGIWarning, stacklevel=3) - - script_name = environ.get("SCRIPT_NAME", "") - path_info = environ.get("PATH_INFO", "") - - if script_name and script_name[0] != "/": - warn( - f"'SCRIPT_NAME' does not start with a slash: {script_name!r}", - WSGIWarning, - stacklevel=3, - ) - - if path_info and path_info[0] != "/": - warn( - f"'PATH_INFO' does not start with a slash: {path_info!r}", - WSGIWarning, - stacklevel=3, - ) - - def check_start_response( - self, - status: str, - headers: list[tuple[str, str]], - exc_info: None | (tuple[type[BaseException], BaseException, TracebackType]), - ) -> tuple[int, Headers]: - check_type("status", status, str) - status_code_str = status.split(None, 1)[0] - - if len(status_code_str) != 3 or not status_code_str.isdecimal(): - warn("Status code must be three digits.", WSGIWarning, stacklevel=3) - - if len(status) < 4 or status[3] != " ": - warn( - f"Invalid value for status {status!r}. Valid status strings are three" - " digits, a space and a status explanation.", - WSGIWarning, - stacklevel=3, - ) - - status_code = int(status_code_str) - - if status_code < 100: - warn("Status code < 100 detected.", WSGIWarning, stacklevel=3) - - if type(headers) is not list: # noqa: E721 - warn("Header list is not a list.", WSGIWarning, stacklevel=3) - - for item in headers: - if type(item) is not tuple or len(item) != 2: - warn("Header items must be 2-item tuples.", WSGIWarning, stacklevel=3) - name, value = item - if type(name) is not str or type(value) is not str: # noqa: E721 - warn( - "Header keys and values must be strings.", WSGIWarning, stacklevel=3 - ) - if name.lower() == "status": - warn( - "The status header is not supported due to" - " conflicts with the CGI spec.", - WSGIWarning, - stacklevel=3, - ) - - if exc_info is not None and not isinstance(exc_info, tuple): - warn("Invalid value for exc_info.", WSGIWarning, stacklevel=3) - - headers_obj = Headers(headers) - self.check_headers(headers_obj) - - return status_code, headers_obj - - def check_headers(self, headers: Headers) -> None: - etag = headers.get("etag") - - if etag is not None: - if etag.startswith(("W/", "w/")): - if etag.startswith("w/"): - warn( - "Weak etag indicator should be upper case.", - HTTPWarning, - stacklevel=4, - ) - - etag = etag[2:] - - if not (etag[:1] == etag[-1:] == '"'): - warn("Unquoted etag emitted.", HTTPWarning, stacklevel=4) - - location = headers.get("location") - - if location is not None: - if not urlparse(location).netloc: - warn( - "Absolute URLs required for location header.", - HTTPWarning, - stacklevel=4, - ) - - def check_iterator(self, app_iter: t.Iterable[bytes]) -> None: - if isinstance(app_iter, str): - warn( - "The application returned a string. The response will send one" - " character at a time to the client, which will kill performance." - " Return a list or iterable instead.", - WSGIWarning, - stacklevel=3, - ) - - def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Iterable[bytes]: - if len(args) != 2: - warn("A WSGI app takes two arguments.", WSGIWarning, stacklevel=2) - - if kwargs: - warn( - "A WSGI app does not take keyword arguments.", WSGIWarning, stacklevel=2 - ) - - environ: WSGIEnvironment = args[0] - start_response: StartResponse = args[1] - - self.check_environ(environ) - environ["wsgi.input"] = InputStream(environ["wsgi.input"]) - environ["wsgi.errors"] = ErrorStream(environ["wsgi.errors"]) - - # Hook our own file wrapper in so that applications will always - # iterate to the end and we can check the content length. - environ["wsgi.file_wrapper"] = FileWrapper - - headers_set: list[t.Any] = [] - chunks: list[int] = [] - - def checking_start_response( - *args: t.Any, **kwargs: t.Any - ) -> t.Callable[[bytes], None]: - if len(args) not in {2, 3}: - warn( - f"Invalid number of arguments: {len(args)}, expected 2 or 3.", - WSGIWarning, - stacklevel=2, - ) - - if kwargs: - warn( - "'start_response' does not take keyword arguments.", - WSGIWarning, - stacklevel=2, - ) - - status: str = args[0] - headers: list[tuple[str, str]] = args[1] - exc_info: ( - None | (tuple[type[BaseException], BaseException, TracebackType]) - ) = args[2] if len(args) == 3 else None - - headers_set[:] = self.check_start_response(status, headers, exc_info) - return GuardedWrite(start_response(status, headers, exc_info), chunks) - - app_iter = self.app(environ, t.cast("StartResponse", checking_start_response)) - self.check_iterator(app_iter) - return GuardedIterator( - app_iter, t.cast(t.Tuple[int, Headers], headers_set), chunks - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/profiler.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/profiler.py deleted file mode 100644 index 112b877..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/profiler.py +++ /dev/null @@ -1,155 +0,0 @@ -""" -Application Profiler -==================== - -This module provides a middleware that profiles each request with the -:mod:`cProfile` module. This can help identify bottlenecks in your code -that may be slowing down your application. - -.. autoclass:: ProfilerMiddleware - -:copyright: 2007 Pallets -:license: BSD-3-Clause -""" - -from __future__ import annotations - -import os.path -import sys -import time -import typing as t -from pstats import Stats - -try: - from cProfile import Profile -except ImportError: - from profile import Profile # type: ignore - -if t.TYPE_CHECKING: - from _typeshed.wsgi import StartResponse - from _typeshed.wsgi import WSGIApplication - from _typeshed.wsgi import WSGIEnvironment - - -class ProfilerMiddleware: - """Wrap a WSGI application and profile the execution of each - request. Responses are buffered so that timings are more exact. - - If ``stream`` is given, :class:`pstats.Stats` are written to it - after each request. If ``profile_dir`` is given, :mod:`cProfile` - data files are saved to that directory, one file per request. - - The filename can be customized by passing ``filename_format``. If - it is a string, it will be formatted using :meth:`str.format` with - the following fields available: - - - ``{method}`` - The request method; GET, POST, etc. - - ``{path}`` - The request path or 'root' should one not exist. - - ``{elapsed}`` - The elapsed time of the request in milliseconds. - - ``{time}`` - The time of the request. - - If it is a callable, it will be called with the WSGI ``environ`` and - be expected to return a filename string. The ``environ`` dictionary - will also have the ``"werkzeug.profiler"`` key populated with a - dictionary containing the following fields (more may be added in the - future): - - ``{elapsed}`` - The elapsed time of the request in milliseconds. - - ``{time}`` - The time of the request. - - :param app: The WSGI application to wrap. - :param stream: Write stats to this stream. Disable with ``None``. - :param sort_by: A tuple of columns to sort stats by. See - :meth:`pstats.Stats.sort_stats`. - :param restrictions: A tuple of restrictions to filter stats by. See - :meth:`pstats.Stats.print_stats`. - :param profile_dir: Save profile data files to this directory. - :param filename_format: Format string for profile data file names, - or a callable returning a name. See explanation above. - - .. code-block:: python - - from werkzeug.middleware.profiler import ProfilerMiddleware - app = ProfilerMiddleware(app) - - .. versionchanged:: 3.0 - Added the ``"werkzeug.profiler"`` key to the ``filename_format(environ)`` - parameter with the ``elapsed`` and ``time`` fields. - - .. versionchanged:: 0.15 - Stats are written even if ``profile_dir`` is given, and can be - disable by passing ``stream=None``. - - .. versionadded:: 0.15 - Added ``filename_format``. - - .. versionadded:: 0.9 - Added ``restrictions`` and ``profile_dir``. - """ - - def __init__( - self, - app: WSGIApplication, - stream: t.IO[str] | None = sys.stdout, - sort_by: t.Iterable[str] = ("time", "calls"), - restrictions: t.Iterable[str | int | float] = (), - profile_dir: str | None = None, - filename_format: str = "{method}.{path}.{elapsed:.0f}ms.{time:.0f}.prof", - ) -> None: - self._app = app - self._stream = stream - self._sort_by = sort_by - self._restrictions = restrictions - self._profile_dir = profile_dir - self._filename_format = filename_format - - def __call__( - self, environ: WSGIEnvironment, start_response: StartResponse - ) -> t.Iterable[bytes]: - response_body: list[bytes] = [] - - def catching_start_response(status, headers, exc_info=None): # type: ignore - start_response(status, headers, exc_info) - return response_body.append - - def runapp() -> None: - app_iter = self._app( - environ, t.cast("StartResponse", catching_start_response) - ) - response_body.extend(app_iter) - - if hasattr(app_iter, "close"): - app_iter.close() - - profile = Profile() - start = time.time() - profile.runcall(runapp) - body = b"".join(response_body) - elapsed = time.time() - start - - if self._profile_dir is not None: - if callable(self._filename_format): - environ["werkzeug.profiler"] = { - "elapsed": elapsed * 1000.0, - "time": time.time(), - } - filename = self._filename_format(environ) - else: - filename = self._filename_format.format( - method=environ["REQUEST_METHOD"], - path=environ["PATH_INFO"].strip("/").replace("/", ".") or "root", - elapsed=elapsed * 1000.0, - time=time.time(), - ) - filename = os.path.join(self._profile_dir, filename) - profile.dump_stats(filename) - - if self._stream is not None: - stats = Stats(profile, stream=self._stream) - stats.sort_stats(*self._sort_by) - print("-" * 80, file=self._stream) - path_info = environ.get("PATH_INFO", "") - print(f"PATH: {path_info!r}", file=self._stream) - stats.print_stats(*self._restrictions) - print(f"{'-' * 80}\n", file=self._stream) - - return [body] diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/proxy_fix.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/proxy_fix.py deleted file mode 100644 index cbf4e0b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/proxy_fix.py +++ /dev/null @@ -1,183 +0,0 @@ -""" -X-Forwarded-For Proxy Fix -========================= - -This module provides a middleware that adjusts the WSGI environ based on -``X-Forwarded-`` headers that proxies in front of an application may -set. - -When an application is running behind a proxy server, WSGI may see the -request as coming from that server rather than the real client. Proxies -set various headers to track where the request actually came from. - -This middleware should only be used if the application is actually -behind such a proxy, and should be configured with the number of proxies -that are chained in front of it. Not all proxies set all the headers. -Since incoming headers can be faked, you must set how many proxies are -setting each header so the middleware knows what to trust. - -.. autoclass:: ProxyFix - -:copyright: 2007 Pallets -:license: BSD-3-Clause -""" - -from __future__ import annotations - -import typing as t - -from ..http import parse_list_header - -if t.TYPE_CHECKING: - from _typeshed.wsgi import StartResponse - from _typeshed.wsgi import WSGIApplication - from _typeshed.wsgi import WSGIEnvironment - - -class ProxyFix: - """Adjust the WSGI environ based on ``X-Forwarded-`` that proxies in - front of the application may set. - - - ``X-Forwarded-For`` sets ``REMOTE_ADDR``. - - ``X-Forwarded-Proto`` sets ``wsgi.url_scheme``. - - ``X-Forwarded-Host`` sets ``HTTP_HOST``, ``SERVER_NAME``, and - ``SERVER_PORT``. - - ``X-Forwarded-Port`` sets ``HTTP_HOST`` and ``SERVER_PORT``. - - ``X-Forwarded-Prefix`` sets ``SCRIPT_NAME``. - - You must tell the middleware how many proxies set each header so it - knows what values to trust. It is a security issue to trust values - that came from the client rather than a proxy. - - The original values of the headers are stored in the WSGI - environ as ``werkzeug.proxy_fix.orig``, a dict. - - :param app: The WSGI application to wrap. - :param x_for: Number of values to trust for ``X-Forwarded-For``. - :param x_proto: Number of values to trust for ``X-Forwarded-Proto``. - :param x_host: Number of values to trust for ``X-Forwarded-Host``. - :param x_port: Number of values to trust for ``X-Forwarded-Port``. - :param x_prefix: Number of values to trust for - ``X-Forwarded-Prefix``. - - .. code-block:: python - - from werkzeug.middleware.proxy_fix import ProxyFix - # App is behind one proxy that sets the -For and -Host headers. - app = ProxyFix(app, x_for=1, x_host=1) - - .. versionchanged:: 1.0 - The ``num_proxies`` argument and attribute; the ``get_remote_addr`` method; and - the environ keys ``orig_remote_addr``, ``orig_wsgi_url_scheme``, and - ``orig_http_host`` were removed. - - .. versionchanged:: 0.15 - All headers support multiple values. Each header is configured with a separate - number of trusted proxies. - - .. versionchanged:: 0.15 - Original WSGI environ values are stored in the ``werkzeug.proxy_fix.orig`` dict. - - .. versionchanged:: 0.15 - Support ``X-Forwarded-Port`` and ``X-Forwarded-Prefix``. - - .. versionchanged:: 0.15 - ``X-Forwarded-Host`` and ``X-Forwarded-Port`` modify - ``SERVER_NAME`` and ``SERVER_PORT``. - """ - - def __init__( - self, - app: WSGIApplication, - x_for: int = 1, - x_proto: int = 1, - x_host: int = 0, - x_port: int = 0, - x_prefix: int = 0, - ) -> None: - self.app = app - self.x_for = x_for - self.x_proto = x_proto - self.x_host = x_host - self.x_port = x_port - self.x_prefix = x_prefix - - def _get_real_value(self, trusted: int, value: str | None) -> str | None: - """Get the real value from a list header based on the configured - number of trusted proxies. - - :param trusted: Number of values to trust in the header. - :param value: Comma separated list header value to parse. - :return: The real value, or ``None`` if there are fewer values - than the number of trusted proxies. - - .. versionchanged:: 1.0 - Renamed from ``_get_trusted_comma``. - - .. versionadded:: 0.15 - """ - if not (trusted and value): - return None - values = parse_list_header(value) - if len(values) >= trusted: - return values[-trusted] - return None - - def __call__( - self, environ: WSGIEnvironment, start_response: StartResponse - ) -> t.Iterable[bytes]: - """Modify the WSGI environ based on the various ``Forwarded`` - headers before calling the wrapped application. Store the - original environ values in ``werkzeug.proxy_fix.orig_{key}``. - """ - environ_get = environ.get - orig_remote_addr = environ_get("REMOTE_ADDR") - orig_wsgi_url_scheme = environ_get("wsgi.url_scheme") - orig_http_host = environ_get("HTTP_HOST") - environ.update( - { - "werkzeug.proxy_fix.orig": { - "REMOTE_ADDR": orig_remote_addr, - "wsgi.url_scheme": orig_wsgi_url_scheme, - "HTTP_HOST": orig_http_host, - "SERVER_NAME": environ_get("SERVER_NAME"), - "SERVER_PORT": environ_get("SERVER_PORT"), - "SCRIPT_NAME": environ_get("SCRIPT_NAME"), - } - } - ) - - x_for = self._get_real_value(self.x_for, environ_get("HTTP_X_FORWARDED_FOR")) - if x_for: - environ["REMOTE_ADDR"] = x_for - - x_proto = self._get_real_value( - self.x_proto, environ_get("HTTP_X_FORWARDED_PROTO") - ) - if x_proto: - environ["wsgi.url_scheme"] = x_proto - - x_host = self._get_real_value(self.x_host, environ_get("HTTP_X_FORWARDED_HOST")) - if x_host: - environ["HTTP_HOST"] = environ["SERVER_NAME"] = x_host - # "]" to check for IPv6 address without port - if ":" in x_host and not x_host.endswith("]"): - environ["SERVER_NAME"], environ["SERVER_PORT"] = x_host.rsplit(":", 1) - - x_port = self._get_real_value(self.x_port, environ_get("HTTP_X_FORWARDED_PORT")) - if x_port: - host = environ.get("HTTP_HOST") - if host: - # "]" to check for IPv6 address without port - if ":" in host and not host.endswith("]"): - host = host.rsplit(":", 1)[0] - environ["HTTP_HOST"] = f"{host}:{x_port}" - environ["SERVER_PORT"] = x_port - - x_prefix = self._get_real_value( - self.x_prefix, environ_get("HTTP_X_FORWARDED_PREFIX") - ) - if x_prefix: - environ["SCRIPT_NAME"] = x_prefix - - return self.app(environ, start_response) diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/shared_data.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/shared_data.py deleted file mode 100644 index 0f467f2..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/middleware/shared_data.py +++ /dev/null @@ -1,283 +0,0 @@ -""" -Serve Shared Static Files -========================= - -.. autoclass:: SharedDataMiddleware - :members: is_allowed - -:copyright: 2007 Pallets -:license: BSD-3-Clause -""" - -from __future__ import annotations - -import collections.abc as cabc -import importlib.util -import mimetypes -import os -import posixpath -import typing as t -from datetime import datetime -from datetime import timezone -from io import BytesIO -from time import time -from zlib import adler32 - -from ..http import http_date -from ..http import is_resource_modified -from ..security import safe_join -from ..utils import get_content_type -from ..wsgi import get_path_info -from ..wsgi import wrap_file - -_TOpener = t.Callable[[], t.Tuple[t.IO[bytes], datetime, int]] -_TLoader = t.Callable[[t.Optional[str]], t.Tuple[t.Optional[str], t.Optional[_TOpener]]] - -if t.TYPE_CHECKING: - from _typeshed.wsgi import StartResponse - from _typeshed.wsgi import WSGIApplication - from _typeshed.wsgi import WSGIEnvironment - - -class SharedDataMiddleware: - """A WSGI middleware which provides static content for development - environments or simple server setups. Its usage is quite simple:: - - import os - from werkzeug.middleware.shared_data import SharedDataMiddleware - - app = SharedDataMiddleware(app, { - '/shared': os.path.join(os.path.dirname(__file__), 'shared') - }) - - The contents of the folder ``./shared`` will now be available on - ``http://example.com/shared/``. This is pretty useful during development - because a standalone media server is not required. Files can also be - mounted on the root folder and still continue to use the application because - the shared data middleware forwards all unhandled requests to the - application, even if the requests are below one of the shared folders. - - If `pkg_resources` is available you can also tell the middleware to serve - files from package data:: - - app = SharedDataMiddleware(app, { - '/static': ('myapplication', 'static') - }) - - This will then serve the ``static`` folder in the `myapplication` - Python package. - - The optional `disallow` parameter can be a list of :func:`~fnmatch.fnmatch` - rules for files that are not accessible from the web. If `cache` is set to - `False` no caching headers are sent. - - Currently the middleware does not support non-ASCII filenames. If the - encoding on the file system happens to match the encoding of the URI it may - work but this could also be by accident. We strongly suggest using ASCII - only file names for static files. - - The middleware will guess the mimetype using the Python `mimetype` - module. If it's unable to figure out the charset it will fall back - to `fallback_mimetype`. - - :param app: the application to wrap. If you don't want to wrap an - application you can pass it :exc:`NotFound`. - :param exports: a list or dict of exported files and folders. - :param disallow: a list of :func:`~fnmatch.fnmatch` rules. - :param cache: enable or disable caching headers. - :param cache_timeout: the cache timeout in seconds for the headers. - :param fallback_mimetype: The fallback mimetype for unknown files. - - .. versionchanged:: 1.0 - The default ``fallback_mimetype`` is - ``application/octet-stream``. If a filename looks like a text - mimetype, the ``utf-8`` charset is added to it. - - .. versionadded:: 0.6 - Added ``fallback_mimetype``. - - .. versionchanged:: 0.5 - Added ``cache_timeout``. - """ - - def __init__( - self, - app: WSGIApplication, - exports: ( - cabc.Mapping[str, str | tuple[str, str]] - | t.Iterable[tuple[str, str | tuple[str, str]]] - ), - disallow: None = None, - cache: bool = True, - cache_timeout: int = 60 * 60 * 12, - fallback_mimetype: str = "application/octet-stream", - ) -> None: - self.app = app - self.exports: list[tuple[str, _TLoader]] = [] - self.cache = cache - self.cache_timeout = cache_timeout - - if isinstance(exports, cabc.Mapping): - exports = exports.items() - - for key, value in exports: - if isinstance(value, tuple): - loader = self.get_package_loader(*value) - elif isinstance(value, str): - if os.path.isfile(value): - loader = self.get_file_loader(value) - else: - loader = self.get_directory_loader(value) - else: - raise TypeError(f"unknown def {value!r}") - - self.exports.append((key, loader)) - - if disallow is not None: - from fnmatch import fnmatch - - self.is_allowed = lambda x: not fnmatch(x, disallow) - - self.fallback_mimetype = fallback_mimetype - - def is_allowed(self, filename: str) -> bool: - """Subclasses can override this method to disallow the access to - certain files. However by providing `disallow` in the constructor - this method is overwritten. - """ - return True - - def _opener(self, filename: str) -> _TOpener: - return lambda: ( - open(filename, "rb"), - datetime.fromtimestamp(os.path.getmtime(filename), tz=timezone.utc), - int(os.path.getsize(filename)), - ) - - def get_file_loader(self, filename: str) -> _TLoader: - return lambda x: (os.path.basename(filename), self._opener(filename)) - - def get_package_loader(self, package: str, package_path: str) -> _TLoader: - load_time = datetime.now(timezone.utc) - spec = importlib.util.find_spec(package) - reader = spec.loader.get_resource_reader(package) # type: ignore[union-attr] - - def loader( - path: str | None, - ) -> tuple[str | None, _TOpener | None]: - if path is None: - return None, None - - path = safe_join(package_path, path) - - if path is None: - return None, None - - basename = posixpath.basename(path) - - try: - resource = reader.open_resource(path) - except OSError: - return None, None - - if isinstance(resource, BytesIO): - return ( - basename, - lambda: (resource, load_time, len(resource.getvalue())), - ) - - return ( - basename, - lambda: ( - resource, - datetime.fromtimestamp( - os.path.getmtime(resource.name), tz=timezone.utc - ), - os.path.getsize(resource.name), - ), - ) - - return loader - - def get_directory_loader(self, directory: str) -> _TLoader: - def loader( - path: str | None, - ) -> tuple[str | None, _TOpener | None]: - if path is not None: - path = safe_join(directory, path) - - if path is None: - return None, None - else: - path = directory - - if os.path.isfile(path): - return os.path.basename(path), self._opener(path) - - return None, None - - return loader - - def generate_etag(self, mtime: datetime, file_size: int, real_filename: str) -> str: - fn_str = os.fsencode(real_filename) - timestamp = mtime.timestamp() - checksum = adler32(fn_str) & 0xFFFFFFFF - return f"wzsdm-{timestamp}-{file_size}-{checksum}" - - def __call__( - self, environ: WSGIEnvironment, start_response: StartResponse - ) -> t.Iterable[bytes]: - path = get_path_info(environ) - file_loader = None - - for search_path, loader in self.exports: - if search_path == path: - real_filename, file_loader = loader(None) - - if file_loader is not None: - break - - if not search_path.endswith("/"): - search_path += "/" - - if path.startswith(search_path): - real_filename, file_loader = loader(path[len(search_path) :]) - - if file_loader is not None: - break - - if file_loader is None or not self.is_allowed(real_filename): # type: ignore - return self.app(environ, start_response) - - guessed_type = mimetypes.guess_type(real_filename) # type: ignore - mime_type = get_content_type(guessed_type[0] or self.fallback_mimetype, "utf-8") - f, mtime, file_size = file_loader() - - headers = [("Date", http_date())] - - if self.cache: - timeout = self.cache_timeout - etag = self.generate_etag(mtime, file_size, real_filename) # type: ignore - headers += [ - ("Etag", f'"{etag}"'), - ("Cache-Control", f"max-age={timeout}, public"), - ] - - if not is_resource_modified(environ, etag, last_modified=mtime): - f.close() - start_response("304 Not Modified", headers) - return [] - - headers.append(("Expires", http_date(time() + timeout))) - else: - headers.append(("Cache-Control", "public")) - - headers.extend( - ( - ("Content-Type", mime_type), - ("Content-Length", str(file_size)), - ("Last-Modified", http_date(mtime)), - ) - ) - start_response("200 OK", headers) - return wrap_file(environ, f) diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/py.typed b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__init__.py deleted file mode 100644 index 62adc48..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__init__.py +++ /dev/null @@ -1,134 +0,0 @@ -"""When it comes to combining multiple controller or view functions -(however you want to call them) you need a dispatcher. A simple way -would be applying regular expression tests on the ``PATH_INFO`` and -calling registered callback functions that return the value then. - -This module implements a much more powerful system than simple regular -expression matching because it can also convert values in the URLs and -build URLs. - -Here a simple example that creates a URL map for an application with -two subdomains (www and kb) and some URL rules: - -.. code-block:: python - - m = Map([ - # Static URLs - Rule('/', endpoint='static/index'), - Rule('/about', endpoint='static/about'), - Rule('/help', endpoint='static/help'), - # Knowledge Base - Subdomain('kb', [ - Rule('/', endpoint='kb/index'), - Rule('/browse/', endpoint='kb/browse'), - Rule('/browse//', endpoint='kb/browse'), - Rule('/browse//', endpoint='kb/browse') - ]) - ], default_subdomain='www') - -If the application doesn't use subdomains it's perfectly fine to not set -the default subdomain and not use the `Subdomain` rule factory. The -endpoint in the rules can be anything, for example import paths or -unique identifiers. The WSGI application can use those endpoints to get the -handler for that URL. It doesn't have to be a string at all but it's -recommended. - -Now it's possible to create a URL adapter for one of the subdomains and -build URLs: - -.. code-block:: python - - c = m.bind('example.com') - - c.build("kb/browse", dict(id=42)) - 'http://kb.example.com/browse/42/' - - c.build("kb/browse", dict()) - 'http://kb.example.com/browse/' - - c.build("kb/browse", dict(id=42, page=3)) - 'http://kb.example.com/browse/42/3' - - c.build("static/about") - '/about' - - c.build("static/index", force_external=True) - 'http://www.example.com/' - - c = m.bind('example.com', subdomain='kb') - - c.build("static/about") - 'http://www.example.com/about' - -The first argument to bind is the server name *without* the subdomain. -Per default it will assume that the script is mounted on the root, but -often that's not the case so you can provide the real mount point as -second argument: - -.. code-block:: python - - c = m.bind('example.com', '/applications/example') - -The third argument can be the subdomain, if not given the default -subdomain is used. For more details about binding have a look at the -documentation of the `MapAdapter`. - -And here is how you can match URLs: - -.. code-block:: python - - c = m.bind('example.com') - - c.match("/") - ('static/index', {}) - - c.match("/about") - ('static/about', {}) - - c = m.bind('example.com', '/', 'kb') - - c.match("/") - ('kb/index', {}) - - c.match("/browse/42/23") - ('kb/browse', {'id': 42, 'page': 23}) - -If matching fails you get a ``NotFound`` exception, if the rule thinks -it's a good idea to redirect (for example because the URL was defined -to have a slash at the end but the request was missing that slash) it -will raise a ``RequestRedirect`` exception. Both are subclasses of -``HTTPException`` so you can use those errors as responses in the -application. - -If matching succeeded but the URL rule was incompatible to the given -method (for example there were only rules for ``GET`` and ``HEAD`` but -routing tried to match a ``POST`` request) a ``MethodNotAllowed`` -exception is raised. -""" - -from .converters import AnyConverter as AnyConverter -from .converters import BaseConverter as BaseConverter -from .converters import FloatConverter as FloatConverter -from .converters import IntegerConverter as IntegerConverter -from .converters import PathConverter as PathConverter -from .converters import UnicodeConverter as UnicodeConverter -from .converters import UUIDConverter as UUIDConverter -from .converters import ValidationError as ValidationError -from .exceptions import BuildError as BuildError -from .exceptions import NoMatch as NoMatch -from .exceptions import RequestAliasRedirect as RequestAliasRedirect -from .exceptions import RequestPath as RequestPath -from .exceptions import RequestRedirect as RequestRedirect -from .exceptions import RoutingException as RoutingException -from .exceptions import WebsocketMismatch as WebsocketMismatch -from .map import Map as Map -from .map import MapAdapter as MapAdapter -from .matcher import StateMachineMatcher as StateMachineMatcher -from .rules import EndpointPrefix as EndpointPrefix -from .rules import parse_converter_args as parse_converter_args -from .rules import Rule as Rule -from .rules import RuleFactory as RuleFactory -from .rules import RuleTemplate as RuleTemplate -from .rules import RuleTemplateFactory as RuleTemplateFactory -from .rules import Subdomain as Subdomain -from .rules import Submount as Submount diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 515a21d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__pycache__/converters.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__pycache__/converters.cpython-38.pyc deleted file mode 100644 index 4ad6dc8..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__pycache__/converters.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__pycache__/exceptions.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__pycache__/exceptions.cpython-38.pyc deleted file mode 100644 index c893245..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__pycache__/exceptions.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__pycache__/map.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__pycache__/map.cpython-38.pyc deleted file mode 100644 index 2f5bed4..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__pycache__/map.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__pycache__/matcher.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__pycache__/matcher.cpython-38.pyc deleted file mode 100644 index 4414a14..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__pycache__/matcher.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__pycache__/rules.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__pycache__/rules.cpython-38.pyc deleted file mode 100644 index 5ad3b57..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/__pycache__/rules.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/converters.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/converters.py deleted file mode 100644 index 6016a97..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/converters.py +++ /dev/null @@ -1,261 +0,0 @@ -from __future__ import annotations - -import re -import typing as t -import uuid -from urllib.parse import quote - -if t.TYPE_CHECKING: - from .map import Map - - -class ValidationError(ValueError): - """Validation error. If a rule converter raises this exception the rule - does not match the current URL and the next URL is tried. - """ - - -class BaseConverter: - """Base class for all converters. - - .. versionchanged:: 2.3 - ``part_isolating`` defaults to ``False`` if ``regex`` contains a ``/``. - """ - - regex = "[^/]+" - weight = 100 - part_isolating = True - - def __init_subclass__(cls, **kwargs: t.Any) -> None: - super().__init_subclass__(**kwargs) - - # If the converter isn't inheriting its regex, disable part_isolating by default - # if the regex contains a / character. - if "regex" in cls.__dict__ and "part_isolating" not in cls.__dict__: - cls.part_isolating = "/" not in cls.regex - - def __init__(self, map: Map, *args: t.Any, **kwargs: t.Any) -> None: - self.map = map - - def to_python(self, value: str) -> t.Any: - return value - - def to_url(self, value: t.Any) -> str: - # safe = https://url.spec.whatwg.org/#url-path-segment-string - return quote(str(value), safe="!$&'()*+,/:;=@") - - -class UnicodeConverter(BaseConverter): - """This converter is the default converter and accepts any string but - only one path segment. Thus the string can not include a slash. - - This is the default validator. - - Example:: - - Rule('/pages/'), - Rule('/') - - :param map: the :class:`Map`. - :param minlength: the minimum length of the string. Must be greater - or equal 1. - :param maxlength: the maximum length of the string. - :param length: the exact length of the string. - """ - - def __init__( - self, - map: Map, - minlength: int = 1, - maxlength: int | None = None, - length: int | None = None, - ) -> None: - super().__init__(map) - if length is not None: - length_regex = f"{{{int(length)}}}" - else: - if maxlength is None: - maxlength_value = "" - else: - maxlength_value = str(int(maxlength)) - length_regex = f"{{{int(minlength)},{maxlength_value}}}" - self.regex = f"[^/]{length_regex}" - - -class AnyConverter(BaseConverter): - """Matches one of the items provided. Items can either be Python - identifiers or strings:: - - Rule('/') - - :param map: the :class:`Map`. - :param items: this function accepts the possible items as positional - arguments. - - .. versionchanged:: 2.2 - Value is validated when building a URL. - """ - - def __init__(self, map: Map, *items: str) -> None: - super().__init__(map) - self.items = set(items) - self.regex = f"(?:{'|'.join([re.escape(x) for x in items])})" - - def to_url(self, value: t.Any) -> str: - if value in self.items: - return str(value) - - valid_values = ", ".join(f"'{item}'" for item in sorted(self.items)) - raise ValueError(f"'{value}' is not one of {valid_values}") - - -class PathConverter(BaseConverter): - """Like the default :class:`UnicodeConverter`, but it also matches - slashes. This is useful for wikis and similar applications:: - - Rule('/') - Rule('//edit') - - :param map: the :class:`Map`. - """ - - part_isolating = False - regex = "[^/].*?" - weight = 200 - - -class NumberConverter(BaseConverter): - """Baseclass for `IntegerConverter` and `FloatConverter`. - - :internal: - """ - - weight = 50 - num_convert: t.Callable[[t.Any], t.Any] = int - - def __init__( - self, - map: Map, - fixed_digits: int = 0, - min: int | None = None, - max: int | None = None, - signed: bool = False, - ) -> None: - if signed: - self.regex = self.signed_regex - super().__init__(map) - self.fixed_digits = fixed_digits - self.min = min - self.max = max - self.signed = signed - - def to_python(self, value: str) -> t.Any: - if self.fixed_digits and len(value) != self.fixed_digits: - raise ValidationError() - value_num = self.num_convert(value) - if (self.min is not None and value_num < self.min) or ( - self.max is not None and value_num > self.max - ): - raise ValidationError() - return value_num - - def to_url(self, value: t.Any) -> str: - value_str = str(self.num_convert(value)) - if self.fixed_digits: - value_str = value_str.zfill(self.fixed_digits) - return value_str - - @property - def signed_regex(self) -> str: - return f"-?{self.regex}" - - -class IntegerConverter(NumberConverter): - """This converter only accepts integer values:: - - Rule("/page/") - - By default it only accepts unsigned, positive values. The ``signed`` - parameter will enable signed, negative values. :: - - Rule("/page/") - - :param map: The :class:`Map`. - :param fixed_digits: The number of fixed digits in the URL. If you - set this to ``4`` for example, the rule will only match if the - URL looks like ``/0001/``. The default is variable length. - :param min: The minimal value. - :param max: The maximal value. - :param signed: Allow signed (negative) values. - - .. versionadded:: 0.15 - The ``signed`` parameter. - """ - - regex = r"\d+" - - -class FloatConverter(NumberConverter): - """This converter only accepts floating point values:: - - Rule("/probability/") - - By default it only accepts unsigned, positive values. The ``signed`` - parameter will enable signed, negative values. :: - - Rule("/offset/") - - :param map: The :class:`Map`. - :param min: The minimal value. - :param max: The maximal value. - :param signed: Allow signed (negative) values. - - .. versionadded:: 0.15 - The ``signed`` parameter. - """ - - regex = r"\d+\.\d+" - num_convert = float - - def __init__( - self, - map: Map, - min: float | None = None, - max: float | None = None, - signed: bool = False, - ) -> None: - super().__init__(map, min=min, max=max, signed=signed) # type: ignore - - -class UUIDConverter(BaseConverter): - """This converter only accepts UUID strings:: - - Rule('/object/') - - .. versionadded:: 0.10 - - :param map: the :class:`Map`. - """ - - regex = ( - r"[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-" - r"[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}" - ) - - def to_python(self, value: str) -> uuid.UUID: - return uuid.UUID(value) - - def to_url(self, value: uuid.UUID) -> str: - return str(value) - - -#: the default converter mapping for the map. -DEFAULT_CONVERTERS: t.Mapping[str, type[BaseConverter]] = { - "default": UnicodeConverter, - "string": UnicodeConverter, - "any": AnyConverter, - "path": PathConverter, - "int": IntegerConverter, - "float": FloatConverter, - "uuid": UUIDConverter, -} diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/exceptions.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/exceptions.py deleted file mode 100644 index eeabd4e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/exceptions.py +++ /dev/null @@ -1,152 +0,0 @@ -from __future__ import annotations - -import difflib -import typing as t - -from ..exceptions import BadRequest -from ..exceptions import HTTPException -from ..utils import cached_property -from ..utils import redirect - -if t.TYPE_CHECKING: - from _typeshed.wsgi import WSGIEnvironment - - from ..wrappers.request import Request - from ..wrappers.response import Response - from .map import MapAdapter - from .rules import Rule - - -class RoutingException(Exception): - """Special exceptions that require the application to redirect, notifying - about missing urls, etc. - - :internal: - """ - - -class RequestRedirect(HTTPException, RoutingException): - """Raise if the map requests a redirect. This is for example the case if - `strict_slashes` are activated and an url that requires a trailing slash. - - The attribute `new_url` contains the absolute destination url. - """ - - code = 308 - - def __init__(self, new_url: str) -> None: - super().__init__(new_url) - self.new_url = new_url - - def get_response( - self, - environ: WSGIEnvironment | Request | None = None, - scope: dict[str, t.Any] | None = None, - ) -> Response: - return redirect(self.new_url, self.code) - - -class RequestPath(RoutingException): - """Internal exception.""" - - __slots__ = ("path_info",) - - def __init__(self, path_info: str) -> None: - super().__init__() - self.path_info = path_info - - -class RequestAliasRedirect(RoutingException): # noqa: B903 - """This rule is an alias and wants to redirect to the canonical URL.""" - - def __init__(self, matched_values: t.Mapping[str, t.Any], endpoint: t.Any) -> None: - super().__init__() - self.matched_values = matched_values - self.endpoint = endpoint - - -class BuildError(RoutingException, LookupError): - """Raised if the build system cannot find a URL for an endpoint with the - values provided. - """ - - def __init__( - self, - endpoint: t.Any, - values: t.Mapping[str, t.Any], - method: str | None, - adapter: MapAdapter | None = None, - ) -> None: - super().__init__(endpoint, values, method) - self.endpoint = endpoint - self.values = values - self.method = method - self.adapter = adapter - - @cached_property - def suggested(self) -> Rule | None: - return self.closest_rule(self.adapter) - - def closest_rule(self, adapter: MapAdapter | None) -> Rule | None: - def _score_rule(rule: Rule) -> float: - return sum( - [ - 0.98 - * difflib.SequenceMatcher( - # endpoints can be any type, compare as strings - None, - str(rule.endpoint), - str(self.endpoint), - ).ratio(), - 0.01 * bool(set(self.values or ()).issubset(rule.arguments)), - 0.01 * bool(rule.methods and self.method in rule.methods), - ] - ) - - if adapter and adapter.map._rules: - return max(adapter.map._rules, key=_score_rule) - - return None - - def __str__(self) -> str: - message = [f"Could not build url for endpoint {self.endpoint!r}"] - if self.method: - message.append(f" ({self.method!r})") - if self.values: - message.append(f" with values {sorted(self.values)!r}") - message.append(".") - if self.suggested: - if self.endpoint == self.suggested.endpoint: - if ( - self.method - and self.suggested.methods is not None - and self.method not in self.suggested.methods - ): - message.append( - " Did you mean to use methods" - f" {sorted(self.suggested.methods)!r}?" - ) - missing_values = self.suggested.arguments.union( - set(self.suggested.defaults or ()) - ) - set(self.values.keys()) - if missing_values: - message.append( - f" Did you forget to specify values {sorted(missing_values)!r}?" - ) - else: - message.append(f" Did you mean {self.suggested.endpoint!r} instead?") - return "".join(message) - - -class WebsocketMismatch(BadRequest): - """The only matched rule is either a WebSocket and the request is - HTTP, or the rule is HTTP and the request is a WebSocket. - """ - - -class NoMatch(Exception): - __slots__ = ("have_match_for", "websocket_mismatch") - - def __init__(self, have_match_for: set[str], websocket_mismatch: bool) -> None: - self.have_match_for = have_match_for - self.websocket_mismatch = websocket_mismatch diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/map.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/map.py deleted file mode 100644 index 4d15e88..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/map.py +++ /dev/null @@ -1,951 +0,0 @@ -from __future__ import annotations - -import typing as t -import warnings -from pprint import pformat -from threading import Lock -from urllib.parse import quote -from urllib.parse import urljoin -from urllib.parse import urlunsplit - -from .._internal import _get_environ -from .._internal import _wsgi_decoding_dance -from ..datastructures import ImmutableDict -from ..datastructures import MultiDict -from ..exceptions import BadHost -from ..exceptions import HTTPException -from ..exceptions import MethodNotAllowed -from ..exceptions import NotFound -from ..urls import _urlencode -from ..wsgi import get_host -from .converters import DEFAULT_CONVERTERS -from .exceptions import BuildError -from .exceptions import NoMatch -from .exceptions import RequestAliasRedirect -from .exceptions import RequestPath -from .exceptions import RequestRedirect -from .exceptions import WebsocketMismatch -from .matcher import StateMachineMatcher -from .rules import _simple_rule_re -from .rules import Rule - -if t.TYPE_CHECKING: - from _typeshed.wsgi import WSGIApplication - from _typeshed.wsgi import WSGIEnvironment - - from ..wrappers.request import Request - from .converters import BaseConverter - from .rules import RuleFactory - - -class Map: - """The map class stores all the URL rules and some configuration - parameters. Some of the configuration values are only stored on the - `Map` instance since those affect all rules, others are just defaults - and can be overridden for each rule. Note that you have to specify all - arguments besides the `rules` as keyword arguments! - - :param rules: sequence of url rules for this map. - :param default_subdomain: The default subdomain for rules without a - subdomain defined. - :param strict_slashes: If a rule ends with a slash but the matched - URL does not, redirect to the URL with a trailing slash. - :param merge_slashes: Merge consecutive slashes when matching or - building URLs. Matches will redirect to the normalized URL. - Slashes in variable parts are not merged. - :param redirect_defaults: This will redirect to the default rule if it - wasn't visited that way. This helps creating - unique URLs. - :param converters: A dict of converters that adds additional converters - to the list of converters. If you redefine one - converter this will override the original one. - :param sort_parameters: If set to `True` the url parameters are sorted. - See `url_encode` for more details. - :param sort_key: The sort key function for `url_encode`. - :param host_matching: if set to `True` it enables the host matching - feature and disables the subdomain one. If - enabled the `host` parameter to rules is used - instead of the `subdomain` one. - - .. versionchanged:: 3.0 - The ``charset`` and ``encoding_errors`` parameters were removed. - - .. versionchanged:: 1.0 - If ``url_scheme`` is ``ws`` or ``wss``, only WebSocket rules will match. - - .. versionchanged:: 1.0 - The ``merge_slashes`` parameter was added. - - .. versionchanged:: 0.7 - The ``encoding_errors`` and ``host_matching`` parameters were added. - - .. versionchanged:: 0.5 - The ``sort_parameters`` and ``sort_key`` paramters were added. - """ - - #: A dict of default converters to be used. - default_converters = ImmutableDict(DEFAULT_CONVERTERS) - - #: The type of lock to use when updating. - #: - #: .. versionadded:: 1.0 - lock_class = Lock - - def __init__( - self, - rules: t.Iterable[RuleFactory] | None = None, - default_subdomain: str = "", - strict_slashes: bool = True, - merge_slashes: bool = True, - redirect_defaults: bool = True, - converters: t.Mapping[str, type[BaseConverter]] | None = None, - sort_parameters: bool = False, - sort_key: t.Callable[[t.Any], t.Any] | None = None, - host_matching: bool = False, - ) -> None: - self._matcher = StateMachineMatcher(merge_slashes) - self._rules_by_endpoint: dict[t.Any, list[Rule]] = {} - self._remap = True - self._remap_lock = self.lock_class() - - self.default_subdomain = default_subdomain - self.strict_slashes = strict_slashes - self.redirect_defaults = redirect_defaults - self.host_matching = host_matching - - self.converters = self.default_converters.copy() - if converters: - self.converters.update(converters) - - self.sort_parameters = sort_parameters - self.sort_key = sort_key - - for rulefactory in rules or (): - self.add(rulefactory) - - @property - def merge_slashes(self) -> bool: - return self._matcher.merge_slashes - - @merge_slashes.setter - def merge_slashes(self, value: bool) -> None: - self._matcher.merge_slashes = value - - def is_endpoint_expecting(self, endpoint: t.Any, *arguments: str) -> bool: - """Iterate over all rules and check if the endpoint expects - the arguments provided. This is for example useful if you have - some URLs that expect a language code and others that do not and - you want to wrap the builder a bit so that the current language - code is automatically added if not provided but endpoints expect - it. - - :param endpoint: the endpoint to check. - :param arguments: this function accepts one or more arguments - as positional arguments. Each one of them is - checked. - """ - self.update() - arguments_set = set(arguments) - for rule in self._rules_by_endpoint[endpoint]: - if arguments_set.issubset(rule.arguments): - return True - return False - - @property - def _rules(self) -> list[Rule]: - return [rule for rules in self._rules_by_endpoint.values() for rule in rules] - - def iter_rules(self, endpoint: t.Any | None = None) -> t.Iterator[Rule]: - """Iterate over all rules or the rules of an endpoint. - - :param endpoint: if provided only the rules for that endpoint - are returned. - :return: an iterator - """ - self.update() - if endpoint is not None: - return iter(self._rules_by_endpoint[endpoint]) - return iter(self._rules) - - def add(self, rulefactory: RuleFactory) -> None: - """Add a new rule or factory to the map and bind it. Requires that the - rule is not bound to another map. - - :param rulefactory: a :class:`Rule` or :class:`RuleFactory` - """ - for rule in rulefactory.get_rules(self): - rule.bind(self) - if not rule.build_only: - self._matcher.add(rule) - self._rules_by_endpoint.setdefault(rule.endpoint, []).append(rule) - self._remap = True - - def bind( - self, - server_name: str, - script_name: str | None = None, - subdomain: str | None = None, - url_scheme: str = "http", - default_method: str = "GET", - path_info: str | None = None, - query_args: t.Mapping[str, t.Any] | str | None = None, - ) -> MapAdapter: - """Return a new :class:`MapAdapter` with the details specified to the - call. Note that `script_name` will default to ``'/'`` if not further - specified or `None`. The `server_name` at least is a requirement - because the HTTP RFC requires absolute URLs for redirects and so all - redirect exceptions raised by Werkzeug will contain the full canonical - URL. - - If no path_info is passed to :meth:`match` it will use the default path - info passed to bind. While this doesn't really make sense for - manual bind calls, it's useful if you bind a map to a WSGI - environment which already contains the path info. - - `subdomain` will default to the `default_subdomain` for this map if - no defined. If there is no `default_subdomain` you cannot use the - subdomain feature. - - .. versionchanged:: 1.0 - If ``url_scheme`` is ``ws`` or ``wss``, only WebSocket rules - will match. - - .. versionchanged:: 0.15 - ``path_info`` defaults to ``'/'`` if ``None``. - - .. versionchanged:: 0.8 - ``query_args`` can be a string. - - .. versionchanged:: 0.7 - Added ``query_args``. - """ - server_name = server_name.lower() - if self.host_matching: - if subdomain is not None: - raise RuntimeError("host matching enabled and a subdomain was provided") - elif subdomain is None: - subdomain = self.default_subdomain - if script_name is None: - script_name = "/" - if path_info is None: - path_info = "/" - - # Port isn't part of IDNA, and might push a name over the 63 octet limit. - server_name, port_sep, port = server_name.partition(":") - - try: - server_name = server_name.encode("idna").decode("ascii") - except UnicodeError as e: - raise BadHost() from e - - return MapAdapter( - self, - f"{server_name}{port_sep}{port}", - script_name, - subdomain, - url_scheme, - path_info, - default_method, - query_args, - ) - - def bind_to_environ( - self, - environ: WSGIEnvironment | Request, - server_name: str | None = None, - subdomain: str | None = None, - ) -> MapAdapter: - """Like :meth:`bind` but you can pass it an WSGI environment and it - will fetch the information from that dictionary. Note that because of - limitations in the protocol there is no way to get the current - subdomain and real `server_name` from the environment. If you don't - provide it, Werkzeug will use `SERVER_NAME` and `SERVER_PORT` (or - `HTTP_HOST` if provided) as used `server_name` with disabled subdomain - feature. - - If `subdomain` is `None` but an environment and a server name is - provided it will calculate the current subdomain automatically. - Example: `server_name` is ``'example.com'`` and the `SERVER_NAME` - in the wsgi `environ` is ``'staging.dev.example.com'`` the calculated - subdomain will be ``'staging.dev'``. - - If the object passed as environ has an environ attribute, the value of - this attribute is used instead. This allows you to pass request - objects. Additionally `PATH_INFO` added as a default of the - :class:`MapAdapter` so that you don't have to pass the path info to - the match method. - - .. versionchanged:: 1.0.0 - If the passed server name specifies port 443, it will match - if the incoming scheme is ``https`` without a port. - - .. versionchanged:: 1.0.0 - A warning is shown when the passed server name does not - match the incoming WSGI server name. - - .. versionchanged:: 0.8 - This will no longer raise a ValueError when an unexpected server - name was passed. - - .. versionchanged:: 0.5 - previously this method accepted a bogus `calculate_subdomain` - parameter that did not have any effect. It was removed because - of that. - - :param environ: a WSGI environment. - :param server_name: an optional server name hint (see above). - :param subdomain: optionally the current subdomain (see above). - """ - env = _get_environ(environ) - wsgi_server_name = get_host(env).lower() - scheme = env["wsgi.url_scheme"] - upgrade = any( - v.strip() == "upgrade" - for v in env.get("HTTP_CONNECTION", "").lower().split(",") - ) - - if upgrade and env.get("HTTP_UPGRADE", "").lower() == "websocket": - scheme = "wss" if scheme == "https" else "ws" - - if server_name is None: - server_name = wsgi_server_name - else: - server_name = server_name.lower() - - # strip standard port to match get_host() - if scheme in {"http", "ws"} and server_name.endswith(":80"): - server_name = server_name[:-3] - elif scheme in {"https", "wss"} and server_name.endswith(":443"): - server_name = server_name[:-4] - - if subdomain is None and not self.host_matching: - cur_server_name = wsgi_server_name.split(".") - real_server_name = server_name.split(".") - offset = -len(real_server_name) - - if cur_server_name[offset:] != real_server_name: - # This can happen even with valid configs if the server was - # accessed directly by IP address under some situations. - # Instead of raising an exception like in Werkzeug 0.7 or - # earlier we go by an invalid subdomain which will result - # in a 404 error on matching. - warnings.warn( - f"Current server name {wsgi_server_name!r} doesn't match configured" - f" server name {server_name!r}", - stacklevel=2, - ) - subdomain = "" - else: - subdomain = ".".join(filter(None, cur_server_name[:offset])) - - def _get_wsgi_string(name: str) -> str | None: - val = env.get(name) - if val is not None: - return _wsgi_decoding_dance(val) - return None - - script_name = _get_wsgi_string("SCRIPT_NAME") - path_info = _get_wsgi_string("PATH_INFO") - query_args = _get_wsgi_string("QUERY_STRING") - return Map.bind( - self, - server_name, - script_name, - subdomain, - scheme, - env["REQUEST_METHOD"], - path_info, - query_args=query_args, - ) - - def update(self) -> None: - """Called before matching and building to keep the compiled rules - in the correct order after things changed. - """ - if not self._remap: - return - - with self._remap_lock: - if not self._remap: - return - - self._matcher.update() - for rules in self._rules_by_endpoint.values(): - rules.sort(key=lambda x: x.build_compare_key()) - self._remap = False - - def __repr__(self) -> str: - rules = self.iter_rules() - return f"{type(self).__name__}({pformat(list(rules))})" - - -class MapAdapter: - """Returned by :meth:`Map.bind` or :meth:`Map.bind_to_environ` and does - the URL matching and building based on runtime information. - """ - - def __init__( - self, - map: Map, - server_name: str, - script_name: str, - subdomain: str | None, - url_scheme: str, - path_info: str, - default_method: str, - query_args: t.Mapping[str, t.Any] | str | None = None, - ): - self.map = map - self.server_name = server_name - - if not script_name.endswith("/"): - script_name += "/" - - self.script_name = script_name - self.subdomain = subdomain - self.url_scheme = url_scheme - self.path_info = path_info - self.default_method = default_method - self.query_args = query_args - self.websocket = self.url_scheme in {"ws", "wss"} - - def dispatch( - self, - view_func: t.Callable[[str, t.Mapping[str, t.Any]], WSGIApplication], - path_info: str | None = None, - method: str | None = None, - catch_http_exceptions: bool = False, - ) -> WSGIApplication: - """Does the complete dispatching process. `view_func` is called with - the endpoint and a dict with the values for the view. It should - look up the view function, call it, and return a response object - or WSGI application. http exceptions are not caught by default - so that applications can display nicer error messages by just - catching them by hand. If you want to stick with the default - error messages you can pass it ``catch_http_exceptions=True`` and - it will catch the http exceptions. - - Here a small example for the dispatch usage:: - - from werkzeug.wrappers import Request, Response - from werkzeug.wsgi import responder - from werkzeug.routing import Map, Rule - - def on_index(request): - return Response('Hello from the index') - - url_map = Map([Rule('/', endpoint='index')]) - views = {'index': on_index} - - @responder - def application(environ, start_response): - request = Request(environ) - urls = url_map.bind_to_environ(environ) - return urls.dispatch(lambda e, v: views[e](request, **v), - catch_http_exceptions=True) - - Keep in mind that this method might return exception objects, too, so - use :class:`Response.force_type` to get a response object. - - :param view_func: a function that is called with the endpoint as - first argument and the value dict as second. Has - to dispatch to the actual view function with this - information. (see above) - :param path_info: the path info to use for matching. Overrides the - path info specified on binding. - :param method: the HTTP method used for matching. Overrides the - method specified on binding. - :param catch_http_exceptions: set to `True` to catch any of the - werkzeug :class:`HTTPException`\\s. - """ - try: - try: - endpoint, args = self.match(path_info, method) - except RequestRedirect as e: - return e - return view_func(endpoint, args) - except HTTPException as e: - if catch_http_exceptions: - return e - raise - - @t.overload - def match( - self, - path_info: str | None = None, - method: str | None = None, - return_rule: t.Literal[False] = False, - query_args: t.Mapping[str, t.Any] | str | None = None, - websocket: bool | None = None, - ) -> tuple[t.Any, t.Mapping[str, t.Any]]: ... - - @t.overload - def match( - self, - path_info: str | None = None, - method: str | None = None, - return_rule: t.Literal[True] = True, - query_args: t.Mapping[str, t.Any] | str | None = None, - websocket: bool | None = None, - ) -> tuple[Rule, t.Mapping[str, t.Any]]: ... - - def match( - self, - path_info: str | None = None, - method: str | None = None, - return_rule: bool = False, - query_args: t.Mapping[str, t.Any] | str | None = None, - websocket: bool | None = None, - ) -> tuple[t.Any | Rule, t.Mapping[str, t.Any]]: - """The usage is simple: you just pass the match method the current - path info as well as the method (which defaults to `GET`). The - following things can then happen: - - - you receive a `NotFound` exception that indicates that no URL is - matching. A `NotFound` exception is also a WSGI application you - can call to get a default page not found page (happens to be the - same object as `werkzeug.exceptions.NotFound`) - - - you receive a `MethodNotAllowed` exception that indicates that there - is a match for this URL but not for the current request method. - This is useful for RESTful applications. - - - you receive a `RequestRedirect` exception with a `new_url` - attribute. This exception is used to notify you about a request - Werkzeug requests from your WSGI application. This is for example the - case if you request ``/foo`` although the correct URL is ``/foo/`` - You can use the `RequestRedirect` instance as response-like object - similar to all other subclasses of `HTTPException`. - - - you receive a ``WebsocketMismatch`` exception if the only - match is a WebSocket rule but the bind is an HTTP request, or - if the match is an HTTP rule but the bind is a WebSocket - request. - - - you get a tuple in the form ``(endpoint, arguments)`` if there is - a match (unless `return_rule` is True, in which case you get a tuple - in the form ``(rule, arguments)``) - - If the path info is not passed to the match method the default path - info of the map is used (defaults to the root URL if not defined - explicitly). - - All of the exceptions raised are subclasses of `HTTPException` so they - can be used as WSGI responses. They will all render generic error or - redirect pages. - - Here is a small example for matching: - - >>> m = Map([ - ... Rule('/', endpoint='index'), - ... Rule('/downloads/', endpoint='downloads/index'), - ... Rule('/downloads/', endpoint='downloads/show') - ... ]) - >>> urls = m.bind("example.com", "/") - >>> urls.match("/", "GET") - ('index', {}) - >>> urls.match("/downloads/42") - ('downloads/show', {'id': 42}) - - And here is what happens on redirect and missing URLs: - - >>> urls.match("/downloads") - Traceback (most recent call last): - ... - RequestRedirect: http://example.com/downloads/ - >>> urls.match("/missing") - Traceback (most recent call last): - ... - NotFound: 404 Not Found - - :param path_info: the path info to use for matching. Overrides the - path info specified on binding. - :param method: the HTTP method used for matching. Overrides the - method specified on binding. - :param return_rule: return the rule that matched instead of just the - endpoint (defaults to `False`). - :param query_args: optional query arguments that are used for - automatic redirects as string or dictionary. It's - currently not possible to use the query arguments - for URL matching. - :param websocket: Match WebSocket instead of HTTP requests. A - websocket request has a ``ws`` or ``wss`` - :attr:`url_scheme`. This overrides that detection. - - .. versionadded:: 1.0 - Added ``websocket``. - - .. versionchanged:: 0.8 - ``query_args`` can be a string. - - .. versionadded:: 0.7 - Added ``query_args``. - - .. versionadded:: 0.6 - Added ``return_rule``. - """ - self.map.update() - if path_info is None: - path_info = self.path_info - if query_args is None: - query_args = self.query_args or {} - method = (method or self.default_method).upper() - - if websocket is None: - websocket = self.websocket - - domain_part = self.server_name - - if not self.map.host_matching and self.subdomain is not None: - domain_part = self.subdomain - - path_part = f"/{path_info.lstrip('/')}" if path_info else "" - - try: - result = self.map._matcher.match(domain_part, path_part, method, websocket) - except RequestPath as e: - # safe = https://url.spec.whatwg.org/#url-path-segment-string - new_path = quote(e.path_info, safe="!$&'()*+,/:;=@") - raise RequestRedirect( - self.make_redirect_url(new_path, query_args) - ) from None - except RequestAliasRedirect as e: - raise RequestRedirect( - self.make_alias_redirect_url( - f"{domain_part}|{path_part}", - e.endpoint, - e.matched_values, - method, - query_args, - ) - ) from None - except NoMatch as e: - if e.have_match_for: - raise MethodNotAllowed(valid_methods=list(e.have_match_for)) from None - - if e.websocket_mismatch: - raise WebsocketMismatch() from None - - raise NotFound() from None - else: - rule, rv = result - - if self.map.redirect_defaults: - redirect_url = self.get_default_redirect(rule, method, rv, query_args) - if redirect_url is not None: - raise RequestRedirect(redirect_url) - - if rule.redirect_to is not None: - if isinstance(rule.redirect_to, str): - - def _handle_match(match: t.Match[str]) -> str: - value = rv[match.group(1)] - return rule._converters[match.group(1)].to_url(value) - - redirect_url = _simple_rule_re.sub(_handle_match, rule.redirect_to) - else: - redirect_url = rule.redirect_to(self, **rv) - - if self.subdomain: - netloc = f"{self.subdomain}.{self.server_name}" - else: - netloc = self.server_name - - raise RequestRedirect( - urljoin( - f"{self.url_scheme or 'http'}://{netloc}{self.script_name}", - redirect_url, - ) - ) - - if return_rule: - return rule, rv - else: - return rule.endpoint, rv - - def test(self, path_info: str | None = None, method: str | None = None) -> bool: - """Test if a rule would match. Works like `match` but returns `True` - if the URL matches, or `False` if it does not exist. - - :param path_info: the path info to use for matching. Overrides the - path info specified on binding. - :param method: the HTTP method used for matching. Overrides the - method specified on binding. - """ - try: - self.match(path_info, method) - except RequestRedirect: - pass - except HTTPException: - return False - return True - - def allowed_methods(self, path_info: str | None = None) -> t.Iterable[str]: - """Returns the valid methods that match for a given path. - - .. versionadded:: 0.7 - """ - try: - self.match(path_info, method="--") - except MethodNotAllowed as e: - return e.valid_methods # type: ignore - except HTTPException: - pass - return [] - - def get_host(self, domain_part: str | None) -> str: - """Figures out the full host name for the given domain part. The - domain part is a subdomain in case host matching is disabled or - a full host name. - """ - if self.map.host_matching: - if domain_part is None: - return self.server_name - - return domain_part - - if domain_part is None: - subdomain = self.subdomain - else: - subdomain = domain_part - - if subdomain: - return f"{subdomain}.{self.server_name}" - else: - return self.server_name - - def get_default_redirect( - self, - rule: Rule, - method: str, - values: t.MutableMapping[str, t.Any], - query_args: t.Mapping[str, t.Any] | str, - ) -> str | None: - """A helper that returns the URL to redirect to if it finds one. - This is used for default redirecting only. - - :internal: - """ - assert self.map.redirect_defaults - for r in self.map._rules_by_endpoint[rule.endpoint]: - # every rule that comes after this one, including ourself - # has a lower priority for the defaults. We order the ones - # with the highest priority up for building. - if r is rule: - break - if r.provides_defaults_for(rule) and r.suitable_for(values, method): - values.update(r.defaults) # type: ignore - domain_part, path = r.build(values) # type: ignore - return self.make_redirect_url(path, query_args, domain_part=domain_part) - return None - - def encode_query_args(self, query_args: t.Mapping[str, t.Any] | str) -> str: - if not isinstance(query_args, str): - return _urlencode(query_args) - return query_args - - def make_redirect_url( - self, - path_info: str, - query_args: t.Mapping[str, t.Any] | str | None = None, - domain_part: str | None = None, - ) -> str: - """Creates a redirect URL. - - :internal: - """ - if query_args is None: - query_args = self.query_args - - if query_args: - query_str = self.encode_query_args(query_args) - else: - query_str = None - - scheme = self.url_scheme or "http" - host = self.get_host(domain_part) - path = "/".join((self.script_name.strip("/"), path_info.lstrip("/"))) - return urlunsplit((scheme, host, path, query_str, None)) - - def make_alias_redirect_url( - self, - path: str, - endpoint: t.Any, - values: t.Mapping[str, t.Any], - method: str, - query_args: t.Mapping[str, t.Any] | str, - ) -> str: - """Internally called to make an alias redirect URL.""" - url = self.build( - endpoint, values, method, append_unknown=False, force_external=True - ) - if query_args: - url += f"?{self.encode_query_args(query_args)}" - assert url != path, "detected invalid alias setting. No canonical URL found" - return url - - def _partial_build( - self, - endpoint: t.Any, - values: t.Mapping[str, t.Any], - method: str | None, - append_unknown: bool, - ) -> tuple[str, str, bool] | None: - """Helper for :meth:`build`. Returns subdomain and path for the - rule that accepts this endpoint, values and method. - - :internal: - """ - # in case the method is none, try with the default method first - if method is None: - rv = self._partial_build( - endpoint, values, self.default_method, append_unknown - ) - if rv is not None: - return rv - - # Default method did not match or a specific method is passed. - # Check all for first match with matching host. If no matching - # host is found, go with first result. - first_match = None - - for rule in self.map._rules_by_endpoint.get(endpoint, ()): - if rule.suitable_for(values, method): - build_rv = rule.build(values, append_unknown) - - if build_rv is not None: - rv = (build_rv[0], build_rv[1], rule.websocket) - if self.map.host_matching: - if rv[0] == self.server_name: - return rv - elif first_match is None: - first_match = rv - else: - return rv - - return first_match - - def build( - self, - endpoint: t.Any, - values: t.Mapping[str, t.Any] | None = None, - method: str | None = None, - force_external: bool = False, - append_unknown: bool = True, - url_scheme: str | None = None, - ) -> str: - """Building URLs works pretty much the other way round. Instead of - `match` you call `build` and pass it the endpoint and a dict of - arguments for the placeholders. - - The `build` function also accepts an argument called `force_external` - which, if you set it to `True` will force external URLs. Per default - external URLs (include the server name) will only be used if the - target URL is on a different subdomain. - - >>> m = Map([ - ... Rule('/', endpoint='index'), - ... Rule('/downloads/', endpoint='downloads/index'), - ... Rule('/downloads/', endpoint='downloads/show') - ... ]) - >>> urls = m.bind("example.com", "/") - >>> urls.build("index", {}) - '/' - >>> urls.build("downloads/show", {'id': 42}) - '/downloads/42' - >>> urls.build("downloads/show", {'id': 42}, force_external=True) - 'http://example.com/downloads/42' - - Because URLs cannot contain non ASCII data you will always get - bytes back. Non ASCII characters are urlencoded with the - charset defined on the map instance. - - Additional values are converted to strings and appended to the URL as - URL querystring parameters: - - >>> urls.build("index", {'q': 'My Searchstring'}) - '/?q=My+Searchstring' - - When processing those additional values, lists are furthermore - interpreted as multiple values (as per - :py:class:`werkzeug.datastructures.MultiDict`): - - >>> urls.build("index", {'q': ['a', 'b', 'c']}) - '/?q=a&q=b&q=c' - - Passing a ``MultiDict`` will also add multiple values: - - >>> urls.build("index", MultiDict((('p', 'z'), ('q', 'a'), ('q', 'b')))) - '/?p=z&q=a&q=b' - - If a rule does not exist when building a `BuildError` exception is - raised. - - The build method accepts an argument called `method` which allows you - to specify the method you want to have an URL built for if you have - different methods for the same endpoint specified. - - :param endpoint: the endpoint of the URL to build. - :param values: the values for the URL to build. Unhandled values are - appended to the URL as query parameters. - :param method: the HTTP method for the rule if there are different - URLs for different methods on the same endpoint. - :param force_external: enforce full canonical external URLs. If the URL - scheme is not provided, this will generate - a protocol-relative URL. - :param append_unknown: unknown parameters are appended to the generated - URL as query string argument. Disable this - if you want the builder to ignore those. - :param url_scheme: Scheme to use in place of the bound - :attr:`url_scheme`. - - .. versionchanged:: 2.0 - Added the ``url_scheme`` parameter. - - .. versionadded:: 0.6 - Added the ``append_unknown`` parameter. - """ - self.map.update() - - if values: - if isinstance(values, MultiDict): - values = { - k: (v[0] if len(v) == 1 else v) - for k, v in dict.items(values) - if len(v) != 0 - } - else: # plain dict - values = {k: v for k, v in values.items() if v is not None} - else: - values = {} - - rv = self._partial_build(endpoint, values, method, append_unknown) - if rv is None: - raise BuildError(endpoint, values, method, self) - - domain_part, path, websocket = rv - host = self.get_host(domain_part) - - if url_scheme is None: - url_scheme = self.url_scheme - - # Always build WebSocket routes with the scheme (browsers - # require full URLs). If bound to a WebSocket, ensure that HTTP - # routes are built with an HTTP scheme. - secure = url_scheme in {"https", "wss"} - - if websocket: - force_external = True - url_scheme = "wss" if secure else "ws" - elif url_scheme: - url_scheme = "https" if secure else "http" - - # shortcut this. - if not force_external and ( - (self.map.host_matching and host == self.server_name) - or (not self.map.host_matching and domain_part == self.subdomain) - ): - return f"{self.script_name.rstrip('/')}/{path.lstrip('/')}" - - scheme = f"{url_scheme}:" if url_scheme else "" - return f"{scheme}//{host}{self.script_name[:-1]}/{path.lstrip('/')}" diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/matcher.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/matcher.py deleted file mode 100644 index 1fd00ef..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/matcher.py +++ /dev/null @@ -1,202 +0,0 @@ -from __future__ import annotations - -import re -import typing as t -from dataclasses import dataclass -from dataclasses import field - -from .converters import ValidationError -from .exceptions import NoMatch -from .exceptions import RequestAliasRedirect -from .exceptions import RequestPath -from .rules import Rule -from .rules import RulePart - - -class SlashRequired(Exception): - pass - - -@dataclass -class State: - """A representation of a rule state. - - This includes the *rules* that correspond to the state and the - possible *static* and *dynamic* transitions to the next state. - """ - - dynamic: list[tuple[RulePart, State]] = field(default_factory=list) - rules: list[Rule] = field(default_factory=list) - static: dict[str, State] = field(default_factory=dict) - - -class StateMachineMatcher: - def __init__(self, merge_slashes: bool) -> None: - self._root = State() - self.merge_slashes = merge_slashes - - def add(self, rule: Rule) -> None: - state = self._root - for part in rule._parts: - if part.static: - state.static.setdefault(part.content, State()) - state = state.static[part.content] - else: - for test_part, new_state in state.dynamic: - if test_part == part: - state = new_state - break - else: - new_state = State() - state.dynamic.append((part, new_state)) - state = new_state - state.rules.append(rule) - - def update(self) -> None: - # For every state the dynamic transitions should be sorted by - # the weight of the transition - state = self._root - - def _update_state(state: State) -> None: - state.dynamic.sort(key=lambda entry: entry[0].weight) - for new_state in state.static.values(): - _update_state(new_state) - for _, new_state in state.dynamic: - _update_state(new_state) - - _update_state(state) - - def match( - self, domain: str, path: str, method: str, websocket: bool - ) -> tuple[Rule, t.MutableMapping[str, t.Any]]: - # To match to a rule we need to start at the root state and - # try to follow the transitions until we find a match, or find - # there is no transition to follow. - - have_match_for = set() - websocket_mismatch = False - - def _match( - state: State, parts: list[str], values: list[str] - ) -> tuple[Rule, list[str]] | None: - # This function is meant to be called recursively, and will attempt - # to match the head part to the state's transitions. - nonlocal have_match_for, websocket_mismatch - - # The base case is when all parts have been matched via - # transitions. Hence if there is a rule with methods & - # websocket that work return it and the dynamic values - # extracted. - if parts == []: - for rule in state.rules: - if rule.methods is not None and method not in rule.methods: - have_match_for.update(rule.methods) - elif rule.websocket != websocket: - websocket_mismatch = True - else: - return rule, values - - # Test if there is a match with this path with a - # trailing slash, if so raise an exception to report - # that matching is possible with an additional slash - if "" in state.static: - for rule in state.static[""].rules: - if websocket == rule.websocket and ( - rule.methods is None or method in rule.methods - ): - if rule.strict_slashes: - raise SlashRequired() - else: - return rule, values - return None - - part = parts[0] - # To match this part try the static transitions first - if part in state.static: - rv = _match(state.static[part], parts[1:], values) - if rv is not None: - return rv - # No match via the static transitions, so try the dynamic - # ones. - for test_part, new_state in state.dynamic: - target = part - remaining = parts[1:] - # A final part indicates a transition that always - # consumes the remaining parts i.e. transitions to a - # final state. - if test_part.final: - target = "/".join(parts) - remaining = [] - match = re.compile(test_part.content).match(target) - if match is not None: - if test_part.suffixed: - # If a part_isolating=False part has a slash suffix, remove the - # suffix from the match and check for the slash redirect next. - suffix = match.groups()[-1] - if suffix == "/": - remaining = [""] - - converter_groups = sorted( - match.groupdict().items(), key=lambda entry: entry[0] - ) - groups = [ - value - for key, value in converter_groups - if key[:11] == "__werkzeug_" - ] - rv = _match(new_state, remaining, values + groups) - if rv is not None: - return rv - - # If there is no match and the only part left is a - # trailing slash ("") consider rules that aren't - # strict-slashes as these should match if there is a final - # slash part. - if parts == [""]: - for rule in state.rules: - if rule.strict_slashes: - continue - if rule.methods is not None and method not in rule.methods: - have_match_for.update(rule.methods) - elif rule.websocket != websocket: - websocket_mismatch = True - else: - return rule, values - - return None - - try: - rv = _match(self._root, [domain, *path.split("/")], []) - except SlashRequired: - raise RequestPath(f"{path}/") from None - - if self.merge_slashes and rv is None: - # Try to match again, but with slashes merged - path = re.sub("/{2,}?", "/", path) - try: - rv = _match(self._root, [domain, *path.split("/")], []) - except SlashRequired: - raise RequestPath(f"{path}/") from None - if rv is None or rv[0].merge_slashes is False: - raise NoMatch(have_match_for, websocket_mismatch) - else: - raise RequestPath(f"{path}") - elif rv is not None: - rule, values = rv - - result = {} - for name, value in zip(rule._converters.keys(), values): - try: - value = rule._converters[name].to_python(value) - except ValidationError: - raise NoMatch(have_match_for, websocket_mismatch) from None - result[str(name)] = value - if rule.defaults: - result.update(rule.defaults) - - if rule.alias and rule.map.redirect_defaults: - raise RequestAliasRedirect(result, rule.endpoint) - - return rule, result - - raise NoMatch(have_match_for, websocket_mismatch) diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/rules.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/rules.py deleted file mode 100644 index 2dad31d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/routing/rules.py +++ /dev/null @@ -1,928 +0,0 @@ -from __future__ import annotations - -import ast -import re -import typing as t -from dataclasses import dataclass -from string import Template -from types import CodeType -from urllib.parse import quote - -from ..datastructures import iter_multi_items -from ..urls import _urlencode -from .converters import ValidationError - -if t.TYPE_CHECKING: - from .converters import BaseConverter - from .map import Map - - -class Weighting(t.NamedTuple): - number_static_weights: int - static_weights: list[tuple[int, int]] - number_argument_weights: int - argument_weights: list[int] - - -@dataclass -class RulePart: - """A part of a rule. - - Rules can be represented by parts as delimited by `/` with - instances of this class representing those parts. The *content* is - either the raw content if *static* or a regex string to match - against. The *weight* can be used to order parts when matching. - - """ - - content: str - final: bool - static: bool - suffixed: bool - weight: Weighting - - -_part_re = re.compile( - r""" - (?: - (?P/) # a slash - | - (?P[^[a-zA-Z_][a-zA-Z0-9_]*) # converter name - (?:\((?P.*?)\))? # converter arguments - : # variable delimiter - )? - (?P[a-zA-Z_][a-zA-Z0-9_]*) # variable name - > - ) - ) - """, - re.VERBOSE, -) - -_simple_rule_re = re.compile(r"<([^>]+)>") -_converter_args_re = re.compile( - r""" - \s* - ((?P\w+)\s*=\s*)? - (?P - True|False| - \d+.\d+| - \d+.| - \d+| - [\w\d_.]+| - [urUR]?(?P"[^"]*?"|'[^']*') - )\s*, - """, - re.VERBOSE, -) - - -_PYTHON_CONSTANTS = {"None": None, "True": True, "False": False} - - -def _find(value: str, target: str, pos: int) -> int: - """Find the *target* in *value* after *pos*. - - Returns the *value* length if *target* isn't found. - """ - try: - return value.index(target, pos) - except ValueError: - return len(value) - - -def _pythonize(value: str) -> None | bool | int | float | str: - if value in _PYTHON_CONSTANTS: - return _PYTHON_CONSTANTS[value] - for convert in int, float: - try: - return convert(value) - except ValueError: - pass - if value[:1] == value[-1:] and value[0] in "\"'": - value = value[1:-1] - return str(value) - - -def parse_converter_args(argstr: str) -> tuple[tuple[t.Any, ...], dict[str, t.Any]]: - argstr += "," - args = [] - kwargs = {} - position = 0 - - for item in _converter_args_re.finditer(argstr): - if item.start() != position: - raise ValueError( - f"Cannot parse converter argument '{argstr[position:item.start()]}'" - ) - - value = item.group("stringval") - if value is None: - value = item.group("value") - value = _pythonize(value) - if not item.group("name"): - args.append(value) - else: - name = item.group("name") - kwargs[name] = value - position = item.end() - - return tuple(args), kwargs - - -class RuleFactory: - """As soon as you have more complex URL setups it's a good idea to use rule - factories to avoid repetitive tasks. Some of them are builtin, others can - be added by subclassing `RuleFactory` and overriding `get_rules`. - """ - - def get_rules(self, map: Map) -> t.Iterable[Rule]: - """Subclasses of `RuleFactory` have to override this method and return - an iterable of rules.""" - raise NotImplementedError() - - -class Subdomain(RuleFactory): - """All URLs provided by this factory have the subdomain set to a - specific domain. For example if you want to use the subdomain for - the current language this can be a good setup:: - - url_map = Map([ - Rule('/', endpoint='#select_language'), - Subdomain('', [ - Rule('/', endpoint='index'), - Rule('/about', endpoint='about'), - Rule('/help', endpoint='help') - ]) - ]) - - All the rules except for the ``'#select_language'`` endpoint will now - listen on a two letter long subdomain that holds the language code - for the current request. - """ - - def __init__(self, subdomain: str, rules: t.Iterable[RuleFactory]) -> None: - self.subdomain = subdomain - self.rules = rules - - def get_rules(self, map: Map) -> t.Iterator[Rule]: - for rulefactory in self.rules: - for rule in rulefactory.get_rules(map): - rule = rule.empty() - rule.subdomain = self.subdomain - yield rule - - -class Submount(RuleFactory): - """Like `Subdomain` but prefixes the URL rule with a given string:: - - url_map = Map([ - Rule('/', endpoint='index'), - Submount('/blog', [ - Rule('/', endpoint='blog/index'), - Rule('/entry/', endpoint='blog/show') - ]) - ]) - - Now the rule ``'blog/show'`` matches ``/blog/entry/``. - """ - - def __init__(self, path: str, rules: t.Iterable[RuleFactory]) -> None: - self.path = path.rstrip("/") - self.rules = rules - - def get_rules(self, map: Map) -> t.Iterator[Rule]: - for rulefactory in self.rules: - for rule in rulefactory.get_rules(map): - rule = rule.empty() - rule.rule = self.path + rule.rule - yield rule - - -class EndpointPrefix(RuleFactory): - """Prefixes all endpoints (which must be strings for this factory) with - another string. This can be useful for sub applications:: - - url_map = Map([ - Rule('/', endpoint='index'), - EndpointPrefix('blog/', [Submount('/blog', [ - Rule('/', endpoint='index'), - Rule('/entry/', endpoint='show') - ])]) - ]) - """ - - def __init__(self, prefix: str, rules: t.Iterable[RuleFactory]) -> None: - self.prefix = prefix - self.rules = rules - - def get_rules(self, map: Map) -> t.Iterator[Rule]: - for rulefactory in self.rules: - for rule in rulefactory.get_rules(map): - rule = rule.empty() - rule.endpoint = self.prefix + rule.endpoint - yield rule - - -class RuleTemplate: - """Returns copies of the rules wrapped and expands string templates in - the endpoint, rule, defaults or subdomain sections. - - Here a small example for such a rule template:: - - from werkzeug.routing import Map, Rule, RuleTemplate - - resource = RuleTemplate([ - Rule('/$name/', endpoint='$name.list'), - Rule('/$name/', endpoint='$name.show') - ]) - - url_map = Map([resource(name='user'), resource(name='page')]) - - When a rule template is called the keyword arguments are used to - replace the placeholders in all the string parameters. - """ - - def __init__(self, rules: t.Iterable[Rule]) -> None: - self.rules = list(rules) - - def __call__(self, *args: t.Any, **kwargs: t.Any) -> RuleTemplateFactory: - return RuleTemplateFactory(self.rules, dict(*args, **kwargs)) - - -class RuleTemplateFactory(RuleFactory): - """A factory that fills in template variables into rules. Used by - `RuleTemplate` internally. - - :internal: - """ - - def __init__( - self, rules: t.Iterable[RuleFactory], context: dict[str, t.Any] - ) -> None: - self.rules = rules - self.context = context - - def get_rules(self, map: Map) -> t.Iterator[Rule]: - for rulefactory in self.rules: - for rule in rulefactory.get_rules(map): - new_defaults = subdomain = None - if rule.defaults: - new_defaults = {} - for key, value in rule.defaults.items(): - if isinstance(value, str): - value = Template(value).substitute(self.context) - new_defaults[key] = value - if rule.subdomain is not None: - subdomain = Template(rule.subdomain).substitute(self.context) - new_endpoint = rule.endpoint - if isinstance(new_endpoint, str): - new_endpoint = Template(new_endpoint).substitute(self.context) - yield Rule( - Template(rule.rule).substitute(self.context), - new_defaults, - subdomain, - rule.methods, - rule.build_only, - new_endpoint, - rule.strict_slashes, - ) - - -_ASTT = t.TypeVar("_ASTT", bound=ast.AST) - - -def _prefix_names(src: str, expected_type: type[_ASTT]) -> _ASTT: - """ast parse and prefix names with `.` to avoid collision with user vars""" - tree: ast.AST = ast.parse(src).body[0] - if isinstance(tree, ast.Expr): - tree = tree.value - if not isinstance(tree, expected_type): - raise TypeError( - f"AST node is of type {type(tree).__name__}, not {expected_type.__name__}" - ) - for node in ast.walk(tree): - if isinstance(node, ast.Name): - node.id = f".{node.id}" - return tree - - -_CALL_CONVERTER_CODE_FMT = "self._converters[{elem!r}].to_url()" -_IF_KWARGS_URL_ENCODE_CODE = """\ -if kwargs: - params = self._encode_query_vars(kwargs) - q = "?" if params else "" -else: - q = params = "" -""" -_IF_KWARGS_URL_ENCODE_AST = _prefix_names(_IF_KWARGS_URL_ENCODE_CODE, ast.If) -_URL_ENCODE_AST_NAMES = ( - _prefix_names("q", ast.Name), - _prefix_names("params", ast.Name), -) - - -class Rule(RuleFactory): - """A Rule represents one URL pattern. There are some options for `Rule` - that change the way it behaves and are passed to the `Rule` constructor. - Note that besides the rule-string all arguments *must* be keyword arguments - in order to not break the application on Werkzeug upgrades. - - `string` - Rule strings basically are just normal URL paths with placeholders in - the format ```` where the converter and the - arguments are optional. If no converter is defined the `default` - converter is used which means `string` in the normal configuration. - - URL rules that end with a slash are branch URLs, others are leaves. - If you have `strict_slashes` enabled (which is the default), all - branch URLs that are matched without a trailing slash will trigger a - redirect to the same URL with the missing slash appended. - - The converters are defined on the `Map`. - - `endpoint` - The endpoint for this rule. This can be anything. A reference to a - function, a string, a number etc. The preferred way is using a string - because the endpoint is used for URL generation. - - `defaults` - An optional dict with defaults for other rules with the same endpoint. - This is a bit tricky but useful if you want to have unique URLs:: - - url_map = Map([ - Rule('/all/', defaults={'page': 1}, endpoint='all_entries'), - Rule('/all/page/', endpoint='all_entries') - ]) - - If a user now visits ``http://example.com/all/page/1`` they will be - redirected to ``http://example.com/all/``. If `redirect_defaults` is - disabled on the `Map` instance this will only affect the URL - generation. - - `subdomain` - The subdomain rule string for this rule. If not specified the rule - only matches for the `default_subdomain` of the map. If the map is - not bound to a subdomain this feature is disabled. - - Can be useful if you want to have user profiles on different subdomains - and all subdomains are forwarded to your application:: - - url_map = Map([ - Rule('/', subdomain='', endpoint='user/homepage'), - Rule('/stats', subdomain='', endpoint='user/stats') - ]) - - `methods` - A sequence of http methods this rule applies to. If not specified, all - methods are allowed. For example this can be useful if you want different - endpoints for `POST` and `GET`. If methods are defined and the path - matches but the method matched against is not in this list or in the - list of another rule for that path the error raised is of the type - `MethodNotAllowed` rather than `NotFound`. If `GET` is present in the - list of methods and `HEAD` is not, `HEAD` is added automatically. - - `strict_slashes` - Override the `Map` setting for `strict_slashes` only for this rule. If - not specified the `Map` setting is used. - - `merge_slashes` - Override :attr:`Map.merge_slashes` for this rule. - - `build_only` - Set this to True and the rule will never match but will create a URL - that can be build. This is useful if you have resources on a subdomain - or folder that are not handled by the WSGI application (like static data) - - `redirect_to` - If given this must be either a string or callable. In case of a - callable it's called with the url adapter that triggered the match and - the values of the URL as keyword arguments and has to return the target - for the redirect, otherwise it has to be a string with placeholders in - rule syntax:: - - def foo_with_slug(adapter, id): - # ask the database for the slug for the old id. this of - # course has nothing to do with werkzeug. - return f'foo/{Foo.get_slug_for_id(id)}' - - url_map = Map([ - Rule('/foo/', endpoint='foo'), - Rule('/some/old/url/', redirect_to='foo/'), - Rule('/other/old/url/', redirect_to=foo_with_slug) - ]) - - When the rule is matched the routing system will raise a - `RequestRedirect` exception with the target for the redirect. - - Keep in mind that the URL will be joined against the URL root of the - script so don't use a leading slash on the target URL unless you - really mean root of that domain. - - `alias` - If enabled this rule serves as an alias for another rule with the same - endpoint and arguments. - - `host` - If provided and the URL map has host matching enabled this can be - used to provide a match rule for the whole host. This also means - that the subdomain feature is disabled. - - `websocket` - If ``True``, this rule is only matches for WebSocket (``ws://``, - ``wss://``) requests. By default, rules will only match for HTTP - requests. - - .. versionchanged:: 2.1 - Percent-encoded newlines (``%0a``), which are decoded by WSGI - servers, are considered when routing instead of terminating the - match early. - - .. versionadded:: 1.0 - Added ``websocket``. - - .. versionadded:: 1.0 - Added ``merge_slashes``. - - .. versionadded:: 0.7 - Added ``alias`` and ``host``. - - .. versionchanged:: 0.6.1 - ``HEAD`` is added to ``methods`` if ``GET`` is present. - """ - - def __init__( - self, - string: str, - defaults: t.Mapping[str, t.Any] | None = None, - subdomain: str | None = None, - methods: t.Iterable[str] | None = None, - build_only: bool = False, - endpoint: t.Any | None = None, - strict_slashes: bool | None = None, - merge_slashes: bool | None = None, - redirect_to: str | t.Callable[..., str] | None = None, - alias: bool = False, - host: str | None = None, - websocket: bool = False, - ) -> None: - if not string.startswith("/"): - raise ValueError(f"URL rule '{string}' must start with a slash.") - - self.rule = string - self.is_leaf = not string.endswith("/") - self.is_branch = string.endswith("/") - - self.map: Map = None # type: ignore - self.strict_slashes = strict_slashes - self.merge_slashes = merge_slashes - self.subdomain = subdomain - self.host = host - self.defaults = defaults - self.build_only = build_only - self.alias = alias - self.websocket = websocket - - if methods is not None: - if isinstance(methods, str): - raise TypeError("'methods' should be a list of strings.") - - methods = {x.upper() for x in methods} - - if "HEAD" not in methods and "GET" in methods: - methods.add("HEAD") - - if websocket and methods - {"GET", "HEAD", "OPTIONS"}: - raise ValueError( - "WebSocket rules can only use 'GET', 'HEAD', and 'OPTIONS' methods." - ) - - self.methods = methods - self.endpoint: t.Any = endpoint - self.redirect_to = redirect_to - - if defaults: - self.arguments = set(map(str, defaults)) - else: - self.arguments = set() - - self._converters: dict[str, BaseConverter] = {} - self._trace: list[tuple[bool, str]] = [] - self._parts: list[RulePart] = [] - - def empty(self) -> Rule: - """ - Return an unbound copy of this rule. - - This can be useful if want to reuse an already bound URL for another - map. See ``get_empty_kwargs`` to override what keyword arguments are - provided to the new copy. - """ - return type(self)(self.rule, **self.get_empty_kwargs()) - - def get_empty_kwargs(self) -> t.Mapping[str, t.Any]: - """ - Provides kwargs for instantiating empty copy with empty() - - Use this method to provide custom keyword arguments to the subclass of - ``Rule`` when calling ``some_rule.empty()``. Helpful when the subclass - has custom keyword arguments that are needed at instantiation. - - Must return a ``dict`` that will be provided as kwargs to the new - instance of ``Rule``, following the initial ``self.rule`` value which - is always provided as the first, required positional argument. - """ - defaults = None - if self.defaults: - defaults = dict(self.defaults) - return dict( - defaults=defaults, - subdomain=self.subdomain, - methods=self.methods, - build_only=self.build_only, - endpoint=self.endpoint, - strict_slashes=self.strict_slashes, - redirect_to=self.redirect_to, - alias=self.alias, - host=self.host, - ) - - def get_rules(self, map: Map) -> t.Iterator[Rule]: - yield self - - def refresh(self) -> None: - """Rebinds and refreshes the URL. Call this if you modified the - rule in place. - - :internal: - """ - self.bind(self.map, rebind=True) - - def bind(self, map: Map, rebind: bool = False) -> None: - """Bind the url to a map and create a regular expression based on - the information from the rule itself and the defaults from the map. - - :internal: - """ - if self.map is not None and not rebind: - raise RuntimeError(f"url rule {self!r} already bound to map {self.map!r}") - self.map = map - if self.strict_slashes is None: - self.strict_slashes = map.strict_slashes - if self.merge_slashes is None: - self.merge_slashes = map.merge_slashes - if self.subdomain is None: - self.subdomain = map.default_subdomain - self.compile() - - def get_converter( - self, - variable_name: str, - converter_name: str, - args: tuple[t.Any, ...], - kwargs: t.Mapping[str, t.Any], - ) -> BaseConverter: - """Looks up the converter for the given parameter. - - .. versionadded:: 0.9 - """ - if converter_name not in self.map.converters: - raise LookupError(f"the converter {converter_name!r} does not exist") - return self.map.converters[converter_name](self.map, *args, **kwargs) - - def _encode_query_vars(self, query_vars: t.Mapping[str, t.Any]) -> str: - items: t.Iterable[tuple[str, str]] = iter_multi_items(query_vars) - - if self.map.sort_parameters: - items = sorted(items, key=self.map.sort_key) - - return _urlencode(items) - - def _parse_rule(self, rule: str) -> t.Iterable[RulePart]: - content = "" - static = True - argument_weights = [] - static_weights: list[tuple[int, int]] = [] - final = False - convertor_number = 0 - - pos = 0 - while pos < len(rule): - match = _part_re.match(rule, pos) - if match is None: - raise ValueError(f"malformed url rule: {rule!r}") - - data = match.groupdict() - if data["static"] is not None: - static_weights.append((len(static_weights), -len(data["static"]))) - self._trace.append((False, data["static"])) - content += data["static"] if static else re.escape(data["static"]) - - if data["variable"] is not None: - if static: - # Switching content to represent regex, hence the need to escape - content = re.escape(content) - static = False - c_args, c_kwargs = parse_converter_args(data["arguments"] or "") - convobj = self.get_converter( - data["variable"], data["converter"] or "default", c_args, c_kwargs - ) - self._converters[data["variable"]] = convobj - self.arguments.add(data["variable"]) - if not convobj.part_isolating: - final = True - content += f"(?P<__werkzeug_{convertor_number}>{convobj.regex})" - convertor_number += 1 - argument_weights.append(convobj.weight) - self._trace.append((True, data["variable"])) - - if data["slash"] is not None: - self._trace.append((False, "/")) - if final: - content += "/" - else: - if not static: - content += r"\Z" - weight = Weighting( - -len(static_weights), - static_weights, - -len(argument_weights), - argument_weights, - ) - yield RulePart( - content=content, - final=final, - static=static, - suffixed=False, - weight=weight, - ) - content = "" - static = True - argument_weights = [] - static_weights = [] - final = False - convertor_number = 0 - - pos = match.end() - - suffixed = False - if final and content[-1] == "/": - # If a converter is part_isolating=False (matches slashes) and ends with a - # slash, augment the regex to support slash redirects. - suffixed = True - content = content[:-1] + "(? None: - """Compiles the regular expression and stores it.""" - assert self.map is not None, "rule not bound" - - if self.map.host_matching: - domain_rule = self.host or "" - else: - domain_rule = self.subdomain or "" - self._parts = [] - self._trace = [] - self._converters = {} - if domain_rule == "": - self._parts = [ - RulePart( - content="", - final=False, - static=True, - suffixed=False, - weight=Weighting(0, [], 0, []), - ) - ] - else: - self._parts.extend(self._parse_rule(domain_rule)) - self._trace.append((False, "|")) - rule = self.rule - if self.merge_slashes: - rule = re.sub("/{2,}?", "/", self.rule) - self._parts.extend(self._parse_rule(rule)) - - self._build: t.Callable[..., tuple[str, str]] - self._build = self._compile_builder(False).__get__(self, None) - self._build_unknown: t.Callable[..., tuple[str, str]] - self._build_unknown = self._compile_builder(True).__get__(self, None) - - @staticmethod - def _get_func_code(code: CodeType, name: str) -> t.Callable[..., tuple[str, str]]: - globs: dict[str, t.Any] = {} - locs: dict[str, t.Any] = {} - exec(code, globs, locs) - return locs[name] # type: ignore - - def _compile_builder( - self, append_unknown: bool = True - ) -> t.Callable[..., tuple[str, str]]: - defaults = self.defaults or {} - dom_ops: list[tuple[bool, str]] = [] - url_ops: list[tuple[bool, str]] = [] - - opl = dom_ops - for is_dynamic, data in self._trace: - if data == "|" and opl is dom_ops: - opl = url_ops - continue - # this seems like a silly case to ever come up but: - # if a default is given for a value that appears in the rule, - # resolve it to a constant ahead of time - if is_dynamic and data in defaults: - data = self._converters[data].to_url(defaults[data]) - opl.append((False, data)) - elif not is_dynamic: - # safe = https://url.spec.whatwg.org/#url-path-segment-string - opl.append((False, quote(data, safe="!$&'()*+,/:;=@"))) - else: - opl.append((True, data)) - - def _convert(elem: str) -> ast.Call: - ret = _prefix_names(_CALL_CONVERTER_CODE_FMT.format(elem=elem), ast.Call) - ret.args = [ast.Name(elem, ast.Load())] - return ret - - def _parts(ops: list[tuple[bool, str]]) -> list[ast.expr]: - parts: list[ast.expr] = [ - _convert(elem) if is_dynamic else ast.Constant(elem) - for is_dynamic, elem in ops - ] - parts = parts or [ast.Constant("")] - # constant fold - ret = [parts[0]] - for p in parts[1:]: - if isinstance(p, ast.Constant) and isinstance(ret[-1], ast.Constant): - ret[-1] = ast.Constant(ret[-1].value + p.value) - else: - ret.append(p) - return ret - - dom_parts = _parts(dom_ops) - url_parts = _parts(url_ops) - body: list[ast.stmt] - if not append_unknown: - body = [] - else: - body = [_IF_KWARGS_URL_ENCODE_AST] - url_parts.extend(_URL_ENCODE_AST_NAMES) - - def _join(parts: list[ast.expr]) -> ast.expr: - if len(parts) == 1: # shortcut - return parts[0] - return ast.JoinedStr(parts) - - body.append( - ast.Return(ast.Tuple([_join(dom_parts), _join(url_parts)], ast.Load())) - ) - - pargs = [ - elem - for is_dynamic, elem in dom_ops + url_ops - if is_dynamic and elem not in defaults - ] - kargs = [str(k) for k in defaults] - - func_ast = _prefix_names("def _(): pass", ast.FunctionDef) - func_ast.name = f"" - func_ast.args.args.append(ast.arg(".self", None)) - for arg in pargs + kargs: - func_ast.args.args.append(ast.arg(arg, None)) - func_ast.args.kwarg = ast.arg(".kwargs", None) - for _ in kargs: - func_ast.args.defaults.append(ast.Constant("")) - func_ast.body = body - - # Use `ast.parse` instead of `ast.Module` for better portability, since the - # signature of `ast.Module` can change. - module = ast.parse("") - module.body = [func_ast] - - # mark everything as on line 1, offset 0 - # less error-prone than `ast.fix_missing_locations` - # bad line numbers cause an assert to fail in debug builds - for node in ast.walk(module): - if "lineno" in node._attributes: - node.lineno = 1 # type: ignore[attr-defined] - if "end_lineno" in node._attributes: - node.end_lineno = node.lineno # type: ignore[attr-defined] - if "col_offset" in node._attributes: - node.col_offset = 0 # type: ignore[attr-defined] - if "end_col_offset" in node._attributes: - node.end_col_offset = node.col_offset # type: ignore[attr-defined] - - code = compile(module, "", "exec") - return self._get_func_code(code, func_ast.name) - - def build( - self, values: t.Mapping[str, t.Any], append_unknown: bool = True - ) -> tuple[str, str] | None: - """Assembles the relative url for that rule and the subdomain. - If building doesn't work for some reasons `None` is returned. - - :internal: - """ - try: - if append_unknown: - return self._build_unknown(**values) - else: - return self._build(**values) - except ValidationError: - return None - - def provides_defaults_for(self, rule: Rule) -> bool: - """Check if this rule has defaults for a given rule. - - :internal: - """ - return bool( - not self.build_only - and self.defaults - and self.endpoint == rule.endpoint - and self != rule - and self.arguments == rule.arguments - ) - - def suitable_for( - self, values: t.Mapping[str, t.Any], method: str | None = None - ) -> bool: - """Check if the dict of values has enough data for url generation. - - :internal: - """ - # if a method was given explicitly and that method is not supported - # by this rule, this rule is not suitable. - if ( - method is not None - and self.methods is not None - and method not in self.methods - ): - return False - - defaults = self.defaults or () - - # all arguments required must be either in the defaults dict or - # the value dictionary otherwise it's not suitable - for key in self.arguments: - if key not in defaults and key not in values: - return False - - # in case defaults are given we ensure that either the value was - # skipped or the value is the same as the default value. - if defaults: - for key, value in defaults.items(): - if key in values and value != values[key]: - return False - - return True - - def build_compare_key(self) -> tuple[int, int, int]: - """The build compare key for sorting. - - :internal: - """ - return (1 if self.alias else 0, -len(self.arguments), -len(self.defaults or ())) - - def __eq__(self, other: object) -> bool: - return isinstance(other, type(self)) and self._trace == other._trace - - __hash__ = None # type: ignore - - def __str__(self) -> str: - return self.rule - - def __repr__(self) -> str: - if self.map is None: - return f"<{type(self).__name__} (unbound)>" - parts = [] - for is_dynamic, data in self._trace: - if is_dynamic: - parts.append(f"<{data}>") - else: - parts.append(data) - parts_str = "".join(parts).lstrip("|") - methods = f" ({', '.join(self.methods)})" if self.methods is not None else "" - return f"<{type(self).__name__} {parts_str!r}{methods} -> {self.endpoint}>" diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 9e8c7df..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/http.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/http.cpython-38.pyc deleted file mode 100644 index e56dc69..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/http.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/multipart.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/multipart.cpython-38.pyc deleted file mode 100644 index a0afa7b..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/multipart.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/request.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/request.cpython-38.pyc deleted file mode 100644 index 9ca6cba..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/request.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/response.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/response.cpython-38.pyc deleted file mode 100644 index d6784e2..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/response.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/utils.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/utils.cpython-38.pyc deleted file mode 100644 index d67c3a9..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/utils.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/http.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/http.py deleted file mode 100644 index b2b8877..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/http.py +++ /dev/null @@ -1,171 +0,0 @@ -from __future__ import annotations - -import re -import typing as t -from datetime import datetime - -from .._internal import _dt_as_utc -from ..http import generate_etag -from ..http import parse_date -from ..http import parse_etags -from ..http import parse_if_range_header -from ..http import unquote_etag - -_etag_re = re.compile(r'([Ww]/)?(?:"(.*?)"|(.*?))(?:\s*,\s*|$)') - - -def is_resource_modified( - http_range: str | None = None, - http_if_range: str | None = None, - http_if_modified_since: str | None = None, - http_if_none_match: str | None = None, - http_if_match: str | None = None, - etag: str | None = None, - data: bytes | None = None, - last_modified: datetime | str | None = None, - ignore_if_range: bool = True, -) -> bool: - """Convenience method for conditional requests. - :param http_range: Range HTTP header - :param http_if_range: If-Range HTTP header - :param http_if_modified_since: If-Modified-Since HTTP header - :param http_if_none_match: If-None-Match HTTP header - :param http_if_match: If-Match HTTP header - :param etag: the etag for the response for comparison. - :param data: or alternatively the data of the response to automatically - generate an etag using :func:`generate_etag`. - :param last_modified: an optional date of the last modification. - :param ignore_if_range: If `False`, `If-Range` header will be taken into - account. - :return: `True` if the resource was modified, otherwise `False`. - - .. versionadded:: 2.2 - """ - if etag is None and data is not None: - etag = generate_etag(data) - elif data is not None: - raise TypeError("both data and etag given") - - unmodified = False - if isinstance(last_modified, str): - last_modified = parse_date(last_modified) - - # HTTP doesn't use microsecond, remove it to avoid false positive - # comparisons. Mark naive datetimes as UTC. - if last_modified is not None: - last_modified = _dt_as_utc(last_modified.replace(microsecond=0)) - - if_range = None - if not ignore_if_range and http_range is not None: - # https://tools.ietf.org/html/rfc7233#section-3.2 - # A server MUST ignore an If-Range header field received in a request - # that does not contain a Range header field. - if_range = parse_if_range_header(http_if_range) - - if if_range is not None and if_range.date is not None: - modified_since: datetime | None = if_range.date - else: - modified_since = parse_date(http_if_modified_since) - - if modified_since and last_modified and last_modified <= modified_since: - unmodified = True - - if etag: - etag, _ = unquote_etag(etag) - etag = t.cast(str, etag) - - if if_range is not None and if_range.etag is not None: - unmodified = parse_etags(if_range.etag).contains(etag) - else: - if_none_match = parse_etags(http_if_none_match) - if if_none_match: - # https://tools.ietf.org/html/rfc7232#section-3.2 - # "A recipient MUST use the weak comparison function when comparing - # entity-tags for If-None-Match" - unmodified = if_none_match.contains_weak(etag) - - # https://tools.ietf.org/html/rfc7232#section-3.1 - # "Origin server MUST use the strong comparison function when - # comparing entity-tags for If-Match" - if_match = parse_etags(http_if_match) - if if_match: - unmodified = not if_match.is_strong(etag) - - return not unmodified - - -_cookie_re = re.compile( - r""" - ([^=;]*) - (?:\s*=\s* - ( - "(?:[^\\"]|\\.)*" - | - .*? - ) - )? - \s*;\s* - """, - flags=re.ASCII | re.VERBOSE, -) -_cookie_unslash_re = re.compile(rb"\\([0-3][0-7]{2}|.)") - - -def _cookie_unslash_replace(m: t.Match[bytes]) -> bytes: - v = m.group(1) - - if len(v) == 1: - return v - - return int(v, 8).to_bytes(1, "big") - - -def parse_cookie( - cookie: str | None = None, - cls: type[ds.MultiDict[str, str]] | None = None, -) -> ds.MultiDict[str, str]: - """Parse a cookie from a string. - - The same key can be provided multiple times, the values are stored - in-order. The default :class:`MultiDict` will have the first value - first, and all values can be retrieved with - :meth:`MultiDict.getlist`. - - :param cookie: The cookie header as a string. - :param cls: A dict-like class to store the parsed cookies in. - Defaults to :class:`MultiDict`. - - .. versionchanged:: 3.0 - Passing bytes, and the ``charset`` and ``errors`` parameters, were removed. - - .. versionadded:: 2.2 - """ - if cls is None: - cls = t.cast("type[ds.MultiDict[str, str]]", ds.MultiDict) - - if not cookie: - return cls() - - cookie = f"{cookie};" - out = [] - - for ck, cv in _cookie_re.findall(cookie): - ck = ck.strip() - cv = cv.strip() - - if not ck: - continue - - if len(cv) >= 2 and cv[0] == cv[-1] == '"': - # Work with bytes here, since a UTF-8 character could be multiple bytes. - cv = _cookie_unslash_re.sub( - _cookie_unslash_replace, cv[1:-1].encode() - ).decode(errors="replace") - - out.append((ck, cv)) - - return cls(out) - - -# circular dependencies -from .. import datastructures as ds diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/multipart.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/multipart.py deleted file mode 100644 index 731be03..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/multipart.py +++ /dev/null @@ -1,323 +0,0 @@ -from __future__ import annotations - -import re -import typing as t -from dataclasses import dataclass -from enum import auto -from enum import Enum - -from ..datastructures import Headers -from ..exceptions import RequestEntityTooLarge -from ..http import parse_options_header - - -class Event: - pass - - -@dataclass(frozen=True) -class Preamble(Event): - data: bytes - - -@dataclass(frozen=True) -class Field(Event): - name: str - headers: Headers - - -@dataclass(frozen=True) -class File(Event): - name: str - filename: str - headers: Headers - - -@dataclass(frozen=True) -class Data(Event): - data: bytes - more_data: bool - - -@dataclass(frozen=True) -class Epilogue(Event): - data: bytes - - -class NeedData(Event): - pass - - -NEED_DATA = NeedData() - - -class State(Enum): - PREAMBLE = auto() - PART = auto() - DATA = auto() - DATA_START = auto() - EPILOGUE = auto() - COMPLETE = auto() - - -# Multipart line breaks MUST be CRLF (\r\n) by RFC-7578, except that -# many implementations break this and either use CR or LF alone. -LINE_BREAK = b"(?:\r\n|\n|\r)" -BLANK_LINE_RE = re.compile(b"(?:\r\n\r\n|\r\r|\n\n)", re.MULTILINE) -LINE_BREAK_RE = re.compile(LINE_BREAK, re.MULTILINE) -# Header values can be continued via a space or tab after the linebreak, as -# per RFC2231 -HEADER_CONTINUATION_RE = re.compile(b"%s[ \t]" % LINE_BREAK, re.MULTILINE) -# This must be long enough to contain any line breaks plus any -# additional boundary markers (--) such that they will be found in a -# subsequent search -SEARCH_EXTRA_LENGTH = 8 - - -class MultipartDecoder: - """Decodes a multipart message as bytes into Python events. - - The part data is returned as available to allow the caller to save - the data from memory to disk, if desired. - """ - - def __init__( - self, - boundary: bytes, - max_form_memory_size: int | None = None, - *, - max_parts: int | None = None, - ) -> None: - self.buffer = bytearray() - self.complete = False - self.max_form_memory_size = max_form_memory_size - self.max_parts = max_parts - self.state = State.PREAMBLE - self.boundary = boundary - - # Note in the below \h i.e. horizontal whitespace is used - # as [^\S\n\r] as \h isn't supported in python. - - # The preamble must end with a boundary where the boundary is - # prefixed by a line break, RFC2046. Except that many - # implementations including Werkzeug's tests omit the line - # break prefix. In addition the first boundary could be the - # epilogue boundary (for empty form-data) hence the matching - # group to understand if it is an epilogue boundary. - self.preamble_re = re.compile( - rb"%s?--%s(--[^\S\n\r]*%s?|[^\S\n\r]*%s)" - % (LINE_BREAK, re.escape(boundary), LINE_BREAK, LINE_BREAK), - re.MULTILINE, - ) - # A boundary must include a line break prefix and suffix, and - # may include trailing whitespace. In addition the boundary - # could be the epilogue boundary hence the matching group to - # understand if it is an epilogue boundary. - self.boundary_re = re.compile( - rb"%s--%s(--[^\S\n\r]*%s?|[^\S\n\r]*%s)" - % (LINE_BREAK, re.escape(boundary), LINE_BREAK, LINE_BREAK), - re.MULTILINE, - ) - self._search_position = 0 - self._parts_decoded = 0 - - def last_newline(self, data: bytes) -> int: - try: - last_nl = data.rindex(b"\n") - except ValueError: - last_nl = len(data) - try: - last_cr = data.rindex(b"\r") - except ValueError: - last_cr = len(data) - - return min(last_nl, last_cr) - - def receive_data(self, data: bytes | None) -> None: - if data is None: - self.complete = True - elif ( - self.max_form_memory_size is not None - and len(self.buffer) + len(data) > self.max_form_memory_size - ): - # Ensure that data within single event does not exceed limit. - # Also checked across accumulated events in MultiPartParser. - raise RequestEntityTooLarge() - else: - self.buffer.extend(data) - - def next_event(self) -> Event: - event: Event = NEED_DATA - - if self.state == State.PREAMBLE: - match = self.preamble_re.search(self.buffer, self._search_position) - if match is not None: - if match.group(1).startswith(b"--"): - self.state = State.EPILOGUE - else: - self.state = State.PART - data = bytes(self.buffer[: match.start()]) - del self.buffer[: match.end()] - event = Preamble(data=data) - self._search_position = 0 - else: - # Update the search start position to be equal to the - # current buffer length (already searched) minus a - # safe buffer for part of the search target. - self._search_position = max( - 0, len(self.buffer) - len(self.boundary) - SEARCH_EXTRA_LENGTH - ) - - elif self.state == State.PART: - match = BLANK_LINE_RE.search(self.buffer, self._search_position) - if match is not None: - headers = self._parse_headers(self.buffer[: match.start()]) - # The final header ends with a single CRLF, however a - # blank line indicates the start of the - # body. Therefore the end is after the first CRLF. - headers_end = (match.start() + match.end()) // 2 - del self.buffer[:headers_end] - - if "content-disposition" not in headers: - raise ValueError("Missing Content-Disposition header") - - disposition, extra = parse_options_header( - headers["content-disposition"] - ) - name = t.cast(str, extra.get("name")) - filename = extra.get("filename") - if filename is not None: - event = File( - filename=filename, - headers=headers, - name=name, - ) - else: - event = Field( - headers=headers, - name=name, - ) - self.state = State.DATA_START - self._search_position = 0 - self._parts_decoded += 1 - - if self.max_parts is not None and self._parts_decoded > self.max_parts: - raise RequestEntityTooLarge() - else: - # Update the search start position to be equal to the - # current buffer length (already searched) minus a - # safe buffer for part of the search target. - self._search_position = max(0, len(self.buffer) - SEARCH_EXTRA_LENGTH) - - elif self.state == State.DATA_START: - data, del_index, more_data = self._parse_data(self.buffer, start=True) - del self.buffer[:del_index] - event = Data(data=data, more_data=more_data) - if more_data: - self.state = State.DATA - - elif self.state == State.DATA: - data, del_index, more_data = self._parse_data(self.buffer, start=False) - del self.buffer[:del_index] - if data or not more_data: - event = Data(data=data, more_data=more_data) - - elif self.state == State.EPILOGUE and self.complete: - event = Epilogue(data=bytes(self.buffer)) - del self.buffer[:] - self.state = State.COMPLETE - - if self.complete and isinstance(event, NeedData): - raise ValueError(f"Invalid form-data cannot parse beyond {self.state}") - - return event - - def _parse_headers(self, data: bytes) -> Headers: - headers: list[tuple[str, str]] = [] - # Merge the continued headers into one line - data = HEADER_CONTINUATION_RE.sub(b" ", data) - # Now there is one header per line - for line in data.splitlines(): - line = line.strip() - - if line != b"": - name, _, value = line.decode().partition(":") - headers.append((name.strip(), value.strip())) - return Headers(headers) - - def _parse_data(self, data: bytes, *, start: bool) -> tuple[bytes, int, bool]: - # Body parts must start with CRLF (or CR or LF) - if start: - match = LINE_BREAK_RE.match(data) - data_start = t.cast(t.Match[bytes], match).end() - else: - data_start = 0 - - boundary = b"--" + self.boundary - - if self.buffer.find(boundary) == -1: - # No complete boundary in the buffer, but there may be - # a partial boundary at the end. As the boundary - # starts with either a nl or cr find the earliest and - # return up to that as data. - data_end = del_index = self.last_newline(data[data_start:]) + data_start - # If amount of data after last newline is far from - # possible length of partial boundary, we should - # assume that there is no partial boundary in the buffer - # and return all pending data. - if (len(data) - data_end) > len(b"\n" + boundary): - data_end = del_index = len(data) - more_data = True - else: - match = self.boundary_re.search(data) - if match is not None: - if match.group(1).startswith(b"--"): - self.state = State.EPILOGUE - else: - self.state = State.PART - data_end = match.start() - del_index = match.end() - else: - data_end = del_index = self.last_newline(data[data_start:]) + data_start - more_data = match is None - - return bytes(data[data_start:data_end]), del_index, more_data - - -class MultipartEncoder: - def __init__(self, boundary: bytes) -> None: - self.boundary = boundary - self.state = State.PREAMBLE - - def send_event(self, event: Event) -> bytes: - if isinstance(event, Preamble) and self.state == State.PREAMBLE: - self.state = State.PART - return event.data - elif isinstance(event, (Field, File)) and self.state in { - State.PREAMBLE, - State.PART, - State.DATA, - }: - data = b"\r\n--" + self.boundary + b"\r\n" - data += b'Content-Disposition: form-data; name="%s"' % event.name.encode() - if isinstance(event, File): - data += b'; filename="%s"' % event.filename.encode() - data += b"\r\n" - for name, value in t.cast(Field, event).headers: - if name.lower() != "content-disposition": - data += f"{name}: {value}\r\n".encode() - self.state = State.DATA_START - return data - elif isinstance(event, Data) and self.state == State.DATA_START: - self.state = State.DATA - if len(event.data) > 0: - return b"\r\n" + event.data - else: - return event.data - elif isinstance(event, Data) and self.state == State.DATA: - return event.data - elif isinstance(event, Epilogue): - self.state = State.COMPLETE - return b"\r\n--" + self.boundary + b"--\r\n" + event.data - else: - raise ValueError(f"Cannot generate {event} in state: {self.state}") diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/request.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/request.py deleted file mode 100644 index dd0805d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/request.py +++ /dev/null @@ -1,536 +0,0 @@ -from __future__ import annotations - -import typing as t -from datetime import datetime -from urllib.parse import parse_qsl - -from ..datastructures import Accept -from ..datastructures import Authorization -from ..datastructures import CharsetAccept -from ..datastructures import ETags -from ..datastructures import Headers -from ..datastructures import HeaderSet -from ..datastructures import IfRange -from ..datastructures import ImmutableList -from ..datastructures import ImmutableMultiDict -from ..datastructures import LanguageAccept -from ..datastructures import MIMEAccept -from ..datastructures import MultiDict -from ..datastructures import Range -from ..datastructures import RequestCacheControl -from ..http import parse_accept_header -from ..http import parse_cache_control_header -from ..http import parse_date -from ..http import parse_etags -from ..http import parse_if_range_header -from ..http import parse_list_header -from ..http import parse_options_header -from ..http import parse_range_header -from ..http import parse_set_header -from ..user_agent import UserAgent -from ..utils import cached_property -from ..utils import header_property -from .http import parse_cookie -from .utils import get_content_length -from .utils import get_current_url -from .utils import get_host - - -class Request: - """Represents the non-IO parts of a HTTP request, including the - method, URL info, and headers. - - This class is not meant for general use. It should only be used when - implementing WSGI, ASGI, or another HTTP application spec. Werkzeug - provides a WSGI implementation at :cls:`werkzeug.wrappers.Request`. - - :param method: The method the request was made with, such as - ``GET``. - :param scheme: The URL scheme of the protocol the request used, such - as ``https`` or ``wss``. - :param server: The address of the server. ``(host, port)``, - ``(path, None)`` for unix sockets, or ``None`` if not known. - :param root_path: The prefix that the application is mounted under. - This is prepended to generated URLs, but is not part of route - matching. - :param path: The path part of the URL after ``root_path``. - :param query_string: The part of the URL after the "?". - :param headers: The headers received with the request. - :param remote_addr: The address of the client sending the request. - - .. versionchanged:: 3.0 - The ``charset``, ``url_charset``, and ``encoding_errors`` attributes - were removed. - - .. versionadded:: 2.0 - """ - - #: the class to use for `args` and `form`. The default is an - #: :class:`~werkzeug.datastructures.ImmutableMultiDict` which supports - #: multiple values per key. alternatively it makes sense to use an - #: :class:`~werkzeug.datastructures.ImmutableOrderedMultiDict` which - #: preserves order or a :class:`~werkzeug.datastructures.ImmutableDict` - #: which is the fastest but only remembers the last key. It is also - #: possible to use mutable structures, but this is not recommended. - #: - #: .. versionadded:: 0.6 - parameter_storage_class: type[MultiDict[str, t.Any]] = ImmutableMultiDict - - #: The type to be used for dict values from the incoming WSGI - #: environment. (For example for :attr:`cookies`.) By default an - #: :class:`~werkzeug.datastructures.ImmutableMultiDict` is used. - #: - #: .. versionchanged:: 1.0.0 - #: Changed to ``ImmutableMultiDict`` to support multiple values. - #: - #: .. versionadded:: 0.6 - dict_storage_class: type[MultiDict[str, t.Any]] = ImmutableMultiDict - - #: the type to be used for list values from the incoming WSGI environment. - #: By default an :class:`~werkzeug.datastructures.ImmutableList` is used - #: (for example for :attr:`access_list`). - #: - #: .. versionadded:: 0.6 - list_storage_class: type[list[t.Any]] = ImmutableList - - user_agent_class: type[UserAgent] = UserAgent - """The class used and returned by the :attr:`user_agent` property to - parse the header. Defaults to - :class:`~werkzeug.user_agent.UserAgent`, which does no parsing. An - extension can provide a subclass that uses a parser to provide other - data. - - .. versionadded:: 2.0 - """ - - #: Valid host names when handling requests. By default all hosts are - #: trusted, which means that whatever the client says the host is - #: will be accepted. - #: - #: Because ``Host`` and ``X-Forwarded-Host`` headers can be set to - #: any value by a malicious client, it is recommended to either set - #: this property or implement similar validation in the proxy (if - #: the application is being run behind one). - #: - #: .. versionadded:: 0.9 - trusted_hosts: list[str] | None = None - - def __init__( - self, - method: str, - scheme: str, - server: tuple[str, int | None] | None, - root_path: str, - path: str, - query_string: bytes, - headers: Headers, - remote_addr: str | None, - ) -> None: - #: The method the request was made with, such as ``GET``. - self.method = method.upper() - #: The URL scheme of the protocol the request used, such as - #: ``https`` or ``wss``. - self.scheme = scheme - #: The address of the server. ``(host, port)``, ``(path, None)`` - #: for unix sockets, or ``None`` if not known. - self.server = server - #: The prefix that the application is mounted under, without a - #: trailing slash. :attr:`path` comes after this. - self.root_path = root_path.rstrip("/") - #: The path part of the URL after :attr:`root_path`. This is the - #: path used for routing within the application. - self.path = "/" + path.lstrip("/") - #: The part of the URL after the "?". This is the raw value, use - #: :attr:`args` for the parsed values. - self.query_string = query_string - #: The headers received with the request. - self.headers = headers - #: The address of the client sending the request. - self.remote_addr = remote_addr - - def __repr__(self) -> str: - try: - url = self.url - except Exception as e: - url = f"(invalid URL: {e})" - - return f"<{type(self).__name__} {url!r} [{self.method}]>" - - @cached_property - def args(self) -> MultiDict[str, str]: - """The parsed URL parameters (the part in the URL after the question - mark). - - By default an - :class:`~werkzeug.datastructures.ImmutableMultiDict` - is returned from this function. This can be changed by setting - :attr:`parameter_storage_class` to a different type. This might - be necessary if the order of the form data is important. - - .. versionchanged:: 2.3 - Invalid bytes remain percent encoded. - """ - return self.parameter_storage_class( - parse_qsl( - self.query_string.decode(), - keep_blank_values=True, - errors="werkzeug.url_quote", - ) - ) - - @cached_property - def access_route(self) -> list[str]: - """If a forwarded header exists this is a list of all ip addresses - from the client ip to the last proxy server. - """ - if "X-Forwarded-For" in self.headers: - return self.list_storage_class( - parse_list_header(self.headers["X-Forwarded-For"]) - ) - elif self.remote_addr is not None: - return self.list_storage_class([self.remote_addr]) - return self.list_storage_class() - - @cached_property - def full_path(self) -> str: - """Requested path, including the query string.""" - return f"{self.path}?{self.query_string.decode()}" - - @property - def is_secure(self) -> bool: - """``True`` if the request was made with a secure protocol - (HTTPS or WSS). - """ - return self.scheme in {"https", "wss"} - - @cached_property - def url(self) -> str: - """The full request URL with the scheme, host, root path, path, - and query string.""" - return get_current_url( - self.scheme, self.host, self.root_path, self.path, self.query_string - ) - - @cached_property - def base_url(self) -> str: - """Like :attr:`url` but without the query string.""" - return get_current_url(self.scheme, self.host, self.root_path, self.path) - - @cached_property - def root_url(self) -> str: - """The request URL scheme, host, and root path. This is the root - that the application is accessed from. - """ - return get_current_url(self.scheme, self.host, self.root_path) - - @cached_property - def host_url(self) -> str: - """The request URL scheme and host only.""" - return get_current_url(self.scheme, self.host) - - @cached_property - def host(self) -> str: - """The host name the request was made to, including the port if - it's non-standard. Validated with :attr:`trusted_hosts`. - """ - return get_host( - self.scheme, self.headers.get("host"), self.server, self.trusted_hosts - ) - - @cached_property - def cookies(self) -> ImmutableMultiDict[str, str]: - """A :class:`dict` with the contents of all cookies transmitted with - the request.""" - wsgi_combined_cookie = ";".join(self.headers.getlist("Cookie")) - return parse_cookie( # type: ignore - wsgi_combined_cookie, cls=self.dict_storage_class - ) - - # Common Descriptors - - content_type = header_property[str]( - "Content-Type", - doc="""The Content-Type entity-header field indicates the media - type of the entity-body sent to the recipient or, in the case of - the HEAD method, the media type that would have been sent had - the request been a GET.""", - read_only=True, - ) - - @cached_property - def content_length(self) -> int | None: - """The Content-Length entity-header field indicates the size of the - entity-body in bytes or, in the case of the HEAD method, the size of - the entity-body that would have been sent had the request been a - GET. - """ - return get_content_length( - http_content_length=self.headers.get("Content-Length"), - http_transfer_encoding=self.headers.get("Transfer-Encoding"), - ) - - content_encoding = header_property[str]( - "Content-Encoding", - doc="""The Content-Encoding entity-header field is used as a - modifier to the media-type. When present, its value indicates - what additional content codings have been applied to the - entity-body, and thus what decoding mechanisms must be applied - in order to obtain the media-type referenced by the Content-Type - header field. - - .. versionadded:: 0.9""", - read_only=True, - ) - content_md5 = header_property[str]( - "Content-MD5", - doc="""The Content-MD5 entity-header field, as defined in - RFC 1864, is an MD5 digest of the entity-body for the purpose of - providing an end-to-end message integrity check (MIC) of the - entity-body. (Note: a MIC is good for detecting accidental - modification of the entity-body in transit, but is not proof - against malicious attacks.) - - .. versionadded:: 0.9""", - read_only=True, - ) - referrer = header_property[str]( - "Referer", - doc="""The Referer[sic] request-header field allows the client - to specify, for the server's benefit, the address (URI) of the - resource from which the Request-URI was obtained (the - "referrer", although the header field is misspelled).""", - read_only=True, - ) - date = header_property( - "Date", - None, - parse_date, - doc="""The Date general-header field represents the date and - time at which the message was originated, having the same - semantics as orig-date in RFC 822. - - .. versionchanged:: 2.0 - The datetime object is timezone-aware. - """, - read_only=True, - ) - max_forwards = header_property( - "Max-Forwards", - None, - int, - doc="""The Max-Forwards request-header field provides a - mechanism with the TRACE and OPTIONS methods to limit the number - of proxies or gateways that can forward the request to the next - inbound server.""", - read_only=True, - ) - - def _parse_content_type(self) -> None: - if not hasattr(self, "_parsed_content_type"): - self._parsed_content_type = parse_options_header( - self.headers.get("Content-Type", "") - ) - - @property - def mimetype(self) -> str: - """Like :attr:`content_type`, but without parameters (eg, without - charset, type etc.) and always lowercase. For example if the content - type is ``text/HTML; charset=utf-8`` the mimetype would be - ``'text/html'``. - """ - self._parse_content_type() - return self._parsed_content_type[0].lower() - - @property - def mimetype_params(self) -> dict[str, str]: - """The mimetype parameters as dict. For example if the content - type is ``text/html; charset=utf-8`` the params would be - ``{'charset': 'utf-8'}``. - """ - self._parse_content_type() - return self._parsed_content_type[1] - - @cached_property - def pragma(self) -> HeaderSet: - """The Pragma general-header field is used to include - implementation-specific directives that might apply to any recipient - along the request/response chain. All pragma directives specify - optional behavior from the viewpoint of the protocol; however, some - systems MAY require that behavior be consistent with the directives. - """ - return parse_set_header(self.headers.get("Pragma", "")) - - # Accept - - @cached_property - def accept_mimetypes(self) -> MIMEAccept: - """List of mimetypes this client supports as - :class:`~werkzeug.datastructures.MIMEAccept` object. - """ - return parse_accept_header(self.headers.get("Accept"), MIMEAccept) - - @cached_property - def accept_charsets(self) -> CharsetAccept: - """List of charsets this client supports as - :class:`~werkzeug.datastructures.CharsetAccept` object. - """ - return parse_accept_header(self.headers.get("Accept-Charset"), CharsetAccept) - - @cached_property - def accept_encodings(self) -> Accept: - """List of encodings this client accepts. Encodings in a HTTP term - are compression encodings such as gzip. For charsets have a look at - :attr:`accept_charset`. - """ - return parse_accept_header(self.headers.get("Accept-Encoding")) - - @cached_property - def accept_languages(self) -> LanguageAccept: - """List of languages this client accepts as - :class:`~werkzeug.datastructures.LanguageAccept` object. - - .. versionchanged 0.5 - In previous versions this was a regular - :class:`~werkzeug.datastructures.Accept` object. - """ - return parse_accept_header(self.headers.get("Accept-Language"), LanguageAccept) - - # ETag - - @cached_property - def cache_control(self) -> RequestCacheControl: - """A :class:`~werkzeug.datastructures.RequestCacheControl` object - for the incoming cache control headers. - """ - cache_control = self.headers.get("Cache-Control") - return parse_cache_control_header(cache_control, None, RequestCacheControl) - - @cached_property - def if_match(self) -> ETags: - """An object containing all the etags in the `If-Match` header. - - :rtype: :class:`~werkzeug.datastructures.ETags` - """ - return parse_etags(self.headers.get("If-Match")) - - @cached_property - def if_none_match(self) -> ETags: - """An object containing all the etags in the `If-None-Match` header. - - :rtype: :class:`~werkzeug.datastructures.ETags` - """ - return parse_etags(self.headers.get("If-None-Match")) - - @cached_property - def if_modified_since(self) -> datetime | None: - """The parsed `If-Modified-Since` header as a datetime object. - - .. versionchanged:: 2.0 - The datetime object is timezone-aware. - """ - return parse_date(self.headers.get("If-Modified-Since")) - - @cached_property - def if_unmodified_since(self) -> datetime | None: - """The parsed `If-Unmodified-Since` header as a datetime object. - - .. versionchanged:: 2.0 - The datetime object is timezone-aware. - """ - return parse_date(self.headers.get("If-Unmodified-Since")) - - @cached_property - def if_range(self) -> IfRange: - """The parsed ``If-Range`` header. - - .. versionchanged:: 2.0 - ``IfRange.date`` is timezone-aware. - - .. versionadded:: 0.7 - """ - return parse_if_range_header(self.headers.get("If-Range")) - - @cached_property - def range(self) -> Range | None: - """The parsed `Range` header. - - .. versionadded:: 0.7 - - :rtype: :class:`~werkzeug.datastructures.Range` - """ - return parse_range_header(self.headers.get("Range")) - - # User Agent - - @cached_property - def user_agent(self) -> UserAgent: - """The user agent. Use ``user_agent.string`` to get the header - value. Set :attr:`user_agent_class` to a subclass of - :class:`~werkzeug.user_agent.UserAgent` to provide parsing for - the other properties or other extended data. - - .. versionchanged:: 2.1 - The built-in parser was removed. Set ``user_agent_class`` to a ``UserAgent`` - subclass to parse data from the string. - """ - return self.user_agent_class(self.headers.get("User-Agent", "")) - - # Authorization - - @cached_property - def authorization(self) -> Authorization | None: - """The ``Authorization`` header parsed into an :class:`.Authorization` object. - ``None`` if the header is not present. - - .. versionchanged:: 2.3 - :class:`Authorization` is no longer a ``dict``. The ``token`` attribute - was added for auth schemes that use a token instead of parameters. - """ - return Authorization.from_header(self.headers.get("Authorization")) - - # CORS - - origin = header_property[str]( - "Origin", - doc=( - "The host that the request originated from. Set" - " :attr:`~CORSResponseMixin.access_control_allow_origin` on" - " the response to indicate which origins are allowed." - ), - read_only=True, - ) - - access_control_request_headers = header_property( - "Access-Control-Request-Headers", - load_func=parse_set_header, - doc=( - "Sent with a preflight request to indicate which headers" - " will be sent with the cross origin request. Set" - " :attr:`~CORSResponseMixin.access_control_allow_headers`" - " on the response to indicate which headers are allowed." - ), - read_only=True, - ) - - access_control_request_method = header_property[str]( - "Access-Control-Request-Method", - doc=( - "Sent with a preflight request to indicate which method" - " will be used for the cross origin request. Set" - " :attr:`~CORSResponseMixin.access_control_allow_methods`" - " on the response to indicate which methods are allowed." - ), - read_only=True, - ) - - @property - def is_json(self) -> bool: - """Check if the mimetype indicates JSON data, either - :mimetype:`application/json` or :mimetype:`application/*+json`. - """ - mt = self.mimetype - return ( - mt == "application/json" - or mt.startswith("application/") - and mt.endswith("+json") - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/response.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/response.py deleted file mode 100644 index 9093b0a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/response.py +++ /dev/null @@ -1,754 +0,0 @@ -from __future__ import annotations - -import typing as t -from datetime import datetime -from datetime import timedelta -from datetime import timezone -from http import HTTPStatus - -from ..datastructures import CallbackDict -from ..datastructures import ContentRange -from ..datastructures import ContentSecurityPolicy -from ..datastructures import Headers -from ..datastructures import HeaderSet -from ..datastructures import ResponseCacheControl -from ..datastructures import WWWAuthenticate -from ..http import COEP -from ..http import COOP -from ..http import dump_age -from ..http import dump_cookie -from ..http import dump_header -from ..http import dump_options_header -from ..http import http_date -from ..http import HTTP_STATUS_CODES -from ..http import parse_age -from ..http import parse_cache_control_header -from ..http import parse_content_range_header -from ..http import parse_csp_header -from ..http import parse_date -from ..http import parse_options_header -from ..http import parse_set_header -from ..http import quote_etag -from ..http import unquote_etag -from ..utils import get_content_type -from ..utils import header_property - -if t.TYPE_CHECKING: - from ..datastructures.cache_control import _CacheControl - - -def _set_property(name: str, doc: str | None = None) -> property: - def fget(self: Response) -> HeaderSet: - def on_update(header_set: HeaderSet) -> None: - if not header_set and name in self.headers: - del self.headers[name] - elif header_set: - self.headers[name] = header_set.to_header() - - return parse_set_header(self.headers.get(name), on_update) - - def fset( - self: Response, - value: None | (str | dict[str, str | int] | t.Iterable[str]), - ) -> None: - if not value: - del self.headers[name] - elif isinstance(value, str): - self.headers[name] = value - else: - self.headers[name] = dump_header(value) - - return property(fget, fset, doc=doc) - - -class Response: - """Represents the non-IO parts of an HTTP response, specifically the - status and headers but not the body. - - This class is not meant for general use. It should only be used when - implementing WSGI, ASGI, or another HTTP application spec. Werkzeug - provides a WSGI implementation at :cls:`werkzeug.wrappers.Response`. - - :param status: The status code for the response. Either an int, in - which case the default status message is added, or a string in - the form ``{code} {message}``, like ``404 Not Found``. Defaults - to 200. - :param headers: A :class:`~werkzeug.datastructures.Headers` object, - or a list of ``(key, value)`` tuples that will be converted to a - ``Headers`` object. - :param mimetype: The mime type (content type without charset or - other parameters) of the response. If the value starts with - ``text/`` (or matches some other special cases), the charset - will be added to create the ``content_type``. - :param content_type: The full content type of the response. - Overrides building the value from ``mimetype``. - - .. versionchanged:: 3.0 - The ``charset`` attribute was removed. - - .. versionadded:: 2.0 - """ - - #: the default status if none is provided. - default_status = 200 - - #: the default mimetype if none is provided. - default_mimetype: str | None = "text/plain" - - #: Warn if a cookie header exceeds this size. The default, 4093, should be - #: safely `supported by most browsers `_. A cookie larger than - #: this size will still be sent, but it may be ignored or handled - #: incorrectly by some browsers. Set to 0 to disable this check. - #: - #: .. versionadded:: 0.13 - #: - #: .. _`cookie`: http://browsercookielimits.squawky.net/ - max_cookie_size = 4093 - - # A :class:`Headers` object representing the response headers. - headers: Headers - - def __init__( - self, - status: int | str | HTTPStatus | None = None, - headers: t.Mapping[str, str | t.Iterable[str]] - | t.Iterable[tuple[str, str]] - | None = None, - mimetype: str | None = None, - content_type: str | None = None, - ) -> None: - if isinstance(headers, Headers): - self.headers = headers - elif not headers: - self.headers = Headers() - else: - self.headers = Headers(headers) - - if content_type is None: - if mimetype is None and "content-type" not in self.headers: - mimetype = self.default_mimetype - if mimetype is not None: - mimetype = get_content_type(mimetype, "utf-8") - content_type = mimetype - if content_type is not None: - self.headers["Content-Type"] = content_type - if status is None: - status = self.default_status - self.status = status # type: ignore - - def __repr__(self) -> str: - return f"<{type(self).__name__} [{self.status}]>" - - @property - def status_code(self) -> int: - """The HTTP status code as a number.""" - return self._status_code - - @status_code.setter - def status_code(self, code: int) -> None: - self.status = code # type: ignore - - @property - def status(self) -> str: - """The HTTP status code as a string.""" - return self._status - - @status.setter - def status(self, value: str | int | HTTPStatus) -> None: - self._status, self._status_code = self._clean_status(value) - - def _clean_status(self, value: str | int | HTTPStatus) -> tuple[str, int]: - if isinstance(value, (int, HTTPStatus)): - status_code = int(value) - else: - value = value.strip() - - if not value: - raise ValueError("Empty status argument") - - code_str, sep, _ = value.partition(" ") - - try: - status_code = int(code_str) - except ValueError: - # only message - return f"0 {value}", 0 - - if sep: - # code and message - return value, status_code - - # only code, look up message - try: - status = f"{status_code} {HTTP_STATUS_CODES[status_code].upper()}" - except KeyError: - status = f"{status_code} UNKNOWN" - - return status, status_code - - def set_cookie( - self, - key: str, - value: str = "", - max_age: timedelta | int | None = None, - expires: str | datetime | int | float | None = None, - path: str | None = "/", - domain: str | None = None, - secure: bool = False, - httponly: bool = False, - samesite: str | None = None, - ) -> None: - """Sets a cookie. - - A warning is raised if the size of the cookie header exceeds - :attr:`max_cookie_size`, but the header will still be set. - - :param key: the key (name) of the cookie to be set. - :param value: the value of the cookie. - :param max_age: should be a number of seconds, or `None` (default) if - the cookie should last only as long as the client's - browser session. - :param expires: should be a `datetime` object or UNIX timestamp. - :param path: limits the cookie to a given path, per default it will - span the whole domain. - :param domain: if you want to set a cross-domain cookie. For example, - ``domain="example.com"`` will set a cookie that is - readable by the domain ``www.example.com``, - ``foo.example.com`` etc. Otherwise, a cookie will only - be readable by the domain that set it. - :param secure: If ``True``, the cookie will only be available - via HTTPS. - :param httponly: Disallow JavaScript access to the cookie. - :param samesite: Limit the scope of the cookie to only be - attached to requests that are "same-site". - """ - self.headers.add( - "Set-Cookie", - dump_cookie( - key, - value=value, - max_age=max_age, - expires=expires, - path=path, - domain=domain, - secure=secure, - httponly=httponly, - max_size=self.max_cookie_size, - samesite=samesite, - ), - ) - - def delete_cookie( - self, - key: str, - path: str | None = "/", - domain: str | None = None, - secure: bool = False, - httponly: bool = False, - samesite: str | None = None, - ) -> None: - """Delete a cookie. Fails silently if key doesn't exist. - - :param key: the key (name) of the cookie to be deleted. - :param path: if the cookie that should be deleted was limited to a - path, the path has to be defined here. - :param domain: if the cookie that should be deleted was limited to a - domain, that domain has to be defined here. - :param secure: If ``True``, the cookie will only be available - via HTTPS. - :param httponly: Disallow JavaScript access to the cookie. - :param samesite: Limit the scope of the cookie to only be - attached to requests that are "same-site". - """ - self.set_cookie( - key, - expires=0, - max_age=0, - path=path, - domain=domain, - secure=secure, - httponly=httponly, - samesite=samesite, - ) - - @property - def is_json(self) -> bool: - """Check if the mimetype indicates JSON data, either - :mimetype:`application/json` or :mimetype:`application/*+json`. - """ - mt = self.mimetype - return mt is not None and ( - mt == "application/json" - or mt.startswith("application/") - and mt.endswith("+json") - ) - - # Common Descriptors - - @property - def mimetype(self) -> str | None: - """The mimetype (content type without charset etc.)""" - ct = self.headers.get("content-type") - - if ct: - return ct.split(";")[0].strip() - else: - return None - - @mimetype.setter - def mimetype(self, value: str) -> None: - self.headers["Content-Type"] = get_content_type(value, "utf-8") - - @property - def mimetype_params(self) -> dict[str, str]: - """The mimetype parameters as dict. For example if the - content type is ``text/html; charset=utf-8`` the params would be - ``{'charset': 'utf-8'}``. - - .. versionadded:: 0.5 - """ - - def on_update(d: CallbackDict[str, str]) -> None: - self.headers["Content-Type"] = dump_options_header(self.mimetype, d) - - d = parse_options_header(self.headers.get("content-type", ""))[1] - return CallbackDict(d, on_update) - - location = header_property[str]( - "Location", - doc="""The Location response-header field is used to redirect - the recipient to a location other than the Request-URI for - completion of the request or identification of a new - resource.""", - ) - age = header_property( - "Age", - None, - parse_age, - dump_age, # type: ignore - doc="""The Age response-header field conveys the sender's - estimate of the amount of time since the response (or its - revalidation) was generated at the origin server. - - Age values are non-negative decimal integers, representing time - in seconds.""", - ) - content_type = header_property[str]( - "Content-Type", - doc="""The Content-Type entity-header field indicates the media - type of the entity-body sent to the recipient or, in the case of - the HEAD method, the media type that would have been sent had - the request been a GET.""", - ) - content_length = header_property( - "Content-Length", - None, - int, - str, - doc="""The Content-Length entity-header field indicates the size - of the entity-body, in decimal number of OCTETs, sent to the - recipient or, in the case of the HEAD method, the size of the - entity-body that would have been sent had the request been a - GET.""", - ) - content_location = header_property[str]( - "Content-Location", - doc="""The Content-Location entity-header field MAY be used to - supply the resource location for the entity enclosed in the - message when that entity is accessible from a location separate - from the requested resource's URI.""", - ) - content_encoding = header_property[str]( - "Content-Encoding", - doc="""The Content-Encoding entity-header field is used as a - modifier to the media-type. When present, its value indicates - what additional content codings have been applied to the - entity-body, and thus what decoding mechanisms must be applied - in order to obtain the media-type referenced by the Content-Type - header field.""", - ) - content_md5 = header_property[str]( - "Content-MD5", - doc="""The Content-MD5 entity-header field, as defined in - RFC 1864, is an MD5 digest of the entity-body for the purpose of - providing an end-to-end message integrity check (MIC) of the - entity-body. (Note: a MIC is good for detecting accidental - modification of the entity-body in transit, but is not proof - against malicious attacks.)""", - ) - date = header_property( - "Date", - None, - parse_date, - http_date, - doc="""The Date general-header field represents the date and - time at which the message was originated, having the same - semantics as orig-date in RFC 822. - - .. versionchanged:: 2.0 - The datetime object is timezone-aware. - """, - ) - expires = header_property( - "Expires", - None, - parse_date, - http_date, - doc="""The Expires entity-header field gives the date/time after - which the response is considered stale. A stale cache entry may - not normally be returned by a cache. - - .. versionchanged:: 2.0 - The datetime object is timezone-aware. - """, - ) - last_modified = header_property( - "Last-Modified", - None, - parse_date, - http_date, - doc="""The Last-Modified entity-header field indicates the date - and time at which the origin server believes the variant was - last modified. - - .. versionchanged:: 2.0 - The datetime object is timezone-aware. - """, - ) - - @property - def retry_after(self) -> datetime | None: - """The Retry-After response-header field can be used with a - 503 (Service Unavailable) response to indicate how long the - service is expected to be unavailable to the requesting client. - - Time in seconds until expiration or date. - - .. versionchanged:: 2.0 - The datetime object is timezone-aware. - """ - value = self.headers.get("retry-after") - if value is None: - return None - - try: - seconds = int(value) - except ValueError: - return parse_date(value) - - return datetime.now(timezone.utc) + timedelta(seconds=seconds) - - @retry_after.setter - def retry_after(self, value: datetime | int | str | None) -> None: - if value is None: - if "retry-after" in self.headers: - del self.headers["retry-after"] - return - elif isinstance(value, datetime): - value = http_date(value) - else: - value = str(value) - self.headers["Retry-After"] = value - - vary = _set_property( - "Vary", - doc="""The Vary field value indicates the set of request-header - fields that fully determines, while the response is fresh, - whether a cache is permitted to use the response to reply to a - subsequent request without revalidation.""", - ) - content_language = _set_property( - "Content-Language", - doc="""The Content-Language entity-header field describes the - natural language(s) of the intended audience for the enclosed - entity. Note that this might not be equivalent to all the - languages used within the entity-body.""", - ) - allow = _set_property( - "Allow", - doc="""The Allow entity-header field lists the set of methods - supported by the resource identified by the Request-URI. The - purpose of this field is strictly to inform the recipient of - valid methods associated with the resource. An Allow header - field MUST be present in a 405 (Method Not Allowed) - response.""", - ) - - # ETag - - @property - def cache_control(self) -> ResponseCacheControl: - """The Cache-Control general-header field is used to specify - directives that MUST be obeyed by all caching mechanisms along the - request/response chain. - """ - - def on_update(cache_control: _CacheControl) -> None: - if not cache_control and "cache-control" in self.headers: - del self.headers["cache-control"] - elif cache_control: - self.headers["Cache-Control"] = cache_control.to_header() - - return parse_cache_control_header( - self.headers.get("cache-control"), on_update, ResponseCacheControl - ) - - def set_etag(self, etag: str, weak: bool = False) -> None: - """Set the etag, and override the old one if there was one.""" - self.headers["ETag"] = quote_etag(etag, weak) - - def get_etag(self) -> tuple[str, bool] | tuple[None, None]: - """Return a tuple in the form ``(etag, is_weak)``. If there is no - ETag the return value is ``(None, None)``. - """ - return unquote_etag(self.headers.get("ETag")) - - accept_ranges = header_property[str]( - "Accept-Ranges", - doc="""The `Accept-Ranges` header. Even though the name would - indicate that multiple values are supported, it must be one - string token only. - - The values ``'bytes'`` and ``'none'`` are common. - - .. versionadded:: 0.7""", - ) - - @property - def content_range(self) -> ContentRange: - """The ``Content-Range`` header as a - :class:`~werkzeug.datastructures.ContentRange` object. Available - even if the header is not set. - - .. versionadded:: 0.7 - """ - - def on_update(rng: ContentRange) -> None: - if not rng: - del self.headers["content-range"] - else: - self.headers["Content-Range"] = rng.to_header() - - rv = parse_content_range_header(self.headers.get("content-range"), on_update) - # always provide a content range object to make the descriptor - # more user friendly. It provides an unset() method that can be - # used to remove the header quickly. - if rv is None: - rv = ContentRange(None, None, None, on_update=on_update) - return rv - - @content_range.setter - def content_range(self, value: ContentRange | str | None) -> None: - if not value: - del self.headers["content-range"] - elif isinstance(value, str): - self.headers["Content-Range"] = value - else: - self.headers["Content-Range"] = value.to_header() - - # Authorization - - @property - def www_authenticate(self) -> WWWAuthenticate: - """The ``WWW-Authenticate`` header parsed into a :class:`.WWWAuthenticate` - object. Modifying the object will modify the header value. - - This header is not set by default. To set this header, assign an instance of - :class:`.WWWAuthenticate` to this attribute. - - .. code-block:: python - - response.www_authenticate = WWWAuthenticate( - "basic", {"realm": "Authentication Required"} - ) - - Multiple values for this header can be sent to give the client multiple options. - Assign a list to set multiple headers. However, modifying the items in the list - will not automatically update the header values, and accessing this attribute - will only ever return the first value. - - To unset this header, assign ``None`` or use ``del``. - - .. versionchanged:: 2.3 - This attribute can be assigned to to set the header. A list can be assigned - to set multiple header values. Use ``del`` to unset the header. - - .. versionchanged:: 2.3 - :class:`WWWAuthenticate` is no longer a ``dict``. The ``token`` attribute - was added for auth challenges that use a token instead of parameters. - """ - value = WWWAuthenticate.from_header(self.headers.get("WWW-Authenticate")) - - if value is None: - value = WWWAuthenticate("basic") - - def on_update(value: WWWAuthenticate) -> None: - self.www_authenticate = value - - value._on_update = on_update - return value - - @www_authenticate.setter - def www_authenticate( - self, value: WWWAuthenticate | list[WWWAuthenticate] | None - ) -> None: - if not value: # None or empty list - del self.www_authenticate - elif isinstance(value, list): - # Clear any existing header by setting the first item. - self.headers.set("WWW-Authenticate", value[0].to_header()) - - for item in value[1:]: - # Add additional header lines for additional items. - self.headers.add("WWW-Authenticate", item.to_header()) - else: - self.headers.set("WWW-Authenticate", value.to_header()) - - def on_update(value: WWWAuthenticate) -> None: - self.www_authenticate = value - - # When setting a single value, allow updating it directly. - value._on_update = on_update - - @www_authenticate.deleter - def www_authenticate(self) -> None: - if "WWW-Authenticate" in self.headers: - del self.headers["WWW-Authenticate"] - - # CSP - - @property - def content_security_policy(self) -> ContentSecurityPolicy: - """The ``Content-Security-Policy`` header as a - :class:`~werkzeug.datastructures.ContentSecurityPolicy` object. Available - even if the header is not set. - - The Content-Security-Policy header adds an additional layer of - security to help detect and mitigate certain types of attacks. - """ - - def on_update(csp: ContentSecurityPolicy) -> None: - if not csp: - del self.headers["content-security-policy"] - else: - self.headers["Content-Security-Policy"] = csp.to_header() - - rv = parse_csp_header(self.headers.get("content-security-policy"), on_update) - if rv is None: - rv = ContentSecurityPolicy(None, on_update=on_update) - return rv - - @content_security_policy.setter - def content_security_policy( - self, value: ContentSecurityPolicy | str | None - ) -> None: - if not value: - del self.headers["content-security-policy"] - elif isinstance(value, str): - self.headers["Content-Security-Policy"] = value - else: - self.headers["Content-Security-Policy"] = value.to_header() - - @property - def content_security_policy_report_only(self) -> ContentSecurityPolicy: - """The ``Content-Security-policy-report-only`` header as a - :class:`~werkzeug.datastructures.ContentSecurityPolicy` object. Available - even if the header is not set. - - The Content-Security-Policy-Report-Only header adds a csp policy - that is not enforced but is reported thereby helping detect - certain types of attacks. - """ - - def on_update(csp: ContentSecurityPolicy) -> None: - if not csp: - del self.headers["content-security-policy-report-only"] - else: - self.headers["Content-Security-policy-report-only"] = csp.to_header() - - rv = parse_csp_header( - self.headers.get("content-security-policy-report-only"), on_update - ) - if rv is None: - rv = ContentSecurityPolicy(None, on_update=on_update) - return rv - - @content_security_policy_report_only.setter - def content_security_policy_report_only( - self, value: ContentSecurityPolicy | str | None - ) -> None: - if not value: - del self.headers["content-security-policy-report-only"] - elif isinstance(value, str): - self.headers["Content-Security-policy-report-only"] = value - else: - self.headers["Content-Security-policy-report-only"] = value.to_header() - - # CORS - - @property - def access_control_allow_credentials(self) -> bool: - """Whether credentials can be shared by the browser to - JavaScript code. As part of the preflight request it indicates - whether credentials can be used on the cross origin request. - """ - return "Access-Control-Allow-Credentials" in self.headers - - @access_control_allow_credentials.setter - def access_control_allow_credentials(self, value: bool | None) -> None: - if value is True: - self.headers["Access-Control-Allow-Credentials"] = "true" - else: - self.headers.pop("Access-Control-Allow-Credentials", None) - - access_control_allow_headers = header_property( - "Access-Control-Allow-Headers", - load_func=parse_set_header, - dump_func=dump_header, - doc="Which headers can be sent with the cross origin request.", - ) - - access_control_allow_methods = header_property( - "Access-Control-Allow-Methods", - load_func=parse_set_header, - dump_func=dump_header, - doc="Which methods can be used for the cross origin request.", - ) - - access_control_allow_origin = header_property[str]( - "Access-Control-Allow-Origin", - doc="The origin or '*' for any origin that may make cross origin requests.", - ) - - access_control_expose_headers = header_property( - "Access-Control-Expose-Headers", - load_func=parse_set_header, - dump_func=dump_header, - doc="Which headers can be shared by the browser to JavaScript code.", - ) - - access_control_max_age = header_property( - "Access-Control-Max-Age", - load_func=int, - dump_func=str, - doc="The maximum age in seconds the access control settings can be cached for.", - ) - - cross_origin_opener_policy = header_property[COOP]( - "Cross-Origin-Opener-Policy", - load_func=lambda value: COOP(value), - dump_func=lambda value: value.value, - default=COOP.UNSAFE_NONE, - doc="""Allows control over sharing of browsing context group with cross-origin - documents. Values must be a member of the :class:`werkzeug.http.COOP` enum.""", - ) - - cross_origin_embedder_policy = header_property[COEP]( - "Cross-Origin-Embedder-Policy", - load_func=lambda value: COEP(value), - dump_func=lambda value: value.value, - default=COEP.UNSAFE_NONE, - doc="""Prevents a document from loading any cross-origin resources that do not - explicitly grant the document permission. Values must be a member of the - :class:`werkzeug.http.COEP` enum.""", - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/utils.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/utils.py deleted file mode 100644 index 14fa0ac..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/sansio/utils.py +++ /dev/null @@ -1,159 +0,0 @@ -from __future__ import annotations - -import typing as t -from urllib.parse import quote - -from .._internal import _plain_int -from ..exceptions import SecurityError -from ..urls import uri_to_iri - - -def host_is_trusted(hostname: str | None, trusted_list: t.Iterable[str]) -> bool: - """Check if a host matches a list of trusted names. - - :param hostname: The name to check. - :param trusted_list: A list of valid names to match. If a name - starts with a dot it will match all subdomains. - - .. versionadded:: 0.9 - """ - if not hostname: - return False - - try: - hostname = hostname.partition(":")[0].encode("idna").decode("ascii") - except UnicodeEncodeError: - return False - - if isinstance(trusted_list, str): - trusted_list = [trusted_list] - - for ref in trusted_list: - if ref.startswith("."): - ref = ref[1:] - suffix_match = True - else: - suffix_match = False - - try: - ref = ref.partition(":")[0].encode("idna").decode("ascii") - except UnicodeEncodeError: - return False - - if ref == hostname or (suffix_match and hostname.endswith(f".{ref}")): - return True - - return False - - -def get_host( - scheme: str, - host_header: str | None, - server: tuple[str, int | None] | None = None, - trusted_hosts: t.Iterable[str] | None = None, -) -> str: - """Return the host for the given parameters. - - This first checks the ``host_header``. If it's not present, then - ``server`` is used. The host will only contain the port if it is - different than the standard port for the protocol. - - Optionally, verify that the host is trusted using - :func:`host_is_trusted` and raise a - :exc:`~werkzeug.exceptions.SecurityError` if it is not. - - :param scheme: The protocol the request used, like ``"https"``. - :param host_header: The ``Host`` header value. - :param server: Address of the server. ``(host, port)``, or - ``(path, None)`` for unix sockets. - :param trusted_hosts: A list of trusted host names. - - :return: Host, with port if necessary. - :raise ~werkzeug.exceptions.SecurityError: If the host is not - trusted. - """ - host = "" - - if host_header is not None: - host = host_header - elif server is not None: - host = server[0] - - if server[1] is not None: - host = f"{host}:{server[1]}" - - if scheme in {"http", "ws"} and host.endswith(":80"): - host = host[:-3] - elif scheme in {"https", "wss"} and host.endswith(":443"): - host = host[:-4] - - if trusted_hosts is not None: - if not host_is_trusted(host, trusted_hosts): - raise SecurityError(f"Host {host!r} is not trusted.") - - return host - - -def get_current_url( - scheme: str, - host: str, - root_path: str | None = None, - path: str | None = None, - query_string: bytes | None = None, -) -> str: - """Recreate the URL for a request. If an optional part isn't - provided, it and subsequent parts are not included in the URL. - - The URL is an IRI, not a URI, so it may contain Unicode characters. - Use :func:`~werkzeug.urls.iri_to_uri` to convert it to ASCII. - - :param scheme: The protocol the request used, like ``"https"``. - :param host: The host the request was made to. See :func:`get_host`. - :param root_path: Prefix that the application is mounted under. This - is prepended to ``path``. - :param path: The path part of the URL after ``root_path``. - :param query_string: The portion of the URL after the "?". - """ - url = [scheme, "://", host] - - if root_path is None: - url.append("/") - return uri_to_iri("".join(url)) - - # safe = https://url.spec.whatwg.org/#url-path-segment-string - # as well as percent for things that are already quoted - url.append(quote(root_path.rstrip("/"), safe="!$&'()*+,/:;=@%")) - url.append("/") - - if path is None: - return uri_to_iri("".join(url)) - - url.append(quote(path.lstrip("/"), safe="!$&'()*+,/:;=@%")) - - if query_string: - url.append("?") - url.append(quote(query_string, safe="!$&'()*+,/:;=?@%")) - - return uri_to_iri("".join(url)) - - -def get_content_length( - http_content_length: str | None = None, - http_transfer_encoding: str | None = None, -) -> int | None: - """Return the ``Content-Length`` header value as an int. If the header is not given - or the ``Transfer-Encoding`` header is ``chunked``, ``None`` is returned to indicate - a streaming request. If the value is not an integer, or negative, 0 is returned. - - :param http_content_length: The Content-Length HTTP header. - :param http_transfer_encoding: The Transfer-Encoding HTTP header. - - .. versionadded:: 2.2 - """ - if http_transfer_encoding == "chunked" or http_content_length is None: - return None - - try: - return max(0, _plain_int(http_content_length)) - except ValueError: - return 0 diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/security.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/security.py deleted file mode 100644 index 9975979..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/security.py +++ /dev/null @@ -1,163 +0,0 @@ -from __future__ import annotations - -import hashlib -import hmac -import os -import posixpath -import secrets - -SALT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" -DEFAULT_PBKDF2_ITERATIONS = 600000 - -_os_alt_seps: list[str] = list( - sep for sep in [os.sep, os.path.altsep] if sep is not None and sep != "/" -) - - -def gen_salt(length: int) -> str: - """Generate a random string of SALT_CHARS with specified ``length``.""" - if length <= 0: - raise ValueError("Salt length must be at least 1.") - - return "".join(secrets.choice(SALT_CHARS) for _ in range(length)) - - -def _hash_internal(method: str, salt: str, password: str) -> tuple[str, str]: - method, *args = method.split(":") - salt_bytes = salt.encode() - password_bytes = password.encode() - - if method == "scrypt": - if not args: - n = 2**15 - r = 8 - p = 1 - else: - try: - n, r, p = map(int, args) - except ValueError: - raise ValueError("'scrypt' takes 3 arguments.") from None - - maxmem = 132 * n * r * p # ideally 128, but some extra seems needed - return ( - hashlib.scrypt( - password_bytes, salt=salt_bytes, n=n, r=r, p=p, maxmem=maxmem - ).hex(), - f"scrypt:{n}:{r}:{p}", - ) - elif method == "pbkdf2": - len_args = len(args) - - if len_args == 0: - hash_name = "sha256" - iterations = DEFAULT_PBKDF2_ITERATIONS - elif len_args == 1: - hash_name = args[0] - iterations = DEFAULT_PBKDF2_ITERATIONS - elif len_args == 2: - hash_name = args[0] - iterations = int(args[1]) - else: - raise ValueError("'pbkdf2' takes 2 arguments.") - - return ( - hashlib.pbkdf2_hmac( - hash_name, password_bytes, salt_bytes, iterations - ).hex(), - f"pbkdf2:{hash_name}:{iterations}", - ) - else: - raise ValueError(f"Invalid hash method '{method}'.") - - -def generate_password_hash( - password: str, method: str = "scrypt", salt_length: int = 16 -) -> str: - """Securely hash a password for storage. A password can be compared to a stored hash - using :func:`check_password_hash`. - - The following methods are supported: - - - ``scrypt``, the default. The parameters are ``n``, ``r``, and ``p``, the default - is ``scrypt:32768:8:1``. See :func:`hashlib.scrypt`. - - ``pbkdf2``, less secure. The parameters are ``hash_method`` and ``iterations``, - the default is ``pbkdf2:sha256:600000``. See :func:`hashlib.pbkdf2_hmac`. - - Default parameters may be updated to reflect current guidelines, and methods may be - deprecated and removed if they are no longer considered secure. To migrate old - hashes, you may generate a new hash when checking an old hash, or you may contact - users with a link to reset their password. - - :param password: The plaintext password. - :param method: The key derivation function and parameters. - :param salt_length: The number of characters to generate for the salt. - - .. versionchanged:: 2.3 - Scrypt support was added. - - .. versionchanged:: 2.3 - The default iterations for pbkdf2 was increased to 600,000. - - .. versionchanged:: 2.3 - All plain hashes are deprecated and will not be supported in Werkzeug 3.0. - """ - salt = gen_salt(salt_length) - h, actual_method = _hash_internal(method, salt, password) - return f"{actual_method}${salt}${h}" - - -def check_password_hash(pwhash: str, password: str) -> bool: - """Securely check that the given stored password hash, previously generated using - :func:`generate_password_hash`, matches the given password. - - Methods may be deprecated and removed if they are no longer considered secure. To - migrate old hashes, you may generate a new hash when checking an old hash, or you - may contact users with a link to reset their password. - - :param pwhash: The hashed password. - :param password: The plaintext password. - - .. versionchanged:: 2.3 - All plain hashes are deprecated and will not be supported in Werkzeug 3.0. - """ - try: - method, salt, hashval = pwhash.split("$", 2) - except ValueError: - return False - - return hmac.compare_digest(_hash_internal(method, salt, password)[0], hashval) - - -def safe_join(directory: str, *pathnames: str) -> str | None: - """Safely join zero or more untrusted path components to a base - directory to avoid escaping the base directory. - - :param directory: The trusted base directory. - :param pathnames: The untrusted path components relative to the - base directory. - :return: A safe path, otherwise ``None``. - """ - if not directory: - # Ensure we end up with ./path if directory="" is given, - # otherwise the first untrusted part could become trusted. - directory = "." - - parts = [directory] - - for filename in pathnames: - if filename != "": - filename = posixpath.normpath(filename) - - if ( - any(sep in filename for sep in _os_alt_seps) - or os.path.isabs(filename) - # ntpath.isabs doesn't catch this on Python < 3.11 - or filename.startswith("/") - or filename == ".." - or filename.startswith("../") - ): - return None - - parts.append(filename) - - return posixpath.join(*parts) diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/serving.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/serving.py deleted file mode 100644 index ef32b88..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/serving.py +++ /dev/null @@ -1,1125 +0,0 @@ -"""A WSGI and HTTP server for use **during development only**. This -server is convenient to use, but is not designed to be particularly -stable, secure, or efficient. Use a dedicate WSGI server and HTTP -server when deploying to production. - -It provides features like interactive debugging and code reloading. Use -``run_simple`` to start the server. Put this in a ``run.py`` script: - -.. code-block:: python - - from myapp import create_app - from werkzeug import run_simple -""" - -from __future__ import annotations - -import errno -import io -import os -import selectors -import socket -import socketserver -import sys -import typing as t -from datetime import datetime as dt -from datetime import timedelta -from datetime import timezone -from http.server import BaseHTTPRequestHandler -from http.server import HTTPServer -from urllib.parse import unquote -from urllib.parse import urlsplit - -from ._internal import _log -from ._internal import _wsgi_encoding_dance -from .exceptions import InternalServerError -from .urls import uri_to_iri - -try: - import ssl - - connection_dropped_errors: tuple[type[Exception], ...] = ( - ConnectionError, - socket.timeout, - ssl.SSLEOFError, - ) -except ImportError: - - class _SslDummy: - def __getattr__(self, name: str) -> t.Any: - raise RuntimeError( # noqa: B904 - "SSL is unavailable because this Python runtime was not" - " compiled with SSL/TLS support." - ) - - ssl = _SslDummy() # type: ignore - connection_dropped_errors = (ConnectionError, socket.timeout) - -_log_add_style = True - -if os.name == "nt": - try: - __import__("colorama") - except ImportError: - _log_add_style = False - -can_fork = hasattr(os, "fork") - -if can_fork: - ForkingMixIn = socketserver.ForkingMixIn -else: - - class ForkingMixIn: # type: ignore - pass - - -try: - af_unix = socket.AF_UNIX -except AttributeError: - af_unix = None # type: ignore - -LISTEN_QUEUE = 128 - -_TSSLContextArg = t.Optional[ - t.Union["ssl.SSLContext", t.Tuple[str, t.Optional[str]], t.Literal["adhoc"]] -] - -if t.TYPE_CHECKING: - from _typeshed.wsgi import WSGIApplication - from _typeshed.wsgi import WSGIEnvironment - from cryptography.hazmat.primitives.asymmetric.rsa import ( - RSAPrivateKeyWithSerialization, - ) - from cryptography.x509 import Certificate - - -class DechunkedInput(io.RawIOBase): - """An input stream that handles Transfer-Encoding 'chunked'""" - - def __init__(self, rfile: t.IO[bytes]) -> None: - self._rfile = rfile - self._done = False - self._len = 0 - - def readable(self) -> bool: - return True - - def read_chunk_len(self) -> int: - try: - line = self._rfile.readline().decode("latin1") - _len = int(line.strip(), 16) - except ValueError as e: - raise OSError("Invalid chunk header") from e - if _len < 0: - raise OSError("Negative chunk length not allowed") - return _len - - def readinto(self, buf: bytearray) -> int: # type: ignore - read = 0 - while not self._done and read < len(buf): - if self._len == 0: - # This is the first chunk or we fully consumed the previous - # one. Read the next length of the next chunk - self._len = self.read_chunk_len() - - if self._len == 0: - # Found the final chunk of size 0. The stream is now exhausted, - # but there is still a final newline that should be consumed - self._done = True - - if self._len > 0: - # There is data (left) in this chunk, so append it to the - # buffer. If this operation fully consumes the chunk, this will - # reset self._len to 0. - n = min(len(buf), self._len) - - # If (read + chunk size) becomes more than len(buf), buf will - # grow beyond the original size and read more data than - # required. So only read as much data as can fit in buf. - if read + n > len(buf): - buf[read:] = self._rfile.read(len(buf) - read) - self._len -= len(buf) - read - read = len(buf) - else: - buf[read : read + n] = self._rfile.read(n) - self._len -= n - read += n - - if self._len == 0: - # Skip the terminating newline of a chunk that has been fully - # consumed. This also applies to the 0-sized final chunk - terminator = self._rfile.readline() - if terminator not in (b"\n", b"\r\n", b"\r"): - raise OSError("Missing chunk terminating newline") - - return read - - -class WSGIRequestHandler(BaseHTTPRequestHandler): - """A request handler that implements WSGI dispatching.""" - - server: BaseWSGIServer - - @property - def server_version(self) -> str: # type: ignore - return self.server._server_version - - def make_environ(self) -> WSGIEnvironment: - request_url = urlsplit(self.path) - url_scheme = "http" if self.server.ssl_context is None else "https" - - if not self.client_address: - self.client_address = ("", 0) - elif isinstance(self.client_address, str): - self.client_address = (self.client_address, 0) - - # If there was no scheme but the path started with two slashes, - # the first segment may have been incorrectly parsed as the - # netloc, prepend it to the path again. - if not request_url.scheme and request_url.netloc: - path_info = f"/{request_url.netloc}{request_url.path}" - else: - path_info = request_url.path - - path_info = unquote(path_info) - - environ: WSGIEnvironment = { - "wsgi.version": (1, 0), - "wsgi.url_scheme": url_scheme, - "wsgi.input": self.rfile, - "wsgi.errors": sys.stderr, - "wsgi.multithread": self.server.multithread, - "wsgi.multiprocess": self.server.multiprocess, - "wsgi.run_once": False, - "werkzeug.socket": self.connection, - "SERVER_SOFTWARE": self.server_version, - "REQUEST_METHOD": self.command, - "SCRIPT_NAME": "", - "PATH_INFO": _wsgi_encoding_dance(path_info), - "QUERY_STRING": _wsgi_encoding_dance(request_url.query), - # Non-standard, added by mod_wsgi, uWSGI - "REQUEST_URI": _wsgi_encoding_dance(self.path), - # Non-standard, added by gunicorn - "RAW_URI": _wsgi_encoding_dance(self.path), - "REMOTE_ADDR": self.address_string(), - "REMOTE_PORT": self.port_integer(), - "SERVER_NAME": self.server.server_address[0], - "SERVER_PORT": str(self.server.server_address[1]), - "SERVER_PROTOCOL": self.request_version, - } - - for key, value in self.headers.items(): - if "_" in key: - continue - - key = key.upper().replace("-", "_") - value = value.replace("\r\n", "") - if key not in ("CONTENT_TYPE", "CONTENT_LENGTH"): - key = f"HTTP_{key}" - if key in environ: - value = f"{environ[key]},{value}" - environ[key] = value - - if environ.get("HTTP_TRANSFER_ENCODING", "").strip().lower() == "chunked": - environ["wsgi.input_terminated"] = True - environ["wsgi.input"] = DechunkedInput(environ["wsgi.input"]) - - # Per RFC 2616, if the URL is absolute, use that as the host. - # We're using "has a scheme" to indicate an absolute URL. - if request_url.scheme and request_url.netloc: - environ["HTTP_HOST"] = request_url.netloc - - try: - # binary_form=False gives nicer information, but wouldn't be compatible with - # what Nginx or Apache could return. - peer_cert = self.connection.getpeercert(binary_form=True) - if peer_cert is not None: - # Nginx and Apache use PEM format. - environ["SSL_CLIENT_CERT"] = ssl.DER_cert_to_PEM_cert(peer_cert) - except ValueError: - # SSL handshake hasn't finished. - self.server.log("error", "Cannot fetch SSL peer certificate info") - except AttributeError: - # Not using TLS, the socket will not have getpeercert(). - pass - - return environ - - def run_wsgi(self) -> None: - if self.headers.get("Expect", "").lower().strip() == "100-continue": - self.wfile.write(b"HTTP/1.1 100 Continue\r\n\r\n") - - self.environ = environ = self.make_environ() - status_set: str | None = None - headers_set: list[tuple[str, str]] | None = None - status_sent: str | None = None - headers_sent: list[tuple[str, str]] | None = None - chunk_response: bool = False - - def write(data: bytes) -> None: - nonlocal status_sent, headers_sent, chunk_response - assert status_set is not None, "write() before start_response" - assert headers_set is not None, "write() before start_response" - if status_sent is None: - status_sent = status_set - headers_sent = headers_set - try: - code_str, msg = status_sent.split(None, 1) - except ValueError: - code_str, msg = status_sent, "" - code = int(code_str) - self.send_response(code, msg) - header_keys = set() - for key, value in headers_sent: - self.send_header(key, value) - header_keys.add(key.lower()) - - # Use chunked transfer encoding if there is no content - # length. Do not use for 1xx and 204 responses. 304 - # responses and HEAD requests are also excluded, which - # is the more conservative behavior and matches other - # parts of the code. - # https://httpwg.org/specs/rfc7230.html#rfc.section.3.3.1 - if ( - not ( - "content-length" in header_keys - or environ["REQUEST_METHOD"] == "HEAD" - or (100 <= code < 200) - or code in {204, 304} - ) - and self.protocol_version >= "HTTP/1.1" - ): - chunk_response = True - self.send_header("Transfer-Encoding", "chunked") - - # Always close the connection. This disables HTTP/1.1 - # keep-alive connections. They aren't handled well by - # Python's http.server because it doesn't know how to - # drain the stream before the next request line. - self.send_header("Connection", "close") - self.end_headers() - - assert isinstance(data, bytes), "applications must write bytes" - - if data: - if chunk_response: - self.wfile.write(hex(len(data))[2:].encode()) - self.wfile.write(b"\r\n") - - self.wfile.write(data) - - if chunk_response: - self.wfile.write(b"\r\n") - - self.wfile.flush() - - def start_response(status, headers, exc_info=None): # type: ignore - nonlocal status_set, headers_set - if exc_info: - try: - if headers_sent: - raise exc_info[1].with_traceback(exc_info[2]) - finally: - exc_info = None - elif headers_set: - raise AssertionError("Headers already set") - status_set = status - headers_set = headers - return write - - def execute(app: WSGIApplication) -> None: - application_iter = app(environ, start_response) - try: - for data in application_iter: - write(data) - if not headers_sent: - write(b"") - if chunk_response: - self.wfile.write(b"0\r\n\r\n") - finally: - # Check for any remaining data in the read socket, and discard it. This - # will read past request.max_content_length, but lets the client see a - # 413 response instead of a connection reset failure. If we supported - # keep-alive connections, this naive approach would break by reading the - # next request line. Since we know that write (above) closes every - # connection we can read everything. - selector = selectors.DefaultSelector() - selector.register(self.connection, selectors.EVENT_READ) - total_size = 0 - total_reads = 0 - - # A timeout of 0 tends to fail because a client needs a small amount of - # time to continue sending its data. - while selector.select(timeout=0.01): - # Only read 10MB into memory at a time. - data = self.rfile.read(10_000_000) - total_size += len(data) - total_reads += 1 - - # Stop reading on no data, >=10GB, or 1000 reads. If a client sends - # more than that, they'll get a connection reset failure. - if not data or total_size >= 10_000_000_000 or total_reads > 1000: - break - - selector.close() - - if hasattr(application_iter, "close"): - application_iter.close() - - try: - execute(self.server.app) - except connection_dropped_errors as e: - self.connection_dropped(e, environ) - except Exception as e: - if self.server.passthrough_errors: - raise - - if status_sent is not None and chunk_response: - self.close_connection = True - - try: - # if we haven't yet sent the headers but they are set - # we roll back to be able to set them again. - if status_sent is None: - status_set = None - headers_set = None - execute(InternalServerError()) - except Exception: - pass - - from .debug.tbtools import DebugTraceback - - msg = DebugTraceback(e).render_traceback_text() - self.server.log("error", f"Error on request:\n{msg}") - - def handle(self) -> None: - """Handles a request ignoring dropped connections.""" - try: - super().handle() - except (ConnectionError, socket.timeout) as e: - self.connection_dropped(e) - except Exception as e: - if self.server.ssl_context is not None and is_ssl_error(e): - self.log_error("SSL error occurred: %s", e) - else: - raise - - def connection_dropped( - self, error: BaseException, environ: WSGIEnvironment | None = None - ) -> None: - """Called if the connection was closed by the client. By default - nothing happens. - """ - - def __getattr__(self, name: str) -> t.Any: - # All HTTP methods are handled by run_wsgi. - if name.startswith("do_"): - return self.run_wsgi - - # All other attributes are forwarded to the base class. - return getattr(super(), name) - - def address_string(self) -> str: - if getattr(self, "environ", None): - return self.environ["REMOTE_ADDR"] # type: ignore - - if not self.client_address: - return "" - - return self.client_address[0] - - def port_integer(self) -> int: - return self.client_address[1] - - # Escape control characters. This is defined (but private) in Python 3.12. - _control_char_table = str.maketrans( - {c: rf"\x{c:02x}" for c in [*range(0x20), *range(0x7F, 0xA0)]} - ) - _control_char_table[ord("\\")] = r"\\" - - def log_request(self, code: int | str = "-", size: int | str = "-") -> None: - try: - path = uri_to_iri(self.path) - msg = f"{self.command} {path} {self.request_version}" - except AttributeError: - # path isn't set if the requestline was bad - msg = self.requestline - - # Escape control characters that may be in the decoded path. - msg = msg.translate(self._control_char_table) - code = str(code) - - if code[0] == "1": # 1xx - Informational - msg = _ansi_style(msg, "bold") - elif code == "200": # 2xx - Success - pass - elif code == "304": # 304 - Resource Not Modified - msg = _ansi_style(msg, "cyan") - elif code[0] == "3": # 3xx - Redirection - msg = _ansi_style(msg, "green") - elif code == "404": # 404 - Resource Not Found - msg = _ansi_style(msg, "yellow") - elif code[0] == "4": # 4xx - Client Error - msg = _ansi_style(msg, "bold", "red") - else: # 5xx, or any other response - msg = _ansi_style(msg, "bold", "magenta") - - self.log("info", '"%s" %s %s', msg, code, size) - - def log_error(self, format: str, *args: t.Any) -> None: - self.log("error", format, *args) - - def log_message(self, format: str, *args: t.Any) -> None: - self.log("info", format, *args) - - def log(self, type: str, message: str, *args: t.Any) -> None: - # an IPv6 scoped address contains "%" which breaks logging - address_string = self.address_string().replace("%", "%%") - _log( - type, - f"{address_string} - - [{self.log_date_time_string()}] {message}\n", - *args, - ) - - -def _ansi_style(value: str, *styles: str) -> str: - if not _log_add_style: - return value - - codes = { - "bold": 1, - "red": 31, - "green": 32, - "yellow": 33, - "magenta": 35, - "cyan": 36, - } - - for style in styles: - value = f"\x1b[{codes[style]}m{value}" - - return f"{value}\x1b[0m" - - -def generate_adhoc_ssl_pair( - cn: str | None = None, -) -> tuple[Certificate, RSAPrivateKeyWithSerialization]: - try: - from cryptography import x509 - from cryptography.hazmat.backends import default_backend - from cryptography.hazmat.primitives import hashes - from cryptography.hazmat.primitives.asymmetric import rsa - from cryptography.x509.oid import NameOID - except ImportError: - raise TypeError( - "Using ad-hoc certificates requires the cryptography library." - ) from None - - backend = default_backend() - pkey = rsa.generate_private_key( - public_exponent=65537, key_size=2048, backend=backend - ) - - # pretty damn sure that this is not actually accepted by anyone - if cn is None: - cn = "*" - - subject = x509.Name( - [ - x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Dummy Certificate"), - x509.NameAttribute(NameOID.COMMON_NAME, cn), - ] - ) - - backend = default_backend() - cert = ( - x509.CertificateBuilder() - .subject_name(subject) - .issuer_name(subject) - .public_key(pkey.public_key()) - .serial_number(x509.random_serial_number()) - .not_valid_before(dt.now(timezone.utc)) - .not_valid_after(dt.now(timezone.utc) + timedelta(days=365)) - .add_extension(x509.ExtendedKeyUsage([x509.OID_SERVER_AUTH]), critical=False) - .add_extension( - x509.SubjectAlternativeName([x509.DNSName(cn), x509.DNSName(f"*.{cn}")]), - critical=False, - ) - .sign(pkey, hashes.SHA256(), backend) - ) - return cert, pkey - - -def make_ssl_devcert( - base_path: str, host: str | None = None, cn: str | None = None -) -> tuple[str, str]: - """Creates an SSL key for development. This should be used instead of - the ``'adhoc'`` key which generates a new cert on each server start. - It accepts a path for where it should store the key and cert and - either a host or CN. If a host is given it will use the CN - ``*.host/CN=host``. - - For more information see :func:`run_simple`. - - .. versionadded:: 0.9 - - :param base_path: the path to the certificate and key. The extension - ``.crt`` is added for the certificate, ``.key`` is - added for the key. - :param host: the name of the host. This can be used as an alternative - for the `cn`. - :param cn: the `CN` to use. - """ - - if host is not None: - cn = host - cert, pkey = generate_adhoc_ssl_pair(cn=cn) - - from cryptography.hazmat.primitives import serialization - - cert_file = f"{base_path}.crt" - pkey_file = f"{base_path}.key" - - with open(cert_file, "wb") as f: - f.write(cert.public_bytes(serialization.Encoding.PEM)) - with open(pkey_file, "wb") as f: - f.write( - pkey.private_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.TraditionalOpenSSL, - encryption_algorithm=serialization.NoEncryption(), - ) - ) - - return cert_file, pkey_file - - -def generate_adhoc_ssl_context() -> ssl.SSLContext: - """Generates an adhoc SSL context for the development server.""" - import atexit - import tempfile - - cert, pkey = generate_adhoc_ssl_pair() - - from cryptography.hazmat.primitives import serialization - - cert_handle, cert_file = tempfile.mkstemp() - pkey_handle, pkey_file = tempfile.mkstemp() - atexit.register(os.remove, pkey_file) - atexit.register(os.remove, cert_file) - - os.write(cert_handle, cert.public_bytes(serialization.Encoding.PEM)) - os.write( - pkey_handle, - pkey.private_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.TraditionalOpenSSL, - encryption_algorithm=serialization.NoEncryption(), - ), - ) - - os.close(cert_handle) - os.close(pkey_handle) - ctx = load_ssl_context(cert_file, pkey_file) - return ctx - - -def load_ssl_context( - cert_file: str, pkey_file: str | None = None, protocol: int | None = None -) -> ssl.SSLContext: - """Loads SSL context from cert/private key files and optional protocol. - Many parameters are directly taken from the API of - :py:class:`ssl.SSLContext`. - - :param cert_file: Path of the certificate to use. - :param pkey_file: Path of the private key to use. If not given, the key - will be obtained from the certificate file. - :param protocol: A ``PROTOCOL`` constant from the :mod:`ssl` module. - Defaults to :data:`ssl.PROTOCOL_TLS_SERVER`. - """ - if protocol is None: - protocol = ssl.PROTOCOL_TLS_SERVER - - ctx = ssl.SSLContext(protocol) - ctx.load_cert_chain(cert_file, pkey_file) - return ctx - - -def is_ssl_error(error: Exception | None = None) -> bool: - """Checks if the given error (or the current one) is an SSL error.""" - if error is None: - error = t.cast(Exception, sys.exc_info()[1]) - return isinstance(error, ssl.SSLError) - - -def select_address_family(host: str, port: int) -> socket.AddressFamily: - """Return ``AF_INET4``, ``AF_INET6``, or ``AF_UNIX`` depending on - the host and port.""" - if host.startswith("unix://"): - return socket.AF_UNIX - elif ":" in host and hasattr(socket, "AF_INET6"): - return socket.AF_INET6 - return socket.AF_INET - - -def get_sockaddr( - host: str, port: int, family: socket.AddressFamily -) -> tuple[str, int] | str: - """Return a fully qualified socket address that can be passed to - :func:`socket.bind`.""" - if family == af_unix: - # Absolute path avoids IDNA encoding error when path starts with dot. - return os.path.abspath(host.partition("://")[2]) - try: - res = socket.getaddrinfo( - host, port, family, socket.SOCK_STREAM, socket.IPPROTO_TCP - ) - except socket.gaierror: - return host, port - return res[0][4] # type: ignore - - -def get_interface_ip(family: socket.AddressFamily) -> str: - """Get the IP address of an external interface. Used when binding to - 0.0.0.0 or ::1 to show a more useful URL. - - :meta private: - """ - # arbitrary private address - host = "fd31:f903:5ab5:1::1" if family == socket.AF_INET6 else "10.253.155.219" - - with socket.socket(family, socket.SOCK_DGRAM) as s: - try: - s.connect((host, 58162)) - except OSError: - return "::1" if family == socket.AF_INET6 else "127.0.0.1" - - return s.getsockname()[0] # type: ignore - - -class BaseWSGIServer(HTTPServer): - """A WSGI server that that handles one request at a time. - - Use :func:`make_server` to create a server instance. - """ - - multithread = False - multiprocess = False - request_queue_size = LISTEN_QUEUE - allow_reuse_address = True - - def __init__( - self, - host: str, - port: int, - app: WSGIApplication, - handler: type[WSGIRequestHandler] | None = None, - passthrough_errors: bool = False, - ssl_context: _TSSLContextArg | None = None, - fd: int | None = None, - ) -> None: - if handler is None: - handler = WSGIRequestHandler - - # If the handler doesn't directly set a protocol version and - # thread or process workers are used, then allow chunked - # responses and keep-alive connections by enabling HTTP/1.1. - if "protocol_version" not in vars(handler) and ( - self.multithread or self.multiprocess - ): - handler.protocol_version = "HTTP/1.1" - - self.host = host - self.port = port - self.app = app - self.passthrough_errors = passthrough_errors - - self.address_family = address_family = select_address_family(host, port) - server_address = get_sockaddr(host, int(port), address_family) - - # Remove a leftover Unix socket file from a previous run. Don't - # remove a file that was set up by run_simple. - if address_family == af_unix and fd is None: - server_address = t.cast(str, server_address) - - if os.path.exists(server_address): - os.unlink(server_address) - - # Bind and activate will be handled manually, it should only - # happen if we're not using a socket that was already set up. - super().__init__( - server_address, # type: ignore[arg-type] - handler, - bind_and_activate=False, - ) - - if fd is None: - # No existing socket descriptor, do bind_and_activate=True. - try: - self.server_bind() - self.server_activate() - except OSError as e: - # Catch connection issues and show them without the traceback. Show - # extra instructions for address not found, and for macOS. - self.server_close() - print(e.strerror, file=sys.stderr) - - if e.errno == errno.EADDRINUSE: - print( - f"Port {port} is in use by another program. Either identify and" - " stop that program, or start the server with a different" - " port.", - file=sys.stderr, - ) - - if sys.platform == "darwin" and port == 5000: - print( - "On macOS, try disabling the 'AirPlay Receiver' service" - " from System Preferences -> General -> AirDrop & Handoff.", - file=sys.stderr, - ) - - sys.exit(1) - except BaseException: - self.server_close() - raise - else: - # TCPServer automatically opens a socket even if bind_and_activate is False. - # Close it to silence a ResourceWarning. - self.server_close() - - # Use the passed in socket directly. - self.socket = socket.fromfd(fd, address_family, socket.SOCK_STREAM) - self.server_address = self.socket.getsockname() - - if address_family != af_unix: - # If port was 0, this will record the bound port. - self.port = self.server_address[1] - - if ssl_context is not None: - if isinstance(ssl_context, tuple): - ssl_context = load_ssl_context(*ssl_context) - elif ssl_context == "adhoc": - ssl_context = generate_adhoc_ssl_context() - - self.socket = ssl_context.wrap_socket(self.socket, server_side=True) - self.ssl_context: ssl.SSLContext | None = ssl_context - else: - self.ssl_context = None - - import importlib.metadata - - self._server_version = f"Werkzeug/{importlib.metadata.version('werkzeug')}" - - def log(self, type: str, message: str, *args: t.Any) -> None: - _log(type, message, *args) - - def serve_forever(self, poll_interval: float = 0.5) -> None: - try: - super().serve_forever(poll_interval=poll_interval) - except KeyboardInterrupt: - pass - finally: - self.server_close() - - def handle_error( - self, request: t.Any, client_address: tuple[str, int] | str - ) -> None: - if self.passthrough_errors: - raise - - return super().handle_error(request, client_address) - - def log_startup(self) -> None: - """Show information about the address when starting the server.""" - dev_warning = ( - "WARNING: This is a development server. Do not use it in a production" - " deployment. Use a production WSGI server instead." - ) - dev_warning = _ansi_style(dev_warning, "bold", "red") - messages = [dev_warning] - - if self.address_family == af_unix: - messages.append(f" * Running on {self.host}") - else: - scheme = "http" if self.ssl_context is None else "https" - display_hostname = self.host - - if self.host in {"0.0.0.0", "::"}: - messages.append(f" * Running on all addresses ({self.host})") - - if self.host == "0.0.0.0": - localhost = "127.0.0.1" - display_hostname = get_interface_ip(socket.AF_INET) - else: - localhost = "[::1]" - display_hostname = get_interface_ip(socket.AF_INET6) - - messages.append(f" * Running on {scheme}://{localhost}:{self.port}") - - if ":" in display_hostname: - display_hostname = f"[{display_hostname}]" - - messages.append(f" * Running on {scheme}://{display_hostname}:{self.port}") - - _log("info", "\n".join(messages)) - - -class ThreadedWSGIServer(socketserver.ThreadingMixIn, BaseWSGIServer): - """A WSGI server that handles concurrent requests in separate - threads. - - Use :func:`make_server` to create a server instance. - """ - - multithread = True - daemon_threads = True - - -class ForkingWSGIServer(ForkingMixIn, BaseWSGIServer): - """A WSGI server that handles concurrent requests in separate forked - processes. - - Use :func:`make_server` to create a server instance. - """ - - multiprocess = True - - def __init__( - self, - host: str, - port: int, - app: WSGIApplication, - processes: int = 40, - handler: type[WSGIRequestHandler] | None = None, - passthrough_errors: bool = False, - ssl_context: _TSSLContextArg | None = None, - fd: int | None = None, - ) -> None: - if not can_fork: - raise ValueError("Your platform does not support forking.") - - super().__init__(host, port, app, handler, passthrough_errors, ssl_context, fd) - self.max_children = processes - - -def make_server( - host: str, - port: int, - app: WSGIApplication, - threaded: bool = False, - processes: int = 1, - request_handler: type[WSGIRequestHandler] | None = None, - passthrough_errors: bool = False, - ssl_context: _TSSLContextArg | None = None, - fd: int | None = None, -) -> BaseWSGIServer: - """Create an appropriate WSGI server instance based on the value of - ``threaded`` and ``processes``. - - This is called from :func:`run_simple`, but can be used separately - to have access to the server object, such as to run it in a separate - thread. - - See :func:`run_simple` for parameter docs. - """ - if threaded and processes > 1: - raise ValueError("Cannot have a multi-thread and multi-process server.") - - if threaded: - return ThreadedWSGIServer( - host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd - ) - - if processes > 1: - return ForkingWSGIServer( - host, - port, - app, - processes, - request_handler, - passthrough_errors, - ssl_context, - fd=fd, - ) - - return BaseWSGIServer( - host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd - ) - - -def is_running_from_reloader() -> bool: - """Check if the server is running as a subprocess within the - Werkzeug reloader. - - .. versionadded:: 0.10 - """ - return os.environ.get("WERKZEUG_RUN_MAIN") == "true" - - -def run_simple( - hostname: str, - port: int, - application: WSGIApplication, - use_reloader: bool = False, - use_debugger: bool = False, - use_evalex: bool = True, - extra_files: t.Iterable[str] | None = None, - exclude_patterns: t.Iterable[str] | None = None, - reloader_interval: int = 1, - reloader_type: str = "auto", - threaded: bool = False, - processes: int = 1, - request_handler: type[WSGIRequestHandler] | None = None, - static_files: dict[str, str | tuple[str, str]] | None = None, - passthrough_errors: bool = False, - ssl_context: _TSSLContextArg | None = None, -) -> None: - """Start a development server for a WSGI application. Various - optional features can be enabled. - - .. warning:: - - Do not use the development server when deploying to production. - It is intended for use only during local development. It is not - designed to be particularly efficient, stable, or secure. - - :param hostname: The host to bind to, for example ``'localhost'``. - Can be a domain, IPv4 or IPv6 address, or file path starting - with ``unix://`` for a Unix socket. - :param port: The port to bind to, for example ``8080``. Using ``0`` - tells the OS to pick a random free port. - :param application: The WSGI application to run. - :param use_reloader: Use a reloader process to restart the server - process when files are changed. - :param use_debugger: Use Werkzeug's debugger, which will show - formatted tracebacks on unhandled exceptions. - :param use_evalex: Make the debugger interactive. A Python terminal - can be opened for any frame in the traceback. Some protection is - provided by requiring a PIN, but this should never be enabled - on a publicly visible server. - :param extra_files: The reloader will watch these files for changes - in addition to Python modules. For example, watch a - configuration file. - :param exclude_patterns: The reloader will ignore changes to any - files matching these :mod:`fnmatch` patterns. For example, - ignore cache files. - :param reloader_interval: How often the reloader tries to check for - changes. - :param reloader_type: The reloader to use. The ``'stat'`` reloader - is built in, but may require significant CPU to watch files. The - ``'watchdog'`` reloader is much more efficient but requires - installing the ``watchdog`` package first. - :param threaded: Handle concurrent requests using threads. Cannot be - used with ``processes``. - :param processes: Handle concurrent requests using up to this number - of processes. Cannot be used with ``threaded``. - :param request_handler: Use a different - :class:`~BaseHTTPServer.BaseHTTPRequestHandler` subclass to - handle requests. - :param static_files: A dict mapping URL prefixes to directories to - serve static files from using - :class:`~werkzeug.middleware.SharedDataMiddleware`. - :param passthrough_errors: Don't catch unhandled exceptions at the - server level, let the server crash instead. If ``use_debugger`` - is enabled, the debugger will still catch such errors. - :param ssl_context: Configure TLS to serve over HTTPS. Can be an - :class:`ssl.SSLContext` object, a ``(cert_file, key_file)`` - tuple to create a typical context, or the string ``'adhoc'`` to - generate a temporary self-signed certificate. - - .. versionchanged:: 2.1 - Instructions are shown for dealing with an "address already in - use" error. - - .. versionchanged:: 2.1 - Running on ``0.0.0.0`` or ``::`` shows the loopback IP in - addition to a real IP. - - .. versionchanged:: 2.1 - The command-line interface was removed. - - .. versionchanged:: 2.0 - Running on ``0.0.0.0`` or ``::`` shows a real IP address that - was bound as well as a warning not to run the development server - in production. - - .. versionchanged:: 2.0 - The ``exclude_patterns`` parameter was added. - - .. versionchanged:: 0.15 - Bind to a Unix socket by passing a ``hostname`` that starts with - ``unix://``. - - .. versionchanged:: 0.10 - Improved the reloader and added support for changing the backend - through the ``reloader_type`` parameter. - - .. versionchanged:: 0.9 - A command-line interface was added. - - .. versionchanged:: 0.8 - ``ssl_context`` can be a tuple of paths to the certificate and - private key files. - - .. versionchanged:: 0.6 - The ``ssl_context`` parameter was added. - - .. versionchanged:: 0.5 - The ``static_files`` and ``passthrough_errors`` parameters were - added. - """ - if not isinstance(port, int): - raise TypeError("port must be an integer") - - if static_files: - from .middleware.shared_data import SharedDataMiddleware - - application = SharedDataMiddleware(application, static_files) - - if use_debugger: - from .debug import DebuggedApplication - - application = DebuggedApplication(application, evalex=use_evalex) - # Allow the specified hostname to use the debugger, in addition to - # localhost domains. - application.trusted_hosts.append(hostname) - - if not is_running_from_reloader(): - fd = None - else: - fd = int(os.environ["WERKZEUG_SERVER_FD"]) - - srv = make_server( - hostname, - port, - application, - threaded, - processes, - request_handler, - passthrough_errors, - ssl_context, - fd=fd, - ) - srv.socket.set_inheritable(True) - os.environ["WERKZEUG_SERVER_FD"] = str(srv.fileno()) - - if not is_running_from_reloader(): - srv.log_startup() - _log("info", _ansi_style("Press CTRL+C to quit", "yellow")) - - if use_reloader: - from ._reloader import run_with_reloader - - try: - run_with_reloader( - srv.serve_forever, - extra_files=extra_files, - exclude_patterns=exclude_patterns, - interval=reloader_interval, - reloader_type=reloader_type, - ) - finally: - srv.server_close() - else: - srv.serve_forever() diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/test.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/test.py deleted file mode 100644 index 38f69bf..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/test.py +++ /dev/null @@ -1,1464 +0,0 @@ -from __future__ import annotations - -import dataclasses -import mimetypes -import sys -import typing as t -from collections import defaultdict -from datetime import datetime -from io import BytesIO -from itertools import chain -from random import random -from tempfile import TemporaryFile -from time import time -from urllib.parse import unquote -from urllib.parse import urlsplit -from urllib.parse import urlunsplit - -from ._internal import _get_environ -from ._internal import _wsgi_decoding_dance -from ._internal import _wsgi_encoding_dance -from .datastructures import Authorization -from .datastructures import CallbackDict -from .datastructures import CombinedMultiDict -from .datastructures import EnvironHeaders -from .datastructures import FileMultiDict -from .datastructures import Headers -from .datastructures import MultiDict -from .http import dump_cookie -from .http import dump_options_header -from .http import parse_cookie -from .http import parse_date -from .http import parse_options_header -from .sansio.multipart import Data -from .sansio.multipart import Epilogue -from .sansio.multipart import Field -from .sansio.multipart import File -from .sansio.multipart import MultipartEncoder -from .sansio.multipart import Preamble -from .urls import _urlencode -from .urls import iri_to_uri -from .utils import cached_property -from .utils import get_content_type -from .wrappers.request import Request -from .wrappers.response import Response -from .wsgi import ClosingIterator -from .wsgi import get_current_url - -if t.TYPE_CHECKING: - import typing_extensions as te - from _typeshed.wsgi import WSGIApplication - from _typeshed.wsgi import WSGIEnvironment - - -def stream_encode_multipart( - data: t.Mapping[str, t.Any], - use_tempfile: bool = True, - threshold: int = 1024 * 500, - boundary: str | None = None, -) -> tuple[t.IO[bytes], int, str]: - """Encode a dict of values (either strings or file descriptors or - :class:`FileStorage` objects.) into a multipart encoded string stored - in a file descriptor. - - .. versionchanged:: 3.0 - The ``charset`` parameter was removed. - """ - if boundary is None: - boundary = f"---------------WerkzeugFormPart_{time()}{random()}" - - stream: t.IO[bytes] = BytesIO() - total_length = 0 - on_disk = False - write_binary: t.Callable[[bytes], int] - - if use_tempfile: - - def write_binary(s: bytes) -> int: - nonlocal stream, total_length, on_disk - - if on_disk: - return stream.write(s) - else: - length = len(s) - - if length + total_length <= threshold: - stream.write(s) - else: - new_stream = t.cast(t.IO[bytes], TemporaryFile("wb+")) - new_stream.write(stream.getvalue()) # type: ignore - new_stream.write(s) - stream = new_stream - on_disk = True - - total_length += length - return length - - else: - write_binary = stream.write - - encoder = MultipartEncoder(boundary.encode()) - write_binary(encoder.send_event(Preamble(data=b""))) - for key, value in _iter_data(data): - reader = getattr(value, "read", None) - if reader is not None: - filename = getattr(value, "filename", getattr(value, "name", None)) - content_type = getattr(value, "content_type", None) - if content_type is None: - content_type = ( - filename - and mimetypes.guess_type(filename)[0] - or "application/octet-stream" - ) - headers = value.headers - headers.update([("Content-Type", content_type)]) - if filename is None: - write_binary(encoder.send_event(Field(name=key, headers=headers))) - else: - write_binary( - encoder.send_event( - File(name=key, filename=filename, headers=headers) - ) - ) - while True: - chunk = reader(16384) - - if not chunk: - write_binary(encoder.send_event(Data(data=chunk, more_data=False))) - break - - write_binary(encoder.send_event(Data(data=chunk, more_data=True))) - else: - if not isinstance(value, str): - value = str(value) - write_binary(encoder.send_event(Field(name=key, headers=Headers()))) - write_binary(encoder.send_event(Data(data=value.encode(), more_data=False))) - - write_binary(encoder.send_event(Epilogue(data=b""))) - - length = stream.tell() - stream.seek(0) - return stream, length, boundary - - -def encode_multipart( - values: t.Mapping[str, t.Any], boundary: str | None = None -) -> tuple[str, bytes]: - """Like `stream_encode_multipart` but returns a tuple in the form - (``boundary``, ``data``) where data is bytes. - - .. versionchanged:: 3.0 - The ``charset`` parameter was removed. - """ - stream, length, boundary = stream_encode_multipart( - values, use_tempfile=False, boundary=boundary - ) - return boundary, stream.read() - - -def _iter_data(data: t.Mapping[str, t.Any]) -> t.Iterator[tuple[str, t.Any]]: - """Iterate over a mapping that might have a list of values, yielding - all key, value pairs. Almost like iter_multi_items but only allows - lists, not tuples, of values so tuples can be used for files. - """ - if isinstance(data, MultiDict): - yield from data.items(multi=True) - else: - for key, value in data.items(): - if isinstance(value, list): - for v in value: - yield key, v - else: - yield key, value - - -_TAnyMultiDict = t.TypeVar("_TAnyMultiDict", bound="MultiDict[t.Any, t.Any]") - - -class EnvironBuilder: - """This class can be used to conveniently create a WSGI environment - for testing purposes. It can be used to quickly create WSGI environments - or request objects from arbitrary data. - - The signature of this class is also used in some other places as of - Werkzeug 0.5 (:func:`create_environ`, :meth:`Response.from_values`, - :meth:`Client.open`). Because of this most of the functionality is - available through the constructor alone. - - Files and regular form data can be manipulated independently of each - other with the :attr:`form` and :attr:`files` attributes, but are - passed with the same argument to the constructor: `data`. - - `data` can be any of these values: - - - a `str` or `bytes` object: The object is converted into an - :attr:`input_stream`, the :attr:`content_length` is set and you have to - provide a :attr:`content_type`. - - a `dict` or :class:`MultiDict`: The keys have to be strings. The values - have to be either any of the following objects, or a list of any of the - following objects: - - - a :class:`file`-like object: These are converted into - :class:`FileStorage` objects automatically. - - a `tuple`: The :meth:`~FileMultiDict.add_file` method is called - with the key and the unpacked `tuple` items as positional - arguments. - - a `str`: The string is set as form data for the associated key. - - a file-like object: The object content is loaded in memory and then - handled like a regular `str` or a `bytes`. - - :param path: the path of the request. In the WSGI environment this will - end up as `PATH_INFO`. If the `query_string` is not defined - and there is a question mark in the `path` everything after - it is used as query string. - :param base_url: the base URL is a URL that is used to extract the WSGI - URL scheme, host (server name + server port) and the - script root (`SCRIPT_NAME`). - :param query_string: an optional string or dict with URL parameters. - :param method: the HTTP method to use, defaults to `GET`. - :param input_stream: an optional input stream. Do not specify this and - `data`. As soon as an input stream is set you can't - modify :attr:`args` and :attr:`files` unless you - set the :attr:`input_stream` to `None` again. - :param content_type: The content type for the request. As of 0.5 you - don't have to provide this when specifying files - and form data via `data`. - :param content_length: The content length for the request. You don't - have to specify this when providing data via - `data`. - :param errors_stream: an optional error stream that is used for - `wsgi.errors`. Defaults to :data:`stderr`. - :param multithread: controls `wsgi.multithread`. Defaults to `False`. - :param multiprocess: controls `wsgi.multiprocess`. Defaults to `False`. - :param run_once: controls `wsgi.run_once`. Defaults to `False`. - :param headers: an optional list or :class:`Headers` object of headers. - :param data: a string or dict of form data or a file-object. - See explanation above. - :param json: An object to be serialized and assigned to ``data``. - Defaults the content type to ``"application/json"``. - Serialized with the function assigned to :attr:`json_dumps`. - :param environ_base: an optional dict of environment defaults. - :param environ_overrides: an optional dict of environment overrides. - :param auth: An authorization object to use for the - ``Authorization`` header value. A ``(username, password)`` tuple - is a shortcut for ``Basic`` authorization. - - .. versionchanged:: 3.0 - The ``charset`` parameter was removed. - - .. versionchanged:: 2.1 - ``CONTENT_TYPE`` and ``CONTENT_LENGTH`` are not duplicated as - header keys in the environ. - - .. versionchanged:: 2.0 - ``REQUEST_URI`` and ``RAW_URI`` is the full raw URI including - the query string, not only the path. - - .. versionchanged:: 2.0 - The default :attr:`request_class` is ``Request`` instead of - ``BaseRequest``. - - .. versionadded:: 2.0 - Added the ``auth`` parameter. - - .. versionadded:: 0.15 - The ``json`` param and :meth:`json_dumps` method. - - .. versionadded:: 0.15 - The environ has keys ``REQUEST_URI`` and ``RAW_URI`` containing - the path before percent-decoding. This is not part of the WSGI - PEP, but many WSGI servers include it. - - .. versionchanged:: 0.6 - ``path`` and ``base_url`` can now be unicode strings that are - encoded with :func:`iri_to_uri`. - """ - - #: the server protocol to use. defaults to HTTP/1.1 - server_protocol = "HTTP/1.1" - - #: the wsgi version to use. defaults to (1, 0) - wsgi_version = (1, 0) - - #: The default request class used by :meth:`get_request`. - request_class = Request - - import json - - #: The serialization function used when ``json`` is passed. - json_dumps = staticmethod(json.dumps) - del json - - _args: MultiDict[str, str] | None - _query_string: str | None - _input_stream: t.IO[bytes] | None - _form: MultiDict[str, str] | None - _files: FileMultiDict | None - - def __init__( - self, - path: str = "/", - base_url: str | None = None, - query_string: t.Mapping[str, str] | str | None = None, - method: str = "GET", - input_stream: t.IO[bytes] | None = None, - content_type: str | None = None, - content_length: int | None = None, - errors_stream: t.IO[str] | None = None, - multithread: bool = False, - multiprocess: bool = False, - run_once: bool = False, - headers: Headers | t.Iterable[tuple[str, str]] | None = None, - data: None | (t.IO[bytes] | str | bytes | t.Mapping[str, t.Any]) = None, - environ_base: t.Mapping[str, t.Any] | None = None, - environ_overrides: t.Mapping[str, t.Any] | None = None, - mimetype: str | None = None, - json: t.Mapping[str, t.Any] | None = None, - auth: Authorization | tuple[str, str] | None = None, - ) -> None: - if query_string is not None and "?" in path: - raise ValueError("Query string is defined in the path and as an argument") - request_uri = urlsplit(path) - if query_string is None and "?" in path: - query_string = request_uri.query - - self.path = iri_to_uri(request_uri.path) - self.request_uri = path - if base_url is not None: - base_url = iri_to_uri(base_url) - self.base_url = base_url # type: ignore - if isinstance(query_string, str): - self.query_string = query_string - else: - if query_string is None: - query_string = MultiDict() - elif not isinstance(query_string, MultiDict): - query_string = MultiDict(query_string) - self.args = query_string - self.method = method - if headers is None: - headers = Headers() - elif not isinstance(headers, Headers): - headers = Headers(headers) - self.headers = headers - if content_type is not None: - self.content_type = content_type - if errors_stream is None: - errors_stream = sys.stderr - self.errors_stream = errors_stream - self.multithread = multithread - self.multiprocess = multiprocess - self.run_once = run_once - self.environ_base = environ_base - self.environ_overrides = environ_overrides - self.input_stream = input_stream - self.content_length = content_length - self.closed = False - - if auth is not None: - if isinstance(auth, tuple): - auth = Authorization( - "basic", {"username": auth[0], "password": auth[1]} - ) - - self.headers.set("Authorization", auth.to_header()) - - if json is not None: - if data is not None: - raise TypeError("can't provide both json and data") - - data = self.json_dumps(json) - - if self.content_type is None: - self.content_type = "application/json" - - if data: - if input_stream is not None: - raise TypeError("can't provide input stream and data") - if hasattr(data, "read"): - data = data.read() - if isinstance(data, str): - data = data.encode() - if isinstance(data, bytes): - self.input_stream = BytesIO(data) - if self.content_length is None: - self.content_length = len(data) - else: - for key, value in _iter_data(data): - if isinstance(value, (tuple, dict)) or hasattr(value, "read"): - self._add_file_from_data(key, value) - else: - self.form.setlistdefault(key).append(value) - - if mimetype is not None: - self.mimetype = mimetype - - @classmethod - def from_environ(cls, environ: WSGIEnvironment, **kwargs: t.Any) -> EnvironBuilder: - """Turn an environ dict back into a builder. Any extra kwargs - override the args extracted from the environ. - - .. versionchanged:: 2.0 - Path and query values are passed through the WSGI decoding - dance to avoid double encoding. - - .. versionadded:: 0.15 - """ - headers = Headers(EnvironHeaders(environ)) - out = { - "path": _wsgi_decoding_dance(environ["PATH_INFO"]), - "base_url": cls._make_base_url( - environ["wsgi.url_scheme"], - headers.pop("Host"), - _wsgi_decoding_dance(environ["SCRIPT_NAME"]), - ), - "query_string": _wsgi_decoding_dance(environ["QUERY_STRING"]), - "method": environ["REQUEST_METHOD"], - "input_stream": environ["wsgi.input"], - "content_type": headers.pop("Content-Type", None), - "content_length": headers.pop("Content-Length", None), - "errors_stream": environ["wsgi.errors"], - "multithread": environ["wsgi.multithread"], - "multiprocess": environ["wsgi.multiprocess"], - "run_once": environ["wsgi.run_once"], - "headers": headers, - } - out.update(kwargs) - return cls(**out) - - def _add_file_from_data( - self, - key: str, - value: (t.IO[bytes] | tuple[t.IO[bytes], str] | tuple[t.IO[bytes], str, str]), - ) -> None: - """Called in the EnvironBuilder to add files from the data dict.""" - if isinstance(value, tuple): - self.files.add_file(key, *value) - else: - self.files.add_file(key, value) - - @staticmethod - def _make_base_url(scheme: str, host: str, script_root: str) -> str: - return urlunsplit((scheme, host, script_root, "", "")).rstrip("/") + "/" - - @property - def base_url(self) -> str: - """The base URL is used to extract the URL scheme, host name, - port, and root path. - """ - return self._make_base_url(self.url_scheme, self.host, self.script_root) - - @base_url.setter - def base_url(self, value: str | None) -> None: - if value is None: - scheme = "http" - netloc = "localhost" - script_root = "" - else: - scheme, netloc, script_root, qs, anchor = urlsplit(value) - if qs or anchor: - raise ValueError("base url must not contain a query string or fragment") - self.script_root = script_root.rstrip("/") - self.host = netloc - self.url_scheme = scheme - - @property - def content_type(self) -> str | None: - """The content type for the request. Reflected from and to - the :attr:`headers`. Do not set if you set :attr:`files` or - :attr:`form` for auto detection. - """ - ct = self.headers.get("Content-Type") - if ct is None and not self._input_stream: - if self._files: - return "multipart/form-data" - if self._form: - return "application/x-www-form-urlencoded" - return None - return ct - - @content_type.setter - def content_type(self, value: str | None) -> None: - if value is None: - self.headers.pop("Content-Type", None) - else: - self.headers["Content-Type"] = value - - @property - def mimetype(self) -> str | None: - """The mimetype (content type without charset etc.) - - .. versionadded:: 0.14 - """ - ct = self.content_type - return ct.split(";")[0].strip() if ct else None - - @mimetype.setter - def mimetype(self, value: str) -> None: - self.content_type = get_content_type(value, "utf-8") - - @property - def mimetype_params(self) -> t.Mapping[str, str]: - """The mimetype parameters as dict. For example if the - content type is ``text/html; charset=utf-8`` the params would be - ``{'charset': 'utf-8'}``. - - .. versionadded:: 0.14 - """ - - def on_update(d: CallbackDict[str, str]) -> None: - self.headers["Content-Type"] = dump_options_header(self.mimetype, d) - - d = parse_options_header(self.headers.get("content-type", ""))[1] - return CallbackDict(d, on_update) - - @property - def content_length(self) -> int | None: - """The content length as integer. Reflected from and to the - :attr:`headers`. Do not set if you set :attr:`files` or - :attr:`form` for auto detection. - """ - return self.headers.get("Content-Length", type=int) - - @content_length.setter - def content_length(self, value: int | None) -> None: - if value is None: - self.headers.pop("Content-Length", None) - else: - self.headers["Content-Length"] = str(value) - - def _get_form(self, name: str, storage: type[_TAnyMultiDict]) -> _TAnyMultiDict: - """Common behavior for getting the :attr:`form` and - :attr:`files` properties. - - :param name: Name of the internal cached attribute. - :param storage: Storage class used for the data. - """ - if self.input_stream is not None: - raise AttributeError("an input stream is defined") - - rv = getattr(self, name) - - if rv is None: - rv = storage() - setattr(self, name, rv) - - return rv # type: ignore - - def _set_form(self, name: str, value: MultiDict[str, t.Any]) -> None: - """Common behavior for setting the :attr:`form` and - :attr:`files` properties. - - :param name: Name of the internal cached attribute. - :param value: Value to assign to the attribute. - """ - self._input_stream = None - setattr(self, name, value) - - @property - def form(self) -> MultiDict[str, str]: - """A :class:`MultiDict` of form values.""" - return self._get_form("_form", MultiDict) - - @form.setter - def form(self, value: MultiDict[str, str]) -> None: - self._set_form("_form", value) - - @property - def files(self) -> FileMultiDict: - """A :class:`FileMultiDict` of uploaded files. Use - :meth:`~FileMultiDict.add_file` to add new files. - """ - return self._get_form("_files", FileMultiDict) - - @files.setter - def files(self, value: FileMultiDict) -> None: - self._set_form("_files", value) - - @property - def input_stream(self) -> t.IO[bytes] | None: - """An optional input stream. This is mutually exclusive with - setting :attr:`form` and :attr:`files`, setting it will clear - those. Do not provide this if the method is not ``POST`` or - another method that has a body. - """ - return self._input_stream - - @input_stream.setter - def input_stream(self, value: t.IO[bytes] | None) -> None: - self._input_stream = value - self._form = None - self._files = None - - @property - def query_string(self) -> str: - """The query string. If you set this to a string - :attr:`args` will no longer be available. - """ - if self._query_string is None: - if self._args is not None: - return _urlencode(self._args) - return "" - return self._query_string - - @query_string.setter - def query_string(self, value: str | None) -> None: - self._query_string = value - self._args = None - - @property - def args(self) -> MultiDict[str, str]: - """The URL arguments as :class:`MultiDict`.""" - if self._query_string is not None: - raise AttributeError("a query string is defined") - if self._args is None: - self._args = MultiDict() - return self._args - - @args.setter - def args(self, value: MultiDict[str, str] | None) -> None: - self._query_string = None - self._args = value - - @property - def server_name(self) -> str: - """The server name (read-only, use :attr:`host` to set)""" - return self.host.split(":", 1)[0] - - @property - def server_port(self) -> int: - """The server port as integer (read-only, use :attr:`host` to set)""" - pieces = self.host.split(":", 1) - - if len(pieces) == 2: - try: - return int(pieces[1]) - except ValueError: - pass - - if self.url_scheme == "https": - return 443 - return 80 - - def __del__(self) -> None: - try: - self.close() - except Exception: - pass - - def close(self) -> None: - """Closes all files. If you put real :class:`file` objects into the - :attr:`files` dict you can call this method to automatically close - them all in one go. - """ - if self.closed: - return - try: - files = self.files.values() - except AttributeError: - files = () # type: ignore - for f in files: - try: - f.close() - except Exception: - pass - self.closed = True - - def get_environ(self) -> WSGIEnvironment: - """Return the built environ. - - .. versionchanged:: 0.15 - The content type and length headers are set based on - input stream detection. Previously this only set the WSGI - keys. - """ - input_stream = self.input_stream - content_length = self.content_length - - mimetype = self.mimetype - content_type = self.content_type - - if input_stream is not None: - start_pos = input_stream.tell() - input_stream.seek(0, 2) - end_pos = input_stream.tell() - input_stream.seek(start_pos) - content_length = end_pos - start_pos - elif mimetype == "multipart/form-data": - input_stream, content_length, boundary = stream_encode_multipart( - CombinedMultiDict([self.form, self.files]) - ) - content_type = f'{mimetype}; boundary="{boundary}"' - elif mimetype == "application/x-www-form-urlencoded": - form_encoded = _urlencode(self.form).encode("ascii") - content_length = len(form_encoded) - input_stream = BytesIO(form_encoded) - else: - input_stream = BytesIO() - - result: WSGIEnvironment = {} - if self.environ_base: - result.update(self.environ_base) - - def _path_encode(x: str) -> str: - return _wsgi_encoding_dance(unquote(x)) - - raw_uri = _wsgi_encoding_dance(self.request_uri) - result.update( - { - "REQUEST_METHOD": self.method, - "SCRIPT_NAME": _path_encode(self.script_root), - "PATH_INFO": _path_encode(self.path), - "QUERY_STRING": _wsgi_encoding_dance(self.query_string), - # Non-standard, added by mod_wsgi, uWSGI - "REQUEST_URI": raw_uri, - # Non-standard, added by gunicorn - "RAW_URI": raw_uri, - "SERVER_NAME": self.server_name, - "SERVER_PORT": str(self.server_port), - "HTTP_HOST": self.host, - "SERVER_PROTOCOL": self.server_protocol, - "wsgi.version": self.wsgi_version, - "wsgi.url_scheme": self.url_scheme, - "wsgi.input": input_stream, - "wsgi.errors": self.errors_stream, - "wsgi.multithread": self.multithread, - "wsgi.multiprocess": self.multiprocess, - "wsgi.run_once": self.run_once, - } - ) - - headers = self.headers.copy() - # Don't send these as headers, they're part of the environ. - headers.remove("Content-Type") - headers.remove("Content-Length") - - if content_type is not None: - result["CONTENT_TYPE"] = content_type - - if content_length is not None: - result["CONTENT_LENGTH"] = str(content_length) - - combined_headers = defaultdict(list) - - for key, value in headers.to_wsgi_list(): - combined_headers[f"HTTP_{key.upper().replace('-', '_')}"].append(value) - - for key, values in combined_headers.items(): - result[key] = ", ".join(values) - - if self.environ_overrides: - result.update(self.environ_overrides) - - return result - - def get_request(self, cls: type[Request] | None = None) -> Request: - """Returns a request with the data. If the request class is not - specified :attr:`request_class` is used. - - :param cls: The request wrapper to use. - """ - if cls is None: - cls = self.request_class - - return cls(self.get_environ()) - - -class ClientRedirectError(Exception): - """If a redirect loop is detected when using follow_redirects=True with - the :cls:`Client`, then this exception is raised. - """ - - -class Client: - """Simulate sending requests to a WSGI application without running a WSGI or HTTP - server. - - :param application: The WSGI application to make requests to. - :param response_wrapper: A :class:`.Response` class to wrap response data with. - Defaults to :class:`.TestResponse`. If it's not a subclass of ``TestResponse``, - one will be created. - :param use_cookies: Persist cookies from ``Set-Cookie`` response headers to the - ``Cookie`` header in subsequent requests. Domain and path matching is supported, - but other cookie parameters are ignored. - :param allow_subdomain_redirects: Allow requests to follow redirects to subdomains. - Enable this if the application handles subdomains and redirects between them. - - .. versionchanged:: 2.3 - Simplify cookie implementation, support domain and path matching. - - .. versionchanged:: 2.1 - All data is available as properties on the returned response object. The - response cannot be returned as a tuple. - - .. versionchanged:: 2.0 - ``response_wrapper`` is always a subclass of :class:``TestResponse``. - - .. versionchanged:: 0.5 - Added the ``use_cookies`` parameter. - """ - - def __init__( - self, - application: WSGIApplication, - response_wrapper: type[Response] | None = None, - use_cookies: bool = True, - allow_subdomain_redirects: bool = False, - ) -> None: - self.application = application - - if response_wrapper in {None, Response}: - response_wrapper = TestResponse - elif response_wrapper is not None and not issubclass( - response_wrapper, TestResponse - ): - response_wrapper = type( - "WrapperTestResponse", - (TestResponse, response_wrapper), - {}, - ) - - self.response_wrapper = t.cast(t.Type["TestResponse"], response_wrapper) - - if use_cookies: - self._cookies: dict[tuple[str, str, str], Cookie] | None = {} - else: - self._cookies = None - - self.allow_subdomain_redirects = allow_subdomain_redirects - - def get_cookie( - self, key: str, domain: str = "localhost", path: str = "/" - ) -> Cookie | None: - """Return a :class:`.Cookie` if it exists. Cookies are uniquely identified by - ``(domain, path, key)``. - - :param key: The decoded form of the key for the cookie. - :param domain: The domain the cookie was set for. - :param path: The path the cookie was set for. - - .. versionadded:: 2.3 - """ - if self._cookies is None: - raise TypeError( - "Cookies are disabled. Create a client with 'use_cookies=True'." - ) - - return self._cookies.get((domain, path, key)) - - def set_cookie( - self, - key: str, - value: str = "", - *, - domain: str = "localhost", - origin_only: bool = True, - path: str = "/", - **kwargs: t.Any, - ) -> None: - """Set a cookie to be sent in subsequent requests. - - This is a convenience to skip making a test request to a route that would set - the cookie. To test the cookie, make a test request to a route that uses the - cookie value. - - The client uses ``domain``, ``origin_only``, and ``path`` to determine which - cookies to send with a request. It does not use other cookie parameters that - browsers use, since they're not applicable in tests. - - :param key: The key part of the cookie. - :param value: The value part of the cookie. - :param domain: Send this cookie with requests that match this domain. If - ``origin_only`` is true, it must be an exact match, otherwise it may be a - suffix match. - :param origin_only: Whether the domain must be an exact match to the request. - :param path: Send this cookie with requests that match this path either exactly - or as a prefix. - :param kwargs: Passed to :func:`.dump_cookie`. - - .. versionchanged:: 3.0 - The parameter ``server_name`` is removed. The first parameter is - ``key``. Use the ``domain`` and ``origin_only`` parameters instead. - - .. versionchanged:: 2.3 - The ``origin_only`` parameter was added. - - .. versionchanged:: 2.3 - The ``domain`` parameter defaults to ``localhost``. - """ - if self._cookies is None: - raise TypeError( - "Cookies are disabled. Create a client with 'use_cookies=True'." - ) - - cookie = Cookie._from_response_header( - domain, "/", dump_cookie(key, value, domain=domain, path=path, **kwargs) - ) - cookie.origin_only = origin_only - - if cookie._should_delete: - self._cookies.pop(cookie._storage_key, None) - else: - self._cookies[cookie._storage_key] = cookie - - def delete_cookie( - self, - key: str, - *, - domain: str = "localhost", - path: str = "/", - ) -> None: - """Delete a cookie if it exists. Cookies are uniquely identified by - ``(domain, path, key)``. - - :param key: The decoded form of the key for the cookie. - :param domain: The domain the cookie was set for. - :param path: The path the cookie was set for. - - .. versionchanged:: 3.0 - The ``server_name`` parameter is removed. The first parameter is - ``key``. Use the ``domain`` parameter instead. - - .. versionchanged:: 3.0 - The ``secure``, ``httponly`` and ``samesite`` parameters are removed. - - .. versionchanged:: 2.3 - The ``domain`` parameter defaults to ``localhost``. - """ - if self._cookies is None: - raise TypeError( - "Cookies are disabled. Create a client with 'use_cookies=True'." - ) - - self._cookies.pop((domain, path, key), None) - - def _add_cookies_to_wsgi(self, environ: WSGIEnvironment) -> None: - """If cookies are enabled, set the ``Cookie`` header in the environ to the - cookies that are applicable to the request host and path. - - :meta private: - - .. versionadded:: 2.3 - """ - if self._cookies is None: - return - - url = urlsplit(get_current_url(environ)) - server_name = url.hostname or "localhost" - value = "; ".join( - c._to_request_header() - for c in self._cookies.values() - if c._matches_request(server_name, url.path) - ) - - if value: - environ["HTTP_COOKIE"] = value - else: - environ.pop("HTTP_COOKIE", None) - - def _update_cookies_from_response( - self, server_name: str, path: str, headers: list[str] - ) -> None: - """If cookies are enabled, update the stored cookies from any ``Set-Cookie`` - headers in the response. - - :meta private: - - .. versionadded:: 2.3 - """ - if self._cookies is None: - return - - for header in headers: - cookie = Cookie._from_response_header(server_name, path, header) - - if cookie._should_delete: - self._cookies.pop(cookie._storage_key, None) - else: - self._cookies[cookie._storage_key] = cookie - - def run_wsgi_app( - self, environ: WSGIEnvironment, buffered: bool = False - ) -> tuple[t.Iterable[bytes], str, Headers]: - """Runs the wrapped WSGI app with the given environment. - - :meta private: - """ - self._add_cookies_to_wsgi(environ) - rv = run_wsgi_app(self.application, environ, buffered=buffered) - url = urlsplit(get_current_url(environ)) - self._update_cookies_from_response( - url.hostname or "localhost", url.path, rv[2].getlist("Set-Cookie") - ) - return rv - - def resolve_redirect( - self, response: TestResponse, buffered: bool = False - ) -> TestResponse: - """Perform a new request to the location given by the redirect - response to the previous request. - - :meta private: - """ - scheme, netloc, path, qs, anchor = urlsplit(response.location) - builder = EnvironBuilder.from_environ( - response.request.environ, path=path, query_string=qs - ) - - to_name_parts = netloc.split(":", 1)[0].split(".") - from_name_parts = builder.server_name.split(".") - - if to_name_parts != [""]: - # The new location has a host, use it for the base URL. - builder.url_scheme = scheme - builder.host = netloc - else: - # A local redirect with autocorrect_location_header=False - # doesn't have a host, so use the request's host. - to_name_parts = from_name_parts - - # Explain why a redirect to a different server name won't be followed. - if to_name_parts != from_name_parts: - if to_name_parts[-len(from_name_parts) :] == from_name_parts: - if not self.allow_subdomain_redirects: - raise RuntimeError("Following subdomain redirects is not enabled.") - else: - raise RuntimeError("Following external redirects is not supported.") - - path_parts = path.split("/") - root_parts = builder.script_root.split("/") - - if path_parts[: len(root_parts)] == root_parts: - # Strip the script root from the path. - builder.path = path[len(builder.script_root) :] - else: - # The new location is not under the script root, so use the - # whole path and clear the previous root. - builder.path = path - builder.script_root = "" - - # Only 307 and 308 preserve all of the original request. - if response.status_code not in {307, 308}: - # HEAD is preserved, everything else becomes GET. - if builder.method != "HEAD": - builder.method = "GET" - - # Clear the body and the headers that describe it. - - if builder.input_stream is not None: - builder.input_stream.close() - builder.input_stream = None - - builder.content_type = None - builder.content_length = None - builder.headers.pop("Transfer-Encoding", None) - - return self.open(builder, buffered=buffered) - - def open( - self, - *args: t.Any, - buffered: bool = False, - follow_redirects: bool = False, - **kwargs: t.Any, - ) -> TestResponse: - """Generate an environ dict from the given arguments, make a - request to the application using it, and return the response. - - :param args: Passed to :class:`EnvironBuilder` to create the - environ for the request. If a single arg is passed, it can - be an existing :class:`EnvironBuilder` or an environ dict. - :param buffered: Convert the iterator returned by the app into - a list. If the iterator has a ``close()`` method, it is - called automatically. - :param follow_redirects: Make additional requests to follow HTTP - redirects until a non-redirect status is returned. - :attr:`TestResponse.history` lists the intermediate - responses. - - .. versionchanged:: 2.1 - Removed the ``as_tuple`` parameter. - - .. versionchanged:: 2.0 - The request input stream is closed when calling - ``response.close()``. Input streams for redirects are - automatically closed. - - .. versionchanged:: 0.5 - If a dict is provided as file in the dict for the ``data`` - parameter the content type has to be called ``content_type`` - instead of ``mimetype``. This change was made for - consistency with :class:`werkzeug.FileWrapper`. - - .. versionchanged:: 0.5 - Added the ``follow_redirects`` parameter. - """ - request: Request | None = None - - if not kwargs and len(args) == 1: - arg = args[0] - - if isinstance(arg, EnvironBuilder): - request = arg.get_request() - elif isinstance(arg, dict): - request = EnvironBuilder.from_environ(arg).get_request() - elif isinstance(arg, Request): - request = arg - - if request is None: - builder = EnvironBuilder(*args, **kwargs) - - try: - request = builder.get_request() - finally: - builder.close() - - response_parts = self.run_wsgi_app(request.environ, buffered=buffered) - response = self.response_wrapper(*response_parts, request=request) - - redirects = set() - history: list[TestResponse] = [] - - if not follow_redirects: - return response - - while response.status_code in { - 301, - 302, - 303, - 305, - 307, - 308, - }: - # Exhaust intermediate response bodies to ensure middleware - # that returns an iterator runs any cleanup code. - if not buffered: - response.make_sequence() - response.close() - - new_redirect_entry = (response.location, response.status_code) - - if new_redirect_entry in redirects: - raise ClientRedirectError( - f"Loop detected: A {response.status_code} redirect" - f" to {response.location} was already made." - ) - - redirects.add(new_redirect_entry) - response.history = tuple(history) - history.append(response) - response = self.resolve_redirect(response, buffered=buffered) - else: - # This is the final request after redirects. - response.history = tuple(history) - # Close the input stream when closing the response, in case - # the input is an open temporary file. - response.call_on_close(request.input_stream.close) - return response - - def get(self, *args: t.Any, **kw: t.Any) -> TestResponse: - """Call :meth:`open` with ``method`` set to ``GET``.""" - kw["method"] = "GET" - return self.open(*args, **kw) - - def post(self, *args: t.Any, **kw: t.Any) -> TestResponse: - """Call :meth:`open` with ``method`` set to ``POST``.""" - kw["method"] = "POST" - return self.open(*args, **kw) - - def put(self, *args: t.Any, **kw: t.Any) -> TestResponse: - """Call :meth:`open` with ``method`` set to ``PUT``.""" - kw["method"] = "PUT" - return self.open(*args, **kw) - - def delete(self, *args: t.Any, **kw: t.Any) -> TestResponse: - """Call :meth:`open` with ``method`` set to ``DELETE``.""" - kw["method"] = "DELETE" - return self.open(*args, **kw) - - def patch(self, *args: t.Any, **kw: t.Any) -> TestResponse: - """Call :meth:`open` with ``method`` set to ``PATCH``.""" - kw["method"] = "PATCH" - return self.open(*args, **kw) - - def options(self, *args: t.Any, **kw: t.Any) -> TestResponse: - """Call :meth:`open` with ``method`` set to ``OPTIONS``.""" - kw["method"] = "OPTIONS" - return self.open(*args, **kw) - - def head(self, *args: t.Any, **kw: t.Any) -> TestResponse: - """Call :meth:`open` with ``method`` set to ``HEAD``.""" - kw["method"] = "HEAD" - return self.open(*args, **kw) - - def trace(self, *args: t.Any, **kw: t.Any) -> TestResponse: - """Call :meth:`open` with ``method`` set to ``TRACE``.""" - kw["method"] = "TRACE" - return self.open(*args, **kw) - - def __repr__(self) -> str: - return f"<{type(self).__name__} {self.application!r}>" - - -def create_environ(*args: t.Any, **kwargs: t.Any) -> WSGIEnvironment: - """Create a new WSGI environ dict based on the values passed. The first - parameter should be the path of the request which defaults to '/'. The - second one can either be an absolute path (in that case the host is - localhost:80) or a full path to the request with scheme, netloc port and - the path to the script. - - This accepts the same arguments as the :class:`EnvironBuilder` - constructor. - - .. versionchanged:: 0.5 - This function is now a thin wrapper over :class:`EnvironBuilder` which - was added in 0.5. The `headers`, `environ_base`, `environ_overrides` - and `charset` parameters were added. - """ - builder = EnvironBuilder(*args, **kwargs) - - try: - return builder.get_environ() - finally: - builder.close() - - -def run_wsgi_app( - app: WSGIApplication, environ: WSGIEnvironment, buffered: bool = False -) -> tuple[t.Iterable[bytes], str, Headers]: - """Return a tuple in the form (app_iter, status, headers) of the - application output. This works best if you pass it an application that - returns an iterator all the time. - - Sometimes applications may use the `write()` callable returned - by the `start_response` function. This tries to resolve such edge - cases automatically. But if you don't get the expected output you - should set `buffered` to `True` which enforces buffering. - - If passed an invalid WSGI application the behavior of this function is - undefined. Never pass non-conforming WSGI applications to this function. - - :param app: the application to execute. - :param buffered: set to `True` to enforce buffering. - :return: tuple in the form ``(app_iter, status, headers)`` - """ - # Copy environ to ensure any mutations by the app (ProxyFix, for - # example) don't affect subsequent requests (such as redirects). - environ = _get_environ(environ).copy() - status: str - response: tuple[str, list[tuple[str, str]]] | None = None - buffer: list[bytes] = [] - - def start_response(status, headers, exc_info=None): # type: ignore - nonlocal response - - if exc_info: - try: - raise exc_info[1].with_traceback(exc_info[2]) - finally: - exc_info = None - - response = (status, headers) - return buffer.append - - app_rv = app(environ, start_response) - close_func = getattr(app_rv, "close", None) - app_iter: t.Iterable[bytes] = iter(app_rv) - - # when buffering we emit the close call early and convert the - # application iterator into a regular list - if buffered: - try: - app_iter = list(app_iter) - finally: - if close_func is not None: - close_func() - - # otherwise we iterate the application iter until we have a response, chain - # the already received data with the already collected data and wrap it in - # a new `ClosingIterator` if we need to restore a `close` callable from the - # original return value. - else: - for item in app_iter: - buffer.append(item) - - if response is not None: - break - - if buffer: - app_iter = chain(buffer, app_iter) - - if close_func is not None and app_iter is not app_rv: - app_iter = ClosingIterator(app_iter, close_func) - - status, headers = response # type: ignore - return app_iter, status, Headers(headers) - - -class TestResponse(Response): - """:class:`~werkzeug.wrappers.Response` subclass that provides extra - information about requests made with the test :class:`Client`. - - Test client requests will always return an instance of this class. - If a custom response class is passed to the client, it is - subclassed along with this to support test information. - - If the test request included large files, or if the application is - serving a file, call :meth:`close` to close any open files and - prevent Python showing a ``ResourceWarning``. - - .. versionchanged:: 2.2 - Set the ``default_mimetype`` to None to prevent a mimetype being - assumed if missing. - - .. versionchanged:: 2.1 - Response instances cannot be treated as tuples. - - .. versionadded:: 2.0 - Test client methods always return instances of this class. - """ - - default_mimetype = None - # Don't assume a mimetype, instead use whatever the response provides - - request: Request - """A request object with the environ used to make the request that - resulted in this response. - """ - - history: tuple[TestResponse, ...] - """A list of intermediate responses. Populated when the test request - is made with ``follow_redirects`` enabled. - """ - - # Tell Pytest to ignore this, it's not a test class. - __test__ = False - - def __init__( - self, - response: t.Iterable[bytes], - status: str, - headers: Headers, - request: Request, - history: tuple[TestResponse] = (), # type: ignore - **kwargs: t.Any, - ) -> None: - super().__init__(response, status, headers, **kwargs) - self.request = request - self.history = history - self._compat_tuple = response, status, headers - - @cached_property - def text(self) -> str: - """The response data as text. A shortcut for - ``response.get_data(as_text=True)``. - - .. versionadded:: 2.1 - """ - return self.get_data(as_text=True) - - -@dataclasses.dataclass -class Cookie: - """A cookie key, value, and parameters. - - The class itself is not a public API. Its attributes are documented for inspection - with :meth:`.Client.get_cookie` only. - - .. versionadded:: 2.3 - """ - - key: str - """The cookie key, encoded as a client would see it.""" - - value: str - """The cookie key, encoded as a client would see it.""" - - decoded_key: str - """The cookie key, decoded as the application would set and see it.""" - - decoded_value: str - """The cookie value, decoded as the application would set and see it.""" - - expires: datetime | None - """The time at which the cookie is no longer valid.""" - - max_age: int | None - """The number of seconds from when the cookie was set at which it is - no longer valid. - """ - - domain: str - """The domain that the cookie was set for, or the request domain if not set.""" - - origin_only: bool - """Whether the cookie will be sent for exact domain matches only. This is ``True`` - if the ``Domain`` parameter was not present. - """ - - path: str - """The path that the cookie was set for.""" - - secure: bool | None - """The ``Secure`` parameter.""" - - http_only: bool | None - """The ``HttpOnly`` parameter.""" - - same_site: str | None - """The ``SameSite`` parameter.""" - - def _matches_request(self, server_name: str, path: str) -> bool: - return ( - server_name == self.domain - or ( - not self.origin_only - and server_name.endswith(self.domain) - and server_name[: -len(self.domain)].endswith(".") - ) - ) and ( - path == self.path - or ( - path.startswith(self.path) - and path[len(self.path) - self.path.endswith("/") :].startswith("/") - ) - ) - - def _to_request_header(self) -> str: - return f"{self.key}={self.value}" - - @classmethod - def _from_response_header(cls, server_name: str, path: str, header: str) -> te.Self: - header, _, parameters_str = header.partition(";") - key, _, value = header.partition("=") - decoded_key, decoded_value = next(parse_cookie(header).items()) - params = {} - - for item in parameters_str.split(";"): - k, sep, v = item.partition("=") - params[k.strip().lower()] = v.strip() if sep else None - - return cls( - key=key.strip(), - value=value.strip(), - decoded_key=decoded_key, - decoded_value=decoded_value, - expires=parse_date(params.get("expires")), - max_age=int(params["max-age"] or 0) if "max-age" in params else None, - domain=params.get("domain") or server_name, - origin_only="domain" not in params, - path=params.get("path") or path.rpartition("/")[0] or "/", - secure="secure" in params, - http_only="httponly" in params, - same_site=params.get("samesite"), - ) - - @property - def _storage_key(self) -> tuple[str, str, str]: - return self.domain, self.path, self.decoded_key - - @property - def _should_delete(self) -> bool: - return self.max_age == 0 or ( - self.expires is not None and self.expires.timestamp() == 0 - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/testapp.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/testapp.py deleted file mode 100644 index cdf7fac..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/testapp.py +++ /dev/null @@ -1,194 +0,0 @@ -"""A small application that can be used to test a WSGI server and check -it for WSGI compliance. -""" - -from __future__ import annotations - -import importlib.metadata -import os -import sys -import typing as t -from textwrap import wrap - -from markupsafe import escape - -from .wrappers.request import Request -from .wrappers.response import Response - -TEMPLATE = """\ - - -WSGI Information - -
    -

    WSGI Information

    -

    - This page displays all available information about the WSGI server and - the underlying Python interpreter. -

    Python Interpreter

    - - - - - - -
    Python Version - %(python_version)s -
    Platform - %(platform)s [%(os)s] -
    API Version - %(api_version)s -
    Byteorder - %(byteorder)s -
    Werkzeug Version - %(werkzeug_version)s -
    -

    WSGI Environment

    - %(wsgi_env)s
    -

    Installed Eggs

    -

    - The following python packages were installed on the system as - Python eggs: -

      %(python_eggs)s
    -

    System Path

    -

    - The following paths are the current contents of the load path. The - following entries are looked up for Python packages. Note that not - all items in this path are folders. Gray and underlined items are - entries pointing to invalid resources or used by custom import hooks - such as the zip importer. -

    - Items with a bright background were expanded for display from a relative - path. If you encounter such paths in the output you might want to check - your setup as relative paths are usually problematic in multithreaded - environments. -

      %(sys_path)s
    -
    -""" - - -def iter_sys_path() -> t.Iterator[tuple[str, bool, bool]]: - if os.name == "posix": - - def strip(x: str) -> str: - prefix = os.path.expanduser("~") - if x.startswith(prefix): - x = f"~{x[len(prefix) :]}" - return x - - else: - - def strip(x: str) -> str: - return x - - cwd = os.path.abspath(os.getcwd()) - for item in sys.path: - path = os.path.join(cwd, item or os.path.curdir) - yield strip(os.path.normpath(path)), not os.path.isdir(path), path != item - - -@Request.application -def test_app(req: Request) -> Response: - """Simple test application that dumps the environment. You can use - it to check if Werkzeug is working properly: - - .. sourcecode:: pycon - - >>> from werkzeug.serving import run_simple - >>> from werkzeug.testapp import test_app - >>> run_simple('localhost', 3000, test_app) - * Running on http://localhost:3000/ - - The application displays important information from the WSGI environment, - the Python interpreter and the installed libraries. - """ - try: - import pkg_resources - except ImportError: - eggs: t.Iterable[t.Any] = () - else: - eggs = sorted( - pkg_resources.working_set, - key=lambda x: x.project_name.lower(), - ) - python_eggs = [] - for egg in eggs: - try: - version = egg.version - except (ValueError, AttributeError): - version = "unknown" - python_eggs.append( - f"
  • {escape(egg.project_name)} [{escape(version)}]" - ) - - wsgi_env = [] - sorted_environ = sorted(req.environ.items(), key=lambda x: repr(x[0]).lower()) - for key, value in sorted_environ: - value = "".join(wrap(str(escape(repr(value))))) - wsgi_env.append(f"{escape(key)}{value}") - - sys_path = [] - for item, virtual, expanded in iter_sys_path(): - css = [] - if virtual: - css.append("virtual") - if expanded: - css.append("exp") - class_str = f' class="{" ".join(css)}"' if css else "" - sys_path.append(f"{escape(item)}") - - context = { - "python_version": "
    ".join(escape(sys.version).splitlines()), - "platform": escape(sys.platform), - "os": escape(os.name), - "api_version": sys.api_version, - "byteorder": sys.byteorder, - "werkzeug_version": _get_werkzeug_version(), - "python_eggs": "\n".join(python_eggs), - "wsgi_env": "\n".join(wsgi_env), - "sys_path": "\n".join(sys_path), - } - return Response(TEMPLATE % context, mimetype="text/html") - - -_werkzeug_version = "" - - -def _get_werkzeug_version() -> str: - global _werkzeug_version - - if not _werkzeug_version: - _werkzeug_version = importlib.metadata.version("werkzeug") - - return _werkzeug_version - - -if __name__ == "__main__": - from .serving import run_simple - - run_simple("localhost", 5000, test_app, use_reloader=True) diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/urls.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/urls.py deleted file mode 100644 index 5bffe39..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/urls.py +++ /dev/null @@ -1,203 +0,0 @@ -from __future__ import annotations - -import codecs -import re -import typing as t -import urllib.parse -from urllib.parse import quote -from urllib.parse import unquote -from urllib.parse import urlencode -from urllib.parse import urlsplit -from urllib.parse import urlunsplit - -from .datastructures import iter_multi_items - - -def _codec_error_url_quote(e: UnicodeError) -> tuple[str, int]: - """Used in :func:`uri_to_iri` after unquoting to re-quote any - invalid bytes. - """ - # the docs state that UnicodeError does have these attributes, - # but mypy isn't picking them up - out = quote(e.object[e.start : e.end], safe="") # type: ignore - return out, e.end # type: ignore - - -codecs.register_error("werkzeug.url_quote", _codec_error_url_quote) - - -def _make_unquote_part(name: str, chars: str) -> t.Callable[[str], str]: - """Create a function that unquotes all percent encoded characters except those - given. This allows working with unquoted characters if possible while not changing - the meaning of a given part of a URL. - """ - choices = "|".join(f"{ord(c):02X}" for c in sorted(chars)) - pattern = re.compile(f"((?:%(?:{choices}))+)", re.I) - - def _unquote_partial(value: str) -> str: - parts = iter(pattern.split(value)) - out = [] - - for part in parts: - out.append(unquote(part, "utf-8", "werkzeug.url_quote")) - out.append(next(parts, "")) - - return "".join(out) - - _unquote_partial.__name__ = f"_unquote_{name}" - return _unquote_partial - - -# characters that should remain quoted in URL parts -# based on https://url.spec.whatwg.org/#percent-encoded-bytes -# always keep all controls, space, and % quoted -_always_unsafe = bytes((*range(0x21), 0x25, 0x7F)).decode() -_unquote_fragment = _make_unquote_part("fragment", _always_unsafe) -_unquote_query = _make_unquote_part("query", _always_unsafe + "&=+#") -_unquote_path = _make_unquote_part("path", _always_unsafe + "/?#") -_unquote_user = _make_unquote_part("user", _always_unsafe + ":@/?#") - - -def uri_to_iri(uri: str) -> str: - """Convert a URI to an IRI. All valid UTF-8 characters are unquoted, - leaving all reserved and invalid characters quoted. If the URL has - a domain, it is decoded from Punycode. - - >>> uri_to_iri("http://xn--n3h.net/p%C3%A5th?q=%C3%A8ry%DF") - 'http://\\u2603.net/p\\xe5th?q=\\xe8ry%DF' - - :param uri: The URI to convert. - - .. versionchanged:: 3.0 - Passing a tuple or bytes, and the ``charset`` and ``errors`` parameters, - are removed. - - .. versionchanged:: 2.3 - Which characters remain quoted is specific to each part of the URL. - - .. versionchanged:: 0.15 - All reserved and invalid characters remain quoted. Previously, - only some reserved characters were preserved, and invalid bytes - were replaced instead of left quoted. - - .. versionadded:: 0.6 - """ - parts = urlsplit(uri) - path = _unquote_path(parts.path) - query = _unquote_query(parts.query) - fragment = _unquote_fragment(parts.fragment) - - if parts.hostname: - netloc = _decode_idna(parts.hostname) - else: - netloc = "" - - if ":" in netloc: - netloc = f"[{netloc}]" - - if parts.port: - netloc = f"{netloc}:{parts.port}" - - if parts.username: - auth = _unquote_user(parts.username) - - if parts.password: - password = _unquote_user(parts.password) - auth = f"{auth}:{password}" - - netloc = f"{auth}@{netloc}" - - return urlunsplit((parts.scheme, netloc, path, query, fragment)) - - -def iri_to_uri(iri: str) -> str: - """Convert an IRI to a URI. All non-ASCII and unsafe characters are - quoted. If the URL has a domain, it is encoded to Punycode. - - >>> iri_to_uri('http://\\u2603.net/p\\xe5th?q=\\xe8ry%DF') - 'http://xn--n3h.net/p%C3%A5th?q=%C3%A8ry%DF' - - :param iri: The IRI to convert. - - .. versionchanged:: 3.0 - Passing a tuple or bytes, the ``charset`` and ``errors`` parameters, - and the ``safe_conversion`` parameter, are removed. - - .. versionchanged:: 2.3 - Which characters remain unquoted is specific to each part of the URL. - - .. versionchanged:: 0.15 - All reserved characters remain unquoted. Previously, only some reserved - characters were left unquoted. - - .. versionchanged:: 0.9.6 - The ``safe_conversion`` parameter was added. - - .. versionadded:: 0.6 - """ - parts = urlsplit(iri) - # safe = https://url.spec.whatwg.org/#url-path-segment-string - # as well as percent for things that are already quoted - path = quote(parts.path, safe="%!$&'()*+,/:;=@") - query = quote(parts.query, safe="%!$&'()*+,/:;=?@") - fragment = quote(parts.fragment, safe="%!#$&'()*+,/:;=?@") - - if parts.hostname: - netloc = parts.hostname.encode("idna").decode("ascii") - else: - netloc = "" - - if ":" in netloc: - netloc = f"[{netloc}]" - - if parts.port: - netloc = f"{netloc}:{parts.port}" - - if parts.username: - auth = quote(parts.username, safe="%!$&'()*+,;=") - - if parts.password: - password = quote(parts.password, safe="%!$&'()*+,;=") - auth = f"{auth}:{password}" - - netloc = f"{auth}@{netloc}" - - return urlunsplit((parts.scheme, netloc, path, query, fragment)) - - -# Python < 3.12 -# itms-services was worked around in previous iri_to_uri implementations, but -# we can tell Python directly that it needs to preserve the //. -if "itms-services" not in urllib.parse.uses_netloc: - urllib.parse.uses_netloc.append("itms-services") - - -def _decode_idna(domain: str) -> str: - try: - data = domain.encode("ascii") - except UnicodeEncodeError: - # If the domain is not ASCII, it's decoded already. - return domain - - try: - # Try decoding in one shot. - return data.decode("idna") - except UnicodeDecodeError: - pass - - # Decode each part separately, leaving invalid parts as punycode. - parts = [] - - for part in data.split(b"."): - try: - parts.append(part.decode("idna")) - except UnicodeDecodeError: - parts.append(part.decode("ascii")) - - return ".".join(parts) - - -def _urlencode(query: t.Mapping[str, str] | t.Iterable[tuple[str, str]]) -> str: - items = [x for x in iter_multi_items(query) if x[1] is not None] - # safe = https://url.spec.whatwg.org/#percent-encoded-bytes - return urlencode(items, safe="!$'()*,/:;?@") diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/user_agent.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/user_agent.py deleted file mode 100644 index 17e5d3f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/user_agent.py +++ /dev/null @@ -1,47 +0,0 @@ -from __future__ import annotations - - -class UserAgent: - """Represents a parsed user agent header value. - - The default implementation does no parsing, only the :attr:`string` - attribute is set. A subclass may parse the string to set the - common attributes or expose other information. Set - :attr:`werkzeug.wrappers.Request.user_agent_class` to use a - subclass. - - :param string: The header value to parse. - - .. versionadded:: 2.0 - This replaces the previous ``useragents`` module, but does not - provide a built-in parser. - """ - - platform: str | None = None - """The OS name, if it could be parsed from the string.""" - - browser: str | None = None - """The browser name, if it could be parsed from the string.""" - - version: str | None = None - """The browser version, if it could be parsed from the string.""" - - language: str | None = None - """The browser language, if it could be parsed from the string.""" - - def __init__(self, string: str) -> None: - self.string: str = string - """The original header value.""" - - def __repr__(self) -> str: - return f"<{type(self).__name__} {self.browser}/{self.version}>" - - def __str__(self) -> str: - return self.string - - def __bool__(self) -> bool: - return bool(self.browser) - - def to_header(self) -> str: - """Convert to a header value.""" - return self.string diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/utils.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/utils.py deleted file mode 100644 index 59b97b7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/utils.py +++ /dev/null @@ -1,691 +0,0 @@ -from __future__ import annotations - -import io -import mimetypes -import os -import pkgutil -import re -import sys -import typing as t -import unicodedata -from datetime import datetime -from time import time -from urllib.parse import quote -from zlib import adler32 - -from markupsafe import escape - -from ._internal import _DictAccessorProperty -from ._internal import _missing -from ._internal import _TAccessorValue -from .datastructures import Headers -from .exceptions import NotFound -from .exceptions import RequestedRangeNotSatisfiable -from .security import safe_join -from .wsgi import wrap_file - -if t.TYPE_CHECKING: - from _typeshed.wsgi import WSGIEnvironment - - from .wrappers.request import Request - from .wrappers.response import Response - -_T = t.TypeVar("_T") - -_entity_re = re.compile(r"&([^;]+);") -_filename_ascii_strip_re = re.compile(r"[^A-Za-z0-9_.-]") -_windows_device_files = { - "CON", - "PRN", - "AUX", - "NUL", - *(f"COM{i}" for i in range(10)), - *(f"LPT{i}" for i in range(10)), -} - - -class cached_property(property, t.Generic[_T]): - """A :func:`property` that is only evaluated once. Subsequent access - returns the cached value. Setting the property sets the cached - value. Deleting the property clears the cached value, accessing it - again will evaluate it again. - - .. code-block:: python - - class Example: - @cached_property - def value(self): - # calculate something important here - return 42 - - e = Example() - e.value # evaluates - e.value # uses cache - e.value = 16 # sets cache - del e.value # clears cache - - If the class defines ``__slots__``, it must add ``_cache_{name}`` as - a slot. Alternatively, it can add ``__dict__``, but that's usually - not desirable. - - .. versionchanged:: 2.1 - Works with ``__slots__``. - - .. versionchanged:: 2.0 - ``del obj.name`` clears the cached value. - """ - - def __init__( - self, - fget: t.Callable[[t.Any], _T], - name: str | None = None, - doc: str | None = None, - ) -> None: - super().__init__(fget, doc=doc) - self.__name__ = name or fget.__name__ - self.slot_name = f"_cache_{self.__name__}" - self.__module__ = fget.__module__ - - def __set__(self, obj: object, value: _T) -> None: - if hasattr(obj, "__dict__"): - obj.__dict__[self.__name__] = value - else: - setattr(obj, self.slot_name, value) - - def __get__(self, obj: object, type: type = None) -> _T: # type: ignore - if obj is None: - return self # type: ignore - - obj_dict = getattr(obj, "__dict__", None) - - if obj_dict is not None: - value: _T = obj_dict.get(self.__name__, _missing) - else: - value = getattr(obj, self.slot_name, _missing) # type: ignore[arg-type] - - if value is _missing: - value = self.fget(obj) # type: ignore - - if obj_dict is not None: - obj.__dict__[self.__name__] = value - else: - setattr(obj, self.slot_name, value) - - return value - - def __delete__(self, obj: object) -> None: - if hasattr(obj, "__dict__"): - del obj.__dict__[self.__name__] - else: - setattr(obj, self.slot_name, _missing) - - -class environ_property(_DictAccessorProperty[_TAccessorValue]): - """Maps request attributes to environment variables. This works not only - for the Werkzeug request object, but also any other class with an - environ attribute: - - >>> class Test(object): - ... environ = {'key': 'value'} - ... test = environ_property('key') - >>> var = Test() - >>> var.test - 'value' - - If you pass it a second value it's used as default if the key does not - exist, the third one can be a converter that takes a value and converts - it. If it raises :exc:`ValueError` or :exc:`TypeError` the default value - is used. If no default value is provided `None` is used. - - Per default the property is read only. You have to explicitly enable it - by passing ``read_only=False`` to the constructor. - """ - - read_only = True - - def lookup(self, obj: Request) -> WSGIEnvironment: - return obj.environ - - -class header_property(_DictAccessorProperty[_TAccessorValue]): - """Like `environ_property` but for headers.""" - - def lookup(self, obj: Request | Response) -> Headers: - return obj.headers - - -# https://cgit.freedesktop.org/xdg/shared-mime-info/tree/freedesktop.org.xml.in -# https://www.iana.org/assignments/media-types/media-types.xhtml -# Types listed in the XDG mime info that have a charset in the IANA registration. -_charset_mimetypes = { - "application/ecmascript", - "application/javascript", - "application/sql", - "application/xml", - "application/xml-dtd", - "application/xml-external-parsed-entity", -} - - -def get_content_type(mimetype: str, charset: str) -> str: - """Returns the full content type string with charset for a mimetype. - - If the mimetype represents text, the charset parameter will be - appended, otherwise the mimetype is returned unchanged. - - :param mimetype: The mimetype to be used as content type. - :param charset: The charset to be appended for text mimetypes. - :return: The content type. - - .. versionchanged:: 0.15 - Any type that ends with ``+xml`` gets a charset, not just those - that start with ``application/``. Known text types such as - ``application/javascript`` are also given charsets. - """ - if ( - mimetype.startswith("text/") - or mimetype in _charset_mimetypes - or mimetype.endswith("+xml") - ): - mimetype += f"; charset={charset}" - - return mimetype - - -def secure_filename(filename: str) -> str: - r"""Pass it a filename and it will return a secure version of it. This - filename can then safely be stored on a regular file system and passed - to :func:`os.path.join`. The filename returned is an ASCII only string - for maximum portability. - - On windows systems the function also makes sure that the file is not - named after one of the special device files. - - >>> secure_filename("My cool movie.mov") - 'My_cool_movie.mov' - >>> secure_filename("../../../etc/passwd") - 'etc_passwd' - >>> secure_filename('i contain cool \xfcml\xe4uts.txt') - 'i_contain_cool_umlauts.txt' - - The function might return an empty filename. It's your responsibility - to ensure that the filename is unique and that you abort or - generate a random filename if the function returned an empty one. - - .. versionadded:: 0.5 - - :param filename: the filename to secure - """ - filename = unicodedata.normalize("NFKD", filename) - filename = filename.encode("ascii", "ignore").decode("ascii") - - for sep in os.sep, os.path.altsep: - if sep: - filename = filename.replace(sep, " ") - filename = str(_filename_ascii_strip_re.sub("", "_".join(filename.split()))).strip( - "._" - ) - - # on nt a couple of special files are present in each folder. We - # have to ensure that the target file is not such a filename. In - # this case we prepend an underline - if ( - os.name == "nt" - and filename - and filename.split(".")[0].upper() in _windows_device_files - ): - filename = f"_{filename}" - - return filename - - -def redirect( - location: str, code: int = 302, Response: type[Response] | None = None -) -> Response: - """Returns a response object (a WSGI application) that, if called, - redirects the client to the target location. Supported codes are - 301, 302, 303, 305, 307, and 308. 300 is not supported because - it's not a real redirect and 304 because it's the answer for a - request with a request with defined If-Modified-Since headers. - - .. versionadded:: 0.6 - The location can now be a unicode string that is encoded using - the :func:`iri_to_uri` function. - - .. versionadded:: 0.10 - The class used for the Response object can now be passed in. - - :param location: the location the response should redirect to. - :param code: the redirect status code. defaults to 302. - :param class Response: a Response class to use when instantiating a - response. The default is :class:`werkzeug.wrappers.Response` if - unspecified. - """ - if Response is None: - from .wrappers import Response - - html_location = escape(location) - response = Response( # type: ignore[misc] - "\n" - "\n" - "Redirecting...\n" - "

    Redirecting...

    \n" - "

    You should be redirected automatically to the target URL: " - f'{html_location}. If not, click the link.\n', - code, - mimetype="text/html", - ) - response.headers["Location"] = location - return response - - -def append_slash_redirect(environ: WSGIEnvironment, code: int = 308) -> Response: - """Redirect to the current URL with a slash appended. - - If the current URL is ``/user/42``, the redirect URL will be - ``42/``. When joined to the current URL during response - processing or by the browser, this will produce ``/user/42/``. - - The behavior is undefined if the path ends with a slash already. If - called unconditionally on a URL, it may produce a redirect loop. - - :param environ: Use the path and query from this WSGI environment - to produce the redirect URL. - :param code: the status code for the redirect. - - .. versionchanged:: 2.1 - Produce a relative URL that only modifies the last segment. - Relevant when the current path has multiple segments. - - .. versionchanged:: 2.1 - The default status code is 308 instead of 301. This preserves - the request method and body. - """ - tail = environ["PATH_INFO"].rpartition("/")[2] - - if not tail: - new_path = "./" - else: - new_path = f"{tail}/" - - query_string = environ.get("QUERY_STRING") - - if query_string: - new_path = f"{new_path}?{query_string}" - - return redirect(new_path, code) - - -def send_file( - path_or_file: os.PathLike[str] | str | t.IO[bytes], - environ: WSGIEnvironment, - mimetype: str | None = None, - as_attachment: bool = False, - download_name: str | None = None, - conditional: bool = True, - etag: bool | str = True, - last_modified: datetime | int | float | None = None, - max_age: None | (int | t.Callable[[str | None], int | None]) = None, - use_x_sendfile: bool = False, - response_class: type[Response] | None = None, - _root_path: os.PathLike[str] | str | None = None, -) -> Response: - """Send the contents of a file to the client. - - The first argument can be a file path or a file-like object. Paths - are preferred in most cases because Werkzeug can manage the file and - get extra information from the path. Passing a file-like object - requires that the file is opened in binary mode, and is mostly - useful when building a file in memory with :class:`io.BytesIO`. - - Never pass file paths provided by a user. The path is assumed to be - trusted, so a user could craft a path to access a file you didn't - intend. Use :func:`send_from_directory` to safely serve user-provided paths. - - If the WSGI server sets a ``file_wrapper`` in ``environ``, it is - used, otherwise Werkzeug's built-in wrapper is used. Alternatively, - if the HTTP server supports ``X-Sendfile``, ``use_x_sendfile=True`` - will tell the server to send the given path, which is much more - efficient than reading it in Python. - - :param path_or_file: The path to the file to send, relative to the - current working directory if a relative path is given. - Alternatively, a file-like object opened in binary mode. Make - sure the file pointer is seeked to the start of the data. - :param environ: The WSGI environ for the current request. - :param mimetype: The MIME type to send for the file. If not - provided, it will try to detect it from the file name. - :param as_attachment: Indicate to a browser that it should offer to - save the file instead of displaying it. - :param download_name: The default name browsers will use when saving - the file. Defaults to the passed file name. - :param conditional: Enable conditional and range responses based on - request headers. Requires passing a file path and ``environ``. - :param etag: Calculate an ETag for the file, which requires passing - a file path. Can also be a string to use instead. - :param last_modified: The last modified time to send for the file, - in seconds. If not provided, it will try to detect it from the - file path. - :param max_age: How long the client should cache the file, in - seconds. If set, ``Cache-Control`` will be ``public``, otherwise - it will be ``no-cache`` to prefer conditional caching. - :param use_x_sendfile: Set the ``X-Sendfile`` header to let the - server to efficiently send the file. Requires support from the - HTTP server. Requires passing a file path. - :param response_class: Build the response using this class. Defaults - to :class:`~werkzeug.wrappers.Response`. - :param _root_path: Do not use. For internal use only. Use - :func:`send_from_directory` to safely send files under a path. - - .. versionchanged:: 2.0.2 - ``send_file`` only sets a detected ``Content-Encoding`` if - ``as_attachment`` is disabled. - - .. versionadded:: 2.0 - Adapted from Flask's implementation. - - .. versionchanged:: 2.0 - ``download_name`` replaces Flask's ``attachment_filename`` - parameter. If ``as_attachment=False``, it is passed with - ``Content-Disposition: inline`` instead. - - .. versionchanged:: 2.0 - ``max_age`` replaces Flask's ``cache_timeout`` parameter. - ``conditional`` is enabled and ``max_age`` is not set by - default. - - .. versionchanged:: 2.0 - ``etag`` replaces Flask's ``add_etags`` parameter. It can be a - string to use instead of generating one. - - .. versionchanged:: 2.0 - If an encoding is returned when guessing ``mimetype`` from - ``download_name``, set the ``Content-Encoding`` header. - """ - if response_class is None: - from .wrappers import Response - - response_class = Response - - path: str | None = None - file: t.IO[bytes] | None = None - size: int | None = None - mtime: float | None = None - headers = Headers() - - if isinstance(path_or_file, (os.PathLike, str)) or hasattr( - path_or_file, "__fspath__" - ): - path_or_file = t.cast("t.Union[os.PathLike[str], str]", path_or_file) - - # Flask will pass app.root_path, allowing its send_file wrapper - # to not have to deal with paths. - if _root_path is not None: - path = os.path.join(_root_path, path_or_file) - else: - path = os.path.abspath(path_or_file) - - stat = os.stat(path) - size = stat.st_size - mtime = stat.st_mtime - else: - file = path_or_file - - if download_name is None and path is not None: - download_name = os.path.basename(path) - - if mimetype is None: - if download_name is None: - raise TypeError( - "Unable to detect the MIME type because a file name is" - " not available. Either set 'download_name', pass a" - " path instead of a file, or set 'mimetype'." - ) - - mimetype, encoding = mimetypes.guess_type(download_name) - - if mimetype is None: - mimetype = "application/octet-stream" - - # Don't send encoding for attachments, it causes browsers to - # save decompress tar.gz files. - if encoding is not None and not as_attachment: - headers.set("Content-Encoding", encoding) - - if download_name is not None: - try: - download_name.encode("ascii") - except UnicodeEncodeError: - simple = unicodedata.normalize("NFKD", download_name) - simple = simple.encode("ascii", "ignore").decode("ascii") - # safe = RFC 5987 attr-char - quoted = quote(download_name, safe="!#$&+-.^_`|~") - names = {"filename": simple, "filename*": f"UTF-8''{quoted}"} - else: - names = {"filename": download_name} - - value = "attachment" if as_attachment else "inline" - headers.set("Content-Disposition", value, **names) - elif as_attachment: - raise TypeError( - "No name provided for attachment. Either set" - " 'download_name' or pass a path instead of a file." - ) - - if use_x_sendfile and path is not None: - headers["X-Sendfile"] = path - data = None - else: - if file is None: - file = open(path, "rb") # type: ignore - elif isinstance(file, io.BytesIO): - size = file.getbuffer().nbytes - elif isinstance(file, io.TextIOBase): - raise ValueError("Files must be opened in binary mode or use BytesIO.") - - data = wrap_file(environ, file) - - rv = response_class( - data, mimetype=mimetype, headers=headers, direct_passthrough=True - ) - - if size is not None: - rv.content_length = size - - if last_modified is not None: - rv.last_modified = last_modified # type: ignore - elif mtime is not None: - rv.last_modified = mtime # type: ignore - - rv.cache_control.no_cache = True - - # Flask will pass app.get_send_file_max_age, allowing its send_file - # wrapper to not have to deal with paths. - if callable(max_age): - max_age = max_age(path) - - if max_age is not None: - if max_age > 0: - rv.cache_control.no_cache = None - rv.cache_control.public = True - - rv.cache_control.max_age = max_age - rv.expires = int(time() + max_age) # type: ignore - - if isinstance(etag, str): - rv.set_etag(etag) - elif etag and path is not None: - check = adler32(path.encode()) & 0xFFFFFFFF - rv.set_etag(f"{mtime}-{size}-{check}") - - if conditional: - try: - rv = rv.make_conditional(environ, accept_ranges=True, complete_length=size) - except RequestedRangeNotSatisfiable: - if file is not None: - file.close() - - raise - - # Some x-sendfile implementations incorrectly ignore the 304 - # status code and send the file anyway. - if rv.status_code == 304: - rv.headers.pop("x-sendfile", None) - - return rv - - -def send_from_directory( - directory: os.PathLike[str] | str, - path: os.PathLike[str] | str, - environ: WSGIEnvironment, - **kwargs: t.Any, -) -> Response: - """Send a file from within a directory using :func:`send_file`. - - This is a secure way to serve files from a folder, such as static - files or uploads. Uses :func:`~werkzeug.security.safe_join` to - ensure the path coming from the client is not maliciously crafted to - point outside the specified directory. - - If the final path does not point to an existing regular file, - returns a 404 :exc:`~werkzeug.exceptions.NotFound` error. - - :param directory: The directory that ``path`` must be located under. This *must not* - be a value provided by the client, otherwise it becomes insecure. - :param path: The path to the file to send, relative to ``directory``. This is the - part of the path provided by the client, which is checked for security. - :param environ: The WSGI environ for the current request. - :param kwargs: Arguments to pass to :func:`send_file`. - - .. versionadded:: 2.0 - Adapted from Flask's implementation. - """ - path_str = safe_join(os.fspath(directory), os.fspath(path)) - - if path_str is None: - raise NotFound() - - # Flask will pass app.root_path, allowing its send_from_directory - # wrapper to not have to deal with paths. - if "_root_path" in kwargs: - path_str = os.path.join(kwargs["_root_path"], path_str) - - if not os.path.isfile(path_str): - raise NotFound() - - return send_file(path_str, environ, **kwargs) - - -def import_string(import_name: str, silent: bool = False) -> t.Any: - """Imports an object based on a string. This is useful if you want to - use import paths as endpoints or something similar. An import path can - be specified either in dotted notation (``xml.sax.saxutils.escape``) - or with a colon as object delimiter (``xml.sax.saxutils:escape``). - - If `silent` is True the return value will be `None` if the import fails. - - :param import_name: the dotted name for the object to import. - :param silent: if set to `True` import errors are ignored and - `None` is returned instead. - :return: imported object - """ - import_name = import_name.replace(":", ".") - try: - try: - __import__(import_name) - except ImportError: - if "." not in import_name: - raise - else: - return sys.modules[import_name] - - module_name, obj_name = import_name.rsplit(".", 1) - module = __import__(module_name, globals(), locals(), [obj_name]) - try: - return getattr(module, obj_name) - except AttributeError as e: - raise ImportError(e) from None - - except ImportError as e: - if not silent: - raise ImportStringError(import_name, e).with_traceback( - sys.exc_info()[2] - ) from None - - return None - - -def find_modules( - import_path: str, include_packages: bool = False, recursive: bool = False -) -> t.Iterator[str]: - """Finds all the modules below a package. This can be useful to - automatically import all views / controllers so that their metaclasses / - function decorators have a chance to register themselves on the - application. - - Packages are not returned unless `include_packages` is `True`. This can - also recursively list modules but in that case it will import all the - packages to get the correct load path of that module. - - :param import_path: the dotted name for the package to find child modules. - :param include_packages: set to `True` if packages should be returned, too. - :param recursive: set to `True` if recursion should happen. - :return: generator - """ - module = import_string(import_path) - path = getattr(module, "__path__", None) - if path is None: - raise ValueError(f"{import_path!r} is not a package") - basename = f"{module.__name__}." - for _importer, modname, ispkg in pkgutil.iter_modules(path): - modname = basename + modname - if ispkg: - if include_packages: - yield modname - if recursive: - yield from find_modules(modname, include_packages, True) - else: - yield modname - - -class ImportStringError(ImportError): - """Provides information about a failed :func:`import_string` attempt.""" - - #: String in dotted notation that failed to be imported. - import_name: str - #: Wrapped exception. - exception: BaseException - - def __init__(self, import_name: str, exception: BaseException) -> None: - self.import_name = import_name - self.exception = exception - msg = import_name - name = "" - tracked = [] - for part in import_name.replace(":", ".").split("."): - name = f"{name}.{part}" if name else part - imported = import_string(name, silent=True) - if imported: - tracked.append((name, getattr(imported, "__file__", None))) - else: - track = [f"- {n!r} found in {i!r}." for n, i in tracked] - track.append(f"- {name!r} not found.") - track_str = "\n".join(track) - msg = ( - f"import_string() failed for {import_name!r}. Possible reasons" - f" are:\n\n" - "- missing __init__.py in a package;\n" - "- package or module path not included in sys.path;\n" - "- duplicated package or module name taking precedence in" - " sys.path;\n" - "- missing module, class, function or variable;\n\n" - f"Debugged import:\n\n{track_str}\n\n" - f"Original exception:\n\n{type(exception).__name__}: {exception}" - ) - break - - super().__init__(msg) - - def __repr__(self) -> str: - return f"<{type(self).__name__}({self.import_name!r}, {self.exception!r})>" diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__init__.py deleted file mode 100644 index b36f228..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .request import Request as Request -from .response import Response as Response -from .response import ResponseStream as ResponseStream diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index f95c300..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/request.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/request.cpython-38.pyc deleted file mode 100644 index a441084..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/request.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/response.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/response.cpython-38.pyc deleted file mode 100644 index 07c448c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/response.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/request.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/request.py deleted file mode 100644 index 344f28b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/request.py +++ /dev/null @@ -1,647 +0,0 @@ -from __future__ import annotations - -import collections.abc as cabc -import functools -import json -import typing as t -from io import BytesIO - -from .._internal import _wsgi_decoding_dance -from ..datastructures import CombinedMultiDict -from ..datastructures import EnvironHeaders -from ..datastructures import FileStorage -from ..datastructures import ImmutableMultiDict -from ..datastructures import iter_multi_items -from ..datastructures import MultiDict -from ..exceptions import BadRequest -from ..exceptions import UnsupportedMediaType -from ..formparser import default_stream_factory -from ..formparser import FormDataParser -from ..sansio.request import Request as _SansIORequest -from ..utils import cached_property -from ..utils import environ_property -from ..wsgi import _get_server -from ..wsgi import get_input_stream - -if t.TYPE_CHECKING: - from _typeshed.wsgi import WSGIApplication - from _typeshed.wsgi import WSGIEnvironment - - -class Request(_SansIORequest): - """Represents an incoming WSGI HTTP request, with headers and body - taken from the WSGI environment. Has properties and methods for - using the functionality defined by various HTTP specs. The data in - requests object is read-only. - - Text data is assumed to use UTF-8 encoding, which should be true for - the vast majority of modern clients. Using an encoding set by the - client is unsafe in Python due to extra encodings it provides, such - as ``zip``. To change the assumed encoding, subclass and replace - :attr:`charset`. - - :param environ: The WSGI environ is generated by the WSGI server and - contains information about the server configuration and client - request. - :param populate_request: Add this request object to the WSGI environ - as ``environ['werkzeug.request']``. Can be useful when - debugging. - :param shallow: Makes reading from :attr:`stream` (and any method - that would read from it) raise a :exc:`RuntimeError`. Useful to - prevent consuming the form data in middleware, which would make - it unavailable to the final application. - - .. versionchanged:: 3.0 - The ``charset``, ``url_charset``, and ``encoding_errors`` parameters - were removed. - - .. versionchanged:: 2.1 - Old ``BaseRequest`` and mixin classes were removed. - - .. versionchanged:: 2.1 - Remove the ``disable_data_descriptor`` attribute. - - .. versionchanged:: 2.0 - Combine ``BaseRequest`` and mixins into a single ``Request`` - class. - - .. versionchanged:: 0.5 - Read-only mode is enforced with immutable classes for all data. - """ - - #: the maximum content length. This is forwarded to the form data - #: parsing function (:func:`parse_form_data`). When set and the - #: :attr:`form` or :attr:`files` attribute is accessed and the - #: parsing fails because more than the specified value is transmitted - #: a :exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception is raised. - #: - #: .. versionadded:: 0.5 - max_content_length: int | None = None - - #: the maximum form field size. This is forwarded to the form data - #: parsing function (:func:`parse_form_data`). When set and the - #: :attr:`form` or :attr:`files` attribute is accessed and the - #: data in memory for post data is longer than the specified value a - #: :exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception is raised. - #: - #: .. versionadded:: 0.5 - max_form_memory_size: int | None = None - - #: The maximum number of multipart parts to parse, passed to - #: :attr:`form_data_parser_class`. Parsing form data with more than this - #: many parts will raise :exc:`~.RequestEntityTooLarge`. - #: - #: .. versionadded:: 2.2.3 - max_form_parts = 1000 - - #: The form data parser that should be used. Can be replaced to customize - #: the form date parsing. - form_data_parser_class: type[FormDataParser] = FormDataParser - - #: The WSGI environment containing HTTP headers and information from - #: the WSGI server. - environ: WSGIEnvironment - - #: Set when creating the request object. If ``True``, reading from - #: the request body will cause a ``RuntimeException``. Useful to - #: prevent modifying the stream from middleware. - shallow: bool - - def __init__( - self, - environ: WSGIEnvironment, - populate_request: bool = True, - shallow: bool = False, - ) -> None: - super().__init__( - method=environ.get("REQUEST_METHOD", "GET"), - scheme=environ.get("wsgi.url_scheme", "http"), - server=_get_server(environ), - root_path=_wsgi_decoding_dance(environ.get("SCRIPT_NAME") or ""), - path=_wsgi_decoding_dance(environ.get("PATH_INFO") or ""), - query_string=environ.get("QUERY_STRING", "").encode("latin1"), - headers=EnvironHeaders(environ), - remote_addr=environ.get("REMOTE_ADDR"), - ) - self.environ = environ - self.shallow = shallow - - if populate_request and not shallow: - self.environ["werkzeug.request"] = self - - @classmethod - def from_values(cls, *args: t.Any, **kwargs: t.Any) -> Request: - """Create a new request object based on the values provided. If - environ is given missing values are filled from there. This method is - useful for small scripts when you need to simulate a request from an URL. - Do not use this method for unittesting, there is a full featured client - object (:class:`Client`) that allows to create multipart requests, - support for cookies etc. - - This accepts the same options as the - :class:`~werkzeug.test.EnvironBuilder`. - - .. versionchanged:: 0.5 - This method now accepts the same arguments as - :class:`~werkzeug.test.EnvironBuilder`. Because of this the - `environ` parameter is now called `environ_overrides`. - - :return: request object - """ - from ..test import EnvironBuilder - - builder = EnvironBuilder(*args, **kwargs) - try: - return builder.get_request(cls) - finally: - builder.close() - - @classmethod - def application(cls, f: t.Callable[[Request], WSGIApplication]) -> WSGIApplication: - """Decorate a function as responder that accepts the request as - the last argument. This works like the :func:`responder` - decorator but the function is passed the request object as the - last argument and the request object will be closed - automatically:: - - @Request.application - def my_wsgi_app(request): - return Response('Hello World!') - - As of Werkzeug 0.14 HTTP exceptions are automatically caught and - converted to responses instead of failing. - - :param f: the WSGI callable to decorate - :return: a new WSGI callable - """ - #: return a callable that wraps the -2nd argument with the request - #: and calls the function with all the arguments up to that one and - #: the request. The return value is then called with the latest - #: two arguments. This makes it possible to use this decorator for - #: both standalone WSGI functions as well as bound methods and - #: partially applied functions. - from ..exceptions import HTTPException - - @functools.wraps(f) - def application(*args: t.Any) -> cabc.Iterable[bytes]: - request = cls(args[-2]) - with request: - try: - resp = f(*args[:-2] + (request,)) - except HTTPException as e: - resp = t.cast("WSGIApplication", e.get_response(args[-2])) - return resp(*args[-2:]) - - return t.cast("WSGIApplication", application) - - def _get_file_stream( - self, - total_content_length: int | None, - content_type: str | None, - filename: str | None = None, - content_length: int | None = None, - ) -> t.IO[bytes]: - """Called to get a stream for the file upload. - - This must provide a file-like class with `read()`, `readline()` - and `seek()` methods that is both writeable and readable. - - The default implementation returns a temporary file if the total - content length is higher than 500KB. Because many browsers do not - provide a content length for the files only the total content - length matters. - - :param total_content_length: the total content length of all the - data in the request combined. This value - is guaranteed to be there. - :param content_type: the mimetype of the uploaded file. - :param filename: the filename of the uploaded file. May be `None`. - :param content_length: the length of this file. This value is usually - not provided because webbrowsers do not provide - this value. - """ - return default_stream_factory( - total_content_length=total_content_length, - filename=filename, - content_type=content_type, - content_length=content_length, - ) - - @property - def want_form_data_parsed(self) -> bool: - """``True`` if the request method carries content. By default - this is true if a ``Content-Type`` is sent. - - .. versionadded:: 0.8 - """ - return bool(self.environ.get("CONTENT_TYPE")) - - def make_form_data_parser(self) -> FormDataParser: - """Creates the form data parser. Instantiates the - :attr:`form_data_parser_class` with some parameters. - - .. versionadded:: 0.8 - """ - return self.form_data_parser_class( - stream_factory=self._get_file_stream, - max_form_memory_size=self.max_form_memory_size, - max_content_length=self.max_content_length, - max_form_parts=self.max_form_parts, - cls=self.parameter_storage_class, - ) - - def _load_form_data(self) -> None: - """Method used internally to retrieve submitted data. After calling - this sets `form` and `files` on the request object to multi dicts - filled with the incoming form data. As a matter of fact the input - stream will be empty afterwards. You can also call this method to - force the parsing of the form data. - - .. versionadded:: 0.8 - """ - # abort early if we have already consumed the stream - if "form" in self.__dict__: - return - - if self.want_form_data_parsed: - parser = self.make_form_data_parser() - data = parser.parse( - self._get_stream_for_parsing(), - self.mimetype, - self.content_length, - self.mimetype_params, - ) - else: - data = ( - self.stream, - self.parameter_storage_class(), - self.parameter_storage_class(), - ) - - # inject the values into the instance dict so that we bypass - # our cached_property non-data descriptor. - d = self.__dict__ - d["stream"], d["form"], d["files"] = data - - def _get_stream_for_parsing(self) -> t.IO[bytes]: - """This is the same as accessing :attr:`stream` with the difference - that if it finds cached data from calling :meth:`get_data` first it - will create a new stream out of the cached data. - - .. versionadded:: 0.9.3 - """ - cached_data = getattr(self, "_cached_data", None) - if cached_data is not None: - return BytesIO(cached_data) - return self.stream - - def close(self) -> None: - """Closes associated resources of this request object. This - closes all file handles explicitly. You can also use the request - object in a with statement which will automatically close it. - - .. versionadded:: 0.9 - """ - files = self.__dict__.get("files") - for _key, value in iter_multi_items(files or ()): - value.close() - - def __enter__(self) -> Request: - return self - - def __exit__(self, exc_type, exc_value, tb) -> None: # type: ignore - self.close() - - @cached_property - def stream(self) -> t.IO[bytes]: - """The WSGI input stream, with safety checks. This stream can only be consumed - once. - - Use :meth:`get_data` to get the full data as bytes or text. The :attr:`data` - attribute will contain the full bytes only if they do not represent form data. - The :attr:`form` attribute will contain the parsed form data in that case. - - Unlike :attr:`input_stream`, this stream guards against infinite streams or - reading past :attr:`content_length` or :attr:`max_content_length`. - - If ``max_content_length`` is set, it can be enforced on streams if - ``wsgi.input_terminated`` is set. Otherwise, an empty stream is returned. - - If the limit is reached before the underlying stream is exhausted (such as a - file that is too large, or an infinite stream), the remaining contents of the - stream cannot be read safely. Depending on how the server handles this, clients - may show a "connection reset" failure instead of seeing the 413 response. - - .. versionchanged:: 2.3 - Check ``max_content_length`` preemptively and while reading. - - .. versionchanged:: 0.9 - The stream is always set (but may be consumed) even if form parsing was - accessed first. - """ - if self.shallow: - raise RuntimeError( - "This request was created with 'shallow=True', reading" - " from the input stream is disabled." - ) - - return get_input_stream( - self.environ, max_content_length=self.max_content_length - ) - - input_stream = environ_property[t.IO[bytes]]( - "wsgi.input", - doc="""The raw WSGI input stream, without any safety checks. - - This is dangerous to use. It does not guard against infinite streams or reading - past :attr:`content_length` or :attr:`max_content_length`. - - Use :attr:`stream` instead. - """, - ) - - @cached_property - def data(self) -> bytes: - """The raw data read from :attr:`stream`. Will be empty if the request - represents form data. - - To get the raw data even if it represents form data, use :meth:`get_data`. - """ - return self.get_data(parse_form_data=True) - - @t.overload - def get_data( - self, - cache: bool = True, - as_text: t.Literal[False] = False, - parse_form_data: bool = False, - ) -> bytes: ... - - @t.overload - def get_data( - self, - cache: bool = True, - as_text: t.Literal[True] = ..., - parse_form_data: bool = False, - ) -> str: ... - - def get_data( - self, cache: bool = True, as_text: bool = False, parse_form_data: bool = False - ) -> bytes | str: - """This reads the buffered incoming data from the client into one - bytes object. By default this is cached but that behavior can be - changed by setting `cache` to `False`. - - Usually it's a bad idea to call this method without checking the - content length first as a client could send dozens of megabytes or more - to cause memory problems on the server. - - Note that if the form data was already parsed this method will not - return anything as form data parsing does not cache the data like - this method does. To implicitly invoke form data parsing function - set `parse_form_data` to `True`. When this is done the return value - of this method will be an empty string if the form parser handles - the data. This generally is not necessary as if the whole data is - cached (which is the default) the form parser will used the cached - data to parse the form data. Please be generally aware of checking - the content length first in any case before calling this method - to avoid exhausting server memory. - - If `as_text` is set to `True` the return value will be a decoded - string. - - .. versionadded:: 0.9 - """ - rv = getattr(self, "_cached_data", None) - if rv is None: - if parse_form_data: - self._load_form_data() - rv = self.stream.read() - if cache: - self._cached_data = rv - if as_text: - rv = rv.decode(errors="replace") - return rv - - @cached_property - def form(self) -> ImmutableMultiDict[str, str]: - """The form parameters. By default an - :class:`~werkzeug.datastructures.ImmutableMultiDict` - is returned from this function. This can be changed by setting - :attr:`parameter_storage_class` to a different type. This might - be necessary if the order of the form data is important. - - Please keep in mind that file uploads will not end up here, but instead - in the :attr:`files` attribute. - - .. versionchanged:: 0.9 - - Previous to Werkzeug 0.9 this would only contain form data for POST - and PUT requests. - """ - self._load_form_data() - return self.form - - @cached_property - def values(self) -> CombinedMultiDict[str, str]: - """A :class:`werkzeug.datastructures.CombinedMultiDict` that - combines :attr:`args` and :attr:`form`. - - For GET requests, only ``args`` are present, not ``form``. - - .. versionchanged:: 2.0 - For GET requests, only ``args`` are present, not ``form``. - """ - sources = [self.args] - - if self.method != "GET": - # GET requests can have a body, and some caching proxies - # might not treat that differently than a normal GET - # request, allowing form data to "invisibly" affect the - # cache without indication in the query string / URL. - sources.append(self.form) - - args = [] - - for d in sources: - if not isinstance(d, MultiDict): - d = MultiDict(d) - - args.append(d) - - return CombinedMultiDict(args) - - @cached_property - def files(self) -> ImmutableMultiDict[str, FileStorage]: - """:class:`~werkzeug.datastructures.MultiDict` object containing - all uploaded files. Each key in :attr:`files` is the name from the - ````. Each value in :attr:`files` is a - Werkzeug :class:`~werkzeug.datastructures.FileStorage` object. - - It basically behaves like a standard file object you know from Python, - with the difference that it also has a - :meth:`~werkzeug.datastructures.FileStorage.save` function that can - store the file on the filesystem. - - Note that :attr:`files` will only contain data if the request method was - POST, PUT or PATCH and the ``

    `` that posted to the request had - ``enctype="multipart/form-data"``. It will be empty otherwise. - - See the :class:`~werkzeug.datastructures.MultiDict` / - :class:`~werkzeug.datastructures.FileStorage` documentation for - more details about the used data structure. - """ - self._load_form_data() - return self.files - - @property - def script_root(self) -> str: - """Alias for :attr:`self.root_path`. ``environ["SCRIPT_ROOT"]`` - without a trailing slash. - """ - return self.root_path - - @cached_property - def url_root(self) -> str: - """Alias for :attr:`root_url`. The URL with scheme, host, and - root path. For example, ``https://example.com/app/``. - """ - return self.root_url - - remote_user = environ_property[str]( - "REMOTE_USER", - doc="""If the server supports user authentication, and the - script is protected, this attribute contains the username the - user has authenticated as.""", - ) - is_multithread = environ_property[bool]( - "wsgi.multithread", - doc="""boolean that is `True` if the application is served by a - multithreaded WSGI server.""", - ) - is_multiprocess = environ_property[bool]( - "wsgi.multiprocess", - doc="""boolean that is `True` if the application is served by a - WSGI server that spawns multiple processes.""", - ) - is_run_once = environ_property[bool]( - "wsgi.run_once", - doc="""boolean that is `True` if the application will be - executed only once in a process lifetime. This is the case for - CGI for example, but it's not guaranteed that the execution only - happens one time.""", - ) - - # JSON - - #: A module or other object that has ``dumps`` and ``loads`` - #: functions that match the API of the built-in :mod:`json` module. - json_module = json - - @property - def json(self) -> t.Any | None: - """The parsed JSON data if :attr:`mimetype` indicates JSON - (:mimetype:`application/json`, see :attr:`is_json`). - - Calls :meth:`get_json` with default arguments. - - If the request content type is not ``application/json``, this - will raise a 415 Unsupported Media Type error. - - .. versionchanged:: 2.3 - Raise a 415 error instead of 400. - - .. versionchanged:: 2.1 - Raise a 400 error if the content type is incorrect. - """ - return self.get_json() - - # Cached values for ``(silent=False, silent=True)``. Initialized - # with sentinel values. - _cached_json: tuple[t.Any, t.Any] = (Ellipsis, Ellipsis) - - @t.overload - def get_json( - self, force: bool = ..., silent: t.Literal[False] = ..., cache: bool = ... - ) -> t.Any: ... - - @t.overload - def get_json( - self, force: bool = ..., silent: bool = ..., cache: bool = ... - ) -> t.Any | None: ... - - def get_json( - self, force: bool = False, silent: bool = False, cache: bool = True - ) -> t.Any | None: - """Parse :attr:`data` as JSON. - - If the mimetype does not indicate JSON - (:mimetype:`application/json`, see :attr:`is_json`), or parsing - fails, :meth:`on_json_loading_failed` is called and - its return value is used as the return value. By default this - raises a 415 Unsupported Media Type resp. - - :param force: Ignore the mimetype and always try to parse JSON. - :param silent: Silence mimetype and parsing errors, and - return ``None`` instead. - :param cache: Store the parsed JSON to return for subsequent - calls. - - .. versionchanged:: 2.3 - Raise a 415 error instead of 400. - - .. versionchanged:: 2.1 - Raise a 400 error if the content type is incorrect. - """ - if cache and self._cached_json[silent] is not Ellipsis: - return self._cached_json[silent] - - if not (force or self.is_json): - if not silent: - return self.on_json_loading_failed(None) - else: - return None - - data = self.get_data(cache=cache) - - try: - rv = self.json_module.loads(data) - except ValueError as e: - if silent: - rv = None - - if cache: - normal_rv, _ = self._cached_json - self._cached_json = (normal_rv, rv) - else: - rv = self.on_json_loading_failed(e) - - if cache: - _, silent_rv = self._cached_json - self._cached_json = (rv, silent_rv) - else: - if cache: - self._cached_json = (rv, rv) - - return rv - - def on_json_loading_failed(self, e: ValueError | None) -> t.Any: - """Called if :meth:`get_json` fails and isn't silenced. - - If this method returns a value, it is used as the return value - for :meth:`get_json`. The default implementation raises - :exc:`~werkzeug.exceptions.BadRequest`. - - :param e: If parsing failed, this is the exception. It will be - ``None`` if the content type wasn't ``application/json``. - - .. versionchanged:: 2.3 - Raise a 415 error instead of 400. - """ - if e is not None: - raise BadRequest(f"Failed to decode JSON object: {e}") - - raise UnsupportedMediaType( - "Did not attempt to load JSON data because the request" - " Content-Type was not 'application/json'." - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/response.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/response.py deleted file mode 100644 index 7f01287..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/response.py +++ /dev/null @@ -1,831 +0,0 @@ -from __future__ import annotations - -import json -import typing as t -from http import HTTPStatus -from urllib.parse import urljoin - -from .._internal import _get_environ -from ..datastructures import Headers -from ..http import generate_etag -from ..http import http_date -from ..http import is_resource_modified -from ..http import parse_etags -from ..http import parse_range_header -from ..http import remove_entity_headers -from ..sansio.response import Response as _SansIOResponse -from ..urls import iri_to_uri -from ..utils import cached_property -from ..wsgi import _RangeWrapper -from ..wsgi import ClosingIterator -from ..wsgi import get_current_url - -if t.TYPE_CHECKING: - from _typeshed.wsgi import StartResponse - from _typeshed.wsgi import WSGIApplication - from _typeshed.wsgi import WSGIEnvironment - - from .request import Request - - -def _iter_encoded(iterable: t.Iterable[str | bytes]) -> t.Iterator[bytes]: - for item in iterable: - if isinstance(item, str): - yield item.encode() - else: - yield item - - -class Response(_SansIOResponse): - """Represents an outgoing WSGI HTTP response with body, status, and - headers. Has properties and methods for using the functionality - defined by various HTTP specs. - - The response body is flexible to support different use cases. The - simple form is passing bytes, or a string which will be encoded as - UTF-8. Passing an iterable of bytes or strings makes this a - streaming response. A generator is particularly useful for building - a CSV file in memory or using SSE (Server Sent Events). A file-like - object is also iterable, although the - :func:`~werkzeug.utils.send_file` helper should be used in that - case. - - The response object is itself a WSGI application callable. When - called (:meth:`__call__`) with ``environ`` and ``start_response``, - it will pass its status and headers to ``start_response`` then - return its body as an iterable. - - .. code-block:: python - - from werkzeug.wrappers.response import Response - - def index(): - return Response("Hello, World!") - - def application(environ, start_response): - path = environ.get("PATH_INFO") or "/" - - if path == "/": - response = index() - else: - response = Response("Not Found", status=404) - - return response(environ, start_response) - - :param response: The data for the body of the response. A string or - bytes, or tuple or list of strings or bytes, for a fixed-length - response, or any other iterable of strings or bytes for a - streaming response. Defaults to an empty body. - :param status: The status code for the response. Either an int, in - which case the default status message is added, or a string in - the form ``{code} {message}``, like ``404 Not Found``. Defaults - to 200. - :param headers: A :class:`~werkzeug.datastructures.Headers` object, - or a list of ``(key, value)`` tuples that will be converted to a - ``Headers`` object. - :param mimetype: The mime type (content type without charset or - other parameters) of the response. If the value starts with - ``text/`` (or matches some other special cases), the charset - will be added to create the ``content_type``. - :param content_type: The full content type of the response. - Overrides building the value from ``mimetype``. - :param direct_passthrough: Pass the response body directly through - as the WSGI iterable. This can be used when the body is a binary - file or other iterator of bytes, to skip some unnecessary - checks. Use :func:`~werkzeug.utils.send_file` instead of setting - this manually. - - .. versionchanged:: 2.1 - Old ``BaseResponse`` and mixin classes were removed. - - .. versionchanged:: 2.0 - Combine ``BaseResponse`` and mixins into a single ``Response`` - class. - - .. versionchanged:: 0.5 - The ``direct_passthrough`` parameter was added. - """ - - #: if set to `False` accessing properties on the response object will - #: not try to consume the response iterator and convert it into a list. - #: - #: .. versionadded:: 0.6.2 - #: - #: That attribute was previously called `implicit_seqence_conversion`. - #: (Notice the typo). If you did use this feature, you have to adapt - #: your code to the name change. - implicit_sequence_conversion = True - - #: If a redirect ``Location`` header is a relative URL, make it an - #: absolute URL, including scheme and domain. - #: - #: .. versionchanged:: 2.1 - #: This is disabled by default, so responses will send relative - #: redirects. - #: - #: .. versionadded:: 0.8 - autocorrect_location_header = False - - #: Should this response object automatically set the content-length - #: header if possible? This is true by default. - #: - #: .. versionadded:: 0.8 - automatically_set_content_length = True - - #: The response body to send as the WSGI iterable. A list of strings - #: or bytes represents a fixed-length response, any other iterable - #: is a streaming response. Strings are encoded to bytes as UTF-8. - #: - #: Do not set to a plain string or bytes, that will cause sending - #: the response to be very inefficient as it will iterate one byte - #: at a time. - response: t.Iterable[str] | t.Iterable[bytes] - - def __init__( - self, - response: t.Iterable[bytes] | bytes | t.Iterable[str] | str | None = None, - status: int | str | HTTPStatus | None = None, - headers: t.Mapping[str, str | t.Iterable[str]] - | t.Iterable[tuple[str, str]] - | None = None, - mimetype: str | None = None, - content_type: str | None = None, - direct_passthrough: bool = False, - ) -> None: - super().__init__( - status=status, - headers=headers, - mimetype=mimetype, - content_type=content_type, - ) - - #: Pass the response body directly through as the WSGI iterable. - #: This can be used when the body is a binary file or other - #: iterator of bytes, to skip some unnecessary checks. Use - #: :func:`~werkzeug.utils.send_file` instead of setting this - #: manually. - self.direct_passthrough = direct_passthrough - self._on_close: list[t.Callable[[], t.Any]] = [] - - # we set the response after the headers so that if a class changes - # the charset attribute, the data is set in the correct charset. - if response is None: - self.response = [] - elif isinstance(response, (str, bytes, bytearray)): - self.set_data(response) - else: - self.response = response - - def call_on_close(self, func: t.Callable[[], t.Any]) -> t.Callable[[], t.Any]: - """Adds a function to the internal list of functions that should - be called as part of closing down the response. Since 0.7 this - function also returns the function that was passed so that this - can be used as a decorator. - - .. versionadded:: 0.6 - """ - self._on_close.append(func) - return func - - def __repr__(self) -> str: - if self.is_sequence: - body_info = f"{sum(map(len, self.iter_encoded()))} bytes" - else: - body_info = "streamed" if self.is_streamed else "likely-streamed" - return f"<{type(self).__name__} {body_info} [{self.status}]>" - - @classmethod - def force_type( - cls, response: Response, environ: WSGIEnvironment | None = None - ) -> Response: - """Enforce that the WSGI response is a response object of the current - type. Werkzeug will use the :class:`Response` internally in many - situations like the exceptions. If you call :meth:`get_response` on an - exception you will get back a regular :class:`Response` object, even - if you are using a custom subclass. - - This method can enforce a given response type, and it will also - convert arbitrary WSGI callables into response objects if an environ - is provided:: - - # convert a Werkzeug response object into an instance of the - # MyResponseClass subclass. - response = MyResponseClass.force_type(response) - - # convert any WSGI application into a response object - response = MyResponseClass.force_type(response, environ) - - This is especially useful if you want to post-process responses in - the main dispatcher and use functionality provided by your subclass. - - Keep in mind that this will modify response objects in place if - possible! - - :param response: a response object or wsgi application. - :param environ: a WSGI environment object. - :return: a response object. - """ - if not isinstance(response, Response): - if environ is None: - raise TypeError( - "cannot convert WSGI application into response" - " objects without an environ" - ) - - from ..test import run_wsgi_app - - response = Response(*run_wsgi_app(response, environ)) - - response.__class__ = cls - return response - - @classmethod - def from_app( - cls, app: WSGIApplication, environ: WSGIEnvironment, buffered: bool = False - ) -> Response: - """Create a new response object from an application output. This - works best if you pass it an application that returns a generator all - the time. Sometimes applications may use the `write()` callable - returned by the `start_response` function. This tries to resolve such - edge cases automatically. But if you don't get the expected output - you should set `buffered` to `True` which enforces buffering. - - :param app: the WSGI application to execute. - :param environ: the WSGI environment to execute against. - :param buffered: set to `True` to enforce buffering. - :return: a response object. - """ - from ..test import run_wsgi_app - - return cls(*run_wsgi_app(app, environ, buffered)) - - @t.overload - def get_data(self, as_text: t.Literal[False] = False) -> bytes: ... - - @t.overload - def get_data(self, as_text: t.Literal[True]) -> str: ... - - def get_data(self, as_text: bool = False) -> bytes | str: - """The string representation of the response body. Whenever you call - this property the response iterable is encoded and flattened. This - can lead to unwanted behavior if you stream big data. - - This behavior can be disabled by setting - :attr:`implicit_sequence_conversion` to `False`. - - If `as_text` is set to `True` the return value will be a decoded - string. - - .. versionadded:: 0.9 - """ - self._ensure_sequence() - rv = b"".join(self.iter_encoded()) - - if as_text: - return rv.decode() - - return rv - - def set_data(self, value: bytes | str) -> None: - """Sets a new string as response. The value must be a string or - bytes. If a string is set it's encoded to the charset of the - response (utf-8 by default). - - .. versionadded:: 0.9 - """ - if isinstance(value, str): - value = value.encode() - self.response = [value] - if self.automatically_set_content_length: - self.headers["Content-Length"] = str(len(value)) - - data = property( - get_data, - set_data, - doc="A descriptor that calls :meth:`get_data` and :meth:`set_data`.", - ) - - def calculate_content_length(self) -> int | None: - """Returns the content length if available or `None` otherwise.""" - try: - self._ensure_sequence() - except RuntimeError: - return None - return sum(len(x) for x in self.iter_encoded()) - - def _ensure_sequence(self, mutable: bool = False) -> None: - """This method can be called by methods that need a sequence. If - `mutable` is true, it will also ensure that the response sequence - is a standard Python list. - - .. versionadded:: 0.6 - """ - if self.is_sequence: - # if we need a mutable object, we ensure it's a list. - if mutable and not isinstance(self.response, list): - self.response = list(self.response) # type: ignore - return - if self.direct_passthrough: - raise RuntimeError( - "Attempted implicit sequence conversion but the" - " response object is in direct passthrough mode." - ) - if not self.implicit_sequence_conversion: - raise RuntimeError( - "The response object required the iterable to be a" - " sequence, but the implicit conversion was disabled." - " Call make_sequence() yourself." - ) - self.make_sequence() - - def make_sequence(self) -> None: - """Converts the response iterator in a list. By default this happens - automatically if required. If `implicit_sequence_conversion` is - disabled, this method is not automatically called and some properties - might raise exceptions. This also encodes all the items. - - .. versionadded:: 0.6 - """ - if not self.is_sequence: - # if we consume an iterable we have to ensure that the close - # method of the iterable is called if available when we tear - # down the response - close = getattr(self.response, "close", None) - self.response = list(self.iter_encoded()) - if close is not None: - self.call_on_close(close) - - def iter_encoded(self) -> t.Iterator[bytes]: - """Iter the response encoded with the encoding of the response. - If the response object is invoked as WSGI application the return - value of this method is used as application iterator unless - :attr:`direct_passthrough` was activated. - """ - # Encode in a separate function so that self.response is fetched - # early. This allows us to wrap the response with the return - # value from get_app_iter or iter_encoded. - return _iter_encoded(self.response) - - @property - def is_streamed(self) -> bool: - """If the response is streamed (the response is not an iterable with - a length information) this property is `True`. In this case streamed - means that there is no information about the number of iterations. - This is usually `True` if a generator is passed to the response object. - - This is useful for checking before applying some sort of post - filtering that should not take place for streamed responses. - """ - try: - len(self.response) # type: ignore - except (TypeError, AttributeError): - return True - return False - - @property - def is_sequence(self) -> bool: - """If the iterator is buffered, this property will be `True`. A - response object will consider an iterator to be buffered if the - response attribute is a list or tuple. - - .. versionadded:: 0.6 - """ - return isinstance(self.response, (tuple, list)) - - def close(self) -> None: - """Close the wrapped response if possible. You can also use the object - in a with statement which will automatically close it. - - .. versionadded:: 0.9 - Can now be used in a with statement. - """ - if hasattr(self.response, "close"): - self.response.close() - for func in self._on_close: - func() - - def __enter__(self) -> Response: - return self - - def __exit__(self, exc_type, exc_value, tb): # type: ignore - self.close() - - def freeze(self) -> None: - """Make the response object ready to be pickled. Does the - following: - - * Buffer the response into a list, ignoring - :attr:`implicity_sequence_conversion` and - :attr:`direct_passthrough`. - * Set the ``Content-Length`` header. - * Generate an ``ETag`` header if one is not already set. - - .. versionchanged:: 2.1 - Removed the ``no_etag`` parameter. - - .. versionchanged:: 2.0 - An ``ETag`` header is always added. - - .. versionchanged:: 0.6 - The ``Content-Length`` header is set. - """ - # Always freeze the encoded response body, ignore - # implicit_sequence_conversion and direct_passthrough. - self.response = list(self.iter_encoded()) - self.headers["Content-Length"] = str(sum(map(len, self.response))) - self.add_etag() - - def get_wsgi_headers(self, environ: WSGIEnvironment) -> Headers: - """This is automatically called right before the response is started - and returns headers modified for the given environment. It returns a - copy of the headers from the response with some modifications applied - if necessary. - - For example the location header (if present) is joined with the root - URL of the environment. Also the content length is automatically set - to zero here for certain status codes. - - .. versionchanged:: 0.6 - Previously that function was called `fix_headers` and modified - the response object in place. Also since 0.6, IRIs in location - and content-location headers are handled properly. - - Also starting with 0.6, Werkzeug will attempt to set the content - length if it is able to figure it out on its own. This is the - case if all the strings in the response iterable are already - encoded and the iterable is buffered. - - :param environ: the WSGI environment of the request. - :return: returns a new :class:`~werkzeug.datastructures.Headers` - object. - """ - headers = Headers(self.headers) - location: str | None = None - content_location: str | None = None - content_length: str | int | None = None - status = self.status_code - - # iterate over the headers to find all values in one go. Because - # get_wsgi_headers is used each response that gives us a tiny - # speedup. - for key, value in headers: - ikey = key.lower() - if ikey == "location": - location = value - elif ikey == "content-location": - content_location = value - elif ikey == "content-length": - content_length = value - - if location is not None: - location = iri_to_uri(location) - - if self.autocorrect_location_header: - # Make the location header an absolute URL. - current_url = get_current_url(environ, strip_querystring=True) - current_url = iri_to_uri(current_url) - location = urljoin(current_url, location) - - headers["Location"] = location - - # make sure the content location is a URL - if content_location is not None: - headers["Content-Location"] = iri_to_uri(content_location) - - if 100 <= status < 200 or status == 204: - # Per section 3.3.2 of RFC 7230, "a server MUST NOT send a - # Content-Length header field in any response with a status - # code of 1xx (Informational) or 204 (No Content)." - headers.remove("Content-Length") - elif status == 304: - remove_entity_headers(headers) - - # if we can determine the content length automatically, we - # should try to do that. But only if this does not involve - # flattening the iterator or encoding of strings in the - # response. We however should not do that if we have a 304 - # response. - if ( - self.automatically_set_content_length - and self.is_sequence - and content_length is None - and status not in (204, 304) - and not (100 <= status < 200) - ): - content_length = sum(len(x) for x in self.iter_encoded()) - headers["Content-Length"] = str(content_length) - - return headers - - def get_app_iter(self, environ: WSGIEnvironment) -> t.Iterable[bytes]: - """Returns the application iterator for the given environ. Depending - on the request method and the current status code the return value - might be an empty response rather than the one from the response. - - If the request method is `HEAD` or the status code is in a range - where the HTTP specification requires an empty response, an empty - iterable is returned. - - .. versionadded:: 0.6 - - :param environ: the WSGI environment of the request. - :return: a response iterable. - """ - status = self.status_code - if ( - environ["REQUEST_METHOD"] == "HEAD" - or 100 <= status < 200 - or status in (204, 304) - ): - iterable: t.Iterable[bytes] = () - elif self.direct_passthrough: - return self.response # type: ignore - else: - iterable = self.iter_encoded() - return ClosingIterator(iterable, self.close) - - def get_wsgi_response( - self, environ: WSGIEnvironment - ) -> tuple[t.Iterable[bytes], str, list[tuple[str, str]]]: - """Returns the final WSGI response as tuple. The first item in - the tuple is the application iterator, the second the status and - the third the list of headers. The response returned is created - specially for the given environment. For example if the request - method in the WSGI environment is ``'HEAD'`` the response will - be empty and only the headers and status code will be present. - - .. versionadded:: 0.6 - - :param environ: the WSGI environment of the request. - :return: an ``(app_iter, status, headers)`` tuple. - """ - headers = self.get_wsgi_headers(environ) - app_iter = self.get_app_iter(environ) - return app_iter, self.status, headers.to_wsgi_list() - - def __call__( - self, environ: WSGIEnvironment, start_response: StartResponse - ) -> t.Iterable[bytes]: - """Process this response as WSGI application. - - :param environ: the WSGI environment. - :param start_response: the response callable provided by the WSGI - server. - :return: an application iterator - """ - app_iter, status, headers = self.get_wsgi_response(environ) - start_response(status, headers) - return app_iter - - # JSON - - #: A module or other object that has ``dumps`` and ``loads`` - #: functions that match the API of the built-in :mod:`json` module. - json_module = json - - @property - def json(self) -> t.Any | None: - """The parsed JSON data if :attr:`mimetype` indicates JSON - (:mimetype:`application/json`, see :attr:`is_json`). - - Calls :meth:`get_json` with default arguments. - """ - return self.get_json() - - @t.overload - def get_json(self, force: bool = ..., silent: t.Literal[False] = ...) -> t.Any: ... - - @t.overload - def get_json(self, force: bool = ..., silent: bool = ...) -> t.Any | None: ... - - def get_json(self, force: bool = False, silent: bool = False) -> t.Any | None: - """Parse :attr:`data` as JSON. Useful during testing. - - If the mimetype does not indicate JSON - (:mimetype:`application/json`, see :attr:`is_json`), this - returns ``None``. - - Unlike :meth:`Request.get_json`, the result is not cached. - - :param force: Ignore the mimetype and always try to parse JSON. - :param silent: Silence parsing errors and return ``None`` - instead. - """ - if not (force or self.is_json): - return None - - data = self.get_data() - - try: - return self.json_module.loads(data) - except ValueError: - if not silent: - raise - - return None - - # Stream - - @cached_property - def stream(self) -> ResponseStream: - """The response iterable as write-only stream.""" - return ResponseStream(self) - - def _wrap_range_response(self, start: int, length: int) -> None: - """Wrap existing Response in case of Range Request context.""" - if self.status_code == 206: - self.response = _RangeWrapper(self.response, start, length) # type: ignore - - def _is_range_request_processable(self, environ: WSGIEnvironment) -> bool: - """Return ``True`` if `Range` header is present and if underlying - resource is considered unchanged when compared with `If-Range` header. - """ - return ( - "HTTP_IF_RANGE" not in environ - or not is_resource_modified( - environ, - self.headers.get("etag"), - None, - self.headers.get("last-modified"), - ignore_if_range=False, - ) - ) and "HTTP_RANGE" in environ - - def _process_range_request( - self, - environ: WSGIEnvironment, - complete_length: int | None, - accept_ranges: bool | str, - ) -> bool: - """Handle Range Request related headers (RFC7233). If `Accept-Ranges` - header is valid, and Range Request is processable, we set the headers - as described by the RFC, and wrap the underlying response in a - RangeWrapper. - - Returns ``True`` if Range Request can be fulfilled, ``False`` otherwise. - - :raises: :class:`~werkzeug.exceptions.RequestedRangeNotSatisfiable` - if `Range` header could not be parsed or satisfied. - - .. versionchanged:: 2.0 - Returns ``False`` if the length is 0. - """ - from ..exceptions import RequestedRangeNotSatisfiable - - if ( - not accept_ranges - or complete_length is None - or complete_length == 0 - or not self._is_range_request_processable(environ) - ): - return False - - if accept_ranges is True: - accept_ranges = "bytes" - - parsed_range = parse_range_header(environ.get("HTTP_RANGE")) - - if parsed_range is None: - raise RequestedRangeNotSatisfiable(complete_length) - - range_tuple = parsed_range.range_for_length(complete_length) - content_range_header = parsed_range.to_content_range_header(complete_length) - - if range_tuple is None or content_range_header is None: - raise RequestedRangeNotSatisfiable(complete_length) - - content_length = range_tuple[1] - range_tuple[0] - self.headers["Content-Length"] = str(content_length) - self.headers["Accept-Ranges"] = accept_ranges - self.content_range = content_range_header # type: ignore - self.status_code = 206 - self._wrap_range_response(range_tuple[0], content_length) - return True - - def make_conditional( - self, - request_or_environ: WSGIEnvironment | Request, - accept_ranges: bool | str = False, - complete_length: int | None = None, - ) -> Response: - """Make the response conditional to the request. This method works - best if an etag was defined for the response already. The `add_etag` - method can be used to do that. If called without etag just the date - header is set. - - This does nothing if the request method in the request or environ is - anything but GET or HEAD. - - For optimal performance when handling range requests, it's recommended - that your response data object implements `seekable`, `seek` and `tell` - methods as described by :py:class:`io.IOBase`. Objects returned by - :meth:`~werkzeug.wsgi.wrap_file` automatically implement those methods. - - It does not remove the body of the response because that's something - the :meth:`__call__` function does for us automatically. - - Returns self so that you can do ``return resp.make_conditional(req)`` - but modifies the object in-place. - - :param request_or_environ: a request object or WSGI environment to be - used to make the response conditional - against. - :param accept_ranges: This parameter dictates the value of - `Accept-Ranges` header. If ``False`` (default), - the header is not set. If ``True``, it will be set - to ``"bytes"``. If it's a string, it will use this - value. - :param complete_length: Will be used only in valid Range Requests. - It will set `Content-Range` complete length - value and compute `Content-Length` real value. - This parameter is mandatory for successful - Range Requests completion. - :raises: :class:`~werkzeug.exceptions.RequestedRangeNotSatisfiable` - if `Range` header could not be parsed or satisfied. - - .. versionchanged:: 2.0 - Range processing is skipped if length is 0 instead of - raising a 416 Range Not Satisfiable error. - """ - environ = _get_environ(request_or_environ) - if environ["REQUEST_METHOD"] in ("GET", "HEAD"): - # if the date is not in the headers, add it now. We however - # will not override an already existing header. Unfortunately - # this header will be overridden by many WSGI servers including - # wsgiref. - if "date" not in self.headers: - self.headers["Date"] = http_date() - is206 = self._process_range_request(environ, complete_length, accept_ranges) - if not is206 and not is_resource_modified( - environ, - self.headers.get("etag"), - None, - self.headers.get("last-modified"), - ): - if parse_etags(environ.get("HTTP_IF_MATCH")): - self.status_code = 412 - else: - self.status_code = 304 - if ( - self.automatically_set_content_length - and "content-length" not in self.headers - ): - length = self.calculate_content_length() - if length is not None: - self.headers["Content-Length"] = str(length) - return self - - def add_etag(self, overwrite: bool = False, weak: bool = False) -> None: - """Add an etag for the current response if there is none yet. - - .. versionchanged:: 2.0 - SHA-1 is used to generate the value. MD5 may not be - available in some environments. - """ - if overwrite or "etag" not in self.headers: - self.set_etag(generate_etag(self.get_data()), weak) - - -class ResponseStream: - """A file descriptor like object used by :meth:`Response.stream` to - represent the body of the stream. It directly pushes into the - response iterable of the response object. - """ - - mode = "wb+" - - def __init__(self, response: Response): - self.response = response - self.closed = False - - def write(self, value: bytes) -> int: - if self.closed: - raise ValueError("I/O operation on closed file") - self.response._ensure_sequence(mutable=True) - self.response.response.append(value) # type: ignore - self.response.headers.pop("Content-Length", None) - return len(value) - - def writelines(self, seq: t.Iterable[bytes]) -> None: - for item in seq: - self.write(item) - - def close(self) -> None: - self.closed = True - - def flush(self) -> None: - if self.closed: - raise ValueError("I/O operation on closed file") - - def isatty(self) -> bool: - if self.closed: - raise ValueError("I/O operation on closed file") - return False - - def tell(self) -> int: - self.response._ensure_sequence() - return sum(map(len, self.response.response)) - - @property - def encoding(self) -> str: - return "utf-8" diff --git a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wsgi.py b/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wsgi.py deleted file mode 100644 index 01d40af..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/werkzeug/wsgi.py +++ /dev/null @@ -1,595 +0,0 @@ -from __future__ import annotations - -import io -import typing as t -from functools import partial -from functools import update_wrapper - -from .exceptions import ClientDisconnected -from .exceptions import RequestEntityTooLarge -from .sansio import utils as _sansio_utils -from .sansio.utils import host_is_trusted # noqa: F401 # Imported as part of API - -if t.TYPE_CHECKING: - from _typeshed.wsgi import WSGIApplication - from _typeshed.wsgi import WSGIEnvironment - - -def responder(f: t.Callable[..., WSGIApplication]) -> WSGIApplication: - """Marks a function as responder. Decorate a function with it and it - will automatically call the return value as WSGI application. - - Example:: - - @responder - def application(environ, start_response): - return Response('Hello World!') - """ - return update_wrapper(lambda *a: f(*a)(*a[-2:]), f) - - -def get_current_url( - environ: WSGIEnvironment, - root_only: bool = False, - strip_querystring: bool = False, - host_only: bool = False, - trusted_hosts: t.Iterable[str] | None = None, -) -> str: - """Recreate the URL for a request from the parts in a WSGI - environment. - - The URL is an IRI, not a URI, so it may contain Unicode characters. - Use :func:`~werkzeug.urls.iri_to_uri` to convert it to ASCII. - - :param environ: The WSGI environment to get the URL parts from. - :param root_only: Only build the root path, don't include the - remaining path or query string. - :param strip_querystring: Don't include the query string. - :param host_only: Only build the scheme and host. - :param trusted_hosts: A list of trusted host names to validate the - host against. - """ - parts = { - "scheme": environ["wsgi.url_scheme"], - "host": get_host(environ, trusted_hosts), - } - - if not host_only: - parts["root_path"] = environ.get("SCRIPT_NAME", "") - - if not root_only: - parts["path"] = environ.get("PATH_INFO", "") - - if not strip_querystring: - parts["query_string"] = environ.get("QUERY_STRING", "").encode("latin1") - - return _sansio_utils.get_current_url(**parts) - - -def _get_server( - environ: WSGIEnvironment, -) -> tuple[str, int | None] | None: - name = environ.get("SERVER_NAME") - - if name is None: - return None - - try: - port: int | None = int(environ.get("SERVER_PORT", None)) - except (TypeError, ValueError): - # unix socket - port = None - - return name, port - - -def get_host( - environ: WSGIEnvironment, trusted_hosts: t.Iterable[str] | None = None -) -> str: - """Return the host for the given WSGI environment. - - The ``Host`` header is preferred, then ``SERVER_NAME`` if it's not - set. The returned host will only contain the port if it is different - than the standard port for the protocol. - - Optionally, verify that the host is trusted using - :func:`host_is_trusted` and raise a - :exc:`~werkzeug.exceptions.SecurityError` if it is not. - - :param environ: A WSGI environment dict. - :param trusted_hosts: A list of trusted host names. - - :return: Host, with port if necessary. - :raise ~werkzeug.exceptions.SecurityError: If the host is not - trusted. - """ - return _sansio_utils.get_host( - environ["wsgi.url_scheme"], - environ.get("HTTP_HOST"), - _get_server(environ), - trusted_hosts, - ) - - -def get_content_length(environ: WSGIEnvironment) -> int | None: - """Return the ``Content-Length`` header value as an int. If the header is not given - or the ``Transfer-Encoding`` header is ``chunked``, ``None`` is returned to indicate - a streaming request. If the value is not an integer, or negative, 0 is returned. - - :param environ: The WSGI environ to get the content length from. - - .. versionadded:: 0.9 - """ - return _sansio_utils.get_content_length( - http_content_length=environ.get("CONTENT_LENGTH"), - http_transfer_encoding=environ.get("HTTP_TRANSFER_ENCODING"), - ) - - -def get_input_stream( - environ: WSGIEnvironment, - safe_fallback: bool = True, - max_content_length: int | None = None, -) -> t.IO[bytes]: - """Return the WSGI input stream, wrapped so that it may be read safely without going - past the ``Content-Length`` header value or ``max_content_length``. - - If ``Content-Length`` exceeds ``max_content_length``, a - :exc:`RequestEntityTooLarge`` ``413 Content Too Large`` error is raised. - - If the WSGI server sets ``environ["wsgi.input_terminated"]``, it indicates that the - server handles terminating the stream, so it is safe to read directly. For example, - a server that knows how to handle chunked requests safely would set this. - - If ``max_content_length`` is set, it can be enforced on streams if - ``wsgi.input_terminated`` is set. Otherwise, an empty stream is returned unless the - user explicitly disables this safe fallback. - - If the limit is reached before the underlying stream is exhausted (such as a file - that is too large, or an infinite stream), the remaining contents of the stream - cannot be read safely. Depending on how the server handles this, clients may show a - "connection reset" failure instead of seeing the 413 response. - - :param environ: The WSGI environ containing the stream. - :param safe_fallback: Return an empty stream when ``Content-Length`` is not set. - Disabling this allows infinite streams, which can be a denial-of-service risk. - :param max_content_length: The maximum length that content-length or streaming - requests may not exceed. - - .. versionchanged:: 2.3.2 - ``max_content_length`` is only applied to streaming requests if the server sets - ``wsgi.input_terminated``. - - .. versionchanged:: 2.3 - Check ``max_content_length`` and raise an error if it is exceeded. - - .. versionadded:: 0.9 - """ - stream = t.cast(t.IO[bytes], environ["wsgi.input"]) - content_length = get_content_length(environ) - - if content_length is not None and max_content_length is not None: - if content_length > max_content_length: - raise RequestEntityTooLarge() - - # A WSGI server can set this to indicate that it terminates the input stream. In - # that case the stream is safe without wrapping, or can enforce a max length. - if "wsgi.input_terminated" in environ: - if max_content_length is not None: - # If this is moved above, it can cause the stream to hang if a read attempt - # is made when the client sends no data. For example, the development server - # does not handle buffering except for chunked encoding. - return t.cast( - t.IO[bytes], LimitedStream(stream, max_content_length, is_max=True) - ) - - return stream - - # No limit given, return an empty stream unless the user explicitly allows the - # potentially infinite stream. An infinite stream is dangerous if it's not expected, - # as it can tie up a worker indefinitely. - if content_length is None: - return io.BytesIO() if safe_fallback else stream - - return t.cast(t.IO[bytes], LimitedStream(stream, content_length)) - - -def get_path_info(environ: WSGIEnvironment) -> str: - """Return ``PATH_INFO`` from the WSGI environment. - - :param environ: WSGI environment to get the path from. - - .. versionchanged:: 3.0 - The ``charset`` and ``errors`` parameters were removed. - - .. versionadded:: 0.9 - """ - path: bytes = environ.get("PATH_INFO", "").encode("latin1") - return path.decode(errors="replace") - - -class ClosingIterator: - """The WSGI specification requires that all middlewares and gateways - respect the `close` callback of the iterable returned by the application. - Because it is useful to add another close action to a returned iterable - and adding a custom iterable is a boring task this class can be used for - that:: - - return ClosingIterator(app(environ, start_response), [cleanup_session, - cleanup_locals]) - - If there is just one close function it can be passed instead of the list. - - A closing iterator is not needed if the application uses response objects - and finishes the processing if the response is started:: - - try: - return response(environ, start_response) - finally: - cleanup_session() - cleanup_locals() - """ - - def __init__( - self, - iterable: t.Iterable[bytes], - callbacks: None - | (t.Callable[[], None] | t.Iterable[t.Callable[[], None]]) = None, - ) -> None: - iterator = iter(iterable) - self._next = t.cast(t.Callable[[], bytes], partial(next, iterator)) - if callbacks is None: - callbacks = [] - elif callable(callbacks): - callbacks = [callbacks] - else: - callbacks = list(callbacks) - iterable_close = getattr(iterable, "close", None) - if iterable_close: - callbacks.insert(0, iterable_close) - self._callbacks = callbacks - - def __iter__(self) -> ClosingIterator: - return self - - def __next__(self) -> bytes: - return self._next() - - def close(self) -> None: - for callback in self._callbacks: - callback() - - -def wrap_file( - environ: WSGIEnvironment, file: t.IO[bytes], buffer_size: int = 8192 -) -> t.Iterable[bytes]: - """Wraps a file. This uses the WSGI server's file wrapper if available - or otherwise the generic :class:`FileWrapper`. - - .. versionadded:: 0.5 - - If the file wrapper from the WSGI server is used it's important to not - iterate over it from inside the application but to pass it through - unchanged. If you want to pass out a file wrapper inside a response - object you have to set :attr:`Response.direct_passthrough` to `True`. - - More information about file wrappers are available in :pep:`333`. - - :param file: a :class:`file`-like object with a :meth:`~file.read` method. - :param buffer_size: number of bytes for one iteration. - """ - return environ.get("wsgi.file_wrapper", FileWrapper)( # type: ignore - file, buffer_size - ) - - -class FileWrapper: - """This class can be used to convert a :class:`file`-like object into - an iterable. It yields `buffer_size` blocks until the file is fully - read. - - You should not use this class directly but rather use the - :func:`wrap_file` function that uses the WSGI server's file wrapper - support if it's available. - - .. versionadded:: 0.5 - - If you're using this object together with a :class:`Response` you have - to use the `direct_passthrough` mode. - - :param file: a :class:`file`-like object with a :meth:`~file.read` method. - :param buffer_size: number of bytes for one iteration. - """ - - def __init__(self, file: t.IO[bytes], buffer_size: int = 8192) -> None: - self.file = file - self.buffer_size = buffer_size - - def close(self) -> None: - if hasattr(self.file, "close"): - self.file.close() - - def seekable(self) -> bool: - if hasattr(self.file, "seekable"): - return self.file.seekable() - if hasattr(self.file, "seek"): - return True - return False - - def seek(self, *args: t.Any) -> None: - if hasattr(self.file, "seek"): - self.file.seek(*args) - - def tell(self) -> int | None: - if hasattr(self.file, "tell"): - return self.file.tell() - return None - - def __iter__(self) -> FileWrapper: - return self - - def __next__(self) -> bytes: - data = self.file.read(self.buffer_size) - if data: - return data - raise StopIteration() - - -class _RangeWrapper: - # private for now, but should we make it public in the future ? - - """This class can be used to convert an iterable object into - an iterable that will only yield a piece of the underlying content. - It yields blocks until the underlying stream range is fully read. - The yielded blocks will have a size that can't exceed the original - iterator defined block size, but that can be smaller. - - If you're using this object together with a :class:`Response` you have - to use the `direct_passthrough` mode. - - :param iterable: an iterable object with a :meth:`__next__` method. - :param start_byte: byte from which read will start. - :param byte_range: how many bytes to read. - """ - - def __init__( - self, - iterable: t.Iterable[bytes] | t.IO[bytes], - start_byte: int = 0, - byte_range: int | None = None, - ): - self.iterable = iter(iterable) - self.byte_range = byte_range - self.start_byte = start_byte - self.end_byte = None - - if byte_range is not None: - self.end_byte = start_byte + byte_range - - self.read_length = 0 - self.seekable = hasattr(iterable, "seekable") and iterable.seekable() - self.end_reached = False - - def __iter__(self) -> _RangeWrapper: - return self - - def _next_chunk(self) -> bytes: - try: - chunk = next(self.iterable) - self.read_length += len(chunk) - return chunk - except StopIteration: - self.end_reached = True - raise - - def _first_iteration(self) -> tuple[bytes | None, int]: - chunk = None - if self.seekable: - self.iterable.seek(self.start_byte) # type: ignore - self.read_length = self.iterable.tell() # type: ignore - contextual_read_length = self.read_length - else: - while self.read_length <= self.start_byte: - chunk = self._next_chunk() - if chunk is not None: - chunk = chunk[self.start_byte - self.read_length :] - contextual_read_length = self.start_byte - return chunk, contextual_read_length - - def _next(self) -> bytes: - if self.end_reached: - raise StopIteration() - chunk = None - contextual_read_length = self.read_length - if self.read_length == 0: - chunk, contextual_read_length = self._first_iteration() - if chunk is None: - chunk = self._next_chunk() - if self.end_byte is not None and self.read_length >= self.end_byte: - self.end_reached = True - return chunk[: self.end_byte - contextual_read_length] - return chunk - - def __next__(self) -> bytes: - chunk = self._next() - if chunk: - return chunk - self.end_reached = True - raise StopIteration() - - def close(self) -> None: - if hasattr(self.iterable, "close"): - self.iterable.close() - - -class LimitedStream(io.RawIOBase): - """Wrap a stream so that it doesn't read more than a given limit. This is used to - limit ``wsgi.input`` to the ``Content-Length`` header value or - :attr:`.Request.max_content_length`. - - When attempting to read after the limit has been reached, :meth:`on_exhausted` is - called. When the limit is a maximum, this raises :exc:`.RequestEntityTooLarge`. - - If reading from the stream returns zero bytes or raises an error, - :meth:`on_disconnect` is called, which raises :exc:`.ClientDisconnected`. When the - limit is a maximum and zero bytes were read, no error is raised, since it may be the - end of the stream. - - If the limit is reached before the underlying stream is exhausted (such as a file - that is too large, or an infinite stream), the remaining contents of the stream - cannot be read safely. Depending on how the server handles this, clients may show a - "connection reset" failure instead of seeing the 413 response. - - :param stream: The stream to read from. Must be a readable binary IO object. - :param limit: The limit in bytes to not read past. Should be either the - ``Content-Length`` header value or ``request.max_content_length``. - :param is_max: Whether the given ``limit`` is ``request.max_content_length`` instead - of the ``Content-Length`` header value. This changes how exhausted and - disconnect events are handled. - - .. versionchanged:: 2.3 - Handle ``max_content_length`` differently than ``Content-Length``. - - .. versionchanged:: 2.3 - Implements ``io.RawIOBase`` rather than ``io.IOBase``. - """ - - def __init__(self, stream: t.IO[bytes], limit: int, is_max: bool = False) -> None: - self._stream = stream - self._pos = 0 - self.limit = limit - self._limit_is_max = is_max - - @property - def is_exhausted(self) -> bool: - """Whether the current stream position has reached the limit.""" - return self._pos >= self.limit - - def on_exhausted(self) -> None: - """Called when attempting to read after the limit has been reached. - - The default behavior is to do nothing, unless the limit is a maximum, in which - case it raises :exc:`.RequestEntityTooLarge`. - - .. versionchanged:: 2.3 - Raises ``RequestEntityTooLarge`` if the limit is a maximum. - - .. versionchanged:: 2.3 - Any return value is ignored. - """ - if self._limit_is_max: - raise RequestEntityTooLarge() - - def on_disconnect(self, error: Exception | None = None) -> None: - """Called when an attempted read receives zero bytes before the limit was - reached. This indicates that the client disconnected before sending the full - request body. - - The default behavior is to raise :exc:`.ClientDisconnected`, unless the limit is - a maximum and no error was raised. - - .. versionchanged:: 2.3 - Added the ``error`` parameter. Do nothing if the limit is a maximum and no - error was raised. - - .. versionchanged:: 2.3 - Any return value is ignored. - """ - if not self._limit_is_max or error is not None: - raise ClientDisconnected() - - # If the limit is a maximum, then we may have read zero bytes because the - # streaming body is complete. There's no way to distinguish that from the - # client disconnecting early. - - def exhaust(self) -> bytes: - """Exhaust the stream by reading until the limit is reached or the client - disconnects, returning the remaining data. - - .. versionchanged:: 2.3 - Return the remaining data. - - .. versionchanged:: 2.2.3 - Handle case where wrapped stream returns fewer bytes than requested. - """ - if not self.is_exhausted: - return self.readall() - - return b"" - - def readinto(self, b: bytearray) -> int | None: # type: ignore[override] - size = len(b) - remaining = self.limit - self._pos - - if remaining <= 0: - self.on_exhausted() - return 0 - - if hasattr(self._stream, "readinto"): - # Use stream.readinto if it's available. - if size <= remaining: - # The size fits in the remaining limit, use the buffer directly. - try: - out_size: int | None = self._stream.readinto(b) - except (OSError, ValueError) as e: - self.on_disconnect(error=e) - return 0 - else: - # Use a temp buffer with the remaining limit as the size. - temp_b = bytearray(remaining) - - try: - out_size = self._stream.readinto(temp_b) - except (OSError, ValueError) as e: - self.on_disconnect(error=e) - return 0 - - if out_size: - b[:out_size] = temp_b - else: - # WSGI requires that stream.read is available. - try: - data = self._stream.read(min(size, remaining)) - except (OSError, ValueError) as e: - self.on_disconnect(error=e) - return 0 - - out_size = len(data) - b[:out_size] = data - - if not out_size: - # Read zero bytes from the stream. - self.on_disconnect() - return 0 - - self._pos += out_size - return out_size - - def readall(self) -> bytes: - if self.is_exhausted: - self.on_exhausted() - return b"" - - out = bytearray() - - # The parent implementation uses "while True", which results in an extra read. - while not self.is_exhausted: - data = self.read(1024 * 64) - - # Stream may return empty before a max limit is reached. - if not data: - break - - out.extend(data) - - return bytes(out) - - def tell(self) -> int: - """Return the current stream position. - - .. versionadded:: 0.9 - """ - return self._pos - - def readable(self) -> bool: - return True diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/yaml/__init__.py deleted file mode 100644 index 2ec4f20..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/yaml/__init__.py +++ /dev/null @@ -1,390 +0,0 @@ - -from .error import * - -from .tokens import * -from .events import * -from .nodes import * - -from .loader import * -from .dumper import * - -__version__ = '6.0.2' -try: - from .cyaml import * - __with_libyaml__ = True -except ImportError: - __with_libyaml__ = False - -import io - -#------------------------------------------------------------------------------ -# XXX "Warnings control" is now deprecated. Leaving in the API function to not -# break code that uses it. -#------------------------------------------------------------------------------ -def warnings(settings=None): - if settings is None: - return {} - -#------------------------------------------------------------------------------ -def scan(stream, Loader=Loader): - """ - Scan a YAML stream and produce scanning tokens. - """ - loader = Loader(stream) - try: - while loader.check_token(): - yield loader.get_token() - finally: - loader.dispose() - -def parse(stream, Loader=Loader): - """ - Parse a YAML stream and produce parsing events. - """ - loader = Loader(stream) - try: - while loader.check_event(): - yield loader.get_event() - finally: - loader.dispose() - -def compose(stream, Loader=Loader): - """ - Parse the first YAML document in a stream - and produce the corresponding representation tree. - """ - loader = Loader(stream) - try: - return loader.get_single_node() - finally: - loader.dispose() - -def compose_all(stream, Loader=Loader): - """ - Parse all YAML documents in a stream - and produce corresponding representation trees. - """ - loader = Loader(stream) - try: - while loader.check_node(): - yield loader.get_node() - finally: - loader.dispose() - -def load(stream, Loader): - """ - Parse the first YAML document in a stream - and produce the corresponding Python object. - """ - loader = Loader(stream) - try: - return loader.get_single_data() - finally: - loader.dispose() - -def load_all(stream, Loader): - """ - Parse all YAML documents in a stream - and produce corresponding Python objects. - """ - loader = Loader(stream) - try: - while loader.check_data(): - yield loader.get_data() - finally: - loader.dispose() - -def full_load(stream): - """ - Parse the first YAML document in a stream - and produce the corresponding Python object. - - Resolve all tags except those known to be - unsafe on untrusted input. - """ - return load(stream, FullLoader) - -def full_load_all(stream): - """ - Parse all YAML documents in a stream - and produce corresponding Python objects. - - Resolve all tags except those known to be - unsafe on untrusted input. - """ - return load_all(stream, FullLoader) - -def safe_load(stream): - """ - Parse the first YAML document in a stream - and produce the corresponding Python object. - - Resolve only basic YAML tags. This is known - to be safe for untrusted input. - """ - return load(stream, SafeLoader) - -def safe_load_all(stream): - """ - Parse all YAML documents in a stream - and produce corresponding Python objects. - - Resolve only basic YAML tags. This is known - to be safe for untrusted input. - """ - return load_all(stream, SafeLoader) - -def unsafe_load(stream): - """ - Parse the first YAML document in a stream - and produce the corresponding Python object. - - Resolve all tags, even those known to be - unsafe on untrusted input. - """ - return load(stream, UnsafeLoader) - -def unsafe_load_all(stream): - """ - Parse all YAML documents in a stream - and produce corresponding Python objects. - - Resolve all tags, even those known to be - unsafe on untrusted input. - """ - return load_all(stream, UnsafeLoader) - -def emit(events, stream=None, Dumper=Dumper, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None): - """ - Emit YAML parsing events into a stream. - If stream is None, return the produced string instead. - """ - getvalue = None - if stream is None: - stream = io.StringIO() - getvalue = stream.getvalue - dumper = Dumper(stream, canonical=canonical, indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break) - try: - for event in events: - dumper.emit(event) - finally: - dumper.dispose() - if getvalue: - return getvalue() - -def serialize_all(nodes, stream=None, Dumper=Dumper, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None): - """ - Serialize a sequence of representation trees into a YAML stream. - If stream is None, return the produced string instead. - """ - getvalue = None - if stream is None: - if encoding is None: - stream = io.StringIO() - else: - stream = io.BytesIO() - getvalue = stream.getvalue - dumper = Dumper(stream, canonical=canonical, indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break, - encoding=encoding, version=version, tags=tags, - explicit_start=explicit_start, explicit_end=explicit_end) - try: - dumper.open() - for node in nodes: - dumper.serialize(node) - dumper.close() - finally: - dumper.dispose() - if getvalue: - return getvalue() - -def serialize(node, stream=None, Dumper=Dumper, **kwds): - """ - Serialize a representation tree into a YAML stream. - If stream is None, return the produced string instead. - """ - return serialize_all([node], stream, Dumper=Dumper, **kwds) - -def dump_all(documents, stream=None, Dumper=Dumper, - default_style=None, default_flow_style=False, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None, sort_keys=True): - """ - Serialize a sequence of Python objects into a YAML stream. - If stream is None, return the produced string instead. - """ - getvalue = None - if stream is None: - if encoding is None: - stream = io.StringIO() - else: - stream = io.BytesIO() - getvalue = stream.getvalue - dumper = Dumper(stream, default_style=default_style, - default_flow_style=default_flow_style, - canonical=canonical, indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break, - encoding=encoding, version=version, tags=tags, - explicit_start=explicit_start, explicit_end=explicit_end, sort_keys=sort_keys) - try: - dumper.open() - for data in documents: - dumper.represent(data) - dumper.close() - finally: - dumper.dispose() - if getvalue: - return getvalue() - -def dump(data, stream=None, Dumper=Dumper, **kwds): - """ - Serialize a Python object into a YAML stream. - If stream is None, return the produced string instead. - """ - return dump_all([data], stream, Dumper=Dumper, **kwds) - -def safe_dump_all(documents, stream=None, **kwds): - """ - Serialize a sequence of Python objects into a YAML stream. - Produce only basic YAML tags. - If stream is None, return the produced string instead. - """ - return dump_all(documents, stream, Dumper=SafeDumper, **kwds) - -def safe_dump(data, stream=None, **kwds): - """ - Serialize a Python object into a YAML stream. - Produce only basic YAML tags. - If stream is None, return the produced string instead. - """ - return dump_all([data], stream, Dumper=SafeDumper, **kwds) - -def add_implicit_resolver(tag, regexp, first=None, - Loader=None, Dumper=Dumper): - """ - Add an implicit scalar detector. - If an implicit scalar value matches the given regexp, - the corresponding tag is assigned to the scalar. - first is a sequence of possible initial characters or None. - """ - if Loader is None: - loader.Loader.add_implicit_resolver(tag, regexp, first) - loader.FullLoader.add_implicit_resolver(tag, regexp, first) - loader.UnsafeLoader.add_implicit_resolver(tag, regexp, first) - else: - Loader.add_implicit_resolver(tag, regexp, first) - Dumper.add_implicit_resolver(tag, regexp, first) - -def add_path_resolver(tag, path, kind=None, Loader=None, Dumper=Dumper): - """ - Add a path based resolver for the given tag. - A path is a list of keys that forms a path - to a node in the representation tree. - Keys can be string values, integers, or None. - """ - if Loader is None: - loader.Loader.add_path_resolver(tag, path, kind) - loader.FullLoader.add_path_resolver(tag, path, kind) - loader.UnsafeLoader.add_path_resolver(tag, path, kind) - else: - Loader.add_path_resolver(tag, path, kind) - Dumper.add_path_resolver(tag, path, kind) - -def add_constructor(tag, constructor, Loader=None): - """ - Add a constructor for the given tag. - Constructor is a function that accepts a Loader instance - and a node object and produces the corresponding Python object. - """ - if Loader is None: - loader.Loader.add_constructor(tag, constructor) - loader.FullLoader.add_constructor(tag, constructor) - loader.UnsafeLoader.add_constructor(tag, constructor) - else: - Loader.add_constructor(tag, constructor) - -def add_multi_constructor(tag_prefix, multi_constructor, Loader=None): - """ - Add a multi-constructor for the given tag prefix. - Multi-constructor is called for a node if its tag starts with tag_prefix. - Multi-constructor accepts a Loader instance, a tag suffix, - and a node object and produces the corresponding Python object. - """ - if Loader is None: - loader.Loader.add_multi_constructor(tag_prefix, multi_constructor) - loader.FullLoader.add_multi_constructor(tag_prefix, multi_constructor) - loader.UnsafeLoader.add_multi_constructor(tag_prefix, multi_constructor) - else: - Loader.add_multi_constructor(tag_prefix, multi_constructor) - -def add_representer(data_type, representer, Dumper=Dumper): - """ - Add a representer for the given type. - Representer is a function accepting a Dumper instance - and an instance of the given data type - and producing the corresponding representation node. - """ - Dumper.add_representer(data_type, representer) - -def add_multi_representer(data_type, multi_representer, Dumper=Dumper): - """ - Add a representer for the given type. - Multi-representer is a function accepting a Dumper instance - and an instance of the given data type or subtype - and producing the corresponding representation node. - """ - Dumper.add_multi_representer(data_type, multi_representer) - -class YAMLObjectMetaclass(type): - """ - The metaclass for YAMLObject. - """ - def __init__(cls, name, bases, kwds): - super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds) - if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None: - if isinstance(cls.yaml_loader, list): - for loader in cls.yaml_loader: - loader.add_constructor(cls.yaml_tag, cls.from_yaml) - else: - cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml) - - cls.yaml_dumper.add_representer(cls, cls.to_yaml) - -class YAMLObject(metaclass=YAMLObjectMetaclass): - """ - An object that can dump itself to a YAML stream - and load itself from a YAML stream. - """ - - __slots__ = () # no direct instantiation, so allow immutable subclasses - - yaml_loader = [Loader, FullLoader, UnsafeLoader] - yaml_dumper = Dumper - - yaml_tag = None - yaml_flow_style = None - - @classmethod - def from_yaml(cls, loader, node): - """ - Convert a representation node to a Python object. - """ - return loader.construct_yaml_object(node, cls) - - @classmethod - def to_yaml(cls, dumper, data): - """ - Convert a Python object to a representation node. - """ - return dumper.represent_yaml_object(cls.yaml_tag, data, cls, - flow_style=cls.yaml_flow_style) - diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index c51694a..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/composer.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/composer.cpython-38.pyc deleted file mode 100644 index 2ee3f26..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/composer.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/constructor.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/constructor.cpython-38.pyc deleted file mode 100644 index 2127622..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/constructor.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/cyaml.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/cyaml.cpython-38.pyc deleted file mode 100644 index 6da94a0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/cyaml.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/dumper.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/dumper.cpython-38.pyc deleted file mode 100644 index 3184ef4..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/dumper.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/emitter.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/emitter.cpython-38.pyc deleted file mode 100644 index 84cb935..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/emitter.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/error.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/error.cpython-38.pyc deleted file mode 100644 index 03e6dda..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/error.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/events.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/events.cpython-38.pyc deleted file mode 100644 index da30fb9..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/events.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/loader.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/loader.cpython-38.pyc deleted file mode 100644 index 1ae47aa..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/loader.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/nodes.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/nodes.cpython-38.pyc deleted file mode 100644 index 8d50c1a..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/nodes.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/parser.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/parser.cpython-38.pyc deleted file mode 100644 index c4718d9..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/parser.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/reader.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/reader.cpython-38.pyc deleted file mode 100644 index 860abc9..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/reader.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/representer.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/representer.cpython-38.pyc deleted file mode 100644 index 6350b48..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/representer.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/resolver.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/resolver.cpython-38.pyc deleted file mode 100644 index 16bbc23..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/resolver.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/scanner.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/scanner.cpython-38.pyc deleted file mode 100644 index 824d64a..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/scanner.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/serializer.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/serializer.cpython-38.pyc deleted file mode 100644 index 92d1a7e..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/serializer.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/tokens.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/tokens.cpython-38.pyc deleted file mode 100644 index 40c9897..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/yaml/__pycache__/tokens.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/_yaml.cpython-38-x86_64-linux-gnu.so b/plotter-app/venv/lib/python3.8/site-packages/yaml/_yaml.cpython-38-x86_64-linux-gnu.so deleted file mode 100755 index d6b24b2..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/yaml/_yaml.cpython-38-x86_64-linux-gnu.so and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/composer.py b/plotter-app/venv/lib/python3.8/site-packages/yaml/composer.py deleted file mode 100644 index 6d15cb4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/yaml/composer.py +++ /dev/null @@ -1,139 +0,0 @@ - -__all__ = ['Composer', 'ComposerError'] - -from .error import MarkedYAMLError -from .events import * -from .nodes import * - -class ComposerError(MarkedYAMLError): - pass - -class Composer: - - def __init__(self): - self.anchors = {} - - def check_node(self): - # Drop the STREAM-START event. - if self.check_event(StreamStartEvent): - self.get_event() - - # If there are more documents available? - return not self.check_event(StreamEndEvent) - - def get_node(self): - # Get the root node of the next document. - if not self.check_event(StreamEndEvent): - return self.compose_document() - - def get_single_node(self): - # Drop the STREAM-START event. - self.get_event() - - # Compose a document if the stream is not empty. - document = None - if not self.check_event(StreamEndEvent): - document = self.compose_document() - - # Ensure that the stream contains no more documents. - if not self.check_event(StreamEndEvent): - event = self.get_event() - raise ComposerError("expected a single document in the stream", - document.start_mark, "but found another document", - event.start_mark) - - # Drop the STREAM-END event. - self.get_event() - - return document - - def compose_document(self): - # Drop the DOCUMENT-START event. - self.get_event() - - # Compose the root node. - node = self.compose_node(None, None) - - # Drop the DOCUMENT-END event. - self.get_event() - - self.anchors = {} - return node - - def compose_node(self, parent, index): - if self.check_event(AliasEvent): - event = self.get_event() - anchor = event.anchor - if anchor not in self.anchors: - raise ComposerError(None, None, "found undefined alias %r" - % anchor, event.start_mark) - return self.anchors[anchor] - event = self.peek_event() - anchor = event.anchor - if anchor is not None: - if anchor in self.anchors: - raise ComposerError("found duplicate anchor %r; first occurrence" - % anchor, self.anchors[anchor].start_mark, - "second occurrence", event.start_mark) - self.descend_resolver(parent, index) - if self.check_event(ScalarEvent): - node = self.compose_scalar_node(anchor) - elif self.check_event(SequenceStartEvent): - node = self.compose_sequence_node(anchor) - elif self.check_event(MappingStartEvent): - node = self.compose_mapping_node(anchor) - self.ascend_resolver() - return node - - def compose_scalar_node(self, anchor): - event = self.get_event() - tag = event.tag - if tag is None or tag == '!': - tag = self.resolve(ScalarNode, event.value, event.implicit) - node = ScalarNode(tag, event.value, - event.start_mark, event.end_mark, style=event.style) - if anchor is not None: - self.anchors[anchor] = node - return node - - def compose_sequence_node(self, anchor): - start_event = self.get_event() - tag = start_event.tag - if tag is None or tag == '!': - tag = self.resolve(SequenceNode, None, start_event.implicit) - node = SequenceNode(tag, [], - start_event.start_mark, None, - flow_style=start_event.flow_style) - if anchor is not None: - self.anchors[anchor] = node - index = 0 - while not self.check_event(SequenceEndEvent): - node.value.append(self.compose_node(node, index)) - index += 1 - end_event = self.get_event() - node.end_mark = end_event.end_mark - return node - - def compose_mapping_node(self, anchor): - start_event = self.get_event() - tag = start_event.tag - if tag is None or tag == '!': - tag = self.resolve(MappingNode, None, start_event.implicit) - node = MappingNode(tag, [], - start_event.start_mark, None, - flow_style=start_event.flow_style) - if anchor is not None: - self.anchors[anchor] = node - while not self.check_event(MappingEndEvent): - #key_event = self.peek_event() - item_key = self.compose_node(node, None) - #if item_key in node.value: - # raise ComposerError("while composing a mapping", start_event.start_mark, - # "found duplicate key", key_event.start_mark) - item_value = self.compose_node(node, item_key) - #node.value[item_key] = item_value - node.value.append((item_key, item_value)) - end_event = self.get_event() - node.end_mark = end_event.end_mark - return node - diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/constructor.py b/plotter-app/venv/lib/python3.8/site-packages/yaml/constructor.py deleted file mode 100644 index 619acd3..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/yaml/constructor.py +++ /dev/null @@ -1,748 +0,0 @@ - -__all__ = [ - 'BaseConstructor', - 'SafeConstructor', - 'FullConstructor', - 'UnsafeConstructor', - 'Constructor', - 'ConstructorError' -] - -from .error import * -from .nodes import * - -import collections.abc, datetime, base64, binascii, re, sys, types - -class ConstructorError(MarkedYAMLError): - pass - -class BaseConstructor: - - yaml_constructors = {} - yaml_multi_constructors = {} - - def __init__(self): - self.constructed_objects = {} - self.recursive_objects = {} - self.state_generators = [] - self.deep_construct = False - - def check_data(self): - # If there are more documents available? - return self.check_node() - - def check_state_key(self, key): - """Block special attributes/methods from being set in a newly created - object, to prevent user-controlled methods from being called during - deserialization""" - if self.get_state_keys_blacklist_regexp().match(key): - raise ConstructorError(None, None, - "blacklisted key '%s' in instance state found" % (key,), None) - - def get_data(self): - # Construct and return the next document. - if self.check_node(): - return self.construct_document(self.get_node()) - - def get_single_data(self): - # Ensure that the stream contains a single document and construct it. - node = self.get_single_node() - if node is not None: - return self.construct_document(node) - return None - - def construct_document(self, node): - data = self.construct_object(node) - while self.state_generators: - state_generators = self.state_generators - self.state_generators = [] - for generator in state_generators: - for dummy in generator: - pass - self.constructed_objects = {} - self.recursive_objects = {} - self.deep_construct = False - return data - - def construct_object(self, node, deep=False): - if node in self.constructed_objects: - return self.constructed_objects[node] - if deep: - old_deep = self.deep_construct - self.deep_construct = True - if node in self.recursive_objects: - raise ConstructorError(None, None, - "found unconstructable recursive node", node.start_mark) - self.recursive_objects[node] = None - constructor = None - tag_suffix = None - if node.tag in self.yaml_constructors: - constructor = self.yaml_constructors[node.tag] - else: - for tag_prefix in self.yaml_multi_constructors: - if tag_prefix is not None and node.tag.startswith(tag_prefix): - tag_suffix = node.tag[len(tag_prefix):] - constructor = self.yaml_multi_constructors[tag_prefix] - break - else: - if None in self.yaml_multi_constructors: - tag_suffix = node.tag - constructor = self.yaml_multi_constructors[None] - elif None in self.yaml_constructors: - constructor = self.yaml_constructors[None] - elif isinstance(node, ScalarNode): - constructor = self.__class__.construct_scalar - elif isinstance(node, SequenceNode): - constructor = self.__class__.construct_sequence - elif isinstance(node, MappingNode): - constructor = self.__class__.construct_mapping - if tag_suffix is None: - data = constructor(self, node) - else: - data = constructor(self, tag_suffix, node) - if isinstance(data, types.GeneratorType): - generator = data - data = next(generator) - if self.deep_construct: - for dummy in generator: - pass - else: - self.state_generators.append(generator) - self.constructed_objects[node] = data - del self.recursive_objects[node] - if deep: - self.deep_construct = old_deep - return data - - def construct_scalar(self, node): - if not isinstance(node, ScalarNode): - raise ConstructorError(None, None, - "expected a scalar node, but found %s" % node.id, - node.start_mark) - return node.value - - def construct_sequence(self, node, deep=False): - if not isinstance(node, SequenceNode): - raise ConstructorError(None, None, - "expected a sequence node, but found %s" % node.id, - node.start_mark) - return [self.construct_object(child, deep=deep) - for child in node.value] - - def construct_mapping(self, node, deep=False): - if not isinstance(node, MappingNode): - raise ConstructorError(None, None, - "expected a mapping node, but found %s" % node.id, - node.start_mark) - mapping = {} - for key_node, value_node in node.value: - key = self.construct_object(key_node, deep=deep) - if not isinstance(key, collections.abc.Hashable): - raise ConstructorError("while constructing a mapping", node.start_mark, - "found unhashable key", key_node.start_mark) - value = self.construct_object(value_node, deep=deep) - mapping[key] = value - return mapping - - def construct_pairs(self, node, deep=False): - if not isinstance(node, MappingNode): - raise ConstructorError(None, None, - "expected a mapping node, but found %s" % node.id, - node.start_mark) - pairs = [] - for key_node, value_node in node.value: - key = self.construct_object(key_node, deep=deep) - value = self.construct_object(value_node, deep=deep) - pairs.append((key, value)) - return pairs - - @classmethod - def add_constructor(cls, tag, constructor): - if not 'yaml_constructors' in cls.__dict__: - cls.yaml_constructors = cls.yaml_constructors.copy() - cls.yaml_constructors[tag] = constructor - - @classmethod - def add_multi_constructor(cls, tag_prefix, multi_constructor): - if not 'yaml_multi_constructors' in cls.__dict__: - cls.yaml_multi_constructors = cls.yaml_multi_constructors.copy() - cls.yaml_multi_constructors[tag_prefix] = multi_constructor - -class SafeConstructor(BaseConstructor): - - def construct_scalar(self, node): - if isinstance(node, MappingNode): - for key_node, value_node in node.value: - if key_node.tag == 'tag:yaml.org,2002:value': - return self.construct_scalar(value_node) - return super().construct_scalar(node) - - def flatten_mapping(self, node): - merge = [] - index = 0 - while index < len(node.value): - key_node, value_node = node.value[index] - if key_node.tag == 'tag:yaml.org,2002:merge': - del node.value[index] - if isinstance(value_node, MappingNode): - self.flatten_mapping(value_node) - merge.extend(value_node.value) - elif isinstance(value_node, SequenceNode): - submerge = [] - for subnode in value_node.value: - if not isinstance(subnode, MappingNode): - raise ConstructorError("while constructing a mapping", - node.start_mark, - "expected a mapping for merging, but found %s" - % subnode.id, subnode.start_mark) - self.flatten_mapping(subnode) - submerge.append(subnode.value) - submerge.reverse() - for value in submerge: - merge.extend(value) - else: - raise ConstructorError("while constructing a mapping", node.start_mark, - "expected a mapping or list of mappings for merging, but found %s" - % value_node.id, value_node.start_mark) - elif key_node.tag == 'tag:yaml.org,2002:value': - key_node.tag = 'tag:yaml.org,2002:str' - index += 1 - else: - index += 1 - if merge: - node.value = merge + node.value - - def construct_mapping(self, node, deep=False): - if isinstance(node, MappingNode): - self.flatten_mapping(node) - return super().construct_mapping(node, deep=deep) - - def construct_yaml_null(self, node): - self.construct_scalar(node) - return None - - bool_values = { - 'yes': True, - 'no': False, - 'true': True, - 'false': False, - 'on': True, - 'off': False, - } - - def construct_yaml_bool(self, node): - value = self.construct_scalar(node) - return self.bool_values[value.lower()] - - def construct_yaml_int(self, node): - value = self.construct_scalar(node) - value = value.replace('_', '') - sign = +1 - if value[0] == '-': - sign = -1 - if value[0] in '+-': - value = value[1:] - if value == '0': - return 0 - elif value.startswith('0b'): - return sign*int(value[2:], 2) - elif value.startswith('0x'): - return sign*int(value[2:], 16) - elif value[0] == '0': - return sign*int(value, 8) - elif ':' in value: - digits = [int(part) for part in value.split(':')] - digits.reverse() - base = 1 - value = 0 - for digit in digits: - value += digit*base - base *= 60 - return sign*value - else: - return sign*int(value) - - inf_value = 1e300 - while inf_value != inf_value*inf_value: - inf_value *= inf_value - nan_value = -inf_value/inf_value # Trying to make a quiet NaN (like C99). - - def construct_yaml_float(self, node): - value = self.construct_scalar(node) - value = value.replace('_', '').lower() - sign = +1 - if value[0] == '-': - sign = -1 - if value[0] in '+-': - value = value[1:] - if value == '.inf': - return sign*self.inf_value - elif value == '.nan': - return self.nan_value - elif ':' in value: - digits = [float(part) for part in value.split(':')] - digits.reverse() - base = 1 - value = 0.0 - for digit in digits: - value += digit*base - base *= 60 - return sign*value - else: - return sign*float(value) - - def construct_yaml_binary(self, node): - try: - value = self.construct_scalar(node).encode('ascii') - except UnicodeEncodeError as exc: - raise ConstructorError(None, None, - "failed to convert base64 data into ascii: %s" % exc, - node.start_mark) - try: - if hasattr(base64, 'decodebytes'): - return base64.decodebytes(value) - else: - return base64.decodestring(value) - except binascii.Error as exc: - raise ConstructorError(None, None, - "failed to decode base64 data: %s" % exc, node.start_mark) - - timestamp_regexp = re.compile( - r'''^(?P[0-9][0-9][0-9][0-9]) - -(?P[0-9][0-9]?) - -(?P[0-9][0-9]?) - (?:(?:[Tt]|[ \t]+) - (?P[0-9][0-9]?) - :(?P[0-9][0-9]) - :(?P[0-9][0-9]) - (?:\.(?P[0-9]*))? - (?:[ \t]*(?PZ|(?P[-+])(?P[0-9][0-9]?) - (?::(?P[0-9][0-9]))?))?)?$''', re.X) - - def construct_yaml_timestamp(self, node): - value = self.construct_scalar(node) - match = self.timestamp_regexp.match(node.value) - values = match.groupdict() - year = int(values['year']) - month = int(values['month']) - day = int(values['day']) - if not values['hour']: - return datetime.date(year, month, day) - hour = int(values['hour']) - minute = int(values['minute']) - second = int(values['second']) - fraction = 0 - tzinfo = None - if values['fraction']: - fraction = values['fraction'][:6] - while len(fraction) < 6: - fraction += '0' - fraction = int(fraction) - if values['tz_sign']: - tz_hour = int(values['tz_hour']) - tz_minute = int(values['tz_minute'] or 0) - delta = datetime.timedelta(hours=tz_hour, minutes=tz_minute) - if values['tz_sign'] == '-': - delta = -delta - tzinfo = datetime.timezone(delta) - elif values['tz']: - tzinfo = datetime.timezone.utc - return datetime.datetime(year, month, day, hour, minute, second, fraction, - tzinfo=tzinfo) - - def construct_yaml_omap(self, node): - # Note: we do not check for duplicate keys, because it's too - # CPU-expensive. - omap = [] - yield omap - if not isinstance(node, SequenceNode): - raise ConstructorError("while constructing an ordered map", node.start_mark, - "expected a sequence, but found %s" % node.id, node.start_mark) - for subnode in node.value: - if not isinstance(subnode, MappingNode): - raise ConstructorError("while constructing an ordered map", node.start_mark, - "expected a mapping of length 1, but found %s" % subnode.id, - subnode.start_mark) - if len(subnode.value) != 1: - raise ConstructorError("while constructing an ordered map", node.start_mark, - "expected a single mapping item, but found %d items" % len(subnode.value), - subnode.start_mark) - key_node, value_node = subnode.value[0] - key = self.construct_object(key_node) - value = self.construct_object(value_node) - omap.append((key, value)) - - def construct_yaml_pairs(self, node): - # Note: the same code as `construct_yaml_omap`. - pairs = [] - yield pairs - if not isinstance(node, SequenceNode): - raise ConstructorError("while constructing pairs", node.start_mark, - "expected a sequence, but found %s" % node.id, node.start_mark) - for subnode in node.value: - if not isinstance(subnode, MappingNode): - raise ConstructorError("while constructing pairs", node.start_mark, - "expected a mapping of length 1, but found %s" % subnode.id, - subnode.start_mark) - if len(subnode.value) != 1: - raise ConstructorError("while constructing pairs", node.start_mark, - "expected a single mapping item, but found %d items" % len(subnode.value), - subnode.start_mark) - key_node, value_node = subnode.value[0] - key = self.construct_object(key_node) - value = self.construct_object(value_node) - pairs.append((key, value)) - - def construct_yaml_set(self, node): - data = set() - yield data - value = self.construct_mapping(node) - data.update(value) - - def construct_yaml_str(self, node): - return self.construct_scalar(node) - - def construct_yaml_seq(self, node): - data = [] - yield data - data.extend(self.construct_sequence(node)) - - def construct_yaml_map(self, node): - data = {} - yield data - value = self.construct_mapping(node) - data.update(value) - - def construct_yaml_object(self, node, cls): - data = cls.__new__(cls) - yield data - if hasattr(data, '__setstate__'): - state = self.construct_mapping(node, deep=True) - data.__setstate__(state) - else: - state = self.construct_mapping(node) - data.__dict__.update(state) - - def construct_undefined(self, node): - raise ConstructorError(None, None, - "could not determine a constructor for the tag %r" % node.tag, - node.start_mark) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:null', - SafeConstructor.construct_yaml_null) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:bool', - SafeConstructor.construct_yaml_bool) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:int', - SafeConstructor.construct_yaml_int) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:float', - SafeConstructor.construct_yaml_float) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:binary', - SafeConstructor.construct_yaml_binary) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:timestamp', - SafeConstructor.construct_yaml_timestamp) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:omap', - SafeConstructor.construct_yaml_omap) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:pairs', - SafeConstructor.construct_yaml_pairs) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:set', - SafeConstructor.construct_yaml_set) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:str', - SafeConstructor.construct_yaml_str) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:seq', - SafeConstructor.construct_yaml_seq) - -SafeConstructor.add_constructor( - 'tag:yaml.org,2002:map', - SafeConstructor.construct_yaml_map) - -SafeConstructor.add_constructor(None, - SafeConstructor.construct_undefined) - -class FullConstructor(SafeConstructor): - # 'extend' is blacklisted because it is used by - # construct_python_object_apply to add `listitems` to a newly generate - # python instance - def get_state_keys_blacklist(self): - return ['^extend$', '^__.*__$'] - - def get_state_keys_blacklist_regexp(self): - if not hasattr(self, 'state_keys_blacklist_regexp'): - self.state_keys_blacklist_regexp = re.compile('(' + '|'.join(self.get_state_keys_blacklist()) + ')') - return self.state_keys_blacklist_regexp - - def construct_python_str(self, node): - return self.construct_scalar(node) - - def construct_python_unicode(self, node): - return self.construct_scalar(node) - - def construct_python_bytes(self, node): - try: - value = self.construct_scalar(node).encode('ascii') - except UnicodeEncodeError as exc: - raise ConstructorError(None, None, - "failed to convert base64 data into ascii: %s" % exc, - node.start_mark) - try: - if hasattr(base64, 'decodebytes'): - return base64.decodebytes(value) - else: - return base64.decodestring(value) - except binascii.Error as exc: - raise ConstructorError(None, None, - "failed to decode base64 data: %s" % exc, node.start_mark) - - def construct_python_long(self, node): - return self.construct_yaml_int(node) - - def construct_python_complex(self, node): - return complex(self.construct_scalar(node)) - - def construct_python_tuple(self, node): - return tuple(self.construct_sequence(node)) - - def find_python_module(self, name, mark, unsafe=False): - if not name: - raise ConstructorError("while constructing a Python module", mark, - "expected non-empty name appended to the tag", mark) - if unsafe: - try: - __import__(name) - except ImportError as exc: - raise ConstructorError("while constructing a Python module", mark, - "cannot find module %r (%s)" % (name, exc), mark) - if name not in sys.modules: - raise ConstructorError("while constructing a Python module", mark, - "module %r is not imported" % name, mark) - return sys.modules[name] - - def find_python_name(self, name, mark, unsafe=False): - if not name: - raise ConstructorError("while constructing a Python object", mark, - "expected non-empty name appended to the tag", mark) - if '.' in name: - module_name, object_name = name.rsplit('.', 1) - else: - module_name = 'builtins' - object_name = name - if unsafe: - try: - __import__(module_name) - except ImportError as exc: - raise ConstructorError("while constructing a Python object", mark, - "cannot find module %r (%s)" % (module_name, exc), mark) - if module_name not in sys.modules: - raise ConstructorError("while constructing a Python object", mark, - "module %r is not imported" % module_name, mark) - module = sys.modules[module_name] - if not hasattr(module, object_name): - raise ConstructorError("while constructing a Python object", mark, - "cannot find %r in the module %r" - % (object_name, module.__name__), mark) - return getattr(module, object_name) - - def construct_python_name(self, suffix, node): - value = self.construct_scalar(node) - if value: - raise ConstructorError("while constructing a Python name", node.start_mark, - "expected the empty value, but found %r" % value, node.start_mark) - return self.find_python_name(suffix, node.start_mark) - - def construct_python_module(self, suffix, node): - value = self.construct_scalar(node) - if value: - raise ConstructorError("while constructing a Python module", node.start_mark, - "expected the empty value, but found %r" % value, node.start_mark) - return self.find_python_module(suffix, node.start_mark) - - def make_python_instance(self, suffix, node, - args=None, kwds=None, newobj=False, unsafe=False): - if not args: - args = [] - if not kwds: - kwds = {} - cls = self.find_python_name(suffix, node.start_mark) - if not (unsafe or isinstance(cls, type)): - raise ConstructorError("while constructing a Python instance", node.start_mark, - "expected a class, but found %r" % type(cls), - node.start_mark) - if newobj and isinstance(cls, type): - return cls.__new__(cls, *args, **kwds) - else: - return cls(*args, **kwds) - - def set_python_instance_state(self, instance, state, unsafe=False): - if hasattr(instance, '__setstate__'): - instance.__setstate__(state) - else: - slotstate = {} - if isinstance(state, tuple) and len(state) == 2: - state, slotstate = state - if hasattr(instance, '__dict__'): - if not unsafe and state: - for key in state.keys(): - self.check_state_key(key) - instance.__dict__.update(state) - elif state: - slotstate.update(state) - for key, value in slotstate.items(): - if not unsafe: - self.check_state_key(key) - setattr(instance, key, value) - - def construct_python_object(self, suffix, node): - # Format: - # !!python/object:module.name { ... state ... } - instance = self.make_python_instance(suffix, node, newobj=True) - yield instance - deep = hasattr(instance, '__setstate__') - state = self.construct_mapping(node, deep=deep) - self.set_python_instance_state(instance, state) - - def construct_python_object_apply(self, suffix, node, newobj=False): - # Format: - # !!python/object/apply # (or !!python/object/new) - # args: [ ... arguments ... ] - # kwds: { ... keywords ... } - # state: ... state ... - # listitems: [ ... listitems ... ] - # dictitems: { ... dictitems ... } - # or short format: - # !!python/object/apply [ ... arguments ... ] - # The difference between !!python/object/apply and !!python/object/new - # is how an object is created, check make_python_instance for details. - if isinstance(node, SequenceNode): - args = self.construct_sequence(node, deep=True) - kwds = {} - state = {} - listitems = [] - dictitems = {} - else: - value = self.construct_mapping(node, deep=True) - args = value.get('args', []) - kwds = value.get('kwds', {}) - state = value.get('state', {}) - listitems = value.get('listitems', []) - dictitems = value.get('dictitems', {}) - instance = self.make_python_instance(suffix, node, args, kwds, newobj) - if state: - self.set_python_instance_state(instance, state) - if listitems: - instance.extend(listitems) - if dictitems: - for key in dictitems: - instance[key] = dictitems[key] - return instance - - def construct_python_object_new(self, suffix, node): - return self.construct_python_object_apply(suffix, node, newobj=True) - -FullConstructor.add_constructor( - 'tag:yaml.org,2002:python/none', - FullConstructor.construct_yaml_null) - -FullConstructor.add_constructor( - 'tag:yaml.org,2002:python/bool', - FullConstructor.construct_yaml_bool) - -FullConstructor.add_constructor( - 'tag:yaml.org,2002:python/str', - FullConstructor.construct_python_str) - -FullConstructor.add_constructor( - 'tag:yaml.org,2002:python/unicode', - FullConstructor.construct_python_unicode) - -FullConstructor.add_constructor( - 'tag:yaml.org,2002:python/bytes', - FullConstructor.construct_python_bytes) - -FullConstructor.add_constructor( - 'tag:yaml.org,2002:python/int', - FullConstructor.construct_yaml_int) - -FullConstructor.add_constructor( - 'tag:yaml.org,2002:python/long', - FullConstructor.construct_python_long) - -FullConstructor.add_constructor( - 'tag:yaml.org,2002:python/float', - FullConstructor.construct_yaml_float) - -FullConstructor.add_constructor( - 'tag:yaml.org,2002:python/complex', - FullConstructor.construct_python_complex) - -FullConstructor.add_constructor( - 'tag:yaml.org,2002:python/list', - FullConstructor.construct_yaml_seq) - -FullConstructor.add_constructor( - 'tag:yaml.org,2002:python/tuple', - FullConstructor.construct_python_tuple) - -FullConstructor.add_constructor( - 'tag:yaml.org,2002:python/dict', - FullConstructor.construct_yaml_map) - -FullConstructor.add_multi_constructor( - 'tag:yaml.org,2002:python/name:', - FullConstructor.construct_python_name) - -class UnsafeConstructor(FullConstructor): - - def find_python_module(self, name, mark): - return super(UnsafeConstructor, self).find_python_module(name, mark, unsafe=True) - - def find_python_name(self, name, mark): - return super(UnsafeConstructor, self).find_python_name(name, mark, unsafe=True) - - def make_python_instance(self, suffix, node, args=None, kwds=None, newobj=False): - return super(UnsafeConstructor, self).make_python_instance( - suffix, node, args, kwds, newobj, unsafe=True) - - def set_python_instance_state(self, instance, state): - return super(UnsafeConstructor, self).set_python_instance_state( - instance, state, unsafe=True) - -UnsafeConstructor.add_multi_constructor( - 'tag:yaml.org,2002:python/module:', - UnsafeConstructor.construct_python_module) - -UnsafeConstructor.add_multi_constructor( - 'tag:yaml.org,2002:python/object:', - UnsafeConstructor.construct_python_object) - -UnsafeConstructor.add_multi_constructor( - 'tag:yaml.org,2002:python/object/new:', - UnsafeConstructor.construct_python_object_new) - -UnsafeConstructor.add_multi_constructor( - 'tag:yaml.org,2002:python/object/apply:', - UnsafeConstructor.construct_python_object_apply) - -# Constructor is same as UnsafeConstructor. Need to leave this in place in case -# people have extended it directly. -class Constructor(UnsafeConstructor): - pass diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/cyaml.py b/plotter-app/venv/lib/python3.8/site-packages/yaml/cyaml.py deleted file mode 100644 index 0c21345..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/yaml/cyaml.py +++ /dev/null @@ -1,101 +0,0 @@ - -__all__ = [ - 'CBaseLoader', 'CSafeLoader', 'CFullLoader', 'CUnsafeLoader', 'CLoader', - 'CBaseDumper', 'CSafeDumper', 'CDumper' -] - -from yaml._yaml import CParser, CEmitter - -from .constructor import * - -from .serializer import * -from .representer import * - -from .resolver import * - -class CBaseLoader(CParser, BaseConstructor, BaseResolver): - - def __init__(self, stream): - CParser.__init__(self, stream) - BaseConstructor.__init__(self) - BaseResolver.__init__(self) - -class CSafeLoader(CParser, SafeConstructor, Resolver): - - def __init__(self, stream): - CParser.__init__(self, stream) - SafeConstructor.__init__(self) - Resolver.__init__(self) - -class CFullLoader(CParser, FullConstructor, Resolver): - - def __init__(self, stream): - CParser.__init__(self, stream) - FullConstructor.__init__(self) - Resolver.__init__(self) - -class CUnsafeLoader(CParser, UnsafeConstructor, Resolver): - - def __init__(self, stream): - CParser.__init__(self, stream) - UnsafeConstructor.__init__(self) - Resolver.__init__(self) - -class CLoader(CParser, Constructor, Resolver): - - def __init__(self, stream): - CParser.__init__(self, stream) - Constructor.__init__(self) - Resolver.__init__(self) - -class CBaseDumper(CEmitter, BaseRepresenter, BaseResolver): - - def __init__(self, stream, - default_style=None, default_flow_style=False, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None, sort_keys=True): - CEmitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, encoding=encoding, - allow_unicode=allow_unicode, line_break=line_break, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - Representer.__init__(self, default_style=default_style, - default_flow_style=default_flow_style, sort_keys=sort_keys) - Resolver.__init__(self) - -class CSafeDumper(CEmitter, SafeRepresenter, Resolver): - - def __init__(self, stream, - default_style=None, default_flow_style=False, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None, sort_keys=True): - CEmitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, encoding=encoding, - allow_unicode=allow_unicode, line_break=line_break, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - SafeRepresenter.__init__(self, default_style=default_style, - default_flow_style=default_flow_style, sort_keys=sort_keys) - Resolver.__init__(self) - -class CDumper(CEmitter, Serializer, Representer, Resolver): - - def __init__(self, stream, - default_style=None, default_flow_style=False, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None, sort_keys=True): - CEmitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, encoding=encoding, - allow_unicode=allow_unicode, line_break=line_break, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - Representer.__init__(self, default_style=default_style, - default_flow_style=default_flow_style, sort_keys=sort_keys) - Resolver.__init__(self) - diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/dumper.py b/plotter-app/venv/lib/python3.8/site-packages/yaml/dumper.py deleted file mode 100644 index 6aadba5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/yaml/dumper.py +++ /dev/null @@ -1,62 +0,0 @@ - -__all__ = ['BaseDumper', 'SafeDumper', 'Dumper'] - -from .emitter import * -from .serializer import * -from .representer import * -from .resolver import * - -class BaseDumper(Emitter, Serializer, BaseRepresenter, BaseResolver): - - def __init__(self, stream, - default_style=None, default_flow_style=False, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None, sort_keys=True): - Emitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break) - Serializer.__init__(self, encoding=encoding, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - Representer.__init__(self, default_style=default_style, - default_flow_style=default_flow_style, sort_keys=sort_keys) - Resolver.__init__(self) - -class SafeDumper(Emitter, Serializer, SafeRepresenter, Resolver): - - def __init__(self, stream, - default_style=None, default_flow_style=False, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None, sort_keys=True): - Emitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break) - Serializer.__init__(self, encoding=encoding, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - SafeRepresenter.__init__(self, default_style=default_style, - default_flow_style=default_flow_style, sort_keys=sort_keys) - Resolver.__init__(self) - -class Dumper(Emitter, Serializer, Representer, Resolver): - - def __init__(self, stream, - default_style=None, default_flow_style=False, - canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None, - encoding=None, explicit_start=None, explicit_end=None, - version=None, tags=None, sort_keys=True): - Emitter.__init__(self, stream, canonical=canonical, - indent=indent, width=width, - allow_unicode=allow_unicode, line_break=line_break) - Serializer.__init__(self, encoding=encoding, - explicit_start=explicit_start, explicit_end=explicit_end, - version=version, tags=tags) - Representer.__init__(self, default_style=default_style, - default_flow_style=default_flow_style, sort_keys=sort_keys) - Resolver.__init__(self) - diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/emitter.py b/plotter-app/venv/lib/python3.8/site-packages/yaml/emitter.py deleted file mode 100644 index a664d01..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/yaml/emitter.py +++ /dev/null @@ -1,1137 +0,0 @@ - -# Emitter expects events obeying the following grammar: -# stream ::= STREAM-START document* STREAM-END -# document ::= DOCUMENT-START node DOCUMENT-END -# node ::= SCALAR | sequence | mapping -# sequence ::= SEQUENCE-START node* SEQUENCE-END -# mapping ::= MAPPING-START (node node)* MAPPING-END - -__all__ = ['Emitter', 'EmitterError'] - -from .error import YAMLError -from .events import * - -class EmitterError(YAMLError): - pass - -class ScalarAnalysis: - def __init__(self, scalar, empty, multiline, - allow_flow_plain, allow_block_plain, - allow_single_quoted, allow_double_quoted, - allow_block): - self.scalar = scalar - self.empty = empty - self.multiline = multiline - self.allow_flow_plain = allow_flow_plain - self.allow_block_plain = allow_block_plain - self.allow_single_quoted = allow_single_quoted - self.allow_double_quoted = allow_double_quoted - self.allow_block = allow_block - -class Emitter: - - DEFAULT_TAG_PREFIXES = { - '!' : '!', - 'tag:yaml.org,2002:' : '!!', - } - - def __init__(self, stream, canonical=None, indent=None, width=None, - allow_unicode=None, line_break=None): - - # The stream should have the methods `write` and possibly `flush`. - self.stream = stream - - # Encoding can be overridden by STREAM-START. - self.encoding = None - - # Emitter is a state machine with a stack of states to handle nested - # structures. - self.states = [] - self.state = self.expect_stream_start - - # Current event and the event queue. - self.events = [] - self.event = None - - # The current indentation level and the stack of previous indents. - self.indents = [] - self.indent = None - - # Flow level. - self.flow_level = 0 - - # Contexts. - self.root_context = False - self.sequence_context = False - self.mapping_context = False - self.simple_key_context = False - - # Characteristics of the last emitted character: - # - current position. - # - is it a whitespace? - # - is it an indention character - # (indentation space, '-', '?', or ':')? - self.line = 0 - self.column = 0 - self.whitespace = True - self.indention = True - - # Whether the document requires an explicit document indicator - self.open_ended = False - - # Formatting details. - self.canonical = canonical - self.allow_unicode = allow_unicode - self.best_indent = 2 - if indent and 1 < indent < 10: - self.best_indent = indent - self.best_width = 80 - if width and width > self.best_indent*2: - self.best_width = width - self.best_line_break = '\n' - if line_break in ['\r', '\n', '\r\n']: - self.best_line_break = line_break - - # Tag prefixes. - self.tag_prefixes = None - - # Prepared anchor and tag. - self.prepared_anchor = None - self.prepared_tag = None - - # Scalar analysis and style. - self.analysis = None - self.style = None - - def dispose(self): - # Reset the state attributes (to clear self-references) - self.states = [] - self.state = None - - def emit(self, event): - self.events.append(event) - while not self.need_more_events(): - self.event = self.events.pop(0) - self.state() - self.event = None - - # In some cases, we wait for a few next events before emitting. - - def need_more_events(self): - if not self.events: - return True - event = self.events[0] - if isinstance(event, DocumentStartEvent): - return self.need_events(1) - elif isinstance(event, SequenceStartEvent): - return self.need_events(2) - elif isinstance(event, MappingStartEvent): - return self.need_events(3) - else: - return False - - def need_events(self, count): - level = 0 - for event in self.events[1:]: - if isinstance(event, (DocumentStartEvent, CollectionStartEvent)): - level += 1 - elif isinstance(event, (DocumentEndEvent, CollectionEndEvent)): - level -= 1 - elif isinstance(event, StreamEndEvent): - level = -1 - if level < 0: - return False - return (len(self.events) < count+1) - - def increase_indent(self, flow=False, indentless=False): - self.indents.append(self.indent) - if self.indent is None: - if flow: - self.indent = self.best_indent - else: - self.indent = 0 - elif not indentless: - self.indent += self.best_indent - - # States. - - # Stream handlers. - - def expect_stream_start(self): - if isinstance(self.event, StreamStartEvent): - if self.event.encoding and not hasattr(self.stream, 'encoding'): - self.encoding = self.event.encoding - self.write_stream_start() - self.state = self.expect_first_document_start - else: - raise EmitterError("expected StreamStartEvent, but got %s" - % self.event) - - def expect_nothing(self): - raise EmitterError("expected nothing, but got %s" % self.event) - - # Document handlers. - - def expect_first_document_start(self): - return self.expect_document_start(first=True) - - def expect_document_start(self, first=False): - if isinstance(self.event, DocumentStartEvent): - if (self.event.version or self.event.tags) and self.open_ended: - self.write_indicator('...', True) - self.write_indent() - if self.event.version: - version_text = self.prepare_version(self.event.version) - self.write_version_directive(version_text) - self.tag_prefixes = self.DEFAULT_TAG_PREFIXES.copy() - if self.event.tags: - handles = sorted(self.event.tags.keys()) - for handle in handles: - prefix = self.event.tags[handle] - self.tag_prefixes[prefix] = handle - handle_text = self.prepare_tag_handle(handle) - prefix_text = self.prepare_tag_prefix(prefix) - self.write_tag_directive(handle_text, prefix_text) - implicit = (first and not self.event.explicit and not self.canonical - and not self.event.version and not self.event.tags - and not self.check_empty_document()) - if not implicit: - self.write_indent() - self.write_indicator('---', True) - if self.canonical: - self.write_indent() - self.state = self.expect_document_root - elif isinstance(self.event, StreamEndEvent): - if self.open_ended: - self.write_indicator('...', True) - self.write_indent() - self.write_stream_end() - self.state = self.expect_nothing - else: - raise EmitterError("expected DocumentStartEvent, but got %s" - % self.event) - - def expect_document_end(self): - if isinstance(self.event, DocumentEndEvent): - self.write_indent() - if self.event.explicit: - self.write_indicator('...', True) - self.write_indent() - self.flush_stream() - self.state = self.expect_document_start - else: - raise EmitterError("expected DocumentEndEvent, but got %s" - % self.event) - - def expect_document_root(self): - self.states.append(self.expect_document_end) - self.expect_node(root=True) - - # Node handlers. - - def expect_node(self, root=False, sequence=False, mapping=False, - simple_key=False): - self.root_context = root - self.sequence_context = sequence - self.mapping_context = mapping - self.simple_key_context = simple_key - if isinstance(self.event, AliasEvent): - self.expect_alias() - elif isinstance(self.event, (ScalarEvent, CollectionStartEvent)): - self.process_anchor('&') - self.process_tag() - if isinstance(self.event, ScalarEvent): - self.expect_scalar() - elif isinstance(self.event, SequenceStartEvent): - if self.flow_level or self.canonical or self.event.flow_style \ - or self.check_empty_sequence(): - self.expect_flow_sequence() - else: - self.expect_block_sequence() - elif isinstance(self.event, MappingStartEvent): - if self.flow_level or self.canonical or self.event.flow_style \ - or self.check_empty_mapping(): - self.expect_flow_mapping() - else: - self.expect_block_mapping() - else: - raise EmitterError("expected NodeEvent, but got %s" % self.event) - - def expect_alias(self): - if self.event.anchor is None: - raise EmitterError("anchor is not specified for alias") - self.process_anchor('*') - self.state = self.states.pop() - - def expect_scalar(self): - self.increase_indent(flow=True) - self.process_scalar() - self.indent = self.indents.pop() - self.state = self.states.pop() - - # Flow sequence handlers. - - def expect_flow_sequence(self): - self.write_indicator('[', True, whitespace=True) - self.flow_level += 1 - self.increase_indent(flow=True) - self.state = self.expect_first_flow_sequence_item - - def expect_first_flow_sequence_item(self): - if isinstance(self.event, SequenceEndEvent): - self.indent = self.indents.pop() - self.flow_level -= 1 - self.write_indicator(']', False) - self.state = self.states.pop() - else: - if self.canonical or self.column > self.best_width: - self.write_indent() - self.states.append(self.expect_flow_sequence_item) - self.expect_node(sequence=True) - - def expect_flow_sequence_item(self): - if isinstance(self.event, SequenceEndEvent): - self.indent = self.indents.pop() - self.flow_level -= 1 - if self.canonical: - self.write_indicator(',', False) - self.write_indent() - self.write_indicator(']', False) - self.state = self.states.pop() - else: - self.write_indicator(',', False) - if self.canonical or self.column > self.best_width: - self.write_indent() - self.states.append(self.expect_flow_sequence_item) - self.expect_node(sequence=True) - - # Flow mapping handlers. - - def expect_flow_mapping(self): - self.write_indicator('{', True, whitespace=True) - self.flow_level += 1 - self.increase_indent(flow=True) - self.state = self.expect_first_flow_mapping_key - - def expect_first_flow_mapping_key(self): - if isinstance(self.event, MappingEndEvent): - self.indent = self.indents.pop() - self.flow_level -= 1 - self.write_indicator('}', False) - self.state = self.states.pop() - else: - if self.canonical or self.column > self.best_width: - self.write_indent() - if not self.canonical and self.check_simple_key(): - self.states.append(self.expect_flow_mapping_simple_value) - self.expect_node(mapping=True, simple_key=True) - else: - self.write_indicator('?', True) - self.states.append(self.expect_flow_mapping_value) - self.expect_node(mapping=True) - - def expect_flow_mapping_key(self): - if isinstance(self.event, MappingEndEvent): - self.indent = self.indents.pop() - self.flow_level -= 1 - if self.canonical: - self.write_indicator(',', False) - self.write_indent() - self.write_indicator('}', False) - self.state = self.states.pop() - else: - self.write_indicator(',', False) - if self.canonical or self.column > self.best_width: - self.write_indent() - if not self.canonical and self.check_simple_key(): - self.states.append(self.expect_flow_mapping_simple_value) - self.expect_node(mapping=True, simple_key=True) - else: - self.write_indicator('?', True) - self.states.append(self.expect_flow_mapping_value) - self.expect_node(mapping=True) - - def expect_flow_mapping_simple_value(self): - self.write_indicator(':', False) - self.states.append(self.expect_flow_mapping_key) - self.expect_node(mapping=True) - - def expect_flow_mapping_value(self): - if self.canonical or self.column > self.best_width: - self.write_indent() - self.write_indicator(':', True) - self.states.append(self.expect_flow_mapping_key) - self.expect_node(mapping=True) - - # Block sequence handlers. - - def expect_block_sequence(self): - indentless = (self.mapping_context and not self.indention) - self.increase_indent(flow=False, indentless=indentless) - self.state = self.expect_first_block_sequence_item - - def expect_first_block_sequence_item(self): - return self.expect_block_sequence_item(first=True) - - def expect_block_sequence_item(self, first=False): - if not first and isinstance(self.event, SequenceEndEvent): - self.indent = self.indents.pop() - self.state = self.states.pop() - else: - self.write_indent() - self.write_indicator('-', True, indention=True) - self.states.append(self.expect_block_sequence_item) - self.expect_node(sequence=True) - - # Block mapping handlers. - - def expect_block_mapping(self): - self.increase_indent(flow=False) - self.state = self.expect_first_block_mapping_key - - def expect_first_block_mapping_key(self): - return self.expect_block_mapping_key(first=True) - - def expect_block_mapping_key(self, first=False): - if not first and isinstance(self.event, MappingEndEvent): - self.indent = self.indents.pop() - self.state = self.states.pop() - else: - self.write_indent() - if self.check_simple_key(): - self.states.append(self.expect_block_mapping_simple_value) - self.expect_node(mapping=True, simple_key=True) - else: - self.write_indicator('?', True, indention=True) - self.states.append(self.expect_block_mapping_value) - self.expect_node(mapping=True) - - def expect_block_mapping_simple_value(self): - self.write_indicator(':', False) - self.states.append(self.expect_block_mapping_key) - self.expect_node(mapping=True) - - def expect_block_mapping_value(self): - self.write_indent() - self.write_indicator(':', True, indention=True) - self.states.append(self.expect_block_mapping_key) - self.expect_node(mapping=True) - - # Checkers. - - def check_empty_sequence(self): - return (isinstance(self.event, SequenceStartEvent) and self.events - and isinstance(self.events[0], SequenceEndEvent)) - - def check_empty_mapping(self): - return (isinstance(self.event, MappingStartEvent) and self.events - and isinstance(self.events[0], MappingEndEvent)) - - def check_empty_document(self): - if not isinstance(self.event, DocumentStartEvent) or not self.events: - return False - event = self.events[0] - return (isinstance(event, ScalarEvent) and event.anchor is None - and event.tag is None and event.implicit and event.value == '') - - def check_simple_key(self): - length = 0 - if isinstance(self.event, NodeEvent) and self.event.anchor is not None: - if self.prepared_anchor is None: - self.prepared_anchor = self.prepare_anchor(self.event.anchor) - length += len(self.prepared_anchor) - if isinstance(self.event, (ScalarEvent, CollectionStartEvent)) \ - and self.event.tag is not None: - if self.prepared_tag is None: - self.prepared_tag = self.prepare_tag(self.event.tag) - length += len(self.prepared_tag) - if isinstance(self.event, ScalarEvent): - if self.analysis is None: - self.analysis = self.analyze_scalar(self.event.value) - length += len(self.analysis.scalar) - return (length < 128 and (isinstance(self.event, AliasEvent) - or (isinstance(self.event, ScalarEvent) - and not self.analysis.empty and not self.analysis.multiline) - or self.check_empty_sequence() or self.check_empty_mapping())) - - # Anchor, Tag, and Scalar processors. - - def process_anchor(self, indicator): - if self.event.anchor is None: - self.prepared_anchor = None - return - if self.prepared_anchor is None: - self.prepared_anchor = self.prepare_anchor(self.event.anchor) - if self.prepared_anchor: - self.write_indicator(indicator+self.prepared_anchor, True) - self.prepared_anchor = None - - def process_tag(self): - tag = self.event.tag - if isinstance(self.event, ScalarEvent): - if self.style is None: - self.style = self.choose_scalar_style() - if ((not self.canonical or tag is None) and - ((self.style == '' and self.event.implicit[0]) - or (self.style != '' and self.event.implicit[1]))): - self.prepared_tag = None - return - if self.event.implicit[0] and tag is None: - tag = '!' - self.prepared_tag = None - else: - if (not self.canonical or tag is None) and self.event.implicit: - self.prepared_tag = None - return - if tag is None: - raise EmitterError("tag is not specified") - if self.prepared_tag is None: - self.prepared_tag = self.prepare_tag(tag) - if self.prepared_tag: - self.write_indicator(self.prepared_tag, True) - self.prepared_tag = None - - def choose_scalar_style(self): - if self.analysis is None: - self.analysis = self.analyze_scalar(self.event.value) - if self.event.style == '"' or self.canonical: - return '"' - if not self.event.style and self.event.implicit[0]: - if (not (self.simple_key_context and - (self.analysis.empty or self.analysis.multiline)) - and (self.flow_level and self.analysis.allow_flow_plain - or (not self.flow_level and self.analysis.allow_block_plain))): - return '' - if self.event.style and self.event.style in '|>': - if (not self.flow_level and not self.simple_key_context - and self.analysis.allow_block): - return self.event.style - if not self.event.style or self.event.style == '\'': - if (self.analysis.allow_single_quoted and - not (self.simple_key_context and self.analysis.multiline)): - return '\'' - return '"' - - def process_scalar(self): - if self.analysis is None: - self.analysis = self.analyze_scalar(self.event.value) - if self.style is None: - self.style = self.choose_scalar_style() - split = (not self.simple_key_context) - #if self.analysis.multiline and split \ - # and (not self.style or self.style in '\'\"'): - # self.write_indent() - if self.style == '"': - self.write_double_quoted(self.analysis.scalar, split) - elif self.style == '\'': - self.write_single_quoted(self.analysis.scalar, split) - elif self.style == '>': - self.write_folded(self.analysis.scalar) - elif self.style == '|': - self.write_literal(self.analysis.scalar) - else: - self.write_plain(self.analysis.scalar, split) - self.analysis = None - self.style = None - - # Analyzers. - - def prepare_version(self, version): - major, minor = version - if major != 1: - raise EmitterError("unsupported YAML version: %d.%d" % (major, minor)) - return '%d.%d' % (major, minor) - - def prepare_tag_handle(self, handle): - if not handle: - raise EmitterError("tag handle must not be empty") - if handle[0] != '!' or handle[-1] != '!': - raise EmitterError("tag handle must start and end with '!': %r" % handle) - for ch in handle[1:-1]: - if not ('0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ - or ch in '-_'): - raise EmitterError("invalid character %r in the tag handle: %r" - % (ch, handle)) - return handle - - def prepare_tag_prefix(self, prefix): - if not prefix: - raise EmitterError("tag prefix must not be empty") - chunks = [] - start = end = 0 - if prefix[0] == '!': - end = 1 - while end < len(prefix): - ch = prefix[end] - if '0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ - or ch in '-;/?!:@&=+$,_.~*\'()[]': - end += 1 - else: - if start < end: - chunks.append(prefix[start:end]) - start = end = end+1 - data = ch.encode('utf-8') - for ch in data: - chunks.append('%%%02X' % ord(ch)) - if start < end: - chunks.append(prefix[start:end]) - return ''.join(chunks) - - def prepare_tag(self, tag): - if not tag: - raise EmitterError("tag must not be empty") - if tag == '!': - return tag - handle = None - suffix = tag - prefixes = sorted(self.tag_prefixes.keys()) - for prefix in prefixes: - if tag.startswith(prefix) \ - and (prefix == '!' or len(prefix) < len(tag)): - handle = self.tag_prefixes[prefix] - suffix = tag[len(prefix):] - chunks = [] - start = end = 0 - while end < len(suffix): - ch = suffix[end] - if '0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ - or ch in '-;/?:@&=+$,_.~*\'()[]' \ - or (ch == '!' and handle != '!'): - end += 1 - else: - if start < end: - chunks.append(suffix[start:end]) - start = end = end+1 - data = ch.encode('utf-8') - for ch in data: - chunks.append('%%%02X' % ch) - if start < end: - chunks.append(suffix[start:end]) - suffix_text = ''.join(chunks) - if handle: - return '%s%s' % (handle, suffix_text) - else: - return '!<%s>' % suffix_text - - def prepare_anchor(self, anchor): - if not anchor: - raise EmitterError("anchor must not be empty") - for ch in anchor: - if not ('0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ - or ch in '-_'): - raise EmitterError("invalid character %r in the anchor: %r" - % (ch, anchor)) - return anchor - - def analyze_scalar(self, scalar): - - # Empty scalar is a special case. - if not scalar: - return ScalarAnalysis(scalar=scalar, empty=True, multiline=False, - allow_flow_plain=False, allow_block_plain=True, - allow_single_quoted=True, allow_double_quoted=True, - allow_block=False) - - # Indicators and special characters. - block_indicators = False - flow_indicators = False - line_breaks = False - special_characters = False - - # Important whitespace combinations. - leading_space = False - leading_break = False - trailing_space = False - trailing_break = False - break_space = False - space_break = False - - # Check document indicators. - if scalar.startswith('---') or scalar.startswith('...'): - block_indicators = True - flow_indicators = True - - # First character or preceded by a whitespace. - preceded_by_whitespace = True - - # Last character or followed by a whitespace. - followed_by_whitespace = (len(scalar) == 1 or - scalar[1] in '\0 \t\r\n\x85\u2028\u2029') - - # The previous character is a space. - previous_space = False - - # The previous character is a break. - previous_break = False - - index = 0 - while index < len(scalar): - ch = scalar[index] - - # Check for indicators. - if index == 0: - # Leading indicators are special characters. - if ch in '#,[]{}&*!|>\'\"%@`': - flow_indicators = True - block_indicators = True - if ch in '?:': - flow_indicators = True - if followed_by_whitespace: - block_indicators = True - if ch == '-' and followed_by_whitespace: - flow_indicators = True - block_indicators = True - else: - # Some indicators cannot appear within a scalar as well. - if ch in ',?[]{}': - flow_indicators = True - if ch == ':': - flow_indicators = True - if followed_by_whitespace: - block_indicators = True - if ch == '#' and preceded_by_whitespace: - flow_indicators = True - block_indicators = True - - # Check for line breaks, special, and unicode characters. - if ch in '\n\x85\u2028\u2029': - line_breaks = True - if not (ch == '\n' or '\x20' <= ch <= '\x7E'): - if (ch == '\x85' or '\xA0' <= ch <= '\uD7FF' - or '\uE000' <= ch <= '\uFFFD' - or '\U00010000' <= ch < '\U0010ffff') and ch != '\uFEFF': - unicode_characters = True - if not self.allow_unicode: - special_characters = True - else: - special_characters = True - - # Detect important whitespace combinations. - if ch == ' ': - if index == 0: - leading_space = True - if index == len(scalar)-1: - trailing_space = True - if previous_break: - break_space = True - previous_space = True - previous_break = False - elif ch in '\n\x85\u2028\u2029': - if index == 0: - leading_break = True - if index == len(scalar)-1: - trailing_break = True - if previous_space: - space_break = True - previous_space = False - previous_break = True - else: - previous_space = False - previous_break = False - - # Prepare for the next character. - index += 1 - preceded_by_whitespace = (ch in '\0 \t\r\n\x85\u2028\u2029') - followed_by_whitespace = (index+1 >= len(scalar) or - scalar[index+1] in '\0 \t\r\n\x85\u2028\u2029') - - # Let's decide what styles are allowed. - allow_flow_plain = True - allow_block_plain = True - allow_single_quoted = True - allow_double_quoted = True - allow_block = True - - # Leading and trailing whitespaces are bad for plain scalars. - if (leading_space or leading_break - or trailing_space or trailing_break): - allow_flow_plain = allow_block_plain = False - - # We do not permit trailing spaces for block scalars. - if trailing_space: - allow_block = False - - # Spaces at the beginning of a new line are only acceptable for block - # scalars. - if break_space: - allow_flow_plain = allow_block_plain = allow_single_quoted = False - - # Spaces followed by breaks, as well as special character are only - # allowed for double quoted scalars. - if space_break or special_characters: - allow_flow_plain = allow_block_plain = \ - allow_single_quoted = allow_block = False - - # Although the plain scalar writer supports breaks, we never emit - # multiline plain scalars. - if line_breaks: - allow_flow_plain = allow_block_plain = False - - # Flow indicators are forbidden for flow plain scalars. - if flow_indicators: - allow_flow_plain = False - - # Block indicators are forbidden for block plain scalars. - if block_indicators: - allow_block_plain = False - - return ScalarAnalysis(scalar=scalar, - empty=False, multiline=line_breaks, - allow_flow_plain=allow_flow_plain, - allow_block_plain=allow_block_plain, - allow_single_quoted=allow_single_quoted, - allow_double_quoted=allow_double_quoted, - allow_block=allow_block) - - # Writers. - - def flush_stream(self): - if hasattr(self.stream, 'flush'): - self.stream.flush() - - def write_stream_start(self): - # Write BOM if needed. - if self.encoding and self.encoding.startswith('utf-16'): - self.stream.write('\uFEFF'.encode(self.encoding)) - - def write_stream_end(self): - self.flush_stream() - - def write_indicator(self, indicator, need_whitespace, - whitespace=False, indention=False): - if self.whitespace or not need_whitespace: - data = indicator - else: - data = ' '+indicator - self.whitespace = whitespace - self.indention = self.indention and indention - self.column += len(data) - self.open_ended = False - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - - def write_indent(self): - indent = self.indent or 0 - if not self.indention or self.column > indent \ - or (self.column == indent and not self.whitespace): - self.write_line_break() - if self.column < indent: - self.whitespace = True - data = ' '*(indent-self.column) - self.column = indent - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - - def write_line_break(self, data=None): - if data is None: - data = self.best_line_break - self.whitespace = True - self.indention = True - self.line += 1 - self.column = 0 - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - - def write_version_directive(self, version_text): - data = '%%YAML %s' % version_text - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - self.write_line_break() - - def write_tag_directive(self, handle_text, prefix_text): - data = '%%TAG %s %s' % (handle_text, prefix_text) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - self.write_line_break() - - # Scalar streams. - - def write_single_quoted(self, text, split=True): - self.write_indicator('\'', True) - spaces = False - breaks = False - start = end = 0 - while end <= len(text): - ch = None - if end < len(text): - ch = text[end] - if spaces: - if ch is None or ch != ' ': - if start+1 == end and self.column > self.best_width and split \ - and start != 0 and end != len(text): - self.write_indent() - else: - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end - elif breaks: - if ch is None or ch not in '\n\x85\u2028\u2029': - if text[start] == '\n': - self.write_line_break() - for br in text[start:end]: - if br == '\n': - self.write_line_break() - else: - self.write_line_break(br) - self.write_indent() - start = end - else: - if ch is None or ch in ' \n\x85\u2028\u2029' or ch == '\'': - if start < end: - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end - if ch == '\'': - data = '\'\'' - self.column += 2 - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end + 1 - if ch is not None: - spaces = (ch == ' ') - breaks = (ch in '\n\x85\u2028\u2029') - end += 1 - self.write_indicator('\'', False) - - ESCAPE_REPLACEMENTS = { - '\0': '0', - '\x07': 'a', - '\x08': 'b', - '\x09': 't', - '\x0A': 'n', - '\x0B': 'v', - '\x0C': 'f', - '\x0D': 'r', - '\x1B': 'e', - '\"': '\"', - '\\': '\\', - '\x85': 'N', - '\xA0': '_', - '\u2028': 'L', - '\u2029': 'P', - } - - def write_double_quoted(self, text, split=True): - self.write_indicator('"', True) - start = end = 0 - while end <= len(text): - ch = None - if end < len(text): - ch = text[end] - if ch is None or ch in '"\\\x85\u2028\u2029\uFEFF' \ - or not ('\x20' <= ch <= '\x7E' - or (self.allow_unicode - and ('\xA0' <= ch <= '\uD7FF' - or '\uE000' <= ch <= '\uFFFD'))): - if start < end: - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end - if ch is not None: - if ch in self.ESCAPE_REPLACEMENTS: - data = '\\'+self.ESCAPE_REPLACEMENTS[ch] - elif ch <= '\xFF': - data = '\\x%02X' % ord(ch) - elif ch <= '\uFFFF': - data = '\\u%04X' % ord(ch) - else: - data = '\\U%08X' % ord(ch) - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end+1 - if 0 < end < len(text)-1 and (ch == ' ' or start >= end) \ - and self.column+(end-start) > self.best_width and split: - data = text[start:end]+'\\' - if start < end: - start = end - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - self.write_indent() - self.whitespace = False - self.indention = False - if text[start] == ' ': - data = '\\' - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - end += 1 - self.write_indicator('"', False) - - def determine_block_hints(self, text): - hints = '' - if text: - if text[0] in ' \n\x85\u2028\u2029': - hints += str(self.best_indent) - if text[-1] not in '\n\x85\u2028\u2029': - hints += '-' - elif len(text) == 1 or text[-2] in '\n\x85\u2028\u2029': - hints += '+' - return hints - - def write_folded(self, text): - hints = self.determine_block_hints(text) - self.write_indicator('>'+hints, True) - if hints[-1:] == '+': - self.open_ended = True - self.write_line_break() - leading_space = True - spaces = False - breaks = True - start = end = 0 - while end <= len(text): - ch = None - if end < len(text): - ch = text[end] - if breaks: - if ch is None or ch not in '\n\x85\u2028\u2029': - if not leading_space and ch is not None and ch != ' ' \ - and text[start] == '\n': - self.write_line_break() - leading_space = (ch == ' ') - for br in text[start:end]: - if br == '\n': - self.write_line_break() - else: - self.write_line_break(br) - if ch is not None: - self.write_indent() - start = end - elif spaces: - if ch != ' ': - if start+1 == end and self.column > self.best_width: - self.write_indent() - else: - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end - else: - if ch is None or ch in ' \n\x85\u2028\u2029': - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - if ch is None: - self.write_line_break() - start = end - if ch is not None: - breaks = (ch in '\n\x85\u2028\u2029') - spaces = (ch == ' ') - end += 1 - - def write_literal(self, text): - hints = self.determine_block_hints(text) - self.write_indicator('|'+hints, True) - if hints[-1:] == '+': - self.open_ended = True - self.write_line_break() - breaks = True - start = end = 0 - while end <= len(text): - ch = None - if end < len(text): - ch = text[end] - if breaks: - if ch is None or ch not in '\n\x85\u2028\u2029': - for br in text[start:end]: - if br == '\n': - self.write_line_break() - else: - self.write_line_break(br) - if ch is not None: - self.write_indent() - start = end - else: - if ch is None or ch in '\n\x85\u2028\u2029': - data = text[start:end] - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - if ch is None: - self.write_line_break() - start = end - if ch is not None: - breaks = (ch in '\n\x85\u2028\u2029') - end += 1 - - def write_plain(self, text, split=True): - if self.root_context: - self.open_ended = True - if not text: - return - if not self.whitespace: - data = ' ' - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - self.whitespace = False - self.indention = False - spaces = False - breaks = False - start = end = 0 - while end <= len(text): - ch = None - if end < len(text): - ch = text[end] - if spaces: - if ch != ' ': - if start+1 == end and self.column > self.best_width and split: - self.write_indent() - self.whitespace = False - self.indention = False - else: - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end - elif breaks: - if ch not in '\n\x85\u2028\u2029': - if text[start] == '\n': - self.write_line_break() - for br in text[start:end]: - if br == '\n': - self.write_line_break() - else: - self.write_line_break(br) - self.write_indent() - self.whitespace = False - self.indention = False - start = end - else: - if ch is None or ch in ' \n\x85\u2028\u2029': - data = text[start:end] - self.column += len(data) - if self.encoding: - data = data.encode(self.encoding) - self.stream.write(data) - start = end - if ch is not None: - spaces = (ch == ' ') - breaks = (ch in '\n\x85\u2028\u2029') - end += 1 diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/error.py b/plotter-app/venv/lib/python3.8/site-packages/yaml/error.py deleted file mode 100644 index b796b4d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/yaml/error.py +++ /dev/null @@ -1,75 +0,0 @@ - -__all__ = ['Mark', 'YAMLError', 'MarkedYAMLError'] - -class Mark: - - def __init__(self, name, index, line, column, buffer, pointer): - self.name = name - self.index = index - self.line = line - self.column = column - self.buffer = buffer - self.pointer = pointer - - def get_snippet(self, indent=4, max_length=75): - if self.buffer is None: - return None - head = '' - start = self.pointer - while start > 0 and self.buffer[start-1] not in '\0\r\n\x85\u2028\u2029': - start -= 1 - if self.pointer-start > max_length/2-1: - head = ' ... ' - start += 5 - break - tail = '' - end = self.pointer - while end < len(self.buffer) and self.buffer[end] not in '\0\r\n\x85\u2028\u2029': - end += 1 - if end-self.pointer > max_length/2-1: - tail = ' ... ' - end -= 5 - break - snippet = self.buffer[start:end] - return ' '*indent + head + snippet + tail + '\n' \ - + ' '*(indent+self.pointer-start+len(head)) + '^' - - def __str__(self): - snippet = self.get_snippet() - where = " in \"%s\", line %d, column %d" \ - % (self.name, self.line+1, self.column+1) - if snippet is not None: - where += ":\n"+snippet - return where - -class YAMLError(Exception): - pass - -class MarkedYAMLError(YAMLError): - - def __init__(self, context=None, context_mark=None, - problem=None, problem_mark=None, note=None): - self.context = context - self.context_mark = context_mark - self.problem = problem - self.problem_mark = problem_mark - self.note = note - - def __str__(self): - lines = [] - if self.context is not None: - lines.append(self.context) - if self.context_mark is not None \ - and (self.problem is None or self.problem_mark is None - or self.context_mark.name != self.problem_mark.name - or self.context_mark.line != self.problem_mark.line - or self.context_mark.column != self.problem_mark.column): - lines.append(str(self.context_mark)) - if self.problem is not None: - lines.append(self.problem) - if self.problem_mark is not None: - lines.append(str(self.problem_mark)) - if self.note is not None: - lines.append(self.note) - return '\n'.join(lines) - diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/events.py b/plotter-app/venv/lib/python3.8/site-packages/yaml/events.py deleted file mode 100644 index f79ad38..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/yaml/events.py +++ /dev/null @@ -1,86 +0,0 @@ - -# Abstract classes. - -class Event(object): - def __init__(self, start_mark=None, end_mark=None): - self.start_mark = start_mark - self.end_mark = end_mark - def __repr__(self): - attributes = [key for key in ['anchor', 'tag', 'implicit', 'value'] - if hasattr(self, key)] - arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) - for key in attributes]) - return '%s(%s)' % (self.__class__.__name__, arguments) - -class NodeEvent(Event): - def __init__(self, anchor, start_mark=None, end_mark=None): - self.anchor = anchor - self.start_mark = start_mark - self.end_mark = end_mark - -class CollectionStartEvent(NodeEvent): - def __init__(self, anchor, tag, implicit, start_mark=None, end_mark=None, - flow_style=None): - self.anchor = anchor - self.tag = tag - self.implicit = implicit - self.start_mark = start_mark - self.end_mark = end_mark - self.flow_style = flow_style - -class CollectionEndEvent(Event): - pass - -# Implementations. - -class StreamStartEvent(Event): - def __init__(self, start_mark=None, end_mark=None, encoding=None): - self.start_mark = start_mark - self.end_mark = end_mark - self.encoding = encoding - -class StreamEndEvent(Event): - pass - -class DocumentStartEvent(Event): - def __init__(self, start_mark=None, end_mark=None, - explicit=None, version=None, tags=None): - self.start_mark = start_mark - self.end_mark = end_mark - self.explicit = explicit - self.version = version - self.tags = tags - -class DocumentEndEvent(Event): - def __init__(self, start_mark=None, end_mark=None, - explicit=None): - self.start_mark = start_mark - self.end_mark = end_mark - self.explicit = explicit - -class AliasEvent(NodeEvent): - pass - -class ScalarEvent(NodeEvent): - def __init__(self, anchor, tag, implicit, value, - start_mark=None, end_mark=None, style=None): - self.anchor = anchor - self.tag = tag - self.implicit = implicit - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - self.style = style - -class SequenceStartEvent(CollectionStartEvent): - pass - -class SequenceEndEvent(CollectionEndEvent): - pass - -class MappingStartEvent(CollectionStartEvent): - pass - -class MappingEndEvent(CollectionEndEvent): - pass - diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/loader.py b/plotter-app/venv/lib/python3.8/site-packages/yaml/loader.py deleted file mode 100644 index e90c112..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/yaml/loader.py +++ /dev/null @@ -1,63 +0,0 @@ - -__all__ = ['BaseLoader', 'FullLoader', 'SafeLoader', 'Loader', 'UnsafeLoader'] - -from .reader import * -from .scanner import * -from .parser import * -from .composer import * -from .constructor import * -from .resolver import * - -class BaseLoader(Reader, Scanner, Parser, Composer, BaseConstructor, BaseResolver): - - def __init__(self, stream): - Reader.__init__(self, stream) - Scanner.__init__(self) - Parser.__init__(self) - Composer.__init__(self) - BaseConstructor.__init__(self) - BaseResolver.__init__(self) - -class FullLoader(Reader, Scanner, Parser, Composer, FullConstructor, Resolver): - - def __init__(self, stream): - Reader.__init__(self, stream) - Scanner.__init__(self) - Parser.__init__(self) - Composer.__init__(self) - FullConstructor.__init__(self) - Resolver.__init__(self) - -class SafeLoader(Reader, Scanner, Parser, Composer, SafeConstructor, Resolver): - - def __init__(self, stream): - Reader.__init__(self, stream) - Scanner.__init__(self) - Parser.__init__(self) - Composer.__init__(self) - SafeConstructor.__init__(self) - Resolver.__init__(self) - -class Loader(Reader, Scanner, Parser, Composer, Constructor, Resolver): - - def __init__(self, stream): - Reader.__init__(self, stream) - Scanner.__init__(self) - Parser.__init__(self) - Composer.__init__(self) - Constructor.__init__(self) - Resolver.__init__(self) - -# UnsafeLoader is the same as Loader (which is and was always unsafe on -# untrusted input). Use of either Loader or UnsafeLoader should be rare, since -# FullLoad should be able to load almost all YAML safely. Loader is left intact -# to ensure backwards compatibility. -class UnsafeLoader(Reader, Scanner, Parser, Composer, Constructor, Resolver): - - def __init__(self, stream): - Reader.__init__(self, stream) - Scanner.__init__(self) - Parser.__init__(self) - Composer.__init__(self) - Constructor.__init__(self) - Resolver.__init__(self) diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/nodes.py b/plotter-app/venv/lib/python3.8/site-packages/yaml/nodes.py deleted file mode 100644 index c4f070c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/yaml/nodes.py +++ /dev/null @@ -1,49 +0,0 @@ - -class Node(object): - def __init__(self, tag, value, start_mark, end_mark): - self.tag = tag - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - def __repr__(self): - value = self.value - #if isinstance(value, list): - # if len(value) == 0: - # value = '' - # elif len(value) == 1: - # value = '<1 item>' - # else: - # value = '<%d items>' % len(value) - #else: - # if len(value) > 75: - # value = repr(value[:70]+u' ... ') - # else: - # value = repr(value) - value = repr(value) - return '%s(tag=%r, value=%s)' % (self.__class__.__name__, self.tag, value) - -class ScalarNode(Node): - id = 'scalar' - def __init__(self, tag, value, - start_mark=None, end_mark=None, style=None): - self.tag = tag - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - self.style = style - -class CollectionNode(Node): - def __init__(self, tag, value, - start_mark=None, end_mark=None, flow_style=None): - self.tag = tag - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - self.flow_style = flow_style - -class SequenceNode(CollectionNode): - id = 'sequence' - -class MappingNode(CollectionNode): - id = 'mapping' - diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/parser.py b/plotter-app/venv/lib/python3.8/site-packages/yaml/parser.py deleted file mode 100644 index 13a5995..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/yaml/parser.py +++ /dev/null @@ -1,589 +0,0 @@ - -# The following YAML grammar is LL(1) and is parsed by a recursive descent -# parser. -# -# stream ::= STREAM-START implicit_document? explicit_document* STREAM-END -# implicit_document ::= block_node DOCUMENT-END* -# explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* -# block_node_or_indentless_sequence ::= -# ALIAS -# | properties (block_content | indentless_block_sequence)? -# | block_content -# | indentless_block_sequence -# block_node ::= ALIAS -# | properties block_content? -# | block_content -# flow_node ::= ALIAS -# | properties flow_content? -# | flow_content -# properties ::= TAG ANCHOR? | ANCHOR TAG? -# block_content ::= block_collection | flow_collection | SCALAR -# flow_content ::= flow_collection | SCALAR -# block_collection ::= block_sequence | block_mapping -# flow_collection ::= flow_sequence | flow_mapping -# block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END -# indentless_sequence ::= (BLOCK-ENTRY block_node?)+ -# block_mapping ::= BLOCK-MAPPING_START -# ((KEY block_node_or_indentless_sequence?)? -# (VALUE block_node_or_indentless_sequence?)?)* -# BLOCK-END -# flow_sequence ::= FLOW-SEQUENCE-START -# (flow_sequence_entry FLOW-ENTRY)* -# flow_sequence_entry? -# FLOW-SEQUENCE-END -# flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -# flow_mapping ::= FLOW-MAPPING-START -# (flow_mapping_entry FLOW-ENTRY)* -# flow_mapping_entry? -# FLOW-MAPPING-END -# flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -# -# FIRST sets: -# -# stream: { STREAM-START } -# explicit_document: { DIRECTIVE DOCUMENT-START } -# implicit_document: FIRST(block_node) -# block_node: { ALIAS TAG ANCHOR SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START } -# flow_node: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START } -# block_content: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR } -# flow_content: { FLOW-SEQUENCE-START FLOW-MAPPING-START SCALAR } -# block_collection: { BLOCK-SEQUENCE-START BLOCK-MAPPING-START } -# flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START } -# block_sequence: { BLOCK-SEQUENCE-START } -# block_mapping: { BLOCK-MAPPING-START } -# block_node_or_indentless_sequence: { ALIAS ANCHOR TAG SCALAR BLOCK-SEQUENCE-START BLOCK-MAPPING-START FLOW-SEQUENCE-START FLOW-MAPPING-START BLOCK-ENTRY } -# indentless_sequence: { ENTRY } -# flow_collection: { FLOW-SEQUENCE-START FLOW-MAPPING-START } -# flow_sequence: { FLOW-SEQUENCE-START } -# flow_mapping: { FLOW-MAPPING-START } -# flow_sequence_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY } -# flow_mapping_entry: { ALIAS ANCHOR TAG SCALAR FLOW-SEQUENCE-START FLOW-MAPPING-START KEY } - -__all__ = ['Parser', 'ParserError'] - -from .error import MarkedYAMLError -from .tokens import * -from .events import * -from .scanner import * - -class ParserError(MarkedYAMLError): - pass - -class Parser: - # Since writing a recursive-descendant parser is a straightforward task, we - # do not give many comments here. - - DEFAULT_TAGS = { - '!': '!', - '!!': 'tag:yaml.org,2002:', - } - - def __init__(self): - self.current_event = None - self.yaml_version = None - self.tag_handles = {} - self.states = [] - self.marks = [] - self.state = self.parse_stream_start - - def dispose(self): - # Reset the state attributes (to clear self-references) - self.states = [] - self.state = None - - def check_event(self, *choices): - # Check the type of the next event. - if self.current_event is None: - if self.state: - self.current_event = self.state() - if self.current_event is not None: - if not choices: - return True - for choice in choices: - if isinstance(self.current_event, choice): - return True - return False - - def peek_event(self): - # Get the next event. - if self.current_event is None: - if self.state: - self.current_event = self.state() - return self.current_event - - def get_event(self): - # Get the next event and proceed further. - if self.current_event is None: - if self.state: - self.current_event = self.state() - value = self.current_event - self.current_event = None - return value - - # stream ::= STREAM-START implicit_document? explicit_document* STREAM-END - # implicit_document ::= block_node DOCUMENT-END* - # explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* - - def parse_stream_start(self): - - # Parse the stream start. - token = self.get_token() - event = StreamStartEvent(token.start_mark, token.end_mark, - encoding=token.encoding) - - # Prepare the next state. - self.state = self.parse_implicit_document_start - - return event - - def parse_implicit_document_start(self): - - # Parse an implicit document. - if not self.check_token(DirectiveToken, DocumentStartToken, - StreamEndToken): - self.tag_handles = self.DEFAULT_TAGS - token = self.peek_token() - start_mark = end_mark = token.start_mark - event = DocumentStartEvent(start_mark, end_mark, - explicit=False) - - # Prepare the next state. - self.states.append(self.parse_document_end) - self.state = self.parse_block_node - - return event - - else: - return self.parse_document_start() - - def parse_document_start(self): - - # Parse any extra document end indicators. - while self.check_token(DocumentEndToken): - self.get_token() - - # Parse an explicit document. - if not self.check_token(StreamEndToken): - token = self.peek_token() - start_mark = token.start_mark - version, tags = self.process_directives() - if not self.check_token(DocumentStartToken): - raise ParserError(None, None, - "expected '', but found %r" - % self.peek_token().id, - self.peek_token().start_mark) - token = self.get_token() - end_mark = token.end_mark - event = DocumentStartEvent(start_mark, end_mark, - explicit=True, version=version, tags=tags) - self.states.append(self.parse_document_end) - self.state = self.parse_document_content - else: - # Parse the end of the stream. - token = self.get_token() - event = StreamEndEvent(token.start_mark, token.end_mark) - assert not self.states - assert not self.marks - self.state = None - return event - - def parse_document_end(self): - - # Parse the document end. - token = self.peek_token() - start_mark = end_mark = token.start_mark - explicit = False - if self.check_token(DocumentEndToken): - token = self.get_token() - end_mark = token.end_mark - explicit = True - event = DocumentEndEvent(start_mark, end_mark, - explicit=explicit) - - # Prepare the next state. - self.state = self.parse_document_start - - return event - - def parse_document_content(self): - if self.check_token(DirectiveToken, - DocumentStartToken, DocumentEndToken, StreamEndToken): - event = self.process_empty_scalar(self.peek_token().start_mark) - self.state = self.states.pop() - return event - else: - return self.parse_block_node() - - def process_directives(self): - self.yaml_version = None - self.tag_handles = {} - while self.check_token(DirectiveToken): - token = self.get_token() - if token.name == 'YAML': - if self.yaml_version is not None: - raise ParserError(None, None, - "found duplicate YAML directive", token.start_mark) - major, minor = token.value - if major != 1: - raise ParserError(None, None, - "found incompatible YAML document (version 1.* is required)", - token.start_mark) - self.yaml_version = token.value - elif token.name == 'TAG': - handle, prefix = token.value - if handle in self.tag_handles: - raise ParserError(None, None, - "duplicate tag handle %r" % handle, - token.start_mark) - self.tag_handles[handle] = prefix - if self.tag_handles: - value = self.yaml_version, self.tag_handles.copy() - else: - value = self.yaml_version, None - for key in self.DEFAULT_TAGS: - if key not in self.tag_handles: - self.tag_handles[key] = self.DEFAULT_TAGS[key] - return value - - # block_node_or_indentless_sequence ::= ALIAS - # | properties (block_content | indentless_block_sequence)? - # | block_content - # | indentless_block_sequence - # block_node ::= ALIAS - # | properties block_content? - # | block_content - # flow_node ::= ALIAS - # | properties flow_content? - # | flow_content - # properties ::= TAG ANCHOR? | ANCHOR TAG? - # block_content ::= block_collection | flow_collection | SCALAR - # flow_content ::= flow_collection | SCALAR - # block_collection ::= block_sequence | block_mapping - # flow_collection ::= flow_sequence | flow_mapping - - def parse_block_node(self): - return self.parse_node(block=True) - - def parse_flow_node(self): - return self.parse_node() - - def parse_block_node_or_indentless_sequence(self): - return self.parse_node(block=True, indentless_sequence=True) - - def parse_node(self, block=False, indentless_sequence=False): - if self.check_token(AliasToken): - token = self.get_token() - event = AliasEvent(token.value, token.start_mark, token.end_mark) - self.state = self.states.pop() - else: - anchor = None - tag = None - start_mark = end_mark = tag_mark = None - if self.check_token(AnchorToken): - token = self.get_token() - start_mark = token.start_mark - end_mark = token.end_mark - anchor = token.value - if self.check_token(TagToken): - token = self.get_token() - tag_mark = token.start_mark - end_mark = token.end_mark - tag = token.value - elif self.check_token(TagToken): - token = self.get_token() - start_mark = tag_mark = token.start_mark - end_mark = token.end_mark - tag = token.value - if self.check_token(AnchorToken): - token = self.get_token() - end_mark = token.end_mark - anchor = token.value - if tag is not None: - handle, suffix = tag - if handle is not None: - if handle not in self.tag_handles: - raise ParserError("while parsing a node", start_mark, - "found undefined tag handle %r" % handle, - tag_mark) - tag = self.tag_handles[handle]+suffix - else: - tag = suffix - #if tag == '!': - # raise ParserError("while parsing a node", start_mark, - # "found non-specific tag '!'", tag_mark, - # "Please check 'http://pyyaml.org/wiki/YAMLNonSpecificTag' and share your opinion.") - if start_mark is None: - start_mark = end_mark = self.peek_token().start_mark - event = None - implicit = (tag is None or tag == '!') - if indentless_sequence and self.check_token(BlockEntryToken): - end_mark = self.peek_token().end_mark - event = SequenceStartEvent(anchor, tag, implicit, - start_mark, end_mark) - self.state = self.parse_indentless_sequence_entry - else: - if self.check_token(ScalarToken): - token = self.get_token() - end_mark = token.end_mark - if (token.plain and tag is None) or tag == '!': - implicit = (True, False) - elif tag is None: - implicit = (False, True) - else: - implicit = (False, False) - event = ScalarEvent(anchor, tag, implicit, token.value, - start_mark, end_mark, style=token.style) - self.state = self.states.pop() - elif self.check_token(FlowSequenceStartToken): - end_mark = self.peek_token().end_mark - event = SequenceStartEvent(anchor, tag, implicit, - start_mark, end_mark, flow_style=True) - self.state = self.parse_flow_sequence_first_entry - elif self.check_token(FlowMappingStartToken): - end_mark = self.peek_token().end_mark - event = MappingStartEvent(anchor, tag, implicit, - start_mark, end_mark, flow_style=True) - self.state = self.parse_flow_mapping_first_key - elif block and self.check_token(BlockSequenceStartToken): - end_mark = self.peek_token().start_mark - event = SequenceStartEvent(anchor, tag, implicit, - start_mark, end_mark, flow_style=False) - self.state = self.parse_block_sequence_first_entry - elif block and self.check_token(BlockMappingStartToken): - end_mark = self.peek_token().start_mark - event = MappingStartEvent(anchor, tag, implicit, - start_mark, end_mark, flow_style=False) - self.state = self.parse_block_mapping_first_key - elif anchor is not None or tag is not None: - # Empty scalars are allowed even if a tag or an anchor is - # specified. - event = ScalarEvent(anchor, tag, (implicit, False), '', - start_mark, end_mark) - self.state = self.states.pop() - else: - if block: - node = 'block' - else: - node = 'flow' - token = self.peek_token() - raise ParserError("while parsing a %s node" % node, start_mark, - "expected the node content, but found %r" % token.id, - token.start_mark) - return event - - # block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END - - def parse_block_sequence_first_entry(self): - token = self.get_token() - self.marks.append(token.start_mark) - return self.parse_block_sequence_entry() - - def parse_block_sequence_entry(self): - if self.check_token(BlockEntryToken): - token = self.get_token() - if not self.check_token(BlockEntryToken, BlockEndToken): - self.states.append(self.parse_block_sequence_entry) - return self.parse_block_node() - else: - self.state = self.parse_block_sequence_entry - return self.process_empty_scalar(token.end_mark) - if not self.check_token(BlockEndToken): - token = self.peek_token() - raise ParserError("while parsing a block collection", self.marks[-1], - "expected , but found %r" % token.id, token.start_mark) - token = self.get_token() - event = SequenceEndEvent(token.start_mark, token.end_mark) - self.state = self.states.pop() - self.marks.pop() - return event - - # indentless_sequence ::= (BLOCK-ENTRY block_node?)+ - - def parse_indentless_sequence_entry(self): - if self.check_token(BlockEntryToken): - token = self.get_token() - if not self.check_token(BlockEntryToken, - KeyToken, ValueToken, BlockEndToken): - self.states.append(self.parse_indentless_sequence_entry) - return self.parse_block_node() - else: - self.state = self.parse_indentless_sequence_entry - return self.process_empty_scalar(token.end_mark) - token = self.peek_token() - event = SequenceEndEvent(token.start_mark, token.start_mark) - self.state = self.states.pop() - return event - - # block_mapping ::= BLOCK-MAPPING_START - # ((KEY block_node_or_indentless_sequence?)? - # (VALUE block_node_or_indentless_sequence?)?)* - # BLOCK-END - - def parse_block_mapping_first_key(self): - token = self.get_token() - self.marks.append(token.start_mark) - return self.parse_block_mapping_key() - - def parse_block_mapping_key(self): - if self.check_token(KeyToken): - token = self.get_token() - if not self.check_token(KeyToken, ValueToken, BlockEndToken): - self.states.append(self.parse_block_mapping_value) - return self.parse_block_node_or_indentless_sequence() - else: - self.state = self.parse_block_mapping_value - return self.process_empty_scalar(token.end_mark) - if not self.check_token(BlockEndToken): - token = self.peek_token() - raise ParserError("while parsing a block mapping", self.marks[-1], - "expected , but found %r" % token.id, token.start_mark) - token = self.get_token() - event = MappingEndEvent(token.start_mark, token.end_mark) - self.state = self.states.pop() - self.marks.pop() - return event - - def parse_block_mapping_value(self): - if self.check_token(ValueToken): - token = self.get_token() - if not self.check_token(KeyToken, ValueToken, BlockEndToken): - self.states.append(self.parse_block_mapping_key) - return self.parse_block_node_or_indentless_sequence() - else: - self.state = self.parse_block_mapping_key - return self.process_empty_scalar(token.end_mark) - else: - self.state = self.parse_block_mapping_key - token = self.peek_token() - return self.process_empty_scalar(token.start_mark) - - # flow_sequence ::= FLOW-SEQUENCE-START - # (flow_sequence_entry FLOW-ENTRY)* - # flow_sequence_entry? - # FLOW-SEQUENCE-END - # flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? - # - # Note that while production rules for both flow_sequence_entry and - # flow_mapping_entry are equal, their interpretations are different. - # For `flow_sequence_entry`, the part `KEY flow_node? (VALUE flow_node?)?` - # generate an inline mapping (set syntax). - - def parse_flow_sequence_first_entry(self): - token = self.get_token() - self.marks.append(token.start_mark) - return self.parse_flow_sequence_entry(first=True) - - def parse_flow_sequence_entry(self, first=False): - if not self.check_token(FlowSequenceEndToken): - if not first: - if self.check_token(FlowEntryToken): - self.get_token() - else: - token = self.peek_token() - raise ParserError("while parsing a flow sequence", self.marks[-1], - "expected ',' or ']', but got %r" % token.id, token.start_mark) - - if self.check_token(KeyToken): - token = self.peek_token() - event = MappingStartEvent(None, None, True, - token.start_mark, token.end_mark, - flow_style=True) - self.state = self.parse_flow_sequence_entry_mapping_key - return event - elif not self.check_token(FlowSequenceEndToken): - self.states.append(self.parse_flow_sequence_entry) - return self.parse_flow_node() - token = self.get_token() - event = SequenceEndEvent(token.start_mark, token.end_mark) - self.state = self.states.pop() - self.marks.pop() - return event - - def parse_flow_sequence_entry_mapping_key(self): - token = self.get_token() - if not self.check_token(ValueToken, - FlowEntryToken, FlowSequenceEndToken): - self.states.append(self.parse_flow_sequence_entry_mapping_value) - return self.parse_flow_node() - else: - self.state = self.parse_flow_sequence_entry_mapping_value - return self.process_empty_scalar(token.end_mark) - - def parse_flow_sequence_entry_mapping_value(self): - if self.check_token(ValueToken): - token = self.get_token() - if not self.check_token(FlowEntryToken, FlowSequenceEndToken): - self.states.append(self.parse_flow_sequence_entry_mapping_end) - return self.parse_flow_node() - else: - self.state = self.parse_flow_sequence_entry_mapping_end - return self.process_empty_scalar(token.end_mark) - else: - self.state = self.parse_flow_sequence_entry_mapping_end - token = self.peek_token() - return self.process_empty_scalar(token.start_mark) - - def parse_flow_sequence_entry_mapping_end(self): - self.state = self.parse_flow_sequence_entry - token = self.peek_token() - return MappingEndEvent(token.start_mark, token.start_mark) - - # flow_mapping ::= FLOW-MAPPING-START - # (flow_mapping_entry FLOW-ENTRY)* - # flow_mapping_entry? - # FLOW-MAPPING-END - # flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? - - def parse_flow_mapping_first_key(self): - token = self.get_token() - self.marks.append(token.start_mark) - return self.parse_flow_mapping_key(first=True) - - def parse_flow_mapping_key(self, first=False): - if not self.check_token(FlowMappingEndToken): - if not first: - if self.check_token(FlowEntryToken): - self.get_token() - else: - token = self.peek_token() - raise ParserError("while parsing a flow mapping", self.marks[-1], - "expected ',' or '}', but got %r" % token.id, token.start_mark) - if self.check_token(KeyToken): - token = self.get_token() - if not self.check_token(ValueToken, - FlowEntryToken, FlowMappingEndToken): - self.states.append(self.parse_flow_mapping_value) - return self.parse_flow_node() - else: - self.state = self.parse_flow_mapping_value - return self.process_empty_scalar(token.end_mark) - elif not self.check_token(FlowMappingEndToken): - self.states.append(self.parse_flow_mapping_empty_value) - return self.parse_flow_node() - token = self.get_token() - event = MappingEndEvent(token.start_mark, token.end_mark) - self.state = self.states.pop() - self.marks.pop() - return event - - def parse_flow_mapping_value(self): - if self.check_token(ValueToken): - token = self.get_token() - if not self.check_token(FlowEntryToken, FlowMappingEndToken): - self.states.append(self.parse_flow_mapping_key) - return self.parse_flow_node() - else: - self.state = self.parse_flow_mapping_key - return self.process_empty_scalar(token.end_mark) - else: - self.state = self.parse_flow_mapping_key - token = self.peek_token() - return self.process_empty_scalar(token.start_mark) - - def parse_flow_mapping_empty_value(self): - self.state = self.parse_flow_mapping_key - return self.process_empty_scalar(self.peek_token().start_mark) - - def process_empty_scalar(self, mark): - return ScalarEvent(None, None, (True, False), '', mark, mark) - diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/reader.py b/plotter-app/venv/lib/python3.8/site-packages/yaml/reader.py deleted file mode 100644 index 774b021..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/yaml/reader.py +++ /dev/null @@ -1,185 +0,0 @@ -# This module contains abstractions for the input stream. You don't have to -# looks further, there are no pretty code. -# -# We define two classes here. -# -# Mark(source, line, column) -# It's just a record and its only use is producing nice error messages. -# Parser does not use it for any other purposes. -# -# Reader(source, data) -# Reader determines the encoding of `data` and converts it to unicode. -# Reader provides the following methods and attributes: -# reader.peek(length=1) - return the next `length` characters -# reader.forward(length=1) - move the current position to `length` characters. -# reader.index - the number of the current character. -# reader.line, stream.column - the line and the column of the current character. - -__all__ = ['Reader', 'ReaderError'] - -from .error import YAMLError, Mark - -import codecs, re - -class ReaderError(YAMLError): - - def __init__(self, name, position, character, encoding, reason): - self.name = name - self.character = character - self.position = position - self.encoding = encoding - self.reason = reason - - def __str__(self): - if isinstance(self.character, bytes): - return "'%s' codec can't decode byte #x%02x: %s\n" \ - " in \"%s\", position %d" \ - % (self.encoding, ord(self.character), self.reason, - self.name, self.position) - else: - return "unacceptable character #x%04x: %s\n" \ - " in \"%s\", position %d" \ - % (self.character, self.reason, - self.name, self.position) - -class Reader(object): - # Reader: - # - determines the data encoding and converts it to a unicode string, - # - checks if characters are in allowed range, - # - adds '\0' to the end. - - # Reader accepts - # - a `bytes` object, - # - a `str` object, - # - a file-like object with its `read` method returning `str`, - # - a file-like object with its `read` method returning `unicode`. - - # Yeah, it's ugly and slow. - - def __init__(self, stream): - self.name = None - self.stream = None - self.stream_pointer = 0 - self.eof = True - self.buffer = '' - self.pointer = 0 - self.raw_buffer = None - self.raw_decode = None - self.encoding = None - self.index = 0 - self.line = 0 - self.column = 0 - if isinstance(stream, str): - self.name = "" - self.check_printable(stream) - self.buffer = stream+'\0' - elif isinstance(stream, bytes): - self.name = "" - self.raw_buffer = stream - self.determine_encoding() - else: - self.stream = stream - self.name = getattr(stream, 'name', "") - self.eof = False - self.raw_buffer = None - self.determine_encoding() - - def peek(self, index=0): - try: - return self.buffer[self.pointer+index] - except IndexError: - self.update(index+1) - return self.buffer[self.pointer+index] - - def prefix(self, length=1): - if self.pointer+length >= len(self.buffer): - self.update(length) - return self.buffer[self.pointer:self.pointer+length] - - def forward(self, length=1): - if self.pointer+length+1 >= len(self.buffer): - self.update(length+1) - while length: - ch = self.buffer[self.pointer] - self.pointer += 1 - self.index += 1 - if ch in '\n\x85\u2028\u2029' \ - or (ch == '\r' and self.buffer[self.pointer] != '\n'): - self.line += 1 - self.column = 0 - elif ch != '\uFEFF': - self.column += 1 - length -= 1 - - def get_mark(self): - if self.stream is None: - return Mark(self.name, self.index, self.line, self.column, - self.buffer, self.pointer) - else: - return Mark(self.name, self.index, self.line, self.column, - None, None) - - def determine_encoding(self): - while not self.eof and (self.raw_buffer is None or len(self.raw_buffer) < 2): - self.update_raw() - if isinstance(self.raw_buffer, bytes): - if self.raw_buffer.startswith(codecs.BOM_UTF16_LE): - self.raw_decode = codecs.utf_16_le_decode - self.encoding = 'utf-16-le' - elif self.raw_buffer.startswith(codecs.BOM_UTF16_BE): - self.raw_decode = codecs.utf_16_be_decode - self.encoding = 'utf-16-be' - else: - self.raw_decode = codecs.utf_8_decode - self.encoding = 'utf-8' - self.update(1) - - NON_PRINTABLE = re.compile('[^\x09\x0A\x0D\x20-\x7E\x85\xA0-\uD7FF\uE000-\uFFFD\U00010000-\U0010ffff]') - def check_printable(self, data): - match = self.NON_PRINTABLE.search(data) - if match: - character = match.group() - position = self.index+(len(self.buffer)-self.pointer)+match.start() - raise ReaderError(self.name, position, ord(character), - 'unicode', "special characters are not allowed") - - def update(self, length): - if self.raw_buffer is None: - return - self.buffer = self.buffer[self.pointer:] - self.pointer = 0 - while len(self.buffer) < length: - if not self.eof: - self.update_raw() - if self.raw_decode is not None: - try: - data, converted = self.raw_decode(self.raw_buffer, - 'strict', self.eof) - except UnicodeDecodeError as exc: - character = self.raw_buffer[exc.start] - if self.stream is not None: - position = self.stream_pointer-len(self.raw_buffer)+exc.start - else: - position = exc.start - raise ReaderError(self.name, position, character, - exc.encoding, exc.reason) - else: - data = self.raw_buffer - converted = len(data) - self.check_printable(data) - self.buffer += data - self.raw_buffer = self.raw_buffer[converted:] - if self.eof: - self.buffer += '\0' - self.raw_buffer = None - break - - def update_raw(self, size=4096): - data = self.stream.read(size) - if self.raw_buffer is None: - self.raw_buffer = data - else: - self.raw_buffer += data - self.stream_pointer += len(data) - if not data: - self.eof = True diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/representer.py b/plotter-app/venv/lib/python3.8/site-packages/yaml/representer.py deleted file mode 100644 index 808ca06..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/yaml/representer.py +++ /dev/null @@ -1,389 +0,0 @@ - -__all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer', - 'RepresenterError'] - -from .error import * -from .nodes import * - -import datetime, copyreg, types, base64, collections - -class RepresenterError(YAMLError): - pass - -class BaseRepresenter: - - yaml_representers = {} - yaml_multi_representers = {} - - def __init__(self, default_style=None, default_flow_style=False, sort_keys=True): - self.default_style = default_style - self.sort_keys = sort_keys - self.default_flow_style = default_flow_style - self.represented_objects = {} - self.object_keeper = [] - self.alias_key = None - - def represent(self, data): - node = self.represent_data(data) - self.serialize(node) - self.represented_objects = {} - self.object_keeper = [] - self.alias_key = None - - def represent_data(self, data): - if self.ignore_aliases(data): - self.alias_key = None - else: - self.alias_key = id(data) - if self.alias_key is not None: - if self.alias_key in self.represented_objects: - node = self.represented_objects[self.alias_key] - #if node is None: - # raise RepresenterError("recursive objects are not allowed: %r" % data) - return node - #self.represented_objects[alias_key] = None - self.object_keeper.append(data) - data_types = type(data).__mro__ - if data_types[0] in self.yaml_representers: - node = self.yaml_representers[data_types[0]](self, data) - else: - for data_type in data_types: - if data_type in self.yaml_multi_representers: - node = self.yaml_multi_representers[data_type](self, data) - break - else: - if None in self.yaml_multi_representers: - node = self.yaml_multi_representers[None](self, data) - elif None in self.yaml_representers: - node = self.yaml_representers[None](self, data) - else: - node = ScalarNode(None, str(data)) - #if alias_key is not None: - # self.represented_objects[alias_key] = node - return node - - @classmethod - def add_representer(cls, data_type, representer): - if not 'yaml_representers' in cls.__dict__: - cls.yaml_representers = cls.yaml_representers.copy() - cls.yaml_representers[data_type] = representer - - @classmethod - def add_multi_representer(cls, data_type, representer): - if not 'yaml_multi_representers' in cls.__dict__: - cls.yaml_multi_representers = cls.yaml_multi_representers.copy() - cls.yaml_multi_representers[data_type] = representer - - def represent_scalar(self, tag, value, style=None): - if style is None: - style = self.default_style - node = ScalarNode(tag, value, style=style) - if self.alias_key is not None: - self.represented_objects[self.alias_key] = node - return node - - def represent_sequence(self, tag, sequence, flow_style=None): - value = [] - node = SequenceNode(tag, value, flow_style=flow_style) - if self.alias_key is not None: - self.represented_objects[self.alias_key] = node - best_style = True - for item in sequence: - node_item = self.represent_data(item) - if not (isinstance(node_item, ScalarNode) and not node_item.style): - best_style = False - value.append(node_item) - if flow_style is None: - if self.default_flow_style is not None: - node.flow_style = self.default_flow_style - else: - node.flow_style = best_style - return node - - def represent_mapping(self, tag, mapping, flow_style=None): - value = [] - node = MappingNode(tag, value, flow_style=flow_style) - if self.alias_key is not None: - self.represented_objects[self.alias_key] = node - best_style = True - if hasattr(mapping, 'items'): - mapping = list(mapping.items()) - if self.sort_keys: - try: - mapping = sorted(mapping) - except TypeError: - pass - for item_key, item_value in mapping: - node_key = self.represent_data(item_key) - node_value = self.represent_data(item_value) - if not (isinstance(node_key, ScalarNode) and not node_key.style): - best_style = False - if not (isinstance(node_value, ScalarNode) and not node_value.style): - best_style = False - value.append((node_key, node_value)) - if flow_style is None: - if self.default_flow_style is not None: - node.flow_style = self.default_flow_style - else: - node.flow_style = best_style - return node - - def ignore_aliases(self, data): - return False - -class SafeRepresenter(BaseRepresenter): - - def ignore_aliases(self, data): - if data is None: - return True - if isinstance(data, tuple) and data == (): - return True - if isinstance(data, (str, bytes, bool, int, float)): - return True - - def represent_none(self, data): - return self.represent_scalar('tag:yaml.org,2002:null', 'null') - - def represent_str(self, data): - return self.represent_scalar('tag:yaml.org,2002:str', data) - - def represent_binary(self, data): - if hasattr(base64, 'encodebytes'): - data = base64.encodebytes(data).decode('ascii') - else: - data = base64.encodestring(data).decode('ascii') - return self.represent_scalar('tag:yaml.org,2002:binary', data, style='|') - - def represent_bool(self, data): - if data: - value = 'true' - else: - value = 'false' - return self.represent_scalar('tag:yaml.org,2002:bool', value) - - def represent_int(self, data): - return self.represent_scalar('tag:yaml.org,2002:int', str(data)) - - inf_value = 1e300 - while repr(inf_value) != repr(inf_value*inf_value): - inf_value *= inf_value - - def represent_float(self, data): - if data != data or (data == 0.0 and data == 1.0): - value = '.nan' - elif data == self.inf_value: - value = '.inf' - elif data == -self.inf_value: - value = '-.inf' - else: - value = repr(data).lower() - # Note that in some cases `repr(data)` represents a float number - # without the decimal parts. For instance: - # >>> repr(1e17) - # '1e17' - # Unfortunately, this is not a valid float representation according - # to the definition of the `!!float` tag. We fix this by adding - # '.0' before the 'e' symbol. - if '.' not in value and 'e' in value: - value = value.replace('e', '.0e', 1) - return self.represent_scalar('tag:yaml.org,2002:float', value) - - def represent_list(self, data): - #pairs = (len(data) > 0 and isinstance(data, list)) - #if pairs: - # for item in data: - # if not isinstance(item, tuple) or len(item) != 2: - # pairs = False - # break - #if not pairs: - return self.represent_sequence('tag:yaml.org,2002:seq', data) - #value = [] - #for item_key, item_value in data: - # value.append(self.represent_mapping(u'tag:yaml.org,2002:map', - # [(item_key, item_value)])) - #return SequenceNode(u'tag:yaml.org,2002:pairs', value) - - def represent_dict(self, data): - return self.represent_mapping('tag:yaml.org,2002:map', data) - - def represent_set(self, data): - value = {} - for key in data: - value[key] = None - return self.represent_mapping('tag:yaml.org,2002:set', value) - - def represent_date(self, data): - value = data.isoformat() - return self.represent_scalar('tag:yaml.org,2002:timestamp', value) - - def represent_datetime(self, data): - value = data.isoformat(' ') - return self.represent_scalar('tag:yaml.org,2002:timestamp', value) - - def represent_yaml_object(self, tag, data, cls, flow_style=None): - if hasattr(data, '__getstate__'): - state = data.__getstate__() - else: - state = data.__dict__.copy() - return self.represent_mapping(tag, state, flow_style=flow_style) - - def represent_undefined(self, data): - raise RepresenterError("cannot represent an object", data) - -SafeRepresenter.add_representer(type(None), - SafeRepresenter.represent_none) - -SafeRepresenter.add_representer(str, - SafeRepresenter.represent_str) - -SafeRepresenter.add_representer(bytes, - SafeRepresenter.represent_binary) - -SafeRepresenter.add_representer(bool, - SafeRepresenter.represent_bool) - -SafeRepresenter.add_representer(int, - SafeRepresenter.represent_int) - -SafeRepresenter.add_representer(float, - SafeRepresenter.represent_float) - -SafeRepresenter.add_representer(list, - SafeRepresenter.represent_list) - -SafeRepresenter.add_representer(tuple, - SafeRepresenter.represent_list) - -SafeRepresenter.add_representer(dict, - SafeRepresenter.represent_dict) - -SafeRepresenter.add_representer(set, - SafeRepresenter.represent_set) - -SafeRepresenter.add_representer(datetime.date, - SafeRepresenter.represent_date) - -SafeRepresenter.add_representer(datetime.datetime, - SafeRepresenter.represent_datetime) - -SafeRepresenter.add_representer(None, - SafeRepresenter.represent_undefined) - -class Representer(SafeRepresenter): - - def represent_complex(self, data): - if data.imag == 0.0: - data = '%r' % data.real - elif data.real == 0.0: - data = '%rj' % data.imag - elif data.imag > 0: - data = '%r+%rj' % (data.real, data.imag) - else: - data = '%r%rj' % (data.real, data.imag) - return self.represent_scalar('tag:yaml.org,2002:python/complex', data) - - def represent_tuple(self, data): - return self.represent_sequence('tag:yaml.org,2002:python/tuple', data) - - def represent_name(self, data): - name = '%s.%s' % (data.__module__, data.__name__) - return self.represent_scalar('tag:yaml.org,2002:python/name:'+name, '') - - def represent_module(self, data): - return self.represent_scalar( - 'tag:yaml.org,2002:python/module:'+data.__name__, '') - - def represent_object(self, data): - # We use __reduce__ API to save the data. data.__reduce__ returns - # a tuple of length 2-5: - # (function, args, state, listitems, dictitems) - - # For reconstructing, we calls function(*args), then set its state, - # listitems, and dictitems if they are not None. - - # A special case is when function.__name__ == '__newobj__'. In this - # case we create the object with args[0].__new__(*args). - - # Another special case is when __reduce__ returns a string - we don't - # support it. - - # We produce a !!python/object, !!python/object/new or - # !!python/object/apply node. - - cls = type(data) - if cls in copyreg.dispatch_table: - reduce = copyreg.dispatch_table[cls](data) - elif hasattr(data, '__reduce_ex__'): - reduce = data.__reduce_ex__(2) - elif hasattr(data, '__reduce__'): - reduce = data.__reduce__() - else: - raise RepresenterError("cannot represent an object", data) - reduce = (list(reduce)+[None]*5)[:5] - function, args, state, listitems, dictitems = reduce - args = list(args) - if state is None: - state = {} - if listitems is not None: - listitems = list(listitems) - if dictitems is not None: - dictitems = dict(dictitems) - if function.__name__ == '__newobj__': - function = args[0] - args = args[1:] - tag = 'tag:yaml.org,2002:python/object/new:' - newobj = True - else: - tag = 'tag:yaml.org,2002:python/object/apply:' - newobj = False - function_name = '%s.%s' % (function.__module__, function.__name__) - if not args and not listitems and not dictitems \ - and isinstance(state, dict) and newobj: - return self.represent_mapping( - 'tag:yaml.org,2002:python/object:'+function_name, state) - if not listitems and not dictitems \ - and isinstance(state, dict) and not state: - return self.represent_sequence(tag+function_name, args) - value = {} - if args: - value['args'] = args - if state or not isinstance(state, dict): - value['state'] = state - if listitems: - value['listitems'] = listitems - if dictitems: - value['dictitems'] = dictitems - return self.represent_mapping(tag+function_name, value) - - def represent_ordered_dict(self, data): - # Provide uniform representation across different Python versions. - data_type = type(data) - tag = 'tag:yaml.org,2002:python/object/apply:%s.%s' \ - % (data_type.__module__, data_type.__name__) - items = [[key, value] for key, value in data.items()] - return self.represent_sequence(tag, [items]) - -Representer.add_representer(complex, - Representer.represent_complex) - -Representer.add_representer(tuple, - Representer.represent_tuple) - -Representer.add_multi_representer(type, - Representer.represent_name) - -Representer.add_representer(collections.OrderedDict, - Representer.represent_ordered_dict) - -Representer.add_representer(types.FunctionType, - Representer.represent_name) - -Representer.add_representer(types.BuiltinFunctionType, - Representer.represent_name) - -Representer.add_representer(types.ModuleType, - Representer.represent_module) - -Representer.add_multi_representer(object, - Representer.represent_object) - diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/resolver.py b/plotter-app/venv/lib/python3.8/site-packages/yaml/resolver.py deleted file mode 100644 index 3522bda..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/yaml/resolver.py +++ /dev/null @@ -1,227 +0,0 @@ - -__all__ = ['BaseResolver', 'Resolver'] - -from .error import * -from .nodes import * - -import re - -class ResolverError(YAMLError): - pass - -class BaseResolver: - - DEFAULT_SCALAR_TAG = 'tag:yaml.org,2002:str' - DEFAULT_SEQUENCE_TAG = 'tag:yaml.org,2002:seq' - DEFAULT_MAPPING_TAG = 'tag:yaml.org,2002:map' - - yaml_implicit_resolvers = {} - yaml_path_resolvers = {} - - def __init__(self): - self.resolver_exact_paths = [] - self.resolver_prefix_paths = [] - - @classmethod - def add_implicit_resolver(cls, tag, regexp, first): - if not 'yaml_implicit_resolvers' in cls.__dict__: - implicit_resolvers = {} - for key in cls.yaml_implicit_resolvers: - implicit_resolvers[key] = cls.yaml_implicit_resolvers[key][:] - cls.yaml_implicit_resolvers = implicit_resolvers - if first is None: - first = [None] - for ch in first: - cls.yaml_implicit_resolvers.setdefault(ch, []).append((tag, regexp)) - - @classmethod - def add_path_resolver(cls, tag, path, kind=None): - # Note: `add_path_resolver` is experimental. The API could be changed. - # `new_path` is a pattern that is matched against the path from the - # root to the node that is being considered. `node_path` elements are - # tuples `(node_check, index_check)`. `node_check` is a node class: - # `ScalarNode`, `SequenceNode`, `MappingNode` or `None`. `None` - # matches any kind of a node. `index_check` could be `None`, a boolean - # value, a string value, or a number. `None` and `False` match against - # any _value_ of sequence and mapping nodes. `True` matches against - # any _key_ of a mapping node. A string `index_check` matches against - # a mapping value that corresponds to a scalar key which content is - # equal to the `index_check` value. An integer `index_check` matches - # against a sequence value with the index equal to `index_check`. - if not 'yaml_path_resolvers' in cls.__dict__: - cls.yaml_path_resolvers = cls.yaml_path_resolvers.copy() - new_path = [] - for element in path: - if isinstance(element, (list, tuple)): - if len(element) == 2: - node_check, index_check = element - elif len(element) == 1: - node_check = element[0] - index_check = True - else: - raise ResolverError("Invalid path element: %s" % element) - else: - node_check = None - index_check = element - if node_check is str: - node_check = ScalarNode - elif node_check is list: - node_check = SequenceNode - elif node_check is dict: - node_check = MappingNode - elif node_check not in [ScalarNode, SequenceNode, MappingNode] \ - and not isinstance(node_check, str) \ - and node_check is not None: - raise ResolverError("Invalid node checker: %s" % node_check) - if not isinstance(index_check, (str, int)) \ - and index_check is not None: - raise ResolverError("Invalid index checker: %s" % index_check) - new_path.append((node_check, index_check)) - if kind is str: - kind = ScalarNode - elif kind is list: - kind = SequenceNode - elif kind is dict: - kind = MappingNode - elif kind not in [ScalarNode, SequenceNode, MappingNode] \ - and kind is not None: - raise ResolverError("Invalid node kind: %s" % kind) - cls.yaml_path_resolvers[tuple(new_path), kind] = tag - - def descend_resolver(self, current_node, current_index): - if not self.yaml_path_resolvers: - return - exact_paths = {} - prefix_paths = [] - if current_node: - depth = len(self.resolver_prefix_paths) - for path, kind in self.resolver_prefix_paths[-1]: - if self.check_resolver_prefix(depth, path, kind, - current_node, current_index): - if len(path) > depth: - prefix_paths.append((path, kind)) - else: - exact_paths[kind] = self.yaml_path_resolvers[path, kind] - else: - for path, kind in self.yaml_path_resolvers: - if not path: - exact_paths[kind] = self.yaml_path_resolvers[path, kind] - else: - prefix_paths.append((path, kind)) - self.resolver_exact_paths.append(exact_paths) - self.resolver_prefix_paths.append(prefix_paths) - - def ascend_resolver(self): - if not self.yaml_path_resolvers: - return - self.resolver_exact_paths.pop() - self.resolver_prefix_paths.pop() - - def check_resolver_prefix(self, depth, path, kind, - current_node, current_index): - node_check, index_check = path[depth-1] - if isinstance(node_check, str): - if current_node.tag != node_check: - return - elif node_check is not None: - if not isinstance(current_node, node_check): - return - if index_check is True and current_index is not None: - return - if (index_check is False or index_check is None) \ - and current_index is None: - return - if isinstance(index_check, str): - if not (isinstance(current_index, ScalarNode) - and index_check == current_index.value): - return - elif isinstance(index_check, int) and not isinstance(index_check, bool): - if index_check != current_index: - return - return True - - def resolve(self, kind, value, implicit): - if kind is ScalarNode and implicit[0]: - if value == '': - resolvers = self.yaml_implicit_resolvers.get('', []) - else: - resolvers = self.yaml_implicit_resolvers.get(value[0], []) - wildcard_resolvers = self.yaml_implicit_resolvers.get(None, []) - for tag, regexp in resolvers + wildcard_resolvers: - if regexp.match(value): - return tag - implicit = implicit[1] - if self.yaml_path_resolvers: - exact_paths = self.resolver_exact_paths[-1] - if kind in exact_paths: - return exact_paths[kind] - if None in exact_paths: - return exact_paths[None] - if kind is ScalarNode: - return self.DEFAULT_SCALAR_TAG - elif kind is SequenceNode: - return self.DEFAULT_SEQUENCE_TAG - elif kind is MappingNode: - return self.DEFAULT_MAPPING_TAG - -class Resolver(BaseResolver): - pass - -Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:bool', - re.compile(r'''^(?:yes|Yes|YES|no|No|NO - |true|True|TRUE|false|False|FALSE - |on|On|ON|off|Off|OFF)$''', re.X), - list('yYnNtTfFoO')) - -Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:float', - re.compile(r'''^(?:[-+]?(?:[0-9][0-9_]*)\.[0-9_]*(?:[eE][-+][0-9]+)? - |\.[0-9][0-9_]*(?:[eE][-+][0-9]+)? - |[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]* - |[-+]?\.(?:inf|Inf|INF) - |\.(?:nan|NaN|NAN))$''', re.X), - list('-+0123456789.')) - -Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:int', - re.compile(r'''^(?:[-+]?0b[0-1_]+ - |[-+]?0[0-7_]+ - |[-+]?(?:0|[1-9][0-9_]*) - |[-+]?0x[0-9a-fA-F_]+ - |[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$''', re.X), - list('-+0123456789')) - -Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:merge', - re.compile(r'^(?:<<)$'), - ['<']) - -Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:null', - re.compile(r'''^(?: ~ - |null|Null|NULL - | )$''', re.X), - ['~', 'n', 'N', '']) - -Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:timestamp', - re.compile(r'''^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] - |[0-9][0-9][0-9][0-9] -[0-9][0-9]? -[0-9][0-9]? - (?:[Tt]|[ \t]+)[0-9][0-9]? - :[0-9][0-9] :[0-9][0-9] (?:\.[0-9]*)? - (?:[ \t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$''', re.X), - list('0123456789')) - -Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:value', - re.compile(r'^(?:=)$'), - ['=']) - -# The following resolver is only for documentation purposes. It cannot work -# because plain scalars cannot start with '!', '&', or '*'. -Resolver.add_implicit_resolver( - 'tag:yaml.org,2002:yaml', - re.compile(r'^(?:!|&|\*)$'), - list('!&*')) - diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/scanner.py b/plotter-app/venv/lib/python3.8/site-packages/yaml/scanner.py deleted file mode 100644 index de925b0..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/yaml/scanner.py +++ /dev/null @@ -1,1435 +0,0 @@ - -# Scanner produces tokens of the following types: -# STREAM-START -# STREAM-END -# DIRECTIVE(name, value) -# DOCUMENT-START -# DOCUMENT-END -# BLOCK-SEQUENCE-START -# BLOCK-MAPPING-START -# BLOCK-END -# FLOW-SEQUENCE-START -# FLOW-MAPPING-START -# FLOW-SEQUENCE-END -# FLOW-MAPPING-END -# BLOCK-ENTRY -# FLOW-ENTRY -# KEY -# VALUE -# ALIAS(value) -# ANCHOR(value) -# TAG(value) -# SCALAR(value, plain, style) -# -# Read comments in the Scanner code for more details. -# - -__all__ = ['Scanner', 'ScannerError'] - -from .error import MarkedYAMLError -from .tokens import * - -class ScannerError(MarkedYAMLError): - pass - -class SimpleKey: - # See below simple keys treatment. - - def __init__(self, token_number, required, index, line, column, mark): - self.token_number = token_number - self.required = required - self.index = index - self.line = line - self.column = column - self.mark = mark - -class Scanner: - - def __init__(self): - """Initialize the scanner.""" - # It is assumed that Scanner and Reader will have a common descendant. - # Reader do the dirty work of checking for BOM and converting the - # input data to Unicode. It also adds NUL to the end. - # - # Reader supports the following methods - # self.peek(i=0) # peek the next i-th character - # self.prefix(l=1) # peek the next l characters - # self.forward(l=1) # read the next l characters and move the pointer. - - # Had we reached the end of the stream? - self.done = False - - # The number of unclosed '{' and '['. `flow_level == 0` means block - # context. - self.flow_level = 0 - - # List of processed tokens that are not yet emitted. - self.tokens = [] - - # Add the STREAM-START token. - self.fetch_stream_start() - - # Number of tokens that were emitted through the `get_token` method. - self.tokens_taken = 0 - - # The current indentation level. - self.indent = -1 - - # Past indentation levels. - self.indents = [] - - # Variables related to simple keys treatment. - - # A simple key is a key that is not denoted by the '?' indicator. - # Example of simple keys: - # --- - # block simple key: value - # ? not a simple key: - # : { flow simple key: value } - # We emit the KEY token before all keys, so when we find a potential - # simple key, we try to locate the corresponding ':' indicator. - # Simple keys should be limited to a single line and 1024 characters. - - # Can a simple key start at the current position? A simple key may - # start: - # - at the beginning of the line, not counting indentation spaces - # (in block context), - # - after '{', '[', ',' (in the flow context), - # - after '?', ':', '-' (in the block context). - # In the block context, this flag also signifies if a block collection - # may start at the current position. - self.allow_simple_key = True - - # Keep track of possible simple keys. This is a dictionary. The key - # is `flow_level`; there can be no more that one possible simple key - # for each level. The value is a SimpleKey record: - # (token_number, required, index, line, column, mark) - # A simple key may start with ALIAS, ANCHOR, TAG, SCALAR(flow), - # '[', or '{' tokens. - self.possible_simple_keys = {} - - # Public methods. - - def check_token(self, *choices): - # Check if the next token is one of the given types. - while self.need_more_tokens(): - self.fetch_more_tokens() - if self.tokens: - if not choices: - return True - for choice in choices: - if isinstance(self.tokens[0], choice): - return True - return False - - def peek_token(self): - # Return the next token, but do not delete if from the queue. - # Return None if no more tokens. - while self.need_more_tokens(): - self.fetch_more_tokens() - if self.tokens: - return self.tokens[0] - else: - return None - - def get_token(self): - # Return the next token. - while self.need_more_tokens(): - self.fetch_more_tokens() - if self.tokens: - self.tokens_taken += 1 - return self.tokens.pop(0) - - # Private methods. - - def need_more_tokens(self): - if self.done: - return False - if not self.tokens: - return True - # The current token may be a potential simple key, so we - # need to look further. - self.stale_possible_simple_keys() - if self.next_possible_simple_key() == self.tokens_taken: - return True - - def fetch_more_tokens(self): - - # Eat whitespaces and comments until we reach the next token. - self.scan_to_next_token() - - # Remove obsolete possible simple keys. - self.stale_possible_simple_keys() - - # Compare the current indentation and column. It may add some tokens - # and decrease the current indentation level. - self.unwind_indent(self.column) - - # Peek the next character. - ch = self.peek() - - # Is it the end of stream? - if ch == '\0': - return self.fetch_stream_end() - - # Is it a directive? - if ch == '%' and self.check_directive(): - return self.fetch_directive() - - # Is it the document start? - if ch == '-' and self.check_document_start(): - return self.fetch_document_start() - - # Is it the document end? - if ch == '.' and self.check_document_end(): - return self.fetch_document_end() - - # TODO: support for BOM within a stream. - #if ch == '\uFEFF': - # return self.fetch_bom() <-- issue BOMToken - - # Note: the order of the following checks is NOT significant. - - # Is it the flow sequence start indicator? - if ch == '[': - return self.fetch_flow_sequence_start() - - # Is it the flow mapping start indicator? - if ch == '{': - return self.fetch_flow_mapping_start() - - # Is it the flow sequence end indicator? - if ch == ']': - return self.fetch_flow_sequence_end() - - # Is it the flow mapping end indicator? - if ch == '}': - return self.fetch_flow_mapping_end() - - # Is it the flow entry indicator? - if ch == ',': - return self.fetch_flow_entry() - - # Is it the block entry indicator? - if ch == '-' and self.check_block_entry(): - return self.fetch_block_entry() - - # Is it the key indicator? - if ch == '?' and self.check_key(): - return self.fetch_key() - - # Is it the value indicator? - if ch == ':' and self.check_value(): - return self.fetch_value() - - # Is it an alias? - if ch == '*': - return self.fetch_alias() - - # Is it an anchor? - if ch == '&': - return self.fetch_anchor() - - # Is it a tag? - if ch == '!': - return self.fetch_tag() - - # Is it a literal scalar? - if ch == '|' and not self.flow_level: - return self.fetch_literal() - - # Is it a folded scalar? - if ch == '>' and not self.flow_level: - return self.fetch_folded() - - # Is it a single quoted scalar? - if ch == '\'': - return self.fetch_single() - - # Is it a double quoted scalar? - if ch == '\"': - return self.fetch_double() - - # It must be a plain scalar then. - if self.check_plain(): - return self.fetch_plain() - - # No? It's an error. Let's produce a nice error message. - raise ScannerError("while scanning for the next token", None, - "found character %r that cannot start any token" % ch, - self.get_mark()) - - # Simple keys treatment. - - def next_possible_simple_key(self): - # Return the number of the nearest possible simple key. Actually we - # don't need to loop through the whole dictionary. We may replace it - # with the following code: - # if not self.possible_simple_keys: - # return None - # return self.possible_simple_keys[ - # min(self.possible_simple_keys.keys())].token_number - min_token_number = None - for level in self.possible_simple_keys: - key = self.possible_simple_keys[level] - if min_token_number is None or key.token_number < min_token_number: - min_token_number = key.token_number - return min_token_number - - def stale_possible_simple_keys(self): - # Remove entries that are no longer possible simple keys. According to - # the YAML specification, simple keys - # - should be limited to a single line, - # - should be no longer than 1024 characters. - # Disabling this procedure will allow simple keys of any length and - # height (may cause problems if indentation is broken though). - for level in list(self.possible_simple_keys): - key = self.possible_simple_keys[level] - if key.line != self.line \ - or self.index-key.index > 1024: - if key.required: - raise ScannerError("while scanning a simple key", key.mark, - "could not find expected ':'", self.get_mark()) - del self.possible_simple_keys[level] - - def save_possible_simple_key(self): - # The next token may start a simple key. We check if it's possible - # and save its position. This function is called for - # ALIAS, ANCHOR, TAG, SCALAR(flow), '[', and '{'. - - # Check if a simple key is required at the current position. - required = not self.flow_level and self.indent == self.column - - # The next token might be a simple key. Let's save it's number and - # position. - if self.allow_simple_key: - self.remove_possible_simple_key() - token_number = self.tokens_taken+len(self.tokens) - key = SimpleKey(token_number, required, - self.index, self.line, self.column, self.get_mark()) - self.possible_simple_keys[self.flow_level] = key - - def remove_possible_simple_key(self): - # Remove the saved possible key position at the current flow level. - if self.flow_level in self.possible_simple_keys: - key = self.possible_simple_keys[self.flow_level] - - if key.required: - raise ScannerError("while scanning a simple key", key.mark, - "could not find expected ':'", self.get_mark()) - - del self.possible_simple_keys[self.flow_level] - - # Indentation functions. - - def unwind_indent(self, column): - - ## In flow context, tokens should respect indentation. - ## Actually the condition should be `self.indent >= column` according to - ## the spec. But this condition will prohibit intuitively correct - ## constructions such as - ## key : { - ## } - #if self.flow_level and self.indent > column: - # raise ScannerError(None, None, - # "invalid indentation or unclosed '[' or '{'", - # self.get_mark()) - - # In the flow context, indentation is ignored. We make the scanner less - # restrictive then specification requires. - if self.flow_level: - return - - # In block context, we may need to issue the BLOCK-END tokens. - while self.indent > column: - mark = self.get_mark() - self.indent = self.indents.pop() - self.tokens.append(BlockEndToken(mark, mark)) - - def add_indent(self, column): - # Check if we need to increase indentation. - if self.indent < column: - self.indents.append(self.indent) - self.indent = column - return True - return False - - # Fetchers. - - def fetch_stream_start(self): - # We always add STREAM-START as the first token and STREAM-END as the - # last token. - - # Read the token. - mark = self.get_mark() - - # Add STREAM-START. - self.tokens.append(StreamStartToken(mark, mark, - encoding=self.encoding)) - - - def fetch_stream_end(self): - - # Set the current indentation to -1. - self.unwind_indent(-1) - - # Reset simple keys. - self.remove_possible_simple_key() - self.allow_simple_key = False - self.possible_simple_keys = {} - - # Read the token. - mark = self.get_mark() - - # Add STREAM-END. - self.tokens.append(StreamEndToken(mark, mark)) - - # The steam is finished. - self.done = True - - def fetch_directive(self): - - # Set the current indentation to -1. - self.unwind_indent(-1) - - # Reset simple keys. - self.remove_possible_simple_key() - self.allow_simple_key = False - - # Scan and add DIRECTIVE. - self.tokens.append(self.scan_directive()) - - def fetch_document_start(self): - self.fetch_document_indicator(DocumentStartToken) - - def fetch_document_end(self): - self.fetch_document_indicator(DocumentEndToken) - - def fetch_document_indicator(self, TokenClass): - - # Set the current indentation to -1. - self.unwind_indent(-1) - - # Reset simple keys. Note that there could not be a block collection - # after '---'. - self.remove_possible_simple_key() - self.allow_simple_key = False - - # Add DOCUMENT-START or DOCUMENT-END. - start_mark = self.get_mark() - self.forward(3) - end_mark = self.get_mark() - self.tokens.append(TokenClass(start_mark, end_mark)) - - def fetch_flow_sequence_start(self): - self.fetch_flow_collection_start(FlowSequenceStartToken) - - def fetch_flow_mapping_start(self): - self.fetch_flow_collection_start(FlowMappingStartToken) - - def fetch_flow_collection_start(self, TokenClass): - - # '[' and '{' may start a simple key. - self.save_possible_simple_key() - - # Increase the flow level. - self.flow_level += 1 - - # Simple keys are allowed after '[' and '{'. - self.allow_simple_key = True - - # Add FLOW-SEQUENCE-START or FLOW-MAPPING-START. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(TokenClass(start_mark, end_mark)) - - def fetch_flow_sequence_end(self): - self.fetch_flow_collection_end(FlowSequenceEndToken) - - def fetch_flow_mapping_end(self): - self.fetch_flow_collection_end(FlowMappingEndToken) - - def fetch_flow_collection_end(self, TokenClass): - - # Reset possible simple key on the current level. - self.remove_possible_simple_key() - - # Decrease the flow level. - self.flow_level -= 1 - - # No simple keys after ']' or '}'. - self.allow_simple_key = False - - # Add FLOW-SEQUENCE-END or FLOW-MAPPING-END. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(TokenClass(start_mark, end_mark)) - - def fetch_flow_entry(self): - - # Simple keys are allowed after ','. - self.allow_simple_key = True - - # Reset possible simple key on the current level. - self.remove_possible_simple_key() - - # Add FLOW-ENTRY. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(FlowEntryToken(start_mark, end_mark)) - - def fetch_block_entry(self): - - # Block context needs additional checks. - if not self.flow_level: - - # Are we allowed to start a new entry? - if not self.allow_simple_key: - raise ScannerError(None, None, - "sequence entries are not allowed here", - self.get_mark()) - - # We may need to add BLOCK-SEQUENCE-START. - if self.add_indent(self.column): - mark = self.get_mark() - self.tokens.append(BlockSequenceStartToken(mark, mark)) - - # It's an error for the block entry to occur in the flow context, - # but we let the parser detect this. - else: - pass - - # Simple keys are allowed after '-'. - self.allow_simple_key = True - - # Reset possible simple key on the current level. - self.remove_possible_simple_key() - - # Add BLOCK-ENTRY. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(BlockEntryToken(start_mark, end_mark)) - - def fetch_key(self): - - # Block context needs additional checks. - if not self.flow_level: - - # Are we allowed to start a key (not necessary a simple)? - if not self.allow_simple_key: - raise ScannerError(None, None, - "mapping keys are not allowed here", - self.get_mark()) - - # We may need to add BLOCK-MAPPING-START. - if self.add_indent(self.column): - mark = self.get_mark() - self.tokens.append(BlockMappingStartToken(mark, mark)) - - # Simple keys are allowed after '?' in the block context. - self.allow_simple_key = not self.flow_level - - # Reset possible simple key on the current level. - self.remove_possible_simple_key() - - # Add KEY. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(KeyToken(start_mark, end_mark)) - - def fetch_value(self): - - # Do we determine a simple key? - if self.flow_level in self.possible_simple_keys: - - # Add KEY. - key = self.possible_simple_keys[self.flow_level] - del self.possible_simple_keys[self.flow_level] - self.tokens.insert(key.token_number-self.tokens_taken, - KeyToken(key.mark, key.mark)) - - # If this key starts a new block mapping, we need to add - # BLOCK-MAPPING-START. - if not self.flow_level: - if self.add_indent(key.column): - self.tokens.insert(key.token_number-self.tokens_taken, - BlockMappingStartToken(key.mark, key.mark)) - - # There cannot be two simple keys one after another. - self.allow_simple_key = False - - # It must be a part of a complex key. - else: - - # Block context needs additional checks. - # (Do we really need them? They will be caught by the parser - # anyway.) - if not self.flow_level: - - # We are allowed to start a complex value if and only if - # we can start a simple key. - if not self.allow_simple_key: - raise ScannerError(None, None, - "mapping values are not allowed here", - self.get_mark()) - - # If this value starts a new block mapping, we need to add - # BLOCK-MAPPING-START. It will be detected as an error later by - # the parser. - if not self.flow_level: - if self.add_indent(self.column): - mark = self.get_mark() - self.tokens.append(BlockMappingStartToken(mark, mark)) - - # Simple keys are allowed after ':' in the block context. - self.allow_simple_key = not self.flow_level - - # Reset possible simple key on the current level. - self.remove_possible_simple_key() - - # Add VALUE. - start_mark = self.get_mark() - self.forward() - end_mark = self.get_mark() - self.tokens.append(ValueToken(start_mark, end_mark)) - - def fetch_alias(self): - - # ALIAS could be a simple key. - self.save_possible_simple_key() - - # No simple keys after ALIAS. - self.allow_simple_key = False - - # Scan and add ALIAS. - self.tokens.append(self.scan_anchor(AliasToken)) - - def fetch_anchor(self): - - # ANCHOR could start a simple key. - self.save_possible_simple_key() - - # No simple keys after ANCHOR. - self.allow_simple_key = False - - # Scan and add ANCHOR. - self.tokens.append(self.scan_anchor(AnchorToken)) - - def fetch_tag(self): - - # TAG could start a simple key. - self.save_possible_simple_key() - - # No simple keys after TAG. - self.allow_simple_key = False - - # Scan and add TAG. - self.tokens.append(self.scan_tag()) - - def fetch_literal(self): - self.fetch_block_scalar(style='|') - - def fetch_folded(self): - self.fetch_block_scalar(style='>') - - def fetch_block_scalar(self, style): - - # A simple key may follow a block scalar. - self.allow_simple_key = True - - # Reset possible simple key on the current level. - self.remove_possible_simple_key() - - # Scan and add SCALAR. - self.tokens.append(self.scan_block_scalar(style)) - - def fetch_single(self): - self.fetch_flow_scalar(style='\'') - - def fetch_double(self): - self.fetch_flow_scalar(style='"') - - def fetch_flow_scalar(self, style): - - # A flow scalar could be a simple key. - self.save_possible_simple_key() - - # No simple keys after flow scalars. - self.allow_simple_key = False - - # Scan and add SCALAR. - self.tokens.append(self.scan_flow_scalar(style)) - - def fetch_plain(self): - - # A plain scalar could be a simple key. - self.save_possible_simple_key() - - # No simple keys after plain scalars. But note that `scan_plain` will - # change this flag if the scan is finished at the beginning of the - # line. - self.allow_simple_key = False - - # Scan and add SCALAR. May change `allow_simple_key`. - self.tokens.append(self.scan_plain()) - - # Checkers. - - def check_directive(self): - - # DIRECTIVE: ^ '%' ... - # The '%' indicator is already checked. - if self.column == 0: - return True - - def check_document_start(self): - - # DOCUMENT-START: ^ '---' (' '|'\n') - if self.column == 0: - if self.prefix(3) == '---' \ - and self.peek(3) in '\0 \t\r\n\x85\u2028\u2029': - return True - - def check_document_end(self): - - # DOCUMENT-END: ^ '...' (' '|'\n') - if self.column == 0: - if self.prefix(3) == '...' \ - and self.peek(3) in '\0 \t\r\n\x85\u2028\u2029': - return True - - def check_block_entry(self): - - # BLOCK-ENTRY: '-' (' '|'\n') - return self.peek(1) in '\0 \t\r\n\x85\u2028\u2029' - - def check_key(self): - - # KEY(flow context): '?' - if self.flow_level: - return True - - # KEY(block context): '?' (' '|'\n') - else: - return self.peek(1) in '\0 \t\r\n\x85\u2028\u2029' - - def check_value(self): - - # VALUE(flow context): ':' - if self.flow_level: - return True - - # VALUE(block context): ':' (' '|'\n') - else: - return self.peek(1) in '\0 \t\r\n\x85\u2028\u2029' - - def check_plain(self): - - # A plain scalar may start with any non-space character except: - # '-', '?', ':', ',', '[', ']', '{', '}', - # '#', '&', '*', '!', '|', '>', '\'', '\"', - # '%', '@', '`'. - # - # It may also start with - # '-', '?', ':' - # if it is followed by a non-space character. - # - # Note that we limit the last rule to the block context (except the - # '-' character) because we want the flow context to be space - # independent. - ch = self.peek() - return ch not in '\0 \t\r\n\x85\u2028\u2029-?:,[]{}#&*!|>\'\"%@`' \ - or (self.peek(1) not in '\0 \t\r\n\x85\u2028\u2029' - and (ch == '-' or (not self.flow_level and ch in '?:'))) - - # Scanners. - - def scan_to_next_token(self): - # We ignore spaces, line breaks and comments. - # If we find a line break in the block context, we set the flag - # `allow_simple_key` on. - # The byte order mark is stripped if it's the first character in the - # stream. We do not yet support BOM inside the stream as the - # specification requires. Any such mark will be considered as a part - # of the document. - # - # TODO: We need to make tab handling rules more sane. A good rule is - # Tabs cannot precede tokens - # BLOCK-SEQUENCE-START, BLOCK-MAPPING-START, BLOCK-END, - # KEY(block), VALUE(block), BLOCK-ENTRY - # So the checking code is - # if : - # self.allow_simple_keys = False - # We also need to add the check for `allow_simple_keys == True` to - # `unwind_indent` before issuing BLOCK-END. - # Scanners for block, flow, and plain scalars need to be modified. - - if self.index == 0 and self.peek() == '\uFEFF': - self.forward() - found = False - while not found: - while self.peek() == ' ': - self.forward() - if self.peek() == '#': - while self.peek() not in '\0\r\n\x85\u2028\u2029': - self.forward() - if self.scan_line_break(): - if not self.flow_level: - self.allow_simple_key = True - else: - found = True - - def scan_directive(self): - # See the specification for details. - start_mark = self.get_mark() - self.forward() - name = self.scan_directive_name(start_mark) - value = None - if name == 'YAML': - value = self.scan_yaml_directive_value(start_mark) - end_mark = self.get_mark() - elif name == 'TAG': - value = self.scan_tag_directive_value(start_mark) - end_mark = self.get_mark() - else: - end_mark = self.get_mark() - while self.peek() not in '\0\r\n\x85\u2028\u2029': - self.forward() - self.scan_directive_ignored_line(start_mark) - return DirectiveToken(name, value, start_mark, end_mark) - - def scan_directive_name(self, start_mark): - # See the specification for details. - length = 0 - ch = self.peek(length) - while '0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ - or ch in '-_': - length += 1 - ch = self.peek(length) - if not length: - raise ScannerError("while scanning a directive", start_mark, - "expected alphabetic or numeric character, but found %r" - % ch, self.get_mark()) - value = self.prefix(length) - self.forward(length) - ch = self.peek() - if ch not in '\0 \r\n\x85\u2028\u2029': - raise ScannerError("while scanning a directive", start_mark, - "expected alphabetic or numeric character, but found %r" - % ch, self.get_mark()) - return value - - def scan_yaml_directive_value(self, start_mark): - # See the specification for details. - while self.peek() == ' ': - self.forward() - major = self.scan_yaml_directive_number(start_mark) - if self.peek() != '.': - raise ScannerError("while scanning a directive", start_mark, - "expected a digit or '.', but found %r" % self.peek(), - self.get_mark()) - self.forward() - minor = self.scan_yaml_directive_number(start_mark) - if self.peek() not in '\0 \r\n\x85\u2028\u2029': - raise ScannerError("while scanning a directive", start_mark, - "expected a digit or ' ', but found %r" % self.peek(), - self.get_mark()) - return (major, minor) - - def scan_yaml_directive_number(self, start_mark): - # See the specification for details. - ch = self.peek() - if not ('0' <= ch <= '9'): - raise ScannerError("while scanning a directive", start_mark, - "expected a digit, but found %r" % ch, self.get_mark()) - length = 0 - while '0' <= self.peek(length) <= '9': - length += 1 - value = int(self.prefix(length)) - self.forward(length) - return value - - def scan_tag_directive_value(self, start_mark): - # See the specification for details. - while self.peek() == ' ': - self.forward() - handle = self.scan_tag_directive_handle(start_mark) - while self.peek() == ' ': - self.forward() - prefix = self.scan_tag_directive_prefix(start_mark) - return (handle, prefix) - - def scan_tag_directive_handle(self, start_mark): - # See the specification for details. - value = self.scan_tag_handle('directive', start_mark) - ch = self.peek() - if ch != ' ': - raise ScannerError("while scanning a directive", start_mark, - "expected ' ', but found %r" % ch, self.get_mark()) - return value - - def scan_tag_directive_prefix(self, start_mark): - # See the specification for details. - value = self.scan_tag_uri('directive', start_mark) - ch = self.peek() - if ch not in '\0 \r\n\x85\u2028\u2029': - raise ScannerError("while scanning a directive", start_mark, - "expected ' ', but found %r" % ch, self.get_mark()) - return value - - def scan_directive_ignored_line(self, start_mark): - # See the specification for details. - while self.peek() == ' ': - self.forward() - if self.peek() == '#': - while self.peek() not in '\0\r\n\x85\u2028\u2029': - self.forward() - ch = self.peek() - if ch not in '\0\r\n\x85\u2028\u2029': - raise ScannerError("while scanning a directive", start_mark, - "expected a comment or a line break, but found %r" - % ch, self.get_mark()) - self.scan_line_break() - - def scan_anchor(self, TokenClass): - # The specification does not restrict characters for anchors and - # aliases. This may lead to problems, for instance, the document: - # [ *alias, value ] - # can be interpreted in two ways, as - # [ "value" ] - # and - # [ *alias , "value" ] - # Therefore we restrict aliases to numbers and ASCII letters. - start_mark = self.get_mark() - indicator = self.peek() - if indicator == '*': - name = 'alias' - else: - name = 'anchor' - self.forward() - length = 0 - ch = self.peek(length) - while '0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ - or ch in '-_': - length += 1 - ch = self.peek(length) - if not length: - raise ScannerError("while scanning an %s" % name, start_mark, - "expected alphabetic or numeric character, but found %r" - % ch, self.get_mark()) - value = self.prefix(length) - self.forward(length) - ch = self.peek() - if ch not in '\0 \t\r\n\x85\u2028\u2029?:,]}%@`': - raise ScannerError("while scanning an %s" % name, start_mark, - "expected alphabetic or numeric character, but found %r" - % ch, self.get_mark()) - end_mark = self.get_mark() - return TokenClass(value, start_mark, end_mark) - - def scan_tag(self): - # See the specification for details. - start_mark = self.get_mark() - ch = self.peek(1) - if ch == '<': - handle = None - self.forward(2) - suffix = self.scan_tag_uri('tag', start_mark) - if self.peek() != '>': - raise ScannerError("while parsing a tag", start_mark, - "expected '>', but found %r" % self.peek(), - self.get_mark()) - self.forward() - elif ch in '\0 \t\r\n\x85\u2028\u2029': - handle = None - suffix = '!' - self.forward() - else: - length = 1 - use_handle = False - while ch not in '\0 \r\n\x85\u2028\u2029': - if ch == '!': - use_handle = True - break - length += 1 - ch = self.peek(length) - handle = '!' - if use_handle: - handle = self.scan_tag_handle('tag', start_mark) - else: - handle = '!' - self.forward() - suffix = self.scan_tag_uri('tag', start_mark) - ch = self.peek() - if ch not in '\0 \r\n\x85\u2028\u2029': - raise ScannerError("while scanning a tag", start_mark, - "expected ' ', but found %r" % ch, self.get_mark()) - value = (handle, suffix) - end_mark = self.get_mark() - return TagToken(value, start_mark, end_mark) - - def scan_block_scalar(self, style): - # See the specification for details. - - if style == '>': - folded = True - else: - folded = False - - chunks = [] - start_mark = self.get_mark() - - # Scan the header. - self.forward() - chomping, increment = self.scan_block_scalar_indicators(start_mark) - self.scan_block_scalar_ignored_line(start_mark) - - # Determine the indentation level and go to the first non-empty line. - min_indent = self.indent+1 - if min_indent < 1: - min_indent = 1 - if increment is None: - breaks, max_indent, end_mark = self.scan_block_scalar_indentation() - indent = max(min_indent, max_indent) - else: - indent = min_indent+increment-1 - breaks, end_mark = self.scan_block_scalar_breaks(indent) - line_break = '' - - # Scan the inner part of the block scalar. - while self.column == indent and self.peek() != '\0': - chunks.extend(breaks) - leading_non_space = self.peek() not in ' \t' - length = 0 - while self.peek(length) not in '\0\r\n\x85\u2028\u2029': - length += 1 - chunks.append(self.prefix(length)) - self.forward(length) - line_break = self.scan_line_break() - breaks, end_mark = self.scan_block_scalar_breaks(indent) - if self.column == indent and self.peek() != '\0': - - # Unfortunately, folding rules are ambiguous. - # - # This is the folding according to the specification: - - if folded and line_break == '\n' \ - and leading_non_space and self.peek() not in ' \t': - if not breaks: - chunks.append(' ') - else: - chunks.append(line_break) - - # This is Clark Evans's interpretation (also in the spec - # examples): - # - #if folded and line_break == '\n': - # if not breaks: - # if self.peek() not in ' \t': - # chunks.append(' ') - # else: - # chunks.append(line_break) - #else: - # chunks.append(line_break) - else: - break - - # Chomp the tail. - if chomping is not False: - chunks.append(line_break) - if chomping is True: - chunks.extend(breaks) - - # We are done. - return ScalarToken(''.join(chunks), False, start_mark, end_mark, - style) - - def scan_block_scalar_indicators(self, start_mark): - # See the specification for details. - chomping = None - increment = None - ch = self.peek() - if ch in '+-': - if ch == '+': - chomping = True - else: - chomping = False - self.forward() - ch = self.peek() - if ch in '0123456789': - increment = int(ch) - if increment == 0: - raise ScannerError("while scanning a block scalar", start_mark, - "expected indentation indicator in the range 1-9, but found 0", - self.get_mark()) - self.forward() - elif ch in '0123456789': - increment = int(ch) - if increment == 0: - raise ScannerError("while scanning a block scalar", start_mark, - "expected indentation indicator in the range 1-9, but found 0", - self.get_mark()) - self.forward() - ch = self.peek() - if ch in '+-': - if ch == '+': - chomping = True - else: - chomping = False - self.forward() - ch = self.peek() - if ch not in '\0 \r\n\x85\u2028\u2029': - raise ScannerError("while scanning a block scalar", start_mark, - "expected chomping or indentation indicators, but found %r" - % ch, self.get_mark()) - return chomping, increment - - def scan_block_scalar_ignored_line(self, start_mark): - # See the specification for details. - while self.peek() == ' ': - self.forward() - if self.peek() == '#': - while self.peek() not in '\0\r\n\x85\u2028\u2029': - self.forward() - ch = self.peek() - if ch not in '\0\r\n\x85\u2028\u2029': - raise ScannerError("while scanning a block scalar", start_mark, - "expected a comment or a line break, but found %r" % ch, - self.get_mark()) - self.scan_line_break() - - def scan_block_scalar_indentation(self): - # See the specification for details. - chunks = [] - max_indent = 0 - end_mark = self.get_mark() - while self.peek() in ' \r\n\x85\u2028\u2029': - if self.peek() != ' ': - chunks.append(self.scan_line_break()) - end_mark = self.get_mark() - else: - self.forward() - if self.column > max_indent: - max_indent = self.column - return chunks, max_indent, end_mark - - def scan_block_scalar_breaks(self, indent): - # See the specification for details. - chunks = [] - end_mark = self.get_mark() - while self.column < indent and self.peek() == ' ': - self.forward() - while self.peek() in '\r\n\x85\u2028\u2029': - chunks.append(self.scan_line_break()) - end_mark = self.get_mark() - while self.column < indent and self.peek() == ' ': - self.forward() - return chunks, end_mark - - def scan_flow_scalar(self, style): - # See the specification for details. - # Note that we loose indentation rules for quoted scalars. Quoted - # scalars don't need to adhere indentation because " and ' clearly - # mark the beginning and the end of them. Therefore we are less - # restrictive then the specification requires. We only need to check - # that document separators are not included in scalars. - if style == '"': - double = True - else: - double = False - chunks = [] - start_mark = self.get_mark() - quote = self.peek() - self.forward() - chunks.extend(self.scan_flow_scalar_non_spaces(double, start_mark)) - while self.peek() != quote: - chunks.extend(self.scan_flow_scalar_spaces(double, start_mark)) - chunks.extend(self.scan_flow_scalar_non_spaces(double, start_mark)) - self.forward() - end_mark = self.get_mark() - return ScalarToken(''.join(chunks), False, start_mark, end_mark, - style) - - ESCAPE_REPLACEMENTS = { - '0': '\0', - 'a': '\x07', - 'b': '\x08', - 't': '\x09', - '\t': '\x09', - 'n': '\x0A', - 'v': '\x0B', - 'f': '\x0C', - 'r': '\x0D', - 'e': '\x1B', - ' ': '\x20', - '\"': '\"', - '\\': '\\', - '/': '/', - 'N': '\x85', - '_': '\xA0', - 'L': '\u2028', - 'P': '\u2029', - } - - ESCAPE_CODES = { - 'x': 2, - 'u': 4, - 'U': 8, - } - - def scan_flow_scalar_non_spaces(self, double, start_mark): - # See the specification for details. - chunks = [] - while True: - length = 0 - while self.peek(length) not in '\'\"\\\0 \t\r\n\x85\u2028\u2029': - length += 1 - if length: - chunks.append(self.prefix(length)) - self.forward(length) - ch = self.peek() - if not double and ch == '\'' and self.peek(1) == '\'': - chunks.append('\'') - self.forward(2) - elif (double and ch == '\'') or (not double and ch in '\"\\'): - chunks.append(ch) - self.forward() - elif double and ch == '\\': - self.forward() - ch = self.peek() - if ch in self.ESCAPE_REPLACEMENTS: - chunks.append(self.ESCAPE_REPLACEMENTS[ch]) - self.forward() - elif ch in self.ESCAPE_CODES: - length = self.ESCAPE_CODES[ch] - self.forward() - for k in range(length): - if self.peek(k) not in '0123456789ABCDEFabcdef': - raise ScannerError("while scanning a double-quoted scalar", start_mark, - "expected escape sequence of %d hexadecimal numbers, but found %r" % - (length, self.peek(k)), self.get_mark()) - code = int(self.prefix(length), 16) - chunks.append(chr(code)) - self.forward(length) - elif ch in '\r\n\x85\u2028\u2029': - self.scan_line_break() - chunks.extend(self.scan_flow_scalar_breaks(double, start_mark)) - else: - raise ScannerError("while scanning a double-quoted scalar", start_mark, - "found unknown escape character %r" % ch, self.get_mark()) - else: - return chunks - - def scan_flow_scalar_spaces(self, double, start_mark): - # See the specification for details. - chunks = [] - length = 0 - while self.peek(length) in ' \t': - length += 1 - whitespaces = self.prefix(length) - self.forward(length) - ch = self.peek() - if ch == '\0': - raise ScannerError("while scanning a quoted scalar", start_mark, - "found unexpected end of stream", self.get_mark()) - elif ch in '\r\n\x85\u2028\u2029': - line_break = self.scan_line_break() - breaks = self.scan_flow_scalar_breaks(double, start_mark) - if line_break != '\n': - chunks.append(line_break) - elif not breaks: - chunks.append(' ') - chunks.extend(breaks) - else: - chunks.append(whitespaces) - return chunks - - def scan_flow_scalar_breaks(self, double, start_mark): - # See the specification for details. - chunks = [] - while True: - # Instead of checking indentation, we check for document - # separators. - prefix = self.prefix(3) - if (prefix == '---' or prefix == '...') \ - and self.peek(3) in '\0 \t\r\n\x85\u2028\u2029': - raise ScannerError("while scanning a quoted scalar", start_mark, - "found unexpected document separator", self.get_mark()) - while self.peek() in ' \t': - self.forward() - if self.peek() in '\r\n\x85\u2028\u2029': - chunks.append(self.scan_line_break()) - else: - return chunks - - def scan_plain(self): - # See the specification for details. - # We add an additional restriction for the flow context: - # plain scalars in the flow context cannot contain ',' or '?'. - # We also keep track of the `allow_simple_key` flag here. - # Indentation rules are loosed for the flow context. - chunks = [] - start_mark = self.get_mark() - end_mark = start_mark - indent = self.indent+1 - # We allow zero indentation for scalars, but then we need to check for - # document separators at the beginning of the line. - #if indent == 0: - # indent = 1 - spaces = [] - while True: - length = 0 - if self.peek() == '#': - break - while True: - ch = self.peek(length) - if ch in '\0 \t\r\n\x85\u2028\u2029' \ - or (ch == ':' and - self.peek(length+1) in '\0 \t\r\n\x85\u2028\u2029' - + (u',[]{}' if self.flow_level else u''))\ - or (self.flow_level and ch in ',?[]{}'): - break - length += 1 - if length == 0: - break - self.allow_simple_key = False - chunks.extend(spaces) - chunks.append(self.prefix(length)) - self.forward(length) - end_mark = self.get_mark() - spaces = self.scan_plain_spaces(indent, start_mark) - if not spaces or self.peek() == '#' \ - or (not self.flow_level and self.column < indent): - break - return ScalarToken(''.join(chunks), True, start_mark, end_mark) - - def scan_plain_spaces(self, indent, start_mark): - # See the specification for details. - # The specification is really confusing about tabs in plain scalars. - # We just forbid them completely. Do not use tabs in YAML! - chunks = [] - length = 0 - while self.peek(length) in ' ': - length += 1 - whitespaces = self.prefix(length) - self.forward(length) - ch = self.peek() - if ch in '\r\n\x85\u2028\u2029': - line_break = self.scan_line_break() - self.allow_simple_key = True - prefix = self.prefix(3) - if (prefix == '---' or prefix == '...') \ - and self.peek(3) in '\0 \t\r\n\x85\u2028\u2029': - return - breaks = [] - while self.peek() in ' \r\n\x85\u2028\u2029': - if self.peek() == ' ': - self.forward() - else: - breaks.append(self.scan_line_break()) - prefix = self.prefix(3) - if (prefix == '---' or prefix == '...') \ - and self.peek(3) in '\0 \t\r\n\x85\u2028\u2029': - return - if line_break != '\n': - chunks.append(line_break) - elif not breaks: - chunks.append(' ') - chunks.extend(breaks) - elif whitespaces: - chunks.append(whitespaces) - return chunks - - def scan_tag_handle(self, name, start_mark): - # See the specification for details. - # For some strange reasons, the specification does not allow '_' in - # tag handles. I have allowed it anyway. - ch = self.peek() - if ch != '!': - raise ScannerError("while scanning a %s" % name, start_mark, - "expected '!', but found %r" % ch, self.get_mark()) - length = 1 - ch = self.peek(length) - if ch != ' ': - while '0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ - or ch in '-_': - length += 1 - ch = self.peek(length) - if ch != '!': - self.forward(length) - raise ScannerError("while scanning a %s" % name, start_mark, - "expected '!', but found %r" % ch, self.get_mark()) - length += 1 - value = self.prefix(length) - self.forward(length) - return value - - def scan_tag_uri(self, name, start_mark): - # See the specification for details. - # Note: we do not check if URI is well-formed. - chunks = [] - length = 0 - ch = self.peek(length) - while '0' <= ch <= '9' or 'A' <= ch <= 'Z' or 'a' <= ch <= 'z' \ - or ch in '-;/?:@&=+$,_.!~*\'()[]%': - if ch == '%': - chunks.append(self.prefix(length)) - self.forward(length) - length = 0 - chunks.append(self.scan_uri_escapes(name, start_mark)) - else: - length += 1 - ch = self.peek(length) - if length: - chunks.append(self.prefix(length)) - self.forward(length) - length = 0 - if not chunks: - raise ScannerError("while parsing a %s" % name, start_mark, - "expected URI, but found %r" % ch, self.get_mark()) - return ''.join(chunks) - - def scan_uri_escapes(self, name, start_mark): - # See the specification for details. - codes = [] - mark = self.get_mark() - while self.peek() == '%': - self.forward() - for k in range(2): - if self.peek(k) not in '0123456789ABCDEFabcdef': - raise ScannerError("while scanning a %s" % name, start_mark, - "expected URI escape sequence of 2 hexadecimal numbers, but found %r" - % self.peek(k), self.get_mark()) - codes.append(int(self.prefix(2), 16)) - self.forward(2) - try: - value = bytes(codes).decode('utf-8') - except UnicodeDecodeError as exc: - raise ScannerError("while scanning a %s" % name, start_mark, str(exc), mark) - return value - - def scan_line_break(self): - # Transforms: - # '\r\n' : '\n' - # '\r' : '\n' - # '\n' : '\n' - # '\x85' : '\n' - # '\u2028' : '\u2028' - # '\u2029 : '\u2029' - # default : '' - ch = self.peek() - if ch in '\r\n\x85': - if self.prefix(2) == '\r\n': - self.forward(2) - else: - self.forward() - return '\n' - elif ch in '\u2028\u2029': - self.forward() - return ch - return '' diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/serializer.py b/plotter-app/venv/lib/python3.8/site-packages/yaml/serializer.py deleted file mode 100644 index fe911e6..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/yaml/serializer.py +++ /dev/null @@ -1,111 +0,0 @@ - -__all__ = ['Serializer', 'SerializerError'] - -from .error import YAMLError -from .events import * -from .nodes import * - -class SerializerError(YAMLError): - pass - -class Serializer: - - ANCHOR_TEMPLATE = 'id%03d' - - def __init__(self, encoding=None, - explicit_start=None, explicit_end=None, version=None, tags=None): - self.use_encoding = encoding - self.use_explicit_start = explicit_start - self.use_explicit_end = explicit_end - self.use_version = version - self.use_tags = tags - self.serialized_nodes = {} - self.anchors = {} - self.last_anchor_id = 0 - self.closed = None - - def open(self): - if self.closed is None: - self.emit(StreamStartEvent(encoding=self.use_encoding)) - self.closed = False - elif self.closed: - raise SerializerError("serializer is closed") - else: - raise SerializerError("serializer is already opened") - - def close(self): - if self.closed is None: - raise SerializerError("serializer is not opened") - elif not self.closed: - self.emit(StreamEndEvent()) - self.closed = True - - #def __del__(self): - # self.close() - - def serialize(self, node): - if self.closed is None: - raise SerializerError("serializer is not opened") - elif self.closed: - raise SerializerError("serializer is closed") - self.emit(DocumentStartEvent(explicit=self.use_explicit_start, - version=self.use_version, tags=self.use_tags)) - self.anchor_node(node) - self.serialize_node(node, None, None) - self.emit(DocumentEndEvent(explicit=self.use_explicit_end)) - self.serialized_nodes = {} - self.anchors = {} - self.last_anchor_id = 0 - - def anchor_node(self, node): - if node in self.anchors: - if self.anchors[node] is None: - self.anchors[node] = self.generate_anchor(node) - else: - self.anchors[node] = None - if isinstance(node, SequenceNode): - for item in node.value: - self.anchor_node(item) - elif isinstance(node, MappingNode): - for key, value in node.value: - self.anchor_node(key) - self.anchor_node(value) - - def generate_anchor(self, node): - self.last_anchor_id += 1 - return self.ANCHOR_TEMPLATE % self.last_anchor_id - - def serialize_node(self, node, parent, index): - alias = self.anchors[node] - if node in self.serialized_nodes: - self.emit(AliasEvent(alias)) - else: - self.serialized_nodes[node] = True - self.descend_resolver(parent, index) - if isinstance(node, ScalarNode): - detected_tag = self.resolve(ScalarNode, node.value, (True, False)) - default_tag = self.resolve(ScalarNode, node.value, (False, True)) - implicit = (node.tag == detected_tag), (node.tag == default_tag) - self.emit(ScalarEvent(alias, node.tag, implicit, node.value, - style=node.style)) - elif isinstance(node, SequenceNode): - implicit = (node.tag - == self.resolve(SequenceNode, node.value, True)) - self.emit(SequenceStartEvent(alias, node.tag, implicit, - flow_style=node.flow_style)) - index = 0 - for item in node.value: - self.serialize_node(item, node, index) - index += 1 - self.emit(SequenceEndEvent()) - elif isinstance(node, MappingNode): - implicit = (node.tag - == self.resolve(MappingNode, node.value, True)) - self.emit(MappingStartEvent(alias, node.tag, implicit, - flow_style=node.flow_style)) - for key, value in node.value: - self.serialize_node(key, node, None) - self.serialize_node(value, node, key) - self.emit(MappingEndEvent()) - self.ascend_resolver() - diff --git a/plotter-app/venv/lib/python3.8/site-packages/yaml/tokens.py b/plotter-app/venv/lib/python3.8/site-packages/yaml/tokens.py deleted file mode 100644 index 4d0b48a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/yaml/tokens.py +++ /dev/null @@ -1,104 +0,0 @@ - -class Token(object): - def __init__(self, start_mark, end_mark): - self.start_mark = start_mark - self.end_mark = end_mark - def __repr__(self): - attributes = [key for key in self.__dict__ - if not key.endswith('_mark')] - attributes.sort() - arguments = ', '.join(['%s=%r' % (key, getattr(self, key)) - for key in attributes]) - return '%s(%s)' % (self.__class__.__name__, arguments) - -#class BOMToken(Token): -# id = '' - -class DirectiveToken(Token): - id = '' - def __init__(self, name, value, start_mark, end_mark): - self.name = name - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - -class DocumentStartToken(Token): - id = '' - -class DocumentEndToken(Token): - id = '' - -class StreamStartToken(Token): - id = '' - def __init__(self, start_mark=None, end_mark=None, - encoding=None): - self.start_mark = start_mark - self.end_mark = end_mark - self.encoding = encoding - -class StreamEndToken(Token): - id = '' - -class BlockSequenceStartToken(Token): - id = '' - -class BlockMappingStartToken(Token): - id = '' - -class BlockEndToken(Token): - id = '' - -class FlowSequenceStartToken(Token): - id = '[' - -class FlowMappingStartToken(Token): - id = '{' - -class FlowSequenceEndToken(Token): - id = ']' - -class FlowMappingEndToken(Token): - id = '}' - -class KeyToken(Token): - id = '?' - -class ValueToken(Token): - id = ':' - -class BlockEntryToken(Token): - id = '-' - -class FlowEntryToken(Token): - id = ',' - -class AliasToken(Token): - id = '' - def __init__(self, value, start_mark, end_mark): - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - -class AnchorToken(Token): - id = '' - def __init__(self, value, start_mark, end_mark): - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - -class TagToken(Token): - id = '' - def __init__(self, value, start_mark, end_mark): - self.value = value - self.start_mark = start_mark - self.end_mark = end_mark - -class ScalarToken(Token): - id = '' - def __init__(self, value, plain, start_mark, end_mark, style=None): - self.value = value - self.plain = plain - self.start_mark = start_mark - self.end_mark = end_mark - self.style = style - diff --git a/plotter-app/venv/lib/python3.8/site-packages/zipp-3.20.2.dist-info/INSTALLER b/plotter-app/venv/lib/python3.8/site-packages/zipp-3.20.2.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zipp-3.20.2.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/plotter-app/venv/lib/python3.8/site-packages/zipp-3.20.2.dist-info/LICENSE b/plotter-app/venv/lib/python3.8/site-packages/zipp-3.20.2.dist-info/LICENSE deleted file mode 100644 index 1bb5a44..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zipp-3.20.2.dist-info/LICENSE +++ /dev/null @@ -1,17 +0,0 @@ -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/plotter-app/venv/lib/python3.8/site-packages/zipp-3.20.2.dist-info/METADATA b/plotter-app/venv/lib/python3.8/site-packages/zipp-3.20.2.dist-info/METADATA deleted file mode 100644 index cf6be83..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zipp-3.20.2.dist-info/METADATA +++ /dev/null @@ -1,106 +0,0 @@ -Metadata-Version: 2.1 -Name: zipp -Version: 3.20.2 -Summary: Backport of pathlib-compatible object wrapper for zip files -Author-email: "Jason R. Coombs" -Project-URL: Source, https://github.com/jaraco/zipp -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3 :: Only -Requires-Python: >=3.8 -Description-Content-Type: text/x-rst -License-File: LICENSE -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: 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: enabler -Requires-Dist: pytest-enabler >=2.2 ; extra == 'enabler' -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' -Requires-Dist: importlib-resources ; (python_version < "3.9") and extra == 'test' -Provides-Extra: type -Requires-Dist: pytest-mypy ; extra == 'type' - -.. 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/charliermarsh/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-2024-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/plotter-app/venv/lib/python3.8/site-packages/zipp-3.20.2.dist-info/RECORD b/plotter-app/venv/lib/python3.8/site-packages/zipp-3.20.2.dist-info/RECORD deleted file mode 100644 index 5af473f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zipp-3.20.2.dist-info/RECORD +++ /dev/null @@ -1,16 +0,0 @@ -zipp-3.20.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -zipp-3.20.2.dist-info/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023 -zipp-3.20.2.dist-info/METADATA,sha256=BY9j659m6ayOwffzL5nKNwcZW5S5Ms2LVHQJ4qspY1g,3682 -zipp-3.20.2.dist-info/RECORD,, -zipp-3.20.2.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91 -zipp-3.20.2.dist-info/top_level.txt,sha256=iAbdoSHfaGqBfVb2XuR9JqSQHCoOsOtG6y9C_LSpqFw,5 -zipp/__init__.py,sha256=WFZd5W532NrC0tyPAEqzjk2xifPH7BePG0L2PPihoM0,11831 -zipp/__pycache__/__init__.cpython-38.pyc,, -zipp/__pycache__/glob.cpython-38.pyc,, -zipp/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -zipp/compat/__pycache__/__init__.cpython-38.pyc,, -zipp/compat/__pycache__/overlay.cpython-38.pyc,, -zipp/compat/__pycache__/py310.cpython-38.pyc,, -zipp/compat/overlay.py,sha256=oEIGAnbr8yGjuKTrVSO2ByewPui71uppbX18BLnYTKE,783 -zipp/compat/py310.py,sha256=KS3sidGTSkoGh3biXiCqRzE6RMEGH0sbRQBevWU73dU,256 -zipp/glob.py,sha256=yPjGfHwcJxUn0fld7I-K-ZQSfTaJBBoimCIygU1SZQw,3315 diff --git a/plotter-app/venv/lib/python3.8/site-packages/zipp-3.20.2.dist-info/WHEEL b/plotter-app/venv/lib/python3.8/site-packages/zipp-3.20.2.dist-info/WHEEL deleted file mode 100644 index 0fde4dd..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zipp-3.20.2.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (74.1.2) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/plotter-app/venv/lib/python3.8/site-packages/zipp-3.20.2.dist-info/top_level.txt b/plotter-app/venv/lib/python3.8/site-packages/zipp-3.20.2.dist-info/top_level.txt deleted file mode 100644 index e82f676..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zipp-3.20.2.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -zipp diff --git a/plotter-app/venv/lib/python3.8/site-packages/zipp/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/zipp/__init__.py deleted file mode 100644 index 161a4fb..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zipp/__init__.py +++ /dev/null @@ -1,452 +0,0 @@ -""" -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 io -import posixpath -import zipfile -import itertools -import contextlib -import pathlib -import re -import stat -import sys - -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. - """ - - def __init__(self, *args, **kwargs): - self.__args = args - self.__kwargs = kwargs - super().__init__(*args, **kwargs) - - def __getstate__(self): - return self.__args, self.__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): - with contextlib.suppress(AttributeError): - return self.__names - self.__names = super().namelist() - return self.__names - - def _name_set(self): - with contextlib.suppress(AttributeError): - return self.__lookup - self.__lookup = super()._name_set() - return self.__lookup - - -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' - stack_level = 3 + is_pypy - 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 not self.exists() and zip_mode == 'r': - 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 or self.root.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/plotter-app/venv/lib/python3.8/site-packages/zipp/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zipp/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 296bae8..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zipp/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zipp/__pycache__/glob.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zipp/__pycache__/glob.cpython-38.pyc deleted file mode 100644 index 15e2c0e..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zipp/__pycache__/glob.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zipp/compat/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/zipp/compat/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/plotter-app/venv/lib/python3.8/site-packages/zipp/compat/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zipp/compat/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 3da4878..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zipp/compat/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zipp/compat/__pycache__/overlay.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zipp/compat/__pycache__/overlay.cpython-38.pyc deleted file mode 100644 index df33964..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zipp/compat/__pycache__/overlay.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zipp/compat/__pycache__/py310.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zipp/compat/__pycache__/py310.cpython-38.pyc deleted file mode 100644 index 5ea1414..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zipp/compat/__pycache__/py310.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zipp/compat/overlay.py b/plotter-app/venv/lib/python3.8/site-packages/zipp/compat/overlay.py deleted file mode 100644 index 5a97ee7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zipp/compat/overlay.py +++ /dev/null @@ -1,37 +0,0 @@ -""" -Expose zipp.Path as .zipfile.Path. - -Includes everything else in ``zipfile`` to match future usage. Just -use: - ->>> from zipp.compat.overlay import zipfile - -in place of ``import zipfile``. - -Relative imports are supported too. - ->>> from zipp.compat.overlay.zipfile import ZipInfo - -The ``zipfile`` object added to ``sys.modules`` needs to be -hashable (#126). - ->>> _ = hash(sys.modules['zipp.compat.overlay.zipfile']) -""" - -import importlib -import sys -import types - -import zipp - - -class HashableNamespace(types.SimpleNamespace): - def __hash__(self): - return hash(tuple(vars(self))) - - -zipfile = HashableNamespace(**vars(importlib.import_module('zipfile'))) -zipfile.Path = zipp.Path -zipfile._path = zipp - -sys.modules[__name__ + '.zipfile'] = zipfile # type: ignore[assignment] diff --git a/plotter-app/venv/lib/python3.8/site-packages/zipp/compat/py310.py b/plotter-app/venv/lib/python3.8/site-packages/zipp/compat/py310.py deleted file mode 100644 index 8264a48..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zipp/compat/py310.py +++ /dev/null @@ -1,13 +0,0 @@ -import sys -import io - - -def _text_encoding(encoding, stacklevel=2, /): # pragma: no cover - return encoding - - -text_encoding = ( - io.text_encoding # type: ignore[unused-ignore, attr-defined] - if sys.version_info > (3, 10) - else _text_encoding -) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zipp/glob.py b/plotter-app/venv/lib/python3.8/site-packages/zipp/glob.py deleted file mode 100644 index 4320f1c..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zipp/glob.py +++ /dev/null @@ -1,114 +0,0 @@ -import os -import re - - -_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))) - - 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/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0-py3.11-nspkg.pth b/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0-py3.11-nspkg.pth deleted file mode 100644 index 4fa827e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0-py3.11-nspkg.pth +++ /dev/null @@ -1 +0,0 @@ -import sys, types, os;has_mfs = sys.version_info > (3, 5);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('zope',));importlib = has_mfs and __import__('importlib.util');has_mfs and __import__('importlib.machinery');m = has_mfs and sys.modules.setdefault('zope', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('zope', [os.path.dirname(p)])));m = m or sys.modules.setdefault('zope', types.ModuleType('zope'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/INSTALLER b/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/LICENSE.txt b/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/LICENSE.txt deleted file mode 100644 index e1f9ad7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/LICENSE.txt +++ /dev/null @@ -1,44 +0,0 @@ -Zope Public License (ZPL) Version 2.1 - -A copyright notice accompanies this license document that identifies the -copyright holders. - -This license has been certified as open source. It has also been designated as -GPL compatible by the Free Software Foundation (FSF). - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions in source code must retain the accompanying copyright -notice, this list of conditions, and the following disclaimer. - -2. Redistributions in binary form must reproduce the accompanying copyright -notice, this list of conditions, and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -3. Names of the copyright holders must not be used to endorse or promote -products derived from this software without prior written permission from the -copyright holders. - -4. The right to distribute this software or to use it for any purpose does not -give you the right to use Servicemarks (sm) or Trademarks (tm) of the -copyright -holders. Use of them is covered by separate agreement with the copyright -holders. - -5. If any files are modified, you must cause the modified files to carry -prominent notices stating that you changed the files and the date of any -change. - -Disclaimer - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED -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 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/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/METADATA b/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/METADATA deleted file mode 100644 index ad93559..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/METADATA +++ /dev/null @@ -1,186 +0,0 @@ -Metadata-Version: 2.1 -Name: zope.event -Version: 5.0 -Summary: Very basic event publishing system -Home-page: https://github.com/zopefoundation/zope.event -Author: Zope Foundation and Contributors -Author-email: zope-dev@zope.dev -License: ZPL-2.1 -Keywords: event framework dispatch subscribe publish -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: Zope Public License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: Jython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Framework :: Zope :: 3 -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Requires-Python: >=3.7 -License-File: LICENSE.txt -Requires-Dist: setuptools -Provides-Extra: docs -Requires-Dist: Sphinx ; extra == 'docs' -Provides-Extra: test -Requires-Dist: zope.testrunner ; extra == 'test' - -======================= - ``zope.event`` README -======================= - -.. image:: https://img.shields.io/pypi/v/zope.event.svg - :target: https://pypi.python.org/pypi/zope.event/ - :alt: Latest Version - -.. image:: https://github.com/zopefoundation/zope.event/actions/workflows/tests.yml/badge.svg - :target: https://github.com/zopefoundation/zope.event/actions/workflows/tests.yml - -.. image:: https://readthedocs.org/projects/zopeevent/badge/?version=latest - :target: http://zopeevent.readthedocs.org/en/latest/ - :alt: Documentation Status - -The ``zope.event`` package provides a simple event system, including: - -- An event publishing API, intended for use by applications which are - unaware of any subscribers to their events. - -- A very simple synchronous event-dispatching system, on which more sophisticated - event dispatching systems can be built. For example, a type-based - event dispatching system that builds on ``zope.event`` can be found in - ``zope.component``. - -Please see http://zopeevent.readthedocs.io/ for the documentation. - -========================== - ``zope.event`` Changelog -========================== - -5.0 (2023-06-23) -================ - -- Drop support for Python 2.7, 3.5, 3.6. - - -4.6 (2022-12-15) -================ - -- Port documentation to Python 3. - -- Add support for Python 3.10, 3.11. - - -4.5.0 (2020-09-18) -================== - -- Add support for Python 3.8 and 3.9. - -- Remove support for Python 3.4. - - -4.4 (2018-10-05) -================ - -- Add support for Python 3.7 - - -4.3.0 (2017-07-25) -================== - -- Add support for Python 3.6. - -- Drop support for Python 3.3. - - -4.2.0 (2016-02-17) -================== - -- Add support for Python 3.5. - -- Drop support for Python 2.6 and 3.2. - - -4.1.0 (2015-10-18) -================== - -- Require 100% branch (as well as statement) coverage. - -- Add a simple class-based handler implementation. - - -4.0.3 (2014-03-19) -================== - -- Add support for Python 3.4. - -- Update ``boostrap.py`` to version 2.2. - - -4.0.2 (2012-12-31) -================== - -- Flesh out PyPI Trove classifiers. - -- Add support for jython 2.7. - - -4.0.1 (2012-11-21) -================== - -- Add support for Python 3.3. - - -4.0.0 (2012-05-16) -================== - -- Automate build of Sphinx HTML docs and running doctest snippets via tox. - -- Drop explicit support for Python 2.4 / 2.5 / 3.1. - -- Add support for PyPy. - - -3.5.2 (2012-03-30) -================== - -- This release is the last which will maintain support for Python 2.4 / - Python 2.5. - -- Add support for continuous integration using ``tox`` and ``jenkins``. - -- Add 'setup.py dev' alias (runs ``setup.py develop`` plus installs - ``nose`` and ``coverage``). - -- Add 'setup.py docs' alias (installs ``Sphinx`` and dependencies). - - -3.5.1 (2011-08-04) -================== - -- Add Sphinx documentation. - - -3.5.0 (2010-05-01) -================== - -- Add change log to ``long-description``. - -- Add support for Python 3.x. - - -3.4.1 (2009-03-03) -================== - -- A few minor cleanups. - - -3.4.0 (2007-07-14) -================== - -- Initial release as a separate project. diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/RECORD b/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/RECORD deleted file mode 100644 index 767d936..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/RECORD +++ /dev/null @@ -1,14 +0,0 @@ -zope.event-5.0-py3.11-nspkg.pth,sha256=SWEVH-jEWsKYrL0qoC6GBJaStx_iKxGoAY9PQycFVC4,529 -zope.event-5.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -zope.event-5.0.dist-info/LICENSE.txt,sha256=PmcdsR32h1FswdtbPWXkqjg-rKPCDOo_r1Og9zNdCjw,2070 -zope.event-5.0.dist-info/METADATA,sha256=M6gNfnFnH8LUu4qQQclpYHTzA-6D3kIRmhA7WNBtW3g,4438 -zope.event-5.0.dist-info/RECORD,, -zope.event-5.0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92 -zope.event-5.0.dist-info/namespace_packages.txt,sha256=QpUHvpO4wIuZDeEgKY8qZCtD-tAukB0fn_f6utzlb98,5 -zope.event-5.0.dist-info/top_level.txt,sha256=QpUHvpO4wIuZDeEgKY8qZCtD-tAukB0fn_f6utzlb98,5 -zope/event/__init__.py,sha256=wSR1Y_yVzSd97zWmPCf4nLZqWyidlJ8_D85Ur_iYx1s,1142 -zope/event/__pycache__/__init__.cpython-38.pyc,, -zope/event/__pycache__/classhandler.cpython-38.pyc,, -zope/event/__pycache__/tests.cpython-38.pyc,, -zope/event/classhandler.py,sha256=2xh4_ofStVc0an9FlHguXNTJwarIRYrWzR-LHf0Ud1Y,1820 -zope/event/tests.py,sha256=GpIP4EIxUZ0fA8vnT01Qv-B7vI6El2w9_prF0EmDrM8,1871 diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/WHEEL b/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/WHEEL deleted file mode 100644 index 1f37c02..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.40.0) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/namespace_packages.txt b/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/namespace_packages.txt deleted file mode 100644 index 66179d4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/namespace_packages.txt +++ /dev/null @@ -1 +0,0 @@ -zope diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/top_level.txt b/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/top_level.txt deleted file mode 100644 index 66179d4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope.event-5.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -zope diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1-py3.8-nspkg.pth b/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1-py3.8-nspkg.pth deleted file mode 100644 index 8012119..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1-py3.8-nspkg.pth +++ /dev/null @@ -1 +0,0 @@ -import sys, types, os;p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('zope',));importlib = __import__('importlib.util');__import__('importlib.machinery');m = sys.modules.setdefault('zope', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('zope', [os.path.dirname(p)])));m = m or sys.modules.setdefault('zope', types.ModuleType('zope'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/INSTALLER b/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/LICENSE.txt b/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/LICENSE.txt deleted file mode 100644 index e1f9ad7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/LICENSE.txt +++ /dev/null @@ -1,44 +0,0 @@ -Zope Public License (ZPL) Version 2.1 - -A copyright notice accompanies this license document that identifies the -copyright holders. - -This license has been certified as open source. It has also been designated as -GPL compatible by the Free Software Foundation (FSF). - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions in source code must retain the accompanying copyright -notice, this list of conditions, and the following disclaimer. - -2. Redistributions in binary form must reproduce the accompanying copyright -notice, this list of conditions, and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -3. Names of the copyright holders must not be used to endorse or promote -products derived from this software without prior written permission from the -copyright holders. - -4. The right to distribute this software or to use it for any purpose does not -give you the right to use Servicemarks (sm) or Trademarks (tm) of the -copyright -holders. Use of them is covered by separate agreement with the copyright -holders. - -5. If any files are modified, you must cause the modified files to carry -prominent notices stating that you changed the files and the date of any -change. - -Disclaimer - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED -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 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/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/METADATA b/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/METADATA deleted file mode 100644 index eb379d3..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/METADATA +++ /dev/null @@ -1,1214 +0,0 @@ -Metadata-Version: 2.1 -Name: zope.interface -Version: 7.1.1 -Summary: Interfaces for Python -Home-page: https://github.com/zopefoundation/zope.interface -Author: Zope Foundation and Contributors -Author-email: zope-dev@zope.dev -License: ZPL 2.1 -Keywords: interface,components,plugins -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: Zope Public License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: 3.13 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Framework :: Zope :: 3 -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Requires-Python: >=3.8 -Description-Content-Type: text/x-rst -License-File: LICENSE.txt -Requires-Dist: setuptools -Provides-Extra: docs -Requires-Dist: Sphinx ; extra == 'docs' -Requires-Dist: repoze.sphinx.autointerface ; extra == 'docs' -Requires-Dist: furo ; extra == 'docs' -Provides-Extra: test -Requires-Dist: coverage[toml] ; extra == 'test' -Requires-Dist: zope.event ; extra == 'test' -Requires-Dist: zope.testing ; extra == 'test' -Provides-Extra: testing -Requires-Dist: coverage[toml] ; extra == 'testing' -Requires-Dist: zope.event ; extra == 'testing' -Requires-Dist: zope.testing ; extra == 'testing' - -==================== - ``zope.interface`` -==================== - -.. image:: https://img.shields.io/pypi/v/zope.interface.svg - :target: https://pypi.python.org/pypi/zope.interface/ - :alt: Latest Version - -.. image:: https://img.shields.io/pypi/pyversions/zope.interface.svg - :target: https://pypi.org/project/zope.interface/ - :alt: Supported Python versions - -.. image:: https://github.com/zopefoundation/zope.interface/actions/workflows/tests.yml/badge.svg - :target: https://github.com/zopefoundation/zope.interface/actions/workflows/tests.yml - -.. image:: https://readthedocs.org/projects/zopeinterface/badge/?version=latest - :target: https://zopeinterface.readthedocs.io/en/latest/ - :alt: Documentation Status - -This package is intended to be independently reusable in any Python -project. It is maintained by the `Zope Toolkit project -`_. - -This package provides an implementation of "object interfaces" for Python. -Interfaces are a mechanism for labeling objects as conforming to a given -API or contract. So, this package can be considered as implementation of -the `Design By Contract`_ methodology support in Python. - -.. _Design By Contract: http://en.wikipedia.org/wiki/Design_by_contract - -For detailed documentation, please see https://zopeinterface.readthedocs.io/en/latest/ - -========= - Changes -========= - -7.1.1 (2024-10-23) -================== - -- Fix segmentation faults in `weakrefobject.c` on Python 3.12 and 3.13. - (`#323 `_) - -7.1.0 (2024-10-10) -================== - -- Declare support for Python 3.13. - -- Fix segmentation faults on Python 3.13. - (`#323 `_) - - -7.0.3 (2024-08-27) -================== - -- Fix `Assertion 'memb->type == T_PYSSIZET' failed.` for Python < 3.12. - (`#319 `_) - - -7.0.2 (2024-08-26) -================== - -- Fix reference-counting bug in C module initialization (broken in 7.0). - (`#316 `_) - - -7.0.1 (2024-08-06) -================== - -- Fix subclassability of ``ObjectSpecificationDescriptor`` (broken in 7.0). - (`#312 `_) - - -7.0 (2024-08-06) -================ - -- Enable heap-based types (PEP 384) for Python >= 3.11. - -- Adopt multi-phase module initialization (PEP 489). - -- Drop support for Python 3.7. - - -6.4.post2 (2024-05-24) -====================== - -- Publish missing Windows wheels, second attempt. - (`#295 `_) - - -6.4.post1 (2024-05-23) -====================== - -- Publish missing Windows wheels. - (`#295 `_) - - -6.4.post0 (2024-05-22) -====================== - -- The sdist of version 6.4 was uploaded to PyPI as - ``zope_interface-6.4.tar.gz`` instead of ``zope.interface-6.4-py2.tar.gz`` - which cannot be installed by ``zc.buildout``. This release is a re-release - of version 6.4 with the correct sdist name. - (`#298 `_) - - -6.4 (2024-05-15) -================ - -- Adjust for incompatible changes in Python 3.13b1. - (`#292 `_) - -- Build windows wheels on GHA. - -6.3 (2024-04-12) -================ - -- Add preliminary support for Python 3.13 as of 3.13a6. - - -6.2 (2024-02-16) -================ - -- Add preliminary support for Python 3.13 as of 3.13a3. - -- Add support to use the pipe (``|``) syntax for ``typing.Union``. - (`#280 `_) - - -6.1 (2023-10-05) -================ - -- Build Linux binary wheels for Python 3.12. - -- Add support for Python 3.12. - -- Fix building of the docs for non-final versions. - - -6.0 (2023-03-17) -================ - -- Build Linux binary wheels for Python 3.11. - -- Drop support for Python 2.7, 3.5, 3.6. - -- Fix test deprecation warning on Python 3.11. - -- Add preliminary support for Python 3.12 as of 3.12a5. - -- Drop: - - + `zope.interface.implements` - + `zope.interface.implementsOnly` - + `zope.interface.classProvides` - - -5.5.2 (2022-11-17) -================== - -- Add support for building arm64 wheels on macOS. - - -5.5.1 (2022-11-03) -================== - -- Add support for final Python 3.11 release. - - -5.5.0 (2022-10-10) -================== - -- Add support for Python 3.10 and 3.11 (as of 3.11.0rc2). - -- Add missing Trove classifier showing support for Python 3.9. - -- Add some more entries to ``zope.interface.interfaces.__all__``. - -- Disable unsafe math optimizations in C code. See `pull request 262 - `_. - - -5.4.0 (2021-04-15) -================== - -- Make the C implementation of the ``__providedBy__`` descriptor stop - ignoring all errors raised when accessing the instance's - ``__provides__``. Now it behaves like the Python version and only - catches ``AttributeError``. The previous behaviour could lead to - crashing the interpreter in cases of recursion and errors. See - `issue 239 `_. - -- Update the ``repr()`` and ``str()`` of various objects to be shorter - and more informative. In many cases, the ``repr()`` is now something - that can be evaluated to produce an equal object. For example, what - was previously printed as ```` is now - shown as ``classImplements(list, IMutableSequence, IIterable)``. See - `issue 236 `_. - -- Make ``Declaration.__add__`` (as in ``implementedBy(Cls) + - ISomething``) try harder to preserve a consistent resolution order - when the two arguments share overlapping pieces of the interface - inheritance hierarchy. Previously, the right hand side was always - put at the end of the resolution order, which could easily produce - invalid orders. See `issue 193 - `_. - -5.3.0 (2020-03-21) -================== - -- No changes from 5.3.0a1 - - -5.3.0a1 (2021-03-18) -==================== - -- Improve the repr of ``zope.interface.Provides`` to remove ambiguity - about what is being provided. This is especially helpful diagnosing - IRO issues. - -- Allow subclasses of ``BaseAdapterRegistry`` (including - ``AdapterRegistry`` and ``VerifyingAdapterRegistry``) to have - control over the data structures. This allows persistent - implementations such as those based on ZODB to choose more scalable - options (e.g., BTrees instead of dicts). See `issue 224 - `_. - -- Fix a reference counting issue in ``BaseAdapterRegistry`` that could - lead to references to interfaces being kept around even when all - utilities/adapters/subscribers providing that interface have been - removed. This is mostly an issue for persistent implementations. - Note that this only corrects the issue moving forward, it does not - solve any already corrupted reference counts. See `issue 227 - `_. - -- Add the method ``BaseAdapterRegistry.rebuild()``. This can be used - to fix the reference counting issue mentioned above, as well as to - update the data structures when custom data types have changed. - -- Add the interface method ``IAdapterRegistry.subscribed()`` and - implementation ``BaseAdapterRegistry.subscribed()`` for querying - directly registered subscribers. See `issue 230 - `_. - -- Add the maintenance method - ``Components.rebuildUtilityRegistryFromLocalCache()``. Most users - will not need this, but it can be useful if the ``Components.utilities`` - registry is suspected to be out of sync with the ``Components`` - object itself (this might happen to persistent ``Components`` - implementations in the face of bugs). - -- Fix the ``Provides`` and ``ClassProvides`` descriptors to stop - allowing redundant interfaces (those already implemented by the - underlying class or meta class) to produce an inconsistent - resolution order. This is similar to the change in ``@implementer`` - in 5.1.0, and resolves inconsistent resolution orders with - ``zope.proxy`` and ``zope.location``. See `issue 207 - `_. - -5.2.0 (2020-11-05) -================== - -- Add documentation section ``Persistency and Equality`` - (`#218 `_). - -- Create arm64 wheels. - -- Add support for Python 3.9. - - -5.1.2 (2020-10-01) -================== - -- Make sure to call each invariant only once when validating invariants. - Previously, invariants could be called multiple times because when an - invariant is defined in an interface, it's found by in all interfaces - inheriting from that interface. See `pull request 215 - `_. - -5.1.1 (2020-09-30) -================== - -- Fix the method definitions of ``IAdapterRegistry.subscribe``, - ``subscriptions`` and ``subscribers``. Previously, they all were - defined to accept a ``name`` keyword argument, but subscribers have - no names and the implementation of that interface did not accept - that argument. See `issue 208 - `_. - -- Fix a potential reference leak in the C optimizations. Previously, - applications that dynamically created unique ``Specification`` - objects (e.g., used ``@implementer`` on dynamic classes) could - notice a growth of small objects over time leading to increased - garbage collection times. See `issue 216 - `_. - - .. caution:: - - This leak could prevent interfaces used as the bases of - other interfaces from being garbage collected. Those interfaces - will now be collected. - - One way in which this would manifest was that ``weakref.ref`` - objects (and things built upon them, like - ``Weak[Key|Value]Dictionary``) would continue to have access to - the original object even if there were no other visible - references to Python and the original object *should* have been - collected. This could be especially problematic for the - ``WeakKeyDictionary`` when combined with dynamic or local - (created in the scope of a function) interfaces, since interfaces - are hashed based just on their name and module name. See the - linked issue for an example of a resulting ``KeyError``. - - Note that such potential errors are not new, they are just once - again a possibility. - -5.1.0 (2020-04-08) -================== - -- Make ``@implementer(*iface)`` and ``classImplements(cls, *iface)`` - ignore redundant interfaces. If the class already implements an - interface through inheritance, it is no longer redeclared - specifically for *cls*. This solves many instances of inconsistent - resolution orders, while still allowing the interface to be declared - for readability and maintenance purposes. See `issue 199 - `_. - -- Remove all bare ``except:`` statements. Previously, when accessing - special attributes such as ``__provides__``, ``__providedBy__``, - ``__class__`` and ``__conform__``, this package wrapped such access - in a bare ``except:`` statement, meaning that many errors could pass - silently; typically this would result in a fallback path being taken - and sometimes (like with ``providedBy()``) the result would be - non-sensical. This is especially true when those attributes are - implemented with descriptors. Now, only ``AttributeError`` is - caught. This makes errors more obvious. - - Obviously, this means that some exceptions will be propagated - differently than before. In particular, ``RuntimeError`` raised by - Acquisition in the case of circular containment will now be - propagated. Previously, when adapting such a broken object, a - ``TypeError`` would be the common result, but now it will be a more - informative ``RuntimeError``. - - In addition, ZODB errors like ``POSKeyError`` could now be - propagated where previously they would ignored by this package. - - See `issue 200 `_. - -- Require that the second argument (*bases*) to ``InterfaceClass`` is - a tuple. This only matters when directly using ``InterfaceClass`` to - create new interfaces dynamically. Previously, an individual - interface was allowed, but did not work correctly. Now it is - consistent with ``type`` and requires a tuple. - -- Let interfaces define custom ``__adapt__`` methods. This implements - the other side of the :pep:`246` adaptation protocol: objects being - adapted could already implement ``__conform__`` if they know about - the interface, and now interfaces can implement ``__adapt__`` if - they know about particular objects. There is no performance penalty - for interfaces that do not supply custom ``__adapt__`` methods. - - This includes the ability to add new methods, or override existing - interface methods using the new ``@interfacemethod`` decorator. - - See `issue 3 `_. - -- Make the internal singleton object returned by APIs like - ``implementedBy`` and ``directlyProvidedBy`` for objects that - implement or provide no interfaces more immutable. Previously an - internal cache could be mutated. See `issue 204 - `_. - -5.0.2 (2020-03-30) -================== - -- Ensure that objects that implement no interfaces (such as direct - subclasses of ``object``) still include ``Interface`` itself in - their ``__iro___`` and ``__sro___``. This fixes adapter registry - lookups for such objects when the adapter is registered for - ``Interface``. See `issue 197 - `_. - - -5.0.1 (2020-03-21) -================== - -- Ensure the resolution order for ``InterfaceClass`` is consistent. - See `issue 192 `_. - -- Ensure the resolution order for ``collections.OrderedDict`` is - consistent on CPython 2. (It was already consistent on Python 3 and PyPy). - -- Fix the handling of the ``ZOPE_INTERFACE_STRICT_IRO`` environment - variable. Previously, ``ZOPE_INTERFACE_STRICT_RO`` was read, in - contrast with the documentation. See `issue 194 - `_. - - -5.0.0 (2020-03-19) -================== - -- Make an internal singleton object returned by APIs like - ``implementedBy`` and ``directlyProvidedBy`` immutable. Previously, - it was fully mutable and allowed changing its ``__bases___``. That - could potentially lead to wrong results in pathological corner - cases. See `issue 158 - `_. - -- Support the ``PURE_PYTHON`` environment variable at runtime instead - of just at wheel build time. A value of 0 forces the C extensions to - be used (even on PyPy) failing if they aren't present. Any other - value forces the Python implementation to be used, ignoring the C - extensions. See `PR 151 `_. - -- Cache the result of ``__hash__`` method in ``InterfaceClass`` as a - speed optimization. The method is called very often (i.e several - hundred thousand times during Plone 5.2 startup). Because the hash value never - changes it can be cached. This improves test performance from 0.614s - down to 0.575s (1.07x faster). In a real world Plone case a reindex - index came down from 402s to 320s (1.26x faster). See `PR 156 - `_. - -- Change the C classes ``SpecificationBase`` and its subclass - ``ClassProvidesBase`` to store implementation attributes in their structures - instead of their instance dictionaries. This eliminates the use of - an undocumented private C API function, and helps make some - instances require less memory. See `PR 154 `_. - -- Reduce memory usage in other ways based on observations of usage - patterns in Zope (3) and Plone code bases. - - - Specifications with no dependents are common (more than 50%) so - avoid allocating a ``WeakKeyDictionary`` unless we need it. - - Likewise, tagged values are relatively rare, so don't allocate a - dictionary to hold them until they are used. - - Use ``__slots___`` or the C equivalent ``tp_members`` in more - common places. Note that this removes the ability to set arbitrary - instance variables on certain objects. - See `PR 155 `_. - - The changes in this release resulted in a 7% memory reduction after - loading about 6,000 modules that define about 2,200 interfaces. - - .. caution:: - - Details of many private attributes have changed, and external use - of those private attributes may break. In particular, the - lifetime and default value of ``_v_attrs`` has changed. - -- Remove support for hashing uninitialized interfaces. This could only - be done by subclassing ``InterfaceClass``. This has generated a - warning since it was first added in 2011 (3.6.5). Please call the - ``InterfaceClass`` constructor or otherwise set the appropriate - fields in your subclass before attempting to hash or sort it. See - `issue 157 `_. - -- Remove unneeded override of the ``__hash__`` method from - ``zope.interface.declarations.Implements``. Watching a reindex index - process in ZCatalog with on a Py-Spy after 10k samples the time for - ``.adapter._lookup`` was reduced from 27.5s to 18.8s (~1.5x faster). - Overall reindex index time shrunk from 369s to 293s (1.26x faster). - See `PR 161 - `_. - -- Make the Python implementation closer to the C implementation by - ignoring all exceptions, not just ``AttributeError``, during (parts - of) interface adaptation. See `issue 163 - `_. - -- Micro-optimization in ``.adapter._lookup`` , ``.adapter._lookupAll`` - and ``.adapter._subscriptions``: By loading ``components.get`` into - a local variable before entering the loop a bytcode "LOAD_FAST 0 - (components)" in the loop can be eliminated. In Plone, while running - all tests, average speedup of the "owntime" of ``_lookup`` is ~5x. - See `PR 167 - `_. - -- Add ``__all__`` declarations to all modules. This helps tools that - do auto-completion and documentation and results in less cluttered - results. Wildcard ("*") are not recommended and may be affected. See - `issue 153 - `_. - -- Fix ``verifyClass`` and ``verifyObject`` for builtin types like - ``dict`` that have methods taking an optional, unnamed argument with - no default value like ``dict.pop``. On PyPy3, the verification is - strict, but on PyPy2 (as on all versions of CPython) those methods - cannot be verified and are ignored. See `issue 118 - `_. - -- Update the common interfaces ``IEnumerableMapping``, - ``IExtendedReadMapping``, ``IExtendedWriteMapping``, - ``IReadSequence`` and ``IUniqueMemberWriteSequence`` to no longer - require methods that were removed from Python 3 on Python 3, such as - ``__setslice___``. Now, ``dict``, ``list`` and ``tuple`` properly - verify as ``IFullMapping``, ``ISequence`` and ``IReadSequence,`` - respectively on all versions of Python. - -- Add human-readable ``__str___`` and ``__repr___`` to ``Attribute`` - and ``Method``. These contain the name of the defining interface - and the attribute. For methods, it also includes the signature. - -- Change the error strings raised by ``verifyObject`` and - ``verifyClass``. They now include more human-readable information - and exclude extraneous lines and spaces. See `issue 170 - `_. - - .. caution:: This will break consumers (such as doctests) that - depended on the exact error messages. - -- Make ``verifyObject`` and ``verifyClass`` report all errors, if the - candidate object has multiple detectable violations. Previously they - reported only the first error. See `issue - `_. - - Like the above, this will break consumers depending on the exact - output of error messages if more than one error is present. - -- Add ``zope.interface.common.collections``, - ``zope.interface.common.numbers``, and ``zope.interface.common.io``. - These modules define interfaces based on the ABCs defined in the - standard library ``collections.abc``, ``numbers`` and ``io`` - modules, respectively. Importing these modules will make the - standard library concrete classes that are registered with those - ABCs declare the appropriate interface. See `issue 138 - `_. - -- Add ``zope.interface.common.builtins``. This module defines - interfaces of common builtin types, such as ``ITextString`` and - ``IByteString``, ``IDict``, etc. These interfaces extend the - appropriate interfaces from ``collections`` and ``numbers``, and the - standard library classes implement them after importing this module. - This is intended as a replacement for third-party packages like - `dolmen.builtins `_. - See `issue 138 `_. - -- Make ``providedBy()`` and ``implementedBy()`` respect ``super`` - objects. For instance, if class ``Derived`` implements ``IDerived`` - and extends ``Base`` which in turn implements ``IBase``, then - ``providedBy(super(Derived, derived))`` will return ``[IBase]``. - Previously it would have returned ``[IDerived]`` (in general, it - would previously have returned whatever would have been returned - without ``super``). - - Along with this change, adapter registries will unpack ``super`` - objects into their ``__self___`` before passing it to the factory. - Together, this means that ``component.getAdapter(super(Derived, - self), ITarget)`` is now meaningful. - - See `issue 11 `_. - -- Fix a potential interpreter crash in the low-level adapter - registry lookup functions. See issue 11. - -- Adopt Python's standard `C3 resolution order - `_ to compute the - ``__iro__`` and ``__sro__`` of interfaces, with tweaks to support - additional cases that are common in interfaces but disallowed for - Python classes. Previously, an ad-hoc ordering that made no - particular guarantees was used. - - This has many beneficial properties, including the fact that base - interface and base classes tend to appear near the end of the - resolution order instead of the beginning. The resolution order in - general should be more predictable and consistent. - - .. caution:: - In some cases, especially with complex interface inheritance - trees or when manually providing or implementing interfaces, the - resulting IRO may be quite different. This may affect adapter - lookup. - - The C3 order enforces some constraints in order to be able to - guarantee a sensible ordering. Older versions of zope.interface did - not impose similar constraints, so it was possible to create - interfaces and declarations that are inconsistent with the C3 - constraints. In that event, zope.interface will still produce a - resolution order equal to the old order, but it won't be guaranteed - to be fully C3 compliant. In the future, strict enforcement of C3 - order may be the default. - - A set of environment variables and module constants allows - controlling several aspects of this new behaviour. It is possible to - request warnings about inconsistent resolution orders encountered, - and even to forbid them. Differences between the C3 resolution order - and the previous order can be logged, and, in extreme cases, the - previous order can still be used (this ability will be removed in - the future). For details, see the documentation for - ``zope.interface.ro``. - -- Make inherited tagged values in interfaces respect the resolution - order (``__iro__``), as method and attribute lookup does. Previously - tagged values could give inconsistent results. See `issue 190 - `_. - -- Add ``getDirectTaggedValue`` (and related methods) to interfaces to - allow accessing tagged values irrespective of inheritance. See - `issue 190 - `_. - -- Ensure that ``Interface`` is always the last item in the ``__iro__`` - and ``__sro__``. This is usually the case, but if classes that do - not implement any interfaces are part of a class inheritance - hierarchy, ``Interface`` could be assigned too high a priority. - See `issue 8 `_. - -- Implement sorting, equality, and hashing in C for ``Interface`` - objects. In micro benchmarks, this makes those operations 40% to 80% - faster. This translates to a 20% speed up in querying adapters. - - Note that this changes certain implementation details. In - particular, ``InterfaceClass`` now has a non-default metaclass, and - it is enforced that ``__module__`` in instances of - ``InterfaceClass`` is read-only. - - See `PR 183 `_. - - -4.7.2 (2020-03-10) -================== - -- Remove deprecated use of setuptools features. See `issue 30 - `_. - - -4.7.1 (2019-11-11) -================== - -- Use Python 3 syntax in the documentation. See `issue 119 - `_. - - -4.7.0 (2019-11-11) -================== - -- Drop support for Python 3.4. - -- Change ``queryTaggedValue``, ``getTaggedValue``, - ``getTaggedValueTags`` in interfaces. They now include inherited - values by following ``__bases__``. See `PR 144 - `_. - - .. caution:: This may be a breaking change. - -- Add support for Python 3.8. - - -4.6.0 (2018-10-23) -================== - -- Add support for Python 3.7 - -- Fix ``verifyObject`` for class objects with staticmethods on - Python 3. See `issue 126 - `_. - - -4.5.0 (2018-04-19) -================== - -- Drop support for 3.3, avoid accidental dependence breakage via setup.py. - See `PR 110 `_. -- Allow registering and unregistering instance methods as listeners. - See `issue 12 `_ - and `PR 102 `_. -- Synchronize and simplify zope/__init__.py. See `issue 114 - `_ - - -4.4.3 (2017-09-22) -================== - -- Avoid exceptions when the ``__annotations__`` attribute is added to - interface definitions with Python 3.x type hints. See `issue 98 - `_. -- Fix the possibility of a rare crash in the C extension when - deallocating items. See `issue 100 - `_. - - -4.4.2 (2017-06-14) -================== - -- Fix a regression storing - ``zope.component.persistentregistry.PersistentRegistry`` instances. - See `issue 85 `_. - -- Fix a regression that could lead to the utility registration cache - of ``Components`` getting out of sync. See `issue 93 - `_. - -4.4.1 (2017-05-13) -================== - -- Simplify the caching of utility-registration data. In addition to - simplification, avoids spurious test failures when checking for - leaks in tests with persistent registries. See `pull 84 - `_. - -- Raise ``ValueError`` when non-text names are passed to adapter registry - methods: prevents corruption of lookup caches. - -4.4.0 (2017-04-21) -================== - -- Avoid a warning from the C compiler. - (https://github.com/zopefoundation/zope.interface/issues/71) - -- Add support for Python 3.6. - -4.3.3 (2016-12-13) -================== - -- Correct typos and ReST formatting errors in documentation. - -- Add API documentation for the adapter registry. - -- Ensure that the ``LICENSE.txt`` file is included in built wheels. - -- Fix C optimizations broken on Py3k. See the Python bug at: - http://bugs.python.org/issue15657 - (https://github.com/zopefoundation/zope.interface/issues/60) - - -4.3.2 (2016-09-05) -================== - -- Fix equality testing of ``implementedBy`` objects and proxies. - (https://github.com/zopefoundation/zope.interface/issues/55) - - -4.3.1 (2016-08-31) -================== - -- Support Components subclasses that are not hashable. - (https://github.com/zopefoundation/zope.interface/issues/53) - - -4.3.0 (2016-08-31) -================== - -- Add the ability to sort the objects returned by ``implementedBy``. - This is compatible with the way interface classes sort so they can - be used together in ordered containers like BTrees. - (https://github.com/zopefoundation/zope.interface/issues/42) - -- Make ``setuptools`` a hard dependency of ``setup.py``. - (https://github.com/zopefoundation/zope.interface/issues/13) - -- Change a linear algorithm (O(n)) in ``Components.registerUtility`` and - ``Components.unregisterUtility`` into a dictionary lookup (O(1)) for - hashable components. This substantially improves the time taken to - manipulate utilities in large registries at the cost of some - additional memory usage. (https://github.com/zopefoundation/zope.interface/issues/46) - - -4.2.0 (2016-06-10) -================== - -- Add support for Python 3.5 - -- Drop support for Python 2.6 and 3.2. - - -4.1.3 (2015-10-05) -================== - -- Fix installation without a C compiler on Python 3.5 - (https://github.com/zopefoundation/zope.interface/issues/24). - - -4.1.2 (2014-12-27) -================== - -- Add support for PyPy3. - -- Remove unittest assertions deprecated in Python3.x. - -- Add ``zope.interface.document.asReStructuredText``, which formats the - generated text for an interface using ReST double-backtick markers. - - -4.1.1 (2014-03-19) -================== - -- Add support for Python 3.4. - - -4.1.0 (2014-02-05) -================== - -- Update ``boostrap.py`` to version 2.2. - -- Add ``@named(name)`` declaration, that specifies the component name, so it - does not have to be passed in during registration. - - -4.0.5 (2013-02-28) -================== - -- Fix a bug where a decorated method caused false positive failures on - ``verifyClass()``. - - -4.0.4 (2013-02-21) -================== - -- Fix a bug that was revealed by porting zope.traversing. During a loop, the - loop body modified a weakref dict causing a ``RuntimeError`` error. - -4.0.3 (2012-12-31) -================== - -- Fleshed out PyPI Trove classifiers. - -4.0.2 (2012-11-21) -================== - -- Add support for Python 3.3. - -- Restored ability to install the package in the absence of ``setuptools``. - -- LP #1055223: Fix test which depended on dictionary order and failed randomly - in Python 3.3. - -4.0.1 (2012-05-22) -================== - -- Drop explicit ``DeprecationWarnings`` for "class advice" APIS (these - APIs are still deprecated under Python 2.x, and still raise an exception - under Python 3.x, but no longer cause a warning to be emitted under - Python 2.x). - -4.0.0 (2012-05-16) -================== - -- Automated build of Sphinx HTML docs and running doctest snippets via tox. - -- Deprecate the "class advice" APIs from ``zope.interface.declarations``: - ``implements``, ``implementsOnly``, and ``classProvides``. In their place, - prefer the equivalent class decorators: ``@implementer``, - ``@implementer_only``, and ``@provider``. Code which uses the deprecated - APIs will not work as expected under Py3k. - -- Remove use of '2to3' and associated fixers when installing under Py3k. - The code is now in a "compatible subset" which supports Python 2.6, 2.7, - and 3.2, including PyPy 1.8 (the version compatible with the 2.7 language - spec). - -- Drop explicit support for Python 2.4 / 2.5 / 3.1. - -- Add support for PyPy. - -- Add support for continuous integration using ``tox`` and ``jenkins``. - -- Add 'setup.py dev' alias (runs ``setup.py develop`` plus installs - ``nose`` and ``coverage``). - -- Add 'setup.py docs' alias (installs ``Sphinx`` and dependencies). - -- Replace all unittest coverage previously accomplished via doctests with - unittests. The doctests have been moved into a ``docs`` section, managed - as a Sphinx collection. - -- LP #910987: Ensure that the semantics of the ``lookup`` method of - ``zope.interface.adapter.LookupBase`` are the same in both the C and - Python implementations. - -- LP #900906: Avoid exceptions due to tne new ``__qualname__`` attribute - added in Python 3.3 (see PEP 3155 for rationale). Thanks to Antoine - Pitrou for the patch. - -3.8.0 (2011-09-22) -================== - -- New module ``zope.interface.registry``. This is code moved from - ``zope.component.registry`` which implements a basic nonperistent component - registry as ``zope.interface.registry.Components``. This class was moved - from ``zope.component`` to make porting systems (such as Pyramid) that rely - only on a basic component registry to Python 3 possible without needing to - port the entirety of the ``zope.component`` package. Backwards - compatibility import shims have been left behind in ``zope.component``, so - this change will not break any existing code. - -- New ``tests_require`` dependency: ``zope.event`` to test events sent by - Components implementation. The ``zope.interface`` package does not have a - hard dependency on ``zope.event``, but if ``zope.event`` is importable, it - will send component registration events when methods of an instance of - ``zope.interface.registry.Components`` are called. - -- New interfaces added to support ``zope.interface.registry.Components`` - addition: ``ComponentLookupError``, ``Invalid``, ``IObjectEvent``, - ``ObjectEvent``, ``IComponentLookup``, ``IRegistration``, - ``IUtilityRegistration``, ``IAdapterRegistration``, - ``ISubscriptionAdapterRegistration``, ``IHandlerRegistration``, - ``IRegistrationEvent``, ``RegistrationEvent``, ``IRegistered``, - ``Registered``, ``IUnregistered``, ``Unregistered``, - ``IComponentRegistry``, and ``IComponents``. - -- No longer Python 2.4 compatible (tested under 2.5, 2.6, 2.7, and 3.2). - -3.7.0 (2011-08-13) -================== - -- Move changes from 3.6.2 - 3.6.5 to a new 3.7.x release line. - -3.6.7 (2011-08-20) -================== - -- Fix sporadic failures on x86-64 platforms in tests of rich comparisons - of interfaces. - -3.6.6 (2011-08-13) -================== - -- LP #570942: Now correctly compare interfaces from different modules but - with the same names. - - N.B.: This is a less intrusive / destabilizing fix than the one applied in - 3.6.3: we only fix the underlying cmp-alike function, rather than adding - the other "rich comparison" functions. - -- Revert to software as released with 3.6.1 for "stable" 3.6 release branch. - -3.6.5 (2011-08-11) -================== - -- LP #811792: work around buggy behavior in some subclasses of - ``zope.interface.interface.InterfaceClass``, which invoke ``__hash__`` - before initializing ``__module__`` and ``__name__``. The workaround - returns a fixed constant hash in such cases, and issues a ``UserWarning``. - -- LP #804832: Under PyPy, ``zope.interface`` should not build its C - extension. Also, prevent attempting to build it under Jython. - -- Add a tox.ini for easier xplatform testing. - -- Fix testing deprecation warnings issued when tested under Py3K. - -3.6.4 (2011-07-04) -================== - -- LP 804951: InterfaceClass instances were unhashable under Python 3.x. - -3.6.3 (2011-05-26) -================== - -- LP #570942: Now correctly compare interfaces from different modules but - with the same names. - -3.6.2 (2011-05-17) -================== - -- Moved detailed documentation out-of-line from PyPI page, linking instead to - http://docs.zope.org/zope.interface . - -- Fixes for small issues when running tests under Python 3.2 using - ``zope.testrunner``. - -- LP # 675064: Specify return value type for C optimizations module init - under Python 3: undeclared value caused warnings, and segfaults on some - 64 bit architectures. - -- setup.py now raises RuntimeError if you don't have Distutils installed when - running under Python 3. - -3.6.1 (2010-05-03) -================== - -- A non-ASCII character in the changelog made 3.6.0 uninstallable on - Python 3 systems with another default encoding than UTF-8. - -- Fix compiler warnings under GCC 4.3.3. - -3.6.0 (2010-04-29) -================== - -- LP #185974: Clear the cache used by ``Specificaton.get`` inside - ``Specification.changed``. Thanks to Jacob Holm for the patch. - -- Add support for Python 3.1. Contributors: - - Lennart Regebro - Martin v Loewis - Thomas Lotze - Wolfgang Schnerring - - The 3.1 support is completely backwards compatible. However, the implements - syntax used under Python 2.X does not work under 3.X, since it depends on - how metaclasses are implemented and this has changed. Instead it now supports - a decorator syntax (also under Python 2.X):: - - class Foo: - implements(IFoo) - ... - - can now also be written:: - - @implementer(IFoo): - class Foo: - ... - - There are 2to3 fixers available to do this change automatically in the - zope.fixers package. - -- Python 2.3 is no longer supported. - - -3.5.4 (2009-12-23) -================== - -- Use the standard Python doctest module instead of zope.testing.doctest, which - has been deprecated. - - -3.5.3 (2009-12-08) -================== - -- Fix an edge case: make providedBy() work when a class has '__provides__' in - its __slots__ (see http://thread.gmane.org/gmane.comp.web.zope.devel/22490) - - -3.5.2 (2009-07-01) -================== - -- BaseAdapterRegistry.unregister, unsubscribe: Remove empty portions of - the data structures when something is removed. This avoids leaving - references to global objects (interfaces) that may be slated for - removal from the calling application. - - -3.5.1 (2009-03-18) -================== - -- verifyObject: use getattr instead of hasattr to test for object attributes - in order to let exceptions other than AttributeError raised by properties - propagate to the caller - -- Add Sphinx-based documentation building to the package buildout - configuration. Use the ``bin/docs`` command after buildout. - -- Improve package description a bit. Unify changelog entries formatting. - -- Change package's mailing list address to zope-dev at zope.org as - zope3-dev at zope.org is now retired. - - -3.5.0 (2008-10-26) -================== - -- Fix declaration of _zope_interface_coptimizations, it's not a top level - package. - -- Add a DocTestSuite for odd.py module, so their tests are run. - -- Allow to bootstrap on Jython. - -- Fix https://bugs.launchpad.net/zope3/3.3/+bug/98388: ISpecification - was missing a declaration for __iro__. - -- Add optional code optimizations support, which allows the building - of C code optimizations to fail (Jython). - -- Replace `_flatten` with a non-recursive implementation, effectively making - it 3x faster. - - -3.4.1 (2007-10-02) -================== - -- Fix a setup bug that prevented installation from source on systems - without setuptools. - - -3.4.0 (2007-07-19) -================== - -- Final release for 3.4.0. - - -3.4.0b3 (2007-05-22) -==================== - - -- When checking whether an object is already registered, use identity - comparison, to allow adding registering with picky custom comparison methods. - - -3.3.0.1 (2007-01-03) -==================== - -- Made a reference to OverflowWarning, which disappeared in Python - 2.5, conditional. - - -3.3.0 (2007/01/03) -================== - -New Features ------------- - -- Refactor the adapter-lookup algorithim to make it much simpler and faster. - - Also, implement more of the adapter-lookup logic in C, making - debugging of application code easier, since there is less - infrastructre code to step through. - -- Treat objects without interface declarations as if they - declared that they provide ``zope.interface.Interface``. - -- Add a number of richer new adapter-registration interfaces - that provide greater control and introspection. - -- Add a new interface decorator to zope.interface that allows the - setting of tagged values on an interface at definition time (see - zope.interface.taggedValue). - -Bug Fixes ---------- - -- A bug in multi-adapter lookup sometimes caused incorrect adapters to - be returned. - - -3.2.0.2 (2006-04-15) -==================== - -- Fix packaging bug: 'package_dir' must be a *relative* path. - - -3.2.0.1 (2006-04-14) -==================== - -- Packaging change: suppress inclusion of 'setup.cfg' in 'sdist' builds. - - -3.2.0 (2006-01-05) -================== - -- Corresponds to the version of the zope.interface package shipped as part of - the Zope 3.2.0 release. - - -3.1.0 (2005-10-03) -================== - -- Corresponds to the version of the zope.interface package shipped as part of - the Zope 3.1.0 release. - -- Made attribute resolution order consistent with component lookup order, - i.e. new-style class MRO semantics. - -- Deprecate 'isImplementedBy' and 'isImplementedByInstancesOf' APIs in - favor of 'implementedBy' and 'providedBy'. - - -3.0.1 (2005-07-27) -================== - -- Corresponds to the version of the zope.interface package shipped as part of - the Zope X3.0.1 release. - -- Fix a bug reported by James Knight, which caused adapter registries - to fail occasionally to reflect declaration changes. - - -3.0.0 (2004-11-07) -================== - -- Corresponds to the version of the zope.interface package shipped as part of - the Zope X3.0.0 release. diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/RECORD b/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/RECORD deleted file mode 100644 index 99b83b5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/RECORD +++ /dev/null @@ -1,110 +0,0 @@ -zope.interface-7.1.1-py3.8-nspkg.pth,sha256=_l6EZJaefCi1yytmDbFQGX_jfS2gAtFyaDmlK1uFLec,457 -zope.interface-7.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -zope.interface-7.1.1.dist-info/LICENSE.txt,sha256=PmcdsR32h1FswdtbPWXkqjg-rKPCDOo_r1Og9zNdCjw,2070 -zope.interface-7.1.1.dist-info/METADATA,sha256=oWE7uV0Etf30ZskrA9gQ4xifIG1VQLSra2J8hfLddeY,44074 -zope.interface-7.1.1.dist-info/RECORD,, -zope.interface-7.1.1.dist-info/WHEEL,sha256=Avi1L8sk3K2lPLYGkLNew0DbUtt2kW_vpSWOV-Z4hjY,216 -zope.interface-7.1.1.dist-info/namespace_packages.txt,sha256=QpUHvpO4wIuZDeEgKY8qZCtD-tAukB0fn_f6utzlb98,5 -zope.interface-7.1.1.dist-info/top_level.txt,sha256=QpUHvpO4wIuZDeEgKY8qZCtD-tAukB0fn_f6utzlb98,5 -zope/interface/__init__.py,sha256=9Euz0jFaHg3kT84m2HzikMt7s_JIAjM8BlVctyUfNTk,3475 -zope/interface/__pycache__/__init__.cpython-38.pyc,, -zope/interface/__pycache__/_compat.cpython-38.pyc,, -zope/interface/__pycache__/_flatten.cpython-38.pyc,, -zope/interface/__pycache__/adapter.cpython-38.pyc,, -zope/interface/__pycache__/advice.cpython-38.pyc,, -zope/interface/__pycache__/declarations.cpython-38.pyc,, -zope/interface/__pycache__/document.cpython-38.pyc,, -zope/interface/__pycache__/exceptions.cpython-38.pyc,, -zope/interface/__pycache__/interface.cpython-38.pyc,, -zope/interface/__pycache__/interfaces.cpython-38.pyc,, -zope/interface/__pycache__/registry.cpython-38.pyc,, -zope/interface/__pycache__/ro.cpython-38.pyc,, -zope/interface/__pycache__/verify.cpython-38.pyc,, -zope/interface/_compat.py,sha256=yj7mY66LhNQFWEuWWz7AYnn6zEPavfgBryaIHjo0tVQ,4367 -zope/interface/_flatten.py,sha256=NKviK4ZyLBCjxXvz5voI9pV_ui2SLqUhubbqZkvgEcg,1059 -zope/interface/_zope_interface_coptimizations.c,sha256=oCYrpT2efApCO-EbKs71YXyaSohAbTqdWQ7QEh49SNA,71198 -zope/interface/_zope_interface_coptimizations.cpython-38-x86_64-linux-gnu.so,sha256=PcmhBZffEJjCFWidwhOnTh8DtFi50MsfaCbuKzxhJSU,201936 -zope/interface/adapter.py,sha256=XN9PD8OrRmP1ZJy2gPvAMXtiOnKcHL34doH0HaKHguQ,36647 -zope/interface/advice.py,sha256=YftOBzbXuWQ3AuJImMtYv4SYbE7ubJrjodXzTgYR-dk,3918 -zope/interface/common/__init__.py,sha256=ho-q_3jeKVq-jO-5xrN9_Q4PR0o8PDwja5NYsO7qTI0,10644 -zope/interface/common/__pycache__/__init__.cpython-38.pyc,, -zope/interface/common/__pycache__/builtins.cpython-38.pyc,, -zope/interface/common/__pycache__/collections.cpython-38.pyc,, -zope/interface/common/__pycache__/idatetime.cpython-38.pyc,, -zope/interface/common/__pycache__/interfaces.cpython-38.pyc,, -zope/interface/common/__pycache__/io.cpython-38.pyc,, -zope/interface/common/__pycache__/mapping.cpython-38.pyc,, -zope/interface/common/__pycache__/numbers.cpython-38.pyc,, -zope/interface/common/__pycache__/sequence.cpython-38.pyc,, -zope/interface/common/builtins.py,sha256=P_QNQmCmieAuJFgiIgWeLLZbNyQ5Czz7riOmtIAPjX8,3030 -zope/interface/common/collections.py,sha256=oqH9d7BJ4Jv4IRzWhKOK74x_aBKBuNQquZWCuRqwYOw,6712 -zope/interface/common/idatetime.py,sha256=sn5ccP9CiQAYzn6mptWf05uInZFp9o_DRu9_MaDXM5c,21039 -zope/interface/common/interfaces.py,sha256=fT2pigpAZuqcm0bqVVNDlvSWtv9oKfsJ73ewrymUpIE,5908 -zope/interface/common/io.py,sha256=if6Yzclu_T7qeUQNsXeIqREqgVTu-rjYB_VGZfYyz3Y,1242 -zope/interface/common/mapping.py,sha256=XI9lO7Uwt_9sWDFyhDIk0Rjcr5nlu7SlD2KDyqdupdw,4687 -zope/interface/common/numbers.py,sha256=D4kUnF5OgAk6CKcxaW86VH_OPLh1OXt_cF6WaOfvjLk,1682 -zope/interface/common/sequence.py,sha256=SYvqSZpKRaX11vCADK6zGn0LqogPQ0u7INbqgTJ4sg8,5531 -zope/interface/common/tests/__init__.py,sha256=Am8tpD6HZQx6pfW7N22-dloC83-dKLA45NiM9Lx7Xss,5553 -zope/interface/common/tests/__pycache__/__init__.cpython-38.pyc,, -zope/interface/common/tests/__pycache__/basemapping.cpython-38.pyc,, -zope/interface/common/tests/__pycache__/test_builtins.cpython-38.pyc,, -zope/interface/common/tests/__pycache__/test_collections.cpython-38.pyc,, -zope/interface/common/tests/__pycache__/test_idatetime.cpython-38.pyc,, -zope/interface/common/tests/__pycache__/test_import_interfaces.cpython-38.pyc,, -zope/interface/common/tests/__pycache__/test_io.cpython-38.pyc,, -zope/interface/common/tests/__pycache__/test_numbers.cpython-38.pyc,, -zope/interface/common/tests/basemapping.py,sha256=iitIJEMm7LlS3vQU51RjoN3AeBUNNf0EGLXi_VklJAs,3768 -zope/interface/common/tests/test_builtins.py,sha256=slmlaiZjaZJXjplCEaWXAaUFaWW6bcK3z8Je7al5mdE,1345 -zope/interface/common/tests/test_collections.py,sha256=FpnJI74M9E9vAzTeQr_bkYl1eOIIb79b-GkM8aN8Qv0,6066 -zope/interface/common/tests/test_idatetime.py,sha256=qGr8FDbiVuDZHjyDIKcz7Vz6qNfoFzd9eHejMTfIyAg,1923 -zope/interface/common/tests/test_import_interfaces.py,sha256=wCFk-2kc7r-kAeOXZn5th5Qw_6k_-pMvdguk5b4NstM,813 -zope/interface/common/tests/test_io.py,sha256=gYJnFT52De1JAJruKK6dzyQIdLgYdQME9Yn6QTn2QyI,1686 -zope/interface/common/tests/test_numbers.py,sha256=tPrvkFVYa2kSSxf8_j7XYctupfS4BI-ee_jQ5yU8zRM,1395 -zope/interface/declarations.py,sha256=csCgY3nsiTjpkjdHs53DJGoeIGSeI_mUaTISYQB_9dQ,43512 -zope/interface/document.py,sha256=mhSCJoxw7Whl-q-EMSkys--z3C7IyJ6TEsZ3psMqyJE,4139 -zope/interface/exceptions.py,sha256=lW7SnsOT73LZI8w3aRdCRVUubfSy-CGztchnKqv1-5A,8566 -zope/interface/interface.py,sha256=hM1UaVXyY_k2KScmEtRfnblJUBul-mXLi9KVXoPasWU,39684 -zope/interface/interfaces.py,sha256=9abPucc_lYWpELDFt28wnbjRVtQ8pS48srV8toZmLgk,50268 -zope/interface/registry.py,sha256=eDnfUtTFp1z9f-4F-4ZQ9EATLheAYuCP9_coZ3XLdVw,25812 -zope/interface/ro.py,sha256=NeImEPsautGBC1VtQAQ1J1BGXmksw6hoSNWD7gVmEaQ,24577 -zope/interface/tests/__init__.py,sha256=Vu2CALgC5nADbRAZFb99LcDSXECcPCNdJDL-nv_6mZA,4242 -zope/interface/tests/__pycache__/__init__.cpython-38.pyc,, -zope/interface/tests/__pycache__/advisory_testing.cpython-38.pyc,, -zope/interface/tests/__pycache__/dummy.cpython-38.pyc,, -zope/interface/tests/__pycache__/idummy.cpython-38.pyc,, -zope/interface/tests/__pycache__/m1.cpython-38.pyc,, -zope/interface/tests/__pycache__/odd.cpython-38.pyc,, -zope/interface/tests/__pycache__/test_adapter.cpython-38.pyc,, -zope/interface/tests/__pycache__/test_advice.cpython-38.pyc,, -zope/interface/tests/__pycache__/test_compile_flags.cpython-38.pyc,, -zope/interface/tests/__pycache__/test_declarations.cpython-38.pyc,, -zope/interface/tests/__pycache__/test_document.cpython-38.pyc,, -zope/interface/tests/__pycache__/test_element.cpython-38.pyc,, -zope/interface/tests/__pycache__/test_exceptions.cpython-38.pyc,, -zope/interface/tests/__pycache__/test_interface.cpython-38.pyc,, -zope/interface/tests/__pycache__/test_interfaces.cpython-38.pyc,, -zope/interface/tests/__pycache__/test_odd_declarations.cpython-38.pyc,, -zope/interface/tests/__pycache__/test_registry.cpython-38.pyc,, -zope/interface/tests/__pycache__/test_ro.cpython-38.pyc,, -zope/interface/tests/__pycache__/test_sorting.cpython-38.pyc,, -zope/interface/tests/__pycache__/test_verify.cpython-38.pyc,, -zope/interface/tests/advisory_testing.py,sha256=T2XF0DT8t9zi1JxnvR1u0WXpRtyOn5027VMyEcN7Ldc,900 -zope/interface/tests/dummy.py,sha256=dd_cYIiPvYJDrEMQD0m2cl9SpkVMrQzMMu1yglSrnb8,913 -zope/interface/tests/idummy.py,sha256=ESC_l4UqjCkiCeXWfcphVGtrP7PoNwwTTGzLpp6Doo4,890 -zope/interface/tests/m1.py,sha256=clhSXmtaT8liCnM87Kp0bzAHfxIxr6gUBjXnVuxaUNI,850 -zope/interface/tests/odd.py,sha256=nwjmxbHCFuI2UvY3Pwc7CfkmZWfXsn3oCb7r7C34DxU,2967 -zope/interface/tests/test_adapter.py,sha256=pB3Y_oXXtdtDsKf0BYlvpW281nT1yendxtzWgWp9o_0,80508 -zope/interface/tests/test_advice.py,sha256=gFhlbHvSWuC9kdlBPeXO_8_9ihkSU-ih0pVhoocdp4U,6036 -zope/interface/tests/test_compile_flags.py,sha256=wU3vtmqgzll6HMPbzFErMpORoVghrcZ5krIV4x6rfo4,1290 -zope/interface/tests/test_declarations.py,sha256=7hlSiT33AN4lzZfFigGyAmf6E9ts6-TgIN0jQb0NB04,83091 -zope/interface/tests/test_document.py,sha256=0JIYe0GWvDPkr_IBACPw-8HsHNJyNNHBU_lUHYOXd9M,17220 -zope/interface/tests/test_element.py,sha256=z9Q2hYwPVzMX05NpA_IeiHoAb4qhTiHI57XQI38w4zQ,1120 -zope/interface/tests/test_exceptions.py,sha256=hxceYZyLZghgpxm-ABg1k50-H1CYRSTUGMZyiTrElo8,6445 -zope/interface/tests/test_interface.py,sha256=KhTcp5nM_K2ffiZSTRzvjhag9tZMfc8qvpV-a3OlSBg,92717 -zope/interface/tests/test_interfaces.py,sha256=l4SCfUmVfBO1UoZLsTOOvDyoYUM7Wex3J67uR7taFpQ,4395 -zope/interface/tests/test_odd_declarations.py,sha256=0DBSBIoOUdSRcDnhiScexGhZ2mVydYWnlJOBvny76Uo,7660 -zope/interface/tests/test_registry.py,sha256=8CyxRkJ46ITauSGp86y1gSN4KHvZNVPTVKgjD13aXBk,112599 -zope/interface/tests/test_ro.py,sha256=CvuZne2GdQu0JA4WQObKm8mG-F7__3T4VBINwoj3QQI,14465 -zope/interface/tests/test_sorting.py,sha256=I4P2jvStUzYrNOnIQgkZZs0sTn9BHe7g5RLQlFmTTW4,2051 -zope/interface/tests/test_verify.py,sha256=JnVLFlHGgVn8fC6u-WPW-mDtfmSuDA2PC0sglWuVBjI,19200 -zope/interface/verify.py,sha256=cEaIWPcS6gT97RkQW2SQJxxuUP7wRrwYwsJtXqo8jdE,7333 diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/WHEEL b/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/WHEEL deleted file mode 100644 index 6eda423..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/WHEEL +++ /dev/null @@ -1,8 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (73.0.1) -Root-Is-Purelib: false -Tag: cp38-cp38-manylinux_2_5_x86_64 -Tag: cp38-cp38-manylinux1_x86_64 -Tag: cp38-cp38-manylinux_2_17_x86_64 -Tag: cp38-cp38-manylinux2014_x86_64 - diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/namespace_packages.txt b/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/namespace_packages.txt deleted file mode 100644 index 66179d4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/namespace_packages.txt +++ /dev/null @@ -1 +0,0 @@ -zope diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/top_level.txt b/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/top_level.txt deleted file mode 100644 index 66179d4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope.interface-7.1.1.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -zope diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/event/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/zope/event/__init__.py deleted file mode 100644 index 07675b6..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/event/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -############################################################################## -# -# Copyright (c) 2004 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -""" Base event system implementation - -""" - -#: Applications may register for notification of events by appending a -#: callable to the ``subscribers`` list. -#: -#: Each subscriber takes a single argument, which is the event object -#: being published. -#: -#: Exceptions raised by subscribers will be propagated *without* running -#: any remaining subscribers. -subscribers = [] - - -def notify(event): - """ Notify all subscribers of ``event``. - """ - for subscriber in subscribers: - subscriber(event) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/event/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/event/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 7348660..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/event/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/event/__pycache__/classhandler.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/event/__pycache__/classhandler.cpython-38.pyc deleted file mode 100644 index 6f4158c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/event/__pycache__/classhandler.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/event/__pycache__/tests.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/event/__pycache__/tests.cpython-38.pyc deleted file mode 100644 index 06440c0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/event/__pycache__/tests.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/event/classhandler.py b/plotter-app/venv/lib/python3.8/site-packages/zope/event/classhandler.py deleted file mode 100644 index 464572a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/event/classhandler.py +++ /dev/null @@ -1,76 +0,0 @@ -"""Class-based event handlers - - -A light-weight event-handler framework based on event classes. - -Handlers are registered for event classes: - - >>> import zope.event.classhandler - - >>> class MyEvent(object): - ... pass - - >>> def handler1(event): - ... print("handler1 %r" % event.__class__.__name__) - - >>> zope.event.classhandler.handler(MyEvent, handler1) - -Descriptor syntax: - - >>> @zope.event.classhandler.handler(MyEvent) - ... def handler2(event): - ... print("handler2 %r" % event.__class__.__name__) - - >>> class MySubEvent(MyEvent): - ... pass - - >>> @zope.event.classhandler.handler(MySubEvent) - ... def handler3(event): - ... print("handler3 %r" % event.__class__.__name__) - - -Subscribers are called in class method-resolution order, so only -new-style event classes are supported, and then by order of registry. - - >>> import zope.event - >>> zope.event.notify(MySubEvent()) - handler3 'MySubEvent' - handler1 'MySubEvent' - handler2 'MySubEvent' - -""" -import zope.event - - -__all__ = [ - 'handler', -] - -registry = {} - - -def handler(event_class, handler_=None, _decorator=False): - """ Define an event handler for a (new-style) class. - - This can be called with a class and a handler, or with just a - class and the result used as a handler decorator. - """ - if handler_ is None: - return lambda func: handler(event_class, func, True) - - if not registry: - zope.event.subscribers.append(dispatch) - - if event_class not in registry: - registry[event_class] = [handler_] - else: - registry[event_class].append(handler_) - - if _decorator: - return handler - - -def dispatch(event): - for event_class in event.__class__.__mro__: - for handler in registry.get(event_class, ()): - handler(event) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/event/tests.py b/plotter-app/venv/lib/python3.8/site-packages/zope/event/tests.py deleted file mode 100644 index 0dc7df2..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/event/tests.py +++ /dev/null @@ -1,64 +0,0 @@ -############################################################################## -# -# Copyright (c) 2004 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -""" Test the event system -""" -import doctest -import unittest - - -class Test_notify(unittest.TestCase): - - def setUp(self): - from zope.event import subscribers - self._old_subscribers = subscribers[:] - subscribers[:] = [] - - def tearDown(self): - from zope.event import subscribers - subscribers[:] = self._old_subscribers - - def _callFUT(self, event): - from zope.event import notify - notify(event) - - def test_empty(self): - event = object() - self._callFUT(event) - - def test_not_empty(self): - from zope.event import subscribers - dummy = [] - subscribers.append(dummy.append) - event = object() - self._callFUT(event) - self.assertEqual(dummy, [event]) - - -def setUpClassHandlers(test): - import zope.event - test.globs['old_subs'] = zope.event.subscribers - - -def tearDownClassHandlers(test): - import zope.event - zope.event.subscribers = test.globs['old_subs'] - - -def test_suite(): - return unittest.TestSuite(( - unittest.defaultTestLoader.loadTestsFromName(__name__), - doctest.DocTestSuite( - 'zope.event.classhandler', - setUp=setUpClassHandlers, tearDown=tearDownClassHandlers) - )) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__init__.py deleted file mode 100644 index 2b8e7eb..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__init__.py +++ /dev/null @@ -1,90 +0,0 @@ -############################################################################## -# -# Copyright (c) 2001, 2002 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Interfaces - -This package implements the Python "scarecrow" proposal. - -The package exports two objects, `Interface` and `Attribute` directly. It also -exports several helper methods. Interface is used to create an interface with -a class statement, as in: - - class IMyInterface(Interface): - '''Interface documentation - ''' - - def meth(arg1, arg2): - '''Documentation for meth - ''' - - # Note that there is no self argument - -To find out what you can do with interfaces, see the interface -interface, `IInterface` in the `interfaces` module. - -The package has several public modules: - - o `declarations` provides utilities to declare interfaces on objects. It - also provides a wide range of helpful utilities that aid in managing - declared interfaces. Most of its public names are however imported here. - - o `document` has a utility for documenting an interface as structured text. - - o `exceptions` has the interface-defined exceptions - - o `interfaces` contains a list of all public interfaces for this package. - - o `verify` has utilities for verifying implementations of interfaces. - -See the module doc strings for more information. -""" -__docformat__ = 'restructuredtext' -# pylint:disable=wrong-import-position,unused-import -from zope.interface.interface import Interface -from zope.interface.interface import _wire - - -# Need to actually get the interface elements to implement the right interfaces -_wire() -del _wire - -from zope.interface.declarations import Declaration # isort: skip -# The following are to make spec pickles cleaner -from zope.interface.declarations import Provides -from zope.interface.declarations import alsoProvides -from zope.interface.declarations import classImplements -from zope.interface.declarations import classImplementsFirst -from zope.interface.declarations import classImplementsOnly -from zope.interface.declarations import directlyProvidedBy -from zope.interface.declarations import directlyProvides -from zope.interface.declarations import implementedBy -from zope.interface.declarations import implementer -from zope.interface.declarations import implementer_only -from zope.interface.declarations import moduleProvides -from zope.interface.declarations import named -from zope.interface.declarations import noLongerProvides -from zope.interface.declarations import providedBy -from zope.interface.declarations import provider -from zope.interface.exceptions import Invalid -from zope.interface.interface import Attribute -from zope.interface.interface import interfacemethod -from zope.interface.interface import invariant -from zope.interface.interface import taggedValue -from zope.interface.interfaces import IInterfaceDeclaration - - -moduleProvides(IInterfaceDeclaration) - -__all__ = ('Interface', 'Attribute') + tuple(IInterfaceDeclaration) - -assert all(k in globals() for k in __all__) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index d612bfe..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/_compat.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/_compat.cpython-38.pyc deleted file mode 100644 index 800e2d5..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/_compat.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/_flatten.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/_flatten.cpython-38.pyc deleted file mode 100644 index b87c7cc..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/_flatten.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/adapter.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/adapter.cpython-38.pyc deleted file mode 100644 index 94a3752..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/adapter.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/advice.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/advice.cpython-38.pyc deleted file mode 100644 index b94aaa1..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/advice.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/declarations.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/declarations.cpython-38.pyc deleted file mode 100644 index fa9f65c..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/declarations.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/document.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/document.cpython-38.pyc deleted file mode 100644 index 0eb82d7..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/document.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/exceptions.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/exceptions.cpython-38.pyc deleted file mode 100644 index 8029cf7..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/exceptions.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/interface.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/interface.cpython-38.pyc deleted file mode 100644 index c7c7ab1..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/interface.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/interfaces.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/interfaces.cpython-38.pyc deleted file mode 100644 index 2926abd..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/interfaces.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/registry.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/registry.cpython-38.pyc deleted file mode 100644 index 921ba9e..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/registry.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/ro.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/ro.cpython-38.pyc deleted file mode 100644 index 13470e0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/ro.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/verify.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/verify.cpython-38.pyc deleted file mode 100644 index 3b3a226..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/__pycache__/verify.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/_compat.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/_compat.py deleted file mode 100644 index bc3f867..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/_compat.py +++ /dev/null @@ -1,136 +0,0 @@ -############################################################################## -# -# Copyright (c) 2006 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -""" -Support functions for dealing with differences in platforms, including Python -versions and implementations. - -This file should have no imports from the rest of zope.interface because it is -used during early bootstrapping. -""" -import os -import sys - - -def _normalize_name(name): - if isinstance(name, bytes): - name = str(name, 'ascii') - if isinstance(name, str): - return name - raise TypeError("name must be a string or ASCII-only bytes") - - -PYPY = hasattr(sys, 'pypy_version_info') - - -def _c_optimizations_required(): - """ - Return a true value if the C optimizations are required. - - This uses the ``PURE_PYTHON`` variable as documented in `_use_c_impl`. - """ - pure_env = os.environ.get('PURE_PYTHON') - require_c = pure_env == "0" - return require_c - - -def _c_optimizations_available(): - """ - Return the C optimization module, if available, otherwise - a false value. - - If the optimizations are required but not available, this - raises the ImportError. - - This does not say whether they should be used or not. - """ - catch = () if _c_optimizations_required() else (ImportError,) - try: - from zope.interface import _zope_interface_coptimizations as c_opt - return c_opt - except catch: # pragma: no cover (only Jython doesn't build extensions) - return False - - -def _c_optimizations_ignored(): - """ - The opposite of `_c_optimizations_required`. - """ - pure_env = os.environ.get('PURE_PYTHON') - return pure_env is not None and pure_env != "0" - - -def _should_attempt_c_optimizations(): - """ - Return a true value if we should attempt to use the C optimizations. - - This takes into account whether we're on PyPy and the value of the - ``PURE_PYTHON`` environment variable, as defined in `_use_c_impl`. - """ - is_pypy = hasattr(sys, 'pypy_version_info') - - if _c_optimizations_required(): - return True - if is_pypy: - return False - return not _c_optimizations_ignored() - - -def _use_c_impl(py_impl, name=None, globs=None): - """ - Decorator. Given an object implemented in Python, with a name like - ``Foo``, import the corresponding C implementation from - ``zope.interface._zope_interface_coptimizations`` with the name - ``Foo`` and use it instead. - - If the ``PURE_PYTHON`` environment variable is set to any value - other than ``"0"``, or we're on PyPy, ignore the C implementation - and return the Python version. If the C implementation cannot be - imported, return the Python version. If ``PURE_PYTHON`` is set to - 0, *require* the C implementation (let the ImportError propagate); - note that PyPy can import the C implementation in this case (and all - tests pass). - - In all cases, the Python version is kept available. in the module - globals with the name ``FooPy`` and the name ``FooFallback`` (both - conventions have been used; the C implementation of some functions - looks for the ``Fallback`` version, as do some of the Sphinx - documents). - - Example:: - - @_use_c_impl - class Foo(object): - ... - """ - name = name or py_impl.__name__ - globs = globs or sys._getframe(1).f_globals - - def find_impl(): - if not _should_attempt_c_optimizations(): - return py_impl - - c_opt = _c_optimizations_available() - if not c_opt: # pragma: no cover (Jython doesn't build extensions) - return py_impl - - __traceback_info__ = c_opt - return getattr(c_opt, name) - - c_impl = find_impl() - # Always make available by the FooPy name and FooFallback - # name (for testing and documentation) - globs[name + 'Py'] = py_impl - globs[name + 'Fallback'] = py_impl - - return c_impl diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/_flatten.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/_flatten.py deleted file mode 100644 index a2cb86b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/_flatten.py +++ /dev/null @@ -1,36 +0,0 @@ -############################################################################## -# -# Copyright (c) 2002 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Adapter-style interface registry - -See Adapter class. -""" -from zope.interface import Declaration - - -def _flatten(implements, include_None=0): - - try: - r = implements.flattened() - except AttributeError: - if implements is None: - r = () - else: - r = Declaration(implements).flattened() - - if not include_None: - return r - - r = list(r) - r.append(None) - return r diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/_zope_interface_coptimizations.c b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/_zope_interface_coptimizations.c deleted file mode 100644 index 45810ec..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/_zope_interface_coptimizations.c +++ /dev/null @@ -1,2669 +0,0 @@ -/*########################################################################### - # - # Copyright (c) 2003 Zope Foundation and Contributors. - # All Rights Reserved. - # - # This software is subject to the provisions of the Zope Public License, - # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. - # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED - # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS - # FOR A PARTICULAR PURPOSE. - # - ############################################################################*/ - -#include "Python.h" -#include "structmember.h" - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-parameter" -#pragma clang diagnostic ignored "-Wmissing-field-initializers" -#endif - -#define TYPE(O) ((PyTypeObject*)(O)) -#define OBJECT(O) ((PyObject*)(O)) -#define CLASSIC(O) ((PyClassObject*)(O)) -#ifndef Py_TYPE -#define Py_TYPE(o) ((o)->ob_type) -#endif - -#define PyNative_FromString PyUnicode_FromString - -#define ASSURE_DICT(N) \ - if (N == NULL) { \ - N = PyDict_New(); \ - if (N == NULL) \ - return NULL; \ - } - -/* - * Don't use heap-allocated types for Python < 3.11: the API needed - * to find the dynamic module, 'PyType_GetModuleByDef', was added then. - */ -#if PY_VERSION_HEX < 0x030b0000 -#define USE_STATIC_TYPES 1 -#define USE_HEAP_TYPES 0 -#else -#define USE_STATIC_TYPES 0 -#define USE_HEAP_TYPES 1 -#endif - -#define BASETYPE_FLAGS \ - Py_TPFLAGS_DEFAULT | \ - Py_TPFLAGS_BASETYPE | \ - Py_TPFLAGS_HAVE_GC - -#if PY_VERSION_HEX >= 0x030c0000 -/* Add MANAGED_WEAKREF flag for Python >= 3.12, and don't define - * the '.tp_weaklistoffset' slot. - * - * See: https://docs.python.org/3/c-api/typeobj.html - * #c.PyTypeObject.tp_weaklistoffset - */ -#define USE_EXPLICIT_WEAKREFLIST 0 -#define WEAKREFTYPE_FLAGS BASETYPE_FLAGS | Py_TPFLAGS_MANAGED_WEAKREF -#else -/* No MANAGED_WEAKREF flag for Python < 3.12, and therefore define - * the '.tp_weaklistoffset' slot, and the member whose offset it holds. - * - * See: https://docs.python.org/3/c-api/typeobj.html - * #c.PyTypeObject.tp_weaklistoffset - */ -#define USE_EXPLICIT_WEAKREFLIST 1 -#define WEAKREFTYPE_FLAGS BASETYPE_FLAGS -#endif - -/* Static strings, used to invoke PyObject_GetAttr (only in hot paths) */ -static PyObject *str__class__ = NULL; -static PyObject *str__conform__ = NULL; -static PyObject *str__dict__ = NULL; -static PyObject *str__module__ = NULL; -static PyObject *str__name__ = NULL; -static PyObject *str__providedBy__ = NULL; -static PyObject *str__provides__ = NULL; -static PyObject *str__self__ = NULL; -static PyObject *str_generation = NULL; -static PyObject *str_registry = NULL; -static PyObject *strro = NULL; - -/* Static strings, used to invoke PyObject_CallMethodObjArgs */ -static PyObject *str_call_conform = NULL; -static PyObject *str_uncached_lookup = NULL; -static PyObject *str_uncached_lookupAll = NULL; -static PyObject *str_uncached_subscriptions = NULL; -static PyObject *strchanged = NULL; -static PyObject *str__adapt__ = NULL; - -/* Static strings, used to invoke PyObject_GetItem - * - * We cannot use PyDict_GetItemString, because the '__dict__' we get - * from our types can be a 'types.mappingproxy', which causes a segfault. - */ -static PyObject* str__implemented__; - - -static int -define_static_strings() -{ - if (str__class__ != NULL) { - return 0; - } - -#define DEFINE_STATIC_STRING(S) \ - if (!(str##S = PyUnicode_FromString(#S))) \ - return -1 - - DEFINE_STATIC_STRING(__class__); - DEFINE_STATIC_STRING(__conform__); - DEFINE_STATIC_STRING(__dict__); - DEFINE_STATIC_STRING(__module__); - DEFINE_STATIC_STRING(__name__); - DEFINE_STATIC_STRING(__providedBy__); - DEFINE_STATIC_STRING(__provides__); - DEFINE_STATIC_STRING(__self__); - DEFINE_STATIC_STRING(_generation); - DEFINE_STATIC_STRING(_registry); - DEFINE_STATIC_STRING(ro); - DEFINE_STATIC_STRING(__implemented__); - DEFINE_STATIC_STRING(_call_conform); - DEFINE_STATIC_STRING(_uncached_lookup); - DEFINE_STATIC_STRING(_uncached_lookupAll); - DEFINE_STATIC_STRING(_uncached_subscriptions); - DEFINE_STATIC_STRING(changed); - DEFINE_STATIC_STRING(__adapt__); -#undef DEFINE_STATIC_STRING - - return 0; -} - -/* Public module-scope functions, forward-declared here for type methods. */ -static PyObject *implementedBy(PyObject* module, PyObject *cls); -static PyObject *getObjectSpecification(PyObject *module, PyObject *ob); -static PyObject *providedBy(PyObject *module, PyObject *ob); - -/* - * Utility functions, forward-declared here for type methods. - */ -static PyObject* _get_module(PyTypeObject *typeobj); -static PyObject* _get_adapter_hooks(PyTypeObject *typeobj); -static PyTypeObject* _get_specification_base_class(PyTypeObject *typeobj); -static PyTypeObject* _get_interface_base_class(PyTypeObject *typeobj); - -#if USE_STATIC_TYPES -/* - * Global used by static IB__adapt - */ -static PyObject* adapter_hooks = NULL; - -/* - * Globals imported from 'zope.interface.declarations' - */ -static int imported_declarations = 0; -static PyObject* BuiltinImplementationSpecifications; -static PyObject* empty; -static PyObject* fallback; -static PyTypeObject *Implements; - -/* Import zope.interface.declarations and store results in global statics. - * - * Static alternative to '_zic_state_load_declarations' below. - */ -static int -import_declarations(void) -{ - PyObject *declarations, *i; - - declarations = PyImport_ImportModule("zope.interface.declarations"); - if (declarations == NULL) { return -1; } - - BuiltinImplementationSpecifications = PyObject_GetAttrString( - declarations, "BuiltinImplementationSpecifications"); - if (BuiltinImplementationSpecifications == NULL) { return -1; } - - empty = PyObject_GetAttrString(declarations, "_empty"); - if (empty == NULL) { return -1; } - - fallback = PyObject_GetAttrString(declarations, "implementedByFallback"); - if (fallback == NULL) { return -1;} - - i = PyObject_GetAttrString(declarations, "Implements"); - if (i == NULL) { return -1; } - - if (! PyType_Check(i)) { - PyErr_SetString( - PyExc_TypeError, - "zope.interface.declarations.Implements is not a type"); - return -1; - } - - Implements = (PyTypeObject *)i; - - Py_DECREF(declarations); - - imported_declarations = 1; - return 0; -} - -#endif - -/* - * SpecificationBase class - */ -typedef struct -{ - PyObject_HEAD - /* - In the past, these fields were stored in the __dict__ - and were technically allowed to contain any Python object, though - other type checks would fail or fall back to generic code paths if - they didn't have the expected type. We preserve that behaviour and don't - make any assumptions about contents. - */ - PyObject* _implied; -#if USE_EXPLICIT_WEAKREFLIST - PyObject* weakreflist; -#endif - /* - The remainder aren't used in C code but must be stored here - to prevent instance layout conflicts. - */ - PyObject* _dependents; - PyObject* _bases; - PyObject* _v_attrs; - PyObject* __iro__; - PyObject* __sro__; -} SB; - -/* - We know what the fields are *supposed* to define, but - they could have anything, so we need to traverse them. -*/ -static int -SB_traverse(SB* self, visitproc visit, void* arg) -{ -/* Visit our 'tp_type' only on Python >= 3.9, per - * https://docs.python.org/3/howto/isolating-extensions.html - * #tp-traverse-in-python-3-8-and-lower - */ -#if USE_HEAP_TYPES && PY_VERSION_HEX > 0x03090000 - Py_VISIT(Py_TYPE(self)); -#endif - Py_VISIT(self->_implied); - Py_VISIT(self->_dependents); - Py_VISIT(self->_bases); - Py_VISIT(self->_v_attrs); - Py_VISIT(self->__iro__); - Py_VISIT(self->__sro__); - return 0; -} - -static int -SB_clear(SB* self) -{ - Py_CLEAR(self->_implied); - Py_CLEAR(self->_dependents); - Py_CLEAR(self->_bases); - Py_CLEAR(self->_v_attrs); - Py_CLEAR(self->__iro__); - Py_CLEAR(self->__sro__); - return 0; -} - -static void -SB_dealloc(SB* self) -{ - PyObject_GC_UnTrack((PyObject*)self); - PyObject_ClearWeakRefs(OBJECT(self)); - PyTypeObject* tp = Py_TYPE(self); - SB_clear(self); - tp->tp_free(OBJECT(self)); -#if USE_HEAP_TYPES - Py_DECREF(tp); -#endif -} - -static char SB_extends__doc__[] = - "Test whether a specification is or extends another"; - -static PyObject* -SB_extends(SB* self, PyObject* other) -{ - PyObject* implied; - - implied = self->_implied; - if (implied == NULL) { - return NULL; - } - - if (PyDict_GetItem(implied, other) != NULL) - Py_RETURN_TRUE; - Py_RETURN_FALSE; -} - -static PyObject* -SB__call__(SB* self, PyObject* args, PyObject* kw) -{ - PyObject* spec; - - if (!PyArg_ParseTuple(args, "O", &spec)) - return NULL; - return SB_extends(self, spec); -} - -static char SB_providedBy__doc__[] = - "Test whether an interface is implemented by the specification"; - -static PyObject* -SB_providedBy(PyObject* self, PyObject* ob) -{ - PyObject *decl; - PyObject *item; - PyObject *module; - PyTypeObject *specification_base_class; - - module = _get_module(Py_TYPE(self)); - specification_base_class = _get_specification_base_class(Py_TYPE(self)); - - decl = providedBy(module, ob); - if (decl == NULL) - return NULL; - - if (PyObject_TypeCheck(decl, specification_base_class)) - item = SB_extends((SB*)decl, self); - else - /* decl is probably a security proxy. We have to go the long way - around. - */ - item = PyObject_CallFunctionObjArgs(decl, self, NULL); - - Py_DECREF(decl); - return item; -} - -static char SB_implementedBy__doc__[] = - "Test whether the specification is implemented by a class or factory.\n" - "Raise TypeError if argument is neither a class nor a callable."; - -static PyObject* -SB_implementedBy(PyObject* self, PyObject* cls) -{ - PyObject *decl; - PyObject *item; - PyObject *module; - PyTypeObject *specification_base_class; - - module = _get_module(Py_TYPE(self)); - specification_base_class = _get_specification_base_class(Py_TYPE(self)); - - decl = implementedBy(module, cls); - if (decl == NULL) - return NULL; - - if (PyObject_TypeCheck(decl, specification_base_class)) - item = SB_extends((SB*)decl, self); - else - item = PyObject_CallFunctionObjArgs(decl, self, NULL); - - Py_DECREF(decl); - return item; -} - -static struct PyMethodDef SB_methods[] = { - { "providedBy", - (PyCFunction)SB_providedBy, - METH_O, - SB_providedBy__doc__ }, - { "implementedBy", - (PyCFunction)SB_implementedBy, - METH_O, - SB_implementedBy__doc__ }, - { "isOrExtends", - (PyCFunction)SB_extends, - METH_O, - SB_extends__doc__ }, - - { NULL, NULL } /* sentinel */ -}; - -static PyMemberDef SB_members[] = { - { "_implied", T_OBJECT_EX, offsetof(SB, _implied), 0, "" }, - { "_dependents", T_OBJECT_EX, offsetof(SB, _dependents), 0, "" }, - { "_bases", T_OBJECT_EX, offsetof(SB, _bases), 0, "" }, - { "_v_attrs", T_OBJECT_EX, offsetof(SB, _v_attrs), 0, "" }, - { "__iro__", T_OBJECT_EX, offsetof(SB, __iro__), 0, "" }, - { "__sro__", T_OBJECT_EX, offsetof(SB, __sro__), 0, "" }, -#if USE_EXPLICIT_WEAKREFLIST - { "__weaklistoffset__", T_PYSSIZET, offsetof(SB, weakreflist), READONLY, "" }, -#endif - { NULL }, -}; - -static char SB__name__[] = "_zope_interface_coptimizations.SpecificationBase"; -static char SB__doc__[] = "Base type for Specification objects"; - -#if USE_STATIC_TYPES - -/* - * Static type: SpecificationBase - */ - -static PyTypeObject SB_type_def = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = SB__name__, - .tp_doc = SB__doc__, - .tp_basicsize = sizeof(SB), - .tp_flags = WEAKREFTYPE_FLAGS, - .tp_call = (ternaryfunc)SB__call__, - .tp_traverse = (traverseproc)SB_traverse, - .tp_clear = (inquiry)SB_clear, - .tp_dealloc = (destructor)SB_dealloc, -#if USE_EXPLICIT_WEAKREFLIST - .tp_weaklistoffset = offsetof(SB, weakreflist), -#endif - .tp_methods = SB_methods, - .tp_members = SB_members, -}; - -#else - -/* - * Heap-based type: SpecificationBase - */ -static PyType_Slot SB_type_slots[] = { - {Py_tp_doc, SB__doc__}, - {Py_tp_call, SB__call__}, - {Py_tp_traverse, SB_traverse}, - {Py_tp_clear, SB_clear}, - {Py_tp_dealloc, SB_dealloc}, - {Py_tp_methods, SB_methods}, - {Py_tp_members, SB_members}, - {0, NULL} -}; - -static PyType_Spec SB_type_spec = { - .name = SB__name__, - .basicsize = sizeof(SB), - .flags = WEAKREFTYPE_FLAGS, - .slots = SB_type_slots -}; - -#endif - -/* - * ObjectSpecificationDescriptor class - */ -#if USE_HEAP_TYPES -static int -OSD_traverse(PyObject* self, visitproc visit, void* arg) -{ - Py_VISIT(Py_TYPE(self)); - return 0; -} - -static void -OSD_dealloc(PyObject* self) -{ - PyObject_GC_UnTrack(self); - PyTypeObject *tp = Py_TYPE(self); - tp->tp_free(OBJECT(self)); - Py_DECREF(tp); -} -#endif - -static PyObject* -OSD_descr_get(PyObject* self, PyObject* inst, PyObject* cls) -{ - PyObject* provides; - PyObject *module; - - module = _get_module(Py_TYPE(self)); - - if (inst == NULL) { - return getObjectSpecification(module, cls); - } - - provides = PyObject_GetAttr(inst, str__provides__); - /* Return __provides__ if we got it, or return NULL and propagate - * non-AttributeError. */ - if (provides != NULL || !PyErr_ExceptionMatches(PyExc_AttributeError)) { - return provides; - } - - PyErr_Clear(); - - return implementedBy(module, cls); -} - -static char OSD__name__[] = ( - "_zope_interface_coptimizations.ObjectSpecificationDescriptor"); -static char OSD__doc__[] = "Object Specification Descriptor"; - -#if USE_STATIC_TYPES - -/* - * Static type: ObjectSpecificationDescriptor - */ - -static PyTypeObject OSD_type_def = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = OSD__name__, - .tp_doc = OSD__doc__, - /* No GC for the static version */ - .tp_flags = Py_TPFLAGS_DEFAULT | - Py_TPFLAGS_BASETYPE, - .tp_descr_get = (descrgetfunc)OSD_descr_get, - /*.tp_traverse, = OSD_traverse}, not reqd for static */ - /*.tp_dealloc, = OSD_dealloc}, not reqd for static */ -}; - -#else - -/* - * Heap type: ObjectSpecificationDescriptor - */ -static PyType_Slot OSD_type_slots[] = { - {Py_tp_doc, OSD__doc__}, - {Py_tp_descr_get, OSD_descr_get}, - {Py_tp_traverse, OSD_traverse}, - {Py_tp_dealloc, OSD_dealloc}, - {0, NULL} -}; - -static PyType_Spec OSD_type_spec = { - .name = OSD__name__, - .basicsize = 0, - .flags = BASETYPE_FLAGS, - .slots = OSD_type_slots -}; - -#endif - -/* - * ClassProvidesBase class - */ -typedef struct -{ - SB spec; - /* These members are handled generically, as for SB members. */ - PyObject* _cls; - PyObject* _implements; -} CPB; - -static int -CPB_traverse(CPB* self, visitproc visit, void* arg) -{ - Py_VISIT(self->_cls); - Py_VISIT(self->_implements); - return SB_traverse((SB*)self, visit, arg); -} - -static int -CPB_clear(CPB* self) -{ - Py_CLEAR(self->_cls); - Py_CLEAR(self->_implements); - return SB_clear((SB*)self); -} - -static void -CPB_dealloc(CPB* self) -{ - PyObject_GC_UnTrack((PyObject*)self); - CPB_clear(self); - SB_dealloc((SB*)self); /* handles decrefing tp */ -} - -static PyObject* -CPB_descr_get(CPB* self, PyObject* inst, PyObject* cls) -{ - PyObject* implements; - - if (self->_cls == NULL) - return NULL; - - if (cls == self->_cls) { - if (inst == NULL) { - Py_INCREF(self); - return OBJECT(self); - } - - implements = self->_implements; - Py_XINCREF(implements); - return implements; - } - - PyErr_SetString(PyExc_AttributeError, "__provides__"); - return NULL; -} - -static PyMemberDef CPB_members[] = { - { "_cls", T_OBJECT_EX, offsetof(CPB, _cls), 0, "Defining class." }, - { "_implements", - T_OBJECT_EX, - offsetof(CPB, _implements), - 0, - "Result of implementedBy." }, - { NULL } -}; - -static char CPB__name__[] = "_zope_interface_coptimizations.ClassProvidesBase"; -static char CPB__doc__[] = "C Base class for ClassProvides"; - -#if USE_STATIC_TYPES - -/* - * Static type: ClassProvidesBase - */ - -static PyTypeObject CPB_type_def = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = CPB__name__, - .tp_doc = CPB__doc__, - .tp_base = &SB_type_def, - .tp_basicsize = sizeof(CPB), - .tp_flags = BASETYPE_FLAGS, - .tp_descr_get = (descrgetfunc)CPB_descr_get, - .tp_traverse = (traverseproc)CPB_traverse, - .tp_clear = (inquiry)CPB_clear, - .tp_dealloc = (destructor)CPB_dealloc, - .tp_members = CPB_members, -}; - -#else - -/* - * Heap type: ClassProvidesBase - */ -static PyType_Slot CPB_type_slots[] = { - {Py_tp_doc, CPB__doc__}, - {Py_tp_descr_get, CPB_descr_get}, - {Py_tp_traverse, CPB_traverse}, - {Py_tp_clear, CPB_clear}, - {Py_tp_dealloc, CPB_dealloc}, - {Py_tp_members, CPB_members}, - /* tp_base cannot be set as a slot -- pass to PyType_FromModuleAndSpec */ - {0, NULL} -}; - -static PyType_Spec CPB_type_spec = { - .name = CPB__name__, - .basicsize = sizeof(CPB), - .flags = BASETYPE_FLAGS, - .slots = CPB_type_slots -}; - -#endif - -/* - * InterfaceBase class - */ - -typedef struct -{ - SB spec; - PyObject* __name__; - PyObject* __module__; - Py_hash_t _v_cached_hash; -} IB; - -static int -IB_traverse(IB* self, visitproc visit, void* arg) -{ - Py_VISIT(self->__name__); - Py_VISIT(self->__module__); - return SB_traverse((SB*)self, visit, arg); -} - -static int -IB_clear(IB* self) -{ - Py_CLEAR(self->__name__); - Py_CLEAR(self->__module__); - return SB_clear((SB*)self); -} - -static void -IB_dealloc(IB* self) -{ - PyObject_GC_UnTrack((PyObject*)self); - IB_clear(self); - SB_dealloc((SB*)self); /* handles decrefing tp */ -} - -static int -IB__init__(IB* self, PyObject* args, PyObject* kwargs) -{ - static char* kwlist[] = { "__name__", "__module__", NULL }; - PyObject* module = NULL; - PyObject* name = NULL; - - if (!PyArg_ParseTupleAndKeywords( - args, kwargs, "|OO:InterfaceBase.__init__", kwlist, &name, &module)) { - return -1; - } - IB_clear(self); - self->__module__ = module ? module : Py_None; - Py_INCREF(self->__module__); - self->__name__ = name ? name : Py_None; - Py_INCREF(self->__name__); - return 0; -} - -/* - def __adapt__(self, obj): - """Adapt an object to the receiver - """ - if self.providedBy(obj): - return obj - - for hook in adapter_hooks: - adapter = hook(self, obj) - if adapter is not None: - return adapter - - -*/ -const char IB__adapt____doc__[] = "Adapt an object to the receiver"; - -static PyObject* -IB__adapt__(PyObject* self, PyObject* obj) -{ - PyObject *decl; - PyObject *args; - PyObject *adapter; - PyObject *module; - PyObject *adapter_hooks; - PyTypeObject *specification_base_class; - int implements; - int i; - int l; - - module = _get_module(Py_TYPE(self)); - - decl = providedBy(module, obj); - if (decl == NULL) - return NULL; - - specification_base_class = _get_specification_base_class(Py_TYPE(self)); - - if (PyObject_TypeCheck(decl, specification_base_class)) { - PyObject* implied; - - implied = ((SB*)decl)->_implied; - if (implied == NULL) { - Py_DECREF(decl); - return NULL; - } - - implements = PyDict_GetItem(implied, self) != NULL; - Py_DECREF(decl); - } else { - /* decl is probably a security proxy. We have to go the long way - around. - */ - PyObject* r; - r = PyObject_CallFunctionObjArgs(decl, self, NULL); - Py_DECREF(decl); - if (r == NULL) - return NULL; - implements = PyObject_IsTrue(r); - Py_DECREF(r); - } - - if (implements) { - Py_INCREF(obj); - return obj; - } - - args = PyTuple_New(2); - if (args == NULL) { return NULL; } - - Py_INCREF(self); - PyTuple_SET_ITEM(args, 0, self); - - Py_INCREF(obj); - PyTuple_SET_ITEM(args, 1, obj); - - adapter_hooks = _get_adapter_hooks(Py_TYPE(self)); - l = PyList_GET_SIZE(adapter_hooks); - for (i = 0; i < l; i++) { - adapter = PyObject_CallObject(PyList_GET_ITEM(adapter_hooks, i), args); - if (adapter == NULL || adapter != Py_None) { - Py_DECREF(args); - return adapter; - } - Py_DECREF(adapter); - } - - Py_DECREF(args); - - Py_INCREF(Py_None); - return Py_None; -} - -/* - def __call__(self, obj, alternate=_marker): - try: - conform = obj.__conform__ - except AttributeError: # pylint:disable=bare-except - conform = None - - if conform is not None: - adapter = self._call_conform(conform) - if adapter is not None: - return adapter - - adapter = self.__adapt__(obj) - - if adapter is not None: - return adapter - if alternate is not _marker: - return alternate - raise TypeError("Could not adapt", obj, self) - -*/ -static PyObject* -IB__call__(PyObject* self, PyObject* args, PyObject* kwargs) -{ - PyObject *conform, *obj, *alternate, *adapter; - static char* kwlist[] = { "obj", "alternate", NULL }; - conform = obj = alternate = adapter = NULL; - - if (!PyArg_ParseTupleAndKeywords( - args, kwargs, "O|O", kwlist, &obj, &alternate)) - return NULL; - - conform = PyObject_GetAttr(obj, str__conform__); - if (conform == NULL) { - if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { - /* Propagate non-AttributeErrors */ - return NULL; - } - PyErr_Clear(); - - Py_INCREF(Py_None); - conform = Py_None; - } - - if (conform != Py_None) { - adapter = - PyObject_CallMethodObjArgs(self, str_call_conform, conform, NULL); - Py_DECREF(conform); - if (adapter == NULL || adapter != Py_None) - return adapter; - Py_DECREF(adapter); - } else { - Py_DECREF(conform); - } - - /* We differ from the Python code here. For speed, instead of always calling - self.__adapt__(), we check to see if the type has defined it. Checking in - the dict for __adapt__ isn't sufficient because there's no cheap way to - tell if it's the __adapt__ that InterfaceBase itself defines (our type - will *never* be InterfaceBase, we're always subclassed by - InterfaceClass). Instead, we cooperate with InterfaceClass in Python to - set a flag in a new subclass when this is necessary. */ - if (PyDict_GetItemString(self->ob_type->tp_dict, "_CALL_CUSTOM_ADAPT")) { - /* Doesn't matter what the value is. Simply being present is enough. */ - adapter = PyObject_CallMethodObjArgs(self, str__adapt__, obj, NULL); - } else { - adapter = IB__adapt__(self, obj); - } - - if (adapter == NULL || adapter != Py_None) { - return adapter; - } - Py_DECREF(adapter); - - if (alternate != NULL) { - Py_INCREF(alternate); - return alternate; - } - - adapter = Py_BuildValue("sOO", "Could not adapt", obj, self); - if (adapter != NULL) { - PyErr_SetObject(PyExc_TypeError, adapter); - Py_DECREF(adapter); - } - return NULL; -} - -static Py_hash_t -IB__hash__(IB* self) -{ - PyObject* tuple; - if (!self->__module__) { - PyErr_SetString(PyExc_AttributeError, "__module__"); - return -1; - } - if (!self->__name__) { - PyErr_SetString(PyExc_AttributeError, "__name__"); - return -1; - } - - if (self->_v_cached_hash) { - return self->_v_cached_hash; - } - - tuple = PyTuple_Pack(2, self->__name__, self->__module__); - if (!tuple) { - return -1; - } - self->_v_cached_hash = PyObject_Hash(tuple); - Py_CLEAR(tuple); - return self->_v_cached_hash; -} - -static PyObject* -IB_richcompare(IB* self, PyObject* other, int op) -{ - PyObject* othername; - PyObject* othermod; - PyObject* oresult; - PyTypeObject* interface_base_class; - IB* otherib; - int result; - - otherib = NULL; - oresult = othername = othermod = NULL; - - if (OBJECT(self) == other) { - switch (op) { - case Py_EQ: - case Py_LE: - case Py_GE: - Py_RETURN_TRUE; - break; - case Py_NE: - Py_RETURN_FALSE; - } - } - - if (other == Py_None) { - switch (op) { - case Py_LT: - case Py_LE: - case Py_NE: - Py_RETURN_TRUE; - default: - Py_RETURN_FALSE; - } - } - - interface_base_class = _get_interface_base_class(Py_TYPE(self)); - if (interface_base_class == NULL) { - oresult = Py_NotImplemented; - goto cleanup; - } - - if (PyObject_TypeCheck(other, interface_base_class)) { - // This branch borrows references. No need to clean - // up if otherib is not null. - otherib = (IB*)other; - othername = otherib->__name__; - othermod = otherib->__module__; - } else { - othername = PyObject_GetAttr(other, str__name__); - if (othername) { - othermod = PyObject_GetAttr(other, str__module__); - } - if (!othername || !othermod) { - if (PyErr_Occurred() && - PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Clear(); - oresult = Py_NotImplemented; - } - goto cleanup; - } - } -#if 0 -// This is the simple, straightforward version of what Python does. - PyObject* pt1 = PyTuple_Pack(2, self->__name__, self->__module__); - PyObject* pt2 = PyTuple_Pack(2, othername, othermod); - oresult = PyObject_RichCompare(pt1, pt2, op); -#endif - - // tuple comparison is decided by the first non-equal element. - result = PyObject_RichCompareBool(self->__name__, othername, Py_EQ); - if (result == 0) { - result = PyObject_RichCompareBool(self->__name__, othername, op); - } else if (result == 1) { - result = PyObject_RichCompareBool(self->__module__, othermod, op); - } - // If either comparison failed, we have an error set. - // Leave oresult NULL so we raise it. - if (result == -1) { - goto cleanup; - } - - oresult = result ? Py_True : Py_False; - -cleanup: - Py_XINCREF(oresult); - - if (!otherib) { - Py_XDECREF(othername); - Py_XDECREF(othermod); - } - return oresult; -} - -static PyMemberDef IB_members[] = { - { "__name__", T_OBJECT_EX, offsetof(IB, __name__), 0, "" }, - // The redundancy between __module__ and __ibmodule__ is because - // __module__ is often shadowed by subclasses. - { "__module__", T_OBJECT_EX, offsetof(IB, __module__), READONLY, "" }, - { "__ibmodule__", T_OBJECT_EX, offsetof(IB, __module__), 0, "" }, - { NULL } -}; - -static struct PyMethodDef IB_methods[] = { - { "__adapt__", (PyCFunction)IB__adapt__, METH_O, IB__adapt____doc__}, - { NULL, NULL } /* sentinel */ -}; - -static char IB__name__[] ="_zope_interface_coptimizations.InterfaceBase"; -static char IB__doc__[] = ( - "Interface base type providing __call__ and __adapt__" -); - -#if USE_STATIC_TYPES - -/* - * Static type: InterfaceBase - */ - -static PyTypeObject IB_type_def = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = IB__name__, - .tp_doc = IB__doc__, - .tp_base = &SB_type_def, - .tp_basicsize = sizeof(IB), - .tp_flags = BASETYPE_FLAGS, - .tp_init = (initproc)IB__init__, - .tp_hash = (hashfunc)IB__hash__, - .tp_richcompare = (richcmpfunc)IB_richcompare, - .tp_call = (ternaryfunc)IB__call__, - .tp_traverse = (traverseproc)IB_traverse, - .tp_clear = (inquiry)IB_clear, - .tp_dealloc = (destructor)IB_dealloc, - .tp_methods = IB_methods, - .tp_members = IB_members, -}; - -#else - -/* - * Heap type: InterfaceBase - */ -static PyType_Slot IB_type_slots[] = { - {Py_tp_doc, IB__doc__}, - {Py_tp_init, IB__init__}, - {Py_tp_hash, IB__hash__}, - {Py_tp_richcompare, IB_richcompare}, - {Py_tp_call, IB__call__}, - {Py_tp_traverse, IB_traverse}, - {Py_tp_clear, IB_clear}, - {Py_tp_dealloc, IB_dealloc}, - {Py_tp_methods, IB_methods}, - {Py_tp_members, IB_members}, - /* tp_base cannot be set as a slot -- pass to PyType_FromModuleAndSpec */ - {0, NULL} -}; - -static PyType_Spec IB_type_spec = { - .name = IB__name__, - .basicsize = sizeof(IB), - .flags = BASETYPE_FLAGS, - .slots = IB_type_slots -}; - -#endif - -/* - * LookupBase class - */ -typedef struct -{ - PyObject_HEAD - PyObject* _cache; - PyObject* _mcache; - PyObject* _scache; -} LB; - -static int -LB_traverse(LB* self, visitproc visit, void* arg) -{ -/* Visit our 'tp_type' only on Python >= 3.9, per - * https://docs.python.org/3/howto/isolating-extensions.html - * #tp-traverse-in-python-3-8-and-lower - */ -#if USE_HEAP_TYPES && PY_VERSION_HEX > 0x03090000 - Py_VISIT(Py_TYPE(self)); -#endif - Py_VISIT(self->_cache); - Py_VISIT(self->_mcache); - Py_VISIT(self->_scache); - return 0; -} - -static int -LB_clear(LB* self) -{ - Py_CLEAR(self->_cache); - Py_CLEAR(self->_mcache); - Py_CLEAR(self->_scache); - return 0; -} - -static void -LB_dealloc(LB* self) -{ - PyObject_GC_UnTrack((PyObject*)self); - PyTypeObject* tp = Py_TYPE(self); - LB_clear(self); - tp->tp_free((PyObject*)self); -#if USE_HEAP_TYPES - Py_DECREF(tp); -#endif -} - -/* - def changed(self, ignored=None): - self._cache.clear() - self._mcache.clear() - self._scache.clear() -*/ -static PyObject* -LB_changed(LB* self, PyObject* ignored) -{ - LB_clear(self); - Py_INCREF(Py_None); - return Py_None; -} - -/* - def _getcache(self, provided, name): - cache = self._cache.get(provided) - if cache is None: - cache = {} - self._cache[provided] = cache - if name: - c = cache.get(name) - if c is None: - c = {} - cache[name] = c - cache = c - return cache -*/ -static PyObject* -_subcache(PyObject* cache, PyObject* key) -{ - PyObject* subcache; - - subcache = PyDict_GetItem(cache, key); - if (subcache == NULL) { - int status; - - subcache = PyDict_New(); - if (subcache == NULL) - return NULL; - status = PyDict_SetItem(cache, key, subcache); - Py_DECREF(subcache); - if (status < 0) - return NULL; - } - - return subcache; -} - -static PyObject* -_getcache(LB* self, PyObject* provided, PyObject* name) -{ - PyObject* cache; - - ASSURE_DICT(self->_cache); - - cache = _subcache(self->_cache, provided); - if (cache == NULL) - return NULL; - - if (name != NULL && PyObject_IsTrue(name)) - cache = _subcache(cache, name); - - return cache; -} - -/* - def lookup(self, required, provided, name=u'', default=None): - cache = self._getcache(provided, name) - if len(required) == 1: - result = cache.get(required[0], _not_in_mapping) - else: - result = cache.get(tuple(required), _not_in_mapping) - - if result is _not_in_mapping: - result = self._uncached_lookup(required, provided, name) - if len(required) == 1: - cache[required[0]] = result - else: - cache[tuple(required)] = result - - if result is None: - return default - - return result -*/ - -static PyObject* -_lookup(LB* self, - PyObject* required, - PyObject* provided, - PyObject* name, - PyObject* default_) -{ - PyObject *result, *key, *cache; - result = key = cache = NULL; - if (name && !PyUnicode_Check(name)) { - PyErr_SetString(PyExc_ValueError, "name is not a string"); - return NULL; - } - - /* If `required` is a lazy sequence, it could have arbitrary side-effects, - such as clearing our caches. So we must not retrieve the cache until - after resolving it. */ - required = PySequence_Tuple(required); - if (required == NULL) - return NULL; - - cache = _getcache(self, provided, name); - if (cache == NULL) - return NULL; - - if (PyTuple_GET_SIZE(required) == 1) - key = PyTuple_GET_ITEM(required, 0); - else - key = required; - - result = PyDict_GetItem(cache, key); - if (result == NULL) { - int status; - - result = PyObject_CallMethodObjArgs( - OBJECT(self), str_uncached_lookup, required, provided, name, NULL); - if (result == NULL) { - Py_DECREF(required); - return NULL; - } - status = PyDict_SetItem(cache, key, result); - Py_DECREF(required); - if (status < 0) { - Py_DECREF(result); - return NULL; - } - } else { - Py_INCREF(result); - Py_DECREF(required); - } - - if (result == Py_None && default_ != NULL) { - Py_DECREF(Py_None); - Py_INCREF(default_); - return default_; - } - - return result; -} - -static PyObject* -LB_lookup(LB* self, PyObject* args, PyObject* kwds) -{ - static char* kwlist[] = { "required", "provided", "name", "default", NULL }; - PyObject *required, *provided, *name = NULL, *default_ = NULL; - - if (!PyArg_ParseTupleAndKeywords(args, - kwds, - "OO|OO:LookupBase.lookup", - kwlist, - &required, - &provided, - &name, - &default_)) - return NULL; - - return _lookup(self, required, provided, name, default_); -} - -/* - def lookup1(self, required, provided, name=u'', default=None): - cache = self._getcache(provided, name) - result = cache.get(required, _not_in_mapping) - if result is _not_in_mapping: - return self.lookup((required, ), provided, name, default) - - if result is None: - return default - - return result -*/ -static PyObject* -_lookup1(LB* self, - PyObject* required, - PyObject* provided, - PyObject* name, - PyObject* default_) -{ - PyObject *result, *cache; - - if (name && !PyUnicode_Check(name)) { - PyErr_SetString(PyExc_ValueError, "name is not a string"); - return NULL; - } - - cache = _getcache(self, provided, name); - if (cache == NULL) - return NULL; - - result = PyDict_GetItem(cache, required); - if (result == NULL) { - PyObject* tup; - - tup = PyTuple_New(1); - if (tup == NULL) - return NULL; - Py_INCREF(required); - PyTuple_SET_ITEM(tup, 0, required); - result = _lookup(self, tup, provided, name, default_); - Py_DECREF(tup); - } else { - if (result == Py_None && default_ != NULL) { - result = default_; - } - Py_INCREF(result); - } - - return result; -} -static PyObject* -LB_lookup1(LB* self, PyObject* args, PyObject* kwds) -{ - static char* kwlist[] = { "required", "provided", "name", "default", NULL }; - PyObject *required, *provided, *name = NULL, *default_ = NULL; - - if (!PyArg_ParseTupleAndKeywords(args, - kwds, - "OO|OO:LookupBase.lookup1", - kwlist, - &required, - &provided, - &name, - &default_)) - return NULL; - - return _lookup1(self, required, provided, name, default_); -} - -/* - def adapter_hook(self, provided, object, name=u'', default=None): - required = providedBy(object) - cache = self._getcache(provided, name) - factory = cache.get(required, _not_in_mapping) - if factory is _not_in_mapping: - factory = self.lookup((required, ), provided, name) - - if factory is not None: - if isinstance(object, super): - object = object.__self__ - result = factory(object) - if result is not None: - return result - - return default -*/ -static PyObject* -_adapter_hook(LB* self, - PyObject* provided, - PyObject* object, - PyObject* name, - PyObject* default_) -{ - PyObject *required; - PyObject *factory; - PyObject *result; - PyObject *module; - - module = _get_module(Py_TYPE(self)); - - if (name && !PyUnicode_Check(name)) { - PyErr_SetString(PyExc_ValueError, "name is not a string"); - return NULL; - } - - required = providedBy(module, object); - if (required == NULL) - return NULL; - - factory = _lookup1(self, required, provided, name, Py_None); - Py_DECREF(required); - if (factory == NULL) - return NULL; - - if (factory != Py_None) { - if (PyObject_TypeCheck(object, &PySuper_Type)) { - PyObject* self = PyObject_GetAttr(object, str__self__); - if (self == NULL) { - Py_DECREF(factory); - return NULL; - } - // Borrow the reference to self - Py_DECREF(self); - object = self; - } - result = PyObject_CallFunctionObjArgs(factory, object, NULL); - Py_DECREF(factory); - if (result == NULL || result != Py_None) - return result; - } else - result = factory; /* None */ - - if (default_ == NULL || default_ == result) /* No default specified, */ - return result; /* Return None. result is owned None */ - - Py_DECREF(result); - Py_INCREF(default_); - - return default_; -} - -static PyObject* -LB_adapter_hook(LB* self, PyObject* args, PyObject* kwds) -{ - static char* kwlist[] = { "provided", "object", "name", "default", NULL }; - PyObject *object, *provided, *name = NULL, *default_ = NULL; - - if (!PyArg_ParseTupleAndKeywords(args, - kwds, - "OO|OO:LookupBase.adapter_hook", - kwlist, - &provided, - &object, - &name, - &default_)) - return NULL; - - return _adapter_hook(self, provided, object, name, default_); -} - -static PyObject* -LB_queryAdapter(LB* self, PyObject* args, PyObject* kwds) -{ - static char* kwlist[] = { "object", "provided", "name", "default", NULL }; - PyObject *object, *provided, *name = NULL, *default_ = NULL; - - if (!PyArg_ParseTupleAndKeywords(args, - kwds, - "OO|OO:LookupBase.queryAdapter", - kwlist, - &object, - &provided, - &name, - &default_)) - return NULL; - - return _adapter_hook(self, provided, object, name, default_); -} - -/* - def lookupAll(self, required, provided): - cache = self._mcache.get(provided) - if cache is None: - cache = {} - self._mcache[provided] = cache - - required = tuple(required) - result = cache.get(required, _not_in_mapping) - if result is _not_in_mapping: - result = self._uncached_lookupAll(required, provided) - cache[required] = result - - return result -*/ -static PyObject* -_lookupAll(LB* self, PyObject* required, PyObject* provided) -{ - PyObject *cache, *result; - - /* resolve before getting cache. See note in _lookup. */ - required = PySequence_Tuple(required); - if (required == NULL) - return NULL; - - ASSURE_DICT(self->_mcache); - - cache = _subcache(self->_mcache, provided); - if (cache == NULL) - return NULL; - - result = PyDict_GetItem(cache, required); - if (result == NULL) { - int status; - - result = PyObject_CallMethodObjArgs( - OBJECT(self), str_uncached_lookupAll, required, provided, NULL); - if (result == NULL) { - Py_DECREF(required); - return NULL; - } - status = PyDict_SetItem(cache, required, result); - Py_DECREF(required); - if (status < 0) { - Py_DECREF(result); - return NULL; - } - } else { - Py_INCREF(result); - Py_DECREF(required); - } - - return result; -} - -static PyObject* -LB_lookupAll(LB* self, PyObject* args, PyObject* kwds) -{ - static char* kwlist[] = { "required", "provided", NULL }; - PyObject *required, *provided; - - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "OO:LookupBase.lookupAll", kwlist, &required, &provided)) - return NULL; - - return _lookupAll(self, required, provided); -} - -/* - def subscriptions(self, required, provided): - cache = self._scache.get(provided) - if cache is None: - cache = {} - self._scache[provided] = cache - - required = tuple(required) - result = cache.get(required, _not_in_mapping) - if result is _not_in_mapping: - result = self._uncached_subscriptions(required, provided) - cache[required] = result - - return result -*/ -static PyObject* -_subscriptions(LB* self, PyObject* required, PyObject* provided) -{ - PyObject *cache, *result; - - /* resolve before getting cache. See note in _lookup. */ - required = PySequence_Tuple(required); - if (required == NULL) - return NULL; - - ASSURE_DICT(self->_scache); - - cache = _subcache(self->_scache, provided); - if (cache == NULL) - return NULL; - - result = PyDict_GetItem(cache, required); - if (result == NULL) { - int status; - - result = PyObject_CallMethodObjArgs( - OBJECT(self), str_uncached_subscriptions, required, provided, NULL); - if (result == NULL) { - Py_DECREF(required); - return NULL; - } - status = PyDict_SetItem(cache, required, result); - Py_DECREF(required); - if (status < 0) { - Py_DECREF(result); - return NULL; - } - } else { - Py_INCREF(result); - Py_DECREF(required); - } - - return result; -} - -static PyObject* -LB_subscriptions(LB* self, PyObject* args, PyObject* kwds) -{ - static char* kwlist[] = { "required", "provided", NULL }; - PyObject *required, *provided; - - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "OO", kwlist, &required, &provided)) - return NULL; - - return _subscriptions(self, required, provided); -} - -static struct PyMethodDef LB_methods[] = { - { "changed", (PyCFunction)LB_changed, METH_O, "" }, - { "lookup", (PyCFunction)LB_lookup, METH_KEYWORDS | METH_VARARGS, "" }, - { "lookup1", - (PyCFunction)LB_lookup1, - METH_KEYWORDS | METH_VARARGS, - "" }, - { "queryAdapter", - (PyCFunction)LB_queryAdapter, - METH_KEYWORDS | METH_VARARGS, - "" }, - { "adapter_hook", - (PyCFunction)LB_adapter_hook, - METH_KEYWORDS | METH_VARARGS, - "" }, - { "lookupAll", - (PyCFunction)LB_lookupAll, - METH_KEYWORDS | METH_VARARGS, - "" }, - { "subscriptions", - (PyCFunction)LB_subscriptions, - METH_KEYWORDS | METH_VARARGS, - "" }, - { NULL, NULL } /* sentinel */ -}; - -static char LB__name__[] = "_zope_interface_coptimizations.LookupBase"; -static char LB__doc__[] = "Base class for adapter registries"; - - -#if USE_STATIC_TYPES - -/* - * Static type: LookupBase - */ - -static PyTypeObject LB_type_def = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = LB__name__, - .tp_doc = LB__doc__, - .tp_basicsize = sizeof(LB), - .tp_flags = BASETYPE_FLAGS, - .tp_traverse = (traverseproc)LB_traverse, - .tp_clear = (inquiry)LB_clear, - .tp_dealloc = (destructor)&LB_dealloc, - .tp_methods = LB_methods, -}; - -#else - -/* - * Heap type: LookupBase - */ -static PyType_Slot LB_type_slots[] = { - {Py_tp_doc, LB__doc__}, - {Py_tp_traverse, LB_traverse}, - {Py_tp_clear, LB_clear}, - {Py_tp_dealloc, LB_dealloc}, - {Py_tp_methods, LB_methods}, - {0, NULL} -}; - -static PyType_Spec LB_type_spec = { - .name = LB__name__, - .basicsize = sizeof(LB), - .flags = BASETYPE_FLAGS, - .slots = LB_type_slots -}; - -#endif - -typedef struct -{ - LB lookup; - PyObject* _verify_ro; - PyObject* _verify_generations; -} VB; - -static int -VB_traverse(VB* self, visitproc visit, void* arg) -{ - Py_VISIT(self->_verify_ro); - Py_VISIT(self->_verify_generations); - return LB_traverse((LB*)self, visit, arg); -} - -static int -VB_clear(VB* self) -{ - Py_CLEAR(self->_verify_generations); - Py_CLEAR(self->_verify_ro); - return LB_clear((LB*)self); -} - -static void -VB_dealloc(VB* self) -{ - PyObject_GC_UnTrack((PyObject*)self); - PyTypeObject *tp = Py_TYPE(self); - VB_clear(self); - tp->tp_free((PyObject*)self); -#if USE_HEAP_TYPES - Py_DECREF(tp); -#endif -} - -/* - def changed(self, originally_changed): - super(VerifyingBasePy, self).changed(originally_changed) - self._verify_ro = self._registry.ro[1:] - self._verify_generations = [r._generation for r in self._verify_ro] -*/ -static PyObject* -_generations_tuple(PyObject* ro) -{ - int i, l; - PyObject* generations; - - l = PyTuple_GET_SIZE(ro); - generations = PyTuple_New(l); - for (i = 0; i < l; i++) { - PyObject* generation; - - generation = PyObject_GetAttr(PyTuple_GET_ITEM(ro, i), str_generation); - if (generation == NULL) { - Py_DECREF(generations); - return NULL; - } - PyTuple_SET_ITEM(generations, i, generation); - } - - return generations; -} -static PyObject* -verify_changed(VB* self, PyObject* ignored) -{ - PyObject *t, *ro; - - VB_clear(self); - - t = PyObject_GetAttr(OBJECT(self), str_registry); - if (t == NULL) - return NULL; - - ro = PyObject_GetAttr(t, strro); - Py_DECREF(t); - if (ro == NULL) - return NULL; - - t = PyObject_CallFunctionObjArgs(OBJECT(&PyTuple_Type), ro, NULL); - Py_DECREF(ro); - if (t == NULL) - return NULL; - - ro = PyTuple_GetSlice(t, 1, PyTuple_GET_SIZE(t)); - Py_DECREF(t); - if (ro == NULL) - return NULL; - - self->_verify_generations = _generations_tuple(ro); - if (self->_verify_generations == NULL) { - Py_DECREF(ro); - return NULL; - } - - self->_verify_ro = ro; - - Py_INCREF(Py_None); - return Py_None; -} - -/* - def _verify(self): - if ([r._generation for r in self._verify_ro] - != self._verify_generations): - self.changed(None) -*/ -static int -_verify(VB* self) -{ - PyObject* changed_result; - - if (self->_verify_ro != NULL && self->_verify_generations != NULL) { - PyObject* generations; - int changed; - - generations = _generations_tuple(self->_verify_ro); - if (generations == NULL) - return -1; - - changed = PyObject_RichCompareBool( - self->_verify_generations, generations, Py_NE); - Py_DECREF(generations); - if (changed == -1) - return -1; - - if (changed == 0) - return 0; - } - - changed_result = - PyObject_CallMethodObjArgs(OBJECT(self), strchanged, Py_None, NULL); - if (changed_result == NULL) - return -1; - - Py_DECREF(changed_result); - return 0; -} - -static PyObject* -VB_lookup(VB* self, PyObject* args, PyObject* kwds) -{ - static char* kwlist[] = { "required", "provided", "name", "default", NULL }; - PyObject *required, *provided, *name = NULL, *default_ = NULL; - - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "OO|OO", kwlist, &required, &provided, &name, &default_)) - return NULL; - - if (_verify(self) < 0) - return NULL; - - return _lookup((LB*)self, required, provided, name, default_); -} - -static PyObject* -VB_lookup1(VB* self, PyObject* args, PyObject* kwds) -{ - static char* kwlist[] = { "required", "provided", "name", "default", NULL }; - PyObject *required, *provided, *name = NULL, *default_ = NULL; - - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "OO|OO", kwlist, &required, &provided, &name, &default_)) - return NULL; - - if (_verify(self) < 0) - return NULL; - - return _lookup1((LB*)self, required, provided, name, default_); -} - -static PyObject* -VB_adapter_hook(VB* self, PyObject* args, PyObject* kwds) -{ - static char* kwlist[] = { "provided", "object", "name", "default", NULL }; - PyObject *object, *provided, *name = NULL, *default_ = NULL; - - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "OO|OO", kwlist, &provided, &object, &name, &default_)) - return NULL; - - if (_verify(self) < 0) - return NULL; - - return _adapter_hook((LB*)self, provided, object, name, default_); -} - -static PyObject* -VB_queryAdapter(VB* self, PyObject* args, PyObject* kwds) -{ - static char* kwlist[] = { "object", "provided", "name", "default", NULL }; - PyObject *object, *provided, *name = NULL, *default_ = NULL; - - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "OO|OO", kwlist, &object, &provided, &name, &default_)) - return NULL; - - if (_verify(self) < 0) - return NULL; - - return _adapter_hook((LB*)self, provided, object, name, default_); -} - -static PyObject* -VB_lookupAll(VB* self, PyObject* args, PyObject* kwds) -{ - static char* kwlist[] = { "required", "provided", NULL }; - PyObject *required, *provided; - - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "OO", kwlist, &required, &provided)) - return NULL; - - if (_verify(self) < 0) - return NULL; - - return _lookupAll((LB*)self, required, provided); -} - -static PyObject* -VB_subscriptions(VB* self, PyObject* args, PyObject* kwds) -{ - static char* kwlist[] = { "required", "provided", NULL }; - PyObject *required, *provided; - - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "OO", kwlist, &required, &provided)) - return NULL; - - if (_verify(self) < 0) - return NULL; - - return _subscriptions((LB*)self, required, provided); -} - -static struct PyMethodDef VB_methods[] = { - { "changed", (PyCFunction)verify_changed, METH_O, "" }, - { "lookup", - (PyCFunction)VB_lookup, - METH_KEYWORDS | METH_VARARGS, - "" }, - { "lookup1", - (PyCFunction)VB_lookup1, - METH_KEYWORDS | METH_VARARGS, - "" }, - { "queryAdapter", - (PyCFunction)VB_queryAdapter, - METH_KEYWORDS | METH_VARARGS, - "" }, - { "adapter_hook", - (PyCFunction)VB_adapter_hook, - METH_KEYWORDS | METH_VARARGS, - "" }, - { "lookupAll", - (PyCFunction)VB_lookupAll, - METH_KEYWORDS | METH_VARARGS, - "" }, - { "subscriptions", - (PyCFunction)VB_subscriptions, - METH_KEYWORDS | METH_VARARGS, - "" }, - { NULL, NULL } /* sentinel */ -}; - -static char VB__name__[] = "_zope_interface_coptimizations.VerifyingBase"; -static char VB__doc__[] = "Base class for verifying adapter registries."; - -#if USE_STATIC_TYPES - -/* - * Static type: VerifyingBase - */ - -static PyTypeObject VB_type_def = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = VB__name__, - .tp_doc = VB__doc__, - .tp_base = &LB_type_def, - .tp_basicsize = sizeof(VB), - .tp_flags = BASETYPE_FLAGS, - .tp_traverse = (traverseproc)VB_traverse, - .tp_clear = (inquiry)VB_clear, - .tp_dealloc = (destructor)&VB_dealloc, - .tp_methods = VB_methods, -}; - - -#else - -/* - * Heap type: VerifyingBase - */ -static PyType_Slot VB_type_slots[] = { - {Py_tp_doc, VB__doc__}, - {Py_tp_traverse, VB_traverse}, - {Py_tp_clear, VB_clear}, - {Py_tp_dealloc, VB_dealloc}, - {Py_tp_methods, VB_methods}, - /* tp_base cannot be set as a slot -- pass to PyType_FromModuleAndSpec */ - {0, NULL} -}; - -static PyType_Spec VB_type_spec = { - .name = VB__name__, - .basicsize = sizeof(VB), - .flags = BASETYPE_FLAGS, - .slots = VB_type_slots -}; - -#endif - - -/* - * Module state struct: holds all data formerly kept as static globals. - */ -typedef struct -{ - /* our globals (exposed to Python) */ - PyTypeObject* specification_base_class; - PyTypeObject* object_specification_descriptor_class; - PyTypeObject* class_provides_base_class; - PyTypeObject* interface_base_class; - PyTypeObject* lookup_base_class; - PyTypeObject* verifying_base_class; - PyObject* adapter_hooks; - /* members imported from 'zope.interface.declarations' - */ - PyObject* empty; - PyObject* fallback; - PyObject* builtin_impl_specs; - PyTypeObject* implements_class; - /* flag: have we imported the next set of members yet from - * 'zope.interface.declarations? - */ - int decl_imported; -} _zic_module_state; - -/* - * Macro to speed lookup of state members - */ -#define _zic_state(o) ((_zic_module_state*)PyModule_GetState(o)) - -static _zic_module_state* -_zic_state_init(PyObject* module) -{ - _zic_module_state* rec = _zic_state(module); - - rec->specification_base_class = NULL; - rec->object_specification_descriptor_class = NULL; - rec->class_provides_base_class = NULL; - rec->interface_base_class = NULL; - rec->lookup_base_class = NULL; - rec->verifying_base_class = NULL; - rec->adapter_hooks = NULL; - - rec->builtin_impl_specs = NULL; - rec->empty = NULL; - rec->fallback = NULL; - rec->implements_class = NULL; - rec->decl_imported = 0; - - return rec; -} - -static int -_zic_state_traverse(PyObject* module, visitproc visit, void* arg) -{ - _zic_module_state* rec = _zic_state(module); - - Py_VISIT(rec->specification_base_class); - Py_VISIT(rec->object_specification_descriptor_class); - Py_VISIT(rec->class_provides_base_class); - Py_VISIT(rec->interface_base_class); - Py_VISIT(rec->lookup_base_class); - Py_VISIT(rec->verifying_base_class); - Py_VISIT(rec->adapter_hooks); - - Py_VISIT(rec->builtin_impl_specs); - Py_VISIT(rec->empty); - Py_VISIT(rec->fallback); - Py_VISIT(rec->implements_class); - - return 0; -} - -static int -_zic_state_clear(PyObject* module) -{ - _zic_module_state* rec = _zic_state(module); - - Py_CLEAR(rec->specification_base_class); - Py_CLEAR(rec->object_specification_descriptor_class); - Py_CLEAR(rec->class_provides_base_class); - Py_CLEAR(rec->interface_base_class); - Py_CLEAR(rec->lookup_base_class); - Py_CLEAR(rec->verifying_base_class); - Py_CLEAR(rec->adapter_hooks); - - Py_CLEAR(rec->builtin_impl_specs); - Py_CLEAR(rec->empty); - Py_CLEAR(rec->fallback); - Py_CLEAR(rec->implements_class); - - return 0; -} - -#if USE_HEAP_TYPES -/* Import zope.interface.declarations and store results in module state. - * - * Dynamic alternative to 'import_declarations' above. - */ -static _zic_module_state* -_zic_state_load_declarations(PyObject* module) -{ - PyObject* declarations; - PyObject* builtin_impl_specs; - PyObject* empty; - PyObject* fallback; - PyObject* implements; - - _zic_module_state* rec = _zic_state(module); - - if (!rec->decl_imported) { - declarations = PyImport_ImportModule("zope.interface.declarations"); - if (declarations == NULL) { - return NULL; - } - - builtin_impl_specs = PyObject_GetAttrString( - declarations, "BuiltinImplementationSpecifications"); - if (builtin_impl_specs == NULL) { - return NULL; - } - - empty = PyObject_GetAttrString(declarations, "_empty"); - if (empty == NULL) { - return NULL; - } - - fallback = - PyObject_GetAttrString(declarations, "implementedByFallback"); - if (fallback == NULL) { - return NULL; - } - - implements = PyObject_GetAttrString(declarations, "Implements"); - if (implements == NULL) { - return NULL; - } - - if (!PyType_Check(implements)) { - PyErr_SetString( - PyExc_TypeError, - "zope.interface.declarations.Implements is not a type"); - return NULL; - } - - Py_DECREF(declarations); - - rec->builtin_impl_specs = builtin_impl_specs; - rec->empty = empty; - rec->fallback = fallback; - rec->implements_class = (PyTypeObject*)implements; - rec->decl_imported = 1; - } - return rec; -} - -#endif - -/* - * Provide access to the current module given the type. - */ - -static struct PyModuleDef _zic_module_def; - -static PyObject* -_get_module(PyTypeObject *typeobj) -{ -#if USE_STATIC_TYPES - return (PyObject*)&_zic_module_def; -#else - if (PyType_Check(typeobj)) { - /* Only added in Python 3.11 */ - return PyType_GetModuleByDef(typeobj, &_zic_module_def); - } - - PyErr_SetString(PyExc_TypeError, "_get_module: called w/ non-type"); - return NULL; -#endif -} - -/* - * Fetch the adapter hooks for the current type's module. - */ -static PyObject* -_get_adapter_hooks(PyTypeObject *typeobj) -{ -#if USE_STATIC_TYPES - return adapter_hooks; -#else - PyObject* module; - _zic_module_state* rec; - - module = _get_module(typeobj); - if (module == NULL) { return NULL; } - - rec = _zic_state(module); - return rec->adapter_hooks; -#endif -} - -/* - * Fetch the 'SpecificationBase' class for the current type's module. - */ -static PyTypeObject* -_get_specification_base_class(PyTypeObject *typeobj) -{ -#if USE_STATIC_TYPES - return &SB_type_def; -#else - PyObject* module; - _zic_module_state* rec; - - module = _get_module(typeobj); - if (module == NULL) { return NULL; } - - rec = _zic_state(module); - return rec->specification_base_class; -#endif -} - -/* - * Fetch the 'InterfaceBase' class for the current type's module. - */ -static PyTypeObject* -_get_interface_base_class(PyTypeObject *typeobj) -{ -#if USE_STATIC_TYPES - return &IB_type_def; -#else - PyObject* module; - _zic_module_state* rec; - - module = _get_module(typeobj); - if (module == NULL) { return NULL; } - - rec = _zic_state(module); - return rec->interface_base_class; -#endif -} - -static PyObject* -implementedByFallback(PyObject* module, PyObject* cls) -{ -#if USE_STATIC_TYPES - if (imported_declarations == 0 && import_declarations() < 0) { - return NULL; - } - /* now use static 'fallback' */ -#else - PyObject* fallback; - - _zic_module_state* rec = _zic_state_load_declarations(module); - if (rec == NULL) { return NULL; } - - fallback = rec->fallback; -#endif - - return PyObject_CallFunctionObjArgs(fallback, cls, NULL); -} - -static char implementedBy___doc__[] = - ("Interfaces implemented by a class or factory.\n" - "Raises TypeError if argument is neither a class nor a callable."); - -static PyObject* -implementedBy(PyObject* module, PyObject* cls) -{ - /* Fast retrieval of implements spec, if possible, to optimize - common case. Use fallback code if we get stuck. - */ - PyObject *dict = NULL; - PyObject *spec; - PyTypeObject *implements_class; - PyObject *builtin_impl_specs; - -#if USE_STATIC_TYPES - if (imported_declarations == 0 && import_declarations() < 0) { - return NULL; - } - - implements_class = Implements; - builtin_impl_specs = BuiltinImplementationSpecifications; -#else - _zic_module_state* rec = _zic_state_load_declarations(module); - if (rec == NULL) { return NULL; } - - implements_class = rec->implements_class; - builtin_impl_specs = rec->builtin_impl_specs; -#endif - - if (PyObject_TypeCheck(cls, &PySuper_Type)) { - // Let merging be handled by Python. - return implementedByFallback(module, cls); - } - - if (PyType_Check(cls)) { - dict = TYPE(cls)->tp_dict; - Py_XINCREF(dict); - } - - if (dict == NULL) - dict = PyObject_GetAttr(cls, str__dict__); - - if (dict == NULL) { - /* Probably a security proxied class, use more expensive fallback code - */ - PyErr_Clear(); - return implementedByFallback(module, cls); - } - - spec = PyObject_GetItem(dict, str__implemented__); - Py_DECREF(dict); - if (spec) { - - if (PyObject_TypeCheck(spec, implements_class)) - return spec; - - /* Old-style declaration, use more expensive fallback code */ - Py_DECREF(spec); - return implementedByFallback(module, cls); - } - - PyErr_Clear(); - - /* Maybe we have a builtin */ - spec = PyDict_GetItem(builtin_impl_specs, cls); - if (spec != NULL) { - Py_INCREF(spec); - return spec; - } - - /* We're stuck, use fallback */ - return implementedByFallback(module, cls); -} - -static char getObjectSpecification___doc__[] = - ("Get an object's interfaces (internal api)"); - -static PyObject* -getObjectSpecification(PyObject* module, PyObject* ob) -{ - PyObject *cls; - PyObject *result; - PyTypeObject *specification_base_class; - PyObject *empty_; - -#if USE_STATIC_TYPES - specification_base_class = &SB_type_def; - - if (imported_declarations == 0 && import_declarations() < 0) { - return NULL; - } - empty_ = empty; /* global from import */ - -#else - _zic_module_state* rec = _zic_state_load_declarations(module); - if (rec == NULL) { return NULL; } - - specification_base_class = rec->specification_base_class; - empty_ = rec->empty; -#endif - - result = PyObject_GetAttr(ob, str__provides__); - if (!result) { - if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { - /* Propagate non AttributeError exceptions. */ - return NULL; - } - PyErr_Clear(); - } else { - int is_instance = -1; - is_instance = - PyObject_IsInstance(result, OBJECT(specification_base_class)); - if (is_instance < 0) { - /* Propagate all errors */ - return NULL; - } - if (is_instance) { - return result; - } - } - - /* We do a getattr here so as not to be defeated by proxies */ - cls = PyObject_GetAttr(ob, str__class__); - if (cls == NULL) { - if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { - /* Propagate non-AttributeErrors */ - return NULL; - } - PyErr_Clear(); - - Py_INCREF(empty_); - return empty_; - } - result = implementedBy(module, cls); - Py_DECREF(cls); - - return result; -} - -static char providedBy___doc__[] = ("Get an object's interfaces"); - -static PyObject* -providedBy(PyObject* module, PyObject* ob) -{ - PyObject *result = NULL; - PyObject *cls; - PyObject *cp; - PyTypeObject *specification_base_class; - int is_instance = -1; - - is_instance = PyObject_IsInstance(ob, (PyObject*)&PySuper_Type); - if (is_instance < 0) { - if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { - /* Propagate non-AttributeErrors */ - return NULL; - } - PyErr_Clear(); - } - if (is_instance) { - return implementedBy(module, ob); - } - - result = PyObject_GetAttr(ob, str__providedBy__); - - if (result == NULL) { - if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { - return NULL; - } - - PyErr_Clear(); - return getObjectSpecification(module, ob); - } - - /* We want to make sure we have a spec. We can't do a type check - because we may have a proxy, so we'll just try to get the - only attribute. - */ -#if USE_STATIC_TYPES - specification_base_class = &SB_type_def; -#else - _zic_module_state* rec = _zic_state(module); - specification_base_class = rec->specification_base_class; -#endif - if (PyObject_TypeCheck(result, specification_base_class) || - PyObject_HasAttrString(result, "extends")) - return result; - - /* - The object's class doesn't understand descriptors. - Sigh. We need to get an object descriptor, but we have to be - careful. We want to use the instance's __provides__,l if - there is one, but only if it didn't come from the class. - */ - Py_DECREF(result); - - cls = PyObject_GetAttr(ob, str__class__); - if (cls == NULL) - return NULL; - - result = PyObject_GetAttr(ob, str__provides__); - if (result == NULL) { - /* No __provides__, so just fall back to implementedBy */ - PyErr_Clear(); - result = implementedBy(module, cls); - Py_DECREF(cls); - return result; - } - - cp = PyObject_GetAttr(cls, str__provides__); - if (cp == NULL) { - /* The the class has no provides, assume we're done: */ - PyErr_Clear(); - Py_DECREF(cls); - return result; - } - - if (cp == result) { - /* - Oops, we got the provides from the class. This means - the object doesn't have it's own. We should use implementedBy - */ - Py_DECREF(result); - result = implementedBy(module, cls); - } - - Py_DECREF(cls); - Py_DECREF(cp); - - return result; -} - -static struct PyMethodDef _zic_module_methods[] = { - { "implementedBy", - (PyCFunction)implementedBy, - METH_O, - implementedBy___doc__ }, - { "getObjectSpecification", - (PyCFunction)getObjectSpecification, - METH_O, - getObjectSpecification___doc__ }, - { "providedBy", (PyCFunction)providedBy, METH_O, providedBy___doc__ }, - - { NULL, (PyCFunction)NULL, 0, NULL } /* sentinel */ -}; - - -/* Handler for the 'execute' phase of multi-phase initialization - * - * See: https://docs.python.org/3/c-api/module.html#multi-phase-initialization - * and: https://peps.python.org/pep-0489/#module-execution-phase - */ -static int -_zic_module_exec(PyObject* module) -{ - _zic_module_state* rec = _zic_state_init(module); - - rec->adapter_hooks = PyList_New(0); - if (rec->adapter_hooks == NULL) - return -1; - Py_INCREF(rec->adapter_hooks); - -#if USE_STATIC_TYPES - - /* Initialize static global */ - adapter_hooks = rec->adapter_hooks; - - /* Initialize types: */ - SB_type_def.tp_new = PyBaseObject_Type.tp_new; - if (PyType_Ready(&SB_type_def) < 0) { return -1; } - Py_INCREF(&SB_type_def); - rec->specification_base_class = &SB_type_def; - - OSD_type_def.tp_new = PyBaseObject_Type.tp_new; - if (PyType_Ready(&OSD_type_def) < 0) { return -1; } - Py_INCREF(&OSD_type_def); - rec->object_specification_descriptor_class = &OSD_type_def; - - CPB_type_def.tp_new = PyBaseObject_Type.tp_new; - if (PyType_Ready(&CPB_type_def) < 0) { return -1; } - Py_INCREF(&CPB_type_def); - rec->class_provides_base_class = &CPB_type_def; - - IB_type_def.tp_new = PyBaseObject_Type.tp_new; - if (PyType_Ready(&IB_type_def) < 0) { return -1; } - Py_INCREF(&IB_type_def); - rec->interface_base_class = &IB_type_def; - - LB_type_def.tp_new = PyBaseObject_Type.tp_new; - if (PyType_Ready(&LB_type_def) < 0) { return -1; } - Py_INCREF(&LB_type_def); - rec->lookup_base_class = &LB_type_def; - - VB_type_def.tp_new = PyBaseObject_Type.tp_new; - if (PyType_Ready(&VB_type_def) < 0) { return -1; } - Py_INCREF(&VB_type_def); - rec->verifying_base_class = &VB_type_def; - -#else - - PyObject *sb_class; - PyObject *osd_class; - PyObject *cpb_class; - PyObject *ib_class; - PyObject *lb_class; - PyObject *vb_class; - - /* Initialize types: - */ - sb_class = PyType_FromModuleAndSpec(module, &SB_type_spec, NULL); - if (sb_class == NULL) { return -1; } - Py_INCREF(sb_class); - rec->specification_base_class = TYPE(sb_class); - - osd_class = PyType_FromModuleAndSpec(module, &OSD_type_spec, NULL); - if (osd_class == NULL) { return -1; } - Py_INCREF(osd_class); - rec->object_specification_descriptor_class = TYPE(osd_class); - - cpb_class = PyType_FromModuleAndSpec(module, &CPB_type_spec, sb_class); - if (cpb_class == NULL) { return -1; } - Py_INCREF(cpb_class); - rec->class_provides_base_class = TYPE(cpb_class); - - ib_class = PyType_FromModuleAndSpec(module, &IB_type_spec, sb_class); - if (ib_class == NULL) { return -1; } - Py_INCREF(ib_class); - rec->interface_base_class = TYPE(ib_class); - - lb_class = PyType_FromModuleAndSpec(module, &LB_type_spec, NULL); - if (lb_class == NULL) { return -1; } - Py_INCREF(lb_class); - rec->lookup_base_class = TYPE(lb_class); - - vb_class = PyType_FromModuleAndSpec(module, &VB_type_spec, lb_class); - if (vb_class == NULL) { return -1; } - Py_INCREF(vb_class); - rec->verifying_base_class = TYPE(vb_class); - -#endif - - /* Add types to our dict FBO python; also the adapter hooks */ - if (PyModule_AddObject(module, - "SpecificationBase", OBJECT(rec->specification_base_class)) < 0) - return -1; - - if (PyModule_AddObject(module, - "ObjectSpecificationDescriptor", - OBJECT(rec->object_specification_descriptor_class)) < - 0) - return -1; - - if (PyModule_AddObject(module, - "ClassProvidesBase", OBJECT(rec->class_provides_base_class)) < 0) - return -1; - - if (PyModule_AddObject(module, - "InterfaceBase", OBJECT(rec->interface_base_class)) < 0) - return -1; - - if (PyModule_AddObject(module, - "LookupBase", OBJECT(rec->lookup_base_class)) < 0) - return -1; - - if (PyModule_AddObject(module, - "VerifyingBase", OBJECT(rec->verifying_base_class)) < 0) - return -1; - - if (PyModule_AddObject(module, "adapter_hooks", rec->adapter_hooks) < 0) - return -1; - - return 0; -} - - -/* Slot definitions for multi-phase initialization - * - * See: https://docs.python.org/3/c-api/module.html#multi-phase-initialization - * and: https://peps.python.org/pep-0489 - */ -static PyModuleDef_Slot _zic_module_slots[] = { - {Py_mod_exec, _zic_module_exec}, - {0, NULL} -}; - -static char _zic_module__doc__[] = "C optimizations for zope.interface\n\n"; - -static struct PyModuleDef _zic_module_def = { - PyModuleDef_HEAD_INIT, - .m_name = "_zope_interface_coptimizations", - .m_doc = _zic_module__doc__, - .m_size = sizeof(_zic_module_state), - .m_methods = _zic_module_methods, - .m_slots=_zic_module_slots, - .m_traverse = _zic_state_traverse, - .m_clear = _zic_state_clear, -}; - -static PyObject* -init(void) -{ - if (define_static_strings() < 0) { return NULL; } - - return PyModuleDef_Init(&_zic_module_def); -} - -PyMODINIT_FUNC -PyInit__zope_interface_coptimizations(void) -{ - return init(); -} - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/_zope_interface_coptimizations.cpython-38-x86_64-linux-gnu.so b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/_zope_interface_coptimizations.cpython-38-x86_64-linux-gnu.so deleted file mode 100755 index 468c592..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/_zope_interface_coptimizations.cpython-38-x86_64-linux-gnu.so and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/adapter.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/adapter.py deleted file mode 100644 index a35c3f9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/adapter.py +++ /dev/null @@ -1,1048 +0,0 @@ -############################################################################## -# -# Copyright (c) 2004 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Adapter management -""" -import itertools -import weakref - -from zope.interface import Interface -from zope.interface import implementer -from zope.interface import providedBy -from zope.interface import ro -from zope.interface._compat import _normalize_name -from zope.interface._compat import _use_c_impl -from zope.interface.interfaces import IAdapterRegistry - - -__all__ = [ - 'AdapterRegistry', - 'VerifyingAdapterRegistry', -] - -# In the CPython implementation, -# ``tuple`` and ``list`` cooperate so that ``tuple([some list])`` -# directly allocates and iterates at the C level without using a -# Python iterator. That's not the case for -# ``tuple(generator_expression)`` or ``tuple(map(func, it))``. -## -# 3.8 -# ``tuple([t for t in range(10)])`` -> 610ns -# ``tuple(t for t in range(10))`` -> 696ns -# ``tuple(map(lambda t: t, range(10)))`` -> 881ns -## -# 2.7 -# ``tuple([t fon t in range(10)])`` -> 625ns -# ``tuple(t for t in range(10))`` -> 665ns -# ``tuple(map(lambda t: t, range(10)))`` -> 958ns -# -# All three have substantial variance. -## -# On PyPy, this is also the best option. -## -# PyPy 2.7.18-7.3.3 -# ``tuple([t fon t in range(10)])`` -> 128ns -# ``tuple(t for t in range(10))`` -> 175ns -# ``tuple(map(lambda t: t, range(10)))`` -> 153ns -## -# PyPy 3.7.9 7.3.3-beta -# ``tuple([t fon t in range(10)])`` -> 82ns -# ``tuple(t for t in range(10))`` -> 177ns -# ``tuple(map(lambda t: t, range(10)))`` -> 168ns - - -class BaseAdapterRegistry: - """ - A basic implementation of the data storage and algorithms required - for a :class:`zope.interface.interfaces.IAdapterRegistry`. - - Subclasses can set the following attributes to control how the data - is stored; in particular, these hooks can be helpful for ZODB - persistence. They can be class attributes that are the named - (or similar) type, or they can be methods that act as a constructor - for an object that behaves like the types defined here; this object - will not assume that they are type objects, but subclasses are free - to do so: - - _sequenceType = list - This is the type used for our two mutable top-level "byorder" sequences. - Must support mutation operations like ``append()`` and ``del - seq[index]``. These are usually small (< 10). Although at least one of - them is accessed when performing lookups or queries on this object, the - other is untouched. In many common scenarios, both are only required - when mutating registrations and subscriptions (like what - :meth:`zope.interface.interfaces.IComponents.registerUtility` does). - This use pattern makes it an ideal candidate to be a - :class:`~persistent.list.PersistentList`. - - _leafSequenceType = tuple - This is the type used for the leaf sequences of subscribers. - It could be set to a ``PersistentList`` to avoid many unnecessary data - loads when subscribers aren't being used. Mutation operations are - directed through :meth:`_addValueToLeaf` and - :meth:`_removeValueFromLeaf`; if you use a mutable type, you'll need to - override those. - - _mappingType = dict - This is the mutable mapping type used for the keyed mappings. A - :class:`~persistent.mapping.PersistentMapping` could be used to help - reduce the number of data loads when the registry is large and parts of - it are rarely used. Further reductions in data loads can come from using - a :class:`~BTrees.OOBTree.OOBTree`, but care is required to be sure that - all required/provided values are fully ordered (e.g., no required or - provided values that are classes can be used). - - _providedType = dict - This is the mutable mapping type used for the ``_provided`` mapping. - This is separate from the generic mapping type because the values - are always integers, so one might choose to use a more optimized data - structure such as a :class:`~BTrees.OIBTree.OIBTree`. - The same caveats regarding key types - apply as for ``_mappingType``. - - It is possible to also set these on an instance, but because of the need - to potentially also override :meth:`_addValueToLeaf` and - :meth:`_removeValueFromLeaf`, this may be less useful in a persistent - scenario; using a subclass is recommended. - - .. versionchanged:: 5.3.0 - Add support for customizing the way internal data - structures are created. - .. versionchanged:: 5.3.0 - Add methods :meth:`rebuild`, :meth:`allRegistrations` - and :meth:`allSubscriptions`. - """ - - # List of methods copied from lookup sub-objects: - _delegated = ('lookup', 'queryMultiAdapter', 'lookup1', 'queryAdapter', - 'adapter_hook', 'lookupAll', 'names', - 'subscriptions', 'subscribers') - - # All registries maintain a generation that can be used by verifying - # registries - _generation = 0 - - def __init__(self, bases=()): - - # The comments here could be improved. Possibly this bit needs - # explaining in a separate document, as the comments here can - # be quite confusing. /regebro - - # {order -> {required -> {provided -> {name -> value}}}} - # Here "order" is actually an index in a list, "required" and - # "provided" are interfaces, and "required" is really a nested - # key. So, for example: - # for order == 0 (that is, self._adapters[0]), we have: - # {provided -> {name -> value}} - # but for order == 2 (that is, self._adapters[2]), we have: - # {r1 -> {r2 -> {provided -> {name -> value}}}} - # - self._adapters = self._sequenceType() - - # {order -> {required -> {provided -> {name -> [value]}}}} - # where the remarks about adapters above apply - self._subscribers = self._sequenceType() - - # Set, with a reference count, keeping track of the interfaces - # for which we have provided components: - self._provided = self._providedType() - - # Create ``_v_lookup`` object to perform lookup. We make this a - # separate object to to make it easier to implement just the - # lookup functionality in C. This object keeps track of cache - # invalidation data in two kinds of registries. - - # Invalidating registries have caches that are invalidated - # when they or their base registies change. An invalidating - # registry can only have invalidating registries as bases. - # See LookupBaseFallback below for the pertinent logic. - - # Verifying registies can't rely on getting invalidation messages, - # so have to check the generations of base registries to determine - # if their cache data are current. See VerifyingBasePy below - # for the pertinent object. - self._createLookup() - - # Setting the bases causes the registries described above - # to be initialized (self._setBases -> self.changed -> - # self._v_lookup.changed). - - self.__bases__ = bases - - def _setBases(self, bases): - """ - If subclasses need to track when ``__bases__`` changes, they - can override this method. - - Subclasses must still call this method. - """ - self.__dict__['__bases__'] = bases - self.ro = ro.ro(self) - self.changed(self) - - __bases__ = property(lambda self: self.__dict__['__bases__'], - lambda self, bases: self._setBases(bases), - ) - - def _createLookup(self): - self._v_lookup = self.LookupClass(self) - for name in self._delegated: - self.__dict__[name] = getattr(self._v_lookup, name) - - # Hooks for subclasses to define the types of objects used in - # our data structures. - # These have to be documented in the docstring, instead of local - # comments, because Sphinx autodoc ignores the comment and just writes - # "alias of list" - _sequenceType = list - _leafSequenceType = tuple - _mappingType = dict - _providedType = dict - - def _addValueToLeaf(self, existing_leaf_sequence, new_item): - """ - Add the value *new_item* to the *existing_leaf_sequence*, which may - be ``None``. - - Subclasses that redefine `_leafSequenceType` should override this - method. - - :param existing_leaf_sequence: - If *existing_leaf_sequence* is not *None*, it will be an instance - of `_leafSequenceType`. (Unless the object has been unpickled from - an old pickle and the class definition has changed, in which case - it may be an instance of a previous definition, commonly a - `tuple`.) - - :return: - This method returns the new value to be stored. It may mutate the - sequence in place if it was not ``None`` and the type is mutable, - but it must also return it. - - .. versionadded:: 5.3.0 - """ - if existing_leaf_sequence is None: - return (new_item,) - return existing_leaf_sequence + (new_item,) - - def _removeValueFromLeaf(self, existing_leaf_sequence, to_remove): - """ - Remove the item *to_remove* from the (non-``None``, non-empty) - *existing_leaf_sequence* and return the mutated sequence. - - If there is more than one item that is equal to *to_remove* - they must all be removed. - - Subclasses that redefine `_leafSequenceType` should override - this method. Note that they can call this method to help - in their implementation; this implementation will always - return a new tuple constructed by iterating across - the *existing_leaf_sequence* and omitting items equal to *to_remove*. - - :param existing_leaf_sequence: - As for `_addValueToLeaf`, probably an instance of - `_leafSequenceType` but possibly an older type; never `None`. - :return: - A version of *existing_leaf_sequence* with all items equal to - *to_remove* removed. Must not return `None`. However, - returning an empty - object, even of another type such as the empty tuple, ``()`` is - explicitly allowed; such an object will never be stored. - - .. versionadded:: 5.3.0 - """ - return tuple([v for v in existing_leaf_sequence if v != to_remove]) - - def changed(self, originally_changed): - self._generation += 1 - self._v_lookup.changed(originally_changed) - - def register(self, required, provided, name, value): - if not isinstance(name, str): - raise ValueError('name is not a string') - if value is None: - self.unregister(required, provided, name, value) - return - - required = tuple([_convert_None_to_Interface(r) for r in required]) - name = _normalize_name(name) - order = len(required) - byorder = self._adapters - while len(byorder) <= order: - byorder.append(self._mappingType()) - components = byorder[order] - key = required + (provided,) - - for k in key: - d = components.get(k) - if d is None: - d = self._mappingType() - components[k] = d - components = d - - if components.get(name) is value: - return - - components[name] = value - - n = self._provided.get(provided, 0) + 1 - self._provided[provided] = n - if n == 1: - self._v_lookup.add_extendor(provided) - - self.changed(self) - - def _find_leaf(self, byorder, required, provided, name): - # Find the leaf value, if any, in the *byorder* list - # for the interface sequence *required* and the interface - # *provided*, given the already normalized *name*. - # - # If no such leaf value exists, returns ``None`` - required = tuple([_convert_None_to_Interface(r) for r in required]) - order = len(required) - if len(byorder) <= order: - return None - - components = byorder[order] - key = required + (provided,) - - for k in key: - d = components.get(k) - if d is None: - return None - components = d - - return components.get(name) - - def registered(self, required, provided, name=''): - return self._find_leaf( - self._adapters, - required, - provided, - _normalize_name(name) - ) - - @classmethod - def _allKeys(cls, components, i, parent_k=()): - if i == 0: - for k, v in components.items(): - yield parent_k + (k,), v - else: - for k, v in components.items(): - new_parent_k = parent_k + (k,) - yield from cls._allKeys(v, i - 1, new_parent_k) - - def _all_entries(self, byorder): - # Recurse through the mapping levels of the `byorder` sequence, - # reconstructing a flattened sequence of ``(required, provided, name, - # value)`` tuples that can be used to reconstruct the sequence with - # the appropriate registration methods. - # - # Locally reference the `byorder` data; it might be replaced while - # this method is running (see ``rebuild``). - for i, components in enumerate(byorder): - # We will have *i* levels of dictionaries to go before - # we get to the leaf. - for key, value in self._allKeys(components, i + 1): - assert len(key) == i + 2 - required = key[:i] - provided = key[-2] - name = key[-1] - yield (required, provided, name, value) - - def allRegistrations(self): - """ - Yields tuples ``(required, provided, name, value)`` for all - the registrations that this object holds. - - These tuples could be passed as the arguments to the - :meth:`register` method on another adapter registry to - duplicate the registrations this object holds. - - .. versionadded:: 5.3.0 - """ - yield from self._all_entries(self._adapters) - - def unregister(self, required, provided, name, value=None): - required = tuple([_convert_None_to_Interface(r) for r in required]) - order = len(required) - byorder = self._adapters - if order >= len(byorder): - return False - components = byorder[order] - key = required + (provided,) - - # Keep track of how we got to `components`: - lookups = [] - for k in key: - d = components.get(k) - if d is None: - return - lookups.append((components, k)) - components = d - - old = components.get(name) - if old is None: - return - if (value is not None) and (old is not value): - return - - del components[name] - if not components: - # Clean out empty containers, since we don't want our keys - # to reference global objects (interfaces) unnecessarily. - # This is often a problem when an interface is slated for - # removal; a hold-over entry in the registry can make it - # difficult to remove such interfaces. - for comp, k in reversed(lookups): - d = comp[k] - if d: - break - else: - del comp[k] - while byorder and not byorder[-1]: - del byorder[-1] - n = self._provided[provided] - 1 - if n == 0: - del self._provided[provided] - self._v_lookup.remove_extendor(provided) - else: - self._provided[provided] = n - - self.changed(self) - - def subscribe(self, required, provided, value): - required = tuple([_convert_None_to_Interface(r) for r in required]) - name = '' - order = len(required) - byorder = self._subscribers - while len(byorder) <= order: - byorder.append(self._mappingType()) - components = byorder[order] - key = required + (provided,) - - for k in key: - d = components.get(k) - if d is None: - d = self._mappingType() - components[k] = d - components = d - - components[name] = self._addValueToLeaf(components.get(name), value) - - if provided is not None: - n = self._provided.get(provided, 0) + 1 - self._provided[provided] = n - if n == 1: - self._v_lookup.add_extendor(provided) - - self.changed(self) - - def subscribed(self, required, provided, subscriber): - subscribers = self._find_leaf( - self._subscribers, - required, - provided, - '' - ) or () - return subscriber if subscriber in subscribers else None - - def allSubscriptions(self): - """ - Yields tuples ``(required, provided, value)`` for all the - subscribers that this object holds. - - These tuples could be passed as the arguments to the - :meth:`subscribe` method on another adapter registry to - duplicate the registrations this object holds. - - .. versionadded:: 5.3.0 - """ - for required, provided, _name, value in self._all_entries( - self._subscribers, - ): - for v in value: - yield (required, provided, v) - - def unsubscribe(self, required, provided, value=None): - required = tuple([_convert_None_to_Interface(r) for r in required]) - order = len(required) - byorder = self._subscribers - if order >= len(byorder): - return - components = byorder[order] - key = required + (provided,) - - # Keep track of how we got to `components`: - lookups = [] - for k in key: - d = components.get(k) - if d is None: - return - lookups.append((components, k)) - components = d - - old = components.get('') - if not old: - # this is belt-and-suspenders against the failure of cleanup below - return # pragma: no cover - len_old = len(old) - if value is None: - # Removing everything; note that the type of ``new`` won't - # necessarily match the ``_leafSequenceType``, but that's - # OK because we're about to delete the entire entry - # anyway. - new = () - else: - new = self._removeValueFromLeaf(old, value) - # ``new`` may be the same object as ``old``, just mutated in place, - # so we cannot compare it to ``old`` to check for changes. Remove - # our reference to it now to avoid trying to do so below. - del old - - if len(new) == len_old: - # No changes, so nothing could have been removed. - return - - if new: - components[''] = new - else: - # Instead of setting components[u''] = new, we clean out - # empty containers, since we don't want our keys to - # reference global objects (interfaces) unnecessarily. This - # is often a problem when an interface is slated for - # removal; a hold-over entry in the registry can make it - # difficult to remove such interfaces. - del components[''] - for comp, k in reversed(lookups): - d = comp[k] - if d: - break - else: - del comp[k] - while byorder and not byorder[-1]: - del byorder[-1] - - if provided is not None: - n = self._provided[provided] + len(new) - len_old - if n == 0: - del self._provided[provided] - self._v_lookup.remove_extendor(provided) - else: - self._provided[provided] = n - - self.changed(self) - - def rebuild(self): - """ - Rebuild (and replace) all the internal data structures of this - object. - - This is useful, especially for persistent implementations, if - you suspect an issue with reference counts keeping interfaces - alive even though they are no longer used. - - It is also useful if you or a subclass change the data types - (``_mappingType`` and friends) that are to be used. - - This method replaces all internal data structures with new objects; - it specifically does not re-use any storage. - - .. versionadded:: 5.3.0 - """ - - # Grab the iterators, we're about to discard their data. - registrations = self.allRegistrations() - subscriptions = self.allSubscriptions() - - def buffer(it): - # The generator doesn't actually start running until we - # ask for its next(), by which time the attributes will change - # unless we do so before calling __init__. - try: - first = next(it) - except StopIteration: - return iter(()) - - return itertools.chain((first,), it) - - registrations = buffer(registrations) - subscriptions = buffer(subscriptions) - - # Replace the base data structures as well as _v_lookup. - self.__init__(self.__bases__) - # Re-register everything previously registered and subscribed. - # - # XXX: This is going to call ``self.changed()`` a lot, all of - # which is unnecessary (because ``self.__init__`` just - # re-created those dependent objects and also called - # ``self.changed()``). Is this a bottleneck that needs fixed? - # (We could do ``self.changed = lambda _: None`` before - # beginning and remove it after to disable the presumably expensive - # part of passing that notification to the change of objects.) - for args in registrations: - self.register(*args) - - for args in subscriptions: - self.subscribe(*args) - - # XXX hack to fake out twisted's use of a private api. - # We need to get them to use the new registered method. - def get(self, _): # pragma: no cover - class XXXTwistedFakeOut: - selfImplied = {} - return XXXTwistedFakeOut - - -_not_in_mapping = object() - - -@_use_c_impl -class LookupBase: - - def __init__(self): - self._cache = {} - self._mcache = {} - self._scache = {} - - def changed(self, ignored=None): - self._cache.clear() - self._mcache.clear() - self._scache.clear() - - def _getcache(self, provided, name): - cache = self._cache.get(provided) - if cache is None: - cache = {} - self._cache[provided] = cache - if name: - c = cache.get(name) - if c is None: - c = {} - cache[name] = c - cache = c - return cache - - def lookup(self, required, provided, name='', default=None): - if not isinstance(name, str): - raise ValueError('name is not a string') - cache = self._getcache(provided, name) - required = tuple(required) - if len(required) == 1: - result = cache.get(required[0], _not_in_mapping) - else: - result = cache.get(tuple(required), _not_in_mapping) - - if result is _not_in_mapping: - result = self._uncached_lookup(required, provided, name) - if len(required) == 1: - cache[required[0]] = result - else: - cache[tuple(required)] = result - - if result is None: - return default - - return result - - def lookup1(self, required, provided, name='', default=None): - if not isinstance(name, str): - raise ValueError('name is not a string') - cache = self._getcache(provided, name) - result = cache.get(required, _not_in_mapping) - if result is _not_in_mapping: - return self.lookup((required, ), provided, name, default) - - if result is None: - return default - - return result - - def queryAdapter(self, object, provided, name='', default=None): - return self.adapter_hook(provided, object, name, default) - - def adapter_hook(self, provided, object, name='', default=None): - if not isinstance(name, str): - raise ValueError('name is not a string') - required = providedBy(object) - cache = self._getcache(provided, name) - factory = cache.get(required, _not_in_mapping) - if factory is _not_in_mapping: - factory = self.lookup((required, ), provided, name) - - if factory is not None: - if isinstance(object, super): - object = object.__self__ - result = factory(object) - if result is not None: - return result - - return default - - def lookupAll(self, required, provided): - cache = self._mcache.get(provided) - if cache is None: - cache = {} - self._mcache[provided] = cache - - required = tuple(required) - result = cache.get(required, _not_in_mapping) - if result is _not_in_mapping: - result = self._uncached_lookupAll(required, provided) - cache[required] = result - - return result - - def subscriptions(self, required, provided): - cache = self._scache.get(provided) - if cache is None: - cache = {} - self._scache[provided] = cache - - required = tuple(required) - result = cache.get(required, _not_in_mapping) - if result is _not_in_mapping: - result = self._uncached_subscriptions(required, provided) - cache[required] = result - - return result - - -@_use_c_impl -class VerifyingBase(LookupBaseFallback): # noqa F821 - # Mixin for lookups against registries which "chain" upwards, and - # whose lookups invalidate their own caches whenever a parent registry - # bumps its own '_generation' counter. E.g., used by - # zope.component.persistentregistry - - def changed(self, originally_changed): - LookupBaseFallback.changed(self, originally_changed) # noqa F821 - self._verify_ro = self._registry.ro[1:] - self._verify_generations = [r._generation for r in self._verify_ro] - - def _verify(self): - if ( - [ - r._generation for r in self._verify_ro - ] != self._verify_generations - ): - self.changed(None) - - def _getcache(self, provided, name): - self._verify() - return LookupBaseFallback._getcache( # noqa F821 - self, provided, name, - ) - - def lookupAll(self, required, provided): - self._verify() - return LookupBaseFallback.lookupAll( # noqa F821 - self, required, provided, - ) - - def subscriptions(self, required, provided): - self._verify() - return LookupBaseFallback.subscriptions( # noqa F821 - self, required, provided, - ) - - -class AdapterLookupBase: - - def __init__(self, registry): - self._registry = registry - self._required = {} - self.init_extendors() - super().__init__() - - def changed(self, ignored=None): - super().changed(None) - for r in self._required.keys(): - r = r() - if r is not None: - r.unsubscribe(self) - self._required.clear() - - # Extendors - # --------- - - # When given an target interface for an adapter lookup, we need to consider - # adapters for interfaces that extend the target interface. This is - # what the extendors dictionary is about. It tells us all of the - # interfaces that extend an interface for which there are adapters - # registered. - - # We could separate this by order and name, thus reducing the - # number of provided interfaces to search at run time. The tradeoff, - # however, is that we have to store more information. For example, - # if the same interface is provided for multiple names and if the - # interface extends many interfaces, we'll have to keep track of - # a fair bit of information for each name. It's better to - # be space efficient here and be time efficient in the cache - # implementation. - - # TODO: add invalidation when a provided interface changes, in case - # the interface's __iro__ has changed. This is unlikely enough that - # we'll take our chances for now. - - def init_extendors(self): # noqa E301 - self._extendors = {} - for p in self._registry._provided: - self.add_extendor(p) - - def add_extendor(self, provided): - _extendors = self._extendors - for i in provided.__iro__: - extendors = _extendors.get(i, ()) - _extendors[i] = ( - [ - e for e in extendors if provided.isOrExtends(e) - ] + [ - provided - ] + [ - e for e in extendors if not provided.isOrExtends(e) - ] - ) - - def remove_extendor(self, provided): - _extendors = self._extendors - for i in provided.__iro__: - _extendors[i] = [e for e in _extendors.get(i, ()) - if e != provided] - - def _subscribe(self, *required): - _refs = self._required - for r in required: - ref = r.weakref() - if ref not in _refs: - r.subscribe(self) - _refs[ref] = 1 - - def _uncached_lookup(self, required, provided, name=''): - required = tuple(required) - result = None - order = len(required) - for registry in self._registry.ro: - byorder = registry._adapters - if order >= len(byorder): - continue - - extendors = registry._v_lookup._extendors.get(provided) - if not extendors: - continue - - components = byorder[order] - result = _lookup(components, required, extendors, name, 0, - order) - if result is not None: - break - - self._subscribe(*required) - - return result - - def queryMultiAdapter(self, objects, provided, name='', default=None): - factory = self.lookup([providedBy(o) for o in objects], provided, name) - if factory is None: - return default - - result = factory(*[ - o.__self__ if isinstance(o, super) else o for o in objects - ]) - if result is None: - return default - - return result - - def _uncached_lookupAll(self, required, provided): - required = tuple(required) - order = len(required) - result = {} - for registry in reversed(self._registry.ro): - byorder = registry._adapters - if order >= len(byorder): - continue - extendors = registry._v_lookup._extendors.get(provided) - if not extendors: - continue - components = byorder[order] - _lookupAll(components, required, extendors, result, 0, order) - - self._subscribe(*required) - - return tuple(result.items()) - - def names(self, required, provided): - return [c[0] for c in self.lookupAll(required, provided)] - - def _uncached_subscriptions(self, required, provided): - required = tuple(required) - order = len(required) - result = [] - for registry in reversed(self._registry.ro): - byorder = registry._subscribers - if order >= len(byorder): - continue - - if provided is None: - extendors = (provided, ) - else: - extendors = registry._v_lookup._extendors.get(provided) - if extendors is None: - continue - - _subscriptions(byorder[order], required, extendors, '', - result, 0, order) - - self._subscribe(*required) - - return result - - def subscribers(self, objects, provided): - subscriptions = self.subscriptions( - [providedBy(o) for o in objects], provided - ) - if provided is None: - result = () - for subscription in subscriptions: - subscription(*objects) - else: - result = [] - for subscription in subscriptions: - subscriber = subscription(*objects) - if subscriber is not None: - result.append(subscriber) - return result - - -class AdapterLookup(AdapterLookupBase, LookupBase): - pass - - -@implementer(IAdapterRegistry) -class AdapterRegistry(BaseAdapterRegistry): - """ - A full implementation of ``IAdapterRegistry`` that adds support for - sub-registries. - """ - - LookupClass = AdapterLookup - - def __init__(self, bases=()): - # AdapterRegisties are invalidating registries, so - # we need to keep track of our invalidating subregistries. - self._v_subregistries = weakref.WeakKeyDictionary() - - super().__init__(bases) - - def _addSubregistry(self, r): - self._v_subregistries[r] = 1 - - def _removeSubregistry(self, r): - if r in self._v_subregistries: - del self._v_subregistries[r] - - def _setBases(self, bases): - old = self.__dict__.get('__bases__', ()) - for r in old: - if r not in bases: - r._removeSubregistry(self) - for r in bases: - if r not in old: - r._addSubregistry(self) - - super()._setBases(bases) - - def changed(self, originally_changed): - super().changed(originally_changed) - - for sub in self._v_subregistries.keys(): - sub.changed(originally_changed) - - -class VerifyingAdapterLookup(AdapterLookupBase, VerifyingBase): - pass - - -@implementer(IAdapterRegistry) -class VerifyingAdapterRegistry(BaseAdapterRegistry): - """ - The most commonly-used adapter registry. - """ - - LookupClass = VerifyingAdapterLookup - - -def _convert_None_to_Interface(x): - if x is None: - return Interface - else: - return x - - -def _lookup(components, specs, provided, name, i, l): # noqa: E741 - # this function is called very often. - # The components.get in loops is executed 100 of 1000s times. - # by loading get into a local variable the bytecode - # "LOAD_FAST 0 (components)" in the loop can be eliminated. - components_get = components.get - if i < l: - for spec in specs[i].__sro__: - comps = components_get(spec) - if comps: - r = _lookup(comps, specs, provided, name, i + 1, l) - if r is not None: - return r - else: - for iface in provided: - comps = components_get(iface) - if comps: - r = comps.get(name) - if r is not None: - return r - - return None - - -def _lookupAll(components, specs, provided, result, i, l): # noqa: E741 - components_get = components.get # see _lookup above - if i < l: - for spec in reversed(specs[i].__sro__): - comps = components_get(spec) - if comps: - _lookupAll(comps, specs, provided, result, i + 1, l) - else: - for iface in reversed(provided): - comps = components_get(iface) - if comps: - result.update(comps) - - -def _subscriptions( - components, specs, provided, name, result, i, l # noqa: E741 -): - components_get = components.get # see _lookup above - if i < l: - for spec in reversed(specs[i].__sro__): - comps = components_get(spec) - if comps: - _subscriptions( - comps, specs, provided, name, result, i + 1, l - ) - else: - for iface in reversed(provided): - comps = components_get(iface) - if comps: - comps = comps.get(name) - if comps: - result.extend(comps) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/advice.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/advice.py deleted file mode 100644 index f577d42..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/advice.py +++ /dev/null @@ -1,121 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Class advice. - -This module was adapted from 'protocols.advice', part of the Python -Enterprise Application Kit (PEAK). Please notify the PEAK authors -(pje@telecommunity.com and tsarna@sarna.org) if bugs are found or -Zope-specific changes are required, so that the PEAK version of this module -can be kept in sync. - -PEAK is a Python application framework that interoperates with (but does -not require) Zope 3 and Twisted. It provides tools for manipulating UML -models, object-relational persistence, aspect-oriented programming, and more. -Visit the PEAK home page at http://peak.telecommunity.com for more information. -""" - -from types import FunctionType - - -__all__ = [ - 'determineMetaclass', - 'getFrameInfo', - 'isClassAdvisor', - 'minimalBases', -] - -import sys - - -def getFrameInfo(frame): - """Return (kind,module,locals,globals) for a frame - - 'kind' is one of "exec", "module", "class", "function call", or "unknown". - """ - - f_locals = frame.f_locals - f_globals = frame.f_globals - - sameNamespace = f_locals is f_globals - hasModule = '__module__' in f_locals - hasName = '__name__' in f_globals - - sameName = hasModule and hasName - sameName = sameName and f_globals['__name__'] == f_locals['__module__'] - - module = hasName and sys.modules.get(f_globals['__name__']) or None - - namespaceIsModule = module and module.__dict__ is f_globals - - if not namespaceIsModule: - # some kind of funky exec - kind = "exec" - elif sameNamespace and not hasModule: - kind = "module" - elif sameName and not sameNamespace: - kind = "class" - elif not sameNamespace: - kind = "function call" - else: # pragma: no cover - # How can you have f_locals is f_globals, and have '__module__' - # set? # This is probably module-level code, but with a - # '__module__' variable. - kind = "unknown" - return kind, module, f_locals, f_globals - - -def isClassAdvisor(ob): - """True if 'ob' is a class advisor function""" - return isinstance(ob, FunctionType) and hasattr(ob, 'previousMetaclass') - - -def determineMetaclass(bases, explicit_mc=None): - """Determine metaclass from 1+ bases and optional explicit __metaclass__""" - - meta = [getattr(b, '__class__', type(b)) for b in bases] - - if explicit_mc is not None: - # The explicit metaclass needs to be verified for compatibility - # as well, and allowed to resolve the incompatible bases, if any - meta.append(explicit_mc) - - if len(meta) == 1: - # easy case - return meta[0] - - candidates = minimalBases(meta) # minimal set of metaclasses - - if len(candidates) > 1: - # We could auto-combine, but for now we won't... - raise TypeError("Incompatible metatypes", bases) - - # Just one, return it - return candidates[0] - - -def minimalBases(classes): - """Reduce a list of base classes to its ordered minimum equivalent""" - candidates = [] - - for m in classes: - for n in classes: - if issubclass(n, m) and m is not n: - break - else: - # m has no subclasses in 'classes' - if m in candidates: - candidates.remove(m) # ensure that we're later in the list - candidates.append(m) - - return candidates diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__init__.py deleted file mode 100644 index 256ddc9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__init__.py +++ /dev/null @@ -1,291 +0,0 @@ -############################################################################## -# Copyright (c) 2020 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -############################################################################## - -import itertools -from types import FunctionType - -from zope.interface import Interface -from zope.interface import classImplements -from zope.interface.interface import InterfaceClass -from zope.interface.interface import _decorator_non_return -from zope.interface.interface import fromFunction - - -__all__ = [ - # Nothing public here. -] - -# pylint:disable=inherit-non-class, -# pylint:disable=no-self-argument,no-method-argument -# pylint:disable=unexpected-special-method-signature - - -class optional: - # Apply this decorator to a method definition to make it - # optional (remove it from the list of required names), overriding - # the definition inherited from the ABC. - def __init__(self, method): - self.__doc__ = method.__doc__ - - -class ABCInterfaceClass(InterfaceClass): - """ - An interface that is automatically derived from a - :class:`abc.ABCMeta` type. - - Internal use only. - - The body of the interface definition *must* define - a property ``abc`` that is the ABC to base the interface on. - - If ``abc`` is *not* in the interface definition, a regular - interface will be defined instead (but ``extra_classes`` is still - respected). - - Use the ``@optional`` decorator on method definitions if - the ABC defines methods that are not actually required in all cases - because the Python language has multiple ways to implement a protocol. - For example, the ``iter()`` protocol can be implemented with - ``__iter__`` or the pair ``__len__`` and ``__getitem__``. - - When created, any existing classes that are registered to conform - to the ABC are declared to implement this interface. This is *not* - automatically updated as the ABC registry changes. If the body of the - interface definition defines ``extra_classes``, it should be a - tuple giving additional classes to declare implement the interface. - - Note that this is not fully symmetric. For example, it is usually - the case that a subclass relationship carries the interface - declarations over:: - - >>> from zope.interface import Interface - >>> class I1(Interface): - ... pass - ... - >>> from zope.interface import implementer - >>> @implementer(I1) - ... class Root(object): - ... pass - ... - >>> class Child(Root): - ... pass - ... - >>> child = Child() - >>> isinstance(child, Root) - True - >>> from zope.interface import providedBy - >>> list(providedBy(child)) - [] - - However, that's not the case with ABCs and ABC interfaces. Just - because ``isinstance(A(), AnABC)`` and ``isinstance(B(), AnABC)`` - are both true, that doesn't mean there's any class hierarchy - relationship between ``A`` and ``B``, or between either of them - and ``AnABC``. Thus, if ``AnABC`` implemented ``IAnABC``, it would - not follow that either ``A`` or ``B`` implements ``IAnABC`` (nor - their instances provide it):: - - >>> class SizedClass(object): - ... def __len__(self): return 1 - ... - >>> from collections.abc import Sized - >>> isinstance(SizedClass(), Sized) - True - >>> from zope.interface import classImplements - >>> classImplements(Sized, I1) - None - >>> list(providedBy(SizedClass())) - [] - - Thus, to avoid conflicting assumptions, ABCs should not be - declared to implement their parallel ABC interface. Only concrete - classes specifically registered with the ABC should be declared to - do so. - - .. versionadded:: 5.0.0 - """ - - # If we could figure out invalidation, and used some special - # Specification/Declaration instances, and override the method - # ``providedBy`` here, perhaps we could more closely integrate with ABC - # virtual inheritance? - - def __init__(self, name, bases, attrs): - # go ahead and give us a name to ease debugging. - self.__name__ = name - extra_classes = attrs.pop('extra_classes', ()) - ignored_classes = attrs.pop('ignored_classes', ()) - - if 'abc' not in attrs: - # Something like ``IList(ISequence)``: We're extending - # abc interfaces but not an ABC interface ourself. - InterfaceClass.__init__(self, name, bases, attrs) - ABCInterfaceClass.__register_classes( - self, extra_classes, ignored_classes, - ) - self.__class__ = InterfaceClass - return - - based_on = attrs.pop('abc') - self.__abc = based_on - self.__extra_classes = tuple(extra_classes) - self.__ignored_classes = tuple(ignored_classes) - - assert name[1:] == based_on.__name__, (name, based_on) - methods = { - # Passing the name is important in case of aliases, - # e.g., ``__ror__ = __or__``. - k: self.__method_from_function(v, k) - for k, v in vars(based_on).items() - if isinstance(v, FunctionType) and - not self.__is_private_name(k) and - not self.__is_reverse_protocol_name(k) - } - - methods['__doc__'] = self.__create_class_doc(attrs) - # Anything specified in the body takes precedence. - methods.update(attrs) - InterfaceClass.__init__(self, name, bases, methods) - self.__register_classes() - - @staticmethod - def __optional_methods_to_docs(attrs): - optionals = {k: v for k, v in attrs.items() if isinstance(v, optional)} - for k in optionals: - attrs[k] = _decorator_non_return - - if not optionals: - return '' - - docs = "\n\nThe following methods are optional:\n - " + "\n-".join( - f"{k}\n{v.__doc__}" for k, v in optionals.items() - ) - return docs - - def __create_class_doc(self, attrs): - based_on = self.__abc - - def ref(c): - mod = c.__module__ - name = c.__name__ - if mod == str.__module__: - return "`%s`" % name - if mod == '_io': - mod = 'io' - return f"`{mod}.{name}`" - - implementations_doc = "\n - ".join( - ref(c) - for c in sorted(self.getRegisteredConformers(), key=ref) - ) - if implementations_doc: - implementations_doc = ( - "\n\nKnown implementations are:\n\n - " + implementations_doc - ) - - based_on_doc = (based_on.__doc__ or '') - based_on_doc = based_on_doc.splitlines() - based_on_doc = based_on_doc[0] if based_on_doc else '' - - doc = """Interface for the ABC `{}.{}`.\n\n{}{}{}""".format( - based_on.__module__, based_on.__name__, - attrs.get('__doc__', based_on_doc), - self.__optional_methods_to_docs(attrs), - implementations_doc - ) - return doc - - @staticmethod - def __is_private_name(name): - if name.startswith('__') and name.endswith('__'): - return False - return name.startswith('_') - - @staticmethod - def __is_reverse_protocol_name(name): - # The reverse names, like __rand__, - # aren't really part of the protocol. The interpreter has - # very complex behaviour around invoking those. PyPy - # doesn't always even expose them as attributes. - return name.startswith('__r') and name.endswith('__') - - def __method_from_function(self, function, name): - method = fromFunction(function, self, name=name) - # Eliminate the leading *self*, which is implied in - # an interface, but explicit in an ABC. - method.positional = method.positional[1:] - return method - - def __register_classes(self, conformers=None, ignored_classes=None): - # Make the concrete classes already present in our ABC's registry - # declare that they implement this interface. - conformers = ( - conformers if conformers is not None - else self.getRegisteredConformers() - ) - ignored = ( - ignored_classes if ignored_classes is not None - else self.__ignored_classes - ) - for cls in conformers: - if cls in ignored: - continue - classImplements(cls, self) - - def getABC(self): - """ - Return the ABC this interface represents. - """ - return self.__abc - - def getRegisteredConformers(self): - """ - Return an iterable of the classes that are known to conform to - the ABC this interface parallels. - """ - based_on = self.__abc - - # The registry only contains things that aren't already - # known to be subclasses of the ABC. But the ABC is in charge - # of checking that, so its quite possible that registrations - # are in fact ignored, winding up just in the _abc_cache. - try: - registered = ( - list(based_on._abc_registry) + list(based_on._abc_cache) - ) - except AttributeError: - # Rewritten in C in CPython 3.7. - # These expose the underlying weakref. - from abc import _get_dump - data = _get_dump(based_on) - registry = data[0] - cache = data[1] - registered = [x() for x in itertools.chain(registry, cache)] - registered = [x for x in registered if x is not None] - - return set(itertools.chain(registered, self.__extra_classes)) - - -def _create_ABCInterface(): - # It's a two-step process to create the root ABCInterface, because without - # specifying a corresponding ABC, using the normal constructor gets us a - # plain InterfaceClass object, and there is no ABC to associate with the - # root. - abc_name_bases_attrs = ('ABCInterface', (Interface,), {}) - instance = ABCInterfaceClass.__new__( - ABCInterfaceClass, *abc_name_bases_attrs, - ) - InterfaceClass.__init__(instance, *abc_name_bases_attrs) - return instance - - -ABCInterface = _create_ABCInterface() diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 5c4cb21..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/builtins.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/builtins.cpython-38.pyc deleted file mode 100644 index 03cf143..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/builtins.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/collections.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/collections.cpython-38.pyc deleted file mode 100644 index 7b8d30d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/collections.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/idatetime.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/idatetime.cpython-38.pyc deleted file mode 100644 index b68b09f..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/idatetime.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/interfaces.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/interfaces.cpython-38.pyc deleted file mode 100644 index ada439b..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/interfaces.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/io.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/io.cpython-38.pyc deleted file mode 100644 index e638943..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/io.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/mapping.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/mapping.cpython-38.pyc deleted file mode 100644 index a75430a..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/mapping.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/numbers.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/numbers.cpython-38.pyc deleted file mode 100644 index b7b1808..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/numbers.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/sequence.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/sequence.cpython-38.pyc deleted file mode 100644 index a2a1028..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/__pycache__/sequence.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/builtins.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/builtins.py deleted file mode 100644 index 09de5b3..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/builtins.py +++ /dev/null @@ -1,122 +0,0 @@ -############################################################################## -# Copyright (c) 2020 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -############################################################################## -""" -Interface definitions for builtin types. - -After this module is imported, the standard library types will declare -that they implement the appropriate interface. - -.. versionadded:: 5.0.0 -""" - -from zope.interface import classImplements -from zope.interface.common import collections -from zope.interface.common import io -from zope.interface.common import numbers - - -__all__ = [ - 'IList', - 'ITuple', - 'ITextString', - 'IByteString', - 'INativeString', - 'IBool', - 'IDict', - 'IFile', -] - - -# pylint:disable=no-self-argument -class IList(collections.IMutableSequence): - """ - Interface for :class:`list` - """ - extra_classes = (list,) - - def sort(key=None, reverse=False): - """ - Sort the list in place and return None. - - *key* and *reverse* must be passed by name only. - """ - - -class ITuple(collections.ISequence): - """ - Interface for :class:`tuple` - """ - extra_classes = (tuple,) - - -class ITextString(collections.ISequence): - """ - Interface for text ("unicode") strings. - - This is :class:`str` - """ - extra_classes = (str,) - - -class IByteString(collections.IByteString): - """ - Interface for immutable byte strings. - - On all Python versions this is :class:`bytes`. - - Unlike :class:`zope.interface.common.collections.IByteString` - (the parent of this interface) this does *not* include - :class:`bytearray`. - """ - extra_classes = (bytes,) - - -class INativeString(ITextString): - """ - Interface for native strings. - - On all Python versions, this is :class:`str`. Tt extends - :class:`ITextString`. - """ - - -# We're not extending ABCInterface so extra_classes won't work -classImplements(str, INativeString) - - -class IBool(numbers.IIntegral): - """ - Interface for :class:`bool` - """ - extra_classes = (bool,) - - -class IDict(collections.IMutableMapping): - """ - Interface for :class:`dict` - """ - extra_classes = (dict,) - - -class IFile(io.IIOBase): - """ - Interface for :class:`file`. - - It is recommended to use the interfaces from - :mod:`zope.interface.common.io` instead of this interface. - - On Python 3, there is no single implementation of this interface; - depending on the arguments, the :func:`open` builtin can return - many different classes that implement different interfaces from - :mod:`zope.interface.common.io`. - """ - extra_classes = () diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/collections.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/collections.py deleted file mode 100644 index 543266d..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/collections.py +++ /dev/null @@ -1,267 +0,0 @@ -############################################################################## -# Copyright (c) 2020 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -############################################################################## -""" -Interface definitions paralleling the abstract base classes defined in -:mod:`collections.abc`. - -After this module is imported, the standard library types will declare that -they implement the appropriate interface. While most standard library types -will properly implement that interface (that is, ``verifyObject(ISequence, -list()))`` will pass, for example), a few might not: - - - `memoryview` doesn't feature all the defined methods of - ``ISequence`` such as ``count``; it is still declared to provide - ``ISequence`` though. - - - `collections.deque.pop` doesn't accept the ``index`` argument of - `collections.abc.MutableSequence.pop` - - - `range.index` does not accept the ``start`` and ``stop`` arguments. - -.. versionadded:: 5.0.0 -""" - -import sys -from abc import ABCMeta -from collections import OrderedDict -from collections import UserDict -from collections import UserList -from collections import UserString -from collections import abc - -from zope.interface.common import ABCInterface -from zope.interface.common import optional - - -# pylint:disable=inherit-non-class, -# pylint:disable=no-self-argument,no-method-argument -# pylint:disable=unexpected-special-method-signature -# pylint:disable=no-value-for-parameter - - -def _new_in_ver(name, ver, - bases_if_missing=(ABCMeta,), - register_if_missing=()): - if ver: - return getattr(abc, name) - - # TODO: It's a shame to have to repeat the bases when - # the ABC is missing. Can we DRY that? - missing = ABCMeta(name, bases_if_missing, { - '__doc__': "The ABC %s is not defined in this version of Python." % ( - name - ), - }) - - for c in register_if_missing: - missing.register(c) - - return missing - - -__all__ = [ - 'IAsyncGenerator', - 'IAsyncIterable', - 'IAsyncIterator', - 'IAwaitable', - 'ICollection', - 'IContainer', - 'ICoroutine', - 'IGenerator', - 'IHashable', - 'IItemsView', - 'IIterable', - 'IIterator', - 'IKeysView', - 'IMapping', - 'IMappingView', - 'IMutableMapping', - 'IMutableSequence', - 'IMutableSet', - 'IReversible', - 'ISequence', - 'ISet', - 'ISized', - 'IValuesView', -] - - -class IContainer(ABCInterface): - abc = abc.Container - - @optional - def __contains__(other): - """ - Optional method. If not provided, the interpreter will use - ``__iter__`` or the old ``__getitem__`` protocol - to implement ``in``. - """ - - -class IHashable(ABCInterface): - abc = abc.Hashable - - -class IIterable(ABCInterface): - abc = abc.Iterable - - @optional - def __iter__(): - """ - Optional method. If not provided, the interpreter will - implement `iter` using the old ``__getitem__`` protocol. - """ - - -class IIterator(IIterable): - abc = abc.Iterator - - -class IReversible(IIterable): - abc = _new_in_ver('Reversible', True, (IIterable.getABC(),)) - - @optional - def __reversed__(): - """ - Optional method. If this isn't present, the interpreter - will use ``__len__`` and ``__getitem__`` to implement the - `reversed` builtin. - """ - - -class IGenerator(IIterator): - # New in Python 3.5 - abc = _new_in_ver('Generator', True, (IIterator.getABC(),)) - - -class ISized(ABCInterface): - abc = abc.Sized - - -# ICallable is not defined because there's no standard signature. - - -class ICollection(ISized, - IIterable, - IContainer): - abc = _new_in_ver( - 'Collection', - True, - (ISized.getABC(), IIterable.getABC(), IContainer.getABC()) - ) - - -class ISequence(IReversible, - ICollection): - abc = abc.Sequence - extra_classes = (UserString,) - # On Python 2, basestring was registered as an ISequence, and - # its subclass str is an IByteString. If we also register str as - # an ISequence, that tends to lead to inconsistent resolution order. - ignored_classes = () - - @optional - def __reversed__(): - """ - Optional method. If this isn't present, the interpreter - will use ``__len__`` and ``__getitem__`` to implement the - `reversed` builtin. - """ - - @optional - def __iter__(): - """ - Optional method. If not provided, the interpreter will - implement `iter` using the old ``__getitem__`` protocol. - """ - - -class IMutableSequence(ISequence): - abc = abc.MutableSequence - extra_classes = (UserList,) - - -class IByteString(ISequence): - """ - This unifies `bytes` and `bytearray`. - """ - abc = _new_in_ver( - 'ByteString', True, (ISequence.getABC(),), (bytes, bytearray), - ) - - -class ISet(ICollection): - abc = abc.Set - - -class IMutableSet(ISet): - abc = abc.MutableSet - - -class IMapping(ICollection): - abc = abc.Mapping - extra_classes = (dict,) - # OrderedDict is a subclass of dict. On CPython 2, - # it winds up registered as a IMutableMapping, which - # produces an inconsistent IRO if we also try to register it - # here. - ignored_classes = (OrderedDict,) - - -class IMutableMapping(IMapping): - abc = abc.MutableMapping - extra_classes = (dict, UserDict,) - ignored_classes = (OrderedDict,) - - -class IMappingView(ISized): - abc = abc.MappingView - - -class IItemsView(IMappingView, ISet): - abc = abc.ItemsView - - -class IKeysView(IMappingView, ISet): - abc = abc.KeysView - - -class IValuesView(IMappingView, ICollection): - abc = abc.ValuesView - - @optional - def __contains__(other): - """ - Optional method. If not provided, the interpreter will use - ``__iter__`` or the old ``__len__`` and ``__getitem__`` protocol - to implement ``in``. - """ - - -class IAwaitable(ABCInterface): - abc = _new_in_ver('Awaitable', True) - - -class ICoroutine(IAwaitable): - abc = _new_in_ver('Coroutine', True) - - -class IAsyncIterable(ABCInterface): - abc = _new_in_ver('AsyncIterable', True) - - -class IAsyncIterator(IAsyncIterable): - abc = _new_in_ver('AsyncIterator', True) - - -class IAsyncGenerator(IAsyncIterator): - abc = _new_in_ver('AsyncGenerator', True) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/idatetime.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/idatetime.py deleted file mode 100644 index 24b2606..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/idatetime.py +++ /dev/null @@ -1,622 +0,0 @@ -############################################################################## -# Copyright (c) 2002 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -############################################################################## -"""Datetime interfaces. - -This module is called idatetime because if it were called datetime the import -of the real datetime would fail. -""" -from datetime import date -from datetime import datetime -from datetime import time -from datetime import timedelta -from datetime import tzinfo - -from zope.interface import Attribute -from zope.interface import Interface -from zope.interface import classImplements - - -class ITimeDeltaClass(Interface): - """This is the timedelta class interface. - - This is symbolic; this module does **not** make - `datetime.timedelta` provide this interface. - """ - - min = Attribute("The most negative timedelta object") - - max = Attribute("The most positive timedelta object") - - resolution = Attribute( - "The smallest difference between non-equal timedelta objects") - - -class ITimeDelta(ITimeDeltaClass): - """Represent the difference between two datetime objects. - - Implemented by `datetime.timedelta`. - - Supported operators: - - - add, subtract timedelta - - unary plus, minus, abs - - compare to timedelta - - multiply, divide by int/long - - In addition, `.datetime` supports subtraction of two `.datetime` objects - returning a `.timedelta`, and addition or subtraction of a `.datetime` - and a `.timedelta` giving a `.datetime`. - - Representation: (days, seconds, microseconds). - """ - - days = Attribute("Days between -999999999 and 999999999 inclusive") - - seconds = Attribute("Seconds between 0 and 86399 inclusive") - - microseconds = Attribute("Microseconds between 0 and 999999 inclusive") - - -class IDateClass(Interface): - """This is the date class interface. - - This is symbolic; this module does **not** make - `datetime.date` provide this interface. - """ - - min = Attribute("The earliest representable date") - - max = Attribute("The latest representable date") - - resolution = Attribute( - "The smallest difference between non-equal date objects") - - def today(): - """Return the current local time. - - This is equivalent to ``date.fromtimestamp(time.time())``""" - - def fromtimestamp(timestamp): - """Return the local date from a POSIX timestamp (like time.time()) - - This may raise `ValueError`, if the timestamp is out of the range of - values supported by the platform C ``localtime()`` function. It's - common for this to be restricted to years from 1970 through 2038. Note - that on non-POSIX systems that include leap seconds in their notion of - a timestamp, leap seconds are ignored by `fromtimestamp`. - """ - - def fromordinal(ordinal): - """Return the date corresponding to the proleptic Gregorian ordinal. - - January 1 of year 1 has ordinal 1. `ValueError` is raised unless - 1 <= ordinal <= date.max.toordinal(). - - For any date *d*, ``date.fromordinal(d.toordinal()) == d``. - """ - - -class IDate(IDateClass): - """Represents a date (year, month and day) in an idealized calendar. - - Implemented by `datetime.date`. - - Operators: - - __repr__, __str__ - __cmp__, __hash__ - __add__, __radd__, __sub__ (add/radd only with timedelta arg) - """ - - year = Attribute("Between MINYEAR and MAXYEAR inclusive.") - - month = Attribute("Between 1 and 12 inclusive") - - day = Attribute( - "Between 1 and the number of days " - "in the given month of the given year." - ) - - def replace(year, month, day): - """Return a date with the same value. - - Except for those members given new values by whichever keyword - arguments are specified. - - For example, if ``d == date(2002, 12, 31)``, then - ``d.replace(day=26) == date(2000, 12, 26)``. - """ - - def timetuple(): - """Return a 9-element tuple of the form returned by `time.localtime`. - - The hours, minutes and seconds are 0, and the DST flag is -1. - ``d.timetuple()`` is equivalent to - ``(d.year, d.month, d.day, 0, 0, 0, d.weekday(), d.toordinal() - - date(d.year, 1, 1).toordinal() + 1, -1)`` - """ - - def toordinal(): - """Return the proleptic Gregorian ordinal of the date - - January 1 of year 1 has ordinal 1. For any date object *d*, - ``date.fromordinal(d.toordinal()) == d``. - """ - - def weekday(): - """Return the day of the week as an integer. - - Monday is 0 and Sunday is 6. For example, - ``date(2002, 12, 4).weekday() == 2``, a Wednesday. - - .. seealso:: `isoweekday`. - """ - - def isoweekday(): - """Return the day of the week as an integer. - - Monday is 1 and Sunday is 7. For example, - date(2002, 12, 4).isoweekday() == 3, a Wednesday. - - .. seealso:: `weekday`, `isocalendar`. - """ - - def isocalendar(): - """Return a 3-tuple, (ISO year, ISO week number, ISO weekday). - - The ISO calendar is a widely used variant of the Gregorian calendar. - See http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm for a good - explanation. - - The ISO year consists of 52 or 53 full weeks, and where a week starts - on a Monday and ends on a Sunday. The first week of an ISO year is the - first (Gregorian) calendar week of a year containing a Thursday. This - is called week number 1, and the ISO year of that Thursday is the same - as its Gregorian year. - - For example, 2004 begins on a Thursday, so the first week of ISO year - 2004 begins on Monday, 29 Dec 2003 and ends on Sunday, 4 Jan 2004, so - that ``date(2003, 12, 29).isocalendar() == (2004, 1, 1)`` and - ``date(2004, 1, 4).isocalendar() == (2004, 1, 7)``. - """ - - def isoformat(): - """Return a string representing the date in ISO 8601 format. - - This is 'YYYY-MM-DD'. - For example, ``date(2002, 12, 4).isoformat() == '2002-12-04'``. - """ - - def __str__(): - """For a date *d*, ``str(d)`` is equivalent to ``d.isoformat()``.""" - - def ctime(): - """Return a string representing the date. - - For example date(2002, 12, 4).ctime() == 'Wed Dec 4 00:00:00 2002'. - d.ctime() is equivalent to time.ctime(time.mktime(d.timetuple())) - on platforms where the native C ctime() function - (which `time.ctime` invokes, but which date.ctime() does not invoke) - conforms to the C standard. - """ - - def strftime(format): - """Return a string representing the date. - - Controlled by an explicit format string. Format codes referring to - hours, minutes or seconds will see 0 values. - """ - - -class IDateTimeClass(Interface): - """This is the datetime class interface. - - This is symbolic; this module does **not** make - `datetime.datetime` provide this interface. - """ - - min = Attribute("The earliest representable datetime") - - max = Attribute("The latest representable datetime") - - resolution = Attribute( - "The smallest possible difference between non-equal datetime objects") - - def today(): - """Return the current local datetime, with tzinfo None. - - This is equivalent to ``datetime.fromtimestamp(time.time())``. - - .. seealso:: `now`, `fromtimestamp`. - """ - - def now(tz=None): - """Return the current local date and time. - - If optional argument *tz* is None or not specified, this is like - `today`, but, if possible, supplies more precision than can be gotten - from going through a `time.time` timestamp (for example, this may be - possible on platforms supplying the C ``gettimeofday()`` function). - - Else tz must be an instance of a class tzinfo subclass, and the current - date and time are converted to tz's time zone. In this case the result - is equivalent to tz.fromutc(datetime.utcnow().replace(tzinfo=tz)). - - .. seealso:: `today`, `utcnow`. - """ - - def utcnow(): - """Return the current UTC date and time, with tzinfo None. - - This is like `now`, but returns the current UTC date and time, as a - naive datetime object. - - .. seealso:: `now`. - """ - - def fromtimestamp(timestamp, tz=None): - """Return the local date and time corresponding to the POSIX timestamp. - - Same as is returned by time.time(). If optional argument tz is None or - not specified, the timestamp is converted to the platform's local date - and time, and the returned datetime object is naive. - - Else tz must be an instance of a class tzinfo subclass, and the - timestamp is converted to tz's time zone. In this case the result is - equivalent to - ``tz.fromutc(datetime.utcfromtimestamp(timestamp).replace(tzinfo=tz))`` - - fromtimestamp() may raise `ValueError`, if the timestamp is out of the - range of values supported by the platform C localtime() or gmtime() - functions. It's common for this to be restricted to years in 1970 - through 2038. Note that on non-POSIX systems that include leap seconds - in their notion of a timestamp, leap seconds are ignored by - fromtimestamp(), and then it's possible to have two timestamps - differing by a second that yield identical datetime objects. - - .. seealso:: `utcfromtimestamp`. - """ - - def utcfromtimestamp(timestamp): - """Return the UTC datetime from the POSIX timestamp with tzinfo None. - - This may raise `ValueError`, if the timestamp is out of the range of - values supported by the platform C ``gmtime()`` function. It's common - for this to be restricted to years in 1970 through 2038. - - .. seealso:: `fromtimestamp`. - """ - - def fromordinal(ordinal): - """Return the datetime from the proleptic Gregorian ordinal. - - January 1 of year 1 has ordinal 1. `ValueError` is raised unless - 1 <= ordinal <= datetime.max.toordinal(). - The hour, minute, second and microsecond of the result are all 0, and - tzinfo is None. - """ - - def combine(date, time): - """Return a new datetime object. - - Its date members are equal to the given date object's, and whose time - and tzinfo members are equal to the given time object's. For any - datetime object *d*, ``d == datetime.combine(d.date(), d.timetz())``. - If date is a datetime object, its time and tzinfo members are ignored. - """ - - -class IDateTime(IDate, IDateTimeClass): - """Contains all the information from a date object and a time object. - - Implemented by `datetime.datetime`. - """ - - year = Attribute("Year between MINYEAR and MAXYEAR inclusive") - - month = Attribute("Month between 1 and 12 inclusive") - - day = Attribute( - "Day between 1 and the number of days in the given month of the year") - - hour = Attribute("Hour in range(24)") - - minute = Attribute("Minute in range(60)") - - second = Attribute("Second in range(60)") - - microsecond = Attribute("Microsecond in range(1000000)") - - tzinfo = Attribute( - """The object passed as the tzinfo argument to the datetime constructor - or None if none was passed""") - - def date(): - """Return date object with same year, month and day.""" - - def time(): - """Return time object with same hour, minute, second, microsecond. - - tzinfo is None. - - .. seealso:: Method :meth:`timetz`. - """ - - def timetz(): - """Return time object with same hour, minute, second, microsecond, - and tzinfo. - - .. seealso:: Method :meth:`time`. - """ - - def replace(year, month, day, hour, minute, second, microsecond, tzinfo): - """Return a datetime with the same members, except for those members - given new values by whichever keyword arguments are specified. - - Note that ``tzinfo=None`` can be specified to create a naive datetime - from an aware datetime with no conversion of date and time members. - """ - - def astimezone(tz): - """Return a datetime object with new tzinfo member tz, adjusting the - date and time members so the result is the same UTC time as self, but - in tz's local time. - - tz must be an instance of a tzinfo subclass, and its utcoffset() and - dst() methods must not return None. self must be aware (self.tzinfo - must not be None, and self.utcoffset() must not return None). - - If self.tzinfo is tz, self.astimezone(tz) is equal to self: no - adjustment of date or time members is performed. Else the result is - local time in time zone tz, representing the same UTC time as self: - - after astz = dt.astimezone(tz), astz - astz.utcoffset() - - will usually have the same date and time members as dt - - dt.utcoffset(). The discussion of class `datetime.tzinfo` explains - the cases at Daylight Saving Time transition boundaries where this - cannot be achieved (an issue only if tz models both standard and - daylight time). - - If you merely want to attach a time zone object *tz* to a datetime - *dt* without adjustment of date and time members, use - ``dt.replace(tzinfo=tz)``. - - If you merely want to remove the time zone object from an aware - datetime dt without conversion of date and time members, use - ``dt.replace(tzinfo=None)``. - - Note that the default `tzinfo.fromutc` method can be overridden in a - tzinfo subclass to effect the result returned by `astimezone`. - """ - - def utcoffset(): - """Return the timezone offset in minutes east of UTC (negative west of - UTC).""" - - def dst(): - """Return 0 if DST is not in effect, or the DST offset (in minutes - eastward) if DST is in effect. - """ - - def tzname(): - """Return the timezone name.""" - - def timetuple(): - """Return a 9-tuple of the form returned by `time.localtime`.""" - - def utctimetuple(): - """Return UTC time tuple compatilble with `time.gmtime`.""" - - def toordinal(): - """Return the proleptic Gregorian ordinal of the date. - - The same as self.date().toordinal(). - """ - - def weekday(): - """Return the day of the week as an integer. - - Monday is 0 and Sunday is 6. The same as self.date().weekday(). - See also isoweekday(). - """ - - def isoweekday(): - """Return the day of the week as an integer. - - Monday is 1 and Sunday is 7. The same as self.date().isoweekday. - - .. seealso:: `weekday`, `isocalendar`. - """ - - def isocalendar(): - """Return a 3-tuple, (ISO year, ISO week number, ISO weekday). - - The same as self.date().isocalendar(). - """ - - def isoformat(sep='T'): - """Return a string representing the date and time in ISO 8601 format. - - YYYY-MM-DDTHH:MM:SS.mmmmmm or YYYY-MM-DDTHH:MM:SS if microsecond is 0 - - If `utcoffset` does not return None, a 6-character string is appended, - giving the UTC offset in (signed) hours and minutes: - - YYYY-MM-DDTHH:MM:SS.mmmmmm+HH:MM or YYYY-MM-DDTHH:MM:SS+HH:MM - if microsecond is 0. - - The optional argument sep (default 'T') is a one-character separator, - placed between the date and time portions of the result. - """ - - def __str__(): - """Convert to a stirng - - For a datetime instance *d*, ``str(d)`` is equivalent to - ``d.isoformat(' ')``. - """ - - def ctime(): - """Return a string representing the date and time. - - ``datetime(2002, 12, 4, 20, 30, 40).ctime()`` yields - ``'Wed Dec 4 20:30:40 2002'``. - ``d.ctime()`` is equivalent to - ``time.ctime(time.mktime(d.timetuple()))`` on platforms where the - native C ``ctime()`` function (which `time.ctime` invokes, but which - `datetime.ctime` does not invoke) conforms to the C standard. - """ - - def strftime(format): - """Return a string representing the date and time. - - This is controlled by an explicit format string. - """ - - -class ITimeClass(Interface): - """This is the time class interface. - - This is symbolic; this module does **not** make - `datetime.time` provide this interface. - - """ - - min = Attribute("The earliest representable time") - - max = Attribute("The latest representable time") - - resolution = Attribute( - "The smallest possible difference between non-equal time objects") - - -class ITime(ITimeClass): - """Represent time with time zone. - - Implemented by `datetime.time`. - - Operators: - - __repr__, __str__ - __cmp__, __hash__ - """ - - hour = Attribute("Hour in range(24)") - - minute = Attribute("Minute in range(60)") - - second = Attribute("Second in range(60)") - - microsecond = Attribute("Microsecond in range(1000000)") - - tzinfo = Attribute( - """The object passed as the tzinfo argument to the time constructor - or None if none was passed.""") - - def replace(hour, minute, second, microsecond, tzinfo): - """Return a time with the same value. - - Except for those members given new values by whichever keyword - arguments are specified. Note that tzinfo=None can be specified - to create a naive time from an aware time, without conversion of the - time members. - """ - - def isoformat(): - """Return a string representing the time in ISO 8601 format. - - That is HH:MM:SS.mmmmmm or, if self.microsecond is 0, HH:MM:SS - If utcoffset() does not return None, a 6-character string is appended, - giving the UTC offset in (signed) hours and minutes: - HH:MM:SS.mmmmmm+HH:MM or, if self.microsecond is 0, HH:MM:SS+HH:MM - """ - - def __str__(): - """For a time t, str(t) is equivalent to t.isoformat().""" - - def strftime(format): - """Return a string representing the time. - - This is controlled by an explicit format string. - """ - - def utcoffset(): - """Return the timezone offset in minutes east of UTC (negative west of - UTC). - - If tzinfo is None, returns None, else returns - self.tzinfo.utcoffset(None), and raises an exception if the latter - doesn't return None or a timedelta object representing a whole number - of minutes with magnitude less than one day. - """ - - def dst(): - """Return 0 if DST is not in effect, or the DST offset (in minutes - eastward) if DST is in effect. - - If tzinfo is None, returns None, else returns self.tzinfo.dst(None), - and raises an exception if the latter doesn't return None, or a - timedelta object representing a whole number of minutes with - magnitude less than one day. - """ - - def tzname(): - """Return the timezone name. - - If tzinfo is None, returns None, else returns self.tzinfo.tzname(None), - or raises an exception if the latter doesn't return None or a string - object. - """ - - -class ITZInfo(Interface): - """Time zone info class. - """ - - def utcoffset(dt): - """Return offset of local time from UTC, in minutes east of UTC. - - If local time is west of UTC, this should be negative. - Note that this is intended to be the total offset from UTC; - for example, if a tzinfo object represents both time zone and DST - adjustments, utcoffset() should return their sum. If the UTC offset - isn't known, return None. Else the value returned must be a timedelta - object specifying a whole number of minutes in the range -1439 to 1439 - inclusive (1440 = 24*60; the magnitude of the offset must be less - than one day). - """ - - def dst(dt): - """Return the daylight saving time (DST) adjustment, in minutes east - of UTC, or None if DST information isn't known. - """ - - def tzname(dt): - """Return the time zone name corresponding to the datetime object as - a string. - """ - - def fromutc(dt): - """Return an equivalent datetime in self's local time.""" - - -classImplements(timedelta, ITimeDelta) -classImplements(date, IDate) -classImplements(datetime, IDateTime) -classImplements(time, ITime) -classImplements(tzinfo, ITZInfo) - -# directlyProvides(timedelta, ITimeDeltaClass) -# directlyProvides(date, IDateClass) -# directlyProvides(datetime, IDateTimeClass) -# directlyProvides(time, ITimeClass) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/interfaces.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/interfaces.py deleted file mode 100644 index 9075795..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/interfaces.py +++ /dev/null @@ -1,281 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Interfaces for standard python exceptions -""" -from zope.interface import Interface -from zope.interface import classImplements - - -class IException(Interface): - "Interface for `Exception`" - - -classImplements(Exception, IException) # noqa E305 - - -class IStandardError(IException): - "Interface for `StandardError` (no longer existing.)" - - -class IWarning(IException): - "Interface for `Warning`" - - -classImplements(Warning, IWarning) # noqa E305 - - -class ISyntaxError(IStandardError): - "Interface for `SyntaxError`" - - -classImplements(SyntaxError, ISyntaxError) # noqa E305 - - -class ILookupError(IStandardError): - "Interface for `LookupError`" - - -classImplements(LookupError, ILookupError) # noqa E305 - - -class IValueError(IStandardError): - "Interface for `ValueError`" - - -classImplements(ValueError, IValueError) # noqa E305 - - -class IRuntimeError(IStandardError): - "Interface for `RuntimeError`" - - -classImplements(RuntimeError, IRuntimeError) # noqa E305 - - -class IArithmeticError(IStandardError): - "Interface for `ArithmeticError`" - - -classImplements(ArithmeticError, IArithmeticError) # noqa E305 - - -class IAssertionError(IStandardError): - "Interface for `AssertionError`" - - -classImplements(AssertionError, IAssertionError) # noqa E305 - - -class IAttributeError(IStandardError): - "Interface for `AttributeError`" - - -classImplements(AttributeError, IAttributeError) # noqa E305 - - -class IDeprecationWarning(IWarning): - "Interface for `DeprecationWarning`" - - -classImplements(DeprecationWarning, IDeprecationWarning) # noqa E305 - - -class IEOFError(IStandardError): - "Interface for `EOFError`" - - -classImplements(EOFError, IEOFError) # noqa E305 - - -class IEnvironmentError(IStandardError): - "Interface for `EnvironmentError`" - - -classImplements(EnvironmentError, IEnvironmentError) # noqa E305 - - -class IFloatingPointError(IArithmeticError): - "Interface for `FloatingPointError`" - - -classImplements(FloatingPointError, IFloatingPointError) # noqa E305 - - -class IIOError(IEnvironmentError): - "Interface for `IOError`" - - -classImplements(IOError, IIOError) # noqa E305 - - -class IImportError(IStandardError): - "Interface for `ImportError`" - - -classImplements(ImportError, IImportError) # noqa E305 - - -class IIndentationError(ISyntaxError): - "Interface for `IndentationError`" - - -classImplements(IndentationError, IIndentationError) # noqa E305 - - -class IIndexError(ILookupError): - "Interface for `IndexError`" - - -classImplements(IndexError, IIndexError) # noqa E305 - - -class IKeyError(ILookupError): - "Interface for `KeyError`" - - -classImplements(KeyError, IKeyError) # noqa E305 - - -class IKeyboardInterrupt(IStandardError): - "Interface for `KeyboardInterrupt`" - - -classImplements(KeyboardInterrupt, IKeyboardInterrupt) # noqa E305 - - -class IMemoryError(IStandardError): - "Interface for `MemoryError`" - - -classImplements(MemoryError, IMemoryError) # noqa E305 - - -class INameError(IStandardError): - "Interface for `NameError`" - - -classImplements(NameError, INameError) # noqa E305 - - -class INotImplementedError(IRuntimeError): - "Interface for `NotImplementedError`" - - -classImplements(NotImplementedError, INotImplementedError) # noqa E305 - - -class IOSError(IEnvironmentError): - "Interface for `OSError`" - - -classImplements(OSError, IOSError) # noqa E305 - - -class IOverflowError(IArithmeticError): - "Interface for `ArithmeticError`" - - -classImplements(OverflowError, IOverflowError) # noqa E305 - - -class IOverflowWarning(IWarning): - """Deprecated, no standard class implements this. - - This was the interface for ``OverflowWarning`` prior to Python 2.5, - but that class was removed for all versions after that. - """ - - -class IReferenceError(IStandardError): - "Interface for `ReferenceError`" - - -classImplements(ReferenceError, IReferenceError) # noqa E305 - - -class IRuntimeWarning(IWarning): - "Interface for `RuntimeWarning`" - - -classImplements(RuntimeWarning, IRuntimeWarning) # noqa E305 - - -class IStopIteration(IException): - "Interface for `StopIteration`" - - -classImplements(StopIteration, IStopIteration) # noqa E305 - - -class ISyntaxWarning(IWarning): - "Interface for `SyntaxWarning`" - - -classImplements(SyntaxWarning, ISyntaxWarning) # noqa E305 - - -class ISystemError(IStandardError): - "Interface for `SystemError`" - - -classImplements(SystemError, ISystemError) # noqa E305 - - -class ISystemExit(IException): - "Interface for `SystemExit`" - - -classImplements(SystemExit, ISystemExit) # noqa E305 - - -class ITabError(IIndentationError): - "Interface for `TabError`" - - -classImplements(TabError, ITabError) # noqa E305 - - -class ITypeError(IStandardError): - "Interface for `TypeError`" - - -classImplements(TypeError, ITypeError) # noqa E305 - - -class IUnboundLocalError(INameError): - "Interface for `UnboundLocalError`" - - -classImplements(UnboundLocalError, IUnboundLocalError) # noqa E305 - - -class IUnicodeError(IValueError): - "Interface for `UnicodeError`" - - -classImplements(UnicodeError, IUnicodeError) # noqa E305 - - -class IUserWarning(IWarning): - "Interface for `UserWarning`" - - -classImplements(UserWarning, IUserWarning) # noqa E305 - - -class IZeroDivisionError(IArithmeticError): - "Interface for `ZeroDivisionError`" - - -classImplements(ZeroDivisionError, IZeroDivisionError) # noqa E305 diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/io.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/io.py deleted file mode 100644 index 89f1ba8..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/io.py +++ /dev/null @@ -1,44 +0,0 @@ -############################################################################## -# Copyright (c) 2020 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -############################################################################## -""" -Interface definitions paralleling the abstract base classes defined in -:mod:`io`. - -After this module is imported, the standard library types will declare -that they implement the appropriate interface. - -.. versionadded:: 5.0.0 -""" - -import io as abc - -from zope.interface.common import ABCInterface - - -# pylint:disable=inherit-non-class, -# pylint:disable=no-member - -class IIOBase(ABCInterface): - abc = abc.IOBase - - -class IRawIOBase(IIOBase): - abc = abc.RawIOBase - - -class IBufferedIOBase(IIOBase): - abc = abc.BufferedIOBase - extra_classes = () - - -class ITextIOBase(IIOBase): - abc = abc.TextIOBase diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/mapping.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/mapping.py deleted file mode 100644 index d8ea074..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/mapping.py +++ /dev/null @@ -1,177 +0,0 @@ -############################################################################## -# -# Copyright (c) 2001, 2002 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -""" -Mapping Interfaces. - -Importing this module does *not* mark any standard classes as -implementing any of these interfaces. - -While this module is not deprecated, new code should generally use -:mod:`zope.interface.common.collections`, specifically -:class:`~zope.interface.common.collections.IMapping` and -:class:`~zope.interface.common.collections.IMutableMapping`. This -module is occasionally useful for its extremely fine grained breakdown -of interfaces. - -The standard library :class:`dict` and :class:`collections.UserDict` -implement ``IMutableMapping``, but *do not* implement any of the -interfaces in this module. -""" -from zope.interface import Interface -from zope.interface.common import collections - - -class IItemMapping(Interface): - """Simplest readable mapping object - """ - - def __getitem__(key): - """Get a value for a key - - A `KeyError` is raised if there is no value for the key. - """ - - -class IReadMapping(collections.IContainer, IItemMapping): - """ - Basic mapping interface. - - .. versionchanged:: 5.0.0 - Extend ``IContainer`` - """ - - def get(key, default=None): - """Get a value for a key - - The default is returned if there is no value for the key. - """ - - def __contains__(key): - """Tell if a key exists in the mapping.""" - # Optional in IContainer, required by this interface. - - -class IWriteMapping(Interface): - """Mapping methods for changing data""" - - def __delitem__(key): - """Delete a value from the mapping using the key.""" - - def __setitem__(key, value): - """Set a new item in the mapping.""" - - -class IEnumerableMapping(collections.ISized, IReadMapping): - """ - Mapping objects whose items can be enumerated. - - .. versionchanged:: 5.0.0 - Extend ``ISized`` - """ - - def keys(): - """Return the keys of the mapping object. - """ - - def __iter__(): - """Return an iterator for the keys of the mapping object. - """ - - def values(): - """Return the values of the mapping object. - """ - - def items(): - """Return the items of the mapping object. - """ - - -class IMapping(IWriteMapping, IEnumerableMapping): - ''' Simple mapping interface ''' - - -class IIterableMapping(IEnumerableMapping): - """A mapping that has distinct methods for iterating - without copying. - - """ - - -class IClonableMapping(Interface): - """Something that can produce a copy of itself. - - This is available in `dict`. - """ - - def copy(): - "return copy of dict" - - -class IExtendedReadMapping(IIterableMapping): - """ - Something with a particular method equivalent to ``__contains__``. - - On Python 2, `dict` provided the ``has_key`` method, but it was removed - in Python 3. - """ - - -class IExtendedWriteMapping(IWriteMapping): - """Additional mutation methods. - - These are all provided by `dict`. - """ - - def clear(): - "delete all items" - - def update(d): - " Update D from E: for k in E.keys(): D[k] = E[k]" - - def setdefault(key, default=None): - "D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D" - - def pop(k, default=None): - """ - pop(k[,default]) -> value - - Remove specified key and return the corresponding value. - - If key is not found, *default* is returned if given, otherwise - `KeyError` is raised. Note that *default* must not be passed by - name. - """ - - def popitem(): - """remove and return some (key, value) pair as a - 2-tuple; but raise KeyError if mapping is empty""" - - -class IFullMapping( - collections.IMutableMapping, - IExtendedReadMapping, - IExtendedWriteMapping, - IClonableMapping, - IMapping, -): - """ - Full mapping interface. - - Most uses of this interface should instead use - :class:`~zope.interface.commons.collections.IMutableMapping` (one of the - bases of this interface). The required methods are the same. - - .. versionchanged:: 5.0.0 - Extend ``IMutableMapping`` - """ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/numbers.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/numbers.py deleted file mode 100644 index 6b20e09..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/numbers.py +++ /dev/null @@ -1,65 +0,0 @@ -############################################################################## -# Copyright (c) 2020 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -############################################################################## -""" -Interface definitions paralleling the abstract base classes defined in -:mod:`numbers`. - -After this module is imported, the standard library types will declare -that they implement the appropriate interface. - -.. versionadded:: 5.0.0 -""" - -import numbers as abc - -from zope.interface.common import ABCInterface -from zope.interface.common import optional - - -# pylint:disable=inherit-non-class, -# pylint:disable=no-self-argument,no-method-argument -# pylint:disable=unexpected-special-method-signature -# pylint:disable=no-value-for-parameter - - -class INumber(ABCInterface): - abc = abc.Number - - -class IComplex(INumber): - abc = abc.Complex - - @optional - def __complex__(): - """ - Rarely implemented, even in builtin types. - """ - - -class IReal(IComplex): - abc = abc.Real - - @optional - def __complex__(): - """ - Rarely implemented, even in builtin types. - """ - - __floor__ = __ceil__ = __complex__ - - -class IRational(IReal): - abc = abc.Rational - - -class IIntegral(IRational): - abc = abc.Integral diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/sequence.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/sequence.py deleted file mode 100644 index c63f5a4..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/sequence.py +++ /dev/null @@ -1,195 +0,0 @@ -############################################################################## -# -# Copyright (c) 2001, 2002 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -""" -Sequence Interfaces - -Importing this module does *not* mark any standard classes as -implementing any of these interfaces. - -While this module is not deprecated, new code should generally use -:mod:`zope.interface.common.collections`, specifically -:class:`~zope.interface.common.collections.ISequence` and -:class:`~zope.interface.common.collections.IMutableSequence`. This -module is occasionally useful for its fine-grained breakdown of interfaces. - -The standard library :class:`list`, :class:`tuple` and -:class:`collections.UserList`, among others, implement ``ISequence`` -or ``IMutableSequence`` but *do not* implement any of the interfaces -in this module. -""" - -__docformat__ = 'restructuredtext' -from zope.interface import Interface -from zope.interface.common import collections - - -class IMinimalSequence(collections.IIterable): - """Most basic sequence interface. - - All sequences are iterable. This requires at least one of the - following: - - - a `__getitem__()` method that takes a single argument; integer - values starting at 0 must be supported, and `IndexError` should - be raised for the first index for which there is no value, or - - - an `__iter__()` method that returns an iterator as defined in - the Python documentation (http://docs.python.org/lib/typeiter.html). - - """ - - def __getitem__(index): - """``x.__getitem__(index) <==> x[index]`` - - Declaring this interface does not specify whether `__getitem__` - supports slice objects.""" - - -class IFiniteSequence(collections.ISized, IMinimalSequence): - """ - A sequence of bound size. - - .. versionchanged:: 5.0.0 - Extend ``ISized`` - """ - - -class IReadSequence(collections.IContainer, IFiniteSequence): - """ - read interface shared by tuple and list - - This interface is similar to - :class:`~zope.interface.common.collections.ISequence`, but - requires that all instances be totally ordered. Most users - should prefer ``ISequence``. - - .. versionchanged:: 5.0.0 - Extend ``IContainer`` - """ - - def __contains__(item): - """``x.__contains__(item) <==> item in x``""" - # Optional in IContainer, required here. - - def __lt__(other): - """``x.__lt__(other) <==> x < other``""" - - def __le__(other): - """``x.__le__(other) <==> x <= other``""" - - def __eq__(other): - """``x.__eq__(other) <==> x == other``""" - - def __ne__(other): - """``x.__ne__(other) <==> x != other``""" - - def __gt__(other): - """``x.__gt__(other) <==> x > other``""" - - def __ge__(other): - """``x.__ge__(other) <==> x >= other``""" - - def __add__(other): - """``x.__add__(other) <==> x + other``""" - - def __mul__(n): - """``x.__mul__(n) <==> x * n``""" - - def __rmul__(n): - """``x.__rmul__(n) <==> n * x``""" - - -class IExtendedReadSequence(IReadSequence): - """Full read interface for lists""" - - def count(item): - """Return number of occurrences of value""" - - def index(item, *args): - """index(value, [start, [stop]]) -> int - - Return first index of *value* - """ - - -class IUniqueMemberWriteSequence(Interface): - """The write contract for a sequence that may enforce unique members""" - - def __setitem__(index, item): - """``x.__setitem__(index, item) <==> x[index] = item`` - - Declaring this interface does not specify whether `__setitem__` - supports slice objects. - """ - - def __delitem__(index): - """``x.__delitem__(index) <==> del x[index]`` - - Declaring this interface does not specify whether `__delitem__` - supports slice objects. - """ - - def __iadd__(y): - """``x.__iadd__(y) <==> x += y``""" - - def append(item): - """Append item to end""" - - def insert(index, item): - """Insert item before index""" - - def pop(index=-1): - """Remove and return item at index (default last)""" - - def remove(item): - """Remove first occurrence of value""" - - def reverse(): - """Reverse *IN PLACE*""" - - def sort(cmpfunc=None): - """Stable sort *IN PLACE*; `cmpfunc(x, y)` -> -1, 0, 1""" - - def extend(iterable): - """Extend list by appending elements from the iterable""" - - -class IWriteSequence(IUniqueMemberWriteSequence): - """Full write contract for sequences""" - - def __imul__(n): - """``x.__imul__(n) <==> x *= n``""" - - -class ISequence(IReadSequence, IWriteSequence): - """ - Full sequence contract. - - New code should prefer - :class:`~zope.interface.common.collections.IMutableSequence`. - - Compared to that interface, which is implemented by :class:`list` - (:class:`~zope.interface.common.builtins.IList`), among others, - this interface is missing the following methods: - - - clear - - - count - - - index - - This interface adds the following methods: - - - sort - """ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__init__.py deleted file mode 100644 index f70a530..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__init__.py +++ /dev/null @@ -1,146 +0,0 @@ -############################################################################## -# Copyright (c) 2020 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -############################################################################## - -import unittest - -from zope.interface.common import ABCInterface -from zope.interface.common import ABCInterfaceClass -from zope.interface.verify import verifyClass -from zope.interface.verify import verifyObject - - -def iter_abc_interfaces(predicate=lambda iface: True): - # Iterate ``(iface, classes)``, where ``iface`` is a descendent of - # the ABCInterfaceClass passing the *predicate* and ``classes`` is - # an iterable of classes registered to conform to that interface. - # - # Note that some builtin classes are registered for two distinct parts of - # the ABC/interface tree. For example, bytearray is both ByteString and - # MutableSequence. - seen = set() - stack = list( - ABCInterface.dependents - ) # subclasses, but also implementedBy objects - - while stack: - iface = stack.pop(0) - if iface in seen or not isinstance(iface, ABCInterfaceClass): - continue - seen.add(iface) - stack.extend(list(iface.dependents)) - if not predicate(iface): - continue - - registered = set(iface.getRegisteredConformers()) - registered -= set(iface._ABCInterfaceClass__ignored_classes) - if registered: - yield iface, registered - - -def add_abc_interface_tests(cls, module): - def predicate(iface): - return iface.__module__ == module - add_verify_tests(cls, iter_abc_interfaces(predicate)) - - -def add_verify_tests(cls, iface_classes_iter): - cls.maxDiff = None - for iface, registered_classes in iface_classes_iter: - for stdlib_class in registered_classes: - def test(self, stdlib_class=stdlib_class, iface=iface): - if ( - stdlib_class in self.UNVERIFIABLE or - stdlib_class.__name__ in self.UNVERIFIABLE - ): - self.skipTest("Unable to verify %s" % stdlib_class) - - self.assertTrue(self.verify(iface, stdlib_class)) - - suffix = "{}_{}_{}_{}".format( - stdlib_class.__module__.replace('.', '_'), - stdlib_class.__name__, - iface.__module__.replace('.', '_'), - iface.__name__ - ) - name = 'test_auto_' + suffix - test.__name__ = name - assert not hasattr(cls, name), (name, list(cls.__dict__)) - setattr(cls, name, test) - - def test_ro(self, stdlib_class=stdlib_class, iface=iface): - from zope.interface import Interface - from zope.interface import implementedBy - from zope.interface import ro - self.assertEqual( - tuple(ro.ro(iface, strict=True)), - iface.__sro__) - implements = implementedBy(stdlib_class) - sro = implements.__sro__ - self.assertIs(sro[-1], Interface) - - if stdlib_class not in self.UNVERIFIABLE_RO: - # Check that we got the strict C3 resolution order, unless - # we know we cannot. Note that 'Interface' is virtual base - # that doesn't necessarily appear at the same place in the - # calculated SRO as in the final SRO. - strict = stdlib_class not in self.NON_STRICT_RO - isro = ro.ro(implements, strict=strict) - isro.remove(Interface) - isro.append(Interface) - - self.assertEqual(tuple(isro), sro) - - name = 'test_auto_ro_' + suffix - test_ro.__name__ = name - assert not hasattr(cls, name) - setattr(cls, name, test_ro) - - -class VerifyClassMixin(unittest.TestCase): - verifier = staticmethod(verifyClass) - UNVERIFIABLE = () - NON_STRICT_RO = () - UNVERIFIABLE_RO = () - - def _adjust_object_before_verify(self, iface, x): - return x - - def verify(self, iface, klass, **kwargs): - return self.verifier(iface, - self._adjust_object_before_verify(iface, klass), - **kwargs) - - -class VerifyObjectMixin(VerifyClassMixin): - verifier = staticmethod(verifyObject) - CONSTRUCTORS = { - } - - def _adjust_object_before_verify(self, iface, x): - constructor = self.CONSTRUCTORS.get(x) - if not constructor: - constructor = self.CONSTRUCTORS.get(iface) - if not constructor: - constructor = self.CONSTRUCTORS.get(x.__name__) - if not constructor: - constructor = x - if constructor is unittest.SkipTest: - self.skipTest("Cannot create " + str(x)) - - try: - result = constructor() - except Exception as e: # pragma: no cover - raise TypeError( - f'Failed to create instance of {constructor}') from e - if hasattr(result, 'close'): - self.addCleanup(result.close) - return result diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 084c4a6..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/basemapping.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/basemapping.cpython-38.pyc deleted file mode 100644 index 5af577d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/basemapping.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/test_builtins.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/test_builtins.cpython-38.pyc deleted file mode 100644 index f864850..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/test_builtins.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/test_collections.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/test_collections.cpython-38.pyc deleted file mode 100644 index 00d8230..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/test_collections.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/test_idatetime.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/test_idatetime.cpython-38.pyc deleted file mode 100644 index 4cf8d76..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/test_idatetime.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/test_import_interfaces.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/test_import_interfaces.cpython-38.pyc deleted file mode 100644 index 4fcdd7d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/test_import_interfaces.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/test_io.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/test_io.cpython-38.pyc deleted file mode 100644 index 9d7cc7a..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/test_io.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/test_numbers.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/test_numbers.cpython-38.pyc deleted file mode 100644 index a284895..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/__pycache__/test_numbers.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/basemapping.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/basemapping.py deleted file mode 100644 index f58e32a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/basemapping.py +++ /dev/null @@ -1,115 +0,0 @@ -############################################################################## -# -# Copyright (c) 2001, 2002 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Base Mapping tests -""" -from operator import __getitem__ - - -def testIReadMapping(self, inst, state, absent): - for key in state: - self.assertEqual(inst[key], state[key]) - self.assertEqual(inst.get(key, None), state[key]) - self.assertIn(key, inst) - - for key in absent: - self.assertEqual(inst.get(key, None), None) - self.assertEqual(inst.get(key), None) - self.assertEqual(inst.get(key, self), self) - self.assertRaises(KeyError, __getitem__, inst, key) - - -def test_keys(self, inst, state): - # Return the keys of the mapping object - inst_keys = sorted(inst.keys()) - state_keys = sorted(state.keys()) - self.assertEqual(inst_keys, state_keys) - - -def test_iter(self, inst, state): - # Return the keys of the mapping object - inst_keys = sorted(inst) - state_keys = sorted(state.keys()) - self.assertEqual(inst_keys, state_keys) - - -def test_values(self, inst, state): - # Return the values of the mapping object - inst_values = sorted(inst.values()) - state_values = sorted(state.values()) - self.assertEqual(inst_values, state_values) - - -def test_items(self, inst, state): - # Return the items of the mapping object - inst_items = sorted(inst.items()) - state_items = sorted(state.items()) - self.assertEqual(inst_items, state_items) - - -def test___len__(self, inst, state): - # Return the number of items - self.assertEqual(len(inst), len(state)) - - -def testIEnumerableMapping(self, inst, state): - test_keys(self, inst, state) - test_items(self, inst, state) - test_values(self, inst, state) - test___len__(self, inst, state) - - -class BaseTestIReadMapping: - - def testIReadMapping(self): - inst = self._IReadMapping__sample() - state = self._IReadMapping__stateDict() - absent = self._IReadMapping__absentKeys() - testIReadMapping(self, inst, state, absent) - - -class BaseTestIEnumerableMapping(BaseTestIReadMapping): - # Mapping objects whose items can be enumerated - - def test_keys(self): - # Return the keys of the mapping object - inst = self._IEnumerableMapping__sample() - state = self._IEnumerableMapping__stateDict() - test_keys(self, inst, state) - - def test_values(self): - # Return the values of the mapping object - inst = self._IEnumerableMapping__sample() - state = self._IEnumerableMapping__stateDict() - test_values(self, inst, state) - - def test_items(self): - # Return the items of the mapping object - inst = self._IEnumerableMapping__sample() - state = self._IEnumerableMapping__stateDict() - test_items(self, inst, state) - - def test___len__(self): - # Return the number of items - inst = self._IEnumerableMapping__sample() - state = self._IEnumerableMapping__stateDict() - test___len__(self, inst, state) - - def _IReadMapping__stateDict(self): - return self._IEnumerableMapping__stateDict() - - def _IReadMapping__sample(self): - return self._IEnumerableMapping__sample() - - def _IReadMapping__absentKeys(self): - return self._IEnumerableMapping__absentKeys() diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/test_builtins.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/test_builtins.py deleted file mode 100644 index cf7019b..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/test_builtins.py +++ /dev/null @@ -1,43 +0,0 @@ -############################################################################## -# Copyright (c) 2020 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -############################################################################## - -import unittest - -from zope.interface.common import builtins - -from . import VerifyClassMixin -from . import VerifyObjectMixin -from . import add_verify_tests - - -class TestVerifyClass(VerifyClassMixin, - unittest.TestCase): - pass - - -add_verify_tests(TestVerifyClass, ( - (builtins.IList, (list,)), - (builtins.ITuple, (tuple,)), - (builtins.ITextString, (str,)), - (builtins.IByteString, (bytes,)), - (builtins.INativeString, (str,)), - (builtins.IBool, (bool,)), - (builtins.IDict, (dict,)), - (builtins.IFile, ()), -)) - - -class TestVerifyObject(VerifyObjectMixin, - TestVerifyClass): - CONSTRUCTORS = { - builtins.IFile: lambda: open(__file__) - } diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/test_collections.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/test_collections.py deleted file mode 100644 index df7dec9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/test_collections.py +++ /dev/null @@ -1,147 +0,0 @@ -############################################################################## -# Copyright (c) 2020 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -############################################################################## - - -import array -import sys -import unittest -from collections import OrderedDict -from collections import abc -from collections import deque -from types import MappingProxyType - -from zope.interface import Invalid -from zope.interface._compat import PYPY -# Note that importing z.i.c.collections does work on import. -from zope.interface.common import collections - -from . import VerifyClassMixin -from . import VerifyObjectMixin -from . import add_abc_interface_tests - - -class TestVerifyClass(VerifyClassMixin, unittest.TestCase): - - # Here we test some known builtin classes that are defined to implement - # various collection interfaces as a quick sanity test. - def test_frozenset(self): - self.assertIsInstance(frozenset(), abc.Set) - self.assertTrue(self.verify(collections.ISet, frozenset)) - - def test_list(self): - self.assertIsInstance(list(), abc.MutableSequence) - self.assertTrue(self.verify(collections.IMutableSequence, list)) - - # Here we test some derived classes. - def test_UserList(self): - self.assertTrue(self.verify(collections.IMutableSequence, - collections.UserList)) - - def test_UserDict(self): - self.assertTrue(self.verify(collections.IMutableMapping, - collections.UserDict)) - - def test_UserString(self): - self.assertTrue(self.verify(collections.ISequence, - collections.UserString)) - - # Now we go through the registry, which should have several things, mostly - # builtins, but if we've imported other libraries already, it could - # contain things from outside of there too. We aren't concerned about - # third-party code here, just standard library types. We start with a - # blacklist of things to exclude, but if that gets out of hand we can - # figure out a better whitelisting. - UNVERIFIABLE = { - # This is declared to be an ISequence, but is missing lots of methods, - # including some that aren't part of a language protocol, such as - # ``index`` and ``count``. - memoryview, - # 'pkg_resources._vendor.pyparsing.ParseResults' is registered as a - # MutableMapping but is missing methods like ``popitem`` and - # ``setdefault``. It's imported due to namespace packages. - 'ParseResults', - # sqlite3.Row claims ISequence but also misses ``index`` and - # ``count``. It's imported because...? Coverage imports it, but why - # do we have it without coverage? - 'Row', - # In Python 3.10 ``array.array`` appears as ``IMutableSequence`` but it - # does not provide a ``clear()`` method and it cannot be instantiated - # using ``array.array()``. - array.array, - } - - if PYPY: - UNVERIFIABLE.update({ - # collections.deque.pop() doesn't support the index= argument to - # MutableSequence.pop(). We can't verify this on CPython because - # we can't get the signature, but on PyPy we /can/ get the - # signature, and of course it doesn't match. - deque, - # Likewise for index - range, - }) - UNVERIFIABLE_RO = { - # ``array.array`` fails the ``test_auto_ro_*`` tests with and - # without strict RO but only on Windows (AppVeyor) on Python 3.10.0 - # (in older versions ``array.array`` does not appear as - # ``IMutableSequence``). - array.array, - } - - -add_abc_interface_tests(TestVerifyClass, collections.ISet.__module__) - - -def _get_FrameLocalsProxy(): - return type(sys._getframe().f_locals) - - -class TestVerifyObject(VerifyObjectMixin, - TestVerifyClass): - CONSTRUCTORS = { - collections.IValuesView: {}.values, - collections.IItemsView: {}.items, - collections.IKeysView: {}.keys, - memoryview: lambda: memoryview(b'abc'), - range: lambda: range(10), - MappingProxyType: lambda: MappingProxyType({}), - collections.UserString: lambda: collections.UserString('abc'), - type(iter(bytearray())): lambda: iter(bytearray()), - type(iter(b'abc')): lambda: iter(b'abc'), - 'coroutine': unittest.SkipTest, - type(iter({}.keys())): lambda: iter({}.keys()), - type(iter({}.items())): lambda: iter({}.items()), - type(iter({}.values())): lambda: iter({}.values()), - type(i for i in range(1)): lambda: (i for i in range(3)), - type(iter([])): lambda: iter([]), - type(reversed([])): lambda: reversed([]), - 'longrange_iterator': unittest.SkipTest, - 'range_iterator': lambda: iter(range(3)), - 'rangeiterator': lambda: iter(range(3)), - type(iter(set())): lambda: iter(set()), - type(iter('')): lambda: iter(''), - 'async_generator': unittest.SkipTest, - type(iter(tuple())): lambda: iter(tuple()), - } - if sys.version_info >= (3, 13): - def FrameLocalsProxy_constructor(): - return _get_FrameLocalsProxy()(sys._getframe()) - FrameLocalsProxy = _get_FrameLocalsProxy() - CONSTRUCTORS[FrameLocalsProxy] = FrameLocalsProxy_constructor - - UNVERIFIABLE_RO = { - # ``array.array`` fails the ``test_auto_ro_*`` tests with and - # without strict RO but only on Windows (AppVeyor) on Python 3.10.0 - # (in older versions ``array.array`` does not appear as - # ``IMutableSequence``). - array.array, - } diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/test_idatetime.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/test_idatetime.py deleted file mode 100644 index 9550daa..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/test_idatetime.py +++ /dev/null @@ -1,48 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Test for datetime interfaces -""" - -import unittest -from datetime import date -from datetime import datetime -from datetime import time -from datetime import timedelta -from datetime import tzinfo - -from zope.interface.common.idatetime import IDate -from zope.interface.common.idatetime import IDateClass -from zope.interface.common.idatetime import IDateTime -from zope.interface.common.idatetime import IDateTimeClass -from zope.interface.common.idatetime import ITime -from zope.interface.common.idatetime import ITimeClass -from zope.interface.common.idatetime import ITimeDelta -from zope.interface.common.idatetime import ITimeDeltaClass -from zope.interface.common.idatetime import ITZInfo -from zope.interface.verify import verifyClass -from zope.interface.verify import verifyObject - - -class TestDateTimeInterfaces(unittest.TestCase): - - def test_interfaces(self): - verifyObject(ITimeDelta, timedelta(minutes=20)) - verifyObject(IDate, date(2000, 1, 2)) - verifyObject(IDateTime, datetime(2000, 1, 2, 10, 20)) - verifyObject(ITime, time(20, 30, 15, 1234)) - verifyObject(ITZInfo, tzinfo()) - verifyClass(ITimeDeltaClass, timedelta) - verifyClass(IDateClass, date) - verifyClass(IDateTimeClass, datetime) - verifyClass(ITimeClass, time) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/test_import_interfaces.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/test_import_interfaces.py deleted file mode 100644 index 67d1448..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/test_import_interfaces.py +++ /dev/null @@ -1,21 +0,0 @@ -############################################################################## -# -# Copyright (c) 2006 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -import unittest - - -class TestInterfaceImport(unittest.TestCase): - - def test_import(self): - import zope.interface.common.interfaces as x - self.assertIsNotNone(x) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/test_io.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/test_io.py deleted file mode 100644 index 72684bf..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/test_io.py +++ /dev/null @@ -1,46 +0,0 @@ -############################################################################## -# Copyright (c) 2020 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -############################################################################## - - -import io as abc -import unittest - -# Note that importing z.i.c.io does work on import. -from zope.interface.common import io - -from . import VerifyClassMixin -from . import VerifyObjectMixin -from . import add_abc_interface_tests - - -class TestVerifyClass(VerifyClassMixin, - unittest.TestCase): - pass - - -add_abc_interface_tests(TestVerifyClass, io.IIOBase.__module__) - - -class TestVerifyObject(VerifyObjectMixin, - TestVerifyClass): - CONSTRUCTORS = { - abc.BufferedWriter: lambda: abc.BufferedWriter(abc.StringIO()), - abc.BufferedReader: lambda: abc.BufferedReader(abc.StringIO()), - abc.TextIOWrapper: lambda: abc.TextIOWrapper(abc.BytesIO()), - abc.BufferedRandom: lambda: abc.BufferedRandom(abc.BytesIO()), - abc.BufferedRWPair: lambda: abc.BufferedRWPair( - abc.BytesIO(), abc.BytesIO() - ), - abc.FileIO: lambda: abc.FileIO(__file__), - '_WindowsConsoleIO': unittest.SkipTest, - 'WinConsoleIO': unittest.SkipTest, # breaks on PyPy-3.10 - } diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/test_numbers.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/test_numbers.py deleted file mode 100644 index 4e86514..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/common/tests/test_numbers.py +++ /dev/null @@ -1,42 +0,0 @@ -############################################################################## -# Copyright (c) 2020 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -############################################################################## - - -import numbers as abc -import unittest - -# Note that importing z.i.c.numbers does work on import. -from zope.interface.common import numbers - -from . import VerifyClassMixin -from . import VerifyObjectMixin -from . import add_abc_interface_tests - - -class TestVerifyClass(VerifyClassMixin, - unittest.TestCase): - - def test_int(self): - self.assertIsInstance(int(), abc.Integral) - self.assertTrue(self.verify(numbers.IIntegral, int)) - - def test_float(self): - self.assertIsInstance(float(), abc.Real) - self.assertTrue(self.verify(numbers.IReal, float)) - - -add_abc_interface_tests(TestVerifyClass, numbers.INumber.__module__) - - -class TestVerifyObject(VerifyObjectMixin, - TestVerifyClass): - pass diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/declarations.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/declarations.py deleted file mode 100644 index 825c578..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/declarations.py +++ /dev/null @@ -1,1219 +0,0 @@ -############################################################################## -# Copyright (c) 2003 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -############################################################################## -"""Implementation of interface declarations - -There are three flavors of declarations: - - - Declarations are used to simply name declared interfaces. - - - ImplementsDeclarations are used to express the interfaces that a - class implements (that instances of the class provides). - - Implements specifications support inheriting interfaces. - - - ProvidesDeclarations are used to express interfaces directly - provided by objects. - -""" -__docformat__ = 'restructuredtext' - -import sys -import weakref -from types import FunctionType -from types import MethodType -from types import ModuleType - -from zope.interface._compat import _use_c_impl -from zope.interface.interface import Interface -from zope.interface.interface import InterfaceBase -from zope.interface.interface import InterfaceClass -from zope.interface.interface import NameAndModuleComparisonMixin -from zope.interface.interface import Specification -from zope.interface.interface import SpecificationBase - - -__all__ = [ - # None. The public APIs of this module are - # re-exported from zope.interface directly. -] - -# pylint:disable=too-many-lines - -# Registry of class-implementation specifications -BuiltinImplementationSpecifications = {} - - -def _next_super_class(ob): - # When ``ob`` is an instance of ``super``, return - # the next class in the MRO that we should actually be - # looking at. Watch out for diamond inheritance! - self_class = ob.__self_class__ - class_that_invoked_super = ob.__thisclass__ - complete_mro = self_class.__mro__ - next_class = complete_mro[complete_mro.index(class_that_invoked_super) + 1] - return next_class - - -class named: - - def __init__(self, name): - self.name = name - - def __call__(self, ob): - ob.__component_name__ = self.name - return ob - - -class Declaration(Specification): - """Interface declarations""" - - __slots__ = () - - def __init__(self, *bases): - Specification.__init__(self, _normalizeargs(bases)) - - def __contains__(self, interface): - """Test whether an interface is in the specification - """ - - return self.extends(interface) and interface in self.interfaces() - - def __iter__(self): - """Return an iterator for the interfaces in the specification - """ - return self.interfaces() - - def flattened(self): - """Return an iterator of all included and extended interfaces - """ - return iter(self.__iro__) - - def __sub__(self, other): - """Remove interfaces from a specification - """ - return Declaration(*[ - i for i in self.interfaces() - if not [ - j - for j in other.interfaces() - if i.extends(j, 0) # non-strict extends - ] - ]) - - def __add__(self, other): - """ - Add two specifications or a specification and an interface - and produce a new declaration. - - .. versionchanged:: 5.4.0 - Now tries to preserve a consistent resolution order. Interfaces - being added to this object are added to the front of the resulting - resolution order if they already extend an interface in this - object. Previously, they were always added to the end of the order, - which easily resulted in invalid orders. - """ - before = [] - result = list(self.interfaces()) - seen = set(result) - for i in other.interfaces(): - if i in seen: - continue - seen.add(i) - if any(i.extends(x) for x in result): - # It already extends us, e.g., is a subclass, - # so it needs to go at the front of the RO. - before.append(i) - else: - result.append(i) - return Declaration(*(before + result)) - - # XXX: Is __radd__ needed? No tests break if it's removed. - # If it is needed, does it need to handle the C3 ordering differently? - # I (JAM) don't *think* it does. - __radd__ = __add__ - - @staticmethod - def _add_interfaces_to_cls(interfaces, cls): - # Strip redundant interfaces already provided - # by the cls so we don't produce invalid - # resolution orders. - implemented_by_cls = implementedBy(cls) - interfaces = tuple([ - iface - for iface in interfaces - if not implemented_by_cls.isOrExtends(iface) - ]) - return interfaces + (implemented_by_cls,) - - @staticmethod - def _argument_names_for_repr(interfaces): - # These don't actually have to be interfaces, they could be other - # Specification objects like Implements. Also, the first - # one is typically/nominally the cls. - ordered_names = [] - names = set() - for iface in interfaces: - duplicate_transform = repr - if isinstance(iface, InterfaceClass): - # Special case to get 'foo.bar.IFace' - # instead of '' - this_name = iface.__name__ - duplicate_transform = str - elif isinstance(iface, type): - # Likewise for types. (Ignoring legacy old-style - # classes.) - this_name = iface.__name__ - duplicate_transform = _implements_name - elif ( - isinstance(iface, Implements) and - not iface.declared and - iface.inherit in interfaces - ): - # If nothing is declared, there's no need to even print this; - # it would just show as ``classImplements(Class)``, and the - # ``Class`` has typically already. - continue - else: - this_name = repr(iface) - - already_seen = this_name in names - names.add(this_name) - if already_seen: - this_name = duplicate_transform(iface) - - ordered_names.append(this_name) - return ', '.join(ordered_names) - - -class _ImmutableDeclaration(Declaration): - # A Declaration that is immutable. Used as a singleton to - # return empty answers for things like ``implementedBy``. - # We have to define the actual singleton after normalizeargs - # is defined, and that in turn is defined after InterfaceClass and - # Implements. - - __slots__ = () - - __instance = None - - def __new__(cls): - if _ImmutableDeclaration.__instance is None: - _ImmutableDeclaration.__instance = object.__new__(cls) - return _ImmutableDeclaration.__instance - - def __reduce__(self): - return "_empty" - - @property - def __bases__(self): - return () - - @__bases__.setter - def __bases__(self, new_bases): - # We expect the superclass constructor to set ``self.__bases__ = ()``. - # Rather than attempt to special case that in the constructor and - # allow setting __bases__ only at that time, it's easier to just allow - # setting the empty tuple at any time. That makes ``x.__bases__ = - # x.__bases__`` a nice no-op too. (Skipping the superclass constructor - # altogether is a recipe for maintenance headaches.) - if new_bases != (): - raise TypeError( - "Cannot set non-empty bases on shared empty Declaration." - ) - - # As the immutable empty declaration, we cannot be changed. - # This means there's no logical reason for us to have dependents - # or subscriptions: we'll never notify them. So there's no need for - # us to keep track of any of that. - @property - def dependents(self): - return {} - - changed = subscribe = unsubscribe = lambda self, _ignored: None - - def interfaces(self): - # An empty iterator - return iter(()) - - def extends(self, interface, strict=True): - return interface is self._ROOT - - def get(self, name, default=None): - return default - - def weakref(self, callback=None): - # We're a singleton, we never go away. So there's no need to return - # distinct weakref objects here; their callbacks will never be called. - # Instead, we only need to return a callable that returns ourself. The - # easiest one is to return _ImmutableDeclaration itself; testing on - # Python 3.8 shows that's faster than a function that returns _empty. - # (Remember, one goal is to avoid allocating any object, and that - # includes a method.) - return _ImmutableDeclaration - - @property - def _v_attrs(self): - # _v_attrs is not a public, documented property, but some client code - # uses it anyway as a convenient place to cache things. To keep the - # empty declaration truly immutable, we must ignore that. That - # includes ignoring assignments as well. - return {} - - @_v_attrs.setter - def _v_attrs(self, new_attrs): - pass - - -############################################################################## -# -# Implementation specifications -# -# These specify interfaces implemented by instances of classes - -class Implements(NameAndModuleComparisonMixin, - Declaration): - # Inherit from NameAndModuleComparisonMixin to be mutually comparable with - # InterfaceClass objects. (The two must be mutually comparable to be able - # to work in e.g., BTrees.) Instances of this class generally don't have a - # __module__ other than `zope.interface.declarations`, whereas they *do* - # have a __name__ that is the fully qualified name of the object they are - # representing. - - # Note, though, that equality and hashing are still identity based. This - # accounts for things like nested objects that have the same name - # (typically only in tests) and is consistent with pickling. As far as - # comparisons to InterfaceClass goes, we'll never have equal name and - # module to those, so we're still consistent there. Instances of this - # class are essentially intended to be unique and are heavily cached (note - # how our __reduce__ handles this) so having identity based hash and eq - # should also work. - - # We want equality and hashing to be based on identity. However, we can't - # actually implement __eq__/__ne__ to do this because sometimes we get - # wrapped in a proxy. We need to let the proxy types implement these - # methods so they can handle unwrapping and then rely on: (1) the - # interpreter automatically changing `implements == proxy` into `proxy == - # implements` (which will call proxy.__eq__ to do the unwrapping) and then - # (2) the default equality and hashing semantics being identity based. - - # class whose specification should be used as additional base - inherit = None - - # interfaces actually declared for a class - declared = () - - # Weak cache of {class: } for super objects. - # Created on demand. These are rare, as of 5.0 anyway. Using a class - # level default doesn't take space in instances. Using _v_attrs would be - # another place to store this without taking space unless needed. - _super_cache = None - - __name__ = '?' - - @classmethod - def named(cls, name, *bases): - # Implementation method: Produce an Implements interface with a fully - # fleshed out __name__ before calling the constructor, which sets - # bases to the given interfaces and which may pass this object to - # other objects (e.g., to adjust dependents). If they're sorting or - # comparing by name, this needs to be set. - inst = cls.__new__(cls) - inst.__name__ = name - inst.__init__(*bases) - return inst - - def changed(self, originally_changed): - try: - del self._super_cache - except AttributeError: - pass - return super().changed(originally_changed) - - def __repr__(self): - if self.inherit: - name = ( - getattr(self.inherit, '__name__', None) or - _implements_name(self.inherit) - ) - else: - name = self.__name__ - declared_names = self._argument_names_for_repr(self.declared) - if declared_names: - declared_names = ', ' + declared_names - return f'classImplements({name}{declared_names})' - - def __reduce__(self): - return implementedBy, (self.inherit, ) - - -def _implements_name(ob): - # Return the __name__ attribute to be used by its __implemented__ - # property. - # This must be stable for the "same" object across processes - # because it is used for sorting. It needn't be unique, though, in cases - # like nested classes named Foo created by different functions, because - # equality and hashing is still based on identity. - # It might be nice to use __qualname__ on Python 3, but that would produce - # different values between Py2 and Py3. - - # Special-case 'InterfaceBase': its '__module__' member descriptor - # behaves differently across Python 3.x versions. - if ob is InterfaceBase: - return 'zope.interface.interface.InterfaceBase' - - return (getattr(ob, '__module__', '?') or '?') + \ - '.' + (getattr(ob, '__name__', '?') or '?') - - -def _implementedBy_super(sup): - # TODO: This is now simple enough we could probably implement - # in C if needed. - - # If the class MRO is strictly linear, we could just - # follow the normal algorithm for the next class in the - # search order (e.g., just return - # ``implemented_by_next``). But when diamond inheritance - # or mixins + interface declarations are present, we have - # to consider the whole MRO and compute a new Implements - # that excludes the classes being skipped over but - # includes everything else. - implemented_by_self = implementedBy(sup.__self_class__) - cache = implemented_by_self._super_cache # pylint:disable=protected-access - if cache is None: - cache = implemented_by_self._super_cache = weakref.WeakKeyDictionary() - - key = sup.__thisclass__ - try: - return cache[key] - except KeyError: - pass - - next_cls = _next_super_class(sup) - # For ``implementedBy(cls)``: - # .__bases__ is .declared + [implementedBy(b) for b in cls.__bases__] - # .inherit is cls - - implemented_by_next = implementedBy(next_cls) - mro = sup.__self_class__.__mro__ - ix_next_cls = mro.index(next_cls) - classes_to_keep = mro[ix_next_cls:] - new_bases = [implementedBy(c) for c in classes_to_keep] - - new = Implements.named( - implemented_by_self.__name__ + ':' + implemented_by_next.__name__, - *new_bases - ) - new.inherit = implemented_by_next.inherit - new.declared = implemented_by_next.declared - # I don't *think* that new needs to subscribe to ``implemented_by_self``; - # it auto-subscribed to its bases, and that should be good enough. - cache[key] = new - - return new - - -@_use_c_impl -def implementedBy( - cls -): # pylint:disable=too-many-return-statements,too-many-branches - """Return the interfaces implemented for a class' instances - - The value returned is an `~zope.interface.interfaces.IDeclaration`. - """ - try: - if isinstance(cls, super): - # Yes, this needs to be inside the try: block. Some objects - # like security proxies even break isinstance. - return _implementedBy_super(cls) - - spec = cls.__dict__.get('__implemented__') - except AttributeError: - - # we can't get the class dict. This is probably due to a - # security proxy. If this is the case, then probably no - # descriptor was installed for the class. - - # We don't want to depend directly on zope.security in - # zope.interface, but we'll try to make reasonable - # accommodations in an indirect way. - - # We'll check to see if there's an implements: - - spec = getattr(cls, '__implemented__', None) - if spec is None: - # There's no spec stred in the class. Maybe its a builtin: - spec = BuiltinImplementationSpecifications.get(cls) - if spec is not None: - return spec - return _empty - - if spec.__class__ == Implements: - # we defaulted to _empty or there was a spec. Good enough. - # Return it. - return spec - - # TODO: need old style __implements__ compatibility? - # Hm, there's an __implemented__, but it's not a spec. Must be - # an old-style declaration. Just compute a spec for it - return Declaration(*_normalizeargs((spec, ))) - - if isinstance(spec, Implements): - return spec - - if spec is None: - spec = BuiltinImplementationSpecifications.get(cls) - if spec is not None: - return spec - - # TODO: need old style __implements__ compatibility? - spec_name = _implements_name(cls) - if spec is not None: - # old-style __implemented__ = foo declaration - spec = (spec, ) # tuplefy, as it might be just an int - spec = Implements.named(spec_name, *_normalizeargs(spec)) - spec.inherit = None # old-style implies no inherit - del cls.__implemented__ # get rid of the old-style declaration - else: - try: - bases = cls.__bases__ - except AttributeError: - if not callable(cls): - raise TypeError("ImplementedBy called for non-factory", cls) - bases = () - - spec = Implements.named(spec_name, *[implementedBy(c) for c in bases]) - spec.inherit = cls - - try: - cls.__implemented__ = spec - if not hasattr(cls, '__providedBy__'): - cls.__providedBy__ = objectSpecificationDescriptor - - if isinstance(cls, type) and '__provides__' not in cls.__dict__: - # Make sure we get a __provides__ descriptor - cls.__provides__ = ClassProvides( - cls, getattr(cls, '__class__', type(cls)), - ) - - except TypeError: - if not isinstance(cls, type): - raise TypeError("ImplementedBy called for non-type", cls) - BuiltinImplementationSpecifications[cls] = spec - - return spec - - -def classImplementsOnly(cls, *interfaces): - """ - Declare the only interfaces implemented by instances of a class - - The arguments after the class are one or more interfaces or interface - specifications (`~zope.interface.interfaces.IDeclaration` objects). - - The interfaces given (including the interfaces in the specifications) - replace any previous declarations, *including* inherited definitions. If - you wish to preserve inherited declarations, you can pass - ``implementedBy(cls)`` in *interfaces*. This can be used to alter the - interface resolution order. - """ - spec = implementedBy(cls) - # Clear out everything inherited. It's important to - # also clear the bases right now so that we don't improperly discard - # interfaces that are already implemented by *old* bases that we're - # about to get rid of. - spec.declared = () - spec.inherit = None - spec.__bases__ = () - _classImplements_ordered(spec, interfaces, ()) - - -def classImplements(cls, *interfaces): - """ - Declare additional interfaces implemented for instances of a class - - The arguments after the class are one or more interfaces or interface - specifications (`~zope.interface.interfaces.IDeclaration` objects). - - The interfaces given (including the interfaces in the specifications) - are added to any interfaces previously declared. An effort is made to - keep a consistent C3 resolution order, but this cannot be guaranteed. - - .. versionchanged:: 5.0.0 - Each individual interface in *interfaces* may be added to either the - beginning or end of the list of interfaces declared for *cls*, - based on inheritance, in order to try to maintain a consistent - resolution order. Previously, all interfaces were added to the end. - .. versionchanged:: 5.1.0 - If *cls* is already declared to implement an interface (or derived - interface) in *interfaces* through inheritance, the interface is - ignored. Previously, it would redundantly be made direct base of *cls*, - which often produced inconsistent interface resolution orders. Now, the - order will be consistent, but may change. Also, if the ``__bases__`` - of the *cls* are later changed, the *cls* will no longer be considered - to implement such an interface (changing the ``__bases__`` of *cls* has - never been supported). - """ - spec = implementedBy(cls) - interfaces = tuple(_normalizeargs(interfaces)) - - before = [] - after = [] - - # Take steps to try to avoid producing an invalid resolution - # order, while still allowing for BWC (in the past, we always - # appended) - for iface in interfaces: - for b in spec.declared: - if iface.extends(b): - before.append(iface) - break - else: - after.append(iface) - _classImplements_ordered(spec, tuple(before), tuple(after)) - - -def classImplementsFirst(cls, iface): - """ - Declare that instances of *cls* additionally provide *iface*. - - The second argument is an interface or interface specification. - It is added as the highest priority (first in the IRO) interface; - no attempt is made to keep a consistent resolution order. - - .. versionadded:: 5.0.0 - """ - spec = implementedBy(cls) - _classImplements_ordered(spec, (iface,), ()) - - -def _classImplements_ordered(spec, before=(), after=()): - # Elide everything already inherited. - # Except, if it is the root, and we don't already declare anything else - # that would imply it, allow the root through. (TODO: When we disallow - # non-strict IRO, this part of the check can be removed because it's not - # possible to re-declare like that.) - before = [ - x - for x in before - if not spec.isOrExtends(x) or (x is Interface and not spec.declared) - ] - after = [ - x - for x in after - if not spec.isOrExtends(x) or (x is Interface and not spec.declared) - ] - - # eliminate duplicates - new_declared = [] - seen = set() - for lst in before, spec.declared, after: - for b in lst: - if b not in seen: - new_declared.append(b) - seen.add(b) - - spec.declared = tuple(new_declared) - - # compute the bases - bases = new_declared # guaranteed no dupes - - if spec.inherit is not None: - for c in spec.inherit.__bases__: - b = implementedBy(c) - if b not in seen: - seen.add(b) - bases.append(b) - - spec.__bases__ = tuple(bases) - - -def _implements_advice(cls): - interfaces, do_classImplements = cls.__dict__['__implements_advice_data__'] - del cls.__implements_advice_data__ - do_classImplements(cls, *interfaces) - return cls - - -class implementer: - """ - Declare the interfaces implemented by instances of a class. - - This function is called as a class decorator. - - The arguments are one or more interfaces or interface specifications - (`~zope.interface.interfaces.IDeclaration` objects). - - The interfaces given (including the interfaces in the specifications) are - added to any interfaces previously declared, unless the interface is - already implemented. - - Previous declarations include declarations for base classes unless - implementsOnly was used. - - This function is provided for convenience. It provides a more convenient - way to call `classImplements`. For example:: - - @implementer(I1) - class C(object): - pass - - is equivalent to calling:: - - classImplements(C, I1) - - after the class has been created. - - .. seealso:: `classImplements` - The change history provided there applies to this function too. - """ - __slots__ = ('interfaces',) - - def __init__(self, *interfaces): - self.interfaces = interfaces - - def __call__(self, ob): - if isinstance(ob, type): - # This is the common branch for classes. - classImplements(ob, *self.interfaces) - return ob - - spec_name = _implements_name(ob) - spec = Implements.named(spec_name, *self.interfaces) - try: - ob.__implemented__ = spec - except AttributeError: - raise TypeError("Can't declare implements", ob) - return ob - - -class implementer_only: - """Declare the only interfaces implemented by instances of a class - - This function is called as a class decorator. - - The arguments are one or more interfaces or interface - specifications (`~zope.interface.interfaces.IDeclaration` objects). - - Previous declarations including declarations for base classes - are overridden. - - This function is provided for convenience. It provides a more - convenient way to call `classImplementsOnly`. For example:: - - @implementer_only(I1) - class C(object): pass - - is equivalent to calling:: - - classImplementsOnly(I1) - - after the class has been created. - """ - - def __init__(self, *interfaces): - self.interfaces = interfaces - - def __call__(self, ob): - if isinstance(ob, (FunctionType, MethodType)): - # XXX Does this decorator make sense for anything but classes? - # I don't think so. There can be no inheritance of interfaces - # on a method or function.... - raise ValueError('The implementer_only decorator is not ' - 'supported for methods or functions.') - - # Assume it's a class: - classImplementsOnly(ob, *self.interfaces) - return ob - - -############################################################################## -# -# Instance declarations - -class Provides(Declaration): # Really named ProvidesClass - """Implement ``__provides__``, the instance-specific specification - - When an object is pickled, we pickle the interfaces that it implements. - """ - - def __init__(self, cls, *interfaces): - self.__args = (cls, ) + interfaces - self._cls = cls - Declaration.__init__( - self, *self._add_interfaces_to_cls(interfaces, cls) - ) - - # Added to by ``moduleProvides``, et al - _v_module_names = () - - def __repr__(self): - # The typical way to create instances of this object is via calling - # ``directlyProvides(...)`` or ``alsoProvides()``, but that's not the - # only way. Proxies, for example, directly use the ``Provides(...)`` - # function (which is the more generic method, and what we pickle as). - # We're after the most readable, useful repr in the common case, so we - # use the most common name. - # - # We also cooperate with ``moduleProvides`` to attempt to do the right - # thing for that API. See it for details. - function_name = 'directlyProvides' - if self._cls is ModuleType and self._v_module_names: - # See notes in ``moduleProvides``/``directlyProvides`` - providing_on_module = True - interfaces = self.__args[1:] - else: - providing_on_module = False - interfaces = (self._cls,) + self.__bases__ - ordered_names = self._argument_names_for_repr(interfaces) - if providing_on_module: - mod_names = self._v_module_names - if len(mod_names) == 1: - mod_names = "sys.modules[%r]" % mod_names[0] - ordered_names = ( - f'{mod_names}, ' - ) + ordered_names - return "{}({})".format( - function_name, - ordered_names, - ) - - def __reduce__(self): - # This reduces to the Provides *function*, not - # this class. - return Provides, self.__args - - __module__ = 'zope.interface' - - def __get__(self, inst, cls): - """Make sure that a class __provides__ doesn't leak to an instance - """ - if inst is None and cls is self._cls: - # We were accessed through a class, so we are the class' - # provides spec. Just return this object, but only if we are - # being called on the same class that we were defined for: - return self - - raise AttributeError('__provides__') - - -ProvidesClass = Provides - - -# Registry of instance declarations -# This is a memory optimization to allow objects to share specifications. -InstanceDeclarations = weakref.WeakValueDictionary() - - -def Provides(*interfaces): # pylint:disable=function-redefined - """Declaration for an instance of *cls*. - - The correct signature is ``cls, *interfaces``. - The *cls* is necessary to avoid the - construction of inconsistent resolution orders. - - Instance declarations are shared among instances that have the same - declaration. The declarations are cached in a weak value dictionary. - """ - spec = InstanceDeclarations.get(interfaces) - if spec is None: - spec = ProvidesClass(*interfaces) - InstanceDeclarations[interfaces] = spec - - return spec - - -Provides.__safe_for_unpickling__ = True - - -def directlyProvides(object, *interfaces): # pylint:disable=redefined-builtin - """Declare interfaces declared directly for an object - - The arguments after the object are one or more interfaces or interface - specifications (`~zope.interface.interfaces.IDeclaration` objects). - - The interfaces given (including the interfaces in the specifications) - replace interfaces previously declared for the object. - """ - cls = getattr(object, '__class__', None) - if cls is not None and getattr(cls, '__class__', None) is cls: - # It's a meta class (well, at least it it could be an extension class) - # Note that we can't get here from the tests: there is no normal - # class which isn't descriptor aware. - if not isinstance(object, type): - raise TypeError("Attempt to make an interface declaration on a " - "non-descriptor-aware class") - - interfaces = _normalizeargs(interfaces) - if cls is None: - cls = type(object) - - if issubclass(cls, type): - # we have a class or type. We'll use a special descriptor - # that provides some extra caching - object.__provides__ = ClassProvides(object, cls, *interfaces) - else: - provides = object.__provides__ = Provides(cls, *interfaces) - # See notes in ``moduleProvides``. - if issubclass(cls, ModuleType) and hasattr(object, '__name__'): - provides._v_module_names += (object.__name__,) - - -def alsoProvides(object, *interfaces): # pylint:disable=redefined-builtin - """Declare interfaces declared directly for an object - - The arguments after the object are one or more interfaces or interface - specifications (`~zope.interface.interfaces.IDeclaration` objects). - - The interfaces given (including the interfaces in the specifications) are - added to the interfaces previously declared for the object. - """ - directlyProvides(object, directlyProvidedBy(object), *interfaces) - - -def noLongerProvides(object, interface): # pylint:disable=redefined-builtin - """ Removes a directly provided interface from an object. - """ - directlyProvides(object, directlyProvidedBy(object) - interface) - if interface.providedBy(object): - raise ValueError("Can only remove directly provided interfaces.") - - -@_use_c_impl -class ClassProvidesBase(SpecificationBase): - - __slots__ = ( - '_cls', - '_implements', - ) - - def __get__(self, inst, cls): - # member slots are set by subclass - # pylint:disable=no-member - if cls is self._cls: - # We only work if called on the class we were defined for - - if inst is None: - # We were accessed through a class, so we are the class' - # provides spec. Just return this object as is: - return self - - return self._implements - - raise AttributeError('__provides__') - - -class ClassProvides(Declaration, ClassProvidesBase): - """Special descriptor for class ``__provides__`` - - The descriptor caches the implementedBy info, so that - we can get declarations for objects without instance-specific - interfaces a bit quicker. - """ - - __slots__ = ( - '__args', - ) - - def __init__(self, cls, metacls, *interfaces): - self._cls = cls - self._implements = implementedBy(cls) - self.__args = (cls, metacls, ) + interfaces - Declaration.__init__( - self, *self._add_interfaces_to_cls(interfaces, metacls) - ) - - def __repr__(self): - # There are two common ways to get instances of this object: The most - # interesting way is calling ``@provider(..)`` as a decorator of a - # class; this is the same as calling ``directlyProvides(cls, ...)``. - # - # The other way is by default: anything that invokes - # ``implementedBy(x)`` will wind up putting an instance in - # ``type(x).__provides__``; this includes the ``@implementer(...)`` - # decorator. Those instances won't have any interfaces. - # - # Thus, as our repr, we go with the ``directlyProvides()`` syntax. - interfaces = (self._cls, ) + self.__args[2:] - ordered_names = self._argument_names_for_repr(interfaces) - return f"directlyProvides({ordered_names})" - - def __reduce__(self): - return self.__class__, self.__args - - # Copy base-class method for speed - __get__ = ClassProvidesBase.__get__ - - -# autopep8: off (it breaks the statements in the "if") -def directlyProvidedBy(object): # pylint:disable=redefined-builtin - """Return the interfaces directly provided by the given object - - The value returned is an `~zope.interface.interfaces.IDeclaration`. - """ - provides = getattr(object, "__provides__", None) - if ( - provides is None # no spec - # We might have gotten the implements spec, as an - # optimization. If so, it's like having only one base, that we - # lop off to exclude class-supplied declarations: - or isinstance(provides, Implements) # noqa W503 - ): - return _empty - - # Strip off the class part of the spec: - return Declaration(provides.__bases__[:-1]) -# autopep8: on - - -class provider: - """Declare interfaces provided directly by a class - - This function is called in a class definition. - - The arguments are one or more interfaces or interface specifications - (`~zope.interface.interfaces.IDeclaration` objects). - - The given interfaces (including the interfaces in the specifications) - are used to create the class's direct-object interface specification. - An error will be raised if the module class has an direct interface - specification. In other words, it is an error to call this function more - than once in a class definition. - - Note that the given interfaces have nothing to do with the interfaces - implemented by instances of the class. - - This function is provided for convenience. It provides a more convenient - way to call `directlyProvides` for a class. For example:: - - @provider(I1) - class C: - pass - - is equivalent to calling:: - - directlyProvides(C, I1) - - after the class has been created. - """ - - def __init__(self, *interfaces): - self.interfaces = interfaces - - def __call__(self, ob): - directlyProvides(ob, *self.interfaces) - return ob - - -def moduleProvides(*interfaces): - """Declare interfaces provided by a module - - This function is used in a module definition. - - The arguments are one or more interfaces or interface specifications - (`~zope.interface.interfaces.IDeclaration` objects). - - The given interfaces (including the interfaces in the specifications) are - used to create the module's direct-object interface specification. An - error will be raised if the module already has an interface specification. - In other words, it is an error to call this function more than once in a - module definition. - - This function is provided for convenience. It provides a more convenient - way to call directlyProvides. For example:: - - moduleProvides(I1) - - is equivalent to:: - - directlyProvides(sys.modules[__name__], I1) - """ - frame = sys._getframe(1) # pylint:disable=protected-access - locals = frame.f_locals # pylint:disable=redefined-builtin - - # Try to make sure we were called from a module body - if (locals is not frame.f_globals) or ('__name__' not in locals): - raise TypeError( - "moduleProvides can only be used from a module definition.") - - if '__provides__' in locals: - raise TypeError( - "moduleProvides can only be used once in a module definition.") - - # Note: This is cached based on the key ``(ModuleType, *interfaces)``; One - # consequence is that any module that provides the same interfaces gets - # the same ``__repr__``, meaning that you can't tell what module such a - # declaration came from. Adding the module name to ``_v_module_names`` - # attempts to correct for this; it works in some common situations, but - # fails (1) after pickling (the data is lost) and (2) if declarations are - # actually shared and (3) if the alternate spelling of - # ``directlyProvides()`` is used. Problem (3) is fixed by cooperating - # with ``directlyProvides`` to maintain this information, and problem (2) - # is worked around by printing all the names, but (1) is unsolvable - # without introducing new classes or changing the stored data...but it - # doesn't actually matter, because ``ModuleType`` can't be pickled! - p = locals["__provides__"] = Provides(ModuleType, - *_normalizeargs(interfaces)) - p._v_module_names += (locals['__name__'],) - - -############################################################################## -# -# Declaration querying support - -# XXX: is this a fossil? Nobody calls it, no unit tests exercise it, no -# doctests import it, and the package __init__ doesn't import it. -# (Answer: Versions of zope.container prior to 4.4.0 called this, -# and zope.proxy.decorator up through at least 4.3.5 called this.) -def ObjectSpecification(direct, cls): - """Provide object specifications - - These combine information for the object and for it's classes. - """ - return Provides(cls, direct) # pragma: no cover fossil - - -@_use_c_impl -def getObjectSpecification(ob): - try: - provides = ob.__provides__ - except AttributeError: - provides = None - - if provides is not None: - if isinstance(provides, SpecificationBase): - return provides - - try: - cls = ob.__class__ - except AttributeError: - # We can't get the class, so just consider provides - return _empty - return implementedBy(cls) - - -@_use_c_impl -def providedBy(ob): - """ - Return the interfaces provided by *ob*. - - If *ob* is a :class:`super` object, then only interfaces implemented - by the remainder of the classes in the method resolution order are - considered. Interfaces directly provided by the object underlying *ob* - are not. - """ - # Here we have either a special object, an old-style declaration - # or a descriptor - - # Try to get __providedBy__ - try: - if isinstance(ob, super): # Some objects raise errors on isinstance() - return implementedBy(ob) - - r = ob.__providedBy__ - except AttributeError: - # Not set yet. Fall back to lower-level thing that computes it - return getObjectSpecification(ob) - - try: - # We might have gotten a descriptor from an instance of a - # class (like an ExtensionClass) that doesn't support - # descriptors. We'll make sure we got one by trying to get - # the only attribute, which all specs have. - r.extends - except AttributeError: - - # The object's class doesn't understand descriptors. - # Sigh. We need to get an object descriptor, but we have to be - # careful. We want to use the instance's __provides__, if - # there is one, but only if it didn't come from the class. - - try: - r = ob.__provides__ - except AttributeError: - # No __provides__, so just fall back to implementedBy - return implementedBy(ob.__class__) - - # We need to make sure we got the __provides__ from the - # instance. We'll do this by making sure we don't get the same - # thing from the class: - - try: - cp = ob.__class__.__provides__ - except AttributeError: - # The ob doesn't have a class or the class has no - # provides, assume we're done: - return r - - if r is cp: - # Oops, we got the provides from the class. This means - # the object doesn't have it's own. We should use implementedBy - return implementedBy(ob.__class__) - - return r - - -@_use_c_impl -class ObjectSpecificationDescriptor: - """Implement the ``__providedBy__`` attribute - - The ``__providedBy__`` attribute computes the interfaces provided by an - object. If an object has an ``__provides__`` attribute, that is returned. - Otherwise, `implementedBy` the *cls* is returned. - - .. versionchanged:: 5.4.0 - Both the default (C) implementation and the Python implementation - now let exceptions raised by accessing ``__provides__`` propagate. - Previously, the C version ignored all exceptions. - .. versionchanged:: 5.4.0 - The Python implementation now matches the C implementation and lets - a ``__provides__`` of ``None`` override what the class is declared to - implement. - """ - - def __get__(self, inst, cls): - """Get an object specification for an object - """ - if inst is None: - return getObjectSpecification(cls) - - try: - return inst.__provides__ - except AttributeError: - return implementedBy(cls) - - -############################################################################## - -def _normalizeargs(sequence, output=None): - """Normalize declaration arguments - - Normalization arguments might contain Declarions, tuples, or single - interfaces. - - Anything but individual interfaces or implements specs will be expanded. - """ - if output is None: - output = [] - - cls = sequence.__class__ - if InterfaceClass in cls.__mro__ or Implements in cls.__mro__: - output.append(sequence) - else: - for v in sequence: - _normalizeargs(v, output) - - return output - - -_empty = _ImmutableDeclaration() - -objectSpecificationDescriptor = ObjectSpecificationDescriptor() diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/document.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/document.py deleted file mode 100644 index 3725037..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/document.py +++ /dev/null @@ -1,133 +0,0 @@ -############################################################################## -# -# Copyright (c) 2001, 2002 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -""" Pretty-Print an Interface object as structured text (Yum) - -This module provides a function, asStructuredText, for rendering an -interface as structured text. -""" -import zope.interface - - -__all__ = [ - 'asReStructuredText', - 'asStructuredText', -] - - -def asStructuredText(iface, munge=0, rst=False): - """ Output structured text format. Note, this will whack any existing - 'structured' format of the text. - - If `rst=True`, then the output will quote all code as inline literals in - accordance with 'reStructuredText' markup principles. - """ - - if rst: - def inline_literal(s): - return f"``{s}``" - else: - def inline_literal(s): - return s - - r = [inline_literal(iface.getName())] - outp = r.append - level = 1 - - if iface.getDoc(): - outp(_justify_and_indent(_trim_doc_string(iface.getDoc()), level)) - - bases = [base - for base in iface.__bases__ - if base is not zope.interface.Interface - ] - if bases: - outp(_justify_and_indent("This interface extends:", level, munge)) - level += 1 - for b in bases: - item = "o %s" % inline_literal(b.getName()) - outp(_justify_and_indent(_trim_doc_string(item), level, munge)) - level -= 1 - - namesAndDescriptions = sorted(iface.namesAndDescriptions()) - - outp(_justify_and_indent("Attributes:", level, munge)) - level += 1 - for name, desc in namesAndDescriptions: - if not hasattr(desc, 'getSignatureString'): # ugh... - item = "{} -- {}".format( - inline_literal(desc.getName()), - desc.getDoc() or 'no documentation' - ) - outp(_justify_and_indent(_trim_doc_string(item), level, munge)) - level -= 1 - - outp(_justify_and_indent("Methods:", level, munge)) - level += 1 - for name, desc in namesAndDescriptions: - if hasattr(desc, 'getSignatureString'): # ugh... - _call = f"{desc.getName()}{desc.getSignatureString()}" - item = "{} -- {}".format( - inline_literal(_call), - desc.getDoc() or 'no documentation' - ) - outp(_justify_and_indent(_trim_doc_string(item), level, munge)) - - return "\n\n".join(r) + "\n\n" - - -def asReStructuredText(iface, munge=0): - """ Output reStructuredText format. - - Note, this will whack any existing 'structured' format of the text.""" - return asStructuredText(iface, munge=munge, rst=True) - - -def _trim_doc_string(text): - """ Trims a doc string to make it format - correctly with structured text. """ - - lines = text.replace('\r\n', '\n').split('\n') - nlines = [lines.pop(0)] - if lines: - min_indent = min([len(line) - len(line.lstrip()) - for line in lines]) - for line in lines: - nlines.append(line[min_indent:]) - - return '\n'.join(nlines) - - -def _justify_and_indent(text, level, munge=0, width=72): - """ indent and justify text, rejustify (munge) if specified """ - - indent = " " * level - - if munge: - lines = [] - line = indent - text = text.split() - - for word in text: - line = ' '.join([line, word]) - if len(line) > width: - lines.append(line) - line = indent - else: - lines.append(line) - - return '\n'.join(lines) - - else: - return indent + \ - text.strip().replace("\r\n", "\n") .replace("\n", "\n" + indent) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/exceptions.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/exceptions.py deleted file mode 100644 index b86fb1e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/exceptions.py +++ /dev/null @@ -1,278 +0,0 @@ -############################################################################## -# -# Copyright (c) 2002 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Interface-specific exceptions -""" - -__all__ = [ - # Invalid tree - 'Invalid', - 'DoesNotImplement', - 'BrokenImplementation', - 'BrokenMethodImplementation', - 'MultipleInvalid', - # Other - 'BadImplements', - 'InvalidInterface', -] - - -class Invalid(Exception): - """A specification is violated - """ - - -class _TargetInvalid(Invalid): - # Internal use. Subclass this when you're describing - # a particular target object that's invalid according - # to a specific interface. - # - # For backwards compatibility, the *target* and *interface* are - # optional, and the signatures are inconsistent in their ordering. - # - # We deal with the inconsistency in ordering by defining the index - # of the two values in ``self.args``. *target* uses a marker object to - # distinguish "not given" from "given, but None", because the latter - # can be a value that gets passed to validation. For this reason, it must - # always be the last argument (we detect absence by the ``IndexError``). - - _IX_INTERFACE = 0 - _IX_TARGET = 1 - # The exception to catch when indexing self.args indicating that - # an argument was not given. If all arguments are expected, - # a subclass should set this to (). - _NOT_GIVEN_CATCH = IndexError - _NOT_GIVEN = '' - - def _get_arg_or_default(self, ix, default=None): - try: - return self.args[ix] # pylint:disable=unsubscriptable-object - except self._NOT_GIVEN_CATCH: - return default - - @property - def interface(self): - return self._get_arg_or_default(self._IX_INTERFACE) - - @property - def target(self): - return self._get_arg_or_default(self._IX_TARGET, self._NOT_GIVEN) - - ### - # str - # - # The ``__str__`` of self is implemented by concatenating (%s), in order, - # these properties (none of which should have leading or trailing - # whitespace): - # - # - self._str_subject - # Begin the message, including a description of the target. - # - self._str_description - # Provide a general description of the type of error, including - # the interface name if possible and relevant. - # - self._str_conjunction - # Join the description to the details. Defaults to ": ". - # - self._str_details - # Provide details about how this particular instance of the error. - # - self._str_trailer - # End the message. Usually just a period. - ### - - @property - def _str_subject(self): - target = self.target - if target is self._NOT_GIVEN: - return "An object" - return f"The object {target!r}" - - @property - def _str_description(self): - return "has failed to implement interface %s" % ( - self.interface or '' - ) - - _str_conjunction = ": " - _str_details = "" - _str_trailer = '.' - - def __str__(self): - return "{} {}{}{}{}".format( - self._str_subject, - self._str_description, - self._str_conjunction, - self._str_details, - self._str_trailer - ) - - -class DoesNotImplement(_TargetInvalid): - """ - DoesNotImplement(interface[, target]) - - The *target* (optional) does not implement the *interface*. - - .. versionchanged:: 5.0.0 - Add the *target* argument and attribute, and change the resulting - string value of this object accordingly. - """ - - _str_details = "Does not declaratively implement the interface" - - -class BrokenImplementation(_TargetInvalid): - """ - BrokenImplementation(interface, name[, target]) - - The *target* (optional) is missing the attribute *name*. - - .. versionchanged:: 5.0.0 - Add the *target* argument and attribute, and change the resulting - string value of this object accordingly. - - The *name* can either be a simple string or a ``Attribute`` object. - """ - - _IX_NAME = _TargetInvalid._IX_INTERFACE + 1 - _IX_TARGET = _IX_NAME + 1 - - @property - def name(self): - return self.args[1] # pylint:disable=unsubscriptable-object - - @property - def _str_details(self): - return "The %s attribute was not provided" % ( - repr(self.name) if isinstance(self.name, str) else self.name - ) - - -class BrokenMethodImplementation(_TargetInvalid): - """ - BrokenMethodImplementation( - method, message[, implementation, interface, target] - ) - - The *target* (optional) has a *method* in *implementation* that violates - its contract in a way described by *mess*. - - .. versionchanged:: 5.0.0 - Add the *interface* and *target* argument and attribute, - and change the resulting string value of this object accordingly. - - The *method* can either be a simple string or a ``Method`` object. - - .. versionchanged:: 5.0.0 - If *implementation* is given, then the *message* will have the - string "implementation" replaced with an short but informative - representation of *implementation*. - - """ - - _IX_IMPL = 2 - _IX_INTERFACE = _IX_IMPL + 1 - _IX_TARGET = _IX_INTERFACE + 1 - - @property - def method(self): - return self.args[0] # pylint:disable=unsubscriptable-object - - @property - def mess(self): - return self.args[1] # pylint:disable=unsubscriptable-object - - @staticmethod - def __implementation_str(impl): - # It could be a callable or some arbitrary object, we don't - # know yet. - import inspect # Inspect is a heavy-weight dependency, lots of imports - try: - sig = inspect.signature - formatsig = str - except AttributeError: - sig = inspect.getargspec - formatsig = inspect.formatargspec - - try: - sig = sig(impl) - except (ValueError, TypeError): - # Unable to introspect. Darn. - # This could be a non-callable, or a particular builtin, - # or a bound method that doesn't even accept 'self', e.g., - # ``Class.method = lambda: None; Class().method`` - return repr(impl) - - try: - name = impl.__qualname__ - except AttributeError: - name = impl.__name__ - - return name + formatsig(sig) - - @property - def _str_details(self): - impl = self._get_arg_or_default(self._IX_IMPL, self._NOT_GIVEN) - message = self.mess - if impl is not self._NOT_GIVEN and 'implementation' in message: - message = message.replace("implementation", '%r') - message = message % (self.__implementation_str(impl),) - - return 'The contract of {} is violated because {}'.format( - repr(self.method) if isinstance(self.method, str) else self.method, - message, - ) - - -class MultipleInvalid(_TargetInvalid): - """ - The *target* has failed to implement the *interface* in - multiple ways. - - The failures are described by *exceptions*, a collection of - other `Invalid` instances. - - .. versionadded:: 5.0 - """ - - _NOT_GIVEN_CATCH = () - - def __init__(self, interface, target, exceptions): - super().__init__(interface, target, tuple(exceptions)) - - @property - def exceptions(self): - return self.args[2] # pylint:disable=unsubscriptable-object - - @property - def _str_details(self): - # It would be nice to use tabs here, but that - # is hard to represent in doctests. - return '\n ' + '\n '.join( - x._str_details.strip() if isinstance(x, _TargetInvalid) else str(x) - for x in self.exceptions - ) - - _str_conjunction = ':' # no trailing space, messes up doctests - _str_trailer = '' - - -class InvalidInterface(Exception): - """The interface has invalid contents - """ - - -class BadImplements(TypeError): - """An implementation assertion is invalid - - because it doesn't contain an interface or a sequence of valid - implementation assertions. - """ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/interface.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/interface.py deleted file mode 100644 index e5dddb8..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/interface.py +++ /dev/null @@ -1,1183 +0,0 @@ -############################################################################## -# -# Copyright (c) 2001, 2002 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Interface object implementation -""" -# pylint:disable=protected-access -import sys -import weakref -from types import FunctionType -from types import MethodType -from typing import Union - -from zope.interface import ro -from zope.interface._compat import _use_c_impl -from zope.interface.exceptions import Invalid -from zope.interface.ro import ro as calculate_ro - - -__all__ = [ - # Most of the public API from this module is directly exported - # from zope.interface. The only remaining public API intended to - # be imported from here should be those few things documented as - # such. - 'InterfaceClass', - 'Specification', - 'adapter_hooks', -] - -CO_VARARGS = 4 -CO_VARKEYWORDS = 8 -# Put in the attrs dict of an interface by ``taggedValue`` and ``invariants`` -TAGGED_DATA = '__interface_tagged_values__' -# Put in the attrs dict of an interface by ``interfacemethod`` -INTERFACE_METHODS = '__interface_methods__' - -_decorator_non_return = object() -_marker = object() - - -def invariant(call): - f_locals = sys._getframe(1).f_locals - tags = f_locals.setdefault(TAGGED_DATA, {}) - invariants = tags.setdefault('invariants', []) - invariants.append(call) - return _decorator_non_return - - -def taggedValue(key, value): - """Attaches a tagged value to an interface at definition time.""" - f_locals = sys._getframe(1).f_locals - tagged_values = f_locals.setdefault(TAGGED_DATA, {}) - tagged_values[key] = value - return _decorator_non_return - - -class Element: - """ - Default implementation of `zope.interface.interfaces.IElement`. - """ - - # We can't say this yet because we don't have enough - # infrastructure in place. - # - # implements(IElement) - - def __init__( - self, __name__, __doc__='', - ): # pylint:disable=redefined-builtin - if not __doc__ and __name__.find(' ') >= 0: - __doc__ = __name__ - __name__ = None - - self.__name__ = __name__ - self.__doc__ = __doc__ - # Tagged values are rare, especially on methods or attributes. - # Deferring the allocation can save substantial memory. - self.__tagged_values = None - - def getName(self): - """ Returns the name of the object. """ - return self.__name__ - - def getDoc(self): - """ Returns the documentation for the object. """ - return self.__doc__ - - ### - # Tagged values. - # - # Direct tagged values are set only in this instance. Others - # may be inherited (for those subclasses that have that concept). - ### - - def getTaggedValue(self, tag): - """ Returns the value associated with 'tag'. """ - if not self.__tagged_values: - raise KeyError(tag) - return self.__tagged_values[tag] - - def queryTaggedValue(self, tag, default=None): - """ Returns the value associated with 'tag'. """ - return self.__tagged_values.get( - tag, default, - ) if self.__tagged_values else default - - def getTaggedValueTags(self): - """ Returns a collection of all tags. """ - return self.__tagged_values.keys() if self.__tagged_values else () - - def setTaggedValue(self, tag, value): - """ Associates 'value' with 'key'. """ - if self.__tagged_values is None: - self.__tagged_values = {} - self.__tagged_values[tag] = value - - queryDirectTaggedValue = queryTaggedValue - getDirectTaggedValue = getTaggedValue - getDirectTaggedValueTags = getTaggedValueTags - - -SpecificationBasePy = object # filled by _use_c_impl. - - -@_use_c_impl -class SpecificationBase: - # This object is the base of the inheritance hierarchy for ClassProvides: - # - # ClassProvides < ClassProvidesBase, Declaration - # Declaration < Specification < SpecificationBase - # ClassProvidesBase < SpecificationBase - # - # In order to have compatible instance layouts, we need to declare - # the storage used by Specification and Declaration here (and - # those classes must have ``__slots__ = ()``); fortunately this is - # not a waste of space because those are the only two inheritance - # trees. These all translate into tp_members in C. - __slots__ = ( - # Things used here. - '_implied', - # Things used in Specification. - '_dependents', - '_bases', - '_v_attrs', - '__iro__', - '__sro__', - '__weakref__', - ) - - def providedBy(self, ob): - """Is the interface implemented by an object - """ - spec = providedBy(ob) - return self in spec._implied - - def implementedBy(self, cls): - """Test whether the specification is implemented by a class or factory. - - Raise TypeError if argument is neither a class nor a callable. - """ - spec = implementedBy(cls) - return self in spec._implied - - def isOrExtends(self, interface): - """Is the interface the same as or extend the given interface - """ - return interface in self._implied # pylint:disable=no-member - - __call__ = isOrExtends - - -class NameAndModuleComparisonMixin: - # Internal use. Implement the basic sorting operators (but not (in)equality - # or hashing). Subclasses must provide ``__name__`` and ``__module__`` - # attributes. Subclasses will be mutually comparable; but because equality - # and hashing semantics are missing from this class, take care in how - # you define those two attributes: If you stick with the default equality - # and hashing (identity based) you should make sure that all possible - # ``__name__`` and ``__module__`` pairs are unique ACROSS ALL SUBCLASSES. - # (Actually, pretty much the same thing goes if you define equality and - # hashing to be based on those two attributes: they must still be - # consistent ACROSS ALL SUBCLASSES.) - - # pylint:disable=assigning-non-slot - __slots__ = () - - def _compare(self, other): - """ - Compare *self* to *other* based on ``__name__`` and ``__module__``. - - Return 0 if they are equal, return 1 if *self* is - greater than *other*, and return -1 if *self* is less than - *other*. - - If *other* does not have ``__name__`` or ``__module__``, then - return ``NotImplemented``. - - .. caution:: - This allows comparison to things well outside the type hierarchy, - perhaps not symmetrically. - - For example, ``class Foo(object)`` and ``class Foo(Interface)`` - in the same file would compare equal, depending on the order of - operands. Writing code like this by hand would be unusual, but it - could happen with dynamic creation of types and interfaces. - - None is treated as a pseudo interface that implies the loosest - contact possible, no contract. For that reason, all interfaces - sort before None. - """ - if other is self: - return 0 - - if other is None: - return -1 - - n1 = (self.__name__, self.__module__) - try: - n2 = (other.__name__, other.__module__) - except AttributeError: - return NotImplemented - - # This spelling works under Python3, which doesn't have cmp(). - return (n1 > n2) - (n1 < n2) - - def __lt__(self, other): - c = self._compare(other) - if c is NotImplemented: - return c - return c < 0 - - def __le__(self, other): - c = self._compare(other) - if c is NotImplemented: - return c - return c <= 0 - - def __gt__(self, other): - c = self._compare(other) - if c is NotImplemented: - return c - return c > 0 - - def __ge__(self, other): - c = self._compare(other) - if c is NotImplemented: - return c - return c >= 0 - - -@_use_c_impl -class InterfaceBase(NameAndModuleComparisonMixin, SpecificationBasePy): - """Base class that wants to be replaced with a C base :) - """ - - __slots__ = ( - '__name__', - '__ibmodule__', - '_v_cached_hash', - ) - - def __init__(self, name=None, module=None): - self.__name__ = name - self.__ibmodule__ = module - - def _call_conform(self, conform): - raise NotImplementedError - - @property - def __module_property__(self): - # This is for _InterfaceMetaClass - return self.__ibmodule__ - - def __call__(self, obj, alternate=_marker): - """Adapt an object to the interface - """ - try: - conform = obj.__conform__ - except AttributeError: - conform = None - - if conform is not None: - adapter = self._call_conform(conform) - if adapter is not None: - return adapter - - adapter = self.__adapt__(obj) - - if adapter is not None: - return adapter - if alternate is not _marker: - return alternate - raise TypeError("Could not adapt", obj, self) - - def __adapt__(self, obj): - """Adapt an object to the receiver - """ - if self.providedBy(obj): - return obj - - for hook in adapter_hooks: - adapter = hook(self, obj) - if adapter is not None: - return adapter - - return None - - def __hash__(self): - # pylint:disable=assigning-non-slot,attribute-defined-outside-init - try: - return self._v_cached_hash - except AttributeError: - self._v_cached_hash = hash((self.__name__, self.__module__)) - return self._v_cached_hash - - def __eq__(self, other): - c = self._compare(other) - if c is NotImplemented: - return c - return c == 0 - - def __ne__(self, other): - if other is self: - return False - - c = self._compare(other) - if c is NotImplemented: - return c - return c != 0 - - -adapter_hooks = _use_c_impl([], 'adapter_hooks') - - -class Specification(SpecificationBase): - """Specifications - - An interface specification is used to track interface declarations - and component registrations. - - This class is a base class for both interfaces themselves and for - interface specifications (declarations). - - Specifications are mutable. If you reassign their bases, their - relations with other specifications are adjusted accordingly. - """ - __slots__ = () - - # The root of all Specifications. This will be assigned `Interface`, - # once it is defined. - _ROOT = None - - # Copy some base class methods for speed - isOrExtends = SpecificationBase.isOrExtends - providedBy = SpecificationBase.providedBy - - def __init__(self, bases=()): - # There are many leaf interfaces with no dependents, - # and a few with very many. It's a heavily left-skewed - # distribution. In a survey of Plone and Zope related packages - # that loaded 2245 InterfaceClass objects and 2235 ClassProvides - # instances, there were a total of 7000 Specification objects created. - # 4700 had 0 dependents, 1400 had 1, 382 had 2 and so on. Only one - # for had 1664. So there's savings to be had deferring - # the creation of dependents. - self._dependents = None # type: weakref.WeakKeyDictionary - self._bases = () - self._implied = {} - self._v_attrs = None - self.__iro__ = () - self.__sro__ = () - - self.__bases__ = tuple(bases) - - @property - def dependents(self): - if self._dependents is None: - self._dependents = weakref.WeakKeyDictionary() - return self._dependents - - def subscribe(self, dependent): - self._dependents[dependent] = self.dependents.get(dependent, 0) + 1 - - def unsubscribe(self, dependent): - try: - n = self._dependents[dependent] - except TypeError: - raise KeyError(dependent) - n -= 1 - if not n: - del self.dependents[dependent] - else: - assert n > 0 - self.dependents[dependent] = n - - def __setBases(self, bases): - # Remove ourselves as a dependent of our old bases - for b in self.__bases__: - b.unsubscribe(self) - - # Register ourselves as a dependent of our new bases - self._bases = bases - for b in bases: - b.subscribe(self) - - self.changed(self) - - __bases__ = property( - lambda self: self._bases, __setBases, - ) - - # This method exists for tests to override the way we call - # ro.calculate_ro(), usually by adding extra kwargs. We don't - # want to have a mutable dictionary as a class member that we pass - # ourself because mutability is bad, and passing **kw is slower than - # calling the bound function. - _do_calculate_ro = calculate_ro - - def _calculate_sro(self): - """Compute resolution order for this object using its ``__bases__``. - - Ensures that ``Interface`` is always the last (lowest priority) - element. - """ - # We'd like to make Interface the lowest priority as a property of the - # resolution order algorithm. That almost works out naturally, but it - # fails when class inheritance has some bases that DO implement an - # interface, and some that DO NOT. In such a mixed scenario, you wind - # up with a set of bases to consider that look like this: [[..., - # Interface], [..., object], ...]. Depending on the order of - # inheritance, Interface can wind up before or after object, and that - # can happen at any point in the tree, meaning Interface can wind up - # somewhere in the middle of the order. Since Interface is treated as - # something that everything winds up implementing anyway (a catch-all - # for things like adapters), having it high up the order is bad. It's - # also bad to have it at the end, just before some concrete class: - # concrete classes should be HIGHER priority than interfaces (because - # there's only one class, but many implementations). - # - # One technically nice way to fix this would be to have - # ``implementedBy(object).__bases__ = (Interface,)`` - # - # But: (1) That fails for old-style classes and (2) that causes - # everything to appear to *explicitly* implement Interface, when up - # to this point it's been an implicit virtual sort of relationship. - # - # So we force the issue by mutating the resolution order. - - # Note that we let C3 use pre-computed __sro__ for our bases. - # This requires that by the time this method is invoked, our bases - # have settled their SROs. Thus, ``changed()`` must first - # update itself before telling its descendents of changes. - sro = self._do_calculate_ro(base_mros={ - b: b.__sro__ - for b in self.__bases__ - }) - root = self._ROOT - if root is not None and sro and sro[-1] is not root: - # In one dataset of 1823 Interface objects, 1117 ClassProvides - # objects, sro[-1] was root 4496 times, and only not root 118 - # times. So it's probably worth checking. - - # Once we don't have to deal with old-style classes, - # we can add a check and only do this if base_count > 1, - # if we tweak the bootstrapping for ```` - sro = [ - x - for x in sro - if x is not root - ] - sro.append(root) - - return sro - - def changed(self, originally_changed): - """ - We, or something we depend on, have changed. - - By the time this is called, the things we depend on, - such as our bases, should themselves be stable. - """ - self._v_attrs = None - - implied = self._implied - implied.clear() - - ancestors = self._calculate_sro() - self.__sro__ = tuple(ancestors) - self.__iro__ = tuple([ancestor for ancestor in ancestors - if isinstance(ancestor, InterfaceClass) - ]) - - for ancestor in ancestors: - # We directly imply our ancestors: - implied[ancestor] = () - - # Now, advise our dependents of change - # (being careful not to create the WeakKeyDictionary if not needed): - for dependent in tuple( - self._dependents.keys() if self._dependents else () - ): - dependent.changed(originally_changed) - - # Just in case something called get() at some point - # during that process and we have a cycle of some sort - # make sure we didn't cache incomplete results. - self._v_attrs = None - - def interfaces(self): - """Return an iterator for the interfaces in the specification. - """ - seen = {} - for base in self.__bases__: - for interface in base.interfaces(): - if interface not in seen: - seen[interface] = 1 - yield interface - - def extends(self, interface, strict=True): - """Does the specification extend the given interface? - - Test whether an interface in the specification extends the - given interface - """ - return ( - (interface in self._implied) and ( - (not strict) or (self != interface) - ) - ) - - def weakref(self, callback=None): - return weakref.ref(self, callback) - - def get(self, name, default=None): - """Query for an attribute description - """ - attrs = self._v_attrs - if attrs is None: - attrs = self._v_attrs = {} - attr = attrs.get(name) - if attr is None: - for iface in self.__iro__: - attr = iface.direct(name) - if attr is not None: - attrs[name] = attr - break - - return default if attr is None else attr - - -class _InterfaceMetaClass(type): - # Handling ``__module__`` on ``InterfaceClass`` is tricky. We need to be - # able to read it on a type and get the expected string. We also need to - # be able to set it on an instance and get the value we set. So far so - # good. But what gets tricky is that we'd like to store the value in the C - # structure (``InterfaceBase.__ibmodule__``) for direct access during - # equality, sorting, and hashing. "No problem, you think, I'll just use a - # property" (well, the C equivalents, ``PyMemberDef`` or ``PyGetSetDef``). - # - # Except there is a problem. When a subclass is created, the - # metaclass (``type``) always automatically puts the expected - # string in the class's dictionary under ``__module__``, thus - # overriding the property inherited from the superclass. Writing - # ``Subclass.__module__`` still works, but - # ``Subclass().__module__`` fails. - # - # There are multiple ways to work around this: - # - # (1) Define ``InterfaceBase.__getattribute__`` to watch for - # ``__module__`` and return the C storage. - # - # This works, but slows down *all* attribute access (except, - # ironically, to ``__module__``) by about 25% (40ns becomes 50ns) - # (when implemented in C). Since that includes methods like - # ``providedBy``, that's probably not acceptable. - # - # All the other methods involve modifying subclasses. This can be done - # either on the fly in some cases, as instances are constructed, or by - # using a metaclass. These next few can be done on the fly. - # - # (2) Make ``__module__`` a descriptor in each subclass dictionary. It - # can't be a straight up ``@property`` descriptor, though, because - # accessing it on the class returns a ``property`` object, not the desired - # string. - # - # (3) Implement a data descriptor (``__get__`` and ``__set__``) - # that is both a subclass of string, and also does the redirect of - # ``__module__`` to ``__ibmodule__`` and does the correct thing - # with the ``instance`` argument to ``__get__`` is None (returns - # the class's value.) (Why must it be a subclass of string? Because - # when it' s in the class's dict, it's defined on an *instance* of the - # metaclass; descriptors in an instance's dict aren't honored --- their - # ``__get__`` is never invoked --- so it must also *be* the value we want - # returned.) - # - # This works, preserves the ability to read and write - # ``__module__``, and eliminates any penalty accessing other - # attributes. But it slows down accessing ``__module__`` of - # instances by 200% (40ns to 124ns), requires editing class dicts on the - # fly (in InterfaceClass.__init__), thus slightly slowing down all - # interface creation, and is ugly. - # - # (4) As in the last step, but make it a non-data descriptor (no - # ``__set__``). - # - # If you then *also* store a copy of ``__ibmodule__`` in - # ``__module__`` in the instance's dict, reading works for both - # class and instance and is full speed for instances. But the cost - # is storage space, and you can't write to it anymore, not without - # things getting out of sync. - # - # (Actually, ``__module__`` was never meant to be writable. Doing - # so would break BTrees and normal dictionaries, as well as the - # repr, maybe more.) - # - # That leaves us with a metaclass. (Recall that a class is an - # instance of its metaclass, so properties/descriptors defined in - # the metaclass are used when accessing attributes on the - # instance/class. We'll use that to define ``__module__``.) Here - # we can have our cake and eat it too: no extra storage, and - # C-speed access to the underlying storage. The only substantial - # cost is that metaclasses tend to make people's heads hurt. (But - # still less than the descriptor-is-string, hopefully.) - - __slots__ = () - - def __new__(cls, name, bases, attrs): - # Figure out what module defined the interface. - # This is copied from ``InterfaceClass.__init__``; - # reviewers aren't sure how AttributeError or KeyError - # could be raised. - __module__ = sys._getframe(1).f_globals['__name__'] - # Get the C optimized __module__ accessor and give it - # to the new class. - moduledescr = InterfaceBase.__dict__['__module__'] - if isinstance(moduledescr, str): - # We're working with the Python implementation, - # not the C version - moduledescr = InterfaceBase.__dict__['__module_property__'] - attrs['__module__'] = moduledescr - kind = type.__new__(cls, name, bases, attrs) - kind.__module = __module__ - return kind - - @property - def __module__(cls): - return cls.__module - - def __repr__(cls): - return "".format( - cls.__module, - cls.__name__, - ) - - -_InterfaceClassBase = _InterfaceMetaClass( - 'InterfaceClass', - # From least specific to most specific. - (InterfaceBase, Specification, Element), - {'__slots__': ()} -) - - -def interfacemethod(func): - """ - Convert a method specification to an actual method of the interface. - - This is a decorator that functions like `staticmethod` et al. - - The primary use of this decorator is to allow interface definitions to - define the ``__adapt__`` method, but other interface methods can be - overridden this way too. - - .. seealso:: - `zope.interface.interfaces.IInterfaceDeclaration.interfacemethod` - """ - f_locals = sys._getframe(1).f_locals - methods = f_locals.setdefault(INTERFACE_METHODS, {}) - methods[func.__name__] = func - return _decorator_non_return - - -class InterfaceClass(_InterfaceClassBase): - """ - Prototype (scarecrow) Interfaces Implementation. - - Note that it is not possible to change the ``__name__`` or ``__module__`` - after an instance of this object has been constructed. - """ - - # We can't say this yet because we don't have enough - # infrastructure in place. - # - # implements(IInterface) - - def __new__( - cls, - name=None, - bases=(), - attrs=None, - __doc__=None, # pylint:disable=redefined-builtin - __module__=None, - ): - assert isinstance(bases, tuple) - attrs = attrs or {} - needs_custom_class = attrs.pop(INTERFACE_METHODS, None) - if needs_custom_class: - needs_custom_class.update( - {'__classcell__': attrs.pop('__classcell__')} - if '__classcell__' in attrs - else {} - ) - if '__adapt__' in needs_custom_class: - # We need to tell the C code to call this. - needs_custom_class['_CALL_CUSTOM_ADAPT'] = 1 - - if issubclass(cls, _InterfaceClassWithCustomMethods): - cls_bases = (cls,) - elif cls is InterfaceClass: - cls_bases = (_InterfaceClassWithCustomMethods,) - else: - cls_bases = (cls, _InterfaceClassWithCustomMethods) - - cls = type(cls)( # pylint:disable=self-cls-assignment - name + "", - cls_bases, - needs_custom_class - ) - - return _InterfaceClassBase.__new__(cls) - - def __init__( - self, - name, - bases=(), - attrs=None, - __doc__=None, # pylint:disable=redefined-builtin - __module__=None, - ): - # We don't call our metaclass parent directly - # pylint:disable=non-parent-init-called - # pylint:disable=super-init-not-called - if not all(isinstance(base, InterfaceClass) for base in bases): - raise TypeError('Expected base interfaces') - - if attrs is None: - attrs = {} - - if __module__ is None: - __module__ = attrs.get('__module__') - if isinstance(__module__, str): - del attrs['__module__'] - else: - try: - # Figure out what module defined the interface. - # This is how cPython figures out the module of - # a class, but of course it does it in C. :-/ - __module__ = sys._getframe(1).f_globals['__name__'] - except (AttributeError, KeyError): # pragma: no cover - pass - - InterfaceBase.__init__(self, name, __module__) - # These asserts assisted debugging the metaclass - # assert '__module__' not in self.__dict__ - # assert self.__ibmodule__ is self.__module__ is __module__ - - d = attrs.get('__doc__') - if d is not None: - if not isinstance(d, Attribute): - if __doc__ is None: - __doc__ = d - del attrs['__doc__'] - - if __doc__ is None: - __doc__ = '' - - Element.__init__(self, name, __doc__) - - tagged_data = attrs.pop(TAGGED_DATA, None) - if tagged_data is not None: - for key, val in tagged_data.items(): - self.setTaggedValue(key, val) - - Specification.__init__(self, bases) - self.__attrs = self.__compute_attrs(attrs) - - self.__identifier__ = f"{__module__}.{name}" - - def __compute_attrs(self, attrs): - # Make sure that all recorded attributes (and methods) are of type - # `Attribute` and `Method` - def update_value(aname, aval): - if isinstance(aval, Attribute): - aval.interface = self - if not aval.__name__: - aval.__name__ = aname - elif isinstance(aval, FunctionType): - aval = fromFunction(aval, self, name=aname) - else: - raise InvalidInterface("Concrete attribute, " + aname) - return aval - - return { - aname: update_value(aname, aval) - for aname, aval in attrs.items() - if aname not in ( - # __locals__: Python 3 sometimes adds this. - '__locals__', - # __qualname__: PEP 3155 (Python 3.3+) - '__qualname__', - # __annotations__: PEP 3107 (Python 3.0+) - '__annotations__', - # __static_attributes__: Python 3.13a6+ - # https://github.com/python/cpython/pull/115913 - '__static_attributes__', - # __firstlineno__: Python 3.13b1+ - # https://github.com/python/cpython/pull/118475 - '__firstlineno__', - ) and - aval is not _decorator_non_return # noqa W503 - } - - def interfaces(self): - """Return an iterator for the interfaces in the specification. - """ - yield self - - def getBases(self): - return self.__bases__ - - def isEqualOrExtendedBy(self, other): - """Same interface or extends?""" - return self == other or other.extends(self) - - def names(self, all=False): # pylint:disable=redefined-builtin - """Return the attribute names defined by the interface.""" - if not all: - return self.__attrs.keys() - - r = self.__attrs.copy() - - for base in self.__bases__: - r.update(dict.fromkeys(base.names(all))) - - return r.keys() - - def __iter__(self): - return iter(self.names(all=True)) - - def namesAndDescriptions( - self, all=False # pylint:disable=redefined-builtin - ): - """Return attribute names and descriptions defined by interface.""" - if not all: - return self.__attrs.items() - - r = {} - for base in self.__bases__[::-1]: - r.update(dict(base.namesAndDescriptions(all))) - - r.update(self.__attrs) - - return r.items() - - def getDescriptionFor(self, name): - """Return the attribute description for the given name.""" - r = self.get(name) - if r is not None: - return r - - raise KeyError(name) - - __getitem__ = getDescriptionFor - - def __contains__(self, name): - return self.get(name) is not None - - def direct(self, name): - return self.__attrs.get(name) - - def queryDescriptionFor(self, name, default=None): - return self.get(name, default) - - def validateInvariants(self, obj, errors=None): - """validate object to defined invariants.""" - - for iface in self.__iro__: - for invariant in iface.queryDirectTaggedValue('invariants', ()): - try: - invariant(obj) - except Invalid as error: - if errors is not None: - errors.append(error) - else: - raise - - if errors: - raise Invalid(errors) - - def queryTaggedValue(self, tag, default=None): - """ - Queries for the value associated with *tag*, returning it from the - nearest interface in the ``__iro__``. - - If not found, returns *default*. - """ - for iface in self.__iro__: - value = iface.queryDirectTaggedValue(tag, _marker) - if value is not _marker: - return value - return default - - def getTaggedValue(self, tag): - """ Returns the value associated with 'tag'. """ - value = self.queryTaggedValue(tag, default=_marker) - if value is _marker: - raise KeyError(tag) - return value - - def getTaggedValueTags(self): - """ Returns a list of all tags. """ - keys = set() - for base in self.__iro__: - keys.update(base.getDirectTaggedValueTags()) - return keys - - def __repr__(self): - try: - return self._v_repr - except AttributeError: - name = str(self) - r = f"<{self.__class__.__name__} {name}>" - self._v_repr = r # pylint:disable=attribute-defined-outside-init - return r - - def __str__(self): - name = self.__name__ - m = self.__ibmodule__ - if m: - name = f'{m}.{name}' - return name - - def _call_conform(self, conform): - try: - return conform(self) - except TypeError: # pragma: no cover - # We got a TypeError. It might be an error raised by - # the __conform__ implementation, or *we* may have - # made the TypeError by calling an unbound method - # (object is a class). In the later case, we behave - # as though there is no __conform__ method. We can - # detect this case by checking whether there is more - # than one traceback object in the traceback chain: - if sys.exc_info()[2].tb_next is not None: - # There is more than one entry in the chain, so - # reraise the error: - raise - # This clever trick is from Phillip Eby - - return None # pragma: no cover - - def __reduce__(self): - return self.__name__ - - def __or__(self, other): - """Allow type hinting syntax: Interface | None.""" - return Union[self, other] - - def __ror__(self, other): - """Allow type hinting syntax: None | Interface.""" - return Union[other, self] - - -Interface = InterfaceClass("Interface", __module__='zope.interface') -# Interface is the only member of its own SRO. -Interface._calculate_sro = lambda: (Interface,) -Interface.changed(Interface) -assert Interface.__sro__ == (Interface,) -Specification._ROOT = Interface -ro._ROOT = Interface - - -class _InterfaceClassWithCustomMethods(InterfaceClass): - """ - Marker class for interfaces with custom methods that override - InterfaceClass methods. - """ - - -class Attribute(Element): - """Attribute descriptions - """ - - # We can't say this yet because we don't have enough - # infrastructure in place. - # - # implements(IAttribute) - - interface = None - - def _get_str_info(self): - """Return extra data to put at the end of __str__.""" - return "" - - def __str__(self): - of = '' - if self.interface is not None: - of = ( - self.interface.__module__ + - '.' + - self.interface.__name__ + - '.' - ) - # self.__name__ may be None during construction (e.g., debugging) - return of + (self.__name__ or '') + self._get_str_info() - - def __repr__(self): - return "<{}.{} object at 0x{:x} {}>".format( - type(self).__module__, - type(self).__name__, - id(self), - self - ) - - -class Method(Attribute): - """Method interfaces - - The idea here is that you have objects that describe methods. - This provides an opportunity for rich meta-data. - """ - - # We can't say this yet because we don't have enough - # infrastructure in place. - # - # implements(IMethod) - - positional = required = () - _optional = varargs = kwargs = None - - def _get_optional(self): - if self._optional is None: - return {} - return self._optional - - def _set_optional(self, opt): - self._optional = opt - - def _del_optional(self): - self._optional = None - - optional = property(_get_optional, _set_optional, _del_optional) - - def __call__(self, *args, **kw): - raise BrokenImplementation(self.interface, self.__name__) - - def getSignatureInfo(self): - return {'positional': self.positional, - 'required': self.required, - 'optional': self.optional, - 'varargs': self.varargs, - 'kwargs': self.kwargs, - } - - def getSignatureString(self): - sig = [] - for v in self.positional: - sig.append(v) - if v in self.optional.keys(): - sig[-1] += "=" + repr(self.optional[v]) - if self.varargs: - sig.append("*" + self.varargs) - if self.kwargs: - sig.append("**" + self.kwargs) - - return "(%s)" % ", ".join(sig) - - _get_str_info = getSignatureString - - -def fromFunction(func, interface=None, imlevel=0, name=None): - name = name or func.__name__ - method = Method(name, func.__doc__) - defaults = getattr(func, '__defaults__', None) or () - code = func.__code__ - # Number of positional arguments - na = code.co_argcount - imlevel - names = code.co_varnames[imlevel:] - opt = {} - # Number of required arguments - defaults_count = len(defaults) - if not defaults_count: - # PyPy3 uses ``__defaults_count__`` for builtin methods - # like ``dict.pop``. Surprisingly, these don't have recorded - # ``__defaults__`` - defaults_count = getattr(func, '__defaults_count__', 0) - - nr = na - defaults_count - if nr < 0: - defaults = defaults[-nr:] - nr = 0 - - # Determine the optional arguments. - opt.update(dict(zip(names[nr:], defaults))) - - method.positional = names[:na] - method.required = names[:nr] - method.optional = opt - - argno = na - - # Determine the function's variable argument's name (i.e. *args) - if code.co_flags & CO_VARARGS: - method.varargs = names[argno] - argno = argno + 1 - else: - method.varargs = None - - # Determine the function's keyword argument's name (i.e. **kw) - if code.co_flags & CO_VARKEYWORDS: - method.kwargs = names[argno] - else: - method.kwargs = None - - method.interface = interface - - for key, value in func.__dict__.items(): - method.setTaggedValue(key, value) - - return method - - -def fromMethod(meth, interface=None, name=None): - if isinstance(meth, MethodType): - func = meth.__func__ - else: - func = meth - return fromFunction(func, interface, imlevel=1, name=name) - - -# Now we can create the interesting interfaces and wire them up: -def _wire(): - from zope.interface.declarations import classImplements - # From lest specific to most specific. - from zope.interface.interfaces import IElement - classImplements(Element, IElement) - - from zope.interface.interfaces import IAttribute - classImplements(Attribute, IAttribute) - - from zope.interface.interfaces import IMethod - classImplements(Method, IMethod) - - from zope.interface.interfaces import ISpecification - classImplements(Specification, ISpecification) - - from zope.interface.interfaces import IInterface - classImplements(InterfaceClass, IInterface) - - -# We import this here to deal with module dependencies. -# pylint:disable=wrong-import-position -from zope.interface.declarations import implementedBy -from zope.interface.declarations import providedBy -from zope.interface.exceptions import BrokenImplementation -from zope.interface.exceptions import InvalidInterface - - -# This ensures that ``Interface`` winds up in the flattened() -# list of the immutable declaration. It correctly overrides changed() -# as a no-op, so we bypass that. -# pylint:disable=wrong-import-position -from zope.interface.declarations import _empty # isort: skip -Specification.changed(_empty, _empty) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/interfaces.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/interfaces.py deleted file mode 100644 index 9bafeb5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/interfaces.py +++ /dev/null @@ -1,1516 +0,0 @@ -############################################################################## -# -# Copyright (c) 2002 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Interface Package Interfaces -""" -__docformat__ = 'restructuredtext' - -from zope.interface.declarations import implementer -from zope.interface.interface import Attribute -from zope.interface.interface import Interface - - -__all__ = [ - 'ComponentLookupError', - 'IAdapterRegistration', - 'IAdapterRegistry', - 'IAttribute', - 'IComponentLookup', - 'IComponentRegistry', - 'IComponents', - 'IDeclaration', - 'IElement', - 'IHandlerRegistration', - 'IInterface', - 'IInterfaceDeclaration', - 'IMethod', - 'Invalid', - 'IObjectEvent', - 'IRegistered', - 'IRegistration', - 'IRegistrationEvent', - 'ISpecification', - 'ISubscriptionAdapterRegistration', - 'IUnregistered', - 'IUtilityRegistration', - 'ObjectEvent', - 'Registered', - 'Unregistered', -] - -# pylint:disable=inherit-non-class,no-method-argument,no-self-argument -# pylint:disable=unexpected-special-method-signature -# pylint:disable=too-many-lines - - -class IElement(Interface): - """ - Objects that have basic documentation and tagged values. - - Known derivatives include :class:`IAttribute` and its derivative - :class:`IMethod`; these have no notion of inheritance. - :class:`IInterface` is also a derivative, and it does have a - notion of inheritance, expressed through its ``__bases__`` and - ordered in its ``__iro__`` (both defined by - :class:`ISpecification`). - """ - - # pylint:disable=arguments-differ - - # Note that defining __doc__ as an Attribute hides the docstring - # from introspection. When changing it, also change it in the Sphinx - # ReST files. - - __name__ = Attribute('__name__', 'The object name') - __doc__ = Attribute('__doc__', 'The object doc string') - - ### - # Tagged values. - # - # Direct values are established in this instance. Others may be - # inherited. Although ``IElement`` itself doesn't have a notion of - # inheritance, ``IInterface`` *does*. It might have been better to - # make ``IInterface`` define new methods - # ``getIndirectTaggedValue``, etc, to include inheritance instead - # of overriding ``getTaggedValue`` to do that, but that ship has sailed. - # So to keep things nice and symmetric, we define the ``Direct`` methods - # here. - ### - - def getTaggedValue(tag): - """Returns the value associated with *tag*. - - Raise a `KeyError` if the tag isn't set. - - If the object has a notion of inheritance, this searches - through the inheritance hierarchy and returns the nearest result. - If there is no such notion, this looks only at this object. - - .. versionchanged:: 4.7.0 - This method should respect inheritance if present. - """ - - def queryTaggedValue(tag, default=None): - """ - As for `getTaggedValue`, but instead of raising a `KeyError`, returns - *default*. - - - .. versionchanged:: 4.7.0 - This method should respect inheritance if present. - """ - - def getTaggedValueTags(): - """ - Returns a collection of all tags in no particular order. - - If the object has a notion of inheritance, this - includes all the inherited tagged values. If there is - no such notion, this looks only at this object. - - .. versionchanged:: 4.7.0 - This method should respect inheritance if present. - """ - - def setTaggedValue(tag, value): - """ - Associates *value* with *key* directly in this object. - """ - - def getDirectTaggedValue(tag): - """ - As for `getTaggedValue`, but never includes inheritance. - - .. versionadded:: 5.0.0 - """ - - def queryDirectTaggedValue(tag, default=None): - """ - As for `queryTaggedValue`, but never includes inheritance. - - .. versionadded:: 5.0.0 - """ - - def getDirectTaggedValueTags(): - """ - As for `getTaggedValueTags`, but includes only tags directly - set on this object. - - .. versionadded:: 5.0.0 - """ - - -class IAttribute(IElement): - """Attribute descriptors""" - - interface = Attribute('interface', - 'Stores the interface instance in which the ' - 'attribute is located.') - - -class IMethod(IAttribute): - """Method attributes""" - - def getSignatureInfo(): - """Returns the signature information. - - This method returns a dictionary with the following string keys: - - - positional - A sequence of the names of positional arguments. - - required - A sequence of the names of required arguments. - - optional - A dictionary mapping argument names to their default values. - - varargs - The name of the varargs argument (or None). - - kwargs - The name of the kwargs argument (or None). - """ - - def getSignatureString(): - """Return a signature string suitable for inclusion in documentation. - - This method returns the function signature string. For example, if you - have ``def func(a, b, c=1, d='f')``, then the signature string is - ``"(a, b, c=1, d='f')"``. - """ - - -class ISpecification(Interface): - """Object Behavioral specifications""" - # pylint:disable=arguments-differ - def providedBy(object): # pylint:disable=redefined-builtin - """Test whether the interface is implemented by the object - - Return true of the object asserts that it implements the - interface, including asserting that it implements an extended - interface. - """ - - def implementedBy(class_): - """Test whether the interface is implemented by instances of the class - - Return true of the class asserts that its instances implement the - interface, including asserting that they implement an extended - interface. - """ - - def isOrExtends(other): - """Test whether the specification is or extends another - """ - - def extends(other, strict=True): - """Test whether a specification extends another - - The specification extends other if it has other as a base - interface or if one of it's bases extends other. - - If strict is false, then the specification extends itself. - """ - - def weakref(callback=None): - """Return a weakref to the specification - - This method is, regrettably, needed to allow weakrefs to be - computed to security-proxied specifications. While the - zope.interface package does not require zope.security or - zope.proxy, it has to be able to coexist with it. - - """ - - __bases__ = Attribute("""Base specifications - - A tuple of specifications from which this specification is - directly derived. - - """) - - __sro__ = Attribute("""Specification-resolution order - - A tuple of the specification and all of it's ancestor - specifications from most specific to least specific. The specification - itself is the first element. - - (This is similar to the method-resolution order for new-style classes.) - """) - - __iro__ = Attribute("""Interface-resolution order - - A tuple of the specification's ancestor interfaces from - most specific to least specific. The specification itself is - included if it is an interface. - - (This is similar to the method-resolution order for new-style classes.) - """) - - def get(name, default=None): - """Look up the description for a name - - If the named attribute is not defined, the default is - returned. - """ - - -class IInterface(ISpecification, IElement): - """Interface objects - - Interface objects describe the behavior of an object by containing - useful information about the object. This information includes: - - - Prose documentation about the object. In Python terms, this - is called the "doc string" of the interface. In this element, - you describe how the object works in prose language and any - other useful information about the object. - - - Descriptions of attributes. Attribute descriptions include - the name of the attribute and prose documentation describing - the attributes usage. - - - Descriptions of methods. Method descriptions can include: - - - Prose "doc string" documentation about the method and its - usage. - - - A description of the methods arguments; how many arguments - are expected, optional arguments and their default values, - the position or arguments in the signature, whether the - method accepts arbitrary arguments and whether the method - accepts arbitrary keyword arguments. - - - Optional tagged data. Interface objects (and their attributes and - methods) can have optional, application specific tagged data - associated with them. Examples uses for this are examples, - security assertions, pre/post conditions, and other possible - information you may want to associate with an Interface or its - attributes. - - Not all of this information is mandatory. For example, you may - only want the methods of your interface to have prose - documentation and not describe the arguments of the method in - exact detail. Interface objects are flexible and let you give or - take any of these components. - - Interfaces are created with the Python class statement using - either `zope.interface.Interface` or another interface, as in:: - - from zope.interface import Interface - - class IMyInterface(Interface): - '''Interface documentation''' - - def meth(arg1, arg2): - '''Documentation for meth''' - - # Note that there is no self argument - - class IMySubInterface(IMyInterface): - '''Interface documentation''' - - def meth2(): - '''Documentation for meth2''' - - You use interfaces in two ways: - - - You assert that your object implement the interfaces. - - There are several ways that you can declare that an object - provides an interface: - - 1. Call `zope.interface.implementer` on your class definition. - - 2. Call `zope.interface.directlyProvides` on your object. - - 3. Call `zope.interface.classImplements` to declare that instances - of a class implement an interface. - - For example:: - - from zope.interface import classImplements - - classImplements(some_class, some_interface) - - This approach is useful when it is not an option to modify - the class source. Note that this doesn't affect what the - class itself implements, but only what its instances - implement. - - - You query interface meta-data. See the IInterface methods and - attributes for details. - - """ - # pylint:disable=arguments-differ - def names(all=False): # pylint:disable=redefined-builtin - """Get the interface attribute names - - Return a collection of the names of the attributes, including - methods, included in the interface definition. - - Normally, only directly defined attributes are included. If - a true positional or keyword argument is given, then - attributes defined by base classes will be included. - """ - - def namesAndDescriptions(all=False): # pylint:disable=redefined-builtin - """Get the interface attribute names and descriptions - - Return a collection of the names and descriptions of the - attributes, including methods, as name-value pairs, included - in the interface definition. - - Normally, only directly defined attributes are included. If - a true positional or keyword argument is given, then - attributes defined by base classes will be included. - """ - - def __getitem__(name): - """Get the description for a name - - If the named attribute is not defined, a `KeyError` is raised. - """ - - def direct(name): - """Get the description for the name if it was defined by the interface - - If the interface doesn't define the name, returns None. - """ - - def validateInvariants(obj, errors=None): - """Validate invariants - - Validate object to defined invariants. If errors is None, - raises first Invalid error; if errors is a list, appends all errors - to list, then raises Invalid with the errors as the first element - of the "args" tuple.""" - - def __contains__(name): - """Test whether the name is defined by the interface""" - - def __iter__(): - """Return an iterator over the names defined by the interface - - The names iterated include all of the names defined by the - interface directly and indirectly by base interfaces. - """ - - __module__ = Attribute("""The name of the module defining the interface""") - - -class IDeclaration(ISpecification): - """Interface declaration - - Declarations are used to express the interfaces implemented by - classes or provided by objects. - """ - - def __contains__(interface): - """Test whether an interface is in the specification - - Return true if the given interface is one of the interfaces in - the specification and false otherwise. - """ - - def __iter__(): - """Return an iterator for the interfaces in the specification - """ - - def flattened(): - """Return an iterator of all included and extended interfaces - - An iterator is returned for all interfaces either included in - or extended by interfaces included in the specifications - without duplicates. The interfaces are in "interface - resolution order". The interface resolution order is such that - base interfaces are listed after interfaces that extend them - and, otherwise, interfaces are included in the order that they - were defined in the specification. - """ - - def __sub__(interfaces): - """Create an interface specification with some interfaces excluded - - The argument can be an interface or an interface - specifications. The interface or interfaces given in a - specification are subtracted from the interface specification. - - Removing an interface that is not in the specification does - not raise an error. Doing so has no effect. - - Removing an interface also removes sub-interfaces of the interface. - - """ - - def __add__(interfaces): - """Create an interface specification with some interfaces added - - The argument can be an interface or an interface - specifications. The interface or interfaces given in a - specification are added to the interface specification. - - Adding an interface that is already in the specification does - not raise an error. Doing so has no effect. - """ - - def __nonzero__(): - """Return a true value of the interface specification is non-empty - """ - - -class IInterfaceDeclaration(Interface): - """ - Declare and check the interfaces of objects. - - The functions defined in this interface are used to declare the - interfaces that objects provide and to query the interfaces that - have been declared. - - Interfaces can be declared for objects in two ways: - - - Interfaces are declared for instances of the object's class - - - Interfaces are declared for the object directly. - - The interfaces declared for an object are, therefore, the union of - interfaces declared for the object directly and the interfaces - declared for instances of the object's class. - - Note that we say that a class implements the interfaces provided - by it's instances. An instance can also provide interfaces - directly. The interfaces provided by an object are the union of - the interfaces provided directly and the interfaces implemented by - the class. - - This interface is implemented by :mod:`zope.interface`. - """ - # pylint:disable=arguments-differ - ### - # Defining interfaces - ### - - Interface = Attribute("The base class used to create new interfaces") - - def taggedValue(key, value): - """ - Attach a tagged value to an interface while defining the interface. - - This is a way of executing :meth:`IElement.setTaggedValue` from - the definition of the interface. For example:: - - class IFoo(Interface): - taggedValue('key', 'value') - - .. seealso:: `zope.interface.taggedValue` - """ - - def invariant(checker_function): - """ - Attach an invariant checker function to an interface while defining it. - - Invariants can later be validated against particular implementations by - calling :meth:`IInterface.validateInvariants`. - - For example:: - - def check_range(ob): - if ob.max < ob.min: - raise ValueError("max value is less than min value") - - class IRange(Interface): - min = Attribute("The min value") - max = Attribute("The max value") - - invariant(check_range) - - .. seealso:: `zope.interface.invariant` - """ - - def interfacemethod(method): - """ - A decorator that transforms a method specification into an - implementation method. - - This is used to override methods of ``Interface`` or provide new - methods. Definitions using this decorator will not appear in - :meth:`IInterface.names()`. It is possible to have an implementation - method and a method specification of the same name. - - For example:: - - class IRange(Interface): - @interfacemethod - def __adapt__(self, obj): - if isinstance(obj, range): - # Return the builtin ``range`` as-is - return obj - return super(type(IRange), self).__adapt__(obj) - - You can use ``super`` to call the parent class functionality. Note - that the zero-argument version (``super().__adapt__``) works on Python - 3.6 and above, but prior to that the two-argument version must be - used, and the class must be explicitly passed as the first argument. - - .. versionadded:: 5.1.0 - .. seealso:: `zope.interface.interfacemethod` - """ - - ### - # Querying interfaces - ### - - def providedBy(ob): - """ - Return the interfaces provided by an object. - - This is the union of the interfaces directly provided by an - object and interfaces implemented by it's class. - - The value returned is an `IDeclaration`. - - .. seealso:: `zope.interface.providedBy` - """ - - def implementedBy(class_): - """ - Return the interfaces implemented for a class's instances. - - The value returned is an `IDeclaration`. - - .. seealso:: `zope.interface.implementedBy` - """ - - ### - # Declaring interfaces - ### - - def classImplements(class_, *interfaces): - """ - Declare additional interfaces implemented for instances of a class. - - The arguments after the class are one or more interfaces or - interface specifications (`IDeclaration` objects). - - The interfaces given (including the interfaces in the - specifications) are added to any interfaces previously - declared. - - Consider the following example:: - - class C(A, B): - ... - - classImplements(C, I1, I2) - - - Instances of ``C`` provide ``I1``, ``I2``, and whatever interfaces - instances of ``A`` and ``B`` provide. This is equivalent to:: - - @implementer(I1, I2) - class C(A, B): - pass - - .. seealso:: `zope.interface.classImplements` - .. seealso:: `zope.interface.implementer` - """ - - def classImplementsFirst(cls, interface): - """ - See :func:`zope.interface.classImplementsFirst`. - """ - - def implementer(*interfaces): - """ - Create a decorator for declaring interfaces implemented by a - factory. - - A callable is returned that makes an implements declaration on - objects passed to it. - - .. seealso:: :meth:`classImplements` - """ - - def classImplementsOnly(class_, *interfaces): - """ - Declare the only interfaces implemented by instances of a class. - - The arguments after the class are one or more interfaces or - interface specifications (`IDeclaration` objects). - - The interfaces given (including the interfaces in the - specifications) replace any previous declarations. - - Consider the following example:: - - class C(A, B): - ... - - classImplements(C, IA, IB. IC) - classImplementsOnly(C. I1, I2) - - Instances of ``C`` provide only ``I1``, ``I2``, and regardless of - whatever interfaces instances of ``A`` and ``B`` implement. - - .. seealso:: `zope.interface.classImplementsOnly` - """ - - def implementer_only(*interfaces): - """ - Create a decorator for declaring the only interfaces implemented. - - A callable is returned that makes an implements declaration on - objects passed to it. - - .. seealso:: `zope.interface.implementer_only` - """ - - def directlyProvidedBy(object): # pylint:disable=redefined-builtin - """ - Return the interfaces directly provided by the given object. - - The value returned is an `IDeclaration`. - - .. seealso:: `zope.interface.directlyProvidedBy` - """ - - def directlyProvides( - object, *interfaces, - ): # pylint:disable=redefined-builtin - """ - Declare interfaces declared directly for an object. - - The arguments after the object are one or more interfaces or - interface specifications (`IDeclaration` objects). - - .. caution:: - The interfaces given (including the interfaces in the - specifications) *replace* interfaces previously - declared for the object. See :meth:`alsoProvides` to add - additional interfaces. - - Consider the following example:: - - class C(A, B): - ... - - ob = C() - directlyProvides(ob, I1, I2) - - The object, ``ob`` provides ``I1``, ``I2``, and whatever interfaces - instances have been declared for instances of ``C``. - - To remove directly provided interfaces, use `directlyProvidedBy` and - subtract the unwanted interfaces. For example:: - - directlyProvides(ob, directlyProvidedBy(ob)-I2) - - removes I2 from the interfaces directly provided by - ``ob``. The object, ``ob`` no longer directly provides ``I2``, - although it might still provide ``I2`` if it's class - implements ``I2``. - - To add directly provided interfaces, use `directlyProvidedBy` and - include additional interfaces. For example:: - - directlyProvides(ob, directlyProvidedBy(ob), I2) - - adds I2 to the interfaces directly provided by ob. - - .. seealso:: `zope.interface.directlyProvides` - """ - - def alsoProvides(object, *interfaces): # pylint:disable=redefined-builtin - """ - Declare additional interfaces directly for an object. - - For example:: - - alsoProvides(ob, I1) - - is equivalent to:: - - directlyProvides(ob, directlyProvidedBy(ob), I1) - - .. seealso:: `zope.interface.alsoProvides` - """ - - def noLongerProvides( - object, interface, - ): # pylint:disable=redefined-builtin - """ - Remove an interface from the list of an object's directly provided - interfaces. - - For example:: - - noLongerProvides(ob, I1) - - is equivalent to:: - - directlyProvides(ob, directlyProvidedBy(ob) - I1) - - with the exception that if ``I1`` is an interface that is - provided by ``ob`` through the class's implementation, - `ValueError` is raised. - - .. seealso:: `zope.interface.noLongerProvides` - """ - - def provider(*interfaces): - """ - Declare interfaces provided directly by a class. - - .. seealso:: `zope.interface.provider` - """ - - def moduleProvides(*interfaces): - """ - Declare interfaces provided by a module. - - This function is used in a module definition. - - The arguments are one or more interfaces or interface - specifications (`IDeclaration` objects). - - The given interfaces (including the interfaces in the - specifications) are used to create the module's direct-object - interface specification. An error will be raised if the module - already has an interface specification. In other words, it is - an error to call this function more than once in a module - definition. - - This function is provided for convenience. It provides a more - convenient way to call `directlyProvides` for a module. For example:: - - moduleImplements(I1) - - is equivalent to:: - - directlyProvides(sys.modules[__name__], I1) - - .. seealso:: `zope.interface.moduleProvides` - """ - - def Declaration(*interfaces): - """ - Create an interface specification. - - The arguments are one or more interfaces or interface - specifications (`IDeclaration` objects). - - A new interface specification (`IDeclaration`) with the given - interfaces is returned. - - .. seealso:: `zope.interface.Declaration` - """ - - -class IAdapterRegistry(Interface): - """Provide an interface-based registry for adapters - - This registry registers objects that are in some sense "from" a - sequence of specification to an interface and a name. - - No specific semantics are assumed for the registered objects, - however, the most common application will be to register factories - that adapt objects providing required specifications to a provided - interface. - """ - - def register(required, provided, name, value): - """Register a value - - A value is registered for a *sequence* of required specifications, a - provided interface, and a name, which must be text. - """ - - def registered(required, provided, name=''): - """Return the component registered for the given interfaces and name - - name must be text. - - Unlike the lookup method, this methods won't retrieve - components registered for more specific required interfaces or - less specific provided interfaces. - - If no component was registered exactly for the given - interfaces and name, then None is returned. - - """ - - def lookup(required, provided, name='', default=None): - """Lookup a value - - A value is looked up based on a *sequence* of required - specifications, a provided interface, and a name, which must be - text. - """ - - def queryMultiAdapter(objects, provided, name='', default=None): - """Adapt a sequence of objects to a named, provided, interface - """ - - def lookup1(required, provided, name='', default=None): - """Lookup a value using a single required interface - - A value is looked up based on a single required - specifications, a provided interface, and a name, which must be - text. - """ - - def queryAdapter( - object, provided, name='', default=None, - ): # pylint:disable=redefined-builtin - """Adapt an object using a registered adapter factory. - """ - - def adapter_hook( - provided, object, name='', default=None, - ): # pylint:disable=redefined-builtin - """Adapt an object using a registered adapter factory. - - name must be text. - """ - - def lookupAll(required, provided): - """Find all adapters from the required to the provided interfaces - - An iterable object is returned that provides name-value two-tuples. - """ - - def names(required, provided): # pylint:disable=arguments-differ - """Return the names for which there are registered objects - """ - - def subscribe( - required, provided, subscriber, - ): # pylint:disable=arguments-differ - """Register a subscriber - - A subscriber is registered for a *sequence* of required - specifications, a provided interface, and a name. - - Multiple subscribers may be registered for the same (or - equivalent) interfaces. - - .. versionchanged:: 5.1.1 - Correct the method signature to remove the ``name`` parameter. - Subscribers have no names. - """ - - def subscribed(required, provided, subscriber): - """ - Check whether the object *subscriber* is registered directly - with this object via a previous call to - ``subscribe(required, provided, subscriber)``. - - If the *subscriber*, or one equal to it, has been subscribed, - for the given *required* sequence and *provided* interface, - return that object. (This does not guarantee whether the *subscriber* - itself is returned, or an object equal to it.) - - If it has not, return ``None``. - - Unlike :meth:`subscriptions`, this method won't retrieve - components registered for more specific required interfaces or - less specific provided interfaces. - - .. versionadded:: 5.3.0 - """ - - def subscriptions(required, provided): - """ - Get a sequence of subscribers. - - Subscribers for a sequence of *required* interfaces, and a *provided* - interface are returned. This takes into account subscribers - registered with this object, as well as those registered with - base adapter registries in the resolution order, and interfaces that - extend *provided*. - - .. versionchanged:: 5.1.1 - Correct the method signature to remove the ``name`` parameter. - Subscribers have no names. - """ - - def subscribers(objects, provided): - """ - Get a sequence of subscription **adapters**. - - This is like :meth:`subscriptions`, but calls the returned - subscribers with *objects* (and optionally returns the results - of those calls), instead of returning the subscribers directly. - - :param objects: A sequence of objects; they will be used to - determine the *required* argument to :meth:`subscriptions`. - :param provided: A single interface, or ``None``, to pass - as the *provided* parameter to :meth:`subscriptions`. - If an interface is given, the results of calling each returned - subscriber with the the *objects* are collected and returned - from this method; each result should be an object implementing - the *provided* interface. If ``None``, the resulting subscribers - are still called, but the results are ignored. - :return: A sequence of the results of calling the subscribers - if *provided* is not ``None``. If there are no registered - subscribers, or *provided* is ``None``, this will be an empty - sequence. - - .. versionchanged:: 5.1.1 - Correct the method signature to remove the ``name`` parameter. - Subscribers have no names. - """ - -# begin formerly in zope.component - - -class ComponentLookupError(LookupError): - """A component could not be found.""" - - -class Invalid(Exception): - """A component doesn't satisfy a promise.""" - - -class IObjectEvent(Interface): - """An event related to an object. - - The object that generated this event is not necessarily the object - referred to by location. - """ - - object = Attribute("The subject of the event.") - - -@implementer(IObjectEvent) -class ObjectEvent: - - def __init__(self, object): # pylint:disable=redefined-builtin - self.object = object - - -class IComponentLookup(Interface): - """Component Manager for a Site - - This object manages the components registered at a particular site. The - definition of a site is intentionally vague. - """ - - adapters = Attribute( - "Adapter Registry to manage all registered adapters.") - - utilities = Attribute( - "Adapter Registry to manage all registered utilities.") - - def queryAdapter( - object, interface, name='', default=None - ): # pylint:disable=redefined-builtin - """Look for a named adapter to an interface for an object - - If a matching adapter cannot be found, returns the default. - """ - - def getAdapter( - object, interface, name='' - ): # pylint:disable=redefined-builtin - """Look for a named adapter to an interface for an object - - If a matching adapter cannot be found, a `ComponentLookupError` - is raised. - """ - - def queryMultiAdapter(objects, interface, name='', default=None): - """Look for a multi-adapter to an interface for multiple objects - - If a matching adapter cannot be found, returns the default. - """ - - def getMultiAdapter(objects, interface, name=''): - """Look for a multi-adapter to an interface for multiple objects - - If a matching adapter cannot be found, a `ComponentLookupError` - is raised. - """ - - def getAdapters(objects, provided): - """Look for all matching adapters to a provided interface for objects - - Return an iterable of name-adapter pairs for adapters that - provide the given interface. - """ - - def subscribers(objects, provided): - """Get subscribers - - Subscribers are returned that provide the provided interface - and that depend on and are computed from the sequence of - required objects. - """ - - def handle(*objects): - """Call handlers for the given objects - - Handlers registered for the given objects are called. - """ - - def queryUtility(interface, name='', default=None): - """Look up a utility that provides an interface. - - If one is not found, returns default. - """ - - def getUtilitiesFor(interface): - """Look up the registered utilities that provide an interface. - - Returns an iterable of name-utility pairs. - """ - - def getAllUtilitiesRegisteredFor(interface): - """Return all registered utilities for an interface - - This includes overridden utilities. - - An iterable of utility instances is returned. No names are - returned. - """ - - -class IRegistration(Interface): - """A registration-information object - """ - - registry = Attribute("The registry having the registration") - - name = Attribute("The registration name") - - info = Attribute("""Information about the registration - - This is information deemed useful to people browsing the - configuration of a system. It could, for example, include - commentary or information about the source of the configuration. - """) - - -class IUtilityRegistration(IRegistration): - """Information about the registration of a utility - """ - - factory = Attribute("The factory used to create the utility. Optional.") - component = Attribute("The object registered") - provided = Attribute("The interface provided by the component") - - -class _IBaseAdapterRegistration(IRegistration): - """Information about the registration of an adapter - """ - - factory = Attribute("The factory used to create adapters") - - required = Attribute("""The adapted interfaces - - This is a sequence of interfaces adapters by the registered - factory. The factory will be caled with a sequence of objects, as - positional arguments, that provide these interfaces. - """) - - provided = Attribute("""The interface provided by the adapters. - - This interface is implemented by the factory - """) - - -class IAdapterRegistration(_IBaseAdapterRegistration): - """Information about the registration of an adapter - """ - - -class ISubscriptionAdapterRegistration(_IBaseAdapterRegistration): - """Information about the registration of a subscription adapter - """ - - -class IHandlerRegistration(IRegistration): - - handler = Attribute("An object called used to handle an event") - - required = Attribute("""The handled interfaces - - This is a sequence of interfaces handled by the registered - handler. The handler will be caled with a sequence of objects, as - positional arguments, that provide these interfaces. - """) - - -class IRegistrationEvent(IObjectEvent): - """An event that involves a registration""" - - -@implementer(IRegistrationEvent) -class RegistrationEvent(ObjectEvent): - """There has been a change in a registration - """ - - def __repr__(self): - return f"{self.__class__.__name__} event:\n{self.object!r}" - - -class IRegistered(IRegistrationEvent): - """A component or factory was registered - """ - - -@implementer(IRegistered) -class Registered(RegistrationEvent): - pass - - -class IUnregistered(IRegistrationEvent): - """A component or factory was unregistered - """ - - -@implementer(IUnregistered) -class Unregistered(RegistrationEvent): - """A component or factory was unregistered - """ - - -class IComponentRegistry(Interface): - """Register components - """ - - def registerUtility(component=None, provided=None, name='', - info='', factory=None): - """Register a utility - - :param factory: - Factory for the component to be registered. - - :param component: - The registered component - - :param provided: - This is the interface provided by the utility. If the - component provides a single interface, then this - argument is optional and the component-implemented - interface will be used. - - :param name: - The utility name. - - :param info: - An object that can be converted to a string to provide - information about the registration. - - Only one of *component* and *factory* can be used. - - A `IRegistered` event is generated with an `IUtilityRegistration`. - """ - - def unregisterUtility(component=None, provided=None, name='', - factory=None): - """Unregister a utility - - :returns: - A boolean is returned indicating whether the registry was - changed. If the given *component* is None and there is no - component registered, or if the given *component* is not - None and is not registered, then the function returns - False, otherwise it returns True. - - :param factory: - Factory for the component to be unregistered. - - :param component: - The registered component The given component can be - None, in which case any component registered to provide - the given provided interface with the given name is - unregistered. - - :param provided: - This is the interface provided by the utility. If the - component is not None and provides a single interface, - then this argument is optional and the - component-implemented interface will be used. - - :param name: - The utility name. - - Only one of *component* and *factory* can be used. - An `IUnregistered` event is generated with an `IUtilityRegistration`. - """ - - def registeredUtilities(): - """Return an iterable of `IUtilityRegistration` instances. - - These registrations describe the current utility registrations - in the object. - """ - - def registerAdapter(factory, required=None, provided=None, name='', - info=''): - """Register an adapter factory - - :param factory: - The object used to compute the adapter - - :param required: - This is a sequence of specifications for objects to be - adapted. If omitted, then the value of the factory's - ``__component_adapts__`` attribute will be used. The - ``__component_adapts__`` attribute is - normally set in class definitions using - the `.adapter` - decorator. If the factory doesn't have a - ``__component_adapts__`` adapts attribute, then this - argument is required. - - :param provided: - This is the interface provided by the adapter and - implemented by the factory. If the factory - implements a single interface, then this argument is - optional and the factory-implemented interface will be - used. - - :param name: - The adapter name. - - :param info: - An object that can be converted to a string to provide - information about the registration. - - A `IRegistered` event is generated with an `IAdapterRegistration`. - """ - - def unregisterAdapter(factory=None, required=None, - provided=None, name=''): - """Unregister an adapter factory - - :returns: - A boolean is returned indicating whether the registry was - changed. If the given component is None and there is no - component registered, or if the given component is not - None and is not registered, then the function returns - False, otherwise it returns True. - - :param factory: - This is the object used to compute the adapter. The - factory can be None, in which case any factory - registered to implement the given provided interface - for the given required specifications with the given - name is unregistered. - - :param required: - This is a sequence of specifications for objects to be - adapted. If the factory is not None and the required - arguments is omitted, then the value of the factory's - __component_adapts__ attribute will be used. The - __component_adapts__ attribute attribute is normally - set in class definitions using adapts function, or for - callables using the adapter decorator. If the factory - is None or doesn't have a __component_adapts__ adapts - attribute, then this argument is required. - - :param provided: - This is the interface provided by the adapter and - implemented by the factory. If the factory is not - None and implements a single interface, then this - argument is optional and the factory-implemented - interface will be used. - - :param name: - The adapter name. - - An `IUnregistered` event is generated with an `IAdapterRegistration`. - """ - - def registeredAdapters(): - """Return an iterable of `IAdapterRegistration` instances. - - These registrations describe the current adapter registrations - in the object. - """ - - def registerSubscriptionAdapter(factory, required=None, provides=None, - name='', info=''): - """Register a subscriber factory - - :param factory: - The object used to compute the adapter - - :param required: - This is a sequence of specifications for objects to be - adapted. If omitted, then the value of the factory's - ``__component_adapts__`` attribute will be used. The - ``__component_adapts__`` attribute is - normally set using the adapter - decorator. If the factory doesn't have a - ``__component_adapts__`` adapts attribute, then this - argument is required. - - :param provided: - This is the interface provided by the adapter and - implemented by the factory. If the factory implements - a single interface, then this argument is optional and - the factory-implemented interface will be used. - - :param name: - The adapter name. - - Currently, only the empty string is accepted. Other - strings will be accepted in the future when support for - named subscribers is added. - - :param info: - An object that can be converted to a string to provide - information about the registration. - - A `IRegistered` event is generated with an - `ISubscriptionAdapterRegistration`. - """ - - def unregisterSubscriptionAdapter(factory=None, required=None, - provides=None, name=''): - """Unregister a subscriber factory. - - :returns: - A boolean is returned indicating whether the registry was - changed. If the given component is None and there is no - component registered, or if the given component is not - None and is not registered, then the function returns - False, otherwise it returns True. - - :param factory: - This is the object used to compute the adapter. The - factory can be None, in which case any factories - registered to implement the given provided interface - for the given required specifications with the given - name are unregistered. - - :param required: - This is a sequence of specifications for objects to be - adapted. If omitted, then the value of the factory's - ``__component_adapts__`` attribute will be used. The - ``__component_adapts__`` attribute is - normally set using the adapter - decorator. If the factory doesn't have a - ``__component_adapts__`` adapts attribute, then this - argument is required. - - :param provided: - This is the interface provided by the adapter and - implemented by the factory. If the factory is not - None implements a single interface, then this argument - is optional and the factory-implemented interface will - be used. - - :param name: - The adapter name. - - Currently, only the empty string is accepted. Other - strings will be accepted in the future when support for - named subscribers is added. - - An `IUnregistered` event is generated with an - `ISubscriptionAdapterRegistration`. - """ - - def registeredSubscriptionAdapters(): - """Return an iterable of `ISubscriptionAdapterRegistration` instances. - - These registrations describe the current subscription adapter - registrations in the object. - """ - - def registerHandler(handler, required=None, name='', info=''): - """Register a handler. - - A handler is a subscriber that doesn't compute an adapter - but performs some function when called. - - :param handler: - The object used to handle some event represented by - the objects passed to it. - - :param required: - This is a sequence of specifications for objects to be - adapted. If omitted, then the value of the factory's - ``__component_adapts__`` attribute will be used. The - ``__component_adapts__`` attribute is - normally set using the adapter - decorator. If the factory doesn't have a - ``__component_adapts__`` adapts attribute, then this - argument is required. - - :param name: - The handler name. - - Currently, only the empty string is accepted. Other - strings will be accepted in the future when support for - named handlers is added. - - :param info: - An object that can be converted to a string to provide - information about the registration. - - - A `IRegistered` event is generated with an `IHandlerRegistration`. - """ - - def unregisterHandler(handler=None, required=None, name=''): - """Unregister a handler. - - A handler is a subscriber that doesn't compute an adapter - but performs some function when called. - - :returns: A boolean is returned indicating whether the registry was - changed. - - :param handler: - This is the object used to handle some event - represented by the objects passed to it. The handler - can be None, in which case any handlers registered for - the given required specifications with the given are - unregistered. - - :param required: - This is a sequence of specifications for objects to be - adapted. If omitted, then the value of the factory's - ``__component_adapts__`` attribute will be used. The - ``__component_adapts__`` attribute is - normally set using the adapter - decorator. If the factory doesn't have a - ``__component_adapts__`` adapts attribute, then this - argument is required. - - :param name: - The handler name. - - Currently, only the empty string is accepted. Other - strings will be accepted in the future when support for - named handlers is added. - - An `IUnregistered` event is generated with an `IHandlerRegistration`. - """ - - def registeredHandlers(): - """Return an iterable of `IHandlerRegistration` instances. - - These registrations describe the current handler registrations - in the object. - """ - - -class IComponents(IComponentLookup, IComponentRegistry): - """Component registration and access - """ - - -# end formerly in zope.component diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/registry.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/registry.py deleted file mode 100644 index 37c4ec9..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/registry.py +++ /dev/null @@ -1,750 +0,0 @@ -############################################################################## -# -# Copyright (c) 2006 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Basic components support -""" -from collections import defaultdict - - -try: - from zope.event import notify -except ImportError: # pragma: no cover - def notify(*arg, **kw): - pass - -from zope.interface.adapter import AdapterRegistry -from zope.interface.declarations import implementedBy -from zope.interface.declarations import implementer -from zope.interface.declarations import implementer_only -from zope.interface.declarations import providedBy -from zope.interface.interface import Interface -from zope.interface.interfaces import ComponentLookupError -from zope.interface.interfaces import IAdapterRegistration -from zope.interface.interfaces import IComponents -from zope.interface.interfaces import IHandlerRegistration -from zope.interface.interfaces import ISpecification -from zope.interface.interfaces import ISubscriptionAdapterRegistration -from zope.interface.interfaces import IUtilityRegistration -from zope.interface.interfaces import Registered -from zope.interface.interfaces import Unregistered - - -__all__ = [ - # Components is public API, but - # the *Registration classes are just implementations - # of public interfaces. - 'Components', -] - - -class _UnhashableComponentCounter: - # defaultdict(int)-like object for unhashable components - - def __init__(self, otherdict): - # [(component, count)] - self._data = [item for item in otherdict.items()] - - def __getitem__(self, key): - for component, count in self._data: - if component == key: - return count - return 0 - - def __setitem__(self, component, count): - for i, data in enumerate(self._data): - if data[0] == component: - self._data[i] = component, count - return - self._data.append((component, count)) - - def __delitem__(self, component): - for i, data in enumerate(self._data): - if data[0] == component: - del self._data[i] - return - raise KeyError(component) # pragma: no cover - - -def _defaultdict_int(): - return defaultdict(int) - - -class _UtilityRegistrations: - - def __init__(self, utilities, utility_registrations): - # {provided -> {component: count}} - self._cache = defaultdict(_defaultdict_int) - self._utilities = utilities - self._utility_registrations = utility_registrations - - self.__populate_cache() - - def __populate_cache(self): - for ((p, _), data) in iter(self._utility_registrations.items()): - component = data[0] - self.__cache_utility(p, component) - - def __cache_utility(self, provided, component): - try: - self._cache[provided][component] += 1 - except TypeError: - # The component is not hashable, and we have a dict. Switch to a - # strategy that doesn't use hashing. - prov = self._cache[provided] = _UnhashableComponentCounter( - self._cache[provided] - ) - prov[component] += 1 - - def __uncache_utility(self, provided, component): - provided = self._cache[provided] - # It seems like this line could raise a TypeError if component isn't - # hashable and we haven't yet switched to _UnhashableComponentCounter. - # However, we can't actually get in that situation. In order to get - # here, we would have had to cache the utility already which would - # have switched the datastructure if needed. - count = provided[component] - count -= 1 - if count == 0: - del provided[component] - else: - provided[component] = count - return count > 0 - - def _is_utility_subscribed(self, provided, component): - try: - return self._cache[provided][component] > 0 - except TypeError: - # Not hashable and we're still using a dict - return False - - def registerUtility(self, provided, name, component, info, factory): - subscribed = self._is_utility_subscribed(provided, component) - - self._utility_registrations[ - (provided, name) - ] = component, info, factory - self._utilities.register((), provided, name, component) - - if not subscribed: - self._utilities.subscribe((), provided, component) - - self.__cache_utility(provided, component) - - def unregisterUtility(self, provided, name, component): - del self._utility_registrations[(provided, name)] - self._utilities.unregister((), provided, name) - - subscribed = self.__uncache_utility(provided, component) - - if not subscribed: - self._utilities.unsubscribe((), provided, component) - - -@implementer(IComponents) -class Components: - - _v_utility_registrations_cache = None - - def __init__(self, name='', bases=()): - # __init__ is used for test cleanup as well as initialization. - # XXX add a separate API for test cleanup. - assert isinstance(name, str) - self.__name__ = name - self._init_registries() - self._init_registrations() - self.__bases__ = tuple(bases) - self._v_utility_registrations_cache = None - - def __repr__(self): - return f"<{self.__class__.__name__} {self.__name__}>" - - def __reduce__(self): - # Mimic what a persistent.Persistent object does and elide - # _v_ attributes so that they don't get saved in ZODB. - # This allows us to store things that cannot be pickled in such - # attributes. - reduction = super().__reduce__() - # (callable, args, state, listiter, dictiter) - # We assume the state is always a dict; the last three items - # are technically optional and can be missing or None. - filtered_state = {k: v for k, v in reduction[2].items() - if not k.startswith('_v_')} - reduction = list(reduction) - reduction[2] = filtered_state - return tuple(reduction) - - def _init_registries(self): - # Subclasses have never been required to call this method - # if they override it, merely to fill in these two attributes. - self.adapters = AdapterRegistry() - self.utilities = AdapterRegistry() - - def _init_registrations(self): - self._utility_registrations = {} - self._adapter_registrations = {} - self._subscription_registrations = [] - self._handler_registrations = [] - - @property - def _utility_registrations_cache(self): - # We use a _v_ attribute internally so that data aren't saved in ZODB, - # because this object cannot be pickled. - cache = self._v_utility_registrations_cache - if ( - cache is None or - cache._utilities is not self.utilities or - cache._utility_registrations is not self._utility_registrations - ): - cache = self._v_utility_registrations_cache = ( - _UtilityRegistrations( - self.utilities, self._utility_registrations, - ) - ) - return cache - - def _getBases(self): - # Subclasses might override - return self.__dict__.get('__bases__', ()) - - def _setBases(self, bases): - # Subclasses might override - self.adapters.__bases__ = tuple([ - base.adapters for base in bases]) - self.utilities.__bases__ = tuple([ - base.utilities for base in bases]) - self.__dict__['__bases__'] = tuple(bases) - - __bases__ = property( - lambda self: self._getBases(), - lambda self, bases: self._setBases(bases), - ) - - def registerUtility(self, component=None, provided=None, name='', - info='', event=True, factory=None): - if factory: - if component: - raise TypeError("Can't specify factory and component.") - component = factory() - - if provided is None: - provided = _getUtilityProvided(component) - - if name == '': - name = _getName(component) - - reg = self._utility_registrations.get((provided, name)) - if reg is not None: - if reg[:2] == (component, info): - # already registered - return - self.unregisterUtility(reg[0], provided, name) - - self._utility_registrations_cache.registerUtility( - provided, name, component, info, factory) - - if event: - notify(Registered( - UtilityRegistration( - self, provided, name, component, info, factory) - )) - - def unregisterUtility(self, component=None, provided=None, name='', - factory=None): - if factory: - if component: - raise TypeError("Can't specify factory and component.") - component = factory() - - if provided is None: - if component is None: - raise TypeError("Must specify one of component, factory and " - "provided") - provided = _getUtilityProvided(component) - - old = self._utility_registrations.get((provided, name)) - if (old is None) or ((component is not None) and - (component != old[0])): - return False - - if component is None: - component = old[0] - - # Note that component is now the old thing registered - self._utility_registrations_cache.unregisterUtility( - provided, name, component) - - notify(Unregistered( - UtilityRegistration(self, provided, name, component, *old[1:]) - )) - - return True - - def registeredUtilities(self): - for ((provided, name), data - ) in iter(self._utility_registrations.items()): - yield UtilityRegistration(self, provided, name, *data) - - def queryUtility(self, provided, name='', default=None): - return self.utilities.lookup((), provided, name, default) - - def getUtility(self, provided, name=''): - utility = self.utilities.lookup((), provided, name) - if utility is None: - raise ComponentLookupError(provided, name) - return utility - - def getUtilitiesFor(self, interface): - yield from self.utilities.lookupAll((), interface) - - def getAllUtilitiesRegisteredFor(self, interface): - return self.utilities.subscriptions((), interface) - - def registerAdapter(self, factory, required=None, provided=None, - name='', info='', event=True): - if provided is None: - provided = _getAdapterProvided(factory) - required = _getAdapterRequired(factory, required) - if name == '': - name = _getName(factory) - self._adapter_registrations[(required, provided, name) - ] = factory, info - self.adapters.register(required, provided, name, factory) - - if event: - notify(Registered( - AdapterRegistration( - self, required, provided, name, factory, info - ) - )) - - def unregisterAdapter(self, factory=None, - required=None, provided=None, name='', - ): - if provided is None: - if factory is None: - raise TypeError("Must specify one of factory and provided") - provided = _getAdapterProvided(factory) - - if (required is None) and (factory is None): - raise TypeError("Must specify one of factory and required") - - required = _getAdapterRequired(factory, required) - old = self._adapter_registrations.get((required, provided, name)) - if (old is None) or ((factory is not None) and - (factory != old[0])): - return False - - del self._adapter_registrations[(required, provided, name)] - self.adapters.unregister(required, provided, name) - - notify(Unregistered( - AdapterRegistration(self, required, provided, name, *old) - )) - - return True - - def registeredAdapters(self): - for ((required, provided, name), (component, info) - ) in iter(self._adapter_registrations.items()): - yield AdapterRegistration(self, required, provided, name, - component, info) - - def queryAdapter(self, object, interface, name='', default=None): - return self.adapters.queryAdapter(object, interface, name, default) - - def getAdapter(self, object, interface, name=''): - adapter = self.adapters.queryAdapter(object, interface, name) - if adapter is None: - raise ComponentLookupError(object, interface, name) - return adapter - - def queryMultiAdapter(self, objects, interface, name='', - default=None): - return self.adapters.queryMultiAdapter( - objects, interface, name, default) - - def getMultiAdapter(self, objects, interface, name=''): - adapter = self.adapters.queryMultiAdapter(objects, interface, name) - if adapter is None: - raise ComponentLookupError(objects, interface, name) - return adapter - - def getAdapters(self, objects, provided): - for name, factory in self.adapters.lookupAll( - list(map(providedBy, objects)), provided, - ): - adapter = factory(*objects) - if adapter is not None: - yield name, adapter - - def registerSubscriptionAdapter(self, - factory, required=None, provided=None, - name='', info='', - event=True): - if name: - raise TypeError("Named subscribers are not yet supported") - if provided is None: - provided = _getAdapterProvided(factory) - required = _getAdapterRequired(factory, required) - self._subscription_registrations.append( - (required, provided, name, factory, info) - ) - self.adapters.subscribe(required, provided, factory) - - if event: - notify(Registered( - SubscriptionRegistration( - self, required, provided, name, factory, info, - ) - )) - - def registeredSubscriptionAdapters(self): - for data in self._subscription_registrations: - yield SubscriptionRegistration(self, *data) - - def unregisterSubscriptionAdapter( - self, factory=None, required=None, provided=None, name='', - ): - if name: - raise TypeError("Named subscribers are not yet supported") - if provided is None: - if factory is None: - raise TypeError("Must specify one of factory and provided") - provided = _getAdapterProvided(factory) - - if (required is None) and (factory is None): - raise TypeError("Must specify one of factory and required") - - required = _getAdapterRequired(factory, required) - - if factory is None: - new = [(r, p, n, f, i) - for (r, p, n, f, i) - in self._subscription_registrations - if not (r == required and p == provided) - ] - else: - new = [(r, p, n, f, i) - for (r, p, n, f, i) - in self._subscription_registrations - if not (r == required and p == provided and f == factory) - ] - - if len(new) == len(self._subscription_registrations): - return False - - self._subscription_registrations[:] = new - self.adapters.unsubscribe(required, provided, factory) - - notify(Unregistered( - SubscriptionRegistration( - self, required, provided, name, factory, '', - ) - )) - - return True - - def subscribers(self, objects, provided): - return self.adapters.subscribers(objects, provided) - - def registerHandler(self, - factory, required=None, - name='', info='', - event=True): - if name: - raise TypeError("Named handlers are not yet supported") - required = _getAdapterRequired(factory, required) - self._handler_registrations.append( - (required, name, factory, info) - ) - self.adapters.subscribe(required, None, factory) - - if event: - notify(Registered( - HandlerRegistration(self, required, name, factory, info) - )) - - def registeredHandlers(self): - for data in self._handler_registrations: - yield HandlerRegistration(self, *data) - - def unregisterHandler(self, factory=None, required=None, name=''): - if name: - raise TypeError("Named subscribers are not yet supported") - - if (required is None) and (factory is None): - raise TypeError("Must specify one of factory and required") - - required = _getAdapterRequired(factory, required) - - if factory is None: - new = [(r, n, f, i) - for (r, n, f, i) - in self._handler_registrations - if r != required - ] - else: - new = [(r, n, f, i) - for (r, n, f, i) - in self._handler_registrations - if not (r == required and f == factory) - ] - - if len(new) == len(self._handler_registrations): - return False - - self._handler_registrations[:] = new - self.adapters.unsubscribe(required, None, factory) - - notify(Unregistered( - HandlerRegistration(self, required, name, factory, '') - )) - - return True - - def handle(self, *objects): - self.adapters.subscribers(objects, None) - - def rebuildUtilityRegistryFromLocalCache(self, rebuild=False): - """ - Emergency maintenance method to rebuild the ``.utilities`` - registry from the local copy maintained in this object, or - detect the need to do so. - - Most users will never need to call this, but it can be helpful - in the event of suspected corruption. - - By default, this method only checks for corruption. To make it - actually rebuild the registry, pass `True` for *rebuild*. - - :param bool rebuild: If set to `True` (not the default), - this method will actually register and subscribe utilities - in the registry as needed to synchronize with the local cache. - - :return: A dictionary that's meant as diagnostic data. The keys - and values may change over time. When called with a false - *rebuild*, the keys ``"needed_registered"`` and - ``"needed_subscribed"`` will be non-zero if any corruption was - detected, but that will not be corrected. - - .. versionadded:: 5.3.0 - """ - regs = dict(self._utility_registrations) - utils = self.utilities - needed_registered = 0 - did_not_register = 0 - needed_subscribed = 0 - did_not_subscribe = 0 - - # Avoid the expensive change process during this; we'll call - # it once at the end if needed. - assert 'changed' not in utils.__dict__ - utils.changed = lambda _: None - - if rebuild: - register = utils.register - subscribe = utils.subscribe - else: - register = subscribe = lambda *args: None - - try: - for (provided, name), (value, _info, _factory) in regs.items(): - if utils.registered((), provided, name) != value: - register((), provided, name, value) - needed_registered += 1 - else: - did_not_register += 1 - - if utils.subscribed((), provided, value) is None: - needed_subscribed += 1 - subscribe((), provided, value) - else: - did_not_subscribe += 1 - finally: - del utils.changed - if rebuild and (needed_subscribed or needed_registered): - utils.changed(utils) - - return { - 'needed_registered': needed_registered, - 'did_not_register': did_not_register, - 'needed_subscribed': needed_subscribed, - 'did_not_subscribe': did_not_subscribe - } - - -def _getName(component): - try: - return component.__component_name__ - except AttributeError: - return '' - - -def _getUtilityProvided(component): - provided = list(providedBy(component)) - if len(provided) == 1: - return provided[0] - raise TypeError( - "The utility doesn't provide a single interface " - "and no provided interface was specified." - ) - - -def _getAdapterProvided(factory): - provided = list(implementedBy(factory)) - if len(provided) == 1: - return provided[0] - raise TypeError( - "The adapter factory doesn't implement a single interface " - "and no provided interface was specified." - ) - - -def _getAdapterRequired(factory, required): - if required is None: - try: - required = factory.__component_adapts__ - except AttributeError: - raise TypeError( - "The adapter factory doesn't have a __component_adapts__ " - "attribute and no required specifications were specified" - ) - elif ISpecification.providedBy(required): - raise TypeError( - "the required argument should be a list of " - "interfaces, not a single interface" - ) - - result = [] - for r in required: - if r is None: - r = Interface - elif not ISpecification.providedBy(r): - if isinstance(r, type): - r = implementedBy(r) - else: - raise TypeError( - "Required specification must be a " - "specification or class, not %r" % type(r) - ) - result.append(r) - return tuple(result) - - -@implementer(IUtilityRegistration) -class UtilityRegistration: - - def __init__(self, registry, provided, name, component, doc, factory=None): - self.registry = registry - self.provided = provided - self.name = name - self.component = component - self.info = doc - self.factory = factory - - def __repr__(self): - return '{}({!r}, {}, {!r}, {}, {!r}, {!r})'.format( - self.__class__.__name__, - self.registry, - getattr(self.provided, '__name__', None), self.name, - getattr(self.component, '__name__', repr(self.component)), - self.factory, self.info, - ) - - def __hash__(self): - return id(self) - - def __eq__(self, other): - return repr(self) == repr(other) - - def __ne__(self, other): - return repr(self) != repr(other) - - def __lt__(self, other): - return repr(self) < repr(other) - - def __le__(self, other): - return repr(self) <= repr(other) - - def __gt__(self, other): - return repr(self) > repr(other) - - def __ge__(self, other): - return repr(self) >= repr(other) - - -@implementer(IAdapterRegistration) -class AdapterRegistration: - - def __init__(self, registry, required, provided, name, component, doc): - (self.registry, self.required, self.provided, self.name, - self.factory, self.info - ) = registry, required, provided, name, component, doc - - def __repr__(self): - return '{}({!r}, {}, {}, {!r}, {}, {!r})'.format( - self.__class__.__name__, - self.registry, - '[' + ", ".join([r.__name__ for r in self.required]) + ']', - getattr(self.provided, '__name__', None), self.name, - getattr(self.factory, '__name__', repr(self.factory)), self.info, - ) - - def __hash__(self): - return id(self) - - def __eq__(self, other): - return repr(self) == repr(other) - - def __ne__(self, other): - return repr(self) != repr(other) - - def __lt__(self, other): - return repr(self) < repr(other) - - def __le__(self, other): - return repr(self) <= repr(other) - - def __gt__(self, other): - return repr(self) > repr(other) - - def __ge__(self, other): - return repr(self) >= repr(other) - - -@implementer_only(ISubscriptionAdapterRegistration) -class SubscriptionRegistration(AdapterRegistration): - pass - - -@implementer_only(IHandlerRegistration) -class HandlerRegistration(AdapterRegistration): - - def __init__(self, registry, required, name, handler, doc): - (self.registry, self.required, self.name, self.handler, self.info - ) = registry, required, name, handler, doc - - @property - def factory(self): - return self.handler - - provided = None - - def __repr__(self): - return '{}({!r}, {}, {!r}, {}, {!r})'.format( - self.__class__.__name__, - self.registry, - '[' + ", ".join([r.__name__ for r in self.required]) + ']', - self.name, - getattr(self.factory, '__name__', repr(self.factory)), self.info, - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/ro.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/ro.py deleted file mode 100644 index 5233e49..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/ro.py +++ /dev/null @@ -1,722 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -""" -Compute a resolution order for an object and its bases. - -.. versionchanged:: 5.0 - The resolution order is now based on the same C3 order that Python - uses for classes. In complex instances of multiple inheritance, this - may result in a different ordering. - - In older versions, the ordering wasn't required to be C3 compliant, - and for backwards compatibility, it still isn't. If the ordering - isn't C3 compliant (if it is *inconsistent*), zope.interface will - make a best guess to try to produce a reasonable resolution order. - Still (just as before), the results in such cases may be - surprising. - -.. rubric:: Environment Variables - -Due to the change in 5.0, certain environment variables can be used to control -errors and warnings about inconsistent resolution orders. They are listed in -priority order, with variables at the bottom generally overriding variables -above them. - -ZOPE_INTERFACE_WARN_BAD_IRO - If this is set to "1", then if there is at least one inconsistent - resolution order discovered, a warning - (:class:`InconsistentResolutionOrderWarning`) will be issued. Use the - usual warning mechanisms to control this behaviour. The warning text will - contain additional information on debugging. - -ZOPE_INTERFACE_TRACK_BAD_IRO - If this is set to "1", then zope.interface will log information about each - inconsistent resolution order discovered, and keep those details in memory - in this module for later inspection. - -ZOPE_INTERFACE_STRICT_IRO - If this is set to "1", any attempt to use :func:`ro` that would produce a - non-C3 ordering will fail by raising - :class:`InconsistentResolutionOrderError`. - -.. important:: - - ``ZOPE_INTERFACE_STRICT_IRO`` is intended to become the default in the - future. - -There are two environment variables that are independent. - -ZOPE_INTERFACE_LOG_CHANGED_IRO - If this is set to "1", then if the C3 resolution order is different from - the legacy resolution order for any given object, a message explaining the - differences will be logged. This is intended to be used for debugging - complicated IROs. - -ZOPE_INTERFACE_USE_LEGACY_IRO - If this is set to "1", then the C3 resolution order will *not* be used. - The legacy IRO will be used instead. This is a temporary measure and will - be removed in the future. It is intended to help during the transition. - It implies ``ZOPE_INTERFACE_LOG_CHANGED_IRO``. - -.. rubric:: Debugging Behaviour Changes in zope.interface 5 - -Most behaviour changes from zope.interface 4 to 5 are related to -inconsistent resolution orders. ``ZOPE_INTERFACE_STRICT_IRO`` is the -most effective tool to find such inconsistent resolution orders, and -we recommend running your code with this variable set if at all -possible. Doing so will ensure that all interface resolution orders -are consistent, and if they're not, will immediately point the way to -where this is violated. - -Occasionally, however, this may not be enough. This is because in some -cases, a C3 ordering can be found (the resolution order is fully -consistent) that is substantially different from the ad-hoc legacy -ordering. In such cases, you may find that you get an unexpected value -returned when adapting one or more objects to an interface. To debug -this, *also* enable ``ZOPE_INTERFACE_LOG_CHANGED_IRO`` and examine the -output. The main thing to look for is changes in the relative -positions of interfaces for which there are registered adapters. -""" -__docformat__ = 'restructuredtext' - -import warnings - - -__all__ = [ - 'ro', - 'InconsistentResolutionOrderError', - 'InconsistentResolutionOrderWarning', -] - -__logger = None - - -def _logger(): - global __logger # pylint:disable=global-statement - if __logger is None: - import logging - __logger = logging.getLogger(__name__) - return __logger - - -def _legacy_mergeOrderings(orderings): - """Merge multiple orderings so that within-ordering order is preserved - - Orderings are constrained in such a way that if an object appears - in two or more orderings, then the suffix that begins with the - object must be in both orderings. - - For example: - - >>> _legacy_mergeOrderings([ - ... ['x', 'y', 'z'], - ... ['q', 'z'], - ... [1, 3, 5], - ... ['z'] - ... ]) - ['x', 'y', 'q', 1, 3, 5, 'z'] - - """ - - seen = set() - result = [] - for ordering in reversed(orderings): - for o in reversed(ordering): - if o not in seen: - seen.add(o) - result.insert(0, o) - - return result - - -def _legacy_flatten(begin): - result = [begin] - i = 0 - for ob in iter(result): - i += 1 - # The recursive calls can be avoided by inserting the base classes - # into the dynamically growing list directly after the currently - # considered object; the iterator makes sure this will keep working - # in the future, since it cannot rely on the length of the list - # by definition. - result[i:i] = ob.__bases__ - return result - - -def _legacy_ro(ob): - return _legacy_mergeOrderings([_legacy_flatten(ob)]) - -### -# Compare base objects using identity, not equality. This matches what -# the CPython MRO algorithm does, and is *much* faster to boot: that, -# plus some other small tweaks makes the difference between 25s and 6s -# in loading 446 plone/zope interface.py modules (1925 InterfaceClass, -# 1200 Implements, 1100 ClassProvides objects) -### - - -class InconsistentResolutionOrderWarning(PendingDeprecationWarning): - """ - The warning issued when an invalid IRO is requested. - """ - - -class InconsistentResolutionOrderError(TypeError): - """ - The error raised when an invalid IRO is requested in strict mode. - """ - - def __init__(self, c3, base_tree_remaining): - self.C = c3.leaf - base_tree = c3.base_tree - self.base_ros = { - base: base_tree[i + 1] - for i, base in enumerate(self.C.__bases__) - } - # Unfortunately, this doesn't necessarily directly match - # up to any transformation on C.__bases__, because - # if any were fully used up, they were removed already. - self.base_tree_remaining = base_tree_remaining - - TypeError.__init__(self) - - def __str__(self): - import pprint - return ( - "{}: For object {!r}.\nBase ROs:\n{}\nConflict Location:\n{}" - ).format( - self.__class__.__name__, - self.C, - pprint.pformat(self.base_ros), - pprint.pformat(self.base_tree_remaining), - ) - - -class _NamedBool(int): # cannot actually inherit bool - - def __new__(cls, val, name): - inst = super(cls, _NamedBool).__new__(cls, val) - inst.__name__ = name - return inst - - -class _ClassBoolFromEnv: - """ - Non-data descriptor that reads a transformed environment variable - as a boolean, and caches the result in the class. - """ - - def __get__(self, inst, klass): - import os - for cls in klass.__mro__: - my_name = None - for k in dir(klass): - if k in cls.__dict__ and cls.__dict__[k] is self: - my_name = k - break - if my_name is not None: - break - else: # pragma: no cover - raise RuntimeError("Unable to find self") - - env_name = 'ZOPE_INTERFACE_' + my_name - val = os.environ.get(env_name, '') == '1' - val = _NamedBool(val, my_name) - setattr(klass, my_name, val) - setattr(klass, 'ORIG_' + my_name, self) - return val - - -class _StaticMRO: - # A previously resolved MRO, supplied by the caller. - # Used in place of calculating it. - - had_inconsistency = None # We don't know... - - def __init__(self, C, mro): - self.leaf = C - self.__mro = tuple(mro) - - def mro(self): - return list(self.__mro) - - -_INCONSISTENT_RESOLUTION_ORDER = """\ -An inconsistent resolution order is being requested. (Interfaces should -follow the Python class rules known as C3.) For backwards compatibility, -zope.interface will allow this, making the best guess it can to produce as -meaningful an order as possible. In the future this might be an error. Set -the warning filter to error, or set the environment variable -'ZOPE_INTERFACE_TRACK_BAD_IRO' to '1' and examine ro.C3.BAD_IROS to debug, or -set 'ZOPE_INTERFACE_STRICT_IRO' to raise exceptions.""" - - -class C3: - # Holds the shared state during computation of an MRO. - - @staticmethod - def resolver(C, strict, base_mros): - strict = strict if strict is not None else C3.STRICT_IRO - factory = C3 - if strict: - factory = _StrictC3 - elif C3.TRACK_BAD_IRO: - factory = _TrackingC3 - - memo = {} - base_mros = base_mros or {} - for base, mro in base_mros.items(): - assert base in C.__bases__ - memo[base] = _StaticMRO(base, mro) - - return factory(C, memo) - - __mro = None - __legacy_ro = None - direct_inconsistency = False - - def __init__(self, C, memo): - self.leaf = C - self.memo = memo - kind = self.__class__ - - base_resolvers = [] - for base in C.__bases__: - if base not in memo: - resolver = kind(base, memo) - memo[base] = resolver - base_resolvers.append(memo[base]) - - self.base_tree = [ - [C] - ] + [ - memo[base].mro() for base in C.__bases__ - ] + [ - list(C.__bases__) - ] - - self.bases_had_inconsistency = any( - base.had_inconsistency for base in base_resolvers - ) - - if len(C.__bases__) == 1: - self.__mro = [C] + memo[C.__bases__[0]].mro() - - @property - def had_inconsistency(self): - return self.direct_inconsistency or self.bases_had_inconsistency - - @property - def legacy_ro(self): - if self.__legacy_ro is None: - self.__legacy_ro = tuple(_legacy_ro(self.leaf)) - return list(self.__legacy_ro) - - TRACK_BAD_IRO = _ClassBoolFromEnv() - STRICT_IRO = _ClassBoolFromEnv() - WARN_BAD_IRO = _ClassBoolFromEnv() - LOG_CHANGED_IRO = _ClassBoolFromEnv() - USE_LEGACY_IRO = _ClassBoolFromEnv() - BAD_IROS = () - - def _warn_iro(self): - if not self.WARN_BAD_IRO: - # For the initial release, one must opt-in to see the warning. - # In the future (2021?) seeing at least the first warning will - # be the default - return - warnings.warn( - _INCONSISTENT_RESOLUTION_ORDER, - InconsistentResolutionOrderWarning, - ) - - @staticmethod - def _can_choose_base(base, base_tree_remaining): - # From C3: - # nothead = [s for s in nonemptyseqs if cand in s[1:]] - for bases in base_tree_remaining: - if not bases or bases[0] is base: - continue - - for b in bases: - if b is base: - return False - return True - - @staticmethod - def _nonempty_bases_ignoring(base_tree, ignoring): - return list(filter(None, [ - [b for b in bases if b is not ignoring] - for bases - in base_tree - ])) - - def _choose_next_base(self, base_tree_remaining): - """ - Return the next base. - - The return value will either fit the C3 constraints or be our best - guess about what to do. If we cannot guess, this may raise an - exception. - """ - base = self._find_next_C3_base(base_tree_remaining) - if base is not None: - return base - return self._guess_next_base(base_tree_remaining) - - def _find_next_C3_base(self, base_tree_remaining): - """Return the next base that fits the constraints - - Return ``None`` if there isn't one. - """ - for bases in base_tree_remaining: - base = bases[0] - if self._can_choose_base(base, base_tree_remaining): - return base - return None - - class _UseLegacyRO(Exception): - pass - - def _guess_next_base(self, base_tree_remaining): - # Narf. We may have an inconsistent order (we won't know for - # sure until we check all the bases). Python cannot create - # classes like this: - # - # class B1: - # pass - # class B2(B1): - # pass - # class C(B1, B2): # -> TypeError; this is like saying C(B1, B2, B1). - # pass - # - # However, older versions of zope.interface were fine with this order. - # A good example is ``providedBy(IOError())``. Because of the way - # ``classImplements`` works, it winds up with ``__bases__`` == - # ``[IEnvironmentError, IIOError, IOSError, ]`` (on Python 3). But ``IEnvironmentError`` is a base of - # both ``IIOError`` and ``IOSError``. Previously, we would get a - # resolution order of ``[IIOError, IOSError, IEnvironmentError, - # IStandardError, IException, Interface]`` but the standard Python - # algorithm would forbid creating that order entirely. - - # Unlike Python's MRO, we attempt to resolve the issue. A few - # heuristics have been tried. One was: - # - # Strip off the first (highest priority) base of each direct - # base one at a time and seeing if we can come to an agreement - # with the other bases. (We're trying for a partial ordering - # here.) This often resolves cases (such as the IOSError case - # above), and frequently produces the same ordering as the - # legacy MRO did. If we looked at all the highest priority - # bases and couldn't find any partial ordering, then we strip - # them *all* out and begin the C3 step again. We take care not - # to promote a common root over all others. - # - # If we only did the first part, stripped off the first - # element of the first item, we could resolve simple cases. - # But it tended to fail badly. If we did the whole thing, it - # could be extremely painful from a performance perspective - # for deep/wide things like Zope's OFS.SimpleItem.Item. Plus, - # anytime you get ExtensionClass.Base into the mix, you're - # likely to wind up in trouble, because it messes with the MRO - # of classes. Sigh. - # - # So now, we fall back to the old linearization (fast to compute). - self._warn_iro() - self.direct_inconsistency = InconsistentResolutionOrderError( - self, base_tree_remaining, - ) - raise self._UseLegacyRO - - def _merge(self): - # Returns a merged *list*. - result = self.__mro = [] - base_tree_remaining = self.base_tree - base = None - while 1: - # Take last picked base out of the base tree wherever it is. - # This differs slightly from the standard Python MRO and is needed - # because we have no other step that prevents duplicates - # from coming in (e.g., in the inconsistent fallback path) - base_tree_remaining = self._nonempty_bases_ignoring( - base_tree_remaining, base - ) - - if not base_tree_remaining: - return result - try: - base = self._choose_next_base(base_tree_remaining) - except self._UseLegacyRO: - self.__mro = self.legacy_ro - return self.legacy_ro - - result.append(base) - - def mro(self): - if self.__mro is None: - self.__mro = tuple(self._merge()) - return list(self.__mro) - - -class _StrictC3(C3): - __slots__ = () - - def _guess_next_base(self, base_tree_remaining): - raise InconsistentResolutionOrderError(self, base_tree_remaining) - - -class _TrackingC3(C3): - __slots__ = () - - def _guess_next_base(self, base_tree_remaining): - import traceback - bad_iros = C3.BAD_IROS - if self.leaf not in bad_iros: - if bad_iros == (): - import weakref - - # This is a race condition, but it doesn't matter much. - bad_iros = C3.BAD_IROS = weakref.WeakKeyDictionary() - bad_iros[self.leaf] = t = ( - InconsistentResolutionOrderError(self, base_tree_remaining), - traceback.format_stack() - ) - _logger().warning("Tracking inconsistent IRO: %s", t[0]) - return C3._guess_next_base(self, base_tree_remaining) - - -class _ROComparison: - # Exists to compute and print a pretty string comparison - # for differing ROs. - # Since we're used in a logging context, and may actually never be printed, - # this is a class so we can defer computing the diff until asked. - - # Components we use to build up the comparison report - class Item: - prefix = ' ' - - def __init__(self, item): - self.item = item - - def __str__(self): - return "{}{}".format( - self.prefix, - self.item, - ) - - class Deleted(Item): - prefix = '- ' - - class Inserted(Item): - prefix = '+ ' - - Empty = str - - class ReplacedBy: # pragma: no cover - prefix = '- ' - suffix = '' - - def __init__(self, chunk, total_count): - self.chunk = chunk - self.total_count = total_count - - def __iter__(self): - lines = [ - self.prefix + str(item) + self.suffix - for item in self.chunk - ] - while len(lines) < self.total_count: - lines.append('') - - return iter(lines) - - class Replacing(ReplacedBy): - prefix = "+ " - suffix = '' - - _c3_report = None - _legacy_report = None - - def __init__(self, c3, c3_ro, legacy_ro): - self.c3 = c3 - self.c3_ro = c3_ro - self.legacy_ro = legacy_ro - - def __move(self, from_, to_, chunk, operation): - for x in chunk: - to_.append(operation(x)) - from_.append(self.Empty()) - - def _generate_report(self): - if self._c3_report is None: - import difflib - - # The opcodes we get describe how to turn 'a' into 'b'. So - # the old one (legacy) needs to be first ('a') - matcher = difflib.SequenceMatcher(None, self.legacy_ro, self.c3_ro) - # The reports are equal length sequences. We're going for a - # side-by-side diff. - self._c3_report = c3_report = [] - self._legacy_report = legacy_report = [] - for opcode, leg1, leg2, c31, c32 in matcher.get_opcodes(): - c3_chunk = self.c3_ro[c31:c32] - legacy_chunk = self.legacy_ro[leg1:leg2] - - if opcode == 'equal': - # Guaranteed same length - c3_report.extend(self.Item(x) for x in c3_chunk) - legacy_report.extend(self.Item(x) for x in legacy_chunk) - if opcode == 'delete': - # Guaranteed same length - assert not c3_chunk - self.__move( - c3_report, legacy_report, legacy_chunk, self.Deleted, - ) - if opcode == 'insert': - # Guaranteed same length - assert not legacy_chunk - self.__move( - legacy_report, c3_report, c3_chunk, self.Inserted, - ) - if opcode == 'replace': # pragma: no cover - # (How do you make it output this?) - # Either side could be longer. - chunk_size = max(len(c3_chunk), len(legacy_chunk)) - c3_report.extend(self.Replacing(c3_chunk, chunk_size)) - legacy_report.extend( - self.ReplacedBy(legacy_chunk, chunk_size), - ) - - return self._c3_report, self._legacy_report - - @property - def _inconsistent_label(self): - inconsistent = [] - if self.c3.direct_inconsistency: - inconsistent.append('direct') - if self.c3.bases_had_inconsistency: - inconsistent.append('bases') - return '+'.join(inconsistent) if inconsistent else 'no' - - def __str__(self): - c3_report, legacy_report = self._generate_report() - assert len(c3_report) == len(legacy_report) - - left_lines = [str(x) for x in legacy_report] - right_lines = [str(x) for x in c3_report] - - # We have the same number of lines in the report; this is not - # necessarily the same as the number of items in either RO. - assert len(left_lines) == len(right_lines) - - padding = ' ' * 2 - max_left = max(len(x) for x in left_lines) - max_right = max(len(x) for x in right_lines) - - left_title = f'Legacy RO (len={len(self.legacy_ro)})' - - right_title = 'C3 RO (len={}; inconsistent={})'.format( - len(self.c3_ro), - self._inconsistent_label, - ) - lines = [ - ( - padding + - left_title.ljust(max_left) + - padding + - right_title.ljust(max_right) - ), - padding + '=' * (max_left + len(padding) + max_right) - ] - lines += [ - padding + left.ljust(max_left) + padding + right - for left, right in zip(left_lines, right_lines) - ] - - return '\n'.join(lines) - - -# Set to `Interface` once it is defined. This is used to -# avoid logging false positives about changed ROs. -_ROOT = None - - -def ro( - C, strict=None, base_mros=None, log_changed_ro=None, use_legacy_ro=None, -): - """ - ro(C) -> list - - Compute the precedence list (mro) according to C3. - - :return: A fresh `list` object. - - .. versionchanged:: 5.0.0 - Add the *strict*, *log_changed_ro* and *use_legacy_ro* - keyword arguments. These are provisional and likely to be - removed in the future. They are most useful for testing. - """ - # The ``base_mros`` argument is for internal optimization and - # not documented. - resolver = C3.resolver(C, strict, base_mros) - mro = resolver.mro() - - log_changed = ( - log_changed_ro if log_changed_ro is not None - else resolver.LOG_CHANGED_IRO - ) - use_legacy = ( - use_legacy_ro if use_legacy_ro is not None - else resolver.USE_LEGACY_IRO - ) - - if log_changed or use_legacy: - legacy_ro = resolver.legacy_ro - assert isinstance(legacy_ro, list) - assert isinstance(mro, list) - changed = legacy_ro != mro - if changed: - # Did only Interface move? The fix for issue #8 made that - # somewhat common. It's almost certainly not a problem, though, - # so allow ignoring it. - legacy_without_root = [x for x in legacy_ro if x is not _ROOT] - mro_without_root = [x for x in mro if x is not _ROOT] - changed = legacy_without_root != mro_without_root - - if changed: - comparison = _ROComparison(resolver, mro, legacy_ro) - _logger().warning( - "Object %r has different legacy and C3 MROs:\n%s", - C, comparison - ) - if resolver.had_inconsistency and legacy_ro == mro: - comparison = _ROComparison(resolver, mro, legacy_ro) - _logger().warning( - "Object %r had inconsistent IRO and used the legacy RO:\n%s" - "\nInconsistency entered at:\n%s", - C, comparison, resolver.direct_inconsistency - ) - if use_legacy: - return legacy_ro - - return mro - - -def is_consistent(C): - """Is the resolution order for *C*, consistent according to C3. - - Order as computed by :func:`ro`. - """ - return not C3.resolver(C, False, None).had_inconsistency diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__init__.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__init__.py deleted file mode 100644 index c5fe924..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__init__.py +++ /dev/null @@ -1,130 +0,0 @@ -from zope.interface._compat import _should_attempt_c_optimizations - - -class OptimizationTestMixin: - """Mixin testing that C optimizations are used when appropriate. - """ - - def _getTargetClass(self): - """Return the implementation in use, without 'Py' or 'Fallback' suffix. - """ - raise NotImplementedError - - def _getFallbackClass(self): - """Return the fallback Python implementation. - """ - # Is there an algorithmic way to do this? The C - # objects all come from the same module so I don't see how we can - # get the Python object from that. - raise NotImplementedError - - def test_optimizations(self): - used = self._getTargetClass() - fallback = self._getFallbackClass() - - if _should_attempt_c_optimizations(): - self.assertIsNot(used, fallback) - else: - self.assertIs(used, fallback) - - -class SubclassableMixin: - - def _getTargetClass(self): - """Return the implementation in use without 'Py' or 'Fallback' suffix. - """ - raise NotImplementedError - - def test_can_subclass(self): - klass = self._getTargetClass() - - class Derived(klass): # no raise - pass - - -class MissingSomeAttrs: - """ - Helper for tests that raises a specific exception - for attributes that are missing. This is usually not - an AttributeError, and this object is used to test that - those errors are not improperly caught and treated like - an AttributeError. - """ - - def __init__(self, exc_kind, **other_attrs): - self.__exc_kind = exc_kind - d = object.__getattribute__(self, '__dict__') - d.update(other_attrs) - - def __getattribute__(self, name): - # Note that we ignore objects found in the class dictionary. - d = object.__getattribute__(self, '__dict__') - try: - return d[name] - except KeyError: - raise d['_MissingSomeAttrs__exc_kind'](name) - - EXCEPTION_CLASSES = ( - TypeError, - RuntimeError, - BaseException, - ValueError, - ) - - @classmethod - def test_raises(cls, unittest, test_func, expected_missing, **other_attrs): - """ - Loop through various exceptions, calling *test_func* inside a - ``assertRaises`` block. - - :param test_func: A callable of one argument, the instance of this - class. - :param str expected_missing: The attribute that should fail with the - exception. This is used to ensure that we're testing the path we - think we are. - :param other_attrs: Attributes that should be provided on the test - object. Must not contain *expected_missing*. - """ - assert isinstance(expected_missing, str) - assert expected_missing not in other_attrs - for exc in cls.EXCEPTION_CLASSES: - ob = cls(exc, **other_attrs) - with unittest.assertRaises(exc) as ex: - test_func(ob) - - unittest.assertEqual(ex.exception.args[0], expected_missing) - - # Now test that the AttributeError for that expected_missing is *not* - # raised. - ob = cls(AttributeError, **other_attrs) - try: - test_func(ob) - except AttributeError as e: - unittest.assertNotIn(expected_missing, str(e)) - except Exception: # pylint:disable=broad-except - pass - -# Be sure cleanup functionality is available; classes that use the adapter hook -# need to be sure to subclass ``CleanUp``. -# -# If zope.component is installed and imported when we run our tests -# (import chain: -# zope.testrunner->zope.security->zope.location->zope.component.api) -# it adds an adapter hook that uses its global site manager. That can cause -# leakage from one test to another unless its cleanup hooks are run. The -# symptoms can be odd, especially if one test used C objects and the next used -# the Python implementation. (For example, you can get strange TypeErrors or -# find inexplicable comparisons being done.) - - -try: - from zope.testing import cleanup -except ImportError: - - class CleanUp: - def cleanUp(self): - pass - - setUp = tearDown = cleanUp -else: - CleanUp = cleanup.CleanUp diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/__init__.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 618faf7..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/advisory_testing.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/advisory_testing.cpython-38.pyc deleted file mode 100644 index 502a6f1..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/advisory_testing.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/dummy.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/dummy.cpython-38.pyc deleted file mode 100644 index bb0f7c3..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/dummy.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/idummy.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/idummy.cpython-38.pyc deleted file mode 100644 index 86bf6cc..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/idummy.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/m1.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/m1.cpython-38.pyc deleted file mode 100644 index 1a8426e..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/m1.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/odd.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/odd.cpython-38.pyc deleted file mode 100644 index 57bd7b2..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/odd.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_adapter.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_adapter.cpython-38.pyc deleted file mode 100644 index ff98ed5..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_adapter.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_advice.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_advice.cpython-38.pyc deleted file mode 100644 index 10e4b8f..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_advice.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_compile_flags.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_compile_flags.cpython-38.pyc deleted file mode 100644 index 854d723..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_compile_flags.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_declarations.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_declarations.cpython-38.pyc deleted file mode 100644 index 87e91ee..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_declarations.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_document.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_document.cpython-38.pyc deleted file mode 100644 index e9037eb..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_document.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_element.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_element.cpython-38.pyc deleted file mode 100644 index 684495d..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_element.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_exceptions.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_exceptions.cpython-38.pyc deleted file mode 100644 index d37b112..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_exceptions.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_interface.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_interface.cpython-38.pyc deleted file mode 100644 index 5a8465e..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_interface.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_interfaces.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_interfaces.cpython-38.pyc deleted file mode 100644 index 7f2fd94..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_interfaces.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_odd_declarations.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_odd_declarations.cpython-38.pyc deleted file mode 100644 index da36823..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_odd_declarations.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_registry.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_registry.cpython-38.pyc deleted file mode 100644 index 0c7c311..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_registry.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_ro.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_ro.cpython-38.pyc deleted file mode 100644 index 234d583..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_ro.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_sorting.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_sorting.cpython-38.pyc deleted file mode 100644 index 9130e3a..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_sorting.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_verify.cpython-38.pyc b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_verify.cpython-38.pyc deleted file mode 100644 index 76041b0..0000000 Binary files a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/__pycache__/test_verify.cpython-38.pyc and /dev/null differ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/advisory_testing.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/advisory_testing.py deleted file mode 100644 index 9376955..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/advisory_testing.py +++ /dev/null @@ -1,29 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -import sys - -from zope.interface.advice import getFrameInfo - - -my_globals = globals() - -ClassicClass = None - - -class NewStyleClass: - __metaclass__ = type - classLevelFrameInfo = getFrameInfo(sys._getframe()) - - -moduleLevelFrameInfo = getFrameInfo(sys._getframe()) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/dummy.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/dummy.py deleted file mode 100644 index 8ec85f8..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/dummy.py +++ /dev/null @@ -1,25 +0,0 @@ -############################################################################## -# -# Copyright (c) 2001, 2002 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -""" Dummy Module -""" -from zope.interface import moduleProvides -from zope.interface.tests.idummy import IDummyModule - - -moduleProvides(IDummyModule) - - -def bar(baz): - # Note: no 'self', because the module provides the interface directly. - raise NotImplementedError() diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/idummy.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/idummy.py deleted file mode 100644 index 7855ce7..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/idummy.py +++ /dev/null @@ -1,24 +0,0 @@ -############################################################################## -# -# Copyright (c) 2001, 2002 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -""" Interface describing API of zope.interface.tests.dummy test module -""" -from zope.interface import Interface - - -class IDummyModule(Interface): - """ Dummy interface for unit tests. - """ - def bar(baz): - """ Just a note. - """ diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/m1.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/m1.py deleted file mode 100644 index 8998e46..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/m1.py +++ /dev/null @@ -1,28 +0,0 @@ -############################################################################## -# -# Copyright (c) 2004 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Test module that declares an interface -""" -from zope.interface import Interface -from zope.interface import moduleProvides - - -class I1(Interface): - pass - - -class I2(Interface): - pass - - -moduleProvides(I1, I2) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/odd.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/odd.py deleted file mode 100644 index b385beb..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/odd.py +++ /dev/null @@ -1,130 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Odd meta class that doesn't subclass type. - -This is used for testing support for ExtensionClass in new interfaces. - - >>> class A(object): - ... __metaclass__ = MetaClass - ... a = 1 - ... - >>> A.__name__ - 'A' - >>> A.__bases__ == (object,) - True - >>> class B(object): - ... __metaclass__ = MetaClass - ... b = 1 - ... - >>> class C(A, B): pass - ... - >>> C.__name__ - 'C' - >>> int(C.__bases__ == (A, B)) - 1 - >>> a = A() - >>> aa = A() - >>> a.a - 1 - >>> aa.a - 1 - >>> aa.a = 2 - >>> a.a - 1 - >>> aa.a - 2 - >>> c = C() - >>> c.a - 1 - >>> c.b - 1 - >>> c.b = 2 - >>> c.b - 2 - >>> C.c = 1 - >>> c.c - 1 - - >>> int(C.__class__.__class__ is C.__class__) - 1 -""" - -# class OddClass is an odd meta class - - -class MetaMetaClass(type): - - def __getattribute__(cls, name): - if name == '__class__': - return cls - # Under Python 3.6, __prepare__ gets requested - return type.__getattribute__(cls, name) - - -class MetaClass: - """Odd classes - """ - - def __init__(self, name, bases, dict): - self.__name__ = name - self.__bases__ = bases - self.__dict__.update(dict) - - def __call__(self): - return OddInstance(self) - - def __getattr__(self, name): - for b in self.__bases__: - v = getattr(b, name, self) - if v is not self: - return v - raise AttributeError(name) - - def __repr__(self): # pragma: no cover - return f"" - - -MetaClass = MetaMetaClass( - 'MetaClass', - MetaClass.__bases__, - { - k: v for k, v in MetaClass.__dict__.items() - if k not in ('__dict__',) - } -) - - -class OddInstance: - - def __init__(self, cls): - self.__dict__['__class__'] = cls - - def __getattribute__(self, name): - dict = object.__getattribute__(self, '__dict__') - if name == '__dict__': - return dict - v = dict.get(name, self) - if v is not self: - return v - return getattr(dict['__class__'], name) - - def __setattr__(self, name, v): - self.__dict__[name] = v - - def __delattr__(self, name): - raise NotImplementedError() - - def __repr__(self): # pragma: no cover - return "".format( - self.__class__.__name__, hex(id(self))) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_adapter.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_adapter.py deleted file mode 100644 index ea90732..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_adapter.py +++ /dev/null @@ -1,2315 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Adapter registry tests -""" -import unittest - -from zope.interface.tests import OptimizationTestMixin - - -# pylint:disable=inherit-non-class,protected-access,too-many-lines -# pylint:disable=attribute-defined-outside-init,blacklisted-name - -def _makeInterfaces(): - from zope.interface import Interface - - class IB0(Interface): - pass - - class IB1(IB0): - pass - - class IB2(IB0): - pass - - class IB3(IB2, IB1): - pass - - class IB4(IB1, IB2): - pass - - class IF0(Interface): - pass - - class IF1(IF0): - pass - - class IR0(Interface): - pass - - class IR1(IR0): - pass - - return IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1 - - -# Custom types to use as part of the AdapterRegistry data structures. -# Our custom types do strict type checking to make sure -# types propagate through the data tree as expected. -class CustomDataTypeBase: - _data = None - - def __getitem__(self, name): - return self._data[name] - - def __setitem__(self, name, value): - self._data[name] = value - - def __delitem__(self, name): - del self._data[name] - - def __len__(self): - return len(self._data) - - def __contains__(self, name): - return name in self._data - - def __eq__(self, other): - if other is self: - return True - # pylint:disable=unidiomatic-typecheck - if type(other) is not type(self): - return False - return other._data == self._data - - def __repr__(self): - return repr(self._data) - - -class CustomMapping(CustomDataTypeBase): - def __init__(self, other=None): - self._data = {} - if other: - self._data.update(other) - self.get = self._data.get - self.items = self._data.items - - -class CustomSequence(CustomDataTypeBase): - def __init__(self, other=None): - self._data = [] - if other: - self._data.extend(other) - self.append = self._data.append - - -class CustomLeafSequence(CustomSequence): - pass - - -class CustomProvided(CustomMapping): - pass - - -class BaseAdapterRegistryTests(unittest.TestCase): - - maxDiff = None - - def _getBaseAdapterRegistry(self): - from zope.interface.adapter import BaseAdapterRegistry - return BaseAdapterRegistry - - def _getTargetClass(self): - BaseAdapterRegistry = self._getBaseAdapterRegistry() - - class _CUT(BaseAdapterRegistry): - - class LookupClass: - _changed = _extendors = () - - def __init__(self, reg): - pass - - def changed(self, orig): - self._changed += (orig,) - - def add_extendor(self, provided): - self._extendors += (provided,) - - def remove_extendor(self, provided): - self._extendors = tuple([x for x in self._extendors - if x != provided]) - - for name in BaseAdapterRegistry._delegated: - setattr(_CUT.LookupClass, name, object()) - return _CUT - - def _makeOne(self): - return self._getTargetClass()() - - def _getMappingType(self): - return dict - - def _getProvidedType(self): - return dict - - def _getMutableListType(self): - return list - - def _getLeafSequenceType(self): - return tuple - - def test_lookup_delegation(self): - CUT = self._getTargetClass() - registry = CUT() - for name in CUT._delegated: - self.assertIs( - getattr(registry, name), getattr(registry._v_lookup, name) - ) - - def test__generation_on_first_creation(self): - registry = self._makeOne() - # Bumped to 1 in BaseAdapterRegistry.__init__ - self.assertEqual(registry._generation, 1) - - def test__generation_after_calling_changed(self): - registry = self._makeOne() - orig = object() - registry.changed(orig) - # Bumped to 1 in BaseAdapterRegistry.__init__ - self.assertEqual(registry._generation, 2) - self.assertEqual(registry._v_lookup._changed, (registry, orig,)) - - def test__generation_after_changing___bases__(self): - class _Base: - pass - registry = self._makeOne() - registry.__bases__ = (_Base,) - self.assertEqual(registry._generation, 2) - - def _check_basic_types_of_adapters(self, registry, expected_order=2): - self.assertEqual( - len(registry._adapters), expected_order, - ) # order 0 and order 1 - self.assertIsInstance(registry._adapters, self._getMutableListType()) - MT = self._getMappingType() - for mapping in registry._adapters: - self.assertIsInstance(mapping, MT) - self.assertEqual(registry._adapters[0], MT()) - self.assertIsInstance(registry._adapters[1], MT) - self.assertEqual(len(registry._adapters[expected_order - 1]), 1) - - def _check_basic_types_of_subscribers(self, registry, expected_order=2): - self.assertEqual( - len(registry._subscribers), expected_order, - ) # order 0 and order 1 - self.assertIsInstance( - registry._subscribers, self._getMutableListType(), - ) - MT = self._getMappingType() - for mapping in registry._subscribers: - self.assertIsInstance(mapping, MT) - if expected_order: - self.assertEqual(registry._subscribers[0], MT()) - self.assertIsInstance(registry._subscribers[1], MT) - self.assertEqual(len(registry._subscribers[expected_order - 1]), 1) - - def test_register(self): - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - registry.register([IB0], IR0, '', 'A1') - self.assertEqual(registry.registered([IB0], IR0, ''), 'A1') - self.assertEqual(registry._generation, 2) - self._check_basic_types_of_adapters(registry) - MT = self._getMappingType() - self.assertEqual(registry._adapters[1], MT({ - IB0: MT({ - IR0: MT({'': 'A1'}) - }) - })) - PT = self._getProvidedType() - self.assertEqual(registry._provided, PT({ - IR0: 1 - })) - - registered = list(registry.allRegistrations()) - self.assertEqual(registered, [( - (IB0,), # required - IR0, # provided - '', # name - 'A1' # value - )]) - - def test_register_multiple_allRegistrations(self): - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - # Use several different depths and several different names - registry.register([], IR0, '', 'A1') - registry.register([], IR0, 'name1', 'A2') - - registry.register([IB0], IR0, '', 'A1') - registry.register([IB0], IR0, 'name2', 'A2') - registry.register([IB0], IR1, '', 'A3') - registry.register([IB0], IR1, 'name3', 'A4') - - registry.register([IB0, IB1], IR0, '', 'A1') - registry.register([IB0, IB2], IR0, 'name2', 'A2') - registry.register([IB0, IB2], IR1, 'name4', 'A4') - registry.register([IB0, IB3], IR1, '', 'A3') - - def build_adapters(L, MT): - return L([ - # 0 - MT({ - IR0: MT({ - '': 'A1', - 'name1': 'A2' - }) - }), - # 1 - MT({ - IB0: MT({ - IR0: MT({ - '': 'A1', - 'name2': 'A2' - }), - IR1: MT({ - '': 'A3', - 'name3': 'A4' - }) - }) - }), - # 3 - MT({ - IB0: MT({ - IB1: MT({ - IR0: MT({'': 'A1'}) - }), - IB2: MT({ - IR0: MT({'name2': 'A2'}), - IR1: MT({'name4': 'A4'}), - }), - IB3: MT({ - IR1: MT({'': 'A3'}) - }) - }), - }), - ]) - - self.assertEqual(registry._adapters, - build_adapters(L=self._getMutableListType(), - MT=self._getMappingType())) - - registered = sorted(registry.allRegistrations()) - self.assertEqual(registered, [ - ((), IR0, '', 'A1'), - ((), IR0, 'name1', 'A2'), - ((IB0,), IR0, '', 'A1'), - ((IB0,), IR0, 'name2', 'A2'), - ((IB0,), IR1, '', 'A3'), - ((IB0,), IR1, 'name3', 'A4'), - ((IB0, IB1), IR0, '', 'A1'), - ((IB0, IB2), IR0, 'name2', 'A2'), - ((IB0, IB2), IR1, 'name4', 'A4'), - ((IB0, IB3), IR1, '', 'A3') - ]) - - # We can duplicate to another object. - registry2 = self._makeOne() - for args in registered: - registry2.register(*args) - - self.assertEqual(registry2._adapters, registry._adapters) - self.assertEqual(registry2._provided, registry._provided) - - # We can change the types and rebuild the data structures. - registry._mappingType = CustomMapping - registry._leafSequenceType = CustomLeafSequence - registry._sequenceType = CustomSequence - registry._providedType = CustomProvided - - def addValue(existing, new): - existing = ( - existing if existing is not None else CustomLeafSequence() - ) - existing.append(new) - return existing - - registry._addValueToLeaf = addValue - - registry.rebuild() - - self.assertEqual(registry._adapters, - build_adapters( - L=CustomSequence, - MT=CustomMapping - )) - - def test_register_with_invalid_name(self): - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - with self.assertRaises(ValueError): - registry.register([IB0], IR0, object(), 'A1') - - def test_register_with_value_None_unregisters(self): - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - registry.register([None], IR0, '', 'A1') - registry.register([None], IR0, '', None) - self.assertEqual(len(registry._adapters), 0) - self.assertIsInstance(registry._adapters, self._getMutableListType()) - registered = list(registry.allRegistrations()) - self.assertEqual(registered, []) - - def test_register_with_same_value(self): - from zope.interface import Interface - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - _value = object() - registry.register([None], IR0, '', _value) - _before = registry._generation - registry.register([None], IR0, '', _value) - self.assertEqual(registry._generation, _before) # skipped changed() - self._check_basic_types_of_adapters(registry) - MT = self._getMappingType() - self.assertEqual(registry._adapters[1], MT( - { - Interface: MT( - { - IR0: MT({'': _value}) - } - ) - } - )) - registered = list(registry.allRegistrations()) - self.assertEqual(registered, [( - (Interface,), # required - IR0, # provided - '', # name - _value # value - )]) - - def test_registered_empty(self): - registry = self._makeOne() - self.assertEqual(registry.registered([None], None, ''), None) - registered = list(registry.allRegistrations()) - self.assertEqual(registered, []) - - def test_registered_non_empty_miss(self): - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - registry.register([IB1], None, '', 'A1') - self.assertEqual(registry.registered([IB2], None, ''), None) - - def test_registered_non_empty_hit(self): - registry = self._makeOne() - registry.register([None], None, '', 'A1') - self.assertEqual(registry.registered([None], None, ''), 'A1') - - def test_unregister_empty(self): - registry = self._makeOne() - registry.unregister([None], None, '') # doesn't raise - self.assertEqual(registry.registered([None], None, ''), None) - self.assertEqual(len(registry._provided), 0) - - def test_unregister_non_empty_miss_on_required(self): - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - registry.register([IB1], None, '', 'A1') - registry.unregister([IB2], None, '') # doesn't raise - self.assertEqual(registry.registered([IB1], None, ''), 'A1') - self._check_basic_types_of_adapters(registry) - MT = self._getMappingType() - self.assertEqual(registry._adapters[1], MT( - { - IB1: MT( - { - None: MT({'': 'A1'}) - } - ) - } - )) - PT = self._getProvidedType() - self.assertEqual(registry._provided, PT({ - None: 1 - })) - - def test_unregister_non_empty_miss_on_name(self): - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - registry.register([IB1], None, '', 'A1') - registry.unregister([IB1], None, 'nonesuch') # doesn't raise - self.assertEqual(registry.registered([IB1], None, ''), 'A1') - self._check_basic_types_of_adapters(registry) - MT = self._getMappingType() - self.assertEqual(registry._adapters[1], MT( - { - IB1: MT( - { - None: MT({'': 'A1'}) - } - ) - } - )) - PT = self._getProvidedType() - self.assertEqual(registry._provided, PT({ - None: 1 - })) - - def test_unregister_with_value_not_None_miss(self): - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - orig = object() - nomatch = object() - registry.register([IB1], None, '', orig) - registry.unregister([IB1], None, '', nomatch) # doesn't raise - self.assertIs(registry.registered([IB1], None, ''), orig) - - def test_unregister_hit_clears_empty_subcomponents(self): - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - one = object() - another = object() - registry.register([IB1, IB2], None, '', one) - registry.register([IB1, IB3], None, '', another) - self._check_basic_types_of_adapters(registry, expected_order=3) - self.assertIn(IB2, registry._adapters[2][IB1]) - self.assertIn(IB3, registry._adapters[2][IB1]) - MT = self._getMappingType() - self.assertEqual(registry._adapters[2], MT( - { - IB1: MT( - { - IB2: MT({None: MT({'': one})}), - IB3: MT({None: MT({'': another})}) - } - ) - } - )) - PT = self._getProvidedType() - self.assertEqual(registry._provided, PT({ - None: 2 - })) - - registry.unregister([IB1, IB3], None, '', another) - self.assertIn(IB2, registry._adapters[2][IB1]) - self.assertNotIn(IB3, registry._adapters[2][IB1]) - self.assertEqual(registry._adapters[2], MT( - { - IB1: MT( - { - IB2: MT({None: MT({'': one})}), - } - ) - } - )) - self.assertEqual(registry._provided, PT({ - None: 1 - })) - - def test_unsubscribe_empty(self): - registry = self._makeOne() - registry.unsubscribe([None], None, '') # doesn't raise - self.assertEqual(registry.registered([None], None, ''), None) - self._check_basic_types_of_subscribers(registry, expected_order=0) - - def test_unsubscribe_hit(self): - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - orig = object() - registry.subscribe([IB1], None, orig) - MT = self._getMappingType() - L = self._getLeafSequenceType() - PT = self._getProvidedType() - self._check_basic_types_of_subscribers(registry) - self.assertEqual(registry._subscribers[1], MT({ - IB1: MT({ - None: MT({ - '': L((orig,)) - }) - }) - })) - self.assertEqual(registry._provided, PT({})) - registry.unsubscribe([IB1], None, orig) # doesn't raise - self.assertEqual(len(registry._subscribers), 0) - self.assertEqual(registry._provided, PT({})) - - def assertLeafIdentity(self, leaf1, leaf2): - """ - Implementations may choose to use new, immutable objects - instead of mutating existing subscriber leaf objects, or vice versa. - - The default implementation uses immutable tuples, so they are never - the same. Other implementations may use persistent lists so they - should be the same and mutated in place. Subclasses testing this - behaviour need to override this method. - """ - self.assertIsNot(leaf1, leaf2) - - def test_unsubscribe_after_multiple(self): - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - first = object() - second = object() - third = object() - fourth = object() - registry.subscribe([IB1], None, first) - registry.subscribe([IB1], None, second) - registry.subscribe([IB1], IR0, third) - registry.subscribe([IB1], IR0, fourth) - self._check_basic_types_of_subscribers(registry, expected_order=2) - MT = self._getMappingType() - L = self._getLeafSequenceType() - PT = self._getProvidedType() - self.assertEqual(registry._subscribers[1], MT({ - IB1: MT({ - None: MT({'': L((first, second))}), - IR0: MT({'': L((third, fourth))}), - }) - })) - self.assertEqual(registry._provided, PT({ - IR0: 2 - })) - # The leaf objects may or may not stay the same as they are - # unsubscribed, depending on the implementation - IR0_leaf_orig = registry._subscribers[1][IB1][IR0][''] - Non_leaf_orig = registry._subscribers[1][IB1][None][''] - - registry.unsubscribe([IB1], None, first) - registry.unsubscribe([IB1], IR0, third) - - self.assertEqual(registry._subscribers[1], MT({ - IB1: MT({ - None: MT({'': L((second,))}), - IR0: MT({'': L((fourth,))}), - }) - })) - self.assertEqual(registry._provided, PT({ - IR0: 1 - })) - IR0_leaf_new = registry._subscribers[1][IB1][IR0][''] - Non_leaf_new = registry._subscribers[1][IB1][None][''] - - self.assertLeafIdentity(IR0_leaf_orig, IR0_leaf_new) - self.assertLeafIdentity(Non_leaf_orig, Non_leaf_new) - - registry.unsubscribe([IB1], None, second) - registry.unsubscribe([IB1], IR0, fourth) - self.assertEqual(len(registry._subscribers), 0) - self.assertEqual(len(registry._provided), 0) - - def test_subscribe_unsubscribe_identical_objects_provided(self): - # https://github.com/zopefoundation/zope.interface/issues/227 - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - first = object() - registry.subscribe([IB1], IR0, first) - registry.subscribe([IB1], IR0, first) - - MT = self._getMappingType() - L = self._getLeafSequenceType() - PT = self._getProvidedType() - self.assertEqual(registry._subscribers[1], MT({ - IB1: MT({ - IR0: MT({'': L((first, first))}), - }) - })) - self.assertEqual(registry._provided, PT({ - IR0: 2 - })) - - registry.unsubscribe([IB1], IR0, first) - registry.unsubscribe([IB1], IR0, first) - self.assertEqual(len(registry._subscribers), 0) - self.assertEqual(registry._provided, PT()) - - def test_subscribe_unsubscribe_nonequal_objects_provided(self): - # https://github.com/zopefoundation/zope.interface/issues/227 - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - first = object() - second = object() - registry.subscribe([IB1], IR0, first) - registry.subscribe([IB1], IR0, second) - - MT = self._getMappingType() - L = self._getLeafSequenceType() - PT = self._getProvidedType() - self.assertEqual(registry._subscribers[1], MT({ - IB1: MT({ - IR0: MT({'': L((first, second))}), - }) - })) - self.assertEqual(registry._provided, PT({ - IR0: 2 - })) - - registry.unsubscribe([IB1], IR0, first) - registry.unsubscribe([IB1], IR0, second) - self.assertEqual(len(registry._subscribers), 0) - self.assertEqual(registry._provided, PT()) - - def test_subscribed_empty(self): - registry = self._makeOne() - self.assertIsNone(registry.subscribed([None], None, '')) - subscribed = list(registry.allSubscriptions()) - self.assertEqual(subscribed, []) - - def test_subscribed_non_empty_miss(self): - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - registry.subscribe([IB1], IF0, 'A1') - # Mismatch required - self.assertIsNone(registry.subscribed([IB2], IF0, '')) - # Mismatch provided - self.assertIsNone(registry.subscribed([IB1], IF1, '')) - # Mismatch value - self.assertIsNone(registry.subscribed([IB1], IF0, '')) - - def test_subscribed_non_empty_hit(self): - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - registry.subscribe([IB0], IF0, 'A1') - self.assertEqual(registry.subscribed([IB0], IF0, 'A1'), 'A1') - - def test_unsubscribe_w_None_after_multiple(self): - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - first = object() - second = object() - - registry.subscribe([IB1], None, first) - registry.subscribe([IB1], None, second) - self._check_basic_types_of_subscribers(registry, expected_order=2) - registry.unsubscribe([IB1], None) - self.assertEqual(len(registry._subscribers), 0) - - def test_unsubscribe_non_empty_miss_on_required(self): - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - registry.subscribe([IB1], None, 'A1') - self._check_basic_types_of_subscribers(registry, expected_order=2) - registry.unsubscribe([IB2], None, '') # doesn't raise - self.assertEqual(len(registry._subscribers), 2) - MT = self._getMappingType() - L = self._getLeafSequenceType() - self.assertEqual(registry._subscribers[1], MT({ - IB1: MT({ - None: MT({'': L(('A1',))}), - }) - })) - - def test_unsubscribe_non_empty_miss_on_value(self): - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - registry.subscribe([IB1], None, 'A1') - self._check_basic_types_of_subscribers(registry, expected_order=2) - registry.unsubscribe([IB1], None, 'A2') # doesn't raise - self.assertEqual(len(registry._subscribers), 2) - MT = self._getMappingType() - L = self._getLeafSequenceType() - self.assertEqual(registry._subscribers[1], MT({ - IB1: MT({ - None: MT({'': L(('A1',))}), - }) - })) - - def test_unsubscribe_with_value_not_None_miss(self): - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - orig = object() - nomatch = object() - registry.subscribe([IB1], None, orig) - registry.unsubscribe([IB1], None, nomatch) # doesn't raise - self.assertEqual(len(registry._subscribers), 2) - - def _instance_method_notify_target(self): - self.fail("Example method, not intended to be called.") - - def test_unsubscribe_instance_method(self): - # Checking that the values are compared by equality, not identity - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - self.assertEqual(len(registry._subscribers), 0) - registry.subscribe([IB1], None, self._instance_method_notify_target) - registry.unsubscribe([IB1], None, self._instance_method_notify_target) - self.assertEqual(len(registry._subscribers), 0) - - def test_subscribe_multiple_allRegistrations(self): - ( - IB0, IB1, IB2, IB3, IB4, IF0, IF1, IR0, IR1, - ) = _makeInterfaces() # pylint:disable=unused-variable - registry = self._makeOne() - # Use several different depths and several different values - registry.subscribe([], IR0, 'A1') - registry.subscribe([], IR0, 'A2') - - registry.subscribe([IB0], IR0, 'A1') - registry.subscribe([IB0], IR0, 'A2') - registry.subscribe([IB0], IR1, 'A3') - registry.subscribe([IB0], IR1, 'A4') - - registry.subscribe([IB0, IB1], IR0, 'A1') - registry.subscribe([IB0, IB2], IR0, 'A2') - registry.subscribe([IB0, IB2], IR1, 'A4') - registry.subscribe([IB0, IB3], IR1, 'A3') - - def build_subscribers(L, F, MT): - return L([ - # 0 - MT({ - IR0: MT({ - '': F(['A1', 'A2']) - }) - }), - # 1 - MT({ - IB0: MT({ - IR0: MT({ - '': F(['A1', 'A2']) - }), - IR1: MT({ - '': F(['A3', 'A4']) - }) - }) - }), - # 3 - MT({ - IB0: MT({ - IB1: MT({ - IR0: MT({'': F(['A1'])}) - }), - IB2: MT({ - IR0: MT({'': F(['A2'])}), - IR1: MT({'': F(['A4'])}), - }), - IB3: MT({ - IR1: MT({'': F(['A3'])}) - }) - }), - }), - ]) - - self.assertEqual(registry._subscribers, - build_subscribers( - L=self._getMutableListType(), - F=self._getLeafSequenceType(), - MT=self._getMappingType() - )) - - def build_provided(P): - return P({ - IR0: 6, - IR1: 4, - }) - - self.assertEqual(registry._provided, - build_provided(P=self._getProvidedType())) - - registered = sorted(registry.allSubscriptions()) - self.assertEqual(registered, [ - ((), IR0, 'A1'), - ((), IR0, 'A2'), - ((IB0,), IR0, 'A1'), - ((IB0,), IR0, 'A2'), - ((IB0,), IR1, 'A3'), - ((IB0,), IR1, 'A4'), - ((IB0, IB1), IR0, 'A1'), - ((IB0, IB2), IR0, 'A2'), - ((IB0, IB2), IR1, 'A4'), - ((IB0, IB3), IR1, 'A3') - ]) - - # We can duplicate this to another object - registry2 = self._makeOne() - for args in registered: - registry2.subscribe(*args) - - self.assertEqual(registry2._subscribers, registry._subscribers) - self.assertEqual(registry2._provided, registry._provided) - - # We can change the types and rebuild the data structures. - registry._mappingType = CustomMapping - registry._leafSequenceType = CustomLeafSequence - registry._sequenceType = CustomSequence - registry._providedType = CustomProvided - - def addValue(existing, new): - existing = ( - existing if existing is not None else CustomLeafSequence() - ) - existing.append(new) - return existing - - registry._addValueToLeaf = addValue - - registry.rebuild() - - self.assertEqual(registry._subscribers, - build_subscribers( - L=CustomSequence, - F=CustomLeafSequence, - MT=CustomMapping - )) - - -class CustomTypesBaseAdapterRegistryTests(BaseAdapterRegistryTests): - """ - This class may be extended by other packages to test their own - adapter registries that use custom types. (So be cautious about - breaking changes.) - - One known user is ``zope.component.persistentregistry``. - """ - - def _getMappingType(self): - return CustomMapping - - def _getProvidedType(self): - return CustomProvided - - def _getMutableListType(self): - return CustomSequence - - def _getLeafSequenceType(self): - return CustomLeafSequence - - def _getBaseAdapterRegistry(self): - from zope.interface.adapter import BaseAdapterRegistry - - class CustomAdapterRegistry(BaseAdapterRegistry): - _mappingType = self._getMappingType() - _sequenceType = self._getMutableListType() - _leafSequenceType = self._getLeafSequenceType() - _providedType = self._getProvidedType() - - def _addValueToLeaf(self, existing_leaf_sequence, new_item): - if not existing_leaf_sequence: - existing_leaf_sequence = self._leafSequenceType() - existing_leaf_sequence.append(new_item) - return existing_leaf_sequence - - def _removeValueFromLeaf(self, existing_leaf_sequence, to_remove): - without_removed = BaseAdapterRegistry._removeValueFromLeaf( - self, - existing_leaf_sequence, - to_remove) - existing_leaf_sequence[:] = without_removed - assert to_remove not in existing_leaf_sequence - return existing_leaf_sequence - - return CustomAdapterRegistry - - def assertLeafIdentity(self, leaf1, leaf2): - self.assertIs(leaf1, leaf2) - - -class LookupBaseFallbackTests(unittest.TestCase): - - def _getFallbackClass(self): - from zope.interface.adapter import LookupBaseFallback - return LookupBaseFallback - - _getTargetClass = _getFallbackClass - - def _makeOne( - self, uc_lookup=None, uc_lookupAll=None, uc_subscriptions=None, - ): - # pylint:disable=function-redefined - if uc_lookup is None: - - def uc_lookup(self, required, provided, name): - pass - - if uc_lookupAll is None: - - def uc_lookupAll(self, required, provided): - raise NotImplementedError() - - if uc_subscriptions is None: - - def uc_subscriptions(self, required, provided): - raise NotImplementedError() - - class Derived(self._getTargetClass()): - _uncached_lookup = uc_lookup - _uncached_lookupAll = uc_lookupAll - _uncached_subscriptions = uc_subscriptions - - return Derived() - - def test_lookup_w_invalid_name(self): - - def _lookup(self, required, provided, name): - self.fail("This should never be called") - - lb = self._makeOne(uc_lookup=_lookup) - with self.assertRaises(ValueError): - lb.lookup(('A',), 'B', object()) - - def test_lookup_miss_no_default(self): - _called_with = [] - - def _lookup(self, required, provided, name): - _called_with.append((required, provided, name)) - - lb = self._makeOne(uc_lookup=_lookup) - found = lb.lookup(('A',), 'B', 'C') - self.assertIsNone(found) - self.assertEqual(_called_with, [(('A',), 'B', 'C')]) - - def test_lookup_miss_w_default(self): - _called_with = [] - _default = object() - - def _lookup(self, required, provided, name): - _called_with.append((required, provided, name)) - - lb = self._makeOne(uc_lookup=_lookup) - found = lb.lookup(('A',), 'B', 'C', _default) - self.assertIs(found, _default) - self.assertEqual(_called_with, [(('A',), 'B', 'C')]) - - def test_lookup_not_cached(self): - _called_with = [] - a, b, c = object(), object(), object() - _results = [a, b, c] - - def _lookup(self, required, provided, name): - _called_with.append((required, provided, name)) - return _results.pop(0) - - lb = self._makeOne(uc_lookup=_lookup) - found = lb.lookup(('A',), 'B', 'C') - self.assertIs(found, a) - self.assertEqual(_called_with, [(('A',), 'B', 'C')]) - self.assertEqual(_results, [b, c]) - - def test_lookup_cached(self): - _called_with = [] - a, b, c = object(), object(), object() - _results = [a, b, c] - - def _lookup(self, required, provided, name): - _called_with.append((required, provided, name)) - return _results.pop(0) - - lb = self._makeOne(uc_lookup=_lookup) - found = lb.lookup(('A',), 'B', 'C') - found = lb.lookup(('A',), 'B', 'C') - self.assertIs(found, a) - self.assertEqual(_called_with, [(('A',), 'B', 'C')]) - self.assertEqual(_results, [b, c]) - - def test_lookup_not_cached_multi_required(self): - _called_with = [] - a, b, c = object(), object(), object() - _results = [a, b, c] - - def _lookup(self, required, provided, name): - _called_with.append((required, provided, name)) - return _results.pop(0) - - lb = self._makeOne(uc_lookup=_lookup) - found = lb.lookup(('A', 'D'), 'B', 'C') - self.assertIs(found, a) - self.assertEqual(_called_with, [(('A', 'D'), 'B', 'C')]) - self.assertEqual(_results, [b, c]) - - def test_lookup_cached_multi_required(self): - _called_with = [] - a, b, c = object(), object(), object() - _results = [a, b, c] - - def _lookup(self, required, provided, name): - _called_with.append((required, provided, name)) - return _results.pop(0) - - lb = self._makeOne(uc_lookup=_lookup) - found = lb.lookup(('A', 'D'), 'B', 'C') - found = lb.lookup(('A', 'D'), 'B', 'C') - self.assertIs(found, a) - self.assertEqual(_called_with, [(('A', 'D'), 'B', 'C')]) - self.assertEqual(_results, [b, c]) - - def test_lookup_not_cached_after_changed(self): - _called_with = [] - a, b, c = object(), object(), object() - _results = [a, b, c] - - def _lookup(self, required, provided, name): - _called_with.append((required, provided, name)) - return _results.pop(0) - - lb = self._makeOne(uc_lookup=_lookup) - found = lb.lookup(('A',), 'B', 'C') - lb.changed(lb) - found = lb.lookup(('A',), 'B', 'C') - self.assertIs(found, b) - self.assertEqual(_called_with, - [(('A',), 'B', 'C'), (('A',), 'B', 'C')]) - self.assertEqual(_results, [c]) - - def test_lookup1_w_invalid_name(self): - - def _lookup(self, required, provided, name): - self.fail("This should never be called") - - lb = self._makeOne(uc_lookup=_lookup) - with self.assertRaises(ValueError): - lb.lookup1('A', 'B', object()) - - def test_lookup1_miss_no_default(self): - _called_with = [] - - def _lookup(self, required, provided, name): - _called_with.append((required, provided, name)) - - lb = self._makeOne(uc_lookup=_lookup) - found = lb.lookup1('A', 'B', 'C') - self.assertIsNone(found) - self.assertEqual(_called_with, [(('A',), 'B', 'C')]) - - def test_lookup1_miss_w_default(self): - _called_with = [] - _default = object() - - def _lookup(self, required, provided, name): - _called_with.append((required, provided, name)) - - lb = self._makeOne(uc_lookup=_lookup) - found = lb.lookup1('A', 'B', 'C', _default) - self.assertIs(found, _default) - self.assertEqual(_called_with, [(('A',), 'B', 'C')]) - - def test_lookup1_miss_w_default_negative_cache(self): - _called_with = [] - _default = object() - - def _lookup(self, required, provided, name): - _called_with.append((required, provided, name)) - - lb = self._makeOne(uc_lookup=_lookup) - found = lb.lookup1('A', 'B', 'C', _default) - self.assertIs(found, _default) - found = lb.lookup1('A', 'B', 'C', _default) - self.assertIs(found, _default) - self.assertEqual(_called_with, [(('A',), 'B', 'C')]) - - def test_lookup1_not_cached(self): - _called_with = [] - a, b, c = object(), object(), object() - _results = [a, b, c] - - def _lookup(self, required, provided, name): - _called_with.append((required, provided, name)) - return _results.pop(0) - - lb = self._makeOne(uc_lookup=_lookup) - found = lb.lookup1('A', 'B', 'C') - self.assertIs(found, a) - self.assertEqual(_called_with, [(('A',), 'B', 'C')]) - self.assertEqual(_results, [b, c]) - - def test_lookup1_cached(self): - _called_with = [] - a, b, c = object(), object(), object() - _results = [a, b, c] - - def _lookup(self, required, provided, name): - _called_with.append((required, provided, name)) - return _results.pop(0) - - lb = self._makeOne(uc_lookup=_lookup) - found = lb.lookup1('A', 'B', 'C') - found = lb.lookup1('A', 'B', 'C') - self.assertIs(found, a) - self.assertEqual(_called_with, [(('A',), 'B', 'C')]) - self.assertEqual(_results, [b, c]) - - def test_lookup1_not_cached_after_changed(self): - _called_with = [] - a, b, c = object(), object(), object() - _results = [a, b, c] - - def _lookup(self, required, provided, name): - _called_with.append((required, provided, name)) - return _results.pop(0) - - lb = self._makeOne(uc_lookup=_lookup) - found = lb.lookup1('A', 'B', 'C') - lb.changed(lb) - found = lb.lookup1('A', 'B', 'C') - self.assertIs(found, b) - self.assertEqual(_called_with, - [(('A',), 'B', 'C'), (('A',), 'B', 'C')]) - self.assertEqual(_results, [c]) - - def test_adapter_hook_w_invalid_name(self): - req, prv = object(), object() - lb = self._makeOne() - with self.assertRaises(ValueError): - lb.adapter_hook(prv, req, object()) - - def test_adapter_hook_miss_no_default(self): - req, prv = object(), object() - lb = self._makeOne() - found = lb.adapter_hook(prv, req, '') - self.assertIsNone(found) - - def test_adapter_hook_miss_w_default(self): - req, prv, _default = object(), object(), object() - lb = self._makeOne() - found = lb.adapter_hook(prv, req, '', _default) - self.assertIs(found, _default) - - def test_adapter_hook_hit_factory_returns_None(self): - _f_called_with = [] - - def _factory(context): - _f_called_with.append(context) - - def _lookup(self, required, provided, name): - return _factory - - req, prv, _default = object(), object(), object() - lb = self._makeOne(uc_lookup=_lookup) - adapted = lb.adapter_hook(prv, req, 'C', _default) - self.assertIs(adapted, _default) - self.assertEqual(_f_called_with, [req]) - - def test_adapter_hook_hit_factory_returns_adapter(self): - _f_called_with = [] - _adapter = object() - - def _factory(context): - _f_called_with.append(context) - return _adapter - - def _lookup(self, required, provided, name): - return _factory - - req, prv, _default = object(), object(), object() - lb = self._makeOne(uc_lookup=_lookup) - adapted = lb.adapter_hook(prv, req, 'C', _default) - self.assertIs(adapted, _adapter) - self.assertEqual(_f_called_with, [req]) - - def test_adapter_hook_super_unwraps(self): - _f_called_with = [] - - def _factory(context): - _f_called_with.append(context) - return context - - def _lookup(self, required, provided, name=''): - return _factory - - required = super() - provided = object() - lb = self._makeOne(uc_lookup=_lookup) - adapted = lb.adapter_hook(provided, required) - self.assertIs(adapted, self) - self.assertEqual(_f_called_with, [self]) - - def test_queryAdapter(self): - _f_called_with = [] - _adapter = object() - - def _factory(context): - _f_called_with.append(context) - return _adapter - - def _lookup(self, required, provided, name): - return _factory - - req, prv, _default = object(), object(), object() - lb = self._makeOne(uc_lookup=_lookup) - adapted = lb.queryAdapter(req, prv, 'C', _default) - self.assertIs(adapted, _adapter) - self.assertEqual(_f_called_with, [req]) - - def test_lookupAll_uncached(self): - _called_with = [] - _results = [object(), object(), object()] - - def _lookupAll(self, required, provided): - _called_with.append((required, provided)) - return tuple(_results) - - lb = self._makeOne(uc_lookupAll=_lookupAll) - found = lb.lookupAll('A', 'B') - self.assertEqual(found, tuple(_results)) - self.assertEqual(_called_with, [(('A',), 'B')]) - - def test_lookupAll_cached(self): - _called_with = [] - _results = [object(), object(), object()] - - def _lookupAll(self, required, provided): - _called_with.append((required, provided)) - return tuple(_results) - - lb = self._makeOne(uc_lookupAll=_lookupAll) - found = lb.lookupAll('A', 'B') - found = lb.lookupAll('A', 'B') - self.assertEqual(found, tuple(_results)) - self.assertEqual(_called_with, [(('A',), 'B')]) - - def test_subscriptions_uncached(self): - _called_with = [] - _results = [object(), object(), object()] - - def _subscriptions(self, required, provided): - _called_with.append((required, provided)) - return tuple(_results) - - lb = self._makeOne(uc_subscriptions=_subscriptions) - found = lb.subscriptions('A', 'B') - self.assertEqual(found, tuple(_results)) - self.assertEqual(_called_with, [(('A',), 'B')]) - - def test_subscriptions_cached(self): - _called_with = [] - _results = [object(), object(), object()] - - def _subscriptions(self, required, provided): - _called_with.append((required, provided)) - return tuple(_results) - - lb = self._makeOne(uc_subscriptions=_subscriptions) - found = lb.subscriptions('A', 'B') - found = lb.subscriptions('A', 'B') - self.assertEqual(found, tuple(_results)) - self.assertEqual(_called_with, [(('A',), 'B')]) - - -class LookupBaseTests(LookupBaseFallbackTests, - OptimizationTestMixin): - - def _getTargetClass(self): - from zope.interface.adapter import LookupBase - return LookupBase - - -class VerifyingBaseFallbackTests(unittest.TestCase): - - def _getFallbackClass(self): - from zope.interface.adapter import VerifyingBaseFallback - return VerifyingBaseFallback - - _getTargetClass = _getFallbackClass - - def _makeOne(self, registry, uc_lookup=None, uc_lookupAll=None, - uc_subscriptions=None): - # pylint:disable=function-redefined - if uc_lookup is None: - - def uc_lookup(self, required, provided, name): - raise NotImplementedError() - - if uc_lookupAll is None: - - def uc_lookupAll(self, required, provided): - raise NotImplementedError() - - if uc_subscriptions is None: - - def uc_subscriptions(self, required, provided): - raise NotImplementedError() - - class Derived(self._getTargetClass()): - _uncached_lookup = uc_lookup - _uncached_lookupAll = uc_lookupAll - _uncached_subscriptions = uc_subscriptions - - def __init__(self, registry): - super().__init__() - self._registry = registry - - derived = Derived(registry) - derived.changed(derived) # init. '_verify_ro' / '_verify_generations' - return derived - - def _makeRegistry(self, depth): - - class WithGeneration: - _generation = 1 - - class Registry: - def __init__(self, depth): - self.ro = [WithGeneration() for i in range(depth)] - - return Registry(depth) - - def test_lookup(self): - _called_with = [] - a, b, c = object(), object(), object() - _results = [a, b, c] - - def _lookup(self, required, provided, name): - _called_with.append((required, provided, name)) - return _results.pop(0) - - reg = self._makeRegistry(3) - lb = self._makeOne(reg, uc_lookup=_lookup) - found = lb.lookup(('A',), 'B', 'C') - found = lb.lookup(('A',), 'B', 'C') - self.assertIs(found, a) - self.assertEqual(_called_with, [(('A',), 'B', 'C')]) - self.assertEqual(_results, [b, c]) - reg.ro[1]._generation += 1 - found = lb.lookup(('A',), 'B', 'C') - self.assertIs(found, b) - self.assertEqual(_called_with, - [(('A',), 'B', 'C'), (('A',), 'B', 'C')]) - self.assertEqual(_results, [c]) - - def test_lookup1(self): - _called_with = [] - a, b, c = object(), object(), object() - _results = [a, b, c] - - def _lookup(self, required, provided, name): - _called_with.append((required, provided, name)) - return _results.pop(0) - - reg = self._makeRegistry(3) - lb = self._makeOne(reg, uc_lookup=_lookup) - found = lb.lookup1('A', 'B', 'C') - found = lb.lookup1('A', 'B', 'C') - self.assertIs(found, a) - self.assertEqual(_called_with, [(('A',), 'B', 'C')]) - self.assertEqual(_results, [b, c]) - reg.ro[1]._generation += 1 - found = lb.lookup1('A', 'B', 'C') - self.assertIs(found, b) - self.assertEqual(_called_with, - [(('A',), 'B', 'C'), (('A',), 'B', 'C')]) - self.assertEqual(_results, [c]) - - def test_adapter_hook(self): - a, b, _c = [object(), object(), object()] # noqa F841 - - def _factory1(context): - return a - - def _factory2(context): - return b - - def _factory3(context): - self.fail("This should never be called") - - _factories = [_factory1, _factory2, _factory3] - - def _lookup(self, required, provided, name): - return _factories.pop(0) - - req, prv, _default = object(), object(), object() - reg = self._makeRegistry(3) - lb = self._makeOne(reg, uc_lookup=_lookup) - adapted = lb.adapter_hook(prv, req, 'C', _default) - self.assertIs(adapted, a) - adapted = lb.adapter_hook(prv, req, 'C', _default) - self.assertIs(adapted, a) - reg.ro[1]._generation += 1 - adapted = lb.adapter_hook(prv, req, 'C', _default) - self.assertIs(adapted, b) - - def test_queryAdapter(self): - a, b, _c = [object(), object(), object()] # noqa F841 - - def _factory1(context): - return a - - def _factory2(context): - return b - - def _factory3(context): - self.fail("This should never be called") - - _factories = [_factory1, _factory2, _factory3] - - def _lookup(self, required, provided, name): - return _factories.pop(0) - - req, prv, _default = object(), object(), object() - reg = self._makeRegistry(3) - lb = self._makeOne(reg, uc_lookup=_lookup) - adapted = lb.queryAdapter(req, prv, 'C', _default) - self.assertIs(adapted, a) - adapted = lb.queryAdapter(req, prv, 'C', _default) - self.assertIs(adapted, a) - reg.ro[1]._generation += 1 - adapted = lb.adapter_hook(prv, req, 'C', _default) - self.assertIs(adapted, b) - - def test_lookupAll(self): - _results_1 = [object(), object(), object()] - _results_2 = [object(), object(), object()] - _results = [_results_1, _results_2] - - def _lookupAll(self, required, provided): - return tuple(_results.pop(0)) - - reg = self._makeRegistry(3) - lb = self._makeOne(reg, uc_lookupAll=_lookupAll) - found = lb.lookupAll('A', 'B') - self.assertEqual(found, tuple(_results_1)) - found = lb.lookupAll('A', 'B') - self.assertEqual(found, tuple(_results_1)) - reg.ro[1]._generation += 1 - found = lb.lookupAll('A', 'B') - self.assertEqual(found, tuple(_results_2)) - - def test_subscriptions(self): - _results_1 = [object(), object(), object()] - _results_2 = [object(), object(), object()] - _results = [_results_1, _results_2] - - def _subscriptions(self, required, provided): - return tuple(_results.pop(0)) - - reg = self._makeRegistry(3) - lb = self._makeOne(reg, uc_subscriptions=_subscriptions) - found = lb.subscriptions('A', 'B') - self.assertEqual(found, tuple(_results_1)) - found = lb.subscriptions('A', 'B') - self.assertEqual(found, tuple(_results_1)) - reg.ro[1]._generation += 1 - found = lb.subscriptions('A', 'B') - self.assertEqual(found, tuple(_results_2)) - - -class VerifyingBaseTests(VerifyingBaseFallbackTests, - OptimizationTestMixin): - - def _getTargetClass(self): - from zope.interface.adapter import VerifyingBase - return VerifyingBase - - -class AdapterLookupBaseTests(unittest.TestCase): - - def _getTargetClass(self): - from zope.interface.adapter import AdapterLookupBase - return AdapterLookupBase - - def _makeOne(self, registry): - return self._getTargetClass()(registry) - - def _makeSubregistry(self, *provided): - - class Subregistry: - def __init__(self): - self._adapters = [] - self._subscribers = [] - - return Subregistry() - - def _makeRegistry(self, *provided): - - class Registry: - def __init__(self, provided): - self._provided = provided - self.ro = [] - - return Registry(provided) - - def test_ctor_empty_registry(self): - registry = self._makeRegistry() - alb = self._makeOne(registry) - self.assertEqual(alb._extendors, {}) - - def test_ctor_w_registry_provided(self): - from zope.interface import Interface - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - registry = self._makeRegistry(IFoo, IBar) - alb = self._makeOne(registry) - self.assertEqual(sorted(alb._extendors.keys()), - sorted([IBar, IFoo, Interface])) - self.assertEqual(alb._extendors[IFoo], [IFoo, IBar]) - self.assertEqual(alb._extendors[IBar], [IBar]) - self.assertEqual(sorted(alb._extendors[Interface]), - sorted([IFoo, IBar])) - - def test_changed_empty_required(self): - # ALB.changed expects to call a mixed in changed. - - class Mixin: - def changed(self, *other): - pass - - class Derived(self._getTargetClass(), Mixin): - pass - - registry = self._makeRegistry() - alb = Derived(registry) - alb.changed(alb) - - def test_changed_w_required(self): - # ALB.changed expects to call a mixed in changed. - - class Mixin: - def changed(self, *other): - pass - - class Derived(self._getTargetClass(), Mixin): - pass - - class FauxWeakref: - _unsub = None - - def __init__(self, here): - self._here = here - - def __call__(self): - return self if self._here else None - - def unsubscribe(self, target): - self._unsub = target - - gone = FauxWeakref(False) - here = FauxWeakref(True) - registry = self._makeRegistry() - alb = Derived(registry) - alb._required[gone] = 1 - alb._required[here] = 1 - alb.changed(alb) - self.assertEqual(len(alb._required), 0) - self.assertEqual(gone._unsub, None) - self.assertEqual(here._unsub, alb) - - def test_init_extendors_after_registry_update(self): - from zope.interface import Interface - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - registry = self._makeRegistry() - alb = self._makeOne(registry) - registry._provided = [IFoo, IBar] - alb.init_extendors() - self.assertEqual(sorted(alb._extendors.keys()), - sorted([IBar, IFoo, Interface])) - self.assertEqual(alb._extendors[IFoo], [IFoo, IBar]) - self.assertEqual(alb._extendors[IBar], [IBar]) - self.assertEqual(sorted(alb._extendors[Interface]), - sorted([IFoo, IBar])) - - def test_add_extendor(self): - from zope.interface import Interface - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - registry = self._makeRegistry() - alb = self._makeOne(registry) - alb.add_extendor(IFoo) - alb.add_extendor(IBar) - self.assertEqual(sorted(alb._extendors.keys()), - sorted([IBar, IFoo, Interface])) - self.assertEqual(alb._extendors[IFoo], [IFoo, IBar]) - self.assertEqual(alb._extendors[IBar], [IBar]) - self.assertEqual(sorted(alb._extendors[Interface]), - sorted([IFoo, IBar])) - - def test_remove_extendor(self): - from zope.interface import Interface - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - registry = self._makeRegistry(IFoo, IBar) - alb = self._makeOne(registry) - alb.remove_extendor(IFoo) - self.assertEqual(sorted(alb._extendors.keys()), - sorted([IFoo, IBar, Interface])) - self.assertEqual(alb._extendors[IFoo], [IBar]) - self.assertEqual(alb._extendors[IBar], [IBar]) - self.assertEqual(sorted(alb._extendors[Interface]), - sorted([IBar])) - - # test '_subscribe' via its callers, '_uncached_lookup', etc. - - def test__uncached_lookup_empty_ro(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - registry = self._makeRegistry() - alb = self._makeOne(registry) - result = alb._uncached_lookup((IFoo,), IBar) - self.assertEqual(result, None) - self.assertEqual(len(alb._required), 1) - self.assertIn(IFoo.weakref(), alb._required) - - def test__uncached_lookup_order_miss(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - registry = self._makeRegistry(IFoo, IBar) - subr = self._makeSubregistry() - registry.ro.append(subr) - alb = self._makeOne(registry) - result = alb._uncached_lookup((IFoo,), IBar) - self.assertEqual(result, None) - - def test__uncached_lookup_extendors_miss(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - registry = self._makeRegistry() - subr = self._makeSubregistry() - subr._adapters = [{}, {}] # utilities, single adapters - registry.ro.append(subr) - alb = self._makeOne(registry) - subr._v_lookup = alb - result = alb._uncached_lookup((IFoo,), IBar) - self.assertEqual(result, None) - - def test__uncached_lookup_components_miss_wrong_iface(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - IQux = InterfaceClass('IQux') - registry = self._makeRegistry(IFoo, IBar) - subr = self._makeSubregistry() - irrelevant = object() - subr._adapters = [ # utilities, single adapters - {}, - { - IFoo: { - IQux: {'': irrelevant}, - }, - }, - ] - registry.ro.append(subr) - alb = self._makeOne(registry) - subr._v_lookup = alb - result = alb._uncached_lookup((IFoo,), IBar) - self.assertEqual(result, None) - - def test__uncached_lookup_components_miss_wrong_name(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - registry = self._makeRegistry(IFoo, IBar) - subr = self._makeSubregistry() - - wrongname = object() - subr._adapters = [ # utilities, single adapters - {}, - { - IFoo: { - IBar: {'wrongname': wrongname}, - }, - }, - ] - registry.ro.append(subr) - alb = self._makeOne(registry) - subr._v_lookup = alb - result = alb._uncached_lookup((IFoo,), IBar) - self.assertEqual(result, None) - - def test__uncached_lookup_simple_hit(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - registry = self._makeRegistry(IFoo, IBar) - subr = self._makeSubregistry() - _expected = object() - subr._adapters = [ # utilities, single adapters - {}, - {IFoo: {IBar: {'': _expected}}}, - ] - registry.ro.append(subr) - alb = self._makeOne(registry) - subr._v_lookup = alb - result = alb._uncached_lookup((IFoo,), IBar) - self.assertIs(result, _expected) - - def test__uncached_lookup_repeated_hit(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - registry = self._makeRegistry(IFoo, IBar) - subr = self._makeSubregistry() - _expected = object() - subr._adapters = [ # utilities, single adapters - {}, - {IFoo: {IBar: {'': _expected}}}, - ] - registry.ro.append(subr) - alb = self._makeOne(registry) - subr._v_lookup = alb - result = alb._uncached_lookup((IFoo,), IBar) - result2 = alb._uncached_lookup((IFoo,), IBar) - self.assertIs(result, _expected) - self.assertIs(result2, _expected) - - def test_queryMultiAdaptor_lookup_miss(self): - from zope.interface.declarations import implementer - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - - @implementer(IFoo) - class Foo: - pass - - foo = Foo() - registry = self._makeRegistry() - subr = self._makeSubregistry() - subr._adapters = [ # utilities, single adapters - {}, - {}, - ] - registry.ro.append(subr) - alb = self._makeOne(registry) - alb.lookup = alb._uncached_lookup # provided by derived - subr._v_lookup = alb - _default = object() - result = alb.queryMultiAdapter((foo,), IBar, default=_default) - self.assertIs(result, _default) - - def test_queryMultiAdapter_errors_on_attribute_access(self): - # Any error on attribute access previously lead to using the _empty - # singleton as "requires" argument (See - # https://github.com/zopefoundation/zope.interface/issues/162) - # but after https://github.com/zopefoundation/zope.interface/issues/200 - # they get propagated. - from zope.interface.interface import InterfaceClass - from zope.interface.tests import MissingSomeAttrs - - IFoo = InterfaceClass('IFoo') - registry = self._makeRegistry() - alb = self._makeOne(registry) - alb.lookup = alb._uncached_lookup - - def test(ob): - return alb.queryMultiAdapter( - (ob,), - IFoo, - ) - - MissingSomeAttrs.test_raises(self, test, expected_missing='__class__') - - def test_queryMultiAdaptor_factory_miss(self): - from zope.interface.declarations import implementer - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - - @implementer(IFoo) - class Foo: - pass - - foo = Foo() - registry = self._makeRegistry(IFoo, IBar) - subr = self._makeSubregistry() - _called_with = [] - - def _factory(context): - _called_with.append(context) - - subr._adapters = [ # utilities, single adapters - {}, - {IFoo: {IBar: {'': _factory}}}, - ] - registry.ro.append(subr) - alb = self._makeOne(registry) - alb.lookup = alb._uncached_lookup # provided by derived - subr._v_lookup = alb - _default = object() - result = alb.queryMultiAdapter((foo,), IBar, default=_default) - self.assertIs(result, _default) - self.assertEqual(_called_with, [foo]) - - def test_queryMultiAdaptor_factory_hit(self): - from zope.interface.declarations import implementer - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - - @implementer(IFoo) - class Foo: - pass - - foo = Foo() - registry = self._makeRegistry(IFoo, IBar) - subr = self._makeSubregistry() - _expected = object() - _called_with = [] - - def _factory(context): - _called_with.append(context) - return _expected - - subr._adapters = [ # utilities, single adapters - {}, - {IFoo: {IBar: {'': _factory}}}, - ] - registry.ro.append(subr) - alb = self._makeOne(registry) - alb.lookup = alb._uncached_lookup # provided by derived - subr._v_lookup = alb - _default = object() - result = alb.queryMultiAdapter((foo,), IBar, default=_default) - self.assertIs(result, _expected) - self.assertEqual(_called_with, [foo]) - - def test_queryMultiAdapter_super_unwraps(self): - alb = self._makeOne(self._makeRegistry()) - - def lookup(*args): - return factory - - def factory(*args): - return args - - alb.lookup = lookup - - objects = [ - super(), - 42, - "abc", - super(), - ] - - result = alb.queryMultiAdapter(objects, None) - self.assertEqual(result, ( - self, - 42, - "abc", - self, - )) - - def test__uncached_lookupAll_empty_ro(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - registry = self._makeRegistry() - alb = self._makeOne(registry) - result = alb._uncached_lookupAll((IFoo,), IBar) - self.assertEqual(result, ()) - self.assertEqual(len(alb._required), 1) - self.assertIn(IFoo.weakref(), alb._required) - - def test__uncached_lookupAll_order_miss(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - registry = self._makeRegistry(IFoo, IBar) - subr = self._makeSubregistry() - registry.ro.append(subr) - alb = self._makeOne(registry) - subr._v_lookup = alb - result = alb._uncached_lookupAll((IFoo,), IBar) - self.assertEqual(result, ()) - - def test__uncached_lookupAll_extendors_miss(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - registry = self._makeRegistry() - subr = self._makeSubregistry() - subr._adapters = [{}, {}] # utilities, single adapters - registry.ro.append(subr) - alb = self._makeOne(registry) - subr._v_lookup = alb - result = alb._uncached_lookupAll((IFoo,), IBar) - self.assertEqual(result, ()) - - def test__uncached_lookupAll_components_miss(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - IQux = InterfaceClass('IQux') - registry = self._makeRegistry(IFoo, IBar) - subr = self._makeSubregistry() - irrelevant = object() - subr._adapters = [ # utilities, single adapters - {}, - {IFoo: {IQux: {'': irrelevant}}}, - ] - registry.ro.append(subr) - alb = self._makeOne(registry) - subr._v_lookup = alb - result = alb._uncached_lookupAll((IFoo,), IBar) - self.assertEqual(result, ()) - - def test__uncached_lookupAll_simple_hit(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - registry = self._makeRegistry(IFoo, IBar) - subr = self._makeSubregistry() - _expected = object() - _named = object() - subr._adapters = [ # utilities, single adapters - {}, - {IFoo: {IBar: {'': _expected, 'named': _named}}}, - ] - registry.ro.append(subr) - alb = self._makeOne(registry) - subr._v_lookup = alb - result = alb._uncached_lookupAll((IFoo,), IBar) - self.assertEqual(sorted(result), [('', _expected), ('named', _named)]) - - def test_names(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - registry = self._makeRegistry(IFoo, IBar) - subr = self._makeSubregistry() - _expected = object() - _named = object() - subr._adapters = [ # utilities, single adapters - {}, - {IFoo: {IBar: {'': _expected, 'named': _named}}}, - ] - registry.ro.append(subr) - alb = self._makeOne(registry) - alb.lookupAll = alb._uncached_lookupAll - subr._v_lookup = alb - result = alb.names((IFoo,), IBar) - self.assertEqual(sorted(result), ['', 'named']) - - def test__uncached_subscriptions_empty_ro(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - registry = self._makeRegistry() - alb = self._makeOne(registry) - result = alb._uncached_subscriptions((IFoo,), IBar) - self.assertEqual(result, []) - self.assertEqual(len(alb._required), 1) - self.assertIn(IFoo.weakref(), alb._required) - - def test__uncached_subscriptions_order_miss(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - registry = self._makeRegistry(IFoo, IBar) - subr = self._makeSubregistry() - registry.ro.append(subr) - alb = self._makeOne(registry) - subr._v_lookup = alb - result = alb._uncached_subscriptions((IFoo,), IBar) - self.assertEqual(result, []) - - def test__uncached_subscriptions_extendors_miss(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - registry = self._makeRegistry() - subr = self._makeSubregistry() - subr._subscribers = [{}, {}] # utilities, single adapters - registry.ro.append(subr) - alb = self._makeOne(registry) - subr._v_lookup = alb - result = alb._uncached_subscriptions((IFoo,), IBar) - self.assertEqual(result, []) - - def test__uncached_subscriptions_components_miss_wrong_iface(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - IQux = InterfaceClass('IQux') - registry = self._makeRegistry(IFoo, IBar) - subr = self._makeSubregistry() - irrelevant = object() - subr._subscribers = [ # utilities, single adapters - {}, - {IFoo: {IQux: {'': irrelevant}}}, - ] - registry.ro.append(subr) - alb = self._makeOne(registry) - subr._v_lookup = alb - result = alb._uncached_subscriptions((IFoo,), IBar) - self.assertEqual(result, []) - - def test__uncached_subscriptions_components_miss_wrong_name(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - registry = self._makeRegistry(IFoo, IBar) - subr = self._makeSubregistry() - wrongname = object() - subr._subscribers = [ # utilities, single adapters - {}, - {IFoo: {IBar: {'wrongname': wrongname}}}, - ] - registry.ro.append(subr) - alb = self._makeOne(registry) - subr._v_lookup = alb - result = alb._uncached_subscriptions((IFoo,), IBar) - self.assertEqual(result, []) - - def test__uncached_subscriptions_simple_hit(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - registry = self._makeRegistry(IFoo, IBar) - subr = self._makeSubregistry() - - class Foo: - - def __lt__(self, other): - return True - - _exp1, _exp2 = Foo(), Foo() - subr._subscribers = [ # utilities, single adapters - {}, - {IFoo: {IBar: {'': (_exp1, _exp2)}}}, - ] - registry.ro.append(subr) - alb = self._makeOne(registry) - subr._v_lookup = alb - result = alb._uncached_subscriptions((IFoo,), IBar) - self.assertEqual(sorted(result), sorted([_exp1, _exp2])) - - def test_subscribers_wo_provided(self): - from zope.interface.declarations import implementer - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - - @implementer(IFoo) - class Foo: - pass - - foo = Foo() - registry = self._makeRegistry(IFoo, IBar) - registry = self._makeRegistry(IFoo, IBar) - subr = self._makeSubregistry() - _called = {} - - def _factory1(context): - _called.setdefault('_factory1', []).append(context) - - def _factory2(context): - _called.setdefault('_factory2', []).append(context) - - subr._subscribers = [ # utilities, single adapters - {}, - {IFoo: {None: {'': (_factory1, _factory2)}}}, - ] - registry.ro.append(subr) - alb = self._makeOne(registry) - alb.subscriptions = alb._uncached_subscriptions - subr._v_lookup = alb - result = alb.subscribers((foo,), None) - self.assertEqual(result, ()) - self.assertEqual(_called, {'_factory1': [foo], '_factory2': [foo]}) - - def test_subscribers_w_provided(self): - from zope.interface.declarations import implementer - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - - @implementer(IFoo) - class Foo: - pass - - foo = Foo() - registry = self._makeRegistry(IFoo, IBar) - registry = self._makeRegistry(IFoo, IBar) - subr = self._makeSubregistry() - _called = {} - _exp1, _exp2 = object(), object() - - def _factory1(context): - _called.setdefault('_factory1', []).append(context) - return _exp1 - - def _factory2(context): - _called.setdefault('_factory2', []).append(context) - return _exp2 - - def _side_effect_only(context): - _called.setdefault('_side_effect_only', []).append(context) - - subr._subscribers = [ # utilities, single adapters - {}, - {IFoo: {IBar: {'': (_factory1, _factory2, _side_effect_only)}}}, - ] - registry.ro.append(subr) - alb = self._makeOne(registry) - alb.subscriptions = alb._uncached_subscriptions - subr._v_lookup = alb - result = alb.subscribers((foo,), IBar) - self.assertEqual(result, [_exp1, _exp2]) - self.assertEqual( - _called, { - '_factory1': [foo], - '_factory2': [foo], - '_side_effect_only': [foo], - } - ) - - -class VerifyingAdapterRegistryTests(unittest.TestCase): - # This is also the base for AdapterRegistryTests. That makes the - # inheritance seems backwards, but even though they implement the - # same interfaces, VAR and AR each only extend BAR; and neither - # one will pass the test cases for BAR (it uses a special - # LookupClass just for the tests). - - def _getTargetClass(self): - from zope.interface.adapter import VerifyingAdapterRegistry - return VerifyingAdapterRegistry - - def _makeOne(self, *args, **kw): - return self._getTargetClass()(*args, **kw) - - def test_verify_object_provides_IAdapterRegistry(self): - from zope.interface.interfaces import IAdapterRegistry - from zope.interface.verify import verifyObject - registry = self._makeOne() - verifyObject(IAdapterRegistry, registry) - - -class AdapterRegistryTests(VerifyingAdapterRegistryTests): - - def _getTargetClass(self): - from zope.interface.adapter import AdapterRegistry - return AdapterRegistry - - def test_ctor_no_bases(self): - ar = self._makeOne() - self.assertEqual(len(ar._v_subregistries), 0) - - def test_ctor_w_bases(self): - base = self._makeOne() - sub = self._makeOne([base]) - self.assertEqual(len(sub._v_subregistries), 0) - self.assertEqual(len(base._v_subregistries), 1) - self.assertIn(sub, base._v_subregistries) - - # test _addSubregistry / _removeSubregistry via only caller, _setBases - - def test__setBases_removing_existing_subregistry(self): - before = self._makeOne() - after = self._makeOne() - sub = self._makeOne([before]) - sub.__bases__ = [after] - self.assertEqual(len(before._v_subregistries), 0) - self.assertEqual(len(after._v_subregistries), 1) - self.assertIn(sub, after._v_subregistries) - - def test__setBases_wo_stray_entry(self): - before = self._makeOne() - stray = self._makeOne() - after = self._makeOne() - sub = self._makeOne([before]) - sub.__dict__['__bases__'].append(stray) - sub.__bases__ = [after] - self.assertEqual(len(before._v_subregistries), 0) - self.assertEqual(len(after._v_subregistries), 1) - self.assertIn(sub, after._v_subregistries) - - def test__setBases_w_existing_entry_continuing(self): - before = self._makeOne() - after = self._makeOne() - sub = self._makeOne([before]) - sub.__bases__ = [before, after] - self.assertEqual(len(before._v_subregistries), 1) - self.assertEqual(len(after._v_subregistries), 1) - self.assertIn(sub, before._v_subregistries) - self.assertIn(sub, after._v_subregistries) - - def test_changed_w_subregistries(self): - base = self._makeOne() - - class Derived: - _changed = None - - def changed(self, originally_changed): - self._changed = originally_changed - - derived1, derived2 = Derived(), Derived() - base._addSubregistry(derived1) - base._addSubregistry(derived2) - orig = object() - base.changed(orig) - self.assertIs(derived1._changed, orig) - self.assertIs(derived2._changed, orig) - - -class Test_utils(unittest.TestCase): - - def test__convert_None_to_Interface_w_None(self): - from zope.interface.adapter import _convert_None_to_Interface - from zope.interface.interface import Interface - self.assertIs(_convert_None_to_Interface(None), Interface) - - def test__convert_None_to_Interface_w_other(self): - from zope.interface.adapter import _convert_None_to_Interface - other = object() - self.assertIs(_convert_None_to_Interface(other), other) - - def test__normalize_name_str(self): - from zope.interface.adapter import _normalize_name - STR = b'str' - UNICODE = 'str' - norm = _normalize_name(STR) - self.assertEqual(norm, UNICODE) - self.assertIsInstance(norm, type(UNICODE)) - - def test__normalize_name_unicode(self): - from zope.interface.adapter import _normalize_name - - USTR = 'ustr' - self.assertEqual(_normalize_name(USTR), USTR) - - def test__normalize_name_other(self): - from zope.interface.adapter import _normalize_name - for other in 1, 1.0, (), [], {}, object(): - self.assertRaises(TypeError, _normalize_name, other) - - # _lookup, _lookupAll, and _subscriptions tested via their callers - # (AdapterLookupBase.{lookup,lookupAll,subscriptions}). diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_advice.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_advice.py deleted file mode 100644 index 9612b6f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_advice.py +++ /dev/null @@ -1,217 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Tests for advice - -This module was adapted from 'protocols.tests.advice', part of the Python -Enterprise Application Kit (PEAK). Please notify the PEAK authors -(pje@telecommunity.com and tsarna@sarna.org) if bugs are found or -Zope-specific changes are required, so that the PEAK version of this module -can be kept in sync. - -PEAK is a Python application framework that interoperates with (but does -not require) Zope 3 and Twisted. It provides tools for manipulating UML -models, object-relational persistence, aspect-oriented programming, and more. -Visit the PEAK home page at http://peak.telecommunity.com for more information. -""" - -import sys -import unittest - - -class FrameInfoTest(unittest.TestCase): - - def test_w_module(self): - from zope.interface.tests import advisory_testing - (kind, module, - f_locals, f_globals) = advisory_testing.moduleLevelFrameInfo - self.assertEqual(kind, "module") - for d in module.__dict__, f_locals, f_globals: - self.assertIs(d, advisory_testing.my_globals) - - def test_w_class(self): - from zope.interface.tests import advisory_testing - (kind, - module, - f_locals, - f_globals) = advisory_testing.NewStyleClass.classLevelFrameInfo - self.assertEqual(kind, "class") - - for d in module.__dict__, f_globals: - self.assertIs(d, advisory_testing.my_globals) - - def test_inside_function_call(self): - from zope.interface.advice import getFrameInfo - kind, module, f_locals, f_globals = getFrameInfo(sys._getframe()) - self.assertEqual(kind, "function call") - - frame = sys._getframe() - self.assertEqual(f_locals, frame.f_locals) - self.assertEqual(f_locals, locals()) - - for d in module.__dict__, f_globals: - self.assertIs(d, globals()) - - def test_inside_exec(self): - from zope.interface.advice import getFrameInfo - _globals = {'getFrameInfo': getFrameInfo} - _locals = {} - exec(_FUNKY_EXEC, _globals, _locals) - self.assertEqual(_locals['kind'], "exec") - self.assertIs(_locals['f_locals'], _locals) - self.assertIsNone(_locals['module']) - self.assertIs(_locals['f_globals'], _globals) - - -_FUNKY_EXEC = """\ -import sys -kind, module, f_locals, f_globals = getFrameInfo(sys._getframe()) -""" - - -class Test_isClassAdvisor(unittest.TestCase): - - def _callFUT(self, *args, **kw): - from zope.interface.advice import isClassAdvisor - return isClassAdvisor(*args, **kw) - - def test_w_non_function(self): - self.assertEqual(self._callFUT(self), False) - - def test_w_normal_function(self): - - def foo(): - raise NotImplementedError() - - self.assertEqual(self._callFUT(foo), False) - - def test_w_advisor_function(self): - - def bar(): - raise NotImplementedError() - - bar.previousMetaclass = object() - self.assertEqual(self._callFUT(bar), True) - - -class Test_determineMetaclass(unittest.TestCase): - - def _callFUT(self, *args, **kw): - from zope.interface.advice import determineMetaclass - return determineMetaclass(*args, **kw) - - def test_empty_w_explicit_metatype(self): - - class Meta(type): - pass - - self.assertEqual(self._callFUT((), Meta), Meta) - - def test_single(self): - - class Meta(type): - pass - - self.assertEqual(self._callFUT((Meta,)), type) - - def test_meta_of_class(self): - - class Metameta(type): - pass - - class Meta(type, metaclass=Metameta): - pass - - self.assertEqual(self._callFUT((Meta, type)), Metameta) - - def test_multiple_in_hierarchy_py3k(self): - - class Meta_A(type): - pass - - class Meta_B(Meta_A): - pass - - class A(type, metaclass=Meta_A): - pass - - class B(type, metaclass=Meta_B): - pass - - self.assertEqual(self._callFUT((A, B)), Meta_B) - - def test_multiple_not_in_hierarchy_py3k(self): - - class Meta_A(type): - pass - - class Meta_B(type): - pass - - class A(type, metaclass=Meta_A): - pass - - class B(type, metaclass=Meta_B): - pass - - self.assertRaises(TypeError, self._callFUT, (A, B)) - - -class Test_minimalBases(unittest.TestCase): - - def _callFUT(self, klasses): - from zope.interface.advice import minimalBases - return minimalBases(klasses) - - def test_empty(self): - self.assertEqual(self._callFUT([]), []) - - def test_w_newstyle_meta(self): - self.assertEqual(self._callFUT([type]), [type]) - - def test_w_newstyle_class(self): - - class C: - pass - - self.assertEqual(self._callFUT([C]), [C]) - - def test_simple_hierarchy_skips_implied(self): - - class A: - pass - - class B(A): - pass - - class C(B): - pass - - class D: - pass - - self.assertEqual(self._callFUT([A, B, C]), [C]) - self.assertEqual(self._callFUT([A, C]), [C]) - self.assertEqual(self._callFUT([B, C]), [C]) - self.assertEqual(self._callFUT([A, B]), [B]) - self.assertEqual(self._callFUT([D, B, D]), [B, D]) - - def test_repeats_kicked_to_end_of_queue(self): - - class A: - pass - - class B: - pass - - self.assertEqual(self._callFUT([A, B, A]), [B, A]) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_compile_flags.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_compile_flags.py deleted file mode 100644 index 3455c44..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_compile_flags.py +++ /dev/null @@ -1,29 +0,0 @@ -############################################################################## -# -# Copyright (c) 2022 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE -# -############################################################################## -import struct -import unittest - -import zope.interface # noqa: try to load a C module for side effects - - -class TestFloatingPoint(unittest.TestCase): - - def test_no_fast_math_optimization(self): - # Building with -Ofast enables -ffast-math, which sets certain FPU - # flags that can cause breakage elsewhere. A library such as BTrees - # has no business changing global FPU flags for the entire process. - zero_bits = struct.unpack("!Q", struct.pack("!d", 0.0))[0] - next_up = zero_bits + 1 - smallest_subnormal = struct.unpack("!d", struct.pack("!Q", next_up))[0] - self.assertNotEqual(smallest_subnormal, 0.0) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_declarations.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_declarations.py deleted file mode 100644 index f6bdfde..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_declarations.py +++ /dev/null @@ -1,2685 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Test the new API for making and checking interface declarations -""" -import unittest - -from zope.interface.tests import MissingSomeAttrs -from zope.interface.tests import OptimizationTestMixin -from zope.interface.tests import SubclassableMixin -from zope.interface.tests.test_interface import \ - NameAndModuleComparisonTestsMixin - - -# pylint:disable=inherit-non-class,too-many-lines,protected-access -# pylint:disable=blacklisted-name,attribute-defined-outside-init - -class _Py3ClassAdvice: - - def _run_generated_code( - self, code, globs, locs, fails_under_py3k=True, - ): - # pylint:disable=exec-used,no-member - import warnings - with warnings.catch_warnings(record=True) as _: - warnings.resetwarnings() - - try: - exec(code, globs, locs) - except TypeError: - return False - else: - if fails_under_py3k: - self.fail("Didn't raise TypeError") - return None - - -class NamedTests(unittest.TestCase): - - def test_class(self): - from zope.interface.declarations import named - - @named('foo') - class Foo: - pass - - self.assertEqual( - Foo.__component_name__, 'foo' - ) # pylint:disable=no-member - - def test_function(self): - from zope.interface.declarations import named - - @named('foo') - def doFoo(o): - raise NotImplementedError() - - self.assertEqual(doFoo.__component_name__, 'foo') - - def test_instance(self): - from zope.interface.declarations import named - - class Foo: - pass - foo = Foo() - named('foo')(foo) - - self.assertEqual( - foo.__component_name__, 'foo' - ) # pylint:disable=no-member - - -class EmptyDeclarationTests(unittest.TestCase): - # Tests that should pass for all objects that are empty - # declarations. This includes a Declaration explicitly created - # that way, and the empty ImmutableDeclaration. - def _getEmpty(self): - from zope.interface.declarations import Declaration - return Declaration() - - def test___iter___empty(self): - decl = self._getEmpty() - self.assertEqual(list(decl), []) - - def test_flattened_empty(self): - from zope.interface.interface import Interface - decl = self._getEmpty() - self.assertEqual(list(decl.flattened()), [Interface]) - - def test___contains___empty(self): - from zope.interface.interface import Interface - decl = self._getEmpty() - self.assertNotIn(Interface, decl) - - def test_extends_empty(self): - from zope.interface.interface import Interface - decl = self._getEmpty() - self.assertTrue(decl.extends(Interface)) - self.assertTrue(decl.extends(Interface, strict=True)) - - def test_interfaces_empty(self): - decl = self._getEmpty() - iface_list = list(decl.interfaces()) - self.assertEqual(iface_list, []) - - def test___sro___(self): - from zope.interface.interface import Interface - decl = self._getEmpty() - self.assertEqual(decl.__sro__, (decl, Interface,)) - - def test___iro___(self): - from zope.interface.interface import Interface - decl = self._getEmpty() - self.assertEqual(decl.__iro__, (Interface,)) - - def test_get(self): - decl = self._getEmpty() - self.assertIsNone(decl.get('attr')) - self.assertEqual(decl.get('abc', 'def'), 'def') - # It's a positive cache only (when it even exists) - # so this added nothing. - self.assertFalse(decl._v_attrs) - - def test_changed_w_existing__v_attrs(self): - decl = self._getEmpty() - decl._v_attrs = object() - decl.changed(decl) - self.assertFalse(decl._v_attrs) - - -class DeclarationTests(EmptyDeclarationTests): - - def _getTargetClass(self): - from zope.interface.declarations import Declaration - return Declaration - - def _makeOne(self, *args, **kw): - return self._getTargetClass()(*args, **kw) - - def test_ctor_no_bases(self): - decl = self._makeOne() - self.assertEqual(list(decl.__bases__), []) - - def test_ctor_w_interface_in_bases(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - decl = self._makeOne(IFoo) - self.assertEqual(list(decl.__bases__), [IFoo]) - - def test_ctor_w_implements_in_bases(self): - from zope.interface.declarations import Implements - impl = Implements() - decl = self._makeOne(impl) - self.assertEqual(list(decl.__bases__), [impl]) - - def test_changed_wo_existing__v_attrs(self): - decl = self._makeOne() - decl.changed(decl) # doesn't raise - self.assertIsNone(decl._v_attrs) - - def test___contains__w_self(self): - decl = self._makeOne() - self.assertNotIn(decl, decl) - - def test___contains__w_unrelated_iface(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - decl = self._makeOne() - self.assertNotIn(IFoo, decl) - - def test___contains__w_base_interface(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - decl = self._makeOne(IFoo) - self.assertIn(IFoo, decl) - - def test___iter___single_base(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - decl = self._makeOne(IFoo) - self.assertEqual(list(decl), [IFoo]) - - def test___iter___multiple_bases(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar') - decl = self._makeOne(IFoo, IBar) - self.assertEqual(list(decl), [IFoo, IBar]) - - def test___iter___inheritance(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - decl = self._makeOne(IBar) - self.assertEqual(list(decl), [IBar]) # IBar.interfaces() omits bases - - def test___iter___w_nested_sequence_overlap(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar') - decl = self._makeOne(IBar, (IFoo, IBar)) - self.assertEqual(list(decl), [IBar, IFoo]) - - def test_flattened_single_base(self): - from zope.interface.interface import Interface - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - decl = self._makeOne(IFoo) - self.assertEqual(list(decl.flattened()), [IFoo, Interface]) - - def test_flattened_multiple_bases(self): - from zope.interface.interface import Interface - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar') - decl = self._makeOne(IFoo, IBar) - self.assertEqual(list(decl.flattened()), [IFoo, IBar, Interface]) - - def test_flattened_inheritance(self): - from zope.interface.interface import Interface - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - decl = self._makeOne(IBar) - self.assertEqual(list(decl.flattened()), [IBar, IFoo, Interface]) - - def test_flattened_w_nested_sequence_overlap(self): - from zope.interface.interface import Interface - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar') - # This is the same as calling ``Declaration(IBar, IFoo, IBar)`` - # which doesn't make much sense, but here it is. In older - # versions of zope.interface, the __iro__ would have been - # IFoo, IBar, Interface, which especially makes no sense. - decl = self._makeOne(IBar, (IFoo, IBar)) - # Note that decl.__iro__ has IFoo first. - self.assertEqual(list(decl.flattened()), [IBar, IFoo, Interface]) - - def test___sub___unrelated_interface(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar') - before = self._makeOne(IFoo) - after = before - IBar - self.assertIsInstance(after, self._getTargetClass()) - self.assertEqual(list(after), [IFoo]) - - def test___sub___related_interface(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - before = self._makeOne(IFoo) - after = before - IFoo - self.assertEqual(list(after), []) - - def test___sub___related_interface_by_inheritance(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar', (IFoo,)) - before = self._makeOne(IBar) - after = before - IBar - self.assertEqual(list(after), []) - - def test___add___unrelated_interface(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar') - before = self._makeOne(IFoo) - after = before + IBar - self.assertIsInstance(after, self._getTargetClass()) - self.assertEqual(list(after), [IFoo, IBar]) - - def test___add___related_interface(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar') - IBaz = InterfaceClass('IBaz') - before = self._makeOne(IFoo, IBar) - other = self._makeOne(IBar, IBaz) - after = before + other - self.assertEqual(list(after), [IFoo, IBar, IBaz]) - - def test___add___overlapping_interface(self): - # The derived interfaces end up with higher priority, and - # don't produce a C3 resolution order violation. This - # example produced a C3 error, and the resulting legacy order - # used to be wrong ([IBase, IDerived] instead of - # the other way). - from zope.interface import Interface - from zope.interface import ro - from zope.interface.interface import InterfaceClass - from zope.interface.tests.test_ro import C3Setting - - IBase = InterfaceClass('IBase') - IDerived = InterfaceClass('IDerived', (IBase,)) - - with C3Setting(ro.C3.STRICT_IRO, True): - base = self._makeOne(IBase) - after = base + IDerived - - self.assertEqual(after.__iro__, (IDerived, IBase, Interface)) - self.assertEqual(after.__bases__, (IDerived, IBase)) - self.assertEqual(list(after), [IDerived, IBase]) - - def test___add___overlapping_interface_implementedBy(self): - # Like test___add___overlapping_interface, but pulling - # in a realistic example. This one previously produced a - # C3 error, but the resulting legacy order was (somehow) - # correct. - from zope.interface import Interface - from zope.interface import implementedBy - from zope.interface import implementer - from zope.interface import ro - from zope.interface.tests.test_ro import C3Setting - - class IBase(Interface): - pass - - class IDerived(IBase): - pass - - @implementer(IBase) - class Base: - pass - - with C3Setting(ro.C3.STRICT_IRO, True): - after = implementedBy(Base) + IDerived - - self.assertEqual(after.__sro__, (after, IDerived, IBase, Interface)) - self.assertEqual(after.__bases__, (IDerived, IBase)) - self.assertEqual(list(after), [IDerived, IBase]) - - -class TestImmutableDeclaration(EmptyDeclarationTests): - - def _getTargetClass(self): - from zope.interface.declarations import _ImmutableDeclaration - return _ImmutableDeclaration - - def _getEmpty(self): - from zope.interface.declarations import _empty - return _empty - - def test_pickle(self): - import pickle - copied = pickle.loads(pickle.dumps(self._getEmpty())) - self.assertIs(copied, self._getEmpty()) - - def test_singleton(self): - self.assertIs( - self._getTargetClass()(), - self._getEmpty() - ) - - def test__bases__(self): - self.assertEqual(self._getEmpty().__bases__, ()) - - def test_change__bases__(self): - empty = self._getEmpty() - empty.__bases__ = () - self.assertEqual(self._getEmpty().__bases__, ()) - - with self.assertRaises(TypeError): - empty.__bases__ = (1,) - - def test_dependents(self): - empty = self._getEmpty() - deps = empty.dependents - self.assertEqual({}, deps) - # Doesn't change the return. - deps[1] = 2 - self.assertEqual({}, empty.dependents) - - def test_changed(self): - # Does nothing, has no visible side-effects - self._getEmpty().changed(None) - - def test_extends_always_false(self): - self.assertFalse(self._getEmpty().extends(self)) - self.assertFalse(self._getEmpty().extends(self, strict=True)) - self.assertFalse(self._getEmpty().extends(self, strict=False)) - - def test_get_always_default(self): - self.assertIsNone(self._getEmpty().get('name')) - self.assertEqual(self._getEmpty().get('name', 42), 42) - - def test_v_attrs(self): - decl = self._getEmpty() - self.assertEqual(decl._v_attrs, {}) - - decl._v_attrs['attr'] = 42 - self.assertEqual(decl._v_attrs, {}) - self.assertIsNone(decl.get('attr')) - - attrs = decl._v_attrs = {} - attrs['attr'] = 42 - self.assertEqual(decl._v_attrs, {}) - self.assertIsNone(decl.get('attr')) - - -class TestImplements(NameAndModuleComparisonTestsMixin, - unittest.TestCase): - - def _getTargetClass(self): - from zope.interface.declarations import Implements - return Implements - - def _makeOne(self, *args, **kw): - return self._getTargetClass()(*args, **kw) - - def _makeOneToCompare(self): - from zope.interface.declarations import implementedBy - - class A: - pass - - return implementedBy(A) - - def test_ctor_no_bases(self): - impl = self._makeOne() - self.assertEqual(impl.inherit, None) - self.assertEqual(impl.declared, ()) - self.assertEqual(impl.__name__, '?') - self.assertEqual(list(impl.__bases__), []) - - def test___repr__(self): - impl = self._makeOne() - impl.__name__ = 'Testing' - self.assertEqual(repr(impl), 'classImplements(Testing)') - - def test___reduce__(self): - from zope.interface.declarations import implementedBy - impl = self._makeOne() - self.assertEqual(impl.__reduce__(), (implementedBy, (None,))) - - def test_sort(self): - from zope.interface.declarations import implementedBy - from zope.interface.interface import InterfaceClass - - class A: - pass - - class B: - pass - - IFoo = InterfaceClass('IFoo') - - self.assertEqual(implementedBy(A), implementedBy(A)) - self.assertEqual(hash(implementedBy(A)), hash(implementedBy(A))) - self.assertLess(implementedBy(A), None) - self.assertGreater( - None, - implementedBy(A) - ) - self.assertLess(implementedBy(A), implementedBy(B)) - self.assertGreater(implementedBy(A), IFoo) - self.assertLessEqual(implementedBy(A), implementedBy(B)) - self.assertGreaterEqual(implementedBy(A), IFoo) - self.assertNotEqual(implementedBy(A), IFoo) - - def test_proxy_equality(self): - # https://github.com/zopefoundation/zope.interface/issues/55 - from zope.interface.declarations import implementedBy - - class Proxy: - def __init__(self, wrapped): - self._wrapped = wrapped - - def __getattr__(self, name): - raise NotImplementedError() - - def __eq__(self, other): - return self._wrapped == other - - def __ne__(self, other): - return self._wrapped != other - - class A: - pass - - class B: - pass - - implementedByA = implementedBy(A) - implementedByB = implementedBy(B) - proxy = Proxy(implementedByA) - - # The order of arguments to the operators matters, - # test both - self.assertEqual( - implementedByA, - implementedByA - ) - self.assertNotEqual(implementedByA, implementedByB) - self.assertNotEqual(implementedByB, implementedByA) - - self.assertEqual(proxy, implementedByA) - self.assertEqual(implementedByA, proxy) - self.assertEqual(proxy, implementedByA) - self.assertEqual(implementedByA, proxy) - - self.assertNotEqual(proxy, implementedByB) - self.assertNotEqual(implementedByB, proxy) - - def test_changed_deletes_super_cache(self): - impl = self._makeOne() - self.assertIsNone(impl._super_cache) - self.assertNotIn('_super_cache', impl.__dict__) - - impl._super_cache = 42 - self.assertIn('_super_cache', impl.__dict__) - - impl.changed(None) - self.assertIsNone(impl._super_cache) - self.assertNotIn('_super_cache', impl.__dict__) - - def test_changed_does_not_add_super_cache(self): - impl = self._makeOne() - self.assertIsNone(impl._super_cache) - self.assertNotIn('_super_cache', impl.__dict__) - - impl.changed(None) - self.assertIsNone(impl._super_cache) - self.assertNotIn('_super_cache', impl.__dict__) - - -class Test_implementedByFallback(unittest.TestCase): - - def _getTargetClass(self): - # pylint:disable=no-name-in-module - from zope.interface.declarations import implementedByFallback - return implementedByFallback - - _getFallbackClass = _getTargetClass - - def _callFUT(self, *args, **kw): - return self._getTargetClass()(*args, **kw) - - def test_dictless_wo_existing_Implements_wo_registrations(self): - class Foo: - __slots__ = ('__implemented__',) - foo = Foo() - foo.__implemented__ = None - self.assertEqual(list(self._callFUT(foo)), []) - - def test_dictless_wo_existing_Implements_cant_assign___implemented__(self): - - class Foo: - def _get_impl(self): - raise NotImplementedError() - - def _set_impl(self, val): - raise TypeError - - __implemented__ = property(_get_impl, _set_impl) - - def __call__(self): - # act like a factory - raise NotImplementedError() - - foo = Foo() - self.assertRaises(TypeError, self._callFUT, foo) - - def test_dictless_wo_existing_Implements_w_registrations(self): - from zope.interface import declarations - - class Foo: - __slots__ = ('__implemented__',) - - foo = Foo() - foo.__implemented__ = None - reg = object() - with _MonkeyDict(declarations, - 'BuiltinImplementationSpecifications') as specs: - specs[foo] = reg - self.assertIs(self._callFUT(foo), reg) - - def test_dictless_w_existing_Implements(self): - from zope.interface.declarations import Implements - impl = Implements() - - class Foo: - __slots__ = ('__implemented__',) - - foo = Foo() - foo.__implemented__ = impl - self.assertIs(self._callFUT(foo), impl) - - def test_dictless_w_existing_not_Implements(self): - from zope.interface.interface import InterfaceClass - - class Foo: - __slots__ = ('__implemented__',) - - foo = Foo() - IFoo = InterfaceClass('IFoo') - foo.__implemented__ = (IFoo,) - self.assertEqual(list(self._callFUT(foo)), [IFoo]) - - def test_w_existing_attr_as_Implements(self): - from zope.interface.declarations import Implements - impl = Implements() - - class Foo: - __implemented__ = impl - - self.assertIs(self._callFUT(Foo), impl) - - def test_builtins_added_to_cache(self): - from zope.interface import declarations - from zope.interface.declarations import Implements - with _MonkeyDict(declarations, - 'BuiltinImplementationSpecifications') as specs: - self.assertEqual(list(self._callFUT(tuple)), []) - self.assertEqual(list(self._callFUT(list)), []) - self.assertEqual(list(self._callFUT(dict)), []) - for typ in (tuple, list, dict): - spec = specs[typ] - self.assertIsInstance(spec, Implements) - self.assertEqual(repr(spec), - 'classImplements(%s)' - % (typ.__name__,)) - - def test_builtins_w_existing_cache(self): - from zope.interface import declarations - t_spec, l_spec, d_spec = object(), object(), object() - with _MonkeyDict(declarations, - 'BuiltinImplementationSpecifications') as specs: - specs[tuple] = t_spec - specs[list] = l_spec - specs[dict] = d_spec - self.assertIs(self._callFUT(tuple), t_spec) - self.assertIs(self._callFUT(list), l_spec) - self.assertIs(self._callFUT(dict), d_spec) - - def test_oldstyle_class_no_assertions(self): - # TODO: Figure out P3 story - - class Foo: - pass - - self.assertEqual(list(self._callFUT(Foo)), []) - - def test_no_assertions(self): - # TODO: Figure out P3 story - - class Foo: - pass - - self.assertEqual(list(self._callFUT(Foo)), []) - - def test_w_None_no_bases_not_factory(self): - - class Foo: - __implemented__ = None - - foo = Foo() - self.assertRaises(TypeError, self._callFUT, foo) - - def test_w_None_no_bases_w_factory(self): - from zope.interface.declarations import objectSpecificationDescriptor - - class Foo: - __implemented__ = None - - def __call__(self): - raise NotImplementedError() - - foo = Foo() - foo.__name__ = 'foo' - spec = self._callFUT(foo) - self.assertEqual(spec.__name__, - 'zope.interface.tests.test_declarations.foo') - self.assertIs(spec.inherit, foo) - self.assertIs(foo.__implemented__, spec) - self.assertIs( - foo.__providedBy__, objectSpecificationDescriptor - ) # pylint:disable=no-member - self.assertNotIn('__provides__', foo.__dict__) - - def test_w_None_no_bases_w_class(self): - from zope.interface.declarations import ClassProvides - - class Foo: - __implemented__ = None - - spec = self._callFUT(Foo) - self.assertEqual(spec.__name__, - 'zope.interface.tests.test_declarations.Foo') - self.assertIs(spec.inherit, Foo) - self.assertIs(Foo.__implemented__, spec) - self.assertIsInstance( - Foo.__providedBy__, ClassProvides - ) # pylint:disable=no-member - self.assertIsInstance( - Foo.__provides__, ClassProvides - ) # pylint:disable=no-member - self.assertEqual( - Foo.__provides__, Foo.__providedBy__ - ) # pylint:disable=no-member - - def test_w_existing_Implements(self): - from zope.interface.declarations import Implements - impl = Implements() - - class Foo: - __implemented__ = impl - - self.assertIs(self._callFUT(Foo), impl) - - def test_super_when_base_implements_interface(self): - from zope.interface import Interface - from zope.interface.declarations import implementer - - class IBase(Interface): - pass - - class IDerived(IBase): - pass - - @implementer(IBase) - class Base: - pass - - @implementer(IDerived) - class Derived(Base): - pass - - self.assertEqual(list(self._callFUT(Derived)), [IDerived, IBase]) - sup = super(Derived, Derived) - self.assertEqual(list(self._callFUT(sup)), [IBase]) - - def test_super_when_base_implements_interface_diamond(self): - from zope.interface import Interface - from zope.interface.declarations import implementer - - class IBase(Interface): - pass - - class IDerived(IBase): - pass - - @implementer(IBase) - class Base: - pass - - class Child1(Base): - pass - - class Child2(Base): - pass - - @implementer(IDerived) - class Derived(Child1, Child2): - pass - - self.assertEqual(list(self._callFUT(Derived)), [IDerived, IBase]) - sup = super(Derived, Derived) - self.assertEqual(list(self._callFUT(sup)), [IBase]) - - def test_super_when_parent_implements_interface_diamond(self): - from zope.interface import Interface - from zope.interface.declarations import implementer - - class IBase(Interface): - pass - - class IDerived(IBase): - pass - - class Base: - pass - - class Child1(Base): - pass - - @implementer(IBase) - class Child2(Base): - pass - - @implementer(IDerived) - class Derived(Child1, Child2): - pass - - self.assertEqual( - Derived.__mro__, (Derived, Child1, Child2, Base, object) - ) - self.assertEqual(list(self._callFUT(Derived)), [IDerived, IBase]) - sup = super(Derived, Derived) - fut = self._callFUT(sup) - self.assertEqual(list(fut), [IBase]) - self.assertIsNone(fut._dependents) - - def test_super_when_base_doesnt_implement_interface(self): - from zope.interface import Interface - from zope.interface.declarations import implementer - - class IBase(Interface): - pass - - class IDerived(IBase): - pass - - class Base: - pass - - @implementer(IDerived) - class Derived(Base): - pass - - self.assertEqual(list(self._callFUT(Derived)), [IDerived]) - - sup = super(Derived, Derived) - self.assertEqual(list(self._callFUT(sup)), []) - - def test_super_when_base_is_object(self): - from zope.interface import Interface - from zope.interface.declarations import implementer - - class IBase(Interface): - pass - - class IDerived(IBase): - pass - - @implementer(IDerived) - class Derived: - pass - - self.assertEqual(list(self._callFUT(Derived)), [IDerived]) - - sup = super(Derived, Derived) - self.assertEqual(list(self._callFUT(sup)), []) - - def test_super_multi_level_multi_inheritance(self): - from zope.interface import Interface - from zope.interface.declarations import implementer - - class IBase(Interface): - pass - - class IM1(Interface): - pass - - class IM2(Interface): - pass - - class IDerived(IBase): - pass - - class IUnrelated(Interface): - pass - - @implementer(IBase) - class Base: - pass - - @implementer(IM1) - class M1(Base): - pass - - @implementer(IM2) - class M2(Base): - pass - - @implementer(IDerived, IUnrelated) - class Derived(M1, M2): - pass - - d = Derived - sd = super(Derived, Derived) - sm1 = super(M1, Derived) - sm2 = super(M2, Derived) - - self.assertEqual(list(self._callFUT(d)), - [IDerived, IUnrelated, IM1, IBase, IM2]) - self.assertEqual(list(self._callFUT(sd)), - [IM1, IBase, IM2]) - self.assertEqual(list(self._callFUT(sm1)), - [IM2, IBase]) - self.assertEqual(list(self._callFUT(sm2)), - [IBase]) - - -class Test_implementedBy(Test_implementedByFallback, - OptimizationTestMixin): - # Repeat tests for C optimizations - - def _getTargetClass(self): - from zope.interface.declarations import implementedBy - return implementedBy - - -class _ImplementsTestMixin: - FUT_SETS_PROVIDED_BY = True - - def _callFUT(self, cls, iface): - # Declare that *cls* implements *iface*; return *cls* - raise NotImplementedError - - def _check_implementer(self, Foo, - orig_spec=None, - spec_name=__name__ + '.Foo', - inherit="not given"): - from zope.interface.declarations import ClassProvides - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - - returned = self._callFUT(Foo, IFoo) - - self.assertIs(returned, Foo) - spec = Foo.__implemented__ - if orig_spec is not None: - self.assertIs(spec, orig_spec) - - self.assertEqual(spec.__name__, - spec_name) - inherit = Foo if inherit == "not given" else inherit - self.assertIs(spec.inherit, inherit) - self.assertIs(Foo.__implemented__, spec) - if self.FUT_SETS_PROVIDED_BY: - self.assertIsInstance(Foo.__providedBy__, ClassProvides) - self.assertIsInstance(Foo.__provides__, ClassProvides) - self.assertEqual(Foo.__provides__, Foo.__providedBy__) - - return Foo, IFoo - - def test_class(self): - - class Foo: - pass - - self._check_implementer(Foo) - - -class Test_classImplementsOnly(_ImplementsTestMixin, unittest.TestCase): - FUT_SETS_PROVIDED_BY = False - - def _callFUT(self, cls, iface): - from zope.interface.declarations import classImplementsOnly - classImplementsOnly(cls, iface) - return cls - - def test_w_existing_Implements(self): - from zope.interface.declarations import Implements - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar') - impl = Implements(IFoo) - impl.declared = (IFoo,) - - class Foo: - __implemented__ = impl - - impl.inherit = Foo - self._callFUT(Foo, IBar) - # Same spec, now different values - self.assertIs(Foo.__implemented__, impl) - self.assertEqual(impl.inherit, None) - self.assertEqual(impl.declared, (IBar,)) - - def test_class(self): - from zope.interface.declarations import Implements - from zope.interface.interface import InterfaceClass - IBar = InterfaceClass('IBar') - old_spec = Implements(IBar) - - class Foo: - __implemented__ = old_spec - - self._check_implementer(Foo, old_spec, '?', inherit=None) - - def test_redundant_with_super_still_implements(self): - Base, IBase = self._check_implementer( - type('Foo', (object,), {}), - inherit=None, - ) - - class Child(Base): - pass - - self._callFUT(Child, IBase) - self.assertTrue(IBase.implementedBy(Child)) - - -class Test_classImplements(_ImplementsTestMixin, unittest.TestCase): - - def _callFUT(self, cls, iface): - from zope.interface.declarations import classImplements - result = classImplements( - cls, iface - ) # pylint:disable=assignment-from-no-return - self.assertIsNone(result) - return cls - - def __check_implementer_redundant(self, Base): - # If we @implementer exactly what was already present, we write no - # declared attributes on the parent (we still set everything, though) - Base, IBase = self._check_implementer(Base) - - class Child(Base): - pass - - returned = self._callFUT(Child, IBase) - self.assertIn('__implemented__', returned.__dict__) - self.assertNotIn('__providedBy__', returned.__dict__) - self.assertIn('__provides__', returned.__dict__) - - spec = Child.__implemented__ - self.assertEqual(spec.declared, ()) - self.assertEqual(spec.inherit, Child) - - self.assertTrue(IBase.providedBy(Child())) - - def test_redundant_implementer_empty_class_declarations(self): - - class Foo: - pass - - self.__check_implementer_redundant(Foo) - - def test_redundant_implementer_Interface(self): - from zope.interface import Interface - from zope.interface import implementedBy - from zope.interface import ro - from zope.interface.tests.test_ro import C3Setting - - class Foo: - pass - - with C3Setting(ro.C3.STRICT_IRO, False): - self._callFUT(Foo, Interface) - self.assertEqual(list(implementedBy(Foo)), [Interface]) - - class Baz(Foo): - pass - - self._callFUT(Baz, Interface) - self.assertEqual(list(implementedBy(Baz)), [Interface]) - - def _order_for_two(self, applied_first, applied_second): - return (applied_first, applied_second) - - def test_w_existing_Implements(self): - from zope.interface.declarations import Implements - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar') - impl = Implements(IFoo) - impl.declared = (IFoo,) - - class Foo: - __implemented__ = impl - - impl.inherit = Foo - self._callFUT(Foo, IBar) - # Same spec, now different values - self.assertIs(Foo.__implemented__, impl) - self.assertEqual(impl.inherit, Foo) - self.assertEqual(impl.declared, - self._order_for_two(IFoo, IBar)) - - def test_w_existing_Implements_w_bases(self): - from zope.interface.declarations import Implements - from zope.interface.interface import InterfaceClass - IRoot = InterfaceClass('IRoot') - ISecondRoot = InterfaceClass('ISecondRoot') - IExtendsRoot = InterfaceClass('IExtendsRoot', (IRoot,)) - - impl_root = Implements.named('Root', IRoot) - impl_root.declared = (IRoot,) - - class Root1: - __implemented__ = impl_root - - class Root2: - __implemented__ = impl_root - - impl_extends_root = Implements.named('ExtendsRoot1', IExtendsRoot) - impl_extends_root.declared = (IExtendsRoot,) - - class ExtendsRoot(Root1, Root2): - __implemented__ = impl_extends_root - - impl_extends_root.inherit = ExtendsRoot - - self._callFUT(ExtendsRoot, ISecondRoot) - # Same spec, now different values - self.assertIs(ExtendsRoot.__implemented__, impl_extends_root) - self.assertEqual(impl_extends_root.inherit, ExtendsRoot) - self.assertEqual(impl_extends_root.declared, - self._order_for_two(IExtendsRoot, ISecondRoot,)) - self.assertEqual( - impl_extends_root.__bases__, - self._order_for_two(IExtendsRoot, ISecondRoot) + (impl_root,) - ) - - -class Test_classImplementsFirst(Test_classImplements): - - def _callFUT(self, cls, iface): - from zope.interface.declarations import classImplementsFirst - result = classImplementsFirst( - cls, iface - ) # pylint:disable=assignment-from-no-return - self.assertIsNone(result) - return cls - - def _order_for_two(self, applied_first, applied_second): - return (applied_second, applied_first) - - -class Test__implements_advice(unittest.TestCase): - - def _callFUT(self, *args, **kw): - from zope.interface.declarations import _implements_advice - return _implements_advice(*args, **kw) - - def test_no_existing_implements(self): - from zope.interface.declarations import Implements - from zope.interface.declarations import classImplements - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - - class Foo: - __implements_advice_data__ = ((IFoo,), classImplements) - - self._callFUT(Foo) - self.assertNotIn('__implements_advice_data__', Foo.__dict__) - self.assertIsInstance( - Foo.__implemented__, Implements - ) # pylint:disable=no-member - self.assertEqual( - list(Foo.__implemented__), [IFoo] - ) # pylint:disable=no-member - - -class Test_implementer(Test_classImplements): - - def _getTargetClass(self): - from zope.interface.declarations import implementer - return implementer - - def _makeOne(self, *args, **kw): - return self._getTargetClass()(*args, **kw) - - def _callFUT(self, cls, *ifaces): - decorator = self._makeOne(*ifaces) - return decorator(cls) - - def test_nonclass_cannot_assign_attr(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - decorator = self._makeOne(IFoo) - self.assertRaises(TypeError, decorator, object()) - - def test_nonclass_can_assign_attr(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - - class Foo: - pass - - foo = Foo() - decorator = self._makeOne(IFoo) - returned = decorator(foo) - self.assertIs(returned, foo) - spec = foo.__implemented__ # pylint:disable=no-member - self.assertEqual( - spec.__name__, 'zope.interface.tests.test_declarations.?' - ) - self.assertIsNone(spec.inherit,) - self.assertIs(foo.__implemented__, spec) # pylint:disable=no-member - - def test_does_not_leak_on_unique_classes(self): - # Make sure nothing is hanging on to the class or Implements - # object after they go out of scope. There was briefly a bug - # in 5.x that caused SpecificationBase._bases (in C) to not be - # traversed or cleared. - # https://github.com/zopefoundation/zope.interface/issues/216 - import gc - - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - - begin_count = len(gc.get_objects()) - - for _ in range(1900): - class TestClass: - pass - - self._callFUT(TestClass, IFoo) - - gc.collect() - - end_count = len(gc.get_objects()) - - # How many new objects might still be around? In all currently - # tested interpreters, there aren't any, so our counts should - # match exactly. When the bug existed, in a steady state, the loop - # would grow by two objects each iteration - fudge_factor = 0 - self.assertLessEqual(end_count, begin_count + fudge_factor) - - -class Test_implementer_only(Test_classImplementsOnly): - - def _getTargetClass(self): - from zope.interface.declarations import implementer_only - return implementer_only - - def _makeOne(self, *args, **kw): - return self._getTargetClass()(*args, **kw) - - def _callFUT(self, cls, iface): - decorator = self._makeOne(iface) - return decorator(cls) - - def test_function(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - decorator = self._makeOne(IFoo) - - def _function(): - raise NotImplementedError() - - self.assertRaises(ValueError, decorator, _function) - - def test_method(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass('IFoo') - decorator = self._makeOne(IFoo) - - class Bar: - def _method(self): - raise NotImplementedError() - - self.assertRaises(ValueError, decorator, Bar._method) - - -class ProvidesClassTests(unittest.TestCase): - - def _getTargetClass(self): - from zope.interface.declarations import ProvidesClass - return ProvidesClass - - def _makeOne(self, *args, **kw): - return self._getTargetClass()(*args, **kw) - - def test_simple_class_one_interface(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - class Foo: - pass - - spec = self._makeOne(Foo, IFoo) - self.assertEqual(list(spec), [IFoo]) - - def test___reduce__(self): - from zope.interface.declarations import Provides # the function - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - class Foo: - pass - - spec = self._makeOne(Foo, IFoo) - klass, args = spec.__reduce__() - self.assertIs(klass, Provides) - self.assertEqual(args, (Foo, IFoo)) - - def test___get___class(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - class Foo: - pass - - spec = self._makeOne(Foo, IFoo) - Foo.__provides__ = spec - self.assertIs(Foo.__provides__, spec) - - def test___get___instance(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - class Foo: - pass - - spec = self._makeOne(Foo, IFoo) - Foo.__provides__ = spec - - def _test(): - foo = Foo() - return foo.__provides__ - - self.assertRaises(AttributeError, _test) - - -class ProvidesClassStrictTests(ProvidesClassTests): - # Tests that require the strict C3 resolution order. - - def _getTargetClass(self): - ProvidesClass = super()._getTargetClass() - - class StrictProvides(ProvidesClass): - def _do_calculate_ro(self, base_mros): - return ProvidesClass._do_calculate_ro( - self, base_mros=base_mros, strict=True, - ) - - return StrictProvides - - def test_overlapping_interfaces_corrected(self): - # Giving Provides(cls, IFace), where IFace is already - # provided by cls, doesn't produce invalid resolution orders. - from zope.interface import Interface - from zope.interface import implementedBy - from zope.interface import implementer - - class IBase(Interface): - pass - - @implementer(IBase) - class Base: - pass - - spec = self._makeOne(Base, IBase) - self.assertEqual(spec.__sro__, ( - spec, - implementedBy(Base), - IBase, - implementedBy(object), - Interface - )) - - -class TestProvidesClassRepr(unittest.TestCase): - - def _getTargetClass(self): - from zope.interface.declarations import ProvidesClass - return ProvidesClass - - def _makeOne(self, *args, **kw): - return self._getTargetClass()(*args, **kw) - - def test__repr__(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - assert IFoo.__name__ == 'IFoo' - assert IFoo.__module__ == __name__ - assert repr(IFoo) == f'' - - IBar = InterfaceClass("IBar") - - inst = self._makeOne(type(self), IFoo, IBar) - self.assertEqual( - repr(inst), - "directlyProvides(TestProvidesClassRepr, IFoo, IBar)" - ) - - def test__repr__module_provides_typical_use(self): - # as created through a ``moduleProvides()`` statement - # in a module body - from zope.interface.tests import dummy - provides = dummy.__provides__ # pylint:disable=no-member - self.assertEqual( - repr(provides), - "directlyProvides(" - "sys.modules['zope.interface.tests.dummy'], " - "IDummyModule)" - ) - - def test__repr__module_after_pickle(self): - # It doesn't matter, these objects can't be pickled. - import pickle - - from zope.interface.tests import dummy - provides = dummy.__provides__ # pylint:disable=no-member - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - with self.assertRaises(pickle.PicklingError): - pickle.dumps(provides, proto) - - def test__repr__directlyProvides_module(self): - import sys - - from zope.interface.declarations import alsoProvides - from zope.interface.declarations import directlyProvides - from zope.interface.interface import InterfaceClass - from zope.interface.tests import dummy - - IFoo = InterfaceClass('IFoo') - IBar = InterfaceClass('IBar') - - orig_provides = dummy.__provides__ # pylint:disable=no-member - del dummy.__provides__ # pylint:disable=no-member - self.addCleanup(setattr, dummy, '__provides__', orig_provides) - - directlyProvides(dummy, IFoo) - provides = dummy.__provides__ # pylint:disable=no-member - - self.assertEqual( - repr(provides), - "directlyProvides(sys.modules['zope.interface.tests.dummy'], IFoo)" - ) - - alsoProvides(dummy, IBar) - provides = dummy.__provides__ # pylint:disable=no-member - - self.assertEqual( - repr(provides), - "directlyProvides(" - "sys.modules['zope.interface.tests.dummy'], " - "IFoo, IBar)" - ) - - # If we make this module also provide IFoo and IBar, then the repr - # lists both names. - my_module = sys.modules[__name__] - assert not hasattr(my_module, '__provides__') - - directlyProvides(my_module, IFoo, IBar) - self.addCleanup(delattr, my_module, '__provides__') - self.assertIs(my_module.__provides__, provides) - self.assertEqual( - repr(provides), - "directlyProvides(('zope.interface.tests.dummy', " - "'zope.interface.tests.test_declarations'), " - "IFoo, IBar)" - ) - - def test__repr__module_provides_cached_shared(self): - from zope.interface.declarations import ModuleType - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - inst = self._makeOne(ModuleType, IFoo) - inst._v_module_names += ('some.module',) - inst._v_module_names += ('another.module',) - self.assertEqual( - repr(inst), - "directlyProvides(('some.module', 'another.module'), IFoo)" - ) - - def test__repr__duplicate_names(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo", __module__='mod1') - IFoo2 = InterfaceClass("IFoo", __module__='mod2') - IBaz = InterfaceClass("IBaz") - - inst = self._makeOne(type(self), IFoo, IBaz, IFoo2) - self.assertEqual( - repr(inst), - "directlyProvides(TestProvidesClassRepr, IFoo, IBaz, mod2.IFoo)" - ) - - def test__repr__implementedBy_in_interfaces(self): - from zope.interface import Interface - from zope.interface import implementedBy - - class IFoo(Interface): - "Does nothing" - - class Bar: - "Does nothing" - - impl = implementedBy(type(self)) - - inst = self._makeOne(Bar, IFoo, impl) - self.assertEqual( - repr(inst), - 'directlyProvides(' - 'Bar, IFoo, ' - 'classImplements(TestProvidesClassRepr))' - ) - - def test__repr__empty_interfaces(self): - inst = self._makeOne(type(self)) - self.assertEqual( - repr(inst), - 'directlyProvides(TestProvidesClassRepr)', - ) - - def test__repr__non_class(self): - - def str___dont_call_me(): - self.fail("Should not call str") - - class Object: - __bases__ = () - __str__ = str___dont_call_me - - def __repr__(self): - return '' - - inst = self._makeOne(Object()) - self.assertEqual( - repr(inst), - 'directlyProvides()', - ) - - def test__repr__providedBy_from_class(self): - from zope.interface.declarations import implementer - from zope.interface.declarations import providedBy - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - @implementer(IFoo) - class Foo: - pass - - inst = providedBy(Foo()) - self.assertEqual( - repr(inst), - 'classImplements(Foo, IFoo)' - ) - - def test__repr__providedBy_alsoProvides(self): - from zope.interface.declarations import alsoProvides - from zope.interface.declarations import implementer - from zope.interface.declarations import providedBy - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - IBar = InterfaceClass("IBar") - - @implementer(IFoo) - class Foo: - pass - - foo = Foo() - alsoProvides(foo, IBar) - - inst = providedBy(foo) - self.assertEqual( - repr(inst), - "directlyProvides(Foo, IBar, classImplements(Foo, IFoo))" - ) - - -class Test_Provides(unittest.TestCase): - - def _callFUT(self, *args, **kw): - from zope.interface.declarations import Provides - return Provides(*args, **kw) - - def test_no_cached_spec(self): - from zope.interface import declarations - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - cache = {} - - class Foo: - pass - - with _Monkey(declarations, InstanceDeclarations=cache): - spec = self._callFUT(Foo, IFoo) - self.assertEqual(list(spec), [IFoo]) - self.assertIs(cache[(Foo, IFoo)], spec) - - def test_w_cached_spec(self): - from zope.interface import declarations - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - prior = object() - - class Foo: - pass - - cache = {(Foo, IFoo): prior} - with _Monkey(declarations, InstanceDeclarations=cache): - spec = self._callFUT(Foo, IFoo) - self.assertIs(spec, prior) - - -class Test_directlyProvides(unittest.TestCase): - - def _callFUT(self, *args, **kw): - from zope.interface.declarations import directlyProvides - return directlyProvides(*args, **kw) - - def test_w_normal_object(self): - from zope.interface.declarations import ProvidesClass - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - class Foo: - pass - - obj = Foo() - self._callFUT(obj, IFoo) - self.assertIsInstance( - obj.__provides__, ProvidesClass - ) # pylint:disable=no-member - self.assertEqual( - list(obj.__provides__), [IFoo] - ) # pylint:disable=no-member - - def test_w_class(self): - from zope.interface.declarations import ClassProvides - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - class Foo: - pass - - self._callFUT(Foo, IFoo) - self.assertIsInstance( - Foo.__provides__, ClassProvides - ) # pylint:disable=no-member - self.assertEqual( - list(Foo.__provides__), [IFoo] - ) # pylint:disable=no-member - - def test_w_classless_object(self): - from zope.interface.declarations import ProvidesClass - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - the_dict = {} - - class Foo: - def __getattribute__(self, name): - # Emulate object w/o any class - if name == '__class__': - return None - raise NotImplementedError(name) - - def __setattr__(self, name, value): - the_dict[name] = value - - obj = Foo() - self._callFUT(obj, IFoo) - self.assertIsInstance(the_dict['__provides__'], ProvidesClass) - self.assertEqual(list(the_dict['__provides__']), [IFoo]) - - -class Test_alsoProvides(unittest.TestCase): - - def _callFUT(self, *args, **kw): - from zope.interface.declarations import alsoProvides - return alsoProvides(*args, **kw) - - def test_wo_existing_provides(self): - from zope.interface.declarations import ProvidesClass - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - class Foo: - pass - - obj = Foo() - self._callFUT(obj, IFoo) - self.assertIsInstance( - obj.__provides__, ProvidesClass - ) # pylint:disable=no-member - self.assertEqual( - list(obj.__provides__), [IFoo] - ) # pylint:disable=no-member - - def test_w_existing_provides(self): - from zope.interface.declarations import ProvidesClass - from zope.interface.declarations import directlyProvides - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - IBar = InterfaceClass("IBar") - - class Foo: - pass - - obj = Foo() - directlyProvides(obj, IFoo) - self._callFUT(obj, IBar) - self.assertIsInstance( - obj.__provides__, ProvidesClass - ) # pylint:disable=no-member - self.assertEqual( - list(obj.__provides__), [IFoo, IBar] - ) # pylint:disable=no-member - - -class Test_noLongerProvides(unittest.TestCase): - - def _callFUT(self, *args, **kw): - from zope.interface.declarations import noLongerProvides - return noLongerProvides(*args, **kw) - - def test_wo_existing_provides(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - class Foo: - pass - - obj = Foo() - self._callFUT(obj, IFoo) - self.assertEqual( - list(obj.__provides__), [] - ) # pylint:disable=no-member - - def test_w_existing_provides_hit(self): - from zope.interface.declarations import directlyProvides - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - class Foo: - pass - - obj = Foo() - directlyProvides(obj, IFoo) - self._callFUT(obj, IFoo) - self.assertEqual( - list(obj.__provides__), [] - ) # pylint:disable=no-member - - def test_w_existing_provides_miss(self): - from zope.interface.declarations import directlyProvides - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - IBar = InterfaceClass("IBar") - - class Foo: - pass - - obj = Foo() - directlyProvides(obj, IFoo) - self._callFUT(obj, IBar) - self.assertEqual( - list(obj.__provides__), [IFoo] - ) # pylint:disable=no-member - - def test_w_iface_implemented_by_class(self): - from zope.interface.declarations import implementer - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - @implementer(IFoo) - class Foo: - pass - - obj = Foo() - self.assertRaises(ValueError, self._callFUT, obj, IFoo) - - -class ClassProvidesBaseFallbackTests(unittest.TestCase): - - def _getTargetClass(self): - # pylint:disable=no-name-in-module - from zope.interface.declarations import ClassProvidesBaseFallback - return ClassProvidesBaseFallback - - def _makeOne(self, klass, implements): - # Don't instantiate directly: the C version can't have attributes - # assigned. - - class Derived(self._getTargetClass()): - def __init__(self, k, i): - self._cls = k - self._implements = i - - return Derived(klass, implements) - - def test_w_same_class_via_class(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - class Foo: - pass - - cpbp = Foo.__provides__ = self._makeOne(Foo, IFoo) - self.assertIs(Foo.__provides__, cpbp) - - def test_w_same_class_via_instance(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - class Foo: - pass - - foo = Foo() - Foo.__provides__ = self._makeOne(Foo, IFoo) - self.assertIs(foo.__provides__, IFoo) - - def test_w_different_class(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - class Foo: - pass - - class Bar(Foo): - pass - - bar = Bar() - Foo.__provides__ = self._makeOne(Foo, IFoo) - self.assertRaises(AttributeError, getattr, Bar, '__provides__') - self.assertRaises(AttributeError, getattr, bar, '__provides__') - - -class ClassProvidesBaseTests( - OptimizationTestMixin, - ClassProvidesBaseFallbackTests, - SubclassableMixin, -): - # Repeat tests for C optimizations - - def _getTargetClass(self): - from zope.interface.declarations import ClassProvidesBase - return ClassProvidesBase - - def _getFallbackClass(self): - # pylint:disable=no-name-in-module - from zope.interface.declarations import ClassProvidesBaseFallback - return ClassProvidesBaseFallback - - -class ClassProvidesTests(unittest.TestCase): - - def _getTargetClass(self): - from zope.interface.declarations import ClassProvides - return ClassProvides - - def _makeOne(self, *args, **kw): - return self._getTargetClass()(*args, **kw) - - def test_w_simple_metaclass(self): - from zope.interface.declarations import implementer - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - IBar = InterfaceClass("IBar") - - @implementer(IFoo) - class Foo: - pass - - cp = Foo.__provides__ = self._makeOne(Foo, type(Foo), IBar) - self.assertIs(Foo.__provides__, cp) - self.assertEqual(list(Foo().__provides__), [IFoo]) - - def test___reduce__(self): - from zope.interface.declarations import implementer - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - IBar = InterfaceClass("IBar") - - @implementer(IFoo) - class Foo: - pass - - cp = Foo.__provides__ = self._makeOne(Foo, type(Foo), IBar) - self.assertEqual(cp.__reduce__(), - (type(cp), (Foo, type(Foo), IBar))) - - -class ClassProvidesStrictTests(ClassProvidesTests): - # Tests that require the strict C3 resolution order. - - def _getTargetClass(self): - ClassProvides = super()._getTargetClass() - - class StrictClassProvides(ClassProvides): - def _do_calculate_ro(self, base_mros): - return ClassProvides._do_calculate_ro( - self, base_mros=base_mros, strict=True - ) - - return StrictClassProvides - - def test_overlapping_interfaces_corrected(self): - # Giving ClassProvides(cls, metaclass, IFace), where IFace is already - # provided by metacls, doesn't produce invalid resolution orders. - from zope.interface import Interface - from zope.interface import implementedBy - from zope.interface import implementer - - class IBase(Interface): - pass - - @implementer(IBase) - class metaclass(type): - pass - - cls = metaclass( - 'cls', - (object,), - {} - ) - - spec = self._makeOne(cls, metaclass, IBase) - self.assertEqual(spec.__sro__, ( - spec, - implementedBy(metaclass), - IBase, - implementedBy(type), - implementedBy(object), - Interface - )) - - -class TestClassProvidesRepr(unittest.TestCase): - - def _getTargetClass(self): - from zope.interface.declarations import ClassProvides - return ClassProvides - - def _makeOne(self, *args, **kw): - return self._getTargetClass()(*args, **kw) - - def test__repr__empty(self): - inst = self._makeOne(type(self), type) - self.assertEqual( - repr(inst), - "directlyProvides(TestClassProvidesRepr)" - ) - - def test__repr__providing_one(self): - from zope.interface import Interface - - class IFoo(Interface): - "Does nothing" - - inst = self._makeOne(type(self), type, IFoo) - self.assertEqual( - repr(inst), - "directlyProvides(TestClassProvidesRepr, IFoo)" - ) - - def test__repr__duplicate_names(self): - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo", __module__='mod1') - IFoo2 = InterfaceClass("IFoo", __module__='mod2') - IBaz = InterfaceClass("IBaz") - - inst = self._makeOne(type(self), type, IFoo, IBaz, IFoo2) - self.assertEqual( - repr(inst), - "directlyProvides(TestClassProvidesRepr, IFoo, IBaz, mod2.IFoo)" - ) - - def test__repr__implementedBy(self): - from zope.interface.declarations import implementedBy - from zope.interface.declarations import implementer - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - @implementer(IFoo) - class Foo: - pass - - inst = implementedBy(Foo) - self.assertEqual( - repr(inst), - 'classImplements(Foo, IFoo)' - ) - - def test__repr__implementedBy_generic_callable(self): - from zope.interface.declarations import implementedBy - - # We can't get a __name__ by default, so we get a - # module name and a question mark - class Callable: - def __call__(self): - return self - - inst = implementedBy(Callable()) - self.assertEqual( - repr(inst), - f'classImplements({__name__}.?)' - ) - - c = Callable() - c.__name__ = 'Callable' - inst = implementedBy(c) - self.assertEqual( - repr(inst), - 'classImplements(Callable)' - ) - - -class Test_directlyProvidedBy(unittest.TestCase): - - def _callFUT(self, *args, **kw): - from zope.interface.declarations import directlyProvidedBy - return directlyProvidedBy(*args, **kw) - - def test_wo_declarations_in_class_or_instance(self): - - class Foo: - pass - - foo = Foo() - self.assertEqual(list(self._callFUT(foo)), []) - - def test_w_declarations_in_class_but_not_instance(self): - from zope.interface.declarations import implementer - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - @implementer(IFoo) - class Foo: - pass - - foo = Foo() - self.assertEqual(list(self._callFUT(foo)), []) - - def test_w_declarations_in_instance_but_not_class(self): - from zope.interface.declarations import directlyProvides - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - class Foo: - pass - - foo = Foo() - directlyProvides(foo, IFoo) - self.assertEqual(list(self._callFUT(foo)), [IFoo]) - - def test_w_declarations_in_instance_and_class(self): - from zope.interface.declarations import directlyProvides - from zope.interface.declarations import implementer - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - IBar = InterfaceClass("IBar") - - @implementer(IFoo) - class Foo: - pass - - foo = Foo() - directlyProvides(foo, IBar) - self.assertEqual(list(self._callFUT(foo)), [IBar]) - - -class Test_provider(unittest.TestCase): - - def _getTargetClass(self): - from zope.interface.declarations import provider - return provider - - def _makeOne(self, *args, **kw): - return self._getTargetClass()(*args, **kw) - - def test_w_class(self): - from zope.interface.declarations import ClassProvides - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - @self._makeOne(IFoo) - class Foo: - pass - - self.assertIsInstance( - Foo.__provides__, ClassProvides - ) # pylint:disable=no-member - self.assertEqual( - list(Foo.__provides__), [IFoo] - ) # pylint:disable=no-member - - -class Test_moduleProvides(unittest.TestCase): - # pylint:disable=exec-used - - def test_called_from_function(self): - from zope.interface.declarations import moduleProvides - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - globs = {'__name__': 'zope.interface.tests.foo', - 'moduleProvides': moduleProvides, 'IFoo': IFoo} - locs = {} - CODE = "\n".join([ - 'def foo():', - ' moduleProvides(IFoo)' - ]) - exec(CODE, globs, locs) - foo = locs['foo'] - self.assertRaises(TypeError, foo) - - def test_called_from_class(self): - from zope.interface.declarations import moduleProvides - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - globs = {'__name__': 'zope.interface.tests.foo', - 'moduleProvides': moduleProvides, 'IFoo': IFoo} - locs = {} - CODE = "\n".join([ - 'class Foo(object):', - ' moduleProvides(IFoo)', - ]) - with self.assertRaises(TypeError): - exec(CODE, globs, locs) - - def test_called_once_from_module_scope(self): - from zope.interface.declarations import moduleProvides - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - globs = {'__name__': 'zope.interface.tests.foo', - 'moduleProvides': moduleProvides, 'IFoo': IFoo} - CODE = "\n".join([ - 'moduleProvides(IFoo)', - ]) - exec(CODE, globs) - spec = globs['__provides__'] - self.assertEqual(list(spec), [IFoo]) - - def test_called_twice_from_module_scope(self): - from zope.interface.declarations import moduleProvides - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - globs = {'__name__': 'zope.interface.tests.foo', - 'moduleProvides': moduleProvides, 'IFoo': IFoo} - - CODE = "\n".join([ - 'moduleProvides(IFoo)', - 'moduleProvides(IFoo)', - ]) - with self.assertRaises(TypeError): - exec(CODE, globs) - - -class Test_getObjectSpecificationFallback(unittest.TestCase): - - def _getFallbackClass(self): - # pylint:disable=no-name-in-module - from zope.interface.declarations import getObjectSpecificationFallback - return getObjectSpecificationFallback - - _getTargetClass = _getFallbackClass - - def _callFUT(self, *args, **kw): - return self._getTargetClass()(*args, **kw) - - def test_wo_existing_provides_classless(self): - the_dict = {} - - class Foo: - def __getattribute__(self, name): - # Emulate object w/o any class - if name == '__class__': - raise AttributeError(name) - try: - return the_dict[name] - except KeyError: - raise AttributeError(name) - - def __setattr__(self, name, value): - raise NotImplementedError() - - foo = Foo() - spec = self._callFUT(foo) - self.assertEqual(list(spec), []) - - def test_existing_provides_is_spec(self): - from zope.interface.declarations import directlyProvides - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - def foo(): - raise NotImplementedError() - - directlyProvides(foo, IFoo) - spec = self._callFUT(foo) - self.assertIs(spec, foo.__provides__) # pylint:disable=no-member - - def test_existing_provides_is_not_spec(self): - - def foo(): - raise NotImplementedError() - - foo.__provides__ = object() # not a valid spec - spec = self._callFUT(foo) - self.assertEqual(list(spec), []) - - def test_existing_provides(self): - from zope.interface.declarations import directlyProvides - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - class Foo: - pass - - foo = Foo() - directlyProvides(foo, IFoo) - spec = self._callFUT(foo) - self.assertEqual(list(spec), [IFoo]) - - def test_wo_provides_on_class_w_implements(self): - from zope.interface.declarations import implementer - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - @implementer(IFoo) - class Foo: - pass - - foo = Foo() - spec = self._callFUT(foo) - self.assertEqual(list(spec), [IFoo]) - - def test_wo_provides_on_class_wo_implements(self): - - class Foo: - pass - - foo = Foo() - spec = self._callFUT(foo) - self.assertEqual(list(spec), []) - - def test_catches_only_AttributeError_on_provides(self): - MissingSomeAttrs.test_raises( - self, self._callFUT, expected_missing='__provides__' - ) - - def test_catches_only_AttributeError_on_class(self): - MissingSomeAttrs.test_raises( - self, - self._callFUT, - expected_missing='__class__', - __provides__=None, - ) - - def test_raises_AttrError_w_provides_fails_type_check_AttrError(self): - # isinstance(ob.__provides__, SpecificationBase) is not - # protected inside any kind of block. - - class Foo: - __provides__ = MissingSomeAttrs(AttributeError) - - # isinstance() ignores AttributeError on __class__ - self._callFUT(Foo()) - - def test_raises_AttrError_w_provides_fails_type_check_RuntimeError(self): - # isinstance(ob.__provides__, SpecificationBase) is not - # protected inside any kind of block. - class Foo: - __provides__ = MissingSomeAttrs(RuntimeError) - - with self.assertRaises(RuntimeError) as exc: - self._callFUT(Foo()) - - self.assertEqual('__class__', exc.exception.args[0]) - - -class Test_getObjectSpecification(Test_getObjectSpecificationFallback, - OptimizationTestMixin): - # Repeat tests for C optimizations - - def _getTargetClass(self): - from zope.interface.declarations import getObjectSpecification - return getObjectSpecification - - -class Test_providedByFallback(unittest.TestCase): - - def _getFallbackClass(self): - # pylint:disable=no-name-in-module - from zope.interface.declarations import providedByFallback - return providedByFallback - - _getTargetClass = _getFallbackClass - - def _callFUT(self, *args, **kw): - return self._getTargetClass()(*args, **kw) - - def test_wo_providedBy_on_class_wo_implements(self): - - class Foo: - pass - - foo = Foo() - spec = self._callFUT(foo) - self.assertEqual(list(spec), []) - - def test_w_providedBy_valid_spec(self): - from zope.interface.declarations import Provides - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - class Foo: - pass - - foo = Foo() - foo.__providedBy__ = Provides(Foo, IFoo) - spec = self._callFUT(foo) - self.assertEqual(list(spec), [IFoo]) - - def test_w_providedBy_invalid_spec(self): - - class Foo: - pass - - foo = Foo() - foo.__providedBy__ = object() - spec = self._callFUT(foo) - self.assertEqual(list(spec), []) - - def test_w_providedBy_invalid_spec_class_w_implements(self): - from zope.interface.declarations import implementer - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - @implementer(IFoo) - class Foo: - pass - - foo = Foo() - foo.__providedBy__ = object() - spec = self._callFUT(foo) - self.assertEqual(list(spec), [IFoo]) - - def test_w_providedBy_invalid_spec_w_provides_no_provides_on_class(self): - - class Foo: - pass - - foo = Foo() - foo.__providedBy__ = object() - expected = foo.__provides__ = object() - spec = self._callFUT(foo) - self.assertIs(spec, expected) - - def test_w_providedBy_invalid_spec_w_provides_diff_provides_on_class(self): - - class Foo: - pass - - foo = Foo() - foo.__providedBy__ = object() - expected = foo.__provides__ = object() - Foo.__provides__ = object() - spec = self._callFUT(foo) - self.assertIs(spec, expected) - - def test_w_providedBy_invalid_spec_w_provides_same_provides_on_class(self): - from zope.interface.declarations import implementer - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - @implementer(IFoo) - class Foo: - pass - - foo = Foo() - foo.__providedBy__ = object() - foo.__provides__ = Foo.__provides__ = object() - spec = self._callFUT(foo) - self.assertEqual(list(spec), [IFoo]) - - def test_super_when_base_implements_interface(self): - from zope.interface import Interface - from zope.interface.declarations import implementer - - class IBase(Interface): - pass - - class IDerived(IBase): - pass - - @implementer(IBase) - class Base: - pass - - @implementer(IDerived) - class Derived(Base): - pass - - derived = Derived() - self.assertEqual(list(self._callFUT(derived)), [IDerived, IBase]) - - sup = super(Derived, derived) - fut = self._callFUT(sup) - self.assertIsNone(fut._dependents) - self.assertEqual(list(fut), [IBase]) - - def test_super_when_base_doesnt_implement_interface(self): - from zope.interface import Interface - from zope.interface.declarations import implementer - - class IBase(Interface): - pass - - class IDerived(IBase): - pass - - class Base: - pass - - @implementer(IDerived) - class Derived(Base): - pass - - derived = Derived() - self.assertEqual(list(self._callFUT(derived)), [IDerived]) - - sup = super(Derived, derived) - self.assertEqual(list(self._callFUT(sup)), []) - - def test_super_when_base_is_object(self): - from zope.interface import Interface - from zope.interface.declarations import implementer - - class IBase(Interface): - pass - - class IDerived(IBase): - pass - - @implementer(IDerived) - class Derived: - pass - - derived = Derived() - self.assertEqual(list(self._callFUT(derived)), [IDerived]) - - sup = super(Derived, derived) - fut = self._callFUT(sup) - self.assertIsNone(fut._dependents) - self.assertEqual(list(fut), []) - - def test_super_when_object_directly_provides(self): - from zope.interface import Interface - from zope.interface.declarations import directlyProvides - from zope.interface.declarations import implementer - - class IBase(Interface): - pass - - class IDerived(IBase): - pass - - @implementer(IBase) - class Base: - pass - - class Derived(Base): - pass - - derived = Derived() - self.assertEqual(list(self._callFUT(derived)), [IBase]) - - directlyProvides(derived, IDerived) - self.assertEqual(list(self._callFUT(derived)), [IDerived, IBase]) - - sup = super(Derived, derived) - fut = self._callFUT(sup) - self.assertIsNone(fut._dependents) - self.assertEqual(list(fut), [IBase]) - - def test_super_multi_level_multi_inheritance(self): - from zope.interface import Interface - from zope.interface.declarations import implementer - - class IBase(Interface): - pass - - class IM1(Interface): - pass - - class IM2(Interface): - pass - - class IDerived(IBase): - pass - - class IUnrelated(Interface): - pass - - @implementer(IBase) - class Base: - pass - - @implementer(IM1) - class M1(Base): - pass - - @implementer(IM2) - class M2(Base): - pass - - @implementer(IDerived, IUnrelated) - class Derived(M1, M2): - pass - - d = Derived() - sd = super(Derived, d) - sm1 = super(M1, d) - sm2 = super(M2, d) - - self.assertEqual(list(self._callFUT(d)), - [IDerived, IUnrelated, IM1, IBase, IM2]) - self.assertEqual(list(self._callFUT(sd)), - [IM1, IBase, IM2]) - self.assertEqual(list(self._callFUT(sm1)), - [IM2, IBase]) - self.assertEqual(list(self._callFUT(sm2)), - [IBase]) - - def test_catches_only_AttributeError_on_providedBy(self): - MissingSomeAttrs.test_raises(self, self._callFUT, - expected_missing='__providedBy__', - __class__=object) - - def test_catches_only_AttributeError_on_class(self): - # isinstance() tries to get the __class__, which is non-obvious, - # so it must be protected too. - MissingSomeAttrs.test_raises( - self, self._callFUT, expected_missing='__class__') - - -class Test_providedBy(Test_providedByFallback, - OptimizationTestMixin): - # Repeat tests for C optimizations - - def _getTargetClass(self): - from zope.interface.declarations import providedBy - return providedBy - - -class ObjectSpecificationDescriptorFallbackTests(unittest.TestCase): - - def _getFallbackClass(self): - # pylint:disable=no-name-in-module - from zope.interface.declarations import \ - ObjectSpecificationDescriptorFallback - return ObjectSpecificationDescriptorFallback - - _getTargetClass = _getFallbackClass - - def _makeOne(self, *args, **kw): - return self._getTargetClass()(*args, **kw) - - def test_accessed_via_class(self): - from zope.interface.declarations import Provides - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - - class Foo: - pass - - Foo.__provides__ = Provides(Foo, IFoo) - Foo.__providedBy__ = self._makeOne() - self.assertEqual(list(Foo.__providedBy__), [IFoo]) - - def test_accessed_via_inst_wo_provides(self): - from zope.interface.declarations import Provides - from zope.interface.declarations import implementer - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - IBar = InterfaceClass("IBar") - - @implementer(IFoo) - class Foo: - pass - - Foo.__provides__ = Provides(Foo, IBar) - Foo.__providedBy__ = self._makeOne() - foo = Foo() - self.assertEqual(list(foo.__providedBy__), [IFoo]) - - def test_accessed_via_inst_w_provides(self): - from zope.interface.declarations import Provides - from zope.interface.declarations import directlyProvides - from zope.interface.declarations import implementer - from zope.interface.interface import InterfaceClass - IFoo = InterfaceClass("IFoo") - IBar = InterfaceClass("IBar") - IBaz = InterfaceClass("IBaz") - - @implementer(IFoo) - class Foo: - pass - - Foo.__provides__ = Provides(Foo, IBar) - Foo.__providedBy__ = self._makeOne() - foo = Foo() - directlyProvides(foo, IBaz) - self.assertEqual(list(foo.__providedBy__), [IBaz, IFoo]) - - def test_arbitrary_exception_accessing_provides_not_caught(self): - - class MyException(Exception): - pass - - class Foo: - __providedBy__ = self._makeOne() - - @property - def __provides__(self): - raise MyException - - foo = Foo() - with self.assertRaises(MyException): - getattr(foo, '__providedBy__') - - def test_AttributeError_accessing_provides_caught(self): - - class MyException(Exception): - pass - - class Foo: - __providedBy__ = self._makeOne() - - @property - def __provides__(self): - raise AttributeError - - foo = Foo() - provided = getattr(foo, '__providedBy__') - self.assertIsNotNone(provided) - - def test_None_in__provides__overrides(self): - from zope.interface import Interface - from zope.interface import implementer - - class IFoo(Interface): - pass - - @implementer(IFoo) - class Foo: - - @property - def __provides__(self): - return None - - Foo.__providedBy__ = self._makeOne() - - provided = getattr(Foo(), '__providedBy__') - self.assertIsNone(provided) - - -class ObjectSpecificationDescriptorTests( - ObjectSpecificationDescriptorFallbackTests, - OptimizationTestMixin, - SubclassableMixin, -): - # Repeat tests for C optimizations - - def _getTargetClass(self): - from zope.interface.declarations import ObjectSpecificationDescriptor - return ObjectSpecificationDescriptor - - -# Test _normalizeargs through its callers. - - -class _Monkey: - # context-manager for replacing module names in the scope of a test. - def __init__(self, module, **kw): - self.module = module - self.to_restore = {key: getattr(module, key) for key in kw} - for key, value in kw.items(): - setattr(module, key, value) - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - for key, value in self.to_restore.items(): - setattr(self.module, key, value) - - -class _MonkeyDict: - # context-manager for restoring a dict w/in a module in the scope of a - # test. - def __init__(self, module, attrname, **kw): - self.module = module - self.target = getattr(module, attrname) - self.to_restore = self.target.copy() - self.target.clear() - self.target.update(kw) - - def __enter__(self): - return self.target - - def __exit__(self, exc_type, exc_val, exc_tb): - self.target.clear() - self.target.update(self.to_restore) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_document.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_document.py deleted file mode 100644 index 82b175a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_document.py +++ /dev/null @@ -1,547 +0,0 @@ -############################################################################## -# -# Copyright (c) 2001, 2002 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Documentation tests. -""" -import sys -import unittest - - -class Test_asStructuredText(unittest.TestCase): - - def _callFUT(self, iface): - from zope.interface.document import asStructuredText - return asStructuredText(iface) - - def test_asStructuredText_no_docstring(self): - from zope.interface import Interface - EXPECTED = '\n\n'.join([ - "INoDocstring", - " Attributes:", - " Methods:", - "" - ]) - - class INoDocstring(Interface): - pass - - self.assertEqual(self._callFUT(INoDocstring), EXPECTED) - - def test_asStructuredText_empty_with_docstring(self): - from zope.interface import Interface - EXPECTED = '\n\n'.join([ - "IEmpty", - " This is an empty interface.", - " Attributes:", - " Methods:", - "" - ]) - - class IEmpty(Interface): - """ This is an empty interface. - """ - - self.assertEqual(self._callFUT(IEmpty), EXPECTED) - - def test_asStructuredText_empty_with_multiline_docstring(self): - from zope.interface import Interface - - # In Python 3.13+, compiler strips indents from docstrings - indent = " " * 12 if sys.version_info < (3, 13) else "" - - EXPECTED = '\n'.join([ - "IEmpty", - "", - " This is an empty interface.", - " ", - (f"{indent} It can be used to annotate any class or object, " - "because it promises"), # noqa E127 - f"{indent} nothing.", - "", - " Attributes:", - "", - " Methods:", - "", - "" - ]) - - class IEmpty(Interface): - """ This is an empty interface. - - It can be used to annotate any class or object, because it promises - nothing. - """ - - self.assertEqual(self._callFUT(IEmpty), EXPECTED) - - def test_asStructuredText_with_attribute_no_docstring(self): - from zope.interface import Attribute - from zope.interface import Interface - EXPECTED = '\n\n'.join([ - "IHasAttribute", - " This interface has an attribute.", - " Attributes:", - " an_attribute -- no documentation", - " Methods:", - "" - ]) - - class IHasAttribute(Interface): - """ This interface has an attribute. - """ - an_attribute = Attribute('an_attribute') - - self.assertEqual(self._callFUT(IHasAttribute), EXPECTED) - - def test_asStructuredText_with_attribute_with_docstring(self): - from zope.interface import Attribute - from zope.interface import Interface - EXPECTED = '\n\n'.join([ - "IHasAttribute", - " This interface has an attribute.", - " Attributes:", - " an_attribute -- This attribute is documented.", - " Methods:", - "" - ]) - - class IHasAttribute(Interface): - """ This interface has an attribute. - """ - an_attribute = Attribute('an_attribute', - 'This attribute is documented.') - - self.assertEqual(self._callFUT(IHasAttribute), EXPECTED) - - def test_asStructuredText_with_method_no_args_no_docstring(self): - from zope.interface import Interface - EXPECTED = '\n\n'.join([ - "IHasMethod", - " This interface has a method.", - " Attributes:", - " Methods:", - " aMethod() -- no documentation", - "" - ]) - - class IHasMethod(Interface): - """ This interface has a method. - """ - def aMethod(): - pass # pragma: no cover - - self.assertEqual(self._callFUT(IHasMethod), EXPECTED) - - def test_asStructuredText_with_method_positional_args_no_docstring(self): - from zope.interface import Interface - EXPECTED = '\n\n'.join([ - "IHasMethod", - " This interface has a method.", - " Attributes:", - " Methods:", - " aMethod(first, second) -- no documentation", - "" - ]) - - class IHasMethod(Interface): - """ This interface has a method. - """ - def aMethod(first, second): - pass # pragma: no cover - - self.assertEqual(self._callFUT(IHasMethod), EXPECTED) - - def test_asStructuredText_with_method_starargs_no_docstring(self): - from zope.interface import Interface - EXPECTED = '\n\n'.join([ - "IHasMethod", - " This interface has a method.", - " Attributes:", - " Methods:", - " aMethod(first, second, *rest) -- no documentation", - "" - ]) - - class IHasMethod(Interface): - """ This interface has a method. - """ - def aMethod(first, second, *rest): - pass # pragma: no cover - - self.assertEqual(self._callFUT(IHasMethod), EXPECTED) - - def test_asStructuredText_with_method_kwargs_no_docstring(self): - from zope.interface import Interface - EXPECTED = '\n\n'.join([ - "IHasMethod", - " This interface has a method.", - " Attributes:", - " Methods:", - " aMethod(first, second, **kw) -- no documentation", - "" - ]) - - class IHasMethod(Interface): - """ This interface has a method. - """ - def aMethod(first, second, **kw): - pass # pragma: no cover - - self.assertEqual(self._callFUT(IHasMethod), EXPECTED) - - def test_asStructuredText_with_method_with_docstring(self): - from zope.interface import Interface - EXPECTED = '\n\n'.join([ - "IHasMethod", - " This interface has a method.", - " Attributes:", - " Methods:", - " aMethod() -- This method is documented.", - "" - ]) - - class IHasMethod(Interface): - """ This interface has a method. - """ - def aMethod(): - """This method is documented. - """ - - self.assertEqual(self._callFUT(IHasMethod), EXPECTED) - - def test_asStructuredText_derived_ignores_base(self): - from zope.interface import Attribute - from zope.interface import Interface - EXPECTED = '\n\n'.join([ - "IDerived", - " IDerived doc", - " This interface extends:", - " o IBase", - " Attributes:", - " attr1 -- no documentation", - " attr2 -- attr2 doc", - " Methods:", - " method3() -- method3 doc", - " method4() -- no documentation", - " method5() -- method5 doc", - "", - ]) - - class IBase(Interface): - def method1(): - """docstring""" - - def method2(): - """docstring""" - - class IDerived(IBase): - "IDerived doc" - attr1 = Attribute('attr1') - attr2 = Attribute('attr2', 'attr2 doc') - - def method3(): - "method3 doc" - - def method4(): - pass # pragma: no cover - - def method5(): - "method5 doc" - - self.assertEqual(self._callFUT(IDerived), EXPECTED) - - -class Test_asReStructuredText(unittest.TestCase): - - def _callFUT(self, iface): - from zope.interface.document import asReStructuredText - return asReStructuredText(iface) - - def test_asReStructuredText_no_docstring(self): - from zope.interface import Interface - EXPECTED = '\n\n'.join([ - "``INoDocstring``", - " Attributes:", - " Methods:", - "" - ]) - - class INoDocstring(Interface): - pass - - self.assertEqual(self._callFUT(INoDocstring), EXPECTED) - - def test_asReStructuredText_empty_with_docstring(self): - from zope.interface import Interface - EXPECTED = '\n\n'.join([ - "``IEmpty``", - " This is an empty interface.", - " Attributes:", - " Methods:", - "" - ]) - - class IEmpty(Interface): - """ This is an empty interface. - """ - - self.assertEqual(self._callFUT(IEmpty), EXPECTED) - - def test_asReStructuredText_empty_with_multiline_docstring(self): - from zope.interface import Interface - - # In Python 3.13+, compiler strips indents from docstrings - indent = " " * 12 if sys.version_info < (3, 13) else "" - - EXPECTED = '\n'.join([ - "``IEmpty``", - "", - " This is an empty interface.", - " ", - (f"{indent} It can be used to annotate any class or object, " - f"because it" - ), # noqa E124 - f"{indent} promises nothing.", - "", - " Attributes:", - "", - " Methods:", - "", - "" - ]) - - class IEmpty(Interface): - """ This is an empty interface. - - It can be used to annotate any class or object, because it - promises nothing. - """ - - self.assertEqual(self._callFUT(IEmpty), EXPECTED) - - def test_asReStructuredText_with_attribute_no_docstring(self): - from zope.interface import Attribute - from zope.interface import Interface - EXPECTED = '\n\n'.join([ - "``IHasAttribute``", - " This interface has an attribute.", - " Attributes:", - " ``an_attribute`` -- no documentation", - " Methods:", - "" - ]) - - class IHasAttribute(Interface): - """ This interface has an attribute. - """ - an_attribute = Attribute('an_attribute') - - self.assertEqual(self._callFUT(IHasAttribute), EXPECTED) - - def test_asReStructuredText_with_attribute_with_docstring(self): - from zope.interface import Attribute - from zope.interface import Interface - EXPECTED = '\n\n'.join([ - "``IHasAttribute``", - " This interface has an attribute.", - " Attributes:", - " ``an_attribute`` -- This attribute is documented.", - " Methods:", - "" - ]) - - class IHasAttribute(Interface): - """ This interface has an attribute. - """ - an_attribute = Attribute('an_attribute', - 'This attribute is documented.') - - self.assertEqual(self._callFUT(IHasAttribute), EXPECTED) - - def test_asReStructuredText_with_method_no_args_no_docstring(self): - from zope.interface import Interface - EXPECTED = '\n\n'.join([ - "``IHasMethod``", - " This interface has a method.", - " Attributes:", - " Methods:", - " ``aMethod()`` -- no documentation", - "" - ]) - - class IHasMethod(Interface): - """ This interface has a method. - """ - def aMethod(): - pass # pragma: no cover - - self.assertEqual(self._callFUT(IHasMethod), EXPECTED) - - def test_asReStructuredText_with_method_positional_args_no_docstring(self): - from zope.interface import Interface - EXPECTED = '\n\n'.join([ - "``IHasMethod``", - " This interface has a method.", - " Attributes:", - " Methods:", - " ``aMethod(first, second)`` -- no documentation", - "" - ]) - - class IHasMethod(Interface): - """ This interface has a method. - """ - def aMethod(first, second): - pass # pragma: no cover - - self.assertEqual(self._callFUT(IHasMethod), EXPECTED) - - def test_asReStructuredText_with_method_starargs_no_docstring(self): - from zope.interface import Interface - EXPECTED = '\n\n'.join([ - "``IHasMethod``", - " This interface has a method.", - " Attributes:", - " Methods:", - " ``aMethod(first, second, *rest)`` -- no documentation", - "" - ]) - - class IHasMethod(Interface): - """ This interface has a method. - """ - def aMethod(first, second, *rest): - pass # pragma: no cover - - self.assertEqual(self._callFUT(IHasMethod), EXPECTED) - - def test_asReStructuredText_with_method_kwargs_no_docstring(self): - from zope.interface import Interface - EXPECTED = '\n\n'.join([ - "``IHasMethod``", - " This interface has a method.", - " Attributes:", - " Methods:", - " ``aMethod(first, second, **kw)`` -- no documentation", - "" - ]) - - class IHasMethod(Interface): - """ This interface has a method. - """ - def aMethod(first, second, **kw): - pass # pragma: no cover - - self.assertEqual(self._callFUT(IHasMethod), EXPECTED) - - def test_asReStructuredText_with_method_with_docstring(self): - from zope.interface import Interface - EXPECTED = '\n\n'.join([ - "``IHasMethod``", - " This interface has a method.", - " Attributes:", - " Methods:", - " ``aMethod()`` -- This method is documented.", - "" - ]) - - class IHasMethod(Interface): - """ This interface has a method. - """ - def aMethod(): - """This method is documented. - """ - - self.assertEqual(self._callFUT(IHasMethod), EXPECTED) - - def test_asReStructuredText_derived_ignores_base(self): - from zope.interface import Attribute - from zope.interface import Interface - EXPECTED = '\n\n'.join([ - "``IDerived``", - " IDerived doc", - " This interface extends:", - " o ``IBase``", - " Attributes:", - " ``attr1`` -- no documentation", - " ``attr2`` -- attr2 doc", - " Methods:", - " ``method3()`` -- method3 doc", - " ``method4()`` -- no documentation", - " ``method5()`` -- method5 doc", - "", - ]) - - class IBase(Interface): - def method1(): - pass # pragma: no cover - - def method2(): - pass # pragma: no cover - - class IDerived(IBase): - "IDerived doc" - attr1 = Attribute('attr1') - attr2 = Attribute('attr2', 'attr2 doc') - - def method3(): - "method3 doc" - - def method4(): - pass # pragma: no cover - - def method5(): - "method5 doc" - - self.assertEqual(self._callFUT(IDerived), EXPECTED) - - -class Test__justify_and_indent(unittest.TestCase): - - def _callFUT(self, text, level, **kw): - from zope.interface.document import _justify_and_indent - return _justify_and_indent(text, level, **kw) - - def test_simple_level_0(self): - LINES = ['Three blind mice', 'See how they run'] - text = '\n'.join(LINES) - self.assertEqual(self._callFUT(text, 0), text) - - def test_simple_level_1(self): - LINES = ['Three blind mice', 'See how they run'] - text = '\n'.join(LINES) - self.assertEqual(self._callFUT(text, 1), - '\n'.join([' ' + line for line in LINES])) - - def test_simple_level_2(self): - LINES = ['Three blind mice', 'See how they run'] - text = '\n'.join(LINES) - self.assertEqual(self._callFUT(text, 1), - '\n'.join([' ' + line for line in LINES])) - - def test_simple_w_CRLF(self): - LINES = ['Three blind mice', 'See how they run'] - text = '\r\n'.join(LINES) - self.assertEqual(self._callFUT(text, 1), - '\n'.join([' ' + line for line in LINES])) - - def test_with_munge(self): - TEXT = ("This is a piece of text longer than 15 characters, \n" - "and split across multiple lines.") - EXPECTED = (" This is a piece\n" - " of text longer\n" - " than 15 characters,\n" - " and split across\n" - " multiple lines.\n" - " ") - self.assertEqual(self._callFUT(TEXT, 1, munge=1, width=15), EXPECTED) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_element.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_element.py deleted file mode 100644 index b47b3b5..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_element.py +++ /dev/null @@ -1,33 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Test Element meta-class. -""" - -import unittest - -from zope.interface.interface import Element - - -class TestElement(unittest.TestCase): - - def test_taggedValues(self): - """Test that we can update tagged values of more than one element - """ - - e1 = Element("foo") - e2 = Element("bar") - e1.setTaggedValue("x", 1) - e2.setTaggedValue("x", 2) - self.assertEqual(e1.getTaggedValue("x"), 1) - self.assertEqual(e2.getTaggedValue("x"), 2) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_exceptions.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_exceptions.py deleted file mode 100644 index e48c00f..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_exceptions.py +++ /dev/null @@ -1,190 +0,0 @@ -############################################################################## -# -# Copyright (c) 2010 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -""" zope.interface.exceptions unit tests -""" -import unittest - - -def _makeIface(): - from zope.interface import Interface - - class IDummy(Interface): - pass - - return IDummy - - -class DoesNotImplementTests(unittest.TestCase): - - def _getTargetClass(self): - from zope.interface.exceptions import DoesNotImplement - return DoesNotImplement - - def _makeOne(self, *args): - iface = _makeIface() - return self._getTargetClass()(iface, *args) - - def test___str__(self): - dni = self._makeOne() - self.assertEqual( - str(dni), - "An object has failed to implement interface " - "zope.interface.tests.test_exceptions.IDummy: " - "Does not declaratively implement the interface." - ) - - def test___str__w_candidate(self): - dni = self._makeOne('candidate') - self.assertEqual( - str(dni), - "The object 'candidate' has failed to implement interface " - "zope.interface.tests.test_exceptions.IDummy: " - "Does not declaratively implement the interface." - ) - - -class BrokenImplementationTests(unittest.TestCase): - - def _getTargetClass(self): - from zope.interface.exceptions import BrokenImplementation - return BrokenImplementation - - def _makeOne(self, *args): - iface = _makeIface() - return self._getTargetClass()(iface, 'missing', *args) - - def test___str__(self): - dni = self._makeOne() - self.assertEqual( - str(dni), - 'An object has failed to implement interface ' - 'zope.interface.tests.test_exceptions.IDummy: ' - "The 'missing' attribute was not provided.") - - def test___str__w_candidate(self): - dni = self._makeOne('candidate') - self.assertEqual( - str(dni), - 'The object \'candidate\' has failed to implement interface ' - 'zope.interface.tests.test_exceptions.IDummy: ' - "The 'missing' attribute was not provided.") - - -def broken_function(): - """ - This is a global function with a simple argument list. - - It exists to be able to report the same information when - formatting signatures. - """ - - -class BrokenMethodImplementationTests(unittest.TestCase): - - def _getTargetClass(self): - from zope.interface.exceptions import BrokenMethodImplementation - return BrokenMethodImplementation - - message = 'I said so' - - def _makeOne(self, *args): - return self._getTargetClass()('aMethod', self.message, *args) - - def test___str__(self): - dni = self._makeOne() - self.assertEqual( - str(dni), - "An object has failed to implement interface : " - "The contract of 'aMethod' is violated because I said so." - ) - - def test___str__w_candidate_no_implementation(self): - dni = self._makeOne('some_function', '', 'candidate') - self.assertEqual( - str(dni), - "The object 'candidate' has failed to implement interface : " - "The contract of 'aMethod' is violated because I said so." - ) - - def test___str__w_candidate_w_implementation(self): - self.message = 'implementation is wonky' - dni = self._makeOne(broken_function, '', 'candidate') - self.assertEqual( - str(dni), - "The object 'candidate' has failed to implement interface : " - "The contract of 'aMethod' is violated because " - "'broken_function()' is wonky." - ) - - def test___str__w_candidate_w_implementation_not_callable(self): - self.message = 'implementation is not callable' - dni = self._makeOne(42, '', 'candidate') - self.assertEqual( - str(dni), - "The object 'candidate' has failed to implement interface : " - "The contract of 'aMethod' is violated because " - "'42' is not callable." - ) - - def test___repr__w_candidate(self): - dni = self._makeOne(None, 'candidate') - self.assertEqual( - repr(dni), - "BrokenMethodImplementation(" - "'aMethod', 'I said so', None, 'candidate')" - ) - - -class MultipleInvalidTests(unittest.TestCase): - - def _getTargetClass(self): - from zope.interface.exceptions import MultipleInvalid - return MultipleInvalid - - def _makeOne(self, excs): - iface = _makeIface() - return self._getTargetClass()(iface, 'target', excs) - - def test__str__(self): - from zope.interface.exceptions import BrokenMethodImplementation - excs = [ - BrokenMethodImplementation('aMethod', 'I said so'), - Exception("Regular exception") - ] - dni = self._makeOne(excs) - self.assertEqual( - str(dni), - "The object 'target' has failed to implement interface " - "zope.interface.tests.test_exceptions.IDummy:\n" - " The contract of 'aMethod' is violated because I said so\n" - " Regular exception" - ) - - def test__repr__(self): - from zope.interface.exceptions import BrokenMethodImplementation - excs = [ - BrokenMethodImplementation('aMethod', 'I said so'), - # Use multiple arguments to normalize repr; versions of Python - # prior to 3.7 add a trailing comma if there's just one. - Exception("Regular", "exception") - ] - dni = self._makeOne(excs) - self.assertEqual( - repr(dni), - "MultipleInvalid(" - "," - " 'target'," - " (BrokenMethodImplementation('aMethod', 'I said so')," - " Exception('Regular', 'exception')))" - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_interface.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_interface.py deleted file mode 100644 index 164add1..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_interface.py +++ /dev/null @@ -1,2871 +0,0 @@ -############################################################################## -# -# Copyright (c) 2001, 2002 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Test Interface implementation -""" -# Things we let slide because it's a test -# pylint:disable=protected-access -# pylint:disable=blacklisted-name -# pylint:disable=attribute-defined-outside-init -# pylint:disable=too-many-public-methods -# pylint:disable=too-many-lines -# pylint:disable=abstract-method -# pylint:disable=redefined-builtin -# pylint:disable=signature-differs -# pylint:disable=arguments-differ -# Things you get inheriting from Interface -# pylint:disable=inherit-non-class -# pylint:disable=no-self-argument -# pylint:disable=no-method-argument -# Things you get using methods of an Interface 'subclass' -# pylint:disable=no-value-for-parameter -import unittest - -from zope.interface.tests import CleanUp -from zope.interface.tests import MissingSomeAttrs -from zope.interface.tests import OptimizationTestMixin -from zope.interface.tests import SubclassableMixin - - -_marker = object() - - -class Test_invariant(unittest.TestCase): - - def test_w_single(self): - from zope.interface.interface import TAGGED_DATA - from zope.interface.interface import invariant - - def _check(*args, **kw): - raise NotImplementedError() - - class Foo: - invariant(_check) - - self.assertEqual(getattr(Foo, TAGGED_DATA, None), - {'invariants': [_check]}) - - def test_w_multiple(self): - from zope.interface.interface import TAGGED_DATA - from zope.interface.interface import invariant - - def _check(*args, **kw): - raise NotImplementedError() - - def _another_check(*args, **kw): - raise NotImplementedError() - - class Foo: - invariant(_check) - invariant(_another_check) - - self.assertEqual(getattr(Foo, TAGGED_DATA, None), - {'invariants': [_check, _another_check]}) - - -class Test_taggedValue(unittest.TestCase): - - def test_w_single(self): - from zope.interface.interface import TAGGED_DATA - from zope.interface.interface import taggedValue - - class Foo: - taggedValue('bar', ['baz']) - - self.assertEqual(getattr(Foo, TAGGED_DATA, None), - {'bar': ['baz']}) - - def test_w_multiple(self): - from zope.interface.interface import TAGGED_DATA - from zope.interface.interface import taggedValue - - class Foo: - taggedValue('bar', ['baz']) - taggedValue('qux', 'spam') - - self.assertEqual(getattr(Foo, TAGGED_DATA, None), - {'bar': ['baz'], 'qux': 'spam'}) - - def test_w_multiple_overwriting(self): - from zope.interface.interface import TAGGED_DATA - from zope.interface.interface import taggedValue - - class Foo: - taggedValue('bar', ['baz']) - taggedValue('qux', 'spam') - taggedValue('bar', 'frob') - - self.assertEqual(getattr(Foo, TAGGED_DATA, None), - {'bar': 'frob', 'qux': 'spam'}) - - -class ElementTests(unittest.TestCase): - - DEFAULT_NAME = 'AnElement' - - def _getTargetClass(self): - from zope.interface.interface import Element - return Element - - def _makeOne(self, name=None): - if name is None: - name = self.DEFAULT_NAME - return self._getTargetClass()(name) - - def test_ctor_defaults(self): - element = self._makeOne() - self.assertEqual(element.__name__, self.DEFAULT_NAME) - self.assertEqual(element.getName(), self.DEFAULT_NAME) - self.assertEqual(element.__doc__, '') - self.assertEqual(element.getDoc(), '') - self.assertEqual(list(element.getTaggedValueTags()), []) - - def test_ctor_no_doc_space_in_name(self): - element = self._makeOne('An Element') - self.assertEqual(element.__name__, None) - self.assertEqual(element.__doc__, 'An Element') - - def test_getTaggedValue_miss(self): - element = self._makeOne() - self.assertRaises(KeyError, element.getTaggedValue, 'nonesuch') - - def test_getDirectTaggedValueTags(self): - element = self._makeOne() - self.assertEqual([], list(element.getDirectTaggedValueTags())) - - element.setTaggedValue('foo', 'bar') - self.assertEqual(['foo'], list(element.getDirectTaggedValueTags())) - - def test_queryTaggedValue_miss(self): - element = self._makeOne() - self.assertEqual(element.queryTaggedValue('nonesuch'), None) - - def test_queryTaggedValue_miss_w_default(self): - element = self._makeOne() - self.assertEqual(element.queryTaggedValue('nonesuch', 'bar'), 'bar') - - def test_getDirectTaggedValue_miss(self): - element = self._makeOne() - self.assertRaises(KeyError, element.getDirectTaggedValue, 'nonesuch') - - def test_queryDirectTaggedValue_miss(self): - element = self._makeOne() - self.assertEqual(element.queryDirectTaggedValue('nonesuch'), None) - - def test_queryDirectTaggedValue_miss_w_default(self): - element = self._makeOne() - self.assertEqual( - element.queryDirectTaggedValue('nonesuch', 'bar'), 'bar' - ) - - def test_setTaggedValue(self): - element = self._makeOne() - element.setTaggedValue('foo', 'bar') - self.assertEqual(list(element.getTaggedValueTags()), ['foo']) - self.assertEqual(element.getTaggedValue('foo'), 'bar') - self.assertEqual(element.queryTaggedValue('foo'), 'bar') - - def test_verifies(self): - from zope.interface.interfaces import IElement - from zope.interface.verify import verifyObject - - element = self._makeOne() - verifyObject(IElement, element) - - -class GenericSpecificationBaseTests(unittest.TestCase): - # Tests that work with both implementations - def _getFallbackClass(self): - from zope.interface.interface import SpecificationBasePy - return SpecificationBasePy - - _getTargetClass = _getFallbackClass - - def _makeOne(self): - return self._getTargetClass()() - - def test_providedBy_miss(self): - from zope.interface import interface - from zope.interface.declarations import _empty - sb = self._makeOne() - - def _providedBy(obj): - return _empty - - with _Monkey(interface, providedBy=_providedBy): - self.assertFalse(sb.providedBy(object())) - - def test_implementedBy_miss(self): - from zope.interface import interface - from zope.interface.declarations import _empty - sb = self._makeOne() - - def _implementedBy(obj): - return _empty - - with _Monkey(interface, implementedBy=_implementedBy): - self.assertFalse(sb.implementedBy(object())) - - -class SpecificationBaseTests( - GenericSpecificationBaseTests, - OptimizationTestMixin, - SubclassableMixin, -): - # Tests that use the C implementation - - def _getTargetClass(self): - from zope.interface.interface import SpecificationBase - return SpecificationBase - - -class SpecificationBasePyTests(GenericSpecificationBaseTests): - # Tests that only work with the Python implementation - - def test___call___miss(self): - sb = self._makeOne() - sb._implied = {} # not defined by SpecificationBasePy - self.assertFalse(sb.isOrExtends(object())) - - def test___call___hit(self): - sb = self._makeOne() - testing = object() - sb._implied = {testing: {}} # not defined by SpecificationBasePy - self.assertTrue(sb(testing)) - - def test_isOrExtends_miss(self): - sb = self._makeOne() - sb._implied = {} # not defined by SpecificationBasePy - self.assertFalse(sb.isOrExtends(object())) - - def test_isOrExtends_hit(self): - sb = self._makeOne() - testing = object() - sb._implied = {testing: {}} # not defined by SpecificationBasePy - self.assertTrue(sb(testing)) - - def test_implementedBy_hit(self): - from zope.interface import interface - sb = self._makeOne() - - class _Decl: - _implied = {sb: {}} - - def _implementedBy(obj): - return _Decl() - - with _Monkey(interface, implementedBy=_implementedBy): - self.assertTrue(sb.implementedBy(object())) - - def test_providedBy_hit(self): - from zope.interface import interface - sb = self._makeOne() - - class _Decl: - _implied = {sb: {}} - - def _providedBy(obj): - return _Decl() - - with _Monkey(interface, providedBy=_providedBy): - self.assertTrue(sb.providedBy(object())) - - -class NameAndModuleComparisonTestsMixin(CleanUp): - - def _makeOneToCompare(self): - return self._makeOne('a', 'b') - - def __check_NotImplemented_comparison(self, name): - # Without the correct attributes of __name__ and __module__, - # comparison switches to the reverse direction. - - import operator - ib = self._makeOneToCompare() - op = getattr(operator, name) - meth = getattr(ib, '__%s__' % name) - - # If either the __name__ or __module__ attribute - # is missing from the other object, then we return - # NotImplemented. - class RaisesErrorOnMissing: - Exc = AttributeError - - def __getattribute__(self, name): - try: - return object.__getattribute__(self, name) - except AttributeError: - exc = RaisesErrorOnMissing.Exc - raise exc(name) - - class RaisesErrorOnModule(RaisesErrorOnMissing): - def __init__(self): - self.__name__ = 'foo' - - @property - def __module__(self): - raise AttributeError - - class RaisesErrorOnName(RaisesErrorOnMissing): - def __init__(self): - self.__module__ = 'foo' - - self.assertEqual(RaisesErrorOnModule().__name__, 'foo') - self.assertEqual(RaisesErrorOnName().__module__, 'foo') - with self.assertRaises(AttributeError): - getattr(RaisesErrorOnModule(), '__module__') - with self.assertRaises(AttributeError): - getattr(RaisesErrorOnName(), '__name__') - - for cls in RaisesErrorOnModule, RaisesErrorOnName: - self.assertIs(meth(cls()), NotImplemented) - - # If the other object has a comparison function, returning - # NotImplemented means Python calls it. - - class AllowsAnyComparison(RaisesErrorOnMissing): - def __eq__(self, other): - return True - __lt__ = __eq__ - __le__ = __eq__ - __gt__ = __eq__ - __ge__ = __eq__ - __ne__ = __eq__ - - self.assertTrue(op(ib, AllowsAnyComparison())) - self.assertIs(meth(AllowsAnyComparison()), NotImplemented) - - # If it doesn't have the comparison, Python raises a TypeError. - class AllowsNoComparison: - __eq__ = None - __lt__ = __eq__ - __le__ = __eq__ - __gt__ = __eq__ - __ge__ = __eq__ - __ne__ = __eq__ - - self.assertIs(meth(AllowsNoComparison()), NotImplemented) - with self.assertRaises(TypeError): - op(ib, AllowsNoComparison()) - - # Errors besides AttributeError are passed - class MyException(Exception): - pass - - RaisesErrorOnMissing.Exc = MyException - - with self.assertRaises(MyException): - getattr(RaisesErrorOnModule(), '__module__') - with self.assertRaises(MyException): - getattr(RaisesErrorOnName(), '__name__') - - for cls in RaisesErrorOnModule, RaisesErrorOnName: - with self.assertRaises(MyException): - op(ib, cls()) - with self.assertRaises(MyException): - meth(cls()) - - def test__lt__NotImplemented(self): - self.__check_NotImplemented_comparison('lt') - - def test__le__NotImplemented(self): - self.__check_NotImplemented_comparison('le') - - def test__gt__NotImplemented(self): - self.__check_NotImplemented_comparison('gt') - - def test__ge__NotImplemented(self): - self.__check_NotImplemented_comparison('ge') - - -class InterfaceBaseTestsMixin(NameAndModuleComparisonTestsMixin): - # Tests for both C and Python implementation - - def _getTargetClass(self): - raise NotImplementedError - - def _getFallbackClass(self): - # pylint:disable=no-name-in-module - from zope.interface.interface import InterfaceBasePy - return InterfaceBasePy - - def _makeOne(self, object_should_provide=False, name=None, module=None): - - class IB(self._getTargetClass()): - def _call_conform(self, conform): - return conform(self) - - def providedBy(self, obj): - return object_should_provide - - return IB(name, module) - - def test___call___w___conform___returning_value(self): - ib = self._makeOne(False) - conformed = object() - - class _Adapted: - def __conform__(self, iface): - return conformed - - self.assertIs(ib(_Adapted()), conformed) - - def test___call___wo___conform___ob_no_provides_w_alternate(self): - ib = self._makeOne(False) - __traceback_info__ = ib, self._getTargetClass() - adapted = object() - alternate = object() - self.assertIs(ib(adapted, alternate), alternate) - - def test___call___w___conform___ob_no_provides_wo_alternate(self): - ib = self._makeOne(False) - with self.assertRaises(TypeError) as exc: - ib(object()) - - self.assertIn('Could not adapt', str(exc.exception)) - - def test___call___w_no_conform_catches_only_AttributeError(self): - MissingSomeAttrs.test_raises( - self, self._makeOne(), expected_missing='__conform__' - ) - - -class InterfaceBaseTests( - InterfaceBaseTestsMixin, - OptimizationTestMixin, - SubclassableMixin, - unittest.TestCase, -): - # Tests that work with the C implementation - def _getTargetClass(self): - from zope.interface.interface import InterfaceBase - return InterfaceBase - - -class InterfaceBasePyTests(InterfaceBaseTestsMixin, unittest.TestCase): - # Tests that only work with the Python implementation - - _getTargetClass = InterfaceBaseTestsMixin._getFallbackClass - - def test___call___w___conform___miss_ob_provides(self): - ib = self._makeOne(True) - - class _Adapted: - def __conform__(self, iface): - return None - - adapted = _Adapted() - self.assertIs(ib(adapted), adapted) - - def test___adapt___ob_provides(self): - ib = self._makeOne(True) - adapted = object() - self.assertIs(ib.__adapt__(adapted), adapted) - - def test___adapt___ob_no_provides_uses_hooks(self): - from zope.interface import interface - ib = self._makeOne(False) - adapted = object() - _missed = [] - - def _hook_miss(iface, obj): - _missed.append((iface, obj)) - - def _hook_hit(iface, obj): - return obj - - with _Monkey(interface, adapter_hooks=[_hook_miss, _hook_hit]): - self.assertIs(ib.__adapt__(adapted), adapted) - self.assertEqual(_missed, [(ib, adapted)]) - - -class SpecificationTests(unittest.TestCase): - - def _getTargetClass(self): - from zope.interface.interface import Specification - return Specification - - def _makeOne(self, bases=_marker): - if bases is _marker: - return self._getTargetClass()() - return self._getTargetClass()(bases) - - def test_ctor(self): - from zope.interface.interface import Interface - spec = self._makeOne() - self.assertEqual(spec.__bases__, ()) - self.assertEqual(len(spec._implied), 2) - self.assertIn(spec, spec._implied) - self.assertIn(Interface, spec._implied) - self.assertEqual(len(spec.dependents), 0) - - def test_subscribe_first_time(self): - spec = self._makeOne() - dep = DummyDependent() - spec.subscribe(dep) - self.assertEqual(len(spec.dependents), 1) - self.assertEqual(spec.dependents[dep], 1) - - def test_subscribe_again(self): - spec = self._makeOne() - dep = DummyDependent() - spec.subscribe(dep) - spec.subscribe(dep) - self.assertEqual(spec.dependents[dep], 2) - - def test_unsubscribe_miss(self): - spec = self._makeOne() - dep = DummyDependent() - self.assertRaises(KeyError, spec.unsubscribe, dep) - - def test_unsubscribe(self): - spec = self._makeOne() - dep = DummyDependent() - spec.subscribe(dep) - spec.subscribe(dep) - spec.unsubscribe(dep) - self.assertEqual(spec.dependents[dep], 1) - spec.unsubscribe(dep) - self.assertNotIn(dep, spec.dependents) - - def test___setBases_subscribes_bases_and_notifies_dependents(self): - from zope.interface.interface import Interface - spec = self._makeOne() - dep = DummyDependent() - spec.subscribe(dep) - - class IFoo(Interface): - pass - - class IBar(Interface): - pass - - spec.__bases__ = (IFoo,) - self.assertEqual(dep._changed, [spec]) - self.assertEqual(IFoo.dependents[spec], 1) - spec.__bases__ = (IBar,) - self.assertEqual(IFoo.dependents.get(spec), None) - self.assertEqual(IBar.dependents[spec], 1) - - def test_changed_clears_volatiles_and_implied(self): - from zope.interface.interface import Interface - - class IFoo(Interface): - pass - - spec = self._makeOne() - spec._v_attrs = 'Foo' - spec._implied[IFoo] = () - spec.changed(spec) - self.assertIsNone(spec._v_attrs) - self.assertNotIn(IFoo, spec._implied) - - def test_interfaces_skips_already_seen(self): - from zope.interface.interface import Interface - - class IFoo(Interface): - pass - - spec = self._makeOne([IFoo, IFoo]) - self.assertEqual(list(spec.interfaces()), [IFoo]) - - def test_extends_strict_wo_self(self): - from zope.interface.interface import Interface - - class IFoo(Interface): - pass - - spec = self._makeOne(IFoo) - self.assertFalse(spec.extends(IFoo, strict=True)) - - def test_extends_strict_w_self(self): - spec = self._makeOne() - self.assertFalse(spec.extends(spec, strict=True)) - - def test_extends_non_strict_w_self(self): - spec = self._makeOne() - self.assertTrue(spec.extends(spec, strict=False)) - - def test_get_hit_w__v_attrs(self): - spec = self._makeOne() - foo = object() - spec._v_attrs = {'foo': foo} - self.assertIs(spec.get('foo'), foo) - - def test_get_hit_from_base_wo__v_attrs(self): - from zope.interface.interface import Attribute - from zope.interface.interface import Interface - - class IFoo(Interface): - foo = Attribute('foo') - - class IBar(Interface): - bar = Attribute('bar') - - spec = self._makeOne([IFoo, IBar]) - self.assertIs(spec.get('foo'), IFoo.get('foo')) - self.assertIs(spec.get('bar'), IBar.get('bar')) - - def test_multiple_inheritance_no_interfaces(self): - # If we extend an object that implements interfaces, - # plus one that doesn't, we do not interject `Interface` - # early in the resolution order. It stays at the end, - # like it should. - # See https://github.com/zopefoundation/zope.interface/issues/8 - from zope.interface.declarations import implementedBy - from zope.interface.declarations import implementer - from zope.interface.interface import Interface - - class IDefaultViewName(Interface): - pass - - class Context: - pass - - class RDBModel(Context): - pass - - class IOther(Interface): - pass - - @implementer(IOther) - class OtherBase: - pass - - class Model(OtherBase, Context): - pass - - self.assertEqual( - implementedBy(Model).__sro__, - ( - implementedBy(Model), - implementedBy(OtherBase), - IOther, - implementedBy(Context), - implementedBy(object), - Interface, # This used to be wrong, it used to be 2 too high. - ) - ) - - -class InterfaceClassTests(unittest.TestCase): - - def _getTargetClass(self): - from zope.interface.interface import InterfaceClass - return InterfaceClass - - def _makeOne(self, name='ITest', bases=(), attrs=None, __doc__=None, - __module__=None): - return self._getTargetClass()(name, bases, attrs, __doc__, __module__) - - def test_ctor_defaults(self): - klass = self._getTargetClass() - inst = klass('ITesting') - self.assertEqual(inst.__name__, 'ITesting') - self.assertEqual(inst.__doc__, '') - self.assertEqual(inst.__bases__, ()) - self.assertEqual(inst.getBases(), ()) - - def test_ctor_bad_bases(self): - klass = self._getTargetClass() - self.assertRaises(TypeError, klass, 'ITesting', (object(),)) - - def test_ctor_w_attrs_attrib_methods(self): - from zope.interface.interface import Attribute - from zope.interface.interface import fromFunction - - def _bar(): - """DOCSTRING""" - - ATTRS = { - 'foo': Attribute('Foo', ''), - 'bar': fromFunction(_bar), - } - klass = self._getTargetClass() - inst = klass('ITesting', attrs=ATTRS) - self.assertEqual(inst.__name__, 'ITesting') - self.assertEqual(inst.__doc__, '') - self.assertEqual(inst.__bases__, ()) - self.assertEqual(inst.names(), ATTRS.keys()) - - def test_ctor_attrs_w___locals__(self): - ATTRS = {'__locals__': {}} - klass = self._getTargetClass() - inst = klass('ITesting', attrs=ATTRS) - self.assertEqual(inst.__name__, 'ITesting') - self.assertEqual(inst.__doc__, '') - self.assertEqual(inst.__bases__, ()) - self.assertEqual(list(inst.names()), []) - - def test_ctor_attrs_w___annotations__(self): - ATTRS = {'__annotations__': {}} - klass = self._getTargetClass() - inst = klass('ITesting', attrs=ATTRS) - self.assertEqual(inst.__name__, 'ITesting') - self.assertEqual(inst.__doc__, '') - self.assertEqual(inst.__bases__, ()) - self.assertEqual(list(inst.names()), []) - - def test_ctor_attrs_w__decorator_non_return(self): - from zope.interface.interface import _decorator_non_return - ATTRS = {'dropme': _decorator_non_return} - klass = self._getTargetClass() - inst = klass('ITesting', attrs=ATTRS) - self.assertEqual(inst.__name__, 'ITesting') - self.assertEqual(inst.__doc__, '') - self.assertEqual(inst.__bases__, ()) - self.assertEqual(list(inst.names()), []) - - def test_ctor_attrs_w_invalid_attr_type(self): - from zope.interface.exceptions import InvalidInterface - ATTRS = {'invalid': object()} - klass = self._getTargetClass() - self.assertRaises(InvalidInterface, klass, 'ITesting', attrs=ATTRS) - - def test_ctor_w_explicit___doc__(self): - ATTRS = {'__doc__': 'ATTR'} - klass = self._getTargetClass() - inst = klass('ITesting', attrs=ATTRS, __doc__='EXPLICIT') - self.assertEqual(inst.__doc__, 'EXPLICIT') - - def test_interfaces(self): - iface = self._makeOne() - self.assertEqual(list(iface.interfaces()), [iface]) - - def test_getBases(self): - iface = self._makeOne() - sub = self._makeOne('ISub', bases=(iface,)) - self.assertEqual(sub.getBases(), (iface,)) - - def test_isEqualOrExtendedBy_identity(self): - iface = self._makeOne() - self.assertTrue(iface.isEqualOrExtendedBy(iface)) - - def test_isEqualOrExtendedBy_subiface(self): - iface = self._makeOne() - sub = self._makeOne('ISub', bases=(iface,)) - self.assertTrue(iface.isEqualOrExtendedBy(sub)) - self.assertFalse(sub.isEqualOrExtendedBy(iface)) - - def test_isEqualOrExtendedBy_unrelated(self): - one = self._makeOne('One') - another = self._makeOne('Another') - self.assertFalse(one.isEqualOrExtendedBy(another)) - self.assertFalse(another.isEqualOrExtendedBy(one)) - - def test_names_w_all_False_ignores_bases(self): - from zope.interface.interface import Attribute - from zope.interface.interface import fromFunction - - def _bar(): - """DOCSTRING""" - - BASE_ATTRS = { - 'foo': Attribute('Foo', ''), - 'bar': fromFunction(_bar), - } - DERIVED_ATTRS = { - 'baz': Attribute('Baz', ''), - } - base = self._makeOne('IBase', attrs=BASE_ATTRS) - derived = self._makeOne('IDerived', bases=(base,), attrs=DERIVED_ATTRS) - self.assertEqual(sorted(derived.names(all=False)), ['baz']) - - def test_names_w_all_True_no_bases(self): - from zope.interface.interface import Attribute - from zope.interface.interface import fromFunction - - def _bar(): - """DOCSTRING""" - - ATTRS = { - 'foo': Attribute('Foo', ''), - 'bar': fromFunction(_bar), - } - one = self._makeOne(attrs=ATTRS) - self.assertEqual(sorted(one.names(all=True)), ['bar', 'foo']) - - def test_names_w_all_True_w_bases_simple(self): - from zope.interface.interface import Attribute - from zope.interface.interface import fromFunction - - def _bar(): - """DOCSTRING""" - - BASE_ATTRS = { - 'foo': Attribute('Foo', ''), - 'bar': fromFunction(_bar), - } - DERIVED_ATTRS = { - 'baz': Attribute('Baz', ''), - } - base = self._makeOne('IBase', attrs=BASE_ATTRS) - derived = self._makeOne('IDerived', bases=(base,), attrs=DERIVED_ATTRS) - self.assertEqual( - sorted(derived.names(all=True)), ['bar', 'baz', 'foo'] - ) - - def test_names_w_all_True_bases_w_same_names(self): - from zope.interface.interface import Attribute - from zope.interface.interface import fromFunction - - def _bar(): - """DOCSTRING""" - - def _foo(): - """DOCSTRING""" - - BASE_ATTRS = { - 'foo': Attribute('Foo', ''), - 'bar': fromFunction(_bar), - } - DERIVED_ATTRS = { - 'foo': fromFunction(_foo), - 'baz': Attribute('Baz', ''), - } - base = self._makeOne('IBase', attrs=BASE_ATTRS) - derived = self._makeOne('IDerived', bases=(base,), attrs=DERIVED_ATTRS) - self.assertEqual(sorted( - derived.names(all=True)), ['bar', 'baz', 'foo'] - ) - - def test___iter__(self): - from zope.interface.interface import Attribute - from zope.interface.interface import fromFunction - - def _bar(): - """DOCSTRING""" - - def _foo(): - """DOCSTRING""" - - BASE_ATTRS = { - 'foo': Attribute('Foo', ''), - 'bar': fromFunction(_bar), - } - DERIVED_ATTRS = { - 'foo': fromFunction(_foo), - 'baz': Attribute('Baz', ''), - } - base = self._makeOne('IBase', attrs=BASE_ATTRS) - derived = self._makeOne('IDerived', bases=(base,), attrs=DERIVED_ATTRS) - self.assertEqual(sorted(derived), ['bar', 'baz', 'foo']) - - def test_namesAndDescriptions_w_all_False_ignores_bases(self): - from zope.interface.interface import Attribute - from zope.interface.interface import fromFunction - - def _bar(): - """DOCSTRING""" - - BASE_ATTRS = { - 'foo': Attribute('Foo', ''), - 'bar': fromFunction(_bar), - } - DERIVED_ATTRS = { - 'baz': Attribute('Baz', ''), - } - base = self._makeOne('IBase', attrs=BASE_ATTRS) - derived = self._makeOne('IDerived', bases=(base,), attrs=DERIVED_ATTRS) - self.assertEqual( - sorted(derived.namesAndDescriptions(all=False)), [ - ('baz', DERIVED_ATTRS['baz']), - ] - ) - - def test_namesAndDescriptions_w_all_True_no_bases(self): - from zope.interface.interface import Attribute - from zope.interface.interface import fromFunction - - def _bar(): - """DOCSTRING""" - - ATTRS = { - 'foo': Attribute('Foo', ''), - 'bar': fromFunction(_bar), - } - one = self._makeOne(attrs=ATTRS) - self.assertEqual( - sorted(one.namesAndDescriptions(all=False)), [ - ('bar', ATTRS['bar']), - ('foo', ATTRS['foo']), - ] - ) - - def test_namesAndDescriptions_w_all_True_simple(self): - from zope.interface.interface import Attribute - from zope.interface.interface import fromFunction - - def _bar(): - """DOCSTRING""" - - BASE_ATTRS = { - 'foo': Attribute('Foo', ''), - 'bar': fromFunction(_bar), - } - DERIVED_ATTRS = { - 'baz': Attribute('Baz', ''), - } - base = self._makeOne('IBase', attrs=BASE_ATTRS) - derived = self._makeOne('IDerived', bases=(base,), attrs=DERIVED_ATTRS) - self.assertEqual( - sorted(derived.namesAndDescriptions(all=True)), [ - ('bar', BASE_ATTRS['bar']), - ('baz', DERIVED_ATTRS['baz']), - ('foo', BASE_ATTRS['foo']), - ] - ) - - def test_namesAndDescriptions_w_all_True_bases_w_same_names(self): - from zope.interface.interface import Attribute - from zope.interface.interface import fromFunction - - def _bar(): - """DOCSTRING""" - - def _foo(): - """DOCSTRING""" - - BASE_ATTRS = { - 'foo': Attribute('Foo', ''), - 'bar': fromFunction(_bar), - } - DERIVED_ATTRS = { - 'foo': fromFunction(_foo), - 'baz': Attribute('Baz', ''), - } - base = self._makeOne('IBase', attrs=BASE_ATTRS) - derived = self._makeOne('IDerived', bases=(base,), attrs=DERIVED_ATTRS) - self.assertEqual( - sorted(derived.namesAndDescriptions(all=True)), [ - ('bar', BASE_ATTRS['bar']), - ('baz', DERIVED_ATTRS['baz']), - ('foo', DERIVED_ATTRS['foo']), - ] - ) - - def test_getDescriptionFor_miss(self): - one = self._makeOne() - self.assertRaises(KeyError, one.getDescriptionFor, 'nonesuch') - - def test_getDescriptionFor_hit(self): - from zope.interface.interface import Attribute - from zope.interface.interface import fromFunction - - def _bar(): - """DOCSTRING""" - - ATTRS = { - 'foo': Attribute('Foo', ''), - 'bar': fromFunction(_bar), - } - one = self._makeOne(attrs=ATTRS) - self.assertEqual(one.getDescriptionFor('foo'), ATTRS['foo']) - self.assertEqual(one.getDescriptionFor('bar'), ATTRS['bar']) - - def test___getitem___miss(self): - one = self._makeOne() - - def _test(): - return one['nonesuch'] - - self.assertRaises(KeyError, _test) - - def test___getitem___hit(self): - from zope.interface.interface import Attribute - from zope.interface.interface import fromFunction - - def _bar(): - """DOCSTRING""" - - ATTRS = { - 'foo': Attribute('Foo', ''), - 'bar': fromFunction(_bar), - } - one = self._makeOne(attrs=ATTRS) - self.assertEqual(one['foo'], ATTRS['foo']) - self.assertEqual(one['bar'], ATTRS['bar']) - - def test___contains___miss(self): - one = self._makeOne() - self.assertNotIn('nonesuch', one) - - def test___contains___hit(self): - from zope.interface.interface import Attribute - from zope.interface.interface import fromFunction - - def _bar(): - """DOCSTRING""" - - ATTRS = { - 'foo': Attribute('Foo', ''), - 'bar': fromFunction(_bar), - } - one = self._makeOne(attrs=ATTRS) - self.assertIn('foo', one) - self.assertIn('bar', one) - - def test_direct_miss(self): - one = self._makeOne() - self.assertEqual(one.direct('nonesuch'), None) - - def test_direct_hit_local_miss_bases(self): - from zope.interface.interface import Attribute - from zope.interface.interface import fromFunction - - def _bar(): - """DOCSTRING""" - - def _foo(): - """DOCSTRING""" - - BASE_ATTRS = { - 'foo': Attribute('Foo', ''), - 'bar': fromFunction(_bar), - } - DERIVED_ATTRS = { - 'foo': fromFunction(_foo), - 'baz': Attribute('Baz', ''), - } - base = self._makeOne('IBase', attrs=BASE_ATTRS) - derived = self._makeOne('IDerived', bases=(base,), attrs=DERIVED_ATTRS) - self.assertEqual(derived.direct('foo'), DERIVED_ATTRS['foo']) - self.assertEqual(derived.direct('baz'), DERIVED_ATTRS['baz']) - self.assertEqual(derived.direct('bar'), None) - - def test_queryDescriptionFor_miss(self): - iface = self._makeOne() - self.assertEqual(iface.queryDescriptionFor('nonesuch'), None) - - def test_queryDescriptionFor_hit(self): - from zope.interface import Attribute - ATTRS = {'attr': Attribute('Title', 'Description')} - iface = self._makeOne(attrs=ATTRS) - self.assertEqual(iface.queryDescriptionFor('attr'), ATTRS['attr']) - - def test_validateInvariants_pass(self): - _called_with = [] - - def _passable(*args, **kw): - _called_with.append((args, kw)) - return True - - iface = self._makeOne() - obj = object() - iface.setTaggedValue('invariants', [_passable]) - self.assertEqual(iface.validateInvariants(obj), None) - self.assertEqual(_called_with, [((obj,), {})]) - - def test_validateInvariants_fail_wo_errors_passed(self): - from zope.interface.exceptions import Invalid - _passable_called_with = [] - - def _passable(*args, **kw): - _passable_called_with.append((args, kw)) - return True - - _fail_called_with = [] - - def _fail(*args, **kw): - _fail_called_with.append((args, kw)) - raise Invalid - - iface = self._makeOne() - obj = object() - iface.setTaggedValue('invariants', [_passable, _fail]) - self.assertRaises(Invalid, iface.validateInvariants, obj) - self.assertEqual(_passable_called_with, [((obj,), {})]) - self.assertEqual(_fail_called_with, [((obj,), {})]) - - def test_validateInvariants_fail_w_errors_passed(self): - from zope.interface.exceptions import Invalid - _errors = [] - _fail_called_with = [] - - def _fail(*args, **kw): - _fail_called_with.append((args, kw)) - raise Invalid - - iface = self._makeOne() - obj = object() - iface.setTaggedValue('invariants', [_fail]) - self.assertRaises(Invalid, iface.validateInvariants, obj, _errors) - self.assertEqual(_fail_called_with, [((obj,), {})]) - self.assertEqual(len(_errors), 1) - self.assertIsInstance(_errors[0], Invalid) - - def test_validateInvariants_fail_in_base_wo_errors_passed(self): - from zope.interface.exceptions import Invalid - _passable_called_with = [] - - def _passable(*args, **kw): - _passable_called_with.append((args, kw)) - return True - - _fail_called_with = [] - - def _fail(*args, **kw): - _fail_called_with.append((args, kw)) - raise Invalid - - base = self._makeOne('IBase') - derived = self._makeOne('IDerived', (base,)) - obj = object() - base.setTaggedValue('invariants', [_fail]) - derived.setTaggedValue('invariants', [_passable]) - self.assertRaises(Invalid, derived.validateInvariants, obj) - self.assertEqual(_passable_called_with, [((obj,), {})]) - self.assertEqual(_fail_called_with, [((obj,), {})]) - - def test_validateInvariants_fail_in_base_w_errors_passed(self): - from zope.interface.exceptions import Invalid - _errors = [] - _passable_called_with = [] - - def _passable(*args, **kw): - _passable_called_with.append((args, kw)) - return True - - _fail_called_with = [] - - def _fail(*args, **kw): - _fail_called_with.append((args, kw)) - raise Invalid - - base = self._makeOne('IBase') - derived = self._makeOne('IDerived', (base,)) - obj = object() - base.setTaggedValue('invariants', [_fail]) - derived.setTaggedValue('invariants', [_passable]) - self.assertRaises(Invalid, derived.validateInvariants, obj, _errors) - self.assertEqual(_passable_called_with, [((obj,), {})]) - self.assertEqual(_fail_called_with, [((obj,), {})]) - self.assertEqual(len(_errors), 1) - self.assertIsInstance(_errors[0], Invalid) - - def test_validateInvariants_inherited_not_called_multiple_times(self): - _passable_called_with = [] - - def _passable(*args, **kw): - _passable_called_with.append((args, kw)) - return True - - obj = object() - base = self._makeOne('IBase') - base.setTaggedValue('invariants', [_passable]) - derived = self._makeOne('IDerived', (base,)) - derived.validateInvariants(obj) - self.assertEqual(1, len(_passable_called_with)) - - def test___reduce__(self): - iface = self._makeOne('PickleMe') - self.assertEqual(iface.__reduce__(), 'PickleMe') - - def test___hash___normal(self): - iface = self._makeOne('HashMe') - self.assertEqual( - hash(iface), - hash(('HashMe', 'zope.interface.tests.test_interface')) - ) - - def test___hash___missing_required_attrs(self): - - class Derived(self._getTargetClass()): - def __init__(self): # pylint:disable=super-init-not-called - pass # Don't call base class. - - derived = Derived() - with self.assertRaises(AttributeError): - hash(derived) - - def test_comparison_with_None(self): - # pylint:disable=singleton-comparison,misplaced-comparison-constant - iface = self._makeOne() - self.assertLess(iface, None) # noqa E711 - self.assertLessEqual(iface, None) # noqa E711 - self.assertNotEqual(iface, None) # noqa E711 - self.assertNotEqual(iface, None) # noqa E711 - self.assertFalse(iface >= None) # noqa E711 - self.assertFalse(iface > None) # noqa E711 - - self.assertFalse(None < iface) # noqa E711 - self.assertFalse(None <= iface) # noqa E711 - self.assertNotEqual(None, iface) # noqa E711 - self.assertNotEqual(None, iface) # noqa E711 - self.assertGreaterEqual(None, iface) # noqa E711 - self.assertGreater(None, iface) # noqa E711 - - def test_comparison_with_same_instance(self): - # pylint:disable=comparison-with-itself - iface = self._makeOne() - - self.assertFalse(iface < iface) - self.assertLessEqual(iface, iface) - self.assertEqual(iface, iface) - self.assertEqual(iface, iface) - self.assertGreaterEqual(iface, iface) - self.assertFalse(iface > iface) - - def test_comparison_with_same_named_instance_in_other_module(self): - - one = self._makeOne('IName', __module__='zope.interface.tests.one') - other = self._makeOne('IName', __module__='zope.interface.tests.other') - - self.assertLess(one, other) - self.assertFalse(other < one) - self.assertLessEqual(one, other) - self.assertFalse(other <= one) - self.assertNotEqual(one, other) - self.assertNotEqual(other, one) - self.assertNotEqual(one, other) - self.assertNotEqual(other, one) - self.assertFalse(one >= other) - self.assertGreaterEqual(other, one) - self.assertFalse(one > other) - self.assertGreater(other, one) - - def test_assignment_to__class__(self): - # https://github.com/zopefoundation/zope.interface/issues/6 - class MyException(Exception): - pass - - class MyInterfaceClass(self._getTargetClass()): - def __call__(self, target): - raise MyException(target) - - IFoo = self._makeOne('IName') - self.assertIsInstance(IFoo, self._getTargetClass()) - self.assertIs(type(IFoo), self._getTargetClass()) - - with self.assertRaises(TypeError): - IFoo(1) - - IFoo.__class__ = MyInterfaceClass - self.assertIsInstance(IFoo, MyInterfaceClass) - self.assertIs(type(IFoo), MyInterfaceClass) - - with self.assertRaises(MyException): - IFoo(1) - - def test_assignment_to__class__2(self): - # https://github.com/zopefoundation/zope.interface/issues/6 - # This is essentially a transcription of the - # test presented in the bug report. - from zope.interface import Interface - - class MyInterfaceClass(self._getTargetClass()): - def __call__(self, *args): - return args - - IFoo = MyInterfaceClass('IFoo', (Interface,)) - self.assertEqual(IFoo(1), (1,)) - - class IBar(IFoo): - pass - - self.assertEqual(IBar(1), (1,)) - - class ISpam(Interface): - pass - - with self.assertRaises(TypeError): - ISpam() - - ISpam.__class__ = MyInterfaceClass - self.assertEqual(ISpam(1), (1,)) - - def test__module__is_readonly(self): - inst = self._makeOne() - with self.assertRaises(AttributeError): - inst.__module__ = 'different.module' - - -class InterfaceTests(unittest.TestCase): - - def test_attributes_link_to_interface(self): - from zope.interface import Attribute - from zope.interface import Interface - - class I1(Interface): - attr = Attribute("My attr") - - self.assertIs(I1['attr'].interface, I1) - - def test_methods_link_to_interface(self): - from zope.interface import Interface - - class I1(Interface): - def method(foo, bar, bingo): - "A method" - - self.assertIs(I1['method'].interface, I1) - - def test_classImplements_simple(self): - from zope.interface import Interface - from zope.interface import implementedBy - from zope.interface import providedBy - - class ICurrent(Interface): - def method1(a, b): - """docstring""" - - def method2(a, b): - """docstring""" - - class IOther(Interface): - pass - - class Current: - __implemented__ = ICurrent - - def method1(self, a, b): - raise NotImplementedError() - - def method2(self, a, b): - raise NotImplementedError() - - current = Current() - - self.assertTrue(ICurrent.implementedBy(Current)) - self.assertFalse(IOther.implementedBy(Current)) - self.assertEqual(ICurrent, ICurrent) - self.assertIn(ICurrent, implementedBy(Current)) - self.assertNotIn(IOther, implementedBy(Current)) - self.assertIn(ICurrent, providedBy(current)) - self.assertNotIn(IOther, providedBy(current)) - - def test_classImplements_base_not_derived(self): - from zope.interface import Interface - from zope.interface import implementedBy - from zope.interface import providedBy - - class IBase(Interface): - def method(): - """docstring""" - - class IDerived(IBase): - pass - - class Current(): - __implemented__ = IBase - - def method(self): - raise NotImplementedError() - - current = Current() - - self.assertTrue(IBase.implementedBy(Current)) - self.assertFalse(IDerived.implementedBy(Current)) - self.assertIn(IBase, implementedBy(Current)) - self.assertNotIn(IDerived, implementedBy(Current)) - self.assertIn(IBase, providedBy(current)) - self.assertNotIn(IDerived, providedBy(current)) - - def test_classImplements_base_and_derived(self): - from zope.interface import Interface - from zope.interface import implementedBy - from zope.interface import providedBy - - class IBase(Interface): - def method(): - """docstring""" - - class IDerived(IBase): - pass - - class Current: - __implemented__ = IDerived - - def method(self): - raise NotImplementedError() - - current = Current() - - self.assertTrue(IBase.implementedBy(Current)) - self.assertTrue(IDerived.implementedBy(Current)) - self.assertNotIn(IBase, implementedBy(Current)) - self.assertIn(IBase, implementedBy(Current).flattened()) - self.assertIn(IDerived, implementedBy(Current)) - self.assertNotIn(IBase, providedBy(current)) - self.assertIn(IBase, providedBy(current).flattened()) - self.assertIn(IDerived, providedBy(current)) - - def test_classImplements_multiple(self): - from zope.interface import Interface - from zope.interface import implementedBy - from zope.interface import providedBy - - class ILeft(Interface): - def method(): - """docstring""" - - class IRight(ILeft): - pass - - class Left: - __implemented__ = ILeft - - def method(self): - raise NotImplementedError() - - class Right: - __implemented__ = IRight - - class Ambi(Left, Right): - pass - - ambi = Ambi() - - self.assertTrue(ILeft.implementedBy(Ambi)) - self.assertTrue(IRight.implementedBy(Ambi)) - self.assertIn(ILeft, implementedBy(Ambi)) - self.assertIn(IRight, implementedBy(Ambi)) - self.assertIn(ILeft, providedBy(ambi)) - self.assertIn(IRight, providedBy(ambi)) - - def test_classImplements_multiple_w_explict_implements(self): - from zope.interface import Interface - from zope.interface import implementedBy - from zope.interface import providedBy - - class ILeft(Interface): - - def method(): - """docstring""" - - class IRight(ILeft): - pass - - class IOther(Interface): - pass - - class Left(): - __implemented__ = ILeft - - def method(self): - raise NotImplementedError() - - class Right: - __implemented__ = IRight - - class Other: - __implemented__ = IOther - - class Mixed(Left, Right): - __implemented__ = Left.__implemented__, Other.__implemented__ - - mixed = Mixed() - - self.assertTrue(ILeft.implementedBy(Mixed)) - self.assertFalse(IRight.implementedBy(Mixed)) - self.assertTrue(IOther.implementedBy(Mixed)) - self.assertIn(ILeft, implementedBy(Mixed)) - self.assertNotIn(IRight, implementedBy(Mixed)) - self.assertIn(IOther, implementedBy(Mixed)) - self.assertIn(ILeft, providedBy(mixed)) - self.assertNotIn(IRight, providedBy(mixed)) - self.assertIn(IOther, providedBy(mixed)) - - def testInterfaceExtendsInterface(self): - from zope.interface import Interface - - new = Interface.__class__ - FunInterface = new('FunInterface') - BarInterface = new('BarInterface', (FunInterface,)) - BobInterface = new('BobInterface') - BazInterface = new('BazInterface', (BobInterface, BarInterface,)) - - self.assertTrue(BazInterface.extends(BobInterface)) - self.assertTrue(BazInterface.extends(BarInterface)) - self.assertTrue(BazInterface.extends(FunInterface)) - self.assertFalse(BobInterface.extends(FunInterface)) - self.assertFalse(BobInterface.extends(BarInterface)) - self.assertTrue(BarInterface.extends(FunInterface)) - self.assertFalse(BarInterface.extends(BazInterface)) - - def test_verifyClass(self): - from zope.interface import Attribute - from zope.interface import Interface - from zope.interface.verify import verifyClass - - class ICheckMe(Interface): - attr = Attribute('My attr') - - def method(): - "A method" - - class CheckMe: - __implemented__ = ICheckMe - attr = 'value' - - def method(self): - raise NotImplementedError() - - self.assertTrue(verifyClass(ICheckMe, CheckMe)) - - def test_verifyObject(self): - from zope.interface import Attribute - from zope.interface import Interface - from zope.interface.verify import verifyObject - - class ICheckMe(Interface): - attr = Attribute('My attr') - - def method(): - "A method" - - class CheckMe: - __implemented__ = ICheckMe - attr = 'value' - - def method(self): - raise NotImplementedError() - - check_me = CheckMe() - - self.assertTrue(verifyObject(ICheckMe, check_me)) - - def test_interface_object_provides_Interface(self): - from zope.interface import Interface - - class AnInterface(Interface): - pass - - self.assertTrue(Interface.providedBy(AnInterface)) - - def test_names_simple(self): - from zope.interface import Attribute - from zope.interface import Interface - - class ISimple(Interface): - attr = Attribute('My attr') - - def method(): - """docstring""" - - self.assertEqual(sorted(ISimple.names()), ['attr', 'method']) - - def test_names_derived(self): - from zope.interface import Attribute - from zope.interface import Interface - - class IBase(Interface): - attr = Attribute('My attr') - - def method(): - """docstring""" - - class IDerived(IBase): - attr2 = Attribute('My attr2') - - def method(): - """docstring""" - - def method2(): - """docstring""" - - self.assertEqual(sorted(IDerived.names()), - ['attr2', 'method', 'method2']) - self.assertEqual(sorted(IDerived.names(all=True)), - ['attr', 'attr2', 'method', 'method2']) - - def test_namesAndDescriptions_simple(self): - from zope.interface import Attribute - from zope.interface import Interface - from zope.interface.interface import Method - - class ISimple(Interface): - attr = Attribute('My attr') - - def method(): - "My method" - - name_values = sorted(ISimple.namesAndDescriptions()) - - self.assertEqual(len(name_values), 2) - self.assertEqual(name_values[0][0], 'attr') - self.assertIsInstance(name_values[0][1], Attribute) - self.assertEqual(name_values[0][1].__name__, 'attr') - self.assertEqual(name_values[0][1].__doc__, 'My attr') - self.assertEqual(name_values[1][0], 'method') - self.assertIsInstance(name_values[1][1], Method) - self.assertEqual(name_values[1][1].__name__, 'method') - self.assertEqual(name_values[1][1].__doc__, 'My method') - - def test_namesAndDescriptions_derived(self): - from zope.interface import Attribute - from zope.interface import Interface - from zope.interface.interface import Method - - class IBase(Interface): - attr = Attribute('My attr') - - def method(): - "My method" - - class IDerived(IBase): - attr2 = Attribute('My attr2') - - def method(): - "My method, overridden" - - def method2(): - "My method2" - - name_values = sorted(IDerived.namesAndDescriptions()) - - self.assertEqual(len(name_values), 3) - self.assertEqual(name_values[0][0], 'attr2') - self.assertIsInstance(name_values[0][1], Attribute) - self.assertEqual(name_values[0][1].__name__, 'attr2') - self.assertEqual(name_values[0][1].__doc__, 'My attr2') - self.assertEqual(name_values[1][0], 'method') - self.assertIsInstance(name_values[1][1], Method) - self.assertEqual(name_values[1][1].__name__, 'method') - self.assertEqual(name_values[1][1].__doc__, 'My method, overridden') - self.assertEqual(name_values[2][0], 'method2') - self.assertIsInstance(name_values[2][1], Method) - self.assertEqual(name_values[2][1].__name__, 'method2') - self.assertEqual(name_values[2][1].__doc__, 'My method2') - - name_values = sorted(IDerived.namesAndDescriptions(all=True)) - - self.assertEqual(len(name_values), 4) - self.assertEqual(name_values[0][0], 'attr') - self.assertIsInstance(name_values[0][1], Attribute) - self.assertEqual(name_values[0][1].__name__, 'attr') - self.assertEqual(name_values[0][1].__doc__, 'My attr') - self.assertEqual(name_values[1][0], 'attr2') - self.assertIsInstance(name_values[1][1], Attribute) - self.assertEqual(name_values[1][1].__name__, 'attr2') - self.assertEqual(name_values[1][1].__doc__, 'My attr2') - self.assertEqual(name_values[2][0], 'method') - self.assertIsInstance(name_values[2][1], Method) - self.assertEqual(name_values[2][1].__name__, 'method') - self.assertEqual(name_values[2][1].__doc__, 'My method, overridden') - self.assertEqual(name_values[3][0], 'method2') - self.assertIsInstance(name_values[3][1], Method) - self.assertEqual(name_values[3][1].__name__, 'method2') - self.assertEqual(name_values[3][1].__doc__, 'My method2') - - def test_getDescriptionFor_nonesuch_no_default(self): - from zope.interface import Interface - - class IEmpty(Interface): - pass - - self.assertRaises(KeyError, IEmpty.getDescriptionFor, 'nonesuch') - - def test_getDescriptionFor_simple(self): - from zope.interface import Attribute - from zope.interface import Interface - from zope.interface.interface import Method - - class ISimple(Interface): - attr = Attribute('My attr') - - def method(): - "My method" - - a_desc = ISimple.getDescriptionFor('attr') - self.assertIsInstance(a_desc, Attribute) - self.assertEqual(a_desc.__name__, 'attr') - self.assertEqual(a_desc.__doc__, 'My attr') - - m_desc = ISimple.getDescriptionFor('method') - self.assertIsInstance(m_desc, Method) - self.assertEqual(m_desc.__name__, 'method') - self.assertEqual(m_desc.__doc__, 'My method') - - def test_getDescriptionFor_derived(self): - from zope.interface import Attribute - from zope.interface import Interface - from zope.interface.interface import Method - - class IBase(Interface): - attr = Attribute('My attr') - - def method(): - "My method" - - class IDerived(IBase): - attr2 = Attribute('My attr2') - - def method(): - "My method, overridden" - - def method2(): - "My method2" - - a_desc = IDerived.getDescriptionFor('attr') - self.assertIsInstance(a_desc, Attribute) - self.assertEqual(a_desc.__name__, 'attr') - self.assertEqual(a_desc.__doc__, 'My attr') - - m_desc = IDerived.getDescriptionFor('method') - self.assertIsInstance(m_desc, Method) - self.assertEqual(m_desc.__name__, 'method') - self.assertEqual(m_desc.__doc__, 'My method, overridden') - - a2_desc = IDerived.getDescriptionFor('attr2') - self.assertIsInstance(a2_desc, Attribute) - self.assertEqual(a2_desc.__name__, 'attr2') - self.assertEqual(a2_desc.__doc__, 'My attr2') - - m2_desc = IDerived.getDescriptionFor('method2') - self.assertIsInstance(m2_desc, Method) - self.assertEqual(m2_desc.__name__, 'method2') - self.assertEqual(m2_desc.__doc__, 'My method2') - - def test___getitem__nonesuch(self): - from zope.interface import Interface - - class IEmpty(Interface): - pass - - self.assertRaises(KeyError, IEmpty.__getitem__, 'nonesuch') - - def test___getitem__simple(self): - from zope.interface import Attribute - from zope.interface import Interface - from zope.interface.interface import Method - - class ISimple(Interface): - attr = Attribute('My attr') - - def method(): - "My method" - - a_desc = ISimple['attr'] - self.assertIsInstance(a_desc, Attribute) - self.assertEqual(a_desc.__name__, 'attr') - self.assertEqual(a_desc.__doc__, 'My attr') - - m_desc = ISimple['method'] - self.assertIsInstance(m_desc, Method) - self.assertEqual(m_desc.__name__, 'method') - self.assertEqual(m_desc.__doc__, 'My method') - - def test___getitem___derived(self): - from zope.interface import Attribute - from zope.interface import Interface - from zope.interface.interface import Method - - class IBase(Interface): - attr = Attribute('My attr') - - def method(): - "My method" - - class IDerived(IBase): - attr2 = Attribute('My attr2') - - def method(): - "My method, overridden" - - def method2(): - "My method2" - - a_desc = IDerived['attr'] - self.assertIsInstance(a_desc, Attribute) - self.assertEqual(a_desc.__name__, 'attr') - self.assertEqual(a_desc.__doc__, 'My attr') - - m_desc = IDerived['method'] - self.assertIsInstance(m_desc, Method) - self.assertEqual(m_desc.__name__, 'method') - self.assertEqual(m_desc.__doc__, 'My method, overridden') - - a2_desc = IDerived['attr2'] - self.assertIsInstance(a2_desc, Attribute) - self.assertEqual(a2_desc.__name__, 'attr2') - self.assertEqual(a2_desc.__doc__, 'My attr2') - - m2_desc = IDerived['method2'] - self.assertIsInstance(m2_desc, Method) - self.assertEqual(m2_desc.__name__, 'method2') - self.assertEqual(m2_desc.__doc__, 'My method2') - - def test___contains__nonesuch(self): - from zope.interface import Interface - - class IEmpty(Interface): - pass - - self.assertNotIn('nonesuch', IEmpty) - - def test___contains__simple(self): - from zope.interface import Attribute - from zope.interface import Interface - - class ISimple(Interface): - attr = Attribute('My attr') - - def method(): - "My method" - - self.assertIn('attr', ISimple) - self.assertIn('method', ISimple) - - def test___contains__derived(self): - from zope.interface import Attribute - from zope.interface import Interface - - class IBase(Interface): - attr = Attribute('My attr') - - def method(): - "My method" - - class IDerived(IBase): - attr2 = Attribute('My attr2') - - def method(): - "My method, overridden" - - def method2(): - "My method2" - - self.assertIn('attr', IDerived) - self.assertIn('method', IDerived) - self.assertIn('attr2', IDerived) - self.assertIn('method2', IDerived) - - def test___iter__empty(self): - from zope.interface import Interface - - class IEmpty(Interface): - pass - - self.assertEqual(list(IEmpty), []) - - def test___iter__simple(self): - from zope.interface import Attribute - from zope.interface import Interface - - class ISimple(Interface): - attr = Attribute('My attr') - - def method(): - "My method" - - self.assertEqual(sorted(list(ISimple)), ['attr', 'method']) - - def test___iter__derived(self): - from zope.interface import Attribute - from zope.interface import Interface - - class IBase(Interface): - attr = Attribute('My attr') - - def method(): - "My method" - - class IDerived(IBase): - attr2 = Attribute('My attr2') - - def method(): - "My method, overridden" - - def method2(): - "My method2" - - self.assertEqual(sorted(list(IDerived)), - ['attr', 'attr2', 'method', 'method2']) - - def test_function_attributes_become_tagged_values(self): - from zope.interface import Interface - - class ITagMe(Interface): - def method(): - """docstring""" - method.optional = 1 - - method = ITagMe['method'] - self.assertEqual(method.getTaggedValue('optional'), 1) - - def test___doc___non_element(self): - from zope.interface import Interface - - class IHaveADocString(Interface): - "xxx" - - self.assertEqual(IHaveADocString.__doc__, "xxx") - self.assertEqual(list(IHaveADocString), []) - - def test___doc___as_element(self): - from zope.interface import Attribute - from zope.interface import Interface - - class IHaveADocString(Interface): - "xxx" - __doc__ = Attribute('the doc') - - self.assertEqual(IHaveADocString.__doc__, "") - self.assertEqual(list(IHaveADocString), ['__doc__']) - - def _errorsEqual(self, has_invariant, error_len, error_msgs, iface): - from zope.interface.exceptions import Invalid - self.assertRaises(Invalid, iface.validateInvariants, has_invariant) - e = [] - try: - iface.validateInvariants(has_invariant, e) - self.fail("validateInvariants should always raise") - except Invalid as error: - self.assertEqual(error.args[0], e) - - self.assertEqual(len(e), error_len) - msgs = [error.args[0] for error in e] - msgs.sort() - for msg in msgs: - self.assertEqual(msg, error_msgs.pop(0)) - - def test_invariant_simple(self): - from zope.interface import Attribute - from zope.interface import Interface - from zope.interface import directlyProvides - from zope.interface import invariant - - class IInvariant(Interface): - foo = Attribute('foo') - bar = Attribute('bar; must eval to Boolean True if foo does') - invariant(_ifFooThenBar) - - class HasInvariant: - pass - - # set up - has_invariant = HasInvariant() - directlyProvides(has_invariant, IInvariant) - - # the tests - self.assertEqual(IInvariant.getTaggedValue('invariants'), - [_ifFooThenBar]) - self.assertEqual(IInvariant.validateInvariants(has_invariant), None) - has_invariant.bar = 27 - self.assertEqual(IInvariant.validateInvariants(has_invariant), None) - has_invariant.foo = 42 - self.assertEqual(IInvariant.validateInvariants(has_invariant), None) - del has_invariant.bar - self._errorsEqual(has_invariant, 1, ['If Foo, then Bar!'], - IInvariant) - - def test_invariant_nested(self): - from zope.interface import Attribute - from zope.interface import Interface - from zope.interface import directlyProvides - from zope.interface import invariant - - class IInvariant(Interface): - foo = Attribute('foo') - bar = Attribute('bar; must eval to Boolean True if foo does') - invariant(_ifFooThenBar) - - class ISubInvariant(IInvariant): - invariant(_barGreaterThanFoo) - - class HasInvariant: - pass - - # nested interfaces with invariants: - self.assertEqual(ISubInvariant.getTaggedValue('invariants'), - [_barGreaterThanFoo]) - has_invariant = HasInvariant() - directlyProvides(has_invariant, ISubInvariant) - has_invariant.foo = 42 - # even though the interface has changed, we should still only have one - # error. - self._errorsEqual(has_invariant, 1, ['If Foo, then Bar!'], - ISubInvariant) - # however, if we set foo to 0 (Boolean False) and bar to a negative - # number then we'll get the new error - has_invariant.foo = 2 - has_invariant.bar = 1 - self._errorsEqual(has_invariant, 1, - ['Please, Boo MUST be greater than Foo!'], - ISubInvariant) - # and if we set foo to a positive number and boo to 0, we'll - # get both errors! - has_invariant.foo = 1 - has_invariant.bar = 0 - self._errorsEqual(has_invariant, 2, - ['If Foo, then Bar!', - 'Please, Boo MUST be greater than Foo!'], - ISubInvariant) - # for a happy ending, we'll make the invariants happy - has_invariant.foo = 1 - has_invariant.bar = 2 - self.assertEqual(IInvariant.validateInvariants(has_invariant), None) - - def test_invariant_mutandis(self): - from zope.interface import Attribute - from zope.interface import Interface - from zope.interface import directlyProvides - from zope.interface import invariant - - class IInvariant(Interface): - foo = Attribute('foo') - bar = Attribute('bar; must eval to Boolean True if foo does') - invariant(_ifFooThenBar) - - class HasInvariant: - pass - - # now we'll do two invariants on the same interface, - # just to make sure that a small - # multi-invariant interface is at least minimally tested. - has_invariant = HasInvariant() - directlyProvides(has_invariant, IInvariant) - has_invariant.foo = 42 - - # if you really need to mutate, then this would be the way to do it. - # Probably a bad idea, though. :-) - old_invariants = IInvariant.getTaggedValue('invariants') - invariants = old_invariants[:] - invariants.append(_barGreaterThanFoo) - IInvariant.setTaggedValue('invariants', invariants) - - # even though the interface has changed, we should still only have one - # error. - self._errorsEqual(has_invariant, 1, ['If Foo, then Bar!'], - IInvariant) - # however, if we set foo to 0 (Boolean False) and bar to a negative - # number then we'll get the new error - has_invariant.foo = 2 - has_invariant.bar = 1 - self._errorsEqual( - has_invariant, - 1, - ['Please, Boo MUST be greater than Foo!'], - IInvariant - ) - # and if we set foo to a positive number and boo to 0, we'll - # get both errors! - has_invariant.foo = 1 - has_invariant.bar = 0 - self._errorsEqual(has_invariant, 2, - ['If Foo, then Bar!', - 'Please, Boo MUST be greater than Foo!'], - IInvariant) - # for another happy ending, we'll make the invariants happy again - has_invariant.foo = 1 - has_invariant.bar = 2 - self.assertEqual(IInvariant.validateInvariants(has_invariant), None) - # clean up - IInvariant.setTaggedValue('invariants', old_invariants) - - def test___doc___element(self): - from zope.interface import Attribute - from zope.interface import Interface - - class IDocstring(Interface): - "xxx" - - self.assertEqual(IDocstring.__doc__, "xxx") - self.assertEqual(list(IDocstring), []) - - class IDocstringAndAttribute(Interface): - "xxx" - - __doc__ = Attribute('the doc') - - self.assertEqual(IDocstringAndAttribute.__doc__, "") - self.assertEqual(list(IDocstringAndAttribute), ['__doc__']) - - def test_invariant_as_decorator(self): - from zope.interface import Attribute - from zope.interface import Interface - from zope.interface import implementer - from zope.interface import invariant - from zope.interface.exceptions import Invalid - - class IRange(Interface): - min = Attribute("Lower bound") - max = Attribute("Upper bound") - - @invariant - def range_invariant(ob): - if ob.max < ob.min: - raise Invalid('max < min') - - @implementer(IRange) - class Range: - - def __init__(self, min, max): - self.min, self.max = min, max - - IRange.validateInvariants(Range(1, 2)) - IRange.validateInvariants(Range(1, 1)) - try: - IRange.validateInvariants(Range(2, 1)) - except Invalid as e: - self.assertEqual(str(e), 'max < min') - - def test_taggedValue(self): - from zope.interface import Attribute - from zope.interface import Interface - from zope.interface import taggedValue - - class ITagged(Interface): - foo = Attribute('foo') - bar = Attribute('bar; must eval to Boolean True if foo does') - taggedValue('qux', 'Spam') - - class IDerived(ITagged): - taggedValue('qux', 'Spam Spam') - taggedValue('foo', 'bar') - - class IDerived2(IDerived): - pass - - self.assertEqual(ITagged.getTaggedValue('qux'), 'Spam') - self.assertRaises(KeyError, ITagged.getTaggedValue, 'foo') - self.assertEqual(list(ITagged.getTaggedValueTags()), ['qux']) - - self.assertEqual(IDerived2.getTaggedValue('qux'), 'Spam Spam') - self.assertEqual(IDerived2.getTaggedValue('foo'), 'bar') - self.assertEqual(set(IDerived2.getTaggedValueTags()), {'qux', 'foo'}) - - def _make_taggedValue_tree(self, base): - from zope.interface import Attribute - from zope.interface import taggedValue - - class F(base): - taggedValue('tag', 'F') - tag = Attribute('F') - - class E(base): - taggedValue('tag', 'E') - tag = Attribute('E') - - class D(base): - taggedValue('tag', 'D') - tag = Attribute('D') - - class C(D, F): - taggedValue('tag', 'C') - tag = Attribute('C') - - class B(D, E): - pass - - class A(B, C): - pass - - return A - - def test_getTaggedValue_follows__iro__(self): - # And not just looks at __bases__. - # https://github.com/zopefoundation/zope.interface/issues/190 - from zope.interface import Interface - - # First, confirm that looking at a true class - # hierarchy follows the __mro__. - class_A = self._make_taggedValue_tree(object) - self.assertEqual(class_A.tag.__name__, 'C') - - # Now check that Interface does, both for attributes... - iface_A = self._make_taggedValue_tree(Interface) - self.assertEqual(iface_A['tag'].__name__, 'C') - # ... and for tagged values. - self.assertEqual(iface_A.getTaggedValue('tag'), 'C') - self.assertEqual(iface_A.queryTaggedValue('tag'), 'C') - # Of course setting something lower overrides it. - assert iface_A.__bases__[0].__name__ == 'B' - iface_A.__bases__[0].setTaggedValue('tag', 'B') - self.assertEqual(iface_A.getTaggedValue('tag'), 'B') - - def test_getDirectTaggedValue_ignores__iro__(self): - # https://github.com/zopefoundation/zope.interface/issues/190 - from zope.interface import Interface - - A = self._make_taggedValue_tree(Interface) - self.assertIsNone(A.queryDirectTaggedValue('tag')) - self.assertEqual([], list(A.getDirectTaggedValueTags())) - - with self.assertRaises(KeyError): - A.getDirectTaggedValue('tag') - - A.setTaggedValue('tag', 'A') - self.assertEqual(A.queryDirectTaggedValue('tag'), 'A') - self.assertEqual(A.getDirectTaggedValue('tag'), 'A') - self.assertEqual(['tag'], list(A.getDirectTaggedValueTags())) - - assert A.__bases__[1].__name__ == 'C' - C = A.__bases__[1] - self.assertEqual(C.queryDirectTaggedValue('tag'), 'C') - self.assertEqual(C.getDirectTaggedValue('tag'), 'C') - self.assertEqual(['tag'], list(C.getDirectTaggedValueTags())) - - def test_description_cache_management(self): - # See https://bugs.launchpad.net/zope.interface/+bug/185974 - # There was a bug where the cache used by Specification.get() was not - # cleared when the bases were changed. - from zope.interface import Attribute - from zope.interface import Interface - - class I1(Interface): - a = Attribute('a') - - class I2(I1): - pass - - class I3(I2): - pass - - self.assertIs(I3.get('a'), I1.get('a')) - - I2.__bases__ = (Interface,) - self.assertIsNone(I3.get('a')) - - def test___call___defers_to___conform___(self): - from zope.interface import Interface - from zope.interface import implementer - - class IFoo(Interface): - pass - - @implementer(IFoo) - class C: - def __conform__(self, proto): - return 0 - - self.assertEqual(IFoo(C()), 0) - - def test___call___object_implements(self): - from zope.interface import Interface - from zope.interface import implementer - - class IFoo(Interface): - pass - - @implementer(IFoo) - class C: - pass - - c = C() - self.assertIs(IFoo(c), c) - - def test___call___miss_wo_alternate(self): - from zope.interface import Interface - - class IFoo(Interface): - pass - - class C: - pass - - c = C() - self.assertRaises(TypeError, IFoo, c) - - def test___call___miss_w_alternate(self): - from zope.interface import Interface - - class IFoo(Interface): - pass - - class C: - pass - - c = C() - self.assertIs(IFoo(c, self), self) - - def test___call___w_adapter_hook(self): - from zope.interface import Interface - from zope.interface.interface import adapter_hooks - - def _miss(iface, obj): - pass - - def _hit(iface, obj): - return self - - class IFoo(Interface): - pass - - class C: - pass - - c = C() - - old_adapter_hooks = adapter_hooks[:] - adapter_hooks[:] = [_miss, _hit] - try: - self.assertIs(IFoo(c), self) - finally: - adapter_hooks[:] = old_adapter_hooks - - def test___call___w_overridden_adapt(self): - from zope.interface import Interface - from zope.interface import implementer - from zope.interface import interfacemethod - - class IFoo(Interface): - - @interfacemethod - def __adapt__(self, obj): - return 42 - - @implementer(IFoo) - class Obj: - pass - - self.assertEqual(42, IFoo(object())) - # __adapt__ can ignore the fact that the object provides - # the interface if it chooses. - self.assertEqual(42, IFoo(Obj())) - - def test___call___w_overridden_adapt_and_conform(self): - # Conform is first, taking precedence over __adapt__, - # *if* it returns non-None - from zope.interface import Interface - from zope.interface import implementer - from zope.interface import interfacemethod - - class IAdapt(Interface): - @interfacemethod - def __adapt__(self, obj): - return 42 - - class ISimple(Interface): - """Nothing special.""" - - @implementer(IAdapt) - class Conform24: - def __conform__(self, iface): - return 24 - - @implementer(IAdapt) - class ConformNone: - def __conform__(self, iface): - return None - - self.assertEqual(42, IAdapt(object())) - - self.assertEqual(24, ISimple(Conform24())) - self.assertEqual(24, IAdapt(Conform24())) - - with self.assertRaises(TypeError): - ISimple(ConformNone()) - - self.assertEqual(42, IAdapt(ConformNone())) - - def test___call___w_overridden_adapt_call_super(self): - import sys - - from zope.interface import Interface - from zope.interface import implementer - from zope.interface import interfacemethod - - class IFoo(Interface): - - @interfacemethod - def __adapt__(self, obj): - if not self.providedBy(obj): - return 42 - return super().__adapt__(obj) - - @implementer(IFoo) - class Obj: - pass - - self.assertEqual(42, IFoo(object())) - obj = Obj() - self.assertIs(obj, IFoo(obj)) - - def test___adapt___as_method_and_implementation(self): - from zope.interface import Interface - from zope.interface import interfacemethod - - class IFoo(Interface): - @interfacemethod - def __adapt__(self, obj): - return 42 - - def __adapt__(to_adapt): # noqa F811 - "This is a protocol" - - self.assertEqual(42, IFoo(object())) - self.assertEqual(IFoo['__adapt__'].getSignatureString(), '(to_adapt)') - - def test___adapt__inheritance_and_type(self): - from zope.interface import Interface - from zope.interface import interfacemethod - - class IRoot(Interface): - """Root""" - - class IWithAdapt(IRoot): - @interfacemethod - def __adapt__(self, obj): - return 42 - - class IOther(IRoot): - """Second branch""" - - class IUnrelated(Interface): - """Unrelated""" - - class IDerivedAdapt(IUnrelated, IWithAdapt, IOther): - """Inherits an adapt""" - # Order of "inheritance" matters here. - - class IDerived2Adapt(IDerivedAdapt): - """Overrides an inherited custom adapt.""" - @interfacemethod - def __adapt__(self, obj): - return 24 - - self.assertEqual(42, IDerivedAdapt(object())) - for iface in IRoot, IWithAdapt, IOther, IUnrelated, IDerivedAdapt: - self.assertEqual(__name__, iface.__module__) - - for iface in IRoot, IOther, IUnrelated: - self.assertEqual(type(IRoot), type(Interface)) - - # But things that implemented __adapt__ got a new type - self.assertNotEqual(type(Interface), type(IWithAdapt)) - self.assertEqual(type(IWithAdapt), type(IDerivedAdapt)) - self.assertIsInstance(IWithAdapt, type(Interface)) - - self.assertEqual(24, IDerived2Adapt(object())) - self.assertNotEqual(type(IDerived2Adapt), type(IDerivedAdapt)) - self.assertIsInstance(IDerived2Adapt, type(IDerivedAdapt)) - - def test_interfacemethod_is_general(self): - from zope.interface import Interface - from zope.interface import interfacemethod - - class IFoo(Interface): - - @interfacemethod - def __call__(self, obj): - """Replace an existing method""" - return 42 - - @interfacemethod - def this_is_new(self): - return 42 - - self.assertEqual(IFoo(self), 42) - self.assertEqual(IFoo.this_is_new(), 42) - - -class AttributeTests(ElementTests): - - DEFAULT_NAME = 'TestAttribute' - - def _getTargetClass(self): - from zope.interface.interface import Attribute - return Attribute - - def test__repr__w_interface(self): - method = self._makeOne() - method.interface = type(self) - r = repr(method) - self.assertTrue( - r.startswith(''), r - ) - - def test__repr__wo_interface(self): - method = self._makeOne() - r = repr(method) - self.assertTrue( - r.startswith(''), r) - - def test__str__w_interface(self): - method = self._makeOne() - method.interface = type(self) - r = str(method) - self.assertEqual(r, __name__ + '.AttributeTests.TestAttribute') - - def test__str__wo_interface(self): - method = self._makeOne() - r = str(method) - self.assertEqual(r, 'TestAttribute') - - -class MethodTests(AttributeTests): - - DEFAULT_NAME = 'TestMethod' - - def _getTargetClass(self): - from zope.interface.interface import Method - return Method - - def test_optional_as_property(self): - method = self._makeOne() - self.assertEqual(method.optional, {}) - method.optional = {'foo': 'bar'} - self.assertEqual(method.optional, {'foo': 'bar'}) - del method.optional - self.assertEqual(method.optional, {}) - - def test___call___raises_BrokenImplementation(self): - from zope.interface.exceptions import BrokenImplementation - method = self._makeOne() - try: - method() - except BrokenImplementation as e: - self.assertEqual(e.interface, None) - self.assertEqual(e.name, self.DEFAULT_NAME) - else: - self.fail('__call__ should raise BrokenImplementation') - - def test_getSignatureInfo_bare(self): - method = self._makeOne() - info = method.getSignatureInfo() - self.assertEqual(list(info['positional']), []) - self.assertEqual(list(info['required']), []) - self.assertEqual(info['optional'], {}) - self.assertEqual(info['varargs'], None) - self.assertEqual(info['kwargs'], None) - - def test_getSignatureString_bare(self): - method = self._makeOne() - self.assertEqual(method.getSignatureString(), '()') - - def test_getSignatureString_w_only_required(self): - method = self._makeOne() - method.positional = method.required = ['foo'] - self.assertEqual(method.getSignatureString(), '(foo)') - - def test_getSignatureString_w_optional(self): - method = self._makeOne() - method.positional = method.required = ['foo'] - method.optional = {'foo': 'bar'} - self.assertEqual(method.getSignatureString(), "(foo='bar')") - - def test_getSignatureString_w_varargs(self): - method = self._makeOne() - method.varargs = 'args' - self.assertEqual(method.getSignatureString(), "(*args)") - - def test_getSignatureString_w_kwargs(self): - method = self._makeOne() - method.kwargs = 'kw' - self.assertEqual(method.getSignatureString(), "(**kw)") - - def test__repr__w_interface(self): - method = self._makeOne() - method.kwargs = 'kw' - method.interface = type(self) - r = repr(method) - self.assertTrue( - r.startswith(''), r - ) - - def test__repr__wo_interface(self): - method = self._makeOne() - method.kwargs = 'kw' - r = repr(method) - self.assertTrue( - r.startswith(''), r) - - def test__str__w_interface(self): - method = self._makeOne() - method.kwargs = 'kw' - method.interface = type(self) - r = str(method) - self.assertEqual(r, __name__ + '.MethodTests.TestMethod(**kw)') - - def test__str__wo_interface(self): - method = self._makeOne() - method.kwargs = 'kw' - r = str(method) - self.assertEqual(r, 'TestMethod(**kw)') - - -class Test_fromFunction(unittest.TestCase): - - def _callFUT(self, *args, **kw): - from zope.interface.interface import fromFunction - return fromFunction(*args, **kw) - - def test_bare(self): - - def _func(): - "DOCSTRING" - - method = self._callFUT(_func) - self.assertEqual(method.getName(), '_func') - self.assertEqual(method.getDoc(), 'DOCSTRING') - self.assertEqual(method.interface, None) - self.assertEqual(list(method.getTaggedValueTags()), []) - info = method.getSignatureInfo() - self.assertEqual(list(info['positional']), []) - self.assertEqual(list(info['required']), []) - self.assertEqual(info['optional'], {}) - self.assertEqual(info['varargs'], None) - self.assertEqual(info['kwargs'], None) - - def test_w_interface(self): - from zope.interface.interface import InterfaceClass - - class IFoo(InterfaceClass): - pass - - def _func(): - "DOCSTRING" - - method = self._callFUT(_func, interface=IFoo) - self.assertEqual(method.interface, IFoo) - - def test_w_name(self): - - def _func(): - "DOCSTRING" - - method = self._callFUT(_func, name='anotherName') - self.assertEqual(method.getName(), 'anotherName') - - def test_w_only_required(self): - - def _func(foo): - "DOCSTRING" - - method = self._callFUT(_func) - info = method.getSignatureInfo() - self.assertEqual(list(info['positional']), ['foo']) - self.assertEqual(list(info['required']), ['foo']) - self.assertEqual(info['optional'], {}) - self.assertEqual(info['varargs'], None) - self.assertEqual(info['kwargs'], None) - - def test_w_optional(self): - - def _func(foo='bar'): - "DOCSTRING" - - method = self._callFUT(_func) - info = method.getSignatureInfo() - self.assertEqual(list(info['positional']), ['foo']) - self.assertEqual(list(info['required']), []) - self.assertEqual(info['optional'], {'foo': 'bar'}) - self.assertEqual(info['varargs'], None) - self.assertEqual(info['kwargs'], None) - - def test_w_optional_self(self): - # This is a weird case, trying to cover the following code in - # FUT:: - # - # nr = na-len(defaults) - # if nr < 0: - # defaults=defaults[-nr:] - # nr = 0 - - def _func(self='bar'): - "DOCSTRING" - - method = self._callFUT(_func, imlevel=1) - info = method.getSignatureInfo() - self.assertEqual(list(info['positional']), []) - self.assertEqual(list(info['required']), []) - self.assertEqual(info['optional'], {}) - self.assertEqual(info['varargs'], None) - self.assertEqual(info['kwargs'], None) - - def test_w_varargs(self): - - def _func(*args): - "DOCSTRING" - - method = self._callFUT(_func) - info = method.getSignatureInfo() - self.assertEqual(list(info['positional']), []) - self.assertEqual(list(info['required']), []) - self.assertEqual(info['optional'], {}) - self.assertEqual(info['varargs'], 'args') - self.assertEqual(info['kwargs'], None) - - def test_w_kwargs(self): - - def _func(**kw): - "DOCSTRING" - - method = self._callFUT(_func) - info = method.getSignatureInfo() - self.assertEqual(list(info['positional']), []) - self.assertEqual(list(info['required']), []) - self.assertEqual(info['optional'], {}) - self.assertEqual(info['varargs'], None) - self.assertEqual(info['kwargs'], 'kw') - - def test_full_spectrum(self): - - def _func( - foo, bar='baz', *args, **kw - ): # pylint:disable=keyword-arg-before-vararg - "DOCSTRING" - - method = self._callFUT(_func) - info = method.getSignatureInfo() - self.assertEqual(list(info['positional']), ['foo', 'bar']) - self.assertEqual(list(info['required']), ['foo']) - self.assertEqual(info['optional'], {'bar': 'baz'}) - self.assertEqual(info['varargs'], 'args') - self.assertEqual(info['kwargs'], 'kw') - - -class Test_fromMethod(unittest.TestCase): - - def _callFUT(self, *args, **kw): - from zope.interface.interface import fromMethod - return fromMethod(*args, **kw) - - def test_no_args(self): - - class Foo: - def bar(self): - "DOCSTRING" - - method = self._callFUT(Foo.bar) - self.assertEqual(method.getName(), 'bar') - self.assertEqual(method.getDoc(), 'DOCSTRING') - self.assertEqual(method.interface, None) - self.assertEqual(list(method.getTaggedValueTags()), []) - info = method.getSignatureInfo() - self.assertEqual(list(info['positional']), []) - self.assertEqual(list(info['required']), []) - self.assertEqual(info['optional'], {}) - self.assertEqual(info['varargs'], None) - self.assertEqual(info['kwargs'], None) - - def test_full_spectrum(self): - - class Foo: - def bar( - self, foo, bar='baz', *args, **kw - ): # pylint:disable=keyword-arg-before-vararg - "DOCSTRING" - - method = self._callFUT(Foo.bar) - info = method.getSignatureInfo() - self.assertEqual(list(info['positional']), ['foo', 'bar']) - self.assertEqual(list(info['required']), ['foo']) - self.assertEqual(info['optional'], {'bar': 'baz'}) - self.assertEqual(info['varargs'], 'args') - self.assertEqual(info['kwargs'], 'kw') - - def test_w_non_method(self): - - def foo(): - "DOCSTRING" - - method = self._callFUT(foo) - self.assertEqual(method.getName(), 'foo') - self.assertEqual(method.getDoc(), 'DOCSTRING') - self.assertEqual(method.interface, None) - self.assertEqual(list(method.getTaggedValueTags()), []) - info = method.getSignatureInfo() - self.assertEqual(list(info['positional']), []) - self.assertEqual(list(info['required']), []) - self.assertEqual(info['optional'], {}) - self.assertEqual(info['varargs'], None) - self.assertEqual(info['kwargs'], None) - - -class DummyDependent: - - def __init__(self): - self._changed = [] - - def changed(self, originally_changed): - self._changed.append(originally_changed) - - -def _barGreaterThanFoo(obj): - from zope.interface.exceptions import Invalid - foo = getattr(obj, 'foo', None) - bar = getattr(obj, 'bar', None) - if foo is not None and isinstance(foo, type(bar)): - # type checking should be handled elsewhere (like, say, - # schema); these invariants should be intra-interface - # constraints. This is a hacky way to do it, maybe, but you - # get the idea - if not bar > foo: - raise Invalid('Please, Boo MUST be greater than Foo!') - - -def _ifFooThenBar(obj): - from zope.interface.exceptions import Invalid - if getattr(obj, 'foo', None) and not getattr(obj, 'bar', None): - raise Invalid('If Foo, then Bar!') - - -class _Monkey: - # context-manager for replacing module names in the scope of a test. - def __init__(self, module, **kw): - self.module = module - self.to_restore = {key: getattr(module, key) for key in kw} - for key, value in kw.items(): - setattr(module, key, value) - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - for key, value in self.to_restore.items(): - setattr(self.module, key, value) - - -class TestTypeAnnotations(unittest.TestCase): - """Test using Interfaces in type annotations.""" - - def test___or__(self): - from typing import Optional - from typing import Union - - from zope.interface import Interface - - class I1(Interface): - pass - - class I2(Interface): - pass - - class B: - a: I1 | None - b: I1 | I2 - - self.assertEqual( - B.__annotations__, {'a': Optional[I1], 'b': Union[I1, I2]}) - - def test___ror__(self): - from typing import Optional - from typing import Union - - from zope.interface import Interface - - class I1(Interface): - pass - - class A: - pass - - class B: - a: None | I1 - b: A | I1 - - self.assertEqual( - B.__annotations__, {'a': Optional[I1], 'b': Union[A, I1]}) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_interfaces.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_interfaces.py deleted file mode 100644 index a100649..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_interfaces.py +++ /dev/null @@ -1,130 +0,0 @@ -import unittest - - -class _ConformsToIObjectEvent: - - def _makeOne(self, target=None): - if target is None: - target = object() - return self._getTargetClass()(target) - - def test_class_conforms_to_IObjectEvent(self): - from zope.interface.interfaces import IObjectEvent - from zope.interface.verify import verifyClass - verifyClass(IObjectEvent, self._getTargetClass()) - - def test_instance_conforms_to_IObjectEvent(self): - from zope.interface.interfaces import IObjectEvent - from zope.interface.verify import verifyObject - verifyObject(IObjectEvent, self._makeOne()) - - -class _ConformsToIRegistrationEvent(_ConformsToIObjectEvent): - - def test_class_conforms_to_IRegistrationEvent(self): - from zope.interface.interfaces import IRegistrationEvent - from zope.interface.verify import verifyClass - verifyClass(IRegistrationEvent, self._getTargetClass()) - - def test_instance_conforms_to_IRegistrationEvent(self): - from zope.interface.interfaces import IRegistrationEvent - from zope.interface.verify import verifyObject - verifyObject(IRegistrationEvent, self._makeOne()) - - -class ObjectEventTests(unittest.TestCase, _ConformsToIObjectEvent): - - def _getTargetClass(self): - from zope.interface.interfaces import ObjectEvent - return ObjectEvent - - def test_ctor(self): - target = object() - event = self._makeOne(target) - self.assertIs(event.object, target) - - -class RegistrationEventTests(unittest.TestCase, - _ConformsToIRegistrationEvent): - - def _getTargetClass(self): - from zope.interface.interfaces import RegistrationEvent - return RegistrationEvent - - def test___repr__(self): - target = object() - event = self._makeOne(target) - r = repr(event) - self.assertEqual(r.splitlines(), - ['RegistrationEvent event:', repr(target)]) - - -class RegisteredTests(unittest.TestCase, - _ConformsToIRegistrationEvent): - - def _getTargetClass(self): - from zope.interface.interfaces import Registered - return Registered - - def test_class_conforms_to_IRegistered(self): - from zope.interface.interfaces import IRegistered - from zope.interface.verify import verifyClass - verifyClass(IRegistered, self._getTargetClass()) - - def test_instance_conforms_to_IRegistered(self): - from zope.interface.interfaces import IRegistered - from zope.interface.verify import verifyObject - verifyObject(IRegistered, self._makeOne()) - - -class UnregisteredTests(unittest.TestCase, - _ConformsToIRegistrationEvent): - - def _getTargetClass(self): - from zope.interface.interfaces import Unregistered - return Unregistered - - def test_class_conforms_to_IUnregistered(self): - from zope.interface.interfaces import IUnregistered - from zope.interface.verify import verifyClass - verifyClass(IUnregistered, self._getTargetClass()) - - def test_instance_conforms_to_IUnregistered(self): - from zope.interface.interfaces import IUnregistered - from zope.interface.verify import verifyObject - verifyObject(IUnregistered, self._makeOne()) - - -class InterfaceClassTests(unittest.TestCase): - - def _getTargetClass(self): - from zope.interface.interface import InterfaceClass - return InterfaceClass - - def _getTargetInterface(self): - from zope.interface.interfaces import IInterface - return IInterface - - def _makeOne(self): - from zope.interface.interface import Interface - return Interface - - def test_class_conforms(self): - from zope.interface.verify import verifyClass - verifyClass(self._getTargetInterface(), self._getTargetClass()) - - def test_instance_conforms(self): - from zope.interface.verify import verifyObject - verifyObject(self._getTargetInterface(), self._makeOne()) - - def test_instance_consistent__iro__(self): - from zope.interface import ro - self.assertTrue(ro.is_consistent(self._getTargetInterface())) - - def test_class_consistent__iro__(self): - from zope.interface import implementedBy - from zope.interface import ro - - self.assertTrue( - ro.is_consistent(implementedBy(self._getTargetClass())) - ) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_odd_declarations.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_odd_declarations.py deleted file mode 100644 index 09a8de8..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_odd_declarations.py +++ /dev/null @@ -1,301 +0,0 @@ -############################################################################## -# -# Copyright (c) 2003 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Test interface declarations against ExtensionClass-like classes. - -These tests are to make sure we do something sane in the presence of -classic ExtensionClass classes and instances. -""" -import unittest - -from zope.interface import Interface -from zope.interface import classImplements -from zope.interface import classImplementsOnly -from zope.interface import directlyProvidedBy -from zope.interface import directlyProvides -from zope.interface import implementedBy -from zope.interface import implementer -from zope.interface import providedBy -from zope.interface.tests import odd - - -class I1(Interface): - pass - - -class I2(Interface): - pass - - -class I3(Interface): - pass - - -class I31(I3): - pass - - -class I4(Interface): - pass - - -class I5(Interface): - pass - - -class Odd: - pass - - -Odd = odd.MetaClass('Odd', Odd.__bases__, {}) - - -class B(Odd): - __implemented__ = I2 - - -# TODO: We are going to need more magic to make classProvides work with odd -# classes. This will work in the next iteration. For now, we'll use -# a different mechanism. - -# from zope.interface import classProvides -class A(Odd): - pass - - -classImplements(A, I1) - - -class C(A, B): - pass - - -classImplements(C, I31) - - -class Test(unittest.TestCase): - - def test_ObjectSpecification(self): - c = C() - directlyProvides(c, I4) - self.assertEqual([i.getName() for i in providedBy(c)], - ['I4', 'I31', 'I1', 'I2'] - ) - self.assertEqual([i.getName() for i in providedBy(c).flattened()], - ['I4', 'I31', 'I3', 'I1', 'I2', 'Interface'] - ) - self.assertIn(I1, providedBy(c)) - self.assertNotIn(I3, providedBy(c)) - self.assertTrue(providedBy(c).extends(I3)) - self.assertTrue(providedBy(c).extends(I31)) - self.assertFalse(providedBy(c).extends(I5)) - - class COnly(A, B): - pass - classImplementsOnly(COnly, I31) - - class D(COnly): - pass - classImplements(D, I5) - - classImplements(D, I5) - - c = D() - directlyProvides(c, I4) - self.assertEqual([i.getName() for i in providedBy(c)], - ['I4', 'I5', 'I31']) - self.assertEqual([i.getName() for i in providedBy(c).flattened()], - ['I4', 'I5', 'I31', 'I3', 'Interface']) - self.assertNotIn(I1, providedBy(c)) - self.assertNotIn(I3, providedBy(c)) - self.assertTrue(providedBy(c).extends(I3)) - self.assertFalse(providedBy(c).extends(I1)) - self.assertTrue(providedBy(c).extends(I31)) - self.assertTrue(providedBy(c).extends(I5)) - - class COnly(A, B): - __implemented__ = I31 - - class D(COnly): - pass - - classImplements(D, I5) - classImplements(D, I5) - - c = D() - directlyProvides(c, I4) - self.assertEqual([i.getName() for i in providedBy(c)], - ['I4', 'I5', 'I31']) - self.assertEqual([i.getName() for i in providedBy(c).flattened()], - ['I4', 'I5', 'I31', 'I3', 'Interface']) - self.assertNotIn(I1, providedBy(c)) - self.assertNotIn(I3, providedBy(c)) - self.assertTrue(providedBy(c).extends(I3)) - self.assertFalse(providedBy(c).extends(I1)) - self.assertTrue(providedBy(c).extends(I31)) - self.assertTrue(providedBy(c).extends(I5)) - - def test_classImplements(self): - - @implementer(I3) - class A(Odd): - pass - - @implementer(I4) - class B(Odd): - pass - - class C(A, B): - pass - - classImplements(C, I1, I2) - self.assertEqual([i.getName() for i in implementedBy(C)], - ['I1', 'I2', 'I3', 'I4']) - - classImplements(C, I5) - self.assertEqual([i.getName() for i in implementedBy(C)], - ['I1', 'I2', 'I5', 'I3', 'I4']) - - def test_classImplementsOnly(self): - - @implementer(I3) - class A(Odd): - pass - - @implementer(I4) - class B(Odd): - pass - - class C(A, B): - pass - - classImplementsOnly(C, I1, I2) - self.assertEqual([i.__name__ for i in implementedBy(C)], - ['I1', 'I2']) - - def test_directlyProvides(self): - - class IA1(Interface): - pass - - class IA2(Interface): - pass - - class IB(Interface): - pass - - class IC(Interface): - pass - - class A(Odd): - pass - classImplements(A, IA1, IA2) - - class B(Odd): - pass - classImplements(B, IB) - - class C(A, B): - pass - classImplements(C, IC) - - ob = C() - directlyProvides(ob, I1, I2) - self.assertIn(I1, providedBy(ob)) - self.assertIn(I2, providedBy(ob)) - self.assertIn(IA1, providedBy(ob)) - self.assertIn(IA2, providedBy(ob)) - self.assertIn(IB, providedBy(ob)) - self.assertIn(IC, providedBy(ob)) - - directlyProvides(ob, directlyProvidedBy(ob) - I2) - self.assertIn(I1, providedBy(ob)) - self.assertNotIn(I2, providedBy(ob)) - self.assertNotIn(I2, providedBy(ob)) - directlyProvides(ob, directlyProvidedBy(ob), I2) - self.assertIn(I2, providedBy(ob)) - - # see above - # def TODO_test_classProvides_fails_for_odd_class(self): - # try: - # class A(Odd): - # classProvides(I1) - # except TypeError: - # pass # Success - # self.assert_( - # False, - # "Shouldn't be able to use directlyProvides on odd class." - # ) - - def test_implementedBy(self): - - class I2(I1): - pass - - class C1(Odd): - pass - - classImplements(C1, I2) - - class C2(C1): - pass - - classImplements(C2, I3) - - self.assertEqual([i.getName() for i in implementedBy(C2)], - ['I3', 'I2']) - - def test_odd_metaclass_that_doesnt_subclass_type(self): - # This was originally a doctest in odd.py. - # It verifies that the metaclass the rest of these tests use - # works as expected. - - # This is used for testing support for ExtensionClass in new - # interfaces. - - class A: - a = 1 - - A = odd.MetaClass('A', A.__bases__, A.__dict__) - - class B: - b = 1 - - B = odd.MetaClass('B', B.__bases__, B.__dict__) - - class C(A, B): - pass - - self.assertEqual(C.__bases__, (A, B)) - - a = A() - aa = A() - self.assertEqual(a.a, 1) - self.assertEqual(aa.a, 1) - - aa.a = 2 - self.assertEqual(a.a, 1) - self.assertEqual(aa.a, 2) - - c = C() - self.assertEqual(c.a, 1) - self.assertEqual(c.b, 1) - - c.b = 2 - self.assertEqual(c.b, 2) - - C.c = 1 - self.assertEqual(c.c, 1) - c.c - - self.assertIs(C.__class__.__class__, C.__class__) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_registry.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_registry.py deleted file mode 100644 index 0da200e..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_registry.py +++ /dev/null @@ -1,3391 +0,0 @@ -############################################################################## -# -# Copyright (c) 2001, 2002, 2009 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Component Registry Tests""" -# pylint:disable=protected-access -import unittest - -from zope.interface import Interface -from zope.interface.adapter import VerifyingAdapterRegistry -from zope.interface.registry import Components - - -class ComponentsTests(unittest.TestCase): - - def _getTargetClass(self): - return Components - - def _makeOne(self, name='test', *args, **kw): - return self._getTargetClass()(name, *args, **kw) - - def _wrapEvents(self): - from zope.interface import registry - _events = [] - - def _notify(*args, **kw): - _events.append((args, kw)) - - _monkey = _Monkey(registry, notify=_notify) - return _monkey, _events - - def test_ctor_no_bases(self): - from zope.interface.adapter import AdapterRegistry - comp = self._makeOne('testing') - self.assertEqual(comp.__name__, 'testing') - self.assertEqual(comp.__bases__, ()) - self.assertIsInstance(comp.adapters, AdapterRegistry) - self.assertIsInstance(comp.utilities, AdapterRegistry) - self.assertEqual(comp.adapters.__bases__, ()) - self.assertEqual(comp.utilities.__bases__, ()) - self.assertEqual(comp._utility_registrations, {}) - self.assertEqual(comp._adapter_registrations, {}) - self.assertEqual(comp._subscription_registrations, []) - self.assertEqual(comp._handler_registrations, []) - - def test_ctor_w_base(self): - base = self._makeOne('base') - comp = self._makeOne('testing', (base,)) - self.assertEqual(comp.__name__, 'testing') - self.assertEqual(comp.__bases__, (base,)) - self.assertEqual(comp.adapters.__bases__, (base.adapters,)) - self.assertEqual(comp.utilities.__bases__, (base.utilities,)) - - def test___repr__(self): - comp = self._makeOne('testing') - self.assertEqual(repr(comp), '') - - # test _init_registries / _init_registrations via only caller, __init__. - - def test_assign_to___bases__(self): - base1 = self._makeOne('base1') - base2 = self._makeOne('base2') - comp = self._makeOne() - comp.__bases__ = (base1, base2) - self.assertEqual(comp.__bases__, (base1, base2)) - self.assertEqual(comp.adapters.__bases__, - (base1.adapters, base2.adapters)) - self.assertEqual(comp.utilities.__bases__, - (base1.utilities, base2.utilities)) - - def test_registerUtility_with_component_name(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import named - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - - @named('foo') - class Foo: - pass - - foo = Foo() - _info = 'info' - - comp = self._makeOne() - comp.registerUtility(foo, ifoo, info=_info) - self.assertEqual( - comp._utility_registrations[ifoo, 'foo'], - (foo, _info, None)) - - def test_registerUtility_both_factory_and_component(self): - def _factory(): - raise NotImplementedError() - _to_reg = object() - comp = self._makeOne() - self.assertRaises(TypeError, comp.registerUtility, - component=_to_reg, factory=_factory) - - def test_registerUtility_w_component(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.interfaces import Registered - from zope.interface.registry import UtilityRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _info = 'info' - _name = 'name' - _to_reg = object() - comp = self._makeOne() - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerUtility(_to_reg, ifoo, _name, _info) - self.assertIs(comp.utilities._adapters[0][ifoo][_name], _to_reg) - self.assertEqual(comp._utility_registrations[ifoo, _name], - (_to_reg, _info, None)) - self.assertEqual(comp.utilities._subscribers[0][ifoo][''], (_to_reg,)) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Registered) - self.assertIsInstance(event.object, UtilityRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertIs(event.object.name, _name) - self.assertIs(event.object.component, _to_reg) - self.assertIs(event.object.info, _info) - self.assertIsNone(event.object.factory) - - def test_registerUtility_w_factory(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.interfaces import Registered - from zope.interface.registry import UtilityRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _info = 'info' - _name = 'name' - _to_reg = object() - - def _factory(): - return _to_reg - - comp = self._makeOne() - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerUtility(None, ifoo, _name, _info, factory=_factory) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Registered) - self.assertIsInstance(event.object, UtilityRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertIs(event.object.name, _name) - self.assertIs(event.object.component, _to_reg) - self.assertIs(event.object.info, _info) - self.assertIs(event.object.factory, _factory) - - def test_registerUtility_no_provided_available(self): - - class Foo: - pass - - _info = 'info' - _name = 'name' - _to_reg = Foo() - comp = self._makeOne() - self.assertRaises(TypeError, - comp.registerUtility, _to_reg, None, _name, _info) - - def test_registerUtility_wo_provided(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import directlyProvides - from zope.interface.interfaces import Registered - from zope.interface.registry import UtilityRegistration - - class IFoo(InterfaceClass): - pass - - class Foo: - pass - - ifoo = IFoo('IFoo') - _info = 'info' - _name = 'name' - _to_reg = Foo() - directlyProvides(_to_reg, ifoo) - comp = self._makeOne() - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerUtility(_to_reg, None, _name, _info) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Registered) - self.assertIsInstance(event.object, UtilityRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertIs(event.object.name, _name) - self.assertIs(event.object.component, _to_reg) - self.assertIs(event.object.info, _info) - self.assertIsNone(event.object.factory) - - def test_registerUtility_duplicates_existing_reg(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _info = 'info' - _name = 'name' - _to_reg = object() - comp = self._makeOne() - comp.registerUtility(_to_reg, ifoo, _name, _info) - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerUtility(_to_reg, ifoo, _name, _info) - self.assertEqual(len(_events), 0) - - def test_registerUtility_w_different_info(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _info1 = 'info1' - _info2 = 'info2' - _name = 'name' - _to_reg = object() - comp = self._makeOne() - comp.registerUtility(_to_reg, ifoo, _name, _info1) - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerUtility(_to_reg, ifoo, _name, _info2) - self.assertEqual(len(_events), 2) # unreg, reg - self.assertEqual(comp._utility_registrations[(ifoo, _name)], - (_to_reg, _info2, None)) # replaced - self.assertEqual(comp.utilities._subscribers[0][ifoo][''], - (_to_reg,)) - - def test_registerUtility_w_different_names_same_component(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _info = 'info' - _name1 = 'name1' - _name2 = 'name2' - _other_reg = object() - _to_reg = object() - comp = self._makeOne() - comp.registerUtility(_other_reg, ifoo, _name1, _info) - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerUtility(_to_reg, ifoo, _name2, _info) - self.assertEqual(len(_events), 1) # reg - self.assertEqual(comp._utility_registrations[(ifoo, _name1)], - (_other_reg, _info, None)) - self.assertEqual(comp._utility_registrations[(ifoo, _name2)], - (_to_reg, _info, None)) - self.assertEqual(comp.utilities._subscribers[0][ifoo][''], - (_other_reg, _to_reg,)) - - def test_registerUtility_replaces_existing_reg(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.interfaces import Registered - from zope.interface.interfaces import Unregistered - from zope.interface.registry import UtilityRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _info = 'info' - _name = 'name' - _before, _after = object(), object() - comp = self._makeOne() - comp.registerUtility(_before, ifoo, _name, _info) - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerUtility(_after, ifoo, _name, _info) - self.assertEqual(len(_events), 2) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Unregistered) - self.assertIsInstance(event.object, UtilityRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertIs(event.object.name, _name) - self.assertIs(event.object.component, _before) - self.assertIs(event.object.info, _info) - self.assertIsNone(event.object.factory) - args, kw = _events[1] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Registered) - self.assertIsInstance(event.object, UtilityRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertIs(event.object.name, _name) - self.assertIs(event.object.component, _after) - self.assertIs(event.object.info, _info) - self.assertIsNone(event.object.factory) - - def test_registerUtility_w_existing_subscr(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _info = 'info' - _name1 = 'name1' - _name2 = 'name2' - _to_reg = object() - comp = self._makeOne() - comp.registerUtility(_to_reg, ifoo, _name1, _info) - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerUtility(_to_reg, ifoo, _name2, _info) - self.assertEqual(comp.utilities._subscribers[0][ifoo][''], (_to_reg,)) - - def test_registerUtility_wo_event(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _info = 'info' - _name = 'name' - _to_reg = object() - comp = self._makeOne() - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerUtility(_to_reg, ifoo, _name, _info, False) - self.assertEqual(len(_events), 0) - - def test_registerUtility_changes_object_identity_after(self): - # If a subclass changes the identity of the _utility_registrations, - # the cache is updated and the right thing still happens. - class CompThatChangesAfter1Reg(self._getTargetClass()): - reg_count = 0 - - def registerUtility(self, *args): - self.reg_count += 1 - super().registerUtility(*args) - if self.reg_count == 1: - self._utility_registrations = dict( - self._utility_registrations - ) - - comp = CompThatChangesAfter1Reg() - comp.registerUtility(object(), Interface) - - self.assertEqual(len(list(comp.registeredUtilities())), 1) - - class IFoo(Interface): - pass - - comp.registerUtility(object(), IFoo) - self.assertEqual(len(list(comp.registeredUtilities())), 2) - - def test_registerUtility_changes_object_identity_before(self): - # If a subclass changes the identity of the _utility_registrations, - # the cache is updated and the right thing still happens. - class CompThatChangesAfter2Reg(self._getTargetClass()): - reg_count = 0 - - def registerUtility(self, *args): - self.reg_count += 1 - if self.reg_count == 2: - self._utility_registrations = dict( - self._utility_registrations - ) - - super().registerUtility(*args) - - comp = CompThatChangesAfter2Reg() - comp.registerUtility(object(), Interface) - - self.assertEqual(len(list(comp.registeredUtilities())), 1) - - class IFoo(Interface): - pass - - comp.registerUtility(object(), IFoo) - self.assertEqual(len(list(comp.registeredUtilities())), 2) - - class IBar(Interface): - pass - - comp.registerUtility(object(), IBar) - self.assertEqual(len(list(comp.registeredUtilities())), 3) - - def test_unregisterUtility_wo_factory_nor_component_nor_provided(self): - comp = self._makeOne() - self.assertRaises(TypeError, comp.unregisterUtility, - component=None, provided=None, factory=None) - - def test_unregisterUtility_both_factory_and_component(self): - def _factory(): - raise NotImplementedError() - _to_reg = object() - comp = self._makeOne() - self.assertRaises(TypeError, comp.unregisterUtility, - component=_to_reg, factory=_factory) - - def test_unregisterUtility_w_component_miss(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _name = 'name' - _to_reg = object() - comp = self._makeOne() - _monkey, _events = self._wrapEvents() - with _monkey: - unreg = comp.unregisterUtility(_to_reg, ifoo, _name) - self.assertFalse(unreg) - self.assertFalse(_events) - - def test_unregisterUtility_w_component(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.interfaces import Unregistered - from zope.interface.registry import UtilityRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _name = 'name' - _to_reg = object() - comp = self._makeOne() - comp.registerUtility(_to_reg, ifoo, _name) - _monkey, _events = self._wrapEvents() - with _monkey: - unreg = comp.unregisterUtility(_to_reg, ifoo, _name) - self.assertTrue(unreg) - self.assertFalse(comp.utilities._adapters) # all erased - self.assertNotIn((ifoo, _name), comp._utility_registrations) - self.assertFalse(comp.utilities._subscribers) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Unregistered) - self.assertIsInstance(event.object, UtilityRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertIs(event.object.name, _name) - self.assertIs(event.object.component, _to_reg) - self.assertIsNone(event.object.factory) - - def test_unregisterUtility_w_factory(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.interfaces import Unregistered - from zope.interface.registry import UtilityRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _info = 'info' - _name = 'name' - _to_reg = object() - - def _factory(): - return _to_reg - - comp = self._makeOne() - comp.registerUtility(None, ifoo, _name, _info, factory=_factory) - _monkey, _events = self._wrapEvents() - with _monkey: - unreg = comp.unregisterUtility(None, ifoo, _name, factory=_factory) - self.assertTrue(unreg) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Unregistered) - self.assertIsInstance(event.object, UtilityRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertIs(event.object.name, _name) - self.assertIs(event.object.component, _to_reg) - self.assertIs(event.object.factory, _factory) - - def test_unregisterUtility_wo_explicit_provided(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import directlyProvides - from zope.interface.interfaces import Unregistered - from zope.interface.registry import UtilityRegistration - - class IFoo(InterfaceClass): - pass - - class Foo: - pass - - ifoo = IFoo('IFoo') - _info = 'info' - _name = 'name' - _to_reg = Foo() - directlyProvides(_to_reg, ifoo) - comp = self._makeOne() - comp.registerUtility(_to_reg, ifoo, _name, _info) - _monkey, _events = self._wrapEvents() - with _monkey: - unreg = comp.unregisterUtility(_to_reg, None, _name) - self.assertTrue(unreg) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Unregistered) - self.assertIsInstance(event.object, UtilityRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertIs(event.object.name, _name) - self.assertIs(event.object.component, _to_reg) - self.assertIs(event.object.info, _info) - self.assertIsNone(event.object.factory) - - def test_unregisterUtility_wo_component_or_factory(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import directlyProvides - from zope.interface.interfaces import Unregistered - from zope.interface.registry import UtilityRegistration - - class IFoo(InterfaceClass): - pass - - class Foo: - pass - - ifoo = IFoo('IFoo') - _info = 'info' - _name = 'name' - _to_reg = Foo() - directlyProvides(_to_reg, ifoo) - comp = self._makeOne() - comp.registerUtility(_to_reg, ifoo, _name, _info) - _monkey, _events = self._wrapEvents() - with _monkey: - # Just pass the interface / name - unreg = comp.unregisterUtility(provided=ifoo, name=_name) - self.assertTrue(unreg) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Unregistered) - self.assertIsInstance(event.object, UtilityRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertIs(event.object.name, _name) - self.assertIs(event.object.component, _to_reg) - self.assertIs(event.object.info, _info) - self.assertIsNone(event.object.factory) - - def test_unregisterUtility_w_existing_subscr(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _info = 'info' - _name1 = 'name1' - _name2 = 'name2' - _to_reg = object() - comp = self._makeOne() - comp.registerUtility(_to_reg, ifoo, _name1, _info) - comp.registerUtility(_to_reg, ifoo, _name2, _info) - _monkey, _events = self._wrapEvents() - with _monkey: - comp.unregisterUtility(_to_reg, ifoo, _name2) - self.assertEqual(comp.utilities._subscribers[0][ifoo][''], (_to_reg,)) - - def test_unregisterUtility_w_existing_subscr_non_hashable(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _info = 'info' - _name1 = 'name1' - _name2 = 'name2' - _to_reg = dict() - comp = self._makeOne() - comp.registerUtility(_to_reg, ifoo, _name1, _info) - comp.registerUtility(_to_reg, ifoo, _name2, _info) - _monkey, _events = self._wrapEvents() - with _monkey: - comp.unregisterUtility(_to_reg, ifoo, _name2) - self.assertEqual(comp.utilities._subscribers[0][ifoo][''], (_to_reg,)) - - def test_unregisterUtility_w_existing_subs_non_hashable_fresh_cache(self): - # We correctly populate the cache of registrations if it has gone away - # (for example, the Components was unpickled) - from zope.interface.declarations import InterfaceClass - from zope.interface.registry import _UtilityRegistrations - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _info = 'info' - _name1 = 'name1' - _name2 = 'name2' - _to_reg = dict() - comp = self._makeOne() - comp.registerUtility(_to_reg, ifoo, _name1, _info) - comp.registerUtility(_to_reg, ifoo, _name2, _info) - - _monkey, _events = self._wrapEvents() - with _monkey: - comp.unregisterUtility(_to_reg, ifoo, _name2) - self.assertEqual(comp.utilities._subscribers[0][ifoo][''], (_to_reg,)) - - def test_unregisterUtility_w_existing_subscr_non_hashable_reinitted(self): - # We correctly populate the cache of registrations if the base objects - # change out from under us - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _info = 'info' - _name1 = 'name1' - _name2 = 'name2' - _to_reg = dict() - comp = self._makeOne() - comp.registerUtility(_to_reg, ifoo, _name1, _info) - comp.registerUtility(_to_reg, ifoo, _name2, _info) - - # zope.component.testing does this - comp.__init__('base') - - comp.registerUtility(_to_reg, ifoo, _name2, _info) - - _monkey, _events = self._wrapEvents() - with _monkey: - # Nothing to do, but we don't break either - comp.unregisterUtility(_to_reg, ifoo, _name2) - self.assertEqual(0, len(comp.utilities._subscribers)) - - def test_unregisterUtility_w_existing_subscr_other_component(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _info = 'info' - _name1 = 'name1' - _name2 = 'name2' - _other_reg = object() - _to_reg = object() - comp = self._makeOne() - comp.registerUtility(_other_reg, ifoo, _name1, _info) - comp.registerUtility(_to_reg, ifoo, _name2, _info) - _monkey, _events = self._wrapEvents() - with _monkey: - comp.unregisterUtility(_to_reg, ifoo, _name2) - self.assertEqual(comp.utilities._subscribers[0][ifoo][''], - (_other_reg,)) - - def test_unregisterUtility_w_existing_subscr_oter_comp_mixed_hash(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _info = 'info' - _name1 = 'name1' - _name2 = 'name2' - # First register something hashable - _other_reg = object() - # Then it transfers to something unhashable - _to_reg = dict() - comp = self._makeOne() - comp.registerUtility(_other_reg, ifoo, _name1, _info) - comp.registerUtility(_to_reg, ifoo, _name2, _info) - _monkey, _events = self._wrapEvents() - with _monkey: - comp.unregisterUtility(_to_reg, ifoo, _name2) - self.assertEqual(comp.utilities._subscribers[0][ifoo][''], - (_other_reg,)) - - def test_registeredUtilities_empty(self): - comp = self._makeOne() - self.assertEqual(list(comp.registeredUtilities()), []) - - def test_registeredUtilities_notempty(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.registry import UtilityRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _info = 'info' - _name1 = 'name1' - _name2 = 'name2' - _to_reg = object() - comp = self._makeOne() - comp.registerUtility(_to_reg, ifoo, _name1, _info) - comp.registerUtility(_to_reg, ifoo, _name2, _info) - reg = sorted(comp.registeredUtilities(), key=lambda r: r.name) - self.assertEqual(len(reg), 2) - self.assertIsInstance(reg[0], UtilityRegistration) - self.assertIs(reg[0].registry, comp) - self.assertIs(reg[0].provided, ifoo) - self.assertIs(reg[0].name, _name1) - self.assertIs(reg[0].component, _to_reg) - self.assertIs(reg[0].info, _info) - self.assertIsNone(reg[0].factory) - self.assertIsInstance(reg[1], UtilityRegistration) - self.assertIs(reg[1].registry, comp) - self.assertIs(reg[1].provided, ifoo) - self.assertIs(reg[1].name, _name2) - self.assertIs(reg[1].component, _to_reg) - self.assertIs(reg[1].info, _info) - self.assertIsNone(reg[1].factory) - - def test_queryUtility_miss_no_default(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - comp = self._makeOne() - self.assertIsNone(comp.queryUtility(ifoo)) - - def test_queryUtility_miss_w_default(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - comp = self._makeOne() - _default = object() - self.assertIs(comp.queryUtility(ifoo, default=_default), _default) - - def test_queryUtility_hit(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _to_reg = object() - comp = self._makeOne() - comp.registerUtility(_to_reg, ifoo) - self.assertIs(comp.queryUtility(ifoo), _to_reg) - - def test_getUtility_miss(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.interfaces import ComponentLookupError - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - comp = self._makeOne() - self.assertRaises(ComponentLookupError, comp.getUtility, ifoo) - - def test_getUtility_hit(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _to_reg = object() - comp = self._makeOne() - comp.registerUtility(_to_reg, ifoo) - self.assertIs(comp.getUtility(ifoo), _to_reg) - - def test_getUtilitiesFor_miss(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - comp = self._makeOne() - self.assertEqual(list(comp.getUtilitiesFor(ifoo)), []) - - def test_getUtilitiesFor_hit(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _name1 = 'name1' - _name2 = 'name2' - _to_reg = object() - comp = self._makeOne() - comp.registerUtility(_to_reg, ifoo, name=_name1) - comp.registerUtility(_to_reg, ifoo, name=_name2) - self.assertEqual(sorted(comp.getUtilitiesFor(ifoo)), - [(_name1, _to_reg), (_name2, _to_reg)]) - - def test_getAllUtilitiesRegisteredFor_miss(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - comp = self._makeOne() - self.assertEqual(list(comp.getAllUtilitiesRegisteredFor(ifoo)), []) - - def test_getAllUtilitiesRegisteredFor_hit(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _name1 = 'name1' - _name2 = 'name2' - _to_reg = object() - comp = self._makeOne() - comp.registerUtility(_to_reg, ifoo, name=_name1) - comp.registerUtility(_to_reg, ifoo, name=_name2) - self.assertEqual(list(comp.getAllUtilitiesRegisteredFor(ifoo)), - [_to_reg]) - - def test_registerAdapter_with_component_name(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import named - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - - @named('foo') - class Foo: - pass - _info = 'info' - - comp = self._makeOne() - comp.registerAdapter(Foo, (ibar,), ifoo, info=_info) - - self.assertEqual( - comp._adapter_registrations[(ibar,), ifoo, 'foo'], - (Foo, _info)) - - def test_registerAdapter_w_explicit_provided_and_required(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.interfaces import Registered - from zope.interface.registry import AdapterRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - _info = 'info' - _name = 'name' - - def _factory(context): - raise NotImplementedError() - comp = self._makeOne() - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerAdapter(_factory, (ibar,), ifoo, _name, _info) - self.assertIs( - comp.adapters._adapters[1][ibar][ifoo][_name], - _factory - ) - self.assertEqual(comp._adapter_registrations[(ibar,), ifoo, _name], - (_factory, _info)) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Registered) - self.assertIsInstance(event.object, AdapterRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertEqual(event.object.required, (ibar,)) - self.assertIs(event.object.name, _name) - self.assertIs(event.object.info, _info) - self.assertIs(event.object.factory, _factory) - - def test_registerAdapter_no_provided_available(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ibar = IFoo('IBar') - _info = 'info' - _name = 'name' - - class _Factory: - pass - - comp = self._makeOne() - self.assertRaises(TypeError, comp.registerAdapter, _Factory, (ibar,), - name=_name, info=_info) - - def test_registerAdapter_wo_explicit_provided(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import implementer - from zope.interface.interfaces import Registered - from zope.interface.registry import AdapterRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - _info = 'info' - _name = 'name' - - @implementer(ifoo) - class _Factory: - pass - - comp = self._makeOne() - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerAdapter(_Factory, (ibar,), name=_name, info=_info) - self.assertIs( - comp.adapters._adapters[1][ibar][ifoo][_name], - _Factory - ) - self.assertEqual(comp._adapter_registrations[(ibar,), ifoo, _name], - (_Factory, _info)) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Registered) - self.assertIsInstance(event.object, AdapterRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertEqual(event.object.required, (ibar,)) - self.assertIs(event.object.name, _name) - self.assertIs(event.object.info, _info) - self.assertIs(event.object.factory, _Factory) - - def test_registerAdapter_no_required_available(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - - _info = 'info' - _name = 'name' - - class _Factory: - pass - - comp = self._makeOne() - self.assertRaises(TypeError, comp.registerAdapter, _Factory, - provided=ifoo, name=_name, info=_info) - - def test_registerAdapter_w_invalid_required(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - _info = 'info' - _name = 'name' - - class _Factory: - pass - - comp = self._makeOne() - self.assertRaises(TypeError, comp.registerAdapter, _Factory, - ibar, provided=ifoo, name=_name, info=_info) - - def test_registerAdapter_w_required_containing_None(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.interface import Interface - from zope.interface.interfaces import Registered - from zope.interface.registry import AdapterRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _info = 'info' - _name = 'name' - - class _Factory: - pass - - comp = self._makeOne() - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerAdapter(_Factory, [None], provided=ifoo, - name=_name, info=_info) - self.assertIs( - comp.adapters._adapters[1][Interface][ifoo][_name], - _Factory - ) - self.assertEqual( - comp._adapter_registrations[(Interface,), ifoo, _name], - (_Factory, _info) - ) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Registered) - self.assertIsInstance(event.object, AdapterRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertEqual(event.object.required, (Interface,)) - self.assertIs(event.object.name, _name) - self.assertIs(event.object.info, _info) - self.assertIs(event.object.factory, _Factory) - - def test_registerAdapter_w_required_containing_class(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import implementedBy - from zope.interface.declarations import implementer - from zope.interface.interfaces import Registered - from zope.interface.registry import AdapterRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - _info = 'info' - _name = 'name' - - class _Factory: - pass - - @implementer(ibar) - class _Context: - pass - - _ctx_impl = implementedBy(_Context) - comp = self._makeOne() - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerAdapter(_Factory, [_Context], provided=ifoo, - name=_name, info=_info) - self.assertIs( - comp.adapters._adapters[1][_ctx_impl][ifoo][_name], - _Factory - ) - self.assertEqual( - comp._adapter_registrations[(_ctx_impl,), ifoo, _name], - (_Factory, _info) - ) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Registered) - self.assertIsInstance(event.object, AdapterRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertEqual(event.object.required, (_ctx_impl,)) - self.assertIs(event.object.name, _name) - self.assertIs(event.object.info, _info) - self.assertIs(event.object.factory, _Factory) - - def test_registerAdapter_w_required_containing_junk(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - - _info = 'info' - _name = 'name' - - class _Factory: - pass - - comp = self._makeOne() - with self.assertRaises(TypeError): - comp.registerAdapter( - _Factory, [object()], provided=ifoo, name=_name, info=_info, - ) - - def test_registerAdapter_wo_explicit_required(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.interfaces import Registered - from zope.interface.registry import AdapterRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - _info = 'info' - _name = 'name' - - class _Factory: - __component_adapts__ = (ibar,) - - comp = self._makeOne() - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerAdapter(_Factory, provided=ifoo, name=_name, - info=_info) - self.assertIs( - comp.adapters._adapters[1][ibar][ifoo][_name], - _Factory - ) - self.assertEqual(comp._adapter_registrations[(ibar,), ifoo, _name], - (_Factory, _info)) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Registered) - self.assertIsInstance(event.object, AdapterRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertEqual(event.object.required, (ibar,)) - self.assertIs(event.object.name, _name) - self.assertIs(event.object.info, _info) - self.assertIs(event.object.factory, _Factory) - - def test_registerAdapter_wo_event(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - _info = 'info' - _name = 'name' - - def _factory(context): - raise NotImplementedError() - comp = self._makeOne() - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerAdapter(_factory, (ibar,), ifoo, _name, _info, - event=False) - self.assertEqual(len(_events), 0) - - def test_unregisterAdapter_neither_factory_nor_provided(self): - comp = self._makeOne() - self.assertRaises(TypeError, comp.unregisterAdapter, - factory=None, provided=None) - - def test_unregisterAdapter_neither_factory_nor_required(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - comp = self._makeOne() - self.assertRaises(TypeError, comp.unregisterAdapter, - factory=None, provided=ifoo, required=None) - - def test_unregisterAdapter_miss(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - - class _Factory: - pass - - comp = self._makeOne() - _monkey, _events = self._wrapEvents() - with _monkey: - unreg = comp.unregisterAdapter(_Factory, (ibar,), ifoo) - self.assertFalse(unreg) - - def test_unregisterAdapter_hit_w_explicit_provided_and_required(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.interfaces import Unregistered - from zope.interface.registry import AdapterRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - - class _Factory: - pass - - comp = self._makeOne() - comp.registerAdapter(_Factory, (ibar,), ifoo) - _monkey, _events = self._wrapEvents() - with _monkey: - unreg = comp.unregisterAdapter(_Factory, (ibar,), ifoo) - self.assertTrue(unreg) - self.assertFalse(comp.adapters._adapters) - self.assertFalse(comp._adapter_registrations) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Unregistered) - self.assertIsInstance(event.object, AdapterRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertEqual(event.object.required, (ibar,)) - self.assertEqual(event.object.name, '') - self.assertEqual(event.object.info, '') - self.assertIs(event.object.factory, _Factory) - - def test_unregisterAdapter_wo_explicit_provided(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import implementer - from zope.interface.interfaces import Unregistered - from zope.interface.registry import AdapterRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - - @implementer(ifoo) - class _Factory: - pass - - comp = self._makeOne() - comp.registerAdapter(_Factory, (ibar,), ifoo) - _monkey, _events = self._wrapEvents() - with _monkey: - unreg = comp.unregisterAdapter(_Factory, (ibar,)) - self.assertTrue(unreg) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Unregistered) - self.assertIsInstance(event.object, AdapterRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertEqual(event.object.required, (ibar,)) - self.assertEqual(event.object.name, '') - self.assertEqual(event.object.info, '') - self.assertIs(event.object.factory, _Factory) - - def test_unregisterAdapter_wo_explicit_required(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.interfaces import Unregistered - from zope.interface.registry import AdapterRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - - class _Factory: - __component_adapts__ = (ibar,) - - comp = self._makeOne() - comp.registerAdapter(_Factory, (ibar,), ifoo) - _monkey, _events = self._wrapEvents() - with _monkey: - unreg = comp.unregisterAdapter(_Factory, provided=ifoo) - self.assertTrue(unreg) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Unregistered) - self.assertIsInstance(event.object, AdapterRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertEqual(event.object.required, (ibar,)) - self.assertEqual(event.object.name, '') - self.assertEqual(event.object.info, '') - self.assertIs(event.object.factory, _Factory) - - def test_registeredAdapters_empty(self): - comp = self._makeOne() - self.assertEqual(list(comp.registeredAdapters()), []) - - def test_registeredAdapters_notempty(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.registry import AdapterRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IFoo') - _info = 'info' - _name1 = 'name1' - _name2 = 'name2' - - class _Factory: - pass - - comp = self._makeOne() - comp.registerAdapter(_Factory, (ibar,), ifoo, _name1, _info) - comp.registerAdapter(_Factory, (ibar,), ifoo, _name2, _info) - reg = sorted(comp.registeredAdapters(), key=lambda r: r.name) - self.assertEqual(len(reg), 2) - self.assertIsInstance(reg[0], AdapterRegistration) - self.assertIs(reg[0].registry, comp) - self.assertIs(reg[0].provided, ifoo) - self.assertEqual(reg[0].required, (ibar,)) - self.assertIs(reg[0].name, _name1) - self.assertIs(reg[0].info, _info) - self.assertIs(reg[0].factory, _Factory) - self.assertIsInstance(reg[1], AdapterRegistration) - self.assertIs(reg[1].registry, comp) - self.assertIs(reg[1].provided, ifoo) - self.assertEqual(reg[1].required, (ibar,)) - self.assertIs(reg[1].name, _name2) - self.assertIs(reg[1].info, _info) - self.assertIs(reg[1].factory, _Factory) - - def test_queryAdapter_miss_no_default(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - comp = self._makeOne() - _context = object() - self.assertIsNone(comp.queryAdapter(_context, ifoo)) - - def test_queryAdapter_miss_w_default(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - comp = self._makeOne() - _context = object() - _default = object() - self.assertIs( - comp.queryAdapter(_context, ifoo, default=_default), - _default - ) - - def test_queryAdapter_hit(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import implementer - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - - class _Factory: - def __init__(self, context): - self.context = context - - @implementer(ibar) - class _Context: - pass - - _context = _Context() - comp = self._makeOne() - comp.registerAdapter(_Factory, (ibar,), ifoo) - adapter = comp.queryAdapter(_context, ifoo) - self.assertIsInstance(adapter, _Factory) - self.assertIs(adapter.context, _context) - - def test_getAdapter_miss(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import implementer - from zope.interface.interfaces import ComponentLookupError - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - - @implementer(ibar) - class _Context: - pass - - _context = _Context() - comp = self._makeOne() - self.assertRaises(ComponentLookupError, - comp.getAdapter, _context, ifoo) - - def test_getAdapter_hit(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import implementer - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - - class _Factory: - def __init__(self, context): - self.context = context - - @implementer(ibar) - class _Context: - pass - - _context = _Context() - comp = self._makeOne() - comp.registerAdapter(_Factory, (ibar,), ifoo) - adapter = comp.getAdapter(_context, ifoo) - self.assertIsInstance(adapter, _Factory) - self.assertIs(adapter.context, _context) - - def test_getAdapter_hit_super(self): - from zope.interface import Interface - from zope.interface.declarations import implementer - - class IBase(Interface): - pass - - class IDerived(IBase): - pass - - class IFoo(Interface): - pass - - @implementer(IBase) - class Base: - pass - - @implementer(IDerived) - class Derived(Base): - pass - - class AdapterBase: - def __init__(self, context): - self.context = context - - class AdapterDerived: - def __init__(self, context): - self.context = context - - comp = self._makeOne() - comp.registerAdapter(AdapterDerived, (IDerived,), IFoo) - comp.registerAdapter(AdapterBase, (IBase,), IFoo) - self._should_not_change(comp) - - derived = Derived() - adapter = comp.getAdapter(derived, IFoo) - self.assertIsInstance(adapter, AdapterDerived) - self.assertIs(adapter.context, derived) - - supe = super(Derived, derived) - adapter = comp.getAdapter(supe, IFoo) - self.assertIsInstance(adapter, AdapterBase) - self.assertIs(adapter.context, derived) - - def test_getAdapter_hit_super_w_parent_implements_interface_diamond(self): - from zope.interface import Interface - from zope.interface.declarations import implementer - - class IBase(Interface): - pass - - class IDerived(IBase): - pass - - class IFoo(Interface): - pass - - class Base: - pass - - class Child1(Base): - pass - - @implementer(IBase) - class Child2(Base): - pass - - @implementer(IDerived) - class Derived(Child1, Child2): - pass - - class AdapterBase: - def __init__(self, context): - self.context = context - - class AdapterDerived: - def __init__(self, context): - self.context = context - - comp = self._makeOne() - comp.registerAdapter(AdapterDerived, (IDerived,), IFoo) - comp.registerAdapter(AdapterBase, (IBase,), IFoo) - self._should_not_change(comp) - - derived = Derived() - adapter = comp.getAdapter(derived, IFoo) - self.assertIsInstance(adapter, AdapterDerived) - self.assertIs(adapter.context, derived) - - supe = super(Derived, derived) - adapter = comp.getAdapter(supe, IFoo) - self.assertIsInstance(adapter, AdapterBase) - self.assertIs(adapter.context, derived) - - def test_queryMultiAdapter_miss(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import implementer - - class IFoo(InterfaceClass): - pass - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - ibaz = IFoo('IBaz') - - @implementer(ibar) - class _Context1: - pass - - @implementer(ibaz) - class _Context2: - pass - - _context1 = _Context1() - _context2 = _Context2() - comp = self._makeOne() - self.assertEqual(comp.queryMultiAdapter((_context1, _context2), ifoo), - None) - - def test_queryMultiAdapter_miss_w_default(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import implementer - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - ibaz = IFoo('IBaz') - - @implementer(ibar) - class _Context1: - pass - - @implementer(ibaz) - class _Context2: - pass - - _context1 = _Context1() - _context2 = _Context2() - _default = object() - comp = self._makeOne() - self.assertIs( - comp.queryMultiAdapter((_context1, _context2), ifoo, - default=_default), - _default - ) - - def test_queryMultiAdapter_hit(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import implementer - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - ibaz = IFoo('IBaz') - - @implementer(ibar) - class _Context1: - pass - - @implementer(ibaz) - class _Context2: - pass - - _context1 = _Context1() - _context2 = _Context2() - - class _Factory: - def __init__(self, context1, context2): - self.context = context1, context2 - - comp = self._makeOne() - comp.registerAdapter(_Factory, (ibar, ibaz), ifoo) - adapter = comp.queryMultiAdapter((_context1, _context2), ifoo) - self.assertIsInstance(adapter, _Factory) - self.assertEqual(adapter.context, (_context1, _context2)) - - def test_getMultiAdapter_miss(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import implementer - from zope.interface.interfaces import ComponentLookupError - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - ibaz = IFoo('IBaz') - - @implementer(ibar) - class _Context1: - pass - - @implementer(ibaz) - class _Context2: - pass - - _context1 = _Context1() - _context2 = _Context2() - comp = self._makeOne() - self.assertRaises(ComponentLookupError, - comp.getMultiAdapter, (_context1, _context2), ifoo) - - def test_getMultiAdapter_hit(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import implementer - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - ibaz = IFoo('IBaz') - - @implementer(ibar) - class _Context1: - pass - - @implementer(ibaz) - class _Context2: - pass - - _context1 = _Context1() - _context2 = _Context2() - - class _Factory: - def __init__(self, context1, context2): - self.context = context1, context2 - - comp = self._makeOne() - comp.registerAdapter(_Factory, (ibar, ibaz), ifoo) - adapter = comp.getMultiAdapter((_context1, _context2), ifoo) - self.assertIsInstance(adapter, _Factory) - self.assertEqual(adapter.context, (_context1, _context2)) - - def _should_not_change(self, comp): - # Be sure that none of the underlying structures - # get told that they have changed during this process - # because that invalidates caches. - def no_changes(*args): - self.fail("Nothing should get changed") - comp.changed = no_changes - comp.adapters.changed = no_changes - comp.adapters._v_lookup.changed = no_changes - - def test_getMultiAdapter_hit_super(self): - from zope.interface import Interface - from zope.interface.declarations import implementer - - class IBase(Interface): - pass - - class IDerived(IBase): - pass - - class IFoo(Interface): - pass - - @implementer(IBase) - class Base: - pass - - @implementer(IDerived) - class Derived(Base): - pass - - class AdapterBase: - def __init__(self, context1, context2): - self.context1 = context1 - self.context2 = context2 - - class AdapterDerived(AdapterBase): - pass - - comp = self._makeOne() - comp.registerAdapter(AdapterDerived, (IDerived, IDerived), IFoo) - comp.registerAdapter(AdapterBase, (IBase, IDerived), IFoo) - self._should_not_change(comp) - - derived = Derived() - adapter = comp.getMultiAdapter((derived, derived), IFoo) - self.assertIsInstance(adapter, AdapterDerived) - self.assertIs(adapter.context1, derived) - self.assertIs(adapter.context2, derived) - - supe = super(Derived, derived) - adapter = comp.getMultiAdapter((supe, derived), IFoo) - self.assertIsInstance(adapter, AdapterBase) - self.assertNotIsInstance(adapter, AdapterDerived) - self.assertIs(adapter.context1, derived) - self.assertIs(adapter.context2, derived) - - def test_getAdapters_empty(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import implementer - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - ibaz = IFoo('IBaz') - - @implementer(ibar) - class _Context1: - pass - - @implementer(ibaz) - class _Context2: - pass - - _context1 = _Context1() - _context2 = _Context2() - comp = self._makeOne() - self.assertEqual( - list(comp.getAdapters((_context1, _context2), ifoo)), []) - - def test_getAdapters_factory_returns_None(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import implementer - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - ibaz = IFoo('IBaz') - - @implementer(ibar) - class _Context1: - pass - - @implementer(ibaz) - class _Context2: - pass - - _context1 = _Context1() - _context2 = _Context2() - comp = self._makeOne() - _called_with = [] - - def _side_effect_only(context1, context2): - _called_with.append((context1, context2)) - return None - - comp.registerAdapter(_side_effect_only, (ibar, ibaz), ifoo) - self.assertEqual( - list(comp.getAdapters((_context1, _context2), ifoo)), []) - self.assertEqual(_called_with, [(_context1, _context2)]) - - def test_getAdapters_non_empty(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import implementer - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - ibaz = IFoo('IBaz') - - @implementer(ibar) - class _Context1: - pass - - @implementer(ibaz) - class _Context2: - pass - - _context1 = _Context1() - _context2 = _Context2() - - class _Factory1: - def __init__(self, context1, context2): - self.context = context1, context2 - - class _Factory2: - def __init__(self, context1, context2): - self.context = context1, context2 - - _name1 = 'name1' - _name2 = 'name2' - comp = self._makeOne() - comp.registerAdapter(_Factory1, (ibar, ibaz), ifoo, name=_name1) - comp.registerAdapter(_Factory2, (ibar, ibaz), ifoo, name=_name2) - found = sorted(comp.getAdapters((_context1, _context2), ifoo)) - self.assertEqual(len(found), 2) - self.assertEqual(found[0][0], _name1) - self.assertIsInstance(found[0][1], _Factory1) - self.assertEqual(found[1][0], _name2) - self.assertIsInstance(found[1][1], _Factory2) - - def test_registerSubscriptionAdapter_w_nonblank_name(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - _name = 'name' - _info = 'info' - - def _factory(context): - raise NotImplementedError() - - comp = self._makeOne() - self.assertRaises(TypeError, comp.registerSubscriptionAdapter, - _factory, (ibar,), ifoo, _name, _info) - - def test_registerSubscriptionAdapter_w_explicit_provided_n_required(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.interfaces import Registered - from zope.interface.registry import SubscriptionRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - _blank = '' - _info = 'info' - - def _factory(context): - raise NotImplementedError() - - comp = self._makeOne() - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerSubscriptionAdapter(_factory, (ibar,), ifoo, - info=_info) - reg = comp.adapters._subscribers[1][ibar][ifoo][_blank] - self.assertEqual(len(reg), 1) - self.assertIs(reg[0], _factory) - self.assertEqual(comp._subscription_registrations, - [((ibar,), ifoo, _blank, _factory, _info)]) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Registered) - self.assertIsInstance(event.object, SubscriptionRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertEqual(event.object.required, (ibar,)) - self.assertEqual(event.object.name, _blank) - self.assertIs(event.object.info, _info) - self.assertIs(event.object.factory, _factory) - - def test_registerSubscriptionAdapter_wo_explicit_provided(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import implementer - from zope.interface.interfaces import Registered - from zope.interface.registry import SubscriptionRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - _info = 'info' - _blank = '' - - @implementer(ifoo) - class _Factory: - pass - - comp = self._makeOne() - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerSubscriptionAdapter(_Factory, (ibar,), info=_info) - reg = comp.adapters._subscribers[1][ibar][ifoo][_blank] - self.assertEqual(len(reg), 1) - self.assertIs(reg[0], _Factory) - self.assertEqual(comp._subscription_registrations, - [((ibar,), ifoo, _blank, _Factory, _info)]) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Registered) - self.assertIsInstance(event.object, SubscriptionRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertEqual(event.object.required, (ibar,)) - self.assertEqual(event.object.name, _blank) - self.assertIs(event.object.info, _info) - self.assertIs(event.object.factory, _Factory) - - def test_registerSubscriptionAdapter_wo_explicit_required(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.interfaces import Registered - from zope.interface.registry import SubscriptionRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - _info = 'info' - _blank = '' - - class _Factory: - __component_adapts__ = (ibar,) - - comp = self._makeOne() - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerSubscriptionAdapter( - _Factory, provided=ifoo, info=_info, - ) - reg = comp.adapters._subscribers[1][ibar][ifoo][_blank] - self.assertEqual(len(reg), 1) - self.assertIs(reg[0], _Factory) - self.assertEqual(comp._subscription_registrations, - [((ibar,), ifoo, _blank, _Factory, _info)]) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Registered) - self.assertIsInstance(event.object, SubscriptionRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertEqual(event.object.required, (ibar,)) - self.assertEqual(event.object.name, _blank) - self.assertIs(event.object.info, _info) - self.assertIs(event.object.factory, _Factory) - - def test_registerSubscriptionAdapter_wo_event(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - _info = 'info' - - def _factory(context): - raise NotImplementedError() - - comp = self._makeOne() - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerSubscriptionAdapter(_factory, (ibar,), ifoo, - info=_info, event=False) - self.assertEqual(len(_events), 0) - - def test_registeredSubscriptionAdapters_empty(self): - comp = self._makeOne() - self.assertEqual(list(comp.registeredSubscriptionAdapters()), []) - - def test_registeredSubscriptionAdapters_notempty(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.registry import SubscriptionRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IFoo') - _info = 'info' - _blank = '' - - class _Factory: - pass - - comp = self._makeOne() - comp.registerSubscriptionAdapter(_Factory, (ibar,), ifoo, info=_info) - comp.registerSubscriptionAdapter(_Factory, (ibar,), ifoo, info=_info) - reg = list(comp.registeredSubscriptionAdapters()) - self.assertEqual(len(reg), 2) - self.assertIsInstance(reg[0], SubscriptionRegistration) - self.assertIs(reg[0].registry, comp) - self.assertIs(reg[0].provided, ifoo) - self.assertEqual(reg[0].required, (ibar,)) - self.assertEqual(reg[0].name, _blank) - self.assertIs(reg[0].info, _info) - self.assertIs(reg[0].factory, _Factory) - self.assertIsInstance(reg[1], SubscriptionRegistration) - self.assertIs(reg[1].registry, comp) - self.assertIs(reg[1].provided, ifoo) - self.assertEqual(reg[1].required, (ibar,)) - self.assertEqual(reg[1].name, _blank) - self.assertIs(reg[1].info, _info) - self.assertIs(reg[1].factory, _Factory) - - def test_unregisterSubscriptionAdapter_w_nonblank_name(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - _nonblank = 'nonblank' - comp = self._makeOne() - self.assertRaises(TypeError, comp.unregisterSubscriptionAdapter, - required=ifoo, provided=ibar, name=_nonblank) - - def test_unregisterSubscriptionAdapter_neither_factory_nor_provided(self): - comp = self._makeOne() - self.assertRaises(TypeError, comp.unregisterSubscriptionAdapter, - factory=None, provided=None) - - def test_unregisterSubscriptionAdapter_neither_factory_nor_required(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - comp = self._makeOne() - self.assertRaises(TypeError, comp.unregisterSubscriptionAdapter, - factory=None, provided=ifoo, required=None) - - def test_unregisterSubscriptionAdapter_miss(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - - class _Factory: - pass - - comp = self._makeOne() - _monkey, _events = self._wrapEvents() - with _monkey: - unreg = comp.unregisterSubscriptionAdapter(_Factory, (ibar,), ifoo) - self.assertFalse(unreg) - self.assertFalse(_events) - - def test_unregisterSubscriptionAdapter_hit_wo_factory(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.interfaces import Unregistered - from zope.interface.registry import SubscriptionRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - - class _Factory: - pass - - comp = self._makeOne() - comp.registerSubscriptionAdapter(_Factory, (ibar,), ifoo) - _monkey, _events = self._wrapEvents() - with _monkey: - unreg = comp.unregisterSubscriptionAdapter(None, (ibar,), ifoo) - self.assertTrue(unreg) - self.assertFalse(comp.adapters._subscribers) - self.assertFalse(comp._subscription_registrations) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Unregistered) - self.assertIsInstance(event.object, SubscriptionRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertEqual(event.object.required, (ibar,)) - self.assertEqual(event.object.name, '') - self.assertEqual(event.object.info, '') - self.assertIsNone(event.object.factory) - - def test_unregisterSubscriptionAdapter_hit_w_factory(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.interfaces import Unregistered - from zope.interface.registry import SubscriptionRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - - class _Factory: - pass - - comp = self._makeOne() - comp.registerSubscriptionAdapter(_Factory, (ibar,), ifoo) - _monkey, _events = self._wrapEvents() - with _monkey: - unreg = comp.unregisterSubscriptionAdapter(_Factory, (ibar,), ifoo) - self.assertTrue(unreg) - self.assertFalse(comp.adapters._subscribers) - self.assertFalse(comp._subscription_registrations) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Unregistered) - self.assertIsInstance(event.object, SubscriptionRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertEqual(event.object.required, (ibar,)) - self.assertEqual(event.object.name, '') - self.assertEqual(event.object.info, '') - self.assertIs(event.object.factory, _Factory) - - def test_unregisterSubscriptionAdapter_wo_explicit_provided(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import implementer - from zope.interface.interfaces import Unregistered - from zope.interface.registry import SubscriptionRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - - @implementer(ifoo) - class _Factory: - pass - - comp = self._makeOne() - comp.registerSubscriptionAdapter(_Factory, (ibar,), ifoo) - _monkey, _events = self._wrapEvents() - with _monkey: - unreg = comp.unregisterSubscriptionAdapter(_Factory, (ibar,)) - self.assertTrue(unreg) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Unregistered) - self.assertIsInstance(event.object, SubscriptionRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertEqual(event.object.required, (ibar,)) - self.assertEqual(event.object.name, '') - self.assertEqual(event.object.info, '') - self.assertIs(event.object.factory, _Factory) - - def test_unregisterSubscriptionAdapter_wo_explicit_required(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.interfaces import Unregistered - from zope.interface.registry import SubscriptionRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - - class _Factory: - __component_adapts__ = (ibar,) - - comp = self._makeOne() - comp.registerSubscriptionAdapter(_Factory, (ibar,), ifoo) - _monkey, _events = self._wrapEvents() - with _monkey: - unreg = comp.unregisterSubscriptionAdapter(_Factory, provided=ifoo) - self.assertTrue(unreg) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Unregistered) - self.assertIsInstance(event.object, SubscriptionRegistration) - self.assertIs(event.object.registry, comp) - self.assertIs(event.object.provided, ifoo) - self.assertEqual(event.object.required, (ibar,)) - self.assertEqual(event.object.name, '') - self.assertEqual(event.object.info, '') - self.assertIs(event.object.factory, _Factory) - - def test_subscribers_empty(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import implementer - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - comp = self._makeOne() - - @implementer(ibar) - class Bar: - pass - - bar = Bar() - self.assertEqual(list(comp.subscribers((bar,), ifoo)), []) - - def test_subscribers_non_empty(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import implementer - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - - class _Factory: - __component_adapts__ = (ibar,) - - def __init__(self, context): - self._context = context - - class _Derived(_Factory): - pass - - comp = self._makeOne() - comp.registerSubscriptionAdapter(_Factory, (ibar,), ifoo) - comp.registerSubscriptionAdapter(_Derived, (ibar,), ifoo) - - @implementer(ibar) - class Bar: - pass - - bar = Bar() - subscribers = comp.subscribers((bar,), ifoo) - - def _klassname(x): - return x.__class__.__name__ - - subscribers = sorted(subscribers, key=_klassname) - self.assertEqual(len(subscribers), 2) - self.assertIsInstance(subscribers[0], _Derived) - self.assertIsInstance(subscribers[1], _Factory) - - def test_registerHandler_w_nonblank_name(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _nonblank = 'nonblank' - comp = self._makeOne() - - def _factory(context): - raise NotImplementedError() - - self.assertRaises(TypeError, comp.registerHandler, _factory, - required=ifoo, name=_nonblank) - - def test_registerHandler_w_explicit_required(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.interfaces import Registered - from zope.interface.registry import HandlerRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _blank = '' - _info = 'info' - - def _factory(context): - raise NotImplementedError() - - comp = self._makeOne() - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerHandler(_factory, (ifoo,), info=_info) - reg = comp.adapters._subscribers[1][ifoo][None][_blank] - self.assertEqual(len(reg), 1) - self.assertIs(reg[0], _factory) - self.assertEqual(comp._handler_registrations, - [((ifoo,), _blank, _factory, _info)]) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Registered) - self.assertIsInstance(event.object, HandlerRegistration) - self.assertIs(event.object.registry, comp) - self.assertEqual(event.object.required, (ifoo,)) - self.assertEqual(event.object.name, _blank) - self.assertIs(event.object.info, _info) - self.assertIs(event.object.factory, _factory) - - def test_registerHandler_wo_explicit_required_no_event(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _info = 'info' - _blank = '' - - class _Factory: - __component_adapts__ = (ifoo,) - pass - - comp = self._makeOne() - _monkey, _events = self._wrapEvents() - with _monkey: - comp.registerHandler(_Factory, info=_info, event=False) - reg = comp.adapters._subscribers[1][ifoo][None][_blank] - self.assertEqual(len(reg), 1) - self.assertIs(reg[0], _Factory) - self.assertEqual(comp._handler_registrations, - [((ifoo,), _blank, _Factory, _info)]) - self.assertEqual(len(_events), 0) - - def test_registeredHandlers_empty(self): - comp = self._makeOne() - self.assertFalse(list(comp.registeredHandlers())) - - def test_registeredHandlers_non_empty(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.registry import HandlerRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - - def _factory1(context): - raise NotImplementedError() - - def _factory2(context): - raise NotImplementedError() - - comp = self._makeOne() - comp.registerHandler(_factory1, (ifoo,)) - comp.registerHandler(_factory2, (ifoo,)) - - def _factory_name(x): - return x.factory.__code__.co_name - - subscribers = sorted(comp.registeredHandlers(), key=_factory_name) - self.assertEqual(len(subscribers), 2) - self.assertIsInstance(subscribers[0], HandlerRegistration) - self.assertEqual(subscribers[0].required, (ifoo,)) - self.assertEqual(subscribers[0].name, '') - self.assertEqual(subscribers[0].factory, _factory1) - self.assertEqual(subscribers[0].info, '') - self.assertIsInstance(subscribers[1], HandlerRegistration) - self.assertEqual(subscribers[1].required, (ifoo,)) - self.assertEqual(subscribers[1].name, '') - self.assertEqual(subscribers[1].factory, _factory2) - self.assertEqual(subscribers[1].info, '') - - def test_unregisterHandler_w_nonblank_name(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _nonblank = 'nonblank' - comp = self._makeOne() - self.assertRaises(TypeError, comp.unregisterHandler, - required=(ifoo,), name=_nonblank) - - def test_unregisterHandler_neither_factory_nor_required(self): - comp = self._makeOne() - self.assertRaises(TypeError, comp.unregisterHandler) - - def test_unregisterHandler_miss(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - comp = self._makeOne() - unreg = comp.unregisterHandler(required=(ifoo,)) - self.assertFalse(unreg) - - def test_unregisterHandler_hit_w_factory_and_explicit_provided(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.interfaces import Unregistered - from zope.interface.registry import HandlerRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - comp = self._makeOne() - - def _factory(context): - raise NotImplementedError() - - comp = self._makeOne() - comp.registerHandler(_factory, (ifoo,)) - _monkey, _events = self._wrapEvents() - with _monkey: - unreg = comp.unregisterHandler(_factory, (ifoo,)) - self.assertTrue(unreg) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Unregistered) - self.assertIsInstance(event.object, HandlerRegistration) - self.assertIs(event.object.registry, comp) - self.assertEqual(event.object.required, (ifoo,)) - self.assertEqual(event.object.name, '') - self.assertIs(event.object.factory, _factory) - - def test_unregisterHandler_hit_w_only_explicit_provided(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.interfaces import Unregistered - from zope.interface.registry import HandlerRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - comp = self._makeOne() - - def _factory(context): - raise NotImplementedError() - - comp = self._makeOne() - comp.registerHandler(_factory, (ifoo,)) - _monkey, _events = self._wrapEvents() - with _monkey: - unreg = comp.unregisterHandler(required=(ifoo,)) - self.assertTrue(unreg) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Unregistered) - self.assertIsInstance(event.object, HandlerRegistration) - self.assertIs(event.object.registry, comp) - self.assertEqual(event.object.required, (ifoo,)) - self.assertEqual(event.object.name, '') - self.assertIsNone(event.object.factory) - - def test_unregisterHandler_wo_explicit_required(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.interfaces import Unregistered - from zope.interface.registry import HandlerRegistration - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - - class _Factory: - __component_adapts__ = (ifoo,) - - comp = self._makeOne() - comp.registerHandler(_Factory) - _monkey, _events = self._wrapEvents() - with _monkey: - unreg = comp.unregisterHandler(_Factory) - self.assertTrue(unreg) - self.assertEqual(len(_events), 1) - args, kw = _events[0] - event, = args - self.assertEqual(kw, {}) - self.assertIsInstance(event, Unregistered) - self.assertIsInstance(event.object, HandlerRegistration) - self.assertIs(event.object.registry, comp) - self.assertEqual(event.object.required, (ifoo,)) - self.assertEqual(event.object.name, '') - self.assertEqual(event.object.info, '') - self.assertIs(event.object.factory, _Factory) - - def test_handle_empty(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import implementer - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - comp = self._makeOne() - - @implementer(ifoo) - class Bar: - pass - - bar = Bar() - comp.handle((bar,)) # doesn't raise - - def test_handle_non_empty(self): - from zope.interface.declarations import InterfaceClass - from zope.interface.declarations import implementer - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - _called_1 = [] - - def _factory_1(context): - _called_1.append(context) - - _called_2 = [] - - def _factory_2(context): - _called_2.append(context) - - comp = self._makeOne() - comp.registerHandler(_factory_1, (ifoo,)) - comp.registerHandler(_factory_2, (ifoo,)) - - @implementer(ifoo) - class Bar: - pass - - bar = Bar() - comp.handle(bar) - self.assertEqual(_called_1, [bar]) - self.assertEqual(_called_2, [bar]) - - def test_register_unregister_identical_objects_provided( - self, identical=True, - ): - # https://github.com/zopefoundation/zope.interface/issues/227 - class IFoo(Interface): - pass - - comp = self._makeOne() - first = object() - second = first if identical else object() - - comp.registerUtility(first, provided=IFoo) - comp.registerUtility(second, provided=IFoo, name='bar') - - self.assertEqual(len(comp.utilities._subscribers), 1) - self.assertEqual(comp.utilities._subscribers, [{ - IFoo: {'': (first, ) if identical else (first, second)} - }]) - self.assertEqual(comp.utilities._provided, { - IFoo: 3 if identical else 4 - }) - - res = comp.unregisterUtility(first, provided=IFoo) - self.assertTrue(res) - res = comp.unregisterUtility(second, provided=IFoo, name='bar') - self.assertTrue(res) - - self.assertEqual(comp.utilities._provided, {}) - self.assertEqual(len(comp.utilities._subscribers), 0) - - def test_register_unregister_nonequal_objects_provided(self): - self.test_register_unregister_identical_objects_provided( - identical=False, - ) - - def test_rebuildUtilityRegistryFromLocalCache(self): - - class IFoo(Interface): - "Does nothing" - - class UtilityImplementingFoo: - "Does nothing" - - comps = self._makeOne() - - for i in range(30): - comps.registerUtility( - UtilityImplementingFoo(), IFoo, name=f'{i}' - ) - - orig_generation = comps.utilities._generation - - orig_adapters = comps.utilities._adapters - self.assertEqual(len(orig_adapters), 1) - self.assertEqual(len(orig_adapters[0]), 1) - self.assertEqual(len(orig_adapters[0][IFoo]), 30) - - orig_subscribers = comps.utilities._subscribers - self.assertEqual(len(orig_subscribers), 1) - self.assertEqual(len(orig_subscribers[0]), 1) - self.assertEqual(len(orig_subscribers[0][IFoo]), 1) - self.assertEqual(len(orig_subscribers[0][IFoo]['']), 30) - - # Blow a bunch of them away, creating artificial corruption - new_adapters = comps.utilities._adapters = type(orig_adapters)() - new_adapters.append({}) - d = new_adapters[0][IFoo] = {} - for name in range(10): - name = str(str(name)) - d[name] = orig_adapters[0][IFoo][name] - - self.assertNotEqual(orig_adapters, new_adapters) - - new_subs = comps.utilities._subscribers = type(orig_subscribers)() - new_subs.append({}) - d = new_subs[0][IFoo] = {} - d[''] = () - - for name in range(5, 12): # 12 - 5 = 7 - name = str(str(name)) - comp = orig_adapters[0][IFoo][name] - d[''] += (comp,) - - # We can preflight (by default) and nothing changes - rebuild_preflight = comps.rebuildUtilityRegistryFromLocalCache() - - self.assertEqual(comps.utilities._generation, orig_generation) - self.assertEqual(rebuild_preflight, { - 'did_not_register': 10, - 'needed_registered': 20, - - 'did_not_subscribe': 7, - 'needed_subscribed': 23, - }) - - # Now for real - rebuild_results = comps.rebuildUtilityRegistryFromLocalCache( - rebuild=True, - ) - - # The generation only got incremented once - self.assertEqual(comps.utilities._generation, orig_generation + 1) - # The result was the same - self.assertEqual(rebuild_preflight, rebuild_results) - self.assertEqual(new_adapters, orig_adapters) - self.assertEqual( - len(new_subs[0][IFoo]['']), - len(orig_subscribers[0][IFoo][''])) - - for orig_subscriber in orig_subscribers[0][IFoo]['']: - self.assertIn(orig_subscriber, new_subs[0][IFoo]['']) - - # Preflighting, rebuilding again produce no changes. - preflight_after = comps.rebuildUtilityRegistryFromLocalCache() - self.assertEqual(preflight_after, { - 'did_not_register': 30, - 'needed_registered': 0, - - 'did_not_subscribe': 30, - 'needed_subscribed': 0, - }) - - rebuild_after = comps.rebuildUtilityRegistryFromLocalCache( - rebuild=True, - ) - self.assertEqual(rebuild_after, preflight_after) - self.assertEqual(comps.utilities._generation, orig_generation + 1) - - -class UnhashableComponentsTests(ComponentsTests): - - def _getTargetClass(self): - # Mimic what pyramid does to create an unhashable - # registry - - class Components( - super(UnhashableComponentsTests, self)._getTargetClass(), dict, - ): - pass - - return Components - -# Test _getUtilityProvided, _getAdapterProvided, _getAdapterRequired via their -# callers (Component.registerUtility, Component.registerAdapter). - - -class UtilityRegistrationTests(unittest.TestCase): - - def _getTargetClass(self): - from zope.interface.registry import UtilityRegistration - return UtilityRegistration - - def _makeOne(self, component=None, factory=None): - from zope.interface.declarations import InterfaceClass - - class InterfaceClassSubclass(InterfaceClass): - pass - - ifoo = InterfaceClassSubclass('IFoo') - - class _Registry: - def __repr__(self): - return '_REGISTRY' - - registry = _Registry() - name = 'name' - doc = 'DOCSTRING' - klass = self._getTargetClass() - return ( - klass(registry, ifoo, name, component, doc, factory), - registry, - name, - ) - - def test_class_conforms_to_IUtilityRegistration(self): - from zope.interface.interfaces import IUtilityRegistration - from zope.interface.verify import verifyClass - verifyClass(IUtilityRegistration, self._getTargetClass()) - - def test_instance_conforms_to_IUtilityRegistration(self): - from zope.interface.interfaces import IUtilityRegistration - from zope.interface.verify import verifyObject - ur, _, _ = self._makeOne() - verifyObject(IUtilityRegistration, ur) - - def test___repr__(self): - - class _Component: - __name__ = 'TEST' - - _component = _Component() - ur, _registry, _name = self._makeOne(_component) - self.assertEqual( - repr(ur), ( - "UtilityRegistration(" - "_REGISTRY, IFoo, %r, TEST, None, 'DOCSTRING')" - ) % (_name) - ) - - def test___repr___provided_wo_name(self): - - class _Component: - def __repr__(self): - return 'TEST' - - _component = _Component() - ur, _registry, _name = self._makeOne(_component) - ur.provided = object() - self.assertEqual( - repr(ur), ( - "UtilityRegistration(" - "_REGISTRY, None, %r, TEST, None, 'DOCSTRING')" - ) % (_name) - ) - - def test___repr___component_wo_name(self): - - class _Component: - def __repr__(self): - return 'TEST' - - _component = _Component() - ur, _registry, _name = self._makeOne(_component) - ur.provided = object() - self.assertEqual( - repr(ur), ( - "UtilityRegistration(" - "_REGISTRY, None, %r, TEST, None, 'DOCSTRING')" - ) % (_name) - ) - - def test___hash__(self): - _component = object() - ur, _registry, _name = self._makeOne(_component) - self.assertEqual(ur.__hash__(), id(ur)) - - def test___eq___identity(self): - _component = object() - ur, _registry, _name = self._makeOne(_component) - self.assertEqual(ur, ur) - - def test___eq___hit(self): - _component = object() - ur, _registry, _name = self._makeOne(_component) - ur2, _, _ = self._makeOne(_component) - self.assertEqual(ur, ur2) - - def test___eq___miss(self): - _component = object() - _component2 = object() - ur, _registry, _name = self._makeOne(_component) - ur2, _, _ = self._makeOne(_component2) - self.assertNotEqual(ur, ur2) - - def test___ne___identity(self): - _component = object() - ur, _registry, _name = self._makeOne(_component) - self.assertEqual(ur, ur) - - def test___ne___hit(self): - _component = object() - ur, _registry, _name = self._makeOne(_component) - ur2, _, _ = self._makeOne(_component) - self.assertEqual(ur, ur2) - - def test___ne___miss(self): - _component = object() - _component2 = object() - ur, _registry, _name = self._makeOne(_component) - ur2, _, _ = self._makeOne(_component2) - self.assertNotEqual(ur, ur2) - - def test___lt___identity(self): - _component = object() - ur, _registry, _name = self._makeOne(_component) - self.assertFalse(ur < ur) - - def test___lt___hit(self): - _component = object() - ur, _registry, _name = self._makeOne(_component) - ur2, _, _ = self._makeOne(_component) - self.assertFalse(ur < ur2) - - def test___lt___miss(self): - _component = object() - _component2 = object() - ur, _registry, _name = self._makeOne(_component) - ur2, _, _ = self._makeOne(_component2) - ur2.name = _name + '2' - self.assertLess(ur, ur2) - - def test___le___identity(self): - _component = object() - ur, _registry, _name = self._makeOne(_component) - self.assertLessEqual(ur, ur) - - def test___le___hit(self): - _component = object() - ur, _registry, _name = self._makeOne(_component) - ur2, _, _ = self._makeOne(_component) - self.assertLessEqual(ur, ur2) - - def test___le___miss(self): - _component = object() - _component2 = object() - ur, _registry, _name = self._makeOne(_component) - ur2, _, _ = self._makeOne(_component2) - ur2.name = _name + '2' - self.assertLessEqual(ur, ur2) - - def test___gt___identity(self): - _component = object() - ur, _registry, _name = self._makeOne(_component) - self.assertFalse(ur > ur) - - def test___gt___hit(self): - _component = object() - _component2 = object() - ur, _registry, _name = self._makeOne(_component) - ur2, _, _ = self._makeOne(_component2) - ur2.name = _name + '2' - self.assertGreater(ur2, ur) - - def test___gt___miss(self): - _component = object() - ur, _registry, _name = self._makeOne(_component) - ur2, _, _ = self._makeOne(_component) - self.assertFalse(ur2 > ur) - - def test___ge___identity(self): - _component = object() - ur, _registry, _name = self._makeOne(_component) - self.assertGreaterEqual(ur, ur) - - def test___ge___miss(self): - _component = object() - _component2 = object() - ur, _registry, _name = self._makeOne(_component) - ur2, _, _ = self._makeOne(_component2) - ur2.name = _name + '2' - self.assertFalse(ur >= ur2) - - def test___ge___hit(self): - _component = object() - ur, _registry, _name = self._makeOne(_component) - ur2, _, _ = self._makeOne(_component) - ur2.name = _name + '2' - self.assertGreaterEqual(ur2, ur) - - -class AdapterRegistrationTests(unittest.TestCase): - - def _getTargetClass(self): - from zope.interface.registry import AdapterRegistration - return AdapterRegistration - - def _makeOne(self, component=None): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - - class _Registry: - def __repr__(self): - return '_REGISTRY' - - registry = _Registry() - name = 'name' - doc = 'DOCSTRING' - klass = self._getTargetClass() - return ( - klass(registry, (ibar,), ifoo, name, component, doc), - registry, - name, - ) - - def test_class_conforms_to_IAdapterRegistration(self): - from zope.interface.interfaces import IAdapterRegistration - from zope.interface.verify import verifyClass - verifyClass(IAdapterRegistration, self._getTargetClass()) - - def test_instance_conforms_to_IAdapterRegistration(self): - from zope.interface.interfaces import IAdapterRegistration - from zope.interface.verify import verifyObject - ar, _, _ = self._makeOne() - verifyObject(IAdapterRegistration, ar) - - def test___repr__(self): - - class _Component: - __name__ = 'TEST' - - _component = _Component() - ar, _registry, _name = self._makeOne(_component) - self.assertEqual( - repr(ar), ( - "AdapterRegistration(_REGISTRY, [IBar], IFoo, %r, TEST, " - "'DOCSTRING')" - ) % (_name) - ) - - def test___repr___provided_wo_name(self): - - class _Component: - def __repr__(self): - return 'TEST' - - _component = _Component() - ar, _registry, _name = self._makeOne(_component) - ar.provided = object() - self.assertEqual( - repr(ar), ( - "AdapterRegistration(_REGISTRY, [IBar], None, %r, TEST, " - "'DOCSTRING')" - ) % (_name) - ) - - def test___repr___component_wo_name(self): - - class _Component: - def __repr__(self): - return 'TEST' - - _component = _Component() - ar, _registry, _name = self._makeOne(_component) - ar.provided = object() - self.assertEqual( - repr(ar), ( - "AdapterRegistration(_REGISTRY, [IBar], None, %r, TEST, " - "'DOCSTRING')" - ) % (_name) - ) - - def test___hash__(self): - _component = object() - ar, _registry, _name = self._makeOne(_component) - self.assertEqual(ar.__hash__(), id(ar)) - - def test___eq___identity(self): - _component = object() - ar, _registry, _name = self._makeOne(_component) - self.assertEqual(ar, ar) - - def test___eq___hit(self): - _component = object() - ar, _registry, _name = self._makeOne(_component) - ar2, _, _ = self._makeOne(_component) - self.assertEqual(ar, ar2) - - def test___eq___miss(self): - _component = object() - _component2 = object() - ar, _registry, _name = self._makeOne(_component) - ar2, _, _ = self._makeOne(_component2) - self.assertNotEqual(ar, ar2) - - def test___ne___identity(self): - _component = object() - ar, _registry, _name = self._makeOne(_component) - self.assertEqual(ar, ar) - - def test___ne___miss(self): - _component = object() - ar, _registry, _name = self._makeOne(_component) - ar2, _, _ = self._makeOne(_component) - self.assertEqual(ar, ar2) - - def test___ne___hit_component(self): - _component = object() - _component2 = object() - ar, _registry, _name = self._makeOne(_component) - ar2, _, _ = self._makeOne(_component2) - self.assertNotEqual(ar, ar2) - - def test___ne___hit_provided(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ibaz = IFoo('IBaz') - _component = object() - ar, _registry, _name = self._makeOne(_component) - ar2, _, _ = self._makeOne(_component) - ar2.provided = ibaz - self.assertNotEqual(ar, ar2) - - def test___ne___hit_required(self): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ibaz = IFoo('IBaz') - _component = object() - _component2 = object() - ar, _registry, _name = self._makeOne(_component) - ar2, _, _ = self._makeOne(_component2) - ar2.required = (ibaz,) - self.assertNotEqual(ar, ar2) - - def test___lt___identity(self): - _component = object() - ar, _registry, _name = self._makeOne(_component) - self.assertFalse(ar < ar) - - def test___lt___hit(self): - _component = object() - ar, _registry, _name = self._makeOne(_component) - ar2, _, _ = self._makeOne(_component) - self.assertFalse(ar < ar2) - - def test___lt___miss(self): - _component = object() - _component2 = object() - ar, _registry, _name = self._makeOne(_component) - ar2, _, _ = self._makeOne(_component2) - ar2.name = _name + '2' - self.assertLess(ar, ar2) - - def test___le___identity(self): - _component = object() - ar, _registry, _name = self._makeOne(_component) - self.assertLessEqual(ar, ar) - - def test___le___hit(self): - _component = object() - ar, _registry, _name = self._makeOne(_component) - ar2, _, _ = self._makeOne(_component) - self.assertLessEqual(ar, ar2) - - def test___le___miss(self): - _component = object() - _component2 = object() - ar, _registry, _name = self._makeOne(_component) - ar2, _, _ = self._makeOne(_component2) - ar2.name = _name + '2' - self.assertLessEqual(ar, ar2) - - def test___gt___identity(self): - _component = object() - ar, _registry, _name = self._makeOne(_component) - self.assertFalse(ar > ar) - - def test___gt___hit(self): - _component = object() - _component2 = object() - ar, _registry, _name = self._makeOne(_component) - ar2, _, _ = self._makeOne(_component2) - ar2.name = _name + '2' - self.assertGreater(ar2, ar) - - def test___gt___miss(self): - _component = object() - ar, _registry, _name = self._makeOne(_component) - ar2, _, _ = self._makeOne(_component) - self.assertFalse(ar2 > ar) - - def test___ge___identity(self): - _component = object() - ar, _registry, _name = self._makeOne(_component) - self.assertGreaterEqual(ar, ar) - - def test___ge___miss(self): - _component = object() - _component2 = object() - ar, _registry, _name = self._makeOne(_component) - ar2, _, _ = self._makeOne(_component2) - ar2.name = _name + '2' - self.assertFalse(ar >= ar2) - - def test___ge___hit(self): - _component = object() - ar, _registry, _name = self._makeOne(_component) - ar2, _, _ = self._makeOne(_component) - ar2.name = _name + '2' - self.assertGreaterEqual(ar2, ar) - - -class SubscriptionRegistrationTests(unittest.TestCase): - - def _getTargetClass(self): - from zope.interface.registry import SubscriptionRegistration - return SubscriptionRegistration - - def _makeOne(self, component=None): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - ibar = IFoo('IBar') - - class _Registry: - def __repr__(self): # pragma: no cover - return '_REGISTRY' - - registry = _Registry() - name = 'name' - doc = 'DOCSTRING' - klass = self._getTargetClass() - return ( - klass(registry, (ibar,), ifoo, name, component, doc), - registry, - name, - ) - - def test_class_conforms_to_ISubscriptionAdapterRegistration(self): - from zope.interface.interfaces import ISubscriptionAdapterRegistration - from zope.interface.verify import verifyClass - verifyClass(ISubscriptionAdapterRegistration, self._getTargetClass()) - - def test_instance_conforms_to_ISubscriptionAdapterRegistration(self): - from zope.interface.interfaces import ISubscriptionAdapterRegistration - from zope.interface.verify import verifyObject - sar, _, _ = self._makeOne() - verifyObject(ISubscriptionAdapterRegistration, sar) - - -class HandlerRegistrationTests(unittest.TestCase): - - def _getTargetClass(self): - from zope.interface.registry import HandlerRegistration - return HandlerRegistration - - def _makeOne(self, component=None): - from zope.interface.declarations import InterfaceClass - - class IFoo(InterfaceClass): - pass - - ifoo = IFoo('IFoo') - - class _Registry: - def __repr__(self): - return '_REGISTRY' - - registry = _Registry() - name = 'name' - doc = 'DOCSTRING' - klass = self._getTargetClass() - return ( - klass(registry, (ifoo,), name, component, doc), - registry, - name, - ) - - def test_class_conforms_to_IHandlerRegistration(self): - from zope.interface.interfaces import IHandlerRegistration - from zope.interface.verify import verifyClass - verifyClass(IHandlerRegistration, self._getTargetClass()) - - def test_instance_conforms_to_IHandlerRegistration(self): - from zope.interface.interfaces import IHandlerRegistration - from zope.interface.verify import verifyObject - hr, _, _ = self._makeOne() - verifyObject(IHandlerRegistration, hr) - - def test_properties(self): - def _factory(context): - raise NotImplementedError() - hr, _, _ = self._makeOne(_factory) - self.assertIs(hr.handler, _factory) - self.assertIs(hr.factory, hr.handler) - self.assertIsNone(hr.provided) - - def test___repr___factory_w_name(self): - - class _Factory: - __name__ = 'TEST' - - hr, _registry, _name = self._makeOne(_Factory()) - self.assertEqual( - repr(hr), ( - "HandlerRegistration(_REGISTRY, [IFoo], %r, TEST, " - "'DOCSTRING')" - ) % (_name)) - - def test___repr___factory_wo_name(self): - - class _Factory: - def __repr__(self): - return 'TEST' - - hr, _registry, _name = self._makeOne(_Factory()) - self.assertEqual( - repr(hr), ( - "HandlerRegistration(_REGISTRY, [IFoo], %r, TEST, " - "'DOCSTRING')" - ) % (_name) - ) - - -class PersistentAdapterRegistry(VerifyingAdapterRegistry): - - def __getstate__(self): - state = self.__dict__.copy() - for k in list(state): - if k in self._delegated or k.startswith('_v'): - state.pop(k) - state.pop('ro', None) - return state - - def __setstate__(self, state): - bases = state.pop('__bases__', ()) - self.__dict__.update(state) - self._createLookup() - self.__bases__ = bases - self._v_lookup.changed(self) - - -class PersistentComponents(Components): - # Mimic zope.component.persistentregistry.PersistentComponents: - # we should be picklalable, but not persistent.Persistent ourself. - - def _init_registries(self): - self.adapters = PersistentAdapterRegistry() - self.utilities = PersistentAdapterRegistry() - - -class PersistentDictComponents(PersistentComponents, dict): - # Like Pyramid's Registry, we subclass Components and dict - pass - - -class PersistentComponentsDict(dict, PersistentComponents): - # Like the above, but inheritance is flipped - def __init__(self, name): - dict.__init__(self) - PersistentComponents.__init__(self, name) - - -class TestPersistentComponents(unittest.TestCase): - - def _makeOne(self): - return PersistentComponents('test') - - def _check_equality_after_pickle(self, made): - pass - - def test_pickles_empty(self): - import pickle - comp = self._makeOne() - pickle.dumps(comp) - comp2 = pickle.loads(pickle.dumps(comp)) - - self.assertEqual(comp2.__name__, 'test') - - def test_pickles_with_utility_registration(self): - import pickle - comp = self._makeOne() - utility = object() - comp.registerUtility( - utility, - Interface) - - self.assertIs(utility, - comp.getUtility(Interface)) - - comp2 = pickle.loads(pickle.dumps(comp)) - self.assertEqual(comp2.__name__, 'test') - - # The utility is still registered - self.assertIsNotNone(comp2.getUtility(Interface)) - - # We can register another one - comp2.registerUtility( - utility, - Interface) - self.assertIs(utility, - comp2.getUtility(Interface)) - - self._check_equality_after_pickle(comp2) - - -class TestPersistentDictComponents(TestPersistentComponents): - - def _getTargetClass(self): - return PersistentDictComponents - - def _makeOne(self): - comp = self._getTargetClass()(name='test') - comp['key'] = 42 - return comp - - def _check_equality_after_pickle(self, made): - self.assertIn('key', made) - self.assertEqual(made['key'], 42) - - -class TestPersistentComponentsDict(TestPersistentDictComponents): - - def _getTargetClass(self): - return PersistentComponentsDict - - -class _Monkey: - # context-manager for replacing module names in the scope of a test. - def __init__(self, module, **kw): - self.module = module - self.to_restore = {key: getattr(module, key) for key in kw} - for key, value in kw.items(): - setattr(module, key, value) - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - for key, value in self.to_restore.items(): - setattr(self.module, key, value) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_ro.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_ro.py deleted file mode 100644 index 26376c6..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_ro.py +++ /dev/null @@ -1,487 +0,0 @@ -############################################################################## -# -# Copyright (c) 2014 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Resolution ordering utility tests""" -import unittest - - -# pylint:disable=blacklisted-name -# pylint:disable=protected-access -# pylint:disable=attribute-defined-outside-init - -class Test__mergeOrderings(unittest.TestCase): - - def _callFUT(self, orderings): - from zope.interface.ro import _legacy_mergeOrderings - return _legacy_mergeOrderings(orderings) - - def test_empty(self): - self.assertEqual(self._callFUT([]), []) - - def test_single(self): - self.assertEqual(self._callFUT(['a', 'b', 'c']), ['a', 'b', 'c']) - - def test_w_duplicates(self): - self.assertEqual(self._callFUT([['a'], ['b', 'a']]), ['b', 'a']) - - def test_suffix_across_multiple_duplicates(self): - O1 = ['x', 'y', 'z'] - O2 = ['q', 'z'] - O3 = [1, 3, 5] - O4 = ['z'] - self.assertEqual(self._callFUT([O1, O2, O3, O4]), - ['x', 'y', 'q', 1, 3, 5, 'z']) - - -class Test__flatten(unittest.TestCase): - - def _callFUT(self, ob): - from zope.interface.ro import _legacy_flatten - return _legacy_flatten(ob) - - def test_w_empty_bases(self): - - class Foo: - pass - - foo = Foo() - foo.__bases__ = () - self.assertEqual(self._callFUT(foo), [foo]) - - def test_w_single_base(self): - - class Foo: - pass - - self.assertEqual(self._callFUT(Foo), [Foo, object]) - - def test_w_bases(self): - - class Foo: - pass - - class Bar(Foo): - pass - - self.assertEqual(self._callFUT(Bar), [Bar, Foo, object]) - - def test_w_diamond(self): - - class Foo: - pass - - class Bar(Foo): - pass - - class Baz(Foo): - pass - - class Qux(Bar, Baz): - pass - - self.assertEqual(self._callFUT(Qux), - [Qux, Bar, Foo, object, Baz, Foo, object]) - - -class Test_ro(unittest.TestCase): - maxDiff = None - - def _callFUT(self, ob, **kwargs): - from zope.interface.ro import _legacy_ro - return _legacy_ro(ob, **kwargs) - - def test_w_empty_bases(self): - - class Foo: - pass - - foo = Foo() - foo.__bases__ = () - self.assertEqual(self._callFUT(foo), [foo]) - - def test_w_single_base(self): - - class Foo: - pass - - self.assertEqual(self._callFUT(Foo), [Foo, object]) - - def test_w_bases(self): - - class Foo: - pass - - class Bar(Foo): - pass - - self.assertEqual(self._callFUT(Bar), [Bar, Foo, object]) - - def test_w_diamond(self): - - class Foo: - pass - - class Bar(Foo): - pass - - class Baz(Foo): - pass - - class Qux(Bar, Baz): - pass - - self.assertEqual(self._callFUT(Qux), - [Qux, Bar, Baz, Foo, object]) - - def _make_IOErr(self): - # This can't be done in the standard C3 ordering. - - class Foo: - def __init__(self, name, *bases): - self.__name__ = name - self.__bases__ = bases - - def __repr__(self): # pragma: no cover - return self.__name__ - - # Mimic what classImplements(IOError, IIOError) - # does. - IEx = Foo('IEx') - IStdErr = Foo('IStdErr', IEx) - IEnvErr = Foo('IEnvErr', IStdErr) - IIOErr = Foo('IIOErr', IEnvErr) - IOSErr = Foo('IOSErr', IEnvErr) - - IOErr = Foo('IOErr', IEnvErr, IIOErr, IOSErr) - return IOErr, [IOErr, IIOErr, IOSErr, IEnvErr, IStdErr, IEx] - - def test_non_orderable(self): - IOErr, bases = self._make_IOErr() - - self.assertEqual(self._callFUT(IOErr), bases) - - def test_mixed_inheritance_and_implementation(self): - # https://github.com/zopefoundation/zope.interface/issues/8 - # This test should fail, but doesn't, as described in that issue. - # pylint:disable=inherit-non-class - from zope.interface import Interface - from zope.interface import implementedBy - from zope.interface import implementer - from zope.interface import providedBy - - class IFoo(Interface): - pass - - @implementer(IFoo) - class ImplementsFoo: - pass - - class ExtendsFoo(ImplementsFoo): - pass - - class ImplementsNothing: - pass - - class ExtendsFooImplementsNothing(ExtendsFoo, ImplementsNothing): - pass - - self.assertEqual( - self._callFUT(providedBy(ExtendsFooImplementsNothing())), - [implementedBy(ExtendsFooImplementsNothing), - implementedBy(ExtendsFoo), - implementedBy(ImplementsFoo), - IFoo, - Interface, - implementedBy(ImplementsNothing), - implementedBy(object)]) - - -class C3Setting: - - def __init__(self, setting, value): - self._setting = setting - self._value = value - - def __enter__(self): - from zope.interface import ro - setattr(ro.C3, self._setting.__name__, self._value) - - def __exit__(self, t, v, tb): - from zope.interface import ro - setattr(ro.C3, self._setting.__name__, self._setting) - - -class Test_c3_ro(Test_ro): - - def setUp(self): - Test_ro.setUp(self) - from zope.testing.loggingsupport import InstalledHandler - self.log_handler = handler = InstalledHandler('zope.interface.ro') - self.addCleanup(handler.uninstall) - - def _callFUT(self, ob, **kwargs): - from zope.interface.ro import ro - return ro(ob, **kwargs) - - def _make_complex_diamond(self, base): - # https://github.com/zopefoundation/zope.interface/issues/21 - - class F(base): - pass - - class E(base): - pass - - class D(base): - pass - - class C(D, F): - pass - - class B(D, E): - pass - - class A(B, C): - pass - - if hasattr(A, 'mro'): - self.assertEqual(A.mro(), self._callFUT(A)) - - return A - - def test_complex_diamond_object(self): - self._make_complex_diamond(object) - - def test_complex_diamond_interface(self): - from zope.interface import Interface - - IA = self._make_complex_diamond(Interface) - - self.assertEqual( - [x.__name__ for x in IA.__iro__], - ['A', 'B', 'C', 'D', 'E', 'F', 'Interface'] - ) - - def test_complex_diamond_use_legacy_argument(self): - from zope.interface import Interface - - A = self._make_complex_diamond(Interface) - legacy_A_iro = self._callFUT(A, use_legacy_ro=True) - self.assertNotEqual(A.__iro__, legacy_A_iro) - - # And logging happened as a side-effect. - self._check_handler_complex_diamond() - - def test_complex_diamond_compare_legacy_argument(self): - from zope.interface import Interface - - A = self._make_complex_diamond(Interface) - computed_A_iro = self._callFUT(A, log_changed_ro=True) - # It matches, of course, but we did log a warning. - self.assertEqual(tuple(computed_A_iro), A.__iro__) - self._check_handler_complex_diamond() - - def _check_handler_complex_diamond(self): - handler = self.log_handler - self.assertEqual(1, len(handler.records)) - record = handler.records[0] - - expected = """\ -Object has different legacy and C3 MROs: - Legacy RO (len=7) C3 RO (len=7; inconsistent=no) - ================================================================== - zope.interface.tests.test_ro.A zope.interface.tests.test_ro.A - zope.interface.tests.test_ro.B zope.interface.tests.test_ro.B - - zope.interface.tests.test_ro.E - zope.interface.tests.test_ro.C zope.interface.tests.test_ro.C - zope.interface.tests.test_ro.D zope.interface.tests.test_ro.D - + zope.interface.tests.test_ro.E - zope.interface.tests.test_ro.F zope.interface.tests.test_ro.F - zope.interface.Interface zope.interface.Interface""".format( - name="zope.interface.tests.test_ro.A" - ) - - self.assertEqual( - '\n'.join(ln.rstrip() for ln in record.getMessage().splitlines()), - expected, - ) - - def test_ExtendedPathIndex_implement_thing_implementedby_super(self): - # See - # https://github.com/zopefoundation/zope.interface/pull/182#issuecomment-598754056 - from zope.interface import ro - - # pylint:disable=inherit-non-class - class _Based: - __bases__ = () - - def __init__(self, name, bases=(), attrs=None): - self.__name__ = name - self.__bases__ = bases - - def __repr__(self): - return self.__name__ - - Interface = _Based('Interface', (), {}) - - class IPluggableIndex(Interface): - pass - - class ILimitedResultIndex(IPluggableIndex): - pass - - class IQueryIndex(IPluggableIndex): - pass - - class IPathIndex(Interface): - pass - - # A parent class who implements two distinct interfaces whose - # only common ancestor is Interface. An easy case. - # @implementer(IPathIndex, IQueryIndex) - # class PathIndex(object): - # pass - obj = _Based('object') - PathIndex = _Based('PathIndex', (IPathIndex, IQueryIndex, obj)) - - # Child class that tries to put an interface the parent declares - # later ahead of the parent. - # @implementer(ILimitedResultIndex, IQueryIndex) - # class ExtendedPathIndex(PathIndex): - # pass - ExtendedPathIndex = _Based( - 'ExtendedPathIndex', - (ILimitedResultIndex, IQueryIndex, PathIndex) - ) - - # We were able to resolve it, and in exactly the same way as - # the legacy RO did, even though it is inconsistent. - result = self._callFUT( - ExtendedPathIndex, log_changed_ro=True, strict=False - ) - self.assertEqual(result, [ - ExtendedPathIndex, - ILimitedResultIndex, - PathIndex, - IPathIndex, - IQueryIndex, - IPluggableIndex, - Interface, - obj]) - - record, = self.log_handler.records - self.assertIn('used the legacy', record.getMessage()) - - with self.assertRaises(ro.InconsistentResolutionOrderError): - self._callFUT(ExtendedPathIndex, strict=True) - - def test_OSError_IOError(self): - from zope.interface import providedBy - from zope.interface.common import interfaces - - self.assertEqual( - list(providedBy(OSError()).flattened()), - [ - interfaces.IOSError, - interfaces.IIOError, - interfaces.IEnvironmentError, - interfaces.IStandardError, - interfaces.IException, - interfaces.Interface, - ]) - - def test_non_orderable(self): - import warnings - - from zope.interface import ro - try: - # If we've already warned, we must reset that state. - del ro.__warningregistry__ - except AttributeError: - pass - - with warnings.catch_warnings(): - warnings.simplefilter('error') - with C3Setting( - ro.C3.WARN_BAD_IRO, True - ), C3Setting( - ro.C3.STRICT_IRO, False - ): - with self.assertRaises(ro.InconsistentResolutionOrderWarning): - super().test_non_orderable() - - IOErr, _ = self._make_IOErr() - with self.assertRaises(ro.InconsistentResolutionOrderError): - self._callFUT(IOErr, strict=True) - - with C3Setting( - ro.C3.TRACK_BAD_IRO, True - ), C3Setting( - ro.C3.STRICT_IRO, False - ): - with warnings.catch_warnings(): - warnings.simplefilter('ignore') - self._callFUT(IOErr) - self.assertIn(IOErr, ro.C3.BAD_IROS) - - iro = self._callFUT(IOErr, strict=False) - legacy_iro = self._callFUT(IOErr, use_legacy_ro=True, strict=False) - self.assertEqual(iro, legacy_iro) - - -class TestC3(unittest.TestCase): - def _makeOne(self, C, strict=False, base_mros=None): - from zope.interface.ro import C3 - return C3.resolver(C, strict, base_mros) - - def test_base_mros_given(self): - c3 = self._makeOne( - type(self), - base_mros={unittest.TestCase: unittest.TestCase.__mro__} - ) - memo = c3.memo - self.assertIn(unittest.TestCase, memo) - # We used the StaticMRO class - self.assertIsNone(memo[unittest.TestCase].had_inconsistency) - - def test_one_base_optimization(self): - c3 = self._makeOne(type(self)) - # Even though we didn't call .mro() yet, the MRO has been - # computed. - self.assertIsNotNone(c3._C3__mro) # pylint:disable=no-member - c3._merge = None - self.assertEqual(c3.mro(), list(type(self).__mro__)) - - -class Test_ROComparison(unittest.TestCase): - - class MockC3: - direct_inconsistency = False - bases_had_inconsistency = False - - def _makeOne(self, c3=None, c3_ro=(), legacy_ro=()): - from zope.interface.ro import _ROComparison - return _ROComparison(c3 or self.MockC3(), c3_ro, legacy_ro) - - def test_inconsistent_label(self): - comp = self._makeOne() - self.assertEqual('no', comp._inconsistent_label) - - comp.c3.direct_inconsistency = True - self.assertEqual("direct", comp._inconsistent_label) - - comp.c3.bases_had_inconsistency = True - self.assertEqual("direct+bases", comp._inconsistent_label) - - comp.c3.direct_inconsistency = False - self.assertEqual('bases', comp._inconsistent_label) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_sorting.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_sorting.py deleted file mode 100644 index 813d9d3..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_sorting.py +++ /dev/null @@ -1,83 +0,0 @@ -############################################################################## -# -# Copyright (c) 2001, 2002 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Test interface sorting -""" - -import unittest - -from zope.interface import Interface - - -class I1(Interface): - pass - - -class I2(I1): - pass - - -class I3(I1): - pass - - -class I4(Interface): - pass - - -class I5(I4): - pass - - -class I6(I2): - pass - - -class Test(unittest.TestCase): - - def test(self): - iface_list = [I1, I3, I5, I6, I4, I2] - iface_list.sort() - self.assertEqual(iface_list, [I1, I2, I3, I4, I5, I6]) - - def test_w_None(self): - iface_list = [I1, None, I3, I5, I6, I4, I2] - iface_list.sort() - self.assertEqual(iface_list, [I1, I2, I3, I4, I5, I6, None]) - - def test_w_equal_names(self): - # interfaces with equal names but different modules should sort by - # module name - from zope.interface.tests.m1 import I1 as m1_I1 - iface_list = [I1, m1_I1] - iface_list.sort() - self.assertEqual(iface_list, [m1_I1, I1]) - - def test_I1_I2(self): - self.assertLess(I1.__name__, I2.__name__) - self.assertEqual(I1.__module__, I2.__module__) - self.assertEqual(I1.__module__, __name__) - self.assertLess(I1, I2) - - def _makeI1(self): - - class I1(Interface): - pass - - return I1 - - def test_nested(self): - nested_I1 = self._makeI1() - self.assertEqual(I1, nested_I1) - self.assertEqual(nested_I1, I1) - self.assertEqual(hash(I1), hash(nested_I1)) diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_verify.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_verify.py deleted file mode 100644 index 046cb6a..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/tests/test_verify.py +++ /dev/null @@ -1,656 +0,0 @@ -############################################################################## -# -# Copyright (c) 2001, 2002 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -""" zope.interface.verify unit tests -""" -import unittest - - -# pylint:disable=inherit-non-class,no-method-argument,no-self-argument - -class Test_verifyClass(unittest.TestCase): - - verifier = None - - def setUp(self): - self.verifier = self._get_FUT() - - @classmethod - def _get_FUT(cls): - from zope.interface.verify import verifyClass - return verifyClass - - def _adjust_object_before_verify(self, x): - return x - - def _callFUT(self, iface, klass, **kwargs): - return self.verifier(iface, - self._adjust_object_before_verify(klass), - **kwargs) - - def test_class_doesnt_implement(self): - from zope.interface import Interface - from zope.interface.exceptions import DoesNotImplement - - class ICurrent(Interface): - pass - - class Current: - pass - - self.assertRaises(DoesNotImplement, self._callFUT, ICurrent, Current) - - def test_class_doesnt_implement_but_classImplements_later(self): - from zope.interface import Interface - from zope.interface import classImplements - - class ICurrent(Interface): - pass - - class Current: - pass - - classImplements(Current, ICurrent) - - self._callFUT(ICurrent, Current) - - def test_class_doesnt_have_required_method_simple(self): - from zope.interface import Interface - from zope.interface import implementer - from zope.interface.exceptions import BrokenImplementation - - class ICurrent(Interface): - def method(): - """docstring""" - - @implementer(ICurrent) - class Current: - pass - - self.assertRaises(BrokenImplementation, - self._callFUT, ICurrent, Current) - - def test_class_has_required_method_simple(self): - from zope.interface import Interface - from zope.interface import implementer - - class ICurrent(Interface): - def method(): - """docstring""" - - @implementer(ICurrent) - class Current: - - def method(self): - raise NotImplementedError() - - self._callFUT(ICurrent, Current) - - def test_class_doesnt_have_required_method_derived(self): - from zope.interface import Interface - from zope.interface import implementer - from zope.interface.exceptions import BrokenImplementation - - class IBase(Interface): - def method(): - """docstring""" - - class IDerived(IBase): - pass - - @implementer(IDerived) - class Current: - pass - - self.assertRaises(BrokenImplementation, - self._callFUT, IDerived, Current) - - def test_class_has_required_method_derived(self): - from zope.interface import Interface - from zope.interface import implementer - - class IBase(Interface): - def method(): - """docstring""" - - class IDerived(IBase): - pass - - @implementer(IDerived) - class Current: - - def method(self): - raise NotImplementedError() - - self._callFUT(IDerived, Current) - - def test_method_takes_wrong_arg_names_but_OK(self): - # We no longer require names to match. - from zope.interface import Interface - from zope.interface import implementer - - class ICurrent(Interface): - - def method(a): - """docstring""" - - @implementer(ICurrent) - class Current: - - def method(self, b): - raise NotImplementedError() - - self._callFUT(ICurrent, Current) - - def test_method_takes_not_enough_args(self): - from zope.interface import Interface - from zope.interface import implementer - from zope.interface.exceptions import BrokenMethodImplementation - - class ICurrent(Interface): - - def method(a): - """docstring""" - - @implementer(ICurrent) - class Current: - - def method(self): - raise NotImplementedError() - - self.assertRaises(BrokenMethodImplementation, - self._callFUT, ICurrent, Current) - - def test_method_doesnt_take_required_starargs(self): - from zope.interface import Interface - from zope.interface import implementer - from zope.interface.exceptions import BrokenMethodImplementation - - class ICurrent(Interface): - - def method(*args): - """docstring""" - - @implementer(ICurrent) - class Current: - - def method(self): - raise NotImplementedError() - - self.assertRaises(BrokenMethodImplementation, - self._callFUT, ICurrent, Current) - - def test_method_doesnt_take_required_only_kwargs(self): - from zope.interface import Interface - from zope.interface import implementer - from zope.interface.exceptions import BrokenMethodImplementation - - class ICurrent(Interface): - - def method(**kw): - """docstring""" - - @implementer(ICurrent) - class Current: - - def method(self): - raise NotImplementedError() - - self.assertRaises(BrokenMethodImplementation, - self._callFUT, ICurrent, Current) - - def test_method_takes_extra_arg(self): - from zope.interface import Interface - from zope.interface import implementer - from zope.interface.exceptions import BrokenMethodImplementation - - class ICurrent(Interface): - - def method(a): - """docstring""" - - @implementer(ICurrent) - class Current: - - def method(self, a, b): - raise NotImplementedError() - - self.assertRaises(BrokenMethodImplementation, - self._callFUT, ICurrent, Current) - - def test_method_takes_extra_arg_with_default(self): - from zope.interface import Interface - from zope.interface import implementer - - class ICurrent(Interface): - - def method(a): - """docstring""" - - @implementer(ICurrent) - class Current: - - def method(self, a, b=None): - raise NotImplementedError() - - self._callFUT(ICurrent, Current) - - def test_method_takes_only_positional_args(self): - from zope.interface import Interface - from zope.interface import implementer - - class ICurrent(Interface): - - def method(a): - """docstring""" - - @implementer(ICurrent) - class Current: - - def method(self, *args): - raise NotImplementedError() - - self._callFUT(ICurrent, Current) - - def test_method_takes_only_kwargs(self): - from zope.interface import Interface - from zope.interface import implementer - from zope.interface.exceptions import BrokenMethodImplementation - - class ICurrent(Interface): - - def method(a): - """docstring""" - - @implementer(ICurrent) - class Current: - - def method(self, **kw): - raise NotImplementedError() - - self.assertRaises(BrokenMethodImplementation, - self._callFUT, ICurrent, Current) - - def test_method_takes_extra_starargs(self): - from zope.interface import Interface - from zope.interface import implementer - - class ICurrent(Interface): - - def method(a): - """docstring""" - - @implementer(ICurrent) - class Current: - - def method(self, a, *args): - raise NotImplementedError() - - self._callFUT(ICurrent, Current) - - def test_method_takes_extra_starargs_and_kwargs(self): - from zope.interface import Interface - from zope.interface import implementer - - class ICurrent(Interface): - - def method(a): - """docstring""" - - @implementer(ICurrent) - class Current: - - def method(self, a, *args, **kw): - raise NotImplementedError() - - self._callFUT(ICurrent, Current) - - def test_method_doesnt_take_required_positional_and_starargs(self): - from zope.interface import Interface - from zope.interface import implementer - from zope.interface.exceptions import BrokenMethodImplementation - - class ICurrent(Interface): - - def method(a, *args): - """docstring""" - - @implementer(ICurrent) - class Current: - - def method(self, a): - raise NotImplementedError() - - self.assertRaises(BrokenMethodImplementation, - self._callFUT, ICurrent, Current) - - def test_method_takes_required_positional_and_starargs(self): - from zope.interface import Interface - from zope.interface import implementer - - class ICurrent(Interface): - - def method(a, *args): - """docstring""" - - @implementer(ICurrent) - class Current: - - def method(self, a, *args): - raise NotImplementedError() - - self._callFUT(ICurrent, Current) - - def test_method_takes_only_starargs(self): - from zope.interface import Interface - from zope.interface import implementer - - class ICurrent(Interface): - - def method(a, *args): - """docstring""" - - @implementer(ICurrent) - class Current: - - def method(self, *args): - raise NotImplementedError() - - self._callFUT(ICurrent, Current) - - def test_method_takes_required_kwargs(self): - from zope.interface import Interface - from zope.interface import implementer - - class ICurrent(Interface): - - def method(**kwargs): - """docstring""" - - @implementer(ICurrent) - class Current: - - def method(self, **kw): - raise NotImplementedError() - - self._callFUT(ICurrent, Current) - - def test_method_takes_positional_plus_required_starargs(self): - from zope.interface import Interface - from zope.interface import implementer - from zope.interface.exceptions import BrokenMethodImplementation - - class ICurrent(Interface): - - def method(*args): - """docstring""" - - @implementer(ICurrent) - class Current: - - def method(self, a, *args): - raise NotImplementedError() - - self.assertRaises(BrokenMethodImplementation, - self._callFUT, ICurrent, Current) - - def test_method_doesnt_take_required_kwargs(self): - from zope.interface import Interface - from zope.interface import implementer - from zope.interface.exceptions import BrokenMethodImplementation - - class ICurrent(Interface): - - def method(**kwargs): - """docstring""" - - @implementer(ICurrent) - class Current: - - def method(self, a): - raise NotImplementedError() - - self.assertRaises(BrokenMethodImplementation, - self._callFUT, ICurrent, Current) - - def test_class_has_method_for_iface_attr(self): - from zope.interface import Attribute - from zope.interface import Interface - from zope.interface import implementer - - class ICurrent(Interface): - attr = Attribute("The foo Attribute") - - @implementer(ICurrent) - class Current: - - def attr(self): - raise NotImplementedError() - - self._callFUT(ICurrent, Current) - - def test_class_has_nonmethod_for_method(self): - from zope.interface import Interface - from zope.interface import implementer - from zope.interface.exceptions import BrokenMethodImplementation - - class ICurrent(Interface): - def method(): - """docstring""" - - @implementer(ICurrent) - class Current: - method = 1 - - self.assertRaises(BrokenMethodImplementation, - self._callFUT, ICurrent, Current) - - def test_class_has_attribute_for_attribute(self): - from zope.interface import Attribute - from zope.interface import Interface - from zope.interface import implementer - - class ICurrent(Interface): - attr = Attribute("The foo Attribute") - - @implementer(ICurrent) - class Current: - - attr = 1 - - self._callFUT(ICurrent, Current) - - def test_class_misses_attribute_for_attribute(self): - # This check *passes* for verifyClass - from zope.interface import Attribute - from zope.interface import Interface - from zope.interface import implementer - - class ICurrent(Interface): - attr = Attribute("The foo Attribute") - - @implementer(ICurrent) - class Current: - pass - - self._callFUT(ICurrent, Current) - - def test_w_callable_non_func_method(self): - from zope.interface import Interface - from zope.interface import implementer - from zope.interface.interface import Method - - class QuasiMethod(Method): - def __call__(self, *args, **kw): - raise NotImplementedError() - - class QuasiCallable: - def __call__(self, *args, **kw): - raise NotImplementedError() - - class ICurrent(Interface): - attr = QuasiMethod('This is callable') - - @implementer(ICurrent) - class Current: - attr = QuasiCallable() - - self._callFUT(ICurrent, Current) - - def test_w_decorated_method(self): - from zope.interface import Interface - from zope.interface import implementer - - def decorator(func): - # this is, in fact, zope.proxy.non_overridable - return property(lambda self: func.__get__(self)) - - class ICurrent(Interface): - - def method(a): - """docstring""" - - @implementer(ICurrent) - class Current: - - @decorator - def method(self, a): - raise NotImplementedError() - - self._callFUT(ICurrent, Current) - - def test_dict_IFullMapping(self): - # A dict should be an IFullMapping, but this exposes two - # issues. First, on CPython, methods of builtin types are - # "method_descriptor" objects, and are harder to introspect. - # Second, on PyPy, the signatures can be just plain wrong, - # specifying as required arguments that are actually optional. - # See https://github.com/zopefoundation/zope.interface/issues/118 - from zope.interface.common.mapping import IFullMapping - self._callFUT(IFullMapping, dict, tentative=True) - - def test_list_ISequence(self): - # As for test_dict_IFullMapping - from zope.interface.common.sequence import ISequence - self._callFUT(ISequence, list, tentative=True) - - def test_tuple_IReadSequence(self): - # As for test_dict_IFullMapping - from zope.interface.common.sequence import IReadSequence - self._callFUT(IReadSequence, tuple, tentative=True) - - def test_multiple_invalid(self): - from zope.interface import Interface - from zope.interface import classImplements - from zope.interface.exceptions import BrokenImplementation - from zope.interface.exceptions import DoesNotImplement - from zope.interface.exceptions import MultipleInvalid - - class ISeveralMethods(Interface): - def meth1(arg1): - "Method 1" - def meth2(arg1): - "Method 2" - - class SeveralMethods: - pass - - with self.assertRaises(MultipleInvalid) as exc: - self._callFUT(ISeveralMethods, SeveralMethods) - - ex = exc.exception - self.assertEqual(3, len(ex.exceptions)) - self.assertIsInstance(ex.exceptions[0], DoesNotImplement) - self.assertIsInstance(ex.exceptions[1], BrokenImplementation) - self.assertIsInstance(ex.exceptions[2], BrokenImplementation) - - # If everything else is correct, only the single error is raised - # without the wrapper. - classImplements(SeveralMethods, ISeveralMethods) - SeveralMethods.meth1 = lambda self, arg1: "Hi" - - with self.assertRaises(BrokenImplementation): - self._callFUT(ISeveralMethods, SeveralMethods) - - -class Test_verifyObject(Test_verifyClass): - - @classmethod - def _get_FUT(cls): - from zope.interface.verify import verifyObject - return verifyObject - - def _adjust_object_before_verify(self, target): - if isinstance(target, (type, type(OldSkool))): - target = target() - return target - - def test_class_misses_attribute_for_attribute(self): - # This check *fails* for verifyObject - from zope.interface import Attribute - from zope.interface import Interface - from zope.interface import implementer - from zope.interface.exceptions import BrokenImplementation - - class ICurrent(Interface): - attr = Attribute("The foo Attribute") - - @implementer(ICurrent) - class Current: - pass - - self.assertRaises(BrokenImplementation, - self._callFUT, ICurrent, Current) - - def test_module_hit(self): - from zope.interface.tests import dummy - from zope.interface.tests.idummy import IDummyModule - - self._callFUT(IDummyModule, dummy) - - def test_module_miss(self): - from zope.interface import Interface - from zope.interface.exceptions import DoesNotImplement - from zope.interface.tests import dummy - - # same name, different object - class IDummyModule(Interface): - pass - - self.assertRaises(DoesNotImplement, - self._callFUT, IDummyModule, dummy) - - def test_staticmethod_hit_on_class(self): - from zope.interface import Interface - from zope.interface import provider - from zope.interface.verify import verifyObject - - class IFoo(Interface): - - def bar(a, b): - "The bar method" - - @provider(IFoo) - class Foo: - - @staticmethod - def bar(a, b): - raise AssertionError("We're never actually called") - - # Don't use self._callFUT, we don't want to instantiate the - # class. - verifyObject(IFoo, Foo) - - -class OldSkool: - pass diff --git a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/verify.py b/plotter-app/venv/lib/python3.8/site-packages/zope/interface/verify.py deleted file mode 100644 index 5bee470..0000000 --- a/plotter-app/venv/lib/python3.8/site-packages/zope/interface/verify.py +++ /dev/null @@ -1,209 +0,0 @@ -############################################################################## -# -# Copyright (c) 2001, 2002 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Verify interface implementations -""" -import inspect -import sys -from types import FunctionType -from types import MethodType - -from zope.interface.exceptions import BrokenImplementation -from zope.interface.exceptions import BrokenMethodImplementation -from zope.interface.exceptions import DoesNotImplement -from zope.interface.exceptions import Invalid -from zope.interface.exceptions import MultipleInvalid -from zope.interface.interface import Method -from zope.interface.interface import fromFunction -from zope.interface.interface import fromMethod - - -__all__ = [ - 'verifyObject', - 'verifyClass', -] - -# This will be monkey-patched when running under Zope 2, so leave this -# here: -MethodTypes = (MethodType, ) - - -def _verify(iface, candidate, tentative=False, vtype=None): - """ - Verify that *candidate* might correctly provide *iface*. - - This involves: - - - Making sure the candidate claims that it provides the - interface using ``iface.providedBy`` (unless *tentative* is `True`, in - which case this step is skipped). This means that the candidate's class - declares that it `implements ` the - interface, or the candidate itself declares that it `provides - ` - the interface - - - Making sure the candidate defines all the necessary methods - - - Making sure the methods have the correct signature (to the - extent possible) - - - Making sure the candidate defines all the necessary attributes - - :return bool: Returns a true value if everything that could be - checked passed. - :raises zope.interface.Invalid: If any of the previous - conditions does not hold. - - .. versionchanged:: 5.0 - If multiple methods or attributes are invalid, all such errors - are collected and reported. Previously, only the first error was - reported. As a special case, if only one such error is present, it is - raised alone, like before. - """ - - if vtype == 'c': - tester = iface.implementedBy - else: - tester = iface.providedBy - - excs = [] - if not tentative and not tester(candidate): - excs.append(DoesNotImplement(iface, candidate)) - - for name, desc in iface.namesAndDescriptions(all=True): - try: - _verify_element(iface, name, desc, candidate, vtype) - except Invalid as e: - excs.append(e) - - if excs: - if len(excs) == 1: - raise excs[0] - raise MultipleInvalid(iface, candidate, excs) - - return True - - -def _verify_element(iface, name, desc, candidate, vtype): - # Here the `desc` is either an `Attribute` or `Method` instance - try: - attr = getattr(candidate, name) - except AttributeError: - - if (not isinstance(desc, Method)) and vtype == 'c': - # We can't verify non-methods on classes, since the - # class may provide attrs in it's __init__. - return - - # TODO: This should use ``raise...from`` - raise BrokenImplementation(iface, desc, candidate) - - if not isinstance(desc, Method): - # If it's not a method, there's nothing else we can test - return - - if inspect.ismethoddescriptor(attr) or inspect.isbuiltin(attr): - # The first case is what you get for things like ``dict.pop`` - # on CPython (e.g., ``verifyClass(IFullMapping, dict))``). The - # second case is what you get for things like ``dict().pop`` on - # CPython (e.g., ``verifyObject(IFullMapping, dict()))``. - # In neither case can we get a signature, so there's nothing - # to verify. Even the inspect module gives up and raises - # ValueError: no signature found. The ``__text_signature__`` attribute - # isn't typically populated either. - # - # Note that on PyPy 2 or 3 (up through 7.3 at least), these are not - # true for things like ``dict.pop`` (but might be true for C - # extensions?) - return - - if isinstance(attr, FunctionType): - - if isinstance(candidate, type) and vtype == 'c': - # This is an "unbound method". - # Only unwrap this if we're verifying implementedBy; - # otherwise we can unwrap @staticmethod on classes that directly - # provide an interface. - meth = fromFunction(attr, iface, name=name, imlevel=1) - else: - # Nope, just a normal function - meth = fromFunction(attr, iface, name=name) - - elif ( - isinstance(attr, MethodTypes) and - type(attr.__func__) is FunctionType - ): - meth = fromMethod(attr, iface, name) - - elif isinstance(attr, property) and vtype == 'c': - # Without an instance we cannot be sure it's not a - # callable. - # TODO: This should probably check inspect.isdatadescriptor(), - # a more general form than ``property`` - return - - else: - if not callable(attr): - raise BrokenMethodImplementation( - desc, - "implementation is not a method", - attr, - iface, - candidate - ) - # sigh, it's callable, but we don't know how to introspect it, so - # we have to give it a pass. - return - - # Make sure that the required and implemented method signatures are - # the same. - mess = _incompat(desc.getSignatureInfo(), meth.getSignatureInfo()) - if mess: - raise BrokenMethodImplementation(desc, mess, attr, iface, candidate) - - -def verifyClass(iface, candidate, tentative=False): - """ - Verify that the *candidate* might correctly provide *iface*. - """ - return _verify(iface, candidate, tentative, vtype='c') - - -def verifyObject(iface, candidate, tentative=False): - return _verify(iface, candidate, tentative, vtype='o') - - -verifyObject.__doc__ = _verify.__doc__ - -_MSG_TOO_MANY = 'implementation requires too many arguments' - - -def _incompat(required, implemented): - # if (required['positional'] != - # implemented['positional'][:len(required['positional'])] - # and implemented['kwargs'] is None): - # return 'imlementation has different argument names' - if len(implemented['required']) > len(required['required']): - return _MSG_TOO_MANY - - if ( - (len(implemented['positional']) < len(required['positional'])) and - not implemented['varargs'] - ): - return "implementation doesn't allow enough arguments" - - if required['kwargs'] and not implemented['kwargs']: - return "implementation doesn't support keyword arguments" - - if required['varargs'] and not implemented['varargs']: - return "implementation doesn't support variable arguments" diff --git a/plotter-app/venv/lib64 b/plotter-app/venv/lib64 deleted file mode 120000 index 7951405..0000000 --- a/plotter-app/venv/lib64 +++ /dev/null @@ -1 +0,0 @@ -lib \ No newline at end of file diff --git a/plotter-app/venv/pyvenv.cfg b/plotter-app/venv/pyvenv.cfg deleted file mode 100644 index 853404e..0000000 --- a/plotter-app/venv/pyvenv.cfg +++ /dev/null @@ -1,3 +0,0 @@ -home = /usr/bin -include-system-site-packages = false -version = 3.8.10 diff --git a/plotter-app/venv/share/man/man1/ttx.1 b/plotter-app/venv/share/man/man1/ttx.1 deleted file mode 100644 index bba23b5..0000000 --- a/plotter-app/venv/share/man/man1/ttx.1 +++ /dev/null @@ -1,225 +0,0 @@ -.Dd May 18, 2004 -.\" ttx is not specific to any OS, but contrary to what groff_mdoc(7) -.\" seems to imply, entirely omitting the .Os macro causes 'BSD' to -.\" be used, so I give a zero-width space as its argument. -.Os \& -.\" The "FontTools Manual" argument apparently has no effect in -.\" groff 1.18.1. I think it is a bug in the -mdoc groff package. -.Dt TTX 1 "FontTools Manual" -.Sh NAME -.Nm ttx -.Nd tool for manipulating TrueType and OpenType fonts -.Sh SYNOPSIS -.Nm -.Bk -.Op Ar option ... -.Ek -.Bk -.Ar file ... -.Ek -.Sh DESCRIPTION -.Nm -is a tool for manipulating TrueType and OpenType fonts. It can convert -TrueType and OpenType fonts to and from an -.Tn XML Ns -based format called -.Tn TTX . -.Tn TTX -files have a -.Ql .ttx -extension. -.Pp -For each -.Ar file -argument it is given, -.Nm -detects whether it is a -.Ql .ttf , -.Ql .otf -or -.Ql .ttx -file and acts accordingly: if it is a -.Ql .ttf -or -.Ql .otf -file, it generates a -.Ql .ttx -file; if it is a -.Ql .ttx -file, it generates a -.Ql .ttf -or -.Ql .otf -file. -.Pp -By default, every output file is created in the same directory as the -corresponding input file and with the same name except for the -extension, which is substituted appropriately. -.Nm -never overwrites existing files; if necessary, it appends a suffix to -the output file name before the extension, as in -.Pa Arial#1.ttf . -.Ss "General options" -.Bl -tag -width ".Fl t Ar table" -.It Fl h -Display usage information. -.It Fl d Ar dir -Write the output files to directory -.Ar dir -instead of writing every output file to the same directory as the -corresponding input file. -.It Fl o Ar file -Write the output to -.Ar file -instead of writing it to the same directory as the -corresponding input file. -.It Fl v -Be verbose. Write more messages to the standard output describing what -is being done. -.It Fl a -Allow virtual glyphs ID's on compile or decompile. -.El -.Ss "Dump options" -The following options control the process of dumping font files -(TrueType or OpenType) to -.Tn TTX -files. -.Bl -tag -width ".Fl t Ar table" -.It Fl l -List table information. Instead of dumping the font to a -.Tn TTX -file, display minimal information about each table. -.It Fl t Ar table -Dump table -.Ar table . -This option may be given multiple times to dump several tables at -once. When not specified, all tables are dumped. -.It Fl x Ar table -Exclude table -.Ar table -from the list of tables to dump. This option may be given multiple -times to exclude several tables from the dump. The -.Fl t -and -.Fl x -options are mutually exclusive. -.It Fl s -Split tables. Dump each table to a separate -.Tn TTX -file and write (under the name that would have been used for the output -file if the -.Fl s -option had not been given) one small -.Tn TTX -file containing references to the individual table dump files. This -file can be used as input to -.Nm -as long as the referenced files can be found in the same directory. -.It Fl i -.\" XXX: I suppose OpenType programs (exist and) are also affected. -Don't disassemble TrueType instructions. When this option is specified, -all TrueType programs (glyph programs, the font program and the -pre-program) are written to the -.Tn TTX -file as hexadecimal data instead of -assembly. This saves some time and results in smaller -.Tn TTX -files. -.It Fl y Ar n -When decompiling a TrueType Collection (TTC) file, -decompile font number -.Ar n , -starting from 0. -.El -.Ss "Compilation options" -The following options control the process of compiling -.Tn TTX -files into font files (TrueType or OpenType): -.Bl -tag -width ".Fl t Ar table" -.It Fl m Ar fontfile -Merge the input -.Tn TTX -file -.Ar file -with -.Ar fontfile . -No more than one -.Ar file -argument can be specified when this option is used. -.It Fl b -Don't recalculate glyph bounding boxes. Use the values in the -.Tn TTX -file as is. -.El -.Sh "THE TTX FILE FORMAT" -You can find some information about the -.Tn TTX -file format in -.Pa documentation.html . -In particular, you will find in that file the list of tables understood by -.Nm -and the relations between TrueType GlyphIDs and the glyph names used in -.Tn TTX -files. -.Sh EXAMPLES -In the following examples, all files are read from and written to the -current directory. Additionally, the name given for the output file -assumes in every case that it did not exist before -.Nm -was invoked. -.Pp -Dump the TrueType font contained in -.Pa FreeSans.ttf -to -.Pa FreeSans.ttx : -.Pp -.Dl ttx FreeSans.ttf -.Pp -Compile -.Pa MyFont.ttx -into a TrueType or OpenType font file: -.Pp -.Dl ttx MyFont.ttx -.Pp -List the tables in -.Pa FreeSans.ttf -along with some information: -.Pp -.Dl ttx -l FreeSans.ttf -.Pp -Dump the -.Sq cmap -table from -.Pa FreeSans.ttf -to -.Pa FreeSans.ttx : -.Pp -.Dl ttx -t cmap FreeSans.ttf -.Sh NOTES -On MS\-Windows and MacOS, -.Nm -is available as a graphical application to which files can be dropped. -.Sh SEE ALSO -.Pa documentation.html -.Pp -.Xr fontforge 1 , -.Xr ftinfo 1 , -.Xr gfontview 1 , -.Xr xmbdfed 1 , -.Xr Font::TTF 3pm -.Sh AUTHORS -.Nm -was written by -.An -nosplit -.An "Just van Rossum" Aq just@letterror.com . -.Pp -This manual page was written by -.An "Florent Rougon" Aq f.rougon@free.fr -for the Debian GNU/Linux system based on the existing FontTools -documentation. It may be freely used, modified and distributed without -restrictions. -.\" For Emacs: -.\" Local Variables: -.\" fill-column: 72 -.\" sentence-end: "[.?!][]\"')}]*\\($\\| $\\| \\| \\)[ \n]*" -.\" sentence-end-double-space: t -.\" End: \ No newline at end of file diff --git a/plotter-app/venv/share/python-wheels/CacheControl-0.12.6-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/CacheControl-0.12.6-py2.py3-none-any.whl deleted file mode 100644 index 69e46f4..0000000 Binary files a/plotter-app/venv/share/python-wheels/CacheControl-0.12.6-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/appdirs-1.4.3-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/appdirs-1.4.3-py2.py3-none-any.whl deleted file mode 100644 index 9ccc8e2..0000000 Binary files a/plotter-app/venv/share/python-wheels/appdirs-1.4.3-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/certifi-2019.11.28-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/certifi-2019.11.28-py2.py3-none-any.whl deleted file mode 100644 index 2cef4d2..0000000 Binary files a/plotter-app/venv/share/python-wheels/certifi-2019.11.28-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/chardet-3.0.4-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/chardet-3.0.4-py2.py3-none-any.whl deleted file mode 100644 index 89dfc34..0000000 Binary files a/plotter-app/venv/share/python-wheels/chardet-3.0.4-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/colorama-0.4.3-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/colorama-0.4.3-py2.py3-none-any.whl deleted file mode 100644 index ad508f1..0000000 Binary files a/plotter-app/venv/share/python-wheels/colorama-0.4.3-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/contextlib2-0.6.0-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/contextlib2-0.6.0-py2.py3-none-any.whl deleted file mode 100644 index 5d08a03..0000000 Binary files a/plotter-app/venv/share/python-wheels/contextlib2-0.6.0-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/distlib-0.3.0-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/distlib-0.3.0-py2.py3-none-any.whl deleted file mode 100644 index 75a6e40..0000000 Binary files a/plotter-app/venv/share/python-wheels/distlib-0.3.0-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/distro-1.4.0-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/distro-1.4.0-py2.py3-none-any.whl deleted file mode 100644 index e94a3a4..0000000 Binary files a/plotter-app/venv/share/python-wheels/distro-1.4.0-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/html5lib-1.0.1-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/html5lib-1.0.1-py2.py3-none-any.whl deleted file mode 100644 index f56e297..0000000 Binary files a/plotter-app/venv/share/python-wheels/html5lib-1.0.1-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/idna-2.8-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/idna-2.8-py2.py3-none-any.whl deleted file mode 100644 index caacbf0..0000000 Binary files a/plotter-app/venv/share/python-wheels/idna-2.8-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/ipaddr-2.2.0-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/ipaddr-2.2.0-py2.py3-none-any.whl deleted file mode 100644 index df29e28..0000000 Binary files a/plotter-app/venv/share/python-wheels/ipaddr-2.2.0-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/lockfile-0.12.2-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/lockfile-0.12.2-py2.py3-none-any.whl deleted file mode 100644 index 4d2a1c6..0000000 Binary files a/plotter-app/venv/share/python-wheels/lockfile-0.12.2-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/msgpack-0.6.2-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/msgpack-0.6.2-py2.py3-none-any.whl deleted file mode 100644 index 593d5ec..0000000 Binary files a/plotter-app/venv/share/python-wheels/msgpack-0.6.2-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/packaging-20.3-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/packaging-20.3-py2.py3-none-any.whl deleted file mode 100644 index 153af45..0000000 Binary files a/plotter-app/venv/share/python-wheels/packaging-20.3-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/pep517-0.8.2-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/pep517-0.8.2-py2.py3-none-any.whl deleted file mode 100644 index 9a41c25..0000000 Binary files a/plotter-app/venv/share/python-wheels/pep517-0.8.2-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/pip-20.0.2-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/pip-20.0.2-py2.py3-none-any.whl deleted file mode 100644 index 3fcefae..0000000 Binary files a/plotter-app/venv/share/python-wheels/pip-20.0.2-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/pkg_resources-0.0.0-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/pkg_resources-0.0.0-py2.py3-none-any.whl deleted file mode 100644 index e066bc7..0000000 Binary files a/plotter-app/venv/share/python-wheels/pkg_resources-0.0.0-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/progress-1.5-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/progress-1.5-py2.py3-none-any.whl deleted file mode 100644 index ec39671..0000000 Binary files a/plotter-app/venv/share/python-wheels/progress-1.5-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/pyparsing-2.4.6-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/pyparsing-2.4.6-py2.py3-none-any.whl deleted file mode 100644 index f6ab988..0000000 Binary files a/plotter-app/venv/share/python-wheels/pyparsing-2.4.6-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/requests-2.22.0-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/requests-2.22.0-py2.py3-none-any.whl deleted file mode 100644 index 8dfb42d..0000000 Binary files a/plotter-app/venv/share/python-wheels/requests-2.22.0-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/retrying-1.3.3-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/retrying-1.3.3-py2.py3-none-any.whl deleted file mode 100644 index 48b9fc0..0000000 Binary files a/plotter-app/venv/share/python-wheels/retrying-1.3.3-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/setuptools-44.0.0-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/setuptools-44.0.0-py2.py3-none-any.whl deleted file mode 100644 index b352c67..0000000 Binary files a/plotter-app/venv/share/python-wheels/setuptools-44.0.0-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/six-1.14.0-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/six-1.14.0-py2.py3-none-any.whl deleted file mode 100644 index 0e572b2..0000000 Binary files a/plotter-app/venv/share/python-wheels/six-1.14.0-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/toml-0.10.0-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/toml-0.10.0-py2.py3-none-any.whl deleted file mode 100644 index 929a552..0000000 Binary files a/plotter-app/venv/share/python-wheels/toml-0.10.0-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/urllib3-1.25.8-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/urllib3-1.25.8-py2.py3-none-any.whl deleted file mode 100644 index e085187..0000000 Binary files a/plotter-app/venv/share/python-wheels/urllib3-1.25.8-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/webencodings-0.5.1-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/webencodings-0.5.1-py2.py3-none-any.whl deleted file mode 100644 index 4fd5bfe..0000000 Binary files a/plotter-app/venv/share/python-wheels/webencodings-0.5.1-py2.py3-none-any.whl and /dev/null differ diff --git a/plotter-app/venv/share/python-wheels/wheel-0.34.2-py2.py3-none-any.whl b/plotter-app/venv/share/python-wheels/wheel-0.34.2-py2.py3-none-any.whl deleted file mode 100644 index 2fcda36..0000000 Binary files a/plotter-app/venv/share/python-wheels/wheel-0.34.2-py2.py3-none-any.whl and /dev/null differ